1
// Derived from Inferno utils/5c/swt.c
2
// http://code.google.com/p/inferno-os/source/browse/utils/5c/swt.c
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.
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:
20
// The above copyright notice and this permission notice shall be included in
21
// all copies or substantial portions of the Software.
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
35
#include "../cmd/5l/5.out.h"
36
#include "../pkg/runtime/stack.h"
40
.scond = C_SCOND_NONE,
63
return p->as == ADATA || p->as == AGLOBL;
85
settextflag(Prog *p, int f)
91
progedit(Link *ctxt, Prog *p)
100
// Rewrite B/BL to symbol as D_BRANCH.
106
if(p->to.type == D_OREG && (p->to.name == D_EXTERN || p->to.name == D_STATIC) && p->to.sym != nil)
107
p->to.type = D_BRANCH;
111
// Replace TLS register fetches on older ARM procesors.
114
// If the instruction matches MRC 15, 0, <reg>, C13, C0, 3, replace it.
115
if(ctxt->goarm < 7 && (p->to.offset & 0xffff0fff) == 0xee1d0f70) {
116
tlsfallback = linklookup(ctxt, "runtime.read_tls_fallback", 0);
118
// BL runtime.read_tls_fallback(SB)
120
p->to.type = D_BRANCH;
121
p->to.sym = tlsfallback;
124
// Otherwise, MRC/MCR instructions need no further treatment.
130
// Rewrite float constants to values stored in memory.
133
if(p->from.type == D_FCONST && chipfloat5(ctxt, p->from.u.dval) < 0 &&
134
(chipzero5(ctxt, p->from.u.dval) < 0 || (p->scond & C_SCOND) != C_SCOND_NONE)) {
137
f32 = p->from.u.dval;
138
memmove(&i32, &f32, 4);
139
sprint(literal, "$f32.%08ux", (uint32)i32);
140
s = linklookup(ctxt, literal, 0);
143
adduint32(ctxt, s, i32);
146
p->from.type = D_OREG;
148
p->from.name = D_EXTERN;
154
if(p->from.type == D_FCONST && chipfloat5(ctxt, p->from.u.dval) < 0 &&
155
(chipzero5(ctxt, p->from.u.dval) < 0 || (p->scond & C_SCOND) != C_SCOND_NONE)) {
157
memmove(&i64, &p->from.u.dval, 8);
158
sprint(literal, "$f64.%016llux", (uvlong)i64);
159
s = linklookup(ctxt, literal, 0);
162
adduint64(ctxt, s, i64);
165
p->from.type = D_OREG;
167
p->from.name = D_EXTERN;
173
if(ctxt->flag_shared) {
174
// Shared libraries use R_ARM_TLS_IE32 instead of
175
// R_ARM_TLS_LE32, replacing the link time constant TLS offset in
176
// runtime.tlsgm with an address to a GOT entry containing the
177
// offset. Rewrite $runtime.tlsgm(SB) to runtime.tlsgm(SB) to
179
if(ctxt->gmsym == nil)
180
ctxt->gmsym = linklookup(ctxt, "runtime.tlsgm", 0);
182
if(p->from.type == D_CONST && p->from.name == D_EXTERN && p->from.sym == ctxt->gmsym)
183
p->from.type = D_OREG;
184
if(p->to.type == D_CONST && p->to.name == D_EXTERN && p->to.sym == ctxt->gmsym)
194
p = emallocz(sizeof(*p));
199
static Prog* stacksplit(Link*, Prog*, int32, int);
200
static void initdiv(Link*);
201
static void softfloat(Link*, LSym*);
212
linkcase(Prog *casep)
216
for(p = casep; p != nil; p = p->link){
217
if(p->as == ABCASE) {
218
for(; p != nil && p->as == ABCASE; p = p->link)
234
addstacksplit(Link *ctxt, LSym *cursym)
236
Prog *p, *pl, *q, *q1, *q2;
238
int32 autosize, autoffset;
242
if(ctxt->symmorestack[0] == nil) {
243
ctxt->symmorestack[0] = linklookup(ctxt, "runtime.morestack", 0);
244
ctxt->symmorestack[1] = linklookup(ctxt, "runtime.morestack_noctxt", 0);
249
ctxt->cursym = cursym;
251
if(cursym->text == nil || cursym->text->link == nil)
254
softfloat(ctxt, cursym);
257
autoffset = p->to.offset;
260
cursym->locals = autoffset;
261
cursym->args = p->to.offset2;
263
if(ctxt->debugzerostack) {
264
if(autoffset && !(p->reg&NOSPLIT)) {
266
p = appendp(ctxt, p);
268
p->from.type = D_CONST;
275
p = appendp(ctxt, p);
277
p->from.type = D_CONST;
279
p->from.offset = 4 + autoffset;
284
p = appendp(ctxt, p);
286
p->from.type = D_CONST;
292
// MOVW.nil R3, 0(R1) +4
295
p = pl = appendp(ctxt, p);
297
p->from.type = D_REG;
304
p = appendp(ctxt, p);
306
p->from.type = D_REG;
310
p = appendp(ctxt, p);
312
p->to.type = D_BRANCH;
318
* find leaf subroutines
321
* expand BECOME pseudo
324
for(p = cursym->text; p != nil; p = p->link) {
327
if(ctxt->flag_shared)
343
if(ctxt->sym_div == nil)
345
cursym->text->mark &= ~LEAF;
350
q->link = q1; /* q is non-nop */
359
cursym->text->mark &= ~LEAF;
382
while(q1->as == ANOP) {
392
for(p = cursym->text; p != nil; p = p->link) {
396
autosize = p->to.offset + 4;
398
if(cursym->text->mark & LEAF) {
403
if(!autosize && !(cursym->text->mark & LEAF)) {
404
if(ctxt->debugvlog) {
405
Bprint(ctxt->bso, "save suppressed in: %s\n",
409
cursym->text->mark |= LEAF;
411
if(cursym->text->mark & LEAF) {
417
if(!(p->reg & NOSPLIT))
418
p = stacksplit(ctxt, p, autosize, !(cursym->text->reg&NEEDCTXT)); // emit split check
420
// MOVW.W R14,$-autosize(SP)
421
p = appendp(ctxt, p);
424
p->from.type = D_REG;
425
p->from.reg = REGLINK;
427
p->to.offset = -autosize;
431
if(cursym->text->reg & WRAPPER) {
432
// g->panicwrap += autosize;
433
// MOVW panicwrap_offset(g), R3
435
// MOVW R3 panicwrap_offset(g)
436
p = appendp(ctxt, p);
438
p->from.type = D_OREG;
440
p->from.offset = 2*ctxt->arch->ptrsize;
444
p = appendp(ctxt, p);
446
p->from.type = D_CONST;
447
p->from.offset = autosize;
451
p = appendp(ctxt, p);
453
p->from.type = D_REG;
457
p->to.offset = 2*ctxt->arch->ptrsize;
463
if(cursym->text->mark & LEAF) {
467
if(p->to.sym) { // retjmp
468
p->to.type = D_BRANCH;
478
if(cursym->text->reg & WRAPPER) {
481
// Preserve original RET's cond, to allow RET.EQ
482
// in the implementation of reflect.call.
484
p->scond = C_SCOND_NONE;
486
// g->panicwrap -= autosize;
487
// MOVW panicwrap_offset(g), R3
489
// MOVW R3 panicwrap_offset(g)
491
p->from.type = D_OREG;
493
p->from.offset = 2*ctxt->arch->ptrsize;
496
p = appendp(ctxt, p);
499
p->from.type = D_CONST;
500
p->from.offset = autosize;
503
p = appendp(ctxt, p);
506
p->from.type = D_REG;
510
p->to.offset = 2*ctxt->arch->ptrsize;
511
p = appendp(ctxt, p);
518
p->from.type = D_OREG;
519
p->from.offset = autosize;
523
// If there are instructions following
524
// this ARET, they come from a branch
525
// with the same stackframe, so no spadj.
527
if(p->to.sym) { // retjmp
529
q2 = appendp(ctxt, p);
531
q2->to.type = D_BRANCH;
532
q2->to.sym = p->to.sym;
539
if(p->from.type == D_CONST && p->from.reg == NREG && p->to.type == D_REG && p->to.reg == REGSP)
540
p->spadj = -p->from.offset;
544
if(p->from.type == D_CONST && p->from.reg == NREG && p->to.type == D_REG && p->to.reg == REGSP)
545
p->spadj = p->from.offset;
552
if(ctxt->debugdivmod)
554
if(p->from.type != D_REG)
556
if(p->to.type != D_REG)
561
p = appendp(ctxt, p);
563
p->lineno = q1->lineno;
564
p->from.type = D_REG;
565
p->from.reg = q1->from.reg;
571
p = appendp(ctxt, p);
573
p->lineno = q1->lineno;
574
p->from.type = D_REG;
575
p->from.reg = q1->reg;
577
p->from.reg = q1->to.reg;
582
/* CALL appropriate */
583
p = appendp(ctxt, p);
585
p->lineno = q1->lineno;
586
p->to.type = D_BRANCH;
589
p->to.sym = ctxt->sym_div;
592
p->to.sym = ctxt->sym_divu;
595
p->to.sym = ctxt->sym_mod;
598
p->to.sym = ctxt->sym_modu;
603
p = appendp(ctxt, p);
605
p->lineno = q1->lineno;
606
p->from.type = D_REG;
607
p->from.reg = REGTMP;
610
p->to.reg = q1->to.reg;
613
p = appendp(ctxt, p);
615
p->lineno = q1->lineno;
616
p->from.type = D_CONST;
624
/* Keep saved LR at 0(SP) after SP change. */
625
/* MOVW 0(SP), REGTMP; MOVW REGTMP, -8!(SP) */
626
/* TODO: Remove SP adjustments; see issue 6699. */
628
q1->from.type = D_OREG;
629
q1->from.reg = REGSP;
636
q1 = appendp(ctxt, q1);
638
q1->from.type = D_REG;
639
q1->from.reg = REGTMP;
641
q1->to.type = D_OREG;
649
if((p->scond & C_WBIT) && p->to.type == D_OREG && p->to.reg == REGSP)
650
p->spadj = -p->to.offset;
651
if((p->scond & C_PBIT) && p->from.type == D_OREG && p->from.reg == REGSP && p->to.reg != REGPC)
652
p->spadj = -p->from.offset;
653
if(p->from.type == D_CONST && p->from.reg == REGSP && p->to.type == D_REG && p->to.reg == REGSP)
654
p->spadj = -p->from.offset;
661
softfloat(Link *ctxt, LSym *cursym)
670
symsfloat = linklookup(ctxt, "_sfloat", 0);
673
for(p = cursym->text; p != nil; p = p->link)
675
p->pcond->mark |= LABEL;
676
for(p = cursym->text; p != nil; p = p->link) {
679
if(p->to.type == D_FREG || p->from.type == D_FREG)
712
if (!wasfloat || (p->mark&LABEL)) {
713
next = ctxt->arch->prg();
720
p->to.type = D_BRANCH;
721
p->to.sym = symsfloat;
722
p->lineno = next->lineno;
736
stacksplit(Link *ctxt, Prog *p, int32 framesize, int noctxt)
740
// MOVW g_stackguard(g), R1
741
p = appendp(ctxt, p);
743
p->from.type = D_OREG;
748
if(framesize <= StackSmall) {
749
// small stack: SP < stackguard
750
// CMP stackguard, SP
751
p = appendp(ctxt, p);
753
p->from.type = D_REG;
756
} else if(framesize <= StackBig) {
757
// large stack: SP-framesize < stackguard-StackSmall
758
// MOVW $-framesize(SP), R2
759
// CMP stackguard, R2
760
p = appendp(ctxt, p);
762
p->from.type = D_CONST;
764
p->from.offset = -framesize;
768
p = appendp(ctxt, p);
770
p->from.type = D_REG;
774
// Such a large stack we need to protect against wraparound
775
// if SP is close to zero.
776
// SP-stackguard+StackGuard < framesize + (StackGuard-StackSmall)
777
// The +StackGuard on both sides is required to keep the left side positive:
778
// SP is allowed to be slightly below stackguard. See stack.h.
779
// CMP $StackPreempt, R1
780
// MOVW.NE $StackGuard(SP), R2
782
// MOVW.NE $(framesize+(StackGuard-StackSmall)), R3
784
p = appendp(ctxt, p);
786
p->from.type = D_CONST;
787
p->from.offset = (uint32)StackPreempt;
790
p = appendp(ctxt, p);
792
p->from.type = D_CONST;
794
p->from.offset = StackGuard;
797
p->scond = C_SCOND_NE;
799
p = appendp(ctxt, p);
801
p->from.type = D_REG;
805
p->scond = C_SCOND_NE;
807
p = appendp(ctxt, p);
809
p->from.type = D_CONST;
810
p->from.offset = framesize + (StackGuard - StackSmall);
813
p->scond = C_SCOND_NE;
815
p = appendp(ctxt, p);
817
p->from.type = D_REG;
820
p->scond = C_SCOND_NE;
823
// MOVW.LS $framesize, R1
824
p = appendp(ctxt, p);
826
p->scond = C_SCOND_LS;
827
p->from.type = D_CONST;
828
p->from.offset = framesize;
833
p = appendp(ctxt, p);
835
p->scond = C_SCOND_LS;
836
p->from.type = D_CONST;
837
arg = ctxt->cursym->text->to.offset2;
838
if(arg == 1) // special marker for known 0
841
ctxt->diag("misaligned argument size in stack split");
842
p->from.offset = arg;
847
p = appendp(ctxt, p);
849
p->scond = C_SCOND_LS;
850
p->from.type = D_REG;
851
p->from.reg = REGLINK;
855
// BL.LS runtime.morestack(SB) // modifies LR, returns with LO still asserted
856
p = appendp(ctxt, p);
858
p->scond = C_SCOND_LS;
859
p->to.type = D_BRANCH;
860
p->to.sym = ctxt->symmorestack[noctxt];
863
p = appendp(ctxt, p);
865
p->to.type = D_BRANCH;
866
p->pcond = ctxt->cursym->text->link;
874
if(ctxt->sym_div != nil)
876
ctxt->sym_div = linklookup(ctxt, "_div", 0);
877
ctxt->sym_divu = linklookup(ctxt, "_divu", 0);
878
ctxt->sym_mod = linklookup(ctxt, "_mod", 0);
879
ctxt->sym_modu = linklookup(ctxt, "_modu", 0);
882
static void xfol(Link*, Prog*, Prog**);
885
follow(Link *ctxt, LSym *s)
887
Prog *firstp, *lastp;
891
firstp = ctxt->arch->prg();
893
xfol(ctxt, s->text, &lastp);
895
s->text = firstp->link;
902
case ABEQ: return ABNE;
903
case ABNE: return ABEQ;
904
case ABCS: return ABCC;
905
case ABHS: return ABLO;
906
case ABCC: return ABCS;
907
case ABLO: return ABHS;
908
case ABMI: return ABPL;
909
case ABPL: return ABMI;
910
case ABVS: return ABVC;
911
case ABVC: return ABVS;
912
case ABHI: return ABLS;
913
case ABLS: return ABHI;
914
case ABGE: return ABLT;
915
case ABLT: return ABGE;
916
case ABGT: return ABLE;
917
case ABLE: return ABGT;
919
sysfatal("unknown relation: %s", anames5[a]);
924
xfol(Link *ctxt, Prog *p, Prog **last)
935
if(q != nil && q->as != ATEXT) {
938
if(!(p->mark & FOLL))
943
for(i=0,q=p; i<4; i++,q=q->link) {
944
if(q == *last || q == nil)
951
if(a == AB || (a == ARET && q->scond == C_SCOND_NONE) || a == ARFE || a == AUNDEF)
953
if(q->pcond == nil || (q->pcond->mark&FOLL))
955
if(a != ABEQ && a != ABNE)
959
r = ctxt->arch->prg();
962
print("can't happen 1\n");
972
if(a == AB || (a == ARET && q->scond == C_SCOND_NONE) || a == ARFE || a == AUNDEF)
979
if(!(r->link->mark&FOLL))
980
xfol(ctxt, r->link, last);
981
if(!(r->pcond->mark&FOLL))
982
print("can't happen 2\n");
987
q = ctxt->arch->prg();
989
q->lineno = p->lineno;
990
q->to.type = D_BRANCH;
991
q->to.offset = p->pc;
998
if(a == AB || (a == ARET && p->scond == C_SCOND_NONE) || a == ARFE || a == AUNDEF){
1002
if(a != ABL && a != ABX && p->link != nil) {
1003
q = brchain(ctxt, p->link);
1004
if(a != ATEXT && a != ABCASE)
1005
if(q != nil && (q->mark&FOLL)) {
1010
xfol(ctxt, p->link, last);
1011
q = brchain(ctxt, p->pcond);
1025
LinkArch linkarm = {
1029
.addstacksplit = addstacksplit,
1031
.datasize = datasize,
1036
.progedit = progedit,
1037
.settextflag = settextflag,
1039
.textflag = textflag,
1047
.D_BRANCH = D_BRANCH,
1049
.D_EXTERN = D_EXTERN,
1050
.D_FCONST = D_FCONST,
1053
.D_SCONST = D_SCONST,
1054
.D_STATIC = D_STATIC,
1059
.AFUNCDATA = AFUNCDATA,
1067
.AUSEFIELD = AUSEFIELD,