Homework B (benchmarks)

This commit is contained in:
Carlos Galindo 2020-01-15 22:38:07 +01:00
parent 72cc3206c4
commit 76fbabdf53
Signed by: kauron
GPG key ID: 83E68706DEE119A3
141 changed files with 7540 additions and 2032 deletions

View file

@ -5,7 +5,7 @@
<classpathentry kind="lib" path="lib/junit-4.12.jar"/> <classpathentry kind="lib" path="lib/junit-4.12.jar"/>
<classpathentry kind="lib" path="lib/hamcrest-core-1.3.jar"/> <classpathentry kind="lib" path="lib/hamcrest-core-1.3.jar"/>
<classpathentry kind="lib" path="lib/antlr-4.7.1-complete.jar"/> <classpathentry kind="lib" path="lib/antlr-4.7.1-complete.jar"/>
<classpathentry kind="lib" path="lib/javaliParserObf.jar"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/> <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
<classpathentry kind="lib" path="lib/javaliParserObf.jar"/>
<classpathentry kind="output" path="bin"/> <classpathentry kind="output" path="bin"/>
</classpath> </classpath>

View file

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<projectDescription> <projectDescription>
<name>Javali-HW4</name> <name>Javali-HWB</name>
<comment></comment> <comment></comment>
<projects> <projects>
</projects> </projects>

View file

@ -0,0 +1,2 @@
eclipse.preferences.version=1
encoding/<project>=UTF-8

View file

@ -0,0 +1,7 @@
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
org.eclipse.jdt.core.compiler.compliance=1.8
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
org.eclipse.jdt.core.compiler.source=1.8

View file

@ -1,39 +1,15 @@
nop90: 29/30 points team name: nop90
task 1 correctness: 95
Comments: task 1 score: 541.3
I found that the following tasks were not implemented correctly or contained errors. task 1 points (max 25 points): 16
task 2 deadline 1 correctness: 100
* Null pointers check doesn't work for methods: task 2 deadline 1 score: 14.1
" task 2 deadline 1 points: 1
class Main { task 2 deadline 2 correctness: 97
void main() { task 2 deadline 2 score: 264.5
A a; task 2 deadline 2 points: 15
a.m(); task 2 runtimeOpts correctness: 98
} task 2 runtimeOpts score: 265.3
} task 2 runtimeOpts bonus points: 0
class A { task 2 points (max 25 points): 15
void m() { } HWB TOTAL (max 50 points): 31
}
"
* Side effects
If methods have side effects, then the execution order matters:
"
class Main {
void main() {
write(m1() + m2());
writeln();
}
int m1() {
write(1);
return 1;
}
int m2() {
write(2);
return 2;
}
}
"
* Can't compile programs which use many registers

16
Javali tests.launch Normal file
View file

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<launchConfiguration type="org.eclipse.jdt.junit.launchconfig">
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
<listEntry value="/Javali"/>
</listAttribute>
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
<listEntry value="4"/>
</listAttribute>
<stringAttribute key="org.eclipse.jdt.junit.CONTAINER" value="=Javali"/>
<booleanAttribute key="org.eclipse.jdt.junit.KEEPRUNNING_ATTR" value="false"/>
<stringAttribute key="org.eclipse.jdt.junit.TESTNAME" value=""/>
<stringAttribute key="org.eclipse.jdt.junit.TEST_KIND" value="org.eclipse.jdt.junit.loader.junit4"/>
<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value=""/>
<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="Javali"/>
<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Dcd.meta_hidden.Version=REFERENCE"/>
</launchConfiguration>

111
README.md
View file

@ -1,111 +0,0 @@
# HW4: code generation
Better viewed as markdown
# VTables
They are generated when each class is visited, except for those of Object,
Object[], int[] and boolean[], which are generated in the bootstrapping.
When the object vtable is generated, so is the array table, which just
contains a reference to the supertype (in our case `Object`) and an unused
pointer to the declared type of the element's vtable.
supertype pointer (Object) <-- vtable pointer
element vtable pointer (unused)
Each type's vtable contains the supertype as the first element (trait shared
with array type's vtables for easier cast type comparisons). Then they
contain all the methods available to an object of that type, including
all those inherited. As the dynamic type is not known, the number and
position of the methods is not known when executing a method, therefore
each method's pointer is prepended by the method's name hash (similar to
fields in an object).
supertype pointer <-- vtable pointer
hash("method0")
method0 pointer
hash("method1")
method1 pointer
...
hash("methodN")
methodN pointer
# Method calls
The basic scheme used for method calls and the structure of the stack
frame is that described in the `__cdecl` used in GCC. In short, this is
the execution of a method call:
1. The caller pushes the arguments in the stack, from right-to-left,
leaving the 0th argument (the reference to `this`) at the top of the stack.
2. The caller executes `call` (the instruction pointer gets stored in the stack)
3. The callee now takes control, and its first action is to save the base
pointer to the stack and set the base pointer to the current stack pointer.
Therefore, the arguments and return address are available to the method.
4. The local variables are reserved and zeroed in the stack.
5. The CALLEE_SAVED registers are saved to the stack.
6. The method executes its code
7. The CALLEE_SAVED registers are restored to their original status.
8. The callee restores the stack and base registers to its initial statuses.
This is accomplished with a `leave` instruction. Then it returns using `ret`.
This removes the instruction pointer from the stack leaving only the arguments.
9. The caller finally removes all the arguments from the stack and obtains the
return value from EAX.
Therefore, the stack frame follows the following structure (N arguments
and M local variables):
top of stack <-- ESP
...
saved registers
localM <-- EBP - 4 - M * 4
...
local1 <-- EBP - 8
local0 <-- EBP - 4
Old EBP <-- EBP
Return EIP
this <-- EBP + 8
arg0 <-- EBP + 12
...
argN <-- EBP + 12 + N * 4
# Variable and object representation
## Primitive variables
They are represented by value, with both integers and booleans occupying
4 bytes, and booleans being represented as TRUE = 0, FALSE = 1.
## Reference variables
Stored in the heap, using calloc (to zero-initialize them).
### Arrays
On top of storing the array elements, some overhead is stored with it,
namely a pointer to the array vtable for casts, and the size of the
array to check the bounds of the array on accesses or assignments.
The pointer to the array points to the vtable pointer (the first
element in the object structure):
array vtable pointer <-- var pointer
array size
element0
element1
...
elementN
### Objects
Stores a pointer to the object's type vtable and all the fields. As the
dynamic type of the object cannot be determined and therefore the fields
not statically ordered, each field occupies 8 bytes, with the first 4 containing
the hash for the VariableSymbol that represents it and the others the
value or reference to the field.
The hash allows hiding variables, but introduces an overhead when accessing
a field, to look for the position in which it is located.
vtable pointer <-- var pointer
field0's hash
field0
field1's hash
field1
...
fieldN's hash
fieldN

View file

@ -0,0 +1,60 @@
// Overall test of arrays, loops, etc. that does a simple quicksort.
class Record {
int a ;
}
class Main {
Record [] a;
int i;
void swap(Record r1, Record r2) {
int temp;
temp = 0+1;
temp = r1.a;
r1.a = r2.a;
r2.a = temp;
}
void sort(int left, int right) {
int i,j;
int m;
m = (a[left].a + a[right].a) / (1 + 1);
i = left;
j = right;
while (i <= j) {
while (a[i].a < m) { i = i+1; }
while (a[j].a > m) { j = j-1; }
if (i <= j) {
swap(a[i], a[j]);
i = i+1;
j = j-1;
}
}
if (left < j) { sort(left,j); }
if (i < right) { sort(i,right); }
}
void main() {
int SIZE;
int j;
SIZE = 10;
a = new Record[SIZE * 1];
j = 0;
while (j < SIZE) {
a[j] = new Record();
a[j].a = read();
j = j + 1;
}
sort(0, SIZE-1);
j = 0;
while (j < SIZE) {
i = a[j].a;
write(i);
writeln();
j = j + 1;
}
}
}

View file

@ -0,0 +1,10 @@
1
5
6
7
8
5
2
4
6
3

View file

@ -0,0 +1,40 @@
class Main {
int SIZE;
void main() {
int[] a;
int i;
int pi;
int e;
int thou;
pi = 3141;
e = 2718;
a = readStuff();
i = 0;
while(i < SIZE) {
write(pi * e * a[i] / (1000 * 1000));
writeln();
write(a[i] * pi * e / (1000 * 1000));
writeln();
i = i + 1;
}
}
int[] readStuff() {
int[] a;
int i;
SIZE = 5;
a = new int[SIZE];
i = 0;
while (i < SIZE) {
a[i] = read();
i = i + 1;
}
return a;
}
}

View file

@ -0,0 +1,5 @@
42
1337
9000
12345678
777

View file

@ -0,0 +1,13 @@
class Main {
void main() {
int i;
int j;
i = read();
j = 2 * i;
i = (i + 31) / 2;
j = (i * j) / 3;
write(i);
}
}

View file

@ -0,0 +1 @@
43

View file

@ -8,33 +8,40 @@
<property name="src.dir" value="${basedir}/src"/> <property name="src.dir" value="${basedir}/src"/>
<property name="test.dir" value="${basedir}/test"/> <property name="test.dir" value="${basedir}/test"/>
<property name="javali_tests.dir" value="${basedir}/javali_tests"/> <property name="javali_tests.dir" value="${basedir}/javali_tests"/>
<property name="benchmarks.dir" value="${basedir}/benchmarks"/>
<property name="parser.dir" value="${src.dir}/cd/frontend/parser"/>
<property name="parser.jar" value="${basedir}/lib/javaliParserObf.jar"/> <property name="parser.jar" value="${basedir}/lib/javaliParserObf.jar"/>
<property name="build.dir" value="${basedir}/bin"/> <property name="build.dir" value="${basedir}/bin"/>
<property name="junit.jar" value="${basedir}/lib/junit-4.12.jar"/> <property name="junit.jar" value="${basedir}/lib/junit-4.12.jar"/>
<property name="hamcrest.jar" value="${basedir}/lib/hamcrest-core-1.3.jar"/> <property name="hamcrest.jar" value="${basedir}/lib/hamcrest-core-1.3.jar"/>
<property name="antlr.jar" value="${basedir}/lib/antlr-4.4-complete.jar"/> <property name="antlr.jar" value="${basedir}/lib/antlr-4.7.1-complete.jar"/>
<property name="antlr.profile" value="false"/> <property name="antlr.profile" value="false"/>
<property name="antlr.report" value="false"/> <property name="antlr.report" value="false"/>
<property name="jacocoant.jar" value="${basedir}/lib/jacocoant.jar"/>
<property name="coverage.file" location="${build.dir}/jacoco.exec"/>
<property name="min.coverage" value="0.5"/>
<property name="coverage.check" value="cd.frontend.*:cd.backend.*"/>
<!-- Cleans generated code, but NOT the parser source! --> <!-- Cleans generated code, but NOT the parser source! -->
<target name="clean"> <target name="clean">
<delete dir="${build.dir}"/> <delete dir="${build.dir}"/>
</target> </target>
<target name="compile"> <!-- Builds the compiler framework for HW > HW1. -->
<mkdir dir="${build.dir}"/> <target name="compile">
<mkdir dir="${build.dir}"/>
<javac debug="true" destdir="${build.dir}" includeantruntime="false"> <javac debug="true" destdir="${build.dir}" includeantruntime="false">
<src path="${src.dir}"/> <src path="${src.dir}"/>
<src path="${test.dir}"/> <src path="${test.dir}"/>
<classpath> <classpath>
<pathelement location="${antlr.jar}"/> <pathelement location="${antlr.jar}"/>
<pathelement location="${junit.jar}"/> <pathelement location="${junit.jar}"/>
<pathelement location="${hamcrest.jar}"/> <pathelement location="${hamcrest.jar}"/>
<pathelement location="${parser.jar}"/> <pathelement location="${parser.jar}"/>
</classpath> </classpath>
</javac> </javac>
</target> </target>
<!-- Deletes all byproducts of running the tests --> <!-- Deletes all byproducts of running the tests -->
<target name="clean-test"> <target name="clean-test">
@ -44,28 +51,53 @@
<fileset dir="${javali_tests.dir}" includes="**/*.bin"/> <fileset dir="${javali_tests.dir}" includes="**/*.bin"/>
<fileset dir="${javali_tests.dir}" includes="**/*.dot"/> <fileset dir="${javali_tests.dir}" includes="**/*.dot"/>
<fileset dir="${javali_tests.dir}" includes="**/*.exe"/> <fileset dir="${javali_tests.dir}" includes="**/*.exe"/>
<fileset dir="${javali_tests.dir}" includes="**/*.ref"/> <fileset dir="${javali_tests.dir}" includes="**/*.ref"/>
<fileset dir="${benchmarks.dir}" includes="**/*.err"/>
<fileset dir="${benchmarks.dir}" includes="**/*.s"/>
<fileset dir="${benchmarks.dir}" includes="**/*.bin"/>
<fileset dir="${benchmarks.dir}" includes="**/*.dot"/>
<fileset dir="${benchmarks.dir}" includes="**/*.exe"/>
<fileset dir="${benchmarks.dir}" includes="**/*.ref"/>
</delete> </delete>
</target> </target>
<taskdef uri="antlib:org.jacoco.ant" resource="org/jacoco/ant/antlib.xml">
<classpath path="${jacocoant.jar}"/>
</taskdef>
<!-- Runs the tests. Use the compile target first! --> <!-- Runs the tests. Use the compile target first! -->
<target name="test" depends="compile"> <target name="test" depends="compile">
<junit fork="true" forkmode="once" failureproperty="tests-failed" outputtoformatters="false"> <jacoco:coverage destfile="${coverage.file}">
<formatter type="brief" usefile="false"/> <junit fork="true" forkmode="once" failureproperty="tests-failed" outputtoformatters="false">
<batchtest skipNonTests="true"> <formatter type="brief" usefile="false"/>
<fileset dir="bin" includes="**/*.class" /> <batchtest skipNonTests="true">
</batchtest> <fileset dir="bin" includes="**/*.class" />
<assertions enablesystemassertions="true" /> </batchtest>
<sysproperty key="cd.meta_hidden.Version" value="REFERENCE" /> <assertions enablesystemassertions="true" />
<classpath> <sysproperty key="cd.meta_hidden.Version" value="BENCH" />
<pathelement location="${build.dir}"/> <classpath>
<pathelement location="${junit.jar}"/> <pathelement location="${build.dir}"/>
<pathelement location="${hamcrest.jar}"/> <pathelement location="${junit.jar}"/>
<pathelement location="${antlr.jar}"/> <pathelement location="${hamcrest.jar}"/>
<pathelement location="${parser.jar}"/> <pathelement location="${antlr.jar}"/>
</classpath> <pathelement location="${parser.jar}"/>
</junit> </classpath>
<fail if="tests-failed" /> </junit>
</jacoco:coverage>
<fail if="tests-failed" />
</target> </target>
<target name="bench" depends="compile">
<java classname="cd.BenchmarksRunner" fork="yes" error="bench-log.txt">
<classpath>
<pathelement location="${build.dir}"/>
<pathelement location="${antlr.jar}"/>
<pathelement location="${junit.jar}"/>
<pathelement location="${hamcrest.jar}"/>
<pathelement location="${parser.jar}"/>
</classpath>
</java>
</target>
</project> </project>

0
javali_tests/HW1_nop90/Division.javali Executable file → Normal file
View file

0
javali_tests/HW1_nop90/EightVariablesWrite.javali Executable file → Normal file
View file

0
javali_tests/HW1_nop90/Multiplication.javali Executable file → Normal file
View file

0
javali_tests/HW1_nop90/Overflow.javali Executable file → Normal file
View file

0
javali_tests/HW1_nop90/Overflow.javali.in Executable file → Normal file
View file

0
javali_tests/HW1_nop90/ReadWrite.javali Executable file → Normal file
View file

0
javali_tests/HW1_nop90/ReadWrite.javali.in Executable file → Normal file
View file

0
javali_tests/HW1_nop90/UnaryOperators.javali Executable file → Normal file
View file

0
javali_tests/HW1_nop90/noParentheses.javali Executable file → Normal file
View file

0
javali_tests/HW2_nop90/Casts.javali Executable file → Normal file
View file

0
javali_tests/HW2_nop90/Division.javali Executable file → Normal file
View file

0
javali_tests/HW2_nop90/EightVariablesWrite.javali Executable file → Normal file
View file

0
javali_tests/HW2_nop90/Multiplication.javali Executable file → Normal file
View file

0
javali_tests/HW2_nop90/NULLTest.javali Executable file → Normal file
View file

0
javali_tests/HW2_nop90/Overflow.javali Executable file → Normal file
View file

0
javali_tests/HW2_nop90/Overflow.javali.in Executable file → Normal file
View file

0
javali_tests/HW2_nop90/ReadWrite.javali Executable file → Normal file
View file

0
javali_tests/HW2_nop90/ReadWrite.javali.in Executable file → Normal file
View file

0
javali_tests/HW2_nop90/UnaryOperators.javali Executable file → Normal file
View file

0
javali_tests/HW2_nop90/conditionExpressions.javali Executable file → Normal file
View file

0
javali_tests/HW2_nop90/invalidCasts.javali Executable file → Normal file
View file

0
javali_tests/HW2_nop90/noParentheses.javali Executable file → Normal file
View file

View file

@ -24,5 +24,3 @@ class Main {
} }
} }

0
javali_tests/HW4_nop90/Booleans/OkEquals2.javali Executable file → Normal file
View file

0
javali_tests/HW4_nop90/Booleans/OkEquals3.javali Executable file → Normal file
View file

View file

View file

0
javali_tests/HW4_nop90/Casts/ErrPrimitiveCast.javali Executable file → Normal file
View file

0
javali_tests/HW4_nop90/Casts/ErrPrimitiveCast2.javali Executable file → Normal file
View file

View file

View file

0
javali_tests/HW4_nop90/Casts/OkSubtype.javali Executable file → Normal file
View file

0
javali_tests/HW4_nop90/Casts/OkTypeCast.javali Executable file → Normal file
View file

0
javali_tests/HW4_nop90/Casts/OkTypeToObjectCast.javali Executable file → Normal file
View file

0
javali_tests/HW4_nop90/EightVariablesWrite.javali Executable file → Normal file
View file

0
javali_tests/HW4_nop90/ErrDivisionByZero.javali Executable file → Normal file
View file

0
javali_tests/HW4_nop90/Fields/OkObjectFields.javali Executable file → Normal file
View file

0
javali_tests/HW4_nop90/OkBoolBinaryOp.javali Executable file → Normal file
View file

0
javali_tests/HW4_nop90/OkCasts.javali Executable file → Normal file
View file

View file

@ -0,0 +1,10 @@
/* Test that variables are zero initialized */
class Main {
void main() {
boolean a;
if (!a){
write(5); }
}
}

0
javali_tests/HW4_nop90/OkNullAssignment.javali Executable file → Normal file
View file

View file

@ -8,8 +8,7 @@ class Main {
e = true; e = true;
f = true; f = true;
g = true; g = true;
z = 0; while (e) {
while (z < 30) {
if (f){ if (f){
if (g){ if (g){
u = 5; u = 5;
@ -22,8 +21,6 @@ class Main {
else { else {
j = true; j = true;
} }
z = z + 1;
} }
} }

0
javali_tests/HW4_nop90/OkUnaryOperators.javali Executable file → Normal file
View file

0
javali_tests/HW4_nop90/OkVariables.javali Executable file → Normal file
View file

0
javali_tests/HW4_nop90/OkZeroInitialized.javali Executable file → Normal file
View file

View file

View file

View file

View file

View file

View file

View file

View file

@ -0,0 +1,60 @@
// Overall test of arrays, loops, etc. that does a simple quicksort.
class Record {
int a ;
}
class Main {
Record [] a;
int i;
void swap(Record r1, Record r2) {
int temp;
temp = 0+1;
temp = r1.a;
r1.a = r2.a;
r2.a = temp;
}
void sort(int left, int right) {
int i,j;
int m;
m = (a[left].a + a[right].a) / (1 + 1);
i = left;
j = right;
while (i <= j) {
while (a[i].a < m) { i = i+1; }
while (a[j].a > m) { j = j-1; }
if (i <= j) {
swap(a[i], a[j]);
i = i+1;
j = j-1;
}
}
if (left < j) { sort(left,j); }
if (i < right) { sort(i,right); }
}
void main() {
int SIZE;
int j;
SIZE = 10;
a = new Record[SIZE * 1];
j = 0;
while (j < SIZE) {
a[j] = new Record();
a[j].a = read();
j = j + 1;
}
sort(0, SIZE-1);
j = 0;
while (j < SIZE) {
i = a[j].a;
write(i);
writeln();
j = j + 1;
}
}
}

View file

@ -0,0 +1,10 @@
1
5
6
7
8
5
2
4
6
3

View file

@ -3,137 +3,160 @@ package cd;
import java.io.File; import java.io.File;
public class Config { public class Config {
public static enum SystemKind {
LINUX,
WINDOWS,
MACOSX
}
/**
* Defines the extension used for assembler files on this platform.
* Currently always {@code .s}.
*/
public static final String ASMEXT = ".s";
/** Defines the extension used for binary files on this platform. */
public static final String BINARYEXT;
/** Defines the name of the main function to be used in .s file */
public static final String MAIN;
/** Defines the name of the printf function to be used in .s file */
public static final String PRINTF;
/** Defines the name of the scanf function to be used in .s file */
public static final String SCANF;
/** Defines the name of the calloc function to be used in .s file */
public static final String CALLOC;
/** Defines the name of the exit function to be used in .s file */
public static final String EXIT;
/** The assembler directive used to define a constant string */
public static final String DOT_STRING;
/** The assembler directive used to define a constant int */
public static final String DOT_INT;
/** The assembler directive used to start the text section */
public static final String TEXT_SECTION;
/** The assembler directive used to start the section for integer data */
public static final String DATA_INT_SECTION;
/**
* The assembler directive used to start the section for string data (if
* different from {@link #DATA_INT_SECTION}
*/
public static final String DATA_STR_SECTION;
/** Comment separator used in assembly files */
public static final String COMMENT_SEP;
/**
* Defines the assembler command to use. Should be a string array where each
* entry is one argument. Use the special string "$0" to refer to the output
* file, and $1 to refer to the ".s" file.
*/
public static final String[] ASM;
/**
* The directory from which to run the assembler. In a CYGWIN installation,
* this can make a big difference!
*/
public static final File ASM_DIR;
/**
* sizeof a pointer in bytes in the target platform.
*/
public static final int SIZEOF_PTR = 4;
/** public static enum SystemKind {
* Name of java executable in JRE path LINUX,
*/ WINDOWS,
MACOSX
}
public static final int TRUE = 1;
public static final int FALSE = 0;
/**
* Defines the extension used for assembler files on this platform.
* Currently always {@code .s}.
*/
public static final String ASMEXT = ".s";
/**
* Defines the extension used for binary files on this platform.
*/
public static final String BINARYEXT;
/**
* Defines the name of the main function to be used in .s file
*/
public static final String MAIN;
/**
* Defines the name of the printf function to be used in .s file
*/
public static final String PRINTF;
/**
* Defines the name of the scanf function to be used in .s file
*/
public static final String SCANF;
/**
* Defines the name of the calloc function to be used in .s file
*/
public static final String CALLOC;
/**
* Defines the name of the exit function to be used in .s file
*/
public static final String EXIT;
/**
* The assembler directive used to define a constant string
*/
public static final String DOT_STRING;
/**
* The assembler directive used to define a constant int
*/
public static final String DOT_INT;
/**
* The assembler directive used to start the text section
*/
public static final String TEXT_SECTION;
/**
* The assembler directive used to start the section for integer data
*/
public static final String DATA_INT_SECTION;
/**
* The assembler directive used to start the section for string data (if
* different from {@link #DATA_INT_SECTION}
*/
public static final String DATA_STR_SECTION;
/**
* Comment separator used in assembly files
*/
public static final String COMMENT_SEP;
/**
* Defines the assembler command to use. Should be a string array where each
* entry is one argument. Use the special string "$0" to refer to the output
* file, and $1 to refer to the ".s" file.
*/
public static final String[] ASM;
/**
* The directory from which to run the assembler. In a CYGWIN installation,
* this can make a big difference!
*/
public static final File ASM_DIR;
/**
* sizeof a pointer in bytes in the target platform.
*/
public static final int SIZEOF_PTR = 4;
/**
* Name of java executable in JRE path
*/
public static final String JAVA_EXE; public static final String JAVA_EXE;
static { static {
final String os = System.getProperty("os.name").toLowerCase(); final String os = System.getProperty("os.name").toLowerCase();
if(os.contains("windows") || os.contains("nt")) { if (os.contains("windows") || os.contains("nt")) {
BINARYEXT = ".exe"; BINARYEXT = ".exe";
MAIN = "_main"; MAIN = "_main";
PRINTF = "_printf"; PRINTF = "_printf";
SCANF = "_scanf"; SCANF = "_scanf";
CALLOC = "_calloc"; CALLOC = "_calloc";
EXIT = "_exit"; EXIT = "_exit";
// These are set up for a Cygwin installation on C:, // These are set up for a Cygwin installation on C:,
// you can change as needed. // you can change as needed.
ASM = new String[]{"gcc", "-o", "$0", "$1"}; ASM = new String[]{"gcc", "-o", "$0", "$1"};
ASM_DIR = new File("C:\\CYGWIN\\BIN"); ASM_DIR = new File("C:\\CYGWIN\\BIN");
JAVA_EXE = "javaw.exe"; JAVA_EXE = "javaw.exe";
DOT_STRING = ".string"; DOT_STRING = ".string";
DOT_INT = ".int"; DOT_INT = ".int";
TEXT_SECTION = ".section .text"; TEXT_SECTION = ".section .text";
DATA_INT_SECTION = ".section .data"; DATA_INT_SECTION = ".section .data";
DATA_STR_SECTION = ".section .data"; DATA_STR_SECTION = ".section .data";
COMMENT_SEP = "#"; COMMENT_SEP = "#";
} } else if (os.contains("mac os x") || os.contains("darwin")) {
else if(os.contains("mac os x") || os.contains("darwin")) { BINARYEXT = ".bin";
BINARYEXT = ".bin"; MAIN = "_main";
MAIN = "_main"; PRINTF = "_printf";
PRINTF = "_printf"; SCANF = "_scanf";
SCANF = "_scanf"; CALLOC = "_calloc";
CALLOC = "_calloc"; EXIT = "_exit";
EXIT = "_exit"; ASM = new String[]{"gcc", "-m32", "-o", "$0", "$1"};
ASM = new String[]{"gcc", "-m32", "-o", "$0", "$1"}; ASM_DIR = new File(".");
ASM_DIR = new File("."); JAVA_EXE = "java";
JAVA_EXE = "java"; DOT_STRING = ".asciz";
DOT_STRING = ".asciz"; DOT_INT = ".long";
DOT_INT = ".long"; TEXT_SECTION = ".text";
TEXT_SECTION = ".text"; DATA_INT_SECTION = ".data";
DATA_INT_SECTION = ".data"; DATA_STR_SECTION = ".cstring";
DATA_STR_SECTION = ".cstring"; COMMENT_SEP = "#";
COMMENT_SEP = "#"; } else {
} BINARYEXT = ".bin";
else { MAIN = "main";
BINARYEXT = ".bin"; PRINTF = "printf";
MAIN = "main"; SCANF = "scanf";
PRINTF = "printf"; CALLOC = "calloc";
SCANF = "scanf"; EXIT = "exit";
CALLOC = "calloc"; ASM = new String[]{"gcc", "-m32", "-o", "$0", "$1"};
EXIT = "exit"; ASM_DIR = new File(".");
ASM = new String[]{"gcc", "-m32", "-o", "$0", "$1"}; JAVA_EXE = "java";
ASM_DIR = new File("."); DOT_STRING = ".string";
JAVA_EXE = "java"; DOT_INT = ".int";
DOT_STRING = ".string"; TEXT_SECTION = ".section .text";
DOT_INT = ".int"; DATA_INT_SECTION = ".section .data";
TEXT_SECTION = ".section .text"; DATA_STR_SECTION = ".section .data";
DATA_INT_SECTION = ".section .data"; COMMENT_SEP = "#";
DATA_STR_SECTION = ".section .data"; }
COMMENT_SEP = "#"; }
}
}
} }

View file

@ -1,6 +1,6 @@
package cd; package cd;
import cd.backend.codegen.AstCodeGenerator; import cd.backend.codegen.CfgCodeGenerator;
import cd.frontend.parser.JavaliAstVisitor; import cd.frontend.parser.JavaliAstVisitor;
import cd.frontend.parser.JavaliLexer; import cd.frontend.parser.JavaliLexer;
import cd.frontend.parser.JavaliParser; import cd.frontend.parser.JavaliParser;
@ -8,9 +8,13 @@ import cd.frontend.parser.JavaliParser.UnitContext;
import cd.frontend.parser.ParseFailure; import cd.frontend.parser.ParseFailure;
import cd.frontend.semantic.SemanticAnalyzer; import cd.frontend.semantic.SemanticAnalyzer;
import cd.ir.Ast.ClassDecl; import cd.ir.Ast.ClassDecl;
import cd.ir.Symbol; import cd.ir.Ast.MethodDecl;
import cd.ir.Symbol.ClassSymbol;
import cd.ir.Symbol.TypeSymbol; import cd.ir.Symbol.TypeSymbol;
import cd.transform.AstOptimizer;
import cd.transform.CfgBuilder;
import cd.util.debug.AstDump; import cd.util.debug.AstDump;
import cd.util.debug.CfgDump;
import org.antlr.v4.runtime.ANTLRInputStream; import org.antlr.v4.runtime.ANTLRInputStream;
import org.antlr.v4.runtime.BailErrorStrategy; import org.antlr.v4.runtime.BailErrorStrategy;
import org.antlr.v4.runtime.CommonTokenStream; import org.antlr.v4.runtime.CommonTokenStream;
@ -20,26 +24,29 @@ import java.io.*;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
/** /**
* The main entrypoint for the compiler. Consists of a series * The main entrypoint for the compiler. Consists of a series of routines which must be invoked in
* of routines which must be invoked in order. The main() * order. The main() routine here invokes these routines, as does the unit testing code. This is not
* routine here invokes these routines, as does the unit testing * the <b>best</b> programming practice, as the series of calls to be invoked is duplicated in two
* code. This is not the <b>best</b> programming practice, as the * places in the code, but it will do for now.
* series of calls to be invoked is duplicated in two places in the */
* code, but it will do for now. */
public class Main { public class Main {
// Set to non-null to write debug info out // Set to non-null to write debug info out
public Writer debug = null; public Writer debug = null;
// Set to non-null to write dump of control flow graph // Set to non-null to write dump of control flow graph
public File cfgdumpbase; public File cfgdumpbase;
/** Symbol for the Main type */ /**
public Symbol.ClassSymbol mainType; * Symbol for the Main type
*/
/** List of all type symbols, used by code generator. */ public ClassSymbol mainType;
public List<TypeSymbol> allTypeSymbols;
/**
* List of all type symbols, used by code generator.
*/
public List<TypeSymbol> allTypeSymbols;
public void debug(String format, Object... args) { public void debug(String format, Object... args) {
if (debug != null) { if (debug != null) {
@ -53,23 +60,28 @@ public class Main {
} }
} }
} }
/** Parse command line, invoke compile() routine */ /**
* Parse command line, invoke compile() routine
*/
public static void main(String args[]) throws IOException { public static void main(String args[]) throws IOException {
Main m = new Main(); Main m = new Main();
for (String arg : args) { for (String arg : args) {
if (arg.equals("-d")) if (arg.equals("-d"))
m.debug = new OutputStreamWriter(System.err); m.debug = new OutputStreamWriter(System.err);
else { else {
if (m.debug != null)
m.cfgdumpbase = new File(arg);
FileReader fin = new FileReader(arg); FileReader fin = new FileReader(arg);
// Parse: // Parse:
List<ClassDecl> astRoots = m.parse(fin); List<ClassDecl> astRoots = m.parse(fin);
// Run the semantic check: // Run the semantic check:
m.semanticCheck(astRoots); m.semanticCheck(astRoots);
// Generate code: // Generate code:
String sFile = arg + Config.ASMEXT; String sFile = arg + Config.ASMEXT;
try (FileWriter fout = new FileWriter(sFile)) { try (FileWriter fout = new FileWriter(sFile)) {
@ -78,51 +90,63 @@ public class Main {
} }
} }
} }
/**
/** Parses an input stream into an AST * Parses an input stream into an AST
* @throws IOException */ *
* @throws IOException
*/
public List<ClassDecl> parse(Reader reader) throws IOException { public List<ClassDecl> parse(Reader reader) throws IOException {
List<ClassDecl> result = new ArrayList<ClassDecl>(); List<ClassDecl> result = new ArrayList<ClassDecl>();
try { try {
JavaliLexer lexer = new JavaliLexer(new ANTLRInputStream(reader)); JavaliLexer lexer = new JavaliLexer(new ANTLRInputStream(reader));
JavaliParser parser = new JavaliParser(new CommonTokenStream(lexer)); JavaliParser parser = new JavaliParser(new CommonTokenStream(lexer));
parser.setErrorHandler(new BailErrorStrategy()); parser.setErrorHandler(new BailErrorStrategy());
UnitContext unit = parser.unit(); UnitContext unit = parser.unit();
JavaliAstVisitor visitor = new JavaliAstVisitor(); JavaliAstVisitor visitor = new JavaliAstVisitor();
visitor.visit(unit); visitor.visit(unit);
result = visitor.classDecls; result = visitor.classDecls;
} catch (ParseCancellationException e) { } catch (ParseCancellationException e) {
ParseFailure pf = new ParseFailure(0, "?"); ParseFailure pf = new ParseFailure(0, "?");
pf.initCause(e); pf.initCause(e);
throw pf; throw pf;
} }
debug("AST Resulting From Parsing Stage:"); debug("AST Resulting From Parsing Stage:");
dumpAst(result); dumpAst(result);
return result; return result;
} }
// TODO: decide how to do/call optimization
public void semanticCheck(List<ClassDecl> astRoots) { public void semanticCheck(List<ClassDecl> astRoots) {
{ new SemanticAnalyzer(this).check(astRoots);
new SemanticAnalyzer(this).check(astRoots);
} // Build control flow graph:
} for (ClassDecl cd : astRoots)
for (MethodDecl md : cd.methods()) {
public void generateCode(List<ClassDecl> astRoots, Writer out) { new CfgBuilder().build(md);
{ }
AstCodeGenerator cg = AstCodeGenerator.createCodeGenerator(this, out); CfgDump.toString(astRoots, ".cfg", cfgdumpbase, false);
cg.go(astRoots);
} // Optimize
new AstOptimizer().go(astRoots, allTypeSymbols);
CfgDump.toString(astRoots, ".cfgOPT", cfgdumpbase, false);
} }
/** Dumps the AST to the debug stream */ public void generateCode(List<ClassDecl> astRoots, Writer out) {
CfgCodeGenerator cg = new CfgCodeGenerator(this, out);
cg.go(astRoots);
}
/**
* Dumps the AST to the debug stream
*/
private void dumpAst(List<ClassDecl> astRoots) throws IOException { private void dumpAst(List<ClassDecl> astRoots) throws IOException {
if (this.debug == null) return; if (this.debug == null)
return;
this.debug.write(AstDump.toString(astRoots)); this.debug.write(AstDump.toString(astRoots));
} }
} }

View file

@ -1,14 +0,0 @@
package cd;
/** TAs insert this to mark code that students need to write */
public class ToDoException extends RuntimeException {
private static final long serialVersionUID = 4054810321239901944L;
public ToDoException() {
}
public ToDoException(String message) {
super(message);
}
}

View file

@ -10,10 +10,10 @@ public enum ExitCode {
INFINITE_LOOP(6), INFINITE_LOOP(6),
DIVISION_BY_ZERO(7), DIVISION_BY_ZERO(7),
INTERNAL_ERROR(22); INTERNAL_ERROR(22);
public final int value; public final int value;
private ExitCode(int value) { private ExitCode(int value) {
this.value = value; this.value = value;
} }
} }

View file

@ -15,25 +15,33 @@ public class AssemblyEmitter {
this.out = out; this.out = out;
} }
/** Creates an constant operand. */ /**
* Creates an constant operand.
*/
static String constant(int i) { static String constant(int i) {
return "$" + i; return "$" + i;
} }
/** Creates an constant operand with the address of a label. */ /**
* Creates an constant operand with the address of a label.
*/
static String labelAddress(String lbl) { static String labelAddress(String lbl) {
return "$" + lbl; return "$" + lbl;
} }
/** Creates an operand relative to another operand. */ /**
* Creates an operand relative to another operand.
*/
static String registerOffset(int offset, Register reg) { static String registerOffset(int offset, Register reg) {
return String.format("%d(%s)", offset, reg); return String.format("%d(%s)", offset, reg);
} }
/** Creates an operand addressing an item in an array */ /**
* Creates an operand addressing an item in an array
*/
static String arrayAddress(Register arrReg, Register idxReg) { static String arrayAddress(Register arrReg, Register idxReg) {
final int offset = Config.SIZEOF_PTR * 2; // one word each in front for final int offset = Config.SIZEOF_PTR * 2; // one word each in front for
// vptr and length // vptr and length
final int mul = Config.SIZEOF_PTR; // assume all arrays of 4-byte elem final int mul = Config.SIZEOF_PTR; // assume all arrays of 4-byte elem
return String.format("%d(%s,%s,%d)", offset, arrReg, idxReg, mul); return String.format("%d(%s,%s,%d)", offset, arrReg, idxReg, mul);
} }
@ -90,10 +98,6 @@ public class AssemblyEmitter {
emit(op, constant(src), dest); emit(op, constant(src), dest);
} }
void emit(String op, int src, String dest) {
emit(op, constant(src), dest);
}
void emit(String op, String dest) { void emit(String op, String dest) {
emitRaw(op + " " + dest); emitRaw(op + " " + dest);
} }
@ -106,10 +110,6 @@ public class AssemblyEmitter {
emit(op, constant(dest)); emit(op, constant(dest));
} }
void emit(String op, int src, int dest) {
emit(op, constant(src), constant(dest));
}
void emitMove(Register src, String dest) { void emitMove(Register src, String dest) {
emitMove(src.repr, dest); emitMove(src.repr, dest);
} }
@ -127,10 +127,6 @@ public class AssemblyEmitter {
emit("movl", src, dest); emit("movl", src, dest);
} }
void emitMove(int src, Register dest) {
emitMove(constant(src), dest);
}
void emitLoad(int srcOffset, Register src, Register dest) { void emitLoad(int srcOffset, Register src, Register dest) {
emitMove(registerOffset(srcOffset, src), dest.repr); emitMove(registerOffset(srcOffset, src), dest.repr);
} }
@ -143,10 +139,6 @@ public class AssemblyEmitter {
emitMove(src, registerOffset(destOffset, dest)); emitMove(src, registerOffset(destOffset, dest));
} }
void emitStore(int src, int destOffset, Register dest) {
emitStore(constant(src), destOffset, dest);
}
void emitConstantData(String data) { void emitConstantData(String data) {
emitRaw(String.format("%s %s", Config.DOT_INT, data)); emitRaw(String.format("%s %s", Config.DOT_INT, data));
} }

View file

@ -2,13 +2,14 @@ package cd.backend.codegen;
public class AssemblyFailedException extends RuntimeException { public class AssemblyFailedException extends RuntimeException {
private static final long serialVersionUID = -5658502514441032016L; private static final long serialVersionUID = -5658502514441032016L;
public final String assemblerOutput; public final String assemblerOutput;
public AssemblyFailedException( public AssemblyFailedException(
String assemblerOutput) { String assemblerOutput) {
super("Executing assembler failed.\n" super("Executing assembler failed.\n"
+ "Output:\n" + "Output:\n"
+ assemblerOutput); + assemblerOutput);
this.assemblerOutput = assemblerOutput; this.assemblerOutput = assemblerOutput;
} }
} }

View file

@ -2,43 +2,42 @@ package cd.backend.codegen;
import cd.Config; import cd.Config;
import cd.Main; import cd.Main;
import cd.backend.ExitCode;
import cd.backend.codegen.RegisterManager.Register; import cd.backend.codegen.RegisterManager.Register;
import cd.ir.Ast; import cd.ir.Ast;
import cd.ir.Ast.ClassDecl; import cd.ir.Ast.ClassDecl;
import cd.ir.Ast.Expr;
import cd.ir.Ast.MethodDecl;
import cd.ir.Symbol.*; import cd.ir.Symbol.*;
import java.io.Writer; import java.io.Writer;
import java.util.*; import java.util.Collections;
import java.util.List;
import static cd.Config.MAIN; import static cd.Config.*;
import static cd.backend.codegen.AssemblyEmitter.constant;
import static cd.backend.codegen.AssemblyEmitter.registerOffset;
import static cd.backend.codegen.RegisterManager.BASE_REG; import static cd.backend.codegen.RegisterManager.BASE_REG;
import static cd.backend.codegen.RegisterManager.STACK_REG; import static cd.backend.codegen.RegisterManager.STACK_REG;
public class AstCodeGenerator { public class AstCodeGenerator {
/** Constant representing the boolean TRUE as integer */
static final int TRUE = 1;
/** Constant representing the boolean FALSE as integer */
static final int FALSE = 0;
/** Size of any variable in assembly
* Primitive variables take up 4 bytes (booleans are integers)
* Reference variables are a 4 byte pointer
*/
static final int VAR_SIZE = 4;
RegsNeededVisitor rnv; protected RegsNeededVisitor rnv;
ExprGenerator eg; protected ExprGenerator eg;
StmtGenerator sg; protected StmtGenerator sg;
protected final Main main; protected final Main main;
final AssemblyEmitter emit; protected final AssemblyEmitter emit;
final RegisterManager rm = new RegisterManager(); protected final RegisterManager rm = new RegisterManager();
protected ExprGeneratorRef egRef;
protected StmtGeneratorRef sgRef;
AstCodeGenerator(Main main, Writer out) { AstCodeGenerator(Main main, Writer out) {
initMethodData(); initMethodData();
main.allTypeSymbols = new ArrayList<>();
this.emit = new AssemblyEmitter(out); this.emit = new AssemblyEmitter(out);
this.main = main; this.main = main;
this.rnv = new RegsNeededVisitor(); this.rnv = new RegsNeededVisitor();
@ -52,14 +51,15 @@ public class AstCodeGenerator {
} }
public static AstCodeGenerator createCodeGenerator(Main main, Writer out) { public static AstCodeGenerator createCodeGenerator(Main main, Writer out) {
return new AstCodeGenerator(main, out); return new AstCodeGeneratorRef(main, out);
} }
/** /**
* Main method. Causes us to emit x86 assembly corresponding to {@code ast} * Main method. Causes us to emit x86 assembly corresponding to {@code ast}
* into {@code file}. Throws a {@link RuntimeException} should any I/O error * into {@code file}. Throws a {@link RuntimeException} should any I/O error
* occur. * occur.
* *
* <p> * <p>
* The generated file will be divided into two sections: * The generated file will be divided into two sections:
* <ol> * <ol>
@ -70,103 +70,693 @@ public class AstCodeGenerator {
* </ol> * </ol>
*/ */
public void go(List<? extends ClassDecl> astRoots) { public void go(List<? extends ClassDecl> astRoots) {
// Find main type and assign to main.mainType
// Add array types to the types list to lookup later
for (ClassDecl decl : astRoots) {
if (decl.name.equals("Main")) {
main.mainType = decl.sym;
}
main.allTypeSymbols.add(new ArrayTypeSymbol(decl.sym));
}
main.allTypeSymbols.add(new ArrayTypeSymbol(PrimitiveTypeSymbol.intType));
main.allTypeSymbols.add(new ArrayTypeSymbol(PrimitiveTypeSymbol.booleanType));
main.allTypeSymbols.add(new ArrayTypeSymbol(ClassSymbol.objectType));
emitPrefix();
for (ClassDecl ast : astRoots) { for (ClassDecl ast : astRoots) {
sg.gen(ast); sg.gen(ast);
} }
} }
private void emitPrefix() {
// Emit some useful string constants (copied from old HW1 method declaration) protected void initMethodData() {
rm.initRegisters();
}
protected void emitMethodSuffix(boolean returnNull) {
if (returnNull)
emit.emit("movl", "$0", Register.EAX);
emit.emitMove(BASE_REG, STACK_REG);
emit.emit("pop", BASE_REG);
emit.emitRaw("ret");
}
}
class AstCodeGeneratorRef extends AstCodeGenerator {
/**
* The address of the this ptr relative to the BP. Note that the this ptr is
* always the first argument. Default offset value is 8 but this can change
* depending on the number of parameters pushed on the stack.
*/
protected int THIS_OFFSET = 8;
/**
* Name of the internal Javali$CheckCast() helper function we generate.
*/
static final String CHECK_CAST = "Javali$CheckCast";
/**
* Name of the internal Javali$CheckNull() helper function we generate.
*/
static final String CHECK_NULL = "Javali$CheckNull";
/**
* Name of the internal Javali$CheckNonZero() helper function we generate.
*/
static final String CHECK_NON_ZERO = "Javali$CheckNonZero";
/**
* Name of the internal Javali$CheckArraySize() helper function we generate.
*/
static final String CHECK_ARRAY_SIZE = "Javali$CheckArraySize";
/**
* Name of the internal Javali$CheckArrayBounds() helper function we
* generate.
*/
static final String CHECK_ARRAY_BOUNDS = "Javali$CheckArrayBounds";
/**
* Name of the internal Javali$Alloc() helper function we generate.
*/
static final String ALLOC = "Javali$Alloc";
/**
* Name of the internal Javali$PrintNewLine() helper function we generate.
*/
static final String PRINT_NEW_LINE = "Javali$PrintNewLine";
/**
* Name of the internal Javali$PrintInteger() helper function we generate.
*/
static final String PRINT_INTEGER = "Javali$PrintInteger";
/**
* Name of the internal Javali$ReadInteger() helper function we generate.
*/
static final String READ_INTEGER = "Javali$ReadInteger";
public AstCodeGeneratorRef(Main main, Writer out) {
super(main, out);
this.egRef = new ExprGeneratorRef(this);
this.eg = this.egRef;
this.sgRef = new StmtGeneratorRef(this);
this.sg = this.sgRef;
}
protected void emitPrefix(List<? extends ClassDecl> astRoots) {
// compute method and field offsets
for (ClassDecl ast : astRoots) {
computeFieldOffsets(ast.sym);
computeVtableOffsets(ast.sym);
}
// emit vtables
for (TypeSymbol ts : main.allTypeSymbols)
emitVtable(ts);
// Emit some useful string constants and static data:
emit.emitRaw(Config.DATA_STR_SECTION); emit.emitRaw(Config.DATA_STR_SECTION);
emit.emitLabel("STR_NL"); emit.emitLabel("STR_NL");
emit.emitRaw(Config.DOT_STRING + " \"\\n\""); emit.emitRaw(Config.DOT_STRING + " \"\\n\"");
emit.emitLabel("STR_D"); emit.emitLabel("STR_D");
emit.emitRaw(Config.DOT_STRING + " \"%d\""); emit.emitRaw(Config.DOT_STRING + " \"%d\"");
emit.emitLabel("STR_F");
// Define Object, Object[], int, int[], boolean and boolean[] emit.emitRaw(Config.DOT_STRING + " \"%.5f\"");
List<TypeSymbol> elementTypes = new ArrayList<>(); emit.emitLabel("SCANF_STR_F");
elementTypes.add(ClassSymbol.objectType); emit.emitRaw(Config.DOT_STRING + " \"%f\"");
elementTypes.add(PrimitiveTypeSymbol.intType);
elementTypes.add(PrimitiveTypeSymbol.booleanType);
emit.emitRaw(Config.DATA_INT_SECTION); emit.emitRaw(Config.DATA_INT_SECTION);
for (TypeSymbol type : elementTypes) {
// type vtable emit.emitRaw(Config.TEXT_SECTION);
emit.emitLabel(Label.type(type));
emit.emitConstantData("0"); // Supertype (null) // Generate a helper method for checking casts:
// array vtable // It takes first a vtable and second an object ptr.
emit.emitLabel(Label.type(new ArrayTypeSymbol(type))); {
emit.emitConstantData(Label.type(ClassSymbol.objectType)); // Supertype Register obj = RegisterManager.CALLER_SAVE[0];
emit.emitConstantData(Label.type(type)); // Element type Register cls = RegisterManager.CALLER_SAVE[1];
String looplbl = emit.uniqueLabel();
String donelbl = emit.uniqueLabel();
String faillbl = emit.uniqueLabel();
emit.emitCommentSection(CHECK_CAST + " function");
emit.emitLabel(CHECK_CAST);
emit.emit("push", BASE_REG);
emit.emitMove(STACK_REG, BASE_REG);
emit.emit("sub", constant(8), STACK_REG);
emit.emit("and", constant(-16), STACK_REG);
emit.emit("sub", constant(16), STACK_REG);
emit.emitLoad(SIZEOF_PTR * 2, BASE_REG, cls);
emit.emitLoad(SIZEOF_PTR * 3, BASE_REG, obj);
emit.emit("cmpl", constant(0), obj);
emit.emit("je", donelbl); // allow null objects to pass
emit.emitLoad(0, obj, obj); // load vtbl of object
emit.emitLabel(looplbl);
emit.emit("cmpl", obj, cls);
emit.emit("je", donelbl);
emit.emit("cmpl", constant(0), obj);
emit.emit("je", faillbl);
emit.emitLoad(0, obj, obj); // load parent vtable
emit.emit("jmp", looplbl);
emit.emitLabel(faillbl);
emit.emitStore(constant(ExitCode.INVALID_DOWNCAST.value), 0, STACK_REG);
emit.emit("call", Config.EXIT);
emit.emitLabel(donelbl);
emit.emitMove(BASE_REG, STACK_REG);
emit.emit("pop", BASE_REG);
emit.emitRaw("ret");
} }
// Emit the new Main().main() code to start the program: // Generate a helper method for checking for null ptrs:
{
String oknulllbl = emit.uniqueLabel();
emit.emitCommentSection(CHECK_NULL + " function");
emit.emitLabel(CHECK_NULL);
// 1. Enter TEXT and start the program emit.emit("push", BASE_REG);
emit.emitRaw(Config.TEXT_SECTION); emit.emitMove(STACK_REG, BASE_REG);
emit.emit(".globl", MAIN); emit.emit("sub", constant(8), STACK_REG);
emit.emitLabel(MAIN);
// 1.1. Prepare first frame
emit.emit("push", BASE_REG);
emit.emitMove(STACK_REG, BASE_REG);
// 2. Create main variable emit.emit("and", constant(-16), STACK_REG);
emit.emit("sub", constant(16), STACK_REG);
emit.emit("cmpl", constant(0), registerOffset(SIZEOF_PTR * 2, BASE_REG));
emit.emit("jne", oknulllbl);
emit.emitStore(constant(ExitCode.NULL_POINTER.value), 0, STACK_REG);
emit.emit("call", Config.EXIT);
emit.emitLabel(oknulllbl);
emit.emitMove(BASE_REG, STACK_REG);
emit.emit("pop", BASE_REG);
emit.emitRaw("ret");
}
// Generate a helper method for checking that we don't divide by zero:
{
String oknzlbl = emit.uniqueLabel();
emit.emitCommentSection(CHECK_NON_ZERO + " function");
emit.emitLabel(CHECK_NON_ZERO);
emit.emit("push", BASE_REG);
emit.emitMove(STACK_REG, BASE_REG);
emit.emit("sub", constant(8), STACK_REG);
emit.emit("and", constant(-16), STACK_REG);
emit.emit("sub", constant(16), STACK_REG);
emit.emit("cmpl", constant(0), registerOffset(SIZEOF_PTR * 2, BASE_REG));
emit.emit("jne", oknzlbl);
emit.emitStore(constant(ExitCode.DIVISION_BY_ZERO.value), 0, STACK_REG);
emit.emit("call", Config.EXIT);
emit.emitLabel(oknzlbl);
emit.emitMove(BASE_REG, STACK_REG);
emit.emit("pop", BASE_REG);
emit.emitRaw("ret");
}
// Generate a helper method for checking array size:
{
String okunqlbl = emit.uniqueLabel();
emit.emitCommentSection(CHECK_ARRAY_SIZE + " function");
emit.emitLabel(CHECK_ARRAY_SIZE);
emit.emit("push", BASE_REG);
emit.emitMove(STACK_REG, BASE_REG);
emit.emit("sub", constant(8), STACK_REG);
emit.emit("and", constant(-16), STACK_REG);
emit.emit("sub", constant(16), STACK_REG);
emit.emit("cmpl", constant(0), registerOffset(SIZEOF_PTR * 2, BASE_REG));
emit.emit("jge", okunqlbl);
emit.emitStore(constant(ExitCode.INVALID_ARRAY_SIZE.value), 0, STACK_REG);
emit.emit("call", Config.EXIT);
emit.emitLabel(okunqlbl);
emit.emitMove(BASE_REG, STACK_REG);
emit.emit("pop", BASE_REG);
emit.emitRaw("ret");
}
// Generate a helper method for checking array bounds:
{
Register arr = RegisterManager.CALLER_SAVE[0];
Register idx = RegisterManager.CALLER_SAVE[1];
String faillbl = emit.uniqueLabel();
emit.emitCommentSection(CHECK_ARRAY_BOUNDS + " function");
emit.emitLabel(CHECK_ARRAY_BOUNDS);
emit.emit("push", BASE_REG);
emit.emitMove(STACK_REG, BASE_REG);
emit.emit("sub", constant(8), STACK_REG);
emit.emit("and", constant(-16), STACK_REG);
emit.emit("sub", constant(16), STACK_REG);
emit.emitLoad(SIZEOF_PTR * 3, BASE_REG, idx);
emit.emitLoad(SIZEOF_PTR * 2, BASE_REG, arr);
emit.emit("cmpl", constant(0), idx); // idx < 0
emit.emit("jl", faillbl);
emit.emit("cmpl", registerOffset(Config.SIZEOF_PTR, arr), idx); // idx >= len
emit.emit("jge", faillbl);
// done
emit.emitMove(BASE_REG, STACK_REG);
emit.emit("pop", BASE_REG);
emit.emitRaw("ret");
// fail
emit.emitLabel(faillbl);
emit.emitStore(constant(ExitCode.INVALID_ARRAY_BOUNDS.value), 0, STACK_REG);
emit.emit("call", Config.EXIT);
}
// Generate a helper method for allocating objects/arrays
{
Register size = RegisterManager.CALLER_SAVE[0];
emit.emitCommentSection(ALLOC + " function");
emit.emitLabel(ALLOC);
emit.emit("push", BASE_REG);
emit.emitMove(STACK_REG, BASE_REG);
emit.emit("sub", constant(8), STACK_REG);
emit.emit("and", constant(-16), STACK_REG);
emit.emit("sub", constant(16), STACK_REG);
emit.emitLoad(8, BASE_REG, size);
emit.emitStore(size, 0, STACK_REG);
emit.emitStore(constant(1), 4, STACK_REG);
emit.emit("call", Config.CALLOC);
emit.emitMove(BASE_REG, STACK_REG);
emit.emit("pop", BASE_REG);
emit.emitRaw("ret");
}
// Generate a helper method for printing a new line
{
emit.emitCommentSection(PRINT_NEW_LINE + " function");
emit.emitLabel(PRINT_NEW_LINE);
emit.emit("push", BASE_REG);
emit.emitMove(STACK_REG, BASE_REG);
emit.emit("sub", constant(8), STACK_REG);
emit.emit("and", constant(-16), STACK_REG);
emit.emit("sub", constant(16), STACK_REG);
emit.emitStore("$STR_NL", 0, STACK_REG);
emit.emit("call", Config.PRINTF);
emit.emitMove(BASE_REG, STACK_REG);
emit.emit("pop", BASE_REG);
emit.emitRaw("ret");
}
// Generate a helper method for printing an integer
{
Register temp = RegisterManager.CALLER_SAVE[0];
emit.emitCommentSection(PRINT_INTEGER + " function");
emit.emitLabel(PRINT_INTEGER);
emit.emit("push", BASE_REG);
emit.emitMove(STACK_REG, BASE_REG);
emit.emit("sub", constant(8), STACK_REG);
emit.emit("and", constant(-16), STACK_REG);
emit.emit("sub", constant(16), STACK_REG);
emit.emitLoad(8, BASE_REG, temp);
emit.emitStore(temp, 4, STACK_REG);
emit.emitStore("$STR_D", 0, STACK_REG);
emit.emit("call", Config.PRINTF);
emit.emitMove(BASE_REG, STACK_REG);
emit.emit("pop", BASE_REG);
emit.emitRaw("ret");
}
// Generate a helper method for reading an integer
{
Register number = RegisterManager.CALLER_SAVE[0];
emit.emitCommentSection(READ_INTEGER + " function");
emit.emitLabel(READ_INTEGER);
emit.emit("push", BASE_REG);
emit.emitMove(STACK_REG, BASE_REG);
emit.emit("sub", constant(8), STACK_REG);
emit.emit("and", constant(-16), STACK_REG);
emit.emit("sub", constant(16), STACK_REG);
emit.emit("leal", registerOffset(8, STACK_REG), number);
emit.emitStore(number, 4, STACK_REG);
emit.emitStore("$STR_D", 0, STACK_REG);
emit.emit("call", SCANF);
emit.emitLoad(8, STACK_REG, Register.EAX);
emit.emitMove(BASE_REG, STACK_REG);
emit.emit("pop", BASE_REG);
emit.emitRaw("ret");
}
// Generate AST for main() method:
// new Main().main()
Ast.NewObject newMain = new Ast.NewObject("Main"); Ast.NewObject newMain = new Ast.NewObject("Main");
newMain.type = main.mainType; newMain.type = main.mainType;
Register mainLocation = eg.visit(newMain, null);
// 3. Call main() Ast.MethodCallExpr mce = new Ast.MethodCallExpr(newMain, "main", Collections.<Expr>emptyList());
emit.emit("push", mainLocation); Ast.MethodCall callMain = new Ast.MethodCall(mce);
for (ClassSymbol sym = main.mainType; sym != ClassSymbol.objectType; sym = sym.superClass) mce.sym = main.mainType.getMethod("main");
if (sym.methods.getOrDefault("main", null) != null) {
emit.emit("call", Label.method(sym, sym.methods.get("main"))); // Emit the main() method:
break; // new Main().main();
} emit.emitCommentSection("main() function");
emitMethodSuffix(true); emit.emitRaw(".globl " + MAIN);
emit.emitLabel(MAIN);
emit.emit("push", BASE_REG);
emit.emitMove(STACK_REG, BASE_REG);
emit.emit("sub", constant(8), STACK_REG);
emit.emit("and", -16, STACK_REG);
sg.gen(callMain);
emit.emit("movl", constant(ExitCode.OK.value), Register.EAX); // normal termination:
emit.emitMove(BASE_REG, STACK_REG);
emit.emit("pop", BASE_REG);
emit.emitRaw("ret");
} }
void initMethodData() { @Override
rm.initRegisters(); public void go(List<? extends ClassDecl> astRoots) {
emitPrefix(astRoots);
super.go(astRoots);
}
/**
* Computes the vtable offset for each method defined in the class
* {@code sym}.
*/
protected int computeVtableOffsets(ClassSymbol sym) {
if (sym == null)
return 0;
if (sym.totalMethods != -1)
return sym.totalMethods;
int index = computeVtableOffsets(sym.superClass);
for (MethodSymbol ms : sym.methods.values()) {
assert ms.vtableIndex == -1;
if (ms.overrides != null)
ms.vtableIndex = ms.overrides.vtableIndex;
else
ms.vtableIndex = index++;
}
sym.totalMethods = index;
return index;
}
/**
* Computes the offset for each field.
*/
protected int computeFieldOffsets(ClassSymbol sym) {
if (sym == null)
return 0;
if (sym.totalFields != -1)
return sym.totalFields;
int index = computeFieldOffsets(sym.superClass);
for (VariableSymbol fs : sym.fields.values()) {
assert fs.offset == -1;
// compute offset in bytes; note that 0 is the vtable
fs.offset = (index * SIZEOF_PTR) + SIZEOF_PTR;
index++;
}
sym.totalFields = index;
sym.sizeof = (sym.totalFields + 1) * Config.SIZEOF_PTR;
return index;
}
private void collectVtable(MethodSymbol[] vtable, ClassSymbol sym) {
if (sym.superClass != null)
collectVtable(vtable, sym.superClass);
for (MethodSymbol ms : sym.methods.values())
vtable[ms.vtableIndex] = ms;
}
protected void emitVtable(TypeSymbol ts) {
if (ts instanceof ClassSymbol) {
ClassSymbol cs = (ClassSymbol) ts;
// Collect the vtable:
MethodSymbol[] vtable = new MethodSymbol[cs.totalMethods];
collectVtable(vtable, cs);
// Emit vtable for this class:
emit.emitLabel(vtable(cs));
if (cs.superClass != null)
emit.emitConstantData(vtable(cs.superClass));
else
emit.emitConstantData("0");
for (int i = 0; i < cs.totalMethods; i++)
emit.emitConstantData(methodLabel(vtable[i]));
} else if (ts instanceof ArrayTypeSymbol) {
ArrayTypeSymbol as = (ArrayTypeSymbol) ts;
emit.emitLabel(vtable(as));
emit.emitConstantData(vtable(ClassSymbol.objectType));
}
}
protected String vtable(TypeSymbol ts) {
if (ts instanceof ClassSymbol) {
return "vtable_" + ((ClassSymbol) ts).name;
} else if (ts instanceof ArrayTypeSymbol) {
return "vtablearr_" + ((ArrayTypeSymbol) ts).elementType.name;
} else {
throw new RuntimeException("No vtable for " + ts.name);
}
} }
void emitMethodSuffix(boolean returnNull) { @Override
protected void initMethodData() {
THIS_OFFSET = 8;
bytes = 0;
super.initMethodData();
}
protected int padding(int numberOfParameters) {
int padding = (bytes + numberOfParameters * Config.SIZEOF_PTR + 15) & 0xFFFFFFF0;
return padding - bytes - numberOfParameters * Config.SIZEOF_PTR;
}
protected void push(int padding) {
if (padding > 0) {
emit.emit("sub", padding, STACK_REG);
bytes += padding;
}
}
protected void pop(int padding) {
if (padding > 0) {
emit.emit("add", padding, STACK_REG);
bytes -= padding;
}
assert bytes >= 0;
}
protected void push(String reg) {
emit.emit("push", reg);
bytes += Config.SIZEOF_PTR;
}
protected void pop(String reg) {
emit.emit("pop", reg);
bytes -= Config.SIZEOF_PTR;
assert bytes >= 0;
}
protected void restoreCalleeSaveRegs() {
for (int reg = RegisterManager.CALLEE_SAVE.length - 1; reg >= 0; reg--) {
emit.emit("pop", RegisterManager.CALLEE_SAVE[reg]);
}
}
protected void storeCalleeSaveRegs() {
bytes = 0;
for (int reg = 0; reg < RegisterManager.CALLEE_SAVE.length; reg++) {
emit.emit("push", RegisterManager.CALLEE_SAVE[reg]);
bytes += Config.SIZEOF_PTR;
}
}
protected void restoreCallerSaveRegs(Register res) {
for (int reg = RegisterManager.CALLER_SAVE.length - 1; reg >= 0; reg--) {
if (!rm.isInUse(RegisterManager.CALLER_SAVE[reg]))
continue; // not in use
if (RegisterManager.CALLER_SAVE[reg].equals(res))
continue; // contains our result
pop(RegisterManager.CALLER_SAVE[reg].repr);
}
}
protected void storeCallerSaveRegs(Register res) {
for (int reg = 0; reg < RegisterManager.CALLER_SAVE.length; reg++) {
if (!rm.isInUse(RegisterManager.CALLER_SAVE[reg]))
continue; // not in use
if (RegisterManager.CALLER_SAVE[reg].equals(res))
continue; // will contain our result
push(RegisterManager.CALLER_SAVE[reg].repr);
}
}
protected int emitCallPrefix(Register res, int numberOfParameters) {
storeCallerSaveRegs(res);
int padding = padding(numberOfParameters);
push(padding);
return padding;
}
protected void emitCallSuffix(Register res, int numberOfParameters,
int padding) {
pop(numberOfParameters * Config.SIZEOF_PTR + padding);
if (res != null) {
emit.emitMove(Register.EAX, res);
}
restoreCallerSaveRegs(res);
}
/**
* Generates code which evaluates {@code ast} and branches to {@code lbl} if
* the value generated for {@code ast} is false.
*/
protected void genJumpIfFalse(Expr ast, String lbl) {
// A better way to implement this would be with a separate
// visitor.
Register reg = eg.gen(ast);
emit.emit("cmpl", "$0", reg);
emit.emit("je", lbl);
rm.releaseRegister(reg);
}
/**
* Used to store the temporaries. We grow our stack dynamically, we allocate
* "temporary" values on this stack during method execution. Values can be
* stored and retrieved using {@link #push(String)} and {@link #pop(String)}
* , which use the program stack.
*/
protected int bytes = 0;
protected String methodLabel(MethodSymbol msym) {
return "meth_" + msym.owner.name + "_" + msym.name;
}
protected void emitMethodPrefix(MethodDecl ast) {
// Emit the label for the method:
emit.emitRaw(Config.TEXT_SECTION);
emit.emitCommentSection(String.format("Method %s.%s", ast.sym.owner.name,
ast.name));
emit.emitRaw(".globl " + methodLabel(ast.sym));
emit.emitLabel(methodLabel(ast.sym));
// Compute the size and layout of the stack frame. Our
// frame looks like (the numbers are relative to our ebp):
//
// (caller's locals)
// (padding)
// arg 0 (this ptr)
// ...
// 12 arg N - 1
// 8 arg N
// 4 linkage ptr (return address)
// 0 saved ebp
// -4 locals
// (callee's arguments + temporaries)
//
// We allocate on the stack during the course of
// a function call using push(...) and pop(...) instructions.
//
// Stack slots fall into several
// categories:
// - "Linkage": overhead for function calls.
// This includes the return address and saved ebp.
// - locals: these store the value of user-declared local
// variables.
// - temporaries: these are stack slots used to store
// values during expression evaluation when we run out
// of registers, saving caller-saved registers, and
// other miscellaneous purposes.
// - padding: only there to ensure the stack size is a multiple
// of 16.
// - arguments: values we will pass to functions being
// invoked.
//
// We calculate all address relative to the base pointer.
// Initialize method-specific data
initMethodData();
// Assign parameter offsets:
// As shown above, these start from 8.
// Being able to evaluate parameters like in Java
// with left-to-right evaluation order they result
// on the stack in reversed order.
// The "this" parameter is the first pushed on the stack
// thus receiving higher offset.
int paramOffset = Config.SIZEOF_PTR * 2;
for (int i = ast.sym.parameters.size() - 1; i >= 0; i--) {
final VariableSymbol param = ast.sym.parameters.get(i);
assert param.offset == -1;
param.offset = paramOffset;
paramOffset += Config.SIZEOF_PTR;
}
THIS_OFFSET = paramOffset;
paramOffset += Config.SIZEOF_PTR;
// First few slots are reserved for caller save regs:
int localSlot = RegisterManager.CALLER_SAVE.length * RegisterManager.SIZEOF_REG;
// Assign local variable offsets:
emit.emitComment(String.format("%-10s Offset", "Variable"));
for (VariableSymbol local : ast.sym.locals.values()) {
assert local.offset == -1;
local.offset = -localSlot;
localSlot += Config.SIZEOF_PTR;
emit.emitComment(String.format("%-10s %d", local, local.offset));
}
// Round up stack size to make it a multiple of 16.
// The actual amount passed to the enter instruction is 8
// less, however, because it wants to know the amount
// in addition to the linkage ptr and saved ebp.
int implicit = Config.SIZEOF_PTR * 2;
int stackSize = (implicit + localSlot + 15) & 0xFFFFFFF0;
stackSize -= implicit;
emit.emitComment(String.format("implicit=%d localSlot=%d sum=%d", implicit,
localSlot, implicit + localSlot));
// emit.emitRaw(String.format("enter $%d, $0", stackSize));
emit.emit("push", BASE_REG);
emit.emitMove(STACK_REG, BASE_REG);
emit.emit("sub", constant(stackSize), STACK_REG);
emit.emit("and", -16, STACK_REG);
storeCalleeSaveRegs();
// zero-initialize locals
for (VariableSymbol local : ast.sym.locals.values()) {
emit.emitMove(constant(0), registerOffset(local.offset, BASE_REG));
}
}
@Override
protected void emitMethodSuffix(boolean returnNull) {
if (returnNull) if (returnNull)
emit.emit("movl", 0, Register.EAX); emit.emit("movl", "$0", Register.EAX);
emit.emitRaw("leave"); restoreCalleeSaveRegs();
emit.emitMove(BASE_REG, STACK_REG);
emit.emit("pop", BASE_REG);
emit.emitRaw("ret"); emit.emitRaw("ret");
} }
}
static class Label {
static String type(TypeSymbol symbol) { class AstCodeGeneratorNop90 extends AstCodeGeneratorRef {
if (symbol instanceof ClassSymbol) public AstCodeGeneratorNop90(Main main, Writer out, CfgCodeGenerator cfgCg) {
return String.format("type_%s", symbol); super(main, out);
else if (symbol instanceof ArrayTypeSymbol) this.egRef = new ExprGeneratorNop90(this, cfgCg);
return String.format("array_%s", ((ArrayTypeSymbol) symbol).elementType); this.eg = this.egRef;
else if (symbol instanceof PrimitiveTypeSymbol) this.sgRef = new StmtGeneratorNop90(this, cfgCg);
return String.format("primive_%s", symbol); this.sg = this.sgRef;
throw new RuntimeException("Unimplemented type symbol"); }
}
static String method(ClassSymbol classSymbol, MethodSymbol methodSymbol) {
return String.format("method_%s_%s", classSymbol.name, methodSymbol.name);
}
static String returnMethod(ClassSymbol classSymbol, MethodSymbol methodSymbol) {
return String.format("return_%s", method(classSymbol, methodSymbol));
}
}
} }

View file

@ -0,0 +1,130 @@
package cd.backend.codegen;
import cd.Main;
import cd.ir.Ast.ClassDecl;
import cd.ir.Ast.MethodDecl;
import cd.ir.Ast.Stmt;
import cd.ir.Ast;
import cd.ir.AstVisitor;
import cd.ir.BasicBlock;
import cd.ir.ControlFlowGraph;
import cd.ir.Symbol.PrimitiveTypeSymbol;
import cd.ir.Symbol.TypeSymbol;
import cd.ir.Symbol.VariableSymbol;
import cd.transform.analysis.ArraySizeAnalysis;
import cd.transform.analysis.AssignmentAnalysis;
import cd.transform.analysis.DynamicTypeAnalysis;
import cd.transform.analysis.MaybeC;
import cd.transform.analysis.NullAnalysis;
import java.io.Writer;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class CfgCodeGenerator {
public final Main main;
private final AstCodeGeneratorRef cg;
public final CompileTimeChecks check;
protected Map<VariableSymbol, MaybeC<Integer>> arraySizeState;
protected Map<VariableSymbol, MaybeC<Boolean>> nullState;
protected Map<VariableSymbol, MaybeC<TypeSymbol>> dynamicTypeState;
//protected Map<VariableSymbol, MaybeC<List<Ast.Assign>>> unusedAssignmentsState;
public CfgCodeGenerator(Main main, Writer out) {
this.main = main;
cg = new AstCodeGeneratorNop90(main, out, this);
check = new CompileTimeChecks(cg, this);
}
public void go(List<? extends ClassDecl> astRoots) {
cg.emitPrefix(astRoots);
for (ClassDecl cdecl : astRoots)
new CfgStmtVisitor().visit(cdecl, null);
}
private class CfgStmtVisitor extends AstVisitor<Void, Void> {
@Override
public Void classDecl(ClassDecl ast, Void arg) {
cg.emit.emitCommentSection("Class " + ast.name);
cg.emit.increaseIndent("");
super.classDecl(ast, arg);
cg.emit.decreaseIndent();
return null;
}
@Override
public Void methodDecl(MethodDecl ast, Void arg) {
cg.emitMethodPrefix(ast);
ControlFlowGraph cfg = ast.cfg;
assert cfg != null;
Map<BasicBlock, String> labels = new HashMap<BasicBlock, String>();
for (BasicBlock blk : cfg.allBlocks)
labels.put(blk, cg.emit.uniqueLabel());
String exitLabel = cg.emit.uniqueLabel();
cg.emit.emit("jmp", labels.get(cfg.start));
// Run analysis here to have the relevant results to this cfg object
ArraySizeAnalysis arraySizeAnalysis = new ArraySizeAnalysis(cfg);
NullAnalysis nullAnalysis = new NullAnalysis(cfg);
DynamicTypeAnalysis dynamicTypeAnalysis = new DynamicTypeAnalysis(cfg);
//AssignmentAnalysis assignmentAnalysis = new AssignmentAnalysis(cfg);
for (BasicBlock blk : cfg.allBlocks) {
arraySizeState = arraySizeAnalysis.inStateOf(blk);
nullState = nullAnalysis.inStateOf(blk);
dynamicTypeState = dynamicTypeAnalysis.inStateOf(blk);
//unusedAssignmentsState = assignmentAnalysis.inStateOf(blk);
assert nullState != null;
assert arraySizeState != null;
assert dynamicTypeState != null;
//assert unusedAssignmentsState != null;
cg.emit.emitCommentSection("Basic block " + blk.index);
cg.emit.emitLabel(labels.get(blk));
for (Stmt stmt : blk.stmts) {
cg.sg.gen(stmt);
//assignmentAnalysis.transferStmt(stmt, unusedAssignmentsState);
arraySizeAnalysis.transferStmt(stmt, arraySizeState);
nullAnalysis.transferStmt(stmt, nullState);
dynamicTypeAnalysis.transferStmt(stmt, dynamicTypeState);
}
if (blk == cfg.end) {
cg.emit.emitComment(String.format("Return"));
assert blk.successors.size() == 0;
cg.emit.emit("jmp", exitLabel);
} else if (blk.condition != null) {
assert blk.successors.size() == 2;
cg.emit.emitComment(String.format(
"Exit to block %d if true, block %d if false",
blk.trueSuccessor().index, blk.falseSuccessor().index));
cg.genJumpIfFalse(blk.condition, labels.get(blk.falseSuccessor()));
cg.emit.emit("jmp", labels.get(blk.trueSuccessor()));
} else {
cg.emit.emitComment(String.format(
"Exit to block %d", blk.successors.get(0).index));
assert blk.successors.size() == 1;
cg.emit.emit("jmp", labels.get(blk.successors.get(0)));
}
}
cg.emit.emitLabel(exitLabel);
if (ast.sym.returnType.equals(PrimitiveTypeSymbol.voidType))
cg.emitMethodSuffix(true);
else
cg.emitMethodSuffix(true);
return null;
}
}
}

View file

@ -0,0 +1,106 @@
package cd.backend.codegen;
import cd.Config;
import cd.backend.ExitCode;
import cd.ir.Ast;
import cd.ir.Ast.*;
import cd.ir.CompileTimeEvaluator;
import cd.ir.Symbol.VariableSymbol;
import cd.transform.analysis.MaybeC;
import java.util.Optional;
import static cd.backend.codegen.AssemblyEmitter.constant;
import static cd.backend.codegen.RegisterManager.STACK_REG;
/**
* Class with simple static checks mimicking the runtime system with the functions to check
* possible errors at runtime. The purpose is to skip unnecessary calls to those functions
*/
public class CompileTimeChecks {
protected final CfgCodeGenerator cfgCg;
protected final AstCodeGeneratorRef cgRef;
protected CompileTimeEvaluator cte;
public CompileTimeChecks(AstCodeGeneratorRef cg, CfgCodeGenerator cfgCodeGenerator) {
this.cfgCg = cfgCodeGenerator;
this.cgRef = cg;
cte = new CompileTimeEvaluator();
}
/**
* Convenience method that extracts the VariableSymbol from a Var or Field node.
* <br/> If the node is not one of those, the node must be a Index
*/
private VariableSymbol symbolOf(Expr expr) {
if (expr instanceof Var)
return ((Var) expr).sym;
if (expr instanceof Field)
return ((Field) expr).sym;
// assert expr instanceof Index // only for local checks
return null;
}
/** Returns whether a NewArray expression needs to check dynamically for the
* validity of the index (>=0). In case that it is known but invalid, a call
* to exit is done with the appropriate error code.
*/
public boolean checkArraySize(Ast.NewArray expr) {
Optional<Integer> arraySize = cte.calc(expr.arg());
if (!arraySize.isPresent()) {
return true;
}
int arraySizeValue = arraySize.get();
if (arraySizeValue < 0) {
cgRef.emit.emitStore(constant(ExitCode.INVALID_ARRAY_SIZE.value), 0, STACK_REG);
cgRef.emit.emit("call", Config.EXIT);
return false;
}
return false;
}
/** Returns whether a run-time check for the bounds of an array index is necessary.
* <br/> A check is only necessary if the index expression cannot be evaluated in
* compile time or the size of the array is unknown.
* <br/> If the value is known and it is invalid, an unconditional call to exit is
* performed with the appropriate exit code
*/
public boolean checkArrayBound(Index expr) {
Optional<Integer> index = cte.calc(expr.right());
if (!index.isPresent()) {
return true;
}
VariableSymbol symbol = symbolOf(expr.left());
if (!cfgCg.arraySizeState.containsKey(symbol)) {
return true;
}
int indexValue = index.get();
MaybeC<Integer> bound = cfgCg.arraySizeState.get(symbol);
if (bound.isNotConstant()) {
return true;
}
int boundValue = bound.getValue();
if (indexValue < 0 || indexValue > (boundValue - 1)) {
cgRef.emit.emitStore(constant(ExitCode.INVALID_ARRAY_BOUNDS.value), 0, STACK_REG);
cgRef.emit.emit("call", Config.EXIT);
}
return false;
}
/**
* Returns true only when it is impossible for the expression (Field or Var) to be null.
* <br/> Currently doesn't check indexes.
*/
public boolean isNotNull(Expr expr) {
if (expr instanceof ThisRef) {
return false;
} else {
VariableSymbol symbol = symbolOf(expr);
MaybeC<Boolean> isNull = cfgCg.nullState.get(symbol);
return isNull != null && isNull.isConstant() && !isNull.getValue();
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -1,19 +0,0 @@
package cd.backend.codegen;
import cd.backend.ExitCode;
import cd.backend.codegen.RegisterManager.Register;
public class Interrupts {
protected static final int INTERRUPT_EXIT = 1;
/**
* Generates a exit interrupt with the code provided
* @param cg AstCodeGenerator to print instructions
* @param exitCode Number to use as exit code (can use constants in this class)
*/
protected static void exit(AstCodeGenerator cg, ExitCode exitCode) {
cg.emit.emitMove(AssemblyEmitter.constant(INTERRUPT_EXIT), Register.EAX);
cg.emit.emitMove(AssemblyEmitter.constant(exitCode.value), Register.EBX);
cg.emit.emit("int", 0x80);
}
}

View file

@ -1,42 +0,0 @@
package cd.backend.codegen;
import cd.ir.Symbol.ClassSymbol;
import cd.ir.Symbol.MethodSymbol;
public class Location {
private ClassSymbol classSymbol;
private MethodSymbol methodSymbol = null;
private boolean obtainReference = false;
public Location (ClassSymbol sym) {
classSymbol = sym;
}
public ClassSymbol classSym() {
return classSymbol;
}
public MethodSymbol methodSym() {
assert methodSymbol != null;
return methodSymbol;
}
public void enterMethod(MethodSymbol sym) {
methodSymbol = sym;
}
public void leaveMethod() {
methodSymbol = null;
}
public boolean isObtainReference() {
boolean aux = obtainReference;
obtainReference = false;
return aux;
}
public Location obtainReference() {
obtainReference = true;
return this;
}
}

View file

@ -16,10 +16,10 @@ public class RegisterManager {
Register.EDI, Register.EBX}; Register.EDI, Register.EBX};
public static final Register CALLER_SAVE[] = new Register[]{Register.EAX, public static final Register CALLER_SAVE[] = new Register[]{Register.EAX,
Register.ECX, Register.EDX}; Register.ECX, Register.EDX};
// list of general purpose registers // list of general purpose registers
public static final Register GPR[] = new Register[]{Register.EAX, Register.EBX, public static final Register GPR[] = new Register[]{Register.EAX, Register.EBX,
Register.ECX, Register.EDX, Register.ESI, Register.EDI}; Register.ECX, Register.EDX, Register.ESI, Register.EDI};
// special purpose registers // special purpose registers
public static final Register BASE_REG = Register.EBP; public static final Register BASE_REG = Register.EBP;
@ -27,7 +27,7 @@ public class RegisterManager {
public static final int SIZEOF_REG = 4; public static final int SIZEOF_REG = 4;
public enum Register { public enum Register {
EAX("%eax", ByteRegister.EAX), EBX("%ebx", ByteRegister.EBX), ECX( EAX("%eax", ByteRegister.EAX), EBX("%ebx", ByteRegister.EBX), ECX(
"%ecx", ByteRegister.ECX), EDX("%edx", ByteRegister.EDX), ESI( "%ecx", ByteRegister.ECX), EDX("%edx", ByteRegister.EDX), ESI(
@ -99,18 +99,11 @@ public class RegisterManager {
return registers.remove(last); return registers.remove(last);
} }
public void useRegister(Register reg) {
assert registers.contains(reg);
assert reg != null;
registers.remove(reg);
}
/** /**
* marks a currently used register as free * marks a currently used register as free
*/ */
public void releaseRegister(Register reg) { public void releaseRegister(Register reg) {
assert !registers.contains(reg); assert !registers.contains(reg);
assert reg != null;
registers.add(reg); registers.add(reg);
} }

View file

@ -10,18 +10,19 @@ import java.util.Map;
import static java.lang.Math.max; import static java.lang.Math.max;
import static java.lang.Math.min; import static java.lang.Math.min;
/** /**
* Determines the maximum number of registers * Determines the maximum number of registers
* required to execute one subtree. */ * required to execute one subtree.
*/
public class RegsNeededVisitor extends AstVisitor<Integer, Void> { public class RegsNeededVisitor extends AstVisitor<Integer, Void> {
public int calc(Ast ast) { public int calc(Ast ast) {
return visit(ast, null); return visit(ast, null);
} }
private Map<Ast,Integer> memo = new HashMap<Ast, Integer>(); private Map<Ast, Integer> memo = new HashMap<Ast, Integer>();
/** /**
* Override visit() so as to memorize the results and avoid * Override visit() so as to memorize the results and avoid
* unnecessary computation * unnecessary computation
*/ */
@ -31,9 +32,9 @@ public class RegsNeededVisitor extends AstVisitor<Integer, Void> {
return memo.get(ast); return memo.get(ast);
Integer res = ast.accept(this, null); Integer res = ast.accept(this, null);
memo.put(ast, res); memo.put(ast, res);
return res; return res;
} }
@Override @Override
protected Integer dflt(Ast ast, Void arg) { protected Integer dflt(Ast ast, Void arg) {
// For a non-expression, it suffices to find the // For a non-expression, it suffices to find the
@ -54,8 +55,8 @@ public class RegsNeededVisitor extends AstVisitor<Integer, Void> {
public Integer binaryOp(BinaryOp ast, Void arg) { public Integer binaryOp(BinaryOp ast, Void arg) {
int left = calc(ast.left()); int left = calc(ast.left());
int right = calc(ast.right()); int right = calc(ast.right());
int ifLeftFirst = max(left, right+1); int ifLeftFirst = max(left, right + 1);
int ifRightFirst = max(left+1, right); int ifRightFirst = max(left + 1, right);
int overall = min(ifLeftFirst, ifRightFirst); int overall = min(ifLeftFirst, ifRightFirst);
return overall; return overall;
} }
@ -64,7 +65,7 @@ public class RegsNeededVisitor extends AstVisitor<Integer, Void> {
public Integer assign(Assign ast, Void arg) { public Integer assign(Assign ast, Void arg) {
return max(calc(ast.left()) + 1, calc(ast.right())); return max(calc(ast.left()) + 1, calc(ast.right()));
} }
@Override @Override
public Integer booleanConst(BooleanConst ast, Void arg) { public Integer booleanConst(BooleanConst ast, Void arg) {
return 1; return 1;
@ -74,7 +75,7 @@ public class RegsNeededVisitor extends AstVisitor<Integer, Void> {
public Integer builtInRead(BuiltInRead ast, Void arg) { public Integer builtInRead(BuiltInRead ast, Void arg) {
return 1; return 1;
} }
@Override @Override
public Integer cast(Cast ast, Void arg) { public Integer cast(Cast ast, Void arg) {
return calc(ast.arg()); return calc(ast.arg());
@ -84,7 +85,7 @@ public class RegsNeededVisitor extends AstVisitor<Integer, Void> {
public Integer index(Index ast, Void arg) { public Integer index(Index ast, Void arg) {
return max(calc(ast.left()), calc(ast.right()) + 1); return max(calc(ast.left()), calc(ast.right()) + 1);
} }
@Override @Override
public Integer field(Field ast, Void arg) { public Integer field(Field ast, Void arg) {
return calc(ast.arg()); return calc(ast.arg());
@ -114,7 +115,7 @@ public class RegsNeededVisitor extends AstVisitor<Integer, Void> {
public Integer thisRef(ThisRef ast, Void arg) { public Integer thisRef(ThisRef ast, Void arg) {
return 1; return 1;
} }
@Override @Override
public Integer methodCall(MethodCallExpr ast, Void arg) { public Integer methodCall(MethodCallExpr ast, Void arg) {
int max = 1; int max = 1;
@ -134,5 +135,5 @@ public class RegsNeededVisitor extends AstVisitor<Integer, Void> {
@Override @Override
public Integer var(Var ast, Void arg) { public Integer var(Var ast, Void arg) {
return 1; return 1;
} }
} }

View file

@ -1,34 +1,31 @@
package cd.backend.codegen; package cd.backend.codegen;
import cd.Config; import cd.Config;
import cd.backend.ExitCode;
import cd.backend.codegen.AstCodeGenerator.Label;
import cd.backend.codegen.RegisterManager.Register; import cd.backend.codegen.RegisterManager.Register;
import cd.frontend.semantic.ReturnCheckerVisitor;
import cd.ir.Ast; import cd.ir.Ast;
import cd.ir.Ast.*; import cd.ir.Ast.*;
import cd.ir.AstVisitor; import cd.ir.AstVisitor;
import cd.ir.Symbol; import cd.ir.CompileTimeEvaluator;
import cd.ir.Symbol.ClassSymbol; import cd.ir.ExprVisitor;
import cd.ir.Symbol.MethodSymbol; import cd.ir.Symbol.MethodSymbol;
import cd.ir.Symbol.PrimitiveTypeSymbol;
import cd.util.Pair;
import cd.util.debug.AstOneLine; import cd.util.debug.AstOneLine;
import java.util.*; import java.util.List;
import static cd.backend.codegen.AstCodeGenerator.VAR_SIZE; import static cd.backend.codegen.AssemblyEmitter.arrayAddress;
import static cd.backend.codegen.AstCodeGenerator.TRUE;
import static cd.backend.codegen.RegisterManager.BASE_REG; import static cd.backend.codegen.RegisterManager.BASE_REG;
import static cd.backend.codegen.RegisterManager.STACK_REG; import static cd.backend.codegen.RegisterManager.STACK_REG;
/** /**
* Generates code to process statements and declarations. * Generates code to process statements and declarations.
*/ */
class StmtGenerator extends AstVisitor<Register,Location> { class StmtGenerator extends AstVisitor<Register, Void> {
protected final AstCodeGenerator cg; protected final AstCodeGenerator cg;
StmtGenerator(AstCodeGenerator astCodeGenerator) { StmtGenerator(AstCodeGenerator astCodeGenerator) {
cg = astCodeGenerator; cg = astCodeGenerator;
} }
public void gen(Ast ast) { public void gen(Ast ast) {
@ -36,253 +33,337 @@ class StmtGenerator extends AstVisitor<Register,Location> {
} }
@Override @Override
public Register visit(Ast ast, Location arg) { public Register visit(Ast ast, Void arg) {
try { try {
cg.emit.increaseIndent("Emitting " + AstOneLine.toString(ast)); cg.emit.increaseIndent("Emitting " + AstOneLine.toString(ast));
if (cg.rnv.calc(ast) > cg.rm.availableRegisters()) { return super.visit(ast, arg);
Deque<Register> pushed = new ArrayDeque<>();
for (Register r : RegisterManager.GPR) {
if (cg.rm.isInUse(r)) {
cg.emit.emit("push", r);
pushed.push(r);
cg.rm.releaseRegister(r);
}
}
Register result = super.visit(ast, arg);
for (Register r : pushed)
cg.rm.useRegister(r);
Register finalResult = cg.rm.getRegister();
cg.emit.emitMove(result, finalResult);
for (Register r = pushed.pop(); !pushed.isEmpty(); r = pushed.pop())
cg.emit.emit("pop", r);
return finalResult;
} else return super.visit(ast, arg);
} finally { } finally {
cg.emit.decreaseIndent(); cg.emit.decreaseIndent();
} }
} }
@Override
public Register methodCall(MethodCall ast, Location arg) {
Register result = cg.eg.visit(ast.getMethodCallExpr(), arg);
if (result != null)
cg.rm.releaseRegister(result);
return null;
}
public Register methodCall(MethodSymbol sym, List<Expr> allArguments) { public Register methodCall(MethodSymbol sym, List<Expr> allArguments) {
throw new RuntimeException("Not required"); throw new RuntimeException("Not required");
} }
/** }
* vtable structure for a class type: pointer to superclass, and methods, each with 2 entries, name's hashCode and
* pointer to method execution /*
*/ * StmtGenerator with the reference solution
*/
class StmtGeneratorRef extends StmtGenerator {
/* cg and cgRef are the same instance. cgRef simply
* provides a wider interface */
protected final AstCodeGeneratorRef cgRef;
StmtGeneratorRef(AstCodeGeneratorRef astCodeGenerator) {
super(astCodeGenerator);
this.cgRef = astCodeGenerator;
}
@Override @Override
public Register classDecl(ClassDecl ast, Location arg) { public Register methodCall(MethodSymbol mthSymbol, List<Expr> allArgs) {
cg.emit.emitRaw(Config.DATA_INT_SECTION); // Push the arguments and the method prefix (caller save register,
// Emit vtable for class // and padding) onto the stack.
cg.emit.emitLabel(Label.type(ast.sym)); // Label // Note that the space for the arguments is not already reserved,
cg.emit.emitConstantData(Label.type(ast.sym.superClass)); // Superclass // so we just push them in the Java left-to-right order.
// Methods (don't write those that are overridden twice) //
Set<String> generated = new HashSet<>(); // After each iteration of the following loop, reg holds the
ClassSymbol currSym = ast.sym; // register used for the previous argument.
while (currSym != ClassSymbol.objectType) { int padding = cgRef.emitCallPrefix(null, allArgs.size());
ClassSymbol finalCurrSym = currSym;
currSym.methods.values().forEach(o -> { Register reg = null;
if (!generated.add(o.name)) return; for (int i = 0; i < allArgs.size(); i++) {
cg.emit.emitConstantData(String.valueOf(o.name.hashCode())); if (reg != null) {
cg.emit.emitConstantData(Label.method(finalCurrSym, o)); cgRef.rm.releaseRegister(reg);
}); }
currSym = currSym.superClass; reg = cgRef.eg.gen(allArgs.get(i));
cgRef.push(reg.repr);
} }
// End of class vtable
// Array vtable // Since "this" is the first parameter that push
boolean found = false; // we have to get it back to resolve the method call
for (Symbol.TypeSymbol type : cg.main.allTypeSymbols) { cgRef.emit.emitComment("Load \"this\" pointer");
if (type instanceof Symbol.ArrayTypeSymbol) { cgRef.emit.emitLoad((allArgs.size() - 1) * Config.SIZEOF_PTR, STACK_REG, reg);
Symbol.ArrayTypeSymbol aType = (Symbol.ArrayTypeSymbol) type;
if (aType.elementType == ast.sym) { // Check for a null receiver
cg.emit.emitLabel(Label.type(aType)); // Label int cnPadding = cgRef.emitCallPrefix(null, 1);
found = true; cgRef.push(reg.repr);
break; cgRef.emit.emit("call", AstCodeGeneratorRef.CHECK_NULL);
} cgRef.emitCallSuffix(null, 1, cnPadding);
// Load the address of the method to call into "reg"
// and call it indirectly.
cgRef.emit.emitLoad(0, reg, reg);
int mthdoffset = 4 + mthSymbol.vtableIndex * Config.SIZEOF_PTR;
cgRef.emit.emitLoad(mthdoffset, reg, reg);
cgRef.emit.emit("call", "*" + reg);
cgRef.emitCallSuffix(reg, allArgs.size(), padding);
if (mthSymbol.returnType == PrimitiveTypeSymbol.voidType) {
cgRef.rm.releaseRegister(reg);
return null;
}
return reg;
}
@Override
public Register methodCall(MethodCall ast, Void dummy) {
Register reg = cgRef.eg.gen(ast.getMethodCallExpr());
if (reg != null)
cgRef.rm.releaseRegister(reg);
return reg;
}
@Override
public Register classDecl(ClassDecl ast, Void arg) {
// Emit each method:
cgRef.emit.emitCommentSection("Class " + ast.name);
return visitChildren(ast, arg);
}
@Override
public Register methodDecl(MethodDecl ast, Void arg) {
cgRef.emitMethodPrefix(ast);
gen(ast.body());
cgRef.emitMethodSuffix(false);
return null;
}
@Override
public Register ifElse(IfElse ast, Void arg) {
String falseLbl = cgRef.emit.uniqueLabel();
String doneLbl = cgRef.emit.uniqueLabel();
cgRef.genJumpIfFalse(ast.condition(), falseLbl);
gen(ast.then());
cgRef.emit.emit("jmp", doneLbl);
cgRef.emit.emitLabel(falseLbl);
gen(ast.otherwise());
cgRef.emit.emitLabel(doneLbl);
return null;
}
@Override
public Register whileLoop(WhileLoop ast, Void arg) {
String nextLbl = cgRef.emit.uniqueLabel();
String doneLbl = cgRef.emit.uniqueLabel();
cgRef.emit.emitLabel(nextLbl);
cgRef.genJumpIfFalse(ast.condition(), doneLbl);
gen(ast.body());
cgRef.emit.emit("jmp", nextLbl);
cgRef.emit.emitLabel(doneLbl);
return null;
}
@Override
public Register assign(Assign ast, Void arg) {
class AssignVisitor extends ExprVisitor<Void, Expr> {
@Override
public Void var(Var ast, Expr right) {
final Register rhsReg = cgRef.eg.gen(right);
cgRef.emit.emitStore(rhsReg, ast.sym.offset, BASE_REG);
cgRef.rm.releaseRegister(rhsReg);
return null;
}
@Override
public Void field(Field ast, Expr right) {
final Register rhsReg = cgRef.eg.gen(right);
Pair<Register> regs = cgRef.egRef.genPushing(rhsReg, ast.arg());
int padding = cgRef.emitCallPrefix(null, 1);
cgRef.push(regs.b.repr);
cgRef.emit.emit("call", AstCodeGeneratorRef.CHECK_NULL);
cgRef.emitCallSuffix(null, 1, padding);
cgRef.emit.emitStore(regs.a, ast.sym.offset, regs.b);
cgRef.rm.releaseRegister(regs.b);
cgRef.rm.releaseRegister(regs.a);
return null;
}
@Override
public Void index(Index ast, Expr right) {
Register rhsReg = cgRef.egRef.gen(right);
Pair<Register> regs = cgRef.egRef.genPushing(rhsReg, ast.left());
rhsReg = regs.a;
Register arrReg = regs.b;
int padding = cgRef.emitCallPrefix(null, 1);
cgRef.push(arrReg.repr);
cgRef.emit.emit("call", AstCodeGeneratorRef.CHECK_NULL);
cgRef.emitCallSuffix(null, 1, padding);
regs = cgRef.egRef.genPushing(arrReg, ast.right());
arrReg = regs.a;
Register idxReg = regs.b;
// Check array bounds
padding = cgRef.emitCallPrefix(null, 2);
cgRef.push(idxReg.repr);
cgRef.push(arrReg.repr);
cgRef.emit.emit("call", AstCodeGeneratorRef.CHECK_ARRAY_BOUNDS);
cgRef.emitCallSuffix(null, 2, padding);
cgRef.emit.emitMove(rhsReg, arrayAddress(arrReg, idxReg));
cgRef.rm.releaseRegister(arrReg);
cgRef.rm.releaseRegister(idxReg);
cgRef.rm.releaseRegister(rhsReg);
return null;
}
@Override
protected Void dfltExpr(Expr ast, Expr arg) {
throw new RuntimeException("Store to unexpected lvalue " + ast);
}
}
new AssignVisitor().visit(ast.left(), ast.right());
return null;
}
@Override
public Register builtInWrite(BuiltInWrite ast, Void arg) {
Register reg = cgRef.eg.gen(ast.arg());
int padding = cgRef.emitCallPrefix(null, 1);
cgRef.push(reg.repr);
cgRef.emit.emit("call", AstCodeGeneratorRef.PRINT_INTEGER);
cgRef.emitCallSuffix(null, 1, padding);
cgRef.rm.releaseRegister(reg);
return null;
}
@Override
public Register builtInWriteln(BuiltInWriteln ast, Void arg) {
int padding = cgRef.emitCallPrefix(null, 0);
cgRef.emit.emit("call", AstCodeGeneratorRef.PRINT_NEW_LINE);
cgRef.emitCallSuffix(null, 0, padding);
return null;
}
@Override
public Register returnStmt(ReturnStmt ast, Void arg) {
if (ast.arg() != null) {
Register reg = cgRef.eg.gen(ast.arg());
cgRef.emit.emitMove(reg, "%eax");
cgRef.emitMethodSuffix(false);
cgRef.rm.releaseRegister(reg);
} else {
cgRef.emitMethodSuffix(true); // no return value -- return NULL as
// a default (required for main())
}
return null;
}
}
class StmtGeneratorNop90 extends StmtGeneratorRef {
protected final CfgCodeGenerator cfgCg;
protected CompileTimeEvaluator cte;
StmtGeneratorNop90(AstCodeGeneratorNop90 astCodeGenerator, CfgCodeGenerator cfgCodeGenerator) {
super(astCodeGenerator);
this.cfgCg = cfgCodeGenerator;
cte = new CompileTimeEvaluator();
}
@Override
public Register assign(Assign ast, Void arg) {
/*
if (ast.left() instanceof Ast.Var) {
Ast.Var var = (Ast.Var) ast.left();
VariableSymbol sym = var.sym;
MaybeC<List<Assign>> maybeStateList = cfgCg.unusedAssignmentsState.get(sym);
List<Ast.Assign> stateList = maybeStateList.getValue();
if (stateList.contains(ast)) {
return null;
} }
} }
if (!found) */
throw new RuntimeException("The array type could not be found"); class AssignVisitor extends ExprVisitor<Void, Expr> {
cg.emit.emitConstantData(Label.type(ClassSymbol.objectType)); // Supertype
cg.emit.emitConstantData(Label.type(ast.sym)); // Type of elements
// End of array vtable
cg.emit.emitRaw(Config.TEXT_SECTION);
// Method bodies
for (Ast method : ast.methods())
visit(method, new Location(ast.sym));
return null;
}
@Override @Override
public Register methodDecl(MethodDecl ast, Location arg) { public Void var(Var ast, Expr right) {
// Bookkeeping for framework final Register rhsReg = cgRef.eg.gen(right);
arg.enterMethod(ast.sym); cgRef.emit.emitStore(rhsReg, ast.sym.offset, BASE_REG);
cg.initMethodData(); cgRef.rm.releaseRegister(rhsReg);
return null;
}
// Begin method @Override
cg.emit.emitLabel(Label.method(arg.classSym(), ast.sym)); public Void field(Field ast, Expr right) {
final Register rhsReg = cgRef.eg.gen(right);
Pair<Register> regs = cgRef.egRef.genPushing(rhsReg, ast.arg());
if (!cfgCg.check.isNotNull(ast.arg())) {
int padding = cgRef.emitCallPrefix(null, 1);
cgRef.push(regs.b.repr);
cgRef.emit.emit("call", AstCodeGeneratorRef.CHECK_NULL);
cgRef.emitCallSuffix(null, 1, padding);
}
cgRef.emit.emitStore(regs.a, ast.sym.offset, regs.b);
cgRef.rm.releaseRegister(regs.b);
cgRef.rm.releaseRegister(regs.a);
return null;
}
// 1. Save and update the base register, reserve space for local variables @Override
cg.emit.emit("enter", ast.sym.locals.size() * VAR_SIZE, 0); public Void index(Index ast, Expr right) {
for (int i = 0; i < ast.sym.locals.size(); i++) boolean isNotNull = cfgCg.check.isNotNull(ast.left());
cg.emit.emitStore(0, -(i + 1) * VAR_SIZE, BASE_REG); boolean emitBoundCheck = cfgCg.check.checkArrayBound(ast);
Register rhsReg = cgRef.egRef.gen(right);
// 2. Save CPU registers Pair<Register> regs = cgRef.egRef.genPushing(rhsReg, ast.left());
Register[] regs = RegisterManager.CALLEE_SAVE; rhsReg = regs.a;
for (int i = 0; i < regs.length; i++) Register arrReg = regs.b;
cg.emit.emit("push", regs[i]); if (!isNotNull) {
int padding = cgRef.emitCallPrefix(null, 1);
cgRef.push(arrReg.repr);
cgRef.emit.emit("call", AstCodeGeneratorRef.CHECK_NULL);
cgRef.emitCallSuffix(null, 1, padding);
}
// 3. Run the code contained in the function regs = cgRef.egRef.genPushing(arrReg, ast.right());
Register returnReg = visit(ast.body(), arg); arrReg = regs.a;
cg.emit.emitLabel(Label.returnMethod(arg.classSym(), ast.sym)); Register idxReg = regs.b;
// 4. Restore saved registers (if there is a mismatch, the stack will be corrupted) // Check array bounds
for (int i = regs.length - 1; i >= 0; i--) if (emitBoundCheck) {
cg.emit.emit("pop", regs[i]); int padding = cgRef.emitCallPrefix(null, 2);
cgRef.push(idxReg.repr);
cgRef.push(arrReg.repr);
cgRef.emit.emit("call", AstCodeGeneratorRef.CHECK_ARRAY_BOUNDS);
cgRef.emitCallSuffix(null, 2, padding);
}
// 5 and 6: Restore the old base pointer and return from the function cgRef.emit.emitMove(rhsReg, arrayAddress(arrReg, idxReg));
cg.emitMethodSuffix(returnReg == null); cgRef.rm.releaseRegister(arrReg);
cgRef.rm.releaseRegister(idxReg);
cgRef.rm.releaseRegister(rhsReg);
// Framework bookkeeping return null;
arg.leaveMethod(); }
return null;
} @Override
protected Void dfltExpr(Expr ast, Expr arg) {
throw new RuntimeException("Store to unexpected lvalue " + ast);
}
@Override
public Register ifElse(IfElse ast, Location arg) {
// Emit condition check
Register conditionRegister = cg.eg.visit(ast.condition(), arg);
// If both blocks are empty, no more code need be generated, and the condition can be ignored
if (ast.then().children().isEmpty() && ast.otherwise().children().isEmpty()) {
// Generate the condition and ignore the result
cg.rm.releaseRegister(conditionRegister);
return null;
}
String notIfLabel = cg.emit.uniqueLabel();
String endLabel = cg.emit.uniqueLabel();
cg.emit.emit("cmp", TRUE, conditionRegister);
cg.rm.releaseRegister(conditionRegister);
if (!ast.then().children().isEmpty()) {
cg.emit.emit("jne", notIfLabel);
visit(ast.then(), arg);
// If there is no otherwise, the jump instruction makes no sense
// as the next code executed will be the next instruction
if (!ast.otherwise().children().isEmpty())
cg.emit.emit("jmp", endLabel);
} else {
// No if, therefore the else follows the condition immediately
cg.emit.emit("je", endLabel);
}
cg.emit.emitLabel(notIfLabel);
// Emit otherwise
visit(ast.otherwise(), arg);
cg.emit.emitLabel(endLabel);
// Check if the ifElse ast node contains a return statement
ReturnCheckerVisitor rc = new ReturnCheckerVisitor();
if (rc.ifElse(ast, null)) {
return Register.EAX;
} else {
return null;
}
}
@Override
public Register whileLoop(WhileLoop ast, Location arg) {
String conditionLabel = cg.emit.uniqueLabel();
cg.emit.emitLabel(conditionLabel);
Register conditionRegister = cg.eg.visit(ast.condition(), arg);
cg.emit.emit("cmp", TRUE, conditionRegister);
cg.rm.releaseRegister(conditionRegister);
if (ast.body().children().isEmpty()) {
// Jump structure can be easier if there is no body
cg.emit.emit("je", conditionLabel);
} else {
String endLabel = cg.emit.uniqueLabel();
// Emit jumps, labels and body
cg.emit.emit("jne", endLabel);
visit(ast.body(), arg);
cg.emit.emit("jmp", conditionLabel);
cg.emit.emitLabel(endLabel);
}
return null;
}
@Override
public Register assign(Assign ast, Location arg) {
Register value = cg.eg.visit(ast.right(), arg);
// If the type is a reference, visiting the lhs will yield its address
// else, the lhs will yield the current value. We need a visitor that
// returns a pointer to the position of the variable/field in memory
// for primitive types.
Register pointer = cg.eg.visit(ast.left(), arg.obtainReference());
if (ast.left().type.isReferenceType()) {
// Check null pointer
String validPointerLabel = cg.emit.uniqueLabel();
cg.emit.emit("cmp", 0, pointer);
cg.emit.emit("jne", validPointerLabel);
Interrupts.exit(cg, ExitCode.NULL_POINTER);
cg.emit.emitLabel(validPointerLabel);
}
cg.emit.emitStore(value, 0, pointer);
cg.rm.releaseRegister(pointer);
cg.rm.releaseRegister(value);
return null;
}
@Override
public Register builtInWrite(BuiltInWrite ast, Location arg) {
Register reg = cg.eg.visit(ast.arg(), arg);
cg.emit.emit("sub", 16, STACK_REG);
cg.emit.emitStore(reg, 4, STACK_REG);
cg.emit.emitStore("$STR_D", 0, STACK_REG);
cg.emit.emit("call", Config.PRINTF);
cg.emit.emit("add", 16, STACK_REG);
cg.rm.releaseRegister(reg);
return null;
}
@Override
public Register builtInWriteln(BuiltInWriteln ast, Location arg) {
cg.emit.emit("sub", 16, STACK_REG);
cg.emit.emitStore("$STR_NL", 0, STACK_REG);
cg.emit.emit("call", Config.PRINTF);
cg.emit.emit("add", 16, STACK_REG);
return null;
}
@Override
public Register returnStmt(ReturnStmt ast, Location arg) {
if (ast.arg() != null) {
Register retReg = cg.eg.visit(ast.arg(), arg);
cg.emit.emitMove(retReg, Register.EAX);
cg.rm.releaseRegister(retReg);
}
cg.emit.emit("jmp", Label.returnMethod(arg.classSym(), arg.methodSym()));
return Register.EAX;
}
@Override
public Register seq(Seq ast, Location arg) {
for (Ast instruction : ast.children()) {
Register res = visit(instruction, arg);
if (res != null)
return res; // don't generate instructions after a return
} }
new AssignVisitor().visit(ast.left(), ast.right());
return null; return null;
} }
} }

View file

@ -0,0 +1,40 @@
package cd.frontend.semantic;
import cd.ir.Ast;
import cd.ir.Ast.ClassDecl;
import cd.ir.Ast.Var;
import cd.ir.AstRewriteVisitor;
import cd.ir.Symbol.ClassSymbol;
/**
* Runs after the semantic check and rewrites expressions to be more normalized.
* References to a field {@code foo} are rewritten to always use
* {@link Ast.Field} objects (i.e., {@code this.foo}.
*/
public class FieldQualifier {
public void rewrite(ClassDecl cd) {
AstRewriteVisitor<ClassSymbol> rewriter = new AstRewriteVisitor<ClassSymbol>() {
@Override
public Ast var(Var ast, ClassSymbol cs) {
switch (ast.sym.kind) {
case PARAM:
case LOCAL:
// Leave params or local variables alone
return ast;
case FIELD:
// Convert an implicit field reference to "this.foo"
Ast.Field f = new Ast.Field(new Ast.ThisRef(), ast.name);
f.arg().type = cs;
f.sym = ast.sym;
f.type = ast.type;
return f;
}
throw new RuntimeException("Unknown kind of var");
}
};
cd.accept(rewriter, cd.sym);
}
}

View file

@ -13,13 +13,13 @@ import java.util.HashSet;
import java.util.Set; import java.util.Set;
public class InheritanceChecker extends AstVisitor<Void, Void> { public class InheritanceChecker extends AstVisitor<Void, Void> {
ClassSymbol classSym; ClassSymbol classSym;
@Override @Override
public Void classDecl(ClassDecl ast, Void arg) { public Void classDecl(ClassDecl ast, Void arg) {
classSym = ast.sym; classSym = ast.sym;
// check for cycles in the inheritance hierarchy: // check for cycles in the inheritance hierarchy:
Set<ClassSymbol> supers = new HashSet<ClassSymbol>(); Set<ClassSymbol> supers = new HashSet<ClassSymbol>();
ClassSymbol sc = classSym.superClass; ClassSymbol sc = classSym.superClass;
@ -28,30 +28,31 @@ public class InheritanceChecker extends AstVisitor<Void, Void> {
if (supers.contains(sc)) if (supers.contains(sc))
throw new SemanticFailure( throw new SemanticFailure(
Cause.CIRCULAR_INHERITANCE, Cause.CIRCULAR_INHERITANCE,
"Class %s has %s as a superclass twice", "Class %s has %s as a superclass twice",
ast.name, sc.name); ast.name, sc.name);
supers.add(sc); supers.add(sc);
sc = sc.superClass; sc = sc.superClass;
} }
this.visitChildren(ast, null); this.visitChildren(ast, null);
return null; return null;
} }
@Override @Override
public Void methodDecl(MethodDecl ast, Void arg) { public Void methodDecl(MethodDecl ast, Void arg) {
// check that methods overridden from a parent class agree // check that methods overridden from a parent class agree
// on number/type of parameters // on number/type of parameters
MethodSymbol sym = ast.sym; MethodSymbol sym = ast.sym;
MethodSymbol superSym = classSym.superClass.getMethod(ast.name); MethodSymbol superSym = classSym.superClass.getMethod(ast.name);
sym.overrides = superSym;
if (superSym != null) { if (superSym != null) {
if (superSym.parameters.size() != sym.parameters.size()) if (superSym.parameters.size() != sym.parameters.size())
throw new SemanticFailure( throw new SemanticFailure(
Cause.INVALID_OVERRIDE, Cause.INVALID_OVERRIDE,
"Overridden method %s has %d parameters, " + "Overridden method %s has %d parameters, " +
"but original has %d", "but original has %d",
ast.name, sym.parameters.size(), ast.name, sym.parameters.size(),
superSym.parameters.size()); superSym.parameters.size());
for (Pair<VariableSymbol> pair : Pair.zip(sym.parameters, superSym.parameters)) for (Pair<VariableSymbol> pair : Pair.zip(sym.parameters, superSym.parameters))
@ -59,17 +60,17 @@ public class InheritanceChecker extends AstVisitor<Void, Void> {
throw new SemanticFailure( throw new SemanticFailure(
Cause.INVALID_OVERRIDE, Cause.INVALID_OVERRIDE,
"Method parameter %s has type %s, but " + "Method parameter %s has type %s, but " +
"corresponding base class parameter %s has type %s", "corresponding base class parameter %s has type %s",
pair.a.name, pair.a.type, pair.b.name, pair.b.type); pair.a.name, pair.a.type, pair.b.name, pair.b.type);
if (superSym.returnType != sym.returnType) if (superSym.returnType != sym.returnType)
throw new SemanticFailure( throw new SemanticFailure(
Cause.INVALID_OVERRIDE, Cause.INVALID_OVERRIDE,
"Overridden method %s has return type %s," + "Overridden method %s has return type %s," +
"but its superclass has %s", "but its superclass has %s",
ast.name, sym.returnType, superSym.returnType); ast.name, sym.returnType, superSym.returnType);
} }
return null; return null;
} }
} }

View file

@ -5,22 +5,21 @@ import cd.ir.Ast.*;
import cd.ir.AstVisitor; import cd.ir.AstVisitor;
/** /**
* Visitor that checks if all paths of a given sequence have a * Visitor that checks if all paths of a given sequence have a
* return statement. * return statement.
* * <p>
* This visitor only needs to be used if are not using the Control Flow Graph. * This visitor only needs to be used if are not using the Control Flow Graph.
* *
* @author Leo Buttiker * @author Leo Buttiker
*/ */
public class ReturnCheckerVisitor extends AstVisitor<Boolean, Void> { public class ReturnCheckerVisitor extends AstVisitor<Boolean, Void> {
@Override @Override
protected Boolean dfltStmt(Stmt ast, Void arg) { protected Boolean dfltStmt(Stmt ast, Void arg) {
return false; return false;
} }
@Override @Override
public Boolean returnStmt(ReturnStmt ast, Void arg) { public Boolean returnStmt(ReturnStmt ast, Void arg) {
return true; return true;
@ -28,7 +27,7 @@ public class ReturnCheckerVisitor extends AstVisitor<Boolean, Void> {
@Override @Override
public Boolean ifElse(IfElse ast, Void arg) { public Boolean ifElse(IfElse ast, Void arg) {
boolean allPathHaveAReturnStmt = true; boolean allPathHaveAReturnStmt = true;
allPathHaveAReturnStmt &= visit(ast.then(), null); allPathHaveAReturnStmt &= visit(ast.then(), null);
allPathHaveAReturnStmt &= visit(ast.otherwise(), null); allPathHaveAReturnStmt &= visit(ast.otherwise(), null);
return allPathHaveAReturnStmt; return allPathHaveAReturnStmt;
@ -36,17 +35,17 @@ public class ReturnCheckerVisitor extends AstVisitor<Boolean, Void> {
@Override @Override
public Boolean seq(Seq ast, Void arg) { public Boolean seq(Seq ast, Void arg) {
boolean allPathHaveAReturnStmt = false; boolean allPathHaveAReturnStmt = false;
for (Ast child : ast.children()) { for (Ast child : ast.children()) {
allPathHaveAReturnStmt |= this.visit(child, null); allPathHaveAReturnStmt |= this.visit(child, null);
} }
return allPathHaveAReturnStmt; return allPathHaveAReturnStmt;
} }
@Override @Override
public Boolean whileLoop(WhileLoop ast, Void arg) { public Boolean whileLoop(WhileLoop ast, Void arg) {
return false; return false;
} }
} }

View file

@ -11,70 +11,76 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
public class SemanticAnalyzer { public class SemanticAnalyzer {
public final Main main; public final Main main;
public SemanticAnalyzer(Main main) { public SemanticAnalyzer(Main main) {
this.main = main; this.main = main;
} }
public void check(List<ClassDecl> classDecls) public void check(List<ClassDecl> classDecls)
throws SemanticFailure { throws SemanticFailure {
{ {
SymTable<TypeSymbol> typeSymbols = createSymbols(classDecls); SymTable<TypeSymbol> typeSymbols = createSymbols(classDecls);
checkInheritance(classDecls); checkInheritance(classDecls);
checkStartPoint(typeSymbols); checkStartPoint(typeSymbols);
checkMethodBodies(typeSymbols, classDecls); checkMethodBodies(typeSymbols, classDecls);
{
rewriteMethodBodies(classDecls);
main.allTypeSymbols = typeSymbols.allSymbols();
}
} }
} }
/** /**
* Creates a symbol table with symbols for all built-in types, * Creates a symbol table with symbols for all built-in types,
* as well as all classes and their fields and methods. Also * as well as all classes and their fields and methods. Also
* creates a corresponding array symbol for every type * creates a corresponding array symbol for every type
* (named {@code type[]}). * (named {@code type[]}).
*
* @see SymbolCreator * @see SymbolCreator
*/ */
private SymTable<TypeSymbol> createSymbols(List<ClassDecl> classDecls) { private SymTable<TypeSymbol> createSymbols(List<ClassDecl> classDecls) {
// Start by creating a symbol for all built-in types. // Start by creating a symbol for all built-in types.
SymTable<TypeSymbol> typeSymbols = new SymTable<TypeSymbol>(null); SymTable<TypeSymbol> typeSymbols = new SymTable<TypeSymbol>(null);
typeSymbols.add(PrimitiveTypeSymbol.intType); typeSymbols.add(PrimitiveTypeSymbol.intType);
typeSymbols.add(PrimitiveTypeSymbol.booleanType); typeSymbols.add(PrimitiveTypeSymbol.booleanType);
typeSymbols.add(PrimitiveTypeSymbol.voidType); typeSymbols.add(PrimitiveTypeSymbol.voidType);
typeSymbols.add(ClassSymbol.objectType); typeSymbols.add(ClassSymbol.objectType);
// Add symbols for all declared classes. // Add symbols for all declared classes.
for (ClassDecl ast : classDecls) { for (ClassDecl ast : classDecls) {
// Check for classes named Object // Check for classes named Object
if (ast.name.equals(ClassSymbol.objectType.name)) if (ast.name.equals(ClassSymbol.objectType.name))
throw new SemanticFailure(Cause.OBJECT_CLASS_DEFINED); throw new SemanticFailure(Cause.OBJECT_CLASS_DEFINED);
ast.sym = new ClassSymbol(ast); ast.sym = new ClassSymbol(ast);
typeSymbols.add(ast.sym); typeSymbols.add(ast.sym);
} }
// Create symbols for arrays of each type. // Create symbols for arrays of each type.
for (Symbol sym : new ArrayList<Symbol>(typeSymbols.localSymbols())) { for (Symbol sym : new ArrayList<Symbol>(typeSymbols.localSymbols())) {
Symbol.ArrayTypeSymbol array = Symbol.ArrayTypeSymbol array =
new Symbol.ArrayTypeSymbol((TypeSymbol) sym); new Symbol.ArrayTypeSymbol((TypeSymbol) sym);
typeSymbols.add(array); typeSymbols.add(array);
} }
// For each class, create symbols for each method and field // For each class, create symbols for each method and field
SymbolCreator sc = new SymbolCreator(main, typeSymbols); SymbolCreator sc = new SymbolCreator(main, typeSymbols);
for (ClassDecl ast : classDecls) for (ClassDecl ast : classDecls)
sc.createSymbols(ast); sc.createSymbols(ast);
return typeSymbols; return typeSymbols;
} }
/** /**
* Check for errors related to inheritance: * Check for errors related to inheritance:
* circular inheritance, invalid super * circular inheritance, invalid super
* classes, methods with different types, etc. * classes, methods with different types, etc.
* Note that this must be run early because other code assumes * Note that this must be run early because other code assumes
* that the inheritance is correct, for type checking etc. * that the inheritance is correct, for type checking etc.
*
* @see InheritanceChecker * @see InheritanceChecker
*/ */
private void checkInheritance(List<ClassDecl> classDecls) { private void checkInheritance(List<ClassDecl> classDecls) {
@ -93,25 +99,26 @@ public class SemanticAnalyzer {
MethodSymbol mainMethod = cs.getMethod("main"); MethodSymbol mainMethod = cs.getMethod("main");
if (mainMethod != null && mainMethod.parameters.size() == 0 && if (mainMethod != null && mainMethod.parameters.size() == 0 &&
mainMethod.returnType == PrimitiveTypeSymbol.voidType) { mainMethod.returnType == PrimitiveTypeSymbol.voidType) {
main.mainType = cs;
return; // found the main() method! return; // found the main() method!
} }
} }
throw new SemanticFailure(Cause.INVALID_START_POINT, "No Main class with method 'void main()' found"); throw new SemanticFailure(Cause.INVALID_START_POINT, "No Main class with method 'void main()' found");
} }
/** /**
* Check the bodies of methods for errors, particularly type errors * Check the bodies of methods for errors, particularly type errors
* but also undefined identifiers and the like. * but also undefined identifiers and the like.
*
* @see TypeChecker * @see TypeChecker
*/ */
private void checkMethodBodies( private void checkMethodBodies(
SymTable<TypeSymbol> typeSymbols, SymTable<TypeSymbol> typeSymbols,
List<ClassDecl> classDecls) List<ClassDecl> classDecls) {
{
TypeChecker tc = new TypeChecker(typeSymbols); TypeChecker tc = new TypeChecker(typeSymbols);
for (ClassDecl classd : classDecls) { for (ClassDecl classd : classDecls) {
SymTable<VariableSymbol> fldTable = new SymTable<VariableSymbol>(null); SymTable<VariableSymbol> fldTable = new SymTable<VariableSymbol>(null);
// add all fields of this class, or any of its super classes // add all fields of this class, or any of its super classes
@ -119,37 +126,42 @@ public class SemanticAnalyzer {
for (VariableSymbol s : p.fields.values()) for (VariableSymbol s : p.fields.values())
if (!fldTable.contains(s.name)) if (!fldTable.contains(s.name))
fldTable.add(s); fldTable.add(s);
// type check any method bodies and final locals // type check any method bodies and final locals
for (MethodDecl md : classd.methods()) { for (MethodDecl md : classd.methods()) {
boolean hasReturn = new ReturnCheckerVisitor().visit(md.body(), null); boolean hasReturn = new ReturnCheckerVisitor().visit(md.body(), null);
if (!md.returnType.equals("void") && !hasReturn) { if (!md.returnType.equals("void") && !hasReturn) {
throw new SemanticFailure(Cause.MISSING_RETURN, throw new SemanticFailure(Cause.MISSING_RETURN,
"Method %s.%s is missing a return statement", "Method %s.%s is missing a return statement",
classd.name, classd.name,
md.name); md.name);
} }
SymTable<VariableSymbol> mthdTable = new SymTable<VariableSymbol>(fldTable); SymTable<VariableSymbol> mthdTable = new SymTable<VariableSymbol>(fldTable);
mthdTable.add(classd.sym.thisSymbol); mthdTable.add(classd.sym.thisSymbol);
for (VariableSymbol p : md.sym.parameters) { for (VariableSymbol p : md.sym.parameters) {
mthdTable.add(p); mthdTable.add(p);
} }
for (VariableSymbol l : md.sym.locals.values()) { for (VariableSymbol l : md.sym.locals.values()) {
mthdTable.add(l); mthdTable.add(l);
} }
tc.checkMethodDecl(md, mthdTable); tc.checkMethodDecl(md, mthdTable);
} }
} }
} }
private void rewriteMethodBodies(List<ClassDecl> classDecls) {
for (ClassDecl cd : classDecls)
new FieldQualifier().rewrite(cd);
}
} }

View file

@ -1,40 +1,50 @@
package cd.frontend.semantic; package cd.frontend.semantic;
/** /**
* Thrown by the semantic checker when a semantic error is detected * Thrown by the semantic checker when a semantic error is detected
* in the user's program. */ * in the user's program.
*/
public class SemanticFailure extends RuntimeException { public class SemanticFailure extends RuntimeException {
private static final long serialVersionUID = 5375946759285719123L; private static final long serialVersionUID = 5375946759285719123L;
public enum Cause { public enum Cause {
/** /**
* Caused by an assignment to either a final field, {@code this}, * Caused by an assignment to either a final field, {@code this},
* or some other kind of expression which cannot be assigned to. * or some other kind of expression which cannot be assigned to.
* <b>Not</b> used for type errors in assignments, which fall * <b>Not</b> used for type errors in assignments, which fall
* under {@link #TYPE_ERROR}. */ * under {@link #TYPE_ERROR}.
*/
NOT_ASSIGNABLE, NOT_ASSIGNABLE,
/** Two variables, fields, methods, or classes with the same name /**
* were declared in the same scope */ * Two variables, fields, methods, or classes with the same name
* were declared in the same scope
*/
DOUBLE_DECLARATION, DOUBLE_DECLARATION,
/** A field was accessed that does not exist */ /**
* A field was accessed that does not exist
*/
NO_SUCH_FIELD, NO_SUCH_FIELD,
/** A method was called that does not exist */ /**
* A method was called that does not exist
*/
NO_SUCH_METHOD, NO_SUCH_METHOD,
/** /**
* A variable or other identifier was used in a method * A variable or other identifier was used in a method
* body which has no corresponding declaration */ * body which has no corresponding declaration
*/
NO_SUCH_VARIABLE, NO_SUCH_VARIABLE,
/** /**
* A method with a return type is missing a return statement among one of its paths */ * A method with a return type is missing a return statement among one of its paths
*/
MISSING_RETURN, MISSING_RETURN,
/** /**
* Can occur in many contents: * Can occur in many contents:
* <ul> * <ul>
* <li> Assignment to a variable from an expression of wrong type * <li> Assignment to a variable from an expression of wrong type
@ -53,12 +63,12 @@ public class SemanticFailure extends RuntimeException {
* </ul> * </ul>
*/ */
TYPE_ERROR, TYPE_ERROR,
/** /**
* A class is its own super class * A class is its own super class
*/ */
CIRCULAR_INHERITANCE, CIRCULAR_INHERITANCE,
/** /**
* One of the following: * One of the following:
* <ul> * <ul>
@ -69,13 +79,13 @@ public class SemanticFailure extends RuntimeException {
* </ul> * </ul>
*/ */
INVALID_START_POINT, INVALID_START_POINT,
/** /**
* A class {@code Object} was defined. This class is implicitly * A class {@code Object} was defined. This class is implicitly
* defined and cannot be defined explicitly. * defined and cannot be defined explicitly.
*/ */
OBJECT_CLASS_DEFINED, OBJECT_CLASS_DEFINED,
/** /**
* A type name was found for which no class declaration exists. * A type name was found for which no class declaration exists.
* This can occur in many contexts: * This can occur in many contexts:
@ -86,34 +96,36 @@ public class SemanticFailure extends RuntimeException {
* </ul> * </ul>
*/ */
NO_SUCH_TYPE, NO_SUCH_TYPE,
/** /**
* The parameters of an overridden method have different types * The parameters of an overridden method have different types
* from the base method, there is a different * from the base method, there is a different
* number of parameters, or the return value is different. * number of parameters, or the return value is different.
*/ */
INVALID_OVERRIDE, INVALID_OVERRIDE,
/** A method was called with the wrong number of arguments */ /**
* A method was called with the wrong number of arguments
*/
WRONG_NUMBER_OF_ARGUMENTS, WRONG_NUMBER_OF_ARGUMENTS,
/** /**
* Indicates the use of a local variable that may not have been * Indicates the use of a local variable that may not have been
* initialized (ACD only). * initialized (ACD only).
*/ */
POSSIBLY_UNINITIALIZED, POSSIBLY_UNINITIALIZED,
} }
public final Cause cause; public final Cause cause;
public SemanticFailure(Cause cause) { public SemanticFailure(Cause cause) {
super(cause.name()); super(cause.name());
this.cause = cause; this.cause = cause;
} }
public SemanticFailure(Cause cause, String format, Object... args) { public SemanticFailure(Cause cause, String format, Object... args) {
super(String.format(format, args)); super(String.format(format, args));
this.cause = cause; this.cause = cause;
} }
} }

View file

@ -3,24 +3,23 @@ package cd.frontend.semantic;
import cd.frontend.semantic.SemanticFailure.Cause; import cd.frontend.semantic.SemanticFailure.Cause;
import cd.ir.Symbol; import cd.ir.Symbol;
import java.util.Collection; import java.util.*;
import java.util.HashMap;
import java.util.Map;
/** /**
* A simple symbol table, with a pointer to the enclosing scope. * A simple symbol table, with a pointer to the enclosing scope.
* Used by {@link TypeChecker} to store the various scopes for * Used by {@link TypeChecker} to store the various scopes for
* local, parameter, and field lookup. */ * local, parameter, and field lookup.
*/
public class SymTable<S extends Symbol> { public class SymTable<S extends Symbol> {
private final Map<String, S> map = new HashMap<String, S>(); private final Map<String, S> map = new HashMap<String, S>();
private final SymTable<S> parent; private final SymTable<S> parent;
public SymTable(SymTable<S> parent) { public SymTable(SymTable<S> parent) {
this.parent = parent; this.parent = parent;
} }
public void add(S sym) { public void add(S sym) {
// check that the symbol is not already declared *at this level* // check that the symbol is not already declared *at this level*
if (containsLocally(sym.name)) if (containsLocally(sym.name))
@ -28,10 +27,21 @@ public class SymTable<S extends Symbol> {
map.put(sym.name, sym); map.put(sym.name, sym);
} }
public List<S> allSymbols() {
List<S> result = new ArrayList<S>();
SymTable<S> st = this;
while (st != null) {
for (S sym : st.map.values())
result.add(sym);
st = st.parent;
}
return result;
}
public Collection<S> localSymbols() { public Collection<S> localSymbols() {
return this.map.values(); return this.map.values();
} }
/** /**
* True if there is a declaration with the given name at any * True if there is a declaration with the given name at any
* level in the symbol table * level in the symbol table
@ -40,7 +50,7 @@ public class SymTable<S extends Symbol> {
return get(name) != null; return get(name) != null;
} }
/** /**
* True if there is a declaration at THIS level in the symbol * True if there is a declaration at THIS level in the symbol
* table; may return {@code false} even if a declaration exists * table; may return {@code false} even if a declaration exists
* in some enclosing scope * in some enclosing scope
@ -49,9 +59,10 @@ public class SymTable<S extends Symbol> {
return this.map.containsKey(name); return this.map.containsKey(name);
} }
/** /**
* Base method: returns {@code null} if no symbol by that * Base method: returns {@code null} if no symbol by that
* name can be found, in this table or in its parents */ * name can be found, in this table or in its parents
*/
public S get(String name) { public S get(String name) {
S res = map.get(name); S res = map.get(name);
if (res != null) if (res != null)
@ -60,11 +71,12 @@ public class SymTable<S extends Symbol> {
return null; return null;
return parent.get(name); return parent.get(name);
} }
/** /**
* Finds the symbol with the given name, or fails with a * Finds the symbol with the given name, or fails with a
* NO_SUCH_TYPE error. Only really makes sense to use this * NO_SUCH_TYPE error. Only really makes sense to use this
* if S == TypeSymbol, but we don't strictly forbid it... */ * if S == TypeSymbol, but we don't strictly forbid it...
*/
public S getType(String name) { public S getType(String name) {
S res = get(name); S res = get(name);
if (res == null) if (res == null)

View file

@ -21,10 +21,10 @@ import java.util.Set;
* and local variables. * and local variables.
*/ */
public class SymbolCreator extends Object { public class SymbolCreator extends Object {
final Main main; final Main main;
final SymTable<TypeSymbol> typesTable; final SymTable<TypeSymbol> typesTable;
public SymbolCreator(Main main, SymTable<TypeSymbol> typesTable) { public SymbolCreator(Main main, SymTable<TypeSymbol> typesTable) {
this.main = main; this.main = main;
this.typesTable = typesTable; this.typesTable = typesTable;
@ -34,33 +34,33 @@ public class SymbolCreator extends Object {
// lookup the super class. the grammar guarantees that this // lookup the super class. the grammar guarantees that this
// will refer to a class, if the lookup succeeds. // will refer to a class, if the lookup succeeds.
cd.sym.superClass = (ClassSymbol) typesTable.getType(cd.superClass); cd.sym.superClass = (ClassSymbol) typesTable.getType(cd.superClass);
new ClassSymbolCreator(cd.sym).visitChildren(cd, null); new ClassSymbolCreator(cd.sym).visitChildren(cd, null);
} }
/** /**
* Useful method which adds a symbol to a map, checking to see * Useful method which adds a symbol to a map, checking to see
* that there is not already an entry with the same name. * that there is not already an entry with the same name.
* If a symbol with the same name exists, throws an exception. * If a symbol with the same name exists, throws an exception.
*/ */
public <S extends Symbol> void add(Map<String,S> map, S sym) { public <S extends Symbol> void add(Map<String, S> map, S sym) {
if (map.containsKey(sym.name)) if (map.containsKey(sym.name))
throw new SemanticFailure( throw new SemanticFailure(
Cause.DOUBLE_DECLARATION, Cause.DOUBLE_DECLARATION,
"Symbol '%s' was declared twice in the same scope", "Symbol '%s' was declared twice in the same scope",
sym.name); sym.name);
map.put(sym.name, sym); map.put(sym.name, sym);
} }
/** /**
* Creates symbols for all fields/constants/methods in a class. * Creates symbols for all fields/constants/methods in a class.
* Uses {@link MethodSymbolCreator} to create symbols for all * Uses {@link MethodSymbolCreator} to create symbols for all
* parameters and local variables to each method as well. * parameters and local variables to each method as well.
* Checks for duplicate members. * Checks for duplicate members.
*/ */
public class ClassSymbolCreator extends AstVisitor<Void, Void> { public class ClassSymbolCreator extends AstVisitor<Void, Void> {
final ClassSymbol classSym; final ClassSymbol classSym;
public ClassSymbolCreator(ClassSymbol classSym) { public ClassSymbolCreator(ClassSymbol classSym) {
this.classSym = classSym; this.classSym = classSym;
} }
@ -75,9 +75,11 @@ public class SymbolCreator extends Object {
@Override @Override
public Void methodDecl(MethodDecl ast, Void arg) { public Void methodDecl(MethodDecl ast, Void arg) {
ast.sym = new MethodSymbol(ast); ast.sym = new MethodSymbol(ast);
ast.sym.owner = classSym;
add(classSym.methods, ast.sym); add(classSym.methods, ast.sym);
// create return type symbol // create return type symbol
@ -86,13 +88,13 @@ public class SymbolCreator extends Object {
} else { } else {
ast.sym.returnType = typesTable.getType(ast.returnType); ast.sym.returnType = typesTable.getType(ast.returnType);
} }
// create symbols for each parameter // create symbols for each parameter
Set<String> pnames = new HashSet<String>(); Set<String> pnames = new HashSet<String>();
for (int i = 0; i < ast.argumentNames.size(); i++) { for (int i = 0; i < ast.argumentNames.size(); i++) {
String argumentName = ast.argumentNames.get(i); String argumentName = ast.argumentNames.get(i);
String argumentType = ast.argumentTypes.get(i); String argumentType = ast.argumentTypes.get(i);
if (pnames.contains(argumentName)) if (pnames.contains(argumentName))
throw new SemanticFailure( throw new SemanticFailure(
Cause.DOUBLE_DECLARATION, Cause.DOUBLE_DECLARATION,
"Method '%s' has two parameters named '%s'", "Method '%s' has two parameters named '%s'",
@ -102,23 +104,23 @@ public class SymbolCreator extends Object {
argumentName, typesTable.getType(argumentType)); argumentName, typesTable.getType(argumentType));
ast.sym.parameters.add(vs); ast.sym.parameters.add(vs);
} }
// create symbols for the local variables // create symbols for the local variables
new MethodSymbolCreator(ast.sym).visitChildren(ast.decls(), null); new MethodSymbolCreator(ast.sym).visitChildren(ast.decls(), null);
return null; return null;
} }
} }
public class MethodSymbolCreator extends AstVisitor<Void, Void> { public class MethodSymbolCreator extends AstVisitor<Void, Void> {
final MethodSymbol methodSym; final MethodSymbol methodSym;
public MethodSymbolCreator(MethodSymbol methodSym) { public MethodSymbolCreator(MethodSymbol methodSym) {
this.methodSym = methodSym; this.methodSym = methodSym;
} }
@Override @Override
public Void varDecl(VarDecl ast, Void arg) { public Void varDecl(VarDecl ast, Void arg) {
ast.sym = new VariableSymbol( ast.sym = new VariableSymbol(

View file

@ -11,7 +11,7 @@ import cd.util.debug.AstOneLine;
public class TypeChecker { public class TypeChecker {
final private SymTable<TypeSymbol> typeSymbols; final private SymTable<TypeSymbol> typeSymbols;
public TypeChecker(SymTable<TypeSymbol> typeSymbols) { public TypeChecker(SymTable<TypeSymbol> typeSymbols) {
this.typeSymbols = typeSymbols; this.typeSymbols = typeSymbols;
} }
@ -19,11 +19,12 @@ public class TypeChecker {
public void checkMethodDecl(MethodDecl method, SymTable<VariableSymbol> locals) { public void checkMethodDecl(MethodDecl method, SymTable<VariableSymbol> locals) {
new MethodDeclVisitor(method, locals).visit(method.body(), null); new MethodDeclVisitor(method, locals).visit(method.body(), null);
} }
/** /**
* Checks whether two expressions have the same type * Checks whether two expressions have the same type
* and throws an exception otherwise. * and throws an exception otherwise.
*
* @return The common type of the two expression. * @return The common type of the two expression.
*/ */
private TypeSymbol checkTypesEqual(Expr leftExpr, Expr rightExpr, SymTable<VariableSymbol> locals) { private TypeSymbol checkTypesEqual(Expr leftExpr, Expr rightExpr, SymTable<VariableSymbol> locals) {
@ -32,30 +33,30 @@ public class TypeChecker {
if (leftType != rightType) { if (leftType != rightType) {
throw new SemanticFailure( throw new SemanticFailure(
Cause.TYPE_ERROR, Cause.TYPE_ERROR,
"Expected operand types to be equal but found %s, %s", "Expected operand types to be equal but found %s, %s",
leftType, leftType,
rightType); rightType);
} }
return leftType; return leftType;
} }
private void checkTypeIsInt(TypeSymbol type) { private void checkTypeIsInt(TypeSymbol type) {
if (type != PrimitiveTypeSymbol.intType) { if (type != PrimitiveTypeSymbol.intType) {
throw new SemanticFailure( throw new SemanticFailure(
Cause.TYPE_ERROR, Cause.TYPE_ERROR,
"Expected %s for operands but found type %s", "Expected %s for operands but found type %s",
PrimitiveTypeSymbol.intType, PrimitiveTypeSymbol.intType,
type); type);
} }
} }
private ClassSymbol asClassSymbol(TypeSymbol type) { private ClassSymbol asClassSymbol(TypeSymbol type) {
if (type instanceof ClassSymbol) if (type instanceof ClassSymbol)
return (ClassSymbol) type; return (ClassSymbol) type;
throw new SemanticFailure( throw new SemanticFailure(
Cause.TYPE_ERROR, Cause.TYPE_ERROR,
"A class type was required, but %s was found", type); "A class type was required, but %s was found", type);
} }
@ -63,10 +64,10 @@ public class TypeChecker {
if (type instanceof ArrayTypeSymbol) if (type instanceof ArrayTypeSymbol)
return (ArrayTypeSymbol) type; return (ArrayTypeSymbol) type;
throw new SemanticFailure( throw new SemanticFailure(
Cause.TYPE_ERROR, Cause.TYPE_ERROR,
"An array type was required, but %s was found", type); "An array type was required, but %s was found", type);
} }
private TypeSymbol typeExpr(Expr expr, SymTable<VariableSymbol> locals) { private TypeSymbol typeExpr(Expr expr, SymTable<VariableSymbol> locals) {
return new TypingVisitor().visit(expr, locals); return new TypingVisitor().visit(expr, locals);
} }
@ -80,9 +81,9 @@ public class TypeChecker {
expected, expected,
actual); actual);
} }
private class MethodDeclVisitor extends AstVisitor<Void, Void> { private class MethodDeclVisitor extends AstVisitor<Void, Void> {
private MethodDecl method; private MethodDecl method;
private SymTable<VariableSymbol> locals; private SymTable<VariableSymbol> locals;
@ -95,7 +96,7 @@ public class TypeChecker {
protected Void dfltExpr(Expr ast, Void arg) { protected Void dfltExpr(Expr ast, Void arg) {
throw new RuntimeException("Should not get here"); throw new RuntimeException("Should not get here");
} }
@Override @Override
public Void assign(Assign ast, Void arg) { public Void assign(Assign ast, Void arg) {
TypeSymbol lhs = typeLhs(ast.left(), locals); TypeSymbol lhs = typeLhs(ast.left(), locals);
@ -113,15 +114,15 @@ public class TypeChecker {
checkType(ast.arg(), PrimitiveTypeSymbol.intType, locals); checkType(ast.arg(), PrimitiveTypeSymbol.intType, locals);
return null; return null;
} }
@Override @Override
public Void builtInWriteln(BuiltInWriteln ast, Void arg) { public Void builtInWriteln(BuiltInWriteln ast, Void arg) {
return null; return null;
} }
@Override @Override
public Void ifElse(IfElse ast, Void arg) { public Void ifElse(IfElse ast, Void arg) {
checkType((Expr)ast.condition(), PrimitiveTypeSymbol.booleanType, locals); checkType((Expr) ast.condition(), PrimitiveTypeSymbol.booleanType, locals);
visit(ast.then(), arg); visit(ast.then(), arg);
if (ast.otherwise() != null) if (ast.otherwise() != null)
visit(ast.otherwise(), arg); visit(ast.otherwise(), arg);
@ -131,17 +132,17 @@ public class TypeChecker {
@Override @Override
public Void methodCall(MethodCall ast, Void arg) { public Void methodCall(MethodCall ast, Void arg) {
typeExpr(ast.getMethodCallExpr(), locals); typeExpr(ast.getMethodCallExpr(), locals);
return null; return null;
} }
@Override @Override
public Void whileLoop(WhileLoop ast, Void arg) { public Void whileLoop(WhileLoop ast, Void arg) {
checkType((Expr)ast.condition(), PrimitiveTypeSymbol.booleanType, locals); checkType((Expr) ast.condition(), PrimitiveTypeSymbol.booleanType, locals);
return visit(ast.body(), arg); return visit(ast.body(), arg);
} }
@Override @Override
public Void returnStmt(ReturnStmt ast, Void arg) { public Void returnStmt(ReturnStmt ast, Void arg) {
boolean hasArg = ast.arg() != null; boolean hasArg = ast.arg() != null;
@ -150,11 +151,11 @@ public class TypeChecker {
throw new SemanticFailure( throw new SemanticFailure(
Cause.TYPE_ERROR, Cause.TYPE_ERROR,
"Return statement of method with void return type should " "Return statement of method with void return type should "
+ "not have arguments."); + "not have arguments.");
} else if (!hasArg) { } else if (!hasArg) {
// X m() { return; } // X m() { return; }
if (method.sym.returnType != PrimitiveTypeSymbol.voidType) { if (method.sym.returnType != PrimitiveTypeSymbol.voidType) {
throw new SemanticFailure( throw new SemanticFailure(
Cause.TYPE_ERROR, Cause.TYPE_ERROR,
"Return statement has no arguments. Expected %s but type was %s", "Return statement has no arguments. Expected %s but type was %s",
method.sym.returnType, method.sym.returnType,
@ -164,58 +165,58 @@ public class TypeChecker {
// X m() { return y; } // X m() { return y; }
checkType(ast.arg(), method.sym.returnType, locals); checkType(ast.arg(), method.sym.returnType, locals);
} }
return null; return null;
} }
} }
private class TypingVisitor extends ExprVisitor<TypeSymbol, SymTable<VariableSymbol>> { private class TypingVisitor extends ExprVisitor<TypeSymbol, SymTable<VariableSymbol>> {
@Override @Override
public TypeSymbol visit(Expr ast, SymTable<VariableSymbol> arg) { public TypeSymbol visit(Expr ast, SymTable<VariableSymbol> arg) {
ast.type = super.visit(ast, arg); ast.type = super.visit(ast, arg);
return ast.type; return ast.type;
} }
@Override @Override
public TypeSymbol binaryOp(BinaryOp ast, SymTable<VariableSymbol> locals) { public TypeSymbol binaryOp(BinaryOp ast, SymTable<VariableSymbol> locals) {
switch (ast.operator) { switch (ast.operator) {
case B_TIMES: case B_TIMES:
case B_DIV: case B_DIV:
case B_MOD: case B_MOD:
case B_PLUS: case B_PLUS:
case B_MINUS: case B_MINUS:
TypeSymbol type = checkTypesEqual(ast.left(), ast.right(), locals); TypeSymbol type = checkTypesEqual(ast.left(), ast.right(), locals);
checkTypeIsInt(type); checkTypeIsInt(type);
return type; return type;
case B_AND: case B_AND:
case B_OR: case B_OR:
checkType(ast.left(), PrimitiveTypeSymbol.booleanType, locals); checkType(ast.left(), PrimitiveTypeSymbol.booleanType, locals);
checkType(ast.right(), PrimitiveTypeSymbol.booleanType, locals); checkType(ast.right(), PrimitiveTypeSymbol.booleanType, locals);
return PrimitiveTypeSymbol.booleanType; return PrimitiveTypeSymbol.booleanType;
case B_EQUAL: case B_EQUAL:
case B_NOT_EQUAL: case B_NOT_EQUAL:
TypeSymbol left = typeExpr(ast.left(), locals); TypeSymbol left = typeExpr(ast.left(), locals);
TypeSymbol right = typeExpr(ast.right(), locals); TypeSymbol right = typeExpr(ast.right(), locals);
if (left.isSuperTypeOf(right) || right.isSuperTypeOf(left)) if (left.isSuperTypeOf(right) || right.isSuperTypeOf(left))
return PrimitiveTypeSymbol.booleanType; return PrimitiveTypeSymbol.booleanType;
throw new SemanticFailure( throw new SemanticFailure(
Cause.TYPE_ERROR, Cause.TYPE_ERROR,
"Types %s and %s could never be equal", "Types %s and %s could never be equal",
left, right); left, right);
case B_LESS_THAN: case B_LESS_THAN:
case B_LESS_OR_EQUAL: case B_LESS_OR_EQUAL:
case B_GREATER_THAN: case B_GREATER_THAN:
case B_GREATER_OR_EQUAL: case B_GREATER_OR_EQUAL:
checkTypeIsInt(checkTypesEqual(ast.left(), ast.right(), locals)); checkTypeIsInt(checkTypesEqual(ast.left(), ast.right(), locals));
return PrimitiveTypeSymbol.booleanType; return PrimitiveTypeSymbol.booleanType;
} }
throw new RuntimeException("Unhandled operator "+ast.operator); throw new RuntimeException("Unhandled operator " + ast.operator);
} }
@Override @Override
@ -232,10 +233,10 @@ public class TypeChecker {
public TypeSymbol cast(Cast ast, SymTable<VariableSymbol> locals) { public TypeSymbol cast(Cast ast, SymTable<VariableSymbol> locals) {
TypeSymbol argType = typeExpr(ast.arg(), locals); TypeSymbol argType = typeExpr(ast.arg(), locals);
ast.type = typeSymbols.getType(ast.typeName); ast.type = typeSymbols.getType(ast.typeName);
if (argType.isSuperTypeOf(ast.type) || ast.type.isSuperTypeOf(argType)) if (argType.isSuperTypeOf(ast.type) || ast.type.isSuperTypeOf(argType))
return ast.type; return ast.type;
throw new SemanticFailure( throw new SemanticFailure(
Cause.TYPE_ERROR, Cause.TYPE_ERROR,
"Types %s and %s in cast are completely unrelated.", "Types %s and %s in cast are completely unrelated.",
@ -295,21 +296,20 @@ public class TypeChecker {
@Override @Override
public TypeSymbol unaryOp(UnaryOp ast, SymTable<VariableSymbol> locals) { public TypeSymbol unaryOp(UnaryOp ast, SymTable<VariableSymbol> locals) {
switch (ast.operator) { switch (ast.operator) {
case U_PLUS: case U_PLUS:
case U_MINUS: case U_MINUS: {
{ TypeSymbol type = typeExpr(ast.arg(), locals);
TypeSymbol type = typeExpr(ast.arg(), locals); checkTypeIsInt(type);
checkTypeIsInt(type); return type;
return type; }
case U_BOOL_NOT:
checkType(ast.arg(), PrimitiveTypeSymbol.booleanType, locals);
return PrimitiveTypeSymbol.booleanType;
} }
throw new RuntimeException("Unknown unary op " + ast.operator);
case U_BOOL_NOT:
checkType(ast.arg(), PrimitiveTypeSymbol.booleanType, locals);
return PrimitiveTypeSymbol.booleanType;
}
throw new RuntimeException("Unknown unary op "+ast.operator);
} }
@Override @Override
@ -322,20 +322,20 @@ public class TypeChecker {
ast.setSymbol(locals.get(ast.name)); ast.setSymbol(locals.get(ast.name));
return ast.sym.type; return ast.sym.type;
} }
@Override @Override
public TypeSymbol methodCall(MethodCallExpr ast, SymTable<VariableSymbol> locals) { public TypeSymbol methodCall(MethodCallExpr ast, SymTable<VariableSymbol> locals) {
ClassSymbol rcvrType = asClassSymbol(typeExpr(ast.receiver(), locals)); ClassSymbol rcvrType = asClassSymbol(typeExpr(ast.receiver(), locals));
MethodSymbol mthd = rcvrType.getMethod(ast.methodName); MethodSymbol mthd = rcvrType.getMethod(ast.methodName);
if (mthd == null) if (mthd == null)
throw new SemanticFailure( throw new SemanticFailure(
Cause.NO_SUCH_METHOD, Cause.NO_SUCH_METHOD,
"Class %s has no method %s()", "Class %s has no method %s()",
rcvrType.name, ast.methodName); rcvrType.name, ast.methodName);
ast.sym = mthd; ast.sym = mthd;
// Check that the number of arguments is correct. // Check that the number of arguments is correct.
if (ast.argumentsWithoutReceiver().size() != mthd.parameters.size()) if (ast.argumentsWithoutReceiver().size() != mthd.parameters.size())
throw new SemanticFailure( throw new SemanticFailure(
@ -344,33 +344,36 @@ public class TypeChecker {
ast.methodName, ast.methodName,
mthd.parameters.size(), mthd.parameters.size(),
ast.argumentsWithoutReceiver().size()); ast.argumentsWithoutReceiver().size());
// Check that the arguments are of correct type. // Check that the arguments are of correct type.
int i = 0; int i = 0;
for (Ast argAst : ast.argumentsWithoutReceiver()) for (Ast argAst : ast.argumentsWithoutReceiver())
checkType((Expr)argAst, mthd.parameters.get(i++).type, locals); checkType((Expr) argAst, mthd.parameters.get(i++).type, locals);
return ast.sym.returnType; return ast.sym.returnType;
} }
} }
/** /**
* Checks an expr as the left-hand-side of an assignment, * Checks an expr as the left-hand-side of an assignment,
* returning the type of value that may be assigned there. * returning the type of value that may be assigned there.
* May fail if the expression is not a valid LHS (for example, * May fail if the expression is not a valid LHS (for example,
* a "final" field). */ * a "final" field).
*/
private TypeSymbol typeLhs(Expr expr, SymTable<VariableSymbol> locals) { private TypeSymbol typeLhs(Expr expr, SymTable<VariableSymbol> locals) {
return new LValueVisitor().visit(expr, locals); return new LValueVisitor().visit(expr, locals);
} }
/** /**
* @see TypeChecker#typeLhs(Expr, SymTable) * @see TypeChecker#typeLhs(Expr, SymTable)
*/ */
private class LValueVisitor extends ExprVisitor<TypeSymbol, SymTable<VariableSymbol>> { private class LValueVisitor extends ExprVisitor<TypeSymbol, SymTable<VariableSymbol>> {
/** Fields, array-indexing, and vars can be on the LHS: */ /**
* Fields, array-indexing, and vars can be on the LHS:
*/
@Override @Override
public TypeSymbol field(Field ast, SymTable<VariableSymbol> locals) { public TypeSymbol field(Field ast, SymTable<VariableSymbol> locals) {
return typeExpr(ast, locals); return typeExpr(ast, locals);
@ -385,13 +388,15 @@ public class TypeChecker {
public TypeSymbol var(Var ast, SymTable<VariableSymbol> locals) { public TypeSymbol var(Var ast, SymTable<VariableSymbol> locals) {
return typeExpr(ast, locals); return typeExpr(ast, locals);
} }
/** Any other kind of expression is not a value lvalue */ /**
* Any other kind of expression is not a value lvalue
*/
@Override @Override
protected TypeSymbol dfltExpr(Expr ast, SymTable<VariableSymbol> locals) { protected TypeSymbol dfltExpr(Expr ast, SymTable<VariableSymbol> locals) {
throw new SemanticFailure( throw new SemanticFailure(
Cause.NOT_ASSIGNABLE, Cause.NOT_ASSIGNABLE,
"'%s' is not a valid lvalue", "'%s' is not a valid lvalue",
AstOneLine.toString(ast)); AstOneLine.toString(ast));
} }
} }

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,51 @@
package cd.ir;
import cd.ir.Ast.Expr;
import cd.ir.Symbol.PrimitiveTypeSymbol;
import java.util.Map;
/**
* A visitor that replaces AST nodes in toBeReplaced
* used in ConstantAnalysis, deletes all the variables that have a constant value and replace them with a constant
*/
public class AstReplaceVisitor extends AstRewriteVisitor<Ast> {
// use this information to replace each Var node with it's correct constant value
public Map<Ast.Expr, Ast.LeafExpr> toBeReplaced;
public Map<Expr, Integer> initializePositions;
public int currentPosition;
public Ast.LeafExpr getReplacement(Ast.Var arg) {
// Var was declared constant but initialization happens later
// replace it with IntConst or BooleanConst and their default value
if (toBeReplaced.containsKey(arg) && currentPosition <= initializePositions.get(arg)) {
//if (toBeReplaced.containsKey(arg)) {
Ast.LeafExpr leafEpr = (Ast.LeafExpr) arg;
if (leafEpr.type == PrimitiveTypeSymbol.intType) {
return new Ast.IntConst(0);
} else {
return new Ast.BooleanConst(false);
}
}
// Var was declared constant and initialization already happened
else if (toBeReplaced.containsKey(arg) && currentPosition > initializePositions.get(arg)) {
return toBeReplaced.get(arg);
}
// Var was not declared to be a constant
else {
return arg;
}
}
public Ast var(Ast.Var ast, Void arg) {
return getReplacement(ast);
}
}

View file

@ -19,13 +19,14 @@ public class AstRewriteVisitor<A> extends AstVisitor<Ast, A> {
} }
return ast; return ast;
} }
/** /**
* This method is called when a node is replaced. Subclasses can override it to do some * This method is called when a node is replaced. Subclasses can override it to do some
* bookkeeping. * bookkeeping.
* <p> * <p>
* The default implementation does nothing. * The default implementation does nothing.
*/ */
protected void nodeReplaced(Ast oldNode, Ast newNode) {} protected void nodeReplaced(Ast oldNode, Ast newNode) {
}
} }

View file

@ -4,18 +4,21 @@ import cd.ir.Ast.Decl;
import cd.ir.Ast.Expr; import cd.ir.Ast.Expr;
import cd.ir.Ast.Stmt; import cd.ir.Ast.Stmt;
/** A visitor that visits any kind of node */ /**
public class AstVisitor<R,A> extends ExprVisitor<R,A> { * A visitor that visits any kind of node
*/
/** public class AstVisitor<R, A> extends ExprVisitor<R, A> {
* Recurse and process {@code ast}. It is preferred to
/**
* Recurse and process {@code ast}. It is preferred to
* call this rather than calling accept directly, since * call this rather than calling accept directly, since
* it can be overloaded to introduce memoization, * it can be overloaded to introduce memoization,
* for example. */ * for example.
*/
public R visit(Ast ast, A arg) { public R visit(Ast ast, A arg) {
return ast.accept(this, arg); return ast.accept(this, arg);
} }
/** /**
* Overrides {@link ExprVisitor#visitChildren(Expr, Object)} and * Overrides {@link ExprVisitor#visitChildren(Expr, Object)} and
* delegates to the more general {@link #visitChildren(Ast, Object)} * delegates to the more general {@link #visitChildren(Ast, Object)}
@ -26,11 +29,11 @@ public class AstVisitor<R,A> extends ExprVisitor<R,A> {
public final R visitChildren(Expr ast, A arg) { public final R visitChildren(Expr ast, A arg) {
return visitChildren((Ast) ast, arg); return visitChildren((Ast) ast, arg);
} }
/** /**
* A handy function which visits the children of {@code ast}, * A handy function which visits the children of {@code ast},
* providing "arg" to each of them. It returns the result of * providing "arg" to each of them. It returns the result of
* the last child in the list. It is invoked by the method * the last child in the list. It is invoked by the method
* {@link #dflt(Ast, Object)} by default. * {@link #dflt(Ast, Object)} by default.
*/ */
public R visitChildren(Ast ast, A arg) { public R visitChildren(Ast ast, A arg) {
@ -39,35 +42,39 @@ public class AstVisitor<R,A> extends ExprVisitor<R,A> {
lastValue = visit(child, arg); lastValue = visit(child, arg);
return lastValue; return lastValue;
} }
/** /**
* The default action for default actions is to call this, * The default action for default actions is to call this,
* which simply recurses to any children. Also called * which simply recurses to any children. Also called
* by seq() by default. */ * by seq() by default.
*/
protected R dflt(Ast ast, A arg) { protected R dflt(Ast ast, A arg) {
return visitChildren(ast, arg); return visitChildren(ast, arg);
} }
/** /**
* The default action for statements is to call this */ * The default action for statements is to call this
*/
protected R dfltStmt(Stmt ast, A arg) { protected R dfltStmt(Stmt ast, A arg) {
return dflt(ast, arg); return dflt(ast, arg);
} }
/** /**
* The default action for expressions is to call this */ * The default action for expressions is to call this
*/
@Override @Override
protected R dfltExpr(Expr ast, A arg) { protected R dfltExpr(Expr ast, A arg) {
return dflt(ast, arg); return dflt(ast, arg);
} }
/** /**
* The default action for AST nodes representing declarations * The default action for AST nodes representing declarations
* is to call this function */ * is to call this function
*/
protected R dfltDecl(Decl ast, A arg) { protected R dfltDecl(Decl ast, A arg) {
return dflt(ast, arg); return dflt(ast, arg);
} }
public R assign(Ast.Assign ast, A arg) { public R assign(Ast.Assign ast, A arg) {
return dfltStmt(ast, arg); return dfltStmt(ast, arg);
} }
@ -79,23 +86,23 @@ public class AstVisitor<R,A> extends ExprVisitor<R,A> {
public R builtInWriteln(Ast.BuiltInWriteln ast, A arg) { public R builtInWriteln(Ast.BuiltInWriteln ast, A arg) {
return dfltStmt(ast, arg); return dfltStmt(ast, arg);
} }
public R classDecl(Ast.ClassDecl ast, A arg) { public R classDecl(Ast.ClassDecl ast, A arg) {
return dfltDecl(ast, arg); return dfltDecl(ast, arg);
} }
public R methodDecl(Ast.MethodDecl ast, A arg) { public R methodDecl(Ast.MethodDecl ast, A arg) {
return dfltDecl(ast, arg); return dfltDecl(ast, arg);
} }
public R varDecl(Ast.VarDecl ast, A arg) { public R varDecl(Ast.VarDecl ast, A arg) {
return dfltDecl(ast, arg); return dfltDecl(ast, arg);
} }
public R ifElse(Ast.IfElse ast, A arg) { public R ifElse(Ast.IfElse ast, A arg) {
return dfltStmt(ast, arg); return dfltStmt(ast, arg);
} }
public R returnStmt(Ast.ReturnStmt ast, A arg) { public R returnStmt(Ast.ReturnStmt ast, A arg) {
return dfltStmt(ast, arg); return dfltStmt(ast, arg);
} }
@ -107,11 +114,11 @@ public class AstVisitor<R,A> extends ExprVisitor<R,A> {
public R nop(Ast.Nop ast, A arg) { public R nop(Ast.Nop ast, A arg) {
return dfltStmt(ast, arg); return dfltStmt(ast, arg);
} }
public R seq(Ast.Seq ast, A arg) { public R seq(Ast.Seq ast, A arg) {
return dflt(ast, arg); return dflt(ast, arg);
} }
public R whileLoop(Ast.WhileLoop ast, A arg) { public R whileLoop(Ast.WhileLoop ast, A arg) {
return dfltStmt(ast, arg); return dfltStmt(ast, arg);
} }

113
src/cd/ir/BasicBlock.java Normal file
View file

@ -0,0 +1,113 @@
package cd.ir;
import cd.ir.Ast.Expr;
import cd.ir.Ast.Stmt;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* Node in a control flow graph. New instances should be created
* via the methods in {@link ControlFlowGraph}.
* Basic blocks consist of a list of statements ({@link #stmts}) which are
* executed at runtime. When the basic block ends, control flows into its
* {@link #successors}. If the block has more than one successor, it must also
* have a non-{@code null} value for {@link #condition}, which describes an expression
* that will determine which successor to take. Basic blocks also have fields
* for storing the parent and children in the dominator tree. These are generally computed
* in a second pass once the graph is fully built.
* <p>
* Your team will have to write code that builds the control flow graph and computes the
* relevant dominator information.
*/
public class BasicBlock {
/**
* Unique numerical index assigned by CFG builder between 0 and the total number of
* basic blocks. Useful for indexing into arrays and the like.
*/
public final int index;
/**
* List of predecessor blocks in the flow graph (i.e., blocks for
* which {@code this} is a successor).
*/
public final List<BasicBlock> predecessors = new ArrayList<BasicBlock>();
/**
* List of successor blocks in the flow graph (those that come after the
* current block). This list is always either of size 0, 1 or 2: 1 indicates
* that control flow continues directly into the next block, and 2 indicates
* that control flow goes in one of two directions, depending on the
* value that results when {@link #condition} is evaluated at runtime.
* If there are two successors, then the 0th entry is taken when {@code condition}
* evaluates to {@code true}.
*
* @see #trueSuccessor()
* @see #falseSuccessor()
*/
public final List<BasicBlock> successors = new ArrayList<BasicBlock>();
/**
* List of statements in this basic block.
*/
public final List<Stmt> stmts = new ArrayList<>();
/**
* If non-null, indicates that this basic block should have
* two successors. Control flows to the first successor if
* this condition evaluates at runtime to true, otherwise to
* the second successor. If null, the basic block should have
* only one successor.
*/
public Expr condition;
/**
* Parent of this basic block in the dominator tree (initially null until computed).
* Otherwise known as the immediate dominator.
*/
public BasicBlock dominatorTreeParent = null;
/**
* Children of this basic block in the dominator tree (initially empty until
* computed).
*/
public final List<BasicBlock> dominatorTreeChildren = new ArrayList<BasicBlock>();
/**
* Contains the dominance frontier of this block. A block b is in the dominance
* frontier of another block c if c does not dominate b, but c DOES dominate a
* predecessor of b.
*/
public final Set<BasicBlock> dominanceFrontier = new HashSet<BasicBlock>();
public BasicBlock(int index) {
this.index = index;
}
public BasicBlock trueSuccessor() {
assert this.condition != null;
return this.successors.get(0);
}
public BasicBlock falseSuccessor() {
assert this.condition != null;
return this.successors.get(1);
}
public <A, B> A accept(AstVisitor<A, B> visitor, B arg) {
A lastA = null;
for (Stmt stmt : stmts)
lastA = visitor.visit(stmt, arg);
if (condition != null)
lastA = visitor.visit(condition, arg);
return lastA;
}
@Override
public String toString() {
return "BB" + index;
}
}

View file

@ -0,0 +1,135 @@
package cd.ir;
import cd.ir.Ast.BinaryOp;
import cd.ir.Ast.BooleanConst;
import cd.ir.Ast.Expr;
import cd.ir.Ast.UnaryOp;
import cd.ir.Symbol.VariableSymbol;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import static cd.Config.FALSE;
import static cd.Config.TRUE;
/**
* A visitor that only visits {@link Expr} nodes.
* calculates the compile time value of an rhs in an assignment statement or the boolean value of a condition
*/
public class CompileTimeEvaluator extends ExprVisitor<Integer, Map<VariableSymbol, Integer>> {
// if the expression contains non constant variables, field or index; or if the expression is a read()
// no compile Time value can be computed
private boolean failedToEvaluate;
public Optional<Integer> calc(Ast.Expr ast) {
return calc(ast, new HashMap<>());
}
public Optional<Integer> calc(Ast.Expr ast, Map<VariableSymbol, Integer> constantMap) {
try {
failedToEvaluate = false;
Integer result = visit(ast, constantMap);
if (failedToEvaluate)
return Optional.empty();
else
return Optional.of(result);
} catch (ArithmeticException e) {
return Optional.empty();
}
}
@Override
protected Integer dfltExpr(Expr ast, Map<VariableSymbol, Integer> arg) {
failedToEvaluate = true;
return Integer.MIN_VALUE;
}
@Override
public Integer binaryOp(BinaryOp ast, Map<VariableSymbol, Integer> arg) {
boolean tempResult;
int left = visit(ast.left(), arg);
int right = visit(ast.right(), arg);
switch (ast.operator) {
case B_TIMES:
return left * right;
case B_PLUS:
return left + right;
case B_MINUS:
return left - right;
case B_DIV:
return left / right;
case B_MOD:
return left % right;
case B_AND:
tempResult = (left == TRUE) && (right == TRUE);
break;
case B_OR:
tempResult = (left == TRUE) || (right == TRUE);
break;
case B_EQUAL:
tempResult = left == right;
break;
case B_NOT_EQUAL:
tempResult = left != right;
break;
case B_LESS_THAN:
tempResult = left < right;
break;
case B_LESS_OR_EQUAL:
tempResult = left <= right;
break;
case B_GREATER_THAN:
tempResult = left > right;
break;
case B_GREATER_OR_EQUAL:
tempResult = left >= right;
break;
default:
throw new RuntimeException("Invalid binary operator");
}
return tempResult ? TRUE : FALSE;
}
@Override
public Integer unaryOp(UnaryOp ast, Map<VariableSymbol, Integer> arg) {
int result = visit(ast.arg(), arg);
switch (ast.operator) {
case U_PLUS:
return result;
case U_MINUS:
return -result;
case U_BOOL_NOT:
return result == FALSE ? TRUE : FALSE;
default:
throw new RuntimeException("Invalid unary operator");
}
}
@Override
public Integer booleanConst(BooleanConst ast, Map<VariableSymbol, Integer> arg) {
return ast.value ? TRUE : FALSE;
}
@Override
public Integer intConst(Ast.IntConst ast, Map<VariableSymbol, Integer> arg) {
return ast.value;
}
// check if a given Variable has a constant value
@Override
public Integer var(Ast.Var ast, Map<VariableSymbol, Integer> arg) {
if (arg.containsKey(ast.sym)) {
return arg.get(ast.sym);
} else {
failedToEvaluate = true;
return Integer.MIN_VALUE;
}
}
}

View file

@ -0,0 +1,61 @@
package cd.ir;
import cd.ir.Ast.Expr;
import java.util.ArrayList;
import java.util.List;
/**
* Represents the control flow graph of a single method.
*/
public class ControlFlowGraph {
public BasicBlock start, end;
public final List<BasicBlock> allBlocks = new ArrayList<BasicBlock>();
public int count() {
return allBlocks.size();
}
public BasicBlock newBlock() {
BasicBlock blk = new BasicBlock(count());
allBlocks.add(blk);
return blk;
}
/**
* Given a list of basic blocks that do not yet have successors,
* merges their control flows into a single successor and returns
* the new successor.
*/
public BasicBlock join(BasicBlock... pred) {
BasicBlock result = newBlock();
for (BasicBlock p : pred) {
assert p.condition == null;
assert p.successors.size() == 0;
p.successors.add(result);
result.predecessors.add(p);
}
return result;
}
/**
* Terminates {@code blk} so that it evaluates {@code cond},
* and creates two new basic blocks, one for the case where
* the result is true, and one for the case where the result is
* false.
*/
public void terminateInCondition(BasicBlock blk, Expr cond) {
assert blk.condition == null;
assert blk.successors.size() == 0;
blk.condition = cond;
blk.successors.add(newBlock());
blk.successors.add(newBlock());
blk.trueSuccessor().predecessors.add(blk);
blk.falseSuccessor().predecessors.add(blk);
}
public void connect(BasicBlock from, BasicBlock to) {
to.predecessors.add(from);
from.successors.add(to);
}
}

View file

@ -5,16 +5,17 @@ import cd.ir.Ast.Expr;
/** /**
* A visitor that only visits {@link Expr} nodes. * A visitor that only visits {@link Expr} nodes.
*/ */
public class ExprVisitor<R,A> { public class ExprVisitor<R, A> {
/** /**
* Recurse and process {@code ast}. It is preferred to * Recurse and process {@code ast}. It is preferred to
* call this rather than calling accept directly, since * call this rather than calling accept directly, since
* it can be overloaded to introduce memoization, * it can be overloaded to introduce memoization,
* for example. */ * for example.
*/
public R visit(Expr ast, A arg) { public R visit(Expr ast, A arg) {
return ast.accept(this, arg); return ast.accept(this, arg);
} }
/** /**
* Visits all children of the expression. Relies on the fact * Visits all children of the expression. Relies on the fact
* that {@link Expr} nodes only contain other {@link Expr} nodes. * that {@link Expr} nodes only contain other {@link Expr} nodes.
@ -22,18 +23,19 @@ public class ExprVisitor<R,A> {
public R visitChildren(Expr ast, A arg) { public R visitChildren(Expr ast, A arg) {
R lastValue = null; R lastValue = null;
for (Ast child : ast.children()) for (Ast child : ast.children())
lastValue = visit((Expr)child, arg); lastValue = visit((Expr) child, arg);
return lastValue; return lastValue;
} }
/** /**
* The default action for default actions is to call this, * The default action for default actions is to call this,
* which simply recurses to any children. Also called * which simply recurses to any children. Also called
* by seq() by default. */ * by seq() by default.
*/
protected R dfltExpr(Expr ast, A arg) { protected R dfltExpr(Expr ast, A arg) {
return visitChildren(ast, arg); return visitChildren(ast, arg);
} }
public R binaryOp(Ast.BinaryOp ast, A arg) { public R binaryOp(Ast.BinaryOp ast, A arg) {
return dfltExpr(ast, arg); return dfltExpr(ast, arg);
} }
@ -41,15 +43,15 @@ public class ExprVisitor<R,A> {
public R booleanConst(Ast.BooleanConst ast, A arg) { public R booleanConst(Ast.BooleanConst ast, A arg) {
return dfltExpr(ast, arg); return dfltExpr(ast, arg);
} }
public R builtInRead(Ast.BuiltInRead ast, A arg) { public R builtInRead(Ast.BuiltInRead ast, A arg) {
return dfltExpr(ast, arg); return dfltExpr(ast, arg);
} }
public R cast(Ast.Cast ast, A arg) { public R cast(Ast.Cast ast, A arg) {
return dfltExpr(ast, arg); return dfltExpr(ast, arg);
} }
public R field(Ast.Field ast, A arg) { public R field(Ast.Field ast, A arg) {
return dfltExpr(ast, arg); return dfltExpr(ast, arg);
} }
@ -61,7 +63,7 @@ public class ExprVisitor<R,A> {
public R intConst(Ast.IntConst ast, A arg) { public R intConst(Ast.IntConst ast, A arg) {
return dfltExpr(ast, arg); return dfltExpr(ast, arg);
} }
public R methodCall(Ast.MethodCallExpr ast, A arg) { public R methodCall(Ast.MethodCallExpr ast, A arg) {
return dfltExpr(ast, arg); return dfltExpr(ast, arg);
} }
@ -73,7 +75,7 @@ public class ExprVisitor<R,A> {
public R newArray(Ast.NewArray ast, A arg) { public R newArray(Ast.NewArray ast, A arg) {
return dfltExpr(ast, arg); return dfltExpr(ast, arg);
} }
public R nullConst(Ast.NullConst ast, A arg) { public R nullConst(Ast.NullConst ast, A arg) {
return dfltExpr(ast, arg); return dfltExpr(ast, arg);
} }

Some files were not shown because too many files have changed in this diff Show more