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 };