aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorlemon <lsof@mailbox.org>2022-08-04 10:07:13 +0200
committerlemon <lsof@mailbox.org>2022-08-04 10:07:13 +0200
commitc5d837d2cd9a57e453da9eaab0e41e0c185e084e (patch)
tree60539ae58df6738772f1e63e2aa58dd8002803e4
parentbb1d4b4a3e51a06fb0530dfc271a97a6cd88cc73 (diff)
fold & more
-rw-r--r--bootstrap/all.h17
-rw-r--r--bootstrap/cgen.c19
-rw-r--r--bootstrap/fold.c210
-rw-r--r--bootstrap/parse.c152
-rw-r--r--bootstrap/test.cff73
-rw-r--r--bootstrap/types.c122
6 files changed, 381 insertions, 212 deletions
diff --git a/bootstrap/all.h b/bootstrap/all.h
index 6b5fd7d..762e978 100644
--- a/bootstrap/all.h
+++ b/bootstrap/all.h
@@ -237,8 +237,11 @@ struct expr {
struct span span;
const struct type *ty;
union {
- u64 i; // also for bool lit
- double f;
+ union {
+ i64 i; // also for bool lit
+ u64 u;
+ double f;
+ };
struct {
const char *d;
u64 n;
@@ -405,6 +408,13 @@ bool typeeql(const struct type *lhs, const struct type *rhs);
bool completetype(const struct type *);
void putprimtypes(struct env *);
void visittypes(void (*visitor)(const struct type *, void *), void *arg);
+const struct type *constify(const struct type *ty);
+const struct type *unconstify(const struct type *ty);
+const struct type *constifychild(const struct type *ty);
+int numtype2rank(const struct type *a);
+const struct type * rank2numtype(int r);
+bool isnumtype(const struct type *a);
+const struct type *typeof2(const struct type *a, const struct type *b);
/** env.c **/
struct decl *envfind(const struct env *, const char *name);
@@ -419,3 +429,6 @@ void pritoktree(struct toktree);
/** cgen.c **/
void cgen(FILE *, const struct transunit *);
+
+/** fold.c **/
+int fold(struct expr *);
diff --git a/bootstrap/cgen.c b/bootstrap/cgen.c
index 938e8f3..4bc6280 100644
--- a/bootstrap/cgen.c
+++ b/bootstrap/cgen.c
@@ -87,6 +87,9 @@ pri(const char *fmt, ...) {
case 'U':
fprintf(outfp, "%llu", (unsigned long long)va_arg(ap, u64));
break;
+ case 'I':
+ fprintf(outfp, "%lld", (long long)va_arg(ap, u64));
+ break;
case 'z':
fprintf(outfp, "%zu", va_arg(ap, size_t));
break;
@@ -124,13 +127,15 @@ static void genblock(struct blockstmt block);
static void
genexpr(struct expr *ex) {
- const struct type *ty = ex->ty;
+ const struct type *ty = unconstify(ex->ty);
switch (ex->t) {
case Eintlit:
if (ty == ty_int)
- pri("%U", ex->i);
+ pri("%I", ex->i);
+ else if (ty == ty_u64)
+ pri("((uint64_t)%U)", ex->i);
else
- pri("((%t)%U)", ty, ex->i);
+ pri("((%t)%I)", ty, ex->i);
break;
case Eflolit:
pri("%f%s", ex->f, ty->size == 4 ? "f" : "");
@@ -378,7 +383,8 @@ liftnested(struct stmt *stmt) {
liftnested(blocktostmt(*stmt->iswitch.f));
break;
case Sreturn:
- liftnestedex(stmt->retex);
+ if (stmt->retex)
+ liftnestedex(stmt->retex);
break;
}
}
@@ -488,7 +494,10 @@ defctype(const struct type *ty, void *_) {
static void
prelude() {
pri("#include <stdint.h>\n"
- "#include <stddef.h>\n");
+ "#include <stddef.h>\n"
+ "#define not !\n"
+ "#define and &&\n"
+ "#define or ||\n");
}
void
diff --git a/bootstrap/fold.c b/bootstrap/fold.c
new file mode 100644
index 0000000..8f080a0
--- /dev/null
+++ b/bootstrap/fold.c
@@ -0,0 +1,210 @@
+#include "all.h"
+#include <stdint.h>
+
+int fold(struct expr *ex);
+
+static void
+numcast(struct expr *ex, const struct type *to) {
+ enum typetype t0 = ex->ty->t;
+ enum typetype t1 = to->t;
+ const struct type *from = ex->ty;
+ const struct type *uto = unconstify(to);
+ const struct type *ufrom = unconstify(from);
+ int size = to->size;
+ bool sgn = to->int_signed;
+ assert(t0 == TYint || t0 == TYfloat || t0 == TYbool);
+ assert(t1 == TYint || t1 == TYfloat || t1 == TYbool);
+
+ if (ufrom == uto)
+ /* pass */;
+ else if (t1 == TYint && t0 == TYfloat)
+ ex->i = from->size == 4 ? (float)ex->f : ex->f;
+ else if (t0 == TYfloat && t1 == TYfloat && size == 4)
+ ex->f = (float)ex->f;
+ else if (t0 == TYfloat && t1 == TYfloat && size == 8)
+ ex->f = (double)ex->f;
+ else if (t0 == TYbool)
+ ex->i = !!ex->i;
+ #define intint(siz,sgnd,Ty) \
+ else if (t0 == TYint && t1 == TYint && size == (siz) && sgn == (sgnd)) \
+ ex->i = (Ty)ex->i;
+ intint(1,0,uint8_t)
+ intint(1,1,int8_t)
+ intint(2,0,uint16_t)
+ intint(2,1,int16_t)
+ intint(4,0,uint32_t)
+ intint(4,1,int32_t)
+ intint(8,0,uint64_t)
+ intint(8,1,int64_t)
+ else if (uto == ty_f64 && ufrom == ty_u64)
+ ex->f = (u64)ex->i;
+ else if (uto == ty_f32 && ufrom == ty_u64)
+ ex->f = (float)(u64)ex->i;
+ else if (uto == ty_f64)
+ ex->f = ex->i;
+ else if (uto == ty_f32)
+ ex->f = (float)ex->i;
+ else if (uto == ty_bool && t0 == TYint)
+ ex->i = !!ex->i;
+ else if (ufrom == ty_bool && t1 == TYint)
+ ex->i = !!ex->i;
+ else
+ assert(0);
+
+ ex->ty = to;
+ ex->t = to->t == TYfloat ? Eflolit
+ : to->t == TYint ? Eintlit
+ : Eboolit;
+}
+
+static void
+unary(struct expr *ex) {
+ struct expr *r = ex->unop.child;
+ const struct type *ty = ex->ty;
+
+ if (!fold(r))
+ return;
+
+ switch (ex->unop.op) {
+ case '-':
+ *ex = *r;
+ ex->ty = ty;
+ if (r->ty->t == TYint)
+ ex->i = -ex->i;
+ else if (r->ty->t == TYfloat)
+ ex->f = -ex->f;
+ else assert(0);
+ free(r);
+ numcast(ex, ty);
+ break;
+ case '~':
+ *ex = *r;
+ ex->ty = ty;
+ assert(r->ty->t == TYint);
+ ex->i = ~ex->i;
+ numcast(ex, ty);
+ free(r);
+ break;
+ case 'not':
+ *ex = *r;
+ ex->ty = ty;
+ assert(ty->t == TYbool);
+ ex->i = !ex->i;
+ free(r);
+ break;
+ }
+}
+
+static void
+binop(struct expr *ex) {
+ struct expr *l = ex->binop.lhs;
+ struct expr *r = ex->binop.rhs;
+ const struct type *ty = ex->ty,
+ *uty = unconstify(ty);
+ int op = ex->binop.op;
+
+ if (!fold(l) || !fold(r))
+ return;
+
+ if (uty == ty_bool)
+ ty = typeof2(l->ty, r->ty);
+ numcast(l, ty);
+ numcast(r, ty);
+ if (op == '/' && r->ty->t == TYint && r->i == 0) // div/0
+ return;
+
+ if (op == '+' && ty->t == TYint) ex->i = l->i + r->i;
+ else if (op == '+' && ty->t == TYfloat) ex->i = l->f + r->f;
+ else if (op == '-' && ty->t == TYint) ex->i = l->i - r->i;
+ else if (op == '-' && ty->t == TYfloat) ex->i = l->f - r->f;
+ else if (op == '*' && uty == ty_u64) ex->i = (u64)l->i * (u64)r->i;
+ else if (op == '*' && ty->t == TYint) ex->i = l->i * r->i;
+ else if (op == '*' && ty->t == TYfloat) ex->i = l->f * r->f;
+ else if (op == '/' && uty == ty_u64) ex->i = (u64)l->i / (u64)r->i;
+ else if (op == '/' && ty->t == TYint) ex->i = l->i / r->i;
+ else if (op == '/' && ty->t == TYfloat) ex->i = l->f / r->f;
+ else if (op == '%' && uty == ty_u64) ex->i = (u64)l->i % (u64)r->i;
+ else if (op == '%' && ty->t == TYint) ex->i = l->i % r->i;
+ else if (op == '&' && ty->t == TYint) ex->i = l->i & r->i;
+ else if (op == '|' && ty->t == TYint) ex->i = l->i | r->i;
+ else if (op == '^' && ty->t == TYint) ex->i = l->i ^ r->i;
+ else if (op == '<<' && ty->t == TYint) ex->i = l->i << r->i;
+ else if (op == '>>' && ty->t == TYint && !ty->int_signed)
+ ex->i = (u64)l->i >> r->i;
+ else if (op == '>>' && ty->t == TYint) ex->i = l->i >> r->i;
+ else if (op == '==' && ty->t == TYfloat) ex->i = l->f == r->f;
+ else if (op == '==' && ty->t == TYint) ex->i = l->i == r->i;
+ else if (op == '<' && ty->t == TYfloat) ex->i = l->f < r->f;
+ else if (op == '<' && uty == ty_u64) ex->i = (u64)l->i < (u64)r->i;
+ else if (op == '<' && ty->t == TYint) ex->i = l->i < r->i;
+ else if (op == '>' && ty->t == TYfloat) ex->i = l->f > r->f;
+ else if (op == '>' && uty == ty_u64) ex->i = (u64)l->i > (u64)r->i;
+ else if (op == '>' && ty->t == TYint) ex->i = l->i > r->i;
+ else if (op == '<=' && ty->t == TYfloat) ex->i = l->f <= r->f;
+ else if (op == '<=' && uty == ty_u64) ex->i = (u64)l->i <= (u64)r->i;
+ else if (op == '<=' && ty->t == TYint) ex->i = l->i <= r->i;
+ else if (op == '>=' && ty->t == TYfloat) ex->i = l->f >= r->f;
+ else if (op == '>=' && uty == ty_u64) ex->i = (u64)l->i >= (u64)r->i;
+ else if (op == '>=' && ty->t == TYint) ex->i = l->i >= r->i;
+ else if (op == 'and') ex->i = l->i && r->i;
+ else if (op == 'or') ex->i = l->i || r->i;
+ else assert(0);
+
+ free(l);
+ free(r);
+ if (uty != ty_bool)
+ numcast(ex, ty);
+ else
+ ex->t = Eboolit;
+}
+
+static void
+cond(struct expr *ex) {
+ struct expr *test = ex->cond.test,
+ *t = ex->cond.t,
+ *f = ex->cond.f;
+ const struct type *ty = ex->ty;
+
+ if (!fold(test) || !fold(f) || !fold(t))
+ return;
+ assert(test->ty->t == TYbool);
+ numcast(f, ty);
+ numcast(t, ty);
+ if (test->i)
+ ex->i = t->i;
+ else
+ ex->i = f->i;
+ numcast(ex, ty);
+ free(test);
+ free(t);
+ free(f);
+}
+
+int
+fold(struct expr *ex) {
+ switch (ex->t) {
+ case Eintlit: case Eflolit: case Eboolit:
+ numcast(ex, ex->ty);
+ return 1;
+ case Estrlit:case Enullit:
+ return 1;
+ case Eprefix:
+ unary(ex);
+ break;
+ case Ebinop:
+ binop(ex);
+ break;
+ case Econd:
+ cond(ex);
+ default:
+ break;
+ }
+
+ switch (ex->t) {
+ case Eintlit: case Eflolit: case Estrlit:
+ case Eboolit: case Enullit:
+ return 1;
+ default:
+ return 0;
+ }
+}
diff --git a/bootstrap/parse.c b/bootstrap/parse.c
index 3d609c4..3cb6924 100644
--- a/bootstrap/parse.c
+++ b/bootstrap/parse.c
@@ -502,38 +502,6 @@ finddecl(struct parser *P, const char *name) {
static struct expr parseexpr(struct parser *P);
static const struct type *
-constify(const struct type *ty) {
- struct type ty2 = *ty;
- if (ty->konst)
- return ty;
- if (ty2.t != TYfn)
- ty2.konst = 1;
- return interntype(ty2);
-}
-
-static const struct type *
-unconstify(const struct type *ty) {
- struct type ty2 = *ty;
- if (!ty->konst)
- return ty;
- ty2.konst = 0;
- return interntype(ty2);
-}
-
-static const struct type *
-constifychild(const struct type *ty) {
- struct type ty2 = *ty;
- const struct type *child = constify(ty->child);
- if (child == ty->child)
- return ty;
- ty2.child = child;
- return interntype(ty2);
-}
-
-
-static const struct type * typeof2(const struct type *, const struct type *);
-
-static const struct type *
parsetype(struct parser *P) {
struct tok tok;
if (lexmatch(P, &tok, '*')) {
@@ -545,8 +513,12 @@ parsetype(struct parser *P) {
} else if (lexmatch(P, &tok, '[')) {
i64 length = -1;
if (!lexmatch(P, &tok, ']')) {
- length = lexexpect(P, TKintlit).ilit.i;
- assert(length >= 0);
+ struct expr ex = parseexpr(P);
+ if (!fold(&ex) || ex.t != Eintlit)
+ fatal(P, ex.span,
+ "array length should be a compile-time integral expression");
+ if ((length = ex.i) < 0)
+ fatal(P, ex.span, "negative array length");
lexexpect(P, ']');
}
return interntype((struct type) {
@@ -583,42 +555,16 @@ parsetype(struct parser *P) {
}
static const struct type *
-ilittype(struct parser *P, u64 i) {
- if (i <= INT_MAX)
+ilittype(struct parser *P, u64 n) {
+ if (n <= INT_MAX)
return ty_int;
- if (i <= INT32_MAX)
+ if (n <= INT32_MAX)
return ty_i32;
- if (i <= INT64_MAX)
+ if (n <= INT64_MAX)
return ty_i64;
return ty_u64;
}
-static int
-numtype2rank(const struct type *a) {
- if (a->t == TYint) {
- if (a->size < g_targ.intsize || a == ty_int)
- return 0;
- if (a == ty_uint) return 1;
- if (a == ty_i32) return 2;
- if (a == ty_u32) return 3;
- if (a == ty_i64) return 4;
- if (a == ty_u64) return 5;
- }
- if (a == ty_f32) return 6;
- if (a == ty_f64) return 7;
- return -1;
-}
-
-static const struct type *
-rank2numtype(int r) {
- static const struct type **types[] = {
- &ty_int, &ty_uint, &ty_i32, &ty_u32,
- &ty_i64, &ty_u64, &ty_f32, &ty_f64,
- };
- assert(r >= 0 && r < ARRAY_LENGTH(types));
- return *types[r];
-}
-
static const struct type *
numpromote(const struct type *ty) {
int r = numtype2rank(ty);
@@ -628,73 +574,6 @@ numpromote(const struct type *ty) {
}
static bool
-isnumtype(const struct type *a) {
- return a->t == TYint || a->t == TYfloat;
-}
-
-static const struct type *
-arraydecay(const struct type *ty) {
- struct type ty2 = *ty;
-
- if (ty->t != TYarr)
- return ty;
-
- ty2.t = TYptr;
- return interntype(ty2);
-}
-
-// peer type resolution
-static const struct type *
-typeof2(const struct type *a, const struct type *b) {
- if (isnumtype(a) && isnumtype(b)) {
- int ra = numtype2rank(a), rb = numtype2rank(b);
- return rank2numtype(MAX(ra, rb));
- }
- if (a == b)
- return a;
- if (unconstify(a) == b)
- return a;
- if (a == unconstify(b))
- return b;
- if (a->t == TYarr && b->t == TYarr)
- a = arraydecay(a);
- if (a->t == TYptr && b->t == TYarr)
- b = arraydecay(b);
- if (a->t == TYarr && b->t == TYptr)
- a = arraydecay(a);
- if (a->t == TYptr && b->t == TYptr) {
- bool akonst = a->child->konst,
- bkonst = b->child->konst;
- const struct type *uac = unconstify(a->child),
- *ubc = unconstify(b->child);
- if (uac == ubc)
- return akonst ? a : b;
- if (uac == ty_void) {
- if (bkonst && !akonst)
- return constifychild(a);
- return a;
- }
- if (ubc == ty_void) {
- if (akonst && !bkonst)
- return constifychild(b);
- return b;
- }
- if (uac == ty_u8) {
- if (bkonst && !akonst)
- return constifychild(a);
- return a;
- }
- if (ubc == ty_u8) {
- if (akonst && !bkonst)
- return constifychild(b);
- return b;
- }
-
- }
- return NULL;
-}
-
-static bool
islvalue(const struct expr *ex) {
if (ex->t == Ename)
return 1;
@@ -731,7 +610,7 @@ pexprimary(struct parser *P) {
ex.t = Eintlit;
ex.span = tok.span;
ex.i = tok.ilit.i;
- ex.ty = tok.ilit.ty ? tok.ilit.ty : ilittype(P, ex.i);
+ ex.ty = tok.ilit.ty ? tok.ilit.ty : ilittype(P, ex.u);
} else if (lexmatch(P, &tok, TKflolit)) {
ex.t = Eflolit;
ex.span = tok.span;
@@ -957,9 +836,10 @@ peeksbitarithop(struct parser *P, struct tok* tokp) {
if (tokp)
*tokp = tok;
switch (tok.t) {
- case '+': case '-': case '*': case '/': case '%':
+ case '+': case '-': case '*': case '/':
return 1;
- case '&': case '|': case '^': case '<<': case '>>':
+ case '%': case '&': case '|':
+ case '^': case '<<': case '>>':
return 2;
case '##':
return 3;
@@ -1206,6 +1086,10 @@ parsevardecl(struct parser *P, struct decl *decl) {
fatal(P, tok.span, "variable must be initialized");
}
}
+
+ if (ini && decl->t == Dstatic && !fold(ini))
+ fatal(P, ini->span, "static initializer isn't constant");
+
if (ini && !typeof2(ty, ini->ty))
fatal(P, tok.span, "incompatible initializer type");
diff --git a/bootstrap/test.cff b/bootstrap/test.cff
index 61fcbf3..1f426b8 100644
--- a/bootstrap/test.cff
+++ b/bootstrap/test.cff
@@ -1,76 +1,7 @@
-
-defmacro MAX(x, y) [ ((x) < (y) ? (y) : (x)) ]
-
-defmacro fmt(fmt, ...args) [ printf(fmt, args) ]
-defmacro add {
-(x) [ (x) ],
-(x, y, ...rest) [ (x) + add(y, rest) ]
-}
-
-defmacro swap(x, y) [
- (do
- let $x = &(x);
- let $y = &(y);
- let $z = *$x;
- *$x = *$y;
- *$y = $z;)
-]
-
-defmacro map {
-(f, x) [ f(x) ],
-(f, x, ...rest) [ f(x) map(f, rest) ]
-}
-
-defmacro printints_s(x) [ "%d " ## ]
-defmacro printints(...rest) [ printf(map(printints_s, rest) "\n", rest) ]
-
-
-fn fact(x usize) usize {
- fn f(acc usize, n usize) usize {
- return n == 0 ? acc : f(acc * n, n - 1);
- }
- return f(1, x);
-}
-
-defmacro lambda(tys, body) [
- (do
- fn $lam tys body
- &$lam;)
-]
-
-fn counter() int {
- static xs int = 0;
- extern static glob int;
- glob;
- return xs++;
-}
-
-extern static glob int = 42;
+typedef v3f [-(-1 * 2) + -(-8/~~5)]f32;
extern fn main (argc int, argv **u8) void {
extern fn printf(fmt *const u8, ...) int;
- fmt("%d\n", add(1, 2, 3, 4));
-
- let x = 0;
- let y = 7;
- switch y {
- case 0, 1 do
- printf("wow\n");
- case else
- printf("p\n");
- }
- printf("x: %d; y: %d\n", x, y);
- swap(x, y);
- printf("x: %d; y: %d\n", x, y);
- printf("fact(6) = %zu\n", fact(6));
-
- printints(1, 7, 8 + 9, x, x / (++y ^ 2));
- let z = (do
- printf("hi");
- x + 1;);
-
- let fo = lambda((x int) void, {});
-
- return (*fo)(0);
+ return;
}
diff --git a/bootstrap/types.c b/bootstrap/types.c
index 8e735d0..401f9e6 100644
--- a/bootstrap/types.c
+++ b/bootstrap/types.c
@@ -206,3 +206,125 @@ visittypes(void (*visitor)(const struct type *, void *), void *arg) {
for (struct typesnode *n = types.buckets[i]; n; n = n->next)
visitor(&n->ty, arg);
}
+
+const struct type *
+constify(const struct type *ty) {
+ struct type ty2 = *ty;
+ if (ty->konst)
+ return ty;
+ if (ty2.t != TYfn)
+ ty2.konst = 1;
+ return interntype(ty2);
+}
+
+const struct type *
+unconstify(const struct type *ty) {
+ struct type ty2 = *ty;
+ if (!ty->konst)
+ return ty;
+ ty2.konst = 0;
+ return interntype(ty2);
+}
+
+const struct type *
+constifychild(const struct type *ty) {
+ struct type ty2 = *ty;
+ const struct type *child = constify(ty->child);
+ if (child == ty->child)
+ return ty;
+ ty2.child = child;
+ return interntype(ty2);
+}
+
+static const struct type *
+arraydecay(const struct type *ty) {
+ struct type ty2 = *ty;
+
+ if (ty->t != TYarr)
+ return ty;
+
+ ty2.t = TYptr;
+ return interntype(ty2);
+}
+
+int
+numtype2rank(const struct type *a) {
+ if (a->t == TYint) {
+ if (a->size < g_targ.intsize || a == ty_int)
+ return 0;
+ if (a == ty_uint) return 1;
+ if (a == ty_i32) return 2;
+ if (a == ty_u32) return 3;
+ if (a == ty_i64) return 4;
+ if (a == ty_u64) return 5;
+ }
+ if (a == ty_f32) return 6;
+ if (a == ty_f64) return 7;
+ return -1;
+}
+
+const struct type *
+rank2numtype(int r) {
+ static const struct type **types[] = {
+ &ty_int, &ty_uint, &ty_i32, &ty_u32,
+ &ty_i64, &ty_u64, &ty_f32, &ty_f64,
+ };
+ assert(r >= 0 && r < ARRAY_LENGTH(types));
+ return *types[r];
+}
+
+bool
+isnumtype(const struct type *a) {
+ return a->t == TYint || a->t == TYfloat;
+}
+
+// peer type resolution
+const struct type *
+typeof2(const struct type *a, const struct type *b) {
+ if (isnumtype(a) && isnumtype(b)) {
+ int ra = numtype2rank(a), rb = numtype2rank(b);
+ return rank2numtype(MAX(ra, rb));
+ }
+ if (a == b)
+ return a;
+ if (unconstify(a) == b)
+ return a;
+ if (a == unconstify(b))
+ return b;
+ if (a->t == TYarr && b->t == TYarr)
+ a = arraydecay(a);
+ if (a->t == TYptr && b->t == TYarr)
+ b = arraydecay(b);
+ if (a->t == TYarr && b->t == TYptr)
+ a = arraydecay(a);
+ if (a->t == TYptr && b->t == TYptr) {
+ bool akonst = a->child->konst,
+ bkonst = b->child->konst;
+ const struct type *uac = unconstify(a->child),
+ *ubc = unconstify(b->child);
+ if (uac == ubc)
+ return akonst ? a : b;
+ if (uac == ty_void) {
+ if (bkonst && !akonst)
+ return constifychild(a);
+ return a;
+ }
+ if (ubc == ty_void) {
+ if (akonst && !bkonst)
+ return constifychild(b);
+ return b;
+ }
+ if (uac == ty_u8) {
+ if (bkonst && !akonst)
+ return constifychild(a);
+ return a;
+ }
+ if (ubc == ty_u8) {
+ if (akonst && !bkonst)
+ return constifychild(b);
+ return b;
+ }
+
+ }
+ return NULL;
+}