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 <ctype.h>
#include <string.h>
#include <stdbool.h>
/*
* 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();

View File

@ -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) {

View File

@ -13,6 +13,7 @@
int TypeSizes[9] = { 0, 1, 4, 8, 0, 8, 8, 8, 8}; // in BYTES
char* TokenNames[] = {
"", // Push everything up by one
"End of file",
"Equivalency",

View File

@ -391,6 +391,7 @@ 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)

View File

@ -6,8 +6,31 @@
#include <Defs.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.
@ -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();

View File

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

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