123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364 |
- {
- 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, ngppcadd, cpubase;
- type
- tppcaddnode = class(tgenppcaddnode)
- procedure pass_generate_code override;
- private
- procedure emit_compare(unsigned: boolean); override;
- end;
- implementation
- uses
- sysutils,
- globtype, systems,
- cutils, verbose, globals,
- symconst, symdef, paramgr,
- aasmbase, aasmtai,aasmdata, aasmcpu, defutil, htypechk,
- cgbase, cpuinfo, pass_1, pass_2,
- cpupara, cgcpu, cgutils,procinfo,
- ncon, nset,
- ncgutil, tgobj, rgobj, rgcpu, cgobj;
- {*****************************************************************************
- Helpers
- *****************************************************************************}
- procedure tppcaddnode.emit_compare(unsigned: boolean);
- const
- { unsigned useconst 32bit-op }
- cmpop_table : array[boolean, boolean, boolean] of TAsmOp = (
- ((A_CMPD, A_CMPW), (A_CMPDI, A_CMPWI)),
- ((A_CMPLD, A_CMPLW), (A_CMPLDI, A_CMPLWI))
- );
- var
- op: TAsmOp;
- tmpreg: TRegister;
- useconst: boolean;
- opsize : TCgSize;
- begin
- tmpreg:=NR_NO;
- { get the constant on the right if there is one }
- if (left.location.loc = LOC_CONSTANT) then
- swapleftright;
- opsize := def_cgsize(left.resultdef);
- {$IFDEF EXTDEBUG}
- current_asmdata.CurrAsmList.concat(tai_comment.create(strpnew('tppcaddnode.emit_compare ' + inttostr(ord(opsize)) + ' ' + inttostr(tcgsize2size[opsize]))));
- {$ENDIF EXTDEBUG}
- { can we use a signed comparison or not? In case of equal/unequal comparison
- we can check whether this is possible because it does not matter. }
- if (right.location.loc = LOC_CONSTANT) then
- 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;
- { calculate the size of the comparison because ppc64 only has 32 and 64
- bit comparison opcodes; prefer 32 bits }
- if (not (opsize in [OS_32, OS_S32, OS_64, OS_S64])) then begin
- if (unsigned) then
- opsize := OS_32
- else
- opsize := OS_S32;
- cg.a_load_reg_reg(current_asmdata.CurrAsmList, def_cgsize(left.resultdef), opsize,
- left.location.register, left.location.register);
- end;
- { 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 (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(current_asmdata.CurrAsmList, OS_INT);
- cg.a_load_const_reg(current_asmdata.CurrAsmList, opsize, right.location.value, tmpreg);
- end
- end else
- useconst := false;
- location.loc := LOC_FLAGS;
- location.resflags := getresflags;
- op := cmpop_table[unsigned, useconst, opsize in [OS_S32, OS_32]];
- { actually do the operation }
- if (right.location.loc = LOC_CONSTANT) then begin
- if useconst then
- current_asmdata.CurrAsmList.concat(taicpu.op_reg_const(op, left.location.register,
- longint(right.location.value)))
- else
- current_asmdata.CurrAsmList.concat(taicpu.op_reg_reg(op, left.location.register, tmpreg));
- end else
- current_asmdata.CurrAsmList.concat(taicpu.op_reg_reg(op, left.location.register,
- right.location.register));
- end;
- {*****************************************************************************
- pass_2
- *****************************************************************************}
- procedure tppcaddnode.pass_generate_code;
- { is also being used for xor, and "mul", "sub, or and comparative }
- { operators }
- var
- cgop: topcg;
- op: tasmop;
- tmpreg: tregister;
- hl: tasmlabel;
- cmpop: boolean;
- checkoverflow: 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.resultdef.typ of
- orddef:
- begin
- { handling boolean expressions }
- if is_boolean(left.resultdef) and
- is_boolean(right.resultdef) then
- begin
- second_addboolean;
- exit;
- end;
- end;
- stringdef:
- begin
- internalerror(2002072402);
- exit;
- end;
- setdef:
- begin
- { normalsets are already handled in pass1 }
- if not is_smallset(left.resultdef) then
- internalerror(200109041);
- second_addsmallset;
- exit;
- end;
- arraydef:
- begin
- {$IFDEF SUPPORT_MMX}
- if is_mmx_able_array(left.resultdef) 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.resultdef)) or
- not (is_signed(right.resultdef));
- 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(resultdef))
- else
- location_reset(location, LOC_FLAGS, OS_NO);
- checkoverflow:=
- (nodetype in [addn,subn,muln]) and
- (cs_check_overflow in current_settings.localswitches) and
- (left.resultdef.typ<>pointerdef) and
- (right.resultdef.typ<>pointerdef);
- load_left_right(cmpop, checkoverflow);
- if not (cmpop) then
- location.register := cg.getintregister(current_asmdata.CurrAsmList, OS_INT);
- if not (checkoverflow) 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;
- else
- internalerror(2013120112);
- end;
- if (left.location.loc = LOC_CONSTANT) then
- swapleftright;
- if (right.location.loc <> LOC_CONSTANT) then
- cg.a_op_reg_reg_reg(current_asmdata.CurrAsmList, cgop, OS_INT,
- left.location.register, right.location.register,
- location.register)
- else
- cg.a_op_const_reg_reg(current_asmdata.CurrAsmList, cgop, OS_INT,
- right.location.value, left.location.register,
- location.register);
- end;
- subn:
- begin
- if (nf_swapped 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(current_asmdata.CurrAsmList, OP_SUB, OS_INT,
- right.location.register, left.location.register,
- location.register);
- end else begin
- cg.a_op_const_reg_reg(current_asmdata.CurrAsmList, OP_SUB, OS_INT,
- right.location.value, left.location.register,
- location.register);
- end
- else
- begin
- tmpreg := cg.getintregister(current_asmdata.CurrAsmList, OS_INT);
- cg.a_load_const_reg(current_asmdata.CurrAsmList, OS_INT,
- left.location.value, tmpreg);
- cg.a_op_reg_reg_reg(current_asmdata.CurrAsmList, OP_SUB, OS_INT,
- right.location.register, tmpreg, location.register);
- end;
- end;
- ltn, lten, gtn, gten, equaln, unequaln:
- begin
- {$ifdef extdebug}
- current_asmdata.CurrAsmList.concat(tai_comment.create('tppcaddnode.pass2'));
- {$endif extdebug}
- emit_compare(unsigned);
- end;
- end;
- end
- else
- // overflow checking is on and we have an addn, subn or muln
- begin
- if is_signed(resultdef) then
- begin
- case nodetype of
- addn:
- op := A_ADDO;
- subn:
- begin
- op := A_SUBO;
- if (nf_swapped in flags) then
- swapleftright;
- end;
- muln:
- op := A_MULLDO;
- else
- internalerror(2002072601);
- end;
- current_asmdata.CurrAsmList.concat(taicpu.op_reg_reg_reg(op, location.register,
- left.location.register, right.location.register));
- cg.g_overflowcheck(current_asmdata.CurrAsmList, location, resultdef);
- end
- else
- begin
- case nodetype of
- addn:
- begin
- current_asmdata.CurrAsmList.concat(taicpu.op_reg_reg_reg(A_ADD, location.register,
- left.location.register, right.location.register));
- current_asmdata.CurrAsmList.concat(taicpu.op_reg_reg(A_CMPLD, location.register,
- left.location.register));
- cg.g_overflowcheck(current_asmdata.CurrAsmList, location, resultdef);
- end;
- subn:
- begin
- if (nf_swapped in flags) then
- swapleftright;
- current_asmdata.CurrAsmList.concat(taicpu.op_reg_reg_reg(A_SUB, location.register,
- left.location.register, right.location.register));
- current_asmdata.CurrAsmList.concat(taicpu.op_reg_reg(A_CMPLD,
- left.location.register, location.register));
- cg.g_overflowcheck(current_asmdata.CurrAsmList, location, resultdef);
- end;
- muln:
- begin
- { calculate the upper 64 bits of the product, = 0 if no overflow }
- cg.a_reg_alloc(current_asmdata.CurrAsmList, NR_R0);
- current_asmdata.CurrAsmList.concat(taicpu.op_reg_reg_reg(A_MULHDU_, NR_R0,
- left.location.register, right.location.register));
- cg.a_reg_dealloc(current_asmdata.CurrAsmList, NR_R0);
- { calculate the real result }
- current_asmdata.CurrAsmList.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 :/ }
- current_asmdata.getjumplabel(hl);
- tcgppc(cg).a_jmp_cond(current_asmdata.CurrAsmList, OC_EQ, hl);
- cg.a_call_name(current_asmdata.CurrAsmList, 'FPC_OVERFLOW',false);
- cg.a_label(current_asmdata.CurrAsmList, hl);
- end;
- end;
- end;
- end;
- end;
- begin
- caddnode := tppcaddnode;
- end.
|