diff --git a/AstBuilding.py b/AstBuilding.py index e3382ba4857ebe8f718af68b222fd8d5a9693318..5d5f6ada335a0d9e088a004d15e9a01e3130f182 100644 --- a/AstBuilding.py +++ b/AstBuilding.py @@ -28,6 +28,8 @@ def buildEnvAndLink(ASTs): # print('\n\n\n', t[0]) # print("###################### Comp Unit Env ####################") # t[1].recurseAction("printEnv") + # if t[0] == "./Tests/A5/J1_02_Array_Length.java": + # t[1].printTree() # type Linking @@ -57,18 +59,62 @@ def reachabilityChecking(ASTs): t[1].reachCheck() #################################################### +# Returns a dictionary (type e.g. "boolean") -> str (the assembly code generated) +def arrayClassMemory(types): + # All method labels from java.lang.Object + # TODO: add subtype testing table + methods = ["M_Object_Object_", "M_Object_equals_Object", "M_Object_toString_", "M_Object_hashCode_", "M_Object_clone_", "M_Object_getClass_"] + typeDict = {} + for t in types: + # Class memory layout + # Label: "A_type" + # Label for SIT pointer: "I_SIT_spot_typeName_array" + # Label for method pointers: "V_typeName_methodName_param_array" + code = "section .data\n" + code += "; Start of class memory layout\n" + code += pLabel(name=t, type="array") + \ + pLabel("SIT_spot_"+ t + "_array", "inter") + \ + p(instruction="dd", arg1=64) + for m in methods: + code += pLabel(name=t+"_"+m+"_array", type="vtable") + \ + p(instruction="dd", arg1=64) + code += "; End of class memory layout\n" + + # Creating a function to initialize class memory layout + # Function label: H_typeName_arrayMemoryInitialize + code += "section .text\n" + \ + "; Function to initialize class memory layout\n" + \ + pLabel(name=t+"_arrayMemoryInitialize", type="helper") + \ + p(instruction="mov", arg1="eax", arg2="dword I_SIT_spot_"+t+ "_array") + \ + p(instruction="extern", arg1="I_SIT_Object") + \ + p(instruction="mov", arg1="[eax]", arg2="dword I_SIT_Object", comment="Points to the SIT column of java.lang.Object") + + for m in methods: + code += p(instruction="extern", arg1=m) + \ + p(instruction="mov", arg1="eax", arg2="dword V_"+t+"_"+m+"_array") + \ + p(instruction="mov", arg1="[eax]", arg2="dword "+m, comment="points to method implementation") + code += p(instruction="ret", arg1="") + code += "; End of function to initialize class memory layout\n" + + typeDict[t] = code + + return typeDict + + # Preparation before code Gen: # 1. Calculating the size of the object from each class # (also simultaneosly creating fieldOffset because fieldOffset table is required to generate size) +# 2. SIT building +# 3. Creating class memory layout for every possible array types def codeGenPrep(ASTs): interM = [] - types = [] - + types = ['boolean', 'byte', 'char', 'int', 'short'] # All possible types for t in ASTs: classInterNode = t[1].typeDcl types.append(classInterNode) if classInterNode.__class__.__name__ == "ClassNode": classInterNode.populateSizeAndFieldOffset() + types.append(classInterNode.name) else: # interfaceNode, get their methods to prep for SIT interM += classInterNode.methods @@ -88,10 +134,17 @@ def codeGenPrep(ASTs): classInterNode.SITsize = len(interM) classInterNode.subTypeSize = len(types) + return arrayClassMemory(types) + def codeGen(ASTs, output="output"): - codeGenPrep(ASTs) ASTs = ASTs[:1] # TOREMOVE: don't compile stdlib for now + # rASTs = [] + # for index,t in enumerate(ASTs): + # if index == 0 or t[0] == "stdlib/5.0/java/lang/Object.java": + # rASTs.append(t) + # ASTs = rASTs + arrayClassMemory = codeGenPrep(ASTs) for t in ASTs: t[1].codeGen() # Outputting the generated code into className.s @@ -115,13 +168,13 @@ def codeGen(ASTs, output="output"): f.close() # ASTs[0][1].codeGen() - startGen(ASTs, output) # pass in the first AST that contains the test method + startGen(ASTs, output, arrayClassMemory) # pass in the first AST that contains the test method # reset lables for running multiple testCases used_labels = set() local_labels = 0 -def startGen(ASTs, output): +def startGen(ASTs, output, arrayClassMemory): # Test method lable ast = ASTs[0][1] @@ -132,16 +185,29 @@ def startGen(ASTs, output): for t in ASTs: classInterNode = t[1].typeDcl if classInterNode and classInterNode.__class__.__name__ == "ClassNode": - label = "H_" + classInterNode.name + "_" + "classAndStaticFieldInit \n" + label = "H_" + classInterNode.name + "_" + "classAndStaticFieldInit" callInit += p(instruction="extern", arg1=label) + \ p(instruction="call", arg1=label) callInit += "; End of calling each class's classAndStaticFieldInit \n" + callArrayInit = "; Start of calling each array type's arrayMemoryInitialize\n" + for t, code in arrayClassMemory.items(): + label = "H_" + t + "_arrayMemoryInitialize" + callArrayInit += p(instruction="extern", arg1=label) + \ + p(instruction="call", arg1=label) + # Creating .s files for each array type to store the class memory layout + fileName = output + "/" + t + "_arrayClass" + ".s" + f = open(fileName, "w") + f.write(code) + f.close() + callArrayInit += "; End of calling each array type's arrayMemoryInitialize\n" + f = open( output + "/RunTest.s","w") result = " global _start\n_start:\n" \ + callInit \ + + callArrayInit \ + p("extern", method_label, None, None) \ + p("call", method_label, None, None) \ + p("mov", "ebx", "eax", " move eax to exit code") \ diff --git a/CodeGenUtils.py b/CodeGenUtils.py index 76cb71aa7f0afd0e66557e28e24d8455096bc71c..11f7e2a741a8d7b808911d405a1b3c56d2e2a06a 100644 --- a/CodeGenUtils.py +++ b/CodeGenUtils.py @@ -10,7 +10,7 @@ def p(instruction, arg1, arg2="", comment=""): return result + "\n" # format a string that represents label -# String, "class"/"method"/"field"/"constant"/"local/vtable/"srarix"/"helper","globalConstant" +# String, "class"/"method"/"field"/"constant"/"local/vtable/"srarix"/"helper","globalConstant", "array" # helper: useful helper functions (these ARE NOT METHODS) # (label list to be extended) # (local label are just for local running of the code) @@ -32,20 +32,24 @@ def getLStringLabel(): def pLabel(name, type, comment=""): global local_labels global used_labels + C = "" # Used for checking if lable is used + l = "" # The result label if type == "local": - l = "_" + str(local_labels) + C = "_" + str(local_labels) local_labels += 1 + l = type[0].upper() + "_" + name else: if type == "constant": l = "cc_" + name else : l = type[0].upper() + "_" + name + C = l # if l in used_labels: # raise("ERROR: Generated repeated label " + l) - used_labels.add(l) + used_labels.add(C) result = "" - if type in ["class", "method", "static", "helper", "globalConstant"]: # make global + if type in ["class", "method", "static", "helper", "globalConstant", "array"]: # make global result += "global " + l + "\n" if comment: result += l + ": ;" + comment + "\n" @@ -134,6 +138,18 @@ def genericHelperFunctions(): p(instruction="cmp", arg1="eax", arg2="[G__Zero]") + \ p(instruction="jle", arg1="H__Throw_Exception") + \ p(instruction="ret", arg1="") + + # Helper function to perform bounds check on array + # eax = pointer to array + # ecx = index i + code += pLabel(name="_Bounds_Check", type="helper", comment="helper function to check array bounds") + \ + p(instruction="push", arg1="ebx", comment="saving ebx") + \ + p(instruction="mov", arg1="ebx", arg2="[eax+4]", comment="ebx stores length of array") + \ + p(instruction="cmp", arg1="ecx", arg2="ebx") + \ + p(instruction="jge", arg1="H__Throw_Exception") + \ + p(instruction="pop", arg1="ebx", comment="restoring ebx") + \ + p(instruction="ret", arg1="") + return code # Generates code to import helper generic functions and global constants diff --git a/ExprPrimaryNodes.py b/ExprPrimaryNodes.py index 015d1196df10a6fcde3a959f28280c464ea594ca..875443891797d24426f33b1e1bfaed15477aae94 100644 --- a/ExprPrimaryNodes.py +++ b/ExprPrimaryNodes.py @@ -164,13 +164,52 @@ class ArrayAccessNode(ASTNode): raise Exception("ERROR: Array index must be a number.") self.myType = TypeStruct(self.array.myType.name, self.array.myType.typePointer) + def addr(self): + result = "; Start of calculating address for array access\n" + \ + p(instruction="push", arg1="ecx", comment="saving register ecx") + # 1. Generating code for a + if not hasattr(self.array, "code"): + self.array.codeGen() + result += "; Evaluating pointer to array\n" + \ + self.array.code + \ + p(instruction="push", arg1="eax", comment="push arrray pointer") + + # 2. Evaluating i + if not hasattr(self.index, "code"): + self.index.codeGen() + result += "; Evaluating index i\n"+ \ + self.index.code + \ + p(instruction="mov", arg1="ecx", arg2="eax", comment="ecx stores index i") + + # 3. Runtime checks + result += p(instruction="pop", arg1="eax", comment="eax is pointer to array") + \ + p(instruction="call", arg1="H__Null_Check") + \ + p(instruction="call", arg1="H__Bounds_Check") + + # 4. Make eax store the address to the item we're accessing + result += p(instruction="add", arg1="eax", arg2=8, comment="eax points at a[0]") + \ + p(instruction="imul", arg1="ecx", arg2=4, comment="ecx contains the number of bytes to shift according to i") + \ + p(instruction="add", arg1="eax", arg2="ecx", comment="eax now points at a[i]") + \ + p(instruction="pop", arg1="ecx", comment="restoring ecx") + + result += "; End of calculating address for array access\n" + + return result + + def codeGen(self): + if hasattr(self, "code"): + return + self.code = "; Array access\n" + \ + self.addr() + \ + p(instruction="mov", arg1="eax", arg2="[eax]", comment="eax now contains a[i]") +\ + "; End of array access\n" + + ################################################################################### # arrayCreationExpr # arrayCreationExpr NEW primitiveType LSQRBRACK expr RSQRBRACK # arrayCreationExpr NEW name LSQRBRACK expr RSQRBRACK -# arrayCreationExpr NEW primitiveType LSQRBRACK RSQRBRACK -# arrayCreationExpr NEW name LSQRBRACK RSQRBRACK class ArrayCreateNode(ASTNode): # always list all fields in the init method to show the class structure def __init__(self, parseTree, typeName): @@ -203,6 +242,41 @@ class ArrayCreateNode(ASTNode): self.myType = TypeStruct(self.arrayType.myType.name, self.arrayType.myType.typePointer) self.myType.isArray = True + def codeGen(self): + if hasattr(self, "code"): + return + self.code = "; Creating an array\n" + + # 1. Allocating space for the array in heap + # Note: uses ecx to temporarily store the array length + if not hasattr(self.arraySize, "code"): + self.arraySize.codeGen() + self.code += "; Evaluating array length\n" + \ + self.arraySize.code + \ + " ; End of evaluating array length\n" + \ + p(instruction="push", arg1="ecx", comment="saving ecx's value") + \ + p(instruction="mov", arg1="ecx", arg2="eax", comment="ecx now stroes the array's length") + \ + p(instruction="add", arg1="eax", arg2=2, comment="number of items to allocate on heap") + \ + p(instruction="imul", arg1="eax", arg2=4, comment="number of bytes to allocate on heap") + \ + p(instruction="call", arg1="__malloc", comment="allocating memory for array on heap") + + # 2. Pointing first item to vtable + aLabel = "A_" + if self.arrayType.myType.isPrimitive: + aLabel += self.arrayType.myType.name + else: + aLabel += self.arrayType.myType.typePointer.name + + self.code += p(instruction="extern", arg1=aLabel) + \ + p(instruction="mov", arg1="[eax]", arg2="dword "+aLabel, comment="first item is vtable pointer") + + # 3. Storing length in the second slot + self.code += p(instruction="mov", arg1="[eax+4]", arg2="ecx", comment="storing array length in second slot") + + # 4. Restoring ecx + self.code += p(instruction="pop", arg1="ecx", comment="restoring register ecx") + self.code += ";End of array creation\n" + ################################################################################### # assignment leftHandSide ASSIGN expr class AssignNode(ASTNode): @@ -732,10 +806,13 @@ class FieldAccessNode(ASTNode): self.env = None self.children = [] self.typeName = typeName # the type (class/interface) this node belongs under + self.isLength = False if not compid: # input: fieldAccess primary PERIOD ID self.primary = makeNodeFromAllPrimary(parseTree.children[0], typeName) self.ID = NameNode(parseTree.children[2], False, typeName) + if self.ID.name == "length": + self.isLength = True self.children.append(self.primary) else: # input: COMPID @@ -783,7 +860,10 @@ class FieldAccessNode(ASTNode): # At this point, eax stores the address of the object result += p(instruction="call", arg1="H__Null_Check", comment="calling null check function") # Make eax store the address to the field we're accessing - result += p(instruction="add", arg1="eax", arg2=self.ID.prefixLink.offset, comment="calculating pointer to the field") + if self.isLength: + result += p(instruction="add", arg1="eax", arg2=4, comment="calculating pointer to length") + else: + result += p(instruction="add", arg1="eax", arg2=self.ID.prefixLink.offset, comment="calculating pointer to the field") result += "; End of calculating address for non-static field\n" return result diff --git a/NameNode.py b/NameNode.py index 68ee3bd75ae648b95be7d89e1939007edd27deca..859f25fdeb799941053e0a70b25754de7ab09056 100644 --- a/NameNode.py +++ b/NameNode.py @@ -239,7 +239,9 @@ class NameNode(ASTNode): if curType.myType and curType.myType.isArray and self.IDs[0] == 'length': self.myType = TypeStruct("int", None) - # TODO: handle length at nameNode + cNameNode = genNameNode(self.typeName) + cNameNode.isLength = True + self.prefixNodes.append(cNameNode) return else: if curType.__class__.__name__ in ['ParamNode', 'VarDclNode']: diff --git a/Tests/A5/J1_02_Array_Creation.java b/Tests/A5/J1_02_Array_Creation.java new file mode 100644 index 0000000000000000000000000000000000000000..6e6482d1100a7a2667e2edf0ac49eea1f64c8d78 --- /dev/null +++ b/Tests/A5/J1_02_Array_Creation.java @@ -0,0 +1,10 @@ +public class J1_02_Array_Creation { + public int i = 5; + public J1_02_Array_Creation(){} + public static int test(){ + J1_02_Array_Creation[] array = new J1_02_Array_Creation[10]; + array[0] = new J1_02_Array_Creation(); + array[9] = new J1_02_Array_Creation(); + return array[0].i; + } +} \ No newline at end of file diff --git a/Tests/A5/J1_02_Array_Length.java b/Tests/A5/J1_02_Array_Length.java new file mode 100644 index 0000000000000000000000000000000000000000..451e663ea2051b68d43b183d3a51f7fec6633b07 --- /dev/null +++ b/Tests/A5/J1_02_Array_Length.java @@ -0,0 +1,7 @@ +public class J1_02_Array_Length { + public J1_02_Array_Length(){} + public static int test(){ + J1_02_Array_Length[] array = new J1_02_Array_Length[10]; + return array.length; + } +} \ No newline at end of file diff --git a/Tests/A5/J1_02_Array_Length_Primary.java b/Tests/A5/J1_02_Array_Length_Primary.java new file mode 100644 index 0000000000000000000000000000000000000000..51e3687e2d4408cefe925c3b247f5205301d7e57 --- /dev/null +++ b/Tests/A5/J1_02_Array_Length_Primary.java @@ -0,0 +1,9 @@ +public class J1_02_Array_Length_Primary { + public int[] arr = new int[10]; + public J1_02_Array_Length_Primary(){} + public static int test(){ + J1_02_Array_Length_Primary o = new J1_02_Array_Length_Primary(); + return o.arr.length; + } + +} \ No newline at end of file diff --git a/codeGenNodes.py b/codeGenNodes.py index e911b6d2b290d0c392c41f418ef8acc342b472bb..828dc5bd2a13dc32f6d11b36c4b56bf8cbf310d8 100644 --- a/codeGenNodes.py +++ b/codeGenNodes.py @@ -4,7 +4,7 @@ class genNameNode(): def __init__(self, typeName): self.isThis = False self.isStatic = False - # self.isLength = False + self.isLength = False self.nodeLink = None self.typeName = typeName # which type this node resides in self.code = "" @@ -13,6 +13,12 @@ class genNameNode(): def codeGen(self): if self.isThis: self.code = p("mov", "eax", "[ebp + 8]") + elif self.isLength: + # object will already be in eax + # Null check + self.code += p("call", "H__Null_Check") + self.code += p("add", "eax", 4) + self.code += p("mov", "eax", "[eax]") elif self.nodeLink.__class__.__name__ == "VarDclNode": self.code = p("mov", "eax", "[ebp - " + str(self.nodeLink.offset) + "]") elif self.nodeLink.__class__.__name__ == "ParamNode": @@ -36,6 +42,10 @@ class genNameNode(): if self.isThis: result = p("mov", "eax", "ebp") result += p("add", "eax", "8") + # Object will already be in eax + elif self.isLength: + result += p("call", "H__Null_Check") + result += p("add", "eax", 4) elif self.nodeLink.__class__.__name__ == "VarDclNode": result = p("mov", "eax", "ebp") result += p("sub", "eax", str(self.nodeLink.offset))