github.com/rakyll/go@v0.0.0-20170216000551-64c02460d703/src/cmd/compile/internal/ppc64/ssa.go (about)

     1  // Copyright 2016 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 ppc64
     6  
     7  import (
     8  	"cmd/compile/internal/gc"
     9  	"cmd/compile/internal/ssa"
    10  	"cmd/internal/obj"
    11  	"cmd/internal/obj/ppc64"
    12  	"math"
    13  )
    14  
    15  var condOps = map[ssa.Op]obj.As{
    16  	ssa.OpPPC64Equal:        ppc64.ABEQ,
    17  	ssa.OpPPC64NotEqual:     ppc64.ABNE,
    18  	ssa.OpPPC64LessThan:     ppc64.ABLT,
    19  	ssa.OpPPC64GreaterEqual: ppc64.ABGE,
    20  	ssa.OpPPC64GreaterThan:  ppc64.ABGT,
    21  	ssa.OpPPC64LessEqual:    ppc64.ABLE,
    22  
    23  	ssa.OpPPC64FLessThan:     ppc64.ABLT, // 1 branch for FCMP
    24  	ssa.OpPPC64FGreaterThan:  ppc64.ABGT, // 1 branch for FCMP
    25  	ssa.OpPPC64FLessEqual:    ppc64.ABLT, // 2 branches for FCMP <=, second is BEQ
    26  	ssa.OpPPC64FGreaterEqual: ppc64.ABGT, // 2 branches for FCMP >=, second is BEQ
    27  }
    28  
    29  // iselOp encodes mapping of comparison operations onto ISEL operands
    30  type iselOp struct {
    31  	cond        int64
    32  	valueIfCond int // if cond is true, the value to return (0 or 1)
    33  }
    34  
    35  // Input registers to ISEL used for comparison. Index 0 is zero, 1 is (will be) 1
    36  var iselRegs = [2]int16{ppc64.REG_R0, ppc64.REGTMP}
    37  
    38  var iselOps = map[ssa.Op]iselOp{
    39  	ssa.OpPPC64Equal:         iselOp{cond: ppc64.C_COND_EQ, valueIfCond: 1},
    40  	ssa.OpPPC64NotEqual:      iselOp{cond: ppc64.C_COND_EQ, valueIfCond: 0},
    41  	ssa.OpPPC64LessThan:      iselOp{cond: ppc64.C_COND_LT, valueIfCond: 1},
    42  	ssa.OpPPC64GreaterEqual:  iselOp{cond: ppc64.C_COND_LT, valueIfCond: 0},
    43  	ssa.OpPPC64GreaterThan:   iselOp{cond: ppc64.C_COND_GT, valueIfCond: 1},
    44  	ssa.OpPPC64LessEqual:     iselOp{cond: ppc64.C_COND_GT, valueIfCond: 0},
    45  	ssa.OpPPC64FLessThan:     iselOp{cond: ppc64.C_COND_LT, valueIfCond: 1},
    46  	ssa.OpPPC64FGreaterThan:  iselOp{cond: ppc64.C_COND_GT, valueIfCond: 1},
    47  	ssa.OpPPC64FLessEqual:    iselOp{cond: ppc64.C_COND_LT, valueIfCond: 1}, // 2 comparisons, 2nd is EQ
    48  	ssa.OpPPC64FGreaterEqual: iselOp{cond: ppc64.C_COND_GT, valueIfCond: 1}, // 2 comparisons, 2nd is EQ
    49  }
    50  
    51  // markMoves marks any MOVXconst ops that need to avoid clobbering flags.
    52  func ssaMarkMoves(s *gc.SSAGenState, b *ssa.Block) {
    53  	//	flive := b.FlagsLiveAtEnd
    54  	//	if b.Control != nil && b.Control.Type.IsFlags() {
    55  	//		flive = true
    56  	//	}
    57  	//	for i := len(b.Values) - 1; i >= 0; i-- {
    58  	//		v := b.Values[i]
    59  	//		if flive && (v.Op == v.Op == ssa.OpPPC64MOVDconst) {
    60  	//			// The "mark" is any non-nil Aux value.
    61  	//			v.Aux = v
    62  	//		}
    63  	//		if v.Type.IsFlags() {
    64  	//			flive = false
    65  	//		}
    66  	//		for _, a := range v.Args {
    67  	//			if a.Type.IsFlags() {
    68  	//				flive = true
    69  	//			}
    70  	//		}
    71  	//	}
    72  }
    73  
    74  // loadByType returns the load instruction of the given type.
    75  func loadByType(t ssa.Type) obj.As {
    76  	if t.IsFloat() {
    77  		switch t.Size() {
    78  		case 4:
    79  			return ppc64.AFMOVS
    80  		case 8:
    81  			return ppc64.AFMOVD
    82  		}
    83  	} else {
    84  		switch t.Size() {
    85  		case 1:
    86  			if t.IsSigned() {
    87  				return ppc64.AMOVB
    88  			} else {
    89  				return ppc64.AMOVBZ
    90  			}
    91  		case 2:
    92  			if t.IsSigned() {
    93  				return ppc64.AMOVH
    94  			} else {
    95  				return ppc64.AMOVHZ
    96  			}
    97  		case 4:
    98  			if t.IsSigned() {
    99  				return ppc64.AMOVW
   100  			} else {
   101  				return ppc64.AMOVWZ
   102  			}
   103  		case 8:
   104  			return ppc64.AMOVD
   105  		}
   106  	}
   107  	panic("bad load type")
   108  }
   109  
   110  // storeByType returns the store instruction of the given type.
   111  func storeByType(t ssa.Type) obj.As {
   112  	if t.IsFloat() {
   113  		switch t.Size() {
   114  		case 4:
   115  			return ppc64.AFMOVS
   116  		case 8:
   117  			return ppc64.AFMOVD
   118  		}
   119  	} else {
   120  		switch t.Size() {
   121  		case 1:
   122  			return ppc64.AMOVB
   123  		case 2:
   124  			return ppc64.AMOVH
   125  		case 4:
   126  			return ppc64.AMOVW
   127  		case 8:
   128  			return ppc64.AMOVD
   129  		}
   130  	}
   131  	panic("bad store type")
   132  }
   133  
   134  func ssaGenISEL(v *ssa.Value, cr int64, r1, r2 int16) {
   135  	r := v.Reg()
   136  	p := gc.Prog(ppc64.AISEL)
   137  	p.To.Type = obj.TYPE_REG
   138  	p.To.Reg = r
   139  	p.Reg = r1
   140  	p.From3 = &obj.Addr{Type: obj.TYPE_REG, Reg: r2}
   141  	p.From.Type = obj.TYPE_CONST
   142  	p.From.Offset = cr
   143  }
   144  
   145  func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
   146  	s.SetPos(v.Pos)
   147  	switch v.Op {
   148  	case ssa.OpInitMem:
   149  		// memory arg needs no code
   150  	case ssa.OpArg:
   151  		// input args need no code
   152  	case ssa.OpSP, ssa.OpSB, ssa.OpGetG:
   153  		// nothing to do
   154  
   155  	case ssa.OpCopy, ssa.OpPPC64MOVDconvert:
   156  		t := v.Type
   157  		if t.IsMemory() {
   158  			return
   159  		}
   160  		x := v.Args[0].Reg()
   161  		y := v.Reg()
   162  		if x != y {
   163  			rt := obj.TYPE_REG
   164  			op := ppc64.AMOVD
   165  
   166  			if t.IsFloat() {
   167  				op = ppc64.AFMOVD
   168  			}
   169  			p := gc.Prog(op)
   170  			p.From.Type = rt
   171  			p.From.Reg = x
   172  			p.To.Type = rt
   173  			p.To.Reg = y
   174  		}
   175  
   176  	case ssa.OpPPC64Xf2i64:
   177  		{
   178  			x := v.Args[0].Reg()
   179  			y := v.Reg()
   180  			p := gc.Prog(ppc64.AFMOVD)
   181  			p.From.Type = obj.TYPE_REG
   182  			p.From.Reg = x
   183  			s.AddrScratch(&p.To)
   184  			p = gc.Prog(ppc64.AMOVD)
   185  			p.To.Type = obj.TYPE_REG
   186  			p.To.Reg = y
   187  			s.AddrScratch(&p.From)
   188  		}
   189  	case ssa.OpPPC64Xi2f64:
   190  		{
   191  			x := v.Args[0].Reg()
   192  			y := v.Reg()
   193  			p := gc.Prog(ppc64.AMOVD)
   194  			p.From.Type = obj.TYPE_REG
   195  			p.From.Reg = x
   196  			s.AddrScratch(&p.To)
   197  			p = gc.Prog(ppc64.AFMOVD)
   198  			p.To.Type = obj.TYPE_REG
   199  			p.To.Reg = y
   200  			s.AddrScratch(&p.From)
   201  		}
   202  
   203  	case ssa.OpPPC64LoweredGetClosurePtr:
   204  		// Closure pointer is R11 (already)
   205  		gc.CheckLoweredGetClosurePtr(v)
   206  
   207  	case ssa.OpLoadReg:
   208  		loadOp := loadByType(v.Type)
   209  		p := gc.Prog(loadOp)
   210  		gc.AddrAuto(&p.From, v.Args[0])
   211  		p.To.Type = obj.TYPE_REG
   212  		p.To.Reg = v.Reg()
   213  
   214  	case ssa.OpStoreReg:
   215  		storeOp := storeByType(v.Type)
   216  		p := gc.Prog(storeOp)
   217  		p.From.Type = obj.TYPE_REG
   218  		p.From.Reg = v.Args[0].Reg()
   219  		gc.AddrAuto(&p.To, v)
   220  
   221  	case ssa.OpPPC64DIVD:
   222  		// For now,
   223  		//
   224  		// cmp arg1, -1
   225  		// be  ahead
   226  		// v = arg0 / arg1
   227  		// b over
   228  		// ahead: v = - arg0
   229  		// over: nop
   230  		r := v.Reg()
   231  		r0 := v.Args[0].Reg()
   232  		r1 := v.Args[1].Reg()
   233  
   234  		p := gc.Prog(ppc64.ACMP)
   235  		p.From.Type = obj.TYPE_REG
   236  		p.From.Reg = r1
   237  		p.To.Type = obj.TYPE_CONST
   238  		p.To.Offset = -1
   239  
   240  		pbahead := gc.Prog(ppc64.ABEQ)
   241  		pbahead.To.Type = obj.TYPE_BRANCH
   242  
   243  		p = gc.Prog(v.Op.Asm())
   244  		p.From.Type = obj.TYPE_REG
   245  		p.From.Reg = r1
   246  		p.Reg = r0
   247  		p.To.Type = obj.TYPE_REG
   248  		p.To.Reg = r
   249  
   250  		pbover := gc.Prog(obj.AJMP)
   251  		pbover.To.Type = obj.TYPE_BRANCH
   252  
   253  		p = gc.Prog(ppc64.ANEG)
   254  		p.To.Type = obj.TYPE_REG
   255  		p.To.Reg = r
   256  		p.From.Type = obj.TYPE_REG
   257  		p.From.Reg = r0
   258  		gc.Patch(pbahead, p)
   259  
   260  		p = gc.Prog(obj.ANOP)
   261  		gc.Patch(pbover, p)
   262  
   263  	case ssa.OpPPC64DIVW:
   264  		// word-width version of above
   265  		r := v.Reg()
   266  		r0 := v.Args[0].Reg()
   267  		r1 := v.Args[1].Reg()
   268  
   269  		p := gc.Prog(ppc64.ACMPW)
   270  		p.From.Type = obj.TYPE_REG
   271  		p.From.Reg = r1
   272  		p.To.Type = obj.TYPE_CONST
   273  		p.To.Offset = -1
   274  
   275  		pbahead := gc.Prog(ppc64.ABEQ)
   276  		pbahead.To.Type = obj.TYPE_BRANCH
   277  
   278  		p = gc.Prog(v.Op.Asm())
   279  		p.From.Type = obj.TYPE_REG
   280  		p.From.Reg = r1
   281  		p.Reg = r0
   282  		p.To.Type = obj.TYPE_REG
   283  		p.To.Reg = r
   284  
   285  		pbover := gc.Prog(obj.AJMP)
   286  		pbover.To.Type = obj.TYPE_BRANCH
   287  
   288  		p = gc.Prog(ppc64.ANEG)
   289  		p.To.Type = obj.TYPE_REG
   290  		p.To.Reg = r
   291  		p.From.Type = obj.TYPE_REG
   292  		p.From.Reg = r0
   293  		gc.Patch(pbahead, p)
   294  
   295  		p = gc.Prog(obj.ANOP)
   296  		gc.Patch(pbover, p)
   297  
   298  	case ssa.OpPPC64ADD, ssa.OpPPC64FADD, ssa.OpPPC64FADDS, ssa.OpPPC64SUB, ssa.OpPPC64FSUB, ssa.OpPPC64FSUBS,
   299  		ssa.OpPPC64MULLD, ssa.OpPPC64MULLW, ssa.OpPPC64DIVDU, ssa.OpPPC64DIVWU,
   300  		ssa.OpPPC64SRAD, ssa.OpPPC64SRAW, ssa.OpPPC64SRD, ssa.OpPPC64SRW, ssa.OpPPC64SLD, ssa.OpPPC64SLW,
   301  		ssa.OpPPC64MULHD, ssa.OpPPC64MULHW, ssa.OpPPC64MULHDU, ssa.OpPPC64MULHWU,
   302  		ssa.OpPPC64FMUL, ssa.OpPPC64FMULS, ssa.OpPPC64FDIV, ssa.OpPPC64FDIVS,
   303  		ssa.OpPPC64AND, ssa.OpPPC64OR, ssa.OpPPC64ANDN, ssa.OpPPC64ORN, ssa.OpPPC64NOR, ssa.OpPPC64XOR, ssa.OpPPC64EQV:
   304  		r := v.Reg()
   305  		r1 := v.Args[0].Reg()
   306  		r2 := v.Args[1].Reg()
   307  		p := gc.Prog(v.Op.Asm())
   308  		p.From.Type = obj.TYPE_REG
   309  		p.From.Reg = r2
   310  		p.Reg = r1
   311  		p.To.Type = obj.TYPE_REG
   312  		p.To.Reg = r
   313  
   314  	case ssa.OpPPC64MaskIfNotCarry:
   315  		r := v.Reg()
   316  		p := gc.Prog(v.Op.Asm())
   317  		p.From.Type = obj.TYPE_REG
   318  		p.From.Reg = ppc64.REGZERO
   319  		p.To.Type = obj.TYPE_REG
   320  		p.To.Reg = r
   321  
   322  	case ssa.OpPPC64ADDconstForCarry:
   323  		r1 := v.Args[0].Reg()
   324  		p := gc.Prog(v.Op.Asm())
   325  		p.Reg = r1
   326  		p.From.Type = obj.TYPE_CONST
   327  		p.From.Offset = v.AuxInt
   328  		p.To.Type = obj.TYPE_REG
   329  		p.To.Reg = ppc64.REGTMP // Ignored; this is for the carry effect.
   330  
   331  	case ssa.OpPPC64NEG, ssa.OpPPC64FNEG, ssa.OpPPC64FSQRT, ssa.OpPPC64FSQRTS, ssa.OpPPC64FCTIDZ, ssa.OpPPC64FCTIWZ, ssa.OpPPC64FCFID, ssa.OpPPC64FRSP:
   332  		r := v.Reg()
   333  		p := gc.Prog(v.Op.Asm())
   334  		p.To.Type = obj.TYPE_REG
   335  		p.To.Reg = r
   336  		p.From.Type = obj.TYPE_REG
   337  		p.From.Reg = v.Args[0].Reg()
   338  
   339  	case ssa.OpPPC64ADDconst, ssa.OpPPC64ANDconst, ssa.OpPPC64ORconst, ssa.OpPPC64XORconst,
   340  		ssa.OpPPC64SRADconst, ssa.OpPPC64SRAWconst, ssa.OpPPC64SRDconst, ssa.OpPPC64SRWconst, ssa.OpPPC64SLDconst, ssa.OpPPC64SLWconst:
   341  		p := gc.Prog(v.Op.Asm())
   342  		p.Reg = v.Args[0].Reg()
   343  
   344  		if v.Aux != nil {
   345  			p.From.Type = obj.TYPE_CONST
   346  			p.From.Offset = gc.AuxOffset(v)
   347  		} else {
   348  			p.From.Type = obj.TYPE_CONST
   349  			p.From.Offset = v.AuxInt
   350  		}
   351  
   352  		p.To.Type = obj.TYPE_REG
   353  		p.To.Reg = v.Reg()
   354  
   355  	case ssa.OpPPC64ANDCCconst:
   356  		p := gc.Prog(v.Op.Asm())
   357  		p.Reg = v.Args[0].Reg()
   358  
   359  		if v.Aux != nil {
   360  			p.From.Type = obj.TYPE_CONST
   361  			p.From.Offset = gc.AuxOffset(v)
   362  		} else {
   363  			p.From.Type = obj.TYPE_CONST
   364  			p.From.Offset = v.AuxInt
   365  		}
   366  
   367  		p.To.Type = obj.TYPE_REG
   368  		p.To.Reg = ppc64.REGTMP // discard result
   369  
   370  	case ssa.OpPPC64MOVDaddr:
   371  		p := gc.Prog(ppc64.AMOVD)
   372  		p.From.Type = obj.TYPE_ADDR
   373  		p.To.Type = obj.TYPE_REG
   374  		p.To.Reg = v.Reg()
   375  
   376  		var wantreg string
   377  		// Suspect comment, copied from ARM code
   378  		// MOVD $sym+off(base), R
   379  		// the assembler expands it as the following:
   380  		// - base is SP: add constant offset to SP
   381  		//               when constant is large, tmp register (R11) may be used
   382  		// - base is SB: load external address from constant pool (use relocation)
   383  		switch v.Aux.(type) {
   384  		default:
   385  			v.Fatalf("aux is of unknown type %T", v.Aux)
   386  		case *ssa.ExternSymbol:
   387  			wantreg = "SB"
   388  			gc.AddAux(&p.From, v)
   389  		case *ssa.ArgSymbol, *ssa.AutoSymbol:
   390  			wantreg = "SP"
   391  			gc.AddAux(&p.From, v)
   392  		case nil:
   393  			// No sym, just MOVD $off(SP), R
   394  			wantreg = "SP"
   395  			p.From.Reg = ppc64.REGSP
   396  			p.From.Offset = v.AuxInt
   397  		}
   398  		if reg := v.Args[0].RegName(); reg != wantreg {
   399  			v.Fatalf("bad reg %s for symbol type %T, want %s", reg, v.Aux, wantreg)
   400  		}
   401  
   402  	case ssa.OpPPC64MOVDconst:
   403  		p := gc.Prog(v.Op.Asm())
   404  		p.From.Type = obj.TYPE_CONST
   405  		p.From.Offset = v.AuxInt
   406  		p.To.Type = obj.TYPE_REG
   407  		p.To.Reg = v.Reg()
   408  
   409  	case ssa.OpPPC64FMOVDconst, ssa.OpPPC64FMOVSconst:
   410  		p := gc.Prog(v.Op.Asm())
   411  		p.From.Type = obj.TYPE_FCONST
   412  		p.From.Val = math.Float64frombits(uint64(v.AuxInt))
   413  		p.To.Type = obj.TYPE_REG
   414  		p.To.Reg = v.Reg()
   415  
   416  	case ssa.OpPPC64FCMPU, ssa.OpPPC64CMP, ssa.OpPPC64CMPW, ssa.OpPPC64CMPU, ssa.OpPPC64CMPWU:
   417  		p := gc.Prog(v.Op.Asm())
   418  		p.From.Type = obj.TYPE_REG
   419  		p.From.Reg = v.Args[0].Reg()
   420  		p.To.Type = obj.TYPE_REG
   421  		p.To.Reg = v.Args[1].Reg()
   422  
   423  	case ssa.OpPPC64CMPconst, ssa.OpPPC64CMPUconst, ssa.OpPPC64CMPWconst, ssa.OpPPC64CMPWUconst:
   424  		p := gc.Prog(v.Op.Asm())
   425  		p.From.Type = obj.TYPE_REG
   426  		p.From.Reg = v.Args[0].Reg()
   427  		p.To.Type = obj.TYPE_CONST
   428  		p.To.Offset = v.AuxInt
   429  
   430  	case ssa.OpPPC64MOVBreg, ssa.OpPPC64MOVBZreg, ssa.OpPPC64MOVHreg, ssa.OpPPC64MOVHZreg, ssa.OpPPC64MOVWreg, ssa.OpPPC64MOVWZreg:
   431  		// Shift in register to required size
   432  		p := gc.Prog(v.Op.Asm())
   433  		p.From.Type = obj.TYPE_REG
   434  		p.From.Reg = v.Args[0].Reg()
   435  		p.To.Reg = v.Reg()
   436  		p.To.Type = obj.TYPE_REG
   437  
   438  	case ssa.OpPPC64MOVDload, ssa.OpPPC64MOVWload, ssa.OpPPC64MOVHload, ssa.OpPPC64MOVWZload, ssa.OpPPC64MOVBZload, ssa.OpPPC64MOVHZload:
   439  		p := gc.Prog(v.Op.Asm())
   440  		p.From.Type = obj.TYPE_MEM
   441  		p.From.Reg = v.Args[0].Reg()
   442  		gc.AddAux(&p.From, v)
   443  		p.To.Type = obj.TYPE_REG
   444  		p.To.Reg = v.Reg()
   445  
   446  	case ssa.OpPPC64FMOVDload, ssa.OpPPC64FMOVSload:
   447  		p := gc.Prog(v.Op.Asm())
   448  		p.From.Type = obj.TYPE_MEM
   449  		p.From.Reg = v.Args[0].Reg()
   450  		gc.AddAux(&p.From, v)
   451  		p.To.Type = obj.TYPE_REG
   452  		p.To.Reg = v.Reg()
   453  
   454  	case ssa.OpPPC64MOVDstorezero, ssa.OpPPC64MOVWstorezero, ssa.OpPPC64MOVHstorezero, ssa.OpPPC64MOVBstorezero:
   455  		p := gc.Prog(v.Op.Asm())
   456  		p.From.Type = obj.TYPE_REG
   457  		p.From.Reg = ppc64.REGZERO
   458  		p.To.Type = obj.TYPE_MEM
   459  		p.To.Reg = v.Args[0].Reg()
   460  		gc.AddAux(&p.To, v)
   461  
   462  	case ssa.OpPPC64MOVDstore, ssa.OpPPC64MOVWstore, ssa.OpPPC64MOVHstore, ssa.OpPPC64MOVBstore:
   463  		p := gc.Prog(v.Op.Asm())
   464  		p.From.Type = obj.TYPE_REG
   465  		p.From.Reg = v.Args[1].Reg()
   466  		p.To.Type = obj.TYPE_MEM
   467  		p.To.Reg = v.Args[0].Reg()
   468  		gc.AddAux(&p.To, v)
   469  	case ssa.OpPPC64FMOVDstore, ssa.OpPPC64FMOVSstore:
   470  		p := gc.Prog(v.Op.Asm())
   471  		p.From.Type = obj.TYPE_REG
   472  		p.From.Reg = v.Args[1].Reg()
   473  		p.To.Type = obj.TYPE_MEM
   474  		p.To.Reg = v.Args[0].Reg()
   475  		gc.AddAux(&p.To, v)
   476  
   477  	case ssa.OpPPC64Equal,
   478  		ssa.OpPPC64NotEqual,
   479  		ssa.OpPPC64LessThan,
   480  		ssa.OpPPC64FLessThan,
   481  		ssa.OpPPC64LessEqual,
   482  		ssa.OpPPC64GreaterThan,
   483  		ssa.OpPPC64FGreaterThan,
   484  		ssa.OpPPC64GreaterEqual:
   485  
   486  		// On Power7 or later, can use isel instruction:
   487  		// for a < b, a > b, a = b:
   488  		//   rtmp := 1
   489  		//   isel rt,rtmp,r0,cond // rt is target in ppc asm
   490  
   491  		// for  a >= b, a <= b, a != b:
   492  		//   rtmp := 1
   493  		//   isel rt,0,rtmp,!cond // rt is target in ppc asm
   494  
   495  		if v.Block.Func.Config.OldArch {
   496  			p := gc.Prog(ppc64.AMOVD)
   497  			p.From.Type = obj.TYPE_CONST
   498  			p.From.Offset = 1
   499  			p.To.Type = obj.TYPE_REG
   500  			p.To.Reg = v.Reg()
   501  
   502  			pb := gc.Prog(condOps[v.Op])
   503  			pb.To.Type = obj.TYPE_BRANCH
   504  
   505  			p = gc.Prog(ppc64.AMOVD)
   506  			p.From.Type = obj.TYPE_CONST
   507  			p.From.Offset = 0
   508  			p.To.Type = obj.TYPE_REG
   509  			p.To.Reg = v.Reg()
   510  
   511  			p = gc.Prog(obj.ANOP)
   512  			gc.Patch(pb, p)
   513  			break
   514  		}
   515  		// Modern PPC uses ISEL
   516  		p := gc.Prog(ppc64.AMOVD)
   517  		p.From.Type = obj.TYPE_CONST
   518  		p.From.Offset = 1
   519  		p.To.Type = obj.TYPE_REG
   520  		p.To.Reg = iselRegs[1]
   521  		iop := iselOps[v.Op]
   522  		ssaGenISEL(v, iop.cond, iselRegs[iop.valueIfCond], iselRegs[1-iop.valueIfCond])
   523  
   524  	case ssa.OpPPC64FLessEqual, // These include a second branch for EQ -- dealing with NaN prevents REL= to !REL conversion
   525  		ssa.OpPPC64FGreaterEqual:
   526  
   527  		if v.Block.Func.Config.OldArch {
   528  			p := gc.Prog(ppc64.AMOVW)
   529  			p.From.Type = obj.TYPE_CONST
   530  			p.From.Offset = 1
   531  			p.To.Type = obj.TYPE_REG
   532  			p.To.Reg = v.Reg()
   533  
   534  			pb0 := gc.Prog(condOps[v.Op])
   535  			pb0.To.Type = obj.TYPE_BRANCH
   536  			pb1 := gc.Prog(ppc64.ABEQ)
   537  			pb1.To.Type = obj.TYPE_BRANCH
   538  
   539  			p = gc.Prog(ppc64.AMOVW)
   540  			p.From.Type = obj.TYPE_CONST
   541  			p.From.Offset = 0
   542  			p.To.Type = obj.TYPE_REG
   543  			p.To.Reg = v.Reg()
   544  
   545  			p = gc.Prog(obj.ANOP)
   546  			gc.Patch(pb0, p)
   547  			gc.Patch(pb1, p)
   548  			break
   549  		}
   550  		// Modern PPC uses ISEL
   551  		p := gc.Prog(ppc64.AMOVD)
   552  		p.From.Type = obj.TYPE_CONST
   553  		p.From.Offset = 1
   554  		p.To.Type = obj.TYPE_REG
   555  		p.To.Reg = iselRegs[1]
   556  		iop := iselOps[v.Op]
   557  		ssaGenISEL(v, iop.cond, iselRegs[iop.valueIfCond], iselRegs[1-iop.valueIfCond])
   558  		ssaGenISEL(v, ppc64.C_COND_EQ, iselRegs[1], v.Reg())
   559  
   560  	case ssa.OpPPC64LoweredZero:
   561  		// Similar to how this is done on ARM,
   562  		// except that PPC MOVDU x,off(y) is *(y+off) = x; y=y+off
   563  		// not store-and-increment.
   564  		// Therefore R3 should be dest-align
   565  		// and arg1 should be dest+size-align
   566  		// HOWEVER, the input dest address cannot be dest-align because
   567  		// that does not necessarily address valid memory and it's not
   568  		// known how that might be optimized.  Therefore, correct it in
   569  		// in the expansion:
   570  		//
   571  		// ADD    -8,R3,R3
   572  		// MOVDU  R0, 8(R3)
   573  		// CMP	  R3, Rarg1
   574  		// BL	  -2(PC)
   575  		// arg1 is the address of the last element to zero
   576  		// auxint is alignment
   577  		var sz int64
   578  		var movu obj.As
   579  		switch {
   580  		case v.AuxInt%8 == 0:
   581  			sz = 8
   582  			movu = ppc64.AMOVDU
   583  		case v.AuxInt%4 == 0:
   584  			sz = 4
   585  			movu = ppc64.AMOVWZU // MOVWU instruction not implemented
   586  		case v.AuxInt%2 == 0:
   587  			sz = 2
   588  			movu = ppc64.AMOVHU
   589  		default:
   590  			sz = 1
   591  			movu = ppc64.AMOVBU
   592  		}
   593  
   594  		p := gc.Prog(ppc64.AADD)
   595  		p.Reg = v.Args[0].Reg()
   596  		p.From.Type = obj.TYPE_CONST
   597  		p.From.Offset = -sz
   598  		p.To.Type = obj.TYPE_REG
   599  		p.To.Reg = v.Args[0].Reg()
   600  
   601  		p = gc.Prog(movu)
   602  		p.From.Type = obj.TYPE_REG
   603  		p.From.Reg = ppc64.REG_R0
   604  		p.To.Type = obj.TYPE_MEM
   605  		p.To.Reg = v.Args[0].Reg()
   606  		p.To.Offset = sz
   607  
   608  		p2 := gc.Prog(ppc64.ACMPU)
   609  		p2.From.Type = obj.TYPE_REG
   610  		p2.From.Reg = v.Args[0].Reg()
   611  		p2.To.Reg = v.Args[1].Reg()
   612  		p2.To.Type = obj.TYPE_REG
   613  
   614  		p3 := gc.Prog(ppc64.ABLT)
   615  		p3.To.Type = obj.TYPE_BRANCH
   616  		gc.Patch(p3, p)
   617  
   618  	case ssa.OpPPC64LoweredMove:
   619  		// Similar to how this is done on ARM,
   620  		// except that PPC MOVDU x,off(y) is *(y+off) = x; y=y+off,
   621  		// not store-and-increment.
   622  		// Inputs must be valid pointers to memory,
   623  		// so adjust arg0 and arg1 as part of the expansion.
   624  		// arg2 should be src+size-align,
   625  		//
   626  		// ADD    -8,R3,R3
   627  		// ADD    -8,R4,R4
   628  		// MOVDU	8(R4), Rtmp
   629  		// MOVDU 	Rtmp, 8(R3)
   630  		// CMP	R4, Rarg2
   631  		// BL	-3(PC)
   632  		// arg2 is the address of the last element of src
   633  		// auxint is alignment
   634  		var sz int64
   635  		var movu obj.As
   636  		switch {
   637  		case v.AuxInt%8 == 0:
   638  			sz = 8
   639  			movu = ppc64.AMOVDU
   640  		case v.AuxInt%4 == 0:
   641  			sz = 4
   642  			movu = ppc64.AMOVWZU // MOVWU instruction not implemented
   643  		case v.AuxInt%2 == 0:
   644  			sz = 2
   645  			movu = ppc64.AMOVHU
   646  		default:
   647  			sz = 1
   648  			movu = ppc64.AMOVBU
   649  		}
   650  
   651  		p := gc.Prog(ppc64.AADD)
   652  		p.Reg = v.Args[0].Reg()
   653  		p.From.Type = obj.TYPE_CONST
   654  		p.From.Offset = -sz
   655  		p.To.Type = obj.TYPE_REG
   656  		p.To.Reg = v.Args[0].Reg()
   657  
   658  		p = gc.Prog(ppc64.AADD)
   659  		p.Reg = v.Args[1].Reg()
   660  		p.From.Type = obj.TYPE_CONST
   661  		p.From.Offset = -sz
   662  		p.To.Type = obj.TYPE_REG
   663  		p.To.Reg = v.Args[1].Reg()
   664  
   665  		p = gc.Prog(movu)
   666  		p.From.Type = obj.TYPE_MEM
   667  		p.From.Reg = v.Args[1].Reg()
   668  		p.From.Offset = sz
   669  		p.To.Type = obj.TYPE_REG
   670  		p.To.Reg = ppc64.REGTMP
   671  
   672  		p2 := gc.Prog(movu)
   673  		p2.From.Type = obj.TYPE_REG
   674  		p2.From.Reg = ppc64.REGTMP
   675  		p2.To.Type = obj.TYPE_MEM
   676  		p2.To.Reg = v.Args[0].Reg()
   677  		p2.To.Offset = sz
   678  
   679  		p3 := gc.Prog(ppc64.ACMPU)
   680  		p3.From.Reg = v.Args[1].Reg()
   681  		p3.From.Type = obj.TYPE_REG
   682  		p3.To.Reg = v.Args[2].Reg()
   683  		p3.To.Type = obj.TYPE_REG
   684  
   685  		p4 := gc.Prog(ppc64.ABLT)
   686  		p4.To.Type = obj.TYPE_BRANCH
   687  		gc.Patch(p4, p)
   688  
   689  	case ssa.OpPPC64CALLstatic:
   690  		if v.Aux.(*obj.LSym) == gc.Deferreturn {
   691  			// Deferred calls will appear to be returning to
   692  			// the CALL deferreturn(SB) that we are about to emit.
   693  			// However, the stack trace code will show the line
   694  			// of the instruction byte before the return PC.
   695  			// To avoid that being an unrelated instruction,
   696  			// insert two actual hardware NOPs that will have the right line number.
   697  			// This is different from obj.ANOP, which is a virtual no-op
   698  			// that doesn't make it into the instruction stream.
   699  			// PPC64 is unusual because TWO nops are required
   700  			// (see gc/cgen.go, gc/plive.go -- copy of comment below)
   701  			//
   702  			// On ppc64, when compiling Go into position
   703  			// independent code on ppc64le we insert an
   704  			// instruction to reload the TOC pointer from the
   705  			// stack as well. See the long comment near
   706  			// jmpdefer in runtime/asm_ppc64.s for why.
   707  			// If the MOVD is not needed, insert a hardware NOP
   708  			// so that the same number of instructions are used
   709  			// on ppc64 in both shared and non-shared modes.
   710  			ginsnop()
   711  			if gc.Ctxt.Flag_shared {
   712  				p := gc.Prog(ppc64.AMOVD)
   713  				p.From.Type = obj.TYPE_MEM
   714  				p.From.Offset = 24
   715  				p.From.Reg = ppc64.REGSP
   716  				p.To.Type = obj.TYPE_REG
   717  				p.To.Reg = ppc64.REG_R2
   718  			} else {
   719  				ginsnop()
   720  			}
   721  		}
   722  		p := gc.Prog(obj.ACALL)
   723  		p.To.Type = obj.TYPE_MEM
   724  		p.To.Name = obj.NAME_EXTERN
   725  		p.To.Sym = v.Aux.(*obj.LSym)
   726  		if gc.Maxarg < v.AuxInt {
   727  			gc.Maxarg = v.AuxInt
   728  		}
   729  
   730  	case ssa.OpPPC64CALLclosure, ssa.OpPPC64CALLinter:
   731  		p := gc.Prog(ppc64.AMOVD)
   732  		p.From.Type = obj.TYPE_REG
   733  		p.From.Reg = v.Args[0].Reg()
   734  		p.To.Type = obj.TYPE_REG
   735  		p.To.Reg = ppc64.REG_CTR
   736  
   737  		if gc.Ctxt.Flag_shared && p.From.Reg != ppc64.REG_R12 {
   738  			// Make sure function pointer is in R12 as well when
   739  			// compiling Go into PIC.
   740  			// TODO(mwhudson): it would obviously be better to
   741  			// change the register allocation to put the value in
   742  			// R12 already, but I don't know how to do that.
   743  			// TODO: We have the technology now to implement TODO above.
   744  			q := gc.Prog(ppc64.AMOVD)
   745  			q.From = p.From
   746  			q.To.Type = obj.TYPE_REG
   747  			q.To.Reg = ppc64.REG_R12
   748  		}
   749  
   750  		pp := gc.Prog(obj.ACALL)
   751  		pp.To.Type = obj.TYPE_REG
   752  		pp.To.Reg = ppc64.REG_CTR
   753  
   754  		if gc.Ctxt.Flag_shared {
   755  			// When compiling Go into PIC, the function we just
   756  			// called via pointer might have been implemented in
   757  			// a separate module and so overwritten the TOC
   758  			// pointer in R2; reload it.
   759  			q := gc.Prog(ppc64.AMOVD)
   760  			q.From.Type = obj.TYPE_MEM
   761  			q.From.Offset = 24
   762  			q.From.Reg = ppc64.REGSP
   763  			q.To.Type = obj.TYPE_REG
   764  			q.To.Reg = ppc64.REG_R2
   765  		}
   766  
   767  		if gc.Maxarg < v.AuxInt {
   768  			gc.Maxarg = v.AuxInt
   769  		}
   770  
   771  	case ssa.OpPPC64CALLdefer:
   772  		p := gc.Prog(obj.ACALL)
   773  		p.To.Type = obj.TYPE_MEM
   774  		p.To.Name = obj.NAME_EXTERN
   775  		p.To.Sym = gc.Deferproc
   776  		if gc.Maxarg < v.AuxInt {
   777  			gc.Maxarg = v.AuxInt
   778  		}
   779  	case ssa.OpPPC64CALLgo:
   780  		p := gc.Prog(obj.ACALL)
   781  		p.To.Type = obj.TYPE_MEM
   782  		p.To.Name = obj.NAME_EXTERN
   783  		p.To.Sym = gc.Newproc
   784  		if gc.Maxarg < v.AuxInt {
   785  			gc.Maxarg = v.AuxInt
   786  		}
   787  	case ssa.OpVarDef:
   788  		gc.Gvardef(v.Aux.(*gc.Node))
   789  	case ssa.OpVarKill:
   790  		gc.Gvarkill(v.Aux.(*gc.Node))
   791  	case ssa.OpVarLive:
   792  		gc.Gvarlive(v.Aux.(*gc.Node))
   793  	case ssa.OpKeepAlive:
   794  		gc.KeepAlive(v)
   795  	case ssa.OpPhi:
   796  		gc.CheckLoweredPhi(v)
   797  
   798  	case ssa.OpPPC64LoweredNilCheck:
   799  		// Issue a load which will fault if arg is nil.
   800  		p := gc.Prog(ppc64.AMOVBZ)
   801  		p.From.Type = obj.TYPE_MEM
   802  		p.From.Reg = v.Args[0].Reg()
   803  		gc.AddAux(&p.From, v)
   804  		p.To.Type = obj.TYPE_REG
   805  		p.To.Reg = ppc64.REGTMP
   806  		if gc.Debug_checknil != 0 && v.Pos.Line() > 1 { // v.Pos.Line()==1 in generated wrappers
   807  			gc.Warnl(v.Pos, "generated nil check")
   808  		}
   809  
   810  	case ssa.OpPPC64InvertFlags:
   811  		v.Fatalf("InvertFlags should never make it to codegen %v", v.LongString())
   812  	case ssa.OpPPC64FlagEQ, ssa.OpPPC64FlagLT, ssa.OpPPC64FlagGT:
   813  		v.Fatalf("Flag* ops should never make it to codegen %v", v.LongString())
   814  
   815  	default:
   816  		v.Fatalf("genValue not implemented: %s", v.LongString())
   817  	}
   818  }
   819  
   820  var blockJump = [...]struct {
   821  	asm, invasm     obj.As
   822  	asmeq, invasmun bool
   823  }{
   824  	ssa.BlockPPC64EQ: {ppc64.ABEQ, ppc64.ABNE, false, false},
   825  	ssa.BlockPPC64NE: {ppc64.ABNE, ppc64.ABEQ, false, false},
   826  
   827  	ssa.BlockPPC64LT: {ppc64.ABLT, ppc64.ABGE, false, false},
   828  	ssa.BlockPPC64GE: {ppc64.ABGE, ppc64.ABLT, false, false},
   829  	ssa.BlockPPC64LE: {ppc64.ABLE, ppc64.ABGT, false, false},
   830  	ssa.BlockPPC64GT: {ppc64.ABGT, ppc64.ABLE, false, false},
   831  
   832  	// TODO: need to work FP comparisons into block jumps
   833  	ssa.BlockPPC64FLT: {ppc64.ABLT, ppc64.ABGE, false, false},
   834  	ssa.BlockPPC64FGE: {ppc64.ABGT, ppc64.ABLT, true, true}, // GE = GT or EQ; !GE = LT or UN
   835  	ssa.BlockPPC64FLE: {ppc64.ABLT, ppc64.ABGT, true, true}, // LE = LT or EQ; !LE = GT or UN
   836  	ssa.BlockPPC64FGT: {ppc64.ABGT, ppc64.ABLE, false, false},
   837  }
   838  
   839  func ssaGenBlock(s *gc.SSAGenState, b, next *ssa.Block) {
   840  	s.SetPos(b.Pos)
   841  
   842  	switch b.Kind {
   843  
   844  	case ssa.BlockDefer:
   845  		// defer returns in R3:
   846  		// 0 if we should continue executing
   847  		// 1 if we should jump to deferreturn call
   848  		p := gc.Prog(ppc64.ACMP)
   849  		p.From.Type = obj.TYPE_REG
   850  		p.From.Reg = ppc64.REG_R3
   851  		p.To.Type = obj.TYPE_REG
   852  		p.To.Reg = ppc64.REG_R0
   853  
   854  		p = gc.Prog(ppc64.ABNE)
   855  		p.To.Type = obj.TYPE_BRANCH
   856  		s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[1].Block()})
   857  		if b.Succs[0].Block() != next {
   858  			p := gc.Prog(obj.AJMP)
   859  			p.To.Type = obj.TYPE_BRANCH
   860  			s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[0].Block()})
   861  		}
   862  
   863  	case ssa.BlockPlain:
   864  		if b.Succs[0].Block() != next {
   865  			p := gc.Prog(obj.AJMP)
   866  			p.To.Type = obj.TYPE_BRANCH
   867  			s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[0].Block()})
   868  		}
   869  	case ssa.BlockExit:
   870  		gc.Prog(obj.AUNDEF) // tell plive.go that we never reach here
   871  	case ssa.BlockRet:
   872  		gc.Prog(obj.ARET)
   873  	case ssa.BlockRetJmp:
   874  		p := gc.Prog(obj.AJMP)
   875  		p.To.Type = obj.TYPE_MEM
   876  		p.To.Name = obj.NAME_EXTERN
   877  		p.To.Sym = b.Aux.(*obj.LSym)
   878  
   879  	case ssa.BlockPPC64EQ, ssa.BlockPPC64NE,
   880  		ssa.BlockPPC64LT, ssa.BlockPPC64GE,
   881  		ssa.BlockPPC64LE, ssa.BlockPPC64GT,
   882  		ssa.BlockPPC64FLT, ssa.BlockPPC64FGE,
   883  		ssa.BlockPPC64FLE, ssa.BlockPPC64FGT:
   884  		jmp := blockJump[b.Kind]
   885  		likely := b.Likely
   886  		var p *obj.Prog
   887  		switch next {
   888  		case b.Succs[0].Block():
   889  			p = gc.Prog(jmp.invasm)
   890  			likely *= -1
   891  			p.To.Type = obj.TYPE_BRANCH
   892  			s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[1].Block()})
   893  			if jmp.invasmun {
   894  				// TODO: The second branch is probably predict-not-taken since it is for FP unordered
   895  				q := gc.Prog(ppc64.ABVS)
   896  				q.To.Type = obj.TYPE_BRANCH
   897  				s.Branches = append(s.Branches, gc.Branch{P: q, B: b.Succs[1].Block()})
   898  			}
   899  		case b.Succs[1].Block():
   900  			p = gc.Prog(jmp.asm)
   901  			p.To.Type = obj.TYPE_BRANCH
   902  			s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[0].Block()})
   903  			if jmp.asmeq {
   904  				q := gc.Prog(ppc64.ABEQ)
   905  				q.To.Type = obj.TYPE_BRANCH
   906  				s.Branches = append(s.Branches, gc.Branch{P: q, B: b.Succs[0].Block()})
   907  			}
   908  		default:
   909  			p = gc.Prog(jmp.asm)
   910  			p.To.Type = obj.TYPE_BRANCH
   911  			s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[0].Block()})
   912  			if jmp.asmeq {
   913  				q := gc.Prog(ppc64.ABEQ)
   914  				q.To.Type = obj.TYPE_BRANCH
   915  				s.Branches = append(s.Branches, gc.Branch{P: q, B: b.Succs[0].Block()})
   916  			}
   917  			q := gc.Prog(obj.AJMP)
   918  			q.To.Type = obj.TYPE_BRANCH
   919  			s.Branches = append(s.Branches, gc.Branch{P: q, B: b.Succs[1].Block()})
   920  		}
   921  
   922  		// liblink reorders the instruction stream as it sees fit.
   923  		// Pass along what we know so liblink can make use of it.
   924  		// TODO: Once we've fully switched to SSA,
   925  		// make liblink leave our output alone.
   926  		//switch likely {
   927  		//case ssa.BranchUnlikely:
   928  		//	p.From.Type = obj.TYPE_CONST
   929  		//	p.From.Offset = 0
   930  		//case ssa.BranchLikely:
   931  		//	p.From.Type = obj.TYPE_CONST
   932  		//	p.From.Offset = 1
   933  		//}
   934  
   935  	default:
   936  		b.Fatalf("branch not implemented: %s. Control: %s", b.LongString(), b.Control.LongString())
   937  	}
   938  }