LEFT | RIGHT |
1 // Derived from Inferno utils/5c/swt.c | |
2 // http://code.google.com/p/inferno-os/source/browse/utils/5c/swt.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.vitanuov
a.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 #include <u.h> | |
32 #include <libc.h> | |
33 #include <bio.h> | |
34 #include <link.h> | |
35 #include "../cmd/5l/5.out.h" | |
36 #include "../pkg/runtime/stack.h" | |
37 | |
38 static Prog zprg = { | |
39 .as = AGOK, | |
40 .scond = C_SCOND_NONE, | |
41 .reg = NREG, | |
42 .from = { | |
43 .name = D_NONE, | |
44 .type = D_NONE, | |
45 .reg = NREG, | |
46 }, | |
47 .to = { | |
48 .name = D_NONE, | |
49 .type = D_NONE, | |
50 .reg = NREG, | |
51 }, | |
52 }; | |
53 | |
54 static int | |
55 symtype(Addr *a) | |
56 { | |
57 return a->name; | |
58 } | |
59 | |
60 static int | |
61 isdata(Prog *p) | |
62 { | |
63 return p->as == ADATA || p->as == AGLOBL; | |
64 } | |
65 | |
66 static int | |
67 iscall(Prog *p) | |
68 { | |
69 return p->as == ABL; | |
70 } | |
71 | |
72 static int | |
73 datasize(Prog *p) | |
74 { | |
75 return p->reg; | |
76 } | |
77 | |
78 static int | |
79 textflag(Prog *p) | |
80 { | |
81 return p->reg; | |
82 } | |
83 | |
84 static void | |
85 settextflag(Prog *p, int f) | |
86 { | |
87 p->reg = f; | |
88 } | |
89 | |
90 static void | |
91 progedit(Link *ctxt, Prog *p) | |
92 { | |
93 char literal[64]; | |
94 LSym *s; | |
95 | |
96 p->from.class = 0; | |
97 p->to.class = 0; | |
98 | |
99 // Rewrite B/BL to symbol as D_BRANCH. | |
100 switch(p->as) { | |
101 case AB: | |
102 case ABL: | |
103 if(p->to.type == D_OREG && (p->to.name == D_EXTERN || p->to.name
== D_STATIC) && p->to.sym != nil) | |
104 p->to.type = D_BRANCH; | |
105 break; | |
106 } | |
107 | |
108 // Rewrite float constants to values stored in memory. | |
109 switch(p->as) { | |
110 case AMOVF: | |
111 if(p->from.type == D_FCONST && chipfloat5(ctxt, p->from.u.dval)
< 0 && | |
112 (chipzero5(ctxt, p->from.u.dval) < 0 || (p->scond & C_SCOND)
!= C_SCOND_NONE)) { | |
113 int32 i32; | |
114 float32 f32; | |
115 f32 = p->from.u.dval; | |
116 memmove(&i32, &f32, 4); | |
117 sprint(literal, "$f32.%08ux", (uint32)i32); | |
118 s = linklookup(ctxt, literal, 0); | |
119 if(s->type == 0) { | |
120 s->type = SRODATA; | |
121 adduint32(ctxt, s, i32); | |
122 s->reachable = 0; | |
123 } | |
124 p->from.type = D_OREG; | |
125 p->from.sym = s; | |
126 p->from.name = D_EXTERN; | |
127 p->from.offset = 0; | |
128 } | |
129 break; | |
130 | |
131 case AMOVD: | |
132 if(p->from.type == D_FCONST && chipfloat5(ctxt, p->from.u.dval)
< 0 && | |
133 (chipzero5(ctxt, p->from.u.dval) < 0 || (p->scond & C_SCOND)
!= C_SCOND_NONE)) { | |
134 int64 i64; | |
135 memmove(&i64, &p->from.u.dval, 8); | |
136 sprint(literal, "$f64.%016llux", (uvlong)i64); | |
137 s = linklookup(ctxt, literal, 0); | |
138 if(s->type == 0) { | |
139 s->type = SRODATA; | |
140 adduint64(ctxt, s, i64); | |
141 s->reachable = 0; | |
142 } | |
143 p->from.type = D_OREG; | |
144 p->from.sym = s; | |
145 p->from.name = D_EXTERN; | |
146 p->from.offset = 0; | |
147 } | |
148 break; | |
149 } | |
150 } | |
151 | |
152 static Prog* | |
153 prg(void) | |
154 { | |
155 Prog *p; | |
156 | |
157 p = emallocz(sizeof(*p)); | |
158 *p = zprg; | |
159 return p; | |
160 } | |
161 | |
162 static Prog* stacksplit(Link*, Prog*, int32); | |
163 static void initdiv(Link*); | |
164 static void softfloat(Link*, LSym*); | |
165 | |
166 // Prog.mark | |
167 enum | |
168 { | |
169 FOLL = 1<<0, | |
170 LABEL = 1<<1, | |
171 LEAF = 1<<2, | |
172 }; | |
173 | |
174 static void | |
175 linkcase(Prog *casep) | |
176 { | |
177 Prog *p; | |
178 | |
179 for(p = casep; p != nil; p = p->link){ | |
180 if(p->as == ABCASE) { | |
181 for(; p != nil && p->as == ABCASE; p = p->link) | |
182 p->pcrel = casep; | |
183 break; | |
184 } | |
185 } | |
186 } | |
187 | |
188 static void | |
189 nocache(Prog *p) | |
190 { | |
191 p->optab = 0; | |
192 p->from.class = 0; | |
193 p->to.class = 0; | |
194 } | |
195 | |
196 static void | |
197 addstacksplit(Link *ctxt, LSym *cursym) | |
198 { | 1 { |
199 Prog *p, *pl, *q, *q1, *q2; | 2 Prog *p, *pl, *q, *q1, *q2; |
200 int o; | 3 int o; |
201 LSym *tlsfallback; | 4 LSym *tlsfallback; |
202 int32 autosize, autoffset; | 5 int32 autosize, autoffset; |
203 ········ | 6 ········ |
204 autosize = 0; | 7 autosize = 0; |
205 | |
206 if(ctxt->symmorestack[0] == nil) | |
207 ctxt->symmorestack[0] = linklookup(ctxt, "runtime.morestack", 0)
; | |
208 ········ | |
209 tlsfallback = linklookup(ctxt, "runtime.read_tls_fallback", 0); | |
210 if(ctxt->gmsym == nil) | |
211 ctxt->gmsym = linklookup(ctxt, "runtime.tlsgm", 0); | |
212 q = nil; | |
213 ········ | |
214 ctxt->cursym = cursym; | |
215 | |
216 if(cursym->text == nil || cursym->text->link == nil) | |
217 return;························· | |
218 | |
219 softfloat(ctxt, cursym); | |
220 | |
221 p = cursym->text; | |
222 autoffset = p->to.offset; | |
223 if(autoffset < 0) | |
224 autoffset = 0; | |
225 cursym->locals = autoffset; | |
226 cursym->args = p->to.offset2; | |
227 | |
228 if(ctxt->debugzerostack) { | |
229 if(autoffset && !(p->reg&NOSPLIT)) { | |
230 // MOVW $4(R13), R1 | |
231 p = appendp(ctxt, p); | |
232 p->as = AMOVW; | |
233 p->from.type = D_CONST; | |
234 p->from.reg = 13; | |
235 p->from.offset = 4; | |
236 p->to.type = D_REG; | |
237 p->to.reg = 1; | |
238 ········ | |
239 // MOVW $n(R13), R2 | |
240 p = appendp(ctxt, p); | |
241 p->as = AMOVW; | |
242 p->from.type = D_CONST; | |
243 p->from.reg = 13; | |
244 p->from.offset = 4 + autoffset; | |
245 p->to.type = D_REG; | |
246 p->to.reg = 2; | |
247 ········ | |
248 // MOVW $0, R3 | |
249 p = appendp(ctxt, p); | |
250 p->as = AMOVW; | |
251 p->from.type = D_CONST; | |
252 p->from.offset = 0; | |
253 p->to.type = D_REG; | |
254 p->to.reg = 3; | |
255 ········ | |
256 // L: | |
257 // MOVW.nil R3, 0(R1) +4 | |
258 // CMP R1, R2 | |
259 // BNE L | |
260 p = pl = appendp(ctxt, p); | |
261 p->as = AMOVW; | |
262 p->from.type = D_REG; | |
263 p->from.reg = 3; | |
264 p->to.type = D_OREG; | |
265 p->to.reg = 1; | |
266 p->to.offset = 4; | |
267 p->scond |= C_PBIT; | |
268 ········ | |
269 p = appendp(ctxt, p); | |
270 p->as = ACMP; | |
271 p->from.type = D_REG; | |
272 p->from.reg = 1; | |
273 p->reg = 2; | |
274 ········ | |
275 p = appendp(ctxt, p); | |
276 p->as = ABNE; | |
277 p->to.type = D_BRANCH; | |
278 p->pcond = pl; | |
279 } | |
280 } | |
281 | |
282 /* | |
283 * find leaf subroutines | |
284 * strip NOPs | |
285 * expand RET | |
286 * expand BECOME pseudo | |
287 * fixup TLS | |
288 */ | |
289 | |
290 for(p = cursym->text; p != nil; p = p->link) { | |
291 switch(p->as) { | |
292 case ACASE: | |
293 if(ctxt->flag_shared) | |
294 linkcase(p); | |
295 break; | |
296 | |
297 case ATEXT: | |
298 p->mark |= LEAF; | |
299 break; | |
300 | |
301 case ARET: | |
302 break; | |
303 | |
304 case ADIV: | |
305 case ADIVU: | |
306 case AMOD: | |
307 case AMODU: | |
308 q = p; | |
309 if(ctxt->sym_div == nil) | |
310 initdiv(ctxt); | |
311 cursym->text->mark &= ~LEAF; | |
312 continue; | |
313 | |
314 case ANOP: | |
315 q1 = p->link; | |
316 q->link = q1; /* q is non-nop */ | |
317 if(q1 != nil) | |
318 q1->mark |= p->mark; | |
319 continue; | |
320 | |
321 case ABL: | |
322 case ABX: | |
323 cursym->text->mark &= ~LEAF; | |
324 | |
325 case ABCASE: | |
326 case AB: | |
327 | |
328 case ABEQ: | |
329 case ABNE: | |
330 case ABCS: | |
331 case ABHS: | |
332 case ABCC: | |
333 case ABLO: | |
334 case ABMI: | |
335 case ABPL: | |
336 case ABVS: | |
337 case ABVC: | |
338 case ABHI: | |
339 case ABLS: | |
340 case ABGE: | |
341 case ABLT: | |
342 case ABGT: | |
343 case ABLE: | |
344 q1 = p->pcond; | |
345 if(q1 != nil) { | |
346 while(q1->as == ANOP) { | |
347 q1 = q1->link; | |
348 p->pcond = q1; | |
349 } | |
350 } | |
351 break; | |
352 } | |
353 q = p; | |
354 } | |
355 | |
356 for(p = cursym->text; p != nil; p = p->link) { | |
357 o = p->as; | |
358 switch(o) { | |
359 case ATEXT: | |
360 autosize = p->to.offset + 4; | |
361 if(autosize <= 4) | |
362 if(cursym->text->mark & LEAF) { | |
363 p->to.offset = -4; | |
364 autosize = 0; | |
365 } | |
366 | |
367 if(!autosize && !(cursym->text->mark & LEAF)) { | |
368 if(ctxt->debugvlog) { | |
369 Bprint(ctxt->bso, "save suppressed in: %
s\n", | |
370 cursym->name); | |
371 Bflush(ctxt->bso); | |
372 } | |
373 cursym->text->mark |= LEAF; | |
374 } | |
375 if(cursym->text->mark & LEAF) { | |
376 cursym->leaf = 1; | |
377 if(!autosize) | |
378 break; | |
379 } | |
380 | |
381 if(!(p->reg & NOSPLIT)) | |
382 p = stacksplit(ctxt, p, autosize); // emit split
check | |
383 ························ | |
384 // MOVW.W R14,$-autosize(SP) | |
385 p = appendp(ctxt, p); | |
386 p->as = AMOVW; | |
387 p->scond |= C_WBIT; | |
388 p->from.type = D_REG; | |
389 p->from.reg = REGLINK; | |
390 p->to.type = D_OREG; | |
391 p->to.offset = -autosize; | |
392 p->to.reg = REGSP; | |
393 p->spadj = autosize; | |
394 ························ | |
395 if(cursym->text->reg & WRAPPER) { | |
396 // g->panicwrap += autosize; | |
397 // MOVW panicwrap_offset(g), R3 | |
398 // ADD $autosize, R3 | |
399 // MOVW R3 panicwrap_offset(g) | |
400 p = appendp(ctxt, p); | |
401 p->as = AMOVW; | |
402 p->from.type = D_OREG; | |
403 p->from.reg = REGG; | |
404 p->from.offset = 2*ctxt->arch->ptrsize; | |
405 p->to.type = D_REG; | |
406 p->to.reg = 3; | |
407 ························ | |
408 p = appendp(ctxt, p); | |
409 p->as = AADD; | |
410 p->from.type = D_CONST; | |
411 p->from.offset = autosize; | |
412 p->to.type = D_REG; | |
413 p->to.reg = 3; | |
414 ································ | |
415 p = appendp(ctxt, p); | |
416 p->as = AMOVW; | |
417 p->from.type = D_REG; | |
418 p->from.reg = 3; | |
419 p->to.type = D_OREG; | |
420 p->to.reg = REGG; | |
421 p->to.offset = 2*ctxt->arch->ptrsize; | |
422 } | |
423 break; | |
424 | |
425 case ARET: | |
426 nocache(p); | |
427 if(cursym->text->mark & LEAF) { | |
428 if(!autosize) { | |
429 p->as = AB; | |
430 p->from = zprg.from; | |
431 if(p->to.sym) { // retjmp | |
432 p->to.type = D_BRANCH; | |
433 } else { | |
434 p->to.type = D_OREG; | |
435 p->to.offset = 0; | |
436 p->to.reg = REGLINK; | |
437 } | |
438 break; | |
439 } | |
440 } | |
441 | |
442 if(cursym->text->reg & WRAPPER) { | |
443 int scond; | |
444 ································ | |
445 // Preserve original RET's cond, to allow RET.EQ | |
446 // in the implementation of reflect.call. | |
447 scond = p->scond; | |
448 p->scond = C_SCOND_NONE; | |
449 | |
450 // g->panicwrap -= autosize; | |
451 // MOVW panicwrap_offset(g), R3 | |
452 // SUB $autosize, R3 | |
453 // MOVW R3 panicwrap_offset(g) | |
454 p->as = AMOVW; | |
455 p->from.type = D_OREG; | |
456 p->from.reg = REGG; | |
457 p->from.offset = 2*ctxt->arch->ptrsize; | |
458 p->to.type = D_REG; | |
459 p->to.reg = 3; | |
460 p = appendp(ctxt, p); | |
461 ························ | |
462 p->as = ASUB; | |
463 p->from.type = D_CONST; | |
464 p->from.offset = autosize; | |
465 p->to.type = D_REG; | |
466 p->to.reg = 3; | |
467 p = appendp(ctxt, p); | |
468 | |
469 p->as = AMOVW; | |
470 p->from.type = D_REG; | |
471 p->from.reg = 3; | |
472 p->to.type = D_OREG; | |
473 p->to.reg = REGG; | |
474 p->to.offset = 2*ctxt->arch->ptrsize; | |
475 p = appendp(ctxt, p); | |
476 | |
477 p->scond = scond; | |
478 } | |
479 | |
480 p->as = AMOVW; | |
481 p->scond |= C_PBIT; | |
482 p->from.type = D_OREG; | |
483 p->from.offset = autosize; | |
484 p->from.reg = REGSP; | |
485 p->to.type = D_REG; | |
486 p->to.reg = REGPC; | |
487 // If there are instructions following | |
488 // this ARET, they come from a branch | |
489 // with the same stackframe, so no spadj. | |
490 ························ | |
491 if(p->to.sym) { // retjmp | |
492 p->to.reg = REGLINK; | |
493 q2 = appendp(ctxt, p); | |
494 q2->as = AB; | |
495 q2->to.type = D_BRANCH; | |
496 q2->to.sym = p->to.sym; | |
497 p->to.sym = nil; | |
498 p = q2; | |
499 } | |
500 break; | |
501 | |
502 case AADD: | |
503 if(p->from.type == D_CONST && p->from.reg == NREG && p->
to.type == D_REG && p->to.reg == REGSP) | |
504 p->spadj = -p->from.offset; | |
505 break; | |
506 | |
507 case ASUB: | |
508 if(p->from.type == D_CONST && p->from.reg == NREG && p->
to.type == D_REG && p->to.reg == REGSP) | |
509 p->spadj = p->from.offset; | |
510 break; | |
511 | |
512 case ADIV: | |
513 case ADIVU: | |
514 case AMOD: | |
515 case AMODU: | |
516 if(ctxt->debugdivmod) | |
517 break; | |
518 if(p->from.type != D_REG) | |
519 break; | |
520 if(p->to.type != D_REG) | |
521 break; | |
522 q1 = p; | |
523 | |
524 /* MOV a,4(SP) */ | |
525 p = appendp(ctxt, p); | |
526 p->as = AMOVW; | |
527 p->lineno = q1->lineno; | |
528 p->from.type = D_REG; | |
529 p->from.reg = q1->from.reg; | |
530 p->to.type = D_OREG; | |
531 p->to.reg = REGSP; | |
532 p->to.offset = 4; | |
533 | |
534 /* MOV b,REGTMP */ | |
535 p = appendp(ctxt, p); | |
536 p->as = AMOVW; | |
537 p->lineno = q1->lineno; | |
538 p->from.type = D_REG; | |
539 p->from.reg = q1->reg; | |
540 if(q1->reg == NREG) | |
541 p->from.reg = q1->to.reg; | |
542 p->to.type = D_REG; | |
543 p->to.reg = REGTMP; | |
544 p->to.offset = 0; | |
545 | |
546 /* CALL appropriate */ | |
547 p = appendp(ctxt, p); | |
548 p->as = ABL; | |
549 p->lineno = q1->lineno; | |
550 p->to.type = D_BRANCH; | |
551 switch(o) { | |
552 case ADIV: | |
553 p->to.sym = ctxt->sym_div; | |
554 break; | |
555 case ADIVU: | |
556 p->to.sym = ctxt->sym_divu; | |
557 break; | |
558 case AMOD: | |
559 p->to.sym = ctxt->sym_mod; | |
560 break; | |
561 case AMODU: | |
562 p->to.sym = ctxt->sym_modu; | |
563 break; | |
564 } | |
565 | |
566 /* MOV REGTMP, b */ | |
567 p = appendp(ctxt, p); | |
568 p->as = AMOVW; | |
569 p->lineno = q1->lineno; | |
570 p->from.type = D_REG; | |
571 p->from.reg = REGTMP; | |
572 p->from.offset = 0; | |
573 p->to.type = D_REG; | |
574 p->to.reg = q1->to.reg; | |
575 | |
576 /* ADD $8,SP */ | |
577 p = appendp(ctxt, p); | |
578 p->as = AADD; | |
579 p->lineno = q1->lineno; | |
580 p->from.type = D_CONST; | |
581 p->from.reg = NREG; | |
582 p->from.offset = 8; | |
583 p->reg = NREG; | |
584 p->to.type = D_REG; | |
585 p->to.reg = REGSP; | |
586 p->spadj = -8; | |
587 | |
588 /* Keep saved LR at 0(SP) after SP change. */ | |
589 /* MOVW 0(SP), REGTMP; MOVW REGTMP, -8!(SP) */ | |
590 /* TODO: Remove SP adjustments; see issue 6699. */ | |
591 q1->as = AMOVW; | |
592 q1->from.type = D_OREG; | |
593 q1->from.reg = REGSP; | |
594 q1->from.offset = 0; | |
595 q1->reg = NREG; | |
596 q1->to.type = D_REG; | |
597 q1->to.reg = REGTMP; | |
598 | |
599 /* SUB $8,SP */ | |
600 q1 = appendp(ctxt, q1); | |
601 q1->as = AMOVW; | |
602 q1->from.type = D_REG; | |
603 q1->from.reg = REGTMP; | |
604 q1->reg = NREG; | |
605 q1->to.type = D_OREG; | |
606 q1->to.reg = REGSP; | |
607 q1->to.offset = -8; | |
608 q1->scond |= C_WBIT; | |
609 q1->spadj = 8; | |
610 | |
611 break; | |
612 case AMOVW: | |
613 if((p->scond & C_WBIT) && p->to.type == D_OREG && p->to.
reg == REGSP) | |
614 p->spadj = -p->to.offset; | |
615 if((p->scond & C_PBIT) && p->from.type == D_OREG && p->f
rom.reg == REGSP && p->to.reg != REGPC) | |
616 p->spadj = -p->from.offset; | |
617 if(p->from.type == D_CONST && p->from.reg == REGSP && p-
>to.type == D_REG && p->to.reg == REGSP) | |
618 p->spadj = -p->from.offset; | |
619 break; | |
620 } | |
621 } | |
622 } | |
623 | |
624 static void | |
625 softfloat(Link *ctxt, LSym *cursym) | |
626 { | |
627 Prog *p, *next; | |
628 LSym *symsfloat; | |
629 int wasfloat; | |
630 | |
631 if(ctxt->goarm > 5) | |
632 return; | |
633 | |
634 symsfloat = linklookup(ctxt, "_sfloat", 0); | |
635 | |
636 wasfloat = 0; | |
637 for(p = cursym->text; p != nil; p = p->link) | |
638 if(p->pcond != nil) | |
639 p->pcond->mark |= LABEL; | |
640 for(p = cursym->text; p != nil; p = p->link) { | |
641 switch(p->as) { | |
642 case AMOVW: | |
643 if(p->to.type == D_FREG || p->from.type == D_FREG) | |
644 goto soft; | |
645 goto notsoft; | |
646 | |
647 case AMOVWD: | |
648 case AMOVWF: | |
649 case AMOVDW: | |
650 case AMOVFW: | |
651 case AMOVFD: | |
652 case AMOVDF: | |
653 case AMOVF: | |
654 case AMOVD: | |
655 | |
656 case ACMPF: | |
657 case ACMPD: | |
658 case AADDF: | |
659 case AADDD: | |
660 case ASUBF: | |
661 case ASUBD: | |
662 case AMULF: | |
663 case AMULD: | |
664 case ADIVF: | |
665 case ADIVD: | |
666 case ASQRTF: | |
667 case ASQRTD: | |
668 case AABSF: | |
669 case AABSD: | |
670 goto soft; | |
671 | |
672 default: | |
673 goto notsoft; | |
674 | |
675 soft: | |
676 if (!wasfloat || (p->mark&LABEL)) { | |
677 next = ctxt->arch->prg(); | |
678 *next = *p; | |
679 | |
680 // BL _sfloat(SB) | |
681 *p = zprg; | |
682 p->link = next; | |
683 p->as = ABL; | |
684 p->to.type = D_BRANCH; | |
685 p->to.sym = symsfloat; | |
686 p->lineno = next->lineno; | |
687 | |
688 p = next; | |
689 wasfloat = 1; | |
690 } | |
691 break; | |
692 | |
693 notsoft: | |
694 wasfloat = 0; | |
695 } | |
696 } | |
697 } | |
698 | |
699 static Prog* | |
700 stacksplit(Link *ctxt, Prog *p, int32 framesize) | |
701 { | |
702 int32 arg; | |
703 | |
704 // MOVW g_stackguard(g), R1 | |
705 p = appendp(ctxt, p); | |
706 p->as = AMOVW; | |
707 p->from.type = D_OREG; | |
708 p->from.reg = REGG; | |
709 p->to.type = D_REG; | |
710 p->to.reg = 1; | |
711 ········ | |
712 if(framesize <= StackSmall) { | |
713 // small stack: SP < stackguard | |
714 // CMP stackguard, SP | |
715 p = appendp(ctxt, p); | |
716 p->as = ACMP; | |
717 p->from.type = D_REG; | |
718 p->from.reg = 1; | |
719 p->reg = REGSP; | |
720 } else if(framesize <= StackBig) { | |
721 // large stack: SP-framesize < stackguard-StackSmall | |
722 // MOVW $-framesize(SP), R2 | |
723 // CMP stackguard, R2 | |
724 p = appendp(ctxt, p); | |
725 p->as = AMOVW; | |
726 p->from.type = D_CONST; | |
727 p->from.reg = REGSP; | |
728 p->from.offset = -framesize; | |
729 p->to.type = D_REG; | |
730 p->to.reg = 2; | |
731 ················ | |
732 p = appendp(ctxt, p); | |
733 p->as = ACMP; | |
734 p->from.type = D_REG; | |
735 p->from.reg = 1; | |
736 p->reg = 2; | |
737 } else { | |
738 // Such a large stack we need to protect against wraparound | |
739 // if SP is close to zero. | |
740 // SP-stackguard+StackGuard < framesize + (StackGuard-Stack
Small) | |
741 // The +StackGuard on both sides is required to keep the left si
de positive: | |
742 // SP is allowed to be slightly below stackguard. See stack.h. | |
743 // CMP $StackPreempt, R1 | |
744 // MOVW.NE $StackGuard(SP), R2 | |
745 // SUB.NE R1, R2 | |
746 // MOVW.NE $(framesize+(StackGuard-StackSmall)), R3 | |
747 // CMP.NE R3, R2 | |
748 p = appendp(ctxt, p); | |
749 p->as = ACMP; | |
750 p->from.type = D_CONST; | |
751 p->from.offset = (uint32)StackPreempt; | |
752 p->reg = 1; | |
753 | |
754 p = appendp(ctxt, p); | |
755 p->as = AMOVW; | |
756 p->from.type = D_CONST; | |
757 p->from.reg = REGSP; | |
758 p->from.offset = StackGuard; | |
759 p->to.type = D_REG; | |
760 p->to.reg = 2; | |
761 p->scond = C_SCOND_NE; | |
762 ················ | |
763 p = appendp(ctxt, p); | |
764 p->as = ASUB; | |
765 p->from.type = D_REG; | |
766 p->from.reg = 1; | |
767 p->to.type = D_REG; | |
768 p->to.reg = 2; | |
769 p->scond = C_SCOND_NE; | |
770 ················ | |
771 p = appendp(ctxt, p); | |
772 p->as = AMOVW; | |
773 p->from.type = D_CONST; | |
774 p->from.offset = framesize + (StackGuard - StackSmall); | |
775 p->to.type = D_REG; | |
776 p->to.reg = 3; | |
777 p->scond = C_SCOND_NE; | |
778 ················ | |
779 p = appendp(ctxt, p); | |
780 p->as = ACMP; | |
781 p->from.type = D_REG; | |
782 p->from.reg = 3; | |
783 p->reg = 2; | |
784 p->scond = C_SCOND_NE; | |
785 } | |
786 ········ | |
787 // MOVW.LS $framesize, R1 | |
788 p = appendp(ctxt, p); | |
789 p->as = AMOVW; | |
790 p->scond = C_SCOND_LS; | |
791 p->from.type = D_CONST; | |
792 p->from.offset = framesize; | |
793 p->to.type = D_REG; | |
794 p->to.reg = 1; | |
795 | |
796 // MOVW.LS $args, R2 | |
797 p = appendp(ctxt, p); | |
798 p->as = AMOVW; | |
799 p->scond = C_SCOND_LS; | |
800 p->from.type = D_CONST; | |
801 arg = ctxt->cursym->text->to.offset2; | |
802 if(arg == 1) // special marker for known 0 | |
803 arg = 0; | |
804 if(arg&3) | |
805 ctxt->diag("misaligned argument size in stack split"); | |
806 p->from.offset = arg; | |
807 p->to.type = D_REG; | |
808 p->to.reg = 2; | |
809 | |
810 // MOVW.LS R14, R3 | |
811 p = appendp(ctxt, p); | |
812 p->as = AMOVW; | |
813 p->scond = C_SCOND_LS; | |
814 p->from.type = D_REG; | |
815 p->from.reg = REGLINK; | |
816 p->to.type = D_REG; | |
817 p->to.reg = 3; | |
818 | |
819 // BL.LS runtime.morestack(SB) // modifies LR, returns wi
th LO still asserted | |
820 p = appendp(ctxt, p); | |
821 p->as = ABL; | |
822 p->scond = C_SCOND_LS; | |
823 p->to.type = D_BRANCH; | |
824 p->to.sym = ctxt->symmorestack[0]; | |
825 ········ | |
826 // BLS start | |
827 p = appendp(ctxt, p); | |
828 p->as = ABLS; | |
829 p->to.type = D_BRANCH; | |
830 p->pcond = ctxt->cursym->text->link; | |
831 ········ | |
832 return p; | |
833 } | |
834 | |
835 static void | |
836 initdiv(Link *ctxt) | |
837 { | |
838 if(ctxt->sym_div != nil) | |
839 return; | |
840 ctxt->sym_div = linklookup(ctxt, "_div", 0); | |
841 ctxt->sym_divu = linklookup(ctxt, "_divu", 0); | |
842 ctxt->sym_mod = linklookup(ctxt, "_mod", 0); | |
843 ctxt->sym_modu = linklookup(ctxt, "_modu", 0); | |
844 } | |
845 | |
846 static void xfol(Link*, Prog*, Prog**); | |
847 | |
848 static void | |
849 follow(Link *ctxt, LSym *s) | |
850 { | |
851 Prog *firstp, *lastp; | |
852 | |
853 ctxt->cursym = s; | |
854 | |
855 firstp = ctxt->arch->prg(); | |
856 lastp = firstp; | |
857 xfol(ctxt, s->text, &lastp); | |
858 lastp->link = nil; | |
859 s->text = firstp->link; | |
860 } | |
861 | |
862 static int | |
863 relinv(int a) | |
864 { | |
865 switch(a) { | |
866 case ABEQ: return ABNE; | |
867 case ABNE: return ABEQ; | |
868 case ABCS: return ABCC; | |
869 case ABHS: return ABLO; | |
870 case ABCC: return ABCS; | |
871 case ABLO: return ABHS; | |
872 case ABMI: return ABPL; | |
873 case ABPL: return ABMI; | |
874 case ABVS: return ABVC; | |
875 case ABVC: return ABVS; | |
876 case ABHI: return ABLS; | |
877 case ABLS: return ABHI; | |
878 case ABGE: return ABLT; | |
879 case ABLT: return ABGE; | |
880 case ABGT: return ABLE; | |
881 case ABLE: return ABGT; | |
882 } | |
883 sysfatal("unknown relation: %s", anames5[a]); | |
884 return 0; | |
885 } | |
886 | |
887 static void | |
888 xfol(Link *ctxt, Prog *p, Prog **last) | |
889 { | |
890 Prog *q, *r; | |
891 int a, i; | |
892 | |
893 loop: | |
894 if(p == nil) | |
895 return; | |
896 a = p->as; | |
897 if(a == AB) { | |
898 q = p->pcond; | |
899 if(q != nil && q->as != ATEXT) { | |
900 p->mark |= FOLL; | |
901 p = q; | |
902 if(!(p->mark & FOLL)) | |
903 goto loop; | |
904 } | |
905 } | |
906 if(p->mark & FOLL) { | |
907 for(i=0,q=p; i<4; i++,q=q->link) { | |
908 if(q == *last || q == nil) | |
909 break; | |
910 a = q->as; | |
911 if(a == ANOP) { | |
912 i--; | |
913 continue; | |
914 } | |
915 if(a == AB || (a == ARET && q->scond == C_SCOND_NONE) ||
a == ARFE || a == AUNDEF) | |
916 goto copy; | |
917 if(q->pcond == nil || (q->pcond->mark&FOLL)) | |
918 continue; | |
919 if(a != ABEQ && a != ABNE) | |
920 continue; | |
921 copy: | |
922 for(;;) { | |
923 r = ctxt->arch->prg(); | |
924 *r = *p; | |
925 if(!(r->mark&FOLL)) | |
926 print("can't happen 1\n"); | |
927 r->mark |= FOLL; | |
928 if(p != q) { | |
929 p = p->link; | |
930 (*last)->link = r; | |
931 *last = r; | |
932 continue; | |
933 } | |
934 (*last)->link = r; | |
935 *last = r; | |
936 if(a == AB || (a == ARET && q->scond == C_SCOND_
NONE) || a == ARFE || a == AUNDEF) | |
937 return; | |
938 r->as = ABNE; | |
939 if(a == ABNE) | |
940 r->as = ABEQ; | |
941 r->pcond = p->link; | |
942 r->link = p->pcond; | |
943 if(!(r->link->mark&FOLL)) | |
944 xfol(ctxt, r->link, last); | |
945 if(!(r->pcond->mark&FOLL)) | |
946 print("can't happen 2\n"); | |
947 return; | |
948 } | |
949 } | |
950 a = AB; | |
951 q = ctxt->arch->prg(); | |
952 q->as = a; | |
953 q->lineno = p->lineno; | |
954 q->to.type = D_BRANCH; | |
955 q->to.offset = p->pc; | |
956 q->pcond = p; | |
957 p = q; | |
958 } | |
959 p->mark |= FOLL; | |
960 (*last)->link = p; | |
961 *last = p; | |
962 if(a == AB || (a == ARET && p->scond == C_SCOND_NONE) || a == ARFE || a
== AUNDEF){ | |
963 return; | |
964 } | |
965 if(p->pcond != nil) | |
966 if(a != ABL && a != ABX && p->link != nil) { | |
967 q = brchain(ctxt, p->link); | |
968 if(a != ATEXT && a != ABCASE) | |
969 if(q != nil && (q->mark&FOLL)) { | |
970 p->as = relinv(a); | |
971 p->link = p->pcond; | |
972 p->pcond = q; | |
973 } | |
974 xfol(ctxt, p->link, last); | |
975 q = brchain(ctxt, p->pcond); | |
976 if(q == nil) | |
977 q = p->pcond; | |
978 if(q->mark&FOLL) { | |
979 p->pcond = q; | |
980 return; | |
981 } | |
982 p = q; | |
983 goto loop; | |
984 } | |
985 p = p->link; | |
986 goto loop; | |
987 } | |
988 | |
989 LinkArch linkarm = { | |
990 .name = "arm", | |
991 .thechar = '5', | |
992 | |
993 .addstacksplit = addstacksplit, | |
994 .assemble = span5, | |
995 .datasize = datasize, | |
996 .follow = follow, | |
997 .iscall = iscall, | |
998 .isdata = isdata, | |
999 .prg = prg, | |
1000 .progedit = progedit, | |
1001 .settextflag = settextflag, | |
1002 .symtype = symtype, | |
1003 .textflag = textflag, | |
1004 | |
1005 .minlc = 4, | |
1006 .ptrsize = 4, | |
1007 | |
1008 .D_ADDR = D_ADDR, | |
1009 .D_BRANCH = D_BRANCH, | |
1010 .D_CONST = D_CONST, | |
1011 .D_EXTERN = D_EXTERN, | |
1012 .D_FCONST = D_FCONST, | |
1013 .D_NONE = D_NONE, | |
1014 .D_PCREL = D_PCREL, | |
1015 .D_SCONST = D_SCONST, | |
1016 .D_SIZE = D_SIZE, | |
1017 .D_STATIC = D_STATIC, | |
1018 | |
1019 .ACALL = ABL, | |
1020 .ADATA = ADATA, | |
1021 .AEND = AEND, | |
1022 .AFUNCDATA = AFUNCDATA, | |
1023 .AGLOBL = AGLOBL, | |
1024 .AJMP = AB, | |
1025 .ANOP = ANOP, | |
1026 .APCDATA = APCDATA, | |
1027 .ARET = ARET, | |
1028 .ATEXT = ATEXT, | |
1029 .ATYPE = ATYPE, | |
1030 .AUSEFIELD = AUSEFIELD, | |
1031 }; | |
LEFT | RIGHT |