github.com/4ad/go@v0.0.0-20161219182952-69a12818b605/src/cmd/internal/obj/arm/obj5.go (about) 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.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 package arm 32 33 import ( 34 "cmd/internal/obj" 35 "cmd/internal/sys" 36 "fmt" 37 "log" 38 "math" 39 ) 40 41 var progedit_tlsfallback *obj.LSym 42 43 func progedit(ctxt *obj.Link, p *obj.Prog) { 44 p.From.Class = 0 45 p.To.Class = 0 46 47 // Rewrite B/BL to symbol as TYPE_BRANCH. 48 switch p.As { 49 case AB, 50 ABL, 51 obj.ADUFFZERO, 52 obj.ADUFFCOPY: 53 if p.To.Type == obj.TYPE_MEM && (p.To.Name == obj.NAME_EXTERN || p.To.Name == obj.NAME_STATIC) && p.To.Sym != nil { 54 p.To.Type = obj.TYPE_BRANCH 55 } 56 } 57 58 // Replace TLS register fetches on older ARM processors. 59 switch p.As { 60 // Treat MRC 15, 0, <reg>, C13, C0, 3 specially. 61 case AMRC: 62 if p.To.Offset&0xffff0fff == 0xee1d0f70 { 63 // Because the instruction might be rewritten to a BL which returns in R0 64 // the register must be zero. 65 if p.To.Offset&0xf000 != 0 { 66 ctxt.Diag("%v: TLS MRC instruction must write to R0 as it might get translated into a BL instruction", p.Line()) 67 } 68 69 if ctxt.Goarm < 7 { 70 // Replace it with BL runtime.read_tls_fallback(SB) for ARM CPUs that lack the tls extension. 71 if progedit_tlsfallback == nil { 72 progedit_tlsfallback = obj.Linklookup(ctxt, "runtime.read_tls_fallback", 0) 73 } 74 75 // MOVW LR, R11 76 p.As = AMOVW 77 78 p.From.Type = obj.TYPE_REG 79 p.From.Reg = REGLINK 80 p.To.Type = obj.TYPE_REG 81 p.To.Reg = REGTMP 82 83 // BL runtime.read_tls_fallback(SB) 84 p = obj.Appendp(ctxt, p) 85 86 p.As = ABL 87 p.To.Type = obj.TYPE_BRANCH 88 p.To.Sym = progedit_tlsfallback 89 p.To.Offset = 0 90 91 // MOVW R11, LR 92 p = obj.Appendp(ctxt, p) 93 94 p.As = AMOVW 95 p.From.Type = obj.TYPE_REG 96 p.From.Reg = REGTMP 97 p.To.Type = obj.TYPE_REG 98 p.To.Reg = REGLINK 99 break 100 } 101 } 102 103 // Otherwise, MRC/MCR instructions need no further treatment. 104 p.As = AWORD 105 } 106 107 // Rewrite float constants to values stored in memory. 108 switch p.As { 109 case AMOVF: 110 if p.From.Type == obj.TYPE_FCONST && chipfloat5(ctxt, p.From.Val.(float64)) < 0 && (chipzero5(ctxt, p.From.Val.(float64)) < 0 || p.Scond&C_SCOND != C_SCOND_NONE) { 111 f32 := float32(p.From.Val.(float64)) 112 i32 := math.Float32bits(f32) 113 literal := fmt.Sprintf("$f32.%08x", i32) 114 s := obj.Linklookup(ctxt, literal, 0) 115 p.From.Type = obj.TYPE_MEM 116 p.From.Sym = s 117 p.From.Name = obj.NAME_EXTERN 118 p.From.Offset = 0 119 } 120 121 case AMOVD: 122 if p.From.Type == obj.TYPE_FCONST && chipfloat5(ctxt, p.From.Val.(float64)) < 0 && (chipzero5(ctxt, p.From.Val.(float64)) < 0 || p.Scond&C_SCOND != C_SCOND_NONE) { 123 i64 := math.Float64bits(p.From.Val.(float64)) 124 literal := fmt.Sprintf("$f64.%016x", i64) 125 s := obj.Linklookup(ctxt, literal, 0) 126 p.From.Type = obj.TYPE_MEM 127 p.From.Sym = s 128 p.From.Name = obj.NAME_EXTERN 129 p.From.Offset = 0 130 } 131 } 132 133 if ctxt.Flag_dynlink { 134 rewriteToUseGot(ctxt, p) 135 } 136 } 137 138 // Rewrite p, if necessary, to access global data via the global offset table. 139 func rewriteToUseGot(ctxt *obj.Link, p *obj.Prog) { 140 if p.As == obj.ADUFFCOPY || p.As == obj.ADUFFZERO { 141 // ADUFFxxx $offset 142 // becomes 143 // MOVW runtime.duffxxx@GOT, R9 144 // ADD $offset, R9 145 // CALL (R9) 146 var sym *obj.LSym 147 if p.As == obj.ADUFFZERO { 148 sym = obj.Linklookup(ctxt, "runtime.duffzero", 0) 149 } else { 150 sym = obj.Linklookup(ctxt, "runtime.duffcopy", 0) 151 } 152 offset := p.To.Offset 153 p.As = AMOVW 154 p.From.Type = obj.TYPE_MEM 155 p.From.Name = obj.NAME_GOTREF 156 p.From.Sym = sym 157 p.To.Type = obj.TYPE_REG 158 p.To.Reg = REG_R9 159 p.To.Name = obj.NAME_NONE 160 p.To.Offset = 0 161 p.To.Sym = nil 162 p1 := obj.Appendp(ctxt, p) 163 p1.As = AADD 164 p1.From.Type = obj.TYPE_CONST 165 p1.From.Offset = offset 166 p1.To.Type = obj.TYPE_REG 167 p1.To.Reg = REG_R9 168 p2 := obj.Appendp(ctxt, p1) 169 p2.As = obj.ACALL 170 p2.To.Type = obj.TYPE_MEM 171 p2.To.Reg = REG_R9 172 return 173 } 174 175 // We only care about global data: NAME_EXTERN means a global 176 // symbol in the Go sense, and p.Sym.Local is true for a few 177 // internally defined symbols. 178 if p.From.Type == obj.TYPE_ADDR && p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local { 179 // MOVW $sym, Rx becomes MOVW sym@GOT, Rx 180 // MOVW $sym+<off>, Rx becomes MOVW sym@GOT, Rx; ADD <off>, Rx 181 if p.As != AMOVW { 182 ctxt.Diag("do not know how to handle TYPE_ADDR in %v with -dynlink", p) 183 } 184 if p.To.Type != obj.TYPE_REG { 185 ctxt.Diag("do not know how to handle LEAQ-type insn to non-register in %v with -dynlink", p) 186 } 187 p.From.Type = obj.TYPE_MEM 188 p.From.Name = obj.NAME_GOTREF 189 if p.From.Offset != 0 { 190 q := obj.Appendp(ctxt, p) 191 q.As = AADD 192 q.From.Type = obj.TYPE_CONST 193 q.From.Offset = p.From.Offset 194 q.To = p.To 195 p.From.Offset = 0 196 } 197 } 198 if p.From3 != nil && p.From3.Name == obj.NAME_EXTERN { 199 ctxt.Diag("don't know how to handle %v with -dynlink", p) 200 } 201 var source *obj.Addr 202 // MOVx sym, Ry becomes MOVW sym@GOT, R9; MOVx (R9), Ry 203 // MOVx Ry, sym becomes MOVW sym@GOT, R9; MOVx Ry, (R9) 204 // An addition may be inserted between the two MOVs if there is an offset. 205 if p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local { 206 if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local { 207 ctxt.Diag("cannot handle NAME_EXTERN on both sides in %v with -dynlink", p) 208 } 209 source = &p.From 210 } else if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local { 211 source = &p.To 212 } else { 213 return 214 } 215 if p.As == obj.ATEXT || p.As == obj.AFUNCDATA || p.As == obj.ACALL || p.As == obj.ARET || p.As == obj.AJMP { 216 return 217 } 218 if source.Sym.Type == obj.STLSBSS { 219 return 220 } 221 if source.Type != obj.TYPE_MEM { 222 ctxt.Diag("don't know how to handle %v with -dynlink", p) 223 } 224 p1 := obj.Appendp(ctxt, p) 225 p2 := obj.Appendp(ctxt, p1) 226 227 p1.As = AMOVW 228 p1.From.Type = obj.TYPE_MEM 229 p1.From.Sym = source.Sym 230 p1.From.Name = obj.NAME_GOTREF 231 p1.To.Type = obj.TYPE_REG 232 p1.To.Reg = REG_R9 233 234 p2.As = p.As 235 p2.From = p.From 236 p2.To = p.To 237 if p.From.Name == obj.NAME_EXTERN { 238 p2.From.Reg = REG_R9 239 p2.From.Name = obj.NAME_NONE 240 p2.From.Sym = nil 241 } else if p.To.Name == obj.NAME_EXTERN { 242 p2.To.Reg = REG_R9 243 p2.To.Name = obj.NAME_NONE 244 p2.To.Sym = nil 245 } else { 246 return 247 } 248 obj.Nopout(p) 249 } 250 251 // Prog.mark 252 const ( 253 FOLL = 1 << 0 254 LABEL = 1 << 1 255 LEAF = 1 << 2 256 ) 257 258 func preprocess(ctxt *obj.Link, cursym *obj.LSym) { 259 autosize := int32(0) 260 261 ctxt.Cursym = cursym 262 263 if cursym.Text == nil || cursym.Text.Link == nil { 264 return 265 } 266 267 softfloat(ctxt, cursym) 268 269 p := cursym.Text 270 autoffset := int32(p.To.Offset) 271 if autoffset < 0 { 272 autoffset = 0 273 } 274 cursym.Locals = autoffset 275 cursym.Args = p.To.Val.(int32) 276 277 /* 278 * find leaf subroutines 279 * strip NOPs 280 * expand RET 281 * expand BECOME pseudo 282 */ 283 var q1 *obj.Prog 284 var q *obj.Prog 285 for p := cursym.Text; p != nil; p = p.Link { 286 switch p.As { 287 case obj.ATEXT: 288 p.Mark |= LEAF 289 290 case obj.ARET: 291 break 292 293 case ADIV, ADIVU, AMOD, AMODU: 294 q = p 295 if ctxt.Sym_div == nil { 296 initdiv(ctxt) 297 } 298 cursym.Text.Mark &^= LEAF 299 continue 300 301 case obj.ANOP: 302 q1 = p.Link 303 q.Link = q1 /* q is non-nop */ 304 if q1 != nil { 305 q1.Mark |= p.Mark 306 } 307 continue 308 309 case ABL, 310 ABX, 311 obj.ADUFFZERO, 312 obj.ADUFFCOPY: 313 cursym.Text.Mark &^= LEAF 314 fallthrough 315 316 case AB, 317 ABEQ, 318 ABNE, 319 ABCS, 320 ABHS, 321 ABCC, 322 ABLO, 323 ABMI, 324 ABPL, 325 ABVS, 326 ABVC, 327 ABHI, 328 ABLS, 329 ABGE, 330 ABLT, 331 ABGT, 332 ABLE: 333 q1 = p.Pcond 334 if q1 != nil { 335 for q1.As == obj.ANOP { 336 q1 = q1.Link 337 p.Pcond = q1 338 } 339 } 340 } 341 342 q = p 343 } 344 345 var p1 *obj.Prog 346 var p2 *obj.Prog 347 var q2 *obj.Prog 348 for p := cursym.Text; p != nil; p = p.Link { 349 o := p.As 350 switch o { 351 case obj.ATEXT: 352 autosize = int32(p.To.Offset + 4) 353 if autosize <= 4 { 354 if cursym.Text.Mark&LEAF != 0 { 355 p.To.Offset = -4 356 autosize = 0 357 } 358 } 359 360 if autosize == 0 && cursym.Text.Mark&LEAF == 0 { 361 if ctxt.Debugvlog != 0 { 362 fmt.Fprintf(ctxt.Bso, "save suppressed in: %s\n", cursym.Name) 363 ctxt.Bso.Flush() 364 } 365 366 cursym.Text.Mark |= LEAF 367 } 368 369 if cursym.Text.Mark&LEAF != 0 { 370 cursym.Leaf = true 371 if autosize == 0 { 372 break 373 } 374 } 375 376 if p.From3.Offset&obj.NOSPLIT == 0 { 377 p = stacksplit(ctxt, p, autosize) // emit split check 378 } 379 380 // MOVW.W R14,$-autosize(SP) 381 p = obj.Appendp(ctxt, p) 382 383 p.As = AMOVW 384 p.Scond |= C_WBIT 385 p.From.Type = obj.TYPE_REG 386 p.From.Reg = REGLINK 387 p.To.Type = obj.TYPE_MEM 388 p.To.Offset = int64(-autosize) 389 p.To.Reg = REGSP 390 p.Spadj = autosize 391 392 if cursym.Text.From3.Offset&obj.WRAPPER != 0 { 393 // if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame 394 // 395 // MOVW g_panic(g), R1 396 // CMP $0, R1 397 // B.EQ end 398 // MOVW panic_argp(R1), R2 399 // ADD $(autosize+4), R13, R3 400 // CMP R2, R3 401 // B.NE end 402 // ADD $4, R13, R4 403 // MOVW R4, panic_argp(R1) 404 // end: 405 // NOP 406 // 407 // The NOP is needed to give the jumps somewhere to land. 408 // It is a liblink NOP, not an ARM NOP: it encodes to 0 instruction bytes. 409 410 p = obj.Appendp(ctxt, p) 411 412 p.As = AMOVW 413 p.From.Type = obj.TYPE_MEM 414 p.From.Reg = REGG 415 p.From.Offset = 4 * int64(ctxt.Arch.PtrSize) // G.panic 416 p.To.Type = obj.TYPE_REG 417 p.To.Reg = REG_R1 418 419 p = obj.Appendp(ctxt, p) 420 p.As = ACMP 421 p.From.Type = obj.TYPE_CONST 422 p.From.Offset = 0 423 p.Reg = REG_R1 424 425 p = obj.Appendp(ctxt, p) 426 p.As = ABEQ 427 p.To.Type = obj.TYPE_BRANCH 428 p1 = p 429 430 p = obj.Appendp(ctxt, p) 431 p.As = AMOVW 432 p.From.Type = obj.TYPE_MEM 433 p.From.Reg = REG_R1 434 p.From.Offset = 0 // Panic.argp 435 p.To.Type = obj.TYPE_REG 436 p.To.Reg = REG_R2 437 438 p = obj.Appendp(ctxt, p) 439 p.As = AADD 440 p.From.Type = obj.TYPE_CONST 441 p.From.Offset = int64(autosize) + 4 442 p.Reg = REG_R13 443 p.To.Type = obj.TYPE_REG 444 p.To.Reg = REG_R3 445 446 p = obj.Appendp(ctxt, p) 447 p.As = ACMP 448 p.From.Type = obj.TYPE_REG 449 p.From.Reg = REG_R2 450 p.Reg = REG_R3 451 452 p = obj.Appendp(ctxt, p) 453 p.As = ABNE 454 p.To.Type = obj.TYPE_BRANCH 455 p2 = p 456 457 p = obj.Appendp(ctxt, p) 458 p.As = AADD 459 p.From.Type = obj.TYPE_CONST 460 p.From.Offset = 4 461 p.Reg = REG_R13 462 p.To.Type = obj.TYPE_REG 463 p.To.Reg = REG_R4 464 465 p = obj.Appendp(ctxt, p) 466 p.As = AMOVW 467 p.From.Type = obj.TYPE_REG 468 p.From.Reg = REG_R4 469 p.To.Type = obj.TYPE_MEM 470 p.To.Reg = REG_R1 471 p.To.Offset = 0 // Panic.argp 472 473 p = obj.Appendp(ctxt, p) 474 475 p.As = obj.ANOP 476 p1.Pcond = p 477 p2.Pcond = p 478 } 479 480 case obj.ARET: 481 nocache(p) 482 if cursym.Text.Mark&LEAF != 0 { 483 if autosize == 0 { 484 p.As = AB 485 p.From = obj.Addr{} 486 if p.To.Sym != nil { // retjmp 487 p.To.Type = obj.TYPE_BRANCH 488 } else { 489 p.To.Type = obj.TYPE_MEM 490 p.To.Offset = 0 491 p.To.Reg = REGLINK 492 } 493 494 break 495 } 496 } 497 498 p.As = AMOVW 499 p.Scond |= C_PBIT 500 p.From.Type = obj.TYPE_MEM 501 p.From.Offset = int64(autosize) 502 p.From.Reg = REGSP 503 p.To.Type = obj.TYPE_REG 504 p.To.Reg = REGPC 505 506 // If there are instructions following 507 // this ARET, they come from a branch 508 // with the same stackframe, so no spadj. 509 if p.To.Sym != nil { // retjmp 510 p.To.Reg = REGLINK 511 q2 = obj.Appendp(ctxt, p) 512 q2.As = AB 513 q2.To.Type = obj.TYPE_BRANCH 514 q2.To.Sym = p.To.Sym 515 p.To.Sym = nil 516 p = q2 517 } 518 519 case AADD: 520 if p.From.Type == obj.TYPE_CONST && p.From.Reg == 0 && p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP { 521 p.Spadj = int32(-p.From.Offset) 522 } 523 524 case ASUB: 525 if p.From.Type == obj.TYPE_CONST && p.From.Reg == 0 && p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP { 526 p.Spadj = int32(p.From.Offset) 527 } 528 529 case ADIV, ADIVU, AMOD, AMODU: 530 if cursym.Text.From3.Offset&obj.NOSPLIT != 0 { 531 ctxt.Diag("cannot divide in NOSPLIT function") 532 } 533 if ctxt.Debugdivmod != 0 { 534 break 535 } 536 if p.From.Type != obj.TYPE_REG { 537 break 538 } 539 if p.To.Type != obj.TYPE_REG { 540 break 541 } 542 543 // Make copy because we overwrite p below. 544 q1 := *p 545 if q1.Reg == REGTMP || q1.Reg == 0 && q1.To.Reg == REGTMP { 546 ctxt.Diag("div already using REGTMP: %v", p) 547 } 548 549 /* MOV m(g),REGTMP */ 550 p.As = AMOVW 551 p.Lineno = q1.Lineno 552 p.From.Type = obj.TYPE_MEM 553 p.From.Reg = REGG 554 p.From.Offset = 6 * 4 // offset of g.m 555 p.Reg = 0 556 p.To.Type = obj.TYPE_REG 557 p.To.Reg = REGTMP 558 559 /* MOV a,m_divmod(REGTMP) */ 560 p = obj.Appendp(ctxt, p) 561 p.As = AMOVW 562 p.Lineno = q1.Lineno 563 p.From.Type = obj.TYPE_REG 564 p.From.Reg = q1.From.Reg 565 p.To.Type = obj.TYPE_MEM 566 p.To.Reg = REGTMP 567 p.To.Offset = 8 * 4 // offset of m.divmod 568 569 /* MOV b,REGTMP */ 570 p = obj.Appendp(ctxt, p) 571 p.As = AMOVW 572 p.Lineno = q1.Lineno 573 p.From.Type = obj.TYPE_REG 574 p.From.Reg = q1.Reg 575 if q1.Reg == 0 { 576 p.From.Reg = q1.To.Reg 577 } 578 p.To.Type = obj.TYPE_REG 579 p.To.Reg = REGTMP 580 p.To.Offset = 0 581 582 /* CALL appropriate */ 583 p = obj.Appendp(ctxt, p) 584 p.As = ABL 585 p.Lineno = q1.Lineno 586 p.To.Type = obj.TYPE_BRANCH 587 switch o { 588 case ADIV: 589 p.To.Sym = ctxt.Sym_div 590 591 case ADIVU: 592 p.To.Sym = ctxt.Sym_divu 593 594 case AMOD: 595 p.To.Sym = ctxt.Sym_mod 596 597 case AMODU: 598 p.To.Sym = ctxt.Sym_modu 599 } 600 601 /* MOV REGTMP, b */ 602 p = obj.Appendp(ctxt, p) 603 p.As = AMOVW 604 p.Lineno = q1.Lineno 605 p.From.Type = obj.TYPE_REG 606 p.From.Reg = REGTMP 607 p.From.Offset = 0 608 p.To.Type = obj.TYPE_REG 609 p.To.Reg = q1.To.Reg 610 611 case AMOVW: 612 if (p.Scond&C_WBIT != 0) && p.To.Type == obj.TYPE_MEM && p.To.Reg == REGSP { 613 p.Spadj = int32(-p.To.Offset) 614 } 615 if (p.Scond&C_PBIT != 0) && p.From.Type == obj.TYPE_MEM && p.From.Reg == REGSP && p.To.Reg != REGPC { 616 p.Spadj = int32(-p.From.Offset) 617 } 618 if p.From.Type == obj.TYPE_ADDR && p.From.Reg == REGSP && p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP { 619 p.Spadj = int32(-p.From.Offset) 620 } 621 } 622 } 623 } 624 625 func isfloatreg(a *obj.Addr) bool { 626 return a.Type == obj.TYPE_REG && REG_F0 <= a.Reg && a.Reg <= REG_F15 627 } 628 629 func softfloat(ctxt *obj.Link, cursym *obj.LSym) { 630 if ctxt.Goarm > 5 { 631 return 632 } 633 634 symsfloat := obj.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 } 641 } 642 var next *obj.Prog 643 for p := cursym.Text; p != nil; p = p.Link { 644 switch p.As { 645 case AMOVW: 646 if isfloatreg(&p.To) || isfloatreg(&p.From) { 647 goto soft 648 } 649 goto notsoft 650 651 case AMOVWD, 652 AMOVWF, 653 AMOVDW, 654 AMOVFW, 655 AMOVFD, 656 AMOVDF, 657 AMOVF, 658 AMOVD, 659 ACMPF, 660 ACMPD, 661 AADDF, 662 AADDD, 663 ASUBF, 664 ASUBD, 665 AMULF, 666 AMULD, 667 ADIVF, 668 ADIVD, 669 ASQRTF, 670 ASQRTD, 671 AABSF, 672 AABSD: 673 goto soft 674 675 default: 676 goto notsoft 677 } 678 679 soft: 680 if wasfloat == 0 || (p.Mark&LABEL != 0) { 681 next = ctxt.NewProg() 682 *next = *p 683 684 // BL _sfloat(SB) 685 *p = obj.Prog{} 686 p.Ctxt = ctxt 687 p.Link = next 688 p.As = ABL 689 p.To.Type = obj.TYPE_BRANCH 690 p.To.Sym = symsfloat 691 p.Lineno = next.Lineno 692 693 p = next 694 wasfloat = 1 695 } 696 697 continue 698 699 notsoft: 700 wasfloat = 0 701 } 702 } 703 704 func stacksplit(ctxt *obj.Link, p *obj.Prog, framesize int32) *obj.Prog { 705 // MOVW g_stackguard(g), R1 706 p = obj.Appendp(ctxt, p) 707 708 p.As = AMOVW 709 p.From.Type = obj.TYPE_MEM 710 p.From.Reg = REGG 711 p.From.Offset = 2 * int64(ctxt.Arch.PtrSize) // G.stackguard0 712 if ctxt.Cursym.Cfunc { 713 p.From.Offset = 3 * int64(ctxt.Arch.PtrSize) // G.stackguard1 714 } 715 p.To.Type = obj.TYPE_REG 716 p.To.Reg = REG_R1 717 718 if framesize <= obj.StackSmall { 719 // small stack: SP < stackguard 720 // CMP stackguard, SP 721 p = obj.Appendp(ctxt, p) 722 723 p.As = ACMP 724 p.From.Type = obj.TYPE_REG 725 p.From.Reg = REG_R1 726 p.Reg = REGSP 727 } else if framesize <= obj.StackBig { 728 // large stack: SP-framesize < stackguard-StackSmall 729 // MOVW $-framesize(SP), R2 730 // CMP stackguard, R2 731 p = obj.Appendp(ctxt, p) 732 733 p.As = AMOVW 734 p.From.Type = obj.TYPE_ADDR 735 p.From.Reg = REGSP 736 p.From.Offset = int64(-framesize) 737 p.To.Type = obj.TYPE_REG 738 p.To.Reg = REG_R2 739 740 p = obj.Appendp(ctxt, p) 741 p.As = ACMP 742 p.From.Type = obj.TYPE_REG 743 p.From.Reg = REG_R1 744 p.Reg = REG_R2 745 } else { 746 // Such a large stack we need to protect against wraparound 747 // if SP is close to zero. 748 // SP-stackguard+StackGuard < framesize + (StackGuard-StackSmall) 749 // The +StackGuard on both sides is required to keep the left side positive: 750 // SP is allowed to be slightly below stackguard. See stack.h. 751 // CMP $StackPreempt, R1 752 // MOVW.NE $StackGuard(SP), R2 753 // SUB.NE R1, R2 754 // MOVW.NE $(framesize+(StackGuard-StackSmall)), R3 755 // CMP.NE R3, R2 756 p = obj.Appendp(ctxt, p) 757 758 p.As = ACMP 759 p.From.Type = obj.TYPE_CONST 760 p.From.Offset = int64(uint32(obj.StackPreempt & (1<<32 - 1))) 761 p.Reg = REG_R1 762 763 p = obj.Appendp(ctxt, p) 764 p.As = AMOVW 765 p.From.Type = obj.TYPE_ADDR 766 p.From.Reg = REGSP 767 p.From.Offset = obj.StackGuard 768 p.To.Type = obj.TYPE_REG 769 p.To.Reg = REG_R2 770 p.Scond = C_SCOND_NE 771 772 p = obj.Appendp(ctxt, p) 773 p.As = ASUB 774 p.From.Type = obj.TYPE_REG 775 p.From.Reg = REG_R1 776 p.To.Type = obj.TYPE_REG 777 p.To.Reg = REG_R2 778 p.Scond = C_SCOND_NE 779 780 p = obj.Appendp(ctxt, p) 781 p.As = AMOVW 782 p.From.Type = obj.TYPE_ADDR 783 p.From.Offset = int64(framesize) + (obj.StackGuard - obj.StackSmall) 784 p.To.Type = obj.TYPE_REG 785 p.To.Reg = REG_R3 786 p.Scond = C_SCOND_NE 787 788 p = obj.Appendp(ctxt, p) 789 p.As = ACMP 790 p.From.Type = obj.TYPE_REG 791 p.From.Reg = REG_R3 792 p.Reg = REG_R2 793 p.Scond = C_SCOND_NE 794 } 795 796 // BLS call-to-morestack 797 bls := obj.Appendp(ctxt, p) 798 bls.As = ABLS 799 bls.To.Type = obj.TYPE_BRANCH 800 801 var last *obj.Prog 802 for last = ctxt.Cursym.Text; last.Link != nil; last = last.Link { 803 } 804 805 spfix := obj.Appendp(ctxt, last) 806 spfix.As = obj.ANOP 807 spfix.Spadj = -framesize 808 809 // MOVW LR, R3 810 movw := obj.Appendp(ctxt, spfix) 811 movw.As = AMOVW 812 movw.From.Type = obj.TYPE_REG 813 movw.From.Reg = REGLINK 814 movw.To.Type = obj.TYPE_REG 815 movw.To.Reg = REG_R3 816 817 bls.Pcond = movw 818 819 // BL runtime.morestack 820 call := obj.Appendp(ctxt, movw) 821 call.As = obj.ACALL 822 call.To.Type = obj.TYPE_BRANCH 823 morestack := "runtime.morestack" 824 switch { 825 case ctxt.Cursym.Cfunc: 826 morestack = "runtime.morestackc" 827 case ctxt.Cursym.Text.From3.Offset&obj.NEEDCTXT == 0: 828 morestack = "runtime.morestack_noctxt" 829 } 830 call.To.Sym = obj.Linklookup(ctxt, morestack, 0) 831 832 // B start 833 b := obj.Appendp(ctxt, call) 834 b.As = obj.AJMP 835 b.To.Type = obj.TYPE_BRANCH 836 b.Pcond = ctxt.Cursym.Text.Link 837 b.Spadj = +framesize 838 839 return bls 840 } 841 842 func initdiv(ctxt *obj.Link) { 843 if ctxt.Sym_div != nil { 844 return 845 } 846 ctxt.Sym_div = obj.Linklookup(ctxt, "_div", 0) 847 ctxt.Sym_divu = obj.Linklookup(ctxt, "_divu", 0) 848 ctxt.Sym_mod = obj.Linklookup(ctxt, "_mod", 0) 849 ctxt.Sym_modu = obj.Linklookup(ctxt, "_modu", 0) 850 } 851 852 func follow(ctxt *obj.Link, s *obj.LSym) { 853 ctxt.Cursym = s 854 855 firstp := ctxt.NewProg() 856 lastp := firstp 857 xfol(ctxt, s.Text, &lastp) 858 lastp.Link = nil 859 s.Text = firstp.Link 860 } 861 862 func relinv(a obj.As) obj.As { 863 switch a { 864 case ABEQ: 865 return ABNE 866 case ABNE: 867 return ABEQ 868 case ABCS: 869 return ABCC 870 case ABHS: 871 return ABLO 872 case ABCC: 873 return ABCS 874 case ABLO: 875 return ABHS 876 case ABMI: 877 return ABPL 878 case ABPL: 879 return ABMI 880 case ABVS: 881 return ABVC 882 case ABVC: 883 return ABVS 884 case ABHI: 885 return ABLS 886 case ABLS: 887 return ABHI 888 case ABGE: 889 return ABLT 890 case ABLT: 891 return ABGE 892 case ABGT: 893 return ABLE 894 case ABLE: 895 return ABGT 896 } 897 898 log.Fatalf("unknown relation: %s", Anames[a]) 899 return 0 900 } 901 902 func xfol(ctxt *obj.Link, p *obj.Prog, last **obj.Prog) { 903 var q *obj.Prog 904 var r *obj.Prog 905 var i int 906 907 loop: 908 if p == nil { 909 return 910 } 911 a := p.As 912 if a == AB { 913 q = p.Pcond 914 if q != nil && q.As != obj.ATEXT { 915 p.Mark |= FOLL 916 p = q 917 if p.Mark&FOLL == 0 { 918 goto loop 919 } 920 } 921 } 922 923 if p.Mark&FOLL != 0 { 924 i = 0 925 q = p 926 for ; i < 4; i, q = i+1, q.Link { 927 if q == *last || q == nil { 928 break 929 } 930 a = q.As 931 if a == obj.ANOP { 932 i-- 933 continue 934 } 935 936 if a == AB || (a == obj.ARET && q.Scond == C_SCOND_NONE) || a == ARFE || a == obj.AUNDEF { 937 goto copy 938 } 939 if q.Pcond == nil || (q.Pcond.Mark&FOLL != 0) { 940 continue 941 } 942 if a != ABEQ && a != ABNE { 943 continue 944 } 945 946 copy: 947 for { 948 r = ctxt.NewProg() 949 *r = *p 950 if r.Mark&FOLL == 0 { 951 fmt.Printf("can't happen 1\n") 952 } 953 r.Mark |= FOLL 954 if p != q { 955 p = p.Link 956 (*last).Link = r 957 *last = r 958 continue 959 } 960 961 (*last).Link = r 962 *last = r 963 if a == AB || (a == obj.ARET && q.Scond == C_SCOND_NONE) || a == ARFE || a == obj.AUNDEF { 964 return 965 } 966 r.As = ABNE 967 if a == ABNE { 968 r.As = ABEQ 969 } 970 r.Pcond = p.Link 971 r.Link = p.Pcond 972 if r.Link.Mark&FOLL == 0 { 973 xfol(ctxt, r.Link, last) 974 } 975 if r.Pcond.Mark&FOLL == 0 { 976 fmt.Printf("can't happen 2\n") 977 } 978 return 979 } 980 } 981 982 a = AB 983 q = ctxt.NewProg() 984 q.As = a 985 q.Lineno = p.Lineno 986 q.To.Type = obj.TYPE_BRANCH 987 q.To.Offset = p.Pc 988 q.Pcond = p 989 p = q 990 } 991 992 p.Mark |= FOLL 993 (*last).Link = p 994 *last = p 995 if a == AB || (a == obj.ARET && p.Scond == C_SCOND_NONE) || a == ARFE || a == obj.AUNDEF { 996 return 997 } 998 999 if p.Pcond != nil { 1000 if a != ABL && a != ABX && p.Link != nil { 1001 q = obj.Brchain(ctxt, p.Link) 1002 if a != obj.ATEXT { 1003 if q != nil && (q.Mark&FOLL != 0) { 1004 p.As = relinv(a) 1005 p.Link = p.Pcond 1006 p.Pcond = q 1007 } 1008 } 1009 1010 xfol(ctxt, p.Link, last) 1011 q = obj.Brchain(ctxt, p.Pcond) 1012 if q == nil { 1013 q = p.Pcond 1014 } 1015 if q.Mark&FOLL != 0 { 1016 p.Pcond = q 1017 return 1018 } 1019 1020 p = q 1021 goto loop 1022 } 1023 } 1024 1025 p = p.Link 1026 goto loop 1027 } 1028 1029 var unaryDst = map[obj.As]bool{ 1030 ASWI: true, 1031 AWORD: true, 1032 } 1033 1034 var Linkarm = obj.LinkArch{ 1035 Arch: sys.ArchARM, 1036 Preprocess: preprocess, 1037 Assemble: span5, 1038 Follow: follow, 1039 Progedit: progedit, 1040 UnaryDst: unaryDst, 1041 }