diff --git a/AstBuilding.py b/AstBuilding.py index f31e27bd462e5612df8358a3c39e8a72871f03cc..c7b6dcdc07808f53c0e54b1246b4c73a665db55a 100644 --- a/AstBuilding.py +++ b/AstBuilding.py @@ -1,7 +1,7 @@ from CompNode import CompNode from pprint import pprint from Environment import GlobalEnv -from CodeGenUtils import p, genProcedure, used_labels, local_labels, pLabel, genHelperFunctions, genGlobalImport +from CodeGenUtils import p, genProcedure, used_labels, local_labels, pLabel, genericHelperFunctions, globalImport # tree: (filename, parseTree) def astBuild(trees): @@ -79,28 +79,46 @@ def codeGen(ASTs, output="output"): if classInterNode and classInterNode.__class__.__name__ == "ClassNode": fileName = output + "/" + classInterNode.name + ".s" f = open(fileName, "w") + f.write("section .data\n") + f.write(classInterNode.data) + f.write("section .text\n") if not flagGlobalHelper: - f.write(genGlobalImport(True)) - f.write(genHelperFunctions()) + f.write(globalImport(True)) + f.write(genericHelperFunctions()) flagGlobalHelper = True else: - f.write(genGlobalImport()) + f.write(globalImport()) f.write(classInterNode.code) f.close() # ASTs[0][1].codeGen() - startGen(ASTs[0][1], output) # pass in the first AST that contains the test method + startGen(ASTs, output) # pass in the first AST that contains the test method # reset lables for running multiple testCases used_labels = set() local_labels = 0 -def startGen(ast, output): +def startGen(ASTs, output): + + # Test method lable + ast = ASTs[0][1] className = ast.typeDcl.name method_label = "M_" + className + "_test_" + + callInit = "; Start of calling each class's classAndStaticFieldInit \n" + for t in ASTs: + classInterNode = t[1].typeDcl + if classInterNode and classInterNode.__class__.__name__ == "ClassNode": + label = "H_" + classInterNode.name + "_" + "classAndStaticFieldInit \n" + callInit += p(instruction="extern", arg1=label) + \ + p(instruction="call", arg1=label) + callInit += "; End of calling each class's classAndStaticFieldInit \n" + + f = open( output + "/RunTest.s","w") result = " global _start\n_start:\n" \ + + callInit \ + 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 da0e5af46d7d484c0604808e5120330bdd46a556..0b986e50f75a0b64a1336e95e2678904dc69778d 100644 --- a/CodeGenUtils.py +++ b/CodeGenUtils.py @@ -95,7 +95,8 @@ def iffalse(cond, label): return result -def genHelperFunctions(): +# Generates generic (not specific to class) helper functions +def genericHelperFunctions(): code = "" # The Zero constant @@ -110,15 +111,18 @@ def genHelperFunctions(): # Helper function to perform a null check # Note: using jle instead of calling the function since we don't need to preserve eip upon calling the exception function (?) + # saving and restoring ebx, a callee-save register code += pLabel(name="_Null_Check", type="helper", comment="helper function for null check") + \ - p(instruction="mov", arg1="ecx", arg2="[G__Zero]") + \ - p(instruction="cmp", arg1="eax", arg2="ecx") + \ + p(instruction="push", arg1="ebx", comment="saving ebx") + \ + p(instruction="mov", arg1="ebx", arg2="[G__Zero]") + \ + p(instruction="cmp", arg1="eax", arg2="ebx") + \ p(instruction="jle", arg1="H__Throw_Exception") + \ - ";End function for null checking\n" + p(instruction="pop", arg1="ebx", comment="restoring ebx") + \ + p(instruction="ret", arg1="") return code -# Generates code to import helper functions and constants -def genGlobalImport(genGlobalFunction=False): +# Generates code to import helper generic functions and global constants +def globalImport(genGlobalFunction=False): code = "; Importing helper functions and constants\n" + \ p(instruction="extern", arg1="__malloc") + \ p(instruction="extern", arg1="__exception") @@ -131,12 +135,18 @@ def genGlobalImport(genGlobalFunction=False): code += "; End of importing helper functions and constants\n" return code -# Helper function for importation of label +# Helper function for importation of label (only import if it's not already inside the same .s file) def importHelper(targetClass, ownClass, labelName): if targetClass != ownClass: return p(instruction="extern", arg1=labelName, comment="importing label") return "" +# Helper function for allocating space on the heap +def heapAllocate(size): + code = p(instruction="mov", arg1="eax", arg2=size, comment="size of field in bytes") + \ + p(instruction="call", arg1="__malloc", comment="allocating space on heap for the field") + return code + #################################################### class codeSectionUtil(): diff --git a/ExprPrimaryNodes.py b/ExprPrimaryNodes.py index 80555aae2f13d1695661504847a905692d16d827..608c2e1cc5a09e0715585c0480144d9d106e0225 100644 --- a/ExprPrimaryNodes.py +++ b/ExprPrimaryNodes.py @@ -368,7 +368,7 @@ class ClassCreateNode(ASTNode): self.code += importHelper(classDef.name, self.typeName, "C_"+classDef.name) self.code += p(instruction="mov", arg1="[eax]", arg2="[C_"+classDef.name+"]", comment="first item is vtable pointer") - # 4. Calling constructor + # 3. Calling constructor self.code += "; Calling constructor for object\n" self.code += p(instruction="push", arg1="eax", comment="pushing object as first argument") # Evaluate arguments and pushing parameters @@ -379,7 +379,7 @@ class ClassCreateNode(ASTNode): self.code += importHelper(classDef.name, self.typeName, label) self.code += p(instruction="call", arg1=label, comment="Calling constructor") - # 5. Popping parameters and pointer to object + # 4. Popping parameters and pointer to object self.code += p(instruction="add", arg1="esp", arg2=len(self.args.exprs)*4, comment="Popping parameters") + \ p(instruction="pop", arg1="eax", comment="eax now contains pointer to newly created object") self.code += ";End of object creation\n" @@ -680,28 +680,30 @@ class FieldAccessNode(ASTNode): # result stored in eax (address) # NOTE: this is always called by codeGen, so self.code is already intialized to "" def addr(self): - result = "" - fieldNode = self.ID.prefixLink - - if "static" in fieldNode.mods: - label = fieldNode.typeName + "_" + fieldNode.name - result += "; Start of calculating address for static field: " + label + "\n" + \ - importHelper(fieldNode.typeName, self.typeName, "S_"+label) + \ - p(instruction="mov", arg1="eax", arg2="dword S_"+label, comment="getting address to static field") + \ - "; End of field access\n" - - else: - result += "; Start of calculating address for non-static field\n" - self.primary.codeGen() - result += self.primary.code - # Null check - # 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") - result += "; End of calculating address for non-static field\n" - - return result + result = "" + fieldNode = self.ID.prefixLink + if fieldNode.__class__.__name__ == "VarDclNode": + fieldNode = self.ID.staticField + + if "static" in fieldNode.mods: + label = fieldNode.typeName + "_" + fieldNode.name + result += "; Start of calculating address for static field: " + label + "\n" + \ + importHelper(fieldNode.typeName, self.typeName, "S_"+label) + \ + p(instruction="mov", arg1="eax", arg2="dword S_"+label, comment="getting address to static field") + \ + "; End of field access\n" + + else: + result += "; Start of calculating address for non-static field\n" + self.primary.codeGen() + result += self.primary.code + # Null check + # 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") + result += "; End of calculating address for non-static field\n" + + return result def codeGen(self): @@ -828,7 +830,7 @@ class MethodInvNode(ASTNode): if self.args: toPop = len(self.args.exprs) - self.code += p(instruction="add", arg1="esp", arg2=toPop, comment="popping off all arguments off stack") + self.code += p(instruction="add", arg1="esp", arg2=toPop*4, comment="popping off all arguments off stack") # Epilogue self.code += epi diff --git a/MemberNodes.py b/MemberNodes.py index 82658d652dcd21a91640103a95d90f27bc566782..780bd809f04c1b9dfa28a5533495e2ba59d900f7 100644 --- a/MemberNodes.py +++ b/MemberNodes.py @@ -60,19 +60,22 @@ class FieldNode(ASTNode): if hasattr(self, "code"): return self.code = "" + self.data = "" label = self.typeName + "_" + self.name # static fields: the pointer lives in assembly if "static" in self.mods: - self.code += ";Declaring a static field: " + label + "\n" - self.code += pLabel(name=label, type="static") + \ - p(instruction="dd", arg1="64", comment="Declaring space on assembly for a static field") + self.data += ";Declaring a static field: " + label + "\n" + self.data += pLabel(name=label, type="static") + \ + p(instruction="dd", arg1=0, comment="Declaring space on assembly for a static field") + self.data += ";End of declaration of static field\n" # Initializing static fields # static fields are intialized in the order of declaration within the class and has to be intialized # before the test() method is being called initNode = self.variableDcl.variableInit if initNode: + self.code += ";Start of initialization of static field\n" initNode.codeGen() self.code += "; Calculating the initial value of declared field: " + label + "\n" @@ -81,8 +84,8 @@ class FieldNode(ASTNode): self.code += p(instruction="mov", arg1="ebx", arg2="dword S_"+label) + \ p(instruction="mov", arg1="[ebx]", arg2="eax", comment="eax is a pointer to field value in heap") - self.code += ";End of declaration of static field\n" - + self.code += ";End of initialization of static field\n" + ########################################################### diff --git a/NameNode.py b/NameNode.py index 62acb8e6d074b33dd1622ccc7619c74440190524..b413c6963c3b98675a692a52eba2647672c9abb2 100644 --- a/NameNode.py +++ b/NameNode.py @@ -36,6 +36,7 @@ class NameNode(ASTNode): self.myType = None # will become TypeStruct to tell us what the whole is/returns self.shouldBeStatic = False self.pointToThis = False + self.staticField = None self.name = getParseTreeNodes(["ID", "COMPID"], parseTree)[0].lex @@ -130,7 +131,7 @@ class NameNode(ASTNode): typeFieldNode = typeNode.env.getNode(staticFieldName, "fieldDcl") if "static" in typeFieldNode.mods: self.prefixLink = typeFieldNode.variableDcl - + self.staticField = typeFieldNode # if it is primitive, then we leave it as a VarDclNode if not self.prefixLink.dclType.myType.isPrimitive: self.prefixLink = self.prefixLink.dclType.myType.typePointer diff --git a/Tests/A5/J1_00_Field_Basic_Empty.java b/Tests/A5/J1_00_Field_Basic_Empty.java new file mode 100644 index 0000000000000000000000000000000000000000..204244cd68b059cfe32edc4dc34f6bf4ef5694d0 --- /dev/null +++ b/Tests/A5/J1_00_Field_Basic_Empty.java @@ -0,0 +1,4 @@ +public class J1_00_Field_Basic_Empty { + public J1_00_Field_Basic_Empty(){} + public static void test(){} +} \ No newline at end of file diff --git a/Tests/A5/J1_00_Field_Basic_Static_Fields.java b/Tests/A5/J1_00_Field_Basic_Static_Fields.java index 6e353aaafffd7cf29c964c28ee6183e47dfde421..998264951b88acb0ca4bca1a4c22e3a4f1f07aeb 100644 --- a/Tests/A5/J1_00_Field_Basic_Static_Fields.java +++ b/Tests/A5/J1_00_Field_Basic_Static_Fields.java @@ -3,7 +3,11 @@ public class J1_00_Field_Basic_Static_Fields { public static int f2; public static int f3 = 4; public J1_00_Field_Basic_Static_Fields(){} - public int test(){ + public static int test() { + return J1_00_Field_Basic_Static_Fields.test2(); + } + + public static int test2(){ return J1_00_Field_Basic_Static_Fields.f3; } } \ No newline at end of file diff --git a/Tests/A5/J1_01_basicTest.java b/Tests/A5/J1_01_basicTest.java index dd5b796567130e138dfea50ded9552ea150e5a63..b2b700cf49e38d3f58e24f210adceace38e1a220 100644 --- a/Tests/A5/J1_01_basicTest.java +++ b/Tests/A5/J1_01_basicTest.java @@ -3,12 +3,12 @@ public class J1_01_basicTest { public J1_01_basicTest() {} public static int test() { - return J1_01_basicTest.test2(); + return J1_01_basicTest.test2(4,5); } - public static int test2(){ + public static int test2(int i, int j){ if (true) - return 123; - return 7; + return i; + return j; } } diff --git a/Tests/A5/J1_0X_Field_Basic_Empty.java b/Tests/A5/J1_0X_Field_Basic_Empty.java deleted file mode 100644 index dcc9b9c2ce9c45e88403eed8a7b5c52dd0492ec7..0000000000000000000000000000000000000000 --- a/Tests/A5/J1_0X_Field_Basic_Empty.java +++ /dev/null @@ -1,4 +0,0 @@ -public class J1_0X_Field_Basic_Empty { - public J1_0X_Field_Basic_Empty(){} - public static void test(){} -} \ No newline at end of file diff --git a/TypeNodes.py b/TypeNodes.py index eb4b588350b0281530c62e88a5988c92863b60d9..14b6f8f9047e26a87a0592430e81025961c18762 100644 --- a/TypeNodes.py +++ b/TypeNodes.py @@ -305,13 +305,15 @@ class ClassNode(ClassInterNode): return - self.code = "" + self.code = "" # For read-only section + self.data = "" # For writeable data section # print("This is the super class: {}".format(self.superClass)) # Generate class label self.label = pLabel(name=self.name, type="class") - self.code += ";START OF CLASS MEMORY LAYOUT FOR CLASS: " + self.canonName + "\n" - self.code += self.label + self.data += ";START OF CLASS MEMORY LAYOUT FOR CLASS: " + self.canonName + "\n" + self.data += self.label + # TODO: SIT and subtype testing tables @@ -329,8 +331,8 @@ class ClassNode(ClassInterNode): # Iterating through method offset table sorted by offset for key,value in sorted(self.superClass.methodOffset.items(), key=lambda item: item[1]): self.methodOffset[key] = value - self.code += pLabel(name=self.name + "_" + key[0] + "_" + key[1], type="vtable") - self.code += p(instruction="dd", arg1=64) # just declaring a memory segment with a random number + self.data += pLabel(name=self.name + "_" + key[0] + "_" + key[1], type="vtable") + self.data += p(instruction="dd", arg1=64) # just declaring a memory segment with a random number lastMethodOffset = max(value, lastMethodOffset) @@ -339,8 +341,8 @@ class ClassNode(ClassInterNode): lastMethodOffset += 4 self.methodOffset[(method.name, method.paramTypes)] = lastMethodOffset method.offset = lastMethodOffset - self.code += pLabel(name=self.name + "_" + method.name + "_" + method.paramTypes, type="vtable") - self.code += p(instruction="dd", arg1=64) # just declaring a memory segment with a random number + self.data += pLabel(name=self.name + "_" + method.name + "_" + method.paramTypes, type="vtable") + self.data += p(instruction="dd", arg1=64) # just declaring a memory segment with a random number # 3. Assigning offsets to methods that aren't in the super class, DECLARING memory segment for the methods # Also simultaneosly creating a dictionary of methods for easier lookup @@ -351,10 +353,10 @@ class ClassNode(ClassInterNode): lastMethodOffset += 4 method.methodOffset = lastMethodOffset self.methodOffset[(method.name, method.paramTypes)] = lastMethodOffset - self.code += pLabel(name=self.name + "_" + method.name + "_" + method.paramTypes, type="vtable") - self.code += p(instruction="dd", arg1=64) # just declaring a memory segment with a random number - - # Adding inherited method to the methodDict + self.data += pLabel(name=self.name + "_" + method.name + "_" + method.paramTypes, type="vtable") + self.data += p(instruction="dd", arg1=64) # just declaring a memory segment with a random number + + # Adding inherited method to the methodDict for i in self.inherits: if isinstance(i, MethodNode): key = (i.name, i.paramTypes) @@ -362,11 +364,12 @@ class ClassNode(ClassInterNode): methodDict[(i.name, i.paramTypes)] = i # print(self.methodOffset) - self.code += ";END OF CLASS MEMORY LAYOUT FOR CLASS " + self.name + "\n" + self.data += ";END OF CLASS MEMORY LAYOUT FOR CLASS " + self.name + "\n" # 4. Fill in the memory segment declared in step 1 and 2 with the addresses of the method implementations - self.code += "; Filling in class memory layout for class " + self.name + "\n" + self.code += "; Function for filling in class memory layout for class " + self.name + "\n" + self.code += pLabel(name=self.name + "_"+ "classMemoryInit", type="helper") for key,value in self.methodOffset.items(): vLabel = "V_" + self.name + "_" + key[0] + "_" + key[1]+"" # method at class's vtable @@ -382,7 +385,8 @@ class ClassNode(ClassInterNode): self.code += p(instruction="extern", arg1=mLabel, comment="importing method implementation label") self.code += p(instruction="mov", arg1="eax", arg2=vLabel, comment="Filling in class memory segment for method " + mLabel) self.code += p(instruction="mov", arg1="[eax]", arg2="dword " + mLabel) - self.code += "; End of filling in class memory layout\n" + self.code += p(instruction="ret", arg1="") + self.code += "; End of function for filling in class memory layout\n" # print(self.name) # print(self.fieldOffset) @@ -391,7 +395,28 @@ class ClassNode(ClassInterNode): ########################################################### - + # Generating a function that allocates and initializes all static fields + # Note: 1. saving and restoring ebx, a callee-save register + # 2. static fields are intialized in the order of declaration within the class and has to be intialized + # before the test() method is being called + self.code += "; Function for filling allocating space and initializing static fields for class " + self.name + "\n" + self.code += pLabel(name=self.name + "_" + "staticFieldMemoryInit", type="helper") + self.code += p(instruction="push", arg1="ebx", comment="saving ebx") + for field in self.fields: + if not hasattr(field, "code"): + field.codeGen() + self.code += field.code + self.data += field.data + self.code += p(instruction="pop", arg1="ebx", comment="restoring ebx") + self.code += p(instruction="ret", arg1="") + self.code += "; End of function for filling allocating space and initializing static fields for class " + self.name + "\n" + + # Generating a function that calls both the class memory init and static field init functions + self.code += "; Function that calls staticFieldMemoryInit and classMemoryInit \n" + self.code += pLabel(name=self.name + "_" + "classAndStaticFieldInit", type="helper") + self.code += p(instruction="call", arg1="H_"+self.name+"_"+"classMemoryInit") + \ + p(instruction="call", arg1="H_"+self.name+"_"+ "staticFieldMemoryInit") + \ + p(instruction="ret", arg1="") for c in self.fields + self.methods: c.codeGen()