From 389d4b47ffc99356e731bdbcea8beed7882de241 Mon Sep 17 00:00:00 2001 From: Curle Date: Sun, 17 Jan 2021 06:37:39 +0000 Subject: [PATCH] Working towards functions.. We have parameters and function local types --- include/Defs.h | 8 ++++--- src/Assembler.c | 60 ++++++++++++++++++++++++++++++++++++++++-------- src/Main.c | 3 ++- src/Parser.c | 9 +++++--- src/Statements.c | 46 +++++++++++++++++++++++++++++++------ src/Symbols.c | 28 ++++++++++++++++++---- tests/arguments | 17 ++++++++++++++ 7 files changed, 143 insertions(+), 28 deletions(-) create mode 100644 tests/arguments diff --git a/include/Defs.h b/include/Defs.h index 7f734da..b433459 100644 --- a/include/Defs.h +++ b/include/Defs.h @@ -8,6 +8,7 @@ #include #include #include +#include /* * ARithmetic tokens are prefixed AR. @@ -204,7 +205,7 @@ enum StorageScope { SC_GLOBAL = 1, // Global Scope //SC_CLASS, // Class-local definitions //SC_STATIC, // Static storage definitions - //SC_PARAM, // Function parameters + SC_PARAM, // Function parameters SC_LOCAL // Function-local scope. // There is no deeper scope than function. }; @@ -327,6 +328,7 @@ int FindSymbol(char* Symbol); int AddSymbol(char* Name, int Type, int Structure, int Storage, int EndLabel, int Length); +void FreeLocals(); /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * C O N T R O L S T A T U S * * * * @@ -366,7 +368,7 @@ int AsLdLocalVar(int ID, int Operation); int AsStrGlobalVar(int Register, int ID); int AsStrLocalVar(int Register, int ID); -int AsCalcOffset(int Type, int Param); +int AsCalcOffset(int Type); void AsNewStackFrame(); int AsDeref(int Reg, int Type); @@ -422,7 +424,7 @@ void AsFunctionEpilogue(int ID); * * * * D E C L A R A T I O N * * * * * * * * * * * * * * * * * * * * * * * * * * */ -void BeginVariableDeclaration(int Type, int Scope); +void BeginVariableDeclaration(int Type, int Scope, bool isParameter); struct ASTNode* ParseIdentifier(void); struct ASTNode* IfStatement(); diff --git a/src/Assembler.c b/src/Assembler.c index 9357df1..7e503ab 100644 --- a/src/Assembler.c +++ b/src/Assembler.c @@ -11,7 +11,7 @@ /* * If the entry in UsedRegisters * that correlates to the position of a register in Registers - * is 1 + * is 1, * then that register is classed as used - * it has useful data inside it. * @@ -19,9 +19,19 @@ */ static int UsedRegisters[4]; -static char* Registers[4] = { "%r8", "%r9", "%r10", "%r11" }; -static char* DoubleRegisters[4] = { "%r8d", "%r9d", "%r10d", "%r11d" }; -static char* ByteRegisters[4] = { "%r8b", "%r9b", "%r10b", "%r11b" }; +/* The https://en.wikipedia.org/wiki/X86_calling_conventions#Microsoft_x64_calling_convention + * calling convention on Windows requires that + * the last 4 arguments are placed in registers + * rcx, rdx, r8 and r9. + * This order must be preserved, and they must be placed + * right to left. + * + * That is the reason for the weird arrangement here. + * The parameter registers are last, in reverse order. + */ +static char* Registers[10] = { "%rsi", "%rdi", "%r10", "%r11" , "%r12" , "%r13", "%r9" , "%r8", "%rdx", "%rcx" }; +static char* DoubleRegisters[10] = { "%esi", "%edi", "%r10d", "%r11d", "%r12d", "%r13d", "%r9d", "%r8d", "%edx", "%ecx" }; +static char* ByteRegisters[10] = { "%sil", "%dil", "%r10b", "%r11b", "%r12b", "%r13b", "%r9b", "%r8b", "%dl" , "%cl" }; static char* Comparisons[6] = { "sete", "setne", "setl", "setg", "setle", "setge" }; static char* InvComparisons[6] = { "jne", "je", "jge", "jle", "jg", "jl"}; @@ -170,7 +180,7 @@ int AssembleTree(struct ASTNode* Node, int Register, int ParentOp) { case REF_IDENT: if(Node->RVal || ParentOp == OP_DEREF) { - if(Symbols[Node->Value.ID].Storage == SC_LOCAL) + if(Symbols[Node->Value.ID].Storage == SC_LOCAL || Symbols[Node->Value.ID].Storage == SC_PARAM) return AsLdLocalVar(Node->Value.ID, Node->Operation); else return AsLdGlobalVar(Node->Value.ID, Node->Operation); @@ -276,7 +286,7 @@ void AsNewStackFrame() { LocalVarOffset = 0; } -int AsCalcOffset(int Type, int Param) { +int AsCalcOffset(int Type) { LocalVarOffset += PrimitiveSize(Type) > 4 ? PrimitiveSize(Type) : 4; return -LocalVarOffset; } @@ -887,8 +897,10 @@ void AssemblerPreamble() { void AsFunctionPreamble(int FunctionID) { char* Name = Symbols[FunctionID].Name; + int ParamOffset = 48, ParamReg = 9; - StackFrameOffset = (LocalVarOffset + 31) & ~31; + + AsNewStackFrame(); fprintf(OutputFile, "\t.text\n" @@ -896,12 +908,42 @@ void AsFunctionPreamble(int FunctionID) { "\t.def\t%s; .scl 2; .type 32; .endef\n" "%s:\n" "\tpushq\t%%rbp\n" - "\tmovq\t%%rsp, %%rbp\n" - "\taddq\t$%d, %%rsp\n", Name, Name, Name, -StackFrameOffset); + "\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(LoopIndex = SYMBOLS - 1; LoopIndex > CurrentLocal; LoopIndex--) { + if(Symbols[LoopIndex].Storage != SC_PARAM) + break; + if(LoopIndex < SYMBOLS - 4) // We only have 4 argument registers + break; + + Symbols[LoopIndex].SinkOffset = AsCalcOffset(Symbols[LoopIndex].Type); + AsStrLocalVar(ParamReg--, LoopIndex); + } + + // If we have more parameters, move them to the stack + for(; LoopIndex > CurrentLocal; LoopIndex--) { + if(Symbols[LoopIndex].Storage == SC_PARAM) { + Symbols[LoopIndex].SinkOffset = ParamOffset; + ParamOffset += 8; + } else { + Symbols[LoopIndex].SinkOffset = AsCalcOffset(Symbols[LoopIndex].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); + } void AsFunctionEpilogue(int FunctionID) { diff --git a/src/Main.c b/src/Main.c index a6e8d7f..c643c00 100644 --- a/src/Main.c +++ b/src/Main.c @@ -12,7 +12,8 @@ int TypeSizes[9] = { 0, 1, 4, 8, 0, 8, 8, 8, 8}; // in BYTES -char* TokenNames[] = { +char* TokenNames[] = { + "", // Push everything up by one "End of file", "Equivalency", diff --git a/src/Parser.c b/src/Parser.c index 173f17f..e299d47 100644 --- a/src/Parser.c +++ b/src/Parser.c @@ -390,7 +390,8 @@ struct ASTNode* CallFunction() { struct ASTNode* ParseStatement(void) { int Type; - + + printf("\t\tBranch leads to here, type %s/%d\r\n", TokenNames[CurrentToken.type], CurrentToken.type); switch(CurrentToken.type) { case TY_CHAR: case TY_LONG: @@ -398,7 +399,8 @@ struct ASTNode* ParseStatement(void) { printf("\t\tNew Variable: %s\n", CurrentIdentifier); Type = ParseOptionalPointer(); VerifyToken(TY_IDENTIFIER, "ident"); - BeginVariableDeclaration(Type, SC_LOCAL); + BeginVariableDeclaration(Type, SC_LOCAL, false); + VerifyToken(LI_SEMIC, ";"); // TODO: single line assignment? return NULL; /*case TY_IDENTIFIER: @@ -481,9 +483,10 @@ void ParseGlobals() { Tree = ParseFunction(Type); printf("\nBeginning assembler creation of new function %s\n", Symbols[Tree->Value.ID].Name); AssembleTree(Tree, -1, 0); + FreeLocals(); } else { printf("\tParsing global variable declaration\n"); - BeginVariableDeclaration(Type, SC_GLOBAL); + BeginVariableDeclaration(Type, SC_GLOBAL, false); } if(CurrentToken.type == LI_EOF) diff --git a/src/Statements.c b/src/Statements.c index 8cf8e25..4790734 100644 --- a/src/Statements.c +++ b/src/Statements.c @@ -6,8 +6,31 @@ #include #include +#include +static int ReadParameters() { + int TokenType, ParamCount = 0; + while(CurrentToken.type != LI_RPARE) { + TokenType = ParseOptionalPointer(); + VerifyToken(TY_IDENTIFIER, "identifier"); + + BeginVariableDeclaration(TokenType, SC_PARAM, true); + ParamCount++; + + switch(CurrentToken.type) { + case LI_COM: + Tokenise(&CurrentToken); + break; + case LI_RPARE: + break; + default: + DieDecimal("Unexpected token in parameter", CurrentToken.type); + } + } + + return ParamCount; +} /* * Handles the declaration of a type of a variable. @@ -22,7 +45,7 @@ * //TODO: int i = 5; * */ -void BeginVariableDeclaration(int Type, int Scope) { +void BeginVariableDeclaration(int Type, int Scope, bool isParameter) { int ID; printf("type: %s\n", TypeNames[Type]); @@ -31,27 +54,34 @@ void BeginVariableDeclaration(int Type, int Scope) { //Type = Type - 2; if(CurrentToken.type == LI_INT) { printf("Adding array %s that is %d x %s.\r\n", CurrentIdentifier, CurrentToken.value, TypeNames[Type]); - AddSymbol(CurrentIdentifier, PointerTo(Type), ST_ARR, Scope, 0, CurrentToken.value); + if(Scope == SC_LOCAL) + Die("Local arrays are not supported"); + else + AddSymbol(CurrentIdentifier, PointerTo(Type), ST_ARR, Scope, 0, CurrentToken.value); } Tokenise(&CurrentToken); VerifyToken(LI_RBRAS, "]"); } else { printf("Adding var %s that is a %s\r\n", CurrentIdentifier, TypeNames[Type]); - AddSymbol(CurrentIdentifier, Type, ST_VAR, Scope, 0, 1); + if(Scope == SC_LOCAL) { + if(AddSymbol(CurrentIdentifier, Type, ST_VAR, Scope, 0, 1) == -1) + Die("Illegal state: Identical locals in current function"); + } else { + AddSymbol(CurrentIdentifier, Type, ST_VAR, Scope, 0, 1); + } } - VerifyToken(LI_SEMIC, ";"); + //VerifyToken(LI_SEMIC, ";"); } struct ASTNode* ParseFunction(int Type) { struct ASTNode* Tree; struct ASTNode* FinalStatement; - int SymbolSlot, BreakLabel; - - printf("\nIdentified function %s of return type %s\n", CurrentIdentifier, TypeNames[Type]); + int SymbolSlot, BreakLabel, ParamCount; BreakLabel = NewLabel(); + printf("\nIdentified function %s of return type %s, end label %d\n", CurrentIdentifier, TypeNames[Type], BreakLabel); SymbolSlot = AddSymbol(CurrentIdentifier, Type, ST_FUNC, SC_GLOBAL, BreakLabel, 1); CurrentFunction = SymbolSlot; @@ -59,6 +89,8 @@ struct ASTNode* ParseFunction(int Type) { AsNewStackFrame(); VerifyToken(LI_LPARE, "("); + ParamCount = ReadParameters(); + Symbols[SymbolSlot].Length = ParamCount; VerifyToken(LI_RPARE, ")"); Tree = ParseCompound(); diff --git a/src/Symbols.c b/src/Symbols.c index 493c036..b83600a 100644 --- a/src/Symbols.c +++ b/src/Symbols.c @@ -22,7 +22,7 @@ int FindSymbolImpl(char* Symbol, int Storage) { : SYMBOLS /* Otherwise, start at the end and work backward */ ); Ind++) { - + if(Storage == SC_GLOBAL && Symbols[Ind].Storage == SC_PARAM) continue; // Skip searching globals for parameters. if(*Symbol == *Symbols[Ind].Name && !strcmp(Symbol, Symbols[Ind].Name)) { printf("\t\tFound %s at %d\r\n", Symbol, Ind); return Ind; @@ -83,6 +83,15 @@ static int NewLocalSymbol() { return Pos; } +/* + * Reset the local counter on functions. + */ + +void FreeLocals() { + CurrentLocal = SYMBOLS - 1; +} + + /* * Add a symbol to the tables, and set all the metadata. * @param Name: The string representing the name of the symbol. @@ -99,19 +108,27 @@ int AddSymbol(char* Name, int Type, int Structure, int Storage, int EndLabel, in int TableSlot; int SinkOffset = 0; - if((TableSlot = FindSymbolImpl(Name, Storage) != -1)) - return TableSlot; + if((TableSlot = FindSymbolImpl(Name, Storage)) != -1) + return -1; // Instaed of spliting this up into AddLocalSymbol and AddGlobalSymbol, // we can use this switch to avoid duplicated code. switch(Storage) { + case SC_PARAM: + // Instead of special casing parameters, we can just add these to the symbol lists and be done with it. + printf("\tPreparing new parameter %s of type %s\r\n", Name, TypeNames[Type]); + TableSlot = AddSymbol(Name, Type, Structure, SC_GLOBAL, 88, 1); + Symbols[TableSlot].Storage = SC_PARAM; // Fix the parameter after running the global process + TableSlot = AddSymbol(Name, Type, Structure, SC_LOCAL, 88, 1); + Symbols[TableSlot].Storage = SC_PARAM; // Fix the parameter after running the local process + return TableSlot; case SC_GLOBAL: TableSlot = NewGlobalSymbol(); break; case SC_LOCAL: printf("\tCreating new local symbol %s\r\n", Name); TableSlot = NewLocalSymbol(); - SinkOffset = AsCalcOffset(Type, 0); + SinkOffset = AsCalcOffset(Type); break; } @@ -121,12 +138,13 @@ int AddSymbol(char* Name, int Type, int Structure, int Storage, int EndLabel, in Symbols[TableSlot].Storage = Storage; Symbols[TableSlot].Length = Length; Symbols[TableSlot].SinkOffset = SinkOffset; + Symbols[TableSlot].EndLabel = EndLabel; // NOTE: Generating global symbol names must happen AFTER the name and type are declared. switch(Storage) { case SC_GLOBAL: printf("\tCreating new global symbol %s into slot %d\r\n", Name, TableSlot); - if(Structure != ST_FUNC) { + if(Structure != ST_FUNC && EndLabel != 88) { // Magic keyword so that we don't generate ASM globals for parameters printf("\t\tGenerating data symbol.\r\n"); AsGlobalSymbol(TableSlot); } diff --git a/tests/arguments b/tests/arguments new file mode 100644 index 0000000..a731ca4 --- /dev/null +++ b/tests/arguments @@ -0,0 +1,17 @@ +i32 :: twoparams(int a, i32 b) { + int c; + int d; + int e; + c = 3; + d = 4; + e = 5; + PrintInteger(a); PrintInteger(b); PrintInteger(c); PrintInteger(d); PrintInteger(e); + return (0); +} + +int eightparams(int a, int b, int c, int d, int e, int f, int g, int h) { + PrintInteger(a); PrintInteger(b); PrintInteger(c); + PrintInteger(d); PrintInteger(e); PrintInteger(f); + PrintInteger(g); PrintInteger(h); + return (0); +} \ No newline at end of file