/mjc

Compiler for MiniJava, a simple but non-trivial subset of Java

Primary LanguageJavaMIT LicenseMIT

/*
 * Author:  Trevor Schiff, tschiff2020@my.fit.edu
 * Course:  CSE 4251, Section 01, Spring 2023
 * Project: MiniJava Compiler Project
 * Charset: US-ASCII
 */

==========================================================================
Status of Appel's testcases
==========================================================================
BinarySearch.java: Compiles OK, executes OK, and gives the correct output
BinaryTree.java:   Compiles OK, executes OK, and gives the correct output
BubbleSort.java:   Compiles OK, executes OK, and gives the correct output
Factorial.java:    Compiles OK, executes OK, and gives the correct output
LinearSearch.java: Compiles OK, executes OK, and gives the correct output
LinkedList.java:   Compiles OK, executes OK, and gives the correct output
QuickSort.java:    Compiles OK, executes OK, and gives the correct output
TreeVisitor.java:  Compiles OK, executes OK, but gives the WRONG output 
                   (due to no dynamic dispatch implementation)

Total:
      - Compiled OK:    8/8
      - Executed OK:    8/8
      - Correct output: 7/8

==========================================================================
Status of my testcases
==========================================================================
##########################################################################
# NOTE: Test.java exists in the same directory as the compiler           #
# (as specified by the Assignment 8 instruction).                        #
#                                                                        #
#     All other test cases mentioned here are inside the "tests"         #
# directory. I'm not even sure if I am allowed to submit multiple,       #
# because the asssignment description says "You must also submit your    #
# one best test case"; however, the README section of the assignment     #
# mentions optionally supplying other files (such as MyTestCase1.java).  #
#                                                                        #
#    If you would like to run these extra cases, they exist inside       #
# the "tests" directory. The main case (Test.java) and all other cases   #
# are explained below in detail.                                         #
##########################################################################

Test.java: My "one best testcase". Demonstrates that my compiler supports
           the following concepts:
           - Printing
           - Integer/boolean expressions
           - Local variables
           - If-else statements
           - Else-if statements
           - While statements
           - Function calls (with formal parameters)
           - Object creation
           - Class field/method access (including ones inherited from
             base class FizzBuzzImpl)
           - Recursion

IROptimizerTest.java: Another test case. Demonstrates that my compiler's
                      front-end supports the following optimizations:
    [NOTE: These are not to be confused with constant *propagation*]
    [NOTE 2: This test case can be executed, but it is meant for
             observing the assembly code output]
                      - Compile-time constant folding in logical AND
                        expressions
                          - (x && false) -> (false)
                          - (x && true)  -> (x)

                      - Compile-time constant folding in LessThan
                        expressions
                          - (1 < 5) -> (true)
                          - (5 < 1) -> (false)

                      - Compile-time constant folding in BINOP
                        arithmetic (Plus/Minus/Times)

                      - Compile-time constant folding in logical NOT
                        expressions
                          - (!true)    -> (false)
                          - (!(1 < 5)) -> (false) 

                      - Compile-time constant folding when computing
                        array subscript offset, where the index is
                        a constant literal
                          - myArray[10] -> *(&myArray + (10 + 1) * 4)
                            -> *(&myArray + 44)

ArrayTest.java: Another test case (albeit small). Demonstrates that my
                compiler supports the following concepts not present
                in Test.java:
                - Array creation
                - Array lookup
                - Array assignment
                - Array length field

DefUseTest.java: Another much smaller test case. Demonstrates that my
                 compiler supports detecting uninitialized local variables
                 during the semantic analysis phase.
    [NOTE: This test case is meant to be compiled, but it is meant to
           throw errors (as there are uninitialized variables detected).]

==========================================================================
Optional features
==========================================================================
    The only optional feature I implemented was the ability to detect
uninitialized local variables. (See check.DefUseVisitor for implementation)

==========================================================================
Re: Zero-size object creation
==========================================================================
    When invoking the new operator to create a class instance, if the size
of the class is zero it will be substituted with a null pointer (CONST 0).
This is because there's no point in calling runtime_alloc_object to get a
buffer of zero bytes.

    It's not obvious from the assembly that this is happening, so I wanted
to mention it here.

==========================================================================
Re: Register allocation / back-end optimization
==========================================================================
    I believe my method of register allocation would be considered
"stupid" register allocation. Registers are assigned on a first-come,
first-serve basis, although they become re-available after their owner's
(temp's) lifetime ends.

    My "lifetime" data is a bit naive, it doesn't take into account things
like the order of basic blocks/control flow: it simply has a start and end
point in the code fragment.

    Despite this, it seems to have worked out nicely. This is in part due
to the optimizations I've written in (see optimize.Phase and/or
codegen.arch.sparc.SparcOptimizer). The optimization heuiristics are also
somewhat naive, but I am so happy with how they've managed to clean up
the assembly code. Because the optimization phase comes before the
register allocation phase, the optimizations also help me save registers.