Home | History | Annotate | Download | only in arm64
      1 // Derived from Inferno utils/6c/peep.c
      2 // http://code.google.com/p/inferno-os/source/browse/utils/6c/peep.c
      3 //
      4 //	Copyright  1994-1999 Lucent Technologies Inc.  All rights reserved.
      5 //	Portions Copyright  1995-1997 C H Forsyth (forsyth (a] 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 (a] 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 arm64
     32 
     33 import (
     34 	"cmd/compile/internal/gc"
     35 	"cmd/internal/obj"
     36 	"cmd/internal/obj/arm64"
     37 	"fmt"
     38 )
     39 
     40 var gactive uint32
     41 
     42 func peep(firstp *obj.Prog) {
     43 	g := (*gc.Graph)(gc.Flowstart(firstp, nil))
     44 	if g == nil {
     45 		return
     46 	}
     47 	gactive = 0
     48 
     49 	var p *obj.Prog
     50 	var r *gc.Flow
     51 	var t int
     52 loop1:
     53 	if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 {
     54 		gc.Dumpit("loop1", g.Start, 0)
     55 	}
     56 
     57 	t = 0
     58 	for r = g.Start; r != nil; r = r.Link {
     59 		p = r.Prog
     60 
     61 		// TODO(minux) Handle smaller moves. arm and amd64
     62 		// distinguish between moves that *must* sign/zero
     63 		// extend and moves that don't care so they
     64 		// can eliminate moves that don't care without
     65 		// breaking moves that do care. This might let us
     66 		// simplify or remove the next peep loop, too.
     67 		if p.As == arm64.AMOVD || p.As == arm64.AFMOVD {
     68 			if regtyp(&p.To) {
     69 				// Try to eliminate reg->reg moves
     70 				if regtyp(&p.From) {
     71 					if p.From.Type == p.To.Type {
     72 						if copyprop(r) {
     73 							excise(r)
     74 							t++
     75 						} else if subprop(r) && copyprop(r) {
     76 							excise(r)
     77 							t++
     78 						}
     79 					}
     80 				}
     81 			}
     82 		}
     83 	}
     84 
     85 	if t != 0 {
     86 		goto loop1
     87 	}
     88 
     89 	/*
     90 	 * look for MOVB x,R; MOVB R,R (for small MOVs not handled above)
     91 	 */
     92 	var p1 *obj.Prog
     93 	var r1 *gc.Flow
     94 	for r := (*gc.Flow)(g.Start); r != nil; r = r.Link {
     95 		p = r.Prog
     96 		switch p.As {
     97 		default:
     98 			continue
     99 
    100 		case arm64.AMOVH,
    101 			arm64.AMOVHU,
    102 			arm64.AMOVB,
    103 			arm64.AMOVBU,
    104 			arm64.AMOVW,
    105 			arm64.AMOVWU:
    106 			if p.To.Type != obj.TYPE_REG {
    107 				continue
    108 			}
    109 		}
    110 
    111 		r1 = r.Link
    112 		if r1 == nil {
    113 			continue
    114 		}
    115 		p1 = r1.Prog
    116 		if p1.As != p.As {
    117 			continue
    118 		}
    119 		if p1.From.Type != obj.TYPE_REG || p1.From.Reg != p.To.Reg {
    120 			continue
    121 		}
    122 		if p1.To.Type != obj.TYPE_REG || p1.To.Reg != p.To.Reg {
    123 			continue
    124 		}
    125 		excise(r1)
    126 	}
    127 
    128 	if gc.Debug['D'] > 1 {
    129 		goto ret /* allow following code improvement to be suppressed */
    130 	}
    131 
    132 	// MOVD $c, R'; ADD R', R (R' unused) -> ADD $c, R
    133 	for r := (*gc.Flow)(g.Start); r != nil; r = r.Link {
    134 		p = r.Prog
    135 		switch p.As {
    136 		default:
    137 			continue
    138 
    139 		case arm64.AMOVD:
    140 			if p.To.Type != obj.TYPE_REG {
    141 				continue
    142 			}
    143 			if p.From.Type != obj.TYPE_CONST {
    144 				continue
    145 			}
    146 			if p.From.Offset < 0 || 4096 <= p.From.Offset {
    147 				continue
    148 			}
    149 		}
    150 		r1 = r.Link
    151 		if r1 == nil {
    152 			continue
    153 		}
    154 		p1 = r1.Prog
    155 		if p1.As != arm64.AADD && p1.As != arm64.ASUB { // TODO(aram): also logical after we have bimm.
    156 			continue
    157 		}
    158 		if p1.From.Type != obj.TYPE_REG || p1.From.Reg != p.To.Reg {
    159 			continue
    160 		}
    161 		if p1.To.Type != obj.TYPE_REG {
    162 			continue
    163 		}
    164 		if gc.Debug['P'] != 0 {
    165 			fmt.Printf("encoding $%d directly into %v in:\n%v\n%v\n", p.From.Offset, obj.Aconv(int(p1.As)), p, p1)
    166 		}
    167 		p1.From.Type = obj.TYPE_CONST
    168 		p1.From = p.From
    169 		excise(r)
    170 	}
    171 
    172 	/* TODO(minux):
    173 	 * look for OP x,y,R; CMP R, $0 -> OP.S x,y,R
    174 	 * when OP can set condition codes correctly
    175 	 */
    176 
    177 ret:
    178 	gc.Flowend(g)
    179 }
    180 
    181 func excise(r *gc.Flow) {
    182 	p := (*obj.Prog)(r.Prog)
    183 	if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 {
    184 		fmt.Printf("%v ===delete===\n", p)
    185 	}
    186 	obj.Nopout(p)
    187 	gc.Ostats.Ndelmov++
    188 }
    189 
    190 func regtyp(a *obj.Addr) bool {
    191 	// TODO(rsc): Floating point register exclusions?
    192 	return a.Type == obj.TYPE_REG && arm64.REG_R0 <= a.Reg && a.Reg <= arm64.REG_F31 && a.Reg != arm64.REGZERO
    193 }
    194 
    195 /*
    196  * the idea is to substitute
    197  * one register for another
    198  * from one MOV to another
    199  *	MOV	a, R1
    200  *	ADD	b, R1	/ no use of R2
    201  *	MOV	R1, R2
    202  * would be converted to
    203  *	MOV	a, R2
    204  *	ADD	b, R2
    205  *	MOV	R2, R1
    206  * hopefully, then the former or latter MOV
    207  * will be eliminated by copy propagation.
    208  *
    209  * r0 (the argument, not the register) is the MOV at the end of the
    210  * above sequences. This returns 1 if it modified any instructions.
    211  */
    212 func subprop(r0 *gc.Flow) bool {
    213 	p := (*obj.Prog)(r0.Prog)
    214 	v1 := (*obj.Addr)(&p.From)
    215 	if !regtyp(v1) {
    216 		return false
    217 	}
    218 	v2 := (*obj.Addr)(&p.To)
    219 	if !regtyp(v2) {
    220 		return false
    221 	}
    222 	for r := gc.Uniqp(r0); r != nil; r = gc.Uniqp(r) {
    223 		if gc.Uniqs(r) == nil {
    224 			break
    225 		}
    226 		p = r.Prog
    227 		if p.As == obj.AVARDEF || p.As == obj.AVARKILL {
    228 			continue
    229 		}
    230 		if p.Info.Flags&gc.Call != 0 {
    231 			return false
    232 		}
    233 
    234 		if p.Info.Flags&(gc.RightRead|gc.RightWrite) == gc.RightWrite {
    235 			if p.To.Type == v1.Type {
    236 				if p.To.Reg == v1.Reg {
    237 					copysub(&p.To, v1, v2, 1)
    238 					if gc.Debug['P'] != 0 {
    239 						fmt.Printf("gotit: %v->%v\n%v", gc.Ctxt.Dconv(v1), gc.Ctxt.Dconv(v2), r.Prog)
    240 						if p.From.Type == v2.Type {
    241 							fmt.Printf(" excise")
    242 						}
    243 						fmt.Printf("\n")
    244 					}
    245 
    246 					for r = gc.Uniqs(r); r != r0; r = gc.Uniqs(r) {
    247 						p = r.Prog
    248 						copysub(&p.From, v1, v2, 1)
    249 						copysub1(p, v1, v2, 1)
    250 						copysub(&p.To, v1, v2, 1)
    251 						if gc.Debug['P'] != 0 {
    252 							fmt.Printf("%v\n", r.Prog)
    253 						}
    254 					}
    255 
    256 					t := int(int(v1.Reg))
    257 					v1.Reg = v2.Reg
    258 					v2.Reg = int16(t)
    259 					if gc.Debug['P'] != 0 {
    260 						fmt.Printf("%v last\n", r.Prog)
    261 					}
    262 					return true
    263 				}
    264 			}
    265 		}
    266 
    267 		if copyau(&p.From, v2) || copyau1(p, v2) || copyau(&p.To, v2) {
    268 			break
    269 		}
    270 		if copysub(&p.From, v1, v2, 0) != 0 || copysub1(p, v1, v2, 0) != 0 || copysub(&p.To, v1, v2, 0) != 0 {
    271 			break
    272 		}
    273 	}
    274 
    275 	return false
    276 }
    277 
    278 /*
    279  * The idea is to remove redundant copies.
    280  *	v1->v2	F=0
    281  *	(use v2	s/v2/v1/)*
    282  *	set v1	F=1
    283  *	use v2	return fail (v1->v2 move must remain)
    284  *	-----------------
    285  *	v1->v2	F=0
    286  *	(use v2	s/v2/v1/)*
    287  *	set v1	F=1
    288  *	set v2	return success (caller can remove v1->v2 move)
    289  */
    290 func copyprop(r0 *gc.Flow) bool {
    291 	p := (*obj.Prog)(r0.Prog)
    292 	v1 := (*obj.Addr)(&p.From)
    293 	v2 := (*obj.Addr)(&p.To)
    294 	if copyas(v1, v2) {
    295 		if gc.Debug['P'] != 0 {
    296 			fmt.Printf("eliminating self-move: %v\n", r0.Prog)
    297 		}
    298 		return true
    299 	}
    300 
    301 	gactive++
    302 	if gc.Debug['P'] != 0 {
    303 		fmt.Printf("trying to eliminate %v->%v move from:\n%v\n", gc.Ctxt.Dconv(v1), gc.Ctxt.Dconv(v2), r0.Prog)
    304 	}
    305 	return copy1(v1, v2, r0.S1, 0)
    306 }
    307 
    308 // copy1 replaces uses of v2 with v1 starting at r and returns 1 if
    309 // all uses were rewritten.
    310 func copy1(v1 *obj.Addr, v2 *obj.Addr, r *gc.Flow, f int) bool {
    311 	if uint32(r.Active) == gactive {
    312 		if gc.Debug['P'] != 0 {
    313 			fmt.Printf("act set; return 1\n")
    314 		}
    315 		return true
    316 	}
    317 
    318 	r.Active = int32(gactive)
    319 	if gc.Debug['P'] != 0 {
    320 		fmt.Printf("copy1 replace %v with %v f=%d\n", gc.Ctxt.Dconv(v2), gc.Ctxt.Dconv(v1), f)
    321 	}
    322 	var t int
    323 	var p *obj.Prog
    324 	for ; r != nil; r = r.S1 {
    325 		p = r.Prog
    326 		if gc.Debug['P'] != 0 {
    327 			fmt.Printf("%v", p)
    328 		}
    329 		if f == 0 && gc.Uniqp(r) == nil {
    330 			// Multiple predecessors; conservatively
    331 			// assume v1 was set on other path
    332 			f = 1
    333 
    334 			if gc.Debug['P'] != 0 {
    335 				fmt.Printf("; merge; f=%d", f)
    336 			}
    337 		}
    338 
    339 		t = copyu(p, v2, nil)
    340 		switch t {
    341 		case 2: /* rar, can't split */
    342 			if gc.Debug['P'] != 0 {
    343 				fmt.Printf("; %v rar; return 0\n", gc.Ctxt.Dconv(v2))
    344 			}
    345 			return false
    346 
    347 		case 3: /* set */
    348 			if gc.Debug['P'] != 0 {
    349 				fmt.Printf("; %v set; return 1\n", gc.Ctxt.Dconv(v2))
    350 			}
    351 			return true
    352 
    353 		case 1, /* used, substitute */
    354 			4: /* use and set */
    355 			if f != 0 {
    356 				if gc.Debug['P'] == 0 {
    357 					return false
    358 				}
    359 				if t == 4 {
    360 					fmt.Printf("; %v used+set and f=%d; return 0\n", gc.Ctxt.Dconv(v2), f)
    361 				} else {
    362 					fmt.Printf("; %v used and f=%d; return 0\n", gc.Ctxt.Dconv(v2), f)
    363 				}
    364 				return false
    365 			}
    366 
    367 			if copyu(p, v2, v1) != 0 {
    368 				if gc.Debug['P'] != 0 {
    369 					fmt.Printf("; sub fail; return 0\n")
    370 				}
    371 				return false
    372 			}
    373 
    374 			if gc.Debug['P'] != 0 {
    375 				fmt.Printf("; sub %v->%v\n => %v", gc.Ctxt.Dconv(v2), gc.Ctxt.Dconv(v1), p)
    376 			}
    377 			if t == 4 {
    378 				if gc.Debug['P'] != 0 {
    379 					fmt.Printf("; %v used+set; return 1\n", gc.Ctxt.Dconv(v2))
    380 				}
    381 				return true
    382 			}
    383 		}
    384 
    385 		if f == 0 {
    386 			t = copyu(p, v1, nil)
    387 			if f == 0 && (t == 2 || t == 3 || t == 4) {
    388 				f = 1
    389 				if gc.Debug['P'] != 0 {
    390 					fmt.Printf("; %v set and !f; f=%d", gc.Ctxt.Dconv(v1), f)
    391 				}
    392 			}
    393 		}
    394 
    395 		if gc.Debug['P'] != 0 {
    396 			fmt.Printf("\n")
    397 		}
    398 		if r.S2 != nil {
    399 			if !copy1(v1, v2, r.S2, f) {
    400 				return false
    401 			}
    402 		}
    403 	}
    404 
    405 	return true
    406 }
    407 
    408 // If s==nil, copyu returns the set/use of v in p; otherwise, it
    409 // modifies p to replace reads of v with reads of s and returns 0 for
    410 // success or non-zero for failure.
    411 //
    412 // If s==nil, copy returns one of the following values:
    413 //	1 if v only used
    414 //	2 if v is set and used in one address (read-alter-rewrite;
    415 //	  can't substitute)
    416 //	3 if v is only set
    417 //	4 if v is set in one address and used in another (so addresses
    418 //	  can be rewritten independently)
    419 //	0 otherwise (not touched)
    420 func copyu(p *obj.Prog, v *obj.Addr, s *obj.Addr) int {
    421 	if p.From3Type() != obj.TYPE_NONE {
    422 		// 7g never generates a from3
    423 		fmt.Printf("copyu: from3 (%v) not implemented\n", gc.Ctxt.Dconv(p.From3))
    424 	}
    425 	if p.RegTo2 != obj.REG_NONE {
    426 		// 7g never generates a to2
    427 		fmt.Printf("copyu: RegTo2 (%v) not implemented\n", obj.Rconv(int(p.RegTo2)))
    428 	}
    429 
    430 	switch p.As {
    431 	default:
    432 		fmt.Printf("copyu: can't find %v\n", obj.Aconv(int(p.As)))
    433 		return 2
    434 
    435 	case obj.ANOP, /* read p->from, write p->to */
    436 		arm64.ANEG,
    437 		arm64.AFNEGD,
    438 		arm64.AFNEGS,
    439 		arm64.AFSQRTD,
    440 		arm64.AFCVTZSD,
    441 		arm64.AFCVTZSS,
    442 		arm64.AFCVTZSDW,
    443 		arm64.AFCVTZSSW,
    444 		arm64.AFCVTZUD,
    445 		arm64.AFCVTZUS,
    446 		arm64.AFCVTZUDW,
    447 		arm64.AFCVTZUSW,
    448 		arm64.AFCVTSD,
    449 		arm64.AFCVTDS,
    450 		arm64.ASCVTFD,
    451 		arm64.ASCVTFS,
    452 		arm64.ASCVTFWD,
    453 		arm64.ASCVTFWS,
    454 		arm64.AUCVTFD,
    455 		arm64.AUCVTFS,
    456 		arm64.AUCVTFWD,
    457 		arm64.AUCVTFWS,
    458 		arm64.AMOVB,
    459 		arm64.AMOVBU,
    460 		arm64.AMOVH,
    461 		arm64.AMOVHU,
    462 		arm64.AMOVW,
    463 		arm64.AMOVWU,
    464 		arm64.AMOVD,
    465 		arm64.AFMOVS,
    466 		arm64.AFMOVD:
    467 		if p.Scond == 0 {
    468 			if s != nil {
    469 				if copysub(&p.From, v, s, 1) != 0 {
    470 					return 1
    471 				}
    472 
    473 				// Update only indirect uses of v in p->to
    474 				if !copyas(&p.To, v) {
    475 					if copysub(&p.To, v, s, 1) != 0 {
    476 						return 1
    477 					}
    478 				}
    479 				return 0
    480 			}
    481 
    482 			if copyas(&p.To, v) {
    483 				// Fix up implicit from
    484 				if p.From.Type == obj.TYPE_NONE {
    485 					p.From = p.To
    486 				}
    487 				if copyau(&p.From, v) {
    488 					return 4
    489 				}
    490 				return 3
    491 			}
    492 
    493 			if copyau(&p.From, v) {
    494 				return 1
    495 			}
    496 			if copyau(&p.To, v) {
    497 				// p->to only indirectly uses v
    498 				return 1
    499 			}
    500 
    501 			return 0
    502 		}
    503 
    504 		/* rar p->from, write p->to or read p->from, rar p->to */
    505 		if p.From.Type == obj.TYPE_MEM {
    506 			if copyas(&p.From, v) {
    507 				// No s!=nil check; need to fail
    508 				// anyway in that case
    509 				return 2
    510 			}
    511 
    512 			if s != nil {
    513 				if copysub(&p.To, v, s, 1) != 0 {
    514 					return 1
    515 				}
    516 				return 0
    517 			}
    518 
    519 			if copyas(&p.To, v) {
    520 				return 3
    521 			}
    522 		} else if p.To.Type == obj.TYPE_MEM {
    523 			if copyas(&p.To, v) {
    524 				return 2
    525 			}
    526 			if s != nil {
    527 				if copysub(&p.From, v, s, 1) != 0 {
    528 					return 1
    529 				}
    530 				return 0
    531 			}
    532 
    533 			if copyau(&p.From, v) {
    534 				return 1
    535 			}
    536 		} else {
    537 			fmt.Printf("copyu: bad %v\n", p)
    538 		}
    539 
    540 		return 0
    541 
    542 	case arm64.AADD, /* read p->from, read p->reg, write p->to */
    543 		arm64.ASUB,
    544 		arm64.AAND,
    545 		arm64.AORR,
    546 		arm64.AEOR,
    547 		arm64.AMUL,
    548 		arm64.ASMULL,
    549 		arm64.AUMULL,
    550 		arm64.ASMULH,
    551 		arm64.AUMULH,
    552 		arm64.ASDIV,
    553 		arm64.AUDIV,
    554 		arm64.ALSL,
    555 		arm64.ALSR,
    556 		arm64.AASR,
    557 		arm64.AFADDD,
    558 		arm64.AFADDS,
    559 		arm64.AFSUBD,
    560 		arm64.AFSUBS,
    561 		arm64.AFMULD,
    562 		arm64.AFMULS,
    563 		arm64.AFDIVD,
    564 		arm64.AFDIVS:
    565 		if s != nil {
    566 			if copysub(&p.From, v, s, 1) != 0 {
    567 				return 1
    568 			}
    569 			if copysub1(p, v, s, 1) != 0 {
    570 				return 1
    571 			}
    572 
    573 			// Update only indirect uses of v in p->to
    574 			if !copyas(&p.To, v) {
    575 				if copysub(&p.To, v, s, 1) != 0 {
    576 					return 1
    577 				}
    578 			}
    579 			return 0
    580 		}
    581 
    582 		if copyas(&p.To, v) {
    583 			if p.Reg == 0 {
    584 				// Fix up implicit reg (e.g., ADD
    585 				// R3,R4 -> ADD R3,R4,R4) so we can
    586 				// update reg and to separately.
    587 				p.Reg = p.To.Reg
    588 			}
    589 
    590 			if copyau(&p.From, v) {
    591 				return 4
    592 			}
    593 			if copyau1(p, v) {
    594 				return 4
    595 			}
    596 			return 3
    597 		}
    598 
    599 		if copyau(&p.From, v) {
    600 			return 1
    601 		}
    602 		if copyau1(p, v) {
    603 			return 1
    604 		}
    605 		if copyau(&p.To, v) {
    606 			return 1
    607 		}
    608 		return 0
    609 
    610 	case arm64.ABEQ,
    611 		arm64.ABNE,
    612 		arm64.ABGE,
    613 		arm64.ABLT,
    614 		arm64.ABGT,
    615 		arm64.ABLE,
    616 		arm64.ABLO,
    617 		arm64.ABLS,
    618 		arm64.ABHI,
    619 		arm64.ABHS:
    620 		return 0
    621 
    622 	case obj.ACHECKNIL, /* read p->from */
    623 		arm64.ACMP, /* read p->from, read p->reg */
    624 		arm64.AFCMPD,
    625 		arm64.AFCMPS:
    626 		if s != nil {
    627 			if copysub(&p.From, v, s, 1) != 0 {
    628 				return 1
    629 			}
    630 			return copysub1(p, v, s, 1)
    631 		}
    632 
    633 		if copyau(&p.From, v) {
    634 			return 1
    635 		}
    636 		if copyau1(p, v) {
    637 			return 1
    638 		}
    639 		return 0
    640 
    641 	case arm64.AB: /* read p->to */
    642 		if s != nil {
    643 			if copysub(&p.To, v, s, 1) != 0 {
    644 				return 1
    645 			}
    646 			return 0
    647 		}
    648 
    649 		if copyau(&p.To, v) {
    650 			return 1
    651 		}
    652 		return 0
    653 
    654 	case obj.ARET: /* funny */
    655 		if s != nil {
    656 			return 0
    657 		}
    658 
    659 		// All registers die at this point, so claim
    660 		// everything is set (and not used).
    661 		return 3
    662 
    663 	case arm64.ABL: /* funny */
    664 		if p.From.Type == obj.TYPE_REG && v.Type == obj.TYPE_REG && p.From.Reg == v.Reg {
    665 			return 2
    666 		}
    667 
    668 		if s != nil {
    669 			if copysub(&p.To, v, s, 1) != 0 {
    670 				return 1
    671 			}
    672 			return 0
    673 		}
    674 
    675 		if copyau(&p.To, v) {
    676 			return 4
    677 		}
    678 		return 3
    679 
    680 	// R31 is zero, used by DUFFZERO, cannot be substituted.
    681 	// R16 is ptr to memory, used and set, cannot be substituted.
    682 	case obj.ADUFFZERO:
    683 		if v.Type == obj.TYPE_REG {
    684 			if v.Reg == 31 {
    685 				return 1
    686 			}
    687 			if v.Reg == 16 {
    688 				return 2
    689 			}
    690 		}
    691 
    692 		return 0
    693 
    694 	// R16, R17 are ptr to src, dst, used and set, cannot be substituted.
    695 	// R27 is scratch, set by DUFFCOPY, cannot be substituted.
    696 	case obj.ADUFFCOPY:
    697 		if v.Type == obj.TYPE_REG {
    698 			if v.Reg == 16 || v.Reg == 17 {
    699 				return 2
    700 			}
    701 			if v.Reg == 27 {
    702 				return 3
    703 			}
    704 		}
    705 
    706 		return 0
    707 
    708 	case arm64.AHINT,
    709 		obj.ATEXT,
    710 		obj.APCDATA,
    711 		obj.AFUNCDATA,
    712 		obj.AVARDEF,
    713 		obj.AVARKILL:
    714 		return 0
    715 	}
    716 }
    717 
    718 // copyas returns 1 if a and v address the same register.
    719 //
    720 // If a is the from operand, this means this operation reads the
    721 // register in v. If a is the to operand, this means this operation
    722 // writes the register in v.
    723 func copyas(a *obj.Addr, v *obj.Addr) bool {
    724 	if regtyp(v) {
    725 		if a.Type == v.Type {
    726 			if a.Reg == v.Reg {
    727 				return true
    728 			}
    729 		}
    730 	}
    731 	return false
    732 }
    733 
    734 // copyau returns 1 if a either directly or indirectly addresses the
    735 // same register as v.
    736 //
    737 // If a is the from operand, this means this operation reads the
    738 // register in v. If a is the to operand, this means the operation
    739 // either reads or writes the register in v (if !copyas(a, v), then
    740 // the operation reads the register in v).
    741 func copyau(a *obj.Addr, v *obj.Addr) bool {
    742 	if copyas(a, v) {
    743 		return true
    744 	}
    745 	if v.Type == obj.TYPE_REG {
    746 		if a.Type == obj.TYPE_MEM || (a.Type == obj.TYPE_ADDR && a.Reg != 0) {
    747 			if v.Reg == a.Reg {
    748 				return true
    749 			}
    750 		}
    751 	}
    752 	return false
    753 }
    754 
    755 // copyau1 returns 1 if p->reg references the same register as v and v
    756 // is a direct reference.
    757 func copyau1(p *obj.Prog, v *obj.Addr) bool {
    758 	if regtyp(v) && v.Reg != 0 {
    759 		if p.Reg == v.Reg {
    760 			return true
    761 		}
    762 	}
    763 	return false
    764 }
    765 
    766 // copysub replaces v with s in a if f!=0 or indicates it if could if f==0.
    767 // Returns 1 on failure to substitute (it always succeeds on arm64).
    768 func copysub(a *obj.Addr, v *obj.Addr, s *obj.Addr, f int) int {
    769 	if f != 0 {
    770 		if copyau(a, v) {
    771 			a.Reg = s.Reg
    772 		}
    773 	}
    774 	return 0
    775 }
    776 
    777 // copysub1 replaces v with s in p1->reg if f!=0 or indicates if it could if f==0.
    778 // Returns 1 on failure to substitute (it always succeeds on arm64).
    779 func copysub1(p1 *obj.Prog, v *obj.Addr, s *obj.Addr, f int) int {
    780 	if f != 0 {
    781 		if copyau1(p1, v) {
    782 			p1.Reg = s.Reg
    783 		}
    784 	}
    785 	return 0
    786 }
    787 
    788 func sameaddr(a *obj.Addr, v *obj.Addr) bool {
    789 	if a.Type != v.Type {
    790 		return false
    791 	}
    792 	if regtyp(v) && a.Reg == v.Reg {
    793 		return true
    794 	}
    795 	if v.Type == obj.NAME_AUTO || v.Type == obj.NAME_PARAM {
    796 		if v.Offset == a.Offset {
    797 			return true
    798 		}
    799 	}
    800 	return false
    801 }
    802 
    803 func smallindir(a *obj.Addr, reg *obj.Addr) bool {
    804 	return reg.Type == obj.TYPE_REG && a.Type == obj.TYPE_MEM && a.Reg == reg.Reg && 0 <= a.Offset && a.Offset < 4096
    805 }
    806 
    807 func stackaddr(a *obj.Addr) bool {
    808 	return a.Type == obj.TYPE_REG && a.Reg == arm64.REGSP
    809 }
    810