📄 ccout.c
字号:
case P_TRZ:
case P_TRO:
case P_CAI:
if (typ != PTA_RCONST || (p->Pvalue &~ 0777777L) == 0)
break;
if ((p->Pvalue & 0777777L) == 0)
{
switch (p->Pop & POF_OPCODE)
{
case P_TRN:
i = P_TRN ^ P_TLN;
break;
case P_TRO:
i = P_TRO ^ P_TLO;
break;
case P_TRZ:
i = P_TRZ ^ P_TLZ;
break;
case P_TRC:
i = P_TRC ^ P_TLC;
break;
default:
i = 0;
break;
}
if (i)
{
p->Pop ^= i; /* Flip from RH to LH */
p->Pvalue = ((unsigned INT) p->Pvalue) >> 18;
break;
}
}
if (! (p->Pvalue &~ 0777777L))
break; /* still too big? */
p->Pop = directop (p->Pop); /* Turn into memory version */
typ |= PTF_IMM; /* Make PTV_IMMED so outinstr hacks bigness */
break;
/* Optimization for non-test bitwise operations (IOR, AND, XOR).
** We try turning these into tests to avoid referencing memory
** for a literal constant.
*/
case P_IOR:
if (!optobj || typ != PTV_IMMED || (p->Pvalue &~ 0777777L) == 0)
break; /* Can't or don't optimize */
if ((p->Pvalue & 0777777L) == 0) /* x,,0 */
{
p->Pop = P_TLO;
typ = PTA_RCONST;
p->Pvalue = ((unsigned INT) p->Pvalue) >> 18;
}
else if (( (unsigned INT) p->Pvalue >> 18) == 0777777L) /* 777777,,x */
{
p->Pop = P_ORCM;
p->Pvalue = (~p->Pvalue) & 0777777L;
}
break;
case P_XOR:
if (!optobj || typ != PTV_IMMED || (p->Pvalue &~ 0777777L) == 0)
break; /* Can't or don't optimize */
if ((p->Pvalue & 0777777L) == 0) /* x,,0 */
{
p->Pop = P_TLC;
typ = PTA_RCONST;
p->Pvalue = ((unsigned INT) p->Pvalue) >> 18;
}
else if (( (unsigned INT) p->Pvalue >> 18) == 0777777L) /* 777777,,x */
{
p->Pop = P_EQV;
p->Pvalue = (~p->Pvalue) & 0777777L;
}
break;
case P_AND:
if (!optobj || typ != PTV_IMMED || (p->Pvalue &~ 0777777L) == 0)
break; /* Can't or don't optimize */
if ((p->Pvalue & 0777777L) == 0777777L) /* x,,777777 */
{
p->Pop = P_TLZ;
typ = PTA_RCONST;
p->Pvalue = (unsigned INT) (~p->Pvalue) >> 18;
}
else if (( (unsigned INT)p->Pvalue >> 18) == 0777777L) /* 777777,,x */
{
p->Pop = P_TRZ;
typ = PTA_RCONST;
p->Pvalue = (~p->Pvalue) & 0777777L;
}
break;
/*
** Signed integer multiplication by a power of two is better done as an ASH
** (arithmetic shift - not the same as LSH). Don't try this
** for division, as the result is incorrect for negative numbers!
** Note that unsigned multiplication will use P_MUL, not P_IMUL.
** Note the subtle test for power-of-two-ness which relies on
** twos-complement arithmetic.
**
** Similarly, floating multiply by two becomes FSC (floating scale).
** Division is OK, unlike the integer case.
** This can only work for single-precision floats (not doubles).
** Otherwise we fall through to the other floating code.
*/
case P_IMUL:
if (optobj && typ == PTV_IMMED && (p->Pvalue & (p->Pvalue - 1)) == 0)
{
p->Pvalue = binexp (p->Pvalue); /* get # of bits to shift */
p->Pop = P_ASH; /* and opcode */
typ = PTA_RCONST; /* and code type */
}
break;
case P_FMPR: /* If doing single-precision mult or div */
case P_FDVR:
if (optobj
&& typ == PTA_FCONST /* If operand is a float constant */
&& (i = fltpow2 (p->Pfloat)) != 0) /* and is a power of 2 */
{
p->Pvalue = (p->Pop == P_FDVR) ? -i : i;
p->Pop = P_FSC;
typ = PTA_RCONST;
break;
}
/*
** Optimize moves of constants to use immediate form if possible.
** A check for "float" constants, which can be
** MOVSI'd into the LH if their RH is zero, is made in outinstr ().
** "double" constants
** normally do not come here as they use DMOVx, but it is possible
** as part of a code sequence that zeros the second AC separately.
*/
case P_MOVN:
if (!optobj || typ != PTV_IMMED || (p->Pvalue &~ 0777777L) == 0)
break;
p->Pop = P_MOVE; /* re-invert P_MOVN */
p->Pvalue = - p->Pvalue; /* for fixup into P_MOVSI */
case P_MOVE:
if (!optobj || typ != PTV_IMMED)
break;
if (p->Pvalue && (p->Pvalue & 0777777L) == 0)
{
p->Pop = P_MOVS; /* MOVEI of left half quantity */
p->Pvalue = ((unsigned INT) p->Pvalue) >> 18; /* becomes MOVSI */
}
break;
default:
; /* do nothing */
}
/* End of moby switch on instruction opcode! */
typ |= p->Ptype & PTF_SKIPPED;
p->Ptype = typ; /* Put back type in case it was changed. */
outinstr (p); /* Output the instruction! */
}
/* OUTINSTR (p) - Output instruction.
**
** We give a skipped-over op an extra space of indentation, to make it look
** more like human code (big deal) and to make debugging KCC easier.
*/
static void
outinstr (PCODE *p)
{
int big, i, opr;
outtab (); /* Start instruction output w/ tab */
if (p->Ptype & PTF_SKIPPED) /* Indent skipped-over instr */
outc (' ');
switch (p->Ptype & PTF_ADRMODE)
{
case PTA_ONEREG:
if (p->Pop == P_MUUO)
{
outstr (p->p_im.mnemonic);
if (p->p_im.p_chnl != -1) /* KAR-6/91, chgd sentinal to -1 */
{
outtab (); /* KAR-2/92, chgd outreg () to outnum () call */
outnum (p->p_im.p_chnl);
outc (',');
}
else
outreg (p->Preg);
}
else
{
outop (p->Pop);
outreg (p->Preg);
}
break;
case PTA_REGIS:
outop (p->Pop);
outreg (p->Preg);
if (mlist && Register_Preserve (p->Pr2))
outid (Reg_Id[p->Pr2 -
(r_maxnopreserve + 1)]->Sname); /* FW 2A (47) */
else if (mlist && p->Pr2 == R_SP)
outid ("SP");
else
outnum (p->Pr2);
break;
/* PTA_MINDEXED operands require special hackery when the operand is
** being used in an immediate fashion, i.e. the address value itself
** is needed. This can happen either if the operand is marked
** immediate (PTF_IMM set) or if the opcode is one that treats E as
** an immediate operand (only CAIx and TRx are checked).
*/
case PTA_MINDEXED:
if (p->Pop == P_NULPTR)
{
outnpd (p);
break;
}
if (! (p->Ptype & PTF_IMM))
{
/* Not using immediate address, only need to check for
** instrs that are always immediate.
*/
if ((i = directop (p->Pop)) != 0) /* Convert CAIx to CAMx? */
{
p->Pop = i; /* Ugh, convert to direct form */
/* Fall past to do MOVEI */
}
else
{
/* Normal operand, easy! */
if (p->Pop == P_MUUO)
{
outstr (p->p_im.mnemonic);
/* KAR-6/91, changed sentinal value to -1 from 0 */
if (p->p_im.p_chnl != -1)
outreg (p->p_im.p_chnl);
else
outreg (p->Preg);
}
else
{
outop (p->Pop);
outreg (p->Preg);
}
outaddress (p); /* and address */
break; /* Done! */
}
}
/* Instruction is using an immediate address.
*/
if (p->Pop == P_MOVE || p->Pop == P_MOVEI)
{
outop (P_MOVEI);
outreg (p->Preg); /* Use given reg */
outaddress (p); /* and address */
break;
}
/* Instruction needs an MOVEI preceding it. Barf if we
** generated a skip over this instr in the mistaken belief it
** only occupied 1 word, which means that oneinstr () was either
** wrong or wasn't called. In particular, this should never happen
** for the second of a cascaded skip.
*/
if (p->Ptype & PTF_SKIPPED)
int_error ("outinstr: MOVEI skipped");
outop (P_MOVEI);
outreg (R_SCRREG); /* simulate by MOVEI into scratch */
outaddress (p); /* of desired address */
outnl ();
outtab ();
outop (p->Pop); /* followed by real instruction */
outreg (p->Preg); /* into desired reg */
outnum (R_SCRREG); /* from scratch reg */
break;
/* PTA_PCONST requires some special checks for immediate pointer
** operands, which are distinguished by the lack of a word address
** symbol (Pptr is NULL).
** These should only be seen for TDZ, TDO, and IOR.
** TDZ uses them to mask out the P+S fields of a ptr (converting to wd ptr)
** IOR uses them to generate a byte ptr from a word ptr.
** TDOx is sometimes seen due to conversion from an IOR.
** Not clear if anything else can ever have a null word address.
*/
case PTA_PCONST:
i = 0;
if (p->Pptr == NULL)
switch (p->Pop & POF_OPCODE)
{
case P_TDZ:
i = P_TDZ ^ P_TLZ;
break; /* Change TDZ to TLZ */
case P_TDO:
i = P_TDO ^ P_TLO;
break; /* Change TDO to TLO */
#if 0
case P_IOR:
i = P_IOR ^ P_TLO;
break; /* Change IOR to TLO */
#else
case P_IOR:
i = P_IOR ^ P_HRLI;
break; /* Change IOR to HRLI */
#endif
default:
; /* do nothing */
}
if (i) /* If can use immediate form, */
{
outop (p->Pop ^ i); /* change opcode and output it! */
outreg (p->Preg);
outpti ((int) p->Pbsize, p->Poffset); /* Output immediate P+S sym */
}
else
{
outop (p->Pop); /* Normal full-word pointer constant */
outreg (p->Preg);
outc ('[');
outptr (p->Pptr, (int) p->Pbsize, p->Poffset);
outc (']');
}
break;
case PTA_BYTEPOINT:
outop (p->Pop);
outreg (p->Preg);
outc ('[');
outnum (p->Pbsize); /* Output P+S field */
outstr (",,");
outaddress (p); /* Now add right half addr+offset (index) */
outc (']');
break;
case PTA_RCONST:
outop (opr = p->Pop);
big = 1; /* Assume full-word operand */
if ((popflg[opr&POF_OPCODE]&PF_EIMM) /* If op takes E as immed op */
|| ! (p->Ptype & PTF_IMM)) /* or optype not marked "imm"*/
big = 0; /* then just output OP R,val */
else
{
/* Op is not an E-immediate type, but is marked as "immediate".
** So either we generate an opI form,
** or make the operand into a memory literal as in OP R,[val].
*/
if ((popflg[opr&POF_OPCODE] & PF_OPI) /* If op can be opI,*/
&& (p->Pvalue &~ 0777777L) == 0) /* and operand has zero LH, */
{
outc ('I'); /* make immediate op! */
big = 0; /* and say small operand */
}
}
outreg (p->Preg); /* Output reg and comma */
if (big)
{
outc ('[');
outnum (p->Pvalue);
outc (']');
}
else
outnum (p->Pvalue);
break;
case PTA_FCONST:
switch (p->Pop & POF_OPCODE)
{
case P_FADR:
case P_FSBR: /* Single-precision operations */
case P_FMPR:
case P_FDVR: /* can be optimized sometimes */
case P_MOVS:
big = bigfloat (p); /* Set flag 0 if small */
break;
case P_MOVE: /* Check for quick setup of float */
if ((big = bigfloat (p)) == 0)
p->Pop = P_MOVS; /* MOVE R,[flt] into MOVSI R, (flt) */
break;
default: /* Always big otherwise */
big = 1;
break;
}
outop (p->Pop);
if (!big)
outc ('I');
outreg (p->Preg);
outc (big ? '[' : ' (');
#ifdef __COMPILER_KCC__ /* native mode */
outnum (* (INT *) (&p->Pfloat)); /* Pass float as INT val */
#else /* host may not be PDP-10! */
if (big)
outmpdbl ((INT *) &p->Pdouble, 3);
else
outmpdbl ((INT *) &p->Pfloat, 1);
#endif
outc (big ? ']' : ')');
/* Add a readable comment so humans can understand what the floating
* constant is. However, because this is slow, only do it when
* we know the assembler output is going to stay around.
*/
if (!delete) /* If keeping asm file around, */
fprintf (out,"\t; %.20g",p->Pfloat); /* add comment for humans */
break;
case PTA_DCONST1: /* Double-word floating constant */
case PTA_DCONST2:
i = ((p->Ptype)&PTF_ADRMODE) == PTA_DCONST1 ? 1 : 2;
outop (p->Pop);
outreg (p->Preg);
outc ('[');
if (tgmachuse.mapdbl) /* If target mach fmt is different */
outmpdbl ((INT *)&p->Pdouble, i);/* Output part of mapped double */
else
outnum (i == 1 ? p->Pdouble1 : p->Pdouble2);
outc (']');
if (!delete) /* If keeping asm file around, */
fprintf (out,"\t; %.20g", p->Pdouble); /* add comment for humans */
break;
case PTA_DCONST: /* Double-word floating constant */
outop (p->Pop);
outreg (p->Preg);
if (p->Pdouble == 0) /* Hack: if a double zero, */
{
outstr (crtsnam[CRT_ZERO]); /* Use universal constant location. */
crtref[CRT_ZERO]++;
}
else if (tgmachuse.mapdbl) /* If target mach fmt is different */
{
outc ('[');
outmpdbl ((INT *) &p->Pdouble, 3); /* Output mapped double */
outc (']');
}
else
{
outc ('[');
outnum (p->Pdouble1);
outnl ();
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -