github.com/aloncn/graphics-go@v0.0.1/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 spfix := obj.Appendp(ctxt, last) 807 spfix.As = obj.ANOP 808 spfix.Spadj = -framesize 809 810 // MOVW LR, R3 811 movw := obj.Appendp(ctxt, spfix) 812 movw.As = AMOVW 813 movw.From.Type = obj.TYPE_REG 814 movw.From.Reg = REGLINK 815 movw.To.Type = obj.TYPE_REG 816 movw.To.Reg = REG_R3 817 818 bls.Pcond = movw 819 820 // BL runtime.morestack 821 call := obj.Appendp(ctxt, movw) 822 call.As = obj.ACALL 823 call.To.Type = obj.TYPE_BRANCH 824 morestack := "runtime.morestack" 825 switch { 826 case ctxt.Cursym.Cfunc != 0: 827 morestack = "runtime.morestackc" 828 case ctxt.Cursym.Text.From3.Offset&obj.NEEDCTXT == 0: 829 morestack = "runtime.morestack_noctxt" 830 } 831 call.To.Sym = obj.Linklookup(ctxt, morestack, 0) 832 833 // B start 834 b := obj.Appendp(ctxt, call) 835 b.As = obj.AJMP 836 b.To.Type = obj.TYPE_BRANCH 837 b.Pcond = ctxt.Cursym.Text.Link 838 b.Spadj = +framesize 839 840 return bls 841 } 842 843 func initdiv(ctxt *obj.Link) { 844 if ctxt.Sym_div != nil { 845 return 846 } 847 ctxt.Sym_div = obj.Linklookup(ctxt, "_div", 0) 848 ctxt.Sym_divu = obj.Linklookup(ctxt, "_divu", 0) 849 ctxt.Sym_mod = obj.Linklookup(ctxt, "_mod", 0) 850 ctxt.Sym_modu = obj.Linklookup(ctxt, "_modu", 0) 851 } 852 853 func follow(ctxt *obj.Link, s *obj.LSym) { 854 ctxt.Cursym = s 855 856 firstp := ctxt.NewProg() 857 lastp := firstp 858 xfol(ctxt, s.Text, &lastp) 859 lastp.Link = nil 860 s.Text = firstp.Link 861 } 862 863 func relinv(a int) int { 864 switch a { 865 case ABEQ: 866 return ABNE 867 case ABNE: 868 return ABEQ 869 case ABCS: 870 return ABCC 871 case ABHS: 872 return ABLO 873 case ABCC: 874 return ABCS 875 case ABLO: 876 return ABHS 877 case ABMI: 878 return ABPL 879 case ABPL: 880 return ABMI 881 case ABVS: 882 return ABVC 883 case ABVC: 884 return ABVS 885 case ABHI: 886 return ABLS 887 case ABLS: 888 return ABHI 889 case ABGE: 890 return ABLT 891 case ABLT: 892 return ABGE 893 case ABGT: 894 return ABLE 895 case ABLE: 896 return ABGT 897 } 898 899 log.Fatalf("unknown relation: %s", Anames[a]) 900 return 0 901 } 902 903 func xfol(ctxt *obj.Link, p *obj.Prog, last **obj.Prog) { 904 var q *obj.Prog 905 var r *obj.Prog 906 var a int 907 var i int 908 909 loop: 910 if p == nil { 911 return 912 } 913 a = int(p.As) 914 if a == AB { 915 q = p.Pcond 916 if q != nil && q.As != obj.ATEXT { 917 p.Mark |= FOLL 918 p = q 919 if p.Mark&FOLL == 0 { 920 goto loop 921 } 922 } 923 } 924 925 if p.Mark&FOLL != 0 { 926 i = 0 927 q = p 928 for ; i < 4; i, q = i+1, q.Link { 929 if q == *last || q == nil { 930 break 931 } 932 a = int(q.As) 933 if a == obj.ANOP { 934 i-- 935 continue 936 } 937 938 if a == AB || (a == obj.ARET && q.Scond == C_SCOND_NONE) || a == ARFE || a == obj.AUNDEF { 939 goto copy 940 } 941 if q.Pcond == nil || (q.Pcond.Mark&FOLL != 0) { 942 continue 943 } 944 if a != ABEQ && a != ABNE { 945 continue 946 } 947 948 copy: 949 for { 950 r = ctxt.NewProg() 951 *r = *p 952 if r.Mark&FOLL == 0 { 953 fmt.Printf("can't happen 1\n") 954 } 955 r.Mark |= FOLL 956 if p != q { 957 p = p.Link 958 (*last).Link = r 959 *last = r 960 continue 961 } 962 963 (*last).Link = r 964 *last = r 965 if a == AB || (a == obj.ARET && q.Scond == C_SCOND_NONE) || a == ARFE || a == obj.AUNDEF { 966 return 967 } 968 r.As = ABNE 969 if a == ABNE { 970 r.As = ABEQ 971 } 972 r.Pcond = p.Link 973 r.Link = p.Pcond 974 if r.Link.Mark&FOLL == 0 { 975 xfol(ctxt, r.Link, last) 976 } 977 if r.Pcond.Mark&FOLL == 0 { 978 fmt.Printf("can't happen 2\n") 979 } 980 return 981 } 982 } 983 984 a = AB 985 q = ctxt.NewProg() 986 q.As = int16(a) 987 q.Lineno = p.Lineno 988 q.To.Type = obj.TYPE_BRANCH 989 q.To.Offset = p.Pc 990 q.Pcond = p 991 p = q 992 } 993 994 p.Mark |= FOLL 995 (*last).Link = p 996 *last = p 997 if a == AB || (a == obj.ARET && p.Scond == C_SCOND_NONE) || a == ARFE || a == obj.AUNDEF { 998 return 999 } 1000 1001 if p.Pcond != nil { 1002 if a != ABL && a != ABX && p.Link != nil { 1003 q = obj.Brchain(ctxt, p.Link) 1004 if a != obj.ATEXT { 1005 if q != nil && (q.Mark&FOLL != 0) { 1006 p.As = int16(relinv(a)) 1007 p.Link = p.Pcond 1008 p.Pcond = q 1009 } 1010 } 1011 1012 xfol(ctxt, p.Link, last) 1013 q = obj.Brchain(ctxt, p.Pcond) 1014 if q == nil { 1015 q = p.Pcond 1016 } 1017 if q.Mark&FOLL != 0 { 1018 p.Pcond = q 1019 return 1020 } 1021 1022 p = q 1023 goto loop 1024 } 1025 } 1026 1027 p = p.Link 1028 goto loop 1029 } 1030 1031 var unaryDst = map[int]bool{ 1032 ASWI: true, 1033 AWORD: true, 1034 } 1035 1036 var Linkarm = obj.LinkArch{ 1037 ByteOrder: binary.LittleEndian, 1038 Name: "arm", 1039 Thechar: '5', 1040 Preprocess: preprocess, 1041 Assemble: span5, 1042 Follow: follow, 1043 Progedit: progedit, 1044 UnaryDst: unaryDst, 1045 Minlc: 4, 1046 Ptrsize: 4, 1047 Regsize: 4, 1048 }