FreeBASIC  0.91.0
ast-node-iif.bas
Go to the documentation of this file.
1 '' AST conditional IF nodes
2 '' l = VAR access to the iif temp var
3 '' r = LINK( condexpr, LINK( truexpr, falsexpr ) )
4 
5 #include once "fb.bi"
6 #include once "fbint.bi"
7 #include once "ir.bi"
8 #include once "ast.bi"
9 #include once "rtl.bi"
10 
11 function hCheckTypes _
12  ( _
13  byval ldtype as integer, _
14  byval lsubtype as FBSYMBOL ptr, _
15  byval rdtype as integer, _
16  byval rsubtype as FBSYMBOL ptr, _
17  byref dtype as integer, _
18  byref subtype as FBSYMBOL ptr _
19  ) as integer
20 
21  dim as integer lmatch = any, rmatch = any, otherdtype = any
22 
23  function = FALSE
24 
25  '' Any pointers?
26  lmatch = typeIsPtr( ldtype )
27  rmatch = typeIsPtr( rdtype )
28  if( lmatch or rmatch ) then
29  '' If both are pointers, it must be the same pointer type,
30  '' otherwise we can't know which one to prefer, and an implicit
31  '' conversion to a common type like ANY PTR seems useless...
32 
33  if( lmatch and rmatch ) then
34  if( (typeGetDtAndPtrOnly( ldtype ) <> typeGetDtAndPtrOnly( rdtype )) or _
35  (lsubtype <> rsubtype) ) then
36  exit function
37  end if
38  dtype = ldtype
39  subtype = lsubtype
40  else
41  '' If only one is a ptr, use the ptr type as result,
42  '' and allow only integers on the other side.
43  '' At least something like
44  '' #define NULL 0
45  '' iif( cond, someptr, NULL )
46  '' should work... allowing floats seems useless though.
47  if( lmatch ) then
48  dtype = ldtype
49  subtype = lsubtype
50  otherdtype = rdtype
51  else
52  dtype = rdtype
53  subtype = rsubtype
54  otherdtype = ldtype
55  end if
56 
57  if( typeGetClass( otherdtype ) <> FB_DATACLASS_INTEGER ) then
58  exit function
59  end if
60 
61  '' no char/wchar though (they're also treated as
62  '' FB_DATACLASS_INTEGER), they are strings in iif().
63  select case( typeGetDtOnly( otherdtype ) )
64  case FB_DATATYPE_CHAR, FB_DATATYPE_WCHAR
65  exit function
66  end select
67  end if
68 
69  return TRUE
70  end if
71 
72  '' (Assuming no more pointers from here on)
73 
74  '' Any strings?
75  lmatch = (typeGetDtOnly( ldtype ) = FB_DATATYPE_STRING) or _
76  (typeGetDtOnly( ldtype ) = FB_DATATYPE_FIXSTR) or _
77  (typeGetDtOnly( ldtype ) = FB_DATATYPE_CHAR )
78  rmatch = (typeGetDtOnly( rdtype ) = FB_DATATYPE_STRING) or _
79  (typeGetDtOnly( rdtype ) = FB_DATATYPE_FIXSTR) or _
80  (typeGetDtOnly( rdtype ) = FB_DATATYPE_CHAR )
81  if( lmatch or rmatch ) then
82  '' If one is, both must be
83  if( lmatch <> rmatch ) then
84  exit function
85  end if
86  dtype = FB_DATATYPE_STRING
87  subtype = NULL
88  return TRUE
89  end if
90 
91  '' Any wstrings?
92  lmatch = (typeGetDtOnly( ldtype ) = FB_DATATYPE_WCHAR)
93  rmatch = (typeGetDtOnly( rdtype ) = FB_DATATYPE_WCHAR)
94  if( lmatch or rmatch ) then
95  '' If one is, both must be
96  if( lmatch <> rmatch ) then
97  exit function
98  end if
99  dtype = FB_DATATYPE_WCHAR
100  subtype = NULL
101  return TRUE
102  end if
103 
104  '' Any UDTs?
105  lmatch = (typeGetDtOnly( ldtype ) = FB_DATATYPE_STRUCT)
106  rmatch = (typeGetDtOnly( rdtype ) = FB_DATATYPE_STRUCT)
107  if( lmatch or rmatch ) then
108  '' If one is, both must be
109  if( lmatch <> rmatch ) then
110  exit function
111  end if
112 
113  '' And it must be the same UDT
114  if( lsubtype <> rsubtype ) then
115  exit function
116  end if
117 
118  dtype = FB_DATATYPE_STRUCT
119  subtype = lsubtype
120  return TRUE
121  end if
122 
123  '' Any enums?
124  lmatch = (typeGetDtOnly( ldtype ) = FB_DATATYPE_ENUM)
125  rmatch = (typeGetDtOnly( rdtype ) = FB_DATATYPE_ENUM)
126  if( lmatch or rmatch ) then
127  '' Both sides are enums?
128  if( lmatch and rmatch ) then
129  if( lsubtype = rsubtype ) then
130  '' Both sides are the same enum, preserve the enum type.
131  dtype = FB_DATATYPE_ENUM
132  subtype = lsubtype
133  else
134  '' Different enum, make result a generic integer,
135  '' since we don't know which enum to prefer.
136  dtype = FB_DATATYPE_INTEGER
137  subtype = NULL
138  end if
139  return TRUE
140  end if
141 
142  '' Only one side is an enum: it should be treated as integer
143  '' and then be combined with the other side as usual (max).
144  if( lmatch ) then
145  ldtype = FB_DATATYPE_INTEGER
146  lsubtype = NULL
147  else
148  rdtype = FB_DATATYPE_INTEGER
149  rsubtype = NULL
150  end if
151  end if
152 
153  '' Use the "max" of both types, like BOPs
154  typeMax( ldtype, lsubtype, rdtype, rsubtype, dtype, subtype )
155  function = TRUE
156 end function
157 
158 function astNewIIF _
159  ( _
160  byval condexpr as ASTNODE ptr, _
161  byval truexpr as ASTNODE ptr, _
162  byval truecookie as integer, _
163  byval falsexpr as ASTNODE ptr, _
164  byval falsecookie as integer _
165  ) as ASTNODE ptr
166 
167  dim as ASTNODE ptr n = any, varexpr = any
168  dim as integer dtype = any
169  dim as integer is_true_ctorcall = any, is_false_ctorcall = any
170  dim as integer call_true_defctor = any, call_false_defctor = any
171  dim as FBSYMBOL ptr falselabel = any, subtype = any, temp = any
172 
173  function = NULL
174 
175  if( condexpr = NULL ) then
176  exit function
177  end if
178 
179  '' Constant condition?
180  if( astIsCONST( condexpr ) ) then
181  '' Note: maybe the type checks should be done for this too,
182  '' but then what result type should something like
183  '' iif( 0, mystr, myfixstr )
184  '' produce? myfixstr is a fix-len string, it cannot just be
185  '' treated as a var-len string, and there is no temp var...
186  if( astConstEqZero( condexpr ) ) then
187  astDelTree( truexpr )
188  function = falsexpr
189  astDtorListUnscope( falsecookie )
190  else
191  astDelTree( falsexpr )
192  function = truexpr
193  astDtorListUnscope( truecookie )
194  end if
195  astDelTree( condexpr )
196  exit function
197  end if
198 
199  dtype = FB_DATATYPE_INVALID
200  subtype = NULL
201 
202  '' check types & find the iif() result type
203  if( hCheckTypes( truexpr->dtype, truexpr->subtype, _
204  falsexpr->dtype, falsexpr->subtype, _
205  dtype, subtype ) = FALSE ) then
206  exit function
207  end if
208 
209  '' Merge CONST bits
210  '' byte ptr, const byte ptr -> const byte ptr
211  dtype or= typeGetConstMask( truexpr->dtype ) or _
212  typeGetConstMask( falsexpr->dtype )
213 
214  falselabel = symbAddLabel( NULL )
215 
216  condexpr = astBuildBranch( condexpr, falselabel, FALSE, TRUE )
217  if( condexpr = NULL ) then
218  exit function
219  end if
220 
221  ' Special treatment for fixed-len/zstrings, promote to real FBSTRING
222  select case( typeGetDtAndPtrOnly( dtype ) )
223  case FB_DATATYPE_FIXSTR, FB_DATATYPE_CHAR
224  dtype = FB_DATATYPE_STRING
225  end select
226 
227  '' Note: Any changes to the true/false expressions must be enclosed
228  '' in astDtorListScopeBegin/astDtorListScopeEnd calls using the same
229  '' true/false cookies, until the astDtorListFlush()s below, to ensure
230  '' that any newly allocated temp vars end up in the same dtorlist scope
231  '' that was also used when the original true/false expressions were
232  '' being parsed (i.e. the same cookie must be used).
233 
234  if( typeGetDtAndPtrOnly( dtype ) = FB_DATATYPE_WCHAR ) then
235  '' Just like with SELECT CASE, for iif() on wstrings we need
236  '' a temporary wstring, but since the length of the iif() result
237  '' is unknown at compile-time it must be a dynamically allocated
238  '' buffer. For [z]strings we can use a normal STRING temp var,
239  '' but since there is no such thing for wstrings yet,
240  '' the "fake wstring", i.e. a wchar ptr, must be used.
241 
242  '' dim temp as wstring ptr (doesn't need to be cleared)
243  temp = symbAddTempVar( typeAddrOf( FB_DATATYPE_WCHAR ) )
244  symbSetIsWstring( temp )
245 
246  '' Register for cleanup at the end of the statement
247  astDtorListAdd( temp )
248 
249  varexpr = astBuildFakeWstringAccess( temp )
250 
251  astDtorListScopeBegin( truecookie )
252  '' Using AST_OPOPT_ISINI to get a WstrAssign() immediately,
253  '' as astOptAssignment() will miss it because it's nested
254  '' inside an IIF node
255  truexpr = astBuildFakeWstringAssign( temp, truexpr, AST_OPOPT_ISINI )
257 
258  astDtorListScopeBegin( falsecookie )
259  falsexpr = astBuildFakeWstringAssign( temp, falsexpr, AST_OPOPT_ISINI )
261  else
262  temp = symbAddTempVar( dtype, subtype )
263 
264  '' Register for cleanup at the end of the statement
265  astDtorListAdd( temp )
266 
267  varexpr = astNewVAR( temp )
268 
269  is_true_ctorcall = FALSE
270  is_false_ctorcall = FALSE
271  call_true_defctor = FALSE
272  call_false_defctor = FALSE
273 
274  '' Any constructors?
275  if( symbHasCtor( temp ) ) then
276  '' Try calling them, to construct the temp var from the true/false expressions.
277  astDtorListScopeBegin( truecookie )
278  truexpr = astBuildImplicitCtorCallEx( temp, truexpr , INVALID, is_true_ctorcall )
280 
281  astDtorListScopeBegin( falsecookie )
282  falsexpr = astBuildImplicitCtorCallEx( temp, falsexpr, INVALID, is_false_ctorcall )
284 
285  '' If the temp var can be constructed from the true/false expressions...
286  '' a) in both cases, just use these ctorcalls
287  '' b) in only one case (true or false), let's try to call a defctor in the other case
288  '' c) in no case at all, let's try to call a defctor once before the conditional branch
289 
290  if( is_true_ctorcall or is_false_ctorcall ) then
291  if( is_true_ctorcall ) then
292  astDtorListScopeBegin( truecookie )
293  truexpr = astPatchCtorCall( truexpr , astNewVAR( temp ) )
295  else
296  '' Do a normal assignment and call the defctor in front of it
297  call_true_defctor = TRUE
298  end if
299 
300  if( is_false_ctorcall ) then
301  astDtorListScopeBegin( falsecookie )
302  falsexpr = astPatchCtorCall( falsexpr, astNewVAR( temp ) )
304  else
305  '' Do a normal assignment and call the defctor in front of it
306  call_false_defctor = TRUE
307  end if
308  else
309  '' Insert defctor call in front of the conditional branch
310  condexpr = astNewLINK( astBuildCtorCall( subtype, astNewVAR( temp ) ), condexpr )
311  end if
312 
313  '' No defctor but it's needed?
314  if( symbHasDefCtor( temp ) = FALSE ) then
315  if( (is_true_ctorcall = FALSE) or (is_false_ctorcall = FALSE) ) then
316  exit function
317  end if
318  end if
319  end if
320 
321  '' Using AST_OPOPT_ISINI to get fb_StrInit()'s instead of fb_StrAssign()'s,
322  '' because those would require the temp string to be cleared manually...
323 
324  if( is_true_ctorcall = FALSE ) then
325  astDtorListScopeBegin( truecookie )
326  truexpr = astNewASSIGN( astNewVAR( temp ), truexpr , AST_OPOPT_ISINI )
327  if( call_true_defctor ) then
328  truexpr = astNewLINK( astBuildCtorCall( subtype, astNewVAR( temp ) ), truexpr )
329  end if
331  end if
332 
333  if( is_false_ctorcall = FALSE ) then
334  astDtorListScopeBegin( falsecookie )
335  falsexpr = astNewASSIGN( astNewVAR( temp ), falsexpr, AST_OPOPT_ISINI )
336  if( call_false_defctor ) then
337  falsexpr = astNewLINK( astBuildCtorCall( subtype, astNewVAR( temp ) ), falsexpr )
338  end if
340  end if
341  end if
342 
343  '' Update any remaining TYPEINIs (after the astNewASSIGN()s above,
344  '' which could solve out TYPEINIs for optimization) in the true/false
345  '' expressions, in case they have dtors, otherwise astAdd() later would
346  '' do that, causing the corresponding temp vars for TYPEINIs from both
347  '' true/false code paths to always be constructed and destructed.
348  astDtorListScopeBegin( truecookie )
349  truexpr = astTypeIniUpdate( truexpr )
351 
352  astDtorListScopeBegin( falsecookie )
353  falsexpr = astTypeIniUpdate( falsexpr )
355 
356  '' Add dtor calls to the true/false code paths, behind the assignments
357  '' to the iif temp var, so that any temp vars constructed inside the
358  '' true/false expressions themselves will only be destructed when their
359  '' code path was executed, but not when the other code path was chosen.
360  '' Zero (0) can be given for one or both cookies, in case the
361  '' corresponding expression won't have any temp var to destruct.
362  if( truecookie ) then
363  truexpr = astNewLINK( truexpr , astDtorListFlush( truecookie ) )
364  end if
365  if( falsecookie ) then
366  falsexpr = astNewLINK( falsexpr, astDtorListFlush( falsecookie ) )
367  end if
368 
369  n = astNewNode( AST_NODECLASS_IIF, dtype, subtype )
370 
371  n->sym = temp
372  n->l = varexpr
373  n->r = astNewLINK( condexpr, astNewLINK( truexpr, falsexpr ) )
374  n->iif.falselabel = falselabel
375 
376  function = n
377 end function
378 
379 function astLoadIIF( byval n as ASTNODE ptr ) as IRVREG ptr
380  dim as ASTNODE ptr condexpr = any, truexpr = any, falsexpr = any
381  dim as FBSYMBOL ptr exitlabel = any
382 
383  assert( n->r->class = AST_NODECLASS_LINK )
384  assert( n->r->r->class = AST_NODECLASS_LINK )
385 
386  condexpr = n->r->l
387  truexpr = n->r->r->l
388  falsexpr = n->r->r->r
389 
390  if( ast.doemit ) then
391  '' IR can't handle inter-blocks and live vregs atm, so any
392  '' register used must be spilled now or that could happen in a
393  '' function call done in any child trees and also if complex
394  '' expressions were used
395  '''''if( astIsClassOnTree( AST_NODECLASS_CALL, r->l ) <> NULL ) then
396  irEmitSPILLREGS( )
397  '''''end if
398  end if
399 
400  '' condition
401  astLoad( condexpr )
402  astDelNode( condexpr )
403 
404  exitlabel = symbAddLabel( NULL )
405 
406  '' true expr
407  astLoad( truexpr )
408  astDelNode( truexpr )
409 
410  if( ast.doemit ) then
411  irEmitBRANCH( AST_OP_JMP, exitlabel )
412  end if
413 
414  '' false expr
415  if( ast.doemit ) then
416  irEmitLABELNF( n->iif.falselabel )
417  end if
418 
419  if( ast.doemit ) then
420  '' see above
421  '''''if( astIsClassOnTree( AST_NODECLASS_CALL, r->r ) <> NULL ) then
422  irEmitSPILLREGS( )
423  '''''end if
424  end if
425 
426  astLoad( falsexpr )
427  astDelNode( falsexpr )
428 
429  if( ast.doemit ) then
430  '' exit
431  irEmitLABELNF( exitlabel )
432  end if
433 
434  '' Return the VAR access on the temp var
435  function = astLoad( n->l )
436  astDelNode( n->l )
437 
438  astDelNode( n->r->r )
439  astDelNode( n->r )
440 end function
441