Working towards functions.. We have parameters and function local types
This commit is contained in:
parent
0c148f6e0c
commit
389d4b47ff
|
@ -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();
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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",
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
17
tests/arguments
Normal 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);
|
||||
}
|
Loading…
Reference in New Issue
Block a user