1 {
2     Copyright (c) 1998-2011 by Florian Klaempfl and Jonas Maebe
3 
4     Generate assembler for nodes that influence the flow for the JVM
5 
6     This program is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10 
11     This program is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15 
16     You should have received a copy of the GNU General Public License
17     along with this program; if not, write to the Free Software
18     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 
20  ****************************************************************************
21 }
22 unit njvmflw;
23 
24 {$i fpcdefs.inc}
25 
26 interface
27 
28     uses
29       aasmbase,node,nflw,ncgflw;
30 
31     type
32        tjvmfornode = class(tcgfornode)
pass_1null33           function pass_1: tnode; override;
34        end;
35 
36        tjvmraisenode = class(traisenode)
pass_typechecknull37           function pass_typecheck: tnode; override;
pass_1null38           function pass_1: tnode; override;
39           procedure pass_generate_code;override;
40        end;
41 
42        tjvmtryexceptnode = class(ttryexceptnode)
43           procedure pass_generate_code;override;
44          protected
45           procedure adjust_estimated_stack_size; override;
46        end;
47 
48        tjvmtryfinallynode = class(ttryfinallynode)
49           procedure pass_generate_code;override;
50          protected
51           procedure adjust_estimated_stack_size; override;
52        end;
53 
54        tjvmonnode = class(tonnode)
55           procedure pass_generate_code;override;
56        end;
57 
58 implementation
59 
60     uses
61       verbose,globals,systems,globtype,constexp,
62       symconst,symdef,symsym,aasmtai,aasmdata,aasmcpu,defutil,jvmdef,defcmp,
63       procinfo,cgbase,pass_1,pass_2,parabase,
64       cpubase,cpuinfo,
65       nbas,nld,ncon,ncnv,
66       tgobj,paramgr,
67       cgutils,hlcgobj,hlcgcpu
68       ;
69 
70 {*****************************************************************************
71                              TFJVMFORNODE
72 *****************************************************************************}
73 
tjvmfornode.pass_1null74     function tjvmfornode.pass_1: tnode;
75       var
76         iteratortmp: ttempcreatenode;
77         olditerator: tnode;
78         block,
79         newbody: tblocknode;
80         stat,
81         newbodystat: tstatementnode;
82       begin
83         { transform for-loops with enums to:
84             for tempint:=ord(lowval) to ord(upperval) do
85               begin
86                 originalctr:=tenum(tempint);
87                 <original loop body>
88               end;
89 
90           enums are class instances in Java and hence can't be increased or so.
91           The type conversion consists of an array lookup in a final method,
92           so it shouldn't be too expensive.
93         }
94         if left.resultdef.typ=enumdef then
95           begin
96             block:=internalstatements(stat);
97             iteratortmp:=ctempcreatenode.create(s32inttype,left.resultdef.size,tt_persistent,true);
98             addstatement(stat,iteratortmp);
99             olditerator:=left;
100             left:=ctemprefnode.create(iteratortmp);
101             inserttypeconv_explicit(right,s32inttype);
102             inserttypeconv_explicit(t1,s32inttype);
103             newbody:=internalstatements(newbodystat);
104             addstatement(newbodystat,cassignmentnode.create(olditerator,
105               ctypeconvnode.create_explicit(ctemprefnode.create(iteratortmp),
106                 olditerator.resultdef)));
107             addstatement(newbodystat,t2);
108             addstatement(stat,cfornode.create(left,right,t1,newbody,lnf_backward in loopflags));
109             addstatement(stat,ctempdeletenode.create(iteratortmp));
110             left:=nil;
111             right:=nil;
112             t1:=nil;
113             t2:=nil;
114             result:=block
115           end
116         else
117           result:=inherited pass_1;
118       end;
119 
120 {*****************************************************************************
121                              SecondRaise
122 *****************************************************************************}
123 
124     var
125       current_except_loc: tlocation;
126 
tjvmraisenode.pass_typechecknull127     function tjvmraisenode.pass_typecheck: tnode;
128       begin
129          Result:=inherited pass_typecheck;
130          if codegenerror then
131            exit;
132          { Java exceptions must descend from java.lang.Throwable }
133          if assigned(left) and
134             not def_is_related(left.resultdef,java_jlthrowable) then
135            MessagePos2(left.fileinfo,type_e_incompatible_types,left.resultdef.typename,'class(JLThrowable)');
136          { Java exceptions cannot be raised "at" a specific location }
137          if assigned(right) then
138            MessagePos(right.fileinfo,parser_e_illegal_expression);
139       end;
140 
141 
tjvmraisenode.pass_1null142     function tjvmraisenode.pass_1: tnode;
143       begin
144          result:=nil;
145          expectloc:=LOC_VOID;
146          if assigned(left) then
147            firstpass(left);
148       end;
149 
150 
151     procedure tjvmraisenode.pass_generate_code;
152       begin
153         if assigned(left) then
154           begin
155             secondpass(left);
156             thlcgjvm(hlcg).a_load_loc_stack(current_asmdata.CurrAsmList,left.resultdef,left.location);
157           end
158         else
159           thlcgjvm(hlcg).a_load_loc_stack(current_asmdata.CurrAsmList,java_jlthrowable,current_except_loc);
160         current_asmdata.CurrAsmList.Concat(taicpu.op_none(a_athrow));
161         thlcgjvm(hlcg).decstack(current_asmdata.CurrAsmList,1);
162       end;
163 
164 
165 {*****************************************************************************
166                              SecondTryExcept
167 *****************************************************************************}
168 
169     var
170        begintrylabel,
171        endtrylabel: tasmlabel;
172        endexceptlabel : tasmlabel;
173 
174 
175     procedure tjvmtryexceptnode.pass_generate_code;
176 
177       var
178          oldendexceptlabel,
179          oldbegintrylabel,
180          oldendtrylabel,
181          defaultcatchlabel: tasmlabel;
182          oldflowcontrol,tryflowcontrol,
183          exceptflowcontrol : tflowcontrol;
184          prev_except_loc: tlocation;
185       begin
186          location_reset(location,LOC_VOID,OS_NO);
187 
188          oldflowcontrol:=flowcontrol;
189          flowcontrol:=[fc_inflowcontrol];
190          { this can be called recursivly }
191          oldbegintrylabel:=begintrylabel;
192          oldendtrylabel:=endtrylabel;
193          oldendexceptlabel:=endexceptlabel;
194 
195          { get new labels for the control flow statements }
196          current_asmdata.getaddrlabel(begintrylabel);
197          current_asmdata.getaddrlabel(endtrylabel);
198          current_asmdata.getjumplabel(endexceptlabel);
199 
200          { try block }
201          { set control flow labels for the try block }
202 
203          hlcg.a_label(current_asmdata.CurrAsmList,begintrylabel);
204          secondpass(left);
205          hlcg.a_label(current_asmdata.CurrAsmList,endtrylabel);
206          tryflowcontrol:=flowcontrol;
207 
208          { jump over exception handling blocks }
209          current_asmdata.CurrAsmList.concat(tai_marker.create(mark_NoLineInfoStart));
210          hlcg.a_jmp_always(current_asmdata.CurrAsmList,endexceptlabel);
211          current_asmdata.CurrAsmList.concat(tai_marker.create(mark_NoLineInfoEnd));
212 
213          { set control flow labels for the except block }
214          { and the on statements                        }
215 
216          flowcontrol:=[fc_inflowcontrol];
217          { on-statements }
218          if assigned(right) then
219            secondpass(right);
220 
221          { default handling except handling }
222          if assigned(t1) then
223            begin
224              current_asmdata.getaddrlabel(defaultcatchlabel);
225              current_asmdata.CurrAsmList.concat(tai_jcatch.create(
226                'all',begintrylabel,endtrylabel,defaultcatchlabel));
227              hlcg.a_label(current_asmdata.CurrAsmList,defaultcatchlabel);
228              { here we don't have to reset flowcontrol           }
229              { the default and on flowcontrols are handled equal }
230 
231              { get the exception object from the stack and store it for use by
232                the exception code (in case of an anonymous "raise") }
233              current_asmdata.CurrAsmList.concat(tai_marker.create(mark_NoLineInfoStart));
234              prev_except_loc:=current_except_loc;
235              location_reset_ref(current_except_loc,LOC_REFERENCE,OS_ADDR,4,[]);
236              tg.GetLocal(current_asmdata.CurrAsmList,sizeof(pint),java_jlthrowable,current_except_loc.reference);
237              thlcgjvm(hlcg).incstack(current_asmdata.CurrAsmList,1);
238              thlcgjvm(hlcg).a_load_stack_loc(current_asmdata.CurrAsmList,java_jlthrowable,current_except_loc);
239              current_asmdata.CurrAsmList.concat(tai_marker.create(mark_NoLineInfoEnd));
240 
241              { and generate the exception handling code }
242              secondpass(t1);
243 
244              { free the temp containing the exception and invalidate }
245              tg.UngetLocal(current_asmdata.CurrAsmList,current_except_loc.reference);
246              current_except_loc:=prev_except_loc;
247 
248              exceptflowcontrol:=flowcontrol;
249            end
250          else
251            exceptflowcontrol:=flowcontrol;
252          hlcg.a_label(current_asmdata.CurrAsmList,endexceptlabel);
253 
254          { restore all saved labels }
255          begintrylabel:=oldbegintrylabel;
256          endtrylabel:=oldendtrylabel;
257          endexceptlabel:=oldendexceptlabel;
258 
259          { return all used control flow statements }
260          flowcontrol:=oldflowcontrol+(exceptflowcontrol +
261            tryflowcontrol - [fc_inflowcontrol]);
262       end;
263 
264 
265     procedure tjvmtryexceptnode.adjust_estimated_stack_size;
266       begin
267         { do nothing }
268       end;
269 
270 
271     {*****************************************************************************
272                                    SecondOn
273     *****************************************************************************}
274 
275     procedure tjvmonnode.pass_generate_code;
276       var
277          thisonlabel : tasmlabel;
278          oldflowcontrol : tflowcontrol;
279          exceptvarsym : tlocalvarsym;
280          prev_except_loc : tlocation;
281       begin
282          location_reset(location,LOC_VOID,OS_NO);
283 
284          oldflowcontrol:=flowcontrol;
285          flowcontrol:=[fc_inflowcontrol];
286          current_asmdata.getjumplabel(thisonlabel);
287 
288          hlcg.a_label(current_asmdata.CurrAsmList,thisonlabel);
289 
290          if assigned(excepTSymtable) then
291            exceptvarsym:=tlocalvarsym(excepTSymtable.SymList[0])
292          else
293            internalerror(2011020402);
294 
295          { add exception catching information for the JVM: exception type
296            (will have to be adjusted if/when support for catching class
297             reference types is added), begin/end of code in which the exception
298             can be raised, and start of this exception handling code }
299          current_asmdata.CurrAsmList.concat(tai_jcatch.create(
300            tobjectdef(exceptvarsym.vardef).jvm_full_typename(true),
301            begintrylabel,endtrylabel,thisonlabel));
302 
303          { Retrieve exception variable }
304          { 1) prepare the location where we'll store it }
305          location_reset_ref(exceptvarsym.localloc,LOC_REFERENCE,OS_ADDR,sizeof(pint),[]);
306          tg.GetLocal(current_asmdata.CurrAsmList,sizeof(pint),exceptvarsym.vardef,exceptvarsym.localloc.reference);
307          prev_except_loc:=current_except_loc;
308          current_except_loc:=exceptvarsym.localloc;
309          { 2) the exception variable is at the top of the evaluation stack
310            (placed there by the JVM) -> adjust stack count, then store it }
311          thlcgjvm(hlcg).incstack(current_asmdata.CurrAsmList,1);
312          thlcgjvm(hlcg).a_load_stack_loc(current_asmdata.CurrAsmList,exceptvarsym.vardef,current_except_loc);
313 
314          if assigned(right) then
315            secondpass(right);
316 
317          { clear some stuff }
318          tg.UngetLocal(current_asmdata.CurrAsmList,exceptvarsym.localloc.reference);
319          exceptvarsym.localloc.loc:=LOC_INVALID;
320          current_except_loc:=prev_except_loc;
321          hlcg.a_jmp_always(current_asmdata.CurrAsmList,endexceptlabel);
322 
323          flowcontrol:=oldflowcontrol+(flowcontrol-[fc_inflowcontrol]);
324 
325          { next on node }
326          if assigned(left) then
327            secondpass(left);
328       end;
329 
330 {*****************************************************************************
331                              SecondTryFinally
332 *****************************************************************************}
333 
334     procedure tjvmtryfinallynode.pass_generate_code;
335       var
336          begintrylabel,
337          endtrylabel,
338          reraiselabel,
339          finallylabel,
340          finallyexceptlabel,
341          endfinallylabel,
342          exitfinallylabel,
343          continuefinallylabel,
344          breakfinallylabel,
345          oldCurrExitLabel,
346          oldContinueLabel,
347          oldBreakLabel : tasmlabel;
348          oldflowcontrol,tryflowcontrol : tflowcontrol;
349          finallycodecopy: tnode;
350          reasonbuf,
351          exceptreg: tregister;
352       begin
353          oldBreakLabel:=nil;
354          oldContinueLabel:=nil;
355          finallycodecopy:=nil;
356          continuefinallylabel:=nil;
357          breakfinallylabel:=nil;
358 
359          { not necessary on a garbage-collected platform }
360          if implicitframe then
361            internalerror(2011031803);
362          location_reset(location,LOC_VOID,OS_NO);
363 
364          { check if child nodes do a break/continue/exit }
365          oldflowcontrol:=flowcontrol;
366          flowcontrol:=[fc_inflowcontrol];
367          current_asmdata.getjumplabel(finallylabel);
368          current_asmdata.getjumplabel(endfinallylabel);
369          current_asmdata.getjumplabel(reraiselabel);
370 
371          { the finally block must catch break, continue and exit }
372          { statements                                            }
373          oldCurrExitLabel:=current_procinfo.CurrExitLabel;
374          current_asmdata.getjumplabel(exitfinallylabel);
375          current_procinfo.CurrExitLabel:=exitfinallylabel;
376          if assigned(current_procinfo.CurrBreakLabel) then
377           begin
378             oldContinueLabel:=current_procinfo.CurrContinueLabel;
379             oldBreakLabel:=current_procinfo.CurrBreakLabel;
380             current_asmdata.getjumplabel(breakfinallylabel);
381             current_asmdata.getjumplabel(continuefinallylabel);
382             current_procinfo.CurrContinueLabel:=continuefinallylabel;
383             current_procinfo.CurrBreakLabel:=breakfinallylabel;
384           end;
385 
386          { allocate reg to store the reason why the finally block was entered
387            (no exception, break, continue, exit), so we can continue to the
388            right label afterwards. In case of an exception, we use a separate
389            (duplicate) finally block because otherwise the JVM's bytecode
390            verification cannot statically prove that the exception reraise code
391            will only execute in case an exception actually happened }
392          reasonbuf:=hlcg.getaddressregister(current_asmdata.CurrAsmList,s32inttype);
393 
394          { try code }
395          begintrylabel:=nil;
396          endtrylabel:=nil;
397          if assigned(left) then
398            begin
399               current_asmdata.getaddrlabel(begintrylabel);
400               current_asmdata.getaddrlabel(endtrylabel);
401               hlcg.a_label(current_asmdata.CurrAsmList,begintrylabel);
402               secondpass(left);
403               hlcg.a_label(current_asmdata.CurrAsmList,endtrylabel);
404               tryflowcontrol:=flowcontrol;
405               if codegenerror then
406                 exit;
407               { reason: no exception occurred }
408               hlcg.a_load_const_reg(current_asmdata.CurrAsmList,s32inttype,0,reasonbuf);
409            end
410          else
411            tryflowcontrol:=[fc_inflowcontrol];
412 
413          { begin of the finally code }
414          hlcg.a_label(current_asmdata.CurrAsmList,finallylabel);
415          { finally code }
416          flowcontrol:=[fc_inflowcontrol];
417          { duplicate finally code for case when exception happened }
418          if assigned(begintrylabel) then
419            finallycodecopy:=right.getcopy;
420          secondpass(right);
421          { goto is allowed if it stays inside the finally block,
422            this is checked using the exception block number }
423          if (flowcontrol-[fc_gotolabel])<>[fc_inflowcontrol] then
424            CGMessage(cg_e_control_flow_outside_finally);
425          if codegenerror then
426            begin
427              if assigned(begintrylabel) then
428                finallycodecopy.free;
429              exit;
430            end;
431 
432          { don't generate line info for internal cleanup }
433          current_asmdata.CurrAsmList.concat(tai_marker.create(mark_NoLineInfoStart));
434 
435          { the reasonbuf holds the reason why this (non-exception) finally code
436            was executed:
437              0 = try code simply finished
438              1 = (unused) exception raised
439              2 = exit called
440              3 = break called
441              4 = continue called }
442          hlcg.a_cmp_const_reg_label(current_asmdata.CurrAsmList,s32inttype,OC_EQ,0,reasonbuf,endfinallylabel);
443          if fc_exit in tryflowcontrol then
444            if ([fc_break,fc_continue]*tryflowcontrol)<>[] then
445              hlcg.a_cmp_const_reg_label(current_asmdata.CurrAsmList,s32inttype,OC_EQ,2,reasonbuf,oldCurrExitLabel)
446            else
447              hlcg.a_jmp_always(current_asmdata.CurrAsmList,oldCurrExitLabel);
448          if fc_break in tryflowcontrol then
449            if fc_continue in tryflowcontrol then
450              hlcg.a_cmp_const_reg_label(current_asmdata.CurrAsmList,s32inttype,OC_EQ,3,reasonbuf,oldBreakLabel)
451            else
452              hlcg.a_jmp_always(current_asmdata.CurrAsmList,oldBreakLabel);
453          if fc_continue in tryflowcontrol then
454            hlcg.a_jmp_always(current_asmdata.CurrAsmList,oldContinueLabel);
455          { now generate the trampolines for exit/break/continue to load the reasonbuf }
456          if fc_exit in tryflowcontrol then
457            begin
458               hlcg.a_label(current_asmdata.CurrAsmList,exitfinallylabel);
459               hlcg.a_load_const_reg(current_asmdata.CurrAsmList,s32inttype,2,reasonbuf);
460               hlcg.a_jmp_always(current_asmdata.CurrAsmList,finallylabel);
461            end;
462          if fc_break in tryflowcontrol then
463           begin
464               hlcg.a_label(current_asmdata.CurrAsmList,breakfinallylabel);
465               hlcg.a_load_const_reg(current_asmdata.CurrAsmList,s32inttype,3,reasonbuf);
466               hlcg.a_jmp_always(current_asmdata.CurrAsmList,finallylabel);
467            end;
468          if fc_continue in tryflowcontrol then
469            begin
470               hlcg.a_label(current_asmdata.CurrAsmList,continuefinallylabel);
471               hlcg.a_load_const_reg(current_asmdata.CurrAsmList,s32inttype,4,reasonbuf);
472               hlcg.a_jmp_always(current_asmdata.CurrAsmList,finallylabel);
473            end;
474          { jump over finally-code-in-case-an-exception-happened }
475          hlcg.a_jmp_always(current_asmdata.CurrAsmList,endfinallylabel);
476 
477          { generate finally code in case an exception occurred }
478          if assigned(begintrylabel) then
479            begin
480              current_asmdata.getaddrlabel(finallyexceptlabel);
481              hlcg.a_label(current_asmdata.CurrAsmList,finallyexceptlabel);
482              { catch the exceptions }
483              current_asmdata.CurrAsmList.concat(tai_jcatch.create(
484                'all',begintrylabel,endtrylabel,finallyexceptlabel));
485              { store the generated exception object to a temp }
486              exceptreg:=hlcg.getaddressregister(current_asmdata.CurrAsmList,java_jlthrowable);
487              thlcgjvm(hlcg).incstack(current_asmdata.CurrAsmList,1);
488              thlcgjvm(hlcg).a_load_stack_reg(current_asmdata.CurrAsmList,java_jlthrowable,exceptreg);
489              { generate the finally code again }
490              secondpass(finallycodecopy);
491              finallycodecopy.free;
492              { reraise the exception }
493              thlcgjvm(hlcg).a_load_reg_stack(current_asmdata.CurrAsmList,java_jlthrowable,exceptreg);
494              current_asmdata.CurrAsmList.Concat(taicpu.op_none(a_athrow));
495              thlcgjvm(hlcg).decstack(current_asmdata.CurrAsmList,1);
496            end;
497          hlcg.a_label(current_asmdata.CurrAsmList,endfinallylabel);
498 
499          { end cleanup }
500          current_asmdata.CurrAsmList.concat(tai_marker.create(mark_NoLineInfoEnd));
501 
502          current_procinfo.CurrExitLabel:=oldCurrExitLabel;
503          if assigned(current_procinfo.CurrBreakLabel) then
504           begin
505             current_procinfo.CurrContinueLabel:=oldContinueLabel;
506             current_procinfo.CurrBreakLabel:=oldBreakLabel;
507           end;
508          flowcontrol:=oldflowcontrol+(tryflowcontrol-[fc_inflowcontrol]);
509       end;
510 
511 
512     procedure tjvmtryfinallynode.adjust_estimated_stack_size;
513       begin
514         { do nothing }
515       end;
516 
517 begin
518    cfornode:=tjvmfornode;
519    craisenode:=tjvmraisenode;
520    ctryexceptnode:=tjvmtryexceptnode;
521    ctryfinallynode:=tjvmtryfinallynode;
522    connode:=tjvmonnode;
523 end.
524 
525