FreeBASIC  0.91.0
ast-gosub.bas
Go to the documentation of this file.
1 '' ast support for GOSUB/RETURN (for asm and setjmp/longjmp implementations)
2 ''
3 '' chng: apr/2008 written [jeffm]
4 
5 #include once "fb.bi"
6 #include once "fbint.bi"
7 #include once "ast.bi"
8 #include once "lex.bi"
9 #include once "rtl.bi"
10 
11 ''
12 '' GOSUB/RETURN support
13 ''
14 
15 '' -gen option is used to select the GOSUB implementation:
16 '' - GAS backend will use CALL/RET
17 '' - C backend will use setjmp/longjmp
18 '' However, setjmp/longjmp implementation will also work with the GAS backend,
19 '' but since it is like 1000 times slower than CALL/RET, it isn't. To explicitly
20 '' select setjmp/longjmp implementation with ASM backend, use "-z gosub-with-setjmp"
21 '' on the command line (jeffm)
22 ''
23 #define AsmBackend() _
24  ( (env.clopt.backend = FB_BACKEND_GAS) and _
25  (env.clopt.gosubsetjmp = FALSE) )
26 
27 sub astGosubAddInit( byval proc as FBSYMBOL ptr )
28  dim as FBARRAYDIM dTB(0) = any
29  dim as FBSYMBOL ptr sym = any
30  dim as ASTNODE ptr var_decl = any
31  dim as integer dtype = any
32 
34 
35  if( symbGetProcStatGosub( proc ) ) then
36  exit sub
37  end if
38 
39  if( AsmBackend() ) then
40  '' create a local counter for depth of gosub calls
41  '' this is needed for the error checks
42 
43  '' DIM "{gosubctx}" as integer = 0
44  dtype = FB_DATATYPE_INTEGER
45  else
46 
47  '' create a local pointer to the gosub stack
48  '' Note: on the rtlib side, GOSUBCTX contains one pointer field only
49  '' so this cheap trick using an any ptr will work.
50 
51  '' DIM "{gosubctx}" as GOSUBCTX = NULL
52  dtype = typeAddrOf( FB_DATATYPE_VOID )
53  end if
54 
55  sym = symbAddImplicitVar( dtype, NULL, FB_SYMBOPT_UNSCOPE )
56 
57  var_decl = astNewDECL( sym, TRUE )
58 
59  symbSetIsDeclared( sym )
60 
61  astAddUnscoped( var_decl )
62 
63  symbSetProcGosubSym( proc, sym )
64  symbSetProcStatGosub( proc )
65 end sub
66 
67 sub astGosubAddJmp _
68  ( _
69  byval proc as FBSYMBOL ptr, _
70  byval l as FBSYMBOL ptr _
71  )
72 
73  dim as FBSYMBOL ptr label = any
74 
75  '' make sure gosub-ctx var is declared
77 
78  if( AsmBackend() ) then
79  '' ctx += 1
80  astAdd( astBuildVarInc( symbGetProcGosubSym( proc ), 1 ) )
81 
82  astAdd( astNewBRANCH( AST_OP_CALL, l ) )
83  else
84  '' if ( setjmp( fb_GosubPush( @ctx ) ) ) = 0 ) then
85  label = symbAddLabel( NULL )
86 
88  astNewBOP( AST_OP_EQ, _
90  astNewADDROF( astNewVAR( symbGetProcGosubSym( proc ) ) ) _
91  ) ), _
92  astNewCONSTi( 0 ) ), _
93  label, _
94  FALSE ) )
95 
96  '' goto label
97  astAdd( astNewBRANCH( AST_OP_JMP, l ) )
98 
99  '' end if
100  astAdd( astNewLABEL( label ) )
101  end if
102 end sub
103 
104 sub astGosubAddJumpPtr _
105  ( _
106  byval proc as FBSYMBOL ptr, _
107  byval jumptb as ASTNODE ptr, _
108  byval exitlabel as FBSYMBOL ptr _
109  )
110 
111  dim as FBSYMBOL ptr label = any
112 
113  '' make sure gosub-ctx var is declared
115 
116  if( AsmBackend() ) then
117  '' ctx += 1
118  astAdd( astBuildVarInc( symbGetProcGosubSym( proc ), 1 ) )
119 
120  astAdd( astNewSTACK( AST_OP_PUSH, _
121  astNewADDROF( astNewVAR( exitlabel ) ) ) )
122 
123  '' goto table[expr]
124  astAdd( jumptb )
125  else
126  '' make sure gosub-ctx var is declared
128 
129  '' if ( setjmp( fb_GosubPush( @ctx ) ) ) = 0 ) then
130  label = symbAddLabel( NULL )
131 
133  astNewBOP( AST_OP_EQ, _
135  astNewADDROF( astNewVAR( symbGetProcGosubSym( proc ) ) ) _
136  ) ), _
137  astNewCONSTi( 0 ) ), _
138  label, _
139  FALSE ) )
140 
141  '' goto table[expr]
142  astAdd( jumptb )
143 
144  '' end if
145  astAdd( astNewLABEL( label ) )
146 
147  '' jump to exit label
148  astAdd( astNewBRANCH( AST_OP_JMP, exitlabel ) )
149  end if
150 end sub
151 
152 '':::::
153 function astGosubAddReturn _
154  ( _
155  byval proc as FBSYMBOL ptr, _
156  byval l as FBSYMBOL ptr _
157  ) as integer
158 
159  dim as FBSYMBOL ptr label = any
160 
161  '' make sure gosub-ctx var is declared
163 
164  if( AsmBackend() ) then
165 
166  '' if( ctx <> 0 ) then
167  label = symbAddLabel( NULL )
168 
170  astNewBOP( AST_OP_NE, _
171  astNewVAR( symbGetProcGosubSym( proc ) ), _
172  astNewCONSTi( 0 ) ), _
173  label, _
174  FALSE ) )
175 
176  '' ctx -= 1
177  astAdd( astBuildVarInc( symbGetProcGosubSym( proc ), -1 ) )
178 
179  '' RETURN
180  if( l = NULL ) then
181  astAdd( astNewBRANCH( AST_OP_RET, NULL ) )
182 
183  '' RETURN [label]
184  else
185  '' pop return address from the stack. Uses "POP immed" which will be
186  '' handled specially in emit_x86.bas::_emitPOPI()
187  astAdd( astNewSTACK( AST_OP_POP, _
188  astNewCONSTi( env.pointersize ) ) )
189 
190  '' GOTO label
191  astAdd( astNewBRANCH( AST_OP_JMP, l ) )
192 
193  end if
194 
195  '' else
196  astAdd( astNewLABEL( label ) )
197 
198  '' set/throw error
199  rtlErrorSetNum( astNewCONSTi( FB_RTERROR_RETURNWITHOUTGOSUB ) )
200  if( env.clopt.errorcheck ) then
201  rtlErrorThrow( astNewCONSTi( FB_RTERROR_RETURNWITHOUTGOSUB ), _
202  lexLineNum( ), env.inf.name )
203  end if
204 
205  '' end if
206 
207  function = TRUE
208 
209  else
210 
211  '' RETURN
212  if( l = NULL ) then
213 
214  '' fb_GosubReturn( @ctx )
215  function = rtlGosubReturn( astNewADDROF( astNewVAR( symbGetProcGosubSym( proc ) ) ) )
216 
217  '' RETURN [label]
218  else
219 
220  '' if( fb_GosubPop( @ctx ) = 0 ) then
221  label = symbAddLabel( NULL )
222 
224  astNewBOP( AST_OP_EQ, _
225  rtlGosubPop( _
226  astNewADDROF( astNewVAR( symbGetProcGosubSym( proc ) ) ) _
227  ), _
228  astNewCONSTi( 0 ) ), _
229  label, _
230  FALSE ) )
231 
232  '' goto label
233  astAdd( astNewBRANCH( AST_OP_JMP, l ) )
234 
235  '' else
236  astAdd( astNewLABEL( label ) )
237 
238  '' set/throw error
239  rtlErrorSetNum( astNewCONSTi( FB_RTERROR_RETURNWITHOUTGOSUB ) )
240  if( env.clopt.errorcheck ) then
241  rtlErrorThrow( astNewCONSTi( FB_RTERROR_RETURNWITHOUTGOSUB ), _
242  lexLineNum( ), env.inf.name )
243  end if
244 
245  '' end if
246 
247  function = TRUE
248 
249  end if
250 
251  end if
252 
253 end function
254 
255 sub astGosubAddExit(byval proc as FBSYMBOL ptr)
256  if( symbGetProcStatGosub( proc ) ) then
257  if( AsmBackend() = FALSE ) then
258  astAdd( rtlGosubExit( astNewADDROF( astNewVAR( symbGetProcGosubSym( proc ) ) ) ) )
259  end if
260  end if
261 end sub
262