1 /* $Id: mach5.c, v3.3 25-Apr-89 AJM */
3 branch(brtyp, link, val)
10 offset = val - DOTVAL - 8; /* Allow for pipeline */
11 if ((offset & 0xFC000000) != 0 && (offset & 0xFC000000) != 0xFC000000){
12 serror("offset out of range");
14 offset = offset>>2 & 0xFFFFFF;
15 emit4(brtyp|link|offset);
19 data(opc, ins, val, typ)
27 if (typ == S_REG){ /* The argument is a register */
32 /* Do a bit of optimisation here, since the backend might produce instructions
33 of the type MOV R0, R0, #0. We can ignore these. */
35 if (((opc == ADD) || (opc == SUB)) && (val == 0)){ /* ADD or SUB 0 ? */
36 if ((ins & 0x000F0000) == ((ins & 0x0000F000) << 4)) /* Same reg ? */
37 return; /* Don't emit anything */
40 /* No optimisation, so carry on ... */
42 ins |= 0x02000000; /* The argument is an immediate value */
44 if (opc == 0xff){ /* This is an ADR */
49 if (typ == S_ABS){ /* An absolute value */
50 if (calcimm(&opc, &tmpval, typ)){
51 emit4(opc|ins|tmpval);
57 if (!adrflag){ /* Don't do this for ADRs */
58 if (oursmall(calcimm(&opc, &tmpval, typ), 12)){
59 emit4(opc|ins|tmpval);
64 if (opc == MOV || opc == MVN || opc == ADD || opc == SUB){
65 if (!bflag && pass == PASS_3){ /* Debugging info */
66 /* warning("MOV/ADD extension"); */
68 printf("value: %lx\n", val);*/
70 if (oursmall((val & 0xFFFF0000) == 0, 8)){
71 putaddr(opc, ins, val, 2);
74 if (oursmall((val & 0xFF000000) == 0, 4)){
75 putaddr(opc, ins, val, 3);
78 putaddr(opc, ins, val, 4);
83 DOTVAL += 16; /* Worst case we can emit */
85 serror("immediate value out of range");
90 /* Calculate an immediate value. This is not as easy as it sounds, because
91 the ARM uses an 8-bit value and 4-bit shift to encode the value into a
92 12-bit field. Unfortunately this means that some numbers may not fit at
103 return(0); /* Can't do anything with an undefined label */
105 if ((*val & 0xFFFFFF00) == 0) /* Value is positive, but < 256, */
106 return(1); /* so doesn't need a shift */
108 if ((~*val & 0xFFFFFF00) == 0){ /* Value is negative, but < 256, */
109 if (*opc == AND) /* so no shift required, only */
129 if ((-1**val & 0xFFFFFF00) == 0){ /* Same idea ... */
144 do{ /* Now we need to shift */
145 rotateleft2(&*val); /* Rotate left by two bits */
147 if((*val & 0xFFFFFF00) == 0){ /* Got a value < 256 */
148 *val = *val|i<<8; /* OR in the shift */
151 if ((~*val & 0xFFFFFF00) == 0){ /* If negative, carry out */
152 if (*opc == AND) /* inversion as before */
173 return(0); /* Failed if can't encode it after 16 rotates */
177 /* Calculate an offset in an address */
183 if((val & 0xFFFFF000) == 0)
184 return(val|0x00800000);
186 if((val & 0xFFFFF000) == 0)
188 serror("offset out of range");
193 /* This routine deals with STR and LDR instructions */
195 strldr(opc, ins, val)
200 long reg, reg2; /* The registers we are using */
203 /* If the expression was a register, then just output it and save 24
211 reg = ins & 0x0000F000; /* Extract register from instruction */
215 tmpval = val - DOTVAL - 8;
216 if (oursmall((tmpval & 0xFFFFF000) == 0, 16)){ /* If it's +ve */
217 emit4(opc|ins|tmpval|0x018F0000); /* PC rel, up bit */
222 if (oursmall((tmpval & 0xFFFFF000) == 0, 16)){ /* If it's -ve */
223 emit4(opc|ins|tmpval|0x010F0000); /* PC rel, no up bit */
227 if (!bflag && pass == PASS_3){ /* Debugging info */
228 /* warning("LDR address extension"); */
230 printf("value: %lx\n", val);
233 opc = 0x03A00000; /* Set opc for putaddr */
235 if (oursmall((val & 0xFFFF0000) == 0, 8)){
236 putaddr(opc, ins & 0xFFBFFFFF, val, 2);
237 emit4(0x05100000|ins|reg<<4);
240 if (oursmall((val & 0xFF000000) == 0, 4)){
241 putaddr(opc, ins & 0xFFBFFFFF, val, 3);
242 emit4(0x05100000|ins|reg<<4);
245 putaddr(opc, ins & 0xFFBFFFFF, val, 4);
246 emit4(0x05100000|ins|reg<<4);
250 /* If the failure was an STR instruction, things are a bit more complicated as
251 we can't overwrite the register before we store its value. We therefore
252 need to use another register as well, which must be saved and restored.
253 This register is saved on a stack pointed to by R12. Apart from this
254 complication, the scheme is similar to the LDR above. */
257 reg2 = reg >> 12; /* Use R6 as the second register, */
258 reg2 = (reg2 == 6 ? 0 : 6); /* or R0 if we can't */
260 tmpval = val - DOTVAL - 8;
261 if (oursmall((tmpval & 0xFFFFF000) == 0, 24)){ /* If it's +ve */
262 emit4(opc|ins|tmpval|0x018F0000); /* PC rel, up bit */
267 if (oursmall((tmpval & 0xFFFFF000) == 0, 24)){ /* If it's -ve */
268 emit4(opc|ins|tmpval|0x010F0000); /* PC rel, no up bit */
272 if (!bflag && pass == PASS_3){ /* Debugging info */
273 /* warning("STR address extension"); */
275 printf("value: %lx\n", val);
278 opc = 0x03A00000; /* Set opc for putaddr */
280 if (oursmall((val & 0xFFFF0000) == 0, 8)){
281 emit4(0xE92C0000|1<<reg2);
282 putaddr(opc, (ins & 0xFFBF0FFF)|reg2<<12, val, 2);
283 emit4(0x05000000|ins|reg2<<16);
284 emit4(0xE8BC0000|1<<reg2);
287 if (oursmall((val & 0xFF000000) == 0, 4)){
288 emit4(0xE92C0000|1<<reg2);
289 putaddr(opc, (ins & 0xFFBF0FFF)|reg2<<12, val, 3);
290 emit4(0x05000000|ins|reg2<<16);
291 emit4(0xE8BC0000|1<<reg2);
294 emit4(0xE92C0000|1<<reg2);
295 putaddr(opc, (ins & 0xFFBF0FFF)|reg2<<12, val, 4);
296 emit4(0x05000000|ins|reg2<<16);
297 emit4(0xE8BC0000|1<<reg2);
304 /* This routine deals with ADR instructions. The ARM does not have a
305 'calculate effective address' instruction, so we use ADD, SUB, MOV or
306 MVN instead. ADR is not a genuine instruction, but is provided to make
307 life easier. At present these are all calculated by using a MOV and
308 successive ADDs. Even if the address will fit into a single MOV, we
309 still use two instructions; the second is a no-op. This is to cure the
310 optimisation problem with mobile addresses ! */
313 calcadr(ins, reg, val, typ)
319 word_t opc = 0xff; /* Dummy opc used as a flag for data() */
321 /* First check that the address is in range */
324 tmpval = ~tmpval; /* Invert negative addresses for check */
326 if ((tmpval & 0xFC000000) && (typ != S_UND)){
327 serror("adr address out of range");
331 /* Can't do it PC relative, so use an absolute MOV instead */
333 data (opc, ins|reg<<12, val, typ);
340 calcshft(val, typ, styp)
348 if (val & 0xFFFFFFE0)
349 serror("shiftcount out of range");
352 warning("shiftcount 0");
354 return((val & 0x1F)<<7);
362 bits = *x & 0xC0000000;
373 This routine overcomes the 12-bit encoding problem by outputting a number
374 a byte at a time. For a MOV, it first uses a MOV, then successive ADDs.
375 It will not use any more ADDs than needed to completely output the number.
376 A similar approach is used for ADDs and SUBs.
377 There is a problem here with optimisation in the third pass; if the
378 instruction needed two ADDs in the second pass, but only one in the third
379 pass, then the second ADD is replaced with a no-op. We cannot emit one
380 less instruction, because that will upset other addresses.
383 putaddr(opc, ins, val, count)
388 long reg = ins & 0x0000F000;
390 emit4(opc|ins|(val & 0x000000FF));
392 tmpval = (val & 0x0000FF00) >> 8 | 0x00000C00;
394 /* Decide what to use for the additional instructions */
396 if (opc == 0x03a00000) /* This one is for strldr */
405 if ((tmpval & 0x000000FF) != 0)
406 emit4(opc|ins|reg<<4|tmpval);
408 emit4(0xF0000000); /* No-op if a zero argument */
410 if (count == 3 || count == 4){ /* Must use three or more instructions */
411 if ((val & 0xFFFF0000) != 0){
412 tmpval = (val & 0x00FF0000) >> 16 | 0x00000800;
413 emit4(opc|ins|reg<<4|tmpval);
416 emit4(0xF0000000); /* No-op */
419 if (count == 4){ /* Must use four instructions */
420 if ((val & 0xFF000000) != 0){
421 tmpval = (val & 0xFF000000) >> 24 | 0x00000400;
422 emit4(opc|ins|reg<<4|tmpval);
425 emit4(0xF0000000); /* No-op */
432 /* The following piece of code is stolen from comm7.c; it needs some minor
433 fixes for the ARM, so it is included here rather than altering the existing
434 code. It maintains a bit table to say whether or not an optimisation is
435 possible. The original had some problems:
436 (a). It assumed that the memory returned by malloc() was cleared to zero.
437 This is true on a Sun, but not under Minix; small() should really
438 use calloc() instead.
439 (b). It assumed that if an optimisation was possible in pass 2, it must
440 also be possible in pass 3, and produced an assertion error if it
441 wasn't. This is OK for optimising things like long or short branch
442 instructions on a 68000, but not for ADRs on the ARM. A previous
443 optimisation may place an address out of 12-bit encoding range on
444 pass 3, when it was in range on pass 2. However we have to be
448 #define PBITTABSZ 128
449 static char *pbittab[PBITTABSZ];
451 oursmall(fitsmall, gain)
460 if (nbits == BITCHUNK) {
463 if (bitindex == PBITTABSZ) {
465 if (pass == PASS_1 && ! w_given) {
467 warning("bit table overflow");
471 if (pbittab[bitindex] == 0 && pass == PASS_1) {
472 if ((pbittab[bitindex] = malloc(MEMINCR)) == 0) {
477 warning("out of space for bit table");
481 if (pbittab[bitindex] == 0)
484 bit = 1 << (nbits&7);
485 p = pbittab[bitindex]+(nbits>>3);
499 if (!(fitsmall || (*p & bit) == 0)){
500 printf("line: %ld - small failed\n", lineno);
501 printf("fitsmall: %d bit: %d\n", fitsmall, (*p & bit));
505 serror("This one is fatal!");