1024 lines
28 KiB
Plaintext
1024 lines
28 KiB
Plaintext
|
package slangc.api;
|
||
|
|
||
|
import slang.data.List;
|
||
|
import slangc.api.BytecodeHeap;
|
||
|
import slangc.api.TypeOption;
|
||
|
import slangc.bytecode.FieldSignature;
|
||
|
import slangc.bytecode.MethodSignature;
|
||
|
import slangc.bytecode.TypeSignature;
|
||
|
import slangc.model.BuiltinTypeModel;
|
||
|
import slangc.model.TypeModel;
|
||
|
import slangc.model.UserTypeModel;
|
||
|
import slangc.parser.AnnotationType;
|
||
|
import slangc.parser.ErrorType;
|
||
|
import slangc.parser.LocationAnnotation;
|
||
|
import slangc.parser.Node;
|
||
|
|
||
|
public class BytecodeInstructionWriter {
|
||
|
public final BytecodeTarget target;
|
||
|
public final TypeModel owner;
|
||
|
public final BytecodeHeap.ObjectLike method;
|
||
|
private BytecodeHeap.ArrayInt32 instructions;
|
||
|
private List<Node> sourceStack = new List<Node>();
|
||
|
private List<Label> breakStack = new List<Label>();
|
||
|
private List<Label> continueStack = new List<Label>();
|
||
|
private List<Label> labels = new List<Label>();
|
||
|
private List<Location> locations = new List<Location>();
|
||
|
private List<Reference> references = new List<Reference>();
|
||
|
private int currentStackSize = 0;
|
||
|
private int maxStackSize = 0;
|
||
|
|
||
|
public static class Reference {
|
||
|
public int referenceLocation;
|
||
|
public Label targetLabel;
|
||
|
public boolean isLinked;
|
||
|
//public boolean compressed;
|
||
|
}
|
||
|
|
||
|
public static class Location {
|
||
|
public int instruction;
|
||
|
public int line;
|
||
|
public int character;
|
||
|
|
||
|
public Location(int instruction, int line, int character) {
|
||
|
super();
|
||
|
this.instruction = instruction;
|
||
|
this.line = line;
|
||
|
this.character = character;
|
||
|
}
|
||
|
|
||
|
public void genLocationData(BytecodeHeap.ArrayInt16 dbglist, boolean b) {
|
||
|
if (dbglist.elementsLength() == 0 || dbglist.getElement(dbglist.elementsLength() - 1) != line) {
|
||
|
dbglist.appendElement((short) instruction);
|
||
|
dbglist.appendElement((short) line);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
}
|
||
|
|
||
|
public static class Label {
|
||
|
public String name;
|
||
|
public int location = -1;
|
||
|
public int stackSize = -1;
|
||
|
|
||
|
public Label(String name) {
|
||
|
this.name = name;
|
||
|
}
|
||
|
|
||
|
public byte labelTypeCode() {
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
public void genLabelData(BytecodeHeap.ArrayInt32 lbllist, boolean verbose) {
|
||
|
if (verbose) {
|
||
|
lbllist.appendElement(labelTypeCode());
|
||
|
int nameidx = lbllist.getHeap().getConstString(name).recordIndex;
|
||
|
lbllist.appendElement(nameidx);
|
||
|
//lbllist.appendElement((short) (nameidx >> 16));
|
||
|
lbllist.appendElement(location);
|
||
|
//lbllist.appendElement((short) (location >> 16));
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public static class TryLabel extends Label {
|
||
|
Label begin;
|
||
|
Label end;
|
||
|
public TryLabel(String name, Label begin, Label end) {
|
||
|
super(name);
|
||
|
this.begin = begin;
|
||
|
this.end = end;
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public byte labelTypeCode() {
|
||
|
return 2;
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public void genLabelData(BytecodeHeap.ArrayInt32 lbllist, boolean verbose) {
|
||
|
lbllist.appendElement(labelTypeCode());
|
||
|
int nameidx = lbllist.getHeap().getConstString(name).recordIndex;
|
||
|
lbllist.appendElement(nameidx);
|
||
|
//lbllist.appendElement((short) (nameidx >> 16));
|
||
|
lbllist.appendElement(location);
|
||
|
//lbllist.appendElement((short) (location >> 16));
|
||
|
lbllist.appendElement(begin.location);
|
||
|
//lbllist.appendElement((short) (begin.location >> 16));
|
||
|
lbllist.appendElement(end.location);
|
||
|
//lbllist.appendElement((short) (end.location >> 16));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public static class CatchLabel extends TryLabel {
|
||
|
TypeSignature type;
|
||
|
|
||
|
public CatchLabel(String name, Label begin, Label end, TypeSignature type) {
|
||
|
super(name, begin, end);
|
||
|
this.type = type;
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public byte labelTypeCode() {
|
||
|
return 3;
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public void genLabelData(BytecodeHeap.ArrayInt32 lbllist, boolean verbose) {
|
||
|
super.genLabelData(lbllist, true); // TODO: Should this be true
|
||
|
int tidx = lbllist.getHeap().getTypeSignature(type).recordIndex;
|
||
|
lbllist.appendElement(tidx);
|
||
|
//lbllist.appendElement((short) (tidx >> 16));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public static class FinallyLabel extends TryLabel {
|
||
|
|
||
|
public FinallyLabel(String name, Label begin, Label end) {
|
||
|
super(name, begin, end);
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public byte labelTypeCode() {
|
||
|
return 4;
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public void genLabelData(BytecodeHeap.ArrayInt32 lbllist, boolean verbose) {
|
||
|
super.genLabelData(lbllist, true);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
public static class EndTryLabel extends Label {
|
||
|
|
||
|
public EndTryLabel(String name) {
|
||
|
super(name);
|
||
|
// TODO Auto-generated constructor stub
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public byte labelTypeCode() {
|
||
|
return 5;
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public void genLabelData(BytecodeHeap.ArrayInt32 lbllist, boolean verbose) {
|
||
|
super.genLabelData(lbllist, true);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public BytecodeInstructionWriter(BytecodeTarget target, TypeModel owner, BytecodeHeap.ObjectLike method) {
|
||
|
super();
|
||
|
this.target = target;
|
||
|
this.owner = owner;
|
||
|
this.method = method;
|
||
|
}
|
||
|
|
||
|
public boolean hasLabel(String uniqueName) {
|
||
|
return findLabel(uniqueName) != null;
|
||
|
}
|
||
|
|
||
|
public Label findLabel(String uniqueName) {
|
||
|
for (int i = 0; i < labels.count(); i++) {
|
||
|
if (labels.get(i).name.equals(uniqueName)) {
|
||
|
return labels.get(i);
|
||
|
}
|
||
|
}
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
/* Returns a name like "startOfLoop4" using a base name like "startOfLoop". */
|
||
|
public String nextLabelName(String baseName) {
|
||
|
int x = 1;
|
||
|
while (hasLabel(baseName + x)) {
|
||
|
x++;
|
||
|
}
|
||
|
return baseName + x;
|
||
|
}
|
||
|
|
||
|
public Label newLabel(String baseName) {
|
||
|
Label result = new Label(nextLabelName(baseName));
|
||
|
labels.append(result);
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
public CatchLabel newCatchLabel(String baseName, Label begin, Label end, TypeSignature catchType) {
|
||
|
CatchLabel result = new CatchLabel(nextLabelName(baseName), begin, end, catchType);
|
||
|
labels.append(result);
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
public FinallyLabel newFinallyLabel(String baseName, Label begin, Label end) {
|
||
|
FinallyLabel result = new FinallyLabel(nextLabelName(baseName), begin, end);
|
||
|
labels.append(result);
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
public void labelHere(Label l) {
|
||
|
l.location = currentLocation();
|
||
|
}
|
||
|
|
||
|
public StackType stackType(TypeSignature type) {
|
||
|
return target.stackType(type); // Moved here so they can be cached
|
||
|
}
|
||
|
|
||
|
/*public StackType stackType(TypeSignature type) {
|
||
|
if (type.mappableEquals(TypeSignature.VOID)) {
|
||
|
return StackType.VOID;
|
||
|
} /X*else if (type.mappableEquals(getTarget().getSystem().getDefaultBooleanType().getTypeSignature())) {
|
||
|
return StackType.BIT;
|
||
|
} else if (type.mappableEquals(getTarget().getSystem().getDefaultFloat32Type().getTypeSignature())) {
|
||
|
return StackType.FLOAT32;
|
||
|
} else if (type.mappableEquals(getTarget().getSystem().getDefaultFloat64Type().getTypeSignature())) {
|
||
|
return StackType.FLOAT64;
|
||
|
} else if (type.mappableEquals(getTarget().getSystem().getDefaultInt64Type().getTypeSignature())) {
|
||
|
return StackType.INT64;
|
||
|
} else if (getTarget().getSystem().matchesTypeOption(TypeOption.Kind.DUCK, type)) {
|
||
|
return StackType.DUCK;
|
||
|
}*X/ else if (getTarget().getSystem().getType(type) instanceof BuiltinTypeModel) {
|
||
|
for (int i = 0; i < StackType.lookupCount(StackType.class); i++) {
|
||
|
if (getTarget().getSystem().matchesTypeOption((TypeOption.Kind)TypeOption.Kind.lookup(TypeOption.Kind.class, i), type)) {
|
||
|
return (StackType)StackType.lookup(StackType.class, i);
|
||
|
}
|
||
|
}
|
||
|
return StackType.INT32;
|
||
|
} else {
|
||
|
for (int i = 0; i < StackType.lookupCount(StackType.class); i++) {
|
||
|
if (getTarget().getSystem().matchesTypeOption((TypeOption.Kind)TypeOption.Kind.lookup(TypeOption.Kind.class, i), type)) {
|
||
|
return (StackType)StackType.lookup(StackType.class, i);
|
||
|
}
|
||
|
}
|
||
|
return StackType.OBJECT;
|
||
|
}
|
||
|
}*/
|
||
|
|
||
|
public StackType stackType(BytecodeHeap.StandardClass type) {
|
||
|
switch (type) {
|
||
|
default:
|
||
|
return StackType.OBJECT;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public void vpush(TypeSignature type) {
|
||
|
vpush(stackType(type));
|
||
|
}
|
||
|
|
||
|
public void vpop(TypeSignature type) {
|
||
|
vpop(stackType(type));
|
||
|
}
|
||
|
|
||
|
public void vpush(BytecodeHeap.StandardClass type) {
|
||
|
vpush(stackType(type));
|
||
|
}
|
||
|
|
||
|
public void vpop(BytecodeHeap.StandardClass type) {
|
||
|
vpop(stackType(type));
|
||
|
}
|
||
|
|
||
|
public void vpush(StackType type) {
|
||
|
if (type == StackType.VOID) {
|
||
|
return;
|
||
|
}
|
||
|
currentStackSize++;
|
||
|
if (currentStackSize > maxStackSize) {
|
||
|
maxStackSize = currentStackSize;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public void vpop(StackType type) {
|
||
|
if (type == StackType.VOID) {
|
||
|
return;
|
||
|
}
|
||
|
currentStackSize--;
|
||
|
if (currentStackSize < 0) {
|
||
|
genError("Stack underflow");
|
||
|
Log.line("WARNING: Stack underflow");
|
||
|
currentStackSize = 0;
|
||
|
//throw new Error("Stack underflow");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public int currentLocation() {
|
||
|
if (instructions == null) {
|
||
|
return 0;
|
||
|
} else {
|
||
|
return instructions.elementsLength();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public BytecodeHeap.ArrayInt32 writableInstructions() {
|
||
|
if (instructions == null) {
|
||
|
instructions = target.heap.newInstructions(0);
|
||
|
method.setElement(BytecodeHeap.MethodFields.BYTECODE.value, instructions);
|
||
|
}
|
||
|
return instructions;
|
||
|
}
|
||
|
|
||
|
//public void writeRaw16(short data) {
|
||
|
// writableInstructions().appendElement(data);
|
||
|
//}
|
||
|
|
||
|
public void writeRaw32(int data, boolean canBeBig) {
|
||
|
if (!canBeBig && ((data & 0xFFFF) != data)) throw new Error("This data (" + data + ") isn't allowed to be this big!");
|
||
|
writableInstructions().appendElement(data);
|
||
|
//writeRaw16((short) data);
|
||
|
//writeRaw16((short) (data >> 16));
|
||
|
}
|
||
|
|
||
|
public void writeRef(Label label/*, boolean compressed*/) {
|
||
|
Reference r = new Reference();
|
||
|
references.append(r);
|
||
|
r.referenceLocation = currentLocation();
|
||
|
r.targetLabel = label;
|
||
|
//r.compressed = compressed;
|
||
|
if (label.location >= 0) {
|
||
|
writeRaw32(label.location, true);
|
||
|
r.isLinked = true;
|
||
|
} else {
|
||
|
writeRaw32(-1, true);
|
||
|
r.isLinked = false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public void linkRef(Reference r) {
|
||
|
if (!r.isLinked) {
|
||
|
if (r.targetLabel.location < 0) {
|
||
|
throw new Error("Can't link - label '" + r.targetLabel.name + "' hasn't been assigned a valid location");
|
||
|
}
|
||
|
writableInstructions().setElement(r.referenceLocation, r.targetLabel.location);
|
||
|
/*if (!r.compressed) {
|
||
|
writableInstructions().setElement(r.referenceLocation + 1, (short) (r.targetLabel.location >> 16));
|
||
|
}*/
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public void linkAllRefs() {
|
||
|
for (int i = 0; i < references.count(); i++) {
|
||
|
linkRef(references.get(i));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public void writeRef(BytecodeHeap.Record record) {
|
||
|
writeRaw32(record.getRecordIndex(), true);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Called when generating most statements/expressions/clauses to give the nearest source
|
||
|
* for debugging purposes. When generating synthetic code, null should be pushed instead.
|
||
|
*/
|
||
|
public void pushSource(Node source) {
|
||
|
sourceStack.append(source);
|
||
|
}
|
||
|
|
||
|
public Node popSource() {
|
||
|
return sourceStack.pop();
|
||
|
}
|
||
|
|
||
|
public void popSource(Node source) {
|
||
|
Node popped = popSource();
|
||
|
if (popped != source) {
|
||
|
throw new Error("Source stack mismatch: Expecting " + source + " but got " + popped);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public void pushBreak(Label label) {
|
||
|
breakStack.append(label);
|
||
|
}
|
||
|
|
||
|
public Label getBreak() {
|
||
|
if (breakStack.count() < 1) {
|
||
|
return null;
|
||
|
}
|
||
|
return breakStack.get(breakStack.count()-1);
|
||
|
}
|
||
|
|
||
|
public Label popBreak() {
|
||
|
return breakStack.pop();
|
||
|
}
|
||
|
|
||
|
public void popBreak(Label label) {
|
||
|
Label popped = popBreak();
|
||
|
if (popped != label) {
|
||
|
throw new Error("Break stack mismatch: Expecting " + label + " but got " + popped);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public void pushContinue(Label label) {
|
||
|
continueStack.append(label);
|
||
|
}
|
||
|
|
||
|
public Label getContinue() {
|
||
|
if (continueStack.count() < 1) {
|
||
|
return null;
|
||
|
}
|
||
|
return continueStack.get(continueStack.count()-1);
|
||
|
}
|
||
|
|
||
|
public Label popContinue() {
|
||
|
return continueStack.pop();
|
||
|
}
|
||
|
|
||
|
public void popContinue(Label label) {
|
||
|
Label popped = popContinue();
|
||
|
if (popped != label) {
|
||
|
throw new Error("Continue stack mismatch: Expecting " + label + " but got " + popped);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public Node getNearestSource(boolean requireLocation) {
|
||
|
for (int i = sourceStack.count() - 1; i >= 0; i--) {
|
||
|
if (sourceStack.get(i) != null) {
|
||
|
if (!requireLocation || sourceStack.get(i).countAnnotations(AnnotationType.LOCATION) > 0) {
|
||
|
return sourceStack.get(i);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
public int getNearestLine() {
|
||
|
Node s = getNearestSource(true);
|
||
|
if (s == null) {
|
||
|
return 0;
|
||
|
}
|
||
|
LocationAnnotation l = (LocationAnnotation) s.getAnnotations(AnnotationType.LOCATION)[0];
|
||
|
return l.getToken().getSnippet().getStart().getLineCount();
|
||
|
}
|
||
|
|
||
|
public int getNearestCharacter() {
|
||
|
Node s = getNearestSource(true);
|
||
|
if (s == null) {
|
||
|
return 0;
|
||
|
}
|
||
|
LocationAnnotation l = (LocationAnnotation) s.getAnnotations(AnnotationType.LOCATION)[0];
|
||
|
return l.getToken().getSnippet().getStart().getCharacterCount();
|
||
|
}
|
||
|
|
||
|
public boolean isPartlySynthetic() {
|
||
|
for (int i = 0; i < sourceStack.count(); i++) {
|
||
|
if (sourceStack.get(i) == null) {
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
public static enum ParameterFormat {
|
||
|
NONE,
|
||
|
CONSTANT_INDEX,
|
||
|
INSTRUCTION_INDEX,
|
||
|
VARIABLE_INDEX
|
||
|
}
|
||
|
|
||
|
public static enum MajorOpcode {
|
||
|
RESERVED,
|
||
|
ALU,
|
||
|
COMP,
|
||
|
CALL,
|
||
|
JUMP,
|
||
|
MEM,
|
||
|
TYPE,
|
||
|
MISC,
|
||
|
TINYCONST,
|
||
|
EXTENSION_A,
|
||
|
EXTENSION_B,
|
||
|
EXTENSION_C,
|
||
|
EXTENSION_D,
|
||
|
}
|
||
|
|
||
|
public static enum MiscOp {
|
||
|
CONST,
|
||
|
CONST_SPLIT32,
|
||
|
CONST24,
|
||
|
RETURN,
|
||
|
THROW,
|
||
|
RESERVED_FOR_ASSERT,
|
||
|
DROP,
|
||
|
DUP,
|
||
|
SWAP,
|
||
|
STRCAT,
|
||
|
NOT,
|
||
|
LOCK,
|
||
|
UNLOCK,
|
||
|
CHECKRETHROW
|
||
|
}
|
||
|
|
||
|
public static enum TypeOp {
|
||
|
DECLARE_LOCAL,
|
||
|
NEW_ARRAY,
|
||
|
CONVERT,
|
||
|
CONVERT_SMALL,
|
||
|
TYPECHECK,
|
||
|
ARRAYLENGTH,
|
||
|
THIS,
|
||
|
OUTERTHIS,
|
||
|
TYPEOBJECT,
|
||
|
}
|
||
|
|
||
|
public static enum CallOp {
|
||
|
STATIC_FUNCTION,
|
||
|
STATIC_PROCEDURE,
|
||
|
INSTANCE_FUNCTION,
|
||
|
INSTANCE_PROCEDURE,
|
||
|
INTERFACE_FUNCTION,
|
||
|
INTERFACE_PROCEDURE,
|
||
|
SUPER_INSTANCE_FUNCTION,
|
||
|
SUPER_INSTANCE_PROCEDURE,
|
||
|
SUPER_INTERFACE_FUNCTION,
|
||
|
SUPER_INTERFACE_PROCEDURE,
|
||
|
CONSTRUCTOR_NEW,
|
||
|
CONSTRUCTOR_NESTED,
|
||
|
CONSTRUCTOR_INNER
|
||
|
}
|
||
|
|
||
|
public static enum JumpOp {
|
||
|
GOTO,
|
||
|
IFTRUE,
|
||
|
IFFALSE
|
||
|
}
|
||
|
|
||
|
/* These should match the first 16 TypeOption.Kind values. */
|
||
|
public static enum StackType {
|
||
|
/* #0-#3 are used for special types (these are not valid in all scenarios). */
|
||
|
UNINITIALISED,
|
||
|
INVALID,
|
||
|
DUCK,
|
||
|
VOID,
|
||
|
/* #4 is the object type. */
|
||
|
OBJECT,
|
||
|
/* #5-#7 are the basic non-integer primitives. */
|
||
|
BIT,
|
||
|
FLOAT32,
|
||
|
FLOAT64,
|
||
|
|
||
|
/* #8-#15 are the basic integer primitives. */
|
||
|
INT8,
|
||
|
INT16,
|
||
|
INT32,
|
||
|
INT64,
|
||
|
UINT8,
|
||
|
UINT16,
|
||
|
UINT32,
|
||
|
UINT64
|
||
|
/*
|
||
|
INT32,
|
||
|
UINT32,
|
||
|
FLOAT32,
|
||
|
EXT32,
|
||
|
INT64,
|
||
|
UINT64,
|
||
|
FLOAT64,
|
||
|
EXT64,
|
||
|
EXTA,
|
||
|
EXTB,
|
||
|
EXTC,
|
||
|
EXTD,
|
||
|
OBJECT,
|
||
|
RESERVED1,
|
||
|
BOOLEAN,
|
||
|
VOID
|
||
|
*/
|
||
|
}
|
||
|
|
||
|
public static enum MemOp {
|
||
|
LOAD_LOCAL,
|
||
|
LOAD_PARAM,
|
||
|
LOAD_INSTANCE_FIELD,
|
||
|
LOAD_STATIC_FIELD,
|
||
|
LOAD_ARRAY,
|
||
|
LOAD_EXT1,
|
||
|
LOAD_EXT2,
|
||
|
LOAD_EXT3,
|
||
|
STORE_LOCAL,
|
||
|
STORE_PARAM,
|
||
|
STORE_INSTANCE_FIELD,
|
||
|
STORE_STATIC_FIELD,
|
||
|
STORE_ARRAY,
|
||
|
STORE_EXT1,
|
||
|
STORE_EXT2,
|
||
|
STORE_EXT3
|
||
|
}
|
||
|
|
||
|
public static enum ALUOp {
|
||
|
ADD,
|
||
|
SUB,
|
||
|
MUL,
|
||
|
DIV,
|
||
|
MOD,
|
||
|
SHRS,
|
||
|
SHRZ,
|
||
|
SHLZ,
|
||
|
AND,
|
||
|
OR,
|
||
|
XOR
|
||
|
}
|
||
|
|
||
|
public static enum CompOp {
|
||
|
EQUAL,
|
||
|
NOT_EQUAL,
|
||
|
ABOVE,
|
||
|
ABOVE_OR_EQUAL,
|
||
|
BELOW,
|
||
|
BELOW_OR_EQUAL
|
||
|
}
|
||
|
|
||
|
public int instrWord(MajorOpcode op, int minor, int a, int b) {
|
||
|
int result = 0;
|
||
|
|
||
|
result |= minor << 0;
|
||
|
result |= op.value << 4;
|
||
|
result |= a << 8;
|
||
|
result |= b << 12;
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
public void genInstrWord(MajorOpcode op, int minor, int a, int b) {
|
||
|
genInstrWord(instrWord(op, minor, a, b));
|
||
|
}
|
||
|
public void genInstrWord(MajorOpcode op, int minor, int a) {
|
||
|
genInstrWord(op,minor,a,0);
|
||
|
}
|
||
|
public void genInstrWord(MajorOpcode op, int minor) {
|
||
|
genInstrWord(op,minor,0,0);
|
||
|
}
|
||
|
public void genInstrWord(MajorOpcode op) {
|
||
|
genInstrWord(op,0,0,0);
|
||
|
}
|
||
|
|
||
|
private int prevLineNumber = 0;
|
||
|
private int prevCharNumber = 0;
|
||
|
public void topupDebugInfo() {
|
||
|
if (getNearestLine() != prevLineNumber || getNearestCharacter() != prevCharNumber) {
|
||
|
prevLineNumber = getNearestLine();
|
||
|
prevCharNumber = getNearestCharacter();
|
||
|
locations.append(new Location(currentLocation(), prevLineNumber, prevCharNumber));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public void genInstrWord(int instr) {
|
||
|
topupDebugInfo();
|
||
|
writeRaw32(instr, false);
|
||
|
}
|
||
|
|
||
|
public void genALU(ALUOp op, TypeSignature type) {
|
||
|
genALU(op, stackType(type));
|
||
|
}
|
||
|
|
||
|
public void genALU(ALUOp op, StackType type) {
|
||
|
vpop(type);
|
||
|
vpop(type);
|
||
|
genInstrWord(MajorOpcode.ALU, op.value, type.value);
|
||
|
vpush(type);
|
||
|
}
|
||
|
|
||
|
public void genComp(CompOp op, StackType type) {
|
||
|
vpop(type);
|
||
|
vpop(type);
|
||
|
genInstrWord(MajorOpcode.COMP, op.value, type.value);
|
||
|
vpush(StackType.BIT);
|
||
|
}
|
||
|
|
||
|
public void genComp(CompOp op, TypeSignature type) {
|
||
|
genComp(op, stackType(type));
|
||
|
}
|
||
|
|
||
|
public void genDrop(StackType type) {
|
||
|
vpop(type);
|
||
|
genInstrWord(MajorOpcode.MISC, MiscOp.DROP.value, type.value, 1);
|
||
|
}
|
||
|
|
||
|
public void genDrop(TypeSignature type) {
|
||
|
genDrop(stackType(type));
|
||
|
}
|
||
|
|
||
|
public void genDup(StackType type) {
|
||
|
vpop(type);
|
||
|
genInstrWord(MajorOpcode.MISC, MiscOp.DUP.value, type.value, 2);
|
||
|
vpush(type);
|
||
|
vpush(type);
|
||
|
}
|
||
|
|
||
|
public void genSwap(StackType currentTopType, StackType nextTopType) {
|
||
|
vpop(currentTopType);
|
||
|
vpop(nextTopType);
|
||
|
genInstrWord(MajorOpcode.MISC, MiscOp.SWAP.value, currentTopType.value, nextTopType.value);
|
||
|
vpush(currentTopType);
|
||
|
vpush(nextTopType);
|
||
|
}
|
||
|
|
||
|
public void genSwap(TypeSignature currentTopType, TypeSignature nextTopType) {
|
||
|
genSwap(stackType(currentTopType), stackType(nextTopType));
|
||
|
}
|
||
|
|
||
|
public void genDup(TypeSignature type) {
|
||
|
genDup(stackType(type));
|
||
|
}
|
||
|
|
||
|
public void genConst(BytecodeHeap.Record value) {
|
||
|
if ((value.getRecordIndex() & 0xFFF) == value.getRecordIndex()) {
|
||
|
genInstrWord(MajorOpcode.TINYCONST, (value.getRecordIndex() >> 8) & 0xF, (value.getRecordIndex() >> 0) & 0xF, (value.getRecordIndex() >> 4) & 0xF);
|
||
|
}/* else if ((value.getRecordIndex() & 0xFFFFFF) == value.getRecordIndex()) {
|
||
|
genInstrWord(MajorOpcode.MISC, MiscOp.CONST24.value, (value.getRecordIndex() >> 16) & 0xF, (value.getRecordIndex() >> 20) & 0xF);
|
||
|
writeRaw32(value.getRecordIndex() & 0xFFFF, false);
|
||
|
} else if (value.getRecordIndex() >= 65536) {
|
||
|
genInstrWord(MajorOpcode.MISC, MiscOp.CONST_SPLIT32.value);
|
||
|
writeRaw32(value.getRecordIndex() & 0xFFFF, false);
|
||
|
writeRaw32((value.getRecordIndex() >> 16) & 0xFFFF, false);
|
||
|
}*/ else {
|
||
|
genInstrWord(MajorOpcode.MISC, MiscOp.CONST.value);
|
||
|
writeRef(value);
|
||
|
}
|
||
|
vpush(value.getStandardClass());
|
||
|
}
|
||
|
|
||
|
public void genThis() {
|
||
|
genInstrWord(MajorOpcode.TYPE, TypeOp.THIS.value);
|
||
|
vpush(owner.getTypeSignature());
|
||
|
}
|
||
|
|
||
|
public void genOuterThis() {
|
||
|
vpop(StackType.OBJECT);
|
||
|
genInstrWord(MajorOpcode.TYPE, TypeOp.OUTERTHIS.value, 1);
|
||
|
vpush(StackType.OBJECT);
|
||
|
}
|
||
|
|
||
|
public void genNull() {
|
||
|
genConst(target.heap.getNull());
|
||
|
}
|
||
|
|
||
|
public void genBoolean(boolean value) {
|
||
|
genConst(value ? target.heap.getTrue() : target.heap.getFalse());
|
||
|
}
|
||
|
|
||
|
public void genJump(JumpOp op, Label target) {
|
||
|
if (op == JumpOp.IFFALSE || op == JumpOp.IFTRUE) {
|
||
|
vpop(StackType.BIT);
|
||
|
}
|
||
|
genInstrWord(MajorOpcode.JUMP, op.value);
|
||
|
writeRef(target);
|
||
|
}
|
||
|
|
||
|
public void genLoad(FieldSignature field) {
|
||
|
if (!field.isStatic) {
|
||
|
vpop(StackType.OBJECT);
|
||
|
}
|
||
|
genInstrWord(MajorOpcode.MEM, field.isStatic ? MemOp.LOAD_STATIC_FIELD.value : MemOp.LOAD_INSTANCE_FIELD.value);
|
||
|
writeRef(target.heap.getFieldSignature(field));
|
||
|
vpush(field.storageType);
|
||
|
}
|
||
|
|
||
|
public void genStore(FieldSignature field) {
|
||
|
if (!field.isStatic) {
|
||
|
vpop(StackType.OBJECT);
|
||
|
}
|
||
|
vpop(field.storageType);
|
||
|
genInstrWord(MajorOpcode.MEM, field.isStatic ? MemOp.STORE_STATIC_FIELD.value : MemOp.STORE_INSTANCE_FIELD.value);
|
||
|
writeRef(target.heap.getFieldSignature(field));
|
||
|
}
|
||
|
|
||
|
public void genArrayLoad(TypeSignature elementType) {
|
||
|
vpop(StackType.OBJECT);
|
||
|
vpop(StackType.INT32);
|
||
|
genInstrWord(MajorOpcode.MEM, MemOp.LOAD_ARRAY.value, stackType(elementType).value);
|
||
|
vpush(elementType);
|
||
|
}
|
||
|
|
||
|
public void genArrayStore(TypeSignature elementType) {
|
||
|
vpop(elementType);
|
||
|
vpop(StackType.INT32);
|
||
|
vpop(StackType.OBJECT);
|
||
|
genInstrWord(MajorOpcode.MEM, MemOp.STORE_ARRAY.value, stackType(elementType).value);
|
||
|
}
|
||
|
|
||
|
public void genLoadLocalOrParam(TypeSignature typeSignature, int index) {
|
||
|
genInstrWord(MajorOpcode.MEM, index < 0 ? MemOp.LOAD_PARAM.value : MemOp.LOAD_LOCAL.value, stackType(typeSignature).value);
|
||
|
writeRaw32(index < 0 ? -index : index, false);
|
||
|
vpush(typeSignature);
|
||
|
}
|
||
|
|
||
|
public void genStoreLocalOrParam(TypeSignature typeSignature, int index) {
|
||
|
vpop(typeSignature);
|
||
|
genInstrWord(MajorOpcode.MEM, index < 0 ? MemOp.STORE_PARAM.value : MemOp.STORE_LOCAL.value, stackType(typeSignature).value);
|
||
|
writeRaw32(index < 0 ? -index : index, false);
|
||
|
}
|
||
|
|
||
|
public void genArrayLength() {
|
||
|
vpop(StackType.OBJECT);
|
||
|
genInstrWord(MajorOpcode.TYPE, TypeOp.ARRAYLENGTH.value);
|
||
|
vpush(StackType.INT32);
|
||
|
}
|
||
|
|
||
|
public void genDeclareLocal(TypeSignature typeSignature, int index) {
|
||
|
genInstrWord(MajorOpcode.TYPE, TypeOp.DECLARE_LOCAL.value, stackType(typeSignature).value);
|
||
|
writeRaw32(index, false);
|
||
|
writeRef(getTarget().getHeap().getTypeSignature(typeSignature));
|
||
|
}
|
||
|
|
||
|
public void genLock() {
|
||
|
vpop(StackType.OBJECT);
|
||
|
genInstrWord(MajorOpcode.MISC, MiscOp.LOCK.value);
|
||
|
}
|
||
|
|
||
|
public void genUnlock() {
|
||
|
vpop(StackType.OBJECT);
|
||
|
genInstrWord(MajorOpcode.MISC, MiscOp.UNLOCK.value);
|
||
|
}
|
||
|
|
||
|
public void genVoidReturn() {
|
||
|
genInstrWord(MajorOpcode.MISC, MiscOp.RETURN.value, StackType.VOID.value);
|
||
|
}
|
||
|
|
||
|
public void genValueReturn(TypeSignature t) {
|
||
|
vpop(t);
|
||
|
genInstrWord(MajorOpcode.MISC, MiscOp.RETURN.value, stackType(t).value);
|
||
|
}
|
||
|
|
||
|
public void genConvert(TypeSignature expected, TypeSignature result) {
|
||
|
vpop(expected);
|
||
|
BytecodeHeap.ObjectLike from = getTarget().getHeap().getTypeSignature(expected);
|
||
|
BytecodeHeap.ObjectLike to = getTarget().getHeap().getTypeSignature(result);
|
||
|
genInstrWord(MajorOpcode.TYPE, TypeOp.CONVERT.value, stackType(expected).value, stackType(result).value);
|
||
|
writeRef(from);
|
||
|
writeRef(to);
|
||
|
vpush(result);
|
||
|
}
|
||
|
|
||
|
public void genTypeCheck(TypeSignature knownType, TypeSignature check) {
|
||
|
vpop(knownType);
|
||
|
genInstrWord(MajorOpcode.TYPE, TypeOp.TYPECHECK.value, stackType(knownType).value, stackType(check).value);
|
||
|
writeRef(getTarget().getHeap().getTypeSignature(knownType));
|
||
|
writeRef(getTarget().getHeap().getTypeSignature(check));
|
||
|
vpush(StackType.BIT);
|
||
|
}
|
||
|
|
||
|
public void genTypeObject(TypeSignature type) {
|
||
|
genInstrWord(MajorOpcode.TYPE, TypeOp.TYPEOBJECT.value, stackType(type).value);
|
||
|
writeRef(getTarget().getHeap().getTypeSignature(type));
|
||
|
vpush(StackType.OBJECT);
|
||
|
}
|
||
|
|
||
|
public void genThrow() {
|
||
|
vpop(StackType.OBJECT);
|
||
|
genInstrWord(MajorOpcode.MISC, MiscOp.THROW.value, StackType.OBJECT.value);
|
||
|
}
|
||
|
|
||
|
public void genCheckRethrow() {
|
||
|
genInstrWord(MajorOpcode.MISC, MiscOp.CHECKRETHROW.value);
|
||
|
}
|
||
|
|
||
|
public void genNot() {
|
||
|
vpop(StackType.BIT);
|
||
|
genInstrWord(MajorOpcode.MISC, MiscOp.NOT.value, StackType.BIT.value);
|
||
|
vpush(StackType.BIT);
|
||
|
}
|
||
|
|
||
|
public void vpopArgs(TypeSignature[] args) {
|
||
|
for (int i = args.length-1; i > 0; i--) {
|
||
|
vpop(args[i]);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public void genNewArray(TypeSignature arrayType) {
|
||
|
// TODO: May require some extension to account for local variables used in an inner class
|
||
|
vpop(StackType.INT32);
|
||
|
genInstrWord(MajorOpcode.TYPE, TypeOp.NEW_ARRAY.value, StackType.OBJECT.value);
|
||
|
writeRef(target.heap.getTypeSignature(arrayType));
|
||
|
vpush(arrayType);
|
||
|
}
|
||
|
|
||
|
public void genStrcat(int nstrs) {
|
||
|
// A quick hack in case > 15 need to be concatenated, just generate multiple instructions
|
||
|
while (nstrs > 15) {
|
||
|
genStrcat(15);
|
||
|
nstrs -= 14;
|
||
|
}
|
||
|
for (int i = 0; i < nstrs; i++) {
|
||
|
vpop(StackType.OBJECT);
|
||
|
}
|
||
|
genInstrWord(MajorOpcode.MISC, MiscOp.STRCAT.value, nstrs);
|
||
|
vpush(StackType.OBJECT);
|
||
|
}
|
||
|
|
||
|
public void genNewObject(MethodSignature constructor) {
|
||
|
vpopArgs(constructor.argumentTypes);
|
||
|
genInstrWord(MajorOpcode.CALL, CallOp.CONSTRUCTOR_NEW.value, StackType.OBJECT.value);
|
||
|
writeRef(target.heap.getMethodSignature(constructor));
|
||
|
vpush(constructor.owner);
|
||
|
}
|
||
|
|
||
|
public void genNewInner(MethodSignature constructor, TypeSignature[] upvalues) {
|
||
|
vpopArgs(constructor.argumentTypes);
|
||
|
vpopArgs(upvalues);
|
||
|
vpop(StackType.OBJECT); // Assume the relevant "this" has been pushed appropriately
|
||
|
genInstrWord(MajorOpcode.CALL, CallOp.CONSTRUCTOR_INNER.value, StackType.OBJECT.value);
|
||
|
writeRef(target.heap.getMethodSignature(constructor));
|
||
|
vpush(constructor.owner);
|
||
|
}
|
||
|
|
||
|
public void genNestedConstructorCall(MethodSignature constructor) {
|
||
|
// Conceptually, this instruction pushes, and then pops, the "this" value implicitly
|
||
|
vpush(StackType.OBJECT);
|
||
|
vpop(StackType.OBJECT);
|
||
|
// TODO: May require some extension to account for local variables used in an inner class
|
||
|
vpopArgs(constructor.argumentTypes);
|
||
|
genInstrWord(MajorOpcode.CALL, CallOp.CONSTRUCTOR_NESTED.value, StackType.OBJECT.value);
|
||
|
writeRef(target.heap.getMethodSignature(constructor));
|
||
|
vpush(StackType.OBJECT);
|
||
|
// Autogenerate a drop after the call
|
||
|
genDrop(StackType.OBJECT);
|
||
|
}
|
||
|
|
||
|
public void genCall(MethodSignature method) {
|
||
|
genCall(method, false);
|
||
|
}
|
||
|
|
||
|
public void genCall(MethodSignature method, boolean isSuperCall) {
|
||
|
if (!method.isStatic()) {
|
||
|
vpop(StackType.OBJECT);
|
||
|
}
|
||
|
vpopArgs(method.argumentTypes);
|
||
|
CallOp type = null;
|
||
|
if (method.isStatic() && method.returnType.mappableEquals(TypeSignature.VOID)) {
|
||
|
type = CallOp.STATIC_PROCEDURE;
|
||
|
} else if (!method.isStatic() && method.returnType.mappableEquals(TypeSignature.VOID)) {
|
||
|
if (isSuperCall) {
|
||
|
type = method.kind == MethodSignature.Kind.INTERFACE_METHOD ? CallOp.SUPER_INTERFACE_PROCEDURE : CallOp.SUPER_INSTANCE_PROCEDURE;
|
||
|
} else {
|
||
|
type = method.kind == MethodSignature.Kind.INTERFACE_METHOD ? CallOp.INTERFACE_PROCEDURE : CallOp.INSTANCE_PROCEDURE;
|
||
|
}
|
||
|
} else if (method.isStatic() && !method.returnType.mappableEquals(TypeSignature.VOID)) {
|
||
|
type = CallOp.STATIC_FUNCTION;
|
||
|
} else {
|
||
|
if (isSuperCall) {
|
||
|
type = method.kind == MethodSignature.Kind.INTERFACE_METHOD ? CallOp.SUPER_INTERFACE_FUNCTION : CallOp.SUPER_INSTANCE_FUNCTION;
|
||
|
} else {
|
||
|
type = method.kind == MethodSignature.Kind.INTERFACE_METHOD ? CallOp.INTERFACE_FUNCTION : CallOp.INSTANCE_FUNCTION;
|
||
|
}
|
||
|
}
|
||
|
genInstrWord(MajorOpcode.CALL, type.value, stackType(method.returnType).value);
|
||
|
writeRef(target.heap.getMethodSignature(method));
|
||
|
vpush(method.returnType);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* This can be used for reporting some kind of error. For purely synthetic code, this will throw
|
||
|
* an error, whereas if a source is available the error will be reported there.
|
||
|
*/
|
||
|
public void genError(String message) {
|
||
|
if (getNearestSource(false) == null) {
|
||
|
throw new Error("Failed to produce synthetic code: " + message);
|
||
|
} else {
|
||
|
getNearestSource(false).annotate(ErrorType.INTERNAL_ERROR, message);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Added for convenience when generating constructors with an implicit super-constructor.
|
||
|
*/
|
||
|
public void genDefaultSuperConstructorCall() {
|
||
|
if (((UserTypeModel)owner).getBaseClass() == null) {
|
||
|
return; // Skip super-constructor call if this is the root class
|
||
|
}
|
||
|
genNestedConstructorCall(((UserTypeModel)owner).getBaseClass().getDefaultInstanceConstructor().getMethodSignature());
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Called at the end of most methods to add a default return or report error.
|
||
|
*/
|
||
|
public void genEnd() {
|
||
|
/* This should cause the default behaviour if the return type is void, or
|
||
|
* trigger an error if reached in a non-void method.
|
||
|
*/
|
||
|
genVoidReturn();
|
||
|
|
||
|
linkAllRefs();
|
||
|
|
||
|
BytecodeHeap.ArrayInt32 lbllist = getTarget().getHeap().newLabels(0);
|
||
|
method.setElement(BytecodeHeap.MethodFields.EXCEPTIONS.value, lbllist);
|
||
|
|
||
|
for (int i = 0; i < labels.count(); i++) {
|
||
|
labels.get(i).genLabelData(lbllist, false);
|
||
|
}
|
||
|
|
||
|
BytecodeHeap.ArrayInt16 dbglist = getTarget().getHeap().newDebug(0);
|
||
|
method.setElement(BytecodeHeap.MethodFields.DEBUG.value, dbglist);
|
||
|
|
||
|
for (int i = 0; i < locations.count(); i++) {
|
||
|
locations.get(i).genLocationData(dbglist, false);
|
||
|
}
|
||
|
|
||
|
method.setElement(BytecodeHeap.MethodFields.MAXSTACK.value, getTarget().getHeap().getConstInt32(""+maxStackSize));
|
||
|
}
|
||
|
|
||
|
public BytecodeTarget getTarget() {
|
||
|
return target;
|
||
|
}
|
||
|
|
||
|
public TypeModel getOwner() {
|
||
|
return owner;
|
||
|
}
|
||
|
}
|