aboutsummaryrefslogtreecommitdiff
path: root/src/irgen.cff
blob: 1ba080d3f2a697ecfc86fdab8a8953eea574b0b5 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
import "ir.hff";
import "common.hff";

extern fn ir_genstatic(IR *IRCtx, decl *Decl) void {
}

struct InstStream {
   IR *IRCtx,
   head *IRInst,
   tail *IRInst,
   fn push(S *InstStream, inst *IRInst) void {
      if S.tail {
         S.tail.next = inst;
      } else {
         S.head = inst;
      }
      S.tail = inst;
   }
}


fn mkinstr(S *InstStream, t IRInstT, args [#]IRArg) *IRInst {
   let inst *IRInst = S.IR.alloc->alloc(sizeof IRInst + (args.#len * sizeof IRArg), alignof IRInst);
   *inst = { .t: t };
   for let i = 0; i < args.#len; ++i {
      inst.args[i] = args[i];
   }
   return inst;
}

defmacro mkslice(Ty, ...args) [ (as([]Ty){args})[0::] ]

fn genblock(S *InstStream, block [#]Stmt) void;
fn genexpr(S *InstStream, ex *Expr) IRValue {
   static tmpid int = 0;

   // fold(ex);

   defmacro genunop(u,t) [ {
      let child = genexpr(S, (u).ex);
      let res = IRValue{ex.ty, .u: :Tmp(tmpid++)};
      S->push(mkinstr(S, (t), mkslice(IRArg, :Val(res), :Val(child))));
      return res;
   } ]

   defmacro genbinop(b,t) [ {
      let lhs = genexpr(S, (b).lhs);
      let rhs = genexpr(S, (b).rhs);
      let res = IRValue{ex.ty, .u: :Tmp(tmpid++)};
      S->push(mkinstr(S, (t), mkslice(IRArg, :Val(res), :Val(lhs), :Val(rhs))));
      return res;
   } ]

   switch ex.u {
   case IntLit i; return {ex.ty, :IImm(i.i)};
   case FloLit f; return {ex.ty, :FImm(f)};
   case StrLit s; return {ex.ty, :SImm(s)};
   case BoolLit b; return {ex.ty, :BImm(b)};
   case NullLit; return {ex.ty, :Null};
   case UnOp u;
      switch u.op {
      case :neg;   genunop(u, ex.ty->is(:Int) ? :neg : :fneg);
      case :compl; genunop(u, :compl);
      }
   case BinOp b;
      switch b.op {
      case  '+'; genbinop(b, ex.ty->is(:Int) ? :add : :fadd);
      case  '-'; genbinop(b, ex.ty->is(:Int) ? :sub : :fsub);
      case  '*'; genbinop(b, ex.ty->is(:Int) ? :mul : :fmul);
      case  '/'; genbinop(b, ex.ty->is(:Int) ? :div : :fdiv);
      case  '%'; genbinop(b, :mod);
      case  '&'; genbinop(b, :band);
      case  '|'; genbinop(b, :bor);
      case  '^'; genbinop(b, :xor);
      case '<<'; genbinop(b, :lsl);
      case '>>'; genbinop(b, ex.ty.u.Int.sgn ? :asr : :lsr);
      }
   case Call c;
      let n =  c.args.#len;
      let args [#]IRArg = (as(*IRArg)malloc((n + 2) * sizeof IRArg))[0::n + 2];
      defer free(args.#ptr);
      let res = IRValue{ex.ty, .u: :Tmp(tmpid++)};
      args[0] = :Val(res);
      if c.lhs.u.#tag == :Symbol {
         args[1] = :Fn(c.lhs.u.Symbol);
         for let i = 0; i < n; ++i {
            args[i + 2] = :Val(genexpr(S, &c.args[i]));
         }
         let inst = mkinstr(S, :call, args);
         inst.call_nargs = n;
         S->push(inst);
         return res;
      } else {
      }
      
   }
   assert(#f, "NYI ex");
}

fn genstmt(S *InstStream, stmt *Stmt) void {
   switch stmt.u {
   case Expr *ex;
      genexpr(S, ex);
   case else
      assert(#f, "NYI st");
   }
}

fn genblock(S *InstStream, block [#]Stmt) void {
   foreach_ptr(st, _, block) {
      genstmt(S, st);
   }
}

extern fn ir_genfn(IR *IRCtx, f *Fn) void {
   let stream InstStream = {.IR: IR};

   genblock(&stream, f.body.Some);
   stream->push(mkinstr(&stream, :ret0, {}));

   efmt("----------------------\n");
   efmt("function %s.%d:\n", container_of(f, Decl, u.Fn).name, f.id);
   irdump(stream.head);
}