Sections are now top-level nodes (which contain TLCs)

Previously, `@section` was a statement inside a top-level chunk, which
caused issues when the TLC allocated stack memory (the prologue was
generated before the `@section`).

This way, Nectar source code is now defined as a list of sections,
first and foremost.
This commit is contained in:
Mid 2025-11-12 12:29:15 +02:00
parent 7a8b14308b
commit f406b2a032
7 changed files with 132 additions and 80 deletions

View File

@ -49,7 +49,11 @@ void generic_visitor(AST **nptr, AST *stmt, AST *stmtPrev, AST *chu, AST *tlc, v
} else if(n->nodeKind == AST_STMT_EXPR) {
generic_visitor(&n->stmtExpr.expr, stmt, stmtPrev, chu, tlc, ud, preHandler, postHandler);
} else if(n->nodeKind == AST_STMT_EXT_ORG) {
} else if(n->nodeKind == AST_STMT_EXT_SECTION) {
} else if(n->nodeKind == AST_SECTION) {
generic_visitor(&n->section.tlc, stmt, stmtPrev, n->section.tlc, n->section.tlc, ud, preHandler, postHandler);
if(n->section.next) {
generic_visitor(&n->section.next, stmt, stmtPrev, chu, tlc, ud, preHandler, postHandler);
}
} else if(n->nodeKind == AST_STMT_RETURN) {
if(n->stmtReturn.val) {
generic_visitor(&n->stmtReturn.val, stmt, stmtPrev, chu, tlc, ud, preHandler, postHandler);

View File

@ -38,7 +38,7 @@
K(AST_EXPR_ARRAY) \
K(AST_EXPR_FUNC) \
K(AST_STMT_EXT_ORG) \
K(AST_STMT_EXT_SECTION) \
K(AST_SECTION) \
K(AST_STMT_RETURN) \
K(AST_EXPR_EXT_SALLOC) \
K(AST_EXPR_DOT) \
@ -204,7 +204,17 @@ typedef struct {
union AST *expression;
} ASTStmtDecl;
typedef struct {
struct ASTChunk;
typedef struct ASTSection {
ASTBase;
Token name;
struct ASTChunk *tlc;
struct ASTSection *next;
} ASTSection;
typedef struct ASTChunk {
ASTBase;
/* Flattened variable array for global register allocation */
@ -300,12 +310,6 @@ typedef struct {
size_t val;
} ASTStmtExtOrg;
typedef struct {
ASTStmt;
Token name;
} ASTStmtExtSection;
typedef struct {
ASTStmt;
@ -364,7 +368,7 @@ typedef union AST {
ASTExprDot exprDot;
ASTExprExtSalloc exprExtSalloc;
ASTStmtExtOrg stmtExtOrg;
ASTStmtExtSection stmtExtSection;
ASTSection section;
ASTExprExtSizeOf exprExtSizeOf;
ASTStmtJump stmtJump;
ASTStmtLabel stmtLabel;

View File

@ -86,28 +86,30 @@ int main(int argc_, char **argv_) {
if(in) fclose(f);
AST *chunk = nct_parse(tokens);
AST *sects = nct_parse(tokens);
free(tokens);
if(ntc_get_int("pdbg")) {
char *astdump = ast_dump(chunk);
char *astdump = ast_dump(sects);
fprintf(stderr, "### ORIGINAL ###\n%s\n", astdump);
free(astdump);
}
ast_segmented_dereference(chunk);
ast_secondclass_record(chunk);
ast_sroa(chunk);
ast_segmented_dereference(sects);
ast_secondclass_record(sects);
ast_sroa(sects);
ast_linearize(chunk);
ast_linearize(sects);
arch_normalize_pre(chunk);
arch_normalize_pre(sects);
arch_normalize(chunk);
while(!cg_go(chunk)) {
arch_normalize(chunk);
arch_normalize(sects);
while(!cg_attempt_sections(sects)) {
arch_normalize(sects);
}
cg_go_sections(sects);
return 0;
}

View File

@ -18,6 +18,7 @@ bool arch_verify_target();
int arch_ptr_size();
void arch_normalize_pre(union AST *tlc);
void arch_normalize(union AST *tlc);
int cg_go(union AST *tlc);
int cg_attempt_sections(union AST *tlc);
void cg_go_sections(union AST *tlc);
#endif

View File

@ -1236,21 +1236,6 @@ static void nct_parse_statement(Parser *P) {
expect(P, TOKEN_SEMICOLON);
pushstat(P, ret);
return;
} else if(!strcmp(peek(P, 0).content, "@section")) {
ASTStmtExtSection *ret = alloc_node(P, sizeof(*ret));
ret->nodeKind = AST_STMT_EXT_SECTION;
ret->next = NULL;
get(P);
expect(P, TOKEN_PAREN_L);
ret->name = expect(P, TOKEN_STRING);
expect(P, TOKEN_PAREN_R);
expect(P, TOKEN_SEMICOLON);
pushstat(P, ret);
return;
} else if(!strcmp(peek(P, 0).content, "@instantiate")) {
@ -1516,9 +1501,12 @@ Type *nct_parse_record_definition(Parser *P) {
return tr;
}
static void skim_chunk(Parser *P, int isTopLevel) {
/* Find all symbol names and struct types ahead of time. Searches for colons as those can only mean symbol declarations */
/*
* Find all symbol names and struct types ahead of time.
* Searches for colons as they can only mean symbol declarations.
* This function abuses Nectar's grammar to work, so malformed source
* code can fuck it up. */
static void skim_tokens(Parser *P, int isTopLevel) {
P->skimMode++;
{
intmax_t oldIdx = P->i;
@ -1606,7 +1594,7 @@ static void skim_chunk(Parser *P, int isTopLevel) {
free(path);
Parser subp = {.tokens = nct_lex(f), .scope = scope_new(NULL), .externalify = 1};
skim_chunk(&subp, 1);
skim_tokens(&subp, 1);
// Copy all extern symbols from the scope into our TLC's externs array
for(size_t i = 0; i < subp.scope->count; i++) {
@ -1650,8 +1638,6 @@ ASTChunk *nct_parse_chunk(Parser *P, int isTopLevel, int varPrioritize, Scope *t
P->topLevel = &ret->chunk;
}
skim_chunk(P, isTopLevel);
/* Arguments */
if(ft && isTopLevel) {
P->topLevel->functionType = ft;
@ -1676,7 +1662,7 @@ ASTChunk *nct_parse_chunk(Parser *P, int isTopLevel, int varPrioritize, Scope *t
arch_add_hidden_variables(P->scope);
while(peek(P, 0).type != TOKEN_EOF && peek(P, 0).type != TOKEN_SQUIGGLY_R) {
while(peek(P, 0).type != TOKEN_EOF && peek(P, 0).type != TOKEN_SQUIGGLY_R && !(peek(P, 0).type == TOKEN_IDENTIFIER && !strcmp(peek(P, 0).content, "@section"))) {
nct_parse_statement(P);
}
@ -1711,9 +1697,49 @@ ASTChunk *nct_parse_chunk(Parser *P, int isTopLevel, int varPrioritize, Scope *t
return &ret->chunk;
}
ASTSection *nct_parse_sections(Parser *P) {
ASTSection *start = alloc_node(P, sizeof(*start));
start->nodeKind = AST_SECTION;
ASTSection *current = start;
while(1) {
current->tlc = nct_parse_chunk(P, 1, 0, P->scope, NULL);
if(peek(P, 0).type == TOKEN_EOF) {
break;
}
if(!(peek(P, 0).type == TOKEN_IDENTIFIER && !strcmp(peek(P, 0).content, "@section"))) {
stahp_token(&P->tokens[P->i - 1], "Expected @section.");
}
ASTSection *next = alloc_node(P, sizeof(*next));
next->nodeKind = AST_SECTION;
get(P);
expect(P, TOKEN_PAREN_L);
next->name = expect(P, TOKEN_STRING);
expect(P, TOKEN_PAREN_R);
expect(P, TOKEN_SEMICOLON);
current->next = next;
current = next;
}
return start;
}
AST *nct_parse(Token *tokens) {
Scope *scope = scope_new(NULL);
Parser P;
memset(&P, 0, sizeof(P));
P.tokens = tokens;
return (AST*) nct_parse_chunk(&P, 1, 0, scope_new(NULL), NULL);
P.scope = scope;
skim_tokens(&P, 1);
return (AST*) nct_parse_sections(&P);
}

View File

@ -337,15 +337,6 @@ void cg_chunk(CGState *cg, AST *a) {
printf("jmp .%s\n", s->stmtJump.label);
}
} else if(s->nodeKind == AST_STMT_EXT_SECTION) {
Token t = s->stmtExtSection.name;
printf("section %.*s\n", (int) t.length, t.content);
} else if(s->nodeKind == AST_STMT_EXT_ORG) {
printf("org %lu\n", s->stmtExtOrg.val);
} else if(s->nodeKind == AST_STMT_EXT_ALIGN) {
uint32_t val = s->stmtExtAlign.val;
@ -401,13 +392,13 @@ void cg_chunk(CGState *cg, AST *a) {
ast_linearize(s->stmtDecl.expression->exprFunc.chunk);
arch_normalize_pre(s->stmtDecl.expression->exprFunc.chunk);
arch_normalize(s->stmtDecl.expression->exprFunc.chunk);
while(!cg_go(s->stmtDecl.expression->exprFunc.chunk)) {
while(!cg_attempt_sections(s->stmtDecl.expression->exprFunc.chunk)) {
arch_normalize(s->stmtDecl.expression->exprFunc.chunk);
}
cg_go_sections(s->stmtDecl.expression->exprFunc.chunk);
}
} else abort();
@ -800,7 +791,7 @@ nextColor:;
return mustSpillRegisterClass;
}
int cg_go(AST *a) {
int cg_tlc(AST *a) {
assert(a->nodeKind == AST_CHUNK);
for(size_t e = 0; e < a->chunk.externCount; e++) {
@ -810,6 +801,23 @@ int cg_go(AST *a) {
printf("extern %s\n", a->chunk.externs[e]->data.symbol.name);
}
struct CalleeSavedState calleeSaved = {};
if(a->chunk.functionType) {
callee_saved(a, &calleeSaved);
}
CGState cg;
memset(&cg, 0, sizeof(cg));
cg.tlc = a;
cg.isFunction = !!a->chunk.functionType;
cg.calleeSaved = calleeSaved;
cg_chunk(&cg, a);
return 1;
}
static int cg_attempt(AST *a) {
ast_usedef_reset(a);
size_t adjCount = 0;
@ -899,19 +907,36 @@ cont:;
return 0;
}
struct CalleeSavedState calleeSaved = {};
if(a->chunk.functionType) {
callee_saved(a, &calleeSaved);
return 1;
}
int cg_attempt_sections(AST *a) {
assert(a->nodeKind == AST_SECTION);
ASTSection *current = &a->section;
while(current) {
if(!cg_attempt(current->tlc)) {
return 0;
}
CGState cg;
memset(&cg, 0, sizeof(cg));
cg.tlc = a;
cg.isFunction = !!a->chunk.functionType;
cg.calleeSaved = calleeSaved;
cg_chunk(&cg, a);
current = current->next;
}
return 1;
}
void cg_go_sections(AST *a) {
assert(a->nodeKind == AST_SECTION);
ASTSection *current = &a->section;
while(current) {
if(current->name.content) {
printf("section %s\n", current->name.content);
}
cg_tlc(current->tlc);
current = current->next;
}
}

View File

@ -107,8 +107,6 @@ struct NormState {
static void normalize_visitor(AST **nptr, AST *stmt, AST *stmtPrev, AST *chu, AST *tlc, void *ud) {
struct NormState *this = ud;
if(this->targetTLC != tlc) return;
AST *s = *nptr;
if(s->nodeKind == AST_STMT_JUMP && s->stmtJump.condition) {
@ -477,8 +475,8 @@ static void normalize_visitor(AST **nptr, AST *stmt, AST *stmtPrev, AST *chu, AS
static void pre_norm_visitor(AST **nptr, AST *stmt, AST *stmtPrev, AST *chunk, AST *tlc, void *ud) {
AST *n = *nptr;
if(n == ud) {
if(tlc->chunk.functionType) {
if(n->nodeKind == AST_CHUNK) {
if(n->chunk.functionType) {
size_t argCount = n->chunk.functionType->function.argCount;
// First argCount vtes in list are the arguments
@ -533,8 +531,6 @@ static void pre_norm_visitor(AST **nptr, AST *stmt, AST *stmtPrev, AST *chunk, A
}
static void decompose_symbol_record_field_access(AST **nptr, AST *stmt, AST *stmtPrev, AST *chunk, AST *tlc, void *ud) {
if(tlc != (AST*) ud) return;
AST *n = *nptr;
if(n->nodeKind == AST_EXPR_DOT) {
@ -593,8 +589,6 @@ struct DenoopState {
static void denoop_visitor(AST **nptr, AST *stmt, AST *stmtPrev, AST *chunk, AST *tlc, void *ud) {
struct DenoopState *state = ud;
if(state->targetTLC != tlc) return;
AST *n = *nptr;
bool *success = &state->success;
@ -755,10 +749,6 @@ void ast_denoop(AST *tlc, AST **node) {
* TODO: convert records to proper form also.
*/
static void convention_correctness_visitor(AST **nptr, AST *stmt, AST *stmtPrev, AST *chunk, AST *tlc, void *ud) {
if(tlc != ud) {
return;
}
AST *n = *nptr;
if(n->nodeKind == AST_EXPR_CALL) {