mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2025-01-10 17:04:09 -08:00
Add IR parser of cast instructions
This commit is contained in:
parent
825acfc85a
commit
2b94a8b05c
|
@ -232,7 +232,7 @@ class StatementGenerator implements InstructionVisitor {
|
||||||
case SHORT:
|
case SHORT:
|
||||||
value = Expr.unary(UnaryOperation.INT_TO_SHORT, null, value);
|
value = Expr.unary(UnaryOperation.INT_TO_SHORT, null, value);
|
||||||
break;
|
break;
|
||||||
case CHARACTER:
|
case CHAR:
|
||||||
value = Expr.unary(UnaryOperation.INT_TO_CHAR, null, value);
|
value = Expr.unary(UnaryOperation.INT_TO_CHAR, null, value);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -448,7 +448,7 @@ public class Interpreter {
|
||||||
case SHORT:
|
case SHORT:
|
||||||
result = (short) a;
|
result = (short) a;
|
||||||
break;
|
break;
|
||||||
case CHARACTER:
|
case CHAR:
|
||||||
result = (char) a;
|
result = (char) a;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -467,7 +467,7 @@ public class Interpreter {
|
||||||
case SHORT:
|
case SHORT:
|
||||||
result = (Short) a;
|
result = (Short) a;
|
||||||
break;
|
break;
|
||||||
case CHARACTER:
|
case CHAR:
|
||||||
result = (Character) a;
|
result = (Character) a;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -231,7 +231,7 @@ public class ValueEmitter {
|
||||||
case SHORT:
|
case SHORT:
|
||||||
return IntegerSubtype.SHORT;
|
return IntegerSubtype.SHORT;
|
||||||
case CHARACTER:
|
case CHARACTER:
|
||||||
return IntegerSubtype.CHARACTER;
|
return IntegerSubtype.CHAR;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -776,7 +776,7 @@ public class ValueEmitter {
|
||||||
return ValueType.BYTE;
|
return ValueType.BYTE;
|
||||||
case SHORT:
|
case SHORT:
|
||||||
return ValueType.SHORT;
|
return ValueType.SHORT;
|
||||||
case CHARACTER:
|
case CHAR:
|
||||||
return ValueType.CHARACTER;
|
return ValueType.CHARACTER;
|
||||||
}
|
}
|
||||||
throw new IllegalArgumentException("Unknown subtype: " + subtype);
|
throw new IllegalArgumentException("Unknown subtype: " + subtype);
|
||||||
|
@ -794,7 +794,7 @@ public class ValueEmitter {
|
||||||
throw new EmitException("Can't cast non-short value: " + type);
|
throw new EmitException("Can't cast non-short value: " + type);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case CHARACTER:
|
case CHAR:
|
||||||
if (type != ValueType.CHARACTER) {
|
if (type != ValueType.CHARACTER) {
|
||||||
throw new EmitException("Can't cast non-char value: " + type);
|
throw new EmitException("Can't cast non-char value: " + type);
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,12 +15,8 @@
|
||||||
*/
|
*/
|
||||||
package org.teavm.model.instructions;
|
package org.teavm.model.instructions;
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @author Alexey Andreev
|
|
||||||
*/
|
|
||||||
public enum IntegerSubtype {
|
public enum IntegerSubtype {
|
||||||
BYTE,
|
BYTE,
|
||||||
SHORT,
|
SHORT,
|
||||||
CHARACTER
|
CHAR
|
||||||
}
|
}
|
||||||
|
|
|
@ -560,7 +560,7 @@ public class GlobalValueNumbering implements MethodOptimization {
|
||||||
case SHORT:
|
case SHORT:
|
||||||
evaluatedConstant = value.intValue() << 16 >> 16;
|
evaluatedConstant = value.intValue() << 16 >> 16;
|
||||||
break;
|
break;
|
||||||
case CHARACTER:
|
case CHAR:
|
||||||
evaluatedConstant = value.intValue() & 0xFFFF;
|
evaluatedConstant = value.intValue() & 0xFFFF;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,7 @@ import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import org.teavm.model.BasicBlock;
|
import org.teavm.model.BasicBlock;
|
||||||
|
@ -40,12 +41,17 @@ import org.teavm.model.instructions.BinaryInstruction;
|
||||||
import org.teavm.model.instructions.BinaryOperation;
|
import org.teavm.model.instructions.BinaryOperation;
|
||||||
import org.teavm.model.instructions.BranchingCondition;
|
import org.teavm.model.instructions.BranchingCondition;
|
||||||
import org.teavm.model.instructions.BranchingInstruction;
|
import org.teavm.model.instructions.BranchingInstruction;
|
||||||
|
import org.teavm.model.instructions.CastInstruction;
|
||||||
|
import org.teavm.model.instructions.CastIntegerDirection;
|
||||||
|
import org.teavm.model.instructions.CastIntegerInstruction;
|
||||||
|
import org.teavm.model.instructions.CastNumberInstruction;
|
||||||
import org.teavm.model.instructions.ClassConstantInstruction;
|
import org.teavm.model.instructions.ClassConstantInstruction;
|
||||||
import org.teavm.model.instructions.DoubleConstantInstruction;
|
import org.teavm.model.instructions.DoubleConstantInstruction;
|
||||||
import org.teavm.model.instructions.EmptyInstruction;
|
import org.teavm.model.instructions.EmptyInstruction;
|
||||||
import org.teavm.model.instructions.ExitInstruction;
|
import org.teavm.model.instructions.ExitInstruction;
|
||||||
import org.teavm.model.instructions.FloatConstantInstruction;
|
import org.teavm.model.instructions.FloatConstantInstruction;
|
||||||
import org.teavm.model.instructions.IntegerConstantInstruction;
|
import org.teavm.model.instructions.IntegerConstantInstruction;
|
||||||
|
import org.teavm.model.instructions.IntegerSubtype;
|
||||||
import org.teavm.model.instructions.InvocationType;
|
import org.teavm.model.instructions.InvocationType;
|
||||||
import org.teavm.model.instructions.InvokeInstruction;
|
import org.teavm.model.instructions.InvokeInstruction;
|
||||||
import org.teavm.model.instructions.JumpInstruction;
|
import org.teavm.model.instructions.JumpInstruction;
|
||||||
|
@ -290,6 +296,9 @@ public class ListingParser {
|
||||||
case "invokeStatic":
|
case "invokeStatic":
|
||||||
parseInvoke(block, receiver);
|
parseInvoke(block, receiver);
|
||||||
break;
|
break;
|
||||||
|
case "cast":
|
||||||
|
parseCast(block, receiver);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
unexpected();
|
unexpected();
|
||||||
break;
|
break;
|
||||||
|
@ -549,6 +558,125 @@ public class ListingParser {
|
||||||
insn.getArguments().addAll(arguments);
|
insn.getArguments().addAll(arguments);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
insn.setLocation(currentLocation);
|
||||||
|
block.getInstructions().add(insn);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void parseCast(BasicBlock block, Variable receiver) throws IOException, ListingParseException {
|
||||||
|
lexer.nextToken();
|
||||||
|
Variable value = expectVariable();
|
||||||
|
expect(ListingToken.IDENTIFIER);
|
||||||
|
switch ((String) lexer.getTokenValue()) {
|
||||||
|
case "from":
|
||||||
|
parseNumericCast(block, receiver, value);
|
||||||
|
break;
|
||||||
|
case "to":
|
||||||
|
parseObjectCast(block, receiver, value);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
unexpected();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void parseNumericCast(BasicBlock block, Variable receiver, Variable value)
|
||||||
|
throws IOException, ListingParseException {
|
||||||
|
lexer.nextToken();
|
||||||
|
|
||||||
|
NumericTypeOrIntegerSubtype source = expectTypeOrIntegerSubtype();
|
||||||
|
|
||||||
|
if (source.subtype != null) {
|
||||||
|
CastIntegerInstruction insn = new CastIntegerInstruction(source.subtype,
|
||||||
|
CastIntegerDirection.TO_INTEGER);
|
||||||
|
expectKeyword("to");
|
||||||
|
expectKeyword("int");
|
||||||
|
insn.setReceiver(receiver);
|
||||||
|
insn.setValue(value);
|
||||||
|
insn.setLocation(currentLocation);
|
||||||
|
block.getInstructions().add(insn);
|
||||||
|
} else {
|
||||||
|
expectKeyword("to");
|
||||||
|
expect(ListingToken.IDENTIFIER);
|
||||||
|
NumericTypeOrIntegerSubtype target = expectTypeOrIntegerSubtype();
|
||||||
|
if (target.subtype != null) {
|
||||||
|
if (source.type != NumericOperandType.INT) {
|
||||||
|
throw new ListingParseException("Only int can be cast to "
|
||||||
|
+ target.subtype.name().toLowerCase(Locale.ROOT), lexer.getIndex());
|
||||||
|
}
|
||||||
|
CastIntegerInstruction insn = new CastIntegerInstruction(source.subtype,
|
||||||
|
CastIntegerDirection.FROM_INTEGER);
|
||||||
|
insn.setReceiver(receiver);
|
||||||
|
insn.setValue(value);
|
||||||
|
insn.setLocation(currentLocation);
|
||||||
|
block.getInstructions().add(insn);
|
||||||
|
} else {
|
||||||
|
CastNumberInstruction insn = new CastNumberInstruction(source.type, target.type);
|
||||||
|
insn.setReceiver(receiver);
|
||||||
|
insn.setValue(value);
|
||||||
|
insn.setLocation(currentLocation);
|
||||||
|
block.getInstructions().add(insn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private NumericTypeOrIntegerSubtype expectTypeOrIntegerSubtype() throws IOException, ListingParseException {
|
||||||
|
IntegerSubtype subtype = null;
|
||||||
|
NumericOperandType type = null;
|
||||||
|
expect(ListingToken.IDENTIFIER);
|
||||||
|
switch ((String) lexer.getTokenValue()) {
|
||||||
|
case "byte":
|
||||||
|
subtype = IntegerSubtype.BYTE;
|
||||||
|
break;
|
||||||
|
case "short":
|
||||||
|
subtype = IntegerSubtype.SHORT;
|
||||||
|
break;
|
||||||
|
case "char":
|
||||||
|
subtype = IntegerSubtype.CHAR;
|
||||||
|
break;
|
||||||
|
case "int":
|
||||||
|
type = NumericOperandType.INT;
|
||||||
|
break;
|
||||||
|
case "long":
|
||||||
|
type = NumericOperandType.LONG;
|
||||||
|
break;
|
||||||
|
case "float":
|
||||||
|
type = NumericOperandType.FLOAT;
|
||||||
|
break;
|
||||||
|
case "double":
|
||||||
|
type = NumericOperandType.DOUBLE;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
unexpected();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
lexer.nextToken();
|
||||||
|
return new NumericTypeOrIntegerSubtype(type, subtype);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class NumericTypeOrIntegerSubtype {
|
||||||
|
NumericOperandType type;
|
||||||
|
IntegerSubtype subtype;
|
||||||
|
|
||||||
|
public NumericTypeOrIntegerSubtype(NumericOperandType type, IntegerSubtype subtype) {
|
||||||
|
this.type = type;
|
||||||
|
this.subtype = subtype;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void parseObjectCast(BasicBlock block, Variable receiver, Variable value)
|
||||||
|
throws IOException, ListingParseException {
|
||||||
|
lexer.nextToken();
|
||||||
|
expect(ListingToken.IDENTIFIER);
|
||||||
|
ValueType type = ValueType.parseIfPossible((String) lexer.getTokenValue());
|
||||||
|
if (type == null) {
|
||||||
|
throw new ListingParseException("Unparseable type", lexer.getTokenStart());
|
||||||
|
}
|
||||||
|
lexer.nextToken();
|
||||||
|
|
||||||
|
CastInstruction insn = new CastInstruction();
|
||||||
|
insn.setReceiver(receiver);
|
||||||
|
insn.setValue(value);
|
||||||
|
insn.setTargetType(type);
|
||||||
|
insn.setLocation(currentLocation);
|
||||||
block.getInstructions().add(insn);
|
block.getInstructions().add(insn);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -185,12 +185,7 @@ public class ProgramParser {
|
||||||
}
|
}
|
||||||
for (LocalVariableNode localVar : method.localVariables) {
|
for (LocalVariableNode localVar : method.localVariables) {
|
||||||
int location = labelIndexes.get(localVar.start.getLabel());
|
int location = labelIndexes.get(localVar.start.getLabel());
|
||||||
List<LocalVariableNode> vars = localVariableMap.get(location);
|
localVariableMap.computeIfAbsent(location, k -> new ArrayList<>()).add(localVar);
|
||||||
if (vars == null) {
|
|
||||||
vars = new ArrayList<>();
|
|
||||||
localVariableMap.put(location, vars);
|
|
||||||
}
|
|
||||||
vars.add(localVar);
|
|
||||||
}
|
}
|
||||||
targetInstructions = new ArrayList<>(instructions.size());
|
targetInstructions = new ArrayList<>(instructions.size());
|
||||||
targetInstructions.addAll(Collections.nCopies(instructions.size(), null));
|
targetInstructions.addAll(Collections.nCopies(instructions.size(), null));
|
||||||
|
@ -1004,7 +999,7 @@ public class ProgramParser {
|
||||||
}
|
}
|
||||||
case Opcodes.CALOAD: {
|
case Opcodes.CALOAD: {
|
||||||
loadArrayElement(1, ArrayElementType.CHAR);
|
loadArrayElement(1, ArrayElementType.CHAR);
|
||||||
CastIntegerInstruction insn = new CastIntegerInstruction(IntegerSubtype.CHARACTER,
|
CastIntegerInstruction insn = new CastIntegerInstruction(IntegerSubtype.CHAR,
|
||||||
CastIntegerDirection.TO_INTEGER);
|
CastIntegerDirection.TO_INTEGER);
|
||||||
insn.setValue(getVariable(popSingle()));
|
insn.setValue(getVariable(popSingle()));
|
||||||
insn.setReceiver(getVariable(pushSingle()));
|
insn.setReceiver(getVariable(pushSingle()));
|
||||||
|
@ -1492,7 +1487,7 @@ public class ProgramParser {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Opcodes.I2C: {
|
case Opcodes.I2C: {
|
||||||
CastIntegerInstruction insn = new CastIntegerInstruction(IntegerSubtype.CHARACTER,
|
CastIntegerInstruction insn = new CastIntegerInstruction(IntegerSubtype.CHAR,
|
||||||
CastIntegerDirection.FROM_INTEGER);
|
CastIntegerDirection.FROM_INTEGER);
|
||||||
insn.setValue(getVariable(popSingle()));
|
insn.setValue(getVariable(popSingle()));
|
||||||
insn.setReceiver(getVariable(pushSingle()));
|
insn.setReceiver(getVariable(pushSingle()));
|
||||||
|
|
|
@ -26,6 +26,9 @@ import org.junit.Assert;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.teavm.model.BasicBlock;
|
import org.teavm.model.BasicBlock;
|
||||||
import org.teavm.model.Program;
|
import org.teavm.model.Program;
|
||||||
|
import org.teavm.model.instructions.CastInstruction;
|
||||||
|
import org.teavm.model.instructions.CastIntegerInstruction;
|
||||||
|
import org.teavm.model.instructions.CastNumberInstruction;
|
||||||
import org.teavm.model.instructions.ClassConstantInstruction;
|
import org.teavm.model.instructions.ClassConstantInstruction;
|
||||||
import org.teavm.model.instructions.DoubleConstantInstruction;
|
import org.teavm.model.instructions.DoubleConstantInstruction;
|
||||||
import org.teavm.model.instructions.FloatConstantInstruction;
|
import org.teavm.model.instructions.FloatConstantInstruction;
|
||||||
|
@ -102,6 +105,18 @@ public class ParserTest {
|
||||||
assertNotNull(invoke.getInstance());
|
assertNotNull(invoke.getInstance());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void casting() throws Exception {
|
||||||
|
Program program = runTest("casting");
|
||||||
|
assertEquals(1, program.basicBlockCount());
|
||||||
|
|
||||||
|
BasicBlock block = program.basicBlockAt(0);
|
||||||
|
assertTrue(block.getInstructions().get(0) instanceof CastInstruction);
|
||||||
|
assertTrue(block.getInstructions().get(1) instanceof CastIntegerInstruction);
|
||||||
|
assertTrue(block.getInstructions().get(2) instanceof CastIntegerInstruction);
|
||||||
|
assertTrue(block.getInstructions().get(3) instanceof CastNumberInstruction);
|
||||||
|
}
|
||||||
|
|
||||||
private Program runTest(String name) throws IOException {
|
private Program runTest(String name) throws IOException {
|
||||||
ClassLoader classLoader = ParserTest.class.getClassLoader();
|
ClassLoader classLoader = ParserTest.class.getClassLoader();
|
||||||
String path = "model/text/" + name + ".txt";
|
String path = "model/text/" + name + ".txt";
|
||||||
|
|
6
core/src/test/resources/model/text/casting.txt
Normal file
6
core/src/test/resources/model/text/casting.txt
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
$block
|
||||||
|
@r1 := cast @a to `Ljava/lang/String;`
|
||||||
|
@r2 := cast @b from int to char
|
||||||
|
@r3 := cast @c from byte to int
|
||||||
|
@r4 := cast @d from double to long
|
||||||
|
return @r1
|
Loading…
Reference in New Issue
Block a user