Working towards functions.. We have parameters and function local types

This commit is contained in:
Curle 2021-01-17 06:37:39 +00:00
parent 0c148f6e0c
commit 389d4b47ff
No known key found for this signature in database
GPG Key ID: 58A5C4688ECE6E7C
7 changed files with 143 additions and 28 deletions

View File

@ -8,6 +8,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <ctype.h> #include <ctype.h>
#include <string.h> #include <string.h>
#include <stdbool.h>
/* /*
* ARithmetic tokens are prefixed AR. * ARithmetic tokens are prefixed AR.
@ -204,7 +205,7 @@ enum StorageScope {
SC_GLOBAL = 1, // Global Scope SC_GLOBAL = 1, // Global Scope
//SC_CLASS, // Class-local definitions //SC_CLASS, // Class-local definitions
//SC_STATIC, // Static storage definitions //SC_STATIC, // Static storage definitions
//SC_PARAM, // Function parameters SC_PARAM, // Function parameters
SC_LOCAL // Function-local scope. SC_LOCAL // Function-local scope.
// There is no deeper scope than function. // 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); 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 * * * * * * * * 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 AsStrGlobalVar(int Register, int ID);
int AsStrLocalVar(int Register, int ID); int AsStrLocalVar(int Register, int ID);
int AsCalcOffset(int Type, int Param); int AsCalcOffset(int Type);
void AsNewStackFrame(); void AsNewStackFrame();
int AsDeref(int Reg, int Type); 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 * * * * * * * * 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* ParseIdentifier(void);
struct ASTNode* IfStatement(); struct ASTNode* IfStatement();

View File

@ -11,7 +11,7 @@
/* /*
* If the entry in UsedRegisters * If the entry in UsedRegisters
* that correlates to the position of a register in Registers * that correlates to the position of a register in Registers
* is 1 * is 1,
* then that register is classed as used - * then that register is classed as used -
* it has useful data inside it. * it has useful data inside it.
* *
@ -19,9 +19,19 @@
*/ */
static int UsedRegisters[4]; static int UsedRegisters[4];
static char* Registers[4] = { "%r8", "%r9", "%r10", "%r11" }; /* The https://en.wikipedia.org/wiki/X86_calling_conventions#Microsoft_x64_calling_convention
static char* DoubleRegisters[4] = { "%r8d", "%r9d", "%r10d", "%r11d" }; * calling convention on Windows requires that
static char* ByteRegisters[4] = { "%r8b", "%r9b", "%r10b", "%r11b" }; * 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* Comparisons[6] = { "sete", "setne", "setl", "setg", "setle", "setge" };
static char* InvComparisons[6] = { "jne", "je", "jge", "jle", "jg", "jl"}; 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: case REF_IDENT:
if(Node->RVal || ParentOp == OP_DEREF) { 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); return AsLdLocalVar(Node->Value.ID, Node->Operation);
else else
return AsLdGlobalVar(Node->Value.ID, Node->Operation); return AsLdGlobalVar(Node->Value.ID, Node->Operation);
@ -276,7 +286,7 @@ void AsNewStackFrame() {
LocalVarOffset = 0; LocalVarOffset = 0;
} }
int AsCalcOffset(int Type, int Param) { int AsCalcOffset(int Type) {
LocalVarOffset += PrimitiveSize(Type) > 4 ? PrimitiveSize(Type) : 4; LocalVarOffset += PrimitiveSize(Type) > 4 ? PrimitiveSize(Type) : 4;
return -LocalVarOffset; return -LocalVarOffset;
} }
@ -887,8 +897,10 @@ void AssemblerPreamble() {
void AsFunctionPreamble(int FunctionID) { void AsFunctionPreamble(int FunctionID) {
char* Name = Symbols[FunctionID].Name; char* Name = Symbols[FunctionID].Name;
int ParamOffset = 48, ParamReg = 9;
StackFrameOffset = (LocalVarOffset + 31) & ~31;
AsNewStackFrame();
fprintf(OutputFile, fprintf(OutputFile,
"\t.text\n" "\t.text\n"
@ -896,12 +908,42 @@ void AsFunctionPreamble(int FunctionID) {
"\t.def\t%s; .scl 2; .type 32; .endef\n" "\t.def\t%s; .scl 2; .type 32; .endef\n"
"%s:\n" "%s:\n"
"\tpushq\t%%rbp\n" "\tpushq\t%%rbp\n"
"\tmovq\t%%rsp, %%rbp\n" "\tmovq\t%%rsp, %%rbp\r\n",
"\taddq\t$%d, %%rsp\n", Name, Name, Name, -StackFrameOffset); Name, Name, Name);
//PECOFF requires we call the global initialisers //PECOFF requires we call the global initialisers
if(!strcmp(Name, "main")) if(!strcmp(Name, "main"))
fprintf(OutputFile, "\tcall\t__main\n"); 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) { void AsFunctionEpilogue(int FunctionID) {

View File

@ -13,6 +13,7 @@
int TypeSizes[9] = { 0, 1, 4, 8, 0, 8, 8, 8, 8}; // in BYTES 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", "End of file",
"Equivalency", "Equivalency",

View File

@ -391,6 +391,7 @@ struct ASTNode* CallFunction() {
struct ASTNode* ParseStatement(void) { struct ASTNode* ParseStatement(void) {
int Type; int Type;
printf("\t\tBranch leads to here, type %s/%d\r\n", TokenNames[CurrentToken.type], CurrentToken.type);
switch(CurrentToken.type) { switch(CurrentToken.type) {
case TY_CHAR: case TY_CHAR:
case TY_LONG: case TY_LONG:
@ -398,7 +399,8 @@ struct ASTNode* ParseStatement(void) {
printf("\t\tNew Variable: %s\n", CurrentIdentifier); printf("\t\tNew Variable: %s\n", CurrentIdentifier);
Type = ParseOptionalPointer(); Type = ParseOptionalPointer();
VerifyToken(TY_IDENTIFIER, "ident"); VerifyToken(TY_IDENTIFIER, "ident");
BeginVariableDeclaration(Type, SC_LOCAL); BeginVariableDeclaration(Type, SC_LOCAL, false);
VerifyToken(LI_SEMIC, ";"); // TODO: single line assignment?
return NULL; return NULL;
/*case TY_IDENTIFIER: /*case TY_IDENTIFIER:
@ -481,9 +483,10 @@ void ParseGlobals() {
Tree = ParseFunction(Type); Tree = ParseFunction(Type);
printf("\nBeginning assembler creation of new function %s\n", Symbols[Tree->Value.ID].Name); printf("\nBeginning assembler creation of new function %s\n", Symbols[Tree->Value.ID].Name);
AssembleTree(Tree, -1, 0); AssembleTree(Tree, -1, 0);
FreeLocals();
} else { } else {
printf("\tParsing global variable declaration\n"); printf("\tParsing global variable declaration\n");
BeginVariableDeclaration(Type, SC_GLOBAL); BeginVariableDeclaration(Type, SC_GLOBAL, false);
} }
if(CurrentToken.type == LI_EOF) if(CurrentToken.type == LI_EOF)

View File

@ -6,8 +6,31 @@
#include <Defs.h> #include <Defs.h>
#include <Data.h> #include <Data.h>
#include <stdbool.h>
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. * Handles the declaration of a type of a variable.
@ -22,7 +45,7 @@
* //TODO: int i = 5; * //TODO: int i = 5;
* *
*/ */
void BeginVariableDeclaration(int Type, int Scope) { void BeginVariableDeclaration(int Type, int Scope, bool isParameter) {
int ID; int ID;
printf("type: %s\n", TypeNames[Type]); printf("type: %s\n", TypeNames[Type]);
@ -31,27 +54,34 @@ void BeginVariableDeclaration(int Type, int Scope) {
//Type = Type - 2; //Type = Type - 2;
if(CurrentToken.type == LI_INT) { if(CurrentToken.type == LI_INT) {
printf("Adding array %s that is %d x %s.\r\n", CurrentIdentifier, CurrentToken.value, TypeNames[Type]); 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); Tokenise(&CurrentToken);
VerifyToken(LI_RBRAS, "]"); VerifyToken(LI_RBRAS, "]");
} else { } else {
printf("Adding var %s that is a %s\r\n", CurrentIdentifier, TypeNames[Type]); 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* ParseFunction(int Type) {
struct ASTNode* Tree; struct ASTNode* Tree;
struct ASTNode* FinalStatement; struct ASTNode* FinalStatement;
int SymbolSlot, BreakLabel; int SymbolSlot, BreakLabel, ParamCount;
printf("\nIdentified function %s of return type %s\n", CurrentIdentifier, TypeNames[Type]);
BreakLabel = NewLabel(); 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); SymbolSlot = AddSymbol(CurrentIdentifier, Type, ST_FUNC, SC_GLOBAL, BreakLabel, 1);
CurrentFunction = SymbolSlot; CurrentFunction = SymbolSlot;
@ -59,6 +89,8 @@ struct ASTNode* ParseFunction(int Type) {
AsNewStackFrame(); AsNewStackFrame();
VerifyToken(LI_LPARE, "("); VerifyToken(LI_LPARE, "(");
ParamCount = ReadParameters();
Symbols[SymbolSlot].Length = ParamCount;
VerifyToken(LI_RPARE, ")"); VerifyToken(LI_RPARE, ")");
Tree = ParseCompound(); Tree = ParseCompound();

View File

@ -22,7 +22,7 @@ int FindSymbolImpl(char* Symbol, int Storage) {
: SYMBOLS /* Otherwise, start at the end and work backward */ : SYMBOLS /* Otherwise, start at the end and work backward */
); );
Ind++) { 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)) { if(*Symbol == *Symbols[Ind].Name && !strcmp(Symbol, Symbols[Ind].Name)) {
printf("\t\tFound %s at %d\r\n", Symbol, Ind); printf("\t\tFound %s at %d\r\n", Symbol, Ind);
return Ind; return Ind;
@ -83,6 +83,15 @@ static int NewLocalSymbol() {
return Pos; return Pos;
} }
/*
* Reset the local counter on functions.
*/
void FreeLocals() {
CurrentLocal = SYMBOLS - 1;
}
/* /*
* Add a symbol to the tables, and set all the metadata. * Add a symbol to the tables, and set all the metadata.
* @param Name: The string representing the name of the symbol. * @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 TableSlot;
int SinkOffset = 0; int SinkOffset = 0;
if((TableSlot = FindSymbolImpl(Name, Storage) != -1)) if((TableSlot = FindSymbolImpl(Name, Storage)) != -1)
return TableSlot; return -1;
// Instaed of spliting this up into AddLocalSymbol and AddGlobalSymbol, // Instaed of spliting this up into AddLocalSymbol and AddGlobalSymbol,
// we can use this switch to avoid duplicated code. // we can use this switch to avoid duplicated code.
switch(Storage) { 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: case SC_GLOBAL:
TableSlot = NewGlobalSymbol(); TableSlot = NewGlobalSymbol();
break; break;
case SC_LOCAL: case SC_LOCAL:
printf("\tCreating new local symbol %s\r\n", Name); printf("\tCreating new local symbol %s\r\n", Name);
TableSlot = NewLocalSymbol(); TableSlot = NewLocalSymbol();
SinkOffset = AsCalcOffset(Type, 0); SinkOffset = AsCalcOffset(Type);
break; break;
} }
@ -121,12 +138,13 @@ int AddSymbol(char* Name, int Type, int Structure, int Storage, int EndLabel, in
Symbols[TableSlot].Storage = Storage; Symbols[TableSlot].Storage = Storage;
Symbols[TableSlot].Length = Length; Symbols[TableSlot].Length = Length;
Symbols[TableSlot].SinkOffset = SinkOffset; Symbols[TableSlot].SinkOffset = SinkOffset;
Symbols[TableSlot].EndLabel = EndLabel;
// NOTE: Generating global symbol names must happen AFTER the name and type are declared. // NOTE: Generating global symbol names must happen AFTER the name and type are declared.
switch(Storage) { switch(Storage) {
case SC_GLOBAL: case SC_GLOBAL:
printf("\tCreating new global symbol %s into slot %d\r\n", Name, TableSlot); 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"); printf("\t\tGenerating data symbol.\r\n");
AsGlobalSymbol(TableSlot); AsGlobalSymbol(TableSlot);
} }

17
tests/arguments Normal file
View File

@ -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);
}