aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorhachem <im@hachem.wtf>2026-08-31 16:56:40 +0200
committerhachem <im@hachem.wtf>2026-08-31 16:56:40 +0200
commit53aed3fadf1f7ad66dea9a8a0b46bf42eda921be (patch)
treeea14a08b29a18072d1e420353a39c1397f85bee2 /src
parentf8a2d02c75df62f74e5907696d5a99a024d59018 (diff)
feat: add memory loads via ^reg
Diffstat (limited to 'src')
-rw-r--r--src/codegen/nasm.c44
-rw-r--r--src/parser/ast.c3
-rw-r--r--src/parser/ast.h26
-rw-r--r--src/parser/parser.c41
-rw-r--r--src/sema/sema.c13
5 files changed, 102 insertions, 25 deletions
diff --git a/src/codegen/nasm.c b/src/codegen/nasm.c
index e55c791..2f8de80 100644
--- a/src/codegen/nasm.c
+++ b/src/codegen/nasm.c
@@ -203,6 +203,7 @@ static bool emit_operand(struct Emitter* emitter, struct Expr* expr)
return true;
}
case EXPR_BINARY:
+ case EXPR_DEREF:
return false;
}
@@ -248,6 +249,7 @@ static bool expr_supported(struct Emitter* emitter, struct Expr* expr)
{
case EXPR_PRIMARY:
case EXPR_MEMBER:
+ case EXPR_DEREF:
return true;
case EXPR_BINARY:
if (expr->binary.right->kind != EXPR_PRIMARY &&
@@ -262,8 +264,41 @@ static bool expr_supported(struct Emitter* emitter, struct Expr* expr)
return false;
}
+// dst = [address], zero-extending narrower loads into the full register
+static void emit_load(struct Emitter* emitter, const char* dst, struct DerefExpr* deref)
+{
+ FILE* out = emitter->out;
+ switch (deref->size)
+ {
+ case STORE_SIZE_BYTE:
+ fprintf(out, "\tmovzx %s, byte [", dst);
+ break;
+ case STORE_SIZE_WORD:
+ fprintf(out, "\tmovzx %s, word [", dst);
+ break;
+ case STORE_SIZE_DWORD:
+ {
+ const char* dword = sized_register(text_token(dst), STORE_SIZE_DWORD);
+ fprintf(out, "\tmov %s, [", dword != NULL ? dword : dst);
+ break;
+ }
+ default:
+ fprintf(out, "\tmov %s, [", dst);
+ break;
+ }
+
+ emit_operand(emitter, deref->address);
+ fprintf(out, "]\n");
+}
+
static void emit_expr_into(struct Emitter* emitter, const char* dst, struct Expr* expr)
{
+ if (expr->kind == EXPR_DEREF)
+ {
+ emit_load(emitter, dst, &expr->deref);
+ return;
+ }
+
if (expr->kind == EXPR_BINARY)
{
emit_expr_into(emitter, dst, expr->binary.left);
@@ -389,7 +424,8 @@ static void emit_assign(struct Emitter* emitter, struct AssignStatement* assign)
const char* mnemonic = assign_mnemonic(assign->op.type);
bool value_is_buffer = assign->value->kind == EXPR_PRIMARY
&& is_buffer_name(emitter, assign->value->primary.token);
- if (mnemonic == NULL || assign->value->kind == EXPR_BINARY || value_is_buffer)
+ if (mnemonic == NULL || assign->value->kind == EXPR_BINARY
+ || assign->value->kind == EXPR_DEREF || value_is_buffer)
{
fprintf(emitter->out, "\t; TODO: unsupported assignment\n");
return;
@@ -458,7 +494,7 @@ static void emit_call(struct Emitter* emitter, struct CallStatement* call)
for (size_t i = 0; i < call->arg_count; i += 1)
{
- if (call->args[i]->kind == EXPR_BINARY)
+ if (call->args[i]->kind == EXPR_BINARY || call->args[i]->kind == EXPR_DEREF)
{
fprintf(emitter->out, "\t; TODO: unsupported call argument\n");
continue;
@@ -478,7 +514,9 @@ static void emit_statement(struct Emitter* emitter, struct Statement* statement)
static void emit_if(struct Emitter* emitter, struct IfStatement* branch)
{
const char* jump = jump_if_false(branch->comparison.type);
- if (jump == NULL || branch->left->kind == EXPR_BINARY || branch->right->kind == EXPR_BINARY)
+ if (jump == NULL
+ || branch->left->kind == EXPR_BINARY || branch->left->kind == EXPR_DEREF
+ || branch->right->kind == EXPR_BINARY || branch->right->kind == EXPR_DEREF)
{
fprintf(emitter->out, "\t; TODO: unsupported if\n");
return;
diff --git a/src/parser/ast.c b/src/parser/ast.c
index 96ee479..f9f857d 100644
--- a/src/parser/ast.c
+++ b/src/parser/ast.c
@@ -18,6 +18,9 @@ void free_expr(struct Expr* expr)
case EXPR_MEMBER:
free_expr(expr->member.object);
break;
+ case EXPR_DEREF:
+ free_expr(expr->deref.address);
+ break;
}
free(expr);
diff --git a/src/parser/ast.h b/src/parser/ast.h
index a98f43b..fc7af2b 100644
--- a/src/parser/ast.h
+++ b/src/parser/ast.h
@@ -24,11 +24,21 @@ struct Param
struct Token reg;
};
+enum StoreSize
+{
+ STORE_SIZE_NONE,
+ STORE_SIZE_BYTE,
+ STORE_SIZE_WORD,
+ STORE_SIZE_DWORD,
+ STORE_SIZE_QWORD,
+};
+
enum ExprKind
{
EXPR_PRIMARY,
EXPR_BINARY,
EXPR_MEMBER,
+ EXPR_DEREF,
};
struct PrimaryExpr
@@ -49,6 +59,12 @@ struct MemberExpr
struct Token member;
};
+struct DerefExpr
+{
+ enum StoreSize size;
+ struct Expr* address;
+};
+
struct Expr
{
enum ExprKind kind;
@@ -57,18 +73,10 @@ struct Expr
struct PrimaryExpr primary;
struct BinaryExpr binary;
struct MemberExpr member;
+ struct DerefExpr deref;
};
};
-enum StoreSize
-{
- STORE_SIZE_NONE,
- STORE_SIZE_BYTE,
- STORE_SIZE_WORD,
- STORE_SIZE_DWORD,
- STORE_SIZE_QWORD,
-};
-
enum StatementKind
{
STATEMENT_ASSIGN,
diff --git a/src/parser/parser.c b/src/parser/parser.c
index beb6b04..969ed86 100644
--- a/src/parser/parser.c
+++ b/src/parser/parser.c
@@ -133,8 +133,35 @@ static struct Expr* alloc_expr(enum ExprKind kind)
return expr;
}
+static enum StoreSize parse_store_size(struct Parser* parser)
+{
+ if (match_token(parser, TOKEN_BYTE))
+ return STORE_SIZE_BYTE;
+ if (match_token(parser, TOKEN_WORD))
+ return STORE_SIZE_WORD;
+ if (match_token(parser, TOKEN_DWORD))
+ return STORE_SIZE_DWORD;
+ if (match_token(parser, TOKEN_QWORD))
+ return STORE_SIZE_QWORD;
+ return STORE_SIZE_NONE;
+}
+
static struct Expr* parse_primary(struct Parser* parser)
{
+ if (match_token(parser, TOKEN_CARET))
+ {
+ enum StoreSize size = parse_store_size(parser);
+
+ struct Expr* address = parse_primary(parser);
+ if (address == NULL)
+ return NULL;
+
+ struct Expr* deref = alloc_expr(EXPR_DEREF);
+ deref->deref.size = size;
+ deref->deref.address = address;
+ return deref;
+ }
+
if (check(parser, TOKEN_IDENTIFIER) || check(parser, TOKEN_INTEGER) || check(parser, TOKEN_CHAR))
{
advance_parser(parser);
@@ -350,19 +377,7 @@ static bool parse_statement(struct Parser* parser, struct Statement* out)
}
bool deref = match_token(parser, TOKEN_CARET);
-
- enum StoreSize store_size = STORE_SIZE_NONE;
- if (deref)
- {
- if (match_token(parser, TOKEN_BYTE))
- store_size = STORE_SIZE_BYTE;
- else if (match_token(parser, TOKEN_WORD))
- store_size = STORE_SIZE_WORD;
- else if (match_token(parser, TOKEN_DWORD))
- store_size = STORE_SIZE_DWORD;
- else if (match_token(parser, TOKEN_QWORD))
- store_size = STORE_SIZE_QWORD;
- }
+ enum StoreSize store_size = deref ? parse_store_size(parser) : STORE_SIZE_NONE;
if (!consume(parser, TOKEN_IDENTIFIER, "expected a statement"))
return false;
diff --git a/src/sema/sema.c b/src/sema/sema.c
index ad2b12a..fb53bbf 100644
--- a/src/sema/sema.c
+++ b/src/sema/sema.c
@@ -227,6 +227,19 @@ static void check_expr(struct RefCheck* check, struct Expr* expr)
check_expr(check, expr->binary.left);
check_expr(check, expr->binary.right);
break;
+ case EXPR_DEREF:
+ {
+ struct Expr* address = expr->deref.address;
+ if (address->kind == EXPR_PRIMARY
+ && (is_register(check, address->primary.token) || is_param(check, address->primary.token)))
+ break;
+
+ if (address->kind == EXPR_PRIMARY)
+ ref_error(check, address->primary.token, "dereference address must be a register");
+ else
+ check_expr(check, address);
+ break;
+ }
case EXPR_MEMBER:
{
struct Expr* object = expr->member.object;