FreeBASIC  0.91.0
ast-node-branch.bas
Go to the documentation of this file.
1 '' AST branch nodes (including jump tables)
2 ''
3 '' chng: sep/2004 written [v1ctor]
4 
5 
6 #include once "fb.bi"
7 #include once "fbint.bi"
8 #include once "ir.bi"
9 #include once "ast.bi"
10 
11 '':::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
12 '' branches (l = link to the stream to be also flushed, if any; r = NULL)
13 '':::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
14 
15 '':::::
16 function astNewBRANCH _
17  ( _
18  byval op as integer, _
19  byval label as FBSYMBOL ptr, _
20  byval l as ASTNODE ptr _
21  ) as ASTNODE ptr
22 
23  dim as ASTNODE ptr n = any
24  dim as integer dtype = any
25 
26  if( l = NULL ) then
27  dtype = FB_DATATYPE_INVALID
28  else
29  dtype = astGetFullType( l )
30  end if
31 
32  '' alloc new node
33  n = astNewNode( AST_NODECLASS_BRANCH, dtype )
34  function = n
35 
36  n->l = l
37  n->op.op = op
38  n->op.ex = label
39  n->op.options = AST_OPOPT_ALLOCRES
40 
41 end function
42 
43 '':::::
44 function astLoadBRANCH _
45  ( _
46  byval n as ASTNODE ptr _
47  ) as IRVREG ptr
48 
49  dim as ASTNODE ptr l = any
50  dim as IRVREG ptr vr = any
51 
52  l = n->l
53 
54  if( l <> NULL ) then
55  vr = astLoad( l )
56  astDelNode( l )
57  else
58  vr = NULL
59  end if
60 
61  if( ast.doemit ) then
62  '' pointer?
63  if( n->op.ex = NULL ) then
64  '' jump or call?
65  select case n->op.op
66  case AST_OP_JUMPPTR
67  irEmitJUMPPTR( vr )
68 
69  case AST_OP_CALLPTR
70  irEmitCALLPTR( vr, NULL, 0, -1 )
71 
72  case AST_OP_RET
73  irEmitRETURN( 0 )
74  end select
75 
76  else
77  irEmitBRANCH( n->op.op, n->op.ex )
78  end if
79  end if
80 
81  function = vr
82 
83 end function
84 
85 '' JMPTB (l = condition expr; r = NULL)
86 '' Takes a condition expression and two arrays representing constant value
87 '' and label pairs, and a fallback label.
88 function astNewJMPTB _
89  ( _
90  byval l as ASTNODE ptr, _
91  byval tbsym as FBSYMBOL ptr, _
92  byval values1 as ulongint ptr, _
93  byval labels1 as FBSYMBOL ptr ptr, _
94  byval labelcount as integer, _
95  byval deflabel as FBSYMBOL ptr, _
96  byval minval as ulongint, _
97  byval maxval as ulongint _
98  ) as ASTNODE ptr
99 
100  dim as ASTNODE ptr n = any, tree = any
101  dim as ulongint ptr values = any
102  dim as FBSYMBOL ptr ptr labels = any
103 
104  tree = NULL
105 
106  '' The jump table can be empty, in case of a SELECT CASE AS CONST
107  '' with only a CASE ELSE. (it's not worth it to "optimize" that useless
108  '' case, but it must still be handled without crashing the compiler...)
109  if( labelcount > 0 ) then
110  '' Duplicate the values/labels arrays
111  values = callocate( sizeof( *values ) * labelcount )
112  labels = callocate( sizeof( *labels ) * labelcount )
113  for i as integer = 0 to labelcount - 1
114  values[i] = values1[i]
115  labels[i] = labels1[i]
116  next
117  else
118  values = NULL
119  labels = NULL
120  end if
121 
122  n = astNewNode( AST_NODECLASS_JMPTB, FB_DATATYPE_INVALID )
123 
124  n->l = l
125  n->sym = tbsym
126  n->jmptb.values = values
127  n->jmptb.labels = labels
128  n->jmptb.labelcount = labelcount
129  n->jmptb.deflabel = deflabel
130  n->jmptb.minval = minval
131  n->jmptb.maxval = maxval
132 
133  function = astNewLINK( tree, n )
134 end function
135 
136 function astLoadJMPTB( byval n as ASTNODE ptr ) as IRVREG ptr
137  dim as IRVREG ptr v1 = any
138 
139  v1 = astLoad( n->l )
140  astDelNode( n->l )
141 
142  if( ast.doemit ) then
143  irEmitJMPTB( v1, n->sym, n->jmptb.values, n->jmptb.labels, _
144  n->jmptb.labelcount, n->jmptb.deflabel, _
145  n->jmptb.minval, n->jmptb.maxval )
146  end if
147 
148  deallocate( n->jmptb.values )
149  deallocate( n->jmptb.labels )
150 
151  function = NULL
152 end function
153 
154 function astBuildJMPTB _
155  ( _
156  byval tempvar as FBSYMBOL ptr, _
157  byval values1 as ulongint ptr, _
158  byval labels1 as FBSYMBOL ptr ptr, _
159  byval labelcount as integer, _
160  byval deflabel as FBSYMBOL ptr, _
161  byval minval as ulongint, _
162  byval maxval as ulongint _
163  ) as ASTNODE ptr
164 
165  dim as ASTNODE ptr tree = any, l = any
166  static as FBARRAYDIM dTB(0)
167  dim as FBSYMBOL ptr tbsym = any
168 
169  assert( symbGetType( tempvar ) = FB_DATATYPE_UINT or symbGetType( tempvar ) = FB_DATATYPE_ULONGINT )
170 
171  tree = NULL
172 
173  '' Empty jump table? Just jump to the ELSE block or the END SELECT
174  '' (GAS/LLVM backends only, the GCC backend handles this case manually)
175  select case( env.clopt.backend )
176  case FB_BACKEND_GAS, FB_BACKEND_LLVM
177  if( labelcount <= 0 ) then
178  return astNewBRANCH( AST_OP_JMP, deflabel )
179  end if
180  end select
181 
182  ''
183  '' For the ASM backend, the checks and the jump are emitted separately,
184  '' so the normal x86 backend code generation & register allocation
185  '' can be re-used.
186  ''
187  '' For the C and LLVM backends, the irEmitJMPTB() handles it all,
188  '' because for C it's easy to enough to implement the checks "manually"
189  '' and for the LLVM backend the LLVM instructions do it already anyways.
190  ''
191  '' This means for the ASM backend we need to add a symbol representing
192  '' the jump table, so we can do a VAR access on it. It won't be emitted
193  '' like normal vars though, it just makes the jump table's label name
194  '' available/known to the AST. This wouldn't be needed if the x86
195  '' backend generated the checking code automatically, because then the
196  '' jump table's name would be needed there (internally) only, not here
197  '' in the AST.
198  ''
199  if( env.clopt.backend = FB_BACKEND_GAS ) then
200  tbsym = symbAddVar( symbUniqueLabel( ), NULL, _
201  typeAddrOf( FB_DATATYPE_VOID ), NULL, 0, _
202  1, dTB(), FB_SYMBATTRIB_SHARED )
203  symbSetIsJumpTb( tbsym )
204  symbSetIsInitialized( tbsym )
205 
206  '' if( expr < minval or expr > maxval ) then goto deflabel
207  '' optimised to:
208  '' if( cunsg(expr - minval) > (maxval - minval) ) then goto deflabel
209  tree = astNewLINK( tree, _
210  astNewBOP( AST_OP_GT, astNewBOP( AST_OP_SUB, _
211  astNewVAR( tempvar ), _
212  astNewCONSTi( minval, FB_DATATYPE_UINT ) ), _
213  astNewCONSTi( maxval - minval, FB_DATATYPE_UINT ), _
214  deflabel, AST_OPOPT_NONE ) )
215 
216  '' goto table[expr - minval]
217  tree = astNewLINK( tree, _
218  astNewBRANCH( AST_OP_JUMPPTR, NULL, _
219  astNewIDX( astNewVAR( tbsym, -minval * env.pointersize ), _
220  astNewBOP( AST_OP_MUL, _
221  astNewVAR( tempvar ), _
222  astNewCONSTi( env.pointersize, FB_DATATYPE_UINT ) ), _
223  typeAddrOf( FB_DATATYPE_VOID ), NULL ) ) )
224  else
225  tbsym = NULL
226  end if
227 
228  tree = astNewLINK( tree, _
229  astNewJMPTB( astNewVAR( tempvar ), tbsym, _
230  values1, labels1, labelcount, deflabel, minval, maxval ) )
231 
232  function = tree
233 end function
234 
235 function astNewLOOP _
236  ( _
237  byval label as FBSYMBOL ptr, _
238  byval tree as ASTNODE ptr _
239  ) as ASTNODE ptr
240 
241  dim as ASTNODE ptr n = any
242 
243  n = astNewNode( AST_NODECLASS_LOOP, FB_DATATYPE_INVALID )
244  function = n
245 
246  n->l = tree
247  n->op.ex = label
248 
249  '' just faking something, so it's safe to handle LOOPs in the same
250  '' context like BRANCH nodes
251  n->op.op = AST_OP_FOR
252  n->op.options = 0
253 
254 end function
255 
256 function astLoadLOOP( byval n as ASTNODE ptr ) as IRVREG ptr
257  astLoad( n->l )
258  astDelNode( n->l )
259  function = NULL
260 end function
261