Commit b42a3f3f authored by 徐世桐's avatar 徐世桐
Browse files

Merge branch 'master' into develop

parents b798cdd8 ca5ed482
......@@ -10,6 +10,7 @@ stages:
- build
- frontend-test
- backend-test
- optimise-test
build-job:
stage: build
......@@ -57,6 +58,17 @@ test-execute-valid:
- echo "Making arm executable for each assembly file and trying to execute them..."
- ./scripts/assembleExecuteTest
- ./scripts/executeOutputTest
artifacts:
paths:
- log/assembly
- log/output
test-optimise:
stage: optimise-test
script:
- echo "compile oprimise sample tests and conpair with given .log result file"
- ./scripts/constantPropagationTest.sh
- ./scripts/extensionValidTest.sh
artifacts:
paths:
- log/assembly
......
......@@ -131,8 +131,7 @@ expr : INT_LITER #IntExpr
| expr bop=( '+' | '-' ) expr #ArithmeticExpr
| expr bop=( '>' | '>=' | '<' | '<=' ) expr #CmpExpr
| expr bop=( '==' | '!=' ) expr #EqExpr
| expr '&' expr #BitwiseAndExpr
| expr '|' expr #BitwiseOrExpr
| expr bitop=( '&' | '|' ) expr #BitwiseExpr
| expr '&&' expr #AndExpr
| expr '||' expr #OrExpr
| OPEN_PARENTHESES expr CLOSE_PARENTHESES #ParenExpr
......
......@@ -39,4 +39,5 @@ then
fi
# compile the given wacc file here
java -cp ./bin:./lib/antlr-4.9.1-complete.jar Compiler $1 $PARSE_ONLY $PRINT_AST $OPTIMISE "--assembly" $EXECUTE
\ No newline at end of file
# default as using -o1 optimise and generate assembly
java -cp ./bin:./lib/antlr-4.9.1-complete.jar Compiler $1 $PARSE_ONLY $PRINT_AST $OPTIMISE "--optimise" "1" "--assembly" $EXECUTE
\ No newline at end of file
......@@ -7,4 +7,6 @@
./scripts/semanticCheckerValid
./scripts/semanticCheckerSemanticErr
./scripts/assembleTest.sh
./scripts/executeTest.sh
\ No newline at end of file
./scripts/executeTest.sh
./scripts/constantPropagation.sh
./scripts/extensionValidTest.sh
\ No newline at end of file
#!/bin/bash
VALID_EXAMPLE_DIR="./src/test/examples/valid/"
VALID_EXAMPLES=(
"/constantPropagation/evaluation/"
"/propagation"
# "/evaluation"
)
VALID_EXAMPLE_LOG="./log/const_eval.log"
> $VALID_EXAMPLE_LOG
VALID_EXAMPLES_SRC_DIR="./src/test/custom/valid/constantPropagation"
ASSEMBLY_OUTPUT_DIR="./log/assembly/constantPropagation"
mkdir log
mkdir $ASSEMBLY_OUTPUT_DIR
TEST_DIR="$VALID_EXAMPLE_DIR$VALID_EXAMPLES"
# counters to represent the total number of test files to be processed
TOTAL_COUNT=$(find "$VALID_EXAMPLE_DIR${VALID_EXAMPLES[@]}" -name "*.wacc" | wc -l)
TOTAL_COUNT=$(find "${VALID_EXAMPLES[@]/#/${VALID_EXAMPLES_SRC_DIR}}" -name "*.wacc" | wc -l)
COUNTER=0
for folder in "$VALID_EXAMPLE_DIR${VALID_EXAMPLES[@]}"; do
for file in $(find "$folder" -name "*.wacc"); do
for folder in ${VALID_EXAMPLES[@]}; do
ASSEMBLY_OUTPUT_VALID_FOLDER="${ASSEMBLY_OUTPUT_DIR}${folder}"
mkdir $ASSEMBLY_OUTPUT_VALID_FOLDER
for file in $(find "${VALID_EXAMPLES_SRC_DIR}${folder}" -name "*.wacc")
do
FILE_NAME=$(basename "${file%.*}")
./compile -o1 -t $file 2> $VALID_EXAMPLE_LOG
ret=$?
echo "exit status: " $ret
if [ $ret -ne 0 ]; then
echo $file
echo "status code incorrect: " $ret
exit 1
fi
if diff "$TEST_DIR${FILE_NAME}.log" $VALID_EXAMPLE_LOG > temp; then
echo $FILE_NAME
echo "optimise differ from expected result: "
exit 1
EXECUTABLE_FILE_NAME="${ASSEMBLY_OUTPUT_VALID_FOLDER}/${FILE_NAME}"
echo $file
./compile -t -o1 $file > "${EXECUTABLE_FILE_NAME}.log.txt"
if diff "${EXECUTABLE_FILE_NAME}.log.txt" "${VALID_EXAMPLES_SRC_DIR}${folder}/${FILE_NAME}.log" -I scope; then
(( COUNTER += 1 ))
fi
(( COUNTER += 1 ))
echo "$COUNTER / $(($TOTAL_COUNT)) finished"
echo ""
echo "$COUNTER / $(($TOTAL_COUNT)) files have been executed"
done
echo "========================================================================================"
......
......@@ -5,8 +5,8 @@ VALID_EXAMPLES=(
"/bitwiseOperation"
"/do-while"
"/for"
"/import" # OK
"/jump" # OK
"/import"
"/jump"
"/struct"
"/switch"
)
......@@ -35,6 +35,8 @@ for folder in ${VALID_EXAMPLES[@]}; do
EXECUTABLE_OUTPUT_FILE="${EXECUTE_OUTPUT_VALID_FOLDER}/${FILE_NAME}"
echo $file
./compile $file 2> "${EXECUTABLE_FILE_NAME}.log.txt"
echo "${EXECUTABLE_FILE_NAME}.s"
echo "${FILE_NAME}.s"
mv "${FILE_NAME}.s" "${EXECUTABLE_FILE_NAME}.s"
arm-linux-gnueabi-gcc -o $EXECUTABLE_OUTPUT_FILE -mcpu=arm1176jzf-s -mtune=arm1176jzf-s "${EXECUTABLE_FILE_NAME}.s" > "${EXECUTABLE_OUTPUT_FILE}.log.txt"
......@@ -45,11 +47,11 @@ for folder in ${VALID_EXAMPLES[@]}; do
echo "execution exit status" $ret2
## altomatically generate output .log file
mv "${EXECUTABLE_OUTPUT_FILE}.output.txt" "${VALID_EXAMPLES_SRC_DIR}${folder}/${FILE_NAME}.log"
# mv "${EXECUTABLE_OUTPUT_FILE}.output.txt" "${VALID_EXAMPLES_SRC_DIR}${folder}/${FILE_NAME}.log"
if [ "$ret1" -eq 0 ] && [ "$ret2" -eq 0 ]; then
## test if output is same as .log file
# diff "${EXECUTABLE_OUTPUT_FILE}.output.txt" "${VALID_EXAMPLES_SRC_DIR}${folder}/${FILE_NAME}.log"
diff "${EXECUTABLE_OUTPUT_FILE}.output.txt" "${VALID_EXAMPLES_SRC_DIR}${folder}/${FILE_NAME}.log"
(( COUNTER += 1 ))
fi
......
......@@ -64,13 +64,13 @@ public class Compiler {
int optimise_cmd_index = cmd_ops.indexOf("--optimise");
/* if not found optimise flag, no optimise */
if (optimise_cmd_index == 0) {
if (optimise_cmd_index != 0) {
String optimise_level = cmd_ops.get(optimise_cmd_index + 1);
switch (optimise_level) {
case "0":
break;
case "1":
System.out.println("optimising using const propagate");
NodeVisitor<Node> constPropOptimiser = new ConstantPropagation();
program = constPropOptimiser.visit(program);
break;
......
......@@ -83,6 +83,9 @@ public class ARMInstructionGenerator implements NodeVisitor<Void> {
no need to distinguish function and non function scope, as non function scope does not call return */
private int funcStackSize;
/* used when a break/jump occure in a loop statment, accumulated on entering scope */
private int loopStackSize;
/* used for mapping type with its print routine function */
private final Map<Type, RoutineInstruction> typeRoutineMap = Map.of(
INT_BASIC_TYPE, PRINT_INT,
......@@ -683,16 +686,11 @@ public class ARMInstructionGenerator implements NodeVisitor<Void> {
/* 1 leave space for variables in stack */
int stackSize = node.getStackSize();
int temp = stackSize;
while (temp > 0) {
int realStackSize = temp / MAX_STACK_STEP >= 1 ? MAX_STACK_STEP : temp;
instructions.add(new Sub(SP, SP,
new Operand2(realStackSize)));
temp = temp - realStackSize;
}
decStack(stackSize);
/* accumulate function stack size, in case this scope is a function scope and contain return */
funcStackSize += stackSize;
loopStackSize += stackSize;
/* 2 visit statements
* set currentSymbolTable here, eliminate all other set symbol table in other statNode */
......@@ -704,15 +702,10 @@ public class ARMInstructionGenerator implements NodeVisitor<Void> {
/* decrease function stack size, as from this point stack is freed by the scope, not by return */
funcStackSize -= stackSize;
loopStackSize -= stackSize;
/* 3 restore stack */
temp = stackSize;
while (temp > 0) {
int realStackSize = temp / MAX_STACK_STEP >= 1 ? MAX_STACK_STEP : temp;
instructions.add(new Add(SP, SP,
new Operand2(realStackSize)));
temp = temp - realStackSize;
}
incStack(stackSize);
return null;
}
......@@ -743,8 +736,15 @@ public class ARMInstructionGenerator implements NodeVisitor<Void> {
instructions.add(startLabel);
/* record how much stack parent loop used */
int prevLoopSize = loopStackSize;
loopStackSize = 0;
/* 3 loop body */
visit(node.getBody());
/* restore parent loop stack size */
loopStackSize = prevLoopSize;
currBreakJumpToLabel = lastBreakJumpToLabel;
currContinueJumpToLabel = lastContinueJumpToLabel;
......@@ -863,86 +863,78 @@ public class ARMInstructionGenerator implements NodeVisitor<Void> {
@Override
public Void visitForNode(ForNode node) {
/* 1 translate the initiator of the for-loop */
currSymbolTable = node.getIncrement().getScope();
int stackSize = currSymbolTable.getSize();
decStack(stackSize);
visit(node.getInit());
/* 2 create labels and translate for for-loop body */
Label bodyLabel = branchLabelGenerator.getLabel();
Label condLabel = branchLabelGenerator.getLabel();
Label nextLabel = branchLabelGenerator.getLabel();
Label incrementLabel = branchLabelGenerator.getLabel();
/* restore the last jump-to label after visiting the for-loop body */
Label lastBreakJumpToLabel = currBreakJumpToLabel;
Label lastContinueJumpToLabel = currContinueJumpToLabel;
currBreakJumpToLabel = nextLabel;
currContinueJumpToLabel = condLabel;
currContinueJumpToLabel = incrementLabel;
instructions.add(new B(NULL, condLabel.getName()));
instructions.add(bodyLabel);
currForLoopSymbolTable = node.getBody().getScope();
/* record now much stack loop body have occupied */
int prevLoopSize = loopStackSize;
loopStackSize = 0;
visit(node.getBody());
/* restore parent loop stack size */
loopStackSize = prevLoopSize;
/* here we also need to append the for-loop increment at the end */
instructions.add(incrementLabel);
visit(node.getIncrement());
currBreakJumpToLabel = lastBreakJumpToLabel;
currContinueJumpToLabel = lastContinueJumpToLabel;
/* here we also need to append the for-loop in crement at the end */
visit(node.getIncrement());
/* 3 add label for condition checking */
instructions.add(condLabel);
currSymbolTable = node.getBody().getScope();
/* TODO: better code quality here */
int stackSize = currSymbolTable.getSize();
int temp = stackSize;
while (temp > 0) {
int realStackSize = temp / MAX_STACK_STEP >= 1 ? MAX_STACK_STEP : temp;
instructions.add(new Sub(SP, SP,
new Operand2(realStackSize)));
temp = temp - realStackSize;
}
currSymbolTable = node.getIncrement().getScope();
visit(node.getCond());
temp = stackSize;
while (temp > 0) {
int realStackSize = temp / MAX_STACK_STEP >= 1 ? MAX_STACK_STEP : temp;
instructions.add(new Add(SP, SP,
new Operand2(realStackSize)));
temp = temp - realStackSize;
}
currSymbolTable = currSymbolTable.getParentSymbolTable();
instructions.add(new Cmp(armRegAllocator.curr(), new Operand2(TRUE)));
armRegAllocator.free();
/* 4 conditional branch jump to the start of loop */
instructions.add(new B(EQ, bodyLabel.getName()));
/* 5 add the label for the following instructions after for-loop */
instructions.add(nextLabel);
/* loop varient's stack clearing should be after next label,
so that BREAK can add back stack used by varients */
incStack(stackSize);
return null;
}
@Override
public Void visitJumpNode(JumpNode node) {
Instruction addStack = null;
List<Instruction> addStack = new ArrayList<>();
/* this snippet is to deal with for-loop stack difference */
if (currForLoopSymbolTable != null && !node.getJumpContext().equals(JumpContext.SWITCH)) {
int stackSize = currForLoopSymbolTable.getSize();
int temp = stackSize;
while (temp > 0) {
int realStackSize = temp / MAX_STACK_STEP >= 1 ? MAX_STACK_STEP : temp;
addStack = new Add(SP, SP,
new Operand2(realStackSize));
temp = temp - realStackSize;
}
if (!node.getJumpType().equals(JumpType.BREAK) || !node.getJumpContext().equals(JumpContext.SWITCH)) {
incStack(loopStackSize);
}
if (node.getJumpType().equals(JumpType.BREAK)) {
if (addStack != null) instructions.add(addStack);
instructions.add(new B(NULL, currBreakJumpToLabel.getName()));
} else if (node.getJumpType().equals(JumpType.CONTINUE)) {
/* this snippet is to deal with the for-loop increment */
StatNode increment = node.getForIncrement();
if (increment != null) visit(increment);
if (addStack != null) instructions.add(addStack);
instructions.add(new B(NULL, currContinueJumpToLabel.getName()));
}
......@@ -988,4 +980,22 @@ public class ARMInstructionGenerator implements NodeVisitor<Void> {
return null;
}
private void incStack(int stackSize) {
while (stackSize > 0) {
int realStackSize = stackSize / MAX_STACK_STEP >= 1 ? MAX_STACK_STEP : stackSize;
instructions.add(new Add(SP, SP,
new Operand2(realStackSize)));
stackSize = stackSize - realStackSize;
}
}
private void decStack(int stackSize) {
while (stackSize > 0) {
int realStackSize = stackSize / MAX_STACK_STEP >= 1 ? MAX_STACK_STEP : stackSize;
instructions.add(new Sub(SP, SP,
new Operand2(realStackSize)));
stackSize = stackSize - realStackSize;
}
}
}
......@@ -328,12 +328,13 @@ public class ASTPrinter implements NodeVisitor<Void> {
/* body */
leadingSpace += INDENT_SIZE;
visit(node.getBody());
leadingSpace -= INDENT_SIZE;
appendLeadingSpace();
System.out.print("while ");
visit(node.getCond());
System.out.println(" :");
leadingSpace -= INDENT_SIZE;
} else {
System.out.print("while ");
visit(node.getCond());
......@@ -384,10 +385,16 @@ public class ASTPrinter implements NodeVisitor<Void> {
/* for 3 expressions : */
appendLeadingSpace();
System.out.print("for ");
leadingSpace += INDENT_SIZE;
visit(node.getInit());
appendLeadingSpace();
visit(node.getCond());
visit(node.getIncrement());
System.out.println(" :");
leadingSpace -= INDENT_SIZE;
appendLeadingSpace();
System.out.println(":");
/* body */
leadingSpace += INDENT_SIZE;
......@@ -406,15 +413,18 @@ public class ASTPrinter implements NodeVisitor<Void> {
public Void visitSwitchNode(SwitchNode node) {
/* switch statement */
appendLeadingSpace();
System.out.println("switch ");
System.out.println(node.getExpr());
System.out.print("switch ");
visit(node.getExpr());
System.out.println();
/* switch body */
leadingSpace += INDENT_SIZE;
for (CaseStat c : node.getCases()) {
System.out.println("case ");
appendLeadingSpace();
System.out.print("case ");
visit(c.getExpr());
System.out.println();
/* case body */
leadingSpace += INDENT_SIZE;
......@@ -422,9 +432,12 @@ public class ASTPrinter implements NodeVisitor<Void> {
leadingSpace -= INDENT_SIZE;
}
appendLeadingSpace();
System.out.println("default");
leadingSpace += INDENT_SIZE;
visit(node.getDefault());
leadingSpace -= INDENT_SIZE;
leadingSpace -= 2 * INDENT_SIZE;
return null;
}
......
......@@ -11,11 +11,7 @@ import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.*;
import frontend.node.FuncNode;
import frontend.node.Node;
......@@ -30,7 +26,6 @@ import frontend.node.expr.BinopNode.Binop;
import frontend.node.expr.UnopNode.Unop;
import frontend.type.*;
import java.util.Set;
import org.antlr.v4.runtime.tree.TerminalNode;
import org.antlr.v4.runtime.CharStream;
......@@ -70,10 +65,13 @@ public class SemanticChecker extends WACCParserBaseVisitor<Node> {
private boolean isMainFunction;
/* used in determining whether branching statement is legal, i.e. break/continue is within a loop/switch */
private boolean isBreakAllowed;
private boolean isContinueAllowed;
private boolean isJumpRepeated;
private JumpContext jumpContext;
private Stack<Boolean> isBreakAllowed;
private Stack<Boolean> isContinueAllowed;
private Stack<Boolean> isJumpRepeated;
private Stack<JumpContext> jumpContext;
/* record the for-loop incrementer so that break and continue know what to do before jumping */
private Stack<StatNode> currForLoopIncrementBreak;
private Stack<StatNode> currForLoopIncrementContinue;
/* used only in function declare step, to check function has the correct return type */
private Type expectedFunctionReturn;
......@@ -81,15 +79,11 @@ public class SemanticChecker extends WACCParserBaseVisitor<Node> {
/* record whether a skipable semantic error is found in visiting to support checking of multiple errors */
private boolean semanticError;
/* record the for-loop incrementer so that break and continue know what to do before jumping */
private StatNode currForLoopIncrementBreak;
private StatNode currForLoopIncrementContinue;
/* record which file have already been included, IN the current import chain */
private Set<String> libraryCollection;
/* record all files that have been imported */
private Set<String> allImportCollection = new HashSet<>();
private static Set<String> allImportCollection = new HashSet<>();
/* for function overload */
private Set<String> overloadFuncNames = new HashSet<>();
......@@ -101,13 +95,21 @@ public class SemanticChecker extends WACCParserBaseVisitor<Node> {
globalStructTable = new HashMap<>();
globalFuncTable = new HashMap<>();
isMainFunction = false;
isBreakAllowed = false;
isContinueAllowed = false;
jumpContext = null;
isJumpRepeated = false;
isBreakAllowed = new Stack<>();
isBreakAllowed.push(false);
isContinueAllowed = new Stack<>();
isContinueAllowed.push(false);
jumpContext = new Stack<>();
isJumpRepeated = new Stack<>();
isJumpRepeated.push(false);
expectedFunctionReturn = null;
currForLoopIncrementBreak = null;
currForLoopIncrementContinue = null;
currForLoopIncrementBreak = new Stack<>();
currForLoopIncrementContinue = new Stack<>();
this.libraryCollection = libraryCollection;
}
......@@ -376,11 +378,11 @@ public class SemanticChecker extends WACCParserBaseVisitor<Node> {
currSymbolTable = currSymbolTable.getParentSymbolTable();
if (functionBody instanceof ScopeNode) {
((ScopeNode) functionBody).setFuncBody();
((ScopeNode) functionBody).setAvoidSubStack();
return functionBody;
}
ScopeNode enclosedBody = new ScopeNode(functionBody);
enclosedBody.setFuncBody();
enclosedBody.setAvoidSubStack();
return enclosedBody;
}
......@@ -412,19 +414,26 @@ public class SemanticChecker extends WACCParserBaseVisitor<Node> {
semanticError |= typeCheck(ctx.expr(), BOOL_BASIC_TYPE, conditionType);
/* both branch are permitted to have/not have jump */
boolean permitJump = isJumpRepeated;
boolean permitJump = isJumpRepeated.peek();
/* create the StatNode for the if body and gegerate new child scope */
currSymbolTable = new SymbolTable(currSymbolTable);
StatNode ifBody = visit(ctx.stat(0)).asStatNode();
currSymbolTable = currSymbolTable.getParentSymbolTable();
isJumpRepeated = permitJump;
/* restore permit/not permit jump */
isJumpRepeated.pop();
isJumpRepeated.push(permitJump);
/* create the StatNode for the else body and generate new child scope */
currSymbolTable = new SymbolTable(currSymbolTable);
StatNode elseBody = visit(ctx.stat(1)).asStatNode();
currSymbolTable = currSymbolTable.getParentSymbolTable();
/* restore permit/not permit jump */
isJumpRepeated.pop();
isJumpRepeated.push(permitJump);
StatNode node = new IfNode(condition,
ifBody instanceof ScopeNode ?
ifBody :
......@@ -448,16 +457,20 @@ public class SemanticChecker extends WACCParserBaseVisitor<Node> {
/* get the StatNode of the execution body of while loop */
currSymbolTable = new SymbolTable(currSymbolTable);
boolean isNestedBreak = isBreakAllowed;
boolean isNestedContinue = isContinueAllowed;
isBreakAllowed = isContinueAllowed = true;
jumpContext = JumpContext.WHILE;
isJumpRepeated = false;
/* set context for WHILE */
isJumpRepeated.push(false);
isBreakAllowed.push(true);
isContinueAllowed.push(true);
jumpContext.push(JumpContext.WHILE);
StatNode body = visit(ctx.stat()).asStatNode();
currSymbolTable = currSymbolTable.getParentSymbolTable();
if (!isNestedBreak) isBreakAllowed = false;
if (!isNestedContinue) isContinueAllowed = false;
/* reset context to parent scope */
isJumpRepeated.pop();
isBreakAllowed.pop();
isContinueAllowed.pop();
jumpContext.pop();
StatNode node = (body instanceof ScopeNode) ?
new WhileNode(condition, body) :
......@@ -476,9 +489,21 @@ public class SemanticChecker extends WACCParserBaseVisitor<Node> {
/* get the StatNode of the execution body of while loop */
currSymbolTable = new SymbolTable(currSymbolTable);
isBreakAllowed = isContinueAllowed = true;
/* set contexts for DOWHILE */
isJumpRepeated.push(false);
isBreakAllowed.push(true);
isContinueAllowed.push(true);
jumpContext.push(JumpContext.WHILE);
StatNode body = visit(ctx.stat()).asStatNode();
isBreakAllowed = isContinueAllowed = false;
/* reset context to parent scope */
isJumpRepeated.pop();
isBreakAllowed.pop();
isContinueAllowed.pop();
jumpContext.pop();
currSymbolTable = currSymbolTable.getParentSymbolTable();
StatNode node = (body instanceof ScopeNode) ?
......@@ -495,32 +520,39 @@ public class SemanticChecker extends WACCParserBaseVisitor<Node> {
currSymbolTable = new SymbolTable(currSymbolTable);