123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510 |
- /* $Id: mach5.c, v3.3 25-Apr-89 AJM */
- branch(brtyp, link, val)
- word_t brtyp;
- word_t link;
- valu_t val;
- {
- valu_t offset;
- offset = val - DOTVAL - 8; /* Allow for pipeline */
- if ((offset & 0xFC000000) != 0 && (offset & 0xFC000000) != 0xFC000000){
- serror("offset out of range");
- }
- offset = offset>>2 & 0xFFFFFF;
- emit4(brtyp|link|offset);
- return;
- }
- data(opc, ins, val, typ)
- long opc, ins;
- valu_t val;
- short typ;
- {
- valu_t tmpval;
- int adrflag = 0;
- if (typ == S_REG){ /* The argument is a register */
- emit4(opc|ins|val);
- return;
- }
- /* Do a bit of optimisation here, since the backend might produce instructions
- of the type MOV R0, R0, #0. We can ignore these. */
- if (((opc == ADD) || (opc == SUB)) && (val == 0)){ /* ADD or SUB 0 ? */
- if ((ins & 0x000F0000) == ((ins & 0x0000F000) << 4)) /* Same reg ? */
- return; /* Don't emit anything */
- }
- /* No optimisation, so carry on ... */
- ins |= 0x02000000; /* The argument is an immediate value */
- tmpval = val;
- if (opc == 0xff){ /* This is an ADR */
- adrflag = 1;
- opc = MOV;
- }
- if (typ == S_ABS){ /* An absolute value */
- if (calcimm(&opc, &tmpval, typ)){
- emit4(opc|ins|tmpval);
- return;
- }
- }
- tmpval = val;
- if (!adrflag){ /* Don't do this for ADRs */
- if (oursmall(calcimm(&opc, &tmpval, typ), 12)){
- emit4(opc|ins|tmpval);
- return;
- }
- }
- if (opc == MOV || opc == MVN || opc == ADD || opc == SUB){
- if (!bflag && pass == PASS_3){ /* Debugging info */
- /* warning("MOV/ADD extension"); */
- /* if (dflag)
- printf("value: %lx\n", val);*/
- }
- if (oursmall((val & 0xFFFF0000) == 0, 8)){
- putaddr(opc, ins, val, 2);
- return;
- }
- if (oursmall((val & 0xFF000000) == 0, 4)){
- putaddr(opc, ins, val, 3);
- return;
- }
- putaddr(opc, ins, val, 4);
- return;
- }
-
- if (pass == PASS_1)
- DOTVAL += 16; /* Worst case we can emit */
- else
- serror("immediate value out of range");
- return;
- }
- /* Calculate an immediate value. This is not as easy as it sounds, because
- the ARM uses an 8-bit value and 4-bit shift to encode the value into a
- 12-bit field. Unfortunately this means that some numbers may not fit at
- all. */
- calcimm(opc,val,typ)
- word_t *opc;
- valu_t *val;
- short typ;
- {
- int i = 0;
- if (typ == S_UND)
- return(0); /* Can't do anything with an undefined label */
- if ((*val & 0xFFFFFF00) == 0) /* Value is positive, but < 256, */
- return(1); /* so doesn't need a shift */
- if ((~*val & 0xFFFFFF00) == 0){ /* Value is negative, but < 256, */
- if (*opc == AND) /* so no shift required, only */
- { /* inversion */
- *val = ~*val;
- *opc = BIC;
- return(1);
- }
- if (*opc == MOV)
- {
- *val = ~*val;
- *opc = MVN;
- return(1);
- }
- if (*opc == ADC)
- {
- *val = ~*val;
- *opc = SBC;
- return(1);
- }
- }
- if ((-1**val & 0xFFFFFF00) == 0){ /* Same idea ... */
- if (*opc == ADD)
- {
- *val *= -1;
- *opc = SUB;
- return(1);
- }
- if (*opc == CMP)
- {
- *val *= -1;
- *opc = CMN;
- return(1);
- }
- }
- do{ /* Now we need to shift */
- rotateleft2(&*val); /* Rotate left by two bits */
- i++;
- if((*val & 0xFFFFFF00) == 0){ /* Got a value < 256 */
- *val = *val|i<<8; /* OR in the shift */
- return(1);
- }
- if ((~*val & 0xFFFFFF00) == 0){ /* If negative, carry out */
- if (*opc == AND) /* inversion as before */
- {
- *val = ~*val|i<<8;
- *opc = BIC;
- return(1);
- }
- if (*opc == MOV)
- {
- *val = ~*val|i<<8;
- *opc = MVN;
- return(1);
- }
- if (*opc == ADC)
- {
- *val = ~*val|i<<8;
- *opc = SBC;
- return(1);
- }
- }
- }while(i<15);
- return(0); /* Failed if can't encode it after 16 rotates */
- }
- /* Calculate an offset in an address */
- word_t
- calcoffset(val)
- valu_t val;
- {
- if((val & 0xFFFFF000) == 0)
- return(val|0x00800000);
- val *= -1;
- if((val & 0xFFFFF000) == 0)
- return(val);
- serror("offset out of range");
- return(0);
- }
- /* This routine deals with STR and LDR instructions */
- strldr(opc, ins, val)
- long opc, ins;
- valu_t val;
- {
- long reg, reg2; /* The registers we are using */
- long tmpval;
- /* If the expression was a register, then just output it and save 24
- bytes */
- if (success){
- emit4(opc|ins|val);
- return;
- }
- reg = ins & 0x0000F000; /* Extract register from instruction */
- if (opc == LDR){
- tmpval = val - DOTVAL - 8;
- if (oursmall((tmpval & 0xFFFFF000) == 0, 16)){ /* If it's +ve */
- emit4(opc|ins|tmpval|0x018F0000); /* PC rel, up bit */
- return;
- }
- tmpval *= -1;
- if (oursmall((tmpval & 0xFFFFF000) == 0, 16)){ /* If it's -ve */
- emit4(opc|ins|tmpval|0x010F0000); /* PC rel, no up bit */
- return;
- }
- if (!bflag && pass == PASS_3){ /* Debugging info */
- /* warning("LDR address extension"); */
- if (dflag)
- printf("value: %lx\n", val);
- }
- opc = 0x03A00000; /* Set opc for putaddr */
- if (oursmall((val & 0xFFFF0000) == 0, 8)){
- putaddr(opc, ins & 0xFFBFFFFF, val, 2);
- emit4(0x05100000|ins|reg<<4);
- return;
- }
- if (oursmall((val & 0xFF000000) == 0, 4)){
- putaddr(opc, ins & 0xFFBFFFFF, val, 3);
- emit4(0x05100000|ins|reg<<4);
- return;
- }
- putaddr(opc, ins & 0xFFBFFFFF, val, 4);
- emit4(0x05100000|ins|reg<<4);
- return;
- }
- /* If the failure was an STR instruction, things are a bit more complicated as
- we can't overwrite the register before we store its value. We therefore
- need to use another register as well, which must be saved and restored.
- This register is saved on a stack pointed to by R12. Apart from this
- complication, the scheme is similar to the LDR above. */
- if (opc == STR){
- reg2 = reg >> 12; /* Use R6 as the second register, */
- reg2 = (reg2 == 6 ? 0 : 6); /* or R0 if we can't */
- tmpval = val - DOTVAL - 8;
- if (oursmall((tmpval & 0xFFFFF000) == 0, 24)){ /* If it's +ve */
- emit4(opc|ins|tmpval|0x018F0000); /* PC rel, up bit */
- return;
- }
- tmpval *= -1;
- if (oursmall((tmpval & 0xFFFFF000) == 0, 24)){ /* If it's -ve */
- emit4(opc|ins|tmpval|0x010F0000); /* PC rel, no up bit */
- return;
- }
- if (!bflag && pass == PASS_3){ /* Debugging info */
- /* warning("STR address extension"); */
- if (dflag)
- printf("value: %lx\n", val);
- }
- opc = 0x03A00000; /* Set opc for putaddr */
- if (oursmall((val & 0xFFFF0000) == 0, 8)){
- emit4(0xE92C0000|1<<reg2);
- putaddr(opc, (ins & 0xFFBF0FFF)|reg2<<12, val, 2);
- emit4(0x05000000|ins|reg2<<16);
- emit4(0xE8BC0000|1<<reg2);
- return;
- }
- if (oursmall((val & 0xFF000000) == 0, 4)){
- emit4(0xE92C0000|1<<reg2);
- putaddr(opc, (ins & 0xFFBF0FFF)|reg2<<12, val, 3);
- emit4(0x05000000|ins|reg2<<16);
- emit4(0xE8BC0000|1<<reg2);
- return;
- }
- emit4(0xE92C0000|1<<reg2);
- putaddr(opc, (ins & 0xFFBF0FFF)|reg2<<12, val, 4);
- emit4(0x05000000|ins|reg2<<16);
- emit4(0xE8BC0000|1<<reg2);
- return;
- }
- }
- /* This routine deals with ADR instructions. The ARM does not have a
- 'calculate effective address' instruction, so we use ADD, SUB, MOV or
- MVN instead. ADR is not a genuine instruction, but is provided to make
- life easier. At present these are all calculated by using a MOV and
- successive ADDs. Even if the address will fit into a single MOV, we
- still use two instructions; the second is a no-op. This is to cure the
- optimisation problem with mobile addresses ! */
- calcadr(ins, reg, val, typ)
- word_t ins, reg;
- valu_t val;
- short typ;
- {
- valu_t tmpval = val;
- word_t opc = 0xff; /* Dummy opc used as a flag for data() */
- /* First check that the address is in range */
- if (val < 0)
- tmpval = ~tmpval; /* Invert negative addresses for check */
- if ((tmpval & 0xFC000000) && (typ != S_UND)){
- serror("adr address out of range");
- return;
- }
- /* Can't do it PC relative, so use an absolute MOV instead */
- data (opc, ins|reg<<12, val, typ);
- return;
- }
- word_t
- calcshft(val, typ, styp)
- valu_t val;
- short typ;
- word_t styp;
- {
- if (typ == S_UND)
- return(0);
- if (val & 0xFFFFFFE0)
- serror("shiftcount out of range");
- if (styp && !val)
- warning("shiftcount 0");
- return((val & 0x1F)<<7);
- }
- rotateleft2(x)
- long *x;
- {
- unsigned long bits;
- bits = *x & 0xC0000000;
- *x <<= 2 ;
- if (bits){
- bits >>= 30;
- *x |= bits;
- }
- return;
- }
- /*
- This routine overcomes the 12-bit encoding problem by outputting a number
- a byte at a time. For a MOV, it first uses a MOV, then successive ADDs.
- It will not use any more ADDs than needed to completely output the number.
- A similar approach is used for ADDs and SUBs.
- There is a problem here with optimisation in the third pass; if the
- instruction needed two ADDs in the second pass, but only one in the third
- pass, then the second ADD is replaced with a no-op. We cannot emit one
- less instruction, because that will upset other addresses.
- */
- putaddr(opc, ins, val, count)
- long opc, ins, val;
- int count;
- {
- long tmpval = val;
- long reg = ins & 0x0000F000;
- emit4(opc|ins|(val & 0x000000FF));
- tmpval = (val & 0x0000FF00) >> 8 | 0x00000C00;
- /* Decide what to use for the additional instructions */
- if (opc == 0x03a00000) /* This one is for strldr */
- opc = 0x02800000;
- if (opc == MOV)
- opc = ADD;
- if (opc == MVN)
- opc = SUB;
- if ((tmpval & 0x000000FF) != 0)
- emit4(opc|ins|reg<<4|tmpval);
- else
- emit4(0xF0000000); /* No-op if a zero argument */
- if (count == 3 || count == 4){ /* Must use three or more instructions */
- if ((val & 0xFFFF0000) != 0){
- tmpval = (val & 0x00FF0000) >> 16 | 0x00000800;
- emit4(opc|ins|reg<<4|tmpval);
- }
- else
- emit4(0xF0000000); /* No-op */
- }
- if (count == 4){ /* Must use four instructions */
- if ((val & 0xFF000000) != 0){
- tmpval = (val & 0xFF000000) >> 24 | 0x00000400;
- emit4(opc|ins|reg<<4|tmpval);
- }
- else
- emit4(0xF0000000); /* No-op */
- }
- return;
- }
- /* The following piece of code is stolen from comm7.c; it needs some minor
- fixes for the ARM, so it is included here rather than altering the existing
- code. It maintains a bit table to say whether or not an optimisation is
- possible. The original had some problems:
- (a). It assumed that the memory returned by malloc() was cleared to zero.
- This is true on a Sun, but not under Minix; small() should really
- use calloc() instead.
- (b). It assumed that if an optimisation was possible in pass 2, it must
- also be possible in pass 3, and produced an assertion error if it
- wasn't. This is OK for optimising things like long or short branch
- instructions on a 68000, but not for ADRs on the ARM. A previous
- optimisation may place an address out of 12-bit encoding range on
- pass 3, when it was in range on pass 2. However we have to be
- careful here .....
- */
- #define PBITTABSZ 128
- static char *pbittab[PBITTABSZ];
- oursmall(fitsmall, gain)
- {
- register bit;
- register char *p;
- if (DOTSCT == NULL)
- nosect();
- if (bflag)
- return(0);
- if (nbits == BITCHUNK) {
- bitindex++;
- nbits = 0;
- if (bitindex == PBITTABSZ) {
- static int w_given;
- if (pass == PASS_1 && ! w_given) {
- w_given = 1;
- warning("bit table overflow");
- }
- return(0);
- }
- if (pbittab[bitindex] == 0 && pass == PASS_1) {
- if ((pbittab[bitindex] = malloc(MEMINCR)) == 0) {
- static int w2_given;
- if (!w2_given) {
- w2_given = 1;
- warning("out of space for bit table");
- }
- }
- }
- if (pbittab[bitindex] == 0)
- return (0);
- }
- bit = 1 << (nbits&7);
- p = pbittab[bitindex]+(nbits>>3);
- nbits++;
- switch (pass) {
- case PASS_1:
- *p = 0;
- return(0);
- case PASS_2:
- if (fitsmall) {
- DOTGAIN += gain;
- *p |= bit;
- }
- return(fitsmall);
- case PASS_3:
- if (!(fitsmall || (*p & bit) == 0)){
- printf("line: %ld - small failed\n", lineno);
- printf("fitsmall: %d bit: %d\n", fitsmall, (*p & bit));
- if (fitsmall)
- return(0);
- else
- serror("This one is fatal!");
- }
- return(*p & bit);
- }
- /*NOTREACHED*/
- }
|