diff --git a/src/Importer.c b/src/Importer.c index 40b7c4f..13f4636 100644 --- a/src/Importer.c +++ b/src/Importer.c @@ -12,6 +12,10 @@ #include #include +#if defined(__GNUC__) || defined(APPLE) +#include +#endif + /** * The function of the importer is to read in definitions from a file, and store * them into the symbol tables. @@ -50,6 +54,10 @@ // Read in the string that we know must be there. char* Module = strdup(CurrentIdentifier); + // Two strategies for finding the module; either it's relative to cwd, or it's relative to source. + bool FoundImport = false; + // Check the cwd first. + // Figure out the working directory char CWD[PATH_MAX]; @@ -65,8 +73,23 @@ // Stat the file to see if it exists struct stat FileInfo; - if (stat(ModulePath, &FileInfo) != 0) - DieMessage("Unable to access the imported module", ModulePath); + if (stat(ModulePath, &FileInfo) != 0) { + free(ModulePath); + char SourcePath[PATH_MAX + 1]; + realpath(CurrentFile->SourceName, SourcePath); + char* SourceFolderLength = strrchr(SourcePath, '/'); + *(SourceFolderLength + 1) = '\0'; + size_t SourcePathLength = strlen(SourcePath); + + ModulePath = malloc(SourcePathLength + sizeof(Module) + 1); + strcpy(ModulePath, SourcePath); + strcpy(ModulePath + SourcePathLength, Module); + + printf("Scanning %s for module definitions.\n", ModulePath); + + if (stat(ModulePath, &FileInfo) != 0) + DieMessage("Unable to access the imported module", ModulePath); + } // At this point, the file exists and we have the path. // Save the current file, so that we can restore it later diff --git a/src/assemble/ASMAssembler.c b/src/assemble/ASMAssembler.c index 6e02a1f..8245363 100644 --- a/src/assemble/ASMAssembler.c +++ b/src/assemble/ASMAssembler.c @@ -1337,4 +1337,903 @@ static struct AssemblerModule Win32ASMModule = { void RegisterWin32ASM() { RegisterModule(&Win32ASMModule); +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * * * 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); } \ No newline at end of file diff --git a/tests/sieve.er b/tests/sieve.er index 65ec885..792cd70 100644 --- a/tests/sieve.er +++ b/tests/sieve.er @@ -1,4 +1,4 @@ -import "tests/import/defs.eh" +import "import/defs.eh" long num[100]; int :: main() {