From 216d6c6b5ea5398362a310c72e615e0192a4bb7a Mon Sep 17 00:00:00 2001 From: Curle Date: Sun, 23 Apr 2023 17:32:02 +0100 Subject: [PATCH] Refactor to generalise assembler output --- CMakeLists.txt | 7 +- include/Data.h | 4 + include/Defs.h | 175 +- src/Delegate.c | 2 +- src/Main.c | 12 +- src/Parser.c | 4 +- src/Statements.c | 2 +- src/Symbols.c | 2 +- src/{Assembler.c => assemble/ASMAssembler.c} | 1873 +++++++++--------- src/assemble/AssemblerDispatcher.c | 39 + src/assemble/JVMAssembler.c | 180 ++ src/assemble/QBEAssembler.c | 180 ++ tests/printf.er | 2 +- tests/test.java | 5 + 14 files changed, 1480 insertions(+), 1007 deletions(-) rename src/{Assembler.c => assemble/ASMAssembler.c} (87%) create mode 100644 src/assemble/AssemblerDispatcher.c create mode 100644 src/assemble/JVMAssembler.c create mode 100644 src/assemble/QBEAssembler.c create mode 100644 tests/test.java diff --git a/CMakeLists.txt b/CMakeLists.txt index 21726e3..75bed9e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,7 +8,9 @@ include_directories(include) add_executable(Erythro include/Data.h include/Defs.h - src/Assembler.c + src/assemble/AssemblerDispatcher.c + src/assemble/ASMAssembler.c + src/assemble/QBEAssembler.c src/Delegate.c src/Dump.c src/Lexer.c @@ -18,4 +20,5 @@ add_executable(Erythro src/Statements.c src/Symbols.c src/Types.c - src/Importer.c) + src/Importer.c + src/assemble/JVMAssembler.c) diff --git a/include/Data.h b/include/Data.h index 442e4d6..3442547 100644 --- a/include/Data.h +++ b/include/Data.h @@ -48,6 +48,10 @@ extern_ bool OptVerboseOutput; extern_ char* OutputFileName; // The sizes of each of the core types, in bytes. extern_ int TypeSizes[5]; +// The name of the Assembler Module that we should use. +extern_ char* OptAssemblerName; +// The Assembler Module to call to when performing generation operations. +extern_ struct AssemblerModule* Assembler; // The names of each token in the language, synchronized to the TokenTypes enum. extern_ char* TokenNames[]; diff --git a/include/Defs.h b/include/Defs.h index 08797bf..3c49dec 100644 --- a/include/Defs.h +++ b/include/Defs.h @@ -513,113 +513,96 @@ void DieBinary(char* Error, int Number); * * * * C O D E G E N E R A T I O N * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ -int AssembleTree(struct ASTNode* Node, int Register, int LoopBeginLabel, int LoopEndLabel, int ParentOp); +int PrimitiveSize (int); -void DeallocateAllRegisters(); +/** + * All of the functions required to be implemented by an Assembler Module. + */ +struct AssemblerVtable { + // Entry Point + int (*AssembleTree)(struct ASTNode*, int, int, int, int); + // Register management + void (*DeallocateAllRegisters)(); + int (*RetrieveRegister)(); + void (*DeallocateRegister)(int); + // Alignment + int (*AsAlignMemory)(int, int, int); + int (*AsCalcOffset)(int); + void (*AsNewStackFrame)(); + // Basic operations + int (*AsLoad)(int); + int (*AsAdd)(int, int); + int (*AsMul)(int, int); + int (*AsSub)(int, int); + int (*AsDiv)(int, int); + int (*AsLdGlobalVar)(struct SymbolTableEntry*, int); + int (*AsLdLocalVar)(struct SymbolTableEntry*, int); + int (*AsStrGlobalVar)(struct SymbolTableEntry*, int); + int (*AsStrLocalVar)(struct SymbolTableEntry*, int); + int (*AsDeref)(int, int); + int (*AsStrDeref)(int, int, int); + int (*AsAddr)(struct SymbolTableEntry*); + int (*AsNewString)(char*); + int (*AsLoadString)(int); -int RetrieveRegister(); + // Comparisons + int (*AsEqual)(int, int); + int (*AsIneq)(int, int); + int (*AsLess)(int, int); + int (*AsGreat)(int, int); + int (*AsLessE)(int, int); + int (*AsGreatE)(int, int); -void DeallocateRegister(int Register); + // Binary operations + int (*AsBitwiseAND)(int, int); + int (*AsBitwiseOR)(int, int); + int (*AsBitwiseXOR)(int, int); + int (*AsNegate)(int); + int (*AsInvert)(int); + int (*AsBooleanNOT)(int); + int (*AsShiftLeft)(int, int); + int (*AsShiftRight)(int, int); -int PrimitiveSize(int Type); + // Comparisons + int (*AsBooleanConvert)(int, int, int); + int (*AsCompareJmp)(int, int, int, int); + int (*AsCompare)(int, int, int); -int AsAlignMemory(int Type, int Offset, int Direction); + // Loops and jumps + int (*AsIf)(struct ASTNode*, int, int); + int (*AsWhile)(struct ASTNode*); + int (*NewLabel)(); + void (*AsJmp)(int); + void (*AsLabel)(int); -int AsLoad(int Value); + // Call and return + int (*AsShl)(int, int); + int (*AsReturn)(struct SymbolTableEntry*, int); + int (*AsCallWrapper)(struct ASTNode*); + void (*AsCopyArgs)(int, int); + int (*AsCall)(struct SymbolTableEntry*, int); + void (*AssemblerPrint)(int); -int AsAdd(int Left, int Right); + // Preamble and epilogue + void (*AsGlobalSymbol)(struct SymbolTableEntry*); + void (*AssemblerPreamble)(); + void (*AsFunctionPreamble)(struct SymbolTableEntry*); + void (*AsFunctionEpilogue)(struct SymbolTableEntry*); +}; -int AsMul(int Left, int Right); +struct AssemblerModule{ + char* name; + const struct AssemblerVtable* vtable; +}; -int AsSub(int Left, int Right); +int RegisterModule(struct AssemblerModule*); -int AsDiv(int Left, int Right); +void RegisterAllModules(); -int AsLdGlobalVar(struct SymbolTableEntry* Entry, int Operation); - -int AsLdLocalVar(struct SymbolTableEntry* Entry, int Operation); - -int AsStrGlobalVar(struct SymbolTableEntry* Entry, int Register); - -int AsStrLocalVar(struct SymbolTableEntry* Entry, int Register); - -int AsCalcOffset(int Type); - -void AsNewStackFrame(); - -int AsDeref(int Reg, int Type); - -int AsStrDeref(int Register1, int Register2, int Type); - -int AsAddr(struct SymbolTableEntry* Entry); - -void AsGlobalSymbol(struct SymbolTableEntry* Entry); - -int AsNewString(char* Value); - -int AsLoadString(int ID); - -int AsEqual(int Left, int Right); - -int AsIneq(int Left, int Right); - -int AsLess(int Left, int Right); - -int AsGreat(int Left, int Right); - -int AsLessE(int Left, int Right); - -int AsGreatE(int Left, int Right); - -int AsBitwiseAND(int Left, int Right); - -int AsBitwiseOR(int Left, int Right); - -int AsBitwiseXOR(int Left, int Right); - -int AsNegate(int Register); - -int AsInvert(int Register); - -int AsBooleanNOT(int Register); - -int AsShiftLeft(int Left, int Right); - -int AsShiftRight(int Left, int Right); - -int AsBooleanConvert(int Register, int Operation, int Label); - -int AsCompareJmp(int Operation, int RegisterLeft, int RegisterRight, int Label); - -int AsCompare(int Operation, int RegisterLeft, int RegisterRight); - -int AsIf(struct ASTNode* Node, int LoopStartLabel, int LoopEndLabel); - -int NewLabel(void); - -void AsJmp(int Label); - -void AsLabel(int Label); - -int AsShl(int Register, int Val); - -int AsReturn(struct SymbolTableEntry* Entry, int Register); - -int AsCallWrapper(struct ASTNode* Node); - -void AsCopyArgs(int Register, int Position); - -int AsCall(struct SymbolTableEntry* Entry, int Args); - -int AsWhile(struct ASTNode* Node); - -void AssemblerPrint(int Register); - -void AssemblerPreamble(); - -void AsFunctionPreamble(struct SymbolTableEntry* Entry); - -void AsFunctionEpilogue(struct SymbolTableEntry* Entry); +// Module List +void RegisterQBE(); +void RegisterWin32ASM(); +void RegisterJVM(); /* * * * * * * * * * * * * * * * * * * * * * * diff --git a/src/Delegate.c b/src/Delegate.c index 868ac3f..260a46b 100644 --- a/src/Delegate.c +++ b/src/Delegate.c @@ -95,7 +95,7 @@ void Compile(struct FileData* InputFile) { Tokenise(); - AssemblerPreamble(); + Assembler->vtable->AssemblerPreamble(); ParseGlobals(); diff --git a/src/Main.c b/src/Main.c index 7f4cefb..4150736 100644 --- a/src/Main.c +++ b/src/Main.c @@ -107,6 +107,7 @@ int main(int argc, char* argv[]) { OptAssembleFiles = true; OptLinkFiles = true; OptVerboseOutput = false; + OptAssemblerName = "Win32 GAS ASM"; // Temporary .o storage and counter int ObjectCount = 0; @@ -126,24 +127,32 @@ int main(int argc, char* argv[]) { switch (argv[i][j]) { case 'o': // output OutputFileName = argv[++i]; - break; + case 'T': // print Tree (debug) OptDumpTree = true; break; + case 'c': // Compile only OptAssembleFiles = true; OptKeepAssembly = false; OptLinkFiles = false; break; + case 'S': // aSsemble only OptAssembleFiles = true; OptKeepAssembly = true; OptLinkFiles = false; break; + case 'v': // Verbose output OptVerboseOutput = true; break; + + case 'm': // Specify Assembler Module + OptAssemblerName = argv[++i]; + break; + default: DisplayUsage(argv[0]); } @@ -159,6 +168,7 @@ int main(int argc, char* argv[]) { Files = malloc(sizeof(struct FileData*) * (argc - i) + 1); memset(Files, 0, sizeof(struct FileData*) * (argc - i) + 1); + RegisterAllModules(); // For the rest of the files specified, we can iterate them right to left. while (i < argc) { diff --git a/src/Parser.c b/src/Parser.c index a86d014..96af774 100644 --- a/src/Parser.c +++ b/src/Parser.c @@ -241,7 +241,7 @@ struct ASTNode* ParsePrimary(void) { case LI_STR: - ID = AsNewString(CurrentIdentifier); + ID = Assembler->vtable->AsNewString(CurrentIdentifier); Node = ConstructASTLeaf(TERM_STRLITERAL, PointerTo(RET_CHAR), NULL, ID); break; @@ -621,7 +621,7 @@ void ParseGlobals() { Tree = ParseFunction(Type); if (Tree && CurrentFile->AllowDefinitions) { printf("\nBeginning assembler creation of new function %s\n", Tree->Symbol->Name); - AssembleTree(Tree, -1, -1, -1, 0); + Assembler->vtable->AssembleTree(Tree, -1, -1, -1, 0); FreeLocals(); } else { printf("\nFunction prototype saved\r\n"); diff --git a/src/Statements.c b/src/Statements.c index 9d39915..53eb343 100644 --- a/src/Statements.c +++ b/src/Statements.c @@ -110,7 +110,7 @@ struct SymbolTableEntry* BeginCompositeDeclaration(int Type) { for (Member = Member->NextSymbol; Member != NULL; Member = Member->NextSymbol) { if (Type == DAT_STRUCT) - Member->SinkOffset = AsAlignMemory(Member->Type, Offset, 1); + Member->SinkOffset = Assembler->vtable->AsAlignMemory(Member->Type, Offset, 1); else Member->SinkOffset = 0; diff --git a/src/Symbols.c b/src/Symbols.c index a080795..c5d3f78 100644 --- a/src/Symbols.c +++ b/src/Symbols.c @@ -257,7 +257,7 @@ struct SymbolTableEntry* AddSymbol(char* Name, int Type, int Structure, int Stor case SC_GLOBAL: AppendSymbol(&Globals, &GlobalsEnd, Node); // We don't want to generate a static block for functions. - if (Structure != ST_FUNC) AsGlobalSymbol(Node); + if (Structure != ST_FUNC) Assembler->vtable->AsGlobalSymbol(Node); break; case SC_STRUCT: AppendSymbol(&Structs, &StructsEnd, Node); diff --git a/src/Assembler.c b/src/assemble/ASMAssembler.c similarity index 87% rename from src/Assembler.c rename to src/assemble/ASMAssembler.c index 0068a95..b60f724 100644 --- a/src/Assembler.c +++ b/src/assemble/ASMAssembler.c @@ -4,9 +4,10 @@ /* ERYTHRO*/ /*************/ -#include -#include +#include "Defs.h" +#include "Data.h" +static int AssembleTree(struct ASTNode* Node, int Register, int LoopBeginLabel, int LoopEndLabel, int ParentOp); /* * Stores how many hardware registers are being used at any one time. @@ -53,6 +54,910 @@ static int StackFrameOffset; // Just a short "hack" to make sure we only dump the tree the first time this function is called static int Started = 0; + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * * * R E G I S T E R M A N A G E M E N T * * * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +// Set all Registers to unused. +static void DeallocateAllRegisters() { + UsedRegisters[0] = UsedRegisters[1] = UsedRegisters[2] = UsedRegisters[3] = 0; +} + +/* + * Search for an unused register, allocate it, and return it. + * If none available, cancel compilation. + */ +static int RetrieveRegister() { + for (size_t i = 0; i < 4; i++) { + if (UsedRegisters[i] == 0) { + UsedRegisters[i] = 1; + return i; + } + } + fprintf(stderr, "Out of registers!\n"); + exit(1); +} + +/* + * Set the given register to unused. + * If the register is not used, it is an invalid state. + * @param Register: The Registers index to deallocate. + */ +static void DeallocateRegister(int Register) { + if (UsedRegisters[Register] != 1) { + fprintf(stderr, "Error trying to free register %d\n", Register); + exit(1); + } + + UsedRegisters[Register] = 0; +} + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * * * * * S T A C K M A N A G E M E N T * * * * * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +/* + * Prepare a new stack frame pointer. + * This resets the highest local. + * + */ +static void AsNewStackFrame() { + LocalVarOffset = 0; +} + +// Assemble an immediate jump +static void AsJmp(int Label) { + printf("\t\tJumping to label %d\n", Label); + fprintf(OutputFile, "\tjmp\tL%d\n", Label); +} + +/* Create a new base label + * @param Label: The number to create the label of + */ +static void AsLabel(int Label) { + printf("\tCreating label %d\n", Label); + fprintf(OutputFile, "\nL%d:\n", Label); +} + +/* + * Given the type of input, how far do we need to go down the stack frame + * to store or retrieve this type? + * + * The stack must be 4-bytes aligned, so we set a hard minimum. + * + * @param Type: The DataTypes we want to store. + * @return the offset to store the type, taking into account the current state of the stack frame. + * + */ +static int AsCalcOffset(int Type) { + LocalVarOffset += PrimitiveSize(Type) > 4 ? PrimitiveSize(Type) : 4; + return -LocalVarOffset; +} + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * * * C O D E G E N E R A T I O N * * * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +/* + * A way to keep track of the largest label number. + * Call this function to increase the number SRG-like. + * + * @return the highest available label number + * + */ +int NewLabel(void) { + static int id = 1; + return id++; +} + +/* + * Align non-char types to a 4 byte alignment. + * Chars need no alignment on x86_64. + * + * @param Type: The DataTypes representation of the data to align + * @param Offset: The offset to align + * @param Direction: The desired direction to move the address for alignment. 1 = up, -1 = down. + * @return the new alignment + * + */ +static int AsAlignMemory(int Type, int Offset, int Direction) { + switch (Type) { + case RET_CHAR: + return Offset; + case RET_INT: + case RET_LONG: + break; + default: + DieDecimal("Unable to align type", Type); + } + + int Alignment = 4; + Offset = (Offset + Direction * (Alignment - 1)) & ~(Alignment - 1); + return (Offset); +} + +// Assemble an If statement +static int AsIf(struct ASTNode* Node, int LoopStartLabel, int LoopEndLabel) { + int FalseLabel, EndLabel; + + FalseLabel = NewLabel(); + if (Node->Right) + EndLabel = NewLabel(); + + // Left is the condition + AssembleTree(Node->Left, FalseLabel, LoopStartLabel, LoopEndLabel, Node->Operation); + DeallocateAllRegisters(); + + // Middle is the true block + AssembleTree(Node->Middle, -1, LoopStartLabel, LoopEndLabel, Node->Operation); + DeallocateAllRegisters(); + + // Right is the optional else + if (Node->Right) + AsJmp(EndLabel); + + AsLabel(FalseLabel); + + if (Node->Right) { + AssembleTree(Node->Right, -1, LoopStartLabel, LoopEndLabel, Node->Operation); + DeallocateAllRegisters(); + AsLabel(EndLabel); + } + + return -1; +} + +// Assemble a comparison +static int AsCompare(int Operation, int RegisterLeft, int RegisterRight) { + printf("Comparing registers %d & %d\n", RegisterLeft, RegisterRight); + + if (Operation < OP_EQUAL || Operation > OP_GREATE) + Die("Bad Operation in AsCompare"); + + fprintf(OutputFile, "\tcmpq\t%s, %s\n", Registers[RegisterRight], Registers[RegisterLeft]); + fprintf(OutputFile, "\t%s\t\t%s\n", Comparisons[Operation - OP_EQUAL], ByteRegisters[RegisterRight]); + fprintf(OutputFile, "\tmovzbq\t%s, %s\n", ByteRegisters[RegisterRight], Registers[RegisterLeft]); + DeallocateRegister(RegisterLeft); + return RegisterRight; +} + +// Assemble an inverse comparison (a one-line jump) +static int AsCompareJmp(int Operation, int RegisterLeft, int RegisterRight, int Label) { + if (Operation < OP_EQUAL || Operation > OP_GREATE) + Die("Bad Operation in AsCompareJmp"); + + printf("\tBranching on comparison of registers %d & %d, with operation %s\n\n", RegisterLeft, RegisterRight, + Comparisons[Operation - OP_EQUAL]); + + fprintf(OutputFile, "\tcmpq\t%s, %s\n", Registers[RegisterRight], Registers[RegisterLeft]); + fprintf(OutputFile, "\t%s\tL%d\n", InvComparisons[Operation - OP_EQUAL], Label); + DeallocateAllRegisters(); + + return -1; +} + + +/* + * Assemble a new global string into the data segment. + * @param Value: The name of the string, as a string + */ +static int AsNewString(char* Value) { + int Label = NewLabel(); + char* CharPtr; + + AsLabel(Label); + + for (CharPtr = Value; *CharPtr; CharPtr++) + fprintf(OutputFile, "\t.byte\t%d\r\n", *CharPtr); + fprintf(OutputFile, "\t.byte\t0\r\n"); + + return Label; +} + +/* + * Load a string into a Register. + * @param ID: the Label number of the string + */ +static int AsLoadString(int ID) { + int Register = RetrieveRegister(); + fprintf(OutputFile, "\tleaq\tL%d(\%%rip), %s\r\n", ID, Registers[Register]); + return Register; +} + +// Assemble a While loop +static int AsWhile(struct ASTNode* Node) { + int BodyLabel, BreakLabel; + + BodyLabel = NewLabel(); + BreakLabel = NewLabel(); + + printf("\tInitiating loop between labels %d and %d\n", BodyLabel, BreakLabel); + + // Mark the start position + AsLabel(BodyLabel); + + // Assemble the condition - this should include a jump to end! + AssembleTree(Node->Left, BreakLabel, BodyLabel, BreakLabel, Node->Operation); + DeallocateAllRegisters(); + + // Assemble the body + AssembleTree(Node->Right, -1, BodyLabel, BreakLabel, Node->Operation); + DeallocateAllRegisters(); + + // Jump back to the body - as awe've already failed the condition check if we get here + AsJmp(BodyLabel); + + // Set up the label to break out of the loop. + AsLabel(BreakLabel); + + + return -1; + +} + +// Load a value into a register. +static int AsLoad(int Value) { + int Register = RetrieveRegister(); + + printf("\tStoring value %d into %s\n", Value, Registers[Register]); + + fprintf(OutputFile, "\tmovq\t$%d, %s\n", Value, Registers[Register]); + + return Register; +} + +// Assemble an addition. +static int AsAdd(int Left, int Right) { + printf("\tAdding Registers %s, %s\n", Registers[Left], Registers[Right]); + fprintf(OutputFile, "\taddq\t%s, %s\n", Registers[Left], Registers[Right]); + + DeallocateRegister(Left); + + return Right; +} + +// Assemble a multiplication. +static int AsMul(int Left, int Right) { + printf("\tMultiplying Registers %s, %s\n", Registers[Left], Registers[Right]); + fprintf(OutputFile, "\timulq\t%s, %s\n", Registers[Left], Registers[Right]); + + DeallocateRegister(Left); + + return Right; +} + +// Assemble a subtraction. +static int AsSub(int Left, int Right) { + printf("\tSubtracting Registers %s, %s\n", Registers[Left], Registers[Right]); + fprintf(OutputFile, "\tsubq\t%s, %s\n", Registers[Right], Registers[Left]); + + DeallocateRegister(Right); + + return Left; +} + +// Assemble a division. +static int AsDiv(int Left, int Right) { + printf("\tDividing Registers %s, %s\n", Registers[Left], Registers[Right]); + fprintf(OutputFile, "\tmovq\t%s, %%rax\n", Registers[Left]); + fprintf(OutputFile, "\tcqo\n"); + fprintf(OutputFile, "\tidivq\t%s\n", Registers[Right]); + fprintf(OutputFile, "\tmovq\t%%rax, %s\n", Registers[Left]); + + DeallocateRegister(Right); + + return Left; +} + +// Assemble an ASL +static int AsShl(int Register, int Val) { + printf("\tShifting %s to the left by %d bits.\n", Registers[Register], Val); + fprintf(OutputFile, "\tsalq\t$%d, %s\n", Val, Registers[Register]); + return Register; +} + +/* + * Load a global variable into a register, with optional pre/post-inc/dec + * @param Entry: The variable to load. + * @param Operation: An optional SyntaxOps element + */ +static int AsLdGlobalVar(struct SymbolTableEntry* Entry, int Operation) { + int Reg = RetrieveRegister(); + printf("\tGlobally storing a %s\n", ScopeNames[Entry->Storage]); + printf("\tStoring %s's contents into %s, globally\n", Entry->Name, Registers[Reg]); + + int TypeSize = PrimitiveSize(Entry->Type); + switch (TypeSize) { + case 1: + switch (Operation) { + case OP_PREINC: + fprintf(OutputFile, "\tincb\t%s(\%%rip)\n", Entry->Name); + break; + case OP_PREDEC: + fprintf(OutputFile, "\tdecb\t%s(\%%rip)\n", Entry->Name); + break; + } + + fprintf(OutputFile, "\tmovzbq\t%s(\%%rip), %s\n", Entry->Name, Registers[Reg]); + + switch (Operation) { + case OP_POSTINC: + fprintf(OutputFile, "\tincb\t%s(\%%rip)\n", Entry->Name); + break; + case OP_POSTDEC: + fprintf(OutputFile, "\tdecb\t%s(\%%rip)\n", Entry->Name); + break; + } + + break; + + case 4: + switch (Operation) { + case OP_PREINC: + fprintf(OutputFile, "\tincl\t%s(\%%rip)\n", Entry->Name); + break; + case OP_PREDEC: + fprintf(OutputFile, "\tdecl\t%s(\%%rip)\n", Entry->Name); + break; + } + + fprintf(OutputFile, "\tmovslq\t%s(\%%rip), %s\n", Entry->Name, Registers[Reg]); + + switch (Operation) { + case OP_POSTINC: + fprintf(OutputFile, "\tincl\t%s(\%%rip)\n", Entry->Name); + break; + case OP_POSTDEC: + fprintf(OutputFile, "\tdecl\t%s(\%%rip)\n", Entry->Name); + break; + } + + break; + case 8: + switch (Operation) { + case OP_PREINC: + fprintf(OutputFile, "\tincq\t%s(\%%rip)\n", Entry->Name); + break; + case OP_PREDEC: + fprintf(OutputFile, "\tdecq\t%s(\%%rip)\n", Entry->Name); + break; + } + + fprintf(OutputFile, "\tmovq\t%s(\%%rip), %s\n", Entry->Name, Registers[Reg]); + + switch (Operation) { + case OP_POSTINC: + fprintf(OutputFile, "\tincq\t%s(\%%rip)\n", Entry->Name); + break; + case OP_POSTDEC: + fprintf(OutputFile, "\tdecq\t%s(\%%rip)\n", Entry->Name); + break; + } + + break; + + default: + DieMessage("Bad type for loading", TypeNames(Entry->Type)); + } + + return Reg; +} + +/* + * Store a value from a register into a global variable. + * @param Entry: The variable to store into. + * @param Regsiter: The Registers index containing the value to store. + */ +static int AsStrGlobalVar(struct SymbolTableEntry* Entry, int Register) { + printf("\tStoring contents of %s into %s, type %d, globally:\n", Registers[Register], Entry->Name, Entry->Type); + + int TypeSize = PrimitiveSize(Entry->Type); + switch (TypeSize) { + case 1: + // movzbq zeroes, then moves a byte into the quad register + fprintf(OutputFile, "\tmovb\t%s, %s(\%%rip)\n", ByteRegisters[Register], Entry->Name); + break; + + case 4: + fprintf(OutputFile, "\tmovl\t%s, %s(\%%rip)\n", DoubleRegisters[Register], Entry->Name); + break; + + case 8: + fprintf(OutputFile, "\tmovq\t%s, %s(%%rip)\n", Registers[Register], Entry->Name); + break; + + default: + DieMessage("Bad type for saving", TypeNames(Entry->Type)); + } + + return Register; +} + +/* + * Load a value from a local variable into a register, with optional post/pre-inc/dec + * @param Entry: The local variable to read + * @param Operation: An optional SyntaxOps entry + */ + +static int AsLdLocalVar(struct SymbolTableEntry* Entry, int Operation) { + int Reg = RetrieveRegister(); + + printf("\tStoring the var at %d's contents into %s, locally\n", Entry->SinkOffset, Registers[Reg]); + + int TypeSize = PrimitiveSize(Entry->Type); + switch (TypeSize) { + case 1: + switch (Operation) { + case OP_PREINC: + fprintf(OutputFile, "\tincb\t%d(\%%rbp)\n", Entry->SinkOffset); + break; + case OP_PREDEC: + fprintf(OutputFile, "\tdecb\t%d(\%%rbp)\n", Entry->SinkOffset); + break; + } + + fprintf(OutputFile, "\tmovzbq\t%d(\%%rbp), %s\n", Entry->SinkOffset, Registers[Reg]); + + switch (Operation) { + case OP_POSTINC: + fprintf(OutputFile, "\tincb\t%d(\%%rbp)\n", Entry->SinkOffset); + break; + case OP_POSTDEC: + fprintf(OutputFile, "\tdecb\t%d(\%%rbp)\n", Entry->SinkOffset); + break; + } + + break; + + case 4: + switch (Operation) { + case OP_PREINC: + fprintf(OutputFile, "\tincl\t%d(\%%rbp)\n", Entry->SinkOffset); + break; + case OP_PREDEC: + fprintf(OutputFile, "\tdecl\t%d(\%%rbp)\n", Entry->SinkOffset); + break; + } + + fprintf(OutputFile, "\tmovslq\t%d(\%%rbp), %s\n", Entry->SinkOffset, Registers[Reg]); + + switch (Operation) { + case OP_POSTINC: + fprintf(OutputFile, "\tincl\t%d(\%%rbp)\n", Entry->SinkOffset); + break; + case OP_POSTDEC: + fprintf(OutputFile, "\tdecl\t%d(\%%rbp)\n", Entry->SinkOffset); + break; + } + + break; + case 8: + switch (Operation) { + case OP_PREINC: + fprintf(OutputFile, "\tincq\t%d(\%%rbp)\n", Entry->SinkOffset); + break; + case OP_PREDEC: + fprintf(OutputFile, "\tdecq\t%d(\%%rbp)\n", Entry->SinkOffset); + break; + } + + fprintf(OutputFile, "\tmovq\t%d(\%%rbp), %s\n", Entry->SinkOffset, Registers[Reg]); + + switch (Operation) { + case OP_POSTINC: + fprintf(OutputFile, "\tincq\t%d(\%%rbp)\n", Entry->SinkOffset); + break; + case OP_POSTDEC: + fprintf(OutputFile, "\tdecq\t%d(\%%rbp)\n", Entry->SinkOffset); + break; + } + + break; + + default: + DieMessage("Bad type for loading", TypeNames(Entry->Type)); + } + + return Reg; +} + +/* + * Store a value from a register into a local variable. + * @param Entry: The local variable to write to. + * @param Register: The Registers index containing the desired value + * + */ +static int AsStrLocalVar(struct SymbolTableEntry* Entry, int Register) { + printf("\tStoring contents of %s into %s, type %d, locally\n", Registers[Register], Entry->Name, Entry->Type); + + int TypeSize = PrimitiveSize(Entry->Type); + switch (TypeSize) { + case 1: + // movzbq zeroes, then moves a byte into the quad register + fprintf(OutputFile, "\tmovb\t%s, %d(\%%rbp)\n", ByteRegisters[Register], Entry->SinkOffset); + break; + + case 4: + fprintf(OutputFile, "\tmovl\t%s, %d(\%%rbp)\n", DoubleRegisters[Register], Entry->SinkOffset); + break; + + case 8: + fprintf(OutputFile, "\tmovq\t%s, %d(%%rbp)\n", Registers[Register], Entry->SinkOffset); + break; + + default: + DieMessage("Bad type for saving", TypeNames(Entry->Type)); + } + + return Register; +} + +// Assemble a pointerisation +static int AsAddr(struct SymbolTableEntry* Entry) { + int Register = RetrieveRegister(); + printf("\tSaving pointer of %s into %s\n", Entry->Name, Registers[Register]); + + fprintf(OutputFile, "\tleaq\t%s(%%rip), %s\n", Entry->Name, Registers[Register]); + return Register; +} + +// Assemble a dereference +static int AsDeref(int Reg, int Type) { + + int DestSize = PrimitiveSize(ValueAt(Type)); + + printf("\tDereferencing %s\n", Registers[Reg]); + switch (DestSize) { + case 1: + fprintf(OutputFile, "\tmovzbq\t(%s), %s\n", Registers[Reg], Registers[Reg]); + break; + case 2: + fprintf(OutputFile, "\tmovslq\t(%s), %s\n", Registers[Reg], DoubleRegisters[Reg]); + break; + case 4: + fprintf(OutputFile, "\tmovl\t(%s), %s\n", Registers[Reg], DoubleRegisters[Reg]); + break; + case 8: + fprintf(OutputFile, "\tmovq\t(%s), %s\n", Registers[Reg], Registers[Reg]); + break; + default: + DieDecimal("Can't generate dereference for type", Type); + } + + return Reg; +} + +// Assemble a store-through-dereference +static int AsStrDeref(int Register1, int Register2, int Type) { + printf("\tStoring contents of %s into %s through a dereference, type %d\n", Registers[Register1], + Registers[Register2], Type); + + switch (Type) { + case RET_CHAR: + fprintf(OutputFile, "\tmovb\t%s, (%s)\n", ByteRegisters[Register1], Registers[Register2]); + break; + case RET_INT: + fprintf(OutputFile, "\tmovl\t%s, (%s)\n", DoubleRegisters[Register1], Registers[Register2]); + break; + case RET_LONG: + fprintf(OutputFile, "\tmovq\t%s, (%s)\n", Registers[Register1], Registers[Register2]); + break; + default: + DieDecimal("Can't generate store-into-deref of type", Type); + } + + return Register1; +} + +// Assemble a global symbol (variable, struct, enum, function, string) +static void AsGlobalSymbol(struct SymbolTableEntry* Entry) { + + if (Entry == NULL) return; + if (Entry->Structure == ST_FUNC) return; + + + int Size = TypeSize(Entry->Type, Entry->CompositeType); + + fprintf(OutputFile, "\t.data\n" + "\t.globl\t%s\n", + Entry->Name); + + fprintf(OutputFile, "%s:\n", Entry->Name); + + switch (Size) { + case 1: + fprintf(OutputFile, "\t.byte\t0\r\n"); + break; + case 4: + fprintf(OutputFile, "\t.long\t0\r\n"); + break; + case 8: + fprintf(OutputFile, "\t.quad\t0\r\n"); + break; + default: + for (int i = 0; i < Size; i++) + fprintf(OutputFile, "\t.byte\t0\n"); + } + +} + + +// Copy a function argument from Register to argument Position +static void AsCopyArgs(int Register, int Position) { + if (Position > 4) { // Args above 4 go on the stack + fprintf(OutputFile, "\tpushq\t%s\n", Registers[Register]); + } else { + fprintf(OutputFile, "\tmovq\t%s, %s\n", Registers[Register], Registers[8 - Position]); + } +} + +// Assemble an actual function call. +// NOTE: this should not be called. Use AsCallWrapper. +static int AsCall(struct SymbolTableEntry* Entry, int Args) { + + int OutRegister = RetrieveRegister(); + + printf("\t\tCalling function %s with %d parameters\n", Entry->Name, Args); + printf("\t\t\tFunction returns into %s\n", Registers[OutRegister]); + + // Allocate shadow space + fprintf(OutputFile, "\taddq\t$-32, %rsp\n"); + fprintf(OutputFile, "\tcall\t%s\n", Entry->Name); + // Deallocate arguments and stack space. + if (Args > 4) + fprintf(OutputFile, "\taddq\t$%d, %%rsp\n", (8 * (Args - 4)) + 32); + else + fprintf(OutputFile, "\taddq\t$32, %rsp\n"); + + fprintf(OutputFile, "\tmovq\t%%rax, %s\n", Registers[OutRegister]); + + return OutRegister; +} + +// Assemble a function call, with all associated parameter bumping and stack movement. +static int AsCallWrapper(struct ASTNode* Node) { + struct ASTNode* CompositeTree = Node->Left; + int Register, Args = 0; + + while (CompositeTree) { + Register = AssembleTree(CompositeTree->Right, -1, -1, -1, CompositeTree->Operation); + AsCopyArgs(Register, CompositeTree->Size); + if (Args == 0) Args = CompositeTree->Size; + DeallocateAllRegisters(); + CompositeTree = CompositeTree->Left; + } + + return AsCall(Node->Symbol, Args); +} + +// Assemble a function return. +static int AsReturn(struct SymbolTableEntry* Entry, int Register) { + + printf("\t\tCreating return for function %s\n", Entry->Name); + + switch (Entry->Type) { + case RET_CHAR: + fprintf(OutputFile, "\tmovzbl\t%s, %%eax\n", ByteRegisters[Register]); + break; + + case RET_INT: + fprintf(OutputFile, "\tmovl\t%s, %%eax\n", DoubleRegisters[Register]); + break; + + case RET_LONG: + fprintf(OutputFile, "\tmovq\t%s, %%rax\n", Registers[Register]); + break; + + default: + DieMessage("Bad function type in generating return", TypeNames(Entry->Type)); + + } + + AsJmp(Entry->EndLabel); +} + + +// Assemble a =? +static int AsEqual(int Left, int Right) { + // Set the lowest bit if left = right + return AsCompare(OP_EQUAL, Left, Right); +} + +// Assemble a != +static int AsIneq(int Left, int Right) { + // Set the lowest bit if left != right + return AsCompare(OP_INEQ, Left, Right); +} + +// Assemble a < +static int AsLess(int Left, int Right) { + // Set the lowest bit if left < right + return AsCompare(OP_LESS, Left, Right); +} + +// Assemble a > +static int AsGreat(int Left, int Right) { + // Set the lowest bit if left > right + return AsCompare(OP_GREAT, Left, Right); +} + +// Assemble a <= +static int AsLessE(int Left, int Right) { + // Set the lowest bit if left <= right + return AsCompare(OP_LESSE, Left, Right); +} + +// Assemble a => +static int AsGreatE(int Left, int Right) { + // Set the lowest bit if left => right + return AsCompare(OP_GREATE, Left, Right); +} + +// Assemble a print statement +static void AssemblerPrint(int Register) { + printf("\t\tPrinting Register %s\n", Registers[Register]); + + fprintf(OutputFile, "\tmovq\t%s, %%rcx\n", Registers[Register]); + //fprintf(OutputFile, "\tleaq\t.LC0(%%rip), %%rcx\n"); + fprintf(OutputFile, "\tcall\tPrintInteger\n"); + + DeallocateRegister(Register); +} + +// Assemble a & +static int AsBitwiseAND(int Left, int Right) { + fprintf(OutputFile, "\tandq\t%s, %s\n", Registers[Left], Registers[Right]); + DeallocateRegister(Left); + return Right; +} + +// Assemble a | +static int AsBitwiseOR(int Left, int Right) { + fprintf(OutputFile, "\torq\t%s, %s\n", Registers[Left], Registers[Right]); + DeallocateRegister(Left); + return Right; +} + +// Assemble a ^ +static int AsBitwiseXOR(int Left, int Right) { + fprintf(OutputFile, "\txorq\t%s, %s\n", Registers[Left], Registers[Right]); + DeallocateRegister(Left); + return Right; +} + +// Assemble a ~ +static int AsNegate(int Register) { + fprintf(OutputFile, "\tnegq\t%s\n", Registers[Register]); + return Register; +} + +// Assemble a ! +static int AsInvert(int Register) { + fprintf(OutputFile, "\tnotq\t%s\n", Registers[Register]); + return Register; +} + +// Assemble a ! +static int AsBooleanNOT(int Register) { + fprintf(OutputFile, "\ttest\t%s, %s\n", Registers[Register], Registers[Register]); + fprintf(OutputFile, "\tsete\t%s\n", ByteRegisters[Register]); + fprintf(OutputFile, "\tmovzbq\t%s, %s\n", ByteRegisters[Register], Registers[Register]); + return Register; +} + +// Assemble a << +static int AsShiftLeft(int Left, int Right) { + fprintf(OutputFile, "\tmovb\t%s, \%%cl\n", ByteRegisters[Right]); + fprintf(OutputFile, "\tshlq\t\%%cl, %s\n", Registers[Left]); + DeallocateRegister(Right); + return Left; +} + +// Assemble a >> +static int AsShiftRight(int Left, int Right) { + fprintf(OutputFile, "\tmovb\t%s, \%%cl\n", ByteRegisters[Right]); + fprintf(OutputFile, "\tshrq\t\%%cl, %s\n", Registers[Left]); + DeallocateRegister(Right); + return Left; +} + +// Assemble a conversion from arbitrary type to boolean. +// Facilitates if(ptr) +static int AsBooleanConvert(int Register, int Operation, int Label) { + fprintf(OutputFile, "\ttest\t%s, %s\n", Registers[Register], Registers[Register]); + + switch (Operation) { + case OP_IF: + case OP_LOOP: + fprintf(OutputFile, "\tje\tL%d\n", Label); + break; + default: + fprintf(OutputFile, "\tsetnz\t%s\n", ByteRegisters[Register]); + fprintf(OutputFile, "\tmovzbq\t%s, %s\n", ByteRegisters[Register], Registers[Register]); + break; + } + + return Register; +} + +// Assemble the start of an assembly file +static void AssemblerPreamble() { + DeallocateAllRegisters(); + fputs( + "\t.text\n", /* + ".LC0:\n" + "\t.string\t\"%d\\n\"\n", */ + OutputFile); +} + +/* + * Assemble a function block for the Entry. + * Handles all stack logic for local variables, + * as well as copying parameters out of registers and + * into the spill space. + * + * @param Entry: The function to generate + * + */ +static void AsFunctionPreamble(struct SymbolTableEntry* Entry) { + char* Name = Entry->Name; + struct SymbolTableEntry* Param, * Local; + int ParamOffset = 0, ParamReg = 9, ParamCount = 0; + + LocalVarOffset = 4; // Prepare parameters + + fprintf(OutputFile, + "\t.text\n" + "\t.globl\t%s\n" + "\t.def\t%s; .scl 2; .type 32; .endef\n" + "%s:\n" + "\tpushq\t%%rbp\n" + "\tmovq\t%%rsp, %%rbp\r\n", + Name, Name, Name); + + //PECOFF requires we call the global initialisers + if (!strcmp(Name, "main")) + fprintf(OutputFile, "\tcall\t__main\n"); + + // Need to share this between two loops. Fun. + int LoopIndex; + + // If we have parameters, move them to the last 4 registers + for (Param = Entry->Start, ParamCount = 1; Param != NULL; Param = Param->NextSymbol, ParamCount++) { + if (ParamCount > 4) { // We only have 4 argument registers + Param->SinkOffset = ParamOffset; + ParamOffset += 8; + } + + Entry->SinkOffset = AsCalcOffset(Param->Type); + AsStrLocalVar(Param, ParamReg--); + } + + // If we have more parameters, move them to the stack + for (Local = Locals; Local != NULL; Local = Local->NextSymbol) { + Local->SinkOffset = AsCalcOffset(Local->Type); + } + + // With all the parameters on the stack, we can allocate the shadow space + StackFrameOffset = ((LocalVarOffset + 31) & ~31); + fprintf(OutputFile, + "\taddq\t$%d, %%rsp\n", -StackFrameOffset); + +} + + +// Assemble the epilogue of a function +static void AsFunctionEpilogue(struct SymbolTableEntry* Entry) { + AsLabel(Entry->EndLabel); + + fprintf(OutputFile, + "\tpopq\t%%rbp\n" + "\taddq\t$%d, %%rsp\n" + "\tret\n", + StackFrameOffset); +} + /* * Walk the AST tree given, and generate the assembly code that represents * it. @@ -64,7 +969,7 @@ static int Started = 0; * @return dependant on the Node. Typically the Register that stores the result of the Node's operation. * */ -int AssembleTree(struct ASTNode* Node, int Register, int LoopBeginLabel, int LoopEndLabel, int ParentOp) { +static int AssembleTree(struct ASTNode* Node, int Register, int LoopBeginLabel, int LoopEndLabel, int ParentOp) { int LeftVal, RightVal; if (!Started && OptDumpTree) DumpTree(Node, 0); @@ -263,906 +1168,70 @@ int AssembleTree(struct ASTNode* Node, int Register, int LoopBeginLabel, int Loo default: DieDecimal("Unknown ASM Operation", Node->Operation); + return 0; } } -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * * * * R E G I S T E R M A N A G E M E N T * * * * - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -// Set all Registers to unused. -void DeallocateAllRegisters() { - UsedRegisters[0] = UsedRegisters[1] = UsedRegisters[2] = UsedRegisters[3] = 0; -} - -/* - * Search for an unused register, allocate it, and return it. - * If none available, cancel compilation. - */ -int RetrieveRegister() { - for (size_t i = 0; i < 4; i++) { - if (UsedRegisters[i] == 0) { - UsedRegisters[i] = 1; - return i; - } - } - fprintf(stderr, "Out of registers!\n"); - exit(1); -} - -/* - * Set the given register to unused. - * If the register is not used, it is an invalid state. - * @param Register: The Registers index to deallocate. - */ -void DeallocateRegister(int Register) { - if (UsedRegisters[Register] != 1) { - fprintf(stderr, "Error trying to free register %d\n", Register); - exit(1); - } - - UsedRegisters[Register] = 0; -} - -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * * * * * * S T A C K M A N A G E M E N T * * * * * * - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -/* - * Prepare a new stack frame pointer. - * This resets the highest local. - * - */ -void AsNewStackFrame() { - LocalVarOffset = 0; -} - -/* - * Given the type of input, how far do we need to go down the stack frame - * to store or retrieve this type? - * - * The stack must be 4-bytes aligned, so we set a hard minimum. - * - * @param Type: The DataTypes we want to store. - * @return the offset to store the type, taking into account the current state of the stack frame. - * - */ -int AsCalcOffset(int Type) { - LocalVarOffset += PrimitiveSize(Type) > 4 ? PrimitiveSize(Type) : 4; - return -LocalVarOffset; -} - -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * - * * * * C O D E G E N E R A T I O N * * * * - * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -/* - * A way to keep track of the largest label number. - * Call this function to increase the number SRG-like. - * - * @return the highest available label number - * - */ -int NewLabel(void) { - static int id = 1; - return id++; -} - -/* - * Align non-char types to a 4 byte alignment. - * Chars need no alignment on x86_64. - * - * @param Type: The DataTypes representation of the data to align - * @param Offset: The offset to align - * @param Direction: The desired direction to move the address for alignment. 1 = up, -1 = down. - * @return the new alignment - * - */ -int AsAlignMemory(int Type, int Offset, int Direction) { - switch (Type) { - case RET_CHAR: - return Offset; - case RET_INT: - case RET_LONG: - break; - default: - DieDecimal("Unable to align type", Type); - } - - int Alignment = 4; - Offset = (Offset + Direction * (Alignment - 1)) & ~(Alignment - 1); - return (Offset); -} - -// Assemble an If statement -int AsIf(struct ASTNode* Node, int LoopStartLabel, int LoopEndLabel) { - int FalseLabel, EndLabel; - - FalseLabel = NewLabel(); - if (Node->Right) - EndLabel = NewLabel(); - - // Left is the condition - AssembleTree(Node->Left, FalseLabel, LoopStartLabel, LoopEndLabel, Node->Operation); - DeallocateAllRegisters(); - - // Middle is the true block - AssembleTree(Node->Middle, -1, LoopStartLabel, LoopEndLabel, Node->Operation); - DeallocateAllRegisters(); - - // Right is the optional else - if (Node->Right) - AsJmp(EndLabel); - - AsLabel(FalseLabel); - - if (Node->Right) { - AssembleTree(Node->Right, -1, LoopStartLabel, LoopEndLabel, Node->Operation); - DeallocateAllRegisters(); - AsLabel(EndLabel); - } - - return -1; -} - -// Assemble a comparison -int AsCompare(int Operation, int RegisterLeft, int RegisterRight) { - printf("Comparing registers %d & %d\n", RegisterLeft, RegisterRight); - - if (Operation < OP_EQUAL || Operation > OP_GREATE) - Die("Bad Operation in AsCompare"); - - fprintf(OutputFile, "\tcmpq\t%s, %s\n", Registers[RegisterRight], Registers[RegisterLeft]); - fprintf(OutputFile, "\t%s\t\t%s\n", Comparisons[Operation - OP_EQUAL], ByteRegisters[RegisterRight]); - fprintf(OutputFile, "\tmovzbq\t%s, %s\n", ByteRegisters[RegisterRight], Registers[RegisterLeft]); - DeallocateRegister(RegisterLeft); - return RegisterRight; -} - -// Assemble an inverse comparison (a one-line jump) -int AsCompareJmp(int Operation, int RegisterLeft, int RegisterRight, int Label) { - if (Operation < OP_EQUAL || Operation > OP_GREATE) - Die("Bad Operation in AsCompareJmp"); - - printf("\tBranching on comparison of registers %d & %d, with operation %s\n\n", RegisterLeft, RegisterRight, - Comparisons[Operation - OP_EQUAL]); - - fprintf(OutputFile, "\tcmpq\t%s, %s\n", Registers[RegisterRight], Registers[RegisterLeft]); - fprintf(OutputFile, "\t%s\tL%d\n", InvComparisons[Operation - OP_EQUAL], Label); - DeallocateAllRegisters(); - - return -1; -} - -// Assemble an immediate jump -void AsJmp(int Label) { - printf("\t\tJumping to label %d\n", Label); - fprintf(OutputFile, "\tjmp\tL%d\n", Label); -} - -/* Create a new base label - * @param Label: The number to create the label of - */ -void AsLabel(int Label) { - printf("\tCreating label %d\n", Label); - fprintf(OutputFile, "\nL%d:\n", Label); -} - -/* - * Assemble a new global string into the data segment. - * @param Value: The name of the string, as a string - */ -int AsNewString(char* Value) { - int Label = NewLabel(); - char* CharPtr; - - AsLabel(Label); - - for (CharPtr = Value; *CharPtr; CharPtr++) - fprintf(OutputFile, "\t.byte\t%d\r\n", *CharPtr); - fprintf(OutputFile, "\t.byte\t0\r\n"); - - return Label; -} - -/* - * Load a string into a Register. - * @param ID: the Label number of the string - */ -int AsLoadString(int ID) { - int Register = RetrieveRegister(); - fprintf(OutputFile, "\tleaq\tL%d(\%%rip), %s\r\n", ID, Registers[Register]); - return Register; -} - -// Assemble a While loop -int AsWhile(struct ASTNode* Node) { - int BodyLabel, BreakLabel; - - BodyLabel = NewLabel(); - BreakLabel = NewLabel(); - - printf("\tInitiating loop between labels %d and %d\n", BodyLabel, BreakLabel); - - // Mark the start position - AsLabel(BodyLabel); - - // Assemble the condition - this should include a jump to end! - AssembleTree(Node->Left, BreakLabel, BodyLabel, BreakLabel, Node->Operation); - DeallocateAllRegisters(); - - // Assemble the body - AssembleTree(Node->Right, -1, BodyLabel, BreakLabel, Node->Operation); - DeallocateAllRegisters(); - - // Jump back to the body - as awe've already failed the condition check if we get here - AsJmp(BodyLabel); - - // Set up the label to break out of the loop. - AsLabel(BreakLabel); - - - return -1; - -} - -// Load a value into a register. -int AsLoad(int Value) { - int Register = RetrieveRegister(); - - printf("\tStoring value %d into %s\n", Value, Registers[Register]); - - fprintf(OutputFile, "\tmovq\t$%d, %s\n", Value, Registers[Register]); - - return Register; -} - -// Assemble an addition. -int AsAdd(int Left, int Right) { - printf("\tAdding Registers %s, %s\n", Registers[Left], Registers[Right]); - fprintf(OutputFile, "\taddq\t%s, %s\n", Registers[Left], Registers[Right]); - - DeallocateRegister(Left); - - return Right; -} - -// Assemble a multiplication. -int AsMul(int Left, int Right) { - printf("\tMultiplying Registers %s, %s\n", Registers[Left], Registers[Right]); - fprintf(OutputFile, "\timulq\t%s, %s\n", Registers[Left], Registers[Right]); - - DeallocateRegister(Left); - - return Right; -} - -// Assemble a subtraction. -int AsSub(int Left, int Right) { - printf("\tSubtracting Registers %s, %s\n", Registers[Left], Registers[Right]); - fprintf(OutputFile, "\tsubq\t%s, %s\n", Registers[Right], Registers[Left]); - - DeallocateRegister(Right); - - return Left; -} - -// Assemble a division. -int AsDiv(int Left, int Right) { - printf("\tDividing Registers %s, %s\n", Registers[Left], Registers[Right]); - fprintf(OutputFile, "\tmovq\t%s, %%rax\n", Registers[Left]); - fprintf(OutputFile, "\tcqo\n"); - fprintf(OutputFile, "\tidivq\t%s\n", Registers[Right]); - fprintf(OutputFile, "\tmovq\t%%rax, %s\n", Registers[Left]); - - DeallocateRegister(Right); - - return Left; -} - -// Assemble an ASL -int AsShl(int Register, int Val) { - printf("\tShifting %s to the left by %d bits.\n", Registers[Register], Val); - fprintf(OutputFile, "\tsalq\t$%d, %s\n", Val, Registers[Register]); - return Register; -} - -/* - * Load a global variable into a register, with optional pre/post-inc/dec - * @param Entry: The variable to load. - * @param Operation: An optional SyntaxOps element - */ -int AsLdGlobalVar(struct SymbolTableEntry* Entry, int Operation) { - int Reg = RetrieveRegister(); - printf("\tGlobally storing a %s\n", ScopeNames[Entry->Storage]); - printf("\tStoring %s's contents into %s, globally\n", Entry->Name, Registers[Reg]); - - int TypeSize = PrimitiveSize(Entry->Type); - switch (TypeSize) { - case 1: - switch (Operation) { - case OP_PREINC: - fprintf(OutputFile, "\tincb\t%s(\%%rip)\n", Entry->Name); - break; - case OP_PREDEC: - fprintf(OutputFile, "\tdecb\t%s(\%%rip)\n", Entry->Name); - break; - } - - fprintf(OutputFile, "\tmovzbq\t%s(\%%rip), %s\n", Entry->Name, Registers[Reg]); - - switch (Operation) { - case OP_POSTINC: - fprintf(OutputFile, "\tincb\t%s(\%%rip)\n", Entry->Name); - break; - case OP_POSTDEC: - fprintf(OutputFile, "\tdecb\t%s(\%%rip)\n", Entry->Name); - break; - } - - break; - - case 4: - switch (Operation) { - case OP_PREINC: - fprintf(OutputFile, "\tincl\t%s(\%%rip)\n", Entry->Name); - break; - case OP_PREDEC: - fprintf(OutputFile, "\tdecl\t%s(\%%rip)\n", Entry->Name); - break; - } - - fprintf(OutputFile, "\tmovslq\t%s(\%%rip), %s\n", Entry->Name, Registers[Reg]); - - switch (Operation) { - case OP_POSTINC: - fprintf(OutputFile, "\tincl\t%s(\%%rip)\n", Entry->Name); - break; - case OP_POSTDEC: - fprintf(OutputFile, "\tdecl\t%s(\%%rip)\n", Entry->Name); - break; - } - - break; - case 8: - switch (Operation) { - case OP_PREINC: - fprintf(OutputFile, "\tincq\t%s(\%%rip)\n", Entry->Name); - break; - case OP_PREDEC: - fprintf(OutputFile, "\tdecq\t%s(\%%rip)\n", Entry->Name); - break; - } - - fprintf(OutputFile, "\tmovq\t%s(\%%rip), %s\n", Entry->Name, Registers[Reg]); - - switch (Operation) { - case OP_POSTINC: - fprintf(OutputFile, "\tincq\t%s(\%%rip)\n", Entry->Name); - break; - case OP_POSTDEC: - fprintf(OutputFile, "\tdecq\t%s(\%%rip)\n", Entry->Name); - break; - } - - break; - - default: - DieMessage("Bad type for loading", TypeNames(Entry->Type)); - } - - return Reg; -} - -/* - * Store a value from a register into a global variable. - * @param Entry: The variable to store into. - * @param Regsiter: The Registers index containing the value to store. - */ -int AsStrGlobalVar(struct SymbolTableEntry* Entry, int Register) { - printf("\tStoring contents of %s into %s, type %d, globally:\n", Registers[Register], Entry->Name, Entry->Type); - - int TypeSize = PrimitiveSize(Entry->Type); - switch (TypeSize) { - case 1: - // movzbq zeroes, then moves a byte into the quad register - fprintf(OutputFile, "\tmovb\t%s, %s(\%%rip)\n", ByteRegisters[Register], Entry->Name); - break; - - case 4: - fprintf(OutputFile, "\tmovl\t%s, %s(\%%rip)\n", DoubleRegisters[Register], Entry->Name); - break; - - case 8: - fprintf(OutputFile, "\tmovq\t%s, %s(%%rip)\n", Registers[Register], Entry->Name); - break; - - default: - DieMessage("Bad type for saving", TypeNames(Entry->Type)); - } - - return Register; -} - -/* - * Load a value from a local variable into a register, with optional post/pre-inc/dec - * @param Entry: The local variable to read - * @param Operation: An optional SyntaxOps entry - */ - -int AsLdLocalVar(struct SymbolTableEntry* Entry, int Operation) { - int Reg = RetrieveRegister(); - - printf("\tStoring the var at %d's contents into %s, locally\n", Entry->SinkOffset, Registers[Reg]); - - int TypeSize = PrimitiveSize(Entry->Type); - switch (TypeSize) { - case 1: - switch (Operation) { - case OP_PREINC: - fprintf(OutputFile, "\tincb\t%d(\%%rbp)\n", Entry->SinkOffset); - break; - case OP_PREDEC: - fprintf(OutputFile, "\tdecb\t%d(\%%rbp)\n", Entry->SinkOffset); - break; - } - - fprintf(OutputFile, "\tmovzbq\t%d(\%%rbp), %s\n", Entry->SinkOffset, Registers[Reg]); - - switch (Operation) { - case OP_POSTINC: - fprintf(OutputFile, "\tincb\t%d(\%%rbp)\n", Entry->SinkOffset); - break; - case OP_POSTDEC: - fprintf(OutputFile, "\tdecb\t%d(\%%rbp)\n", Entry->SinkOffset); - break; - } - - break; - - case 4: - switch (Operation) { - case OP_PREINC: - fprintf(OutputFile, "\tincl\t%d(\%%rbp)\n", Entry->SinkOffset); - break; - case OP_PREDEC: - fprintf(OutputFile, "\tdecl\t%d(\%%rbp)\n", Entry->SinkOffset); - break; - } - - fprintf(OutputFile, "\tmovslq\t%d(\%%rbp), %s\n", Entry->SinkOffset, Registers[Reg]); - - switch (Operation) { - case OP_POSTINC: - fprintf(OutputFile, "\tincl\t%d(\%%rbp)\n", Entry->SinkOffset); - break; - case OP_POSTDEC: - fprintf(OutputFile, "\tdecl\t%d(\%%rbp)\n", Entry->SinkOffset); - break; - } - - break; - case 8: - switch (Operation) { - case OP_PREINC: - fprintf(OutputFile, "\tincq\t%d(\%%rbp)\n", Entry->SinkOffset); - break; - case OP_PREDEC: - fprintf(OutputFile, "\tdecq\t%d(\%%rbp)\n", Entry->SinkOffset); - break; - } - - fprintf(OutputFile, "\tmovq\t%d(\%%rbp), %s\n", Entry->SinkOffset, Registers[Reg]); - - switch (Operation) { - case OP_POSTINC: - fprintf(OutputFile, "\tincq\t%d(\%%rbp)\n", Entry->SinkOffset); - break; - case OP_POSTDEC: - fprintf(OutputFile, "\tdecq\t%d(\%%rbp)\n", Entry->SinkOffset); - break; - } - - break; - - default: - DieMessage("Bad type for loading", TypeNames(Entry->Type)); - } - - return Reg; -} - -/* - * Store a value from a register into a local variable. - * @param Entry: The local variable to write to. - * @param Register: The Registers index containing the desired value - * - */ -int AsStrLocalVar(struct SymbolTableEntry* Entry, int Register) { - printf("\tStoring contents of %s into %s, type %d, locally\n", Registers[Register], Entry->Name, Entry->Type); - - int TypeSize = PrimitiveSize(Entry->Type); - switch (TypeSize) { - case 1: - // movzbq zeroes, then moves a byte into the quad register - fprintf(OutputFile, "\tmovb\t%s, %d(\%%rbp)\n", ByteRegisters[Register], Entry->SinkOffset); - break; - - case 4: - fprintf(OutputFile, "\tmovl\t%s, %d(\%%rbp)\n", DoubleRegisters[Register], Entry->SinkOffset); - break; - - case 8: - fprintf(OutputFile, "\tmovq\t%s, %d(%%rbp)\n", Registers[Register], Entry->SinkOffset); - break; - - default: - DieMessage("Bad type for saving", TypeNames(Entry->Type)); - } - - return Register; -} - -// Assemble a pointerisation -int AsAddr(struct SymbolTableEntry* Entry) { - int Register = RetrieveRegister(); - printf("\tSaving pointer of %s into %s\n", Entry->Name, Registers[Register]); - - fprintf(OutputFile, "\tleaq\t%s(%%rip), %s\n", Entry->Name, Registers[Register]); - return Register; -} - -// Assemble a dereference -int AsDeref(int Reg, int Type) { - - int DestSize = PrimitiveSize(ValueAt(Type)); - - printf("\tDereferencing %s\n", Registers[Reg]); - switch (DestSize) { - case 1: - fprintf(OutputFile, "\tmovzbq\t(%s), %s\n", Registers[Reg], Registers[Reg]); - break; - case 2: - fprintf(OutputFile, "\tmovslq\t(%s), %s\n", Registers[Reg], DoubleRegisters[Reg]); - break; - case 4: - fprintf(OutputFile, "\tmovl\t(%s), %s\n", Registers[Reg], DoubleRegisters[Reg]); - break; - case 8: - fprintf(OutputFile, "\tmovq\t(%s), %s\n", Registers[Reg], Registers[Reg]); - break; - default: - DieDecimal("Can't generate dereference for type", Type); - } - - return Reg; -} - -// Assemble a store-through-dereference -int AsStrDeref(int Register1, int Register2, int Type) { - printf("\tStoring contents of %s into %s through a dereference, type %d\n", Registers[Register1], - Registers[Register2], Type); - - switch (Type) { - case RET_CHAR: - fprintf(OutputFile, "\tmovb\t%s, (%s)\n", ByteRegisters[Register1], Registers[Register2]); - break; - case RET_INT: - fprintf(OutputFile, "\tmovl\t%s, (%s)\n", DoubleRegisters[Register1], Registers[Register2]); - break; - case RET_LONG: - fprintf(OutputFile, "\tmovq\t%s, (%s)\n", Registers[Register1], Registers[Register2]); - break; - default: - DieDecimal("Can't generate store-into-deref of type", Type); - } - - return Register1; -} - -// Assemble a global symbol (variable, struct, enum, function, string) -void AsGlobalSymbol(struct SymbolTableEntry* Entry) { - - if (Entry == NULL) return; - if (Entry->Structure == ST_FUNC) return; - - - int Size = TypeSize(Entry->Type, Entry->CompositeType); - - fprintf(OutputFile, "\t.data\n" - "\t.globl\t%s\n", - Entry->Name); - - fprintf(OutputFile, "%s:\n", Entry->Name); - - switch (Size) { - case 1: - fprintf(OutputFile, "\t.byte\t0\r\n"); - break; - case 4: - fprintf(OutputFile, "\t.long\t0\r\n"); - break; - case 8: - fprintf(OutputFile, "\t.quad\t0\r\n"); - break; - default: - for (int i = 0; i < Size; i++) - fprintf(OutputFile, "\t.byte\t0\n"); - } - -} - -// Assemble a function call, with all associated parameter bumping and stack movement. -int AsCallWrapper(struct ASTNode* Node) { - struct ASTNode* CompositeTree = Node->Left; - int Register, Args = 0; - - while (CompositeTree) { - Register = AssembleTree(CompositeTree->Right, -1, -1, -1, CompositeTree->Operation); - AsCopyArgs(Register, CompositeTree->Size); - if (Args == 0) Args = CompositeTree->Size; - DeallocateAllRegisters(); - CompositeTree = CompositeTree->Left; - } - - return AsCall(Node->Symbol, Args); -} - -// Copy a function argument from Register to argument Position -void AsCopyArgs(int Register, int Position) { - if (Position > 4) { // Args above 4 go on the stack - fprintf(OutputFile, "\tpushq\t%s\n", Registers[Register]); - } else { - fprintf(OutputFile, "\tmovq\t%s, %s\n", Registers[Register], Registers[8 - Position]); - } -} - -// Assemble an actual function call. -// NOTE: this should not be called. Use AsCallWrapper. -int AsCall(struct SymbolTableEntry* Entry, int Args) { - - int OutRegister = RetrieveRegister(); - - printf("\t\tCalling function %s with %d parameters\n", Entry->Name, Args); - printf("\t\t\tFunction returns into %s\n", Registers[OutRegister]); - - // Allocate shadow space - fprintf(OutputFile, "\taddq\t$-32, %rsp\n"); - fprintf(OutputFile, "\tcall\t%s\n", Entry->Name); - // Deallocate arguments and stack space. - if (Args > 4) - fprintf(OutputFile, "\taddq\t$%d, %%rsp\n", (8 * (Args - 4)) + 32); - else - fprintf(OutputFile, "\taddq\t$32, %rsp\n"); - - fprintf(OutputFile, "\tmovq\t%%rax, %s\n", Registers[OutRegister]); - - return OutRegister; -} - -// Assemble a function return. -int AsReturn(struct SymbolTableEntry* Entry, int Register) { - - printf("\t\tCreating return for function %s\n", Entry->Name); - - switch (Entry->Type) { - case RET_CHAR: - fprintf(OutputFile, "\tmovzbl\t%s, %%eax\n", ByteRegisters[Register]); - break; - - case RET_INT: - fprintf(OutputFile, "\tmovl\t%s, %%eax\n", DoubleRegisters[Register]); - break; - - case RET_LONG: - fprintf(OutputFile, "\tmovq\t%s, %%rax\n", Registers[Register]); - break; - - default: - DieMessage("Bad function type in generating return", TypeNames(Entry->Type)); - - } - - AsJmp(Entry->EndLabel); -} - - -// Assemble a =? -int AsEqual(int Left, int Right) { - // Set the lowest bit if left = right - return AsCompare(OP_EQUAL, Left, Right); -} - -// Assemble a != -int AsIneq(int Left, int Right) { - // Set the lowest bit if left != right - return AsCompare(OP_INEQ, Left, Right); -} - -// Assemble a < -int AsLess(int Left, int Right) { - // Set the lowest bit if left < right - return AsCompare(OP_LESS, Left, Right); -} - -// Assemble a > -int AsGreat(int Left, int Right) { - // Set the lowest bit if left > right - return AsCompare(OP_GREAT, Left, Right); -} - -// Assemble a <= -int AsLessE(int Left, int Right) { - // Set the lowest bit if left <= right - return AsCompare(OP_LESSE, Left, Right); -} - -// Assemble a => -int AsGreatE(int Left, int Right) { - // Set the lowest bit if left => right - return AsCompare(OP_GREATE, Left, Right); -} - -// Assemble a print statement -void AssemblerPrint(int Register) { - printf("\t\tPrinting Register %s\n", Registers[Register]); - - fprintf(OutputFile, "\tmovq\t%s, %%rcx\n", Registers[Register]); - //fprintf(OutputFile, "\tleaq\t.LC0(%%rip), %%rcx\n"); - fprintf(OutputFile, "\tcall\tPrintInteger\n"); - - DeallocateRegister(Register); -} - -// Assemble a & -int AsBitwiseAND(int Left, int Right) { - fprintf(OutputFile, "\tandq\t%s, %s\n", Registers[Left], Registers[Right]); - DeallocateRegister(Left); - return Right; -} - -// Assemble a | -int AsBitwiseOR(int Left, int Right) { - fprintf(OutputFile, "\torq\t%s, %s\n", Registers[Left], Registers[Right]); - DeallocateRegister(Left); - return Right; -} - -// Assemble a ^ -int AsBitwiseXOR(int Left, int Right) { - fprintf(OutputFile, "\txorq\t%s, %s\n", Registers[Left], Registers[Right]); - DeallocateRegister(Left); - return Right; -} - -// Assemble a ~ -int AsNegate(int Register) { - fprintf(OutputFile, "\tnegq\t%s\n", Registers[Register]); - return Register; -} - -// Assemble a ! -int AsInvert(int Register) { - fprintf(OutputFile, "\tnotq\t%s\n", Registers[Register]); - return Register; -} - -// Assemble a ! -int AsBooleanNOT(int Register) { - fprintf(OutputFile, "\ttest\t%s, %s\n", Registers[Register], Registers[Register]); - fprintf(OutputFile, "\tsete\t%s\n", ByteRegisters[Register]); - fprintf(OutputFile, "\tmovzbq\t%s, %s\n", ByteRegisters[Register], Registers[Register]); - return Register; -} - -// Assemble a << -int AsShiftLeft(int Left, int Right) { - fprintf(OutputFile, "\tmovb\t%s, \%%cl\n", ByteRegisters[Right]); - fprintf(OutputFile, "\tshlq\t\%%cl, %s\n", Registers[Left]); - DeallocateRegister(Right); - return Left; -} - -// Assemble a >> -int AsShiftRight(int Left, int Right) { - fprintf(OutputFile, "\tmovb\t%s, \%%cl\n", ByteRegisters[Right]); - fprintf(OutputFile, "\tshrq\t\%%cl, %s\n", Registers[Left]); - DeallocateRegister(Right); - return Left; -} - -// Assemble a conversion from arbitrary type to boolean. -// Facilitates if(ptr) -int AsBooleanConvert(int Register, int Operation, int Label) { - fprintf(OutputFile, "\ttest\t%s, %s\n", Registers[Register], Registers[Register]); - - switch (Operation) { - case OP_IF: - case OP_LOOP: - fprintf(OutputFile, "\tje\tL%d\n", Label); - break; - default: - fprintf(OutputFile, "\tsetnz\t%s\n", ByteRegisters[Register]); - fprintf(OutputFile, "\tmovzbq\t%s, %s\n", ByteRegisters[Register], Registers[Register]); - break; - } - - return Register; -} - -// Assemble the start of an assembly file -void AssemblerPreamble() { - DeallocateAllRegisters(); - fputs( - "\t.text\n", /* - ".LC0:\n" - "\t.string\t\"%d\\n\"\n", */ - OutputFile); -} - -/* - * Assemble a function block for the Entry. - * Handles all stack logic for local variables, - * as well as copying parameters out of registers and - * into the spill space. - * - * @param Entry: The function to generate - * - */ -void AsFunctionPreamble(struct SymbolTableEntry* Entry) { - char* Name = Entry->Name; - struct SymbolTableEntry* Param, * Local; - int ParamOffset = 0, ParamReg = 9, ParamCount = 0; - - LocalVarOffset = 4; // Prepare parameters - - fprintf(OutputFile, - "\t.text\n" - "\t.globl\t%s\n" - "\t.def\t%s; .scl 2; .type 32; .endef\n" - "%s:\n" - "\tpushq\t%%rbp\n" - "\tmovq\t%%rsp, %%rbp\r\n", - Name, Name, Name); - - //PECOFF requires we call the global initialisers - if (!strcmp(Name, "main")) - fprintf(OutputFile, "\tcall\t__main\n"); - - // Need to share this between two loops. Fun. - int LoopIndex; - - // If we have parameters, move them to the last 4 registers - for (Param = Entry->Start, ParamCount = 1; Param != NULL; Param = Param->NextSymbol, ParamCount++) { - if (ParamCount > 4) { // We only have 4 argument registers - Param->SinkOffset = ParamOffset; - ParamOffset += 8; - } - - Entry->SinkOffset = AsCalcOffset(Param->Type); - AsStrLocalVar(Param, ParamReg--); - } - - // If we have more parameters, move them to the stack - for (Local = Locals; Local != NULL; Local = Local->NextSymbol) { - Local->SinkOffset = AsCalcOffset(Local->Type); - } - - // With all the parameters on the stack, we can allocate the shadow space - StackFrameOffset = ((LocalVarOffset + 31) & ~31); - fprintf(OutputFile, - "\taddq\t$%d, %%rsp\n", -StackFrameOffset); - -} - - -// Assemble the epilogue of a function -void AsFunctionEpilogue(struct SymbolTableEntry* Entry) { - AsLabel(Entry->EndLabel); - - fprintf(OutputFile, - "\tpopq\t%%rbp\n" - "\taddq\t$%d, %%rsp\n" - "\tret\n", - StackFrameOffset); +static const struct AssemblerVtable Win32ASMVtable = { + .AssembleTree = AssembleTree, + .AsAdd = AsAdd, + .AsAddr = AsAddr, + .AsAlignMemory = AsAlignMemory, + .AsBitwiseAND = AsBitwiseAND, + .AsBitwiseOR = AsBitwiseOR, + .AsBitwiseXOR = AsBitwiseXOR, + .AsBooleanConvert = AsBooleanConvert, + .AsBooleanNOT = AsBooleanNOT, + .AsCalcOffset = AsCalcOffset, + .AsCall = AsCall, + .AsCallWrapper = AsCallWrapper, + .AsCompare = AsCompare, + .AsCompareJmp = AsCompareJmp, + .AsCopyArgs = AsCopyArgs, + .AsDeref = AsDeref, + .AsDiv = AsDiv, + .AsEqual = AsEqual, + .AsFunctionEpilogue = AsFunctionEpilogue, + .AsFunctionPreamble = AsFunctionPreamble, + .AsGreat = AsGreat, + .AsGreatE = AsGreatE, + .AsGlobalSymbol = AsGlobalSymbol, + .AsIf = AsIf, + .AsIneq = AsIneq, + .AsInvert = AsInvert, + .AsJmp = AsJmp, + .AsLabel = AsLabel, + .AsLdGlobalVar = AsLdGlobalVar, + .AsLdLocalVar = AsLdLocalVar, + .AsLess = AsLess, + .AsLessE = AsLessE, + .AsLoad = AsLoad, + .AsLoadString = AsLoadString, + .AsMul = AsMul, + .AsNegate = AsNegate, + .AsNewStackFrame = AsNewStackFrame, + .AsNewString = AsNewString, + .AsReturn = AsReturn, + .AsShiftLeft = AsShiftLeft, + .AsShiftRight = AsShiftRight, + .AsShl = AsShl, + .AsStrDeref = AsStrDeref, + .AsStrGlobalVar = AsStrGlobalVar, + .AsStrLocalVar = AsStrLocalVar, + .AsSub = AsSub, + .AsWhile = AsWhile, + .DeallocateAllRegisters = DeallocateAllRegisters, + .RetrieveRegister = RetrieveRegister, + .DeallocateRegister = DeallocateRegister, + .NewLabel = NewLabel, + .AssemblerPreamble = AssemblerPreamble +}; + +static struct AssemblerModule Win32ASMModule = { + .name = "Win32 GAS ASM", + .vtable = &Win32ASMVtable +}; + +void RegisterWin32ASM() { + RegisterModule(&Win32ASMModule); } \ No newline at end of file diff --git a/src/assemble/AssemblerDispatcher.c b/src/assemble/AssemblerDispatcher.c new file mode 100644 index 0000000..c2e6ee1 --- /dev/null +++ b/src/assemble/AssemblerDispatcher.c @@ -0,0 +1,39 @@ +/*************/ +/*GEMWIRE */ +/* ERYTHRO*/ +/*************/ + +#include +#include + +#define MODULES_SIZE 3 +struct AssemblerModule* modules[MODULES_SIZE]; +static int moduleID = 0; + +void RegisterAllModules() { + RegisterWin32ASM(); + RegisterQBE(); + RegisterJVM(); + + for (size_t i = 0; i < moduleID; i++) { + if (strcmp(modules[i]->name, OptAssemblerName) == 0) { + Assembler = modules[i]; + break; + } + } + + if (Assembler == NULL) { + DieMessage("Unable to find an Assembler for ", OptAssemblerName); + } else { + printf("Using %s assembler.\r\n", OptAssemblerName); + } +} + +int RegisterModule(struct AssemblerModule* mod) { + + printf("Registering Assembler Module for %s output.\r\n", mod->name); + modules[moduleID] = mod; + moduleID++; + + return 0; +} \ No newline at end of file diff --git a/src/assemble/JVMAssembler.c b/src/assemble/JVMAssembler.c new file mode 100644 index 0000000..36081bf --- /dev/null +++ b/src/assemble/JVMAssembler.c @@ -0,0 +1,180 @@ + +/*************/ +/*GEMWIRE */ +/* ERYTHRO*/ +/*************/ + +#include "Defs.h" +#include "Data.h" + +#define AGGREGATE(x) ":##x" +#define GLOBAL(x) "$##x" +#define TEMPORARY(x) "%##x" +#define LABEL(x) "@##x" + + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * * * R O O T O F A S S E M B L E R * * * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +// Just a short "hack" to make sure we only dump the tree the first time this function is called +static int Started = 0; + +/* + * Walk the AST tree given, and generate the assembly code that represents + * it. + * + * @param Node: The current Node to compile. If needed, its children will be parsed recursively. + * @param Register: The index of Registers to store the result of the current compilation. + * @param ParentOp: The Operation of the parent of the current Node. + * + * @return dependant on the Node. Typically the Register that stores the result of the Node's operation. + * + */ +static int AssembleTree(struct ASTNode* Node, int Register, int LoopBeginLabel, int LoopEndLabel, int ParentOp) { + int LeftVal, RightVal; + if (!Started && OptDumpTree) + DumpTree(Node, 0); + Started = 1; + + printf("Current operation: %d\r\n", Node->Operation); + switch (Node->Operation) { + case OP_IF: + + case OP_LOOP: + + case OP_COMP: + + case OP_CALL: + + case OP_FUNC: + break; + } + + + if (Node->Left) + LeftVal = AssembleTree(Node->Left, -1, LoopBeginLabel, LoopEndLabel, Node->Operation); + + if (Node->Right) + RightVal = AssembleTree(Node->Right, LeftVal, LoopBeginLabel, LoopEndLabel, Node->Operation); + + switch (Node->Operation) { + case OP_ADD: + + case OP_SUBTRACT: + + case OP_MULTIPLY: + + case OP_DIVIDE: + + case OP_SCALE: + switch (Node->Size) { + case 2: + case 4: + case 8: + + default: + break; + } + + case OP_BREAK: + + case OP_CONTINUE: + + case OP_ADDRESS: + + case OP_DEREF: + + case OP_ASSIGN: + printf("Preparing for assignment..\r\n"); + if (Node->Right == NULL) + Die("Fault in assigning a null rvalue"); + + printf("\tCalculating assignment for target %s:\r\n", Node->Right->Symbol->Name); + switch (Node->Right->Operation) { + case REF_IDENT: + if (Node->Right->Symbol->Storage == SC_LOCAL) (void)0; + + case OP_DEREF: + default: + DieDecimal("Can't ASSIGN in AssembleTree: ", Node->Operation); + }; return 0; + + case OP_WIDEN: + + case OP_RET: + + case OP_EQUAL: + case OP_INEQ: + case OP_LESS: + case OP_GREAT: + case OP_LESSE: + case OP_GREATE: + if (ParentOp == OP_IF || ParentOp == OP_LOOP) (void)0; + + + case REF_IDENT: + if (TypeIsPtr(Node->ExprType)) { + } + + if (Node->RVal || ParentOp == OP_DEREF) { + if (Node->Symbol->Storage == SC_LOCAL || Node->Symbol->Storage == SC_PARAM) (void)0; + } else + return -1; + + case TERM_INTLITERAL: + + case TERM_STRLITERAL: + + case OP_PRINT: + + case OP_BITAND: + + case OP_BITOR: + + case OP_BITXOR: + + case OP_SHIFTL: + + case OP_SHIFTR: + + case OP_POSTINC: + if (Node->Symbol->Storage == SC_LOCAL || Node->Symbol->Storage == SC_PARAM) (void)0; + + case OP_POSTDEC: + if (Node->Symbol->Storage == SC_LOCAL || Node->Symbol->Storage == SC_PARAM) (void)0; + + case OP_PREINC: + if (Node->Symbol->Storage == SC_LOCAL || Node->Symbol->Storage == SC_PARAM) (void)0; + + case OP_PREDEC: + if (Node->Symbol->Storage == SC_LOCAL || Node->Symbol->Storage == SC_PARAM) (void)0; + + case OP_BOOLNOT: + + case OP_BITNOT: + + case OP_NEGATE: + + case OP_BOOLCONV: + return 0; + + default: + DieDecimal("Unknown ASM Operation", Node->Operation); + return 0; + } +} + +static const struct AssemblerVtable JVMAssemblerVtable = { + .AssembleTree = AssembleTree +}; + +static const struct AssemblerModule JVMAssemblerModule = { + .name = "JVM Bytecode", + .vtable = &JVMAssemblerVtable +}; + + +void RegisterJVM() { + RegisterModule(&JVMAssemblerModule); +} \ No newline at end of file diff --git a/src/assemble/QBEAssembler.c b/src/assemble/QBEAssembler.c new file mode 100644 index 0000000..3b26c6d --- /dev/null +++ b/src/assemble/QBEAssembler.c @@ -0,0 +1,180 @@ + +/*************/ +/*GEMWIRE */ +/* ERYTHRO*/ +/*************/ + +#include "Defs.h" +#include "Data.h" + +#define AGGREGATE(x) ":##x" +#define GLOBAL(x) "$##x" +#define TEMPORARY(x) "%##x" +#define LABEL(x) "@##x" + + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * * * R O O T O F A S S E M B L E R * * * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +// Just a short "hack" to make sure we only dump the tree the first time this function is called +static int Started = 0; + +/* + * Walk the AST tree given, and generate the assembly code that represents + * it. + * + * @param Node: The current Node to compile. If needed, its children will be parsed recursively. + * @param Register: The index of Registers to store the result of the current compilation. + * @param ParentOp: The Operation of the parent of the current Node. + * + * @return dependant on the Node. Typically the Register that stores the result of the Node's operation. + * + */ +static int AssembleTree(struct ASTNode* Node, int Register, int LoopBeginLabel, int LoopEndLabel, int ParentOp) { + int LeftVal, RightVal; + if (!Started && OptDumpTree) + DumpTree(Node, 0); + Started = 1; + + printf("Current operation: %d\r\n", Node->Operation); + switch (Node->Operation) { + case OP_IF: + + case OP_LOOP: + + case OP_COMP: + + case OP_CALL: + + case OP_FUNC: + break; + } + + + if (Node->Left) + LeftVal = AssembleTree(Node->Left, -1, LoopBeginLabel, LoopEndLabel, Node->Operation); + + if (Node->Right) + RightVal = AssembleTree(Node->Right, LeftVal, LoopBeginLabel, LoopEndLabel, Node->Operation); + + switch (Node->Operation) { + case OP_ADD: + + case OP_SUBTRACT: + + case OP_MULTIPLY: + + case OP_DIVIDE: + + case OP_SCALE: + switch (Node->Size) { + case 2: + case 4: + case 8: + + default: + break; + } + + case OP_BREAK: + + case OP_CONTINUE: + + case OP_ADDRESS: + + case OP_DEREF: + + case OP_ASSIGN: + printf("Preparing for assignment..\r\n"); + if (Node->Right == NULL) + Die("Fault in assigning a null rvalue"); + + printf("\tCalculating assignment for target %s:\r\n", Node->Right->Symbol->Name); + switch (Node->Right->Operation) { + case REF_IDENT: + if (Node->Right->Symbol->Storage == SC_LOCAL) (void)0; + + case OP_DEREF: + default: + DieDecimal("Can't ASSIGN in AssembleTree: ", Node->Operation); + }; return 0; + + case OP_WIDEN: + + case OP_RET: + + case OP_EQUAL: + case OP_INEQ: + case OP_LESS: + case OP_GREAT: + case OP_LESSE: + case OP_GREATE: + if (ParentOp == OP_IF || ParentOp == OP_LOOP) (void)0; + + + case REF_IDENT: + if (TypeIsPtr(Node->ExprType)) { + } + + if (Node->RVal || ParentOp == OP_DEREF) { + if (Node->Symbol->Storage == SC_LOCAL || Node->Symbol->Storage == SC_PARAM) (void)0; + } else + return -1; + + case TERM_INTLITERAL: + + case TERM_STRLITERAL: + + case OP_PRINT: + + case OP_BITAND: + + case OP_BITOR: + + case OP_BITXOR: + + case OP_SHIFTL: + + case OP_SHIFTR: + + case OP_POSTINC: + if (Node->Symbol->Storage == SC_LOCAL || Node->Symbol->Storage == SC_PARAM) (void)0; + + case OP_POSTDEC: + if (Node->Symbol->Storage == SC_LOCAL || Node->Symbol->Storage == SC_PARAM) (void)0; + + case OP_PREINC: + if (Node->Symbol->Storage == SC_LOCAL || Node->Symbol->Storage == SC_PARAM) (void)0; + + case OP_PREDEC: + if (Node->Symbol->Storage == SC_LOCAL || Node->Symbol->Storage == SC_PARAM) (void)0; + + case OP_BOOLNOT: + + case OP_BITNOT: + + case OP_NEGATE: + + case OP_BOOLCONV: + return 0; + + default: + DieDecimal("Unknown ASM Operation", Node->Operation); + return 0; + } +} + +static const struct AssemblerVtable QBEAssemblerVtable = { + .AssembleTree = AssembleTree +}; + +static const struct AssemblerModule QBEAssemblerModule = { + .name = "QBE", + .vtable = &QBEAssemblerVtable +}; + + +void RegisterQBE() { + RegisterModule(&QBEAssemblerModule); +} \ No newline at end of file diff --git a/tests/printf.er b/tests/printf.er index 4a67955..9b173a2 100644 --- a/tests/printf.er +++ b/tests/printf.er @@ -1,4 +1,4 @@ -int :: printf(char* format); +import "print.em" int :: main () { printf("%s\r\n", "hi there"); diff --git a/tests/test.java b/tests/test.java new file mode 100644 index 0000000..c881eed --- /dev/null +++ b/tests/test.java @@ -0,0 +1,5 @@ +class Test { + public static void main(String[] args) { + java.lang.System.out.println("hi"); + } +} \ No newline at end of file