''	FreeBASIC - 32-bit BASIC Compiler.
''	Copyright (C) 2004-2010 The FreeBASIC development team.
''
''	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., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA.


'' intermediate representation - three-address-codes module
''
'' chng: sep/2004 written [v1ctor]


#include once "inc\fb.bi"
#include once "inc\fbint.bi"
#include once "inc\reg.bi"
#include once "inc\emit.bi"
#include once "inc\flist.bi"
#include once "inc\ir.bi"
#include once "inc\hlp.bi"

type IRTAC_CTX
	tacTB			as TFLIST
	taccnt			as integer
	tacidx			as IRTAC ptr

	vregTB			as TFLIST

	tmpcnt		as uinteger
end type

declare sub hFlushUOP _
	( _
		byval op as integer, _
		byval v1 as IRVREG ptr, _
		byval vr as IRVREG ptr _
	)

declare sub hFlushBOP _
	( _
		byval op as integer, _
		byval v1 as IRVREG ptr, _
		byval v2 as IRVREG ptr, _
		byval vr as IRVREG ptr _
	)

declare sub hFlushCOMP _
	( _
		byval op as integer, _
		byval v1 as IRVREG ptr, _
		byval v2 as IRVREG ptr, _
		byval vr as IRVREG ptr, _
		byval label as FBSYMBOL ptr _
	)

declare sub hFlushSTORE _
	( _
		byval op as integer, _
		byval v1 as IRVREG ptr, _
		byval v2 as IRVREG ptr _
	)

declare sub hFlushLOAD _
	( _
		byval op as integer, _
		byval v1 as IRVREG ptr, _
		byval vr as IRVREG ptr _
	)

declare sub hFlushCONVERT _
	( _
		byval op as integer, _
		byval v1 as IRVREG ptr, _
		byval v2 as IRVREG ptr _
	)

declare sub hFlushCALL _
	( _
		byval op as integer, _
		byval proc as FBSYMBOL ptr, _
		byval bytestopop as integer, _
		byval v1 as IRVREG ptr, _
		byval vr as IRVREG ptr _
	)

declare sub hFlushBRANCH _
	( _
		byval op as integer, _
		byval label as FBSYMBOL ptr _
	)

declare sub hFlushSTACK _
	( _
		byval op as integer, _
		byval v1 as IRVREG ptr, _
		byval ex as integer _
	)

declare sub hFlushADDR _
	( _
		byval op as integer, _
		byval v1 as IRVREG ptr, _
		byval vr as IRVREG ptr _
	)

declare sub hFlushMEM _
	( _
		byval op as integer, _
		byval v1 as IRVREG ptr, _
		byval v2 as IRVREG ptr, _
		byval bytes as integer, _
		byval extra as any ptr _
	)

declare sub hFlushDBG _
	( _
		byval op as integer, _
		byval proc as FBSYMBOL ptr, _
		byval ex as integer _
	)

declare sub hFlushLIT _
	( _
		byval op as integer, _
		byval ex as any ptr _
	)

declare sub hFreeIDX _
	( _
		byval vreg as IRVREG ptr, _
		byval force as integer = FALSE _
	)

declare sub hFreeREG _
	( _
		byval vreg as IRVREG ptr, _
		byval force as integer = FALSE _
	)

declare sub hCreateTMPVAR _
	( _
		byval vreg as IRVREG ptr _
	)

declare sub hFreePreservedRegs _
	( _
 		_
	)

declare sub hDump _
	( _
		byval op as integer, _
		byval v1 as IRVREG ptr, _
		byval v2 as IRVREG ptr, _
		byval vr as IRVREG ptr _
	)

declare sub _flush _
	( _
	)

declare sub _emitStackAlign _
	( _
		byval bytes as integer _
	)


'' globals
	dim shared ctx as IRTAC_CTX

	dim shared regTB(0 to EMIT_REGCLASSES-1) as REGCLASS ptr

'':::::
private function _init _
	( _
		byval backend as FB_BACKEND _
	) as integer

	dim as integer i

	''
	ctx.tacidx = NULL
	ctx.taccnt = 0
	ctx.tmpcnt = 0

	flistNew( @ctx.tacTB, IR_INITADDRNODES, len( IRTAC ) )

	''
	flistNew( @ctx.vregTB, IR_INITVREGNODES, len( IRVREG ) )

	''
	emitInit( backend )

	for i = 0 to EMIT_REGCLASSES-1
		regTB(i) = emitGetRegClass( i )
	next

	''
	irSetOption( IR_OPT_NESTEDFIELDS )

	function = TRUE

end function

'':::::
private sub _end

	emitEnd( )

	''
	flistFree( @ctx.vregTB )

	''
	flistFree( @ctx.tacTB )

	ctx.tacidx = NULL
	ctx.taccnt = 0

end sub

'':::::
private function _emitBegin _
	( _
	) as integer

	function = emitOpen( )

end function

'':::::
private sub _emitEnd _
	( _
		byval tottime as double _
	)

	emitClose( tottime )

end sub

'':::::
private function _getOptionValue _
	( _
		byval opt as IR_OPTIONVALUE _
	) as integer

    function = emitGetOptionValue( opt )

end function


'':::::
private sub hLoadIDX _
	( _
		byval vreg as IRVREG ptr _
	)

    dim as IRVREG ptr vi = any

	if( vreg = NULL ) then
		exit sub
	end if

	select case vreg->typ
	case IR_VREGTYPE_IDX, IR_VREGTYPE_PTR
	case else
		exit sub
	end select

	'' any vreg attached?
	vi = vreg->vidx
	if( vi = NULL ) then
		exit sub
	end if

	'' don't load immediates to registers
	if( vi->typ = IR_VREGTYPE_IMM ) then
		exit sub
	end if

	regTB(FB_DATACLASS_INTEGER)->ensure( regTB(FB_DATACLASS_INTEGER), _
										 vi, _
										 symbGetDataSize( FB_DATATYPE_INTEGER ) )

end sub

'':::::
#macro hGetVREG( vreg, dt, dc, t )
	if( vreg <> NULL ) then
		t = vreg->typ

		dt = typeGet( vreg->dtype )
		if( dt = FB_DATATYPE_POINTER ) then
			dt = FB_DATATYPE_ULONG
		end if

		dc = symb_dtypeTB(dt).class

	else
		t  = INVALID
		dt = FB_DATATYPE_INVALID
		dc = INVALID
	end if
#endmacro

''::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

''::::
private sub hRelink _
	( _
		byval vreg as IRVREG ptr, _
		byval tvreg as IRTACVREG ptr _
	) static

	if( vreg->tacvhead = NULL ) then
		vreg->tacvhead = tvreg
	else
		vreg->tacvtail->next = tvreg
	end if

	vreg->tacvtail = tvreg

end sub

#macro hRelinkVreg(v,t)
    t->v.reg.pParent = NULL
    t->v.reg.next = NULL

    if( v <> NULL ) then
    	hRelink( v, @t->v.reg )
    	v->taclast = t

    	if( v->vidx <> NULL ) then
    		t->v.idx.vreg = v->vidx
    		t->v.idx.pParent = @v->vidx
    		t->v.idx.next = NULL
    		hRelink( v->vidx, @t->v.idx )
    		v->vidx->taclast = t
    	end if

    	if( v->vaux <> NULL ) then
    		t->v.aux.vreg = v->vaux
    		t->v.aux.pParent = @v->vaux
    		t->v.aux.next = NULL
    		hRelink( v->vaux, @t->v.aux )
    		v->vaux->taclast = t
    	end if
    end if
#endmacro

'':::::
private sub _emit _
	( _
		byval op as integer, _
		byval v1 as IRVREG ptr, _
		byval v2 as IRVREG ptr, _
		byval vr as IRVREG ptr, _
		byval ex1 as FBSYMBOL ptr = NULL, _
		byval ex2 as integer = 0 _
	) static

    dim as IRTAC ptr t

    t = flistNewItem( @ctx.tacTB )

    t->pos = ctx.taccnt

    t->op = op

    t->v1.reg.vreg = v1
    hRelinkVreg( v1, t )

    t->v2.reg.vreg = v2
    hRelinkVreg( v2, t )

    t->vr.reg.vreg = vr
    hRelinkVreg( vr, t )

    t->ex1 = ex1
    t->ex2 = ex2

    ctx.taccnt += 1

end sub

'':::::
private sub _procBegin _
	( _
		byval proc as FBSYMBOL ptr _
	) static

	emitProcBegin( proc )

end sub

'':::::
private sub _procEnd _
	( _
		byval proc as FBSYMBOL ptr _
	) static

	emitProcEnd( proc )

end sub

''::::
private function _procAllocArg _
	( _
		byval proc as FBSYMBOL ptr, _
		byval sym as FBSYMBOL ptr, _
		byval lgt as integer _
	) as integer

	function = emitProcAllocArg( proc, sym, lgt )

end function

'':::::
private function _procAllocLocal _
	( _
		byval proc as FBSYMBOL ptr, _
		byval sym as FBSYMBOL ptr, _
		byval lgt as integer _
	) as integer

	function = emitProcAllocLocal( proc, sym, lgt )

end function

'':::::
private function _procGetFrameRegName _
	( _
	) as zstring ptr

	function = emitProcGetFrameRegName( )

end function

'':::::
private sub _scopeBegin _
	( _
		byval s as FBSYMBOL ptr _
	) static

	emitScopeBegin( s )

end sub

'':::::
private sub _scopeEnd _
	( _
		byval s as FBSYMBOL ptr _
	) static

	emitScopeEnd( s )

end sub

'':::::
private function _procAllocStaticVars _
	( _
		byval head_sym as FBSYMBOL ptr _
	) as integer

	function = emitProcAllocStaticVars( head_sym )

end function

'':::::
private function _makeTmpStr _
	( _
		byval islabel as integer _
	) as zstring ptr

	static as zstring * 4 + 8 + 1 res

	if( islabel ) then
		res = ".Lt_" + *hHexUInt( ctx.tmpcnt )
	else
		res = "Lt_" + *hHexUInt( ctx.tmpcnt )
	end if

	ctx.tmpcnt += 1

	function = @res

end function

'':::::
private sub _emitLabel _
	( _
		byval label as FBSYMBOL ptr _
	) static

	_flush( )

	emitLABEL( label )

end sub

'':::::
private sub _emitReturn _
	( _
		byval bytestopop as integer _
	) static

	_flush( )

	emitRET( bytestopop )

end sub

'':::::
private sub _emitProcBegin _
	( _
		byval proc as FBSYMBOL ptr, _
		byval initlabel as FBSYMBOL ptr _
	) static

	dim as integer class_

	_flush( )

	'' clear regs so they aren't different from one proc to another
	for class_ = 0 to EMIT_REGCLASSES-1
		regTB(class_)->Clear( regTB(class_) )
	next

	emitProcHeader( proc, initlabel )

end sub

'':::::
private sub _emitProcEnd _
	( _
		byval proc as FBSYMBOL ptr, _
		byval initlabel as FBSYMBOL ptr, _
		byval exitlabel as FBSYMBOL ptr _
	) static

	dim as integer bytestopop

	_flush( )

	select case as const symbGetProcMode( proc )
	case FB_FUNCMODE_CDECL
		bytestopop = 0

	case FB_FUNCMODE_STDCALL, FB_FUNCMODE_STDCALL_MS
		if( env.target.allowstdcall ) then
			bytestopop = symbGetProcParamsLen( proc )
		else
			bytestopop = 0
		end if

	case else
		bytestopop = symbGetProcParamsLen( proc )
	end select

	emitProcFooter( proc, bytestopop, initlabel, exitlabel )

end sub

'':::::
private sub _emitScopeBegin _
	( _
		byval s as FBSYMBOL ptr _
	)

	_flush( )

end sub

'':::::
private sub _emitScopeEnd _
	( _
		byval s as FBSYMBOL ptr _
	)

	_flush( )

end sub

''::::
private sub _emitJmpTb _
	( _
		byval op as AST_JMPTB_OP, _
		byval dtype as integer, _
		byval label as FBSYMBOL ptr _
	) static

	_flush( )

	emitJMPTB( op, dtype, symbGetMangledName( label ) )

end sub

'':::::
private sub _emitInfoSection _
	( _
		byval liblist as TLIST ptr, _
		byval libpathlist as TLIST ptr _
	)

	emitWriteInfoSection( liblist, libpathlist )

end sub

'':::::
private sub _emitBop _
	( _
		byval op as integer, _
		byval v1 as IRVREG ptr, _
		byval v2 as IRVREG ptr, _
		byval vr as IRVREG ptr _
	)

	_emit( op, v1, v2, vr )

end sub

'':::::
private sub _emitBopEx _
	( _
		byval op as integer, _
		byval v1 as IRVREG ptr, _
		byval v2 as IRVREG ptr, _
		byval vr as IRVREG ptr, _
		byval ex as FBSYMBOL ptr _
	)

	_emit( op, v1, v2, vr, ex )

end sub

'':::::
private sub _emitUop _
	( _
		byval op as integer, _
		byval v1 as IRVREG ptr, _
		byval vr as IRVREG ptr _
	)

	_emit( op, v1, NULL, vr )

end sub

'':::::
private sub _emitConvert _
	( _
		byval dtype as integer, _
		byval subtype as FBSYMBOL ptr, _
		byval v1 as IRVREG ptr, _
		byval v2 as IRVREG ptr _
	) static

	select case symb_dtypeTB(typeGet( dtype )).class
	case FB_DATACLASS_INTEGER
		_emit( AST_OP_TOINT, v1, v2, NULL )
	case FB_DATACLASS_FPOINT
		_emit( AST_OP_TOFLT, v1, v2, NULL )
	end select

end sub

'':::::
private sub _emitStore _
	( _
		byval v1 as IRVREG ptr, _
		byval v2 as IRVREG ptr _
	)

	_emit( AST_OP_ASSIGN, v1, v2, NULL )

end sub

'':::::
private sub _emitSpillRegs _
	( _
	)

	_emit( AST_OP_SPILLREGS, NULL, NULL, NULL )

end sub

'':::::
private sub _emitLoad _
	( _
		byval v1 as IRVREG ptr _
	)

	_emit( AST_OP_LOAD, v1, NULL, NULL )

end sub

'':::::
private sub _emitLoadRes _
	( _
		byval v1 as IRVREG ptr, _
		byval vr as IRVREG ptr _
	)

	_emit( AST_OP_LOADRES, v1, NULL, vr )

end sub

'':::::
private sub _emitStack _
	( _
		byval op as integer, _
		byval v1 as IRVREG ptr _
	)

	_emit( op, v1, NULL, NULL )

end sub

'':::::
private sub _emitPushUDT _
	( _
		byval v1 as IRVREG ptr, _
		byval lgt as integer _
	)

	_emit( AST_OP_PUSHUDT, v1, NULL, NULL, NULL, lgt )

end sub

'':::::
private sub _emitPushArg _
	( _
		byval vr as IRVREG ptr, _
		byval plen as integer _
	) static

	if( plen = 0 ) then
		_emitStack( AST_OP_PUSH, vr )
	else
		_emitPushUDT( vr, plen )
	end if

end sub

'':::::
private sub _emitAddr _
	( _
		byval op as integer, _
		byval v1 as IRVREG ptr, _
		byval vr as IRVREG ptr _
	)

	_emit( op, v1, NULL, vr )

end sub

'':::::
private sub _emitLabelNF _
	( _
		byval l as FBSYMBOL ptr _
	)

	_emit( AST_OP_LABEL, NULL, NULL, NULL, l )

end sub

'':::::
private sub hEmitCallArgs _
	( _
		byval arg_list as IR_CALL_ARG_LIST ptr, _
		byval bytestopop as integer _
	)


        if( bytestopop >= &h456700 ) then
            _emitStackAlign( bytestopop )
        end if

	if( arg_list = NULL ) then
		return
	end if

	var arg = arg_list->head

	do while( arg )
        var nxt = arg->next

		irEmitPUSHARG( arg->vr, arg->lgt )

		irDelCallArg( arg_list, arg )

		arg = nxt
	loop


end sub

'':::::
private sub _emitCall _
	( _
		byval proc as FBSYMBOL ptr, _
		byval arg_list as IR_CALL_ARG_LIST ptr, _
		byval bytestopop as integer, _
		byval vr as IRVREG ptr _
	)

	if( symbGetProcMode( proc ) = FB_FUNCMODE_CDECL ) then
		'' if this func is VARARG, astCALL() already set the size
		if( bytestopop = 0 ) then
			bytestopop = symbGetProcParamsLen( proc )
		end if
		if( env.target.align16 ) then
			bytestopop += &h456700
		end if
	end if

	hEmitCallArgs( arg_list, bytestopop )

	_emit( AST_OP_CALLFUNCT, NULL, NULL, vr, proc, bytestopop )

end sub

'':::::
private sub _emitCallPtr _
	( _
		byval v1 as IRVREG ptr, _
		byval arg_list as IR_CALL_ARG_LIST ptr, _
		byval vr as IRVREG ptr, _
		byval bytestopop as integer _
	)

	hEmitCallArgs( arg_list, bytestopop )

	_emit( AST_OP_CALLPTR, v1, NULL, vr, NULL, bytestopop )

end sub

'':::::
private sub _emitStackAlign _
	( _
		byval bytes as integer _
	)

	_emit( AST_OP_STACKALIGN, NULL, NULL, NULL, NULL, bytes )

end sub

'':::::
private sub _emitJumpPtr _
	( _
		byval v1 as IRVREG ptr _
	)

	_emit( AST_OP_JUMPPTR, v1, NULL, NULL, NULL )

end sub

'':::::
private sub _emitBranch _
	( _
		byval op as integer, _
		byval label as FBSYMBOL ptr _
	)

	_emit( op, NULL, NULL, NULL, label )

end sub

'':::::
private sub _emitMem _
	( _
		byval op as integer, _
		byval v1 as IRVREG ptr, _
		byval v2 as IRVREG ptr, _
		byval bytes as integer _
	)

	_emit( op, v1, v2, NULL, 0, bytes )

end sub

'':::::
private sub _emitDBG _
	( _
		byval op as integer, _
		byval proc as FBSYMBOL ptr, _
		byval ex as integer _
	)

	_emit( op, NULL, NULL, NULL, proc, ex )

end sub

'':::::
private sub _emitComment _
	( _
		byval text as zstring ptr _
	) static

	_emit( AST_OP_LIT_COMMENT, NULL, NULL, NULL, cast( any ptr, ZstrDup( text ) ) )

end sub

'':::::
private sub _emitASM _
	( _
		byval text as zstring ptr _
	) static

	_emit( AST_OP_LIT_ASM, NULL, NULL, NULL, cast( any ptr, ZstrDup( text ) ) )

end sub

'':::::
private sub _emitVarIniBegin _
	( _
		byval sym as FBSYMBOL ptr _
	) static

	'' no flush, all var-ini go to data sections

	emitVARINIBEGIN( sym )

end sub

'':::::
private sub _emitVarIniEnd _
	( _
		byval sym as FBSYMBOL ptr _
	) static

	emitVARINIEND( sym )

end sub

'':::::
private sub _emitVarIniI _
	( _
		byval dtype as integer, _
		byval value as integer _
	) static

	emitVARINIi( dtype, value )

end sub

'':::::
private sub _emitVarIniF _
	( _
		byval dtype as integer, _
		byval value as double _
	) static

	emitVARINIf( dtype, value )

end sub

'':::::
private sub _emitVarIniI64 _
	( _
		byval dtype as integer, _
		byval value as longint _
	) static

	emitVARINI64( dtype, value )

end sub

'':::::
private sub _emitVarIniOfs _
	( _
		byval sym as FBSYMBOL ptr, _
		byval ofs as integer _
	) static

	emitVARINIOFS( symbGetMangledName( sym ), ofs )

end sub

'':::::
private sub _emitVarIniStr _
	( _
		byval totlgt as integer, _
		byval litstr as zstring ptr, _
		byval litlgt as integer _
	) static

	dim as zstring ptr s

	'' zstring * 1?
	if( totlgt = 0 ) then
		emitVARINIi( FB_DATATYPE_BYTE, 0 )
		exit sub
	end if

	''
	if( litlgt > totlgt ) then
		errReportWarn( FB_WARNINGMSG_LITSTRINGTOOBIG )
		'' !!!FIXME!!! truncate will fail if it lies on an escape seq
		s = hEscape( left( *litstr, totlgt ) )
	else
		s = hEscape( litstr )
	end if

	''
	emitVARINISTR( s )

	if( litlgt < totlgt ) then
		emitVARINIPAD( totlgt - litlgt )
	end if

end sub

'':::::
private sub _emitVarIniWstr _
	( _
		byval totlgt as integer, _
		byval litstr as wstring ptr, _
		byval litlgt as integer _
	) static

	dim as zstring ptr s
	dim as integer wclen

	'' wstring * 1?
	if( totlgt = 0 ) then
		emitVARINIi( env.target.wchar.type, 0 )
		exit sub
	end if

	''
	if( litlgt > totlgt ) then
		errReportWarn( FB_WARNINGMSG_LITSTRINGTOOBIG )
		'' !!!FIXME!!! truncate will fail if it lies on an escape seq
		s = hEscapeW( left( *litstr, totlgt ) )
	else
		s = hEscapeW( litstr )
	end if

	''
	wclen = symbGetDataSize( FB_DATATYPE_WCHAR )

	emitVARINIWSTR( s )

	if( litlgt < totlgt ) then
		emitVARINIPAD( (totlgt - litlgt) * wclen )
	end if

end sub

'':::::
private sub _emitVarIniPad _
	( _
		byval bytes as integer _
	) static

	emitVARINIPAD( bytes )

end sub

'':::::
private sub _emitVarIniScopeBegin _
	( _
		byval basesym as FBSYMBOL ptr, _
		byval sym as FBSYMBOL ptr _
	) static

	emitVARINISCOPEINI( )

end sub

'':::::
private sub _emitVarIniScopeEnd _
	( _
		byval basesym as FBSYMBOL ptr, _
		byval sym as FBSYMBOL ptr _
	) static

	emitVARINISCOPEEND( )

end sub

'':::::
private sub _emitVarIniSeparator _
	( _
		byval basesym as FBSYMBOL ptr, _
		byval sym as FBSYMBOL ptr _
	) static

	emitVARINISEPARATOR( )

end sub

'':::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

'':::::
private function hNewVR _
	( _
		byval dtype as integer, _
		byval subtype as FBSYMBOL ptr, _
		byval vtype as integer _
	) as IRVREG ptr

	dim as IRVREG ptr v = any

	v = flistNewItem( @ctx.vregTB )

	v->typ = vtype
	v->dtype = typeGet( dtype )
	v->subtype = subtype
	v->sym = NULL
	v->reg = INVALID
	v->vidx	= NULL
	v->vaux	= NULL
	v->ofs = 0
	v->vector = 0

	if( env.clopt.fputype = FB_FPUTYPE_FPU ) then
		v->regFamily = IR_REG_FPU_STACK
	else
		v->regFamily = IR_REG_SSE
	end if

	v->tacvhead = NULL
	v->tacvtail = NULL
	v->taclast = NULL

	function = v

end function

'':::::
private function _allocVreg _
	( _
		byval dtype as integer, _
		byval subtype as FBSYMBOL ptr _
	) as IRVREG ptr

	dim as IRVREG ptr vr = any

	vr = hNewVR( dtype, subtype, IR_VREGTYPE_REG )

	'' longint?
	if( ISLONGINT( dtype ) ) then
		 vr->vaux = hNewVR( FB_DATATYPE_INTEGER, NULL, IR_VREGTYPE_REG )
	end if

	function = vr

end function

'':::::
private function _allocVrImm _
	( _
		byval dtype as integer, _
		byval subtype as FBSYMBOL ptr, _
		byval value as integer _
	) as IRVREG ptr

	dim as IRVREG ptr vr = any

	vr = hNewVR( dtype, subtype, IR_VREGTYPE_IMM )

	vr->value.int = value

	'' longint?
	if( ISLONGINT( dtype ) ) then
		 vr->vaux = hNewVR( FB_DATATYPE_INTEGER, NULL, IR_VREGTYPE_IMM )
		 vr->vaux->value.int = 0
	end if

	function = vr

end function

'':::::
private function _allocVrImm64 _
	( _
		byval dtype as integer, _
		byval subtype as FBSYMBOL ptr, _
		byval value as longint _
	) as IRVREG ptr

	dim as IRVREG ptr vr = any

	vr = hNewVR( dtype, subtype, IR_VREGTYPE_IMM )

	vr->value.int = cuint( value )

	'' aux
	vr->vaux = hNewVR( FB_DATATYPE_INTEGER, NULL, IR_VREGTYPE_IMM )

	vr->vaux->value.int = cint( value shr 32 )

	function = vr

end function

'':::::
private function _allocVrImmF _
	( _
		byval dtype as integer, _
		byval subtype as FBSYMBOL ptr, _
		byval value as double _
	) as IRVREG ptr

	dim as IRVREG ptr vr = any

	'' the FPU doesn't support immediates? create a temp const var_..
	if( irGetOption( IR_OPT_FPU_IMMOPER ) = FALSE ) then
		dim as FBSYMBOL ptr s = symbAllocFloatConst( value, dtype )
		return irAllocVRVAR( dtype, subtype, s, symbGetOfs( s ) )
	end if

	vr = hNewVR( dtype, subtype, IR_VREGTYPE_IMM )

	vr->value.float = value

	function = vr

end function

'':::::
private function _allocVrVar _
	( _
		byval dtype as integer, _
		byval subtype as FBSYMBOL ptr, _
		byval symbol as FBSYMBOL ptr, _
		byval ofs as integer _
	) as IRVREG ptr

	dim as IRVREG ptr vr = any, va = any

	vr = hNewVR( dtype, subtype, IR_VREGTYPE_VAR )

	vr->sym = symbol
	vr->ofs = ofs

	'' longint?
	if( ISLONGINT( dtype ) ) then
		va = hNewVR( FB_DATATYPE_INTEGER, NULL, IR_VREGTYPE_VAR )
		vr->vaux = va
		va->ofs = ofs + FB_INTEGERSIZE
	end if

	function = vr

end function

'':::::
private function _allocVrIdx _
	( _
		byval dtype as integer, _
		byval subtype as FBSYMBOL ptr, _
		byval symbol as FBSYMBOL ptr, _
		byval ofs as integer, _
		byval mult as integer, _
		byval vidx as IRVREG ptr _
	) as IRVREG ptr

	dim as IRVREG ptr vr = any, va = any

	vr = hNewVR( dtype, subtype, IR_VREGTYPE_IDX )

	vr->sym = symbol
	vr->ofs = ofs
	vr->mult = mult
	vr->vidx = vidx

	'' longint?
	if( ISLONGINT( dtype ) ) then
		va = hNewVR( FB_DATATYPE_INTEGER, NULL, IR_VREGTYPE_IDX )
		vr->vaux= va
		va->ofs = ofs + FB_INTEGERSIZE
	end if

	function = vr

end function

'':::::
private function _allocVrPtr _
	( _
		byval dtype as integer, _
		byval subtype as FBSYMBOL ptr, _
		byval ofs as integer, _
		byval vidx as IRVREG ptr _
	) as IRVREG ptr

	dim as IRVREG ptr vr = any, va = any

	vr = hNewVR( dtype, subtype, IR_VREGTYPE_PTR )

	vr->ofs = ofs
	vr->mult = 1
	vr->vidx = vidx

	'' longint?
	if( ISLONGINT( dtype ) ) then
		va = hNewVR( FB_DATATYPE_INTEGER, NULL, IR_VREGTYPE_IDX )
		vr->vaux= va
		va->ofs = ofs + FB_INTEGERSIZE
	end if

	function = vr

end function

'':::::
private function _allocVrOfs _
	( _
		byval dtype as integer, _
		byval subtype as FBSYMBOL ptr, _
		byval symbol as FBSYMBOL ptr, _
		byval ofs as integer _
	) as IRVREG ptr

	dim as IRVREG ptr vr = any

	vr = hNewVR( dtype, subtype, IR_VREGTYPE_OFS )

	vr->sym = symbol
	vr->ofs = ofs

	function = vr

end function

'':::::
private sub _setVregDataType _
	( _
		byval vreg as IRVREG ptr, _
		byval dtype as integer, _
		byval subtype as FBSYMBOL ptr _
	)

	if( vreg <> NULL ) then
		vreg->dtype = typeGet( dtype )
		vreg->subtype = subtype
	end if

end sub

''::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

'':::::
private sub hRename _
	( _
		byval vold as IRVREG ptr, _
		byval vnew as IRVREG ptr _
	)

    dim as IRTACVREG ptr t = any
    dim as IRVREG ptr v = any

	'' reassign tac table vregs
	'' (assuming res, v1 and v2 will never point to the same vreg!)
	t = vold->tacvhead
	do
		'' if it's an index or auxiliary vreg, update parent
		if( t->pParent <> NULL ) then
			*t->pParent = vnew
		end if
		t->vreg = vnew
		t = t->next
	loop while( t <> NULL )

	vnew->tacvhead = vold->tacvhead
	vnew->tacvtail = vold->tacvtail
	vnew->taclast = vold->taclast

end sub

'':::::
private sub hReuse _
	( _
		byval t as IRTAC ptr _
	)

    dim as IRVREG ptr v1 = any, v2 = any, vr = any
    dim as integer v1_dtype = any, v1_dclass = any, v1_typ = any
    dim as integer v2_dtype = any, v2_dclass = any, v2_typ = any
    dim as integer vr_dtype = any, vr_dclass = any, vr_typ = any
    dim as integer op = any

	op = t->op
	v1 = t->v1.reg.vreg
	v2 = t->v2.reg.vreg
	vr = t->vr.reg.vreg

	hGetVREG( v1, v1_dtype, v1_dclass, v1_typ )
	hGetVREG( v2, v2_dtype, v2_dclass, v2_typ )
    hGetVREG( vr, vr_dtype, vr_dclass, vr_typ )

	select case astGetOpClass( op )
	case AST_NODECLASS_UOP
		if( vr <> v1 ) then
			if( vr_dtype = v1_dtype ) then
           		if( irGetDistance( v1 ) = IR_MAXDIST ) then
           			hRename( vr, v1 )
           		end if
           	end if
		end if

	case AST_NODECLASS_BOP, AST_NODECLASS_COMP
		dim as integer v1rename = any, v2rename = any

		if( vr = NULL ) then
			exit sub
		end if

		'' check if operands have the same class (can happen 'cause the x86 FPU hacks)
		if( v1_dclass <> v2_dclass ) then
			exit sub
		end if

		v1rename = FALSE
		if( vr <> v1 ) then
			if( vr_dtype = v1_dtype ) then
           		if( irGetDistance( v1 ) = IR_MAXDIST ) then
           			v1rename = TRUE
           		end if
           	end if
		end if

		v2rename = FALSE
		if( astGetOpIsCommutative( op ) ) then
			if( vr <> v2 ) then
				if( vr_dtype = v2_dtype ) then
					if( v2_typ <> IR_VREGTYPE_IMM ) then
           				if( irGetDistance( v2 ) = IR_MAXDIST ) then
           					v2rename = TRUE
           				end if
           			end if
           		end if
			end if
		end if

		if( v1rename and v2rename ) then
			if( irIsREG( v1 ) = FALSE ) then
           		v1rename = FALSE
			end if
		end if

		if( v1rename ) then
           	hRename( vr, v1 )

		elseif( v2rename ) then
		 	swap t->v1, t->v2

			hRename( vr, v2 )
		end if

	end select

end sub

'':::::
private sub _flush static
    dim as integer op
    dim as IRTAC ptr t
    dim as IRVREG ptr v1, v2, vr

	if( ctx.taccnt = 0 ) then
		exit sub
	end if

	t = flistGetHead( @ctx.tacTB )
	do
		ctx.tacidx = t

		hReuse( t )

		op = t->op
		v1 = t->v1.reg.vreg
		v2 = t->v2.reg.vreg
		vr = t->vr.reg.vreg

		''
		'hDump( op, v1, v2, vr )

        ''
		select case as const astGetOpClass( op )
		case AST_NODECLASS_UOP
			hFlushUOP( op, v1, vr )

		case AST_NODECLASS_BOP
			hFlushBOP( op, v1, v2, vr )

		case AST_NODECLASS_COMP
			hFlushCOMP( op, v1, v2, vr, t->ex1 )

		case AST_NODECLASS_ASSIGN
			hFlushSTORE( op, v1, v2 )

		case AST_NODECLASS_LOAD
			hFlushLOAD( op, v1, vr )

		case AST_NODECLASS_CONV
			hFlushCONVERT( op, v1, v2 )

		case AST_NODECLASS_STACK
			hFlushSTACK( op, v1, t->ex2 )

		case AST_NODECLASS_CALL
			hFlushCALL( op, t->ex1, t->ex2, v1, vr )

		case AST_NODECLASS_BRANCH
			hFlushBRANCH( op, t->ex1 )

		case AST_NODECLASS_ADDROF
			hFlushADDR( op, v1, vr )

		case AST_NODECLASS_MEM
			hFlushMEM( op, v1, v2, t->ex2, t->ex1 )

		case AST_NODECLASS_DBG
			hFlushDBG( op, t->ex1, t->ex2 )

		case AST_NODECLASS_LIT
			hFlushLIT( op, cast( any ptr, t->ex1 ) )

		end select

		if( env.clopt.fputype >= FB_FPUTYPE_SSE ) then
			'' after vr has been used for the first time, force reg family to be SSE
			if( astGetOpClass( op ) <> AST_NODECLASS_CALL ) then
				if( vr ) then
					if( vr->regFamily = IR_REG_FPU_STACK ) then vr->regFamily = IR_REG_SSE
				end if
			end if
		end if

		t = flistGetNext( t )
	loop while( t <> NULL )

	''
	ctx.tacidx = NULL
	ctx.taccnt = 0
	flistReset( @ctx.tacTB )

	''
	flistReset( @ctx.vregTB )

    ''
    hFreePreservedRegs( )

end sub

'':::::
private sub hFlushBRANCH _
	( _
		byval op as integer, _
		byval label as FBSYMBOL ptr _
	) static

	''
	select case as const op
	case AST_OP_LABEL
		emitLABEL( label )

	case AST_OP_JMP
		emitJUMP( label )

	case AST_OP_CALL
		emitCALL( label, 0 )

	case AST_OP_RET
		emitRET( 0 )

	case else
		emitBRANCH( op, label )
	end select

end sub

'':::::
private sub hFreePreservedRegs( ) static
    dim as integer class_, reg

	'' for each reg class
	for class_ = 0 to EMIT_REGCLASSES-1

		'' for each register on that class
		reg = regTB(class_)->getFirst( regTB(class_) )
		do until( reg = INVALID )
			'' if not free
			if( regTB(class_)->isFree( regTB(class_), reg ) = FALSE ) then

        		assert( emitIsRegPreserved( class_, reg ) )

        		'' free reg
        		regTB(class_)->free( regTB(class_), reg )

			end if

        	'' next reg
        	reg = regTB(class_)->getNext( regTB(class_), reg )
		loop

	next

end sub

'':::::
private sub hPreserveRegs _
	( _
		byval ptrvreg as IRVREG ptr = NULL _
	) static

    dim as integer class_

	'' for each reg class
	for class_ = 0 to EMIT_REGCLASSES-1

    	'' set the register that shouldn't be preserved (used for CALLPTR only)
    	dim as integer npreg

    	npreg = INVALID
    	if( class_ = FB_DATACLASS_INTEGER ) then
    		if( ptrvreg <> NULL ) then

    			select case ptrvreg->typ
    			case IR_VREGTYPE_REG
    				npreg = ptrvreg->reg

    			case IR_VREGTYPE_IDX, IR_VREGTYPE_PTR
    				ptrvreg = ptrvreg->vidx
    				if( ptrvreg <> NULL ) then
    					npreg = ptrvreg->reg
    				end if
    			end select

    			ptrvreg = NULL
    		end if
    	end if

		'' for each register on that class
    	dim as integer reg

		reg = regTB(class_)->getFirst( regTB(class_) )
		do until( reg = INVALID )
			'' if not free
			if( (regTB(class_)->isFree( regTB(class_), reg ) = FALSE) and _
				(reg <> npreg) ) then

    			dim as IRVREG ptr vr
    			dim as integer vr_dclass, vr_dtype, vr_typ

				'' get the attached vreg
				vr = regTB(class_)->getVreg( regTB(class_), reg )
                assert( vr <> NULL )

                hGetVREG( vr, vr_dtype, vr_dclass, vr_typ )

        		'' if reg is not preserved between calls
        		if( emitIsRegPreserved( vr_dclass, reg ) = FALSE ) then
    				dim as integer freg

        			'' find a preserved reg to copy to
        			freg = emitGetFreePreservedReg( vr_dclass, vr_dtype )

        			'' if none free, spill reg
        			if( freg = INVALID ) then
        				irStoreVR( vr, reg )

        			'' else, copy it to a preserved reg
        			else
    					dim as IRVREG tr

        				tr = *vr
        				vr->reg = regTB(vr_dclass)->allocateReg( regTB(vr_dclass), freg, vr )
        				emitMOV( vr, @tr )
        			end if

        			'' free reg
        			regTB(vr_dclass)->free( regTB(vr_dclass), reg )
        		end if
        	end if

        	'' next reg
        	reg = regTB(class_)->getNext( regTB(class_), reg )
		loop

	next

end sub

'':::::
private sub hFlushCALL _
	( _
		byval op as integer, _
		byval proc as FBSYMBOL ptr, _
		byval bytestopop as integer, _
		byval v1 as IRVREG ptr, _
		byval vr as IRVREG ptr _
	) static

	dim as integer vr_dclass, vr_dtype, vr_typ, vr_reg, vr_reg2
	dim as IRVREG ptr va

	'' function?
	if( proc <> NULL ) then
		'' pop up the stack if needed
		select case symbGetProcMode( proc )
		case FB_FUNCMODE_CDECL
			'' nothing to do, bytestopop calculated in _emitCall

		case FB_FUNCMODE_STDCALL, FB_FUNCMODE_STDCALL_MS
			'' nothing to pop, unless -nostdcall was used
			if( env.target.allowstdcall = FALSE ) then
				if( bytestopop = 0 ) then
					bytestopop = symbGetProcParamsLen( proc )
				end if
			end if

		'' pascal etc.. nothing to pop
		case else
			bytestopop = 0
		end select

		'' save used registers and free the FPU stack
		hPreserveRegs( )

		emitCALL( proc, bytestopop )

	'' call or jump to pointer..
	else

		'' if it's a CALL, save used registers and free the FPU stack
		if( op = AST_OP_CALLPTR ) then
			hPreserveRegs( v1 )
		end if

		'' load pointer
		hGetVREG( v1, vr_dtype, vr_dclass, vr_typ )
		hLoadIDX( v1 )
		if( vr_typ = IR_VREGTYPE_REG ) then
			regTB(vr_dclass)->ensure( regTB(vr_dclass), _
			                          v1, _
			                          symbGetDataSize( vr_dtype ) )
		end if

		'' CALLPTR
		if( op = AST_OP_CALLPTR ) then
			emitCALLPTR( v1, bytestopop )
		'' JUMPPTR
		else
			emitJUMPPTR( v1 )
		end if

		'' free pointer
		hFreeREG( v1 )
	end if

	'' load result
	if( vr <> NULL ) then
		hGetVREG( vr, vr_dtype, vr_dclass, vr_typ )

		emitGetResultReg( vr_dtype, vr_dclass, vr_reg, vr_reg2 )

		'' longints..
		if( ISLONGINT( vr_dtype ) ) then
			va = vr->vaux
			va->reg = regTB(vr_dclass)->allocateReg( regTB(vr_dclass), vr_reg2, va )
			va->typ = IR_VREGTYPE_REG
		end if

		vr->reg = regTB(vr_dclass)->allocateReg( regTB(vr_dclass), vr_reg, vr )
		vr->typ = IR_VREGTYPE_REG

		'' fb allows function calls w/o saving the result
		hFreeREG( vr )
	end if

end sub

'':::::
private sub hFlushSTACK _
	( _
		byval op as integer, _
		byval v1 as IRVREG ptr, _
		byval ex as integer _
	) static

	dim as integer v1_typ, v1_dtype, v1_dclass
	dim as IRVREG ptr va

	''
	if( op = AST_OP_STACKALIGN ) then
		emitSTACKALIGN( ex )
		exit sub
	end if

	''
	hGetVREG( v1, v1_dtype, v1_dclass, v1_typ )

	hLoadIDX( v1 )

	'' load only if it's a reg (x86 assumption)
	if( v1_typ = IR_VREGTYPE_REG ) then
		'' handle longint
		if( ISLONGINT( v1_dtype ) ) then
			va = v1->vaux
			regTB(v1_dclass)->ensure( regTB(v1_dclass), _
									  va, _
									  symbGetDataSize( FB_DATATYPE_INTEGER ), _
									  FALSE )

			v1_dtype = FB_DATATYPE_INTEGER
		end if

		regTB(v1_dclass)->ensure( regTB(v1_dclass), _
								  v1, _
								  symbGetDataSize( v1_dtype ) )
	end if

	''
	select case op
	case AST_OP_PUSH
		emitPUSH( v1 )
	case AST_OP_PUSHUDT
		emitPUSHUDT( v1, ex )
	case AST_OP_POP
		emitPOP( v1 )
	end select

    ''
	hFreeREG( v1 )

end sub

'':::::
private sub hFlushUOP _
	( _
		byval op as integer, _
		byval v1 as IRVREG ptr, _
		byval vr as IRVREG ptr _
	) static

	dim as integer v1_typ, v1_dtype, v1_dclass
	dim as integer vr_typ, vr_dtype, vr_dclass
	dim as IRVREG ptr va

	dim as integer v1vector
	v1vector = v1->vector

	''
	hGetVREG( v1, v1_dtype, v1_dclass, v1_typ )
	hGetVREG( vr, vr_dtype, vr_dclass, vr_typ )

	hLoadIDX( v1 )
	hLoadIDX( vr )

    ''
    if ( vr <> NULL ) then
		if( v1 <> vr ) then
			'' handle longint
			if( ISLONGINT( vr_dtype ) ) then
				va = vr->vaux
				regTB(vr_dclass)->ensure( regTB(vr_dclass), _
										  va, _
										  symbGetDataSize( FB_DATATYPE_INTEGER ), _
										  FALSE )

				vr_dtype = FB_DATATYPE_INTEGER
			end if

			regTB(vr_dclass)->ensure( regTB(vr_dclass), _
									  vr, _
									  symbGetDataSize( vr_dtype ) )
		end if
	end if

	'' UOP to self? x86 assumption at AST
	if( vr <> NULL ) then
		'' handle longint
		if( ISLONGINT( v1_dtype ) ) then
			va = v1->vaux
			regTB(v1_dclass)->ensure( regTB(v1_dclass), _
									  va, _
									  symbGetDataSize( FB_DATATYPE_INTEGER ), _
									  FALSE )

			v1_dtype = FB_DATATYPE_INTEGER
		end if

		if( op = AST_OP_SWZ_REPEAT ) then
			'' v1 must be loaded as a scalar
			v1->vector = 0
		end if

		regTB(v1_dclass)->ensure( regTB(v1_dclass), _
								  v1, _
								  symbGetDataSize( v1_dtype ) )

		if( op = AST_OP_SWZ_REPEAT ) then
			v1->vector = v1vector
		end if
	end if

	''
	select case as const op
	case AST_OP_NEG
		emitNEG( v1 )
	case AST_OP_NOT
		emitNOT( v1 )

	case AST_OP_HADD
		emitHADD( v1 )
		v1->vector = 0

	case AST_OP_ABS
		emitABS( v1 )
	case AST_OP_SGN
		emitSGN( v1 )
	case AST_OP_FIX
		emitFIX( v1 )
	case AST_OP_FRAC
		emitFRAC( v1 )

	case AST_OP_SIN
		emitSIN( v1 )
	case AST_OP_ASIN
		emitASIN( v1 )
	case AST_OP_COS
		emitCOS( v1 )
	case AST_OP_ACOS
		emitACOS( v1 )
	case AST_OP_TAN
		emitTAN( v1 )
	case AST_OP_ATAN
		emitATAN( v1 )
	case AST_OP_SQRT
		emitSQRT( v1 )
	case AST_OP_RSQRT
		emitRSQRT( v1 )
	case AST_OP_RCP
		emitRCP( v1 )
	case AST_OP_LOG
		emitLOG( v1 )
	case AST_OP_EXP
		emitEXP( v1 )
	case AST_OP_FLOOR
		emitFLOOR( v1 )

	case AST_OP_SWZ_REPEAT
		emitSWZREP( v1 )

	end select

    ''
    if ( vr <> NULL ) then
		if( v1 <> vr ) then
			emitMOV( vr, v1 )
		end if
	end if

    ''
	hFreeREG( v1 )
	hFreeREG( vr )

end sub

'':::::
private sub hFlushBOP _
	( _
		byval op as integer, _
		byval v1 as IRVREG ptr, _
		byval v2 as IRVREG ptr, _
		byval vr as IRVREG ptr _
	) static

	dim as integer v1_typ, v1_dtype, v1_dclass
	dim as integer v2_typ, v2_dtype, v2_dclass
	dim as integer vr_typ, vr_dtype, vr_dclass
	dim as IRVREG ptr va

	''
	hGetVREG( v1, v1_dtype, v1_dclass, v1_typ )
	hGetVREG( v2, v2_dtype, v2_dclass, v2_typ )
	hGetVREG( vr, vr_dtype, vr_dclass, vr_typ )

	hLoadIDX( v1 )
	hLoadIDX( v2 )
	hLoadIDX( vr )

	'' BOP to self? (x86 assumption at AST)
	if( vr = NULL ) then
		if( v2_typ <> IR_VREGTYPE_IMM ) then		'' x86 assumption
			'' handle longint
			if( ISLONGINT( v2_dtype ) ) then
				va = v2->vaux
				regTB(v2_dclass)->ensure( regTB(v2_dclass), _
										  va, _
										  symbGetDataSize( FB_DATATYPE_INTEGER ), _
										  FALSE )

				v2_dtype = FB_DATATYPE_INTEGER
			end if

			regTB(v2_dclass)->ensure( regTB(v2_dclass), _
									  v2, _
									  symbGetDataSize( v2_dtype ) )
		end if

	else
		if( v2_typ = IR_VREGTYPE_REG ) then			'' x86 assumption
			'' handle longint
			if( ISLONGINT( v2_dtype ) ) then
				va = v2->vaux
				regTB(v2_dclass)->ensure( regTB(v2_dclass), _
										  va, _
										  symbGetDataSize( FB_DATATYPE_INTEGER ), _
										  FALSE )

				v2_dtype = FB_DATATYPE_INTEGER
			end if

			regTB(v2_dclass)->ensure( regTB(v2_dclass), _
									  v2, _
									  symbGetDataSize( v2_dtype ) )
		end if

		'' destine allocation comes *after* source, 'cause the x86 FPU stack
		'' handle longint
		if( ISLONGINT( v1_dtype ) ) then
			va = v1->vaux
			regTB(v1_dclass)->ensure( regTB(v1_dclass), _
									  va, _
									  symbGetDataSize( FB_DATATYPE_INTEGER ), _
									  FALSE )

			v1_dtype = FB_DATATYPE_INTEGER
		end if

		regTB(v1_dclass)->ensure( regTB(v1_dclass), _
								  v1, _
								  symbGetDataSize( v1_dtype ) )
	end if

    ''
	select case as const op
	case AST_OP_ADD
		emitADD( v1, v2 )
	case AST_OP_SUB
		emitSUB( v1, v2 )
	case AST_OP_MUL
		emitMUL( v1, v2 )
	case AST_OP_DIV
        emitDIV( v1, v2 )
	case AST_OP_INTDIV
        emitINTDIV( v1, v2 )
	case AST_OP_MOD
		emitMOD( v1, v2 )

	case AST_OP_SHL
		emitSHL( v1, v2 )
	case AST_OP_SHR
		emitSHR( v1, v2 )

	case AST_OP_AND
		emitAND( v1, v2 )
	case AST_OP_OR
		emitOR( v1, v2 )
	case AST_OP_XOR
		emitXOR( v1, v2 )
	case AST_OP_EQV
		emitEQV( v1, v2 )
	case AST_OP_IMP
		emitIMP( v1, v2 )

	case AST_OP_ATAN2
        emitATN2( v1, v2 )
    case AST_OP_POW
    	emitPOW( v1, v2 )
	end select

    '' not BOP to self?
	if ( vr <> NULL ) then
		'' result not equal destine? (can happen with DAG optimizations)
		if( (v1 <> vr) ) then
			'' handle longint
			if( ISLONGINT( vr_dtype ) ) then
				va = vr->vaux
				regTB(vr_dclass)->ensure( regTB(vr_dclass), _
										  va, _
										  symbGetDataSize( FB_DATATYPE_INTEGER ), _
										  FALSE )

				vr_dtype = FB_DATATYPE_INTEGER
			end if

			regTB(vr_dclass)->ensure( regTB(vr_dclass), _
									  vr, _
									  symbGetDataSize( vr_dtype ) )

			emitMOV( vr, v1 )
		end if
	end if

    ''
	hFreeREG( v1 )
	hFreeREG( v2 )
	hFreeREG( vr )

end sub

'':::::
private sub hFlushCOMP _
	( _
		byval op as integer, _
		byval v1 as IRVREG ptr, _
		byval v2 as IRVREG ptr, _
		byval vr as IRVREG ptr, _
		byval label as FBSYMBOL ptr _
	) static

	dim as string lname
	dim as integer v1_typ, v1_dtype, v1_dclass
	dim as integer v2_typ, v2_dtype, v2_dclass
	dim as integer vr_typ, vr_dtype, vr_dclass
	dim as IRVREG ptr va
	dim as integer doload

	''
	hGetVREG( v1, v1_dtype, v1_dclass, v1_typ )
	hGetVREG( v2, v2_dtype, v2_dclass, v2_typ )
	hGetVREG( vr, vr_dtype, vr_dclass, vr_typ )

	hLoadIDX( v1 )
	hLoadIDX( v2 )
	hLoadIDX( vr )

	'' load source if it's a reg, or if result was not allocated
	doload = FALSE
	if( vr = NULL ) then							'' x86 assumption
		if( v2_dclass = FB_DATACLASS_INTEGER ) then	'' /
			if( v2_typ <> IR_VREGTYPE_IMM ) then	'' /
				if( v1_dclass <> FB_DATACLASS_FPOINT ) then
					doload = TRUE
				end if
			end if
		end if
	end if

	if( (v2_typ = IR_VREGTYPE_REG) or doload ) then
		'' handle longint
		if( ISLONGINT( v2_dtype ) ) then
			va = v2->vaux
			regTB(v2_dclass)->ensure( regTB(v2_dclass), _
									  va, _
									  symbGetDataSize( FB_DATATYPE_INTEGER ), _
									  FALSE )

			v2_dtype = FB_DATATYPE_INTEGER
		end if

		regTB(v2_dclass)->ensure( regTB(v2_dclass), _
								  v2, _
								  symbGetDataSize( v2_dtype ) )
		v2_typ = IR_VREGTYPE_REG
	end if

	'' destine allocation comes *after* source, 'cause the FPU stack
	doload = FALSE
	if( (vr <> NULL) and (vr = v1) ) then			'' x86 assumption
		doload = TRUE
	elseif( v1_dclass = FB_DATACLASS_FPOINT ) then	'' /
		doload = TRUE
	elseif( v1_typ = IR_VREGTYPE_IMM) then          '' /
		doload = TRUE
	elseif( v2_typ <> IR_VREGTYPE_REG ) then        '' /
		if( v2_typ <> IR_VREGTYPE_IMM ) then
			doload = TRUE
		end if
	end if

	if( (v1_typ = IR_VREGTYPE_REG) or doload ) then
		'' handle longint
		if( ISLONGINT( v1_dtype ) ) then
			va = v1->vaux
			regTB(v1_dclass)->ensure( regTB(v1_dclass), _
									  va, _
									  symbGetDataSize( FB_DATATYPE_INTEGER ), _
									  FALSE )

			v1_dtype = FB_DATATYPE_INTEGER
		end if

		regTB(v1_dclass)->ensure( regTB(v1_dclass), _
								  v1, _
								  symbGetDataSize( v1_dtype ) )
	end if

	'' result not equal destine? (can happen with DAG optimizations and floats comparations)
	if( vr <> NULL ) then
		if( vr <> v1 ) then
			vr->reg = regTB(vr_dclass)->_allocate( regTB(vr_dclass), _
												  vr, _
												  symbGetDataSize( vr_dtype ) )
			vr->typ = IR_VREGTYPE_REG
		end if
	end if

	''
	select case as const op
	case AST_OP_EQ
		emitEQ( vr, label, v1, v2 )
	case AST_OP_NE
		emitNE( vr, label, v1, v2 )
	case AST_OP_GT
		emitGT( vr, label, v1, v2 )
	case AST_OP_LT
		emitLT( vr, label, v1, v2 )
	case AST_OP_LE
		emitLE( vr, label, v1, v2 )
	case AST_OP_GE
		emitGE( vr, label, v1, v2 )
	end select

    ''
	hFreeREG( v1 )
	hFreeREG( v2 )
	if( vr <> NULL ) then
		hFreeREG( vr )
	end if

end sub

'':::::
private sub hSpillRegs( ) static
    dim as IRVREG ptr vr
    dim as integer reg
    dim as integer class_

	'' for each reg class
	for class_ = 0 to EMIT_REGCLASSES-1

		'' for each register on that class
		reg = regTB(class_)->getFirst( regTB(class_) )
		do until( reg = INVALID )
			'' if not free
			if( regTB(class_)->isFree( regTB(class_), reg ) = FALSE ) then

				'' get the attached vreg
				vr = regTB(class_)->getVreg( regTB(class_), reg )

        		'' spill
        		irStoreVR( vr, reg )

        		'' free reg
        		regTB(class_)->free( regTB(class_), reg )
        	end if

        	'' next reg
        	reg = regTB(class_)->getNext( regTB(class_), reg )
		loop

	next

end sub

'':::::
private sub hFlushSTORE _
	( _
		byval op as integer, _
		byval v1 as IRVREG ptr, _
		byval v2 as IRVREG ptr _
	) static

	dim as integer v1_typ, v1_dtype, v1_dclass
	dim as integer v2_typ, v2_dtype, v2_dclass
	dim as IRVREG ptr va

	''
	if( op = AST_OP_SPILLREGS ) then
		hSpillRegs( )
		exit sub
	end if

	''
	hGetVREG( v1, v1_dtype, v1_dclass, v1_typ )
	hGetVREG( v2, v2_dtype, v2_dclass, v2_typ )

	hLoadIDX( v1 )
	hLoadIDX( v2 )

    '' if dst is a fpoint, only load src if its a reg (x86 assumption)
	if( (v2_typ = IR_VREGTYPE_REG) or _
		((v2_typ <> IR_VREGTYPE_IMM) and (v1_dclass = FB_DATACLASS_INTEGER)) ) then

		'' handle longint
		if( ISLONGINT( v2_dtype ) ) then
			va = v2->vaux
			regTB(v2_dclass)->ensure( regTB(v2_dclass), _
									  va, _
									  symbGetDataSize( FB_DATATYPE_INTEGER ), _
									  FALSE )

			v2_dtype = FB_DATATYPE_INTEGER
		end if

		regTB(v2_dclass)->ensure( regTB(v2_dclass), _
								  v2, _
								  symbGetDataSize( v2_dtype ) )
	end if

	''
	emitSTORE( v1, v2 )

    ''
	hFreeREG( v1 )
	hFreeREG( v2 )

end sub

'':::::
private sub hFlushLOAD _
	( _
		byval op as integer, _
		byval v1 as IRVREG ptr, _
		byval vr as IRVREG ptr _
	) static

	dim as integer v1_typ, v1_dtype, v1_dclass, v1_reg
	dim as IRVREG ptr va

	''
	hGetVREG( v1, v1_dtype, v1_dclass, v1_typ )

	hLoadIDX( v1 )

	''
	select case op
	case AST_OP_LOAD
		'' handle longint
		if( ISLONGINT( v1_dtype ) ) then
			va = v1->vaux
			regTB(v1_dclass)->ensure( regTB(v1_dclass), _
									  va, _
									  symbGetDataSize( FB_DATATYPE_INTEGER ), _
									  FALSE )

			v1_dtype = FB_DATATYPE_INTEGER
		end if

		regTB(v1_dclass)->ensure( regTB(v1_dclass), _
								  v1, _
								  symbGetDataSize( v1_dtype ) )

	case AST_OP_LOADRES
		if( v1_typ = IR_VREGTYPE_REG ) then
			'' handle longint
			if( ISLONGINT( v1_dtype ) ) then
				va = v1->vaux
				regTB(v1_dclass)->ensure( regTB(v1_dclass), _
										  va, _
										  symbGetDataSize( FB_DATATYPE_INTEGER ), _
										  FALSE )

				'' can't change v1_dtype
				v1_reg = regTB(v1_dclass)->ensure( regTB(v1_dclass), _
												   v1, _
												   symbGetDataSize( FB_DATATYPE_INTEGER ) )

			else
				v1_reg = regTB(v1_dclass)->ensure( regTB(v1_dclass), _
												   v1, _
												   symbGetDataSize( v1_dtype ) )
			end if


		else
			v1_reg = INVALID
		end if

		dim as integer vr_reg, vr_reg2

		emitGetResultReg( v1_dtype, v1_dclass, vr_reg, vr_reg2 )

		if( vr_reg <> v1_reg ) then
			'' handle longint
			if( ISLONGINT( v1_dtype ) ) then
				va = vr->vaux
				va->reg = regTB(v1_dclass)->allocateReg( regTB(v1_dclass), vr_reg2, va )
				va->typ = IR_VREGTYPE_REG
			end if

			vr->reg = regTB(v1_dclass)->allocateReg( regTB(v1_dclass), vr_reg, vr )
			vr->typ = IR_VREGTYPE_REG

			'' decide where to put the float (st(0) or xmm0) at the end of the function
			if( ast.proc.curr->sym->proc.returnMethod <> FB_RETURN_SSE ) then
				vr->regFamily = IR_REG_FPU_STACK
			end if

			''
			emitLOAD( vr, v1 )

			''
			hFreeREG( vr )						'' assuming this is the last operation
		end if
    end select

	''
	hFreeREG( v1 )

end sub

'':::::
private sub hFlushCONVERT _
	( _
		byval op as integer, _
		byval v1 as IRVREG ptr, _
		byval v2 as IRVREG ptr _
	) static

	dim as integer v1_typ, v1_dtype, v1_dclass
	dim as integer v2_typ, v2_dtype, v2_dclass
	dim as integer reuse
	dim as IRVREG ptr va

	''
	hGetVREG( v1, v1_dtype, v1_dclass, v1_typ )
	hGetVREG( v2, v2_dtype, v2_dclass, v2_typ )

	hLoadIDX( v1 )
	hLoadIDX( v2 )

    '' x86 assumption: if src is a reg and if classes are the same and
    ''                 src won't be used (DAG?), reuse src
	reuse = FALSE
	if( (v1_dclass = v2_dclass) and (v2_typ = IR_VREGTYPE_REG) ) then

		'' fp to fp conversion with source already on stack? do nothing..
		if( v2_dclass = FB_DATACLASS_FPOINT ) then
			if( irGetOption( IR_OPT_FPU_CONVERTOPER ) ) then

				v1->regFamily = v2->regFamily
				if( v2->regFamily = IR_REG_FPU_STACK ) then exit sub
			else
				v1->reg = v2->reg
				v2->reg = INVALID
				v1->typ = IR_VREGTYPE_REG
				regTB(v1_dclass)->setOwner( regTB(v1_dclass), v1->reg, v1 )
				exit sub
			endif

		end if

		'' it's an integer, check if used again
		if( irGetDistance( v2 ) = IR_MAXDIST ) then
			'' don't reuse if any operand is a byte (because [E]SI/[E]DI) or longint
			select case symbGetDataSize( v1_dtype )
			case 1, FB_INTEGERSIZE*2

			case else
				select case symbGetDataSize( v2_dtype )
				case 1, FB_INTEGERSIZE*2

				case else
					reuse = TRUE
				end select
			end select
		end if
	end if

	if( reuse ) then
		v1->reg = v2->reg
		v1->typ = IR_VREGTYPE_REG
		regTB(v1_dclass)->setOwner( regTB(v1_dclass), v1->reg, v1 )

	else
		if( v2_typ = IR_VREGTYPE_REG ) then			'' x86 assumption
			'' handle longint
			if( ISLONGINT( v2_dtype ) ) then
				va = v2->vaux
				regTB(v2_dclass)->ensure( regTB(v2_dclass), _
										  va, _
										  symbGetDataSize( FB_DATATYPE_INTEGER ), _
										  FALSE )

				v2_dtype = FB_DATATYPE_INTEGER
			end if

			regTB(v2_dclass)->ensure( regTB(v2_dclass), _
									  v2, _
									  symbGetDataSize( v2_dtype ) )
		end if

		'' handle longint
		if( ISLONGINT( v1_dtype ) ) then
			va = v1->vaux
			va->reg = regTB(v1_dclass)->_allocate( regTB(v1_dclass), _
												  va, _
												  symbGetDataSize( FB_DATATYPE_INTEGER ) )
			va->typ = IR_VREGTYPE_REG

			v1_dtype = FB_DATATYPE_INTEGER
		end if

		v1->reg = regTB(v1_dclass)->_allocate( regTB(v1_dclass), _
											  v1, _
											  symbGetDataSize( v1_dtype ) )
		v1->typ = IR_VREGTYPE_REG
	end if

	''
	emitLOAD( v1, v2 )

	''
	if( reuse = FALSE ) then
		hFreeREG( v2 )
	else
		v2->reg = INVALID
	end if

	''
	hFreeREG( v1 )

end sub

'':::::
private sub hFlushADDR _
	( _
		byval op as integer, _
		byval v1 as IRVREG ptr, _
		byval vr as IRVREG ptr _
	) static

	dim as integer v1_typ, v1_dtype, v1_dclass
	dim as integer vr_typ, vr_dtype, vr_dclass

	''
	hGetVREG( v1, v1_dtype, v1_dclass, v1_typ )
	hGetVREG( vr, vr_dtype, vr_dclass, vr_typ )

	hLoadIDX( v1 )
	hLoadIDX( vr )

	''
	if( v1_typ = IR_VREGTYPE_REG ) then				'' x86 assumption
		regTB(v1_dclass)->ensure( regTB(v1_dclass), _
								  v1, _
								  symbGetDataSize( v1_dtype ) )
	end if

	if( vr_typ = IR_VREGTYPE_REG ) then             '' x86 assumption
		regTB(vr_dclass)->ensure( regTB(vr_dclass), _
								  vr, _
								  symbGetDataSize( vr_dtype ) )
	end if

	''
	select case op
	case AST_OP_ADDROF
		emitADDROF( vr, v1 )
	case AST_OP_DEREF
		emitDEREF( vr, v1 )
	end select

    ''
	hFreeREG( v1 )
	hFreeREG( vr )

end sub

'':::::
private sub hFlushMEM _
	( _
		byval op as integer, _
		byval v1 as IRVREG ptr, _
		byval v2 as IRVREG ptr, _
		byval bytes as integer, _
		byval extra as any ptr _
	) static

	''
	hLoadIDX( v1 )
	hLoadIDX( v2 )

	''
	select case as const op
	case AST_OP_MEMMOVE
		emitMEMMOVE( v1, v2, bytes )

	case AST_OP_MEMSWAP
		emitMEMSWAP( v1, v2, bytes )

	case AST_OP_MEMCLEAR
		emitMEMCLEAR( v1, v2 )

	case AST_OP_STKCLEAR
		emitSTKCLEAR( bytes, cint( extra ) )
	end select

    ''
	hFreeREG( v1 )
	hFreeREG( v2 )

end sub

'':::::
private sub hFlushDBG _
	( _
		byval op as integer, _
		byval proc as FBSYMBOL ptr, _
		byval ex as integer _
	)

	select case as const op
	case AST_OP_DBG_LINEINI
		emitDBGLineBegin( proc, ex )

	case AST_OP_DBG_LINEEND
		emitDBGLineEnd( proc, ex )

	case AST_OP_DBG_SCOPEINI
		emitDBGScopeBegin( cast( FBSYMBOL ptr, ex ) )

	case AST_OP_DBG_SCOPEEND
		emitDBGScopeEnd( cast( FBSYMBOL ptr, ex ) )
	end select

end sub

'':::::
private sub hFlushLIT _
	( _
		byval op as integer, _
		byval ex as any ptr _
	)

	dim as zstring ptr text = cast( zstring ptr, ex )

	select case op
	case AST_OP_LIT_COMMENT
		emitComment( text )

	case AST_OP_LIT_ASM
		emitASM( text )

	end select

	ZstrFree( text )

end sub

''::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

'':::::
private sub hFreeIDX _
	( _
		byval vreg as IRVREG ptr, _
		byval force as integer = FALSE _
	)

	dim as IRVREG ptr vidx

	if( vreg = NULL ) then
		exit sub
	end if

	vidx = vreg->vidx
    if( vidx <> NULL ) then
    	if( vidx->reg <> INVALID ) then
    		hFreeREG( vidx, force )				'' recursively
    		vreg->vidx = NULL
		end if
	end if

end sub

'':::::
private sub hFreeREG _
	( _
		byval vreg as IRVREG ptr, _
		byval force as integer = FALSE _
	)

	dim as integer dclass, dist
	dim as IRVREG ptr vaux

	if( vreg = NULL ) then
		exit sub
	end if

	'' free any attached index
	hFreeIDX( vreg, force )

    ''
	if( vreg->typ <> IR_VREGTYPE_REG ) then
		exit sub
	end if

	if( vreg->reg = INVALID ) then
		exit sub
	end if

	''
	dist = IR_MAXDIST
	if( force = FALSE ) then
		dist = irGetDistance( vreg )
	end if

	if( dist = IR_MAXDIST ) then
		'' aux?
		if( vreg->vaux <> NULL ) then
			vaux = vreg->vaux
			if( vaux->reg <> INVALID ) then
				hFreeREG( vaux, TRUE )
			end if
		end if

    	dclass = irGetVRDataClass( vreg )
		regTB(dclass)->free( regTB(dclass), vreg->reg )
		vreg->reg = INVALID
	end if

end sub

'':::::
private function _GetDistance _
	( _
		byval vreg as IRVREG ptr _
	) as uinteger

    dim as IRVREG ptr v
    dim as IRTAC ptr t
    dim as integer dist

	if( vreg = NULL ) then
		return IR_MAXDIST
	end if

	'' skip the current tac
	t = flistGetNext( ctx.tacidx )

	'' eol?
	if( t = NULL ) then
		return IR_MAXDIST
	end if

	''
	dist = vreg->taclast->pos - t->pos

	'' not used anymore?
	if( dist < 0 ) then
		function = IR_MAXDIST
	else
		function = dist
	end if

end function

'':::::
private sub _loadVR _
	( _
		byval reg as integer, _
		byval vreg as IRVREG ptr, _
		byval doload as integer _
	) static

	dim as IRVREG rvreg

	if( vreg->typ <> IR_VREGTYPE_REG ) then

		if( doload ) then
			rvreg.typ 	= IR_VREGTYPE_REG
			rvreg.dtype = vreg->dtype
			rvreg.reg	= reg
			rvreg.vaux	= vreg->vaux

			rvreg.regFamily = vreg->regFamily

			emitLOAD( @rvreg, vreg )
		end if

    	'' free any attached reg, forcing if needed
    	hFreeIDX( vreg, TRUE )

    	vreg->typ = IR_VREGTYPE_REG
    end if

	vreg->reg = reg

	if( env.clopt.fputype >= FB_FPUTYPE_SSE ) and ( doLoad = FALSE ) then
		vreg->regFamily = IR_REG_SSE
	end if


end sub

'':::::
private sub hCreateTMPVAR _
	( _
		byval vreg as IRVREG ptr _
	) static

	if( vreg->typ <> IR_VREGTYPE_VAR ) then
		vreg->typ = IR_VREGTYPE_VAR
		vreg->sym = symbAddTempVar( vreg->dtype, NULL, TRUE )
		vreg->ofs = symbGetOfs( vreg->sym )
		vreg->reg = INVALID
	end if

end sub

'':::::
private sub _storeVR _
	( _
		byval vreg as IRVREG ptr, _
		byval reg as integer _
	) static

    dim as IRVREG rvreg
	dim as IRVREG ptr vareg

	if( irGetDistance( vreg ) = IR_MAXDIST ) then
		exit sub
	end if

	rvreg.typ		= IR_VREGTYPE_REG
	rvreg.dtype		= vreg->dtype
	rvreg.reg		= reg
	rvreg.vaux		= vreg->vaux

	rvreg.regFamily	= vreg->regFamily

	hCreateTMPVAR( vreg )

	emitSTORE( vreg, @rvreg )

	'' handle longints
	if( ISLONGINT( vreg->dtype ) ) then
		vareg = vreg->vaux
		if( vareg->typ <> IR_VREGTYPE_VAR ) then
			regTB(FB_DATACLASS_INTEGER)->free( regTB(FB_DATACLASS_INTEGER), vareg->reg )
			vareg->reg = INVALID
			vareg->typ = IR_VREGTYPE_VAR
			vareg->ofs = vreg->ofs + FB_INTEGERSIZE
		end if
	end if

	if( env.clopt.fputype >= FB_FPUTYPE_SSE ) then
		vreg->regFamily = IR_REG_SSE
	end if

end sub

'':::::
private sub _xchgTOS _
	( _
		byval reg as integer _
	) static

    dim as IRVREG rvreg

	rvreg.typ 	= IR_VREGTYPE_REG
	rvreg.dtype = FB_DATATYPE_DOUBLE
	rvreg.reg	= reg

	emitXchgTOS( @rvreg )

end sub

/'':::::
private sub hDump _
	( _
		byval op as integer, _
		byval v1 as IRVREG ptr, _
		byval v2 as IRVREG ptr, _
		byval vr as IRVREG ptr _
	) static

#macro hDumpVr( id, v )
	if( v <> NULL ) then
		print " " id ":" & hex( v ) & "(" & irGetVRType( v ) & ";";
		print using "##"; irGetVRDataType( v );
		print "," & symbGetDataClass( irGetVRDataType( v ) ) & ")";
	end if
#endmacro

	if( astGetOpId( op ) <> NULL ) then
		print using "[\  \]"; *astGetOpId( op );
	else
		print using "[####]"; op;
	end if

	hDumpVr( "d", vr )
	hDumpVr( "l", v1 )
	hDumpVr( "r", v2 )

	print

end sub
'/

''::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

'':::::
function irTAC_ctor _
	( _
	) as integer

	static as IR_VTBL _vtbl = _
	( _
		@_init, _
		@_end, _
		@_flush, _
		@_emitBegin, _
		@_emitEnd, _
		@_getOptionValue, _
		@_procBegin, _
		@_procEnd, _
		@_procAllocArg, _
		@_procAllocLocal, _
		@_procGetFrameRegName, _
		@_scopeBegin, _
		@_scopeEnd, _
		@_procAllocStaticVars, _
		@_emit, _
		@_emitConvert, _
		@_emitLabel, _
		@_emitLabelNF, _
		@_emitReturn, _
		@_emitProcBegin, _
		@_emitProcEnd, _
		@_emitPushArg, _
		@_emitASM, _
		@_emitComment, _
		@_emitJmpTb, _
		@_emitInfoSection, _
		@_emitBop, _
		@_emitBopEx, _
		@_emitUop, _
		@_emitStore, _
		@_emitSpillRegs, _
		@_emitLoad, _
		@_emitLoadRes, _
		@_emitStack, _
		@_emitPushUDT, _
		@_emitAddr, _
		@_emitCall, _
		@_emitCallPtr, _
		@_emitStackAlign, _
		@_emitJumpPtr, _
		@_emitBranch, _
		@_emitMem, _
		@_emitScopeBegin, _
		@_emitScopeEnd, _
		@_emitDBG, _
		@_emitVarIniBegin, _
		@_emitVarIniEnd, _
		@_emitVarIniI, _
		@_emitVarIniF, _
		@_emitVarIniI64, _
		@_emitVarIniOfs, _
		@_emitVarIniStr, _
		@_emitVarIniWstr, _
		@_emitVarIniPad, _
		@_emitVarIniScopeBegin, _
		@_emitVarIniScopeEnd, _
		@_emitVarIniSeparator, _
		@_allocVreg, _
		@_allocVrImm, _
		@_allocVrImm64, _
		@_allocVrImmF, _
		@_allocVrVar, _
		@_allocVrIdx, _
		@_allocVrPtr, _
		@_allocVrOfs, _
		@_setVregDataType, _
		@_getDistance, _
		@_loadVr, _
		@_storeVr, _
		@_xchgTOS, _
		@_makeTmpStr _
	)

	ir.vtbl = _vtbl

	function = TRUE

end function
