Homework B (benchmarks)
This commit is contained in:
parent
72cc3206c4
commit
76fbabdf53
141 changed files with 7540 additions and 2032 deletions
|
@ -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>
|
||||||
|
|
2
.project
2
.project
|
@ -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>
|
||||||
|
|
2
.settings/org.eclipse.core.resources.prefs
Normal file
2
.settings/org.eclipse.core.resources.prefs
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
eclipse.preferences.version=1
|
||||||
|
encoding/<project>=UTF-8
|
7
.settings/org.eclipse.jdt.core.prefs
Normal file
7
.settings/org.eclipse.jdt.core.prefs
Normal 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
|
54
Grade.txt
54
Grade.txt
|
@ -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
16
Javali tests.launch
Normal 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
111
README.md
|
@ -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
|
|
60
benchmarks/HWB/Quicksort.javali
Normal file
60
benchmarks/HWB/Quicksort.javali
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
10
benchmarks/HWB/Quicksort.javali.in
Normal file
10
benchmarks/HWB/Quicksort.javali.in
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
1
|
||||||
|
5
|
||||||
|
6
|
||||||
|
7
|
||||||
|
8
|
||||||
|
5
|
||||||
|
2
|
||||||
|
4
|
||||||
|
6
|
||||||
|
3
|
40
benchmarks/HWB_nop90/AFewConstants.javali
Normal file
40
benchmarks/HWB_nop90/AFewConstants.javali
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
5
benchmarks/HWB_nop90/AFewConstants.javali.in
Normal file
5
benchmarks/HWB_nop90/AFewConstants.javali.in
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
42
|
||||||
|
1337
|
||||||
|
9000
|
||||||
|
12345678
|
||||||
|
777
|
13
benchmarks/HWB_nop90/Unused.javali
Normal file
13
benchmarks/HWB_nop90/Unused.javali
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
1
benchmarks/HWB_nop90/Unused.javali.in
Normal file
1
benchmarks/HWB_nop90/Unused.javali.in
Normal file
|
@ -0,0 +1 @@
|
||||||
|
43
|
36
build.xml
36
build.xml
|
@ -8,19 +8,26 @@
|
||||||
<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>
|
||||||
|
|
||||||
|
<!-- Builds the compiler framework for HW > HW1. -->
|
||||||
<target name="compile">
|
<target name="compile">
|
||||||
<mkdir dir="${build.dir}"/>
|
<mkdir dir="${build.dir}"/>
|
||||||
|
|
||||||
|
@ -45,18 +52,30 @@
|
||||||
<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">
|
||||||
|
<jacoco:coverage destfile="${coverage.file}">
|
||||||
<junit fork="true" forkmode="once" failureproperty="tests-failed" outputtoformatters="false">
|
<junit fork="true" forkmode="once" failureproperty="tests-failed" outputtoformatters="false">
|
||||||
<formatter type="brief" usefile="false"/>
|
<formatter type="brief" usefile="false"/>
|
||||||
<batchtest skipNonTests="true">
|
<batchtest skipNonTests="true">
|
||||||
<fileset dir="bin" includes="**/*.class" />
|
<fileset dir="bin" includes="**/*.class" />
|
||||||
</batchtest>
|
</batchtest>
|
||||||
<assertions enablesystemassertions="true" />
|
<assertions enablesystemassertions="true" />
|
||||||
<sysproperty key="cd.meta_hidden.Version" value="REFERENCE" />
|
<sysproperty key="cd.meta_hidden.Version" value="BENCH" />
|
||||||
<classpath>
|
<classpath>
|
||||||
<pathelement location="${build.dir}"/>
|
<pathelement location="${build.dir}"/>
|
||||||
<pathelement location="${junit.jar}"/>
|
<pathelement location="${junit.jar}"/>
|
||||||
|
@ -65,7 +84,20 @@
|
||||||
<pathelement location="${parser.jar}"/>
|
<pathelement location="${parser.jar}"/>
|
||||||
</classpath>
|
</classpath>
|
||||||
</junit>
|
</junit>
|
||||||
|
</jacoco:coverage>
|
||||||
<fail if="tests-failed" />
|
<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
0
javali_tests/HW1_nop90/Division.javali
Executable file → Normal file
0
javali_tests/HW1_nop90/EightVariablesWrite.javali
Executable file → Normal file
0
javali_tests/HW1_nop90/EightVariablesWrite.javali
Executable file → Normal file
0
javali_tests/HW1_nop90/Multiplication.javali
Executable file → Normal file
0
javali_tests/HW1_nop90/Multiplication.javali
Executable file → Normal file
0
javali_tests/HW1_nop90/Overflow.javali
Executable file → Normal file
0
javali_tests/HW1_nop90/Overflow.javali
Executable file → Normal file
0
javali_tests/HW1_nop90/Overflow.javali.in
Executable file → Normal file
0
javali_tests/HW1_nop90/Overflow.javali.in
Executable file → Normal file
0
javali_tests/HW1_nop90/ReadWrite.javali
Executable file → Normal file
0
javali_tests/HW1_nop90/ReadWrite.javali
Executable file → Normal file
0
javali_tests/HW1_nop90/ReadWrite.javali.in
Executable file → Normal file
0
javali_tests/HW1_nop90/ReadWrite.javali.in
Executable file → Normal file
0
javali_tests/HW1_nop90/UnaryOperators.javali
Executable file → Normal file
0
javali_tests/HW1_nop90/UnaryOperators.javali
Executable file → Normal file
0
javali_tests/HW1_nop90/noParentheses.javali
Executable file → Normal file
0
javali_tests/HW1_nop90/noParentheses.javali
Executable file → Normal file
0
javali_tests/HW2_nop90/Casts.javali
Executable file → Normal file
0
javali_tests/HW2_nop90/Casts.javali
Executable file → Normal file
0
javali_tests/HW2_nop90/Division.javali
Executable file → Normal file
0
javali_tests/HW2_nop90/Division.javali
Executable file → Normal file
0
javali_tests/HW2_nop90/EightVariablesWrite.javali
Executable file → Normal file
0
javali_tests/HW2_nop90/EightVariablesWrite.javali
Executable file → Normal file
0
javali_tests/HW2_nop90/Multiplication.javali
Executable file → Normal file
0
javali_tests/HW2_nop90/Multiplication.javali
Executable file → Normal file
0
javali_tests/HW2_nop90/NULLTest.javali
Executable file → Normal file
0
javali_tests/HW2_nop90/NULLTest.javali
Executable file → Normal file
0
javali_tests/HW2_nop90/Overflow.javali
Executable file → Normal file
0
javali_tests/HW2_nop90/Overflow.javali
Executable file → Normal file
0
javali_tests/HW2_nop90/Overflow.javali.in
Executable file → Normal file
0
javali_tests/HW2_nop90/Overflow.javali.in
Executable file → Normal file
0
javali_tests/HW2_nop90/ReadWrite.javali
Executable file → Normal file
0
javali_tests/HW2_nop90/ReadWrite.javali
Executable file → Normal file
0
javali_tests/HW2_nop90/ReadWrite.javali.in
Executable file → Normal file
0
javali_tests/HW2_nop90/ReadWrite.javali.in
Executable file → Normal file
0
javali_tests/HW2_nop90/UnaryOperators.javali
Executable file → Normal file
0
javali_tests/HW2_nop90/UnaryOperators.javali
Executable file → Normal file
0
javali_tests/HW2_nop90/conditionExpressions.javali
Executable file → Normal file
0
javali_tests/HW2_nop90/conditionExpressions.javali
Executable file → Normal file
0
javali_tests/HW2_nop90/invalidCasts.javali
Executable file → Normal file
0
javali_tests/HW2_nop90/invalidCasts.javali
Executable file → Normal file
0
javali_tests/HW2_nop90/noParentheses.javali
Executable file → Normal file
0
javali_tests/HW2_nop90/noParentheses.javali
Executable file → Normal file
|
@ -24,5 +24,3 @@ class Main {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
0
javali_tests/HW4_nop90/Booleans/OkEquals2.javali
Executable file → Normal file
0
javali_tests/HW4_nop90/Booleans/OkEquals2.javali
Executable file → Normal file
0
javali_tests/HW4_nop90/Booleans/OkEquals3.javali
Executable file → Normal file
0
javali_tests/HW4_nop90/Booleans/OkEquals3.javali
Executable file → Normal file
0
javali_tests/HW4_nop90/Casts/ErrCastUnrelatedType.javali
Executable file → Normal file
0
javali_tests/HW4_nop90/Casts/ErrCastUnrelatedType.javali
Executable file → Normal file
0
javali_tests/HW4_nop90/Casts/ErrObjectToArrayCast.javali
Executable file → Normal file
0
javali_tests/HW4_nop90/Casts/ErrObjectToArrayCast.javali
Executable file → Normal file
0
javali_tests/HW4_nop90/Casts/ErrPrimitiveCast.javali
Executable file → Normal file
0
javali_tests/HW4_nop90/Casts/ErrPrimitiveCast.javali
Executable file → Normal file
0
javali_tests/HW4_nop90/Casts/ErrPrimitiveCast2.javali
Executable file → Normal file
0
javali_tests/HW4_nop90/Casts/ErrPrimitiveCast2.javali
Executable file → Normal file
0
javali_tests/HW4_nop90/Casts/OkArrayToObjectCast.javali
Executable file → Normal file
0
javali_tests/HW4_nop90/Casts/OkArrayToObjectCast.javali
Executable file → Normal file
0
javali_tests/HW4_nop90/Casts/OkArrayToObjectCast2.javali
Executable file → Normal file
0
javali_tests/HW4_nop90/Casts/OkArrayToObjectCast2.javali
Executable file → Normal file
0
javali_tests/HW4_nop90/Casts/OkSubtype.javali
Executable file → Normal file
0
javali_tests/HW4_nop90/Casts/OkSubtype.javali
Executable file → Normal file
0
javali_tests/HW4_nop90/Casts/OkTypeCast.javali
Executable file → Normal file
0
javali_tests/HW4_nop90/Casts/OkTypeCast.javali
Executable file → Normal file
0
javali_tests/HW4_nop90/Casts/OkTypeToObjectCast.javali
Executable file → Normal file
0
javali_tests/HW4_nop90/Casts/OkTypeToObjectCast.javali
Executable file → Normal file
0
javali_tests/HW4_nop90/EightVariablesWrite.javali
Executable file → Normal file
0
javali_tests/HW4_nop90/EightVariablesWrite.javali
Executable file → Normal file
0
javali_tests/HW4_nop90/ErrDivisionByZero.javali
Executable file → Normal file
0
javali_tests/HW4_nop90/ErrDivisionByZero.javali
Executable file → Normal file
0
javali_tests/HW4_nop90/Fields/OkObjectFields.javali
Executable file → Normal file
0
javali_tests/HW4_nop90/Fields/OkObjectFields.javali
Executable file → Normal file
|
@ -1 +1 @@
|
||||||
1
|
5
|
0
javali_tests/HW4_nop90/OkBoolBinaryOp.javali
Executable file → Normal file
0
javali_tests/HW4_nop90/OkBoolBinaryOp.javali
Executable file → Normal file
0
javali_tests/HW4_nop90/OkCasts.javali
Executable file → Normal file
0
javali_tests/HW4_nop90/OkCasts.javali
Executable file → Normal file
10
javali_tests/HW4_nop90/OkFalseInitialized.javali
Normal file
10
javali_tests/HW4_nop90/OkFalseInitialized.javali
Normal 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
0
javali_tests/HW4_nop90/OkNullAssignment.javali
Executable file → Normal 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
0
javali_tests/HW4_nop90/OkUnaryOperators.javali
Executable file → Normal file
0
javali_tests/HW4_nop90/OkVariables.javali
Executable file → Normal file
0
javali_tests/HW4_nop90/OkVariables.javali
Executable file → Normal file
0
javali_tests/HW4_nop90/OkZeroInitialized.javali
Executable file → Normal file
0
javali_tests/HW4_nop90/OkZeroInitialized.javali
Executable file → Normal file
0
javali_tests/HW4_nop90/ReturnTests/OkReturnObject.javali
Executable file → Normal file
0
javali_tests/HW4_nop90/ReturnTests/OkReturnObject.javali
Executable file → Normal file
0
javali_tests/HW4_nop90/method invocation/OkCallByValue.javali
Executable file → Normal file
0
javali_tests/HW4_nop90/method invocation/OkCallByValue.javali
Executable file → Normal file
0
javali_tests/HW4_nop90/method invocation/OkCallByValue2.javali
Executable file → Normal file
0
javali_tests/HW4_nop90/method invocation/OkCallByValue2.javali
Executable file → Normal file
0
javali_tests/HW4_nop90/method invocation/OkCallByValue3.javali
Executable file → Normal file
0
javali_tests/HW4_nop90/method invocation/OkCallByValue3.javali
Executable file → Normal file
0
javali_tests/HW4_nop90/method invocation/OkCallInheritedMethod.javali
Executable file → Normal file
0
javali_tests/HW4_nop90/method invocation/OkCallInheritedMethod.javali
Executable file → Normal file
0
javali_tests/HW4_nop90/method invocation/OkMethod.javali
Executable file → Normal file
0
javali_tests/HW4_nop90/method invocation/OkMethod.javali
Executable file → Normal file
0
javali_tests/HW4_nop90/method invocation/OkMethod2.javali
Executable file → Normal file
0
javali_tests/HW4_nop90/method invocation/OkMethod2.javali
Executable file → Normal file
60
javali_tests/HWB/Quicksort.javali
Normal file
60
javali_tests/HWB/Quicksort.javali
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
10
javali_tests/HWB/Quicksort.javali.in
Normal file
10
javali_tests/HWB/Quicksort.javali.in
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
1
|
||||||
|
5
|
||||||
|
6
|
||||||
|
7
|
||||||
|
8
|
||||||
|
5
|
||||||
|
2
|
||||||
|
4
|
||||||
|
6
|
||||||
|
3
|
|
@ -10,40 +10,63 @@ public class Config {
|
||||||
MACOSX
|
MACOSX
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static final int TRUE = 1;
|
||||||
|
public static final int FALSE = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Defines the extension used for assembler files on this platform.
|
* Defines the extension used for assembler files on this platform.
|
||||||
* Currently always {@code .s}.
|
* Currently always {@code .s}.
|
||||||
*/
|
*/
|
||||||
public static final String ASMEXT = ".s";
|
public static final String ASMEXT = ".s";
|
||||||
|
|
||||||
/** Defines the extension used for binary files on this platform. */
|
/**
|
||||||
|
* Defines the extension used for binary files on this platform.
|
||||||
|
*/
|
||||||
public static final String BINARYEXT;
|
public static final String BINARYEXT;
|
||||||
|
|
||||||
/** Defines the name of the main function to be used in .s file */
|
/**
|
||||||
|
* Defines the name of the main function to be used in .s file
|
||||||
|
*/
|
||||||
public static final String MAIN;
|
public static final String MAIN;
|
||||||
|
|
||||||
/** Defines the name of the printf function to be used in .s file */
|
/**
|
||||||
|
* Defines the name of the printf function to be used in .s file
|
||||||
|
*/
|
||||||
public static final String PRINTF;
|
public static final String PRINTF;
|
||||||
|
|
||||||
/** Defines the name of the scanf function to be used in .s file */
|
/**
|
||||||
|
* Defines the name of the scanf function to be used in .s file
|
||||||
|
*/
|
||||||
public static final String SCANF;
|
public static final String SCANF;
|
||||||
|
|
||||||
/** Defines the name of the calloc function to be used in .s file */
|
/**
|
||||||
|
* Defines the name of the calloc function to be used in .s file
|
||||||
|
*/
|
||||||
public static final String CALLOC;
|
public static final String CALLOC;
|
||||||
|
|
||||||
/** Defines the name of the exit function to be used in .s file */
|
/**
|
||||||
|
* Defines the name of the exit function to be used in .s file
|
||||||
|
*/
|
||||||
public static final String EXIT;
|
public static final String EXIT;
|
||||||
|
|
||||||
/** The assembler directive used to define a constant string */
|
/**
|
||||||
|
* The assembler directive used to define a constant string
|
||||||
|
*/
|
||||||
public static final String DOT_STRING;
|
public static final String DOT_STRING;
|
||||||
|
|
||||||
/** The assembler directive used to define a constant int */
|
/**
|
||||||
|
* The assembler directive used to define a constant int
|
||||||
|
*/
|
||||||
public static final String DOT_INT;
|
public static final String DOT_INT;
|
||||||
|
|
||||||
/** The assembler directive used to start the text section */
|
/**
|
||||||
|
* The assembler directive used to start the text section
|
||||||
|
*/
|
||||||
public static final String TEXT_SECTION;
|
public static final String TEXT_SECTION;
|
||||||
|
|
||||||
/** The assembler directive used to start the section for integer data */
|
/**
|
||||||
|
* The assembler directive used to start the section for integer data
|
||||||
|
*/
|
||||||
public static final String DATA_INT_SECTION;
|
public static final String DATA_INT_SECTION;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -52,7 +75,9 @@ public class Config {
|
||||||
*/
|
*/
|
||||||
public static final String DATA_STR_SECTION;
|
public static final String DATA_STR_SECTION;
|
||||||
|
|
||||||
/** Comment separator used in assembly files */
|
/**
|
||||||
|
* Comment separator used in assembly files
|
||||||
|
*/
|
||||||
public static final String COMMENT_SEP;
|
public static final String COMMENT_SEP;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -99,8 +124,7 @@ public class Config {
|
||||||
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";
|
||||||
|
@ -116,8 +140,7 @@ public class Config {
|
||||||
DATA_INT_SECTION = ".data";
|
DATA_INT_SECTION = ".data";
|
||||||
DATA_STR_SECTION = ".cstring";
|
DATA_STR_SECTION = ".cstring";
|
||||||
COMMENT_SEP = "#";
|
COMMENT_SEP = "#";
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
BINARYEXT = ".bin";
|
BINARYEXT = ".bin";
|
||||||
MAIN = "main";
|
MAIN = "main";
|
||||||
PRINTF = "printf";
|
PRINTF = "printf";
|
||||||
|
|
|
@ -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;
|
||||||
|
@ -21,12 +25,11 @@ 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
|
||||||
|
@ -35,10 +38,14 @@ public class Main {
|
||||||
// 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
|
||||||
|
*/
|
||||||
|
public ClassSymbol mainType;
|
||||||
|
|
||||||
/** List of all type symbols, used by code generator. */
|
/**
|
||||||
|
* List of all type symbols, used by code generator.
|
||||||
|
*/
|
||||||
public List<TypeSymbol> allTypeSymbols;
|
public List<TypeSymbol> allTypeSymbols;
|
||||||
|
|
||||||
public void debug(String format, Object... args) {
|
public void debug(String format, Object... args) {
|
||||||
|
@ -54,7 +61,9 @@ 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();
|
||||||
|
|
||||||
|
@ -62,6 +71,9 @@ public class Main {
|
||||||
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:
|
||||||
|
@ -79,9 +91,11 @@ 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>();
|
||||||
|
|
||||||
|
@ -106,23 +120,33 @@ public class Main {
|
||||||
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()) {
|
||||||
|
new CfgBuilder().build(md);
|
||||||
}
|
}
|
||||||
|
CfgDump.toString(astRoots, ".cfg", cfgdumpbase, false);
|
||||||
|
|
||||||
|
// Optimize
|
||||||
|
new AstOptimizer().go(astRoots, allTypeSymbols);
|
||||||
|
CfgDump.toString(astRoots, ".cfgOPT", cfgdumpbase, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void generateCode(List<ClassDecl> astRoots, Writer out) {
|
public void generateCode(List<ClassDecl> astRoots, Writer out) {
|
||||||
{
|
CfgCodeGenerator cg = new CfgCodeGenerator(this, out);
|
||||||
AstCodeGenerator cg = AstCodeGenerator.createCodeGenerator(this, out);
|
|
||||||
cg.go(astRoots);
|
cg.go(astRoots);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/** Dumps the AST to the debug stream */
|
/**
|
||||||
|
* 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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -15,22 +15,30 @@ 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
|
||||||
|
@ -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));
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ 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"
|
||||||
|
|
|
@ -2,42 +2,41 @@ 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;
|
||||||
|
@ -52,9 +51,10 @@ 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
|
||||||
|
@ -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.emitLabel(Label.type(type));
|
|
||||||
emit.emitConstantData("0"); // Supertype (null)
|
|
||||||
// array vtable
|
|
||||||
emit.emitLabel(Label.type(new ArrayTypeSymbol(type)));
|
|
||||||
emit.emitConstantData(Label.type(ClassSymbol.objectType)); // Supertype
|
|
||||||
emit.emitConstantData(Label.type(type)); // Element type
|
|
||||||
}
|
|
||||||
|
|
||||||
// Emit the new Main().main() code to start the program:
|
|
||||||
|
|
||||||
// 1. Enter TEXT and start the program
|
|
||||||
emit.emitRaw(Config.TEXT_SECTION);
|
emit.emitRaw(Config.TEXT_SECTION);
|
||||||
emit.emit(".globl", MAIN);
|
|
||||||
emit.emitLabel(MAIN);
|
// Generate a helper method for checking casts:
|
||||||
// 1.1. Prepare first frame
|
// It takes first a vtable and second an object ptr.
|
||||||
|
{
|
||||||
|
Register obj = RegisterManager.CALLER_SAVE[0];
|
||||||
|
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.emit("push", BASE_REG);
|
||||||
emit.emitMove(STACK_REG, BASE_REG);
|
emit.emitMove(STACK_REG, BASE_REG);
|
||||||
|
emit.emit("sub", constant(8), STACK_REG);
|
||||||
|
|
||||||
// 2. Create main variable
|
emit.emit("and", constant(-16), STACK_REG);
|
||||||
Ast.NewObject newMain = new Ast.NewObject("Main");
|
emit.emit("sub", constant(16), STACK_REG);
|
||||||
newMain.type = main.mainType;
|
emit.emitLoad(SIZEOF_PTR * 2, BASE_REG, cls);
|
||||||
Register mainLocation = eg.visit(newMain, null);
|
emit.emitLoad(SIZEOF_PTR * 3, BASE_REG, obj);
|
||||||
|
emit.emit("cmpl", constant(0), obj);
|
||||||
// 3. Call main()
|
emit.emit("je", donelbl); // allow null objects to pass
|
||||||
emit.emit("push", mainLocation);
|
emit.emitLoad(0, obj, obj); // load vtbl of object
|
||||||
for (ClassSymbol sym = main.mainType; sym != ClassSymbol.objectType; sym = sym.superClass)
|
emit.emitLabel(looplbl);
|
||||||
if (sym.methods.getOrDefault("main", null) != null) {
|
emit.emit("cmpl", obj, cls);
|
||||||
emit.emit("call", Label.method(sym, sym.methods.get("main")));
|
emit.emit("je", donelbl);
|
||||||
break;
|
emit.emit("cmpl", constant(0), obj);
|
||||||
}
|
emit.emit("je", faillbl);
|
||||||
emitMethodSuffix(true);
|
emit.emitLoad(0, obj, obj); // load parent vtable
|
||||||
}
|
emit.emit("jmp", looplbl);
|
||||||
|
emit.emitLabel(faillbl);
|
||||||
void initMethodData() {
|
emit.emitStore(constant(ExitCode.INVALID_DOWNCAST.value), 0, STACK_REG);
|
||||||
rm.initRegisters();
|
emit.emit("call", Config.EXIT);
|
||||||
}
|
emit.emitLabel(donelbl);
|
||||||
|
emit.emitMove(BASE_REG, STACK_REG);
|
||||||
|
emit.emit("pop", BASE_REG);
|
||||||
void emitMethodSuffix(boolean returnNull) {
|
|
||||||
if (returnNull)
|
|
||||||
emit.emit("movl", 0, Register.EAX);
|
|
||||||
emit.emitRaw("leave");
|
|
||||||
emit.emitRaw("ret");
|
emit.emitRaw("ret");
|
||||||
}
|
}
|
||||||
|
|
||||||
static class Label {
|
// Generate a helper method for checking for null ptrs:
|
||||||
static String type(TypeSymbol symbol) {
|
{
|
||||||
if (symbol instanceof ClassSymbol)
|
String oknulllbl = emit.uniqueLabel();
|
||||||
return String.format("type_%s", symbol);
|
emit.emitCommentSection(CHECK_NULL + " function");
|
||||||
else if (symbol instanceof ArrayTypeSymbol)
|
emit.emitLabel(CHECK_NULL);
|
||||||
return String.format("array_%s", ((ArrayTypeSymbol) symbol).elementType);
|
|
||||||
else if (symbol instanceof PrimitiveTypeSymbol)
|
emit.emit("push", BASE_REG);
|
||||||
return String.format("primive_%s", symbol);
|
emit.emitMove(STACK_REG, BASE_REG);
|
||||||
throw new RuntimeException("Unimplemented type symbol");
|
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", 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");
|
||||||
}
|
}
|
||||||
|
|
||||||
static String method(ClassSymbol classSymbol, MethodSymbol methodSymbol) {
|
// Generate a helper method for checking that we don't divide by zero:
|
||||||
return String.format("method_%s_%s", classSymbol.name, methodSymbol.name);
|
{
|
||||||
|
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");
|
||||||
}
|
}
|
||||||
|
|
||||||
static String returnMethod(ClassSymbol classSymbol, MethodSymbol methodSymbol) {
|
// Generate a helper method for checking array size:
|
||||||
return String.format("return_%s", method(classSymbol, methodSymbol));
|
{
|
||||||
|
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");
|
||||||
|
newMain.type = main.mainType;
|
||||||
|
|
||||||
|
Ast.MethodCallExpr mce = new Ast.MethodCallExpr(newMain, "main", Collections.<Expr>emptyList());
|
||||||
|
Ast.MethodCall callMain = new Ast.MethodCall(mce);
|
||||||
|
mce.sym = main.mainType.getMethod("main");
|
||||||
|
|
||||||
|
// Emit the main() method:
|
||||||
|
// new Main().main();
|
||||||
|
emit.emitCommentSection("main() function");
|
||||||
|
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");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@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)
|
||||||
|
emit.emit("movl", "$0", Register.EAX);
|
||||||
|
restoreCalleeSaveRegs();
|
||||||
|
emit.emitMove(BASE_REG, STACK_REG);
|
||||||
|
emit.emit("pop", BASE_REG);
|
||||||
|
emit.emitRaw("ret");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class AstCodeGeneratorNop90 extends AstCodeGeneratorRef {
|
||||||
|
public AstCodeGeneratorNop90(Main main, Writer out, CfgCodeGenerator cfgCg) {
|
||||||
|
super(main, out);
|
||||||
|
this.egRef = new ExprGeneratorNop90(this, cfgCg);
|
||||||
|
this.eg = this.egRef;
|
||||||
|
this.sgRef = new StmtGeneratorNop90(this, cfgCg);
|
||||||
|
this.sg = this.sgRef;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
130
src/cd/backend/codegen/CfgCodeGenerator.java
Normal file
130
src/cd/backend/codegen/CfgCodeGenerator.java
Normal 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
106
src/cd/backend/codegen/CompileTimeChecks.java
Executable file
106
src/cd/backend/codegen/CompileTimeChecks.java
Executable 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
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,8 @@ 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) {
|
||||||
|
|
|
@ -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
|
||||||
*/
|
*/
|
||||||
@Override
|
class StmtGeneratorRef extends StmtGenerator {
|
||||||
public Register classDecl(ClassDecl ast, Location arg) {
|
|
||||||
cg.emit.emitRaw(Config.DATA_INT_SECTION);
|
|
||||||
// Emit vtable for class
|
|
||||||
cg.emit.emitLabel(Label.type(ast.sym)); // Label
|
|
||||||
cg.emit.emitConstantData(Label.type(ast.sym.superClass)); // Superclass
|
|
||||||
// Methods (don't write those that are overridden twice)
|
|
||||||
Set<String> generated = new HashSet<>();
|
|
||||||
ClassSymbol currSym = ast.sym;
|
|
||||||
while (currSym != ClassSymbol.objectType) {
|
|
||||||
ClassSymbol finalCurrSym = currSym;
|
|
||||||
currSym.methods.values().forEach(o -> {
|
|
||||||
if (!generated.add(o.name)) return;
|
|
||||||
cg.emit.emitConstantData(String.valueOf(o.name.hashCode()));
|
|
||||||
cg.emit.emitConstantData(Label.method(finalCurrSym, o));
|
|
||||||
});
|
|
||||||
currSym = currSym.superClass;
|
|
||||||
}
|
|
||||||
// End of class vtable
|
|
||||||
// Array vtable
|
|
||||||
boolean found = false;
|
|
||||||
for (Symbol.TypeSymbol type : cg.main.allTypeSymbols) {
|
|
||||||
if (type instanceof Symbol.ArrayTypeSymbol) {
|
|
||||||
Symbol.ArrayTypeSymbol aType = (Symbol.ArrayTypeSymbol) type;
|
|
||||||
if (aType.elementType == ast.sym) {
|
|
||||||
cg.emit.emitLabel(Label.type(aType)); // Label
|
|
||||||
found = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!found)
|
|
||||||
throw new RuntimeException("The array type could not be found");
|
|
||||||
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
|
/* cg and cgRef are the same instance. cgRef simply
|
||||||
for (Ast method : ast.methods())
|
* provides a wider interface */
|
||||||
visit(method, new Location(ast.sym));
|
protected final AstCodeGeneratorRef cgRef;
|
||||||
|
|
||||||
|
StmtGeneratorRef(AstCodeGeneratorRef astCodeGenerator) {
|
||||||
|
super(astCodeGenerator);
|
||||||
|
this.cgRef = astCodeGenerator;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Register methodCall(MethodSymbol mthSymbol, List<Expr> allArgs) {
|
||||||
|
// Push the arguments and the method prefix (caller save register,
|
||||||
|
// and padding) onto the stack.
|
||||||
|
// Note that the space for the arguments is not already reserved,
|
||||||
|
// so we just push them in the Java left-to-right order.
|
||||||
|
//
|
||||||
|
// After each iteration of the following loop, reg holds the
|
||||||
|
// register used for the previous argument.
|
||||||
|
int padding = cgRef.emitCallPrefix(null, allArgs.size());
|
||||||
|
|
||||||
|
Register reg = null;
|
||||||
|
for (int i = 0; i < allArgs.size(); i++) {
|
||||||
|
if (reg != null) {
|
||||||
|
cgRef.rm.releaseRegister(reg);
|
||||||
|
}
|
||||||
|
reg = cgRef.eg.gen(allArgs.get(i));
|
||||||
|
cgRef.push(reg.repr);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Since "this" is the first parameter that push
|
||||||
|
// we have to get it back to resolve the method call
|
||||||
|
cgRef.emit.emitComment("Load \"this\" pointer");
|
||||||
|
cgRef.emit.emitLoad((allArgs.size() - 1) * Config.SIZEOF_PTR, STACK_REG, reg);
|
||||||
|
|
||||||
|
// Check for a null receiver
|
||||||
|
int cnPadding = cgRef.emitCallPrefix(null, 1);
|
||||||
|
cgRef.push(reg.repr);
|
||||||
|
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;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Register methodDecl(MethodDecl ast, Location arg) {
|
public Register whileLoop(WhileLoop ast, Void arg) {
|
||||||
// Bookkeeping for framework
|
String nextLbl = cgRef.emit.uniqueLabel();
|
||||||
arg.enterMethod(ast.sym);
|
String doneLbl = cgRef.emit.uniqueLabel();
|
||||||
cg.initMethodData();
|
|
||||||
|
|
||||||
// Begin method
|
cgRef.emit.emitLabel(nextLbl);
|
||||||
cg.emit.emitLabel(Label.method(arg.classSym(), ast.sym));
|
cgRef.genJumpIfFalse(ast.condition(), doneLbl);
|
||||||
|
gen(ast.body());
|
||||||
|
cgRef.emit.emit("jmp", nextLbl);
|
||||||
|
cgRef.emit.emitLabel(doneLbl);
|
||||||
|
|
||||||
// 1. Save and update the base register, reserve space for local variables
|
|
||||||
cg.emit.emit("enter", ast.sym.locals.size() * VAR_SIZE, 0);
|
|
||||||
for (int i = 0; i < ast.sym.locals.size(); i++)
|
|
||||||
cg.emit.emitStore(0, -(i + 1) * VAR_SIZE, BASE_REG);
|
|
||||||
|
|
||||||
// 2. Save CPU registers
|
|
||||||
Register[] regs = RegisterManager.CALLEE_SAVE;
|
|
||||||
for (int i = 0; i < regs.length; i++)
|
|
||||||
cg.emit.emit("push", regs[i]);
|
|
||||||
|
|
||||||
// 3. Run the code contained in the function
|
|
||||||
Register returnReg = visit(ast.body(), arg);
|
|
||||||
cg.emit.emitLabel(Label.returnMethod(arg.classSym(), ast.sym));
|
|
||||||
|
|
||||||
// 4. Restore saved registers (if there is a mismatch, the stack will be corrupted)
|
|
||||||
for (int i = regs.length - 1; i >= 0; i--)
|
|
||||||
cg.emit.emit("pop", regs[i]);
|
|
||||||
|
|
||||||
// 5 and 6: Restore the old base pointer and return from the function
|
|
||||||
cg.emitMethodSuffix(returnReg == null);
|
|
||||||
|
|
||||||
// Framework bookkeeping
|
|
||||||
arg.leaveMethod();
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Register ifElse(IfElse ast, Location arg) {
|
public Register assign(Assign ast, Void arg) {
|
||||||
// Emit condition check
|
class AssignVisitor extends ExprVisitor<Void, Expr> {
|
||||||
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
|
@Override
|
||||||
public Register whileLoop(WhileLoop ast, Location arg) {
|
public Void var(Var ast, Expr right) {
|
||||||
String conditionLabel = cg.emit.uniqueLabel();
|
final Register rhsReg = cgRef.eg.gen(right);
|
||||||
|
cgRef.emit.emitStore(rhsReg, ast.sym.offset, BASE_REG);
|
||||||
cg.emit.emitLabel(conditionLabel);
|
cgRef.rm.releaseRegister(rhsReg);
|
||||||
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;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Register assign(Assign ast, Location arg) {
|
public Void field(Field ast, Expr right) {
|
||||||
Register value = cg.eg.visit(ast.right(), arg);
|
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);
|
||||||
|
|
||||||
// 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;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Register builtInWrite(BuiltInWrite ast, Location arg) {
|
public Void index(Index ast, Expr right) {
|
||||||
Register reg = cg.eg.visit(ast.arg(), arg);
|
Register rhsReg = cgRef.egRef.gen(right);
|
||||||
cg.emit.emit("sub", 16, STACK_REG);
|
|
||||||
cg.emit.emitStore(reg, 4, STACK_REG);
|
Pair<Register> regs = cgRef.egRef.genPushing(rhsReg, ast.left());
|
||||||
cg.emit.emitStore("$STR_D", 0, STACK_REG);
|
rhsReg = regs.a;
|
||||||
cg.emit.emit("call", Config.PRINTF);
|
Register arrReg = regs.b;
|
||||||
cg.emit.emit("add", 16, STACK_REG);
|
int padding = cgRef.emitCallPrefix(null, 1);
|
||||||
cg.rm.releaseRegister(reg);
|
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;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Register builtInWriteln(BuiltInWriteln ast, Location arg) {
|
protected Void dfltExpr(Expr ast, Expr arg) {
|
||||||
cg.emit.emit("sub", 16, STACK_REG);
|
throw new RuntimeException("Store to unexpected lvalue " + ast);
|
||||||
cg.emit.emitStore("$STR_NL", 0, STACK_REG);
|
}
|
||||||
cg.emit.emit("call", Config.PRINTF);
|
|
||||||
cg.emit.emit("add", 16, STACK_REG);
|
}
|
||||||
|
new AssignVisitor().visit(ast.left(), ast.right());
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Register returnStmt(ReturnStmt ast, Location arg) {
|
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) {
|
if (ast.arg() != null) {
|
||||||
Register retReg = cg.eg.visit(ast.arg(), arg);
|
Register reg = cgRef.eg.gen(ast.arg());
|
||||||
cg.emit.emitMove(retReg, Register.EAX);
|
cgRef.emit.emitMove(reg, "%eax");
|
||||||
cg.rm.releaseRegister(retReg);
|
cgRef.emitMethodSuffix(false);
|
||||||
}
|
cgRef.rm.releaseRegister(reg);
|
||||||
cg.emit.emit("jmp", Label.returnMethod(arg.classSym(), arg.methodSym()));
|
} else {
|
||||||
return Register.EAX;
|
cgRef.emitMethodSuffix(true); // no return value -- return NULL as
|
||||||
|
// a default (required for main())
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
return null;
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
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());
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Void index(Index ast, Expr right) {
|
||||||
|
boolean isNotNull = cfgCg.check.isNotNull(ast.left());
|
||||||
|
boolean emitBoundCheck = cfgCg.check.checkArrayBound(ast);
|
||||||
|
Register rhsReg = cgRef.egRef.gen(right);
|
||||||
|
|
||||||
|
Pair<Register> regs = cgRef.egRef.genPushing(rhsReg, ast.left());
|
||||||
|
rhsReg = regs.a;
|
||||||
|
Register arrReg = regs.b;
|
||||||
|
if (!isNotNull) {
|
||||||
|
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
|
||||||
|
if (emitBoundCheck) {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
40
src/cd/frontend/semantic/FieldQualifier.java
Normal file
40
src/cd/frontend/semantic/FieldQualifier.java
Normal 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -46,6 +46,7 @@ public class InheritanceChecker extends AstVisitor<Void, Void> {
|
||||||
// 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(
|
||||||
|
|
|
@ -5,11 +5,10 @@ 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
|
||||||
|
|
|
@ -25,6 +25,10 @@ public class SemanticAnalyzer {
|
||||||
checkInheritance(classDecls);
|
checkInheritance(classDecls);
|
||||||
checkStartPoint(typeSymbols);
|
checkStartPoint(typeSymbols);
|
||||||
checkMethodBodies(typeSymbols, classDecls);
|
checkMethodBodies(typeSymbols, classDecls);
|
||||||
|
{
|
||||||
|
rewriteMethodBodies(classDecls);
|
||||||
|
main.allTypeSymbols = typeSymbols.allSymbols();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,6 +37,7 @@ public class SemanticAnalyzer {
|
||||||
* 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) {
|
||||||
|
@ -75,6 +80,7 @@ public class SemanticAnalyzer {
|
||||||
* 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,6 +99,7 @@ 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!
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -102,12 +109,12 @@ public class SemanticAnalyzer {
|
||||||
/**
|
/**
|
||||||
* 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) {
|
||||||
|
@ -152,4 +159,9 @@ public class SemanticAnalyzer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void rewriteMethodBodies(List<ClassDecl> classDecls) {
|
||||||
|
for (ClassDecl cd : classDecls)
|
||||||
|
new FieldQualifier().rewrite(cd);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,8 @@ 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;
|
||||||
|
|
||||||
|
@ -12,26 +13,35 @@ public class SemanticFailure extends RuntimeException {
|
||||||
* 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,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -94,7 +104,9 @@ public class SemanticFailure extends RuntimeException {
|
||||||
*/
|
*/
|
||||||
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,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -3,14 +3,13 @@ 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>();
|
||||||
|
@ -28,6 +27,17 @@ 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();
|
||||||
}
|
}
|
||||||
|
@ -51,7 +61,8 @@ public class SymTable<S extends Symbol> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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)
|
||||||
|
@ -64,7 +75,8 @@ public class SymTable<S extends Symbol> {
|
||||||
/**
|
/**
|
||||||
* 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)
|
||||||
|
|
|
@ -78,6 +78,8 @@ public class SymbolCreator extends Object {
|
||||||
|
|
||||||
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
|
||||||
|
|
|
@ -24,6 +24,7 @@ public class TypeChecker {
|
||||||
/**
|
/**
|
||||||
* 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) {
|
||||||
|
@ -298,8 +299,7 @@ public class TypeChecker {
|
||||||
|
|
||||||
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;
|
||||||
|
@ -360,7 +360,8 @@ public class TypeChecker {
|
||||||
* 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);
|
||||||
}
|
}
|
||||||
|
@ -369,7 +370,9 @@ public class TypeChecker {
|
||||||
* @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) {
|
||||||
|
@ -386,7 +389,9 @@ public class TypeChecker {
|
||||||
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(
|
||||||
|
|
|
@ -55,10 +55,20 @@ public abstract class Ast {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/** Accept method for the pattern Visitor. */
|
/**
|
||||||
|
* Accept method for the pattern Visitor.
|
||||||
|
*/
|
||||||
public abstract <R, A> R accept(AstVisitor<R, A> visitor, A arg);
|
public abstract <R, A> R accept(AstVisitor<R, A> visitor, A arg);
|
||||||
|
|
||||||
/** Convenient debugging printout */
|
|
||||||
|
/**
|
||||||
|
* Makes a deep clone of this AST node.
|
||||||
|
*/
|
||||||
|
public abstract Ast deepCopy();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convenient debugging printout
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return String.format(
|
return String.format(
|
||||||
|
@ -70,23 +80,30 @@ public abstract class Ast {
|
||||||
// _________________________________________________________________
|
// _________________________________________________________________
|
||||||
// Expressions
|
// Expressions
|
||||||
|
|
||||||
/** Base class for all expressions */
|
/**
|
||||||
|
* Base class for all expressions
|
||||||
|
*/
|
||||||
public static abstract class Expr extends Ast {
|
public static abstract class Expr extends Ast {
|
||||||
|
|
||||||
protected Expr(int fixedCount) {
|
protected Expr(int fixedCount) {
|
||||||
super(fixedCount);
|
super(fixedCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Type that this expression will evaluate to (computed in semantic phase). */
|
/**
|
||||||
|
* Type that this expression will evaluate to (computed in semantic phase).
|
||||||
|
*/
|
||||||
public TypeSymbol type;
|
public TypeSymbol type;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <R, A> R accept(AstVisitor<R, A> visitor, A arg) {
|
public <R, A> R accept(AstVisitor<R, A> visitor, A arg) {
|
||||||
return this.accept((ExprVisitor<R, A>) visitor, arg);
|
return this.accept((ExprVisitor<R, A>) visitor, arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract <R, A> R accept(ExprVisitor<R, A> visitor, A arg);
|
public abstract <R, A> R accept(ExprVisitor<R, A> visitor, A arg);
|
||||||
|
|
||||||
/** Copies any non-AST fields. */
|
/**
|
||||||
|
* Copies any non-AST fields.
|
||||||
|
*/
|
||||||
protected <E extends Expr> E postCopy(E item) {
|
protected <E extends Expr> E postCopy(E item) {
|
||||||
{
|
{
|
||||||
item.type = type;
|
item.type = type;
|
||||||
|
@ -95,9 +112,11 @@ public abstract class Ast {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Base class used for exprs with left/right operands.
|
/**
|
||||||
|
* Base class used for exprs with left/right operands.
|
||||||
* We use this for all expressions that take strictly two operands,
|
* We use this for all expressions that take strictly two operands,
|
||||||
* such as binary operators or array indexing. */
|
* such as binary operators or array indexing.
|
||||||
|
*/
|
||||||
public static abstract class LeftRightExpr extends Expr {
|
public static abstract class LeftRightExpr extends Expr {
|
||||||
|
|
||||||
public LeftRightExpr(Expr left, Expr right) {
|
public LeftRightExpr(Expr left, Expr right) {
|
||||||
|
@ -108,14 +127,26 @@ public abstract class Ast {
|
||||||
setRight(right);
|
setRight(right);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Expr left() { return (Expr) this.rwChildren.get(0); }
|
public Expr left() {
|
||||||
public void setLeft(Expr node) { this.rwChildren.set(0, node); }
|
return (Expr) this.rwChildren.get(0);
|
||||||
|
|
||||||
public Expr right() { return (Expr) this.rwChildren.get(1); }
|
|
||||||
public void setRight(Expr node) { this.rwChildren.set(1, node); }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Base class used for expressions with a single argument */
|
public void setLeft(Expr node) {
|
||||||
|
this.rwChildren.set(0, node);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Expr right() {
|
||||||
|
return (Expr) this.rwChildren.get(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRight(Expr node) {
|
||||||
|
this.rwChildren.set(1, node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base class used for expressions with a single argument
|
||||||
|
*/
|
||||||
public static abstract class ArgExpr extends Expr {
|
public static abstract class ArgExpr extends Expr {
|
||||||
|
|
||||||
public ArgExpr(Expr arg) {
|
public ArgExpr(Expr arg) {
|
||||||
|
@ -124,19 +155,28 @@ public abstract class Ast {
|
||||||
setArg(arg);
|
setArg(arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Expr arg() { return (Expr) this.rwChildren.get(0); }
|
public Expr arg() {
|
||||||
public void setArg(Expr node) { this.rwChildren.set(0, node); }
|
return (Expr) this.rwChildren.get(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setArg(Expr node) {
|
||||||
|
this.rwChildren.set(0, node);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Base class used for things with no arguments */
|
/**
|
||||||
protected static abstract class LeafExpr extends Expr {
|
* Base class used for things with no arguments
|
||||||
|
*/
|
||||||
|
public static abstract class LeafExpr extends Expr {
|
||||||
public LeafExpr() {
|
public LeafExpr() {
|
||||||
super(0);
|
super(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Represents {@code this}, the current object */
|
/**
|
||||||
|
* Represents {@code this}, the current object
|
||||||
|
*/
|
||||||
public static class ThisRef extends LeafExpr {
|
public static class ThisRef extends LeafExpr {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -144,10 +184,17 @@ public abstract class Ast {
|
||||||
return visitor.thisRef(this, arg);
|
return visitor.thisRef(this, arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ThisRef deepCopy() {
|
||||||
|
return postCopy(new ThisRef());
|
||||||
}
|
}
|
||||||
|
|
||||||
/** A binary operation combining a left and right operand,
|
}
|
||||||
* such as "1+2" or "3*4" */
|
|
||||||
|
/**
|
||||||
|
* A binary operation combining a left and right operand,
|
||||||
|
* such as "1+2" or "3*4"
|
||||||
|
*/
|
||||||
public static class BinaryOp extends LeftRightExpr {
|
public static class BinaryOp extends LeftRightExpr {
|
||||||
|
|
||||||
public static enum BOp {
|
public static enum BOp {
|
||||||
|
@ -167,7 +214,10 @@ public abstract class Ast {
|
||||||
B_GREATER_OR_EQUAL(">=");
|
B_GREATER_OR_EQUAL(">=");
|
||||||
|
|
||||||
public String repr;
|
public String repr;
|
||||||
private BOp(String repr) { this.repr = repr; }
|
|
||||||
|
private BOp(String repr) {
|
||||||
|
this.repr = repr;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Note that this method ignores short-circuit evaluation of boolean
|
* Note that this method ignores short-circuit evaluation of boolean
|
||||||
|
@ -189,7 +239,9 @@ public abstract class Ast {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
|
;
|
||||||
|
|
||||||
public BOp operator;
|
public BOp operator;
|
||||||
|
|
||||||
|
@ -203,9 +255,16 @@ public abstract class Ast {
|
||||||
return visitor.binaryOp(this, arg);
|
return visitor.binaryOp(this, arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BinaryOp deepCopy() {
|
||||||
|
return postCopy(new BinaryOp(left(), operator, right()));
|
||||||
}
|
}
|
||||||
|
|
||||||
/** A Cast from one type to another: {@code (typeName)arg} */
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A Cast from one type to another: {@code (typeName)arg}
|
||||||
|
*/
|
||||||
public static class Cast extends ArgExpr {
|
public static class Cast extends ArgExpr {
|
||||||
|
|
||||||
public String typeName;
|
public String typeName;
|
||||||
|
@ -220,6 +279,11 @@ public abstract class Ast {
|
||||||
return visitor.cast(this, arg);
|
return visitor.cast(this, arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Cast deepCopy() {
|
||||||
|
return postCopy(new Cast(arg(), typeName));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected <E extends Expr> E postCopy(E item) {
|
protected <E extends Expr> E postCopy(E item) {
|
||||||
((Cast) item).type = type;
|
((Cast) item).type = type;
|
||||||
|
@ -231,6 +295,7 @@ public abstract class Ast {
|
||||||
public static class IntConst extends LeafExpr {
|
public static class IntConst extends LeafExpr {
|
||||||
|
|
||||||
public final int value;
|
public final int value;
|
||||||
|
|
||||||
public IntConst(int value) {
|
public IntConst(int value) {
|
||||||
this.value = value;
|
this.value = value;
|
||||||
}
|
}
|
||||||
|
@ -240,11 +305,17 @@ public abstract class Ast {
|
||||||
return visitor.intConst(this, arg);
|
return visitor.intConst(this, arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IntConst deepCopy() {
|
||||||
|
return postCopy(new IntConst(value));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class BooleanConst extends LeafExpr {
|
public static class BooleanConst extends LeafExpr {
|
||||||
|
|
||||||
public final boolean value;
|
public final boolean value;
|
||||||
|
|
||||||
public BooleanConst(boolean value) {
|
public BooleanConst(boolean value) {
|
||||||
this.value = value;
|
this.value = value;
|
||||||
}
|
}
|
||||||
|
@ -254,6 +325,11 @@ public abstract class Ast {
|
||||||
return visitor.booleanConst(this, arg);
|
return visitor.booleanConst(this, arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BooleanConst deepCopy() {
|
||||||
|
return postCopy(new BooleanConst(value));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class NullConst extends LeafExpr {
|
public static class NullConst extends LeafExpr {
|
||||||
|
@ -263,6 +339,11 @@ public abstract class Ast {
|
||||||
return visitor.nullConst(this, arg);
|
return visitor.nullConst(this, arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NullConst deepCopy() {
|
||||||
|
return postCopy(new NullConst());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Field extends ArgExpr {
|
public static class Field extends ArgExpr {
|
||||||
|
@ -282,6 +363,11 @@ public abstract class Ast {
|
||||||
return visitor.field(this, arg);
|
return visitor.field(this, arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Field deepCopy() {
|
||||||
|
return postCopy(new Field(arg(), fieldName));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected <E extends Expr> E postCopy(E item) {
|
protected <E extends Expr> E postCopy(E item) {
|
||||||
((Field) item).sym = sym;
|
((Field) item).sym = sym;
|
||||||
|
@ -301,11 +387,18 @@ public abstract class Ast {
|
||||||
return visitor.index(this, arg);
|
return visitor.index(this, arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Index deepCopy() {
|
||||||
|
return postCopy(new Index(left(), right()));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class NewObject extends LeafExpr {
|
public static class NewObject extends LeafExpr {
|
||||||
|
|
||||||
/** Name of the type to be created */
|
/**
|
||||||
|
* Name of the type to be created
|
||||||
|
*/
|
||||||
public String typeName;
|
public String typeName;
|
||||||
|
|
||||||
public NewObject(String typeName) {
|
public NewObject(String typeName) {
|
||||||
|
@ -317,11 +410,18 @@ public abstract class Ast {
|
||||||
return visitor.newObject(this, arg);
|
return visitor.newObject(this, arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NewObject deepCopy() {
|
||||||
|
return postCopy(new NewObject(typeName));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class NewArray extends ArgExpr {
|
public static class NewArray extends ArgExpr {
|
||||||
|
|
||||||
/** Name of the type to be created: must be an array type */
|
/**
|
||||||
|
* Name of the type to be created: must be an array type
|
||||||
|
*/
|
||||||
public String typeName;
|
public String typeName;
|
||||||
|
|
||||||
public NewArray(String typeName, Expr capacity) {
|
public NewArray(String typeName, Expr capacity) {
|
||||||
|
@ -334,6 +434,11 @@ public abstract class Ast {
|
||||||
return visitor.newArray(this, arg);
|
return visitor.newArray(this, arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NewArray deepCopy() {
|
||||||
|
return postCopy(new NewArray(typeName, arg()));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class UnaryOp extends ArgExpr {
|
public static class UnaryOp extends ArgExpr {
|
||||||
|
@ -343,8 +448,13 @@ public abstract class Ast {
|
||||||
U_MINUS("-"),
|
U_MINUS("-"),
|
||||||
U_BOOL_NOT("!");
|
U_BOOL_NOT("!");
|
||||||
public String repr;
|
public String repr;
|
||||||
private UOp(String repr) { this.repr = repr; }
|
|
||||||
};
|
private UOp(String repr) {
|
||||||
|
this.repr = repr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
;
|
||||||
|
|
||||||
public final UOp operator;
|
public final UOp operator;
|
||||||
|
|
||||||
|
@ -358,6 +468,11 @@ public abstract class Ast {
|
||||||
return visitor.unaryOp(this, arg);
|
return visitor.unaryOp(this, arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UnaryOp deepCopy() {
|
||||||
|
return postCopy(new UnaryOp(operator, arg()));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Var extends LeafExpr {
|
public static class Var extends LeafExpr {
|
||||||
|
@ -373,6 +488,7 @@ public abstract class Ast {
|
||||||
public Var(String name) {
|
public Var(String name) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <R, A> R accept(ExprVisitor<R, A> visitor, A arg) {
|
public <R, A> R accept(ExprVisitor<R, A> visitor, A arg) {
|
||||||
return visitor.var(this, arg);
|
return visitor.var(this, arg);
|
||||||
|
@ -390,6 +506,11 @@ public abstract class Ast {
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Var deepCopy() {
|
||||||
|
return postCopy(new Var(name));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected <E extends Expr> E postCopy(E item) {
|
protected <E extends Expr> E postCopy(E item) {
|
||||||
((Var) item).sym = sym;
|
((Var) item).sym = sym;
|
||||||
|
@ -410,6 +531,11 @@ public abstract class Ast {
|
||||||
return visitor.builtInRead(this, arg);
|
return visitor.builtInRead(this, arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BuiltInRead deepCopy() {
|
||||||
|
return postCopy(new BuiltInRead());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class MethodCallExpr extends Expr {
|
public static class MethodCallExpr extends Expr {
|
||||||
|
@ -426,28 +552,38 @@ public abstract class Ast {
|
||||||
this.rwChildren.addAll(arguments);
|
this.rwChildren.addAll(arguments);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns the receiver of the method call.
|
/**
|
||||||
* i.e., for a method call {@code a.b(c,d)} returns {@code a}. */
|
* Returns the receiver of the method call.
|
||||||
public Expr receiver() { return (Expr) this.rwChildren.get(0); }
|
* i.e., for a method call {@code a.b(c,d)} returns {@code a}.
|
||||||
|
*/
|
||||||
|
public Expr receiver() {
|
||||||
|
return (Expr) this.rwChildren.get(0);
|
||||||
|
}
|
||||||
|
|
||||||
/** Changes the receiver of the method call.
|
/**
|
||||||
* i.e., for a method call {@code a.b(c,d)} changes {@code a}. */
|
* Changes the receiver of the method call.
|
||||||
public void setReceiver(Expr rcvr) { this.rwChildren.set(0, rcvr); }
|
* i.e., for a method call {@code a.b(c,d)} changes {@code a}.
|
||||||
|
*/
|
||||||
|
public void setReceiver(Expr rcvr) {
|
||||||
|
this.rwChildren.set(0, rcvr);
|
||||||
|
}
|
||||||
|
|
||||||
/** Returns all arguments to the method, <b>including the receiver.</b>
|
/**
|
||||||
* i.e, for a method call {@code a.b(c,d)} returns {@code [a, c, d]} */
|
* Returns all arguments to the method, <b>including the receiver.</b>
|
||||||
public List<Expr> allArguments()
|
* i.e, for a method call {@code a.b(c,d)} returns {@code [a, c, d]}
|
||||||
{
|
*/
|
||||||
|
public List<Expr> allArguments() {
|
||||||
ArrayList<Expr> result = new ArrayList<Expr>();
|
ArrayList<Expr> result = new ArrayList<Expr>();
|
||||||
for (Ast chi : this.rwChildren)
|
for (Ast chi : this.rwChildren)
|
||||||
result.add((Expr) chi);
|
result.add((Expr) chi);
|
||||||
return Collections.unmodifiableList(result);
|
return Collections.unmodifiableList(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns all arguments to the method, without the receiver.
|
/**
|
||||||
* i.e, for a method call {@code a.b(c,d)} returns {@code [c, d]} */
|
* Returns all arguments to the method, without the receiver.
|
||||||
public List<Expr> argumentsWithoutReceiver()
|
* i.e, for a method call {@code a.b(c,d)} returns {@code [c, d]}
|
||||||
{
|
*/
|
||||||
|
public List<Expr> argumentsWithoutReceiver() {
|
||||||
ArrayList<Expr> result = new ArrayList<Expr>();
|
ArrayList<Expr> result = new ArrayList<Expr>();
|
||||||
for (int i = 1; i < this.rwChildren.size(); i++)
|
for (int i = 1; i < this.rwChildren.size(); i++)
|
||||||
result.add((Expr) this.rwChildren.get(i));
|
result.add((Expr) this.rwChildren.get(i));
|
||||||
|
@ -460,19 +596,40 @@ public abstract class Ast {
|
||||||
return visitor.methodCall(this, arg);
|
return visitor.methodCall(this, arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<Expr> deepCopyArguments() {
|
||||||
|
|
||||||
|
ArrayList<Expr> result = new ArrayList<Expr>();
|
||||||
|
|
||||||
|
for (final Expr expr : argumentsWithoutReceiver()) {
|
||||||
|
result.add((Expr) expr.deepCopy());
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MethodCallExpr deepCopy() {
|
||||||
|
return postCopy(new MethodCallExpr((Expr) receiver().deepCopy(), methodName, deepCopyArguments()));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// _________________________________________________________________
|
// _________________________________________________________________
|
||||||
// Statements
|
// Statements
|
||||||
|
|
||||||
/** Interface for all statements */
|
/**
|
||||||
|
* Interface for all statements
|
||||||
|
*/
|
||||||
public static abstract class Stmt extends Ast {
|
public static abstract class Stmt extends Ast {
|
||||||
protected Stmt(int fixedCount) {
|
protected Stmt(int fixedCount) {
|
||||||
super(fixedCount);
|
super(fixedCount);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Represents an empty statement: has no effect. */
|
/**
|
||||||
|
* Represents an empty statement: has no effect.
|
||||||
|
*/
|
||||||
public static class Nop extends Stmt {
|
public static class Nop extends Stmt {
|
||||||
|
|
||||||
public Nop() {
|
public Nop() {
|
||||||
|
@ -484,9 +641,15 @@ public abstract class Ast {
|
||||||
return visitor.nop(this, arg);
|
return visitor.nop(this, arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Ast deepCopy() {
|
||||||
|
return new Nop();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** An assignment from {@code right()} to the location
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An assignment from {@code right()} to the location
|
||||||
* represented by {@code left()}.
|
* represented by {@code left()}.
|
||||||
*/
|
*/
|
||||||
public static class Assign extends Stmt {
|
public static class Assign extends Stmt {
|
||||||
|
@ -498,17 +661,32 @@ public abstract class Ast {
|
||||||
setRight(right);
|
setRight(right);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Expr left() { return (Expr) this.rwChildren.get(0); }
|
public Expr left() {
|
||||||
public void setLeft(Expr node) { this.rwChildren.set(0, node); }
|
return (Expr) this.rwChildren.get(0);
|
||||||
|
}
|
||||||
|
|
||||||
public Expr right() { return (Expr) this.rwChildren.get(1); }
|
public void setLeft(Expr node) {
|
||||||
public void setRight(Expr node) { this.rwChildren.set(1, node); }
|
this.rwChildren.set(0, node);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Expr right() {
|
||||||
|
return (Expr) this.rwChildren.get(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRight(Expr node) {
|
||||||
|
this.rwChildren.set(1, node);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <R, A> R accept(AstVisitor<R, A> visitor, A arg) {
|
public <R, A> R accept(AstVisitor<R, A> visitor, A arg) {
|
||||||
return visitor.assign(this, arg);
|
return visitor.assign(this, arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Ast deepCopy() {
|
||||||
|
return new Assign((Expr) left().deepCopy(), (Expr) right().deepCopy());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class IfElse extends Stmt {
|
public static class IfElse extends Stmt {
|
||||||
|
@ -521,20 +699,40 @@ public abstract class Ast {
|
||||||
setOtherwise(otherwise);
|
setOtherwise(otherwise);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Expr condition() { return (Expr) this.rwChildren.get(0); }
|
public Expr condition() {
|
||||||
public void setCondition(Expr node) { this.rwChildren.set(0, node); }
|
return (Expr) this.rwChildren.get(0);
|
||||||
|
}
|
||||||
|
|
||||||
public Ast then() { return this.rwChildren.get(1); }
|
public void setCondition(Expr node) {
|
||||||
public void setThen(Ast node) { this.rwChildren.set(1, node); }
|
this.rwChildren.set(0, node);
|
||||||
|
}
|
||||||
|
|
||||||
public Ast otherwise() { return this.rwChildren.get(2); }
|
public Ast then() {
|
||||||
public void setOtherwise(Ast node) { this.rwChildren.set(2, node); }
|
return this.rwChildren.get(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setThen(Ast node) {
|
||||||
|
this.rwChildren.set(1, node);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Ast otherwise() {
|
||||||
|
return this.rwChildren.get(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOtherwise(Ast node) {
|
||||||
|
this.rwChildren.set(2, node);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <R, A> R accept(AstVisitor<R, A> visitor, A arg) {
|
public <R, A> R accept(AstVisitor<R, A> visitor, A arg) {
|
||||||
return visitor.ifElse(this, arg);
|
return visitor.ifElse(this, arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Ast deepCopy() {
|
||||||
|
return new IfElse((Expr) condition().deepCopy(), then().deepCopy(), otherwise().deepCopy());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -545,14 +743,24 @@ public abstract class Ast {
|
||||||
setArg(arg);
|
setArg(arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Expr arg() { return (Expr) this.rwChildren.get(0); }
|
public Expr arg() {
|
||||||
public void setArg(Expr node) { this.rwChildren.set(0, node); }
|
return (Expr) this.rwChildren.get(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setArg(Expr node) {
|
||||||
|
this.rwChildren.set(0, node);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <R, A> R accept(AstVisitor<R, A> visitor, A arg) {
|
public <R, A> R accept(AstVisitor<R, A> visitor, A arg) {
|
||||||
return visitor.returnStmt(this, arg);
|
return visitor.returnStmt(this, arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Ast deepCopy() {
|
||||||
|
return new ReturnStmt(arg() != null ? (Expr) arg().deepCopy() : null);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class BuiltInWrite extends Stmt {
|
public static class BuiltInWrite extends Stmt {
|
||||||
|
@ -563,14 +771,24 @@ public abstract class Ast {
|
||||||
setArg(arg);
|
setArg(arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Expr arg() { return (Expr) this.rwChildren.get(0); }
|
public Expr arg() {
|
||||||
public void setArg(Expr node) { this.rwChildren.set(0, node); }
|
return (Expr) this.rwChildren.get(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setArg(Expr node) {
|
||||||
|
this.rwChildren.set(0, node);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <R, A> R accept(AstVisitor<R, A> visitor, A arg) {
|
public <R, A> R accept(AstVisitor<R, A> visitor, A arg) {
|
||||||
return visitor.builtInWrite(this, arg);
|
return visitor.builtInWrite(this, arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Ast deepCopy() {
|
||||||
|
return new BuiltInWrite((Expr) arg().deepCopy());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class BuiltInWriteln extends Stmt {
|
public static class BuiltInWriteln extends Stmt {
|
||||||
|
@ -584,6 +802,11 @@ public abstract class Ast {
|
||||||
return visitor.builtInWriteln(this, arg);
|
return visitor.builtInWriteln(this, arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Ast deepCopy() {
|
||||||
|
return new BuiltInWriteln();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class MethodCall extends Stmt {
|
public static class MethodCall extends Stmt {
|
||||||
|
@ -602,6 +825,11 @@ public abstract class Ast {
|
||||||
return visitor.methodCall(this, arg);
|
return visitor.methodCall(this, arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Ast deepCopy() {
|
||||||
|
return new MethodCall(this.getMethodCallExpr().deepCopy());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class WhileLoop extends Stmt {
|
public static class WhileLoop extends Stmt {
|
||||||
|
@ -613,22 +841,39 @@ public abstract class Ast {
|
||||||
setBody(body);
|
setBody(body);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Expr condition() { return (Expr) this.rwChildren.get(0); }
|
public Expr condition() {
|
||||||
public void setCondition(Expr cond) { this.rwChildren.set(0, cond); }
|
return (Expr) this.rwChildren.get(0);
|
||||||
|
}
|
||||||
|
|
||||||
public Ast body() { return this.rwChildren.get(1); }
|
public void setCondition(Expr cond) {
|
||||||
public void setBody(Ast body) { this.rwChildren.set(1, body); }
|
this.rwChildren.set(0, cond);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Ast body() {
|
||||||
|
return this.rwChildren.get(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBody(Ast body) {
|
||||||
|
this.rwChildren.set(1, body);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <R, A> R accept(AstVisitor<R, A> visitor, A arg) {
|
public <R, A> R accept(AstVisitor<R, A> visitor, A arg) {
|
||||||
return visitor.whileLoop(this, arg);
|
return visitor.whileLoop(this, arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Ast deepCopy() {
|
||||||
|
return new WhileLoop((Expr) condition().deepCopy(), body().deepCopy());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// _________________________________________________________________
|
// _________________________________________________________________
|
||||||
// Declarations
|
// Declarations
|
||||||
|
|
||||||
/** Interface for all declarations */
|
/**
|
||||||
|
* Interface for all declarations
|
||||||
|
*/
|
||||||
public static abstract class Decl extends Ast {
|
public static abstract class Decl extends Ast {
|
||||||
protected Decl(int fixedCount) {
|
protected Decl(int fixedCount) {
|
||||||
super(fixedCount);
|
super(fixedCount);
|
||||||
|
@ -656,10 +901,17 @@ public abstract class Ast {
|
||||||
return visitor.varDecl(this, arg);
|
return visitor.varDecl(this, arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Ast deepCopy() {
|
||||||
|
return new VarDecl(type, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Used in {@link MethodDecl} to group together declarations
|
}
|
||||||
* and method bodies. */
|
|
||||||
|
/**
|
||||||
|
* Used in {@link MethodDecl} to group together declarations
|
||||||
|
* and method bodies.
|
||||||
|
*/
|
||||||
public static class Seq extends Decl {
|
public static class Seq extends Decl {
|
||||||
|
|
||||||
public Seq(List<Ast> nodes) {
|
public Seq(List<Ast> nodes) {
|
||||||
|
@ -667,7 +919,9 @@ public abstract class Ast {
|
||||||
if (nodes != null) this.rwChildren.addAll(nodes);
|
if (nodes != null) this.rwChildren.addAll(nodes);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Grant access to the raw list of children for seq nodes */
|
/**
|
||||||
|
* Grant access to the raw list of children for seq nodes
|
||||||
|
*/
|
||||||
public List<Ast> rwChildren() {
|
public List<Ast> rwChildren() {
|
||||||
return this.rwChildren;
|
return this.rwChildren;
|
||||||
}
|
}
|
||||||
|
@ -676,6 +930,19 @@ public abstract class Ast {
|
||||||
public <R, A> R accept(AstVisitor<R, A> visitor, A arg) {
|
public <R, A> R accept(AstVisitor<R, A> visitor, A arg) {
|
||||||
return visitor.seq(this, arg);
|
return visitor.seq(this, arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Ast deepCopy() {
|
||||||
|
|
||||||
|
List<Ast> result = new ArrayList<Ast>();
|
||||||
|
|
||||||
|
for (final Ast ast : this.rwChildren) {
|
||||||
|
result.add(ast.deepCopy());
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Seq(result);
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class MethodDecl extends Decl {
|
public static class MethodDecl extends Decl {
|
||||||
|
@ -685,6 +952,8 @@ public abstract class Ast {
|
||||||
public List<String> argumentTypes;
|
public List<String> argumentTypes;
|
||||||
public List<String> argumentNames;
|
public List<String> argumentNames;
|
||||||
public MethodSymbol sym;
|
public MethodSymbol sym;
|
||||||
|
public ControlFlowGraph cfg;
|
||||||
|
|
||||||
public MethodDecl(
|
public MethodDecl(
|
||||||
String returnType,
|
String returnType,
|
||||||
String name,
|
String name,
|
||||||
|
@ -693,6 +962,7 @@ public abstract class Ast {
|
||||||
Seq body) {
|
Seq body) {
|
||||||
this(returnType, name, Pair.unzipA(formalParams), Pair.unzipB(formalParams), decls, body);
|
this(returnType, name, Pair.unzipA(formalParams), Pair.unzipB(formalParams), decls, body);
|
||||||
}
|
}
|
||||||
|
|
||||||
public MethodDecl(
|
public MethodDecl(
|
||||||
String returnType,
|
String returnType,
|
||||||
String name,
|
String name,
|
||||||
|
@ -709,17 +979,38 @@ public abstract class Ast {
|
||||||
setBody(body);
|
setBody(body);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Seq decls() { return (Seq) this.rwChildren.get(0); }
|
public Seq decls() {
|
||||||
public void setDecls(Seq decls) { this.rwChildren.set(0, decls); }
|
return (Seq) this.rwChildren.get(0);
|
||||||
|
}
|
||||||
|
|
||||||
public Seq body() { return (Seq) this.rwChildren.get(1); }
|
public void setDecls(Seq decls) {
|
||||||
public void setBody(Seq body) { this.rwChildren.set(1, body); }
|
this.rwChildren.set(0, decls);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Seq body() {
|
||||||
|
return (Seq) this.rwChildren.get(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBody(Seq body) {
|
||||||
|
this.rwChildren.set(1, body);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <R, A> R accept(AstVisitor<R, A> visitor, A arg) {
|
public <R, A> R accept(AstVisitor<R, A> visitor, A arg) {
|
||||||
return visitor.methodDecl(this, arg);
|
return visitor.methodDecl(this, arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Ast deepCopy() {
|
||||||
|
return new MethodDecl(
|
||||||
|
returnType,
|
||||||
|
name,
|
||||||
|
Collections.unmodifiableList(argumentTypes),
|
||||||
|
Collections.unmodifiableList(argumentNames),
|
||||||
|
(Seq) decls().deepCopy(),
|
||||||
|
(Seq) body().deepCopy());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class ClassDecl extends Decl {
|
public static class ClassDecl extends Decl {
|
||||||
|
@ -755,5 +1046,17 @@ public abstract class Ast {
|
||||||
return visitor.classDecl(this, arg);
|
return visitor.classDecl(this, arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Ast deepCopy() {
|
||||||
|
|
||||||
|
List<Ast> result = new ArrayList<Ast>();
|
||||||
|
|
||||||
|
for (final Ast ast : members()) {
|
||||||
|
result.add(ast.deepCopy());
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ClassDecl(name, superClass, result);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
51
src/cd/ir/AstReplaceVisitor.java
Executable file
51
src/cd/ir/AstReplaceVisitor.java
Executable 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -26,6 +26,7 @@ public class AstRewriteVisitor<A> extends AstVisitor<Ast, A> {
|
||||||
* <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) {
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,14 +4,17 @@ 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 */
|
/**
|
||||||
|
* A visitor that visits any kind of node
|
||||||
|
*/
|
||||||
public class AstVisitor<R, A> extends ExprVisitor<R, A> {
|
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);
|
||||||
}
|
}
|
||||||
|
@ -43,19 +46,22 @@ public class AstVisitor<R,A> extends ExprVisitor<R,A> {
|
||||||
/**
|
/**
|
||||||
* 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);
|
||||||
|
@ -63,7 +69,8 @@ public class AstVisitor<R,A> extends ExprVisitor<R,A> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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);
|
||||||
}
|
}
|
||||||
|
|
113
src/cd/ir/BasicBlock.java
Normal file
113
src/cd/ir/BasicBlock.java
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
135
src/cd/ir/CompileTimeEvaluator.java
Normal file
135
src/cd/ir/CompileTimeEvaluator.java
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
61
src/cd/ir/ControlFlowGraph.java
Normal file
61
src/cd/ir/ControlFlowGraph.java
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -10,7 +10,8 @@ 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);
|
||||||
}
|
}
|
||||||
|
@ -29,7 +30,8 @@ public class ExprVisitor<R,A> {
|
||||||
/**
|
/**
|
||||||
* 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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
package cd.ir;
|
package cd.ir;
|
||||||
|
|
||||||
|
import cd.backend.codegen.AstCodeGenerator;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -51,7 +53,9 @@ public abstract class Symbol {
|
||||||
|
|
||||||
public static class PrimitiveTypeSymbol extends TypeSymbol {
|
public static class PrimitiveTypeSymbol extends TypeSymbol {
|
||||||
|
|
||||||
/** Symbols for the built-in primitive types */
|
/**
|
||||||
|
* Symbols for the built-in primitive types
|
||||||
|
*/
|
||||||
public static final PrimitiveTypeSymbol intType = new PrimitiveTypeSymbol("int");
|
public static final PrimitiveTypeSymbol intType = new PrimitiveTypeSymbol("int");
|
||||||
public static final PrimitiveTypeSymbol voidType = new PrimitiveTypeSymbol("void");
|
public static final PrimitiveTypeSymbol voidType = new PrimitiveTypeSymbol("void");
|
||||||
public static final PrimitiveTypeSymbol booleanType = new PrimitiveTypeSymbol("boolean");
|
public static final PrimitiveTypeSymbol booleanType = new PrimitiveTypeSymbol("boolean");
|
||||||
|
@ -101,7 +105,15 @@ public abstract class Symbol {
|
||||||
public final Map<String, MethodSymbol> methods =
|
public final Map<String, MethodSymbol> methods =
|
||||||
new HashMap<String, MethodSymbol>();
|
new HashMap<String, MethodSymbol>();
|
||||||
|
|
||||||
/** Symbols for the built-in Object and null types */
|
public int totalMethods = -1;
|
||||||
|
|
||||||
|
public int totalFields = -1;
|
||||||
|
|
||||||
|
public int sizeof = -1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Symbols for the built-in Object and null types
|
||||||
|
*/
|
||||||
public static final ClassSymbol nullType = new ClassSymbol("<null>");
|
public static final ClassSymbol nullType = new ClassSymbol("<null>");
|
||||||
public static final ClassSymbol objectType = new ClassSymbol("Object");
|
public static final ClassSymbol objectType = new ClassSymbol("Object");
|
||||||
|
|
||||||
|
@ -110,8 +122,10 @@ public abstract class Symbol {
|
||||||
this.ast = ast;
|
this.ast = ast;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Used to create the default {@code Object}
|
/**
|
||||||
* and {@code <null>} types */
|
* Used to create the default {@code Object}
|
||||||
|
* and {@code <null>} types
|
||||||
|
*/
|
||||||
public ClassSymbol(String name) {
|
public ClassSymbol(String name) {
|
||||||
super(name);
|
super(name);
|
||||||
this.ast = null;
|
this.ast = null;
|
||||||
|
@ -152,6 +166,12 @@ public abstract class Symbol {
|
||||||
|
|
||||||
public TypeSymbol returnType;
|
public TypeSymbol returnType;
|
||||||
|
|
||||||
|
public ClassSymbol owner;
|
||||||
|
|
||||||
|
public int vtableIndex = -1;
|
||||||
|
|
||||||
|
public MethodSymbol overrides;
|
||||||
|
|
||||||
public MethodSymbol(Ast.MethodDecl ast) {
|
public MethodSymbol(Ast.MethodDecl ast) {
|
||||||
super(ast.name);
|
super(ast.name);
|
||||||
this.ast = ast;
|
this.ast = ast;
|
||||||
|
@ -165,10 +185,25 @@ public abstract class Symbol {
|
||||||
|
|
||||||
public static class VariableSymbol extends Symbol {
|
public static class VariableSymbol extends Symbol {
|
||||||
|
|
||||||
public static enum Kind { PARAM, LOCAL, FIELD };
|
public static enum Kind {PARAM, LOCAL, FIELD}
|
||||||
|
|
||||||
|
;
|
||||||
public final TypeSymbol type;
|
public final TypeSymbol type;
|
||||||
public final Kind kind;
|
public final Kind kind;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Meaning depends on the kind of variable, but generally refers
|
||||||
|
* to the offset in bytes from some base ptr to where the variable
|
||||||
|
* is found.
|
||||||
|
* <ul>
|
||||||
|
* <li>{@code PARAM}, {@code LOCAL}: Offset from BP
|
||||||
|
* <li>{@code FIELD}: Offset from object
|
||||||
|
* <li>{@code CONSTANT}: N/A
|
||||||
|
* </ul>
|
||||||
|
* Computed in {@link AstCodeGenerator}.
|
||||||
|
*/
|
||||||
|
public int offset = -1;
|
||||||
|
|
||||||
public VariableSymbol(String name, TypeSymbol type) {
|
public VariableSymbol(String name, TypeSymbol type) {
|
||||||
this(name, type, Kind.PARAM);
|
this(name, type, Kind.PARAM);
|
||||||
}
|
}
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue