mirror of
https://github.com/gbdev/rgbds.git
synced 2025-11-22 11:12:07 +00:00
Implement compact file stacks in object files
Gets rid of `open_memstream`, enabling Windows compatibility again Also fixes #491 as a nice bonus!
This commit is contained in:
392
src/asm/fstack.c
392
src/asm/fstack.c
@@ -29,27 +29,83 @@
|
||||
|
||||
struct Context {
|
||||
struct Context *parent;
|
||||
struct Context *child;
|
||||
struct FileStackNode *fileInfo;
|
||||
struct LexerState *lexerState;
|
||||
uint32_t uniqueID;
|
||||
char const *fileName;
|
||||
char *fileNameBuf;
|
||||
uint32_t lineNo; /* Line number at which the context was EXITED */
|
||||
struct Symbol const *macro;
|
||||
struct MacroArgs *macroArgs; /* Macro args are *saved* here */
|
||||
uint32_t nbReptIters; /* If zero, this isn't a REPT block */
|
||||
size_t reptDepth;
|
||||
uint32_t reptIters[];
|
||||
uint32_t nbReptIters;
|
||||
};
|
||||
|
||||
static struct Context *contextStack;
|
||||
static struct Context *topLevelContext;
|
||||
static size_t contextDepth = 0;
|
||||
#define DEFAULT_MAX_DEPTH 64
|
||||
size_t nMaxRecursionDepth;
|
||||
|
||||
static unsigned int nbIncPaths = 0;
|
||||
static char const *includePaths[MAXINCPATHS];
|
||||
|
||||
char const *dumpNodeAndParents(struct FileStackNode const *node)
|
||||
{
|
||||
char const *name;
|
||||
|
||||
if (node->type == NODE_REPT) {
|
||||
assert(node->parent); /* REPT nodes should always have a parent */
|
||||
struct FileStackReptNode const *reptInfo = (struct FileStackReptNode const *)node;
|
||||
|
||||
name = dumpNodeAndParents(node->parent);
|
||||
fprintf(stderr, "(%" PRIu32 ") -> %s", node->lineNo, name);
|
||||
for (uint32_t i = reptInfo->reptDepth; i--; )
|
||||
fprintf(stderr, "::REPT~%" PRIu32, reptInfo->iters[i]);
|
||||
} else {
|
||||
name = ((struct FileStackNamedNode const *)node)->name;
|
||||
if (node->parent) {
|
||||
dumpNodeAndParents(node->parent);
|
||||
fprintf(stderr, "(%" PRIu32 ") -> %s", node->lineNo, name);
|
||||
} else {
|
||||
fputs(name, stderr);
|
||||
}
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
void fstk_Dump(struct FileStackNode const *node, uint32_t lineNo)
|
||||
{
|
||||
dumpNodeAndParents(node);
|
||||
fprintf(stderr, "(%" PRIu32 ")", lineNo);
|
||||
}
|
||||
|
||||
void fstk_DumpCurrent(void)
|
||||
{
|
||||
if (!contextStack) {
|
||||
fputs("at top level", stderr);
|
||||
return;
|
||||
}
|
||||
fstk_Dump(contextStack->fileInfo, lexer_GetLineNo());
|
||||
}
|
||||
|
||||
struct FileStackNode *fstk_GetFileStack(void)
|
||||
{
|
||||
struct FileStackNode *node = contextStack->fileInfo;
|
||||
|
||||
/* Mark node and all of its parents as referenced if not already so they don't get freed */
|
||||
while (node && !node->referenced) {
|
||||
node->ID = -1;
|
||||
node->referenced = true;
|
||||
node = node->parent;
|
||||
}
|
||||
return contextStack->fileInfo;
|
||||
}
|
||||
|
||||
char const *fstk_GetFileName(void)
|
||||
{
|
||||
/* Iterating via the nodes themselves skips nested REPTs */
|
||||
struct FileStackNode const *node = contextStack->fileInfo;
|
||||
|
||||
while (node->type != NODE_FILE)
|
||||
node = node->parent;
|
||||
return ((struct FileStackNamedNode const *)node)->name;
|
||||
}
|
||||
|
||||
void fstk_AddIncludePath(char const *path)
|
||||
{
|
||||
if (path[0] == '\0')
|
||||
@@ -141,12 +197,28 @@ bool fstk_FindFile(char const *path, char **fullPath, size_t *size)
|
||||
|
||||
bool yywrap(void)
|
||||
{
|
||||
if (contextStack->nbReptIters) { /* The context is a REPT block, which may loop */
|
||||
contextStack->reptIters[contextStack->reptDepth - 1]++;
|
||||
if (contextStack->fileInfo->type == NODE_REPT) { /* The context is a REPT block, which may loop */
|
||||
struct FileStackReptNode *fileInfo = (struct FileStackReptNode *)contextStack->fileInfo;
|
||||
|
||||
/* If the node is referenced, we can't edit it; duplicate it */
|
||||
if (contextStack->fileInfo->referenced) {
|
||||
struct FileStackReptNode *copy = malloc(sizeof(*copy) + sizeof(copy->iters[0]) * fileInfo->reptDepth);
|
||||
|
||||
if (!copy)
|
||||
fatalerror("Failed to duplicate REPT file node: %s\n", strerror(errno));
|
||||
/* Copy all info but the referencing */
|
||||
*copy = *fileInfo;
|
||||
copy->node.next = NULL;
|
||||
copy->node.referenced = false;
|
||||
|
||||
fileInfo = copy;
|
||||
contextStack->fileInfo = (struct FileStackNode *)fileInfo;
|
||||
}
|
||||
|
||||
fileInfo->iters[0]++;
|
||||
/* If this wasn't the last iteration, wrap instead of popping */
|
||||
if (contextStack->reptIters[contextStack->reptDepth - 1]
|
||||
<= contextStack->nbReptIters) {
|
||||
lexer_RestartRept(contextStack->parent->lineNo);
|
||||
if (fileInfo->iters[0] <= contextStack->nbReptIters) {
|
||||
lexer_RestartRept(contextStack->fileInfo->lineNo);
|
||||
contextStack->uniqueID = macro_UseNewUniqueID();
|
||||
return false;
|
||||
}
|
||||
@@ -155,44 +227,52 @@ bool yywrap(void)
|
||||
}
|
||||
dbgPrint("Popping context\n");
|
||||
|
||||
/* Free an `INCLUDE`'s path */
|
||||
if (contextStack->fileNameBuf)
|
||||
free(contextStack->fileNameBuf);
|
||||
struct Context *context = contextStack;
|
||||
|
||||
contextStack = contextStack->parent;
|
||||
contextDepth--;
|
||||
|
||||
lexer_DeleteState(contextStack->child->lexerState);
|
||||
lexer_DeleteState(context->lexerState);
|
||||
/* Restore args if a macro (not REPT) saved them */
|
||||
if (contextStack->child->nbReptIters == 0 && contextStack->child->macro) {
|
||||
if (context->fileInfo->type == NODE_MACRO) {
|
||||
dbgPrint("Restoring macro args %p\n", contextStack->macroArgs);
|
||||
macro_UseNewArgs(contextStack->macroArgs);
|
||||
}
|
||||
/* Free the file stack node */
|
||||
if (!context->fileInfo->referenced)
|
||||
free(context->fileInfo);
|
||||
/* Free the entry and make its parent the current entry */
|
||||
free(contextStack->child);
|
||||
free(context);
|
||||
|
||||
contextStack->child = NULL;
|
||||
lexer_SetState(contextStack->lexerState);
|
||||
macro_SetUniqueID(contextStack->uniqueID);
|
||||
return false;
|
||||
}
|
||||
|
||||
static void newContext(uint32_t reptDepth)
|
||||
/*
|
||||
* Make sure not to switch the lexer state before calling this, so the saved line no is correct
|
||||
* BE CAREFUL!! This modifies the file stack directly, you should have set up the file info first
|
||||
*/
|
||||
static void newContext(struct FileStackNode *fileInfo)
|
||||
{
|
||||
if (++contextDepth >= nMaxRecursionDepth)
|
||||
fatalerror("Recursion limit (%zu) exceeded\n", nMaxRecursionDepth);
|
||||
contextStack->child = malloc(sizeof(*contextStack->child)
|
||||
+ reptDepth * sizeof(contextStack->reptIters[0]));
|
||||
if (!contextStack->child)
|
||||
struct Context *context = malloc(sizeof(*context));
|
||||
|
||||
if (!context)
|
||||
fatalerror("Failed to allocate memory for new context: %s\n", strerror(errno));
|
||||
fileInfo->parent = contextStack->fileInfo;
|
||||
fileInfo->lineNo = 0; /* Init to a default value, see struct definition for info */
|
||||
fileInfo->referenced = false;
|
||||
fileInfo->lineNo = lexer_GetLineNo();
|
||||
context->fileInfo = fileInfo;
|
||||
/*
|
||||
* Link new entry to its parent so it's reachable later
|
||||
* ERRORS SHOULD NOT OCCUR AFTER THIS!!
|
||||
*/
|
||||
context->parent = contextStack;
|
||||
contextStack = context;
|
||||
|
||||
contextStack->lineNo = lexer_GetLineNo();
|
||||
/* Link new entry to its parent so it's reachable later */
|
||||
contextStack->child->parent = contextStack;
|
||||
contextStack = contextStack->child;
|
||||
|
||||
contextStack->child = NULL;
|
||||
contextStack->reptDepth = reptDepth;
|
||||
}
|
||||
|
||||
void fstk_RunInclude(char const *path)
|
||||
@@ -212,21 +292,27 @@ void fstk_RunInclude(char const *path)
|
||||
}
|
||||
dbgPrint("Full path: \"%s\"\n", fullPath);
|
||||
|
||||
newContext(0);
|
||||
contextStack->lexerState = lexer_OpenFile(fullPath);
|
||||
struct FileStackNamedNode *fileInfo = malloc(sizeof(*fileInfo) + size);
|
||||
|
||||
if (!fileInfo) {
|
||||
error("Failed to alloc file info for INCLUDE: %s\n", strerror(errno));
|
||||
return;
|
||||
}
|
||||
fileInfo->node.type = NODE_FILE;
|
||||
strcpy(fileInfo->name, fullPath);
|
||||
free(fullPath);
|
||||
|
||||
newContext((struct FileStackNode *)fileInfo);
|
||||
contextStack->lexerState = lexer_OpenFile(fileInfo->name);
|
||||
if (!contextStack->lexerState)
|
||||
fatalerror("Failed to set up lexer for file include\n");
|
||||
lexer_SetStateAtEOL(contextStack->lexerState);
|
||||
/* We're back at top-level, so most things are reset */
|
||||
contextStack->uniqueID = 0;
|
||||
macro_SetUniqueID(0);
|
||||
contextStack->fileName = fullPath;
|
||||
contextStack->fileNameBuf = fullPath;
|
||||
contextStack->macro = NULL;
|
||||
contextStack->nbReptIters = 0;
|
||||
}
|
||||
|
||||
void fstk_RunMacro(char *macroName, struct MacroArgs *args)
|
||||
void fstk_RunMacro(char const *macroName, struct MacroArgs *args)
|
||||
{
|
||||
dbgPrint("Running macro \"%s\"\n", macroName);
|
||||
|
||||
@@ -242,7 +328,53 @@ void fstk_RunMacro(char *macroName, struct MacroArgs *args)
|
||||
}
|
||||
contextStack->macroArgs = macro_GetCurrentArgs();
|
||||
|
||||
newContext(0);
|
||||
/* Compute total length of this node's name: <base name>::<macro> */
|
||||
size_t reptNameLen = 0;
|
||||
struct FileStackNode const *node = macro->src;
|
||||
|
||||
if (node->type == NODE_REPT) {
|
||||
struct FileStackReptNode const *reptNode = (struct FileStackReptNode const *)node;
|
||||
|
||||
/* 4294967295 = 2^32 - 1, aka UINT32_MAX */
|
||||
reptNameLen += reptNode->reptDepth * strlen("::REPT~4294967295");
|
||||
/* Look for next named node */
|
||||
do {
|
||||
node = node->parent;
|
||||
} while (node->type == NODE_REPT);
|
||||
}
|
||||
struct FileStackNamedNode const *baseNode = (struct FileStackNamedNode const *)node;
|
||||
size_t baseLen = strlen(baseNode->name);
|
||||
size_t macroNameLen = strlen(macro->name);
|
||||
struct FileStackNamedNode *fileInfo = malloc(sizeof(*fileInfo) + baseLen
|
||||
+ reptNameLen + 2 + macroNameLen + 1);
|
||||
|
||||
if (!fileInfo) {
|
||||
error("Failed to alloc file info for \"%s\": %s\n", macro->name, strerror(errno));
|
||||
return;
|
||||
}
|
||||
fileInfo->node.type = NODE_MACRO;
|
||||
/* Print the name... */
|
||||
char *dest = fileInfo->name;
|
||||
|
||||
memcpy(dest, baseNode->name, baseLen);
|
||||
dest += baseLen;
|
||||
if (node->type == NODE_REPT) {
|
||||
struct FileStackReptNode const *reptNode = (struct FileStackReptNode const *)node;
|
||||
|
||||
for (uint32_t i = reptNode->reptDepth; i--; ) {
|
||||
int nbChars = sprintf(dest, "::REPT~%" PRIu32, reptNode->iters[i]);
|
||||
|
||||
if (nbChars < 0)
|
||||
fatalerror("Failed to write macro invocation info: %s\n",
|
||||
strerror(errno));
|
||||
dest += nbChars;
|
||||
}
|
||||
}
|
||||
*dest++ = ':';
|
||||
*dest++ = ':';
|
||||
memcpy(dest, macro->name, macroNameLen + 1);
|
||||
|
||||
newContext((struct FileStackNode *)fileInfo);
|
||||
/* Line minus 1 because buffer begins with a newline */
|
||||
contextStack->lexerState = lexer_OpenFileView(macro->macro, macro->macroSize,
|
||||
macro->fileLine - 1);
|
||||
@@ -250,143 +382,93 @@ void fstk_RunMacro(char *macroName, struct MacroArgs *args)
|
||||
fatalerror("Failed to set up lexer for macro invocation\n");
|
||||
lexer_SetStateAtEOL(contextStack->lexerState);
|
||||
contextStack->uniqueID = macro_UseNewUniqueID();
|
||||
contextStack->fileName = macro->fileName;
|
||||
contextStack->fileNameBuf = NULL;
|
||||
contextStack->macro = macro;
|
||||
contextStack->nbReptIters = 0;
|
||||
macro_UseNewArgs(args);
|
||||
}
|
||||
|
||||
void fstk_RunRept(uint32_t count, int32_t nReptLineNo, char *body, size_t size)
|
||||
void fstk_RunRept(uint32_t count, int32_t reptLineNo, char *body, size_t size)
|
||||
{
|
||||
dbgPrint("Running REPT(%" PRIu32 ")\n", count);
|
||||
|
||||
if (count == 0)
|
||||
return;
|
||||
uint32_t reptDepth = contextStack->reptDepth;
|
||||
|
||||
newContext(reptDepth + 1);
|
||||
contextStack->lexerState = lexer_OpenFileView(body, size, nReptLineNo);
|
||||
uint32_t reptDepth = contextStack->fileInfo->type == NODE_REPT
|
||||
? ((struct FileStackReptNode *)contextStack->fileInfo)->reptDepth
|
||||
: 0;
|
||||
struct FileStackReptNode *fileInfo = malloc(sizeof(*fileInfo)
|
||||
+ (reptDepth + 1) * sizeof(fileInfo->iters[0]));
|
||||
|
||||
if (!fileInfo) {
|
||||
error("Failed to alloc file info for REPT: %s\n", strerror(errno));
|
||||
return;
|
||||
}
|
||||
fileInfo->node.type = NODE_REPT;
|
||||
fileInfo->reptDepth = reptDepth + 1;
|
||||
fileInfo->iters[0] = 1;
|
||||
if (reptDepth)
|
||||
/* Copy all parent iter counts */
|
||||
memcpy(&fileInfo->iters[1],
|
||||
((struct FileStackReptNode *)contextStack->fileInfo)->iters,
|
||||
reptDepth * sizeof(fileInfo->iters[0]));
|
||||
|
||||
newContext((struct FileStackNode *)fileInfo);
|
||||
/* Correct our line number, which currently points to the `ENDR` line */
|
||||
contextStack->fileInfo->lineNo = reptLineNo;
|
||||
|
||||
contextStack->lexerState = lexer_OpenFileView(body, size, reptLineNo);
|
||||
if (!contextStack->lexerState)
|
||||
fatalerror("Failed to set up lexer for macro invocation\n");
|
||||
fatalerror("Failed to set up lexer for rept block\n");
|
||||
lexer_SetStateAtEOL(contextStack->lexerState);
|
||||
contextStack->uniqueID = macro_UseNewUniqueID();
|
||||
contextStack->fileName = contextStack->parent->fileName;
|
||||
contextStack->fileNameBuf = NULL;
|
||||
contextStack->macro = contextStack->parent->macro; /* Inherit */
|
||||
contextStack->nbReptIters = count;
|
||||
/* Copy all of parent's iters, and add ours */
|
||||
if (reptDepth)
|
||||
memcpy(contextStack->reptIters, contextStack->parent->reptIters,
|
||||
sizeof(contextStack->reptIters[0]) * reptDepth);
|
||||
contextStack->reptIters[reptDepth] = 1;
|
||||
|
||||
/* Correct our parent's line number, which currently points to the `ENDR` line */
|
||||
contextStack->parent->lineNo = nReptLineNo;
|
||||
}
|
||||
|
||||
static void printContext(FILE *stream, struct Context const *context)
|
||||
void fstk_Init(char const *mainPath, size_t maxRecursionDepth)
|
||||
{
|
||||
fprintf(stream, "%s", context->fileName);
|
||||
if (context->macro)
|
||||
fprintf(stream, "::%s", context->macro->name);
|
||||
for (size_t i = 0; i < context->reptDepth; i++)
|
||||
fprintf(stream, "::REPT~%" PRIu32, context->reptIters[i]);
|
||||
fprintf(stream, "(%" PRId32 ")", context->lineNo);
|
||||
}
|
||||
struct LexerState *state = lexer_OpenFile(mainPath);
|
||||
|
||||
static void dumpToStream(FILE *stream)
|
||||
{
|
||||
struct Context *context = topLevelContext;
|
||||
|
||||
while (context != contextStack) {
|
||||
printContext(stream, context);
|
||||
fprintf(stream, " -> ");
|
||||
context = context->child;
|
||||
}
|
||||
contextStack->lineNo = lexer_GetLineNo();
|
||||
printContext(stream, contextStack);
|
||||
}
|
||||
|
||||
void fstk_Dump(void)
|
||||
{
|
||||
dumpToStream(stderr);
|
||||
}
|
||||
|
||||
char *fstk_DumpToStr(void)
|
||||
{
|
||||
char *str;
|
||||
size_t size;
|
||||
/* `open_memstream` is specified to always include a '\0' at the end of the buffer! */
|
||||
FILE *stream = open_memstream(&str, &size);
|
||||
|
||||
if (!stream)
|
||||
fatalerror("Failed to dump file stack to string: %s\n", strerror(errno));
|
||||
dumpToStream(stream);
|
||||
fclose(stream);
|
||||
return str;
|
||||
}
|
||||
|
||||
char const *fstk_GetFileName(void)
|
||||
{
|
||||
/* FIXME: this is awful, but all callees copy the buffer anyways */
|
||||
static char fileName[_MAX_PATH + 1];
|
||||
size_t remainingChars = _MAX_PATH + 1;
|
||||
char *dest = fileName;
|
||||
char const *src = contextStack->fileName;
|
||||
|
||||
#define append(...) do { \
|
||||
int nbChars = snprintf(dest, remainingChars, __VA_ARGS__); \
|
||||
\
|
||||
if (nbChars >= remainingChars) \
|
||||
fatalerror("File stack entry too large\n"); \
|
||||
remainingChars -= nbChars; \
|
||||
dest += nbChars; \
|
||||
} while (0)
|
||||
|
||||
while (*src && --remainingChars) /* Leave room for terminator */
|
||||
*dest++ = *src++;
|
||||
if (remainingChars && contextStack->macro)
|
||||
append("::%s", contextStack->macro->name);
|
||||
for (size_t i = 0; i < contextStack->reptDepth; i++)
|
||||
append("::REPT~%" PRIu32, contextStack->reptIters[i]);
|
||||
|
||||
*dest = '\0';
|
||||
return fileName;
|
||||
}
|
||||
|
||||
uint32_t fstk_GetLine(void)
|
||||
{
|
||||
return lexer_GetLineNo();
|
||||
}
|
||||
|
||||
void fstk_Init(char *mainPath, size_t maxRecursionDepth)
|
||||
{
|
||||
topLevelContext = malloc(sizeof(*topLevelContext));
|
||||
if (!topLevelContext)
|
||||
fatalerror("Failed to allocate memory for initial context: %s\n", strerror(errno));
|
||||
topLevelContext->parent = NULL;
|
||||
topLevelContext->child = NULL;
|
||||
topLevelContext->lexerState = lexer_OpenFile(mainPath);
|
||||
if (!topLevelContext->lexerState)
|
||||
if (!state)
|
||||
fatalerror("Failed to open main file!\n");
|
||||
lexer_SetState(topLevelContext->lexerState);
|
||||
topLevelContext->uniqueID = 0;
|
||||
lexer_SetState(state);
|
||||
char const *fileName = lexer_GetFileName();
|
||||
size_t len = strlen(fileName);
|
||||
struct Context *context = malloc(sizeof(*contextStack));
|
||||
struct FileStackNamedNode *fileInfo = malloc(sizeof(*fileInfo) + len + 1);
|
||||
|
||||
if (!context)
|
||||
fatalerror("Failed to allocate memory for main context: %s\n", strerror(errno));
|
||||
if (!fileInfo)
|
||||
fatalerror("Failed to allocate memory for main file info: %s\n", strerror(errno));
|
||||
|
||||
context->fileInfo = (struct FileStackNode *)fileInfo;
|
||||
/* lineNo and reptIter are unused on the top-level context */
|
||||
context->fileInfo->parent = NULL;
|
||||
context->fileInfo->referenced = false;
|
||||
context->fileInfo->type = NODE_FILE;
|
||||
memcpy(fileInfo->name, fileName, len + 1);
|
||||
|
||||
context->parent = NULL;
|
||||
context->lexerState = state;
|
||||
context->uniqueID = 0;
|
||||
macro_SetUniqueID(0);
|
||||
topLevelContext->fileName = lexer_GetFileName();
|
||||
topLevelContext->fileNameBuf = NULL;
|
||||
topLevelContext->macro = NULL;
|
||||
topLevelContext->nbReptIters = 0;
|
||||
topLevelContext->reptDepth = 0;
|
||||
context->nbReptIters = 0;
|
||||
|
||||
contextStack = topLevelContext;
|
||||
/* Now that it's set up properly, register the context */
|
||||
contextStack = context;
|
||||
|
||||
if (maxRecursionDepth
|
||||
> (SIZE_MAX - sizeof(*contextStack)) / sizeof(contextStack->reptIters[0])) {
|
||||
error("Recursion depth may not be higher than %zu, defaulting to 64\n",
|
||||
(SIZE_MAX - sizeof(*contextStack)) / sizeof(contextStack->reptIters[0]));
|
||||
nMaxRecursionDepth = 64;
|
||||
/*
|
||||
* Check that max recursion depth won't allow overflowing node `malloc`s
|
||||
* This assumes that the rept node is larger
|
||||
*/
|
||||
#define DEPTH_LIMIT ((SIZE_MAX - sizeof(struct FileStackReptNode)) / sizeof(uint32_t))
|
||||
if (maxRecursionDepth > DEPTH_LIMIT) {
|
||||
error("Recursion depth may not be higher than %zu, defaulting to "
|
||||
EXPAND_AND_STR(DEFAULT_MAX_DEPTH) "\n", DEPTH_LIMIT);
|
||||
nMaxRecursionDepth = DEFAULT_MAX_DEPTH;
|
||||
} else {
|
||||
nMaxRecursionDepth = maxRecursionDepth;
|
||||
}
|
||||
/* Make sure that the default of 64 is OK, though */
|
||||
assert(DEPTH_LIMIT >= DEFAULT_MAX_DEPTH);
|
||||
#undef DEPTH_LIMIT
|
||||
}
|
||||
|
||||
131
src/asm/output.c
131
src/asm/output.c
@@ -12,6 +12,7 @@
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
@@ -33,7 +34,8 @@
|
||||
#include "platform.h" // strdup
|
||||
|
||||
struct Patch {
|
||||
char *tzFilename;
|
||||
struct FileStackNode const *src;
|
||||
uint32_t lineNo;
|
||||
uint32_t nOffset;
|
||||
struct Section *pcSection;
|
||||
uint32_t pcOffset;
|
||||
@@ -62,19 +64,17 @@ static uint32_t nbSymbols = 0; /* Length of the above list */
|
||||
|
||||
static struct Assertion *assertions = NULL;
|
||||
|
||||
static struct FileStackNode *fileStackNodes = NULL;
|
||||
|
||||
/*
|
||||
* Count the number of sections used in this object
|
||||
*/
|
||||
static uint32_t countsections(void)
|
||||
{
|
||||
struct Section *sect;
|
||||
uint32_t count = 0;
|
||||
|
||||
sect = pSectionList;
|
||||
while (sect) {
|
||||
for (struct Section const *sect = pSectionList; sect; sect = sect->next)
|
||||
count++;
|
||||
sect = sect->next;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
@@ -129,16 +129,60 @@ static void fputstring(char const *s, FILE *f)
|
||||
fputc(0, f);
|
||||
}
|
||||
|
||||
static uint32_t getNbFileStackNodes(void)
|
||||
{
|
||||
return fileStackNodes ? fileStackNodes->ID + 1 : 0;
|
||||
}
|
||||
|
||||
void out_RegisterNode(struct FileStackNode *node)
|
||||
{
|
||||
/* If node is not already registered, register it (and parents), and give it a unique ID */
|
||||
while (node->ID == -1) {
|
||||
node->ID = getNbFileStackNodes();
|
||||
if (node->ID == -1)
|
||||
fatalerror("Reached too many file stack nodes; try splitting the file up\n");
|
||||
node->next = fileStackNodes;
|
||||
fileStackNodes = node;
|
||||
|
||||
/* Also register the node's parents */
|
||||
node = node->parent;
|
||||
if (!node)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void out_ReplaceNode(struct FileStackNode *node)
|
||||
{
|
||||
(void)node;
|
||||
#if 0
|
||||
This is code intended to replace a node, which is pretty useless until ref counting is added...
|
||||
|
||||
struct FileStackNode **ptr = &fileStackNodes;
|
||||
|
||||
/*
|
||||
* The linked list is supposed to have decrementing IDs, so iterate with less memory reads,
|
||||
* to hopefully hit the cache less. A debug check is added after, in case a change is made
|
||||
* that breaks this assumption.
|
||||
*/
|
||||
for (uint32_t i = fileStackNodes->ID; i != node->ID; i--)
|
||||
ptr = &(*ptr)->next;
|
||||
assert((*ptr)->ID == node->ID);
|
||||
|
||||
node->next = (*ptr)->next;
|
||||
assert(!node->next || node->next->ID == node->ID - 1); /* Catch inconsistencies early */
|
||||
/* TODO: unreference the node */
|
||||
*ptr = node;
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* Return a section's ID
|
||||
*/
|
||||
static uint32_t getsectid(struct Section const *sect)
|
||||
{
|
||||
struct Section const *sec;
|
||||
struct Section const *sec = pSectionList;
|
||||
uint32_t ID = 0;
|
||||
|
||||
sec = pSectionList;
|
||||
|
||||
while (sec) {
|
||||
if (sec == sect)
|
||||
return ID;
|
||||
@@ -159,7 +203,10 @@ static uint32_t getSectIDIfAny(struct Section const *sect)
|
||||
*/
|
||||
static void writepatch(struct Patch const *patch, FILE *f)
|
||||
{
|
||||
fputstring(patch->tzFilename, f);
|
||||
assert(patch->src->ID != -1);
|
||||
|
||||
fputlong(patch->src->ID, f);
|
||||
fputlong(patch->lineNo, f);
|
||||
fputlong(patch->nOffset, f);
|
||||
fputlong(getSectIDIfAny(patch->pcSection), f);
|
||||
fputlong(patch->pcOffset, f);
|
||||
@@ -206,26 +253,35 @@ static void writesymbol(struct Symbol const *sym, FILE *f)
|
||||
if (!sym_IsDefined(sym)) {
|
||||
fputc(SYMTYPE_IMPORT, f);
|
||||
} else {
|
||||
assert(sym->src->ID != -1);
|
||||
|
||||
fputc(sym->isExported ? SYMTYPE_EXPORT : SYMTYPE_LOCAL, f);
|
||||
fputstring(sym->fileName, f);
|
||||
fputlong(sym->src->ID, f);
|
||||
fputlong(sym->fileLine, f);
|
||||
fputlong(getSectIDIfAny(sym_GetSection(sym)), f);
|
||||
fputlong(sym->value, f);
|
||||
}
|
||||
}
|
||||
|
||||
static void registerSymbol(struct Symbol *sym)
|
||||
{
|
||||
*objectSymbolsTail = sym;
|
||||
objectSymbolsTail = &sym->next;
|
||||
out_RegisterNode(sym->src);
|
||||
if (nbSymbols == -1)
|
||||
fatalerror("Registered too many symbols (%" PRIu32
|
||||
"); try splitting up your files\n", (uint32_t)-1);
|
||||
sym->ID = nbSymbols++;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns a symbol's ID within the object file
|
||||
* If the symbol does not have one, one is assigned by registering the symbol
|
||||
*/
|
||||
static uint32_t getSymbolID(struct Symbol *sym)
|
||||
{
|
||||
if (sym->ID == -1) {
|
||||
sym->ID = nbSymbols++;
|
||||
|
||||
*objectSymbolsTail = sym;
|
||||
objectSymbolsTail = &sym->next;
|
||||
}
|
||||
if (sym->ID == -1 && !sym_IsPC(sym))
|
||||
registerSymbol(sym);
|
||||
return sym->ID;
|
||||
}
|
||||
|
||||
@@ -303,22 +359,25 @@ static void writerpn(uint8_t *rpnexpr, uint32_t *rpnptr, uint8_t *rpn,
|
||||
|
||||
/*
|
||||
* Allocate a new patch structure and link it into the list
|
||||
* WARNING: all patches are assumed to eventually be written, so the file stack node is registered
|
||||
*/
|
||||
static struct Patch *allocpatch(uint32_t type, struct Expression const *expr,
|
||||
uint32_t ofs)
|
||||
static struct Patch *allocpatch(uint32_t type, struct Expression const *expr, uint32_t ofs)
|
||||
{
|
||||
struct Patch *patch = malloc(sizeof(struct Patch));
|
||||
uint32_t rpnSize = expr->isKnown ? 5 : expr->nRPNPatchSize;
|
||||
struct FileStackNode *node = fstk_GetFileStack();
|
||||
|
||||
if (!patch)
|
||||
fatalerror("No memory for patch: %s\n", strerror(errno));
|
||||
patch->pRPN = malloc(sizeof(*patch->pRPN) * rpnSize);
|
||||
|
||||
patch->pRPN = malloc(sizeof(*patch->pRPN) * rpnSize);
|
||||
if (!patch->pRPN)
|
||||
fatalerror("No memory for patch's RPN expression: %s\n", strerror(errno));
|
||||
|
||||
patch->type = type;
|
||||
patch->tzFilename = fstk_DumpToStr();
|
||||
patch->src = node;
|
||||
out_RegisterNode(node);
|
||||
patch->lineNo = lexer_GetLineNo();
|
||||
patch->nOffset = ofs;
|
||||
patch->pcSection = sect_GetSymbolSection();
|
||||
patch->pcOffset = sect_GetSymbolOffset();
|
||||
@@ -382,13 +441,28 @@ static void writeassert(struct Assertion *assert, FILE *f)
|
||||
fputstring(assert->message, f);
|
||||
}
|
||||
|
||||
static void writeFileStackNode(struct FileStackNode const *node, FILE *f)
|
||||
{
|
||||
fputlong(node->parent ? node->parent->ID : -1, f);
|
||||
fputlong(node->lineNo, f);
|
||||
fputc(node->type, f);
|
||||
if (node->type != NODE_REPT) {
|
||||
fputstring(((struct FileStackNamedNode const *)node)->name, f);
|
||||
} else {
|
||||
struct FileStackReptNode const *reptNode = (struct FileStackReptNode const *)node;
|
||||
|
||||
fputlong(reptNode->reptDepth, f);
|
||||
/* Iters are stored by decreasing depth, so reverse the order for output */
|
||||
for (uint32_t i = reptNode->reptDepth; i--; )
|
||||
fputlong(reptNode->iters[i], f);
|
||||
}
|
||||
}
|
||||
|
||||
static void registerExportedSymbol(struct Symbol *symbol, void *arg)
|
||||
{
|
||||
(void)arg;
|
||||
if (sym_IsExported(symbol) && symbol->ID == -1) {
|
||||
*objectSymbolsTail = symbol;
|
||||
objectSymbolsTail = &symbol->next;
|
||||
nbSymbols++;
|
||||
registerSymbol(symbol);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -411,6 +485,15 @@ void out_WriteObject(void)
|
||||
fputlong(nbSymbols, f);
|
||||
fputlong(countsections(), f);
|
||||
|
||||
fputlong(getNbFileStackNodes(), f);
|
||||
for (struct FileStackNode const *node = fileStackNodes; node; node = node->next) {
|
||||
writeFileStackNode(node, f);
|
||||
if (node->next && node->next->ID != node->ID - 1)
|
||||
fatalerror("Internal error: fstack node #%" PRIu32 " follows #%" PRIu32
|
||||
". Please report this to the developers!\n",
|
||||
node->next->ID, node->ID);
|
||||
}
|
||||
|
||||
for (struct Symbol const *sym = objectSymbols; sym; sym = sym->next)
|
||||
writesymbol(sym, f);
|
||||
|
||||
|
||||
@@ -258,8 +258,8 @@ static int32_t shift(int32_t shiftee, int32_t amount)
|
||||
if (amount >= 0) {
|
||||
// Left shift
|
||||
if (amount >= 32) {
|
||||
warning(WARNING_SHIFT_AMOUNT, "Shifting left by large amount %" PRId32 "\n",
|
||||
amount);
|
||||
warning(WARNING_SHIFT_AMOUNT, "Shifting left by large amount %"
|
||||
PRId32 "\n", amount);
|
||||
return 0;
|
||||
|
||||
} else {
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include "asm/macro.h"
|
||||
#include "asm/main.h"
|
||||
#include "asm/mymath.h"
|
||||
#include "asm/output.h"
|
||||
#include "asm/section.h"
|
||||
#include "asm/symbol.h"
|
||||
#include "asm/util.h"
|
||||
@@ -121,7 +122,7 @@ static char const *Callback__FILE__(void)
|
||||
buf[j - 1] = '\\';
|
||||
buf[j] = fileName[i];
|
||||
}
|
||||
/* Write everything after the loop, to ensure everything has been allocated */
|
||||
/* Write everything after the loop, to ensure the buffer has been allocated */
|
||||
buf[0] = '"';
|
||||
buf[j++] = '"';
|
||||
buf[j] = '\0';
|
||||
@@ -150,15 +151,35 @@ int32_t sym_GetValue(struct Symbol const *sym)
|
||||
return sym->value;
|
||||
}
|
||||
|
||||
static void dumpFilename(struct Symbol const *sym)
|
||||
{
|
||||
if (!sym->src)
|
||||
fputs("<builtin>", stderr);
|
||||
else
|
||||
fstk_Dump(sym->src, sym->fileLine);
|
||||
}
|
||||
|
||||
/*
|
||||
* Set a symbol's definition filename and line
|
||||
*/
|
||||
static void setSymbolFilename(struct Symbol *sym)
|
||||
{
|
||||
sym->src = fstk_GetFileStack();
|
||||
sym->fileLine = lexer_GetLineNo();
|
||||
}
|
||||
|
||||
/*
|
||||
* Update a symbol's definition filename and line
|
||||
*/
|
||||
static void updateSymbolFilename(struct Symbol *sym)
|
||||
{
|
||||
if (snprintf(sym->fileName, _MAX_PATH + 1, "%s",
|
||||
fstk_GetFileName()) > _MAX_PATH)
|
||||
fatalerror("%s: File name is too long: '%s'\n", __func__, fstk_GetFileName());
|
||||
sym->fileLine = fstk_GetLine();
|
||||
struct FileStackNode *oldSrc = sym->src;
|
||||
|
||||
setSymbolFilename(sym);
|
||||
/* If the old node was referenced, ensure the new one is */
|
||||
if (oldSrc->referenced && oldSrc->ID != -1)
|
||||
out_RegisterNode(sym->src);
|
||||
/* TODO: unref the old node, and use `out_ReplaceNode` instead if deleting it */
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -178,7 +199,7 @@ static struct Symbol *createsymbol(char const *s)
|
||||
symbol->isBuiltin = false;
|
||||
symbol->hasCallback = false;
|
||||
symbol->section = NULL;
|
||||
updateSymbolFilename(symbol);
|
||||
setSymbolFilename(symbol);
|
||||
symbol->ID = -1;
|
||||
symbol->next = NULL;
|
||||
|
||||
@@ -253,6 +274,7 @@ void sym_Purge(char const *symName)
|
||||
labelScope = NULL;
|
||||
|
||||
hash_RemoveElement(symbols, symbol->name);
|
||||
/* TODO: ideally, also unref the file stack nodes */
|
||||
free(symbol);
|
||||
}
|
||||
}
|
||||
@@ -338,9 +360,11 @@ static struct Symbol *createNonrelocSymbol(char const *symbolName)
|
||||
|
||||
if (!symbol)
|
||||
symbol = createsymbol(symbolName);
|
||||
else if (sym_IsDefined(symbol))
|
||||
error("'%s' already defined at %s(%" PRIu32 ")\n", symbolName,
|
||||
symbol->fileName, symbol->fileLine);
|
||||
else if (sym_IsDefined(symbol)) {
|
||||
error("'%s' already defined at ", symbolName);
|
||||
dumpFilename(symbol);
|
||||
putc('\n', stderr);
|
||||
}
|
||||
|
||||
return symbol;
|
||||
}
|
||||
@@ -395,15 +419,17 @@ struct Symbol *sym_AddSet(char const *symName, int32_t value)
|
||||
{
|
||||
struct Symbol *sym = findsymbol(symName, NULL);
|
||||
|
||||
if (sym == NULL)
|
||||
if (sym == NULL) {
|
||||
sym = createsymbol(symName);
|
||||
else if (sym_IsDefined(sym) && sym->type != SYM_SET)
|
||||
error("'%s' already defined as %s at %s(%" PRIu32 ")\n",
|
||||
symName, sym->type == SYM_LABEL ? "label" : "constant",
|
||||
sym->fileName, sym->fileLine);
|
||||
else
|
||||
/* TODO: can the scope be incorrect when talking over refs? */
|
||||
} else if (sym_IsDefined(sym) && sym->type != SYM_SET) {
|
||||
error("'%s' already defined as %s at ",
|
||||
symName, sym->type == SYM_LABEL ? "label" : "constant");
|
||||
dumpFilename(sym);
|
||||
putc('\n', stderr);
|
||||
} else {
|
||||
/* TODO: can the scope be incorrect when taking over refs? */
|
||||
updateSymbolFilename(sym);
|
||||
}
|
||||
|
||||
sym->type = SYM_SET;
|
||||
sym->value = value;
|
||||
@@ -424,9 +450,12 @@ static struct Symbol *addLabel(char const *name)
|
||||
if (!sym) {
|
||||
sym = createsymbol(name);
|
||||
} else if (sym_IsDefined(sym)) {
|
||||
error("'%s' already defined in %s(%" PRIu32 ")\n",
|
||||
name, sym->fileName, sym->fileLine);
|
||||
error("'%s' already defined at ", name);
|
||||
dumpFilename(sym);
|
||||
putc('\n', stderr);
|
||||
return NULL;
|
||||
} else {
|
||||
updateSymbolFilename(sym);
|
||||
}
|
||||
/* If the symbol already exists as a ref, just "take over" it */
|
||||
sym->type = SYM_LABEL;
|
||||
@@ -434,7 +463,6 @@ static struct Symbol *addLabel(char const *name)
|
||||
if (exportall)
|
||||
sym->isExported = true;
|
||||
sym->section = sect_GetSymbolSection();
|
||||
updateSymbolFilename(sym);
|
||||
|
||||
if (sym && !sym->section)
|
||||
error("Label \"%s\" created outside of a SECTION\n", name);
|
||||
@@ -517,7 +545,7 @@ struct Symbol *sym_AddMacro(char const *symName, int32_t defLineNo, char *body,
|
||||
sym->type = SYM_MACRO;
|
||||
sym->macroSize = size;
|
||||
sym->macro = body;
|
||||
updateSymbolFilename(sym);
|
||||
setSymbolFilename(sym); /* TODO: is this really necessary? */
|
||||
/*
|
||||
* The symbol is created at the line after the `endm`,
|
||||
* override this with the actual definition line
|
||||
@@ -577,10 +605,11 @@ static inline struct Symbol *createBuiltinSymbol(char const *name)
|
||||
|
||||
sym->isBuiltin = true;
|
||||
sym->hasCallback = true;
|
||||
strcpy(sym->fileName, "<builtin>");
|
||||
sym->src = NULL;
|
||||
sym->fileLine = 0;
|
||||
return sym;
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize the symboltable
|
||||
*/
|
||||
|
||||
@@ -202,7 +202,7 @@ void printDiag(const char *fmt, va_list args, char const *type,
|
||||
char const *flagfmt, char const *flag)
|
||||
{
|
||||
fputs(type, stderr);
|
||||
fstk_Dump();
|
||||
fstk_DumpCurrent();
|
||||
fprintf(stderr, flagfmt, flag);
|
||||
vfprintf(stderr, fmt, args);
|
||||
lexer_DumpStringExpansions();
|
||||
|
||||
Reference in New Issue
Block a user