Pristine Ack-5.5
[Ack-5.5.git] / mach / arm / as / mach5.c
1 /* $Id: mach5.c, v3.3 25-Apr-89 AJM */
2
3 branch(brtyp, link, val)
4 word_t brtyp;
5 word_t link;
6 valu_t val;
7 {
8         valu_t offset;
9
10         offset = val - DOTVAL - 8;              /* Allow for pipeline */
11         if ((offset & 0xFC000000) != 0 && (offset & 0xFC000000) != 0xFC000000){
12                 serror("offset out of range");
13         }
14         offset = offset>>2 & 0xFFFFFF; 
15         emit4(brtyp|link|offset);
16         return;
17 }
18
19 data(opc, ins, val, typ)
20 long opc, ins;
21 valu_t val;
22 short typ;
23 {
24         valu_t tmpval;
25         int adrflag = 0;
26
27         if (typ == S_REG){      /* The argument is a register */
28                 emit4(opc|ins|val);
29                 return;
30         }
31
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. */
34
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 */
38         }
39
40 /* No optimisation, so carry on ... */
41
42         ins |= 0x02000000;      /* The argument is an immediate value */
43         tmpval = val;
44         if (opc == 0xff){               /* This is an ADR */
45                 adrflag = 1;
46                 opc = MOV;
47         }
48
49         if (typ == S_ABS){      /* An absolute value */
50                 if (calcimm(&opc, &tmpval, typ)){
51                         emit4(opc|ins|tmpval);
52                         return;
53                 }
54         }
55
56         tmpval = val;
57         if (!adrflag){          /* Don't do this for ADRs */
58                 if (oursmall(calcimm(&opc, &tmpval, typ), 12)){
59                         emit4(opc|ins|tmpval);
60                         return;
61                 }       
62         }
63
64         if (opc == MOV || opc == MVN || opc == ADD || opc == SUB){
65                 if (!bflag && pass == PASS_3){          /* Debugging info */
66                         /* warning("MOV/ADD extension"); */
67                         /* if (dflag)
68                                 printf("value: %lx\n", val);*/
69                 }
70                 if (oursmall((val & 0xFFFF0000) == 0, 8)){
71                         putaddr(opc, ins, val, 2);
72                         return;
73                 }
74                 if (oursmall((val & 0xFF000000) == 0, 4)){
75                         putaddr(opc, ins, val, 3);
76                         return;
77                 }
78                 putaddr(opc, ins, val, 4);
79                 return;
80         }
81         
82         if (pass == PASS_1)
83                 DOTVAL += 16;   /* Worst case we can emit */
84         else
85                 serror("immediate value out of range");
86         return;
87 }
88
89
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
93    all. */
94
95 calcimm(opc,val,typ)
96 word_t *opc;
97 valu_t *val;
98 short typ;
99 {
100         int i = 0;
101
102         if (typ == S_UND)
103                 return(0);      /* Can't do anything with an undefined label */
104
105         if ((*val & 0xFFFFFF00) == 0)   /* Value is positive, but < 256, */
106                 return(1);              /* so doesn't need a shift */
107
108         if ((~*val & 0xFFFFFF00) == 0){ /* Value is negative, but < 256, */
109                 if (*opc == AND)        /* so no shift required, only */
110                         {               /* inversion */
111                         *val = ~*val;
112                         *opc = BIC;
113                         return(1);
114                         }
115                 if (*opc == MOV)
116                         {
117                         *val = ~*val;
118                         *opc = MVN;
119                         return(1);
120                         }
121                 if (*opc == ADC)
122                         {
123                         *val = ~*val;
124                         *opc = SBC;
125                         return(1);
126                         }
127
128         }       
129         if ((-1**val & 0xFFFFFF00) == 0){ /* Same idea ... */
130                 if (*opc == ADD)
131                         {
132                         *val *= -1;
133                         *opc = SUB;
134                         return(1);
135                         }
136                 if (*opc == CMP)
137                         {
138                         *val *= -1;
139                         *opc = CMN;
140                         return(1);
141                         }
142         }
143
144         do{                                     /* Now we need to shift */
145                 rotateleft2(&*val);             /* Rotate left by two bits */
146                 i++;
147                 if((*val & 0xFFFFFF00) == 0){   /* Got a value < 256 */
148                         *val = *val|i<<8;       /* OR in the shift */
149                         return(1);
150                 }
151                 if ((~*val & 0xFFFFFF00) == 0){ /* If negative, carry out */
152                         if (*opc == AND)        /* inversion as before */
153                                 {
154                                 *val = ~*val|i<<8;
155                                 *opc = BIC;
156                                 return(1);
157                                 }
158                         if (*opc == MOV)
159                                 {
160                                 *val = ~*val|i<<8;
161                                 *opc = MVN;
162                                 return(1);
163                                 }
164                         if (*opc == ADC)
165                                 {
166                                 *val = ~*val|i<<8;
167                                 *opc = SBC;
168                                 return(1);
169                                 }
170                 }       
171         }while(i<15);
172
173         return(0);      /* Failed if can't encode it after 16 rotates */
174 }
175
176
177 /* Calculate an offset in an address */
178
179 word_t
180 calcoffset(val)
181 valu_t val;
182 {
183         if((val & 0xFFFFF000) == 0)
184                 return(val|0x00800000);
185         val *= -1;
186         if((val & 0xFFFFF000) == 0)
187                 return(val);
188         serror("offset out of range");
189         return(0);
190 }
191
192
193 /* This routine deals with STR and LDR instructions */
194
195 strldr(opc, ins, val)
196 long opc, ins;
197 valu_t val;
198 {
199
200         long reg, reg2; /* The registers we are using */
201         long tmpval;
202
203 /* If the expression was a register, then just output it and save 24
204    bytes */
205
206         if (success){ 
207                 emit4(opc|ins|val);
208                 return;
209         }
210
211         reg = ins & 0x0000F000;         /* Extract register from instruction */
212
213         if (opc == LDR){
214
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 */
218                         return;
219                 }
220
221                 tmpval *= -1;
222                 if (oursmall((tmpval & 0xFFFFF000) == 0, 16)){  /* If it's -ve */
223                         emit4(opc|ins|tmpval|0x010F0000);       /* PC rel, no up bit */
224                         return;
225                 }
226
227                 if (!bflag && pass == PASS_3){  /* Debugging info */
228                         /* warning("LDR address extension"); */
229                         if (dflag)
230                                 printf("value: %lx\n", val);
231                 }
232
233                 opc = 0x03A00000;       /* Set opc for putaddr */
234
235                 if (oursmall((val & 0xFFFF0000) == 0, 8)){
236                         putaddr(opc, ins & 0xFFBFFFFF, val, 2);
237                         emit4(0x05100000|ins|reg<<4);
238                         return;
239                 }
240                 if (oursmall((val & 0xFF000000) == 0, 4)){
241                         putaddr(opc, ins & 0xFFBFFFFF, val, 3);
242                         emit4(0x05100000|ins|reg<<4);
243                         return;
244                 }
245                 putaddr(opc, ins & 0xFFBFFFFF, val, 4);
246                 emit4(0x05100000|ins|reg<<4);
247                 return;
248         }
249
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.  */
255
256         if (opc == STR){
257                 reg2 = reg >> 12;           /* Use R6 as the second register, */
258                 reg2 = (reg2 == 6 ? 0 : 6); /* or R0 if we can't */
259
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 */
263                         return;
264                 }
265
266                 tmpval *= -1;
267                 if (oursmall((tmpval & 0xFFFFF000) == 0, 24)){  /* If it's -ve */
268                         emit4(opc|ins|tmpval|0x010F0000);       /* PC rel, no up bit */
269                         return;
270                 }
271
272                 if (!bflag && pass == PASS_3){  /* Debugging info */
273                         /* warning("STR address extension"); */
274                         if (dflag)
275                                 printf("value: %lx\n", val);
276                 }
277
278                 opc = 0x03A00000;       /* Set opc for putaddr */
279
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);
285                         return;
286                 }
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);
292                         return;
293                 }
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);      
298                 return;
299         }
300
301 }
302
303
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 ! */
311
312
313 calcadr(ins, reg, val, typ)
314 word_t ins, reg;
315 valu_t val;
316 short typ;
317 {
318         valu_t tmpval = val;
319         word_t opc = 0xff;      /* Dummy opc used as a flag for data() */
320
321 /* First check that the address is in range */
322
323         if (val < 0) 
324                 tmpval = ~tmpval; /* Invert negative addresses for check */
325
326         if ((tmpval & 0xFC000000) && (typ != S_UND)){
327                 serror("adr address out of range");
328                 return;
329         }
330
331 /* Can't do it PC relative, so use an absolute MOV instead */
332
333         data (opc, ins|reg<<12, val, typ);
334         return; 
335
336 }
337
338
339 word_t
340 calcshft(val, typ, styp)
341 valu_t val;
342 short typ;
343 word_t styp;
344 {
345         if (typ == S_UND) 
346                 return(0);
347
348         if (val & 0xFFFFFFE0) 
349                 serror("shiftcount out of range");
350
351         if (styp && !val) 
352                 warning("shiftcount 0");
353
354         return((val & 0x1F)<<7);
355 }
356
357 rotateleft2(x)
358 long *x;
359 {
360         unsigned long bits;
361
362         bits = *x & 0xC0000000;
363         *x <<= 2 ;
364         if (bits){
365                 bits >>= 30;
366                 *x |= bits;
367         }
368         return;
369 }
370
371
372 /* 
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.
381 */
382
383 putaddr(opc, ins, val, count)
384 long opc, ins, val;                     
385 int count;
386 {
387         long tmpval = val;
388         long reg = ins & 0x0000F000;
389
390         emit4(opc|ins|(val & 0x000000FF));
391
392         tmpval = (val & 0x0000FF00) >> 8 | 0x00000C00;
393
394 /* Decide what to use for the additional instructions */
395
396         if (opc == 0x03a00000)          /* This one is for strldr */
397                 opc = 0x02800000;
398
399         if (opc == MOV)
400                 opc = ADD;
401
402         if (opc == MVN)
403                 opc = SUB;
404
405         if ((tmpval & 0x000000FF) != 0)
406                 emit4(opc|ins|reg<<4|tmpval);
407         else
408                 emit4(0xF0000000);      /* No-op if a zero argument */
409
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);
414                 }
415                 else 
416                         emit4(0xF0000000);              /* No-op */
417         }
418
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);
423                 }
424                 else 
425                         emit4(0xF0000000);              /* No-op */
426         }
427
428         return;
429 }
430
431
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 
445           careful here .....
446  */
447
448 #define PBITTABSZ       128
449 static char *pbittab[PBITTABSZ];
450
451 oursmall(fitsmall, gain)
452 {
453         register bit;
454         register char *p;
455
456         if (DOTSCT == NULL)
457                 nosect();
458         if (bflag)
459                 return(0);
460         if (nbits == BITCHUNK) {
461                 bitindex++;
462                 nbits = 0;
463                 if (bitindex == PBITTABSZ) {
464                         static int w_given;
465                         if (pass == PASS_1 && ! w_given) {
466                                 w_given = 1;
467                                 warning("bit table overflow");
468                         }
469                         return(0);
470                 }
471                 if (pbittab[bitindex] == 0 && pass == PASS_1) {
472                         if ((pbittab[bitindex] = malloc(MEMINCR)) == 0) {
473                                 static int w2_given;
474
475                                 if (!w2_given) {
476                                         w2_given = 1;
477                                         warning("out of space for bit table");
478                                 }
479                         }
480                 }
481                 if (pbittab[bitindex] == 0)
482                         return (0);
483         }
484         bit = 1 << (nbits&7);
485         p = pbittab[bitindex]+(nbits>>3);
486         nbits++;
487
488         switch (pass) {
489         case PASS_1:
490                 *p = 0;
491                 return(0);
492         case PASS_2:
493                 if (fitsmall) {
494                         DOTGAIN += gain;
495                         *p |= bit;
496                 }
497                 return(fitsmall);
498         case PASS_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));
502                         if (fitsmall) 
503                                 return(0);
504                         else
505                                 serror("This one is fatal!");
506                 }
507                 return(*p & bit);
508         }
509         /*NOTREACHED*/
510 }