PageRenderTime 29ms CodeModel.GetById 16ms app.highlight 8ms RepoModel.GetById 1ms app.codeStats 0ms

/compiler/jvm/njvmflw.pas

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