Source file src/cmd/compile/internal/x86/ssa.go

     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 x86
     6  
     7  import (
     8  	"fmt"
     9  	"math"
    10  
    11  	"cmd/compile/internal/base"
    12  	"cmd/compile/internal/ir"
    13  	"cmd/compile/internal/logopt"
    14  	"cmd/compile/internal/ssa"
    15  	"cmd/compile/internal/ssagen"
    16  	"cmd/compile/internal/types"
    17  	"cmd/internal/obj"
    18  	"cmd/internal/obj/x86"
    19  )
    20  
    21  // markMoves marks any MOVXconst ops that need to avoid clobbering flags.
    22  func ssaMarkMoves(s *ssagen.State, b *ssa.Block) {
    23  	flive := b.FlagsLiveAtEnd
    24  	for _, c := range b.ControlValues() {
    25  		flive = c.Type.IsFlags() || flive
    26  	}
    27  	for i := len(b.Values) - 1; i >= 0; i-- {
    28  		v := b.Values[i]
    29  		if flive && v.Op == ssa.Op386MOVLconst {
    30  			// The "mark" is any non-nil Aux value.
    31  			v.Aux = v
    32  		}
    33  		if v.Type.IsFlags() {
    34  			flive = false
    35  		}
    36  		for _, a := range v.Args {
    37  			if a.Type.IsFlags() {
    38  				flive = true
    39  			}
    40  		}
    41  	}
    42  }
    43  
    44  // loadByType returns the load instruction of the given type.
    45  func loadByType(t *types.Type) obj.As {
    46  	// Avoid partial register write
    47  	if !t.IsFloat() {
    48  		switch t.Size() {
    49  		case 1:
    50  			return x86.AMOVBLZX
    51  		case 2:
    52  			return x86.AMOVWLZX
    53  		}
    54  	}
    55  	// Otherwise, there's no difference between load and store opcodes.
    56  	return storeByType(t)
    57  }
    58  
    59  // storeByType returns the store instruction of the given type.
    60  func storeByType(t *types.Type) obj.As {
    61  	width := t.Size()
    62  	if t.IsFloat() {
    63  		switch width {
    64  		case 4:
    65  			return x86.AMOVSS
    66  		case 8:
    67  			return x86.AMOVSD
    68  		}
    69  	} else {
    70  		switch width {
    71  		case 1:
    72  			return x86.AMOVB
    73  		case 2:
    74  			return x86.AMOVW
    75  		case 4:
    76  			return x86.AMOVL
    77  		}
    78  	}
    79  	panic("bad store type")
    80  }
    81  
    82  // moveByType returns the reg->reg move instruction of the given type.
    83  func moveByType(t *types.Type) obj.As {
    84  	if t.IsFloat() {
    85  		switch t.Size() {
    86  		case 4:
    87  			return x86.AMOVSS
    88  		case 8:
    89  			return x86.AMOVSD
    90  		default:
    91  			panic(fmt.Sprintf("bad float register width %d:%s", t.Size(), t))
    92  		}
    93  	} else {
    94  		switch t.Size() {
    95  		case 1:
    96  			// Avoids partial register write
    97  			return x86.AMOVL
    98  		case 2:
    99  			return x86.AMOVL
   100  		case 4:
   101  			return x86.AMOVL
   102  		default:
   103  			panic(fmt.Sprintf("bad int register width %d:%s", t.Size(), t))
   104  		}
   105  	}
   106  }
   107  
   108  // opregreg emits instructions for
   109  //     dest := dest(To) op src(From)
   110  // and also returns the created obj.Prog so it
   111  // may be further adjusted (offset, scale, etc).
   112  func opregreg(s *ssagen.State, op obj.As, dest, src int16) *obj.Prog {
   113  	p := s.Prog(op)
   114  	p.From.Type = obj.TYPE_REG
   115  	p.To.Type = obj.TYPE_REG
   116  	p.To.Reg = dest
   117  	p.From.Reg = src
   118  	return p
   119  }
   120  
   121  func ssaGenValue(s *ssagen.State, v *ssa.Value) {
   122  	switch v.Op {
   123  	case ssa.Op386ADDL:
   124  		r := v.Reg()
   125  		r1 := v.Args[0].Reg()
   126  		r2 := v.Args[1].Reg()
   127  		switch {
   128  		case r == r1:
   129  			p := s.Prog(v.Op.Asm())
   130  			p.From.Type = obj.TYPE_REG
   131  			p.From.Reg = r2
   132  			p.To.Type = obj.TYPE_REG
   133  			p.To.Reg = r
   134  		case r == r2:
   135  			p := s.Prog(v.Op.Asm())
   136  			p.From.Type = obj.TYPE_REG
   137  			p.From.Reg = r1
   138  			p.To.Type = obj.TYPE_REG
   139  			p.To.Reg = r
   140  		default:
   141  			p := s.Prog(x86.ALEAL)
   142  			p.From.Type = obj.TYPE_MEM
   143  			p.From.Reg = r1
   144  			p.From.Scale = 1
   145  			p.From.Index = r2
   146  			p.To.Type = obj.TYPE_REG
   147  			p.To.Reg = r
   148  		}
   149  
   150  	// 2-address opcode arithmetic
   151  	case ssa.Op386SUBL,
   152  		ssa.Op386MULL,
   153  		ssa.Op386ANDL,
   154  		ssa.Op386ORL,
   155  		ssa.Op386XORL,
   156  		ssa.Op386SHLL,
   157  		ssa.Op386SHRL, ssa.Op386SHRW, ssa.Op386SHRB,
   158  		ssa.Op386SARL, ssa.Op386SARW, ssa.Op386SARB,
   159  		ssa.Op386ADDSS, ssa.Op386ADDSD, ssa.Op386SUBSS, ssa.Op386SUBSD,
   160  		ssa.Op386MULSS, ssa.Op386MULSD, ssa.Op386DIVSS, ssa.Op386DIVSD,
   161  		ssa.Op386PXOR,
   162  		ssa.Op386ADCL,
   163  		ssa.Op386SBBL:
   164  		opregreg(s, v.Op.Asm(), v.Reg(), v.Args[1].Reg())
   165  
   166  	case ssa.Op386ADDLcarry, ssa.Op386SUBLcarry:
   167  		// output 0 is carry/borrow, output 1 is the low 32 bits.
   168  		opregreg(s, v.Op.Asm(), v.Reg0(), v.Args[1].Reg())
   169  
   170  	case ssa.Op386ADDLconstcarry, ssa.Op386SUBLconstcarry:
   171  		// output 0 is carry/borrow, output 1 is the low 32 bits.
   172  		p := s.Prog(v.Op.Asm())
   173  		p.From.Type = obj.TYPE_CONST
   174  		p.From.Offset = v.AuxInt
   175  		p.To.Type = obj.TYPE_REG
   176  		p.To.Reg = v.Reg0()
   177  
   178  	case ssa.Op386DIVL, ssa.Op386DIVW,
   179  		ssa.Op386DIVLU, ssa.Op386DIVWU,
   180  		ssa.Op386MODL, ssa.Op386MODW,
   181  		ssa.Op386MODLU, ssa.Op386MODWU:
   182  
   183  		// Arg[0] is already in AX as it's the only register we allow
   184  		// and AX is the only output
   185  		x := v.Args[1].Reg()
   186  
   187  		// CPU faults upon signed overflow, which occurs when most
   188  		// negative int is divided by -1.
   189  		var j *obj.Prog
   190  		if v.Op == ssa.Op386DIVL || v.Op == ssa.Op386DIVW ||
   191  			v.Op == ssa.Op386MODL || v.Op == ssa.Op386MODW {
   192  
   193  			if ssa.DivisionNeedsFixUp(v) {
   194  				var c *obj.Prog
   195  				switch v.Op {
   196  				case ssa.Op386DIVL, ssa.Op386MODL:
   197  					c = s.Prog(x86.ACMPL)
   198  					j = s.Prog(x86.AJEQ)
   199  
   200  				case ssa.Op386DIVW, ssa.Op386MODW:
   201  					c = s.Prog(x86.ACMPW)
   202  					j = s.Prog(x86.AJEQ)
   203  				}
   204  				c.From.Type = obj.TYPE_REG
   205  				c.From.Reg = x
   206  				c.To.Type = obj.TYPE_CONST
   207  				c.To.Offset = -1
   208  
   209  				j.To.Type = obj.TYPE_BRANCH
   210  			}
   211  			// sign extend the dividend
   212  			switch v.Op {
   213  			case ssa.Op386DIVL, ssa.Op386MODL:
   214  				s.Prog(x86.ACDQ)
   215  			case ssa.Op386DIVW, ssa.Op386MODW:
   216  				s.Prog(x86.ACWD)
   217  			}
   218  		}
   219  
   220  		// for unsigned ints, we sign extend by setting DX = 0
   221  		// signed ints were sign extended above
   222  		if v.Op == ssa.Op386DIVLU || v.Op == ssa.Op386MODLU ||
   223  			v.Op == ssa.Op386DIVWU || v.Op == ssa.Op386MODWU {
   224  			c := s.Prog(x86.AXORL)
   225  			c.From.Type = obj.TYPE_REG
   226  			c.From.Reg = x86.REG_DX
   227  			c.To.Type = obj.TYPE_REG
   228  			c.To.Reg = x86.REG_DX
   229  		}
   230  
   231  		p := s.Prog(v.Op.Asm())
   232  		p.From.Type = obj.TYPE_REG
   233  		p.From.Reg = x
   234  
   235  		// signed division, rest of the check for -1 case
   236  		if j != nil {
   237  			j2 := s.Prog(obj.AJMP)
   238  			j2.To.Type = obj.TYPE_BRANCH
   239  
   240  			var n *obj.Prog
   241  			if v.Op == ssa.Op386DIVL || v.Op == ssa.Op386DIVW {
   242  				// n * -1 = -n
   243  				n = s.Prog(x86.ANEGL)
   244  				n.To.Type = obj.TYPE_REG
   245  				n.To.Reg = x86.REG_AX
   246  			} else {
   247  				// n % -1 == 0
   248  				n = s.Prog(x86.AXORL)
   249  				n.From.Type = obj.TYPE_REG
   250  				n.From.Reg = x86.REG_DX
   251  				n.To.Type = obj.TYPE_REG
   252  				n.To.Reg = x86.REG_DX
   253  			}
   254  
   255  			j.To.SetTarget(n)
   256  			j2.To.SetTarget(s.Pc())
   257  		}
   258  
   259  	case ssa.Op386HMULL, ssa.Op386HMULLU:
   260  		// the frontend rewrites constant division by 8/16/32 bit integers into
   261  		// HMUL by a constant
   262  		// SSA rewrites generate the 64 bit versions
   263  
   264  		// Arg[0] is already in AX as it's the only register we allow
   265  		// and DX is the only output we care about (the high bits)
   266  		p := s.Prog(v.Op.Asm())
   267  		p.From.Type = obj.TYPE_REG
   268  		p.From.Reg = v.Args[1].Reg()
   269  
   270  		// IMULB puts the high portion in AH instead of DL,
   271  		// so move it to DL for consistency
   272  		if v.Type.Size() == 1 {
   273  			m := s.Prog(x86.AMOVB)
   274  			m.From.Type = obj.TYPE_REG
   275  			m.From.Reg = x86.REG_AH
   276  			m.To.Type = obj.TYPE_REG
   277  			m.To.Reg = x86.REG_DX
   278  		}
   279  
   280  	case ssa.Op386MULLU:
   281  		// Arg[0] is already in AX as it's the only register we allow
   282  		// results lo in AX
   283  		p := s.Prog(v.Op.Asm())
   284  		p.From.Type = obj.TYPE_REG
   285  		p.From.Reg = v.Args[1].Reg()
   286  
   287  	case ssa.Op386MULLQU:
   288  		// AX * args[1], high 32 bits in DX (result[0]), low 32 bits in AX (result[1]).
   289  		p := s.Prog(v.Op.Asm())
   290  		p.From.Type = obj.TYPE_REG
   291  		p.From.Reg = v.Args[1].Reg()
   292  
   293  	case ssa.Op386AVGLU:
   294  		// compute (x+y)/2 unsigned.
   295  		// Do a 32-bit add, the overflow goes into the carry.
   296  		// Shift right once and pull the carry back into the 31st bit.
   297  		p := s.Prog(x86.AADDL)
   298  		p.From.Type = obj.TYPE_REG
   299  		p.To.Type = obj.TYPE_REG
   300  		p.To.Reg = v.Reg()
   301  		p.From.Reg = v.Args[1].Reg()
   302  		p = s.Prog(x86.ARCRL)
   303  		p.From.Type = obj.TYPE_CONST
   304  		p.From.Offset = 1
   305  		p.To.Type = obj.TYPE_REG
   306  		p.To.Reg = v.Reg()
   307  
   308  	case ssa.Op386ADDLconst:
   309  		r := v.Reg()
   310  		a := v.Args[0].Reg()
   311  		if r == a {
   312  			if v.AuxInt == 1 {
   313  				p := s.Prog(x86.AINCL)
   314  				p.To.Type = obj.TYPE_REG
   315  				p.To.Reg = r
   316  				return
   317  			}
   318  			if v.AuxInt == -1 {
   319  				p := s.Prog(x86.ADECL)
   320  				p.To.Type = obj.TYPE_REG
   321  				p.To.Reg = r
   322  				return
   323  			}
   324  			p := s.Prog(v.Op.Asm())
   325  			p.From.Type = obj.TYPE_CONST
   326  			p.From.Offset = v.AuxInt
   327  			p.To.Type = obj.TYPE_REG
   328  			p.To.Reg = r
   329  			return
   330  		}
   331  		p := s.Prog(x86.ALEAL)
   332  		p.From.Type = obj.TYPE_MEM
   333  		p.From.Reg = a
   334  		p.From.Offset = v.AuxInt
   335  		p.To.Type = obj.TYPE_REG
   336  		p.To.Reg = r
   337  
   338  	case ssa.Op386MULLconst:
   339  		r := v.Reg()
   340  		p := s.Prog(v.Op.Asm())
   341  		p.From.Type = obj.TYPE_CONST
   342  		p.From.Offset = v.AuxInt
   343  		p.To.Type = obj.TYPE_REG
   344  		p.To.Reg = r
   345  		p.SetFrom3Reg(v.Args[0].Reg())
   346  
   347  	case ssa.Op386SUBLconst,
   348  		ssa.Op386ADCLconst,
   349  		ssa.Op386SBBLconst,
   350  		ssa.Op386ANDLconst,
   351  		ssa.Op386ORLconst,
   352  		ssa.Op386XORLconst,
   353  		ssa.Op386SHLLconst,
   354  		ssa.Op386SHRLconst, ssa.Op386SHRWconst, ssa.Op386SHRBconst,
   355  		ssa.Op386SARLconst, ssa.Op386SARWconst, ssa.Op386SARBconst,
   356  		ssa.Op386ROLLconst, ssa.Op386ROLWconst, ssa.Op386ROLBconst:
   357  		p := s.Prog(v.Op.Asm())
   358  		p.From.Type = obj.TYPE_CONST
   359  		p.From.Offset = v.AuxInt
   360  		p.To.Type = obj.TYPE_REG
   361  		p.To.Reg = v.Reg()
   362  	case ssa.Op386SBBLcarrymask:
   363  		r := v.Reg()
   364  		p := s.Prog(v.Op.Asm())
   365  		p.From.Type = obj.TYPE_REG
   366  		p.From.Reg = r
   367  		p.To.Type = obj.TYPE_REG
   368  		p.To.Reg = r
   369  	case ssa.Op386LEAL1, ssa.Op386LEAL2, ssa.Op386LEAL4, ssa.Op386LEAL8:
   370  		r := v.Args[0].Reg()
   371  		i := v.Args[1].Reg()
   372  		p := s.Prog(x86.ALEAL)
   373  		switch v.Op {
   374  		case ssa.Op386LEAL1:
   375  			p.From.Scale = 1
   376  			if i == x86.REG_SP {
   377  				r, i = i, r
   378  			}
   379  		case ssa.Op386LEAL2:
   380  			p.From.Scale = 2
   381  		case ssa.Op386LEAL4:
   382  			p.From.Scale = 4
   383  		case ssa.Op386LEAL8:
   384  			p.From.Scale = 8
   385  		}
   386  		p.From.Type = obj.TYPE_MEM
   387  		p.From.Reg = r
   388  		p.From.Index = i
   389  		ssagen.AddAux(&p.From, v)
   390  		p.To.Type = obj.TYPE_REG
   391  		p.To.Reg = v.Reg()
   392  	case ssa.Op386LEAL:
   393  		p := s.Prog(x86.ALEAL)
   394  		p.From.Type = obj.TYPE_MEM
   395  		p.From.Reg = v.Args[0].Reg()
   396  		ssagen.AddAux(&p.From, v)
   397  		p.To.Type = obj.TYPE_REG
   398  		p.To.Reg = v.Reg()
   399  	case ssa.Op386CMPL, ssa.Op386CMPW, ssa.Op386CMPB,
   400  		ssa.Op386TESTL, ssa.Op386TESTW, ssa.Op386TESTB:
   401  		opregreg(s, v.Op.Asm(), v.Args[1].Reg(), v.Args[0].Reg())
   402  	case ssa.Op386UCOMISS, ssa.Op386UCOMISD:
   403  		// Go assembler has swapped operands for UCOMISx relative to CMP,
   404  		// must account for that right here.
   405  		opregreg(s, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg())
   406  	case ssa.Op386CMPLconst, ssa.Op386CMPWconst, ssa.Op386CMPBconst:
   407  		p := s.Prog(v.Op.Asm())
   408  		p.From.Type = obj.TYPE_REG
   409  		p.From.Reg = v.Args[0].Reg()
   410  		p.To.Type = obj.TYPE_CONST
   411  		p.To.Offset = v.AuxInt
   412  	case ssa.Op386TESTLconst, ssa.Op386TESTWconst, ssa.Op386TESTBconst:
   413  		p := s.Prog(v.Op.Asm())
   414  		p.From.Type = obj.TYPE_CONST
   415  		p.From.Offset = v.AuxInt
   416  		p.To.Type = obj.TYPE_REG
   417  		p.To.Reg = v.Args[0].Reg()
   418  	case ssa.Op386CMPLload, ssa.Op386CMPWload, ssa.Op386CMPBload:
   419  		p := s.Prog(v.Op.Asm())
   420  		p.From.Type = obj.TYPE_MEM
   421  		p.From.Reg = v.Args[0].Reg()
   422  		ssagen.AddAux(&p.From, v)
   423  		p.To.Type = obj.TYPE_REG
   424  		p.To.Reg = v.Args[1].Reg()
   425  	case ssa.Op386CMPLconstload, ssa.Op386CMPWconstload, ssa.Op386CMPBconstload:
   426  		sc := v.AuxValAndOff()
   427  		p := s.Prog(v.Op.Asm())
   428  		p.From.Type = obj.TYPE_MEM
   429  		p.From.Reg = v.Args[0].Reg()
   430  		ssagen.AddAux2(&p.From, v, sc.Off64())
   431  		p.To.Type = obj.TYPE_CONST
   432  		p.To.Offset = sc.Val64()
   433  	case ssa.Op386MOVLconst:
   434  		x := v.Reg()
   435  
   436  		// If flags aren't live (indicated by v.Aux == nil),
   437  		// then we can rewrite MOV $0, AX into XOR AX, AX.
   438  		if v.AuxInt == 0 && v.Aux == nil {
   439  			p := s.Prog(x86.AXORL)
   440  			p.From.Type = obj.TYPE_REG
   441  			p.From.Reg = x
   442  			p.To.Type = obj.TYPE_REG
   443  			p.To.Reg = x
   444  			break
   445  		}
   446  
   447  		p := s.Prog(v.Op.Asm())
   448  		p.From.Type = obj.TYPE_CONST
   449  		p.From.Offset = v.AuxInt
   450  		p.To.Type = obj.TYPE_REG
   451  		p.To.Reg = x
   452  	case ssa.Op386MOVSSconst, ssa.Op386MOVSDconst:
   453  		x := v.Reg()
   454  		p := s.Prog(v.Op.Asm())
   455  		p.From.Type = obj.TYPE_FCONST
   456  		p.From.Val = math.Float64frombits(uint64(v.AuxInt))
   457  		p.To.Type = obj.TYPE_REG
   458  		p.To.Reg = x
   459  	case ssa.Op386MOVSSconst1, ssa.Op386MOVSDconst1:
   460  		p := s.Prog(x86.ALEAL)
   461  		p.From.Type = obj.TYPE_MEM
   462  		p.From.Name = obj.NAME_EXTERN
   463  		f := math.Float64frombits(uint64(v.AuxInt))
   464  		if v.Op == ssa.Op386MOVSDconst1 {
   465  			p.From.Sym = base.Ctxt.Float64Sym(f)
   466  		} else {
   467  			p.From.Sym = base.Ctxt.Float32Sym(float32(f))
   468  		}
   469  		p.To.Type = obj.TYPE_REG
   470  		p.To.Reg = v.Reg()
   471  	case ssa.Op386MOVSSconst2, ssa.Op386MOVSDconst2:
   472  		p := s.Prog(v.Op.Asm())
   473  		p.From.Type = obj.TYPE_MEM
   474  		p.From.Reg = v.Args[0].Reg()
   475  		p.To.Type = obj.TYPE_REG
   476  		p.To.Reg = v.Reg()
   477  
   478  	case ssa.Op386MOVSSload, ssa.Op386MOVSDload, ssa.Op386MOVLload, ssa.Op386MOVWload, ssa.Op386MOVBload, ssa.Op386MOVBLSXload, ssa.Op386MOVWLSXload:
   479  		p := s.Prog(v.Op.Asm())
   480  		p.From.Type = obj.TYPE_MEM
   481  		p.From.Reg = v.Args[0].Reg()
   482  		ssagen.AddAux(&p.From, v)
   483  		p.To.Type = obj.TYPE_REG
   484  		p.To.Reg = v.Reg()
   485  	case ssa.Op386MOVBloadidx1, ssa.Op386MOVWloadidx1, ssa.Op386MOVLloadidx1, ssa.Op386MOVSSloadidx1, ssa.Op386MOVSDloadidx1,
   486  		ssa.Op386MOVSDloadidx8, ssa.Op386MOVLloadidx4, ssa.Op386MOVSSloadidx4, ssa.Op386MOVWloadidx2:
   487  		r := v.Args[0].Reg()
   488  		i := v.Args[1].Reg()
   489  		p := s.Prog(v.Op.Asm())
   490  		p.From.Type = obj.TYPE_MEM
   491  		switch v.Op {
   492  		case ssa.Op386MOVBloadidx1, ssa.Op386MOVWloadidx1, ssa.Op386MOVLloadidx1, ssa.Op386MOVSSloadidx1, ssa.Op386MOVSDloadidx1:
   493  			if i == x86.REG_SP {
   494  				r, i = i, r
   495  			}
   496  			p.From.Scale = 1
   497  		case ssa.Op386MOVSDloadidx8:
   498  			p.From.Scale = 8
   499  		case ssa.Op386MOVLloadidx4, ssa.Op386MOVSSloadidx4:
   500  			p.From.Scale = 4
   501  		case ssa.Op386MOVWloadidx2:
   502  			p.From.Scale = 2
   503  		}
   504  		p.From.Reg = r
   505  		p.From.Index = i
   506  		ssagen.AddAux(&p.From, v)
   507  		p.To.Type = obj.TYPE_REG
   508  		p.To.Reg = v.Reg()
   509  	case ssa.Op386ADDLloadidx4, ssa.Op386SUBLloadidx4, ssa.Op386MULLloadidx4,
   510  		ssa.Op386ANDLloadidx4, ssa.Op386ORLloadidx4, ssa.Op386XORLloadidx4:
   511  		p := s.Prog(v.Op.Asm())
   512  		p.From.Type = obj.TYPE_MEM
   513  		p.From.Reg = v.Args[1].Reg()
   514  		p.From.Index = v.Args[2].Reg()
   515  		p.From.Scale = 4
   516  		ssagen.AddAux(&p.From, v)
   517  		p.To.Type = obj.TYPE_REG
   518  		p.To.Reg = v.Reg()
   519  	case ssa.Op386ADDLload, ssa.Op386SUBLload, ssa.Op386MULLload,
   520  		ssa.Op386ANDLload, ssa.Op386ORLload, ssa.Op386XORLload,
   521  		ssa.Op386ADDSDload, ssa.Op386ADDSSload, ssa.Op386SUBSDload, ssa.Op386SUBSSload,
   522  		ssa.Op386MULSDload, ssa.Op386MULSSload, ssa.Op386DIVSSload, ssa.Op386DIVSDload:
   523  		p := s.Prog(v.Op.Asm())
   524  		p.From.Type = obj.TYPE_MEM
   525  		p.From.Reg = v.Args[1].Reg()
   526  		ssagen.AddAux(&p.From, v)
   527  		p.To.Type = obj.TYPE_REG
   528  		p.To.Reg = v.Reg()
   529  	case ssa.Op386MOVSSstore, ssa.Op386MOVSDstore, ssa.Op386MOVLstore, ssa.Op386MOVWstore, ssa.Op386MOVBstore,
   530  		ssa.Op386ADDLmodify, ssa.Op386SUBLmodify, ssa.Op386ANDLmodify, ssa.Op386ORLmodify, ssa.Op386XORLmodify:
   531  		p := s.Prog(v.Op.Asm())
   532  		p.From.Type = obj.TYPE_REG
   533  		p.From.Reg = v.Args[1].Reg()
   534  		p.To.Type = obj.TYPE_MEM
   535  		p.To.Reg = v.Args[0].Reg()
   536  		ssagen.AddAux(&p.To, v)
   537  	case ssa.Op386ADDLconstmodify:
   538  		sc := v.AuxValAndOff()
   539  		val := sc.Val()
   540  		if val == 1 || val == -1 {
   541  			var p *obj.Prog
   542  			if val == 1 {
   543  				p = s.Prog(x86.AINCL)
   544  			} else {
   545  				p = s.Prog(x86.ADECL)
   546  			}
   547  			off := sc.Off64()
   548  			p.To.Type = obj.TYPE_MEM
   549  			p.To.Reg = v.Args[0].Reg()
   550  			ssagen.AddAux2(&p.To, v, off)
   551  			break
   552  		}
   553  		fallthrough
   554  	case ssa.Op386ANDLconstmodify, ssa.Op386ORLconstmodify, ssa.Op386XORLconstmodify:
   555  		sc := v.AuxValAndOff()
   556  		off := sc.Off64()
   557  		val := sc.Val64()
   558  		p := s.Prog(v.Op.Asm())
   559  		p.From.Type = obj.TYPE_CONST
   560  		p.From.Offset = val
   561  		p.To.Type = obj.TYPE_MEM
   562  		p.To.Reg = v.Args[0].Reg()
   563  		ssagen.AddAux2(&p.To, v, off)
   564  	case ssa.Op386MOVBstoreidx1, ssa.Op386MOVWstoreidx1, ssa.Op386MOVLstoreidx1, ssa.Op386MOVSSstoreidx1, ssa.Op386MOVSDstoreidx1,
   565  		ssa.Op386MOVSDstoreidx8, ssa.Op386MOVSSstoreidx4, ssa.Op386MOVLstoreidx4, ssa.Op386MOVWstoreidx2,
   566  		ssa.Op386ADDLmodifyidx4, ssa.Op386SUBLmodifyidx4, ssa.Op386ANDLmodifyidx4, ssa.Op386ORLmodifyidx4, ssa.Op386XORLmodifyidx4:
   567  		r := v.Args[0].Reg()
   568  		i := v.Args[1].Reg()
   569  		p := s.Prog(v.Op.Asm())
   570  		p.From.Type = obj.TYPE_REG
   571  		p.From.Reg = v.Args[2].Reg()
   572  		p.To.Type = obj.TYPE_MEM
   573  		switch v.Op {
   574  		case ssa.Op386MOVBstoreidx1, ssa.Op386MOVWstoreidx1, ssa.Op386MOVLstoreidx1, ssa.Op386MOVSSstoreidx1, ssa.Op386MOVSDstoreidx1:
   575  			if i == x86.REG_SP {
   576  				r, i = i, r
   577  			}
   578  			p.To.Scale = 1
   579  		case ssa.Op386MOVSDstoreidx8:
   580  			p.To.Scale = 8
   581  		case ssa.Op386MOVSSstoreidx4, ssa.Op386MOVLstoreidx4,
   582  			ssa.Op386ADDLmodifyidx4, ssa.Op386SUBLmodifyidx4, ssa.Op386ANDLmodifyidx4, ssa.Op386ORLmodifyidx4, ssa.Op386XORLmodifyidx4:
   583  			p.To.Scale = 4
   584  		case ssa.Op386MOVWstoreidx2:
   585  			p.To.Scale = 2
   586  		}
   587  		p.To.Reg = r
   588  		p.To.Index = i
   589  		ssagen.AddAux(&p.To, v)
   590  	case ssa.Op386MOVLstoreconst, ssa.Op386MOVWstoreconst, ssa.Op386MOVBstoreconst:
   591  		p := s.Prog(v.Op.Asm())
   592  		p.From.Type = obj.TYPE_CONST
   593  		sc := v.AuxValAndOff()
   594  		p.From.Offset = sc.Val64()
   595  		p.To.Type = obj.TYPE_MEM
   596  		p.To.Reg = v.Args[0].Reg()
   597  		ssagen.AddAux2(&p.To, v, sc.Off64())
   598  	case ssa.Op386ADDLconstmodifyidx4:
   599  		sc := v.AuxValAndOff()
   600  		val := sc.Val()
   601  		if val == 1 || val == -1 {
   602  			var p *obj.Prog
   603  			if val == 1 {
   604  				p = s.Prog(x86.AINCL)
   605  			} else {
   606  				p = s.Prog(x86.ADECL)
   607  			}
   608  			off := sc.Off64()
   609  			p.To.Type = obj.TYPE_MEM
   610  			p.To.Reg = v.Args[0].Reg()
   611  			p.To.Scale = 4
   612  			p.To.Index = v.Args[1].Reg()
   613  			ssagen.AddAux2(&p.To, v, off)
   614  			break
   615  		}
   616  		fallthrough
   617  	case ssa.Op386MOVLstoreconstidx1, ssa.Op386MOVLstoreconstidx4, ssa.Op386MOVWstoreconstidx1, ssa.Op386MOVWstoreconstidx2, ssa.Op386MOVBstoreconstidx1,
   618  		ssa.Op386ANDLconstmodifyidx4, ssa.Op386ORLconstmodifyidx4, ssa.Op386XORLconstmodifyidx4:
   619  		p := s.Prog(v.Op.Asm())
   620  		p.From.Type = obj.TYPE_CONST
   621  		sc := v.AuxValAndOff()
   622  		p.From.Offset = sc.Val64()
   623  		r := v.Args[0].Reg()
   624  		i := v.Args[1].Reg()
   625  		switch v.Op {
   626  		case ssa.Op386MOVBstoreconstidx1, ssa.Op386MOVWstoreconstidx1, ssa.Op386MOVLstoreconstidx1:
   627  			p.To.Scale = 1
   628  			if i == x86.REG_SP {
   629  				r, i = i, r
   630  			}
   631  		case ssa.Op386MOVWstoreconstidx2:
   632  			p.To.Scale = 2
   633  		case ssa.Op386MOVLstoreconstidx4,
   634  			ssa.Op386ADDLconstmodifyidx4, ssa.Op386ANDLconstmodifyidx4, ssa.Op386ORLconstmodifyidx4, ssa.Op386XORLconstmodifyidx4:
   635  			p.To.Scale = 4
   636  		}
   637  		p.To.Type = obj.TYPE_MEM
   638  		p.To.Reg = r
   639  		p.To.Index = i
   640  		ssagen.AddAux2(&p.To, v, sc.Off64())
   641  	case ssa.Op386MOVWLSX, ssa.Op386MOVBLSX, ssa.Op386MOVWLZX, ssa.Op386MOVBLZX,
   642  		ssa.Op386CVTSL2SS, ssa.Op386CVTSL2SD,
   643  		ssa.Op386CVTTSS2SL, ssa.Op386CVTTSD2SL,
   644  		ssa.Op386CVTSS2SD, ssa.Op386CVTSD2SS:
   645  		opregreg(s, v.Op.Asm(), v.Reg(), v.Args[0].Reg())
   646  	case ssa.Op386DUFFZERO:
   647  		p := s.Prog(obj.ADUFFZERO)
   648  		p.To.Type = obj.TYPE_ADDR
   649  		p.To.Sym = ir.Syms.Duffzero
   650  		p.To.Offset = v.AuxInt
   651  	case ssa.Op386DUFFCOPY:
   652  		p := s.Prog(obj.ADUFFCOPY)
   653  		p.To.Type = obj.TYPE_ADDR
   654  		p.To.Sym = ir.Syms.Duffcopy
   655  		p.To.Offset = v.AuxInt
   656  
   657  	case ssa.OpCopy: // TODO: use MOVLreg for reg->reg copies instead of OpCopy?
   658  		if v.Type.IsMemory() {
   659  			return
   660  		}
   661  		x := v.Args[0].Reg()
   662  		y := v.Reg()
   663  		if x != y {
   664  			opregreg(s, moveByType(v.Type), y, x)
   665  		}
   666  	case ssa.OpLoadReg:
   667  		if v.Type.IsFlags() {
   668  			v.Fatalf("load flags not implemented: %v", v.LongString())
   669  			return
   670  		}
   671  		p := s.Prog(loadByType(v.Type))
   672  		ssagen.AddrAuto(&p.From, v.Args[0])
   673  		p.To.Type = obj.TYPE_REG
   674  		p.To.Reg = v.Reg()
   675  
   676  	case ssa.OpStoreReg:
   677  		if v.Type.IsFlags() {
   678  			v.Fatalf("store flags not implemented: %v", v.LongString())
   679  			return
   680  		}
   681  		p := s.Prog(storeByType(v.Type))
   682  		p.From.Type = obj.TYPE_REG
   683  		p.From.Reg = v.Args[0].Reg()
   684  		ssagen.AddrAuto(&p.To, v)
   685  	case ssa.Op386LoweredGetClosurePtr:
   686  		// Closure pointer is DX.
   687  		ssagen.CheckLoweredGetClosurePtr(v)
   688  	case ssa.Op386LoweredGetG:
   689  		r := v.Reg()
   690  		// See the comments in cmd/internal/obj/x86/obj6.go
   691  		// near CanUse1InsnTLS for a detailed explanation of these instructions.
   692  		if x86.CanUse1InsnTLS(base.Ctxt) {
   693  			// MOVL (TLS), r
   694  			p := s.Prog(x86.AMOVL)
   695  			p.From.Type = obj.TYPE_MEM
   696  			p.From.Reg = x86.REG_TLS
   697  			p.To.Type = obj.TYPE_REG
   698  			p.To.Reg = r
   699  		} else {
   700  			// MOVL TLS, r
   701  			// MOVL (r)(TLS*1), r
   702  			p := s.Prog(x86.AMOVL)
   703  			p.From.Type = obj.TYPE_REG
   704  			p.From.Reg = x86.REG_TLS
   705  			p.To.Type = obj.TYPE_REG
   706  			p.To.Reg = r
   707  			q := s.Prog(x86.AMOVL)
   708  			q.From.Type = obj.TYPE_MEM
   709  			q.From.Reg = r
   710  			q.From.Index = x86.REG_TLS
   711  			q.From.Scale = 1
   712  			q.To.Type = obj.TYPE_REG
   713  			q.To.Reg = r
   714  		}
   715  
   716  	case ssa.Op386LoweredGetCallerPC:
   717  		p := s.Prog(x86.AMOVL)
   718  		p.From.Type = obj.TYPE_MEM
   719  		p.From.Offset = -4 // PC is stored 4 bytes below first parameter.
   720  		p.From.Name = obj.NAME_PARAM
   721  		p.To.Type = obj.TYPE_REG
   722  		p.To.Reg = v.Reg()
   723  
   724  	case ssa.Op386LoweredGetCallerSP:
   725  		// caller's SP is the address of the first arg
   726  		p := s.Prog(x86.AMOVL)
   727  		p.From.Type = obj.TYPE_ADDR
   728  		p.From.Offset = -base.Ctxt.FixedFrameSize() // 0 on 386, just to be consistent with other architectures
   729  		p.From.Name = obj.NAME_PARAM
   730  		p.To.Type = obj.TYPE_REG
   731  		p.To.Reg = v.Reg()
   732  
   733  	case ssa.Op386LoweredWB:
   734  		p := s.Prog(obj.ACALL)
   735  		p.To.Type = obj.TYPE_MEM
   736  		p.To.Name = obj.NAME_EXTERN
   737  		p.To.Sym = v.Aux.(*obj.LSym)
   738  
   739  	case ssa.Op386LoweredPanicBoundsA, ssa.Op386LoweredPanicBoundsB, ssa.Op386LoweredPanicBoundsC:
   740  		p := s.Prog(obj.ACALL)
   741  		p.To.Type = obj.TYPE_MEM
   742  		p.To.Name = obj.NAME_EXTERN
   743  		p.To.Sym = ssagen.BoundsCheckFunc[v.AuxInt]
   744  		s.UseArgs(8) // space used in callee args area by assembly stubs
   745  
   746  	case ssa.Op386LoweredPanicExtendA, ssa.Op386LoweredPanicExtendB, ssa.Op386LoweredPanicExtendC:
   747  		p := s.Prog(obj.ACALL)
   748  		p.To.Type = obj.TYPE_MEM
   749  		p.To.Name = obj.NAME_EXTERN
   750  		p.To.Sym = ssagen.ExtendCheckFunc[v.AuxInt]
   751  		s.UseArgs(12) // space used in callee args area by assembly stubs
   752  
   753  	case ssa.Op386CALLstatic, ssa.Op386CALLclosure, ssa.Op386CALLinter:
   754  		s.Call(v)
   755  	case ssa.Op386CALLtail:
   756  		s.TailCall(v)
   757  	case ssa.Op386NEGL,
   758  		ssa.Op386BSWAPL,
   759  		ssa.Op386NOTL:
   760  		p := s.Prog(v.Op.Asm())
   761  		p.To.Type = obj.TYPE_REG
   762  		p.To.Reg = v.Reg()
   763  	case ssa.Op386BSFL, ssa.Op386BSFW,
   764  		ssa.Op386BSRL, ssa.Op386BSRW,
   765  		ssa.Op386SQRTSS, ssa.Op386SQRTSD:
   766  		p := s.Prog(v.Op.Asm())
   767  		p.From.Type = obj.TYPE_REG
   768  		p.From.Reg = v.Args[0].Reg()
   769  		p.To.Type = obj.TYPE_REG
   770  		p.To.Reg = v.Reg()
   771  	case ssa.Op386SETEQ, ssa.Op386SETNE,
   772  		ssa.Op386SETL, ssa.Op386SETLE,
   773  		ssa.Op386SETG, ssa.Op386SETGE,
   774  		ssa.Op386SETGF, ssa.Op386SETGEF,
   775  		ssa.Op386SETB, ssa.Op386SETBE,
   776  		ssa.Op386SETORD, ssa.Op386SETNAN,
   777  		ssa.Op386SETA, ssa.Op386SETAE,
   778  		ssa.Op386SETO:
   779  		p := s.Prog(v.Op.Asm())
   780  		p.To.Type = obj.TYPE_REG
   781  		p.To.Reg = v.Reg()
   782  
   783  	case ssa.Op386SETNEF:
   784  		p := s.Prog(v.Op.Asm())
   785  		p.To.Type = obj.TYPE_REG
   786  		p.To.Reg = v.Reg()
   787  		q := s.Prog(x86.ASETPS)
   788  		q.To.Type = obj.TYPE_REG
   789  		q.To.Reg = x86.REG_AX
   790  		opregreg(s, x86.AORL, v.Reg(), x86.REG_AX)
   791  
   792  	case ssa.Op386SETEQF:
   793  		p := s.Prog(v.Op.Asm())
   794  		p.To.Type = obj.TYPE_REG
   795  		p.To.Reg = v.Reg()
   796  		q := s.Prog(x86.ASETPC)
   797  		q.To.Type = obj.TYPE_REG
   798  		q.To.Reg = x86.REG_AX
   799  		opregreg(s, x86.AANDL, v.Reg(), x86.REG_AX)
   800  
   801  	case ssa.Op386InvertFlags:
   802  		v.Fatalf("InvertFlags should never make it to codegen %v", v.LongString())
   803  	case ssa.Op386FlagEQ, ssa.Op386FlagLT_ULT, ssa.Op386FlagLT_UGT, ssa.Op386FlagGT_ULT, ssa.Op386FlagGT_UGT:
   804  		v.Fatalf("Flag* ops should never make it to codegen %v", v.LongString())
   805  	case ssa.Op386REPSTOSL:
   806  		s.Prog(x86.AREP)
   807  		s.Prog(x86.ASTOSL)
   808  	case ssa.Op386REPMOVSL:
   809  		s.Prog(x86.AREP)
   810  		s.Prog(x86.AMOVSL)
   811  	case ssa.Op386LoweredNilCheck:
   812  		// Issue a load which will fault if the input is nil.
   813  		// TODO: We currently use the 2-byte instruction TESTB AX, (reg).
   814  		// Should we use the 3-byte TESTB $0, (reg) instead? It is larger
   815  		// but it doesn't have false dependency on AX.
   816  		// Or maybe allocate an output register and use MOVL (reg),reg2 ?
   817  		// That trades clobbering flags for clobbering a register.
   818  		p := s.Prog(x86.ATESTB)
   819  		p.From.Type = obj.TYPE_REG
   820  		p.From.Reg = x86.REG_AX
   821  		p.To.Type = obj.TYPE_MEM
   822  		p.To.Reg = v.Args[0].Reg()
   823  		ssagen.AddAux(&p.To, v)
   824  		if logopt.Enabled() {
   825  			logopt.LogOpt(v.Pos, "nilcheck", "genssa", v.Block.Func.Name)
   826  		}
   827  		if base.Debug.Nil != 0 && v.Pos.Line() > 1 { // v.Pos.Line()==1 in generated wrappers
   828  			base.WarnfAt(v.Pos, "generated nil check")
   829  		}
   830  	case ssa.OpClobber:
   831  		p := s.Prog(x86.AMOVL)
   832  		p.From.Type = obj.TYPE_CONST
   833  		p.From.Offset = 0xdeaddead
   834  		p.To.Type = obj.TYPE_MEM
   835  		p.To.Reg = x86.REG_SP
   836  		ssagen.AddAux(&p.To, v)
   837  	case ssa.OpClobberReg:
   838  		// TODO: implement for clobberdead experiment. Nop is ok for now.
   839  	default:
   840  		v.Fatalf("genValue not implemented: %s", v.LongString())
   841  	}
   842  }
   843  
   844  var blockJump = [...]struct {
   845  	asm, invasm obj.As
   846  }{
   847  	ssa.Block386EQ:  {x86.AJEQ, x86.AJNE},
   848  	ssa.Block386NE:  {x86.AJNE, x86.AJEQ},
   849  	ssa.Block386LT:  {x86.AJLT, x86.AJGE},
   850  	ssa.Block386GE:  {x86.AJGE, x86.AJLT},
   851  	ssa.Block386LE:  {x86.AJLE, x86.AJGT},
   852  	ssa.Block386GT:  {x86.AJGT, x86.AJLE},
   853  	ssa.Block386OS:  {x86.AJOS, x86.AJOC},
   854  	ssa.Block386OC:  {x86.AJOC, x86.AJOS},
   855  	ssa.Block386ULT: {x86.AJCS, x86.AJCC},
   856  	ssa.Block386UGE: {x86.AJCC, x86.AJCS},
   857  	ssa.Block386UGT: {x86.AJHI, x86.AJLS},
   858  	ssa.Block386ULE: {x86.AJLS, x86.AJHI},
   859  	ssa.Block386ORD: {x86.AJPC, x86.AJPS},
   860  	ssa.Block386NAN: {x86.AJPS, x86.AJPC},
   861  }
   862  
   863  var eqfJumps = [2][2]ssagen.IndexJump{
   864  	{{Jump: x86.AJNE, Index: 1}, {Jump: x86.AJPS, Index: 1}}, // next == b.Succs[0]
   865  	{{Jump: x86.AJNE, Index: 1}, {Jump: x86.AJPC, Index: 0}}, // next == b.Succs[1]
   866  }
   867  var nefJumps = [2][2]ssagen.IndexJump{
   868  	{{Jump: x86.AJNE, Index: 0}, {Jump: x86.AJPC, Index: 1}}, // next == b.Succs[0]
   869  	{{Jump: x86.AJNE, Index: 0}, {Jump: x86.AJPS, Index: 0}}, // next == b.Succs[1]
   870  }
   871  
   872  func ssaGenBlock(s *ssagen.State, b, next *ssa.Block) {
   873  	switch b.Kind {
   874  	case ssa.BlockPlain:
   875  		if b.Succs[0].Block() != next {
   876  			p := s.Prog(obj.AJMP)
   877  			p.To.Type = obj.TYPE_BRANCH
   878  			s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[0].Block()})
   879  		}
   880  	case ssa.BlockDefer:
   881  		// defer returns in rax:
   882  		// 0 if we should continue executing
   883  		// 1 if we should jump to deferreturn call
   884  		p := s.Prog(x86.ATESTL)
   885  		p.From.Type = obj.TYPE_REG
   886  		p.From.Reg = x86.REG_AX
   887  		p.To.Type = obj.TYPE_REG
   888  		p.To.Reg = x86.REG_AX
   889  		p = s.Prog(x86.AJNE)
   890  		p.To.Type = obj.TYPE_BRANCH
   891  		s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[1].Block()})
   892  		if b.Succs[0].Block() != next {
   893  			p := s.Prog(obj.AJMP)
   894  			p.To.Type = obj.TYPE_BRANCH
   895  			s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[0].Block()})
   896  		}
   897  	case ssa.BlockExit, ssa.BlockRetJmp:
   898  	case ssa.BlockRet:
   899  		s.Prog(obj.ARET)
   900  
   901  	case ssa.Block386EQF:
   902  		s.CombJump(b, next, &eqfJumps)
   903  
   904  	case ssa.Block386NEF:
   905  		s.CombJump(b, next, &nefJumps)
   906  
   907  	case ssa.Block386EQ, ssa.Block386NE,
   908  		ssa.Block386LT, ssa.Block386GE,
   909  		ssa.Block386LE, ssa.Block386GT,
   910  		ssa.Block386OS, ssa.Block386OC,
   911  		ssa.Block386ULT, ssa.Block386UGT,
   912  		ssa.Block386ULE, ssa.Block386UGE:
   913  		jmp := blockJump[b.Kind]
   914  		switch next {
   915  		case b.Succs[0].Block():
   916  			s.Br(jmp.invasm, b.Succs[1].Block())
   917  		case b.Succs[1].Block():
   918  			s.Br(jmp.asm, b.Succs[0].Block())
   919  		default:
   920  			if b.Likely != ssa.BranchUnlikely {
   921  				s.Br(jmp.asm, b.Succs[0].Block())
   922  				s.Br(obj.AJMP, b.Succs[1].Block())
   923  			} else {
   924  				s.Br(jmp.invasm, b.Succs[1].Block())
   925  				s.Br(obj.AJMP, b.Succs[0].Block())
   926  			}
   927  		}
   928  	default:
   929  		b.Fatalf("branch not implemented: %s", b.LongString())
   930  	}
   931  }
   932  

View as plain text