github.com/mdempsky/go@v0.0.0-20151201204031-5dd372bd1e70/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 "encoding/binary" 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 procesors. 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 rewriten 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 o int 346 var p1 *obj.Prog 347 var p2 *obj.Prog 348 var q2 *obj.Prog 349 for p := cursym.Text; p != nil; p = p.Link { 350 o = int(p.As) 351 switch o { 352 case obj.ATEXT: 353 autosize = int32(p.To.Offset + 4) 354 if autosize <= 4 { 355 if cursym.Text.Mark&LEAF != 0 { 356 p.To.Offset = -4 357 autosize = 0 358 } 359 } 360 361 if autosize == 0 && cursym.Text.Mark&LEAF == 0 { 362 if ctxt.Debugvlog != 0 { 363 fmt.Fprintf(ctxt.Bso, "save suppressed in: %s\n", cursym.Name) 364 ctxt.Bso.Flush() 365 } 366 367 cursym.Text.Mark |= LEAF 368 } 369 370 if cursym.Text.Mark&LEAF != 0 { 371 cursym.Leaf = 1 372 if autosize == 0 { 373 break 374 } 375 } 376 377 if p.From3.Offset&obj.NOSPLIT == 0 { 378 p = stacksplit(ctxt, p, autosize) // emit split check 379 } 380 381 // MOVW.W R14,$-autosize(SP) 382 p = obj.Appendp(ctxt, p) 383 384 p.As = AMOVW 385 p.Scond |= C_WBIT 386 p.From.Type = obj.TYPE_REG 387 p.From.Reg = REGLINK 388 p.To.Type = obj.TYPE_MEM 389 p.To.Offset = int64(-autosize) 390 p.To.Reg = REGSP 391 p.Spadj = autosize 392 393 if cursym.Text.From3.Offset&obj.WRAPPER != 0 { 394 // if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame 395 // 396 // MOVW g_panic(g), R1 397 // CMP $0, R1 398 // B.EQ end 399 // MOVW panic_argp(R1), R2 400 // ADD $(autosize+4), R13, R3 401 // CMP R2, R3 402 // B.NE end 403 // ADD $4, R13, R4 404 // MOVW R4, panic_argp(R1) 405 // end: 406 // NOP 407 // 408 // The NOP is needed to give the jumps somewhere to land. 409 // It is a liblink NOP, not an ARM NOP: it encodes to 0 instruction bytes. 410 411 p = obj.Appendp(ctxt, p) 412 413 p.As = AMOVW 414 p.From.Type = obj.TYPE_MEM 415 p.From.Reg = REGG 416 p.From.Offset = 4 * int64(ctxt.Arch.Ptrsize) // G.panic 417 p.To.Type = obj.TYPE_REG 418 p.To.Reg = REG_R1 419 420 p = obj.Appendp(ctxt, p) 421 p.As = ACMP 422 p.From.Type = obj.TYPE_CONST 423 p.From.Offset = 0 424 p.Reg = REG_R1 425 426 p = obj.Appendp(ctxt, p) 427 p.As = ABEQ 428 p.To.Type = obj.TYPE_BRANCH 429 p1 = p 430 431 p = obj.Appendp(ctxt, p) 432 p.As = AMOVW 433 p.From.Type = obj.TYPE_MEM 434 p.From.Reg = REG_R1 435 p.From.Offset = 0 // Panic.argp 436 p.To.Type = obj.TYPE_REG 437 p.To.Reg = REG_R2 438 439 p = obj.Appendp(ctxt, p) 440 p.As = AADD 441 p.From.Type = obj.TYPE_CONST 442 p.From.Offset = int64(autosize) + 4 443 p.Reg = REG_R13 444 p.To.Type = obj.TYPE_REG 445 p.To.Reg = REG_R3 446 447 p = obj.Appendp(ctxt, p) 448 p.As = ACMP 449 p.From.Type = obj.TYPE_REG 450 p.From.Reg = REG_R2 451 p.Reg = REG_R3 452 453 p = obj.Appendp(ctxt, p) 454 p.As = ABNE 455 p.To.Type = obj.TYPE_BRANCH 456 p2 = p 457 458 p = obj.Appendp(ctxt, p) 459 p.As = AADD 460 p.From.Type = obj.TYPE_CONST 461 p.From.Offset = 4 462 p.Reg = REG_R13 463 p.To.Type = obj.TYPE_REG 464 p.To.Reg = REG_R4 465 466 p = obj.Appendp(ctxt, p) 467 p.As = AMOVW 468 p.From.Type = obj.TYPE_REG 469 p.From.Reg = REG_R4 470 p.To.Type = obj.TYPE_MEM 471 p.To.Reg = REG_R1 472 p.To.Offset = 0 // Panic.argp 473 474 p = obj.Appendp(ctxt, p) 475 476 p.As = obj.ANOP 477 p1.Pcond = p 478 p2.Pcond = p 479 } 480 481 case obj.ARET: 482 obj.Nocache(p) 483 if cursym.Text.Mark&LEAF != 0 { 484 if autosize == 0 { 485 p.As = AB 486 p.From = obj.Addr{} 487 if p.To.Sym != nil { // retjmp 488 p.To.Type = obj.TYPE_BRANCH 489 } else { 490 p.To.Type = obj.TYPE_MEM 491 p.To.Offset = 0 492 p.To.Reg = REGLINK 493 } 494 495 break 496 } 497 } 498 499 p.As = AMOVW 500 p.Scond |= C_PBIT 501 p.From.Type = obj.TYPE_MEM 502 p.From.Offset = int64(autosize) 503 p.From.Reg = REGSP 504 p.To.Type = obj.TYPE_REG 505 p.To.Reg = REGPC 506 507 // If there are instructions following 508 // this ARET, they come from a branch 509 // with the same stackframe, so no spadj. 510 if p.To.Sym != nil { // retjmp 511 p.To.Reg = REGLINK 512 q2 = obj.Appendp(ctxt, p) 513 q2.As = AB 514 q2.To.Type = obj.TYPE_BRANCH 515 q2.To.Sym = p.To.Sym 516 p.To.Sym = nil 517 p = q2 518 } 519 520 case AADD: 521 if p.From.Type == obj.TYPE_CONST && p.From.Reg == 0 && p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP { 522 p.Spadj = int32(-p.From.Offset) 523 } 524 525 case ASUB: 526 if p.From.Type == obj.TYPE_CONST && p.From.Reg == 0 && p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP { 527 p.Spadj = int32(p.From.Offset) 528 } 529 530 case ADIV, ADIVU, AMOD, AMODU: 531 if cursym.Text.From3.Offset&obj.NOSPLIT != 0 { 532 ctxt.Diag("cannot divide in NOSPLIT function") 533 } 534 if ctxt.Debugdivmod != 0 { 535 break 536 } 537 if p.From.Type != obj.TYPE_REG { 538 break 539 } 540 if p.To.Type != obj.TYPE_REG { 541 break 542 } 543 544 // Make copy because we overwrite p below. 545 q1 := *p 546 if q1.Reg == REGTMP || q1.Reg == 0 && q1.To.Reg == REGTMP { 547 ctxt.Diag("div already using REGTMP: %v", p) 548 } 549 550 /* MOV m(g),REGTMP */ 551 p.As = AMOVW 552 p.Lineno = q1.Lineno 553 p.From.Type = obj.TYPE_MEM 554 p.From.Reg = REGG 555 p.From.Offset = 6 * 4 // offset of g.m 556 p.Reg = 0 557 p.To.Type = obj.TYPE_REG 558 p.To.Reg = REGTMP 559 560 /* MOV a,m_divmod(REGTMP) */ 561 p = obj.Appendp(ctxt, p) 562 p.As = AMOVW 563 p.Lineno = q1.Lineno 564 p.From.Type = obj.TYPE_REG 565 p.From.Reg = q1.From.Reg 566 p.To.Type = obj.TYPE_MEM 567 p.To.Reg = REGTMP 568 p.To.Offset = 8 * 4 // offset of m.divmod 569 570 /* MOV b,REGTMP */ 571 p = obj.Appendp(ctxt, p) 572 p.As = AMOVW 573 p.Lineno = q1.Lineno 574 p.From.Type = obj.TYPE_REG 575 p.From.Reg = q1.Reg 576 if q1.Reg == 0 { 577 p.From.Reg = q1.To.Reg 578 } 579 p.To.Type = obj.TYPE_REG 580 p.To.Reg = REGTMP 581 p.To.Offset = 0 582 583 /* CALL appropriate */ 584 p = obj.Appendp(ctxt, p) 585 p.As = ABL 586 p.Lineno = q1.Lineno 587 p.To.Type = obj.TYPE_BRANCH 588 switch o { 589 case ADIV: 590 p.To.Sym = ctxt.Sym_div 591 592 case ADIVU: 593 p.To.Sym = ctxt.Sym_divu 594 595 case AMOD: 596 p.To.Sym = ctxt.Sym_mod 597 598 case AMODU: 599 p.To.Sym = ctxt.Sym_modu 600 } 601 602 /* MOV REGTMP, b */ 603 p = obj.Appendp(ctxt, p) 604 p.As = AMOVW 605 p.Lineno = q1.Lineno 606 p.From.Type = obj.TYPE_REG 607 p.From.Reg = REGTMP 608 p.From.Offset = 0 609 p.To.Type = obj.TYPE_REG 610 p.To.Reg = q1.To.Reg 611 612 case AMOVW: 613 if (p.Scond&C_WBIT != 0) && p.To.Type == obj.TYPE_MEM && p.To.Reg == REGSP { 614 p.Spadj = int32(-p.To.Offset) 615 } 616 if (p.Scond&C_PBIT != 0) && p.From.Type == obj.TYPE_MEM && p.From.Reg == REGSP && p.To.Reg != REGPC { 617 p.Spadj = int32(-p.From.Offset) 618 } 619 if p.From.Type == obj.TYPE_ADDR && p.From.Reg == REGSP && p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP { 620 p.Spadj = int32(-p.From.Offset) 621 } 622 } 623 } 624 } 625 626 func isfloatreg(a *obj.Addr) bool { 627 return a.Type == obj.TYPE_REG && REG_F0 <= a.Reg && a.Reg <= REG_F15 628 } 629 630 func softfloat(ctxt *obj.Link, cursym *obj.LSym) { 631 if ctxt.Goarm > 5 { 632 return 633 } 634 635 symsfloat := obj.Linklookup(ctxt, "_sfloat", 0) 636 637 wasfloat := 0 638 for p := cursym.Text; p != nil; p = p.Link { 639 if p.Pcond != nil { 640 p.Pcond.Mark |= LABEL 641 } 642 } 643 var next *obj.Prog 644 for p := cursym.Text; p != nil; p = p.Link { 645 switch p.As { 646 case AMOVW: 647 if isfloatreg(&p.To) || isfloatreg(&p.From) { 648 goto soft 649 } 650 goto notsoft 651 652 case AMOVWD, 653 AMOVWF, 654 AMOVDW, 655 AMOVFW, 656 AMOVFD, 657 AMOVDF, 658 AMOVF, 659 AMOVD, 660 ACMPF, 661 ACMPD, 662 AADDF, 663 AADDD, 664 ASUBF, 665 ASUBD, 666 AMULF, 667 AMULD, 668 ADIVF, 669 ADIVD, 670 ASQRTF, 671 ASQRTD, 672 AABSF, 673 AABSD: 674 goto soft 675 676 default: 677 goto notsoft 678 } 679 680 soft: 681 if wasfloat == 0 || (p.Mark&LABEL != 0) { 682 next = ctxt.NewProg() 683 *next = *p 684 685 // BL _sfloat(SB) 686 *p = obj.Prog{} 687 p.Ctxt = ctxt 688 p.Link = next 689 p.As = ABL 690 p.To.Type = obj.TYPE_BRANCH 691 p.To.Sym = symsfloat 692 p.Lineno = next.Lineno 693 694 p = next 695 wasfloat = 1 696 } 697 698 continue 699 700 notsoft: 701 wasfloat = 0 702 } 703 } 704 705 func stacksplit(ctxt *obj.Link, p *obj.Prog, framesize int32) *obj.Prog { 706 // MOVW g_stackguard(g), R1 707 p = obj.Appendp(ctxt, p) 708 709 p.As = AMOVW 710 p.From.Type = obj.TYPE_MEM 711 p.From.Reg = REGG 712 p.From.Offset = 2 * int64(ctxt.Arch.Ptrsize) // G.stackguard0 713 if ctxt.Cursym.Cfunc != 0 { 714 p.From.Offset = 3 * int64(ctxt.Arch.Ptrsize) // G.stackguard1 715 } 716 p.To.Type = obj.TYPE_REG 717 p.To.Reg = REG_R1 718 719 if framesize <= obj.StackSmall { 720 // small stack: SP < stackguard 721 // CMP stackguard, SP 722 p = obj.Appendp(ctxt, p) 723 724 p.As = ACMP 725 p.From.Type = obj.TYPE_REG 726 p.From.Reg = REG_R1 727 p.Reg = REGSP 728 } else if framesize <= obj.StackBig { 729 // large stack: SP-framesize < stackguard-StackSmall 730 // MOVW $-framesize(SP), R2 731 // CMP stackguard, R2 732 p = obj.Appendp(ctxt, p) 733 734 p.As = AMOVW 735 p.From.Type = obj.TYPE_ADDR 736 p.From.Reg = REGSP 737 p.From.Offset = int64(-framesize) 738 p.To.Type = obj.TYPE_REG 739 p.To.Reg = REG_R2 740 741 p = obj.Appendp(ctxt, p) 742 p.As = ACMP 743 p.From.Type = obj.TYPE_REG 744 p.From.Reg = REG_R1 745 p.Reg = REG_R2 746 } else { 747 // Such a large stack we need to protect against wraparound 748 // if SP is close to zero. 749 // SP-stackguard+StackGuard < framesize + (StackGuard-StackSmall) 750 // The +StackGuard on both sides is required to keep the left side positive: 751 // SP is allowed to be slightly below stackguard. See stack.h. 752 // CMP $StackPreempt, R1 753 // MOVW.NE $StackGuard(SP), R2 754 // SUB.NE R1, R2 755 // MOVW.NE $(framesize+(StackGuard-StackSmall)), R3 756 // CMP.NE R3, R2 757 p = obj.Appendp(ctxt, p) 758 759 p.As = ACMP 760 p.From.Type = obj.TYPE_CONST 761 p.From.Offset = int64(uint32(obj.StackPreempt & (1<<32 - 1))) 762 p.Reg = REG_R1 763 764 p = obj.Appendp(ctxt, p) 765 p.As = AMOVW 766 p.From.Type = obj.TYPE_ADDR 767 p.From.Reg = REGSP 768 p.From.Offset = obj.StackGuard 769 p.To.Type = obj.TYPE_REG 770 p.To.Reg = REG_R2 771 p.Scond = C_SCOND_NE 772 773 p = obj.Appendp(ctxt, p) 774 p.As = ASUB 775 p.From.Type = obj.TYPE_REG 776 p.From.Reg = REG_R1 777 p.To.Type = obj.TYPE_REG 778 p.To.Reg = REG_R2 779 p.Scond = C_SCOND_NE 780 781 p = obj.Appendp(ctxt, p) 782 p.As = AMOVW 783 p.From.Type = obj.TYPE_ADDR 784 p.From.Offset = int64(framesize) + (obj.StackGuard - obj.StackSmall) 785 p.To.Type = obj.TYPE_REG 786 p.To.Reg = REG_R3 787 p.Scond = C_SCOND_NE 788 789 p = obj.Appendp(ctxt, p) 790 p.As = ACMP 791 p.From.Type = obj.TYPE_REG 792 p.From.Reg = REG_R3 793 p.Reg = REG_R2 794 p.Scond = C_SCOND_NE 795 } 796 797 // BLS call-to-morestack 798 bls := obj.Appendp(ctxt, p) 799 bls.As = ABLS 800 bls.To.Type = obj.TYPE_BRANCH 801 802 var last *obj.Prog 803 for last = ctxt.Cursym.Text; last.Link != nil; last = last.Link { 804 } 805 806 // MOVW LR, R3 807 movw := obj.Appendp(ctxt, last) 808 movw.As = AMOVW 809 movw.From.Type = obj.TYPE_REG 810 movw.From.Reg = REGLINK 811 movw.To.Type = obj.TYPE_REG 812 movw.To.Reg = REG_R3 813 814 bls.Pcond = movw 815 816 // BL runtime.morestack 817 call := obj.Appendp(ctxt, movw) 818 call.As = obj.ACALL 819 call.To.Type = obj.TYPE_BRANCH 820 morestack := "runtime.morestack" 821 switch { 822 case ctxt.Cursym.Cfunc != 0: 823 morestack = "runtime.morestackc" 824 case ctxt.Cursym.Text.From3.Offset&obj.NEEDCTXT == 0: 825 morestack = "runtime.morestack_noctxt" 826 } 827 call.To.Sym = obj.Linklookup(ctxt, morestack, 0) 828 829 // B start 830 b := obj.Appendp(ctxt, call) 831 b.As = obj.AJMP 832 b.To.Type = obj.TYPE_BRANCH 833 b.Pcond = ctxt.Cursym.Text.Link 834 835 return bls 836 } 837 838 func initdiv(ctxt *obj.Link) { 839 if ctxt.Sym_div != nil { 840 return 841 } 842 ctxt.Sym_div = obj.Linklookup(ctxt, "_div", 0) 843 ctxt.Sym_divu = obj.Linklookup(ctxt, "_divu", 0) 844 ctxt.Sym_mod = obj.Linklookup(ctxt, "_mod", 0) 845 ctxt.Sym_modu = obj.Linklookup(ctxt, "_modu", 0) 846 } 847 848 func follow(ctxt *obj.Link, s *obj.LSym) { 849 ctxt.Cursym = s 850 851 firstp := ctxt.NewProg() 852 lastp := firstp 853 xfol(ctxt, s.Text, &lastp) 854 lastp.Link = nil 855 s.Text = firstp.Link 856 } 857 858 func relinv(a int) int { 859 switch a { 860 case ABEQ: 861 return ABNE 862 case ABNE: 863 return ABEQ 864 case ABCS: 865 return ABCC 866 case ABHS: 867 return ABLO 868 case ABCC: 869 return ABCS 870 case ABLO: 871 return ABHS 872 case ABMI: 873 return ABPL 874 case ABPL: 875 return ABMI 876 case ABVS: 877 return ABVC 878 case ABVC: 879 return ABVS 880 case ABHI: 881 return ABLS 882 case ABLS: 883 return ABHI 884 case ABGE: 885 return ABLT 886 case ABLT: 887 return ABGE 888 case ABGT: 889 return ABLE 890 case ABLE: 891 return ABGT 892 } 893 894 log.Fatalf("unknown relation: %s", Anames[a]) 895 return 0 896 } 897 898 func xfol(ctxt *obj.Link, p *obj.Prog, last **obj.Prog) { 899 var q *obj.Prog 900 var r *obj.Prog 901 var a int 902 var i int 903 904 loop: 905 if p == nil { 906 return 907 } 908 a = int(p.As) 909 if a == AB { 910 q = p.Pcond 911 if q != nil && q.As != obj.ATEXT { 912 p.Mark |= FOLL 913 p = q 914 if p.Mark&FOLL == 0 { 915 goto loop 916 } 917 } 918 } 919 920 if p.Mark&FOLL != 0 { 921 i = 0 922 q = p 923 for ; i < 4; i, q = i+1, q.Link { 924 if q == *last || q == nil { 925 break 926 } 927 a = int(q.As) 928 if a == obj.ANOP { 929 i-- 930 continue 931 } 932 933 if a == AB || (a == obj.ARET && q.Scond == C_SCOND_NONE) || a == ARFE || a == obj.AUNDEF { 934 goto copy 935 } 936 if q.Pcond == nil || (q.Pcond.Mark&FOLL != 0) { 937 continue 938 } 939 if a != ABEQ && a != ABNE { 940 continue 941 } 942 943 copy: 944 for { 945 r = ctxt.NewProg() 946 *r = *p 947 if r.Mark&FOLL == 0 { 948 fmt.Printf("can't happen 1\n") 949 } 950 r.Mark |= FOLL 951 if p != q { 952 p = p.Link 953 (*last).Link = r 954 *last = r 955 continue 956 } 957 958 (*last).Link = r 959 *last = r 960 if a == AB || (a == obj.ARET && q.Scond == C_SCOND_NONE) || a == ARFE || a == obj.AUNDEF { 961 return 962 } 963 r.As = ABNE 964 if a == ABNE { 965 r.As = ABEQ 966 } 967 r.Pcond = p.Link 968 r.Link = p.Pcond 969 if r.Link.Mark&FOLL == 0 { 970 xfol(ctxt, r.Link, last) 971 } 972 if r.Pcond.Mark&FOLL == 0 { 973 fmt.Printf("can't happen 2\n") 974 } 975 return 976 } 977 } 978 979 a = AB 980 q = ctxt.NewProg() 981 q.As = int16(a) 982 q.Lineno = p.Lineno 983 q.To.Type = obj.TYPE_BRANCH 984 q.To.Offset = p.Pc 985 q.Pcond = p 986 p = q 987 } 988 989 p.Mark |= FOLL 990 (*last).Link = p 991 *last = p 992 if a == AB || (a == obj.ARET && p.Scond == C_SCOND_NONE) || a == ARFE || a == obj.AUNDEF { 993 return 994 } 995 996 if p.Pcond != nil { 997 if a != ABL && a != ABX && p.Link != nil { 998 q = obj.Brchain(ctxt, p.Link) 999 if a != obj.ATEXT { 1000 if q != nil && (q.Mark&FOLL != 0) { 1001 p.As = int16(relinv(a)) 1002 p.Link = p.Pcond 1003 p.Pcond = q 1004 } 1005 } 1006 1007 xfol(ctxt, p.Link, last) 1008 q = obj.Brchain(ctxt, p.Pcond) 1009 if q == nil { 1010 q = p.Pcond 1011 } 1012 if q.Mark&FOLL != 0 { 1013 p.Pcond = q 1014 return 1015 } 1016 1017 p = q 1018 goto loop 1019 } 1020 } 1021 1022 p = p.Link 1023 goto loop 1024 } 1025 1026 var unaryDst = map[int]bool{ 1027 ASWI: true, 1028 AWORD: true, 1029 } 1030 1031 var Linkarm = obj.LinkArch{ 1032 ByteOrder: binary.LittleEndian, 1033 Name: "arm", 1034 Thechar: '5', 1035 Preprocess: preprocess, 1036 Assemble: span5, 1037 Follow: follow, 1038 Progedit: progedit, 1039 UnaryDst: unaryDst, 1040 Minlc: 4, 1041 Ptrsize: 4, 1042 Regsize: 4, 1043 }