1 // Inferno utils/5l/noop.c
2 // http://code.google.com/p/inferno-os/source/browse/utils/5l/noop.c
3 //
4 // Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved.
5 // Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
6 // Portions Copyright © 1997-1999 Vita Nuova Limited
7 // Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
8 // Portions Copyright © 2004,2006 Bruce Ellis
9 // Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
10 // Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
11 // Portions Copyright © 2009 The Go Authors. All rights reserved.
12 //
13 // Permission is hereby granted, free of charge, to any person obtaining a copy
14 // of this software and associated documentation files (the "Software"), to deal
15 // in the Software without restriction, including without limitation the rights
16 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17 // copies of the Software, and to permit persons to whom the Software is
18 // furnished to do so, subject to the following conditions:
19 //
20 // The above copyright notice and this permission notice shall be included in
21 // all copies or substantial portions of the Software.
22 //
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
29 // THE SOFTWARE.
30
31 // Code transformations.
32
33 #include "l.h"
34 #include "../ld/lib.h"
35
36 // see ../../runtime/proc.c:/StackGuard
37 enum
38 {
39 StackBig = 4096,
40 StackSmall = 128,
41 };
42
43 static Sym* sym_div;
44 static Sym* sym_divu;
45 static Sym* sym_mod;
46 static Sym* sym_modu;
47
48 void
49 noops(void)
50 {
51 Prog *p, *q, *q1;
52 int o;
53 Prog *pmorestack;
54 Sym *symmorestack;
55
56 /*
57 * find leaf subroutines
58 * strip NOPs
59 * expand RET
60 * expand BECOME pseudo
61 */
62
63 if(debug['v'])
64 Bprint(&bso, "%5.2f noops\n", cputime());
65 Bflush(&bso);
66
67 symmorestack = lookup("runtime.morestack", 0);
68 if(symmorestack->type != STEXT) {
69 diag("runtime·morestack not defined");
70 errorexit();
71 }
72 pmorestack = symmorestack->text;
73 pmorestack->reg |= NOSPLIT;
74
75 q = P;
76 for(cursym = textp; cursym != nil; cursym = cursym->next) {
77 for(p = cursym->text; p != P; p = p->link) {
78 switch(p->as) {
79 case ATEXT:
80 p->mark |= LEAF;
81 break;
82
83 case ARET:
84 break;
85
86 case ADIV:
87 case ADIVU:
88 case AMOD:
89 case AMODU:
90 q = p;
91 if(prog_div == P)
92 initdiv();
93 cursym->text->mark &= ~LEAF;
94 continue;
95
96 case ANOP:
97 q1 = p->link;
98 q->link = q1; /* q is non-nop */
99 if(q1 != P)
100 q1->mark |= p->mark;
101 continue;
102
103 case ABL:
104 case ABX:
105 cursym->text->mark &= ~LEAF;
106
107 case ABCASE:
108 case AB:
109
110 case ABEQ:
111 case ABNE:
112 case ABCS:
113 case ABHS:
114 case ABCC:
115 case ABLO:
116 case ABMI:
117 case ABPL:
118 case ABVS:
119 case ABVC:
120 case ABHI:
121 case ABLS:
122 case ABGE:
123 case ABLT:
124 case ABGT:
125 case ABLE:
126 q1 = p->cond;
127 if(q1 != P) {
128 while(q1->as == ANOP) {
129 q1 = q1->link;
130 p->cond = q1;
131 }
132 }
133 break;
134 }
135 q = p;
136 }
137 }
138
139 for(cursym = textp; cursym != nil; cursym = cursym->next) {
140 for(p = cursym->text; p != P; p = p->link) {
141 o = p->as;
142 switch(o) {
143 case ATEXT:
144 autosize = p->to.offset + 4;
145 if(autosize <= 4)
146 if(cursym->text->mark & LEAF) {
147 p->to.offset = -4;
148 autosize = 0;
149 }
150
151 if(!autosize && !(cursym->text->mark & LEAF)) {
152 if(debug['v'])
153 Bprint(&bso, "save suppressed in: %s\n",
154 cursym->name);
155 Bflush(&bso);
156 cursym->text->mark |= LEAF;
157 }
158 if(cursym->text->mark & LEAF) {
159 cursym->leaf = 1;
160 if(!autosize)
161 break;
162 }
163
164 if(p->reg & NOSPLIT) {
165 q1 = prg();
166 q1->as = AMOVW;
167 q1->scond |= C_WBIT;
168 q1->line = p->line;
169 q1->from.type = D_REG;
170 q1->from.reg = REGLINK;
171 q1->to.type = D_OREG;
172 q1->to.offset = -autosize;
173 q1->to.reg = REGSP;
174 q1->spadj = autosize;
175 q1->link = p->link;
176 p->link = q1;
177 } else if (autosize < StackBig) {
178 // split stack check for small functions
179 // MOVW g_stackguard(g), R1
180 // CMP R1, $-autosize(SP)
181 // MOVW.LO $autosize, R1
182 // MOVW.LO $args, R2
183 // MOVW.LO R14, R3
184 // BL.LO runtime.morestack(SB) // modifies LR
185 // MOVW.W R14,$-autosize(SP)
186
187 // TODO(kaib): add more trampolines
188 // TODO(kaib): put stackguard in register
189 // TODO(kaib): add support for -K and underflow detection
190
191 // MOVW g_stackguard(g), R1
192 p = appendp(p);
193 p->as = AMOVW;
194 p->from.type = D_OREG;
195 p->from.reg = REGG;
196 p->to.type = D_REG;
197 p->to.reg = 1;
198
199 if(autosize < StackSmall) {
200 // CMP R1, SP
201 p = appendp(p);
202 p->as = ACMP;
203 p->from.type = D_REG;
204 p->from.reg = 1;
205 p->reg = REGSP;
206 } else {
207 // MOVW $-autosize(SP), R2
208 // CMP R1, R2
209 p = appendp(p);
210 p->as = AMOVW;
211 p->from.type = D_CONST;
212 p->from.reg = REGSP;
213 p->from.offset = -autosize;
214 p->to.type = D_REG;
215 p->to.reg = 2;
216
217 p = appendp(p);
218 p->as = ACMP;
219 p->from.type = D_REG;
220 p->from.reg = 1;
221 p->reg = 2;
222 }
223
224 // MOVW.LO $autosize, R1
225 p = appendp(p);
226 p->as = AMOVW;
227 p->scond = C_SCOND_LO;
228 p->from.type = D_CONST;
229 /* 160 comes from 3 calls (3*8) 4 safes (4*8) and 104 guard */
230 p->from.offset = autosize+160;
231 p->to.type = D_REG;
232 p->to.reg = 1;
233
234 // MOVW.LO $args, R2
235 p = appendp(p);
236 p->as = AMOVW;
237 p->scond = C_SCOND_LO;
238 p->from.type = D_CONST;
239 p->from.offset = (cursym->text->to.offset2 + 3) & ~3;
240 p->to.type = D_REG;
241 p->to.reg = 2;
242
243 // MOVW.LO R14, R3
244 p = appendp(p);
245 p->as = AMOVW;
246 p->scond = C_SCOND_LO;
247 p->from.type = D_REG;
248 p->from.reg = REGLINK;
249 p->to.type = D_REG;
250 p->to.reg = 3;
251
252 // BL.LO runtime.morestack(SB) // modifies LR
253 p = appendp(p);
254 p->as = ABL;
255 p->scond = C_SCOND_LO;
256 p->to.type = D_BRANCH;
257 p->to.sym = symmorestack;
258 p->cond = pmorestack;
259
260 // MOVW.W R14,$-autosize(SP)
261 p = appendp(p);
262 p->as = AMOVW;
263 p->scond |= C_WBIT;
264 p->from.type = D_REG;
265 p->from.reg = REGLINK;
266 p->to.type = D_OREG;
267 p->to.offset = -autosize;
268 p->to.reg = REGSP;
269 p->spadj = autosize;
270 } else { // > StackBig
271 // MOVW $autosize, R1
272 // MOVW $args, R2
273 // MOVW R14, R3
274 // BL runtime.morestack(SB) // modifies LR
275 // MOVW.W R14,$-autosize(SP)
276
277 // MOVW $autosize, R1
278 p = appendp(p);
279 p->as = AMOVW;
280 p->from.type = D_CONST;
281 p->from.offset = autosize;
282 p->to.type = D_REG;
283 p->to.reg = 1;
284
285 // MOVW $args, R2
286 // also need to store the extra 4 bytes.
287 p = appendp(p);
288 p->as = AMOVW;
289 p->from.type = D_CONST;
290 p->from.offset = (cursym->text->to.offset2 + 3) & ~3;
291 p->to.type = D_REG;
292 p->to.reg = 2;
293
294 // MOVW R14, R3
295 p = appendp(p);
296 p->as = AMOVW;
297 p->from.type = D_REG;
298 p->from.reg = REGLINK;
299 p->to.type = D_REG;
300 p->to.reg = 3;
301
302 // BL runtime.morestack(SB) // modifies LR
303 p = appendp(p);
304 p->as = ABL;
305 p->to.type = D_BRANCH;
306 p->to.sym = symmorestack;
307 p->cond = pmorestack;
308
309 // MOVW.W R14,$-autosize(SP)
310 p = appendp(p);
311 p->as = AMOVW;
312 p->scond |= C_WBIT;
313 p->from.type = D_REG;
314 p->from.reg = REGLINK;
315 p->to.type = D_OREG;
316 p->to.offset = -autosize;
317 p->to.reg = REGSP;
318 p->spadj = autosize;
319 }
320 break;
321
322 case ARET:
323 nocache(p);
324 if(cursym->text->mark & LEAF) {
325 if(!autosize) {
326 p->as = AB;
327 p->from = zprg.from;
328 p->to.type = D_OREG;
329 p->to.offset = 0;
330 p->to.reg = REGLINK;
331 break;
332 }
333 }
334 p->as = AMOVW;
335 p->scond |= C_PBIT;
336 p->from.type = D_OREG;
337 p->from.offset = autosize;
338 p->from.reg = REGSP;
339 p->to.type = D_REG;
340 p->to.reg = REGPC;
341 // If there are instructions following
342 // this ARET, they come from a branch
343 // with the same stackframe, so no spadj.
344 break;
345
346 case AADD:
347 if(p->from.type == D_CONST && p->from.reg == NREG && p->to.type == D_REG && p->to.reg == REGSP)
348 p->spadj = -p->from.offset;
349 break;
350
351 case ASUB:
352 if(p->from.type == D_CONST && p->from.reg == NREG && p->to.type == D_REG && p->to.reg == REGSP)
353 p->spadj = p->from.offset;
354 break;
355
356 case ADIV:
357 case ADIVU:
358 case AMOD:
359 case AMODU:
360 if(debug['M'])
361 break;
362 if(p->from.type != D_REG)
363 break;
364 if(p->to.type != D_REG)
365 break;
366 q1 = p;
367
368 /* MOV a,4(SP) */
369 q = prg();
370 q->link = p->link;
371 p->link = q;
372 p = q;
373
374 p->as = AMOVW;
375 p->line = q1->line;
376 p->from.type = D_REG;
377 p->from.reg = q1->from.reg;
378 p->to.type = D_OREG;
379 p->to.reg = REGSP;
380 p->to.offset = 4;
381
382 /* MOV b,REGTMP */
383 q = prg();
384 q->link = p->link;
385 p->link = q;
386 p = q;
387
388 p->as = AMOVW;
389 p->line = q1->line;
390 p->from.type = D_REG;
391 p->from.reg = q1->reg;
392 if(q1->reg == NREG)
393 p->from.reg = q1->to.reg;
394 p->to.type = D_REG;
395 p->to.reg = REGTMP;
396 p->to.offset = 0;
397
398 /* CALL appropriate */
399 q = prg();
400 q->link = p->link;
401 p->link = q;
402 p = q;
403
404 p->as = ABL;
405 p->line = q1->line;
406 p->to.type = D_BRANCH;
407 p->cond = p;
408 switch(o) {
409 case ADIV:
410 p->cond = prog_div;
411 p->to.sym = sym_div;
412 break;
413 case ADIVU:
414 p->cond = prog_divu;
415 p->to.sym = sym_divu;
416 break;
417 case AMOD:
418 p->cond = prog_mod;
419 p->to.sym = sym_mod;
420 break;
421 case AMODU:
422 p->cond = prog_modu;
423 p->to.sym = sym_modu;
424 break;
425 }
426
427 /* MOV REGTMP, b */
428 q = prg();
429 q->link = p->link;
430 p->link = q;
431 p = q;
432
433 p->as = AMOVW;
434 p->line = q1->line;
435 p->from.type = D_REG;
436 p->from.reg = REGTMP;
437 p->from.offset = 0;
438 p->to.type = D_REG;
439 p->to.reg = q1->to.reg;
440
441 /* ADD $8,SP */
442 q = prg();
443 q->link = p->link;
444 p->link = q;
445 p = q;
446
447 p->as = AADD;
448 p->from.type = D_CONST;
449 p->from.reg = NREG;
450 p->from.offset = 8;
451 p->reg = NREG;
452 p->to.type = D_REG;
453 p->to.reg = REGSP;
454 p->spadj = -8;
455
456 /* SUB $8,SP */
457 q1->as = ASUB;
458 q1->from.type = D_CONST;
459 q1->from.offset = 8;
460 q1->from.reg = NREG;
461 q1->reg = NREG;
462 q1->to.type = D_REG;
463 q1->to.reg = REGSP;
464 q1->spadj = 8;
465
466 break;
467 case AMOVW:
468 if((p->scond & C_WBIT) && p->to.type == D_OREG && p->to.reg == REGSP)
469 p->spadj = -p->to.offset;
470 if((p->scond & C_PBIT) && p->from.type == D_OREG && p->from.reg == REGSP && p->to.reg != REGPC)
471 p->spadj = -p->from.offset;
472 if(p->from.type == D_CONST && p->from.reg == REGSP && p->to.type == D_REG && p->to.reg == REGSP)
473 p->spadj = -p->from.offset;
474 break;
475 }
476 }
477 }
478 }
479
480 static void
481 sigdiv(char *n)
482 {
483 Sym *s;
484
485 s = lookup(n, 0);
486 if(s->type == STEXT)
487 if(s->sig == 0)
488 s->sig = SIGNINTERN;
489 }
490
491 void
492 divsig(void)
493 {
494 sigdiv("_div");
495 sigdiv("_divu");
496 sigdiv("_mod");
497 sigdiv("_modu");
498 }
499
500 void
501 initdiv(void)
502 {
503 Sym *s2, *s3, *s4, *s5;
504
505 if(prog_div != P)
506 return;
507 sym_div = s2 = lookup("_div", 0);
508 sym_divu = s3 = lookup("_divu", 0);
509 sym_mod = s4 = lookup("_mod", 0);
510 sym_modu = s5 = lookup("_modu", 0);
511 prog_div = s2->text;
512 prog_divu = s3->text;
513 prog_mod = s4->text;
514 prog_modu = s5->text;
515 if(prog_div == P) {
516 diag("undefined: %s", s2->name);
517 prog_div = cursym->text;
518 }
519 if(prog_divu == P) {
520 diag("undefined: %s", s3->name);
521 prog_divu = cursym->text;
522 }
523 if(prog_mod == P) {
524 diag("undefined: %s", s4->name);
525 prog_mod = cursym->text;
526 }
527 if(prog_modu == P) {
528 diag("undefined: %s", s5->name);
529 prog_modu = cursym->text;
530 }
531 }
532
533 void
534 nocache(Prog *p)
535 {
536 p->optab = 0;
537 p->from.class = 0;
538 p->to.class = 0;
539 }