01cdde9cea98b5535b4563d3dd4bc79399e5c34a
[nit.git] / src / icode / icode_builder.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Copyright 2009 Jean Privat <jean@pryen.org>
4 #
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
8 #
9 # http://www.apache.org/licenses/LICENSE-2.0
10 #
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
16
17 # Help generation of icode
18 package icode_builder
19
20 import icode_base
21
22 # Helps to generate icodes in a iroutine
23 class ICodeBuilder
24 # Add a new statment in the current icode sequence
25 # Can be used with expression if the result is ignored of if the result is already set
26 fun stmt(s: ICode)
27 do
28 s.location = _current_location
29 _seq.icodes.add(s)
30 end
31
32 # Add a new expression in the current icode sequence and return a new associated result register
33 fun expr(e: ICode, s: MMType): IRegister
34 do
35 stmt(e)
36 assert e.result == null
37 var reg = new_register(s)
38 e.result = reg
39 return reg
40 end
41
42 # Add an assignement (IMove) in the current icode sequence
43 fun add_assignment(reg: IRegister, v: IRegister)
44 do
45 stmt(new IMove(reg, v))
46 end
47
48 # Check that the reciever e is not null (IIs + IAbort)
49 fun add_null_reciever_check(e: IRegister)
50 do
51 var nul = new_register(module.type_none)
52 var c = expr(new IIs(e, nul), module.type_bool)
53 var iif = new IIf(c)
54 stmt(iif)
55 var old_seq = seq
56 seq = iif.then_seq
57 add_abort("Reciever is null")
58 seq = old_seq
59 end
60
61 # Add a type cast (ITypeCheck + IAbort) in the current icode sequence
62 fun add_type_cast(e: IRegister, stype: MMType)
63 do
64 var c = expr(new ITypeCheck(e, stype), module.type_bool)
65 var iif = new IIf(c)
66 stmt(iif)
67 var old_seq = seq
68 seq = iif.else_seq
69 add_abort("Cast failed")
70 seq = old_seq
71 end
72
73 # Add an attr check (IAttrIsset + IAbort) in the current icode sequence
74 fun add_attr_check(prop: MMAttribute, e: IRegister)
75 do
76 if not prop.signature.return_type.is_nullable then
77 var cond = expr(new IAttrIsset(prop, e), module.type_bool)
78 var iif = new IIf(cond)
79 stmt(iif)
80 var seq_old = seq
81 seq = iif.else_seq
82 add_abort("Uninitialized attribute %s", prop.name.to_s)
83 seq = seq_old
84 end
85 end
86
87 # Add an IAttrRead guarded by an add_attr_check in the current icode sequence
88 fun add_attr_read(prop: MMAttribute, e: IRegister): IRegister
89 do
90 add_attr_check(prop, e)
91 return expr(new IAttrRead(prop, e), prop.signature.return_type.as(not null))
92 end
93
94 # Add a localized IAbort
95 fun add_abort(s: String...)
96 do
97 stmt(new IAbort(s, method, module))
98 end
99
100 # Add an assigment to the iroutine return value
101 # Beware, no jump is generated
102 fun add_return_value(reg: IRegister)
103 do
104 add_assignment(iroutine.result.as(not null), reg)
105 end
106
107 # Add an ICall with possible simple inlining in the current icode sequence
108 fun add_call(prop: MMMethod, args: Array[IRegister], closcns: nullable Array[nullable IClosureDef]): nullable IRegister
109 do
110 var ee = once "==".to_symbol
111
112 # Inline "x!=y" as "not x==y"
113 var ne = once "!=".to_symbol
114 if prop.name == ne then
115 var eqp = prop.signature.recv.local_class.select_method(ee)
116 var eqcall = add_call(eqp, args, closcns).as(not null)
117 return expr(new INot(eqcall), module.type_bool)
118 end
119
120 # TODO: Inline x==y as "x is y or (x != null and (== is not the Object one) and x.==(y))"
121 # inline "x==y" as "x is y or x != null and x.==(y)"
122 var icall = new ICall(prop, args)
123 icall.closure_defs = closcns
124 if prop.name == ee then
125 # Prepare the result
126 var reg = new_register(module.type_bool)
127 # "x is y"
128 var cond = expr(new IIs(args[0], args[1]), module.type_bool)
129 var iif = new IIf(cond)
130 stmt(iif)
131 var seq_old = seq
132 seq = iif.then_seq
133 add_assignment(reg, cond)
134 # "or"
135 seq = iif.else_seq
136 # Do the "x != null" part iff x is nullable
137 if args[0].stype.is_nullable then
138 var nul = new_register(module.type_none)
139 cond = expr(new IIs(args[0], nul), module.type_bool)
140 iif = new IIf(cond)
141 stmt(iif)
142 seq = iif.then_seq
143 add_assignment(reg, expr(new INative("TAG_Bool(false)", null), module.type_bool))
144 seq = iif.else_seq
145 end
146 # "x.==(y)"
147 add_assignment(reg, expr(icall, module.type_bool))
148 seq = seq_old
149 return reg
150 end
151
152 var rtype = prop.signature.return_type
153 if rtype != null then
154 return expr(icall, rtype)
155 else
156 stmt(icall)
157 return null
158 end
159 end
160
161 # Get a new register
162 fun new_register(s: MMType): IRegister
163 do
164 return new IRegister(s)
165 end
166
167 # The module where classes and types are extracted
168 readable var _module: MMModule
169
170 # The current iroutine build
171 readable var _iroutine: IRoutine
172
173 # The current sequence of icodes
174 readable writable var _seq: ISeq
175
176 # The method associated to the iroutine (if any)
177 readable var _method: nullable MMMethod
178
179 init(module: MMModule, r: IRoutine, m: nullable MMMethod)
180 do
181 _module = module
182 _current_location = r.location
183 _iroutine = r
184 _seq = r.body
185 _method = m
186 end
187
188 # New ICodes are set to this location
189 readable writable var _current_location: nullable Location = null
190 end
191
192 redef class MMSignature
193 # Create an empty IRoutine that match the signature
194 fun generate_empty_iroutine: IRoutine
195 do
196 var args = new Array[IRegister]
197 args.add(new IRegister(recv)) # Self
198 for i in [0..arity[ do
199 args.add(new IRegister(self[i]))
200 end
201 var res: nullable IRegister = null
202 var rtype = return_type
203 if rtype != null then
204 res = new IRegister(rtype)
205 end
206 var iroutine = new IRoutine(args, res)
207 var clos: nullable Array[IClosureDecl] = null
208 if not closures.is_empty then
209 clos = new Array[IClosureDecl]
210 for c in closures do
211 clos.add(new IClosureDecl(c))
212 end
213 iroutine.closure_decls = clos
214 end
215 return iroutine
216 end
217
218 # Create an empty IClosureDef that match the signature
219 fun generate_empty_iclosuredef: IClosureDef
220 do
221 var args = new Array[IRegister]
222 for i in [0..arity[ do
223 args.add(new IRegister(self[i]))
224 end
225 var res: nullable IRegister = null
226 var rtype = return_type
227 if rtype != null then
228 res = new IRegister(rtype)
229 end
230 var iroutine = new IClosureDef(args, res)
231 var clos: nullable Array[IClosureDecl] = null
232 if not closures.is_empty then
233 clos = new Array[IClosureDecl]
234 for c in closures do
235 clos.add(new IClosureDecl(c))
236 end
237 iroutine.closure_decls = clos
238 end
239 return iroutine
240 end
241 end
242