github.com/mh-cbon/go@v0.0.0-20160603070303-9e112a3fe4c0/src/cmd/compile/internal/arm64/ggen.go (about) 1 // Copyright 2009 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package arm64 6 7 import ( 8 "cmd/compile/internal/gc" 9 "cmd/internal/obj" 10 "cmd/internal/obj/arm64" 11 "fmt" 12 ) 13 14 func defframe(ptxt *obj.Prog) { 15 // fill in argument size, stack size 16 ptxt.To.Type = obj.TYPE_TEXTSIZE 17 18 ptxt.To.Val = int32(gc.Rnd(gc.Curfn.Type.ArgWidth(), int64(gc.Widthptr))) 19 frame := uint32(gc.Rnd(gc.Stksize+gc.Maxarg, int64(gc.Widthreg))) 20 21 // arm64 requires that the frame size (not counting saved LR) 22 // be empty or be 8 mod 16. If not, pad it. 23 if frame != 0 && frame%16 != 8 { 24 frame += 8 25 } 26 27 ptxt.To.Offset = int64(frame) 28 29 // insert code to zero ambiguously live variables 30 // so that the garbage collector only sees initialized values 31 // when it looks for pointers. 32 p := ptxt 33 34 hi := int64(0) 35 lo := hi 36 37 // iterate through declarations - they are sorted in decreasing xoffset order. 38 for _, n := range gc.Curfn.Func.Dcl { 39 if !n.Name.Needzero { 40 continue 41 } 42 if n.Class != gc.PAUTO { 43 gc.Fatalf("needzero class %d", n.Class) 44 } 45 if n.Type.Width%int64(gc.Widthptr) != 0 || n.Xoffset%int64(gc.Widthptr) != 0 || n.Type.Width == 0 { 46 gc.Fatalf("var %v has size %d offset %d", gc.Nconv(n, gc.FmtLong), int(n.Type.Width), int(n.Xoffset)) 47 } 48 49 if lo != hi && n.Xoffset+n.Type.Width >= lo-int64(2*gc.Widthreg) { 50 // merge with range we already have 51 lo = n.Xoffset 52 53 continue 54 } 55 56 // zero old range 57 p = zerorange(p, int64(frame), lo, hi) 58 59 // set new range 60 hi = n.Xoffset + n.Type.Width 61 62 lo = n.Xoffset 63 } 64 65 // zero final range 66 zerorange(p, int64(frame), lo, hi) 67 } 68 69 var darwin = obj.Getgoos() == "darwin" 70 71 func zerorange(p *obj.Prog, frame int64, lo int64, hi int64) *obj.Prog { 72 cnt := hi - lo 73 if cnt == 0 { 74 return p 75 } 76 if cnt < int64(4*gc.Widthptr) { 77 for i := int64(0); i < cnt; i += int64(gc.Widthptr) { 78 p = appendpp(p, arm64.AMOVD, obj.TYPE_REG, arm64.REGZERO, 0, obj.TYPE_MEM, arm64.REGSP, 8+frame+lo+i) 79 } 80 } else if cnt <= int64(128*gc.Widthptr) && !darwin { // darwin ld64 cannot handle BR26 reloc with non-zero addend 81 p = appendpp(p, arm64.AMOVD, obj.TYPE_REG, arm64.REGSP, 0, obj.TYPE_REG, arm64.REGRT1, 0) 82 p = appendpp(p, arm64.AADD, obj.TYPE_CONST, 0, 8+frame+lo-8, obj.TYPE_REG, arm64.REGRT1, 0) 83 p.Reg = arm64.REGRT1 84 p = appendpp(p, obj.ADUFFZERO, obj.TYPE_NONE, 0, 0, obj.TYPE_MEM, 0, 0) 85 f := gc.Sysfunc("duffzero") 86 gc.Naddr(&p.To, f) 87 gc.Afunclit(&p.To, f) 88 p.To.Offset = 4 * (128 - cnt/int64(gc.Widthptr)) 89 } else { 90 p = appendpp(p, arm64.AMOVD, obj.TYPE_CONST, 0, 8+frame+lo-8, obj.TYPE_REG, arm64.REGTMP, 0) 91 p = appendpp(p, arm64.AMOVD, obj.TYPE_REG, arm64.REGSP, 0, obj.TYPE_REG, arm64.REGRT1, 0) 92 p = appendpp(p, arm64.AADD, obj.TYPE_REG, arm64.REGTMP, 0, obj.TYPE_REG, arm64.REGRT1, 0) 93 p.Reg = arm64.REGRT1 94 p = appendpp(p, arm64.AMOVD, obj.TYPE_CONST, 0, cnt, obj.TYPE_REG, arm64.REGTMP, 0) 95 p = appendpp(p, arm64.AADD, obj.TYPE_REG, arm64.REGTMP, 0, obj.TYPE_REG, arm64.REGRT2, 0) 96 p.Reg = arm64.REGRT1 97 p = appendpp(p, arm64.AMOVD, obj.TYPE_REG, arm64.REGZERO, 0, obj.TYPE_MEM, arm64.REGRT1, int64(gc.Widthptr)) 98 p.Scond = arm64.C_XPRE 99 p1 := p 100 p = appendpp(p, arm64.ACMP, obj.TYPE_REG, arm64.REGRT1, 0, obj.TYPE_NONE, 0, 0) 101 p.Reg = arm64.REGRT2 102 p = appendpp(p, arm64.ABNE, obj.TYPE_NONE, 0, 0, obj.TYPE_BRANCH, 0, 0) 103 gc.Patch(p, p1) 104 } 105 106 return p 107 } 108 109 func appendpp(p *obj.Prog, as obj.As, ftype obj.AddrType, freg int, foffset int64, ttype obj.AddrType, treg int, toffset int64) *obj.Prog { 110 q := gc.Ctxt.NewProg() 111 gc.Clearp(q) 112 q.As = as 113 q.Lineno = p.Lineno 114 q.From.Type = ftype 115 q.From.Reg = int16(freg) 116 q.From.Offset = foffset 117 q.To.Type = ttype 118 q.To.Reg = int16(treg) 119 q.To.Offset = toffset 120 q.Link = p.Link 121 p.Link = q 122 return q 123 } 124 125 func ginsnop() { 126 var con gc.Node 127 gc.Nodconst(&con, gc.Types[gc.TINT], 0) 128 gins(arm64.AHINT, &con, nil) 129 } 130 131 var panicdiv *gc.Node 132 133 /* 134 * generate division. 135 * generates one of: 136 * res = nl / nr 137 * res = nl % nr 138 * according to op. 139 */ 140 func dodiv(op gc.Op, nl *gc.Node, nr *gc.Node, res *gc.Node) { 141 // Have to be careful about handling 142 // most negative int divided by -1 correctly. 143 // The hardware will generate undefined result. 144 // Also need to explicitly trap on division on zero, 145 // the hardware will silently generate undefined result. 146 // DIVW will leave unpredictable result in higher 32-bit, 147 // so always use DIVD/DIVDU. 148 t := nl.Type 149 150 t0 := t 151 check := false 152 if t.IsSigned() { 153 check = true 154 if gc.Isconst(nl, gc.CTINT) && nl.Int64() != -(1<<uint64(t.Width*8-1)) { 155 check = false 156 } else if gc.Isconst(nr, gc.CTINT) && nr.Int64() != -1 { 157 check = false 158 } 159 } 160 161 if t.Width < 8 { 162 if t.IsSigned() { 163 t = gc.Types[gc.TINT64] 164 } else { 165 t = gc.Types[gc.TUINT64] 166 } 167 check = false 168 } 169 170 a := optoas(gc.ODIV, t) 171 172 var tl gc.Node 173 gc.Regalloc(&tl, t0, nil) 174 var tr gc.Node 175 gc.Regalloc(&tr, t0, nil) 176 if nl.Ullman >= nr.Ullman { 177 gc.Cgen(nl, &tl) 178 gc.Cgen(nr, &tr) 179 } else { 180 gc.Cgen(nr, &tr) 181 gc.Cgen(nl, &tl) 182 } 183 184 if t != t0 { 185 // Convert 186 tl2 := tl 187 188 tr2 := tr 189 tl.Type = t 190 tr.Type = t 191 gmove(&tl2, &tl) 192 gmove(&tr2, &tr) 193 } 194 195 // Handle divide-by-zero panic. 196 p1 := gins(optoas(gc.OCMP, t), &tr, nil) 197 p1.Reg = arm64.REGZERO 198 p1 = gc.Gbranch(optoas(gc.ONE, t), nil, +1) 199 if panicdiv == nil { 200 panicdiv = gc.Sysfunc("panicdivide") 201 } 202 gc.Ginscall(panicdiv, -1) 203 gc.Patch(p1, gc.Pc) 204 205 var p2 *obj.Prog 206 if check { 207 var nm1 gc.Node 208 gc.Nodconst(&nm1, t, -1) 209 gcmp(optoas(gc.OCMP, t), &tr, &nm1) 210 p1 := gc.Gbranch(optoas(gc.ONE, t), nil, +1) 211 if op == gc.ODIV { 212 // a / (-1) is -a. 213 gins(optoas(gc.OMINUS, t), &tl, &tl) 214 215 gmove(&tl, res) 216 } else { 217 // a % (-1) is 0. 218 var nz gc.Node 219 gc.Nodconst(&nz, t, 0) 220 221 gmove(&nz, res) 222 } 223 224 p2 = gc.Gbranch(obj.AJMP, nil, 0) 225 gc.Patch(p1, gc.Pc) 226 } 227 228 p1 = gins(a, &tr, &tl) 229 if op == gc.ODIV { 230 gc.Regfree(&tr) 231 gmove(&tl, res) 232 } else { 233 // A%B = A-(A/B*B) 234 var tm gc.Node 235 gc.Regalloc(&tm, t, nil) 236 237 // patch div to use the 3 register form 238 // TODO(minux): add gins3? 239 p1.Reg = p1.To.Reg 240 241 p1.To.Reg = tm.Reg 242 gins(optoas(gc.OMUL, t), &tr, &tm) 243 gc.Regfree(&tr) 244 gins(optoas(gc.OSUB, t), &tm, &tl) 245 gc.Regfree(&tm) 246 gmove(&tl, res) 247 } 248 249 gc.Regfree(&tl) 250 if check { 251 gc.Patch(p2, gc.Pc) 252 } 253 } 254 255 // RightShiftWithCarry generates a constant unsigned 256 // right shift with carry. 257 // 258 // res = n >> shift // with carry 259 func RightShiftWithCarry(n *gc.Node, shift uint, res *gc.Node) { 260 // Extra 1 is for carry bit. 261 maxshift := uint(n.Type.Width*8 + 1) 262 if shift == 0 { 263 gmove(n, res) 264 } else if shift < maxshift { 265 // 1. clear rightmost bit of target 266 var n1 gc.Node 267 gc.Nodconst(&n1, n.Type, 1) 268 gins(optoas(gc.ORSH, n.Type), &n1, n) 269 gins(optoas(gc.OLSH, n.Type), &n1, n) 270 // 2. add carry flag to target 271 var n2 gc.Node 272 gc.Nodconst(&n1, n.Type, 0) 273 gc.Regalloc(&n2, n.Type, nil) 274 gins(optoas(gc.OAS, n.Type), &n1, &n2) 275 gins(arm64.AADC, &n2, n) 276 // 3. right rotate 1 bit 277 gc.Nodconst(&n1, n.Type, 1) 278 gins(arm64.AROR, &n1, n) 279 280 // ARM64 backend doesn't eliminate shifts by 0. It is manually checked here. 281 if shift > 1 { 282 var n3 gc.Node 283 gc.Nodconst(&n3, n.Type, int64(shift-1)) 284 cgen_shift(gc.ORSH, true, n, &n3, res) 285 } else { 286 gmove(n, res) 287 } 288 gc.Regfree(&n2) 289 } else { 290 gc.Fatalf("RightShiftWithCarry: shift(%v) is bigger than max size(%v)", shift, maxshift) 291 } 292 } 293 294 // AddSetCarry generates add and set carry. 295 // 296 // res = nl + nr // with carry flag set 297 func AddSetCarry(nl *gc.Node, nr *gc.Node, res *gc.Node) { 298 gins(arm64.AADDS, nl, nr) 299 gmove(nr, res) 300 } 301 302 /* 303 * generate high multiply: 304 * res = (nl*nr) >> width 305 */ 306 func cgen_hmul(nl *gc.Node, nr *gc.Node, res *gc.Node) { 307 // largest ullman on left. 308 if nl.Ullman < nr.Ullman { 309 nl, nr = nr, nl 310 } 311 312 t := nl.Type 313 w := t.Width * 8 314 var n1 gc.Node 315 gc.Cgenr(nl, &n1, res) 316 var n2 gc.Node 317 gc.Cgenr(nr, &n2, nil) 318 switch gc.Simtype[t.Etype] { 319 case gc.TINT8, 320 gc.TINT16, 321 gc.TINT32: 322 gins(optoas(gc.OMUL, t), &n2, &n1) 323 p := gins(arm64.AASR, nil, &n1) 324 p.From.Type = obj.TYPE_CONST 325 p.From.Offset = w 326 327 case gc.TUINT8, 328 gc.TUINT16, 329 gc.TUINT32: 330 gins(optoas(gc.OMUL, t), &n2, &n1) 331 p := gins(arm64.ALSR, nil, &n1) 332 p.From.Type = obj.TYPE_CONST 333 p.From.Offset = w 334 335 case gc.TINT64, 336 gc.TUINT64: 337 if t.IsSigned() { 338 gins(arm64.ASMULH, &n2, &n1) 339 } else { 340 gins(arm64.AUMULH, &n2, &n1) 341 } 342 343 default: 344 gc.Fatalf("cgen_hmul %v", t) 345 } 346 347 gc.Cgen(&n1, res) 348 gc.Regfree(&n1) 349 gc.Regfree(&n2) 350 } 351 352 /* 353 * generate shift according to op, one of: 354 * res = nl << nr 355 * res = nl >> nr 356 */ 357 func cgen_shift(op gc.Op, bounded bool, nl *gc.Node, nr *gc.Node, res *gc.Node) { 358 a := optoas(op, nl.Type) 359 360 if nr.Op == gc.OLITERAL { 361 var n1 gc.Node 362 gc.Regalloc(&n1, nl.Type, res) 363 gc.Cgen(nl, &n1) 364 sc := uint64(nr.Int64()) 365 if sc >= uint64(nl.Type.Width)*8 { 366 // large shift gets 2 shifts by width-1 367 var n3 gc.Node 368 gc.Nodconst(&n3, gc.Types[gc.TUINT32], nl.Type.Width*8-1) 369 370 gins(a, &n3, &n1) 371 gins(a, &n3, &n1) 372 } else { 373 gins(a, nr, &n1) 374 } 375 gmove(&n1, res) 376 gc.Regfree(&n1) 377 return 378 } 379 380 if nl.Ullman >= gc.UINF { 381 var n4 gc.Node 382 gc.Tempname(&n4, nl.Type) 383 gc.Cgen(nl, &n4) 384 nl = &n4 385 } 386 387 if nr.Ullman >= gc.UINF { 388 var n5 gc.Node 389 gc.Tempname(&n5, nr.Type) 390 gc.Cgen(nr, &n5) 391 nr = &n5 392 } 393 394 // Allow either uint32 or uint64 as shift type, 395 // to avoid unnecessary conversion from uint32 to uint64 396 // just to do the comparison. 397 tcount := gc.Types[gc.Simtype[nr.Type.Etype]] 398 399 if tcount.Etype < gc.TUINT32 { 400 tcount = gc.Types[gc.TUINT32] 401 } 402 403 var n1 gc.Node 404 gc.Regalloc(&n1, nr.Type, nil) // to hold the shift type in CX 405 var n3 gc.Node 406 gc.Regalloc(&n3, tcount, &n1) // to clear high bits of CX 407 408 var n2 gc.Node 409 gc.Regalloc(&n2, nl.Type, res) 410 411 if nl.Ullman >= nr.Ullman { 412 gc.Cgen(nl, &n2) 413 gc.Cgen(nr, &n1) 414 gmove(&n1, &n3) 415 } else { 416 gc.Cgen(nr, &n1) 417 gmove(&n1, &n3) 418 gc.Cgen(nl, &n2) 419 } 420 421 gc.Regfree(&n3) 422 423 // test and fix up large shifts 424 if !bounded { 425 gc.Nodconst(&n3, tcount, nl.Type.Width*8) 426 gcmp(optoas(gc.OCMP, tcount), &n1, &n3) 427 p1 := gc.Gbranch(optoas(gc.OLT, tcount), nil, +1) 428 if op == gc.ORSH && nl.Type.IsSigned() { 429 gc.Nodconst(&n3, gc.Types[gc.TUINT32], nl.Type.Width*8-1) 430 gins(a, &n3, &n2) 431 } else { 432 gc.Nodconst(&n3, nl.Type, 0) 433 gmove(&n3, &n2) 434 } 435 436 gc.Patch(p1, gc.Pc) 437 } 438 439 gins(a, &n1, &n2) 440 441 gmove(&n2, res) 442 443 gc.Regfree(&n1) 444 gc.Regfree(&n2) 445 } 446 447 func clearfat(nl *gc.Node) { 448 /* clear a fat object */ 449 if gc.Debug['g'] != 0 { 450 fmt.Printf("clearfat %v (%v, size: %d)\n", nl, nl.Type, nl.Type.Width) 451 } 452 453 w := uint64(nl.Type.Width) 454 455 // Avoid taking the address for simple enough types. 456 if gc.Componentgen(nil, nl) { 457 return 458 } 459 460 c := w % 8 // bytes 461 q := w / 8 // dwords 462 463 var r0 gc.Node 464 gc.Nodreg(&r0, gc.Types[gc.TUINT64], arm64.REGZERO) 465 var dst gc.Node 466 467 // REGRT1 is reserved on arm64, see arm64/gsubr.go. 468 gc.Nodreg(&dst, gc.Types[gc.Tptr], arm64.REGRT1) 469 gc.Agen(nl, &dst) 470 471 var boff uint64 472 if q > 128 { 473 p := gins(arm64.ASUB, nil, &dst) 474 p.From.Type = obj.TYPE_CONST 475 p.From.Offset = 8 476 477 var end gc.Node 478 gc.Regalloc(&end, gc.Types[gc.Tptr], nil) 479 p = gins(arm64.AMOVD, &dst, &end) 480 p.From.Type = obj.TYPE_ADDR 481 p.From.Offset = int64(q * 8) 482 483 p = gins(arm64.AMOVD, &r0, &dst) 484 p.To.Type = obj.TYPE_MEM 485 p.To.Offset = 8 486 p.Scond = arm64.C_XPRE 487 pl := p 488 489 p = gcmp(arm64.ACMP, &dst, &end) 490 gc.Patch(gc.Gbranch(arm64.ABNE, nil, 0), pl) 491 492 gc.Regfree(&end) 493 494 // The loop leaves R16 on the last zeroed dword 495 boff = 8 496 } else if q >= 4 && !darwin { // darwin ld64 cannot handle BR26 reloc with non-zero addend 497 p := gins(arm64.ASUB, nil, &dst) 498 p.From.Type = obj.TYPE_CONST 499 p.From.Offset = 8 500 f := gc.Sysfunc("duffzero") 501 p = gins(obj.ADUFFZERO, nil, f) 502 gc.Afunclit(&p.To, f) 503 504 // 4 and 128 = magic constants: see ../../runtime/asm_arm64x.s 505 p.To.Offset = int64(4 * (128 - q)) 506 507 // duffzero leaves R16 on the last zeroed dword 508 boff = 8 509 } else { 510 var p *obj.Prog 511 for t := uint64(0); t < q; t++ { 512 p = gins(arm64.AMOVD, &r0, &dst) 513 p.To.Type = obj.TYPE_MEM 514 p.To.Offset = int64(8 * t) 515 } 516 517 boff = 8 * q 518 } 519 520 var p *obj.Prog 521 for t := uint64(0); t < c; t++ { 522 p = gins(arm64.AMOVB, &r0, &dst) 523 p.To.Type = obj.TYPE_MEM 524 p.To.Offset = int64(t + boff) 525 } 526 } 527 528 // Called after regopt and peep have run. 529 // Expand CHECKNIL pseudo-op into actual nil pointer check. 530 func expandchecks(firstp *obj.Prog) { 531 var p1 *obj.Prog 532 533 for p := firstp; p != nil; p = p.Link { 534 if gc.Debug_checknil != 0 && gc.Ctxt.Debugvlog != 0 { 535 fmt.Printf("expandchecks: %v\n", p) 536 } 537 if p.As != obj.ACHECKNIL { 538 continue 539 } 540 if gc.Debug_checknil != 0 && p.Lineno > 1 { // p->lineno==1 in generated wrappers 541 gc.Warnl(p.Lineno, "generated nil check") 542 } 543 if p.From.Type != obj.TYPE_REG { 544 gc.Fatalf("invalid nil check %v\n", p) 545 } 546 547 // check is 548 // CBNZ arg, 2(PC) 549 // MOVD ZR, 0(arg) 550 p1 = gc.Ctxt.NewProg() 551 gc.Clearp(p1) 552 p1.Link = p.Link 553 p.Link = p1 554 p1.Lineno = p.Lineno 555 p1.Pc = 9999 556 557 p.As = arm64.ACBNZ 558 p.To.Type = obj.TYPE_BRANCH 559 p.To.Val = p1.Link 560 561 // crash by write to memory address 0. 562 p1.As = arm64.AMOVD 563 p1.From.Type = obj.TYPE_REG 564 p1.From.Reg = arm64.REGZERO 565 p1.To.Type = obj.TYPE_MEM 566 p1.To.Reg = p.From.Reg 567 p1.To.Offset = 0 568 } 569 } 570 571 // res = runtime.getg() 572 func getg(res *gc.Node) { 573 var n1 gc.Node 574 gc.Nodreg(&n1, res.Type, arm64.REGG) 575 gmove(&n1, res) 576 }