FreeBASIC  0.91.0
ast-node-call.bas
Go to the documentation of this file.
1 '' AST function call nodes
2 '' l = pointer node if any; r = first arg to be pushed
3 ''
4 '' chng: sep/2004 written [v1ctor]
5 
6 
7 #include once "fb.bi"
8 #include once "fbint.bi"
9 #include once "list.bi"
10 #include once "ir.bi"
11 #include once "rtl.bi"
12 #include once "ast.bi"
13 
15  listInit( @ast.call.tmpstrlist, 32, len( AST_TMPSTRLIST_ITEM ), LIST_FLAGS_NOCLEAR )
16 end sub
17 
19  listEnd( @ast.call.tmpstrlist )
20 end sub
21 
22 '':::::
23 function astNewCALL _
24  ( _
25  byval sym as FBSYMBOL ptr, _
26  byval ptrexpr as ASTNODE ptr = NULL _
27  ) as ASTNODE ptr
28 
29  dim as ASTNODE ptr n = any
30  dim as FBRTLCALLBACK callback = any
31  dim as integer dtype = any
32  dim as FBSYMBOL ptr subtype = any
33 
34  assert( sym <> NULL )
35 
36  ''
37  dtype = symbGetFullType( sym )
38  subtype = symbGetSubType( sym )
39 
40  ''
41  symbSetIsAccessed( sym )
42 
43  '' alloc new node
44  n = astNewNode( AST_NODECLASS_CALL, dtype, subtype )
45  function = n
46 
47  n->sym = sym
48  n->l = ptrexpr
49  n->call.args = 0
50 
51  if( sym <> NULL ) then
52  n->call.currarg = symbGetProcHeadParam( sym )
53  n->call.isrtl = symbGetIsRTL( sym )
54 
55  callback = symbGetProcCallback( sym )
56  if( callback <> NULL ) then
57  callback( sym )
58  end if
59  else
60  n->call.currarg = NULL
61  n->call.isrtl = FALSE
62  end if
63 
64  n->call.argtail = NULL
65  n->call.strtail = NULL
66 
67  '' Allocate temp struct result if needed
68  if( symbProcReturnsOnStack( sym ) ) then
69  '' create a temp struct (can't be static, could be an object)
70  n->call.tmpres = symbAddTempVar( FB_DATATYPE_STRUCT, symbGetSubtype( sym ) )
71  astDtorListAdd( n->call.tmpres )
72  else
73  n->call.tmpres = NULL
74  end if
75 
76 end function
77 
78 '':::::
79 function astNewCALLCTOR _
80  ( _
81  byval procexpr as ASTNODE ptr, _
82  byval instptr as ASTNODE ptr _
83  ) as ASTNODE ptr
84 
85  dim as ASTNODE ptr n = any
86 
87  n = astNewNode( AST_NODECLASS_CALLCTOR, _
88  astGetFullType( instptr ), _
89  astGetSubtype( instptr ) )
90 
91  n->l = procexpr
92  n->r = instptr
93 
94  function = n
95 end function
96 
97 sub hCheckTmpStrings( byval f as ASTNODE ptr )
98  dim as ASTNODE ptr t = any
99  dim as AST_TMPSTRLIST_ITEM ptr n = any, p = any
100 
101  '' copy-back any fix-len string passed as parameter and
102  '' delete all temp strings used as parameters
103  n = f->call.strtail
104  do while( n <> NULL )
105 
106  '' copy back if needed
107  if( n->srctree <> NULL ) then
108  t = rtlStrAssign( n->srctree, astNewVAR( n->sym ) )
109  astLoad( t )
110  astDelNode( t )
111  end if
112 
113  '' delete the temp string (or wstring)
114  t = rtlStrDelete( astNewVAR( n->sym ) )
115  astLoad( t )
116  astDelNode( t )
117 
118  p = n->prev
119  listDelNode( @ast.call.tmpstrlist, n )
120  n = p
121  loop
122 end sub
123 
124 function astLoadCALL( byval n as ASTNODE ptr ) as IRVREG ptr
125  static as integer reclevel = 0
126  dim as ASTNODE ptr arg = any, nextarg = any, l = any
127  dim as FBSYMBOL ptr proc = any
128  dim as integer bytestopop = any, bytestoalign = any
129  dim as IRVREG ptr vr = any, v1 = any
130 
131  '' ARGs can contain CALLs themselves, then astLoadCALL() will recurse
132  reclevel += 1
133 
134  proc = n->sym
135 
136 #if 1
137  bytestoalign = 0
138 #else
139  '' Add extra stack alignment to ensure the stack pointer will be
140  '' aligned to a multiple of 16 after the arguments were pushed,
141  '' assuming our current stack pointer already is aligned that way.
142  bytestopop = 0
143  arg = n->r
144  while( arg )
145  l = arg->l
146  bytestopop += symbCalcArgLen( l->dtype, l->subtype, arg->arg.mode )
147  arg = arg->r
148  wend
149 
150  bytestoalign = (16 - (bytestopop and (16-1))) and (16-1)
151  if( bytestoalign > 0 ) then
152  if( ast.doemit ) then
153  irEmitSTACKALIGN( bytestoalign )
154  end if
155  end if
156 #endif
157 
158  '' Count up the size for the caller's stack clean up (after the call)
159  bytestopop = 0
160 
161  '' Push each argument
162  arg = n->r
163  while( arg )
164  nextarg = arg->r
165  l = arg->l
166 
167  '' cdecl: pushed arguments must be popped by caller
168  '' pascal/stdcall: callee does it instead
169  if( symbGetProcMode( proc ) = FB_FUNCMODE_CDECL ) then
170  bytestopop += symbCalcArgLen( l->dtype, l->subtype, arg->arg.mode )
171  end if
172 
173  if( l->class = AST_NODECLASS_CONV ) then
174  astUpdateCONVFD2FS( l, arg->dtype, FALSE )
175  end if
176 
177  '' flush the arg expression
178  v1 = astLoad( l )
179  astDelNode( l )
180 
181  if( ast.doemit ) then
182  irEmitPUSHARG( arg->sym, v1, arg->arg.lgt, reclevel )
183  end if
184 
185  astDelNode( arg )
186  arg = nextarg
187  wend
188 
189  '' Hidden param for functions returning big structs on stack
190  if( symbProcReturnsOnStack( proc ) ) then
191  '' Pop hidden ptr if cdecl and target doesn't want the callee
192  '' to do it, despite it being cdecl.
193  if( (symbGetProcMode( proc ) = FB_FUNCMODE_CDECL) and _
194  ((env.target.options and FB_TARGETOPT_CALLEEPOPSHIDDENPTR) = 0) ) then
195  bytestopop += env.pointersize
196  end if
197  if( ast.doemit ) then
198  '' Clear the temp struct (so the function can safely
199  '' do assignments to it in case it includes STRINGs),
200  '' unless it has a constructor (which the function will
201  '' call anyways).
202  if( symbHasCtor( n->call.tmpres ) = FALSE ) then
203  astLoad( astBuildTempVarClear( n->call.tmpres ) )
204  end if
205 
206  '' Pass the address of the temp result struct
207  l = astNewVAR( n->call.tmpres )
208  l = astNewADDROF( l )
209  v1 = astLoad( l )
210  '' (passing NULL param, because no PARAM symbol exists
211  '' for the hidden struct result param)
212  irEmitPUSHARG( NULL, v1, 0, reclevel )
213  end if
214  end if
215 
216  if( ast.doemit ) then
217  '' SUB or function result ignored?
218  if( astGetDataType( n ) = FB_DATATYPE_VOID ) then
219  vr = NULL
220  else
221  '' When returning BYREF the CALL's dtype should have
222  '' been remapped by astBuildByrefResultDeref()
223  assert( iif( symbProcReturnsByref( proc ), _
224  astGetDataType( n ) = typeGetDtAndPtrOnly( symbGetProcRealType( proc ) ), _
225  TRUE ) )
226 
227  vr = irAllocVREG( typeGetDtAndPtrOnly( symbGetProcRealType( proc ) ), _
228  symbGetProcRealSubtype( proc ) )
229 
230  if( proc->proc.returnMethod <> FB_RETURN_SSE ) then
231  vr->regFamily = IR_REG_FPU_STACK
232  end if
233  end if
234  end if
235 
236  '' caller always has to undo any stack alignment it did
237  bytestopop += bytestoalign
238  bytestoalign = 0
239 
240  '' function pointer?
241  l = n->l
242  if( l ) then
243  v1 = astLoad( l )
244  astDelNode( l )
245  if( ast.doemit ) then
246  irEmitCALLPTR( v1, vr, bytestopop, reclevel )
247  end if
248  else
249  if( ast.doemit ) then
250  irEmitCALLFUNCT( proc, bytestopop, vr, reclevel )
251  end if
252  end if
253 
254  '' del temp strings and copy back if needed
255  hCheckTmpStrings( n )
256 
257  reclevel -= 1
258 
259  function = vr
260 end function
261 
262 function astLoadCALLCTOR( byval n as ASTNODE ptr ) as IRVREG ptr
263  dim as IRVREG ptr vr = any
264 
265  '' flush the ctor CALL to initialize the temp var
266  astLoad( n->l )
267  astDelNode( n->l )
268 
269  '' return the VAR access to the temp var
270  vr = astLoad( n->r )
271  astDelNode( n->r )
272 
273  function = vr
274 end function
275 
276 '':::::
277 sub astCloneCALL _
278  ( _
279  byval n as ASTNODE ptr, _
280  byval c as ASTNODE ptr _
281  )
282 
283  '' temp string list
284  scope
285  dim as AST_TMPSTRLIST_ITEM ptr sn = any, sc = any
286 
287  c->call.strtail = NULL
288  sn = n->call.strtail
289  do while( sn <> NULL )
290  sc = listNewNode( @ast.call.tmpstrlist )
291 
292  sc->sym = sn->sym
293  sc->srctree = astCloneTree( sn->srctree )
294  sc->prev = c->call.strtail
295 
296  c->call.strtail = sc
297 
298  sn = sn->prev
299  loop
300  end scope
301 
302  '' Find the last ARG (if any); for each ARG...
303  n = c->r
304  while( n )
305  '' last one?
306  if( n->r = NULL ) then
307  exit while
308  end if
309  n = n->r
310  wend
311  c->call.argtail = n
312 
313 end sub
314 
315 '':::::
316 sub astDelCALL _
317  ( _
318  byval n as ASTNODE ptr _
319  )
320 
321  '' temp strings list
322  scope
323  dim as AST_TMPSTRLIST_ITEM ptr s = any, p = any
324  s = n->call.strtail
325  do while( s <> NULL )
326  p = s->prev
327 
328  astDelTree( s->srctree )
329 
330  listDelNode( @ast.call.tmpstrlist, s )
331  s = p
332  loop
333  end scope
334 
335 end sub
336 
337 '':::::
339  ( _
340  byval n as ASTNODE ptr, _
341  byval old_sym as FBSYMBOL ptr, _
342  byval new_sym as FBSYMBOL ptr _
343  )
344 
345  '' check temp res
346  if( n->call.tmpres = old_sym ) then
347  n->call.tmpres = new_sym
348  end if
349 
350  '' temp strings list
351  scope
352  dim as AST_TMPSTRLIST_ITEM ptr s = any
353  s = n->call.strtail
354  do while( s <> NULL )
355  if( s->sym = old_sym ) then
356  s->sym = new_sym
357  end if
358 
359  astReplaceSymbolOnTree( s->srctree, old_sym, new_sym )
360 
361  s = s->prev
362  loop
363  end scope
364 
365 end sub
366 
367 '' For accessing the temp result var allocated by CALLs that return on stack
368 function astBuildCallResultVar( byval expr as ASTNODE ptr ) as ASTNODE ptr
369  assert( astIsCALL( expr ) )
370  assert( symbProcReturnsOnStack( expr->sym ) )
371 
372  function = astNewLINK( expr, _
373  astNewVAR( expr->call.tmpres, 0, astGetFullType( expr ), astGetSubtype( expr ) ), _
374  FALSE ) '' CALL first, but return the VAR
375 end function
376 
377 '' For storing UDT CALL results into a temp var and accessing it
378 function astBuildCallResultUdt( byval expr as ASTNODE ptr ) as ASTNODE ptr
379  dim as FBSYMBOL ptr tmp = any
380 
381  assert( astIsCALL( expr ) )
382  assert( astGetDataType( expr ) = FB_DATATYPE_STRUCT )
383  assert( symbProcReturnsByref( expr->sym ) = FALSE )
384 
385  if( symbProcReturnsOnStack( expr->sym ) ) then
386  '' UDT returned in temp var already, just access that one
387  function = astBuildCallResultVar( expr )
388  else
389  '' UDT returned in registers, copy to a temp var to allow field accesses etc.
390  tmp = symbAddTempVar( FB_DATATYPE_STRUCT, expr->subtype )
391 
392  '' No need to bother doing astDtorListAdd()
393  assert( symbHasDtor( tmp ) = FALSE )
394 
395  expr = astNewASSIGN( astBuildVarField( tmp ), expr, AST_OPOPT_DONTCHKOPOVL )
396 
397  function = astNewLINK( expr, _
398  astBuildVarField( tmp ), _
399  FALSE ) '' ASSIGN first, but return the field access
400  end if
401 end function
402 
403 function astBuildByrefResultDeref( byval expr as ASTNODE ptr ) as ASTNODE ptr
404  dim as integer dtype = any
405  dim as FBSYMBOL ptr subtype = any
406 
407  '' Only for CALLs; it could be a CONST( 0 ) after error recovery in
408  '' cProcArgList()
409  if( astIsCALL( expr ) = FALSE ) then
410  return expr
411  end if
412 
413  '' And only if it actually has a BYREF result
414  if( symbProcReturnsByref( expr->sym ) = FALSE ) then
415  return expr
416  end if
417 
418  '' Do an implicit DEREF with the function's type, and remap the CALL
419  '' node's type to the pointer, so the AST is consistent even if that
420  '' DEREF gets optimized out.
421  ''
422  '' If the function type is a forward reference, we must show an error.
423  '' (essentially a function with BYREF AS FWDREF result cannot be
424  '' called until the fwdref was implemented)
425 
426  dtype = symbGetProcRealType( expr->sym )
427  subtype = symbGetProcRealSubtype( expr->sym )
428 
429  if( typeGetDtOnly( dtype ) = FB_DATATYPE_FWDREF ) then
430  errReport( FB_ERRMSG_INCOMPLETETYPE )
431  dtype = typeJoinDtOnly( dtype, FB_DATATYPE_INTEGER )
432  subtype = NULL
433  end if
434 
435  astSetType( expr, dtype, subtype )
436 
437  function = astNewDEREF( expr )
438 end function
439 
440 function astIsByrefResultDeref( byval expr as ASTNODE ptr ) as integer
441  function = FALSE
442  if( astIsDEREF( expr ) ) then
443  if( astIsCALL( expr->l ) ) then
444  function = symbProcReturnsByref( expr->l->sym )
445  end if
446  end if
447 end function
448 
449 function astRemoveByrefResultDeref( byval expr as ASTNODE ptr ) as ASTNODE ptr
450  assert( astIsByrefResultDeref( expr ) )
451  function = expr->l
452  astDelNode( expr )
453 end function
454