hare

[hare] The Hare programming language
git clone https://git.torresjrjr.com/hare.git
Log | Files | Refs | README | LICENSE

gen.ha (6558B)


      1 // License: GPL-3.0
      2 // (c) 2022 Alexey Yerin <yyp@disroot.org>
      3 // (c) 2021 Drew DeVault <sir@cmpwn.com>
      4 // (c) 2021 Ember Sawady <ecs@d2evs.net>
      5 use fmt;
      6 use hare::ast;
      7 use hare::lex;
      8 use hare::module;
      9 use hare::types;
     10 use hare::types::{builtin};
     11 use hare::unit;
     12 use hare::unit::{object_kind};
     13 use io;
     14 use memio;
     15 use os;
     16 use strings;
     17 
     18 fn gen(out: io::handle, store: *types::typestore, unit: *unit::unit) void = {
     19 	// TODO: context_init
     20 	let ctx = context {
     21 		out = out,
     22 		buf = memio::dynamic(),
     23 		store = store,
     24 		unit = unit,
     25 		arch = struct {
     26 			// TODO: Initialize these properly
     27 			ptr: *qtype = &qlong,
     28 			sz: *qtype = &qlong,
     29 		},
     30 		...
     31 	};
     32 	defer io::close(&ctx.buf)!;
     33 	for (let i = 0z; i < len(unit.decls); i += 1) {
     34 		match (unit.decls[i].decl) {
     35 		case let func: unit::decl_func =>
     36 			gen_func(&ctx, &unit.decls[i]);
     37 		case => abort(); // TODO
     38 		};
     39 	};
     40 };
     41 
     42 fn gen_func(ctx: *context, decl: *unit::decl) void = {
     43 	// TODO: const fndecl = &decl.decl as *unit::decl_func;
     44 	const fndecl = decl.decl as unit::decl_func;
     45 	const body = match (fndecl.body) {
     46 	case let expr: *unit::expr =>
     47 		yield expr;
     48 	case null =>
     49 		return; // Prototype
     50 	};
     51 	const fntype = fndecl.prototype.repr as types::func;
     52 	assert(fntype.flags == 0);
     53 	ctx.serial = 0;
     54 
     55 	const ident = module::identuscore(fndecl.ident);
     56 	defer free(ident);
     57 	fmt::fprintf(ctx.out, "{}section \".text.{}\" \"ax\" function",
     58 		if (decl.exported) "export " else "", ident)!;
     59 	const rtype = fntype.result;
     60 	const has_rval =
     61 		match (types::dealias(rtype).repr) {
     62 		case let bi: types::builtin =>
     63 			yield bi != types::builtin::VOID;
     64 		case =>
     65 			yield true;
     66 		};
     67 	if (has_rval) {
     68 		const qrtype = qtype_lookup(ctx, rtype, false);
     69 		fmt::fprintf(ctx.out, " {}", qtype_repr(qrtype))!;
     70 	};
     71 	fmt::fprintf(ctx.out, " ${}(", ident)!;
     72 	assert(len(fntype.params) == 0); // TODO
     73 	fmt::fprintln(ctx.out, ") {")!;
     74 	fmt::fprintln(ctx.out, mklabel(ctx, "start"))!;
     75 
     76 	// We use a dynamic buffer here so that we can emit alloc instructions
     77 	// on-demand at the start of the function, rather than spread out
     78 	// through the body. This is more reliable on qbe's ARM backend, and
     79 	// generates better IL besides.
     80 	memio::reset(&ctx.buf);
     81 
     82 	fmt::fprintln(&ctx.buf, mklabel(ctx, "body"))!;
     83 
     84 	const rval = gen_expr(ctx, body);
     85 	if (!body.terminates) {
     86 		if (has_rval) {
     87 			emit(&ctx.buf, void, qinstr::RET, rval);
     88 		} else {
     89 			emit(&ctx.buf, void, qinstr::RET);
     90 		};
     91 	};
     92 
     93 	io::writeall(ctx.out, memio::buffer(&ctx.buf))!;
     94 	fmt::fprintln(ctx.out, "}\n")!;
     95 };
     96 
     97 fn gen_store(ctx: *context, object: value, value: value) void = {
     98 	match (types::dealias(object._type).repr) {
     99 	case let bi: types::builtin =>
    100 		switch (bi) {
    101 		case builtin::STR => abort(); // TODO
    102 		case => void;
    103 		};
    104 	case types::_struct => abort(); // TODO
    105 	case (types::array | types::slice | types::tagged | types::tuple) =>
    106 		abort(); // TODO
    107 	case => void; // Handled below
    108 	};
    109 	const instr = qinstr_store(ctx, object._type);
    110 	emit(&ctx.buf, void, instr, value, object);
    111 };
    112 
    113 fn gen_load(ctx: *context, object: value) value = {
    114 	match (types::dealias(object._type).repr) {
    115 	case let bi: types::builtin =>
    116 		switch (bi) {
    117 		case builtin::STR => abort(); // TODO
    118 		case => void;
    119 		};
    120 	case types::_struct => abort(); // TODO
    121 	case (types::array | types::slice | types::tagged | types::tuple) =>
    122 		abort(); // TODO
    123 	case => void; // Handled below
    124 	};
    125 	const instr = qinstr_load(ctx, object._type);
    126 	const temp = mktemp(ctx);
    127 	const ty = qtype_lookup(ctx, object._type, false);
    128 	emit(&ctx.buf, (ty, temp), instr, object);
    129 	return value {
    130 		value = temp,
    131 		_type = object._type,
    132 	};
    133 };
    134 
    135 fn gen_expr(ctx: *context, expr: *unit::expr) value = {
    136 	match (expr.expr) {
    137 	case unit::access =>
    138 		return gen_access(ctx, expr);
    139 	case unit::bindings =>
    140 		return gen_binding(ctx, expr);
    141 	case unit::compound =>
    142 		return gen_compound(ctx, expr);
    143 	case unit::constant =>
    144 		return gen_const(ctx, expr);
    145 	case unit::_return =>
    146 		return gen_return(ctx, expr);
    147 	};
    148 };
    149 
    150 fn gen_expr_at(ctx: *context, expr: *unit::expr, at: value) void = {
    151 	match (expr.expr) {
    152 	case => void; // TODO: Some functions will prefer _at
    153 	};
    154 	const value = gen_expr(ctx, expr);
    155 	gen_store(ctx, at, value);
    156 };
    157 
    158 fn gen_access_object(ctx: *context, object: *unit::object) value = {
    159 	switch (object.kind) {
    160 	case object_kind::CONST, object_kind::TYPE => abort();
    161 	case object_kind::BIND =>
    162 		const binding = binding_lookup(ctx, object);
    163 		return value {
    164 			value = binding.name,
    165 			_type = object._type,
    166 		};
    167 	case object_kind::DECL => abort(); // TODO
    168 	};
    169 };
    170 
    171 fn gen_access_addr(ctx: *context, expr: *unit::expr) value = {
    172 	match (expr.expr as unit::access) {
    173 	case let ao: unit::access_object =>
    174 		return gen_access_object(ctx, ao);
    175 	case let ai: unit::access_index => abort(); // TODO
    176 	case let af: unit::access_field => abort(); // TODO
    177 	case let at: unit::access_tuple => abort(); // TODO
    178 	};
    179 };
    180 
    181 fn gen_access(ctx: *context, expr: *unit::expr) value = {
    182 	const addr = gen_access_addr(ctx, expr);
    183 	return gen_load(ctx, addr);
    184 };
    185 
    186 fn gen_binding(ctx: *context, expr: *unit::expr) value = {
    187 	const bindings = expr.expr as unit::bindings;
    188 	for (let i = 0z; i < len(bindings); i += 1) {
    189 		const item = bindings[i];
    190 		const temp = mktemp(ctx);
    191 		append(ctx.bindings, binding {
    192 			object = item.object,
    193 			name = temp,
    194 		});
    195 		const value = value {
    196 			value = temp,
    197 			_type = item.object._type,
    198 		};
    199 		const instr = qinstr_alloc(value._type);
    200 		const lval = mklval(ctx, value);
    201 		emit(&ctx.buf, lval, instr, value._type.sz);
    202 		gen_expr_at(ctx, item.init, value);
    203 	};
    204 	return vvoid;
    205 };
    206 
    207 fn gen_compound(ctx: *context, expr: *unit::expr) value = {
    208 	const compound = expr.expr as unit::compound;
    209 	for (let i = 0z; i < len(compound); i += 1) {
    210 		gen_expr(ctx, compound[i]);
    211 	};
    212 	return vvoid; // TODO
    213 };
    214 
    215 fn gen_const(ctx: *context, expr: *unit::expr) value = {
    216 	const constexpr = expr.expr as unit::constant;
    217 	const val: qval = match (constexpr) {
    218 	case void =>
    219 		return vvoid;
    220 	case let b: bool =>
    221 		yield (if (b) 1u32 else 0u32): constant;
    222 	case ast::_null =>
    223 		yield 0u64; // XXX: Arch
    224 	case let r: rune =>
    225 		yield r: u32;
    226 	case let v: (u64 | f64) =>
    227 		yield v;
    228 	case let i: i64 =>
    229 		yield i: u64;
    230 	case let s: str => abort(); // TODO
    231 	// TODO: Aggregate types
    232 	};
    233 	return value {
    234 		value = val,
    235 		_type = expr.result,
    236 	};
    237 };
    238 
    239 fn gen_return(ctx: *context, expr: *unit::expr) value = {
    240 	const rexpr = expr.expr as unit::_return;
    241 	match (rexpr) {
    242 	case null =>
    243 		emit(&ctx.buf, void, qinstr::RET);
    244 	case let expr: *unit::expr =>
    245 		emit(&ctx.buf, void, qinstr::RET, gen_expr(ctx, expr));
    246 	};
    247 	return vvoid;
    248 };