1 //  Toy interpreter backend using gccjitd.
2 //
3 // Copyright (C) 2014-2015 Iain Buclaw.
4 // This program is free software; you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation; either version 3 of the License, or
7 // (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 // GNU General Public License for more details.
13 
14 // You should have received a copy of the GNU General Public License
15 // along with this program.  If not, see <http://www.gnu.org/licenses/>.
16 
17 // Written by Iain  Buclaw <ibuclaw@gdcproject.org>
18 
19 module toy.backend;
20 
21 import toy.ast;
22 
23 private import gccjit.d;
24 
25 /// Internal backend value exposed via alias.
26 alias BEValue = JITRValue;
27 
28 /// Backend visitor class.
29 class Backend
30 {
31     JITContext context;
32     JITFunction func;
33     JITBlock block;
34 
35     this()
36     {
37         this.context = new JITContext();
38         this.func = this.context.newFunction(JITFunctionKind.EXPORTED,
39                                              JITTypeKind.VOID, "toymain", false);
40         this.block = this.func.newBlock();
41         this.context.setOption(JITStrOption.PROGNAME, "toy");
42         debug this.context.setOption(JITBoolOption.DUMP_INITIAL_GIMPLE, true);
43     }
44 
45     void run()
46     {
47         this.block.endWithReturn();
48 
49         JITResult result = this.context.compile();
50         this.context.release();
51 
52         auto toymain = cast(void function()) result.getCode("toymain");
53         toymain();
54 
55         result.release();
56     }
57 
58     void compile(AssignStatement as)
59     {
60         JITLValue name = cast(JITLValue) as.name.compile(this);
61         JITRValue value = as.value.compile(this);
62         this.block.addAssignment(name, value);
63     }
64 
65     void compile(CompoundStatement cs)
66     {
67         if (cs.s1 !is null)
68             cs.s1.compile(this);
69         if (cs.s2 !is null)
70             cs.s2.compile(this);
71     }
72 
73     void compile(IfStatement ifs)
74     {
75         JITBlock condblock = this.func.newBlock();
76         JITBlock exitblock = this.func.newBlock();
77         JITBlock trueblock = this.func.newBlock();
78         JITBlock falseblock = ifs.elsebody ? this.func.newBlock() : null;
79 
80         this.block.endWithJump(condblock);
81         this.block = condblock;
82         JITRValue condition = ifs.condition.compile(this);
83         this.block.endWithConditional(condition, trueblock, falseblock ? falseblock : exitblock);
84 
85         this.block = trueblock;
86         ifs.ifbody.compile(this);
87         this.block.endWithJump(exitblock);
88 
89         if (falseblock !is null)
90         {
91             this.block = falseblock;
92             ifs.elsebody.compile(this);
93             this.block.endWithJump(exitblock);
94         }
95 
96         this.block = exitblock;
97     }
98 
99     void compile(WhileStatement ws)
100     {
101         JITBlock condblock = this.func.newBlock();
102         JITBlock exitblock = this.func.newBlock();
103         JITBlock loopblock = this.func.newBlock();
104 
105         this.block.endWithJump(condblock);
106         this.block = condblock;
107         JITRValue condition = ws.condition.compile(this);
108         this.block.endWithConditional(condition, loopblock, exitblock);
109 
110         this.block = loopblock;
111         ws.whilebody.compile(this);
112         this.block.endWithJump(condblock);
113 
114         this.block = exitblock;
115     }
116 
117     void compile(PrintStatement ps)
118     {
119         JITRValue value = ps.value.compile(this);
120         this.block.addCall(this.context.getBuiltinFunction("printf"),
121                            this.context.newRValue("%d\n"), value);
122     }
123 
124     BEValue compile(IntegerExp ie)
125     {
126         if (ie.bevalue is null)
127             ie.bevalue = this.context.newRValue(JITTypeKind.INT, ie.e1);
128         return ie.bevalue;
129     }
130 
131     BEValue compile(VarExp ve)
132     {
133         if (ve.bevalue is null)
134             ve.bevalue = this.func.newLocal(this.context.getType(JITTypeKind.INT), ve.e1);
135         return ve.bevalue;
136     }
137 
138     BEValue compile(BinExp be)
139     {
140         JITRValue e1 = be.e1.compile(this);
141         JITRValue e2 = be.e2.compile(this);
142         JITBinaryOp op;
143 
144         final switch (be.op)
145         {
146             case "/": op = JITBinaryOp.DIVIDE; break;
147             case "*": op = JITBinaryOp.MULT; break;
148             case "+": op = JITBinaryOp.PLUS; break;
149             case "-": op = JITBinaryOp.MINUS; break;
150         }
151         return this.context.newBinaryOp(op, e2.getType(), e1, e2);
152     }
153 
154     BEValue compile(CmpExp ce)
155     {
156         JITRValue e1 = ce.e1.compile(this);
157         JITRValue e2 = ce.e2.compile(this);
158         JITComparison op;
159 
160         final switch (ce.op)
161         {
162             case "=":   op = JITComparison.EQ; break;
163             case "!=":  op = JITComparison.NE; break;
164             case "<":   op = JITComparison.LT; break;
165             case "<=":  op = JITComparison.LE; break;
166             case ">":   op = JITComparison.GT; break;
167             case ">=":  op = JITComparison.GE; break;
168         }
169         return this.context.newComparison(op, e1, e2);
170     }
171 
172     BEValue compile(AndExp ae)
173     {
174         JITRValue e1 = ae.e1.compile(this);
175         JITRValue e2 = ae.e2.compile(this);
176         return this.context.newBinaryOp(JITBinaryOp.LOGICAL_AND, e2.getType(), e1, e2);
177     }
178 
179     BEValue compile(OrExp oe)
180     {
181         JITRValue e1 = oe.e1.compile(this);
182         JITRValue e2 = oe.e2.compile(this);
183         return this.context.newBinaryOp(JITBinaryOp.LOGICAL_OR, e2.getType(), e1, e2);
184     }
185 
186     BEValue compile(NotExp ne)
187     {
188         JITRValue e1 = ne.e1.compile(this);
189         return this.context.newUnaryOp(JITUnaryOp.LOGICAL_NEGATE, e1.getType(), e1);
190     }
191 }