From 0f7618e26663483dd3f93e6151037f865f3791be Mon Sep 17 00:00:00 2001
From: Kimplul <kimi.h.kuparinen@gmail.com>
Date: Thu, 6 Mar 2025 21:08:55 +0200
Subject: implement some peephole optimizations

+ Mainly just use immediates wherever possible
---
 src/lower.c | 108 +++++++++++++++++++++++++++++++++++++++++++++++++++++-------
 1 file changed, 97 insertions(+), 11 deletions(-)

(limited to 'src')

diff --git a/src/lower.c b/src/lower.c
index cdede46..7f74a50 100644
--- a/src/lower.c
+++ b/src/lower.c
@@ -155,6 +155,21 @@ static void lower_add(struct fn *f, struct ast *n)
 	struct ast *l = add_l(n);
 	struct ast *r = add_r(n);
 
+	/* we could implement some kind of constant folding here, but it might
+	 * be better to do it during type checking or something so we don't mess
+	 * with stack locations too much */
+	if (r->k == AST_CONST_INT) {
+		f->sp += 1; lower(f, l);
+		f->sp -= 1;
+		return ejit_addi(f->f, reg(n), reg(l), r->v);
+	}
+
+	if (l->k == AST_CONST_INT) {
+		f->sp += 1; lower(f, r);
+		f->sp -= 1;
+		return ejit_addi(f->f, reg(n), reg(r), l->v);
+	}
+
 	/* since variable definitions can't appear in expressions, there's no
 	 * danger of some variable claiming a stack location above this point and
 	 * it being overwritten by accident. If variable definitions were
@@ -176,6 +191,13 @@ static void lower_sub(struct fn *f, struct ast *n)
 	struct ast *l = sub_l(n);
 	struct ast *r = sub_r(n);
 
+	if (r->k == AST_CONST_INT) {
+		f->sp += 1; lower(f, l);
+		f->sp -= 1;
+		ejit_subi(f->f, reg(n), reg(l), r->v);
+		return;
+	}
+
 	f->sp += 1; lower(f, l);
 	f->sp += 1; lower(f, r);
 	f->sp -= 2;
@@ -356,6 +378,12 @@ static void lower_assign(struct fn *f, struct ast *n)
 static void lower_return(struct fn *f, struct ast *n)
 {
 	struct ast *expr = return_expr(n);
+	if (expr->k == AST_CONST_INT) {
+		ejit_reti(f->f, expr->v);
+		n->l = null_loc();
+		return;
+	}
+
 	lower(f, expr);
 	ejit_retr(f->f, reg(expr));
 	n->l = null_loc();
@@ -812,6 +840,70 @@ static void lower_date_diff(struct fn *f, struct ast *n)
 	ejit_retval(f->f, reg(n));
 }
 
+static struct ejit_reloc branch_if(struct fn *f, struct ast *cond, bool branch)
+{
+	if (cond->k == AST_EQ && (eq_r(cond))->k == AST_CONST_INT) {
+		struct ast *r = eq_r(cond);
+		struct ast *l = eq_l(cond);
+		lower(f, l);
+		return (branch ? ejit_beqi : ejit_bnei)(f->f, reg(l), r->v);
+	}
+
+	if (cond->k == AST_EQ && (eq_l(cond))->k == AST_CONST_INT) {
+		struct ast *r = eq_r(cond);
+		struct ast *l = eq_l(cond);
+		lower(f, r);
+		return (branch ? ejit_beqi : ejit_bnei)(f->f, reg(r), l->v);
+	}
+
+	if (cond->k == AST_EQ) {
+		struct ast *r = eq_r(cond);
+		struct ast *l = eq_l(cond);
+		lower(f, r);
+		lower(f, l);
+		return (branch ? ejit_beqr : ejit_bner)(f->f, reg(r), reg(l));
+	}
+
+	if (cond->k == AST_LT && (lt_r(cond))->k == AST_CONST_INT) {
+		struct ast *r = lt_r(cond);
+		struct ast *l = lt_l(cond);
+		lower(f, l);
+		return (branch ? ejit_blti : ejit_bgei)(f->f, reg(l), r->v);
+	}
+
+	if (cond->k == AST_LT && (lt_l(cond))->k == AST_CONST_INT) {
+		struct ast *r = lt_r(cond);
+		struct ast *l = lt_l(cond);
+		lower(f, r);
+		return (branch ? ejit_bgti : ejit_blei)(f->f, reg(r), l->v);
+	}
+
+	if (cond->k == AST_LT) {
+		struct ast *r = lt_r(cond);
+		struct ast *l = lt_l(cond);
+		lower(f, r);
+		lower(f, l);
+		return (branch ? ejit_bltr : ejit_bger)(f->f, reg(l), reg(r));
+	}
+
+	if (cond->k == AST_CONST_INT) {
+		if (branch && cond->v)
+			/* always jump */
+			return ejit_jmp(f->f);
+
+		if (!branch && !cond->v)
+			return ejit_jmp(f->f);
+
+		/** @todo here it would be useful to have some kind of nop with
+		 * relocation, since we must return a branch of some kind, but
+		 * we know that we never want to jump. */
+	}
+
+	/* fallback */
+	lower(f, cond);
+	return (branch ? ejit_bnei : ejit_beqi)(f->f, reg(cond), 0);
+}
+
 static void lower_until(struct fn *f, struct ast *n)
 {
 	struct ejit_label l = ejit_label(f->f);
@@ -819,19 +911,15 @@ static void lower_until(struct fn *f, struct ast *n)
 	lower_list(f, until_body(n));
 
 	struct ast *cond = until_cond(n);
-	lower(f, cond);
-
-	struct ejit_reloc r = ejit_beqi(f->f, reg(cond), 0);
-	ejit_patch(f->f, r, l);
+	struct ejit_reloc branch = branch_if(f, cond, false);
+	ejit_patch(f->f, branch, l);
 	n->l = null_loc();
 }
 
 static void lower_unless(struct fn *f, struct ast *n)
 {
 	struct ast *cond = unless_cond(n);
-	lower(f, cond);
-
-	struct ejit_reloc branch = ejit_bnei(f->f, reg(cond), 0);
+	struct ejit_reloc branch = branch_if(f, cond, true);
 
 	struct ast *body = unless_body(n);
 	lower_list(f, body);
@@ -855,9 +943,7 @@ static void lower_unless_expr(struct fn *f, struct ast *n)
 	n->l = build_local_loc(f->sp);
 
 	struct ast *cond = unless_expr_cond(n);
-	lower(f, cond);
-
-	struct ejit_reloc branch = ejit_bnei(f->f, reg(cond), 0);
+	struct ejit_reloc branch = branch_if(f, cond, true);
 
 	struct ast *body = unless_expr_body(n);
 	lower(f, body);
@@ -983,7 +1069,7 @@ static void lower_proc_def(struct ast *d)
 	struct fn *f = vec_at(&fns, d->l.s);
 	assert(f);
 
-	struct vec formals = lower_formals(f, func_formals(d));
+	struct vec formals = lower_formals(f, proc_formals(d));
 	f->f = ejit_create_func(ejit_type_from(d->t), vec_len(&formals),
 	                        formals.buf);
 	f->sp = vec_len(&formals); f->max_sp = vec_len(&formals);
-- 
cgit v1.2.3