diff --git a/README.md b/README.md index 3cfd557..ca40c0e 100644 --- a/README.md +++ b/README.md @@ -1,47 +1,22 @@ -# Compilers Project - -For this project, you need to install [Java](https://jdk.java.net/), [Gradle](https://gradle.org/install/), and [Git](https://git-scm.com/downloads/) (and optionally, a [Git GUI client](https://git-scm.com/downloads/guis), such as TortoiseGit or GitHub Desktop). Please check the [compatibility matrix](https://docs.gradle.org/current/userguide/compatibility.html) for Java and Gradle versions. - -## Project setup - -There are some import folders in the repository. Your development source code is inside the subfolder named ``src/main``. Specifically, your initially application is in the folder ``src/main/pt/up/fe/comp2023``, and the grammar is in the subfolder ``src/main/antlr/comp2023/grammar``. Finally, the subfolder named ``test`` contains your unit tests. - -## Compile and Running - -To compile and install the program, run ``gradle installDist``. This will compile your classes and create a launcher script in the folder ``./build/install/jmm/bin``. For convenience, there are two script files in the root folder, one for Windows (``jmm.bat``) and another for Linux (``jmm``), that call this launcher script. - -After compilation, a series of tests will be automatically executed. The build will stop if any test fails. Whenever you want to ignore the tests and build the program anyway, you can call Gradle with the flag ``-x test``. - - -## Tests - -The base repository comes with two classes that contains unitary tests in the package ``pt.up.fe.comp``, ``TutorialTest`` and `` GrammarTest``. The tests in ``TutorialTest`` should all pass just using the provided code. ``GrammarTest`` contains tests for the complete Java-- grammar, and most should fail. By the end of Checkpoint 1, all tests should pass. - -The class ``GrammarTest`` contains several static String variables at the beginning of the class where you should put the name of your rules for each type of rule that appears there. You have to set these variables to pass all tests. - -To test the program, run ``gradle test``. This will execute the build, and run the JUnit tests in the ``test`` folder. If you want to see output printed during the tests, use the flag ``-i`` (i.e., ``gradle test -i``). - -You can also see a test report by opening the file ``./build/reports/tests/test/index.html``. - - -### Reports -We also included in this project the class ``pt.up.fe.comp.jmm.report.Report``. This class is used to generate important reports, including error and warning messages, but also can be used to include debugging and logging information. E.g. When you want to generate an error, create a new Report with the ``Error`` type and provide the stage in which the error occurred. - -### Parser Interface - -We have included the interface ``pt.up.fe.comp.jmm.parser.JmmParser``, for which we already provide an example implementation in the file ``src/main/pt/up/fe/comp2023/SimpleParser.java``. - -To configure the name of the class of the JmmParser implementation that should be automatically used for tests, use the file ``config.properties`` (more details below). - -### Compilation Stages - -The project is divided in four compilation stages, that you will be developing during the semester. The stages are Parser, Analysis, Optimization and Backend, and for each of these stages there is a corresponding Java interface that you will have to implement (e.g. for the Parser stage, you have to implement the interface JmmParser). - - -### config.properties - -The testing framework, which uses the class ``pt.up.fe.comp.TestUtils``, has methods to test each of the four compilation stages (e.g., ``TestUtils.parse()`` for testing the Parser stage). - -In order for the test class to find your implementations for the stages, it uses the file ``config.properties`` that is in root of your repository. It has four fields, one for each stage (i.e. ``ParserClass``, ``AnalysisClass``, ``OptimizationClass``, ``BackendClass``), and initially it only has one value, ``pt.up.fe.comp2023.SimpleParser``, associated with the first stage. - -During the development of your compiler you will update this file in order to setup the classes that implement each of the compilation stages. +# Compilers Project (Group 7B) + +## 👥 Group members +- Isabel Amaral (up202006677) +- Mariana Rocha (up202004656) +- Milena Gouveia (up202008862) + +## 👩‍💻 Distribution of work +- Isabel Amaral (33%) - Jasmin +- Mariana Rocha (33%) - Ollir +- Milena Gouveia (33%) - Semantic Analysis and Optimizations + +## 🪛 Implemented optimizations +- Option `-o`: + - constant propagation + - constant folding +- Option `–r=` (register allocation): + - `n ≥ 1`: the compiler tries to use at most `` local variables when generating Jasmin instructions. It aborts and reports an error if `` is not enough to store the local variables. + - `n = −1`: This is the default value where the compiler uses as many variables as originally present in the OLLIR representation. + +## 🔎 Self-assessment +The developed project seems to be working as expected except for the optimization where the compiler tries to use the fewest registers as possible (option `-r 0`). However, considering that all the project requirements and all the remaining optimizations were implemented, we believe that our project deserves a grade of 19-19.5 out of 20. diff --git a/config.properties b/config.properties index e486eba..0c3cd06 100644 --- a/config.properties +++ b/config.properties @@ -2,11 +2,11 @@ ParserClass = pt.up.fe.comp2023.SimpleParser # The fully qualified name of your class that implements the interface JmmAnalysis -AnalysisClass = pt.up.fe.comp2023.Analysis +AnalysisClass = pt.up.fe.comp2023.semantic.Analysis # The fully qualified name of your class that implements the interface JmmOptimization -OptimizationClass = +OptimizationClass = pt.up.fe.comp2023.ollir.Optimization # The fully qualified name of your class that implements the interface JasminBackend -BackendClass = +BackendClass = pt.up.fe.comp2023.jasmin.JasminGenerator diff --git a/libs/comp2023-lib-sources.jar b/libs/comp2023-lib-sources.jar index 1d99b41..da2192c 100644 Binary files a/libs/comp2023-lib-sources.jar and b/libs/comp2023-lib-sources.jar differ diff --git a/libs/comp2023-lib.jar b/libs/comp2023-lib.jar index bf2de12..41cf62d 100644 Binary files a/libs/comp2023-lib.jar and b/libs/comp2023-lib.jar differ diff --git a/libs/comp2023-ollir-sources.jar b/libs/comp2023-ollir-sources.jar new file mode 100644 index 0000000..d6cfa16 Binary files /dev/null and b/libs/comp2023-ollir-sources.jar differ diff --git a/libs/comp2023-ollir.jar b/libs/comp2023-ollir.jar new file mode 100644 index 0000000..e31b06a Binary files /dev/null and b/libs/comp2023-ollir.jar differ diff --git a/libs/ollir-javadoc.jar b/libs/ollir-javadoc.jar deleted file mode 100644 index d098d56..0000000 Binary files a/libs/ollir-javadoc.jar and /dev/null differ diff --git a/libs/ollir-sources.jar b/libs/ollir-sources.jar deleted file mode 100644 index d4eefcc..0000000 Binary files a/libs/ollir-sources.jar and /dev/null differ diff --git a/libs/ollir.jar b/libs/ollir.jar deleted file mode 100644 index 8e856bf..0000000 Binary files a/libs/ollir.jar and /dev/null differ diff --git a/src/main/antlr/comp2023/grammar/Javamm.g4 b/src/main/antlr/comp2023/grammar/Javamm.g4 index 6787d82..68b3c62 100644 --- a/src/main/antlr/comp2023/grammar/Javamm.g4 +++ b/src/main/antlr/comp2023/grammar/Javamm.g4 @@ -49,26 +49,26 @@ type locals[boolean isArray = false] statement : '{' statement* '}' #CodeBlock - | 'if' '(' condition=expression ')' iftrue=statement 'else' iffalse=statement #Condition - | 'while' '(' condition=expression ')' whiletrue=statement #Cycle + | 'if' '(' expression ')' statement 'else' statement #Condition + | 'while' '(' expression ')' statement #Cycle | expression ';' #Expr - | varname=ID '=' value=expression ';' #Assignment - | arrayname=ID '[' index=expression ']' '=' value=expression ';' #ArrayAssignment + | varname=ID '=' expression ';' #Assignment + | arrayname=ID '[' expression ']' '=' expression ';' #ArrayAssignment ; expression : '(' expression ')' #ParenthesesExpr + | 'new' 'int' '[' expression ']' #ArrayCreation + | 'new' classname=ID '(' ')' #ObjectCreation + | expression '[' expression ']' #ArraySubscript + | expression '.' field='length' #LengthFieldAccess + | expression '.' methodcall=ID '(' methodCallParameters ')' #MethodCall | '!' expression #NegationExpr | expression op=('*' | '/') expression #ArithmeticExpr | expression op=('+' | '-') expression #ArithmeticExpr | expression op=('<' | '>') expression #ComparisonExpr | expression op='&&' expression #LogicalExpr | expression op='||' expression #LogicalExpr - | expression '[' expression ']' #ArraySubscript - | expression '.' field='length' #LengthFieldAccess - | expression '.' methodcall=ID '(' methodCallParameters ')' #MethodCall - | 'new' 'int' '[' expression ']' #ArrayCreation - | 'new' classname=ID '(' ')' #ObjectCreation | value=INT #Integer | value=('true' | 'false') #Boolean | 'this' #This diff --git a/src/main/pt/up/fe/comp2023/Launcher.java b/src/main/pt/up/fe/comp2023/Launcher.java index ba4c248..50073c9 100644 --- a/src/main/pt/up/fe/comp2023/Launcher.java +++ b/src/main/pt/up/fe/comp2023/Launcher.java @@ -6,9 +6,15 @@ import java.util.Map; import pt.up.fe.comp.TestUtils; -import pt.up.fe.comp.jmm.analysis.JmmSemanticsResult; import pt.up.fe.comp.jmm.parser.JmmParserResult; +import pt.up.fe.comp.jmm.analysis.JmmSemanticsResult; +import pt.up.fe.comp.jmm.ollir.OllirResult; +import pt.up.fe.comp2023.jasmin.JasminGenerator; +import pt.up.fe.comp.jmm.jasmin.JasminResult; import pt.up.fe.comp.jmm.report.Report; +import pt.up.fe.comp2023.ollir.Optimization; +import pt.up.fe.comp2023.semantic.Analysis; +import pt.up.fe.comp2023.semantic.MySymbolTable; import pt.up.fe.specs.util.SpecsIo; import pt.up.fe.specs.util.SpecsLogs; import pt.up.fe.specs.util.SpecsSystem; @@ -58,31 +64,70 @@ public static void main(String[] args) { JmmSemanticsResult semanticsResult = analysis.semanticAnalysis(parserResult); // Output Semantic Errors - for(Report report : analysis.getReports()){ + for (Report report : analysis.getReports()) { System.out.println(report.toString()); } // Check if there are semantic errors TestUtils.noErrors(semanticsResult.getReports()); - // ... add remaining stages + Optimization optimization = new Optimization(); + + // Apply Constant Propagation and Constant Folding optimizations + if (Boolean.parseBoolean(config.get("optimize"))) { + System.out.println("Applying optimizations..."); + + optimization.optimize(semanticsResult); + + // Output AST after optimizations + System.out.println(semanticsResult.getRootNode().toTree()); + } + + OllirResult ollirResult = optimization.toOllir(semanticsResult); + + // Optimize register allocation + if (Integer.parseInt(config.get("registerAllocation")) >= 0) + optimization.optimize(ollirResult); + + JasminGenerator jasminGenerator = new JasminGenerator(); + JasminResult jasminResult = jasminGenerator.toJasmin(ollirResult); + System.out.println(jasminResult.getJasminCode()); + + TestUtils.runJasmin(jasminResult.getJasminCode()); } private static Map parseArgs(String[] args) { SpecsLogs.info("Executing with args: " + Arrays.toString(args)); // Check if there is at least one argument - if (args.length != 1) { - throw new RuntimeException("Expected a single argument, a path to an existing input file."); - } + if (args.length < 1) + throw new RuntimeException("Usage: ./jmm [-o] [-p ]"); // Create config Map config = new HashMap<>(); config.put("inputFile", args[0]); + config.put("debug", "false"); config.put("optimize", "false"); config.put("registerAllocation", "-1"); - config.put("debug", "false"); + for (int i = 1; i < args.length; i++) { + if(args[i].equals("-o")) + config.put("optimize", "true"); + + else if(args[i].equals("-r")) { + if(i + 1 >= args.length) + throw new RuntimeException("Missing argument for -r option."); + else { + try { + int n = Integer.parseInt(args[i + 1]); + config.put("registerAllocation", Integer.toString(n)); + i++; + } catch (NumberFormatException e) { + System.out.println("Invalid argument for -r option: " + args[i + 1]); + } + } + } + } return config; } } diff --git a/src/main/pt/up/fe/comp2023/jasmin/JVMInstructionUtils.java b/src/main/pt/up/fe/comp2023/jasmin/JVMInstructionUtils.java new file mode 100644 index 0000000..9d28f2b --- /dev/null +++ b/src/main/pt/up/fe/comp2023/jasmin/JVMInstructionUtils.java @@ -0,0 +1,500 @@ +package pt.up.fe.comp2023.jasmin; + +import org.specs.comp.ollir.*; + +import java.util.*; + +import static java.lang.Integer.parseInt; +import static java.lang.Math.abs; +import static java.lang.Math.pow; + +public class JVMInstructionUtils { + + public static int numLocals = 0; + public static int stackSize = 0; + public static int currStackSize = 0; + public static Map varEquivalence = new HashMap<>(); + public static Map iincVars = new HashMap<>(); + + public static void increaseStackSize(int n) { + currStackSize += n; + if (currStackSize > stackSize) + stackSize = currStackSize; + } + + public static void decreaseStackSize(int n) { + currStackSize -= n; + if (currStackSize > stackSize) + stackSize = currStackSize; + } + + public static String getLoadInstruction(Element element, HashMap varTable) { + increaseStackSize(1); + if (element.isLiteral()) { + int literal = parseInt(((LiteralElement)element).getLiteral()); + if (literal >= 0 && literal <= 5) + return "\ticonst_" + literal + '\n'; + if (literal == -1) + return "\ticonst_m" + abs(literal) + '\n'; + if (abs(literal) < pow(2, 7)) + return "\tbipush " + literal + '\n'; + if (abs(literal) < pow(2, 15)) + return "\tsipush " + literal + '\n'; + return "\tldc " + literal + '\n'; + } + + ElementType elementType; + if (element instanceof ArrayOperand) + elementType = ElementType.ARRAYREF; + else + elementType = element.getType().getTypeOfElement(); + int virtualReg = varTable.get(((Operand)element).getName()).getVirtualReg(); + if (virtualReg > numLocals) + numLocals = virtualReg; + + switch (elementType) { + case THIS: + return "\taload_0\n"; + case STRING: case OBJECTREF: case ARRAYREF: + if (virtualReg >= 0 && virtualReg <= 3) + return "\taload_" + virtualReg + '\n'; + else + return "\taload " + virtualReg + '\n'; + case INT32: case BOOLEAN: + if (virtualReg >= 0 && virtualReg <= 3) + return "\tiload_" + virtualReg + '\n'; + else + return "\tiload " + virtualReg + '\n'; + } + return ""; + } + + public static String getArrayLoadInstruction(ArrayOperand array, HashMap varTable) { + String statementList = ""; + statementList += getLoadInstruction(array, varTable); + statementList += getLoadInstruction(array.getIndexOperands().get(0), varTable); + return statementList; + } + + public static String getStoreInstruction(Element element, HashMap varTable) { + decreaseStackSize(1); + int virtualReg = varTable.get(((Operand)element).getName()).getVirtualReg(); + if (virtualReg > numLocals) + numLocals = virtualReg; + + if (element.isLiteral()) { + int literal = parseInt(((LiteralElement)element).getLiteral()); + if (virtualReg >= 0 && virtualReg <= 3) + return "\tistore_" + virtualReg + '\n'; + else + return "\tistore " + virtualReg + '\n'; + } + + ElementType elementType = element.getType().getTypeOfElement(); + switch (elementType) { + case THIS: + return "\tastore_0\n"; + case STRING: case OBJECTREF: case ARRAYREF: + if (virtualReg >= 0 && virtualReg <= 3) + return "\tastore_" + virtualReg + '\n'; + else + return "\tastore " + virtualReg + '\n'; + case INT32: case BOOLEAN: + if (virtualReg >= 0 && virtualReg <= 3) + return "\tistore_" + virtualReg + '\n'; + else + return "\tistore " + virtualReg + '\n'; + } + return ""; + } + + public static String loadInvokeArguments(ArrayList listOfOperands, HashMap varTable) { + String statementList = ""; + for (Element argument: listOfOperands) { + statementList += getLoadInstruction(argument, varTable); + } + return statementList; + } + + public static String createInvokeInstructionArgument(CallInstruction instruction, boolean isStatic) { + return (isStatic ? ((Operand)instruction.getFirstArg()).getName() : + JasminUtils.getTypeDescriptor(instruction.getFirstArg().getType(), false)) + + "/" + JasminUtils.createMethodSignature( + ((LiteralElement)instruction.getSecondArg()).getLiteral().replace("\"", ""), + instruction.getListOfOperands(), + instruction.getReturnType(), + true + ); + } + + public static String getInvokeVirtualInstruction(CallInstruction instruction, HashMap varTable) { + String statementList = ""; + statementList += getLoadInstruction(instruction.getFirstArg(), varTable); + statementList += loadInvokeArguments(instruction.getListOfOperands(), varTable); + statementList += "\tinvokevirtual " + createInvokeInstructionArgument(instruction, false); + decreaseStackSize(instruction.getListOfOperands().size() + 1); + return statementList; + } + + public static String getInvokeStaticInstruction(CallInstruction instruction, HashMap varTable) { + String statementList = ""; + statementList += loadInvokeArguments(instruction.getListOfOperands(), varTable); + statementList += "\tinvokestatic " + createInvokeInstructionArgument(instruction, true); + decreaseStackSize(instruction.getListOfOperands().size()); + return statementList; + } + + public static String getInvokeSpecialInstruction(CallInstruction instruction, HashMap varTable) { + return "\tinvokespecial " + createInvokeInstructionArgument(instruction, false); + } + + public static String getNewInstruction(CallInstruction instruction, HashMap varTable) { + String statementList = ""; + statementList += loadInvokeArguments(instruction.getListOfOperands(), varTable); + statementList += "\tnew " + ((Operand)instruction.getFirstArg()).getName() + '\n'; + statementList += "\tdup\n"; + increaseStackSize(2); + return statementList; + } + + public static String getNewArrayInstruction(CallInstruction instruction, HashMap varTable) { + String statementList = ""; + statementList += loadInvokeArguments(instruction.getListOfOperands(), varTable); + statementList += "\tnewarray int\n"; + return statementList; + } + + public static String getArrayLengthInstruction(CallInstruction instruction, HashMap varTable) { + String statementList = ""; + statementList += getLoadInstruction(instruction.getFirstArg(), varTable); + statementList += "\tarraylength\n"; + return statementList; + } + + public static String createUnaryOpStatement(UnaryOpInstruction instruction, HashMap varTable) { + String statementList = ""; + statementList += getLoadInstruction(instruction.getOperand(), varTable); + + switch (instruction.getOperation().getOpType()) { + case NOT: case NOTB: + statementList += "\tifeq "; + statementList += createAuxBranchStatement(); + decreaseStackSize(1); + break; + } + return statementList; + } + + public static String checkInc(BinaryOpInstruction instruction, Element dest, HashMap varTable) { + OperationType operationType = instruction.getOperation().getOpType(); + Element leftOperand = instruction.getLeftOperand(); + Element rightOperand = instruction.getRightOperand(); + String destName = ((Operand)dest).getName(); + String iincVarEquivalent = varEquivalence.get(destName); + String increment = ""; + + if ((operationType == OperationType.ADD || operationType == OperationType.SUB) && + !(leftOperand instanceof LiteralElement) && + rightOperand instanceof LiteralElement) { + if (operationType == OperationType.ADD && parseInt(((LiteralElement)rightOperand).getLiteral()) <= 127) + increment = ((LiteralElement)rightOperand).getLiteral(); + else if (operationType == OperationType.SUB && parseInt(((LiteralElement)rightOperand).getLiteral()) <= 128) + increment = "-" + ((LiteralElement)rightOperand).getLiteral(); + if (!Objects.equals(increment, "") && iincVarEquivalent != null && + iincVarEquivalent.equals(((Operand) leftOperand).getName())) + iincVars.put(iincVarEquivalent, destName); + if (!Objects.equals(increment, "") && (destName.equals(((Operand) leftOperand).getName()) || + (iincVarEquivalent != null && iincVarEquivalent.equals(((Operand) leftOperand).getName())))) + return "\tiinc " + varTable.get(((Operand) leftOperand).getName()).getVirtualReg() + + " " + increment + "\n"; + } + + if ((operationType == OperationType.ADD || operationType == OperationType.SUB) && + leftOperand instanceof LiteralElement && + !(rightOperand instanceof LiteralElement)) { + if (operationType == OperationType.ADD && parseInt(((LiteralElement)leftOperand).getLiteral()) <= 127) + increment = ((LiteralElement)leftOperand).getLiteral(); + else if (operationType == OperationType.SUB && parseInt(((LiteralElement)leftOperand).getLiteral()) <= 128) + increment = "-" + ((LiteralElement)leftOperand).getLiteral(); + if (!Objects.equals(increment, "") && iincVarEquivalent != null && + iincVarEquivalent.equals(((Operand)rightOperand).getName())) + iincVars.put(iincVarEquivalent, destName); + if (!Objects.equals(increment, "") && (destName.equals(((Operand)rightOperand).getName()) || + (iincVarEquivalent != null && iincVarEquivalent.equals(((Operand)rightOperand).getName())))) + return "\tiinc " + varTable.get(((Operand) rightOperand).getName()).getVirtualReg() + + " " + increment + "\n"; + } + + return ""; + } + + public static String createArithmeticInstruction(OperationType operationType) { + decreaseStackSize(1); + + switch (operationType) { + case ADD: + return "\tiadd\n"; + case SUB: + return "\tisub\n"; + case MUL: + return "\timul\n"; + case DIV: + return "\tidiv\n"; + } + return ""; + } + + public static String createLogicalInstruction(OperationType operationType) { + decreaseStackSize(1); + + switch (operationType) { + case AND: case ANDB: + return "\tiand\n"; + case OR: case ORB: + return "\tior\n"; + } + return ""; + } + + public static String createComparisonInstruction(OperationType operationType, boolean isBranchCond) { + decreaseStackSize(2); + + switch (operationType) { + case LTH: + return isBranchCond ? "\tif_icmplt " : "\tif_icmplt " + createAuxBranchStatement(); + case LTE: + return isBranchCond ? "\tif_icmple " : "\tif_icmple " + createAuxBranchStatement(); + case GTH: + return isBranchCond ? "\tif_icmpgt " : "\tif_icmpgt " + createAuxBranchStatement(); + case GTE: + return isBranchCond ? "\tif_icmpge " : "\tif_icmpge " + createAuxBranchStatement(); + } + return ""; + } + + public static String createZeroComparisonInstruction(OperationType operationType, boolean isBranchCond) { + decreaseStackSize(1); + + switch (operationType) { + case LTH: + return isBranchCond ? "\tiflt " : "\tiflt " + createAuxBranchStatement(); + case LTE: + return isBranchCond ? "\tifle " : "\tifle " + createAuxBranchStatement(); + case GTH: + return isBranchCond ? "\tifgt " : "\tifgt " + createAuxBranchStatement(); + case GTE: + return isBranchCond ? "\tifge " : "\tifge " + createAuxBranchStatement(); + } + return ""; + } + + public static String createBinaryOpInstruction(BinaryOpInstruction instruction, HashMap varTable, boolean isBranchCond) { + OperationType operationType = instruction.getOperation().getOpType(); + Element leftOperand = instruction.getLeftOperand(); + Element rightOperand = instruction.getRightOperand(); + String statementList = ""; + + switch (operationType) { + case ADD: case SUB: case MUL: case DIV: + statementList += getLoadInstruction(leftOperand, varTable); + statementList += getLoadInstruction(rightOperand, varTable); + statementList += createArithmeticInstruction(operationType); + break; + case AND: case ANDB: case OR: case ORB: + statementList += getLoadInstruction(leftOperand, varTable); + statementList += getLoadInstruction(rightOperand, varTable); + statementList += createLogicalInstruction(operationType); + break; + case LTH: case LTE: case GTH: case GTE: + if (leftOperand instanceof LiteralElement && parseInt(((LiteralElement)leftOperand).getLiteral()) == 0) { + statementList += getLoadInstruction(rightOperand, varTable); + statementList += createZeroComparisonInstruction(operationType, isBranchCond); + } else if (rightOperand instanceof LiteralElement && parseInt(((LiteralElement)rightOperand).getLiteral()) == 0) { + statementList += getLoadInstruction(leftOperand, varTable); + statementList += createZeroComparisonInstruction(operationType, isBranchCond); + } else { + statementList += getLoadInstruction(leftOperand, varTable); + statementList += getLoadInstruction(rightOperand, varTable); + statementList += createComparisonInstruction(operationType, isBranchCond); + } + break; + } + return statementList; + } + + public static String createNoperInstruction(SingleOpInstruction instruction, HashMap varTable) { + Element operand = instruction.getSingleOperand(); + if (operand instanceof ArrayOperand) { + String statementList = ""; + statementList += getArrayLoadInstruction((ArrayOperand)operand, varTable); + statementList += "\tiaload\n"; + decreaseStackSize(1); + return statementList; + } + return getLoadInstruction(operand, varTable); + } + + public static boolean checkTempAssign(AssignInstruction instruction) { + Operand lhs = ((Operand)((AssignInstruction)instruction).getDest()); + Instruction rhsInstruction = ((AssignInstruction)instruction).getRhs(); + if (!(rhsInstruction instanceof SingleOpInstruction)) + return false; + return ((SingleOpInstruction) rhsInstruction).getSingleOperand() instanceof Operand; + } + + public static String createAssignStatement(AssignInstruction instruction, HashMap varTable) { + Element assignElement = instruction.getDest(); + String statementList = ""; + + if (checkTempAssign(instruction)) { + Element rhsElement = ((SingleOpInstruction)instruction.getRhs()).getSingleOperand(); + String iincVarEquivalent = iincVars.get(((Operand)assignElement).getName()); + if (iincVarEquivalent != null && iincVarEquivalent.equals(((Operand)rhsElement).getName())) + return ""; + } + + if (instruction.getRhs() instanceof BinaryOpInstruction) { + statementList = checkInc((BinaryOpInstruction)instruction.getRhs(), assignElement, varTable); + if (!statementList.equals("")) + return statementList; + } + + if (assignElement instanceof ArrayOperand) + statementList += getArrayLoadInstruction((ArrayOperand)assignElement, varTable); + statementList += JasminUtils.handleInstruction(instruction.getRhs(), varTable, true); + if (assignElement instanceof ArrayOperand) { + statementList += "\tiastore\n"; + decreaseStackSize(3); + } + else + statementList += getStoreInstruction(assignElement, varTable); + return statementList; + } + + public static String createCallStatement(CallInstruction instruction, HashMap varTable) { + String statementList = ""; + + switch (instruction.getInvocationType()) { + case NEW: + if (Objects.equals(((Operand) instruction.getFirstArg()).getName(), "array")) + statementList += getNewArrayInstruction(instruction, varTable); + else + statementList += getNewInstruction(instruction, varTable); + break; + case invokespecial: + statementList += getInvokeSpecialInstruction(instruction, varTable); + break; + case invokestatic: + statementList += getInvokeStaticInstruction(instruction, varTable); + break; + case invokevirtual: + statementList += getInvokeVirtualInstruction(instruction, varTable); + break; + case arraylength: + statementList += getArrayLengthInstruction(instruction, varTable); + break; + case ldc: + statementList += "\tldc " + ((LiteralElement)instruction.getFirstArg()).getLiteral() + '\n'; + increaseStackSize(1); + break; + } + return statementList; + } + + public static String createGetfieldStatement(GetFieldInstruction instruction, HashMap varTable) { + String statementList = ""; + statementList += getLoadInstruction(instruction.getFirstOperand(), varTable); + statementList += "\tgetfield " + + JasminUtils.getTypeDescriptor(instruction.getFirstOperand().getType(), false) + + "/" + ((Operand)instruction.getSecondOperand()).getName() + " " + + JasminUtils.getTypeDescriptor(instruction.getFieldType(), true) + '\n'; + return statementList; + } + + public static String createPutfieldStatement(PutFieldInstruction instruction, HashMap varTable) { + ArrayList arguments = new ArrayList<>(); + arguments.add(instruction.getThirdOperand()); + + String statementList = ""; + statementList += getLoadInstruction(instruction.getFirstOperand(), varTable); + statementList += loadInvokeArguments(arguments, varTable); + statementList += "\tputfield " + + JasminUtils.getTypeDescriptor(instruction.getFirstOperand().getType(), false) + + '/' + ((Operand)instruction.getSecondOperand()).getName() + " " + + JasminUtils.getTypeDescriptor(instruction.getThirdOperand().getType(), true) + '\n'; + decreaseStackSize(arguments.size() + 1); + return statementList; + } + + public static String createSingleOpConditionStatement(SingleOpCondInstruction instruction, HashMap varTable) { + String statementList = ""; + statementList += createNoperInstruction(instruction.getCondition(), varTable); + statementList += "\tifne " + instruction.getLabel() + "\n"; + decreaseStackSize(1); + return statementList; + } + + public static String createOpConditionStatement(OpCondInstruction instruction, HashMap varTable) { + String statementList = ""; + if (instruction.getCondition() instanceof BinaryOpInstruction) + statementList += createBinaryOpInstruction((BinaryOpInstruction)instruction.getCondition(), varTable, true); + else + statementList += createUnaryOpStatement((UnaryOpInstruction)instruction.getCondition(), varTable); + statementList += instruction.getLabel() + "\n"; + return statementList; + } + + public static String createBranchStatement(CondBranchInstruction instruction, HashMap varTable) { + if (instruction instanceof SingleOpCondInstruction) + return createSingleOpConditionStatement((SingleOpCondInstruction)instruction, varTable); + if (instruction instanceof OpCondInstruction) + return createOpConditionStatement((OpCondInstruction)instruction, varTable); + return ""; + } + + public static String createGotoStatement(GotoInstruction instruction, HashMap varTable) { + return "\tgoto " + instruction.getLabel() + "\n"; + } + + public static String createAuxBranchStatement() { + String statementList = ""; + // goto true section + statementList += "true_" + JasminUtils.customLabelCounter + "\n"; + JasminUtils.customLabelCounter++; + // if condition is false + statementList += "\ticonst_0\n"; + increaseStackSize(1); + // skip true section + statementList += "\tgoto false_" + JasminUtils.customLabelCounter + "\n"; + JasminUtils.customLabelCounter++; + // true section + statementList += "\ttrue_" + (JasminUtils.customLabelCounter - 2) + ":\n"; + // if condition is true + statementList += "\ticonst_1\n"; + increaseStackSize(1); + // false section (for skipping true section) + statementList += "\tfalse_" + (JasminUtils.customLabelCounter - 1) + ":\n"; + return statementList; + } + + public static String createReturnStatement(ReturnInstruction instruction, HashMap varTable) { + ElementType returnType = instruction.getElementType(); + Element returnElement = instruction.getOperand(); + String statementList = ""; + + switch (returnType) { + case VOID: + return "\treturn\n"; + case INT32: case BOOLEAN: + statementList += getLoadInstruction(returnElement, varTable); + statementList += "\tireturn\n"; + decreaseStackSize(1); + break; + case STRING: case OBJECTREF: case ARRAYREF: case THIS: + statementList += getLoadInstruction(returnElement, varTable); + statementList += "\tareturn\n"; + decreaseStackSize(1); + } + return statementList; + } +} diff --git a/src/main/pt/up/fe/comp2023/jasmin/JasminGenerator.java b/src/main/pt/up/fe/comp2023/jasmin/JasminGenerator.java new file mode 100644 index 0000000..c1ab0e2 --- /dev/null +++ b/src/main/pt/up/fe/comp2023/jasmin/JasminGenerator.java @@ -0,0 +1,48 @@ +package pt.up.fe.comp2023.jasmin; + +import org.specs.comp.ollir.ClassUnit; +import org.specs.comp.ollir.Field; +import org.specs.comp.ollir.Method; +import pt.up.fe.comp.jmm.jasmin.JasminBackend; +import pt.up.fe.comp.jmm.jasmin.JasminResult; +import pt.up.fe.comp.jmm.ollir.OllirResult; +import pt.up.fe.comp.jmm.report.Report; + +import java.util.ArrayList; + +public class JasminGenerator implements JasminBackend { + + @Override + public JasminResult toJasmin(OllirResult ollirResult) { + String jasminCode = generateJasminCode(ollirResult.getOllirClass()); + return new JasminResult(ollirResult, jasminCode, new ArrayList()); + } + + private String generateJasminCode(ClassUnit classUnit) { + return createHeader(classUnit) + '\n' + + createFieldDefinitions(classUnit) + '\n' + + createMethodDefinitions(classUnit); + } + + public static String createHeader(ClassUnit classUnit) { + return JasminUtils.createClassDirective(classUnit) + + JasminUtils.createSuperDirective(classUnit); + } + + public static String createFieldDefinitions(ClassUnit classUnit) { + String fieldDefinitions = ""; + for (Field field: classUnit.getFields()) + fieldDefinitions += JasminUtils.createFieldDirective(field); + return fieldDefinitions; + } + + public static String createMethodDefinitions(ClassUnit classUnit) { + String methodDefinitions = ""; + for (Method method: classUnit.getMethods()) + if (method.isConstructMethod()) + methodDefinitions += JasminUtils.createConstructMethod(classUnit.getSuperClass()); + else + methodDefinitions += JasminUtils.createMethodDirective(method); + return methodDefinitions; + } +} diff --git a/src/main/pt/up/fe/comp2023/jasmin/JasminUtils.java b/src/main/pt/up/fe/comp2023/jasmin/JasminUtils.java new file mode 100644 index 0000000..f9811c1 --- /dev/null +++ b/src/main/pt/up/fe/comp2023/jasmin/JasminUtils.java @@ -0,0 +1,236 @@ +package pt.up.fe.comp2023.jasmin; + +import org.specs.comp.ollir.*; + +import java.util.ArrayList; +import java.util.HashMap; + +public class JasminUtils { + + public static int customLabelCounter = 0; + + public static String getTypeDescriptor(Type type, boolean isDeclaration) { + ElementType elementType = type.getTypeOfElement(); + if (elementType.equals(ElementType.INT32)) + return "I"; + if (elementType.equals(ElementType.BOOLEAN)) + return "Z"; + if (elementType.equals(ElementType.VOID)) + return "V"; + if (elementType.equals(ElementType.STRING)) + return (isDeclaration) ? "Ljava/lang/String;" : "Ljava/lang/String"; + if (elementType.equals(ElementType.OBJECTREF)) + return (isDeclaration) ? "L" + ((ClassType)type).getName() + ";" : ((ClassType)type).getName(); + if (elementType.equals(ElementType.CLASS) || + elementType.equals(ElementType.THIS)) + return ((ClassType)type).getName(); + if (elementType.equals(ElementType.ARRAYREF)) + return "[".repeat(((ArrayType)type).getNumDimensions()) + + getTypeDescriptor(((ArrayType)type).getElementType(), isDeclaration); + return ""; + } + + public static String createClassDirective(ClassUnit classUnit) { + String classDirective = ".class "; + if (classUnit.getClassAccessModifier() != AccessModifiers.DEFAULT) + classDirective += classUnit.getClassAccessModifier().toString().toLowerCase() + " "; + if (classUnit.isFinalClass()) + classDirective += "final "; + if (classUnit.getPackage() != null) + classDirective += classUnit.getPackage() + '/'; + classDirective += classUnit.getClassName(); + + return classDirective + '\n'; + } + + public static String createSuperDirective(ClassUnit classUnit) { + String superClassDirective = ".super "; + if (classUnit.getSuperClass() != null) + superClassDirective += classUnit.getSuperClass(); + else + superClassDirective += "java/lang/Object"; + + return superClassDirective + '\n'; + } + + public static String createFieldDirective(Field field) { + String fieldDirective = ".field "; + if (field.getFieldAccessModifier() != AccessModifiers.DEFAULT) + fieldDirective += field.getFieldAccessModifier().toString().toLowerCase() + " "; + if (field.isStaticField()) + fieldDirective += "static "; + if (field.isFinalField()) + fieldDirective += "final "; + fieldDirective += field.getFieldName() + " "; + fieldDirective += getTypeDescriptor(field.getFieldType(), true); + if (field.isInitialized()) + fieldDirective += " = " + field.getInitialValue(); + + return fieldDirective + '\n'; + } + + public static String createConstructMethod(String superClassName) { + String methodDirective = ".method public ()V\n"; + methodDirective += "\taload_0\n"; + methodDirective += "\tinvokespecial "; + if (superClassName != null) + methodDirective += superClassName; + else + methodDirective += "java/lang/Object"; + methodDirective += "/()V\n"; + methodDirective += "\treturn\n"; + return methodDirective + ".end method\n\n"; + } + + public static String createMethodSignature(String methodName, ArrayList listOfParameters, Type returnType, boolean isDeclaration) { + String methodSignature = ""; + methodSignature += methodName + "("; + for (Element parameter: listOfParameters) + methodSignature += getTypeDescriptor(parameter.getType(), isDeclaration); + methodSignature += ")" + getTypeDescriptor(returnType, isDeclaration) + '\n'; + return methodSignature; + } + + public static String createMethodDeclaration(Method method) { + String methodDirective = ""; + if (method.getMethodAccessModifier() != AccessModifiers.DEFAULT) + methodDirective += method.getMethodAccessModifier().toString().toLowerCase() + " "; + if (method.isStaticMethod()) + methodDirective += "static "; + if (method.isFinalMethod()) + methodDirective += "final "; + methodDirective += createMethodSignature( + method.getMethodName(), + method.getParams(), + method.getReturnType(), + true + ); + return methodDirective; + } + + public static String handleInstruction(Instruction instruction, HashMap varTable, boolean isRhs) { + String statementList = ""; + switch (instruction.getInstType()) { + case ASSIGN: + statementList += JVMInstructionUtils.createAssignStatement( + (AssignInstruction)instruction, + varTable + ); + break; + case CALL: + statementList += JVMInstructionUtils.createCallStatement( + (CallInstruction)instruction, + varTable + ); + if (!isRhs && ((CallInstruction)instruction).getReturnType().getTypeOfElement() != ElementType.VOID) { + statementList += "\tpop\n"; + JVMInstructionUtils.decreaseStackSize(1); + } + break; + case GOTO: + statementList += JVMInstructionUtils.createGotoStatement( + (GotoInstruction)instruction, + varTable + ); + break; + case BRANCH: + statementList += JVMInstructionUtils.createBranchStatement( + (CondBranchInstruction)instruction, + varTable + ); + break; + case RETURN: + statementList += JVMInstructionUtils.createReturnStatement( + (ReturnInstruction)instruction, + varTable + ); + break; + case GETFIELD: + statementList += JVMInstructionUtils.createGetfieldStatement( + (GetFieldInstruction)instruction, + varTable + ); + break; + case PUTFIELD: + statementList += JVMInstructionUtils.createPutfieldStatement( + (PutFieldInstruction)instruction, + varTable + ); + break; + case UNARYOPER: + statementList += JVMInstructionUtils.createUnaryOpStatement( + (UnaryOpInstruction)instruction, + varTable + ); + break; + case BINARYOPER: + statementList += JVMInstructionUtils.createBinaryOpInstruction( + (BinaryOpInstruction)instruction, + varTable, + false + ); + break; + case NOPER: + statementList += JVMInstructionUtils.createNoperInstruction( + (SingleOpInstruction)instruction, + varTable + ); + break; + } + return statementList; + } + + public static String handleMethodStatements(Method method) { + String statementList = ""; + for (Instruction instruction: method.getInstructions()) { + String aux = ""; + if (instruction instanceof CallInstruction && ((CallInstruction)instruction).getInvocationType() == CallType.invokespecial) { + aux = statementList.substring(statementList.lastIndexOf('\t')); + statementList = statementList.substring(0, statementList.lastIndexOf('\t')); + } + + for (String label: method.getLabels(instruction)) + statementList += "\t" + label + ":\n"; + statementList += handleInstruction(instruction, method.getVarTable(), false); + statementList += aux; + } + return statementList; + } + + public static void createVarEquivalence(Method method) { + JVMInstructionUtils.iincVars.clear(); + JVMInstructionUtils.varEquivalence.clear(); + JVMInstructionUtils.iincVars.clear(); + + for (Instruction instruction: method.getInstructions()) { + if (instruction instanceof AssignInstruction && JVMInstructionUtils.checkTempAssign((AssignInstruction)instruction)) { + Operand lhs = ((Operand)((AssignInstruction)instruction).getDest()); + Operand rhs = ((Operand)((SingleOpInstruction)(((AssignInstruction)instruction).getRhs())).getSingleOperand()); + JVMInstructionUtils.varEquivalence.put(rhs.getName(), lhs.getName()); + } + } + } + + public static String createMethodDirective(Method method) { + JVMInstructionUtils.numLocals = 0; + JVMInstructionUtils.stackSize = 0; + JVMInstructionUtils.currStackSize = 0; + createVarEquivalence(method); + + String instructions = handleMethodStatements(method); + if (method.isStaticMethod() && method.getParams().size() > 0) + JVMInstructionUtils.numLocals++; + else if (!method.isStaticMethod()) { + if (JVMInstructionUtils.numLocals < method.getParams().size()) + JVMInstructionUtils.numLocals += method.getParams().size(); + JVMInstructionUtils.numLocals++; + } + + String methodDirective = ".method "; + methodDirective += createMethodDeclaration(method); + methodDirective += "\t.limit stack " + JVMInstructionUtils.stackSize + "\n"; + methodDirective += "\t.limit locals " + JVMInstructionUtils.numLocals + "\n"; + methodDirective += instructions; + return methodDirective + ".end method\n\n"; + } +} diff --git a/src/main/pt/up/fe/comp2023/ollir/OllirUtils.java b/src/main/pt/up/fe/comp2023/ollir/OllirUtils.java new file mode 100644 index 0000000..87548be --- /dev/null +++ b/src/main/pt/up/fe/comp2023/ollir/OllirUtils.java @@ -0,0 +1,24 @@ +package pt.up.fe.comp2023.ollir; + +import pt.up.fe.comp.jmm.analysis.table.Type; + +public class OllirUtils { + + public static String ollirTypes(Type type){ + String typeS = ""; // array needs to be checked first + + if (type.isArray()) + typeS = ".array"; + + if (type.getName().equals("boolean")) + typeS += ".bool"; + else if (type.getName().equals("int")) + typeS += ".i32"; + else if (type.getName().equals("void")) + typeS += ".V"; + else + typeS += "." + type.getName(); + + return typeS; + } +} diff --git a/src/main/pt/up/fe/comp2023/ollir/Optimization.java b/src/main/pt/up/fe/comp2023/ollir/Optimization.java new file mode 100644 index 0000000..deda0a0 --- /dev/null +++ b/src/main/pt/up/fe/comp2023/ollir/Optimization.java @@ -0,0 +1,610 @@ +package pt.up.fe.comp2023.ollir; + +import org.specs.comp.ollir.*; +import pt.up.fe.comp.jmm.analysis.JmmSemanticsResult; +import pt.up.fe.comp.jmm.analysis.table.Symbol; +import pt.up.fe.comp.jmm.analysis.table.SymbolTable; +import pt.up.fe.comp.jmm.ast.AJmmVisitor; +import pt.up.fe.comp.jmm.ast.JmmNode; +import pt.up.fe.comp.jmm.ollir.JmmOptimization; +import pt.up.fe.comp.jmm.ollir.OllirResult; +import pt.up.fe.comp.jmm.report.Report; +import pt.up.fe.comp2023.optimization.*; + +import java.util.ArrayList; +import java.util.List; + +public class Optimization extends AJmmVisitor implements JmmOptimization { + String code = ""; + String temp; + List reports = new ArrayList<>(); + private SymbolTable table; + int tempVarId = 0; + + @Override + public OllirResult toOllir(JmmSemanticsResult semanticsResult) { + this.table = semanticsResult.getSymbolTable(); + visit(semanticsResult.getRootNode()); + code += "} \n"; + System.out.println(code); + return new OllirResult(semanticsResult, code, reports); + } + + @Override + public JmmSemanticsResult optimize(JmmSemanticsResult semanticsResult) { + if (Boolean.parseBoolean(semanticsResult.getConfig().get("optimize"))) { + ConstantPropagation constantPropagation = new ConstantPropagation(semanticsResult); + ConstantFolding constantFolding = new ConstantFolding(semanticsResult); + + boolean codeModified = constantPropagation.apply() || constantFolding.apply(); + while (codeModified) { + codeModified = constantPropagation.apply() || constantFolding.apply(); + } + } + return semanticsResult; + } + + public OllirResult optimize(OllirResult ollirResult) { + int registerAllocationOption = Integer.parseInt(ollirResult.getConfig().getOrDefault("registerAllocation", "-1")); + + if (registerAllocationOption >= 0){ + ClassUnit classUnit = ollirResult.getOllirClass(); + for (Method method : classUnit.getMethods()) + new RegisterAllocation(method, registerAllocationOption); + } + return ollirResult; + } + + @Override + protected void buildVisitor() { + setDefaultVisit(this::visitAllChildren); + addVisit("ClassDecl", this::dealWithClass); // Dealing with imports in here + addVisit("MethodDecl", this::dealWithMethod); + addVisit("VoidMethodDecl", this::dealWithVoidMethod); + addVisit("MainMethodDecl", this::dealWithMainMethod); + addVisit("MethodDeclParameters", this::dealWithParamDecl); + addVisit("MethodParameters", this::dealWithMethodCallParam); + addVisit("VarDecl", this::dealWithVarDecl); + addVisit("CodeBlock", this::dealWithCodeBlock); + addVisit("Condition", this::dealWithCondition); + addVisit("Cycle", this::dealWithCycle); + addVisit("Expr", this::dealWithExpr); + addVisit("Assignment", this::dealWithAssignment); + addVisit("ArrayAssignment", this::dealWithArrayAssignment); + addVisit("ParenthesesExpr", this::dealWithParenthesesExpr); + addVisit("NegationExpr", this::dealWithNegationExpr); + addVisit("ArithmeticExpr", this::dealWithArithmetic); + addVisit("ComparisonExpr", this::dealWithComparison); + addVisit("LogicalExpr", this::dealWithLogicalExpr); + addVisit("ArraySubscript", this::dealWithArraySubscript); + addVisit("LengthFieldAccess", this::dealWithLenFieldAccess); + addVisit("MethodCall", this::dealWithMethodCall); + addVisit("ArrayCreation", this::dealWithArrayCreation); + addVisit("ObjectCreation", this::dealWithObjectCreation); + addVisit("Integer", this::dealWithInteger); + addVisit("Boolean", this::dealWithBoolean); + addVisit("This", this::dealWithThis); + addVisit("Identifier", this::dealWithIdentifier); + } + + private Void dealWithCycle(JmmNode jmmNode, Void unused) { + code += "\t\t"; visit(jmmNode.getJmmChild(0)); code += "\n"; + var ifId = tempVarId++; + + // Condition statement - negation + code += "\t\tif (!.bool " + jmmNode.getJmmChild(0).get("valueOl") + ") goto end_loop" + ifId + ";\n"; + + // What occurs if the condition is met + code += "\t\tloop" + ifId + ":\n\t"; visit(jmmNode.getJmmChild(1)); + code += "\t\t"; visit(jmmNode.getJmmChild(0)); code += "\n"; + code += "\t\t if( " + jmmNode.getJmmChild(0).get("valueOl") + ") goto loop" + ifId + ";\n"; + + // End of If + code +="\t\tend_loop" + ifId + ":\n\t"; + + + return null; + } + + private Void dealWithCondition(JmmNode jmmNode, Void unused) { + code += "\t\t"; visit(jmmNode.getJmmChild(0)); + var ifId = tempVarId++; + + // Condition statement + code += "\t\tif (" + jmmNode.getJmmChild(0).get("valueOl") + ") goto if" + ifId + ";\n"; + + // What occurs if the condition isn't met + code += "\t\t\t"; visit(jmmNode.getJmmChild(2)); + code += "\t\t\tgoto endif" + ifId + ";\n"; + + // What occurs if the condition is met + code += "\t\tif" + ifId + ":\n\t"; visit(jmmNode.getJmmChild(1)); + + // End of If + code +="\t\tendif" + ifId + ":\n\t"; + + + return null; + } + + private Void dealWithIdentifier(JmmNode jmmNode, Void unused) { + var method = jmmNode.getAncestor("MethodDecl"); + var voidMethod = jmmNode.getAncestor("VoidMethodDecl"); + var mainMethod = jmmNode.getAncestor("MainMethodDecl"); + Symbol var = null; + boolean isLocal = false, isParam = false, isField = false; + int idParam = 0; + + if (method.isPresent() || voidMethod.isPresent() || mainMethod.isPresent()) { + var methodName = mainMethod.isPresent() ? "main" : method.orElseGet(voidMethod::get).get("methodname"); + List localVarClass = table.getLocalVariables(methodName); + List paramsOnClass = table.getParameters(methodName); + + // Check if local var + for (Symbol lv : localVarClass) + if (lv.getName().equals(jmmNode.get("value"))) { + var = lv; + isLocal = true; + } + + // If not local var, then check if param + if (var == null) + for (int p = 0; p < paramsOnClass.size(); p++) + if (paramsOnClass.get(p).getName().equals(jmmNode.get("value"))) { + var = paramsOnClass.get(p); + isParam = true; + idParam = p + 1; + } + } + + List fields = table.getFields(); + + // If not local nor param, check if field + if (var == null) + for (Symbol f : fields) + if (f.getName().equals(jmmNode.get("value"))) { + var = f; + isField = true; + } + + // If found, send it with its type + if (var != null) { + String ttype = OllirUtils.ollirTypes(var.getType()); + if (isLocal) + jmmNode.put("valueOl", jmmNode.get("value") + ttype); + else if (isParam) + jmmNode.put("valueOl", "$" + idParam + "." + jmmNode.get("value") + ttype); + else if (isField) { + temp = "t" + tempVarId++ + ttype; + jmmNode.put("valueOl", temp); + code += temp + " :=" + ttype + " getfield(this , " + jmmNode.get("value") + ttype + ")" + ttype + ";"; + } + } + // If it's not any of the above, then consider it's in an import + else + jmmNode.put("valueOl", jmmNode.get("value")); + return null; + } + + private Void dealWithThis(JmmNode jmmNode, Void unused) { + jmmNode.put("valueOl", "this." + table.getClassName()); + return null; + } + + private Void dealWithBoolean(JmmNode jmmNode, Void unused) { + jmmNode.put("valueOl", jmmNode.get("value").equals("true") ? "1.bool" : "0.bool"); + return null; + } + + private Void dealWithInteger(JmmNode jmmNode, Void unused) { + jmmNode.put("valueOl", jmmNode.get("value") + ".i32"); + return null; + } + + private Void dealWithObjectCreation(JmmNode jmmNode, Void unused) { + String rightString = jmmNode.get("classname"); + + temp = "t" + tempVarId++ + "." + rightString; + + code += temp + " :=." + rightString + " new(" + rightString + ")." + rightString + ";\n"; + code += "\t\tinvokespecial(" + temp + ",\"\").V;\n"; + + jmmNode.put("valueOl", temp); + + return null; + } + + private Void dealWithArrayCreation(JmmNode jmmNode, Void unused) { + visit(jmmNode.getJmmChild(0)); + code += "\t\tt" + tempVarId + ".array.i32 :=.array.i32 new(array, " + jmmNode.getJmmChild(0).get("valueOl") +").array.i32;\n"; + + jmmNode.put("valueOl", "t" + tempVarId++ + ".array.i32" ); + + return null; + } + + private Void dealWithMethodCall(JmmNode jmmNode, Void unused) { + JmmNode left = jmmNode.getJmmChild(0); + visit(left); + String methodName = jmmNode.get("methodcall"); + String returnType = ".V"; + boolean isStatic = false; + boolean makeTemp = !jmmNode.getJmmParent().getKind().equals("Expr"); + + for (String m : table.getMethods()) + if (m.equals(methodName)) + returnType = OllirUtils.ollirTypes(table.getReturnType(m)); + + + JmmNode params = jmmNode.getJmmChild(1); + for (var child : params.getChildren()) { + visit(child); + } + + temp = "t" + tempVarId++ + returnType; + if (makeTemp) + code += temp + " :=" + returnType + " "; + + if (left.getKind().equals("This")) { + code += "invokevirtual("; + } else { + if (table.getImports().contains(left.get("valueOl"))) { + code += "invokestatic(" + left.get("valueOl") + " , \"" + methodName + "\""; // The first arg is the object that calls the method and the second is the name of the method called + isStatic = true; + } else + code += "invokevirtual("; + } + + // Case the invocation is not static + if (!isStatic) { + code += left.get("valueOl") + " , \"" + methodName + "\""; + } + + // The following arguments can exist or not they are the arguments of the method called + for (var child : params.getChildren()) { + code += " , " + child.get("valueOl"); + } + + code += ")"; + + // Type of method + code += returnType; + + if (makeTemp) + code += ";\n"; + + jmmNode.put("valueOl", temp); + + return null; + } + + private Void dealWithFieldDeclaration(JmmNode jmmNode, Void unused) { + List fieldsOnClass = table.getFields(); + for (Symbol currField : fieldsOnClass) { + code += "\t.field private " + currField.getName() + OllirUtils.ollirTypes(currField.getType()) + ";\n"; + } + return null; + } + + private Void dealWithLenFieldAccess(JmmNode jmmNode, Void unused) { + JmmNode caller = jmmNode.getJmmChild(0); + visit(caller); + jmmNode.put("valueOl", "t" + tempVarId + ".i32"); + String caller_name = caller.get("valueOl"); + code += "t" + tempVarId++ + ".i32 :=.i32 arraylength(" + caller_name + ").i32;\n"; + return null; + } + + private Void dealWithArraySubscript(JmmNode jmmNode, Void unused) { + JmmNode left = jmmNode.getJmmChild(0); + JmmNode right = jmmNode.getJmmChild(1); + visit(left); + visit(right); + String leftS = left.get("valueOl"); + String rightS = right.get("valueOl"); + code += "\t\tt" + tempVarId + ".i32 :=.i32 " + leftS + "[" + rightS + "].i32;\n"; + jmmNode.put("valueOl", "t" + tempVarId++ + ".i32"); + return null; + } + + private Void dealWithLogicalExpr(JmmNode jmmNode, Void unused) { + JmmNode leftSon = jmmNode.getJmmChild(0); + JmmNode rightSon = jmmNode.getJmmChild(1); + + visit(leftSon); + visit(rightSon); + + String left = leftSon.get("valueOl"); + String right = rightSon.get("valueOl"); + temp = "t" + tempVarId++ + ".bool"; + + code += temp + " :=.bool " + left + " " + jmmNode.get("op") + ".bool " + right + ";\n"; + jmmNode.put("valueOl", temp); + + return null; + } + + private Void dealWithComparison(JmmNode jmmNode, Void unused) { + JmmNode leftSon = jmmNode.getJmmChild(0); + JmmNode rightSon = jmmNode.getJmmChild(1); + + visit(leftSon); + visit(rightSon); + + String left = leftSon.get("valueOl"); + String right = rightSon.get("valueOl"); + temp = "t" + tempVarId++ + ".bool"; + + code += temp + " :=.bool " + left + " " + jmmNode.get("op") + ".bool " + right; + code += ";\n"; + jmmNode.put("valueOl", temp); + + return null; + } + + private Void dealWithArithmetic(JmmNode jmmNode, Void unused) { + JmmNode leftSon = jmmNode.getJmmChild(0); + JmmNode rightSon = jmmNode.getJmmChild(1); + + visit(leftSon); + visit(rightSon); + + String left = leftSon.get("valueOl"); + String right = rightSon.get("valueOl"); + temp = "t" + tempVarId++ + ".i32"; + + code += temp + " :=.i32 " + left + " " + jmmNode.get("op") + ".i32 " + right + ";\n"; + jmmNode.put("valueOl", temp); + + return null; + } + + private Void dealWithNegationExpr(JmmNode jmmNode, Void unused) { + JmmNode son = jmmNode.getJmmChild(0); + visit(son); + String sonS = son.get("valueOl"); + temp = "t" + tempVarId++ + ".bool"; + code += temp + " :=.bool !.bool " + sonS + ";\n"; + jmmNode.put("valueOl", temp); + return null; + } + + private Void dealWithArrayAssignment(JmmNode jmmNode, Void unused) { + var method = jmmNode.getAncestor("MethodDecl"); + var voidMethod = jmmNode.getAncestor("VoidMethodDecl"); + var mainMethod = jmmNode.getAncestor("MainMethodDecl"); + Symbol var = null; + boolean isLocal = false, isParam = false, isField = false; + int idParam = 0; + String left = jmmNode.get("arrayname"); + + if (method.isPresent() || voidMethod.isPresent() || mainMethod.isPresent()) { + var methodName = mainMethod.isPresent() ? "main" : method.orElseGet(voidMethod::get).get("methodname"); + List localVarClass = table.getLocalVariables(methodName); + List paramsOnClass = table.getParameters(methodName); + + // Check if local var + for (Symbol lv : localVarClass) + if (lv.getName().equals(left)) { + var = lv; + isLocal = true; + } + + // If not local var, then check if param + if (var == null) + for (int p = 0; p < paramsOnClass.size(); p++) + if (paramsOnClass.get(p).getName().equals(left)) { + var = paramsOnClass.get(p); + isParam = true; + idParam = p + 1; + } + } + + List fields = table.getFields(); + + // If not local nor param, check if field + if (var == null) + for (Symbol f : fields) + if (f.getName().equals(left)) { + var = f; + isField = true; + } + + JmmNode right = jmmNode.getChildren().get(0); + JmmNode last = jmmNode.getChildren().get(1); + visit(right); + visit(last); + + if (isLocal) + code += "\t\t" + left; + else if (isParam) + code += "\t\t$" + idParam + '.' + left; + else if (isField) + code += "\t\tt" + tempVarId + ".array.i32 :=.array.i32 getfield(this, " + left + ".array.i32).array.i32;" + + "\n\t\tt" + tempVarId++; + + code += "[" + right.get("valueOl") + "].i32 :=.i32 " + last.get("valueOl") +";\n"; + + return null; + } + + private Void dealWithParenthesesExpr(JmmNode jmmNode, Void unused) { + visit(jmmNode.getJmmChild(0)); + jmmNode.put("valueOl", jmmNode.getJmmChild(0).get("valueOl")); + return null; + } + + private Void dealWithAssignment(JmmNode jmmNode, Void unused) { + var method = jmmNode.getAncestor("MethodDecl"); + var voidMethod = jmmNode.getAncestor("VoidMethodDecl"); + var mainMethod = jmmNode.getAncestor("MainMethodDecl"); + Symbol var = null; + boolean isLocal = false, isParam = false, isField = false; + int idParam = 0; + String left = jmmNode.get("varname"); + + if (method.isPresent() || voidMethod.isPresent() || mainMethod.isPresent()) { + var methodName = mainMethod.isPresent() ? "main" : method.orElseGet(voidMethod::get).get("methodname"); + List localVarClass = table.getLocalVariables(methodName); + List paramsOnClass = table.getParameters(methodName); + + // Check if local var + for (Symbol lv : localVarClass) + if (lv.getName().equals(left)) { + var = lv; + isLocal = true; + } + + // If not local var, then check if param + if (var == null) + for (int p = 0; p < paramsOnClass.size(); p++) + if (paramsOnClass.get(p).getName().equals(left)) { + var = paramsOnClass.get(p); + isParam = true; + idParam = p + 1; + } + } + + List fields = table.getFields(); + + // If not local nor param, check if field + if (var == null) + for (Symbol f : fields) + if (f.getName().equals(left)) { + var = f; + isField = true; + } + + JmmNode right = jmmNode.getChildren().get(0); + visit(right); + + if (isLocal) + code += "\t\t" + left + OllirUtils.ollirTypes(var.getType()) + " :=" + OllirUtils.ollirTypes(var.getType()) + " "; + else if (isParam) + code += "\t\t$" + idParam + "." + left + OllirUtils.ollirTypes(var.getType()) + " :=" + OllirUtils.ollirTypes(var.getType()) + " "; + else if (isField) + code += "\t\tputfield(this, " + left + OllirUtils.ollirTypes(var.getType()) + ", "; + + code += right.get("valueOl"); + code += isField ? ").V;\n" : ";\n"; + return null; + } + + private Void dealWithExpr(JmmNode jmmNode, Void unused) { + code += "\t\t"; + for (var child : jmmNode.getChildren()) + visit(child); + + code += ";\n"; + System.out.println(jmmNode.getKind()); + return null; + } + + private Void dealWithCodeBlock(JmmNode jmmNode, Void unused) { + for (var child : jmmNode.getChildren()) + visit(child); + + return null; + } + + private Void dealWithVarDecl(JmmNode jmmNode, Void unused) { + for (var child : jmmNode.getChildren()) + visit(child); + + return null; + } + + private Void dealWithMethodCallParam(JmmNode jmmNode, Void unused) { + for (var child : jmmNode.getChildren()) + visit(child); + + return null; + } + + private Void dealWithParamDecl(JmmNode jmmNode, Void unused) { + for (var child : jmmNode.getChildren()) + visit(child); + + return null; + } + + private Void dealWithMethod(JmmNode jmmNode, Void unused) { + code += "\t.method public " + jmmNode.get("methodname") + "("; + // Parameters + List parameters = table.getParameters(jmmNode.get("methodname")); + for (int i = 0; i < parameters.size(); i++) { + Symbol parameter = parameters.get(i); + code += parameter.getName() + OllirUtils.ollirTypes(parameter.getType()); + if (i + 1 < parameters.size()) + code += ", "; + } + code += ")"; + + // Return Type of Method + String returnType = OllirUtils.ollirTypes(table.getReturnType(jmmNode.get("methodname"))); + code += returnType + " {\n"; + + for (int i = 0; i < jmmNode.getChildren().size() - 1; i++) + visit(jmmNode.getChildren().get(i)); + + // Return + JmmNode returnNode = jmmNode.getJmmChild(jmmNode.getNumChildren() - 1); + + visit(returnNode); // Visit the expression after "return" keyword + code += "\t\tret" + returnType + " " + returnNode.get("valueOl") + ";\n\t}\n"; + return null; + } + + private Void dealWithVoidMethod(JmmNode jmmNode, Void unused) { + code += "\t.method public " + jmmNode.get("methodname") + "("; + // Parameters + List parameters = table.getParameters(jmmNode.get("methodname")); + for (int i = 0; i < parameters.size(); i++) { + Symbol parameter = parameters.get(i); + code += parameter.getName() + OllirUtils.ollirTypes(parameter.getType()); + if (i + 1 < parameters.size()) + code += ", "; + } + code += ")"; + + code += ".V{\n"; + + for (int i = 0; i < jmmNode.getChildren().size(); i++) + visit(jmmNode.getChildren().get(i)); + + code += "\t\tret.V;\n\t}\n"; + return null; + } + + + private Void dealWithMainMethod(JmmNode jmmNode, Void unused) { + code += "\t.method public static main(" + jmmNode.get("parametername") + ".array.String).V{\n"; + for (var child : jmmNode.getChildren()) + visit(child); + code += "\t\tret.V;\n\t}\n"; + return null; + } + + private Void dealWithClass(JmmNode jmmNode, Void unused) { + // Imports + List imports = table.getImports(); + for (String currImport : imports) + code += "import " + currImport + ";\n"; + + // Verifies the existence of a superclass + String superClass = table.getSuper(); + if (superClass == null) + code += table.getClassName() + " {\n"; + else + code += table.getClassName() + " extends " + superClass + "{\n"; + dealWithFieldDeclaration(jmmNode, unused); + + // Constructor + code += "\t.construct " + table.getClassName() + "().V {\n" + "\t\tinvokespecial(this, \"\").V;\n\t}\n"; + + for (var child : jmmNode.getChildren()) // Visit methods, etc.. + visit(child); + + return null; + } +} diff --git a/src/main/pt/up/fe/comp2023/optimization/ConstantFolding.java b/src/main/pt/up/fe/comp2023/optimization/ConstantFolding.java new file mode 100644 index 0000000..dae09ae --- /dev/null +++ b/src/main/pt/up/fe/comp2023/optimization/ConstantFolding.java @@ -0,0 +1,128 @@ +package pt.up.fe.comp2023.optimization; + +import pt.up.fe.comp.jmm.analysis.JmmSemanticsResult; +import pt.up.fe.comp.jmm.ast.AJmmVisitor; +import pt.up.fe.comp.jmm.ast.JmmNode; +import pt.up.fe.comp.jmm.ast.JmmNodeImpl; + +public class ConstantFolding extends AJmmVisitor { + private final JmmSemanticsResult semanticsResult; + private boolean codeModified; + public ConstantFolding (JmmSemanticsResult semanticsResult){ + this.semanticsResult = semanticsResult; + } + + public boolean apply(){ + this.codeModified = false; + visit(semanticsResult.getRootNode()); + return this.codeModified; + } + + @Override + protected void buildVisitor() { + setDefaultVisit(this::setDefaultVisit); + addVisit("ParenthesesExpr", this::computeParenthesesExprResult); + addVisit("NegationExpr", this::negateBooleanExpr); + addVisit("ArithmeticExpr", this::computeArithmeticExprResult); + addVisit("ComparisonExpr", this::computeComparisonResult); + addVisit("LogicalExpr", this::computeLogicalExprResult); + + } + + private Void setDefaultVisit(JmmNode jmmNode, Void unused) { + for (JmmNode child: jmmNode.getChildren()) + visit(child); + return null; + } + + private Void computeParenthesesExprResult(JmmNode jmmNode, Void unused) { + JmmNode exprNode = jmmNode.getJmmChild(0); + visit(exprNode); + + if(exprNode.getKind().equals("Integer") || exprNode.getKind().equals("Boolean")){ + this.codeModified = true; + jmmNode.replace(exprNode); + } + return null; + } + + private Void negateBooleanExpr(JmmNode jmmNode, Void unused) { + JmmNode exprNode = jmmNode.getJmmChild(0); + visit(exprNode); + + if (exprNode.getKind().equals("Boolean")) { + this.codeModified = true; + boolean exprValue = Boolean.parseBoolean(exprNode.get("value")); + exprNode.put("value", String.valueOf(!exprValue)); + jmmNode.replace(exprNode); + } + return null; + } + + private Void computeArithmeticExprResult(JmmNode jmmNode, Void unused) { + JmmNode leftExpr = jmmNode.getJmmChild(0); + JmmNode rightExpr = jmmNode.getJmmChild(1); + String operator = jmmNode.get("op"); + visit(leftExpr); + visit(rightExpr); + + if (leftExpr.getKind().equals("Integer") && rightExpr.getKind().equals("Integer")){ + this.codeModified = true; + int leftValue = Integer.parseInt(leftExpr.get("value")); + int rightValue = Integer.parseInt(rightExpr.get("value")); + + switch (operator) { + case "+" -> leftExpr.put("value", String.valueOf(leftValue + rightValue)); + case "-" -> leftExpr.put("value", String.valueOf(leftValue - rightValue)); + case "*" -> leftExpr.put("value", String.valueOf(leftValue * rightValue)); + case "/" -> leftExpr.put("value", String.valueOf(leftValue / rightValue)); + } + jmmNode.replace(leftExpr); + } + return null; + } + + private Void computeComparisonResult(JmmNode jmmNode, Void unused) { + JmmNode leftExpr = jmmNode.getJmmChild(0); + JmmNode rightExpr = jmmNode.getJmmChild(1); + String operator = jmmNode.get("op"); + visit(leftExpr); + visit(rightExpr); + + if (leftExpr.getKind().equals("Integer") && rightExpr.getKind().equals("Integer")){ + this.codeModified = true; + int leftValue = Integer.parseInt(leftExpr.get("value")); + int rightValue = Integer.parseInt(rightExpr.get("value")); + JmmNodeImpl newNode = new JmmNodeImpl("Boolean"); + + if (operator.equals("<")) + newNode.put("value", String.valueOf(leftValue < rightValue)); + else + newNode.put("value", String.valueOf(leftValue > rightValue)); + + jmmNode.replace(newNode); + } + return null; + } + + private Void computeLogicalExprResult(JmmNode jmmNode, Void unused) { + JmmNode leftExpr = jmmNode.getJmmChild(0); + JmmNode rightExpr = jmmNode.getJmmChild(1); + String operator = jmmNode.get("op"); + visit(leftExpr); + visit(rightExpr); + + if (leftExpr.getKind().equals("Boolean") && rightExpr.getKind().equals("Boolean")){ + this.codeModified = true; + boolean leftValue = Boolean.parseBoolean(leftExpr.get("value")); + boolean rightValue = Boolean.parseBoolean(rightExpr.get("value")); + + if (operator.equals("&&")) + leftExpr.put("value", String.valueOf(leftValue && rightValue)); + else + leftExpr.put("value", String.valueOf(leftValue || rightValue)); + jmmNode.replace(leftExpr); + } + return null; + } +} diff --git a/src/main/pt/up/fe/comp2023/optimization/ConstantPropagation.java b/src/main/pt/up/fe/comp2023/optimization/ConstantPropagation.java new file mode 100644 index 0000000..c31abf8 --- /dev/null +++ b/src/main/pt/up/fe/comp2023/optimization/ConstantPropagation.java @@ -0,0 +1,110 @@ +package pt.up.fe.comp2023.optimization; + +import pt.up.fe.comp.jmm.analysis.JmmSemanticsResult; +import pt.up.fe.comp.jmm.ast.AJmmVisitor; +import pt.up.fe.comp.jmm.ast.JmmNode; +import pt.up.fe.comp.jmm.ast.JmmNodeImpl; + +import java.util.*; + +import static pt.up.fe.comp2023.optimization.OptimizationUtils.*; + +public class ConstantPropagation extends AJmmVisitor, Void> { + private final JmmSemanticsResult semanticsResult; + private boolean codeModified; + + public ConstantPropagation (JmmSemanticsResult semanticsResult){ + this.semanticsResult = semanticsResult; + } + + public boolean apply(){ + this.codeModified = false; + Map constants = new HashMap<>(); + + visit(semanticsResult.getRootNode(), constants); + return this.codeModified; + } + + @Override + protected void buildVisitor() { + setDefaultVisit(this::setDefaultVisit); + addVisit("MethodDecl", this::clearConstants); + addVisit("VoidMethodDecl", this::clearConstants); + addVisit("MainMethodDecl", this::clearConstants); + addVisit("Condition", this::dealWithCondition); + addVisit("Cycle", this::dealWithCycle); + addVisit("Assignment", this::dealWithAssignment); + addVisit("Identifier", this::dealWithIdentifier); + } + + private Void setDefaultVisit(JmmNode jmmNode, Map constants) { + for (JmmNode child: jmmNode.getChildren()) + visit(child, constants); + return null; + } + + private Void clearConstants(JmmNode jmmNode, Map constants) { + constants.clear(); + + for (JmmNode child: jmmNode.getChildren()) + visit(child, constants); //each statement modifies the map + return null; + } + + private Void dealWithCondition(JmmNode jmmNode, Map constants) { + JmmNode conditionNode = jmmNode.getJmmChild(0); + JmmNode ifCode = jmmNode.getJmmChild(1); + JmmNode elseCode = jmmNode.getJmmChild(2); + Map ifConstants = new HashMap<>(constants); + Map elseConstants = new HashMap<>(constants); + + visit(conditionNode, constants); + visit(ifCode, ifConstants); + visit(elseCode, elseConstants); + intersectMaps(ifConstants, elseConstants, constants); + return null; + } + + private Void dealWithCycle(JmmNode jmmNode, Map constants) { + Map oldConstants = new HashMap<>(constants); + Map cycleConstants = new HashMap<>(constants); + + for (JmmNode child: jmmNode.getChildren()) + visit(child, cycleConstants); + + intersectMaps(cycleConstants, oldConstants, constants); + return null; + } + + private Void dealWithAssignment(JmmNode jmmNode, Map constants) { + String varName = jmmNode.get("varname"); + if(jmmNode.getAncestor("Cycle").isPresent()) + constants.remove(varName); + + JmmNode exprNode = jmmNode.getJmmChild(0); + visit(exprNode, constants); + + if (exprNode.getKind().equals("Integer") || exprNode.getKind().equals("Boolean")) + constants.put(varName, exprNode.get("value")); + else //Unknown Value + constants.remove(varName); + return null; + } + + private Void dealWithIdentifier(JmmNode jmmNode, Map constants) { + String identifierName = jmmNode.get("value"); + String constant = constants.get(identifierName); + + if(constant != null) { + JmmNode newNode; + if(constant.equals("true") || constant.equals("false")) //Boolean constant + newNode = new JmmNodeImpl("Boolean"); + else //Integer constant + newNode = new JmmNodeImpl("Integer"); + newNode.put("value", constant); + jmmNode.replace(newNode); + this.codeModified = true; + } + return null; + } +} diff --git a/src/main/pt/up/fe/comp2023/optimization/OptimizationUtils.java b/src/main/pt/up/fe/comp2023/optimization/OptimizationUtils.java new file mode 100644 index 0000000..6b059c3 --- /dev/null +++ b/src/main/pt/up/fe/comp2023/optimization/OptimizationUtils.java @@ -0,0 +1,76 @@ +package pt.up.fe.comp2023.optimization; + +import org.specs.comp.ollir.Descriptor; +import org.specs.comp.ollir.Element; +import org.specs.comp.ollir.Method; +import org.specs.comp.ollir.Operand; + +import java.util.*; + +public class OptimizationUtils { + public static void intersectMaps (Map map1, Map map2, Map result){ + result.clear(); + + for (Map.Entry entry : map1.entrySet()) { + String key = entry.getKey(); + String value = entry.getValue(); + + if (map2.containsKey(key) && map2.get(key).equals(value)) + result.put(key, value); + } + } + + public static Set differenceSets(Set set1, Set set2) { + Set result = new HashSet<>(set1); + if(set2 != null) + result.removeAll(set2); + + return result; + } + + public static Set unionSets(Set set1, Set set2) { + Set result = new HashSet<>(set1); + if(set2 != null) + result.addAll(set2); + + return result; + } + + public static boolean isLocalVar(Element element, Method method) { + HashMap varTable = method.getVarTable(); + String varName = ((Operand)element).getName(); + + return isLocalVar(varName, method); + } + + public static boolean isLocalVar(String identifier, Method method) { + HashMap varTable = method.getVarTable(); + int firstLocalVarRegister = methodAccessThis(method) + method.getParams().size(); + + return varTable.get(identifier).getVirtualReg() >= firstLocalVarRegister; + } + + public static List getLocalVars(Method method) { + HashMap varTable = method.getVarTable(); + List localsVars = new ArrayList<>(); + + for(Map.Entry entry : varTable.entrySet()){ + String identifier = entry.getKey(); + if(isLocalVar(identifier, method)) + localsVars.add(identifier); + } + return localsVars; + } + + public static String toVarName(Element element){ + return ((Operand)element).getName(); + } + + public static int methodAccessThis(Method method) { + return method.isStaticMethod() ? 0 : 1; + } + + public static int numParams(Method method){ + return method.getParams().size(); + } +} diff --git a/src/main/pt/up/fe/comp2023/optimization/RegisterAllocation.java b/src/main/pt/up/fe/comp2023/optimization/RegisterAllocation.java new file mode 100644 index 0000000..d72ea82 --- /dev/null +++ b/src/main/pt/up/fe/comp2023/optimization/RegisterAllocation.java @@ -0,0 +1,176 @@ +package pt.up.fe.comp2023.optimization; + +import org.specs.comp.ollir.*; +import pt.up.fe.comp2023.optimization.interferenceGraph.MyInterferenceGraph; + +import java.util.*; + +import static org.specs.comp.ollir.InstructionType.*; +import static pt.up.fe.comp2023.optimization.OptimizationUtils.*; + +public class RegisterAllocation { + private final Method method; + private final int registerAllocationOption; + private Map optimalRegisters = new HashMap<>(); + private final Map> defs = new HashMap<>(); + private final Map> uses = new HashMap<>(); + private final Map> in = new HashMap<>(); + private final Map> out = new HashMap<>(); + private final MyInterferenceGraph interferenceGraph = new MyInterferenceGraph(); + + public RegisterAllocation(Method method, int registerAllocationOption) { + this.registerAllocationOption = registerAllocationOption; + this.method = method; + + livenessAnalysis(); + createInterferenceGraph(); + graphColoring(); //Updates optimalRegisters + updateVirtualRegisters(); + } + + private void livenessAnalysis(){ + this.method.buildCFG(); + for (Instruction instruction : this.method.getInstructions()){ + this.defs.put(instruction, getDef(instruction)); + this.uses.put(instruction, getUse(instruction, new HashSet<>())); + } + computeLiveInOut(); + } + + private void createInterferenceGraph() { + List localVars = getLocalVars(this.method); + + //Add a node for each variable + for(String var : localVars) + this.interferenceGraph.addNode(var); + + //Compute edges + for(Instruction instruction : this.method.getInstructions()){ + List liveIn = new ArrayList<>(this.in.get(instruction)); + List defAndLiveOut = new ArrayList<>(unionSets(this.defs.get(instruction), this.out.get(instruction))); + + this.interferenceGraph.connectInterferingVariables(liveIn); + this.interferenceGraph.connectInterferingVariables(defAndLiveOut); + } + } + + private void graphColoring() { + if(registerAllocationOption > 0) { // Try to use at most local variables + int k = registerAllocationOption - methodAccessThis(method) - numParams(method); + this.optimalRegisters = this.interferenceGraph.isMColoringFeasible(k); + } + else // Try to use as few local variables as it can + this.optimalRegisters = this.interferenceGraph.findOptimalColoring(); + } + + private void updateVirtualRegisters(){ + int firstLocalVarRegister = methodAccessThis(method) + numParams(method); + + for (Map.Entry entry : optimalRegisters.entrySet()) { + String var = entry.getKey(); + Integer register = entry.getValue(); + int virtualRegister = firstLocalVarRegister + register; + + method.getVarTable().get(var).setVirtualReg(virtualRegister); + } + } + + private Set getDef(Instruction instruction){ + Set def = new HashSet<>(); + + if(instruction.getInstType() == ASSIGN) { + AssignInstruction assignInst = (AssignInstruction)instruction; + if(isLocalVar(assignInst.getDest(), this.method)){ + Element dest = assignInst.getDest(); + def.add(toVarName(dest)); + } + } + return def; + } + + private Set getUse(Instruction instruction, Set result){ + switch (instruction.getInstType()) { + case ASSIGN -> { + AssignInstruction assignInst = (AssignInstruction) instruction; + return getUse(assignInst.getRhs(), result); + } + case CALL -> { + CallInstruction callInst = (CallInstruction) instruction; + List arguments = callInst.getListOfOperands(); + for (Element argument : arguments) { + if (!argument.isLiteral() && isLocalVar(argument, this.method)) + result.add(toVarName(argument)); + } + } + case RETURN -> { + ReturnInstruction returnInst = (ReturnInstruction) instruction; + Element returnElement = returnInst.getOperand(); + if (returnElement != null && !returnElement.isLiteral() && isLocalVar(returnElement, this.method)) + result.add(toVarName(returnElement)); + } + case UNARYOPER -> { + UnaryOpInstruction unaryOpInstruction = (UnaryOpInstruction) instruction; + Element operand = unaryOpInstruction.getOperand(); + if (!operand.isLiteral() && isLocalVar(operand, this.method)) + result.add(toVarName(operand)); + } + case BINARYOPER -> { + BinaryOpInstruction binInst = (BinaryOpInstruction) instruction; + Element leftOperand = binInst.getLeftOperand(); + Element rightOperand = binInst.getRightOperand(); + if (!leftOperand.isLiteral() && isLocalVar(leftOperand, this.method)) + result.add(toVarName(leftOperand)); + if (!rightOperand.isLiteral() && isLocalVar(rightOperand, this.method)) + result.add(toVarName(rightOperand)); + } + case NOPER -> { + SingleOpInstruction singleOpInstruction = (SingleOpInstruction) instruction; + Element rightOperand = singleOpInstruction.getSingleOperand(); + if (!rightOperand.isLiteral() && isLocalVar(rightOperand, this.method)) + result.add(toVarName(rightOperand)); + } + case PUTFIELD -> { + PutFieldInstruction putFieldInstruction = (PutFieldInstruction) instruction; + Element rightOperand = putFieldInstruction.getThirdOperand(); + if (!rightOperand.isLiteral() && isLocalVar(rightOperand, this.method)) + result.add(toVarName(rightOperand)); + } + } + return result; + } + + private void computeLiveInOut() { + for (Instruction instruction : method.getInstructions()){ + this.in.put(instruction, new HashSet<>()); + this.out.put(instruction, new HashSet<>()); + } + + boolean liveChanged; + do { + liveChanged = false; + for(Instruction instruction : method.getInstructions()){ + //Save current liveIn and liveOut + Set liveInAux = new HashSet<>(this.in.get(instruction)); + Set liveOutAux = new HashSet<>(this.out.get(instruction)); + + //Update liveIn + Set difference = differenceSets(this.out.get(instruction), this.defs.get(instruction)); + Set newLiveIn = unionSets(this.uses.get(instruction), difference); + this.in.put(instruction, newLiveIn); + + //Update liveOut + Set newLiveOut = new HashSet<>(); + + for(Node successor : instruction.getSuccessors()){ + Set liveInSuccessor = this.in.get(successor); + newLiveOut = unionSets(newLiveOut, liveInSuccessor); + } + this.out.put(instruction, newLiveOut); + + //Check if liveIn or liveOut changed + if(!liveInAux.equals(newLiveIn) || !liveOutAux.equals(newLiveOut)) + liveChanged = true; + } + } while(liveChanged); + } +} diff --git a/src/main/pt/up/fe/comp2023/optimization/interferenceGraph/MyInterferenceGraph.java b/src/main/pt/up/fe/comp2023/optimization/interferenceGraph/MyInterferenceGraph.java new file mode 100644 index 0000000..fc6d84a --- /dev/null +++ b/src/main/pt/up/fe/comp2023/optimization/interferenceGraph/MyInterferenceGraph.java @@ -0,0 +1,113 @@ +package pt.up.fe.comp2023.optimization.interferenceGraph; + +import java.util.*; + +public class MyInterferenceGraph { + private final List nodes = new ArrayList<>(); + private final Map nodeColor = new HashMap<>(); + + public MyInterferenceGraph deepCopy(){ + MyInterferenceGraph copy = new MyInterferenceGraph(); + + for(MyNode node : this.nodes) + copy.addNode(node.getVariable()); + return copy; + } + + public void addNode(String variable){ + MyNode newNode = new MyNode(variable); + this.nodes.add(newNode); + } + + public MyNode getNode(String varName){ + for(MyNode node : this.nodes){ + if(node.getVariable().equals(varName)) + return node; + } + return null; + } + + public void addInterferenceEdge(String src, String dest){ + getNode(src).addNeighbour(dest); + getNode(dest).addNeighbour(src); + } + + public void connectInterferingVariables(List variables){ + for(int i = 0; i < variables.size(); i++){ + for(int j = i + 1; j < variables.size(); j++){ + String src = variables.get(i); + String dest = variables.get(j); + + addInterferenceEdge(src, dest); + } + } + } + + public void removeNode(MyNode node){ + for(String neighbour : node.getNeighbours()){ + MyNode neighbourNode = getNode(neighbour); + neighbourNode.removeNeighbour(node.getVariable()); + } + this.nodes.remove(node); + } + + private boolean isValidColor(MyNode node, int color){ + for(String neighbour : node.getNeighbours()){ + if(nodeColor.getOrDefault(neighbour, -1) == color) + return false; + } + return true; + } + + public Stack computeColoringStack(int maxColors) throws RuntimeException{ + Stack stack = new Stack<>(); + + while (this.nodes.size() > 0) { + MyNode nodeToRemove = null; + + for(MyNode node : this.nodes){ + if(node.getDegree() < maxColors){ + nodeToRemove = node; + break; + } + } + if(nodeToRemove != null){ + stack.push(nodeToRemove.getVariable()); + this.removeNode(nodeToRemove); + } + else + throw new RuntimeException("The provided number of registers is not enough to store the local variables."); + } + return stack; + } + + public Map isMColoringFeasible(int maxColors) throws RuntimeException{ + MyInterferenceGraph copyGraph = deepCopy(); + Stack stack = copyGraph.computeColoringStack(maxColors); + + while (!stack.isEmpty()){ + String nodeName = stack.pop(); + for(int color = 0; color < maxColors; color++){ + if(isValidColor(getNode(nodeName), color)) + this.nodeColor.put(nodeName, color); + } + } + return this.nodeColor; + } + + public Map findOptimalColoring(){ + int maxColors = this.nodes.size(); + + for(int currentMaxColors = 1; currentMaxColors <= maxColors; currentMaxColors++){ + try{ + this.isMColoringFeasible(currentMaxColors); + + }catch (RuntimeException e){ //currentMaxColor is not enough + continue; + } + break; + } + return this.nodeColor; + } + +} diff --git a/src/main/pt/up/fe/comp2023/optimization/interferenceGraph/MyNode.java b/src/main/pt/up/fe/comp2023/optimization/interferenceGraph/MyNode.java new file mode 100644 index 0000000..ffd784b --- /dev/null +++ b/src/main/pt/up/fe/comp2023/optimization/interferenceGraph/MyNode.java @@ -0,0 +1,33 @@ +package pt.up.fe.comp2023.optimization.interferenceGraph; + +import java.util.HashSet; +import java.util.Set; + +public class MyNode { + private final String varName; + private final Set neighbours = new HashSet<>(); + + public MyNode(String varName){ + this.varName = varName; + } + + public String getVariable() { + return this.varName; + } + + public Set getNeighbours() { + return neighbours; + } + + public void addNeighbour(String varName) { + this.neighbours.add(varName); + } + + public int getDegree() { + return this.neighbours.size(); + } + + public void removeNeighbour(String varName){ + this.neighbours.remove(varName); + } +} diff --git a/src/main/pt/up/fe/comp2023/Analysis.java b/src/main/pt/up/fe/comp2023/semantic/Analysis.java similarity index 96% rename from src/main/pt/up/fe/comp2023/Analysis.java rename to src/main/pt/up/fe/comp2023/semantic/Analysis.java index 01d4dff..c563b1d 100644 --- a/src/main/pt/up/fe/comp2023/Analysis.java +++ b/src/main/pt/up/fe/comp2023/semantic/Analysis.java @@ -1,4 +1,4 @@ -package pt.up.fe.comp2023; +package pt.up.fe.comp2023.semantic; import pt.up.fe.comp.jmm.analysis.JmmAnalysis; import pt.up.fe.comp.jmm.analysis.JmmSemanticsResult; diff --git a/src/main/pt/up/fe/comp2023/ExpressionAnalysis.java b/src/main/pt/up/fe/comp2023/semantic/ExpressionAnalysis.java similarity index 81% rename from src/main/pt/up/fe/comp2023/ExpressionAnalysis.java rename to src/main/pt/up/fe/comp2023/semantic/ExpressionAnalysis.java index 96d5d0a..2b5eac9 100644 --- a/src/main/pt/up/fe/comp2023/ExpressionAnalysis.java +++ b/src/main/pt/up/fe/comp2023/semantic/ExpressionAnalysis.java @@ -1,4 +1,4 @@ -package pt.up.fe.comp2023; +package pt.up.fe.comp2023.semantic; import pt.up.fe.comp.jmm.analysis.table.Type; import pt.up.fe.comp.jmm.ast.AJmmVisitor; @@ -6,11 +6,12 @@ import pt.up.fe.comp.jmm.report.Report; import pt.up.fe.comp.jmm.report.ReportType; import pt.up.fe.comp.jmm.report.Stage; +import pt.up.fe.comp2023.semantic.MySymbolTable; import java.util.List; import java.util.Objects; -import static pt.up.fe.comp2023.SemanticUtils.*; +import static pt.up.fe.comp2023.semantic.SemanticUtils.*; public class ExpressionAnalysis extends AJmmVisitor { private String methodName; @@ -60,14 +61,13 @@ private Type checkBooleanExpression(JmmNode jmmNode, Type type) { JmmNode expressionNode = jmmNode.getJmmChild(0); Type expressionType = visit(expressionNode); - if(expressionType.equals(BOOLEAN_TYPE)){ - jmmNode.put(TYPENAME, BOOLEAN); - return BOOLEAN_TYPE; + if(!expressionType.equals(BOOLEAN_TYPE)){ + String message = "Expected expression of type '" + BOOLEAN + "' but found '" + expressionType.print() + "'."; + this.reports.add(new Report(ReportType.ERROR, Stage.SEMANTIC, getNodeLine(expressionNode), getNodeColumn(expressionNode), message)); } - String message = "Expected expression of type '" + BOOLEAN + "' but found '" + expressionType.print() + "'."; - this.reports.add(new Report(ReportType.ERROR, Stage.SEMANTIC, getNodeLine(expressionNode), getNodeColumn(expressionNode), message)); - jmmNode.put(TYPENAME, UNKNOWN); - return UNKNOWN_TYPE; + + jmmNode.put(TYPENAME, BOOLEAN); + return BOOLEAN_TYPE; } private Type checkIntegerOperands(JmmNode jmmNode, Type type) { @@ -79,19 +79,13 @@ private Type checkIntegerOperands(JmmNode jmmNode, Type type) { if(!leftOperandType.equals(INT_TYPE)) { String message = "Expected operand of type '" + INT + "' but found '" + leftOperandType.print() + "'."; this.reports.add(new Report(ReportType.ERROR, Stage.SEMANTIC, getNodeLine(leftNode), getNodeColumn(leftNode), message)); - jmmNode.put(TYPENAME, UNKNOWN); } - if(!rightOperandType.equals(INT_TYPE)) { - String message = "Expected operand of type '" + INT + "' but found '" + leftOperandType.print() + "'."; + String message = "Expected operand of type '" + INT + "' but found '" + rightOperandType.print() + "'."; this.reports.add(new Report(ReportType.ERROR, Stage.SEMANTIC, getNodeLine(rightNode), getNodeColumn(rightNode), message)); - if(!jmmNode.hasAttribute(TYPENAME)) - jmmNode.put(TYPENAME, UNKNOWN); } - if(jmmNode.hasAttribute(TYPENAME)) //Semantic errors were found - return UNKNOWN_TYPE; - else if(Objects.equals(jmmNode.getKind(), "ArithmeticExpr")) { + if(Objects.equals(jmmNode.getKind(), "ArithmeticExpr")) { jmmNode.put(TYPENAME, INT); return INT_TYPE; } @@ -110,18 +104,13 @@ private Type checkBooleanOperands(JmmNode jmmNode, Type type) { if(!leftOperandType.equals(BOOLEAN_TYPE)) { String message = "Expected operand of type '" + BOOLEAN + "' but found '" + leftOperandType.print() + "'."; this.reports.add(new Report(ReportType.ERROR, Stage.SEMANTIC, getNodeLine(leftNode), getNodeColumn(leftNode), message)); - jmmNode.put(TYPENAME, UNKNOWN); } if(!rightOperandType.equals(BOOLEAN_TYPE)) { - String message = "Expected operand of type '" + BOOLEAN + "' but found '" + leftOperandType.print() + "'."; + String message = "Expected operand of type '" + BOOLEAN + "' but found '" + rightOperandType.print() + "'."; this.reports.add(new Report(ReportType.ERROR, Stage.SEMANTIC, getNodeLine(rightNode), getNodeColumn(rightNode), message)); - if(!jmmNode.hasAttribute(TYPENAME)) - jmmNode.put(TYPENAME, UNKNOWN); } - if(jmmNode.hasAttribute(TYPENAME)) //Semantic errors were found - return UNKNOWN_TYPE; jmmNode.put(TYPENAME, BOOLEAN); return BOOLEAN_TYPE; } @@ -135,18 +124,13 @@ private Type dealWithArraySubscript(JmmNode jmmNode, Type type) { if (!variableType.equals(ARRAY_TYPE)) { String message = "Expected '" + ARRAY_TYPE + "' type but found '" + variableType.print() + "'."; this.reports.add(new Report(ReportType.ERROR, Stage.SEMANTIC, getNodeLine(variableNode), getNodeColumn(variableNode), message)); - jmmNode.put(TYPENAME, UNKNOWN); } if (!indexType.equals(INT_TYPE)) { String message = "Expected index expression of type '" + INT +"' but found '" + indexType.print() + "'."; this.reports.add(new Report(ReportType.ERROR, Stage.SEMANTIC, getNodeLine(indexNode), getNodeColumn(indexNode), message)); - if(!jmmNode.hasAttribute(TYPENAME)) - jmmNode.put(TYPENAME, UNKNOWN); } - if(jmmNode.hasAttribute(TYPENAME)) //Semantic errors were found - return UNKNOWN_TYPE; jmmNode.put(TYPENAME, INT); return INT_TYPE; } @@ -154,14 +138,13 @@ private Type dealWithArraySubscript(JmmNode jmmNode, Type type) { private Type dealWithLengthFieldAccess(JmmNode jmmNode, Type type) { Type expressionType = visit(jmmNode.getJmmChild(0)); - if(expressionType.equals(ARRAY_TYPE)){ - jmmNode.put(TYPENAME, INT); - return INT_TYPE; + if(!expressionType.equals(ARRAY_TYPE)){ + String message = "Cannot resolve symbol 'length'."; + this.reports.add(new Report(ReportType.ERROR, Stage.SEMANTIC, getNodeLine(jmmNode), getNodeColumn(jmmNode), message)); } - String message = "Cannot resolve symbol 'length'."; - this.reports.add(new Report(ReportType.ERROR, Stage.SEMANTIC, getNodeLine(jmmNode), getNodeColumn(jmmNode), message)); - jmmNode.put(TYPENAME, UNKNOWN); - return UNKNOWN_TYPE; + + jmmNode.put(TYPENAME, INT); + return INT_TYPE; } private Type dealWithMethodCall(JmmNode jmmNode, Type type) { @@ -169,8 +152,8 @@ private Type dealWithMethodCall(JmmNode jmmNode, Type type) { String method = jmmNode.get("methodcall"); if(this.symbolTable.getMethods().contains(method)){ - if(!Objects.equals(expressionType, this.className)){ - String message = "Expected expression of type '" + this.className + "' but found '" + expressionType + "'."; + if(!Objects.equals(expressionType, this.className) && !Objects.equals(expressionType, this.superClass)){ + String message = "Expected expression of type '" + this.className + "' or '" + this.superClass + "' but found '" + expressionType + "'."; this.reports.add(new Report(ReportType.ERROR, Stage.SEMANTIC, getNodeLine(jmmNode), getNodeColumn(jmmNode), message)); } this.verifyArgumentTypes(jmmNode.getJmmChild(1), method); @@ -219,14 +202,13 @@ private Type checkIntegerLength(JmmNode jmmNode, Type type) { JmmNode lengthNode = jmmNode.getJmmChild(0); Type lengthType = visit(lengthNode); - if (lengthType.equals(INT_TYPE)){ - jmmNode.put(TYPENAME, ARRAY); - return ARRAY_TYPE; + if (!lengthType.equals(INT_TYPE)){ + String message = "Expected array length to be '" + INT + "' but found '" + lengthType.print() + "'."; + this.reports.add(new Report(ReportType.ERROR, Stage.SEMANTIC, getNodeLine(lengthNode), getNodeColumn(lengthNode), message)); } - String message = "Expected array length to be '" + INT + "' but found '" + lengthType.print() + "'."; - this.reports.add(new Report(ReportType.ERROR, Stage.SEMANTIC, getNodeLine(lengthNode), getNodeColumn(lengthNode), message)); - jmmNode.put(TYPENAME, UNKNOWN); - return UNKNOWN_TYPE; + + jmmNode.put(TYPENAME, ARRAY); + return ARRAY_TYPE; } private Type dealWithObjectCreation(JmmNode jmmNode, Type type) { @@ -243,7 +225,7 @@ else if(findImport(this.imports, objectClassName)){ } String message = "Cannot find '" + objectClassName + "'."; - this.reports.add(new Report(ReportType.ERROR, Stage.SEMANTIC, getNodeLine(jmmNode), getNodeColumn(jmmNode), message)); //TODO: use column of 'classname' + this.reports.add(new Report(ReportType.ERROR, Stage.SEMANTIC, getNodeLine(jmmNode), getNodeColumn(jmmNode), message)); jmmNode.put(TYPENAME, UNKNOWN); return UNKNOWN_TYPE; } diff --git a/src/main/pt/up/fe/comp2023/MethodTable.java b/src/main/pt/up/fe/comp2023/semantic/MethodTable.java similarity index 93% rename from src/main/pt/up/fe/comp2023/MethodTable.java rename to src/main/pt/up/fe/comp2023/semantic/MethodTable.java index 4b82ac7..c268b2d 100644 --- a/src/main/pt/up/fe/comp2023/MethodTable.java +++ b/src/main/pt/up/fe/comp2023/semantic/MethodTable.java @@ -1,11 +1,11 @@ -package pt.up.fe.comp2023; +package pt.up.fe.comp2023.semantic; import pt.up.fe.comp.jmm.analysis.table.Symbol; import pt.up.fe.comp.jmm.analysis.table.Type; -import java.util.ArrayList; import java.util.List; public class MethodTable { + private final String name; private final List parameters; private final List localVariables; @@ -33,5 +33,4 @@ public List getLocalVariables() { public Type getReturnType() { return returnType; } - } diff --git a/src/main/pt/up/fe/comp2023/MySymbolTable.java b/src/main/pt/up/fe/comp2023/semantic/MySymbolTable.java similarity index 99% rename from src/main/pt/up/fe/comp2023/MySymbolTable.java rename to src/main/pt/up/fe/comp2023/semantic/MySymbolTable.java index 9a51220..de64c04 100644 --- a/src/main/pt/up/fe/comp2023/MySymbolTable.java +++ b/src/main/pt/up/fe/comp2023/semantic/MySymbolTable.java @@ -1,4 +1,4 @@ -package pt.up.fe.comp2023; +package pt.up.fe.comp2023.semantic; import pt.up.fe.comp.jmm.analysis.table.Symbol; import pt.up.fe.comp.jmm.analysis.table.SymbolTable; diff --git a/src/main/pt/up/fe/comp2023/SemanticAnalysis.java b/src/main/pt/up/fe/comp2023/semantic/SemanticAnalysis.java similarity index 91% rename from src/main/pt/up/fe/comp2023/SemanticAnalysis.java rename to src/main/pt/up/fe/comp2023/semantic/SemanticAnalysis.java index dc6b7f2..3b43ef9 100644 --- a/src/main/pt/up/fe/comp2023/SemanticAnalysis.java +++ b/src/main/pt/up/fe/comp2023/semantic/SemanticAnalysis.java @@ -1,4 +1,4 @@ -package pt.up.fe.comp2023; +package pt.up.fe.comp2023.semantic; import pt.up.fe.comp.jmm.analysis.table.Type; import pt.up.fe.comp.jmm.ast.AJmmVisitor; @@ -10,7 +10,7 @@ import java.util.List; import java.util.Objects; -import static pt.up.fe.comp2023.SemanticUtils.*; +import static pt.up.fe.comp2023.semantic.SemanticUtils.*; public class SemanticAnalysis extends AJmmVisitor { private final MySymbolTable symbolTable; @@ -133,32 +133,41 @@ private Void checkCompatibleAssignment(JmmNode jmmNode, Void unused) { String message = "'" + varName + "' is not declared."; this.reports.add(new Report(ReportType.ERROR, Stage.SEMANTIC, getNodeLine(jmmNode), getNodeColumn(jmmNode), message)); } + + else if (right.equals(UNKNOWN_TYPE)) + return null; + else if (right.equals(UNDEFINED_TYPE)) expressionNode.put(TYPENAME, left.print()); - else if(right.equals(left)) + else if (right.equals(left)) return null; else if (Objects.equals(left.print(), this.superClass) && Objects.equals(right.print(), this.className)) return null; + else if (Objects.equals(left.print(), this.className) && !Objects.equals(right.print(), this.superClass) && findImport(this.imports, right.print())) + return null; + else if (findImport(this.imports, left.print()) && findImport(this.imports, right.print())) return null; - String message = "Type of the assignee is not compatible with the assigned."; - this.reports.add(new Report(ReportType.ERROR, Stage.SEMANTIC, getNodeLine(expressionNode), getNodeColumn(expressionNode), message)); + else { + String message = "Type of the assignee is not compatible with the assigned."; + this.reports.add(new Report(ReportType.ERROR, Stage.SEMANTIC, getNodeLine(expressionNode), getNodeColumn(expressionNode), message)); + } return null; } private Void validateArrayAssignment(JmmNode jmmNode, Void unused) { String varName = jmmNode.get("arrayname"); - JmmNode valueNode = jmmNode.getJmmChild(1); JmmNode indexNode = jmmNode.getJmmChild(0); + JmmNode valueNode = jmmNode.getJmmChild(1); Type varType = getIdentifierType(this.currentMethodName, varName, this.symbolTable); Type indexType = expressionAnalysis.visit(indexNode); Type valueType = expressionAnalysis.visit(valueNode); - if(!varType.isArray()){ + if(!varType.equals(ARRAY_TYPE)){ String message = "'" + varName + "' must be an array."; this.reports.add(new Report(ReportType.ERROR, Stage.SEMANTIC, getNodeLine(jmmNode), getNodeColumn(jmmNode), message)); } diff --git a/src/main/pt/up/fe/comp2023/SemanticUtils.java b/src/main/pt/up/fe/comp2023/semantic/SemanticUtils.java similarity index 88% rename from src/main/pt/up/fe/comp2023/SemanticUtils.java rename to src/main/pt/up/fe/comp2023/semantic/SemanticUtils.java index 681c191..40d2c28 100644 --- a/src/main/pt/up/fe/comp2023/SemanticUtils.java +++ b/src/main/pt/up/fe/comp2023/semantic/SemanticUtils.java @@ -1,4 +1,4 @@ -package pt.up.fe.comp2023; +package pt.up.fe.comp2023.semantic; import pt.up.fe.comp.jmm.analysis.table.Symbol; import pt.up.fe.comp.jmm.analysis.table.Type; @@ -49,10 +49,12 @@ public static Type getIdentifierType(String methodName, String identifier, MySym return parameter.getType(); } - List fields = symbolTable.getFields(); - for (Symbol field : fields) { - if(Objects.equals(field.getName(), identifier)) - return field.getType(); + if (!Objects.equals(methodName, "main")){ + List fields = symbolTable.getFields(); + for (Symbol field : fields) { + if(Objects.equals(field.getName(), identifier)) + return field.getType(); + } } if(findImport(symbolTable.getImports(), identifier)) diff --git a/test/pt/up/fe/comp/CpUtils.java b/test/pt/up/fe/comp/CpUtils.java new file mode 100644 index 0000000..b19cbea --- /dev/null +++ b/test/pt/up/fe/comp/CpUtils.java @@ -0,0 +1,548 @@ +/** + * Copyright 2022 SPeCS. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. under the License. + */ + +package pt.up.fe.comp; + +import static org.junit.Assert.fail; + +import java.io.File; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.function.Consumer; +import java.util.function.Predicate; +import java.util.regex.Pattern; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.hamcrest.Matcher; +import org.junit.Assert; +import org.specs.comp.ollir.ArrayType; +import org.specs.comp.ollir.AssignInstruction; +import org.specs.comp.ollir.BinaryOpInstruction; +import org.specs.comp.ollir.CallInstruction; +import org.specs.comp.ollir.ClassType; +import org.specs.comp.ollir.ClassUnit; +import org.specs.comp.ollir.Element; +import org.specs.comp.ollir.ElementType; +import org.specs.comp.ollir.Instruction; +import org.specs.comp.ollir.Method; +import org.specs.comp.ollir.Node; +import org.specs.comp.ollir.OpInstruction; +import org.specs.comp.ollir.OperationType; +import org.specs.comp.ollir.ReturnInstruction; +import org.specs.comp.ollir.SingleOpInstruction; +import org.specs.comp.ollir.Type; + +import pt.up.fe.comp.jmm.analysis.table.SymbolTable; +import pt.up.fe.comp.jmm.jasmin.JasminResult; +import pt.up.fe.comp.jmm.ollir.OllirResult; +import pt.up.fe.specs.util.SpecsCollections; +import pt.up.fe.specs.util.SpecsIo; +import pt.up.fe.specs.util.SpecsStrings; +import pt.up.fe.specs.util.exceptions.NotImplementedException; +import pt.up.fe.specs.util.utilities.LineStream; + +/** + * Utility methods used in checkpoint tests. + * + * @author Joao Bispo + * + */ +public class CpUtils { + + // (if((\w\w)|_icmp\w+)\s+\w+) + // (if_icmpeq | if_icmpne | if_icmplt | if_icmpge | if_icmpgt | if_icmple | ifeq | ifne | iflt | ifge | ifgt | ifle) + public static final String IF_REGEX = "((if_icmpeq|if_icmpne|if_icmplt|if_icmpge|if_icmpgt|if_icmple|ifeq|ifne|iflt|ifge|ifgt|ifle)\\s+\\w+)"; + public static final String GOTO_REGEX = "(goto\\s+\\w+)"; + + static final String FIELD_PREFIX = "\\.field\\s+((public|private)\\s+)?(')?"; + static final String FIELD_SUFFIX = "(')?\\s+"; + + public static String toMessage(String message, OllirResult result) { + var finalMessage = new StringBuilder(); + + finalMessage.append(message).append("\n"); + + finalMessage.append("\nOLLIR code:\n").append(result.getOllirCode()).append("\n"); + + var numReports = result.getReports().size(); + if (numReports == 0) { + finalMessage.append("\nNo reports\n"); + } else { + finalMessage.append("\nReports (" + result.getReports().size() + "):\n"); + result.getReports().forEach(report -> finalMessage.append(report.toString() + "\n")); + } + finalMessage.append("\n"); + + return finalMessage.toString(); + // return message + "\n\nOLLIR:\n" + ollir.getOllirCode(); + } + + public static String toMessage(String message, JasminResult result) { + var finalMessage = new StringBuilder(); + + finalMessage.append(message).append("\n"); + + finalMessage.append("\nJasmin code:\n").append(result.getJasminCode()).append("\n"); + + var numReports = result.getReports().size(); + if (numReports == 0) { + finalMessage.append("\nNo reports\n"); + } else { + finalMessage.append("\nReports (" + result.getReports().size() + "):\n"); + result.getReports().forEach(report -> finalMessage.append(report.toString() + "\n")); + } + finalMessage.append("\n"); + + return finalMessage.toString(); + // return message + "\n\nJasmin:\n" + result.getJasminCode(); + } + + public static String toMessage(String message, SymbolTable result) { + var finalMessage = new StringBuilder(); + + if (message != null && !message.isBlank()) { + finalMessage.append(message).append("\n"); + } + + finalMessage.append("\nSymbol table:\n").append(result.print()).append("\n"); + + finalMessage.append("\n"); + + return finalMessage.toString(); + // return message + "\n\nJasmin:\n" + result.getJasminCode(); + } + + public static void testOllirToJasmin(String resource, String expectedOutput) { + // If AstToJasmin pipeline, do not execute test + if (TestUtils.hasAstToJasminClass()) { + return; + } + + var ollirResult = new OllirResult(SpecsIo.getResource(resource), Collections.emptyMap()); + + var result = TestUtils.backend(ollirResult); + + var testName = new File(resource).getName(); + System.out.println(testName + ":\n" + result.getJasminCode()); + var runOutput = result.runWithFullOutput(); + Assert.assertEquals("Error while running compiled Jasmin: " + runOutput.getOutput() + "\n\nJasmin code:\n" + + result.getJasminCode(), 0, runOutput.getReturnValue()); + System.out.println("\n Result: " + runOutput.getOutput()); + + if (expectedOutput != null) { + Assert.assertEquals("Output different from what was expected.\n\nJasmin code:\n" + result.getJasminCode(), + expectedOutput, runOutput.getOutput()); + } + } + + public static void testOllirToJasmin(String resource) { + testOllirToJasmin(resource, null); + } + + public static void testJmmCompilation(String resource, Consumer ollirTester, String executionOutput) { + + // If AstToJasmin pipeline, generate Jasmin + if (TestUtils.hasAstToJasminClass()) { + + var result = TestUtils.backend(SpecsIo.getResource(resource)); + + var testName = new File(resource).getName(); + System.out.println(testName + ":\n" + result.getJasminCode()); + var runOutput = result.runWithFullOutput(); + Assert.assertEquals("Error while running compiled Jasmin: " + runOutput.getOutput() + "\n\nJasmin code:\n" + + result.getJasminCode(), 0, + runOutput.getReturnValue()); + System.out.println("\n Result: " + runOutput.getOutput()); + + if (executionOutput != null) { + Assert.assertEquals( + "Output different from what was expected.\n\nJasmin code:\n" + result.getJasminCode(), + executionOutput, runOutput.getOutput()); + } + + return; + } + + var result = TestUtils.optimize(SpecsIo.getResource(resource)); + var testName = new File(resource).getName(); + System.out.println(testName + ":\n" + result.getOllirCode()); + + if (ollirTester != null) { + ollirTester.accept(result); + } + } + + public static void testJmmCompilation(String resource) { + CpUtils.testJmmCompilation(resource, null); + } + + public static void testJmmCompilation(String resource, Consumer ollirTester) { + testJmmCompilation(resource, ollirTester, null); + } + + public static void assertEquals(String message, Object expected, Object actual, OllirResult ollir) { + Assert.assertEquals(toMessage(message, ollir), expected, + actual); + } + + public static void assertEquals(String message, int expected, int actual, OllirResult ollir) { + Assert.assertEquals(toMessage(message, ollir), expected, + actual); + } + + public static void assertEquals(String message, long expected, long actual, OllirResult ollir) { + Assert.assertEquals(toMessage(message, ollir), expected, + actual); + } + + static public void assertTrue(String message, boolean condition, OllirResult ollir) { + Assert.assertTrue(toMessage(message, ollir), condition); + } + + public static void assertThat(String reason, T actual, Matcher matcher, OllirResult ollir) { + Assert.assertThat(toMessage(reason, ollir), actual, matcher); + } + + public static void assertEquals(String message, Object expected, Object actual, JasminResult result) { + Assert.assertEquals(toMessage(message, result), expected, actual); + } + + public static void assertEquals(String message, int expected, int actual, JasminResult result) { + Assert.assertEquals(toMessage(message, result), expected, actual); + } + + public static void assertNotEquals(String message, Object expected, Object actual, JasminResult jasminResult) { + Assert.assertNotEquals(toMessage(message, jasminResult), expected, actual); + } + + public static void assertNotNull(String message, Object obj, JasminResult jasminResult) { + Assert.assertNotNull(toMessage(message, jasminResult), obj); + } + + static public void assertTrue(String message, boolean condition, JasminResult result) { + Assert.assertTrue(toMessage(message, result), condition); + } + + public static void assertEquals(String message, int expected, int actual, SymbolTable results) { + Assert.assertEquals(toMessage(message, results), expected, actual); + } + + public static void assertEquals(String message, Object expected, Object actual, SymbolTable result) { + Assert.assertEquals(toMessage(message, result), expected, actual); + } + + public static org.specs.comp.ollir.Method getMethod(OllirResult result, String methodName) { + ClassUnit classUnit = result.getOllirClass(); + + for (var method : classUnit.getMethods()) { + if (method.getMethodName().equals(methodName)) { + return method; + } + } + + throw new RuntimeException(toMessage("Could not find OLLIR method with name '" + methodName + "'", result)); + } + + public static String toString(Type ollirType) { + return toString(ollirType.getTypeOfElement(), ollirType); + } + + public static String toString(ElementType elementType, Type ollirType) { + // var elementType = ollirType.getTypeOfElement(); + switch (elementType) { + case BOOLEAN: + return "bool"; + case INT32: + return "int"; + case STRING: + return "String"; + case VOID: + return "void"; + case OBJECTREF: + var objectRef = (ClassType) ollirType; + return objectRef.getName(); + case ARRAYREF: + var arrayType = (ArrayType) ollirType; + return toString(arrayType.getTypeOfElements(), null) + + SpecsStrings.buildLine("[]", arrayType.getNumDimensions()); + default: + throw new NotImplementedException(elementType); + } + } + + /** + * Tests if there an intruction of type c in the RHS of an assign. + * + * @param c + * @param method + */ + public static void assertAssignRhs(Class c, Method method, OllirResult ollir) { + + var inst = method.getInstructions().stream() + .filter(i -> i instanceof AssignInstruction) + .map(instr -> (AssignInstruction) instr) + .filter(assign -> c.isInstance(assign.getRhs())) + .findFirst(); + + assertTrue("Could not find a " + c.getName() + " in method " + method.getMethodName(), + inst.isPresent(), ollir); + } + + /** + * Tests if there an intruction of type c in the list of instructions. + * + * @param c + * @param method + */ + public static List assertInstExists(Class c, Method method, OllirResult ollir) { + + var inst = method.getInstructions().stream() + // Unfold instruction inside AssignInstrucion + .flatMap( + i -> i instanceof AssignInstruction ? Stream.of(i, ((AssignInstruction) i).getRhs()) + : Stream.of(i)) + .filter(i -> c.isInstance(i)) + .map(c::cast) + .collect(Collectors.toList()); + + assertTrue("Could not find a " + c.getName() + " in method " + method.getMethodName(), !inst.isEmpty(), + ollir); + + return inst; + } + + public static Method assertMethodExists(String methodName, OllirResult ollir) { + Method method = ollir.getOllirClass().getMethods().stream() + .filter(m -> m.getMethodName().equals(methodName)) + .findFirst() + .orElse(null); + + Assert.assertNotNull("Could not find method " + methodName + "\n\nOLLIR code:\n" + ollir.getOllirCode(), + method); + + return method; + } + + public static void assertReturnExists(Method method, OllirResult ollir) { + + var retInst = method.getInstructions().stream() + .filter(inst -> inst instanceof ReturnInstruction) + .findFirst(); + + assertTrue("Could not find a return instruction in method " + method.getMethodName() + + ollir.getOllirCode(), retInst.isPresent(), ollir); + } + + public static void assertHasOperation(OperationType opType, Method method, OllirResult ollirResult) { + + var binOps = CpUtils.assertInstExists(BinaryOpInstruction.class, method, ollirResult); + + var binOpsOfOperation = binOps.stream() + .filter(binOp -> binOp.getOperation().getOpType() == opType) + .collect(Collectors.toList()); + + CpUtils.assertTrue( + "Could not find binary operation of type " + opType.name() + " in method " + method.getMethodName(), + !binOpsOfOperation.isEmpty(), + ollirResult); + } + + public static List getOllirNodes(ClassUnit classUnit, Predicate filter) { + var nodes = new ArrayList(); + + for (var method : classUnit.getMethods()) { + getOllirNodes(method, filter, nodes); + } + // assertTrue(filterMessage, !nodes.isEmpty()); + // if (nodes.isEmpty()) { + // throw new RuntimeException(); + // } + + return nodes; + } + + public static List getOllirNodes(Method method, Predicate filter) { + var nodes = new ArrayList(); + getOllirNodes(method, filter, nodes); + return nodes; + } + + private static void getOllirNodes(Method method, Predicate filter, List filteredNodes) { + for (var inst : method.getInstructions()) { + getOllirNodes(inst, filter, filteredNodes); + } + + } + + private static void getOllirNodes(Node currentNode, Predicate filter, List filteredNodes) { + // Check if node passes the filter + if (filter.test(currentNode)) { + filteredNodes.add(currentNode); + } + + // Special cases + if (currentNode instanceof AssignInstruction) { + var assign = (AssignInstruction) currentNode; + getOllirNodes(assign.getRhs(), filter, filteredNodes); + } + } + + public static List getElements(Instruction inst) { + if (inst instanceof SingleOpInstruction) { + return Arrays.asList(((SingleOpInstruction) inst).getSingleOperand()); + } + + if (inst instanceof OpInstruction) { + return ((OpInstruction) inst).getOperands(); + } + + if (inst instanceof AssignInstruction) { + var assign = (AssignInstruction) inst; + return SpecsCollections.concat(assign.getDest(), getElements(assign.getRhs())); + } + + if (inst instanceof CallInstruction) { + var call = (CallInstruction) inst; + var operands = call.getListOfOperands(); + return operands != null ? operands : Collections.emptyList(); + } + + throw new NotImplementedException(inst.getClass()); + } + + /** + * Verifies if the given code matches (contains) the given regex + * + * @param code + * @param regex + */ + public static void matches(JasminResult result, String regex) { + matches(result, Pattern.compile(regex)); + } + + public static void matches(JasminResult result, Pattern regex) { + var matches = SpecsStrings.matches(result.getJasminCode(), regex); + assertTrue("Expected code to match /" + regex + "/", matches, result); + } + + public static void matches(String jasminCode, String regex) { + matches(jasminCode, Pattern.compile(regex)); + } + + public static void matches(String jasminCode, Pattern regex) { + var matches = SpecsStrings.matches(jasminCode, regex); + Assert.assertTrue("Expected code to match /" + regex + "/ in the following code:\n\n" + jasminCode, matches); + } + + public static void jasminHasField(JasminResult jasminResult, String fieldName, String fieldType) { + var regex = FIELD_PREFIX + fieldName + FIELD_SUFFIX + fieldType; + matches(jasminResult, regex); + } + + public static void jasminHasField(JasminResult jasminResult, String fieldType) { + var regex = FIELD_PREFIX + "\\w+" + FIELD_SUFFIX + fieldType; + matches(jasminResult, regex); + } + + public static String getJasminMethod(JasminResult jasminResult, String methodName) { + var code = jasminResult.getJasminCode(); + + if (methodName == null) { + methodName = "\\w+"; + } + + var regex = "\\.method\\s+((public|private)\\s)?+" + methodName + "((.)+?)\\.end\\s+method"; + // var regex = "\\.method\\s+((public|private)\\s+)?" + methodName + "((.|\\s)+?)\\.end\\s+method"; + + var results = SpecsStrings.getRegex(code, regex); + + assertTrue("Could not find method '" + methodName + "'", !results.isEmpty(), jasminResult); + + return results.get(2); + } + + public static String getJasminMethod(JasminResult jasminResult) { + return getJasminMethod(jasminResult, null); + } + + public static Integer getBytecodeIndex(String instructionPrefix, String jasminCode) { + try (var lines = LineStream.newInstance(jasminCode)) { + + while (lines.hasNextLine()) { + var line = lines.nextLine().strip(); + + if (!line.startsWith(instructionPrefix)) { + continue; + } + + var substring = line.substring(instructionPrefix.length()).strip(); + + if (substring.startsWith("_")) { + substring = substring.substring(1); + } + + return Integer.parseInt(substring); + } + + throw new RuntimeException("Could not find instruction with prefix " + instructionPrefix); + // fail("Could not find instruction with prefix " + instructionPrefix + " in code:\n\n" + jasminCode); + } catch (Exception e) { + fail("getBytecodeIndex('" + instructionPrefix + "'): " + e.getMessage() + "\n\n" + jasminCode); + // throw new RuntimeException( + // "Exception while looking for instruction " + instructionPrefix + " in code:\n\n" + jasminCode); + } + + return null; + } + + public static void runJasmin(JasminResult jasminResult, String expected) { + try { + var output = SpecsStrings.normalizeFileContents(jasminResult.run(), true); + assertEquals("Jasmin output", expected, output, jasminResult); + } catch(Exception e) { + throw new RuntimeException("Problems while running Jasmin code:\n" + jasminResult.getJasminCode(), e); + } + } + + private static final Pattern LIMIT_LOCALS = Pattern.compile("\\.limit\\s+locals\\s+([0-9]+)\\s+"); + + private static final Pattern LIMIT_STACK = Pattern.compile("\\.limit\\s+stack\\s+([0-9]+)\\s+"); + + public static Pattern getLimitLocalsRegex() { + return LIMIT_LOCALS; + } + + public static Pattern getLimitStackRegex() { + return LIMIT_STACK; + } + + public static String getLocalsRegex(int numLocals) { + return "(a|i)(store|load)(_|\\s+)" + (numLocals - 1); + } + + public static int countOccurences(JasminResult jasminResult, String word) { + String code = jasminResult.getJasminCode(); + return (code.length() - code.replace(word, "").length()) / word.length(); + } + + public static int countOccurrencesRegex(JasminResult jasminResult, String regexString) { + + String jasminCode = jasminResult.getJasminCode(); + var matches = SpecsStrings.getRegexGroups(jasminCode, regexString,0); + + return matches.size(); + } +} diff --git a/test/pt/up/fe/comp/cp2/JasminTest.java b/test/pt/up/fe/comp/cp2/JasminTest.java index 5b28e0d..94ef171 100644 --- a/test/pt/up/fe/comp/cp2/JasminTest.java +++ b/test/pt/up/fe/comp/cp2/JasminTest.java @@ -2,7 +2,9 @@ import org.junit.Test; import pt.up.fe.comp.TestUtils; +import pt.up.fe.comp.jmm.jasmin.JasminResult; import pt.up.fe.comp.jmm.ollir.OllirResult; +import pt.up.fe.comp2023.jasmin.JasminGenerator; import pt.up.fe.specs.util.SpecsCheck; import pt.up.fe.specs.util.SpecsIo; import utils.ProjectTestUtils; @@ -16,21 +18,41 @@ public void ollirToJasminBasic() { testOllirToJasmin("pt/up/fe/comp/cp2/jasmin/OllirToJasminBasic.ollir"); } + @Test + public void ollirToJasminBasic2() { + testOllirToJasmin("pt/up/fe/comp/cp2/jasmin/OllirToJasminBasic2.ollir"); + } + @Test public void ollirToJasminArithmetics() { testOllirToJasmin("pt/up/fe/comp/cp2/jasmin/OllirToJasminArithmetics.ollir"); } + @Test + public void ollirToJasminArithmetics2() { + testOllirToJasmin("pt/up/fe/comp/cp2/jasmin/OllirToJasminArithmetics2.ollir"); + } + @Test public void ollirToJasminInvoke() { testOllirToJasmin("pt/up/fe/comp/cp2/jasmin/OllirToJasminInvoke.ollir"); } + @Test + public void ollirToJasminInvoke2() { + testOllirToJasmin("pt/up/fe/comp/cp2/jasmin/OllirToJasminInvoke2.ollir"); + } + @Test public void ollirToJasminFields() { testOllirToJasmin("pt/up/fe/comp/cp2/jasmin/OllirToJasminFields.ollir"); } + @Test + public void ollirToJasminNonEmptyStack() { + testOllirToJasmin("pt/up/fe/comp/cp2/jasmin/OllirToJasminNonEmptyStack.ollir"); + } + public static void testOllirToJasmin(String resource, String expectedOutput) { SpecsCheck.checkArgument(resource.endsWith(".ollir"), () -> "Expected resource to end with .ollir: " + resource); @@ -47,11 +69,11 @@ public static void testOllirToJasmin(String resource, String expectedOutput) { return; } - var ollirResult = new OllirResult(SpecsIo.getResource(resource), Collections.emptyMap()); - - var result = TestUtils.backend(ollirResult); - - ProjectTestUtils.runJasmin(result, null); + OllirResult ollirResult = new OllirResult(SpecsIo.getResource(resource), Collections.emptyMap()); + JasminGenerator jasminGenerator = new JasminGenerator(); + JasminResult jasminResult = jasminGenerator.toJasmin(ollirResult); + System.out.println(jasminResult.getJasminCode()); + TestUtils.runJasmin(jasminResult.getJasminCode()); } public static void testOllirToJasmin(String resource) { diff --git a/test/pt/up/fe/comp/cp2/jasmin/OllirToJasminArithmetics.ollir b/test/pt/up/fe/comp/cp2/jasmin/OllirToJasminArithmetics.ollir index 5343a6d..000e05e 100644 --- a/test/pt/up/fe/comp/cp2/jasmin/OllirToJasminArithmetics.ollir +++ b/test/pt/up/fe/comp/cp2/jasmin/OllirToJasminArithmetics.ollir @@ -14,6 +14,9 @@ Test { b.i32 :=.i32 2.i32; c.i32 :=.i32 a.i32 +.i32 b.i32; + d.bool :=.bool !.bool 0.bool; + e.bool :=.bool 1.bool &&.bool 1.bool; + f.i32 :=.i32 a.i32 &&.i32 b.i32; ret.i32 c.i32; } diff --git a/test/pt/up/fe/comp/cp2/jasmin/OllirToJasminArithmetics2.ollir b/test/pt/up/fe/comp/cp2/jasmin/OllirToJasminArithmetics2.ollir new file mode 100644 index 0000000..0ffadf4 --- /dev/null +++ b/test/pt/up/fe/comp/cp2/jasmin/OllirToJasminArithmetics2.ollir @@ -0,0 +1,23 @@ +Test { + + .construct Test().V { + invokespecial(this, "").V; + } + + .method public static main(args.array.String).V { + ret.V; + } + + .method public foo2().i32 { + ret.i32 33000.i32; + } + + .method public foo1().i32 { + a.i32 :=.i32 1000.i32; + b.i32 :=.i32 2.i32; + + c.i32 :=.i32 a.i32 +.i32 b.i32; + + ret.i32 c.i32; + } +} \ No newline at end of file diff --git a/test/pt/up/fe/comp/cp2/jasmin/OllirToJasminBasic.ollir b/test/pt/up/fe/comp/cp2/jasmin/OllirToJasminBasic.ollir index a3411d7..da9e085 100644 --- a/test/pt/up/fe/comp/cp2/jasmin/OllirToJasminBasic.ollir +++ b/test/pt/up/fe/comp/cp2/jasmin/OllirToJasminBasic.ollir @@ -5,6 +5,9 @@ SymbolTable extends Quicksort { .field public intField.i32; .field public boolField.bool; + .field public myArray1.array.array.i32; + .field public myArray2.array.String; + .field private static myObject.IOException; .construct SymbolTable().V { invokespecial(this, "").V; diff --git a/test/pt/up/fe/comp/cp2/jasmin/OllirToJasminBasic2.ollir b/test/pt/up/fe/comp/cp2/jasmin/OllirToJasminBasic2.ollir new file mode 100644 index 0000000..0e648ea --- /dev/null +++ b/test/pt/up/fe/comp/cp2/jasmin/OllirToJasminBasic2.ollir @@ -0,0 +1,33 @@ +import io; +import Quicksort; + +SymbolTable extends Quicksort { + + .field public intField.i32; + .field public boolField.bool; + .field public myArray.array.array.i32; + .field public myArray.array.String; + .field private static myObject.IOException; + + .construct SymbolTable().V { + invokespecial(this, "").V; + } + + .method public method1(stringArg.String).i32 { + intLocal1.i32 :=.i32 0.i32; + boolLocal1.bool :=.bool 1.bool; + boolLocal2.bool :=.bool boolLocal1.bool; + stringVar.String :=.String stringArg.String; + + ret.i32 0.i32; + } + + .method public method2(intParam1.i32, boolParam1.bool).bool { + ret.bool boolParam1.bool; + } + + .method public static main(args.array.String).V { + ret.V; + } + +} diff --git a/test/pt/up/fe/comp/cp2/jasmin/OllirToJasminFields.ollir b/test/pt/up/fe/comp/cp2/jasmin/OllirToJasminFields.ollir index 169119d..98cc67a 100644 --- a/test/pt/up/fe/comp/cp2/jasmin/OllirToJasminFields.ollir +++ b/test/pt/up/fe/comp/cp2/jasmin/OllirToJasminFields.ollir @@ -11,11 +11,11 @@ Test { } - .method public foo().V { + .method public foo().i32 { putfield(this, intField.i32, 10.i32).V; a.i32 :=.i32 getfield(this, intField.i32).i32; - ret.V; + ret.i32 0.i32; } } diff --git a/test/pt/up/fe/comp/cp2/jasmin/OllirToJasminInvoke2.ollir b/test/pt/up/fe/comp/cp2/jasmin/OllirToJasminInvoke2.ollir new file mode 100644 index 0000000..581617c --- /dev/null +++ b/test/pt/up/fe/comp/cp2/jasmin/OllirToJasminInvoke2.ollir @@ -0,0 +1,29 @@ +import io; + +Test { + + .construct Test().V { + invokespecial(this, "").V; + } + + .method public foo1().i32 { + ret.i32 33000.i32; + } + + .method public foo2(arg1.i32).i32 { + ret.i32 arg1.i32; + } + + .method public static main(args.array.String).V { + test.Test :=.Test new(Test).Test; + invokespecial(test.Test,"").V; + + var1.i32 :=.i32 invokevirtual(test.Test, "foo1").i32; + var2.i32 :=.i32 invokevirtual(test.Test, "foo2", 3.i32).i32; + + s.String :=.String ldc("HelloWorld").String; + invokestatic(io, "println", s.String).V; + + ret.V; + } +} diff --git a/test/pt/up/fe/comp/cp2/jasmin/OllirToJasminNonEmptyStack.ollir b/test/pt/up/fe/comp/cp2/jasmin/OllirToJasminNonEmptyStack.ollir new file mode 100644 index 0000000..2dc6ee3 --- /dev/null +++ b/test/pt/up/fe/comp/cp2/jasmin/OllirToJasminNonEmptyStack.ollir @@ -0,0 +1,22 @@ +Test { + .construct Test().V { + invokespecial(this, "").V; + } + + .method foo2().i32 { + ret.i32 2.i32; + } + + .method public foo1().i32 { + intLocal1.i32 :=.i32 0.i32; + boolLocal1.bool :=.bool 1.bool; + invokevirtual(this, "foo2").i32; + + ret.i32 0.i32; + } + + .method public static main(args.array.String).V { + ret.V; + } + +} diff --git a/test/pt/up/fe/comp/cp2/ollir/CompileMethodInvocation.jmm b/test/pt/up/fe/comp/cp2/ollir/CompileMethodInvocation.jmm index a5aced4..80a0f61 100644 --- a/test/pt/up/fe/comp/cp2/ollir/CompileMethodInvocation.jmm +++ b/test/pt/up/fe/comp/cp2/ollir/CompileMethodInvocation.jmm @@ -16,3 +16,4 @@ class CompileMethodInvocation { return 0; } } + diff --git a/test/pt/up/fe/comp/cpf/1_parser_and_tree/AddMultConstants.jmm b/test/pt/up/fe/comp/cpf/1_parser_and_tree/AddMultConstants.jmm new file mode 100644 index 0000000..186933b --- /dev/null +++ b/test/pt/up/fe/comp/cpf/1_parser_and_tree/AddMultConstants.jmm @@ -0,0 +1,8 @@ +import io; +class AddMultConstants { + public static void main(String[] args) { + io.println(1+2*3); + io.println(1+2*3+5); + io.println(1+2*3+6/3); + } +} \ No newline at end of file diff --git a/test/pt/up/fe/comp/cpf/2_semantic_analysis/import/ImportSuper.jmm b/test/pt/up/fe/comp/cpf/2_semantic_analysis/import/ImportSuper.jmm new file mode 100644 index 0000000..80df8b2 --- /dev/null +++ b/test/pt/up/fe/comp/cpf/2_semantic_analysis/import/ImportSuper.jmm @@ -0,0 +1,4 @@ +import Sup; + +class Simple extends Sup { +} diff --git a/test/pt/up/fe/comp/cpf/2_semantic_analysis/lookup/VarLookup_Field.jmm b/test/pt/up/fe/comp/cpf/2_semantic_analysis/lookup/VarLookup_Field.jmm new file mode 100644 index 0000000..4de5096 --- /dev/null +++ b/test/pt/up/fe/comp/cpf/2_semantic_analysis/lookup/VarLookup_Field.jmm @@ -0,0 +1,17 @@ +import io; + +class A { + int a; + + public int x(){ + a = 10; + io.print(a); + return a; + } + + public static void main(String[] args){ + A a; + a = new A(); + a.x(); + } +} \ No newline at end of file diff --git a/test/pt/up/fe/comp/cpf/2_semantic_analysis/lookup/VarLookup_Field_Main_Fail.jmm b/test/pt/up/fe/comp/cpf/2_semantic_analysis/lookup/VarLookup_Field_Main_Fail.jmm new file mode 100644 index 0000000..9f39ad7 --- /dev/null +++ b/test/pt/up/fe/comp/cpf/2_semantic_analysis/lookup/VarLookup_Field_Main_Fail.jmm @@ -0,0 +1,8 @@ + +class A { + int a; + + public static void main(String[] args){ + a = 10; + } +} \ No newline at end of file diff --git a/test/pt/up/fe/comp/cpf/2_semantic_analysis/lookup/VarLookup_Local.jmm b/test/pt/up/fe/comp/cpf/2_semantic_analysis/lookup/VarLookup_Local.jmm new file mode 100644 index 0000000..a6819e1 --- /dev/null +++ b/test/pt/up/fe/comp/cpf/2_semantic_analysis/lookup/VarLookup_Local.jmm @@ -0,0 +1,12 @@ +import io; + +class A { + + int a; + + public static void main(String[] args){ + int a; + a = 10; + io.print(10); + } +} \ No newline at end of file diff --git a/test/pt/up/fe/comp/cpf/2_semantic_analysis/symboltable/MethodsAndFields.jmm b/test/pt/up/fe/comp/cpf/2_semantic_analysis/symboltable/MethodsAndFields.jmm new file mode 100644 index 0000000..247b8c7 --- /dev/null +++ b/test/pt/up/fe/comp/cpf/2_semantic_analysis/symboltable/MethodsAndFields.jmm @@ -0,0 +1,28 @@ + +class MethodsAndFields{ + int field1; + boolean field2; + MethodsAndFields field3; + + public int getField1(){ + return field1; + } + + public boolean getField2(){ + return field2; + } + + public MethodsAndFields getField3(){ + return field3; + } + + public int[] all(int a, boolean b, MethodsAndFields maf){ + int[] c; + c = new int[1]; + return c; + } + + public static void main(String[] args){ + + } +} \ No newline at end of file diff --git a/test/pt/up/fe/comp/cpf/2_semantic_analysis/symboltable/Parameters.jmm b/test/pt/up/fe/comp/cpf/2_semantic_analysis/symboltable/Parameters.jmm new file mode 100644 index 0000000..d67d183 --- /dev/null +++ b/test/pt/up/fe/comp/cpf/2_semantic_analysis/symboltable/Parameters.jmm @@ -0,0 +1,7 @@ + +class Parameters{ + + public int all(int a, boolean b, Parameters maf){ + return 0; + } +} \ No newline at end of file diff --git a/test/pt/up/fe/comp/cpf/3_ollir/arithmetic/Arithmetic_and.jmm b/test/pt/up/fe/comp/cpf/3_ollir/arithmetic/Arithmetic_and.jmm new file mode 100644 index 0000000..deb17fa --- /dev/null +++ b/test/pt/up/fe/comp/cpf/3_ollir/arithmetic/Arithmetic_and.jmm @@ -0,0 +1,16 @@ +import io; + +class Arithmetic_and { + public static void main(String[] args) { + boolean a; + + a = true && false; + + if(a) { + io.print(1); + } else { + io.print(0); + } + + } +} \ No newline at end of file diff --git a/test/pt/up/fe/comp/cpf/3_ollir/arithmetic/Arithmetic_less.jmm b/test/pt/up/fe/comp/cpf/3_ollir/arithmetic/Arithmetic_less.jmm new file mode 100644 index 0000000..636daba --- /dev/null +++ b/test/pt/up/fe/comp/cpf/3_ollir/arithmetic/Arithmetic_less.jmm @@ -0,0 +1,15 @@ +import io; + +class Arithmetic_less { + public static void main(String[] args) { + boolean a; + + a = 10 < 20; + + if(a) { + io.print(1); + } else { + io.print(0); + } + } +} \ No newline at end of file diff --git a/test/pt/up/fe/comp/cpf/3_ollir/arrays/ArrayAccess.jmm b/test/pt/up/fe/comp/cpf/3_ollir/arrays/ArrayAccess.jmm new file mode 100644 index 0000000..02436b9 --- /dev/null +++ b/test/pt/up/fe/comp/cpf/3_ollir/arrays/ArrayAccess.jmm @@ -0,0 +1,23 @@ +import ioPlus; +class ArrayAccess { + + public int foo(int[] a) { + int result; + + result = a[0]; + result = a[1] + a[2]; + result = result + a[3]; + result = 1 + a[4]; + + a[0] = 1; + a[1] = 2; + a[2] = 3; + a[3] = 4; + a[4] = 5; + + + return result; + } + + +} \ No newline at end of file diff --git a/test/pt/up/fe/comp/cpf/3_ollir/arrays/ArrayInit.jmm b/test/pt/up/fe/comp/cpf/3_ollir/arrays/ArrayInit.jmm new file mode 100644 index 0000000..bbb1d60 --- /dev/null +++ b/test/pt/up/fe/comp/cpf/3_ollir/arrays/ArrayInit.jmm @@ -0,0 +1,9 @@ +import ioPlus; +class ArrayInit { + + public static void main(String[] args) { + int[] a; + a = new int[5]; + ioPlus.printResult(a.length); + } +} \ No newline at end of file diff --git a/test/pt/up/fe/comp/cpf/3_ollir/arrays/ComplexArrayAccess.jmm b/test/pt/up/fe/comp/cpf/3_ollir/arrays/ComplexArrayAccess.jmm new file mode 100644 index 0000000..080eb32 --- /dev/null +++ b/test/pt/up/fe/comp/cpf/3_ollir/arrays/ComplexArrayAccess.jmm @@ -0,0 +1,27 @@ +import ioPlus; +class ComplexArrayAccess { + + public int func(int a) { + return a; + } + + public static void main(String[] args) { + int[] a; + int i; + ComplexArrayAccess d; + d = new ComplexArrayAccess(); + a = new int[5]; + a[0] = 1; + a[1] = 2; + a[2] = 3; + a[3] = 4; + a[4] = 5; + i = 1; + ioPlus.printResult(a[d.func(0)]); + ioPlus.printResult(a[i]); + ioPlus.printResult(a[4/2]); + ioPlus.printResult(a[a[2]]); + ioPlus.printResult(a[a.length -1 ]); + + } +} \ No newline at end of file diff --git a/test/pt/up/fe/comp/cpf/3_ollir/basic/BasicMethodsArray.jmm b/test/pt/up/fe/comp/cpf/3_ollir/basic/BasicMethodsArray.jmm new file mode 100644 index 0000000..7277605 --- /dev/null +++ b/test/pt/up/fe/comp/cpf/3_ollir/basic/BasicMethodsArray.jmm @@ -0,0 +1,13 @@ + +import Other; +// import Person; + +class BasicMethods extends Other { + + public int[] func4(){ + int[] k; + k = new int[1]; + return k; + } + +} \ No newline at end of file diff --git a/test/pt/up/fe/comp/cpf/3_ollir/control_flow/SimpleIfElseStat.jmm b/test/pt/up/fe/comp/cpf/3_ollir/control_flow/SimpleIfElseStat.jmm new file mode 100644 index 0000000..f089a9e --- /dev/null +++ b/test/pt/up/fe/comp/cpf/3_ollir/control_flow/SimpleIfElseStat.jmm @@ -0,0 +1,17 @@ +import ioPlus; +class SimpleIfElseStat { + + + public int func(int a, int b){ + int result; + + if(a < b){ + result = a; + } else { + result = b; + } + + return result; + } + +} \ No newline at end of file diff --git a/test/pt/up/fe/comp/cpf/3_ollir/control_flow/SimpleWhileStat.jmm b/test/pt/up/fe/comp/cpf/3_ollir/control_flow/SimpleWhileStat.jmm new file mode 100644 index 0000000..aec4c79 --- /dev/null +++ b/test/pt/up/fe/comp/cpf/3_ollir/control_flow/SimpleWhileStat.jmm @@ -0,0 +1,17 @@ +import ioPlus; +class SimpleWhileStat { + + + public int func(int a){ + int c; + int i; + i = 0; + + while (i < a) { + i = i + 1; + } + + return i; + } + +} \ No newline at end of file diff --git a/test/pt/up/fe/comp/cpf/3_ollir/control_flow/SwitchStat.jmm b/test/pt/up/fe/comp/cpf/3_ollir/control_flow/SwitchStat.jmm new file mode 100644 index 0000000..d0de4e3 --- /dev/null +++ b/test/pt/up/fe/comp/cpf/3_ollir/control_flow/SwitchStat.jmm @@ -0,0 +1,37 @@ +import ioPlus; +class SwitchStat { + + + public int func(int a){ + int result; + + if(a < 1){ + result =1; + } else { + if(a < 2){ + result = 2; + } else { + if(a < 3){ + result = 3; + } else { + if(a < 4){ + result = 4; + } else { + if(a < 5){ + result = 5; + } else { + if(a < 6){ + result = 6; + } else { + result = 7; + } + } + } + } + } + } + + return result; + } + +} \ No newline at end of file diff --git a/test/pt/up/fe/comp/cpf/4_jasmin/arithmetic/Arithmetic_and.jmm b/test/pt/up/fe/comp/cpf/4_jasmin/arithmetic/Arithmetic_and.jmm new file mode 100644 index 0000000..deb17fa --- /dev/null +++ b/test/pt/up/fe/comp/cpf/4_jasmin/arithmetic/Arithmetic_and.jmm @@ -0,0 +1,16 @@ +import io; + +class Arithmetic_and { + public static void main(String[] args) { + boolean a; + + a = true && false; + + if(a) { + io.print(1); + } else { + io.print(0); + } + + } +} \ No newline at end of file diff --git a/test/pt/up/fe/comp/cpf/4_jasmin/arithmetic/Arithmetic_and.ollir b/test/pt/up/fe/comp/cpf/4_jasmin/arithmetic/Arithmetic_and.ollir new file mode 100644 index 0000000..81a0da7 --- /dev/null +++ b/test/pt/up/fe/comp/cpf/4_jasmin/arithmetic/Arithmetic_and.ollir @@ -0,0 +1,20 @@ +import io; +Arithmetic_and { + + .construct Arithmetic_and().V { + invokespecial(this, "").V; + } + + .method public static main(args.array.String).V { + a.bool :=.bool 1.bool &&.bool 0.bool; + + if (a.bool) goto ifbody_0; + invokestatic(io, "print", 0.i32).V; + goto endif_0; + ifbody_0: + invokestatic(io, "print", 1.i32).V; + endif_0: + + ret.V; + } +} \ No newline at end of file diff --git a/test/pt/up/fe/comp/cpf/4_jasmin/arithmetic/Arithmetic_less.jmm b/test/pt/up/fe/comp/cpf/4_jasmin/arithmetic/Arithmetic_less.jmm new file mode 100644 index 0000000..636daba --- /dev/null +++ b/test/pt/up/fe/comp/cpf/4_jasmin/arithmetic/Arithmetic_less.jmm @@ -0,0 +1,15 @@ +import io; + +class Arithmetic_less { + public static void main(String[] args) { + boolean a; + + a = 10 < 20; + + if(a) { + io.print(1); + } else { + io.print(0); + } + } +} \ No newline at end of file diff --git a/test/pt/up/fe/comp/cpf/4_jasmin/arithmetic/Arithmetic_less.ollir b/test/pt/up/fe/comp/cpf/4_jasmin/arithmetic/Arithmetic_less.ollir new file mode 100644 index 0000000..a638c7d --- /dev/null +++ b/test/pt/up/fe/comp/cpf/4_jasmin/arithmetic/Arithmetic_less.ollir @@ -0,0 +1,20 @@ +import io; +Arithmetic_less { + + .construct Arithmetic_less().V { + invokespecial(this, "").V; + } + + .method public static main(args.array.String).V { + a.bool :=.bool 10.i32 <.bool 20.i32; + + if (a.bool) goto ifbody_0; + invokestatic(io, "print", 0.i32).V; + goto endif_0; + ifbody_0: + invokestatic(io, "print", 1.i32).V; + endif_0: + + ret.V; + } +} \ No newline at end of file diff --git a/test/pt/up/fe/comp/cpf/4_jasmin/arithmetic/ByteCodeIndexes1.jmm b/test/pt/up/fe/comp/cpf/4_jasmin/arithmetic/ByteCodeIndexes1.jmm new file mode 100644 index 0000000..59410b3 --- /dev/null +++ b/test/pt/up/fe/comp/cpf/4_jasmin/arithmetic/ByteCodeIndexes1.jmm @@ -0,0 +1,7 @@ +class ByteCodeIndexes1 { + + public int func(int a) + { + return a; + } +} \ No newline at end of file diff --git a/test/pt/up/fe/comp/cpf/4_jasmin/arithmetic/ByteCodeIndexes1.ollir b/test/pt/up/fe/comp/cpf/4_jasmin/arithmetic/ByteCodeIndexes1.ollir new file mode 100644 index 0000000..ce8bb81 --- /dev/null +++ b/test/pt/up/fe/comp/cpf/4_jasmin/arithmetic/ByteCodeIndexes1.ollir @@ -0,0 +1,10 @@ +ByteCodeIndexes1 { + + .construct ByteCodeIndexes1().V { + invokespecial(this, "").V; + } + + .method public func(a.i32).i32 { + ret.i32 a.i32; + } +} \ No newline at end of file diff --git a/test/pt/up/fe/comp/cpf/4_jasmin/arithmetic/ByteCodeIndexes2.jmm b/test/pt/up/fe/comp/cpf/4_jasmin/arithmetic/ByteCodeIndexes2.jmm new file mode 100644 index 0000000..5408811 --- /dev/null +++ b/test/pt/up/fe/comp/cpf/4_jasmin/arithmetic/ByteCodeIndexes2.jmm @@ -0,0 +1,9 @@ +class ByteCodeIndexes2 { + + public int func(int a) + { + int b; + b = 11; + return b; + } +} \ No newline at end of file diff --git a/test/pt/up/fe/comp/cpf/4_jasmin/arithmetic/ByteCodeIndexes2.ollir b/test/pt/up/fe/comp/cpf/4_jasmin/arithmetic/ByteCodeIndexes2.ollir new file mode 100644 index 0000000..f06a713 --- /dev/null +++ b/test/pt/up/fe/comp/cpf/4_jasmin/arithmetic/ByteCodeIndexes2.ollir @@ -0,0 +1,11 @@ +ByteCodeIndexes2 { + + .construct ByteCodeIndexes2().V { + invokespecial(this, "").V; + } + + .method public func(a.i32).i32 { + b.i32 :=.i32 11.i32; + ret.i32 b.i32; + } +} \ No newline at end of file diff --git a/test/pt/up/fe/comp/cpf/4_jasmin/arrays/ArrayAccess.jmm b/test/pt/up/fe/comp/cpf/4_jasmin/arrays/ArrayAccess.jmm new file mode 100644 index 0000000..83b9e11 --- /dev/null +++ b/test/pt/up/fe/comp/cpf/4_jasmin/arrays/ArrayAccess.jmm @@ -0,0 +1,20 @@ +import ioPlus; +class ArrayAccess { + + public static void main(String[] args) { + int[] a; + a = new int[5]; + a[0] = 1; + a[1] = 2; + a[2] = 3; + a[3] = 4; + a[4] = 5; + + ioPlus.printResult(a[0]); + ioPlus.printResult(a[1]); + ioPlus.printResult(a[2]); + ioPlus.printResult(a[3]); + ioPlus.printResult(a[4]); + + } +} \ No newline at end of file diff --git a/test/pt/up/fe/comp/cpf/4_jasmin/arrays/ArrayAccess.ollir b/test/pt/up/fe/comp/cpf/4_jasmin/arrays/ArrayAccess.ollir new file mode 100644 index 0000000..78df91e --- /dev/null +++ b/test/pt/up/fe/comp/cpf/4_jasmin/arrays/ArrayAccess.ollir @@ -0,0 +1,40 @@ +import ioPlus; +ArrayAccess { + + .construct ArrayAccess().V { + invokespecial(this, "").V; + } + + .method public static main(args.array.String).V { +temp0.i32 :=.i32 5.i32; +a.array.i32 :=.array.i32 new(array, temp0.i32).array.i32; +temp1.i32 :=.i32 0.i32; +a[temp1.i32].i32 :=.i32 1.i32; +temp2.i32 :=.i32 1.i32; +a[temp2.i32].i32 :=.i32 2.i32; +temp3.i32 :=.i32 2.i32; +a[temp3.i32].i32 :=.i32 3.i32; +temp4.i32 :=.i32 3.i32; +a[temp4.i32].i32 :=.i32 4.i32; +temp5.i32 :=.i32 4.i32; +a[temp5.i32].i32 :=.i32 5.i32; +temp8.i32 :=.i32 0.i32; +temp7.i32 :=.i32 a[temp8.i32].i32; +invokestatic(ioPlus, "printResult", temp7.i32).V; +temp11.i32 :=.i32 1.i32; +temp10.i32 :=.i32 a[temp11.i32].i32; +invokestatic(ioPlus, "printResult", temp10.i32).V; +temp14.i32 :=.i32 2.i32; +temp13.i32 :=.i32 a[temp14.i32].i32; +invokestatic(ioPlus, "printResult", temp13.i32).V; +temp17.i32 :=.i32 3.i32; +temp16.i32 :=.i32 a[temp17.i32].i32; +invokestatic(ioPlus, "printResult", temp16.i32).V; +temp20.i32 :=.i32 4.i32; +temp19.i32 :=.i32 a[temp20.i32].i32; +invokestatic(ioPlus, "printResult", temp19.i32).V; + +ret.V; + } + +} \ No newline at end of file diff --git a/test/pt/up/fe/comp/cpf/4_jasmin/arrays/ArrayAsArg.jmm b/test/pt/up/fe/comp/cpf/4_jasmin/arrays/ArrayAsArg.jmm new file mode 100644 index 0000000..eeb9a56 --- /dev/null +++ b/test/pt/up/fe/comp/cpf/4_jasmin/arrays/ArrayAsArg.jmm @@ -0,0 +1,32 @@ +import ioPlus; +class ArrayAsArg { + + public int func(int[] a) + { + int b; + b = a.length; + return b; + } + + public int func2() + { + int x; + int[] a; + ArrayAsArg c; + c = new ArrayAsArg(); + a = new int[2]; + x = c.func(a); + return x; + } + + public static void main(String[] args) + { + int x; + ArrayAsArg c; + c = new ArrayAsArg(); + x = c.func2(); + ioPlus.printResult(x); + } + + +} \ No newline at end of file diff --git a/test/pt/up/fe/comp/cpf/4_jasmin/arrays/ArrayAsArg.ollir b/test/pt/up/fe/comp/cpf/4_jasmin/arrays/ArrayAsArg.ollir new file mode 100644 index 0000000..ed40a7e --- /dev/null +++ b/test/pt/up/fe/comp/cpf/4_jasmin/arrays/ArrayAsArg.ollir @@ -0,0 +1,29 @@ +import ioPlus; +ArrayAsArg { + + .construct ArrayAsArg().V { + invokespecial(this, "").V; + } + + .method public func(a.array.i32).i32 { +b.i32 :=.i32 arraylength(a.array.i32).i32; +ret.i32 b.i32; + } + .method public func2().i32 { +c.ArrayAsArg :=.ArrayAsArg new(ArrayAsArg).ArrayAsArg; +invokespecial(c.ArrayAsArg,"").V; +temp0.i32 :=.i32 2.i32; +a.array.i32 :=.array.i32 new(array, temp0.i32).array.i32; +x.i32 :=.i32 invokevirtual(c.ArrayAsArg, "func", a.array.i32).i32; +ret.i32 x.i32; + } + .method public static main(args.array.String).V { +c.ArrayAsArg :=.ArrayAsArg new(ArrayAsArg).ArrayAsArg; +invokespecial(c.ArrayAsArg,"").V; +x.i32 :=.i32 invokevirtual(c.ArrayAsArg, "func2").i32; +invokestatic(ioPlus, "printResult", x.i32).V; + +ret.V; + } + +} \ No newline at end of file diff --git a/test/pt/up/fe/comp/cpf/4_jasmin/arrays/ArrayAsArgCode.jmm b/test/pt/up/fe/comp/cpf/4_jasmin/arrays/ArrayAsArgCode.jmm new file mode 100644 index 0000000..99de593 --- /dev/null +++ b/test/pt/up/fe/comp/cpf/4_jasmin/arrays/ArrayAsArgCode.jmm @@ -0,0 +1,17 @@ +import ioPlus; +class ArrayAsArg { + + public int func(int[] a) + { + int x; + int[] arr; + ArrayAsArg c; + + c = new ArrayAsArg(); + arr = new int[2]; + x = c.func(arr); + return x; + } + + +} \ No newline at end of file diff --git a/test/pt/up/fe/comp/cpf/4_jasmin/arrays/ArrayAsArgCode.ollir b/test/pt/up/fe/comp/cpf/4_jasmin/arrays/ArrayAsArgCode.ollir new file mode 100644 index 0000000..1fd8ba9 --- /dev/null +++ b/test/pt/up/fe/comp/cpf/4_jasmin/arrays/ArrayAsArgCode.ollir @@ -0,0 +1,17 @@ +import ioPlus; +ArrayAsArg { + + .construct ArrayAsArg().V { + invokespecial(this, "").V; + } + + .method public func(a.array.i32).i32 { +c.ArrayAsArg :=.ArrayAsArg new(ArrayAsArg).ArrayAsArg; +invokespecial(c.ArrayAsArg,"").V; +temp0.i32 :=.i32 2.i32; +arr.array.i32 :=.array.i32 new(array, temp0.i32).array.i32; +x.i32 :=.i32 invokevirtual(c.ArrayAsArg, "func", arr.array.i32).i32; +ret.i32 x.i32; + } + +} \ No newline at end of file diff --git a/test/pt/up/fe/comp/cpf/4_jasmin/arrays/ArrayInit.jmm b/test/pt/up/fe/comp/cpf/4_jasmin/arrays/ArrayInit.jmm new file mode 100644 index 0000000..bbb1d60 --- /dev/null +++ b/test/pt/up/fe/comp/cpf/4_jasmin/arrays/ArrayInit.jmm @@ -0,0 +1,9 @@ +import ioPlus; +class ArrayInit { + + public static void main(String[] args) { + int[] a; + a = new int[5]; + ioPlus.printResult(a.length); + } +} \ No newline at end of file diff --git a/test/pt/up/fe/comp/cpf/4_jasmin/arrays/ArrayInit.ollir b/test/pt/up/fe/comp/cpf/4_jasmin/arrays/ArrayInit.ollir new file mode 100644 index 0000000..35dd718 --- /dev/null +++ b/test/pt/up/fe/comp/cpf/4_jasmin/arrays/ArrayInit.ollir @@ -0,0 +1,16 @@ +import ioPlus; +ArrayInit { + + .construct ArrayInit().V { + invokespecial(this, "").V; + } + + .method public static main(args.array.String).V { + temp0.i32 :=.i32 5.i32; + a.array.i32 :=.array.i32 new(array, temp0.i32).array.i32; + temp1.i32 :=.i32 arraylength(a.array.i32).i32.i32; + invokestatic(ioPlus, "printResult", temp1.i32).V; + + ret.V; + } +} \ No newline at end of file diff --git a/test/pt/up/fe/comp/cpf/4_jasmin/arrays/ArrayInitSimple.ollir b/test/pt/up/fe/comp/cpf/4_jasmin/arrays/ArrayInitSimple.ollir new file mode 100644 index 0000000..56a3f6b --- /dev/null +++ b/test/pt/up/fe/comp/cpf/4_jasmin/arrays/ArrayInitSimple.ollir @@ -0,0 +1,14 @@ +import ioPlus; +ArrayInit { + + .construct ArrayInit().V { + invokespecial(this, "").V; + } + + .method public static main(args.array.String).V { + temp0.i32 :=.i32 5.i32; + a.array.i32 :=.array.i32 new(array, temp0.i32).array.i32; + + ret.V; + } +} \ No newline at end of file diff --git a/test/pt/up/fe/comp/cpf/4_jasmin/arrays/ComplexArrayAccess.jmm b/test/pt/up/fe/comp/cpf/4_jasmin/arrays/ComplexArrayAccess.jmm new file mode 100644 index 0000000..080eb32 --- /dev/null +++ b/test/pt/up/fe/comp/cpf/4_jasmin/arrays/ComplexArrayAccess.jmm @@ -0,0 +1,27 @@ +import ioPlus; +class ComplexArrayAccess { + + public int func(int a) { + return a; + } + + public static void main(String[] args) { + int[] a; + int i; + ComplexArrayAccess d; + d = new ComplexArrayAccess(); + a = new int[5]; + a[0] = 1; + a[1] = 2; + a[2] = 3; + a[3] = 4; + a[4] = 5; + i = 1; + ioPlus.printResult(a[d.func(0)]); + ioPlus.printResult(a[i]); + ioPlus.printResult(a[4/2]); + ioPlus.printResult(a[a[2]]); + ioPlus.printResult(a[a.length -1 ]); + + } +} \ No newline at end of file diff --git a/test/pt/up/fe/comp/cpf/4_jasmin/arrays/ComplexArrayAccess.ollir b/test/pt/up/fe/comp/cpf/4_jasmin/arrays/ComplexArrayAccess.ollir new file mode 100644 index 0000000..ccf0d32 --- /dev/null +++ b/test/pt/up/fe/comp/cpf/4_jasmin/arrays/ComplexArrayAccess.ollir @@ -0,0 +1,48 @@ +import ioPlus; +ComplexArrayAccess { + + .construct ComplexArrayAccess().V { + invokespecial(this, "").V; + } + + .method public func(a.i32).i32 { +ret.i32 a.i32; + } + .method public static main(args.array.String).V { +d.ComplexArrayAccess :=.ComplexArrayAccess new(ComplexArrayAccess).ComplexArrayAccess; +invokespecial(d.ComplexArrayAccess,"").V; +temp0.i32 :=.i32 5.i32; +a.array.i32 :=.array.i32 new(array, temp0.i32).array.i32; +temp1.i32 :=.i32 0.i32; +a[temp1.i32].i32 :=.i32 1.i32; +temp2.i32 :=.i32 1.i32; +a[temp2.i32].i32 :=.i32 2.i32; +temp3.i32 :=.i32 2.i32; +a[temp3.i32].i32 :=.i32 3.i32; +temp4.i32 :=.i32 3.i32; +a[temp4.i32].i32 :=.i32 4.i32; +temp5.i32 :=.i32 4.i32; +a[temp5.i32].i32 :=.i32 5.i32; +i.i32 :=.i32 1.i32; +temp8.i32 :=.i32 invokevirtual(d.ComplexArrayAccess, "func", 0.i32).i32.i32; +temp7.i32 :=.i32 a[temp8.i32].i32; +invokestatic(ioPlus, "printResult", temp7.i32).V; +temp11.i32 :=.i32 i.i32; +temp10.i32 :=.i32 a[temp11.i32].i32; +invokestatic(ioPlus, "printResult", temp10.i32).V; +temp14.i32 :=.i32 4.i32 /.i32 2.i32; +temp13.i32 :=.i32 a[temp14.i32].i32; +invokestatic(ioPlus, "printResult", temp13.i32).V; +temp21.i32 :=.i32 2.i32; +temp20.i32 :=.i32 a[temp21.i32].i32; +temp18.i32 :=.i32 a[temp20.i32].i32; +invokestatic(ioPlus, "printResult", temp18.i32).V; +temp28.i32 :=.i32 arraylength(a.array.i32).i32.i32.i32; +temp27.i32 :=.i32 temp28.i32 -.i32 1.i32; +temp25.i32 :=.i32 a[temp27.i32].i32; +invokestatic(ioPlus, "printResult", temp25.i32).V; + +ret.V; + } + +} \ No newline at end of file diff --git a/test/pt/up/fe/comp/cpf/4_jasmin/basic/BasicMethodsArray.jmm b/test/pt/up/fe/comp/cpf/4_jasmin/basic/BasicMethodsArray.jmm new file mode 100644 index 0000000..7277605 --- /dev/null +++ b/test/pt/up/fe/comp/cpf/4_jasmin/basic/BasicMethodsArray.jmm @@ -0,0 +1,13 @@ + +import Other; +// import Person; + +class BasicMethods extends Other { + + public int[] func4(){ + int[] k; + k = new int[1]; + return k; + } + +} \ No newline at end of file diff --git a/test/pt/up/fe/comp/cpf/4_jasmin/basic/BasicMethodsArray.ollir b/test/pt/up/fe/comp/cpf/4_jasmin/basic/BasicMethodsArray.ollir new file mode 100644 index 0000000..1a94a87 --- /dev/null +++ b/test/pt/up/fe/comp/cpf/4_jasmin/basic/BasicMethodsArray.ollir @@ -0,0 +1,13 @@ +import Other; +BasicMethods extends Other { + + .construct BasicMethods().V { + invokespecial(this, "").V; + } + + .method public func4().array.i32 { + temp0.i32 :=.i32 1.i32; + k.array.i32 :=.array.i32 new(array, temp0.i32).array.i32; + ret.array.i32 k.array.i32; + } +} \ No newline at end of file diff --git a/test/pt/up/fe/comp/cpf/4_jasmin/calls/ConditionArgsFuncCall.jmm b/test/pt/up/fe/comp/cpf/4_jasmin/calls/ConditionArgsFuncCall.jmm new file mode 100644 index 0000000..4e7e3c2 --- /dev/null +++ b/test/pt/up/fe/comp/cpf/4_jasmin/calls/ConditionArgsFuncCall.jmm @@ -0,0 +1,22 @@ +import ioPlus; +class ConditionArgsFuncCall { + + public int func(boolean a, boolean b, boolean c, boolean d){ + ioPlus.printResult(10); + return 1; + } + + public static void main(String[] args) { + int a; + int b; + boolean i; + boolean j; + ConditionArgsFuncCall d; + d = new ConditionArgsFuncCall(); + a = 10; + b = 5; + i = true; + j = false; + a = d.func(a < b, i && j , a < b && i, !i); + } +} \ No newline at end of file diff --git a/test/pt/up/fe/comp/cpf/4_jasmin/calls/ConditionArgsFuncCall.ollir b/test/pt/up/fe/comp/cpf/4_jasmin/calls/ConditionArgsFuncCall.ollir new file mode 100644 index 0000000..fd07958 --- /dev/null +++ b/test/pt/up/fe/comp/cpf/4_jasmin/calls/ConditionArgsFuncCall.ollir @@ -0,0 +1,29 @@ +import ioPlus; +ConditionArgsFuncCall { + + .construct ConditionArgsFuncCall().V { + invokespecial(this, "").V; + } + + .method public func(a.bool, b.bool, c.bool, d.bool).i32 { +invokestatic(ioPlus, "printResult", 10.i32).V; +ret.i32 1.i32; + } + .method public static main(args.array.String).V { +d.ConditionArgsFuncCall :=.ConditionArgsFuncCall new(ConditionArgsFuncCall).ConditionArgsFuncCall; +invokespecial(d.ConditionArgsFuncCall,"").V; +a.i32 :=.i32 10.i32; +b.i32 :=.i32 5.i32; +i.bool :=.bool 1.bool; +j.bool :=.bool 0.bool; +temp0.bool :=.bool a.i32 <.bool b.i32; +temp1.bool :=.bool i.bool &&.bool j.bool; +temp4.bool :=.bool a.i32 <.bool b.i32; +temp3.bool :=.bool temp4.bool &&.bool i.bool; +temp5.bool :=.bool !.bool i.bool; +a.i32 :=.i32 invokevirtual(d.ConditionArgsFuncCall, "func", temp0.bool, temp1.bool, temp3.bool, temp5.bool).i32; + +ret.V; + } + +} \ No newline at end of file diff --git a/test/pt/up/fe/comp/cpf/4_jasmin/control_flow/IfWhileNested.jmm b/test/pt/up/fe/comp/cpf/4_jasmin/control_flow/IfWhileNested.jmm new file mode 100644 index 0000000..2b14458 --- /dev/null +++ b/test/pt/up/fe/comp/cpf/4_jasmin/control_flow/IfWhileNested.jmm @@ -0,0 +1,32 @@ +import ioPlus; + +class IfWhileNested { + + + public int func(int a){ + int i; + boolean flag; + + flag = true; + i = 0; + while (i < a) { + if(flag){ + ioPlus.printResult(1); + } else { + ioPlus.printResult(2); + } + + flag = !flag; + i = i + 1; + } + + return 1; + } + + public static void main(String[] args) { + IfWhileNested d; + int a; + d = new IfWhileNested(); + a = d.func(3); + } +} \ No newline at end of file diff --git a/test/pt/up/fe/comp/cpf/4_jasmin/control_flow/IfWhileNested.ollir b/test/pt/up/fe/comp/cpf/4_jasmin/control_flow/IfWhileNested.ollir new file mode 100644 index 0000000..52ddf93 --- /dev/null +++ b/test/pt/up/fe/comp/cpf/4_jasmin/control_flow/IfWhileNested.ollir @@ -0,0 +1,35 @@ +import ioPlus; +IfWhileNested { + + .construct IfWhileNested().V { + invokespecial(this, "").V; + } + + .method public func(a.i32).i32 { + flag.bool :=.bool 1.bool; + i.i32 :=.i32 0.i32; + if (i.i32 <.bool a.i32) goto whilebody_1; + goto endwhile_1; + whilebody_1: + + if (flag.bool) goto ifbody_0; + invokestatic(ioPlus, "printResult", 2.i32).V; + goto endif_0; + ifbody_0: + invokestatic(ioPlus, "printResult", 1.i32).V; + endif_0: + flag.bool :=.bool !.bool flag.bool; + i.i32 :=.i32 i.i32 +.i32 1.i32; + if (i.i32 <.bool a.i32) goto whilebody_1; + endwhile_1: + ret.i32 1.i32; + } + + .method public static main(args.array.String).V { + d.IfWhileNested :=.IfWhileNested new(IfWhileNested).IfWhileNested; + invokespecial(d.IfWhileNested,"").V; + a.i32 :=.i32 invokevirtual(d.IfWhileNested, "func", 3.i32).i32; + + ret.V; + } +} \ No newline at end of file diff --git a/test/pt/up/fe/comp/cpf/4_jasmin/control_flow/SimpleControlFlow.jmm b/test/pt/up/fe/comp/cpf/4_jasmin/control_flow/SimpleControlFlow.jmm new file mode 100644 index 0000000..d7d96c9 --- /dev/null +++ b/test/pt/up/fe/comp/cpf/4_jasmin/control_flow/SimpleControlFlow.jmm @@ -0,0 +1,19 @@ +import ioPlus; +class SimpleControlFlow { + + + public static void main(String[] args) { + int a; + int b; + + a = 2; + b = 3; + + if(b < a){ + ioPlus.printResult(a); + } else { + ioPlus.printResult(b); + } + + } +} \ No newline at end of file diff --git a/test/pt/up/fe/comp/cpf/4_jasmin/control_flow/SimpleControlFlow.ollir b/test/pt/up/fe/comp/cpf/4_jasmin/control_flow/SimpleControlFlow.ollir new file mode 100644 index 0000000..15de9fb --- /dev/null +++ b/test/pt/up/fe/comp/cpf/4_jasmin/control_flow/SimpleControlFlow.ollir @@ -0,0 +1,20 @@ +import ioPlus; +SimpleControlFlow { + .construct SimpleControlFlow().V { + invokespecial(this, "").V; + } + + .method public static main(args.array.String).V { + + a.i32 :=.i32 2.i32; + b.i32 :=.i32 3.i32; + if (b.i32 >=.bool a.i32) goto ELSE_0; + invokestatic(ioPlus, "printResult", a.i32).V; + goto ENDIF_1; + ELSE_0: + invokestatic(ioPlus, "printResult", b.i32).V; + ENDIF_1: + ret.V; + } + +} \ No newline at end of file diff --git a/test/pt/up/fe/comp/cpf/4_jasmin/control_flow/SimpleIfElseArithmetic_and.ollir b/test/pt/up/fe/comp/cpf/4_jasmin/control_flow/SimpleIfElseArithmetic_and.ollir new file mode 100644 index 0000000..fb826c3 --- /dev/null +++ b/test/pt/up/fe/comp/cpf/4_jasmin/control_flow/SimpleIfElseArithmetic_and.ollir @@ -0,0 +1,29 @@ +import io; +Arithmetic_and { + + .construct Arithmetic_and().V { + invokespecial(this, "").V; + } + + .method public static main(args.array.String).V { + a.bool :=.bool 1.bool &&.bool 0.bool; + var1.i32 :=.i32 1.i32; + var2.i32 :=.i32 2.i32; + + if (a.bool) goto ifbody_0; + invokestatic(io, "print", 0.i32).V; + goto endif_0; + ifbody_0: + invokestatic(io, "print", 1.i32).V; + endif_0: + + if (var1.i32 <.bool var2.i32) goto ifbody_1; + invokestatic(io, "print", 0.i32).V; + goto endif_1; + ifbody_1: + invokestatic(io, "print", 1.i32).V; + endif_1: + + ret.V; + } +} \ No newline at end of file diff --git a/test/pt/up/fe/comp/cpf/4_jasmin/control_flow/SimpleIfElseNot.jmm b/test/pt/up/fe/comp/cpf/4_jasmin/control_flow/SimpleIfElseNot.jmm new file mode 100644 index 0000000..40b64a3 --- /dev/null +++ b/test/pt/up/fe/comp/cpf/4_jasmin/control_flow/SimpleIfElseNot.jmm @@ -0,0 +1,19 @@ +import io; + +class SimpleIfElseNot { + + + public static void main(String[] args) { + + if(true) + io.println(10); + else + io.println(20); + + if(false) + io.print(100); + else + io.print(200); + + } +} \ No newline at end of file diff --git a/test/pt/up/fe/comp/cpf/4_jasmin/control_flow/SimpleIfElseNot.ollir b/test/pt/up/fe/comp/cpf/4_jasmin/control_flow/SimpleIfElseNot.ollir new file mode 100644 index 0000000..54cf5c4 --- /dev/null +++ b/test/pt/up/fe/comp/cpf/4_jasmin/control_flow/SimpleIfElseNot.ollir @@ -0,0 +1,25 @@ +import io; +SimpleIfElseNot { + + .construct SimpleIfElseNot().V { + invokespecial(this, "").V; + } + + .method public static main(args.array.String).V { + if (1.bool) goto ifbody_0; + invokestatic(io, "println", 20.i32).V; + goto endif_0; + ifbody_0: + invokestatic(io, "println", 10.i32).V; + endif_0: + + if (0.bool) goto ifbody_1; + invokestatic(io, "print", 200.i32).V; + goto endif_1; + ifbody_1: + invokestatic(io, "print", 100.i32).V; + endif_1: + + ret.V; + } +} \ No newline at end of file diff --git a/test/pt/up/fe/comp/cpf/4_jasmin/control_flow/SimpleIfElseStat.jmm b/test/pt/up/fe/comp/cpf/4_jasmin/control_flow/SimpleIfElseStat.jmm new file mode 100644 index 0000000..43ec9af --- /dev/null +++ b/test/pt/up/fe/comp/cpf/4_jasmin/control_flow/SimpleIfElseStat.jmm @@ -0,0 +1,28 @@ +import ioPlus; +class SimpleIfElseStat { + + + public static void main(String[] args) { + int a; + int b; + + a = 5; + b = 10; + + if(a < b){ + ioPlus.printResult(a); + } else { + ioPlus.printResult(b); + } + + a = 10; + b = 8; + + if(a < b){ + ioPlus.printResult(a); + } else { + ioPlus.printResult(b); + } + + } +} \ No newline at end of file diff --git a/test/pt/up/fe/comp/cpf/4_jasmin/control_flow/SimpleIfElseStat.ollir b/test/pt/up/fe/comp/cpf/4_jasmin/control_flow/SimpleIfElseStat.ollir new file mode 100644 index 0000000..eeea6ab --- /dev/null +++ b/test/pt/up/fe/comp/cpf/4_jasmin/control_flow/SimpleIfElseStat.ollir @@ -0,0 +1,30 @@ +import ioPlus; +SimpleIfElseStat { + + .construct SimpleIfElseStat().V { + invokespecial(this, "").V; + } + + .method public static main(args.array.String).V { + a.i32 :=.i32 5.i32; + b.i32 :=.i32 10.i32; + + if (a.i32 <.bool b.i32) goto ifbody_0; + invokestatic(ioPlus, "printResult", b.i32).V; + goto endif_0; + ifbody_0: + invokestatic(ioPlus, "printResult", a.i32).V; + endif_0: + a.i32 :=.i32 10.i32; + b.i32 :=.i32 8.i32; + + if (a.i32 <.bool b.i32) goto ifbody_1; + invokestatic(ioPlus, "printResult", b.i32).V; + goto endif_1; + ifbody_1: + invokestatic(ioPlus, "printResult", a.i32).V; + endif_1: + + ret.V; + } +} \ No newline at end of file diff --git a/test/pt/up/fe/comp/cpf/4_jasmin/control_flow/SimpleWhileStat.jmm b/test/pt/up/fe/comp/cpf/4_jasmin/control_flow/SimpleWhileStat.jmm new file mode 100644 index 0000000..9e5fc0f --- /dev/null +++ b/test/pt/up/fe/comp/cpf/4_jasmin/control_flow/SimpleWhileStat.jmm @@ -0,0 +1,18 @@ +import ioPlus; +class SimpleWhileStat { + + + public static void main(String[] args) { + int a; + int i; + + a = 3; + i = 0; + + while (i < a) { + ioPlus.printResult(i); + i = i + 1; + } + + } +} \ No newline at end of file diff --git a/test/pt/up/fe/comp/cpf/4_jasmin/control_flow/SimpleWhileStat.ollir b/test/pt/up/fe/comp/cpf/4_jasmin/control_flow/SimpleWhileStat.ollir new file mode 100644 index 0000000..dabac04 --- /dev/null +++ b/test/pt/up/fe/comp/cpf/4_jasmin/control_flow/SimpleWhileStat.ollir @@ -0,0 +1,21 @@ +import ioPlus; +SimpleWhileStat { + + .construct SimpleWhileStat().V { + invokespecial(this, "").V; + } + + .method public static main(args.array.String).V { + a.i32 :=.i32 3.i32; + i.i32 :=.i32 0.i32; + if (i.i32 <.bool a.i32) goto whilebody_0; + goto endwhile_0; + whilebody_0: + invokestatic(ioPlus, "printResult", i.i32).V; + i.i32 :=.i32 i.i32 +.i32 1.i32; + if (i.i32 <.bool a.i32) goto whilebody_0; + endwhile_0: + + ret.V; + } +} \ No newline at end of file diff --git a/test/pt/up/fe/comp/cpf/4_jasmin/control_flow/SwitchStat.jmm b/test/pt/up/fe/comp/cpf/4_jasmin/control_flow/SwitchStat.jmm new file mode 100644 index 0000000..fb7194a --- /dev/null +++ b/test/pt/up/fe/comp/cpf/4_jasmin/control_flow/SwitchStat.jmm @@ -0,0 +1,46 @@ +import ioPlus; +class SwitchStat { + + + public int func(int a){ + if(a < 1){ + ioPlus.printResult(1); + } else { + if(a < 2){ + ioPlus.printResult(2); + } else { + if(a < 3){ + ioPlus.printResult(3); + } else { + if(a < 4){ + ioPlus.printResult(4); + } else { + if(a < 5){ + ioPlus.printResult(5); + } else { + if(a < 6){ + ioPlus.printResult(6); + } else { + ioPlus.printResult(7); + } + } + } + } + } + } + return 1; + } + + public static void main(String[] args) { + SwitchStat d; + int a; + d = new SwitchStat(); + a = d.func(0); + a = d.func(1); + a= d.func(2); + a= d.func(3); + a= d.func(4); + a= d.func(5); + a= d.func(6); + } +} \ No newline at end of file diff --git a/test/pt/up/fe/comp/cpf/4_jasmin/control_flow/SwitchStat.ollir b/test/pt/up/fe/comp/cpf/4_jasmin/control_flow/SwitchStat.ollir new file mode 100644 index 0000000..b8344f9 --- /dev/null +++ b/test/pt/up/fe/comp/cpf/4_jasmin/control_flow/SwitchStat.ollir @@ -0,0 +1,61 @@ +import ioPlus; +SwitchStat { + + .construct SwitchStat().V { + invokespecial(this, "").V; + } + + .method public func(a.i32).i32 { + if (a.i32 <.bool 1.i32) goto ifbody_5; + + if (a.i32 <.bool 2.i32) goto ifbody_4; + + if (a.i32 <.bool 3.i32) goto ifbody_3; + + if (a.i32 <.bool 4.i32) goto ifbody_2; + + if (a.i32 <.bool 5.i32) goto ifbody_1; + + if (a.i32 <.bool 6.i32) goto ifbody_0; + invokestatic(ioPlus, "printResult", 7.i32).V; + goto endif_0; + ifbody_0: + invokestatic(ioPlus, "printResult", 6.i32).V; + endif_0: + goto endif_1; + ifbody_1: + invokestatic(ioPlus, "printResult", 5.i32).V; + endif_1: + goto endif_2; + ifbody_2: + invokestatic(ioPlus, "printResult", 4.i32).V; + endif_2: + goto endif_3; + ifbody_3: + invokestatic(ioPlus, "printResult", 3.i32).V; + endif_3: + goto endif_4; + ifbody_4: + invokestatic(ioPlus, "printResult", 2.i32).V; + endif_4: + goto endif_5; + ifbody_5: + invokestatic(ioPlus, "printResult", 1.i32).V; + endif_5: + ret.i32 1.i32; + } + + .method public static main(args.array.String).V { + d.SwitchStat :=.SwitchStat new(SwitchStat).SwitchStat; + invokespecial(d.SwitchStat,"").V; + a.i32 :=.i32 invokevirtual(d.SwitchStat, "func", 0.i32).i32; + a.i32 :=.i32 invokevirtual(d.SwitchStat, "func", 1.i32).i32; + a.i32 :=.i32 invokevirtual(d.SwitchStat, "func", 2.i32).i32; + a.i32 :=.i32 invokevirtual(d.SwitchStat, "func", 3.i32).i32; + a.i32 :=.i32 invokevirtual(d.SwitchStat, "func", 4.i32).i32; + a.i32 :=.i32 invokevirtual(d.SwitchStat, "func", 5.i32).i32; + a.i32 :=.i32 invokevirtual(d.SwitchStat, "func", 6.i32).i32; + + ret.V; + } +} \ No newline at end of file diff --git a/test/pt/up/fe/comp/cpf/4_jasmin/limits/LocalLimits.jmm b/test/pt/up/fe/comp/cpf/4_jasmin/limits/LocalLimits.jmm new file mode 100644 index 0000000..dab4682 --- /dev/null +++ b/test/pt/up/fe/comp/cpf/4_jasmin/limits/LocalLimits.jmm @@ -0,0 +1,10 @@ +class LocalLimits{ + + public int func(int a, int b){ + int j; + + a = b + 10 * (3 + this.func(3,4)); + j = this.func(3,4); + return 1; + } +} \ No newline at end of file diff --git a/test/pt/up/fe/comp/cpf/4_jasmin/limits/LocalLimits.ollir b/test/pt/up/fe/comp/cpf/4_jasmin/limits/LocalLimits.ollir new file mode 100644 index 0000000..f5bd655 --- /dev/null +++ b/test/pt/up/fe/comp/cpf/4_jasmin/limits/LocalLimits.ollir @@ -0,0 +1,16 @@ +LocalLimits { + + .construct LocalLimits().V { + invokespecial(this, "").V; + } + + .method public func(a.i32, b.i32).i32 { +temp6.i32 :=.i32 invokevirtual(this, "func", 3.i32, 4.i32).i32.i32.i32; +temp5.i32 :=.i32 3.i32 +.i32 temp6.i32; +temp3.i32 :=.i32 10.i32 *.i32 temp5.i32; +a.i32 :=.i32 b.i32 +.i32 temp3.i32; +j.i32 :=.i32 invokevirtual(this, "func", 3.i32, 4.i32).i32; +ret.i32 1.i32; + } + +} \ No newline at end of file diff --git a/test/pt/up/fe/comp/cpf/5_optimizations/const_prop/PropSimple.jmm b/test/pt/up/fe/comp/cpf/5_optimizations/const_prop/PropSimple.jmm new file mode 100644 index 0000000..c5edb0b --- /dev/null +++ b/test/pt/up/fe/comp/cpf/5_optimizations/const_prop/PropSimple.jmm @@ -0,0 +1,10 @@ +class PropSimple { + public int foo(){ + int a; + a = 10; + return a; + } + public static void main(String[] args) { + + } +} diff --git a/test/pt/up/fe/comp/cpf/5_optimizations/const_prop/PropWithLoop.jmm b/test/pt/up/fe/comp/cpf/5_optimizations/const_prop/PropWithLoop.jmm new file mode 100644 index 0000000..e7e2382 --- /dev/null +++ b/test/pt/up/fe/comp/cpf/5_optimizations/const_prop/PropWithLoop.jmm @@ -0,0 +1,16 @@ +class PropWithLoop { + public int foo(){ + int a; + int i; + int res; + a = 10; + i = 0; + while (i < a){ + i=i+1; + } + res = i*a; + return res; + } + public static void main(String[] args) { + } +} diff --git a/test/pt/up/fe/comp/cpf/5_optimizations/inst_selection/InstSelection_bipush_6.jmm b/test/pt/up/fe/comp/cpf/5_optimizations/inst_selection/InstSelection_bipush_6.jmm new file mode 100644 index 0000000..47bceb6 --- /dev/null +++ b/test/pt/up/fe/comp/cpf/5_optimizations/inst_selection/InstSelection_bipush_6.jmm @@ -0,0 +1,7 @@ +class InstSelection_bipush_6 { + public int foo() { + int a; + a = 6; + return a; + } +} diff --git a/test/pt/up/fe/comp/cpf/5_optimizations/inst_selection/InstSelection_iconst_0.jmm b/test/pt/up/fe/comp/cpf/5_optimizations/inst_selection/InstSelection_iconst_0.jmm new file mode 100644 index 0000000..ef594de --- /dev/null +++ b/test/pt/up/fe/comp/cpf/5_optimizations/inst_selection/InstSelection_iconst_0.jmm @@ -0,0 +1,7 @@ +class InstSelection_iconst_0 { + public int foo() { + int a; + a = 0; + return a; + } +} diff --git a/test/pt/up/fe/comp/cpf/5_optimizations/inst_selection/InstSelection_if_lt.jmm b/test/pt/up/fe/comp/cpf/5_optimizations/inst_selection/InstSelection_if_lt.jmm new file mode 100644 index 0000000..e8e6e12 --- /dev/null +++ b/test/pt/up/fe/comp/cpf/5_optimizations/inst_selection/InstSelection_if_lt.jmm @@ -0,0 +1,12 @@ +class InstSelection_if_lt { + public static void main(String[] args) { + int a; + a = 0; + + if(a < 0) { + a = 1; + } else { + a = 2; + } + } +} diff --git a/test/pt/up/fe/comp/cpf/5_optimizations/inst_selection/InstSelection_iinc.jmm b/test/pt/up/fe/comp/cpf/5_optimizations/inst_selection/InstSelection_iinc.jmm new file mode 100644 index 0000000..8baccf1 --- /dev/null +++ b/test/pt/up/fe/comp/cpf/5_optimizations/inst_selection/InstSelection_iinc.jmm @@ -0,0 +1,7 @@ +class InstSelection_iinc { + public static void main(String[] args) { + int i; + i = 2; + i = i+1; + } +} diff --git a/test/pt/up/fe/comp/cpf/5_optimizations/inst_selection/InstSelection_iinc.ollir b/test/pt/up/fe/comp/cpf/5_optimizations/inst_selection/InstSelection_iinc.ollir new file mode 100644 index 0000000..750e2d5 --- /dev/null +++ b/test/pt/up/fe/comp/cpf/5_optimizations/inst_selection/InstSelection_iinc.ollir @@ -0,0 +1,11 @@ +InstSelection_iinc { + .construct InstSelection_iinc().V { + invokespecial(this, "").V; + } + + .method public static main(args.array.String).V{ + i.i32 :=.i32 2.i32; + i.i32:=.i32 i.i32 +.i32 1.i32; + ret.V; + } +} \ No newline at end of file diff --git a/test/pt/up/fe/comp/cpf/5_optimizations/inst_selection/InstSelection_ldc_32768.jmm b/test/pt/up/fe/comp/cpf/5_optimizations/inst_selection/InstSelection_ldc_32768.jmm new file mode 100644 index 0000000..400bcfc --- /dev/null +++ b/test/pt/up/fe/comp/cpf/5_optimizations/inst_selection/InstSelection_ldc_32768.jmm @@ -0,0 +1,7 @@ +class InstSelection_ldc_32768 { + public int foo() { + int a; + a = 32768; + return a; + } +} diff --git a/test/pt/up/fe/comp/cpf/5_optimizations/inst_selection/InstSelection_load_1.jmm b/test/pt/up/fe/comp/cpf/5_optimizations/inst_selection/InstSelection_load_1.jmm new file mode 100644 index 0000000..8bc276c --- /dev/null +++ b/test/pt/up/fe/comp/cpf/5_optimizations/inst_selection/InstSelection_load_1.jmm @@ -0,0 +1,5 @@ +class InstSelection_load_1 { + public int foo(int a) { + return a; + } +} diff --git a/test/pt/up/fe/comp/cpf/5_optimizations/inst_selection/InstSelection_sipush_32767.jmm b/test/pt/up/fe/comp/cpf/5_optimizations/inst_selection/InstSelection_sipush_32767.jmm new file mode 100644 index 0000000..66849f4 --- /dev/null +++ b/test/pt/up/fe/comp/cpf/5_optimizations/inst_selection/InstSelection_sipush_32767.jmm @@ -0,0 +1,7 @@ +class InstSelection_sipush_32767 { + public int foo() { + int a; + a = 32767; + return a; + } +} \ No newline at end of file diff --git a/test/pt/up/fe/comp/cpf/5_optimizations/inst_selection/InstSelection_store_1.jmm b/test/pt/up/fe/comp/cpf/5_optimizations/inst_selection/InstSelection_store_1.jmm new file mode 100644 index 0000000..8a93d17 --- /dev/null +++ b/test/pt/up/fe/comp/cpf/5_optimizations/inst_selection/InstSelection_store_1.jmm @@ -0,0 +1,6 @@ +class InstSelection_store_1 { + public int foo(int a) { + a = 2; + return 0; + } +} diff --git a/test/pt/up/fe/comp/cpf/5_optimizations/reg_alloc/regalloc.jmm b/test/pt/up/fe/comp/cpf/5_optimizations/reg_alloc/regalloc.jmm new file mode 100644 index 0000000..c21050c --- /dev/null +++ b/test/pt/up/fe/comp/cpf/5_optimizations/reg_alloc/regalloc.jmm @@ -0,0 +1,16 @@ +class RegAlloc { + public int soManyRegisters(int arg){ + int a; + int b; + int c; + int d; + a = 0; + b = a; + c = b; + d = c; + return d; + } + + public static void main(String[] args) { + } +} \ No newline at end of file diff --git a/test/pt/up/fe/comp/cpf/Cpf1_ParserAndTree.java b/test/pt/up/fe/comp/cpf/Cpf1_ParserAndTree.java new file mode 100644 index 0000000..ebd1728 --- /dev/null +++ b/test/pt/up/fe/comp/cpf/Cpf1_ParserAndTree.java @@ -0,0 +1,37 @@ +/** + * Copyright 2022 SPeCS. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. under the License. + */ + +package pt.up.fe.comp.cpf; + +import org.junit.Test; +import pt.up.fe.comp.CpUtils; +import pt.up.fe.comp.TestUtils; +import pt.up.fe.comp.jmm.jasmin.JasminResult; +import pt.up.fe.specs.util.SpecsIo; +import pt.up.fe.specs.util.SpecsStrings; + +public class Cpf1_ParserAndTree { + + static JasminResult getJmmResult(String filename) { + return TestUtils.backend(SpecsIo.getResource("pt/up/fe/comp/cpf/1_parser_and_tree/" + filename)); + } + + @Test + public void section1_OpPrecedence_1_AddMultConstants() { + var result = getJmmResult("AddMultConstants.jmm"); + TestUtils.noErrors(result.getReports()); + CpUtils.assertEquals("Wrong results", "7\n12\n9", SpecsStrings.normalizeFileContents(result.run(), true), + result); + } + +} diff --git a/test/pt/up/fe/comp/cpf/Cpf2_SemanticAnalysis.java b/test/pt/up/fe/comp/cpf/Cpf2_SemanticAnalysis.java new file mode 100644 index 0000000..b008444 --- /dev/null +++ b/test/pt/up/fe/comp/cpf/Cpf2_SemanticAnalysis.java @@ -0,0 +1,127 @@ +/** + * Copyright 2022 SPeCS. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. under the License. + */ + +package pt.up.fe.comp.cpf; + +import org.junit.Test; +import pt.up.fe.comp.CpUtils; +import pt.up.fe.comp.TestUtils; +import pt.up.fe.comp.jmm.analysis.JmmSemanticsResult; +import pt.up.fe.comp.jmm.jasmin.JasminResult; +import pt.up.fe.specs.util.SpecsIo; + +import static org.junit.Assert.assertEquals; + +public class Cpf2_SemanticAnalysis { + + static JasminResult getJasminResult(String filename) { + return TestUtils.backend(SpecsIo.getResource("pt/up/fe/comp/cpf/2_semantic_analysis/" + filename)); + } + + static JmmSemanticsResult getSemanticsResult(String filename) { + return TestUtils.analyse(SpecsIo.getResource("pt/up/fe/comp/cpf/2_semantic_analysis/" + filename)); + } + + static JmmSemanticsResult test(String filename, boolean fail) { + var semantics = getSemanticsResult(filename); + if (fail) { + TestUtils.mustFail(semantics.getReports()); + } else { + TestUtils.noErrors(semantics.getReports()); + } + return semantics; + } + + @Test + public void section1_SymbolTable_Fields() { + var semantics = test("symboltable/MethodsAndFields.jmm", false); + var st = semantics.getSymbolTable(); + var fields = st.getFields(); + assertEquals(3, fields.size()); + var checkInt = 0; + var checkBool = 0; + var checkObj = 0; + System.out.println("FIELDS: " + fields); + for (var f : fields) { + switch (f.getType().getName()) { + case "MethodsAndFields": + checkObj++; + break; + case "boolean": + checkBool++; + break; + case "int": + checkInt++; + break; + } + } + ; + CpUtils.assertEquals("Field of type int", 1, checkInt, st); + CpUtils.assertEquals("Field of type boolean", 1, checkBool, st); + CpUtils.assertEquals("Field of type object", 1, checkObj, st); + + } + + @Test + public void section1_SymbolTable_Parameters() { + var semantics = test("symboltable/Parameters.jmm", false); + var st = semantics.getSymbolTable(); + var methods = st.getMethods(); + CpUtils.assertEquals("Number of methods", 1, methods.size(), st); + + var parameters = st.getParameters(methods.get(0)); + CpUtils.assertEquals("Number of parameters", 3, parameters.size(), st); + CpUtils.assertEquals("Parameter 1", "int", parameters.get(0).getType().getName(), st); + CpUtils.assertEquals("Parameter 2", "boolean", parameters.get(1).getType().getName(), st); + CpUtils.assertEquals("Parameter 3", "Parameters", parameters.get(2).getType().getName(), st); + } + + /** + * Test if fields are not being accessed from static methods. + */ + @Test + public void section2_Lookup_SuperWithImport() { + var semantics = test("import/ImportSuper.jmm", false); + CpUtils.assertEquals("Super", "Sup", semantics.getSymbolTable().getSuper(), semantics.getSymbolTable()); + } + + /** + * Test if fields are not being accessed from static methods. + */ + @Test + public void section2_Lookup_VarLookup_Field_Main_Fail() { + test("lookup/VarLookup_Field_Main_Fail.jmm", true); + } + + /** + * Test if the code can correctly lookup a local variable, even if there is a field with the same name. + */ + @Test + public void section2_Lookup_VarLookup_Local() { + var jasminResult = getJasminResult("lookup/VarLookup_Local.jmm"); + + assertEquals("10", jasminResult.run().trim()); + } + + /** + * Test if the code can correctly lookup a field. + */ + @Test + public void section2_Lookup_VarLookup_Field() { + var jasminResult = getJasminResult("lookup/VarLookup_Field.jmm"); + + CpUtils.assertEquals("Lookup of field", "10", jasminResult.run().trim(), jasminResult); + } + + +} diff --git a/test/pt/up/fe/comp/cpf/Cpf3_Ollir.java b/test/pt/up/fe/comp/cpf/Cpf3_Ollir.java new file mode 100644 index 0000000..d9f4951 --- /dev/null +++ b/test/pt/up/fe/comp/cpf/Cpf3_Ollir.java @@ -0,0 +1,162 @@ +/** + * Copyright 2022 SPeCS. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. under the License. + */ + +package pt.up.fe.comp.cpf; + +import org.junit.Test; +import org.specs.comp.ollir.*; +import pt.up.fe.comp.CpUtils; +import pt.up.fe.comp.TestUtils; +import pt.up.fe.comp.jmm.ollir.OllirResult; +import pt.up.fe.specs.util.SpecsIo; + +import java.util.stream.Collectors; + +public class Cpf3_Ollir { + + static OllirResult getOllirResult(String filename) { + return TestUtils.optimize(SpecsIo.getResource("pt/up/fe/comp/cpf/3_ollir/" + filename)); + } + + + /*checks if method declaration is correct (array)*/ + @Test + public void section1_Basic_Method_Declaration_Array() { + var result = getOllirResult("basic/BasicMethodsArray.jmm"); + + var method = CpUtils.getMethod(result, "func4"); + + CpUtils.assertEquals("Method return type", "int[]", CpUtils.toString(method.getReturnType()), result); + } + + + @Test + public void section2_Arithmetic_Simple_and() { + var ollirResult = getOllirResult("arithmetic/Arithmetic_and.jmm"); + + var method = CpUtils.getMethod(ollirResult, "main"); + + CpUtils.assertHasOperation(OperationType.ANDB, method, ollirResult); + } + + @Test + public void section2_Arithmetic_Simple_less() { + var ollirResult = getOllirResult("arithmetic/Arithmetic_less.jmm"); + + var method = CpUtils.getMethod(ollirResult, "main"); + + CpUtils.assertHasOperation(OperationType.LTH, method, ollirResult); + + } + + @Test + public void section3_ControlFlow_If_Simple_Single_goto() { + + var result = getOllirResult("control_flow/SimpleIfElseStat.jmm"); + + var method = CpUtils.getMethod(result, "func"); + + var branches = CpUtils.assertInstExists(CondBranchInstruction.class, method, result); + CpUtils.assertEquals("Number of branches", 1, branches.size(), result); + + var gotos = CpUtils.assertInstExists(GotoInstruction.class, method, result); + CpUtils.assertTrue("Has at least 1 goto", gotos.size() >= 1, result); + } + + @Test + public void section3_ControlFlow_If_Switch() { + + var result = getOllirResult("control_flow/SwitchStat.jmm"); + + var method = CpUtils.getMethod(result, "func"); + + var branches = CpUtils.assertInstExists(CondBranchInstruction.class, method, result); + CpUtils.assertEquals("Number of branches", 6, branches.size(), result); + + var gotos = CpUtils.assertInstExists(GotoInstruction.class, method, result); + CpUtils.assertTrue("Has at least 6 gotos", gotos.size() >= 6, result); + } + + @Test + public void section3_ControlFlow_While_Simple() { + + var result = getOllirResult("control_flow/SimpleWhileStat.jmm"); + + var method = CpUtils.getMethod(result, "func"); + + var branches = CpUtils.assertInstExists(CondBranchInstruction.class, method, result); + + CpUtils.assertTrue("Number of branches between 1 and 2", branches.size() > 0 && branches.size() < 3, result); + } + + + /*checks if an array is correctly initialized*/ + @Test + public void section4_Arrays_Init_Array() { + var result = getOllirResult("arrays/ArrayInit.jmm"); + + var method = CpUtils.getMethod(result, "main"); + + var calls = CpUtils.assertInstExists(CallInstruction.class, method, result); + + CpUtils.assertEquals("Number of calls", 3, calls.size(), result); + + // Get new + var newCalls = calls.stream().filter(call -> call.getInvocationType() == CallType.NEW) + .collect(Collectors.toList()); + + CpUtils.assertEquals("Number of 'new' calls", 1, newCalls.size(), result); + + // Get length + var lengthCalls = calls.stream().filter(call -> call.getInvocationType() == CallType.arraylength) + .collect(Collectors.toList()); + + CpUtils.assertEquals("Number of 'arraylenght' calls", 1, lengthCalls.size(), result); + } + + /*checks if the access to the elements of array is correct*/ + @Test + public void section4_Arrays_Access_Array() { + var result = getOllirResult("arrays/ArrayAccess.jmm"); + + var method = CpUtils.getMethod(result, "foo"); + + var assigns = CpUtils.assertInstExists(AssignInstruction.class, method, result); + var numArrayStores = assigns.stream().filter(assign -> assign.getDest() instanceof ArrayOperand).count(); + CpUtils.assertEquals("Number of array stores", 5, numArrayStores, result); + + var numArrayReads = assigns.stream() + .flatMap(assign -> CpUtils.getElements(assign.getRhs()).stream()) + .filter(element -> element instanceof ArrayOperand).count(); + CpUtils.assertEquals("Number of array reads", 5, numArrayReads, result); + } + + /*checks multiple expressions as indexes to access the elements of an array*/ + @Test + public void section4_Arrays_Load_ComplexArrayAccess() { + // Just parse + var result = getOllirResult("arrays/ComplexArrayAccess.jmm"); + + var method = CpUtils.getMethod(result, "main"); + + var assigns = CpUtils.assertInstExists(AssignInstruction.class, method, result); + var numArrayStores = assigns.stream().filter(assign -> assign.getDest() instanceof ArrayOperand).count(); + CpUtils.assertEquals("Number of array stores", 5, numArrayStores, result); + + var numArrayReads = assigns.stream() + .flatMap(assign -> CpUtils.getElements(assign.getRhs()).stream()) + .filter(element -> element instanceof ArrayOperand).count(); + CpUtils.assertEquals("Number of array reads", 6, numArrayReads, result); + } + +} diff --git a/test/pt/up/fe/comp/cpf/Cpf4_Jasmin.java b/test/pt/up/fe/comp/cpf/Cpf4_Jasmin.java new file mode 100644 index 0000000..4711083 --- /dev/null +++ b/test/pt/up/fe/comp/cpf/Cpf4_Jasmin.java @@ -0,0 +1,345 @@ +/** + * Copyright 2022 SPeCS. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. under the License. + */ + +package pt.up.fe.comp.cpf; + +import org.junit.Test; +import pt.up.fe.comp.CpUtils; +import pt.up.fe.comp.TestUtils; +import pt.up.fe.comp.jmm.jasmin.JasminResult; +import pt.up.fe.comp.jmm.ollir.OllirResult; +import pt.up.fe.comp2023.jasmin.JasminGenerator; +import pt.up.fe.specs.util.SpecsCheck; +import pt.up.fe.specs.util.SpecsIo; +import pt.up.fe.specs.util.SpecsStrings; +import utils.ProjectTestUtils; + +import java.util.Collections; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +public class Cpf4_Jasmin { + + private static final String JASMIN_METHOD_REGEX_PREFIX = "\\.method\\s+((public|private)\\s+)?(\\w+)\\(\\)"; + + /*checks if method declaration is correct (array)*/ + @Test + public void section1_Basic_Method_Declaration_Array() { + JasminResult jasminResult = getJasminResult("basic/BasicMethodsArray.ollir"); + CpUtils.matches(jasminResult, JASMIN_METHOD_REGEX_PREFIX + "\\[I"); + } + + /*checks if the index for loading a argument is correct (should be 1) */ + @Test + public void section2_Arithmetic_BytecodeIndex_IloadArg() { + var jasminResult = getJasminResult("arithmetic/ByteCodeIndexes1.ollir"); + var methodCode = CpUtils.getJasminMethod(jasminResult); + + int iloadIndex = CpUtils.getBytecodeIndex("iload", methodCode); + assertEquals(1, iloadIndex); + } + + /*checks if the index for storing a var is correct (should be > 1) */ + @Test + public void section2_Arithmetic_BytecodeIndex_IstoreVar() { + var jasminResult = getJasminResult("arithmetic/ByteCodeIndexes2.ollir"); + var methodCode = CpUtils.getJasminMethod(jasminResult); + + int istoreIndex = CpUtils.getBytecodeIndex("istore", methodCode); + assertTrue("Expected index to be greater than one, is " + istoreIndex, istoreIndex > 1); + } + + @Test + public void section2_Arithmetic_Simple_and() { + CpUtils.runJasmin(getJasminResult("arithmetic/Arithmetic_and.ollir"), "0"); + } + + @Test + public void section2_Arithmetic_Simple_less() { + CpUtils.runJasmin(getJasminResult("arithmetic/Arithmetic_less.ollir"), "1"); + } + + + /*checks if an addition is correct (more than 2 values)*/ + @Test + public void section3_ControlFlow_If_Simple() { + CpUtils.runJasmin(getJasminResult("control_flow/SimpleIfElseStat.ollir"), "Result: 5\nResult: 8"); + } + + /*checks if an addition is correct (more than 2 values)*/ + @Test + public void section3_ControlFlow_Inverted() { + CpUtils.runJasmin(getJasminResult("control_flow/SimpleControlFlow.ollir"), "Result: 3"); + } + + /*checks OLLIR code that uses >= for an inverted condition */ + @Test + public void section3_ControlFlow_If_Not_Simple() { + CpUtils.runJasmin(getJasminResult("control_flow/SimpleIfElseNot.ollir"), "10\n200"); + } + + /*checks if the code of a simple WHILE statement is well executed */ + @Test + public void section3_ControlFlow_While_Simple() { + CpUtils.runJasmin(getJasminResult("control_flow/SimpleWhileStat.ollir"), "Result: 0\nResult: 1\nResult: 2"); + } + + /*checks if the code of a more complex IF ELSE statement (similar a switch statement) is well executed */ + @Test + public void section3_ControlFlow_Mixed_Switch() { + CpUtils.runJasmin(getJasminResult("control_flow/SwitchStat.ollir"), + "Result: 1\nResult: 2\nResult: 3\nResult: 4\nResult: 5\nResult: 6\nResult: 7"); + } + + /*checks if the code of a more complex IF ELSE statement (similar a switch statement) is well executed */ + @Test + public void section3_ControlFlow_Mixed_Nested() { + CpUtils.runJasmin(getJasminResult("control_flow/IfWhileNested.ollir"), "Result: 1\nResult: 2\nResult: 1"); + } + + /*checks if the code of a call to a function with multiple arguments (using boolean expressions in the call) is + well executed*/ + @Test + public void section4_Calls_Misc_ConditionArgs() { + CpUtils.runJasmin(getJasminResult("calls/ConditionArgsFuncCall.ollir"), "Result: 10"); + } + + + /*checks if an array is correctly initialized*/ + @Test + public void section5_Arrays_Init_Array() { + CpUtils.runJasmin(getJasminResult("arrays/ArrayInit.ollir"), "Result: 5"); + } + + /*checks if the access to the elements of array is correct*/ + @Test + public void section5_Arrays_Store_Array() { + CpUtils.runJasmin(getJasminResult("arrays/ArrayAccess.ollir"), + "Result: 1\nResult: 2\nResult: 3\nResult: 4\nResult: 5"); + } + + /*checks multiple expressions as indexes to access the elements of an array*/ + @Test + public void section5_Arrays_Load_ComplexArrayAccess() { + CpUtils.runJasmin(getJasminResult("arrays/ComplexArrayAccess.ollir"), + "Result: 1\nResult: 2\nResult: 3\nResult: 4\nResult: 5"); + } + + /*checks if array has correct signature ?*/ + @Test + public void section5_Arrays_Signature_ArrayAsArg() { + var jasminResult = getJasminResult("arrays/ArrayAsArgCode.ollir"); + var methodCode = CpUtils.getJasminMethod(jasminResult); + + CpUtils.matches(methodCode, "invokevirtual\\s+ArrayAsArg(/|\\.)(\\w+)\\(\\[I\\)I"); + } + + /*checks if array is being passed correctly as an argument to a function*/ + @Test + public void section5_Arrays_As_Arg_Simple() { + CpUtils.runJasmin(getJasminResult("arrays/ArrayAsArg.ollir"), "Result: 2"); + } + + /*checks if array is being passed correctly as an argument to a function (index of aload > 1)*/ + @Test + public void section5_Arrays_As_Arg_Aload() { + var jasminResult = getJasminResult("arrays/ArrayAsArgCode.ollir"); + var methodCode = CpUtils.getJasminMethod(jasminResult); + + int aloadIndex = CpUtils.getBytecodeIndex("aload", methodCode); + assertTrue("Expected aload index to be greater than 1, is " + aloadIndex + ":\n" + methodCode, aloadIndex > 1); + } + + /*checks if the .limits locals is not a const 99 value */ + @Test + public void section6_Limits_Locals_Not_99() { + var jasminResult = getJasminResult("limits/LocalLimits.ollir"); + var methodCode = CpUtils.getJasminMethod(jasminResult); + var numLocals = Integer.parseInt(SpecsStrings.getRegexGroup(methodCode, CpUtils.getLimitLocalsRegex(), 1)); + assertTrue("limit locals should be less than 99:\n" + methodCode, numLocals >= 0 && numLocals < 99); + + // Make sure the code compiles + jasminResult.compile(); + } + + /*checks if the .limits locals is the expected value (with a tolerance of 2) */ + @Test + public void section6_Limits_Locals_Simple() { + + var jasminResult = getJasminResult("limits/LocalLimits.ollir"); + var methodCode = CpUtils.getJasminMethod(jasminResult); + var numLocals = Integer.parseInt(SpecsStrings.getRegexGroup(methodCode, CpUtils.getLimitLocalsRegex(), 1)); + + // Find store or load with numLocals - 1 + var regex = CpUtils.getLocalsRegex(numLocals); + CpUtils.matches(methodCode, regex); + + // Makes sure the code compiles + jasminResult.compile(); + } + + /*checks if the .limits stack is not a const 99 value */ + @Test + public void section6_Limits_Stack_Not_99() { + var jasminResult = getJasminResult("limits/LocalLimits.ollir"); + var methodCode = CpUtils.getJasminMethod(jasminResult); + var numStack = Integer.parseInt(SpecsStrings.getRegexGroup(methodCode, CpUtils.getLimitStackRegex(), 1)); + assertTrue("limit stack should be less than 99:\n" + methodCode, numStack >= 0 && numStack < 99); + + // Make sure the code compiles + jasminResult.compile(); + } + + /*checks if the .limits stack is the expected value (with a tolerance of 2) */ + @Test + public void section6_Limits_Stack_Simple() { + + var jasminResult = getJasminResult("limits/LocalLimits.ollir"); + var methodCode = CpUtils.getJasminMethod(jasminResult); + var numStack = Integer.parseInt(SpecsStrings.getRegexGroup(methodCode, CpUtils.getLimitStackRegex(), 1)); + + int expectedLimit = 3; + int errorMargin = 2; + int upperLimit = expectedLimit + errorMargin; + + assertTrue( + "limit stack should be = " + expectedLimit + " (accepted if <= " + upperLimit + + "), but is " + numStack + ":\n" + methodCode, + numStack <= upperLimit && numStack >= expectedLimit); + + // Make sure the code compiles + jasminResult.compile(); + } + + @Test + public void ollirToJasminArithmeticAnd() { + testOllirToJasmin("pt/up/fe/comp/cpf/4_jasmin/arithmetic/Arithmetic_and.ollir"); + } + + @Test + public void ollirToJasminArithmeticLess() { + testOllirToJasmin("pt/up/fe/comp/cpf/4_jasmin/arithmetic/Arithmetic_less.ollir"); + } + + @Test + public void ollirToJasminIfElseArithmeticAnd() { + testOllirToJasmin("pt/up/fe/comp/cpf/4_jasmin/control_flow/SimpleIfElseArithmetic_and.ollir"); + } + + @Test + public void ollirToJasminSimpleIfElseNot() { + testOllirToJasmin("pt/up/fe/comp/cpf/4_jasmin/control_flow/SimpleIfElseNot.ollir"); + } + + @Test + public void ollirToJasminSimpleIfElseStat() { + testOllirToJasmin("pt/up/fe/comp/cpf/4_jasmin/control_flow/SimpleIfElseStat.ollir"); + } + + @Test + public void ollirToJasminSwitchStat() { + testOllirToJasmin("pt/up/fe/comp/cpf/4_jasmin/control_flow/SwitchStat.ollir"); + } + + @Test + public void ollirToJasminSimpleWhileStat() { + testOllirToJasmin("pt/up/fe/comp/cpf/4_jasmin/control_flow/SimpleWhileStat.ollir"); + } + + @Test + public void ollirToJasminIfWhileNested() { + testOllirToJasmin("pt/up/fe/comp/cpf/4_jasmin/control_flow/IfWhileNested.ollir"); + } + + @Test + public void OllirToJasminArrayInitSimple() { + testOllirToJasmin("pt/up/fe/comp/cpf/4_jasmin/arrays/ArrayInitSimple.ollir"); + } + + @Test + public void OllirToJasminArrayInit() { + testOllirToJasmin("pt/up/fe/comp/cpf/4_jasmin/arrays/ArrayInit.ollir"); + } + + // private static boolean USE_OLLIR_EXPERIMENTAL = false; + /* + public static void enableOllirInputs() { + USE_OLLIR_EXPERIMENTAL = true; + } + + public static boolean useOllirInputs() { + return USE_OLLIR_EXPERIMENTAL; + } + */ + static JasminResult getJasminResult(String filename) { + /* + if (USE_OLLIR_EXPERIMENTAL) { + filename = SpecsIo.removeExtension(filename) + ".ollir"; + return TestUtils.backend(new OllirResult(SpecsIo.getResource("pt/up/fe/comp/cpf/4_jasmin/" + filename), + Collections.emptyMap())); + } + + return TestUtils.backend(SpecsIo.getResource("pt/up/fe/comp/cpf/4_jasmin/" + filename)); + */ + + var resource = "pt/up/fe/comp/cpf/4_jasmin/" + filename; + SpecsCheck.checkArgument(resource.endsWith(".ollir"), () -> "Expected resource to end with .ollir: " + resource); + + // If AstToJasmin pipeline, change name of the resource and execute other test + if (TestUtils.hasAstToJasminClass()) { + + // Rename resource + var jmmResource = SpecsIo.removeExtension(resource) + ".jmm"; + + // Test Jmm resource + var result = TestUtils.backend(SpecsIo.getResource(jmmResource)); + + return result; + } + + var ollirResult = new OllirResult(SpecsIo.getResource(resource), Collections.emptyMap()); + + var result = TestUtils.backend(ollirResult); + + return result; + + } + + public static void testOllirToJasmin(String resource, String expectedOutput) { + SpecsCheck.checkArgument(resource.endsWith(".ollir"), () -> "Expected resource to end with .ollir: " + resource); + + // If AstToJasmin pipeline, change name of the resource and execute other test + if (TestUtils.hasAstToJasminClass()) { + + // Rename resource + var jmmResource = SpecsIo.removeExtension(resource) + ".jmm"; + + // Test Jmm resource + var result = TestUtils.backend(SpecsIo.getResource(jmmResource)); + ProjectTestUtils.runJasmin(result, expectedOutput); + + return; + } + + OllirResult ollirResult = new OllirResult(SpecsIo.getResource(resource), Collections.emptyMap()); + JasminGenerator jasminGenerator = new JasminGenerator(); + JasminResult jasminResult = jasminGenerator.toJasmin(ollirResult); + System.out.println(jasminResult.getJasminCode()); + TestUtils.runJasmin(jasminResult.getJasminCode()); + } + + public static void testOllirToJasmin(String resource) { + testOllirToJasmin(resource, null); + } +} diff --git a/test/pt/up/fe/comp/cpf/Cpf5_Optimizations.java b/test/pt/up/fe/comp/cpf/Cpf5_Optimizations.java new file mode 100644 index 0000000..1067551 --- /dev/null +++ b/test/pt/up/fe/comp/cpf/Cpf5_Optimizations.java @@ -0,0 +1,196 @@ +/** + * Copyright 2022 SPeCS. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. under the License. + */ + +package pt.up.fe.comp.cpf; + +import org.junit.Test; +import pt.up.fe.comp.CpUtils; +import pt.up.fe.comp.TestUtils; +import pt.up.fe.comp.jmm.jasmin.JasminResult; +import pt.up.fe.comp.jmm.ollir.OllirResult; +import pt.up.fe.specs.util.SpecsIo; +import pt.up.fe.specs.util.SpecsStrings; + +import java.util.HashMap; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class Cpf5_Optimizations { + + static OllirResult getOllirResult(String filename) { + return TestUtils.optimize(SpecsIo.getResource("pt/up/fe/comp/cpf/5_optimizations/" + filename)); + } + + static JasminResult getJasminResult(String filename) { + String resource = SpecsIo.getResource("pt/up/fe/comp/cpf/5_optimizations/" + filename); + return TestUtils.backend(resource); + } + + static JasminResult getJasminResultOpt(String filename) { + Map config = new HashMap<>(); + config.put("optimize", "true"); + return TestUtils.backend(SpecsIo.getResource("pt/up/fe/comp/cpf/5_optimizations/" + filename), config); + } + + static JasminResult getJasminResultReg(String filename, int numReg) { + Map config = new HashMap<>(); + config.put("registerAllocation", String.valueOf(numReg)); + return TestUtils.backend(SpecsIo.getResource("pt/up/fe/comp/cpf/5_optimizations/" + filename), config); + } + + /** + * Test if small integers are loaded with iconst + */ + @Test + public void section1_InstSelection_iconst_0() { + JasminResult jasminResult = getJasminResult("inst_selection/InstSelection_iconst_0.jmm"); + CpUtils.matches(jasminResult, "iconst_0"); + + } + + + /** + * Test if integer 6 is loaded with bipush + */ + @Test + public void section1_InstSelection_bipush_6() { + JasminResult jasminResult = getJasminResult("inst_selection/InstSelection_bipush_6.jmm"); + CpUtils.matches(jasminResult, "bipush\\s6"); + } + + + /** + * Test if integer 32767 is loaded with sipush + */ + @Test + public void section1_InstSelection_sipush_32767() { + JasminResult jasminResult = getJasminResult("inst_selection/InstSelection_sipush_32767.jmm"); + CpUtils.matches(jasminResult, "sipush\\s32767"); + } + + /** + * Test if integer 32768 is loaded with ldc + */ + @Test + public void section1_InstSelection_ldc_32768() { + JasminResult jasminResult = getJasminResult("inst_selection/InstSelection_ldc_32768.jmm"); + CpUtils.matches(jasminResult, "ldc\\s32768"); + } + + @Test + public void section1_InstSelection_IfLt() { + var jasminResult = getJasminResult("inst_selection/InstSelection_if_lt.jmm"); + CpUtils.matches(jasminResult, "(iflt|ifge)"); + } + + /** + * Test if iinc is used when incrementing a variable + */ + @Test + public void section1_InstSelection_iinc() { + JasminResult jasminResult = getJasminResult("inst_selection/InstSelection_iinc.jmm"); + CpUtils.matches(jasminResult, "iinc\\s+\\w+\\s+1"); + + } + + /** + * Test if iload_1 is used. + */ + @Test + public void section1_InstSelection_iload_1() { + JasminResult jasminResult = getJasminResult("inst_selection/InstSelection_load_1.jmm"); + CpUtils.matches(jasminResult, "iload_1"); + } + + + /** + * Test if istore_1 is used. + */ + @Test + public void section1_InstSelection_istore_1() { + JasminResult jasminResult = getJasminResult("inst_selection/InstSelection_store_1.jmm"); + CpUtils.matches(jasminResult, "istore_1"); + + } + + + @Test + public void section2_RegAlloc_AtMostRequestedNumber() { + + String filename = "reg_alloc/regalloc.jmm"; + int expectedNumReg = 3; + + JasminResult original = getJasminResult(filename); + JasminResult optimized = getJasminResultReg(filename, expectedNumReg); + + CpUtils.assertNotEquals("Expected code to change with -r flag\n\nOriginal code:\n" + original.getJasminCode(), + original.getJasminCode(), optimized.getJasminCode(), + optimized); + + String method = CpUtils.getJasminMethod(optimized, "soManyRegisters"); + Pattern pattern = Pattern.compile("\\.limit\\s+locals\\s+(\\d+)\\s+"); + Matcher matcher = pattern.matcher(method); + CpUtils.assertTrue("Expected to find correct .limit locals directive", + matcher.find(), + optimized); + + String captured = matcher.group(1); + CpUtils.assertNotNull("Expected to find correct .limit locals directive", + captured, + optimized); + + Integer actualNumReg = SpecsStrings.decodeInteger(captured); + CpUtils.assertNotNull("Could not convert locals limit to integer", + actualNumReg, + optimized); + + CpUtils.assertTrue("Expected number of locals in 'soManyRegisters' to be <= than " + expectedNumReg, + actualNumReg <= expectedNumReg, + optimized); + } + + + @Test + public void section3_ConstProp_Simple() { + + String filename = "const_prop/PropSimple.jmm"; + + JasminResult original = getJasminResult(filename); + JasminResult optimized = getJasminResultOpt(filename); + + CpUtils.assertNotEquals("Expected code to change with -o flag\n\nOriginal code:\n" + original.getJasminCode(), + original.getJasminCode(), optimized.getJasminCode(), + optimized); + + CpUtils.matches(optimized, "(bipush|sipush|ldc) 10\\s+ireturn"); + } + + + @Test + public void section3_ConstProp_WithLoop() { + + String filename = "const_prop/PropWithLoop.jmm"; + + JasminResult original = getJasminResult(filename); + JasminResult optimized = getJasminResultOpt(filename); + + CpUtils.assertNotEquals("Expected code to change with -o flag\n\nOriginal code:\n" + original.getJasminCode(), + original.getJasminCode(), optimized.getJasminCode(), + optimized); + + CpUtils.matches(optimized, "(bipush|sipush|ldc) 10\\s+imul"); + } + + +}