{ Copyright (c) 2000-2002 by Florian Klaempfl and Jonas Maebe Code generation for add nodes on the PowerPC64 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 nppcadd; {$I fpcdefs.inc} interface uses node, nadd, ncgadd, cpubase; type tppcaddnode = class(tcgaddnode) function pass_1: tnode; override; procedure pass_2; override; private procedure pass_left_and_right; procedure load_left_right(cmpop, load_constants: boolean); function getresflags: tresflags; procedure emit_compare(unsigned: boolean); procedure second_addfloat; override; procedure second_addboolean; override; procedure second_addsmallset; override; end; implementation uses sysutils, globtype, systems, cutils, verbose, globals, symconst, symdef, paramgr, aasmbase, aasmtai, aasmcpu, defutil, htypechk, cgbase, cpuinfo, pass_1, pass_2, regvars, cpupara, cgcpu, cgutils, ncon, nset, ncgutil, tgobj, rgobj, rgcpu, cgobj; {***************************************************************************** Pass 1 *****************************************************************************} function tppcaddnode.pass_1: tnode; begin resulttypepass(left); if (nodetype in [equaln, unequaln]) and (left.resulttype.def.deftype = orddef) {and is_64bit(left.resulttype.def)}then begin result := nil; firstpass(left); firstpass(right); expectloc := LOC_FLAGS; calcregisters(self, 2, 0, 0); exit; end; result := inherited pass_1; end; {***************************************************************************** Helpers *****************************************************************************} procedure tppcaddnode.pass_left_and_right; begin { calculate the operator which is more difficult } firstcomplex(self); { in case of constant put it to the left } if (left.nodetype = ordconstn) then swapleftright; secondpass(left); secondpass(right); end; procedure tppcaddnode.load_left_right(cmpop, load_constants: boolean); procedure load_node(var n: tnode); begin case n.location.loc of LOC_REGISTER: if not cmpop then begin location.register := n.location.register; end; LOC_REFERENCE, LOC_CREFERENCE: begin location_force_reg(exprasmlist, n.location, def_cgsize(n.resulttype.def), false); if not cmpop then begin location.register := n.location.register; end; end; LOC_CONSTANT: begin if load_constants then begin location_force_reg(exprasmlist, n.location, def_cgsize(n.resulttype.def), false); if not cmpop then location.register := n.location.register; end; end; end; end; begin load_node(left); load_node(right); if not (cmpop) and (location.register = NR_NO) then begin location.register := cg.getintregister(exprasmlist, OS_INT); end; end; function tppcaddnode.getresflags: tresflags; begin if (left.resulttype.def.deftype <> floatdef) then result.cr := RS_CR0 else result.cr := RS_CR1; case nodetype of equaln: result.flag := F_EQ; unequaln: result.flag := F_NE; else if nf_swaped in flags then case nodetype of ltn: result.flag := F_GT; lten: result.flag := F_GE; gtn: result.flag := F_LT; gten: result.flag := F_LE; end else case nodetype of ltn: result.flag := F_LT; lten: result.flag := F_LE; gtn: result.flag := F_GT; gten: result.flag := F_GE; end; end end; procedure tppcaddnode.emit_compare(unsigned: boolean); var op: tasmop; tmpreg: tregister; useconst: boolean; begin // get the constant on the right if there is one if (left.location.loc = LOC_CONSTANT) then swapleftright; // can we use an immediate, or do we have to load the // constant in a register first? if (right.location.loc = LOC_CONSTANT) then begin if (nodetype in [equaln, unequaln]) then if (unsigned and (aword(right.location.value) > high(word))) or (not unsigned and (aint(right.location.value) < low(smallint)) or (aint(right.location.value) > high(smallint))) then { we can then maybe use a constant in the 'othersigned' case (the sign doesn't matter for // equal/unequal)} unsigned := not unsigned; if (unsigned and (aword(right.location.value) <= high(word))) or (not (unsigned) and (aint(right.location.value) >= low(smallint)) and (aint(right.location.value) <= high(smallint))) then useconst := true else begin useconst := false; tmpreg := cg.getintregister(exprasmlist, OS_INT); cg.a_load_const_reg(exprasmlist, OS_INT, right.location.value, tmpreg); end end else useconst := false; location.loc := LOC_FLAGS; location.resflags := getresflags; if not unsigned then if useconst then op := A_CMPDI else op := A_CMPD else if useconst then op := A_CMPLDI else op := A_CMPLD; if (right.location.loc = LOC_CONSTANT) then begin if useconst then exprasmlist.concat(taicpu.op_reg_const(op, left.location.register, longint(right.location.value))) else exprasmlist.concat(taicpu.op_reg_reg(op, left.location.register, tmpreg)); end else exprasmlist.concat(taicpu.op_reg_reg(op, left.location.register, right.location.register)); end; {***************************************************************************** AddBoolean *****************************************************************************} procedure tppcaddnode.second_addboolean; var cgop: TOpCg; cgsize: TCgSize; cmpop, isjump: boolean; otl, ofl: tasmlabel; begin { calculate the operator which is more difficult } firstcomplex(self); cmpop := false; if (torddef(left.resulttype.def).typ = bool8bit) or (torddef(right.resulttype.def).typ = bool8bit) then cgsize := OS_8 else if (torddef(left.resulttype.def).typ = bool16bit) or (torddef(right.resulttype.def).typ = bool16bit) then cgsize := OS_16 else cgsize := OS_32; if (cs_full_boolean_eval in aktlocalswitches) or (nodetype in [unequaln, ltn, lten, gtn, gten, equaln, xorn]) then begin if left.nodetype in [ordconstn, realconstn] then swapleftright; isjump := (left.expectloc = LOC_JUMP); if isjump then begin otl := truelabel; objectlibrary.getjumplabel(truelabel); ofl := falselabel; objectlibrary.getjumplabel(falselabel); end; secondpass(left); if left.location.loc in [LOC_FLAGS, LOC_JUMP] then location_force_reg(exprasmlist, left.location, cgsize, false); if isjump then begin truelabel := otl; falselabel := ofl; end else if left.location.loc = LOC_JUMP then internalerror(2003122901); isjump := (right.expectloc = LOC_JUMP); if isjump then begin otl := truelabel; objectlibrary.getjumplabel(truelabel); ofl := falselabel; objectlibrary.getjumplabel(falselabel); end; secondpass(right); if right.location.loc in [LOC_FLAGS, LOC_JUMP] then location_force_reg(exprasmlist, right.location, cgsize, false); if isjump then begin truelabel := otl; falselabel := ofl; end else if right.location.loc = LOC_JUMP then internalerror(200312292); cmpop := nodetype in [ltn, lten, gtn, gten, equaln, unequaln]; { set result location } if not cmpop then location_reset(location, LOC_REGISTER, def_cgsize(resulttype.def)) else location_reset(location, LOC_FLAGS, OS_NO); load_left_right(cmpop, false); if (left.location.loc = LOC_CONSTANT) then swapleftright; { compare the } case nodetype of ltn, lten, gtn, gten, equaln, unequaln: begin if (right.location.loc <> LOC_CONSTANT) then exprasmlist.concat(taicpu.op_reg_reg(A_CMPLW, left.location.register, right.location.register)) else exprasmlist.concat(taicpu.op_reg_const(A_CMPLWI, left.location.register, longint(right.location.value))); location.resflags := getresflags; end; else begin case nodetype of xorn: cgop := OP_XOR; orn: cgop := OP_OR; andn: cgop := OP_AND; else internalerror(200203247); end; if right.location.loc <> LOC_CONSTANT then cg.a_op_reg_reg_reg(exprasmlist, cgop, OS_INT, left.location.register, right.location.register, location.register) else cg.a_op_const_reg_reg(exprasmlist, cgop, OS_INT, right.location.value, left.location.register, location.register); end; end; end else begin // just to make sure we free the right registers cmpop := true; case nodetype of andn, orn: begin location_reset(location, LOC_JUMP, OS_NO); case nodetype of andn: begin otl := truelabel; objectlibrary.getjumplabel(truelabel); secondpass(left); maketojumpbool(exprasmlist, left, lr_load_regvars); cg.a_label(exprasmlist, truelabel); truelabel := otl; end; orn: begin ofl := falselabel; objectlibrary.getjumplabel(falselabel); secondpass(left); maketojumpbool(exprasmlist, left, lr_load_regvars); cg.a_label(exprasmlist, falselabel); falselabel := ofl; end; else internalerror(200403181); end; secondpass(right); maketojumpbool(exprasmlist, right, lr_load_regvars); end; end; end; end; {***************************************************************************** AddFloat *****************************************************************************} procedure tppcaddnode.second_addfloat; var op: TAsmOp; cmpop: boolean; begin pass_left_and_right; cmpop := false; case nodetype of addn: op := A_FADD; muln: op := A_FMUL; subn: op := A_FSUB; slashn: op := A_FDIV; ltn, lten, gtn, gten, equaln, unequaln: begin op := A_FCMPO; cmpop := true; end; else internalerror(200403182); end; // get the operands in the correct order, there are no special cases // here, everything is register-based if nf_swaped in flags then swapleftright; // put both operands in a register location_force_fpureg(exprasmlist, right.location, true); location_force_fpureg(exprasmlist, left.location, true); // initialize de result if not cmpop then begin location_reset(location, LOC_FPUREGISTER, def_cgsize(resulttype.def)); if left.location.loc = LOC_FPUREGISTER then location.register := left.location.register else if right.location.loc = LOC_FPUREGISTER then location.register := right.location.register else location.register := cg.getfpuregister(exprasmlist, location.size); end else begin location_reset(location, LOC_FLAGS, OS_NO); location.resflags := getresflags; end; // emit the actual operation if not cmpop then begin exprasmlist.concat(taicpu.op_reg_reg_reg(op, location.register, left.location.register, right.location.register)) end else begin exprasmlist.concat(taicpu.op_reg_reg_reg(op, newreg(R_SPECIALREGISTER, location.resflags.cr, R_SUBNONE), left.location.register, right.location.register)) end; end; {***************************************************************************** AddSmallSet *****************************************************************************} procedure tppcaddnode.second_addsmallset; var cgop: TOpCg; tmpreg: tregister; opdone, cmpop: boolean; astring : string; // ts: todo - speed up by using 32 bit compares/adds/ands here begin pass_left_and_right; { when a setdef is passed, it has to be a smallset } if ((left.resulttype.def.deftype = setdef) and (tsetdef(left.resulttype.def).settype <> smallset)) or ((right.resulttype.def.deftype = setdef) and (tsetdef(right.resulttype.def).settype <> smallset)) then internalerror(200203301); opdone := false; cmpop := nodetype in [equaln, unequaln, lten, gten]; { set result location } if not cmpop then location_reset(location, LOC_REGISTER, def_cgsize(resulttype.def)) else location_reset(location, LOC_FLAGS, OS_NO); load_left_right(cmpop, false); if not (cmpop) and (location.register = NR_NO) then location.register := cg.getintregister(exprasmlist, OS_64); astring := 'addsmallset0 ' + inttostr(aword(1) shl aword(right.location.value)) + ' ' + inttostr(right.location.value); exprasmlist.concat(tai_comment.create(strpnew(astring))); case nodetype of addn: begin if (nf_swaped in flags) and (left.nodetype = setelementn) then swapleftright; { are we adding set elements ? } if right.nodetype = setelementn then begin { no range support for smallsets! } if assigned(tsetelementnode(right).right) then internalerror(43244); if (right.location.loc = LOC_CONSTANT) then begin astring := 'addsmallset1 ' + inttostr(aword(1) shl aword(right.location.value)) + ' ' + inttostr(right.location.value); exprasmlist.concat(tai_comment.create(strpnew(astring))); cg.a_op_const_reg_reg(exprasmlist, OP_OR, OS_64, aint(1) shl aint(right.location.value), left.location.register, location.register) end else begin tmpreg := cg.getintregister(exprasmlist, OS_64); cg.a_load_const_reg(exprasmlist, OS_64, 1, tmpreg); cg.a_op_reg_reg(exprasmlist, OP_SHL, OS_64, right.location.register, tmpreg); if left.location.loc <> LOC_CONSTANT then begin cg.a_op_reg_reg_reg(exprasmlist, OP_OR, OS_64, tmpreg, left.location.register, location.register) end else begin astring := 'addsmallset2 ' + inttostr(left.location.value); exprasmlist.concat(tai_comment.create(strpnew(astring))); cg.a_op_const_reg_reg(exprasmlist, OP_OR, OS_64, left.location.value, tmpreg, location.register); end; end; opdone := true; end else begin cgop := OP_OR; end; end; symdifn: cgop := OP_XOR; muln: cgop := OP_AND; subn: begin cgop := OP_AND; if (not (nf_swaped in flags)) then if (right.location.loc = LOC_CONSTANT) then right.location.value := not (right.location.value) else opdone := true else if (left.location.loc = LOC_CONSTANT) then left.location.value := not (left.location.value) else begin swapleftright; opdone := true; end; if opdone then begin if left.location.loc = LOC_CONSTANT then begin tmpreg := cg.getintregister(exprasmlist, OS_64); cg.a_load_const_reg(exprasmlist, OS_64, left.location.value, tmpreg); exprasmlist.concat(taicpu.op_reg_reg_reg(A_ANDC, location.register, tmpreg, right.location.register)); end else exprasmlist.concat(taicpu.op_reg_reg_reg(A_ANDC, location.register, left.location.register, right.location.register)); end; end; equaln, unequaln: begin emit_compare(true); opdone := true; end; lten, gten: begin if (not (nf_swaped in flags) and (nodetype = lten)) or ((nf_swaped in flags) and (nodetype = gten)) then swapleftright; // now we have to check whether left >= right tmpreg := cg.getintregister(exprasmlist, OS_64); if left.location.loc = LOC_CONSTANT then begin cg.a_op_const_reg_reg(exprasmlist, OP_AND, OS_64, not (left.location.value), right.location.register, tmpreg); exprasmlist.concat(taicpu.op_reg_const(A_CMPDI, tmpreg, 0)); // the two instructions above should be folded together by // the peepholeoptimizer end else begin if right.location.loc = LOC_CONSTANT then begin cg.a_load_const_reg(exprasmlist, OS_64, right.location.value, tmpreg); exprasmlist.concat(taicpu.op_reg_reg_reg(A_ANDC_, tmpreg, tmpreg, left.location.register)); end else exprasmlist.concat(taicpu.op_reg_reg_reg(A_ANDC_, tmpreg, right.location.register, left.location.register)); end; location.resflags.cr := RS_CR0; location.resflags.flag := F_EQ; opdone := true; end; else internalerror(2002072701); end; if not opdone then begin // these are all commutative operations if (left.location.loc = LOC_CONSTANT) then swapleftright; if (right.location.loc = LOC_CONSTANT) then begin astring := 'addsmallset4 ' + inttostr(right.location.value); exprasmlist.concat(tai_comment.create(strpnew(astring))); cg.a_op_const_reg_reg(exprasmlist, cgop, OS_64, right.location.value, left.location.register, location.register) end else begin cg.a_op_reg_reg_reg(exprasmlist, cgop, OS_64, right.location.register, left.location.register, location.register); end; end; end; {***************************************************************************** pass_2 *****************************************************************************} procedure tppcaddnode.pass_2; { is also being used for xor, and "mul", "sub, or and comparative } { operators } var cgop: topcg; op: tasmop; tmpreg: tregister; hl: tasmlabel; cmpop: boolean; { true, if unsigned types are compared } unsigned: boolean; begin { to make it more readable, string and set (not smallset!) have their own procedures } case left.resulttype.def.deftype of orddef: begin { handling boolean expressions } if is_boolean(left.resulttype.def) and is_boolean(right.resulttype.def) then begin second_addboolean; exit; end; end; stringdef: begin internalerror(2002072402); exit; end; setdef: begin { normalsets are already handled in pass1 } if (tsetdef(left.resulttype.def).settype <> smallset) then internalerror(200109041); second_addsmallset; exit; end; arraydef: begin {$IFDEF SUPPORT_MMX} if is_mmx_able_array(left.resulttype.def) then begin second_addmmx; exit; end; {$ENDIF SUPPORT_MMX} end; floatdef: begin second_addfloat; exit; end; end; { defaults } cmpop := nodetype in [ltn, lten, gtn, gten, equaln, unequaln]; unsigned := not (is_signed(left.resulttype.def)) or not (is_signed(right.resulttype.def)); pass_left_and_right; { Convert flags to register first } { can any of these things be in the flags actually?? (JM) } if (left.location.loc = LOC_FLAGS) or (right.location.loc = LOC_FLAGS) then internalerror(2002072602); { set result location } if not cmpop then location_reset(location, LOC_REGISTER, def_cgsize(resulttype.def)) else location_reset(location, LOC_FLAGS, OS_NO); load_left_right(cmpop, (cs_check_overflow in aktlocalswitches) and (nodetype in [addn, subn, muln])); if (location.register = NR_NO) and not (cmpop) then location.register := cg.getintregister(exprasmlist, OS_INT); if not (cs_check_overflow in aktlocalswitches) or (cmpop) or (nodetype in [orn, andn, xorn]) then begin case nodetype of addn, muln, xorn, orn, andn: begin case nodetype of addn: cgop := OP_ADD; muln: if unsigned then cgop := OP_MUL else cgop := OP_IMUL; xorn: cgop := OP_XOR; orn: cgop := OP_OR; andn: cgop := OP_AND; end; if (left.location.loc = LOC_CONSTANT) then swapleftright; if (right.location.loc <> LOC_CONSTANT) then cg.a_op_reg_reg_reg(exprasmlist, cgop, OS_INT, left.location.register, right.location.register, location.register) else cg.a_op_const_reg_reg(exprasmlist, cgop, OS_INT, right.location.value, left.location.register, location.register); end; subn: begin if (nf_swaped in flags) then swapleftright; if left.location.loc <> LOC_CONSTANT then if right.location.loc <> LOC_CONSTANT then begin cg.a_op_reg_reg_reg(exprasmlist, OP_SUB, OS_INT, right.location.register, left.location.register, location.register); end else begin cg.a_op_const_reg_reg(exprasmlist, OP_SUB, OS_INT, right.location.value, left.location.register, location.register); end else begin tmpreg := cg.getintregister(exprasmlist, OS_INT); cg.a_load_const_reg(exprasmlist, OS_INT, left.location.value, tmpreg); cg.a_op_reg_reg_reg(exprasmlist, OP_SUB, OS_INT, right.location.register, tmpreg, location.register); end; end; ltn, lten, gtn, gten, equaln, unequaln: begin emit_compare(unsigned); end; end; end else // overflow checking is on and we have an addn, subn or muln begin if is_signed(resulttype.def) then begin case nodetype of addn: op := A_ADDO; subn: begin op := A_SUBO; if (nf_swaped in flags) then swapleftright; end; muln: op := A_MULLDO; else internalerror(2002072601); end; exprasmlist.concat(taicpu.op_reg_reg_reg(op, location.register, left.location.register, right.location.register)); cg.g_overflowcheck(exprasmlist, location, resulttype.def); end else begin case nodetype of addn: begin exprasmlist.concat(taicpu.op_reg_reg_reg(A_ADD, location.register, left.location.register, right.location.register)); exprasmlist.concat(taicpu.op_reg_reg(A_CMPLD, location.register, left.location.register)); cg.g_overflowcheck(exprasmlist, location, resulttype.def); end; subn: begin exprasmlist.concat(taicpu.op_reg_reg_reg(A_SUB, location.register, left.location.register, right.location.register)); exprasmlist.concat(taicpu.op_reg_reg(A_CMPLD, left.location.register, location.register)); cg.g_overflowcheck(exprasmlist, location, resulttype.def); end; muln: begin { calculate the upper 64 bits of the product, = 0 if no overflow } cg.a_reg_alloc(exprasmlist, NR_R0); exprasmlist.concat(taicpu.op_reg_reg_reg(A_MULHDU_, NR_R0, left.location.register, right.location.register)); cg.a_reg_dealloc(exprasmlist, NR_R0); { calculate the real result } exprasmlist.concat(taicpu.op_reg_reg_reg(A_MULLD, location.register, left.location.register, right.location.register)); { g_overflowcheck generates a OC_AE instead of OC_EQ :/ } objectlibrary.getjumplabel(hl); tcgppc(cg).a_jmp_cond(exprasmlist, OC_EQ, hl); cg.a_call_name(exprasmlist, 'FPC_OVERFLOW'); cg.a_label(exprasmlist, hl); end; end; end; end; end; begin caddnode := tppcaddnode; end.