Initial check-in of ElectricalFire, a Java JIT compiler.
This commit is contained in:
Родитель
989c4fbe60
Коммит
53f8b7e464
|
@ -0,0 +1,194 @@
|
|||
/* -*- 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.
|
||||
*/
|
||||
// Backend.cpp
|
||||
//
|
||||
// Scott M. Silver
|
||||
//
|
||||
// Translate a ControlGraph into native code
|
||||
|
||||
|
||||
#if defined(DEBUG) && (defined(WIN32) || defined(USE_MESA)) && defined(IGVISUALIZE)
|
||||
#define USE_VISUALIZER
|
||||
#endif
|
||||
|
||||
#ifdef USE_VISUALIZER
|
||||
#define IGVISUALIZE_ONLY(x) x
|
||||
#include "IGVisualizer.h"
|
||||
#else
|
||||
#define IGVISUALIZE_ONLY(x)
|
||||
#endif
|
||||
|
||||
#include "Backend.h"
|
||||
#include "ControlGraph.h"
|
||||
#include "JavaVM.h"
|
||||
|
||||
#define INCLUDE_EMITTER
|
||||
#include "CpuInfo.h"
|
||||
#include "RegisterAllocator.h"
|
||||
#include "CodeGenerator.h"
|
||||
#include "CGScheduler.h"
|
||||
#include "NativeFormatter.h"
|
||||
|
||||
#include "FieldOrMethod.h"
|
||||
#include "LogModule.h"
|
||||
|
||||
#if DEBUG_laurentm
|
||||
#include "ControlNodeScheduler.h"
|
||||
#endif
|
||||
|
||||
static void
|
||||
explodeImmediatePrimitives(ControlGraph &cg);
|
||||
|
||||
#ifdef DEBUG_LOG
|
||||
static void
|
||||
printInstructions(LogModuleObject inLogObject, ControlNode& inControlNode);
|
||||
|
||||
static void
|
||||
printInstructions(LogModuleObject inLogObject, ControlNode& inControlNode)
|
||||
{
|
||||
InstructionList& instructions = inControlNode.getInstructions();
|
||||
|
||||
for(InstructionList::iterator i = instructions.begin(); !instructions.done(i); i = instructions.advance(i))
|
||||
{
|
||||
instructions.get(i).printDebug(inLogObject);
|
||||
UT_OBJECTLOG(inLogObject, PR_LOG_ALWAYS, ("\n"));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
UT_DEFINE_LOG_MODULE(Backend);
|
||||
|
||||
void*
|
||||
translateControlGraphToNative(ControlGraph& inControlGraph, Method& inMethod)
|
||||
{
|
||||
VirtualRegisterManager vrMan(inControlGraph.pool);
|
||||
MdEmitter emitter(inControlGraph.pool, vrMan);
|
||||
CodeGenerator codeGenerator(inControlGraph.pool, emitter);
|
||||
|
||||
// break out constants
|
||||
explodeImmediatePrimitives(inControlGraph);
|
||||
|
||||
// * generate code
|
||||
DoublyLinkedList<ControlNode>::iterator j;
|
||||
for (j = inControlGraph.controlNodes.begin(); !inControlGraph.controlNodes.done(j); j = inControlGraph.controlNodes.advance(j))
|
||||
{
|
||||
ControlNode &cn = inControlGraph.controlNodes.get(j);
|
||||
|
||||
codeGenerator.generate(cn);
|
||||
|
||||
#ifdef PRINT_INSTRUCTIONS_BEFORE_REGALLOC
|
||||
cn.printRef(UT_LOG_MODULE(Backend));
|
||||
|
||||
UT_SET_LOG_LEVEL(Backend, PR_LOG_DEBUG);
|
||||
printInstructions(UT_LOG_MODULE(Backend), cn);
|
||||
#endif
|
||||
}
|
||||
|
||||
// If the debugger is enabled, generate pc2bci table
|
||||
if (VM::debugger.getEnabled()) {
|
||||
// Use an arbitrary number - needs to be set appropriately later
|
||||
inMethod.getPC2Bci().setSize(1000);
|
||||
}
|
||||
|
||||
// * register allocation
|
||||
if (!inControlGraph.dfsListIsValid())
|
||||
inControlGraph.dfsSearch();
|
||||
if (!inControlGraph.lndListIsValid())
|
||||
inControlGraph.lndSearch();
|
||||
|
||||
RegisterAllocator registerAllocator(inControlGraph.pool, inControlGraph.dfsList, inControlGraph.lndList, inControlGraph.nNodes, vrMan, emitter);
|
||||
registerAllocator.allocateRegisters();
|
||||
|
||||
#ifdef PRINT_INSTRUCTIONS_AFTER_REGALLOC
|
||||
for (j = inControlGraph.controlNodes.begin(); !inControlGraph.controlNodes.done(j); j = inControlGraph.controlNodes.advance(j))
|
||||
{
|
||||
ControlNode &cn = inControlGraph.controlNodes.get(j);
|
||||
|
||||
printInstructions(UT_LOG_MODULE(Backend), cn);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef NEW_CG_SCHEDULER
|
||||
ControlNodeScheduler cns(inControlGraph.pool, inControlGraph.dfsList, inControlGraph.nNodes);
|
||||
ControlNode** scheduledNodes = cns.getScheduledNodes();
|
||||
#else // NEW_CG_SCHEDULER
|
||||
ControlGraphScheduler cgs(inControlGraph, emitter);
|
||||
ControlNode** scheduledNodes = cgs.scheduleNodes();
|
||||
#endif // NEW_CG_SCHEDULER
|
||||
|
||||
// * output to memory
|
||||
NativeFormatter formatter(emitter, scheduledNodes, inControlGraph.nNodes);
|
||||
|
||||
void* func = formatter.format(inMethod);
|
||||
|
||||
IGVISUALIZE_ONLY(codeGenerator.visualize();)
|
||||
return (func);
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Transform all immediate operations into
|
||||
// operations which no longer intern a constant
|
||||
//
|
||||
// For example:
|
||||
//
|
||||
// vi2 = AddI_I 6, vi1
|
||||
//
|
||||
// becomes
|
||||
//
|
||||
// vi1 = Const_I 6
|
||||
// vi3 = Add_I vi1, vi2
|
||||
//
|
||||
static void
|
||||
explodeImmediatePrimitives(ControlGraph &cg)
|
||||
{
|
||||
cg.dfsSearch();
|
||||
ControlNode **dfsList = cg.dfsList;
|
||||
ControlNode **pcn = dfsList + cg.nNodes;
|
||||
|
||||
while (pcn != dfsList)
|
||||
{
|
||||
ControlNode &cn = **--pcn;
|
||||
|
||||
// Search the primitives backwards; explode primitives as necessary
|
||||
DoublyLinkedList<Primitive> &primitives = cn.getPrimitives();
|
||||
DoublyLinkedList<Primitive>::iterator primIter = primitives.end();
|
||||
while (!primitives.done(primIter))
|
||||
{
|
||||
Primitive &p = primitives.get(primIter);
|
||||
primIter = primitives.retreat(primIter);
|
||||
|
||||
// loop through all consumers of p; if a consumer is constant create
|
||||
// a new PrimConst node, and attach it to p
|
||||
DataConsumer* input;
|
||||
for (input = p.getInputsBegin(); input < p.getInputsEnd(); input++)
|
||||
{
|
||||
if (input->isConstant() && isRegOrMemKind(input->getKind()))
|
||||
{
|
||||
PrimConst *prim = new(cn.getPrimitivePool())
|
||||
PrimConst(input->getKind(), input->getConstant(),
|
||||
p.getBytecodeIndex());
|
||||
|
||||
input->setVariable(*prim);
|
||||
cn.appendPrimitive(*prim);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
/* -*- 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.
|
||||
*/
|
||||
// Backend.h
|
||||
//
|
||||
// Scott M. Silver
|
||||
//
|
||||
// Translate a ControlGraph into native code
|
||||
|
||||
#ifndef _H_BACKEND
|
||||
#define _H_BACKEND
|
||||
|
||||
struct Method;
|
||||
class ControlGraph;
|
||||
|
||||
void* translateControlGraphToNative(ControlGraph& inControlGraph, Method& inMethod);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,56 @@
|
|||
/* -*- 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.
|
||||
*/
|
||||
/*
|
||||
Burg.h
|
||||
*/
|
||||
|
||||
// Burg needs these
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
// Stuff that we use for burg
|
||||
#include "CodeGenerator.h"
|
||||
#include "Primitives.h"
|
||||
|
||||
typedef Primitive* NODEPTR_TYPE;
|
||||
|
||||
#define OP_LABEL(p) p->getOperation()
|
||||
#define STATE_LABEL(p) p->getBurgState()
|
||||
#define SET_STATE_LABEL(p, state) p->setBurgState(state)
|
||||
#define LEFT_CHILD(p) CodeGenerator::getExpressionLeftChild(p)
|
||||
#define RIGHT_CHILD(p) CodeGenerator::getExpressionRightChild(p)
|
||||
#define PANIC printf
|
||||
|
||||
extern short *burm_nts[];
|
||||
extern char *burm_string[];
|
||||
extern char * burm_opname[];
|
||||
extern char burm_arity[];
|
||||
extern short burm_cost[][4];
|
||||
extern char *burm_ntname[];
|
||||
|
||||
// burgs exported functions
|
||||
|
||||
int burm_rule(int state, int goalnt);
|
||||
int burm_state(int op, int l, int r);
|
||||
|
||||
int burm_label(NODEPTR_TYPE n);
|
||||
NODEPTR_TYPE * burm_kids(NODEPTR_TYPE p, int rulenumber, NODEPTR_TYPE *kids);
|
||||
NODEPTR_TYPE burm_child(NODEPTR_TYPE p, int index);
|
||||
int burm_op_label(NODEPTR_TYPE p);
|
||||
int burm_state_label(NODEPTR_TYPE p);
|
||||
|
|
@ -0,0 +1,222 @@
|
|||
/* -*- 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.
|
||||
*/
|
||||
|
||||
#include "Fundamentals.h"
|
||||
#include "CGScheduler.h"
|
||||
#include "ControlGraph.h"
|
||||
#include "InstructionEmitter.h"
|
||||
#include "GraphUtils.h"
|
||||
|
||||
|
||||
inline static bool
|
||||
isPredecessor(ControlNode& pred, ControlNode& node)
|
||||
{
|
||||
ControlEdge* limit = pred.getSuccessorsEnd();
|
||||
|
||||
for (ControlEdge* ptr = pred.getSuccessorsBegin(); ptr < limit; ptr++)
|
||||
if (&ptr->getTarget() == &node)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
ControlNode** ControlGraphScheduler::
|
||||
scheduleNodes()
|
||||
{
|
||||
ControlNode** orderedNodes = orderNodes();
|
||||
addAbsoluteBranches(orderedNodes);
|
||||
|
||||
return orderedNodes;
|
||||
}
|
||||
|
||||
// addAbsoluteBranches
|
||||
// Given an ordering of ControlNodes in the graph add necessary
|
||||
// implicit branches demanded by the ordering (ie to satisfy the ControlEdges)
|
||||
void ControlGraphScheduler::
|
||||
addAbsoluteBranches(ControlNode** orderedNodes)
|
||||
{
|
||||
Uint32 nNodes = cg.nNodes;
|
||||
|
||||
// for each ControlNode in orderedNodes, determine for each sequential pairing
|
||||
// if a falls through to b then don't add an absolute branch from a->b, otherwise
|
||||
// if the edge a->b does not exist, then add an absolute branch to where a should
|
||||
// connect.
|
||||
for (Uint32 n = 0; n < nNodes; n++)
|
||||
{
|
||||
ControlNode* thisNode = orderedNodes[n];
|
||||
ControlNode* nextNode = (n < (nNodes - 1)) ? orderedNodes[n+1] : (ControlNode *) 0;
|
||||
ControlEdge* branchEdge;
|
||||
|
||||
switch(thisNode->getControlKind())
|
||||
{
|
||||
case ckIf:
|
||||
case ckBlock:
|
||||
case ckExc:
|
||||
case ckAExc:
|
||||
case ckCatch:
|
||||
case ckBegin:
|
||||
branchEdge = &thisNode->nthSuccessor(0);
|
||||
break;
|
||||
|
||||
// these three ControlNodes never have any implicit branches
|
||||
case ckSwitch:
|
||||
case ckReturn:
|
||||
case ckEnd:
|
||||
case ckThrow:
|
||||
branchEdge = NULL;
|
||||
break;
|
||||
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
|
||||
if (branchEdge != NULL && nextNode != NULL && &branchEdge->getTarget() != nextNode)
|
||||
emitter.pushAbsoluteBranch(branchEdge->getSource(), branchEdge->getTarget());
|
||||
}
|
||||
}
|
||||
|
||||
ControlNode** ControlGraphScheduler::
|
||||
orderNodes()
|
||||
{
|
||||
Uint32 nNodes = cg.nNodes - 1;
|
||||
Uint32 myGeneration = ControlNode::getNextGeneration();
|
||||
ControlNode** nodes = new ControlNode*[nNodes];
|
||||
ControlNode** n_ptr = nodes;
|
||||
|
||||
SearchStackEntry<ControlEdge> *ceStack = new SearchStackEntry<ControlEdge>[nNodes];
|
||||
SearchStackEntry<ControlEdge> *ceSp = ceStack;
|
||||
|
||||
ControlEdge beginEdge;
|
||||
beginEdge.setTarget(cg.getBeginNode());
|
||||
cg.getEndNode().generation = myGeneration;
|
||||
|
||||
ControlEdge* n = &beginEdge;
|
||||
ControlEdge* l = &beginEdge + 1;
|
||||
|
||||
while(true)
|
||||
{
|
||||
if (n == l)
|
||||
{
|
||||
if (ceSp == ceStack)
|
||||
break;
|
||||
--ceSp;
|
||||
n = ceSp->next;
|
||||
l = ceSp->limit;
|
||||
}
|
||||
else
|
||||
{
|
||||
ControlNode& node = n++->getTarget();
|
||||
if (node.generation != myGeneration)
|
||||
{
|
||||
node.generation = myGeneration;
|
||||
node.schedulePos = Uint32(n_ptr - nodes);
|
||||
*n_ptr++ = &node;
|
||||
ceSp->next = n;
|
||||
ceSp->limit = l;
|
||||
ceSp++;
|
||||
n = node.getSuccessorsBegin();
|
||||
l = node.getSuccessorsEnd();
|
||||
}
|
||||
}
|
||||
}
|
||||
#ifndef WIN32 // ***** Visual C++ has a bug in the code for delete[].
|
||||
delete ceStack;
|
||||
#endif
|
||||
|
||||
Uint32* cnStack = new Uint32[nNodes];
|
||||
Uint32* cnSp = cnStack;
|
||||
|
||||
ControlNode** orderedNodes = new(cg.pool) ControlNode*[nNodes + 1];
|
||||
n_ptr = orderedNodes;
|
||||
Uint32 next;
|
||||
|
||||
#if DEBUG
|
||||
fill_n(orderedNodes, nNodes, (ControlNode *) 0);
|
||||
#endif
|
||||
|
||||
myGeneration = ControlNode::getNextGeneration();
|
||||
orderedNodes[nNodes] = &cg.getEndNode();
|
||||
orderedNodes[nNodes]->generation = myGeneration;
|
||||
*n_ptr++ = nodes[0];
|
||||
*cnSp++ = 0;
|
||||
next = 1;
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (next == 0)
|
||||
{
|
||||
if (cnSp == cnStack)
|
||||
break;
|
||||
next = *--cnSp;
|
||||
}
|
||||
else
|
||||
{
|
||||
ControlNode& node = *nodes[next];
|
||||
|
||||
if (node.generation == myGeneration)
|
||||
{
|
||||
next = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// if (node.hasControlKind(ckIf))
|
||||
const DoublyLinkedList<ControlEdge>& edges = node.getPredecessors();
|
||||
if (!edges.done(edges.advance(edges.begin()))) // more than one predecessor
|
||||
{
|
||||
for (DoublyLinkedList<ControlEdge>::iterator i = edges.begin(); !edges.done(i); i = edges.advance(i))
|
||||
{
|
||||
ControlNode& source = edges.get(i).getSource();
|
||||
if ((source.dfsNum > node.dfsNum) && (source.generation != myGeneration))
|
||||
{
|
||||
*cnSp++ = next;
|
||||
next = source.schedulePos;
|
||||
while (isPredecessor(*nodes[next - 1], *nodes[next]) &&
|
||||
(nodes[next - 1]->generation != myGeneration) && (next != node.schedulePos))
|
||||
next--;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (next == node.schedulePos)
|
||||
{
|
||||
node.generation = myGeneration;
|
||||
*n_ptr++ = &node;
|
||||
if (++next >= nNodes)
|
||||
next = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
node.generation = myGeneration;
|
||||
*n_ptr++ = &node;
|
||||
if (++next >= nNodes)
|
||||
next = 0;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
//delete cnStack;
|
||||
//delete [] nodes;
|
||||
|
||||
#if DEBUG
|
||||
for (Uint32 i = 0; i < nNodes + 1; i++)
|
||||
assert(orderedNodes[i]);
|
||||
#endif
|
||||
|
||||
return orderedNodes;
|
||||
}
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
/* -*- 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 _CG_SCHEDULER_H_
|
||||
#define _CG_SCHEDULER_H_
|
||||
|
||||
#include "Fundamentals.h"
|
||||
#include "Pool.h"
|
||||
|
||||
class ControlGraph;
|
||||
class ControlNode;
|
||||
class InstructionEmitter;
|
||||
|
||||
class ControlGraphScheduler
|
||||
{
|
||||
protected:
|
||||
ControlGraph& cg;
|
||||
InstructionEmitter& emitter;
|
||||
|
||||
ControlNode** orderNodes();
|
||||
void addAbsoluteBranches(ControlNode** orderedNodes);
|
||||
|
||||
public:
|
||||
ControlGraphScheduler(ControlGraph& mCg, InstructionEmitter& mEmitter) : cg(mCg), emitter(mEmitter) {}
|
||||
|
||||
ControlNode** scheduleNodes();
|
||||
};
|
||||
|
||||
#endif /* _CG_SCHEDULER_H_ */
|
|
@ -0,0 +1,458 @@
|
|||
/* -*- 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.
|
||||
*/
|
||||
// CodeGenerator.cpp
|
||||
//
|
||||
// Scott M. Silver
|
||||
// Peter Desantis
|
||||
//
|
||||
// A code generator is a loose organization of code which
|
||||
// finds roots in a given control node of BURG-labellable expression
|
||||
// trees. The trees are labelled, and an emit routine is called
|
||||
// for each labelled tree.
|
||||
|
||||
#define INCLUDE_EMITTER
|
||||
#include "CpuInfo.h"
|
||||
|
||||
#include "Vector.h"
|
||||
#include "Primitives.h"
|
||||
#include "CodeGenerator.h"
|
||||
#include "Burg.h"
|
||||
#include "Scheduler.h"
|
||||
#include "ControlNodes.h"
|
||||
#include "InstructionEmitter.h"
|
||||
|
||||
#ifdef USE_VISUALIZER
|
||||
#include "IGVisualizer.h"
|
||||
#endif
|
||||
|
||||
|
||||
// sFakeRegPrimitives
|
||||
//
|
||||
// A table of primitives which are used as the children
|
||||
// of primitives which are defined to be leaves in a
|
||||
// control node. They are indexed by DataKind, ie
|
||||
// vkInt, etc...
|
||||
//Primitive sFakeReg_V(coReg_V); // no such register
|
||||
Primitive sFakeReg_I(coReg_I, 0);
|
||||
Primitive sFakeReg_L(coReg_L, 0);
|
||||
Primitive sFakeReg_F(coReg_F, 0);
|
||||
Primitive sFakeReg_D(coReg_D, 0);
|
||||
Primitive sFakeReg_P(coReg_A, 0);
|
||||
Primitive sFakeReg_C(coReg_C, 0);
|
||||
Primitive sFakeReg_M(coReg_M, 0); // no such register
|
||||
//Primitive sFakeReg_T(coReg_T); // no such register
|
||||
|
||||
Primitive* sFakeRegPrimitives[nValueKinds] =
|
||||
{
|
||||
NULL, // no such register
|
||||
&sFakeReg_I,
|
||||
&sFakeReg_L,
|
||||
&sFakeReg_F,
|
||||
&sFakeReg_D,
|
||||
&sFakeReg_P,
|
||||
&sFakeReg_C,
|
||||
&sFakeReg_M,
|
||||
NULL // no such register
|
||||
};
|
||||
|
||||
|
||||
// This needs to be here because of header include problems.
|
||||
CodeGenerator::CodeGenerator(Pool& inPool, MdEmitter& inEmitter) :
|
||||
mPool(inPool),
|
||||
mEmitter(inEmitter)
|
||||
#ifdef USE_VISUALIZER
|
||||
,mVisualizer(*(new IGVisualizer()))
|
||||
#endif
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
// generate
|
||||
//
|
||||
// Emit argumetns for the begin node.
|
||||
// Find all roots of trees in inControlNode
|
||||
// Label each root
|
||||
// Call the emitter to emit for each root
|
||||
void CodeGenerator::
|
||||
generate(ControlNode& inControlNode)
|
||||
{
|
||||
Vector<RootPair> roots;
|
||||
|
||||
// First find all the roots in this control node
|
||||
findRoots(inControlNode, roots);
|
||||
|
||||
if (inControlNode.hasControlKind(ckBegin))
|
||||
mEmitter.emitArguments(inControlNode.getBeginExtra());
|
||||
else
|
||||
{
|
||||
// for each root label, and emit code
|
||||
RootPair* curRoot;
|
||||
|
||||
for (curRoot = roots.begin(); curRoot < roots.end(); curRoot++)
|
||||
{
|
||||
label(*(curRoot->root));
|
||||
emit(curRoot->root, 1);
|
||||
}
|
||||
}
|
||||
|
||||
// schedule and output instructions
|
||||
LinearInstructionScheduler scheduler;
|
||||
scheduler.schedule(roots, inControlNode);
|
||||
|
||||
#ifdef IGVISUALIZE
|
||||
mVisualizer.addRoots(roots);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
#ifdef IGVISUALIZE
|
||||
void CodeGenerator::
|
||||
visualize()
|
||||
{
|
||||
mVisualizer.visualize();
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
// label
|
||||
//
|
||||
// Label the treee rooted at inPrimitive
|
||||
void CodeGenerator::
|
||||
label(Primitive& inPrimitive)
|
||||
{
|
||||
|
||||
burm_label(&inPrimitive);
|
||||
}
|
||||
|
||||
|
||||
// emit
|
||||
//
|
||||
// Actually traverse the primitives and emit instructions
|
||||
// once labelled.
|
||||
void CodeGenerator::
|
||||
emit(Primitive* p, int goalnt)
|
||||
{
|
||||
int eruleno = burm_rule(p->getBurgState(), goalnt);
|
||||
short* nts = burm_nts[eruleno];
|
||||
Primitive* kids[10];
|
||||
int i;
|
||||
|
||||
if (eruleno == 0)
|
||||
{
|
||||
trespass("BURG matching -- no cover");
|
||||
}
|
||||
|
||||
burm_kids(p, eruleno, kids);
|
||||
|
||||
for (i = 0; nts[i]; i++)
|
||||
emit(kids[i], nts[i]);
|
||||
|
||||
mEmitter.emitPrimitive(*p, eruleno);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// instructionUseToInstruction
|
||||
//
|
||||
// Move to the definer of this use.
|
||||
//
|
||||
// There are three cases.
|
||||
//
|
||||
// 1. If there is a defining Instruction
|
||||
// in the Use, then that is the defining Instruction.
|
||||
//
|
||||
// 2. If the Use is Store or Cond Use, then if there is a
|
||||
// defining Instruction then it will be attached to the
|
||||
// DP at ID 0.
|
||||
//
|
||||
// 3. If the Use is a Register Use, then there must be some
|
||||
// VR associated with the Use. The Instruction which defines
|
||||
// the VR is the defining Instruction for this Use.
|
||||
//
|
||||
// It is possible that the result of 2 or 3 could be NULL after
|
||||
// emitting for a given ControlNode. This means that the resource
|
||||
// (outgoing edge) has not been defined, and is in another ControlNode.
|
||||
// (or there was a programmer error, how do we detect which one)
|
||||
Instruction* CodeGenerator::
|
||||
instructionUseToInstruction(InstructionUse& inIUse)
|
||||
{
|
||||
Instruction* nextInsn;
|
||||
|
||||
if (inIUse.src != NULL)
|
||||
nextInsn = inIUse.src;
|
||||
else
|
||||
{
|
||||
switch (inIUse.kind)
|
||||
{
|
||||
case udStore: case udCond:
|
||||
if (inIUse.name.dp != NULL)
|
||||
{
|
||||
if (inIUse.name.dp->getInstructionAnnotation() != NULL)
|
||||
nextInsn = inIUse.name.dp->getInstructionAnnotation();
|
||||
else
|
||||
nextInsn = NULL;
|
||||
}
|
||||
else
|
||||
nextInsn = NULL;
|
||||
break;
|
||||
case udRegister:
|
||||
//assert(inIUse.name.vr); can't check this because it's a VR Pointer
|
||||
nextInsn = inIUse.name.vr.getVirtualRegister().getDefiningInstruction();
|
||||
break;
|
||||
case udNone:
|
||||
nextInsn = NULL;
|
||||
break;
|
||||
case udOrder:
|
||||
nextInsn = inIUse.name.instruction;
|
||||
break;
|
||||
case udUninitialized:
|
||||
assert(false);
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
return (nextInsn);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// getExpressionLeftChild
|
||||
//
|
||||
// The BURG implementation of LEFT_CHILD
|
||||
Primitive* CodeGenerator::
|
||||
getExpressionLeftChild(Primitive* inPrimitive)
|
||||
{
|
||||
assert(inPrimitive);
|
||||
|
||||
bool hasIncomingStore = inPrimitive->hasCategory(pcLd) || inPrimitive->hasCategory(pcSt) || inPrimitive->hasCategory(pcCall);
|
||||
|
||||
DataConsumer* leftConsumer = &inPrimitive->nthInput(hasIncomingStore);
|
||||
|
||||
return (consumerToPrimitive(inPrimitive, leftConsumer));
|
||||
}
|
||||
|
||||
|
||||
// getExpressionRightChild
|
||||
//
|
||||
// Grab the right child of inPrimitive, skip over store edges
|
||||
// [The BURG implementation of RIGHT_CHILD]
|
||||
Primitive* CodeGenerator::
|
||||
getExpressionRightChild(Primitive* inPrimitive)
|
||||
{
|
||||
assert(inPrimitive);
|
||||
|
||||
DataConsumer* rightConsumer;
|
||||
|
||||
bool hasIncomingStore = inPrimitive->hasCategory(pcLd) || inPrimitive->hasCategory(pcSt) || inPrimitive->hasCategory(pcCall);
|
||||
|
||||
rightConsumer = &inPrimitive->nthInput(1 + hasIncomingStore);
|
||||
|
||||
return (consumerToPrimitive(inPrimitive, rightConsumer));
|
||||
}
|
||||
|
||||
|
||||
// consumerToPrimitive
|
||||
//
|
||||
// Takes a consumer of a value and finds the primitive which produces
|
||||
// the value that is consumed.
|
||||
//
|
||||
// an example:
|
||||
//
|
||||
// isLeaf
|
||||
//
|
||||
// child parent
|
||||
// P2 pr <-> co P1
|
||||
//
|
||||
// co: inConsumer
|
||||
// P1: inPrimitive
|
||||
//
|
||||
// co is a leaf edge if
|
||||
//
|
||||
// 1. co and pr (or P1 and P2) are in different control nodes -> return fake reg primitive
|
||||
// 2. P2 is a root -> return fake reg primitive
|
||||
//
|
||||
// else
|
||||
//
|
||||
// return P2
|
||||
Primitive* CodeGenerator::
|
||||
consumerToPrimitive(Primitive* inPrimitive, DataConsumer* inConsumer)
|
||||
{
|
||||
// if it's already a fake reg primitive it has no children
|
||||
if (inPrimitive->getOperation() >= coReg_V && inPrimitive->getOperation() <= coReg_I)
|
||||
return NULL;
|
||||
else if (inConsumer->isConstant())
|
||||
return (sFakeRegPrimitives[inConsumer->getKind()]);
|
||||
|
||||
DataNode& consumerChildNode = inConsumer->getVariable();
|
||||
Primitive* consumerChildPrimitive;
|
||||
|
||||
// extract P2 as above
|
||||
if (!consumerChildNode.hasCategory(pcPhi))
|
||||
consumerChildPrimitive = &Primitive::cast(consumerChildNode);
|
||||
else
|
||||
return (sFakeRegPrimitives[inConsumer->getKind()]); // phi node
|
||||
|
||||
// now perform the check to see if this edge connects to a "leaf" edge
|
||||
if (consumerChildPrimitive->getContainer() != inPrimitive->getContainer() ||
|
||||
isRoot(*consumerChildPrimitive))
|
||||
{
|
||||
return (sFakeRegPrimitives[inConsumer->getKind()]);
|
||||
}
|
||||
else
|
||||
return (consumerChildPrimitive);
|
||||
}
|
||||
|
||||
|
||||
// search through all primitives in a control node
|
||||
// return a vector of all the roots of expression trees
|
||||
void CodeGenerator::
|
||||
findRoots(ControlNode& inControlNode, Vector<RootPair>& outRoots)
|
||||
{
|
||||
DoublyLinkedList<Primitive>& primitives = inControlNode.getPrimitives();
|
||||
|
||||
for (DoublyLinkedList<Primitive>::iterator i = primitives.begin(); !primitives.done(i); i = primitives.advance(i))
|
||||
{
|
||||
Primitive& prim = primitives.get(i);
|
||||
|
||||
RootKind root = isRoot(prim);
|
||||
|
||||
if ((root == rkRoot) || (root == rkPrimary))
|
||||
{
|
||||
RootPair newRoot;
|
||||
newRoot.root = &prim;
|
||||
//prim.setRoot(true);
|
||||
if(root == rkPrimary)
|
||||
newRoot.isPrimary = true;
|
||||
else
|
||||
newRoot.isPrimary = false;
|
||||
outRoots.append(newRoot);
|
||||
}
|
||||
|
||||
// root member of prim defaults to false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 1. is a primitive which is a pcIfCond (if or switch) OR
|
||||
// 2. is a primitive which is a pcResult OR
|
||||
// 3. is a primitive all of whose outputs are in a different control node from its own OR
|
||||
// 4. is an interior primitive whose inputs are shared by two primitives in the same (cse)
|
||||
// 5. is a primitive connected to another pcCall/pcSysCall primitive
|
||||
// In cases 1 2 3 the root is primary
|
||||
RootKind CodeGenerator::
|
||||
isRoot(Primitive& inPrimitive)
|
||||
{
|
||||
bool isAtleastRoot = false;
|
||||
// 1 or 2
|
||||
if (inPrimitive.hasCategory(pcIfCond) || inPrimitive.hasCategory(pcResult) ||
|
||||
inPrimitive.hasCategory(pcSwitch))
|
||||
goto isRoot_rkPrimary;
|
||||
|
||||
// 3
|
||||
DataNode* curEdge;
|
||||
|
||||
isAtleastRoot = inPrimitive.hasCategory(pcSt) || inPrimitive.hasCategory(pcCall) || inPrimitive.hasCategory(pcSysCall);
|
||||
|
||||
// all dp's primitive's containers hooked to curEdge must not be in same control node as inPrimitive's container
|
||||
for (curEdge = inPrimitive.getOutgoingEdgesBegin(); curEdge < inPrimitive.getOutgoingEdgesEnd(); curEdge++)
|
||||
{
|
||||
const DoublyLinkedList<DataConsumer>& consumers = curEdge->getConsumers();
|
||||
|
||||
for ( DoublyLinkedList<DataConsumer>::iterator curConsumer = consumers.begin();
|
||||
!consumers.done(curConsumer);
|
||||
curConsumer = consumers.advance(curConsumer))
|
||||
{
|
||||
ControlNode* curConsumerContainer; // container of parentNode
|
||||
DataNode* node; // parentNode (node which consume's inPrimitive's input)
|
||||
|
||||
node = &consumers.get(curConsumer).getNode();
|
||||
|
||||
// can ignore LdV because it has another incoming edge
|
||||
isAtleastRoot |= ((node->getOutgoingEdgesEnd() - node->getOutgoingEdgesBegin() > 1) ||
|
||||
(node->hasCategory(pcCall) || node->hasCategory(pcSysCall)));
|
||||
|
||||
if (node->hasCategory(pcCall))
|
||||
{
|
||||
DataNode* calleeAddress = &node->nthInput(1).getVariable();
|
||||
if (&inPrimitive == calleeAddress)
|
||||
{
|
||||
isAtleastRoot = false;
|
||||
goto isRoot_rkNotRoot;
|
||||
}
|
||||
}
|
||||
|
||||
curConsumerContainer = node->getContainer();
|
||||
|
||||
if (!node->hasCategory(pcPhi) && curConsumerContainer == inPrimitive.getContainer())
|
||||
{
|
||||
// If there are any consumers in the current container, then the primitive is a root
|
||||
// iff it produces a cse----look for another consumer in the primitive's container
|
||||
|
||||
// Continue looking through the curEdge
|
||||
curConsumer = consumers.advance(curConsumer);
|
||||
for ( ;
|
||||
!consumers.done(curConsumer);
|
||||
curConsumer = consumers.advance(curConsumer))
|
||||
{
|
||||
node = &consumers.get(curConsumer).getNode();
|
||||
|
||||
if (node->hasCategory(pcCall) || node->hasCategory(pcSysCall))
|
||||
goto isRoot_rkRoot;
|
||||
|
||||
curConsumerContainer = node->getContainer();
|
||||
|
||||
if (curConsumerContainer == inPrimitive.getContainer()) // cse found.
|
||||
goto isRoot_rkRoot;
|
||||
}
|
||||
curEdge++;
|
||||
// Now continue looking through the other edges
|
||||
for (;curEdge < inPrimitive.getOutgoingEdgesEnd(); curEdge++)
|
||||
{
|
||||
const DoublyLinkedList<DataConsumer>& consumers = curEdge->getConsumers();
|
||||
|
||||
for ( DoublyLinkedList<DataConsumer>::iterator thisConsumer = consumers.begin();
|
||||
!consumers.done(thisConsumer);
|
||||
thisConsumer = consumers.advance(thisConsumer))
|
||||
{
|
||||
node = &consumers.get(thisConsumer).getNode();
|
||||
if (node->hasCategory(pcCall) || node->hasCategory(pcSysCall))
|
||||
goto isRoot_rkRoot;
|
||||
|
||||
curConsumerContainer = node->getContainer();
|
||||
|
||||
if (curConsumerContainer == inPrimitive.getContainer()) // cse found
|
||||
goto isRoot_rkRoot;
|
||||
}
|
||||
}
|
||||
// We did not find a cse => not a root
|
||||
goto isRoot_rkNotRoot;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if we passed through the above loop, 3. must have been met (fall through to rkPrimary)
|
||||
isRoot_rkPrimary:
|
||||
return (rkPrimary);
|
||||
isRoot_rkRoot:
|
||||
return (rkRoot);
|
||||
isRoot_rkNotRoot:
|
||||
return (isAtleastRoot ? rkRoot : rkNotRoot);
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,104 @@
|
|||
/* -*- 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.
|
||||
*/
|
||||
// CodeGenerator.h
|
||||
//
|
||||
// Scott M. Silver
|
||||
//
|
||||
|
||||
#ifndef CODEGENERATOR_H
|
||||
#define CODEGENERATOR_H
|
||||
|
||||
#include "Vector.h"
|
||||
#include "CpuInfo.h"
|
||||
|
||||
class Primitive;
|
||||
class ControlNode;
|
||||
class Pool;
|
||||
class DataConsumer;
|
||||
class Instruction;
|
||||
struct InstructionUse;
|
||||
#ifdef IGVISUALIZE
|
||||
class IGVisualizer;
|
||||
#endif
|
||||
|
||||
struct RenumberData
|
||||
{
|
||||
int neededVisits;
|
||||
int timesVisited;
|
||||
bool renumbered;
|
||||
};
|
||||
|
||||
// RootPair
|
||||
struct RootPair
|
||||
{
|
||||
Primitive* root; // root of a BURG labellable subtree
|
||||
bool isPrimary; // if this root is not reachable by any other root in
|
||||
// this ControlNode
|
||||
RenumberData data;
|
||||
};
|
||||
|
||||
enum RootKind
|
||||
{
|
||||
rkNotRoot,
|
||||
rkRoot,
|
||||
rkPrimary
|
||||
};
|
||||
|
||||
class CodeGenerator
|
||||
{
|
||||
protected:
|
||||
Pool& mPool;
|
||||
MdEmitter& mEmitter;
|
||||
|
||||
protected:
|
||||
void label(Primitive& inPrimitive);
|
||||
void emit(Primitive* p, int goalnt);
|
||||
|
||||
// roots, leaves
|
||||
void findRoots(ControlNode& inControlNode, Vector<RootPair>& outRoots);
|
||||
static RootKind isRoot(Primitive& inPrimitive);
|
||||
|
||||
public:
|
||||
CodeGenerator(Pool& inPool, MdEmitter& inEmitter);
|
||||
void generate(ControlNode& inControlNode);
|
||||
|
||||
// for BURG labeler
|
||||
static Primitive* consumerToPrimitive(Primitive* inPrimitive, DataConsumer* inConsumer);
|
||||
static Primitive* getExpressionLeftChild(Primitive* inPrimitive);
|
||||
static Primitive* getExpressionRightChild(Primitive* inPrimitive);
|
||||
|
||||
static Instruction* instructionUseToInstruction(InstructionUse& inIUse);
|
||||
|
||||
// visualization
|
||||
#ifdef IGVISUALIZE
|
||||
public:
|
||||
void visualize();
|
||||
protected:
|
||||
IGVisualizer& mVisualizer;
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif // CODEGENERATOR_H
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,748 @@
|
|||
/* -*- 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.
|
||||
*/
|
||||
|
||||
#include "Fundamentals.h"
|
||||
#include "Pool.h"
|
||||
#include "ControlNodes.h"
|
||||
#include "ControlNodeScheduler.h"
|
||||
#include "FastBitSet.h"
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// LoopHierarchySet
|
||||
|
||||
//
|
||||
// Create a new set of LoopHierachyNodes. Allocate the LoopHierarchyNode array of
|
||||
// length nLoops and the indexes array of length nNodes in the given pool.
|
||||
//
|
||||
LoopHierarchySet::LoopHierarchySet(Pool& pool, LoopHierarchyNode* nodesArray, Uint32 nNodes) : nodes(nodesArray)
|
||||
{
|
||||
indexes = new(pool) Int32[nNodes];
|
||||
nextFree = 0;
|
||||
|
||||
fill(indexes, &indexes[nNodes], -1);
|
||||
}
|
||||
|
||||
//
|
||||
// Return the set's element corresponding to the ControlNode dfsNum equals nodeIndex.
|
||||
//
|
||||
LoopHierarchyNode& LoopHierarchySet::operator [] (const Uint32 nodeIndex)
|
||||
{
|
||||
Int32 index = indexes[nodeIndex];
|
||||
|
||||
// If index is -1 then this node is not part of the set yet.
|
||||
if (index == -1) {
|
||||
index = nextFree++;
|
||||
indexes[nodeIndex] = index;
|
||||
}
|
||||
|
||||
return nodes[index];
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// ControlNodeScheduler
|
||||
|
||||
//
|
||||
// Return true if node is ready. We will call a node N ready if each of its
|
||||
// incoming edges E: A->N are long.
|
||||
//
|
||||
inline bool nodeIsReady(ControlNode& node)
|
||||
{
|
||||
const DoublyLinkedList<ControlEdge>& predecessors = node.getPredecessors();
|
||||
for (DoublyLinkedList<ControlEdge>::iterator i = predecessors.begin(); !predecessors.done(i); i = predecessors.advance(i))
|
||||
if (!predecessors.get(i).longFlag)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
//
|
||||
// Return true if node is ready with respect to 'respect'. We will call a node N
|
||||
// ready with respect to node P if, for each of N's incoming edges E: A->N, either
|
||||
// E is long or A equals P.
|
||||
//
|
||||
inline bool nodeIsReadyWithRespectTo(ControlNode& node, ControlNode& respect)
|
||||
{
|
||||
const DoublyLinkedList<ControlEdge>& predecessors = node.getPredecessors();
|
||||
for (DoublyLinkedList<ControlEdge>::iterator i = predecessors.begin(); !predecessors.done(i); i = predecessors.advance(i)) {
|
||||
ControlEdge& edge = predecessors.get(i);
|
||||
if (!edge.longFlag && (&edge.getSource() != &respect))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//
|
||||
// Schedule the working nodes in loopToSchedule.
|
||||
//
|
||||
void ControlNodeScheduler::scheduleLoop(LoopHierarchyNode& loopToSchedule)
|
||||
{
|
||||
#if defined(DEBUG_SCHEDULER)
|
||||
fprintf(stdout, "ControlNodeScheduler: Will schedule loop headed by N%d\n", loopToSchedule.header);
|
||||
#endif
|
||||
|
||||
//
|
||||
// Determine the nodes in a region.
|
||||
//
|
||||
|
||||
FastBitSet& REG = loopToSchedule.nodes; // REG is the region of nodes contained in this loop.
|
||||
FastBitSet Rlocal(nNodes); // Rlocal is the set of nodes in REG that are not contained in any subloop of REG.
|
||||
FastBitSet Rsubheaders(nNodes); // Rsubheaders is the set of headers of immediate subloops of REG.
|
||||
FastBitSet Rcombined(nNodes); // Rcombined is the union of Rlocal and Rsubheaders.
|
||||
FastBitSet R(nNodes); // R is the intersection of Rcombined and the set of all working nodes.
|
||||
FastBitSet RR(nNodes); // RR is the intersection of REG and the set of all working nodes.
|
||||
|
||||
Rlocal = REG;
|
||||
DoublyLinkedList<LoopHierarchyNode>& subLoops = loopToSchedule.getSuccessors();
|
||||
for (DoublyLinkedList<LoopHierarchyNode>::iterator i = subLoops.begin(); !subLoops.done(i); i = subLoops.advance(i)) {
|
||||
LoopHierarchyNode& subLoop = subLoops.get(i);
|
||||
Rsubheaders.set(subLoop.header);
|
||||
Rlocal -= subLoop.nodes;
|
||||
}
|
||||
|
||||
Rcombined = Rlocal;
|
||||
Rcombined |= Rsubheaders;
|
||||
|
||||
R = Rcombined;
|
||||
RR = REG;
|
||||
|
||||
FastBitSet nonWorkingNodes(nNodes);
|
||||
for (Int32 j = REG.firstOne(); j != -1; j = REG.nextOne(j))
|
||||
if (!dfsList[j]->workingNode)
|
||||
nonWorkingNodes.set(j);
|
||||
|
||||
R -= nonWorkingNodes;
|
||||
RR -= nonWorkingNodes;
|
||||
|
||||
//
|
||||
// Initialize long edge flag for each edge E: A->B for which at least one of A or B is in R
|
||||
//
|
||||
|
||||
Uint32 generation = ControlNode::getNextGeneration();
|
||||
|
||||
for (Int32 j = R.firstOne(); j != -1; j = R.nextOne(j)) {
|
||||
ControlNode& node = *dfsList[j];
|
||||
|
||||
ControlEdge* limit = node.getSuccessorsEnd();
|
||||
for (ControlEdge* edge = node.getSuccessorsBegin(); edge != limit; edge++)
|
||||
if (edge->getTarget().generation != generation)
|
||||
initializeLongEdgeFlag(*edge, loopToSchedule, RR, Rlocal);
|
||||
|
||||
const DoublyLinkedList<ControlEdge>& predecessors = node.getPredecessors();
|
||||
for (DoublyLinkedList<ControlEdge>::iterator p = predecessors.begin(); !predecessors.done(p); p = predecessors.advance(p)) {
|
||||
ControlEdge& edge = predecessors.get(p);
|
||||
if (edge.getSource().generation != generation)
|
||||
initializeLongEdgeFlag(edge, loopToSchedule, RR, Rlocal);
|
||||
}
|
||||
|
||||
node.generation = generation;
|
||||
}
|
||||
|
||||
//
|
||||
// Linear scheduling.
|
||||
//
|
||||
|
||||
FastBitSet W(nNodes); // W is the set of nodes in the region remaining to be scheduled.
|
||||
W = R; // Initially W contains all nodes in R except the loop header.
|
||||
W.clear(loopToSchedule.header); //
|
||||
|
||||
Clique** cliqueStack = new(pool) Clique*[nNodes]; // Stack of Cliques waiting to be scheduled.
|
||||
Clique** cliqueStackPointer = cliqueStack; // Stack pointer.
|
||||
|
||||
Int32 P = loopToSchedule.header; // P is the last node scheduled. It is initially the loop header.
|
||||
Int32 N; // Next node to schedule.
|
||||
|
||||
Clique* currentClique = new(pool) Clique();
|
||||
currentClique->addLast(scheduledNodes[P]); // The loop header is always the first node of the first clique.
|
||||
nodesAlreadyScheduled.set(P);
|
||||
|
||||
loopToSchedule.cliques.addLast(*currentClique);
|
||||
|
||||
while (!W.empty() || (cliqueStackPointer != cliqueStack)) {
|
||||
// Alternative 1.
|
||||
// If there exists a node N in W such that there exists at least one short edge from P to N
|
||||
// and node N is ready with respect to P, then we pick any such node N to be the next node.
|
||||
ControlNode& nodeP = *dfsList[P];
|
||||
|
||||
ControlEdge* limit = nodeP.getSuccessorsEnd();
|
||||
for (ControlEdge* edge = nodeP.getSuccessorsBegin(); edge != limit; edge++) {
|
||||
ControlNode& target = edge->getTarget();
|
||||
if (!edge->longFlag && nodeIsReadyWithRespectTo(target, nodeP) && W.test(target.dfsNum)) {
|
||||
N = target.dfsNum;
|
||||
goto foundNextNode;
|
||||
}
|
||||
}
|
||||
|
||||
// Alternative 2.
|
||||
// Otherwise, if the stack isn't empty, we pop a clique Ck from the stack, make it become the next clique of
|
||||
// this region, make every edge from P to any node A remaining in W into a long edge, set P to be the
|
||||
// last node of Ck, and continue with alternative 1 above.
|
||||
if (cliqueStackPointer != cliqueStack) {
|
||||
Clique* nextClique = *--cliqueStackPointer;
|
||||
loopToSchedule.cliques.addLast(*nextClique);
|
||||
|
||||
for (ControlEdge* edge = nodeP.getSuccessorsBegin(); edge != limit; edge++)
|
||||
if (W.test(edge->getTarget().dfsNum))
|
||||
edge->longFlag = true;
|
||||
|
||||
P = nextClique->last().dfsNum;
|
||||
currentClique = nextClique;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Alternative 3.
|
||||
// Otherwise, there must exist a node N in W such that node N is ready with respect to P.
|
||||
// We pick any such node N to be the next node.
|
||||
for (N = W.firstOne(); N != -1; N = W.nextOne(N))
|
||||
if (nodeIsReadyWithRespectTo(*dfsList[N], *dfsList[P]))
|
||||
goto foundNextNode;
|
||||
|
||||
PR_ASSERT(false); // Shouldn't happen.
|
||||
|
||||
foundNextNode:
|
||||
|
||||
|
||||
// After we have picked node N, we remove N from the set W and make every edge
|
||||
// from P to any node A remaining in W into a long edge.
|
||||
W.clear(N);
|
||||
|
||||
for (ControlEdge* e = nodeP.getSuccessorsBegin(); e != limit; e++)
|
||||
if (W.test(e->getTarget().dfsNum))
|
||||
e->longFlag = true;
|
||||
|
||||
// If N is not the header of a subloop.
|
||||
if (!Rsubheaders.test(N)) {
|
||||
ControlNode& nodeN = *dfsList[N];
|
||||
bool nodePhasEdgeToNodeN = false;
|
||||
|
||||
for (ControlEdge* edge = nodeP.getSuccessorsBegin(); edge != limit; edge++) {
|
||||
if (&edge->getTarget() == &nodeN) {
|
||||
// If there exists some edge from P to N then we append node N to the current
|
||||
// clique in our region's schedule
|
||||
nodePhasEdgeToNodeN = true;
|
||||
currentClique->addLast(scheduledNodes[N]);
|
||||
nodesAlreadyScheduled.set(N);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!nodePhasEdgeToNodeN) {
|
||||
// There is node edge from P to N. We start a new clique in this region
|
||||
// and make N this clique's first node.
|
||||
Clique* newClique = new(pool) Clique();
|
||||
|
||||
newClique->addLast(scheduledNodes[N]);
|
||||
loopToSchedule.cliques.addLast(*newClique);
|
||||
nodesAlreadyScheduled.set(N);
|
||||
currentClique = newClique;
|
||||
}
|
||||
|
||||
P = N;
|
||||
} else { // N is the header of a subloop.
|
||||
LoopHierarchyNode* subLoop = NULL;
|
||||
|
||||
// Find the clique of the subloop that contains node N.
|
||||
DoublyLinkedList<LoopHierarchyNode>& subLoops = loopToSchedule.getSuccessors();
|
||||
for (DoublyLinkedList<LoopHierarchyNode>::iterator i = subLoops.begin(); !subLoops.done(i); i = subLoops.advance(i)) {
|
||||
subLoop = &subLoops.get(i);
|
||||
if (subLoop->header == N)
|
||||
break;
|
||||
}
|
||||
PR_ASSERT(subLoop);
|
||||
|
||||
Clique* subLoopClique = NULL;
|
||||
for (DoublyLinkedList<Clique>::iterator c = subLoop->cliques.begin(); !subLoop->cliques.done(c); c = subLoop->cliques.advance(c)) {
|
||||
bool foundClique = false;
|
||||
subLoopClique = &subLoop->cliques.get(c);
|
||||
|
||||
for (DoublyLinkedList<ScheduledNode>::iterator n = subLoopClique->begin(); !subLoopClique->done(n); n = subLoopClique->advance(n))
|
||||
if (subLoopClique->get(n).dfsNum == N) {
|
||||
foundClique = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (foundClique)
|
||||
break;
|
||||
}
|
||||
PR_ASSERT(subLoopClique);
|
||||
|
||||
ControlNode& nodeN = *dfsList[N];
|
||||
bool nodePhasEdgeToNodeN = false;
|
||||
|
||||
// If N is the first node of the subloop's clique and there exists some edge from P to N then we append
|
||||
// the entire clique to the current clique. Otherwise, we start a new clique and make the subloop's
|
||||
// clique become this new clique's beginning.
|
||||
for (ControlEdge* edge = nodeP.getSuccessorsBegin(); edge != limit; edge++)
|
||||
if (&edge->getTarget() == &nodeN) {
|
||||
nodePhasEdgeToNodeN = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!((subLoopClique->first().dfsNum == N) && nodePhasEdgeToNodeN)) {
|
||||
currentClique = new(pool) Clique();
|
||||
loopToSchedule.cliques.addLast(*currentClique);
|
||||
}
|
||||
|
||||
for (DoublyLinkedList<ScheduledNode>::iterator s = subLoopClique->begin(); !subLoopClique->done(s);) {
|
||||
ScheduledNode& node = subLoopClique->get(s);
|
||||
s = subLoopClique->advance(s);
|
||||
node.remove();
|
||||
currentClique->addLast(node);
|
||||
}
|
||||
|
||||
subLoopClique->remove();
|
||||
|
||||
// If the subloop contained more than one clique, we push all remaining cliques onto the stack.
|
||||
// We are carefull to keep the scheduling order.
|
||||
for (DoublyLinkedList<Clique>::iterator t = subLoop->cliques.end(); !subLoop->cliques.done(t);) {
|
||||
Clique& slc = subLoop->cliques.get(t);
|
||||
t = subLoop->cliques.retreat(t);
|
||||
|
||||
slc.remove();
|
||||
*cliqueStackPointer++ = &slc;
|
||||
}
|
||||
|
||||
// The last node scheduled is the last node of the current clique.
|
||||
P = currentClique->last().dfsNum;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Loop scheduling
|
||||
//
|
||||
|
||||
// Find the last clique in this region such that the last node N of this clique satisfies the following properties:
|
||||
// 1.There exists an edge from N to H.
|
||||
// 2.There are no short edges E: N->A for which A is outside R.
|
||||
// 3.N is not a switch node.
|
||||
// 4.N is not H.
|
||||
for (DoublyLinkedList<Clique>::iterator c = loopToSchedule.cliques.end(); !loopToSchedule.cliques.done(c); c = loopToSchedule.cliques.retreat(c)) {
|
||||
Clique& clique = loopToSchedule.cliques.get(c);
|
||||
ControlNode& lastNode = *dfsList[clique.last().dfsNum];
|
||||
|
||||
bool hasEdgeToHeader = false;
|
||||
bool noShortEdgeOutsiteR = true;
|
||||
|
||||
ControlEdge* limit = lastNode.getSuccessorsEnd();
|
||||
for (ControlEdge* edge = lastNode.getSuccessorsBegin(); edge != limit; edge++) {
|
||||
if (edge->getTarget().dfsNum == loopToSchedule.header)
|
||||
hasEdgeToHeader = true;
|
||||
if (!edge->longFlag && R.test(edge->getTarget().dfsNum))
|
||||
noShortEdgeOutsiteR = false;
|
||||
}
|
||||
|
||||
if (hasEdgeToHeader && noShortEdgeOutsiteR && (!lastNode.hasControlKind(ckSwitch)) && (lastNode.dfsNum != loopToSchedule.header)) {
|
||||
// We found this clique. We remove the last node N of this clique and prepend it to this
|
||||
// subloop's first clique. Now as long as this clique ends with some node N' that satisfies
|
||||
// all of the properties below, we remove N' from the end of this clique and prepend it
|
||||
// to this subloop's first clique.
|
||||
//
|
||||
// 1.There are no short edges E: N'->A for which A is outside R.
|
||||
// 2.N' is not a switch node.
|
||||
// 3.N' is not the loop's header.
|
||||
ScheduledNode& sNode = scheduledNodes[lastNode.dfsNum];
|
||||
sNode.remove();
|
||||
loopToSchedule.cliques.first().addFirst(sNode);
|
||||
|
||||
for (DoublyLinkedList<ScheduledNode>::iterator n = clique.end(); !clique.done(n);) {
|
||||
ControlNode& node = *dfsList[clique.get(n).dfsNum];
|
||||
n = clique.retreat(n);
|
||||
|
||||
bool noShortEdgeOutsiteR = true;
|
||||
ControlEdge* limit = node.getSuccessorsEnd();
|
||||
for (ControlEdge* edge = node.getSuccessorsBegin(); edge != limit; edge++)
|
||||
if (!edge->longFlag && R.test(edge->getTarget().dfsNum)) {
|
||||
noShortEdgeOutsiteR = false;
|
||||
break;
|
||||
}
|
||||
if (noShortEdgeOutsiteR && (!node.hasControlKind(ckSwitch)) && (node.dfsNum != loopToSchedule.header)) {
|
||||
// The condition is satisfied.
|
||||
ScheduledNode& sNode = scheduledNodes[lastNode.dfsNum];
|
||||
sNode.remove();
|
||||
loopToSchedule.cliques.first().addFirst(sNode);
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
if (clique.empty())
|
||||
clique.remove();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(DEBUG_SCHEDULER)
|
||||
fprintf(stdout, "done scheduled nodes: [ ");
|
||||
for (DoublyLinkedList<Clique>::iterator x = loopToSchedule.cliques.begin(); !loopToSchedule.cliques.done(x); x = loopToSchedule.cliques.advance(x)) {
|
||||
fprintf(stdout, "[ ");
|
||||
Clique& clique = loopToSchedule.cliques.get(x);
|
||||
for (DoublyLinkedList<ScheduledNode>::iterator z = clique.begin(); !clique.done(z); z = clique.advance(z))
|
||||
fprintf(stdout, "N%d ", clique.get(z).dfsNum);
|
||||
fprintf(stdout, "] ");
|
||||
}
|
||||
fprintf(stdout, "]\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
//
|
||||
// Set the long edge flag to the given edge in region.
|
||||
//
|
||||
void ControlNodeScheduler::initializeLongEdgeFlag(ControlEdge& edge, LoopHierarchyNode& inLoop, FastBitSet& RR, FastBitSet& Rlocal)
|
||||
{
|
||||
Int32 A = edge.getSource().dfsNum;
|
||||
|
||||
if (!RR.test(A)) { // Condition 1 - A is outside RR.
|
||||
edge.longFlag = true;
|
||||
return;
|
||||
}
|
||||
|
||||
Int32 B = edge.getTarget().dfsNum;
|
||||
|
||||
if (A >= B) { // Condition 3 - edge is a backward edge.
|
||||
edge.longFlag = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (edge.getSource().hasControlKind(ckSwitch)) { // Condition 4 - A is a switch node.
|
||||
edge.longFlag = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Rlocal.test(A)) { // Condition 5 - A is in a subloop.
|
||||
|
||||
DoublyLinkedList<LoopHierarchyNode>& subLoops = inLoop.getSuccessors();
|
||||
for (DoublyLinkedList<LoopHierarchyNode>::iterator l = subLoops.begin(); !subLoops.done(l); l = subLoops.advance(l)) {
|
||||
|
||||
LoopHierarchyNode& subLoop = subLoops.get(l);
|
||||
if (subLoop.nodes.test(A)) { // subLoop is the largest inLoop's subloop that contains A.
|
||||
for (DoublyLinkedList<Clique>::iterator c = subLoop.cliques.begin(); !subLoop.cliques.done(c); c = subLoop.cliques.advance(c))
|
||||
if (subLoop.cliques.get(c).last().dfsNum == A) { // A is the last node of this clique.
|
||||
edge.longFlag = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// If we get there then A is not the last node of some cliques
|
||||
edge.longFlag = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!Rlocal.test(B)) { // Condition 6 - B is in a subloop.
|
||||
|
||||
DoublyLinkedList<LoopHierarchyNode>& subLoops = inLoop.getSuccessors();
|
||||
for (DoublyLinkedList<LoopHierarchyNode>::iterator l = subLoops.begin(); !subLoops.done(l); l = subLoops.advance(l)) {
|
||||
|
||||
LoopHierarchyNode& subLoop = subLoops.get(l);
|
||||
if (subLoop.nodes.test(B)) { // subLoop is the largest inLoop's subloop that contains B.
|
||||
for (DoublyLinkedList<Clique>::iterator c = subLoop.cliques.begin(); !subLoop.cliques.done(c); c = subLoop.cliques.advance(c))
|
||||
if (subLoop.cliques.get(c).last().dfsNum == B) { // B is the last node of this clique.
|
||||
edge.longFlag = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// If we get there then B is not the last node of some cliques
|
||||
edge.longFlag = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Condition 2 - edge is any exception or return edge.
|
||||
switch (edge.getSource().getControlKind()) {
|
||||
case ckCatch: case ckEnd:
|
||||
edge.longFlag = true;
|
||||
return;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
switch (edge.getTarget().getControlKind()) {
|
||||
case ckCatch: case ckEnd:
|
||||
edge.longFlag = true;
|
||||
return;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
edge.longFlag = false; // Condition 7.
|
||||
}
|
||||
|
||||
//
|
||||
// Fill the dominatorsMatrix with the dominators of all the nodes in this graph.
|
||||
// Each row is a bitset of the dominators' dfsNum for the ControlNode which dfsNum is the row index.
|
||||
// dominatorsMatrix must have (nNodes + 1) rows & nNodes columns. (One extra row is used for
|
||||
// temporary calculations).
|
||||
//
|
||||
void ControlNodeScheduler::findDominators()
|
||||
{
|
||||
// Initially each ControlNode is dominated by all the other ControlNodes.
|
||||
dominatorsMatrix.set();
|
||||
|
||||
// The Begin Node is initialized independently as it is only dominated by itself.
|
||||
dominatorsMatrix.clearRow(0);
|
||||
dominatorsMatrix.set(0, 0);
|
||||
|
||||
bool changed; // loop condition.
|
||||
|
||||
do {
|
||||
changed = false;
|
||||
|
||||
for (Uint32 n = 1; n < nNodes; n++) {
|
||||
dominatorsMatrix.setRow(nNodes);
|
||||
|
||||
// The dominators of a ControlNode are the intersection of the dominators of its
|
||||
// predecessors plus itself.
|
||||
const DoublyLinkedList<ControlEdge>& predecessors = dfsList[n]->getPredecessors();
|
||||
for (DoublyLinkedList<ControlEdge>::iterator i = predecessors.begin(); !predecessors.done(i); i = predecessors.advance(i))
|
||||
dominatorsMatrix.andRows(predecessors.get(i).getSource().dfsNum, nNodes);
|
||||
dominatorsMatrix.set(nNodes, n);
|
||||
|
||||
if (!dominatorsMatrix.compareRows(nNodes, n)) {
|
||||
changed = true;
|
||||
dominatorsMatrix.copyRows(nNodes, n);
|
||||
}
|
||||
}
|
||||
} while(changed);
|
||||
}
|
||||
|
||||
//
|
||||
// Find the loop headers in the given array of ControlNodes & return the number
|
||||
// of headers found.
|
||||
//
|
||||
Uint32 ControlNodeScheduler::findLoopHeaders()
|
||||
{
|
||||
Uint32 nLoopHeaders = 0; // Number of loop headers in this graph.
|
||||
|
||||
findDominators();
|
||||
|
||||
for (Uint32 n = 0; n < nNodes; n++) {
|
||||
ControlNode& node = *dfsList[n];
|
||||
Uint32 nodeIndex = node.dfsNum;
|
||||
|
||||
const DoublyLinkedList<ControlEdge>& predecessors = node.getPredecessors();
|
||||
for (DoublyLinkedList<ControlEdge>::iterator i = predecessors.begin(); !predecessors.done(i); i = predecessors.advance(i)) {
|
||||
Uint32 predecessorIndex = predecessors.get(i).getSource().dfsNum;
|
||||
|
||||
// A loop header is a node with an incoming backward edge. This edge's source must be dominated
|
||||
// by itself (called regular backward edge).
|
||||
if ((predecessorIndex >= nodeIndex) && dominatorsMatrix.test(predecessorIndex, nodeIndex)) {
|
||||
loopHeaders.set(nodeIndex);
|
||||
nLoopHeaders++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!loopHeaders.test(0)) {
|
||||
// The begin node is always considered to be a loop header (even if it's not).
|
||||
loopHeaders.set(0);
|
||||
nLoopHeaders++;
|
||||
}
|
||||
|
||||
return nLoopHeaders;
|
||||
}
|
||||
|
||||
//
|
||||
// Build the LoopHierarchyTree, find the nodes for each loop and return the top of the tree.
|
||||
//
|
||||
LoopHierarchyNode& ControlNodeScheduler::buildLoopHierarchyTree()
|
||||
{
|
||||
Uint32* nodeStack = new(pool) Uint32[nNodes]; // Stack of nodes to look at.
|
||||
|
||||
Uint32 nLoopHeaders = findLoopHeaders();
|
||||
|
||||
LoopHierarchyNode* loopNodes = new(pool) LoopHierarchyNode[nLoopHeaders];
|
||||
LoopHierarchySet loopSet(pool, loopNodes, nNodes); // Set of the loop headers in this graph.
|
||||
|
||||
// If the BeginNode is considered to be a loop then its body
|
||||
// contains all the nodes in this graph.
|
||||
LoopHierarchyNode& beginNode = loopSet[0];
|
||||
beginNode.header = 0;
|
||||
beginNode.nodes.sizeTo(pool, nNodes);
|
||||
beginNode.nodes.set(0, nNodes - 1);
|
||||
|
||||
for (Int32 h = loopHeaders.nextOne(0); h != -1; h = loopHeaders.nextOne(h)) {
|
||||
//
|
||||
// Place this loop in the tree.
|
||||
//
|
||||
|
||||
FastBitSet dominators(dominatorsMatrix.getRow(h), nNodes);
|
||||
|
||||
Int32 parent = h;
|
||||
|
||||
do // parent must be a loop header && this node must be included in the parent's loop nodes.
|
||||
parent = dominators.previousOne(parent);
|
||||
while (!(loopHeaders.test(parent) && loopSet[parent].nodes.test(h)));
|
||||
|
||||
loopSet[parent].addSuccessor(loopSet[h]);
|
||||
|
||||
//
|
||||
// Find this loop's nodes.
|
||||
//
|
||||
|
||||
Uint32* stackPointer = nodeStack;
|
||||
LoopHierarchyNode& loop = loopSet[h];
|
||||
|
||||
// Initialize the loop header's variables.
|
||||
loop.header = h;
|
||||
loop.nodes.sizeToAndClear(pool, nNodes);
|
||||
loop.nodes.set(h);
|
||||
|
||||
// Push the tails of this loop. Each tail must be dominated by the loop header.
|
||||
const DoublyLinkedList<ControlEdge>& headerPredecessors = dfsList[h]->getPredecessors();
|
||||
for (DoublyLinkedList<ControlEdge>::iterator i = headerPredecessors.begin(); !headerPredecessors.done(i); i = headerPredecessors.advance(i)) {
|
||||
Uint32 predecessorIndex = headerPredecessors.get(i).getSource().dfsNum;
|
||||
|
||||
if ((predecessorIndex > Uint32(h)) && dominatorsMatrix.test(predecessorIndex, h)) {
|
||||
loop.nodes.set(predecessorIndex);
|
||||
*stackPointer++ = predecessorIndex;
|
||||
}
|
||||
}
|
||||
|
||||
while (stackPointer != nodeStack) {
|
||||
Uint32 n = *--stackPointer;
|
||||
|
||||
const DoublyLinkedList<ControlEdge>& predecessors = dfsList[n]->getPredecessors();
|
||||
for (DoublyLinkedList<ControlEdge>::iterator i = predecessors.begin(); !predecessors.done(i); i = predecessors.advance(i)) {
|
||||
Uint32 predecessorIndex = predecessors.get(i).getSource().dfsNum;
|
||||
if (!loop.nodes.test(predecessorIndex)) {
|
||||
loop.nodes.set(predecessorIndex);
|
||||
*stackPointer++ = predecessorIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return loopSet[0];
|
||||
}
|
||||
|
||||
//
|
||||
// Return an array of scheduled nodes. This array has nNodes elements.
|
||||
//
|
||||
ControlNode** ControlNodeScheduler::getScheduledNodes()
|
||||
{
|
||||
// Initialize the ScheduledNodes.
|
||||
scheduledNodes = new(pool) ScheduledNode[nNodes];
|
||||
for (Uint32 i = 0; i < nNodes; i++) {
|
||||
ScheduledNode& node = scheduledNodes[i];
|
||||
node.dfsNum = i;
|
||||
}
|
||||
|
||||
nodesAlreadyScheduled.sizeToAndClear(nNodes);
|
||||
|
||||
//
|
||||
// Determine main nodes (called working nodes).
|
||||
//
|
||||
|
||||
Uint32 generation = ControlNode::getNextGeneration();
|
||||
Uint32* nodeStack = new(pool) Uint32[nNodes]; // Stack of nodes to look at.
|
||||
Uint32* stackPointer = nodeStack;
|
||||
*stackPointer++ = 0; // Push the beginNode.
|
||||
|
||||
while (stackPointer != nodeStack) {
|
||||
Uint32 n = *--stackPointer;
|
||||
ControlNode& node = *dfsList[n];
|
||||
|
||||
node.workingNode = true;
|
||||
|
||||
ControlEdge* limit = node.getSuccessorsEnd();
|
||||
for (ControlEdge* edge = node.getSuccessorsBegin(); edge != limit; edge++) {
|
||||
ControlNode& successor = edge->getTarget();
|
||||
|
||||
if (successor.generation != generation) {
|
||||
switch(successor.getControlKind()) {
|
||||
case ckCatch:
|
||||
case ckEnd:
|
||||
// We are not following the exception edges.
|
||||
break;
|
||||
|
||||
default:
|
||||
successor.generation = generation;
|
||||
*stackPointer++ = successor.dfsNum;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Schedule the loops from the inside out.
|
||||
//
|
||||
|
||||
struct EdgeStack
|
||||
{
|
||||
DoublyLinkedList<LoopHierarchyNode>* linkedList;
|
||||
DoublyLinkedList<LoopHierarchyNode>::iterator position;
|
||||
};
|
||||
|
||||
LoopHierarchyNode& top = buildLoopHierarchyTree();
|
||||
|
||||
EdgeStack *edgeStack = new(pool) EdgeStack[nNodes];
|
||||
DoublyLinkedList<LoopHierarchyNode> edgeToBeginNodeList;
|
||||
edgeToBeginNodeList.addLast(top);
|
||||
|
||||
DoublyLinkedList<LoopHierarchyNode>* currentList = &edgeToBeginNodeList;
|
||||
DoublyLinkedList<LoopHierarchyNode>::iterator currentIterator = edgeToBeginNodeList.begin();
|
||||
EdgeStack *edgeStackPointer = edgeStack;
|
||||
|
||||
while (true) {
|
||||
if (currentList->done(currentIterator)) {
|
||||
if (edgeStackPointer == edgeStack)
|
||||
break;
|
||||
|
||||
--edgeStackPointer;
|
||||
currentList = edgeStackPointer->linkedList;
|
||||
currentIterator = edgeStackPointer->position;
|
||||
|
||||
// At this point all the loops below this one in the loop hierarchy tree have
|
||||
// been scheduled.
|
||||
LoopHierarchyNode& loopToSchedule = currentList->get(currentIterator->prev);
|
||||
|
||||
scheduleLoop(loopToSchedule);
|
||||
} else {
|
||||
LoopHierarchyNode& loop = currentList->get(currentIterator);
|
||||
currentIterator = currentList->advance(currentIterator);
|
||||
|
||||
edgeStackPointer->linkedList = currentList;
|
||||
edgeStackPointer->position = currentIterator;
|
||||
edgeStackPointer++;
|
||||
|
||||
currentList = &loop.getSuccessors();
|
||||
currentIterator = currentList->begin();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// FIXME: need to schedule the remaining nodes (exceptions).
|
||||
|
||||
// Summarize the scheduling.
|
||||
ControlNode** result = new(pool) ControlNode*[nNodes];
|
||||
ControlNode** ptr = result;
|
||||
|
||||
for (DoublyLinkedList<Clique>::iterator c = top.cliques.begin(); !top.cliques.done(c); c = top.cliques.advance(c)) {
|
||||
Clique& clique = top.cliques.get(c);
|
||||
for (DoublyLinkedList<ScheduledNode>::iterator s = clique.begin(); !clique.done(s); s = clique.advance(s))
|
||||
*ptr++ = dfsList[clique.get(s).dfsNum];
|
||||
}
|
||||
|
||||
// Add the endNode.
|
||||
*ptr++ = dfsList[nNodes - 1];
|
||||
|
||||
PR_ASSERT(ptr == &result[nNodes]);
|
||||
|
||||
return result;
|
||||
}
|
|
@ -0,0 +1,97 @@
|
|||
/* -*- 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 _CONTROL_NODE_SCHEDULER_H_
|
||||
#define _CONTROL_NODE_SCHEDULER_H_
|
||||
|
||||
#include "Fundamentals.h"
|
||||
#include "Pool.h"
|
||||
|
||||
class ControlNode;
|
||||
|
||||
struct ScheduledNode : public DoublyLinkedEntry<ScheduledNode>
|
||||
{
|
||||
// A ScheduledNode must be a linked in the execution order to other ScheduledNode.
|
||||
// As ControlNode is already a linked to other ControlNode in the graph we create
|
||||
// an instance of this class for each ControlNode of the graph and copy the field dfsNum.
|
||||
Int32 dfsNum;
|
||||
};
|
||||
|
||||
class Clique : public DoublyLinkedList<ScheduledNode>, public DoublyLinkedEntry<Clique>
|
||||
{
|
||||
// A Clique is a linked list of ScheduledNode and also an entry in a linked list (list of Cliques in a loop).
|
||||
};
|
||||
|
||||
class LoopHierarchyNode : public DoublyLinkedEntry<LoopHierarchyNode>
|
||||
{
|
||||
private:
|
||||
DoublyLinkedList<LoopHierarchyNode> successors;
|
||||
|
||||
public:
|
||||
Int32 header; // Loop header's dfsNum.
|
||||
FastBitSet nodes; // Nodes contained in this loop.
|
||||
DoublyLinkedList<Clique> cliques; // Linked list of Clique in this loop.
|
||||
|
||||
inline void addSuccessor(LoopHierarchyNode& successor) {successors.addLast(successor);}
|
||||
inline DoublyLinkedList<LoopHierarchyNode>& getSuccessors() {return successors;}
|
||||
};
|
||||
|
||||
class LoopHierarchySet
|
||||
{
|
||||
private:
|
||||
LoopHierarchyNode* nodes; // Array of loop nodes in the loop hierarchy tree.
|
||||
Int32* indexes; // Array of indexes of nodes in the dfs order.
|
||||
Uint32 nextFree; // Next available slot in nodes.
|
||||
|
||||
LoopHierarchySet (const LoopHierarchySet &); // Copying forbidden
|
||||
void operator = (const LoopHierarchySet &); // Copying forbidden
|
||||
|
||||
public:
|
||||
LoopHierarchySet(Pool& pool, LoopHierarchyNode* nodesArray, Uint32 nNodes);
|
||||
|
||||
LoopHierarchyNode& operator[] (const Uint32 nodeIndex);
|
||||
};
|
||||
|
||||
class ControlNodeScheduler
|
||||
{
|
||||
private:
|
||||
Pool& pool;
|
||||
|
||||
ControlNode** dfsList; // List of ControlNodes in depth first search order (from the ControlGraph).
|
||||
const Uint32 nNodes; // Number of ControlNodes in this graph.
|
||||
|
||||
FastBitMatrix dominatorsMatrix; // Matrix of dominators for all the ControlNodes.
|
||||
FastBitSet loopHeaders; // BitSet of the loop headers in the graph.
|
||||
FastBitSet nodesAlreadyScheduled; // BitSet of the ControlNodes currently scheduled.
|
||||
|
||||
ScheduledNode* scheduledNodes; // Array of ScheduledNodes. Used to chain the nodes in the Cliques.
|
||||
|
||||
LoopHierarchyNode& buildLoopHierarchyTree();
|
||||
Uint32 findLoopHeaders();
|
||||
void scheduleLoop(LoopHierarchyNode& loopToSchedule);
|
||||
void initializeLongEdgeFlag(ControlEdge& edge, LoopHierarchyNode& inLoop, FastBitSet& RR, FastBitSet& Rlocal);
|
||||
void findDominators();
|
||||
|
||||
public:
|
||||
ControlNodeScheduler(Pool& p, ControlNode** nodes, Uint32 n) :
|
||||
pool(p), dfsList(nodes), nNodes(n), dominatorsMatrix(n + 1, n), loopHeaders(n) {}
|
||||
|
||||
ControlNode** getScheduledNodes(); // Return the array of ControlNode in a scheduled order.
|
||||
};
|
||||
|
||||
#endif /* _CONTROL_NODE_SCHEDULER_H_ */
|
|
@ -0,0 +1,181 @@
|
|||
/* -*- 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.
|
||||
*/
|
||||
//
|
||||
// simon
|
||||
//
|
||||
|
||||
#include "JavaVM.h"
|
||||
#include "ExceptionTable.h"
|
||||
#include "CatchAssert.h"
|
||||
#include "LogModule.h"
|
||||
|
||||
UT_DEFINE_LOG_MODULE(ExceptionTable);
|
||||
|
||||
//-----------------------------------------------------------------------------------------------------------
|
||||
// ExceptionTableBuilder
|
||||
|
||||
// METHOD addEntry
|
||||
// In/Out: index -- created handler must be at or after stated index
|
||||
// In newEntry -- entry to add to exception table
|
||||
// Assumptions: entries must be added in ascending scheduled node order
|
||||
void ExceptionTableBuilder::
|
||||
addEntry(Uint32& index, ExceptionTableEntry& newEntry)
|
||||
{
|
||||
Uint32 tableSize = eTable.size();
|
||||
|
||||
// iterate through exception table trying to find an existing entry we can coalesce with
|
||||
for(; index < tableSize; index++)
|
||||
{
|
||||
ExceptionTableEntry& ete = eTable[index];
|
||||
|
||||
// can't coalesce if handler, type or policy differ
|
||||
if( (ete.pHandler != newEntry.pHandler) ||
|
||||
(ete.pExceptionType != newEntry.pExceptionType))
|
||||
continue;
|
||||
|
||||
// we require that the above noted assumption has been met
|
||||
assert(ete.pEnd <= newEntry.pStart);
|
||||
|
||||
// extend range of existing exception table entry (coalesce) if possible
|
||||
if(ete.pEnd == newEntry.pStart)
|
||||
{
|
||||
ete.pEnd = newEntry.pEnd; // coalesce ranges
|
||||
#ifdef DEBUG_LOG
|
||||
UT_LOG(ExceptionTable, PR_LOG_ALWAYS, ("merge"));
|
||||
newEntry.print(UT_LOG_MODULE(ExceptionTable));
|
||||
#endif
|
||||
return; // index will point to index of table entry that was extended
|
||||
}
|
||||
}
|
||||
|
||||
// coalesce wasn't possible, so insert
|
||||
eTable.append(newEntry);
|
||||
|
||||
#ifdef DEBUG_LOG
|
||||
UT_LOG(ExceptionTable, PR_LOG_ALWAYS, ("add "));
|
||||
newEntry.print(UT_LOG_MODULE(ExceptionTable));
|
||||
#endif
|
||||
// index will be correctly set from end of 'for' loop
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------------------------------------
|
||||
|
||||
#ifdef DEBUG
|
||||
// check important assumption that array is sorted and valid
|
||||
// if it is not then nested trys will probably fail
|
||||
void ExceptionTableBuilder::
|
||||
checkInOrder()
|
||||
{
|
||||
if(eTable.begin() == eTable.end())
|
||||
return;
|
||||
|
||||
ExceptionTableEntry *curr, *next, *last;
|
||||
curr= eTable.begin();
|
||||
last = eTable.end() - 1;
|
||||
|
||||
while(curr < last) {
|
||||
next = curr + 1;
|
||||
assert(curr->pStart <= curr->pEnd); // sanity check
|
||||
assert(curr->pStart <= next->pStart); // ensure that items are sorted by pStart field
|
||||
curr = next;
|
||||
}
|
||||
}
|
||||
#endif // DEBUG
|
||||
|
||||
//-----------------------------------------------------------------------------------------------------------
|
||||
// Constructor
|
||||
// builds an exception table in the specified pool
|
||||
ExceptionTable::
|
||||
ExceptionTable(ExceptionTableBuilder eBuilder, Pool& inPool)
|
||||
{
|
||||
numberofEntries = eBuilder.getNumberofEntries();
|
||||
|
||||
start = eBuilder.start;
|
||||
if(numberofEntries == 0)
|
||||
{
|
||||
mEntries = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
Uint32 i;
|
||||
mEntries = new(inPool) ExceptionTableEntry[numberofEntries];
|
||||
for(i = 0; i < numberofEntries; i++)
|
||||
mEntries[i] = eBuilder.getEntry(i);
|
||||
Uint32 sz = eBuilder.getNumberofAPoints();
|
||||
asynchPoints = new(inPool) Uint32[sz];
|
||||
for(i = 0; i < sz; i++)
|
||||
asynchPoints[i] = eBuilder.getAEntry(i);
|
||||
}
|
||||
}
|
||||
|
||||
// In: thrown object
|
||||
// In: PC/EIP of
|
||||
// Out: pointer to an ExceptionTableEntry that matches the thrown object (or NULL if no match)
|
||||
ExceptionTableEntry*
|
||||
ExceptionTable::
|
||||
findCatchHandler(const Uint8* pc, const JavaObject& inObject)
|
||||
{
|
||||
const Class& clazz = inObject.getClass();
|
||||
|
||||
// scan through the handler list in _reverse_ looking for a match
|
||||
// reverse scanning is needed to properly respect nested try blocks
|
||||
if(numberofEntries > 0)
|
||||
for(Int32 i = numberofEntries - 1; i >= 0; i--)
|
||||
{
|
||||
ExceptionTableEntry* tableEntry = mEntries + i;
|
||||
if( (pc >= start + tableEntry->pStart) &&
|
||||
(pc < start + tableEntry->pEnd) &&
|
||||
clazz.implements( *(tableEntry->pExceptionType) ) )
|
||||
return tableEntry;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------------------------------------
|
||||
// DEBUG code
|
||||
#ifdef DEBUG_LOG
|
||||
|
||||
|
||||
void ExceptionTable::
|
||||
print(LogModuleObject &f)
|
||||
{
|
||||
if(numberofEntries == 0)
|
||||
{
|
||||
UT_OBJECTLOG(f, PR_LOG_ALWAYS, ("The exception table is empty.\n"));
|
||||
}
|
||||
else
|
||||
{
|
||||
UT_OBJECTLOG(f, PR_LOG_ALWAYS, ("(begin-offset, end-offset) -> handler-offset (exception caught)\n"));
|
||||
for(Uint32 i = 0; i < numberofEntries; i++)
|
||||
mEntries[i].print(f);
|
||||
}
|
||||
}
|
||||
|
||||
void ExceptionTable::
|
||||
printShort(LogModuleObject &f)
|
||||
{
|
||||
if(numberofEntries != 0)
|
||||
for(Uint32 i = 0; i < numberofEntries; i++)
|
||||
{
|
||||
UT_OBJECTLOG(f, PR_LOG_ALWAYS, ("\t"));
|
||||
mEntries[i].print(f,start);
|
||||
}
|
||||
}
|
||||
|
||||
#endif // DEBUG
|
|
@ -0,0 +1,142 @@
|
|||
/* -*- 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.
|
||||
*/
|
||||
//
|
||||
// simon
|
||||
//
|
||||
|
||||
#ifndef _EXCEPTION_TABLE_
|
||||
#define _EXCEPTION_TABLE_
|
||||
|
||||
#include "Fundamentals.h"
|
||||
#include "Vector.h"
|
||||
#include "JavaObject.h"
|
||||
#include "LogModule.h"
|
||||
|
||||
//-----------------------------------------------------------------------------------------------------------
|
||||
// ExceptionTableEntry
|
||||
class ExceptionTableEntry
|
||||
{
|
||||
public:
|
||||
Uint32 pStart;
|
||||
Uint32 pEnd; // offset to byte _after_ beginning of section
|
||||
const Class* pExceptionType; // points to type of the handler
|
||||
Uint32 pHandler;
|
||||
|
||||
#ifdef DEBUG_LOG
|
||||
void print(LogModuleObject &f,Uint8* s)
|
||||
{
|
||||
UT_OBJECTLOG(f, PR_LOG_ALWAYS, ("(%x,%x) -> %x (",Uint32(s+pStart), Uint32(s+pEnd), Uint32(s+pHandler)));
|
||||
pExceptionType->printRef(f);
|
||||
UT_OBJECTLOG(f, PR_LOG_ALWAYS, (")\n"));
|
||||
}
|
||||
void print(LogModuleObject &f)
|
||||
{
|
||||
UT_OBJECTLOG(f, PR_LOG_ALWAYS, ("(%d,%d) -> %d (",Uint32(pStart), Uint32(pEnd), Uint32(pHandler)));
|
||||
pExceptionType->printRef(f);
|
||||
UT_OBJECTLOG(f, PR_LOG_ALWAYS, (")\n"));
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------------------------------------
|
||||
// ExceptionTableBuilder
|
||||
class ExceptionTableBuilder
|
||||
{
|
||||
private:
|
||||
Vector<ExceptionTableEntry> eTable;
|
||||
Vector<Uint32> asynchPoints;
|
||||
|
||||
Uint8* start; // points to start of method
|
||||
|
||||
public:
|
||||
ExceptionTableBuilder(Uint8* s) : eTable(), asynchPoints() { start = s; };
|
||||
|
||||
void addEntry(Uint32& index, ExceptionTableEntry& ete);
|
||||
const ExceptionTableEntry& getEntry(Uint32 index) const { assert(index < eTable.size()); return (eTable[index]); }
|
||||
Uint32 getNumberofEntries() const { return (eTable.size()); }
|
||||
void addAEntry(Uint32 s) {
|
||||
asynchPoints.append(s);
|
||||
}
|
||||
inline int getAEntry(int i) {
|
||||
return asynchPoints[i];
|
||||
}
|
||||
inline int getNumberofAPoints() {
|
||||
return asynchPoints.size();
|
||||
}
|
||||
|
||||
friend class ExceptionTable;
|
||||
|
||||
DEBUG_ONLY(void checkInOrder());
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------------------------------------
|
||||
// ExceptionTable
|
||||
class ControlNode;
|
||||
|
||||
class ExceptionTable
|
||||
{
|
||||
private:
|
||||
Uint32 numberofEntries;
|
||||
ExceptionTableEntry* mEntries;
|
||||
int numberofAPoints;
|
||||
Uint32* asynchPoints; // array of offsets for where the asynch. points are
|
||||
Uint8* start; // points to start of method
|
||||
static ClassFileSummary* excClass; // points to the class of RuntimeException
|
||||
public:
|
||||
ExceptionTable(ExceptionTableBuilder eBuilder, Pool& inPool);
|
||||
ExceptionTableEntry* findCatchHandler(const Uint8* pc, const JavaObject& inObject);
|
||||
Uint32 getNumberofEntries() { return numberofEntries; }
|
||||
ExceptionTable(ExceptionTable& et) {
|
||||
numberofEntries = et.numberofEntries;
|
||||
mEntries = et.mEntries;
|
||||
numberofAPoints = et.numberofAPoints;
|
||||
asynchPoints = et.asynchPoints;
|
||||
start = et.start;
|
||||
}
|
||||
ExceptionTable& operator=(Uint8* newstart)
|
||||
{
|
||||
start = newstart;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline Uint8* getStart() { return start; }
|
||||
inline Uint32* getAsynchPoints() { return asynchPoints; }
|
||||
inline int getNrOfAsynchPoints() { return numberofAPoints; }
|
||||
static void staticInit(ClassCentral ¢ral);
|
||||
|
||||
#ifdef DEBUG_LOG
|
||||
void print(LogModuleObject &f);
|
||||
void printShort(LogModuleObject &f);
|
||||
void printFormatted(LogModuleObject &f, ControlNode** inNodes, Uint32 numNodes, bool isHTML);
|
||||
#endif // DEBUG_LOG
|
||||
};
|
||||
|
||||
struct ExceptionTableCache
|
||||
{
|
||||
ExceptionTable eTable;
|
||||
JavaObject* exc;
|
||||
ExceptionTableCache(ExceptionTable* et, Uint8* i, JavaObject* ex) : eTable(*et)
|
||||
{
|
||||
eTable = i; // relocates the table
|
||||
exc = ex;
|
||||
}
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------------------------------------
|
||||
|
||||
#endif // _EXCEPTION_TABLE_
|
|
@ -0,0 +1,111 @@
|
|||
/* -*- 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.
|
||||
*/
|
||||
//
|
||||
// File: FormatStructures.h
|
||||
//
|
||||
// Authors: Simon Holmes a Court
|
||||
//
|
||||
// To avoid circular include problems, data structures used in the md emitter, md formatter
|
||||
// and native formatter may be found here.
|
||||
//
|
||||
|
||||
#ifndef _FORMAT_STRUCTURES_
|
||||
#define _FORMAT_STRUCTURES_
|
||||
|
||||
#include "Fundamentals.h"
|
||||
#include "LogModule.h"
|
||||
|
||||
struct Method;
|
||||
class VirtualRegisterManager;
|
||||
struct MethodDescriptor;
|
||||
|
||||
//-----------------------------------------------------------------------------------------------------------
|
||||
// FormattedCodeInfo
|
||||
struct FormattedCodeInfo
|
||||
{
|
||||
Method* method; // runtime descriptor of the method we're formatting
|
||||
Uint8* methodStart; // ptr to beginning of allocated memory for the method
|
||||
Uint8* methodEnd; // ptr to (FIX byte after?) end of allocated memory for the method
|
||||
|
||||
Uint32 prologSize;
|
||||
Uint32 epilogSize;
|
||||
Uint32 bodySize; // prologSize + epilogSize + formatted size of on the instructions in all ControlNodes
|
||||
Uint32 preMethodSize;
|
||||
Uint32 postMethodSize;
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------------------------------------
|
||||
// TVector
|
||||
#ifdef GENERATE_FOR_PPC
|
||||
#define USE_TVECTOR
|
||||
#endif
|
||||
|
||||
#ifdef USE_TVECTOR
|
||||
extern void* _MD_createTVector(const MethodDescriptor& inMethodDescriptor, void* inFunctionMemory);
|
||||
#define M_MD_createTVector(inMethodDescriptor, inFunctionMemory) (_MD_createTVector(inMethodDescriptor, inFunctionMemory))
|
||||
#else
|
||||
#define M_MD_createTVector(inMethodDescriptor, inFunctionMemory) (inFunctionMemory)
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------------------------------------------------------
|
||||
// StackFrameInfo
|
||||
class StackFrameInfo
|
||||
{
|
||||
protected:
|
||||
Uint8 numSavedGPRs;
|
||||
Uint8 numSavedFPRs;
|
||||
Uint8 numMonitorSlots;
|
||||
|
||||
Uint32 localStore_bytes;
|
||||
|
||||
DEBUG_ONLY(bool hasBeenInited;)
|
||||
|
||||
public:
|
||||
StackFrameInfo() { DEBUG_ONLY(hasBeenInited = false); };
|
||||
void init(VirtualRegisterManager& /*inVRManager*/) { DEBUG_ONLY(hasBeenInited = true); }
|
||||
Uint32 getNumSavedGPRWords() { assert(hasBeenInited); return numSavedGPRs; }
|
||||
Uint32 getNumSavedFPRWords() { assert(hasBeenInited); return numSavedFPRs; }
|
||||
Uint32 getNumMonitorSlots() { assert(hasBeenInited); return numMonitorSlots; }
|
||||
|
||||
Uint32 getRegisterOffset() { assert(hasBeenInited); return numMonitorSlots; }
|
||||
|
||||
Uint32 getLocalStoreSizeBytes() { assert(hasBeenInited); return localStore_bytes; }
|
||||
|
||||
// x86 only right now!
|
||||
// returns the offset (off EBP) to the beginning of the registers
|
||||
Uint32 getCalleeSavedBeginOffset()
|
||||
{
|
||||
return localStore_bytes + 4;
|
||||
}
|
||||
|
||||
// returns the offset (off EBP) to the stack pointer before any arguments are pushed
|
||||
|
||||
Uint32 getStackPointerOffset()
|
||||
{
|
||||
return localStore_bytes + (4 * numSavedGPRs) + (4 * numSavedFPRs) + (4 * numMonitorSlots);
|
||||
}
|
||||
|
||||
#ifdef DEBUG_LOG
|
||||
void print(LogModuleObject &f)
|
||||
{
|
||||
UT_OBJECTLOG(f, PR_LOG_ALWAYS, ("Policy: g%d:f%d:l%d\n", getNumSavedGPRWords(), getNumSavedFPRWords(), getLocalStoreSizeBytes()));
|
||||
}
|
||||
#endif // DEBUG_LOG
|
||||
};
|
||||
|
||||
#endif // _FORMAT_STRUCTURES_
|
|
@ -0,0 +1,508 @@
|
|||
/* -*- 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.
|
||||
*/
|
||||
//
|
||||
// File: HTMLMethodDump.cpp
|
||||
// Author: Simon Holmes a Court
|
||||
//
|
||||
|
||||
#include "Fundamentals.h"
|
||||
|
||||
#ifdef DEBUG_LOG
|
||||
|
||||
#include "LogModule.h"
|
||||
#include "HTMLMethodDump.h"
|
||||
#include "NativeFormatter.h"
|
||||
#include "FieldOrMethod.h"
|
||||
#include "ExceptionTable.h"
|
||||
#include "ControlGraph.h"
|
||||
#include <time.h>
|
||||
|
||||
//-----------------------------------------------------------------------------------------------------------
|
||||
// Native Formatter method
|
||||
UT_DEFINE_LOG_MODULE(MethodToHtml);
|
||||
|
||||
void dumpDissasembly(LogModuleObject &f, Uint8* start, Uint8* end)
|
||||
{
|
||||
if (end <= start) // this happens for the last node, or nodes without code
|
||||
return;
|
||||
|
||||
Uint8* curAddr = start;
|
||||
while ((curAddr != NULL) && (curAddr < end))
|
||||
curAddr = (Uint8*) disassemble1(f, curAddr);
|
||||
}
|
||||
|
||||
static void dumpHTMLByteCode(MethodToHTML output, Method* inMethod)
|
||||
{
|
||||
UT_OBJECTLOG(output.mFile, PR_LOG_ALWAYS, ("\n<br>\n"));
|
||||
output.heading("ByteCode");
|
||||
UT_OBJECTLOG(output.mFile, PR_LOG_ALWAYS, ("\n<br><blockquote>\n<pre>\n"));
|
||||
inMethod->dumpBytecodeDisassembly(output.mFile);
|
||||
UT_OBJECTLOG(output.mFile, PR_LOG_ALWAYS, ("</pre></blockquote>\n"));
|
||||
}
|
||||
|
||||
static void dumpHTMLExceptionTable(MethodToHTML output, ExceptionTable& inExceptionTable, ControlNode** nodes, Uint32 nNodes)
|
||||
{
|
||||
UT_OBJECTLOG(output.mFile, PR_LOG_ALWAYS, ("\n<br>\n"));
|
||||
output.heading("Exception Table");
|
||||
UT_OBJECTLOG(output.mFile, PR_LOG_ALWAYS, ("\n<br><blockquote>\n<pre>"));
|
||||
inExceptionTable.print(output.mFile);
|
||||
UT_OBJECTLOG(output.mFile, PR_LOG_ALWAYS, ("</pre></blockquote>\n"));
|
||||
|
||||
inExceptionTable.printFormatted(output.mFile, nodes, nNodes, true);
|
||||
}
|
||||
|
||||
void NativeFormatter::
|
||||
dumpMethodToHTML(FormattedCodeInfo& fci, ExceptionTable& inExceptionTable)
|
||||
{
|
||||
// create and prepare the output file
|
||||
MethodToHTML output;
|
||||
output.mFile.setLogLevel(PR_LOG_ALWAYS);
|
||||
output.mFile.setOptions(PR_LOG_OPTION_NIL);
|
||||
|
||||
assert(fci.method);
|
||||
|
||||
#if defined(XP_PC) || defined(LINUX)
|
||||
if (*fci.methodStart == 0xcc)
|
||||
fci.methodStart++;
|
||||
#endif
|
||||
|
||||
// get clean name
|
||||
const char* oldname = fci.method->getName();
|
||||
char* name = new char[strlen(oldname) + 1];
|
||||
strcpy(name, oldname);
|
||||
char* q = name;
|
||||
while(*q)
|
||||
{
|
||||
switch(*q)
|
||||
{
|
||||
case ':': case '<': case '>': case '/': case ' ':
|
||||
*q = '_';
|
||||
}
|
||||
q++;
|
||||
}
|
||||
|
||||
const char* filename = fci.method->getHTMLName();
|
||||
|
||||
// open file a clean filename
|
||||
output.openFile(filename);
|
||||
output.pageBegin(name);
|
||||
|
||||
// stats
|
||||
const char* className = fci.method->getDeclaringClass()->getName();
|
||||
|
||||
output.statsBegin();
|
||||
output.bigstatsLine("Name", name);
|
||||
output.statsLine("Class/Name/Sig", className, name, fci.method->getSignatureString());
|
||||
output.statsLine("Fully qualified name", fci.method->toString());
|
||||
output.statsLine("HTML File Name", filename);
|
||||
|
||||
output.statsLine("Native Code Bytes", fci.methodEnd - fci.methodStart);
|
||||
|
||||
time_t currentTime; // output the time
|
||||
time(¤tTime);
|
||||
output.statsLine("Created", ctime(¤tTime));
|
||||
|
||||
output.statsEnd();
|
||||
|
||||
// disassemble bytecode
|
||||
dumpHTMLByteCode(output, fci.method);
|
||||
|
||||
// disassemble
|
||||
output.heading("Generated Code");
|
||||
output.disassemblyTableBegin();
|
||||
|
||||
// dump prolog
|
||||
Uint8* curOffset;
|
||||
curOffset = fci.methodStart;
|
||||
output.disassemblyRowBegin("Pre");
|
||||
dumpDissasembly(output.mFile, curOffset, curOffset + fci.preMethodSize);
|
||||
curOffset += fci.preMethodSize;
|
||||
output.disassemblyColumnSeparator(0xeeeeee);
|
||||
output.disassemblyColumnSeparator(0xdddddd);
|
||||
output.disassemblyRowEnd();
|
||||
|
||||
output.disassemblyRowBegin("Pro");
|
||||
dumpDissasembly(output.mFile, curOffset, curOffset + fci.prologSize);
|
||||
curOffset += fci.prologSize;
|
||||
output.disassemblyColumnSeparator(0xeeeeee);
|
||||
output.disassemblyColumnSeparator(0xdddddd);
|
||||
output.disassemblyRowEnd();
|
||||
|
||||
// dump methods
|
||||
nodes[0]->controlGraph.dfsSearch();
|
||||
nodes[0]->controlGraph.assignProducerNumbers(1);
|
||||
|
||||
// now go through and print out the high level representation (w/o prolog etc)
|
||||
for (Uint32 n = 0; n < nNodes; n++)
|
||||
{
|
||||
ControlNode& node = *nodes[n];
|
||||
output.disassemblyRowBegin(node.dfsNum);
|
||||
|
||||
// actual disassembly
|
||||
Uint8* nodeStart;
|
||||
Uint8* nodeEnd;
|
||||
|
||||
nodeStart = curOffset;
|
||||
|
||||
if (n != (nNodes - 1))
|
||||
nodeEnd = fci.methodStart + nodes[n+1]->getNativeOffset();
|
||||
else // last node
|
||||
nodeEnd = fci.methodEnd;
|
||||
|
||||
curOffset = nodeEnd;
|
||||
|
||||
dumpDissasembly(output.mFile, nodeStart, nodeEnd);
|
||||
output.disassemblyColumnSeparator(0xeeeeee);
|
||||
|
||||
// intended disassembly
|
||||
InstructionList& instructions = node.getInstructions();
|
||||
for(InstructionList::iterator i = instructions.begin(); !instructions.done(i); i = instructions.advance(i))
|
||||
{
|
||||
instructions.get(i).printPretty(output.mFile);
|
||||
UT_OBJECTLOG(output.mFile, PR_LOG_ALWAYS, ("\n"));
|
||||
}
|
||||
|
||||
// primitive graph
|
||||
output.disassemblyColumnSeparator(0xdddddd);
|
||||
node.printPretty(output.mFile, 0);
|
||||
|
||||
output.disassemblyRowEnd();
|
||||
}
|
||||
|
||||
output.disassemblyTableEnd();
|
||||
|
||||
// exception table
|
||||
dumpHTMLExceptionTable(output, inExceptionTable, nodes, nNodes);
|
||||
|
||||
output.pageEnd();
|
||||
output.closeFile();
|
||||
|
||||
// add this file reference to the index file
|
||||
FileIndex::insertFile(filename, name, className);
|
||||
FileIndex::output(); // FIX FIX FIX for now we output this file EVERY time we generate code
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------------------------------------
|
||||
FileIndex FileIndex::sFileIndex;
|
||||
|
||||
void FileIndex::
|
||||
outputHelper()
|
||||
{
|
||||
// open the index file
|
||||
FILE* f = fopen("index.html","w");
|
||||
assert(f);
|
||||
|
||||
// print the header
|
||||
fprintf(f, "<HTML>\n<HEAD>\n<TITLE>Generated Code Index</TITLE>\n</HEAD>\n"
|
||||
"<BODY TEXT=#000000 BGCOLOR=#FFFFFF LINK=#FF0000 VLINK=#800080 ALINK=#0000FF>\n\n" );
|
||||
|
||||
// output links
|
||||
ClassNode* c = classes;
|
||||
while(c)
|
||||
{
|
||||
fprintf(f, "\n<br><B><FONT FACE=\"Arial,Helvetica\">%s</FONT></B><br>\n", c->className);
|
||||
FileNode* n = c->files;
|
||||
while(n)
|
||||
{
|
||||
fprintf(f, "\t<A HREF=\"%s\">%s</A><br>\n", n->fileName, n->methodName);
|
||||
n = n->next;
|
||||
}
|
||||
c = c->next;
|
||||
}
|
||||
|
||||
// output footers
|
||||
fprintf(f, "\n</BODY>\n</HTML>\n");
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
ClassNode* FileIndex::
|
||||
findClassName(const char* className)
|
||||
{
|
||||
if(classes == NULL)
|
||||
{
|
||||
classes = new ClassNode(className);
|
||||
return classes;
|
||||
}
|
||||
|
||||
// search for a class object if there is one
|
||||
ClassNode* foundClass = classes;
|
||||
while(true)
|
||||
{
|
||||
if(strncmp(foundClass->className, className, 512) == 0)
|
||||
return foundClass;
|
||||
|
||||
// no match, if end, make new node
|
||||
if(foundClass->next == NULL)
|
||||
{
|
||||
foundClass->next = new ClassNode(className);
|
||||
return foundClass->next;
|
||||
}
|
||||
else
|
||||
foundClass = foundClass->next;
|
||||
}
|
||||
}
|
||||
|
||||
void FileIndex::
|
||||
insertFileHelper(const char* inFileName, const char* inMethodName, const char* inClassName)
|
||||
{
|
||||
ClassNode* classnode = findClassName(inClassName);
|
||||
FileNode* firstNode = classnode->files;
|
||||
FileNode* newNode = new FileNode(inFileName, inMethodName);
|
||||
|
||||
if(firstNode == NULL)
|
||||
{
|
||||
classnode->files = newNode;
|
||||
return;
|
||||
}
|
||||
|
||||
// is it before the first?
|
||||
if(strncmp(inMethodName, firstNode->methodName, 512) < 0)
|
||||
{
|
||||
classnode->files = newNode;
|
||||
newNode->next = firstNode;
|
||||
return;
|
||||
}
|
||||
|
||||
// otherwise iterate through until we are after a node and then insert
|
||||
FileNode* node = firstNode;
|
||||
while(true)
|
||||
{
|
||||
if( node->next == NULL || // at end
|
||||
(strncmp(inMethodName, node->next->methodName, 512) < 0) ) // lexically before next name
|
||||
{
|
||||
FileNode* tempNode = node->next;
|
||||
node->next = newNode;
|
||||
newNode->next = tempNode;
|
||||
return;
|
||||
}
|
||||
node = node->next;
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------------------------------------
|
||||
// Debugging Code for outputting Exception Tables
|
||||
static void tableBegin(LogModuleObject &f, bool isHTML)
|
||||
{
|
||||
if(isHTML)
|
||||
UT_OBJECTLOG(f, PR_LOG_ALWAYS, ("<BLOCKQUOTE>\n<TABLE BORDER=0 CELLSPACING=1 CELLPADDING=0>\n"));
|
||||
}
|
||||
static void tableEnd(LogModuleObject &f, bool isHTML)
|
||||
{
|
||||
isHTML ? UT_OBJECTLOG(f, PR_LOG_ALWAYS, ("</TABLE>\n</BLOCKQUOTE>\n")) : UT_OBJECTLOG(f, PR_LOG_ALWAYS, ("\n\n\n"));
|
||||
}
|
||||
static void heading(LogModuleObject &f, Uint32 num, bool isHTML)
|
||||
{
|
||||
if(isHTML)
|
||||
UT_OBJECTLOG(f, PR_LOG_ALWAYS, ("<TD WIDTH=20 BGCOLOR=#5b88f0 ALIGN=CENTER><B><FONT FACE=\"Arial,Helvetica\">%d</FONT></B></TD>", num));
|
||||
else
|
||||
UT_OBJECTLOG(f, PR_LOG_ALWAYS, (" %02d ", num));
|
||||
}
|
||||
static void rowBegin(LogModuleObject &f, bool isHTML)
|
||||
{
|
||||
if(isHTML)
|
||||
{
|
||||
UT_OBJECTLOG(f, PR_LOG_ALWAYS, ("<TR><TD BGCOLOR=#5b88f0 WIDTH=50><B><FONT FACE=\"Arial,Helvetica\">Node</TD>"));
|
||||
UT_OBJECTLOG(f, PR_LOG_ALWAYS, ("<TD BGCOLOR=#5b88f0 WIDTH=50><B><FONT FACE=\"Arial,Helvetica\">Offset</TD>"));
|
||||
}
|
||||
else
|
||||
UT_OBJECTLOG(f, PR_LOG_ALWAYS, ("N "));
|
||||
}
|
||||
static void nodeRowBegin(LogModuleObject &f, Uint32 node, Uint32 offset, bool isHTML)
|
||||
{
|
||||
if(isHTML)
|
||||
{
|
||||
UT_OBJECTLOG(f, PR_LOG_ALWAYS, ("<TR><TD BGCOLOR=#eeeeee><B><FONT FACE=\"Arial,Helvetica\">N%d</TD>", node));
|
||||
UT_OBJECTLOG(f, PR_LOG_ALWAYS, ("<TD ALIGN=RIGHT BGCOLOR=#dddddd>%d </FONT></B></TD>", offset));
|
||||
}
|
||||
else
|
||||
UT_OBJECTLOG(f, PR_LOG_ALWAYS, ("N%02d: %4d ", node, offset));
|
||||
}
|
||||
static void rowEnd(LogModuleObject &f, bool isHTML)
|
||||
{
|
||||
isHTML ? UT_OBJECTLOG(f, PR_LOG_ALWAYS, ("</TR>\n")) : UT_OBJECTLOG(f, PR_LOG_ALWAYS, ("\n"));
|
||||
}
|
||||
static void printInside(LogModuleObject &f, bool isHTML)
|
||||
{
|
||||
isHTML ? UT_OBJECTLOG(f, PR_LOG_ALWAYS, ("<TD BGCOLOR=#ff0000> </TD>")) : UT_OBJECTLOG(f, PR_LOG_ALWAYS, ("| "));
|
||||
}
|
||||
static void printOutside(LogModuleObject &f, Uint32 col, bool isHTML)
|
||||
{
|
||||
if(isHTML)
|
||||
if(col&1)
|
||||
UT_OBJECTLOG(f, PR_LOG_ALWAYS, ("<TD BGCOLOR=#dddddd> </TD>"));
|
||||
else
|
||||
UT_OBJECTLOG(f, PR_LOG_ALWAYS, ("<TD BGCOLOR=#eeeeee> </TD>"));
|
||||
else
|
||||
UT_OBJECTLOG(f, PR_LOG_ALWAYS, (" "));
|
||||
}
|
||||
|
||||
void ExceptionTable::
|
||||
printFormatted(LogModuleObject &f, ControlNode** inNodes, Uint32 numNodes, bool isHTML)
|
||||
{
|
||||
Uint32 col;
|
||||
if(numberofEntries == 0)
|
||||
return;
|
||||
|
||||
// print headers
|
||||
tableBegin(f, isHTML);
|
||||
rowBegin(f, isHTML);
|
||||
for(col = 0; col < numberofEntries; col++)
|
||||
heading(f, col, isHTML);
|
||||
rowEnd(f, isHTML);
|
||||
|
||||
// print table
|
||||
for (Uint32 i = 0; i < numNodes; i++)
|
||||
{
|
||||
ControlNode* node = inNodes[i];
|
||||
Uint32 pc = node->getNativeOffset();
|
||||
nodeRowBegin(f, node->dfsNum, pc, isHTML);
|
||||
|
||||
for(col = 0; col < numberofEntries; col++)
|
||||
if (pc >= mEntries[col].pStart && pc < mEntries[col].pEnd)
|
||||
printInside(f, isHTML);
|
||||
else
|
||||
printOutside(f, col, isHTML);
|
||||
|
||||
rowEnd(f, isHTML);
|
||||
}
|
||||
tableEnd(f, isHTML);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------------------------------------
|
||||
// Debugging Code for outputting HTML pages
|
||||
|
||||
void MethodToHTML::
|
||||
openFile(const char* fileName)
|
||||
{
|
||||
// open the file
|
||||
if (!mFile.setLogFile(fileName))
|
||||
trespass("opening log file failed");
|
||||
}
|
||||
|
||||
void MethodToHTML::
|
||||
closeFile()
|
||||
{
|
||||
// assert(mFile);
|
||||
// fclose(mFile);
|
||||
}
|
||||
|
||||
void MethodToHTML::
|
||||
pageBegin(const char* name)
|
||||
{
|
||||
UT_OBJECTLOG(mFile, PR_LOG_ALWAYS, ("<HTML>\n<HEAD>\n<TITLE>%s</TITLE>\n</HEAD>\n", name));
|
||||
UT_OBJECTLOG(mFile, PR_LOG_ALWAYS, ("<BODY TEXT=#000000 BGCOLOR=#FFFFFF LINK=#FF0000 VLINK=#800080 ALINK=#0000FF>\n\n"));
|
||||
}
|
||||
|
||||
// Heading
|
||||
void MethodToHTML::
|
||||
heading(const char* label)
|
||||
{
|
||||
UT_OBJECTLOG(mFile, PR_LOG_ALWAYS, ("<B><FONT SIZE=+1 FACE=\"Arial,Helvetica\">%s</FONT></B><BR>\n", label));
|
||||
}
|
||||
|
||||
// Stats
|
||||
void MethodToHTML::
|
||||
statsBegin()
|
||||
{
|
||||
UT_OBJECTLOG(mFile, PR_LOG_ALWAYS, ("\n\n<FONT SIZE=+2>\n<TABLE BORDER=0 CELLSPACING=0 CELLPADDING=3 COLS=2>\n"));
|
||||
}
|
||||
|
||||
void MethodToHTML::
|
||||
statsLine(const char* label, const char* value)
|
||||
{
|
||||
UT_OBJECTLOG(mFile, PR_LOG_ALWAYS, ("<TR><TD WIDTH=\"200\"><B>%s</B></TD><TD>%s</TD></TR>\n", label, value));
|
||||
}
|
||||
|
||||
void MethodToHTML::
|
||||
bigstatsLine(const char* label, const char* value)
|
||||
{
|
||||
UT_OBJECTLOG(mFile, PR_LOG_ALWAYS, ("<TR>\n<TD WIDTH=\"200\"><B><FONT SIZE=+1 FACE=\"Arial,Helvetica\">%s</FONT></B></TD>\n"
|
||||
"<TD><B><FONT SIZE=+1 FACE=\"Arial,Helvetica\">%s</FONT></B></TD>\n</TR>\n", label, value));
|
||||
}
|
||||
|
||||
void MethodToHTML::
|
||||
statsLine(const char* label, const int value)
|
||||
{
|
||||
UT_OBJECTLOG(mFile, PR_LOG_ALWAYS, ("<TR><TD><B>%s</B></TD><TD>%d</TD></TR>\n", label, value));
|
||||
}
|
||||
|
||||
void MethodToHTML::
|
||||
statsLine(const char* label, const char* value1, const char* value2, const char* value3)
|
||||
{
|
||||
UT_OBJECTLOG(mFile, PR_LOG_ALWAYS, ("<TR><TD WIDTH=\"200\"><B>%s</B></TD><TD>%s %s %s</TD></TR>\n", label, value1, value2, value3));
|
||||
}
|
||||
|
||||
|
||||
void MethodToHTML::
|
||||
statsEnd()
|
||||
{
|
||||
UT_OBJECTLOG(mFile, PR_LOG_ALWAYS, ("</TABLE>\n</FONT>\n<BR> \n<BR> \n\n<A HREF=\"index.html\">go to index</A>\n<BR> \n<BR> \n\n"));
|
||||
}
|
||||
|
||||
// disassembly
|
||||
void MethodToHTML::
|
||||
disassemblyTableBegin()
|
||||
{
|
||||
UT_OBJECTLOG(mFile, PR_LOG_ALWAYS, ("<TABLE BORDER=0 CELLSPACING=1 CELLPADDING=3 COLS=4 WIDTH=\"95%%\">\n<TR BGCOLOR=#5b88f0>\n"));
|
||||
UT_OBJECTLOG(mFile, PR_LOG_ALWAYS, ("<TD WIDTH=50><CENTER><B><FONT FACE=\"Arial,Helvetica\">Node</FONT></B></CENTER></TD>\n"));
|
||||
UT_OBJECTLOG(mFile, PR_LOG_ALWAYS, ("<TD><CENTER><B><FONT FACE=\"Arial,Helvetica\">Disassembly</FONT></B></CENTER></TD>\n"));
|
||||
UT_OBJECTLOG(mFile, PR_LOG_ALWAYS, ("<TD><CENTER><B><FONT FACE=\"Arial,Helvetica\">Intended Code</FONT></B></CENTER></TD>\n"));
|
||||
UT_OBJECTLOG(mFile, PR_LOG_ALWAYS, ("<TD><CENTER><B><FONT FACE=\"Arial,Helvetica\">Primitives</FONT></B></CENTER></TD>\n</TR>" ));
|
||||
}
|
||||
|
||||
void MethodToHTML::
|
||||
disassemblyRowBegin(Uint32 nodeNum)
|
||||
{
|
||||
UT_OBJECTLOG(mFile, PR_LOG_ALWAYS, ("\n\n\n<TR>\n<TD ALIGN=CENTER VALIGN=TOP BGCOLOR=#eeeeee><A NAME=\"N%d\"><B><FONT FACE=\"Arial,Helvetica\">N%d</FONT></B></A>"
|
||||
"</TD>\n<TD VALIGN=TOP BGCOLOR=#dddddd><PRE>", nodeNum, nodeNum));
|
||||
}
|
||||
|
||||
void MethodToHTML::
|
||||
disassemblyRowBegin(const char* label)
|
||||
{
|
||||
UT_OBJECTLOG(mFile, PR_LOG_ALWAYS, ("\n\n\n<TR>\n<TD ALIGN=CENTER VALIGN=TOP BGCOLOR=#eeeeee><A NAME=\"%s\"><B><FONT FACE=\"Arial,Helvetica\">%s</FONT></B></A>"
|
||||
"</TD>\n<TD VALIGN=TOP BGCOLOR=#dddddd><PRE>", label, label));
|
||||
}
|
||||
|
||||
void MethodToHTML::
|
||||
disassemblyColumnSeparator(Uint32 color)
|
||||
{
|
||||
UT_OBJECTLOG(mFile, PR_LOG_ALWAYS, (" </PRE></TD>\n\n<TD VALIGN=TOP BGCOLOR=#%06x><PRE>", color));
|
||||
}
|
||||
|
||||
void MethodToHTML::
|
||||
disassemblyRowEnd()
|
||||
{
|
||||
UT_OBJECTLOG(mFile, PR_LOG_ALWAYS, (" </PRE></TD>\n</TR>\n\n"));
|
||||
}
|
||||
|
||||
void MethodToHTML::
|
||||
disassemblyTableEnd()
|
||||
{
|
||||
UT_OBJECTLOG(mFile, PR_LOG_ALWAYS, ("</TABLE>\n\n"));
|
||||
}
|
||||
|
||||
void MethodToHTML::
|
||||
pageEnd()
|
||||
{
|
||||
UT_OBJECTLOG(mFile, PR_LOG_ALWAYS, ("\n</BODY>\n</HTML>\n"));
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------------------------------------
|
||||
|
||||
#endif // DEBUG_LOG
|
|
@ -0,0 +1,108 @@
|
|||
/* -*- 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.
|
||||
*/
|
||||
//
|
||||
// File: HTMLMethodDump.h
|
||||
// Author: Simon Holmes a Court
|
||||
//
|
||||
|
||||
#ifdef DEBUG_LOG
|
||||
#include <fstream.h>
|
||||
|
||||
void dumpDissasembly(LogModuleObject &f, Uint8* start, Uint8* end);
|
||||
|
||||
//-----------------------------------------------------------------------------------------------------------
|
||||
// Debugging Code for outputting HTML index
|
||||
struct FileNode
|
||||
{
|
||||
const char* fileName;
|
||||
const char* methodName;
|
||||
FileNode* next;
|
||||
|
||||
FileNode(const char* inFileName, const char* inMethodName) :
|
||||
fileName(inFileName), methodName(inMethodName), next(NULL) {};
|
||||
};
|
||||
|
||||
struct ClassNode
|
||||
{
|
||||
const char* className;
|
||||
FileNode* files; // pointers to files
|
||||
ClassNode* next;
|
||||
|
||||
ClassNode(const char* inClassName) : className(inClassName), files(NULL), next(NULL) {};
|
||||
};
|
||||
|
||||
// Object to store references to all the HTML files generated
|
||||
|
||||
class FileIndex
|
||||
{
|
||||
private:
|
||||
ClassNode* classes;
|
||||
|
||||
FileIndex() : classes(NULL) {};
|
||||
ClassNode* findClassName(const char* className);
|
||||
void insertFileHelper(const char* fileName, const char* methodName, const char* className);
|
||||
void outputHelper();
|
||||
// FIX no destructor -- memory leak
|
||||
|
||||
public:
|
||||
// static inline FileIndex& getFileIndex() { return (FileIndex::sFileIndex); };
|
||||
static FileIndex sFileIndex;
|
||||
|
||||
static void output() { FileIndex::sFileIndex.outputHelper(); }
|
||||
static void insertFile(const char* f, const char* m, const char* c) { FileIndex::sFileIndex.insertFileHelper(f, m, c); }
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------------------------------------
|
||||
// MethodToHTML
|
||||
UT_EXTERN_LOG_MODULE(MethodToHtml);
|
||||
|
||||
class MethodToHTML
|
||||
{
|
||||
public:
|
||||
LogModuleObject &mFile;
|
||||
|
||||
MethodToHTML() : mFile(UT_LOG_MODULE(MethodToHtml)) {}
|
||||
|
||||
// file stuff
|
||||
void openFile(const char* fileName);
|
||||
void closeFile();
|
||||
|
||||
// html to begin and end a page
|
||||
void pageBegin(const char* name);
|
||||
void pageEnd();
|
||||
void heading(const char* label);
|
||||
|
||||
// statistics
|
||||
void statsBegin();
|
||||
void statsLine(const char* label, const char* value);
|
||||
void statsLine(const char* label, const char* value1, const char* value2, const char* value3);
|
||||
void statsLine(const char* label, const int value);
|
||||
void bigstatsLine(const char* label, const char* value);
|
||||
void statsEnd();
|
||||
|
||||
// dissassembly table
|
||||
void disassemblyTableBegin();
|
||||
void disassemblyRowBegin(Uint32 nodeNum);
|
||||
void disassemblyRowBegin(const char* label);
|
||||
void disassemblyColumnSeparator(Uint32 color);
|
||||
void disassemblyRowEnd();
|
||||
void disassemblyTableEnd();
|
||||
};
|
||||
|
||||
#endif // DEBUG_LOG
|
||||
|
|
@ -0,0 +1,915 @@
|
|||
/* -*- 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.
|
||||
*/
|
||||
/*
|
||||
The instruction graph visulizer.
|
||||
IGVisualizer.cpp
|
||||
|
||||
Peter DeSantis
|
||||
*/
|
||||
|
||||
#if defined(DEBUG) && (defined(WIN32) || defined(USE_MESA)) && defined(IGVISUALIZE)
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <GL/glut.h>
|
||||
|
||||
#include "CodeGenerator.h"
|
||||
#include "IGVisualizer.h"
|
||||
#include "Instruction.h"
|
||||
#include "Primitives.h"
|
||||
#include "Vector.h"
|
||||
|
||||
// To allow glut callbacks to access member data
|
||||
IGVisualizer* IGVisualizer::self = NULL;
|
||||
|
||||
IGVisualizer::
|
||||
IGVisualizer()
|
||||
|
||||
|
||||
{
|
||||
font = GLUT_STROKE_ROMAN;
|
||||
|
||||
zoomFactor = 2;
|
||||
cumZoom = 1;
|
||||
|
||||
|
||||
objectCenterX = 0;
|
||||
objectCenterY = 0;
|
||||
mouseCenterX = 0;
|
||||
mouseCenterY = 0;
|
||||
|
||||
nodeLocX = 0;
|
||||
nodeLocY = 0;
|
||||
|
||||
xWindowDim = 800; // Dimension of window
|
||||
yWindowDim = 600;
|
||||
|
||||
xDimGL = 10000;
|
||||
yDimGL = 10000;
|
||||
|
||||
centerX = 0;
|
||||
centerY = 0;
|
||||
|
||||
INSTRUCTIONWIDTH = 1400; // The size of the instruction itself
|
||||
INSTRUCTIONHEIGHT = 500 ;
|
||||
FRAMEWIDTH = 1450; // Frame is the area around an instruction
|
||||
FRAMEHEIGHT = 750;
|
||||
BORDERWIDTH = 20; // Border around an instruction
|
||||
SPACEBETWEENTREES = 130;
|
||||
|
||||
ARGUMENTREGISTERS = 4; // VR <= AURGUMENTMENTREGISTERS
|
||||
|
||||
self = this;
|
||||
|
||||
roots = new Vector<VisualInstructionRoot>;
|
||||
}
|
||||
|
||||
IGVisualizer ::
|
||||
~IGVisualizer()
|
||||
{
|
||||
delete roots;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Add all the roots you want to be visualized before calling visualize
|
||||
*/
|
||||
|
||||
void IGVisualizer ::
|
||||
addRoots(Vector<RootPair>& inRoots)
|
||||
{
|
||||
|
||||
RootPair* curRoot;
|
||||
VisualInstructionRoot* vRoot = NULL;
|
||||
for(curRoot = inRoots.begin(); curRoot < inRoots.end(); curRoot++)
|
||||
{
|
||||
DataNode* curOutput;
|
||||
Instruction* nextInsn;
|
||||
|
||||
|
||||
if(curRoot->root->getOutgoingEdgesEnd() > curRoot->root->getOutgoingEdgesBegin())
|
||||
{
|
||||
// now walk through all vr's assigned to this producer
|
||||
VirtualRegister* curVR;
|
||||
for (curOutput = curRoot->root->getOutgoingEdgesBegin();
|
||||
curOutput < curRoot->root->getOutgoingEdgesEnd();
|
||||
curOutput++)
|
||||
{
|
||||
switch (curOutput->kind)
|
||||
{
|
||||
case vkCond:
|
||||
case vkMemory:
|
||||
nextInsn = curOutput->getInstructionAnnotation();
|
||||
break;
|
||||
default:
|
||||
curVR = curOutput->getVirtualRegisterAnnotation();
|
||||
assert (curVR);
|
||||
nextInsn = curVR->getDefineInstruction();
|
||||
break;
|
||||
}
|
||||
|
||||
assert (nextInsn);
|
||||
|
||||
vRoot = new VisualInstructionRoot;
|
||||
vRoot->root = nextInsn;
|
||||
vRoot->exploringAsRoot = false;
|
||||
vRoot->locAsCSE = new Vector<TreeLocation>;
|
||||
vRoot->currentCSE = NULL;
|
||||
vRoot->locAsRoot = NULL;
|
||||
|
||||
roots->append(*vRoot);
|
||||
}
|
||||
}
|
||||
|
||||
if (curRoot->root->getInstructionRoot() != NULL) {
|
||||
nextInsn = curRoot->root->getInstructionRoot();
|
||||
|
||||
vRoot = new VisualInstructionRoot;
|
||||
vRoot->root = nextInsn;
|
||||
vRoot->exploringAsRoot = false;
|
||||
vRoot->locAsCSE = new Vector<TreeLocation>;
|
||||
vRoot->currentCSE = NULL;
|
||||
vRoot->locAsRoot = NULL;
|
||||
|
||||
roots->append(*vRoot);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Internal routine which visualizes the set of roots
|
||||
*/
|
||||
void IGVisualizer ::
|
||||
visualizeRoots()
|
||||
{
|
||||
|
||||
int bottom = 0;
|
||||
int left = 0;
|
||||
|
||||
VisualInstructionRoot* curRoot;
|
||||
for(curRoot = self->roots->begin(); curRoot < self->roots->end(); curRoot++)
|
||||
{
|
||||
curRoot->exploringAsRoot = true;
|
||||
curRoot->locAsRoot = &DrawTree(*(curRoot->root), bottom, left);
|
||||
bottom = curRoot->locAsRoot->top + self->SPACEBETWEENTREES;
|
||||
curRoot->exploringAsRoot = false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
VisualInstructionRoot* IGVisualizer ::
|
||||
isInRoots(Instruction* subRoot)
|
||||
{
|
||||
VisualInstructionRoot* curRoot;
|
||||
for(curRoot = self->roots->begin(); curRoot < self->roots->end(); curRoot++)
|
||||
if(curRoot->root == subRoot)
|
||||
return curRoot;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
Internal routine to draw the tree rooted a root so that root's bottom is at yBottom
|
||||
and the tree rooted at root extends to xLeft.
|
||||
*/
|
||||
TreeLocation& IGVisualizer ::
|
||||
DrawTree(Instruction& root, int yBottom, int xLeft)
|
||||
{
|
||||
Instruction* subRoot;
|
||||
InstructionUse* curUse;
|
||||
TreeLocation* loc;
|
||||
TreeLocation* myLocation = new TreeLocation;
|
||||
Vector<TreeLocation> subTreeLocations;
|
||||
VisualInstructionRoot* vRoot;
|
||||
int left = xLeft;
|
||||
int top = yBottom + self->FRAMEHEIGHT;
|
||||
|
||||
if(root.next == NULL)
|
||||
{
|
||||
assert(root.prev == NULL);
|
||||
assert(root.getInstructionDefineEnd() == root.getInstructionDefineBegin() +1);
|
||||
assert(root.getInstructionUseEnd() == root.getInstructionUseBegin() +1);
|
||||
|
||||
// Instruction has been removed from the list
|
||||
curUse = root.getInstructionUseBegin();
|
||||
subRoot = CodeGenerator::instructionUseToInstruction(*curUse);
|
||||
if(subRoot != NULL)
|
||||
{
|
||||
vRoot = isInRoots(subRoot);
|
||||
if(vRoot != NULL) // CSE
|
||||
{
|
||||
loc = new TreeLocation;
|
||||
loc->rootMiddle = (float)self->FRAMEWIDTH/2 + left;
|
||||
loc->right = self->FRAMEWIDTH + left;
|
||||
loc->top = yBottom + self->FRAMEHEIGHT;
|
||||
loc->bottom = yBottom;
|
||||
DrawSubExpressionNode(subRoot, loc->rootMiddle, loc->bottom);
|
||||
vRoot->locAsCSE->append(*loc);
|
||||
loc = new TreeLocation;
|
||||
loc->rootMiddle = (float)self->FRAMEWIDTH/2 + left;
|
||||
loc->right = self->FRAMEWIDTH + left;
|
||||
loc->top = yBottom + self->FRAMEHEIGHT;
|
||||
loc->bottom = yBottom;
|
||||
return *loc;
|
||||
} else
|
||||
{
|
||||
PrimitiveOperation op = subRoot->getPrimitive()->getOperation();
|
||||
switch (op)
|
||||
{
|
||||
case(poPhi_M):
|
||||
case(poPhi_I):
|
||||
case(poPhi_L):
|
||||
case(poPhi_F):
|
||||
case(poPhi_D):
|
||||
case(poPhi_A):
|
||||
case(poPhi_C):
|
||||
loc = new TreeLocation;
|
||||
loc->rootMiddle = (float)self->FRAMEWIDTH/2 + left;
|
||||
loc->right = self->FRAMEWIDTH + left;
|
||||
loc->top = yBottom + self->FRAMEHEIGHT;
|
||||
loc->bottom = yBottom;
|
||||
DrawDummyNode(loc->rootMiddle, loc->bottom);
|
||||
return *loc;
|
||||
break;
|
||||
default:
|
||||
return DrawTree(*subRoot, yBottom, left);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
} else { // This input is not defined
|
||||
loc = new TreeLocation;
|
||||
loc->rootMiddle = (float)self->FRAMEWIDTH/2 + left;
|
||||
loc->right = self->FRAMEWIDTH + left;
|
||||
loc->top = yBottom + self->FRAMEHEIGHT;
|
||||
loc->bottom = yBottom;
|
||||
if( (curUse->kind == udRegister) && ((*(curUse->name.vr)).index <= self->ARGUMENTREGISTERS) )
|
||||
{
|
||||
DrawArgumentNode(loc->rootMiddle, loc->bottom);
|
||||
} else
|
||||
DrawDummyNode(loc->rootMiddle, loc->bottom);
|
||||
return *loc;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
assert(root.prev != NULL);
|
||||
|
||||
|
||||
|
||||
// Draw your sub trees
|
||||
for(curUse = root.getInstructionUseBegin(); curUse < root.getInstructionUseEnd(); curUse++)
|
||||
{
|
||||
subRoot = CodeGenerator::instructionUseToInstruction(*curUse);
|
||||
|
||||
if(subRoot != NULL)
|
||||
{
|
||||
vRoot = isInRoots(subRoot);
|
||||
if(vRoot != NULL) // CSE
|
||||
{
|
||||
loc = new TreeLocation;
|
||||
loc->rootMiddle = (float)self->FRAMEWIDTH/2 + left;
|
||||
loc->right = self->FRAMEWIDTH + left;
|
||||
loc->top = yBottom + (2 * self->FRAMEHEIGHT);
|
||||
loc->bottom = yBottom + self->FRAMEHEIGHT;
|
||||
DrawSubExpressionNode(subRoot, loc->rootMiddle, loc->bottom);
|
||||
vRoot->locAsCSE->append(*loc);
|
||||
loc = new TreeLocation;
|
||||
loc->rootMiddle = (float)self->FRAMEWIDTH/2 + left;
|
||||
loc->right = self->FRAMEWIDTH + left;
|
||||
loc->top = yBottom + (2 * self->FRAMEHEIGHT);
|
||||
loc->bottom = yBottom + self->FRAMEHEIGHT;
|
||||
} else
|
||||
{
|
||||
PrimitiveOperation op = subRoot->getPrimitive()->getOperation();
|
||||
switch (op)
|
||||
{
|
||||
case(poPhi_M):
|
||||
case(poPhi_I):
|
||||
case(poPhi_L):
|
||||
case(poPhi_F):
|
||||
case(poPhi_D):
|
||||
case(poPhi_A):
|
||||
case(poPhi_C):
|
||||
loc = new TreeLocation;
|
||||
loc->rootMiddle = (float)self->FRAMEWIDTH/2 + left;
|
||||
loc->right = self->FRAMEWIDTH + left;
|
||||
loc->top = yBottom + (2 * self->FRAMEHEIGHT);
|
||||
loc->bottom = yBottom + self->FRAMEHEIGHT;
|
||||
DrawDummyNode(loc->rootMiddle, loc->bottom);
|
||||
break;
|
||||
default:
|
||||
loc = &DrawTree(*subRoot, yBottom + self->FRAMEHEIGHT, left);
|
||||
break;
|
||||
}
|
||||
|
||||
}//loc = &DrawTree(*subRoot, yBottom + self->FRAMEHEIGHT, left);
|
||||
}
|
||||
else // This input is not defined
|
||||
{
|
||||
loc = new TreeLocation;
|
||||
loc->rootMiddle = (float)self->FRAMEWIDTH/2 + left;
|
||||
loc->right = self->FRAMEWIDTH + left;
|
||||
loc->top = yBottom + (2 * self->FRAMEHEIGHT);
|
||||
loc->bottom = yBottom + self->FRAMEHEIGHT;
|
||||
if( (curUse->kind == udRegister) && ((*(curUse->name.vr)).index <= self->ARGUMENTREGISTERS) )
|
||||
{
|
||||
DrawArgumentNode(loc->rootMiddle, loc->bottom);
|
||||
} else
|
||||
DrawDummyNode(loc->rootMiddle, loc->bottom);
|
||||
}
|
||||
|
||||
if(loc->top > top)
|
||||
top = loc->top;
|
||||
|
||||
left = loc->right;
|
||||
subTreeLocations.append(*loc);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
if(subTreeLocations.size() == 0)
|
||||
{
|
||||
myLocation->rootMiddle = (float)self->FRAMEWIDTH/2 + xLeft;
|
||||
myLocation->right = self->FRAMEWIDTH + xLeft;
|
||||
myLocation->top = yBottom + self->FRAMEHEIGHT;
|
||||
myLocation->bottom = yBottom;
|
||||
} else {
|
||||
myLocation->rootMiddle = subTreeLocations.front().rootMiddle + (0.5f)*(subTreeLocations.back().rootMiddle - subTreeLocations.front().rootMiddle);
|
||||
myLocation->right = subTreeLocations.back().right;
|
||||
myLocation->top = top;
|
||||
myLocation->bottom = yBottom;
|
||||
}
|
||||
|
||||
DrawNode(myLocation->rootMiddle, yBottom, &root, subTreeLocations);
|
||||
|
||||
return *myLocation;
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
Dum routine reads a file to a stream, returns size
|
||||
printPretty should use strings
|
||||
*/
|
||||
|
||||
int fileToString(char *fileName, char *string, int len)
|
||||
{
|
||||
|
||||
FILE* file = fopen(fileName, "r");
|
||||
assert(file);
|
||||
|
||||
char c = fgetc(file);
|
||||
int i = 0;
|
||||
while(!feof(file) && i < (len-1))
|
||||
{
|
||||
|
||||
if(c == 9)
|
||||
c = ' ';
|
||||
string[i] = c;
|
||||
i++;
|
||||
c = fgetc(file);
|
||||
}
|
||||
|
||||
string[i] = '\0';
|
||||
fclose(file);
|
||||
return i;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
Write instruction Name and label Registers
|
||||
*/
|
||||
|
||||
void IGVisualizer ::
|
||||
LabelNode(Instruction* inst)
|
||||
{
|
||||
FILE* file;
|
||||
char fileName[] = "shitFile";
|
||||
|
||||
file = fopen(fileName, "w" );
|
||||
char prettyString[50];
|
||||
|
||||
|
||||
// Label instruction *****************************************
|
||||
glColor3f(0.0, 0.0, 0.0);
|
||||
|
||||
glPushMatrix(); // 1
|
||||
glTranslated(3*(float)self->BORDERWIDTH, (float)self->INSTRUCTIONHEIGHT/2.5, 0.0);
|
||||
|
||||
int len, i;
|
||||
|
||||
inst->printPretty(file);
|
||||
fclose(file);
|
||||
len = fileToString(fileName, prettyString, 50);
|
||||
|
||||
for (i = 0; i < len; i++)
|
||||
{
|
||||
glutStrokeCharacter(self->font, prettyString[i]);
|
||||
}
|
||||
glPopMatrix(); // 1
|
||||
|
||||
|
||||
|
||||
// Label Outputs *********************************************
|
||||
glColor3f(0.5f, 0.2f, 0.4f);
|
||||
|
||||
char registerName[50];
|
||||
float distanceBetween;
|
||||
int vrn, j;
|
||||
InstructionDefine* curDefine;
|
||||
|
||||
int numberOfOutputs, numberOfInputs;
|
||||
|
||||
numberOfOutputs= inst->getInstructionDefineEnd() - inst->getInstructionDefineBegin();;
|
||||
assert(numberOfOutputs <= 1);
|
||||
|
||||
distanceBetween = (float)self->INSTRUCTIONWIDTH/(numberOfOutputs+1);
|
||||
|
||||
glPushMatrix(); // 2
|
||||
glTranslatef(0.0, 3*(float)self->BORDERWIDTH, 0.0);
|
||||
|
||||
j=1;
|
||||
for(curDefine = inst->getInstructionDefineBegin(); curDefine < inst->getInstructionDefineEnd(); curDefine++)
|
||||
{
|
||||
|
||||
glPushMatrix(); // 3
|
||||
glTranslatef(j*distanceBetween-100, 0.0, 0.0);
|
||||
|
||||
if(curDefine->kind == udRegister)
|
||||
{
|
||||
vrn = (*(curDefine->name.vr)).index;
|
||||
sprintf(registerName, "R%d", vrn);
|
||||
} else if (curDefine->kind == udCond)
|
||||
sprintf(registerName, "Cond");
|
||||
else
|
||||
sprintf(registerName, "Store");
|
||||
len = strlen(registerName);
|
||||
for (i = 0; i < len; i++)
|
||||
{
|
||||
glutStrokeCharacter(self->font, registerName[i]);
|
||||
}
|
||||
glPopMatrix(); // 3
|
||||
j++;
|
||||
}
|
||||
|
||||
|
||||
glPopMatrix(); // 2
|
||||
|
||||
// Label Inputs **********************************************
|
||||
glColor3f(1.0f, 0.0f, 0.2f);
|
||||
|
||||
InstructionUse* curUse;
|
||||
numberOfInputs = inst->getInstructionUseEnd() - inst->getInstructionUseBegin();
|
||||
distanceBetween = (float)self->INSTRUCTIONWIDTH/(numberOfInputs+1);
|
||||
|
||||
glPushMatrix(); // 4
|
||||
glTranslatef(0.0, self->INSTRUCTIONHEIGHT - 100 - 3*(float)self->BORDERWIDTH, 0.0);
|
||||
j=1;
|
||||
for(curUse = inst->getInstructionUseBegin(); curUse < inst->getInstructionUseEnd(); curUse++)
|
||||
{
|
||||
|
||||
glPushMatrix(); // 5
|
||||
glTranslatef(j*distanceBetween-100, 0.0, 0.0);
|
||||
|
||||
if(curUse->kind == udRegister)
|
||||
{
|
||||
vrn = (*(curUse->name.vr)).index;
|
||||
sprintf(registerName, "R%d", vrn);
|
||||
} else if (curUse->kind == udCond)
|
||||
sprintf(registerName, "Cond");
|
||||
else
|
||||
sprintf(registerName, "Store");
|
||||
|
||||
len = strlen(registerName);
|
||||
for (i = 0; i < len; i++)
|
||||
{
|
||||
glutStrokeCharacter(self->font, registerName[i]);
|
||||
}
|
||||
|
||||
glPopMatrix(); // 5
|
||||
j++;
|
||||
}
|
||||
glPopMatrix(); // 4
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Draw a single instruction with its bottom line centered at (xMiddle, yBottom)
|
||||
*/
|
||||
void IGVisualizer ::
|
||||
DrawNode(double xMiddle, int yBottom, Instruction* inst, Vector<TreeLocation>& subTrees)
|
||||
{
|
||||
|
||||
glPushMatrix(); // 1
|
||||
|
||||
glTranslatef((float)xMiddle - self->INSTRUCTIONWIDTH/2, (float)yBottom, 0);
|
||||
|
||||
// Draw Instruction body *************************************
|
||||
glColor3f(0.0, 1.0, 1.0);
|
||||
glRecti(0, 0, self->INSTRUCTIONWIDTH, self->INSTRUCTIONHEIGHT);
|
||||
|
||||
glColor3f(1.0, 1.0, 1.0);
|
||||
glRecti(self->BORDERWIDTH, self->BORDERWIDTH, self->INSTRUCTIONWIDTH-self->BORDERWIDTH, self->INSTRUCTIONHEIGHT-self->BORDERWIDTH);
|
||||
|
||||
|
||||
LabelNode(inst);
|
||||
|
||||
// Connect Inputs *************************************************
|
||||
TreeLocation* loc;
|
||||
int numberOfInputs = subTrees.size();
|
||||
int distToNextLayer = self->FRAMEHEIGHT - self->INSTRUCTIONHEIGHT;
|
||||
float jag = ((float)distToNextLayer/(numberOfInputs + 1));
|
||||
float diffMid;
|
||||
float distanceBetween = (float)self->INSTRUCTIONWIDTH/(numberOfInputs+1);
|
||||
|
||||
glPushMatrix(); // 2
|
||||
glTranslatef(0.0, (float)self->INSTRUCTIONHEIGHT, 0.0);
|
||||
|
||||
int j=1;
|
||||
for(loc = subTrees.begin(); loc < subTrees.end(); loc++)
|
||||
{
|
||||
|
||||
glPushMatrix(); // 3
|
||||
glTranslatef(j*distanceBetween, 0.0, 0.0);
|
||||
|
||||
diffMid = loc->rootMiddle - (float)xMiddle - (j*distanceBetween - ((0.5f)*self->INSTRUCTIONWIDTH));
|
||||
|
||||
glBegin(GL_LINES);
|
||||
glVertex2f(0.0f, 0.0f);
|
||||
glVertex2f(0.0f, j*jag);
|
||||
glVertex2f(0.0f, j*jag);
|
||||
glVertex2f(diffMid, j*jag);
|
||||
glVertex2f(diffMid, j*jag);
|
||||
glVertex2f(diffMid, (numberOfInputs + 1)*jag);
|
||||
glEnd();
|
||||
|
||||
glPopMatrix(); // 3
|
||||
j++;
|
||||
}
|
||||
glPopMatrix(); // 2
|
||||
|
||||
|
||||
glPopMatrix(); // 1
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
Draw a node as a subexpression
|
||||
*/
|
||||
void IGVisualizer ::
|
||||
DrawSubExpressionNode(Instruction* inst, double xMiddle, double yBottom)
|
||||
{
|
||||
glPushMatrix(); // 1
|
||||
|
||||
glTranslatef((float)xMiddle - self->INSTRUCTIONWIDTH/2, (float)yBottom, 0);
|
||||
|
||||
// Draw Instruction body *************************************
|
||||
glColor3f(1.0, 1.0, 0.0);
|
||||
glRecti(0, 0, self->INSTRUCTIONWIDTH, self->INSTRUCTIONHEIGHT);
|
||||
|
||||
glColor3f(1.0, 1.0, 1.0);
|
||||
glRecti(self->BORDERWIDTH, self->BORDERWIDTH, self->INSTRUCTIONWIDTH-self->BORDERWIDTH, self->INSTRUCTIONHEIGHT-self->BORDERWIDTH);
|
||||
|
||||
|
||||
|
||||
// Draw the node
|
||||
LabelNode(inst);
|
||||
|
||||
glPopMatrix(); // 1
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
Draw a dummy "unknown" instruction with its bottom line centered at (xMiddle, yBottom)
|
||||
*/
|
||||
void IGVisualizer ::
|
||||
DrawDummyNode(double xMiddle, int yBottom)
|
||||
{
|
||||
char unknownMess[] = " ??? ";
|
||||
|
||||
glPushMatrix(); // 1
|
||||
|
||||
glTranslatef((float)xMiddle - self->INSTRUCTIONWIDTH/2, (float)yBottom, 0);
|
||||
|
||||
// Draw Instruction body *************************************
|
||||
glColor3f(1.0, 0.0, 0.0);
|
||||
glRecti(0, 0, self->INSTRUCTIONWIDTH, self->INSTRUCTIONHEIGHT);
|
||||
|
||||
glColor3f(1.0, 1.0, 1.0);
|
||||
glRecti(5 * self->BORDERWIDTH, 5 * self->BORDERWIDTH, self->INSTRUCTIONWIDTH-(5*self->BORDERWIDTH), self->INSTRUCTIONHEIGHT-(5*self->BORDERWIDTH));
|
||||
|
||||
// Write Message ******************************************
|
||||
glColor3f(1.0, 0.0, 0.0);
|
||||
glPushMatrix(); // 2
|
||||
glTranslated(3*(float)self->BORDERWIDTH, (float)self->INSTRUCTIONHEIGHT/2.5, 0.0);
|
||||
|
||||
int len, i;
|
||||
len = (int) strlen(unknownMess);
|
||||
for (i = 0; i < len; i++)
|
||||
{
|
||||
glutStrokeCharacter(self->font, unknownMess[i]);
|
||||
}
|
||||
glPopMatrix(); // 2
|
||||
|
||||
glPopMatrix(); //1
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
Draw an argument node
|
||||
*/
|
||||
void IGVisualizer ::
|
||||
DrawArgumentNode(double xMiddle, int yBottom)
|
||||
{
|
||||
char unknownMess[] = " Argument ";
|
||||
|
||||
glPushMatrix(); // 1
|
||||
|
||||
glTranslatef((float)xMiddle - self->INSTRUCTIONWIDTH/2, (float)yBottom, 0);
|
||||
|
||||
// Draw Instruction body *************************************
|
||||
glColor3f(0.0f, 1.0f, 0.3f);
|
||||
glRecti(0, 0, self->INSTRUCTIONWIDTH, self->INSTRUCTIONHEIGHT);
|
||||
|
||||
glColor3f(1.0f, 1.0f, 1.0f);
|
||||
glRecti(5 * self->BORDERWIDTH, 5 * self->BORDERWIDTH, self->INSTRUCTIONWIDTH-(5*self->BORDERWIDTH), self->INSTRUCTIONHEIGHT-(5*self->BORDERWIDTH));
|
||||
|
||||
// Write Message ******************************************
|
||||
glColor3f(0.0f, 1.0f, 0.3f);
|
||||
glPushMatrix(); // 2
|
||||
glTranslated(3*(float)self->BORDERWIDTH, (float)self->INSTRUCTIONHEIGHT/2.5, 0.0);
|
||||
|
||||
int len, i;
|
||||
len = (int) strlen(unknownMess);
|
||||
for (i = 0; i < len; i++)
|
||||
{
|
||||
glutStrokeCharacter(self->font, unknownMess[i]);
|
||||
}
|
||||
glPopMatrix(); // 2
|
||||
|
||||
glPopMatrix(); //1
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
This is the window system specific stuff for setting up the window for gl.
|
||||
Visualize is called only once and it enters a non-returning display loop.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
enum subMenuFields
|
||||
{
|
||||
findAsRoot,
|
||||
findNextRef
|
||||
};
|
||||
|
||||
void IGVisualizer :: visualize()
|
||||
{
|
||||
int zoomMenu, rootMenu;
|
||||
char* argv ="myProgram";
|
||||
int argc = 1;
|
||||
|
||||
// Initialization
|
||||
glutInit(&argc, &argv);
|
||||
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);
|
||||
glutInitWindowSize(xWindowDim, yWindowDim);
|
||||
glutCreateWindow("Instruction Graph Viewer Dulux");
|
||||
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glLoadIdentity();
|
||||
gluOrtho2D(-xDimGL, xDimGL, -yDimGL, yDimGL);
|
||||
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glEnable(GL_LINE_SMOOTH);
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
glLineWidth(1.0);
|
||||
|
||||
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
||||
glColor3f(1.0f, 1.0f, 1.0f);
|
||||
|
||||
// Add callbacks
|
||||
glutDisplayFunc(display);
|
||||
glutMouseFunc(mouse);
|
||||
glutKeyboardFunc(keyboard);
|
||||
glutReshapeFunc(reshape);
|
||||
|
||||
// Create the menu
|
||||
zoomMenu = glutCreateMenu(selectZoom);
|
||||
glutAddMenuEntry("X 2", 2);
|
||||
glutAddMenuEntry("X 5", 5);
|
||||
rootMenu = glutCreateMenu(selectRoot);
|
||||
glutAddMenuEntry("Goto Definition", findAsRoot);
|
||||
glutAddMenuEntry("Goto Next Reference", findNextRef);
|
||||
glutCreateMenu(mainMenu);
|
||||
glutAddSubMenu("ZoomFactor", zoomMenu);
|
||||
glutAddSubMenu("Explore Subexpression", rootMenu);
|
||||
glutAddMenuEntry("Exit", 0);
|
||||
glutAttachMenu(GLUT_RIGHT_BUTTON);
|
||||
|
||||
glutMainLoop();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
The callback routines
|
||||
*/
|
||||
|
||||
void IGVisualizer ::
|
||||
display()
|
||||
{
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
glPushMatrix(); // 1
|
||||
glScaled(self->cumZoom, self->cumZoom, 1);
|
||||
glTranslated(self->centerX - self->objectCenterX + self->mouseCenterX, self->centerY - self->objectCenterY + self->mouseCenterY, 0);
|
||||
|
||||
visualizeRoots();
|
||||
|
||||
glPopMatrix(); // 1
|
||||
|
||||
glutSwapBuffers();
|
||||
}
|
||||
|
||||
void IGVisualizer ::
|
||||
selectZoom(int msg)
|
||||
{
|
||||
self->zoomFactor = float(msg);
|
||||
glutPostRedisplay();
|
||||
}
|
||||
|
||||
|
||||
void IGVisualizer ::
|
||||
selectRoot(int msg)
|
||||
{
|
||||
VisualInstructionRoot* vRoot = findRootAtLoc(self->nodeLocX, self->nodeLocY);
|
||||
if(vRoot != NULL)
|
||||
{
|
||||
//printf("FOUND ONE\n");
|
||||
switch(msg) {
|
||||
case findAsRoot:
|
||||
self->objectCenterX = vRoot->locAsRoot->rootMiddle;
|
||||
self->objectCenterY = vRoot->locAsRoot->bottom;
|
||||
self->mouseCenterX = 0;
|
||||
self->mouseCenterY = 0;
|
||||
break;
|
||||
case findNextRef:
|
||||
if(vRoot->currentCSE == NULL)
|
||||
if(vRoot->locAsCSE->size() != 0)
|
||||
vRoot->currentCSE = vRoot->locAsCSE->begin();
|
||||
else
|
||||
return;
|
||||
else {
|
||||
vRoot->currentCSE++;
|
||||
if(vRoot->currentCSE == vRoot->locAsCSE->end())
|
||||
vRoot->currentCSE = vRoot->locAsCSE->begin();
|
||||
}
|
||||
self->objectCenterX = vRoot->currentCSE->rootMiddle;
|
||||
self->objectCenterY = vRoot->currentCSE->bottom;
|
||||
self->mouseCenterX = 0;
|
||||
self->mouseCenterY = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
glutPostRedisplay();
|
||||
}
|
||||
|
||||
|
||||
VisualInstructionRoot* IGVisualizer ::
|
||||
findRootAtLoc(double x, double y)
|
||||
{
|
||||
|
||||
VisualInstructionRoot* vRoot;
|
||||
TreeLocation* tree;
|
||||
for(vRoot = self->roots->begin(); vRoot < self->roots->end(); vRoot++)
|
||||
{
|
||||
if(isInNode(x, y, vRoot->locAsRoot->rootMiddle, vRoot->locAsRoot->bottom))
|
||||
return vRoot;
|
||||
for(tree = vRoot->locAsCSE->begin(); tree < vRoot->locAsCSE->end(); tree++)
|
||||
if(isInNode(x, y, tree->rootMiddle, tree->bottom))
|
||||
return vRoot;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool IGVisualizer ::
|
||||
isInNode(double x, double y, double middle, int bottom)
|
||||
{
|
||||
if((x < (middle + (.5*self->INSTRUCTIONWIDTH))) && (x > (middle - (.5*self->INSTRUCTIONWIDTH))))
|
||||
if((y > bottom) && (y < (bottom + self->INSTRUCTIONHEIGHT)))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void IGVisualizer ::
|
||||
mainMenu(int msg)
|
||||
{
|
||||
switch(msg) {
|
||||
case 0:
|
||||
exit(0);
|
||||
break;
|
||||
}
|
||||
|
||||
glutPostRedisplay();
|
||||
}
|
||||
|
||||
void IGVisualizer ::
|
||||
mouse(int button, int state, int x, int y)
|
||||
{
|
||||
|
||||
if(button == GLUT_LEFT_BUTTON && state == GLUT_DOWN)
|
||||
{
|
||||
double transX = -(x - (.5 * (self->xWindowDim)))*((2*self->xDimGL/self->xWindowDim)/self->cumZoom);
|
||||
double transY = (y - (.5 * (self->yWindowDim)))*((2*self->yDimGL/self->yWindowDim)/self->cumZoom);
|
||||
self->mouseCenterX = self->mouseCenterX + transX;
|
||||
self->mouseCenterY = self->mouseCenterY + transY;
|
||||
}
|
||||
|
||||
glutPostRedisplay();
|
||||
}
|
||||
|
||||
void IGVisualizer ::
|
||||
keyboard(unsigned char key, int x, int y)
|
||||
{
|
||||
switch(key){
|
||||
case '+':
|
||||
self->cumZoom = self->cumZoom * self->zoomFactor;
|
||||
break;
|
||||
case '-':
|
||||
self->cumZoom = self->cumZoom / self->zoomFactor;
|
||||
break;
|
||||
case 's':
|
||||
double transX = (x - (.5 * (self->xWindowDim)))*((2*self->xDimGL/self->xWindowDim)/self->cumZoom);
|
||||
double transY = -(y - (.5 * (self->yWindowDim)))*((2*self->yDimGL/self->yWindowDim)/self->cumZoom);
|
||||
|
||||
//printf("mouse center**%f, %f**\n", self->mouseCenterX, self->mouseCenterY);
|
||||
//printf("strans **%f, %f**\n", transX, transY);
|
||||
//printf("obj center **%f, %f**\n", self->objectCenterX, self->objectCenterY);
|
||||
|
||||
self->nodeLocX = transX - self->mouseCenterX + self->objectCenterX;
|
||||
self->nodeLocY = transY - self->mouseCenterY + self->objectCenterY;
|
||||
|
||||
//printf("node center **%f, %f**\n", self->nodeLocX, self->nodeLocY);
|
||||
break;
|
||||
}
|
||||
glutPostRedisplay();
|
||||
}
|
||||
|
||||
void IGVisualizer ::
|
||||
reshape(int width, int height)
|
||||
{
|
||||
self->xWindowDim = width;
|
||||
self->yWindowDim = height;
|
||||
|
||||
glViewport(0, 0, width, height);
|
||||
glutPostRedisplay();
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,106 @@
|
|||
/* -*- 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.
|
||||
*/
|
||||
/*
|
||||
The instruction graph visulizer.
|
||||
IGVisualizer.h
|
||||
|
||||
Peter DeSantis
|
||||
*/
|
||||
|
||||
#ifndef IGVISUALIZER
|
||||
#define IGVISUALIZER
|
||||
#if defined(DEBUG) && (defined(WIN32) || defined(USE_MESA)) && defined(IGVISUALIZE)
|
||||
|
||||
#include "CodeGenerator.h"
|
||||
class Instruction;
|
||||
class Vector;
|
||||
|
||||
struct TreeLocation
|
||||
{
|
||||
float rootMiddle;
|
||||
int right;
|
||||
int top;
|
||||
int bottom;
|
||||
};
|
||||
|
||||
struct VisualInstructionRoot
|
||||
{
|
||||
Instruction* root;
|
||||
bool exploringAsRoot;
|
||||
Vector<TreeLocation>* locAsCSE;
|
||||
TreeLocation* currentCSE;
|
||||
TreeLocation* locAsRoot;
|
||||
};
|
||||
|
||||
class IGVisualizer
|
||||
{
|
||||
|
||||
private:
|
||||
void *font;
|
||||
float zoomFactor;
|
||||
double cumZoom;
|
||||
double centerX, centerY;
|
||||
double objectCenterX, objectCenterY;
|
||||
double mouseCenterX, mouseCenterY;
|
||||
double xDimGL, yDimGL;
|
||||
double nodeLocX, nodeLocY;
|
||||
|
||||
|
||||
int xWindowDim, yWindowDim;
|
||||
|
||||
int INSTRUCTIONWIDTH, INSTRUCTIONHEIGHT;
|
||||
int FRAMEWIDTH, FRAMEHEIGHT;
|
||||
|
||||
int SPACEBETWEENTREES;
|
||||
int BORDERWIDTH;
|
||||
uint ARGUMENTREGISTERS;
|
||||
static IGVisualizer* self;
|
||||
|
||||
Vector<VisualInstructionRoot>* roots;
|
||||
|
||||
static void visualizeRoots();
|
||||
static TreeLocation& DrawTree(Instruction& root, int yBottom, int xLeft);
|
||||
static void DrawNode(double xMiddle, int yBottom, Instruction* inst, Vector<TreeLocation>& subTrees);
|
||||
static void DrawDummyNode(double xMiddle, int yBottom);
|
||||
static void DrawArgumentNode(double xMiddle, int yBottom);
|
||||
static VisualInstructionRoot* isInRoots(Instruction* root);
|
||||
static void DrawSubExpressionNode(Instruction* inst, double xMIddle, double yBottom);
|
||||
static void LabelNode(Instruction* inst);
|
||||
static VisualInstructionRoot* findRootAtLoc(double x, double y);
|
||||
static bool isInNode(double x, double y, double middle, int bottom);
|
||||
|
||||
public:
|
||||
static void selectZoom(int msg);
|
||||
static void selectRoot(int msg);
|
||||
static void mainMenu(int msg);
|
||||
static void mouse(int button, int state, int x, int y);
|
||||
static void keyboard(unsigned char key, int x, int y);
|
||||
static void reshape(int width, int height);
|
||||
static void tick(void);
|
||||
static void display();
|
||||
|
||||
void addRoots(Vector<RootPair>& roots);
|
||||
void visualize();
|
||||
IGVisualizer();
|
||||
~IGVisualizer();
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif //#if defined(DEBUG) && defined(WIN32) && defined(IGVISUALIZE)
|
||||
#endif // IGVISUALIZER
|
|
@ -0,0 +1,316 @@
|
|||
/* -*- 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.
|
||||
*/
|
||||
/*
|
||||
Instruction.cpp
|
||||
|
||||
Scott M. Silver
|
||||
*/
|
||||
|
||||
#include "Instruction.h"
|
||||
#include "Pool.h"
|
||||
#include "InstructionEmitter.h"
|
||||
|
||||
|
||||
Instruction::
|
||||
Instruction(DataNode* inSrcPrimitive)
|
||||
{
|
||||
//DEBUG_ONLY(initialize());
|
||||
DEBUG_ONLY(mDebug = 0xDEADBEEF);
|
||||
mSrcPrimitive = inSrcPrimitive;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// unlinkRegisters
|
||||
//
|
||||
// remove all references to VirtualRegister
|
||||
// for this Instruction.
|
||||
void Instruction::
|
||||
unlinkRegisters()
|
||||
{
|
||||
InstructionUse* use_limit = getInstructionUseEnd();
|
||||
for(InstructionUse* use_ptr = getInstructionUseBegin(); use_ptr < use_limit; use_ptr++)
|
||||
if (use_ptr->kind == udRegister)
|
||||
use_ptr->name.vr.uninitialize();
|
||||
|
||||
InstructionDefine* def_limit = getInstructionDefineEnd();
|
||||
for(InstructionDefine* def_ptr = getInstructionDefineBegin(); def_ptr < def_limit; def_ptr++)
|
||||
if (def_ptr->kind == udRegister)
|
||||
def_ptr->name.vr.uninitialize();
|
||||
}
|
||||
|
||||
//#ifdef DEBUG
|
||||
void Instruction::
|
||||
initialize()
|
||||
{
|
||||
// clear out use's
|
||||
InstructionUse* curUse;
|
||||
|
||||
for ( curUse = getInstructionUseBegin();
|
||||
curUse < getInstructionUseEnd();
|
||||
curUse++)
|
||||
{
|
||||
curUse[0].name.dp = NULL;
|
||||
curUse[0].src = NULL;
|
||||
curUse[0].kind = udUninitialized;
|
||||
}
|
||||
|
||||
InstructionDefine* curDefine;
|
||||
|
||||
for ( curDefine = getInstructionDefineBegin();
|
||||
curDefine < getInstructionDefineEnd();
|
||||
curDefine++)
|
||||
{
|
||||
curDefine[0].name.dp = NULL;
|
||||
curDefine[0].kind = udUninitialized;
|
||||
}
|
||||
}
|
||||
//#endif
|
||||
|
||||
|
||||
// addUse
|
||||
//
|
||||
//
|
||||
void Instruction::
|
||||
addUse(Uint8 inWhichInput, VirtualRegister& inVR, VirtualRegisterKind constraint)
|
||||
{
|
||||
InstructionUse* instructionUseBegin = getInstructionUseBegin();
|
||||
|
||||
assert(instructionUseBegin != NULL && getInstructionUseEnd() - inWhichInput > instructionUseBegin);
|
||||
|
||||
instructionUseBegin[inWhichInput].name.vr.initialize(inVR, constraint);
|
||||
instructionUseBegin[inWhichInput].src = inVR.getDefiningInstruction();
|
||||
instructionUseBegin[inWhichInput].kind = udRegister;
|
||||
}
|
||||
|
||||
|
||||
void Instruction::
|
||||
addUse(Uint8 inWhichInput, DataNode& inProducer)
|
||||
{
|
||||
InstructionUse* instructionUseBegin = getInstructionUseBegin();
|
||||
|
||||
assert(instructionUseBegin != NULL && getInstructionUseEnd() - inWhichInput > instructionUseBegin);
|
||||
assert(inProducer.hasKind(vkCond) || inProducer.hasKind(vkMemory));
|
||||
|
||||
instructionUseBegin[inWhichInput].name.dp = &inProducer;
|
||||
|
||||
if (inProducer.hasKind(vkCond))
|
||||
instructionUseBegin[inWhichInput].kind = udCond;
|
||||
else
|
||||
instructionUseBegin[inWhichInput].kind = udStore;
|
||||
}
|
||||
|
||||
|
||||
void Instruction::
|
||||
addUse(Uint8 inWhichInput, UseDefineKind inKind)
|
||||
{
|
||||
InstructionUse* instructionUseBegin = getInstructionUseBegin();
|
||||
|
||||
assert(instructionUseBegin != NULL && getInstructionUseEnd() - inWhichInput > instructionUseBegin);
|
||||
|
||||
instructionUseBegin[inWhichInput].name.dp = NULL;
|
||||
instructionUseBegin[inWhichInput].kind = inKind;
|
||||
}
|
||||
|
||||
|
||||
void Instruction::
|
||||
addUse(Uint8 inWhichInput, InstructionDefine& inUseOrder)
|
||||
{
|
||||
InstructionUse* instructionUseBegin = getInstructionUseBegin();
|
||||
|
||||
assert(instructionUseBegin != NULL && getInstructionUseEnd() - inWhichInput > instructionUseBegin);
|
||||
|
||||
instructionUseBegin[inWhichInput].name.instruction = inUseOrder.name.instruction;
|
||||
instructionUseBegin[inWhichInput].kind = udOrder;
|
||||
}
|
||||
|
||||
|
||||
void Instruction::
|
||||
addDefine( Uint8 inWhichOutput, VirtualRegister& inVR, VirtualRegisterKind constraint)
|
||||
{
|
||||
InstructionDefine* instructionDefineBegin = getInstructionDefineBegin();
|
||||
|
||||
assert(instructionDefineBegin != NULL && getInstructionDefineEnd() - inWhichOutput > instructionDefineBegin);
|
||||
|
||||
instructionDefineBegin[inWhichOutput].name.vr.initialize(inVR, constraint);
|
||||
instructionDefineBegin[inWhichOutput].kind = udRegister;
|
||||
}
|
||||
|
||||
|
||||
void Instruction::
|
||||
addDefine(Uint8 inWhichOutput, DataNode& inProducer)
|
||||
{
|
||||
InstructionDefine* instructionDefineBegin = getInstructionDefineBegin();
|
||||
|
||||
assert(instructionDefineBegin != NULL && getInstructionDefineEnd() - inWhichOutput > instructionDefineBegin);
|
||||
assert(inProducer.hasKind(vkCond) || inProducer.hasKind(vkMemory));
|
||||
|
||||
if (inProducer.hasKind(vkCond))
|
||||
instructionDefineBegin[inWhichOutput].kind = udCond;
|
||||
else
|
||||
instructionDefineBegin[inWhichOutput].kind = udStore;
|
||||
}
|
||||
|
||||
|
||||
void Instruction::
|
||||
addDefine(Uint8 inWhichOutput, UseDefineKind inKind)
|
||||
{
|
||||
InstructionDefine* instructionDefineBegin = getInstructionDefineBegin();
|
||||
|
||||
assert(instructionDefineBegin != NULL && getInstructionDefineEnd() - inWhichOutput > instructionDefineBegin);
|
||||
|
||||
instructionDefineBegin[inWhichOutput].name.dp = NULL;
|
||||
instructionDefineBegin[inWhichOutput].kind = inKind;
|
||||
}
|
||||
|
||||
|
||||
void Instruction::
|
||||
addDefine(Uint8 inWhichOutput, InstructionDefine& /*inDefineOrder*/)
|
||||
{
|
||||
InstructionDefine* instructionDefineBegin = getInstructionDefineBegin();
|
||||
|
||||
assert(instructionDefineBegin != NULL && getInstructionDefineEnd() - inWhichOutput > instructionDefineBegin);
|
||||
|
||||
//instructionDefineBegin[inWhichOutput].name.instruction = this;
|
||||
instructionDefineBegin[inWhichOutput].kind = udOrder;
|
||||
}
|
||||
|
||||
void Instruction::
|
||||
standardUseDefine(InstructionEmitter& inEmitter)
|
||||
{
|
||||
InstructionUse* instructionUseBegin = getInstructionUseBegin();
|
||||
Uint8 curIndex;
|
||||
|
||||
standardDefine(inEmitter);
|
||||
|
||||
InstructionUse* curUse;
|
||||
|
||||
if (instructionUseBegin != NULL)
|
||||
{
|
||||
for (curUse = instructionUseBegin, curIndex = 0; curUse < getInstructionUseEnd(); curUse++, curIndex++)
|
||||
{
|
||||
addStandardUse(inEmitter, curIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
VirtualRegister* Instruction::
|
||||
addStandardUse(InstructionEmitter& inEmitter, Uint8 curIndex)
|
||||
{
|
||||
assert (mSrcPrimitive != NULL);
|
||||
return inEmitter.useProducer(mSrcPrimitive->nthInputVariable(curIndex), *this, curIndex);
|
||||
}
|
||||
|
||||
void Instruction::
|
||||
standardDefine(InstructionEmitter& inEmitter)
|
||||
{
|
||||
InstructionDefine* instructionDefineBegin = getInstructionDefineBegin();
|
||||
Uint8 curIndex;
|
||||
|
||||
assert (mSrcPrimitive != NULL);
|
||||
|
||||
if (instructionDefineBegin != NULL)
|
||||
{
|
||||
InstructionDefine* curDefine;
|
||||
|
||||
for (curDefine = instructionDefineBegin, curIndex = 0; curDefine < getInstructionDefineEnd(); curDefine++, curIndex++)
|
||||
{
|
||||
assert(curIndex == 0);
|
||||
inEmitter.defineProducer(*mSrcPrimitive, *this, curIndex);
|
||||
//inEmitter.defineProducer(nthOutputProducer(*mSrcPrimitive, curIndex), *this, curIndex);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
mSrcPrimitive->setInstructionRoot(this);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DEBUG_LOG
|
||||
void Instruction::
|
||||
printDebug(LogModuleObject &f)
|
||||
{
|
||||
printPretty(f);
|
||||
UT_OBJECTLOG(f, PR_LOG_ALWAYS, ("\n"));
|
||||
// clear out use's
|
||||
InstructionUse* curUse;
|
||||
int i;
|
||||
|
||||
i=0;
|
||||
for ( curUse = getInstructionUseBegin();
|
||||
curUse < getInstructionUseEnd();
|
||||
curUse++)
|
||||
{
|
||||
switch (curUse[0].kind)
|
||||
{
|
||||
case udRegister:
|
||||
{
|
||||
VirtualRegister& vReg = curUse[0].getVirtualRegister();\
|
||||
UT_OBJECTLOG(f, PR_LOG_ALWAYS, ("u[%d]: %scolor = %d index = %d \n", i, vReg.isPreColored() ? "pre" : "", vReg.isPreColored() ?
|
||||
vReg.getPreColor() : vReg.getColor(), vReg.getRegisterIndex()));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
InstructionDefine* curDefine;
|
||||
|
||||
i=0;
|
||||
for ( curDefine = getInstructionDefineBegin();
|
||||
curDefine < getInstructionDefineEnd();
|
||||
curDefine++)
|
||||
{
|
||||
switch (curDefine[0].kind)
|
||||
{
|
||||
case udRegister:
|
||||
{
|
||||
VirtualRegister& vReg = curDefine[0].getVirtualRegister();
|
||||
UT_OBJECTLOG(f, PR_LOG_ALWAYS, ("d[%d]: %scolor = %d index = %d\n", i, vReg.isPreColored() ? "pre" : "",
|
||||
vReg.isPreColored() ? vReg.getPreColor() : vReg.getColor(), vReg.getRegisterIndex()));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
InsnUseXDefineYFromPool::
|
||||
InsnUseXDefineYFromPool(DataNode* inSrcPrimitive, Pool& inPool, Uint8 inX, Uint8 inY) :
|
||||
Instruction(inSrcPrimitive)
|
||||
{
|
||||
mX = inX;
|
||||
mY = inY;
|
||||
if (inX > 0)
|
||||
mInsnUse = new(inPool) InstructionUse[inX];
|
||||
else
|
||||
mInsnUse = NULL;
|
||||
|
||||
if (inY > 0)
|
||||
mDefineResource = new(inPool) InstructionDefine[inY];
|
||||
else
|
||||
mDefineResource = NULL;
|
||||
|
||||
initialize();
|
||||
}
|
|
@ -0,0 +1,272 @@
|
|||
/* -*- 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.
|
||||
*/
|
||||
//
|
||||
// Instruction.h
|
||||
//
|
||||
// Scott M. Silver
|
||||
// Peter Desantis
|
||||
// Laurent Morichetti
|
||||
//
|
||||
// Interface for schedulable and formattable instructions.
|
||||
|
||||
// Summary
|
||||
//
|
||||
// Instructions are medium weight representations of actual native instructions
|
||||
// on a machine. They are manipulated for register allocation, scheduling
|
||||
// and eventually to output themselves to memory.
|
||||
//
|
||||
// The ideas is that subclasses of Instruction (or one of the "helper" subclasses)
|
||||
// are the basic unit of "cross-platform" manipulation. They contain
|
||||
// all resources consumed or created (ie registers, memory, etc).
|
||||
//
|
||||
// Each instruction must implement a series of "interface" methods which
|
||||
// are essentially queries, so that subsequent users of Instructions can
|
||||
// make intelligent decision. (eg. whether an instruction is a call, so
|
||||
// the register allocator can save non-volatiles)
|
||||
|
||||
#ifndef INSTRUCTION_H
|
||||
#define INSTRUCTION_H
|
||||
|
||||
#include "prtypes.h"
|
||||
#include "Primitives.h"
|
||||
#include "CpuInfo.h"
|
||||
|
||||
class Instruction;
|
||||
class VirtualRegister;
|
||||
class InstructionEmitter;
|
||||
|
||||
#ifdef __MWERKS__
|
||||
#pragma mark -
|
||||
#pragma mark ¥ÊInstructionUse/Define ¥
|
||||
#endif
|
||||
|
||||
// InstructionUseOrDefine is a class which represents the
|
||||
// use or definition of a resource (either a store, cond, or
|
||||
// register resource). Assigned to each use and define
|
||||
// is a ResourceName which is the "name" of the resource
|
||||
// being use, eg a virtual register. In addition InstructionUse's
|
||||
// (not defines) contain (if available) the definer of the resource
|
||||
// they are using. This is so that resources can be defined multiple
|
||||
// times, but (data) dependencies remain accurate.
|
||||
typedef enum
|
||||
{
|
||||
udNone = 0,
|
||||
udUninitialized,
|
||||
udStore,
|
||||
udCond,
|
||||
udOrder,
|
||||
udRegister
|
||||
} UseDefineKind;
|
||||
|
||||
struct ResourceName
|
||||
{
|
||||
VirtualRegisterPtr vr;
|
||||
DataNode* dp;
|
||||
Instruction* instruction;
|
||||
};
|
||||
|
||||
struct InstructionUseOrDefine
|
||||
{
|
||||
UseDefineKind kind;
|
||||
ResourceName name;
|
||||
|
||||
inline PRUint32 getRegisterIndex() { assert(isVirtualRegister()); return getVirtualRegister().getRegisterIndex(); }
|
||||
inline VirtualRegister& getVirtualRegister() { assert(isVirtualRegister()); return (name.vr.getVirtualRegister()); }
|
||||
inline VirtualRegisterPtr& getVirtualRegisterPtr() { assert(isVirtualRegister()); return (name.vr); }
|
||||
inline bool isVirtualRegister() { return (kind >= udRegister); }
|
||||
};
|
||||
|
||||
struct InstructionUse : InstructionUseOrDefine
|
||||
{
|
||||
Instruction* src;
|
||||
|
||||
Instruction& getDefiningInstruction() { return (*src); }
|
||||
void setDefiningInstruction(Instruction& inInstruction) { src = &inInstruction; }
|
||||
};
|
||||
|
||||
struct InstructionDefine : InstructionUseOrDefine
|
||||
{
|
||||
};
|
||||
|
||||
#ifdef __MWERKS__
|
||||
#pragma mark -
|
||||
#pragma mark ¥ÊInstructionFlags ¥
|
||||
#endif
|
||||
|
||||
// This is a bitmap of interesting things an instruction might do
|
||||
typedef Uint16 InstructionFlags;
|
||||
enum
|
||||
{
|
||||
ifNone = 0x0000, // default
|
||||
ifModCC = 0x0001, // defines a condition edge (eg. cmp)
|
||||
ifModStore = 0x0002, // modifies the store (eg. st)
|
||||
ifCopy = 0x0004, // copies its uses to its defines (eg mov)
|
||||
ifExternalInsn = 0x0008, // used by the register allocator to keep track of uses/defines outside of a function
|
||||
ifCall = 0x0010, // makes a call - indicates that non-volatile registers should be saved before this instruction (eg call)
|
||||
ifSpecialCall = 0x0020 // FIX-ME Laurent!!! If you ad a new flag please document it!!!
|
||||
};
|
||||
|
||||
#ifdef __MWERKS__
|
||||
#pragma mark -
|
||||
#pragma mark ¥ÊInstruction ¥
|
||||
#endif
|
||||
|
||||
class Instruction :
|
||||
public DoublyLinkedEntry<Instruction> // Inserted into a list of scheduled instructions
|
||||
{
|
||||
public:
|
||||
Instruction(DataNode* inSrcPrimitive);
|
||||
|
||||
void addUse(Uint8 inWhichInput, VirtualRegister& inVR, VRClass cl = vrcInteger);
|
||||
void addUse(Uint8 inWhichInput, DataNode& inProducer);
|
||||
void addUse(Uint8 inWhichInput, UseDefineKind inKind);
|
||||
void addUse(Uint8 inWhichInput, InstructionDefine& inUseOrder);
|
||||
|
||||
void addDefine(Uint8 inWhichOutput, DataNode& inProducer);
|
||||
void addDefine(Uint8 inWhichOutput, VirtualRegister& inProducer, VRClass cl = vrcInteger);
|
||||
void addDefine(Uint8 inWhichOutput, UseDefineKind inKind);
|
||||
void addDefine(Uint8 inWhichOutput, InstructionDefine& inDefineOrder);
|
||||
|
||||
virtual InstructionUse* getInstructionUseBegin() = 0;
|
||||
virtual InstructionUse* getInstructionUseEnd() = 0;
|
||||
|
||||
virtual InstructionDefine* getInstructionDefineBegin() = 0;
|
||||
virtual InstructionDefine* getInstructionDefineEnd() = 0;
|
||||
|
||||
void standardUseDefine(InstructionEmitter& inEmitter);
|
||||
|
||||
virtual PRUint32* getExtraRegisterInterferenceBegin() { return NULL; } // what?? laurent-try again. other registers defined by this instruction.
|
||||
virtual PRUint32* getExtraRegisterInterferenceEnd() { return NULL; }
|
||||
|
||||
inline const Uint16 getLineNumber() const { return (mSrcPrimitive->getLineNumber()); }
|
||||
inline DataNode* getPrimitive() const { return( mSrcPrimitive); }
|
||||
virtual InstructionFlags getFlags() const { return (ifNone); }
|
||||
|
||||
virtual size_t getFormattedSize(MdFormatter& /*inFormatter*/) { return (0); }
|
||||
virtual void formatToMemory(void * /*inStart*/, Uint32 /*inCurOffset*/, MdFormatter& /*inFormatter*/) { }
|
||||
|
||||
virtual bool switchUseToSpill(Uint8 /*inWhichUse*/, VirtualRegister& /*inVR*/) { return false; }
|
||||
virtual bool switchDefineToSpill(Uint8 /*inWhichDefine*/, VirtualRegister& /*inVR*/) { return false; }
|
||||
|
||||
void unlinkRegisters(); // uninitialize the VirtualRegisterPtr for each use and define
|
||||
virtual bool canSwitchToFormat(Uint32 /*mask*/) { return false; }
|
||||
virtual void switchToFormat(Uint32 /*mask*/) {}
|
||||
|
||||
protected:
|
||||
void standardDefine(InstructionEmitter& inEmitter);
|
||||
VirtualRegister* addStandardUse(InstructionEmitter& inEmitter, Uint8 curIndex);
|
||||
|
||||
protected:
|
||||
void initialize();
|
||||
|
||||
public:
|
||||
#ifdef DEBUG_LOG
|
||||
virtual void printPretty(LogModuleObject &f) { UT_OBJECTLOG(f, PR_LOG_ALWAYS, ("none")); }
|
||||
virtual void printDebug(LogModuleObject &f);
|
||||
#endif
|
||||
#ifdef DEBUG
|
||||
int mDebug;
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG // added by simon, FIX should remove
|
||||
virtual void checkIntegrity() { };
|
||||
virtual void printArgs() { };
|
||||
#endif // DEBUG
|
||||
|
||||
|
||||
protected:
|
||||
DataNode* mSrcPrimitive; // the primitive from which this Instruction was generated
|
||||
};
|
||||
|
||||
typedef DoublyLinkedList<Instruction> InstructionList;
|
||||
|
||||
|
||||
// Comparator function for SortedDoublyLinkedList. To get the desired effects, we want to comparison function to
|
||||
// return 1 if instruction a is of a great or equal line number then b. (ensures that later scheduled
|
||||
// instructions of the same lineNumber end up later in the list. Also if the two instructions are the
|
||||
// same, we want to return a 0. (Avoids duplicate scheduling)/
|
||||
inline int instructionCompare(const Instruction* a, const Instruction* b)
|
||||
{
|
||||
assert(a != NULL && b != NULL);
|
||||
|
||||
if(a->getLineNumber() < b->getLineNumber())
|
||||
return -1;
|
||||
if(a == b)
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
class Pool;
|
||||
|
||||
class InsnUseXDefineYFromPool :
|
||||
public Instruction
|
||||
{
|
||||
public:
|
||||
InsnUseXDefineYFromPool(DataNode* inSrcPrimitive, Pool& inPool, Uint8 inX, Uint8 inY);
|
||||
|
||||
virtual InstructionUse* getInstructionUseBegin() { return (mInsnUse); }
|
||||
virtual InstructionUse* getInstructionUseEnd() { return (&mInsnUse[0] + mX); }
|
||||
virtual InstructionDefine* getInstructionDefineBegin() { return (mDefineResource); }
|
||||
virtual InstructionDefine* getInstructionDefineEnd() { return (&mDefineResource[0] + mY); }
|
||||
|
||||
protected:
|
||||
|
||||
Uint8 mX;
|
||||
Uint8 mY;
|
||||
InstructionUse* mInsnUse;
|
||||
InstructionDefine* mDefineResource;
|
||||
};
|
||||
|
||||
#ifdef __MWERKS__
|
||||
#pragma mark -
|
||||
#pragma mark ¥ÊInsnExternalUse/Define ¥
|
||||
#endif
|
||||
|
||||
class InsnExternalUse : public InsnUseXDefineYFromPool
|
||||
{
|
||||
protected:
|
||||
InstructionUse externalUse;
|
||||
|
||||
public:
|
||||
InsnExternalUse(DataNode* inSrcPrimitive, Pool& inPool, Uint8 inUse) :
|
||||
InsnUseXDefineYFromPool(inSrcPrimitive, inPool, inUse, 0) {}
|
||||
|
||||
virtual InstructionFlags getFlags() const { return (ifExternalInsn); }
|
||||
|
||||
#ifdef DEBUG_LOG
|
||||
virtual void printPretty(LogModuleObject &f) { UT_OBJECTLOG(f, PR_LOG_ALWAYS, ("(external use)")); }
|
||||
#endif
|
||||
};
|
||||
|
||||
class InsnExternalDefine : public InsnUseXDefineYFromPool
|
||||
{
|
||||
protected:
|
||||
InstructionDefine externalDef;
|
||||
|
||||
public:
|
||||
InsnExternalDefine(DataNode* inSrcPrimitive, Pool& inPool, Uint8 inDefine) :
|
||||
InsnUseXDefineYFromPool(inSrcPrimitive, inPool, 0, inDefine) {}
|
||||
|
||||
virtual InstructionFlags getFlags() const { return (ifExternalInsn); }
|
||||
|
||||
#ifdef DEBUG_LOG
|
||||
virtual void printPretty(LogModuleObject &f) { UT_OBJECTLOG(f, PR_LOG_ALWAYS, ("(external def)")); }
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif // INSTRUCTION_H
|
|
@ -0,0 +1,258 @@
|
|||
/* -*- 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.
|
||||
*/
|
||||
// InstructionEmitter.cp
|
||||
//
|
||||
// Scott M. Silver
|
||||
//
|
||||
|
||||
#include "InstructionEmitter.h"
|
||||
#include "Primitives.h"
|
||||
#include "Instruction.h"
|
||||
#include "ControlNodes.h"
|
||||
|
||||
// define a temporary platform specific register
|
||||
VirtualRegister& InstructionEmitter::
|
||||
defineTemporary(Instruction& inDefineInsn, Uint8 inWhichOutput, VirtualRegisterKind constraint)
|
||||
{
|
||||
// newRegister stores inDefineInsn in the vr
|
||||
VirtualRegister& vr = mVRAllocator.newVirtualRegister(constraint);
|
||||
|
||||
vr.setDefiningInstruction(inDefineInsn);
|
||||
|
||||
// make this instruction's output this register
|
||||
inDefineInsn.addDefine(inWhichOutput, vr, constraint);
|
||||
|
||||
return vr;
|
||||
}
|
||||
|
||||
// reuse register, basically update who defines this register
|
||||
// and note that the passed in instruction redefines it
|
||||
VirtualRegister& InstructionEmitter::
|
||||
redefineTemporary(Instruction& inDefineInsn, VirtualRegister& inReuseRegister, Uint8 inWhichOutput, VirtualRegisterKind constraint)
|
||||
{
|
||||
inReuseRegister.setDefiningInstruction(inDefineInsn);
|
||||
|
||||
inDefineInsn.addDefine(inWhichOutput, inReuseRegister, constraint);
|
||||
|
||||
return (inReuseRegister);
|
||||
}
|
||||
|
||||
InstructionDefine& InstructionEmitter::
|
||||
defineTemporaryOrder(Instruction& inDefineInsn, Uint8 inWhichOutput)
|
||||
{
|
||||
InstructionDefine& newOrder = *new(mPool) InstructionDefine();
|
||||
newOrder.name.instruction = &inDefineInsn;
|
||||
|
||||
inDefineInsn.addDefine(inWhichOutput, newOrder);
|
||||
|
||||
return (newOrder);
|
||||
}
|
||||
|
||||
InstructionDefine& InstructionEmitter::
|
||||
redefineTemporaryOrder(Instruction& inDefineInsn, InstructionDefine& inReuseOrder, Uint8 inWhichOutput)
|
||||
{
|
||||
inReuseOrder.name.instruction = &inDefineInsn;
|
||||
|
||||
inDefineInsn.addDefine(inWhichOutput, inReuseOrder);
|
||||
|
||||
return (inReuseOrder);
|
||||
}
|
||||
|
||||
void InstructionEmitter::
|
||||
useTemporaryOrder(Instruction& inDefineInsn, InstructionDefine& inOrder, Uint8 inWhichInput)
|
||||
{
|
||||
inDefineInsn.addUse(inWhichInput, inOrder);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// inID is used to indicate which of the two assigned registers to a producer
|
||||
// you wish to use, eg for 32 bit types, etc
|
||||
VirtualRegister* InstructionEmitter::
|
||||
defineProducer(DataNode& inProducer, Instruction& inDefineInsn, Uint8 inWhichOutput, VirtualRegisterKind constraint, VirtualRegisterID inID)
|
||||
{
|
||||
return defineProducerInternal(inProducer, &inDefineInsn, inWhichOutput, NULL, constraint, inID);
|
||||
}
|
||||
|
||||
void InstructionEmitter::
|
||||
defineProducerWithExistingVirtualRegister(VirtualRegister& inVr, DataNode& inProducer, Instruction& inDefineInsn, Uint8 inWhichOutput, VirtualRegisterKind constraint)
|
||||
{
|
||||
defineProducerInternal(inProducer, &inDefineInsn, inWhichOutput, &inVr, constraint, vidLow);
|
||||
}
|
||||
|
||||
inline VirtualRegister*
|
||||
getVirtualRegisterAnnotationByID(DataNode& inDataNode, VirtualRegisterID inID)
|
||||
{
|
||||
if (inID == vidLow)
|
||||
return (inDataNode.getLowVirtualRegisterAnnotation());
|
||||
else if (inID == vidHigh)
|
||||
return (inDataNode.getHighVirtualRegisterAnnotation());
|
||||
else
|
||||
{
|
||||
assert(false);
|
||||
return (NULL);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// inDefineInsn default to NULL
|
||||
// inWhichOutput default to 0xFF
|
||||
VirtualRegister* InstructionEmitter::
|
||||
defineProducerInternal(DataNode& inProducer, Instruction* inDefineInsn, Uint8 inWhichOutput, VirtualRegister* inVr, VirtualRegisterKind constraint, VirtualRegisterID inID)
|
||||
{
|
||||
bool reallyDefiningProducer;
|
||||
|
||||
reallyDefiningProducer = (inDefineInsn != NULL);
|
||||
|
||||
switch (inProducer.getKind())
|
||||
{
|
||||
default:
|
||||
assert(false);
|
||||
break;
|
||||
case vkCond:
|
||||
// fix-me should we set the fact that this instruction
|
||||
// touches a condition code here?
|
||||
if (reallyDefiningProducer)
|
||||
{
|
||||
inProducer.annotate(*inDefineInsn);
|
||||
inDefineInsn->addDefine(inWhichOutput, inProducer);
|
||||
return (NULL);
|
||||
}
|
||||
break;
|
||||
case vkMemory:
|
||||
if (reallyDefiningProducer)
|
||||
{
|
||||
inProducer.annotate(*inDefineInsn);
|
||||
inDefineInsn->addDefine(inWhichOutput, inProducer);
|
||||
return (NULL);
|
||||
}
|
||||
break;
|
||||
case vkDouble:
|
||||
case vkInt:
|
||||
case vkFloat:
|
||||
case vkAddr:
|
||||
assert(inID == vidLow);
|
||||
case vkLong:
|
||||
{
|
||||
VirtualRegister* vr;
|
||||
|
||||
// check to see if a VR was assigned to inProducer
|
||||
if (getVirtualRegisterAnnotationByID(inProducer, inID) == NULL)
|
||||
{
|
||||
// allocate a new VR
|
||||
if (inVr == NULL)
|
||||
vr = &mVRAllocator.newVirtualRegister(constraint);
|
||||
else
|
||||
vr = inVr;
|
||||
|
||||
vr->setDefiningInstruction(*inDefineInsn);
|
||||
|
||||
|
||||
// attach to inProducer
|
||||
if (inID == vidLow)
|
||||
inProducer.annotateLow(*vr, constraint);
|
||||
else if (inID == vidHigh)
|
||||
inProducer.annotateHigh(*vr, constraint);
|
||||
else
|
||||
assert(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
// there is already a VR attached to this producer
|
||||
// make sure user did not try to specify a VR, this is
|
||||
// programmer error
|
||||
assert(inVr == NULL);
|
||||
|
||||
vr = getVirtualRegisterAnnotationByID(inProducer, inID);
|
||||
|
||||
vr->setDefiningInstruction(*inDefineInsn);
|
||||
}
|
||||
|
||||
if (reallyDefiningProducer)
|
||||
{
|
||||
inDefineInsn->addDefine(inWhichOutput, *vr, constraint);
|
||||
vr->setClass(constraint);
|
||||
}
|
||||
|
||||
return (vr);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// NEVER REACHED
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
|
||||
VirtualRegister* InstructionEmitter::
|
||||
useProducer(DataNode& inProducer, Instruction& inUseInstruction, Uint8 inWhichInput, VirtualRegisterKind constraint, VirtualRegisterID inID)
|
||||
{
|
||||
// find out if there is a register assigned to this {producer, id}
|
||||
// fix-me check cast
|
||||
switch (inProducer.getKind())
|
||||
{
|
||||
default:
|
||||
assert(false);
|
||||
break;
|
||||
case vkCond:
|
||||
inUseInstruction.addUse(inWhichInput, inProducer);
|
||||
break;
|
||||
case vkMemory:
|
||||
// now mark our dependence on VR
|
||||
inUseInstruction.addUse(inWhichInput, inProducer);
|
||||
break;
|
||||
case vkDouble:
|
||||
case vkInt:
|
||||
case vkFloat:
|
||||
case vkLong:
|
||||
case vkAddr:
|
||||
VirtualRegister* vr;
|
||||
|
||||
// make sure there is a register assigned to this already
|
||||
// if not assign a new one with a NULL defining instruction
|
||||
if (getVirtualRegisterAnnotationByID(inProducer, inID) == NULL)
|
||||
vr = defineProducerInternal(inProducer, NULL, 0, NULL, constraint, inID); // define NULL instruction for this producer
|
||||
else
|
||||
vr = getVirtualRegisterAnnotationByID(inProducer, inID);
|
||||
|
||||
// now mark our dependence on VR
|
||||
inUseInstruction.addUse(inWhichInput, *vr, constraint);
|
||||
return vr;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void InstructionEmitter::
|
||||
useTemporaryVR(Instruction& inInstruction, VirtualRegister& inVR, Uint8 inWhichInput, VirtualRegisterKind constraint)
|
||||
{
|
||||
inInstruction.addUse(inWhichInput, inVR, constraint);
|
||||
}
|
||||
|
||||
Instruction& InstructionEmitter::
|
||||
pushAbsoluteBranch(ControlNode& inSrc, ControlNode& inTarget)
|
||||
{
|
||||
InstructionList& instructions = inSrc.getInstructions();
|
||||
|
||||
Instruction& branchInsn = emitAbsoluteBranch(*(instructions.get(instructions.end())).getPrimitive(), inTarget);
|
||||
|
||||
instructions.addLast(branchInsn);
|
||||
|
||||
return (branchInsn);
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
/* -*- 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.
|
||||
*/
|
||||
/*
|
||||
InstructionEmitter.h
|
||||
|
||||
Scott M. Silver
|
||||
*/
|
||||
|
||||
#ifndef _H_INSTRUCTIONEMITTER_
|
||||
#define _H_INSTRUCTIONEMITTER_
|
||||
|
||||
#include "Instruction.h"
|
||||
#include "VirtualRegister.h"
|
||||
#include "Primitives.h"
|
||||
#include "Value.h"
|
||||
|
||||
class Pool;
|
||||
struct InstructionDefine;
|
||||
|
||||
typedef uint16 NamedRule;
|
||||
|
||||
enum VirtualRegisterID { vidLow, vidHigh };
|
||||
|
||||
class InstructionEmitter
|
||||
{
|
||||
public:
|
||||
InstructionEmitter(Pool& inPool, VirtualRegisterManager& vrMan) :
|
||||
mPool(inPool),
|
||||
mVRAllocator(vrMan) { }
|
||||
|
||||
VirtualRegister& defineTemporary(Instruction& inDefineInsn, Uint8 inWhichOutput, VirtualRegisterKind constraint = IntegerRegister);
|
||||
VirtualRegister& redefineTemporary(Instruction& inDefineInsn, VirtualRegister& inReuseRegister, Uint8 inWhichOutput, VirtualRegisterKind constraint = IntegerRegister);
|
||||
void useTemporaryVR(Instruction& inInstruction, VirtualRegister& inVR, uint8 inWhichInput, VirtualRegisterKind constraint = IntegerRegister);
|
||||
|
||||
InstructionDefine& defineTemporaryOrder(Instruction& inDefineInsn, Uint8 inWhichOutput);
|
||||
InstructionDefine& redefineTemporaryOrder(Instruction& inDefineInsn, InstructionDefine& inReuseOrder, Uint8 inWhichOutput);
|
||||
void useTemporaryOrder(Instruction& inDefineInsn, InstructionDefine& inOrder, Uint8 inWhichInput);
|
||||
|
||||
VirtualRegister* defineProducer(DataNode& inProducer, Instruction& inDefineInsn, Uint8 inWhichOutput, VirtualRegisterKind constraint = IntegerRegister, VirtualRegisterID inID = vidLow);
|
||||
void defineProducerWithExistingVirtualRegister(VirtualRegister& inVr, DataNode& inProducer, Instruction& inDefineInsn, Uint8 inWhichOutput, VirtualRegisterKind constraint = IntegerRegister);
|
||||
VirtualRegister* useProducer(DataNode& inProducer, Instruction& inUseInstruction, Uint8 inWhichInput, VirtualRegisterKind constraint = IntegerRegister, VirtualRegisterID inID = vidLow);
|
||||
Instruction& pushAbsoluteBranch(ControlNode& inSrc, ControlNode& inTarget);
|
||||
|
||||
|
||||
private:
|
||||
VirtualRegister* defineProducerInternal(DataNode& inProducer, Instruction* inDefineInsn = NULL, uint8 inWhichOutput = 0, VirtualRegister* inVr = NULL, VirtualRegisterKind constraint = IntegerRegister, VirtualRegisterID inID = vidLow);
|
||||
|
||||
virtual Instruction& emitAbsoluteBranch(DataNode& inDataNode, ControlNode& inTarget) = 0;
|
||||
|
||||
protected:
|
||||
Pool& mPool;
|
||||
|
||||
public:
|
||||
VirtualRegisterManager& mVRAllocator;
|
||||
};
|
||||
#endif // _H_INSTRUCTIONEMITTER_
|
|
@ -0,0 +1,86 @@
|
|||
#!gmake
|
||||
#
|
||||
# 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.
|
||||
|
||||
#######################################################################
|
||||
# (1) Directory specific info #
|
||||
#######################################################################
|
||||
|
||||
DEPTH = ../..
|
||||
|
||||
DIRS = md
|
||||
SUBMODULES = $(DIRS)
|
||||
|
||||
CPPSRCS = Backend.cpp \
|
||||
CGScheduler.cpp \
|
||||
CodeGenerator.cpp \
|
||||
IGVisualizer.cpp \
|
||||
Instruction.cpp \
|
||||
InstructionEmitter.cpp \
|
||||
NativeCodeCache.cpp \
|
||||
NativeFormatter.cpp \
|
||||
HTMLMethodDump.cpp \
|
||||
Scheduler.cpp \
|
||||
ExceptionTable.cpp \
|
||||
$(NULL)
|
||||
|
||||
LOCAL_EXPORTS = Backend.h \
|
||||
Burg.h \
|
||||
CGScheduler.h \
|
||||
CodeGenerator.h \
|
||||
ControlNodeScheduler.h \
|
||||
ExceptionTable.h \
|
||||
FormatStructures.h \
|
||||
HTMLMethodDump.h \
|
||||
IGVisualizer.h \
|
||||
Instruction.h \
|
||||
InstructionEmitter.h \
|
||||
NativeCodeCache.h \
|
||||
NativeFormatter.h \
|
||||
Scheduler.h \
|
||||
$(NULL)
|
||||
|
||||
MODULE_NAME = EF
|
||||
|
||||
#######################################################################
|
||||
# (2) Include "component" configuration information. #
|
||||
#######################################################################
|
||||
|
||||
include $(DEPTH)/config/config.mk
|
||||
|
||||
#######################################################################
|
||||
# (3) Include "local" platform-dependent assignments (OPTIONAL). #
|
||||
#######################################################################
|
||||
|
||||
|
||||
|
||||
#######################################################################
|
||||
# (4) Execute "component" rules. (OPTIONAL) #
|
||||
#######################################################################
|
||||
|
||||
include $(DEPTH)/config/rules.mk
|
||||
|
||||
#######################################################################
|
||||
# (7) Execute "local" rules. (OPTIONAL). #
|
||||
#######################################################################
|
||||
|
||||
# Special rules for Instruction.cpp for gcc
|
||||
ifneq ($(OS_ARCH),WINNT)
|
||||
|
||||
$(OBJDIR)/Instruction$(OBJ_SUFFIX): Instruction.cpp
|
||||
$(CCC) -c $(filter-out $(EXC_FLAGS),$(CFLAGS)) $< -o $@
|
||||
|
||||
endif
|
|
@ -0,0 +1,371 @@
|
|||
/* -*- 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.
|
||||
*/
|
||||
// NativeCodeCache
|
||||
//
|
||||
// Scott M. Silver
|
||||
//
|
||||
|
||||
#include "NativeCodeCache.h"
|
||||
#include "StringUtils.h"
|
||||
#include "ClassWorld.h"
|
||||
#include "FieldOrMethod.h"
|
||||
#include "NativeMethodDispatcher.h"
|
||||
#include "JavaVM.h"
|
||||
|
||||
UT_DEFINE_LOG_MODULE(NativeCodeCache);
|
||||
|
||||
#ifdef _WIN32
|
||||
Uint8* CacheEntry::asynchVersion(Pool& pool, Uint8*& ip)
|
||||
{
|
||||
Uint8* src = start.getFunctionAddress();
|
||||
Int32 sz = end-src;
|
||||
Uint8* dest = new(pool) Uint8[sz];
|
||||
Uint32* asynchPoints = eTable->getAsynchPoints();
|
||||
int numberofAPoints = eTable->getNrOfAsynchPoints();
|
||||
// loop through code and for each byte corresponding to an asynchronous
|
||||
// checkpoint, insert 'int 3' (0xCC).
|
||||
memcpy((void*)dest,(const void*)src,sz);
|
||||
for (int j=0; j<numberofAPoints; j++)
|
||||
dest[asynchPoints[j]] = 0xcc;
|
||||
ip = dest + (ip-src);
|
||||
return dest;
|
||||
}
|
||||
#else
|
||||
Uint8* CacheEntry::asynchVersion(Pool& /*pool*/, Uint8*& /*ip*/)
|
||||
{
|
||||
assert(false);
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
// there is one of these per VM instantiation
|
||||
NativeCodeCache NativeCodeCache::sNativeCodeCache;
|
||||
|
||||
// Access to the database of classes
|
||||
extern ClassWorld world;
|
||||
|
||||
#ifdef DEBUG_LOG
|
||||
static void printFrame(Method *method);
|
||||
#endif
|
||||
|
||||
// acquireMemory
|
||||
//
|
||||
// Reserve inBytes of data for (potentially) a new method, or data
|
||||
Uint8* NativeCodeCache::
|
||||
acquireMemory(const size_t inBytes)
|
||||
{
|
||||
// is Pool new thread safe?
|
||||
return ((Uint8*) new(mPool) char[inBytes]);
|
||||
}
|
||||
|
||||
|
||||
// mapMemoryToMethod
|
||||
//
|
||||
// Assign a "name" to a method. When other users ask the cache
|
||||
// for a given method, this is the entry point returned. The
|
||||
// first call for a specific MethodDescriptor M and function f,
|
||||
// will assign f to M.
|
||||
//
|
||||
// FIX-ME do all HashTable entrypoints need monitors?
|
||||
void NativeCodeCache::
|
||||
mapMemoryToMethod( const MethodDescriptor& inMethodDescriptor,
|
||||
PlatformTVector inWhere,
|
||||
Uint8* inEnd,
|
||||
ExceptionTable* pExceptionTable,
|
||||
StackFrameInfo& inPolicy)
|
||||
{
|
||||
CacheEntry* existingEntry = NULL;
|
||||
TemporaryStringCopy key(inMethodDescriptor.method->toString());
|
||||
|
||||
mIndexByDescriptor.get(key, &existingEntry);
|
||||
|
||||
if (!existingEntry)
|
||||
{
|
||||
CacheEntry& cacheEntry = *new(mPool) CacheEntry(inMethodDescriptor);
|
||||
cacheEntry.start = inWhere;
|
||||
cacheEntry.end = inEnd;
|
||||
cacheEntry.eTable = pExceptionTable;
|
||||
cacheEntry.policy = inPolicy;
|
||||
|
||||
mIndexByDescriptor.add(key, &cacheEntry);
|
||||
mIndexByRange.append(&cacheEntry);
|
||||
}
|
||||
else
|
||||
{
|
||||
existingEntry->start = inWhere;
|
||||
existingEntry->end = inEnd;
|
||||
existingEntry->eTable = pExceptionTable;
|
||||
existingEntry->policy = inPolicy;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// lookupByRange
|
||||
//
|
||||
// Purpose: used for stack crawling / debugging purposes
|
||||
// In: pointer to memory
|
||||
// Out: pointer to cache entry if in code cache (NULL otherwise)
|
||||
//
|
||||
// Notes: currently we do a linear search of a vector of methods, eventually
|
||||
// we'll want a better search strategy
|
||||
CacheEntry* NativeCodeCache::
|
||||
lookupByRange(Uint8* inAddress)
|
||||
{
|
||||
for(CacheEntry** ce= mIndexByRange.begin(); ce < mIndexByRange.end(); ce++)
|
||||
{
|
||||
#ifdef GENERATE_FOR_X86
|
||||
if ((*ce)->stub <= inAddress && (*ce)->stub + 10 > inAddress)
|
||||
return *ce;
|
||||
else
|
||||
#endif
|
||||
if(((*ce)->start.getFunctionAddress() <= inAddress) && (inAddress < (*ce)->end))
|
||||
return *ce;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// lookupByDescriptor
|
||||
//
|
||||
// Looks up method inMethodDescriptor, if inMethodDescriptor
|
||||
// is not in the cache, this generates a new "stub" function
|
||||
// (which will compile inMethodDescriptor and backpatch the caller)
|
||||
// Since we have two entries in the CacheEntry, one for stub and
|
||||
// the other for function, we return the function if it exists
|
||||
// otherwise we return the stub.
|
||||
// inLookupOnly - do not create a stub if the lookup fails
|
||||
addr NativeCodeCache::
|
||||
lookupByDescriptor(const MethodDescriptor& inMethodDescriptor, bool /*inhibitBackpatch*/, bool inLookupOnly)
|
||||
{
|
||||
Method *method = inMethodDescriptor.method;
|
||||
TemporaryStringCopy key(method->toString());
|
||||
|
||||
CacheEntry* cacheEntry = NULL;
|
||||
|
||||
mIndexByDescriptor.get(key, &cacheEntry);
|
||||
|
||||
// short circuit if inLookupOnly
|
||||
if (inLookupOnly && !cacheEntry)
|
||||
return (functionAddress(NULL));
|
||||
|
||||
if (!cacheEntry)
|
||||
{
|
||||
CacheEntry& newEntry = *new(mPool) CacheEntry(inMethodDescriptor);
|
||||
|
||||
newEntry.stub = (unsigned char*) generateCompileStub(*this, newEntry);
|
||||
newEntry.start.setTVector(NULL);
|
||||
newEntry.end = NULL;
|
||||
newEntry.eTable = NULL;
|
||||
|
||||
// FIX set shouldBackPatch to always false. Note that this
|
||||
// means that we compile evry function every time
|
||||
newEntry.shouldBackPatch = false;
|
||||
|
||||
//newEntry.shouldBackPatch = !method->getDynamicallyDispatched();
|
||||
|
||||
cacheEntry = &newEntry;
|
||||
mIndexByDescriptor.add(key, cacheEntry);
|
||||
mIndexByRange.append(cacheEntry);
|
||||
}
|
||||
|
||||
bool inhibitBackpatching = false;
|
||||
inhibitBackpatching = VM::theVM.getInhibitBackpatching();
|
||||
|
||||
if (cacheEntry->start.getFunctionAddress() && !inhibitBackpatching)
|
||||
return (functionAddress((void (*)())cacheEntry->start.getFunctionAddress()));
|
||||
else
|
||||
{
|
||||
if (!cacheEntry->stub)
|
||||
cacheEntry->stub = (unsigned char*) generateCompileStub(*this, *cacheEntry);
|
||||
return (functionAddress((void (*)())cacheEntry->stub));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
UT_DEFINE_LOG_MODULE(NativeCodeCacheMonitor);
|
||||
|
||||
// compileOrLoadMethod
|
||||
//
|
||||
// If the method pointed to by inCacheEntry is a native method, resolve and load
|
||||
// the native code library. Otherwise compile the method's bytecode into
|
||||
// native code. Either way, return a pointer to the resulting native code.
|
||||
// Sets shouldBackPatch to true if it is neccessary to back-patch the method.
|
||||
static void*
|
||||
compileOrLoadMethod(const CacheEntry* inCacheEntry, bool &shouldBackPatch)
|
||||
{
|
||||
Method* method;
|
||||
|
||||
// if this function is already compiled then just return it
|
||||
if (inCacheEntry->start.getFunctionAddress() != NULL)
|
||||
{
|
||||
shouldBackPatch = inCacheEntry->shouldBackPatch;
|
||||
goto ExitReturnFunc;
|
||||
}
|
||||
else
|
||||
{
|
||||
NativeCodeCache::enter();
|
||||
// maybe we slept while someone else was compiling, if so
|
||||
// don't do any work.
|
||||
if (inCacheEntry->start.getFunctionAddress() != NULL)
|
||||
goto ExitReturnFunc;
|
||||
|
||||
method = inCacheEntry->descriptor.method;
|
||||
|
||||
// If this is a native method, then resolve it appropriately
|
||||
if (method->getModifiers() & CR_METHOD_NATIVE)
|
||||
{
|
||||
addr a = NativeMethodDispatcher::resolve(*method);
|
||||
|
||||
if (!a)
|
||||
{
|
||||
NativeCodeCache::exit();
|
||||
UT_LOG(NativeCodeCache, PR_LOG_ERROR, ("\tCould not resolve native method %s\n", method->getName()));
|
||||
runtimeError(RuntimeError::linkError);
|
||||
}
|
||||
else
|
||||
UT_LOG(NativeCodeCache, PR_LOG_DEBUG, ("\tResolved native method %s\n", method->getName()));
|
||||
|
||||
|
||||
NativeCodeCache &cache = NativeCodeCache::getCache();
|
||||
void* code = generateNativeStub(cache, *inCacheEntry, addressFunction(a));
|
||||
|
||||
PlatformTVector tVector;
|
||||
tVector.setTVector((Uint8*)code);
|
||||
int codeLength = 0; // We don't know the length of native methods yet
|
||||
|
||||
/*ExceptionTable* pExceptionTable = NULL; */ // FIX native methods don't have exception tables yet
|
||||
StackFrameInfo policy; // FIX we don't know the stack layout policies yet, so pass in junk
|
||||
|
||||
cache.mapMemoryToMethod(inCacheEntry->descriptor, tVector, (Uint8*)code + codeLength, NULL, policy);
|
||||
}
|
||||
else
|
||||
method->compile();
|
||||
}
|
||||
|
||||
ExitReturnFunc:
|
||||
NativeCodeCache::exit();
|
||||
assert(inCacheEntry->start.getFunctionAddress());
|
||||
shouldBackPatch = inCacheEntry->shouldBackPatch;
|
||||
EventBroadcaster::broadcastEvent(gCompileOrLoadBroadcaster, kEndCompileOrLoad, inCacheEntry->descriptor.method);
|
||||
return (inCacheEntry->start.getFunctionAddress());
|
||||
/*
|
||||
ExitMonitorThrowCompileException:
|
||||
PR_CExitMonitor((void*)inCacheEntry);
|
||||
assert(false);
|
||||
return (NULL);
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
// compileAndBackPatchMethod
|
||||
//
|
||||
// compile the method pointed to by inCacheEntry and then back patch
|
||||
// the caller.
|
||||
//
|
||||
// inLastPC = address of next native instruction to be executed
|
||||
// if the callee executed and returned. (where callee is the method that
|
||||
// is to be compiled)
|
||||
extern "C" void*
|
||||
compileAndBackPatchMethod(const CacheEntry* inCacheEntry, void* inLastPC, void* inUserDefined)
|
||||
{
|
||||
bool shouldBackPatch;
|
||||
|
||||
void *nativeCode = compileOrLoadMethod(inCacheEntry, shouldBackPatch);
|
||||
|
||||
if (VM::theVM.getInhibitBackpatching())
|
||||
shouldBackPatch = false;
|
||||
|
||||
#ifdef DEBUG
|
||||
if (VM::theVM.getTraceAllMethods())
|
||||
{
|
||||
Method *method = inCacheEntry->descriptor.method;
|
||||
printFrame(method);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Back-patch method only if method was resolved statically */
|
||||
return (shouldBackPatch) ?
|
||||
(backPatchMethod(nativeCode, inLastPC, inUserDefined)) : nativeCode;
|
||||
}
|
||||
|
||||
|
||||
#ifdef DEBUG_LOG
|
||||
// C-entry point so we can call this from
|
||||
// the MSVC debugger
|
||||
void NS_EXTERN
|
||||
printMethodTable()
|
||||
{
|
||||
NativeCodeCache::getCache().printMethodTable(UT_LOG_MODULE(NativeCodeCache));
|
||||
}
|
||||
|
||||
// printMethodTable
|
||||
//
|
||||
// Print out all the methods currently in the cache, with their
|
||||
// ExceptionTables
|
||||
void NativeCodeCache::
|
||||
printMethodTable(LogModuleObject &f)
|
||||
{
|
||||
UT_OBJECTLOG(f, PR_LOG_ALWAYS, ("\n__________________Method Table___________________\n"));
|
||||
for(CacheEntry** ce= mIndexByRange.begin(); ce < mIndexByRange.end(); ce++)
|
||||
{
|
||||
Uint8* pStart = (*ce)->start.getFunctionAddress();
|
||||
Uint8* pEnd = (*ce)->end;
|
||||
const char* pName= (*ce)->descriptor.method->toString();
|
||||
ExceptionTable* eTable= (*ce)->eTable;
|
||||
|
||||
UT_OBJECTLOG(f, PR_LOG_ALWAYS, ("[%#010x-%#010x] %s\n", Uint32(pStart), Uint32(pEnd), pName));
|
||||
if(eTable)
|
||||
eTable->printShort(f);
|
||||
UT_OBJECTLOG(f, PR_LOG_ALWAYS, ("\n"));
|
||||
}
|
||||
UT_OBJECTLOG(f, PR_LOG_ALWAYS, ("\n\n"));
|
||||
}
|
||||
|
||||
UT_EXTERN_LOG_MODULE(StackWalker);
|
||||
|
||||
#include "StackWalker.h"
|
||||
|
||||
// printFrame
|
||||
//
|
||||
// Print a frame of a given method (which is
|
||||
// the caller). This can be called from a debugger
|
||||
static void printFrame(Method *method)
|
||||
{
|
||||
UT_SET_LOG_LEVEL(StackWalker, PR_LOG_DEBUG);
|
||||
|
||||
Frame frame;
|
||||
frame.moveToPrevFrame();
|
||||
frame.moveToPrevFrame();
|
||||
|
||||
// Indent the frame according to stack depth
|
||||
int stackDepth = Frame::getStackDepth();
|
||||
int i;
|
||||
|
||||
// Discount the stack frames used for internal EF native code
|
||||
stackDepth -= 7;
|
||||
if (stackDepth < 0)
|
||||
stackDepth = 0;
|
||||
|
||||
for (i = 0; i < stackDepth; i++)
|
||||
UT_LOG(StackWalker, PR_LOG_ALWAYS, (" "));
|
||||
|
||||
Frame::printWithArgs(UT_LOG_MODULE(StackWalker), (Uint8*)(frame.getBase()), method);
|
||||
UT_LOG(StackWalker, PR_LOG_ALWAYS, ("\n"));
|
||||
}
|
||||
|
||||
#endif // DEBUG_LOG
|
|
@ -0,0 +1,177 @@
|
|||
/* -*- 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.
|
||||
*/
|
||||
/*
|
||||
NativeCodeCache
|
||||
*/
|
||||
|
||||
#ifndef _NATIVE_CODE_CACHE_
|
||||
#define _NATIVE_CODE_CACHE_
|
||||
|
||||
#include "ExceptionTable.h"
|
||||
#include "FormatStructures.h"
|
||||
#include "Pool.h"
|
||||
#include "Attributes.h"
|
||||
#include "InfoItem.h"
|
||||
#include "HashTable.h"
|
||||
#include "FieldOrMethod.h"
|
||||
|
||||
#include "LogModule.h"
|
||||
#include "prmon.h"
|
||||
|
||||
//-----------------------------------------------------------------------------------------------------------
|
||||
// This is the key for entries in the cache
|
||||
// Currently it retainst he "fully qualified" name
|
||||
// of the method.
|
||||
struct MethodDescriptor
|
||||
{
|
||||
MethodDescriptor(Method& inMethod) :
|
||||
method(&inMethod) { };
|
||||
|
||||
Method *method; // interned full name string describing this method
|
||||
|
||||
void operator=(const MethodDescriptor& inCopy) { method = inCopy.method; }
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------------------------------------
|
||||
struct PlatformTVector
|
||||
{
|
||||
Uint8* mTVector;
|
||||
|
||||
void
|
||||
setTVector(Uint8* inTVector) { mTVector = inTVector; }
|
||||
|
||||
Uint8*
|
||||
getFunctionAddress() const
|
||||
{
|
||||
#ifdef GENERATING_FOR_POWERPC
|
||||
if (mTVEctor != NULL)
|
||||
return (*(Uint8***)mTVector);
|
||||
else
|
||||
return (NULL);
|
||||
#elif defined(GENERATING_FOR_X86)
|
||||
return (mTVector);
|
||||
#else
|
||||
return (mTVector); // FIX-ME This probably is not right
|
||||
#endif
|
||||
}
|
||||
|
||||
Uint8*
|
||||
getEnvironment() const
|
||||
{
|
||||
#ifdef GENERATING_FOR_POWERPC
|
||||
return (*((Uint8***)mTVector + 1));
|
||||
#else
|
||||
assert(false);
|
||||
return (NULL);
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------------------------------------
|
||||
// An entry in the CodeCache
|
||||
// Not intended for use outside of NativeCode Cache
|
||||
struct CacheEntry
|
||||
{
|
||||
CacheEntry(const MethodDescriptor& inMethodDescriptor) :
|
||||
descriptor(inMethodDescriptor), stub(0) { }
|
||||
|
||||
MethodDescriptor descriptor; // unique descriptor of the method
|
||||
PlatformTVector start; // default entry into the function
|
||||
Uint8* end; // pointer to byte _after_ end of function
|
||||
Uint8* stub; // if non-NULL this is the address of the stub function
|
||||
ExceptionTable* eTable; // pointer to the exception table
|
||||
mutable bool shouldBackPatch; // If true, specifies that the call site must be backpatched on compile
|
||||
|
||||
// FIX for now the restore policy will be kept in the NativeCodeCache, later we may want to store the restore policies elsewhere
|
||||
StackFrameInfo policy; // specifies how to restore registers as stack frames are popped
|
||||
Uint8* asynchVersion(Pool& pool, Uint8*& ip);
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------------------------------------
|
||||
class NativeCodeCache
|
||||
{
|
||||
PRMonitor* _mon;
|
||||
public:
|
||||
NativeCodeCache() :
|
||||
mIndexByDescriptor(mPool),
|
||||
mIndexByRange()
|
||||
{ _mon = PR_NewMonitor(); }
|
||||
|
||||
Uint8* acquireMemory(const size_t inBytes);
|
||||
|
||||
void mapMemoryToMethod( const MethodDescriptor& inMethodDescriptor,
|
||||
PlatformTVector inWhere,
|
||||
Uint8* inEnd,
|
||||
ExceptionTable* pExceptionTable,
|
||||
StackFrameInfo& inPolicy );
|
||||
|
||||
addr lookupByDescriptor(const MethodDescriptor& inDescriptor, bool inhibitBackpatch, bool inLookupOnly = false);
|
||||
CacheEntry* lookupByRange(Uint8* inAddress);
|
||||
|
||||
protected:
|
||||
HashTable<CacheEntry*> mIndexByDescriptor; // hashtable of entrypoints to methods
|
||||
Vector<CacheEntry*> mIndexByRange; // maps range -> method
|
||||
public:
|
||||
Pool mPool; // resource for our cache
|
||||
static inline NativeCodeCache& getCache() { return (NativeCodeCache::sNativeCodeCache); }
|
||||
|
||||
static NativeCodeCache sNativeCodeCache;
|
||||
|
||||
#ifdef DEBUG
|
||||
void printMethodTable(LogModuleObject &f);
|
||||
#endif
|
||||
|
||||
static void enter() {
|
||||
PR_EnterMonitor(sNativeCodeCache._mon);
|
||||
}
|
||||
|
||||
static void exit() {
|
||||
PR_ExitMonitor(sNativeCodeCache._mon);
|
||||
}
|
||||
};
|
||||
|
||||
// inCacheEntry: reference to actual CacheEntry in memory
|
||||
// inCache: the cache in question
|
||||
// creates a bit of code which when called compiles the method
|
||||
// described by inCacheEntry.descriptor and then calls "compileAndBackPatchMethod"
|
||||
// then jump to the method returned by compileAndBackPatchMethod
|
||||
|
||||
extern void*
|
||||
generateCompileStub(NativeCodeCache& inCache, const CacheEntry& inCacheEntry);
|
||||
|
||||
extern void*
|
||||
generateNativeStub(NativeCodeCache& inCache, const CacheEntry& inCacheEntry, void *nativeFunction);
|
||||
|
||||
extern void *
|
||||
generateJNIGlue(NativeCodeCache& inCache,
|
||||
const CacheEntry& inCacheEntry,
|
||||
void *nativeFunction);
|
||||
|
||||
extern "C" void *
|
||||
compileAndBackPatchMethod(const CacheEntry* inCacheEntry, void* inLastPC, void* inUserDefined);
|
||||
|
||||
// atomically backpatch a caller of a stub generated with generateCompileStub
|
||||
// inMethodAddress the address of the callee
|
||||
// inLastPC the next PC to be executed up return from the callee
|
||||
// inUserDefined is a (probably) per platform piece of data that will be passed
|
||||
// on to the backPatchMethod function.
|
||||
void* backPatchMethod(void* inMethodAddress, void* inLastPC, void* inUserDefined);
|
||||
|
||||
void NS_EXTERN printMethodTable();
|
||||
|
||||
#endif // _NATIVE_CODE_CACHE_
|
|
@ -0,0 +1,374 @@
|
|||
/* -*- 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.
|
||||
*/
|
||||
//
|
||||
// NativeFormatter.cpp
|
||||
//
|
||||
// Scott M. Silver
|
||||
//
|
||||
// Actually output native code for a function to memory (from the NativeCodeCache).
|
||||
//
|
||||
// Summary
|
||||
//
|
||||
// A NativeFormatter takes an InstructionEmitter (a per/method data structure) and a Method
|
||||
// (a runtime representation of a method) and actually outputs the native representation
|
||||
// of the function out to memory. Memory is acquired from the NativeCodeCache.
|
||||
//
|
||||
// Each function has the following structural layout:
|
||||
//
|
||||
// ControlNodes Formatted Correlation
|
||||
// PreMethod
|
||||
// ckBegin Prolog
|
||||
// ckReturn Epilog
|
||||
// ckEnd PostMethod
|
||||
//
|
||||
// Prolog and Epilog are normal entry and exit points from a function. Note that even
|
||||
// if a function does not return a value a ckReturn node must exist.
|
||||
// PreMethod and PostMethod are invented pieces of code/data which may be used
|
||||
// for debuggers or stack inspection (eg. MacsBug symbols, TraceBack tables, etc...)
|
||||
|
||||
#include "NativeFormatter.h"
|
||||
#include "ControlGraph.h"
|
||||
#include "InstructionEmitter.h"
|
||||
#include "NativeCodeCache.h"
|
||||
#include "ExceptionTable.h"
|
||||
|
||||
#ifdef DEBUG
|
||||
|
||||
#include "JavaVM.h" // to insert breakpoints in jitted code
|
||||
|
||||
#endif
|
||||
|
||||
UT_DEFINE_LOG_MODULE(ExceptionMatrix);
|
||||
|
||||
//-----------------------------------------------------------------------------------------------------------
|
||||
// format
|
||||
//
|
||||
// Actually output the native instructions of a method out to memory
|
||||
// acquired from the cache.
|
||||
//
|
||||
// outInfo: Normally temporal information about the formatting the function
|
||||
// mainly used for debugging purposes
|
||||
void* NativeFormatter::
|
||||
format(Method& inMethod, FormattedCodeInfo* outInfo)
|
||||
{
|
||||
// now setup a FormattedCodeInfo with collected data about the method
|
||||
FormattedCodeInfo fci;
|
||||
fci.method = &inMethod;
|
||||
|
||||
// Need policy before any formatting can be done
|
||||
mFormatter.initStackFrameInfo();
|
||||
|
||||
mFormatter.calculatePrologEpilog(inMethod, fci.prologSize, fci.epilogSize);
|
||||
mFormatter.calculatePrePostMethod(inMethod, fci.preMethodSize, fci.postMethodSize);
|
||||
|
||||
// potentially shortens or lengthens branches
|
||||
fci.bodySize = resolveBranches(fci.prologSize, fci.epilogSize);
|
||||
|
||||
// actually grab some memory for this method
|
||||
Uint32 methodSize = fci.preMethodSize + fci.bodySize + fci.postMethodSize;
|
||||
|
||||
#ifdef DEBUG
|
||||
#if defined(XP_PC) || defined(LINUX)
|
||||
DebugDesc *bps;
|
||||
Uint32 nbps;
|
||||
nbps = VM::theVM.getExecBreakPoints(bps);
|
||||
bool hasInt3 = false;
|
||||
bool foundBreakPoint = false;
|
||||
|
||||
for (Uint32 index = 0; index < nbps; index++)
|
||||
if (inMethod.isSelf(bps[index].className, bps[index].methodName, bps[index].sig)) {
|
||||
methodSize++; // for int 3
|
||||
fci.methodStart = NativeCodeCache::getCache().acquireMemory(methodSize);
|
||||
*fci.methodStart++ = 0xcc;
|
||||
hasInt3 = true;
|
||||
foundBreakPoint = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!foundBreakPoint)
|
||||
fci.methodStart = NativeCodeCache::getCache().acquireMemory(methodSize);
|
||||
|
||||
#else
|
||||
fci.methodStart = NativeCodeCache::getCache().acquireMemory(methodSize);
|
||||
#endif
|
||||
#else
|
||||
fci.methodStart = NativeCodeCache::getCache().acquireMemory(methodSize);
|
||||
#endif
|
||||
|
||||
// build the exception table
|
||||
// FIX FIX FIX
|
||||
// for now just grab some memory
|
||||
Pool* tempPool = new Pool();
|
||||
ExceptionTable& eTable = buildExceptionTable(fci.methodStart, tempPool);
|
||||
DEBUG_LOG_ONLY(eTable.print(UT_LOG_MODULE(ExceptionMatrix)));
|
||||
DEBUG_LOG_ONLY(eTable.printFormatted(UT_LOG_MODULE(ExceptionMatrix), nodes, nNodes, false));
|
||||
|
||||
// fci is now valid
|
||||
mFormatter.beginFormatting(fci);
|
||||
|
||||
// dump out the function to memory
|
||||
outputNativeToMemory(fci.methodStart, fci);
|
||||
|
||||
// make an entry point for this function
|
||||
MethodDescriptor md(inMethod);
|
||||
|
||||
mFormatter.endFormatting(fci);
|
||||
|
||||
#if defined(DEBUG) && (defined(XP_PC) || defined(LINUX))
|
||||
if (hasInt3)
|
||||
fci.methodStart--;
|
||||
#endif
|
||||
|
||||
PlatformTVector functionDescriptor;
|
||||
functionDescriptor.setTVector(mFormatter.createTransitionVector(fci));
|
||||
|
||||
fci.methodEnd = fci.methodStart + methodSize;
|
||||
#ifdef WIN32
|
||||
// temporarily check this invariant
|
||||
assert((fci.methodStart + methodSize) == (functionDescriptor.getFunctionAddress() + methodSize));
|
||||
#endif
|
||||
|
||||
// Eventually we may wish to have choose different GPR/FPR save procedures and local store sizes for
|
||||
// different nodes in the control graph. Correspondingly the restore policies in an exception unwind
|
||||
// can vary by control node -- ie they are not necessarily the same across the whole method.
|
||||
// For now we will give each method the same info, but we reserve the right later to make this optimisation.
|
||||
|
||||
// FIX for mow, NativeCodeCache::mapMemoryToMethod caches a copy of the policy
|
||||
StackFrameInfo& policy = mFormatter.getStackFrameInfo();
|
||||
NativeCodeCache::getCache().mapMemoryToMethod(md, functionDescriptor, fci.methodEnd, &eTable, policy);
|
||||
|
||||
if (outInfo)
|
||||
*outInfo = fci;
|
||||
|
||||
#ifdef DEBUG_LOG
|
||||
// debugging stuff
|
||||
if(VM::theVM.getEmitHTML())
|
||||
dumpMethodToHTML(fci, eTable);
|
||||
#endif
|
||||
|
||||
// return the entry point
|
||||
return (fci.methodStart);
|
||||
}
|
||||
|
||||
// outputNativeToMemory
|
||||
// actually ouput this method to memory starting at inWhere.
|
||||
// inInfo contains some precalculated information about the method
|
||||
// which is useful in its formatting.
|
||||
void NativeFormatter::
|
||||
outputNativeToMemory(void* inWhere, const FormattedCodeInfo& inInfo)
|
||||
{
|
||||
Uint32 curOffset;
|
||||
char* nextMemory = (char*) inWhere;
|
||||
|
||||
assert(nodes[0]->hasControlKind(ckBegin));
|
||||
assert(nodes[nNodes-1]->hasControlKind(ckEnd));
|
||||
|
||||
// first output the PreMethod
|
||||
mFormatter.formatPreMethodToMemory(nextMemory, inInfo);
|
||||
nextMemory += inInfo.preMethodSize;
|
||||
|
||||
mFormatter.formatPrologToMemory(nextMemory);
|
||||
nextMemory += inInfo.prologSize;
|
||||
curOffset = inInfo.prologSize;
|
||||
|
||||
for (Uint32 n = 0; n < nNodes; n++)
|
||||
{
|
||||
InstructionList& instructions = nodes[n]->getInstructions();
|
||||
for(InstructionList::iterator i = instructions.begin(); !instructions.done(i); i = instructions.advance(i))
|
||||
{
|
||||
Instruction& curInstruction = instructions.get(i);
|
||||
|
||||
curInstruction.formatToMemory(nextMemory, curOffset, mFormatter);
|
||||
curOffset += curInstruction.getFormattedSize(mFormatter);
|
||||
nextMemory += curInstruction.getFormattedSize(mFormatter);
|
||||
}
|
||||
|
||||
if (nodes[n]->hasControlKind(ckReturn))
|
||||
{ mFormatter.formatEpilogToMemory(nextMemory);
|
||||
curOffset += inInfo.epilogSize;
|
||||
nextMemory += inInfo.epilogSize;
|
||||
}
|
||||
}
|
||||
|
||||
// Now end with the PostMethod
|
||||
mFormatter.formatPostMethodToMemory(nextMemory, inInfo);
|
||||
//nextMemory += inInfo.preMethodSize;
|
||||
}
|
||||
|
||||
|
||||
// accumulateSize
|
||||
//
|
||||
// Accumulate the size of the the formatted (ie in-memory) representation
|
||||
// of each Instruction in this ControlNode. ckBegin and ckReturn nodes
|
||||
// include the prolog and epilog size, respectively.
|
||||
Uint32 NativeFormatter::
|
||||
accumulateSize(ControlNode& inNode, Uint32 inPrologSize, Uint32 inEpilogSize)
|
||||
{
|
||||
Uint32 accum;
|
||||
|
||||
InstructionList& instructions = inNode.getInstructions();
|
||||
|
||||
accum = 0;
|
||||
for(InstructionList::iterator i = instructions.begin(); !instructions.done(i); i = instructions.advance(i))
|
||||
accum += instructions.get(i).getFormattedSize(mFormatter);
|
||||
|
||||
if (inNode.hasControlKind(ckReturn))
|
||||
accum += inEpilogSize;
|
||||
else if (inNode.hasControlKind(ckBegin))
|
||||
accum += inPrologSize;
|
||||
|
||||
return (accum);
|
||||
}
|
||||
|
||||
// resolveBranches
|
||||
//
|
||||
// for each node in the scheduled ControlNodes
|
||||
// assign a native offset to each ControlNode.
|
||||
// decisions to make short/long branches would normally be made
|
||||
// here. returns the size of the formatted method. including
|
||||
// epilog and prolog. The PreMethod and PostMethod are not
|
||||
// considered here.
|
||||
Uint32 NativeFormatter::
|
||||
resolveBranches(Uint32 inPrologSize, Uint32 inEpilogSize)
|
||||
{
|
||||
Uint32 accumSize;
|
||||
|
||||
accumSize = 0;
|
||||
for (Uint32 i = 0; i < nNodes; i++)
|
||||
{
|
||||
ControlNode& node = *nodes[i];
|
||||
Uint32 formattedSize;
|
||||
|
||||
node.setNativeOffset(accumSize);
|
||||
// fix-me this should be a function of the list, ie the list
|
||||
// should maintain the size of the instructions in it
|
||||
formattedSize = accumulateSize(node, inPrologSize, inEpilogSize);
|
||||
accumSize += formattedSize;
|
||||
}
|
||||
|
||||
// make long/short branch decisions here
|
||||
return (accumSize);
|
||||
}
|
||||
|
||||
// buildExceptionTable
|
||||
// In: start of method
|
||||
// pool to create table in
|
||||
// Out: the Exception Table
|
||||
|
||||
// #define DEBUG_GENERATE_EXCEPTION_MATRIX
|
||||
|
||||
// Debugging code to print out a matrix of exception handlers
|
||||
// Ugly -- farm out to a class, time permitting
|
||||
#ifdef DEBUG_GENERATE_EXCEPTION_MATRIX
|
||||
|
||||
#define DEBUG_SET_UP_EXCEPTION_MATRIX \
|
||||
UT_LOG(ExceptionMatrix, PR_LOG_ALWAYS, ("\n\nBuilding Exception Table\n")); \
|
||||
assert(nNodes < 100); /* dumb check */ \
|
||||
int _exceptionMatrix[100][100]; \
|
||||
Uint32 _rows, _cols; \
|
||||
for(_rows = 0; _rows < nNodes; _rows++) \
|
||||
for(_cols = 0; _cols < nNodes; _cols++) \
|
||||
_exceptionMatrix[_rows][_cols] = 0; \
|
||||
Uint32 _addorder = 1;
|
||||
|
||||
#define DEBUG_ADD_ENTRY_TO_EXCEPTION_MATRIX \
|
||||
_exceptionMatrix[node->dfsNum][e->getTarget().dfsNum] = _addorder++;
|
||||
|
||||
#define DEBUG_PRINT_EXCEPTION_MATRIX \
|
||||
/* print header */ \
|
||||
UT_LOG(ExceptionMatrix, PR_LOG_ALWAYS, ("\n\nException Matrix\n ")); \
|
||||
for(_cols = 0; _cols < nNodes; _cols++) \
|
||||
UT_LOG(ExceptionMatrix, PR_LOG_ALWAYS, ("N%02d ", nodes[_cols]->dfsNum)); \
|
||||
UT_LOG(ExceptionMatrix, PR_LOG_ALWAYS, ("\n")); \
|
||||
/* print rows */ \
|
||||
for(_rows = 0; _rows < nNodes; _rows++) \
|
||||
{ \
|
||||
UT_LOG(ExceptionMatrix, PR_LOG_ALWAYS, (" N%02d: ", nodes[_rows]->dfsNum)); \
|
||||
for(_cols = 0; _cols < nNodes; _cols++) \
|
||||
{ \
|
||||
_addorder = _exceptionMatrix[nodes[_rows]->dfsNum][nodes[_cols]->dfsNum]; \
|
||||
if(_addorder != 0) \
|
||||
UT_LOG(ExceptionMatrix, PR_LOG_ALWAYS, (" %02d ", _addorder)); \
|
||||
else \
|
||||
UT_LOG(ExceptionMatrix, PR_LOG_ALWAYS, (" - ")); \
|
||||
} \
|
||||
UT_LOG(ExceptionMatrix, PR_LOG_ALWAYS, ("\n")); \
|
||||
} \
|
||||
UT_LOG(ExceptionMatrix, PR_LOG_ALWAYS, ("\n\n\n"));
|
||||
|
||||
#else
|
||||
|
||||
#define DEBUG_SET_UP_EXCEPTION_MATRIX
|
||||
#define DEBUG_ADD_ENTRY_TO_EXCEPTION_MATRIX
|
||||
#define DEBUG_PRINT_EXCEPTION_MATRIX
|
||||
|
||||
#endif // DEBUG_GENERATE_EXCEPTION_MATRIX
|
||||
|
||||
ExceptionTable& NativeFormatter::
|
||||
buildExceptionTable(Uint8* methodStart, Pool* inPool)
|
||||
{
|
||||
Uint32 i;
|
||||
|
||||
DEBUG_SET_UP_EXCEPTION_MATRIX;
|
||||
ExceptionTableBuilder eBuilder(methodStart);
|
||||
|
||||
for (i = 0; i < nNodes; i++) // iterate through nodes in scheduled order
|
||||
{
|
||||
ControlNode* node = nodes[i];
|
||||
ControlKind kind = node->getControlKind();
|
||||
if(kind == ckExc || kind == ckAExc || kind == ckThrow)
|
||||
{ // has exception
|
||||
ControlNode::ExceptionExtra& excExtra = node->getExceptionExtra();
|
||||
const Class **exceptionClass = excExtra.handlerFilters + excExtra.nHandlers;
|
||||
|
||||
if (kind == ckAExc)
|
||||
eBuilder.addAEntry(node->getNativeOffset());
|
||||
// iterate through exception edges
|
||||
Uint32 startIndex = 0; // start extending exceptions from beginning of table
|
||||
ControlEdge *e = node->getSuccessorsEnd();
|
||||
for (Uint32 exceptNum = 0; exceptNum < excExtra.nHandlers; exceptNum++)
|
||||
{
|
||||
e--; // move to previous handler edge
|
||||
exceptionClass--; // move to previous handler filter
|
||||
|
||||
assert(&(e->getSource()) == node);
|
||||
|
||||
if(e->getTarget().getControlKind() != ckEnd) // only interested in exceptions handled locally
|
||||
{
|
||||
// assert(e->getTarget().getControlKind() == ckCatch); // targets of exception edges must be End or Catch nodes
|
||||
ExceptionTableEntry ete; // container for exception info passing
|
||||
ete.pStart = node->getNativeOffset();
|
||||
ete.pEnd = nodes[i+1]->getNativeOffset(); // points to byte after 'node'
|
||||
ete.pHandler = e->getTarget().getNativeOffset();
|
||||
ete.pExceptionType = *exceptionClass;
|
||||
UT_LOG(ExceptionMatrix, PR_LOG_ALWAYS, ("N%02d->N%02d ", node->dfsNum, e->getTarget().dfsNum));
|
||||
eBuilder.addEntry(startIndex, ete); // note that startIndex is passed by reference
|
||||
DEBUG_ADD_ENTRY_TO_EXCEPTION_MATRIX;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DEBUG_PRINT_EXCEPTION_MATRIX;
|
||||
DEBUG_ONLY(eBuilder.checkInOrder());
|
||||
|
||||
// copy table into new (efficient) storage and insert into NativeCodeCache
|
||||
ExceptionTable* eTable = new(*inPool) ExceptionTable(eBuilder, *inPool);
|
||||
return (*eTable);
|
||||
}
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
/* -*- 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.
|
||||
*/
|
||||
//
|
||||
// File: NativeFormatter.h
|
||||
// Author: smsilver
|
||||
//
|
||||
|
||||
#ifndef H_NATIVEFORMATTER
|
||||
#define H_NATIVEFORMATTER
|
||||
|
||||
class ControlNode;
|
||||
class Instruction;
|
||||
class Pool;
|
||||
class ExceptionTable;
|
||||
struct MethodDescriptor;
|
||||
|
||||
#include "FormatStructures.h"
|
||||
#include "LogModule.h"
|
||||
|
||||
#define INCLUDE_EMITTER
|
||||
#include "CpuInfo.h"
|
||||
#undef INCLUDE_EMITTER
|
||||
|
||||
class NativeFormatter
|
||||
{
|
||||
protected:
|
||||
MdEmitter& mEmitter;
|
||||
MdFormatter mFormatter;
|
||||
ControlNode** nodes;
|
||||
Uint32 nNodes;
|
||||
|
||||
void outputNativeToMemory(void* inWhere, const FormattedCodeInfo& inInfo);
|
||||
Uint32 accumulateSize(ControlNode& inNode, Uint32 inPrologSize, Uint32 inEpilogSize);
|
||||
Uint32 resolveBranches(Uint32 inPrologSize, Uint32 inEpilogSize);
|
||||
ExceptionTable& buildExceptionTable(Uint8* methodMemoryStart, Pool* inPool);
|
||||
|
||||
public:
|
||||
NativeFormatter(MdEmitter& inEmitter, ControlNode** mNodes, uint32 mNNodes) :
|
||||
mEmitter(inEmitter), mFormatter(mEmitter), nodes(mNodes), nNodes(mNNodes) {}
|
||||
|
||||
void* format(Method& inMethod, FormattedCodeInfo* outInfo = NULL);
|
||||
|
||||
#ifdef DEBUG_LOG
|
||||
void dumpMethodToHTML(FormattedCodeInfo& fci, ExceptionTable& inExceptionTable); // see HTMLMethodDump.cpp for implementation
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif // H_NATIVEFORMATTER
|
|
@ -0,0 +1,374 @@
|
|||
/* -*- 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.
|
||||
*/
|
||||
// LinearInstructionScheduler.cpp
|
||||
//
|
||||
// Peter DeSantis 28 April 1997
|
||||
|
||||
#include "Scheduler.h"
|
||||
#include "Vector.h"
|
||||
#include "ControlNodes.h"
|
||||
#include "Primitives.h"
|
||||
#include "DoublyLinkedList.h"
|
||||
#include "Instruction.h"
|
||||
|
||||
void swapRootToEnd(Vector<RootPair>& inRoots, RootPair* inSwapToEnd);
|
||||
|
||||
|
||||
// schedule [no real purpose currently] FIX-ME
|
||||
// Calls the appropriate internal routine(s).
|
||||
void LinearInstructionScheduler::
|
||||
schedule(Vector<RootPair>& roots, ControlNode& controlNode)
|
||||
{
|
||||
linearSchedule(roots, controlNode);
|
||||
}
|
||||
|
||||
|
||||
// The primary goal of linear scheduling is to schedule all the instructions
|
||||
// which are derived from one line of src code one after another. There are numerous
|
||||
// ways to meet this objective. The simpliest method is to do a simple prioritived
|
||||
// topological sort of the control node. The priority is that from any node the
|
||||
// algorith follows the edges in increasing linenumber order. (Each instruction has
|
||||
// a line number associated with it which corresponds to src code line number.) As the
|
||||
// topological sort backtracks it inserts the instruction which it is leaving into
|
||||
// the list of instructions in the control node. The instructions have to be inserted
|
||||
// in an ordered fashion such that each new instruction is added at the end of the
|
||||
// section with the same associated line numbers. Currently this is done in the
|
||||
// simpliest n^2 fashion. If scheduling is determined to take significant time this
|
||||
// running time can be improved by using a binary search insertion routine.
|
||||
|
||||
|
||||
|
||||
/*
|
||||
priorityTopoEmit
|
||||
does a priority topological sort of the instructions rooted at root and fills the
|
||||
controlnode's list ordered list of instructions.
|
||||
*/
|
||||
void LinearInstructionScheduler::
|
||||
priorityTopoEmit(Instruction* inInstruction, ControlNode& controlNode)
|
||||
{
|
||||
if (controlNode.haveScheduledInstruction(*inInstruction))
|
||||
return;
|
||||
|
||||
InstructionUse* conditionCodeUse = NULL;
|
||||
InstructionUse* curUse;
|
||||
Instruction* curInstruction;
|
||||
|
||||
for (curUse = inInstruction->getInstructionUseBegin();
|
||||
curUse < inInstruction->getInstructionUseEnd();
|
||||
curUse++)
|
||||
{
|
||||
if(curUse->kind == udCond)
|
||||
{
|
||||
assert(conditionCodeUse == NULL); // Instructions only allowed to use one condition code
|
||||
conditionCodeUse = curUse;
|
||||
} else {
|
||||
curInstruction = CodeGenerator::instructionUseToInstruction(*curUse);
|
||||
|
||||
// Only need to schedule if it hasn't been scheduled and it is in coltrolNode
|
||||
if (curInstruction != NULL
|
||||
&& curInstruction->getPrimitive()->getContainer() == inInstruction->getPrimitive()->getContainer())
|
||||
{
|
||||
priorityTopoEmit(curInstruction, controlNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(conditionCodeUse != NULL)
|
||||
{
|
||||
curInstruction = CodeGenerator::instructionUseToInstruction(*conditionCodeUse);
|
||||
|
||||
if (curInstruction != NULL
|
||||
&& curInstruction->getPrimitive()->getContainer() == inInstruction->getPrimitive()->getContainer())
|
||||
{
|
||||
priorityTopoEmit(curInstruction, controlNode);
|
||||
}
|
||||
}
|
||||
|
||||
// Now insert the instruction into the controlnodes list of instructions
|
||||
controlNode.addScheduledInstruction(*inInstruction);
|
||||
|
||||
}
|
||||
|
||||
|
||||
// swapRootToEnd
|
||||
//
|
||||
// Swaps the passed in root to the end of the root vector
|
||||
void
|
||||
swapRootToEnd(Vector<RootPair>& inRoots, RootPair* inSwapToEnd)
|
||||
{
|
||||
RootPair oldRoot;
|
||||
RootPair* end = inRoots.end() - 1;
|
||||
|
||||
oldRoot = *end;
|
||||
*end = *inSwapToEnd;
|
||||
*inSwapToEnd = oldRoot;
|
||||
}
|
||||
|
||||
/*
|
||||
linearSchedule
|
||||
Does a prioritized linear search which fills in controlNode->instructions from the
|
||||
back to the front.
|
||||
*/
|
||||
void LinearInstructionScheduler::
|
||||
linearSchedule(Vector<RootPair>& roots, ControlNode& controlNode)
|
||||
{
|
||||
// FIX-ME move this stuff to the function that builds up roots
|
||||
if (controlNode.hasControlKind(ckReturn))
|
||||
{
|
||||
// Enforce the register allocator requirement that code emitted for
|
||||
// Result nodes be placed at the end of the block
|
||||
// The register allocator has no "global scope" uses, so basically it
|
||||
// would be sufficient to place the ExternalUse instruction emitted for
|
||||
// each Result node at the end, however we just go ahead and make sure
|
||||
// all of the code for a Result is last thing scheduled in a node.
|
||||
// Hopefully the register allocator will have globally scoped uses
|
||||
// in the future. FIX-ME.
|
||||
RootPair* curRoot;
|
||||
RootPair* resultRoot = NULL;
|
||||
|
||||
// find poResult
|
||||
for (curRoot = roots.begin(); curRoot < roots.end(); curRoot++)
|
||||
if (curRoot->root->hasCategory(pcResult))
|
||||
{
|
||||
assert(!resultRoot); // verify there is only one Result node (is this true?)
|
||||
resultRoot = curRoot;
|
||||
#ifndef DEBUG
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
|
||||
// it is not necessary that there be a pcResult
|
||||
if (resultRoot)
|
||||
swapRootToEnd(roots, resultRoot);
|
||||
}
|
||||
else if (controlNode.hasControlKind(ckExc))
|
||||
{
|
||||
// Satisfy constraint that an exception producing primitive
|
||||
// must be the last excecuted in the ControlNode
|
||||
RootPair* curRoot;
|
||||
RootPair* exceptionRoot = NULL;
|
||||
|
||||
// find poResult
|
||||
for (curRoot = roots.begin(); curRoot < roots.end(); curRoot++)
|
||||
if (curRoot->root->canRaiseException())
|
||||
{
|
||||
assert(!exceptionRoot); // verify there is only one exception generating root
|
||||
exceptionRoot = curRoot;
|
||||
#ifndef DEBUG
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
|
||||
assert(exceptionRoot);
|
||||
swapRootToEnd(roots, exceptionRoot);
|
||||
}
|
||||
|
||||
renumberCN(roots, controlNode);
|
||||
|
||||
RootPair* curRoot;
|
||||
Primitive* anchoredPrimitive;
|
||||
|
||||
// take care of any anchored primitives last, so find out the anchored primitive
|
||||
if (controlNode.hasControlKind(ckIf) || controlNode.hasControlKind(ckSwitch))
|
||||
anchoredPrimitive = &controlNode.getControlPrimExtra();
|
||||
else
|
||||
anchoredPrimitive = NULL;
|
||||
|
||||
//For each primary root, rum topologicalEmit
|
||||
for(curRoot = roots.begin(); curRoot < roots.end(); curRoot++)
|
||||
{
|
||||
if(curRoot->isPrimary && (curRoot->root != anchoredPrimitive))
|
||||
{
|
||||
if (curRoot->root->getInstructionRoot() != NULL)
|
||||
priorityTopoEmit(curRoot->root->getInstructionRoot(), controlNode);
|
||||
|
||||
// FIX-ME no longer need curOutput
|
||||
DataNode* curOutput = curRoot->root;
|
||||
|
||||
// although exception edges are not explicit
|
||||
// we need to consider them as real outgoing edges
|
||||
if ((curOutput->hasConsumers() || curOutput->canRaiseException()) && !curOutput->hasKind(vkVoid))
|
||||
{
|
||||
// now walk through all vr's assigned to this producer
|
||||
VirtualRegister* curVR;
|
||||
Instruction* nextInsn;
|
||||
|
||||
switch (curOutput->getKind())
|
||||
{
|
||||
case vkCond:
|
||||
case vkMemory:
|
||||
nextInsn = curOutput->getInstructionAnnotation();
|
||||
if (nextInsn)
|
||||
priorityTopoEmit(nextInsn, controlNode);
|
||||
break;
|
||||
case vkLong:
|
||||
curVR = curOutput->getHighVirtualRegisterAnnotation();
|
||||
assert(curVR);
|
||||
nextInsn = curVR->getDefiningInstruction();
|
||||
if (nextInsn)
|
||||
priorityTopoEmit(nextInsn, controlNode);
|
||||
// FALL THROUGH for low vr
|
||||
case vkAddr:
|
||||
case vkInt:
|
||||
case vkFloat:
|
||||
case vkDouble:
|
||||
curVR = curOutput->getLowVirtualRegisterAnnotation();
|
||||
assert(curVR);
|
||||
nextInsn = curVR->getDefiningInstruction();
|
||||
if (nextInsn)
|
||||
priorityTopoEmit(nextInsn, controlNode);
|
||||
break;
|
||||
case vkTuple:
|
||||
case vkVoid:
|
||||
break;
|
||||
default:
|
||||
trespass("unknown or unhandled output");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (anchoredPrimitive)
|
||||
priorityTopoEmit(anchoredPrimitive->getInstructionRoot(), controlNode);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Compute the debugLineNumbers for each Primitive in the CN.
|
||||
The debugLineNumber assures that the stable sort will not invalidate the scheduled instruction
|
||||
ordering. Debug line numbers are the minimum integer values which maintain the following
|
||||
properties: lineNumber(P) <= debugLineNumber(P) and for every Primitive P' which
|
||||
defines an edge consumed by P, debugLineNumber(P) >= debugLineNumber(P').
|
||||
*/
|
||||
|
||||
void LinearInstructionScheduler::
|
||||
renumberCN(Vector<RootPair>& roots, ControlNode& /*controlNode*/)
|
||||
{
|
||||
|
||||
RootPair* curRoot;
|
||||
|
||||
int rootsToRenumber = roots.size();
|
||||
int rootsRenumbered = 0;
|
||||
|
||||
const DoublyLinkedList<DataConsumer>* consumers;
|
||||
DoublyLinkedList<DataConsumer> :: iterator curConsumer;
|
||||
|
||||
// Initialize all root's renumberData
|
||||
for(curRoot = roots.begin(); curRoot < roots.end(); curRoot++)
|
||||
{
|
||||
if(curRoot->isPrimary)
|
||||
curRoot->data.renumbered = false;
|
||||
else {
|
||||
curRoot->data.renumbered = false;
|
||||
curRoot->data.timesVisited = 0;
|
||||
curRoot->data.neededVisits = 0;
|
||||
|
||||
if (curRoot->root->getOutgoingEdgesEnd() > curRoot->root->getOutgoingEdgesBegin())
|
||||
{
|
||||
DataNode* curOutput;
|
||||
for (curOutput = curRoot->root->getOutgoingEdgesBegin();
|
||||
curOutput < curRoot->root->getOutgoingEdgesEnd();
|
||||
curOutput++)
|
||||
{
|
||||
consumers = &curOutput->getConsumers();
|
||||
for(curConsumer = consumers->begin(); !consumers->done(curConsumer);
|
||||
curConsumer = consumers->advance(curConsumer))
|
||||
curRoot->data.neededVisits++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Renumber Primary Roots
|
||||
for(curRoot = roots.begin(); curRoot < roots.end(); curRoot++)
|
||||
{
|
||||
if(curRoot->isPrimary)
|
||||
{
|
||||
curRoot->root->setDebugLineNumber(curRoot->root->getLineNumber());
|
||||
renumberPrimitive(roots, *(curRoot->root));
|
||||
rootsRenumbered++;
|
||||
curRoot->data.renumbered = true;
|
||||
}
|
||||
}
|
||||
|
||||
curRoot = roots.begin();
|
||||
while ( rootsRenumbered != rootsToRenumber )
|
||||
{
|
||||
while(curRoot->data.renumbered)
|
||||
{
|
||||
curRoot++;
|
||||
if(curRoot == roots.end())
|
||||
curRoot = roots.begin();
|
||||
}
|
||||
|
||||
if(curRoot->data.timesVisited != curRoot->data.neededVisits)
|
||||
{
|
||||
renumberPrimitive(roots, *(curRoot->root));
|
||||
rootsRenumbered++;
|
||||
curRoot->data.renumbered = true;
|
||||
}
|
||||
|
||||
curRoot++;
|
||||
if(curRoot == roots.end())
|
||||
curRoot = roots.begin();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void LinearInstructionScheduler::
|
||||
renumberPrimitive(Vector<RootPair>& roots, Primitive& p)
|
||||
{
|
||||
DataConsumer* curConsumer;
|
||||
|
||||
for (curConsumer = p.getInputsBegin();
|
||||
curConsumer < p.getInputsEnd();
|
||||
curConsumer++)
|
||||
{
|
||||
if(curConsumer->isVariable())
|
||||
{
|
||||
DataNode& possibleChild = curConsumer->getNode();
|
||||
|
||||
if (!possibleChild.hasCategory(pcPhi))
|
||||
{
|
||||
Primitive &child = Primitive::cast(possibleChild);
|
||||
if(child.getDebugLineNumber() == 0)
|
||||
child.setDebugLineNumber(child.getLineNumber());
|
||||
|
||||
if(child.getDebugLineNumber() < p.getDebugLineNumber())
|
||||
child.setDebugLineNumber(p.getDebugLineNumber());
|
||||
|
||||
RootPair* curRoot;
|
||||
bool isRoot = false;
|
||||
for(curRoot = roots.begin(); curRoot < roots.end(); curRoot++)
|
||||
{
|
||||
if(curRoot->root == &child)
|
||||
{
|
||||
isRoot = true;
|
||||
curRoot->data.timesVisited++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(!isRoot)
|
||||
renumberPrimitive(roots, child);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
/* -*- 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.
|
||||
*/
|
||||
// LinearInstructionScheduler.h
|
||||
//
|
||||
// Peter DeSantis
|
||||
// Scott M. Silver
|
||||
|
||||
|
||||
// General Idea:
|
||||
// preserves the src code ordering
|
||||
// instructions. It is the result of a topological sort of the control node from each primary
|
||||
// root. A primary root is one which is not reachable by any other root in the control node.
|
||||
// When there is more than outgoing edge from a node, they are searched in increasing order of
|
||||
// their srcLine fields.
|
||||
|
||||
#ifndef SCHEDULER_H
|
||||
#define SCHEDULER_H
|
||||
|
||||
#include "CodeGenerator.h"
|
||||
#include "Vector.h"
|
||||
|
||||
class LinearInstructionScheduler
|
||||
{
|
||||
public:
|
||||
void schedule(Vector<RootPair>& roots, ControlNode& controlNode);
|
||||
|
||||
protected:
|
||||
void priorityTopoEmit(Instruction* inInstruction, ControlNode& controlNode);
|
||||
void renumberCN(Vector<RootPair>& roots, ControlNode& controlNode);
|
||||
void renumberPrimitive(Vector<RootPair>& roots, Primitive& p);
|
||||
void linearSchedule(Vector<RootPair>& roots, ControlNode& controlNode);
|
||||
};
|
||||
|
||||
#endif //SCHEDULER_H
|
||||
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
/* -*- 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.
|
||||
*/
|
||||
|
||||
#if !defined(_CPU_INFO_H_) || defined(INCLUDE_EMITTER)
|
||||
#define _CPU_INFO_H_
|
||||
|
||||
#ifdef GENERATE_FOR_PPC
|
||||
#include "PPC601Cpu.h"
|
||||
#elif defined(GENERATE_FOR_X86)
|
||||
#include "x86Win32Cpu.h"
|
||||
#elif defined(GENERATE_FOR_SPARC)
|
||||
//#include "SparcCpu.h"
|
||||
#elif defined(GENERATE_FOR_HPPA)
|
||||
#include "HPPACpu.h"
|
||||
#endif
|
||||
|
||||
#ifndef CPU_IS_SUPPORTED
|
||||
#error "Processor not supported"
|
||||
#endif
|
||||
|
||||
#define NUMBER_OF_CALLER_SAVED_GR (LAST_CALLER_SAVED_GR - FIRST_CALLER_SAVED_GR + 1)
|
||||
#define NUMBER_OF_CALLEE_SAVED_GR (LAST_CALLEE_SAVED_GR - FIRST_CALLEE_SAVED_GR + 1)
|
||||
#define NUMBER_OF_CALLER_SAVED_FPR (LAST_CALLER_SAVED_FPR - FIRST_CALLER_SAVED_FPR + 1)
|
||||
#define NUMBER_OF_CALLEE_SAVED_FPR (LAST_CALLEE_SAVED_FPR - FIRST_CALLEE_SAVED_FPR + 1)
|
||||
|
||||
#define NUMBER_OF_GREGISTERS (LAST_GREGISTER - FIRST_GREGISTER + 1)
|
||||
#define NUMBER_OF_FPREGISTERS (LAST_FPREGISTER - FIRST_FPREGISTER + 1)
|
||||
#define NUMBER_OF_REGISTERS (NUMBER_OF_GREGISTERS + NUMBER_OF_FPREGISTERS + NUMBER_OF_SPECIAL_REGISTERS)
|
||||
|
||||
|
||||
#endif /* _CPU_INFO_H_ */
|
|
@ -0,0 +1,53 @@
|
|||
#!gmake
|
||||
#
|
||||
# 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.
|
||||
|
||||
#######################################################################
|
||||
# (1) Directory specific info #
|
||||
#######################################################################
|
||||
|
||||
DEPTH = ../../..
|
||||
|
||||
DIRS = $(CPU_ARCH) $(GENERIC)
|
||||
|
||||
SUBMODULES = $(DIRS)
|
||||
|
||||
LOCAL_EXPORTS = CpuInfo.h
|
||||
|
||||
#######################################################################
|
||||
# (2) Include "component" configuration information. #
|
||||
#######################################################################
|
||||
|
||||
include $(DEPTH)/config/config.mk
|
||||
|
||||
#######################################################################
|
||||
# (3) Include "local" platform-dependent assignments (OPTIONAL). #
|
||||
#######################################################################
|
||||
|
||||
|
||||
|
||||
#######################################################################
|
||||
# (4) Execute "component" rules. (OPTIONAL) #
|
||||
#######################################################################
|
||||
|
||||
include $(DEPTH)/config/rules.mk
|
||||
|
||||
#######################################################################
|
||||
# (7) Execute "local" rules. (OPTIONAL). #
|
||||
#######################################################################
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
/* -*- 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.
|
||||
*/
|
||||
/*
|
||||
Generic_Support
|
||||
*/
|
||||
|
||||
#include "NativeCodeCache.h"
|
||||
#include <stdio.h>
|
||||
#include "Fundamentals.h"
|
||||
|
||||
void* backPatchMethod(void* /*inMethodAddress*/, void* /*inLastPC*/, void* /*inUserDefined*/)
|
||||
{
|
||||
trespass("not implemented");
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
static void dummyStub();
|
||||
|
||||
static void dummyStub()
|
||||
{
|
||||
printf("dummyStub #1\n");
|
||||
}
|
||||
|
||||
void*
|
||||
generateCompileStub(NativeCodeCache& /*inCache*/, const CacheEntry& /*inCacheEntry*/)
|
||||
{
|
||||
#ifndef GENERATE_FOR_PPC
|
||||
return ((void*) dummyStub);
|
||||
#else
|
||||
return (*(void**) dummyStub);
|
||||
#endif
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
#!gmake
|
||||
#
|
||||
# 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.
|
||||
|
||||
#######################################################################
|
||||
# (1) Directory specific info #
|
||||
#######################################################################
|
||||
|
||||
DEPTH = ../../../..
|
||||
|
||||
CPPSRCS = Generic_Support.cpp \
|
||||
$(NULL)
|
||||
|
||||
#######################################################################
|
||||
# (2) Include "global" configuration information. (OPTIONAL) #
|
||||
#######################################################################
|
||||
|
||||
|
||||
#######################################################################
|
||||
# (3) Include "component" configuration information. (OPTIONAL) #
|
||||
#######################################################################
|
||||
|
||||
|
||||
#######################################################################
|
||||
# (4) Include "local" platform-dependent assignments (OPTIONAL). #
|
||||
#######################################################################
|
||||
|
||||
|
||||
|
||||
#######################################################################
|
||||
# (5) Execute "global" rules. (OPTIONAL) #
|
||||
#######################################################################
|
||||
|
||||
|
||||
#######################################################################
|
||||
# (6) Execute "component" rules. (OPTIONAL) #
|
||||
#######################################################################
|
||||
|
||||
|
||||
include $(DEPTH)/config/rules.mk
|
||||
|
||||
#######################################################################
|
||||
# (7) Execute "local" rules. (OPTIONAL). #
|
||||
#######################################################################
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
/* -*- 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 _HPPA_CPU_H_
|
||||
#define _HPPA_CPU_H_
|
||||
|
||||
#define FIRST_CALLER_SAVED_GR 0
|
||||
#define LAST_CALLER_SAVED_GR 12
|
||||
#define FIRST_CALLEE_SAVED_GR 13
|
||||
#define LAST_CALLEE_SAVED_GR 28
|
||||
#define FIRST_CALLER_SAVED_FPR 29
|
||||
#define LAST_CALLER_SAVED_FPR 56
|
||||
#define FIRST_CALLEE_SAVED_FPR 57
|
||||
#define LAST_CALLEE_SAVED_FPR 76
|
||||
|
||||
#define FIRST_GREGISTER 0
|
||||
#define LAST_GREGISTER 28
|
||||
#define FIRST_FPREGISTER 29
|
||||
#define LAST_FPREGISTER 76
|
||||
#define NUMBER_OF_SPECIAL_REGISTERS 3
|
||||
|
||||
#define HAS_GR_PAIRS
|
||||
#define HAS_FPR_PAIRS
|
||||
|
||||
#define DOUBLE_WORD_GR_ALIGNMENT
|
||||
|
||||
#undef SOFT_FLOAT
|
||||
|
||||
#undef SINGLE_WORD_FPR_ALIGNMENT
|
||||
#define SINGLE_WORD_FPR_SIZE 1
|
||||
|
||||
#define DOUBLE_WORD_FPR_ALIGNMENT
|
||||
#define DOUBLE_WORD_FPR_SIZE 2
|
||||
|
||||
#define QUAD_WORD_FPR_ALIGNMENT
|
||||
#define QUAD_WORD_FPR_SIZE 4
|
||||
|
||||
#define CPU_IS_SUPPORTED
|
||||
|
||||
#if defined(INCLUDE_EMITTER)
|
||||
#include "HPPAEmitter.h"
|
||||
typedef HPPAEmitter MdEmitter;
|
||||
#endif /* INCLUDE_EMITTER */
|
||||
|
||||
#endif /* _HPPA_CPU_H_ */
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,103 @@
|
|||
/* -*- 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 _HPPA_EMITTER_H_
|
||||
#define _HPPA_EMITTER_H_
|
||||
|
||||
#include "InstructionEmitter.h"
|
||||
#include "HPPAInstruction.h"
|
||||
#include "VirtualRegister.h"
|
||||
#include "FastBitSet.h"
|
||||
|
||||
class PrimArg;
|
||||
class Pool;
|
||||
|
||||
#define UNDEFINED_RULE(inRuleKind) \
|
||||
case inRuleKind: \
|
||||
PR_ASSERT(false); \
|
||||
break;
|
||||
|
||||
#define UNIMP_CODE(a)
|
||||
|
||||
/*
|
||||
*-----------------------------------------------------------------------
|
||||
*
|
||||
* HPPA-1.1 Instruction Emitter.
|
||||
*
|
||||
*-----------------------------------------------------------------------
|
||||
*/
|
||||
class HPPAEmitter : public InstructionEmitter
|
||||
{
|
||||
protected:
|
||||
Instruction& genInstruction(DataNode* inPrimitive, Pool& inPool, HPPAInstructionKind kind, PRUint32 data = 0);
|
||||
|
||||
void emitInstruction(Primitive& inPrimitive, HPPAInstructionKind kind);
|
||||
void emitCondBranch(Primitive& inPrimitive, HPPAConditionKind kind);
|
||||
void emitOrImm(Primitive& inPrimitive, const Value& value);
|
||||
void emitAndImm(Primitive& inPrimitive, const Value& value);
|
||||
void emitXorImm(Primitive& inPrimitive, const Value& value);
|
||||
void emitAddImm(DataNode& inPrimitive, const Value& value);
|
||||
void emitDivImm(Primitive& inPrimitive, const Value& value);
|
||||
void emitMulImm(Primitive& inPrimitive, const Value& value);
|
||||
void emitShlImm(Primitive& inPrimitive, const Value& value);
|
||||
void emitConst(Primitive& inPrimitive, const Value& value);
|
||||
void emitResult(Primitive& inPrimitive);
|
||||
void emitChkNull(Primitive& inPrimitive);
|
||||
void emitLimitR(Primitive& inPrimitive);
|
||||
void emitLimit(Primitive& inPrimitive);
|
||||
void emitLoad(Primitive& inPrimitive, HPPAInstructionFlags flags = HPPA_NONE);
|
||||
void emitLoadFromImm(Primitive& inPrimitive, HPPAInstructionFlags flags = HPPA_NONE);
|
||||
void emitStore(Primitive& inPrimitive, HPPAInstructionFlags flags = HPPA_NONE);
|
||||
void emitStoreImm(Primitive& inPrimitive, HPPAInstructionFlags flags = HPPA_NONE);
|
||||
void emitStoreToImm(Primitive& inPrimitive, HPPAInstructionFlags flags = HPPA_NONE);
|
||||
void emitStoreImmToImm(Primitive& inPrimitive, HPPAInstructionFlags flags = HPPA_NONE);
|
||||
void emitShAddIndirect(Primitive& inPrimitive);
|
||||
void emitSpecialCall(Primitive& inPrimitive, HPPASpecialCallKind kind);
|
||||
|
||||
VirtualRegister& genLoadConstant(DataNode& inPrimitive, const Value& value, bool checkIM14 = true);
|
||||
VirtualRegister& genShiftAdd(Primitive& inPrimitive, const PRUint8 shiftBy, VirtualRegister& shiftedReg, VirtualRegister& addedReg);
|
||||
VirtualRegister& genShiftSub(Primitive& inPrimitive, const PRUint8 shiftBy, VirtualRegister& shiftedReg, VirtualRegister& subtractedReg, bool neg = false);
|
||||
|
||||
static void initSpecialCall();
|
||||
static bool initSpecialCallDone;
|
||||
|
||||
public:
|
||||
HPPAEmitter(Pool& inPool, VirtualRegisterManager& vrMan) : InstructionEmitter(inPool, vrMan)
|
||||
{
|
||||
if (!initSpecialCallDone)
|
||||
{
|
||||
initSpecialCallDone = true;
|
||||
initSpecialCall();
|
||||
}
|
||||
}
|
||||
|
||||
virtual void emitPrimitive(Primitive& inPrimitive, NamedRule inRule);
|
||||
|
||||
void calculatePrologEpilog(Method& /*inMethod*/, uint32& outPrologSize, uint32& outEpilogSize) {outPrologSize = 32; outEpilogSize = 4;}
|
||||
virtual void formatPrologToMemory(void* inWhere);
|
||||
virtual void formatEpilogToMemory(void* inWhere) {*(PRUint32*)inWhere = 0xe840c002; /* bv,n 0(%rp) */}
|
||||
|
||||
virtual void emitArgument(PrimArg& /*inArguments*/, const PRUint32 /*inArgumentIndex*/) {}
|
||||
virtual void emitArguments(ControlBegin& inBeginNode);
|
||||
virtual Instruction& emitAbsoluteBranch(DataNode& inDataNode, ControlNode& inTarget);
|
||||
virtual bool emitCopyAfter(DataNode& inDataNode, SortedDoublyLinkedList<Instruction>::iterator where, VirtualRegister& fromVr, VirtualRegister& toVr);
|
||||
virtual void emitLoadAfter(DataNode& /*inDataNode*/, SortedDoublyLinkedList<Instruction>::iterator /*where*/, VirtualRegister& /*loadedReg*/, VirtualRegister& /*stackReg*/) {}
|
||||
virtual void emitStoreAfter(DataNode& /*inDataNode*/, SortedDoublyLinkedList<Instruction>::iterator /*where*/, VirtualRegister& /*storedReg*/, VirtualRegister& /*stackReg*/) {}
|
||||
};
|
||||
|
||||
#endif /* _HPPA_EMITTER_H_ */
|
|
@ -0,0 +1,43 @@
|
|||
/* -*- 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 _HPPA_FUNCS_H_
|
||||
#define _HPPA_FUNCS_H_
|
||||
|
||||
#include "prtypes.h"
|
||||
|
||||
|
||||
#if defined(hppa)
|
||||
#define HPPAFuncAddress(func) (func)
|
||||
#else
|
||||
#define HPPAFuncAddress(func) NULL
|
||||
#endif /* defined(hppa) */
|
||||
|
||||
PR_BEGIN_EXTERN_C
|
||||
|
||||
extern PRUint32* HPPASpecialCodeBegin;
|
||||
extern PRUint32* HPPASpecialCodeEnd;
|
||||
|
||||
extern PRUint32* HPPAremI;
|
||||
extern PRUint32* HPPAremU;
|
||||
extern PRUint32* HPPAdivI;
|
||||
extern PRUint32* HPPAdivU;
|
||||
|
||||
PR_END_EXTERN_C
|
||||
|
||||
#endif /* _HPPA_FUNCS_H_ */
|
|
@ -0,0 +1,694 @@
|
|||
/* -*- 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.
|
||||
*/
|
||||
.SPACE $PRIVATE$
|
||||
.SUBSPA $DATA$
|
||||
|
||||
.align 4
|
||||
.EXPORT HPPAremI,DATA
|
||||
HPPAremI:
|
||||
.long _remI
|
||||
|
||||
.EXPORT HPPAremU,DATA
|
||||
HPPAremU:
|
||||
.long _remU
|
||||
|
||||
.EXPORT HPPAdivI,DATA
|
||||
HPPAdivI:
|
||||
.long _divI
|
||||
|
||||
.EXPORT HPPAdivU,DATA
|
||||
HPPAdivU:
|
||||
.long _divU
|
||||
|
||||
.EXPORT HPPASpecialCodeBegin,DATA
|
||||
HPPASpecialCodeBegin:
|
||||
.long _begin
|
||||
|
||||
.EXPORT HPPASpecialCodeEnd,DATA
|
||||
HPPASpecialCodeEnd:
|
||||
.long _end
|
||||
|
||||
.SPACE $TEXT$
|
||||
.SUBSPA $CODE$
|
||||
|
||||
.align 4
|
||||
_begin:
|
||||
|
||||
_remI:
|
||||
addit,= 0,%arg1,%r0
|
||||
add,>= %r0,%arg0,%ret1
|
||||
sub %r0,%ret1,%ret1
|
||||
sub %r0,%arg1,%r1
|
||||
ds %r0,%r1,%r0
|
||||
copy %r0,%r1
|
||||
add %ret1,%ret1,%ret1
|
||||
ds %r1,%arg1,%r1
|
||||
addc %ret1,%ret1,%ret1
|
||||
ds %r1,%arg1,%r1
|
||||
addc %ret1,%ret1,%ret1
|
||||
ds %r1,%arg1,%r1
|
||||
addc %ret1,%ret1,%ret1
|
||||
ds %r1,%arg1,%r1
|
||||
addc %ret1,%ret1,%ret1
|
||||
ds %r1,%arg1,%r1
|
||||
addc %ret1,%ret1,%ret1
|
||||
ds %r1,%arg1,%r1
|
||||
addc %ret1,%ret1,%ret1
|
||||
ds %r1,%arg1,%r1
|
||||
addc %ret1,%ret1,%ret1
|
||||
ds %r1,%arg1,%r1
|
||||
addc %ret1,%ret1,%ret1
|
||||
ds %r1,%arg1,%r1
|
||||
addc %ret1,%ret1,%ret1
|
||||
ds %r1,%arg1,%r1
|
||||
addc %ret1,%ret1,%ret1
|
||||
ds %r1,%arg1,%r1
|
||||
addc %ret1,%ret1,%ret1
|
||||
ds %r1,%arg1,%r1
|
||||
addc %ret1,%ret1,%ret1
|
||||
ds %r1,%arg1,%r1
|
||||
addc %ret1,%ret1,%ret1
|
||||
ds %r1,%arg1,%r1
|
||||
addc %ret1,%ret1,%ret1
|
||||
ds %r1,%arg1,%r1
|
||||
addc %ret1,%ret1,%ret1
|
||||
ds %r1,%arg1,%r1
|
||||
addc %ret1,%ret1,%ret1
|
||||
ds %r1,%arg1,%r1
|
||||
addc %ret1,%ret1,%ret1
|
||||
ds %r1,%arg1,%r1
|
||||
addc %ret1,%ret1,%ret1
|
||||
ds %r1,%arg1,%r1
|
||||
addc %ret1,%ret1,%ret1
|
||||
ds %r1,%arg1,%r1
|
||||
addc %ret1,%ret1,%ret1
|
||||
ds %r1,%arg1,%r1
|
||||
addc %ret1,%ret1,%ret1
|
||||
ds %r1,%arg1,%r1
|
||||
addc %ret1,%ret1,%ret1
|
||||
ds %r1,%arg1,%r1
|
||||
addc %ret1,%ret1,%ret1
|
||||
ds %r1,%arg1,%r1
|
||||
addc %ret1,%ret1,%ret1
|
||||
ds %r1,%arg1,%r1
|
||||
addc %ret1,%ret1,%ret1
|
||||
ds %r1,%arg1,%r1
|
||||
addc %ret1,%ret1,%ret1
|
||||
ds %r1,%arg1,%r1
|
||||
addc %ret1,%ret1,%ret1
|
||||
ds %r1,%arg1,%r1
|
||||
addc %ret1,%ret1,%ret1
|
||||
ds %r1,%arg1,%r1
|
||||
addc %ret1,%ret1,%ret1
|
||||
ds %r1,%arg1,%r1
|
||||
addc %ret1,%ret1,%ret1
|
||||
ds %r1,%arg1,%r1
|
||||
addc %ret1,%ret1,%ret1
|
||||
ds %r1,%arg1,%r1
|
||||
addc %ret1,%ret1,%ret1
|
||||
movb,>=,n %r1,%ret1,L5
|
||||
add,< %arg1,%r0,%r0
|
||||
add,tr %r1,%arg1,%ret1
|
||||
sub %r1,%arg1,%ret1
|
||||
L5: add,>= %arg0,%r0,%r0
|
||||
sub %r0,%ret1,%ret1
|
||||
bv %r0(%r31)
|
||||
nop
|
||||
|
||||
.align 4
|
||||
_remU:
|
||||
comibf,<,n 0x0,%arg1,_remU_special_case
|
||||
sub %r0,%arg1,%ret1
|
||||
ds %r0,%ret1,%r0
|
||||
add %arg0,%arg0,%r1
|
||||
ds %r0,%arg1,%ret1
|
||||
addc %r1,%r1,%r1
|
||||
ds %ret1,%arg1,%ret1
|
||||
addc %r1,%r1,%r1
|
||||
ds %ret1,%arg1,%ret1
|
||||
addc %r1,%r1,%r1
|
||||
ds %ret1,%arg1,%ret1
|
||||
addc %r1,%r1,%r1
|
||||
ds %ret1,%arg1,%ret1
|
||||
addc %r1,%r1,%r1
|
||||
ds %ret1,%arg1,%ret1
|
||||
addc %r1,%r1,%r1
|
||||
ds %ret1,%arg1,%ret1
|
||||
addc %r1,%r1,%r1
|
||||
ds %ret1,%arg1,%ret1
|
||||
addc %r1,%r1,%r1
|
||||
ds %ret1,%arg1,%ret1
|
||||
addc %r1,%r1,%r1
|
||||
ds %ret1,%arg1,%ret1
|
||||
addc %r1,%r1,%r1
|
||||
ds %ret1,%arg1,%ret1
|
||||
addc %r1,%r1,%r1
|
||||
ds %ret1,%arg1,%ret1
|
||||
addc %r1,%r1,%r1
|
||||
ds %ret1,%arg1,%ret1
|
||||
addc %r1,%r1,%r1
|
||||
ds %ret1,%arg1,%ret1
|
||||
addc %r1,%r1,%r1
|
||||
ds %ret1,%arg1,%ret1
|
||||
addc %r1,%r1,%r1
|
||||
ds %ret1,%arg1,%ret1
|
||||
addc %r1,%r1,%r1
|
||||
ds %ret1,%arg1,%ret1
|
||||
addc %r1,%r1,%r1
|
||||
ds %ret1,%arg1,%ret1
|
||||
addc %r1,%r1,%r1
|
||||
ds %ret1,%arg1,%ret1
|
||||
addc %r1,%r1,%r1
|
||||
ds %ret1,%arg1,%ret1
|
||||
addc %r1,%r1,%r1
|
||||
ds %ret1,%arg1,%ret1
|
||||
addc %r1,%r1,%r1
|
||||
ds %ret1,%arg1,%ret1
|
||||
addc %r1,%r1,%r1
|
||||
ds %ret1,%arg1,%ret1
|
||||
addc %r1,%r1,%r1
|
||||
ds %ret1,%arg1,%ret1
|
||||
addc %r1,%r1,%r1
|
||||
ds %ret1,%arg1,%ret1
|
||||
addc %r1,%r1,%r1
|
||||
ds %ret1,%arg1,%ret1
|
||||
addc %r1,%r1,%r1
|
||||
ds %ret1,%arg1,%ret1
|
||||
addc %r1,%r1,%r1
|
||||
ds %ret1,%arg1,%ret1
|
||||
addc %r1,%r1,%r1
|
||||
ds %ret1,%arg1,%ret1
|
||||
addc %r1,%r1,%r1
|
||||
ds %ret1,%arg1,%ret1
|
||||
addc %r1,%r1,%r1
|
||||
ds %ret1,%arg1,%ret1
|
||||
addc %r1,%r1,%r1
|
||||
ds %ret1,%arg1,%ret1
|
||||
comiclr,<= 0,%ret1,%r0
|
||||
add %ret1,%arg1,%ret1
|
||||
bv,n %r0(%r31)
|
||||
nop
|
||||
_remU_special_case:
|
||||
addit,= 0,%arg1,%r0
|
||||
sub,>>= %arg0,%arg1,%ret1
|
||||
copy %arg0,%ret1
|
||||
bv,n %r0(%r31)
|
||||
nop
|
||||
|
||||
.align 4
|
||||
_divI:
|
||||
comibf,<<,n 0xf,%arg1,_divI_small_divisor
|
||||
add,>= %r0,%arg0,%ret1
|
||||
_divI_normal:
|
||||
sub %r0,%ret1,%ret1
|
||||
sub %r0,%arg1,%r1
|
||||
ds %r0,%r1,%r0
|
||||
add %ret1,%ret1,%ret1
|
||||
ds %r0,%arg1,%r1
|
||||
addc %ret1,%ret1,%ret1
|
||||
ds %r1,%arg1,%r1
|
||||
addc %ret1,%ret1,%ret1
|
||||
ds %r1,%arg1,%r1
|
||||
addc %ret1,%ret1,%ret1
|
||||
ds %r1,%arg1,%r1
|
||||
addc %ret1,%ret1,%ret1
|
||||
ds %r1,%arg1,%r1
|
||||
addc %ret1,%ret1,%ret1
|
||||
ds %r1,%arg1,%r1
|
||||
addc %ret1,%ret1,%ret1
|
||||
ds %r1,%arg1,%r1
|
||||
addc %ret1,%ret1,%ret1
|
||||
ds %r1,%arg1,%r1
|
||||
addc %ret1,%ret1,%ret1
|
||||
ds %r1,%arg1,%r1
|
||||
addc %ret1,%ret1,%ret1
|
||||
ds %r1,%arg1,%r1
|
||||
addc %ret1,%ret1,%ret1
|
||||
ds %r1,%arg1,%r1
|
||||
addc %ret1,%ret1,%ret1
|
||||
ds %r1,%arg1,%r1
|
||||
addc %ret1,%ret1,%ret1
|
||||
ds %r1,%arg1,%r1
|
||||
addc %ret1,%ret1,%ret1
|
||||
ds %r1,%arg1,%r1
|
||||
addc %ret1,%ret1,%ret1
|
||||
ds %r1,%arg1,%r1
|
||||
addc %ret1,%ret1,%ret1
|
||||
ds %r1,%arg1,%r1
|
||||
addc %ret1,%ret1,%ret1
|
||||
ds %r1,%arg1,%r1
|
||||
addc %ret1,%ret1,%ret1
|
||||
ds %r1,%arg1,%r1
|
||||
addc %ret1,%ret1,%ret1
|
||||
ds %r1,%arg1,%r1
|
||||
addc %ret1,%ret1,%ret1
|
||||
ds %r1,%arg1,%r1
|
||||
addc %ret1,%ret1,%ret1
|
||||
ds %r1,%arg1,%r1
|
||||
addc %ret1,%ret1,%ret1
|
||||
ds %r1,%arg1,%r1
|
||||
addc %ret1,%ret1,%ret1
|
||||
ds %r1,%arg1,%r1
|
||||
addc %ret1,%ret1,%ret1
|
||||
ds %r1,%arg1,%r1
|
||||
addc %ret1,%ret1,%ret1
|
||||
ds %r1,%arg1,%r1
|
||||
addc %ret1,%ret1,%ret1
|
||||
ds %r1,%arg1,%r1
|
||||
addc %ret1,%ret1,%ret1
|
||||
ds %r1,%arg1,%r1
|
||||
addc %ret1,%ret1,%ret1
|
||||
ds %r1,%arg1,%r1
|
||||
addc %ret1,%ret1,%ret1
|
||||
ds %r1,%arg1,%r1
|
||||
addc %ret1,%ret1,%ret1
|
||||
ds %r1,%arg1,%r1
|
||||
addc %ret1,%ret1,%ret1
|
||||
ds %r1,%arg1,%r1
|
||||
addc %ret1,%ret1,%ret1
|
||||
ds %r1,%arg1,%r1
|
||||
addc %ret1,%ret1,%ret1
|
||||
xor,>= %arg0,%arg1,%r0
|
||||
sub %r0,%ret1,%ret1
|
||||
bv,n %r0(%r31)
|
||||
nop
|
||||
|
||||
_divI_small_divisor:
|
||||
blr,n %arg1,%r0
|
||||
nop
|
||||
addit,= 0,%arg1,%r0
|
||||
nop
|
||||
bv %r0(%r31)
|
||||
copy %arg0,%ret1
|
||||
b,n _divI_2
|
||||
nop
|
||||
b,n _divI_3
|
||||
nop
|
||||
b,n _divI_4
|
||||
nop
|
||||
b,n _divI_5
|
||||
nop
|
||||
b,n _divI_6
|
||||
nop
|
||||
b,n _divI_7
|
||||
nop
|
||||
b,n _divI_8
|
||||
nop
|
||||
b,n _divI_9
|
||||
nop
|
||||
b,n _divI_10
|
||||
nop
|
||||
b _divI_normal
|
||||
add,>= %r0,%arg0,%ret1
|
||||
b,n _divI_12
|
||||
nop
|
||||
b _divI_normal
|
||||
add,>= %r0,%arg0,%ret1
|
||||
b,n _divI_14
|
||||
nop
|
||||
b,n _divI_15
|
||||
nop
|
||||
|
||||
.align 4
|
||||
_divU:
|
||||
comibf,< 0xf,%arg1,_divU_special_divisor
|
||||
sub %r0,%arg1,%r1
|
||||
ds %r0,%r1,%r0
|
||||
_divU_normal:
|
||||
add %arg0,%arg0,%ret1
|
||||
ds %r0,%arg1,%r1
|
||||
addc %ret1,%ret1,%ret1
|
||||
ds %r1,%arg1,%r1
|
||||
addc %ret1,%ret1,%ret1
|
||||
ds %r1,%arg1,%r1
|
||||
addc %ret1,%ret1,%ret1
|
||||
ds %r1,%arg1,%r1
|
||||
addc %ret1,%ret1,%ret1
|
||||
ds %r1,%arg1,%r1
|
||||
addc %ret1,%ret1,%ret1
|
||||
ds %r1,%arg1,%r1
|
||||
addc %ret1,%ret1,%ret1
|
||||
ds %r1,%arg1,%r1
|
||||
addc %ret1,%ret1,%ret1
|
||||
ds %r1,%arg1,%r1
|
||||
addc %ret1,%ret1,%ret1
|
||||
ds %r1,%arg1,%r1
|
||||
addc %ret1,%ret1,%ret1
|
||||
ds %r1,%arg1,%r1
|
||||
addc %ret1,%ret1,%ret1
|
||||
ds %r1,%arg1,%r1
|
||||
addc %ret1,%ret1,%ret1
|
||||
ds %r1,%arg1,%r1
|
||||
addc %ret1,%ret1,%ret1
|
||||
ds %r1,%arg1,%r1
|
||||
addc %ret1,%ret1,%ret1
|
||||
ds %r1,%arg1,%r1
|
||||
addc %ret1,%ret1,%ret1
|
||||
ds %r1,%arg1,%r1
|
||||
addc %ret1,%ret1,%ret1
|
||||
ds %r1,%arg1,%r1
|
||||
addc %ret1,%ret1,%ret1
|
||||
ds %r1,%arg1,%r1
|
||||
addc %ret1,%ret1,%ret1
|
||||
ds %r1,%arg1,%r1
|
||||
addc %ret1,%ret1,%ret1
|
||||
ds %r1,%arg1,%r1
|
||||
addc %ret1,%ret1,%ret1
|
||||
ds %r1,%arg1,%r1
|
||||
addc %ret1,%ret1,%ret1
|
||||
ds %r1,%arg1,%r1
|
||||
addc %ret1,%ret1,%ret1
|
||||
ds %r1,%arg1,%r1
|
||||
addc %ret1,%ret1,%ret1
|
||||
ds %r1,%arg1,%r1
|
||||
addc %ret1,%ret1,%ret1
|
||||
ds %r1,%arg1,%r1
|
||||
addc %ret1,%ret1,%ret1
|
||||
ds %r1,%arg1,%r1
|
||||
addc %ret1,%ret1,%ret1
|
||||
ds %r1,%arg1,%r1
|
||||
addc %ret1,%ret1,%ret1
|
||||
ds %r1,%arg1,%r1
|
||||
addc %ret1,%ret1,%ret1
|
||||
ds %r1,%arg1,%r1
|
||||
addc %ret1,%ret1,%ret1
|
||||
ds %r1,%arg1,%r1
|
||||
addc %ret1,%ret1,%ret1
|
||||
ds %r1,%arg1,%r1
|
||||
addc %ret1,%ret1,%ret1
|
||||
ds %r1,%arg1,%r1
|
||||
addc %ret1,%ret1,%ret1
|
||||
ds %r1,%arg1,%r1
|
||||
bv %r0(%r31)
|
||||
addc %ret1,%ret1,%ret1
|
||||
_divU_special_divisor:
|
||||
comibf,<= 0,%arg1,_divU_big_divisor
|
||||
nop
|
||||
blr %arg1,%r0
|
||||
nop
|
||||
_divU_small_divisor:
|
||||
addit,= 0,%arg1,%r0
|
||||
nop
|
||||
bv %r0(%r31)
|
||||
copy %arg0,%ret1
|
||||
bv %r0(%r31)
|
||||
extru %arg0,30,31,%ret1
|
||||
b,n _divU_3
|
||||
nop
|
||||
bv %r0(%r31)
|
||||
extru %arg0,29,30,%ret1
|
||||
b,n _divU_5
|
||||
nop
|
||||
b,n _divU_6
|
||||
nop
|
||||
b,n _divU_7
|
||||
nop
|
||||
bv %r0(%r31)
|
||||
extru %arg0,28,29,%ret1
|
||||
b,n _divU_9
|
||||
nop
|
||||
b,n _divU_10
|
||||
nop
|
||||
b _divU_normal
|
||||
ds %r0,%r1,%r0
|
||||
b,n _divU_12
|
||||
nop
|
||||
b _divU_normal
|
||||
ds %r0,%r1,%r0
|
||||
b,n _divU_14
|
||||
nop
|
||||
b,n _divU_15
|
||||
nop
|
||||
_divU_big_divisor:
|
||||
sub %arg0,%arg1,%r0
|
||||
bv %r0(%r31)
|
||||
addc %r0,%r0,%ret1
|
||||
|
||||
_divI_2:
|
||||
comclr,>= %arg0,%r0,%r0
|
||||
addi 1,%arg0,%arg0
|
||||
bv %r0(%r31)
|
||||
extrs %arg0,30,31,%ret1
|
||||
|
||||
_divI_4:
|
||||
comclr,>= %arg0,%r0,%r0
|
||||
addi 3,%arg0,%arg0
|
||||
bv %r0(%r31)
|
||||
extrs %arg0,29,30,%ret1
|
||||
|
||||
_divI_8:
|
||||
comclr,>= %arg0,%r0,%r0
|
||||
addi 7,%arg0,%arg0
|
||||
bv %r0(%r31)
|
||||
extrs %arg0,28,29,%ret1
|
||||
|
||||
_divI_3:
|
||||
comb,<,n %arg0,%r0,_divI_neg3
|
||||
addi 1,%arg0,%arg0
|
||||
extru %arg0,1,2,%ret1
|
||||
sh2add %arg0,%arg0,%arg0
|
||||
b _divI_pos
|
||||
addc %ret1,%r0,%ret1
|
||||
_divI_neg3:
|
||||
subi 1,%arg0,%arg0
|
||||
extru %arg0,1,2,%ret1
|
||||
sh2add %arg0,%arg0,%arg0
|
||||
b _divI_neg
|
||||
addc %ret1,%r0,%ret1
|
||||
_divU_3:
|
||||
addi 1,%arg0,%arg0
|
||||
addc %r0,%r0,%ret1
|
||||
shd %ret1,%arg0,30,%arg1
|
||||
sh2add %arg0,%arg0,%arg0
|
||||
b _divI_pos
|
||||
addc %ret1,%arg1,%ret1
|
||||
|
||||
_divI_5:
|
||||
comb,<,n %arg0,%r0,_divI_neg5
|
||||
addi 3,%arg0,%arg1
|
||||
sh1add %arg0,%arg1,%arg0
|
||||
b _divI_pos
|
||||
addc %r0,%r0,%ret1
|
||||
_divI_neg5:
|
||||
sub %r0,%arg0,%arg0
|
||||
addi 1,%arg0,%arg0
|
||||
shd %r0,%arg0,31,%ret1
|
||||
sh1add %arg0,%arg0,%arg0
|
||||
b _divI_neg
|
||||
addc %ret1,%r0,%ret1
|
||||
_divU_5:
|
||||
addi 1,%arg0,%arg0
|
||||
addc %r0,%r0,%ret1
|
||||
shd %ret1,%arg0,31,%arg1
|
||||
sh1add %arg0,%arg0,%arg0
|
||||
b _divI_pos
|
||||
addc %arg1,%ret1,%ret1
|
||||
|
||||
_divI_6:
|
||||
comb,<,n %arg0,%r0,_divI_neg6
|
||||
extru %arg0,30,31,%arg0
|
||||
addi 5,%arg0,%arg1
|
||||
sh2add %arg0,%arg1,%arg0
|
||||
b _divI_pos
|
||||
addc %r0,%r0,%ret1
|
||||
_divI_neg6:
|
||||
subi 2,%arg0,%arg0
|
||||
extru %arg0,30,31,%arg0
|
||||
shd %r0,%arg0,30,%ret1
|
||||
sh2add %arg0,%arg0,%arg0
|
||||
b _divI_neg
|
||||
addc %ret1,%r0,%ret1
|
||||
_divU_6:
|
||||
extru %arg0,30,31,%arg0
|
||||
addi 1,%arg0,%arg0
|
||||
shd %r0,%arg0,30,%ret1
|
||||
sh2add %arg0,%arg0,%arg0
|
||||
b _divI_pos
|
||||
addc %ret1,%r0,%ret1
|
||||
|
||||
_divI_10:
|
||||
comb,< %arg0,%r0,_divI_neg10
|
||||
copy %r0,%ret1
|
||||
extru %arg0,30,31,%arg0
|
||||
addibf 1,%arg0,_divI_pos
|
||||
sh1add %arg0,%arg0,%arg0
|
||||
_divI_neg10:
|
||||
subi 2,%arg0,%arg0
|
||||
extru %arg0,30,31,%arg0
|
||||
sh1add %arg0,%arg0,%arg0
|
||||
|
||||
_divI_neg:
|
||||
shd %ret1,%arg0,28,%arg1
|
||||
shd %arg0,%r0,28,%r1
|
||||
add %arg0,%r1,%arg0
|
||||
addc %ret1,%arg1,%ret1
|
||||
shd %ret1,%arg0,24,%arg1
|
||||
shd %arg0,%r0,24,%r1
|
||||
add %arg0,%r1,%arg0
|
||||
addc %ret1,%arg1,%ret1
|
||||
shd %ret1,%arg0,16,%arg1
|
||||
shd %arg0,%r0,16,%r1
|
||||
add %arg0,%r1,%arg0
|
||||
addc %ret1,%arg1,%ret1
|
||||
bv %r0(%r31)
|
||||
sub %r0,%ret1,%ret1
|
||||
|
||||
_divU_10:
|
||||
extru %arg0,30,31,%arg0
|
||||
addi 3,%arg0,%arg1
|
||||
sh1add %arg0,%arg1,%arg0
|
||||
addc %r0,%r0,%ret1
|
||||
|
||||
_divI_pos:
|
||||
shd %ret1,%arg0,28,%arg1
|
||||
_divI_pos_for_15:
|
||||
shd %arg0,%r0,28,%r1
|
||||
add %arg0,%r1,%arg0
|
||||
addc %ret1,%arg1,%ret1
|
||||
shd %ret1,%arg0,24,%arg1
|
||||
shd %arg0,%r0,24,%r1
|
||||
add %arg0,%r1,%arg0
|
||||
addc %ret1,%arg1,%ret1
|
||||
shd %ret1,%arg0,16,%arg1
|
||||
shd %arg0,%r0,16,%r1
|
||||
add %arg0,%r1,%arg0
|
||||
bv %r0(%r31)
|
||||
addc %ret1,%arg1,%ret1
|
||||
|
||||
_divI_12:
|
||||
comb,< %arg0,%r0,_divI_neg12
|
||||
copy %r0,%ret1
|
||||
extru %arg0,29,30,%arg0
|
||||
addibf 1,%arg0,_divI_pos
|
||||
sh2add %arg0,%arg0,%arg0
|
||||
_divI_neg12:
|
||||
subi 4,%arg0,%arg0
|
||||
extru %arg0,29,30,%arg0
|
||||
b _divI_neg
|
||||
sh2add %arg0,%arg0,%arg0
|
||||
_divU_12:
|
||||
extru %arg0,29,30,%arg0
|
||||
addi 5,%arg0,%arg1
|
||||
sh2add %arg0,%arg1,%arg0
|
||||
b _divI_pos
|
||||
addc %r0,%r0,%ret1
|
||||
|
||||
_divI_15:
|
||||
comb,< %arg0,%r0,_divI_neg15
|
||||
copy %r0,%ret1
|
||||
addibf 1,%arg0,_divI_pos_for_15
|
||||
shd %ret1,%arg0,28,%arg1
|
||||
_divI_neg15:
|
||||
b _divI_neg
|
||||
subi 1,%arg0,%arg0
|
||||
_divU_15:
|
||||
addi 1,%arg0,%arg0
|
||||
b _divI_pos
|
||||
addc %r0,%r0,%ret1
|
||||
|
||||
_divI_7:
|
||||
comb,<,n %arg0,%r0,_divI_neg7
|
||||
_divI_7a:
|
||||
addi 1,%arg0,%arg0
|
||||
shd %r0,%arg0,29,%ret1
|
||||
sh3add %arg0,%arg0,%arg0
|
||||
addc %ret1,%r0,%ret1
|
||||
_divI_pos7:
|
||||
shd %ret1,%arg0,26,%arg1
|
||||
shd %arg0,%r0,26,%r1
|
||||
add %arg0,%r1,%arg0
|
||||
addc %ret1,%arg1,%ret1
|
||||
shd %ret1,%arg0,20,%arg1
|
||||
shd %arg0,%r0,20,%r1
|
||||
add %arg0,%r1,%arg0
|
||||
addc %ret1,%arg1,%arg1
|
||||
copy %r0,%ret1
|
||||
shd,= %arg1,%arg0,24,%arg1
|
||||
L1: addbf %arg1,%ret1,L2
|
||||
extru %arg0,31,24,%arg0
|
||||
bv,n %r0(%r31)
|
||||
L2: addbf %arg1,%arg0,L1
|
||||
extru,= %arg0,7,8,%arg1
|
||||
|
||||
_divI_neg7:
|
||||
subi 1,%arg0,%arg0
|
||||
_divI_neg7a:
|
||||
shd %r0,%arg0,29,%ret1
|
||||
sh3add %arg0,%arg0,%arg0
|
||||
addc %ret1,%r0,%ret1
|
||||
_divI_neg7_shift:
|
||||
shd %ret1,%arg0,26,%arg1
|
||||
shd %arg0,%r0,26,%r1
|
||||
add %arg0,%r1,%arg0
|
||||
addc %ret1,%arg1,%ret1
|
||||
shd %ret1,%arg0,20,%arg1
|
||||
shd %arg0,%r0,20,%r1
|
||||
add %arg0,%r1,%arg0
|
||||
addc %ret1,%arg1,%arg1
|
||||
copy %r0,%ret1
|
||||
shd,= %arg1,%arg0,24,%arg1
|
||||
L3: addbf %arg1,%ret1,L4
|
||||
extru %arg0,31,24,%arg0
|
||||
bv %r0(%r31)
|
||||
sub %r0,%ret1,%ret1
|
||||
L4: addbf %arg1,%arg0,L3
|
||||
extru,= %arg0,7,8,%arg1
|
||||
|
||||
_divU_7:
|
||||
addi 1,%arg0,%arg0
|
||||
addc %r0,%r0,%ret1
|
||||
shd %ret1,%arg0,29,%arg1
|
||||
sh3add %arg0,%arg0,%arg0
|
||||
b _divI_pos7
|
||||
addc %arg1,%ret1,%ret1
|
||||
|
||||
_divI_9:
|
||||
comb,<,n %arg0,%r0,_divI_neg9
|
||||
addi 1,%arg0,%arg0
|
||||
shd %r0,%arg0,29,%arg1
|
||||
shd %arg0,%r0,29,%r1
|
||||
sub %r1,%arg0,%arg0
|
||||
b _divI_pos7
|
||||
subb %arg1,%r0,%ret1
|
||||
_divI_neg9:
|
||||
subi 1,%arg0,%arg0
|
||||
shd %r0,%arg0,29,%arg1
|
||||
shd %arg0,%r0,29,%r1
|
||||
sub %r1,%arg0,%arg0
|
||||
b _divI_neg7_shift
|
||||
subb %arg1,%r0,%ret1
|
||||
_divU_9:
|
||||
addi 1,%arg0,%arg0
|
||||
addc %r0,%r0,%ret1
|
||||
shd %ret1,%arg0,29,%arg1
|
||||
shd %arg0,%r0,29,%r1
|
||||
sub %r1,%arg0,%arg0
|
||||
b _divI_pos7
|
||||
subb %arg1,%ret1,%ret1
|
||||
|
||||
_divI_14:
|
||||
comb,<,n %arg0,%r0,_divI_neg14
|
||||
_divU_14:
|
||||
b _divI_7a
|
||||
extru %arg0,30,31,%arg0
|
||||
_divI_neg14:
|
||||
subi 2,%arg0,%arg0
|
||||
b _divI_neg7a
|
||||
extru %arg0,30,31,%arg0
|
||||
|
||||
_end:
|
|
@ -0,0 +1,677 @@
|
|||
/* -*- 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.
|
||||
*/
|
||||
|
||||
#include "Fundamentals.h"
|
||||
#include "HPPAInstruction.h"
|
||||
#include "HPPAFuncs.h"
|
||||
#include "ControlNodes.h"
|
||||
|
||||
HPPAInstructionInfo hiInfo[nHPPAInstructionKind] =
|
||||
{
|
||||
{ ADD, 0x02, 0x18, 6, "add", HPPA_NONE }, // *****
|
||||
{ ADDBF, 0xff, 0x00, 0, "addbf", HPPA_UNIMP },
|
||||
{ ADDBT, 0xff, 0x00, 0, "addbt", HPPA_UNIMP },
|
||||
{ ADDO, 0xff, 0x00, 0, "addo", HPPA_UNIMP },
|
||||
{ ADDIBF, 0xff, 0x00, 0, "addibf", HPPA_UNIMP },
|
||||
{ ADDIBT, 0xff, 0x00, 0, "addibt", HPPA_UNIMP },
|
||||
{ ADDIL, 0xff, 0x00, 0, "addil", HPPA_UNIMP },
|
||||
{ ADDL, 0x02, 0x28, 6, "addl", HPPA_NONE }, // *****
|
||||
{ ADDI, 0xff, 0x00, 0, "addi", HPPA_UNIMP },
|
||||
{ ADDIT, 0xff, 0x00, 0, "addit", HPPA_UNIMP },
|
||||
{ ADDITO, 0xff, 0x00, 0, "addito", HPPA_UNIMP },
|
||||
{ ADDC, 0xff, 0x00, 0, "addc", HPPA_UNIMP },
|
||||
{ ADDCO, 0xff, 0x00, 0, "addco", HPPA_UNIMP },
|
||||
{ AND, 0x02, 0x08, 6, "and", HPPA_NONE }, // *****
|
||||
{ ANDCM, 0xff, 0x00, 0, "andcm", HPPA_UNIMP },
|
||||
{ BL, 0x3a, 0x00, 13, "bl", HPPA_NONE }, // *****
|
||||
{ BLE, 0xff, 0x00, 0, "ble", HPPA_UNIMP },
|
||||
{ BLR, 0xff, 0x00, 0, "blr", HPPA_UNIMP },
|
||||
{ BE, 0xff, 0x00, 0, "be", HPPA_UNIMP },
|
||||
{ BB, 0xff, 0x00, 0, "bb", HPPA_UNIMP },
|
||||
{ BVB, 0xff, 0x00, 0, "bvb", HPPA_UNIMP },
|
||||
{ BV, 0xff, 0x00, 0, "bv", HPPA_UNIMP },
|
||||
{ BREAK, 0xff, 0x00, 0, "break", HPPA_UNIMP },
|
||||
{ COMBF, 0x22, 0x00, 11, "combf", HPPA_NONE }, // *****
|
||||
{ COMBT, 0x20, 0x00, 11, "combt", HPPA_NONE }, // *****
|
||||
{ COMCLR, 0xff, 0x00, 0, "comclr", HPPA_UNIMP },
|
||||
{ COMIBF, 0x23, 0x00, 0, "comibf", HPPA_DATA_HAS_IM5 }, // *****
|
||||
{ COMIBT, 0x21, 0x00, 11, "comibt", HPPA_DATA_HAS_IM5 }, // *****
|
||||
{ COMICLR, 0xff, 0x00, 0, "comiclr", HPPA_UNIMP },
|
||||
{ CLDDX, 0xff, 0x00, 0, "clddx", HPPA_UNIMP },
|
||||
{ CLDDS, 0xff, 0x00, 0, "cldds", HPPA_UNIMP },
|
||||
{ CLDWX, 0xff, 0x00, 0, "cldwx", HPPA_UNIMP },
|
||||
{ CLDWS, 0xff, 0x00, 0, "cldws", HPPA_UNIMP },
|
||||
{ COPR, 0xff, 0x00, 0, "copr", HPPA_UNIMP },
|
||||
{ CSTDX, 0xff, 0x00, 0, "cstdx", HPPA_UNIMP },
|
||||
{ CSTDS, 0xff, 0x00, 0, "cstds", HPPA_UNIMP },
|
||||
{ CSTWX, 0xff, 0x00, 0, "cstwx", HPPA_UNIMP },
|
||||
{ CSTWS, 0xff, 0x00, 0, "cstws", HPPA_UNIMP },
|
||||
{ COPY, 0x02, 0x09, 6, "copy", HPPA_R1_IS_ZERO | HPPA_IS_COPY }, // *****
|
||||
{ DCOR, 0xff, 0x00, 0, "dcor", HPPA_UNIMP },
|
||||
{ DEP, 0xff, 0x00, 0, "dep", HPPA_UNIMP },
|
||||
{ DEPI, 0x35, 0x07, 9, "depi", HPPA_DATA_HAS_IM5 }, // *****
|
||||
{ DIAG, 0xff, 0x00, 0, "diag", HPPA_UNIMP },
|
||||
{ DS, 0xff, 0x00, 0, "ds", HPPA_UNIMP },
|
||||
{ XOR, 0x02, 0x0a, 6, "xor", HPPA_NONE }, // ****
|
||||
{ EXTRS, 0x34, 0x07, 8, "extrs", HPPA_NONE }, // *****
|
||||
{ EXTRU, 0x34, 0x06, 8, "extru", HPPA_NONE }, // *****
|
||||
{ XMPYU, 0x0e, 0x00, 40, "xmpyu", HPPA_NONE },
|
||||
{ FABS, 0xff, 0x00, 0, "fabs", HPPA_UNIMP },
|
||||
{ FCMP, 0xff, 0x00, 0, "fcmp", HPPA_UNIMP },
|
||||
{ FCNVXF, 0xff, 0x00, 0, "fcnvxf", HPPA_UNIMP },
|
||||
{ FCNVFX, 0xff, 0x00, 0, "fcnvfx", HPPA_UNIMP },
|
||||
{ FCNVFXT, 0xff, 0x00, 0, "fcnvfxt", HPPA_UNIMP },
|
||||
{ FCNVFF, 0xff, 0x00, 0, "fcnvff", HPPA_UNIMP },
|
||||
{ FDIV, 0xff, 0x00, 0, "fdiv", HPPA_UNIMP },
|
||||
{ FLDDX, 0xff, 0x00, 0, "flddx", HPPA_UNIMP },
|
||||
{ FLDDS, 0xff, 0x00, 0, "fldds", HPPA_UNIMP },
|
||||
{ FLDWX, 0xff, 0x00, 0, "fldwx", HPPA_UNIMP },
|
||||
{ FLDWS, 0x09, 0x00, 31, "fldws", HPPA_DATA_HAS_IM5 }, // *****
|
||||
{ FMPY, 0xff, 0x00, 0, "fmpy", HPPA_UNIMP },
|
||||
{ FMPYADD, 0xff, 0x00, 0, "fmpyadd", HPPA_UNIMP },
|
||||
{ FMPYSUB, 0xff, 0x00, 0, "fmpysub", HPPA_UNIMP },
|
||||
{ FRND, 0xff, 0x00, 0, "frnd", HPPA_UNIMP },
|
||||
{ FSQRT, 0xff, 0x00, 0, "fsqrt", HPPA_UNIMP },
|
||||
{ FSTDX, 0xff, 0x00, 0, "fstdx", HPPA_UNIMP },
|
||||
{ FSTDS, 0xff, 0x00, 0, "fstds", HPPA_UNIMP },
|
||||
{ FSTWS, 0x09, 0x00, 32, "fstws", HPPA_DATA_HAS_IM5 }, // *****
|
||||
{ FSUB, 0xff, 0x00, 0, "fsub", HPPA_UNIMP },
|
||||
{ FTEST, 0xff, 0x00, 0, "ftest", HPPA_UNIMP },
|
||||
{ FDC, 0xff, 0x00, 0, "fdc", HPPA_UNIMP },
|
||||
{ FDCE, 0xff, 0x00, 0, "fdce", HPPA_UNIMP },
|
||||
{ FIC, 0xff, 0x00, 0, "fic", HPPA_UNIMP },
|
||||
{ FICE, 0xff, 0x00, 0, "fice", HPPA_UNIMP },
|
||||
{ GATE, 0xff, 0x00, 0, "gate", HPPA_UNIMP },
|
||||
{ DEBUGID, 0xff, 0x00, 0, "debugid", HPPA_UNIMP },
|
||||
{ OR, 0x02, 0x09, 6, "or", HPPA_NONE }, // *****
|
||||
{ IDTLBA, 0xff, 0x00, 0, "idtlba", HPPA_UNIMP },
|
||||
{ IDTLBP, 0xff, 0x00, 0, "idtlbp", HPPA_UNIMP },
|
||||
{ IITLBA, 0xff, 0x00, 0, "iitlba", HPPA_UNIMP },
|
||||
{ IITLBP, 0xff, 0x00, 0, "iitlbp", HPPA_UNIMP },
|
||||
{ IDCOR, 0xff, 0x00, 0, "idcor", HPPA_UNIMP },
|
||||
{ LDCWX, 0xff, 0x00, 0, "ldcwx", HPPA_UNIMP },
|
||||
{ LDCWS, 0xff, 0x00, 0, "ldcws", HPPA_UNIMP },
|
||||
{ LDB, 0xff, 0x00, 0, "ldb", HPPA_UNIMP },
|
||||
{ LDBX, 0xff, 0x00, 0, "ldbx", HPPA_UNIMP },
|
||||
{ LDBS, 0xff, 0x00, 0, "ldbs", HPPA_UNIMP },
|
||||
{ LCI, 0xff, 0x00, 0, "lci", HPPA_UNIMP },
|
||||
{ LDH, 0xff, 0x00, 0, "ldh", HPPA_UNIMP },
|
||||
{ LDHX, 0xff, 0x00, 0, "ldhx", HPPA_UNIMP },
|
||||
{ LDHS, 0xff, 0x00, 0, "ldhs", HPPA_UNIMP },
|
||||
{ LDI, 0x0d, 0x00, 1, "ldi", HPPA_DATA_HAS_IM14 | HPPA_R1_IS_ZERO }, // *****
|
||||
{ LDIL, 0x08, 0x00, 5, "ldil", HPPA_DATA_HAS_IM21 }, // *****
|
||||
{ LDO, 0x0d, 0x00, 1, "ldo", HPPA_DATA_HAS_IM14 }, // *****
|
||||
{ LPA, 0xff, 0x00, 0, "lpa", HPPA_UNIMP },
|
||||
{ LDSID, 0xff, 0x00, 0, "ldsid", HPPA_UNIMP },
|
||||
{ LDW, 0x12, 0x00, 1, "ldw", HPPA_DATA_HAS_IM14 }, // *****
|
||||
{ LDWAX, 0xff, 0x00, 0, "ldwax", HPPA_UNIMP },
|
||||
{ LDWAS, 0xff, 0x00, 0, "ldwas", HPPA_UNIMP },
|
||||
{ LDWM, 0xff, 0x00, 0, "ldwm", HPPA_UNIMP },
|
||||
{ LDWX, 0xff, 0x00, 0, "ldwx", HPPA_UNIMP },
|
||||
{ LDWS, 0xff, 0x00, 0, "ldws", HPPA_UNIMP },
|
||||
{ MOVB, 0xff, 0x00, 0, "movb", HPPA_UNIMP },
|
||||
{ MFCTL, 0xff, 0x00, 0, "mfctl", HPPA_UNIMP },
|
||||
{ MFDBAM, 0xff, 0x00, 0, "mfdbam", HPPA_UNIMP },
|
||||
{ MFDBAO, 0xff, 0x00, 0, "mfdbao", HPPA_UNIMP },
|
||||
{ MFIBAM, 0xff, 0x00, 0, "mfibam", HPPA_UNIMP },
|
||||
{ MFIBAO, 0xff, 0x00, 0, "mfibao", HPPA_UNIMP },
|
||||
{ MFSP, 0xff, 0x00, 0, "mfsp", HPPA_UNIMP },
|
||||
{ MOVIB, 0xff, 0x00, 0, "movib", HPPA_UNIMP },
|
||||
{ MCTL, 0xff, 0x00, 0, "mctl", HPPA_UNIMP },
|
||||
{ MTBAM, 0xff, 0x00, 0, "mtbam", HPPA_UNIMP },
|
||||
{ MTBAO, 0xff, 0x00, 0, "mtbao", HPPA_UNIMP },
|
||||
{ MTIBAM, 0xff, 0x00, 0, "mtibam", HPPA_UNIMP },
|
||||
{ MTIBAO, 0xff, 0x00, 0, "mtibao", HPPA_UNIMP },
|
||||
{ MTSP, 0xff, 0x00, 0, "mtsp", HPPA_UNIMP },
|
||||
{ MTSM, 0xff, 0x00, 0, "mtsm", HPPA_UNIMP },
|
||||
{ PMDIS, 0xff, 0x00, 0, "pmdis", HPPA_UNIMP },
|
||||
{ PMENB, 0xff, 0x00, 0, "pmenb", HPPA_UNIMP },
|
||||
{ PROBER, 0xff, 0x00, 0, "prober", HPPA_UNIMP },
|
||||
{ PROBERI, 0xff, 0x00, 0, "proberi", HPPA_UNIMP },
|
||||
{ PROBEW, 0xff, 0x00, 0, "probew", HPPA_UNIMP },
|
||||
{ PROBEWI, 0xff, 0x00, 0, "probewi", HPPA_UNIMP },
|
||||
{ PDC, 0xff, 0x00, 0, "pdc", HPPA_UNIMP },
|
||||
{ PDTLB, 0xff, 0x00, 0, "pdtlb", HPPA_UNIMP },
|
||||
{ PDTLBE, 0xff, 0x00, 0, "pdtlbe", HPPA_UNIMP },
|
||||
{ PITLB, 0xff, 0x00, 0, "pitlb", HPPA_UNIMP },
|
||||
{ PITLBE, 0xff, 0x00, 0, "pitlbe", HPPA_UNIMP },
|
||||
{ RSM, 0xff, 0x00, 0, "rsm", HPPA_UNIMP },
|
||||
{ RFI, 0xff, 0x00, 0, "rfi", HPPA_UNIMP },
|
||||
{ RFIR, 0xff, 0x00, 0, "rfir", HPPA_UNIMP },
|
||||
{ SSM, 0xff, 0x00, 0, "ssm", HPPA_UNIMP },
|
||||
{ SHD, 0xff, 0x00, 0, "shd", HPPA_UNIMP },
|
||||
{ SH1ADD, 0xff, 0x00, 0, "sh1add", HPPA_UNIMP },
|
||||
{ SH1ADDL, 0x02, 0x29, 6, "sh1addl", HPPA_NONE }, // *****
|
||||
{ SH1ADDO, 0xff, 0x00, 0, "sh1addo", HPPA_UNIMP },
|
||||
{ SH2ADD, 0xff, 0x00, 0, "sh2add", HPPA_UNIMP },
|
||||
{ SH2ADDL, 0x02, 0x2a, 6, "sh2addl", HPPA_NONE }, // *****
|
||||
{ SH2ADDO, 0xff, 0x00, 0, "sh2addo", HPPA_UNIMP },
|
||||
{ SH3ADD, 0xff, 0x00, 0, "sh3add", HPPA_UNIMP },
|
||||
{ SH3ADDL, 0x02, 0x2b, 6, "sh3addl", HPPA_NONE }, // *****
|
||||
{ SH3ADDO, 0xff, 0x00, 0, "sh3addo", HPPA_UNIMP },
|
||||
{ SPOP0, 0xff, 0x00, 0, "spop0", HPPA_UNIMP },
|
||||
{ SPOP1, 0xff, 0x00, 0, "spop1", HPPA_UNIMP },
|
||||
{ SPOP2, 0xff, 0x00, 0, "spop2", HPPA_UNIMP },
|
||||
{ SPOP3, 0xff, 0x00, 0, "spop3", HPPA_UNIMP },
|
||||
{ STB, 0xff, 0x00, 0, "stb", HPPA_UNIMP },
|
||||
{ STBS, 0xff, 0x00, 0, "stbs", HPPA_UNIMP },
|
||||
{ STBYS, 0xff, 0x00, 0, "stbys", HPPA_UNIMP },
|
||||
{ STH, 0xff, 0x00, 0, "sth", HPPA_UNIMP },
|
||||
{ STHS, 0xff, 0x00, 0, "sths", HPPA_UNIMP },
|
||||
{ STW, 0x1a, 0x00, 1, "stw", HPPA_DATA_HAS_IM14 }, // *****
|
||||
{ STWAS, 0xff, 0x00, 0, "stwas", HPPA_UNIMP },
|
||||
{ STWM, 0xff, 0x00, 0, "stwm", HPPA_UNIMP },
|
||||
{ STWS, 0xff, 0x00, 0, "stws", HPPA_UNIMP },
|
||||
{ SUB, 0x02, 0x10, 6, "sub", HPPA_NONE }, // *****
|
||||
{ SUBT, 0xff, 0x00, 0, "subt", HPPA_UNIMP },
|
||||
{ SUBTO, 0xff, 0x00, 0, "subto", HPPA_UNIMP },
|
||||
{ SUBO, 0xff, 0x00, 0, "subo", HPPA_UNIMP },
|
||||
{ SUBI, 0xff, 0x00, 0, "subi", HPPA_UNIMP },
|
||||
{ SUBIO, 0xff, 0x00, 0, "subio", HPPA_UNIMP },
|
||||
{ SUBB, 0xff, 0x00, 0, "subb", HPPA_UNIMP },
|
||||
{ SUBBO, 0xff, 0x00, 0, "subbo", HPPA_UNIMP },
|
||||
{ SYNC, 0xff, 0x00, 0, "sync", HPPA_UNIMP },
|
||||
{ SYNCDMA, 0xff, 0x00, 0, "syncdma", HPPA_UNIMP },
|
||||
{ UADDCM, 0xff, 0x00, 0, "uaddcm", HPPA_UNIMP },
|
||||
{ UADDCMT, 0xff, 0x00, 0, "uaddcmt", HPPA_UNIMP },
|
||||
{ UXOR, 0xff, 0x00, 0, "uxor", HPPA_UNIMP },
|
||||
{ VDEP, 0xff, 0x00, 0, "vdep", HPPA_UNIMP },
|
||||
{ VDEPI, 0xff, 0x00, 0, "vdepi", HPPA_UNIMP },
|
||||
{ VEXTRU, 0xff, 0x00, 0, "vextru", HPPA_UNIMP },
|
||||
{ VSHD, 0xff, 0x00, 0, "vshd", HPPA_UNIMP },
|
||||
{ ZDEP, 0x35, 0x02, 9, "zdep", HPPA_R1_IS_ZERO }, // *****
|
||||
{ ZDEPI, 0xff, 0x00, 0, "zdepi", HPPA_UNIMP },
|
||||
{ ZVDEP, 0xff, 0x00, 0, "zvdep", HPPA_UNIMP },
|
||||
{ ZVDEPI, 0xff, 0x00, 0, "zvdepi", HPPA_UNIMP }
|
||||
};
|
||||
|
||||
HPPAConditionInfo hcInfo[nHPPAConditionKind] =
|
||||
{
|
||||
{ hcNever, hcNever, 0, false, "" },
|
||||
{ hcE, hcE, 1, false, "=" },
|
||||
{ hcL, hcGe, 2, false, "<" },
|
||||
{ hcLe, hcG, 3, false, "<=" },
|
||||
{ hcLu, hcGeu, 4, false, "<<" },
|
||||
{ hcLeu, hcGu, 5, false, "<<=" },
|
||||
{ hcOver, hcOver, 6, false, "SV" },
|
||||
{ hcOdd, hcOdd, 7, false, "OD" },
|
||||
{ hcAlways, hcAlways, 0, true, "TR" },
|
||||
{ hcNe, hcNe, 1, true, "<>" },
|
||||
{ hcGe, hcL, 2, true, ">=" },
|
||||
{ hcG, hcLe, 3, true, ">" },
|
||||
{ hcGeu, hcLu, 4, true, ">>=" },
|
||||
{ hcGu, hcLeu, 5, true, ">>" },
|
||||
{ hcNOver, hcNOver, 6, true, "NSV" },
|
||||
{ hcEven, hcEven, 7, true, "EV" }
|
||||
};
|
||||
|
||||
char* registerNumberToString[] =
|
||||
{
|
||||
// General registers
|
||||
"0", "%r1", "%rp", "%r3", "%r4", "%r5", "%r6", "%r7",
|
||||
"%r8", "%r9", "%r10", "%r11", "%r12", "%r13", "%r14", "%r15",
|
||||
"%r16", "%r17", "%r18", "%r19", "%r20", "%r21", "%r22", "%arg3",
|
||||
"%arg2", "%arg1", "%arg0", "%dp", "%ret0", "%ret1", "%sp", "%r31",
|
||||
// Floating point registers
|
||||
"%fr0L", "%fr0R", "%fr1L", "%fr1R", "%fr2L", "%fr2R", "%fr3L", "%fr3R",
|
||||
"%fr4L", "%fr4R", "%fr5L", "%fr5R", "%fr6L", "%fr6R", "%fr7L", "%fr7R",
|
||||
"%fr8L", "%fr8R", "%fr9L", "%fr9R", "%fr10L", "%fr10R", "%fr11L", "%fr11R",
|
||||
"%fr12L", "%fr12R", "%fr13L", "%fr13R", "%fr14L", "%fr14R", "%fr15L", "%fr15R",
|
||||
"%fr16L", "%fr16R", "%fr17L", "%fr17R", "%fr18L", "%fr18R", "%fr19L", "%fr19R",
|
||||
"%fr20L", "%fr20R", "%fr21L", "%fr21R", "%fr22L", "%fr22R", "%fr23L", "%fr23R",
|
||||
"%fr24L", "%fr24R", "%fr25L", "%fr25R", "%fr26L", "%fr26R", "%fr27L", "%fr27R",
|
||||
"%fr28L", "%fr28R", "%fr29L", "%fr29R", "%fr30L", "%fr30R", "%fr31L", "%fr31R",
|
||||
// Other strings
|
||||
"%fr0", "%fr1", "%fr2", "%fr3", "%fr4", "%fr5", "%fr6", "%fr7",
|
||||
"%fr8", "%fr9", "%fr10", "%fr11", "%fr12", "%fr13", "%fr14", "%fr15",
|
||||
"%fr16", "%fr17", "%fr18", "%fr19", "%fr20", "%fr21", "%fr22", "%fr23",
|
||||
"%fr24", "%fr25", "%fr26", "%fr27", "%fr28", "%fr29", "%fr30", "%fr31",
|
||||
};
|
||||
|
||||
// available caller saved registers:
|
||||
// %r19, %r20, %r21, %r22, %arg3, %arg2, %arg1, %arg0, %ret0, %ret1, %r31
|
||||
// available callee saved registers:
|
||||
// %r4, %r5, %r6, %r7, %r8, %r9, %r10, %r11, %r12, %r13, %r14, %r15, %r16, %r17, %r18
|
||||
|
||||
PRUint8 registerNumberToColor[] =
|
||||
{
|
||||
79, 77, 12, 13, 14, 15, 16, 17,
|
||||
18, 19, 20, 21, 22, 23, 24, 25,
|
||||
26, 27, 28, 0, 1, 2, 3, 4,
|
||||
5, 6, 7, 8, 9, 10, 78, 11
|
||||
};
|
||||
|
||||
HPPARegisterNumber colorToRegisterNumber[] =
|
||||
{
|
||||
// caller saved GR
|
||||
/*0*/ r19, r20, r21, r22, arg3, arg2, arg1, arg0,
|
||||
/*8*/ dp, ret0, ret1, r31, rp,
|
||||
// callee saved GR
|
||||
/*13*/ r3, r4, r5, r6, r7, r8, r9, r10,
|
||||
/*21*/ r11, r12, r13, r14, r15, r16, r17, r18,
|
||||
// caller saved FPR
|
||||
/*29*/ fr8L, fr8R, fr9L, fr9R, fr10L, fr10R, fr11L, fr11R,
|
||||
/*37*/ fr22L, fr22R, fr23L, fr23R, fr24L, fr24R, fr25L, fr25R,
|
||||
/*45*/ fr26L, fr26R, fr27L, fr27R, fr28L, fr28R, fr29L, fr29R,
|
||||
/*53*/ fr30L, fr30R, fr31L, fr31R,
|
||||
// callee saved FPR
|
||||
/*57*/ fr12L, fr12R, fr13L, fr13R, fr14L, fr14R, fr15L, fr15R,
|
||||
/*65*/ fr16L, fr16R, fr17L, fr17R, fr18L, fr18R, fr19L, fr19R,
|
||||
/*73*/ fr20L, fr20R, fr21L, fr21R,
|
||||
// special registers
|
||||
/*77*/ r1, sp, zero
|
||||
};
|
||||
|
||||
#define COLOR_MASK1(regNum) (1 << registerNumberToColor[regNum])
|
||||
#define COLOR_MASK2(regNum) (1 << (registerNumberToColor[regNum] - 32))
|
||||
#define COLOR_MASK3(regNum) (1 << (registerNumberToColor[regNum] - 64))
|
||||
|
||||
HPPASpecialCallInfo hscInfo[nSpecialCalls] =
|
||||
{
|
||||
{ RemI, "$$remI", 2, 1, 0, 0, COLOR_MASK3(r1), HPPAFuncAddress(HPPAremI) }, // inter with r1
|
||||
{ RemU, "$$remU", 2, 1, 0, 0, COLOR_MASK3(r1), HPPAFuncAddress(HPPAremU) }, // inter with r1
|
||||
{ DivI, "$$divI", 2, 1, 0, 0, COLOR_MASK3(r1), HPPAFuncAddress(HPPAdivI) }, // inter with r1
|
||||
{ DivU, "$$divU", 2, 1, 0, 0, COLOR_MASK3(r1), HPPAFuncAddress(HPPAdivU) }, // inter with r1
|
||||
};
|
||||
|
||||
HPPAInstructionKind shiftAddl[4] =
|
||||
{
|
||||
INVALID, SH1ADDL, SH2ADDL, SH3ADDL
|
||||
};
|
||||
|
||||
|
||||
static inline PRUint32
|
||||
dis_assemble_3(PRUint32 x)
|
||||
{
|
||||
return (((x & 4) >> 2) | ((x & 3) << 1)) & 7;
|
||||
}
|
||||
|
||||
static inline void
|
||||
dis_assemble_12(PRUint32 as12, PRUint32& x, PRUint32& y)
|
||||
{
|
||||
y = (as12 & 0x800) >> 11;
|
||||
x = ((as12 & 0x3ff) << 1) | ((as12 & 0x400) >> 10);
|
||||
}
|
||||
|
||||
static inline void
|
||||
dis_assemble_17(PRUint32 as17, PRUint32& x, PRUint32& y, PRUint32& z)
|
||||
{
|
||||
z = (as17 & 0x10000) >> 16;
|
||||
x = (as17 & 0x0f800) >> 11;
|
||||
y = (((as17 & 0x00400) >> 10) | ((as17 & 0x3ff) << 1)) & 0x7ff;
|
||||
}
|
||||
|
||||
static inline PRUint32
|
||||
dis_assemble_21 (PRUint32 as21)
|
||||
{
|
||||
PRUint32 temp;
|
||||
|
||||
temp = (as21 & 0x100000) >> 20;
|
||||
temp |= (as21 & 0x0ffe00) >> 8;
|
||||
temp |= (as21 & 0x000180) << 7;
|
||||
temp |= (as21 & 0x00007c) << 14;
|
||||
temp |= (as21 & 0x000003) << 12;
|
||||
|
||||
return temp;
|
||||
}
|
||||
|
||||
static inline PRUint16
|
||||
low_sign_unext_14(PRUint16 x)
|
||||
{
|
||||
return ((x & 0x1fff) << 1) | ((x & 0x2000) ? 1 : 0);
|
||||
}
|
||||
|
||||
static inline PRUint8
|
||||
low_sign_unext_5(PRUint8 x)
|
||||
{
|
||||
return ((x & 0xf) << 1) | ((x & 0x10) ? 1 : 0);
|
||||
}
|
||||
|
||||
// HPPAInstruction methods
|
||||
|
||||
#if defined(DEBUG)
|
||||
void HPPAInstruction::
|
||||
printPretty(FILE* f)
|
||||
{
|
||||
fprintf(f, "unimp(%s)", hiInfo[kind].string);
|
||||
}
|
||||
#endif
|
||||
|
||||
HPPARegisterNumber HPPAInstruction::
|
||||
getR1()
|
||||
{
|
||||
InstructionUse* use = getInstructionUseBegin();
|
||||
InstructionUse* limit = getInstructionUseEnd();
|
||||
|
||||
while (use < limit && !use->isVirtualRegister()) use++;
|
||||
|
||||
if (use >= limit)
|
||||
return zero;
|
||||
else
|
||||
return udToRegisterNumber(use[0]);
|
||||
}
|
||||
|
||||
HPPARegisterNumber HPPAInstruction::
|
||||
getR2()
|
||||
{
|
||||
InstructionUse* use = getInstructionUseBegin();
|
||||
InstructionUse* limit = getInstructionUseEnd();
|
||||
|
||||
while (use < limit && !use->isVirtualRegister()) use++;
|
||||
|
||||
if (use < &limit[-1] && use[1].isVirtualRegister())
|
||||
use++; // skip R1
|
||||
|
||||
if (use >= limit || !use->isVirtualRegister())
|
||||
return zero;
|
||||
else
|
||||
return udToRegisterNumber(use[0]);
|
||||
}
|
||||
|
||||
HPPARegisterNumber HPPAInstruction::
|
||||
getT()
|
||||
{
|
||||
InstructionDefine* def = getInstructionDefineBegin();
|
||||
InstructionDefine* limit = getInstructionDefineEnd();
|
||||
|
||||
while (def < limit && !def->isVirtualRegister()) def++;
|
||||
|
||||
if (def >= limit)
|
||||
return zero;
|
||||
else
|
||||
return udToRegisterNumber(def[0]);
|
||||
}
|
||||
|
||||
// HPPAFormat1 methods
|
||||
// |31 |25 |20 |15 |13 0|
|
||||
// |-----------------|--------------|--------------|-----|-----------------------------------------|
|
||||
// | op | b | t/r | s | im14 |
|
||||
// |-----------------|--------------|--------------|-----|-----------------------------------------|
|
||||
|
||||
void HPPAFormat1::
|
||||
formatToMemory(void* where, uint32 /*offset*/)
|
||||
{
|
||||
*(PRUint32 *)where = (hiInfo[kind].opcode << 26) | (getR1() << 21) | (getT() << 16) | low_sign_unext_14(getIm14());
|
||||
}
|
||||
|
||||
#if defined(DEBUG)
|
||||
void HPPAFormat1::
|
||||
printPretty(FILE* f)
|
||||
{
|
||||
if (flags & HPPA_R1_IS_ZERO)
|
||||
fprintf(f, "%s %d,%s", hiInfo[kind].string, (PRInt16) getIm14(), getTString());
|
||||
else
|
||||
fprintf(f, "%s %d(%s),%s", hiInfo[kind].string, (PRInt16) getIm14(), getR1String(), getTString());
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(DEBUG)
|
||||
void HPPALoad::
|
||||
printPretty(FILE* f)
|
||||
{
|
||||
fprintf(f, "%s %d(0,%s),%s", hiInfo[kind].string, (PRInt16) getIm14(), getR1String(), getTString());
|
||||
}
|
||||
#endif
|
||||
|
||||
void HPPAStore::
|
||||
formatToMemory(void* where, uint32 /*offset*/)
|
||||
{
|
||||
*(PRUint32 *)where = (hiInfo[kind].opcode << 26) | (getR1() << 21) | (getR2() << 16) | low_sign_unext_14(getIm14());
|
||||
}
|
||||
|
||||
#if defined(DEBUG)
|
||||
void HPPAStore::
|
||||
printPretty(FILE* f)
|
||||
{
|
||||
fprintf(f, "%s %s,%d(0,%s)", hiInfo[kind].string, getR2String(), (PRInt16) getIm14(), getR1String());
|
||||
}
|
||||
#endif
|
||||
|
||||
// HPPAFormat5 methods
|
||||
// |31 |25 |20 0|
|
||||
// |-----------------|--------------|--------------------------------------------------------------|
|
||||
// | op | t/r | im21 |
|
||||
// |-----------------|--------------|--------------------------------------------------------------|
|
||||
|
||||
void HPPAFormat5::
|
||||
formatToMemory(void* where, uint32 /*offset*/)
|
||||
{
|
||||
*(PRUint32 *)where = (hiInfo[kind].opcode << 26) | (getT() << 21) | dis_assemble_21(IM21(im21));
|
||||
}
|
||||
|
||||
#if defined(DEBUG)
|
||||
void HPPAFormat5::
|
||||
printPretty(FILE* f)
|
||||
{
|
||||
fprintf(f, "%s %d,%s", hiInfo[kind].string, IM21(im21), getTString());
|
||||
}
|
||||
#endif
|
||||
|
||||
// HPPAFormat6 methods
|
||||
// |31 |25 |20 |15 |12|11 |5 |4 0|
|
||||
// |-----------------|--------------|--------------|--------|--|-----------------|--|--------------|
|
||||
// | op | r2 | r1 | c | f| ext6 |0 | t |
|
||||
// |-----------------|--------------|--------------|--------|--|-----------------|--|--------------|
|
||||
|
||||
void HPPAFormat6::
|
||||
formatToMemory(void* where, uint32 /*offset*/)
|
||||
{
|
||||
*(PRUint32 *)where = (hiInfo[kind].opcode << 26) | (((flags & HPPA_R1_IS_ZERO) ? 0 : getR2()) << 21) | (getR1() << 16) | (hiInfo[kind].ext << 6) | getT();
|
||||
}
|
||||
|
||||
#if defined(DEBUG)
|
||||
void HPPAFormat6::
|
||||
printPretty(FILE* f)
|
||||
{
|
||||
if (flags & HPPA_IS_COPY)
|
||||
fprintf(f, "%s %s,%s", hiInfo[kind].string, getR1String(), getTString());
|
||||
else if (flags & HPPA_R1_IS_ZERO)
|
||||
fprintf(f, "%s 0,%s,%s", hiInfo[kind].string, getR1String(), getTString());
|
||||
else
|
||||
fprintf(f, "%s %s,%s,%s", hiInfo[kind].string, getR1String(), getR2String(), getTString());
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
// HPPAFormat8 methods
|
||||
// |31 |25 |20 |15 |12 |9 |4 0|
|
||||
// |-----------------|--------------|--------------|--------|--------|--------------|--------------|
|
||||
// | op | r | t | c | ext3 | p | clen |
|
||||
// |-----------------|--------------|--------------|--------|--------|--------------|--------------|
|
||||
|
||||
void HPPAFormat8::
|
||||
formatToMemory(void* where, uint32 /*offset*/)
|
||||
{
|
||||
*(PRUint32 *)where = (hiInfo[kind].opcode << 26) | (getR1() << 21) | (getT() << 16) | (hiInfo[kind].ext << 10) | (cp << 5) | (32 - clen);
|
||||
}
|
||||
|
||||
#if defined(DEBUG)
|
||||
void HPPAFormat8::
|
||||
printPretty(FILE* f)
|
||||
{
|
||||
fprintf(f, "%s %s,%d,%d,%s", hiInfo[kind].string, getR1String(), cp, clen, getTString());
|
||||
}
|
||||
#endif
|
||||
|
||||
// HPPAFormat9 methods
|
||||
// |31 |25 |20 |15 |12 |9 |4 0|
|
||||
// |-----------------|--------------|--------------|--------|--------|--------------|--------------|
|
||||
// | op | t | r/im5 | c | ext3 | cp | clen |
|
||||
// |-----------------|--------------|--------------|--------|--------|--------------|--------------|
|
||||
|
||||
void HPPAFormat9::
|
||||
formatToMemory(void* where, uint32 /*offset*/)
|
||||
{
|
||||
*(PRUint32 *)where = (hiInfo[kind].opcode << 26) | (getT() << 21) | ((validIM5(im5) ? low_sign_unext_5(im5) : (PRUint8) getR1()) << 16) |
|
||||
(hiInfo[kind].ext << 10) | ((31 - cp) << 5) | (32 - clen);
|
||||
}
|
||||
|
||||
#if defined(DEBUG)
|
||||
void HPPAFormat9::
|
||||
printPretty(FILE* f)
|
||||
{
|
||||
if (validIM5(im5))
|
||||
fprintf(f, "%s %d,%d,%d,%s", hiInfo[kind].string, IM5(im5), cp, clen, getTString());
|
||||
else
|
||||
fprintf(f, "%s %s,%d,%d,%s", hiInfo[kind].string, getR1String(), cp, clen, getTString());
|
||||
}
|
||||
#endif
|
||||
|
||||
// HPPAFormat11 methods
|
||||
// |31 |25 |20 |15 |12 | 1| 0|
|
||||
// |-----------------|--------------|--------------|--------|--------------------------------|--|--|
|
||||
// | op | r2/p | r1/im5 | c | w1 | n| w|
|
||||
// |-----------------|--------------|--------------|--------|--------------------------------|--|--|
|
||||
|
||||
void HPPAFormat11::
|
||||
formatToMemory(void* where, uint32 offset)
|
||||
{
|
||||
PRUint32 w1, w;
|
||||
|
||||
dis_assemble_12((target.getNativeOffset() - (offset + 8)) >> 2, w1, w);
|
||||
|
||||
((PRUint32 *)where)[0] = (hiInfo[kind].opcode << 26) | (getR2() << 21) | ((validIM5(im5) ? low_sign_unext_5(im5) : (PRUint8) getR1()) << 16) |
|
||||
(hcInfo[cond].c << 13) | (w1 << 2) | ((nullification ? 1 : 0) << 1) | w;
|
||||
if (nullification)
|
||||
((PRUint32 *)where)[1] = 0x08000240; // nop
|
||||
}
|
||||
|
||||
#if defined(DEBUG)
|
||||
void HPPAFormat11::
|
||||
printPretty(FILE* f)
|
||||
{
|
||||
if (validIM5(im5))
|
||||
fprintf(f, "%s,%s%s %d,%s,N%d%s", hiInfo[kind].string, hcInfo[cond].string, nullification ? ",n" : "",
|
||||
IM5(im5), getR2String(), target.dfsNum, nullification ? "; nop" : "");
|
||||
else
|
||||
fprintf(f, "%s,%s%s %s,%s,N%d%s", hiInfo[kind].string, hcInfo[cond].string, nullification ? ",n" : "",
|
||||
getR1String(), getR2String(), target.dfsNum, nullification ? "; nop" : "");
|
||||
}
|
||||
#endif
|
||||
|
||||
// HPPAFormat12 methods
|
||||
// |31 |25 |20 |15 |12 | 1| 0|
|
||||
// |-----------------|--------------|--------------|--------|--------------------------------|--|--|
|
||||
// | op | b | w1 | s | w2 | n| w|
|
||||
// |-----------------|--------------|--------------|--------|--------------------------------|--|--|
|
||||
|
||||
void HPPAFormat12::
|
||||
formatToMemory(void* where, uint32 offset)
|
||||
{
|
||||
PRUint32 w2, w1, w;
|
||||
|
||||
dis_assemble_17((target.getNativeOffset() - (offset + 8)) >> 2, w1, w2, w);
|
||||
|
||||
((PRUint32 *)where)[0] = (hiInfo[kind].opcode << 26) | /*(getT() << 21) |*/ (w1 << 16) | (w2 << 2) | ((nullification ? 1 : 0) << 1) | w;
|
||||
if (nullification)
|
||||
((PRUint32 *)where)[1] = 0x08000240; // nop
|
||||
}
|
||||
|
||||
#if defined(DEBUG)
|
||||
void HPPAFormat12::
|
||||
printPretty(FILE* f)
|
||||
{
|
||||
fprintf(f, "%s%s N%d,%s%s", hiInfo[kind].string, nullification ? ",n" : "",
|
||||
target.dfsNum, getTString(), nullification ? "; nop" : "");
|
||||
}
|
||||
#endif
|
||||
|
||||
// HPPAFormat13 methods
|
||||
// |31 |25 |20 |15 |12 | 1| 0|
|
||||
// |-----------------|--------------|--------------|--------|--------------------------------|--|--|
|
||||
// | op | t | w1 | ext3 | w2 | n| w|
|
||||
// |-----------------|--------------|--------------|--------|--------------------------------|--|--|
|
||||
void HPPAFormat13::
|
||||
formatToMemory(void* where, uint32 /*offset*/)
|
||||
{
|
||||
PRUint32 w2, w1, w;
|
||||
|
||||
dis_assemble_17((((PRUint32) address) - (((PRUint32) where) + 8)) >> 2, w1, w2, w);
|
||||
|
||||
((PRUint32 *)where)[0] = (hiInfo[kind].opcode << 26) | (returnRegister << 21) | (w1 << 16) | (w2 << 2) | ((nullification ? 1 : 0) << 1) | w;
|
||||
if (nullification)
|
||||
((PRUint32 *)where)[1] = 0x08000240; // nop
|
||||
}
|
||||
|
||||
#if defined(DEBUG)
|
||||
void HPPAFormat13::
|
||||
printPretty(FILE* f)
|
||||
{
|
||||
fprintf(f, "%s%s %s,%s", hiInfo[kind].string, nullification ? ",n" : "", getAddressString(), getTString());
|
||||
}
|
||||
#endif
|
||||
|
||||
// HPPAFormat31 methods
|
||||
// |31 |25 |20 |15 |13|12|11 |9 |8 |5 |4 0|
|
||||
// |-----------------|--------------|--------------|-----|--|--|-----|--|--------|--|--------------|
|
||||
// | op | b | im5 | s |a |1 | cc |0 | uid |m | t |
|
||||
// |-----------------|--------------|--------------|-----|--|--|-----|--|--------|--|--------------|
|
||||
|
||||
void HPPAFormat31::
|
||||
formatToMemory(void* where, uint32 /*offset*/)
|
||||
{
|
||||
*(PRUint32 *)where = 0;
|
||||
}
|
||||
|
||||
#if defined(DEBUG)
|
||||
void HPPAFormat31::
|
||||
printPretty(FILE* f)
|
||||
{
|
||||
fprintf(f, "%s %d(0,%s),%s", hiInfo[kind].string, (PRInt8) getIm5(), getR1String(), getTString());
|
||||
}
|
||||
#endif
|
||||
|
||||
// HPPAFormat32 methods
|
||||
// |31 |25 |20 |15 |13|12|11 |9 |8 |5 |4 0|
|
||||
// |-----------------|--------------|--------------|-----|--|--|-----|--|--------|--|--------------|
|
||||
// | op | b | im5 | s |a |1 | cc |1 | uid |m | r |
|
||||
// |-----------------|--------------|--------------|-----|--|--|-----|--|--------|--|--------------|
|
||||
|
||||
void HPPAFormat32::
|
||||
formatToMemory(void* where, uint32 /*offset*/)
|
||||
{
|
||||
*(PRUint32 *)where = 0;
|
||||
}
|
||||
|
||||
#if defined(DEBUG)
|
||||
void HPPAFormat32::
|
||||
printPretty(FILE* f)
|
||||
{
|
||||
fprintf(f, "%s %s,%d(0,%s)", hiInfo[kind].string, getR2String(), (PRInt8) getIm5(), getR1String());
|
||||
}
|
||||
#endif
|
||||
|
||||
// HPPAFormat40 methods
|
||||
// |31 |25 |20 |15 |12|11|10 |8 |7 |6 |5 |4 0|
|
||||
// |-----------------|--------------|--------------|--------|--|--|-----|--|--|--|--|--------------|
|
||||
// | op | r1 | r2 | sop |r2|f | 3 |x |r1|t |0 | t |
|
||||
// |-----------------|--------------|--------------|--------|--|--|-----|--|--|--|--|--------------|
|
||||
|
||||
void HPPAFormat40::
|
||||
formatToMemory(void* where, uint32 /*offset*/)
|
||||
{
|
||||
*(PRUint32 *)where = 0;
|
||||
}
|
||||
|
||||
#if defined(DEBUG)
|
||||
void HPPAFormat40::
|
||||
printPretty(FILE* f)
|
||||
{
|
||||
fprintf(f, "%s %s,%s,%s", hiInfo[kind].string, getR1String(), getR2String(), getTString());
|
||||
}
|
||||
#endif
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,260 @@
|
|||
/* -*- 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.
|
||||
*/
|
||||
|
||||
#include "Fundamentals.h"
|
||||
#include "prbit.h"
|
||||
|
||||
#include "HPPAMul.h"
|
||||
|
||||
/*
|
||||
*-----------------------------------------------------------------------
|
||||
*
|
||||
* Local data for an HPPA proc. This should be obtained by asking the
|
||||
* Code Generator to fill these arrays.
|
||||
*
|
||||
*-----------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
static PRInt16 shiftCosts[32] =
|
||||
{
|
||||
0, 4, 4, 4, 4, 4, 4, 4,
|
||||
4, 4, 4, 4, 4, 4, 4, 4,
|
||||
4, 4, 4, 4, 4, 4, 4, 4,
|
||||
4, 4, 4, 4, 4, 4, 4, 4
|
||||
};
|
||||
|
||||
static PRInt16 shiftAddCosts[32] =
|
||||
{
|
||||
4, 4, 4, 4, 999, 999, 999, 999,
|
||||
999, 999, 999, 999, 999, 999, 999, 999,
|
||||
999, 999, 999, 999, 999, 999, 999, 999,
|
||||
999, 999, 999, 999, 999, 999, 999, 999
|
||||
};
|
||||
|
||||
static PRInt16 shiftSubCosts[32] =
|
||||
{
|
||||
4, 999, 999, 999, 999, 999, 999, 999,
|
||||
999, 999, 999, 999, 999, 999, 999, 999,
|
||||
999, 999, 999, 999, 999, 999, 999, 999,
|
||||
999, 999, 999, 999, 999, 999, 999, 999
|
||||
};
|
||||
|
||||
static PRInt16 addCost = 4;
|
||||
|
||||
/*
|
||||
*-----------------------------------------------------------------------
|
||||
*
|
||||
* getMulAlgorithm --
|
||||
*
|
||||
* Return the best algorithm for an immediate multiplication.
|
||||
* If algorithm.cost >= maxCost or retval is false then no algorithm
|
||||
* was found and a regular multiplication should be used.
|
||||
*
|
||||
* This algorithm does not work if the register to multiply is larger
|
||||
* than an PRUint32 and the multiplicand does not fit exactely in an PRUint32.
|
||||
* e.g.: long long mul by a negative value.
|
||||
*
|
||||
*-----------------------------------------------------------------------
|
||||
*/
|
||||
bool
|
||||
getMulAlgorithm(MulAlgorithm* algorithm, PRUint32 multiplicand, PRInt16 maxCost)
|
||||
{
|
||||
PRUint32 mask;
|
||||
PRUint8 currentCost;
|
||||
PRInt8 shiftBy;
|
||||
|
||||
algorithm->cost = maxCost;
|
||||
if (maxCost <= 0)
|
||||
return false;
|
||||
|
||||
if (multiplicand == 0x1)
|
||||
{
|
||||
algorithm->nOperations = 1;
|
||||
algorithm->cost = 0;
|
||||
algorithm->operations[0] = maMultiplicand;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (multiplicand == 0x0)
|
||||
{
|
||||
algorithm->nOperations = 1;
|
||||
algorithm->cost = 0;
|
||||
algorithm->operations[0] = maZero;
|
||||
return true;
|
||||
}
|
||||
|
||||
MulAlgorithm* downAlgorithm = new MulAlgorithm();
|
||||
MulAlgorithm* bestAlgorithm = new MulAlgorithm();
|
||||
MulAlgorithm* swapAlgorithm;
|
||||
|
||||
// we try to do a shift if there is a group of 0 bits.
|
||||
if ((multiplicand & 0x1) == 0x0)
|
||||
{
|
||||
// number of low zero bits.
|
||||
mask = multiplicand & -multiplicand;
|
||||
PR_FLOOR_LOG2(shiftBy, mask);
|
||||
|
||||
currentCost = shiftCosts[shiftBy];
|
||||
getMulAlgorithm(downAlgorithm, multiplicand >> shiftBy, maxCost - currentCost);
|
||||
currentCost += downAlgorithm->cost;
|
||||
|
||||
if (currentCost < maxCost)
|
||||
{
|
||||
swapAlgorithm = downAlgorithm, downAlgorithm = bestAlgorithm, bestAlgorithm = swapAlgorithm;
|
||||
bestAlgorithm->shiftAmount[bestAlgorithm->nOperations] = shiftBy;
|
||||
bestAlgorithm->operations[bestAlgorithm->nOperations] = maShiftValue;
|
||||
maxCost = currentCost;
|
||||
}
|
||||
}
|
||||
|
||||
// If this is an odd number, we can try to add one or to subtract one.
|
||||
if ((multiplicand & 0x1) != 0x0)
|
||||
{
|
||||
for (mask = 1; (mask & multiplicand) != 0x0; mask <<= 1);
|
||||
|
||||
if (mask > 2 && multiplicand != 3)
|
||||
{
|
||||
currentCost = addCost;
|
||||
getMulAlgorithm(downAlgorithm, multiplicand + 1, maxCost - currentCost);
|
||||
currentCost += downAlgorithm->cost;
|
||||
|
||||
if (currentCost < maxCost)
|
||||
{
|
||||
swapAlgorithm = downAlgorithm, downAlgorithm = bestAlgorithm, bestAlgorithm = swapAlgorithm;
|
||||
bestAlgorithm->shiftAmount[bestAlgorithm->nOperations] = 0;
|
||||
bestAlgorithm->operations[bestAlgorithm->nOperations] = maSubShiftMultiplicandFromValue;
|
||||
maxCost = currentCost;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
currentCost = addCost;
|
||||
getMulAlgorithm(downAlgorithm, multiplicand - 1, maxCost - currentCost);
|
||||
currentCost += downAlgorithm->cost;
|
||||
|
||||
if (currentCost < maxCost)
|
||||
{
|
||||
swapAlgorithm = downAlgorithm, downAlgorithm = bestAlgorithm, bestAlgorithm = swapAlgorithm;
|
||||
bestAlgorithm->shiftAmount[bestAlgorithm->nOperations] = 0;
|
||||
bestAlgorithm->operations[bestAlgorithm->nOperations] = maAddValueToShiftMultiplicand;
|
||||
maxCost = currentCost;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mask = multiplicand - 1;
|
||||
PR_FLOOR_LOG2(shiftBy, mask);
|
||||
while (shiftBy >= 2)
|
||||
{
|
||||
PRUint32 d;
|
||||
|
||||
d = (0x1 << shiftBy) + 1;
|
||||
if (multiplicand % d == 0 && multiplicand > d)
|
||||
{
|
||||
currentCost = PR_MIN(shiftAddCosts[shiftBy], addCost + shiftCosts[shiftBy]);
|
||||
getMulAlgorithm(downAlgorithm, multiplicand / d, maxCost - currentCost);
|
||||
currentCost += downAlgorithm->cost;
|
||||
|
||||
if (currentCost < maxCost)
|
||||
{
|
||||
swapAlgorithm = downAlgorithm, downAlgorithm = bestAlgorithm, bestAlgorithm = swapAlgorithm;
|
||||
bestAlgorithm->shiftAmount[bestAlgorithm->nOperations] = shiftBy;
|
||||
bestAlgorithm->operations[bestAlgorithm->nOperations] = maAddValueToShiftValue;
|
||||
maxCost = currentCost;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
d = (0x1 << shiftBy) - 1;
|
||||
if (multiplicand % d == 0 && multiplicand > d)
|
||||
{
|
||||
currentCost = PR_MIN(shiftSubCosts[shiftBy], addCost + shiftCosts[shiftBy]);
|
||||
getMulAlgorithm(downAlgorithm, multiplicand / d, maxCost - currentCost);
|
||||
currentCost += downAlgorithm->cost;
|
||||
|
||||
if (currentCost < maxCost)
|
||||
{
|
||||
swapAlgorithm = downAlgorithm, downAlgorithm = bestAlgorithm, bestAlgorithm = swapAlgorithm;
|
||||
bestAlgorithm->shiftAmount[bestAlgorithm->nOperations] = shiftBy;
|
||||
bestAlgorithm->operations[bestAlgorithm->nOperations] = maSubValueFromShiftValue;
|
||||
maxCost = currentCost;
|
||||
}
|
||||
break;
|
||||
}
|
||||
shiftBy--;
|
||||
}
|
||||
|
||||
if ((multiplicand & 0x1) != 0x0)
|
||||
{
|
||||
mask = multiplicand - 1;
|
||||
mask = mask & -mask;
|
||||
if (mask != 0 && (mask & (mask - 1)) == 0)
|
||||
{
|
||||
PR_FLOOR_LOG2(shiftBy, mask);
|
||||
currentCost = shiftAddCosts[shiftBy];
|
||||
getMulAlgorithm(downAlgorithm, (multiplicand - 1) >> shiftBy, maxCost - currentCost);
|
||||
currentCost += downAlgorithm->cost;
|
||||
|
||||
if (currentCost < maxCost)
|
||||
{
|
||||
swapAlgorithm = downAlgorithm, downAlgorithm = bestAlgorithm, bestAlgorithm = swapAlgorithm;
|
||||
bestAlgorithm->shiftAmount[bestAlgorithm->nOperations] = shiftBy;
|
||||
bestAlgorithm->operations[bestAlgorithm->nOperations] = maAddMultiplicandToShiftValue;
|
||||
maxCost = currentCost;
|
||||
}
|
||||
}
|
||||
|
||||
mask = multiplicand + 1;
|
||||
mask = mask & -mask;
|
||||
if (mask != 0 && (mask & (mask - 1)) == 0)
|
||||
{
|
||||
PR_FLOOR_LOG2(shiftBy, mask);
|
||||
currentCost = shiftSubCosts[shiftBy];
|
||||
getMulAlgorithm(downAlgorithm, (multiplicand + 1) >> shiftBy, maxCost - currentCost);
|
||||
currentCost += downAlgorithm->cost;
|
||||
|
||||
if (currentCost < maxCost)
|
||||
{
|
||||
swapAlgorithm = downAlgorithm, downAlgorithm = bestAlgorithm, bestAlgorithm = swapAlgorithm;
|
||||
bestAlgorithm->shiftAmount[bestAlgorithm->nOperations] = shiftBy;
|
||||
bestAlgorithm->operations[bestAlgorithm->nOperations] = maSubMultiplicandFromShiftValue;
|
||||
maxCost = currentCost;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// No algorithm found.
|
||||
if (maxCost == algorithm->cost)
|
||||
return false;
|
||||
|
||||
// Too long
|
||||
if (bestAlgorithm->nOperations == 32)
|
||||
return false;
|
||||
|
||||
algorithm->nOperations = bestAlgorithm->nOperations + 1;
|
||||
algorithm->cost = maxCost;
|
||||
|
||||
copy(bestAlgorithm->operations, &bestAlgorithm->operations[algorithm->nOperations], algorithm->operations);
|
||||
copy(bestAlgorithm->shiftAmount, &bestAlgorithm->shiftAmount[algorithm->nOperations], algorithm->shiftAmount);
|
||||
|
||||
// we better find a way to avoid allocating memory each time.
|
||||
delete downAlgorithm;
|
||||
delete bestAlgorithm;
|
||||
|
||||
return true;
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
/* -*- 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 _HPPA_MUL_H_
|
||||
#define _HPPA_MUL_H_
|
||||
|
||||
#include "Fundamentals.h"
|
||||
|
||||
enum MultiplicationOperation
|
||||
{
|
||||
maZero, // total = 0
|
||||
maMultiplicand, // total = multiplicand
|
||||
maShiftValue, // total = value << shiftBy
|
||||
maAddValueToShiftMultiplicand, // total = value + (multiplicand << shiftBy)
|
||||
maSubShiftMultiplicandFromValue, // total = value - (multiplicand << shiftBy)
|
||||
maAddValueToShiftValue, // total = (value << shiftBy) + value
|
||||
maSubValueFromShiftValue, // total = (value << shiftBy) - value
|
||||
maAddMultiplicandToShiftValue, // total = (value << shiftBy) + multiplicand
|
||||
maSubMultiplicandFromShiftValue, // total = (value << shiftBy) - multiplicand
|
||||
};
|
||||
|
||||
struct MulAlgorithm
|
||||
{
|
||||
PRInt16 cost;
|
||||
PRUint16 nOperations;
|
||||
MultiplicationOperation operations[32];
|
||||
PRUint8 shiftAmount[32];
|
||||
};
|
||||
|
||||
extern bool getMulAlgorithm(MulAlgorithm* algorithm, PRUint32 multiplicand, PRInt16 maxCost);
|
||||
|
||||
#endif /* _HPPA_MUL_H_ */
|
|
@ -0,0 +1,73 @@
|
|||
#!gmake
|
||||
#
|
||||
# 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.
|
||||
|
||||
DEPTH = ../../../..
|
||||
|
||||
MODULE_NAME = hppa
|
||||
|
||||
include $(DEPTH)/config/config.mk
|
||||
|
||||
INCLUDES += \
|
||||
-I$(DEPTH)/Utilities/General \
|
||||
-I$(DEPTH)/Utilities/zlib \
|
||||
-I$(DEPTH)/Runtime/ClassReader \
|
||||
-I$(DEPTH)/Compiler/FrontEnd \
|
||||
-I$(DEPTH)/Runtime/NativeMethods \
|
||||
-I$(DEPTH)/Runtime/System \
|
||||
-I$(DEPTH)/Compiler/PrimitiveGraph \
|
||||
-I$(DEPTH)/Runtime/ClassInfo \
|
||||
-I$(DEPTH)/Runtime/FileReader \
|
||||
-I$(DEPTH)/Compiler/CodeGenerator \
|
||||
-I$(DEPTH)/Compiler/CodeGenerator/md \
|
||||
-I$(DEPTH)/Compiler/RegisterAllocator \
|
||||
$(NULL)
|
||||
|
||||
CXXSRCS = \
|
||||
HPPAEmitter.cpp \
|
||||
HPPAInstruction.cpp \
|
||||
HPPAMul.cpp \
|
||||
hppa.nad.burg.cpp \
|
||||
$(NULL)
|
||||
|
||||
include $(DEPTH)/config/rules.mk
|
||||
|
||||
export:: hppa.nad.burg.cpp
|
||||
|
||||
libs:: $(MODULE)
|
||||
|
||||
#
|
||||
# Rules to generate hppa.nad.burg.[cpp][h]
|
||||
#
|
||||
|
||||
hppa.nad.burg.cpp: hppa.nad.burg $(BURG)
|
||||
$(BURG) -I -o $@ < $<
|
||||
|
||||
hppa.nad.burg: hppa.nad $(DEPTH)/Compiler/PrimitiveGraph/PrimitiveOperations $(DEPTH)/Tools/Nad/nad.pl
|
||||
$(PERL) $(DEPTH)/Tools/Nad/nad.pl $< $(DEPTH)/Compiler/PrimitiveGraph/PrimitiveOperations \
|
||||
$(DEPTH)/Compiler/PrimitiveGraph/PrimitiveOperations.h \
|
||||
$(DEPTH)/Compiler/PrimitiveGraph/PrimitiveOperations.cpp \
|
||||
$<.burg.h > $@
|
||||
|
||||
#
|
||||
# Extra cleaning
|
||||
#
|
||||
|
||||
clobber::
|
||||
rm -f hppa.nad.burg.cpp hppa.nad.burg.h hppa.nad.burg
|
||||
|
||||
realclean clobber_all::
|
||||
rm -f hppa.nad.burg.cpp hppa.nad.burg.h hppa.nad.burg
|
|
@ -0,0 +1,452 @@
|
|||
%top
|
||||
/* -*- 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.
|
||||
*/
|
||||
#include "Burg.h"
|
||||
%
|
||||
|
||||
%terminals
|
||||
%
|
||||
|
||||
%startsymbols
|
||||
Control
|
||||
Result
|
||||
Exception
|
||||
Store
|
||||
Vcond
|
||||
Vint
|
||||
Vfixed
|
||||
Vlong
|
||||
Vfloat
|
||||
Vdouble
|
||||
Vptr
|
||||
Cint
|
||||
Cfixed
|
||||
Clong
|
||||
Cfloat
|
||||
Cdouble
|
||||
Cptr
|
||||
%
|
||||
|
||||
%grammar
|
||||
Vint: coReg_I $1 $
|
||||
Vlong: coReg_L $1 $
|
||||
Vfloat: coReg_F $1 $
|
||||
Vdouble: coReg_D $1 $
|
||||
Vptr: coReg_A $1 $
|
||||
Vcond: coReg_C $1 $
|
||||
Store: coReg_M $1 $
|
||||
Cint: coReg_I $1 $
|
||||
Cfixed: coReg_I $1 $
|
||||
Clong: coReg_L $1 $
|
||||
Cfloat: coReg_F $1 $
|
||||
Cdouble: coReg_D $1 $
|
||||
Cptr: coReg_A $1 $
|
||||
|
||||
Vfixed: Vint $1 $
|
||||
Vint: Vfixed $1 $
|
||||
|
||||
Vfixed: poConst_I $1 $emConst_IF
|
||||
Vint: poConst_I $1 $emConst_I
|
||||
Vlong: poConst_L $1 $emConst_L
|
||||
Vfloat: poConst_F $1 $emConst_F
|
||||
Vdouble: poConst_D $1 $emConst_D
|
||||
Vptr: poConst_A $1 $emConst_A
|
||||
Vcond: poConst_C $1 $emConst_C
|
||||
|
||||
Store: poBreak(Store) $1 $emBreak
|
||||
|
||||
Vint: poArg_I $1 $emArg_I
|
||||
Vlong: poArg_L $1 $emArg_L
|
||||
Vfloat: poArg_F $1 $emArg_F
|
||||
Vdouble: poArg_D $1 $emArg_D
|
||||
Vptr: poArg_A $1 $emArg_A
|
||||
Store: poArg_M $1 $emArg_M
|
||||
|
||||
Result: poResult_I(Vint) $1 $emResult_I
|
||||
Result: poResult_L(Vlong) $1 $emResult_L
|
||||
Result: poResult_F(Vfloat) $1 $emResult_F
|
||||
Result: poResult_D(Vdouble) $1 $emResult_D
|
||||
Result: poResult_A(Vptr) $1 $emResult_A
|
||||
// (not used): poResult_C(Acond) $1 $emResult_C
|
||||
Result: poResult_M(Store) $1 $emResult_M
|
||||
|
||||
Control: poIfLt(Vcond) $1 $emIfLt
|
||||
Control: poIfEq(Vcond) $1 $emIfEq
|
||||
Control: poIfLe(Vcond) $1 $emIfLe
|
||||
Control: poIfGt(Vcond) $1 $emIfGt
|
||||
Control: poIfLgt(Vcond) $1 $emIfLgt
|
||||
Control: poIfGe(Vcond) $1 $emIfGe
|
||||
Control: poIfOrd(Vcond) $1 $emIfOrd
|
||||
Control: poIfUnord(Vcond) $1 $emIfUnord
|
||||
Control: poIfULt(Vcond) $1 $emIfULt
|
||||
Control: poIfUEq(Vcond) $1 $emIfUEq
|
||||
Control: poIfULe(Vcond) $1 $emIfULe
|
||||
Control: poIfUGt(Vcond) $1 $emIfUGt
|
||||
Control: poIfNe(Vcond) $1 $emIfNe
|
||||
Control: poIfUGe(Vcond) $1 $emIfUGe
|
||||
|
||||
Control: poSwitch(Vint) $1 $emSwitch
|
||||
|
||||
Vint: poAnd_I(Vint, Vint) $1 $emAnd_I
|
||||
Vlong: poAnd_L(Vlong, Vlong) $1 $emAnd_L
|
||||
Vint: poAndI_I(Vint, Cint) $1 $emAndI_I
|
||||
Vlong: poAndI_L(Vlong, Clong) $1 $emAndI_L
|
||||
Vint: poOr_I(Vint, Vint) $1 $emOr_I
|
||||
Vlong: poOr_L(Vlong, Vlong) $1 $emOr_L
|
||||
Vint: poOrI_I(Vint, Cint) $1 $emOrI_I
|
||||
Vlong: poOrI_L(Vlong, Clong) $1 $emOrI_L
|
||||
Vint: poXor_I(Vint, Vint) $1 $emXor_I
|
||||
Vlong: poXor_L(Vlong, Vlong) $1 $emXor_L
|
||||
Vint: poXorI_I(Vint, Cint) $1 $emXorI_I
|
||||
Vlong: poXorI_L(Vlong, Clong) $1 $emXorI_L
|
||||
|
||||
|
||||
Vptr: poAdd_A(Vptr, poShlI_I(Vint, Cint)) $1 $emShAdd_IIndirect
|
||||
|
||||
Vint: poAdd_I(Vint, Vint) $1 $emAdd_I
|
||||
Vlong: poAdd_L(Vlong, Vlong) $1 $emAdd_L
|
||||
Vptr: poAdd_A(Vptr, Vint) $1 $emAdd_A
|
||||
Vptr: poAddU_A(Vptr, Vint) $1 $emAddU_A
|
||||
Vint: poAddI_I(Vint, Cint) $1 $emAddI_I
|
||||
Vlong: poAddI_L(Vlong, Clong) $1 $emAddI_L
|
||||
Vptr: poAddI_A(Vptr, Cint) $1 $emAddI_A
|
||||
Vptr: poAddR_A(Cptr, Vint) $1 $emAddR_A
|
||||
Vptr: poAddRU_A(Cptr, Vint) $1 $emAddRU_A
|
||||
|
||||
Vint: poSub_I(Vint, Vint) $1 $emSub_I
|
||||
Vlong: poSub_L(Vlong, Vlong) $1 $emSub_L
|
||||
Vptr: poSub_A(Vptr, Vint) $1 $emSub_A
|
||||
Vptr: poSubU_A(Vptr, Vint) $1 $emSubU_A
|
||||
Vint: poSubR_I(Cint, Vint) $1 $emSubR_I
|
||||
Vlong: poSubR_L(Clong, Vlong) $1 $emSubR_L
|
||||
Vptr: poSubR_A(Cptr, Vint) $1 $emSubR_A
|
||||
Vptr: poSubUR_A(Cptr, Vint) $1 $emSubUR_A
|
||||
Vint: poSubA_I(Vptr, Vptr) $1 $emSubA_I
|
||||
Vint: poSubAI_I(Vptr, Cptr) $1 $emSubAI_I
|
||||
Vint: poSubAR_I(Cptr, Vptr) $1 $emSubAR_I
|
||||
|
||||
Vfixed: poMul_I(Vfixed, Vfixed) $1 $emMul_I
|
||||
Vlong: poMul_L(Vlong, Vlong) $1 $emMul_L
|
||||
Vfixed: poMulI_I(Vfixed, Cfixed) $1 $emMulI_I
|
||||
Vlong: poMulI_L(Vlong, Clong) $1 $emMulI_L
|
||||
|
||||
Vint: poDivI_I(Vint, Cint) $1 $emDivI_I
|
||||
Vint: poModE_I(Vint, Vint) $1 $emModE_I
|
||||
|
||||
Vint: poShl_I(Vint, Vint) $1 $emShl_I
|
||||
Vlong: poShl_L(Vlong, Vint) $1 $emShl_L
|
||||
Vint: poShlI_I(Vint, Cint) $1 $emShlI_I
|
||||
Vlong: poShlI_L(Vlong, Cint) $1 $emShlI_L
|
||||
Vint: poShlR_I(Cint, Vint) $1 $emShlR_I
|
||||
Vlong: poShlR_L(Clong, Vint) $1 $emShlR_L
|
||||
Vint: poShr_I(Vint, Vint) $1 $emShr_I
|
||||
Vlong: poShr_L(Vlong, Vint) $1 $emShr_L
|
||||
Vint: poShrI_I(Vint, Cint) $1 $emShrI_I
|
||||
Vlong: poShrI_L(Vlong, Cint) $1 $emShrI_L
|
||||
Vint: poShrR_I(Cint, Vint) $1 $emShrR_I
|
||||
Vlong: poShrR_L(Clong, Vint) $1 $emShrR_L
|
||||
Vint: poShrU_I(Vint, Vint) $1 $emShrU_I
|
||||
Vlong: poShrU_L(Vlong, Vint) $1 $emShrU_L
|
||||
Vint: poShrUI_I(Vint, Cint) $1 $emShrUI_I
|
||||
Vlong: poShrUI_L(Vlong, Cint) $1 $emShrUI_L
|
||||
Vint: poShrUR_I(Cint, Vint) $1 $emShrUR_I
|
||||
Vlong: poShrUR_L(Clong, Vint) $1 $emShrUR_L
|
||||
|
||||
Vint: poExt_I(Vint, Cint) $1 $emExt_I
|
||||
Vlong: poExt_L(Vlong, Cint) $1 $emExt_L
|
||||
|
||||
Vfloat: poFAdd_F(Vfloat, Vfloat) $1 $emFAdd_F
|
||||
Vdouble: poFAdd_D(Vdouble, Vdouble) $1 $emFAdd_D
|
||||
Vfloat: poFAddI_F(Vfloat, Cfloat) $1 $emFAddI_F
|
||||
Vdouble: poFAddI_D(Vdouble, Cdouble) $1 $emFAddI_D
|
||||
|
||||
Vfloat: poFSub_F(Vfloat, Vfloat) $1 $emFSub_F
|
||||
Vdouble: poFSub_D(Vdouble, Vdouble) $1 $emFSub_D
|
||||
Vfloat: poFSubR_F(Cfloat, Vfloat) $1 $emFSubR_F
|
||||
Vdouble: poFSubR_D(Cdouble, Vdouble) $1 $emFSubR_D
|
||||
|
||||
Vfloat: poFMul_F(Vfloat, Vfloat) $1 $emFMul_F
|
||||
Vdouble: poFMul_D(Vdouble, Vdouble) $1 $emFMul_D
|
||||
Vfloat: poFMulI_F(Vfloat, Cfloat) $1 $emFMulI_F
|
||||
Vdouble: poFMulI_D(Vdouble, Cdouble) $1 $emFMulI_D
|
||||
|
||||
Vfloat: poFDiv_F(Vfloat, Vfloat) $1 $emFDiv_F
|
||||
Vdouble: poFDiv_D(Vdouble, Vdouble) $1 $emFDiv_D
|
||||
Vfloat: poFDivI_F(Vfloat, Cfloat) $1 $emFDivI_F
|
||||
Vdouble: poFDivI_D(Vdouble, Cdouble) $1 $emFDivI_D
|
||||
Vfloat: poFDivR_F(Cfloat, Vfloat) $1 $emFDivR_F
|
||||
Vdouble: poFDivR_D(Cdouble, Vdouble) $1 $emFDivR_D
|
||||
|
||||
Vfloat: poFRem_F(Vfloat, Vfloat) $1 $emFRem_F
|
||||
Vdouble: poFRem_D(Vdouble, Vdouble) $1 $emFRem_D
|
||||
Vfloat: poFRemI_F(Vfloat, Cfloat) $1 $emFRemI_F
|
||||
Vdouble: poFRemI_D(Vdouble, Cdouble) $1 $emFRemI_D
|
||||
Vfloat: poFRemR_F(Cfloat, Vfloat) $1 $emFRemR_F
|
||||
Vdouble: poFRemR_D(Cdouble, Vdouble) $1 $emFRemR_D
|
||||
|
||||
Vint: poConvI_L(Vlong) $1 $emConvI_L
|
||||
Vlong: poConvL_I(Vint) $1 $emConvL_I
|
||||
|
||||
Vint: poFConvI_F(Vfloat) $1 $emFConvI_F
|
||||
Vint: poFConvI_D(Vdouble) $1 $emFConvI_D
|
||||
Vlong: poFConvL_F(Vfloat) $1 $emFConvL_F
|
||||
Vlong: poFConvL_D(Vdouble) $1 $emFConvL_D
|
||||
Vfloat: poFConvF_I(Vint) $1 $emFConvF_I
|
||||
Vfloat: poFConvF_L(Vlong) $1 $emFConvF_L
|
||||
Vfloat: poFConvF_D(Vdouble) $1 $emFConvF_D
|
||||
Vdouble: poFConvD_I(Vint) $1 $emFConvD_I
|
||||
Vdouble: poFConvD_L(Vlong) $1 $emFConvD_L
|
||||
Vdouble: poFConvD_F(Vfloat) $1 $emFConvD_F
|
||||
|
||||
Vcond: poCmp_I(Vint, Vint) $1 $emCmp_I
|
||||
Vcond: poCmp_L(Vlong, Vlong) $1 $emCmp_L
|
||||
Vcond: poCmpI_I(Vint, Cint) $1 $emCmpI_I
|
||||
Vcond: poCmpI_L(Vlong, Clong) $1 $emCmpI_L
|
||||
Vcond: poCmpU_I(Vint, Vint) $1 $emCmpU_I
|
||||
Vcond: poCmpU_L(Vlong, Vlong) $1 $emCmpU_L
|
||||
Vcond: poCmpU_A(Vptr, Vptr) $1 $emCmpU_A
|
||||
Vcond: poCmpUI_I(Vint, Cint) $1 $emCmpUI_I
|
||||
Vcond: poCmpUI_L(Vlong, Clong) $1 $emCmpUI_L
|
||||
Vcond: poCmpUI_A(Vptr, Cptr) $1 $emCmpUI_A
|
||||
|
||||
Vcond: poFCmp_F(Vfloat, Vfloat) $1 $emFCmp_F
|
||||
Vcond: poFCmp_D(Vdouble, Vdouble) $1 $emFCmp_D
|
||||
Vcond: poFCmpI_F(Vfloat, Cfloat) $1 $emFCmpI_F
|
||||
Vcond: poFCmpI_D(Vdouble, Cdouble) $1 $emFCmpI_D
|
||||
|
||||
Vint: poLt_I(Vcond) $1 $emLt_I
|
||||
Vint: poEq_I(Vcond) $1 $emEq_I
|
||||
Vint: poLe_I(Vcond) $1 $emLe_I
|
||||
Vint: poGt_I(Vcond) $1 $emGt_I
|
||||
Vint: poLgt_I(Vcond) $1 $emLgt_I
|
||||
Vint: poGe_I(Vcond) $1 $emGe_I
|
||||
Vint: poOrd_I(Vcond) $1 $emOrd_I
|
||||
Vint: poUnord_I(Vcond) $1 $emUnord_I
|
||||
Vint: poULt_I(Vcond) $1 $emULt_I
|
||||
Vint: poUEq_I(Vcond) $1 $emUEq_I
|
||||
Vint: poULe_I(Vcond) $1 $emULe_I
|
||||
Vint: poUGt_I(Vcond) $1 $emUGt_I
|
||||
Vint: poNe_I(Vcond) $1 $emNe_I
|
||||
Vint: poUGe_I(Vcond) $1 $emUGe_I
|
||||
|
||||
Vint: poCatL_I(Vcond) $1 $emCatL_I
|
||||
Vint: poCatG_I(Vcond) $1 $emCatG_I
|
||||
Vint: poCatCL_I(Vcond) $1 $emCatCL_I
|
||||
Vint: poCatCG_I(Vcond) $1 $emCatCG_I
|
||||
|
||||
Exception: poChkNull(Vptr) $1 $emChkNull
|
||||
Exception: poLimit(Vint, Vint) $1 $emLimit
|
||||
Exception: poLimitI(Vint, Cint) $1 $emLimitI
|
||||
Exception: poLimitR(Cint, Vint) $1 $emLimitR
|
||||
|
||||
Vint: poLd_I(Vptr) $1 $emLd_I
|
||||
Vlong: poLd_L(Vptr) $1 $emLd_L
|
||||
Vfloat: poLd_F(Vptr) $1 $emLd_F
|
||||
Vdouble: poLd_D(Vptr) $1 $emLd_D
|
||||
Vptr: poLd_A(Vptr) $1 $emLd_A
|
||||
Vint: poLdE_I(Cptr) $1 $emLdE_I
|
||||
Vlong: poLdE_L(Cptr) $1 $emLdE_L
|
||||
Vfloat: poLdE_F(Cptr) $1 $emLdE_F
|
||||
Vdouble: poLdE_D(Cptr) $1 $emLdE_D
|
||||
Vptr: poLdE_A(Cptr) $1 $emLdE_A
|
||||
Vint: poLdG_I(Cptr) $1 $emLdG_I
|
||||
Vlong: poLdG_L(Cptr) $1 $emLdG_L
|
||||
Vfloat: poLdG_F(Cptr) $1 $emLdG_F
|
||||
Vdouble: poLdG_D(Cptr) $1 $emLdG_D
|
||||
Vptr: poLdG_A(Cptr) $1 $emLdG_A
|
||||
Vint: poLdS_B(Vptr) $1 $emLdS_B
|
||||
Vint: poLdS_H(Vptr) $1 $emLdS_H
|
||||
Vint: poLdSE_B(Cptr) $1 $emLdSE_B
|
||||
Vint: poLdSE_H(Cptr) $1 $emLdSE_H
|
||||
Vint: poLdSG_B(Cptr) $1 $emLdSG_B
|
||||
Vint: poLdSG_H(Cptr) $1 $emLdSG_H
|
||||
Vint: poLdU_B(Vptr) $1 $emLdU_B
|
||||
Vint: poLdU_H(Vptr) $1 $emLdU_H
|
||||
Vint: poLdUE_B(Cptr) $1 $emLdUE_B
|
||||
Vint: poLdUE_H(Cptr) $1 $emLdUE_H
|
||||
Vint: poLdUG_B(Cptr) $1 $emLdUG_B
|
||||
Vint: poLdUG_H(Cptr) $1 $emLdUG_H
|
||||
Vint: poLdV_I(Vptr) $1 $emLdV_I
|
||||
Vlong: poLdV_L(Vptr) $1 $emLdV_L
|
||||
Vfloat: poLdV_F(Vptr) $1 $emLdV_F
|
||||
Vdouble: poLdV_D(Vptr) $1 $emLdV_D
|
||||
Vptr: poLdV_A(Vptr) $1 $emLdV_A
|
||||
Vint: poLdVE_I(Cptr) $1 $emLdVE_I
|
||||
Vlong: poLdVE_L(Cptr) $1 $emLdVE_L
|
||||
Vfloat: poLdVE_F(Cptr) $1 $emLdVE_F
|
||||
Vdouble: poLdVE_D(Cptr) $1 $emLdVE_D
|
||||
Vptr: poLdVE_A(Cptr) $1 $emLdVE_A
|
||||
Vint: poLdVG_I(Cptr) $1 $emLdVG_I
|
||||
Vlong: poLdVG_L(Cptr) $1 $emLdVG_L
|
||||
Vfloat: poLdVG_F(Cptr) $1 $emLdVG_F
|
||||
Vdouble: poLdVG_D(Cptr) $1 $emLdVG_D
|
||||
Vptr: poLdVG_A(Cptr) $1 $emLdVG_A
|
||||
Vint: poLdVS_B(Vptr) $1 $emLdVS_B
|
||||
Vint: poLdVS_H(Vptr) $1 $emLdVS_H
|
||||
Vint: poLdVSE_B(Cptr) $1 $emLdVSE_B
|
||||
Vint: poLdVSE_H(Cptr) $1 $emLdVSE_H
|
||||
Vint: poLdVSG_B(Cptr) $1 $emLdVSG_B
|
||||
Vint: poLdVSG_H(Cptr) $1 $emLdVSG_H
|
||||
Vint: poLdVU_B(Vptr) $1 $emLdVU_B
|
||||
Vint: poLdVU_H(Vptr) $1 $emLdVU_H
|
||||
Vint: poLdVUE_B(Cptr) $1 $emLdVUE_B
|
||||
Vint: poLdVUE_H(Cptr) $1 $emLdVUE_H
|
||||
Vint: poLdVUG_B(Cptr) $1 $emLdVUG_B
|
||||
Vint: poLdVUG_H(Cptr) $1 $emLdVUG_H
|
||||
Vint: poLdC_I(Vptr) $1 $emLdC_I
|
||||
Vint: poLdC_L(Vptr) $1 $emLdC_L
|
||||
Vint: poLdC_F(Vptr) $1 $emLdC_F
|
||||
Vint: poLdC_D(Vptr) $1 $emLdC_D
|
||||
Vint: poLdC_A(Vptr) $1 $emLdC_A
|
||||
Vint: poLdCE_I(Vptr) $1 $emLdCE_I
|
||||
Vint: poLdCE_L(Vptr) $1 $emLdCE_L
|
||||
Vint: poLdCE_F(Vptr) $1 $emLdCE_F
|
||||
Vint: poLdCE_D(Vptr) $1 $emLdCE_D
|
||||
Vint: poLdCE_A(Vptr) $1 $emLdCE_A
|
||||
|
||||
Vint: poLdC_I(poAddI_A(Vptr, Cint)) $0 $emLdC_IRegisterIndirect
|
||||
Vint: poLd_I(poAddI_A(Vptr, Cint)) $0 $emLd_IRegisterIndirect
|
||||
|
||||
|
||||
// poLdCG_I = 257, // Load constant global *Cptr -> Vint (not used)
|
||||
// poLdCG_L = 258, // Load constant global *Cptr -> Vlong (not used)
|
||||
// poLdCG_F = 259, // Load constant global *Cptr -> Vfloat (not used)
|
||||
// poLdCG_D = 260, // Load constant global *Cptr -> Vdouble (not used)
|
||||
// poLdCG_A = 261, // Load constant global *Cptr -> Vptr (not used)
|
||||
Vint: poLdCS_B(Vptr) $1 $emLdCS_B
|
||||
Vint: poLdCS_H(Vptr) $1 $emLdCS_H
|
||||
Vint: poLdCSE_B(Cptr) $1 $emLdCSE_B
|
||||
Vint: poLdCSE_H(Cptr) $1 $emLdCSE_H
|
||||
//Vint (not used): poLdCSG_B(Cptr) $1 $emLdCSG_B
|
||||
//Vint (not used): poLdCSG_H(Cptr) $1 $emLdCSG_H
|
||||
Vint: poLdCU_B(Vptr) $1 $emLdCU_B
|
||||
Vint: poLdCU_H(Vptr) $1 $emLdCU_H
|
||||
Vint: poLdCUE_B(Cptr) $1 $emLdCUE_B
|
||||
Vint: poLdCUE_H(Cptr) $1 $emLdCUE_H
|
||||
//Vint (not used): poLdCUG_B(Cptr) $1 $emLdCUG_B
|
||||
//Vint (not used): poLdCUG_H(Cptr) $1 $emLdCUG_H
|
||||
|
||||
Store: poSt_B(Vptr, Vint) $1 $emSt_B
|
||||
Store: poSt_H(Vptr, Vint) $1 $emSt_H
|
||||
Store: poSt_I(Vptr, Vint) $1 $emSt_I
|
||||
Store: poSt_L(Vptr, Vlong) $1 $emSt_L
|
||||
Store: poSt_F(Vptr, Vfloat) $1 $emSt_F
|
||||
Store: poSt_D(Vptr, Vdouble) $1 $emSt_D
|
||||
Store: poSt_A(Vptr, Vptr) $1 $emSt_A
|
||||
Store: poStI_B(Vptr, Cint) $1 $emStI_B
|
||||
Store: poStI_H(Vptr, Cint) $1 $emStI_H
|
||||
Store: poStI_I(Vptr, Cint) $1 $emStI_I
|
||||
Store: poStI_L(Vptr, Clong) $1 $emStI_L
|
||||
Store: poStI_F(Vptr, Cfloat) $1 $emStI_F
|
||||
Store: poStI_D(Vptr, Cdouble) $1 $emStI_D
|
||||
Store: poStI_A(Vptr, Cptr) $1 $emStI_A
|
||||
Store: poStE_B(Cptr, Vint) $1 $emStE_B
|
||||
Store: poStE_H(Cptr, Vint) $1 $emStE_H
|
||||
Store: poStE_I(Cptr, Vint) $1 $emStE_I
|
||||
Store: poStE_L(Cptr, Vlong) $1 $emStE_L
|
||||
Store: poStE_F(Cptr, Vfloat) $1 $emStE_F
|
||||
Store: poStE_D(Cptr, Vdouble) $1 $emStE_D
|
||||
Store: poStE_A(Cptr, Vptr) $1 $emStE_A
|
||||
Store: poStEI_B(Cptr, Cint) $1 $emStEI_B
|
||||
Store: poStEI_H(Cptr, Cint) $1 $emStEI_H
|
||||
Store: poStEI_I(Cptr, Cint) $1 $emStEI_I
|
||||
Store: poStEI_L(Cptr, Clong) $1 $emStEI_L
|
||||
Store: poStEI_F(Cptr, Cfloat) $1 $emStEI_F
|
||||
Store: poStEI_D(Cptr, Cdouble) $1 $emStEI_D
|
||||
Store: poStEI_A(Cptr, Cptr) $1 $emStEI_A
|
||||
Store: poStG_B(Cptr, Vint) $1 $emStG_B
|
||||
Store: poStG_H(Cptr, Vint) $1 $emStG_H
|
||||
Store: poStG_I(Cptr, Vint) $1 $emStG_I
|
||||
Store: poStG_L(Cptr, Vlong) $1 $emStG_L
|
||||
Store: poStG_F(Cptr, Vfloat) $1 $emStG_F
|
||||
Store: poStG_D(Cptr, Vdouble) $1 $emStG_D
|
||||
Store: poStG_A(Cptr, Vptr) $1 $emStG_A
|
||||
Store: poStGI_B(Cptr, Cint) $1 $emStGI_B
|
||||
Store: poStGI_H(Cptr, Cint) $1 $emStGI_H
|
||||
Store: poStGI_I(Cptr, Cint) $1 $emStGI_I
|
||||
Store: poStGI_L(Cptr, Clong) $1 $emStGI_L
|
||||
Store: poStGI_F(Cptr, Cfloat) $1 $emStGI_F
|
||||
Store: poStGI_D(Cptr, Cdouble) $1 $emStGI_D
|
||||
Store: poStGI_A(Cptr, Cptr) $1 $emStGI_A
|
||||
Store: poStV_B(Vptr, Vint) $1 $emStV_B
|
||||
Store: poStV_H(Vptr, Vint) $1 $emStV_H
|
||||
Store: poStV_I(Vptr, Vint) $1 $emStV_I
|
||||
Store: poStV_L(Vptr, Vlong) $1 $emStV_L
|
||||
Store: poStV_F(Vptr, Vfloat) $1 $emStV_F
|
||||
Store: poStV_D(Vptr, Vdouble) $1 $emStV_D
|
||||
Store: poStV_A(Vptr, Vptr) $1 $emStV_A
|
||||
Store: poStVI_B(Vptr, Cint) $1 $emStVI_B
|
||||
Store: poStVI_H(Vptr, Cint) $1 $emStVI_H
|
||||
Store: poStVI_I(Vptr, Cint) $1 $emStVI_I
|
||||
Store: poStVI_L(Vptr, Clong) $1 $emStVI_L
|
||||
Store: poStVI_F(Vptr, Cfloat) $1 $emStVI_F
|
||||
Store: poStVI_D(Vptr, Cdouble) $1 $emStVI_D
|
||||
Store: poStVI_A(Vptr, Cptr) $1 $emStVI_A
|
||||
Store: poStVE_B(Cptr, Vint) $1 $emStVE_B
|
||||
Store: poStVE_H(Cptr, Vint) $1 $emStVE_H
|
||||
Store: poStVE_I(Cptr, Vint) $1 $emStVE_I
|
||||
Store: poStVE_L(Cptr, Vlong) $1 $emStVE_L
|
||||
Store: poStVE_F(Cptr, Vfloat) $1 $emStVE_F
|
||||
Store: poStVE_D(Cptr, Vdouble) $1 $emStVE_D
|
||||
Store: poStVE_A(Cptr, Vptr) $1 $emStVE_A
|
||||
Store: poStVEI_B(Cptr, Cint) $1 $emStVEI_B
|
||||
Store: poStVEI_H(Cptr, Cint) $1 $emStVEI_H
|
||||
Store: poStVEI_I(Cptr, Cint) $1 $emStVEI_I
|
||||
Store: poStVEI_L(Cptr, Clong) $1 $emStVEI_L
|
||||
Store: poStVEI_F(Cptr, Cfloat) $1 $emStVEI_F
|
||||
Store: poStVEI_D(Cptr, Cdouble) $1 $emStVEI_D
|
||||
Store: poStVEI_A(Cptr, Cptr) $1 $emStVEI_A
|
||||
Store: poStVG_B(Cptr, Vint) $1 $emStVG_B
|
||||
Store: poStVG_H(Cptr, Vint) $1 $emStVG_H
|
||||
Store: poStVG_I(Cptr, Vint) $1 $emStVG_I
|
||||
Store: poStVG_L(Cptr, Vlong) $1 $emStVG_L
|
||||
Store: poStVG_F(Cptr, Vfloat) $1 $emStVG_F
|
||||
Store: poStVG_D(Cptr, Vdouble) $1 $emStVG_D
|
||||
Store: poStVG_A(Cptr, Vptr) $1 $emStVG_A
|
||||
Store: poStVGI_B(Cptr, Cint) $1 $emStVGI_B
|
||||
Store: poStVGI_H(Cptr, Cint) $1 $emStVGI_H
|
||||
Store: poStVGI_I(Cptr, Cint) $1 $emStVGI_I
|
||||
Store: poStVGI_L(Cptr, Clong) $1 $emStVGI_L
|
||||
Store: poStVGI_F(Cptr, Cfloat) $1 $emStVGI_F
|
||||
Store: poStVGI_D(Cptr, Cdouble) $1 $emStVGI_D
|
||||
Store: poStVGI_A(Cptr, Cptr) $1 $emStVGI_A
|
||||
|
||||
Store: poSt_I(poAddI_A(Vptr, Cint), Vint) $0 $emSt_IRegisterIndirect
|
||||
Store: poStI_I(poAddI_A(Vptr, Cint), Cint) $0 $emStI_IRegisterIndirect
|
||||
|
||||
// Store, Vint: poMEnter_A(Vptr) $1 $emMEnter_A
|
||||
// Store, Vint: poMEnterG_A(Cptr) $1 $emMEnterG_A
|
||||
// Store, Vint: poMExit_A(Vptr) $1 $emMExit_A
|
||||
// Store, Vint: poMExitG_A(Cptr) $1 $emMExitG_A
|
||||
// Vptr: poLookupV_A(Vptr, Cint) $1 $emLookupV_A
|
||||
// Vptr: poLookupI_A(Vptr, Cint) $1 $emLookupI_A
|
||||
// ...: poSysCall(M) $1 $emSysCall
|
||||
// Store, ...: poSysCallV(M) $1 $emSysCallV
|
||||
// ..., E: poSysCallE(M) $1 $emSysCallE
|
||||
// Store, ..., E: poSysCallEV(M) $1 $emSysCallEV
|
||||
//Store, ..., E: poCall(Va) $1 $emCall
|
||||
|
||||
VInt: poCallI $1 $emCallI_I
|
||||
Vlong: poCallI $1 $emCallI_L
|
||||
Vfloat: poCallI $1 $emCallI_F
|
||||
Vdouble: poCallI $1 $emCallI_D
|
||||
Vptr: poCallI $1 $emCallI_P
|
||||
// Store, ..., E: poCallF(F) $1 $emCallF
|
||||
|
||||
%
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
/* -*- 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.
|
||||
*/
|
||||
// AssembledInstructions.h
|
||||
#ifndef _H_ASSEMBLEDINSTRUCTIONS
|
||||
#define _H_ASSEMBLEDINSTRUCTIONS
|
||||
|
||||
const Uint32 kBlr = 0x4e800020; // blr
|
||||
const Uint32 kBlrl = 0x4e800021; // blrl
|
||||
const Uint32 kMtlrR0 = 0x7c0803a6; // mtlr r0
|
||||
const Uint32 kMtctrR0 = 0x7c0903a6; // mtctr r0
|
||||
const Uint32 kBctr = 0x4e800420; // bctr
|
||||
const Uint32 kOriR0 = 0x60000000; // ori r0, r0, 0
|
||||
const Uint32 kAddiR0 = 0x38000000; // addi r0, r0, 0
|
||||
const Uint32 addisR0 = 0x3C000000; // addis r0, r0, 0
|
||||
|
||||
const Uint32 kBla = 0x48000003; // bla
|
||||
const Uint32 kBl = 0x48000001; // bl
|
||||
const Uint32 kB = 0x48000000; // b
|
||||
const Uint32 kNop = 0x60000000; // nop
|
||||
const Uint32 kLwzR0_R13 = 0x800D0000; // lwz r0, ?(r13)
|
||||
|
||||
inline Uint32
|
||||
shiftLeftMask(Uint32 inValue, Uint32 inShift, Uint32 inMask = 0xFFFFFFFF)
|
||||
{
|
||||
return ((inValue << inShift) & inMask);
|
||||
}
|
||||
|
||||
inline Uint32
|
||||
makeDForm(Uint8 inOpcd, Uint8 inD, Uint8 inA, Int16 inIMM)
|
||||
{
|
||||
return (shiftLeftMask(inOpcd, 26) |
|
||||
shiftLeftMask(inD, 21) |
|
||||
shiftLeftMask(inA, 16) |
|
||||
shiftLeftMask(inIMM, 0, 0xFFFF));
|
||||
}
|
||||
|
||||
const uint32 mfcrR0 = 0x7c000026; // mfcr r0
|
||||
const uint32 stwR04R1 = 0x90010004; // stw r0, 4(r1)
|
||||
const uint32 mflrR0 = 0x7c0802a6; // mflr r0
|
||||
const uint32 stwR08R1 = 0x90010008; // stw r0, 8(r1)
|
||||
const uint32 stfd_FR_offR1 = 0xd8010000; // stfd fr?, ?(r1)
|
||||
const uint32 stw_R_offR1 = 0x90010000; // stw r?, ?(r1)
|
||||
const uint32 stwuR1_offR1 = 0x94210000; // stwu r1, ?(r1)
|
||||
const uint32 lisR12_imm = 0x3d800000; // lis r12, ?
|
||||
const uint32 oriR12R12_imm = 0x618c0000; // ori r12, r12, ?
|
||||
const uint32 stwuxR1R1R12 = 0x7c21616e; // stwux r1, r1, r12
|
||||
|
||||
const uint32 lwzR08R1 = 0x80010008; // lwz r0, 8(r1)
|
||||
const uint32 lwzR04R1 = 0x80010004; // lwz r0, 4(r1)
|
||||
const uint32 mtcrR0 = 0x7c0ff120; // mtcr r0
|
||||
const uint32 lfd_FR_offR1 = 0xc8010000; // lfd fr?, ?(r1)
|
||||
const uint32 lwz_R_offR1 = 0x80010000; // lwz r?, ?(r1)
|
||||
const uint32 addiR1R1_imm = 0x38210000; // addi r1, r1, ?
|
||||
const uint32 addR1R1R12 = 0x7c216214; // add r1, r1, r12
|
||||
|
||||
#endif //_H_ASSEMBLEDINSTRUCTIONS
|
||||
|
|
@ -0,0 +1,92 @@
|
|||
#!gmake
|
||||
#
|
||||
# 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.
|
||||
|
||||
#######################################################################
|
||||
# (1) Directory specific info #
|
||||
#######################################################################
|
||||
|
||||
DEPTH = ../../../..
|
||||
|
||||
CPPSRCS = PPC601AppleMacOSEmitter.cpp \
|
||||
PPCInstructions.cpp \
|
||||
ppc601-macos.nad.burg.cpp \
|
||||
$(NULL)
|
||||
|
||||
LOCAL_MD_EXPORTS_ppc = AssembledInstructions.h \
|
||||
PPC601AppleMacOSEmitter.h \
|
||||
PPC601AppleMacOS_Support.h \
|
||||
PPC601Cpu.h \
|
||||
PPCCalls.h \
|
||||
PPCInstructionTemplates.h \
|
||||
PPCInstructions.h \
|
||||
$(NULL)
|
||||
|
||||
|
||||
|
||||
|
||||
MODULE_NAME = EF
|
||||
|
||||
#######################################################################
|
||||
# (2) Include "component" configuration information. #
|
||||
#######################################################################
|
||||
|
||||
include $(DEPTH)/config/config.mk
|
||||
|
||||
#######################################################################
|
||||
# (3) Include "local" platform-dependent assignments (OPTIONAL). #
|
||||
#######################################################################
|
||||
|
||||
|
||||
#######################################################################
|
||||
# (4) Execute "component" rules. (OPTIONAL) #
|
||||
#######################################################################
|
||||
|
||||
include $(DEPTH)/config/rules.mk
|
||||
|
||||
#######################################################################
|
||||
# (7) Execute "local" rules. (OPTIONAL). #
|
||||
#######################################################################
|
||||
|
||||
#
|
||||
# Rules to obtain burg-generated files
|
||||
#
|
||||
export:: ppc601-macos.nad.burg.cpp
|
||||
|
||||
|
||||
#
|
||||
# Rules to generate ppc601-macos.nad.burg.[cpp][h]
|
||||
#
|
||||
|
||||
ppc601-macos.nad.burg.cpp: ppc601-macos.nad.burg $(BURG)
|
||||
$(BURG) -I -o $@ < $<
|
||||
|
||||
ppc601-macos.nad.burg: ppc601-macos.nad $(DEPTH)/Compiler/PrimitiveGraph/PrimitiveOperations $(DEPTH)/Tools/Nad/nad.pl
|
||||
$(PERL) $(DEPTH)/Tools/Nad/nad.pl $< $(DEPTH)/Compiler/PrimitiveGraph/PrimitiveOperations \
|
||||
$(DEPTH)/Compiler/PrimitiveGraph/PrimitiveOperations.h \
|
||||
$(DEPTH)/Compiler/PrimitiveGraph/PrimitiveOperations.cpp \
|
||||
$<.burg.h > $@
|
||||
|
||||
#
|
||||
# Extra cleaning
|
||||
#
|
||||
|
||||
clobber::
|
||||
rm -f ppc601-macos.nad.burg.cpp ppc601-macos.nad.burg.h ppc601-macos.nad.burg
|
||||
|
||||
realclean clobber_all::
|
||||
rm -f ppc601-macos.nad.burg.cpp ppc601-macos.nad.burg.h ppc601-macos.nad.burg
|
||||
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,190 @@
|
|||
/* -*- 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.
|
||||
*/
|
||||
// PPC601AllMacOSEmitter.h
|
||||
//
|
||||
// Scott M. Silver
|
||||
//
|
||||
|
||||
#ifndef _H_PPC601AllMacOSEmitter
|
||||
#define _H_PPC601AllMacOSEmitter
|
||||
|
||||
#include "InstructionEmitter.h"
|
||||
#include "VirtualRegister.h"
|
||||
#include "Value.h"
|
||||
#include "PPCInstructions.h"
|
||||
#include "PPC601AppleMacOS_Support.h"
|
||||
|
||||
#include "FormatStructures.h" // for FCI, StackFrameInfo
|
||||
|
||||
/* FIX these have been moved to FormatStructures.h
|
||||
|
||||
struct StackFrameInfo
|
||||
{
|
||||
Uint8 GPR_words;
|
||||
Uint8 FPR_words;
|
||||
Uint32 localStore_bytes;
|
||||
|
||||
// returns the offset into the stack frame of the beginning of the restore area
|
||||
Uint32 getRestoreOffset() { return localStore_bytes; }
|
||||
};
|
||||
|
||||
// struct FormattedCodeInfo;
|
||||
*/
|
||||
|
||||
template<bool tHasIncomingStore, bool tHasOutgoingStore, bool tHasFunctionAddress, bool tIsDynamic> class Call;
|
||||
|
||||
class PPCEmitter :
|
||||
public InstructionEmitter
|
||||
{
|
||||
// these Call Instructions set "themselves" up, ie they call "useProducer"
|
||||
friend class Call<true, true, false, false>;
|
||||
friend class Call<true, false, false, false>;
|
||||
friend class Call<false, false, false, false>;
|
||||
friend class Call<true, true, true, false>;
|
||||
friend class Call<true, true, true, true>;
|
||||
friend class MacDebugger;
|
||||
|
||||
friend class PPCFormatter; // uses protected member variables for linkage, etc
|
||||
|
||||
protected:
|
||||
uint32 mMaxArgWords; // maximum words needed in call build area (accumulated)
|
||||
uint32 mCrossTOCPtrGlCount; // number of ptr glue's needed (all are cross-toc now, even if not needed)
|
||||
JitGlobals mAccumulatorTOC; // TOC which contains all the data extracted at emit time, transferred to actual TOC
|
||||
// when formatting (we need to make sure we have a TOC which can contain all of the
|
||||
// necessary data)
|
||||
|
||||
public:
|
||||
PPCEmitter(Pool& inPool, VirtualRegisterManager& inVrManager) :
|
||||
InstructionEmitter(inPool, inVrManager),
|
||||
mCrossTOCPtrGlCount(0),
|
||||
mMaxArgWords(0) { }
|
||||
|
||||
virtual void emitPrimitive(Primitive& inPrimitive, NamedRule inRule);
|
||||
bool emitCopyAfter(DataNode& inDataNode, InstructionList::iterator where, VirtualRegister& fromVr, VirtualRegister& toVr);
|
||||
void emitLoadAfter(DataNode& inDataNode, InstructionList::iterator where, VirtualRegister& loadedReg, VirtualRegister& stackReg);
|
||||
void emitStoreAfter(DataNode& inDataNode, InstructionList::iterator where, VirtualRegister& storedReg, VirtualRegister& stackReg);
|
||||
Instruction& emitAbsoluteBranch(DataNode& inDataNode, ControlNode& inTarget);
|
||||
void emitArguments(ControlNode::BeginExtra& inBeginNode);
|
||||
|
||||
private:
|
||||
void emit_LimitR(Primitive& inPrimitive);
|
||||
void emit_Limit(Primitive& inPrimitive, PPCInsnFlags inFlags);
|
||||
void emit_Const_I(Primitive& inPrimitive);
|
||||
void emit_Const_F(Primitive& inPrimitive);
|
||||
void emit_Ld_IRegisterIndirect(Primitive& inPrimitive);
|
||||
void emit_Add_I(Primitive& inPrimitive);
|
||||
void emit_AddI_I(Primitive& inPrimitive);
|
||||
|
||||
void emit_Div_I(Primitive& inPrimitive);
|
||||
void emit_DivE_I(Primitive& inPrimitive);
|
||||
void emit_DivI_I(Primitive& inPrimitive);
|
||||
void emit_DivR_I(Primitive& inPrimitive);
|
||||
void emit_DivRE_I(Primitive& inPrimitive);
|
||||
void emit_DivU_I(Primitive& inPrimitive);
|
||||
void emit_DivUE_I(Primitive& inPrimitive);
|
||||
void emit_DivUI_I(Primitive& inPrimitive);
|
||||
void emit_DivUR_I(Primitive& inPrimitive);
|
||||
void emit_DivURE_I(Primitive& inPrimitive);
|
||||
|
||||
void emit_Mod_I(Primitive& inPrimitive);
|
||||
void emit_ModE_I(Primitive& inPrimitive);
|
||||
|
||||
void emit_CmpI_I(Primitive& inPrimitive);
|
||||
void emit_Cmp_I(Primitive& inPrimitive);
|
||||
void emit_StI_I(Primitive& inPrimitive);
|
||||
void emit_StI_IRegisterIndirect(Primitive& inPrimitive);
|
||||
void emit_St_IRegisterIndirect(Primitive& inPrimitive);
|
||||
void emit_ShlI_I(Primitive& inPrimitive);
|
||||
void emit_Call(Primitive& inPrimitive);
|
||||
void emit_Result_I(Primitive& inPrimitive);
|
||||
void emit_Result_F(Primitive& inPrimitive);
|
||||
|
||||
void genBranch(Primitive& inPrimitive, Condition2 cond);
|
||||
void genLoadIZero(Primitive& inPrimitive, PPCInsnFlags inFlags);
|
||||
void genArith_ID(Primitive& inPrimitive, DFormKind inKind, XFormKind inXKind);
|
||||
void genArith_IX(Primitive& inPrimitive, XFormKind inKind);
|
||||
void genArith_RD(Primitive& inPrimitive, DFormKind inKind, XFormKind inXKind, bool inIsUnsigned);
|
||||
void genArithReversed_IX(Primitive& inPrimitive, XFormKind inKind);
|
||||
void emit_SubR(Primitive& inPrimitive, bool inUnsigned);
|
||||
void emit_SubI(Primitive& inPrimitive);
|
||||
|
||||
void genThrowIfDivisorZero(Primitive& inPrimitive);
|
||||
|
||||
protected:
|
||||
VirtualRegister& genLoadConstant_I(DataNode& inPrimitive, Int32 inConstant);
|
||||
VirtualRegister& genLoadConstant_F(DataNode& inPrimitive, Flt32 inConstant);
|
||||
};
|
||||
|
||||
class PPCFormatter
|
||||
{
|
||||
private:
|
||||
StackFrameInfo mStackPolicy;
|
||||
|
||||
protected:
|
||||
void* mNextFreePostMethod; // next free block of memory following the method's epilog (valid after beginFormatting)
|
||||
JitGlobals* mRealTOC; // contains this methods actual TOC (valid after beginFormatting)
|
||||
Int16 mRealTOCOffset; // this is the "fix-up" added to an offset obtained from the accumulator TOC (valid after beginFormatting)
|
||||
|
||||
uint8 mSaveGPRwords; // number of non-volatile GPR's used in the function (valid after calculatePrologEpilog)
|
||||
uint8 mSaveFPRwords; // number of non-volatile FPR's used in the function (valid after calculatePrologEpilog)
|
||||
uint32 mFrameSizeWords; // whole size of frame (valid after calculatePrologEpilog)
|
||||
|
||||
// these Instructions need access to mNextFreePostMethod and the real TOC
|
||||
friend class Call<true, true, false, false>;
|
||||
friend class Call<true, false, false, false>;
|
||||
friend class Call<false, false, false, false>;
|
||||
friend class Call<true, true, true, false>;
|
||||
friend class Call<true, true, true, true>;
|
||||
friend class MacDebugger;
|
||||
|
||||
// Fix-up for real TOC
|
||||
friend class LdD_RTOC;
|
||||
|
||||
public:
|
||||
const PPCEmitter& mEmitter;
|
||||
|
||||
PPCFormatter(const MdEmitter& inEmitter) :
|
||||
mEmitter(inEmitter) { }
|
||||
|
||||
void calculatePrologEpilog(Method& inMethod, Uint32& outPrologSize, Uint32& outEpilogSize);
|
||||
void formatEpilogToMemory(void* inWhere);
|
||||
void formatPrologToMemory(void* inWhere);
|
||||
|
||||
void beginFormatting(const FormattedCodeInfo& inInfo);
|
||||
void endFormatting(const FormattedCodeInfo& /*inInfo*/) { }
|
||||
void calculatePrePostMethod(Method& inMethod, Uint32& outPreMethodSize, Uint32& outPostMethodSize);
|
||||
void formatPostMethodToMemory(void* inWhere, const FormattedCodeInfo& inInfo);
|
||||
void formatPreMethodToMemory(void* /*inWhere*/, const FormattedCodeInfo& /*inInfo*/) { }
|
||||
|
||||
void initStackFrameInfo() {}
|
||||
|
||||
Uint8* createTransitionVector(const FormattedCodeInfo& /*inInfo*/);
|
||||
|
||||
StackFrameInfo& getStackFrameInfo() {return mStackPolicy; }
|
||||
|
||||
};
|
||||
|
||||
#ifdef DEBUG
|
||||
void *disassemble1(LogModuleObject &f, void* inFrom);
|
||||
#endif
|
||||
|
||||
#endif // _H_PPC601AllMacOSEmitter
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,471 @@
|
|||
/* -*- 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.
|
||||
*/
|
||||
// PPC601AppleMacOS_Support.cpp.
|
||||
//
|
||||
// Scott M. Silver
|
||||
//
|
||||
// Support for the "Just-In-Time" part of the compiler
|
||||
// Also a handful of routines for dealing with PowerPC linkage oddities (ptr gl, etc)
|
||||
|
||||
#include "NativeCodeCache.h"
|
||||
#include "NativeFormatter.h"
|
||||
#include "PPC601AppleMacOS_Support.h"
|
||||
#include "NativeFormatter.h"
|
||||
#include "AssembledInstructions.h"
|
||||
#include "JavaObject.h"
|
||||
|
||||
|
||||
// All volatile registers
|
||||
struct VolatileRegisters
|
||||
{
|
||||
Uint32 r3; Uint32 r4; Uint32 r5; Uint32 r6;
|
||||
Uint32 r7; Uint32 r8; Uint32 r9; Uint32 r10;
|
||||
Flt64 fp0; Flt64 fp1; Flt64 fp2; Flt64 fp3;
|
||||
Flt64 fp4; Flt64 fp5; Flt64 fp6; Flt64 fp7;
|
||||
Flt64 fp8; Flt64 fp9; Flt64 fp10; Flt64 fp11;
|
||||
Flt64 fp12; Flt64 fp13; Flt64 r31;
|
||||
};
|
||||
|
||||
static void*
|
||||
formatDynamicPtrGl(void* inStart, uint8 inRegister);
|
||||
|
||||
static void
|
||||
formatCompileStubPtrGl(void* inStart);
|
||||
|
||||
asm static void
|
||||
locate();
|
||||
|
||||
asm void
|
||||
saveVolatiles(VolatileRegisters *vr);
|
||||
|
||||
asm void
|
||||
restoreVolatiles(VolatileRegisters *vr);
|
||||
|
||||
const uint32 kDynamicPtrGlBytes = 20; // see formatDynamicPtrGl
|
||||
|
||||
// formatDynamicPtrGl
|
||||
//
|
||||
// Output some pointer glue through register inRegister
|
||||
// beginning at inStart in memory. We require that
|
||||
// the size of the buffer be at least kDynamicPtrGlBytes
|
||||
// long. This kind of pointer glue is used for
|
||||
// dispatching v-table based (dynamic) calls.
|
||||
static void*
|
||||
formatDynamicPtrGl(void* inStart, Uint8 inRegister)
|
||||
{
|
||||
Uint32* curPC = (Uint32*) inStart;
|
||||
|
||||
*curPC++ = makeDForm(36, 2, 1, 20); // stw rtoc, 20(sp)
|
||||
*curPC++ = makeDForm(32, 0, inRegister, 0); // lwz r0, 0(inRegister)
|
||||
*curPC++ = makeDForm(32, 2, inRegister, 4); // lwz rtoc, 4(inRegister);
|
||||
*curPC++ = kMtctrR0; // mtctr r0
|
||||
*curPC++ = kBctr; // bctr
|
||||
return (curPC);
|
||||
}
|
||||
|
||||
|
||||
// formatCrossTocPtrGl
|
||||
//
|
||||
// Output cross-toc ptr gl beginning at inStart.
|
||||
// Use inTOCOffset at the offset in the TOC to find
|
||||
// the callee's TVector ptr. The buffer beginning at inStart
|
||||
// must be at least kCrossTocPtrGlBytes long.
|
||||
void*
|
||||
formatCrossTocPtrGl(void* inStart, int16 inTOCOffset)
|
||||
{
|
||||
uint32* curPC = (uint32*) inStart;
|
||||
|
||||
*curPC++ = makeDForm(32, 12, 2, inTOCOffset); // lwz r12, offset(rtoc)
|
||||
*curPC++ = makeDForm(36, 2, 1, 20); // stw rtoc, 20(sp)
|
||||
*curPC++ = makeDForm(32, 0, 12, 0); // lwz r0, 0(r12)
|
||||
*curPC++ = makeDForm(32, 2, 12, 4); // lwz rtoc, 4(r12);
|
||||
*curPC++ = kMtctrR0; // mtctr r0
|
||||
*curPC++ = kBctr; // bctr
|
||||
return (curPC);
|
||||
}
|
||||
|
||||
|
||||
const uint32 kCompileStubPtrGlBytes = 12; // see formatCompileStubPtrGl
|
||||
|
||||
// formatCompileStubPtrGl
|
||||
//
|
||||
// Output a short stub beginng at inStart which loads
|
||||
// the address of "locate" and jumps to it. The TOC must be set to
|
||||
// the system's TOC before this routine is called (which is why a compile stub
|
||||
// has a TVector with it's TOC set to the system TOC). inStart is
|
||||
// assumbed to be a buffer of atleast kCompileStubPtrGlBytes.
|
||||
static void
|
||||
formatCompileStubPtrGl(void* inStart)
|
||||
{
|
||||
uint32* curPC = (uint32*) inStart;
|
||||
|
||||
// this is some cruft to locate the offset (from C) to locate's TVector
|
||||
uint8* startTOC = ((uint8**) formatCompileStubPtrGl)[1]; // grab our TOC
|
||||
uint8* compileStubFunction = (uint8*) locate; // grab &locate (which is ptr to TVector)
|
||||
|
||||
*curPC++ = makeDForm(32, 0, 2, compileStubFunction - startTOC); // lwz r0, locate(RTOC)
|
||||
*curPC++ = kMtctrR0; // mtctr r0
|
||||
*curPC++ = kBctr; // bctr
|
||||
}
|
||||
|
||||
|
||||
// DynamicPtrGlManager
|
||||
//
|
||||
// Manages a hunk of dynamic ptr gl
|
||||
// FIX-ME synchronization
|
||||
class DynamicPtrGlManager
|
||||
{
|
||||
public:
|
||||
enum
|
||||
{
|
||||
kFirstPossibleRegister = 4, // since r3 must be the this ptr and can never be used for dynamic dispatching
|
||||
kLastPossibleRegister = 31,
|
||||
kTotalPtrGls = kLastPossibleRegister - kFirstPossibleRegister + 1
|
||||
};
|
||||
|
||||
DynamicPtrGlManager()
|
||||
{
|
||||
void* allPtrGls = NativeCodeCache::getCache().acquireMemory(kTotalPtrGls * kDynamicPtrGlBytes);
|
||||
mPtrGl = new void*[kTotalPtrGls];
|
||||
|
||||
void** curPtrGl = mPtrGl;
|
||||
|
||||
for (int curReg = kFirstPossibleRegister; curReg < kLastPossibleRegister + 1; curReg++)
|
||||
{
|
||||
*curPtrGl++ = allPtrGls;
|
||||
allPtrGls = formatDynamicPtrGl(allPtrGls, curReg);
|
||||
}
|
||||
}
|
||||
|
||||
void *
|
||||
getGl(void* DEBUG_ONLY(inFromWhere), Uint8 inWhichRegister)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
Int32 dummy;
|
||||
#endif
|
||||
|
||||
assert(inWhichRegister >= kFirstPossibleRegister && inWhichRegister <= kLastPossibleRegister);
|
||||
|
||||
// FIX-ME allocate new ptr glues if this fails
|
||||
assert(canBePCRelative(inFromWhere, mPtrGl[inWhichRegister - kFirstPossibleRegister], dummy));
|
||||
|
||||
return (mPtrGl[inWhichRegister - kFirstPossibleRegister]);
|
||||
}
|
||||
|
||||
|
||||
void** mPtrGl;
|
||||
};
|
||||
|
||||
DynamicPtrGlManager sPtrGlueManager;
|
||||
|
||||
void* getDynamicPtrGl(void* inFromWhere, Uint8 inWhichRegister)
|
||||
{
|
||||
return (sPtrGlueManager.getGl(inFromWhere, inWhichRegister));
|
||||
}
|
||||
|
||||
|
||||
// getCompileStubPtrGl
|
||||
//
|
||||
// Locate a compile stub ptr gl withing pc-relative range of inFromWhere
|
||||
// If one does not exist, create one. Assume that the newly created
|
||||
// one will be in range. We also assume we will be reusing this often
|
||||
// due to (hopefully) monotonically increasing addresses of new blocks
|
||||
// of memory. FIX-ME?
|
||||
//
|
||||
// sMostRecentCompileStubPtrGl is maintained such that it is a single
|
||||
// entry cache for the last CompileStubPtrGl we generated.
|
||||
//
|
||||
// A compileStubPtrGl is a sequence of instructions which jumps to
|
||||
// the "locate" function which, in some way, locates/compiles/loads, etc
|
||||
// a currently uncompiled method.
|
||||
void* getCompileStubPtrGl(void* inFromWhere)
|
||||
{
|
||||
Int32 dummyOffset;
|
||||
static void* sMostRecentCompileStubPtrGl = NULL; // This is a global!
|
||||
|
||||
// make a new ptr glue for the compile stub
|
||||
if ((sMostRecentCompileStubPtrGl == NULL) || (!canBePCRelative(inFromWhere, sMostRecentCompileStubPtrGl, dummyOffset)))
|
||||
{
|
||||
void* newPtrGl = NativeCodeCache::getCache().acquireMemory(kCompileStubPtrGlBytes);
|
||||
formatCompileStubPtrGl(newPtrGl);
|
||||
sMostRecentCompileStubPtrGl = newPtrGl;
|
||||
}
|
||||
|
||||
// assert that we actually succeeded in our goal, this really
|
||||
// should result in programattic failure (abort, etc)
|
||||
bool compileStubAssertion = canBePCRelative(inFromWhere, sMostRecentCompileStubPtrGl, dummyOffset);
|
||||
assert(compileStubAssertion);
|
||||
|
||||
return (sMostRecentCompileStubPtrGl);
|
||||
}
|
||||
|
||||
|
||||
// _MD_createTVector
|
||||
//
|
||||
// Create a "transition vector" a two word entry containing a ptr to inFunctionMemory
|
||||
// (the beginning of a compiled function) and a second word containing the environment
|
||||
// or TOC pointer...currently we always use the system TOC. All other TVector's should
|
||||
// be acquired from the Formatter objects.
|
||||
void* _MD_createTVector(const MethodDescriptor& /*inMethodDescriptor*/, void* inFunctionMemory)
|
||||
{
|
||||
TVector* newTVector = (TVector*) NativeCodeCache::getCache().acquireMemory(sizeof(TVector));
|
||||
newTVector->functionPtr = inFunctionMemory;
|
||||
newTVector->toc = *((void**)_MD_createTVector + 1);
|
||||
|
||||
return (newTVector);
|
||||
}
|
||||
|
||||
|
||||
// BackPatchInfo
|
||||
//
|
||||
// little structure used to communicate between the assembly
|
||||
// stub and the actual compilation and backpatching of the caller
|
||||
struct BackPatchInfo
|
||||
{
|
||||
void** callerTOC; // caller's TOC
|
||||
JavaObject* thisPtr; // callee this ptr (if a dynamic dispatch, otherwise undefined)
|
||||
};
|
||||
|
||||
|
||||
// backPatchMethod
|
||||
//
|
||||
// Cause the caller to call inMethodAddress next time the call instruction
|
||||
// is encountered (instead of going through the stub). inLastPC is the next
|
||||
// PC to be executed upon return from the intended callee (just after the call
|
||||
// instruction. inUserData is assumed to point to a BackPatchInfo*. (above)
|
||||
//
|
||||
// The trick is, that this has to be atomic. That is, atomic from the standpoint
|
||||
// that any thread entering the caller must not crash, but it would be ok for the
|
||||
// thread to end up in the compile stub.
|
||||
//
|
||||
// For backpatching vtables, we call a routine on the method object which
|
||||
// does all our dirty work. (It's non-trivial since the same method can occupy
|
||||
// different positions in a vtable due to interfaces).
|
||||
// We do the work for static dispatches. Our strategy is to try to use the cheapest
|
||||
// call-like instruction. See the "General Idea" section at the top of this file
|
||||
// for details of this strategy.
|
||||
void* backPatchMethod(void* inMethodAddress, void* inLastPC, void* inUserData)
|
||||
{
|
||||
TVector* methodTVector = (TVector*) inMethodAddress;
|
||||
Uint32* nopOrRestoreToc = (Uint32*) inLastPC;
|
||||
BackPatchInfo* backPatchInfo = (BackPatchInfo*) inUserData;
|
||||
|
||||
// find bl
|
||||
Uint32* blToPtrGlue = nopOrRestoreToc - 1;
|
||||
assert ((*blToPtrGlue & kBl) == kBl);
|
||||
|
||||
// see where the destination of this bl -- it must be some kind of ptrgl
|
||||
Int16 offsetToPtrGlue = (Int16) (*blToPtrGlue & 0x000FFFC);
|
||||
Uint32* ptrGlue = (Uint32*) (((Uint8*) (nopOrRestoreToc - 1)) + offsetToPtrGlue);
|
||||
|
||||
if (*ptrGlue == makeDForm(36, 2, 1, 20)) // stw rtoc, 20(sp)
|
||||
{
|
||||
// dynamic call
|
||||
Uint32* dynamicLookup = blToPtrGlue - 1;
|
||||
|
||||
assert ((*dynamicLookup & makeDForm(32, 0, 0, 0)) == makeDForm(32, 0, 0, 0)); // lwz r?, offset(rtoc)
|
||||
Int16 vtableOffset = *dynamicLookup & 0xFFFF; // grab offset in table
|
||||
|
||||
// replace value in vtable
|
||||
((void**)&backPatchInfo->thisPtr->type)[vtableOffset >> 2] = inMethodAddress;
|
||||
}
|
||||
else
|
||||
{
|
||||
// static call
|
||||
assert ((*ptrGlue & makeDForm(32, 12, 2, 0)) == makeDForm(32, 12, 2, 0)); // lwz r12, offset(rtoc)
|
||||
|
||||
Int32 offsetToCallee;
|
||||
bool sameTOC = (backPatchInfo->callerTOC == methodTVector->toc);
|
||||
|
||||
// first try an absolute branch
|
||||
// then try a pc-relative branch
|
||||
// last use the ptr glue (just change the entry in the TOC)
|
||||
if (sameTOC && (Uint32) methodTVector->functionPtr < 0x3FFFFFF)
|
||||
{
|
||||
*blToPtrGlue = kBla | ( (Uint32) methodTVector->functionPtr & 0x03FFFFFF);
|
||||
blToPtrGlue[1] = kNop;
|
||||
}
|
||||
else if (sameTOC && canBePCRelative(blToPtrGlue, methodTVector->functionPtr, offsetToCallee))
|
||||
{
|
||||
*blToPtrGlue = (kBl | offsetToCallee); // change bl to branch directly to callee
|
||||
blToPtrGlue[1] = kNop; // don't restore toc, same toc
|
||||
}
|
||||
else
|
||||
{
|
||||
Int16 tocOffset = *ptrGlue & 0xFFFF; // grab offset in table
|
||||
|
||||
backPatchInfo->callerTOC[tocOffset] = inMethodAddress; // now change the ptr in the table
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return (inMethodAddress);
|
||||
}
|
||||
|
||||
|
||||
// locate
|
||||
//
|
||||
// Assembly glue called by the locateStub to actually locate/compile/etc a
|
||||
// method and back patch the caller. We save all volatiles (and
|
||||
// of course save non-volatiles if we use them), fill in a BackPatchInfo
|
||||
// object, and hand off control to compileAndBackPatchMethod. Upon return
|
||||
// we restore volatiles, fix up the stack (so it appears we were at the
|
||||
// callsite in the caller), and jump to the intended callee (returned by
|
||||
// compileAndBackPatchMethod).
|
||||
//
|
||||
// Only one parameter is assumed, r11 = ptr to CacheEntry of method to be located.
|
||||
// We chose r11 because it is volatile, but not used by any ptr-gl generated by
|
||||
// the Metrowerks Compilers. We may have to do something slightly different for
|
||||
// AIX. We may just push the cacheEntry on the stack in the stub, and fix up
|
||||
// the stack in here.
|
||||
//
|
||||
// Currently we only "locate" methods by compilation
|
||||
asm static void locate()
|
||||
{
|
||||
register CacheEntry* cacheEntry;
|
||||
register void* oldLR;
|
||||
register TVector* compiledMethod;
|
||||
BackPatchInfo backPatchInfo;
|
||||
VolatileRegisters volatileRegisters;
|
||||
|
||||
fralloc
|
||||
mr cacheEntry, r11 // save r11 somewhere else, in case we end up in ptrgl somewhere
|
||||
mflr oldLR
|
||||
|
||||
// save off volatiles
|
||||
stw r3, backPatchInfo.thisPtr // r3 is saved in backPatchInfo (it is the thisPtr sometimes)
|
||||
la r3, volatileRegisters // save rest of volatiles
|
||||
bl saveVolatiles
|
||||
|
||||
// fill in the rest of backPatchInfo
|
||||
lwz r5, 260(SP) // caller's RTOC (260 is a special constant, dependent on amt of stack space used in this function)
|
||||
stw r5, backPatchInfo.callerTOC
|
||||
|
||||
// compileAndBackPatch
|
||||
mr r3, cacheEntry
|
||||
mr r4, oldLR
|
||||
la r5, backPatchInfo
|
||||
bl compileAndBackPatchMethod // r3 = TVector to actual method
|
||||
mr compiledMethod, r3 // compiledMethod = TVector to actual method
|
||||
|
||||
// restore volatiles
|
||||
la r3, volatileRegisters
|
||||
bl restoreVolatiles
|
||||
lwz r3, backPatchInfo.thisPtr // restore r3
|
||||
|
||||
// jmp to the real callee
|
||||
lwz r0, compiledMethod->functionPtr // r0 = ptr to method
|
||||
lwz RTOC, compiledMethod->toc // RTOC = toc of compiled method
|
||||
mtctr r0 // ctr = ptr to method
|
||||
frfree
|
||||
bctr
|
||||
}
|
||||
|
||||
|
||||
// saveVolatiles
|
||||
//
|
||||
// Save all non-volatiles except r3 (because it is used for the incoming parameter)
|
||||
asm void saveVolatiles(register VolatileRegisters *vr)
|
||||
{
|
||||
// stw r3, vr->r3
|
||||
stfd fp0, vr->fp0
|
||||
stw r4, vr->r4
|
||||
stfd fp1, vr->fp1
|
||||
stw r5, vr->r5
|
||||
stfd fp2, vr->fp2
|
||||
stw r6, vr->r6
|
||||
stfd fp3, vr->fp3
|
||||
stw r7, vr->r7
|
||||
stfd fp4, vr->fp4
|
||||
stw r8, vr->r8
|
||||
stfd fp5, vr->fp5
|
||||
stw r9, vr->r9
|
||||
stfd fp6, vr->fp6
|
||||
stw r10, vr->r10
|
||||
stfd fp7, vr->fp7
|
||||
stfd fp8, vr->fp8
|
||||
stfd fp9, vr->fp9
|
||||
stfd fp10, vr->fp10
|
||||
stfd fp11, vr->fp11
|
||||
stfd fp12, vr->fp12
|
||||
stfd fp13, vr->fp13
|
||||
blr
|
||||
}
|
||||
|
||||
// restoreVolatiles
|
||||
//
|
||||
// Restore all non-volatiles except r3 (because it is used for the incoming parameter)
|
||||
asm void restoreVolatiles(register VolatileRegisters *vr)
|
||||
{
|
||||
// lwz r3, vr->r3
|
||||
lfd fp0, vr->fp0
|
||||
lwz r4, vr->r4
|
||||
lfd fp1, vr->fp1
|
||||
lwz r5, vr->r5
|
||||
lfd fp2, vr->fp2
|
||||
lwz r6, vr->r6
|
||||
lfd fp3, vr->fp3
|
||||
lwz r7, vr->r7
|
||||
lfd fp4, vr->fp4
|
||||
lwz r8, vr->r8
|
||||
lfd fp5, vr->fp5
|
||||
lwz r9, vr->r9
|
||||
lfd fp6, vr->fp6
|
||||
lwz r10, vr->r10
|
||||
lfd fp7, vr->fp7
|
||||
lfd fp8, vr->fp8
|
||||
lfd fp9, vr->fp9
|
||||
lfd fp10, vr->fp10
|
||||
lfd fp11, vr->fp11
|
||||
lfd fp12, vr->fp12
|
||||
lfd fp13, vr->fp13
|
||||
blr
|
||||
}
|
||||
|
||||
|
||||
void *
|
||||
generateNativeStub(NativeCodeCache& /*inCache*/, const CacheEntry& /*inCacheEntry*/, void * /*nativeFunction*/)
|
||||
{
|
||||
trespass("Not implemented");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// generateCompileStub
|
||||
//
|
||||
// Genereate a kLocateStubBytes stub from memory acquired fromn inCache.
|
||||
// The branches to some ptr-gl which jumps to the actual "locate" routine.
|
||||
// It is assumed that there is already a reachable compileStubPtrGl, or one
|
||||
// can be created by calling getCompileStubPtrGl.
|
||||
extern void*
|
||||
generateCompileStub(NativeCodeCache& inCache, const CacheEntry& inCacheEntry)
|
||||
{
|
||||
const size_t kLocateStubBytes = 12;
|
||||
|
||||
Uint32* stub = (Uint32*) inCache.acquireMemory(kLocateStubBytes);
|
||||
Uint32* curPC = stub;
|
||||
|
||||
*curPC++ = makeDForm(15, 11, 0, ((Uint32) &inCacheEntry) >> 16); // addis r11, r0, hiword
|
||||
*curPC++ = makeDForm(24, 11, 11, ((Uint32) &inCacheEntry) & 0xFFFF); // ori r11, r11, loword
|
||||
*curPC = (kB | (((PRUptrdiff) getCompileStubPtrGl(curPC) - (PRUptrdiff) curPC) & 0x03FFFFFF)); // b compileStubPtrGl
|
||||
|
||||
return (_MD_createTVector(inCacheEntry.descriptor, stub));
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,163 @@
|
|||
/* -*- 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.
|
||||
*/
|
||||
// PPC601AppleMacOS_Support.h
|
||||
//
|
||||
// Scott M. Silver
|
||||
|
||||
#ifndef _H_PPC601APPLEMACOS_SUPPORT
|
||||
#define _H_PPC601APPLEMACOS_SUPPORT
|
||||
#include <string.h>
|
||||
|
||||
void* getDynamicPtrGl(void* inFromWhere, Uint8 inWhichRegister);
|
||||
|
||||
// JitGlobals
|
||||
//
|
||||
// Maintains a table of size 2^16
|
||||
// 16 bit signed offsets are used to load data out of the table
|
||||
// The gp (or rtoc), therefore, points to the middle of the table
|
||||
class JitGlobals
|
||||
{
|
||||
public:
|
||||
enum
|
||||
{
|
||||
kTOCSize = 0x10000
|
||||
};
|
||||
|
||||
JitGlobals()
|
||||
{
|
||||
mBegin = (const Uint8*) new Uint8[kTOCSize];
|
||||
mGlobalPtr = (const Uint8*) mBegin + (kTOCSize >> 1);
|
||||
mEnd = (const Uint8*) mBegin + kTOCSize;
|
||||
mNext = (Uint8*) mBegin;
|
||||
}
|
||||
|
||||
~JitGlobals()
|
||||
{
|
||||
delete mBegin;
|
||||
}
|
||||
|
||||
public:
|
||||
// addData
|
||||
// Add data beginning at inData of size inSize
|
||||
// to the JitGlobals. Returns the address of the
|
||||
// copied data in JG or NULL if the globals are full.
|
||||
// outOffset is the offset in the table where the data
|
||||
// was added
|
||||
void* addData(const void* inData, Uint16 inSize, Uint16 &outOffset)
|
||||
{
|
||||
// ¥¥ FIX-ME BEGIN LOCK
|
||||
Uint16 alignedSize = (inSize + 3) & ~3; // align to 4 bytes
|
||||
|
||||
Uint8* dest = mNext;
|
||||
|
||||
// make sure we didn't overflow the tbl
|
||||
if (mNext + alignedSize > mEnd)
|
||||
return (NULL);
|
||||
|
||||
// copy data and return offset into table
|
||||
memcpy(dest, inData, inSize);
|
||||
outOffset = dest - mGlobalPtr;
|
||||
mNext += alignedSize;
|
||||
|
||||
return (dest);
|
||||
// ¥¥ÊFIX-ME END LOCK
|
||||
}
|
||||
|
||||
// toOffset
|
||||
// Returnns true if inData is in JG. outOffset
|
||||
// is the offset to the data if its in the table.
|
||||
bool toOffset(void* inData, Uint16& outOffset)
|
||||
{
|
||||
if (inData >= (void*)mBegin && inData < (void*)mNext)
|
||||
{
|
||||
outOffset = (Uint8*) inData - mBegin;
|
||||
return (true);
|
||||
}
|
||||
else
|
||||
return (false);
|
||||
}
|
||||
|
||||
// unsafe, only use if you are the only thread
|
||||
// who will be touching this table
|
||||
inline Uint8* getNext() const { return mNext; }
|
||||
|
||||
public:
|
||||
const Uint8* mBegin; // beginning of table
|
||||
const Uint8* mEnd; // end of table
|
||||
const Uint8* mGlobalPtr; // what the "global ptr" should point to
|
||||
private:
|
||||
Uint8* mNext; // next free entry in table
|
||||
};
|
||||
|
||||
struct TVector
|
||||
{
|
||||
void* functionPtr;
|
||||
void* toc;
|
||||
};
|
||||
|
||||
extern JitGlobals gJitGlobals;
|
||||
|
||||
//bool canBePCRelative(void* inStart, void* inDest, Int32& outOffset);
|
||||
|
||||
|
||||
// canBePCRelative
|
||||
//
|
||||
inline bool canBePCRelative(void* inStart, void* inDest, Int32& outOffset)
|
||||
{
|
||||
outOffset = (PRUptrdiff) inDest - (PRUptrdiff) inStart;
|
||||
|
||||
if (inDest > inStart)
|
||||
return (outOffset < 0x1ffffff);
|
||||
else
|
||||
return (outOffset > (Int32) 0xfe000000);
|
||||
}
|
||||
|
||||
extern JitGlobals&
|
||||
acquireTOC(Uint16 inDesiredAmount);
|
||||
|
||||
void* getCompileStubPtrGl(void* inFromWhere);
|
||||
const uint32 kCrossTocPtrGlBytes = 24;
|
||||
|
||||
void* formatCrossTocPtrGl(void* inStart, int16 inTOCOffset);
|
||||
|
||||
#ifndef XP_MAC
|
||||
inline void* getDynamicPtrGl(void* /* inFromWhere */, Uint8 /* inWhichRegister */)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
inline void* formatCrossTocPtrGl(void* /* inStart */, int16 /* inTOCOffset */)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
// acquireTOC
|
||||
//
|
||||
// Try to find a TOC with enough space left in it to fit a desired amount.
|
||||
// If one cannot be located, create a new TOC and return that.
|
||||
//
|
||||
// FIX-ME - right now we only support one TOC
|
||||
inline JitGlobals&
|
||||
acquireTOC(Uint16 /*inDesiredAmount*/)
|
||||
{
|
||||
static JitGlobals sJitGlobals; // This is a global
|
||||
|
||||
return (sJitGlobals);
|
||||
}
|
||||
|
||||
#endif // _H_PPC601APPLEMACOS_SUPPORT
|
|
@ -0,0 +1,48 @@
|
|||
/* -*- 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.
|
||||
*/
|
||||
|
||||
#if !defined(_PPC601_CPU_H_) || defined(INCLUDE_EMITTER)
|
||||
#define _PPC601_CPU_H_
|
||||
|
||||
#define FIRST_CALLER_SAVED_GR 0
|
||||
#define LAST_CALLER_SAVED_GR 9
|
||||
#define FIRST_CALLEE_SAVED_GR 10
|
||||
#define LAST_CALLEE_SAVED_GR 28
|
||||
#define FIRST_CALLER_SAVED_FPR 0
|
||||
#define LAST_CALLER_SAVED_FPR 9
|
||||
#define FIRST_CALLEE_SAVED_FPR 10
|
||||
#define LAST_CALLEE_SAVED_FPR 28
|
||||
|
||||
#define FIRST_GREGISTER 0
|
||||
#define LAST_GREGISTER 28
|
||||
#define FIRST_FPREGISTER 0
|
||||
#define LAST_FPREGISTER 28
|
||||
#define NUMBER_OF_SPECIAL_REGISTERS 0
|
||||
|
||||
class PPCEmitter;
|
||||
class PPCFormatter;
|
||||
typedef PPCFormatter MdFormatter;
|
||||
typedef PPCEmitter MdEmitter;
|
||||
|
||||
#ifdef INCLUDE_EMITTER
|
||||
#include "PPC601AppleMacOSEmitter.h"
|
||||
#endif
|
||||
|
||||
#define CPU_IS_SUPPORTED
|
||||
|
||||
#endif /* _PPC601_CPU_H_ */
|
|
@ -0,0 +1,313 @@
|
|||
/* -*- 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.
|
||||
*/
|
||||
// PPCCalls.h
|
||||
//
|
||||
// Scott M. Silver
|
||||
//
|
||||
|
||||
#ifndef _H_PPCCALLS
|
||||
#define _H_PPCCALLS
|
||||
|
||||
#include "PPC601AppleMacOSEmitter.h"
|
||||
#include "AssembledInstructions.h"
|
||||
|
||||
template<bool tHasIncomingStore, bool tHasOutgoingStore, bool tHasFunctionAddress, bool tIsDynamic>
|
||||
class Call :
|
||||
public PPCInstructionXY
|
||||
{
|
||||
public:
|
||||
static inline bool hasReturnValue(DataNode& inDataNode); // determines whether Call Primitive has return value
|
||||
static inline uint8 numberOfArguments(DataNode& inDataNode); // determines number of real arguments (not including store)
|
||||
|
||||
Call(DataNode* inDataNode, Pool& inPool, uint8 inRegisterArguments, bool inHasReturnValue, PPCEmitter& inEmitter, void* inFunc = NULL);
|
||||
|
||||
virtual void formatToMemory(void* inStart, Uint32 inOffset, MdFormatter& inFormatter);
|
||||
virtual size_t getFormattedSize(MdFormatter& /*inFormatter*/) { return (8); }
|
||||
virtual InstructionFlags getFlags() const { return (ifCall); }
|
||||
|
||||
|
||||
protected:
|
||||
int16 mTOCOffset; // offset in accumulator TOC
|
||||
TVector* mCalleeAddress; // addres of cuntion
|
||||
|
||||
#ifdef DEBUG
|
||||
public:
|
||||
virtual void printPretty(FILE* f) { fprintf(f, "call %X", mCalleeAddress); }
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
template<bool tHasIncomingStore, bool tHasOutgoingStore>
|
||||
class CallS :
|
||||
public Call<tHasIncomingStore, tHasOutgoingStore, false, false>
|
||||
{
|
||||
public:
|
||||
inline CallS(DataNode* inDataNode, Pool& inPool, uint8 inRegisterArguments, bool inHasReturnValue, PPCEmitter& inEmitter, void* inFunc) :
|
||||
Call<tHasIncomingStore, tHasOutgoingStore, false, false>(inDataNode, inPool, inRegisterArguments, inHasReturnValue, inEmitter, inFunc) { }
|
||||
};
|
||||
|
||||
#ifdef MANUAL_TEMPLATES
|
||||
template class Call<true, true, false, false>;
|
||||
template class Call<true, false, false, false>;
|
||||
template class Call<false, false, false, false>;
|
||||
template class Call<true, true, true, false>;
|
||||
template class Call<true, true, true, true>;
|
||||
#endif
|
||||
|
||||
typedef CallS<true, true> CallS_V;
|
||||
typedef CallS<true, false> CallS_;
|
||||
typedef CallS<false, false> CallS_C;
|
||||
typedef Call<true, true, true, false> Call_;
|
||||
|
||||
|
||||
// Dynamically dispatched call
|
||||
class CallD_ :
|
||||
public Call<true, true, true, true>
|
||||
{
|
||||
public:
|
||||
inline CallD_(DataNode* inDataNode, Pool& inPool, uint8 inRegisterArguments, bool inHasReturnValue, PPCEmitter& inEmitter) :
|
||||
Call<true, true, true, true>(inDataNode, inPool, inRegisterArguments, inHasReturnValue, inEmitter) { }
|
||||
|
||||
void formatToMemory(void* inStart, Uint32 /*inOffset*/, MdFormatter& inFormatter);
|
||||
};
|
||||
|
||||
|
||||
template<bool tHasIncomingStore, bool tHasOutgoingStore, bool tHasFunctionAddress, bool tIsDynamic> bool
|
||||
Call<tHasIncomingStore, tHasOutgoingStore, tHasFunctionAddress, tIsDynamic>::
|
||||
hasReturnValue(DataNode& inDataNode)
|
||||
{
|
||||
bool hasReturnValue = (inDataNode.getOutgoingEdgesBegin() + tHasOutgoingStore < inDataNode.getOutgoingEdgesEnd());
|
||||
|
||||
return (hasReturnValue);
|
||||
}
|
||||
|
||||
template<bool tHasIncomingStore, bool tHasOutgoingStore, bool tHasFunctionAddress, bool tIsDynamic> uint8
|
||||
Call<tHasIncomingStore, tHasOutgoingStore,tHasFunctionAddress, tIsDynamic>::
|
||||
numberOfArguments(DataNode& inDataNode)
|
||||
{
|
||||
DataConsumer* firstArg;
|
||||
DataConsumer* lastArg;
|
||||
|
||||
assert(!(tHasFunctionAddress && !tHasIncomingStore)); // no such primitive
|
||||
firstArg = inDataNode.getInputsBegin() + tHasFunctionAddress + tHasIncomingStore;
|
||||
lastArg = inDataNode.getInputsEnd();
|
||||
|
||||
return (lastArg - firstArg);
|
||||
}
|
||||
|
||||
|
||||
// -> mem regarg1 regarg2 regarg3 regarg3
|
||||
// <- mem [returnval]
|
||||
template<bool tHasIncomingStore, bool tHasOutgoingStore, bool tHasFunctionAddress, bool tIsDynamic>
|
||||
Call<tHasIncomingStore, tHasOutgoingStore, tHasFunctionAddress, tIsDynamic>::
|
||||
Call(DataNode* inDataNode, Pool& inPool, uint8 inRegisterArguments, bool /*inHasReturnValue*/, PPCEmitter& inEmitter, void* inFunc) :
|
||||
PPCInstructionXY(inDataNode, inPool, inRegisterArguments + tHasIncomingStore + tIsDynamic, 1 + tHasOutgoingStore)
|
||||
{
|
||||
if (!tIsDynamic)
|
||||
inEmitter.mCrossTOCPtrGlCount++;
|
||||
|
||||
const DoublyLinkedList<DataConsumer>& projectionConsumers = inDataNode->getConsumers();
|
||||
DoublyLinkedList<DataConsumer>::iterator curProjectionEdge = projectionConsumers.begin();
|
||||
DataNode* returnValProducer;
|
||||
|
||||
// outgoing store
|
||||
if (tHasOutgoingStore)
|
||||
{
|
||||
DataNode& projectionA = projectionConsumers.get(curProjectionEdge).getNode();
|
||||
DataNode* projectionB = (projectionConsumers.done(curProjectionEdge = projectionConsumers.advance(curProjectionEdge))) ? 0 : &projectionConsumers.get(curProjectionEdge).getNode();
|
||||
|
||||
if (projectionA.hasKind(vkMemory))
|
||||
{
|
||||
inEmitter.defineProducer(projectionA, *this, 0); // -> mem
|
||||
returnValProducer = projectionB;
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(projectionB); // projectionB must be the memory producer
|
||||
inEmitter.defineProducer(*projectionB, *this, 0); // -> mem
|
||||
returnValProducer = &projectionA;
|
||||
}
|
||||
}
|
||||
else
|
||||
returnValProducer = (projectionConsumers.done(curProjectionEdge = projectionConsumers.advance(curProjectionEdge))) ? 0 : &projectionConsumers.get(curProjectionEdge).getNode();
|
||||
|
||||
if (returnValProducer)
|
||||
{
|
||||
VirtualRegister* returnValVR;
|
||||
|
||||
// the Call defines a temporary register, which is precolored to
|
||||
// the appropriate return register
|
||||
returnValVR = &inEmitter.defineTemporary(*this, 1);
|
||||
|
||||
switch (returnValProducer->getKind())
|
||||
{
|
||||
case vkInt: case vkAddr:
|
||||
{
|
||||
returnValVR->preColorRegister(kR3Color);
|
||||
|
||||
// now create a "buffer" copy between the precolored return register
|
||||
// and make the Copy define the return value edge from the pkCall
|
||||
Copy_I& copyInsn = *new(inPool) Copy_I(inDataNode, inPool);
|
||||
inEmitter.useTemporaryVR(copyInsn, *returnValVR, 0);
|
||||
inEmitter.defineProducer(*returnValProducer, copyInsn, 0);
|
||||
break;
|
||||
}
|
||||
case vkLong:
|
||||
assert(false);
|
||||
break;
|
||||
case vkFloat: case vkDouble:
|
||||
assert(false);
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
else
|
||||
getInstructionDefineBegin()[1].kind = udNone; // zero out the unused outgoing edge
|
||||
|
||||
DataConsumer* firstArgument;
|
||||
DataConsumer* curArgument;
|
||||
|
||||
// incoming store
|
||||
if (tHasIncomingStore)
|
||||
inEmitter.useProducer(inDataNode->nthInputVariable(0), *this, 0); // -> mem
|
||||
|
||||
firstArgument = inDataNode->getInputsBegin() + tHasFunctionAddress + tHasIncomingStore;
|
||||
|
||||
if (tHasFunctionAddress)
|
||||
{
|
||||
assert(inFunc == NULL); // programmer error to specify a function address if this Call has a function address
|
||||
|
||||
if (tIsDynamic)
|
||||
inEmitter.useProducer(inDataNode->nthInputVariable(1), *this, 1); // -> incoming address
|
||||
else
|
||||
mCalleeAddress = (TVector*) addressFunction(PrimConst::cast(inDataNode->nthInputVariable(1)).value.a);
|
||||
}
|
||||
else
|
||||
mCalleeAddress = (TVector*) inFunc;
|
||||
|
||||
inEmitter.mAccumulatorTOC.addData(&mCalleeAddress, sizeof(TVector), mTOCOffset);
|
||||
|
||||
// move all arguments <= 8 words into registers, handle most of the stack passed arguments later
|
||||
uint8 curFixedArg = 0; // number of words passes as fixed arguments
|
||||
uint8 curFloatArg = 0; // number of words passed as float arguments
|
||||
uint8 curTotalArgNumber = tHasIncomingStore + tIsDynamic; // use number, which starts at 1 after the store, if it exists
|
||||
|
||||
for (curArgument = firstArgument; curArgument < inDataNode->getInputsEnd(); curArgument++, curTotalArgNumber++)
|
||||
{
|
||||
VirtualRegister* vr;
|
||||
|
||||
switch (curArgument->getKind())
|
||||
{
|
||||
case vkInt:
|
||||
case vkAddr:
|
||||
if (curFixedArg <= 8)
|
||||
{
|
||||
if (curArgument->isConstant())
|
||||
vr = &inEmitter.genLoadConstant_I(*inDataNode, curArgument->getConstant().i);
|
||||
else
|
||||
{
|
||||
Copy_I& copyInsn = *new(inPool) Copy_I(inDataNode, inPool);
|
||||
|
||||
inEmitter.useProducer(curArgument->getVariable(), copyInsn, 0);
|
||||
vr = &inEmitter.defineTemporary(copyInsn, 0);
|
||||
}
|
||||
|
||||
inEmitter.useTemporaryVR(*this, *vr, curTotalArgNumber);
|
||||
vr->preColorRegister(curFixedArg);
|
||||
}
|
||||
else
|
||||
{
|
||||
StD_FixedDestRegister<kStackRegister>& storeParam = *new(inPool) StD_FixedDestRegister<kStackRegister>(inDataNode, inPool, dfLwz, 24 + (curFixedArg + curFloatArg) * 4);
|
||||
|
||||
if (curArgument->isConstant())
|
||||
{
|
||||
VirtualRegister& vr = inEmitter.genLoadConstant_I(*inDataNode, curArgument->getConstant().i);
|
||||
inEmitter.useTemporaryVR(storeParam, vr, 1);
|
||||
}
|
||||
else
|
||||
inEmitter.useProducer(curArgument->getVariable(), storeParam, 1);
|
||||
|
||||
// have the store use the incoming store edge, and define a temporary outgoing edge
|
||||
// make the call depend on this temporary outgoing store edge
|
||||
inEmitter.useProducer(inDataNode->nthInputVariable(0), storeParam, 0);
|
||||
inEmitter.useTemporaryOrder(*this, inEmitter.defineTemporaryOrder(storeParam, 0), curTotalArgNumber);
|
||||
}
|
||||
curFixedArg++;
|
||||
break;
|
||||
case vkFloat:
|
||||
case vkDouble:
|
||||
if (curFloatArg <= 13)
|
||||
vr->preColorRegister(curFloatArg++);
|
||||
else
|
||||
assert(false);
|
||||
break;
|
||||
case vkLong:
|
||||
assert(false);
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
// keep track of maximum words used
|
||||
inEmitter.mMaxArgWords = PR_MAX(inEmitter.mMaxArgWords, curFixedArg + curFloatArg);
|
||||
}
|
||||
|
||||
|
||||
template<bool tHasIncomingStore, bool tHasOutgoingStore, bool tHasFunctionAddress, bool tIsDynamic>
|
||||
void Call<tHasIncomingStore, tHasOutgoingStore,tHasFunctionAddress, tIsDynamic>::
|
||||
formatToMemory(void* inStart, Uint32 /*inOffset*/, MdFormatter& inFormatter)
|
||||
{
|
||||
assert(!tIsDynamic);
|
||||
|
||||
uint32 *curPC = curPC = (uint32 *) inStart;
|
||||
int32 branchOffset;
|
||||
bool sameTOC = (mCalleeAddress->toc == inFormatter.mRealTOC->mGlobalPtr);
|
||||
bool isStub = true; // callee is stub
|
||||
|
||||
if (!isStub && sameTOC && (uint32) mCalleeAddress->functionPtr < 0x3FFFFFF)
|
||||
{
|
||||
*curPC++ = kBla | ( (uint32) mCalleeAddress->functionPtr & 0x03FFFFFF);
|
||||
*curPC++ = kNop;
|
||||
}
|
||||
else if (!isStub && sameTOC && canBePCRelative(inStart, (void*) mCalleeAddress, branchOffset))
|
||||
{
|
||||
*curPC++ = kBl | (branchOffset & 0x03FFFFFF);
|
||||
*curPC++ = kNop;
|
||||
}
|
||||
else
|
||||
{
|
||||
// we can or the offset right into the bl instruction (because aa and lk nicely use the last 2 bits)
|
||||
*curPC++ = kBl | (((uint32) inFormatter.mNextFreePostMethod - (uint32)inStart) & 0x03FFFFFF);
|
||||
|
||||
inFormatter.mNextFreePostMethod = (uint32*) formatCrossTocPtrGl(inFormatter.mNextFreePostMethod, mTOCOffset + inFormatter.mRealTOCOffset);
|
||||
*curPC++ = makeDForm(32, 2, 1, 20); // stw rtoc, 20(sp)
|
||||
}
|
||||
}
|
||||
|
||||
inline void CallD_::
|
||||
formatToMemory(void* inStart, Uint32 /*inOffset*/, MdFormatter& /*inFormatter*/)
|
||||
{
|
||||
uint32 *curPC = (uint32 *) inStart;
|
||||
|
||||
*curPC++ = kBl | (((uint32) getDynamicPtrGl(inStart, udToRegisterNumber(getInstructionUseBegin()[1])) - (uint32) inStart) & 0x03FFFFFF);
|
||||
*curPC++ = makeDForm(32, 2, 1, 20); // stw rtoc, 20(sp)
|
||||
}
|
||||
|
||||
#endif // _H_PPCCALLS
|
|
@ -0,0 +1,168 @@
|
|||
/* -*- 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.
|
||||
*/
|
||||
// PPCInstructionTemplates.h
|
||||
//
|
||||
// Scott M. Silver
|
||||
|
||||
// enum name
|
||||
// opcode
|
||||
// string
|
||||
#ifdef DO_DFORM
|
||||
DFORM_ARITH_DEFINE(Addi, 14)
|
||||
DFORM_ARITH_DEFINE(Addis, 15)
|
||||
DFORM_ARITH_DEFINE(Mulli, 7)
|
||||
DFORM_ARITH_DEFINE(Ori, 24)
|
||||
DFORM_ARITH_DEFINE(Oris, 25)
|
||||
DFORM_ARITH_DEFINE(Subfic, 8)
|
||||
DFORM_ARITH_DEFINE(Xori, 26)
|
||||
DFORM_ARITH_DEFINE(Xoris, 27)
|
||||
|
||||
DFORM_ARITHCC_DEFINE(Addic, 13)
|
||||
DFORM_ARITHCC_DEFINE(Andi, 28)
|
||||
DFORM_ARITHCC_DEFINE(Andis, 29)
|
||||
|
||||
DFORM_LOAD_DEFINE(Lbz, 34)
|
||||
DFORM_LOAD_DEFINE(Lfd, 50)
|
||||
DFORM_LOAD_DEFINE(Lfs, 48)
|
||||
DFORM_LOAD_DEFINE(Lha, 42)
|
||||
DFORM_LOAD_DEFINE(Lhz, 40)
|
||||
DFORM_LOAD_DEFINE(Lwz, 32)
|
||||
|
||||
DFORM_LOADU_DEFINE(Lbzu, 35)
|
||||
DFORM_LOADU_DEFINE(Lfdu, 51)
|
||||
DFORM_LOADU_DEFINE(Lfsu, 49)
|
||||
DFORM_LOADU_DEFINE(Lhau, 43)
|
||||
DFORM_LOADU_DEFINE(Lhzu, 41)
|
||||
DFORM_LOADU_DEFINE(Lwzu, 33)
|
||||
|
||||
|
||||
DFORM_STORE_DEFINE(Stb, 38)
|
||||
DFORM_STORE_DEFINE(Stfd, 54)
|
||||
DFORM_STORE_DEFINE(Stfs, 52)
|
||||
DFORM_STORE_DEFINE(Sth, 44)
|
||||
DFORM_STORE_DEFINE(Stw, 36)
|
||||
|
||||
DFORM_STOREU_DEFINE(Stu, 39)
|
||||
DFORM_STOREU_DEFINE(Stfdu, 55)
|
||||
DFORM_STOREU_DEFINE(Stfsu, 53)
|
||||
DFORM_STOREU_DEFINE(Sthu, 45)
|
||||
DFORM_STOREU_DEFINE(Stwu, 37)
|
||||
|
||||
DFORM_ARITH_DEFINE(Cmpi, 11)
|
||||
DFORM_ARITH_DEFINE(Cmpli, 10)
|
||||
|
||||
DFORM_TRAP_DEFINE(Twi, 3)
|
||||
#endif
|
||||
|
||||
#ifdef DO_XFORM
|
||||
XFORM_ARITH_DEFINE(Add, 266, 31)
|
||||
XFORM_ARITH_DEFINE(Addc, 10, 31)
|
||||
XFORM_ARITH_DEFINE(Adde, 138, 31)
|
||||
XFORM_ARITH_DEFINE(Divw, 459, 31)
|
||||
XFORM_ARITH_DEFINE(Divwu, 138, 31)
|
||||
XFORM_ARITH_DEFINE(Mulhw, 75, 31)
|
||||
XFORM_ARITH_DEFINE(Mulhwu, 11, 31)
|
||||
XFORM_ARITH_DEFINE(Mullw, 235, 31)
|
||||
XFORM_ARITH_DEFINE(Subf, 40, 31)
|
||||
XFORM_ARITH_DEFINE(Subfc, 8, 31)
|
||||
XFORM_ARITH_DEFINE(Subfe, 136, 31)
|
||||
|
||||
XFORM_INAONLY_DEFINE(Addme, 234, 31)
|
||||
XFORM_INAONLY_DEFINE(Addze, 202, 31)
|
||||
XFORM_INAONLY_DEFINE(Neg, 104, 31)
|
||||
XFORM_INAONLY_DEFINE(Subfme, 232, 31)
|
||||
XFORM_INAONLY_DEFINE(Subfze, 200, 31)
|
||||
|
||||
XFORM_INAONLY_DEFINE(Srawi, 824-512, 31)
|
||||
|
||||
XFORM_ARITH_DEFINE(And, 138, 31)
|
||||
XFORM_ARITH_DEFINE(Andc, 75, 31)
|
||||
XFORM_ARITH_DEFINE(Eqv, 284, 31)
|
||||
XFORM_ARITH_DEFINE(Nand, 476, 31)
|
||||
XFORM_ARITH_DEFINE(Nor, 124, 31)
|
||||
XFORM_ARITH_DEFINE(Or, 444, 31)
|
||||
XFORM_ARITH_DEFINE(Orc, 412, 31)
|
||||
XFORM_ARITH_DEFINE(Slw, 24, 31)
|
||||
XFORM_ARITH_DEFINE(Sraw, 792, 31) // FIX-ME prob isn't correct
|
||||
XFORM_ARITH_DEFINE(Srw, 536, 31) // FIX-ME prob isn't corect
|
||||
XFORM_ARITH_DEFINE(Xor, 316, 31)
|
||||
|
||||
XFORM_CMP_DEFINE(Cmp, 0, 31)
|
||||
XFORM_CMP_DEFINE(Cmpl, 32, 31)
|
||||
|
||||
XFORM_CMP_DEFINE(Fcmpo, 0, 63)
|
||||
XFORM_CMP_DEFINE(Fcmpu, 32, 63)
|
||||
|
||||
XFORM_LOAD_DEFINE(Lbzx, 87, 31)
|
||||
XFORM_LOAD_DEFINE(Lfdx, 599, 31) // FIX-ME prob isn't corect
|
||||
XFORM_LOAD_DEFINE(Lfsx, 535, 31)
|
||||
XFORM_LOAD_DEFINE(Lhax, 343, 31)
|
||||
XFORM_LOAD_DEFINE(Lhbrx, 790, 31) // FIX-ME prob isn't corect
|
||||
XFORM_LOAD_DEFINE(Lhzx, 279, 31)
|
||||
XFORM_LOAD_DEFINE(Lwarx, 20, 31)
|
||||
XFORM_LOAD_DEFINE(Lwbrx, 534, 31) // FIX-ME prob isn't corect
|
||||
XFORM_LOAD_DEFINE(Lwzx, 23, 31)
|
||||
|
||||
XFORM_LOADU_DEFINE(Lbzux, 119, 31)
|
||||
XFORM_LOADU_DEFINE(Lfdux, 631, 31) // FIX-ME prob isn't corect
|
||||
XFORM_LOADU_DEFINE(Lfsux, 567, 31) // FIX-ME prob isn't corect
|
||||
XFORM_LOADU_DEFINE(Lhaux, 375, 31)
|
||||
XFORM_LOADU_DEFINE(Lhzux, 311, 31)
|
||||
XFORM_LOADU_DEFINE(Lwzux, 55, 31)
|
||||
|
||||
XFORM_STORE_DEFINE(Stbx, 215, 31)
|
||||
XFORM_STORE_DEFINE(Stfdx, 727, 31) // FIX-ME prob isn't corect
|
||||
XFORM_STORE_DEFINE(Stfsx, 663, 31) // FIX-ME prob isn't corect
|
||||
XFORM_STORE_DEFINE(Sthbrx, 918, 31) // FIX-ME prob isn't corect
|
||||
XFORM_STORE_DEFINE(Sthx, 407, 31)
|
||||
XFORM_STORE_DEFINE(Stwbrx, 662, 31) // FIX-ME prob isn't corect
|
||||
XFORM_STORE_DEFINE(Stwx, 151, 31)
|
||||
|
||||
XFORM_STOREU_DEFINE(Stbux, 247, 31)
|
||||
XFORM_STOREU_DEFINE(Stfdux, 759, 31) // FIX-ME prob isn't corect
|
||||
XFORM_STOREU_DEFINE(Stfsux, 695, 31) // FIX-ME prob isn't corect
|
||||
XFORM_STOREU_DEFINE(Sthux, 439, 31)
|
||||
XFORM_STOREU_DEFINE(Stwux, 183, 31)
|
||||
|
||||
/* X Form - TrapWord */
|
||||
XFORM_TRAP_DEFINE(Tw, 4, 31)
|
||||
|
||||
/* X Form - XInBOnly */
|
||||
XFORM_INBONLY_DEFINE(Fabs, 264, 63)
|
||||
XFORM_INBONLY_DEFINE(Fctiw, 14, 63)
|
||||
XFORM_INBONLY_DEFINE(Fctiwz, 15, 63)
|
||||
XFORM_INBONLY_DEFINE(Fmr, 72, 63)
|
||||
XFORM_INBONLY_DEFINE(Fnabs, 136, 63)
|
||||
XFORM_INBONLY_DEFINE(Fneg, 40, 63)
|
||||
XFORM_INBONLY_DEFINE(Frsp, 12, 63)
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef DO_MFORM
|
||||
MFORM_DEFINE(Rlwimi, 20)
|
||||
MFORM_DEFINE(Rlwinm, 21)
|
||||
MFORM_DEFINE(Rlwnm, 23)
|
||||
#endif
|
||||
|
||||
#ifdef DO_AFORM
|
||||
// name, opcode, xo, has A (ie A != 0), has B, hasC, sets CC (ie Rc)
|
||||
|
||||
|
||||
AFORM_DEFINE(Fadd, 63, 21, true, true, false, false)
|
||||
AFORM_DEFINE(FaddC, 63, 21, true, true, false, true)
|
||||
#endif DO_AFORM
|
|
@ -0,0 +1,143 @@
|
|||
/* -*- 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.
|
||||
*/
|
||||
// PPCInstructions.cpp
|
||||
//
|
||||
// Scott M. Silver
|
||||
|
||||
#include "PPCInstructions.h"
|
||||
#include "PPC601AppleMacOSEmitter.h"
|
||||
|
||||
/* Instantiate templates explicitly if needed */
|
||||
#ifdef MANUAL_TEMPLATES
|
||||
template class AForm<true, true, false, true>;
|
||||
template class AForm<true, true, false, false>;
|
||||
#endif
|
||||
|
||||
|
||||
void DFormXY::
|
||||
formatToMemory(void* inStart, Uint32 /*inOffset*/, MdFormatter& /*inFormatter*/)
|
||||
{
|
||||
struct
|
||||
{
|
||||
uint OPCD:6;
|
||||
uint D:5;
|
||||
uint A:5;
|
||||
uint IMM:16;
|
||||
} dForm;
|
||||
|
||||
dForm.OPCD = sDFormInfos[mKind].opcode;
|
||||
dForm.D = getD();
|
||||
dForm.A = getA();
|
||||
dForm.IMM = getIMM();
|
||||
|
||||
*(Uint32*) inStart = *(Uint32*) &dForm;
|
||||
}
|
||||
|
||||
void LdD_RTOC::
|
||||
formatToMemory(void* inStart, Uint32 inOffset, PPCFormatter& inFormatter)
|
||||
{
|
||||
mImmediate += inFormatter.mRealTOCOffset;
|
||||
|
||||
LdD_FixedSource<2>::formatToMemory(inStart, inOffset, inFormatter);
|
||||
}
|
||||
|
||||
void XFormXY::
|
||||
formatToMemory(void* inStart, Uint32 /*inOffset*/, MdFormatter& /*inFormatter*/)
|
||||
{
|
||||
struct
|
||||
{
|
||||
uint OPCD:6;
|
||||
uint D:5;
|
||||
uint A:5;
|
||||
uint B:5;
|
||||
uint OE:1;
|
||||
uint XO:9;
|
||||
uint RC:1;
|
||||
} xForm;
|
||||
|
||||
xForm.OPCD = sXFormInfos[mKind].primary;;
|
||||
xForm.D = getD();
|
||||
xForm.A = getA();
|
||||
xForm.B = getB();
|
||||
xForm.OE = ((mFlags & pfOE) != 0) ? 1 : 0;
|
||||
xForm.XO = sXFormInfos[mKind].opcode;
|
||||
xForm.RC = ((mFlags & pfRc) != 0) ? 1 : 0;
|
||||
|
||||
*(Uint32*) inStart = *(Uint32*) &xForm;
|
||||
}
|
||||
|
||||
void BranchI::
|
||||
formatToMemory(void* inStart, Uint32 inOffset, MdFormatter& /*inFormatter*/)
|
||||
{
|
||||
mIForm.LI = (mTarget.getNativeOffset() - inOffset) >> 2;
|
||||
|
||||
*(Uint32*) inStart = *(Uint32*) &mIForm;
|
||||
}
|
||||
|
||||
|
||||
void BranchCB::
|
||||
formatToMemory(void* inStart, Uint32 inOffset, MdFormatter& /*inFormatter*/)
|
||||
{
|
||||
mCForm.BD = (mTarget.getNativeOffset() - inOffset) >> 2;
|
||||
*(Uint32*) inStart = *(Uint32*) &mCForm;
|
||||
}
|
||||
|
||||
BranchCB::
|
||||
BranchCB(DataNode* inPrimitive, Pool& inPool, ControlNode& inTarget, Condition2 inCondition, bool inAbsolute, bool inLink) :
|
||||
PPCInstructionXY(inPrimitive, inPool, 1, 0),
|
||||
mTarget(inTarget)
|
||||
#if DEBUG
|
||||
, mCond(inCondition)
|
||||
#endif
|
||||
{
|
||||
mCForm.OPCD = 16;
|
||||
|
||||
mCForm.BO = sBranchConditions[inCondition].bo;
|
||||
// mCForm.Y = 0;
|
||||
mCForm.BI = sBranchConditions[inCondition].bi;
|
||||
mCForm.AA = inAbsolute;
|
||||
mCForm.LK = inLink;
|
||||
}
|
||||
|
||||
void MForm::
|
||||
formatToMemory(void* inStart, Uint32 /*inOffset*/, MdFormatter& /*inFormatter*/)
|
||||
{
|
||||
struct
|
||||
{
|
||||
uint OPCD:6;
|
||||
uint S:5;
|
||||
uint A:5;
|
||||
uint B:5;
|
||||
uint MB:5;
|
||||
uint ME:5;
|
||||
uint RC:1;
|
||||
} mForm;
|
||||
|
||||
mForm.OPCD = sMFormInfos[mKind].opcode;
|
||||
mForm.S = getS();
|
||||
mForm.A = getA();
|
||||
mForm.B = getB();
|
||||
mForm.MB = getMB();
|
||||
mForm.ME = getME();
|
||||
mForm.RC = ((mFlags & pfRc) != 0) ? 1 : 0;
|
||||
|
||||
*(Uint32*) inStart = *(Uint32*) &mForm;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,955 @@
|
|||
/* -*- 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.
|
||||
*/
|
||||
// PPCInstructions.h
|
||||
//
|
||||
// Scott M. Silver
|
||||
// Peter Desantis
|
||||
|
||||
#ifndef _H_PPCINSTRUCTIONS
|
||||
#define _H_PPCINSTRUCTIONS
|
||||
|
||||
#include "prtypes.h"
|
||||
#include "Instruction.h"
|
||||
#include "ControlNodes.h"
|
||||
|
||||
typedef Uint8 PPCInsnFlags;
|
||||
enum
|
||||
{
|
||||
pfNil = 0x00, // nothing special
|
||||
pfRc = 0x01, // sets a condition code
|
||||
pfOE = 0x02 // set the overflow flag
|
||||
};
|
||||
|
||||
#ifdef __MWERKS__
|
||||
#pragma mark -
|
||||
#pragma mark ¥ÊPPCInstructionXY ¥
|
||||
#endif
|
||||
|
||||
// PPCInstructionXY
|
||||
//
|
||||
// Base class for all PPC instructions
|
||||
class PPCInstructionXY :
|
||||
public InsnUseXDefineYFromPool
|
||||
{
|
||||
public:
|
||||
inline PPCInstructionXY(DataNode* inPrimitive, Pool& inPool, Uint8 inX, Uint8 inY) :
|
||||
InsnUseXDefineYFromPool(inPrimitive, inPool, inX, inY) { }
|
||||
virtual size_t getFormattedSize(MdFormatter& /*inFormatter*/) { return (4); }
|
||||
};
|
||||
|
||||
|
||||
// Fixed point color to register map.
|
||||
const Uint8 sFixedPointRegisterMap[] =
|
||||
{
|
||||
3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 31, // -> first non-volatile
|
||||
30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20,
|
||||
19, 18, 17, 16, 15, 14 // 13 // -> last non-volatile, but used as globals
|
||||
};
|
||||
|
||||
const Uint8 sFloatingPointRegisterMap[] =
|
||||
{
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
|
||||
11, 12, 13, // -> first non-volatile
|
||||
14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
|
||||
25, 26, 27, 28, 29, 30, 31 // -> last non-volatile
|
||||
};
|
||||
|
||||
// udToRegisterNumber
|
||||
//
|
||||
// Map colors of registers to real register numbers
|
||||
inline Uint8
|
||||
udToRegisterNumber(InstructionUseOrDefine& inUse)
|
||||
{
|
||||
Uint32 color = inUse.getVirtualRegister().colorInfo.color;
|
||||
if (inUse.getVirtualRegister().getClass() == vrcInteger)
|
||||
{
|
||||
if (color < sizeof(sFixedPointRegisterMap) / sizeof(Uint8))
|
||||
return (sFixedPointRegisterMap[color]);
|
||||
#ifdef DEBUG
|
||||
else
|
||||
return 255;
|
||||
#endif
|
||||
}
|
||||
else if (inUse.getVirtualRegister().getClass() == vrcFloatingPoint)
|
||||
{
|
||||
if (color < sizeof(sFloatingPointRegisterMap) / sizeof(Uint8))
|
||||
return (sFloatingPointRegisterMap[color]);
|
||||
#ifdef DEBUG
|
||||
else
|
||||
return 255;
|
||||
#endif
|
||||
}
|
||||
else
|
||||
assert(false);
|
||||
return 255; // never reached
|
||||
}
|
||||
|
||||
#ifdef __MWERKS__
|
||||
#pragma mark -
|
||||
#pragma mark ¥ÊD-Form ¥
|
||||
#endif
|
||||
|
||||
#define MAKE_DFORM_ENUM(inName) df##inName,
|
||||
|
||||
#define DFORM_ARITH_DEFINE(inName, inOpcode) MAKE_DFORM_ENUM(inName)
|
||||
#define DFORM_ARITHCC_DEFINE(inName, inOpcode) MAKE_DFORM_ENUM(inName)
|
||||
#define DFORM_LOAD_DEFINE(inName, inOpcode) MAKE_DFORM_ENUM(inName)
|
||||
#define DFORM_LOADU_DEFINE(inName, inOpcode) MAKE_DFORM_ENUM(inName)
|
||||
#define DFORM_STORE_DEFINE(inName, inOpcode) MAKE_DFORM_ENUM(inName)
|
||||
#define DFORM_STOREU_DEFINE(inName, inOpcode) MAKE_DFORM_ENUM(inName)
|
||||
#define DFORM_TRAP_DEFINE(inName, inOpcode) MAKE_DFORM_ENUM(inName)
|
||||
#define DO_DFORM
|
||||
enum DFormKind
|
||||
{
|
||||
#include "PPCInstructionTemplates.h"
|
||||
dfLast
|
||||
};
|
||||
|
||||
#undef DFORM_ARITH_DEFINE
|
||||
#undef DFORM_ARITHCC_DEFINE
|
||||
#undef DFORM_LOAD_DEFINE
|
||||
#undef DFORM_LOADU_DEFINE
|
||||
#undef DFORM_STORE_DEFINE
|
||||
#undef DFORM_STOREU_DEFINE
|
||||
#undef DFORM_TRAP_DEFINE
|
||||
|
||||
struct DFormInstructionInfo
|
||||
{
|
||||
Uint16 opcode;
|
||||
char* formatStr;
|
||||
};
|
||||
|
||||
#define MAKE_DFORM_INFO(inOpcode, inString) \
|
||||
{ inOpcode, inString },
|
||||
|
||||
#define DFORM_ARITH_DEFINE(inName, inOpcode) MAKE_DFORM_INFO(inOpcode, #inName" r%d, r%d, %d")
|
||||
#define DFORM_ARITHCC_DEFINE(inName, inOpcode) MAKE_DFORM_INFO(inOpcode, #inName" r%d, r%d, %d")
|
||||
#define DFORM_LOAD_DEFINE(inName, inOpcode) MAKE_DFORM_INFO(inOpcode, #inName" r%d, %d(r%d)")
|
||||
#define DFORM_LOADU_DEFINE(inName, inOpcode) MAKE_DFORM_INFO(inOpcode, #inName" r%d, %d(r%d)")
|
||||
#define DFORM_STORE_DEFINE(inName, inOpcode) MAKE_DFORM_INFO(inOpcode, #inName" r%d, %d(r%d)")
|
||||
#define DFORM_STOREU_DEFINE(inName, inOpcode) MAKE_DFORM_INFO(inOpcode, #inName" r%d, %d(r%d)")
|
||||
#define DFORM_TRAP_DEFINE(inName, inOpcode) MAKE_DFORM_INFO(inOpcode, #inName"%s r%d, %d")
|
||||
|
||||
const DFormInstructionInfo sDFormInfos[] =
|
||||
{
|
||||
#include "PPCInstructionTemplates.h"
|
||||
};
|
||||
#undef DO_DFORM
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class DFormXY :
|
||||
public PPCInstructionXY
|
||||
{
|
||||
public:
|
||||
inline DFormXY(DataNode* inPrimitive, Pool& inPool, DFormKind inKind, Uint8 inX, Uint8 inY) :
|
||||
PPCInstructionXY(inPrimitive, inPool, inX, inY),
|
||||
mKind(inKind) { }
|
||||
virtual void formatToMemory(void* inStart, Uint32 inOffset, MdFormatter& /*inFormatter*/);
|
||||
|
||||
protected:
|
||||
Uint8 getOPCD() { return (sDFormInfos[mKind].opcode); }
|
||||
virtual Uint8 getD() { return (udToRegisterNumber(getInstructionDefineBegin()[0])); }
|
||||
virtual Uint8 getA() { return (udToRegisterNumber(getInstructionUseBegin()[0])); }
|
||||
virtual Uint16 getIMM() = 0;
|
||||
|
||||
const Uint16 mKind;
|
||||
|
||||
#ifdef DEBUG
|
||||
public:
|
||||
virtual void printPretty(FILE* f) {fprintf(f, sDFormInfos[mKind].formatStr, getD(), getA(), getIMM());}
|
||||
#endif
|
||||
};
|
||||
|
||||
class DFormXYImmediate :
|
||||
public DFormXY
|
||||
{
|
||||
public:
|
||||
inline DFormXYImmediate(DataNode* inPrimitive, Pool& inPool, DFormKind inKind, Uint8 inX, Uint8 inY, Uint16 inImmediate) :
|
||||
mImmediate(inImmediate),
|
||||
DFormXY(inPrimitive, inPool, inKind, inX, inY) { }
|
||||
|
||||
virtual Uint16 getIMM() { return mImmediate; }
|
||||
|
||||
Uint16 mImmediate; // immediate value
|
||||
};
|
||||
|
||||
|
||||
|
||||
template<Uint8 tUses, Uint8 tDefines, Uint8 tAOffset, Uint8 tDOffset>
|
||||
class LdD :
|
||||
public DFormXYImmediate
|
||||
{
|
||||
public:
|
||||
inline LdD(DataNode* inPrimitive, Pool& inPool, DFormKind inKind, Uint16 inImmediate) :
|
||||
DFormXYImmediate(inPrimitive, inPool, inKind, tUses, tDefines, inImmediate) { }
|
||||
|
||||
virtual Uint8 getA() { return (udToRegisterNumber(getInstructionUseBegin()[tAOffset])); }
|
||||
virtual Uint8 getD() { return (udToRegisterNumber(getInstructionDefineBegin()[tDOffset])); }
|
||||
};
|
||||
|
||||
|
||||
template<Uint8 tFixedSourceRegister>
|
||||
class LdD_FixedSource :
|
||||
public LdD<0, 1, 0, 0>
|
||||
{
|
||||
public:
|
||||
inline LdD_FixedSource(DataNode* inPrimitive, Pool& inPool, DFormKind inKind, Uint16 inImmediate) :
|
||||
LdD<0, 1, 0, 0>(inPrimitive, inPool, inKind, inImmediate) { assert (tFixedSourceRegister < 32); }
|
||||
|
||||
virtual Uint8 getA() { return (tFixedSourceRegister); }
|
||||
};
|
||||
|
||||
// the formatToMemory routine does a fix-up
|
||||
// based on the real toc offset
|
||||
class LdD_RTOC :
|
||||
public LdD_FixedSource<2>
|
||||
{
|
||||
public:
|
||||
inline LdD_RTOC(DataNode* inPrimitive, Pool& inPool, DFormKind inKind, Uint16 inImmediate) :
|
||||
LdD_FixedSource<2>(inPrimitive, inPool, inKind, inImmediate) { }
|
||||
|
||||
virtual void formatToMemory(void* inStart, Uint32 inOffset, MdFormatter& inFormatter);
|
||||
};
|
||||
|
||||
typedef LdD<2, 1, 1, 0> LdD_; // Rvalue | mem Raddress
|
||||
typedef LdD<2, 2, 1, 1> LdD_V; // mem Rvalue | mem Raddress
|
||||
|
||||
// Rvalue | Sstackslot
|
||||
class LdD_SpillRegister :
|
||||
public LdD<1, 1, 0, 0>
|
||||
{
|
||||
public:
|
||||
inline LdD_SpillRegister(DataNode* inPrimitive, Pool& inPool, VirtualRegister& inStackSlot, VirtualRegister& inDefine) :
|
||||
LdD<1, 1, 0, 0>(inPrimitive, inPool, dfLwz, 0) { addUse(0, inStackSlot); addDefine(0, inDefine); }
|
||||
|
||||
// FIX-ME what about 64 bit fregs.
|
||||
virtual Uint16 getIMM() { return ((Uint16)(getInstructionUseBegin()[0].getVirtualRegister().getColor() * -4) - 4); }
|
||||
virtual Uint8 getA() { return (1); }
|
||||
};
|
||||
|
||||
// --
|
||||
|
||||
// mem | mem Raddress Rvalue
|
||||
class StD :
|
||||
public DFormXYImmediate
|
||||
{
|
||||
public:
|
||||
inline StD(DataNode* inPrimitive, Pool& inPool, DFormKind inKind, Int16 inImmediate, Uint8 inUses = 3, Uint8 inDefines = 1) :
|
||||
DFormXYImmediate(inPrimitive, inPool, inKind, inUses, inDefines, inImmediate) { }
|
||||
|
||||
virtual Uint8 getA() { return (udToRegisterNumber(getInstructionUseBegin()[1])); }
|
||||
virtual Uint8 getD() { return (udToRegisterNumber(getInstructionUseBegin()[2])); }
|
||||
|
||||
#ifdef DEBUG
|
||||
public:
|
||||
virtual void printPretty(FILE* f) {fprintf(f, sDFormInfos[mKind].formatStr, getD(), getA(), getIMM());}
|
||||
#endif
|
||||
};
|
||||
|
||||
// Sstackslot | Rvalue
|
||||
class StD_SpillRegister :
|
||||
public StD
|
||||
{
|
||||
public:
|
||||
inline StD_SpillRegister(DataNode* inPrimitive, Pool& inPool, VirtualRegister& inStackSlot, VirtualRegister& inUse) :
|
||||
StD(inPrimitive, inPool, dfStw, 0, 1, 1) { addDefine(0, inStackSlot); addUse(0, inUse); }
|
||||
|
||||
virtual Uint16 getIMM() { return ((Uint16)(getInstructionUseBegin()[0].getVirtualRegister().getColor() * -4) - 4); }
|
||||
virtual Uint8 getA() { return (1); }
|
||||
virtual Uint8 getD() { return (udToRegisterNumber(getInstructionUseBegin()[0])); }
|
||||
};
|
||||
|
||||
// mem | mem value
|
||||
template<Uint8 tFixedDestRegister>
|
||||
class StD_FixedDestRegister :
|
||||
public StD
|
||||
{
|
||||
public:
|
||||
inline StD_FixedDestRegister(DataNode* inPrimitive, Pool& inPool, DFormKind inKind, Uint16 inImmediate) :
|
||||
StD(inPrimitive, inPool, inKind, inImmediate, 2, 1) { }
|
||||
|
||||
virtual Uint8 getA() { return (tFixedDestRegister); }
|
||||
virtual Uint8 getD() { return (udToRegisterNumber(getInstructionUseBegin()[1])); }
|
||||
};
|
||||
|
||||
class ArithID :
|
||||
public DFormXYImmediate
|
||||
{
|
||||
public:
|
||||
inline ArithID(DataNode* inPrimitive, Pool& inPool, DFormKind inKind, Int16 inImmediate) :
|
||||
DFormXYImmediate(inPrimitive, inPool, inKind, 1, 1, inImmediate) { }
|
||||
|
||||
virtual InstructionFlags getFlags() const { return ((mImmediate == 0 && mKind == dfAddi) ? ifCopy : ifNone); }
|
||||
};
|
||||
|
||||
class LogicalID :
|
||||
public DFormXYImmediate
|
||||
{
|
||||
public:
|
||||
inline LogicalID(DataNode* inPrimitive, Pool& inPool, DFormKind inKind, Int16 inImmediate) :
|
||||
DFormXYImmediate(inPrimitive, inPool, inKind, 1, 1, inImmediate) { }
|
||||
|
||||
Uint8 getA() { return (udToRegisterNumber(getInstructionDefineBegin()[0])); }
|
||||
Uint8 getD() { return (udToRegisterNumber(getInstructionUseBegin()[0])); }
|
||||
virtual InstructionFlags getFlags() const { return ((mImmediate == 0 && mKind == dfAddi) ? ifCopy : ifNone); }
|
||||
};
|
||||
|
||||
|
||||
class ArithIZeroInputD :
|
||||
public DFormXYImmediate
|
||||
{
|
||||
public:
|
||||
inline ArithIZeroInputD(DataNode* inPrimitive, Pool& inPool, DFormKind inKind, Uint16 inImmediate, Uint8 inFakeUses = 0) :
|
||||
DFormXYImmediate(inPrimitive, inPool, inKind, inFakeUses, 1, inImmediate) { }
|
||||
|
||||
Uint8 getA() { return (0); }
|
||||
};
|
||||
|
||||
class LogicalIZeroInputD :
|
||||
public DFormXYImmediate
|
||||
{
|
||||
public:
|
||||
inline LogicalIZeroInputD(DataNode* inPrimitive, Pool& inPool, DFormKind inKind, Uint16 inImmediate, Uint8 inFakeUses = 0) :
|
||||
DFormXYImmediate(inPrimitive, inPool, inKind, inFakeUses, 1, inImmediate) { }
|
||||
|
||||
Uint8 getA() { return (udToRegisterNumber(getInstructionDefineBegin()[0])); }
|
||||
Uint8 getD() { return (0); }
|
||||
};
|
||||
|
||||
|
||||
class Copy_I :
|
||||
public ArithID
|
||||
{
|
||||
public:
|
||||
inline Copy_I(DataNode* inPrimitive, Pool& inPool) :
|
||||
ArithID(inPrimitive, inPool, dfAddi, 0) { }
|
||||
};
|
||||
|
||||
|
||||
class ArithIDSetCC :
|
||||
public DFormXYImmediate
|
||||
{
|
||||
public:
|
||||
inline ArithIDSetCC(DataNode* inPrimitive, Pool& inPool, DFormKind inKind, Uint16 inImmediate) :
|
||||
DFormXYImmediate(inPrimitive, inPool, inKind, 2, 2, inImmediate) { }
|
||||
};
|
||||
|
||||
|
||||
class CmpID :
|
||||
public DFormXYImmediate
|
||||
{
|
||||
public:
|
||||
inline CmpID(DataNode* inPrimitive, Pool& inPool, DFormKind inKind, Uint16 inImmediate) :
|
||||
DFormXYImmediate(inPrimitive, inPool, inKind, 1, 1, inImmediate) { }
|
||||
|
||||
protected:
|
||||
virtual Uint8 getD() { return (0); } // always use cr0, and L=0
|
||||
|
||||
#ifdef DEBUG
|
||||
public:
|
||||
virtual void printPretty(FILE* f) { fprintf(f, sDFormInfos[mKind].formatStr, getD() >> 2, getA(), getIMM()); }
|
||||
#endif
|
||||
};
|
||||
|
||||
// the bits in order (or offsets into a condition register)
|
||||
// neg (lt)
|
||||
// pos (gt)
|
||||
// zero (eq)
|
||||
// summary overflow (so)
|
||||
// 0010y branch if cond is false
|
||||
// 0110y branch if cond is true
|
||||
struct TrapCondition
|
||||
{
|
||||
char* name;
|
||||
Uint8 to;
|
||||
};
|
||||
|
||||
// indexed by Condition2
|
||||
static TrapCondition sSignedTrapConditions[] =
|
||||
{
|
||||
{"", 31},
|
||||
{"lt", 16},
|
||||
{"eq", 4},
|
||||
{"le", 20},
|
||||
{"gt", 8},
|
||||
{"ne", 24},
|
||||
{"ge", 12}
|
||||
};
|
||||
|
||||
// indexed by Condition2
|
||||
static TrapCondition sUnsignedTrapConditions[] =
|
||||
{
|
||||
{"", 31},
|
||||
{"llt", 2},
|
||||
{"eq", 4},
|
||||
{"lle", 6},
|
||||
{"lgt", 1},
|
||||
{"ne", 24},
|
||||
{"lge", 5}
|
||||
};
|
||||
|
||||
class TrapD :
|
||||
public DFormXYImmediate
|
||||
{
|
||||
public:
|
||||
inline TrapD(DataNode* inPrimitive, Pool& inPool, Condition2 inCondition, bool inSigned, Uint16 inImmediate) :
|
||||
DFormXYImmediate(inPrimitive, inPool, dfTwi, 1, 0, inImmediate),
|
||||
mSigned(inSigned),
|
||||
mCond(inCondition) { }
|
||||
|
||||
protected:
|
||||
virtual Uint8 getD() { return (mSigned ? sSignedTrapConditions[mCond].to : sUnsignedTrapConditions[mCond].to); }
|
||||
virtual Uint8 getA() { return (udToRegisterNumber(getInstructionUseBegin()[0])); }
|
||||
|
||||
const bool mSigned;
|
||||
const Condition2 mCond;
|
||||
|
||||
#ifdef DEBUG
|
||||
public:
|
||||
virtual void printPretty(FILE* f) { fprintf(f, sDFormInfos[mKind].formatStr, mSigned ? sSignedTrapConditions[mCond].name : sUnsignedTrapConditions[mCond].name, getA(), getIMM()); }
|
||||
#endif
|
||||
};
|
||||
|
||||
#ifdef __MWERKS__
|
||||
#pragma mark -
|
||||
#pragma mark ¥ÊX & XO-Form ¥
|
||||
#endif
|
||||
|
||||
|
||||
#define MAKE_XFORM_ENUM(inName) xf##inName,
|
||||
|
||||
#define XFORM_ARITH_DEFINE(inName, inOpcode, inPrimary) MAKE_XFORM_ENUM(inName)
|
||||
#define XFORM_INAONLY_DEFINE(inName, inOpcode, inPrimary) MAKE_XFORM_ENUM(inName)
|
||||
#define XFORM_CMP_DEFINE(inName, inOpcode, inPrimary) MAKE_XFORM_ENUM(inName)
|
||||
#define XFORM_LOAD_DEFINE(inName, inOpcode, inPrimary) MAKE_XFORM_ENUM(inName)
|
||||
#define XFORM_LOADU_DEFINE(inName, inOpcode, inPrimary) MAKE_XFORM_ENUM(inName)
|
||||
#define XFORM_STORE_DEFINE(inName, inOpcode, inPrimary) MAKE_XFORM_ENUM(inName)
|
||||
#define XFORM_STOREU_DEFINE(inName, inOpcode, inPrimary) MAKE_XFORM_ENUM(inName)
|
||||
#define XFORM_TRAP_DEFINE(inName, inOpcode, inPrimary) MAKE_XFORM_ENUM(inName)
|
||||
#define XFORM_INBONLY_DEFINE(inName, inOpcode, inPrimary) MAKE_XFORM_ENUM(inName)
|
||||
#define DO_XFORM
|
||||
enum XFormKind
|
||||
{
|
||||
#include "PPCInstructionTemplates.h"
|
||||
xfLast
|
||||
};
|
||||
|
||||
#undef XFORM_ARITH_DEFINE
|
||||
#undef XFORM_INAONLY_DEFINE
|
||||
#undef XFORM_CMP_DEFINE
|
||||
#undef XFORM_LOAD_DEFINE
|
||||
#undef XFORM_LOADU_DEFINE
|
||||
#undef XFORM_STORE_DEFINE
|
||||
#undef XFORM_STOREU_DEFINE
|
||||
#undef XFORM_TRAP_DEFINE
|
||||
#undef XFORM_INBONLY_DEFINE
|
||||
|
||||
struct XFormInstructionInfo
|
||||
{
|
||||
Uint16 opcode;
|
||||
Uint16 primary;
|
||||
char* formatStr;
|
||||
};
|
||||
|
||||
#define MAKE_XFORM_INFO(inOpcode, inPrimary, inString) \
|
||||
{ inOpcode, inPrimary, inString },
|
||||
|
||||
#define XFORM_ARITH_DEFINE(inName, inOpcode, inPrimary) MAKE_XFORM_INFO(inOpcode, inPrimary, #inName" r%d, r%d, r%d")
|
||||
#define XFORM_INAONLY_DEFINE(inName, inOpcode, inPrimary) MAKE_XFORM_INFO(inOpcode, inPrimary, #inName" r%d, r%d, r%d")
|
||||
#define XFORM_CMP_DEFINE(inName, inOpcode, inPrimary) MAKE_XFORM_INFO(inOpcode, inPrimary, #inName" r%d, r%d, r%d")
|
||||
#define XFORM_LOAD_DEFINE(inName, inOpcode, inPrimary) MAKE_XFORM_INFO(inOpcode, inPrimary, #inName" r%d, r%d, r%d")
|
||||
#define XFORM_LOADU_DEFINE(inName, inOpcode, inPrimary) MAKE_XFORM_INFO(inOpcode, inPrimary, #inName" r%d, r%d, r%d")
|
||||
#define XFORM_STORE_DEFINE(inName, inOpcode, inPrimary) MAKE_XFORM_INFO(inOpcode, inPrimary, #inName" r%d, r%d, r%d")
|
||||
#define XFORM_STOREU_DEFINE(inName, inOpcode, inPrimary) MAKE_XFORM_INFO(inOpcode, inPrimary, #inName" r%d, r%d, r%d")
|
||||
#define XFORM_TRAP_DEFINE(inName, inOpcode, inPrimary) MAKE_XFORM_INFO(inOpcode, inPrimary, #inName" r%d, r%d, r%d")
|
||||
#define XFORM_INBONLY_DEFINE(inName, inOpcode, inPrimary) MAKE_XFORM_INFO(inOpcode, inPrimary, #inName" r%d, r%d, r%d")
|
||||
|
||||
const XFormInstructionInfo sXFormInfos[] =
|
||||
{
|
||||
#include "PPCInstructionTemplates.h"
|
||||
};
|
||||
#undef DO_XFORM
|
||||
|
||||
static char*
|
||||
sFlagsExtensionTbl[] =
|
||||
{
|
||||
"", // none
|
||||
".", // pfRc
|
||||
"o", // pfOE
|
||||
"o." // pfRC | afOE
|
||||
};
|
||||
|
||||
|
||||
class XFormXY :
|
||||
public PPCInstructionXY
|
||||
{
|
||||
public:
|
||||
inline XFormXY(DataNode* inPrimitive, Pool& inPool, Uint8 inX, Uint8 inY, XFormKind inKind, PPCInsnFlags inFlags) :
|
||||
PPCInstructionXY(inPrimitive, inPool, inX, inY),
|
||||
mFlags(inFlags),
|
||||
mKind(inKind) { }
|
||||
virtual void formatToMemory(void* inStart, Uint32 inOffset, MdFormatter& /*inFormatter*/);
|
||||
|
||||
protected:
|
||||
virtual Uint8 getOPCD() { return (31); }
|
||||
virtual Uint8 getD() { return (udToRegisterNumber(getInstructionDefineBegin()[0])); }
|
||||
virtual Uint8 getA() { return (udToRegisterNumber(getInstructionUseBegin()[0])); }
|
||||
virtual Uint8 getB() { return (udToRegisterNumber(getInstructionUseBegin()[1])); }
|
||||
virtual Uint16 getXO() { return (sXFormInfos[mKind].opcode); }
|
||||
|
||||
PPCInsnFlags mFlags;
|
||||
XFormKind mKind;
|
||||
|
||||
#ifdef DEBUG
|
||||
public:
|
||||
virtual void printPretty(FILE* f) { fprintf(f, sXFormInfos[mKind].formatStr, flagsToExtension(), getD(), getA(), getB()); }
|
||||
const char* flagsToExtension() { return sFlagsExtensionTbl[mFlags & 3]; } // only look at OE and Rc bits
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
class TrapX :
|
||||
public XFormXY
|
||||
{
|
||||
public:
|
||||
inline TrapX(DataNode* inPrimitive, Pool& inPool, Condition2 inCondition, bool inSigned, PPCInsnFlags inFlags) :
|
||||
XFormXY(inPrimitive, inPool, 2, 0, xfTw, inFlags),
|
||||
mSigned(inSigned),
|
||||
mCond(inCondition) { }
|
||||
|
||||
protected:
|
||||
virtual Uint8 getD() { return (mSigned ? sSignedTrapConditions[mCond].to : sUnsignedTrapConditions[mCond].to); }
|
||||
|
||||
#ifdef DEBUG
|
||||
public:
|
||||
virtual void printPretty(FILE* f) { fprintf(f, sXFormInfos[mKind].formatStr, mSigned ? sSignedTrapConditions[mCond].name : sUnsignedTrapConditions[mCond].name, getA(), getB()); }
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool mSigned;
|
||||
Condition2 mCond;
|
||||
};
|
||||
|
||||
template<Uint8 inX, Uint8 inY, Uint8 inD, Uint8 inA, Uint8 inB>
|
||||
class LdX :
|
||||
public XFormXY
|
||||
{
|
||||
public:
|
||||
inline LdX(DataNode* inPrimitive, Pool& inPool, XFormKind inKind) :
|
||||
XFormXY(inPrimitive, inPool, inX, inY, inKind, pfNil) { assert(inKind >= xfLbzx && inKind <= xfLwzx); }
|
||||
|
||||
virtual Uint8 getD() { return (udToRegisterNumber(getInstructionDefineBegin()[inD])); }
|
||||
virtual Uint8 getA() { return (udToRegisterNumber(getInstructionUseBegin()[inA])); }
|
||||
virtual Uint8 getB() { return (udToRegisterNumber(getInstructionUseBegin()[inB])); }
|
||||
};
|
||||
|
||||
typedef LdX<2, 1, 0, 0, 1> LdX_C; // Rvalue <- Raddress Radddress
|
||||
typedef LdX<3, 2, 1, 1, 2> LdX_V; // mem Rvalue Raddress <- mem Raddress Raddress
|
||||
typedef LdX<3, 1, 0, 1, 2> LdX_; // Rvalue <- mem Raddress Raddress
|
||||
|
||||
|
||||
// mem <- mem Raddress Raddress Value
|
||||
class StX :
|
||||
public XFormXY
|
||||
{
|
||||
public:
|
||||
inline StX(DataNode* inPrimitive, Pool& inPool, XFormKind inKind) :
|
||||
XFormXY(inPrimitive, inPool, 4, 1, inKind, pfNil) { assert(inKind >= xfStbx && inKind <= xfStwx); }
|
||||
|
||||
virtual Uint8 getD() { return (udToRegisterNumber(getInstructionUseBegin()[3])); }
|
||||
virtual Uint8 getA() { return (udToRegisterNumber(getInstructionUseBegin()[1])); }
|
||||
virtual Uint8 getB() { return (udToRegisterNumber(getInstructionUseBegin()[2])); }
|
||||
};
|
||||
|
||||
|
||||
class ArithX :
|
||||
public XFormXY
|
||||
{
|
||||
public:
|
||||
inline ArithX(DataNode* inPrimitive, Pool& inPool, XFormKind inKind, PPCInsnFlags inFlags) :
|
||||
XFormXY(inPrimitive, inPool, 2, ((inFlags & pfRc) == 0) ? 1 : 2, inKind, inFlags) { }
|
||||
};
|
||||
|
||||
class XOInAOnly :
|
||||
public XFormXY
|
||||
{
|
||||
public:
|
||||
// if we want to use the lswi, need to deal with memory edge
|
||||
inline XOInAOnly(DataNode* inPrimitive, Pool& inPool, XFormKind inKind, PPCInsnFlags inFlags) :
|
||||
XFormXY(inPrimitive, inPool, 1, ((inFlags & pfRc) == 0) ? 1 : 2, inKind, inFlags) {}
|
||||
|
||||
protected:
|
||||
virtual Uint8 getA() { return 0; }
|
||||
};
|
||||
|
||||
class XInAOnly :
|
||||
public XFormXY
|
||||
{
|
||||
public:
|
||||
// if we want to use the lswi, need to deal with memory edge
|
||||
inline XInAOnly(DataNode* inPrimitive, Pool& inPool, XFormKind inKind, PPCInsnFlags inFlags, Uint8 inB) :
|
||||
XFormXY(inPrimitive, inPool, 1, ((inFlags & pfRc) == 0) ? 1 : 2, inKind, (inFlags | pfOE)),
|
||||
mB(inB) { }
|
||||
|
||||
protected:
|
||||
virtual Uint8 getD() { return (XFormXY::getA()); }
|
||||
virtual Uint8 getA() { return (XFormXY::getD()); }
|
||||
virtual Uint8 getB() { assert (mB < (1 << 6)); return mB; }
|
||||
Uint8 mB;
|
||||
};
|
||||
|
||||
class XInBOnly :
|
||||
public XFormXY
|
||||
{
|
||||
public:
|
||||
inline XInBOnly(DataNode* inPrimitive, Pool& inPool, XFormKind inKind, PPCInsnFlags inFlags) :
|
||||
XFormXY(inPrimitive, inPool, 1, ((inFlags & pfRc) == 0) ? 1 : 2, inKind, inFlags) { }
|
||||
|
||||
protected:
|
||||
virtual Uint8 getA() { return (0); }
|
||||
virtual Uint8 getB() { return (udToRegisterNumber(getInstructionUseBegin()[0])); }
|
||||
virtual InstructionFlags getFlags() const { return ((mKind == xfFmr) ? ifCopy : ifNone); }
|
||||
Uint8 mA;
|
||||
};
|
||||
|
||||
class CmpIX :
|
||||
public XFormXY
|
||||
{
|
||||
public:
|
||||
inline CmpIX(DataNode* inPrimitive, Pool& inPool, XFormKind inKind, PPCInsnFlags inFlags) :
|
||||
XFormXY(inPrimitive, inPool, 2, 1, inKind, inFlags) { }
|
||||
|
||||
protected:
|
||||
virtual Uint8 getD() { return (0); } // always use cr0
|
||||
};
|
||||
|
||||
class CmpFX :
|
||||
public XFormXY
|
||||
{
|
||||
public:
|
||||
inline CmpFX(DataNode* inPrimitive, Pool& inPool, XFormKind inKind, PPCInsnFlags inFlags) :
|
||||
XFormXY(inPrimitive, inPool, 2, 1, inKind, inFlags) { }
|
||||
|
||||
protected:
|
||||
virtual Uint8 getD() { return (0); } // always use cr0
|
||||
};
|
||||
|
||||
#ifdef __MWERKS__
|
||||
#pragma mark -
|
||||
#pragma mark ¥ÊIForm ¥
|
||||
#endif
|
||||
|
||||
class BranchI :
|
||||
public PPCInstructionXY
|
||||
{
|
||||
public:
|
||||
inline BranchI(DataNode* inPrimitive, Pool& inPool, ControlNode& inTarget, bool inAbsolute = false, bool inLink = false) :
|
||||
PPCInstructionXY(inPrimitive, inPool, 0, 0), mTarget(inTarget)
|
||||
{ mIForm.OPCD = 18; mIForm.LI = 0; mIForm.AA = inAbsolute; mIForm.LK = inLink;}
|
||||
|
||||
virtual void formatToMemory(void* inStart, Uint32 inOffset, MdFormatter& /*inFormatter*/);
|
||||
#ifdef DEBUG
|
||||
virtual void printPretty(FILE* f) { fprintf(f, "bxx %d", mTarget.getNativeOffset()); }
|
||||
#endif
|
||||
|
||||
protected:
|
||||
ControlNode& mTarget;
|
||||
|
||||
struct
|
||||
{
|
||||
int OPCD:6;
|
||||
int LI:24;
|
||||
int AA:1;
|
||||
int LK:1;
|
||||
} mIForm;
|
||||
};
|
||||
|
||||
#ifdef __MWERKS__
|
||||
#pragma mark -
|
||||
#pragma mark ¥ÊIForm ¥
|
||||
#endif
|
||||
|
||||
// the bits in order (or offsets into a condition register)
|
||||
// neg (lt)
|
||||
// pos (gt)
|
||||
// zero (eq)
|
||||
// summary overflow (so)
|
||||
// 0010y branch if cond is false
|
||||
// 0110y branch if cond is true
|
||||
struct BranchCondition
|
||||
{
|
||||
char* name;
|
||||
Uint8 bo;
|
||||
Uint8 bi;
|
||||
};
|
||||
|
||||
// indexed by Condition2
|
||||
const BranchCondition sBranchConditions[] =
|
||||
{
|
||||
{"always", 0, 0},
|
||||
{"lt", 12, 0},
|
||||
{"eq", 12, 2},
|
||||
{"le", 4, 1},
|
||||
{"gt", 12, 1},
|
||||
{"ne", 4, 2},
|
||||
{"ge", 4, 0}
|
||||
};
|
||||
|
||||
#if 0
|
||||
BO BI
|
||||
cond0, // 0000 Always false
|
||||
condLt, // 0001 arg1 < arg2 0110y 0 lt
|
||||
condEq, // 0010 arg1 = arg2 0110y 2 eq
|
||||
condLe, // 0011 arg1 <= arg2 0010y 1 not gt
|
||||
condGt, // 0100 arg1 > arg2 0110y 1 gt
|
||||
condLgt, // 0101 arg1 <> arg2 0010y 2 not eq
|
||||
condGe, // 0110 arg1 >= arg2 0010y 0 not lt
|
||||
|
||||
-- only up to these are supported
|
||||
condOrd, // 0111 arg1 <=> arg2 (i.e. arg1 and arg2 are ordered) 0110y 3 so ?? nan
|
||||
condUnord, // 1000 arg1 ? arg2 (i.e. arg1 and arg2 are unordered) 0010y 3 not so
|
||||
condULt, // 1001 arg1 ?< arg2 do we have to deal with these below here??
|
||||
condUEq, // 1010 arg1 ?= arg2
|
||||
condULe, // 1011 arg1 ?<= arg2
|
||||
condUGt, // 1100 arg1 ?> arg2
|
||||
condNe, // 1101 arg1 != arg2
|
||||
condUGe, // 1110 arg1 ?>= arg2
|
||||
cond1 // 1111 Always true
|
||||
#endif
|
||||
|
||||
class BranchCB :
|
||||
public PPCInstructionXY
|
||||
{
|
||||
public:
|
||||
BranchCB(DataNode* inPrimitive, Pool& inPool, ControlNode& inTarget, Condition2 inCondition, bool inAbsolute = false, bool inLink = false);
|
||||
virtual void formatToMemory(void* inStart, Uint32 inOffset, MdFormatter& /*inFormatter*/);
|
||||
|
||||
|
||||
protected:
|
||||
ControlNode& mTarget;
|
||||
|
||||
struct
|
||||
{
|
||||
unsigned int OPCD:6;
|
||||
unsigned int BO:5;
|
||||
// unsigned int Y:1;
|
||||
unsigned int BI:5;
|
||||
unsigned int BD:14;
|
||||
unsigned int AA:1;
|
||||
unsigned int LK:1;
|
||||
} mCForm;
|
||||
|
||||
#if DEBUG
|
||||
public:
|
||||
virtual void printPretty(FILE* f) { fprintf(f, "b%s %d", sBranchConditions[mCond].name, mTarget.getNativeOffset()); }
|
||||
|
||||
protected:
|
||||
Condition2 mCond;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
#ifdef __MWERKS__
|
||||
#pragma mark -
|
||||
#pragma mark ¥ÊM-Form ¥
|
||||
#endif
|
||||
|
||||
#define DO_MFORM
|
||||
struct MFormInstructionInfo
|
||||
{
|
||||
Uint16 opcode;
|
||||
char* formatStr;
|
||||
};
|
||||
|
||||
|
||||
#define MAKE_MFORM_ENUM(inName) mf##inName,
|
||||
#define MFORM_DEFINE(inName, inOpcode) MAKE_MFORM_ENUM(inName)
|
||||
|
||||
enum MFormKind
|
||||
{
|
||||
#include "PPCInstructionTemplates.h"
|
||||
mfLast
|
||||
};
|
||||
|
||||
#undef MFORM_DEFINE
|
||||
|
||||
#define MAKE_MFORM_INFO(inName, inOpcode) \
|
||||
{ inOpcode, #inName"%s r%d, r%d, r%d, %d, %d"},
|
||||
|
||||
#define MFORM_DEFINE(inName, inOpcode) MAKE_MFORM_INFO(inName, inOpcode)
|
||||
|
||||
const MFormInstructionInfo sMFormInfos[] =
|
||||
{
|
||||
#include "PPCInstructionTemplates.h"
|
||||
};
|
||||
|
||||
#undef MFORM_DEFINE
|
||||
#undef DO_MFORM
|
||||
|
||||
class MForm :
|
||||
public PPCInstructionXY
|
||||
{
|
||||
public:
|
||||
inline MForm(DataNode* inPrimitive, Pool& inPool, Uint8 inUses, MFormKind inKind, Uint8 inMaskBegin, Uint8 inMaskEnd, PPCInsnFlags inFlags) :
|
||||
PPCInstructionXY(inPrimitive, inPool, inUses, 1 + (bool) (inFlags & pfRc)),
|
||||
mFlags(inFlags),
|
||||
mKind(inKind),
|
||||
mMaskBegin(inMaskBegin),
|
||||
mMaskEnd(inMaskEnd) { }
|
||||
virtual void formatToMemory(void* inStart, Uint32 inOffset, MdFormatter& /*inFormatter*/);
|
||||
|
||||
|
||||
|
||||
protected:
|
||||
Uint16 getOPCD() { return (sXFormInfos[mKind].opcode); }
|
||||
Uint8 getS() { return (udToRegisterNumber(getInstructionUseBegin()[0])); }
|
||||
Uint8 getA() { return (udToRegisterNumber(getInstructionDefineBegin()[0])); }
|
||||
virtual Uint8 getB() { return (udToRegisterNumber(getInstructionUseBegin()[1])); }
|
||||
Uint8 getMB() { return (mMaskBegin); }
|
||||
Uint8 getME() { return (mMaskEnd); }
|
||||
|
||||
#ifdef DEBUG
|
||||
public:
|
||||
virtual void printPretty(FILE* f) { fprintf(f, sMFormInfos[mKind].formatStr, flagsToExtension(), getA(), getS(), getB(), getMB(), getME()); }
|
||||
protected:
|
||||
const char* flagsToExtension() { return sFlagsExtensionTbl[mFlags & 1]; /* only look at Rc bit */ }
|
||||
#endif
|
||||
|
||||
PPCInsnFlags mFlags;
|
||||
MFormKind mKind;
|
||||
Uint8 mMaskBegin;
|
||||
Uint8 mMaskEnd;
|
||||
};
|
||||
|
||||
|
||||
class MFormInAOnly :
|
||||
public MForm
|
||||
{
|
||||
public:
|
||||
inline MFormInAOnly(DataNode* inPrimitive, Pool& inPool, MFormKind inKind, Uint8 inB, Uint8 inMaskBegin, Uint8 inMaskEnd, PPCInsnFlags inFlags) :
|
||||
MForm(inPrimitive, inPool, 1, inKind, inMaskBegin, inMaskEnd, inFlags),
|
||||
mB(inB) { }
|
||||
protected:
|
||||
virtual Uint8 getB() { return (mB); }
|
||||
|
||||
Uint8 mB;
|
||||
};
|
||||
|
||||
#ifdef __MWERKS__
|
||||
#pragma mark -
|
||||
#pragma mark ¥ÊA-Form ¥
|
||||
#endif
|
||||
|
||||
|
||||
#define DO_AFORM
|
||||
|
||||
|
||||
#define MAKE_AFORM_ENUM(inName) af##inName,
|
||||
#define AFORM_DEFINE(inName, inOpcode, inXo, inHasA, inHasB, inHasC, inRc) MAKE_AFORM_ENUM(inName)
|
||||
|
||||
enum AFormKind
|
||||
{
|
||||
#include "PPCInstructionTemplates.h"
|
||||
afLast
|
||||
};
|
||||
|
||||
#undef AFORM_DEFINE
|
||||
|
||||
template <bool tHasA, bool tHasB, bool tHasC, bool tRc>
|
||||
class AForm :
|
||||
public PPCInstructionXY
|
||||
{
|
||||
public:
|
||||
inline AForm(DataNode* inPrimitive, Pool& inPool, AFormKind inKind) :
|
||||
PPCInstructionXY(inPrimitive, inPool, tHasA + tHasB + tHasC, tRc + 1),
|
||||
mKind(inKind) {}
|
||||
|
||||
protected:
|
||||
Uint16 getOPCD() { return (sAFormInfos[mKind].opcode); }
|
||||
Uint8 getD() { return (udToRegisterNumber(getInstructionDefineBegin()[0])); }
|
||||
Uint8 getA() { return (tHasA ? udToRegisterNumber(getInstructionUseBegin()[0]) : 0); }
|
||||
Uint8 getB() { return (tHasB ? udToRegisterNumber(getInstructionUseBegin()[tHasA]) : 0); }
|
||||
Uint8 getC() { return (tHasC ? udToRegisterNumber(getInstructionUseBegin()[tHasA + tHasB]) : 0); }
|
||||
|
||||
public:
|
||||
virtual void
|
||||
formatToMemory(void* inStart, Uint32 /*inOffset*/, MdFormatter& /*inFormatter*/)
|
||||
{
|
||||
struct
|
||||
{
|
||||
uint OPCD:6;
|
||||
uint D:5;
|
||||
uint A:5;
|
||||
uint B:5;
|
||||
uint C:5;
|
||||
uint XO:5;
|
||||
uint RC:1;
|
||||
} aForm;
|
||||
|
||||
aForm.OPCD = sAFormInfos[mKind].opcode;
|
||||
aForm.D = getD();
|
||||
aForm.A = getA();
|
||||
aForm.B = getB();
|
||||
aForm.C = getC();
|
||||
aForm.XO = sAFormInfos[mKind].xo;
|
||||
aForm.RC = tRc;
|
||||
|
||||
*(Uint32*) inStart = *(Uint32*) &aForm;
|
||||
}
|
||||
|
||||
static Instruction&
|
||||
createInstruction(DataNode& inPrimitive, Pool& mPool, AFormKind inKind)
|
||||
{
|
||||
return *new(mPool) AForm<tHasA, tHasB, tHasC, tRc>(&inPrimitive, mPool, inKind);
|
||||
}
|
||||
|
||||
protected:
|
||||
const AFormKind mKind;
|
||||
#ifdef DEBUG
|
||||
public:
|
||||
virtual void printPretty(FILE* /*f*/) {}
|
||||
protected:
|
||||
const char* flagsToExtension() { return sFlagsExtensionTbl[tRc]; } // only look at Rc bit
|
||||
#endif
|
||||
};
|
||||
|
||||
struct AFormInstructionInfo
|
||||
{
|
||||
Uint16 opcode;
|
||||
Uint16 xo;
|
||||
Instruction& (*creatorFunction)(DataNode&, Pool&, AFormKind);
|
||||
char* formatStr;
|
||||
};
|
||||
|
||||
#define MAKE_AFORM_INFO(inName, inOpcode, inXo, inHasA, inHasB, inHasC, inRc) \
|
||||
{ inOpcode, inXo, AForm<inHasA, inHasB, inHasC, inRc>::createInstruction, #inName"%s r%d, r%d, r%d, %d, %d"},
|
||||
|
||||
#define AFORM_DEFINE(inName, inOpcode, inXo, inHasA, inHasB, inHasC, inRc) MAKE_AFORM_INFO(inName, inOpcode, inXo, inHasA, inHasB, inHasC, inRc)
|
||||
|
||||
const AFormInstructionInfo sAFormInfos[] =
|
||||
{
|
||||
#include "PPCInstructionTemplates.h"
|
||||
};
|
||||
|
||||
#undef AFORM_DEFINE
|
||||
#undef DO_AFORM
|
||||
|
||||
#endif // _H_PPCINSTRUCTIONS
|
|
@ -0,0 +1,41 @@
|
|||
#!gmake
|
||||
#
|
||||
# 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.
|
||||
|
||||
# ppc-makefile.mac
|
||||
#
|
||||
# Scott M. Silver
|
||||
#
|
||||
# Assuming MOZ_SRC was set up (to point to the directory above ns), this
|
||||
# makefile generates the PrimitiveOperations and CodeGenerator specific
|
||||
# stuff (including BURG, etc)
|
||||
|
||||
NAD_SRC = "{MOZ_SRC}:ns:electricalfire:Compiler:CodeGenerator:md:ppc:ppc601-macos.nad"
|
||||
PRIMITIVEOPERATIONS = "{MOZ_SRC}:ns:electricalfire:Compiler:PrimitiveGraph:PrimitiveOperations"
|
||||
NAD = {PERL} "{MOZ_SRC}:ns:electricalfire:Tools:Nad:Nad.pl"
|
||||
|
||||
NAD_OUTPUTS = {NAD_SRC}.burg.h {NAD_SRC}.burg.cpp {PRIMITIVEOPERATIONS}.h {PRIMITIVEOPERATIONS}.cpp
|
||||
|
||||
{NAD_OUTPUTS} Ä {NAD_SRC} {PRIMITIVEOPERATIONS}
|
||||
{NAD} {NAD_SRC} {PRIMITIVEOPERATIONS} {PRIMITIVEOPERATIONS}.h {PRIMITIVEOPERATIONS}.cpp {NAD_SRC}.burg.h > BurgOut
|
||||
BURG -I <BurgOut > {NAD_SRC}.burg.cpp
|
||||
set TouchedFiles "`ResolveAlias {PRIMITIVEOPERATIONS}.h`"
|
||||
set TouchedFiles "{TouchedFiles},`ResolveAlias {NAD_SRC}.burg.h`"
|
||||
set TouchedFiles "{TouchedFiles},`ResolveAlias {NAD_SRC}.burg.cpp`"
|
||||
set TouchedFiles "{TouchedFiles},`ResolveAlias {PRIMITIVEOPERATIONS}.cpp`"
|
||||
export TouchedFiles
|
||||
|
||||
|
|
@ -0,0 +1,406 @@
|
|||
%top
|
||||
/* -*- 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.
|
||||
*/
|
||||
%
|
||||
|
||||
Vptr <- Vint()
|
||||
Vint <- Vptr()
|
||||
|
||||
Vint <- poConst_I() cost(1)
|
||||
%{
|
||||
genPrimConst_32(thisPrimitive);
|
||||
%}
|
||||
|
||||
Vptr <- poConst_A() cost(1)
|
||||
%{
|
||||
genPrimConst_32(thisPrimitive);
|
||||
%}
|
||||
|
||||
Vfloat <- poConst_F() cost(1)
|
||||
%{
|
||||
Flt32 constant = (*static_cast<const PrimConst *>(&thisPrimitive)).value.f;
|
||||
|
||||
Uint16 offset;
|
||||
mAccumulatorTOC.addData(&constant, sizeof(constant), offset);
|
||||
|
||||
Instruction& ldConstant = *new(mPool) LdD_RTOC(&thisPrimitive, mPool, dfLfs, offset);
|
||||
defineProducer(thisPrimitive, ldConstant, 0, vrcFloatingPoint);
|
||||
%}
|
||||
|
||||
Result <- poResult_I(Vint inResult) cost(1)
|
||||
%{
|
||||
genPrimResult_32(thisPrimitive, inResult);
|
||||
%}
|
||||
|
||||
Result <- poResult_A(Vptr inResult) cost(1)
|
||||
%{
|
||||
genPrimResult_32(thisPrimitive, inResult);
|
||||
%}
|
||||
|
||||
Result <- poResult_F(Vfloat inResult) cost(1)
|
||||
%{
|
||||
VirtualRegister* returnVr;
|
||||
|
||||
// generate: fmr fp0, fpResultRegister
|
||||
|
||||
// create a buffer copy instruction between the result
|
||||
// and the precolored register
|
||||
XInBOnly& copyInsn = *new(mPool) XInBOnly(&thisPrimitive, mPool, xfFmr, pfNil);
|
||||
|
||||
useProducer(inResult, copyInsn, 0, vrcFloatingPoint);
|
||||
returnVr = &defineTemporary(copyInsn, 0, vrcFloatingPoint);
|
||||
|
||||
// floating point results go somwhere FIX-ME
|
||||
returnVr->preColorRegister(1);
|
||||
|
||||
// create a special instruction for the RegisterAllocator which says this result
|
||||
// is used elsewhere (but not in this body of code).
|
||||
InsnExternalUse& externalUse = *new(mPool) InsnExternalUse(&thisPrimitive, mPool, 1);
|
||||
useTemporaryVR(externalUse, *returnVr, 0, vrcFloatingPoint);
|
||||
thisPrimitive.setInstructionRoot(&externalUse);
|
||||
%}
|
||||
|
||||
#define IF_CONTROL(inPrimOp, inCondition) \
|
||||
Control <- inPrimOp(Vcond) cost(1) \
|
||||
%{ \
|
||||
genBranch(thisPrimitive, inCondition); \
|
||||
%}
|
||||
|
||||
IF_CONTROL(poIfLt, condLt)
|
||||
IF_CONTROL(poIfNe, condNe)
|
||||
IF_CONTROL(poIfEq, condEq)
|
||||
IF_CONTROL(poIfLe, condLe)
|
||||
IF_CONTROL(poIfGt, condGt)
|
||||
IF_CONTROL(poIfLgt, condLgt)
|
||||
IF_CONTROL(poIfGe, condGe)
|
||||
|
||||
Vint <- poAnd_I(Vint inA, Vint inB) cost(1)
|
||||
%{
|
||||
genArithX_32(thisPrimitive, xfAnd, inA, inB);
|
||||
%}
|
||||
|
||||
Vint <- poOr_I(Vint inA, Vint inB) cost(1)
|
||||
%{
|
||||
genArithX_32(thisPrimitive, xfOr, inA, inB);
|
||||
%}
|
||||
|
||||
Vint <- poXor_I(Vint inA, Vint inB) cost(1)
|
||||
%{
|
||||
genArithX_32(thisPrimitive, xfXor, inA, inB);
|
||||
%}
|
||||
|
||||
Vint <- poAnd_I(Vint inA, Vint inB) cost(1)
|
||||
%{
|
||||
genArithX_32(thisPrimitive, xfAnd, inA, inB);
|
||||
%}
|
||||
|
||||
Vint <- poAdd_I(Vint inA, coConstS16 inConstant) cost(1)
|
||||
%{
|
||||
genArithD_32(thisPrimitive, dfAddi, inA, PrimConst::cast(inConstant).value.i);
|
||||
%}
|
||||
|
||||
Vint <- poAdd_I(coConstS16 inConstant, Vint inB) cost(1)
|
||||
%{
|
||||
genArithD_32(thisPrimitive, dfAddi, inB, PrimConst::cast(inConstant).value.i);
|
||||
%}
|
||||
|
||||
Vint <- poAdd_I(Vptr inA, Vint inB) cost(1)
|
||||
%{
|
||||
genArithX_32(thisPrimitive, xfAdd, inA, inB);
|
||||
%}
|
||||
|
||||
Vptr <- poAddU_A(Vptr inA, Vint inB) cost(1)
|
||||
%{
|
||||
genArithX_32(thisPrimitive, xfAdd, inA, inB);
|
||||
%}
|
||||
|
||||
Vptr <- poAdd_A(Vptr inA, Vint inB) cost(1)
|
||||
%{
|
||||
genArithX_32(thisPrimitive, xfAdd, inA, inB);
|
||||
%}
|
||||
|
||||
Vint <- poSub_I(Vptr inA, Vint inB) cost(1)
|
||||
%{
|
||||
genArithX_32(thisPrimitive, xfSubf, inB, inA);
|
||||
%}
|
||||
|
||||
|
||||
Vptr <- poSub_A(Vptr inA, Vint inB) cost(1)
|
||||
%{
|
||||
genArithX_32(thisPrimitive, xfSubf, inB, inA);
|
||||
%}
|
||||
|
||||
Vint <- poMul_I(Vint inA, Vint inB) cost(1)
|
||||
%{
|
||||
genArithX_32(thisPrimitive, xfMullw, inA, inB);
|
||||
%}
|
||||
|
||||
Vint <- poDiv_I(Vint inDividend, Vint inDivisor) cost(1)
|
||||
%{
|
||||
genArithX_32(thisPrimitive, xfDivw, inDividend, inDivisor);
|
||||
%}
|
||||
|
||||
Vint <- poDivE_I(Vint inDividend, Vint inDivisor) cost(1)
|
||||
%{
|
||||
// FIX-ME must handle divide by zero system exception
|
||||
genArithX_32(thisPrimitive, xfDivw, inDividend, inDivisor);
|
||||
%}
|
||||
|
||||
Vint <- poDivU_I(Vint inDividend, Vint inDivisor) cost(1)
|
||||
%{
|
||||
genArithX_32(thisPrimitive, xfDivwu, inDividend, inDivisor);
|
||||
%}
|
||||
|
||||
Vint <- poDivUE_I(Vint inDividend, Vint inDivisor) cost(1)
|
||||
%{
|
||||
// FIX-ME must handle divide by zero system exception
|
||||
genArithX_32(thisPrimitive, xfDivwu, inDividend, inDivisor);
|
||||
%}
|
||||
|
||||
|
||||
Vint <- poMod_I(Vint inDividend, Vint inDivisor) cost(1)
|
||||
%{
|
||||
genMod(thisPrimitive, xfDivw, inDividend, inDivisor);
|
||||
%}
|
||||
|
||||
Vint <- poModE_I(Vint inDividend, Vint inDivisor) cost(1)
|
||||
%{
|
||||
// FIX-ME must handle divide by zero system exception
|
||||
genMod(thisPrimitive, xfDivw, inDividend, inDivisor);
|
||||
%}
|
||||
|
||||
|
||||
Vint <- poModU_I(Vint inDividend, Vint inDivisor) cost(1)
|
||||
%{
|
||||
// FIX-ME must handle divide by zero system exception
|
||||
genMod(thisPrimitive, xfDivwu, inDividend, inDivisor);
|
||||
%}
|
||||
|
||||
Vint <- poShl_I(Vint inValue, Vint inShiftAmount) cost(1)
|
||||
%{
|
||||
XOutA& newInsn = *new(mPool) XOutA(&thisPrimitive, mPool, xfSlw, pfNil);
|
||||
|
||||
useProducer(inValue, newInsn, 0);
|
||||
useProducer(inShiftAmount, newInsn, 1);
|
||||
defineProducer(thisPrimitive, newInsn, 0);
|
||||
%}
|
||||
|
||||
|
||||
Vfloat <- poFAdd_F(Vfloat inA, Vfloat inB) cost(1)
|
||||
%{
|
||||
genArith_FP(thisPrimitive, afFadd, inA, inB);
|
||||
%}
|
||||
|
||||
Vcond <- poCmp_I(Vint inA, Vint inB) cost(1)
|
||||
%{
|
||||
CmpIX& newInsn = *new(mPool) CmpIX(&thisPrimitive, mPool, xfCmp, 0);
|
||||
|
||||
useProducer(inA, newInsn, 0);
|
||||
useProducer(inB, newInsn, 1);
|
||||
defineProducer(thisPrimitive, newInsn, 0);
|
||||
%}
|
||||
|
||||
Vcond <- poCmpU_I(Vint inA, Vint inB) cost(1)
|
||||
%{
|
||||
CmpIX& newInsn = *new(mPool) CmpIX(&thisPrimitive, mPool, xfCmpl, 0);
|
||||
|
||||
useProducer(inA, newInsn, 0);
|
||||
useProducer(inB, newInsn, 1);
|
||||
defineProducer(thisPrimitive, newInsn, 0);
|
||||
%}
|
||||
|
||||
Exception <- poChkNull(Vptr inTestValue) cost(1)
|
||||
%{
|
||||
genThrowIfZero(thisPrimitive, inTestValue);
|
||||
%}
|
||||
|
||||
|
||||
Exception <- poLimit(Vint inIndex, Vint inBound) cost(1)
|
||||
%{
|
||||
TrapX& newInsn = *new(mPool) TrapX(&thisPrimitive, mPool, condGe, false, pfNil);
|
||||
useProducer(inIndex, newInsn, 0);
|
||||
useProducer(inBound, newInsn, 1);
|
||||
thisPrimitive.setInstructionRoot(&newInsn);
|
||||
%}
|
||||
|
||||
#define LD_FIXED(inPrimOp, inReturnType, inOpcode) \
|
||||
inReturnType <- inPrimOp(Vptr[1] inAddress) cost(1) \
|
||||
%{ \
|
||||
genLd_Fixed(thisPrimitive, inOpcode, inAddress); \
|
||||
%}
|
||||
|
||||
#define LDE_FIXED(inPrimOp, inReturnType, inOpcode) \
|
||||
inReturnType <- inPrimOp(Vptr[1] inAddress) cost(1) \
|
||||
%{ \
|
||||
genThrowIfZero(thisPrimitive, inAddress); \
|
||||
genLd_Fixed(thisPrimitive, inOpcode, inAddress); \
|
||||
%}
|
||||
|
||||
#define LD_SEX(inPrimOp, inLdOpcode, inSexOpcode) \
|
||||
Vint <- inPrimOp(Vptr[1] inAddress) cost(1) \
|
||||
%{ \
|
||||
genLd_Fixed(thisPrimitive, inLdOpcode, inAddress); \
|
||||
\
|
||||
XInAOnly &ext = *new(mPool) XInAOnly(&thisPrimitive, mPool, inSexOpcode, pfNil, 0); \
|
||||
useProducer(thisPrimitive, ext, 0); \
|
||||
defineProducer(thisPrimitive, ext, 0); \
|
||||
%}
|
||||
|
||||
#define LDE_SEX(inPrimOp, inLdOpcode, inSexOpcode) \
|
||||
Vint <- inPrimOp(Vptr[1] inAddress) cost(1) \
|
||||
%{ \
|
||||
genThrowIfZero(thisPrimitive, inAddress); \
|
||||
genLd_Fixed(thisPrimitive, inLdOpcode, inAddress); \
|
||||
\
|
||||
XInAOnly &ext = *new(mPool) XInAOnly(&thisPrimitive, mPool, inSexOpcode, pfNil, 0); \
|
||||
useProducer(thisPrimitive, ext, 0); \
|
||||
defineProducer(thisPrimitive, ext, 0); \
|
||||
%}
|
||||
|
||||
#define LD_FLOATING(inPrimOp, inReturnType, inOpcode) \
|
||||
inReturnType <- inPrimOp(Vptr[1] inAddress) cost(1) \
|
||||
%{ \
|
||||
genLd_FP(thisPrimitive, inOpcode, inAddress); \
|
||||
%}
|
||||
|
||||
|
||||
#define LDE_FLOATING(inPrimOp, inReturnType, inOpcode) \
|
||||
inReturnType <- inPrimOp(Vptr[1] inAddress) cost(1) \
|
||||
%{ \
|
||||
genThrowIfZero(thisPrimitive, inAddress); \
|
||||
genLd_FP(thisPrimitive, inOpcode, inAddress); \
|
||||
%}
|
||||
|
||||
|
||||
LD_FIXED(poLd_I, Vint, dfLwz)
|
||||
LD_FIXED(poLd_A, Vptr, dfLwz)
|
||||
LD_FLOATING(poLd_F, Vfloat, dfLfs)
|
||||
|
||||
LDE_FIXED(poLdE_I, Vint, dfLwz)
|
||||
LDE_FIXED(poLdE_A, Vptr, dfLwz)
|
||||
LDE_FLOATING(poLdE_F, Vfloat, dfLfs)
|
||||
|
||||
LD_SEX(poLdS_B, dfLbz, xfExtsb)
|
||||
LD_SEX(poLdS_H, dfLhz, xfExtsh)
|
||||
LDE_SEX(poLdS_B, dfLbz, xfExtsb)
|
||||
LDE_SEX(poLdS_H, dfLhz, xfExtsh)
|
||||
|
||||
LD_FIXED(poLdU_B, Vint, dfLbz)
|
||||
LD_FIXED(poLdU_H, Vint, dfLhz)
|
||||
LDE_FIXED(poLdUE_B, Vint, dfLbz)
|
||||
LDE_FIXED(poLdUE_H, Vint, dfLhz)
|
||||
|
||||
|
||||
Store <- poSt_B(Vptr[1] inAddress, Vint inValue) cost(1)
|
||||
%{
|
||||
genSt(thisPrimitive, dfStb, inAddress, inValue);
|
||||
%}
|
||||
|
||||
Store <- poSt_H(Vptr[1] inAddress, Vint inValue) cost(1)
|
||||
%{
|
||||
genSt(thisPrimitive, dfSth, inAddress, inValue);
|
||||
%}
|
||||
|
||||
Store <- poSt_I(Vptr[1] inAddress, Vint inValue) cost(1)
|
||||
%{
|
||||
genSt(thisPrimitive, dfStw, inAddress, inValue);
|
||||
%}
|
||||
|
||||
Store <- poSt_F(Vptr[1] inAddress, Vfloat inValue) cost(1)
|
||||
%{
|
||||
genSt(thisPrimitive, dfStfs, inAddress, inValue);
|
||||
%}
|
||||
|
||||
Store <- poSt_A(Vptr[1] inAddress, Vptr inValue) cost(1)
|
||||
%{
|
||||
genSt(thisPrimitive, dfStw, inAddress, inValue);
|
||||
%}
|
||||
|
||||
Store <- poStE_B(Vptr[1] inAddress, Vint inValue) cost(1)
|
||||
%{
|
||||
genThrowIfZero(thisPrimitive, inAddress); // FIX-ME eliminate on AIX
|
||||
genSt(thisPrimitive, dfStb, inAddress, inValue);
|
||||
%}
|
||||
|
||||
Store <- poStE_H(Vptr[1] inAddress, Vint inValue) cost(1)
|
||||
%{
|
||||
genThrowIfZero(thisPrimitive, inAddress); // FIX-ME eliminate on AIX
|
||||
genSt(thisPrimitive, dfSth, inAddress, inValue);
|
||||
%}
|
||||
|
||||
Store <- poStE_I(Vptr[1] inAddress, Vint inValue) cost(1)
|
||||
%{
|
||||
genThrowIfZero(thisPrimitive, inAddress); // FIX-ME eliminate on AIX
|
||||
genSt(thisPrimitive, dfStw, inAddress, inValue);
|
||||
%}
|
||||
|
||||
Store <- poStE_F(Vptr[1] inAddress, Vfloat inValue) cost(1)
|
||||
%{
|
||||
genThrowIfZero(thisPrimitive, inAddress); // FIX-ME eliminate on AIX
|
||||
genSt(thisPrimitive, dfStfs, inAddress, inValue);
|
||||
%}
|
||||
|
||||
Store <- poStE_A(Vptr[1] inAddress, Vptr inValue) cost(1)
|
||||
%{
|
||||
genThrowIfZero(thisPrimitive, inAddress); // FIX-ME eliminate on AIX
|
||||
genSt(thisPrimitive, dfStw, inAddress, inValue);
|
||||
%}
|
||||
|
||||
|
||||
Tuple <- poSysCall() cost(1)
|
||||
%{
|
||||
CallS_& newInsn = *new CallS_(&thisPrimitive, mPool, CallS_::numberOfArguments(thisPrimitive), CallS_::hasReturnValue(thisPrimitive), *this, PrimSysCall::cast(thisPrimitive).sysCall.function);
|
||||
%}
|
||||
|
||||
Tuple <- poSysCallE() cost(1)
|
||||
%{
|
||||
CallS_& newInsn = *new CallS_(&thisPrimitive, mPool, CallS_::numberOfArguments(thisPrimitive), CallS_::hasReturnValue(thisPrimitive), *this, PrimSysCall::cast(thisPrimitive).sysCall.function);
|
||||
%}
|
||||
|
||||
Tuple <- poSysCallC() cost(1)
|
||||
%{
|
||||
CallS_C& newInsn = *new CallS_C(&thisPrimitive, mPool, CallS_C::numberOfArguments(thisPrimitive), CallS_C::hasReturnValue(thisPrimitive), *this, PrimSysCall::cast(thisPrimitive).sysCall.function);
|
||||
%}
|
||||
|
||||
Tuple <- poSysCallEC() cost(1)
|
||||
%{
|
||||
CallS_C& newInsn = *new CallS_C(&thisPrimitive, mPool, CallS_C::numberOfArguments(thisPrimitive), CallS_C::hasReturnValue(thisPrimitive), *this, PrimSysCall::cast(thisPrimitive).sysCall.function);
|
||||
%}
|
||||
|
||||
Tuple <- poSysCallV() cost(1)
|
||||
%{
|
||||
CallS_V& newInsn = *new CallS_V(&thisPrimitive, mPool, CallS_V::numberOfArguments(thisPrimitive), CallS_V::hasReturnValue(thisPrimitive), *this, PrimSysCall::cast(thisPrimitive).sysCall.function);
|
||||
%}
|
||||
|
||||
Tuple <- poSysCallEV() cost(1)
|
||||
%{
|
||||
CallS_V& newInsn = *new CallS_V(&thisPrimitive, mPool, CallS_V::numberOfArguments(thisPrimitive), CallS_V::hasReturnValue(thisPrimitive), *this, PrimSysCall::cast(thisPrimitive).sysCall.function);
|
||||
%}
|
||||
|
||||
Tuple <- poCall(Vptr) cost(1)
|
||||
%{
|
||||
// Dynamic call
|
||||
CallD_& newInsn = *new CallD_(&thisPrimitive, mPool, CallD_::numberOfArguments(thisPrimitive), CallD_::hasReturnValue(thisPrimitive), *this);
|
||||
%}
|
||||
|
||||
Tuple <- poCall(poConst_A) cost(1)
|
||||
%{
|
||||
// Static call
|
||||
Call_& newInsn = *new Call_(&thisPrimitive, mPool, Call_::numberOfArguments(thisPrimitive), Call_::hasReturnValue(thisPrimitive), *this);
|
||||
%}
|
||||
|
|
@ -0,0 +1,286 @@
|
|||
%top
|
||||
/* -*- 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.
|
||||
*/
|
||||
#include "Burg.h"
|
||||
%
|
||||
|
||||
%terminals
|
||||
%
|
||||
|
||||
%startsymbols
|
||||
Control
|
||||
Result
|
||||
Exception
|
||||
Store
|
||||
Vcond
|
||||
Vint
|
||||
Vlong
|
||||
Vfloat
|
||||
Vdouble
|
||||
Vptr
|
||||
Cint
|
||||
Clong
|
||||
Cfloat
|
||||
Cdouble
|
||||
Cptr
|
||||
Tuple
|
||||
%
|
||||
|
||||
%grammar
|
||||
Vint: coReg_I $1 $
|
||||
Vlong: coReg_L $1 $
|
||||
Vfloat: coReg_F $1 $
|
||||
Vdouble: coReg_D $1 $
|
||||
Vptr: coReg_A $1 $
|
||||
Vcond: coReg_C $1 $
|
||||
Store: coReg_M $1 $
|
||||
Cint: coReg_I $1 $
|
||||
Clong: coReg_L $1 $
|
||||
Cfloat: coReg_F $1 $
|
||||
Cdouble: coReg_D $1 $
|
||||
Cptr: coReg_A $1 $
|
||||
|
||||
Vint: poConst_I $1 $emConst_I
|
||||
Vlong: poConst_L $1 $emConst_L
|
||||
Vfloat: poConst_F $1 $emConst_F
|
||||
//Vdouble: poConst_D $1 $emConst_D
|
||||
Vptr: poConst_A $1 $emConst_A
|
||||
//Vcond: poConst_C $1 $emConst_C
|
||||
|
||||
//Store: poBreak(Store) $1 $emBreak
|
||||
|
||||
Vint: poArg_I $1 $
|
||||
Vlong: poArg_L $1 $
|
||||
Vfloat: poArg_F $1 $
|
||||
Vdouble: poArg_D $1 $
|
||||
Vptr: poArg_A $1 $
|
||||
Store: poArg_M $1 $
|
||||
|
||||
Result: poResult_I(Vint) $1 $emResult_I
|
||||
//Result: poResult_L(Vlong) $1 $emResult_L
|
||||
Result: poResult_F(Vfloat) $1 $emResult_F
|
||||
//Result: poResult_D(Vdouble) $1 $emResult_D
|
||||
Result: poResult_A(Vptr) $1 $emResult_A
|
||||
Result: poResult_M(Store) $1 $
|
||||
|
||||
Control: poIfLt(Vcond) $1 $emIfLt
|
||||
Control: poIfEq(Vcond) $1 $emIfEq
|
||||
Control: poIfLe(Vcond) $1 $emIfLe
|
||||
Control: poIfGt(Vcond) $1 $emIfGt
|
||||
Control: poIfLgt(Vcond) $1 $emIfLgt
|
||||
Control: poIfGe(Vcond) $1 $emIfGe
|
||||
Control: poIfOrd(Vcond) $1 $emIfOrd
|
||||
//Control: poIfUnord(Vcond) $1 $emIfUnord
|
||||
Control: poIfULt(Vcond) $1 $emIfULt
|
||||
Control: poIfUEq(Vcond) $1 $emIfUEq
|
||||
Control: poIfULe(Vcond) $1 $emIfULe
|
||||
Control: poIfUGt(Vcond) $1 $emIfUGt
|
||||
Control: poIfNe(Vcond) $1 $emIfNe
|
||||
Control: poIfUGe(Vcond) $1 $emIfUGe
|
||||
|
||||
//Control: poSwitch(Vint) $1 $emSwitch
|
||||
|
||||
Vint: poAnd_I(Vint, Vint) $1 $emAnd_I
|
||||
Vlong: poAnd_L(Vlong, Vlong) $1 $emAnd_L
|
||||
Vint: poOr_I(Vint, Vint) $1 $emOr_I
|
||||
Vlong: poOr_L(Vlong, Vlong) $1 $emOr_L
|
||||
Vint: poXor_I(Vint, Vint) $1 $emXor_I
|
||||
Vlong: poXor_L(Vlong, Vlong) $1 $emXor_L
|
||||
|
||||
Vint: poAdd_I(Vint, Vint) $1 $emAdd_I
|
||||
//Vlong: poAdd_L(Vlong, Vlong) $1 $emAdd_L
|
||||
Vptr: poAdd_A(Vptr, Vint) $1 $emAdd_A
|
||||
Vptr: poAddU_A(Vptr, Vint) $1 $emAddU_A
|
||||
|
||||
Vint: poSub_I(Vint, Vint) $1 $emSub_I
|
||||
//Vlong: poSub_L(Vlong, Vlong) $1 $emSub_L
|
||||
Vptr: poSub_A(Vptr, Vint) $1 $emSub_A
|
||||
Vptr: poSubU_A(Vptr, Vint) $1 $emSubU_A
|
||||
Vint: poSubA_I(Vptr, Vptr) $1 $emSubA_I
|
||||
|
||||
Vint: poMul_I(Vint, Vint) $1 $emMul_I
|
||||
//Vlong: poMul_L(Vlong, Vlong) $1 $emMul_L
|
||||
|
||||
Vint: poDiv_I(Vint, Vint) $1 $emDiv_I
|
||||
//Vint: poDiv_L(Vlong, Vlong) $1 $emDiv_L
|
||||
Vint: poDivE_I(Vint, Vint) $1 $emDivE_I
|
||||
//Vint: poDivE_L(Vlong, Vlong) $1 $emDivE_L
|
||||
Vint: poDivU_I(Vint, Vint) $1 $emDivU_I
|
||||
//Vint: poDivU_L(Vlong, Vlong) $1 $emDivU_L
|
||||
Vint: poDivUE_I(Vint, Vint) $1 $emDivUE_I
|
||||
//Vint: poDivUE_L(Vlong, Vlong) $1 $emDivUE_L
|
||||
|
||||
Vint: poMod_I(Vint, Vint) $1 $emMod_I
|
||||
//Vint: poMod_L(Vlong, Vlong) $1 $emMod_L
|
||||
Vint: poModE_I(Vint, Vint) $1 $emModE_I
|
||||
//Vint: poModE_L(Vlong, Vlong) $1 $emModE_L
|
||||
//Vint: poModU_I(Vint, Vint) $1 $emModU_I
|
||||
//Vint: poModU_L(Vlong, Vlong) $1 $emModU_L
|
||||
//Vint: poModUE_I(Vint, Vint) $1 $emModUE_I
|
||||
//Vint: poModUE_L(Vlong, Vlong) $1 $emModUE_L
|
||||
|
||||
Vint: poShl_I(Vint, Vint) $1 $emShl_I
|
||||
//Vlong: poShl_L(Vlong, Vint) $1 $emShl_L
|
||||
|
||||
//Vint: poShr_I(Vint, Vint) $1 $emShr_I
|
||||
//Vlong: poShr_L(Vlong, Vint) $1 $emShr_L
|
||||
//Vint: poShrU_I(Vint, Vint) $1 $emShrU_I
|
||||
//Vlong: poShrU_L(Vlong, Vint) $1 $emShrU_L
|
||||
|
||||
//Vint: poExt_I(Vint, Cint) $1 $emExt_I
|
||||
//Vlong: poExt_L(Vlong, Cint) $1 $emExt_L
|
||||
|
||||
Vfloat: poFAdd_F(Vfloat, Vfloat) $1 $emFAdd_F
|
||||
//Vdouble: poFAdd_D(Vdouble, Vdouble) $1 $emFAdd_D
|
||||
|
||||
//Vfloat: poFSub_F(Vfloat, Vfloat) $1 $emFSub_F
|
||||
//Vdouble: poFSub_D(Vdouble, Vdouble) $1 $emFSub_D
|
||||
|
||||
//Vfloat: poFMul_F(Vfloat, Vfloat) $1 $emFMul_F
|
||||
//Vdouble: poFMul_D(Vdouble, Vdouble) $1 $emFMul_D
|
||||
|
||||
//Vfloat: poFDiv_F(Vfloat, Vfloat) $1 $emFDiv_F
|
||||
//Vdouble: poFDiv_D(Vdouble, Vdouble) $1 $emFDiv_D
|
||||
|
||||
//Vfloat: poFRem_F(Vfloat, Vfloat) $1 $emFRem_F
|
||||
//Vdouble: poFRem_D(Vdouble, Vdouble) $1 $emFRem_D
|
||||
|
||||
//Vint: poConvI_L(Vlong) $1 $emConvI_L
|
||||
//Vlong: poConvL_I(Vint) $1 $emConvL_I
|
||||
|
||||
//Vint: poFConvI_F(Vfloat) $1 $emFConvI_F
|
||||
//Vint: poFConvI_D(Vdouble) $1 $emFConvI_D
|
||||
//Vlong: poFConvL_F(Vfloat) $1 $emFConvL_F
|
||||
//Vlong: poFConvL_D(Vdouble) $1 $emFConvL_D
|
||||
//Vfloat: poFConvF_I(Vint) $1 $emFConvF_I
|
||||
//Vfloat: poFConvF_L(Vlong) $1 $emFConvF_L
|
||||
//Vfloat: poFConvF_D(Vdouble) $1 $emFConvF_D
|
||||
//Vdouble: poFConvD_I(Vint) $1 $emFConvD_I
|
||||
//Vdouble: poFConvD_L(Vlong) $1 $emFConvD_L
|
||||
//Vdouble: poFConvD_F(Vfloat) $1 $emFConvD_F
|
||||
|
||||
Vcond: poCmp_I(Vint, Vint) $1 $emCmp_I
|
||||
//Vcond: poCmp_L(Vlong, Vlong) $1 $emCmp_L
|
||||
//Vcond: poCmpU_I(Vint, Vint) $1 $emCmpU_I
|
||||
//Vcond: poCmpU_L(Vlong, Vlong) $1 $emCmpU_L
|
||||
//Vcond: poCmpU_A(Vptr, Vptr) $1 $emCmpU_A
|
||||
|
||||
//Vcond: poFCmp_F(Vfloat, Vfloat) $1 $emFCmp_F
|
||||
//Vcond: poFCmp_D(Vdouble, Vdouble) $1 $emFCmp_D
|
||||
|
||||
//Vint: poLt_I(Vcond) $1 $emLt_I
|
||||
//Vint: poEq_I(Vcond) $1 $emEq_I
|
||||
//Vint: poLe_I(Vcond) $1 $emLe_I
|
||||
//Vint: poGt_I(Vcond) $1 $emGt_I
|
||||
//Vint: poLgt_I(Vcond) $1 $emLgt_I
|
||||
//Vint: poGe_I(Vcond) $1 $emGe_I
|
||||
//Vint: poOrd_I(Vcond) $1 $emOrd_I
|
||||
//Vint: poUnord_I(Vcond) $1 $emUnord_I
|
||||
//Vint: poULt_I(Vcond) $1 $emULt_I
|
||||
//Vint: poUEq_I(Vcond) $1 $emUEq_I
|
||||
//Vint: poULe_I(Vcond) $1 $emULe_I
|
||||
//Vint: poUGt_I(Vcond) $1 $emUGt_I
|
||||
//Vint: poNe_I(Vcond) $1 $emNe_I
|
||||
//Vint: poUGe_I(Vcond) $1 $emUGe_I
|
||||
|
||||
//Vint: poCatL_I(Vcond) $1 $emCatL_I
|
||||
//Vint: poCatG_I(Vcond) $1 $emCatG_I
|
||||
//Vint: poCatCL_I(Vcond) $1 $emCatCL_I
|
||||
//Vint: poCatCG_I(Vcond) $1 $emCatCG_I
|
||||
|
||||
Exception: poChkNull(Vptr) $1 $emChkNull
|
||||
Exception: poLimit(Vint, Vint) $1 $emLimit
|
||||
|
||||
Vint: poLd_I(Vptr) $1 $emLd_I
|
||||
//Vlong: poLd_L(Vptr) $1 $emLd_L
|
||||
//Vfloat: poLd_F(Vptr) $1 $emLd_F
|
||||
//Vdouble: poLd_D(Vptr) $1 $emLd_D
|
||||
Vptr: poLd_A(Vptr) $1 $emLd_A
|
||||
//Vint: poLdE_I(Cptr) $1 $emLdE_I
|
||||
//Vlong: poLdE_L(Cptr) $1 $emLdE_L
|
||||
//Vfloat: poLdE_F(Cptr) $1 $emLdE_F
|
||||
//Vdouble: poLdE_D(Cptr) $1 $emLdE_D
|
||||
//Vptr: poLdE_A(Cptr) $1 $emLdE_A
|
||||
//Vint: poLdS_B(Vptr) $1 $emLdS_B
|
||||
//Vint: poLdS_H(Vptr) $1 $emLdS_H
|
||||
//Vint: poLdSE_B(Cptr) $1 $emLdSE_B
|
||||
//Vint: poLdSE_H(Cptr) $1 $emLdSE_H
|
||||
//Vint: poLdU_B(Vptr) $1 $emLdU_B
|
||||
//Vint: poLdU_H(Vptr) $1 $emLdU_H
|
||||
//Vint: poLdUE_B(Cptr) $1 $emLdUE_B
|
||||
//Vint: poLdUE_H(Cptr) $1 $emLdUE_H
|
||||
//Vint: poLdV_I(Vptr) $1 $emLdV_I
|
||||
//Vlong: poLdV_L(Vptr) $1 $emLdV_L
|
||||
//Vfloat: poLdV_F(Vptr) $1 $emLdV_F
|
||||
//Vdouble: poLdV_D(Vptr) $1 $emLdV_D
|
||||
//Vptr: poLdV_A(Vptr) $1 $emLdV_A
|
||||
//Vint: poLdVE_I(Cptr) $1 $emLdVE_I
|
||||
//Vlong: poLdVE_L(Cptr) $1 $emLdVE_L
|
||||
//Vfloat: poLdVE_F(Cptr) $1 $emLdVE_F
|
||||
//Vdouble: poLdVE_D(Cptr) $1 $emLdVE_D
|
||||
//Vptr: poLdVE_A(Cptr) $1 $emLdVE_A
|
||||
|
||||
Vint: poLd_I(poAdd_A(Vptr, Cint)) $0 $emLd_IRegisterIndirect
|
||||
Vptr: poLd_A(poAdd_A(Vptr, Cint)) $0 $emLd_ARegisterIndirect
|
||||
|
||||
//Store: poSt_B(Vptr, Vint) $1 $emSt_B
|
||||
//Store: poSt_H(Vptr, Vint) $1 $emSt_H
|
||||
Store: poSt_I(Vptr, Vint) $1 $emSt_I
|
||||
//Store: poSt_L(Vptr, Vlong) $1 $emSt_L
|
||||
//Store: poSt_F(Vptr, Vfloat) $1 $emSt_F
|
||||
//Store: poSt_D(Vptr, Vdouble) $1 $emSt_D
|
||||
//Store: poSt_A(Vptr, Vptr) $1 $emSt_A
|
||||
//Store: poStE_B(Cptr, Vint) $1 $emStE_B
|
||||
//Store: poStE_H(Cptr, Vint) $1 $emStE_H
|
||||
//Store: poStE_I(Cptr, Vint) $1 $emStE_I
|
||||
//Store: poStE_L(Cptr, Vlong) $1 $emStE_L
|
||||
//Store: poStE_F(Cptr, Vfloat) $1 $emStE_F
|
||||
//Store: poStE_D(Cptr, Vdouble) $1 $emStE_D
|
||||
//Store: poStE_A(Cptr, Vptr) $1 $emStE_A
|
||||
//Store: poStV_B(Vptr, Vint) $1 $emStV_B
|
||||
//Store: poStV_H(Vptr, Vint) $1 $emStV_H
|
||||
//Store: poStV_I(Vptr, Vint) $1 $emStV_I
|
||||
//Store: poStV_L(Vptr, Vlong) $1 $emStV_L
|
||||
//Store: poStV_F(Vptr, Vfloat) $1 $emStV_F
|
||||
//Store: poStV_D(Vptr, Vdouble) $1 $emStV_D
|
||||
//Store: poStV_A(Vptr, Vptr) $1 $emStV_A
|
||||
//Store: poStVE_B(Cptr, Vint) $1 $emStVE_B
|
||||
//Store: poStVE_H(Cptr, Vint) $1 $emStVE_H
|
||||
//Store: poStVE_I(Cptr, Vint) $1 $emStVE_I
|
||||
//Store: poStVE_L(Cptr, Vlong) $1 $emStVE_L
|
||||
//Store: poStVE_F(Cptr, Vfloat) $1 $emStVE_F
|
||||
//Store: poStVE_D(Cptr, Vdouble) $1 $emStVE_D
|
||||
//Store: poStVE_A(Cptr, Vptr) $1 $emStVE_A
|
||||
|
||||
Store: poSt_I(poAdd_A(Vptr, poConst_I), Vint) $0 $emSt_IRegisterIndirect
|
||||
|
||||
Store: poProj_M $1 $
|
||||
Vint: poProj_I $1 $
|
||||
Vptr: poProj_A $1 $
|
||||
|
||||
Tuple: poSysCall $1 $emSysCall
|
||||
Tuple: poSysCallE $1 $emSysCallE
|
||||
Tuple: poSysCallC $1 $emSysCallC
|
||||
Tuple: poSysCallEC $1 $emSysCallEC
|
||||
Tuple: poSysCallV $1 $emSysCallV
|
||||
Tuple: poSysCallEV $1 $emSysCallEV
|
||||
|
||||
Tuple: poCall(Vptr) $1 $emDynamicCall
|
||||
Tuple: poCall(poConst_A) $1 $emStaticCall
|
||||
%
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
#!gmake
|
||||
#
|
||||
# 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.
|
||||
|
||||
DEPTH = ../../../..
|
||||
|
||||
MODULE_NAME = sparc
|
||||
|
||||
include $(DEPTH)/config/config.mk
|
||||
|
||||
INCLUDES += \
|
||||
-I$(DEPTH)/Utilities/General \
|
||||
-I$(DEPTH)/Utilities/zlib \
|
||||
-I$(DEPTH)/Runtime/ClassReader \
|
||||
-I$(DEPTH)/Compiler/FrontEnd \
|
||||
-I$(DEPTH)/Runtime/NativeMethods \
|
||||
-I$(DEPTH)/Runtime/System \
|
||||
-I$(DEPTH)/Compiler/PrimitiveGraph \
|
||||
-I$(DEPTH)/Runtime/ClassInfo \
|
||||
-I$(DEPTH)/Runtime/FileReader \
|
||||
-I$(DEPTH)/Compiler/CodeGenerator \
|
||||
-I$(DEPTH)/Compiler/CodeGenerator/md \
|
||||
-I$(DEPTH)/Compiler/RegisterAllocator \
|
||||
$(NULL)
|
||||
|
||||
CXXSRCS = \
|
||||
SparcInstruction.cpp \
|
||||
SparcSunOSEmitter.cpp \
|
||||
sparc.nad.burg.cpp \
|
||||
$(NULL)
|
||||
|
||||
include $(DEPTH)/config/rules.mk
|
||||
|
||||
export:: sparc.nad.burg.cpp
|
||||
|
||||
libs:: $(MODULE)
|
||||
|
||||
#
|
||||
# Rules to generate sparc.nad.burg.[cpp][h]
|
||||
#
|
||||
|
||||
sparc.nad.burg.cpp: sparc.nad.burg $(BURG)
|
||||
$(BURG) -I -o $@ < $<
|
||||
|
||||
sparc.nad.burg: sparc.nad $(DEPTH)/Compiler/PrimitiveGraph/PrimitiveOperations $(DEPTH)/Tools/Nad/nad.pl
|
||||
$(PERL) $(DEPTH)/Tools/Nad/nad.pl $< $(DEPTH)/Compiler/PrimitiveGraph/PrimitiveOperations \
|
||||
$(DEPTH)/Compiler/PrimitiveGraph/PrimitiveOperations.h \
|
||||
$(DEPTH)/Compiler/PrimitiveGraph/PrimitiveOperations.cpp \
|
||||
$<.burg.h > $@
|
||||
|
||||
#
|
||||
# Extra cleaning
|
||||
#
|
||||
|
||||
clobber::
|
||||
rm -f sparc.nad.burg.cpp sparc.nad.burg.h sparc.nad.burg
|
||||
|
||||
realclean clobber_all::
|
||||
rm -f sparc.nad.burg.cpp sparc.nad.burg.h sparc.nad.burg
|
|
@ -0,0 +1,297 @@
|
|||
/* -*- 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.
|
||||
*/
|
||||
|
||||
#include "Fundamentals.h"
|
||||
#include "SparcInstruction.h"
|
||||
#include "ControlNodes.h"
|
||||
|
||||
SparcInstructionInfo siInfo[nSparcInstructionKind] =
|
||||
{
|
||||
{ siAnd, 2, 0x01, "and" },
|
||||
{ siAndCC, 2, 0x11, "andcc" },
|
||||
{ siAndN, 2, 0x05, "andn" },
|
||||
{ siAndNCC, 2, 0x15, "andncc" },
|
||||
{ siOr, 2, 0x02, "or" },
|
||||
{ siOrCC, 2, 0x12, "orcc" },
|
||||
{ siOrN, 2, 0x06, "orn" },
|
||||
{ siOrNCC, 2, 0x16, "orncc" },
|
||||
{ siXor, 2, 0x03, "xor" },
|
||||
{ siXorCC, 2, 0x13, "xorcc" },
|
||||
{ siXNor, 2, 0x07, "xnor" },
|
||||
{ siXNorCC, 2, 0x17, "xnorcc" },
|
||||
{ siSll, 2, 0x25, "sll" },
|
||||
{ siSrl, 2, 0x26, "srl" },
|
||||
{ siSra, 2, 0x27, "sra" },
|
||||
{ siAdd, 2, 0x00, "add" },
|
||||
{ siAddCC, 2, 0x10, "addcc" },
|
||||
{ siAddX, 2, 0x08, "addx" },
|
||||
{ siAddXCC, 2, 0x18, "addxcc" },
|
||||
{ siSub, 2, 0x04, "sub" },
|
||||
{ siSubCC, 2, 0x14, "subcc" },
|
||||
{ siSubX, 2, 0x0c, "subx" },
|
||||
{ siSubXCC, 2, 0x1c, "subxcc" },
|
||||
{ siUMul, 2, 0x0a, "umul" },
|
||||
{ siUMulCC, 2, 0x1a, "umulcc" },
|
||||
{ siSMul, 2, 0x0b, "smul" },
|
||||
{ siSMulCC, 2, 0x1b, "smulcc" },
|
||||
{ siUDiv, 2, 0x0e, "udiv" },
|
||||
{ siUDivCC, 2, 0x1e, "udivcc" },
|
||||
{ siSDiv, 2, 0x0f, "sdiv" },
|
||||
{ siSDivCC, 2, 0x1f, "sdvicc" },
|
||||
{ siTrap, 2, 0x3a, "trap" },
|
||||
{ siLdSb, 3, 0x09, "ldsb" },
|
||||
{ siLdSh, 3, 0x0a, "ldsh" },
|
||||
{ siLdUb, 3, 0x01, "ldub" },
|
||||
{ siLdUh, 3, 0x02, "lduh" },
|
||||
{ siLd, 3, 0x00, "ld" },
|
||||
{ siLdd, 3, 0x03, "ldd" },
|
||||
{ siStSb, 3, 0x05, "stsb" },
|
||||
{ siStSh, 3, 0x06, "stsh" },
|
||||
{ siSt, 3, 0x04, "st" },
|
||||
{ siStd, 3, 0x07, "std" },
|
||||
{ siBranch, 0, 0x02, "b" },
|
||||
};
|
||||
|
||||
SparcAccessInfo saInfo[nSparcAccessKind] =
|
||||
{
|
||||
{ saRRR, 2, 1 },
|
||||
{ saRIR, 1, 1 },
|
||||
{ saRZR, 1, 1 },
|
||||
{ saZRR, 1, 1 },
|
||||
{ saZIR, 0, 1 },
|
||||
{ saZZR, 0, 1 },
|
||||
{ saRRZ, 2, 0 },
|
||||
{ saRIZ, 1, 0 },
|
||||
{ saRZZ, 1, 0 },
|
||||
};
|
||||
|
||||
SparcConditionInfo scInfo[nSparcConditionKind] =
|
||||
{
|
||||
{ scA, 0x08, "a" },
|
||||
{ scN, 0x00, "n" },
|
||||
{ scNe, 0x09, "ne" },
|
||||
{ scE, 0x01, "e" },
|
||||
{ scG, 0x0a, "g" },
|
||||
{ scLe, 0x02, "le" },
|
||||
{ scGe, 0x0b, "ge" },
|
||||
{ scL, 0x03, "l" },
|
||||
{ scGu, 0x0c, "gu" },
|
||||
{ scLeu, 0x04, "leu" },
|
||||
{ scCc, 0x0d, "cc" },
|
||||
{ scGeu, 0x0d, "cc" },
|
||||
{ scCs, 0x05, "cs" },
|
||||
{ scLu, 0x05, "cs" },
|
||||
{ scPos, 0x0e, "pos" },
|
||||
{ scNeg, 0x06, "neg" },
|
||||
{ scVc, 0x0f, "vc" },
|
||||
{ scVs, 0x07, "vs" }
|
||||
};
|
||||
|
||||
char* registerNumberToString[] =
|
||||
{
|
||||
"g0", "g1", "g2", "g3", "g4", "g5", "g6", "g7",
|
||||
"o0", "o1", "o2", "o3", "o4", "o5", "sp", "o7",
|
||||
"l0", "l1", "l2", "l3", "l4", "l5", "l6", "l7",
|
||||
"i0", "i1", "i2", "i3", "i4", "i5", "fp", "i7"
|
||||
};
|
||||
|
||||
uint8 registerNumberToColor[] =
|
||||
{
|
||||
255, 255, 255, 255, 255, 255, 255, 255,
|
||||
8, 9, 10, 11, 12, 13, 255, 255,
|
||||
0, 1, 2, 3, 4, 5, 6, 7,
|
||||
14, 15, 16, 17, 18, 19, 255, 255
|
||||
};
|
||||
|
||||
SparcRegisterNumber colorToRegisterNumber[] =
|
||||
{
|
||||
l0, l1, l2, l3, l4, l5, l6, l7,
|
||||
o0, o1, o2, o3, o4, o5,
|
||||
i0, i1, i2, i3, i4, i5,
|
||||
};
|
||||
|
||||
void SparcFormatBCond::
|
||||
formatToMemory(void* inStart, uint32 inOffset)
|
||||
{
|
||||
uint32 insn = 0;
|
||||
insn |= (siInfo[kind].op & 0x3) << 30;
|
||||
if (a) insn |= (1 << 29);
|
||||
insn |= (scInfo[cond].cond & 0xf) << 25;
|
||||
insn |= (siInfo[kind].op3 & 0x7) << 22;
|
||||
insn |= ((target.getNativeOffset() - inOffset) >> 2) & 0x3fffff;
|
||||
*(uint32*) inStart = insn;
|
||||
if (a) ((uint32*) inStart)[1] = 1 << 24;
|
||||
}
|
||||
|
||||
void SparcFormatC::
|
||||
formatToMemory(void* inStart, uint32 /*inOffset*/)
|
||||
{
|
||||
uint32 insn = 0;
|
||||
|
||||
insn |= (siInfo[kind].op & 0x3) << 30;
|
||||
insn |= (getRD() & 0x1f) << 25;
|
||||
insn |= (siInfo[kind].op3 & 0x3f) << 19;
|
||||
insn |= (getRS1() & 0x1f) << 14;
|
||||
if (imm13)
|
||||
insn |= (imm13 | 0x2000);
|
||||
else
|
||||
insn |= getRS2();
|
||||
|
||||
*(uint32*) inStart = insn;
|
||||
}
|
||||
|
||||
|
||||
void SparcFormatC::
|
||||
printPretty(FILE* f)
|
||||
{
|
||||
switch (access)
|
||||
{
|
||||
case saRIR: case saZIR: case saRIZ:
|
||||
fprintf(f, "%s %%%s,%d,%%%s", siInfo[kind].string,
|
||||
registerNumberToString[getRS1()],
|
||||
imm13,
|
||||
registerNumberToString[getRD()]);
|
||||
break;
|
||||
default:
|
||||
fprintf(f, "%s %%%s,%%%s,%%%s", siInfo[kind].string,
|
||||
registerNumberToString[getRS1()],
|
||||
registerNumberToString[getRS2()],
|
||||
registerNumberToString[getRD()]);
|
||||
}
|
||||
}
|
||||
|
||||
void SparcLoadStore::
|
||||
printPretty(FILE* f)
|
||||
{
|
||||
switch (access)
|
||||
{
|
||||
case saRIR: case saRZR:
|
||||
if (isLoad)
|
||||
fprintf(f, "%s [%%%s+%d],%%%s", siInfo[kind].string,
|
||||
registerNumberToString[getRS1()],
|
||||
imm13,
|
||||
registerNumberToString[getRD()]);
|
||||
else
|
||||
fprintf(f, "%s %%%s,[%%%s+%d]", siInfo[kind].string,
|
||||
registerNumberToString[getRD()],
|
||||
registerNumberToString[getRS1()],
|
||||
imm13);
|
||||
break;
|
||||
default:
|
||||
if (isLoad)
|
||||
fprintf(f, "%s [%%%s+%%%s],%%%s", siInfo[kind].string,
|
||||
registerNumberToString[getRS1()],
|
||||
registerNumberToString[getRS2()],
|
||||
registerNumberToString[getRD()]);
|
||||
else
|
||||
fprintf(f, "%s %%%s,[%%%s+%%%s]", siInfo[kind].string,
|
||||
registerNumberToString[getRD()],
|
||||
registerNumberToString[getRS1()],
|
||||
registerNumberToString[getRS2()]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void SparcTrap::
|
||||
printPretty(FILE* f)
|
||||
{
|
||||
fprintf(f, "t%s throw_exception", scInfo[cond].string);
|
||||
}
|
||||
|
||||
void SparcFormatBCond::
|
||||
printPretty(FILE* f)
|
||||
{
|
||||
fprintf(f, "%s%s%s N%d%s", siInfo[kind].string, scInfo[cond].string, a ? ",a" : "",
|
||||
target.dfsNum, a ? "; nop" : "");
|
||||
}
|
||||
|
||||
uint8 SparcFormatC::
|
||||
getRS1()
|
||||
{
|
||||
switch (access)
|
||||
{
|
||||
case saZRR: case saZIR: case saZZR:
|
||||
return g0;
|
||||
|
||||
default:
|
||||
return useToRegisterNumber(getInstructionUseBegin()[0]);
|
||||
}
|
||||
}
|
||||
|
||||
uint8 SparcFormatC::
|
||||
getRS2()
|
||||
{
|
||||
#if DEBUG
|
||||
if (access == saRIR || access == saZIR || access == saRIZ) assert(0);
|
||||
#endif
|
||||
|
||||
switch (access)
|
||||
{
|
||||
case saZZR: case saRZZ: case saRZR:
|
||||
return g0;
|
||||
|
||||
default:
|
||||
return useToRegisterNumber(getInstructionUseBegin()[1]);
|
||||
}
|
||||
}
|
||||
|
||||
uint8 SparcFormatC::
|
||||
getRD()
|
||||
{
|
||||
switch (access)
|
||||
{
|
||||
case saRRZ: case saRIZ: case saRZZ:
|
||||
return g0;
|
||||
|
||||
default:
|
||||
return defineToRegisterNumber(getInstructionDefineBegin()[0]);
|
||||
}
|
||||
}
|
||||
|
||||
uint8 SparcLoadStore::
|
||||
getRS1()
|
||||
{
|
||||
switch (access)
|
||||
{
|
||||
case saZRR: case saZIR: case saZZR:
|
||||
return g0;
|
||||
|
||||
default:
|
||||
return useToRegisterNumber(getInstructionUseBegin()[isLoadC ? 0 : 1]);
|
||||
}
|
||||
}
|
||||
|
||||
uint8 SparcLoadStore::
|
||||
getRS2()
|
||||
{
|
||||
assert(0);
|
||||
}
|
||||
|
||||
uint8 SparcLoadStore::
|
||||
getRD()
|
||||
{
|
||||
switch (access)
|
||||
{
|
||||
case saRRZ: case saRIZ: case saRZZ:
|
||||
return g0;
|
||||
|
||||
default:
|
||||
return (isLoad) ? defineToRegisterNumber(getInstructionDefineBegin()[hasMemoryEdge ? 1 : 0]) :
|
||||
useToRegisterNumber(getInstructionUseBegin()[2]);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,318 @@
|
|||
/* -*- 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 _SPARC_INSTRUCTION_H_
|
||||
#define _SPARC_INSTRUCTION_H_
|
||||
|
||||
#include "Fundamentals.h"
|
||||
#include "Instruction.h"
|
||||
|
||||
//
|
||||
// Instruction format A (op=1): CALL
|
||||
//
|
||||
// |31 |29 0|
|
||||
// |-----------------------------------------------------------------------------------------------|
|
||||
// | op | disp30 |
|
||||
// |-----------------------------------------------------------------------------------------------|
|
||||
//
|
||||
//
|
||||
// Instruction format B (op=0): SETHI & Branches
|
||||
//
|
||||
// |31 |29|28 |24 |21 0|
|
||||
// |-----------------------------------------------------------------------------------------------|
|
||||
// | op | rd | op2 | imm22 |
|
||||
// |-----------------------------------------------------------------------------------------------|
|
||||
// | op |a | cond | op2 | disp22 |
|
||||
// |-----------------------------------------------------------------------------------------------|
|
||||
//
|
||||
//
|
||||
// Instruction format C (op=2|op=3): arithmetic, logical, shift, memory instructions & remaining.
|
||||
//
|
||||
// |31 |29 |24 |18 |13|12 |4 0|
|
||||
// |-----------------------------------------------------------------------------------------------|
|
||||
// | op | rd | op3 | rs1 |0 | asi | rs2 |
|
||||
// |-----------------------------------------------------------------------------------------------|
|
||||
// | op | rd | op3 | rs1 |1 | simm13 |
|
||||
// |-----------------------------------------------------------------------------------------------|
|
||||
// | op | rd | op3 | rs1 | opf | rs2 |
|
||||
// |-----------------------------------------------------------------------------------------------|
|
||||
//
|
||||
|
||||
enum SparcInstructionKind
|
||||
{
|
||||
siAnd,
|
||||
siAndCC,
|
||||
siAndN,
|
||||
siAndNCC,
|
||||
siOr,
|
||||
siOrCC,
|
||||
siOrN,
|
||||
siOrNCC,
|
||||
siXor,
|
||||
siXorCC,
|
||||
siXNor,
|
||||
siXNorCC,
|
||||
siSll,
|
||||
siSrl,
|
||||
siSra,
|
||||
siAdd,
|
||||
siAddCC,
|
||||
siAddX,
|
||||
siAddXCC,
|
||||
siSub,
|
||||
siSubCC,
|
||||
siSubX,
|
||||
siSubXCC,
|
||||
siUMul,
|
||||
siUMulCC,
|
||||
siSMul,
|
||||
siSMulCC,
|
||||
siUDiv,
|
||||
siUDivCC,
|
||||
siSDiv,
|
||||
siSDivCC,
|
||||
siTrap,
|
||||
siLdSb,
|
||||
siLdSh,
|
||||
siLdUb,
|
||||
siLdUh,
|
||||
siLd,
|
||||
siLdd,
|
||||
siStSb,
|
||||
siStSh,
|
||||
siSt,
|
||||
siStd,
|
||||
siBranch,
|
||||
nSparcInstructionKind
|
||||
};
|
||||
|
||||
enum SparcAccessKind
|
||||
{
|
||||
saRRR, // insn r[s1],r[s2],r[d]
|
||||
saRIR, // insn r[s1],imm13,r[d]
|
||||
saRZR, // insn r[s1],r[g0],r[d]
|
||||
saZRR, // insn r[g0],r[s2],r[d]
|
||||
saZIR, // insn r[g0],imm13,r[d]
|
||||
saZZR, // insn r[g0],r[g0],r[d]
|
||||
saRRZ, // insn r[s1],r[s2],r[g0]
|
||||
saRIZ, // insn r[s1],imm13,r[g0]
|
||||
saRZZ, // insn r[s1],r[g0],r[g0]
|
||||
nSparcAccessKind
|
||||
};
|
||||
|
||||
#define saRR saRRZ
|
||||
#define saRI saRIZ
|
||||
#define saRZ saRZZ
|
||||
|
||||
enum SparcConditionKind
|
||||
{
|
||||
scA, // always
|
||||
scN, // never
|
||||
scNe, // on Not Equal
|
||||
scE, // on Equal
|
||||
scG, // on Greater
|
||||
scLe, // on Less or Equal
|
||||
scGe, // on Greater or Equal
|
||||
scL, // on Less
|
||||
scGu, // on Greater Unsigned
|
||||
scLeu, // on Less or Equal Unsigned
|
||||
scCc, // on Carry Clear (Greater than or Equal, Unsigned)
|
||||
scGeu,
|
||||
scCs, // on Carry Set (Less than, Unsigned)
|
||||
scLu,
|
||||
scPos, // on Positive
|
||||
scNeg, // on Negative
|
||||
scVc, // on Overflow Clear
|
||||
scVs, // on Overflow Set
|
||||
nSparcConditionKind
|
||||
};
|
||||
|
||||
enum SparcRegisterNumber
|
||||
{
|
||||
g0, g1, g2, g3, g4, g5, g6, g7,
|
||||
o0, o1, o2, o3, o4, o5, sp, o7,
|
||||
l0, l1, l2, l3, l4, l5, l6, l7,
|
||||
i0, i1, i2, i3, i4, i5, fp, i7
|
||||
};
|
||||
|
||||
struct SparcConditionInfo
|
||||
{
|
||||
SparcConditionKind kind;
|
||||
uint8 cond;
|
||||
char *string;
|
||||
};
|
||||
|
||||
struct SparcInstructionInfo
|
||||
{
|
||||
SparcInstructionKind kind;
|
||||
uint8 op;
|
||||
uint8 op3;
|
||||
char* string;
|
||||
};
|
||||
|
||||
struct SparcAccessInfo
|
||||
{
|
||||
SparcAccessKind kind;
|
||||
uint8 nConsumer;
|
||||
uint8 nProducer;
|
||||
};
|
||||
|
||||
extern SparcInstructionInfo siInfo[];
|
||||
extern SparcAccessInfo saInfo[];
|
||||
extern SparcConditionInfo scInfo[];
|
||||
extern char* registerNumberToString[];
|
||||
extern SparcRegisterNumber colorToRegisterNumber[];
|
||||
extern uint8 registerNumberToColor[];
|
||||
|
||||
class SparcFormatBCond : public InsnUseXDefineYFromPool
|
||||
{
|
||||
protected:
|
||||
SparcInstructionKind kind;
|
||||
SparcConditionKind cond;
|
||||
bool a;
|
||||
ControlNode& target;
|
||||
|
||||
public:
|
||||
SparcFormatBCond(DataNode* inPrimitive, Pool& inPool, ControlNode& t, SparcInstructionKind iKind, SparcConditionKind cKind, bool annul) :
|
||||
InsnUseXDefineYFromPool(inPrimitive, inPool, 1, 0),
|
||||
kind(iKind), cond(cKind), a(annul), target(t) {}
|
||||
|
||||
virtual void printPretty(FILE* f);
|
||||
virtual size_t getFormattedSize() {return (a ? 8 : 4);}
|
||||
virtual void formatToMemory(void* inStart, uint32 inOffset);
|
||||
};
|
||||
|
||||
|
||||
class SparcBranch : public SparcFormatBCond
|
||||
{
|
||||
public:
|
||||
SparcBranch(DataNode* inPrimitive, Pool& inPool, ControlNode& t, SparcConditionKind cKind, bool annul) :
|
||||
SparcFormatBCond(inPrimitive, inPool, t, siBranch, cKind, annul) {}
|
||||
};
|
||||
|
||||
class SparcFormatC : public InsnUseXDefineYFromPool
|
||||
{
|
||||
protected:
|
||||
SparcInstructionKind kind;
|
||||
SparcAccessKind access;
|
||||
int16 imm13;
|
||||
|
||||
public:
|
||||
SparcFormatC(DataNode* inPrimitive, Pool& inPool, uint8 nConsumer = 0, uint8 nProducer = 0) :
|
||||
InsnUseXDefineYFromPool(inPrimitive, inPool, nConsumer, nProducer) {}
|
||||
|
||||
virtual void printPretty(FILE* f);
|
||||
virtual size_t getFormattedSize() {return 4;}
|
||||
virtual void formatToMemory(void* inStart, uint32 /*inOffset*/);
|
||||
|
||||
|
||||
inline SparcRegisterNumber useToRegisterNumber(InstructionUse& use);
|
||||
inline SparcRegisterNumber defineToRegisterNumber(InstructionDefine& use);
|
||||
|
||||
virtual uint8 getRS1();
|
||||
virtual uint8 getRS2();
|
||||
virtual uint8 getRD();
|
||||
};
|
||||
|
||||
class SparcInsn : public SparcFormatC
|
||||
{
|
||||
public:
|
||||
SparcInsn(DataNode* inPrimitive, Pool& inPool, SparcInstructionKind iKind, SparcAccessKind aKind, int16 constant = 0) :
|
||||
SparcFormatC(inPrimitive, inPool, saInfo[aKind].nConsumer, saInfo[aKind].nProducer)
|
||||
{kind = iKind; access = aKind; imm13 = constant;}
|
||||
|
||||
virtual InstructionFlags getFlags() const {if (kind == siOr && access == saRZR) return (ifCopy); else return(ifNone);}
|
||||
};
|
||||
|
||||
class SparcTrap : public SparcFormatC
|
||||
{
|
||||
protected:
|
||||
SparcConditionKind cond;
|
||||
public:
|
||||
SparcTrap(DataNode* inPrimitive, Pool& inPool, SparcConditionKind cKind, SparcAccessKind aKind, int16 constant = 0) :
|
||||
SparcFormatC(inPrimitive, inPool, 1, 0), cond(cKind)
|
||||
{kind = siTrap; access = aKind; imm13 = constant;}
|
||||
virtual uint8 getRS1() {return g0;}
|
||||
virtual uint8 getRS2() {return g0;}
|
||||
virtual uint8 getRD() {return scInfo[cond].cond;}
|
||||
virtual void printPretty(FILE* f);
|
||||
};
|
||||
|
||||
class SparcCmp : public SparcFormatC
|
||||
{
|
||||
public:
|
||||
SparcCmp(DataNode* inPrimitive, Pool& inPool, SparcAccessKind aKind, int16 constant = 0) :
|
||||
SparcFormatC(inPrimitive, inPool, saInfo[aKind].nConsumer, 1)
|
||||
{kind = siSubCC; access = aKind; imm13 = constant;}
|
||||
};
|
||||
|
||||
class SparcLoadStore : public SparcFormatC
|
||||
{
|
||||
protected:
|
||||
bool hasMemoryEdge;
|
||||
bool isLoad;
|
||||
bool isLoadC;
|
||||
|
||||
public:
|
||||
SparcLoadStore(DataNode* inPrimitive, Pool& inPool, SparcInstructionKind iKind, SparcAccessKind aKind, bool has_mem, bool is_load, bool is_loadC, int16 constant = 0) :
|
||||
SparcFormatC(inPrimitive, inPool, is_load ? (is_loadC ? 1 : 2) : 3, (is_load && has_mem) ? 2 : 1), hasMemoryEdge(has_mem), isLoad(is_load), isLoadC(is_loadC)
|
||||
{
|
||||
kind = iKind; access = aKind; imm13 = constant;
|
||||
}
|
||||
virtual void printPretty(FILE* f);
|
||||
virtual uint8 getRS1();
|
||||
virtual uint8 getRS2();
|
||||
virtual uint8 getRD();
|
||||
};
|
||||
|
||||
class SparcLoadC : public SparcLoadStore
|
||||
{
|
||||
public:
|
||||
SparcLoadC(DataNode* inPrimitive, Pool& inPool, SparcInstructionKind iKind, SparcAccessKind aKind, int16 constant = 0) :
|
||||
SparcLoadStore(inPrimitive, inPool, iKind, aKind, false, true, true, constant) {}
|
||||
};
|
||||
|
||||
class SparcLoad : public SparcLoadStore
|
||||
{
|
||||
public:
|
||||
SparcLoad(DataNode* inPrimitive, Pool& inPool, SparcInstructionKind iKind, SparcAccessKind aKind, bool memEdge, int16 constant = 0) :
|
||||
SparcLoadStore(inPrimitive, inPool, iKind, aKind, memEdge, true, false, constant) {}
|
||||
};
|
||||
|
||||
class SparcStore : public SparcLoadStore
|
||||
{
|
||||
public:
|
||||
SparcStore(DataNode* inPrimitive, Pool& inPool, SparcInstructionKind iKind, SparcAccessKind aKind, int16 constant = 0) :
|
||||
SparcLoadStore(inPrimitive, inPool, iKind, aKind, true, false, false, constant) {}
|
||||
};
|
||||
|
||||
inline SparcRegisterNumber SparcFormatC::
|
||||
useToRegisterNumber(InstructionUse& use)
|
||||
{
|
||||
assert(use.isVirtualRegister());
|
||||
return (colorToRegisterNumber[use.getVirtualRegister().getColor()]);
|
||||
}
|
||||
|
||||
inline SparcRegisterNumber SparcFormatC::
|
||||
defineToRegisterNumber(InstructionDefine& def)
|
||||
{
|
||||
assert(def.isVirtualRegister());
|
||||
return (colorToRegisterNumber[def.getVirtualRegister().getColor()]);
|
||||
}
|
||||
|
||||
#endif /* _SPARC_INSTRUCTION_H_ */
|
|
@ -0,0 +1,591 @@
|
|||
/* -*- 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.
|
||||
*/
|
||||
|
||||
#include "SparcSunOSEmitter.h"
|
||||
#include "SparcInstruction.h"
|
||||
#include "ControlNodes.h"
|
||||
#include "sparc-sunos.nad.burg.h"
|
||||
|
||||
#define BINARY_PRIMITIVE_I(inRuleKind, inOp) \
|
||||
case inRuleKind: \
|
||||
emit_Insn_I(inPrimitive, inOp); \
|
||||
break;
|
||||
|
||||
#define BINARY_PRIMITIVEI_I(inRuleKind, inOp) \
|
||||
case inRuleKind: \
|
||||
emit_InsnI_I(inPrimitive, inOp); \
|
||||
break;
|
||||
|
||||
#define UNDEFINED_RULE(inRuleKind) \
|
||||
case inRuleKind: \
|
||||
assert(false); \
|
||||
break;
|
||||
|
||||
#define BINARY_PRIMITIVE_SET(inRuleRoot, inOp) \
|
||||
BINARY_PRIMITIVE_I(inRuleRoot##_I, inOp); \
|
||||
UNDEFINED_RULE(inRuleRoot##_L); \
|
||||
BINARY_PRIMITIVEI_I(inRuleRoot##I_I, inOp); \
|
||||
UNDEFINED_RULE(inRuleRoot##I_L);
|
||||
|
||||
void SparcSunOSEmitter::
|
||||
emitPrimitive(Primitive& inPrimitive, NamedRule inRule)
|
||||
{
|
||||
switch (inRule)
|
||||
{
|
||||
case emConst_I:
|
||||
emit_Const_I(inPrimitive);
|
||||
break;
|
||||
case emArg_I:
|
||||
case emArg_L:
|
||||
case emArg_F:
|
||||
case emArg_D:
|
||||
case emArg_A:
|
||||
assert(false);
|
||||
break;
|
||||
case emResult_M:
|
||||
case emArg_M:
|
||||
break;
|
||||
case emResult_I:
|
||||
{
|
||||
VirtualRegister* returnVr;
|
||||
if (inPrimitive.nthInputIsConstant(0))
|
||||
{
|
||||
returnVr = &genLoadConstant(inPrimitive, inPrimitive.nthInputConstant(0).i);
|
||||
}
|
||||
else
|
||||
{
|
||||
SparcInsn& copyInsn = *new(mPool) SparcInsn(&inPrimitive, mPool, siOr, saRZR);
|
||||
|
||||
returnVr = &defineTemporary(copyInsn, 0);
|
||||
useProducer(inPrimitive.nthInputVariable(0), copyInsn, 0);
|
||||
}
|
||||
|
||||
returnVr->precolorRegister(registerNumberToColor[o0]);
|
||||
|
||||
InsnExternalUse& externalUse = *new(mPool) InsnExternalUse(&inPrimitive);
|
||||
useTemporaryVR(externalUse, *returnVr, 0);
|
||||
inPrimitive.setInstructionRoot(&externalUse);
|
||||
}
|
||||
break;
|
||||
case emIfLt:
|
||||
emit_B(inPrimitive, scL);
|
||||
break;
|
||||
case emIfULt:
|
||||
emit_B(inPrimitive, scLu);
|
||||
break;
|
||||
case emIfEq:
|
||||
case emIfUEq:
|
||||
emit_B(inPrimitive, scE);
|
||||
break;
|
||||
case emIfLe:
|
||||
emit_B(inPrimitive, scLe);
|
||||
break;
|
||||
case emIfULe:
|
||||
emit_B(inPrimitive, scLeu);
|
||||
break;
|
||||
case emIfGt:
|
||||
emit_B(inPrimitive, scG);
|
||||
break;
|
||||
case emIfUGt:
|
||||
emit_B(inPrimitive, scGu);
|
||||
break;
|
||||
case emIfLgt:
|
||||
case emIfNe:
|
||||
emit_B(inPrimitive, scNe);
|
||||
break;
|
||||
case emIfGe:
|
||||
emit_B(inPrimitive, scGe);
|
||||
break;
|
||||
case emIfUGe:
|
||||
emit_B(inPrimitive, scGeu);
|
||||
break;
|
||||
case emIfOrd:
|
||||
case emIfUnord:
|
||||
assert(false); // Shouldn't happen with int edges...
|
||||
break;
|
||||
BINARY_PRIMITIVE_SET(emAnd, siAnd);
|
||||
BINARY_PRIMITIVE_SET(emOr, siOr );
|
||||
BINARY_PRIMITIVE_SET(emXor, siXor);
|
||||
BINARY_PRIMITIVE_SET(emAdd, siAdd);
|
||||
case emAdd_A:
|
||||
emit_Insn_I(inPrimitive, siAdd);
|
||||
break;
|
||||
case emAddI_A:
|
||||
emit_InsnI_I(inPrimitive, siAdd);
|
||||
break;
|
||||
BINARY_PRIMITIVE_SET(emMul, siSMul);
|
||||
case emDivI_I:
|
||||
emit_DivI_I(inPrimitive);
|
||||
break;
|
||||
case emModE_I:
|
||||
emit_ModE_I(inPrimitive);
|
||||
break;
|
||||
case emShlI_I:
|
||||
emit_InsnI_I(inPrimitive, siSll);
|
||||
break;
|
||||
case emChkNull:
|
||||
emit_ChkNull(inPrimitive);
|
||||
break;
|
||||
case emLimitR:
|
||||
emit_LimitR(inPrimitive);
|
||||
break;
|
||||
case emLimit:
|
||||
emit_Limit(inPrimitive);
|
||||
break;
|
||||
case emLdC_IRegisterIndirect:
|
||||
emit_LdC_IRegisterIndirect(inPrimitive);
|
||||
break;
|
||||
case emLd_IRegisterIndirect:
|
||||
emit_Ld_IRegisterIndirect(inPrimitive);
|
||||
break;
|
||||
case emLdC_I:
|
||||
fprintf(stdout, "*** should emit emLdC_I\n");
|
||||
//genLoadIZero(inPrimitive, pfConstant);
|
||||
break;
|
||||
case emLd_I:
|
||||
fprintf(stdout, "*** should emit emLd_I\n");
|
||||
//genLoadIZero(inPrimitive, 0);
|
||||
break;
|
||||
case emSt_I:
|
||||
fprintf(stdout, "*** should emit emSt_I\n");
|
||||
//{
|
||||
//StoreNoUpdateD& newInsn = *new(mPool) StoreNoUpdateD(&inPrimitive, mPool, dfStw, 0, 0);
|
||||
//newInsn.standardUseDefine(*this);
|
||||
//}
|
||||
break;
|
||||
case emStI_I:
|
||||
fprintf(stdout, "*** should emit emStI_I\n");
|
||||
//emit_StI_I(inPrimitive);
|
||||
break;
|
||||
case emStI_IRegisterIndirect:
|
||||
emit_StI_IRegisterIndirect(inPrimitive);
|
||||
break;
|
||||
case emSt_IRegisterIndirect:
|
||||
emit_St_IRegisterIndirect(inPrimitive);
|
||||
break;
|
||||
case emCallI_I:
|
||||
case emCallI_L:
|
||||
case emCallI_F:
|
||||
case emCallI_D:
|
||||
case emCallI_P:
|
||||
fprintf(stdout, "*** should emit emCallI_P\n");
|
||||
//emit_Call(inPrimitive);
|
||||
break;
|
||||
case emCmp_I:
|
||||
emit_Cmp_I(inPrimitive);
|
||||
break;
|
||||
case emCmpI_I:
|
||||
emit_CmpI_I(inPrimitive);
|
||||
break;
|
||||
default:
|
||||
//assert(false)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void SparcSunOSEmitter::
|
||||
emit_Const_I(Primitive& inPrimitive)
|
||||
{
|
||||
int32 constI = (*static_cast<const PrimConst *>(&inPrimitive)).value.i;
|
||||
if ((constI >= -4096) && (constI <= 4095))
|
||||
{
|
||||
SparcInsn& newInsn = *new(mPool) SparcInsn(&inPrimitive, mPool, siOr, constI ? saZIR : saZZR, constI & 0x1fff);
|
||||
defineProducer(inPrimitive, newInsn, 0);
|
||||
}
|
||||
else if ((constI & 0x1fff) == 0)
|
||||
{
|
||||
// FIXME
|
||||
assert(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
// FIXME
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
void SparcSunOSEmitter::
|
||||
emit_InsnI_I(Primitive& inPrimitive, SparcInstructionKind kind)
|
||||
{
|
||||
int32 constI = inPrimitive.nthInputConstant(1).i;
|
||||
if ((constI >= -4096) && (constI <= 4095))
|
||||
{
|
||||
SparcInsn& newInsn = *new(mPool) SparcInsn(&inPrimitive, mPool, kind, constI ? saRIR : saRZR, constI & 0x1fff);
|
||||
newInsn.standardUseDefine(*this);
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
void SparcSunOSEmitter::
|
||||
emit_Insn_I(Primitive& inPrimitive, SparcInstructionKind kind)
|
||||
{
|
||||
SparcInsn& newInsn = *new(mPool) SparcInsn(&inPrimitive, mPool, kind, saRRR);
|
||||
newInsn.standardUseDefine(*this);
|
||||
}
|
||||
|
||||
void SparcSunOSEmitter::
|
||||
emit_Cmp_I(Primitive& inPrimitive)
|
||||
{
|
||||
SparcCmp& newInsn = *new(mPool) SparcCmp(&inPrimitive, mPool, saRR);
|
||||
newInsn.standardUseDefine(*this);
|
||||
}
|
||||
|
||||
void SparcSunOSEmitter::
|
||||
emit_CmpI_I(Primitive& inPrimitive)
|
||||
{
|
||||
int32 constI = inPrimitive.nthInputConstant(1).i;
|
||||
if ((constI >= -4096) && (constI <= 4095))
|
||||
{
|
||||
SparcCmp& newInsn = *new(mPool) SparcCmp(&inPrimitive, mPool, constI ? saRI : saRZ, constI & 0x1fff);
|
||||
newInsn.standardUseDefine(*this);
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
void SparcSunOSEmitter::
|
||||
emit_B(Primitive& inPrimitive, SparcConditionKind cond)
|
||||
{
|
||||
ControlIf& controlIf = ControlIf::cast(*inPrimitive.getContainer());
|
||||
SparcBranch& newInsn = *new(mPool) SparcBranch(&inPrimitive, mPool, *(controlIf.getTrueIfEdge().target), cond, true);
|
||||
newInsn.standardUseDefine(*this);
|
||||
}
|
||||
|
||||
Instruction& SparcSunOSEmitter::
|
||||
emitAbsoluteBranch(DataNode& inDataNode, ControlNode& inTarget)
|
||||
{
|
||||
SparcBranch& newInsn = *new(mPool) SparcBranch(&inDataNode, mPool, inTarget, scA, true);
|
||||
return (newInsn);
|
||||
}
|
||||
|
||||
void SparcSunOSEmitter::
|
||||
emitArguments(ControlBegin& inBeginNode)
|
||||
{
|
||||
uint32 curParamWords; // number of words of argument space used
|
||||
uint32 curFloatingPointArg; // number of floating point arguments
|
||||
|
||||
InsnExternalDefine& defineInsn = *new(mPool) InsnExternalDefine(&inBeginNode.arguments[0], mPool, inBeginNode.nArguments * 2);
|
||||
|
||||
uint32 curArgIdx; // current index into the arguments in the ControlBegin
|
||||
|
||||
curParamWords = 0;
|
||||
curFloatingPointArg = 0;
|
||||
for (curArgIdx = 0; curArgIdx < inBeginNode.nArguments && curParamWords < 6; curArgIdx++)
|
||||
{
|
||||
PrimArg& curArg = inBeginNode.arguments[curArgIdx];
|
||||
|
||||
switch (curArg.getOperation())
|
||||
{
|
||||
case poArg_I:
|
||||
case poArg_A:
|
||||
{
|
||||
SparcInsn& copyInsn = *new(mPool) SparcInsn(&curArg, mPool, siOr, saRZR);
|
||||
|
||||
// hook up this vr to the external define FIX-ME
|
||||
VirtualRegister& inputVr = defineTemporary(defineInsn, curArgIdx);
|
||||
inputVr.precolorRegister(registerNumberToColor[i0+curParamWords]);
|
||||
|
||||
// emit the copy
|
||||
useTemporaryVR(copyInsn, inputVr, 0);
|
||||
defineProducer(curArg, copyInsn, 0);
|
||||
|
||||
curParamWords++;
|
||||
break;
|
||||
}
|
||||
case poArg_L:
|
||||
assert(false);
|
||||
|
||||
case poArg_F:
|
||||
assert(false);
|
||||
|
||||
case poArg_D:
|
||||
assert(false);
|
||||
|
||||
break;
|
||||
|
||||
case poArg_M:
|
||||
break;
|
||||
|
||||
default:
|
||||
assert(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: emit the load for the arguments on the stack
|
||||
|
||||
// continue counting from before
|
||||
// make all the rest of the defines as type none
|
||||
for (;curArgIdx < inBeginNode.nArguments * 2; curArgIdx++)
|
||||
{
|
||||
defineInsn.getInstructionDefineBegin()[curArgIdx].kind = udNone;
|
||||
}
|
||||
// check for half stack half reg case
|
||||
}
|
||||
|
||||
Instruction& SparcSunOSEmitter::
|
||||
emitCopy(DataNode& inDataNode, VirtualRegister& fromVr, VirtualRegister& toVr)
|
||||
{
|
||||
SparcInsn& newInsn = *new(mPool) SparcInsn(&inDataNode, mPool, siOr, saRZR);
|
||||
newInsn.addUse(0, fromVr);
|
||||
newInsn.addDefine(0, toVr);
|
||||
#if DEBUG
|
||||
toVr.setDefineInstruction(newInsn);
|
||||
#endif
|
||||
return (newInsn);
|
||||
}
|
||||
|
||||
void SparcSunOSEmitter::
|
||||
emit_DivI_I(Primitive& inPrimitive)
|
||||
{
|
||||
Value& immediate = inPrimitive.nthInputConstant(1);
|
||||
if(isPowerOfTwo(immediate.i))
|
||||
{
|
||||
uint8 shiftBy = 31 - leadingZeros(immediate.i);
|
||||
|
||||
SparcInsn& shiftRightL = *new(mPool) SparcInsn(&inPrimitive, mPool, siSrl, saRIR, 31);
|
||||
useProducer(inPrimitive.nthInputVariable(0), shiftRightL, 0);
|
||||
VirtualRegister& tempVr = defineTemporary(shiftRightL, 0);
|
||||
inPrimitive.setInstructionRoot(&shiftRightL);
|
||||
|
||||
SparcInsn& addInsn = *new(mPool) SparcInsn(&inPrimitive, mPool, siAdd, saRRR);
|
||||
useProducer(inPrimitive.nthInputVariable(0), addInsn, 0);
|
||||
useTemporaryVR(addInsn, tempVr, 1);
|
||||
redefineTemporary(addInsn, tempVr, 0);
|
||||
|
||||
SparcInsn& shiftRight = *new(mPool) SparcInsn(&inPrimitive, mPool, siSra, saRIR, shiftBy);
|
||||
useTemporaryVR(shiftRight, tempVr, 0);
|
||||
defineProducer(inPrimitive, shiftRight, 0);
|
||||
}
|
||||
else
|
||||
// FIXME: check 0
|
||||
emit_InsnI_I(inPrimitive, siSDiv);
|
||||
}
|
||||
|
||||
void SparcSunOSEmitter::
|
||||
emit_ModE_I(Primitive& inPrimitive)
|
||||
{
|
||||
// Check the divisor != 0
|
||||
SparcCmp& cmpInsn = *new(mPool) SparcCmp(&inPrimitive, mPool, saRZ);
|
||||
useProducer(inPrimitive.nthInputVariable(1), cmpInsn, 0);
|
||||
cmpInsn.addDefine(0, udCond);
|
||||
SparcTrap& trapInsn = *new(mPool) SparcTrap(&inPrimitive, mPool, scE, saRRR /* FIXME */);
|
||||
trapInsn.addUse(0, udCond);
|
||||
trapInsn.getInstructionUseBegin()->setDefiningInstruction(cmpInsn);
|
||||
inPrimitive.setInstructionRoot(&trapInsn);
|
||||
|
||||
SparcInsn& divInsn = *new(mPool) SparcInsn(&inPrimitive, mPool, siSDiv, saRRR);
|
||||
useProducer(inPrimitive.nthInputVariable(0), divInsn, 0);
|
||||
useProducer(inPrimitive.nthInputVariable(1), divInsn, 1);
|
||||
VirtualRegister& tempVr = defineTemporary(divInsn, 0);
|
||||
|
||||
SparcInsn& mullInsn = *new(mPool) SparcInsn(&inPrimitive, mPool, siSMul, saRRR);
|
||||
useProducer(inPrimitive.nthInputVariable(1), mullInsn, 1);
|
||||
useTemporaryVR(mullInsn, tempVr, 0);
|
||||
redefineTemporary(mullInsn, tempVr, 0);
|
||||
|
||||
SparcInsn& subInsn = *new(mPool) SparcInsn(&inPrimitive, mPool, siSub, saRRR);
|
||||
useTemporaryVR(subInsn, tempVr, 0);
|
||||
useProducer(inPrimitive.nthInputVariable(0), subInsn, 1);
|
||||
defineProducer(inPrimitive, subInsn, 0);
|
||||
}
|
||||
|
||||
void SparcSunOSEmitter::
|
||||
emit_ChkNull(Primitive& inPrimitive)
|
||||
{
|
||||
SparcCmp& cmpInsn = *new(mPool) SparcCmp(&inPrimitive, mPool, saRZ);
|
||||
useProducer(inPrimitive.nthInputVariable(0), cmpInsn, 0);
|
||||
cmpInsn.addDefine(0, udCond);
|
||||
|
||||
SparcTrap& trapInsn = *new(mPool) SparcTrap(&inPrimitive, mPool, scE, saRRR /* FIXME */);
|
||||
trapInsn.addUse(0, udCond);
|
||||
trapInsn.getInstructionUseBegin()->setDefiningInstruction(cmpInsn);
|
||||
inPrimitive.setInstructionRoot(&trapInsn);
|
||||
}
|
||||
|
||||
void SparcSunOSEmitter::
|
||||
emit_LimitR(Primitive& inPrimitive)
|
||||
{
|
||||
int32 constI = inPrimitive.nthInputConstant(0).i;
|
||||
if ((constI >= -4096) && (constI <= 4095))
|
||||
{
|
||||
SparcCmp& cmpInsn = *new(mPool) SparcCmp(&inPrimitive, mPool, constI ? saRI : saRZ, constI & 0x1fff);
|
||||
useProducer(inPrimitive.nthInputVariable(1), cmpInsn, 0);
|
||||
cmpInsn.addDefine(0, udCond);
|
||||
|
||||
SparcTrap& trapInsn = *new(mPool) SparcTrap(&inPrimitive, mPool, scLe, saRRR /* FIXME */);
|
||||
trapInsn.addUse(0, udCond);
|
||||
trapInsn.getInstructionUseBegin()->setDefiningInstruction(cmpInsn);
|
||||
inPrimitive.setInstructionRoot(&trapInsn);
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
void SparcSunOSEmitter::
|
||||
emit_Limit(Primitive& inPrimitive)
|
||||
{
|
||||
SparcCmp& cmpInsn = *new(mPool) SparcCmp(&inPrimitive, mPool, saRR);
|
||||
useProducer(inPrimitive.nthInputVariable(0), cmpInsn, 0);
|
||||
useProducer(inPrimitive.nthInputVariable(1), cmpInsn, 1);
|
||||
cmpInsn.addDefine(0, udCond);
|
||||
|
||||
SparcTrap& trapInsn = *new(mPool) SparcTrap(&inPrimitive, mPool, scGe, saRRR /* FIXME */);
|
||||
trapInsn.addUse(0, udCond);
|
||||
trapInsn.getInstructionUseBegin()->setDefiningInstruction(cmpInsn);
|
||||
inPrimitive.setInstructionRoot(&trapInsn);
|
||||
}
|
||||
|
||||
|
||||
void SparcSunOSEmitter::
|
||||
emit_LdC_IRegisterIndirect(Primitive& inPrimitive)
|
||||
{
|
||||
DataNode& immSource = inPrimitive.nthInput(0).getProducer().getNode();
|
||||
|
||||
int32 constI = immSource.nthInputConstant(1).i;
|
||||
if ((constI >= -4096) && (constI <= 4095))
|
||||
{
|
||||
SparcLoadC& newInsn = *new(mPool) SparcLoadC(&inPrimitive, mPool, siLd, saRIR, constI);
|
||||
useProducer(immSource.nthInputVariable(0), newInsn, 0);
|
||||
defineProducer(inPrimitive, newInsn, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
void SparcSunOSEmitter::
|
||||
emit_Ld_IRegisterIndirect(Primitive& inPrimitive)
|
||||
{
|
||||
DataNode& immSource = inPrimitive.nthInput(1).getProducer().getNode();
|
||||
|
||||
int32 constI = immSource.nthInputConstant(1).i;
|
||||
|
||||
if ((constI >= -4096) && (constI <= 4095))
|
||||
{
|
||||
SparcLoad& newInsn = *new(mPool) SparcLoad(&inPrimitive, mPool, siLd, saRIR, false, constI);
|
||||
useProducer(inPrimitive.nthInputVariable(0), newInsn, 0); // M to poLd_I
|
||||
useProducer(immSource.nthInputVariable(0), newInsn, 1); // Vint to poAdd_A
|
||||
defineProducer(inPrimitive, newInsn, 0); // Vint from poLd_I
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
void SparcSunOSEmitter::
|
||||
emit_StI_IRegisterIndirect(Primitive& inPrimitive)
|
||||
{
|
||||
SparcStore* storeInsn;
|
||||
|
||||
// load the constant to be stored into constVr
|
||||
VirtualRegister& constVr = genLoadConstant(inPrimitive, inPrimitive.nthInputConstant(2).i);
|
||||
|
||||
// grab the immediate value from the incoming poAddA_I
|
||||
DataNode& addrSource = inPrimitive.nthInput(1).getProducer().getNode();
|
||||
|
||||
int32 offset = addrSource.nthInputConstant(1).i;
|
||||
if ((offset >= -4096) && (offset <= 4095))
|
||||
{
|
||||
storeInsn = new(mPool) SparcStore(&inPrimitive, mPool, siSt, saRIR, offset);
|
||||
useProducer(addrSource.nthInputVariable(0), *storeInsn, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(false);
|
||||
}
|
||||
useProducer(inPrimitive.nthInputVariable(0), *storeInsn, 0); // <- store
|
||||
useTemporaryVR(*storeInsn, constVr, 2); // <- value
|
||||
defineProducer(inPrimitive, *storeInsn, 0); // -> store
|
||||
}
|
||||
|
||||
void SparcSunOSEmitter::
|
||||
emit_St_IRegisterIndirect(Primitive& inPrimitive)
|
||||
{
|
||||
DataNode& addrSource = inPrimitive.nthInput(1).getProducer().getNode();
|
||||
|
||||
int32 offset = addrSource.nthInputConstant(1).i;
|
||||
if ((offset >= -4096) && (offset <= 4095))
|
||||
{
|
||||
SparcStore& storeInsn = *new(mPool) SparcStore(&inPrimitive, mPool, siSt, saRIR, offset);
|
||||
useProducer(inPrimitive.nthInputVariable(0), storeInsn, 0); // <- store
|
||||
useProducer(addrSource.nthInputVariable(0), storeInsn, 1); // <- address base
|
||||
useProducer(inPrimitive.nthInputVariable(2), storeInsn, 2); // <- value
|
||||
defineProducer(inPrimitive, storeInsn, 0); // -> store
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
VirtualRegister& SparcSunOSEmitter::
|
||||
genLoadConstant(DataNode& inPrimitive, int32 constI)
|
||||
{
|
||||
if ((constI >= -4096) && (constI <= 4095))
|
||||
{
|
||||
SparcInsn& newInsn = *new(mPool) SparcInsn(&inPrimitive, mPool, siOr, constI ? saZIR : saZZR, constI & 0x1fff);
|
||||
VirtualRegister& constantVr = defineTemporary(newInsn, 0);
|
||||
return constantVr;
|
||||
}
|
||||
else if ((constI & 0x1fff) == 0)
|
||||
{
|
||||
// FIXME
|
||||
assert(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
// FIXME
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
uint32 SparcSunOSEmitter::
|
||||
getPrologSize() const
|
||||
{
|
||||
return 4;
|
||||
}
|
||||
|
||||
void SparcSunOSEmitter::
|
||||
formatPrologToMemory(void* inWhere)
|
||||
{
|
||||
*(uint32*) inWhere = 0x9de3bf90; // save %sp,-112,%sp
|
||||
}
|
||||
|
||||
uint32 SparcSunOSEmitter::
|
||||
getEpilogSize() const
|
||||
{
|
||||
return 8;
|
||||
}
|
||||
|
||||
void SparcSunOSEmitter::
|
||||
formatEpilogToMemory(void* inWhere)
|
||||
{
|
||||
((uint32*) inWhere)[0] = 0x81c7e008; // ret
|
||||
((uint32*) inWhere)[1] = 0x81e80000; // restore %g0,%g0,%g0
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
/* -*- 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 _SPARC_SUNOS_EMITTER_H_
|
||||
#define _SPARC_SUNOS_EMITTER_H_
|
||||
|
||||
#include "InstructionEmitter.h"
|
||||
#include "SparcInstruction.h"
|
||||
#include "VirtualRegister.h"
|
||||
|
||||
class PrimArg;
|
||||
class Pool;
|
||||
|
||||
class SparcSunOSEmitter : public InstructionEmitter
|
||||
{
|
||||
public:
|
||||
SparcSunOSEmitter(Pool& inPool, VirtualRegisterManager& vrMan) : InstructionEmitter(inPool, vrMan) {}
|
||||
|
||||
virtual void emitPrimitive(Primitive& inPrimitive, NamedRule inRule);
|
||||
|
||||
virtual uint32 getPrologSize() const;
|
||||
virtual void formatPrologToMemory(void* inWhere);
|
||||
virtual uint32 getEpilogSize() const;
|
||||
virtual void formatEpilogToMemory(void* inWhere);
|
||||
|
||||
virtual void emitArguments(ControlBegin& inBeginNode);
|
||||
virtual void emitArgument(PrimArg& /*inArguments*/, const uint /*inArgumentIndex*/) {}
|
||||
virtual Instruction& emitCopy(DataNode& inDataNode, VirtualRegister& fromVr, VirtualRegister& toVr);
|
||||
virtual Instruction& emitLoad(DataNode& /*inDataNode*/, VirtualRegister& /*loadedRegister*/, VirtualRegister& /*stackRegister*/) {}
|
||||
virtual Instruction& emitStore(DataNode& /*inDataNode*/, VirtualRegister& /*storedRegister*/, VirtualRegister& /*stackRegister*/) {};
|
||||
virtual Instruction& emitAbsoluteBranch(DataNode& inDataNode, ControlNode& inTarget);
|
||||
|
||||
void emit_Insn_I(Primitive& inPrimitive, SparcInstructionKind kind);
|
||||
void emit_InsnI_I(Primitive& inPrimitive, SparcInstructionKind kind);
|
||||
void emit_Const_I(Primitive& inPrimitive);
|
||||
void emit_Cmp_I(Primitive& inPrimitive);
|
||||
void emit_CmpI_I(Primitive& inPrimitive);
|
||||
void emit_B(Primitive& inPrimitive, SparcConditionKind kind);
|
||||
void emit_ModE_I(Primitive& inPrimitive);
|
||||
void emit_DivI_I(Primitive& inPrimitive);
|
||||
void emit_ChkNull(Primitive& inPrimitive);
|
||||
void emit_LimitR(Primitive& inPrimitive);
|
||||
void emit_Limit(Primitive& inPrimitive);
|
||||
void emit_LdC_IRegisterIndirect(Primitive& inPrimitive);
|
||||
void emit_Ld_IRegisterIndirect(Primitive& inPrimitive);
|
||||
void emit_StI_IRegisterIndirect(Primitive& inPrimitive);
|
||||
void emit_St_IRegisterIndirect(Primitive& inPrimitive);
|
||||
VirtualRegister& genLoadConstant(DataNode& inPrimitive, int32 inConstant);
|
||||
};
|
||||
|
||||
#endif /* _SPARC_SUNOS_EMITTER_H_ */
|
|
@ -0,0 +1,441 @@
|
|||
%top
|
||||
/* -*- 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.
|
||||
*/
|
||||
#include "Burg.h"
|
||||
%
|
||||
|
||||
%terminals
|
||||
%
|
||||
|
||||
%startsymbols
|
||||
Control
|
||||
Result
|
||||
Exception
|
||||
Store
|
||||
Vcond
|
||||
Vint
|
||||
Vlong
|
||||
Vfloat
|
||||
Vdouble
|
||||
Vptr
|
||||
Cint
|
||||
Clong
|
||||
Cfloat
|
||||
Cdouble
|
||||
Cptr
|
||||
%
|
||||
|
||||
%grammar
|
||||
Vint: coReg_I $1 $
|
||||
Vlong: coReg_L $1 $
|
||||
Vfloat: coReg_F $1 $
|
||||
Vdouble: coReg_D $1 $
|
||||
Vptr: coReg_A $1 $
|
||||
Vcond: coReg_C $1 $
|
||||
Store: coReg_M $1 $
|
||||
Cint: coReg_I $1 $
|
||||
Clong: coReg_L $1 $
|
||||
Cfloat: coReg_F $1 $
|
||||
Cdouble: coReg_D $1 $
|
||||
Cptr: coReg_A $1 $
|
||||
|
||||
Vint: poConst_I $1 $emConst_I
|
||||
Vlong: poConst_L $1 $emConst_L
|
||||
Vfloat: poConst_F $1 $emConst_F
|
||||
Vdouble: poConst_D $1 $emConst_D
|
||||
Vptr: poConst_A $1 $emConst_A
|
||||
Vcond: poConst_C $1 $emConst_C
|
||||
|
||||
Store: poBreak(Store) $1 $emBreak
|
||||
|
||||
Vint: poArg_I $1 $emArg_I
|
||||
Vlong: poArg_L $1 $emArg_L
|
||||
Vfloat: poArg_F $1 $emArg_F
|
||||
Vdouble: poArg_D $1 $emArg_D
|
||||
Vptr: poArg_A $1 $emArg_A
|
||||
Store: poArg_M $1 $emArg_M
|
||||
|
||||
Result: poResult_I(Vint) $1 $emResult_I
|
||||
Result: poResult_L(Vlong) $1 $emResult_L
|
||||
Result: poResult_F(Vfloat) $1 $emResult_F
|
||||
Result: poResult_D(Vdouble) $1 $emResult_D
|
||||
Result: poResult_A(Vptr) $1 $emResult_A
|
||||
// (not used): poResult_C(Acond) $1 $emResult_C
|
||||
Result: poResult_M(Store) $1 $emResult_M
|
||||
|
||||
Control: poIfLt(Vcond) $1 $emIfLt
|
||||
Control: poIfEq(Vcond) $1 $emIfEq
|
||||
Control: poIfLe(Vcond) $1 $emIfLe
|
||||
Control: poIfGt(Vcond) $1 $emIfGt
|
||||
Control: poIfLgt(Vcond) $1 $emIfLgt
|
||||
Control: poIfGe(Vcond) $1 $emIfGe
|
||||
Control: poIfOrd(Vcond) $1 $emIfOrd
|
||||
Control: poIfUnord(Vcond) $1 $emIfUnord
|
||||
Control: poIfULt(Vcond) $1 $emIfULt
|
||||
Control: poIfUEq(Vcond) $1 $emIfUEq
|
||||
Control: poIfULe(Vcond) $1 $emIfULe
|
||||
Control: poIfUGt(Vcond) $1 $emIfUGt
|
||||
Control: poIfNe(Vcond) $1 $emIfNe
|
||||
Control: poIfUGe(Vcond) $1 $emIfUGe
|
||||
|
||||
Control: poSwitch(Vint) $1 $emSwitch
|
||||
|
||||
Vint: poAnd_I(Vint, Vint) $1 $emAnd_I
|
||||
Vlong: poAnd_L(Vlong, Vlong) $1 $emAnd_L
|
||||
Vint: poAndI_I(Vint, Cint) $1 $emAndI_I
|
||||
Vlong: poAndI_L(Vlong, Clong) $1 $emAndI_L
|
||||
Vint: poOr_I(Vint, Vint) $1 $emOr_I
|
||||
Vlong: poOr_L(Vlong, Vlong) $1 $emOr_L
|
||||
Vint: poOrI_I(Vint, Cint) $1 $emOrI_I
|
||||
Vlong: poOrI_L(Vlong, Clong) $1 $emOrI_L
|
||||
Vint: poXor_I(Vint, Vint) $1 $emXor_I
|
||||
Vlong: poXor_L(Vlong, Vlong) $1 $emXor_L
|
||||
Vint: poXorI_I(Vint, Cint) $1 $emXorI_I
|
||||
Vlong: poXorI_L(Vlong, Clong) $1 $emXorI_L
|
||||
|
||||
Vint: poAdd_I(Vint, Vint) $1 $emAdd_I
|
||||
Vlong: poAdd_L(Vlong, Vlong) $1 $emAdd_L
|
||||
Vptr: poAdd_A(Vptr, Vint) $1 $emAdd_A
|
||||
Vptr: poAddU_A(Vptr, Vint) $1 $emAddU_A
|
||||
Vint: poAddI_I(Vint, Cint) $1 $emAddI_I
|
||||
Vlong: poAddI_L(Vlong, Clong) $1 $emAddI_L
|
||||
Vptr: poAddI_A(Vptr, Cint) $1 $emAddI_A
|
||||
Vptr: poAddR_A(Cptr, Vint) $1 $emAddR_A
|
||||
Vptr: poAddRU_A(Cptr, Vint) $1 $emAddRU_A
|
||||
|
||||
Vint: poSub_I(Vint, Vint) $1 $emSub_I
|
||||
Vlong: poSub_L(Vlong, Vlong) $1 $emSub_L
|
||||
Vptr: poSub_A(Vptr, Vint) $1 $emSub_A
|
||||
Vptr: poSubU_A(Vptr, Vint) $1 $emSubU_A
|
||||
Vint: poSubR_I(Cint, Vint) $1 $emSubR_I
|
||||
Vlong: poSubR_L(Clong, Vlong) $1 $emSubR_L
|
||||
Vptr: poSubR_A(Cptr, Vint) $1 $emSubR_A
|
||||
Vptr: poSubUR_A(Cptr, Vint) $1 $emSubUR_A
|
||||
Vint: poSubA_I(Vptr, Vptr) $1 $emSubA_I
|
||||
Vint: poSubAI_I(Vptr, Cptr) $1 $emSubAI_I
|
||||
Vint: poSubAR_I(Cptr, Vptr) $1 $emSubAR_I
|
||||
|
||||
Vint: poMul_I(Vint, Vint) $1 $emMul_I
|
||||
Vlong: poMul_L(Vlong, Vlong) $1 $emMul_L
|
||||
Vint: poMulI_I(Vint, Cint) $1 $emMulI_I
|
||||
Vlong: poMulI_L(Vlong, Clong) $1 $emMulI_L
|
||||
|
||||
Vint: poDivI_I(Vint, Cint) $1 $emDivI_I
|
||||
Vint: poModE_I(Vint, Vint) $1 $emModE_I
|
||||
|
||||
Vint: poShl_I(Vint, Vint) $1 $emShl_I
|
||||
Vlong: poShl_L(Vlong, Vint) $1 $emShl_L
|
||||
Vint: poShlI_I(Vint, Cint) $1 $emShlI_I
|
||||
Vlong: poShlI_L(Vlong, Cint) $1 $emShlI_L
|
||||
Vint: poShlR_I(Cint, Vint) $1 $emShlR_I
|
||||
Vlong: poShlR_L(Clong, Vint) $1 $emShlR_L
|
||||
Vint: poShr_I(Vint, Vint) $1 $emShr_I
|
||||
Vlong: poShr_L(Vlong, Vint) $1 $emShr_L
|
||||
Vint: poShrI_I(Vint, Cint) $1 $emShrI_I
|
||||
Vlong: poShrI_L(Vlong, Cint) $1 $emShrI_L
|
||||
Vint: poShrR_I(Cint, Vint) $1 $emShrR_I
|
||||
Vlong: poShrR_L(Clong, Vint) $1 $emShrR_L
|
||||
Vint: poShrU_I(Vint, Vint) $1 $emShrU_I
|
||||
Vlong: poShrU_L(Vlong, Vint) $1 $emShrU_L
|
||||
Vint: poShrUI_I(Vint, Cint) $1 $emShrUI_I
|
||||
Vlong: poShrUI_L(Vlong, Cint) $1 $emShrUI_L
|
||||
Vint: poShrUR_I(Cint, Vint) $1 $emShrUR_I
|
||||
Vlong: poShrUR_L(Clong, Vint) $1 $emShrUR_L
|
||||
|
||||
Vint: poExt_I(Vint, Cint) $1 $emExt_I
|
||||
Vlong: poExt_L(Vlong, Cint) $1 $emExt_L
|
||||
|
||||
Vfloat: poFAdd_F(Vfloat, Vfloat) $1 $emFAdd_F
|
||||
Vdouble: poFAdd_D(Vdouble, Vdouble) $1 $emFAdd_D
|
||||
Vfloat: poFAddI_F(Vfloat, Cfloat) $1 $emFAddI_F
|
||||
Vdouble: poFAddI_D(Vdouble, Cdouble) $1 $emFAddI_D
|
||||
|
||||
Vfloat: poFSub_F(Vfloat, Vfloat) $1 $emFSub_F
|
||||
Vdouble: poFSub_D(Vdouble, Vdouble) $1 $emFSub_D
|
||||
Vfloat: poFSubR_F(Cfloat, Vfloat) $1 $emFSubR_F
|
||||
Vdouble: poFSubR_D(Cdouble, Vdouble) $1 $emFSubR_D
|
||||
|
||||
Vfloat: poFMul_F(Vfloat, Vfloat) $1 $emFMul_F
|
||||
Vdouble: poFMul_D(Vdouble, Vdouble) $1 $emFMul_D
|
||||
Vfloat: poFMulI_F(Vfloat, Cfloat) $1 $emFMulI_F
|
||||
Vdouble: poFMulI_D(Vdouble, Cdouble) $1 $emFMulI_D
|
||||
|
||||
Vfloat: poFDiv_F(Vfloat, Vfloat) $1 $emFDiv_F
|
||||
Vdouble: poFDiv_D(Vdouble, Vdouble) $1 $emFDiv_D
|
||||
Vfloat: poFDivI_F(Vfloat, Cfloat) $1 $emFDivI_F
|
||||
Vdouble: poFDivI_D(Vdouble, Cdouble) $1 $emFDivI_D
|
||||
Vfloat: poFDivR_F(Cfloat, Vfloat) $1 $emFDivR_F
|
||||
Vdouble: poFDivR_D(Cdouble, Vdouble) $1 $emFDivR_D
|
||||
|
||||
Vfloat: poFRem_F(Vfloat, Vfloat) $1 $emFRem_F
|
||||
Vdouble: poFRem_D(Vdouble, Vdouble) $1 $emFRem_D
|
||||
Vfloat: poFRemI_F(Vfloat, Cfloat) $1 $emFRemI_F
|
||||
Vdouble: poFRemI_D(Vdouble, Cdouble) $1 $emFRemI_D
|
||||
Vfloat: poFRemR_F(Cfloat, Vfloat) $1 $emFRemR_F
|
||||
Vdouble: poFRemR_D(Cdouble, Vdouble) $1 $emFRemR_D
|
||||
|
||||
Vint: poConvI_L(Vlong) $1 $emConvI_L
|
||||
Vlong: poConvL_I(Vint) $1 $emConvL_I
|
||||
|
||||
Vint: poFConvI_F(Vfloat) $1 $emFConvI_F
|
||||
Vint: poFConvI_D(Vdouble) $1 $emFConvI_D
|
||||
Vlong: poFConvL_F(Vfloat) $1 $emFConvL_F
|
||||
Vlong: poFConvL_D(Vdouble) $1 $emFConvL_D
|
||||
Vfloat: poFConvF_I(Vint) $1 $emFConvF_I
|
||||
Vfloat: poFConvF_L(Vlong) $1 $emFConvF_L
|
||||
Vfloat: poFConvF_D(Vdouble) $1 $emFConvF_D
|
||||
Vdouble: poFConvD_I(Vint) $1 $emFConvD_I
|
||||
Vdouble: poFConvD_L(Vlong) $1 $emFConvD_L
|
||||
Vdouble: poFConvD_F(Vfloat) $1 $emFConvD_F
|
||||
|
||||
Vcond: poCmp_I(Vint, Vint) $1 $emCmp_I
|
||||
Vcond: poCmp_L(Vlong, Vlong) $1 $emCmp_L
|
||||
Vcond: poCmpI_I(Vint, Cint) $1 $emCmpI_I
|
||||
Vcond: poCmpI_L(Vlong, Clong) $1 $emCmpI_L
|
||||
Vcond: poCmpU_I(Vint, Vint) $1 $emCmpU_I
|
||||
Vcond: poCmpU_L(Vlong, Vlong) $1 $emCmpU_L
|
||||
Vcond: poCmpU_A(Vptr, Vptr) $1 $emCmpU_A
|
||||
Vcond: poCmpUI_I(Vint, Cint) $1 $emCmpUI_I
|
||||
Vcond: poCmpUI_L(Vlong, Clong) $1 $emCmpUI_L
|
||||
Vcond: poCmpUI_A(Vptr, Cptr) $1 $emCmpUI_A
|
||||
|
||||
Vcond: poFCmp_F(Vfloat, Vfloat) $1 $emFCmp_F
|
||||
Vcond: poFCmp_D(Vdouble, Vdouble) $1 $emFCmp_D
|
||||
Vcond: poFCmpI_F(Vfloat, Cfloat) $1 $emFCmpI_F
|
||||
Vcond: poFCmpI_D(Vdouble, Cdouble) $1 $emFCmpI_D
|
||||
|
||||
Vint: poLt_I(Vcond) $1 $emLt_I
|
||||
Vint: poEq_I(Vcond) $1 $emEq_I
|
||||
Vint: poLe_I(Vcond) $1 $emLe_I
|
||||
Vint: poGt_I(Vcond) $1 $emGt_I
|
||||
Vint: poLgt_I(Vcond) $1 $emLgt_I
|
||||
Vint: poGe_I(Vcond) $1 $emGe_I
|
||||
Vint: poOrd_I(Vcond) $1 $emOrd_I
|
||||
Vint: poUnord_I(Vcond) $1 $emUnord_I
|
||||
Vint: poULt_I(Vcond) $1 $emULt_I
|
||||
Vint: poUEq_I(Vcond) $1 $emUEq_I
|
||||
Vint: poULe_I(Vcond) $1 $emULe_I
|
||||
Vint: poUGt_I(Vcond) $1 $emUGt_I
|
||||
Vint: poNe_I(Vcond) $1 $emNe_I
|
||||
Vint: poUGe_I(Vcond) $1 $emUGe_I
|
||||
|
||||
Vint: poCatL_I(Vcond) $1 $emCatL_I
|
||||
Vint: poCatG_I(Vcond) $1 $emCatG_I
|
||||
Vint: poCatCL_I(Vcond) $1 $emCatCL_I
|
||||
Vint: poCatCG_I(Vcond) $1 $emCatCG_I
|
||||
|
||||
Exception: poChkNull(Vptr) $1 $emChkNull
|
||||
Exception: poLimit(Vint, Vint) $1 $emLimit
|
||||
Exception: poLimitI(Vint, Cint) $1 $emLimitI
|
||||
Exception: poLimitR(Cint, Vint) $1 $emLimitR
|
||||
|
||||
Vint: poLd_I(Vptr) $1 $emLd_I
|
||||
Vlong: poLd_L(Vptr) $1 $emLd_L
|
||||
Vfloat: poLd_F(Vptr) $1 $emLd_F
|
||||
Vdouble: poLd_D(Vptr) $1 $emLd_D
|
||||
Vptr: poLd_A(Vptr) $1 $emLd_A
|
||||
Vint: poLdE_I(Cptr) $1 $emLdE_I
|
||||
Vlong: poLdE_L(Cptr) $1 $emLdE_L
|
||||
Vfloat: poLdE_F(Cptr) $1 $emLdE_F
|
||||
Vdouble: poLdE_D(Cptr) $1 $emLdE_D
|
||||
Vptr: poLdE_A(Cptr) $1 $emLdE_A
|
||||
Vint: poLdG_I(Cptr) $1 $emLdG_I
|
||||
Vlong: poLdG_L(Cptr) $1 $emLdG_L
|
||||
Vfloat: poLdG_F(Cptr) $1 $emLdG_F
|
||||
Vdouble: poLdG_D(Cptr) $1 $emLdG_D
|
||||
Vptr: poLdG_A(Cptr) $1 $emLdG_A
|
||||
Vint: poLdS_B(Vptr) $1 $emLdS_B
|
||||
Vint: poLdS_H(Vptr) $1 $emLdS_H
|
||||
Vint: poLdSE_B(Cptr) $1 $emLdSE_B
|
||||
Vint: poLdSE_H(Cptr) $1 $emLdSE_H
|
||||
Vint: poLdSG_B(Cptr) $1 $emLdSG_B
|
||||
Vint: poLdSG_H(Cptr) $1 $emLdSG_H
|
||||
Vint: poLdU_B(Vptr) $1 $emLdU_B
|
||||
Vint: poLdU_H(Vptr) $1 $emLdU_H
|
||||
Vint: poLdUE_B(Cptr) $1 $emLdUE_B
|
||||
Vint: poLdUE_H(Cptr) $1 $emLdUE_H
|
||||
Vint: poLdUG_B(Cptr) $1 $emLdUG_B
|
||||
Vint: poLdUG_H(Cptr) $1 $emLdUG_H
|
||||
Vint: poLdV_I(Vptr) $1 $emLdV_I
|
||||
Vlong: poLdV_L(Vptr) $1 $emLdV_L
|
||||
Vfloat: poLdV_F(Vptr) $1 $emLdV_F
|
||||
Vdouble: poLdV_D(Vptr) $1 $emLdV_D
|
||||
Vptr: poLdV_A(Vptr) $1 $emLdV_A
|
||||
Vint: poLdVE_I(Cptr) $1 $emLdVE_I
|
||||
Vlong: poLdVE_L(Cptr) $1 $emLdVE_L
|
||||
Vfloat: poLdVE_F(Cptr) $1 $emLdVE_F
|
||||
Vdouble: poLdVE_D(Cptr) $1 $emLdVE_D
|
||||
Vptr: poLdVE_A(Cptr) $1 $emLdVE_A
|
||||
Vint: poLdVG_I(Cptr) $1 $emLdVG_I
|
||||
Vlong: poLdVG_L(Cptr) $1 $emLdVG_L
|
||||
Vfloat: poLdVG_F(Cptr) $1 $emLdVG_F
|
||||
Vdouble: poLdVG_D(Cptr) $1 $emLdVG_D
|
||||
Vptr: poLdVG_A(Cptr) $1 $emLdVG_A
|
||||
Vint: poLdVS_B(Vptr) $1 $emLdVS_B
|
||||
Vint: poLdVS_H(Vptr) $1 $emLdVS_H
|
||||
Vint: poLdVSE_B(Cptr) $1 $emLdVSE_B
|
||||
Vint: poLdVSE_H(Cptr) $1 $emLdVSE_H
|
||||
Vint: poLdVSG_B(Cptr) $1 $emLdVSG_B
|
||||
Vint: poLdVSG_H(Cptr) $1 $emLdVSG_H
|
||||
Vint: poLdVU_B(Vptr) $1 $emLdVU_B
|
||||
Vint: poLdVU_H(Vptr) $1 $emLdVU_H
|
||||
Vint: poLdVUE_B(Cptr) $1 $emLdVUE_B
|
||||
Vint: poLdVUE_H(Cptr) $1 $emLdVUE_H
|
||||
Vint: poLdVUG_B(Cptr) $1 $emLdVUG_B
|
||||
Vint: poLdVUG_H(Cptr) $1 $emLdVUG_H
|
||||
Vint: poLdC_I(Vptr) $1 $emLdC_I
|
||||
Vint: poLdC_L(Vptr) $1 $emLdC_L
|
||||
Vint: poLdC_F(Vptr) $1 $emLdC_F
|
||||
Vint: poLdC_D(Vptr) $1 $emLdC_D
|
||||
Vint: poLdC_A(Vptr) $1 $emLdC_A
|
||||
Vint: poLdCE_I(Vptr) $1 $emLdCE_I
|
||||
Vint: poLdCE_L(Vptr) $1 $emLdCE_L
|
||||
Vint: poLdCE_F(Vptr) $1 $emLdCE_F
|
||||
Vint: poLdCE_D(Vptr) $1 $emLdCE_D
|
||||
Vint: poLdCE_A(Vptr) $1 $emLdCE_A
|
||||
|
||||
Vint: poLdC_I(poAddI_A(Vptr, Cint)) $0 $emLdC_IRegisterIndirect
|
||||
Vint: poLd_I(poAddI_A(Vptr, Cint)) $0 $emLd_IRegisterIndirect
|
||||
|
||||
|
||||
// poLdCG_I = 257, // Load constant global *Cptr -> Vint (not used)
|
||||
// poLdCG_L = 258, // Load constant global *Cptr -> Vlong (not used)
|
||||
// poLdCG_F = 259, // Load constant global *Cptr -> Vfloat (not used)
|
||||
// poLdCG_D = 260, // Load constant global *Cptr -> Vdouble (not used)
|
||||
// poLdCG_A = 261, // Load constant global *Cptr -> Vptr (not used)
|
||||
Vint: poLdCS_B(Vptr) $1 $emLdCS_B
|
||||
Vint: poLdCS_H(Vptr) $1 $emLdCS_H
|
||||
Vint: poLdCSE_B(Cptr) $1 $emLdCSE_B
|
||||
Vint: poLdCSE_H(Cptr) $1 $emLdCSE_H
|
||||
//Vint (not used): poLdCSG_B(Cptr) $1 $emLdCSG_B
|
||||
//Vint (not used): poLdCSG_H(Cptr) $1 $emLdCSG_H
|
||||
Vint: poLdCU_B(Vptr) $1 $emLdCU_B
|
||||
Vint: poLdCU_H(Vptr) $1 $emLdCU_H
|
||||
Vint: poLdCUE_B(Cptr) $1 $emLdCUE_B
|
||||
Vint: poLdCUE_H(Cptr) $1 $emLdCUE_H
|
||||
//Vint (not used): poLdCUG_B(Cptr) $1 $emLdCUG_B
|
||||
//Vint (not used): poLdCUG_H(Cptr) $1 $emLdCUG_H
|
||||
|
||||
Store: poSt_B(Vptr, Vint) $1 $emSt_B
|
||||
Store: poSt_H(Vptr, Vint) $1 $emSt_H
|
||||
Store: poSt_I(Vptr, Vint) $1 $emSt_I
|
||||
Store: poSt_L(Vptr, Vlong) $1 $emSt_L
|
||||
Store: poSt_F(Vptr, Vfloat) $1 $emSt_F
|
||||
Store: poSt_D(Vptr, Vdouble) $1 $emSt_D
|
||||
Store: poSt_A(Vptr, Vptr) $1 $emSt_A
|
||||
Store: poStI_B(Vptr, Cint) $1 $emStI_B
|
||||
Store: poStI_H(Vptr, Cint) $1 $emStI_H
|
||||
Store: poStI_I(Vptr, Cint) $1 $emStI_I
|
||||
Store: poStI_L(Vptr, Clong) $1 $emStI_L
|
||||
Store: poStI_F(Vptr, Cfloat) $1 $emStI_F
|
||||
Store: poStI_D(Vptr, Cdouble) $1 $emStI_D
|
||||
Store: poStI_A(Vptr, Cptr) $1 $emStI_A
|
||||
Store: poStE_B(Cptr, Vint) $1 $emStE_B
|
||||
Store: poStE_H(Cptr, Vint) $1 $emStE_H
|
||||
Store: poStE_I(Cptr, Vint) $1 $emStE_I
|
||||
Store: poStE_L(Cptr, Vlong) $1 $emStE_L
|
||||
Store: poStE_F(Cptr, Vfloat) $1 $emStE_F
|
||||
Store: poStE_D(Cptr, Vdouble) $1 $emStE_D
|
||||
Store: poStE_A(Cptr, Vptr) $1 $emStE_A
|
||||
Store: poStEI_B(Cptr, Cint) $1 $emStEI_B
|
||||
Store: poStEI_H(Cptr, Cint) $1 $emStEI_H
|
||||
Store: poStEI_I(Cptr, Cint) $1 $emStEI_I
|
||||
Store: poStEI_L(Cptr, Clong) $1 $emStEI_L
|
||||
Store: poStEI_F(Cptr, Cfloat) $1 $emStEI_F
|
||||
Store: poStEI_D(Cptr, Cdouble) $1 $emStEI_D
|
||||
Store: poStEI_A(Cptr, Cptr) $1 $emStEI_A
|
||||
Store: poStG_B(Cptr, Vint) $1 $emStG_B
|
||||
Store: poStG_H(Cptr, Vint) $1 $emStG_H
|
||||
Store: poStG_I(Cptr, Vint) $1 $emStG_I
|
||||
Store: poStG_L(Cptr, Vlong) $1 $emStG_L
|
||||
Store: poStG_F(Cptr, Vfloat) $1 $emStG_F
|
||||
Store: poStG_D(Cptr, Vdouble) $1 $emStG_D
|
||||
Store: poStG_A(Cptr, Vptr) $1 $emStG_A
|
||||
Store: poStGI_B(Cptr, Cint) $1 $emStGI_B
|
||||
Store: poStGI_H(Cptr, Cint) $1 $emStGI_H
|
||||
Store: poStGI_I(Cptr, Cint) $1 $emStGI_I
|
||||
Store: poStGI_L(Cptr, Clong) $1 $emStGI_L
|
||||
Store: poStGI_F(Cptr, Cfloat) $1 $emStGI_F
|
||||
Store: poStGI_D(Cptr, Cdouble) $1 $emStGI_D
|
||||
Store: poStGI_A(Cptr, Cptr) $1 $emStGI_A
|
||||
Store: poStV_B(Vptr, Vint) $1 $emStV_B
|
||||
Store: poStV_H(Vptr, Vint) $1 $emStV_H
|
||||
Store: poStV_I(Vptr, Vint) $1 $emStV_I
|
||||
Store: poStV_L(Vptr, Vlong) $1 $emStV_L
|
||||
Store: poStV_F(Vptr, Vfloat) $1 $emStV_F
|
||||
Store: poStV_D(Vptr, Vdouble) $1 $emStV_D
|
||||
Store: poStV_A(Vptr, Vptr) $1 $emStV_A
|
||||
Store: poStVI_B(Vptr, Cint) $1 $emStVI_B
|
||||
Store: poStVI_H(Vptr, Cint) $1 $emStVI_H
|
||||
Store: poStVI_I(Vptr, Cint) $1 $emStVI_I
|
||||
Store: poStVI_L(Vptr, Clong) $1 $emStVI_L
|
||||
Store: poStVI_F(Vptr, Cfloat) $1 $emStVI_F
|
||||
Store: poStVI_D(Vptr, Cdouble) $1 $emStVI_D
|
||||
Store: poStVI_A(Vptr, Cptr) $1 $emStVI_A
|
||||
Store: poStVE_B(Cptr, Vint) $1 $emStVE_B
|
||||
Store: poStVE_H(Cptr, Vint) $1 $emStVE_H
|
||||
Store: poStVE_I(Cptr, Vint) $1 $emStVE_I
|
||||
Store: poStVE_L(Cptr, Vlong) $1 $emStVE_L
|
||||
Store: poStVE_F(Cptr, Vfloat) $1 $emStVE_F
|
||||
Store: poStVE_D(Cptr, Vdouble) $1 $emStVE_D
|
||||
Store: poStVE_A(Cptr, Vptr) $1 $emStVE_A
|
||||
Store: poStVEI_B(Cptr, Cint) $1 $emStVEI_B
|
||||
Store: poStVEI_H(Cptr, Cint) $1 $emStVEI_H
|
||||
Store: poStVEI_I(Cptr, Cint) $1 $emStVEI_I
|
||||
Store: poStVEI_L(Cptr, Clong) $1 $emStVEI_L
|
||||
Store: poStVEI_F(Cptr, Cfloat) $1 $emStVEI_F
|
||||
Store: poStVEI_D(Cptr, Cdouble) $1 $emStVEI_D
|
||||
Store: poStVEI_A(Cptr, Cptr) $1 $emStVEI_A
|
||||
Store: poStVG_B(Cptr, Vint) $1 $emStVG_B
|
||||
Store: poStVG_H(Cptr, Vint) $1 $emStVG_H
|
||||
Store: poStVG_I(Cptr, Vint) $1 $emStVG_I
|
||||
Store: poStVG_L(Cptr, Vlong) $1 $emStVG_L
|
||||
Store: poStVG_F(Cptr, Vfloat) $1 $emStVG_F
|
||||
Store: poStVG_D(Cptr, Vdouble) $1 $emStVG_D
|
||||
Store: poStVG_A(Cptr, Vptr) $1 $emStVG_A
|
||||
Store: poStVGI_B(Cptr, Cint) $1 $emStVGI_B
|
||||
Store: poStVGI_H(Cptr, Cint) $1 $emStVGI_H
|
||||
Store: poStVGI_I(Cptr, Cint) $1 $emStVGI_I
|
||||
Store: poStVGI_L(Cptr, Clong) $1 $emStVGI_L
|
||||
Store: poStVGI_F(Cptr, Cfloat) $1 $emStVGI_F
|
||||
Store: poStVGI_D(Cptr, Cdouble) $1 $emStVGI_D
|
||||
Store: poStVGI_A(Cptr, Cptr) $1 $emStVGI_A
|
||||
|
||||
Store: poSt_I(poAddI_A(Vptr, Cint), Vint) $0 $emSt_IRegisterIndirect
|
||||
Store: poStI_I(poAddI_A(Vptr, Cint), Cint) $0 $emStI_IRegisterIndirect
|
||||
|
||||
// Store, Vint: poMEnter_A(Vptr) $1 $emMEnter_A
|
||||
// Store, Vint: poMEnterG_A(Cptr) $1 $emMEnterG_A
|
||||
// Store, Vint: poMExit_A(Vptr) $1 $emMExit_A
|
||||
// Store, Vint: poMExitG_A(Cptr) $1 $emMExitG_A
|
||||
// Vptr: poLookupV_A(Vptr, Cint) $1 $emLookupV_A
|
||||
// Vptr: poLookupI_A(Vptr, Cint) $1 $emLookupI_A
|
||||
// ...: poSysCall(M) $1 $emSysCall
|
||||
// Store, ...: poSysCallV(M) $1 $emSysCallV
|
||||
// ..., E: poSysCallE(M) $1 $emSysCallE
|
||||
// Store, ..., E: poSysCallEV(M) $1 $emSysCallEV
|
||||
//Store, ..., E: poCall(Va) $1 $emCall
|
||||
|
||||
VInt: poCallI $1 $emCallI_I
|
||||
Vlong: poCallI $1 $emCallI_L
|
||||
Vfloat: poCallI $1 $emCallI_F
|
||||
Vdouble: poCallI $1 $emCallI_D
|
||||
Vptr: poCallI $1 $emCallI_P
|
||||
// Store, ..., E: poCallF(F) $1 $emCallF
|
||||
%
|
||||
|
|
@ -0,0 +1,113 @@
|
|||
#!gmake
|
||||
#
|
||||
# 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.
|
||||
|
||||
#######################################################################
|
||||
# (1) Directory specific info #
|
||||
#######################################################################
|
||||
|
||||
DEPTH = ../../../..
|
||||
|
||||
CPPSRCS = x86-win32.nad.burg.cpp \
|
||||
x86ArgumentList.cpp \
|
||||
x86Opcode.cpp \
|
||||
x86Win32Emitter.cpp \
|
||||
x86Win32Instruction.cpp \
|
||||
x86Formatter.cpp \
|
||||
x86StdCall.cpp \
|
||||
x86Float.cpp \
|
||||
$(OS_SUPPORT) \
|
||||
$(NULL)
|
||||
|
||||
LOCAL_MD_EXPORTS_x86 = x86ArgumentList.h \
|
||||
x86Float.h \
|
||||
x86Formatter.h \
|
||||
x86Linux_Support.h \
|
||||
x86Opcode.h \
|
||||
x86StdCall.h \
|
||||
x86Win32Cpu.h \
|
||||
x86Win32Emitter.h \
|
||||
x86Win32Instruction.h \
|
||||
$(NULL)
|
||||
|
||||
MODULE_NAME = EF
|
||||
|
||||
#######################################################################
|
||||
# (2) Include "component" configuration information. #
|
||||
#######################################################################
|
||||
|
||||
include $(DEPTH)/config/config.mk
|
||||
|
||||
#######################################################################
|
||||
# (3) Include "local" platform-dependent assignments (OPTIONAL). #
|
||||
#######################################################################
|
||||
|
||||
#
|
||||
# Generation of OS-specific support files
|
||||
#
|
||||
ifeq ($(OS_ARCH),WINNT)
|
||||
OS_SUPPORT = x86Win32_Support.cpp
|
||||
endif
|
||||
|
||||
ifeq ($(OS_ARCH),Linux)
|
||||
ASFILES = x86Linux.s
|
||||
OS_SUPPORT = x86Linux_Support.cpp
|
||||
endif
|
||||
|
||||
#######################################################################
|
||||
# (4) Execute "component" rules. (OPTIONAL) #
|
||||
#######################################################################
|
||||
|
||||
include $(DEPTH)/config/rules.mk
|
||||
|
||||
#######################################################################
|
||||
# (7) Execute "local" rules. (OPTIONAL). #
|
||||
#######################################################################
|
||||
|
||||
#
|
||||
# Rules to generate x86-win32.nad.burg.[cpp][h]
|
||||
#
|
||||
ifneq ($(OS_ARCH),WINNT)
|
||||
x86-win32.nad.burg.cpp: x86-win32.nad.burg $(BURG)
|
||||
$(BURG) -I -o $@ < $<
|
||||
|
||||
x86-win32.nad.burg: x86-win32.nad $(DEPTH)/Compiler/PrimitiveGraph/PrimitiveOperations $(DEPTH)/Tools/Nad/nad.pl
|
||||
$(PERL) $(DEPTH)/Tools/Nad/nad.pl $< $(DEPTH)/Compiler/PrimitiveGraph/PrimitiveOperations \
|
||||
$(LOCAL_EXPORT_DIR)/PrimitiveOperations.h \
|
||||
$(DEPTH)/Compiler/PrimitiveGraph/PrimitiveOperations.cpp \
|
||||
$<.burg.h > $@
|
||||
else
|
||||
x86-win32.nad.burg.cpp: x86-win32.nad $(BURG) $(DEPTH)/Compiler/PrimitiveGraph/PrimitiveOperations $(DEPTH)/Tools/Nad/nad.pl
|
||||
$(DEPTH)/config/genburg.bat $(BURG) $(DEPTH)/Tools/Nad/nad.pl $< $(DEPTH)/Compiler/PrimitiveGraph/PrimitiveOperations
|
||||
cp $(DEPTH)/Compiler/PrimitiveGraph/PrimitiveOperations.h $(LOCAL_EXPORT_DIR)
|
||||
endif
|
||||
|
||||
#
|
||||
# Extra cleaning
|
||||
#
|
||||
clean clobber realclean clobber_all::
|
||||
rm -f x86-win32.nad.burg.cpp x86-win32.nad.burg.h x86-win32.nad.burg
|
||||
|
||||
#
|
||||
# Special rules for x86Win32Support.cpp on Windows: it currently breaks the
|
||||
# compiler if compiled with optimizations
|
||||
#
|
||||
ifeq ($(OS_ARCH),WINNT)
|
||||
OPT_FLAG = -O2
|
||||
$(OBJDIR)/x86Win32_Support$(OBJ_SUFFIX): x86Win32_Support.cpp
|
||||
$(CCC) -c $(filter-out $(OPT_FLAG),$(CFLAGS)) $< -Fo$@
|
||||
endif
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
#!gmake
|
||||
#
|
||||
# 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.
|
||||
|
||||
NAD_SRC = "{MOZ_SRC}:ns:electricalfire:Compiler:CodeGenerator:md:x86:x86-win32.nad"
|
||||
PRIMITIVEOPERATIONS = "{MOZ_SRC}:ns:electricalfire:Compiler:PrimitiveGraph:PrimitiveOperations"
|
||||
NAD = {PERL} "{MOZ_SRC}:ns:electricalfire:Tools:Nad:Nad.pl"
|
||||
|
||||
NAD_OUTPUTS = {NAD_SRC}.burg.h {NAD_SRC}.burg.cpp {PRIMITIVEOPERATIONS}.h {PRIMITIVEOPERATIONS}.cpp
|
||||
|
||||
{NAD_OUTPUTS} Ä {NAD_SRC} {PRIMITIVEOPERATIONS}
|
||||
{NAD} {NAD_SRC} {PRIMITIVEOPERATIONS} {PRIMITIVEOPERATIONS}.h {PRIMITIVEOPERATIONS}.cpp {NAD_SRC}.burg.h > BurgOut
|
||||
BURG -I <BurgOut > {NAD_SRC}.burg.cpp
|
||||
set TouchedFiles "`ResolveAlias {PRIMITIVEOPERATIONS}.h`"
|
||||
set TouchedFiles "{TouchedFiles},`ResolveAlias {NAD_SRC}.burg.h`"
|
||||
set TouchedFiles "{TouchedFiles},`ResolveAlias {NAD_SRC}.burg.cpp`"
|
||||
set TouchedFiles "{TouchedFiles},`ResolveAlias {PRIMITIVEOPERATIONS}.cpp`"
|
||||
export TouchedFiles
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,519 @@
|
|||
/* -*- 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.
|
||||
*/
|
||||
//
|
||||
// File: x86ArgumentList.h
|
||||
//
|
||||
// Authors: Peter DeSantis
|
||||
// Simon Holmes a Court
|
||||
//
|
||||
|
||||
#ifndef _X86ARGUMENTLIST
|
||||
#define _X86ARGUMENTLIST
|
||||
|
||||
/*
|
||||
***********************************************************************************************************************************
|
||||
x86 ArgumentLists
|
||||
Anywhere from 0 to 10 bytes of information following the x86 Opcode.
|
||||
Includes the ModR/M byte, the SIB byte, the Displacement field and the immediate field.
|
||||
***********************************************************************************************************************************
|
||||
*/
|
||||
|
||||
#include "CatchAssert.h"
|
||||
#include <stdio.h>
|
||||
|
||||
|
||||
#include "prtypes.h"
|
||||
#include "ControlNodes.h"
|
||||
#include "x86Opcode.h"
|
||||
#include "MemoryAccess.h"
|
||||
|
||||
class x86ArgListInstruction;
|
||||
|
||||
enum x86ImmediateSize
|
||||
{
|
||||
isNoImmediate,
|
||||
is1ByteImmediate,
|
||||
is4ByteImmediate
|
||||
};
|
||||
|
||||
|
||||
enum x86ArgumentType
|
||||
{
|
||||
atRegDirect,
|
||||
atRegAllocStackSlot, // Stack slot designated by the register allocator
|
||||
atRegAllocStackSlotHi32, // Upper 32-bits of 64-bit stack slot designated by the register allocator
|
||||
|
||||
atRegisterIndirect, //
|
||||
atRegisterRelative1ByteDisplacement,
|
||||
atRegisterRelative4ByteDisplacement,
|
||||
|
||||
atBaseIndexed, //
|
||||
atBaseRelativeIndexed1ByteDisplacement,
|
||||
atBaseRelativeIndexed4ByteDisplacement,
|
||||
|
||||
atScaledIndexedBy2, //
|
||||
atScaledIndexed1ByteDisplacementBy2,
|
||||
atScaledIndexed4ByteDisplacementBy2,
|
||||
|
||||
atScaledIndexedBy4, //
|
||||
atScaledIndexed1ByteDisplacementBy4,
|
||||
atScaledIndexed4ByteDisplacementBy4,
|
||||
|
||||
atScaledIndexedBy8, //
|
||||
atScaledIndexed1ByteDisplacementBy8,
|
||||
atScaledIndexed4ByteDisplacementBy8,
|
||||
|
||||
atStackOffset,
|
||||
atStackOffset1ByteDisplacement,
|
||||
atStackOffset4ByteDisplacement,
|
||||
|
||||
atAbsoluteAddress,
|
||||
atImmediateOnly
|
||||
};
|
||||
|
||||
// Maps register alloc color to x86 machine register
|
||||
extern x86GPR colorTox86GPR[];
|
||||
|
||||
// Maps x86 machine register to register alloc color
|
||||
extern Uint8 x86GPRToColor[];
|
||||
|
||||
//--------------------------------------------------------------------------------
|
||||
// x86ArgumentList
|
||||
// Abstract base class
|
||||
class x86ArgumentList
|
||||
{
|
||||
protected:
|
||||
x86ArgumentList() { }
|
||||
|
||||
public:
|
||||
|
||||
virtual void alFormatToMemory(void* inStart, Uint32 inOffset, x86ArgListInstruction& inInsn, MdFormatter& /*inFormatter*/) = 0;
|
||||
virtual Uint8 alSize(x86ArgListInstruction& /*inInsn*/, MdFormatter& /*inFormatter*/) = 0;
|
||||
|
||||
virtual Uint8 alGetRegisterOperand( x86ArgListInstruction& /*inInsn*/ ) { return NOT_A_REG; }
|
||||
|
||||
virtual bool alSwitchArgumentTypeToSpill(Uint8 /*inWhichUse*/, x86ArgListInstruction& /* inInsn */ ) { return false;}
|
||||
virtual x86ImmediateSize alSizeOfImmediate() { return isNoImmediate; }
|
||||
virtual bool alIsRegisterDirect() { return true; }
|
||||
|
||||
#ifdef DEBUG
|
||||
virtual void alCheckIntegrity(x86ArgListInstruction& /*inInsn*/) { /* do nothing for now, except for double argument list */ }
|
||||
virtual void alPrintArgs(x86ArgListInstruction& /*inInsn*/) { /* do nothing for now, except for double argument list */ }
|
||||
#endif // DEBUG
|
||||
|
||||
#ifdef DEBUG_LOG
|
||||
virtual void alPrintPretty(LogModuleObject &f, x86ArgListInstruction& inInsn) = 0;
|
||||
#endif // DEBUG_LOG
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------------
|
||||
// x86EmptyArgumentList
|
||||
// Contains no information
|
||||
class x86EmptyArgumentList :
|
||||
public x86ArgumentList
|
||||
{
|
||||
public:
|
||||
x86EmptyArgumentList() {}
|
||||
virtual void alFormatToMemory(void* /*inStart*/, Uint32 /*inOffset*/, x86ArgListInstruction& /*inInsn*/, MdFormatter& /*inFormatter*/) {}
|
||||
virtual Uint8 alSize(x86ArgListInstruction& /*inInsn*/, MdFormatter& /*inFormatter*/) { return 0; }
|
||||
|
||||
#ifdef DEBUG_LOG
|
||||
public:
|
||||
virtual void alPrintPretty(LogModuleObject /*f*/, x86ArgListInstruction& /*inInsn*/) {}
|
||||
#endif // DEBUG_LOG
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------------
|
||||
// x86ControlNodeOffsetArgumentList
|
||||
class x86ControlNodeOffsetArgumentList :
|
||||
public x86ArgumentList
|
||||
{
|
||||
public:
|
||||
x86ControlNodeOffsetArgumentList( ControlNode& inTarget ) : cnoTarget(inTarget) { }
|
||||
virtual void alFormatToMemory(void* inStart, Uint32 inOffset, x86ArgListInstruction& inInsn, MdFormatter& /*inFormatter*/);
|
||||
// FIX In the future we would like short jumps to be encode with 1 byte immediates.
|
||||
virtual Uint8 alSize(x86ArgListInstruction& /*inInsn*/, MdFormatter& /*inFormatter*/) { return 4; }
|
||||
virtual x86ImmediateSize alSizeOfImmediate() { return is4ByteImmediate; }
|
||||
|
||||
protected:
|
||||
ControlNode& cnoTarget;
|
||||
|
||||
#ifdef DEBUG_LOG
|
||||
public:
|
||||
virtual void alPrintPretty(LogModuleObject &f, x86ArgListInstruction& /*inInsn*/);
|
||||
#endif
|
||||
};
|
||||
|
||||
/*================================================================================
|
||||
The following argument lists all depend on the InstructionUses and InstructionDefines to contain the Virtual Registers
|
||||
which have been assigned machine registers prior to printing or formating. Additionally, all make assumptions about the
|
||||
properties of the uses and defines which hold the VRs.
|
||||
|
||||
Let the array of uses be enumerated u0, u1, u2,...un, and the array of defines d0, d1, d2,...dm.
|
||||
|
||||
These assumptions require the following:
|
||||
|
||||
For some ux, all ui i <= x are uses which contain VRs (represent machine registers) and all uj j > x are uses which do
|
||||
not contain VRs (represent machine registers).
|
||||
|
||||
Similarly, For some dx, all ui i <= x are defines which contain VRs (represent machine registers) and all dj j > x are defines which do
|
||||
not contain VRs (represent machine registers).
|
||||
|
||||
If some register/memory operand is overwritten then it encoded in the first use(s) and the first define(s).
|
||||
|
||||
Many of the functions use a wrap around approach. They take some offset into the uses array and expect to find the virtual registers
|
||||
necessary to format themselves at that spot. If instead they find that they are beyond the end of the array of uses or that the current
|
||||
use is not a VR, then they know that they can find they VRs at the beginning of the defines.
|
||||
*/
|
||||
|
||||
//--------------------------------------------------------------------------------
|
||||
// x86SingleArgumentList
|
||||
// One register or memory operand
|
||||
class x86SingleArgumentList :
|
||||
public x86ArgumentList
|
||||
{
|
||||
public:
|
||||
x86SingleArgumentList(x86ArgumentType inType);
|
||||
x86SingleArgumentList(x86ArgumentType inType, Uint32 inDisplacement);
|
||||
|
||||
virtual void alFormatToMemory(void* inStart, Uint32 inOffset, x86ArgListInstruction& inInsn, MdFormatter& /*inFormatter*/);
|
||||
virtual Uint8 alSize(x86ArgListInstruction& inInsn, MdFormatter& /*inFormatter*/);
|
||||
virtual bool alSwitchArgumentTypeToSpill(Uint8 inWhichUse, x86ArgListInstruction& /* inInsn */);
|
||||
|
||||
virtual Uint8 alGetRegisterOperand( x86ArgListInstruction& inInsn );
|
||||
virtual bool alIsRegisterDirect() { return argType1 == atRegDirect; }
|
||||
|
||||
protected:
|
||||
x86SingleArgumentList() { }
|
||||
|
||||
// Utility functions used by all super classes of x86SingleArgumentList */
|
||||
void alFormatToMemory(void* inStart, x86ArgListInstruction& inInsn, x86ArgumentType inType, Uint8 inInstructionUseStartIndex, MdFormatter& /*inFormatter*/);
|
||||
Uint8 alSize(x86ArgumentType inType, x86ArgListInstruction& inInsn, Uint8 inInstructionUseStartIndex, MdFormatter& /*inFormatter*/);
|
||||
|
||||
x86ArgumentType argType1;
|
||||
|
||||
union
|
||||
{
|
||||
Uint8 arg1ByteDisplacement;
|
||||
Uint32 arg4ByteDisplacement;
|
||||
};
|
||||
|
||||
private:
|
||||
void alFormatStackSlotToMemory(void* inStart, x86ArgListInstruction& inInsn, Uint8 inInstructionUseStartIndex, MdFormatter& /*inFormatter*/, int offset);
|
||||
void alFormatNonRegToMemory(void* inStart, x86ArgListInstruction& inInsn, x86ArgumentType inType, MdFormatter& /*inFormatter*/);
|
||||
void alFormatRegToMemory(void* inStart, x86ArgListInstruction& inInsn, x86ArgumentType inType, Uint8 inInstructionUseStartIndex, MdFormatter& /*inFormatter*/);
|
||||
|
||||
#ifdef DEBUG_LOG
|
||||
public:
|
||||
virtual void alPrintPretty(LogModuleObject &f, x86ArgListInstruction& inInsn) { alPrintPretty(f, inInsn, argType1, 0); }
|
||||
void alPrintPretty(LogModuleObject &f, x86ArgListInstruction& inInsn, x86ArgumentType inType, Uint8 inInstructionUseStartIndex);
|
||||
#endif // DEBUG_LOG
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------------
|
||||
// x86DoubleArgumentList
|
||||
// A memory and a register operand or two register operands
|
||||
class x86DoubleArgumentList :
|
||||
public x86SingleArgumentList
|
||||
{
|
||||
protected:
|
||||
x86ArgumentType argType2;
|
||||
|
||||
public:
|
||||
x86DoubleArgumentList(x86ArgumentType inType1, x86ArgumentType inType2);
|
||||
x86DoubleArgumentList(x86ArgumentType inType1, x86ArgumentType inType2, Uint32 inDisplacement);
|
||||
|
||||
virtual void alFormatToMemory(void* inStart, Uint32 inOffset, x86ArgListInstruction& inInsn, MdFormatter& /*inFormatter*/);
|
||||
virtual Uint8 alSize(x86ArgListInstruction& inInsn, MdFormatter& /*inFormatter*/);
|
||||
|
||||
virtual Uint8 alGetRegisterOperand( x86ArgListInstruction& /*inInsn*/ ) { return NOT_A_REG; }
|
||||
|
||||
virtual bool alSwitchArgumentTypeToSpill(Uint8 inWhichUse, x86ArgListInstruction& /* inInsn */ );
|
||||
|
||||
VirtualRegister* alGetArg1VirtualRegister(x86ArgListInstruction& inInsn);
|
||||
VirtualRegister* alGetArg2VirtualRegister(x86ArgListInstruction& inInsn);
|
||||
|
||||
virtual bool alIsRegisterDirect() { return argType1 == atRegDirect && argType2 == atRegDirect; }
|
||||
bool akIsArg1Direct() { return argType1 == atRegDirect; }
|
||||
bool akIsArg2Direct() { return argType2 == atRegDirect; }
|
||||
bool alHasTwoArgs(x86ArgListInstruction& inInsn);
|
||||
|
||||
#ifdef DEBUG
|
||||
virtual void alCheckIntegrity(x86ArgListInstruction& inInsn);
|
||||
virtual void alPrintArgs(x86ArgListInstruction& inInsn);
|
||||
#endif // DEBUG
|
||||
|
||||
#ifdef DEBUG_LOG
|
||||
public:
|
||||
virtual void alPrintPretty(LogModuleObject &f, x86ArgListInstruction& inInsn);
|
||||
#endif // DEBUG_LOG
|
||||
};
|
||||
|
||||
inline Uint8 x86DoubleArgumentList::
|
||||
alSize(x86ArgListInstruction& inInsn, MdFormatter& inFormatter)
|
||||
{
|
||||
// The formatting of the mem argument determines the size of the argumentlist because one register direct can
|
||||
// just be ORed.
|
||||
int size;
|
||||
if(argType1 != atRegDirect)
|
||||
size = x86SingleArgumentList::alSize(argType1, inInsn, 0, inFormatter);
|
||||
else
|
||||
size = x86SingleArgumentList::alSize(argType2, inInsn, 1, inFormatter);
|
||||
return size;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------
|
||||
// x86ImmediateArgumentList
|
||||
// One reg or mem operand and an immediate operand.
|
||||
class x86ImmediateArgumentList :
|
||||
public x86SingleArgumentList
|
||||
{
|
||||
public:
|
||||
x86ImmediateArgumentList(x86ArgumentType inType1, Uint32 inImm);
|
||||
x86ImmediateArgumentList(x86ArgumentType inType1, Uint32 inDisplacement, Uint32 inImm);
|
||||
|
||||
virtual void alFormatToMemory(void* inStart, Uint32 inOffset, x86ArgListInstruction& inInsn, MdFormatter& /*inFormatter*/);
|
||||
virtual Uint8 alSize(x86ArgListInstruction& inInsn, MdFormatter& /*inFormatter*/);
|
||||
|
||||
virtual x86ImmediateSize alSizeOfImmediate() { return iSize; }
|
||||
|
||||
protected:
|
||||
|
||||
union
|
||||
{
|
||||
Uint8 imm1Byte;
|
||||
Uint32 imm4Bytes;
|
||||
};
|
||||
|
||||
x86ImmediateSize iSize;
|
||||
|
||||
#ifdef DEBUG_LOG
|
||||
public:
|
||||
virtual void alPrintPretty(LogModuleObject &f, x86ArgListInstruction& inInsn);
|
||||
#endif // DEBUG_LOG
|
||||
};
|
||||
|
||||
inline x86ImmediateArgumentList::
|
||||
x86ImmediateArgumentList(x86ArgumentType inType1, Uint32 inImm)
|
||||
: x86SingleArgumentList(inType1)
|
||||
{
|
||||
imm4Bytes = inImm;
|
||||
if(valueIsOneByteSigned(inImm))
|
||||
iSize = is1ByteImmediate;
|
||||
else
|
||||
iSize = is4ByteImmediate;
|
||||
}
|
||||
|
||||
inline x86ImmediateArgumentList::
|
||||
x86ImmediateArgumentList(x86ArgumentType inType1, Uint32 inDisplacement, Uint32 inImm)
|
||||
: x86SingleArgumentList(inType1, inDisplacement)
|
||||
{
|
||||
imm4Bytes = inImm;
|
||||
if(valueIsOneByteSigned(inImm))
|
||||
iSize = is1ByteImmediate;
|
||||
else
|
||||
iSize = is4ByteImmediate;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_LOG
|
||||
|
||||
inline void x86ImmediateArgumentList::
|
||||
alPrintPretty(LogModuleObject &f, x86ArgListInstruction& inInsn)
|
||||
{
|
||||
x86SingleArgumentList::alPrintPretty(f, inInsn, argType1, 0 );
|
||||
if (argType1 != atImmediateOnly)
|
||||
UT_OBJECTLOG(f, PR_LOG_ALWAYS, (", "));
|
||||
if(iSize == is1ByteImmediate)
|
||||
UT_OBJECTLOG(f, PR_LOG_ALWAYS, ("%0x", imm1Byte));
|
||||
else
|
||||
UT_OBJECTLOG(f, PR_LOG_ALWAYS, ("%0x", imm4Bytes));
|
||||
}
|
||||
|
||||
#endif // DEBUG_LOG
|
||||
|
||||
//--------------------------------------------------------------------------------
|
||||
// x86CondensableImmediateArgumentList
|
||||
class x86CondensableImmediateArgumentList :
|
||||
public x86SingleArgumentList
|
||||
{
|
||||
public:
|
||||
x86CondensableImmediateArgumentList(x86ArgumentType inType1, Uint32 inImm);
|
||||
x86CondensableImmediateArgumentList(x86ArgumentType inType1, Uint32 inDisplacement, Uint32 inImm);
|
||||
|
||||
virtual void alFormatToMemory(void* inStart, Uint32 inOffset, x86ArgListInstruction& inInsn, MdFormatter& /*inFormatter*/);
|
||||
virtual Uint8 alSize(x86ArgListInstruction& inInsn, MdFormatter& /*inFormatter*/);
|
||||
|
||||
virtual x86ImmediateSize alSizeOfImmediate() { return iSize; }
|
||||
|
||||
protected:
|
||||
Uint32 imm4Bytes;
|
||||
x86ImmediateSize iSize;
|
||||
|
||||
#ifdef DEBUG_LOG
|
||||
public:
|
||||
virtual void alPrintPretty(LogModuleObject &f, x86ArgListInstruction& inInsn);
|
||||
#endif // DEBUG_LOG
|
||||
};
|
||||
|
||||
inline x86CondensableImmediateArgumentList::
|
||||
x86CondensableImmediateArgumentList(x86ArgumentType inType1, Uint32 inImm)
|
||||
: x86SingleArgumentList(inType1), imm4Bytes(inImm)
|
||||
{
|
||||
}
|
||||
|
||||
inline x86CondensableImmediateArgumentList::
|
||||
x86CondensableImmediateArgumentList(x86ArgumentType inType1, Uint32 inDisplacement, Uint32 inImm)
|
||||
: x86SingleArgumentList(inType1, inDisplacement), imm4Bytes(inImm)
|
||||
{
|
||||
}
|
||||
|
||||
#ifdef DEBUG_LOG
|
||||
|
||||
inline void x86CondensableImmediateArgumentList::
|
||||
alPrintPretty(LogModuleObject &f, x86ArgListInstruction& inInsn)
|
||||
{
|
||||
x86SingleArgumentList::alPrintPretty(f, inInsn, argType1, 0 );
|
||||
UT_OBJECTLOG(f, PR_LOG_ALWAYS, (", %0x", imm4Bytes));
|
||||
}
|
||||
|
||||
#endif // DEBUG_LOG
|
||||
|
||||
//--------------------------------------------------------------------------------
|
||||
// x86DoubleImmediateArgumentList
|
||||
// [ immediate argument, mem, and reg] or [immediate argument, two reg operands]
|
||||
class x86DoubleImmediateArgumentList :
|
||||
public x86DoubleArgumentList
|
||||
{
|
||||
public:
|
||||
x86DoubleImmediateArgumentList(x86ArgumentType inType1, x86ArgumentType inType2, Uint32 inImm);
|
||||
x86DoubleImmediateArgumentList(x86ArgumentType inType1, x86ArgumentType inType2, Uint32 inDisplacement, Uint32 inImm);
|
||||
virtual void alFormatToMemory(void* inStart, Uint32 inOffset, x86ArgListInstruction& inInsn, MdFormatter& /*inFormatter*/);
|
||||
virtual Uint8 alSize(x86ArgListInstruction& /*inInsn*/, MdFormatter& /*inFormatter*/);
|
||||
|
||||
virtual x86ImmediateSize alSizeOfImmediate() { return iSize; }
|
||||
|
||||
protected:
|
||||
|
||||
union
|
||||
{
|
||||
Uint8 imm1Byte;
|
||||
Uint32 imm4Bytes;
|
||||
};
|
||||
|
||||
x86ImmediateSize iSize;
|
||||
|
||||
#ifdef DEBUG_LOG
|
||||
public:
|
||||
virtual void alPrintPretty(LogModuleObject &f, x86ArgListInstruction& inInsn);
|
||||
#endif // DEBUG_LOG
|
||||
};
|
||||
|
||||
inline x86DoubleImmediateArgumentList::
|
||||
x86DoubleImmediateArgumentList(x86ArgumentType inType1, x86ArgumentType inType2, Uint32 inImm)
|
||||
: x86DoubleArgumentList(inType1, inType2)
|
||||
{
|
||||
imm4Bytes = inImm;
|
||||
// if(valueIsOneByteSigned(inImm))
|
||||
// iSize = is1ByteImmediate;
|
||||
// else
|
||||
iSize = is4ByteImmediate;
|
||||
}
|
||||
|
||||
inline x86DoubleImmediateArgumentList::
|
||||
x86DoubleImmediateArgumentList(x86ArgumentType inType1, x86ArgumentType inType2, Uint32 inDisplacement, Uint32 inImm)
|
||||
: x86DoubleArgumentList(inType1, inType2, inDisplacement)
|
||||
{
|
||||
imm4Bytes = inImm;
|
||||
// if(valueIsOneByteSigned(inImm)) // FIX
|
||||
// iSize = is1ByteImmediate;
|
||||
// else
|
||||
iSize = is4ByteImmediate;
|
||||
}
|
||||
|
||||
inline Uint8 x86DoubleImmediateArgumentList::
|
||||
alSize(x86ArgListInstruction& inInsn, MdFormatter& inFormatter)
|
||||
{
|
||||
Uint32 size = x86DoubleArgumentList::alSize(inInsn, inFormatter);
|
||||
|
||||
if(iSize == is1ByteImmediate)
|
||||
size += 1;
|
||||
else
|
||||
size += 4;
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
inline void x86DoubleImmediateArgumentList::
|
||||
alFormatToMemory(void* inStart, Uint32 inOffset, x86ArgListInstruction& inInsn, MdFormatter& inFormatter)
|
||||
{
|
||||
x86DoubleArgumentList::alFormatToMemory(inStart, inOffset, inInsn, inFormatter);
|
||||
|
||||
Uint8* immLocation = (Uint8*)inStart + x86DoubleArgumentList::alSize(inInsn, inFormatter);
|
||||
if(iSize == is1ByteImmediate)
|
||||
*immLocation = imm1Byte;
|
||||
else
|
||||
writeLittleWordUnaligned((void*)immLocation, imm4Bytes);
|
||||
}
|
||||
|
||||
#ifdef DEBUG_LOG
|
||||
|
||||
inline void x86DoubleImmediateArgumentList::
|
||||
alPrintPretty(LogModuleObject &f, x86ArgListInstruction& inInsn)
|
||||
{
|
||||
x86DoubleArgumentList::alPrintPretty(f, inInsn);
|
||||
if(iSize == is1ByteImmediate)
|
||||
UT_OBJECTLOG(f, PR_LOG_ALWAYS, (", %0x", imm1Byte));
|
||||
else
|
||||
UT_OBJECTLOG(f, PR_LOG_ALWAYS, (", %0x", imm4Bytes));
|
||||
}
|
||||
|
||||
#endif // DEBUG_LOG
|
||||
|
||||
//--------------------------------------------------------------------------------
|
||||
// x86SpecialRegMemArgumentList
|
||||
// Only used with x86SpecialRegMemOpcode
|
||||
// This argument list holds an immediate value and a single argument. In its initial form it emits a double argument list
|
||||
// of the one register operand. If it is switched to spill type it prints the memory operand and the immediate value.
|
||||
|
||||
class x86SpecialRegMemArgumentList :
|
||||
public x86ImmediateArgumentList
|
||||
{
|
||||
public:
|
||||
x86SpecialRegMemArgumentList(Uint32 inImm) : x86ImmediateArgumentList(atRegDirect, inImm) { }
|
||||
|
||||
virtual void alFormatToMemory(void* inStart, Uint32 inOffset, x86ArgListInstruction& inInsn, MdFormatter& /*inFormatter*/);
|
||||
virtual Uint8 alSize(x86ArgListInstruction& inInsn, MdFormatter& /*inFormatter*/);
|
||||
|
||||
#ifdef DEBUG_LOG
|
||||
public:
|
||||
virtual void alPrintPretty(LogModuleObject &f, x86ArgListInstruction& inInsn);
|
||||
#endif // DEBUG_LOG
|
||||
};
|
||||
|
||||
inline Uint8 x86SpecialRegMemArgumentList::
|
||||
alSize(x86ArgListInstruction& inInsn, MdFormatter& inFormatter)
|
||||
{
|
||||
Uint8 size;
|
||||
if(argType1 == atRegDirect)
|
||||
size = 1; // Only need the ModR/M byte
|
||||
else
|
||||
size = x86ImmediateArgumentList::alSize(inInsn, inFormatter);
|
||||
return size;
|
||||
}
|
||||
|
||||
#endif //X86_ARGUMENTLIST
|
|
@ -0,0 +1,732 @@
|
|||
/* -*- 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.
|
||||
*/
|
||||
//
|
||||
// x86Float.cpp - Floating-point code-generation for x86 processors
|
||||
//
|
||||
|
||||
#include "x86Float.h"
|
||||
#include "x86Win32Emitter.h"
|
||||
#include "FloatUtils.h"
|
||||
|
||||
// Note: In comments below, TOS = Top of FPU stack
|
||||
|
||||
//====================================================================================================
|
||||
// Register classes for float and double operands
|
||||
// FIXME - For now, all float and double virtual registers are allocated on the stack
|
||||
const VRClass vrcFloat = vrcStackSlot;
|
||||
const VRClass vrcDouble = vrcStackSlot; // FIXME - Stack slots are temporarily 8-bytes, so they can hold a double
|
||||
|
||||
|
||||
//====================================================================================================
|
||||
// Floating-point instruction classes
|
||||
|
||||
/*--------------------------------------------------------------------------------
|
||||
Floating-Point instructions where one operand is a memory location and the other is
|
||||
the top of stack, e.g. binary operations like add, and load/store/conversion instructions.
|
||||
|
||||
oiBaseOpcode
|
||||
contains the opcode for the instruction
|
||||
|
||||
oiOpcodeInformation
|
||||
6 set if opcode extension is used
|
||||
[5 4 3] contain Regfield opcode extension (if bit 6 is set)
|
||||
others set to 0
|
||||
*/
|
||||
|
||||
// Utility macros for setting up opcode table
|
||||
#define NO_EXTENSION 8 // Register extensions range from 0 to 7, so this special code indicates that
|
||||
// there is no R/M byte
|
||||
|
||||
#define FLOAT_INFO(first_opcode, reg_extension) \
|
||||
first_opcode, ((reg_extension != NO_EXTENSION) * ((1<< 6) | ((reg_extension) << 3)))
|
||||
|
||||
x86OpcodeInfo InsnFloatMemory::opcodeTable[] =
|
||||
{
|
||||
{FLOAT_INFO(0xD9, 3), "fstp32"}, // TOS => 32-bit float memory. Pop TOS.
|
||||
{FLOAT_INFO(0xDD, 3), "fstp64"}, // TOS => 64-bit double memory. Pop TOS.
|
||||
{FLOAT_INFO(0xD9, 2), "fst32"}, // TOS => 32-bit float memory. (Don't pop FPU stack.)
|
||||
{FLOAT_INFO(0xDD, 2), "fst64"}, // TOS => 64-bit double memory. (Don't pop FPU stack.)
|
||||
{FLOAT_INFO(0xDB, 3), "fistp32"}, // Round(TOS) => 32-bit int memory. Pop TOS.
|
||||
{FLOAT_INFO(0xDF, 7), "fistp64"}, // Round(TOS) => 64-bit long memory. Pop TOS.
|
||||
{FLOAT_INFO(0xD9, 0), "fld32"}, // 32-bit float memory => Push on FPU stack
|
||||
{FLOAT_INFO(0xDD, 0), "fld64"}, // 64-bit float memory => Push on FPU stack
|
||||
{FLOAT_INFO(0xDB, 0), "fild32"}, // 32-bit int memory => convert to FP and push on FPU stack
|
||||
{FLOAT_INFO(0xDF, 5), "fild64"}, // 64-bit long memory => convert to FP and push on FPU stack
|
||||
{FLOAT_INFO(0xD8, 0), "fadd32"}, // Add TOS and 32-bit float memory => replace TOS
|
||||
{FLOAT_INFO(0xDC, 0), "fadd64"}, // Add TOS and 64-bit double memory => replace TOS
|
||||
{FLOAT_INFO(0xD8, 1), "fmul32"}, // Multiply TOS and 32-bit float memory => replace TOS
|
||||
{FLOAT_INFO(0xDC, 1), "fmul64"}, // Multiply TOS and 64-bit double memory => replace TOS
|
||||
{FLOAT_INFO(0xD8, 4), "fsub32"}, // Subtract TOS from 32-bit float memory => replace TOS
|
||||
{FLOAT_INFO(0xDC, 4), "fsub64"}, // Subtract TOS from 64-bit double memory => replace TOS
|
||||
{FLOAT_INFO(0xD8, 5), "fsubr32"}, // Subtract 32-bit float memory from TOS => replace TOS
|
||||
{FLOAT_INFO(0xDC, 5), "fsubr64"}, // Subtract 64-bit double memory from TOS => replace TOS
|
||||
{FLOAT_INFO(0xD8, 6), "fdiv32"}, // Divide TOS by 32-bit float memory => replace TOS
|
||||
{FLOAT_INFO(0xDC, 6), "fdiv64"}, // Divide TOS by 64-bit double memory => replace TOS
|
||||
{FLOAT_INFO(0xD8, 3), "fcomp32"}, // Compare TOS to 32-bit float memory, setting FPU flags, pop TOS
|
||||
{FLOAT_INFO(0xDC, 3), "fcomp64"} // Compare TOS to 64-bit double memory, setting FPU flags, pop TOS
|
||||
};
|
||||
|
||||
void InsnFloatMemory::
|
||||
formatToMemory(void* inStart, Uint32 inOffset, MdFormatter& inFormatter)
|
||||
{
|
||||
assert(opcodeInfo != NULL && iArgumentList != NULL);
|
||||
Uint8 *opLocation = (Uint8*)inStart;
|
||||
|
||||
// Format the opcode to memory
|
||||
*opLocation++ = opcodeInfo->oiBaseOpcode;
|
||||
|
||||
// Find the location of the argument list and format it to memory
|
||||
iArgumentList->alFormatToMemory((void*)opLocation, inOffset, *this, inFormatter);
|
||||
|
||||
// If the opcode has an opcode extension then OR it into the proper place. ( the reg field of the modr/m byte.)
|
||||
Uint8 regFieldExtension = kRegfield_Mask & opcodeInfo->oiOpcodeInformation;
|
||||
*opLocation |= regFieldExtension;
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------------------------------------
|
||||
Floating-Point instructions where source and destination are implicitly on the FPU stack
|
||||
e.g. negation, comparison
|
||||
|
||||
oiBaseOpcode
|
||||
contains the first byte of the opcode for the instruction
|
||||
|
||||
oiOpcodeInformation
|
||||
contains the second byte of the opcode for the instruction
|
||||
*/
|
||||
|
||||
x86OpcodeInfo InsnFloatReg::opcodeTable[] =
|
||||
{
|
||||
{0xDF, 0xE9, "fucomip st, st(1)"}, // Compare top two operands on FPU stack and set EFLAGS, pop FPU stack
|
||||
{0xD9, 0xE0, "fchs"}, // Negate top of stack value
|
||||
{0xDF, 0xE0, "fnstsw ax"} // Copy FPU status register to AX
|
||||
};
|
||||
|
||||
void InsnFloatReg::
|
||||
formatToMemory(void* inStart, Uint32 /*inOffset*/, MdFormatter& /*inFormatter*/)
|
||||
{
|
||||
// Format the opcode to memory. There is no argument list.
|
||||
Uint8* start = (Uint8*) inStart;
|
||||
*start++ = opcodeInfo->oiBaseOpcode;
|
||||
*start = opcodeInfo->oiOpcodeInformation;
|
||||
}
|
||||
|
||||
//====================================================================================================
|
||||
// Instruction generation utilities
|
||||
|
||||
InsnDoubleOpDir& x86Win32Emitter::
|
||||
copyFromFloatToIntegerRegister(DataNode& inDataNode, InsnUseXDefineYFromPool& defInsn)
|
||||
{
|
||||
VirtualRegister& vr = defineTemporary(defInsn, 0, vrcStackSlot);
|
||||
InsnDoubleOpDir& copyInsn = *new(mPool) InsnDoubleOpDir(&inDataNode, mPool, raCopyI, atRegAllocStackSlot, atRegDirect, 1, 1);
|
||||
useTemporaryVR(copyInsn, vr, 0);
|
||||
return copyInsn;
|
||||
}
|
||||
|
||||
InsnDoubleOpDir& x86Win32Emitter::
|
||||
copyFromIntegerRegisterToFloat(DataNode& inDataNode, InsnUseXDefineYFromPool& defInsn)
|
||||
{
|
||||
VirtualRegister& vr = defineTemporary(defInsn, 0);
|
||||
InsnDoubleOpDir& copyInsn = *new(mPool) InsnDoubleOpDir(&inDataNode, mPool, raCopyI, atRegDirect, atRegAllocStackSlot, 1, 1);
|
||||
useTemporaryVR(copyInsn, vr, 0);
|
||||
return copyInsn;
|
||||
}
|
||||
|
||||
//====================================================================================================
|
||||
// Floating-point binary operations, i.e. add, subtract, multiply, divide, modulus
|
||||
|
||||
void x86Win32Emitter::
|
||||
emit_BinaryFloat(Primitive& inPrimitive,
|
||||
x86FloatMemoryType binary_op, x86FloatMemoryType load_op, x86FloatMemoryType store_op,
|
||||
VRClass vrClass)
|
||||
{
|
||||
// Fetch first operand of binary op from memory and push it on the FPU stack
|
||||
InsnFloatMemory &loadInsn = *new InsnFloatMemory(&inPrimitive, mPool, load_op, 1, 1);
|
||||
useProducer(inPrimitive.nthInputVariable(0), loadInsn, 0, vrClass);
|
||||
InstructionDefine& define1 = defineTemporaryOrder(loadInsn, 0);
|
||||
|
||||
// Fetch second operand and perform binary operation, result replaces top of FPU stack
|
||||
InsnFloatMemory &binaryInsn = *new InsnFloatMemory(&inPrimitive, mPool, binary_op, 2, 1);
|
||||
useProducer(inPrimitive.nthInputVariable(1), binaryInsn, 0, vrClass);
|
||||
useTemporaryOrder(binaryInsn, define1, 1);
|
||||
InstructionDefine& define2 = defineTemporaryOrder(binaryInsn, 0);
|
||||
|
||||
// Pop result of binary operation from FPU stack and store into memory
|
||||
InsnFloatMemory &storeInsn = *new InsnFloatMemory(&inPrimitive, mPool, store_op, 1, 1);
|
||||
useTemporaryOrder(storeInsn, define2, 0);
|
||||
defineProducer(inPrimitive, storeInsn, 0, vrClass); // result
|
||||
}
|
||||
|
||||
// Emit 32-bit float binary operation
|
||||
void x86Win32Emitter::
|
||||
emit_BinaryFloat32(Primitive& inPrimitive, x86FloatMemoryType binary_op)
|
||||
{
|
||||
emit_BinaryFloat(inPrimitive, binary_op, fld32, fstp32, vrcFloat);
|
||||
}
|
||||
|
||||
// Emit 64-bit float binary operation
|
||||
void x86Win32Emitter::
|
||||
emit_BinaryFloat64(Primitive& inPrimitive, x86FloatMemoryType binary_op)
|
||||
{
|
||||
emit_BinaryFloat(inPrimitive, binary_op, fld64, fstp64, vrcDouble);
|
||||
}
|
||||
|
||||
void x86Win32Emitter::
|
||||
emit_FAdd_F(Primitive& inPrimitive) {
|
||||
emit_BinaryFloat32(inPrimitive, fadd32);
|
||||
}
|
||||
|
||||
void x86Win32Emitter::
|
||||
emit_FAdd_D(Primitive& inPrimitive) {
|
||||
emit_BinaryFloat64(inPrimitive, fadd64);
|
||||
}
|
||||
|
||||
void x86Win32Emitter::
|
||||
emit_FMul_F(Primitive& inPrimitive) {
|
||||
emit_BinaryFloat32(inPrimitive, fmul32);
|
||||
}
|
||||
|
||||
void x86Win32Emitter::
|
||||
emit_FMul_D(Primitive& inPrimitive) {
|
||||
emit_BinaryFloat64(inPrimitive, fmul64);
|
||||
}
|
||||
|
||||
void x86Win32Emitter::
|
||||
emit_FSub_F(Primitive& inPrimitive) {
|
||||
emit_BinaryFloat32(inPrimitive, fsub32);
|
||||
}
|
||||
|
||||
void x86Win32Emitter::
|
||||
emit_FSub_D(Primitive& inPrimitive) {
|
||||
emit_BinaryFloat64(inPrimitive, fsub64);
|
||||
}
|
||||
|
||||
void x86Win32Emitter::
|
||||
emit_FDiv_F(Primitive& inPrimitive) {
|
||||
emit_BinaryFloat32(inPrimitive, fdiv32);
|
||||
}
|
||||
|
||||
void x86Win32Emitter::
|
||||
emit_FDiv_D(Primitive& inPrimitive) {
|
||||
emit_BinaryFloat64(inPrimitive, fdiv64);
|
||||
}
|
||||
|
||||
// FIXME - Modulus is wrapper around fmod function. Should be changed to inline code.
|
||||
void x86Win32Emitter::
|
||||
emit_FRem_D(Primitive& inPrimitive)
|
||||
{
|
||||
new(mPool) CallS_C(&inPrimitive, mPool, 2, true, *this, (void (*)(void))&javaFMod);
|
||||
}
|
||||
|
||||
// Wrapper around fmod() for 32-bit float operands instead of double operands
|
||||
static Flt32 fmod32(Flt32 a, Flt32 b)
|
||||
{
|
||||
return (Flt32)javaFMod(a, b);
|
||||
}
|
||||
|
||||
void x86Win32Emitter::
|
||||
emit_FRem_F(Primitive& inPrimitive)
|
||||
{
|
||||
new(mPool) CallS_C(&inPrimitive, mPool, 2, true, *this, (void (*)(void))&fmod32);
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------------------------------------
|
||||
// Conversions to/from floating-point
|
||||
//
|
||||
// All conversions are two steps:
|
||||
// 1) Load input operand onto FPU stack from memory, with possible conversion to floating-point type
|
||||
// 2) Simultaneously convert and store from top of FPU stack into memory location, with possible
|
||||
// conversion to integer type.
|
||||
|
||||
void x86Win32Emitter::
|
||||
emit_FConv(Primitive& inPrimitive)
|
||||
{
|
||||
InsnFloatMemory *loadInsn;
|
||||
|
||||
// Fetch input operand from memory and push it on the FPU stack
|
||||
switch (inPrimitive.nthInputVariable(0).getKind()) {
|
||||
case vkFloat:
|
||||
loadInsn = new InsnFloatMemory(&inPrimitive, mPool, fld32, 1, 1);
|
||||
useProducer(inPrimitive.nthInputVariable(0), *loadInsn, 0, vrcFloat);
|
||||
break;
|
||||
|
||||
case vkDouble:
|
||||
loadInsn = new InsnFloatMemory(&inPrimitive, mPool, fld64, 1, 1);
|
||||
useProducer(inPrimitive.nthInputVariable(0), *loadInsn, 0, vrcDouble);
|
||||
break;
|
||||
|
||||
case vkInt:
|
||||
{
|
||||
InsnDoubleOpDir& copyInsn = *new(mPool) InsnDoubleOpDir(&inPrimitive, mPool, raCopyI, atRegDirect, atRegAllocStackSlot, 1, 1);
|
||||
useProducer(inPrimitive.nthInputVariable(0), copyInsn, 0);
|
||||
VirtualRegister& tmp = defineTemporary(copyInsn, 0, vrcStackSlot);
|
||||
|
||||
loadInsn = new InsnFloatMemory(&inPrimitive, mPool, fild32, 1, 1);
|
||||
useTemporaryVR(*loadInsn, tmp, 0, vrcStackSlot);
|
||||
}
|
||||
break;
|
||||
|
||||
case vkLong:
|
||||
{
|
||||
InsnDoubleOpDir& copyInsnHi = *new(mPool) InsnDoubleOpDir(&inPrimitive, mPool, raCopyI, atRegDirect, atRegAllocStackSlotHi32, 1, 1);
|
||||
useProducer(inPrimitive.nthInputVariable(0), copyInsnHi, 0, vrcInteger, vidHigh);
|
||||
VirtualRegister& tmp64 = defineTemporary(copyInsnHi, 0, vrcStackSlot);
|
||||
|
||||
InsnDoubleOpDir& copyInsnLo = *new(mPool) InsnDoubleOpDir(&inPrimitive, mPool, raCopyI, atRegDirect, atRegAllocStackSlot, 2, 1);
|
||||
useProducer(inPrimitive.nthInputVariable(0), copyInsnLo, 0, vrcInteger, vidLow);
|
||||
useTemporaryVR(copyInsnLo, tmp64, 1);
|
||||
InstructionDefine& orderStoreLoad = defineTemporaryOrder(copyInsnLo, 0);
|
||||
|
||||
loadInsn = new InsnFloatMemory(&inPrimitive, mPool, fild64, 2, 1);
|
||||
useTemporaryVR(*loadInsn, tmp64, 0, vrcStackSlot);
|
||||
useTemporaryOrder(*loadInsn, orderStoreLoad, 1);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// Absence of default case generates gcc warnings.
|
||||
break;
|
||||
}
|
||||
|
||||
InstructionDefine& order = defineTemporaryOrder(*loadInsn, 0);
|
||||
|
||||
// Store value from top of FPU stack into memory
|
||||
ValueKind vk = inPrimitive.getKind();
|
||||
switch (vk) {
|
||||
case vkFloat:
|
||||
{
|
||||
// Pop result from FPU stack and store into memory as 32-bit float
|
||||
InsnFloatMemory& storeInsn = *new InsnFloatMemory(&inPrimitive, mPool, fstp32, 1, 1);
|
||||
useTemporaryOrder(storeInsn, order, 0);
|
||||
defineProducer(inPrimitive, storeInsn, 0, vrcFloat);
|
||||
}
|
||||
break;
|
||||
|
||||
case vkDouble:
|
||||
{
|
||||
// Pop result from FPU stack and store into memory as 64-bit double
|
||||
InsnFloatMemory& storeInsn = *new InsnFloatMemory(&inPrimitive, mPool, fstp64, 1, 1);
|
||||
useTemporaryOrder(storeInsn, order, 0);
|
||||
defineProducer(inPrimitive, storeInsn, 0, vrcDouble);
|
||||
}
|
||||
break;
|
||||
|
||||
case vkInt:
|
||||
case vkLong:
|
||||
{
|
||||
/* Rounding is controlled by the RC flag in the FPU. Round-to-nearest is the desired rounding mode for
|
||||
all floating-point instructions *except* conversions from floating-point types to integer types, in
|
||||
which case round-towards-zero (truncation) is mandated. Rather than temporarily changing the RC flag
|
||||
for all such conversions, we achieve the equivalent result by subtracting or adding 0.5 to the value
|
||||
before rounding, i.e.
|
||||
|
||||
truncate(x) <==> round(x + sign(x) * 0.5)
|
||||
|
||||
FIXME - we still don't handle out-of-range inputs and NaNs per the Java spec.
|
||||
|
||||
*/
|
||||
|
||||
// Store the 32-bit representation of the floating-point input operand into memory so that we can extract its sign.
|
||||
InsnFloatMemory& storeInsn1 = *new InsnFloatMemory(&inPrimitive, mPool, fst32, 1, 1);
|
||||
useTemporaryOrder(storeInsn1, order, 0);
|
||||
VirtualRegister& tmpVR1 = defineTemporary(storeInsn1, 0, vrcStackSlot);
|
||||
|
||||
// Extract the sign bit of the input operand
|
||||
x86Instruction& andInsn = *new(mPool) x86Instruction(&inPrimitive, mPool, iaAndImm, 0x80000000, atRegAllocStackSlot, 1, 1);
|
||||
useTemporaryVR(andInsn, tmpVR1, 0, vrcStackSlot);
|
||||
redefineTemporary(andInsn, tmpVR1, 0, vrcStackSlot);
|
||||
|
||||
// Generate 0.5 * sign(input)
|
||||
const float half = 0.5;
|
||||
x86Instruction& orInsn = *new(mPool) x86Instruction(&inPrimitive, mPool, iaOrImm, *(Uint32*)&half, atRegAllocStackSlot, 1, 1);
|
||||
useTemporaryVR(orInsn, tmpVR1, 0, vrcStackSlot);
|
||||
redefineTemporary(orInsn, tmpVR1, 0, vrcStackSlot);
|
||||
|
||||
// Subtract 0.5 * sign(input) from input operand
|
||||
InsnFloatMemory& subInsn = *new InsnFloatMemory(&inPrimitive, mPool, fsub32, 1, 1);
|
||||
useTemporaryVR(subInsn, tmpVR1, 0, vrcStackSlot);
|
||||
redefineTemporaryOrder(subInsn, order, 0);
|
||||
|
||||
if (vk == vkInt) {
|
||||
// Pop result from FPU stack, convert to 32-bit integer, and store into memory
|
||||
|
||||
InsnFloatMemory& storeInsn = *new InsnFloatMemory(&inPrimitive, mPool, fistp32, 1, 1);
|
||||
useTemporaryOrder(storeInsn, order, 0);
|
||||
|
||||
// All transfers from the FPU must go through memory, so make a copy from the memory location
|
||||
// to the integer register destination.
|
||||
InsnDoubleOpDir& copyInsn = copyFromFloatToIntegerRegister(inPrimitive, storeInsn);
|
||||
defineProducer(inPrimitive, copyInsn, 0);
|
||||
|
||||
} else { // vkLong
|
||||
// Pop result from FPU stack, convert to 64-bit integer, and store into memory
|
||||
InsnFloatMemory& storeInsn = *new InsnFloatMemory(&inPrimitive, mPool, fistp64, 1, 1);
|
||||
useTemporaryOrder(storeInsn, order, 0);
|
||||
VirtualRegister& tmp64 = defineTemporary(storeInsn, 0, vrcStackSlot); // 64-bit integer stack slot
|
||||
|
||||
// Copy high 32-bits from memory to integer register
|
||||
InsnDoubleOpDir& copyInsnHi = *new(mPool) InsnDoubleOpDir(&inPrimitive, mPool, raCopyI, atRegAllocStackSlotHi32, atRegDirect, 1, 1);
|
||||
useTemporaryVR(copyInsnHi, tmp64, 0, vrcStackSlot);
|
||||
defineProducer(inPrimitive, copyInsnHi, 0, vrcInteger, vidHigh);
|
||||
|
||||
// Copy low 32-bits from memory to integer register
|
||||
InsnDoubleOpDir& copyInsnLo = *new(mPool) InsnDoubleOpDir(&inPrimitive, mPool, raCopyI, atRegAllocStackSlot, atRegDirect, 1, 1);
|
||||
useTemporaryVR(copyInsnLo, tmp64, 0, vrcStackSlot);
|
||||
defineProducer(inPrimitive, copyInsnLo, 0, vrcInteger, vidLow);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
// Absence of default case generates gcc warnings.
|
||||
assert(0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//====================================================================================================
|
||||
// Floating-point function-call glue
|
||||
|
||||
// Obtain the 32-bit float return value of a function call
|
||||
void x86Win32Emitter::
|
||||
emit_CallReturnF(InsnUseXDefineYFromPool& callInsn, DataNode& callPrimitive, DataNode& returnValProducer)
|
||||
{
|
||||
InstructionDefine& define = defineTemporaryOrder(callInsn, 1);
|
||||
|
||||
// Pop result from FPU stack and store into memory as 32-bit float
|
||||
InsnFloatMemory& storeInsn = *new InsnFloatMemory(&callPrimitive, mPool, fstp32, 1, 1);
|
||||
useTemporaryOrder(storeInsn, define, 0);
|
||||
defineProducer(returnValProducer, storeInsn, 0, vrcFloat);
|
||||
}
|
||||
|
||||
// Obtain the 64-bit double return value of a function call
|
||||
void x86Win32Emitter::
|
||||
emit_CallReturnD(InsnUseXDefineYFromPool& callInsn, DataNode& callPrimitive, DataNode& returnValProducer)
|
||||
{
|
||||
InstructionDefine& define = defineTemporaryOrder(callInsn, 1);
|
||||
|
||||
// Pop result from FPU stack and store into memory as 64-bit double
|
||||
InsnFloatMemory& storeInsn = *new InsnFloatMemory(&callPrimitive, mPool, fstp64, 1, 1);
|
||||
useTemporaryOrder(storeInsn, define, 0);
|
||||
defineProducer(returnValProducer, storeInsn, 0, vrcDouble);
|
||||
}
|
||||
|
||||
// Retrieve a 32-bit float argument from the call stack
|
||||
void x86Win32Emitter::
|
||||
emit_ArgF(PrimArg& arg, InstructionDefine& order, int curStackOffset)
|
||||
{
|
||||
InsnDoubleOpDir& loadParam = *new(mPool) InsnDoubleOpDir(&arg, mPool, raLoadI, curStackOffset, atStackOffset, atRegDirect, 1, 1);
|
||||
useTemporaryOrder(loadParam, order, 0);
|
||||
InsnDoubleOpDir& copyInsn = copyFromIntegerRegisterToFloat(arg, loadParam);
|
||||
defineProducer(arg, copyInsn, 0, vrcFloat);
|
||||
}
|
||||
|
||||
// Retrieve a 64-bit double argument from the call stack
|
||||
void x86Win32Emitter::
|
||||
emit_ArgD(PrimArg& arg, InstructionDefine& order, int curStackOffset)
|
||||
{
|
||||
InsnFloatMemory& loadInsn = *new InsnFloatMemory(&arg, mPool, fld64, atStackOffset, curStackOffset, 1, 1);
|
||||
useTemporaryOrder(loadInsn, order, 0);
|
||||
redefineTemporaryOrder(loadInsn, order, 0);
|
||||
|
||||
InsnFloatMemory& copyInsn = *new InsnFloatMemory(&arg, mPool, fstp64, 1, 1);
|
||||
useTemporaryOrder(copyInsn, order, 0);
|
||||
defineProducer(arg, copyInsn, 0, vrcDouble);
|
||||
}
|
||||
|
||||
// Push float function return value on top of FPU stack
|
||||
void x86Win32Emitter::
|
||||
emit_Result_F(Primitive& inPrimitive)
|
||||
{
|
||||
InsnFloatMemory ©Insn = *new InsnFloatMemory(&inPrimitive, mPool, fld32, 1, 1);
|
||||
InsnExternalUse& extInsn = *new(mPool) InsnExternalUse(&inPrimitive, mPool, 1);
|
||||
|
||||
useProducer(inPrimitive.nthInputVariable(0), copyInsn, 0, vrcFloat);
|
||||
InstructionDefine& define = defineTemporaryOrder(copyInsn, 0);
|
||||
useTemporaryOrder(extInsn, define, 0);
|
||||
|
||||
inPrimitive.setInstructionRoot(&extInsn);
|
||||
}
|
||||
|
||||
// Push double function return value on top of FPU stack
|
||||
void x86Win32Emitter::
|
||||
emit_Result_D(Primitive& inPrimitive)
|
||||
{
|
||||
InsnFloatMemory ©Insn = *new InsnFloatMemory(&inPrimitive, mPool, fld64, 1, 1);
|
||||
InsnExternalUse& extInsn = *new(mPool) InsnExternalUse(&inPrimitive, mPool, 1);
|
||||
|
||||
useProducer(inPrimitive.nthInputVariable(0), copyInsn, 0, vrcDouble);
|
||||
InstructionDefine& define = defineTemporaryOrder(copyInsn, 0);
|
||||
useTemporaryOrder(extInsn, define, 0);
|
||||
|
||||
inPrimitive.setInstructionRoot(&extInsn);
|
||||
}
|
||||
|
||||
//====================================================================================================
|
||||
// Comparisons
|
||||
|
||||
// Matches pattern: poCatL_I(poFCmp_F(Vfloat, Vfloat))
|
||||
/* Emits code that follows this pattern:
|
||||
|
||||
fld ; push second_arg on FPU stack
|
||||
fcomp [ebp + xx] ; Load first_arg, set integer condition flags and pop all args
|
||||
fnstsw ax ; Copy FPU status reg into AX
|
||||
sahf ; Copy AX into EFLAGS status reg
|
||||
seta al ; al = (first_arg > second_arg) ? 1 : 0;
|
||||
setb bl ; bl = ((first_arg < second_arg) || (first_arg == NAN) || (second_arg == NAN)) ? 1 : 0
|
||||
sub ebx, eax ; Result in lowest byte is -1, 0, or +1
|
||||
movsx eax, bl ; Sign-extend low byte to 32-bits
|
||||
|
||||
(Some changes in operand usage will appear depending on the exact pattern of primitives being matched.)
|
||||
*/
|
||||
|
||||
void x86Win32Emitter::
|
||||
emit_3wayCmpF(Primitive& inPrimitive, DataNode &first_operand, DataNode &second_operand,
|
||||
bool negate_result, x86FloatMemoryType load_op, x86FloatMemoryType cmpOp, VRClass vrClass)
|
||||
{
|
||||
// Push first operand on FPU stack
|
||||
InsnFloatMemory& loadInsn2 = *new InsnFloatMemory(&inPrimitive, mPool, load_op, 1, 1);
|
||||
useProducer(first_operand, loadInsn2, 0, vrClass);
|
||||
InstructionDefine& order = defineTemporaryOrder(loadInsn2, 0);
|
||||
|
||||
// Set FPU status flags
|
||||
// FIXME - Should this define a condition-flag edge ? There really should be separate
|
||||
// edge types for integer and FPU condition codes.
|
||||
InsnFloatMemory& cmpInsn = *new InsnFloatMemory(&inPrimitive, mPool, cmpOp, 2, 1);
|
||||
useProducer(second_operand, cmpInsn, 0, vrClass);
|
||||
useTemporaryOrder(cmpInsn, order, 1);
|
||||
redefineTemporaryOrder(cmpInsn, order, 0);
|
||||
|
||||
// Copy FPU status flags to AX register
|
||||
InsnFloatReg& copyFromStatusFlagsInsn = *new InsnFloatReg(&inPrimitive, mPool, fnstsw, 1, 1);
|
||||
useTemporaryOrder(copyFromStatusFlagsInsn, order, 0);
|
||||
VirtualRegister& FPUstatus = defineTemporary(copyFromStatusFlagsInsn, 0);
|
||||
FPUstatus.preColorRegister(x86GPRToColor[EAX]);
|
||||
|
||||
// sahf instruction (copy from AX into integer status flags register)
|
||||
InsnNoArgs& sahfInsn = *new(mPool) InsnNoArgs(&inPrimitive, mPool, opSahf, 1, 1);
|
||||
useTemporaryVR(sahfInsn, FPUstatus, 0);
|
||||
sahfInsn.addDefine(0, udCond);
|
||||
|
||||
// setnbe instruction
|
||||
InsnSet& setInsn1 = *new(mPool) InsnSet(&inPrimitive, mPool, ccJNBE, 1, 1);
|
||||
setInsn1.addUse(0, udCond);
|
||||
setInsn1.getInstructionUseBegin()[0].src = &sahfInsn; // condition edge
|
||||
|
||||
// setb instruction
|
||||
InsnSet& setInsn2 = *new(mPool) InsnSet(&inPrimitive, mPool, ccJB, 1, 1);
|
||||
setInsn2.addUse(0, udCond);
|
||||
setInsn2.getInstructionUseBegin()[0].src = &sahfInsn; // condition edge
|
||||
|
||||
VirtualRegister* tmpVR1, *tmpVR2;
|
||||
if (negate_result) {
|
||||
tmpVR2 = &defineTemporary(setInsn1, 0); // (first_operand > second_operand) -> tmpVR2
|
||||
tmpVR1 = &defineTemporary(setInsn2, 0); // ((first_operand < second_operand) ||
|
||||
// (first_operand == NAN) ||
|
||||
// (second_operand == NAN)) -> tmpVR1
|
||||
} else {
|
||||
tmpVR1 = &defineTemporary(setInsn1, 0); // (first_operand > second_operand) -> tmpVR1
|
||||
tmpVR2 = &defineTemporary(setInsn2, 0); // ((first_operand < second_operand) ||
|
||||
// (first_operand == NAN) ||
|
||||
// (second_operand == NAN)) -> tmpVR2
|
||||
}
|
||||
|
||||
// FIXME - We must store result of SET instruction in either AL, BL, CL, or DL, but there's no
|
||||
// way to indicate this restriction to the register allocator so, for now, we hard-code the registers
|
||||
tmpVR1->preColorRegister(x86GPRToColor[EAX]);
|
||||
tmpVR2->preColorRegister(x86GPRToColor[EBX]);
|
||||
|
||||
// sub instruction
|
||||
InsnDoubleOpDir& subInsn = *new(mPool) InsnDoubleOpDir(&inPrimitive, mPool, raSub);
|
||||
useTemporaryVR(subInsn, *tmpVR1, 0);
|
||||
useTemporaryVR(subInsn, *tmpVR2, 1);
|
||||
redefineTemporary(subInsn, *tmpVR1, 0); // tmpVR1 - tmpVR2 -> tmpVR1
|
||||
|
||||
// Upper 24 bits are garbage. Sign-extend byte to 32-bits using movs instruction
|
||||
InsnDoubleOp& extInsn = *new(mPool) InsnDoubleOp(&inPrimitive, mPool, opMovSxB, atRegDirect, atRegDirect, 1, 1);
|
||||
useTemporaryVR(extInsn, *tmpVR1, 0);
|
||||
defineProducer(inPrimitive, extInsn, 0); // exts(tmpVR1) -> result
|
||||
}
|
||||
|
||||
void x86Win32Emitter::
|
||||
emit_3wayCmpF_G(Primitive& inPrimitive)
|
||||
{
|
||||
Primitive& cmpPrimitive = Primitive::cast(inPrimitive.nthInputVariable(0));
|
||||
emit_3wayCmpF(inPrimitive, cmpPrimitive.nthInputVariable(0), cmpPrimitive.nthInputVariable(1),
|
||||
false, fld32, fcomp32, vrcFloat);
|
||||
}
|
||||
|
||||
void x86Win32Emitter::
|
||||
emit_3wayCmpF_L(Primitive& inPrimitive)
|
||||
{
|
||||
Primitive& cmpPrimitive = Primitive::cast(inPrimitive.nthInputVariable(0));
|
||||
emit_3wayCmpF(inPrimitive, cmpPrimitive.nthInputVariable(1), cmpPrimitive.nthInputVariable(0),
|
||||
true, fld32, fcomp32, vrcFloat);
|
||||
}
|
||||
|
||||
void x86Win32Emitter::
|
||||
emit_3wayCmpD_G(Primitive& inPrimitive)
|
||||
{
|
||||
Primitive& cmpPrimitive = Primitive::cast(inPrimitive.nthInputVariable(0));
|
||||
emit_3wayCmpF(inPrimitive, cmpPrimitive.nthInputVariable(0), cmpPrimitive.nthInputVariable(1),
|
||||
false, fld64, fcomp64, vrcDouble);
|
||||
}
|
||||
|
||||
void x86Win32Emitter::
|
||||
emit_3wayCmpD_L(Primitive& inPrimitive)
|
||||
{
|
||||
Primitive& cmpPrimitive = Primitive::cast(inPrimitive.nthInputVariable(0));
|
||||
emit_3wayCmpF(inPrimitive, cmpPrimitive.nthInputVariable(1), cmpPrimitive.nthInputVariable(0),
|
||||
true, fld64, fcomp64, vrcDouble);
|
||||
}
|
||||
|
||||
void x86Win32Emitter::
|
||||
emit_3wayCmpCF_G(Primitive& inPrimitive)
|
||||
{
|
||||
Primitive& cmpPrimitive = Primitive::cast(inPrimitive.nthInputVariable(0));
|
||||
emit_3wayCmpF(inPrimitive, cmpPrimitive.nthInputVariable(1), cmpPrimitive.nthInputVariable(0),
|
||||
false, fld32, fcomp32, vrcFloat);
|
||||
}
|
||||
|
||||
void x86Win32Emitter::
|
||||
emit_3wayCmpCF_L(Primitive& inPrimitive)
|
||||
{
|
||||
Primitive& cmpPrimitive = Primitive::cast(inPrimitive.nthInputVariable(0));
|
||||
emit_3wayCmpF(inPrimitive, cmpPrimitive.nthInputVariable(0), cmpPrimitive.nthInputVariable(1),
|
||||
true, fld32, fcomp32, vrcFloat);
|
||||
}
|
||||
|
||||
void x86Win32Emitter::
|
||||
emit_3wayCmpCD_G(Primitive& inPrimitive)
|
||||
{
|
||||
Primitive& cmpPrimitive = Primitive::cast(inPrimitive.nthInputVariable(0));
|
||||
emit_3wayCmpF(inPrimitive, cmpPrimitive.nthInputVariable(1), cmpPrimitive.nthInputVariable(0),
|
||||
false, fld64, fcomp64, vrcDouble);
|
||||
}
|
||||
|
||||
void x86Win32Emitter::
|
||||
emit_3wayCmpCD_L(Primitive& inPrimitive)
|
||||
{
|
||||
Primitive& cmpPrimitive = Primitive::cast(inPrimitive.nthInputVariable(0));
|
||||
emit_3wayCmpF(inPrimitive, cmpPrimitive.nthInputVariable(0), cmpPrimitive.nthInputVariable(1),
|
||||
true, fld64, fcomp64, vrcDouble);
|
||||
}
|
||||
|
||||
//====================================================================================================
|
||||
// Constants
|
||||
|
||||
// Generate 32-bit float constant
|
||||
void x86Win32Emitter::
|
||||
emit_LoadConstant_F(Primitive& inPrimitive)
|
||||
{
|
||||
Uint32 constant = (*static_cast<const PrimConst *>(&inPrimitive)).value.i;
|
||||
|
||||
x86Instruction* newInsn;
|
||||
|
||||
if(constant == 0)
|
||||
newInsn = new(mPool) x86Instruction(&inPrimitive, mPool, srmMoveImm0, 0, 0, 1);
|
||||
else
|
||||
newInsn = new(mPool) x86Instruction(&inPrimitive, mPool, ceMoveImm, constant, atRegDirect, 0, 1);
|
||||
|
||||
defineProducer(inPrimitive, copyFromIntegerRegisterToFloat(inPrimitive, *newInsn), 0, vrcFloat); // result
|
||||
}
|
||||
|
||||
// Generate 64-bit double constant
|
||||
// FIXME: Need to create an in-memory literal pool for storing double constants, rather than using immediate instructions
|
||||
void x86Win32Emitter::
|
||||
emit_LoadConstant_D(Primitive& inPrimitive)
|
||||
{
|
||||
Flt64 constant = (*static_cast<const PrimConst *>(&inPrimitive)).value.d;
|
||||
|
||||
// Store 64-bit double constant in literal pool
|
||||
// FIXME - We should have a literal pool for each method to store in-memory constants
|
||||
// and which can be released when the method is discarded.
|
||||
Flt64* literalPoolEntry = (Flt64*)malloc(sizeof(Flt64));
|
||||
*literalPoolEntry = constant;
|
||||
|
||||
// Fetch from memory and temporarily push 64-bit double on the FPU stack
|
||||
InsnFloatMemory &loadInsn = *new InsnFloatMemory(&inPrimitive, mPool, fld64, atAbsoluteAddress, (Uint32)literalPoolEntry, 0, 1);
|
||||
InstructionDefine& order = defineTemporaryOrder(loadInsn, 0);
|
||||
|
||||
// Pop 64-bit double from FPU stack and store into double variable
|
||||
InsnFloatMemory& storeInsn = *new InsnFloatMemory(&inPrimitive, mPool, fstp64, 1, 1);
|
||||
useTemporaryOrder(storeInsn, order, 0);
|
||||
defineProducer(inPrimitive, storeInsn, 0, vrcDouble); // result
|
||||
}
|
||||
|
||||
//====================================================================================================
|
||||
// Floating-point memory operations
|
||||
|
||||
void x86Win32Emitter::
|
||||
emit_Ld_F(Primitive& inPrimitive)
|
||||
{
|
||||
// Load 32-bit float into an integer register
|
||||
InsnDoubleOpDir& loadInsn = *new(mPool) InsnDoubleOpDir(&inPrimitive, mPool, raLoadI, atRegisterIndirect, atRegDirect);
|
||||
useProducer(inPrimitive.nthInputVariable(0), loadInsn, 1); // memory edge
|
||||
useProducer(inPrimitive.nthInputVariable(1), loadInsn, 0); // address
|
||||
|
||||
// Store into 32-bit float variable
|
||||
InsnDoubleOpDir& storeInsn = copyFromIntegerRegisterToFloat(inPrimitive, loadInsn);
|
||||
defineProducer(inPrimitive, storeInsn, 0, vrcFloat); // result
|
||||
}
|
||||
|
||||
void x86Win32Emitter::
|
||||
emit_Ld_D(Primitive& inPrimitive)
|
||||
{
|
||||
// Fetch from memory and temporarily push 64-bit double on the FPU stack
|
||||
InsnFloatMemory &loadInsn = *new InsnFloatMemory(&inPrimitive, mPool, fld64, atRegisterIndirect, 2, 1);
|
||||
useProducer(inPrimitive.nthInputVariable(0), loadInsn, 1); // memory edge
|
||||
useProducer(inPrimitive.nthInputVariable(1), loadInsn, 0); // address
|
||||
InstructionDefine& order = defineTemporaryOrder(loadInsn, 0);
|
||||
|
||||
// Pop 64-bit double from FPU stack
|
||||
InsnFloatMemory& storeInsn = *new InsnFloatMemory(&inPrimitive, mPool, fstp64, 1, 1);
|
||||
useTemporaryOrder(storeInsn, order, 0);
|
||||
defineProducer(inPrimitive, storeInsn, 0, vrcDouble); // result
|
||||
}
|
||||
|
||||
void x86Win32Emitter::
|
||||
emit_St_F(Primitive& inPrimitive)
|
||||
{
|
||||
// Load 32-bit float into an integer register
|
||||
InsnDoubleOpDir& loadInsn = *new(mPool) InsnDoubleOpDir(&inPrimitive, mPool, raCopyI, atRegAllocStackSlot, atRegDirect, 1, 1);
|
||||
useProducer(inPrimitive.nthInputVariable(2), loadInsn, 0); // data
|
||||
VirtualRegister& floatVal = defineTemporary(loadInsn, 0); // intermediate output
|
||||
|
||||
// Store temporary integer register into indirect register destination
|
||||
InsnDoubleOpDir& storeInsn = *new(mPool) InsnDoubleOpDir(&inPrimitive, mPool, raStoreI, atRegisterIndirect, atRegDirect, 3, 1);
|
||||
useProducer(inPrimitive.nthInputVariable(1), storeInsn, 0); // address
|
||||
useTemporaryVR(storeInsn, floatVal, 1); // data
|
||||
useProducer(inPrimitive.nthInputVariable(0), storeInsn, 2); // memory edge in
|
||||
defineProducer(inPrimitive, storeInsn, 0); // memory edge out
|
||||
}
|
||||
|
||||
void x86Win32Emitter::
|
||||
emit_St_D(Primitive& inPrimitive)
|
||||
{
|
||||
// Temporarily push 64-bit double on the FPU stack
|
||||
InsnFloatMemory& loadInsn = *new InsnFloatMemory(&inPrimitive, mPool, fld64, 1, 1);
|
||||
useProducer(inPrimitive.nthInputVariable(2), loadInsn, 0, vrcDouble); // data
|
||||
InstructionDefine& order = defineTemporaryOrder(loadInsn, 0);
|
||||
|
||||
// Pop result from FPU stack and store into memory as 64-bit double
|
||||
InsnFloatMemory& storeInsn = *new InsnFloatMemory(&inPrimitive, mPool, fstp64, atRegisterIndirect, 3, 1);
|
||||
useProducer(inPrimitive.nthInputVariable(1), storeInsn, 0); // address
|
||||
useTemporaryOrder(storeInsn, order, 1);
|
||||
useProducer(inPrimitive.nthInputVariable(0), storeInsn, 2); // memory edge in
|
||||
defineProducer(inPrimitive, storeInsn, 0); // memory edge out
|
||||
}
|
|
@ -0,0 +1,130 @@
|
|||
/* -*- 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.
|
||||
*/
|
||||
//
|
||||
// x86Float.h - Floating-point code-generation for x86 processors
|
||||
//
|
||||
|
||||
#ifndef X86_FLOAT_H
|
||||
#define X86_FLOAT_H
|
||||
|
||||
#include "x86Win32Instruction.h"
|
||||
|
||||
//-----------------------------------------------------------------------------------------------------------
|
||||
// Floating-point instructions in which one operand is a memory location.
|
||||
// If there is a second operand, then it is implicitly the top of the FPU stack.
|
||||
|
||||
class InsnFloatMemory :
|
||||
public x86ArgListInstruction
|
||||
{
|
||||
public:
|
||||
InsnFloatMemory(DataNode* inPrimitive, Pool& inPool, x86FloatMemoryType inOpInfo, int inX, int inY):
|
||||
x86ArgListInstruction (inPrimitive, inPool, inX, inY )
|
||||
{
|
||||
opcodeInfo = &opcodeTable[inOpInfo];
|
||||
iArgumentList = new x86SingleArgumentList(atRegAllocStackSlot);
|
||||
}
|
||||
|
||||
InsnFloatMemory(DataNode* inPrimitive, Pool& inPool, x86FloatMemoryType inOpInfo, x86ArgumentType argType,
|
||||
Int32 displacement, int inX, int inY):
|
||||
x86ArgListInstruction (inPrimitive, inPool, inX, inY )
|
||||
{
|
||||
opcodeInfo = &opcodeTable[inOpInfo];
|
||||
iArgumentList = new x86SingleArgumentList(argType, displacement);
|
||||
}
|
||||
|
||||
InsnFloatMemory(DataNode* inPrimitive, Pool& inPool, x86FloatMemoryType inOpInfo, x86ArgumentType argType,
|
||||
int inX, int inY):
|
||||
x86ArgListInstruction (inPrimitive, inPool, inX, inY )
|
||||
{
|
||||
opcodeInfo = &opcodeTable[inOpInfo];
|
||||
iArgumentList = new x86SingleArgumentList(argType);
|
||||
}
|
||||
|
||||
virtual Uint8 opcodeSize() {return 1;}
|
||||
|
||||
virtual void formatToMemory(void * /*inStart*/, Uint32 /*inCurOffset*/, MdFormatter& /*inEmitter*/);
|
||||
|
||||
// spilling
|
||||
virtual bool switchUseToSpill(Uint8 /*inWhichUse*/, VirtualRegister &/*inVR*/) { return false; }
|
||||
virtual bool switchDefineToSpill(Uint8 /*inWhichDefine*/, VirtualRegister &/*inVR*/) { return false; }
|
||||
|
||||
// Control Spilling
|
||||
virtual bool opcodeAcceptsSpill() { return false; }
|
||||
virtual void switchOpcodeToSpill() { assert(0); }
|
||||
|
||||
protected:
|
||||
x86OpcodeInfo* opcodeInfo;
|
||||
|
||||
private:
|
||||
static x86OpcodeInfo opcodeTable[];
|
||||
|
||||
public:
|
||||
#ifdef DEBUG_LOG
|
||||
// Debugging Methods
|
||||
virtual void printOpcode(LogModuleObject &f) { UT_OBJECTLOG(f, PR_LOG_ALWAYS, ("%-9s", opcodeInfo->oiText)); }
|
||||
#endif // DEBUG_LOG
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------------------------------------
|
||||
// Floating-point instructions in which all operands and results are implicitly on the FPU stack
|
||||
|
||||
enum x86FloatRegType
|
||||
{
|
||||
fucomip,
|
||||
fneg,
|
||||
fnstsw
|
||||
};
|
||||
|
||||
class InsnFloatReg :
|
||||
public InsnUseXDefineYFromPool
|
||||
{
|
||||
public:
|
||||
InsnFloatReg(DataNode* inPrimitive, Pool& inPool, x86FloatRegType inOpInfo, Uint8 inX, Uint8 inY):
|
||||
InsnUseXDefineYFromPool (inPrimitive, inPool, inX, inY )
|
||||
{
|
||||
opcodeInfo = &opcodeTable[inOpInfo];
|
||||
}
|
||||
|
||||
virtual Uint8 opcodeSize() {return 2;}
|
||||
|
||||
virtual size_t getFormattedSize(MdFormatter& /*inFormatter*/) { return 2; };
|
||||
virtual void formatToMemory(void * /*inStart*/, Uint32 /*inCurOffset*/, MdFormatter& /*inEmitter*/);
|
||||
|
||||
// spilling
|
||||
virtual bool switchUseToSpill(Uint8 /*inWhichUse*/, VirtualRegister &/*inVR*/) { return false; }
|
||||
virtual bool switchDefineToSpill(Uint8 /*inWhichDefine*/, VirtualRegister &/*inVR*/) { return false; }
|
||||
|
||||
// Control Spilling
|
||||
virtual bool opcodeAcceptsSpill() { return false; }
|
||||
virtual void switchOpcodeToSpill() { assert(0); }
|
||||
|
||||
protected:
|
||||
x86OpcodeInfo* opcodeInfo;
|
||||
|
||||
private:
|
||||
static x86OpcodeInfo opcodeTable[];
|
||||
|
||||
public:
|
||||
#ifdef DEBUG_LOG
|
||||
// Debugging Methods
|
||||
virtual void printPretty(LogModuleObject &f) { UT_OBJECTLOG(f, PR_LOG_ALWAYS, ("%-9s", opcodeInfo->oiText)); }
|
||||
#endif // DEBUG_LOG
|
||||
};
|
||||
|
||||
#endif // X86_FLOAT_H
|
||||
|
|
@ -0,0 +1,236 @@
|
|||
/* -*- 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.
|
||||
*/
|
||||
//
|
||||
// File: x86Formatter.cpp
|
||||
//
|
||||
// Authors: Peter DeSantis
|
||||
// Simon Holmes a Court
|
||||
// Scott Silver
|
||||
//
|
||||
|
||||
#include "x86Formatter.h"
|
||||
#include "FieldOrMethod.h"
|
||||
#include "MemoryAccess.h"
|
||||
#include "JavaObject.h"
|
||||
|
||||
//-----------------------------------------------------------------------------------------------------------
|
||||
// Constants
|
||||
const Uint8 kPush_ebp = 0x55;
|
||||
const Uint8 kPush_edi = 0x57;
|
||||
const Uint8 kPush_esi = 0x56;
|
||||
const Uint8 kPush_ebx = 0x53;
|
||||
|
||||
const Uint16 kMov_ebp_esp = 0x8bec;
|
||||
const Uint16 kSub_esp_Imm8 = 0x83ec; // 8-bit immediate subtract (8-bit val extended to 32-bits)
|
||||
const Uint16 kSub_esp_Imm32 = 0x81ec; // Signed, 32-bit immediate subtract
|
||||
|
||||
const Uint8 kPop_ebp = 0x5d;
|
||||
const Uint8 kPop_ebx = 0x5b;
|
||||
const Uint8 kPop_esi = 0x5e;
|
||||
const Uint8 kPop_edi = 0x5f;
|
||||
|
||||
const Uint8 kRet = 0xc3;
|
||||
const Uint8 kRet_Imm16 = 0xc2;
|
||||
const Uint16 kMov_esp_ebp = 0x8be5;
|
||||
|
||||
//-----------------------------------------------------------------------------------------------------------
|
||||
// x86Formatter
|
||||
|
||||
void x86Formatter::
|
||||
beginFormatting(const FormattedCodeInfo& inInfo)
|
||||
{
|
||||
mFCI = inInfo;
|
||||
}
|
||||
|
||||
void x86Formatter::
|
||||
initStackFrameInfo()
|
||||
{
|
||||
mStackFrameInfo.init(mEmitter.mVRAllocator);
|
||||
}
|
||||
|
||||
void x86Formatter::
|
||||
calculatePrologEpilog(Method& inMethod, Uint32& outPrologSize, Uint32& outEpilogSize)
|
||||
{
|
||||
Uint32 GPRwords = mStackFrameInfo.getNumSavedGPRWords();
|
||||
Uint32 localStoreBytes = mStackFrameInfo.getLocalStoreSizeBytes();
|
||||
|
||||
if(localStoreBytes == 0)
|
||||
{
|
||||
mPrologSize_bytes = 3 + GPRwords;
|
||||
mEpilogSize_bytes = 4 + GPRwords;
|
||||
}
|
||||
else {
|
||||
if (localStoreBytes < 128)
|
||||
mPrologSize_bytes = 6 + GPRwords;
|
||||
else
|
||||
mPrologSize_bytes = 9 + GPRwords;
|
||||
mEpilogSize_bytes = 6 + GPRwords;
|
||||
}
|
||||
|
||||
// determine how many words were passed to us, so the epilog can clean up the stack
|
||||
const Signature& ourSignature = inMethod.getSignature();
|
||||
const int numArgs = ourSignature.nArguments;
|
||||
const Type** ourArgs = ourSignature.argumentTypes;
|
||||
|
||||
mNumberOfArgumentWords = 0;
|
||||
for(int i = 0; i < numArgs; i++)
|
||||
mNumberOfArgumentWords += nTypeKindSlots(ourArgs[i]->typeKind);
|
||||
|
||||
outPrologSize = mPrologSize_bytes;
|
||||
outEpilogSize = mEpilogSize_bytes;
|
||||
}
|
||||
|
||||
void x86Formatter::
|
||||
formatPrologToMemory(void* inWhere)
|
||||
{
|
||||
Uint8* where = (uint8*)inWhere;
|
||||
|
||||
// push ebp
|
||||
*where++ = kPush_ebp;
|
||||
|
||||
//mov ebp, esp
|
||||
writeBigHalfwordUnaligned(where, kMov_ebp_esp);
|
||||
where += 2;
|
||||
|
||||
// reserve local store space
|
||||
// sub esp, localstore
|
||||
Uint32 localStoreBytes = mStackFrameInfo.getLocalStoreSizeBytes();
|
||||
if (localStoreBytes) {
|
||||
if (localStoreBytes < 128)
|
||||
{
|
||||
writeBigHalfwordUnaligned(where, kSub_esp_Imm8);
|
||||
where += 2;
|
||||
*where++ = localStoreBytes;
|
||||
}
|
||||
else
|
||||
{
|
||||
writeBigHalfwordUnaligned(where, kSub_esp_Imm32);
|
||||
where += 2;
|
||||
writeLittleWordUnaligned(where, localStoreBytes);
|
||||
where += 4;
|
||||
}
|
||||
}
|
||||
|
||||
// save GPRs -- FIX change to bitfields sometime
|
||||
Uint32 GPRwords = mStackFrameInfo.getNumSavedGPRWords();
|
||||
if(GPRwords > 2)
|
||||
*where++ = kPush_edi;
|
||||
if(GPRwords > 1)
|
||||
*where++ = kPush_esi;
|
||||
if(GPRwords > 0)
|
||||
*where++ = kPush_ebx;
|
||||
}
|
||||
|
||||
void x86Formatter::
|
||||
formatEpilogToMemory(void* inWhere)
|
||||
{
|
||||
Uint8* where = (uint8*)inWhere;
|
||||
|
||||
// restore GPRs -- FIX change to bitfields sometime
|
||||
Uint32 GPRwords = mStackFrameInfo.getNumSavedGPRWords();
|
||||
if(GPRwords > 0)
|
||||
*where++ = kPop_ebx;
|
||||
if(GPRwords > 1)
|
||||
*where++ = kPop_esi;
|
||||
if(GPRwords > 2)
|
||||
*where++ = kPop_edi;
|
||||
|
||||
// mov esp, ebp
|
||||
Uint32 localStoreBytes = mStackFrameInfo.getLocalStoreSizeBytes();
|
||||
if(localStoreBytes != 0)
|
||||
{
|
||||
writeBigHalfwordUnaligned(where, kMov_esp_ebp);
|
||||
where+=2;
|
||||
}
|
||||
|
||||
// pop ebp
|
||||
*where++ = kPop_ebp;
|
||||
|
||||
// return cleaning the stack of passed in arguments
|
||||
if(mNumberOfArgumentWords != 0)
|
||||
{
|
||||
*where++ = kRet_Imm16;
|
||||
writeLittleHalfwordUnaligned(where, mNumberOfArgumentWords * 4);
|
||||
}
|
||||
else
|
||||
{
|
||||
*where = kRet;
|
||||
}
|
||||
}
|
||||
|
||||
Uint8* x86Formatter::
|
||||
createTransitionVector(const FormattedCodeInfo& inInfo)
|
||||
{
|
||||
return inInfo.methodStart;
|
||||
}
|
||||
|
||||
Uint8* x86Formatter::
|
||||
getMethodBegin()
|
||||
{
|
||||
return mFCI.methodStart;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------------------------------------
|
||||
// Debugging
|
||||
|
||||
#ifdef DEBUG_LOG
|
||||
|
||||
#include "XDisAsm.h"
|
||||
|
||||
#define USING_X86_DISASSEMBLER
|
||||
|
||||
const int kMaxDissasemblyBytes = 8; // maximum number of bytes to dump per instruction
|
||||
|
||||
// Function: disassemble1
|
||||
void*
|
||||
disassemble1(LogModuleObject &f, void* inFrom)
|
||||
{
|
||||
#ifdef USING_X86_DISASSEMBLER
|
||||
char* disasmText;
|
||||
char* startAddr = (char*)inFrom;
|
||||
char* curAddr = startAddr;
|
||||
|
||||
disasmText = disasmx86( 0,
|
||||
&curAddr,
|
||||
curAddr + 32,
|
||||
kDisAsmFlag32BitSegments);
|
||||
|
||||
UT_OBJECTLOG(f, PR_LOG_ALWAYS, ("0x%p: ", inFrom));
|
||||
|
||||
// print memory dump
|
||||
Uint8 numBytes = (Uint8)curAddr - (Uint8)startAddr;
|
||||
for(int i = 0; i < kMaxDissasemblyBytes; i++)
|
||||
{
|
||||
if (i < numBytes) {
|
||||
Uint8 address= ((Uint8*)startAddr)[i];
|
||||
UT_OBJECTLOG(f, PR_LOG_ALWAYS, ("%x%x ", address/16, address%16));
|
||||
}
|
||||
else
|
||||
UT_OBJECTLOG(f, PR_LOG_ALWAYS, (" "));
|
||||
}
|
||||
|
||||
UT_OBJECTLOG(f, PR_LOG_ALWAYS, (" %s\n", disasmText));
|
||||
return (curAddr);
|
||||
#else
|
||||
f = NULL; inFrom = NULL;
|
||||
return NULL;
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
#endif // DEBUG_LOG
|
|
@ -0,0 +1,115 @@
|
|||
/* -*- 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.
|
||||
*/
|
||||
//
|
||||
// File: x86Formatter.h
|
||||
//
|
||||
// Authors: Peter DeSantis
|
||||
// Simon Holmes a Court
|
||||
// Scott Silver
|
||||
//
|
||||
|
||||
#ifndef X86_FORMATTER
|
||||
#define X86_FORMATTER
|
||||
|
||||
#include "Fundamentals.h"
|
||||
#include "FormatStructures.h" // for FCI, StackFrameInfo
|
||||
#include "x86Win32Emitter.h" // temp?
|
||||
|
||||
const int kStackSlotSize = 8;
|
||||
|
||||
//-----------------------------------------------------------------------------------------------------------
|
||||
// x86StackFrameInfo
|
||||
class x86StackFrameInfo :
|
||||
public StackFrameInfo
|
||||
{
|
||||
public:
|
||||
x86StackFrameInfo() { }
|
||||
|
||||
void init(VirtualRegisterManager& inVRManager)
|
||||
{
|
||||
// eventually the register allocator will tell us all this information
|
||||
numSavedGPRs = 3;
|
||||
numSavedFPRs = 0;
|
||||
numMonitorSlots = 0;
|
||||
|
||||
localStore_bytes = inVRManager.nUsedStackSlots * kStackSlotSize;
|
||||
|
||||
StackFrameInfo::init(inVRManager);
|
||||
}
|
||||
|
||||
// return number of bytes than EBP of the stack slot
|
||||
Int32 getStackSlotOffset(VirtualRegister& inVr)
|
||||
{
|
||||
assert(hasBeenInited);
|
||||
assert(inVr.getClass() == StackSlotRegister);
|
||||
return (inVr.getColor() * - kStackSlotSize) - kStackSlotSize;
|
||||
}
|
||||
|
||||
private:
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------------------------------------
|
||||
// x86Formatter
|
||||
class x86Formatter
|
||||
{
|
||||
protected:
|
||||
Uint16 mNumberOfArgumentWords;
|
||||
|
||||
Uint32 mFrameSize_words;
|
||||
Uint32 mPrologSize_bytes;
|
||||
Uint32 mEpilogSize_bytes;
|
||||
|
||||
x86StackFrameInfo mStackFrameInfo;
|
||||
|
||||
// later should be object, not pointer
|
||||
FormattedCodeInfo mFCI; // cached format info
|
||||
|
||||
public:
|
||||
// fields
|
||||
const MdEmitter& mEmitter;
|
||||
|
||||
// methods
|
||||
x86Formatter(const MdEmitter& inEmitter) : mEmitter(inEmitter) { }
|
||||
void calculatePrologEpilog(Method& /*inMethod*/, Uint32& outPrologSize, Uint32& outEpilogSize);
|
||||
void formatEpilogToMemory(void* inWhere);
|
||||
void formatPrologToMemory(void* inWhere);
|
||||
|
||||
void beginFormatting(const FormattedCodeInfo& inInfo);
|
||||
void endFormatting(const FormattedCodeInfo& /*inInfo*/) { }
|
||||
|
||||
void calculatePrePostMethod(Method& /*inMethod*/, Uint32& outPreMethodSize, Uint32& outPostMethodSize) { outPreMethodSize = outPostMethodSize = 0; }
|
||||
void formatPostMethodToMemory(void* /*inWhere*/, const FormattedCodeInfo& /*inInfo*/) { }
|
||||
void formatPreMethodToMemory(void* /*inWhere*/, const FormattedCodeInfo& /*inInfo*/) { }
|
||||
|
||||
// stack frame stuff
|
||||
void initStackFrameInfo();
|
||||
StackFrameInfo& getStackFrameInfo() {return mStackFrameInfo; }
|
||||
Int32 getStackSlotOffset(VirtualRegister& inVr) { return mStackFrameInfo.getStackSlotOffset(inVr); }
|
||||
|
||||
Uint8* createTransitionVector(const FormattedCodeInfo& inInfo);
|
||||
|
||||
Uint8* getMethodBegin();
|
||||
};
|
||||
|
||||
|
||||
#ifdef DEBUG_LOG
|
||||
void* disassemble1(LogModuleObject &f, void* inFrom);
|
||||
#endif
|
||||
|
||||
|
||||
#endif // X86_FORMATTER
|
|
@ -0,0 +1,593 @@
|
|||
/* -*- Mode: asm; tab-width:4; truncate-lines:t -*-
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
.file "x86Linux.S"
|
||||
|
||||
#define ALIGN .align 16
|
||||
#define SYMBOL_NAME(name) name
|
||||
#define SYMBOL_NAME_LABEL(name) name##:
|
||||
|
||||
#define GLOBAL_ENTRY_START(name) \
|
||||
ALIGN; \
|
||||
.globl SYMBOL_NAME(name); \
|
||||
.type SYMBOL_NAME(name),@function; \
|
||||
SYMBOL_NAME_LABEL(name)
|
||||
|
||||
#define GLOBAL_ENTRY_END(name) \
|
||||
SYMBOL_NAME_LABEL(.L##name); \
|
||||
.size SYMBOL_NAME(name),SYMBOL_NAME(.L##name)-SYMBOL_NAME(name)
|
||||
|
||||
|
||||
GLOBAL_ENTRY_START(staticCompileStub)
|
||||
|
||||
push %eax /* Save all volatiles */
|
||||
push %ebx
|
||||
push %ecx
|
||||
push %edx
|
||||
push 20(%esp) /* Push the second argument to compileAndBackPatchMethod (return address) */
|
||||
push 20(%esp) /* Push the first argument to compileAndBackPatchMethod (cacheEntry) */
|
||||
call compileAndBackPatchMethod
|
||||
pop %edx /* Remove passes arguments */
|
||||
pop %edx /* Remove passes arguments */
|
||||
mov %eax, 16(%esp) /* Overwrite cacheEntry with function address */
|
||||
pop %edx /* Restore volatiles */
|
||||
pop %ecx
|
||||
pop %ebx
|
||||
pop %eax
|
||||
ret /* Jump to function leaving the return address at the top of the stack */
|
||||
|
||||
GLOBAL_ENTRY_END(staticCompileStub)
|
||||
|
||||
GLOBAL_ENTRY_START(compileStub)
|
||||
push 0xefbeadde /* This is a dummy immediate that will be filled in by */
|
||||
jmp staticCompileStub /* generateCompileStub with the cacheEntry */
|
||||
GLOBAL_ENTRY_END(compileStub)
|
||||
|
||||
/*
|
||||
* 64bit Arithmetic Support Functions
|
||||
*
|
||||
* x86Extract64Bit
|
||||
*
|
||||
* Origin: Simon
|
||||
* Purpose: signed right-aligned field extraction
|
||||
* In: 64 bit source (on stack)
|
||||
* 32 bit extraction size (on stack)
|
||||
* Out: 64 bit result
|
||||
* Note: Only works in range 1 <= b <= 63, b is extraction amount
|
||||
*/
|
||||
GLOBAL_ENTRY_START(x86Extract64Bit)
|
||||
|
||||
mov 4(%esp),%eax /* load low byte of a */
|
||||
|
||||
mov 12(%esp),%ecx /*load shift amount */
|
||||
cmp $0x20,%ecx
|
||||
jg greater32
|
||||
|
||||
/* extract <= than 32 bits
|
||||
* shift amount = 32 - extract
|
||||
*/
|
||||
neg %ecx
|
||||
add $0x20,%ecx /* ecx = 32 - extract */
|
||||
shl %cl,%eax
|
||||
sar %cl,%eax
|
||||
cdq /* sign extend into EDX:EAX */
|
||||
ret $12
|
||||
|
||||
greater32:
|
||||
/* ext > 32 bits
|
||||
* shift amount = 64 - extract
|
||||
*/
|
||||
mov 8(%esp),%edx /* load high byte of a */
|
||||
neg %ecx
|
||||
add $0x40,%ecx /* ecx = 64 - extract */
|
||||
shl %cl,%edx
|
||||
sar %cl,%edx
|
||||
ret $12
|
||||
|
||||
GLOBAL_ENTRY_END(x86Extract64Bit)
|
||||
|
||||
/*
|
||||
* 3WayCompare
|
||||
*
|
||||
* Origin: Symantec JIT
|
||||
* Purpose: compare two longs
|
||||
* In: two longs on the stack
|
||||
* Out: depends on condition flags:
|
||||
* less = -1
|
||||
* equal = 0
|
||||
* greater = 1
|
||||
*/
|
||||
GLOBAL_ENTRY_START(x86ThreeWayCMP_L)
|
||||
|
||||
/* edx:eax is tos, ecx:ebx is nos */
|
||||
mov 8(%esp),%ecx
|
||||
mov 16(%esp),%edx
|
||||
|
||||
cmp %edx,%ecx
|
||||
jl lcmp_m1
|
||||
|
||||
jg lcmp_1
|
||||
|
||||
mov 4(%esp),%ecx
|
||||
mov 12(%esp),%edx
|
||||
|
||||
cmp %edx,%ecx
|
||||
ja lcmp_1
|
||||
|
||||
mov $0,%eax
|
||||
jb lcmp_m1
|
||||
|
||||
ret $16
|
||||
|
||||
.align 4
|
||||
lcmp_m1:
|
||||
mov $-1,%eax
|
||||
ret $16
|
||||
|
||||
.align 4
|
||||
lcmp_1:
|
||||
mov $1,%eax
|
||||
ret $16
|
||||
|
||||
GLOBAL_ENTRY_END(x86ThreeWayCMP_L)
|
||||
|
||||
/*
|
||||
* llmul
|
||||
*
|
||||
* Origin: Intel Code (via MSDev)
|
||||
* Purpose: long multiply (same for signed/unsigned)
|
||||
* In: args are passed on the stack:
|
||||
* 1st pushed: multiplier (QWORD)
|
||||
* 2nd pushed: multiplicand (QWORD)
|
||||
* Out: EDX:EAX - product of multiplier and multiplicand
|
||||
* Note: parameters are removed from the stack
|
||||
* Uses: ECX
|
||||
*/
|
||||
GLOBAL_ENTRY_START(x86Mul64Bit)
|
||||
|
||||
/* A*B = (A.lo * B.lo) + (A.lo * B.hi) + (B.lo * A.hi) ??? */
|
||||
mov 8(%esp),%eax /* A.hi */
|
||||
mov 16(%esp),%ecx /* B.hi */
|
||||
or %eax,%ecx /* test for both hiwords zero */
|
||||
mov 12(%esp),%ecx /* B.lo */
|
||||
jnz hard
|
||||
|
||||
/* easy case
|
||||
* both are zero, just mult ALO and BLO
|
||||
*/
|
||||
mov 4(%esp),%eax /* A.lo */
|
||||
mul %ecx /* A.lo * B.lo */
|
||||
ret $16 /* callee restores the stack */
|
||||
|
||||
/* hard case */
|
||||
hard:
|
||||
push %ebx
|
||||
|
||||
mul %ecx /* A.hi * B.lo */
|
||||
mov %eax,%ebx /* save result */
|
||||
|
||||
mov 8(%esp),%eax /* A.lo */
|
||||
mull 14(%esp) /* A.lo * B.hi */
|
||||
add %eax,%ebx /* ebx = ((A.lo * B.hi) + (A.hi * B.lo)) */
|
||||
|
||||
mov 8(%esp),%eax /* A.lo */
|
||||
mul %ecx /* edx:eax = A.lo * B.lo */
|
||||
add %ebx,%edx /* now edx has all the LO*HI stuff */
|
||||
|
||||
pop %ebx
|
||||
|
||||
ret $16 /* callee restores the stack */
|
||||
|
||||
GLOBAL_ENTRY_END(x86Mul64Bit)
|
||||
|
||||
/*
|
||||
* lldiv
|
||||
*
|
||||
* Origin: Intel Code (via MSDev)
|
||||
* Purpose: signed long divide
|
||||
* In: args are passed on the stack:
|
||||
* 1st pushed: divisor (QWORD)
|
||||
* 2nd pushed: dividend (QWORD)
|
||||
* Out: EDX:EAX contains the quotient (dividend/divisor)
|
||||
* Note: parameters are removed from the stack
|
||||
* Uses: ECX
|
||||
*/
|
||||
GLOBAL_ENTRY_START(x86Div64Bit)
|
||||
|
||||
push %edi
|
||||
push %esi
|
||||
push %ebx
|
||||
|
||||
/* Determine sign of the result (%edi = 0 if result is positive, non-zero
|
||||
* otherwise) and make operands positive.
|
||||
*/
|
||||
xor %edi,%edi /* result sign assumed positive */
|
||||
|
||||
mov 20(%esp),%eax /* hi word of a */
|
||||
or %eax,%eax /* test to see if signed */
|
||||
jge L1 /* skip rest if a is already positive */
|
||||
inc %edi /* complement result sign flag */
|
||||
mov 16(%esp),%edx /* lo word of a */
|
||||
neg %eax /* make a positive */
|
||||
neg %edx
|
||||
sbb $0,%eax
|
||||
mov %eax,20(%esp) /* save positive value */
|
||||
mov %edx,16(%esp)
|
||||
L1:
|
||||
mov 28(%esp),%eax /* hi word of b */
|
||||
or %eax,%eax /* test to see if signed */
|
||||
jge L2 /* skip rest if b is already positive */
|
||||
inc %edi /* complement the result sign flag */
|
||||
mov 24(%esp),%edx /* lo word of a */
|
||||
neg %eax /* make b positive */
|
||||
neg %edx
|
||||
sbb $0,%eax
|
||||
mov %eax,28(%esp) /* save positive value */
|
||||
mov %edx,24(%esp)
|
||||
L2:
|
||||
|
||||
/* Now do the divide. First look to see if the divisor is less than 4194304K.
|
||||
* If so, then we can use a simple algorithm with word divides, otherwise
|
||||
* things get a little more complex.
|
||||
* NOTE - %eax currently contains the high order word of DVSR
|
||||
*/
|
||||
or %eax,%eax /* check to see if divisor < 4194304K */
|
||||
jnz L3 /* nope, gotta do this the hard way */
|
||||
mov 24(%esp),%ecx /* load divisor */
|
||||
mov 20(%esp),%eax /* load high word of dividend */
|
||||
xor %edx,%edx
|
||||
div %ecx /* %eax <- high order bits of quotient */
|
||||
mov %eax,%ebx /* save high bits of quotient */
|
||||
mov 16(%esp),%eax /* %edx:%eax <- remainder:lo word of dividend */
|
||||
div %ecx /* %eax <- low order bits of quotient */
|
||||
mov %ebx,%edx /* %edx:%eax <- quotient */
|
||||
jmp L4 /* set sign, restore stack and return */
|
||||
|
||||
/* Here we do it the hard way. Remember, %eax contains the high word of DVSR */
|
||||
L3:
|
||||
mov %eax,%ebx /* %ebx:ecx <- divisor */
|
||||
mov 24(%esp),%ecx
|
||||
mov 20(%esp),%edx /* %edx:%eax <- dividend */
|
||||
mov 16(%esp),%eax
|
||||
L5:
|
||||
shr $1,%ebx /* shift divisor right one bit */
|
||||
rcr $1,%ecx
|
||||
shr $1,%edx /* shift dividend right one bit */
|
||||
rcr $1,%eax
|
||||
or %ebx,%ebx
|
||||
jnz L5 /* loop until divisor < 4194304K */
|
||||
div %ecx /* now divide, ignore remainder */
|
||||
mov %eax,%esi /* save quotient */
|
||||
|
||||
/* We may be off by one, so to check, we will multiply the quotient
|
||||
* by the divisor and check the result against the orignal dividend
|
||||
* Note that we must also check for overflow, which can occur if the
|
||||
* dividend is close to 2**64 and the quotient is off by 1.
|
||||
*/
|
||||
mull 28(%esp) /* QUOT * HIWORD(DVSR) */
|
||||
mov %eax,%ecx
|
||||
mov 24(%esp),%eax
|
||||
mul %esi /* QUOT * LOWORD(DVSR) */
|
||||
add %ecx,%edx /* %EDX:%EAX = QUOT * DVSR */
|
||||
jc L6 /* carry means Quotient is off by 1 */
|
||||
|
||||
/* do long compare here between original dividend and the result of the
|
||||
* multiply in %edx:%eax. If original is larger or equal, we are ok, otherwise
|
||||
* subtract one (1) from the quotient.
|
||||
*/
|
||||
cmp 20(%esp),%edx /* compare hi words of result and original */
|
||||
ja L6 /* if result > original, do subtract */
|
||||
jb L7 /* if result < original, we are ok */
|
||||
cmp 16(%esp),%eax /* hi words are equal, compare lo words */
|
||||
jbe L7 /* if less or equal we are ok, else subtract */
|
||||
L6:
|
||||
dec %esi /* subtract 1 from quotient */
|
||||
L7:
|
||||
xor %edx,%edx /* %edx:%eax <- quotient */
|
||||
mov %esi,%eax
|
||||
|
||||
/* Just the cleanup left to do. %edx:%eax contains the quotient. Set the sign
|
||||
* according to the save value, cleanup the stack, and return.
|
||||
*/
|
||||
L4:
|
||||
dec %edi /* check to see if result is negative */
|
||||
jnz L8 /* if %EDI == 0, result should be negative */
|
||||
neg %edx /* otherwise, negate the result */
|
||||
neg %eax
|
||||
sbb $0,%edx
|
||||
|
||||
/* Restore the saved registers and return. */
|
||||
L8:
|
||||
pop %ebx
|
||||
pop %esi
|
||||
pop %edi
|
||||
|
||||
ret $16
|
||||
|
||||
GLOBAL_ENTRY_END(x86Div64Bit)
|
||||
|
||||
/*
|
||||
* llrem
|
||||
*
|
||||
* Origin: MSDev
|
||||
* Purpose: signed long remainder
|
||||
* In: args are passed on the stack:
|
||||
* 1st pushed: divisor (QWORD)
|
||||
* 2nd pushed: dividend (QWORD)
|
||||
* Out: %EDX:%EAX contains the quotient (dividend/divisor)
|
||||
* Note: parameters are removed from the stack
|
||||
* Uses: %ECX
|
||||
*/
|
||||
GLOBAL_ENTRY_START(x86Mod64Bit)
|
||||
|
||||
push %ebx
|
||||
push %edi
|
||||
|
||||
/* Determine sign of the result (%edi = 0 if result is positive, non-zero
|
||||
* otherwise) and make operands positive.
|
||||
*/
|
||||
xor %edi,%edi /* result sign assumed positive */
|
||||
|
||||
mov 16(%esp),%eax /* hi word of a */
|
||||
or %eax,%eax /* test to see if signed */
|
||||
jge LL1 /* skip rest if a is already positive */
|
||||
inc %edi /* complement result sign flag bit */
|
||||
mov 12(%esp),%edx /* lo word of a */
|
||||
neg %eax /* make a positive */
|
||||
neg %edx
|
||||
sbb $0,%eax
|
||||
mov %eax,16(%esp) /* save positive value */
|
||||
mov %edx,12(%esp)
|
||||
LL1:
|
||||
mov 24(%esp),%eax /* hi word of b */
|
||||
or %eax,%eax /* test to see if signed */
|
||||
jge LL2 /* skip rest if b is already positive */
|
||||
mov 20(%esp),%edx /* lo word of b */
|
||||
neg %eax /* make b positive */
|
||||
neg %edx
|
||||
sbb $0,%eax
|
||||
mov %eax,24(%esp) /* save positive value */
|
||||
mov %edx,20(%esp)
|
||||
LL2:
|
||||
/* Now do the divide. First look to see if the divisor is less than 4194304K.
|
||||
* If so, then we can use a simple algorithm with word divides, otherwise
|
||||
* things get a little more complex.
|
||||
* NOTE - %eax currently contains the high order word of DVSR
|
||||
*/
|
||||
or %eax,%eax /* check to see if divisor < 4194304K */
|
||||
jnz LL3 /* nope, gotta do this the hard way */
|
||||
mov 20(%esp),%ecx /* load divisor */
|
||||
mov 16(%esp),%eax /* load high word of dividend */
|
||||
xor %edx,%edx
|
||||
div %ecx /* %edx <- remainder */
|
||||
mov 12(%esp),%eax /* %edx:%eax <- remainder:lo word of dividend */
|
||||
div %ecx /* %edx <- final remainder */
|
||||
mov %edx,%eax /* %edx:%eax <- remainder */
|
||||
xor %edx,%edx
|
||||
dec %edi /* check result sign flag */
|
||||
jns LL4 /* negate result, restore stack and return */
|
||||
jmp LL8 /* result sign ok, restore stack and return */
|
||||
|
||||
/* Here we do it the hard way. Remember, %eax contains the high word of DVSR */
|
||||
LL3:
|
||||
mov %eax,%ebx /* %ebx:%ecx <- divisor */
|
||||
mov 20(%esp),%ecx
|
||||
mov 16(%esp),%edx /* %edx:%eax <- dividend */
|
||||
mov 12(%esp),%eax
|
||||
LL5:
|
||||
shr $1,%ebx /* shift divisor right one bit */
|
||||
rcr $1,%ecx
|
||||
shr $1,%edx /* shift dividend right one bit */
|
||||
rcr $1,%eax
|
||||
or %ebx,%ebx
|
||||
jnz LL5 /* loop until divisor < 4194304K */
|
||||
div %ecx /* now divide, ignore remainder */
|
||||
|
||||
/* We may be off by one, so to check, we will multiply the quotient
|
||||
* by the divisor and check the result against the orignal dividend
|
||||
* Note that we must also check for overflow, which can occur if the
|
||||
* dividend is close to 2**64 and the quotient is off by 1.
|
||||
*/
|
||||
mov %eax,%ecx /* save a copy of quotient in %ECX */
|
||||
mull 24(%esp)
|
||||
xchg %eax,%ecx /* save product, get quotient in %EAX */
|
||||
mull 20(%esp)
|
||||
add %ecx,%edx /* %EDX:%EAX = QUOT * DVSR */
|
||||
jc LL6 /* carry means Quotient is off by 1 */
|
||||
|
||||
/* do long compare here between original dividend and the result of the
|
||||
* multiply in %edx:%eax. If original is larger or equal, we are ok, otherwise
|
||||
* subtract the original divisor from the result.
|
||||
*/
|
||||
cmp 16(%esp),%edx /* compare hi words of result and original */
|
||||
ja LL6 /* if result > original, do subtract */
|
||||
jb LL7 /* if result < original, we are ok */
|
||||
cmp 12(%esp),%eax /* hi words are equal, compare lo words */
|
||||
jbe LL7 /* if less or equal we are ok, else subtract */
|
||||
LL6:
|
||||
sub 20(%esp),%eax /* subtract divisor from result */
|
||||
sbb 24(%esp),%edx
|
||||
LL7:
|
||||
|
||||
/* Calculate remainder by subtracting the result from the original dividend.
|
||||
* Since the result is already in a register, we will do the subtract in the
|
||||
* opposite direction and negate the result if necessary.
|
||||
*/
|
||||
sub 12(%esp),%eax /* subtract dividend from result */
|
||||
sbb 16(%esp),%edx
|
||||
|
||||
/* Now check the result sign flag to see if the result is supposed to be positive
|
||||
* or negative. It is currently negated (because we subtracted in the 'wrong'
|
||||
* direction), so if the sign flag is set we are done, otherwise we must negate
|
||||
* the result to make it positive again.
|
||||
*/
|
||||
dec %edi /* check result sign flag */
|
||||
jns LL8 /* result is ok, restore stack and return */
|
||||
LL4:
|
||||
neg %edx /* otherwise, negate the result */
|
||||
neg %eax
|
||||
sbb $0,%edx
|
||||
|
||||
/* Just the cleanup left to do. %edx:%eax contains the quotient.
|
||||
* Restore the saved registers and return.
|
||||
*/
|
||||
LL8:
|
||||
pop %edi
|
||||
pop %ebx
|
||||
|
||||
ret $16
|
||||
|
||||
GLOBAL_ENTRY_END(x86Mod64Bit)
|
||||
|
||||
/*
|
||||
* llshl
|
||||
*
|
||||
* Origin: MSDev. modified
|
||||
* Purpose: long shift left
|
||||
* In: args are passed on the stack: (FIX make fastcall)
|
||||
* 1st pushed: amount (int)
|
||||
* 2nd pushed: source (long)
|
||||
* Out: %EDX:%EAX contains the result
|
||||
* Note: parameters are removed from the stack
|
||||
* Uses: %ECX, destroyed
|
||||
*/
|
||||
GLOBAL_ENTRY_START(x86Shl64Bit)
|
||||
|
||||
/* prepare from stack */
|
||||
mov 4(%esp),%eax
|
||||
mov 8(%esp),%edx
|
||||
mov 12(%esp),%ecx
|
||||
|
||||
cmp $64,%cl
|
||||
jae RETZERO
|
||||
|
||||
/* Handle shifts of between 0 and 31 bits */
|
||||
cmp $32,%cl
|
||||
jae MORE32
|
||||
shld %eax,%edx
|
||||
shl %cl,%eax
|
||||
ret $12
|
||||
|
||||
/* Handle shifts of between 32 and 63 bits */
|
||||
MORE32:
|
||||
mov %eax,%edx
|
||||
xor %eax,%eax
|
||||
and $31,%cl
|
||||
shl %cl,%edx
|
||||
ret $12
|
||||
|
||||
/* return 0 in %edx:%eax */
|
||||
RETZERO:
|
||||
xor %eax,%eax
|
||||
xor %edx,%edx
|
||||
ret $12
|
||||
|
||||
GLOBAL_ENTRY_END(x86Shl64Bit)
|
||||
|
||||
|
||||
/*
|
||||
* llshr
|
||||
*
|
||||
* Origin: MSDev. modified
|
||||
* Purpose: long shift right
|
||||
* In: args are passed on the stack: (FIX make fastcall)
|
||||
* 1st pushed: amount (int)
|
||||
* 2nd pushed: source (long)
|
||||
* Out: %EDX:%EAX contains the result
|
||||
* Note: parameters are removed from the stack
|
||||
* Uses: %ECX, destroyed
|
||||
*/
|
||||
GLOBAL_ENTRY_START(x86Shr64Bit)
|
||||
|
||||
/* prepare from stack */
|
||||
mov 4(%esp),%eax
|
||||
mov 8(%esp),%edx
|
||||
mov 12(%esp),%ecx
|
||||
|
||||
cmp $64,%cl
|
||||
jae RRETZERO
|
||||
|
||||
/* Handle shifts of between 0 and 31 bits */
|
||||
cmp $32,%cl
|
||||
jae MMORE32
|
||||
shrd %edx,%eax
|
||||
shr %cl,%edx
|
||||
ret $12
|
||||
|
||||
/* Handle shifts of between 32 and 63 bits */
|
||||
MMORE32:
|
||||
mov %edx,%eax
|
||||
xor %edx,%edx
|
||||
and $31,%cl
|
||||
shr %cl,%eax
|
||||
ret $12
|
||||
|
||||
/* return 0 in %edx:%eax */
|
||||
RRETZERO:
|
||||
xor %eax,%eax
|
||||
xor %edx,%edx
|
||||
ret $12
|
||||
|
||||
GLOBAL_ENTRY_END(x86Shr64Bit)
|
||||
|
||||
/*
|
||||
* llsar
|
||||
*
|
||||
* Origin: MSDev. modified
|
||||
* Purpose: long shift right signed
|
||||
* In: args are passed on the stack: (FIX make fastcall)
|
||||
* 1st pushed: amount (int)
|
||||
* 2nd pushed: source (long)
|
||||
* Out: %EDX:%EAX contains the result
|
||||
* Note: parameters are removed from the stack
|
||||
* Uses: %ECX, destroyed
|
||||
*/
|
||||
GLOBAL_ENTRY_START(x86Sar64Bit)
|
||||
|
||||
/* prepare from stack */
|
||||
mov 4(%esp),%eax
|
||||
mov 8(%esp),%edx
|
||||
mov 12(%esp),%ecx
|
||||
|
||||
/* Handle shifts of 64 bits or more (if shifting 64 bits or more, the result */
|
||||
/* depends only on the high order bit of %edx). */
|
||||
cmp $64,%cl
|
||||
jae RETSIGN
|
||||
|
||||
/* Handle shifts of between 0 and 31 bits */
|
||||
cmp $32,%cl
|
||||
jae MMMORE32
|
||||
shrd %edx,%eax
|
||||
sar %cl,%edx
|
||||
ret $12
|
||||
|
||||
/* Handle shifts of between 32 and 63 bits */
|
||||
MMMORE32:
|
||||
mov %edx,%eax
|
||||
sar $31,%edx
|
||||
and $31,%cl
|
||||
sar %cl,%eax
|
||||
ret $12
|
||||
|
||||
/* Return double precision 0 or -1, depending on the sign of %edx */
|
||||
RETSIGN:
|
||||
sar $31,%edx
|
||||
mov %edx,%eax
|
||||
ret $12
|
||||
|
||||
GLOBAL_ENTRY_END(x86Sar64Bit)
|
|
@ -0,0 +1,125 @@
|
|||
/* -*- 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.
|
||||
*/
|
||||
/*
|
||||
x86Linux_Support.cpp
|
||||
*/
|
||||
|
||||
#include "NativeCodeCache.h"
|
||||
#include <string.h>
|
||||
#include "Fundamentals.h"
|
||||
#include "MemoryAccess.h"
|
||||
#include "x86Linux_Support.h"
|
||||
|
||||
|
||||
void* JNIenv = 0;
|
||||
|
||||
extern ClassWorld world;
|
||||
|
||||
#ifdef DEBUG
|
||||
// Pointer to the instruction after the call (used by exception handler to check
|
||||
// I wanted to use:
|
||||
// void* compileStubReEntryPoint = (void*) ((Uint8*)staticCompileStub + 17);
|
||||
// but MSDev appears to have a bug, in that compileStubReEntryPoint will be set == (void*)staticCompileStub
|
||||
// which is clearly wrong.
|
||||
void* compileStubAddress = (void*)staticCompileStub;
|
||||
void* compileStubReEntryPoint = (Uint8*)compileStubAddress + 17;
|
||||
#endif // DEBUG
|
||||
|
||||
void *
|
||||
generateNativeStub(NativeCodeCache& inCache, const CacheEntry& inCacheEntry, void *nativeFunction)
|
||||
{
|
||||
Method* method = inCacheEntry.descriptor.method;
|
||||
Uint32 nWords = method->getSignature().nArguments;
|
||||
|
||||
assert(method->getModifiers() & CR_METHOD_NATIVE);
|
||||
|
||||
extern Uint32 sysInvokeNativeStubs[];
|
||||
Uint8 stubSize = 10;
|
||||
void* stub;
|
||||
|
||||
// Write out the native stub
|
||||
stub = inCache.acquireMemory(stubSize);
|
||||
Uint8* where = (Uint8*)stub;
|
||||
*where++ = 0x68; // pushl
|
||||
writeLittleWordUnaligned(where, (uint32)(nativeFunction));
|
||||
where += 4;
|
||||
*where++ = 0xe9; // jmp
|
||||
writeLittleWordUnaligned(where, (Uint8 *) sysInvokeNativeStubs[nWords] - (where + 4));
|
||||
|
||||
// Return the address of the stub.
|
||||
return ((void*)stub);
|
||||
}
|
||||
|
||||
void *
|
||||
generateJNIGlue(NativeCodeCache& inCache,
|
||||
const CacheEntry& inCacheEntry,
|
||||
void *nativeFunction)
|
||||
{
|
||||
void* stub;
|
||||
Method* method = inCacheEntry.descriptor.method;
|
||||
assert(method->getModifiers() & CR_METHOD_NATIVE);
|
||||
|
||||
Uint8 stubSize = 12;
|
||||
|
||||
// Write out the JNI compile stub
|
||||
stub = inCache.acquireMemory(stubSize);
|
||||
Uint8* where = (Uint8*) stub;
|
||||
*where++ = 0x58; // popl %eax
|
||||
*where++ = 0x68; // pushl
|
||||
writeLittleWordUnaligned(where, (Uint32) JNIenv);
|
||||
where += 4;
|
||||
*where++ = 0xe9; // jmp
|
||||
writeLittleWordUnaligned(where, reinterpret_cast<Uint32>(nativeFunction) - Uint32(where + 4));
|
||||
return stub;
|
||||
}
|
||||
|
||||
void *
|
||||
generateCompileStub(NativeCodeCache& inCache, const CacheEntry& inCacheEntry)
|
||||
{
|
||||
void* stub;
|
||||
uint8 stubSize = 10;
|
||||
|
||||
// Write out the dynamic compile stub
|
||||
stub = inCache.acquireMemory(stubSize);
|
||||
Uint8* where = (Uint8*)stub;
|
||||
*where++ = 0x68; // pushl
|
||||
writeLittleWordUnaligned(where, (uint32)(&inCacheEntry));
|
||||
where += 4;
|
||||
*where++ = 0xe9; // jmp
|
||||
writeLittleWordUnaligned(where, (Uint8 *) staticCompileStub - (where + 4));
|
||||
|
||||
// Return the address of the dynamic stub.
|
||||
return ((void*)stub);
|
||||
}
|
||||
|
||||
void*
|
||||
backPatchMethod(void* inMethodAddress, void* inLastPC, void* /*inUserDefined*/)
|
||||
{
|
||||
|
||||
uint32 curAddress = (uint32) inLastPC;
|
||||
uint32 methodAddress = (uint32) inMethodAddress;
|
||||
|
||||
// Compute the relative branch
|
||||
uint32* relativeBranch = ((uint32*)inLastPC)-1;
|
||||
int32 offset = methodAddress - curAddress;
|
||||
|
||||
// Backpatch the method.
|
||||
writeLittleWordUnaligned((void*)relativeBranch, offset);
|
||||
|
||||
return (inMethodAddress);
|
||||
}
|
|
@ -0,0 +1,230 @@
|
|||
/* -*- 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.
|
||||
*/
|
||||
/*
|
||||
desantis
|
||||
holmes a court (commenting)
|
||||
|
||||
x86Opcode.cpp
|
||||
|
||||
NOTE:
|
||||
This file is being phased out -- please do not add to it without speaking to simon
|
||||
*/
|
||||
|
||||
#include "CatchAssert.h"
|
||||
|
||||
#include "x86Opcode.h"
|
||||
#include "x86ArgumentList.h"
|
||||
|
||||
// Not used anywhere
|
||||
// { 0x69, 0x00, "imul " } //iaMulImm 69 0000 0000
|
||||
|
||||
//================================================================================
|
||||
/* Opcode Information
|
||||
|
||||
Each OpcodeInfo consists of
|
||||
x86OpcodeInfo {
|
||||
oiBaseOpcode // generally the opcode, but can have additional info
|
||||
oiOpcodeInformation // additional info about opcode
|
||||
oiText // string for fake disassembly
|
||||
}
|
||||
|
||||
Field bits are referred to as [7 6 5 4 3 2 1 0]
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
ImmediateArithType
|
||||
eg:
|
||||
1000 00s1: 11 111 reg:immdata immediate with register
|
||||
1000 00s1:mod 111 r/m:immdata immediate with memory
|
||||
|
||||
oiBaseOpcode
|
||||
contains the opcode for the instruction
|
||||
1 (Size) always 0 (assigned at format time)
|
||||
|
||||
oiOpcodeInformation
|
||||
7 set if two byte opcode
|
||||
6 set if has an opcode extension
|
||||
[5 4 3] opcode extension of the instructrion
|
||||
1 set if must take a 1-byte immediate
|
||||
0 set if must take a 4-byte immediate
|
||||
others set to 0
|
||||
*/
|
||||
x86OpcodeInfo iaInfo[] =
|
||||
{
|
||||
// ** *
|
||||
{ 0x81, 0x40, "add " }, //iaAddImm 81/0 0100 0000
|
||||
{ 0x81, 0x78, "cmp " }, //iaCmpImm 81/7 0111 1000
|
||||
{ 0xe9, 0x40, "jmp " }, //iaJmp e9/0 0100 0000
|
||||
{ 0x68, 0x40, "push "}, //iaPushImm 68/0 0100 0000
|
||||
{ 0x81, 0x60, "and "}, //iaAndImm 81/4 0110 0000
|
||||
{ 0x81, 0x48, "or "}, //iaOrImm 81/1 0100 1000
|
||||
{ 0x81, 0x70, "xor "}, //iaXorImm 81/6 0111 0000
|
||||
};
|
||||
|
||||
/*--------------------------------------------------------------------------------
|
||||
ExtendedType
|
||||
eg
|
||||
1100 0001: 11 111 reg:imm8data reg by immediate count
|
||||
1100 0001:mod 111 r/m:imm8data memory by immediate count
|
||||
|
||||
oiBaseOpcode
|
||||
contains the opcode for the instruction
|
||||
|
||||
oiOpcodeInformation
|
||||
7 set if two byte opcode
|
||||
[5 4 3] contain Regfield opcode extension
|
||||
1 set if can take a 1-byte immediate
|
||||
0 set if can take a 4-byte immediate
|
||||
others set to 0
|
||||
*/
|
||||
x86OpcodeInfo eInfo[] =
|
||||
{ // ** *
|
||||
{ 0xf7, 0x38, "idiv eax, " }, //eIDiv f7/7 0011 1000
|
||||
{ 0xf7, 0x30, "div eax, " }, //eDiv f7/6 0011 0000
|
||||
{ 0xf7, 0x20, "div eax, " }, //eMul f7/4 0010 0000
|
||||
{ 0xc7, 0x01, "movImm " }, //eMoveImm c7/0 0000 0001 // used when won't be condensable
|
||||
{ 0xff, 0x30, "push "}, //ePush ff/6 0011 0000 // FIX Peter? (what's wrong?)
|
||||
{ 0xc1, 0x3a, "sar "}, //eSarImm c1/7 0011 1010
|
||||
{ 0xc1, 0x2a, "shr "}, //eShrImm c1/5 0010 1010
|
||||
{ 0xc1, 0x22, "shl "}, //eShlImm c1/4 0010 0010
|
||||
{ 0xd1, 7<<3, "sar(by1) "}, //eSar1 c1/7 0011 1000
|
||||
{ 0xd1, 5<<3, "shr(by1) "}, //eShr1 c1/5 0010 1000
|
||||
{ 0xd1, 4<<3, "shl(by1) "}, //eShl1 c1/4 0010 0000
|
||||
{ 0xd3, 7<<3, "sar(bycl) "}, //eSarCl c1/7 0011 1000
|
||||
{ 0xd3, 5<<3, "shr(bycl) "}, //eShrCl c1/5 0010 1000
|
||||
{ 0xd3, 4<<3, "shl(bycl) "}, //eShlCl c1/4 0010 0000
|
||||
{ 0xf7, 3<<3, "neg "}, //eNeg f7/3 0001 1000
|
||||
};
|
||||
|
||||
/*--------------------------------------------------------------------------------
|
||||
CondensableExtendedType
|
||||
Like ExtendedOpcodes but have alternate (condensed) encoding: 5-bit opcode + 3-bit reg operand
|
||||
eg
|
||||
0100 0 rg reg (preferred encoding)
|
||||
1111 1111:mod 000 r/m memory
|
||||
|
||||
oiBaseOpcode
|
||||
contains the opcode for the instruction
|
||||
|
||||
oiOpcodeInformation
|
||||
[7 6 5 4 3] register condensed form opcode
|
||||
[2 1 0] regfield extension in extended form
|
||||
|
||||
NOTE: These opcodes cannot be two bytes and must take 4 BYTE IMMEDIATES
|
||||
*/
|
||||
|
||||
x86OpcodeInfo ceInfo[] =
|
||||
{ // norm condns <cond><x>
|
||||
{ 0xff, 0x40, "inc " }, //ceInc ff/0 40+reg 0100 0000
|
||||
{ 0xff, 0x49, "dec " }, //ceDec ff/1 48+reg 0100 1001
|
||||
{ 0xc7, 0xb8, "movImm " }, //ceMoveImm c7/0 b8+reg 1011 1000
|
||||
{ 0xff, 0x46, "pop " } //cePop ff/0 40+reg 0101 0110 // 50+rd, ff/6
|
||||
};
|
||||
|
||||
|
||||
/*--------------------------------------------------------------------------------
|
||||
SpecialRegMemType
|
||||
|
||||
eg [cmp vrx, 0] is the same as [test vrx, vrx] but the latter saves a byte
|
||||
eg [mov vrx, 0] = [xor vrx, vrx] but the latter saves 4 bytes
|
||||
|
||||
Optimisations like the above can only be applied to reg/reg
|
||||
|
||||
oiBaseOpcode
|
||||
contains the opcode for the instruction
|
||||
|
||||
oiOpcodeInformation
|
||||
7 set if two byte opcode
|
||||
6 0 means register (StandardType) form
|
||||
1 means memory (ExtendedType) form
|
||||
[5 4 3] contain Regfield opcode extension for ExtendedType form
|
||||
|
||||
1 set if can take a 1-byte immediate
|
||||
0 set if can take a 4-byte immediate
|
||||
|
||||
Pairing
|
||||
register form followed by memory form
|
||||
*/
|
||||
|
||||
x86OpcodeInfo srmInfo[] =
|
||||
{
|
||||
// cmp vrx, 0 = test vrx, vrx (save 1 byte)
|
||||
{0x85, 0x00, "test "}, //srmCmpImm0 85/r 0000 0000 (2 bytes)
|
||||
{0x83, 0x7a, "cmp "}, //srm1NOTUSED 83/7 0111 1010 (2+1 bytes)
|
||||
|
||||
// mov vrx, 0 = xor vrx, vrx (save 4 bytes)
|
||||
{0x31, 0x00, "xor "}, //srmMoveImm0 31/r 0000 0000 (2 bytes)
|
||||
{0xc7, 0x41, "mov "} //srm3NOTUSED c7/0 0100 0001 (2+4 bytes)
|
||||
};
|
||||
|
||||
//================================================================================
|
||||
// Member functions
|
||||
|
||||
/*--------------------------------------------------------------------------------
|
||||
x86Opcode_ImmArith -- encodes x86ImmediateArithType
|
||||
|
||||
- Sign/size bit which specifies 8 or 32 bits operand
|
||||
- _possible_ three bit opcode extension (stored in the REG field of the MODR/M byte)
|
||||
*/
|
||||
void x86Opcode_ImmArith::
|
||||
opFormatToMemory(void* inStart, x86ArgumentList& inAL, x86ArgListInstruction& /*inInsn*/ )
|
||||
{
|
||||
Uint8* start = (Uint8*) inStart;
|
||||
|
||||
if(opInfo->oiOpcodeInformation & kIs_2_Byte_Mask)
|
||||
*start++ = kPrefix_For_2_Byte;
|
||||
|
||||
switch(inAL.alSizeOfImmediate())
|
||||
{
|
||||
case(is4ByteImmediate):
|
||||
*start = opInfo->oiBaseOpcode;
|
||||
break;
|
||||
|
||||
case(is1ByteImmediate):
|
||||
*start = opInfo->oiBaseOpcode | (0x02);
|
||||
break;
|
||||
|
||||
default:
|
||||
assert(false); // FIX what other possibilities are there?
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------
|
||||
//x86Opcode_Condensable_Reg -- encodes x86CondensableExtendedType
|
||||
void x86Opcode_Condensable_Reg::
|
||||
opFormatToMemory(void* inStart, x86ArgumentList& inAL, x86ArgListInstruction& inInsn)
|
||||
{
|
||||
Uint8* start = (Uint8*) inStart;
|
||||
Uint8 reg = inAL.alGetRegisterOperand(inInsn);
|
||||
|
||||
if(reg == NOT_A_REG) // Operand is not a register
|
||||
*start = opInfo->oiBaseOpcode;
|
||||
else
|
||||
*start = (opInfo->oiOpcodeInformation & kCondensed_Op_Mask) | reg; // 5-bit opcode + 3-bit reg
|
||||
}
|
||||
|
||||
bool x86Opcode_Condensable_Reg::
|
||||
opHasRegFieldExtension(x86ArgumentList& inAL, x86ArgListInstruction& inInsn)
|
||||
{
|
||||
Uint8 reg = inAL.alGetRegisterOperand( inInsn );
|
||||
if(reg == NOT_A_REG) // Operand is not a register
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------
|
|
@ -0,0 +1,377 @@
|
|||
/* -*- 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.
|
||||
*/
|
||||
/*
|
||||
x86Opcode.h
|
||||
|
||||
desantis
|
||||
simon
|
||||
|
||||
NOTE:
|
||||
This file is being phased out -- please do not add to it without speaking to simon
|
||||
*/
|
||||
|
||||
#ifndef _X86OPCODE
|
||||
#define _X86OPCODE
|
||||
|
||||
#include "CatchAssert.h"
|
||||
#include <stdio.h>
|
||||
|
||||
#include "Fundamentals.h"
|
||||
#include "LogModule.h"
|
||||
|
||||
class x86ArgumentList;
|
||||
class x86ArgListInstruction;
|
||||
|
||||
//================================================================================
|
||||
// enums
|
||||
enum x86NoArgsCode
|
||||
{
|
||||
opCdq,
|
||||
opBreak,
|
||||
opSahf
|
||||
};
|
||||
|
||||
enum x86DoubleOpCode
|
||||
{
|
||||
opMul,
|
||||
opMovSxB,
|
||||
opMovSxH,
|
||||
opMovZxB,
|
||||
opMovZxH
|
||||
};
|
||||
|
||||
enum x86DoubleOpDirCode
|
||||
{
|
||||
raAdd,
|
||||
raAdc,
|
||||
raSub,
|
||||
raSbb,
|
||||
raAnd,
|
||||
raOr,
|
||||
raXor,
|
||||
raCmp,
|
||||
|
||||
raLoadI,
|
||||
raCopyI,
|
||||
raStoreI,
|
||||
raSaveReg,
|
||||
|
||||
raStoreB,
|
||||
raStoreH
|
||||
};
|
||||
|
||||
// Floating-point instructions in which one operand is a memory location.
|
||||
// If there is a second operand, then it is implicitly the top of the FPU stack.
|
||||
enum x86FloatMemoryType
|
||||
{
|
||||
fstp32, // TOS => 32-bit float memory. Pop FPU.
|
||||
fstp64, // TOS => 64-bit double memory. Pop FPU.
|
||||
fst32, // TOS => 32-bit float memory. (Don't pop FPU stack.)
|
||||
fst64, // TOS => 64-bit double memory. (Don't pop FPU stack.)
|
||||
fistp32, // Round(TOS) => 32-bit int memory. Pop FPU.
|
||||
fistp64, // Round(TOS) => 64-bit long memory. Pop FPU.
|
||||
fld32, // 32-bit float memory => Push on FPU stack
|
||||
fld64, // 64-bit float memory => Push on FPU stack
|
||||
fild32, // 32-bit int memory => convert to FP and push on FPU stack
|
||||
fild64, // 64-bit long memory => convert to FP and push on FPU stack
|
||||
fadd32, // Add TOS and 32-bit float memory => replace TOS
|
||||
fadd64, // Add TOS and 64-bit double memory => replace TOS
|
||||
fmul32, // Multiply TOS and 32-bit float memory => replace TOS
|
||||
fmul64, // Multiply TOS and 64-bit double memory => replace TOS
|
||||
fsub32, // Subtract TOS from 32-bit float memory => replace TOS
|
||||
fsub64, // Subtract TOS from 64-bit double memory => replace TOS
|
||||
fsubr32, // Subtract 32-bit float memory from TOS => replace TOS
|
||||
fsubr64, // Subtract 64-bit double memory from TOS => replace TOS
|
||||
fdiv32, // Divide TOS by 32-bit float memory => replace TOS
|
||||
fdiv64, // Divide TOS by 64-bit double memory => replace TOS
|
||||
fcomp32, // Compare TOS to 32-bit float memory, setting FPU flags, pop TOS
|
||||
fcomp64 // Compare TOS to 64-bit double memory, setting FPU flags, pop TOS
|
||||
};
|
||||
|
||||
// enums for the x96 conidtion code
|
||||
enum x86ConditionCode
|
||||
{
|
||||
ccJO = 0x00,
|
||||
ccJNO = 0x01,
|
||||
ccJB = 0x02,
|
||||
ccJNB = 0x03,
|
||||
ccJE = 0x04,
|
||||
ccJNE = 0x05,
|
||||
ccJBE = 0x06,
|
||||
ccJNBE = 0x07,
|
||||
ccJS = 0x08,
|
||||
ccJNS = 0x09,
|
||||
ccJP = 0x0a,
|
||||
ccJNP = 0x0b,
|
||||
ccJL = 0x0c,
|
||||
ccJNL = 0x0d,
|
||||
ccJLE = 0x0e,
|
||||
ccJNLE = 0x0f
|
||||
};
|
||||
|
||||
/*================================================================================
|
||||
x86 Opcodes
|
||||
- Can be one byte or two bytes in length. If two bytes, the first byte is 0x0f.
|
||||
- Can have bits to control:
|
||||
- data flow direction(D),
|
||||
- sign extension(S) // FIX pete, isn't this 'size' not 'sign'?
|
||||
- conditions (cond), and
|
||||
- register field
|
||||
- opcode extensions (Reg).
|
||||
- Can have an alternate (condensed) encoding which allows a single register operand to be encoded in the opcode.
|
||||
*/
|
||||
|
||||
enum x86GPR
|
||||
{
|
||||
EAX = 0,
|
||||
ECX = 1,
|
||||
EDX = 2,
|
||||
EBX = 3,
|
||||
ESP = 4,
|
||||
EBP = 5,
|
||||
ESI = 6,
|
||||
EDI = 7,
|
||||
NOT_A_REG = 255
|
||||
};
|
||||
|
||||
// bit masks
|
||||
const int kRegfield_Mask = 0x38; // 0b00111000
|
||||
const int kIs_Memory_Form_Mask = 0x40; // 0b01000000
|
||||
const int kCondition_Code_Mask = 0x0f; // 0b00001111
|
||||
const int kCondensed_Op_Mask = 0xf8; // 0b11111000
|
||||
const int kIs_2_Byte_Mask = 0x80; // 0b10000000
|
||||
const int kIs_16_Bit_Mask = 0x40; // 0b01000000
|
||||
const int kExtension_Mask = 0x40; // 0b01000000
|
||||
|
||||
const int kPrefix_For_16_Bits = 0x66;
|
||||
const int kPrefix_For_2_Byte = 0x0f; // 0b00001111
|
||||
|
||||
/*--------------------------------------------------------------------------------
|
||||
Opcode Information
|
||||
*/
|
||||
struct x86OpcodeInfo
|
||||
{
|
||||
uint8 oiBaseOpcode; // generally the opcode, but can have additional info
|
||||
uint8 oiOpcodeInformation; // additional info about opcode
|
||||
char* oiText; // string for fake disassembly
|
||||
};
|
||||
|
||||
extern x86OpcodeInfo iaInfo[];
|
||||
enum x86ImmediateArithType
|
||||
{
|
||||
iaAddImm,
|
||||
iaCmpImm,
|
||||
iaJmp,
|
||||
iaPushImm,
|
||||
iaAndImm,
|
||||
iaOrImm,
|
||||
iaXorImm
|
||||
};
|
||||
|
||||
extern x86OpcodeInfo eInfo[];
|
||||
enum x86ExtendedType
|
||||
{
|
||||
eIDiv,
|
||||
eDiv,
|
||||
eMul,
|
||||
eMoveImm,
|
||||
ePush,
|
||||
eSarImm,
|
||||
eShrImm,
|
||||
eShlImm,
|
||||
eSar1,
|
||||
eShr1,
|
||||
eShl1,
|
||||
eSarCl,
|
||||
eShrCl,
|
||||
eShlCl,
|
||||
eNeg
|
||||
};
|
||||
|
||||
extern x86OpcodeInfo ceInfo[];
|
||||
enum x86CondensableExtendedType
|
||||
{
|
||||
ceInc,
|
||||
ceDec,
|
||||
ceMoveImm,
|
||||
cePop
|
||||
};
|
||||
|
||||
extern x86OpcodeInfo srmInfo[];
|
||||
enum x86SpecialRegMemType
|
||||
{
|
||||
srmCmpImm0,
|
||||
srm1NOTUSED,
|
||||
srmMoveImm0,
|
||||
srm3NOTUSED
|
||||
};
|
||||
|
||||
//================================================================================
|
||||
// Class Declarations
|
||||
|
||||
/*--------------------------------------------------------------------------------
|
||||
x86Opcode -- encodes x86StandardType
|
||||
|
||||
Basic opcode
|
||||
- 8 or 16 bits
|
||||
- no toggable bits and
|
||||
- does NOT have a register field opcode extension or alternate encoding.
|
||||
*/
|
||||
class x86Opcode
|
||||
{
|
||||
public:
|
||||
virtual void opPrintPretty(LogModuleObject &f) { UT_OBJECTLOG(f, PR_LOG_ALWAYS, ("%-9s", opInfo->oiText)); }
|
||||
virtual uint8 opSize() { return( (opInfo->oiOpcodeInformation >> 7) + 1); }
|
||||
virtual void opFormatToMemory(void* inStart, x86ArgumentList& /*inAL*/, x86ArgListInstruction& /*inInsn*/);
|
||||
|
||||
virtual bool opHasRegFieldExtension( x86ArgumentList& /*inAL*/, x86ArgListInstruction& /*inInsn*/ ) { return (false); }
|
||||
virtual uint8 opGetRegFieldExtension() { assert(false); return 0; }
|
||||
|
||||
virtual bool opRegOperandCanBeCondensed() { return false; }
|
||||
|
||||
virtual void opReverseOperands() { assert(false); }
|
||||
|
||||
virtual void opSwitchToRegisterIndirect() { }
|
||||
|
||||
virtual bool opCanAccept1ByteImmediate() { assert(false); return false; }
|
||||
virtual bool opCanAccept4ByteImmediate() { assert(false); return false; }
|
||||
|
||||
protected:
|
||||
x86Opcode() {}
|
||||
x86Opcode( x86ImmediateArithType inType ) { opInfo = &(iaInfo[inType]); }
|
||||
x86Opcode( x86ExtendedType inType ) { opInfo = &(eInfo[inType]); }
|
||||
x86Opcode( x86CondensableExtendedType inType ) { opInfo = &(ceInfo[inType]); }
|
||||
x86Opcode( x86SpecialRegMemType inType ) { opInfo = &(srmInfo[inType]); }
|
||||
x86OpcodeInfo* opInfo;
|
||||
};
|
||||
|
||||
inline void x86Opcode::
|
||||
opFormatToMemory(void* inStart, x86ArgumentList& /*inAL*/, x86ArgListInstruction& /*inInsn*/)
|
||||
{
|
||||
uint8* start = (uint8*) inStart;
|
||||
if(opInfo->oiOpcodeInformation & kIs_2_Byte_Mask)
|
||||
*start++ = kPrefix_For_2_Byte;
|
||||
*start = opInfo->oiBaseOpcode;
|
||||
}
|
||||
|
||||
/*--------------------------------------------------------------------------------
|
||||
x86Opcode_Reg -- encodes x86EntendedType
|
||||
|
||||
- no switchable bits
|
||||
- three bit opcode extension (stored in the REG field of the MODR/M byte)
|
||||
*/
|
||||
class x86Opcode_Reg :
|
||||
public virtual x86Opcode
|
||||
{
|
||||
public:
|
||||
x86Opcode_Reg ( x86ExtendedType inInfo ) : x86Opcode(inInfo) { }
|
||||
|
||||
virtual bool opHasRegFieldExtension( x86ArgumentList& /*inAL*/, x86ArgListInstruction& /*inInsn*/ ) { return true; }
|
||||
virtual uint8 opGetRegFieldExtension() { return (kRegfield_Mask & opInfo->oiOpcodeInformation); }
|
||||
|
||||
virtual bool opCanAccept1ByteImmediate() { return ((0x02 & opInfo->oiOpcodeInformation) == 0x02); }
|
||||
virtual bool opCanAccept4ByteImmediate() { return ((0x01 & opInfo->oiOpcodeInformation) == 0x01); }
|
||||
|
||||
protected:
|
||||
x86Opcode_Reg() { }
|
||||
};
|
||||
|
||||
/*--------------------------------------------------------------------------------
|
||||
x86Opcode_Condensable_Reg -- encodes x86CondensableExtendedType
|
||||
|
||||
- no switchable bits
|
||||
- three bit opcode extension (stored in the REG field of the MODR/M byte)
|
||||
- alternate encoding which has a 5-bit opcode + 3-bit register operand
|
||||
*/
|
||||
class x86Opcode_Condensable_Reg :
|
||||
public x86Opcode
|
||||
{
|
||||
public:
|
||||
x86Opcode_Condensable_Reg ( x86CondensableExtendedType inInfo ) : x86Opcode(inInfo) { }
|
||||
|
||||
virtual bool opHasRegFieldExtension( x86ArgumentList& inAL, x86ArgListInstruction& inInsn );
|
||||
virtual uint8 opGetRegFieldExtension() { return ((0x03 & opInfo->oiOpcodeInformation) << 3); }
|
||||
|
||||
virtual bool opCanAccept1ByteImmediate() { return false; }
|
||||
virtual bool opCanAccept4ByteImmediate() { return true; }
|
||||
|
||||
virtual bool opRegOperandCanBeCondensed(){ return true; }
|
||||
|
||||
virtual uint8 opSize() { return 1; }
|
||||
virtual void opFormatToMemory(void* inStart, x86ArgumentList& inAL, x86ArgListInstruction& inInsn);
|
||||
};
|
||||
|
||||
/*--------------------------------------------------------------------------------
|
||||
x86Opcode_ImmArith -- encodes x86ImmediateArithType
|
||||
|
||||
- must have sign/size bit which specifies 8 or 32 bits operand
|
||||
- _possible_ three bit opcode extension (stored in the REG field of the MODR/M byte)
|
||||
*/
|
||||
class x86Opcode_ImmArith :
|
||||
public x86Opcode
|
||||
{
|
||||
public:
|
||||
x86Opcode_ImmArith( x86ImmediateArithType inInfo ) : x86Opcode( inInfo ) { }
|
||||
virtual void opFormatToMemory( void* inStart, x86ArgumentList& /*inAL*/, x86ArgListInstruction& /*inInsn*/);
|
||||
|
||||
virtual bool opHasRegFieldExtension( x86ArgumentList& /*inAL*/, x86ArgListInstruction& /*inInsn*/ )
|
||||
{ return (bool)((opInfo->oiOpcodeInformation >> 6) && 1); }
|
||||
virtual uint8 opGetRegFieldExtension()
|
||||
{ assert(kExtension_Mask & opInfo->oiOpcodeInformation); return (kRegfield_Mask & opInfo->oiOpcodeInformation); }
|
||||
virtual bool opCanAccept1ByteImmediate() { return true; }
|
||||
virtual bool opCanAccept4ByteImmediate() { return true; }
|
||||
|
||||
virtual void opSwitchToRegisterIndirect() { } // FIX can't do this for imul ???
|
||||
|
||||
protected:
|
||||
x86Opcode_ImmArith() { }
|
||||
};
|
||||
|
||||
/*--------------------------------------------------------------------------------
|
||||
x86Opcode_SpecialRegMem -- x86SpecialRegMemType
|
||||
|
||||
- switches between
|
||||
x86Opcode_Reg when operand is a memory argument, and
|
||||
x86Opcode when operand is a register argument
|
||||
*/
|
||||
class x86Opcode_SpecialRegMem :
|
||||
public x86Opcode_Reg // note not x86Opcode!
|
||||
{
|
||||
public:
|
||||
x86Opcode_SpecialRegMem( x86SpecialRegMemType inType) :
|
||||
x86Opcode(inType) { }
|
||||
|
||||
virtual bool opHasRegFieldExtension( x86ArgumentList& /*inAL*/, x86ArgListInstruction& /*inInsn*/ )
|
||||
{
|
||||
return((kIs_Memory_Form_Mask & opInfo->oiOpcodeInformation) == kIs_Memory_Form_Mask);
|
||||
}
|
||||
|
||||
virtual uint8 opGetRegFieldExtension()
|
||||
{
|
||||
assert(kIs_Memory_Form_Mask & opInfo->oiOpcodeInformation);
|
||||
return (kRegfield_Mask & opInfo->oiOpcodeInformation);
|
||||
}
|
||||
|
||||
virtual void opSwitchToRegisterIndirect()
|
||||
{
|
||||
assert(!(kIs_Memory_Form_Mask & opInfo->oiOpcodeInformation));
|
||||
opInfo++;
|
||||
}
|
||||
};
|
||||
|
||||
#endif // _X86OPCODE
|
|
@ -0,0 +1,250 @@
|
|||
/* -*- 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.
|
||||
*/
|
||||
//
|
||||
// x86StdCall.cpp - StdCall calling convention
|
||||
//
|
||||
|
||||
#include "x86StdCall.h"
|
||||
#include "x86Win32Emitter.h"
|
||||
|
||||
// Utility function to push one function-call argument on the stack
|
||||
void
|
||||
pushCallArg(DataConsumer& argument, DataNode* usesNode, InstructionDefine*& orderingDependency, Pool& inPool, x86Win32Emitter& inEmitter)
|
||||
{
|
||||
// Do the first argument specially because that sets up the chain of ordering dependencies
|
||||
int numOrderingUses = orderingDependency ? 1 : 0;
|
||||
|
||||
// If pushing constant arguments, use the immediate form of the push instruction
|
||||
if (argument.getVariable().getCategory() == pcConst) {
|
||||
switch (argument.getKind()) {
|
||||
|
||||
// 32-bit constants
|
||||
case vkInt:
|
||||
case vkAddr:
|
||||
case vkFloat:
|
||||
{
|
||||
Uint32 constant = PrimConst::cast(argument.getVariable()).value.i;
|
||||
x86Instruction& pushInsn = *new(inPool) x86Instruction(usesNode, inPool, iaPushImm, constant, atImmediateOnly, numOrderingUses, 1);
|
||||
|
||||
// First arg is handled differently
|
||||
if (orderingDependency) {
|
||||
inEmitter.useTemporaryOrder(pushInsn, *orderingDependency, 0);
|
||||
inEmitter.redefineTemporaryOrder(pushInsn, *orderingDependency, 0);
|
||||
} else {
|
||||
orderingDependency = &inEmitter.defineTemporaryOrder(pushInsn, 0);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
// 64-bit constants require two immediate-form push instructions
|
||||
case vkDouble:
|
||||
case vkLong:
|
||||
{
|
||||
Int64 constant = PrimConst::cast(argument.getVariable()).value.l;
|
||||
Int32 low = (Int32)((Uint64)constant & 0xFFFFFFFF);
|
||||
Int32 high = (Int32)((Uint64)constant >> 32);
|
||||
|
||||
x86Instruction& pushInsnHi = *new(inPool) x86Instruction(usesNode, inPool, iaPushImm, high, atImmediateOnly, numOrderingUses, 1);
|
||||
|
||||
// First arg is handled differently
|
||||
if (orderingDependency) {
|
||||
inEmitter.useTemporaryOrder(pushInsnHi, *orderingDependency, 0);
|
||||
inEmitter.redefineTemporaryOrder(pushInsnHi, *orderingDependency, 0);
|
||||
} else {
|
||||
orderingDependency = &inEmitter.defineTemporaryOrder(pushInsnHi, 0);
|
||||
}
|
||||
|
||||
x86Instruction& pushInsnLo = *new(inPool) x86Instruction(usesNode, inPool, iaPushImm, low, atImmediateOnly, 1, 1);
|
||||
inEmitter.useTemporaryOrder(pushInsnLo, *orderingDependency, 0);
|
||||
inEmitter.redefineTemporaryOrder(pushInsnLo, *orderingDependency, 0);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Push non-constant function call argument
|
||||
switch (argument.getKind()) {
|
||||
|
||||
case vkInt:
|
||||
case vkAddr:
|
||||
{
|
||||
x86Instruction& storeParam = *new(inPool) x86Instruction(usesNode, inPool, ePush, atRegDirect, 1 + numOrderingUses, 1);
|
||||
inEmitter.useProducer(argument.getVariable(), storeParam, 0);
|
||||
|
||||
// First arg is handled differently
|
||||
if (orderingDependency) {
|
||||
inEmitter.useTemporaryOrder(storeParam, *orderingDependency, 1);
|
||||
inEmitter.redefineTemporaryOrder(storeParam, *orderingDependency, 0);
|
||||
} else {
|
||||
orderingDependency = &inEmitter.defineTemporaryOrder(storeParam, 0);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case vkFloat:
|
||||
{
|
||||
InsnDoubleOpDir& copyParamInsn = *new(inPool) InsnDoubleOpDir(usesNode, inPool, raCopyI, atRegAllocStackSlot, atRegDirect, 1, 1);
|
||||
inEmitter.useProducer(argument.getVariable(), copyParamInsn, 0, vrcStackSlot);
|
||||
VirtualRegister& vr = inEmitter.defineTemporary(copyParamInsn, 0);
|
||||
|
||||
x86Instruction& storeParamInsn = *new(inPool) x86Instruction(usesNode, inPool, ePush, atRegDirect, 1 + numOrderingUses, 1);
|
||||
inEmitter.useTemporaryVR(storeParamInsn, vr, 0);
|
||||
|
||||
// First arg is handled differently
|
||||
if (orderingDependency) {
|
||||
inEmitter.useTemporaryOrder(storeParamInsn, *orderingDependency, 1);
|
||||
inEmitter.redefineTemporaryOrder(storeParamInsn, *orderingDependency, 0);
|
||||
} else {
|
||||
orderingDependency = &inEmitter.defineTemporaryOrder(storeParamInsn, 0);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case vkDouble:
|
||||
{
|
||||
// Push high 32-bits of double
|
||||
InsnDoubleOpDir& copyParamInsnHi = *new(inPool) InsnDoubleOpDir(usesNode, inPool, raCopyI, atRegAllocStackSlotHi32, atRegDirect, 1, 2);
|
||||
inEmitter.useProducer(argument.getVariable(), copyParamInsnHi, 0, vrcStackSlot);
|
||||
VirtualRegister& vrHi = inEmitter.defineTemporary(copyParamInsnHi, 0);
|
||||
|
||||
x86Instruction& storeParamInsnHi = *new(inPool) x86Instruction(usesNode, inPool, ePush, atRegDirect, 1 + numOrderingUses, 1);
|
||||
inEmitter.useTemporaryVR(storeParamInsnHi, vrHi, 0);
|
||||
|
||||
// First arg is handled differently
|
||||
if (orderingDependency) {
|
||||
inEmitter.useTemporaryOrder(storeParamInsnHi, *orderingDependency, 1);
|
||||
inEmitter.redefineTemporaryOrder(storeParamInsnHi, *orderingDependency, 0);
|
||||
} else {
|
||||
orderingDependency = &inEmitter.defineTemporaryOrder(storeParamInsnHi, 0);
|
||||
}
|
||||
|
||||
// Push low 32-bits of double
|
||||
InsnDoubleOpDir& copyParamInsnLo = *new(inPool) InsnDoubleOpDir(usesNode, inPool, raCopyI, atRegAllocStackSlot, atRegDirect, 1, 2);
|
||||
inEmitter.useProducer(argument.getVariable(), copyParamInsnLo, 0, vrcStackSlot);
|
||||
VirtualRegister& vrLo = inEmitter.defineTemporary(copyParamInsnLo, 0);
|
||||
|
||||
x86Instruction& storeParamInsnLo = *new(inPool) x86Instruction(usesNode, inPool, ePush, atRegDirect, 2, 1);
|
||||
inEmitter.useTemporaryVR(storeParamInsnLo, vrLo, 0);
|
||||
inEmitter.useTemporaryOrder(storeParamInsnLo, *orderingDependency, 1);
|
||||
inEmitter.redefineTemporaryOrder(storeParamInsnLo, *orderingDependency, 0);
|
||||
}
|
||||
break;
|
||||
|
||||
case vkLong:
|
||||
{
|
||||
x86Instruction& storeParamHigh = *new(inPool) x86Instruction(usesNode, inPool, ePush, atRegDirect, 1 + numOrderingUses, 1);
|
||||
inEmitter.useProducer(argument.getVariable(), storeParamHigh, 0, vrcInteger, vidHigh);
|
||||
|
||||
// First arg is handled differently
|
||||
if (orderingDependency) {
|
||||
inEmitter.useTemporaryOrder(storeParamHigh, *orderingDependency, 1);
|
||||
inEmitter.redefineTemporaryOrder(storeParamHigh, *orderingDependency, 0);
|
||||
} else {
|
||||
orderingDependency = &inEmitter.defineTemporaryOrder(storeParamHigh, 0);
|
||||
}
|
||||
|
||||
x86Instruction& storeParamLow = *new(inPool) x86Instruction(usesNode, inPool, ePush, atRegDirect, 2, 1);
|
||||
inEmitter.useProducer(argument.getVariable(), storeParamLow, 0, vrcInteger, vidLow);
|
||||
inEmitter.useTemporaryOrder(storeParamLow, *orderingDependency, 1);
|
||||
inEmitter.redefineTemporaryOrder(storeParamLow, *orderingDependency, 0);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void x86Win32Emitter::
|
||||
emitArguments(ControlNode::BeginExtra& inBeginNode)
|
||||
{
|
||||
if (inBeginNode.nArguments == 0)
|
||||
return;
|
||||
|
||||
InsnExternalDefine& defineInsn = *new(mPool) InsnExternalDefine(&inBeginNode[0], mPool, inBeginNode.nArguments * 2);
|
||||
|
||||
// do appropriate loads for each argument
|
||||
Uint32 curStackOffset = 8;
|
||||
|
||||
Uint8 curIndex = 0; // Index into defineInsn
|
||||
unsigned int curArgument;
|
||||
for (curArgument = 0; curArgument < inBeginNode.nArguments; curArgument++)
|
||||
{
|
||||
PrimArg& curArg = inBeginNode[curArgument];
|
||||
|
||||
switch (curArg.getOperation())
|
||||
{
|
||||
case poArg_I:
|
||||
case poArg_A:
|
||||
if (curArg.hasConsumers())
|
||||
{
|
||||
|
||||
InsnDoubleOpDir& loadParam = *new(mPool) InsnDoubleOpDir(&curArg, mPool, raLoadI, curStackOffset, atStackOffset, atRegDirect, 1, 1);
|
||||
useTemporaryOrder(loadParam, defineTemporaryOrder(defineInsn, curIndex), 0);
|
||||
defineProducer(curArg, loadParam, 0);
|
||||
}
|
||||
curIndex++;
|
||||
curStackOffset += 4;
|
||||
break;
|
||||
|
||||
case poArg_F:
|
||||
if (curArg.hasConsumers())
|
||||
emit_ArgF(curArg, defineTemporaryOrder(defineInsn, curIndex), curStackOffset);
|
||||
curIndex++;
|
||||
curStackOffset += 4;
|
||||
break;
|
||||
|
||||
case poArg_L:
|
||||
if (curArg.hasConsumers())
|
||||
{
|
||||
InsnDoubleOpDir& loadHi = *new(mPool) InsnDoubleOpDir(&curArg, mPool, raLoadI, curStackOffset + 4, atStackOffset, atRegDirect, 1, 1);
|
||||
InsnDoubleOpDir& loadLo = *new(mPool) InsnDoubleOpDir(&curArg, mPool, raLoadI, curStackOffset, atStackOffset, atRegDirect, 1, 1);
|
||||
useTemporaryOrder(loadLo, defineTemporaryOrder(defineInsn, curIndex), 0);
|
||||
useTemporaryOrder(loadHi, defineTemporaryOrder(defineInsn, curIndex + 1), 0);
|
||||
defineProducer(curArg, loadLo, 0, vrcInteger, vidLow);
|
||||
defineProducer(curArg, loadHi, 0, vrcInteger, vidHigh);
|
||||
}
|
||||
curIndex += 2;
|
||||
curStackOffset += 8;
|
||||
break;
|
||||
|
||||
case poArg_D:
|
||||
if (curArg.hasConsumers())
|
||||
emit_ArgD(curArg, defineTemporaryOrder(defineInsn, curIndex), curStackOffset);
|
||||
curIndex++;
|
||||
curStackOffset += 8;
|
||||
break;
|
||||
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
// continue counting from before
|
||||
// make all the rest of the defines as type none
|
||||
for (;curIndex < inBeginNode.nArguments * 2; curIndex++)
|
||||
{
|
||||
defineInsn.getInstructionDefineBegin()[curIndex].kind = udNone;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,206 @@
|
|||
/* -*- 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.
|
||||
*/
|
||||
//
|
||||
// x86StdCall.h - StdCall calling convention
|
||||
//
|
||||
|
||||
#include "x86Win32Instruction.h"
|
||||
|
||||
// Utility function to push one function-call argument on the stack
|
||||
void pushCallArg(DataConsumer &curArgument, DataNode* usesNode, InstructionDefine*& orderingDependency, Pool& inPool, x86Win32Emitter& inEmitter);
|
||||
|
||||
// -> mem regarg1 regarg2 regarg3 regarg3
|
||||
// <- mem [returnval]
|
||||
|
||||
|
||||
// inHasReturnValue is deprecated, and is ignored
|
||||
template<bool tHasIncomingStore, bool tHasOutgoingStore, bool tHasFunctionAddress, bool tIsDynamic>
|
||||
Call<tHasIncomingStore, tHasOutgoingStore, tHasFunctionAddress, tIsDynamic>::
|
||||
Call( DataNode* inDataNode,
|
||||
Pool& inPool,
|
||||
Uint8 inRegisterArguments,
|
||||
bool /*inHasReturnValue*/,
|
||||
x86Win32Emitter& inEmitter,
|
||||
void (*inFunc)(),
|
||||
DataNode* inUseDataNode) :
|
||||
InsnUseXDefineYFromPool(inDataNode, inPool, ((inRegisterArguments != 0) ? 1 : 0) + tHasIncomingStore + tIsDynamic, 3)
|
||||
// add extra use for ordering edge (see below) if more than one argument
|
||||
{
|
||||
DataNode* returnValProducer; // node whose outgoing edge is the return value
|
||||
|
||||
if (inDataNode->hasKind(vkTuple))
|
||||
{
|
||||
// if called with a Primitive with kind vkTuple, we know we will have to deal with a projection edge
|
||||
const DoublyLinkedList<DataConsumer>& projectionConsumers = inDataNode->getConsumers();
|
||||
DoublyLinkedList<DataConsumer>::iterator curProjectionEdge = projectionConsumers.begin();
|
||||
|
||||
// grab the outgoing store (if there is one)
|
||||
// set up returnValProducer to point to the return value producer (if it exists)
|
||||
// one of the two consumers of the projection node must be the memory node, the other must be the Return value node
|
||||
if (tHasOutgoingStore)
|
||||
{
|
||||
DataNode& projectionA = projectionConsumers.get(curProjectionEdge).getNode();
|
||||
DataNode* projectionB = (projectionConsumers.done(curProjectionEdge = projectionConsumers.advance(curProjectionEdge))) ? 0 : &projectionConsumers.get(curProjectionEdge).getNode();
|
||||
|
||||
if (projectionA.hasKind(vkMemory))
|
||||
{
|
||||
inEmitter.defineProducer(projectionA, *this, 0); // -> mem
|
||||
returnValProducer = projectionB;
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(projectionB); // projectionB must be the memory producer
|
||||
inEmitter.defineProducer(*projectionB, *this, 0); // -> mem
|
||||
returnValProducer = &projectionA;
|
||||
}
|
||||
}
|
||||
else
|
||||
returnValProducer = (projectionConsumers.done(curProjectionEdge = projectionConsumers.advance(curProjectionEdge))) ? 0 : &projectionConsumers.get(curProjectionEdge).getNode();
|
||||
}
|
||||
else
|
||||
{
|
||||
// non-call Primitive treated as an out-of-line call
|
||||
assert(!tHasFunctionAddress && !tIsDynamic);
|
||||
|
||||
// since outgoing stores are handled separately and are not
|
||||
// a real return type, we make the return value code ignore
|
||||
// them
|
||||
if (tHasOutgoingStore)
|
||||
{
|
||||
assert(inDataNode->hasKind(vkMemory));
|
||||
inEmitter.defineProducer(*inDataNode, *this, 0); //-> mem
|
||||
returnValProducer = NULL;
|
||||
}
|
||||
else
|
||||
returnValProducer = inDataNode;
|
||||
}
|
||||
|
||||
// pre-color (and buffer) the return value
|
||||
if (returnValProducer)
|
||||
{
|
||||
switch (returnValProducer->getKind())
|
||||
{
|
||||
case vkInt:
|
||||
case vkAddr:
|
||||
{
|
||||
VirtualRegister* returnValVR;
|
||||
|
||||
// the Call defines a temporary register, which is precolored to
|
||||
// the appropriate return register
|
||||
returnValVR = &inEmitter.defineTemporary(*this, 1);
|
||||
returnValVR->preColorRegister(x86GPRToColor[EAX]);
|
||||
|
||||
// now create a "buffer" copy between the precolored return register
|
||||
// and make the Copy define the return value edge from the poCall
|
||||
InsnDoubleOpDir& copyInsn = newCopyInstruction(*inDataNode, inPool);
|
||||
inEmitter.useTemporaryVR(copyInsn, *returnValVR, 0);
|
||||
inEmitter.defineProducer(*returnValProducer, copyInsn, 0);
|
||||
// fiX-ME none-out unused return value argument
|
||||
break;
|
||||
}
|
||||
|
||||
case vkLong:
|
||||
{
|
||||
// results returned in eax (low) and edx (high)
|
||||
VirtualRegister* lowVR;
|
||||
VirtualRegister* highVR;
|
||||
|
||||
lowVR = &inEmitter.defineTemporary(*this, 1);
|
||||
highVR = &inEmitter.defineTemporary(*this, 2);
|
||||
lowVR->preColorRegister(x86GPRToColor[EAX]);
|
||||
highVR->preColorRegister(x86GPRToColor[EDX]);
|
||||
|
||||
InsnDoubleOpDir& copyHiInsn = newCopyInstruction(*inDataNode, inPool);
|
||||
inEmitter.useTemporaryVR(copyHiInsn, *highVR, 0);
|
||||
inEmitter.defineProducer(*returnValProducer, copyHiInsn, 0, vrcInteger, vidHigh);
|
||||
|
||||
InsnDoubleOpDir& copyLowInsn = newCopyInstruction(*inDataNode, inPool);
|
||||
inEmitter.useTemporaryVR(copyLowInsn, *lowVR, 0);
|
||||
inEmitter.defineProducer(*returnValProducer, copyLowInsn, 0, vrcInteger, vidLow);
|
||||
}
|
||||
break;
|
||||
|
||||
case vkFloat:
|
||||
inEmitter.emit_CallReturnF(*this, *inDataNode, *returnValProducer);
|
||||
break;
|
||||
|
||||
case vkDouble:
|
||||
inEmitter.emit_CallReturnD(*this, *inDataNode, *returnValProducer);
|
||||
break;
|
||||
|
||||
default:
|
||||
assert(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (!tHasOutgoingStore)
|
||||
inDataNode->setInstructionRoot(this); // if no outgoing edges (no return value or memory edge, we need to hook this on)
|
||||
|
||||
DataConsumer* firstArgument;
|
||||
DataConsumer* curArgument;
|
||||
|
||||
// uses may begin at some passed in node, instead of the node from which the call originates
|
||||
DataNode* usesNode = (inUseDataNode) ? inUseDataNode : inDataNode;
|
||||
|
||||
// incoming store
|
||||
if (tHasIncomingStore)
|
||||
inEmitter.useProducer(usesNode->nthInputVariable(0), *this, tIsDynamic + (inRegisterArguments > 0)); // -> mem
|
||||
|
||||
firstArgument = usesNode->getInputsBegin() + tHasFunctionAddress + tHasIncomingStore;
|
||||
|
||||
// grab the callee address (it might not be a static call)
|
||||
if (tHasFunctionAddress)
|
||||
{
|
||||
assert(inFunc == NULL); // programmer error to specify a function address if this Call has a function address
|
||||
if (tIsDynamic)
|
||||
inEmitter.useProducer(usesNode->nthInputVariable(1), *this, 0); // -> incoming address
|
||||
else
|
||||
mCalleeAddress = (Uint32) nthInputConstantUint32(*usesNode, 1);
|
||||
}
|
||||
else
|
||||
mCalleeAddress = (Uint32) inFunc;
|
||||
|
||||
// start at last argument and push arguments from right to left
|
||||
|
||||
curArgument = usesNode->getInputsEnd() - 1; // arguments array is last + 1
|
||||
|
||||
// BASIC IDEA:
|
||||
//
|
||||
// We are creating a bunch of pushes followed by a call instruction
|
||||
// Since pushes are clearly ordered operations we need an edge to maintain that ordering.
|
||||
// We use an order edge defined by the first push, and which is used and by each
|
||||
// subsequent push. This ends in a use by the Call (*this).
|
||||
//
|
||||
// In the end we will have somethinglike Call -> Arg0 -> Arg1 -> ... -> ArgN-1 -> ArgN
|
||||
// where Call is the call instruction and the Arg's are pushes.
|
||||
//
|
||||
// The scheduler will walk up the use chain and do the last arg first and then come
|
||||
// back down the chain.
|
||||
InstructionDefine* orderingDependency = NULL; // maintains "thread" to order the pushes of the arguments
|
||||
|
||||
if(curArgument >= firstArgument)
|
||||
{
|
||||
// Generate pushes for the arguments. Order them with Order Edges.
|
||||
// Note x86 arguments are pushed on the stack from right to left.
|
||||
for (; curArgument >= firstArgument; curArgument--)
|
||||
pushCallArg(*curArgument, usesNode, orderingDependency, inPool, inEmitter);
|
||||
|
||||
// this is the final order edge which is attached to the Call instruction
|
||||
inEmitter.useTemporaryOrder(*this, *orderingDependency, tIsDynamic);
|
||||
}
|
||||
}
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,435 @@
|
|||
/* -*- 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.
|
||||
*/
|
||||
//
|
||||
// File: x86Win32_support.cpp
|
||||
//
|
||||
// Authors: Peter DeSantis
|
||||
// Simon Holmes a Court
|
||||
//
|
||||
|
||||
#include "NativeCodeCache.h"
|
||||
#include <string.h>
|
||||
#include "Fundamentals.h"
|
||||
#include "MemoryAccess.h"
|
||||
|
||||
|
||||
void* JNIenv = 0;
|
||||
|
||||
extern ClassWorld world;
|
||||
|
||||
#define Naked __declspec( naked )
|
||||
|
||||
/*
|
||||
+-------------------------------+
|
||||
| return address |
|
||||
========+===============================+========
|
||||
| EBP link |
|
||||
+-------------------------------+
|
||||
| Saved Non-volatiles |
|
||||
| eg. EDI |
|
||||
| ESI |
|
||||
| EBX |
|
||||
+-------------------------------+
|
||||
*/
|
||||
|
||||
// Fucntion: staticCompileStub
|
||||
//
|
||||
// WARNING: if you change this method, you must change compileStubReEntryPoint below.
|
||||
// It must point to the instruction after the invokation of compileAndBackPatchMethod
|
||||
static Naked void staticCompileStub()
|
||||
{
|
||||
_asm
|
||||
{
|
||||
// remove cache entry from the stack
|
||||
pop eax
|
||||
|
||||
// make frame
|
||||
push ebp
|
||||
mov ebp,esp
|
||||
|
||||
// save all volatiles (especially for exception handler)
|
||||
push edi
|
||||
push esi
|
||||
push ebx
|
||||
|
||||
// call compileAndBackPatchMethod with args
|
||||
// third argument is not used
|
||||
push [esp + 16] // second argument -- return address
|
||||
push eax // first argument -- cacheEntry
|
||||
call compileAndBackPatchMethod
|
||||
|
||||
// remove args
|
||||
pop edx // <--- compileStubReEntryPoint
|
||||
pop edx
|
||||
|
||||
pop ebx // Restore volatiles
|
||||
pop esi
|
||||
pop edi
|
||||
|
||||
// remove frame
|
||||
mov esp,ebp
|
||||
pop ebp
|
||||
|
||||
// jump to the compiled method
|
||||
push eax // ret will jump to this address
|
||||
ret // Jump to function leaving the return address at the top of the stack
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
// Pointer to the instruction after the call (used by exception handler to check
|
||||
// I wanted to use:
|
||||
// void* compileStubReEntryPoint = (void*) ((Uint8*)staticCompileStub + 17);
|
||||
// but MSDev appears to have a bug, in that compileStubReEntryPoint will be set == (void*)staticCompileStub
|
||||
// which is clearly wrong.
|
||||
void* compileStubAddress = (void*)staticCompileStub;
|
||||
void* compileStubReEntryPoint = (Uint8*)compileStubAddress + 17;
|
||||
#endif // DEBUG
|
||||
|
||||
static Naked void compileStub()
|
||||
{
|
||||
_asm {
|
||||
push 0xEFBEADDE // This is a dummy immediate that will be filled in by
|
||||
jmp staticCompileStub // generateCompileStub with the cacheEntry.
|
||||
}
|
||||
}
|
||||
|
||||
void *
|
||||
generateNativeStub(NativeCodeCache& inCache, const CacheEntry& inCacheEntry, void *nativeFunction)
|
||||
{
|
||||
Method* method = inCacheEntry.descriptor.method;
|
||||
//Uint32 nWords = method->getSignature().nArguments;
|
||||
Uint32 nWords = method->getArgsSize()/sizeof(Int32);
|
||||
|
||||
assert(method->getModifiers() & CR_METHOD_NATIVE);
|
||||
assert(nWords <= 256);
|
||||
|
||||
extern void *sysInvokeNativeStubs[];
|
||||
Uint8 stubSize = 10;
|
||||
void* stub;
|
||||
|
||||
// Write out the native stub
|
||||
stub = inCache.acquireMemory(stubSize);
|
||||
Uint8* where = (Uint8*)stub;
|
||||
*where++ = 0x68; // pushl
|
||||
writeLittleWordUnaligned(where, (uint32)(nativeFunction));
|
||||
where += 4;
|
||||
*where++ = 0xe9; // jmp
|
||||
writeLittleWordUnaligned(where, (Uint8 *) sysInvokeNativeStubs[nWords] - (where + 4));
|
||||
|
||||
// Return the address of the stub.
|
||||
return ((void*)stub);
|
||||
}
|
||||
|
||||
void*
|
||||
generateCompileStub(NativeCodeCache& inCache, const CacheEntry& inCacheEntry)
|
||||
{
|
||||
void* stub;
|
||||
uint8 stubSize;
|
||||
uint8 argumentOffset;
|
||||
uint32 locationOfCompileStub;
|
||||
|
||||
stubSize = 10;
|
||||
|
||||
// Write out the dynamic compile stub
|
||||
stub = inCache.acquireMemory(stubSize);
|
||||
argumentOffset = 1;
|
||||
locationOfCompileStub = (uint32)compileStub ;
|
||||
|
||||
// Copy the stub into the allocated memory
|
||||
memcpy(stub, (void*)locationOfCompileStub, stubSize);
|
||||
|
||||
// Write your cacheEntry into the proper spot in the stub
|
||||
uint8* loadCacheEntryInstruction = (uint8*)stub + argumentOffset;
|
||||
writeLittleWordUnaligned((void*)loadCacheEntryInstruction, (uint32)(&inCacheEntry));
|
||||
|
||||
// Fix the new dynamic stub to jump to the static stub
|
||||
uint32* relativeCallLocation = (uint32*)(loadCacheEntryInstruction + 5);
|
||||
uint32 newRelativeDisplacement = locationOfCompileStub - (uint32)stub + *(uint32*)relativeCallLocation;
|
||||
writeLittleWordUnaligned((void*)relativeCallLocation, newRelativeDisplacement);
|
||||
|
||||
// Return the address of the dynamic stub.
|
||||
return ((void*)stub);
|
||||
}
|
||||
|
||||
void*
|
||||
backPatchMethod(void* inMethodAddress, void* inLastPC, void* /*inUserDefined*/)
|
||||
{
|
||||
|
||||
uint32 curAddress = (uint32) inLastPC;
|
||||
uint32 methodAddress = (uint32) inMethodAddress;
|
||||
|
||||
// Compute the relative branch
|
||||
uint32* relativeBranch = ((uint32*)inLastPC)-1;
|
||||
int32 offset = methodAddress - curAddress;
|
||||
|
||||
// Backpatch the method.
|
||||
writeLittleWordUnaligned((void*)relativeBranch, offset);
|
||||
|
||||
return (inMethodAddress);
|
||||
}
|
||||
|
||||
// Warning silencing stuff
|
||||
// #pragma warning( disable : 4035)
|
||||
// #pragma warning( default : 4035)
|
||||
|
||||
|
||||
//================================================================================
|
||||
// 64bit Arithmetic Support Functions
|
||||
|
||||
// x86Extract64Bit
|
||||
//
|
||||
// Purpose: signed right-aligned field extraction
|
||||
// In: 64 bit source (on stack)
|
||||
// 32 bit extraction size (on stack)
|
||||
// Out: 64 bit result
|
||||
// Note: Only works in range 1 <= b <= 63, b is extraction amount
|
||||
Naked void x86Extract64Bit()
|
||||
{
|
||||
__asm
|
||||
{
|
||||
mov eax, [esp+4] // load low byte of a
|
||||
|
||||
mov ecx, [esp+12] // load shift amount
|
||||
cmp ecx, 0x20
|
||||
jg greater32
|
||||
|
||||
// extract <= than 32 bits
|
||||
// shift amount = 32 - extract
|
||||
neg ecx
|
||||
add ecx, 0x20 // ecx = 32 - extract
|
||||
shl eax, cl
|
||||
sar eax, cl
|
||||
cdq // sign extend into EDX:EAX
|
||||
ret 12
|
||||
|
||||
greater32:
|
||||
// ext > 32 bits
|
||||
// shift amount = 64 - extract
|
||||
mov edx, [esp+8] // load high byte of a
|
||||
neg ecx
|
||||
add ecx, 0x40 // ecx = 64 - extract
|
||||
shl edx, cl
|
||||
sar edx, cl
|
||||
ret 12
|
||||
}
|
||||
}
|
||||
|
||||
// 3WayCompare
|
||||
//
|
||||
// Purpose: compare two longs
|
||||
// In: two longs on the stack
|
||||
// Out: depends on condition flags:
|
||||
// less = -1
|
||||
// equal = 0
|
||||
// greater = 1
|
||||
Naked void x86ThreeWayCMP_L()
|
||||
{
|
||||
// edx:eax is tos, ecx:ebx is nos
|
||||
__asm
|
||||
{
|
||||
mov ecx,[esp+8]
|
||||
mov edx,[esp+16]
|
||||
|
||||
cmp ecx,edx
|
||||
jl lcmp_m1
|
||||
|
||||
jg lcmp_1
|
||||
|
||||
mov ecx,[esp+4]
|
||||
mov edx,[esp+12]
|
||||
|
||||
cmp ecx,edx
|
||||
ja lcmp_1
|
||||
|
||||
mov eax,0
|
||||
jb lcmp_m1
|
||||
|
||||
ret 16
|
||||
|
||||
align 4
|
||||
lcmp_m1:
|
||||
mov eax,-1
|
||||
|
||||
ret 16
|
||||
|
||||
align 4
|
||||
lcmp_1:
|
||||
mov eax,1
|
||||
|
||||
ret 16
|
||||
}
|
||||
}
|
||||
|
||||
// 3WayCompare
|
||||
//
|
||||
// Purpose: compare two longs
|
||||
// In: two longs on the stack
|
||||
// Out: depends on condition flags:
|
||||
// less = 1
|
||||
// equal = 0
|
||||
// greater = -1
|
||||
Naked void x86ThreeWayCMPC_L()
|
||||
{
|
||||
// edx:eax is tos, ecx:ebx is nos
|
||||
__asm
|
||||
{
|
||||
mov ecx,[esp+8]
|
||||
mov edx,[esp+16]
|
||||
|
||||
cmp ecx,edx
|
||||
jl lcmp_m1
|
||||
|
||||
jg lcmp_1
|
||||
|
||||
mov ecx,[esp+4]
|
||||
mov edx,[esp+12]
|
||||
|
||||
cmp ecx,edx
|
||||
ja lcmp_1
|
||||
|
||||
mov eax,0
|
||||
jb lcmp_m1
|
||||
|
||||
ret 16
|
||||
|
||||
align 4
|
||||
lcmp_m1:
|
||||
mov eax,1
|
||||
|
||||
ret 16
|
||||
|
||||
align 4
|
||||
lcmp_1:
|
||||
mov eax,-1
|
||||
|
||||
ret 16
|
||||
}
|
||||
}
|
||||
|
||||
// llmul
|
||||
//
|
||||
// Purpose: long multiply (same for signed/unsigned)
|
||||
// In: args are passed on the stack:
|
||||
// 1st pushed: multiplier (QWORD)
|
||||
// 2nd pushed: multiplicand (QWORD)
|
||||
// Out: EDX:EAX - product of multiplier and multiplicand
|
||||
// Note: parameters are removed from the stack
|
||||
// Uses: ECX
|
||||
Naked void x86Mul64Bit()
|
||||
{
|
||||
// IMPLEMENT: Needs to be written
|
||||
_asm
|
||||
{
|
||||
int 3
|
||||
}
|
||||
}
|
||||
|
||||
// lldiv
|
||||
//
|
||||
// Purpose: signed long divide
|
||||
// In: args are passed on the stack:
|
||||
// 1st pushed: divisor (QWORD)
|
||||
// 2nd pushed: dividend (QWORD)
|
||||
// Out: EDX:EAX contains the quotient (dividend/divisor)
|
||||
// Note: parameters are removed from the stack
|
||||
// Uses: ECX
|
||||
Naked void x86Div64Bit()
|
||||
{
|
||||
// IMPLEMENT: Needs to be written
|
||||
_asm
|
||||
{
|
||||
int 3
|
||||
}
|
||||
}
|
||||
|
||||
// llrem
|
||||
//
|
||||
// Purpose: signed long remainder
|
||||
// In: args are passed on the stack:
|
||||
// 1st pushed: divisor (QWORD)
|
||||
// 2nd pushed: dividend (QWORD)
|
||||
// Out: EDX:EAX contains the quotient (dividend/divisor)
|
||||
// Note: parameters are removed from the stack
|
||||
// Uses: ECX
|
||||
Naked void x86Mod64Bit()
|
||||
{
|
||||
// IMPLEMENT: Needs to be written
|
||||
_asm
|
||||
{
|
||||
int 3
|
||||
}
|
||||
}
|
||||
|
||||
// llshl
|
||||
//
|
||||
// Purpose: long shift left
|
||||
// In: args are passed on the stack: (FIX make fastcall)
|
||||
// 1st pushed: amount (int)
|
||||
// 2nd pushed: source (long)
|
||||
// Out: EDX:EAX contains the result
|
||||
// Note: parameters are removed from the stack
|
||||
// Uses: ECX, destroyed
|
||||
Naked void x86Shl64Bit()
|
||||
{
|
||||
// IMPLEMENT: Needs to be written
|
||||
_asm
|
||||
{
|
||||
int 3
|
||||
}
|
||||
}
|
||||
|
||||
// llshr
|
||||
//
|
||||
// Origin: MSDev. modified
|
||||
// Purpose: long shift right
|
||||
// In: args are passed on the stack: (FIX make fastcall)
|
||||
// 1st pushed: amount (int)
|
||||
// 2nd pushed: source (long)
|
||||
// Out: EDX:EAX contains the result
|
||||
// Note: parameters are removed from the stack
|
||||
// Uses: ECX, destroyed
|
||||
Naked void x86Shr64Bit()
|
||||
{
|
||||
// IMPLEMENT: Needs to be written
|
||||
_asm
|
||||
{
|
||||
int 3
|
||||
}
|
||||
}
|
||||
|
||||
// llsar
|
||||
//
|
||||
// Origin: MSDev. modified
|
||||
// Purpose: long shift right signed
|
||||
// In: args are passed on the stack: (FIX make fastcall)
|
||||
// 1st pushed: amount (int)
|
||||
// 2nd pushed: source (long)
|
||||
// Out: EDX:EAX contains the result
|
||||
// Note: parameters are removed from the stack
|
||||
// Uses: ECX, destroyed
|
||||
Naked void x86Sar64Bit()
|
||||
{
|
||||
// IMPLEMENT: Needs to be written
|
||||
_asm
|
||||
{
|
||||
int 3
|
||||
}
|
||||
}
|
||||
|
||||
//================================================================================
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,521 @@
|
|||
/* -*- 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 BYTECODEGRAPH_H
|
||||
#define BYTECODEGRAPH_H
|
||||
|
||||
#include "DoublyLinkedList.h"
|
||||
#include "JavaBytecodes.h"
|
||||
#include "TranslationEnv.h"
|
||||
#include "VerificationEnv.h"
|
||||
#include "ControlGraph.h"
|
||||
#include "ClassFileSummary.h"
|
||||
#include "LogModule.h"
|
||||
#include "CheckedUnion.h"
|
||||
|
||||
//
|
||||
// Invariants for a BytecodeGraph:
|
||||
//
|
||||
// 1. The BytecodeGraph starts with the beginBlock BytecodeBlock.
|
||||
//
|
||||
// 2. The BytecodeGraph contains exactly one EndBlock.
|
||||
// That block has no bytecodes and no successors.
|
||||
//
|
||||
// 3. Every BytecodeBlock whose subkind is not skForward contains at least one bytecode.
|
||||
// A BytecodeBlock's subkind is skNormal unless specified otherwise below.
|
||||
// The EndBlock and CatchBlocks contain no bytecodes.
|
||||
//
|
||||
// 4. The BytecodeGraph contains at most one BytecodeBlock that contains a return instruction
|
||||
// (return, ireturn, areturn, etc.). The return instruction must be the only one in that
|
||||
// BytecodeBlock. That BytecodeBlock must have exactly one successor, the EndBlock, and
|
||||
// no exceptional successors. That BytecodeBlock's subkind must be skReturn.
|
||||
//
|
||||
// 5. An instruction of kind bckIf can only occur if it is the last one in a BytecodeBlock.
|
||||
// That BytecodeBlock must have exactly two successors, the first of which is the false
|
||||
// branch and the second the true branch.
|
||||
//
|
||||
// 6. An instruction of kind bckGoto or bckGoto_W may occur only if it is the last one in a
|
||||
// BytecodeBlock. That BytecodeBlock must have exactly one successor.
|
||||
//
|
||||
// 7. An instruction of kind bckTableSwitch or bckLookupSwitch may occur only if it is the
|
||||
// last one in a BytecodeBlock. That BytecodeBlock must have n+1 successors, where n
|
||||
// is the number of cases. The first successor is the default, while the remaining ones
|
||||
// correspond to the cases in the order they are listed in the tableswitch or lookupswitch
|
||||
// instruction.
|
||||
//
|
||||
// 8. An instruction of kind bckThrow may occur only if it is the last one in a
|
||||
// BytecodeBlock. That BytecodeBlock must have no successors.
|
||||
//
|
||||
// 9. A jsr (or jsr_w) instruction must be the only one in its BytecodeBlock. That BytecodeBlock
|
||||
// must have either one or two successors and no exceptional successors. The first successor
|
||||
// is the first BytecodeBlock of the subroutine (also called the subroutine's header).
|
||||
// The second successor, if present, is the BytecodeBlock containing instructions after the jsr.
|
||||
// The second successor is absent if verification determines that the subroutine never returns
|
||||
// via a BytecodeBlock of kind skRet. The jsr BytecodeBlock's subkind must be skJsr if it has
|
||||
// two successors or skJsrNoRet if it has one successor.
|
||||
//
|
||||
// 10. A ret (or wide ret) instruction must be the only one in its BytecodeBlock. That BytecodeBlock
|
||||
// must have no successors and no exceptional successors. This BytecodeBlock's subkind must be
|
||||
// skRet.
|
||||
//
|
||||
// 11. A BytecodeBlock with subkind skForward must not have any instructions. That BytecodeBlock
|
||||
// must have exactly one successor and no exceptional successors and is treated as an
|
||||
// unconditional branch to that one successor.
|
||||
//
|
||||
// 12. No BytecodeBlock may contain an instruction of kind bckIllegal.
|
||||
//
|
||||
// 13. Every BytecodeBlock that ends with one of the remaining instructions (kinds bckNormal
|
||||
// or bckExc) must have exactly one successor.
|
||||
//
|
||||
// 14. Exceptional successors are only allowed in BytecodeBlocks that do not contain return,
|
||||
// ret, or jsr instructions. Every exceptional successor must be either a CatchBlock or the
|
||||
// EndBlock.
|
||||
//
|
||||
// 15. Non-exceptional successors may not refer to a CatchBlock.
|
||||
//
|
||||
// 16. The one successor of a CatchBlock must be a BytecodeBlock. That BytecodeBlock's catchBlock
|
||||
// must point back to that CatchBlock.
|
||||
//
|
||||
|
||||
class BytecodeGraph;
|
||||
struct BytecodeBlock;
|
||||
struct BasicBlock: DoublyLinkedEntry<BasicBlock> // Links to other BasicBlocks in the same graph
|
||||
{
|
||||
// Persistent fields
|
||||
enum Kind {bbEnd, bbBytecode, bbCatch};
|
||||
enum StackNormalization {snNoChange, sn0, sn1, sn2}; // Number of words to leave on the stack when entering a BasicBlock.
|
||||
// snNoChange means to leave the stack as is (the normal case);
|
||||
// sn0, sn1, and sn2 mean to leave only the *top* 0, 1, or 2 words on the stack.
|
||||
// These are needed so we can combine all return bytecodes into one block.
|
||||
|
||||
const Kind kind ENUM_8; // Kind of this BasicBlock (EndBlock, BytecodeBlock, or CatchBlock)
|
||||
const StackNormalization stackNormalization ENUM_8; // Number of words to leave on the stack when entering this BasicBlock
|
||||
BasicBlock **successorsBegin; // Array of pointers to successors (nil if this is the end block)
|
||||
BasicBlock **successorsEnd; // Last normal successor pointer + 1; (nil if this is the end block)
|
||||
// also first exceptional successor pointer
|
||||
BasicBlock **handlersEnd; // Last exceptional successor pointer + 1 (nil if this is the end block;
|
||||
#ifdef DEBUG // same as successorsEnd if this is a catch block)
|
||||
BasicBlock **successorsPhysicalEnd; // End of storage allocated for this successors array
|
||||
#endif
|
||||
|
||||
// Fields computed by depthFirstSearch
|
||||
enum {unmarked = -3, unvisited = -2, unnumbered = -1};
|
||||
Int32 dfsNum; // Serial number assigned by depth-first search (-3 is unmarked, -2 is unvisited, -1 is unnumbered)
|
||||
Uint32 nPredecessors; // Number of BasicBlock successor pointers that refer to this BasicBlock
|
||||
// (often different from the number of ControlNodes that jump to this
|
||||
// BasicBlock's ControlNodes because some BasicBlocks may be discovered
|
||||
// to be dead and hence generate no ControlNodes, while others may generate
|
||||
// code that has multiple branches to their successors.)
|
||||
// The beginBlock pointer is also considered to be a successor pointer (so
|
||||
// nPredecessors of the first block is always at least one).
|
||||
// The implicit exception edges going to the EndBlock are not counted
|
||||
#ifdef DEBUG // in the end BasicBlock's nPredecessors.
|
||||
bool hasIncomingBackEdges; // True if this block has incoming backward edges
|
||||
#endif
|
||||
|
||||
private:
|
||||
// Temporary fields for verification
|
||||
struct VerificationTemps
|
||||
{
|
||||
Uint32 generation; // Number of last dataflow pass to have changed verificationEnvIn; 0 if none yet
|
||||
VerificationEnv verificationEnvIn; // State of locals and stack at beginning of block
|
||||
BytecodeBlock *subroutineHeader; // If this is a ret block, points to the entry point (header) of the subroutine;
|
||||
// nil if not known or this is not a ret block.
|
||||
BytecodeBlock *subroutineRet; // If this is a subroutine header, points to the ret block of that subroutine;
|
||||
// nil if the ret block hasn't been found yet or this is not a subroutine header.
|
||||
union {
|
||||
BasicBlock *clone; // The copy of this BasicBlock, temporarily stored here while copying a subgraph of this graph
|
||||
bool recompute; // True if verificationEnvIn has changed since it was last propagated to successors
|
||||
};
|
||||
|
||||
VerificationTemps(VerificationEnv::Common &c):
|
||||
generation(0), verificationEnvIn(c), subroutineHeader(0), subroutineRet(0), recompute(false) {}
|
||||
VerificationTemps(const VerificationEnv &env, Function1<BytecodeBlock *, BytecodeBlock *> &translator,
|
||||
BytecodeBlock *subroutineHeader, BytecodeBlock *subroutineRet):
|
||||
generation(0), verificationEnvIn(env, translator), subroutineHeader(subroutineHeader), subroutineRet(subroutineRet) {}
|
||||
};
|
||||
|
||||
// Temporary fields for translation into a primitive graph
|
||||
struct TranslationTemps
|
||||
{
|
||||
Uint32 nSeenPredecessors; // Number of already seen BasicBlock successor pointers that refer to this BasicBlock
|
||||
TranslationEnv translationEnvIn; // State of locals and stack at beginning of block
|
||||
// For efficiency, the end block's envIn contains information only about memory bindings.
|
||||
bool envInInitialized BOOL_8; // True if envIn has already been initialized; if false, no ControlNodes point to
|
||||
#ifdef DEBUG // firstControlNode yet.
|
||||
bool generatedControlNodes BOOL_8; // True if ControlNodes have already been generated or optimized out for this BasicBlock
|
||||
#endif
|
||||
mutable ControlNode *firstControlNode; // First ControlNode generated for this BasicBlock or nil if none yet or optimized out
|
||||
DoublyLinkedList<ControlEdge> predecessors; // List of predecessors of this block's first generated control node
|
||||
// (Moved into firstControlNode when the ControlNodes for this BasicBlock are created.)
|
||||
TranslationTemps(TranslationCommonEnv &commonEnv);
|
||||
};
|
||||
|
||||
CheckedUnion2(VerificationTemps, TranslationTemps) temps;
|
||||
public:
|
||||
|
||||
BasicBlock(BytecodeGraph &bytecodeGraph, Kind kind, StackNormalization stackNormalization);
|
||||
|
||||
bool hasKind(Kind k) const {return k == kind;}
|
||||
Uint32 nSuccessors() const {return successorsEnd - successorsBegin;}
|
||||
BytecodeBlock &getSuccessor(Uint32 n) const;
|
||||
|
||||
// Accessors for temporary fields for verification
|
||||
void initVerification(VerificationEnv::Common &c);
|
||||
void initVerification(BasicBlock &src, Function1<BytecodeBlock *, BytecodeBlock *> &translator);
|
||||
Uint32 &getGeneration() {return temps.getVerificationTemps().generation;}
|
||||
VerificationEnv &getVerificationEnvIn() {return temps.getVerificationTemps().verificationEnvIn;}
|
||||
BytecodeBlock *&getSubroutineHeader() {return temps.getVerificationTemps().subroutineHeader;}
|
||||
BytecodeBlock *&getSubroutineRet() {return temps.getVerificationTemps().subroutineRet;}
|
||||
BasicBlock *&getClone() {return temps.getVerificationTemps().clone;}
|
||||
bool &getRecompute() {return temps.getVerificationTemps().recompute;}
|
||||
|
||||
// Accessors for temporary fields for primitive graph translation
|
||||
void initTranslation(TranslationCommonEnv &commonEnv);
|
||||
Uint32 &getNSeenPredecessors() {return temps.getTranslationTemps().nSeenPredecessors;}
|
||||
bool &getEnvInInitialized() {return temps.getTranslationTemps().envInInitialized;}
|
||||
#ifdef DEBUG
|
||||
bool getGeneratedControlNodes() const {return temps.getTranslationTemps().generatedControlNodes;}
|
||||
bool &getGeneratedControlNodes() {return temps.getTranslationTemps().generatedControlNodes;}
|
||||
#endif
|
||||
ControlNode *&getFirstControlNode() const {return temps.getTranslationTemps().firstControlNode;}
|
||||
DoublyLinkedList<ControlEdge> &getPredecessors() {return temps.getTranslationTemps().predecessors;}
|
||||
public:
|
||||
TranslationEnv &getTranslationEnvIn() {return temps.getTranslationTemps().translationEnvIn;}
|
||||
const TranslationEnv &getTranslationEnvIn() const {return temps.getTranslationTemps().translationEnvIn;}
|
||||
|
||||
// Debugging
|
||||
#ifdef DEBUG_LOG
|
||||
int printRef(LogModuleObject &f, const BytecodeGraph &bg) const;
|
||||
void printPretty(LogModuleObject &f, const BytecodeGraph &bg, const ConstantPool *c, int margin) const;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
struct EndBlock: BasicBlock
|
||||
{
|
||||
EndBlock(BytecodeGraph &bytecodeGraph);
|
||||
};
|
||||
|
||||
|
||||
struct CatchBlock: BasicBlock
|
||||
{
|
||||
private:
|
||||
BasicBlock *successor; // The handler of exceptions caught here
|
||||
public:
|
||||
|
||||
CatchBlock(BytecodeGraph &bytecodeGraph, BytecodeBlock &handler);
|
||||
CatchBlock(BytecodeGraph &bytecodeGraph, const CatchBlock &src);
|
||||
|
||||
BytecodeBlock &getHandler() const;
|
||||
};
|
||||
|
||||
|
||||
struct BytecodeBlock: BasicBlock
|
||||
{
|
||||
enum Subkind
|
||||
{
|
||||
skNormal, // A BytecodeBlock that does not contain any return, ret, or jsr instructions
|
||||
skReturn, // A BytecodeBlock consisting entirely of one of the return instructions
|
||||
skJsr, // A BytecodeBlock consisting entirely of a jsr or jsr_w instruction whose subroutine may or may not return
|
||||
skJsrNoRet, // A BytecodeBlock consisting entirely of a jsr or jsr_w instruction whose subroutine is known never to return
|
||||
skRet, // A BytecodeBlock consisting entirely of a ret or wide ret instruction
|
||||
skForward // A formerly skRet BytecodeBlock that is known to unconditionally branch to its one successor
|
||||
};
|
||||
|
||||
const bytecode *const bytecodesBegin; // Pointer to first bytecode in this block
|
||||
const bytecode *bytecodesEnd; // End of last bytecode + 1
|
||||
CatchBlock *catchBlock; // If a catch handler begins at bytecodesBegin, a CatchBlock that points back to
|
||||
// this block; nil otherwise.
|
||||
const Class **exceptionClasses; // Array of exception classes; an exception matching the nth exception class
|
||||
const Class **exceptionClassesEnd; // would transfer control to the nth exceptional successor
|
||||
Subkind subkind ENUM_8; // Description of contents of this BytecodeBlock
|
||||
|
||||
BytecodeBlock(BytecodeGraph &bytecodeGraph, const bytecode *bytecodesBegin, StackNormalization stackNormalization);
|
||||
BytecodeBlock(BytecodeGraph &bytecodeGraph, const BytecodeBlock &src);
|
||||
|
||||
bool hasSubkind(Subkind sk) const {return sk == subkind;}
|
||||
bool handlersForbidden() const;
|
||||
|
||||
void initSuccessors(BasicBlock **successors, Uint32 nSuccessors, Int32 nActiveHandlers, Pool &bytecodeGraphPool);
|
||||
BasicBlock **transformToJsrNoRet();
|
||||
void transformToForward(BasicBlock **successor);
|
||||
};
|
||||
|
||||
|
||||
class BytecodeGraph
|
||||
{
|
||||
public:
|
||||
// Persistent fields
|
||||
Pool &bytecodeGraphPool; // Pool for allocating this BytecodeGraph's nodes (blocks)
|
||||
ClassFileSummary &classFileSummary; // Java class file descriptor
|
||||
Method& method; // The method this
|
||||
// BytecodeGraph represents
|
||||
const Uint32 nLocals; // Number of words of local variables
|
||||
const Uint32 stackSize; // Number of words of local stack space
|
||||
const bool isInstanceMethod BOOL_8; // True if this method has a "this" argument
|
||||
const bool isSynchronized BOOL_8; // True if this method is synchronized
|
||||
const uint nArguments; // Number of incoming arguments (including "this")
|
||||
const ValueKind *const argumentKinds; // Kinds of incoming arguments (including "this")
|
||||
const ValueKind resultKind; // Kind of result
|
||||
const bytecode *const bytecodesBegin; // Pointer to first bytecode in this function (must be word-aligned)
|
||||
const bytecode *const bytecodesEnd; // End of last bytecode in this function + 1
|
||||
const Uint32 bytecodesSize; // bytecodesEnd - bytecodesBegin
|
||||
const Uint32 nExceptionEntries; // Number of exception table entries
|
||||
const ExceptionItem *const exceptionEntries;// Exception table
|
||||
BytecodeBlock *beginBlock; // Starting basic block in function
|
||||
EndBlock *endBlock; // The end BasicBlock
|
||||
BytecodeBlock *returnBlock; // The return BytecodeBlock or nil if none
|
||||
private:
|
||||
DoublyLinkedList<BasicBlock> blocks; // List of basic blocks (may include unreachable blocks)
|
||||
Uint32 nBlocks; // Number of reachable basic blocks
|
||||
|
||||
// Fields computed by depthFirstSearch
|
||||
BasicBlock **dfsList; // Array of reachable basic blocks ordered by depth-first search;
|
||||
// each block's dfsNum is an index into this array.
|
||||
const bytecode returnBytecode; // The kind of return bytecode that corresponds to resultKind
|
||||
bool hasBackEdges BOOL_8; // True if this graph contains a cycle
|
||||
bool hasJSRs BOOL_8; // True if this graph contains a jsr or jsr_w instruction
|
||||
|
||||
public:
|
||||
BytecodeGraph(ClassFileSummary &cfs, Pool &bytecodeGraphPool, bool isInstanceMethod, bool isSynchronized,
|
||||
uint nArguments, const ValueKind *argumentKinds, ValueKind resultKind, Uint32 nLocals, Uint32 stackSize,
|
||||
const bytecode *bytecodesBegin, Uint32 bytecodesSize, Uint32 nExceptionEntries,
|
||||
const ExceptionItem *exceptionEntries, Method& method);
|
||||
private:
|
||||
BytecodeGraph(const BytecodeGraph &); // Copying forbidden
|
||||
void operator=(const BytecodeGraph &); // Copying forbidden
|
||||
public:
|
||||
|
||||
BasicBlock **getDFSList() const {assert(dfsList); return dfsList;}
|
||||
Uint32 getDFSListLength() const {assert(dfsList); return nBlocks;}
|
||||
void invalidateDFSList() {DEBUG_ONLY(dfsList = 0);}
|
||||
void addBlock(BasicBlock &block) {blocks.addLast(block); invalidateDFSList();}
|
||||
|
||||
static const bytecode *switchAlign(const bytecode *bc);
|
||||
|
||||
private:
|
||||
BytecodeBlock &followGotos(Uint32 initialOffset, Int32 displacement, BytecodeBlock **blocks);
|
||||
BytecodeBlock ¬eBlockBoundary(Uint32 offset, BytecodeBlock **blocks);
|
||||
void noteExceptionBoundaries(BytecodeBlock **blocks, Int32 *activeHandlerDeltas);
|
||||
const bytecode *noteTableSwitchTargets(const bytecode *bc, BytecodeBlock **blocks);
|
||||
const bytecode *noteLookupSwitchTargets(const bytecode *bc, BytecodeBlock **blocks);
|
||||
const bytecode *recordTableSwitchTargets(const bytecode *bc, BytecodeBlock **blocks, BasicBlock **&successors,
|
||||
Uint32 &nSuccessors, Int32 nActiveHandlers);
|
||||
const bytecode *recordLookupSwitchTargets(const bytecode *bc, BytecodeBlock **blocks, BasicBlock **&successors,
|
||||
Uint32 &nSuccessors, Int32 nActiveHandlers);
|
||||
void recordHandlerTargets(BytecodeBlock **blocks);
|
||||
void verifyNoBoundariesBetween(const bytecode *bc1, const bytecode *bc2, BytecodeBlock **blocks) const;
|
||||
void createBlocks(Pool &tempPool);
|
||||
bool depthFirstSearch(Pool &tempPool);
|
||||
void splitCatchNodes();
|
||||
public:
|
||||
void divideIntoBlocks(Pool &tempPool);
|
||||
|
||||
#ifdef DEBUG_LOG
|
||||
bool dfsBlockNumbersValid() const {return dfsList != 0;}
|
||||
void print(LogModuleObject &f, const ConstantPool *c) const;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
// --- INLINES ----------------------------------------------------------------
|
||||
|
||||
|
||||
//
|
||||
// Initialize BasicBlock fields used for translation into a primitive graph.
|
||||
//
|
||||
inline BasicBlock::TranslationTemps::TranslationTemps(TranslationCommonEnv &commonEnv):
|
||||
nSeenPredecessors(0),
|
||||
translationEnvIn(commonEnv),
|
||||
envInInitialized(false),
|
||||
firstControlNode(0)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
generatedControlNodes = false;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Initialize this BasicBlock contained in the given BytecodeGraph.
|
||||
//
|
||||
inline BasicBlock::BasicBlock(BytecodeGraph &bytecodeGraph, Kind kind, StackNormalization stackNormalization):
|
||||
kind(kind),
|
||||
stackNormalization(stackNormalization)
|
||||
{
|
||||
bytecodeGraph.addBlock(*this);
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Return the nth regular (non-exceptional) successor.
|
||||
// Don't call this to get the successor of the return node, because it is not
|
||||
// a BytecodeBlock.
|
||||
//
|
||||
inline BytecodeBlock &BasicBlock::getSuccessor(Uint32 n) const
|
||||
{
|
||||
assert(n < nSuccessors() && successorsBegin[n]->hasKind(bbBytecode));
|
||||
return *static_cast<BytecodeBlock *>(successorsBegin[n]);
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Prepare this BasicBlock for verification.
|
||||
//
|
||||
inline void BasicBlock::initVerification(VerificationEnv::Common &c)
|
||||
{
|
||||
new(temps.initVerificationTemps()) VerificationTemps(c);
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Prepare this BasicBlock for verification using a copy of src's env, subroutineHeader,
|
||||
// and subroutineRet fields. Translate any BasicBlock pointers using the given
|
||||
// translator. Set the generation to zero.
|
||||
//
|
||||
inline void BasicBlock::initVerification(BasicBlock &src, Function1<BytecodeBlock *, BytecodeBlock *> &translator)
|
||||
{
|
||||
new(temps.initVerificationTemps()) VerificationTemps(src.getVerificationEnvIn(), translator,
|
||||
translator(src.getSubroutineHeader()), translator(src.getSubroutineRet()));
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Prepare this BasicBlock for translation into a primitive graph.
|
||||
//
|
||||
inline void BasicBlock::initTranslation(TranslationCommonEnv &commonEnv)
|
||||
{
|
||||
new(temps.initTranslationTemps()) TranslationTemps(commonEnv);
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Initialize this CatchBlock contained in the given BytecodeGraph.
|
||||
// The CatchBlock's successor is given.
|
||||
//
|
||||
inline CatchBlock::CatchBlock(BytecodeGraph &bytecodeGraph, BytecodeBlock &handler):
|
||||
BasicBlock(bytecodeGraph, bbCatch, sn0),
|
||||
successor(&handler)
|
||||
{
|
||||
successorsBegin = &successor;
|
||||
successorsEnd = &successor + 1;
|
||||
handlersEnd = successorsEnd;
|
||||
#ifdef DEBUG
|
||||
successorsPhysicalEnd = handlersEnd;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Make a copy of the src CatchBlock pointing to the same handler as src and add it
|
||||
// to the given graph. However, do not copy or initialize the VerificationTemps or
|
||||
// TranslationTemps in the copy.
|
||||
//
|
||||
inline CatchBlock::CatchBlock(BytecodeGraph &bytecodeGraph, const CatchBlock &src):
|
||||
BasicBlock(bytecodeGraph, bbCatch, sn0),
|
||||
successor(src.successor)
|
||||
{
|
||||
successorsBegin = &successor;
|
||||
successorsEnd = &successor + 1;
|
||||
handlersEnd = successorsEnd;
|
||||
#ifdef DEBUG
|
||||
successorsPhysicalEnd = handlersEnd;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Return the handler passed to this CatchBlock's constructor.
|
||||
//
|
||||
inline BytecodeBlock &CatchBlock::getHandler() const
|
||||
{
|
||||
return *static_cast<BytecodeBlock *>(successor);
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Initialize this BytecodeBlock contained in the given BytecodeGraph.
|
||||
// The caller should later call initSuccessors to set up this block's successors.
|
||||
//
|
||||
inline BytecodeBlock::BytecodeBlock(BytecodeGraph &bytecodeGraph, const bytecode *bytecodesBegin, StackNormalization stackNormalization):
|
||||
BasicBlock(bytecodeGraph, bbBytecode, stackNormalization),
|
||||
bytecodesBegin(bytecodesBegin),
|
||||
catchBlock(0),
|
||||
subkind(skNormal)
|
||||
{}
|
||||
|
||||
|
||||
//
|
||||
// Return true if this block must not have any handlers and createBlocks should not
|
||||
// allocates any room for handlers.
|
||||
//
|
||||
inline bool BytecodeBlock::handlersForbidden() const
|
||||
{
|
||||
return !hasSubkind(skNormal);
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Change this skJsr block to a skJsrNoRet block. Return a pointer suitable as an
|
||||
// argument to transformToForward.
|
||||
//
|
||||
inline BasicBlock **BytecodeBlock::transformToJsrNoRet()
|
||||
{
|
||||
assert(hasSubkind(skJsr));
|
||||
subkind = skJsrNoRet;
|
||||
BasicBlock **e = successorsEnd - 1;
|
||||
successorsEnd = e;
|
||||
handlersEnd = e;
|
||||
#ifdef DEBUG
|
||||
successorsPhysicalEnd = e;
|
||||
#endif
|
||||
return e;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Change this skRet block to a skForward block. successor must point to a one-word
|
||||
// block of memory allocated from this bytecode graph's pool that points to this block's
|
||||
// unique successor.
|
||||
//
|
||||
inline void BytecodeBlock::transformToForward(BasicBlock **successor)
|
||||
{
|
||||
assert(hasSubkind(skRet));
|
||||
subkind = skForward;
|
||||
successorsBegin = successor;
|
||||
BasicBlock **e = successor + 1;
|
||||
successorsEnd = e;
|
||||
handlersEnd = e;
|
||||
#ifdef DEBUG
|
||||
successorsPhysicalEnd = e;
|
||||
#endif
|
||||
bytecodesEnd = bytecodesBegin;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Return bc word-aligned as needed for a tableswitch or lookupswitch bytecode.
|
||||
//
|
||||
inline const bytecode *BytecodeGraph::switchAlign(const bytecode *bc)
|
||||
{
|
||||
// Assumes that bytecodesBegin is word-aligned.
|
||||
return (const bytecode *)((size_t)bc + 3 & -4);
|
||||
}
|
||||
|
||||
|
||||
#endif
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,161 @@
|
|||
/* -*- 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 BYTECODETRANSLATOR_H
|
||||
#define BYTECODETRANSLATOR_H
|
||||
|
||||
#include "BytecodeGraph.h"
|
||||
|
||||
class BytecodeTranslator: public TranslationCommonEnv
|
||||
{
|
||||
// Helper for translating a BytecodeBlock
|
||||
class BlockTranslator
|
||||
{
|
||||
const BytecodeTranslator &bt; // Backpointer to the current BytecodeTranslator
|
||||
Pool &envPool; // The pool for allocating translation environment parts (disappears after primitives are generated)
|
||||
Pool &primitivePool; // The pool for allocating new parts of the primitive graph
|
||||
ClassFileSummary &classFileSummary; // The BytecodeGraph's ClassFileSummary
|
||||
TranslationEnv *translationEnv; // The current state of local and stack variables; never nil
|
||||
BytecodeBlock █ // The block which we're currently translating
|
||||
ControlNode *cn; // The control node currently being generated or nil if we are done
|
||||
|
||||
public:
|
||||
BlockTranslator(const BytecodeTranslator &bt, TranslationEnv &env, BytecodeBlock &block, ControlNode *cn);
|
||||
|
||||
TranslationEnv &getEnv() const {return *translationEnv;}
|
||||
void setEnv(TranslationEnv &env) {translationEnv = &env;}
|
||||
ControlGraph &getControlGraph() const {return bt.controlGraph;}
|
||||
|
||||
private:
|
||||
void appendControlNode(ControlEdge &e);
|
||||
void genException(ControlKind ck, Primitive *tryPrimitive, const Class &exceptionClass, bool canOwnEnv) const;
|
||||
void genThrowCommon(const VariableOrConstant &exception, const Class &exceptionClass, bool canOwnEnv);
|
||||
void genSimpleThrow(cobj exceptionObject, bool canOwnEnv);
|
||||
void genPossibleThrow(Primitive &prim, const Class &exceptionClass);
|
||||
void createBasicBlock(BasicBlock &successor);
|
||||
void createIf(Condition2 cond2, bool reverseCond2,
|
||||
VariableOrConstant &c, BasicBlock &successorFalse,
|
||||
BasicBlock &successorTrue, Uint32 bci);
|
||||
void createContinuedIf(Condition2 cond2, bool reverseCond2,
|
||||
VariableOrConstant &c,
|
||||
BasicBlock &successorTrue, Uint32 bci);
|
||||
void genNullGuard(const VariableOrConstant &arg, Uint32 bci);
|
||||
void genTableSwitch(const bytecode *bc, Uint32 bci);
|
||||
void genLookupSwitch(const bytecode *bc, Uint32 bci);
|
||||
void genReadObjectType(const VariableOrConstant &object,
|
||||
VariableOrConstant &type, Uint32 bci) const;
|
||||
void genGetStatic(ConstantPoolIndex cpi, Uint32 bci) const;
|
||||
void genPutStatic(ConstantPoolIndex cpi, Uint32 bci) const;
|
||||
void genGetField(ConstantPoolIndex cpi, Uint32 bci);
|
||||
void genPutField(ConstantPoolIndex cpi, Uint32 bci);
|
||||
void genReadArrayLength(const VariableOrConstant &arrayAddr, VariableOrConstant &arrayLength, Uint32 bci);
|
||||
void genArrayCastGuard(const VariableOrConstant &array, const VariableOrConstant &object);
|
||||
void genArrayEltAccess(TypeKind tk, bool write, Uint32 bci);
|
||||
void genCheckInterfaceAssignability(VariableOrConstant &type, const Type &targetInterface, VariableOrConstant *results, Uint32 bci) const;
|
||||
void genCheckCastNonNull(const Type &t, const VariableOrConstant &arg, Uint32 bci);
|
||||
void genCheckCast(ConstantPoolIndex cpi, const VariableOrConstant &arg, Uint32 bci);
|
||||
void genInstanceOfNonNull(const Type &t, const VariableOrConstant &arg, VariableOrConstant &result, ControlNode *cnFalse, Uint32 bci);
|
||||
void genInstanceOf(ConstantPoolIndex cpi, const VariableOrConstant &arg, VariableOrConstant &result, Uint32 bci);
|
||||
void genNewCommon(const SysCall &sysCall, uint nArgs, const VariableOrConstant *args, Uint32 bci);
|
||||
void genTempArrayOfDim(Int32 dim, Uint32 bci);
|
||||
Primitive &genMonitor(PrimitiveOperation op, const VariableOrConstant &arg, Uint32 bci) const;
|
||||
void genThrow(const VariableOrConstant &exception);
|
||||
void genReceiverNullCheck(const Signature &sig, VariableOrConstant &receiver, Uint32 bci);
|
||||
void genVirtualLookup(const VariableOrConstant &receiver, Uint32 vIndex, VariableOrConstant &functionAddr, Uint32 bci) const;
|
||||
void genInterfaceLookup(const VariableOrConstant &receiver, Uint32 interfaceNumber, Uint32 vIndex, const Method *method,
|
||||
VariableOrConstant &functionAddr, Uint32 bci) const;
|
||||
void genInvoke(bytecode opcode, ConstantPoolIndex cpi, uint nArgs, Uint32 bci);
|
||||
public:
|
||||
void genLivePrimitives();
|
||||
void genCycleHeader();
|
||||
|
||||
#ifdef DEBUG
|
||||
bool translatorDone() const {return !cn;}
|
||||
#endif
|
||||
};
|
||||
|
||||
BytecodeGraph &bytecodeGraph; // The BytecodeGraph from which we're translating
|
||||
ControlGraph &controlGraph; // The primitive graph to which we're translating
|
||||
|
||||
BytecodeTranslator(BytecodeGraph &bytecodeGraph, ControlGraph &controlGraph, Pool &envPool);
|
||||
|
||||
static void normalizeEnv(TranslationEnv &env, BasicBlock::StackNormalization stackNormalization);
|
||||
static void linkPredecessor(BasicBlock &block, ControlEdge &controlEdge, TranslationEnv &predecessorEnv, bool canOwnEnv);
|
||||
static void finishedOnePredecessor(BasicBlock &block);
|
||||
static void finishedOutgoingEdges(BasicBlock &block);
|
||||
ControlNode *createControlNode(BasicBlock &block, bool mergeBlock) const;
|
||||
|
||||
void genPrimitives(EndBlock &block) const;
|
||||
void genPrimitives(CatchBlock &block) const;
|
||||
void genPrimitives(BytecodeBlock &block) const;
|
||||
void genRawPrimitiveGraph() const;
|
||||
void addSynchronization(VariableOrConstant &syncHolder, Uint32 bci) const;
|
||||
public:
|
||||
static ControlGraph *genPrimitiveGraph(BytecodeGraph &bytecodeGraph, Pool &primitivePool, Pool &tempPool);
|
||||
|
||||
friend class BytecodeTranslator::BlockTranslator; // BlockTranslator calls the private methods above
|
||||
};
|
||||
|
||||
|
||||
// --- INLINES ----------------------------------------------------------------
|
||||
|
||||
|
||||
//
|
||||
// Initialize a translator that will translate a single BytecodeBlock.
|
||||
//
|
||||
inline BytecodeTranslator::BlockTranslator::BlockTranslator(const BytecodeTranslator &bt, TranslationEnv &env, BytecodeBlock &block,
|
||||
ControlNode *cn):
|
||||
bt(bt),
|
||||
envPool(bt.envPool),
|
||||
primitivePool(bt.primitivePool),
|
||||
classFileSummary(bt.bytecodeGraph.classFileSummary),
|
||||
translationEnv(&env),
|
||||
block(block),
|
||||
cn(cn)
|
||||
{}
|
||||
|
||||
|
||||
//
|
||||
// Initialize a translator that will construct the control graph out of the given
|
||||
// bytecode graph.
|
||||
// envPool will be used for temporary allocation of TranslationEnvs and the data
|
||||
// structures they contain; all of these can be deallocated when the BytecodeTranslator
|
||||
// is done.
|
||||
//
|
||||
inline BytecodeTranslator::BytecodeTranslator(BytecodeGraph &bytecodeGraph, ControlGraph &controlGraph, Pool &envPool):
|
||||
TranslationCommonEnv(envPool, controlGraph.pool, bytecodeGraph.nLocals, bytecodeGraph.stackSize),
|
||||
bytecodeGraph(bytecodeGraph),
|
||||
controlGraph(controlGraph)
|
||||
{}
|
||||
|
||||
|
||||
//
|
||||
// Take note that we just completely generated code for a BasicBlock b
|
||||
// that is one of the predecessors of BasicBlock block. If b jumps to
|
||||
// BasicBlock block, then BasicBlock block's environment should have been
|
||||
// adjusted accordingly by now.
|
||||
// This method lets BasicBlock block keep track of how many other
|
||||
// BasicBlocks remain that can jump to BasicBlock block and whose
|
||||
// environments have not yet been merged into BasicBlock block.
|
||||
//
|
||||
inline void BytecodeTranslator::finishedOnePredecessor(BasicBlock &block)
|
||||
{
|
||||
assert(block.getNSeenPredecessors() != block.nPredecessors);
|
||||
block.getNSeenPredecessors()++;
|
||||
}
|
||||
|
||||
#endif
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,98 @@
|
|||
/* -*- 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 BYTECODEVERIFIER_H
|
||||
#define BYTECODEVERIFIER_H
|
||||
|
||||
#include "BytecodeGraph.h"
|
||||
#include "Tree.h"
|
||||
|
||||
|
||||
// A data structure for keeping track of all call sites for each subroutine.
|
||||
// Only includes mappings for skJsr call sites; skJsrNoRet call sites are deleted
|
||||
// from this association list as soon as they are discovered not to have a ret.
|
||||
class SubroutineCallSiteList
|
||||
{
|
||||
struct Association
|
||||
{
|
||||
BytecodeBlock &subroutine; // A subroutine (never nil) ...
|
||||
BytecodeBlock *const callSite; // ... and one of its call sites (may be nil)
|
||||
|
||||
Association(BytecodeBlock &subroutine, BytecodeBlock &callSite): subroutine(subroutine), callSite(&callSite) {}
|
||||
explicit Association(BytecodeBlock &subroutine): subroutine(subroutine), callSite(0) {}
|
||||
|
||||
bool operator<(const Association &a2) const;
|
||||
};
|
||||
|
||||
class Node: public TreeNode<Node>
|
||||
{
|
||||
Association association; // An association of a subroutine with one of its call sites
|
||||
|
||||
public:
|
||||
explicit Node(Association &association): association(association) {}
|
||||
const Association &getKey() const {return association;}
|
||||
};
|
||||
|
||||
public:
|
||||
class Iterator
|
||||
{
|
||||
BytecodeBlock &subroutine; // The subroutine whose call sites we're iterating
|
||||
Node *node; // The node currently at the iterator's position or nil if none
|
||||
|
||||
public:
|
||||
Iterator(SubroutineCallSiteList &csList, BytecodeBlock &subroutine);
|
||||
bool more() const {return node != 0;}
|
||||
BytecodeBlock &operator*() const {assert(node); return *node->getKey().callSite;}
|
||||
void operator++();
|
||||
};
|
||||
|
||||
private:
|
||||
SortedTree<Node, const Association &> tree; // The actual container of all associations in this SubroutineCallSiteList
|
||||
public:
|
||||
|
||||
void addAssociation(BytecodeBlock &subroutine, BytecodeBlock &callSite, Pool &pool);
|
||||
void removeAssociation(BytecodeBlock &subroutine, BytecodeBlock &callSite);
|
||||
BytecodeBlock *popAssociation(BytecodeBlock *&subroutine, bool &onlyOneCallSite);
|
||||
|
||||
friend class Iterator;
|
||||
};
|
||||
|
||||
|
||||
class BytecodeVerifier: public VerificationEnv::Common
|
||||
{
|
||||
BytecodeGraph &bytecodeGraph; // The BytecodeGraph which we're examining
|
||||
ClassFileSummary &classFileSummary; // The BytecodeGraph's ClassFileSummary
|
||||
SubroutineCallSiteList subroutineCallSiteList; // A mapping from subroutines to their call sites
|
||||
|
||||
BytecodeVerifier(BytecodeGraph &bytecodeGraph, Pool &envPool);
|
||||
|
||||
// Inlining subroutines
|
||||
void normalizeEnv(VerificationEnv &env, BasicBlock::StackNormalization stackNormalization);
|
||||
bool predecessorChanged(BasicBlock &block, VerificationEnv &predecessorEnv, Uint32 generation, bool canOwnEnv);
|
||||
bool predecessorChanged(BasicBlock **blocksBegin, BasicBlock **blocksEnd, VerificationEnv &predecessorEnv,
|
||||
Uint32 generation, bool canOwnEnv);
|
||||
bool propagateDataflow(CatchBlock &block, Uint32 generation);
|
||||
bool propagateDataflow(BytecodeBlock &block, Uint32 generation);
|
||||
void computeDataflow();
|
||||
void computeRetReachables();
|
||||
void duplicateSubroutines();
|
||||
|
||||
public:
|
||||
static void inlineSubroutines(BytecodeGraph &bytecodeGraph, Pool &tempPool);
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,103 @@
|
|||
/* -*- 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.
|
||||
*/
|
||||
#include "Fundamentals.h"
|
||||
#include "ErrorHandling.h"
|
||||
|
||||
|
||||
#ifdef DEBUG
|
||||
|
||||
char* verifyErrorString[] =
|
||||
{
|
||||
"Unknown cause",
|
||||
"Functionality not implemented yet",
|
||||
"Error in reading class file: file not found, or has errors",
|
||||
"Badly formatted class file",
|
||||
"Class does not have permissions to access a particular field/method",
|
||||
"Field not found in class",
|
||||
"Method not found in class",
|
||||
"No bytecodes in a function",
|
||||
"Bad bytecode opcode",
|
||||
"Bad offset to a bytecode instruction",
|
||||
"Bad type passed to newarray instruction",
|
||||
"Constant pool index out of range",
|
||||
"Wrong return instruction used in this function",
|
||||
"Bad tableswitch or lookupswitch bytecode",
|
||||
"More than one ret bytecode for the same subroutine",
|
||||
"Recursive call to a subroutine or multiple returns from the same subroutine",
|
||||
"Bad combination of types in a bytecode",
|
||||
"Attempt to pop from an empty bytecode stack",
|
||||
"Attempt to push onto a full bytecode stack",
|
||||
"Stack depth at a point differs based on how execution got there",
|
||||
"Index of local variable out of range",
|
||||
"Attempt to write to a constant (final) field",
|
||||
"Given class not found in class file",
|
||||
"Catch filter class not a subclass of Throwable",
|
||||
"Class can be its own superclass",
|
||||
"Compiler internal limits reached",
|
||||
"Attempt to invoke abstract method",
|
||||
"Binary incompatibility / incompatibleClassChange"
|
||||
};
|
||||
|
||||
char* runtimeErrorString[] =
|
||||
{
|
||||
"Unknown cause",
|
||||
"Internal error",
|
||||
"Functionality not implemented yet",
|
||||
"incorrect argument to a method",
|
||||
"prohibited operation",
|
||||
"Security violation",
|
||||
"IO Error",
|
||||
"fileNotFound",
|
||||
"Unable to link method",
|
||||
"Null Pointer argument",
|
||||
"Attempt to instantiate an abstract class"
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
//
|
||||
// This is called when there's something wrong with a class.
|
||||
//
|
||||
NS_EXTERN void
|
||||
verifyError(VerifyError::Cause cause)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr, "\n*** VERIFY ERROR *** [%d] %s\n", cause, verifyErrorString[cause]);
|
||||
#endif
|
||||
|
||||
#ifdef __GNUC__
|
||||
exit(cause);
|
||||
#endif
|
||||
throw VerifyError(cause);
|
||||
}
|
||||
|
||||
//
|
||||
// This is called when there is an error during runtime.
|
||||
//
|
||||
NS_EXTERN void
|
||||
runtimeError(RuntimeError::Cause cause)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr, "\n*** RUNTIME ERROR *** [%d] %s\n", cause, runtimeErrorString[cause]);
|
||||
#endif
|
||||
|
||||
#ifdef __GNUC__
|
||||
exit(cause);
|
||||
#endif
|
||||
throw RuntimeError(cause);
|
||||
}
|
|
@ -0,0 +1,91 @@
|
|||
/* -*- 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 ERRORHANDLING_H
|
||||
#define ERRORHANDLING_H
|
||||
|
||||
#include "NativeDefs.h"
|
||||
|
||||
struct VerifyError
|
||||
{
|
||||
enum Cause
|
||||
{
|
||||
unknown, // Unknown cause
|
||||
notImplemented, // Functionality not implemented yet
|
||||
noClassDefFound, // Error in reading class file: file not found, or has errors
|
||||
badClassFormat, // Badly formatted class file
|
||||
illegalAccess, // Class does not have permissions to access a particular field/method
|
||||
noSuchField, // Field not found in class
|
||||
noSuchMethod, // Method not found in class
|
||||
noBytecodes, // No bytecodes in a function
|
||||
badBytecode, // Bad bytecode opcode
|
||||
badBytecodeOffset, // Bad offset to a bytecode instruction
|
||||
badNewArrayType, // Bad type passed to newarray instruction
|
||||
badConstantPoolIndex, // Constant pool index out of range
|
||||
badReturn, // Wrong return instruction used in this function
|
||||
badSwitchBytecode, // Bad tableswitch or lookupswitch bytecode
|
||||
multipleRet, // More than one ret bytecode for the same subroutine
|
||||
jsrNestingError, // Recursive call to a subroutine or multiple returns from the same subroutine
|
||||
badType, // Bad combination of types in a bytecode
|
||||
bytecodeStackUnderflow, // Attempt to pop from an empty bytecode stack
|
||||
bytecodeStackOverflow, // Attempt to push onto a full bytecode stack
|
||||
bytecodeStackDynamic, // Stack depth at a point differs based on how execution got there
|
||||
noSuchLocal, // Index of local variable out of range
|
||||
writeToConst, // Attempt to write to a constant (final) field
|
||||
classNotFound, // Given class not found in class file
|
||||
nonThrowableCatch, // Catch filter class not a subclass of Throwable
|
||||
classCircularity, // Class can be its own superclass
|
||||
resourceExhausted, // Compiler internal limits reached
|
||||
abstractMethod, // Attempt to invoke abstract method
|
||||
incompatibleClassChange // Binary incompatibility
|
||||
};
|
||||
|
||||
const Cause cause;
|
||||
|
||||
VerifyError(Cause cause): cause(cause) {}
|
||||
};
|
||||
|
||||
NS_EXTERN
|
||||
void verifyError(VerifyError::Cause cause);
|
||||
|
||||
struct RuntimeError
|
||||
{
|
||||
enum Cause
|
||||
{
|
||||
unknown, // Unknown cause
|
||||
internal, // Internal error
|
||||
notImplemented, // Functionality not implemented yet
|
||||
illegalArgument, // incorrect argument to a method
|
||||
illegalAccess, // prohibited operation
|
||||
securityViolation, // Security violation
|
||||
IOError, // IO Error
|
||||
fileNotFound,
|
||||
linkError, // Unable to link method
|
||||
nullPointer, // Null Pointer argument
|
||||
notInstantiable // Attempt to instantiate an abstract class
|
||||
};
|
||||
|
||||
const Cause cause;
|
||||
|
||||
RuntimeError(Cause cause): cause(cause) {}
|
||||
};
|
||||
|
||||
NS_EXTERN
|
||||
void runtimeError(RuntimeError::Cause cause);
|
||||
|
||||
|
||||
#endif
|
|
@ -0,0 +1,215 @@
|
|||
/* -*- 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 LOCALENV_H
|
||||
#define LOCALENV_H
|
||||
|
||||
#include "Fundamentals.h"
|
||||
|
||||
|
||||
// Information common to all environments in a graph
|
||||
struct CommonEnv
|
||||
{
|
||||
Pool &envPool; // Memory pool from which to allocate
|
||||
const Uint32 nLocals; // Number of words of local variables
|
||||
const Uint32 stackBase; // Index of first stack temporary
|
||||
const Uint32 stackSize; // Number of words of local stack space
|
||||
const Uint32 nEnvSlots; // nMemoryBindings + nLocals + stackSize
|
||||
|
||||
CommonEnv(Pool &envPool, Uint32 nMemoryBindings, Uint32 nLocals, Uint32 stackSize):
|
||||
envPool(envPool), nLocals(nLocals), stackBase(nMemoryBindings + nLocals), stackSize(stackSize),
|
||||
nEnvSlots(nMemoryBindings + nLocals + stackSize) {}
|
||||
};
|
||||
|
||||
|
||||
// A single environment containings bindings of class Binding.
|
||||
// Class Common, which should be derived from CommonEnv, contains information
|
||||
// common to all environments in a graph.
|
||||
template<class Binding, class Common, Uint32 nMemoryBindings>
|
||||
class LocalEnv
|
||||
{
|
||||
protected:
|
||||
const Common &commonEnv; // Backpointer to information shared among all LocalEnvs in a graph
|
||||
private:
|
||||
Binding *bindings; // Array of:
|
||||
// nMemoryBindings memory bindings
|
||||
// nLocals local variable bindings
|
||||
// stackSize stack temporaries' bindings
|
||||
// (the bindings may be nil).
|
||||
Uint32 sp; // Stack pointer (index within bindings array of first unused temporary)
|
||||
// Bindings at indices 0..sp-1 are valid; others are ignored
|
||||
|
||||
public:
|
||||
explicit LocalEnv(const Common &commonEnv);
|
||||
LocalEnv(const LocalEnv &env): commonEnv(env.commonEnv) {copyEnv(env);}
|
||||
void operator=(const LocalEnv &env) {assert(!bindings); copyEnv(env);}
|
||||
void move(LocalEnv &env);
|
||||
|
||||
void init();
|
||||
|
||||
protected:
|
||||
void copyEnv(const LocalEnv &env);
|
||||
Binding *bindingsBegin() const {return bindings;} // Return the first valid binding
|
||||
Binding *bindingsEnd() const {return bindings + sp;} // Return the last valid binding + 1
|
||||
Binding *bindingsMemoryEnd() const {return bindings + nMemoryBindings;} // Return the last memory binding + 1
|
||||
public:
|
||||
|
||||
Pool &envPool() const {return commonEnv.envPool;}
|
||||
Binding &memory() const {return bindings[0];}
|
||||
Binding &local(Uint32 n) const {assert(n < commonEnv.nLocals); return bindings[nMemoryBindings + n];}
|
||||
Binding &stackNth(Uint32 n) const {assert(n > 0 && sp >= commonEnv.stackBase + n); return bindings[sp - n];}
|
||||
Binding *stackTopN(Uint32 DEBUG_ONLY(n)) const {assert(sp >= commonEnv.stackBase + n); return bindings + sp;}
|
||||
|
||||
// Stack operations
|
||||
Uint32 getSP() const {return sp - commonEnv.stackBase;}
|
||||
void dropAll() {sp = commonEnv.stackBase;}
|
||||
void drop(uint n) {assert(sp >= commonEnv.stackBase + n); sp -= n;}
|
||||
void raise(uint n) {assert(sp + n <= commonEnv.nEnvSlots); sp += n;}
|
||||
Binding &pop() {assert(sp > commonEnv.stackBase); return bindings[--sp];} // Note: Result invalidated by next push!
|
||||
Binding &push() {assert(sp < commonEnv.nEnvSlots); return bindings[sp++];}
|
||||
Binding &pop2();
|
||||
Binding &push2();
|
||||
Binding &pop1or2(bool two);
|
||||
Binding &push1or2(bool two);
|
||||
|
||||
protected:
|
||||
bool compatible(const LocalEnv &env) {return bindings && env.bindings && &commonEnv == &env.commonEnv && sp == env.sp;}
|
||||
};
|
||||
|
||||
|
||||
// --- INLINES ----------------------------------------------------------------
|
||||
|
||||
//
|
||||
// Construct a new LocalEnv for the method described by commonEnv.
|
||||
// The LocalEnv cannot be used unless init is called first or another LocalEnv
|
||||
// is copied to this one.
|
||||
//
|
||||
template<class Binding, class Common, Uint32 nMemoryBindings>
|
||||
inline LocalEnv<Binding, Common, nMemoryBindings>::LocalEnv(const Common &commonEnv):
|
||||
commonEnv(commonEnv),
|
||||
bindings(0)
|
||||
{}
|
||||
|
||||
|
||||
//
|
||||
// Destructively move the given LocalEnv (which must have been initialized)
|
||||
// to this LocalEnv, which must be uninitialized. The given LocalEnv is
|
||||
// left uninitialized.
|
||||
//
|
||||
template<class Binding, class Common, Uint32 nMemoryBindings>
|
||||
inline void LocalEnv<Binding, Common, nMemoryBindings>::move(LocalEnv &env)
|
||||
{
|
||||
assert(!bindings && env.bindings);
|
||||
bindings = env.bindings;
|
||||
sp = env.sp;
|
||||
#ifdef DEBUG
|
||||
env.bindings = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Pop and return a long or double binding from the stack. The two words on the top of
|
||||
// the stack must contain a long or double value.
|
||||
// Note that the returned reference will be invalidated by the next push.
|
||||
//
|
||||
template<class Binding, class Common, Uint32 nMemoryBindings>
|
||||
inline Binding &LocalEnv<Binding, Common, nMemoryBindings>::pop2()
|
||||
{
|
||||
assert(sp > commonEnv.stackBase+1 && bindings[sp-1].isSecondWord());
|
||||
return bindings[sp -= 2];
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Push a long or double binding outo the stack. The return value is a reference to
|
||||
// a new stack slot; the caller must initialize it to refer to the long or double's first word.
|
||||
//
|
||||
template<class Binding, class Common, Uint32 nMemoryBindings>
|
||||
inline Binding &LocalEnv<Binding, Common, nMemoryBindings>::push2()
|
||||
{
|
||||
assert(sp < commonEnv.nEnvSlots-1);
|
||||
Binding *b = &bindings[sp];
|
||||
b[1].defineSecondWord();
|
||||
sp += 2;
|
||||
return *b;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Do pop() if two is false or pop2() if two is true.
|
||||
//
|
||||
template<class Binding, class Common, Uint32 nMemoryBindings>
|
||||
inline Binding &LocalEnv<Binding, Common, nMemoryBindings>::pop1or2(bool two)
|
||||
{
|
||||
assert(sp > commonEnv.stackBase+two && (!two || bindings[sp-1].isSecondWord()));
|
||||
if (two)
|
||||
--sp;
|
||||
return bindings[--sp];
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Do push() if two is false or push2() if two is true.
|
||||
//
|
||||
template<class Binding, class Common, Uint32 nMemoryBindings>
|
||||
inline Binding &LocalEnv<Binding, Common, nMemoryBindings>::push1or2(bool two)
|
||||
{
|
||||
assert(sp < commonEnv.nEnvSlots-two);
|
||||
Binding &b = bindings[sp++];
|
||||
if (two)
|
||||
bindings[sp++].defineSecondWord();
|
||||
return b;
|
||||
}
|
||||
|
||||
|
||||
// --- TEMPLATES --------------------------------------------------------------
|
||||
|
||||
|
||||
//
|
||||
// Initialize every entry in this LocalEnv to be empty and the stack to have no
|
||||
// temporaries. This LocalEnv must not have been initialized before.
|
||||
//
|
||||
template<class Binding, class Common, Uint32 nMemoryBindings>
|
||||
void LocalEnv<Binding, Common, nMemoryBindings>::init()
|
||||
{
|
||||
assert(!bindings);
|
||||
Binding *lb = new(commonEnv.envPool) Binding[commonEnv.nEnvSlots];
|
||||
bindings = lb;
|
||||
Uint32 stackBase = commonEnv.stackBase;
|
||||
sp = stackBase;
|
||||
Binding *lbEnd = lb + stackBase;
|
||||
while (lb != lbEnd)
|
||||
lb++->clear();
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Assign a copy of the given LocalEnv (which must have been initialized)
|
||||
// to this LocalEnv, which is known to be uninitialized.
|
||||
//
|
||||
template<class Binding, class Common, Uint32 nMemoryBindings>
|
||||
void LocalEnv<Binding, Common, nMemoryBindings>::copyEnv(const LocalEnv &env)
|
||||
{
|
||||
assert(env.bindings);
|
||||
Binding *lbDst = new(commonEnv.envPool) Binding[commonEnv.nEnvSlots];
|
||||
copy(env.bindingsBegin(), env.bindingsEnd(), lbDst);
|
||||
bindings = lbDst;
|
||||
sp = env.sp;
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,67 @@
|
|||
#!gmake
|
||||
#
|
||||
# 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.
|
||||
|
||||
#######################################################################
|
||||
# (1) Directory specific info #
|
||||
#######################################################################
|
||||
|
||||
DEPTH = ../..
|
||||
|
||||
|
||||
CPPSRCS = BytecodeGraph.cpp \
|
||||
TranslationEnv.cpp \
|
||||
ErrorHandling.cpp \
|
||||
BytecodeTranslator.cpp \
|
||||
VerificationEnv.cpp \
|
||||
BytecodeVerifier.cpp \
|
||||
$(NULL)
|
||||
|
||||
LOCAL_EXPORTS = BytecodeGraph.h \
|
||||
BytecodeTranslator.h \
|
||||
BytecodeVerifier.h \
|
||||
ErrorHandling.h \
|
||||
LocalEnv.h \
|
||||
TranslationEnv.h \
|
||||
VerificationEnv.h \
|
||||
$(NULL)
|
||||
|
||||
MODULE_NAME = EF
|
||||
|
||||
#######################################################################
|
||||
# (2) Include "component" configuration information. #
|
||||
#######################################################################
|
||||
|
||||
include $(DEPTH)/config/config.mk
|
||||
|
||||
#######################################################################
|
||||
# (3) Include "local" platform-dependent assignments (OPTIONAL). #
|
||||
#######################################################################
|
||||
|
||||
|
||||
|
||||
#######################################################################
|
||||
# (4) Execute "component" rules. (OPTIONAL) #
|
||||
#######################################################################
|
||||
|
||||
include $(DEPTH)/config/rules.mk
|
||||
|
||||
#######################################################################
|
||||
# (7) Execute "local" rules. (OPTIONAL). #
|
||||
#######################################################################
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,84 @@
|
|||
/* -*- 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 READER_H
|
||||
#define READER_H
|
||||
|
||||
#include "NativeDefs.h"
|
||||
|
||||
struct VerifyError
|
||||
{
|
||||
enum Cause
|
||||
{
|
||||
unknown, // Unknown cause
|
||||
notImplemented, // Functionality not implemented yet
|
||||
noClassDefFound, // Error in reading class file: file not found, or has errors
|
||||
badClassFormat, // Badly formatted class file
|
||||
illegalAccess, // Class does not have permissions to access a particular field/method
|
||||
noSuchField, // Field not found in class
|
||||
noSuchMethod, // Method not found in class
|
||||
noBytecodes, // No bytecodes in a function
|
||||
badBytecode, // Bad bytecode opcode
|
||||
badBytecodeOffset, // Bad offset to a bytecode instruction
|
||||
badNewArrayType, // Bad type passed to newarray instruction
|
||||
badConstantPoolIndex, // Constant pool index out of range
|
||||
badReturn, // Wrong return instruction used in this function
|
||||
writeToConst, // Attempt to write to a constant (final) field
|
||||
classNotFound, // Given class not found in class file
|
||||
nonThrowableCatch, // Catch filter class not a subclass of Throwable
|
||||
classCircularity, // Class can be its own superclass
|
||||
resourceExhausted, // Compiler internal limits reached
|
||||
abstractMethod, // Attempt to invoke abstract method
|
||||
incompatibleClassChange // Binary incompatibility
|
||||
};
|
||||
|
||||
const Cause cause;
|
||||
|
||||
VerifyError(Cause cause): cause(cause) {}
|
||||
};
|
||||
|
||||
NS_EXTERN
|
||||
void verifyError(VerifyError::Cause cause);
|
||||
|
||||
struct RuntimeError
|
||||
{
|
||||
enum Cause
|
||||
{
|
||||
unknown, // Unknown cause
|
||||
internal, // Internal error
|
||||
notImplemented, // Functionality not implemented yet
|
||||
illegalArgument, // incorrect argument to a method
|
||||
illegalAccess, // prohibited operation
|
||||
securityViolation, // Security violation
|
||||
IOError, // IO Error
|
||||
fileNotFound,
|
||||
linkError, // Unable to link method
|
||||
nullPointer, // Null Pointer argument
|
||||
notInstantiable, // Attempt to instantiate an abstract class
|
||||
outOfMemory
|
||||
};
|
||||
|
||||
const Cause cause;
|
||||
|
||||
RuntimeError(Cause cause): cause(cause) {}
|
||||
};
|
||||
|
||||
NS_EXTERN
|
||||
void runtimeError(RuntimeError::Cause cause);
|
||||
|
||||
|
||||
#endif
|
|
@ -0,0 +1,516 @@
|
|||
/* -*- 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.
|
||||
*/
|
||||
#include "BytecodeGraph.h"
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// TranslationCommonEnv
|
||||
|
||||
|
||||
//
|
||||
// Create a new, empty phi node in the block's first control node.
|
||||
// nExpectedInputs is the estimated number of inputs that the phi node will
|
||||
// eventually have; however, there is no guarantee that the actual number of
|
||||
// inputs won't be higher or lower.
|
||||
// This method disengages the phi nodes in the block's first control node
|
||||
// and must be balanced by a call to finishPhiNode.
|
||||
//
|
||||
PhiNode *TranslationCommonEnv::genEmptyPhiNode(const BasicBlock &block, ValueKind kind, Uint32 nExpectedInputs) const
|
||||
{
|
||||
PhiNode *phiNode = new(primitivePool) PhiNode(nExpectedInputs, kind, primitivePool);
|
||||
ControlNode *cn = block.getFirstControlNode();
|
||||
assert(cn);
|
||||
cn->disengagePhis();
|
||||
cn->addPhiNode(*phiNode);
|
||||
return phiNode;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Balance a call to genEmptyPhiNode. The caller should have set up the
|
||||
// (formerly) empty phi node's inputs by now.
|
||||
//
|
||||
inline void TranslationCommonEnv::finishPhiNode(const BasicBlock &block) const
|
||||
{
|
||||
ControlNode *cn = block.getFirstControlNode();
|
||||
assert(cn);
|
||||
cn->reengagePhis();
|
||||
}
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// TranslationBinding
|
||||
|
||||
|
||||
//
|
||||
// Create the real constant primitive node for this Constant. Allocate the
|
||||
// primitive node out of the given pool.
|
||||
//
|
||||
void TranslationBinding::Constant::genRealNode(Pool &primitivePool, Uint32 bci)
|
||||
{
|
||||
assert(!producerExists);
|
||||
|
||||
PrimConst *primConst = new(primitivePool) PrimConst(kind, value, bci);
|
||||
placeForDataNode->appendPrimitive(*primConst);
|
||||
producerExists = true;
|
||||
dataNode = primConst;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Create a new PhantomPhi record with room for size arguments. Ouf of these,
|
||||
// set the first nInitialArgs (which may be zero) to initialArgs, and set the
|
||||
// next argument to arg. Clearly, nInitialArgs must be less than size.
|
||||
// container is the BasicBlock in which this phantom phi node is defined.
|
||||
// If alwaysNonzero is true, the value of the PhantomPhi is known to never be
|
||||
// zero.
|
||||
// Allocate needed storage from pool.
|
||||
//
|
||||
TranslationBinding::PhantomPhi::PhantomPhi(ValueKind kind, Uint32 size, Uint32 nInitialArgs, const TranslationBinding &initialArgs,
|
||||
const TranslationBinding &arg, const BasicBlock &container, bool alwaysNonzero, Pool &pool):
|
||||
container(container),
|
||||
nArgs(nInitialArgs + 1),
|
||||
realPhiExists(false),
|
||||
kind(kind),
|
||||
alwaysNonzero(alwaysNonzero)
|
||||
{
|
||||
TranslationBinding *a = new(pool) TranslationBinding[size];
|
||||
args = a;
|
||||
while (nInitialArgs--)
|
||||
*a++ = initialArgs;
|
||||
*a = arg;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Return true if the value of this PhantomPhi is known to never be zero.
|
||||
//
|
||||
bool TranslationBinding::PhantomPhi::isAlwaysNonzero() const
|
||||
{
|
||||
return realPhiExists ? realPhi->isAlwaysNonzero() : alwaysNonzero;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Inform this PhantomPhi about whether its value is never zero.
|
||||
//
|
||||
void TranslationBinding::PhantomPhi::setAlwaysNonzero(bool nz)
|
||||
{
|
||||
if (realPhiExists)
|
||||
realPhi->setAlwaysNonzero(nz);
|
||||
else
|
||||
alwaysNonzero = nz;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Append a new argument arg to this PhantomPhi's array. If expand is true,
|
||||
// the array first needs to be physically expanded to size newSize (which is
|
||||
// guaranteed to hold all of its elements).
|
||||
//
|
||||
void TranslationBinding::PhantomPhi::append(bool expand, Uint32 newSize,
|
||||
const TranslationBinding &arg,
|
||||
Uint32 bci)
|
||||
{
|
||||
if (realPhiExists) {
|
||||
Pool &primitivePool = container.getTranslationEnvIn().getCommonEnv().primitivePool;
|
||||
realPhi->addInput(arg.extract(primitivePool, bci), primitivePool);
|
||||
} else {
|
||||
Uint32 n = nArgs;
|
||||
if (expand) {
|
||||
TranslationBinding *newArgs = new(container.getTranslationEnvIn().envPool()) TranslationBinding[newSize];
|
||||
copy(args, args + n, newArgs);
|
||||
args = newArgs;
|
||||
assert(newSize > n);
|
||||
}
|
||||
args[n] = arg;
|
||||
nArgs = n + 1;
|
||||
if (alwaysNonzero)
|
||||
alwaysNonzero = arg.isAlwaysNonzero();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Create the real phi node for this PhantomPhi.
|
||||
//
|
||||
void TranslationBinding::PhantomPhi::genRealPhi(Uint32 bci)
|
||||
{
|
||||
assert(!realPhiExists);
|
||||
TranslationBinding *a = args;
|
||||
const TranslationEnv &env = container.getTranslationEnvIn();
|
||||
const TranslationCommonEnv &commonEnv = env.getCommonEnv();
|
||||
Pool &primitivePool = commonEnv.primitivePool;
|
||||
Uint32 n = getNArgs();
|
||||
|
||||
// Carefully create a new phi node before calling extract on the arguments
|
||||
// because one of the arguments could refer back to this phi node.
|
||||
realPhi = commonEnv.genEmptyPhiNode(container, kind, env.getPhiSize());
|
||||
realPhiExists = true;
|
||||
while (n--)
|
||||
realPhi->addInput(a++->extract(primitivePool, bci), primitivePool);
|
||||
commonEnv.finishPhiNode(container);
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Return the kind of value represented by this binding.
|
||||
//
|
||||
ValueKind TranslationBinding::getKind() const
|
||||
{
|
||||
switch (category) {
|
||||
case tbConstant:
|
||||
return constant->kind;
|
||||
case tbDataEdge:
|
||||
return dataNode->getKind();
|
||||
case tbPhantomPhi:
|
||||
case tbConstantPhi:
|
||||
return phantomPhi->kind;
|
||||
default:
|
||||
return vkVoid;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Return true if the value of this binding is known to never be zero.
|
||||
//
|
||||
bool TranslationBinding::isAlwaysNonzero() const
|
||||
{
|
||||
switch (category) {
|
||||
case tbConstant:
|
||||
return constant->isNonzero();
|
||||
case tbDataEdge:
|
||||
return dataNode->isAlwaysNonzero();
|
||||
case tbPhantomPhi:
|
||||
case tbConstantPhi:
|
||||
return phantomPhi->isAlwaysNonzero();
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Bind this TranslationBinding to represent the given int constant.
|
||||
// If a primitive to explicitly generate the constant is needed, it will
|
||||
// be placed in the given ControlNode.
|
||||
// Allocate needed storage from pool.
|
||||
//
|
||||
void TranslationBinding::defineInt(Int32 v, ControlNode *placeForDataNode, Pool &pool)
|
||||
{
|
||||
category = tbConstant;
|
||||
Constant *c = new(pool) Constant(vkInt, placeForDataNode);
|
||||
c->value.i = v;
|
||||
constant = c;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Bind this TranslationBinding to represent the first word of the given long constant.
|
||||
// If a primitive to explicitly generate the constant is needed, it will
|
||||
// be placed in the given ControlNode.
|
||||
// Allocate needed storage from pool.
|
||||
//
|
||||
void TranslationBinding::defineLong(Int64 v, ControlNode *placeForDataNode, Pool &pool)
|
||||
{
|
||||
category = tbConstant;
|
||||
Constant *c = new(pool) Constant(vkLong, placeForDataNode);
|
||||
c->value.l = v;
|
||||
constant = c;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Bind this TranslationBinding to represent the given float constant.
|
||||
// If a primitive to explicitly generate the constant is needed, it will
|
||||
// be placed in the given ControlNode.
|
||||
// Allocate needed storage from pool.
|
||||
//
|
||||
void TranslationBinding::defineFloat(Flt32 v, ControlNode *placeForDataNode, Pool &pool)
|
||||
{
|
||||
category = tbConstant;
|
||||
Constant *c = new(pool) Constant(vkFloat, placeForDataNode);
|
||||
c->value.f = v;
|
||||
constant = c;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Bind this TranslationBinding to represent the first word of the given double constant.
|
||||
// If a primitive to explicitly generate the constant is needed, it will
|
||||
// be placed in the given ControlNode.
|
||||
// Allocate needed storage from pool.
|
||||
//
|
||||
void TranslationBinding::defineDouble(Flt64 v, ControlNode *placeForDataNode, Pool &pool)
|
||||
{
|
||||
category = tbConstant;
|
||||
Constant *c = new(pool) Constant(vkDouble, placeForDataNode);
|
||||
c->value.d = v;
|
||||
constant = c;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Bind this TranslationBinding to represent the given pointer constant.
|
||||
// If a primitive to explicitly generate the constant is needed, it will
|
||||
// be placed in the given ControlNode.
|
||||
// Allocate needed storage from pool.
|
||||
//
|
||||
void TranslationBinding::definePtr(addr v, ControlNode *placeForDataNode, Pool &pool)
|
||||
{
|
||||
category = tbConstant;
|
||||
Constant *c = new(pool) Constant(vkAddr, placeForDataNode);
|
||||
c->value.a = v;
|
||||
constant = c;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Bind this TranslationBinding to represent the constant (or first word of the constant
|
||||
// if it takes two words).
|
||||
// If a primitive to explicitly generate the constant is needed, it will
|
||||
// be placed in the given ControlNode.
|
||||
// Allocate needed environment storage from pool.
|
||||
//
|
||||
void TranslationBinding::define(ValueKind kind, const Value &v, ControlNode *placeForDataNode, Pool &pool)
|
||||
{
|
||||
category = tbConstant;
|
||||
Constant *c = new(pool) Constant(kind, placeForDataNode);
|
||||
c->value = v;
|
||||
constant = c;
|
||||
}
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// TranslationEnv
|
||||
|
||||
|
||||
#ifdef DEBUG
|
||||
//
|
||||
// Assert that none of the PhantomPhi or ConstantPhi nodes referring to the current container
|
||||
// are shared.
|
||||
//
|
||||
void TranslationEnv::assertNoSharedPhantomPhis(const BasicBlock &container)
|
||||
{
|
||||
TranslationBinding *dst = bindingsBegin();
|
||||
TranslationBinding *dstEnd = bindingsEnd();
|
||||
for (; dst != dstEnd; dst++)
|
||||
if (dst->category == TranslationBinding::tbPhantomPhi || dst->category == TranslationBinding::tbConstantPhi)
|
||||
dst->phantomPhi->duplicate = false;
|
||||
for (dst = bindingsBegin(); dst != dstEnd; dst++)
|
||||
if (dst->category == TranslationBinding::tbPhantomPhi || dst->category == TranslationBinding::tbConstantPhi) {
|
||||
TranslationBinding::PhantomPhi *phi = dst->phantomPhi;
|
||||
assert(&phi->getContainer() != &container || !phi->duplicate);
|
||||
phi->duplicate = true;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
//
|
||||
// Intersect the given TranslationEnv (which must have been initialized)
|
||||
// into this TranslationEnv, which must also be initialized.
|
||||
// This environment must be part of the given container.
|
||||
// If memoryOnly is true, only intersect the memory binding.
|
||||
//
|
||||
void TranslationEnv::meet(const TranslationEnv &env, bool memoryOnly, const BasicBlock &container)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
assert(this == &container.getTranslationEnvIn());
|
||||
assert(compatible(env));
|
||||
assertNoSharedPhantomPhis(container);
|
||||
#endif
|
||||
TranslationBinding *dst = bindingsBegin();
|
||||
TranslationBinding *dstEnd = memoryOnly ? bindingsMemoryEnd() : bindingsEnd();
|
||||
const TranslationBinding *src = env.bindingsBegin();
|
||||
Pool &pool = commonEnv.envPool;
|
||||
Uint32 oldPhiOffset = phiOffset;
|
||||
Uint32 newPhiSize = phiSize;
|
||||
bool expandThis = false;
|
||||
if (oldPhiOffset >= newPhiSize) {
|
||||
newPhiSize = oldPhiOffset*2 + 1;
|
||||
expandThis = true;
|
||||
}
|
||||
|
||||
while (dst != dstEnd) {
|
||||
TranslationBinding::Category cDst = dst->category;
|
||||
TranslationBinding::Category cSrc = src->category;
|
||||
ValueKind kind;
|
||||
|
||||
if (cSrc == TranslationBinding::tbNone) {
|
||||
assert(!anticipated || cDst == TranslationBinding::tbNone);
|
||||
dst->clear();
|
||||
} else
|
||||
switch (cDst) {
|
||||
|
||||
case TranslationBinding::tbNone:
|
||||
break;
|
||||
|
||||
case TranslationBinding::tbSecondWord:
|
||||
if (cSrc != TranslationBinding::tbSecondWord) {
|
||||
assert(!anticipated);
|
||||
dst->clear();
|
||||
}
|
||||
break;
|
||||
|
||||
case TranslationBinding::tbConstantPhi:
|
||||
{
|
||||
TranslationBinding::ConstantPhi *phi = dst->constantPhi;
|
||||
kind = phi->kind;
|
||||
if (src->getKind() != kind) {
|
||||
assert(!anticipated);
|
||||
dst->clear();
|
||||
break;
|
||||
}
|
||||
const TranslationBinding::Constant *srcConstant = src->constantValue();
|
||||
bool constantMatches = srcConstant && *phi->constant == *srcConstant;
|
||||
|
||||
if (&phi->getContainer() == &container)
|
||||
if (constantMatches)
|
||||
// asharma - fix this!
|
||||
phi->append(expandThis, newPhiSize, *src, 0);
|
||||
else {
|
||||
// The constant doesn't match, so turn this into a regular phantom phi node.
|
||||
assert(!anticipated);
|
||||
dst->category = TranslationBinding::tbPhantomPhi;
|
||||
goto phantomPhi;
|
||||
}
|
||||
else
|
||||
// Don't merge if both source and destination are the same and they don't refer to
|
||||
// this container.
|
||||
if (cSrc != TranslationBinding::tbConstantPhi || src->constantPhi != phi)
|
||||
if (constantMatches) {
|
||||
assert(!anticipated);
|
||||
dst->define(new(pool) TranslationBinding::ConstantPhi(kind, newPhiSize, oldPhiOffset,
|
||||
*dst, *src, container, srcConstant, pool));
|
||||
} else
|
||||
goto createPhi;
|
||||
}
|
||||
break;
|
||||
|
||||
case TranslationBinding::tbPhantomPhi:
|
||||
if (src->getKind() != dst->phantomPhi->kind) {
|
||||
assert(!anticipated);
|
||||
dst->clear();
|
||||
break;
|
||||
}
|
||||
phantomPhi:
|
||||
{
|
||||
TranslationBinding::PhantomPhi *phi = dst->phantomPhi;
|
||||
|
||||
if (&phi->getContainer() == &container) {
|
||||
// Use existing phantom phi node in this node
|
||||
assert(!phi->isAlwaysNonzero() || src->isAlwaysNonzero() || !anticipated);
|
||||
// asharma - fix this!
|
||||
phi->append(expandThis, newPhiSize, *src, 0);
|
||||
break;
|
||||
}
|
||||
|
||||
// Don't merge if both source and destination are the same and they don't refer to
|
||||
// this container.
|
||||
if (cSrc != TranslationBinding::tbPhantomPhi || src->phantomPhi != phi) {
|
||||
kind = phi->kind;
|
||||
goto createPhi;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case TranslationBinding::tbDataEdge:
|
||||
if (cSrc != TranslationBinding::tbDataEdge || src->dataNode != dst->dataNode) {
|
||||
kind = dst->dataNode->getKind();
|
||||
if (src->getKind() != kind) {
|
||||
assert(!anticipated);
|
||||
dst->clear();
|
||||
break;
|
||||
}
|
||||
createPhi:
|
||||
assert(!anticipated);
|
||||
dst->define(new(pool) TranslationBinding::PhantomPhi(kind, newPhiSize, oldPhiOffset, *dst, *src, container,
|
||||
dst->isAlwaysNonzero() && src->isAlwaysNonzero(), pool));
|
||||
}
|
||||
break;
|
||||
|
||||
case TranslationBinding::tbConstant:
|
||||
if (cSrc != TranslationBinding::tbConstant || src->constant != dst->constant) {
|
||||
kind = dst->constant->kind;
|
||||
if (src->getKind() != kind) {
|
||||
assert(!anticipated);
|
||||
dst->clear();
|
||||
break;
|
||||
}
|
||||
const TranslationBinding::Constant *srcConstant = src->constantValue();
|
||||
if (srcConstant && *dst->constant == *srcConstant) {
|
||||
dst->define(new(pool) TranslationBinding::ConstantPhi(kind, newPhiSize, oldPhiOffset,
|
||||
*dst, *src, container, srcConstant, pool));
|
||||
break;
|
||||
}
|
||||
goto createPhi;
|
||||
}
|
||||
}
|
||||
src++;
|
||||
dst++;
|
||||
}
|
||||
phiOffset = oldPhiOffset + 1;
|
||||
phiSize = newPhiSize;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Add phantom phi nodes to all variables in this local environment because we'd like
|
||||
// to use this environment without knowing all of its predecessors yet.
|
||||
// This environment must be part of the given container.
|
||||
//
|
||||
void TranslationEnv::anticipate(const BasicBlock &container)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
assert(this == &container.getTranslationEnvIn());
|
||||
assertNoSharedPhantomPhis(container);
|
||||
assert(!anticipated);
|
||||
anticipated = true;
|
||||
#endif
|
||||
TranslationBinding *dst = bindingsBegin();
|
||||
TranslationBinding *dstEnd = bindingsEnd();
|
||||
Pool &pool = commonEnv.envPool;
|
||||
|
||||
while (dst != dstEnd) {
|
||||
switch (dst->category) {
|
||||
case TranslationBinding::tbNone:
|
||||
case TranslationBinding::tbSecondWord:
|
||||
break;
|
||||
case TranslationBinding::tbConstantPhi:
|
||||
if (&dst->constantPhi->getContainer() != &container)
|
||||
goto createPhi;
|
||||
dst->constantPhi->setAlwaysNonzero(false);
|
||||
dst->category = TranslationBinding::tbPhantomPhi;
|
||||
break;
|
||||
case TranslationBinding::tbPhantomPhi:
|
||||
if (&dst->phantomPhi->getContainer() == &container)
|
||||
break; // We already have an appropriate phi node.
|
||||
// Fall into default case because we need to create a new phi node; the current
|
||||
// value was a phi node from another control node.
|
||||
default:
|
||||
createPhi:
|
||||
assert(phiOffset > 0 && phiSize >= phiOffset);
|
||||
dst->define(new(pool) TranslationBinding::PhantomPhi(dst->getKind(), phiSize, phiOffset-1,
|
||||
*dst, *dst, container, false, pool));
|
||||
break;
|
||||
}
|
||||
dst++;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,465 @@
|
|||
/* -*- 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 TRANSLATIONENV_H
|
||||
#define TRANSLATIONENV_H
|
||||
|
||||
#include "ControlNodes.h"
|
||||
#include "LocalEnv.h"
|
||||
|
||||
struct BasicBlock;
|
||||
|
||||
|
||||
struct TranslationCommonEnv: CommonEnv
|
||||
{
|
||||
Pool &primitivePool; // Pool for allocating phi and constant data flow nodes
|
||||
|
||||
PhiNode *genEmptyPhiNode(const BasicBlock &block, ValueKind kind, Uint32 nExpectedInputs) const;
|
||||
void finishPhiNode(const BasicBlock &block) const;
|
||||
|
||||
TranslationCommonEnv(Pool &envPool, Pool &primitivePool, Uint32 nLocals, Uint32 stackSize):
|
||||
CommonEnv(envPool, 1, nLocals, stackSize), primitivePool(primitivePool) {}
|
||||
};
|
||||
|
||||
|
||||
class TranslationBinding
|
||||
{
|
||||
public:
|
||||
enum Category
|
||||
{
|
||||
tbNone, // Unknown contents
|
||||
tbSecondWord, // This variable is the second word of a long or double
|
||||
tbConstant, // This variable is a constant constVal defined in basic block definer
|
||||
tbDataEdge, // This variable was defined by the given DataNode
|
||||
tbPhantomPhi, // This variable was defined by the given phantom phi node
|
||||
tbConstantPhi // This variable was defined by the given phantom phi node that always evaluates to a constant
|
||||
};
|
||||
|
||||
static bool isPhi(Category c) {return c == tbPhantomPhi || c == tbConstantPhi;}
|
||||
|
||||
|
||||
private:
|
||||
struct Constant
|
||||
{
|
||||
const ValueKind kind ENUM_8; // Kind of constant
|
||||
bool producerExists BOOL_8; // If true, a data flow node exists for this constant and can be found
|
||||
//short unused SHORT_16; // using dataNode below. If false, the data flow node can be created
|
||||
union { // in the placeForDataNode control node if needed.
|
||||
ControlNode *placeForDataNode; // Control node inside which the constant was defined
|
||||
DataNode *dataNode; // Link to data flow node that defined this constant
|
||||
};
|
||||
Value value; // Constant's value
|
||||
|
||||
Constant(ValueKind kind, ControlNode *placeForDataNode);
|
||||
|
||||
bool operator==(const Constant &c) const {ValueKind k = kind; return k == c.kind && value.eq(k, c.value);}
|
||||
bool operator!=(const Constant &c) const {ValueKind k = kind; return k != c.kind || !value.eq(k, c.value);}
|
||||
|
||||
private:
|
||||
void genRealNode(Pool &primitivePool, Uint32 bci);
|
||||
public:
|
||||
bool isNonzero() const {return value.isNonzero(kind);}
|
||||
DataNode &realize(Pool &primitivePool, Uint32 bci);
|
||||
};
|
||||
|
||||
|
||||
class PhantomPhi
|
||||
{
|
||||
const BasicBlock &container; // Block in which this phantom phi node is defined
|
||||
Uint32 nArgs; // Number of occupied arguments in the args array below (valid only when realPhiExists is false)
|
||||
bool realPhiExists BOOL_8; // If true, a real phi node exists and can be found using realPhi below.
|
||||
public:
|
||||
const ValueKind kind ENUM_8; // Kind of value stored in this phi node
|
||||
DEBUG_ONLY(bool duplicate BOOL_8;) // Flag used for assertNoSharedPhantomPhis
|
||||
private:
|
||||
bool alwaysNonzero BOOL_8; // If true, the value of this PhantomPhi is known to always be nonzero
|
||||
union { // (valid only if realPhiExists is false)
|
||||
TranslationBinding *args; // Array of phiSize TranslationBinding records for the arguments of a phantom phi node
|
||||
PhiNode *realPhi; // Real phi node if one has been generated
|
||||
};
|
||||
|
||||
PhantomPhi(const PhantomPhi &); // Copying forbidden
|
||||
void operator=(const PhantomPhi &); // Copying forbidden
|
||||
public:
|
||||
PhantomPhi(ValueKind kind, Uint32 size, Uint32 nInitialArgs, const TranslationBinding &initialArgs, const TranslationBinding &arg,
|
||||
const BasicBlock &container, bool alwaysNonzero, Pool &pool);
|
||||
|
||||
const BasicBlock &getContainer() const {return container;}
|
||||
Uint32 getNArgs() const {assert(!realPhiExists); return nArgs;}
|
||||
bool isAlwaysNonzero() const;
|
||||
void setAlwaysNonzero(bool nz);
|
||||
void append(bool expand, Uint32 newSize,
|
||||
const TranslationBinding &arg, Uint32 bci);
|
||||
private:
|
||||
void genRealPhi(Uint32 bci);
|
||||
public:
|
||||
DataNode &realize(Uint32 bci);
|
||||
};
|
||||
|
||||
|
||||
// A ConstantPhi is a PhantomPhi with the added restriction that all of its arguments are
|
||||
// either the same constant or other ConstantPhis of the same constant.
|
||||
struct ConstantPhi: PhantomPhi
|
||||
{
|
||||
const Constant *constant; // Constant to which all of the arguments of this phantom phi node evaluate
|
||||
|
||||
ConstantPhi(ValueKind kind, Uint32 size, Uint32 nInitialArgs, const TranslationBinding &initialArgs, const TranslationBinding &arg,
|
||||
const BasicBlock &container, const Constant *constant, Pool &pool);
|
||||
};
|
||||
|
||||
|
||||
Category category ENUM_8; // TranslationBinding category (determines interpretation of union below)
|
||||
//bool unused BOOL_8;
|
||||
//short unused SHORT_16;
|
||||
union {
|
||||
Constant *constant; // Constant value (Constant may be shared among different bindings)
|
||||
DataNode *dataNode; // Place where this variable is defined
|
||||
PhantomPhi *phantomPhi; // Phantom phi node (PhantomPhi may be shared among different bindings)
|
||||
ConstantPhi *constantPhi; // Phantom phi node all of whose arguments evaluate to the same constant (may be shared)
|
||||
void *data; // Used for comparing constNum, constAddr, etc.
|
||||
// Note: The phantomPhi and constantPhi pointers from several different bindings in an environment
|
||||
// may point to a common PhantomPhi or ConstantPhi record under the following circumstances:
|
||||
// 1. phantomPhi->container (or constantPhi->container) is not the current environment's container
|
||||
// 2. All calls to meet and anticipate have completed (at this point sharing can be
|
||||
// introduced, for instance, by assigning one local to another).
|
||||
// Sharing of PhantomPhi or ConstantPhi records in the current environment while meet or
|
||||
// anticipate is running would cause trouble because these routines extend the phantom phi arrays.
|
||||
};
|
||||
|
||||
public:
|
||||
bool operator==(const TranslationBinding &b) const
|
||||
{return category == b.category && data == b.data;}
|
||||
bool operator!=(const TranslationBinding &b) const
|
||||
{return category != b.category || data != b.data;}
|
||||
|
||||
bool isDataEdge() const { return category == tbDataEdge; }
|
||||
bool isSecondWord() const {return category == tbSecondWord;}
|
||||
const Constant *constantValue() const;
|
||||
ValueKind getKind() const;
|
||||
bool isAlwaysNonzero() const;
|
||||
|
||||
void extract(VariableOrConstant &result, Uint32 bci) const;
|
||||
DataNode &extract(Pool &primitivePool, Uint32 bci) const;
|
||||
DataNode &extract(Uint32 bci) const;
|
||||
|
||||
void clear();
|
||||
inline void defineSecondWord();
|
||||
void defineInt(Int32 v, ControlNode *placeForDataNode, Pool &pool);
|
||||
void defineLong(Int64 v, ControlNode *placeForDataNode, Pool &pool);
|
||||
void defineFloat(Flt32 v, ControlNode *placeForDataNode, Pool &pool);
|
||||
void defineDouble(Flt64 v, ControlNode *placeForDataNode, Pool &pool);
|
||||
void definePtr(addr v, ControlNode *placeForDataNode, Pool &pool);
|
||||
void define(ValueKind kind, const Value &v, ControlNode *placeForDataNode, Pool &pool);
|
||||
void define(DataNode &dataNode);
|
||||
void define(const VariableOrConstant &poc, ControlNode *placeForDataNode, Pool &pool);
|
||||
private:
|
||||
void define(PhantomPhi *p) {category = tbPhantomPhi; phantomPhi = p;}
|
||||
void define(ConstantPhi *p) {category = tbConstantPhi; constantPhi = p;}
|
||||
|
||||
friend class TranslationEnv;
|
||||
};
|
||||
|
||||
|
||||
class TranslationEnv: public LocalEnv<TranslationBinding, TranslationCommonEnv, 1>
|
||||
{
|
||||
Uint32 phiSize; // Number of LocalBindings allocated in this environment's phantom phi bindings
|
||||
Uint32 phiOffset; // Next index to be allocated in this environment's phantom phi bindings
|
||||
#ifdef DEBUG
|
||||
bool anticipated; // True if anticipate has been called on this environment
|
||||
#endif
|
||||
|
||||
public:
|
||||
TranslationEnv(TranslationCommonEnv &commonEnv);
|
||||
TranslationEnv(const TranslationEnv &env);
|
||||
void operator=(const TranslationEnv &env);
|
||||
void move(TranslationEnv &env);
|
||||
|
||||
Uint32 getPhiSize() const {return phiSize;}
|
||||
const TranslationCommonEnv &getCommonEnv() const {return commonEnv;}
|
||||
|
||||
#ifdef DEBUG
|
||||
void assertNoSharedPhantomPhis(const BasicBlock &container);
|
||||
#endif
|
||||
void meet(const TranslationEnv &env, bool memoryOnly, const BasicBlock &container);
|
||||
void anticipate(const BasicBlock &container);
|
||||
};
|
||||
|
||||
|
||||
// --- INLINES ----------------------------------------------------------------
|
||||
|
||||
|
||||
//
|
||||
// Initialize a Constant of the given kind. The constant starts out with no
|
||||
// real data flow node, but if one is needed, it will be created in placeForDataNode.
|
||||
//
|
||||
inline TranslationBinding::Constant::Constant(ValueKind kind, ControlNode *placeForDataNode):
|
||||
kind(kind),
|
||||
producerExists(false),
|
||||
placeForDataNode(placeForDataNode)
|
||||
{
|
||||
assert(placeForDataNode);
|
||||
// Prevent this ControlNode from being recycled because a real data flow node could be
|
||||
// created in it at any time.
|
||||
placeForDataNode->inhibitRecycling();
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Create if necessary and return a real data flow node for this constant.
|
||||
// Allocate the primitive node out of the given pool.
|
||||
//
|
||||
inline DataNode &TranslationBinding::Constant::realize(Pool &primitivePool,
|
||||
Uint32 bci)
|
||||
{
|
||||
if (!producerExists)
|
||||
genRealNode(primitivePool, bci);
|
||||
return *dataNode;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Create if necessary and return the real phi node for this PhantomPhi.
|
||||
//
|
||||
inline DataNode &TranslationBinding::PhantomPhi::realize(Uint32 bci)
|
||||
{
|
||||
if (!realPhiExists)
|
||||
genRealPhi(bci);
|
||||
return *realPhi;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Create a new ConstantPhi record with room for size arguments. Ouf of these,
|
||||
// set the first nInitialArgs (which may be zero) to initialArgs, and set the
|
||||
// next argument to arg. Clearly, nInitialArgs must be less than size.
|
||||
// container is the BasicBlock in which this constant phantom phi node is defined.
|
||||
// Every argument of this ConstantPhi must evaluate to constant.
|
||||
// Allocate needed storage from pool.
|
||||
//
|
||||
inline TranslationBinding::ConstantPhi::ConstantPhi(ValueKind kind, Uint32 size, Uint32 nInitialArgs, const TranslationBinding &initialArgs,
|
||||
const TranslationBinding &arg, const BasicBlock &container, const Constant *constant, Pool &pool):
|
||||
PhantomPhi(kind, size, nInitialArgs, initialArgs, arg, container, constant->isNonzero(), pool),
|
||||
constant(constant)
|
||||
{}
|
||||
|
||||
|
||||
//
|
||||
// If this TranslationBinding would always evaluate to the same constant, return that
|
||||
// constant; otherwise, return nil.
|
||||
//
|
||||
inline const TranslationBinding::Constant *TranslationBinding::constantValue() const
|
||||
{
|
||||
switch (category) {
|
||||
case tbConstant:
|
||||
return constant;
|
||||
case tbConstantPhi:
|
||||
return constantPhi->constant;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// If this TranslationBinding would always evaluate to the same constant, return that
|
||||
// constant in result. Otherwise, return the producer in result, creating phi
|
||||
// nodes for the producer if necessary.
|
||||
//
|
||||
inline void TranslationBinding::extract(VariableOrConstant &result,
|
||||
Uint32 bci) const
|
||||
{
|
||||
const Constant *c;
|
||||
|
||||
switch (category) {
|
||||
case tbConstant:
|
||||
c = constant;
|
||||
result.setConstant(c->kind, c->value);
|
||||
break;
|
||||
case tbDataEdge:
|
||||
result.setVariable(*dataNode);
|
||||
break;
|
||||
case tbPhantomPhi:
|
||||
result.setVariable(phantomPhi->realize(bci));
|
||||
break;
|
||||
case tbConstantPhi:
|
||||
c = constantPhi->constant;
|
||||
result.setConstant(c->kind, c->value);
|
||||
break;
|
||||
default:
|
||||
trespass("Can't extract this binding");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Return the producer for this TranslationBinding, creating phi nodes or
|
||||
// constant nodes if necessary. Create a producer even if it's just a
|
||||
// constant.
|
||||
//
|
||||
inline DataNode &
|
||||
TranslationBinding::extract(Pool &primitivePool, Uint32 bci) const
|
||||
{
|
||||
switch (category) {
|
||||
case tbConstant:
|
||||
return constant->realize(primitivePool, bci);
|
||||
case tbPhantomPhi:
|
||||
case tbConstantPhi:
|
||||
return phantomPhi->realize(bci);
|
||||
case tbDataEdge:
|
||||
return *dataNode;
|
||||
default:
|
||||
trespass("Can't extract this binding");
|
||||
return *(DataNode *)0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Return the producer for this TranslationBinding, creating phi nodes if
|
||||
// necessary. It is an error if the producer is a constant.
|
||||
//
|
||||
inline DataNode &TranslationBinding::extract(Uint32 bci) const
|
||||
{
|
||||
switch (category) {
|
||||
case tbPhantomPhi:
|
||||
case tbConstantPhi:
|
||||
return phantomPhi->realize(bci);
|
||||
case tbDataEdge:
|
||||
return *dataNode;
|
||||
default:
|
||||
trespass("Can't extract this binding");
|
||||
return *(DataNode *)0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Clear any previous binding from this TranslationBinding.
|
||||
//
|
||||
inline void TranslationBinding::clear()
|
||||
{
|
||||
category = tbNone;
|
||||
data = 0;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Bind this TranslationBinding to represent the second word of a long or double.
|
||||
// This TranslationBinding won't carry any more descriptive information about that
|
||||
// long or double -- all such information is carried by the first word's binding.
|
||||
//
|
||||
inline void TranslationBinding::defineSecondWord()
|
||||
{
|
||||
category = tbSecondWord;
|
||||
data = 0;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Bind this TranslationBinding to be the result (first word if it's a long or double)
|
||||
// of the given DataNode.
|
||||
//
|
||||
inline void TranslationBinding::define(DataNode &dataNode)
|
||||
{
|
||||
category = tbDataEdge;
|
||||
TranslationBinding::dataNode = &dataNode;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Bind this TranslationBinding to be the result (first word if it's a long or double)
|
||||
// of the given DataNode or Constant.
|
||||
// Allocate needed environment storage from pool.
|
||||
//
|
||||
inline void TranslationBinding::define(const VariableOrConstant &poc, ControlNode *placeForDataNode, Pool &pool)
|
||||
{
|
||||
if (poc.isConstant())
|
||||
define(poc.getKind(), poc.getConstant(), placeForDataNode, pool);
|
||||
else
|
||||
define(poc.getVariable());
|
||||
}
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
|
||||
const initialPhiSize = 3;
|
||||
|
||||
//
|
||||
// Create a new translation binding environment.
|
||||
// The environment starts with one predecessor for the purpose of tracking
|
||||
// the origins of phi nodes' entries.
|
||||
//
|
||||
inline TranslationEnv::TranslationEnv(TranslationCommonEnv &commonEnv):
|
||||
LocalEnv<TranslationBinding, TranslationCommonEnv, 1>(commonEnv),
|
||||
phiSize(initialPhiSize),
|
||||
phiOffset(1)
|
||||
#ifdef DEBUG
|
||||
, anticipated(false)
|
||||
#endif
|
||||
{}
|
||||
|
||||
|
||||
//
|
||||
// Copy a translation binding environment.
|
||||
// The copy starts with one predecessor for the purpose of tracking
|
||||
// the origins of phi nodes' entries.
|
||||
//
|
||||
inline TranslationEnv::TranslationEnv(const TranslationEnv &env):
|
||||
LocalEnv<TranslationBinding, TranslationCommonEnv, 1>(env),
|
||||
phiSize(initialPhiSize),
|
||||
phiOffset(1)
|
||||
#ifdef DEBUG
|
||||
, anticipated(false)
|
||||
#endif
|
||||
{}
|
||||
|
||||
|
||||
//
|
||||
// Assign the given translation binding environment to this one.
|
||||
// The copy starts with one predecessor for the purpose of tracking
|
||||
// the origins of phi nodes' entries.
|
||||
//
|
||||
inline void TranslationEnv::operator=(const TranslationEnv &env)
|
||||
{
|
||||
phiSize = initialPhiSize;
|
||||
phiOffset = 1;
|
||||
#ifdef DEBUG
|
||||
anticipated = false;
|
||||
#endif
|
||||
LocalEnv<TranslationBinding, TranslationCommonEnv, 1>::operator=(env);
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Destructively move the given LocalEnv (which must have been initialized)
|
||||
// to this LocalEnv, which must be uninitialized. The given LocalEnv is
|
||||
// left uninitialized.
|
||||
// The moved environment is reset to have only one predecessor for the purpose
|
||||
// of tracking the origins of phi nodes' entries.
|
||||
//
|
||||
inline void TranslationEnv::move(TranslationEnv &env)
|
||||
{
|
||||
phiSize = initialPhiSize;
|
||||
phiOffset = 1;
|
||||
#ifdef DEBUG
|
||||
anticipated = false;
|
||||
#endif
|
||||
LocalEnv<TranslationBinding, TranslationCommonEnv, 1>::move(env);
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,657 @@
|
|||
/* -*- 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.
|
||||
*/
|
||||
#include "VerificationEnv.h"
|
||||
|
||||
|
||||
const VerificationEnv::BindingKind VerificationEnv::valueKindBindingKinds[nValueKinds] =
|
||||
{
|
||||
bkVoid, // vkVoid
|
||||
bkInt, // vkInt
|
||||
bkLong, // vkLong
|
||||
bkFloat, // vkFloat
|
||||
bkDouble, // vkDouble
|
||||
bkAddr, // vkAddr
|
||||
bkVoid, // vkCond
|
||||
bkVoid, // vkMemory
|
||||
bkVoid // vkTuple
|
||||
};
|
||||
|
||||
|
||||
const VerificationEnv::BindingKind VerificationEnv::typeKindBindingKinds[nTypeKinds] =
|
||||
{
|
||||
bkVoid, // tkVoid
|
||||
bkInt, // tkBoolean
|
||||
bkInt, // tkUByte
|
||||
bkInt, // tkByte
|
||||
bkInt, // tkChar
|
||||
bkInt, // tkShort
|
||||
bkInt, // tkInt
|
||||
bkLong, // tkLong
|
||||
bkFloat, // tkFloat
|
||||
bkDouble, // tkDouble
|
||||
bkAddr, // tkObject
|
||||
bkAddr, // tkSpecial
|
||||
bkAddr, // tkArray
|
||||
bkAddr // tkInterface
|
||||
};
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// VerificationEnv::Context
|
||||
|
||||
|
||||
//
|
||||
// Create a new, empty Context that holds information about which variables a subroutine
|
||||
// has modified. Clear the modified array (indicating that no variables have been accessed
|
||||
// yet) and the link to the next context.
|
||||
//
|
||||
VerificationEnv::Context::Context(Subroutine subroutine, const Common &common):
|
||||
next(0),
|
||||
subroutine(subroutine),
|
||||
retReachable(false)
|
||||
#ifdef DEBUG
|
||||
, common(common)
|
||||
#endif
|
||||
{
|
||||
assert(common.bindingPool);
|
||||
modified = new(*common.bindingPool) char[common.nEnvSlots];
|
||||
fill(modified, modified + common.nEnvSlots, 0);
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Create a copy of the given context. Clear the link to the next context.
|
||||
//
|
||||
VerificationEnv::Context::Context(const Context &src, const Common &common):
|
||||
next(0),
|
||||
subroutine(src.subroutine),
|
||||
retReachable(src.retReachable)
|
||||
#ifdef DEBUG
|
||||
, common(common)
|
||||
#endif
|
||||
{
|
||||
assert(common.bindingPool);
|
||||
modified = new(*common.bindingPool) char[common.nEnvSlots];
|
||||
copy(src.modified, src.modified + common.nEnvSlots, modified);
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Create a partial copy of the given context. Do not copy the contents of the
|
||||
// modified array. Translate the Subroutine value using the given translator.
|
||||
// Clear the link to the next context.
|
||||
//
|
||||
inline VerificationEnv::Context::Context(const Context &src, const Common &common, Function1<Subroutine, Subroutine> &translator):
|
||||
next(0),
|
||||
subroutine(translator(src.subroutine)),
|
||||
retReachable(src.retReachable)
|
||||
#ifdef DEBUG
|
||||
, common(common)
|
||||
#endif
|
||||
{}
|
||||
|
||||
|
||||
//
|
||||
// Merge this context with the src context. A slot in the merged context is
|
||||
// set to the modified state if it was modified in either this or the src context.
|
||||
// Return true if the resulting context differs from the original contents of this
|
||||
// context.
|
||||
//
|
||||
bool VerificationEnv::Context::meet(const Context &src, Uint32 nSlots)
|
||||
{
|
||||
assert(subroutine == src.subroutine && common.bindingPool);
|
||||
bool changed = false;
|
||||
char *dstModified = modified;
|
||||
const char *srcModified = src.modified;
|
||||
for (Uint32 slot = 0; slot != nSlots; slot++)
|
||||
if (srcModified[slot] && !dstModified[slot]) {
|
||||
dstModified[slot] = true;
|
||||
changed = true;
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// If a context corresponding to the given subroutine is in the linked list of
|
||||
// contexts, return that context. If not, return nil. list can be nil.
|
||||
//
|
||||
VerificationEnv::Context *VerificationEnv::Context::find(Context *list, const Subroutine subroutine)
|
||||
{
|
||||
while (list && list->subroutine != subroutine)
|
||||
list = list->next;
|
||||
return list;
|
||||
}
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// VerificationEnv
|
||||
|
||||
|
||||
const VerificationEnv::InitBinding VerificationEnv::secondWordBinding(bkSecondWord);
|
||||
|
||||
|
||||
//
|
||||
// Assign a partial copy of the given VerificationEnv (which must have been initialized)
|
||||
// to this VerificationEnv, which is known to be uninitialized. Do not copy the
|
||||
// bindings nor the modified arrays inside Contexts. Translate all Subroutine values
|
||||
// using the given translator.
|
||||
//
|
||||
VerificationEnv::VerificationEnv(const VerificationEnv &env, Function1<Subroutine, Subroutine> &translator):
|
||||
common(env.common)
|
||||
{
|
||||
assert(env.live());
|
||||
|
||||
// Copy the bindings pointer (which is now only relevant to indicate whether this
|
||||
// environment is live or not).
|
||||
bindings = env.bindings;
|
||||
sp = env.sp;
|
||||
|
||||
// Copy the context hierarchy.
|
||||
activeContexts = 0;
|
||||
Context *srcContext = env.activeContexts;
|
||||
Context **dstContext = &activeContexts;
|
||||
while (srcContext) {
|
||||
*dstContext = new(common.envPool) Context(*srcContext, common, translator);
|
||||
dstContext = &(*dstContext)->next;
|
||||
srcContext = srcContext->next;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Make this VerificationEnv (which must not have been initialized) live,
|
||||
// assigning bkVoid to every slot.
|
||||
//
|
||||
void VerificationEnv::initLive()
|
||||
{
|
||||
assert(!live() && common.bindingPool);
|
||||
|
||||
// Create and initialize the bindings.
|
||||
Binding *bdgs = new(*common.bindingPool) Binding[common.nEnvSlots];
|
||||
bindings = bdgs;
|
||||
Binding *bdgsEnd = bdgs + common.nEnvSlots;
|
||||
while (bdgs != bdgsEnd)
|
||||
bdgs++->setVoid();
|
||||
sp = common.stackBase;
|
||||
activeContexts = 0;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Assign a copy of the given VerificationEnv (which must have been initialized)
|
||||
// to this VerificationEnv, which is known to be uninitialized.
|
||||
//
|
||||
void VerificationEnv::copyEnv(const VerificationEnv &env)
|
||||
{
|
||||
assert(env.live() && common.bindingPool);
|
||||
|
||||
// Copy the bindings.
|
||||
bindings = new(*common.bindingPool) Binding[common.nEnvSlots];
|
||||
copy(env.bindings, env.bindings + common.nEnvSlots, bindings);
|
||||
sp = env.sp;
|
||||
|
||||
// Copy the context hierarchy.
|
||||
activeContexts = 0;
|
||||
Context *srcContext = env.activeContexts;
|
||||
Context **dstContext = &activeContexts;
|
||||
while (srcContext) {
|
||||
*dstContext = new(common.envPool) Context(*srcContext, common);
|
||||
dstContext = &(*dstContext)->next;
|
||||
srcContext = srcContext->next;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Set the value of the given environment slot, adding that slot to
|
||||
// the list of slots modified by all currently active subroutines.
|
||||
//
|
||||
void VerificationEnv::setSlot(Uint32 slot, const Binding &binding)
|
||||
{
|
||||
assert(slot < common.nEnvSlots);
|
||||
bindings[slot] = binding;
|
||||
for (Context *c = activeContexts; c; c = c->next)
|
||||
c->modify(slot);
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Return the value of the one-word local variable in the given slot.
|
||||
// Throw a verification error if the slot number is out of bounds or the value
|
||||
// is undefined or a part of a two-word value.
|
||||
//
|
||||
const VerificationEnv::Binding &VerificationEnv::getLocal1(Uint32 n) const
|
||||
{
|
||||
assert(live() && common.bindingPool);
|
||||
if (n >= common.nLocals)
|
||||
verifyError(VerifyError::noSuchLocal);
|
||||
Binding &b = bindings[n];
|
||||
if (!b.isOneWord())
|
||||
verifyError(VerifyError::badType);
|
||||
return b;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Return the value of the two-word local variable in the given slot.
|
||||
// Throw a verification error if the slot number is out of bounds or the value
|
||||
// is undefined or not a two-word value.
|
||||
//
|
||||
const VerificationEnv::Binding &VerificationEnv::getLocal2(Uint32 n) const
|
||||
{
|
||||
assert(live() && common.bindingPool);
|
||||
if (n+1 >= common.nLocals)
|
||||
verifyError(VerifyError::noSuchLocal);
|
||||
Binding &b = bindings[n];
|
||||
if (!b.isTwoWord())
|
||||
verifyError(VerifyError::badType);
|
||||
assert(bindings[n+1].hasKind(bkSecondWord));
|
||||
return b;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Set the value of the one-word local variable in the given slot.
|
||||
// Throw a verification error if the slot number is out of bounds.
|
||||
// The given binding must have a one-word kind.
|
||||
//
|
||||
void VerificationEnv::setLocal1(Uint32 n, const Binding &binding)
|
||||
{
|
||||
assert(live() && binding.isOneWord() && common.bindingPool);
|
||||
if (n >= common.nLocals)
|
||||
verifyError(VerifyError::noSuchLocal);
|
||||
Binding *b = &bindings[n];
|
||||
|
||||
// If we're writing into an existing half of a doubleword,
|
||||
// invalidate the other half of that doubleword.
|
||||
BindingKind bk = b->getKind();
|
||||
if (isTwoOrSecondWordKind(bk))
|
||||
b[bk == bkSecondWord ? -1 : 1].setVoid();
|
||||
|
||||
setSlot(n, binding);
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Set the value of the two-word local variable in the given slot.
|
||||
// Throw a verification error if the slot number is out of bounds.
|
||||
// The given binding must have a two-word kind.
|
||||
//
|
||||
void VerificationEnv::setLocal2(Uint32 n, const Binding &binding)
|
||||
{
|
||||
assert(live() && binding.isTwoWord() && common.bindingPool);
|
||||
if (n+1 >= common.nLocals)
|
||||
verifyError(VerifyError::noSuchLocal);
|
||||
Binding *b = &bindings[n];
|
||||
|
||||
// If we're writing into an existing half of a doubleword,
|
||||
// invalidate the other half of that doubleword.
|
||||
if (b[0].hasKind(bkSecondWord))
|
||||
b[-1].setVoid();
|
||||
if (b[1].isTwoWord())
|
||||
b[2].setVoid();
|
||||
|
||||
setSlot(n, binding);
|
||||
setSlot(n+1, secondWordBinding);
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Pop and return a one-word value from the stack.
|
||||
// Throw a verification error if the stack underflows or the value
|
||||
// is undefined or a part of a two-word value.
|
||||
// Note that the returned reference will be invalidated by the next push.
|
||||
//
|
||||
const VerificationEnv::Binding &VerificationEnv::pop1()
|
||||
{
|
||||
assert(live() && common.bindingPool);
|
||||
if (sp == common.stackBase)
|
||||
verifyError(VerifyError::bytecodeStackUnderflow);
|
||||
Binding &b = bindings[--sp];
|
||||
if (!b.isOneWord())
|
||||
verifyError(VerifyError::badType);
|
||||
return b;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Pop and return a one-word value from the stack.
|
||||
// Throw a verification error if the stack underflows or the value
|
||||
// is undefined or has a kind other than the given kind.
|
||||
// Note that the returned reference will be invalidated by the next push.
|
||||
//
|
||||
const VerificationEnv::Binding &VerificationEnv::pop1(BindingKind bk)
|
||||
{
|
||||
assert(live() && isOneWordKind(bk) && common.bindingPool);
|
||||
if (sp == common.stackBase)
|
||||
verifyError(VerifyError::bytecodeStackUnderflow);
|
||||
Binding &b = bindings[--sp];
|
||||
if (!b.hasKind(bk))
|
||||
verifyError(VerifyError::badType);
|
||||
return b;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Pop and return a two-word value or two one-word values from the stack.
|
||||
// Throw a verification error if the stack underflows or the value
|
||||
// is undefined or a part of a two-word value that includes one of the two
|
||||
// stack slots but not the other.
|
||||
//
|
||||
void VerificationEnv::pop2(Binding &binding1, Binding &binding2)
|
||||
{
|
||||
assert(live() && common.bindingPool);
|
||||
if (sp <= common.stackBase+1)
|
||||
verifyError(VerifyError::bytecodeStackUnderflow);
|
||||
sp -= 2;
|
||||
Binding *b = &bindings[sp];
|
||||
BindingKind bk1 = b[0].getKind();
|
||||
BindingKind bk2 = b[1].getKind();
|
||||
if (!(isOneWordKind(bk1) && isOneWordKind(bk2) || isTwoWordKind(bk1)))
|
||||
verifyError(VerifyError::badType);
|
||||
assert(!isTwoWordKind(bk1) || bk2 == bkSecondWord);
|
||||
binding1 = b[0];
|
||||
binding2 = b[1];
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Pop and return a two-word value from the stack.
|
||||
// Throw a verification error if the stack underflows or the value
|
||||
// is undefined or has a kind other than the given kind.
|
||||
// Note that the returned reference will be invalidated by the next push.
|
||||
//
|
||||
const VerificationEnv::Binding &VerificationEnv::pop2(BindingKind bk)
|
||||
{
|
||||
assert(live() && isTwoWordKind(bk) && common.bindingPool);
|
||||
if (sp <= common.stackBase+1)
|
||||
verifyError(VerifyError::bytecodeStackUnderflow);
|
||||
sp -= 2;
|
||||
Binding *b = &bindings[sp];
|
||||
if (!b[0].hasKind(bk))
|
||||
verifyError(VerifyError::badType);
|
||||
assert(b[1].hasKind(bkSecondWord));
|
||||
return *b;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Pop and return a one or two-word value from the stack, depending on bk.
|
||||
// Throw a verification error if the stack underflows or the value
|
||||
// is undefined or has a kind other than the given kind.
|
||||
// Note that the returned reference will be invalidated by the next push.
|
||||
//
|
||||
const VerificationEnv::Binding &VerificationEnv::pop1or2(BindingKind bk)
|
||||
{
|
||||
return isTwoWordKind(bk) ? pop2(bk) : pop1(bk);
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Push a one-word value onto the stack.
|
||||
// Throw a verification error if the stack overflows.
|
||||
// The given binding must have a one-word kind.
|
||||
//
|
||||
void VerificationEnv::push1(const Binding &binding)
|
||||
{
|
||||
assert(live() && binding.isOneWord() && common.bindingPool);
|
||||
if (sp == common.nEnvSlots)
|
||||
verifyError(VerifyError::bytecodeStackOverflow);
|
||||
setSlot(sp++, binding);
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Push a one-word value that contains a new binding of the given kind onto the stack.
|
||||
// Throw a verification error if the stack overflows.
|
||||
//
|
||||
void VerificationEnv::push1(BindingKind bk)
|
||||
{
|
||||
// Note: bkAddr will be outlawed here once we start to distinguish among types of
|
||||
// pointers.
|
||||
assert(bk == bkVoid || bk == bkInt || bk == bkFloat || bk == bkAddr);
|
||||
InitBinding b(bk);
|
||||
push1(b);
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Push a two-word value onto the stack.
|
||||
// Throw a verification error if the stack overflows.
|
||||
// The given binding must have a two-word kind.
|
||||
//
|
||||
void VerificationEnv::push2(const Binding &binding)
|
||||
{
|
||||
assert(live() && binding.isTwoWord() && common.bindingPool);
|
||||
if (sp+1 >= common.nEnvSlots)
|
||||
verifyError(VerifyError::bytecodeStackOverflow);
|
||||
setSlot(sp++, binding);
|
||||
setSlot(sp++, secondWordBinding);
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Push a two-word value that contains a new binding of the given kind onto the stack.
|
||||
// Throw a verification error if the stack overflows.
|
||||
//
|
||||
void VerificationEnv::push2(BindingKind bk)
|
||||
{
|
||||
assert(bk == bkLong || bk == bkDouble);
|
||||
InitBinding b(bk);
|
||||
push2(b);
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Push a two-word value or two one-word values onto the stack.
|
||||
// Throw a verification error if the stack overflows.
|
||||
// The given bindings must be the two words of a two-word value
|
||||
// or both be one-word values.
|
||||
//
|
||||
void VerificationEnv::push2(const Binding &binding1, const Binding &binding2)
|
||||
{
|
||||
assert(live() && (binding1.isTwoWord() && binding2.hasKind(bkSecondWord) ||
|
||||
binding1.isOneWord() && binding2.isOneWord())
|
||||
&& common.bindingPool);
|
||||
if (sp+1 >= common.nEnvSlots)
|
||||
verifyError(VerifyError::bytecodeStackOverflow);
|
||||
setSlot(sp++, binding1);
|
||||
setSlot(sp++, binding2);
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Push a one or two-word value that contains a new binding of the
|
||||
// given kind onto the stack.
|
||||
// Throw a verification error if the stack overflows.
|
||||
//
|
||||
void VerificationEnv::push1or2(BindingKind bk)
|
||||
{
|
||||
// Note: bkAddr will be outlawed here once we start to distinguish among types of
|
||||
// pointers.
|
||||
assert(bk == bkVoid || bk == bkInt || bk == bkFloat || bk == bkAddr || bk == bkLong || bk == bkDouble);
|
||||
InitBinding b(bk);
|
||||
if (isTwoWordKind(bk))
|
||||
push2(b);
|
||||
else
|
||||
push1(b);
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Push the context of the given subroutine onto the list of currently active
|
||||
// contexts. Initialize that context to record all bindings written to this
|
||||
// environment after this enterSubroutine call.
|
||||
//
|
||||
// Throw a verification error if the given subroutine already is on the list
|
||||
// of active contexts. This rejects bytecode programs that call subroutines
|
||||
// recursively. It also rejects some "valid" bytecode programs that do not call
|
||||
// subroutines recursively (for instance, if subroutine A exits via a jump
|
||||
// instead of a ret and then calls subroutine A again), but, fortunately, the
|
||||
// current definition of the Java language does not generate such programs.
|
||||
// (Specifically, each Java try block has a single entry point and the finally
|
||||
// handler -- say, subroutine A -- for that try block is outside that block. In
|
||||
// order for subroutine A to be called again, execution must proceed again
|
||||
// through the entry point of the try block, and we know that at that point
|
||||
// subroutine A is not one of the active contexts.)
|
||||
//
|
||||
void VerificationEnv::enterSubroutine(Subroutine s)
|
||||
{
|
||||
assert(live() && s && common.bindingPool);
|
||||
if (Context::find(activeContexts, s))
|
||||
verifyError(VerifyError::jsrNestingError);
|
||||
|
||||
Context *c = new(common.envPool) Context(s, common);
|
||||
c->next = activeContexts;
|
||||
activeContexts = c;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Pop the context of the given subroutine from the list of currently active
|
||||
// contexts, and replace environment bindings that were not modified by the
|
||||
// subroutine by their contenst from entryEnv, which represents the environment
|
||||
// as it was just before entry to the subroutine. Also pop any contexts more
|
||||
// recent than that of the given subroutine -- these subroutines apparently
|
||||
// exited using a jump instead of a ret.
|
||||
//
|
||||
// Throw a verification error if the subroutine is not on the list of active
|
||||
// contexts, which indicates that there is some program path along which this
|
||||
// ret could be reached without this subroutine having been called first.
|
||||
//
|
||||
void VerificationEnv::exitSubroutine(Subroutine s, const VerificationEnv &entryEnv)
|
||||
{
|
||||
assert(live() && s && entryEnv.live() && common.bindingPool);
|
||||
Context *c = Context::find(activeContexts, s);
|
||||
if (!c)
|
||||
verifyError(VerifyError::jsrNestingError);
|
||||
activeContexts = c->next;
|
||||
|
||||
// Replace unmodified environment bindings.
|
||||
Uint32 nSlots = sp;
|
||||
const Binding *srcBindings = entryEnv.bindings;
|
||||
Binding *dstBindings = bindings;
|
||||
for (Uint32 slot = 0; slot != nSlots; slot++)
|
||||
if (!c->isModified(slot))
|
||||
dstBindings[slot] = srcBindings[slot];
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Intersect the given VerificationEnv (which must be live) into this
|
||||
// VerificationEnv, which must also be live. The two environments may have
|
||||
// different sets of active subroutine contexts, in which case leave the maximal
|
||||
// common set (for instance, intersecting a->b with b yields b, while
|
||||
// intersecting a->b->d->e with a->c->d yields a->d). Because each subroutine has
|
||||
// only one entry point and cannot be recursive, we don't have to worry about
|
||||
// cases such as intersecting a->b with b->a -- nesting of subroutines follows a
|
||||
// partial order.
|
||||
//
|
||||
// Return true if this VerificationEnv changed.
|
||||
//
|
||||
// Throw a verification error if the environments have different stack depths.
|
||||
//
|
||||
bool VerificationEnv::meet(const VerificationEnv &env)
|
||||
{
|
||||
assert(live() && env.live() && common.bindingPool);
|
||||
Uint32 nSlots = sp;
|
||||
if (nSlots != env.sp)
|
||||
verifyError(VerifyError::bytecodeStackDynamic);
|
||||
bool changed = false;
|
||||
|
||||
// Merge context lists
|
||||
Context **dstContextPtr = &activeContexts;
|
||||
Context *srcContext = env.activeContexts;
|
||||
Context *dstContext;
|
||||
while ((dstContext = *dstContextPtr) != 0) {
|
||||
Context *c = Context::find(srcContext, dstContext->subroutine);
|
||||
if (c) {
|
||||
srcContext = c;
|
||||
changed |= dstContext->meet(*c, nSlots);
|
||||
dstContextPtr = &dstContext->next;
|
||||
} else {
|
||||
// Remove this destination context.
|
||||
*dstContextPtr = dstContext->next;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Merge bindings
|
||||
const Binding *srcBinding = env.bindings;
|
||||
Binding *dstBinding = bindings;
|
||||
Binding *dstBindingsEnd = dstBinding + nSlots;
|
||||
while (dstBinding != dstBindingsEnd) {
|
||||
if (*dstBinding != *srcBinding && !dstBinding->hasKind(bkVoid)) {
|
||||
dstBinding->setVoid();
|
||||
changed = true;
|
||||
}
|
||||
srcBinding++;
|
||||
dstBinding++;
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// This VerificationEnv (which must be live) is the entry environment to a ret
|
||||
// bytecode that returns from subroutine s. That subroutine's context is one
|
||||
// of the active contexts in this VerificationEnv. Set that context's retReachable
|
||||
// flag because clearly subroutine s's ret is reachable from here.
|
||||
//
|
||||
void VerificationEnv::setRetReachable(Subroutine s)
|
||||
{
|
||||
assert(live());
|
||||
Context *c = Context::find(activeContexts, s);
|
||||
assert(c);
|
||||
c->retReachable = true;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Merge the retReachable flags of env into this VerificationEnv. env is the
|
||||
// environment of a successor of this VerificationEnv's block. If s is non-nil,
|
||||
// this VerificationEnv's block is a jsr instruction that calls subroutine s,
|
||||
// and env belongs to s's first block; in this case don't set this environment's
|
||||
// retReachable flag for s's context because this environment is outside s.
|
||||
//
|
||||
// Return true if this VerificationEnv changed.
|
||||
//
|
||||
bool VerificationEnv::mergeRetReachables(const VerificationEnv &env, Subroutine s)
|
||||
{
|
||||
assert(live() && env.live());
|
||||
bool changed = false;
|
||||
|
||||
Context *srcContext = env.activeContexts;
|
||||
Context *dstContext = activeContexts;
|
||||
while (srcContext) {
|
||||
if (srcContext->retReachable) {
|
||||
Subroutine srcSubroutine = srcContext->subroutine;
|
||||
if (srcSubroutine != s) {
|
||||
dstContext = Context::find(dstContext, srcSubroutine);
|
||||
assert(dstContext);
|
||||
if (!dstContext->retReachable) {
|
||||
dstContext->retReachable = true;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
srcContext = srcContext->next;
|
||||
}
|
||||
return changed;
|
||||
}
|
|
@ -0,0 +1,210 @@
|
|||
/* -*- 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 VERIFICATIONENV_H
|
||||
#define VERIFICATIONENV_H
|
||||
|
||||
#include "LocalEnv.h"
|
||||
#include "Value.h"
|
||||
#include "ErrorHandling.h"
|
||||
|
||||
struct BytecodeBlock;
|
||||
|
||||
class VerificationEnv
|
||||
{
|
||||
public:
|
||||
typedef BytecodeBlock *Subroutine; // A subroutine is represented by a pointer to its entry BytecodeBlock
|
||||
|
||||
enum BindingKind
|
||||
{
|
||||
bkVoid, // Undefined stack word
|
||||
bkReturn, // jsr/ret return value
|
||||
bkInt, // int value
|
||||
bkFloat, // float value
|
||||
bkAddr, // Object pointer value
|
||||
bkLong, // First word of a long value
|
||||
bkDouble, // First word of a double value
|
||||
bkSecondWord // Second word of a long or double value
|
||||
};
|
||||
|
||||
private:
|
||||
static const BindingKind valueKindBindingKinds[nValueKinds];
|
||||
static const BindingKind typeKindBindingKinds[nTypeKinds];
|
||||
public:
|
||||
static bool isOneWordKind(BindingKind kind) {return (uint)(kind - bkReturn) <= (uint)(bkAddr - bkReturn);}
|
||||
static bool isTwoWordKind(BindingKind kind) {return (uint)(kind - bkLong) <= (uint)(bkDouble - bkLong);}
|
||||
static bool isTwoOrSecondWordKind(BindingKind kind) {return (uint)(kind - bkLong) <= (uint)(bkSecondWord - bkLong);}
|
||||
static BindingKind valueKindToBindingKind(ValueKind kind) {return valueKindBindingKinds[kind];}
|
||||
static BindingKind typeKindToBindingKind(TypeKind kind) {return typeKindBindingKinds[kind];}
|
||||
|
||||
class Binding
|
||||
{
|
||||
BindingKind kind; // Kind of value held in this stack slot
|
||||
union {
|
||||
Subroutine subroutine; // If kind is bkReturn, the subroutine that was called
|
||||
};
|
||||
|
||||
public:
|
||||
BindingKind getKind() {return kind;}
|
||||
bool hasKind(BindingKind k) const {return kind == k;}
|
||||
bool isOneWord() const {return isOneWordKind(kind);}
|
||||
bool isTwoWord() const {return isTwoWordKind(kind);}
|
||||
bool isTwoOrSecondWord() const {return isTwoOrSecondWordKind(kind);}
|
||||
|
||||
bool operator==(const Binding &b2) const {return kind == b2.kind && (kind != bkReturn || subroutine == b2.subroutine);}
|
||||
bool operator!=(const Binding &b2) const {return !operator==(b2);}
|
||||
|
||||
void setVoid() {kind = bkVoid;}
|
||||
void setReturn(Subroutine s) {kind = bkReturn; subroutine = s;}
|
||||
void setKind(BindingKind k) {assert(k != bkReturn); kind = k;}
|
||||
|
||||
Subroutine getSubroutine() const {assert(kind == bkReturn); return subroutine;}
|
||||
};
|
||||
|
||||
struct InitBinding: Binding
|
||||
{
|
||||
explicit InitBinding(BindingKind kind) {setKind(kind);}
|
||||
};
|
||||
|
||||
struct Common: CommonEnv
|
||||
{
|
||||
Pool *bindingPool; // Pool used for allocating arrays of bindings and 'modified' arrays inside Contexts; nil if none
|
||||
|
||||
Common(Pool &envPool, Pool *bindingPool, Uint32 nLocals, Uint32 stackSize):
|
||||
CommonEnv(envPool, 0, nLocals, stackSize), bindingPool(bindingPool) {}
|
||||
|
||||
void setBindingPool(Pool &pool) {assert(!bindingPool); bindingPool = &pool;}
|
||||
void clearBindingPool() {bindingPool = 0;} // Trashes bindings arrays and 'modified' arrays inside Contexts
|
||||
};
|
||||
|
||||
private:
|
||||
struct Context
|
||||
{
|
||||
Context *next; // Link to next outer subroutine context or nil if none
|
||||
const Subroutine subroutine; // Pointer to the first instruction of a subroutine that was called and not yet returned
|
||||
private:
|
||||
char *modified; // Array of nLocals+stackSize bytes; each is set if the corresponding local variable
|
||||
// or stack temporary has been modified since entry to the subroutine;
|
||||
public: // undefined if common.bindingPool is nil.
|
||||
bool retReachable; // True if subroutine's ret is reachable from this block (without going through subroutine's jsr)
|
||||
#ifdef DEBUG
|
||||
const Common &common; // Backpointer to information shared among all VerificationEnvs in a graph
|
||||
#endif
|
||||
|
||||
Context(Subroutine subroutine, const Common &common);
|
||||
Context(const Context &src, const Common &common);
|
||||
Context(const Context &src, const Common &common, Function1<Subroutine, Subroutine> &translator);
|
||||
|
||||
void modify(Uint32 slot) {assert(common.bindingPool); modified[slot] = 1;}
|
||||
bool isModified(Uint32 slot) {assert(common.bindingPool); return modified[slot] != 0;}
|
||||
bool meet(const Context &src, Uint32 nSlots);
|
||||
|
||||
static Context *find(Context *list, const Subroutine subroutine);
|
||||
};
|
||||
|
||||
Common &common; // Backpointer to information shared among all VerificationEnvs in a graph
|
||||
Binding *bindings; // Array of:
|
||||
// nLocals local variable bindings
|
||||
// stackSize stack temporaries' bindings;
|
||||
// bindings==nil means no information is available yet.
|
||||
// If common.bindingPool is nil, bindings is either nil or non-nil but does not point to anything.
|
||||
Uint32 sp; // Stack pointer (index within bindings array of first unused temporary)
|
||||
// Bindings at indices 0..sp-1 are valid; others are ignored
|
||||
Context *activeContexts; // Linked list of all currently active subroutines from inner to outer
|
||||
|
||||
static const InitBinding secondWordBinding; // Always contains a bkSecondWord binding
|
||||
|
||||
public:
|
||||
explicit VerificationEnv(Common &common): common(common), bindings(0) {}
|
||||
VerificationEnv(const VerificationEnv &env): common(env.common) {copyEnv(env);}
|
||||
VerificationEnv(const VerificationEnv &env, Function1<Subroutine, Subroutine> &translator);
|
||||
void operator=(const VerificationEnv &env) {assert(!live()); copyEnv(env);}
|
||||
void move(VerificationEnv &env);
|
||||
|
||||
void initLive();
|
||||
bool live() const {return bindings != 0;}
|
||||
|
||||
private:
|
||||
void copyEnv(const VerificationEnv &env);
|
||||
void setSlot(Uint32 slot, const Binding &binding);
|
||||
|
||||
public:
|
||||
// Local variable operations
|
||||
const Binding &getLocal1(Uint32 n) const;
|
||||
const Binding &getLocal2(Uint32 n) const;
|
||||
void setLocal1(Uint32 n, const Binding &binding);
|
||||
void setLocal2(Uint32 n, const Binding &binding);
|
||||
|
||||
// Stack operations
|
||||
Uint32 getSP() const {return sp - common.stackBase;}
|
||||
void dropAll() {sp = common.stackBase;}
|
||||
const Binding &pop1();
|
||||
const Binding &pop1(BindingKind bk);
|
||||
void pop2(Binding &binding1, Binding &binding2);
|
||||
const Binding &pop2(BindingKind bk);
|
||||
const Binding &pop1or2(BindingKind bk);
|
||||
void push1(const Binding &binding);
|
||||
void push1(BindingKind bk);
|
||||
void push2(const Binding &binding);
|
||||
void push2(BindingKind bk);
|
||||
void push2(const Binding &binding1, const Binding &binding2);
|
||||
void push1or2(BindingKind bk);
|
||||
|
||||
void enterSubroutine(Subroutine s);
|
||||
void exitSubroutine(Subroutine s, const VerificationEnv &entryEnv);
|
||||
|
||||
bool meet(const VerificationEnv &env);
|
||||
|
||||
bool isRetReachable(Subroutine s);
|
||||
void setRetReachable(Subroutine s);
|
||||
bool mergeRetReachables(const VerificationEnv &env, Subroutine s);
|
||||
};
|
||||
|
||||
|
||||
// --- INLINES ----------------------------------------------------------------
|
||||
|
||||
|
||||
//
|
||||
// Destructively move the given VerificationEnv (which must have been initialized)
|
||||
// to this VerificationEnv, which must be uninitialized. The given VerificationEnv is
|
||||
// left uninitialized.
|
||||
//
|
||||
inline void VerificationEnv::move(VerificationEnv &env)
|
||||
{
|
||||
assert(!live() && env.live());
|
||||
bindings = env.bindings;
|
||||
sp = env.sp;
|
||||
activeContexts = env.activeContexts;
|
||||
#ifdef DEBUG
|
||||
env.bindings = 0;
|
||||
env.activeContexts = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Return true if Subroutine s is one of the active contexts in this VerificationEnv
|
||||
// and that context's retReachable flag is true.
|
||||
//
|
||||
inline bool VerificationEnv::isRetReachable(Subroutine s)
|
||||
{
|
||||
assert(live());
|
||||
Context *c = Context::find(activeContexts, s);
|
||||
return c && c->retReachable;
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,56 @@
|
|||
#!gmake
|
||||
#
|
||||
# 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.
|
||||
|
||||
#######################################################################
|
||||
# (1) Directory specific info #
|
||||
#######################################################################
|
||||
|
||||
DEPTH = ..
|
||||
|
||||
DIRS = CodeGenerator FrontEnd PrimitiveGraph Optimizer RegisterAllocator
|
||||
|
||||
SUBMODULES = $(DIRS)
|
||||
|
||||
MODULE_NAME = EF
|
||||
|
||||
NO_PROGRAM_IN_SUBDIRS = 1
|
||||
NO_INSTALL_IN_SUBDIRS = 1
|
||||
|
||||
#######################################################################
|
||||
# (2) Include "component" configuration information. #
|
||||
#######################################################################
|
||||
|
||||
include $(DEPTH)/config/config.mk
|
||||
|
||||
#######################################################################
|
||||
# (3) Include "local" platform-dependent assignments (OPTIONAL). #
|
||||
#######################################################################
|
||||
|
||||
|
||||
|
||||
#######################################################################
|
||||
# (4) Execute "component" rules. (OPTIONAL) #
|
||||
#######################################################################
|
||||
|
||||
include $(DEPTH)/config/rules.mk
|
||||
|
||||
#######################################################################
|
||||
# (7) Execute "local" rules. (OPTIONAL). #
|
||||
#######################################################################
|
||||
|
||||
|
||||
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче