Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/backend/codegen.c
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,10 @@ int CODEGEN_write_function(
case IR_ALLOC:
target->alloc_memory(sb, (*it)->alloc_size);
break;
case IR_DEALLOC:
const char * src = CODEGEN_get_reg(target, (*it)->src, false);
target->dealloc_memory(sb, src, (*it)->src.size);
break;
case IR_MOV_OFFSET:
if ((*it)->offset.timing == IR_PRE_OFFSET) {
const char* dst = CODEGEN_get_reg(target, (*it)->dest, false);
Expand Down
5 changes: 5 additions & 0 deletions src/backend/target.h
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,11 @@ typedef struct {
(*emit_mov_offset_post)
(string_builder_t*,
const char* dst, size_t size, const char* src);

void
(*dealloc_memory)
(string_builder_t*,
const char* src, size_t size);
} target_t;

#endif // TARGET_H
10 changes: 10 additions & 0 deletions src/backend/x86_64.c
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,15 @@ static void x86_alloc_memory(string_builder_t* sb, int size)
sb_append_fmt(sb, " syscall\n");
}

static void x86_dealloc_memory(
string_builder_t* sb, const char* src, size_t size)
{
sb_append_fmt(sb, " mov rax, 11\n");
sb_append_fmt(sb, " mov rdi, %s\n", src);
sb_append_fmt(sb, " mov rsi, %zu\n", size);
sb_append_fmt(sb, " syscall\n");
}

static void x86_emit_mov_offset_pre(string_builder_t* sb,
const char* dst, size_t size, const char* src)
{
Expand Down Expand Up @@ -322,4 +331,5 @@ const target_t x86_64_target = {
.alloc_memory = x86_alloc_memory,
.emit_mov_offset_pre = x86_emit_mov_offset_pre,
.emit_mov_offset_post = x86_emit_mov_offset_post,
.dealloc_memory = x86_dealloc_memory,
};
36 changes: 36 additions & 0 deletions src/frontend/ast.c
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,10 @@ void free_statement(statement_t* s)
free(s->asm_stmt.args);
}

if (s->type == STATEMENT_FREE) {
free_expression(s->free_stmt.expr);
}

free(s);
}

Expand Down Expand Up @@ -1759,6 +1763,33 @@ statement_t* ast_parse_asm_stmt(parser_t* p)
return stmt;
}

statement_t* ast_parse_free_stmt(parser_t* p)
{
statement_t* stmt = calloc(1, sizeof(statement_t));
if (!stmt) {
error_report_general(ERROR_SEVERITY_ERROR, "out of memory");
return NULL;
}
stmt->type = STATEMENT_FREE;

// consume 'free'
advance(p);

expect(p, '(', "expect '(' after 'free' token'");

stmt->free_stmt.expr = parse_expression(p);
if (!stmt->free_stmt.expr) {
// TODO: maybe make error stack
free_statement(stmt);
return NULL;
}

expect(p, ')', "expect ')' after free statement definition");
expect(p, ';', "expect ';' at the and of statement");

return stmt;
}

statement_t* parse_statement(parser_t* p)
{
if (check(p, LEXER_token_id) &&
Expand Down Expand Up @@ -1786,6 +1817,11 @@ statement_t* parse_statement(parser_t* p)
return ast_parse_asm_stmt(p);
}

if (check(p, LEXER_token_id) &&
strcmp(peek(p)->string_value, "free") == 0) {
return ast_parse_free_stmt(p);
}

// WARNING: this can be unsafe if string_value is NULL
// TODO: keep an eye on this
if (check(p, LEXER_token_id) && ((check_is_type(p)) ||
Expand Down
1 change: 1 addition & 0 deletions src/frontend/ast.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ statement_t* ast_parse_if_stmt(parser_t* p);
statement_t* ast_parse_while_stmt(parser_t* p);
statement_t* ast_parse_for_stmt(parser_t* p);
statement_t* ast_parse_asm_stmt(parser_t* p);
statement_t* ast_parse_free_stmt(parser_t*p);
statement_t* parse_statement(parser_t* p);

expression_t* parse_expression(parser_t* p);
Expand Down
2 changes: 2 additions & 0 deletions src/frontend/ast_definition.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ typedef enum
STATEMENT_WHILE,
STATEMENT_FOR,
STATEMENT_ASM,
STATEMENT_FREE,
} statement_kind;

typedef enum
Expand Down Expand Up @@ -157,6 +158,7 @@ struct statement_t
struct { expression_t* value; } ret;
struct { expression_t* expr; } expr_stmt;
struct { declaration_t* decl; } decl_stmt;
struct { expression_t* expr; } free_stmt;
struct {
char** instr;
expression_t** args;
Expand Down
6 changes: 6 additions & 0 deletions src/frontend/ast_printer.c
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,12 @@ static void print_statement(statement_t* s, const char* prefix, bool is_last)
break;
}

case STATEMENT_FREE:
printf(CLR_STMT "FreeStmt\n" CLR_RESET);
if (s->free_stmt.expr)
print_expression(s->free_stmt.expr, cp, true);
break;

case STATEMENT_ASM: {
printf(CLR_STMT "AsmStmt" CLR_RESET " (%zu instr%s)\n",
s->asm_stmt.instr_count,
Expand Down
17 changes: 15 additions & 2 deletions src/frontend/scope.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ static inline scope_t* scope_enter(scope_t* parent)
return s;
}

static inline void scope_exit(scope_t* scope) {
static inline void scope_exit(scope_t* scope)
{
if (scope) {
if (scope->symbols) {
hashmap_free(scope->symbols, 1);
Expand All @@ -34,7 +35,8 @@ static inline void scope_exit(scope_t* scope) {
}
}

static inline void* scope_resolve(scope_t* scope, const char* name) {
static inline void* scope_resolve(scope_t* scope, const char* name)
{
for (scope_t* s = scope; s != NULL; s = s->parent) {
void* sym = hashmap_get(s->symbols, name);
if (sym)
Expand All @@ -43,4 +45,15 @@ static inline void* scope_resolve(scope_t* scope, const char* name) {
return NULL;
}

static inline int scope_remove(scope_t* scope, const char* name)
{
for (scope_t* s = scope; s != NULL; s = s->parent) {
if (hashmap_remove(s->symbols, name)) {
return 1;
}
}

return 0;
}

#endif // SCOPE_H
24 changes: 24 additions & 0 deletions src/frontend/semantic.c
Original file line number Diff line number Diff line change
Expand Up @@ -704,11 +704,35 @@ void semantic_check_scope(semantic_analyzer_t* analyzer,
if (stmt->type == STATEMENT_EXPR)
semantic_check_expression(
analyzer, stmt->expr_stmt.expr, local_scope);

if (stmt->type == STATEMENT_FREE)
semantic_check_free_statement(analyzer, stmt, local_scope);
}

scope_exit(local_scope);
}

void semantic_check_free_statement(
semantic_analyzer_t* analyzer,
statement_t* stmt,
scope_t* scope)
{
known_type_t t =
semantic_check_expression(analyzer, stmt->free_stmt.expr, scope);

if (t.kind != TYPE_CUSTOM) {
semantic_error_register(
analyzer, stmt->free_stmt.expr->source_pos - 1,
"you are tryning to free unallocated memory. Please not that only stuct typed variable and arrays are allocated in the heap");
return;
}

variable_symbol_t* sym = (variable_symbol_t*)
scope_resolve(scope, stmt->free_stmt.expr->var.ident.ident_name);
free(sym);
scope_remove(scope, stmt->free_stmt.expr->var.ident.ident_name);
}

void semantic_check_asm_statement(
semantic_analyzer_t* analyzer,
statement_t* stmt,
Expand Down
5 changes: 5 additions & 0 deletions src/frontend/semantic.h
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,11 @@ void semantic_check_asm_statement(
statement_t* stmt,
scope_t* scope);

void semantic_check_free_statement(
semantic_analyzer_t* analyzer,
statement_t* stmt,
scope_t* scope);

void semantic_check_var_declaration(
semantic_analyzer_t* analyzer,
declaration_t* decl,
Expand Down
29 changes: 29 additions & 0 deletions src/middleend/hir.c
Original file line number Diff line number Diff line change
Expand Up @@ -557,6 +557,28 @@ int IR_lower_expression(HIR_parser_t* hir,
return 1;
}

int IR_lower_free_statement(
HIR_parser_t* hir,
statement_t* stmt,
IR_function_t* func)
{
IR_instruction_t* instr = calloc(1, sizeof(IR_instruction_t));
if (!instr) {
error_report_general(ERROR_SEVERITY_ERROR, "out if memory");
return 1;
}

instr->kind = IR_DEALLOC;

IR_lower_expression(hir, stmt->free_stmt.expr, func);

instr->src.id = func->next_temp_id;
instr->src.size = stmt->free_stmt.expr->var.ident.type.size;

da_append(func->code, instr);
return 0;
}

int IR_lower_asm_statement(
HIR_parser_t* hir,
statement_t* stmt,
Expand Down Expand Up @@ -966,6 +988,9 @@ int IR_lower_statement(HIR_parser_t* hir,
if (stmt->type == STATEMENT_ASM) {
return IR_lower_asm_statement(hir, stmt, func);
}
if (stmt->type == STATEMENT_FREE) {
return IR_lower_free_statement(hir, stmt, func);
}

error_report_general(ERROR_SEVERITY_NOT_IMPLEMENTED,
"unknown statement instruction");
Expand Down Expand Up @@ -1192,6 +1217,10 @@ char* IR_generate_string_program(IR_function_t* function)
continue;
}

if (instr->kind == IR_DEALLOC) {
sb_append_fmt(&sb, "DEALLOC %c%d, %zu\n", TEMP_STR(instr->src), instr->src.size);
}

if (instr->kind == IR_ASM) {
sb_append_fmt(&sb, "ASM [");
for (size_t j = 0; j < instr->asm_data.string_count; j++) {
Expand Down
13 changes: 10 additions & 3 deletions src/middleend/hir.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,15 @@ typedef struct
IR_function_array* hir_program;
} HIR_parser_t;

int IR_lower_function(HIR_parser_t* hir,
int IR_lower_function(
HIR_parser_t* hir,
declaration_t* function);
int IR_lower_statement(HIR_parser_t* hir,
int IR_lower_statement(
HIR_parser_t* hir,
statement_t* stmt,
IR_function_t* func);
int IR_lower_expression(HIR_parser_t* hir,
int IR_lower_expression(
HIR_parser_t* hir,
expression_t* expr,
IR_function_t* func);
void IR_display_function(IR_function_t* function);
Expand Down Expand Up @@ -65,5 +68,9 @@ int IR_lower_asm_statement(
HIR_parser_t* hir,
statement_t* stmt,
IR_function_t* func);
int IR_lower_free_statement(
HIR_parser_t* hir,
statement_t* stmt,
IR_function_t* func);

#endif // IR_H
1 change: 1 addition & 0 deletions src/middleend/ir_definition.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ typedef enum
IR_EXIT,

IR_ALLOC,
IR_DEALLOC,

IR_CALL,

Expand Down
22 changes: 22 additions & 0 deletions src/thirdparty/hashmap.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,28 @@ inline static void* hashmap_get(hashmap_t* map, const char* key)
return NULL;
}

inline static int hashmap_remove(hashmap_t* map, const char* key)
{
if (!map || !key) return 0;

unsigned idx = hashmap_hash(key);
hashmap_entry_t* e = map->buckets[idx];
hashmap_entry_t* prev = NULL;

for (; e; prev = e, e = e->next) {
if (strcmp(key, e->key) != 0) continue;
if (prev)
prev->next = e->next;
else
map->buckets[idx] = e->next;
free(e->key);
free(e);
return 1;
}

return 0;
}

inline static void hashmap_free(hashmap_t* map, int pointer_value)
{
if (!map) return;
Expand Down
31 changes: 31 additions & 0 deletions test/ast_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -606,3 +606,34 @@ ct_test(ast, char_var_decl, "char a = 'a';") {
free_declaration(decl);
da_free(&parser);
}

// === FREE STATEMENTS ===

ct_test(ast, free_stmt_var, "free(a);")
{
statement_t* s = parse_statement(&parser);

ct_assert_not_null(s, "free statement should not be NULL");
ct_assert_eq(s->type, STATEMENT_FREE, "statement type should be STATEMENT_FREE");
ct_assert_not_null(s->free_stmt.expr, "free expression should not be NULL");
ct_assert_eq(s->free_stmt.expr->type, EXPRESSION_VAR, "free expression should be VAR");
ct_assert_eq(s->free_stmt.expr->var.ident.ident_name, "a", "freed var name should be 'a'");

free_statement(s);
da_free(&parser);
}

ct_test(ast, free_stmt_call, "free(get_ptr());")
{
statement_t* s = parse_statement(&parser);

ct_assert_not_null(s, "free statement should not be NULL");
ct_assert_eq(s->type, STATEMENT_FREE, "statement type should be STATEMENT_FREE");
ct_assert_not_null(s->free_stmt.expr, "free expression should not be NULL");
ct_assert_eq(s->free_stmt.expr->type, EXPRESSION_CALL, "free expression should be a CALL");
ct_assert_eq(s->free_stmt.expr->call.callee, "get_ptr", "callee should be 'get_ptr'");
ct_assert_eq((int)s->free_stmt.expr->call.arg_count, 0, "call should have 0 args");

free_statement(s);
da_free(&parser);
}
Loading
Loading