| // Inferno utils/5l/noop.c |
| // http://code.google.com/p/inferno-os/source/browse/utils/5l/noop.c |
| // |
| // Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. |
| // Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) |
| // Portions Copyright © 1997-1999 Vita Nuova Limited |
| // Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) |
| // Portions Copyright © 2004,2006 Bruce Ellis |
| // Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) |
| // Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others |
| // Portions Copyright © 2009 The Go Authors. All rights reserved. |
| // |
| // Permission is hereby granted, free of charge, to any person obtaining a copy |
| // of this software and associated documentation files (the "Software"), to deal |
| // in the Software without restriction, including without limitation the rights |
| // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
| // copies of the Software, and to permit persons to whom the Software is |
| // furnished to do so, subject to the following conditions: |
| // |
| // The above copyright notice and this permission notice shall be included in |
| // all copies or substantial portions of the Software. |
| // |
| // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
| // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
| // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
| // THE SOFTWARE. |
| |
| #include "l.h" |
| |
| // see ../../runtime/proc.c:/StackGuard |
| enum |
| { |
| StackBig = 4096, |
| }; |
| |
| static Sym* sym_div; |
| static Sym* sym_divu; |
| static Sym* sym_mod; |
| static Sym* sym_modu; |
| |
| static void setdiv(int); |
| |
| static Prog * |
| movrr(Prog *q, int rs, int rd, Prog *p) |
| { |
| if(q == nil) |
| q = prg(); |
| q->as = AMOVW; |
| q->line = p->line; |
| q->from.type = D_REG; |
| q->from.reg = rs; |
| q->to.type = D_REG; |
| q->to.reg = rd; |
| q->link = p->link; |
| return q; |
| } |
| |
| static Prog * |
| fnret(Prog *q, int rs, int foreign, Prog *p) |
| { |
| q = movrr(q, rs, REGPC, p); |
| if(foreign){ // BX rs |
| q->as = ABXRET; |
| q->from.type = D_NONE; |
| q->from.reg = NREG; |
| q->to.reg = rs; |
| } |
| return q; |
| } |
| |
| static Prog * |
| aword(int32 w, Prog *p) |
| { |
| Prog *q; |
| |
| q = prg(); |
| q->as = AWORD; |
| q->line = p->line; |
| q->from.type = D_NONE; |
| q->reg = NREG; |
| q->to.type = D_CONST; |
| q->to.offset = w; |
| q->link = p->link; |
| p->link = q; |
| return q; |
| } |
| |
| static Prog * |
| adword(int32 w1, int32 w2, Prog *p) |
| { |
| Prog *q; |
| |
| q = prg(); |
| q->as = ADWORD; |
| q->line = p->line; |
| q->from.type = D_CONST; |
| q->from.offset = w1; |
| q->reg = NREG; |
| q->to.type = D_CONST; |
| q->to.offset = w2; |
| q->link = p->link; |
| p->link = q; |
| return q; |
| } |
| |
| void |
| noops(void) |
| { |
| Prog *p, *q, *q1, *q2; |
| int o, curframe, curbecome, maxbecome, foreign; |
| Prog *pmorestack; |
| Sym *symmorestack; |
| |
| /* |
| * find leaf subroutines |
| * become sizes |
| * frame sizes |
| * strip NOPs |
| * expand RET |
| * expand BECOME pseudo |
| */ |
| |
| if(debug['v']) |
| Bprint(&bso, "%5.2f noops\n", cputime()); |
| Bflush(&bso); |
| |
| pmorestack = P; |
| symmorestack = lookup("runtime.morestack", 0); |
| |
| if(symmorestack->type == STEXT) |
| for(p = firstp; p != P; p = p->link) { |
| if(p->as == ATEXT) { |
| if(p->from.sym == symmorestack) { |
| pmorestack = p; |
| p->reg |= NOSPLIT; |
| break; |
| } |
| } |
| } |
| // TODO(kaib): make lack of morestack an error |
| // if(pmorestack == P) |
| // diag("runtime·morestack not defined"); |
| |
| curframe = 0; |
| curbecome = 0; |
| maxbecome = 0; |
| curtext = 0; |
| |
| q = P; |
| for(p = firstp; p != P; p = p->link) { |
| setarch(p); |
| |
| /* find out how much arg space is used in this TEXT */ |
| if(p->to.type == D_OREG && p->to.reg == REGSP) |
| if(p->to.offset > curframe) |
| curframe = p->to.offset; |
| |
| switch(p->as) { |
| case ATEXT: |
| if(curtext && curtext->from.sym) { |
| curtext->from.sym->frame = curframe; |
| curtext->from.sym->become = curbecome; |
| if(curbecome > maxbecome) |
| maxbecome = curbecome; |
| } |
| curframe = 0; |
| curbecome = 0; |
| |
| p->mark |= LEAF; |
| curtext = p; |
| break; |
| |
| case ARET: |
| /* special form of RET is BECOME */ |
| if(p->from.type == D_CONST) |
| if(p->from.offset > curbecome) |
| curbecome = p->from.offset; |
| break; |
| |
| case ADIV: |
| case ADIVU: |
| case AMOD: |
| case AMODU: |
| q = p; |
| if(prog_div == P) |
| initdiv(); |
| if(curtext != P) |
| curtext->mark &= ~LEAF; |
| setdiv(p->as); |
| continue; |
| |
| case ANOP: |
| q1 = p->link; |
| q->link = q1; /* q is non-nop */ |
| q1->mark |= p->mark; |
| continue; |
| |
| case ABL: |
| case ABX: |
| if(curtext != P) |
| curtext->mark &= ~LEAF; |
| |
| case ABCASE: |
| case AB: |
| |
| case ABEQ: |
| case ABNE: |
| case ABCS: |
| case ABHS: |
| case ABCC: |
| case ABLO: |
| case ABMI: |
| case ABPL: |
| case ABVS: |
| case ABVC: |
| case ABHI: |
| case ABLS: |
| case ABGE: |
| case ABLT: |
| case ABGT: |
| case ABLE: |
| |
| q1 = p->cond; |
| if(q1 != P) { |
| while(q1->as == ANOP) { |
| q1 = q1->link; |
| p->cond = q1; |
| } |
| } |
| break; |
| } |
| q = p; |
| } |
| |
| if(curtext && curtext->from.sym) { |
| curtext->from.sym->frame = curframe; |
| curtext->from.sym->become = curbecome; |
| if(curbecome > maxbecome) |
| maxbecome = curbecome; |
| } |
| |
| if(debug['b']) |
| print("max become = %d\n", maxbecome); |
| xdefine("ALEFbecome", STEXT, maxbecome); |
| |
| curtext = 0; |
| for(p = firstp; p != P; p = p->link) { |
| setarch(p); |
| switch(p->as) { |
| case ATEXT: |
| curtext = p; |
| break; |
| case ABL: |
| // case ABX: |
| if(curtext != P && curtext->from.sym != S && curtext->to.offset >= 0) { |
| o = maxbecome - curtext->from.sym->frame; |
| if(o <= 0) |
| break; |
| /* calling a become or calling a variable */ |
| if(p->to.sym == S || p->to.sym->become) { |
| curtext->to.offset += o; |
| if(debug['b']) { |
| curp = p; |
| print("%D calling %D increase %d\n", |
| &curtext->from, &p->to, o); |
| } |
| } |
| } |
| break; |
| } |
| } |
| |
| for(p = firstp; p != P; p = p->link) { |
| setarch(p); |
| o = p->as; |
| switch(o) { |
| case ATEXT: |
| curtext = p; |
| autosize = p->to.offset + 4; |
| if(autosize <= 4) |
| if(curtext->mark & LEAF) { |
| p->to.offset = -4; |
| autosize = 0; |
| } |
| |
| if(!autosize && !(curtext->mark & LEAF)) { |
| if(debug['v']) |
| Bprint(&bso, "save suppressed in: %s\n", |
| curtext->from.sym->name); |
| Bflush(&bso); |
| curtext->mark |= LEAF; |
| } |
| #ifdef CALLEEBX |
| if(p->from.sym->foreign){ |
| if(thumb) |
| // don't allow literal pool to seperate these |
| p = adword(0xe28f7001, 0xe12fff17, p); // arm add 1, pc, r7 and bx r7 |
| // p = aword(0xe12fff17, aword(0xe28f7001, p)); // arm add 1, pc, r7 and bx r7 |
| else |
| p = aword(0x4778, p); // thumb bx pc and 2 bytes padding |
| } |
| #endif |
| if(curtext->mark & LEAF) { |
| if(curtext->from.sym) |
| curtext->from.sym->type = SLEAF; |
| if(!autosize) |
| break; |
| } |
| |
| if(thumb){ |
| if(!(p->reg & NOSPLIT)) |
| diag("stack splitting not supported in thumb"); |
| if(!(curtext->mark & LEAF)){ |
| q = movrr(nil, REGLINK, REGTMPT-1, p); |
| p->link = q; |
| q1 = prg(); |
| q1->as = AMOVW; |
| q1->line = p->line; |
| q1->from.type = D_REG; |
| q1->from.reg = REGTMPT-1; |
| q1->to.type = D_OREG; |
| q1->to.name = D_NONE; |
| q1->to.reg = REGSP; |
| q1->to.offset = 0; |
| q1->link = q->link; |
| q->link = q1; |
| } |
| if(autosize){ |
| q2 = prg(); |
| q2->as = ASUB; |
| q2->line = p->line; |
| q2->from.type = D_CONST; |
| q2->from.offset = autosize; |
| q2->to.type = D_REG; |
| q2->to.reg = REGSP; |
| q2->link = p->link; |
| p->link = q2; |
| } |
| break; |
| } |
| |
| if(p->reg & NOSPLIT) { |
| q1 = prg(); |
| q1->as = AMOVW; |
| q1->scond |= C_WBIT; |
| q1->line = p->line; |
| q1->from.type = D_REG; |
| q1->from.reg = REGLINK; |
| q1->to.type = D_OREG; |
| q1->to.offset = -autosize; |
| q1->to.reg = REGSP; |
| q1->link = p->link; |
| p->link = q1; |
| } else if (autosize < StackBig) { |
| // split stack check for small functions |
| // MOVW g_stackguard(g), R1 |
| // CMP R1, $-autosize(SP) |
| // MOVW.LO $autosize, R1 |
| // MOVW.LO $args, R2 |
| // MOVW.LO R14, R3 |
| // BL.LO runtime.morestack(SB) // modifies LR |
| // MOVW.W R14,$-autosize(SP) |
| |
| // TODO(kaib): add more trampolines |
| // TODO(kaib): put stackguard in register |
| // TODO(kaib): add support for -K and underflow detection |
| |
| // MOVW g_stackguard(g), R1 |
| p = appendp(p); |
| p->as = AMOVW; |
| p->from.type = D_OREG; |
| p->from.reg = REGG; |
| p->to.type = D_REG; |
| p->to.reg = 1; |
| |
| // CMP R1, $-autosize(SP) |
| p = appendp(p); |
| p->as = ACMP; |
| p->from.type = D_REG; |
| p->from.reg = 1; |
| p->from.offset = -autosize; |
| p->reg = REGSP; |
| |
| // MOVW.LO $autosize, R1 |
| p = appendp(p); |
| p->as = AMOVW; |
| p->scond = C_SCOND_LO; |
| p->from.type = D_CONST; |
| p->from.offset = 0; |
| p->to.type = D_REG; |
| p->to.reg = 1; |
| |
| // MOVW.LO $args +4, R2 |
| // also need to store the extra 4 bytes. |
| p = appendp(p); |
| p->as = AMOVW; |
| p->scond = C_SCOND_LO; |
| p->from.type = D_CONST; |
| p->from.offset = ((curtext->to.offset2 + 3) & ~3) + 4; |
| p->to.type = D_REG; |
| p->to.reg = 2; |
| |
| // MOVW.LO R14, R3 |
| p = appendp(p); |
| p->as = AMOVW; |
| p->scond = C_SCOND_LO; |
| p->from.type = D_REG; |
| p->from.reg = REGLINK; |
| p->to.type = D_REG; |
| p->to.reg = 3; |
| |
| // BL.LO runtime.morestack(SB) // modifies LR |
| p = appendp(p); |
| p->as = ABL; |
| p->scond = C_SCOND_LO; |
| p->to.type = D_BRANCH; |
| p->to.sym = symmorestack; |
| p->cond = pmorestack; |
| |
| // MOVW.W R14,$-autosize(SP) |
| p = appendp(p); |
| p->as = AMOVW; |
| p->scond |= C_WBIT; |
| p->from.type = D_REG; |
| p->from.reg = REGLINK; |
| p->to.type = D_OREG; |
| p->to.offset = -autosize; |
| p->to.reg = REGSP; |
| } else { // > StackBig |
| // MOVW $autosize, R1 |
| // MOVW $args, R2 |
| // MOVW R14, R3 |
| // BL runtime.morestack(SB) // modifies LR |
| // MOVW.W R14,$-autosize(SP) |
| |
| // MOVW $autosize, R1 |
| p = appendp(p); |
| p->as = AMOVW; |
| p->from.type = D_CONST; |
| p->from.offset = autosize; |
| p->to.type = D_REG; |
| p->to.reg = 1; |
| |
| // MOVW $args +4, R2 |
| // also need to store the extra 4 bytes. |
| p = appendp(p); |
| p->as = AMOVW; |
| p->from.type = D_CONST; |
| p->from.offset = ((curtext->to.offset2 + 3) & ~3) + 4; |
| p->to.type = D_REG; |
| p->to.reg = 2; |
| |
| // MOVW R14, R3 |
| p = appendp(p); |
| p->as = AMOVW; |
| p->from.type = D_REG; |
| p->from.reg = REGLINK; |
| p->to.type = D_REG; |
| p->to.reg = 3; |
| |
| // BL runtime.morestack(SB) // modifies LR |
| p = appendp(p); |
| p->as = ABL; |
| p->to.type = D_BRANCH; |
| p->to.sym = symmorestack; |
| p->cond = pmorestack; |
| |
| // MOVW.W R14,$-autosize(SP) |
| p = appendp(p); |
| p->as = AMOVW; |
| p->scond |= C_WBIT; |
| p->from.type = D_REG; |
| p->from.reg = REGLINK; |
| p->to.type = D_OREG; |
| p->to.offset = -autosize; |
| p->to.reg = REGSP; |
| } |
| break; |
| |
| case ARET: |
| nocache(p); |
| foreign = seenthumb && curtext->from.sym != S && (curtext->from.sym->foreign || curtext->from.sym->fnptr); |
| // print("%s %d %d\n", curtext->from.sym->name, curtext->from.sym->foreign, curtext->from.sym->fnptr); |
| if(p->from.type == D_CONST) |
| goto become; |
| if(curtext->mark & LEAF) { |
| if(!autosize) { |
| if(thumb){ |
| p = fnret(p, REGLINK, foreign, p); |
| break; |
| } |
| // if(foreign) print("ABXRET 1 %s\n", curtext->from.sym->name); |
| p->as = foreign ? ABXRET : AB; |
| p->from = zprg.from; |
| p->to.type = D_OREG; |
| p->to.offset = 0; |
| p->to.reg = REGLINK; |
| break; |
| } |
| } |
| if(thumb){ |
| if(curtext->mark & LEAF){ |
| if(autosize){ |
| p->as = AADD; |
| p->from.type = D_CONST; |
| p->from.offset = autosize; |
| p->to.type = D_REG; |
| p->to.reg = REGSP; |
| q = nil; |
| } |
| else |
| q = p; |
| q = fnret(q, REGLINK, foreign, p); |
| if(q != p) |
| p->link = q; |
| } |
| else{ |
| p->as = AMOVW; |
| p->from.type = D_OREG; |
| p->from.name = D_NONE; |
| p->from.reg = REGSP; |
| p->from.offset = 0; |
| p->to.type = D_REG; |
| p->to.reg = REGTMPT-1; |
| if(autosize){ |
| q = prg(); |
| q->as = AADD; |
| q->from.type = D_CONST; |
| q->from.offset = autosize; |
| q->to.type = D_REG; |
| q->to.reg = REGSP; |
| q->link = p->link; |
| p->link = q; |
| } |
| else |
| q = p; |
| q1 = fnret(nil, REGTMPT-1, foreign, p); |
| q1->link = q->link; |
| q->link = q1; |
| } |
| break; |
| } |
| if(foreign) { |
| // if(foreign) print("ABXRET 3 %s\n", curtext->from.sym->name); |
| #define R 1 |
| p->as = AMOVW; |
| p->from.type = D_OREG; |
| p->from.name = D_NONE; |
| p->from.reg = REGSP; |
| p->from.offset = 0; |
| p->to.type = D_REG; |
| p->to.reg = R; |
| q = prg(); |
| q->as = AADD; |
| q->scond = p->scond; |
| q->line = p->line; |
| q->from.type = D_CONST; |
| q->from.offset = autosize; |
| q->to.type = D_REG; |
| q->to.reg = REGSP; |
| q->link = p->link; |
| p->link = q; |
| q1 = prg(); |
| q1->as = ABXRET; |
| q1->scond = p->scond; |
| q1->line = p->line; |
| q1->to.type = D_OREG; |
| q1->to.offset = 0; |
| q1->to.reg = R; |
| q1->link = q->link; |
| q->link = q1; |
| #undef R |
| } |
| else { |
| p->as = AMOVW; |
| p->scond |= C_PBIT; |
| p->from.type = D_OREG; |
| p->from.offset = autosize; |
| p->from.reg = REGSP; |
| p->to.type = D_REG; |
| p->to.reg = REGPC; |
| } |
| break; |
| |
| become: |
| if(foreign){ |
| diag("foreign become - help"); |
| break; |
| } |
| if(thumb){ |
| diag("thumb become - help"); |
| break; |
| } |
| print("arm become\n"); |
| if(curtext->mark & LEAF) { |
| |
| if(!autosize) { |
| p->as = AB; |
| p->from = zprg.from; |
| break; |
| } |
| } |
| q = prg(); |
| q->scond = p->scond; |
| q->line = p->line; |
| q->as = AB; |
| q->from = zprg.from; |
| q->to = p->to; |
| q->cond = p->cond; |
| q->link = p->link; |
| p->link = q; |
| if(thumb){ |
| q1 = prg(); |
| q1->line = p->line; |
| q1->as = AADD; |
| q1->from.type = D_CONST; |
| q1->from.offset = autosize; |
| q1->to.type = D_REG; |
| q1->to.reg = REGSP; |
| p->as = AMOVW; |
| p->line = p->line; |
| p->from.type = D_OREG; |
| p->from.name = D_NONE; |
| p->from.reg = REGSP; |
| p->from.offset = 0; |
| p->to.type = D_REG; |
| p->to.reg = REGTMPT-1; |
| q1->link = q; |
| p->link = q1; |
| q2 = movrr(nil, REGTMPT-1, REGLINK, p); |
| q2->link = q; |
| q1->link = q2; |
| break; |
| } |
| p->as = AMOVW; |
| p->scond |= C_PBIT; |
| p->from = zprg.from; |
| p->from.type = D_OREG; |
| p->from.offset = autosize; |
| p->from.reg = REGSP; |
| p->to = zprg.to; |
| p->to.type = D_REG; |
| p->to.reg = REGLINK; |
| |
| break; |
| |
| case ADIV: |
| case ADIVU: |
| case AMOD: |
| case AMODU: |
| if(debug['M']) |
| break; |
| if(p->from.type != D_REG) |
| break; |
| if(p->to.type != D_REG) |
| break; |
| q1 = p; |
| |
| /* MOV a,4(SP) */ |
| q = prg(); |
| q->link = p->link; |
| p->link = q; |
| p = q; |
| |
| p->as = AMOVW; |
| p->line = q1->line; |
| p->from.type = D_REG; |
| p->from.reg = q1->from.reg; |
| p->to.type = D_OREG; |
| p->to.reg = REGSP; |
| p->to.offset = 4; |
| |
| /* MOV b,REGTMP */ |
| q = prg(); |
| q->link = p->link; |
| p->link = q; |
| p = q; |
| |
| p->as = AMOVW; |
| p->line = q1->line; |
| p->from.type = D_REG; |
| p->from.reg = q1->reg; |
| if(q1->reg == NREG) |
| p->from.reg = q1->to.reg; |
| p->to.type = D_REG; |
| p->to.reg = prog_div != UP && prog_div->from.sym->thumb ? REGTMPT : REGTMP; |
| p->to.offset = 0; |
| |
| /* CALL appropriate */ |
| q = prg(); |
| q->link = p->link; |
| p->link = q; |
| p = q; |
| |
| #ifdef CALLEEBX |
| p->as = ABL; |
| #else |
| if(prog_div != UP && prog_div->from.sym->thumb) |
| p->as = thumb ? ABL : ABX; |
| else |
| p->as = thumb ? ABX : ABL; |
| #endif |
| p->line = q1->line; |
| p->to.type = D_BRANCH; |
| p->cond = p; |
| switch(o) { |
| case ADIV: |
| p->cond = prog_div; |
| p->to.sym = sym_div; |
| break; |
| case ADIVU: |
| p->cond = prog_divu; |
| p->to.sym = sym_divu; |
| break; |
| case AMOD: |
| p->cond = prog_mod; |
| p->to.sym = sym_mod; |
| break; |
| case AMODU: |
| p->cond = prog_modu; |
| p->to.sym = sym_modu; |
| break; |
| } |
| |
| /* MOV REGTMP, b */ |
| q = prg(); |
| q->link = p->link; |
| p->link = q; |
| p = q; |
| |
| p->as = AMOVW; |
| p->line = q1->line; |
| p->from.type = D_REG; |
| p->from.reg = prog_div != UP && prog_div->from.sym->thumb ? REGTMPT : REGTMP; |
| p->from.offset = 0; |
| p->to.type = D_REG; |
| p->to.reg = q1->to.reg; |
| |
| /* ADD $8,SP */ |
| q = prg(); |
| q->link = p->link; |
| p->link = q; |
| p = q; |
| |
| p->as = AADD; |
| p->from.type = D_CONST; |
| p->from.reg = NREG; |
| p->from.offset = 8; |
| p->reg = NREG; |
| p->to.type = D_REG; |
| p->to.reg = REGSP; |
| |
| /* SUB $8,SP */ |
| q1->as = ASUB; |
| q1->from.type = D_CONST; |
| q1->from.offset = 8; |
| q1->from.reg = NREG; |
| q1->reg = NREG; |
| q1->to.type = D_REG; |
| q1->to.reg = REGSP; |
| |
| break; |
| case AMOVW: |
| if(thumb){ |
| Adr *a = &p->from; |
| |
| if(a->type == D_CONST && ((a->name == D_NONE && a->reg == REGSP) || a->name == D_AUTO || a->name == D_PARAM) && (a->offset & 3)) |
| diag("SP offset not multiple of 4"); |
| } |
| break; |
| case AMOVB: |
| case AMOVBU: |
| case AMOVH: |
| case AMOVHU: |
| if(thumb){ |
| if(p->from.type == D_OREG && (p->from.name == D_AUTO || p->from.name == D_PARAM || (p->from.name == D_CONST && p->from.reg == REGSP))){ |
| q = prg(); |
| *q = *p; |
| if(p->from.name == D_AUTO) |
| q->from.offset += autosize; |
| else if(p->from.name == D_PARAM) |
| q->from.offset += autosize+4; |
| q->from.name = D_NONE; |
| q->from.reg = REGTMPT; |
| p = movrr(p, REGSP, REGTMPT, p); |
| q->link = p->link; |
| p->link = q; |
| } |
| if(p->to.type == D_OREG && (p->to.name == D_AUTO || p->to.name == D_PARAM || (p->to.name == D_CONST && p->to.reg == REGSP))){ |
| q = prg(); |
| *q = *p; |
| if(p->to.name == D_AUTO) |
| q->to.offset += autosize; |
| else if(p->to.name == D_PARAM) |
| q->to.offset += autosize+4; |
| q->to.name = D_NONE; |
| q->to.reg = REGTMPT; |
| p = movrr(p, REGSP, REGTMPT, p); |
| q->link = p->link; |
| p->link = q; |
| if(q->to.offset < 0 || q->to.offset > 255){ // complicated |
| p->to.reg = REGTMPT+1; // mov sp, r8 |
| q1 = prg(); |
| q1->line = p->line; |
| q1->as = AMOVW; |
| q1->from.type = D_CONST; |
| q1->from.offset = q->to.offset; |
| q1->to.type = D_REG; |
| q1->to.reg = REGTMPT; // mov $o, r7 |
| p->link = q1; |
| q1->link = q; |
| q1 = prg(); |
| q1->line = p->line; |
| q1->as = AADD; |
| q1->from.type = D_REG; |
| q1->from.reg = REGTMPT+1; |
| q1->to.type = D_REG; |
| q1->to.reg = REGTMPT; // add r8, r7 |
| p->link->link = q1; |
| q1->link = q; |
| q->to.offset = 0; // mov* r, 0(r7) |
| /* phew */ |
| } |
| } |
| } |
| break; |
| case AMOVM: |
| if(thumb){ |
| if(p->from.type == D_OREG){ |
| if(p->from.offset == 0) |
| p->from.type = D_REG; |
| else |
| diag("non-zero AMOVM offset"); |
| } |
| else if(p->to.type == D_OREG){ |
| if(p->to.offset == 0) |
| p->to.type = D_REG; |
| else |
| diag("non-zero AMOVM offset"); |
| } |
| } |
| break; |
| case AB: |
| if(thumb && p->to.type == D_OREG){ |
| if(p->to.offset == 0){ |
| p->as = AMOVW; |
| p->from.type = D_REG; |
| p->from.reg = p->to.reg; |
| p->to.type = D_REG; |
| p->to.reg = REGPC; |
| } |
| else{ |
| p->as = AADD; |
| p->from.type = D_CONST; |
| p->from.offset = p->to.offset; |
| p->reg = p->to.reg; |
| p->to.type = D_REG; |
| p->to.reg = REGTMPT-1; |
| q = prg(); |
| q->as = AMOVW; |
| q->line = p->line; |
| q->from.type = D_REG; |
| q->from.reg = REGTMPT-1; |
| q->to.type = D_REG; |
| q->to.reg = REGPC; |
| q->link = p->link; |
| p->link = q; |
| } |
| } |
| if(seenthumb && !thumb && p->to.type == D_OREG && p->to.reg == REGLINK){ |
| // print("warn %s: b (R%d) assuming a return\n", curtext->from.sym->name, p->to.reg); |
| p->as = ABXRET; |
| } |
| break; |
| case ABL: |
| case ABX: |
| if(thumb && p->to.type == D_OREG){ |
| if(p->to.offset == 0){ |
| p->as = o; |
| p->from.type = D_NONE; |
| p->to.type = D_REG; |
| } |
| else{ |
| p->as = AADD; |
| p->from.type = D_CONST; |
| p->from.offset = p->to.offset; |
| p->reg = p->to.reg; |
| p->to.type = D_REG; |
| p->to.reg = REGTMPT-1; |
| q = prg(); |
| q->as = o; |
| q->line = p->line; |
| q->from.type = D_NONE; |
| q->to.type = D_REG; |
| q->to.reg = REGTMPT-1; |
| q->link = p->link; |
| p->link = q; |
| } |
| } |
| break; |
| } |
| } |
| } |
| |
| static void |
| sigdiv(char *n) |
| { |
| Sym *s; |
| |
| s = lookup(n, 0); |
| if(s->type == STEXT){ |
| if(s->sig == 0) |
| s->sig = SIGNINTERN; |
| } |
| else if(s->type == 0 || s->type == SXREF) |
| s->type = SUNDEF; |
| } |
| |
| void |
| divsig(void) |
| { |
| sigdiv("_div"); |
| sigdiv("_divu"); |
| sigdiv("_mod"); |
| sigdiv("_modu"); |
| } |
| |
| static void |
| sdiv(Sym *s) |
| { |
| if(s->type == 0 || s->type == SXREF){ |
| /* undefsym(s); */ |
| s->type = SXREF; |
| if(s->sig == 0) |
| s->sig = SIGNINTERN; |
| s->subtype = SIMPORT; |
| } |
| else if(s->type != STEXT) |
| diag("undefined: %s", s->name); |
| } |
| |
| void |
| initdiv(void) |
| { |
| Sym *s2, *s3, *s4, *s5; |
| Prog *p; |
| |
| if(prog_div != P) |
| return; |
| sym_div = s2 = lookup("_div", 0); |
| sym_divu = s3 = lookup("_divu", 0); |
| sym_mod = s4 = lookup("_mod", 0); |
| sym_modu = s5 = lookup("_modu", 0); |
| if(dlm) { |
| sdiv(s2); if(s2->type == SXREF) prog_div = UP; |
| sdiv(s3); if(s3->type == SXREF) prog_divu = UP; |
| sdiv(s4); if(s4->type == SXREF) prog_mod = UP; |
| sdiv(s5); if(s5->type == SXREF) prog_modu = UP; |
| } |
| for(p = firstp; p != P; p = p->link) |
| if(p->as == ATEXT) { |
| if(p->from.sym == s2) |
| prog_div = p; |
| if(p->from.sym == s3) |
| prog_divu = p; |
| if(p->from.sym == s4) |
| prog_mod = p; |
| if(p->from.sym == s5) |
| prog_modu = p; |
| } |
| if(prog_div == P) { |
| diag("undefined: %s", s2->name); |
| prog_div = curtext; |
| } |
| if(prog_divu == P) { |
| diag("undefined: %s", s3->name); |
| prog_divu = curtext; |
| } |
| if(prog_mod == P) { |
| diag("undefined: %s", s4->name); |
| prog_mod = curtext; |
| } |
| if(prog_modu == P) { |
| diag("undefined: %s", s5->name); |
| prog_modu = curtext; |
| } |
| } |
| |
| static void |
| setdiv(int as) |
| { |
| Prog *p = nil; |
| |
| switch(as){ |
| case ADIV: p = prog_div; break; |
| case ADIVU: p = prog_divu; break; |
| case AMOD: p = prog_mod; break; |
| case AMODU: p = prog_modu; break; |
| } |
| if(p != UP && thumb != p->from.sym->thumb) |
| p->from.sym->foreign = 1; |
| } |
| |
| void |
| nocache(Prog *p) |
| { |
| p->optab = 0; |
| p->from.class = 0; |
| p->to.class = 0; |
| } |