FreeBASIC  0.91.0
ir-hlc.bas
Go to the documentation of this file.
1 '' intermediate representation - high-level, direct to "C" output
2 ''
3 '' chng: dec/2006 written [v1ctor]
4 '' chng: apr/2008 function calling implemented / most operators implemented [sir_mud - sir_mud(at)users(dot)sourceforge(dot)net]
5 
6 #include once "fb.bi"
7 #include once "fbint.bi"
8 #include once "ir.bi"
9 #include once "rtl.bi"
10 #include once "flist.bi"
11 #include once "lex.bi"
12 
14  param as FBSYMBOL ptr
15  vr as IRVREG ptr
16  level as integer
17 end type
18 
19 '' The stack of nested sections allows us to go back and emit text to
20 '' the headers of parent sections, while already working on emitting
21 '' something else in an inner section.
22 '' (most commonly used for UDT declarations, which are only emitted
23 '' when they're needed by something else that's being emitted)
24 ''
25 '' index 0 is the "toplevel" section,
26 '' index 1 is the "body" where procedures are emitted into,
27 '' the rest is used for nested procedure/scope blocks.
28 ''
29 '' "body" is separate from "toplevel" to allow adding declarations to
30 '' "toplevel", while the procedures are appended to "body", one after
31 '' another. Then, once all procedures are emitted, "body" is closed,
32 '' and is appended to "toplevel". At that point we're done emitting
33 '' anyways and don't need to add stuff to toplevel's header anymore.
34 ''
35 '' This kind of container/body pair is not currently needed for procs/scopes,
36 '' because there we emit declarations "in line" instead of moving all to the
37 '' top of the scope. For the toplevel emitting all at once makes sense because
38 '' it is more efficient to check the symbol tables for called procedures only
39 '' once during _emitEnd() instead of once during every _emitProcBegin().
40 '' Note that _emitBegin() is called before parsing has even started,
41 '' so the global declarations can't be emitted from there already.
42 
44 
46  text as string
47  old as integer '' old junk text (that is only kept around to keep the string allocated)?
48  indent as integer '' current indendation level to be used when emitting lines into this section
49 end type
50 
51 enum
58 end enum
59 
61  class as integer '' EXPRCLASS_*
62 
63  '' This expression's type, to determine whether CASTs are needed or not
64  dtype as integer
65  subtype as FBSYMBOL ptr
66 
67  l as EXPRNODE ptr '' CAST/UOP/BOP
68  r as EXPRNODE ptr '' BOP
69 
70  union
71  text as zstring ptr '' TEXT
72  val as FBVALUE '' IMM
73  sym as FBSYMBOL ptr '' SYM
74  op as integer '' UOP/BOP
75  end union
76 end type
77 
79  '' Each cache entry associates an expression tree with a vreg id,
80  '' allowing expressions to be looked up for certain vreg accesses,
81  '' instead of having to be emitted as #defines or temp vars.
82  ''
83  '' Having a separate list for the cache is faster than cycling through
84  '' the whole ctx.exprnodes list. Often there will be only 1 (UOPs) or
85  '' 2 (BOPs) expression trees cached, since the AST usually accesses
86  '' expression results right when emitting the next expression/statement.
87  vregid as integer
88  expr as EXPRNODE ptr
89 end type
90 
93  section as integer '' Current section to write to
94  sectiongosublevel as integer
95 
96  regcnt as integer '' register counter used to name vregs
98  callargs as TLIST '' IRCALLARG's during emitPushArg/emitCall[Ptr]
99  linenum as integer
102 
103  anonstack as TLIST '' stack of nested anonymous structs/unions in a struct/union
104 
105  varini as string
106  variniscopelevel as integer
107 
108  fbctinf as string
109 
110  asm_line as string '' line of inline asm built up by _emitAsm*()
111  asm_i as integer '' next operand/symbol index
112  asm_output as string '' output constraints in gcc's syntax
113  asm_input as string '' input constraints in gcc's syntax
114 
115  exprnodes as TLIST '' EXPRNODE
116  exprtext as string '' buffer used by exprFlush() to build the final text
117  exprcache as TLIST '' EXPRCACHENODE
118 end type
119 
120 declare function hEmitType _
121  ( _
122  byval dtype as integer, _
123  byval subtype as FBSYMBOL ptr _
124  ) as string
125 
126 declare sub hEmitStruct( byval s as FBSYMBOL ptr, byval is_ptr as integer )
127 
128 declare sub _emitDBG _
129  ( _
130  byval op as integer, _
131  byval proc as FBSYMBOL ptr, _
132  byval ex as integer _
133  )
134 
135 declare sub exprFreeNode( byval n as EXPRNODE ptr )
136 #if __FB_DEBUG__
137 declare sub exprDump( byval n as EXPRNODE ptr )
138 #endif
139 
140 '' globals
141 dim shared as IRHLCCTX ctx
142 
143 '' same order as FB_DATATYPE
144 dim shared as const zstring ptr dtypeName(0 to FB_DATATYPES-1) = _
145 { _
146  @"void" , _ '' void
147  @"int8" , _ '' byte
148  @"uint8" , _ '' ubyte
149  NULL , _ '' char
150  @"int16" , _ '' short
151  @"uint16" , _ '' ushort
152  NULL , _ '' wchar
153  NULL , _ '' integer
154  NULL , _ '' uint
155  NULL , _ '' enum
156  NULL , _ '' bitfield
157  @"int32" , _ '' long
158  @"uint32" , _ '' ulong
159  @"int64" , _ '' longint
160  @"uint64" , _ '' ulongint
161  @"float" , _ '' single
162  @"double" , _ '' double
163  @"FBSTRING" , _ '' string
164  NULL , _ '' fix-len string
165  NULL , _ '' struct
166  NULL , _ '' namespace
167  NULL , _ '' function
168  @"void" , _ '' fwdref (needed for any un-resolved fwdrefs)
169  NULL _ '' pointer
170 }
171 
172 sub _init( )
173  flistInit( @ctx.vregTB, IR_INITVREGNODES, len( IRVREG ) )
174  listInit( @ctx.callargs, 32, sizeof( IRCALLARG ), LIST_FLAGS_NOCLEAR )
175  listInit( @ctx.anonstack, 8, sizeof( FBSYMBOL ptr ), LIST_FLAGS_NOCLEAR )
176  listInit( @ctx.exprnodes, 32, sizeof( EXPRNODE ), LIST_FLAGS_CLEAR )
177  listInit( @ctx.exprcache, 8, sizeof( EXPRCACHENODE ), LIST_FLAGS_NOCLEAR )
178  irSetOption( IR_OPT_FPUIMMEDIATES or IR_OPT_NOINLINEOPS )
179 
180  '' 64bit?
181  if( fbCpuTypeIs64bit( ) ) then
182  dtypeName(FB_DATATYPE_INTEGER) = @"int64"
183  dtypeName(FB_DATATYPE_UINT ) = @"uint64"
184  else
185  dtypeName(FB_DATATYPE_INTEGER) = @"int32"
186  dtypeName(FB_DATATYPE_UINT ) = @"uint32"
187  end if
188 end sub
189 
190 sub _end( )
191  listEnd( @ctx.exprcache )
192  listEnd( @ctx.exprnodes )
193  listEnd( @ctx.anonstack )
194  listEnd( @ctx.callargs )
195  flistEnd( @ctx.vregTB )
196 end sub
197 
198 '' "Begin/end" to be used to opening/closing sections whenever opening/closing
199 '' procs/scopes and also for the special sections 0 (header) and 1 (body).
201  ctx.section += 1
202  assert( ctx.section < MAX_SECTIONS )
203  '' Tell next hWriteLine() to overwrite instead of appending,
204  '' to overwrite pre-existing string data, keeping the string allocated
205  with( ctx.sections(ctx.section) )
206  .old = TRUE
207  if( ctx.section > 0 ) then
208  '' Use at least the parent section's indentation
209  '' (some emitting functions will temporarily increase
210  '' it for code nested inside {} etc.)
212  else
213  '' Start indendation at zero TAB's
214  .indent = 0
215  end if
216  end with
217 end sub
218 
219 '' Write line to current section (indentation & newline are automatically added)
220 sub sectionWriteLine( byval s as zstring ptr )
221  with( ctx.sections(ctx.section) )
222  if( .old ) then
223  if( .indent > 0 ) then
224  .text = string( .indent, TABCHAR )
225  .text += *s
226  else
227  .text = *s
228  end if
229  .old = FALSE
230  else
231  if( .indent > 0 ) then
232  .text += string( .indent, TABCHAR )
233  end if
234  .text += *s
235  end if
236  .text += NEWLINE
237  end with
238 end sub
239 
242 end sub
243 
245  assert( ctx.sections(ctx.section).indent > 0 )
247 end sub
248 
249 function sectionInsideProc( ) as integer
250  '' 0 and 1 are toplevel, 2+ means inside proc
251  function = (ctx.section >= 2)
252 end function
253 
255  dim as SECTIONENTRY ptr parent = any, child = any
256 
257  assert( ctx.section >= 0 )
258 
259  if( ctx.section > 0 ) then
260  '' Append to parent section, if anything was written
262  child = @ctx.sections(ctx.section)
263  if( child->old = FALSE ) then
264  if( parent->old ) then
265  parent->text = child->text
266  parent->old = FALSE
267  else
268  parent->text += child->text
269  end if
270  end if
271  end if
272 
273  ctx.section -= 1
274 end sub
275 
276 '' "Gosub" for temporarily writing to another section than the current one
277 function sectionGosub( byval section as integer ) as integer
278  assert( (section >= 0) and (section <= ctx.section) )
279  function = ctx.section
282 end function
283 
284 '' "Return" to restore the previous current section
285 sub sectionReturn( byval section as integer )
286  assert( ctx.sectiongosublevel > 0 )
289 end sub
290 
291 '' Main emitting function
292 '' Writes out line of code to current section, and adds #line's
293 sub hWriteLine _
294  ( _
295  byval s as zstring ptr, _
296  byval noline as integer = FALSE _
297  )
298 
299  static as string ln
300 
301  if( env.clopt.debug and (noline = FALSE) ) then
302  ln = "#line " + str( ctx.linenum )
303  ln += " """ + ctx.escapedinputfilename + """"
305  end if
306 
307  sectionWriteLine( s )
308 
309 end sub
310 
311 sub hWriteStaticAssert( byref expr as string )
312  dim as integer section = any
313 
314  if( ctx.static_assert_declared = FALSE ) then
316 
317  '' Emit the #define into the header section, not inside procedures
318  section = sectionGosub( 0 )
319  hWriteLine( "#define __FB_STATIC_ASSERT( expr ) extern int __$fb_structsizecheck[(expr) ? 1 : -1]", TRUE )
321  end if
322 
323  hWriteLine( "__FB_STATIC_ASSERT( " + expr + " );" )
324 end sub
325 
326 ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
327 
331 end enum
332 
333 sub hAppendCtorAttrib _
334  ( _
335  byref ln as string, _
336  byval proc as FBSYMBOL ptr, _
337  byval in_front as integer _
338  )
339 
340  dim as integer priority = any
341 
342  if( proc->stats and (FB_SYMBSTATS_GLOBALCTOR or FB_SYMBSTATS_GLOBALDTOR) ) then
343  if( in_front = FALSE ) then
344  ln += " "
345  end if
346  ln += "__attribute__(( "
347  if( proc->stats and FB_SYMBSTATS_GLOBALCTOR ) then
348  ln += "constructor"
349  else
350  ln += "destructor"
351  end if
352 
353  priority = symbGetProcPriority( proc )
354  if( priority <> 0 ) then
355  ln += "( " + str( priority ) + " )"
356  end if
357 
358  ln += " ))"
359  if( in_front ) then
360  ln += " "
361  end if
362  end if
363 end sub
364 
365 function hEmitProcHeader _
366  ( _
367  byval proc as FBSYMBOL ptr, _
368  byval options as EMITPROC_OPTIONS _
369  ) as string
370 
371  dim as string ln, mangled
372 
373  if( options = 0 ) then
374  '' ctor/dtor flags on bodies
375  hAppendCtorAttrib( ln, proc, TRUE )
376  end if
377 
378  if( (options and EMITPROC_ISPROCPTR) = 0 ) then
379  if( env.clopt.export and (env.target.options and FB_TARGETOPT_EXPORT) ) then
380  if( symbIsExport( proc ) ) then
381  ln += "__declspec( dllexport ) "
382  end if
383  end if
384 
385  if( symbIsPrivate( proc ) ) then
386  ln += "static "
387  end if
388  end if
389 
390  '' Function result type (is 'void' for subs)
391  ln += hEmitType( typeGetDtAndPtrOnly( symbGetProcRealType( proc ) ), _
392  symbGetProcRealSubtype( proc ) )
393 
394  ''
395  '' Calling convention if needed (for function pointers it's usually not
396  '' put in this place, but should work nonetheless)
397  ''
398  '' Note: Pascal is like Stdcall (callee cleans up stack), except that
399  '' arguments are pushed left-to-right (same order as written in code,
400  '' not reversed like Cdecl/Stdcall).
401  '' The symbGetProc*Param() macros take care of changing the order when
402  '' cycling through parameters of Pascal functions. Together with Stdcall
403  '' this results in a double-reverse resulting in the proper ABI.
404  ''
405  '' For non-x86, don't emit any calling convention at all, it would just
406  '' be ignored anyways (for x86_64 and ARM it seems that way at least).
407  ''
408  if( fbCpuTypeIsX86( ) ) then
409  select case( symbGetProcMode( proc ) )
410  case FB_FUNCMODE_STDCALL, FB_FUNCMODE_STDCALL_MS, FB_FUNCMODE_PASCAL
411  select case( env.clopt.target )
412  case FB_COMPTARGET_WIN32, FB_COMPTARGET_XBOX
413  '' MinGW recognizes this shorter & prettier version
414  ln += " __stdcall"
415  case else
416  '' Linux GCC only accepts this
417  ln += " __attribute__((stdcall))"
418  end select
419  end select
420  end if
421 
422  ln += " "
423 
424  mangled = *symbGetMangledName( proc )
425 
426  '' Identifier
427  if( options and EMITPROC_ISPROCPTR ) then
428  ln += "(*"
429  ln += mangled
430  ln += ")"
431  else
432  ln += mangled
433  end if
434 
435  '' Parameter list
436  ln += "( "
437 
438  '' If returning a struct, there's an extra parameter
439  dim as FBSYMBOL ptr hidden = NULL
440  if( symbProcReturnsOnStack( proc ) ) then
441  if( options and EMITPROC_ISPROTO ) then
442  hidden = symbGetSubType( proc )
443  ln += hEmitType( typeAddrOf( symbGetType( hidden ) ), hidden )
444  else
445  hidden = proc->proc.ext->res
446  ln += hEmitType( typeAddrOf( symbGetType( hidden ) ), symbGetSubtype( hidden ) )
447  ln += " " + *symbGetMangledName( hidden )
448  end if
449 
450  if( symbGetProcParams( proc ) > 0 ) then
451  ln += ", "
452  end if
453  end if
454 
455  var param = symbGetProcLastParam( proc )
456 
457  if( (hidden = NULL) and (param = NULL) ) then
458  ln += "void"
459  end if
460 
461  while( param )
462  if( symbGetParamMode( param ) = FB_PARAMMODE_VARARG ) then
463  ln += "..."
464  else
465  var dtype = symbGetType( param )
466  var subtype = param->subtype
468  ln += hEmitType( dtype, subtype )
469 
470  if( (options and EMITPROC_ISPROTO) = 0 ) then
471  ln += " " + *symbGetMangledName( symbGetParamVar( param ) )
472  end if
473  end if
474 
475  param = symbGetProcPrevParam( proc, param )
476  if( param ) then
477  ln += ", "
478  end if
479  wend
480 
481  ln += " )"
482 
483  if( ((options and EMITPROC_ISPROCPTR) = 0) and _
484  ((options and EMITPROC_ISPROTO) <> 0) ) then
485 #if 0
486  '' Add an extra <asm("mangledname")> to prevent gcc
487  '' from adding the stdcall @N suffix. asm() can only
488  '' be used on prototypes.
489  select case( symbGetProcMode( proc ) )
490  case FB_FUNCMODE_STDCALL_MS, FB_FUNCMODE_PASCAL
491  '' Must manually add an underscore prefix if the
492  '' target requires it, because symb-mangling
493  '' won't do that for -gen gcc.
494  if( env.target.options and FB_TARGETOPT_UNDERSCORE ) then
495  mangled = "_" + mangled
496  end if
497  ln += " asm(""" + mangled + """)"
498  end select
499 #endif
500  '' ctor/dtor flags on prototypes
502  end if
503 
504  function = ln
505 end function
506 
507 function hGetUdtTag( byval sym as FBSYMBOL ptr ) as string
508  if( symbIsStruct( sym ) ) then
509  if( symbGetUDTIsUnion( sym ) ) then
510  function = "union "
511  else
512  function = "struct "
513  end if
514  end if
515 end function
516 
517 function hGetUdtId( byval sym as FBSYMBOL ptr ) as string
518  '' Prefixing the mangled name with a $ because it may start with a
519  '' number which isn't allowed in C.
520  function = "$" + *symbGetMangledName( sym )
521 end function
522 
523 function hGetUdtName( byval sym as FBSYMBOL ptr ) as string
524  function = hGetUdtTag( sym ) + hGetUdtId( sym )
525 end function
526 
527 sub hEmitEnum( byval s as FBSYMBOL ptr )
528  dim as string ln
529 
530  symbSetIsEmitted( s )
531 
532  ln = "typedef "
533  '' no subtype, to avoid infinite recursion
534  ln += hEmitType( FB_DATATYPE_ENUM, NULL )
535  ln += " "
536  ln += hGetUdtName( s )
537  ln += ";"
538  hWriteLine( ln )
539 end sub
540 
541 sub hEmitUDT( byval s as FBSYMBOL ptr, byval is_ptr as integer )
542  dim as integer section = any
543 
544  if( s = NULL ) then
545  return
546  end if
547 
548  if( symbGetIsEmitted( s ) ) then
549  return
550  end if
551 
552  if( symbIsLocal( s ) ) then
553  '' Write declaration to corresponding scope
554  '' (FB_MAINSCOPE=0 maps to section index 1)
555  section = 1 + symbGetScope( s )
556 
557  '' Local to FB main? Convert to explicit main() function...
558  '' (should only happen while emitting main(), since we won't
559  '' see main's locals from elsewhere)
560  if( symbGetScope( s ) = FB_MAINSCOPE ) then
561  section += 1
562  end if
563 
564  '' Switching from a parent to a child scope isn't allowed,
565  '' the UDT declaration will be forced to be emitted in the
566  '' parent scope anyways, since apparently that's where we
567  '' need it. (used by _procAllocStaticVars())
568  if( section > ctx.section ) then
570  end if
571  else
572  '' Write to toplevel
573  section = 0
574  end if
575 
577 
578  select case as const symbGetClass( s )
579  case FB_SYMBCLASS_ENUM
580  hEmitEnum( s )
581 
582  case FB_SYMBCLASS_STRUCT
583  hEmitStruct( s, is_ptr )
584 
585  case FB_SYMBCLASS_PROC
586  if( symbGetIsFuncPtr( s ) ) then
587  hWriteLine( "typedef " + hEmitProcHeader( s, EMITPROC_ISPROTO or EMITPROC_ISPROCPTR ) + ";" )
588  symbSetIsEmitted( s )
589  end if
590 
591  end select
592 
594 end sub
595 
596 '' Returns "[N]" (N = array size) if the symbol is an array or a fixlen string.
597 function hEmitArrayDecl( byval sym as FBSYMBOL ptr ) as string
598  dim as string s
599 
600  '' Emit all array dimensions individually
601  '' (This lets array initializers rely on gcc to fill uninitialized
602  '' elements with zeroes)
603  select case( symbGetClass( sym ) )
604  case FB_SYMBCLASS_VAR, FB_SYMBCLASS_FIELD
605  if( (symbGetIsDynamic( sym ) = FALSE) and _
606  (symbGetArrayDimensions( sym ) <> 0) ) then
607  dim as FBVARDIM ptr d = symbGetArrayFirstDim( sym )
608  while( d )
609  '' elements = ubound( array, d ) - lbound( array, d ) + 1
610  s += "[" + str( d->upper - d->lower + 1 ) + "]"
611  d = d->next
612  wend
613  end if
614  end select
615 
616  '' If it's a fixed-length string, add an extra array dimension
617  '' (zstring * 5 becomes char[5])
618  dim as longint length = 0
619  select case( symbGetType( sym ) )
620  case FB_DATATYPE_FIXSTR, FB_DATATYPE_CHAR
621  length = symbGetStrLen( sym )
622  case FB_DATATYPE_WCHAR
623  length = symbGetWstrLen( sym )
624  end select
625  if( length > 0 ) then
626  s += "[" + str( length ) + "]"
627  end if
628 
629  function = s
630 end function
631 
632 sub hEmitVar( byval sym as FBSYMBOL ptr, byval varini as zstring ptr )
633  dim as string ln
634 
635  '' Never used?
636  if( symbGetIsAccessed( sym ) = FALSE ) then
637  '' Extern?
638  if( symbIsExtern( sym ) ) then
639  return
640  end if
641  end if
642 
643  '' Shared (not Local) or Static, but not Common/Public/Extern?
644  if( ((symbGetAttrib( sym ) and (FB_SYMBATTRIB_COMMON or FB_SYMBATTRIB_PUBLIC or FB_SYMBATTRIB_EXTERN)) = 0) and _
645  ((not symbIsLocal( sym )) or symbIsStatic( sym )) ) then
646  ln += "static "
647  end if
648 
649  ln += hEmitType( symbGetType( sym ), symbGetSubType( sym ) )
650  ln += " " + *symbGetMangledName( sym )
651  ln += hEmitArrayDecl( sym )
652 
653  if( symbIsImport( sym ) ) then
654  ln += " __attribute__((dllimport))"
655  end if
656 
657  '' allocation modifier
658  if( symbGetAttrib( sym ) and (FB_SYMBATTRIB_COMMON or FB_SYMBATTRIB_PUBLIC or FB_SYMBATTRIB_EXTERN) ) then
659  hWriteLine( "extern " + ln + ";" )
660  if( symbIsCommon( sym ) ) then
661  ln += " __attribute__((common))"
662  elseif( symbIsExtern( sym ) ) then
663  '' Just an Extern that's used but not allocated in this module
664  return
665  end if
666  end if
667 
668  if( varini ) then
669  ln += " = " + *varini
670  end if
671 
672  hWriteLine( ln + ";" )
673 end sub
674 
675 sub hEmitVariable( byval s as FBSYMBOL ptr )
676  '' already allocated?
677  if( symbGetVarIsAllocated( s ) ) then
678  return
679  end if
680 
681  symbSetVarIsAllocated( s )
682 
683  '' literal? don't emit..
684  if( symbGetIsLiteral( s ) ) then
685  return
686  end if
687 
688  '' initialized? only if not local or local and static
689  if( symbGetIsInitialized( s ) and (symbIsLocal( s ) = FALSE or symbIsStatic( s )) ) then
690  '' extern?
691  if( symbIsExtern( s ) ) then
692  return
693  end if
694 
695  '' never referenced?
696  if( symbIsLocal( s ) = FALSE ) then
697  if( symbGetIsAccessed( s ) = FALSE ) then
698  '' not public?
699  if( symbIsPublic( s ) = FALSE ) then
700  return
701  end if
702  end if
703  end if
704 
705  astTypeIniFlush( s->var_.initree, s, AST_INIOPT_ISINI or AST_INIOPT_ISSTATIC )
706 
707  s->var_.initree = NULL
708  return
709  end if
710 
711  '' dynamic? only the array descriptor is emitted
712  if( symbGetIsDynamic( s ) ) then
713  return
714  end if
715 
716  '' a string or array descriptor?
717  if( symbGetLen( s ) <= 0 ) then
718  return
719  end if
720 
721  hEmitVar( s, NULL )
722 end sub
723 
724 sub hEmitGccBuiltinWrapper( byval sym as FBSYMBOL ptr )
725  dim as integer count = any
726  dim as FBSYMBOL ptr param = any
727  dim as string params
728 
729  count = 0
730  param = symbGetProcLastParam( sym )
731 
732  while( param )
733  params += "temp_ppparam$" + str( count )
734 
735  param = symbGetProcPrevParam( sym, param )
736  if( param ) then
737  params += ", "
738  end if
739 
740  count += 1
741  wend
742 
743  params = *symbGetMangledName( sym ) + "( " + params + " )"
744 
745  hWriteLine( "#define " + params + " __builtin_" + params, TRUE )
746 end sub
747 
748 sub hEmitFuncProto( byval s as FBSYMBOL ptr )
749  dim as integer section = any
750 
751  if( symbGetIsAccessed( s ) = FALSE ) then
752  return
753  end if
754 
755  if( symbGetMangledName( s ) = NULL ) then
756  return
757  end if
758 
759  '' One of our built-in FTOI routines? Those are declared by
760  '' hEmitFTOIBuiltins(), not here.
761  if( symbGetIsIrHlcBuiltin( s ) ) then
762  return
763  end if
764 
765  '' All procedure declarations go into the toplevel header
766  section = sectionGosub( 0 )
767 
768  '' gcc builtin? gen a wrapper..
769  if( symbGetIsGccBuiltin( s ) ) then
771  else
773  end if
774 
776 end sub
777 
779  ( _
780  byval fld as FBSYMBOL ptr _
781  ) as FBSYMBOL ptr ptr
782 
783  dim as FBSYMBOL ptr ptr anonnode = any
784  dim as FBSYMBOL ptr parent = any
785 
786  '' For each parent, starting with the inner-most...
787  parent = fld->parent
788  do
789  '' Check whether it's already on the stack...
790  anonnode = listGetTail( @ctx.anonstack )
791  while( anonnode )
792  if( *anonnode = parent ) then
793  return anonnode
794  end if
795  anonnode = listGetPrev( anonnode )
796  wend
797 
798  parent = parent->parent
799  loop while( parent )
800 
801  function = NULL
802 end function
803 
804 sub hPushAnonParents _
805  ( _
806  byval baseparent as FBSYMBOL ptr, _
807  byval parent as FBSYMBOL ptr _
808  )
809 
810  if( parent = baseparent ) then
811  exit sub
812  end if
813 
814  '' Recurse
815  hPushAnonParents( baseparent, parent->parent )
816 
817  '' Push parents in top-down order
818  assert( symbIsStruct( parent ) )
819  if( symbGetUDTIsUnion( parent ) ) then
820  hWriteLine( "union {", TRUE )
821  else
822  hWriteLine( "struct {", TRUE )
823  end if
824  sectionIndent( )
825  *cptr( FBSYMBOL ptr ptr, listNewNode( @ctx.anonstack ) ) = parent
826 
827 end sub
828 
829 sub hPopAnonParents( byval anonnode as FBSYMBOL ptr ptr )
830  while( listGetTail( @ctx.anonstack ) <> anonnode )
831  sectionUnindent( )
832  hWriteLine( "};", TRUE )
834  wend
835 end sub
836 
837 sub hEmitStruct _
838  ( _
839  byval s as FBSYMBOL ptr, _
840  byval is_ptr as integer _
841  )
842 
843  dim as string ln
844  dim as integer skip = any, dtype = any, align = any
845  dim as FBSYMBOL ptr subtype = any, fld = any
846  dim as FBSYMBOL ptr ptr anonnode = any
847 
848  '' Already in the process of emitting this UDT?
849  if( symbGetIsBeingEmitted( s ) ) then
850  '' This means there is a circular dependency with another UDT.
851  '' One of the references can be a pointer only though,
852  '' because UDTs cannot contain each-other, so this can always
853  '' be solved by using a forward reference.
854  if( is_ptr ) then
855  '' Emit a forward reference for this struct (if not yet done).
856  '' HACK: reusing the accessed flag (that's used by variables only)
857  if( symbGetIsAccessed( s ) = FALSE ) then
858  symbSetIsAccessed( s )
859  hWriteLine( hGetUdtName( s ) + ";" )
860  end if
861  exit sub
862  end if
863  end if
864 
865  symbSetIsBeingEmitted( s )
866 
867  '' Emit types of fields
868  fld = symbUdtGetFirstField( s )
869  while( fld )
870  hEmitUDT( symbGetSubtype( fld ), typeIsPtr( symbGetType( fld ) ) )
871  fld = symbUdtGetNextField( fld )
872  wend
873 
874  '' Has this UDT been emitted in the mean time?
875  '' (due to one of the fields causing a circular dependency)
876  if( symbGetIsEmitted( s ) ) then
877  exit sub
878  end if
879 
880  '' Emit it now
881  symbSetIsEmitted( s )
882 
883  '' Header: struct|union [attributes...] id {
884  ln = hGetUdtTag( s )
885 
886  '' Work-around mingw32 gcc bug 52991; packing is broken for ms_struct
887  '' stucts, which is the default under -mms-bitfields, which is on by
888  '' default in mingw32 gcc 4.7.
889  if( (env.clopt.target = FB_COMPTARGET_WIN32) and _
890  (symbGetUDTAlign( s ) > 0) ) then
891  ln += "__attribute__((gcc_struct)) "
892  end if
893 
894  ln += hGetUdtId( s )
895  ln += " {"
896  hWriteLine( ln, TRUE )
897  sectionIndent( )
898 
899  '' Write out the elements
900  fld = symbUdtGetFirstField( s )
901  while( fld )
902 
903  if( fld->parent = s ) then
904  '' Field from main UDT
905  hPopAnonParents( NULL )
906  else
907  '' Field from a nested anonymous union/struct.
908  '' Check the stack to decide whether we have to start
909  '' nesting further, or instead go upwards, or stay at
910  '' the current level.
911 
912  '' Find the field's inner-most parent that's already on
913  '' stack, if any.
914  anonnode = hFindParentAnonAlreadyOnStack( fld )
915 
916  '' a) Pop the stack until we reach the proper level,
917  '' or stay at the current level.
918  '' b) Reset the stack to the main UDT's level
919  hPopAnonParents( anonnode )
920 
921  '' a) Push any parents that are inside the one that's on stack
922  '' b) Push each new nested anon struct/union
923  hPushAnonParents( iif( anonnode, *anonnode, s ), fld->parent )
924  end if
925 
926  '' For bitfields, emit only the container field, not the
927  '' individual bitfields (bitfields are merged into a "container"
928  '' given by the type of the first bitfield; if further bitfields
929  '' don't fit a new container is started, etc.)
930  ''
931  '' Alternatively we could emit bitfields explicitly via ": N",
932  '' but that would depend on gcc's ABI and we'd have to emit
933  '' things like __attribute__((ms_struct)) too for msbitfields...
934  if( symbGetType( fld ) = FB_DATATYPE_BITFIELD ) then
935  skip = (symbGetSubtype( fld )->bitfld.bitpos <> 0)
936  else
937  skip = FALSE
938  end if
939 
940  if( skip = FALSE ) then
941  dtype = symbGetType( fld )
942  subtype = symbGetSubtype( fld )
943  ln = hEmitType( dtype, subtype )
944  ln += " " + *symbGetName( fld )
945  ln += hEmitArrayDecl( fld )
946 
947  '' Field alignment (FIELD = N)?
948  align = symbGetUDTAlign( s )
949  if( align > 0 ) then
950  '' The aligned(N) attribute alone increases the alignment,
951  '' together with packed it decreases it.
952  '' FIELD = N in FB only decreases alignment, but never increases it.
953  skip = (align >= typeCalcNaturalAlign( dtype, subtype ))
954 
955  '' Don't add unnecessary attributes on nested structures
956  '' that are already packed to the same alignment,
957  '' gcc would show a warning in that case.
958  if( typeGet( dtype ) = FB_DATATYPE_STRUCT ) then
959  skip or= (align >= symbGetUDTAlign( subtype ))
960  end if
961 
962  if( skip = FALSE ) then
963  ln += " __attribute__((packed, aligned(" + str( align ) + ")))"
964  end if
965  end if
966 
967  ln += ";"
968  hWriteLine( ln, TRUE )
969  end if
970 
971  fld = symbUdtGetNextField( fld )
972  wend
973 
974  '' Close any remaining nested anonymous structs/unions
975  hPopAnonParents( NULL )
976 
977  '' Close UDT body
978  assert( listGetHead( @ctx.anonstack ) = NULL )
979  sectionUnindent( )
980  hWriteLine( "};", TRUE )
981 
982  symbResetIsBeingEmitted( s )
983 
984  '' Static assertion to ensure the struct has been emitted correctly,
985  '' at least with the correct sizeof(), because if it'd be too small,
986  '' that could easily cause stack trashing etc., because local vars
987  '' allocated by gcc would be smaller than expected, etc.
988  hWriteStaticAssert( "sizeof( " + hGetUdtTag( s ) + hGetUdtId( s ) + " ) == " + str( culngint( symbGetLen( s ) ) ) )
989 
990 end sub
991 
992 sub hEmitDecls( byval s as FBSYMBOL ptr, byval procs as integer )
993  while( s )
994  select case as const( symbGetClass( s ) )
995  case FB_SYMBCLASS_NAMESPACE
996  hEmitDecls( symbGetNamespaceTbHead( s ), procs )
997 
998  case FB_SYMBCLASS_STRUCT
999  hEmitDecls( symbGetCompSymbTb( s ).head, procs )
1000 
1001  case FB_SYMBCLASS_SCOPE
1002  hEmitDecls( symbGetScopeSymbTbHead( s ), procs )
1003 
1004  case FB_SYMBCLASS_VAR
1005  if( procs ) then
1006  exit select
1007  end if
1008 
1009  '' Skip DATA descriptor arrays here,
1010  '' they're handled by hEmitDataStmt()
1011  if( symbGetType( s ) = FB_DATATYPE_STRUCT ) then
1012  if( symbGetSubtype( s ) = ast.data.desc ) then
1013  exit select
1014  end if
1015  end if
1016 
1017  hEmitVariable( s )
1018 
1019  case FB_SYMBCLASS_PROC
1020  if( procs = FALSE ) then
1021  exit select
1022  end if
1023 
1024  if( symbGetIsFuncPtr( s ) = FALSE ) then
1025  hEmitFuncProto( s )
1026  end if
1027 
1028  end select
1029 
1030  s = s->next
1031  wend
1032 end sub
1033 
1035  var s = astGetLastDataStmtSymbol( )
1036  do while( s <> NULL )
1037  hEmitVariable( s )
1038  s = s->var_.data.prev
1039  loop
1040 end sub
1041 
1042 sub hWriteX86FTOI _
1043  ( _
1044  byref fname as string, _
1045  byval rtype as integer, _
1046  byval ptype as integer _
1047  )
1048 
1049  dim as string rtype_str, rtype_suffix
1050  select case rtype
1051  case FB_DATATYPE_LONG
1052  rtype_str = "int32"
1053  rtype_suffix = "l"
1054  case FB_DATATYPE_LONGINT
1055  rtype_str = "int64"
1056  rtype_suffix = "q"
1057  end select
1058 
1059  dim as string ptype_str, ptype_suffix
1060  select case ptype
1061  case FB_DATATYPE_SINGLE
1062  ptype_str = "float"
1063  ptype_suffix = "s"
1064 
1065  case FB_DATATYPE_DOUBLE
1066  ptype_str = "double"
1067  ptype_suffix = "l"
1068  end select
1069 
1070  if( env.clopt.asmsyntax = FB_ASMSYNTAX_INTEL ) then
1071  rtype_suffix = ""
1072  ptype_suffix = ""
1073  end if
1074 
1075  hWriteLine( "", TRUE )
1076  hWriteLine( "static inline " + rtype_str + " fb_" + fname + "( " + ptype_str + " value )", TRUE )
1077  hWriteLine( "{", TRUE )
1078  sectionIndent( )
1079  hWriteLine( "volatile " + rtype_str + " result;", TRUE )
1080  hWriteLine( "__asm__(", TRUE )
1081  sectionIndent( )
1082  hWriteLine( """fld" + ptype_suffix + " %1;""" , TRUE )
1083  hWriteLine( """fistp" + rtype_suffix + " %0;""", TRUE )
1084  hWriteLine( ":""=m"" (result)", TRUE )
1085  hWriteLine( ":""m"" (value)" , TRUE )
1086  sectionUnindent( )
1087  hWriteLine( ");", TRUE )
1088  hWriteLine( "return result;", TRUE )
1089  sectionUnindent( )
1090  hWriteLine( "}", TRUE )
1091 
1092 end sub
1093 
1094 sub hWriteGenericFTOI _
1095  ( _
1096  byref fname as string, _
1097  byval rtype as integer, _
1098  byval ptype as integer _
1099  )
1100 
1101  dim as string resulttype, callname
1102 
1103  select case rtype
1104  case FB_DATATYPE_LONG
1105  resulttype = "int32"
1106  case FB_DATATYPE_LONGINT
1107  resulttype = "int64"
1108  end select
1109 
1110  select case ptype
1111  case FB_DATATYPE_SINGLE
1112  callname = "rintf"
1113  case FB_DATATYPE_DOUBLE
1114  callname = "rint"
1115  end select
1116 
1117  hWriteLine( "#define fb_" + fname + "( value ) ((" + resulttype + ")__builtin_" + callname + "( value ))", TRUE )
1118 
1119 end sub
1120 
1121 sub hWriteFTOI _
1122  ( _
1123  byref fname as string, _
1124  byval rtype as integer, _
1125  byval ptype as integer _
1126  )
1127 
1128  if( fbCpuTypeIsX86( ) ) then
1129  hWriteX86FTOI( fname, rtype, ptype )
1130  else
1131  hWriteGenericFTOI( fname, rtype, ptype )
1132  end if
1133 
1134 end sub
1135 
1137  '' Special conversion routines for:
1138  '' single/double -> [unsigned] byte/short/integer/longint
1139  '' (which one will be used where is determined at AST/RTL)
1140  ''
1141  '' Simple C casting as in '(int)floatvar' cannot be used because it
1142  '' just truncates instead of rounding to nearest.
1143  ''
1144  '' There are at max 4 routines generated:
1145  '' single -> int
1146  '' single -> longint
1147  '' double -> int
1148  '' double -> longint
1149  '' and all other cases reuse those.
1150  ''
1151  '' A special case to watch out for: float -> unsigned int conversions.
1152  '' When converting to unsigned integer, it has to be converted to
1153  '' longint first, to avoid truncating to signed integer. That's a
1154  '' limitation of the ASM routines, and the ASM emitter is having the
1155  '' same problem, see emit_x86.bas:_emitLOADF2I() & co.
1156 
1157  '' single
1158  if( symbGetIsAccessed( PROCLOOKUP( FTOSL ) ) or _
1159  symbGetIsAccessed( PROCLOOKUP( FTOUL ) ) or _
1160  symbGetIsAccessed( PROCLOOKUP( FTOUI ) ) ) then
1161  hWriteFTOI( "ftosl", FB_DATATYPE_LONGINT, FB_DATATYPE_SINGLE )
1162  end if
1163 
1164  if( symbGetIsAccessed( PROCLOOKUP( FTOUL ) ) ) then
1165  hWriteLine( "#define fb_ftoul( v ) ((uint64)fb_ftosl( v ))", TRUE )
1166  end if
1167 
1168  if( symbGetIsAccessed( PROCLOOKUP( FTOUI ) ) ) then
1169  hWriteLine( "#define fb_ftoui( v ) ((uint32)fb_ftosl( v ))", TRUE )
1170  end if
1171 
1172  if( symbGetIsAccessed( PROCLOOKUP( FTOSI ) ) or _
1173  symbGetIsAccessed( PROCLOOKUP( FTOSS ) ) or _
1174  symbGetIsAccessed( PROCLOOKUP( FTOUS ) ) or _
1175  symbGetIsAccessed( PROCLOOKUP( FTOSB ) ) or _
1176  symbGetIsAccessed( PROCLOOKUP( FTOUB ) ) ) then
1177  hWriteFTOI( "ftosi", FB_DATATYPE_LONG, FB_DATATYPE_SINGLE )
1178  end if
1179 
1180  if( symbGetIsAccessed( PROCLOOKUP( FTOSS ) ) ) then
1181  hWriteLine( "#define fb_ftoss( v ) ((int16)fb_ftosi( v ))", TRUE )
1182  end if
1183 
1184  if( symbGetIsAccessed( PROCLOOKUP( FTOUS ) ) ) then
1185  hWriteLine( "#define fb_ftous( v ) ((uint16)fb_ftosi( v ))", TRUE )
1186  end if
1187 
1188  if( symbGetIsAccessed( PROCLOOKUP( FTOSB ) ) ) then
1189  hWriteLine( "#define fb_ftosb( v ) ((int8)fb_ftosi( v ))", TRUE )
1190  end if
1191 
1192  if( symbGetIsAccessed( PROCLOOKUP( FTOUB ) ) ) then
1193  hWriteLine( "#define fb_ftoub( v ) ((uint8)fb_ftosi( v ))", TRUE )
1194  end if
1195 
1196  '' double
1197  if( symbGetIsAccessed( PROCLOOKUP( DTOSL ) ) or _
1198  symbGetIsAccessed( PROCLOOKUP( DTOUL ) ) or _
1199  symbGetIsAccessed( PROCLOOKUP( DTOUI ) ) ) then
1200  hWriteFTOI( "dtosl", FB_DATATYPE_LONGINT, FB_DATATYPE_DOUBLE )
1201  end if
1202 
1203  if( symbGetIsAccessed( PROCLOOKUP( DTOUL ) ) ) then
1204  hWriteLine( "#define fb_dtoul( v ) ((uint64)fb_dtosl( v ))", TRUE )
1205  end if
1206 
1207  if( symbGetIsAccessed( PROCLOOKUP( DTOUI ) ) ) then
1208  hWriteLine( "#define fb_dtoui( v ) ((uint32)fb_dtosl( v ))", TRUE )
1209  end if
1210 
1211  if( symbGetIsAccessed( PROCLOOKUP( DTOSI ) ) or _
1212  symbGetIsAccessed( PROCLOOKUP( DTOSS ) ) or _
1213  symbGetIsAccessed( PROCLOOKUP( DTOUS ) ) or _
1214  symbGetIsAccessed( PROCLOOKUP( DTOSB ) ) or _
1215  symbGetIsAccessed( PROCLOOKUP( DTOUB ) ) ) then
1216  hWriteFTOI( "dtosi", FB_DATATYPE_LONG, FB_DATATYPE_DOUBLE )
1217  end if
1218 
1219  if( symbGetIsAccessed( PROCLOOKUP( DTOSS ) ) ) then
1220  hWriteLine( "#define fb_dtoss( v ) ((int16)fb_dtosi( v ))", TRUE )
1221  end if
1222 
1223  if( symbGetIsAccessed( PROCLOOKUP( DTOUS ) ) ) then
1224  hWriteLine( "#define fb_dtous( v ) ((uint16)fb_dtosi( v ))", TRUE )
1225  end if
1226 
1227  if( symbGetIsAccessed( PROCLOOKUP( DTOSB ) ) ) then
1228  hWriteLine( "#define fb_dtosb( v ) ((int8)fb_dtosi( v ))", TRUE )
1229  end if
1230 
1231  if( symbGetIsAccessed( PROCLOOKUP( DTOUB ) ) ) then
1232  hWriteLine( "#define fb_dtoub( v ) ((uint8)fb_dtosi( v ))", TRUE )
1233  end if
1234 
1235 end sub
1236 
1237 function _emitBegin( ) as integer
1238  ctx.escapedinputfilename = hReplace( env.inf.name, "\", $"\\" )
1239 
1240  if( hFileExists( env.outf.name ) ) then
1241  kill env.outf.name
1242  end if
1243 
1244  env.outf.num = freefile
1245  if( open( env.outf.name, for binary, access read write, as #env.outf.num ) <> 0 ) then
1246  return FALSE
1247  end if
1248 
1249  ctx.section = -1
1251  ctx.regcnt = 0
1252  ctx.linenum = 0
1254 
1255  '' header
1256  sectionBegin( )
1257 
1258  if( env.clopt.debug ) then
1259  _emitDBG( AST_OP_DBG_LINEINI, NULL, 0 )
1260  end if
1261 
1262  hWriteLine( "// Compilation of " + env.inf.name + " started at " + time( ) + " on " + date( ), TRUE )
1263  hWriteLine( "", TRUE )
1264 
1265  hWriteLine( "typedef signed char int8;", TRUE )
1266  hWriteLine( "typedef unsigned char uint8;", TRUE )
1267  hWriteLine( "typedef signed short int16;", TRUE )
1268  hWriteLine( "typedef unsigned short uint16;", TRUE )
1269  hWriteLine( "typedef signed int int32;", TRUE )
1270  hWriteLine( "typedef unsigned int uint32;", TRUE )
1271  hWriteLine( "typedef signed long long int64;", TRUE )
1272  hWriteLine( "typedef unsigned long long uint64;", TRUE )
1273  if( fbCpuTypeIs64bit( ) ) then
1274  hWriteLine( "typedef struct { char *data; int64 len; int64 size; } FBSTRING;", TRUE )
1275  else
1276  hWriteLine( "typedef struct { char *data; int32 len; int32 size; } FBSTRING;", TRUE )
1277  end if
1278 
1279  '' body
1280  sectionBegin( )
1281 
1282  function = TRUE
1283 end function
1284 
1285 sub _emitEnd( byval tottime as double )
1286  dim as integer section = any
1287  dim as EXPRCACHENODE ptr cachenode = any
1288  dim as EXPRNODE ptr node = any
1289 
1290  '' Switch to header section temporarily
1291  section = sectionGosub( 0 )
1292 
1293  '' Append global declarations to the header of the toplevel section.
1294  '' This must be done during _emitEnd() instead of _emitBegin() because
1295  '' _emitBegin() is called even before any input code is parsed.
1296 
1297  '' Emit proc decls first (because of function pointer initializers
1298  '' taking the address of procedures)
1299  hEmitDecls( symbGetGlobalTbHead( ), TRUE )
1300 
1301  '' Then the variables
1302  hEmitDecls( symbGetGlobalTbHead( ), FALSE )
1303 
1304  '' DATA descriptor arrays must be emitted based on the order indicated
1305  '' by the FBSYMBOL.var_.data.prev linked list, and not in the symtb
1306  '' order as done by hEmitDecls().
1307  '' Also, DATA array initializers can reference globals by taking their
1308  '' address, so they must be emitted after the other global declarations.
1309  hEmitDataStmt( )
1310 
1312 
1314 
1315  '' body (is appended to header section)
1316  sectionEnd( )
1317 
1318  hWriteLine( "", TRUE )
1319  hWriteLine( "// Total compilation time: " + str( tottime ) + " seconds.", TRUE )
1320 
1321  '' Emit & close the main section
1322  if( ctx.sections(0).old = FALSE ) then
1323  if( put( #env.outf.num, , ctx.sections(0).text ) <> 0 ) then
1324  end if
1325  end if
1326  sectionEnd( )
1327 
1328  if( close( #env.outf.num ) <> 0 ) then
1329  '' ...
1330  end if
1331  env.outf.num = 0
1332 
1333  assert( ctx.sectiongosublevel = 0 )
1334  assert( ctx.section = -1 )
1335 
1336  do
1337  cachenode = listGetHead( @ctx.exprcache )
1338  if( cachenode = NULL ) then
1339  exit do
1340  end if
1341  listDelNode( @ctx.exprcache, cachenode )
1342  loop
1343 
1344  do
1345  node = listGetHead( @ctx.exprnodes )
1346  if( node = NULL ) then
1347  exit do
1348  end if
1349  exprFreeNode( node )
1350  loop
1351 end sub
1352 
1353 '':::::
1354 function _getOptionValue _
1355  ( _
1356  byval opt as IR_OPTIONVALUE _
1357  ) as integer
1358 
1359  select case opt
1360  case IR_OPTIONVALUE_MAXMEMBLOCKLEN
1361  return 0
1362 
1363  case else
1364  errReportEx( FB_ERRMSG_INTERNAL, __FUNCTION__ )
1365 
1366  end select
1367 
1368 end function
1369 
1370 sub _procBegin( byval proc as FBSYMBOL ptr )
1371  proc->proc.ext->dbg.iniline = lexLineNum( )
1372 end sub
1373 
1374 sub _procEnd( byval proc as FBSYMBOL ptr )
1375  proc->proc.ext->dbg.endline = lexLineNum( )
1376 end sub
1377 
1378 sub _scopeBegin( byval s as FBSYMBOL ptr )
1379 end sub
1380 
1381 sub _scopeEnd( byval s as FBSYMBOL ptr )
1382 end sub
1383 
1384 sub _procAllocStaticVars( byval sym as FBSYMBOL ptr )
1385  dim as FBSYMBOL ptr desc = any
1386  dim as integer section = any
1387 
1388  ''
1389  '' Emit all statics with dtor into the toplevel header section,
1390  '' so their dtor wrappers can see them.
1391  ''
1392  '' This can't be done for all statics, since they can use local UDTs,
1393  '' and emitting those as globals too would be hard. For static with
1394  '' dtors though we can be sure they're not using local UDTs, because
1395  '' UDTs with dtors aren't allowed inside scopes.
1396  ''
1397 
1398  section = sectionGosub( 0 )
1399 
1400  while( sym )
1401  select case( symbGetClass( sym ) )
1402  '' scope block? recursion..
1403  case FB_SYMBCLASS_SCOPE
1404  _procAllocStaticVars( symbGetScopeSymbTbHead( sym ) )
1405 
1406  '' variable?
1407  case FB_SYMBCLASS_VAR
1408  '' static with dtor?
1409  if( symbIsStatic( sym ) and symbHasDtor( sym ) ) then
1410  hEmitVariable( sym )
1411 
1412  ''
1413  '' Check whether it's a dynamic array with a corresponding
1414  '' descriptor that needs to be emitted instead.
1415  '' (it won't be detected by above check itself,
1416  '' as it's of FB_ARRAYDESC type)
1417  ''
1418  '' It's the descriptor that matters for dynamic
1419  '' arrays - the dynamic array symbol itself is
1420  '' not even emitted by hEmitVariable().
1421  ''
1422  '' Note that for static locals the descriptor and the
1423  '' descriptor UDT will be local too, but since we're
1424  '' emitting to the toplevel section, the descriptor
1425  '' will end up there, and hEmitUDT() isn't allowed
1426  '' to emit the descriptor UDT locally.
1427  '' (this way we force it to be emitted globally)
1428  ''
1429  desc = symbGetArrayDescriptor( sym )
1430  if( desc ) then
1431  hEmitVariable( desc )
1432  end if
1433  end if
1434  end select
1435 
1436  sym = symbGetNext( sym )
1437  wend
1438 
1440 end sub
1441 
1442 ''::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
1443 
1444 '':::::
1445 function hNewVR _
1446  ( _
1447  byval dtype as integer, _
1448  byval subtype as FBSYMBOL ptr, _
1449  byval vtype as integer _
1450  ) as IRVREG ptr
1451 
1452  dim as IRVREG ptr v = any
1453 
1454  v = flistNewItem( @ctx.vregTB )
1455 
1456  v->typ = vtype
1457  v->dtype = dtype
1458  v->subtype = subtype
1459  v->sym = NULL
1460  v->reg = INVALID
1461  v->vidx = NULL
1462  v->ofs = 0
1463 
1464  function = v
1465 
1466 end function
1467 
1468 '':::::
1469 function _allocVreg _
1470  ( _
1471  byval dtype as integer, _
1472  byval subtype as FBSYMBOL ptr _
1473  ) as IRVREG ptr
1474 
1475  function = hNewVR( dtype, subtype, IR_VREGTYPE_REG )
1476 
1477 end function
1478 
1479 function _allocVrImm _
1480  ( _
1481  byval dtype as integer, _
1482  byval subtype as FBSYMBOL ptr, _
1483  byval value as longint _
1484  ) as IRVREG ptr
1485 
1486  dim as IRVREG ptr vr = any
1487 
1488  vr = hNewVR( dtype, subtype, IR_VREGTYPE_IMM )
1489  vr->value.i = value
1490 
1491  function = vr
1492 end function
1493 
1494 function _allocVrImmF _
1495  ( _
1496  byval dtype as integer, _
1497  byval subtype as FBSYMBOL ptr, _
1498  byval value as double _
1499  ) as IRVREG ptr
1500 
1501  dim as IRVREG ptr vr = any
1502 
1503  vr = hNewVR( dtype, subtype, IR_VREGTYPE_IMM )
1504  vr->value.f = value
1505 
1506  function = vr
1507 end function
1508 
1509 '':::::
1510 function _allocVrVar _
1511  ( _
1512  byval dtype as integer, _
1513  byval subtype as FBSYMBOL ptr, _
1514  byval symbol as FBSYMBOL ptr, _
1515  byval ofs as longint _
1516  ) as IRVREG ptr
1517 
1518  dim as IRVREG ptr vr = hNewVR( dtype, subtype, IR_VREGTYPE_VAR )
1519 
1520  vr->sym = symbol
1521  vr->ofs = ofs
1522 
1523  function = vr
1524 
1525 end function
1526 
1527 '':::::
1528 function _allocVrIdx _
1529  ( _
1530  byval dtype as integer, _
1531  byval subtype as FBSYMBOL ptr, _
1532  byval symbol as FBSYMBOL ptr, _
1533  byval ofs as longint, _
1534  byval mult as integer, _
1535  byval vidx as IRVREG ptr _
1536  ) as IRVREG ptr
1537 
1538  dim as IRVREG ptr vr = hNewVR( dtype, subtype, IR_VREGTYPE_IDX )
1539 
1540  vr->sym = symbol
1541  vr->ofs = ofs
1542  vr->vidx = vidx
1543 
1544  function = vr
1545 
1546 end function
1547 
1548 '':::::
1549 function _allocVrPtr _
1550  ( _
1551  byval dtype as integer, _
1552  byval subtype as FBSYMBOL ptr, _
1553  byval ofs as longint, _
1554  byval vidx as IRVREG ptr _
1555  ) as IRVREG ptr
1556 
1557  dim as IRVREG ptr vr = hNewVR( dtype, subtype, IR_VREGTYPE_PTR )
1558 
1559  vr->ofs = ofs
1560  vr->vidx = vidx
1561 
1562  function = vr
1563 
1564 end function
1565 
1566 '':::::
1567 function _allocVrOfs _
1568  ( _
1569  byval dtype as integer, _
1570  byval subtype as FBSYMBOL ptr, _
1571  byval symbol as FBSYMBOL ptr, _
1572  byval ofs as longint _
1573  ) as IRVREG ptr
1574 
1575  dim as IRVREG ptr vr = hNewVR( dtype, subtype, IR_VREGTYPE_OFS )
1576 
1577  vr->sym = symbol
1578  vr->ofs = ofs
1579 
1580  function = vr
1581 
1582 end function
1583 
1584 '':::::
1585 sub _setVregDataType _
1586  ( _
1587  byval vreg as IRVREG ptr, _
1588  byval dtype as integer, _
1589  byval subtype as FBSYMBOL ptr _
1590  )
1591 
1592  if( vreg <> NULL ) then
1593  vreg->dtype = dtype
1594  vreg->subtype = subtype
1595  end if
1596 
1597 end sub
1598 
1599 sub hLoadVreg( byval vreg as IRVREG ptr )
1600  if( vreg = NULL ) then
1601  exit sub
1602  end if
1603 
1604  '' reg?
1605  if( vreg->typ = IR_VREGTYPE_REG ) then
1606  if( vreg->reg <> INVALID ) then
1607  exit sub
1608  end if
1609 
1610  vreg->reg = ctx.regcnt
1611  ctx.regcnt += 1
1612  end if
1613 
1614  '' index?
1615  if( vreg->vidx <> NULL ) then
1616  hLoadVreg( vreg->vidx )
1617  end if
1618 end sub
1619 
1620 function hEmitType _
1621  ( _
1622  byval dtype as integer, _
1623  byval subtype as FBSYMBOL ptr _
1624  ) as string
1625 
1626  dim as string s
1627  dim as integer ptrcount = any
1628 
1629  ptrcount = typeGetPtrCnt( dtype )
1630  dtype = typeGetDtOnly( dtype )
1631 
1632  select case as const( dtype )
1633  case FB_DATATYPE_STRUCT, FB_DATATYPE_ENUM
1634  if( subtype ) then
1635  hEmitUDT( subtype, (ptrcount > 0) )
1636  s = hGetUdtName( subtype )
1637  elseif( dtype = FB_DATATYPE_ENUM ) then
1638  s = *dtypeName(typeGetRemapType( dtype ))
1639  else
1640  s = *dtypeName(FB_DATATYPE_VOID)
1641  end if
1642 
1643  case FB_DATATYPE_FUNCTION
1644  assert( ptrcount > 0 )
1645  ptrcount -= 1
1646  hEmitUDT( subtype, (ptrcount > 0) )
1647  s = *symbGetMangledName( subtype )
1648 
1649  case FB_DATATYPE_CHAR, FB_DATATYPE_WCHAR
1650  '' Emit ubyte instead of char,
1651  '' and ubyte/ushort/uinteger instead of wchar_t
1652  s = *dtypeName(typeGetRemapType( dtype ))
1653 
1654  case FB_DATATYPE_FIXSTR
1655  '' Ditto (but typeGetRemapType() returns FB_DATATYPE_FIXSTR,
1656  '' so do it manually)
1657  s = *dtypeName(FB_DATATYPE_UBYTE)
1658 
1659  case FB_DATATYPE_BITFIELD
1660  s = *dtypeName(symbGetType( subtype ))
1661 
1662  case else
1663  s = *dtypeName(dtype)
1664  end select
1665 
1666  if( ptrcount > 0 ) then
1667  s += string( ptrcount, "*" )
1668  end if
1669 
1670  function = s
1671 end function
1672 
1673 function exprNew _
1674  ( _
1675  byval class_ as integer, _
1676  byval dtype as integer, _
1677  byval subtype as FBSYMBOL ptr _
1678  ) as EXPRNODE ptr
1679 
1680  dim as EXPRNODE ptr n = any
1681 
1682  n = listNewNode( @ctx.exprnodes )
1683  n->class = class_
1684  n->dtype = dtype
1685  n->subtype = subtype
1686 
1687  function = n
1688 end function
1689 
1690 sub exprFreeNode( byval n as EXPRNODE ptr )
1691  if( n->class = EXPRCLASS_TEXT ) then
1692  ZstrFree( n->text )
1693  end if
1694  listDelNode( @ctx.exprnodes, n )
1695 end sub
1696 
1697 sub exprFreeTree( byval n as EXPRNODE ptr )
1698  if( n->l ) then
1699  exprFreeTree( n->l )
1700  end if
1701  if( n->r ) then
1702  exprFreeTree( n->r )
1703  end if
1704  exprFreeNode( n )
1705 end sub
1706 
1707 function exprNewTEXT _
1708  ( _
1709  byval dtype as integer, _
1710  byval subtype as FBSYMBOL ptr, _
1711  byval s as zstring ptr _
1712  ) as EXPRNODE ptr
1713 
1714  dim as EXPRNODE ptr n = any
1715 
1717  n->text = ZstrDup( s )
1718 
1719  function = n
1720 end function
1721 
1722 function exprNewIMMi _
1723  ( _
1724  byval i as longint, _
1725  byval dtype as integer = FB_DATATYPE_INTEGER _
1726  ) as EXPRNODE ptr
1727 
1728  dim as EXPRNODE ptr n = any
1729 
1730  '' Integer literals can only be emitted as either 32bit int or 64bit long long,
1731  '' if other types are needed, an exprNewCAST() should be done afterwards.
1732  if( typeGetSize( dtype ) = 8 ) then
1733  dtype = iif( typeIsSigned( dtype ), FB_DATATYPE_LONGINT, FB_DATATYPE_ULONGINT )
1734  else
1735  dtype = iif( typeIsSigned( dtype ), FB_DATATYPE_LONG, FB_DATATYPE_ULONG )
1736  end if
1737 
1738  n = exprNew( EXPRCLASS_IMM, dtype, NULL )
1739  n->val.i = i
1740 
1741  function = n
1742 end function
1743 
1744 function exprNewIMMf _
1745  ( _
1746  byval f as double, _
1747  byval dtype as integer _
1748  ) as EXPRNODE ptr
1749 
1750  dim as EXPRNODE ptr n = any
1751 
1752  n = exprNew( EXPRCLASS_IMM, dtype, NULL )
1753  n->val.f = f
1754 
1755  function = n
1756 end function
1757 
1758 function symbIsCArray( byval sym as FBSYMBOL ptr ) as integer
1759  '' No bydesc/byref, those are emitted as pointers...
1760  if( symbIsParamBydescOrByref( sym ) ) then
1761  return FALSE
1762  end if
1763 
1764  select case( symbGetClass( sym ) )
1765  case FB_SYMBCLASS_VAR, FB_SYMBCLASS_FIELD
1766  '' No dynamic arrays, they're just descriptor structs
1767  if( symbGetIsDynamic( sym ) ) then
1768  return FALSE
1769  end if
1770 
1771  if( symbGetArrayDimensions( sym ) <> 0 ) then
1772  return TRUE
1773  end if
1774  end select
1775 
1776  '' Fixed-length strings are emitted as arrays,
1777  '' string literals are emitted as string literals,
1778  '' both are pointers in C
1779  select case( symbGetType( sym ) )
1780  case FB_DATATYPE_FIXSTR, FB_DATATYPE_CHAR, FB_DATATYPE_WCHAR
1781  return TRUE
1782  end select
1783 
1784  return FALSE
1785 end function
1786 
1787 function exprNewCAST _
1788  ( _
1789  byval dtype as integer, _
1790  byval subtype as FBSYMBOL ptr, _
1791  byval l as EXPRNODE ptr _
1792  ) as EXPRNODE ptr
1793 
1794  dim as EXPRNODE ptr n = any
1795 
1796  '' Don't add a CAST if l already has the desired type
1797  if( (dtype = l->dtype) and (subtype = l->subtype) ) then
1798  return l
1799  end if
1800 
1801  '' Don't cast if l has a compatible type (e.g. 32bit int vs. 32bit long)
1802  '' (same class, same size, same signedness, and no pointers involved)
1803  if( (typeGetClass( l->dtype ) = typeGetClass( dtype )) and _
1804  (typeIsSigned( l->dtype ) = typeIsSigned( dtype )) and _
1805  (not typeIsPtr( l->dtype )) and (not typeIsPtr( dtype )) and _
1806  (typeGetSize( l->dtype ) = typeGetSize( dtype )) ) then
1807  return l
1808  end if
1809 
1810  '' "(foo*)(bar*)"? Discard the bar* cast and cast only to foo*,
1811  '' pointers are pointers, such double casts are useless.
1812  if( l->class = EXPRCLASS_CAST ) then
1813  if( (typeGetPtrCnt( dtype ) > 0) and (typeGetPtrCnt( l->dtype ) > 0) ) then
1814  l->dtype = dtype
1815  l->subtype = subtype
1816  return l
1817  end if
1818  end if
1819 
1821  n->l = l
1822 
1823  function = n
1824 end function
1825 
1826 function exprNewSYM( byval sym as FBSYMBOL ptr ) as EXPRNODE ptr
1827  dim as EXPRNODE ptr n = any
1828  dim as integer dtype = any
1829  dim as FBSYMBOL ptr subtype = any
1830 
1831  if( symbIsLabel( sym ) ) then
1832  '' &&label is a void* in GCC
1833  '' This is handled as a single SYM instead of ADDROF( SYM ),
1834  '' because a label is not a proper expression on its own.
1835  dtype = typeAddrOf( FB_DATATYPE_VOID )
1836  subtype = NULL
1837  elseif( symbIsProc( sym ) ) then
1838  '' &proc
1839  '' Similar to labels above, this is only used to take the
1840  '' address of functions, not to call them, so the '&' is
1841  '' part of the SYM.
1842  dtype = typeAddrOf( FB_DATATYPE_FUNCTION )
1843  subtype = sym
1844  elseif( symbIsCArray( sym ) ) then
1845  dtype = FB_DATATYPE_INVALID
1846  subtype = NULL
1847  else
1848  dtype = symbGetType( sym )
1849  subtype = symbGetSubtype( sym )
1850  end if
1851 
1853  n->sym = sym
1854 
1855  '' Array? Add CAST to make it a pointer to the first element,
1856  '' instead of a pointer to the array.
1857  if( dtype = FB_DATATYPE_INVALID ) then
1858  n = exprNewCAST( typeAddrOf( symbGetType( sym ) ), symbGetSubtype( sym ), n )
1859  end if
1860 
1861  function = n
1862 end function
1863 
1864 function typeCBop _
1865  ( _
1866  byval op as integer, _
1867  byval a as integer, _
1868  byval asubtype as FBSYMBOL ptr, _
1869  byval b as integer, _
1870  byval bsubtype as FBSYMBOL ptr _
1871  ) as integer
1872 
1873  '' Result of relational/comparison operators is int
1874  select case( op )
1875  case AST_OP_EQ, AST_OP_NE, AST_OP_GT, AST_OP_LT, AST_OP_GE, AST_OP_LE
1876  return FB_DATATYPE_LONG
1877  end select
1878 
1879  '' This tries to do C operand type promotion (and is probably not
1880  '' 100% accurate), in order to figure out the result type of BOP/UOP
1881  '' in the C output code, to allow the expression emitting decide
1882  '' whether it needs to insert casts in the C output code or not.
1883  ''
1884  '' This might only actually make a difference in rare cases;
1885  '' it depends on what kind of BOPs the AST tries to emit.
1886  ''
1887  '' 1. Operands < int/uint (i.e. byte, short) are promoted to int/uint.
1888  '' 2. For operands >= int/uint, one operand is promoted to match the
1889  '' other, if necessary. (except for bitshifts, where the rhs' type
1890  '' isn't taken into account, unlike FB)
1891 
1892  a = typeGet( a )
1893  b = typeGet( b )
1894 
1895  '' Float types take precedence (?)
1896  if( (a = FB_DATATYPE_DOUBLE) or (b = FB_DATATYPE_DOUBLE) ) then
1897  return FB_DATATYPE_DOUBLE
1898  end if
1899  if( (a = FB_DATATYPE_SINGLE) or (b = FB_DATATYPE_SINGLE) ) then
1900  return FB_DATATYPE_SINGLE
1901  end if
1902 
1903  '' Promote 8bit/16bit types to 32bit,
1904  '' and normalize 32bit types to FB_DATATYPE_LONG
1905  if( typeGetSize( a ) <= 4 ) then
1906  a = iif( typeIsSigned( a ), FB_DATATYPE_LONG, FB_DATATYPE_ULONG )
1907  end if
1908  if( typeGetSize( b ) <= 4 ) then
1909  b = iif( typeIsSigned( b ), FB_DATATYPE_LONG, FB_DATATYPE_ULONG )
1910  end if
1911 
1912  '' Promote signed to unsigned
1913  if( (not typeIsSigned( a )) or (not typeIsSigned( b )) ) then
1914  a = typeToUnsigned( a )
1915  b = typeToUnsigned( b )
1916  end if
1917 
1918  '' Promote to 64bit, iff a 64bit operand is involved,
1919  '' and normalize to FB_DATATYPE_LONGINT
1920  if( (typeGetSize( a ) = 8) or (typeGetSize( b ) = 8) ) then
1921  a = iif( typeIsSigned( a ), FB_DATATYPE_LONGINT, FB_DATATYPE_ULONGINT )
1922  b = iif( typeIsSigned( b ), FB_DATATYPE_LONGINT, FB_DATATYPE_ULONGINT )
1923  end if
1924 
1925  '' Promote signed to unsigned
1926  if( (not typeIsSigned( a )) or (not typeIsSigned( b )) ) then
1927  a = typeToUnsigned( a )
1928  b = typeToUnsigned( b )
1929  end if
1930 
1931  function = a
1932 end function
1933 
1934 function exprNewUOP _
1935  ( _
1936  byval op as integer, _
1937  byval l as EXPRNODE ptr _
1938  ) as EXPRNODE ptr
1939 
1940  dim as EXPRNODE ptr n = any
1941  dim as integer dtype = any, solved_out = any
1942 
1943  solved_out = FALSE
1944 
1945  '' Similar to BOPs, the C type promotion rules should be applied
1946  '' to determine the UOP's result type.
1947  select case( op )
1948  case AST_OP_ADDROF
1949  '' peep-hole optimization:
1950  '' ADDROF( DEREF( x ) ) -> x
1951  if( l->class = EXPRCLASS_UOP ) then
1952  solved_out = (l->op = AST_OP_DEREF)
1953  end if
1954 
1955  dtype = l->dtype
1956  dtype = typeAddrOf( dtype )
1957 
1958  case AST_OP_DEREF
1959  '' peep-hole optimization:
1960  '' DEREF( ADDROF( x ) ) -> x
1961  if( l->class = EXPRCLASS_UOP ) then
1962  solved_out = (l->op = AST_OP_ADDROF)
1963  end if
1964 
1965  dtype = l->dtype
1966  assert( typeGetPtrCnt( dtype ) > 0 )
1967  dtype = typeDeref( dtype )
1968 
1969  case AST_OP_NEG, AST_OP_NOT
1970  '' peep-hole optimization:
1971  '' -(-(foo)) -> foo
1972  '' ~(~(foo)) -> foo
1973  if( l->class = EXPRCLASS_UOP ) then
1974  solved_out = (l->op = op)
1975  end if
1976 
1977  dtype = typeCBop( op, l->dtype, l->subtype, l->dtype, l->subtype )
1978 
1979  case else
1980  assert( FALSE )
1981  end select
1982 
1983  if( solved_out ) then
1984  n = l->l
1985  exprFreeNode( l )
1986  return n
1987  end if
1988 
1989  n = exprNew( EXPRCLASS_UOP, dtype, l->subtype )
1990  n->l = l
1991  n->op = op
1992 
1993  function = n
1994 end function
1995 
1996 function exprNewBOP _
1997  ( _
1998  byval op as integer, _
1999  byval l as EXPRNODE ptr, _
2000  byval r as EXPRNODE ptr _
2001  ) as EXPRNODE ptr
2002 
2003  dim as EXPRNODE ptr n = any
2004  dim as integer dtype = any
2005 
2006  '' To find out the BOPs result type, apply C type promotion rules
2007  dtype = typeCBop( op, l->dtype, l->subtype, r->dtype, r->subtype )
2008 
2009  '' BOPs should only be done on simple int/float types,
2010  '' and on pointers only after casting to ubyte* first,
2011  '' so no subtype needs to be preserved here.
2012 
2013  n = exprNew( EXPRCLASS_BOP, dtype, NULL )
2014  n->l = l
2015  n->r = r
2016  n->op = op
2017 
2018  function = n
2019 end function
2020 
2021 '' Add expression root node to cache list, with the corresponding vreg id,
2022 '' allowing it to be looked up later (when the AST accesses that vreg).
2023 sub exprCache( byval vregid as integer, byval expr as EXPRNODE ptr )
2024  dim as EXPRCACHENODE ptr entry = any
2025  entry = listNewNode( @ctx.exprcache )
2026  entry->vregid = vregid
2027  entry->expr = expr
2028 end sub
2029 
2030 function exprLookup( byval vregid as integer ) as EXPRNODE ptr
2031  dim as EXPRCACHENODE ptr entry = any
2032 
2033  '' Find the node corresponding to that vreg, if any.
2034  entry = listGetHead( @ctx.exprcache )
2035  while( entry )
2036  if( entry->vregid = vregid ) then
2037  exit while
2038  end if
2039  entry = listGetNext( entry )
2040  wend
2041 
2042  if( entry ) then
2043  function = entry->expr
2044  listDelNode( @ctx.exprcache, entry )
2045  else
2046  function = NULL
2047  end if
2048 end function
2049 
2050 function hEmitInt _
2051  ( _
2052  byval dtype as integer, _
2053  byval value as longint _
2054  ) as string
2055 
2056  dim as string s
2057 
2058  if( typeIsSigned( dtype ) ) then
2059  s = str( value )
2060 
2061  '' Prevent GCC warnings for INT_MIN/LLONG_MIN:
2062  '' The '-' minus sign doesn't count as part of the number
2063  '' literal, and 2147483648 is too big for a 32bit integer,
2064  '' so it must be marked as unsigned.
2065  if( typeGetSize( dtype ) = 8 ) then
2066  if( value = -9223372036854775808ull ) then
2067  s += "u"
2068  end if
2069  s += "ll"
2070  else
2071  if( value = -2147483648u ) then
2072  s += "u"
2073  end if
2074  end if
2075  else
2076  if( typeGetSize( dtype ) = 8 ) then
2077  s = str( culngint( value ) ) + "ull"
2078  else
2079  s = str( culng( value ) ) + "u"
2080  end if
2081  end if
2082 
2083  function = s
2084 end function
2085 
2086 function hEmitFloat _
2087  ( _
2088  byval dtype as integer, _
2089  byval value as double _
2090  ) as string
2091 
2092  dim as string s
2093  dim as ulong expval = any
2094 
2095  '' x86 little-endian assumption
2096  expval = cast( ulong ptr, @value )[1]
2097 
2098  select case( expval )
2099  '' +/- infinity?
2100  case &h7FF00000UL, &hFFF00000UL
2101  if( dtype = FB_DATATYPE_DOUBLE ) then
2102  if( expval and &h80000000ul ) then
2103  s += "(-__builtin_inf())"
2104  else
2105  s += "__builtin_inf()"
2106  end if
2107  else
2108  if( expval and &h80000000ul ) then
2109  s += "(-__builtin_inff())"
2110  else
2111  s += "__builtin_inff()"
2112  end if
2113  end if
2114 
2115  '' +/- NaN? Quiet-NaN's only
2116  case &h7FF80000UL, &hFFF80000UL
2117  if( dtype = FB_DATATYPE_DOUBLE ) then
2118  if( expval and &h80000000ul ) then
2119  s += "(-__builtin_nan( """" ))"
2120  else
2121  s += "__builtin_nan( """" )"
2122  end if
2123  else
2124  if( expval and &h80000000ul ) then
2125  s += "(-__builtin_nanf( """" ))"
2126  else
2127  s += "__builtin_nanf( """" )"
2128  end if
2129  end if
2130 
2131  case else
2132  if( dtype = FB_DATATYPE_DOUBLE ) then
2133  s = str( value )
2134  else
2135  s = str( csng( value ) )
2136  end if
2137 
2138  '' Append .0 if there is no dot or exponent yet,
2139  '' to prevent gcc from treating it as int
2140  '' (e.g. 1 -> 1.0, but 0.1 or 1e-100 can stay as-is)
2141  if( instr( s, any "e." ) = 0 ) then
2142  s += ".0"
2143  end if
2144 
2145  '' float type suffix
2146  if( dtype = FB_DATATYPE_SINGLE ) then
2147  s += "f"
2148  end if
2149 
2150  end select
2151 
2152  function = s
2153 end function
2154 
2155 sub hBuildStrLit _
2156  ( _
2157  byref ln as string, _
2158  byval z as zstring ptr, _
2159  byval length as longint _ '' including null terminator
2160  )
2161 
2162  dim as integer ch = any
2163 
2164  '' Convert the string to something suitable for C
2165  '' (assuming internal escape sequences have already been solved out
2166  '' using hUnescape())
2167  '' Non-ASCII characters and also \ or " must be escaped, but also care
2168  '' must be taken when normal chars following an escape sequence would
2169  '' be seen as part of that escape sequence. This is handled by splitting
2170  '' the string literal in two at that position.
2171 
2172  ln += """"
2173 
2174  '' Don't bother emitting the null terminator explicitly - gcc will add
2175  '' it automatically already
2176  for i as integer = 0 to length - 2
2177  ch = (*z)[i]
2178 
2179  if( hCharNeedsEscaping( ch, asc( """" ) ) ) then
2180  '' Emit in \xNN escape form
2181  ln += $"\x" + hex( ch, 2 )
2182 
2183  '' Is there an 0-9, a-f or A-F char following?
2184  if( hIsValidHexDigit( (*z)[i+1] ) ) then
2185  '' Split up the string literal to prevent
2186  '' the compiler from treating this following
2187  '' char as part of the escape sequence
2188  ln += """ """
2189  end if
2190  elseif( ch = asc( "?" ) ) then
2191  ln += "?"
2192  '' If the following string literal content would form a
2193  '' trigraph, it must be escaped
2194  if( (*z)[i+1] = asc( "?" ) ) then
2195  assert( (i+2) < length ) '' null terminator not yet reached
2196  select case( (*z)[i+2] )
2197  case asc( "=" ), asc( "/" ), asc( "'" ), _
2198  asc( "(" ), asc( ")" ), asc( "!" ), _
2199  asc( "<" ), asc( ">" ), asc( "-" )
2200  '' Split up the string literal between the two '??', ditto
2201  ln += """ """
2202  end select
2203  end if
2204  else
2205  '' Emit as-is
2206  ln += chr( ch )
2207  end if
2208  next
2209 
2210  ln += """"
2211 end sub
2212 
2213 sub hBuildWstrLit _
2214  ( _
2215  byref ln as string, _
2216  byval w as wstring ptr, _
2217  byval length as longint _ '' including null terminator
2218  )
2219 
2220  dim as integer ch = any
2221  dim as integer wcharsize = any
2222 
2223  '' (ditto)
2224 
2225  ln += "L"""
2226  wcharsize = typeGetSize( FB_DATATYPE_WCHAR )
2227 
2228  '' Don't bother emitting the null terminator explicitly - gcc will add
2229  '' it automatically already
2230  for i as integer = 0 to length - 2
2231  ch = (*w)[i]
2232 
2233  if( hCharNeedsEscaping( ch, asc( """" ) ) ) then
2234  ln += $"\x" + hex( ch, wcharsize * 2 )
2235  if( hIsValidHexDigit( (*w)[i+1] ) ) then
2236  ln += """ L"""
2237  end if
2238  elseif( ch = asc( "?" ) ) then
2239  ln += "?"
2240  if( (*w)[i+1] = asc( "?" ) ) then
2241  assert( (i+2) < length ) '' null terminator not yet reached
2242  select case( (*w)[i+2] )
2243  case asc( "=" ), asc( "/" ), asc( "'" ), _
2244  asc( "(" ), asc( ")" ), asc( "!" ), _
2245  asc( "<" ), asc( ">" ), asc( "-" )
2246  ln += """ L"""
2247  end select
2248  end if
2249  else
2250  ln += chr( ch )
2251  end if
2252  next
2253 
2254  ln += """"
2255 end sub
2256 
2257 function hBopToStr( byval op as integer ) as zstring ptr
2258  select case as const( op )
2259  case AST_OP_ADD
2260  function = @" + "
2261  case AST_OP_SUB
2262  function = @" - "
2263  case AST_OP_MUL
2264  function = @" * "
2265  case AST_OP_DIV
2266  function = @" / "
2267  case AST_OP_INTDIV
2268  function = @" / "
2269  case AST_OP_MOD
2270  function = @" % "
2271  case AST_OP_SHL
2272  function = @" << "
2273  case AST_OP_SHR
2274  function = @" >> "
2275  case AST_OP_AND
2276  function = @" & "
2277  case AST_OP_OR
2278  function = @" | "
2279  case AST_OP_XOR
2280  function = @" ^ "
2281  case AST_OP_EQ
2282  function = @" == "
2283  case AST_OP_GT
2284  function = @" > "
2285  case AST_OP_LT
2286  function = @" < "
2287  case AST_OP_NE
2288  function = @" != "
2289  case AST_OP_GE
2290  function = @" >= "
2291  case AST_OP_LE
2292  function = @" <= "
2293  end select
2294 end function
2295 
2296 '' Builds up final expression text, walking the EXPRNODE tree
2297 sub hExprFlush( byval n as EXPRNODE ptr, byval need_parens as integer )
2298  dim as EXPRNODE ptr l = any
2299  dim as FBSYMBOL ptr sym = any
2300 
2301  select case as const( n->class )
2302  case EXPRCLASS_TEXT
2303  ctx.exprtext += *n->text
2304 
2305  case EXPRCLASS_IMM
2306  if( typeGetClass( n->dtype ) = FB_DATACLASS_FPOINT ) then
2307  ctx.exprtext += hEmitFloat( n->dtype, n->val.f )
2308  else
2309  ctx.exprtext += hEmitInt( n->dtype, n->val.i )
2310  end if
2311 
2312  case EXPRCLASS_SYM
2313  sym = n->sym
2314 
2315  '' String literal?
2316  if( symbGetIsLiteral( sym ) ) then
2317  if( symbGetType( sym ) = FB_DATATYPE_WCHAR ) then
2318  hBuildWstrLit( ctx.exprtext, hUnescapeW( symbGetVarLitTextW( sym ) ), symbGetWstrLen( sym ) )
2319  else
2320  hBuildStrLit( ctx.exprtext, hUnescape( symbGetVarLitText( sym ) ), symbGetStrLen( sym ) )
2321  end if
2322  else
2323  if( symbIsLabel( sym ) ) then
2324  ctx.exprtext += "&&"
2325  elseif( symbIsProc( sym ) ) then
2326  ctx.exprtext += "&"
2327  end if
2329  end if
2330 
2331  case EXPRCLASS_CAST
2332  ctx.exprtext += "("
2333  ctx.exprtext += hEmitType( n->dtype, n->subtype )
2334  ctx.exprtext += ")"
2335  hExprFlush( n->l, TRUE )
2336 
2337  case EXPRCLASS_UOP
2338  select case( n->op )
2339  case AST_OP_ADDROF
2340  ctx.exprtext += "&"
2341  case AST_OP_DEREF
2342  ctx.exprtext += "*"
2343  case AST_OP_NEG
2344  ctx.exprtext += "-"
2345  case AST_OP_NOT
2346  ctx.exprtext += "~"
2347  case else
2348  assert( FALSE )
2349  end select
2350 
2351  '' Add parentheses around UOPs to avoid -(-(foo)) looking like
2352  '' --foo which looks like the -- operator to gcc...
2353  need_parens = (n->l->class = EXPRCLASS_UOP)
2354  if( need_parens ) then
2355  ctx.exprtext += "("
2356  end if
2357  hExprFlush( n->l, TRUE )
2358  if( need_parens ) then
2359  ctx.exprtext += ")"
2360  end if
2361 
2362  case EXPRCLASS_BOP
2363  '' Add parentheses around BOPs if the parent needs it
2364  '' (looks like parentheses are unnecessary for all the other
2365  '' expressions though, CAST/UOP should work fine without
2366  '' parentheses around their operand)
2367  if( need_parens ) then
2368  ctx.exprtext += "("
2369  end if
2370  hExprFlush( n->l, TRUE )
2371  ctx.exprtext += *hBopToStr( n->op )
2372  hExprFlush( n->r, TRUE )
2373  if( need_parens ) then
2374  ctx.exprtext += ")"
2375  end if
2376 
2377  end select
2378 end sub
2379 
2380 function exprFlush _
2381  ( _
2382  byval n as EXPRNODE ptr, _
2383  byval need_parens as integer = FALSE _
2384  ) as string
2385 
2386  hExprFlush( n, need_parens )
2387 
2388  function = ctx.exprtext
2389  ctx.exprtext = ""
2390 
2391  exprFreeTree( n )
2392 end function
2393 
2394 #if __FB_DEBUG__
2395 sub exprDump( byval n as EXPRNODE ptr )
2396  static as integer level
2397  dim as string s
2398 
2399  level += 1
2400 
2401  select case as const( n->class )
2402  case EXPRCLASS_TEXT
2403  s = "TEXT( " + *n->text + " )"
2404 
2405  case EXPRCLASS_IMM
2406  if( typeGetClass( n->dtype ) = FB_DATACLASS_FPOINT ) then
2407  s = "IMM( " + hEmitFloat( n->dtype, n->val.f ) + " )"
2408  else
2409  s = "IMM( " + hEmitInt( n->dtype, n->val.i ) + " )"
2410  end if
2411 
2412  case EXPRCLASS_SYM
2413  s = "SYM( "
2414 
2415  '' String literal?
2416  if( symbGetIsLiteral( n->sym ) ) then
2417  if( symbGetType( n->sym ) = FB_DATATYPE_WCHAR ) then
2418  hBuildWstrLit( s, hUnescapeW( symbGetVarLitTextW( n->sym ) ), symbGetWstrLen( n->sym ) )
2419  else
2420  hBuildStrLit( s, hUnescape( symbGetVarLitText( n->sym ) ), symbGetStrLen( n->sym ) )
2421  end if
2422  else
2423  if( symbIsLabel( n->sym ) ) then
2424  s += "&&"
2425  elseif( symbIsProc( n->sym ) ) then
2426  s += "&"
2427  end if
2428  s += *symbGetMangledName( n->sym )
2429  end if
2430 
2431  s += " )"
2432 
2433  case EXPRCLASS_CAST
2434  s = "CAST( " + hEmitType( n->dtype, n->subtype ) + " )"
2435 
2436  case EXPRCLASS_UOP
2437  s = "UOP( "
2438  select case( n->op )
2439  case AST_OP_ADDROF
2440  s += "&"
2441  case AST_OP_DEREF
2442  s += "*"
2443  case AST_OP_NEG
2444  s += "-"
2445  case AST_OP_NOT
2446  s += "~"
2447  end select
2448  s += " )"
2449 
2450  case EXPRCLASS_BOP
2451  s = "BOP( " + *hBopToStr( n->op ) + " )"
2452 
2453  end select
2454 
2455  s += " as " + typeDump( n->dtype, n->subtype )
2456 
2457  print str( level ), string( level, " " ) + s
2458 
2459  select case( n->class )
2461  exprDump( n->l )
2462  case EXPRCLASS_BOP
2463  exprDump( n->l )
2464  exprDump( n->r )
2465  end select
2466 
2467  level -= 1
2468 end sub
2469 #endif
2470 
2471 function exprNewOFFSET _
2472  ( _
2473  byval sym as FBSYMBOL ptr, _
2474  byval ofs as longint _
2475  ) as EXPRNODE ptr
2476 
2477  dim as EXPRNODE ptr l = any
2478 
2479  l = exprNewSYM( sym )
2480 
2481  '' Add '&' for things that aren't pointers already
2482  if( (symbIsImport( sym ) or symbIsCArray( sym ) or _
2483  symbIsProc( sym ) or symbIsLabel( sym )) = FALSE ) then
2484  l = exprNewUOP( AST_OP_ADDROF, l )
2485  end if
2486 
2487  '' Add on the byte offset, if any
2488  if( ofs <> 0 ) then
2489  '' Cast to ubyte ptr to work around C's pointer arithmetic
2490  l = exprNewCAST( typeAddrOf( FB_DATATYPE_UBYTE ), NULL, l )
2491  l = exprNewBOP( AST_OP_ADD, l, exprNewIMMi( ofs ) )
2492  end if
2493 
2494  function = l
2495 end function
2496 
2497 function exprNewVREG _
2498  ( _
2499  byval vreg as IRVREG ptr, _
2500  byval is_lvalue as integer = FALSE _
2501  ) as EXPRNODE ptr
2502 
2503  dim as EXPRNODE ptr l = any
2504  dim as integer dtype = any, have_offset = any
2505  dim as FBSYMBOL ptr subtype = any
2506 
2507  select case as const( vreg->typ )
2508  case IR_VREGTYPE_VAR, IR_VREGTYPE_IDX, IR_VREGTYPE_PTR
2509  if( vreg->sym = NULL ) then
2510  '' No symbol attached, but vidx instead, unless the
2511  '' address was given as a constant,
2512  '' e.g. in derefs like *cptr(byte ptr, 0),
2513  '' then there is neither a symbol nor vidx,
2514  '' but just the "offset".
2515  '' *(vregtype*)offset
2516  '' *(vregtype*)vidx
2517  '' *(vregtype*)((uint8*)vidx + offset)
2518 
2519  if( vreg->vidx ) then
2520  '' recursion
2521  l = exprNewVREG( vreg->vidx )
2522 
2523  if( vreg->ofs <> 0 ) then
2524  '' Cast to ubyte ptr to work around C's pointer arithmetic
2525  l = exprNewCAST( typeAddrOf( FB_DATATYPE_UBYTE ), NULL, l )
2526  l = exprNewBOP( AST_OP_ADD, l, exprNewIMMi( vreg->ofs ) )
2527  end if
2528  else
2529  l = exprNewIMMi( vreg->ofs )
2530  end if
2531 
2532  l = exprNewCAST( typeAddrOf( vreg->dtype ), vreg->subtype, l )
2533  l = exprNewUOP( AST_OP_DEREF, l )
2534  exit select
2535  end if
2536 
2537  assert( symbIsProc( vreg->sym ) = FALSE ) '' should be an IR_VREGTYPE_OFS
2538  assert( symbIsLabel( vreg->sym ) = FALSE ) '' should be handled in _emitAddr()
2539 
2540  '' memory accesses - stack vars, arrays, UDT fields, ptr derefs
2541  ''
2542  '' - offsets are byte offsets as calculated by the AST
2543  '' - vreg's dtype can be different from symbol's dtype,
2544  '' e.g. UDT var + field access, or due to type casting.
2545  '' - vregs can be structs/strings here in the C backend
2546  '' - C doesn't allow direct casting to/from structs, but we can
2547  '' do a deref/addrof trick like *(vregtype*)&udtvar instead.
2548  '' - no float <-> int conversions should be done here, so be
2549  '' careful with vregdtype=integer while sym=floatvar etc.,
2550  '' the work-around (again) is the deref/addrof trick.
2551  ''
2552  '' simple var accesses:
2553  '' sym
2554  '' (vregtype)sym
2555  '' ptr derefs:
2556  '' *(vregtype*)sym
2557  '' *(vregtype*)((uint8*)sym + offset)
2558  '' array accesses (idx):
2559  '' *(vregtype*)((uint8*)sym + vidx + offset)
2560  '' field accesses:
2561  '' *(vregtype*)&sym
2562  '' *(vregtype*)((uint8*)&sym + offset)
2563 
2564  have_offset = ((vreg->ofs <> 0) or (vreg->vidx <> NULL))
2565 
2566  '' Check whether to do plain access or deref/addrof trick
2567  '' - any offset? use trick, to allow doing +offset
2568  '' - symbol is an array in the C code? (arrays, fixlen strings...)
2569  '' cannot just do (elementtype)carray, it must always be
2570  '' *(elementtype*)carray to access the memory in these cases.
2571  dim as integer is_carray = symbIsCArray( vreg->sym )
2572  dim as integer do_deref = have_offset or is_carray
2573 
2574  dim as integer is_ptr = typeIsPtr( symbGetType( vreg->sym ) )
2575  dim as integer symdtype = symbGetType( vreg->sym )
2576  dim as FBSYMBOL ptr symsubtype = symbGetSubtype( vreg->sym )
2577 
2578  '' Emitted as pointer?
2579  if( symbIsParamByRef( vreg->sym ) or symbIsImport( vreg->sym ) or is_carray ) then
2580  is_ptr = TRUE
2581  symdtype = typeAddrOf( symdtype )
2582  end if
2583 
2584  l = exprNewSYM( vreg->sym )
2585 
2586  '' Different types?
2587  if( (vreg->dtype <> symdtype) or (vreg->subtype <> symsubtype) ) then
2588  '' a) float <-> int: access raw bytes instead of converting
2589  '' b) struct <-> any other: ensure valid C syntax
2590 
2591  '' different data classes?
2592  do_deref or= (typeGetClass( vreg->dtype ) <> typeGetClass( symdtype ))
2593 
2594  '' any structs involved? (note: FBSTRINGs are structs in the C code too!)
2595  select case( typeGet( vreg->dtype ) )
2596  case FB_DATATYPE_STRING, FB_DATATYPE_STRUCT
2597  do_deref = TRUE
2598  case else
2599  select case( typeGet( symdtype ) )
2600  case FB_DATATYPE_STRING, FB_DATATYPE_STRUCT
2601  do_deref = TRUE
2602  end select
2603  end select
2604  end if
2605 
2606  if( do_deref = FALSE ) then
2607  '' Plain access is enough
2608  exit select
2609  end if
2610 
2611  '' Deref/addrof trick
2612 
2613  '' Add '&' for things that aren't pointers already
2614  if( is_ptr = FALSE ) then
2615  l = exprNewUOP( AST_OP_ADDROF, l )
2616  end if
2617  if( have_offset ) then
2618  '' Cast to ubyte ptr to work around C's pointer arithmetic
2619  l = exprNewCAST( typeAddrOf( FB_DATATYPE_UBYTE ), NULL, l )
2620  if( vreg->vidx <> NULL ) then
2621  l = exprNewBOP( AST_OP_ADD, l, exprNewVREG( vreg->vidx ) )
2622  end if
2623  if( vreg->ofs <> 0 ) then
2624  l = exprNewBOP( AST_OP_ADD, l, exprNewIMMi( vreg->ofs ) )
2625  end if
2626  end if
2627 
2628  '' cast to vregdtype*
2629  l = exprNewCAST( typeAddrOf( vreg->dtype ), vreg->subtype, l )
2630 
2631  '' deref to get vregdtype
2632  l = exprNewUOP( AST_OP_DEREF, l )
2633 
2634  case IR_VREGTYPE_OFS
2635  '' Accessing a global, including string literals and function
2636  '' symbols (used when taking address of functions).
2637  l = exprNewOFFSET( vreg->sym, vreg->ofs )
2638 
2639  case IR_VREGTYPE_IMM
2640  static as string s
2641 
2642  '' An immediate -- a constant value
2643  '' The integer literal can be emitted as 32bit or 64bit,
2644  '' signed or unsigned, and afterwards it should be cast to the
2645  '' vreg's type for cases like
2646  '' "cptr(any ptr, 0)"
2647  '' where the constant has some pointer type, and we'd like to
2648  '' avoid gcc warnings about pointers...
2649 
2650  dtype = vreg->dtype
2651  if( typeGetClass( dtype ) = FB_DATACLASS_FPOINT ) then
2652  l = exprNewIMMf( vreg->value.f, dtype )
2653  else
2654  l = exprNewIMMi( vreg->value.i, dtype )
2655  end if
2656 
2657  case IR_VREGTYPE_REG
2658  '' Access to existing vreg (e.g. BOP result)
2659  l = exprLookup( vreg->reg )
2660  if( l = NULL ) then
2661  '' Accessing a previous vreg a second time
2662  '' This currently should only happen with -exx pointer
2663  '' or array checking function calls, where the AST is
2664  '' reusing the function result vreg. Since the vreg is
2665  '' a call result, the C backend will have emitted a
2666  '' temp var, allowing this reuse to work.
2667  l = exprNewTEXT( vreg->dtype, vreg->subtype, "vr$" + str( vreg->reg ) )
2668  end if
2669 
2670  end select
2671 
2672  if( is_lvalue = FALSE ) then
2673  l = exprNewCAST( vreg->dtype, vreg->subtype, l )
2674  end if
2675 
2676  function = l
2677 end function
2678 
2679 sub _emitLabel( byval label as FBSYMBOL ptr )
2680  '' Only when inside normal procedures
2681  '' (NAKED procedures don't increase the indentation)
2682  if( sectionInsideProc( ) ) then
2683  hWriteLine( *symbGetMangledName( label ) + ":;" )
2684  end if
2685 end sub
2686 
2687 '' store an expression into a vreg
2688 sub exprSTORE _
2689  ( _
2690  byval vr as IRVREG ptr, _
2691  byval r as EXPRNODE ptr, _
2692  byval has_sidefx as integer = FALSE _
2693  )
2694 
2695  static as string ln, tempvar
2696  dim as EXPRNODE ptr l = any
2697 
2698  if( irIsREG( vr ) ) then
2699  if( has_sidefx ) then
2700  '' Expressions (REG) with side-effects (i.e. CALLs)
2701  '' should be emitted immediately in-place, that's what
2702  '' the AST expects, like with the ASM backend.
2703  '' a) due to the side-effects
2704  '' b) because sometimes it leaves the vreg dangling
2705  '' and relies only on the side-effects, e.g. when
2706  '' calling functions that return their UDT result
2707  '' through a hidden parameter. The CALL expression
2708  '' must be emitted, but the result vreg won't ever
2709  '' be accessed.
2710  ''
2711  '' -> Create a temp var and use that as the new vreg
2712  '' expression, instead of the original expr itself:
2713  '' type tempvar = expr;
2714  '' (no cast needed, the assignment has the same effect)
2715  tempvar = "vr$" + str( vr->reg )
2716 
2717  ln = hEmitType( vr->dtype, vr->subtype )
2718  ln += " " + tempvar + " = "
2719  ln += exprFlush( r )
2720  ln += ";"
2721 
2722  hWriteLine( ln )
2723 
2724  r = exprNewTEXT( vr->dtype, vr->subtype, tempvar )
2725  else
2726  r = exprNewCAST( vr->dtype, vr->subtype, r )
2727  end if
2728 
2729  '' Put the expression on hold, it'll be used in the following
2730  '' access to that vreg, instead of being emitted right here
2731  '' as a #define or temp var.
2732  exprCache( vr->reg, r )
2733  else
2734  '' Store into existing vreg (assign to var/deref, i.e. lvalue)
2735  '' vreg = (vregtype)r;
2736  '' FB allows noconv casts (no data class/size change) on the
2737  '' lhs, but C does not, the rhs should be casted here instead,
2738  '' although it probably doesn't matter much either way.
2739  l = exprNewVREG( vr, TRUE )
2740 
2741  '' 1st to the desired vreg type
2742  r = exprNewCAST( vr->dtype, vr->subtype, r )
2743 
2744  if( typeIsPtr( l->dtype ) or typeIsPtr( r->dtype ) ) then
2745  '' 2nd to void* to avoid gcc ptr warnings
2746  r = exprNewCAST( l->dtype, l->subtype, r )
2747  end if
2748 
2749  ln = exprFlush( l )
2750  ln += " = "
2751  ln += exprFlush( r )
2752  ln += ";"
2753 
2754  hWriteLine( ln )
2755  end if
2756 
2757 end sub
2758 
2759 sub _emitBop _
2760  ( _
2761  byval op as integer, _
2762  byval v1 as IRVREG ptr, _
2763  byval v2 as IRVREG ptr, _
2764  byval vr as IRVREG ptr, _
2765  byval ex as FBSYMBOL ptr _
2766  )
2767 
2768  dim as EXPRNODE ptr l = any, r = any
2769 
2770  hLoadVreg( v1 )
2771  hLoadVreg( v2 )
2772  hLoadVreg( vr )
2773 
2774  l = exprNewVREG( v1 )
2775  r = exprNewVREG( v2 )
2776 
2777  select case as const( op )
2778  case AST_OP_EQ, AST_OP_NE, AST_OP_GT, AST_OP_LT, AST_OP_GE, AST_OP_LE
2779  if( vr = NULL ) then
2780  '' Conditional branch
2781  static as string s
2782  s = "if( "
2783  s += exprFlush( exprNewBOP( op, l, r ) )
2784  s += " ) goto "
2785  s += *symbGetMangledName( ex )
2786  s += ";"
2787  hWriteLine( s )
2788  exit sub
2789  end if
2790  end select
2791 
2792  if( vr = NULL ) then
2793  vr = v1
2794  end if
2795 
2796  select case as const( op )
2797  case AST_OP_EQ, AST_OP_NE, AST_OP_GT, AST_OP_LT, AST_OP_GE, AST_OP_LE
2798  '' Must work-around C's boolean logic values and convert the "boolean"
2799  '' 1 to -1 while 0 stays 0 to match FB.
2800  l = exprNewUOP( AST_OP_NEG, exprNewBOP( op, l, r ) )
2801 
2802  case AST_OP_ADD, AST_OP_SUB, AST_OP_MUL, AST_OP_DIV, AST_OP_INTDIV, _
2803  AST_OP_MOD, AST_OP_SHL, AST_OP_SHR, AST_OP_AND, AST_OP_OR, _
2804  AST_OP_XOR
2805  dim as integer is_ptr_arith = ((op = AST_OP_ADD) or (op = AST_OP_SUB))
2806 
2807  '' Cast to byte ptr to work around C's pointer arithmetic
2808  if( is_ptr_arith and typeIsPtr( v1->dtype ) ) then
2809  l = exprNewCAST( typeAddrOf( FB_DATATYPE_UBYTE ), NULL, l )
2810  end if
2811  if( is_ptr_arith and typeIsPtr( v2->dtype ) ) then
2812  r = exprNewCAST( typeAddrOf( FB_DATATYPE_UBYTE ), NULL, r )
2813  end if
2814 
2815  '' Ensure '/' means floating point divide by casting to double
2816  '' For AST_OP_INTDIV this is not needed, since the AST will already
2817  '' cast both operands to integer before doing the intdiv.
2818  if( op = AST_OP_DIV ) then
2819  l = exprNewCAST( FB_DATATYPE_DOUBLE, NULL, l )
2820  r = exprNewCAST( FB_DATATYPE_DOUBLE, NULL, r )
2821  end if
2822 
2823  l = exprNewBOP( op, l, r )
2824 
2825  case AST_OP_EQV
2826  '' vr = ~(v1 ^ v2)
2827  l = exprNewUOP( AST_OP_NOT, exprNewBOP( AST_OP_XOR, l, r ) )
2828 
2829  case AST_OP_IMP
2830  '' vr = ~v1 | v2
2831  l = exprNewBOP( AST_OP_OR, exprNewUOP( AST_OP_NOT, l ), r )
2832 
2833  end select
2834 
2835  exprSTORE( vr, l )
2836 end sub
2837 
2838 sub _emitUop _
2839  ( _
2840  byval op as integer, _
2841  byval v1 as IRVREG ptr, _
2842  byval vr as IRVREG ptr _
2843  )
2844 
2845  hLoadVreg( v1 )
2846  hLoadVreg( vr )
2847 
2848  if( vr = NULL ) then
2849  vr = v1
2850  end if
2851 
2852  exprSTORE( vr, exprNewUOP( op, exprNewVREG( v1 ) ) )
2853 
2854 end sub
2855 
2856 sub _emitStore( byval v1 as IRVREG ptr, byval v2 as IRVREG ptr )
2857  hLoadVreg( v1 )
2858  hLoadVreg( v2 )
2859  exprSTORE( v1, exprNewVREG( v2 ) )
2860 end sub
2861 
2863 /' do nothing '/
2864 end sub
2865 
2866 sub _emitLoad( byval v1 as IRVREG ptr )
2867 /' do nothing '/
2868 end sub
2869 
2870 sub _emitLoadRes( byval v1 as IRVREG ptr, byval vr as IRVREG ptr )
2871  _emitStore( vr, v1 )
2872  hWriteLine( "return " + exprFlush( exprNewVREG( vr ) ) + ";" )
2873 end sub
2874 
2875 sub _emitAddr _
2876  ( _
2877  byval op as integer, _
2878  byval v1 as IRVREG ptr, _
2879  byval vr as IRVREG ptr _
2880  )
2881 
2882  dim as EXPRNODE ptr l = NULL
2883 
2884  hLoadVreg( v1 )
2885  hLoadVreg( vr )
2886 
2887  select case( op )
2888  case AST_OP_ADDROF
2889  '' Taking address of label?
2890  if( (v1->typ = IR_VREGTYPE_VAR) and (v1->sym <> NULL) ) then
2891  if( symbIsLabel( v1->sym ) ) then
2892  ''
2893  '' special case used by FB error handling code
2894  ''
2895  '' The VAR vreg's dtype for the label access
2896  '' is useless because 1) the AST is inconsistently
2897  '' using integer or byte and 2) labels cannot be
2898  '' casted anyways.
2899  ''
2900  '' The only thing that matters is the dtype of the
2901  '' result vreg (the type of the ADDROF expression).
2902  ''
2903  l = exprNewSYM( v1->sym )
2904  l = exprNewCAST( vr->dtype, vr->subtype, l )
2905  exit select
2906  end if
2907  end if
2908 
2909 /' lvalue '/ ) )
2910 
2911  case AST_OP_DEREF
2912  '' Note: The deref is already done in the vreg itself; as in
2913  '' the ASM backend, no explicit deref operation is needed.
2914  l = exprNewVREG( v1 )
2915 
2916  end select
2917 
2918  exprSTORE( vr, l )
2919 end sub
2920 
2921 sub hDoCall _
2922  ( _
2923  byref s as string, _
2924  byval bytestopop as integer, _
2925  byval vr as IRVREG ptr, _
2926  byval level as integer _
2927  )
2928 
2929  dim as IRCALLARG ptr arg = any
2930 
2931  '' Flush argument list
2932  s += "( "
2933  arg = listGetTail( @ctx.callargs )
2934  while( arg andalso (arg->level = level) )
2935  dim as IRCALLARG ptr prev = listGetPrev( arg )
2936 
2937  var expr = exprNewVREG( arg->vr )
2938 
2939  '' param will be NULL for hidden struct result arg, since
2940  '' no corresponding PARAM exists.
2941  if( arg->param andalso (arg->param->param.mode <> FB_PARAMMODE_VARARG) ) then
2942  '' Cast arg to param's type to prevent gcc warning.
2943  '' (this will be done by astNewARG() already, except for
2944  '' BYREF AS ANY params, where the exact type will only
2945  '' be known later, or never)
2946  var dtype = symbGetType( arg->param )
2947  var subtype = arg->param->subtype
2950  end if
2951 
2952  s += exprFlush( expr )
2953 
2954  listDelNode( @ctx.callargs, arg )
2955 
2956  if( prev ) then
2957  if( prev->level = level ) then
2958  s += ", "
2959  end if
2960  end if
2961 
2962  arg = prev
2963  wend
2964  s += " )"
2965 
2966  if( vr = NULL ) then
2967  s += ";"
2968  hWriteLine( s )
2969  else
2970  hLoadVreg( vr )
2971  exprSTORE( vr, exprNewTEXT( vr->dtype, vr->subtype, s ), TRUE )
2972  end if
2973 
2974 end sub
2975 
2976 sub _emitCall _
2977  ( _
2978  byval proc as FBSYMBOL ptr, _
2979  byval bytestopop as integer, _
2980  byval vr as IRVREG ptr, _
2981  byval level as integer _
2982  )
2983 
2984  static as string s
2985 
2986  s = *symbGetMangledName( proc )
2987  hDoCall( s, bytestopop, vr, level )
2988 
2989 end sub
2990 
2991 sub _emitCallPtr _
2992  ( _
2993  byval v1 as IRVREG ptr, _
2994  byval vr as IRVREG ptr, _
2995  byval bytestopop as integer, _
2996  byval level as integer _
2997  )
2998 
2999  static as string s
3000 
3001  s = "(" + exprFlush( exprNewVREG( v1 ) ) + ")"
3002  hDoCall( s, bytestopop, vr, level )
3003 
3004 end sub
3005 
3006 sub _emitJumpPtr( byval v1 as IRVREG ptr )
3007  hWriteLine( "goto *" + exprFlush( exprNewVREG( v1 ), TRUE ) + ";" )
3008 end sub
3009 
3010 sub _emitBranch( byval op as integer, byval label as FBSYMBOL ptr )
3011  assert( op = AST_OP_JMP )
3012  hWriteLine( "goto " + *symbGetMangledName( label ) + ";" )
3013 end sub
3014 
3015 sub _emitJmpTb _
3016  ( _
3017  byval v1 as IRVREG ptr, _
3018  byval tbsym as FBSYMBOL ptr, _
3019  byval values as ulongint ptr, _
3020  byval labels as FBSYMBOL ptr ptr, _
3021  byval labelcount as integer, _
3022  byval deflabel as FBSYMBOL ptr, _
3023  byval minval as ulongint, _
3024  byval maxval as ulongint _
3025  )
3026 
3027  dim as string tb, temp, ln
3028  dim as FBSYMBOL ptr label = any
3029  dim as EXPRNODE ptr l = any
3030  dim as integer i = any
3031 
3032  '' SELECT CASE AS CONST always uses a temp var, no need to worry about side effects
3033  assert( v1->typ = IR_VREGTYPE_VAR )
3034  temp = exprFlush( exprNewVREG( v1 ) )
3035 
3036  if( labelcount <= 0 ) then
3037  '' Empty jump table, just jump directly to the ELSE block or END SELECT
3038  hWriteLine( "goto " + *symbGetMangledName( deflabel ) + ";", TRUE )
3039 
3040  '' Silence gcc warning about the unused temp var
3041  hWriteLine( "(void)" + temp + ";", TRUE )
3042  exit sub
3043  end if
3044 
3045  tb = *symbUniqueId( )
3046 
3047  l = exprNewIMMi( maxval - minval + 1 )
3048  hWriteLine( "static const void* " + tb + "[" + exprFlush( l ) + "] = {", TRUE )
3049  sectionIndent( )
3050 
3051  i = 0
3052  for value as ulongint = minval to maxval
3053  assert( i < labelcount )
3054  if( value = values[i] ) then
3055  label = labels[i]
3056  i += 1
3057  else
3058  label = deflabel
3059  end if
3060  hWriteLine( "&&" + *symbGetMangledName( label ) + ",", TRUE )
3061  next
3062 
3063  sectionUnindent( )
3064  hWriteLine( "};", TRUE )
3065 
3066  if( minval > 0 ) then
3067  '' if( temp < minval ) goto deflabel
3068  l = exprNewTEXT( FB_DATATYPE_UINT, NULL, temp )
3069  l = exprNewBOP( AST_OP_LT, l, exprNewIMMi( minval ) )
3070  hWriteLine( "if( " + exprFlush( l ) + " ) goto " + *symbGetMangledName( deflabel ) + ";", TRUE )
3071  end if
3072 
3073  '' if( temp > maxval ) then goto deflabel
3074  l = exprNewTEXT( FB_DATATYPE_UINT, NULL, temp )
3075  l = exprNewBOP( AST_OP_GT, l, exprNewIMMi( maxval ) )
3076  hWriteLine( "if( " + exprFlush( l ) + " ) goto " + *symbGetMangledName( deflabel ) + ";", TRUE )
3077 
3078  '' l = jumptable[l - minval]
3079  l = exprNewTEXT( FB_DATATYPE_UINT, NULL, temp )
3080  l = exprNewBOP( AST_OP_SUB, l, exprNewIMMi( minval ) )
3081  hWriteLine( "goto *" + tb + "[" + exprFlush( l ) + "];", TRUE )
3082 
3083 end sub
3084 
3085 sub _emitMem _
3086  ( _
3087  byval op as integer, _
3088  byval v1 as IRVREG ptr, _
3089  byval v2 as IRVREG ptr, _
3090  byval bytes as longint _
3091  )
3092 
3093  select case op
3094  case AST_OP_MEMCLEAR
3095  hWriteLine("__builtin_memset( " + exprFlush( exprNewVREG( v1 ) ) + ", 0, " + exprFlush( exprNewVREG( v2 ) ) + " );" )
3096  case AST_OP_MEMMOVE
3097  hWriteLine("__builtin_memcpy( " + exprFlush( exprNewVREG( v1 ) ) + ", " + exprFlush( exprNewVREG( v2 ) ) + ", " + str( cunsg( bytes ) ) + " );" )
3098  end select
3099 
3100 end sub
3101 
3102 sub _emitDECL( byval sym as FBSYMBOL ptr )
3103  dim as FBSYMBOL ptr array = any
3104 
3105  '' Emit locals/statics locally, except statics with dtor - those are
3106  '' handled in _procAllocStaticVars(), including their dynamic array
3107  '' descriptors (if any).
3108  if( symbIsStatic( sym ) and symbHasDtor( sym ) ) then
3109  exit sub
3110  end if
3111 
3112  '' Check whether it's a dynamic array descriptor with a back link to
3113  '' the corresponding array that needs to be checked instead...
3114  '' (the descriptor needs to be handled like the array)
3115  assert( symbIsVar( sym ) )
3116  array = sym->var_.desc.array
3117  if( array ) then
3118  if( symbIsStatic( array ) and symbHasDtor( array ) ) then
3119  exit sub
3120  end if
3121  end if
3122 
3123  hEmitVariable( sym )
3124 end sub
3125 
3126 '':::::
3127 sub _emitDBG _
3128  ( _
3129  byval op as integer, _
3130  byval proc as FBSYMBOL ptr, _
3131  byval ex as integer _
3132  )
3133 
3134  if( op = AST_OP_DBG_LINEINI ) then
3135  ctx.linenum = ex
3136  end if
3137 
3138 end sub
3139 
3140 sub _emitComment( byval text as zstring ptr )
3141  static as string s
3142 
3143  s = *text
3144  s = trim( s )
3145 
3146  if( len( s ) > 0 ) then
3147  if( right( s, 1 ) = "\" ) then
3148  s += "not_an_escape"
3149  end if
3150  hWriteLine( "// " + s, TRUE )
3151  end if
3152 end sub
3153 
3154 function hGetMangledNameForASM( byval sym as FBSYMBOL ptr ) as string
3155  dim as string mangled
3156 
3157  mangled = *symbGetMangledName( sym )
3158 
3159  ''
3160  '' Must manually add an underscore prefix if the target requires it,
3161  '' because symb-mangling won't do that for -gen gcc.
3162  ''
3163  '' (assuming this function will only be used by NAKED procedures,
3164  '' which cannot have local variables or parameters)
3165  ''
3166  if( env.target.options and FB_TARGETOPT_UNDERSCORE ) then
3167  mangled = "_" + mangled
3168  end if
3169 
3170  if( symbIsProc( sym ) ) then
3171  if( symbGetProcMode( sym ) = FB_FUNCMODE_STDCALL ) then
3172  '' Add the @N suffix for STDCALL
3173  mangled += "@"
3174  mangled += str( symbCalcProcParamsLen( sym ) )
3175  end if
3176  end if
3177 
3178  function = mangled
3179 end function
3180 
3182  '' -asm intel: FB asm blocks are expected to be in Intel format as
3183  '' usual; we have to convert them to the GCC format here.
3184  '' -asm att: FB asm blocks are expected to be in the GCC format,
3185  '' i.e. quoted and including constraints if needed.
3186  ctx.asm_line = "__asm__"
3187 
3188  '' Only when inside normal procedures
3189  '' (NAKED procedures don't increase the indentation)
3190  if( sectionInsideProc( ) ) then
3191  ctx.asm_line += " __volatile__"
3192  end if
3193 
3194  ctx.asm_line += "( "
3195 
3196  if( env.clopt.asmsyntax = FB_ASMSYNTAX_INTEL ) then
3197  ctx.asm_line += """"
3198  if( sectionInsideProc( ) ) then
3199  ctx.asm_line += $"\t"
3200  end if
3201  ctx.asm_i = 0
3202  ctx.asm_output = ""
3203  ctx.asm_input = ""
3204  end if
3205 end sub
3206 
3207 sub _emitAsmText( byval text as zstring ptr )
3208  ctx.asm_line += *text
3209 end sub
3210 
3211 sub _emitAsmSymb( byval sym as FBSYMBOL ptr )
3212  dim as string id
3213 
3214  '' In NAKED procedure?
3215  if( sectionInsideProc( ) = FALSE ) then
3217  exit sub
3218  end if
3219 
3220  id = *symbGetMangledName( sym )
3221 
3222  if( env.clopt.asmsyntax = FB_ASMSYNTAX_INTEL ) then
3223  '' Insert %0 -%9 place holders, gcc will fill in the proper
3224  '' DWORD PTR [ebp+N] for them based on input/output operands.
3225  ' - unfortunately we don't know whether this symbol is used
3226  '' as input, output or both, so we enlist as operand for both,
3227  '' and use the %i for the output operand.
3228  ctx.asm_line += "%" + str( ctx.asm_i )
3229  ctx.asm_i += 1
3230 
3231  '' output operand constraint: "=m" (symbol)
3232  '' input operand constraint: "m" (symbol)
3233  if( len( ctx.asm_output ) > 0 ) then
3234  ctx.asm_output += ", "
3235  ctx.asm_input += ", "
3236  end if
3237  ctx.asm_output += """=m"" (" + id + ")"
3238  ctx.asm_input += """m"" (" + id + ")"
3239  else
3240  ctx.asm_line += id
3241  end if
3242 end sub
3243 
3245  if( env.clopt.asmsyntax = FB_ASMSYNTAX_INTEL ) then
3246  if( sectionInsideProc( ) ) then
3247  ctx.asm_line += $"\n"
3248  end if
3249 
3250  ctx.asm_line += """"
3251 
3252  '' Only when inside normal procedures
3253  '' (NAKED procedures don't increase the indentation)
3254  if( sectionInsideProc( ) ) then
3255  ctx.asm_line += " : " + ctx.asm_output
3256  ctx.asm_line += " : " + ctx.asm_input
3257 
3258  '' We don't know what registers etc. will be trashed,
3259  '' so assume everything...
3260  ctx.asm_line += " : ""cc"", ""memory"""
3261  ctx.asm_line += ", ""eax"", ""ebx"", ""ecx"", ""edx"", ""esp"", ""edi"", ""esi"""
3262  if( env.clopt.fputype = FB_FPUTYPE_SSE ) then
3263  ctx.asm_line += ", ""mm0"", ""mm1"", ""mm2"", ""mm3"", ""mm4"", ""mm5"", ""mm6"", ""mm7"""
3264  ctx.asm_line += ", ""xmm0"", ""xmm1"", ""xmm2"", ""xmm3"", ""xmm4"", ""xmm5"", ""xmm6"", ""xmm7"""
3265  end if
3266  end if
3267  end if
3268 
3269  ctx.asm_line += " );"
3270 
3272 end sub
3273 
3274 sub _emitVarIniBegin( byval sym as FBSYMBOL ptr )
3275  ctx.varini = ""
3276  ctx.variniscopelevel = 0
3277 end sub
3278 
3279 sub _emitVarIniEnd( byval sym as FBSYMBOL ptr )
3280  hEmitVar( sym, ctx.varini )
3281  ctx.varini = ""
3282 end sub
3283 
3285  if( ctx.variniscopelevel > 0 ) then
3286  ctx.varini += ", "
3287  end if
3288 end sub
3289 
3290 sub _emitVarIniI( byval sym as FBSYMBOL ptr, byval value as longint )
3291  var dtype = symbGetType( sym )
3292  var l = exprNewIMMi( value, dtype )
3293  l = exprNewCAST( dtype, sym->subtype, l )
3294  ctx.varini += exprFlush( l )
3295  hVarIniSeparator( )
3296 end sub
3297 
3298 sub _emitVarIniF( byval sym as FBSYMBOL ptr, byval value as double )
3299  var dtype = symbGetType( sym )
3300  var l = exprNewIMMf( value, dtype )
3301  l = exprNewCAST( dtype, sym->subtype, l )
3302  ctx.varini += exprFlush( l )
3303  hVarIniSeparator( )
3304 end sub
3305 
3306 sub _emitVarIniOfs( byval sym as FBSYMBOL ptr, byval ofs as longint )
3307  dim as EXPRNODE ptr l = any
3308 
3309  l = exprNewOFFSET( sym, ofs )
3310 
3311  '' Cast to void* to prevent gcc ptr warnings (FB should handle that)
3312  l = exprNewCAST( typeAddrOf( FB_DATATYPE_VOID ), NULL, l )
3313 
3314  ctx.varini += exprFlush( l )
3315  hVarIniSeparator( )
3316 end sub
3317 
3318 sub _emitVarIniStr _
3319  ( _
3320  byval varlength as longint, _ '' without null terminator
3321  byval literal as zstring ptr, _
3322  byval litlength as longint _ '' without null terminator
3323  )
3324 
3325  '' Simple fixed-length string initialized from string literal
3326  '' "..."
3327 
3328  '' String literal too long? (GCC would show a warning)
3329  if( litlength > varlength ) then
3330  '' Cut off; may be empty afterwards
3331  litlength = varlength
3332  end if
3333 
3334  hBuildStrLit( ctx.varini, hUnescape( literal ), litlength + 1 )
3335 
3336  hVarIniSeparator( )
3337 
3338 end sub
3339 
3340 sub _emitVarIniWstr _
3341  ( _
3342  byval varlength as longint, _ '' without null terminator
3343  byval literal as wstring ptr, _
3344  byval litlength as longint _ '' without null terminator
3345  )
3346 
3347  dim as uinteger ch = any
3348  dim as integer wcharsize = any
3349 
3350  '' In Linux GCC, wchar_t and thus L"..." expressions use signed int,
3351  '' but FB uses unsigned integers. But GCC will show an error when doing
3352  '' unsigned int mywstring[] = L"foo"
3353  '' so we must emit it as
3354  '' unsigned int mywstring[] = { L'f', L'o', L'o' }
3355 
3356  ctx.varini += "{ "
3357  literal = hUnescapeW( literal )
3358  wcharsize = typeGetSize( FB_DATATYPE_WCHAR )
3359 
3360  '' String literal too long?
3361  if( litlength > varlength ) then
3362  '' Cut off; may be empty afterwards
3363  litlength = varlength
3364  end if
3365 
3366  for i as integer = 0 to litlength - 1
3367  if( i > 0 ) then
3368  ctx.varini += ", "
3369  end if
3370 
3371  ctx.varini += "L'"
3372 
3373  ch = (*literal)[i]
3374 
3375  if( hCharNeedsEscaping( ch, asc( "'" ) ) ) then
3376  ctx.varini += $"\x" + hex( ch, wcharsize * 2 )
3377  else
3378  ctx.varini += chr( ch )
3379  end if
3380 
3381  ctx.varini += "'"
3382  next
3383 
3384  ctx.varini += " }"
3385 
3386  hVarIniSeparator( )
3387 
3388 end sub
3389 
3390 sub _emitVarIniPad( byval bytes as longint )
3391  '' Nothing to do -- we're using {...} for structs and each array
3392  '' dimension, and gcc will zero-initialize any uninitialized elements,
3393  '' aswell as add padding between fields etc. where needed.
3394 end sub
3395 
3397  ctx.variniscopelevel += 1
3398  ctx.varini += "{ "
3399 end sub
3400 
3402  '' Trim separator at the end, to make the output look a bit more clean
3403  '' (this isn't needed though, since the extra comma is allowed in C)
3404  if( right( ctx.varini, 2 ) = ", " ) then
3405  ctx.varini = left( ctx.varini, len( ctx.varini ) - 2 )
3406  end if
3407 
3408  ctx.varini += " }"
3409  ctx.variniscopelevel -= 1
3410  hVarIniSeparator( )
3411 end sub
3412 
3414  hWriteLine( "", TRUE )
3415 
3416  '' static - should not be a public symbol
3417  '' const - read-only
3418  '' char[] - a string
3419  '' used attribute - prevent removal due to optimizations
3420  '' section attribute - This global must be put into a custom .fbctinf
3421  '' section, as done by the ASM backend.
3422  ctx.fbctinf = "static const char "
3423  ctx.fbctinf += "__attribute__((used, section(""." + FB_INFOSEC_NAME + """))) "
3424  ctx.fbctinf += "__fbctinf[] = """
3425 end sub
3426 
3427 sub _emitFbctinfString( byval s as zstring ptr )
3428  ctx.fbctinf += *s + $"\0"
3429 end sub
3430 
3432  '' Cut off unnecessary \0 at the end; gcc will add it automatically,
3433  '' since it's a string literal...
3434  if( right( ctx.fbctinf, 2 ) = $"\0" ) then
3435  ctx.fbctinf = left( ctx.fbctinf, len( ctx.fbctinf ) - 2 )
3436  end if
3437  ctx.fbctinf += """;"
3438  hWriteLine( ctx.fbctinf, TRUE )
3439 end sub
3440 
3441 sub _emitProcBegin _
3442  ( _
3443  byval proc as FBSYMBOL ptr, _
3444  byval initlabel as FBSYMBOL ptr _
3445  )
3446 
3447  dim as string mangled
3448 
3449  hWriteLine( "", TRUE )
3450 
3451  if( env.clopt.debug ) then
3452  _emitDBG( AST_OP_DBG_LINEINI, proc, proc->proc.ext->dbg.iniline )
3453  end if
3454 
3455  '' NAKED procedure? Use inline asm, since gcc doesn't support
3456  '' __attribute__((naked)) on x86
3457  if( symbIsNaked( proc ) ) then
3458  mangled = hGetMangledNameForASM( proc )
3459  hWriteLine( "__asm__( "".globl " + mangled + """ );" )
3460  hWriteLine( "__asm__( """ + mangled + ":"" );" )
3461  exit sub
3462  end if
3463 
3464 #if 0
3465  '' If the asm("mangledname") work-around is needed to tell gcc to not
3466  '' add the @N suffix for stdcall procedures, emit an extra prototype
3467  '' right above the procedure body, because asm() is only allowed on
3468  '' prototypes.
3469  select case( symbGetProcMode( proc ) )
3470  case FB_FUNCMODE_STDCALL_MS, FB_FUNCMODE_PASCAL
3472  end select
3473 #endif
3474 
3475  sectionBegin( )
3476 
3478 
3479  hWriteLine( "{" )
3480  sectionIndent( )
3481 
3482 end sub
3483 
3484 sub _emitProcEnd _
3485  ( _
3486  byval proc as FBSYMBOL ptr, _
3487  byval initlabel as FBSYMBOL ptr, _
3488  byval exitlabel as FBSYMBOL ptr _
3489  )
3490 
3491  dim as string mangled
3492 
3493  '' NAKED procedure? Use inline asm, since gcc doesn't support
3494  '' __attribute__((naked)) on x86
3495  if( symbIsNaked( proc ) ) then
3496  '' Emit .size like ASM backend, for Linux
3497  if( env.clopt.target = FB_COMPTARGET_LINUX ) then
3498  mangled = hGetMangledNameForASM( proc )
3499  hWriteLine( "__asm__( "".size " + mangled + ", .-" + mangled + """ );", TRUE )
3500  end if
3501  exit sub
3502  end if
3503 
3504  sectionUnindent( )
3505  hWriteLine( "}" )
3506 
3507  sectionEnd( )
3508 
3509 end sub
3510 
3511 sub _emitPushArg _
3512  ( _
3513  byval param as FBSYMBOL ptr, _
3514  byval vr as IRVREG ptr, _
3515  byval udtlen as longint, _
3516  byval level as integer _
3517  )
3518 
3519  '' Remember for later, so during _emitCall[Ptr] we can emit the whole
3520  '' call in one go
3521  dim as IRCALLARG ptr arg = listNewNode( @ctx.callargs )
3522  arg->param = param
3523  arg->vr = vr
3524  arg->level = level
3525 
3526 end sub
3527 
3528 sub _emitScopeBegin( byval s as FBSYMBOL ptr )
3529  sectionBegin( )
3530  hWriteLine( "{", TRUE )
3531  sectionIndent( )
3532 end sub
3533 
3534 sub _emitScopeEnd( byval s as FBSYMBOL ptr )
3535  sectionUnindent( )
3536  hWriteLine( "}", TRUE )
3537  sectionEnd( )
3538 end sub
3539 
3540 ''::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
3541 
3542 dim shared as IR_VTBL irhlc_vtbl = _
3543 ( _
3544  @_init, _
3545  @_end, _
3546  @_emitBegin, _
3547  @_emitEnd, _
3548  @_getOptionValue, _
3549  @_procBegin, _
3550  @_procEnd, _
3551  NULL, _
3552  NULL, _
3553  NULL, _
3554  @_scopeBegin, _
3555  @_scopeEnd, _
3557  @_emitStore, _
3558  @_emitLabel, _
3559  @_emitLabel, _
3560  NULL, _
3561  @_emitProcBegin, _
3562  @_emitProcEnd, _
3563  @_emitPushArg, _
3564  @_emitAsmBegin, _
3565  @_emitAsmText, _
3566  @_emitAsmSymb, _
3567  @_emitAsmEnd, _
3568  @_emitComment, _
3569  @_emitBop, _
3570  @_emitUop, _
3571  @_emitStore, _
3572  @_emitSpillRegs, _
3573  @_emitLoad, _
3574  @_emitLoadRes, _
3575  NULL, _
3576  @_emitAddr, _
3577  @_emitCall, _
3578  @_emitCallPtr, _
3579  NULL, _
3580  @_emitJumpPtr, _
3581  @_emitBranch, _
3582  @_emitJmpTb, _
3583  @_emitMem, _
3584  @_emitScopeBegin, _
3585  @_emitScopeEnd, _
3586  @_emitDECL, _
3587  @_emitDBG, _
3588  @_emitVarIniBegin, _
3589  @_emitVarIniEnd, _
3590  @_emitVarIniI, _
3591  @_emitVarIniF, _
3592  @_emitVarIniOfs, _
3593  @_emitVarIniStr, _
3594  @_emitVarIniWstr, _
3595  @_emitVarIniPad, _
3598  @_emitFbctinfBegin, _
3599  @_emitFbctinfString, _
3600  @_emitFbctinfEnd, _
3601  @_allocVreg, _
3602  @_allocVrImm, _
3603  @_allocVrImmF, _
3604  @_allocVrVar, _
3605  @_allocVrIdx, _
3606  @_allocVrPtr, _
3607  @_allocVrOfs, _
3608  @_setVregDataType, _
3609  NULL, _
3610  NULL, _
3611  NULL, _
3612  NULL _
3613 )
3614