hare

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

gen.ha (6472B)


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