зеркало из https://github.com/mozilla/pluotsorbet.git
1456 строки
52 KiB
TypeScript
1456 строки
52 KiB
TypeScript
module J2ME {
|
|
|
|
var debug = false;
|
|
var writer = null; // new IndentingWriter(true);
|
|
var consoleWriter = new IndentingWriter();
|
|
|
|
export var counter = new J2ME.Metrics.Counter(true);
|
|
|
|
export function printResults() {
|
|
counter.traceSorted(stderrWriter);
|
|
for (var k in yieldMap) {
|
|
stderrWriter.writeLn(YieldReason[yieldMap[k]].padRight(" ", 20) + " " + k);
|
|
}
|
|
}
|
|
|
|
import Block = Bytecode.Block;
|
|
import BlockMap = Bytecode.BlockMap;
|
|
|
|
import assert = Debug.assert;
|
|
import unique = ArrayUtilities.unique;
|
|
|
|
import IR = C4.IR;
|
|
import Node = IR.Node;
|
|
import Value = IR.Value;
|
|
import Phi = IR.Phi;
|
|
import Control = IR.Control;
|
|
import Constant = IR.Constant;
|
|
import Start = IR.Start;
|
|
import Region = IR.Region;
|
|
import ProjectionType = IR.ProjectionType;
|
|
import Null = IR.Null;
|
|
import Undefined = IR.Undefined;
|
|
import True = IR.True;
|
|
import False = IR.False;
|
|
import Operator = IR.Operator;
|
|
import PeepholeOptimizer = IR.PeepholeOptimizer;
|
|
|
|
import Bytecodes = Bytecode.Bytecodes;
|
|
import BytecodeStream = Bytecode.BytecodeStream;
|
|
import Condition = Bytecode.Condition;
|
|
|
|
function kindsFromSignature(signature: string) {
|
|
|
|
}
|
|
|
|
var staticCallGraph = Object.create(null);
|
|
|
|
declare var Long: any;
|
|
declare var VM: any;
|
|
|
|
function conditionToOperator(condition: Condition): Operator {
|
|
switch (condition) {
|
|
case Condition.EQ: return Operator.EQ;
|
|
case Condition.NE: return Operator.NE;
|
|
case Condition.LT: return Operator.LT;
|
|
case Condition.LE: return Operator.LE;
|
|
case Condition.GT: return Operator.GT;
|
|
case Condition.GE: return Operator.GE;
|
|
default: throw "TODO"
|
|
}
|
|
}
|
|
|
|
export function isTwoSlot(kind: Kind) {
|
|
return kind === Kind.Long || kind === Kind.Double;
|
|
}
|
|
|
|
export function assertHigh(x: Value) {
|
|
assert (x === null);
|
|
}
|
|
|
|
export enum CompilationTarget {
|
|
Runtime,
|
|
Buildtime,
|
|
Static
|
|
}
|
|
|
|
export enum TAGS {
|
|
CONSTANT_Class = 7,
|
|
CONSTANT_Fieldref = 9,
|
|
CONSTANT_Methodref = 10,
|
|
CONSTANT_InterfaceMethodref = 11,
|
|
CONSTANT_String = 8,
|
|
CONSTANT_Integer = 3,
|
|
CONSTANT_Float = 4,
|
|
CONSTANT_Long = 5,
|
|
CONSTANT_Double = 6,
|
|
CONSTANT_NameAndType = 12,
|
|
CONSTANT_Utf8 = 1,
|
|
CONSTANT_Unicode = 2
|
|
}
|
|
|
|
export interface ConstantPoolEntry {
|
|
tag: TAGS;
|
|
name_index: number;
|
|
bytes: string;
|
|
class_index: number;
|
|
name_and_type_index: number;
|
|
signature_index: number;
|
|
string_index: number;
|
|
integer: number;
|
|
float: number;
|
|
double: number;
|
|
highBits: number;
|
|
lowBits: number;
|
|
}
|
|
|
|
export interface ExceptionHandler {
|
|
start_pc: number;
|
|
end_pc: number;
|
|
handler_pc: number;
|
|
catch_type: number;
|
|
}
|
|
|
|
function assertKind(kind: Kind, x: Node): Node {
|
|
assert(stackKind(x.kind) === stackKind(kind), "Got " + kindCharacter(stackKind(x.kind)) + " expected " + kindCharacter(stackKind(kind)));
|
|
return x;
|
|
}
|
|
|
|
export class State {
|
|
private static _nextID = 0;
|
|
id: number;
|
|
bci: number;
|
|
nextBCI: number;
|
|
local: Value [];
|
|
stack: Value [];
|
|
store: Value;
|
|
loads: Value [];
|
|
constructor(bci: number = 0) {
|
|
this.id = State._nextID += 1;
|
|
this.bci = bci;
|
|
this.nextBCI = -1;
|
|
this.local = [];
|
|
this.stack = [];
|
|
this.store = Undefined;
|
|
this.loads = [];
|
|
}
|
|
|
|
clone(bci: number, nextBCI: number = -1) {
|
|
var s = new State();
|
|
s.bci = bci !== undefined ? bci : this.bci;
|
|
s.nextBCI = nextBCI;
|
|
s.local = this.local.slice(0);
|
|
s.stack = this.stack.slice(0);
|
|
s.loads = this.loads.slice(0);
|
|
s.store = this.store;
|
|
return s;
|
|
}
|
|
|
|
matches(other: State) {
|
|
return this.stack.length === other.stack.length &&
|
|
this.local.length === other.local.length;
|
|
}
|
|
|
|
makeLoopPhis(control: Control, dirtyLocals: boolean []) {
|
|
var s = new State();
|
|
release || assert (control);
|
|
function makePhi(x) {
|
|
var phi = new Phi(control, x);
|
|
phi.kind = x.kind;
|
|
phi.isLoop = true;
|
|
return phi;
|
|
}
|
|
s.bci = this.bci;
|
|
s.local = this.local.map(function (v, i) {
|
|
if (v === null) {
|
|
return null;
|
|
}
|
|
if (true || dirtyLocals[i]) {
|
|
return makePhi(v);
|
|
}
|
|
return v;
|
|
});
|
|
s.stack = this.stack.map(makePhi);
|
|
s.loads = this.loads.slice(0);
|
|
s.store = makePhi(this.store);
|
|
return s;
|
|
}
|
|
|
|
static tryOptimizePhi(x: Value) {
|
|
if (x instanceof Phi) {
|
|
var phi: Phi = <Phi>x;
|
|
if (phi.isLoop) {
|
|
return phi;
|
|
}
|
|
var args = unique(phi.args);
|
|
if (args.length === 1) {
|
|
phi.seal();
|
|
countTimeline("Builder: OptimizedPhi");
|
|
return args[0];
|
|
}
|
|
}
|
|
return x;
|
|
}
|
|
|
|
optimize() {
|
|
this.local = this.local.map(State.tryOptimizePhi);
|
|
this.stack = this.stack.map(State.tryOptimizePhi);
|
|
this.store = State.tryOptimizePhi(this.store);
|
|
}
|
|
|
|
static mergeValue(control: Control, a: Value, b: Value): Phi {
|
|
var phi;
|
|
if (a instanceof Phi && a.control === control) {
|
|
phi = a;
|
|
} else {
|
|
phi = new Phi(control, a);
|
|
phi.kind = a.kind;
|
|
}
|
|
if (a.kind === Kind.Store) {
|
|
release || assert(b.kind === Kind.Store, "Got " + Kind[b.kind] + " should be store.");
|
|
} else if (b === null || b === Illegal || stackKind(a.kind) !== stackKind(b.kind)) {
|
|
// TODO get rid of the null check by pushing Illegals for doubles/longs.
|
|
b = Illegal;
|
|
}
|
|
phi.pushValue(b);
|
|
return phi;
|
|
}
|
|
|
|
static mergeValues(control: Control, a: Value [], b: Value []) {
|
|
for (var i = 0; i < a.length; i++) {
|
|
if (a[i] === null) {
|
|
continue;
|
|
}
|
|
a[i] = State.mergeValue(control, a[i], b[i]);
|
|
if (isTwoSlot(a[i].kind)) {
|
|
i++;
|
|
}
|
|
}
|
|
}
|
|
|
|
merge(control: Control, other: State) {
|
|
release || assert (control);
|
|
release || assert (this.matches(other), this + " !== " + other);
|
|
State.mergeValues(control, this.local, other.local);
|
|
State.mergeValues(control, this.stack, other.stack);
|
|
this.store = State.mergeValue(control, this.store, other.store);
|
|
this.store.abstract = true;
|
|
}
|
|
|
|
trace(writer: IndentingWriter) {
|
|
writer.writeLn(this.toString());
|
|
}
|
|
|
|
static toBriefString(x: Node) : string {
|
|
if (x instanceof Node) {
|
|
return kindCharacter(x.kind); // + x.toString(true);
|
|
}
|
|
if (x === null) {
|
|
return "_";
|
|
} else if (x === undefined) {
|
|
return "undefined";
|
|
}
|
|
return String(x);
|
|
}
|
|
|
|
toString(): string {
|
|
return "<" + String(this.id + " @ " + this.bci).padRight(' ', 10) +
|
|
(" M: " + State.toBriefString(this.store)).padRight(' ', 14) +
|
|
(" L: " + this.local.map(State.toBriefString).join(", ")).padRight(' ', 40) +
|
|
(" S: " + this.stack.map(State.toBriefString).join(", ")).padRight(' ', 60) +
|
|
(" R: " + this.loads.map(State.toBriefString).join(", ")).padRight(' ', 60);
|
|
}
|
|
|
|
/**
|
|
* Pushes a value onto the stack without checking the type.
|
|
*/
|
|
public xpush(x: Node) {
|
|
assert (x === null || !x.isDeleted);
|
|
assert (x === null || (x.kind !== Kind.Void && x.kind !== Kind.Illegal), "Unexpected value: " + x);
|
|
this.stack.push(x);
|
|
}
|
|
|
|
/**
|
|
* Pushes a value onto the stack and checks that it is an int.
|
|
*/
|
|
public ipush(x: Node) {
|
|
this.push(Kind.Int, x);
|
|
}
|
|
|
|
/**
|
|
* Pushes a value onto the stack and checks that it is a float.
|
|
* @param x the instruction to push onto the stack
|
|
*/
|
|
public fpush(x: Node) {
|
|
this.push(Kind.Float, x);
|
|
}
|
|
|
|
/**
|
|
* Pushes a value onto the stack and checks that it is an object.
|
|
*/
|
|
public apush(x: Node) {
|
|
this.push(Kind.Reference, x);
|
|
}
|
|
|
|
/**
|
|
* Pushes a value onto the stack and checks that it is a long.
|
|
*/
|
|
public lpush(x: Node) {
|
|
this.push(Kind.Long, x);
|
|
}
|
|
|
|
/**
|
|
* Pushes a value onto the stack and checks that it is a double.
|
|
*/
|
|
public dpush(x: Node) {
|
|
this.push(Kind.Double, x);
|
|
}
|
|
|
|
/**
|
|
* Pushes an instruction onto the stack with the expected type.
|
|
*/
|
|
public push(kind: Kind, x: Value) {
|
|
assert (kind !== Kind.Void);
|
|
if (x.kind === undefined) {
|
|
x.kind = kind;
|
|
}
|
|
|
|
this.xpush(assertKind(kind, x));
|
|
if (isTwoSlot(kind)) {
|
|
this.xpush(null);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Pops an instruction off the stack with the expected type.
|
|
*/
|
|
public pop(kind: Kind): Value {
|
|
assert (kind !== Kind.Void);
|
|
kind = stackKind(kind);
|
|
if (isTwoSlot(kind)) {
|
|
this.xpop();
|
|
}
|
|
return assertKind(kind, this.xpop());
|
|
}
|
|
|
|
/**
|
|
* Pops a value off of the stack without checking the type.
|
|
*/
|
|
public xpop(): Value {
|
|
var result = this.stack.pop();
|
|
assert (result === null || !result.isDeleted);
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Pops a value off of the stack and checks that it is an int.
|
|
*/
|
|
public ipop(): Value {
|
|
return assertKind(Kind.Int, this.xpop());
|
|
}
|
|
|
|
/**
|
|
* Pops a value off of the stack and checks that it is a float.
|
|
*/
|
|
public fpop(): Value {
|
|
return assertKind(Kind.Float, this.xpop());
|
|
}
|
|
|
|
/**
|
|
* Pops a value off of the stack and checks that it is an object.
|
|
*/
|
|
public apop(): Value {
|
|
return assertKind(Kind.Reference, this.xpop());
|
|
}
|
|
|
|
/**
|
|
* Pops a value off of the stack and checks that it is a long.
|
|
*/
|
|
public lpop(): Value {
|
|
assertHigh(this.xpop());
|
|
return assertKind(Kind.Long, this.xpop());
|
|
}
|
|
|
|
/**
|
|
* Pops a value off of the stack and checks that it is a double.
|
|
*/
|
|
public dpop(): Value {
|
|
assertHigh(this.xpop());
|
|
return assertKind(Kind.Double, this.xpop());
|
|
}
|
|
|
|
public peek(): Value {
|
|
return this.stack[this.stack.length - 1];
|
|
}
|
|
|
|
/**
|
|
* Loads the local variable at the specified index.
|
|
*/
|
|
public loadLocal(i: number): Value {
|
|
var x = this.local[i];
|
|
if (x != null) {
|
|
if (x instanceof Phi) {
|
|
// assert ((PhiNode) x).type() == PhiType.Value;
|
|
if (x.isDeleted) {
|
|
return null;
|
|
}
|
|
}
|
|
assert (!isTwoSlot(x.kind) || this.local[i + 1] === null || this.local[i + 1] instanceof Phi);
|
|
}
|
|
return x;
|
|
}
|
|
|
|
/**
|
|
* Stores a given local variable at the specified index. If the value takes up two slots,
|
|
* then the next local variable index is also overwritten.
|
|
*/
|
|
public storeLocal(i: number, x: Value) {
|
|
assert (x === null || (x.kind !== Kind.Void && x.kind !== Kind.Illegal), "Unexpected value: " + x);
|
|
var local = this.local;
|
|
local[i] = x;
|
|
if (isTwoSlot(x.kind)) {
|
|
// (tw) if this was a double word then kill i + 1
|
|
local[i + 1] = null;
|
|
}
|
|
if (i > 0) {
|
|
// if there was a double word at i - 1, then kill it
|
|
var p = local[i - 1];
|
|
if (p !== null && isTwoSlot(p.kind)) {
|
|
local[i - 1] = null;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
export function quote(s) {
|
|
return "\"" + s + "\"";
|
|
}
|
|
|
|
interface WorklistItem {
|
|
region: Region;
|
|
block: Block;
|
|
}
|
|
|
|
interface Stop {
|
|
|
|
}
|
|
|
|
function genConstant(x: any, kind: Kind): IR.Constant {
|
|
var constant;
|
|
if (kind === Kind.Long) {
|
|
constant = new IR.JVMLong(x, 0);
|
|
} else if (kind === Kind.Reference) {
|
|
if (isString(x)) {
|
|
constant = new IR.JVMString(x);
|
|
} else {
|
|
constant = new IR.Constant(x);
|
|
}
|
|
} else {
|
|
constant = new IR.Constant(x);
|
|
}
|
|
constant.kind = kind;
|
|
return constant;
|
|
}
|
|
|
|
var Illegal = genConstant(undefined, Kind.Illegal);
|
|
|
|
class StopInfo {
|
|
constructor(
|
|
public control: Control,
|
|
public target: Block,
|
|
public state: State) {
|
|
// ...
|
|
}
|
|
}
|
|
|
|
class ReturnInfo {
|
|
constructor(
|
|
public control: Control,
|
|
public store: Node,
|
|
public value: Value) {
|
|
// ...
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* TODO: Consider using debug info for nicer parameter names, if available.
|
|
*/
|
|
function getParameterName(methodInfo: MethodInfo, i: number): string {
|
|
var parameterNames = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
|
return i < parameterNames.length ? parameterNames[i] : "p" + (parameterNames.length - i);
|
|
}
|
|
|
|
class Builder {
|
|
peepholeOptimizer: PeepholeOptimizer;
|
|
signatureDescriptor: SignatureDescriptor;
|
|
|
|
/**
|
|
* Current state vector.
|
|
*/
|
|
state: State;
|
|
|
|
/**
|
|
* Current region.
|
|
*/
|
|
region: Region;
|
|
|
|
/**
|
|
* Stop infos accumulated for the last processed block.
|
|
*/
|
|
blockStopInfos: StopInfo [];
|
|
|
|
/**
|
|
* Methor return infos accumulated during the processing of this method.
|
|
*/
|
|
methodReturnInfos: ReturnInfo [];
|
|
|
|
/**
|
|
* Current block map.
|
|
*/
|
|
blockMap: BlockMap;
|
|
|
|
parameters: IR.Parameter [];
|
|
|
|
referencedClasses: ClassInfo [];
|
|
|
|
constructor(public methodInfo: MethodInfo, public target: CompilationTarget) {
|
|
// ...
|
|
this.peepholeOptimizer = new PeepholeOptimizer();
|
|
this.signatureDescriptor = SignatureDescriptor.makeSignatureDescriptor(methodInfo.signature);
|
|
this.methodReturnInfos = [];
|
|
this.parameters = [];
|
|
this.referencedClasses = [];
|
|
}
|
|
|
|
build(): C4.Backend.Compilation {
|
|
IR.Node.startNumbering();
|
|
|
|
var methodInfo = this.methodInfo;
|
|
|
|
writer && writer.enter("Compiling Method: " + methodInfo.name + " " + methodInfo.signature + " {");
|
|
writer && writer.writeLn("Size: " + methodInfo.code.length);
|
|
var blockMap = this.blockMap = new BlockMap(methodInfo);
|
|
blockMap.build();
|
|
|
|
var start = this.buildStart();
|
|
var dfg = this.buildGraph(start, start.entryState.clone());
|
|
|
|
writer && dfg.trace(writer);
|
|
|
|
enterTimeline("Build CFG");
|
|
var cfg = dfg.buildCFG();
|
|
leaveTimeline();
|
|
|
|
enterTimeline("Verify IR");
|
|
cfg.verify();
|
|
leaveTimeline();
|
|
|
|
enterTimeline("Optimize Phis");
|
|
cfg.optimizePhis();
|
|
leaveTimeline();
|
|
|
|
enterTimeline("Schedule Nodes");
|
|
cfg.scheduleEarly();
|
|
leaveTimeline();
|
|
|
|
writer && cfg.trace(writer);
|
|
|
|
enterTimeline("Verify IR");
|
|
cfg.verify();
|
|
leaveTimeline();
|
|
|
|
enterTimeline("Allocate Variables");
|
|
cfg.allocateVariables();
|
|
leaveTimeline();
|
|
|
|
enterTimeline("Generate Source");
|
|
var result = C4.Backend.generate(cfg);
|
|
leaveTimeline();
|
|
|
|
Node.stopNumbering();
|
|
leaveTimeline();
|
|
|
|
writer && writer.leave("}");
|
|
IR.Node.stopNumbering();
|
|
return result;
|
|
}
|
|
|
|
buildStart(): IR.Start {
|
|
var start = new IR.Start();
|
|
var state = start.entryState = new State();
|
|
var methodInfo = this.methodInfo;
|
|
|
|
for (var i = 0; i < methodInfo.max_locals; i++) {
|
|
state.local.push(null);
|
|
}
|
|
|
|
state.store = new IR.Projection(start, ProjectionType.STORE);
|
|
state.store.kind = Kind.Store;
|
|
|
|
var signatureDescriptor = this.signatureDescriptor;
|
|
writer && writer.writeLn("SIG: " + signatureDescriptor);
|
|
|
|
var typeDescriptors = signatureDescriptor.typeDescriptors;
|
|
|
|
var localIndex = 0;
|
|
var parameterIndex = 1;
|
|
if (!methodInfo.isStatic) {
|
|
var self = new IR.This(start);
|
|
self.kind = Kind.Reference;
|
|
state.storeLocal(0, self);
|
|
parameterIndex++;
|
|
localIndex = 1;
|
|
}
|
|
// Skip the first typeDescriptor since it is the return type.
|
|
for (var i = 1; i < typeDescriptors.length; i++) {
|
|
var kind = Kind.Reference;
|
|
if (typeDescriptors[i] instanceof AtomicTypeDescriptor) {
|
|
kind = (<AtomicTypeDescriptor>typeDescriptors[i]).kind;
|
|
}
|
|
var parameter = new IR.Parameter(start, parameterIndex, getParameterName(this.methodInfo, parameterIndex - 1));
|
|
this.parameters.push(parameter);
|
|
parameter.kind = kind;
|
|
parameterIndex++;
|
|
state.storeLocal(localIndex, parameter);
|
|
localIndex += isTwoSlot(kind) ? 2 : 1;
|
|
}
|
|
return start;
|
|
}
|
|
|
|
buildGraph(start: Region, state: State): IR.DFG {
|
|
var worklist = new SortedList<WorklistItem>(function compare(a: WorklistItem, b: WorklistItem) {
|
|
return a.block.blockID - b.block.blockID;
|
|
});
|
|
|
|
worklist.push({
|
|
region: start,
|
|
block: this.blockMap.blocks[0]
|
|
});
|
|
|
|
var next: WorklistItem;
|
|
while ((next = worklist.pop())) {
|
|
writer && writer.writeLn("Processing: " + next.region + " " + next.block.blockID + " " + next.region.entryState);
|
|
this.buildBlock(next.region, next.block, next.region.entryState.clone());
|
|
if (!this.blockStopInfos) {
|
|
continue;
|
|
}
|
|
this.blockStopInfos.forEach(function (stop: StopInfo) {
|
|
var target = stop.target;
|
|
writer && writer.writeLn(String(target));
|
|
var region = target.region;
|
|
if (region) {
|
|
writer && writer.enter("Merging into region: " + region + " @ " + target.startBci + ", block " + target.blockID + " {");
|
|
writer && writer.writeLn(" R " + region.entryState);
|
|
writer && writer.writeLn("+ I " + stop.state);
|
|
|
|
region.entryState.merge(region, stop.state);
|
|
region.predecessors.push(stop.control);
|
|
|
|
writer && writer.writeLn(" = " + region.entryState);
|
|
writer && writer.leave("}");
|
|
} else {
|
|
region = target.region = new Region(stop.control);
|
|
var dirtyLocals: boolean [] = [];
|
|
// if (target.loop) {
|
|
// dirtyLocals = enableDirtyLocals.value && target.loop.getDirtyLocals();
|
|
// writer && writer.writeLn("Adding PHIs to loop region. " + dirtyLocals);
|
|
// }
|
|
region.entryState = target.isLoopHeader ? stop.state.makeLoopPhis(region, dirtyLocals) : stop.state.clone(target.startBci);
|
|
writer && writer.writeLn("Adding new region: " + region + " @ " + target.startBci + " to worklist.");
|
|
worklist.push({region: region, block: target});
|
|
}
|
|
});
|
|
|
|
writer && writer.enter("Worklist: {");
|
|
worklist.forEach(function (item) {
|
|
writer && writer.writeLn(item.region + " " + item.block.blockID + " " + item.region.entryState);
|
|
});
|
|
writer && writer.leave("}");
|
|
}
|
|
var signatureDescriptor = this.signatureDescriptor;
|
|
var returnType = signatureDescriptor.typeDescriptors[0];
|
|
|
|
// TODO handle void return types
|
|
var stop;
|
|
var returnInfos = this.methodReturnInfos;
|
|
assert (returnInfos.length > 0);
|
|
var returnRegion = new Region(null);
|
|
var returnValuePhi = new Phi(returnRegion, null);
|
|
var returnStorePhi = new Phi(returnRegion, null);
|
|
returnInfos.forEach(function (returnInfo) {
|
|
returnRegion.predecessors.push(returnInfo.control);
|
|
returnValuePhi.pushValue(returnInfo.value);
|
|
returnStorePhi.pushValue(returnInfo.store);
|
|
});
|
|
stop = new IR.Stop(returnRegion, returnStorePhi, returnValuePhi);
|
|
return new IR.DFG(stop);
|
|
}
|
|
|
|
buildBlock(region: Region, block: Block, state: State) {
|
|
this.blockStopInfos = null;
|
|
this.state = state;
|
|
this.region = region;
|
|
var code = this.methodInfo.code;
|
|
var stream = new BytecodeStream(code);
|
|
var bci = block.startBci;
|
|
stream.setBCI(bci);
|
|
|
|
while (stream.currentBCI <= block.endBci) {
|
|
state.bci = bci;
|
|
this.processBytecode(stream, state);
|
|
if (Bytecode.isReturn(stream.currentBC()) ||
|
|
Bytecodes.ATHROW === stream.currentBC()) {
|
|
release || assert (!this.blockStopInfos, "Should not have any stops.");
|
|
return;
|
|
}
|
|
stream.next();
|
|
bci = stream.currentBCI;
|
|
}
|
|
|
|
if (!this.blockStopInfos) {
|
|
this.blockStopInfos = [new StopInfo(region,
|
|
this.blockMap.getBlock(stream.currentBCI),
|
|
this.state
|
|
)];
|
|
}
|
|
}
|
|
|
|
private loadLocal(index: number, kind: Kind) {
|
|
this.state.push(kind, this.state.loadLocal(index));
|
|
}
|
|
|
|
private storeLocal(kind: Kind, index: number) {
|
|
this.state.storeLocal(index, this.state.pop(kind));
|
|
}
|
|
|
|
private stackOp(opcode: Bytecodes) {
|
|
var state = this.state;
|
|
switch (opcode) {
|
|
case Bytecodes.POP: {
|
|
state.xpop();
|
|
break;
|
|
}
|
|
case Bytecodes.POP2: {
|
|
state.xpop();
|
|
state.xpop();
|
|
break;
|
|
}
|
|
case Bytecodes.DUP: {
|
|
var w = state.xpop();
|
|
state.xpush(w);
|
|
state.xpush(w);
|
|
break;
|
|
}
|
|
case Bytecodes.DUP_X1: {
|
|
var w1 = state.xpop();
|
|
var w2 = state.xpop();
|
|
state.xpush(w1);
|
|
state.xpush(w2);
|
|
state.xpush(w1);
|
|
break;
|
|
}
|
|
case Bytecodes.DUP_X2: {
|
|
var w1 = state.xpop();
|
|
var w2 = state.xpop();
|
|
var w3 = state.xpop();
|
|
state.xpush(w1);
|
|
state.xpush(w3);
|
|
state.xpush(w2);
|
|
state.xpush(w1);
|
|
break;
|
|
}
|
|
case Bytecodes.DUP2: {
|
|
var w1 = state.xpop();
|
|
var w2 = state.xpop();
|
|
state.xpush(w2);
|
|
state.xpush(w1);
|
|
state.xpush(w2);
|
|
state.xpush(w1);
|
|
break;
|
|
}
|
|
case Bytecodes.DUP2_X1: {
|
|
var w1 = state.xpop();
|
|
var w2 = state.xpop();
|
|
var w3 = state.xpop();
|
|
state.xpush(w2);
|
|
state.xpush(w1);
|
|
state.xpush(w3);
|
|
state.xpush(w2);
|
|
state.xpush(w1);
|
|
break;
|
|
}
|
|
case Bytecodes.DUP2_X2: {
|
|
var w1 = state.xpop();
|
|
var w2 = state.xpop();
|
|
var w3 = state.xpop();
|
|
var w4 = state.xpop();
|
|
state.xpush(w2);
|
|
state.xpush(w1);
|
|
state.xpush(w4);
|
|
state.xpush(w3);
|
|
state.xpush(w2);
|
|
state.xpush(w1);
|
|
break;
|
|
}
|
|
case Bytecodes.SWAP: {
|
|
var w1 = state.xpop();
|
|
var w2 = state.xpop();
|
|
state.xpush(w1);
|
|
state.xpush(w2);
|
|
break;
|
|
}
|
|
default:
|
|
Debug.unexpected("");
|
|
}
|
|
}
|
|
|
|
genArithmeticOp(result: Kind, opcode: Bytecodes, canTrap: boolean) {
|
|
var state = this.state;
|
|
var y = state.pop(result);
|
|
var x = state.pop(result);
|
|
if (canTrap) {
|
|
this.genDivideByZeroCheck(y);
|
|
}
|
|
var v;
|
|
// var isStrictFP = false; // TODO
|
|
switch(opcode) {
|
|
case Bytecodes.IADD: v = new IR.Binary(Operator.IADD, x, y); break;
|
|
case Bytecodes.LADD: v = new IR.JVMLongBinary(Operator.LADD, x, y); break;
|
|
case Bytecodes.FADD: v = new IR.Binary(Operator.FADD, x, y/*, isStrictFP*/); break;
|
|
case Bytecodes.DADD: v = new IR.Binary(Operator.DADD, x, y/*, isStrictFP*/); break;
|
|
case Bytecodes.ISUB: v = new IR.Binary(Operator.ISUB, x, y); break;
|
|
case Bytecodes.LSUB: v = new IR.JVMLongBinary(Operator.LSUB, x, y); break;
|
|
case Bytecodes.FSUB: v = new IR.Binary(Operator.FSUB, x, y/*, isStrictFP*/); break;
|
|
case Bytecodes.DSUB: v = new IR.Binary(Operator.DSUB, x, y/*, isStrictFP*/); break;
|
|
case Bytecodes.IMUL: v = new IR.Binary(Operator.IMUL, x, y); break;
|
|
case Bytecodes.LMUL: v = new IR.JVMLongBinary(Operator.LMUL, x, y); break;
|
|
case Bytecodes.FMUL: v = new IR.Binary(Operator.FMUL, x, y/*, isStrictFP*/); break;
|
|
case Bytecodes.DMUL: v = new IR.Binary(Operator.DMUL, x, y/*, isStrictFP*/); break;
|
|
case Bytecodes.IDIV: v = new IR.Binary(Operator.IDIV, x, y); break;
|
|
case Bytecodes.LDIV: v = new IR.JVMLongBinary(Operator.LDIV, x, y); break;
|
|
case Bytecodes.FDIV: v = new IR.Binary(Operator.FDIV, x, y/*, isStrictFP*/); break;
|
|
case Bytecodes.DDIV: v = new IR.Binary(Operator.DDIV, x, y/*, isStrictFP*/); break;
|
|
case Bytecodes.IREM: v = new IR.Binary(Operator.IREM, x, y); break;
|
|
case Bytecodes.LREM: v = new IR.JVMLongBinary(Operator.LREM, x, y); break;
|
|
case Bytecodes.FREM: v = new IR.Binary(Operator.FREM, x, y/*, isStrictFP*/); break;
|
|
case Bytecodes.DREM: v = new IR.Binary(Operator.DREM, x, y/*, isStrictFP*/); break;
|
|
default:
|
|
assert(false);
|
|
}
|
|
v = this.peepholeOptimizer.fold(v);
|
|
state.push(result, v);
|
|
}
|
|
|
|
genShiftOp(kind: Kind, opcode: Bytecodes) {
|
|
var state = this.state;
|
|
var s = state.ipop();
|
|
var x = state.pop(kind);
|
|
var v;
|
|
switch(opcode){
|
|
case Bytecodes.ISHL: v = new IR.Binary(Operator.LSH, x, s); break;
|
|
case Bytecodes.LSHL: v = new IR.JVMLongBinary(Operator.LSH, x, s); break;
|
|
case Bytecodes.ISHR: v = new IR.Binary(Operator.RSH, x, s); break;
|
|
case Bytecodes.LSHR: v = new IR.JVMLongBinary(Operator.RSH, x, s); break;
|
|
case Bytecodes.IUSHR: v = new IR.Binary(Operator.URSH, x, s); break;
|
|
case Bytecodes.LUSHR: v = new IR.JVMLongBinary(Operator.URSH, x, s); break;
|
|
default:
|
|
assert(false);
|
|
}
|
|
state.push(kind, v);
|
|
}
|
|
|
|
genLogicOp(kind: Kind, opcode: Bytecodes) {
|
|
var state = this.state;
|
|
var y = state.pop(kind);
|
|
var x = state.pop(kind);
|
|
var v;
|
|
switch(opcode){
|
|
case Bytecodes.IAND: v = new IR.Binary(Operator.AND, x, y); break;
|
|
case Bytecodes.LAND: v = new IR.JVMLongBinary(Operator.AND, x, y); break;
|
|
case Bytecodes.IOR: v = new IR.Binary(Operator.OR, x, y); break;
|
|
case Bytecodes.LOR: v = new IR.JVMLongBinary(Operator.OR, x, y); break;
|
|
case Bytecodes.IXOR: v = new IR.Binary(Operator.XOR, x, y); break;
|
|
case Bytecodes.LXOR: v = new IR.JVMLongBinary(Operator.XOR, x, y); break;
|
|
default:
|
|
assert(false);
|
|
}
|
|
state.push(kind, v);
|
|
}
|
|
|
|
genNegateOp(kind: Kind) {
|
|
var x = this.state.pop(kind);
|
|
var v;
|
|
switch (kind) {
|
|
case Kind.Int: v = new IR.Unary(Operator.INEG, x); break;
|
|
case Kind.Long: v = new IR.JVMLongUnary(Operator.LNEG, x); break;
|
|
case Kind.Float: v = new IR.Unary(Operator.FNEG, x); break;
|
|
case Kind.Double: v = new IR.Unary(Operator.DNEG, x); break;
|
|
default:
|
|
assert(false);
|
|
}
|
|
this.state.push(kind, v);
|
|
}
|
|
|
|
genNewInstance(cpi: number) {
|
|
var classInfo = this.lookupClass(cpi);
|
|
var jvmNew = new IR.JVMNew(this.region, this.state.store, classInfo);
|
|
this.recordStore(jvmNew);
|
|
this.state.apush(jvmNew);
|
|
}
|
|
|
|
genNewTypeArray(typeCode: number) {
|
|
var kind = arrayTypeCodeToKind(typeCode);
|
|
var length = this.state.ipop();
|
|
var result = new IR.JVMNewArray(this.region, this.state.store, kind, length);
|
|
this.recordStore(result);
|
|
this.state.apush(result);
|
|
}
|
|
|
|
genNewObjectArray(cpi: number) {
|
|
var classInfo = this.lookupClass(cpi);
|
|
var length = this.state.ipop();
|
|
var result = new IR.JVMNewObjectArray(this.region, this.state.store, classInfo, length);
|
|
this.recordStore(result);
|
|
this.state.apush(result);
|
|
}
|
|
|
|
genLoadConstant(cpi: number, state: State) {
|
|
var cp = this.methodInfo.classInfo.constant_pool;
|
|
var entry = cp[cpi];
|
|
switch (entry.tag) {
|
|
case TAGS.CONSTANT_Integer:
|
|
state.ipush(genConstant(entry.integer, Kind.Int));
|
|
return;
|
|
case TAGS.CONSTANT_Float:
|
|
state.fpush(genConstant(entry.float, Kind.Float));
|
|
return;
|
|
case TAGS.CONSTANT_Double:
|
|
state.dpush(genConstant(entry.double, Kind.Double));
|
|
return;
|
|
case 5: // TAGS.CONSTANT_Long
|
|
state.lpush(new IR.JVMLong(entry.lowBits, entry.highBits));
|
|
return;
|
|
case TAGS.CONSTANT_String:
|
|
entry = cp[entry.string_index];
|
|
return state.push(Kind.Reference, genConstant(entry.bytes, Kind.Reference));
|
|
|
|
default:
|
|
throw "Not done for: " + entry.tag;
|
|
}
|
|
}
|
|
|
|
genCheckCast(cpi: Bytecodes) {
|
|
var classInfo = this.lookupClass(cpi);
|
|
var object = this.state.peek();
|
|
var checkCast = new IR.JVMCheckCast(this.region, this.state.store, object, classInfo);
|
|
this.recordStore(checkCast);
|
|
}
|
|
|
|
genInstanceOf(cpi: Bytecodes) {
|
|
var classInfo = this.lookupClass(cpi);
|
|
var object = this.state.apop();
|
|
var instanceOf = new IR.JVMInstanceOf(this.region, this.state.store, object, classInfo);
|
|
this.recordStore(instanceOf);
|
|
this.state.push(Kind.Boolean, instanceOf);
|
|
}
|
|
|
|
genIncrement(stream: BytecodeStream) {
|
|
var index = stream.readLocalIndex();
|
|
var local = this.state.loadLocal(index);
|
|
var increment = genConstant(stream.readIncrement(), Kind.Int);
|
|
var value = new IR.Binary(Operator.IADD, local, increment);
|
|
value.kind = stackKind(local.kind);
|
|
this.state.storeLocal(index, value);
|
|
}
|
|
|
|
genConvert(from: Kind, to: Kind) {
|
|
var value = this.state.pop(from);
|
|
this.state.push(to, new IR.JVMConvert(from, to, value));
|
|
}
|
|
|
|
genIf(stream: BytecodeStream, predicate: IR.Binary) {
|
|
release || assert (!this.blockStopInfos);
|
|
var _if = new IR.If(this.region, predicate);
|
|
this.blockStopInfos = [new StopInfo(
|
|
<Control><any>new IR.Projection(_if, ProjectionType.TRUE),
|
|
this.blockMap.getBlock(stream.readBranchDest()),
|
|
this.state
|
|
), new StopInfo(
|
|
<Control><any>new IR.Projection(_if, ProjectionType.FALSE),
|
|
this.blockMap.getBlock(stream.nextBCI),
|
|
this.state
|
|
)];
|
|
}
|
|
|
|
genIfNull(stream: BytecodeStream, condition: Condition) {
|
|
this.state.apush(Null);
|
|
var y = this.state.apop();
|
|
var x = this.state.apop();
|
|
this.genIf(stream, new IR.Binary(conditionToOperator(condition), x, y));
|
|
}
|
|
|
|
genIfSame(stream: BytecodeStream, kind: Kind, condition: Condition) {
|
|
var y = this.state.pop(kind);
|
|
var x = this.state.pop(kind);
|
|
this.genIf(stream, new IR.Binary(conditionToOperator(condition), x, y));
|
|
}
|
|
|
|
genIfZero(stream: BytecodeStream, condition: Condition) {
|
|
this.state.ipush(genConstant(0, Kind.Int));
|
|
var y = this.state.ipop();
|
|
var x = this.state.ipop();
|
|
this.genIf(stream, new IR.Binary(conditionToOperator(condition), x, y));
|
|
}
|
|
|
|
genCompareOp(kind: Kind, isLessThan: boolean) {
|
|
var b = this.state.pop(kind);
|
|
var a = this.state.pop(kind);
|
|
var compare;
|
|
if (kind === Kind.Long) {
|
|
compare = new IR.JVMLongCompare(this.region, a, b);
|
|
} else {
|
|
compare = new IR.JVMFloatCompare(this.region, a, b, isLessThan);
|
|
}
|
|
this.state.ipush(compare);
|
|
}
|
|
|
|
genGoto(stream: BytecodeStream) {
|
|
release || assert (!this.blockStopInfos);
|
|
this.blockStopInfos = [new StopInfo(
|
|
this.region,
|
|
this.blockMap.getBlock(stream.readBranchDest()),
|
|
this.state
|
|
)];
|
|
}
|
|
|
|
genReturn(value: Value) {
|
|
if (value === null) {
|
|
value = Undefined;
|
|
}
|
|
this.methodReturnInfos.push(new ReturnInfo(
|
|
this.region,
|
|
this.state.store,
|
|
value
|
|
));
|
|
}
|
|
|
|
lookupClass(cpi: number): ClassInfo {
|
|
var classInfo = this.methodInfo.classInfo.resolve(cpi, false);
|
|
ArrayUtilities.pushUnique(this.referencedClasses, classInfo);
|
|
return classInfo;
|
|
}
|
|
|
|
lookupMethod(cpi: number, opcode: Bytecodes, isStatic: boolean): MethodInfo {
|
|
var methodInfo = this.methodInfo.classInfo.resolve(cpi, isStatic);
|
|
ArrayUtilities.pushUnique(this.referencedClasses, methodInfo.classInfo);
|
|
return methodInfo;
|
|
}
|
|
|
|
lookupField(cpi: number, opcode: Bytecodes, isStatic: boolean): FieldInfo {
|
|
var fieldInfo = this.methodInfo.classInfo.resolve(cpi, isStatic);
|
|
ArrayUtilities.pushUnique(this.referencedClasses, fieldInfo.classInfo);
|
|
return fieldInfo;
|
|
}
|
|
|
|
/**
|
|
* Marks the |node| as the active store node, with dependencies on all loads appearing after the
|
|
* previous active store node.
|
|
*/
|
|
recordStore(node: any) {
|
|
var state = this.state;
|
|
state.store = new IR.Projection(node, ProjectionType.STORE);
|
|
state.store.kind = Kind.Store;
|
|
node.loads = state.loads.slice(0);
|
|
state.loads.length = 0;
|
|
}
|
|
|
|
/**
|
|
* Keeps track of the current set of loads.
|
|
*/
|
|
recordLoad(node: Node): Value {
|
|
var state = this.state;
|
|
state.loads.push(node);
|
|
return node;
|
|
}
|
|
|
|
genDivideByZeroCheck(value: Value) {
|
|
var checkArithmetic = new IR.JVMCheckArithmetic(this.region, this.state.store, value);
|
|
this.recordStore(checkArithmetic);
|
|
}
|
|
|
|
genThrow(bci: number) {
|
|
var object = this.state.peek();
|
|
var _throw = new IR.JVMThrow(this.region, this.state.store, object);
|
|
this.recordStore(_throw);
|
|
this.methodReturnInfos.push(new ReturnInfo(
|
|
this.region,
|
|
this.state.store,
|
|
Undefined
|
|
));
|
|
}
|
|
|
|
genInvoke(methodInfo: MethodInfo, opcode: Bytecodes, currentBCI: number, nextBCI: number) {
|
|
var calleeCanYield = YieldReason.Virtual;
|
|
if (isStaticallyBound(opcode, methodInfo)) {
|
|
calleeCanYield = canYield(methodInfo);
|
|
}
|
|
var signature = SignatureDescriptor.makeSignatureDescriptor(methodInfo.signature);
|
|
var types = signature.typeDescriptors;
|
|
var args: Value [] = [];
|
|
for (var i = types.length - 1; i > 0; i--) {
|
|
args.unshift(this.state.pop(types[i].kind));
|
|
}
|
|
var object = null;
|
|
if (opcode !== Bytecodes.INVOKESTATIC) {
|
|
object = this.state.pop(Kind.Reference);
|
|
}
|
|
|
|
//switch (methodInfo.implKey) {
|
|
// // There's no reason to call the object initializer.
|
|
// case "java/lang/Object.<init>.()V":
|
|
// return;
|
|
//}
|
|
|
|
var state;
|
|
if (calleeCanYield) {
|
|
// Only save the state if the callee can yield.
|
|
state = this.state.clone(currentBCI, nextBCI);
|
|
}
|
|
counter && counter.count("Yield Code: " + YieldReason[calleeCanYield] + " " + methodInfo.implKey);
|
|
counter && counter.count("Yield Code: " + YieldReason[calleeCanYield]);
|
|
var call = new IR.JVMInvoke(this.region, this.state.store, state, opcode, object, methodInfo, args);
|
|
this.recordStore(call);
|
|
if (types[0].kind !== Kind.Void) {
|
|
this.state.push(types[0].kind, call);
|
|
}
|
|
}
|
|
|
|
genStoreIndexed(kind: Kind) {
|
|
var value = this.state.pop(stackKind(kind));
|
|
var index = this.state.ipop();
|
|
var array = this.state.apop();
|
|
var arrayStore = new IR.JVMStoreIndexed(this.region, this.state.store, kind, array, index, value);
|
|
this.recordStore(arrayStore);
|
|
}
|
|
|
|
genLoadIndexed(kind: Kind) {
|
|
var index = this.state.ipop();
|
|
var array = this.state.apop();
|
|
var arrayLoad = new IR.JVMLoadIndexed(this.region, this.state.store, kind, array, index);
|
|
this.recordLoad(arrayLoad);
|
|
this.state.push(kind, arrayLoad);
|
|
}
|
|
|
|
genArrayLength() {
|
|
var array = this.state.apop();
|
|
var getProperty = new IR.GetProperty(this.region, this.state.store, array, new Constant('length'));
|
|
this.recordLoad(getProperty);
|
|
this.state.ipush(getProperty);
|
|
}
|
|
|
|
genClass(classInfo: ClassInfo): Value {
|
|
ArrayUtilities.pushUnique(this.referencedClasses, classInfo);
|
|
return new IR.JVMClass(classInfo);
|
|
}
|
|
|
|
genGetField(fieldInfo: FieldInfo, isStatic: boolean) {
|
|
var signature = TypeDescriptor.makeTypeDescriptor(fieldInfo.signature);
|
|
var object = isStatic ? null : this.state.apop();
|
|
var getField = new IR.JVMGetField(this.region, this.state.store, object, fieldInfo);
|
|
this.recordLoad(getField);
|
|
this.state.push(signature.kind, getField);
|
|
}
|
|
|
|
genPutField(fieldInfo: FieldInfo, isStatic: boolean) {
|
|
var signature = TypeDescriptor.makeTypeDescriptor(fieldInfo.signature);
|
|
var value = this.state.pop(signature.kind);
|
|
var object = isStatic ? null : this.state.apop();
|
|
var putField = new IR.JVMPutField(this.region, this.state.store, object, fieldInfo, value);
|
|
this.recordStore(putField);
|
|
}
|
|
|
|
processBytecode(stream: BytecodeStream, state: State) {
|
|
var cpi: number;
|
|
var opcode: Bytecodes = stream.currentBC();
|
|
writer && writer.enter("State Before: " + Bytecodes[opcode].padRight(" ", 12) + " " + state.toString());
|
|
switch (opcode) {
|
|
case Bytecodes.NOP : break;
|
|
case Bytecodes.ACONST_NULL : state.apush(genConstant(null, Kind.Reference)); break;
|
|
case Bytecodes.ICONST_M1 : state.ipush(genConstant(-1, Kind.Int)); break;
|
|
case Bytecodes.ICONST_0 : state.ipush(genConstant(0, Kind.Int)); break;
|
|
case Bytecodes.ICONST_1 : state.ipush(genConstant(1, Kind.Int)); break;
|
|
case Bytecodes.ICONST_2 : state.ipush(genConstant(2, Kind.Int)); break;
|
|
case Bytecodes.ICONST_3 : state.ipush(genConstant(3, Kind.Int)); break;
|
|
case Bytecodes.ICONST_4 : state.ipush(genConstant(4, Kind.Int)); break;
|
|
case Bytecodes.ICONST_5 : state.ipush(genConstant(5, Kind.Int)); break;
|
|
case Bytecodes.LCONST_0 : state.lpush(genConstant(0, Kind.Long)); break;
|
|
case Bytecodes.LCONST_1 : state.lpush(genConstant(1, Kind.Long)); break;
|
|
case Bytecodes.FCONST_0 : state.fpush(genConstant(0, Kind.Float)); break;
|
|
case Bytecodes.FCONST_1 : state.fpush(genConstant(1, Kind.Float)); break;
|
|
case Bytecodes.FCONST_2 : state.fpush(genConstant(2, Kind.Float)); break;
|
|
case Bytecodes.DCONST_0 : state.dpush(genConstant(0, Kind.Double)); break;
|
|
case Bytecodes.DCONST_1 : state.dpush(genConstant(1, Kind.Double)); break;
|
|
case Bytecodes.BIPUSH : state.ipush(genConstant(stream.readByte(), Kind.Int)); break;
|
|
case Bytecodes.SIPUSH : state.ipush(genConstant(stream.readShort(), Kind.Int)); break;
|
|
case Bytecodes.LDC :
|
|
case Bytecodes.LDC_W :
|
|
case Bytecodes.LDC2_W : this.genLoadConstant(stream.readCPI(), state); break;
|
|
case Bytecodes.ILOAD : this.loadLocal(stream.readLocalIndex(), Kind.Int); break;
|
|
case Bytecodes.LLOAD : this.loadLocal(stream.readLocalIndex(), Kind.Long); break;
|
|
case Bytecodes.FLOAD : this.loadLocal(stream.readLocalIndex(), Kind.Float); break;
|
|
case Bytecodes.DLOAD : this.loadLocal(stream.readLocalIndex(), Kind.Double); break;
|
|
case Bytecodes.ALOAD : this.loadLocal(stream.readLocalIndex(), Kind.Reference); break;
|
|
case Bytecodes.ILOAD_0 :
|
|
case Bytecodes.ILOAD_1 :
|
|
case Bytecodes.ILOAD_2 :
|
|
case Bytecodes.ILOAD_3 : this.loadLocal(opcode - Bytecodes.ILOAD_0, Kind.Int); break;
|
|
case Bytecodes.LLOAD_0 :
|
|
case Bytecodes.LLOAD_1 :
|
|
case Bytecodes.LLOAD_2 :
|
|
case Bytecodes.LLOAD_3 : this.loadLocal(opcode - Bytecodes.LLOAD_0, Kind.Long); break;
|
|
case Bytecodes.FLOAD_0 :
|
|
case Bytecodes.FLOAD_1 :
|
|
case Bytecodes.FLOAD_2 :
|
|
case Bytecodes.FLOAD_3 : this.loadLocal(opcode - Bytecodes.FLOAD_0, Kind.Float); break;
|
|
case Bytecodes.DLOAD_0 :
|
|
case Bytecodes.DLOAD_1 :
|
|
case Bytecodes.DLOAD_2 :
|
|
case Bytecodes.DLOAD_3 : this.loadLocal(opcode - Bytecodes.DLOAD_0, Kind.Double); break;
|
|
case Bytecodes.ALOAD_0 :
|
|
case Bytecodes.ALOAD_1 :
|
|
case Bytecodes.ALOAD_2 :
|
|
case Bytecodes.ALOAD_3 : this.loadLocal(opcode - Bytecodes.ALOAD_0, Kind.Reference); break;
|
|
|
|
case Bytecodes.IALOAD : this.genLoadIndexed(Kind.Int); break;
|
|
case Bytecodes.LALOAD : this.genLoadIndexed(Kind.Long); break;
|
|
case Bytecodes.FALOAD : this.genLoadIndexed(Kind.Float); break;
|
|
case Bytecodes.DALOAD : this.genLoadIndexed(Kind.Double); break;
|
|
case Bytecodes.AALOAD : this.genLoadIndexed(Kind.Reference); break;
|
|
case Bytecodes.BALOAD : this.genLoadIndexed(Kind.Byte); break;
|
|
case Bytecodes.CALOAD : this.genLoadIndexed(Kind.Char); break;
|
|
case Bytecodes.SALOAD : this.genLoadIndexed(Kind.Short); break;
|
|
case Bytecodes.ISTORE : this.storeLocal(Kind.Int, stream.readLocalIndex()); break;
|
|
case Bytecodes.LSTORE : this.storeLocal(Kind.Long, stream.readLocalIndex()); break;
|
|
case Bytecodes.FSTORE : this.storeLocal(Kind.Float, stream.readLocalIndex()); break;
|
|
case Bytecodes.DSTORE : this.storeLocal(Kind.Double, stream.readLocalIndex()); break;
|
|
case Bytecodes.ASTORE : this.storeLocal(Kind.Reference, stream.readLocalIndex()); break;
|
|
case Bytecodes.ISTORE_0 :
|
|
case Bytecodes.ISTORE_1 :
|
|
case Bytecodes.ISTORE_2 :
|
|
case Bytecodes.ISTORE_3 : this.storeLocal(Kind.Int, opcode - Bytecodes.ISTORE_0); break;
|
|
case Bytecodes.LSTORE_0 :
|
|
case Bytecodes.LSTORE_1 :
|
|
case Bytecodes.LSTORE_2 :
|
|
case Bytecodes.LSTORE_3 : this.storeLocal(Kind.Long, opcode - Bytecodes.LSTORE_0); break;
|
|
case Bytecodes.FSTORE_0 :
|
|
case Bytecodes.FSTORE_1 :
|
|
case Bytecodes.FSTORE_2 :
|
|
case Bytecodes.FSTORE_3 : this.storeLocal(Kind.Float, opcode - Bytecodes.FSTORE_0); break;
|
|
case Bytecodes.DSTORE_0 :
|
|
case Bytecodes.DSTORE_1 :
|
|
case Bytecodes.DSTORE_2 :
|
|
case Bytecodes.DSTORE_3 : this.storeLocal(Kind.Double, opcode - Bytecodes.DSTORE_0); break;
|
|
case Bytecodes.ASTORE_0 :
|
|
case Bytecodes.ASTORE_1 :
|
|
case Bytecodes.ASTORE_2 :
|
|
case Bytecodes.ASTORE_3 : this.storeLocal(Kind.Reference, opcode - Bytecodes.ASTORE_0); break;
|
|
|
|
|
|
case Bytecodes.IASTORE : this.genStoreIndexed(Kind.Int); break;
|
|
case Bytecodes.LASTORE : this.genStoreIndexed(Kind.Long); break;
|
|
case Bytecodes.FASTORE : this.genStoreIndexed(Kind.Float); break;
|
|
case Bytecodes.DASTORE : this.genStoreIndexed(Kind.Double); break;
|
|
case Bytecodes.AASTORE : this.genStoreIndexed(Kind.Reference); break;
|
|
case Bytecodes.BASTORE : this.genStoreIndexed(Kind.Byte); break;
|
|
case Bytecodes.CASTORE : this.genStoreIndexed(Kind.Char); break;
|
|
case Bytecodes.SASTORE : this.genStoreIndexed(Kind.Short); break;
|
|
|
|
case Bytecodes.POP :
|
|
case Bytecodes.POP2 :
|
|
case Bytecodes.DUP :
|
|
case Bytecodes.DUP_X1 :
|
|
case Bytecodes.DUP_X2 :
|
|
case Bytecodes.DUP2 :
|
|
case Bytecodes.DUP2_X1 :
|
|
case Bytecodes.DUP2_X2 :
|
|
case Bytecodes.SWAP : this.stackOp(opcode); break;
|
|
|
|
case Bytecodes.IADD :
|
|
case Bytecodes.ISUB :
|
|
case Bytecodes.IMUL : this.genArithmeticOp(Kind.Int, opcode, false); break;
|
|
case Bytecodes.IDIV :
|
|
case Bytecodes.IREM : this.genArithmeticOp(Kind.Int, opcode, true); break;
|
|
case Bytecodes.LADD :
|
|
case Bytecodes.LSUB :
|
|
case Bytecodes.LMUL : this.genArithmeticOp(Kind.Long, opcode, false); break;
|
|
case Bytecodes.LDIV :
|
|
case Bytecodes.LREM : this.genArithmeticOp(Kind.Long, opcode, true); break;
|
|
case Bytecodes.FADD :
|
|
case Bytecodes.FSUB :
|
|
case Bytecodes.FMUL :
|
|
case Bytecodes.FDIV :
|
|
case Bytecodes.FREM : this.genArithmeticOp(Kind.Float, opcode, false); break;
|
|
case Bytecodes.DADD :
|
|
case Bytecodes.DSUB :
|
|
case Bytecodes.DMUL :
|
|
case Bytecodes.DDIV :
|
|
case Bytecodes.DREM : this.genArithmeticOp(Kind.Double, opcode, false); break;
|
|
case Bytecodes.INEG : this.genNegateOp(Kind.Int); break;
|
|
case Bytecodes.LNEG : this.genNegateOp(Kind.Long); break;
|
|
case Bytecodes.FNEG : this.genNegateOp(Kind.Float); break;
|
|
case Bytecodes.DNEG : this.genNegateOp(Kind.Double); break;
|
|
case Bytecodes.ISHL :
|
|
case Bytecodes.ISHR :
|
|
case Bytecodes.IUSHR : this.genShiftOp(Kind.Int, opcode); break;
|
|
case Bytecodes.IAND :
|
|
case Bytecodes.IOR :
|
|
case Bytecodes.IXOR : this.genLogicOp(Kind.Int, opcode); break;
|
|
case Bytecodes.LSHL :
|
|
case Bytecodes.LSHR :
|
|
case Bytecodes.LUSHR : this.genShiftOp(Kind.Long, opcode); break;
|
|
case Bytecodes.LAND :
|
|
case Bytecodes.LOR :
|
|
case Bytecodes.LXOR : this.genLogicOp(Kind.Long, opcode); break;
|
|
case Bytecodes.IINC : this.genIncrement(stream); break;
|
|
case Bytecodes.I2L : this.genConvert(Kind.Int, Kind.Long); break;
|
|
case Bytecodes.I2F : this.genConvert(Kind.Int, Kind.Float); break;
|
|
case Bytecodes.I2D : this.genConvert(Kind.Int, Kind.Double); break;
|
|
case Bytecodes.L2I : this.genConvert(Kind.Long, Kind.Int); break;
|
|
case Bytecodes.L2F : this.genConvert(Kind.Long, Kind.Float); break;
|
|
case Bytecodes.L2D : this.genConvert(Kind.Long, Kind.Double); break;
|
|
case Bytecodes.F2I : this.genConvert(Kind.Float, Kind.Int); break;
|
|
case Bytecodes.F2L : this.genConvert(Kind.Float, Kind.Long); break;
|
|
case Bytecodes.F2D : this.genConvert(Kind.Float, Kind.Double); break;
|
|
case Bytecodes.D2I : this.genConvert(Kind.Double, Kind.Int); break;
|
|
case Bytecodes.D2L : this.genConvert(Kind.Double, Kind.Long); break;
|
|
case Bytecodes.D2F : this.genConvert(Kind.Double, Kind.Float); break;
|
|
case Bytecodes.I2B : this.genConvert(Kind.Int, Kind.Byte); break;
|
|
case Bytecodes.I2C : this.genConvert(Kind.Int, Kind.Char); break;
|
|
case Bytecodes.I2S : this.genConvert(Kind.Int, Kind.Short); break;
|
|
|
|
case Bytecodes.LCMP : this.genCompareOp(Kind.Long, false); break;
|
|
case Bytecodes.FCMPL : this.genCompareOp(Kind.Float, true); break;
|
|
case Bytecodes.FCMPG : this.genCompareOp(Kind.Float, false); break;
|
|
case Bytecodes.DCMPL : this.genCompareOp(Kind.Double, true); break;
|
|
case Bytecodes.DCMPG : this.genCompareOp(Kind.Double, false); break;
|
|
case Bytecodes.IFEQ : this.genIfZero(stream, Condition.EQ); break;
|
|
case Bytecodes.IFNE : this.genIfZero(stream, Condition.NE); break;
|
|
case Bytecodes.IFLT : this.genIfZero(stream, Condition.LT); break;
|
|
case Bytecodes.IFGE : this.genIfZero(stream, Condition.GE); break;
|
|
case Bytecodes.IFGT : this.genIfZero(stream, Condition.GT); break;
|
|
case Bytecodes.IFLE : this.genIfZero(stream, Condition.LE); break;
|
|
case Bytecodes.IF_ICMPEQ : this.genIfSame(stream, Kind.Int, Condition.EQ); break;
|
|
case Bytecodes.IF_ICMPNE : this.genIfSame(stream, Kind.Int, Condition.NE); break;
|
|
case Bytecodes.IF_ICMPLT : this.genIfSame(stream, Kind.Int, Condition.LT); break;
|
|
case Bytecodes.IF_ICMPGE : this.genIfSame(stream, Kind.Int, Condition.GE); break;
|
|
case Bytecodes.IF_ICMPGT : this.genIfSame(stream, Kind.Int, Condition.GT); break;
|
|
case Bytecodes.IF_ICMPLE : this.genIfSame(stream, Kind.Int, Condition.LE); break;
|
|
case Bytecodes.IF_ACMPEQ : this.genIfSame(stream, Kind.Reference, Condition.EQ); break;
|
|
case Bytecodes.IF_ACMPNE : this.genIfSame(stream, Kind.Reference, Condition.NE); break;
|
|
case Bytecodes.GOTO : this.genGoto(stream); break;
|
|
/*
|
|
case Bytecodes.JSR : genJsr(stream.readBranchDest()); break;
|
|
case Bytecodes.RET : genRet(stream.readLocalIndex()); break;
|
|
case Bytecodes.TABLESWITCH : genTableswitch(); break;
|
|
case Bytecodes.LOOKUPSWITCH : genLookupswitch(); break;
|
|
*/
|
|
case Bytecodes.IRETURN : this.genReturn(state.ipop()); break;
|
|
case Bytecodes.LRETURN : this.genReturn(state.lpop()); break;
|
|
case Bytecodes.FRETURN : this.genReturn(state.fpop()); break;
|
|
case Bytecodes.DRETURN : this.genReturn(state.dpop()); break;
|
|
case Bytecodes.ARETURN : this.genReturn(state.apop()); break;
|
|
case Bytecodes.RETURN : this.genReturn(null); break;
|
|
case Bytecodes.GETSTATIC : cpi = stream.readCPI(); this.genGetField(this.lookupField(cpi, opcode, true), true); break;
|
|
case Bytecodes.PUTSTATIC : cpi = stream.readCPI(); this.genPutField(this.lookupField(cpi, opcode, true), true); break;
|
|
case Bytecodes.GETFIELD : cpi = stream.readCPI(); this.genGetField(this.lookupField(cpi, opcode, false), false); break;
|
|
case Bytecodes.PUTFIELD : cpi = stream.readCPI(); this.genPutField(this.lookupField(cpi, opcode, false), false); break;
|
|
case Bytecodes.INVOKEVIRTUAL : cpi = stream.readCPI(); this.genInvoke(this.lookupMethod(cpi, opcode, false), opcode, stream.currentBCI, stream.nextBCI); break;
|
|
case Bytecodes.INVOKESPECIAL : cpi = stream.readCPI(); this.genInvoke(this.lookupMethod(cpi, opcode, false), opcode, stream.currentBCI, stream.nextBCI); break;
|
|
case Bytecodes.INVOKESTATIC : cpi = stream.readCPI(); this.genInvoke(this.lookupMethod(cpi, opcode, true), opcode, stream.currentBCI, stream.nextBCI); break;
|
|
case Bytecodes.INVOKEINTERFACE: cpi = stream.readCPI(); this.genInvoke(this.lookupMethod(cpi, opcode, false), opcode, stream.currentBCI, stream.nextBCI); break;
|
|
case Bytecodes.NEW : this.genNewInstance(stream.readCPI()); break;
|
|
case Bytecodes.NEWARRAY : this.genNewTypeArray(stream.readLocalIndex()); break;
|
|
case Bytecodes.ANEWARRAY : this.genNewObjectArray(stream.readCPI()); break;
|
|
case Bytecodes.ARRAYLENGTH : this.genArrayLength(); break;
|
|
case Bytecodes.ATHROW : this.genThrow(stream.currentBCI); break;
|
|
case Bytecodes.CHECKCAST : this.genCheckCast(stream.readCPI()); break;
|
|
case Bytecodes.INSTANCEOF : this.genInstanceOf(stream.readCPI()); break;
|
|
/*
|
|
case Bytecodes.MONITORENTER : genMonitorEnter(state.apop()); break;
|
|
case Bytecodes.MONITOREXIT : genMonitorExit(state.apop()); break;
|
|
case Bytecodes.MULTIANEWARRAY : genNewMultiArray(stream.readCPI()); break;
|
|
*/
|
|
case Bytecodes.IFNULL : this.genIfNull(stream, Condition.EQ); break;
|
|
case Bytecodes.IFNONNULL : this.genIfNull(stream, Condition.NE); break;
|
|
/*
|
|
case Bytecodes.GOTO_W : genGoto(stream.readFarBranchDest()); break;
|
|
case Bytecodes.JSR_W : genJsr(stream.readFarBranchDest()); break;
|
|
case Bytecodes.BREAKPOINT:
|
|
throw new CiBailout("concurrent setting of breakpoint");
|
|
default:
|
|
throw new CiBailout("Unsupported opcode " + opcode + " (" + nameOf(opcode) + ") [bci=" + bci + "]");
|
|
}
|
|
*/
|
|
default:
|
|
throw new Error("Not Implemented " + Bytecodes[opcode]);
|
|
}
|
|
writer && writer.leave("State After: " + Bytecodes[opcode].padRight(" ", 12) + " " + state.toString());
|
|
writer && writer.writeLn("");
|
|
}
|
|
}
|
|
|
|
export function optimizerCompileMethod(methodInfo: MethodInfo, target: CompilationTarget): CompiledMethodInfo {
|
|
if (!methodInfo.code) {
|
|
throw new Error("Method: " + methodInfo.implKey + " has no code.");
|
|
}
|
|
if (methodInfo.exception_table.length) {
|
|
throw new Error("Method: " + methodInfo.implKey + " has exception handlers.");
|
|
}
|
|
var builder = new Builder(methodInfo, target);
|
|
var fn;
|
|
var compilation = builder.build();
|
|
var args = [];
|
|
for (var i = 0; i < builder.parameters.length; i++) {
|
|
var parameter = builder.parameters[i];
|
|
args.push(parameter.name);
|
|
}
|
|
var body = compilation.body;
|
|
return new CompiledMethodInfo(args, body, builder.referencedClasses);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|