diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000000000000000000000000000000000000..45dd243cc52e683c2fbaadb90eaa4c6090fb5b32 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,60 @@ +image: maven:3-jdk-8 + +stages: + - build + - test + - analyse + +build-jdk11: + image: maven:3-jdk-11 + stage: build + script: "mvn clean package" + +build-jdk8: + stage: build + script: + - mvn clean package + - ls -alh target + artifacts: + paths: + - target/*.jar + expire_in: 20 minutes + +test-assignment: + stage: test + dependencies: + - build-jdk8 + script: + - ./run.sh ass + - ./run.sh ass 2>/dev/null | grep 'Tests:' > /tmp/result.log + - curl -G 'http://api.img4me.com/?font=firacode&fcolor=000000&size=10&bcolor=FFFFFF&type=png' --data-urlencode "text=$(cat /tmp/result.log)" > /tmp/good_png + - curl "https://git.recolic.net/_r_testing/set/cs164_proj3_test|$(cat /tmp/good_png)" + +test-positive-whole: + stage: test + dependencies: + - build-jdk8 + script: + - ./run.sh run src/test/data/pa3/benchmarks/tree.py > out.gi + - diff out.gi src/test/data/pa3/benchmarks/tree.py.ast.typed.s.result + allow_failure: true + +# code_quality: +# image: docker:stable +# stage: analyse +# variables: +# DOCKER_DRIVER: overlay2 +# allow_failure: true +# services: +# - docker:stable-dind +# script: +# - export SP_VERSION=$(echo "$CI_SERVER_VERSION" | sed 's/^\([0-9]*\)\.\([0-9]*\).*/\1-\2-stable/') +# - docker run +# --env SOURCE_CODE="$PWD" +# --volume "$PWD":/code +# --volume /var/run/docker.sock:/var/run/docker.sock +# "registry.gitlab.com/gitlab-org/security-products/codequality:$SP_VERSION" /code +# artifacts: +# reports: +# codequality: gl-code-quality-report.json + diff --git a/run.sh b/run.sh new file mode 100755 index 0000000000000000000000000000000000000000..2c974e1f3880de01aebf9003be4c6149084d00b0 --- /dev/null +++ b/run.sh @@ -0,0 +1,25 @@ +#!/bin/bash + +task="$1" +input="$2" +output="$3" + +classPath="chocopy-ref.jar:target/assignment.jar" + +if [[ "$task" = parsetype ]]; then + java -cp "$classPath" chocopy.ChocoPy --pass=rr --out "$output" "$input" +elif [[ "$task" = codegen ]]; then + java -cp "$classPath" chocopy.ChocoPy --pass=..s --out "$output" "$input" +elif [[ "$task" = ass ]]; then + java -cp "$classPath" chocopy.ChocoPy --pass=..s --run --dir src/test/data/pa3/sample/ --test | tee /tmp/cs164_ass.log + # Should pass all positive test, else return error_code=2 + # cat /tmp/cs164_ass.log | grep Test: | grep failed | grep -v sample/bad_ && exit 2 || exit 0 +elif [[ "$task" = build ]]; then + mvn clean package > /dev/null 2>&1 && echo success || echo failed +elif [[ "$task" = run ]]; then + java -cp "$classPath" chocopy.ChocoPy --pass=rrs --run "$input" +fi + +exit $? + + diff --git a/src/main/java/chocopy/pa3/CodeGenImpl.java b/src/main/java/chocopy/pa3/CodeGenImpl.java index 7d6c69f4b7d3c01a43b39c518efe97e5a2026c48..e7c8cee1d45128a4958b7e7512c53bec421c4585 100644 --- a/src/main/java/chocopy/pa3/CodeGenImpl.java +++ b/src/main/java/chocopy/pa3/CodeGenImpl.java @@ -4,13 +4,8 @@ import java.util.List; import chocopy.common.analysis.SymbolTable; import chocopy.common.analysis.AbstractNodeAnalyzer; -import chocopy.common.astnodes.Stmt; -import chocopy.common.astnodes.ReturnStmt; -import chocopy.common.codegen.CodeGenBase; -import chocopy.common.codegen.FuncInfo; -import chocopy.common.codegen.Label; -import chocopy.common.codegen.RiscVBackend; -import chocopy.common.codegen.SymbolInfo; +import chocopy.common.astnodes.*; +import chocopy.common.codegen.*; import static chocopy.common.codegen.RiscVBackend.Register.*; @@ -26,13 +21,57 @@ import static chocopy.common.codegen.RiscVBackend.Register.*; * what APIs it exposes for its sub-class (this one). Of particular * importance is knowing what all the SymbolInfo classes contain. */ + + public class CodeGenImpl extends CodeGenBase { + private class BetterRsicVBackend { + private final RiscVBackend backend; + BetterRsicVBackend(RiscVBackend _backend) { + backend = _backend; + } + public void emitNoop(String comment) { + if(comment != null) { + backend.emitMV(ZERO, ZERO, "Noop: " + comment); + } + } + public void emitPush(RiscVBackend.Register reg, String comment) { + backend.emitSW(reg, SP, 0, comment); + backend.emitADDI(SP, SP, -1 * backend.getWordSize(), comment); + } + public void emitPop(RiscVBackend.Register reg, String comment) { + backend.emitADDI(SP, SP, backend.getWordSize(), comment); + if(reg != null) + backend.emitLW(reg, SP, 0, comment); + } + public void emitCall(Label calledLabel, String comment) { + // Arguments should be already pushed to stack. + emitNoop(comment); + emitPush(RA, "backup return address of parent."); + emitPush(FP, "backup parent fp."); + backend.emitADDI(FP, SP, 2 * backend.getWordSize(), "Change FP to apply new frame."); + + // jal will set RA register properly. + backend.emitJAL(calledLabel, "Call it!"); + + emitPop(FP, "restore parent FP."); + emitPop(RA, "restore parent return address."); + } + public void emitRet(String comment) { + // All local variable should already be poped. + // Return value should be in A0 + emitNoop(comment); + backend.emitJR(RA, "Return to caller."); + } + } /** A code generator emitting instructions to BACKEND. */ public CodeGenImpl(RiscVBackend backend) { super(backend); + betterBackend = new BetterRsicVBackend(backend); } + private BetterRsicVBackend betterBackend; + /** Operation on None. */ private final Label errorNone = new Label("error.None"); /** Division by zero. */ @@ -93,7 +132,7 @@ public class CodeGenImpl extends CodeGenBase { } /** An analyzer that encapsulates code generation for statments. */ - private class StmtAnalyzer extends AbstractNodeAnalyzer<Void> { + private class StmtAnalyzer extends AbstractNodeAnalyzer<RiscVBackend.Register> { /* * The symbol table has all the info you need to determine * what a given identifier 'x' in the current scope is. You can @@ -149,7 +188,7 @@ public class CodeGenImpl extends CodeGenBase { // FIXME: Example of statement. @Override - public Void analyze(ReturnStmt stmt) { + public RiscVBackend.Register analyze(ReturnStmt node) { // FIXME: Here, we emit an instruction that does nothing. Clearly, // this is wrong, and you'll have to fix it. // This is here just to demonstrate how to emit a @@ -159,7 +198,45 @@ public class CodeGenImpl extends CodeGenBase { } // FIXME: More, of course. + @Override + public RiscVBackend.Register analyze(ExprStmt node) { + node.expr.dispatch(this); + return null; + } + + @Override + public RiscVBackend.Register analyze(CallExpr node) { + SymbolInfo called = sym.get(node.function.name); + if(called instanceof FuncInfo) { + FuncInfo func = (FuncInfo) called; + // TODO: Push the FUCKING arg0 (outter function frame ptr) + for(Expr expr : node.args) { + RiscVBackend.Register result = expr.dispatch(this); + if(result == null) { + throw new RuntimeException("NotImplemented: Expression " + expr.getClass().getName() + " returns null register."); + } + betterBackend.emitPush(result, "Push function arguments."); + } + betterBackend.emitCall(func.getCodeLabel(), null); + + for(Expr expr : node.args) { + betterBackend.emitPop(null, "Pop function arguments."); + } + + return A0; // Function return value is always in A0. + } + else if(called instanceof ClassInfo) { + // TODO: Fuck the fucking framework. + } + return null; + } + + @Override + public RiscVBackend.Register analyze(IntegerLiteral node) { + backend.emitLI(T0, node.value, "Set constant int to reg."); + return T0; + } } /**