fpc/compiler/jvm/njvmflw.pas
Jonas Maebe a25ebbba3e + added volatility information to all memory references
o separate information for reading and writing, because e.g. in a
     try-block, only the writes to local variables and parameters are
     volatile (they have to be committed immediately in case the next
     instruction causes an exception)
   o for now, only references to absolute memory addresses are marked
     as volatile
   o the volatily information is (should be) properly maintained throughout
     all code generators for all archictures with this patch
   o no optimizers or other compiler infrastructure uses the volatility
     information yet
   o this functionality is not (yet) exposed at the language level, it
     is only for internal code generator use right now

git-svn-id: trunk@34996 -
2016-11-27 18:17:37 +00:00

525 lines
20 KiB
ObjectPascal

{
Copyright (c) 1998-2011 by Florian Klaempfl and Jonas Maebe
Generate assembler for nodes that influence the flow for the JVM
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
****************************************************************************
}
unit njvmflw;
{$i fpcdefs.inc}
interface
uses
aasmbase,node,nflw,ncgflw;
type
tjvmfornode = class(tcgfornode)
function pass_1: tnode; override;
end;
tjvmraisenode = class(traisenode)
function pass_typecheck: tnode; override;
function pass_1: tnode; override;
procedure pass_generate_code;override;
end;
tjvmtryexceptnode = class(ttryexceptnode)
procedure pass_generate_code;override;
protected
procedure adjust_estimated_stack_size; override;
end;
tjvmtryfinallynode = class(ttryfinallynode)
procedure pass_generate_code;override;
protected
procedure adjust_estimated_stack_size; override;
end;
tjvmonnode = class(tonnode)
procedure pass_generate_code;override;
end;
implementation
uses
verbose,globals,systems,globtype,constexp,
symconst,symdef,symsym,aasmtai,aasmdata,aasmcpu,defutil,jvmdef,defcmp,
procinfo,cgbase,pass_1,pass_2,parabase,
cpubase,cpuinfo,
nbas,nld,ncon,ncnv,
tgobj,paramgr,
cgutils,hlcgobj,hlcgcpu
;
{*****************************************************************************
TFJVMFORNODE
*****************************************************************************}
function tjvmfornode.pass_1: tnode;
var
iteratortmp: ttempcreatenode;
olditerator: tnode;
block,
newbody: tblocknode;
stat,
newbodystat: tstatementnode;
begin
{ transform for-loops with enums to:
for tempint:=ord(lowval) to ord(upperval) do
begin
originalctr:=tenum(tempint);
<original loop body>
end;
enums are class instances in Java and hence can't be increased or so.
The type conversion consists of an array lookup in a final method,
so it shouldn't be too expensive.
}
if left.resultdef.typ=enumdef then
begin
block:=internalstatements(stat);
iteratortmp:=ctempcreatenode.create(s32inttype,left.resultdef.size,tt_persistent,true);
addstatement(stat,iteratortmp);
olditerator:=left;
left:=ctemprefnode.create(iteratortmp);
inserttypeconv_explicit(right,s32inttype);
inserttypeconv_explicit(t1,s32inttype);
newbody:=internalstatements(newbodystat);
addstatement(newbodystat,cassignmentnode.create(olditerator,
ctypeconvnode.create_explicit(ctemprefnode.create(iteratortmp),
olditerator.resultdef)));
addstatement(newbodystat,t2);
addstatement(stat,cfornode.create(left,right,t1,newbody,lnf_backward in loopflags));
addstatement(stat,ctempdeletenode.create(iteratortmp));
left:=nil;
right:=nil;
t1:=nil;
t2:=nil;
result:=block
end
else
result:=inherited pass_1;
end;
{*****************************************************************************
SecondRaise
*****************************************************************************}
var
current_except_loc: tlocation;
function tjvmraisenode.pass_typecheck: tnode;
begin
Result:=inherited pass_typecheck;
if codegenerror then
exit;
{ Java exceptions must descend from java.lang.Throwable }
if assigned(left) and
not def_is_related(left.resultdef,java_jlthrowable) then
MessagePos2(left.fileinfo,type_e_incompatible_types,left.resultdef.typename,'class(JLThrowable)');
{ Java exceptions cannot be raised "at" a specific location }
if assigned(right) then
MessagePos(right.fileinfo,parser_e_illegal_expression);
end;
function tjvmraisenode.pass_1: tnode;
begin
result:=nil;
expectloc:=LOC_VOID;
if assigned(left) then
firstpass(left);
end;
procedure tjvmraisenode.pass_generate_code;
begin
if assigned(left) then
begin
secondpass(left);
thlcgjvm(hlcg).a_load_loc_stack(current_asmdata.CurrAsmList,left.resultdef,left.location);
end
else
thlcgjvm(hlcg).a_load_loc_stack(current_asmdata.CurrAsmList,java_jlthrowable,current_except_loc);
current_asmdata.CurrAsmList.Concat(taicpu.op_none(a_athrow));
thlcgjvm(hlcg).decstack(current_asmdata.CurrAsmList,1);
end;
{*****************************************************************************
SecondTryExcept
*****************************************************************************}
var
begintrylabel,
endtrylabel: tasmlabel;
endexceptlabel : tasmlabel;
procedure tjvmtryexceptnode.pass_generate_code;
var
oldendexceptlabel,
oldbegintrylabel,
oldendtrylabel,
defaultcatchlabel: tasmlabel;
oldflowcontrol,tryflowcontrol,
exceptflowcontrol : tflowcontrol;
prev_except_loc: tlocation;
begin
location_reset(location,LOC_VOID,OS_NO);
oldflowcontrol:=flowcontrol;
flowcontrol:=[fc_inflowcontrol];
{ this can be called recursivly }
oldbegintrylabel:=begintrylabel;
oldendtrylabel:=endtrylabel;
oldendexceptlabel:=endexceptlabel;
{ get new labels for the control flow statements }
current_asmdata.getaddrlabel(begintrylabel);
current_asmdata.getaddrlabel(endtrylabel);
current_asmdata.getjumplabel(endexceptlabel);
{ try block }
{ set control flow labels for the try block }
hlcg.a_label(current_asmdata.CurrAsmList,begintrylabel);
secondpass(left);
hlcg.a_label(current_asmdata.CurrAsmList,endtrylabel);
tryflowcontrol:=flowcontrol;
{ jump over exception handling blocks }
current_asmdata.CurrAsmList.concat(tai_marker.create(mark_NoLineInfoStart));
hlcg.a_jmp_always(current_asmdata.CurrAsmList,endexceptlabel);
current_asmdata.CurrAsmList.concat(tai_marker.create(mark_NoLineInfoEnd));
{ set control flow labels for the except block }
{ and the on statements }
flowcontrol:=[fc_inflowcontrol];
{ on-statements }
if assigned(right) then
secondpass(right);
{ default handling except handling }
if assigned(t1) then
begin
current_asmdata.getaddrlabel(defaultcatchlabel);
current_asmdata.CurrAsmList.concat(tai_jcatch.create(
'all',begintrylabel,endtrylabel,defaultcatchlabel));
hlcg.a_label(current_asmdata.CurrAsmList,defaultcatchlabel);
{ here we don't have to reset flowcontrol }
{ the default and on flowcontrols are handled equal }
{ get the exception object from the stack and store it for use by
the exception code (in case of an anonymous "raise") }
current_asmdata.CurrAsmList.concat(tai_marker.create(mark_NoLineInfoStart));
prev_except_loc:=current_except_loc;
location_reset_ref(current_except_loc,LOC_REFERENCE,OS_ADDR,4,[]);
tg.GetLocal(current_asmdata.CurrAsmList,sizeof(pint),java_jlthrowable,current_except_loc.reference);
thlcgjvm(hlcg).incstack(current_asmdata.CurrAsmList,1);
thlcgjvm(hlcg).a_load_stack_loc(current_asmdata.CurrAsmList,java_jlthrowable,current_except_loc);
current_asmdata.CurrAsmList.concat(tai_marker.create(mark_NoLineInfoEnd));
{ and generate the exception handling code }
secondpass(t1);
{ free the temp containing the exception and invalidate }
tg.UngetLocal(current_asmdata.CurrAsmList,current_except_loc.reference);
current_except_loc:=prev_except_loc;
exceptflowcontrol:=flowcontrol;
end
else
exceptflowcontrol:=flowcontrol;
hlcg.a_label(current_asmdata.CurrAsmList,endexceptlabel);
{ restore all saved labels }
begintrylabel:=oldbegintrylabel;
endtrylabel:=oldendtrylabel;
endexceptlabel:=oldendexceptlabel;
{ return all used control flow statements }
flowcontrol:=oldflowcontrol+(exceptflowcontrol +
tryflowcontrol - [fc_inflowcontrol]);
end;
procedure tjvmtryexceptnode.adjust_estimated_stack_size;
begin
{ do nothing }
end;
{*****************************************************************************
SecondOn
*****************************************************************************}
procedure tjvmonnode.pass_generate_code;
var
thisonlabel : tasmlabel;
oldflowcontrol : tflowcontrol;
exceptvarsym : tlocalvarsym;
prev_except_loc : tlocation;
begin
location_reset(location,LOC_VOID,OS_NO);
oldflowcontrol:=flowcontrol;
flowcontrol:=[fc_inflowcontrol];
current_asmdata.getjumplabel(thisonlabel);
hlcg.a_label(current_asmdata.CurrAsmList,thisonlabel);
if assigned(excepTSymtable) then
exceptvarsym:=tlocalvarsym(excepTSymtable.SymList[0])
else
internalerror(2011020402);
{ add exception catching information for the JVM: exception type
(will have to be adjusted if/when support for catching class
reference types is added), begin/end of code in which the exception
can be raised, and start of this exception handling code }
current_asmdata.CurrAsmList.concat(tai_jcatch.create(
tobjectdef(exceptvarsym.vardef).jvm_full_typename(true),
begintrylabel,endtrylabel,thisonlabel));
{ Retrieve exception variable }
{ 1) prepare the location where we'll store it }
location_reset_ref(exceptvarsym.localloc,LOC_REFERENCE,OS_ADDR,sizeof(pint),[]);
tg.GetLocal(current_asmdata.CurrAsmList,sizeof(pint),exceptvarsym.vardef,exceptvarsym.localloc.reference);
prev_except_loc:=current_except_loc;
current_except_loc:=exceptvarsym.localloc;
{ 2) the exception variable is at the top of the evaluation stack
(placed there by the JVM) -> adjust stack count, then store it }
thlcgjvm(hlcg).incstack(current_asmdata.CurrAsmList,1);
thlcgjvm(hlcg).a_load_stack_loc(current_asmdata.CurrAsmList,exceptvarsym.vardef,current_except_loc);
if assigned(right) then
secondpass(right);
{ clear some stuff }
tg.UngetLocal(current_asmdata.CurrAsmList,exceptvarsym.localloc.reference);
exceptvarsym.localloc.loc:=LOC_INVALID;
current_except_loc:=prev_except_loc;
hlcg.a_jmp_always(current_asmdata.CurrAsmList,endexceptlabel);
flowcontrol:=oldflowcontrol+(flowcontrol-[fc_inflowcontrol]);
{ next on node }
if assigned(left) then
secondpass(left);
end;
{*****************************************************************************
SecondTryFinally
*****************************************************************************}
procedure tjvmtryfinallynode.pass_generate_code;
var
begintrylabel,
endtrylabel,
reraiselabel,
finallylabel,
finallyexceptlabel,
endfinallylabel,
exitfinallylabel,
continuefinallylabel,
breakfinallylabel,
oldCurrExitLabel,
oldContinueLabel,
oldBreakLabel : tasmlabel;
oldflowcontrol,tryflowcontrol : tflowcontrol;
finallycodecopy: tnode;
reasonbuf,
exceptreg: tregister;
begin
oldBreakLabel:=nil;
oldContinueLabel:=nil;
finallycodecopy:=nil;
continuefinallylabel:=nil;
breakfinallylabel:=nil;
{ not necessary on a garbage-collected platform }
if implicitframe then
internalerror(2011031803);
location_reset(location,LOC_VOID,OS_NO);
{ check if child nodes do a break/continue/exit }
oldflowcontrol:=flowcontrol;
flowcontrol:=[fc_inflowcontrol];
current_asmdata.getjumplabel(finallylabel);
current_asmdata.getjumplabel(endfinallylabel);
current_asmdata.getjumplabel(reraiselabel);
{ the finally block must catch break, continue and exit }
{ statements }
oldCurrExitLabel:=current_procinfo.CurrExitLabel;
current_asmdata.getjumplabel(exitfinallylabel);
current_procinfo.CurrExitLabel:=exitfinallylabel;
if assigned(current_procinfo.CurrBreakLabel) then
begin
oldContinueLabel:=current_procinfo.CurrContinueLabel;
oldBreakLabel:=current_procinfo.CurrBreakLabel;
current_asmdata.getjumplabel(breakfinallylabel);
current_asmdata.getjumplabel(continuefinallylabel);
current_procinfo.CurrContinueLabel:=continuefinallylabel;
current_procinfo.CurrBreakLabel:=breakfinallylabel;
end;
{ allocate reg to store the reason why the finally block was entered
(no exception, break, continue, exit), so we can continue to the
right label afterwards. In case of an exception, we use a separate
(duplicate) finally block because otherwise the JVM's bytecode
verification cannot statically prove that the exception reraise code
will only execute in case an exception actually happened }
reasonbuf:=hlcg.getaddressregister(current_asmdata.CurrAsmList,s32inttype);
{ try code }
begintrylabel:=nil;
endtrylabel:=nil;
if assigned(left) then
begin
current_asmdata.getaddrlabel(begintrylabel);
current_asmdata.getaddrlabel(endtrylabel);
hlcg.a_label(current_asmdata.CurrAsmList,begintrylabel);
secondpass(left);
hlcg.a_label(current_asmdata.CurrAsmList,endtrylabel);
tryflowcontrol:=flowcontrol;
if codegenerror then
exit;
{ reason: no exception occurred }
hlcg.a_load_const_reg(current_asmdata.CurrAsmList,s32inttype,0,reasonbuf);
end
else
tryflowcontrol:=[fc_inflowcontrol];
{ begin of the finally code }
hlcg.a_label(current_asmdata.CurrAsmList,finallylabel);
{ finally code }
flowcontrol:=[fc_inflowcontrol];
{ duplicate finally code for case when exception happened }
if assigned(begintrylabel) then
finallycodecopy:=right.getcopy;
secondpass(right);
{ goto is allowed if it stays inside the finally block,
this is checked using the exception block number }
if (flowcontrol-[fc_gotolabel])<>[fc_inflowcontrol] then
CGMessage(cg_e_control_flow_outside_finally);
if codegenerror then
begin
if assigned(begintrylabel) then
finallycodecopy.free;
exit;
end;
{ don't generate line info for internal cleanup }
current_asmdata.CurrAsmList.concat(tai_marker.create(mark_NoLineInfoStart));
{ the reasonbuf holds the reason why this (non-exception) finally code
was executed:
0 = try code simply finished
1 = (unused) exception raised
2 = exit called
3 = break called
4 = continue called }
hlcg.a_cmp_const_reg_label(current_asmdata.CurrAsmList,s32inttype,OC_EQ,0,reasonbuf,endfinallylabel);
if fc_exit in tryflowcontrol then
if ([fc_break,fc_continue]*tryflowcontrol)<>[] then
hlcg.a_cmp_const_reg_label(current_asmdata.CurrAsmList,s32inttype,OC_EQ,2,reasonbuf,oldCurrExitLabel)
else
hlcg.a_jmp_always(current_asmdata.CurrAsmList,oldCurrExitLabel);
if fc_break in tryflowcontrol then
if fc_continue in tryflowcontrol then
hlcg.a_cmp_const_reg_label(current_asmdata.CurrAsmList,s32inttype,OC_EQ,3,reasonbuf,oldBreakLabel)
else
hlcg.a_jmp_always(current_asmdata.CurrAsmList,oldBreakLabel);
if fc_continue in tryflowcontrol then
hlcg.a_jmp_always(current_asmdata.CurrAsmList,oldContinueLabel);
{ now generate the trampolines for exit/break/continue to load the reasonbuf }
if fc_exit in tryflowcontrol then
begin
hlcg.a_label(current_asmdata.CurrAsmList,exitfinallylabel);
hlcg.a_load_const_reg(current_asmdata.CurrAsmList,s32inttype,2,reasonbuf);
hlcg.a_jmp_always(current_asmdata.CurrAsmList,finallylabel);
end;
if fc_break in tryflowcontrol then
begin
hlcg.a_label(current_asmdata.CurrAsmList,breakfinallylabel);
hlcg.a_load_const_reg(current_asmdata.CurrAsmList,s32inttype,3,reasonbuf);
hlcg.a_jmp_always(current_asmdata.CurrAsmList,finallylabel);
end;
if fc_continue in tryflowcontrol then
begin
hlcg.a_label(current_asmdata.CurrAsmList,continuefinallylabel);
hlcg.a_load_const_reg(current_asmdata.CurrAsmList,s32inttype,4,reasonbuf);
hlcg.a_jmp_always(current_asmdata.CurrAsmList,finallylabel);
end;
{ jump over finally-code-in-case-an-exception-happened }
hlcg.a_jmp_always(current_asmdata.CurrAsmList,endfinallylabel);
{ generate finally code in case an exception occurred }
if assigned(begintrylabel) then
begin
current_asmdata.getaddrlabel(finallyexceptlabel);
hlcg.a_label(current_asmdata.CurrAsmList,finallyexceptlabel);
{ catch the exceptions }
current_asmdata.CurrAsmList.concat(tai_jcatch.create(
'all',begintrylabel,endtrylabel,finallyexceptlabel));
{ store the generated exception object to a temp }
exceptreg:=hlcg.getaddressregister(current_asmdata.CurrAsmList,java_jlthrowable);
thlcgjvm(hlcg).incstack(current_asmdata.CurrAsmList,1);
thlcgjvm(hlcg).a_load_stack_reg(current_asmdata.CurrAsmList,java_jlthrowable,exceptreg);
{ generate the finally code again }
secondpass(finallycodecopy);
finallycodecopy.free;
{ reraise the exception }
thlcgjvm(hlcg).a_load_reg_stack(current_asmdata.CurrAsmList,java_jlthrowable,exceptreg);
current_asmdata.CurrAsmList.Concat(taicpu.op_none(a_athrow));
thlcgjvm(hlcg).decstack(current_asmdata.CurrAsmList,1);
end;
hlcg.a_label(current_asmdata.CurrAsmList,endfinallylabel);
{ end cleanup }
current_asmdata.CurrAsmList.concat(tai_marker.create(mark_NoLineInfoEnd));
current_procinfo.CurrExitLabel:=oldCurrExitLabel;
if assigned(current_procinfo.CurrBreakLabel) then
begin
current_procinfo.CurrContinueLabel:=oldContinueLabel;
current_procinfo.CurrBreakLabel:=oldBreakLabel;
end;
flowcontrol:=oldflowcontrol+(tryflowcontrol-[fc_inflowcontrol]);
end;
procedure tjvmtryfinallynode.adjust_estimated_stack_size;
begin
{ do nothing }
end;
begin
cfornode:=tjvmfornode;
craisenode:=tjvmraisenode;
ctryexceptnode:=tjvmtryexceptnode;
ctryfinallynode:=tjvmtryfinallynode;
connode:=tjvmonnode;
end.