Skip to content

Commit 56a6de2

Browse files
committed
Global linkage WIP
1 parent 56b9788 commit 56a6de2

File tree

12 files changed

+132
-73
lines changed

12 files changed

+132
-73
lines changed

include/mcc/ast.h

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ typedef struct VariableDecl {
118118
const Type* type;
119119
StorageClass storage_class;
120120
SourceRange source_range;
121-
IdentifierInfo* name;
121+
IdentifierInfo* identifier;
122122
Expr* initializer; // An optional initializer
123123
} VariableDecl;
124124

@@ -229,12 +229,8 @@ typedef struct BlockItem {
229229
typedef struct TranslationUnit {
230230
uint32_t decl_count;
231231
Decl* decls;
232-
Scope* global_scope;
233232

234-
// Besides stored in the scopes, the identifiers of functions in a translation
235-
// unit are also refered to in a separate hash table. This is useful because
236-
// each named only map to one instance of a function no matter the scope
237-
HashMap functions;
233+
struct SymbolTable* symbol_table;
238234
} TranslationUnit;
239235

240236
StringView string_from_ast(const TranslationUnit* tu, Arena* permanent_arena);

src/frontend/ast_printer.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -148,8 +148,8 @@ static void format_var_decl(StringBuffer* output, const VariableDecl* decl,
148148
{
149149
string_buffer_printf(output, "%*sVariableDecl ", indent, "");
150150
format_source_range(output, decl->source_range);
151-
string_buffer_printf(output, " %.*s: ", (int)decl->name->name.size,
152-
decl->name->name.start);
151+
string_buffer_printf(output, " %.*s: ", (int)decl->identifier->name.size,
152+
decl->identifier->name.start);
153153
format_storage_class(output, decl->storage_class);
154154
string_buffer_append(output, str("int\n"));
155155
if (decl->initializer) { format_expr(output, decl->initializer, indent + 2); }

src/frontend/parser.c

Lines changed: 56 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,7 @@ typedef struct Parser {
2929

3030
struct ErrorVec errors;
3131

32-
struct Scope* global_scope;
33-
HashMap functions;
32+
SymbolTable* symbol_table;
3433
} Parser;
3534

3635
#pragma region source range operations
@@ -175,24 +174,24 @@ static Expr* parse_identifier_expr(Parser* parser, Scope* scope)
175174

176175
MCC_ASSERT(token.tag == TOKEN_IDENTIFIER);
177176

178-
const StringView identifier = str_from_token(parser->src, token);
177+
const StringView name = str_from_token(parser->src, token);
179178

180179
// TODO: handle typedef
181180
Expr* result = ARENA_ALLOC_OBJECT(parser->permanent_arena, Expr);
182181

183182
// If local variable does not exist
184-
const IdentifierInfo* variable = lookup_identifier(scope, identifier);
185-
if (!variable) {
183+
const IdentifierInfo* variable_identifier = lookup_identifier(scope, name);
184+
if (!variable_identifier) {
186185
const StringView error_msg = allocate_printf(
187186
parser->permanent_arena, "use of undeclared identifier '%.*s'",
188-
(int)identifier.size, identifier.start);
187+
(int)name.size, name.start);
189188
parse_error_at(parser, error_msg, token_source_range(token));
190189
}
191190

192191
// TODO: handle the case wher variable == nullptr
193192
*result = (Expr){.tag = EXPR_VARIABLE,
194193
.source_range = token_source_range(token),
195-
.variable = variable};
194+
.variable = variable_identifier};
196195
return result;
197196
}
198197

@@ -215,19 +214,18 @@ typedef enum Precedence {
215214
PREC_PRIMARY
216215
} Precedence;
217216

218-
static Expr* parse_expr(Parser* parser, struct Scope* scope);
219-
static Expr* parse_group(Parser* parser, struct Scope* scope);
220-
static Expr* parse_unary_op(Parser* parser, struct Scope* scope);
217+
static Expr* parse_expr(Parser* parser, Scope* scope);
218+
static Expr* parse_group(Parser* parser, Scope* scope);
219+
static Expr* parse_unary_op(Parser* parser, Scope* scope);
221220
static Expr* parse_binop_left(Parser* parser, Expr* lhs_expr,
222-
struct Scope* scope); // left associative
221+
Scope* scope); // left associative
223222
static Expr* parse_assignment(Parser* parser, Expr* lhs_expr,
224-
struct Scope* scope); // right associative
223+
Scope* scope); // right associative
225224
static Expr* parse_ternary(Parser* parser, Expr* cond, struct Scope* scope);
226-
static Expr* parse_function_call(Parser* parser, Expr* function,
227-
struct Scope* scope);
225+
static Expr* parse_function_call(Parser* parser, Expr* function, Scope* scope);
228226

229-
typedef Expr* (*PrefixParseFn)(Parser*, struct Scope* scope);
230-
typedef Expr* (*InfixParseFn)(Parser*, Expr*, struct Scope* scope);
227+
typedef Expr* (*PrefixParseFn)(Parser*, Scope* scope);
228+
typedef Expr* (*InfixParseFn)(Parser*, Expr*, Scope* scope);
231229

232230
typedef struct ParseRule {
233231
PrefixParseFn prefix;
@@ -619,21 +617,34 @@ static DeclSpecifier parse_decl_specifiers(Parser* parser)
619617

620618
static FunctionDecl* parse_function_decl(Parser* parser,
621619
DeclSpecifier decl_specifier,
622-
Token name_token, struct Scope* scope);
620+
Token name_token, Scope* scope);
623621

624622
static VariableDecl parse_variable_decl(Parser* parser,
625623
DeclSpecifier decl_specifier,
626-
Token name_token, struct Scope* scope)
624+
Token name_token, Scope* scope)
627625
{
628626
const StringView name = str_from_token(parser->src, name_token);
629-
// TODO: handle different linkages
630-
IdentifierInfo* variable = add_identifier(
631-
scope, name, IDENT_OBJECT, LINKAGE_NONE, parser->permanent_arena);
627+
628+
const bool is_global_variable = scope == parser->symbol_table->global_scope;
629+
630+
// TODO: properly handle linkage
631+
Linkage linkage = LINKAGE_NONE;
632+
if (is_global_variable) { linkage = LINKAGE_EXTERNAL; }
633+
634+
IdentifierInfo* variable =
635+
add_identifier(parser->symbol_table, scope, name, IDENT_OBJECT, linkage,
636+
parser->permanent_arena);
632637
if (!variable) {
633-
const StringView error_msg =
634-
allocate_printf(parser->permanent_arena, "redefinition of '%.*s'",
635-
(int)name.size, name.start);
636-
parse_error_at(parser, error_msg, token_source_range(name_token));
638+
if (linkage != LINKAGE_NONE) {
639+
variable = lookup_identifier(scope, name);
640+
MCC_ASSERT(variable != nullptr);
641+
642+
} else {
643+
const StringView error_msg =
644+
allocate_printf(parser->permanent_arena, "redefinition of '%.*s'",
645+
(int)name.size, name.start);
646+
parse_error_at(parser, error_msg, token_source_range(name_token));
647+
}
637648
}
638649

639650
Expr* initializer = nullptr;
@@ -646,11 +657,11 @@ static VariableDecl parse_variable_decl(Parser* parser,
646657
// TODO: handle the case where variable == nullptr
647658
return (VariableDecl){.type = decl_specifier.type,
648659
.storage_class = decl_specifier.storage_class,
649-
.name = variable,
660+
.identifier = variable,
650661
.initializer = initializer};
651662
}
652663

653-
static Decl parse_decl(Parser* parser, struct Scope* scope)
664+
static Decl parse_decl(Parser* parser, Scope* scope)
654665
{
655666
const DeclSpecifier decl_specifier = parse_decl_specifiers(parser);
656667
const Token name_token = parse_identifier(parser);
@@ -937,8 +948,9 @@ static IdentifierInfo* parse_parameter(Parser* parser, Scope* scope)
937948
parse_advance(parser);
938949
}
939950

940-
IdentifierInfo* name = add_identifier(
941-
scope, identifier, IDENT_OBJECT, LINKAGE_NONE, parser->permanent_arena);
951+
IdentifierInfo* name =
952+
add_identifier(parser->symbol_table, scope, identifier, IDENT_OBJECT,
953+
LINKAGE_NONE, parser->permanent_arena);
942954
// TODO: error handling
943955
MCC_ASSERT(name != nullptr);
944956
return name;
@@ -1006,8 +1018,9 @@ static FunctionDecl* parse_function_decl(Parser* parser,
10061018
Token name_token, Scope* scope)
10071019
{
10081020
StringView name = str_from_token(parser->src, name_token);
1009-
IdentifierInfo* function_ident = add_identifier(
1010-
scope, name, IDENT_FUNCTION, LINKAGE_EXTERNAL, parser->permanent_arena);
1021+
IdentifierInfo* function_ident =
1022+
add_identifier(parser->symbol_table, scope, name, IDENT_FUNCTION,
1023+
LINKAGE_EXTERNAL, parser->permanent_arena);
10111024

10121025
if (!function_ident) {
10131026
function_ident = lookup_identifier(scope, name);
@@ -1021,11 +1034,8 @@ static FunctionDecl* parse_function_decl(Parser* parser,
10211034
}
10221035
}
10231036

1024-
hashmap_try_insert(&parser->functions, name, function_ident,
1025-
parser->permanent_arena);
1026-
10271037
Scope* function_scope =
1028-
new_scope(parser->global_scope, parser->permanent_arena);
1038+
new_scope(parser->symbol_table->global_scope, parser->permanent_arena);
10291039
const Parameters parameters = parse_parameter_list(parser, function_scope);
10301040

10311041
Block* body = NULL;
@@ -1065,7 +1075,7 @@ static TranslationUnit* parse_translation_unit(Parser* parser)
10651075
struct DeclVec decl_vec = {};
10661076

10671077
while (parser_current_token(parser).tag != TOKEN_EOF) {
1068-
Decl decl = parse_decl(parser, parser->global_scope);
1078+
Decl decl = parse_decl(parser, parser->symbol_table->global_scope);
10691079
DYNARRAY_PUSH_BACK(&decl_vec, Decl, &parser->scratch_arena, decl);
10701080
}
10711081

@@ -1077,12 +1087,9 @@ static TranslationUnit* parse_translation_unit(Parser* parser)
10771087

10781088
TranslationUnit* tu =
10791089
ARENA_ALLOC_OBJECT(parser->permanent_arena, TranslationUnit);
1080-
*tu = (TranslationUnit){
1081-
.decl_count = decl_count,
1082-
.decls = decls,
1083-
.global_scope = parser->global_scope,
1084-
.functions = parser->functions,
1085-
};
1090+
*tu = (TranslationUnit){.decl_count = decl_count,
1091+
.decls = decls,
1092+
.symbol_table = parser->symbol_table};
10861093
parse_consume(parser, TOKEN_EOF, "Expect end of the file");
10871094

10881095
return tu;
@@ -1091,12 +1098,17 @@ static TranslationUnit* parse_translation_unit(Parser* parser)
10911098
ParseResult parse(const char* src, Tokens tokens, Arena* permanent_arena,
10921099
Arena scratch_arena)
10931100
{
1101+
SymbolTable* symbol_table = ARENA_ALLOC_OBJECT(permanent_arena, SymbolTable);
1102+
*symbol_table = (SymbolTable){
1103+
.global_symbols = (HashMap){},
1104+
.global_scope = new_scope(nullptr, permanent_arena),
1105+
};
1106+
10941107
Parser parser = {.src = src,
10951108
.tokens = tokens,
10961109
.permanent_arena = permanent_arena,
10971110
.scratch_arena = scratch_arena,
1098-
.global_scope = new_scope(nullptr, permanent_arena),
1099-
.functions = (HashMap){}};
1111+
.symbol_table = symbol_table};
11001112

11011113
TranslationUnit* tu = parse_translation_unit(&parser);
11021114

src/frontend/symbol_table.c

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,10 @@ IdentifierInfo* lookup_identifier(const Scope* scope, StringView name)
2727
return lookup_identifier(scope->parent, name);
2828
}
2929

30-
IdentifierInfo* add_identifier(Scope* scope, StringView name,
31-
IdentifierKind kind, Linkage linkage,
32-
Arena* arena)
30+
static IdentifierInfo* add_identifier_to_scope(Scope* scope, StringView name,
31+
IdentifierKind kind,
32+
Linkage linkage, Arena* arena)
3333
{
34-
// check variable in current scope
35-
if (hashmap_lookup(&scope->identifiers, name) != nullptr) { return nullptr; }
36-
3734
// lookup variable in parent scopes
3835
const IdentifierInfo* parent_variable = nullptr;
3936
if (scope->parent) {
@@ -64,5 +61,23 @@ IdentifierInfo* add_identifier(Scope* scope, StringView name,
6461
const bool insert_succeed =
6562
hashmap_try_insert(&scope->identifiers, name, variable, arena);
6663
MCC_ASSERT(insert_succeed);
64+
6765
return variable;
6866
}
67+
68+
IdentifierInfo* add_identifier(SymbolTable* symbol_table, Scope* scope,
69+
StringView name, IdentifierKind kind,
70+
Linkage linkage, Arena* arena)
71+
{
72+
// check identifier in current scope
73+
if (hashmap_lookup(&scope->identifiers, name) != nullptr) { return nullptr; }
74+
75+
IdentifierInfo* ident_info =
76+
add_identifier_to_scope(scope, name, kind, linkage, arena);
77+
78+
if (linkage != LINKAGE_NONE) {
79+
hashmap_try_insert(&symbol_table->global_symbols, name, ident_info, arena);
80+
}
81+
82+
return ident_info;
83+
}

src/frontend/symbol_table.h

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,14 +45,26 @@ typedef struct IdentifierInfo {
4545
// Represents a block scope
4646
typedef struct Scope Scope;
4747

48+
// Represents a symbol table in a C translation unit, tracking both scoped and
49+
// global symbols. In C, identifiers with linkage can be referenced across
50+
// multiple scopes while remaining unique within a program. To account for this,
51+
// a pointer to global symbols are stored separately in addition to their
52+
// respective scopes.
53+
typedef struct SymbolTable {
54+
// Tracks globally visible symbols (those with external or internal linkage)
55+
HashMap global_symbols;
56+
// Represents the global scope of the translation unit.
57+
Scope* global_scope;
58+
} SymbolTable;
59+
4860
Scope* new_scope(Scope* parent, Arena* arena);
4961

5062
IdentifierInfo* lookup_identifier(const Scope* scope, StringView name);
5163

5264
// Return nullptr if a variable of the same name already exist in the same scope
5365
// Otherwise we add it to the current scope
54-
IdentifierInfo* add_identifier(Scope* scope, StringView name,
55-
IdentifierKind kind, Linkage linkage,
56-
Arena* arena);
66+
IdentifierInfo* add_identifier(SymbolTable* symbol_table, Scope* scope,
67+
StringView name, IdentifierKind kind,
68+
Linkage linkage, Arena* arena);
5769

5870
#endif // MCC_SYMBOL_TABLE_H

src/frontend/type_check.c

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ struct ErrorVec {
1818
typedef struct Context {
1919
struct ErrorVec errors;
2020
Arena* permanent_arena;
21-
HashMap functions;
21+
SymbolTable* symbol_table;
2222
} Context;
2323

2424
#pragma region error reporter
@@ -114,6 +114,15 @@ static void report_multiple_definition(FunctionDecl* decl, Context* context)
114114
(int)decl->name->name.size, decl->name->name.start);
115115
error_at(msg, decl->source_range, context);
116116
}
117+
118+
static void report_redefinition_of_var(VariableDecl* decl, Context* context)
119+
{
120+
StringView msg = allocate_printf(
121+
context->permanent_arena, "redefinition of '%.*s'",
122+
(int)decl->identifier->name.size, decl->identifier->name.start);
123+
error_at(msg, decl->source_range, context);
124+
}
125+
117126
#pragma endregion
118127

119128
[[nodiscard]]
@@ -280,13 +289,15 @@ static bool type_check_stmt(Stmt* stmt, Context* context)
280289
[[nodiscard]] static bool type_check_variable_decl(VariableDecl* decl,
281290
Context* context)
282291
{
283-
decl->name->type = typ_int;
292+
decl->identifier->type = typ_int;
284293

285294
if (decl->initializer) {
286-
// TODO: handle redefinition of static/global variables
287-
MCC_ASSERT(decl->name->has_definition == false);
295+
if (decl->identifier->has_definition) {
296+
report_redefinition_of_var(decl, context);
297+
return false;
298+
}
288299

289-
decl->name->has_definition = true;
300+
decl->identifier->has_definition = true;
290301

291302
if (!type_check_expr(decl->initializer, context)) { return false; }
292303

@@ -336,7 +347,7 @@ static bool type_check_function_decl(FunctionDecl* decl, Context* context)
336347
{
337348
StringView function_name = decl->name->name;
338349
IdentifierInfo* function_ident =
339-
hashmap_lookup(&context->functions, function_name);
350+
hashmap_lookup(&context->symbol_table->global_symbols, function_name);
340351

341352
if (function_ident->type == nullptr) {
342353
function_ident->type =
@@ -370,7 +381,7 @@ static bool type_check_function_decl(FunctionDecl* decl, Context* context)
370381
ErrorsView type_check(TranslationUnit* ast, Arena* permanent_arena)
371382
{
372383
Context context = {.permanent_arena = permanent_arena,
373-
.functions = ast->functions};
384+
.symbol_table = ast->symbol_table};
374385

375386
for (uint32_t i = 0; i < ast->decl_count; ++i) {
376387
type_check_decl(&ast->decls[i], &context);

src/ir/ir_generator.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -440,7 +440,7 @@ static void emit_ir_instructions_from_decl(const VariableDecl* decl,
440440
emit_ir_instructions_from_expr(decl->initializer, context);
441441
push_instruction(
442442
context,
443-
ir_unary_instr(IR_COPY, ir_variable(decl->name->rewrote_name), value));
443+
ir_unary_instr(IR_COPY, ir_variable(decl->identifier->rewrote_name), value));
444444
}
445445
}
446446

@@ -757,7 +757,7 @@ IRGenerationResult ir_generate(const TranslationUnit* ast,
757757
*top_level = (IRTopLevel){
758758
.tag = IR_TOP_LEVEL_VARIABLE,
759759
.variable =
760-
(IRGlobalVariable){.name = decl->var.name->name, .value = value},
760+
(IRGlobalVariable){.name = decl->var.identifier->name, .value = value},
761761
};
762762
DYNARRAY_PUSH_BACK(&top_level_vec, IRTopLevel*, &scratch_arena,
763763
top_level);

0 commit comments

Comments
 (0)