github.com/sanprasirt/go@v0.0.0-20170607001320-a027466e4b6d/src/cmd/compile/internal/x86/387.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 x86
     6  
     7  import (
     8  	"cmd/compile/internal/gc"
     9  	"cmd/compile/internal/ssa"
    10  	"cmd/compile/internal/types"
    11  	"cmd/internal/obj"
    12  	"cmd/internal/obj/x86"
    13  	"math"
    14  )
    15  
    16  // Generates code for v using 387 instructions.
    17  func ssaGenValue387(s *gc.SSAGenState, v *ssa.Value) {
    18  	// The SSA compiler pretends that it has an SSE backend.
    19  	// If we don't have one of those, we need to translate
    20  	// all the SSE ops to equivalent 387 ops. That's what this
    21  	// function does.
    22  
    23  	switch v.Op {
    24  	case ssa.Op386MOVSSconst, ssa.Op386MOVSDconst:
    25  		p := s.Prog(loadPush(v.Type))
    26  		p.From.Type = obj.TYPE_FCONST
    27  		p.From.Val = math.Float64frombits(uint64(v.AuxInt))
    28  		p.To.Type = obj.TYPE_REG
    29  		p.To.Reg = x86.REG_F0
    30  		popAndSave(s, v)
    31  
    32  	case ssa.Op386MOVSSconst2, ssa.Op386MOVSDconst2:
    33  		p := s.Prog(loadPush(v.Type))
    34  		p.From.Type = obj.TYPE_MEM
    35  		p.From.Reg = v.Args[0].Reg()
    36  		p.To.Type = obj.TYPE_REG
    37  		p.To.Reg = x86.REG_F0
    38  		popAndSave(s, v)
    39  
    40  	case ssa.Op386MOVSSload, ssa.Op386MOVSDload, ssa.Op386MOVSSloadidx1, ssa.Op386MOVSDloadidx1, ssa.Op386MOVSSloadidx4, ssa.Op386MOVSDloadidx8:
    41  		p := s.Prog(loadPush(v.Type))
    42  		p.From.Type = obj.TYPE_MEM
    43  		p.From.Reg = v.Args[0].Reg()
    44  		gc.AddAux(&p.From, v)
    45  		switch v.Op {
    46  		case ssa.Op386MOVSSloadidx1, ssa.Op386MOVSDloadidx1:
    47  			p.From.Scale = 1
    48  			p.From.Index = v.Args[1].Reg()
    49  		case ssa.Op386MOVSSloadidx4:
    50  			p.From.Scale = 4
    51  			p.From.Index = v.Args[1].Reg()
    52  		case ssa.Op386MOVSDloadidx8:
    53  			p.From.Scale = 8
    54  			p.From.Index = v.Args[1].Reg()
    55  		}
    56  		p.To.Type = obj.TYPE_REG
    57  		p.To.Reg = x86.REG_F0
    58  		popAndSave(s, v)
    59  
    60  	case ssa.Op386MOVSSstore, ssa.Op386MOVSDstore:
    61  		// Push to-be-stored value on top of stack.
    62  		push(s, v.Args[1])
    63  
    64  		// Pop and store value.
    65  		var op obj.As
    66  		switch v.Op {
    67  		case ssa.Op386MOVSSstore:
    68  			op = x86.AFMOVFP
    69  		case ssa.Op386MOVSDstore:
    70  			op = x86.AFMOVDP
    71  		}
    72  		p := s.Prog(op)
    73  		p.From.Type = obj.TYPE_REG
    74  		p.From.Reg = x86.REG_F0
    75  		p.To.Type = obj.TYPE_MEM
    76  		p.To.Reg = v.Args[0].Reg()
    77  		gc.AddAux(&p.To, v)
    78  
    79  	case ssa.Op386MOVSSstoreidx1, ssa.Op386MOVSDstoreidx1, ssa.Op386MOVSSstoreidx4, ssa.Op386MOVSDstoreidx8:
    80  		push(s, v.Args[2])
    81  		var op obj.As
    82  		switch v.Op {
    83  		case ssa.Op386MOVSSstoreidx1, ssa.Op386MOVSSstoreidx4:
    84  			op = x86.AFMOVFP
    85  		case ssa.Op386MOVSDstoreidx1, ssa.Op386MOVSDstoreidx8:
    86  			op = x86.AFMOVDP
    87  		}
    88  		p := s.Prog(op)
    89  		p.From.Type = obj.TYPE_REG
    90  		p.From.Reg = x86.REG_F0
    91  		p.To.Type = obj.TYPE_MEM
    92  		p.To.Reg = v.Args[0].Reg()
    93  		gc.AddAux(&p.To, v)
    94  		switch v.Op {
    95  		case ssa.Op386MOVSSstoreidx1, ssa.Op386MOVSDstoreidx1:
    96  			p.To.Scale = 1
    97  			p.To.Index = v.Args[1].Reg()
    98  		case ssa.Op386MOVSSstoreidx4:
    99  			p.To.Scale = 4
   100  			p.To.Index = v.Args[1].Reg()
   101  		case ssa.Op386MOVSDstoreidx8:
   102  			p.To.Scale = 8
   103  			p.To.Index = v.Args[1].Reg()
   104  		}
   105  
   106  	case ssa.Op386ADDSS, ssa.Op386ADDSD, ssa.Op386SUBSS, ssa.Op386SUBSD,
   107  		ssa.Op386MULSS, ssa.Op386MULSD, ssa.Op386DIVSS, ssa.Op386DIVSD:
   108  		if v.Reg() != v.Args[0].Reg() {
   109  			v.Fatalf("input[0] and output not in same register %s", v.LongString())
   110  		}
   111  
   112  		// Push arg1 on top of stack
   113  		push(s, v.Args[1])
   114  
   115  		// Set precision if needed.  64 bits is the default.
   116  		switch v.Op {
   117  		case ssa.Op386ADDSS, ssa.Op386SUBSS, ssa.Op386MULSS, ssa.Op386DIVSS:
   118  			p := s.Prog(x86.AFSTCW)
   119  			s.AddrScratch(&p.To)
   120  			p = s.Prog(x86.AFLDCW)
   121  			p.From.Type = obj.TYPE_MEM
   122  			p.From.Name = obj.NAME_EXTERN
   123  			p.From.Sym = gc.Sysfunc("controlWord32")
   124  		}
   125  
   126  		var op obj.As
   127  		switch v.Op {
   128  		case ssa.Op386ADDSS, ssa.Op386ADDSD:
   129  			op = x86.AFADDDP
   130  		case ssa.Op386SUBSS, ssa.Op386SUBSD:
   131  			op = x86.AFSUBDP
   132  		case ssa.Op386MULSS, ssa.Op386MULSD:
   133  			op = x86.AFMULDP
   134  		case ssa.Op386DIVSS, ssa.Op386DIVSD:
   135  			op = x86.AFDIVDP
   136  		}
   137  		p := s.Prog(op)
   138  		p.From.Type = obj.TYPE_REG
   139  		p.From.Reg = x86.REG_F0
   140  		p.To.Type = obj.TYPE_REG
   141  		p.To.Reg = s.SSEto387[v.Reg()] + 1
   142  
   143  		// Restore precision if needed.
   144  		switch v.Op {
   145  		case ssa.Op386ADDSS, ssa.Op386SUBSS, ssa.Op386MULSS, ssa.Op386DIVSS:
   146  			p := s.Prog(x86.AFLDCW)
   147  			s.AddrScratch(&p.From)
   148  		}
   149  
   150  	case ssa.Op386UCOMISS, ssa.Op386UCOMISD:
   151  		push(s, v.Args[0])
   152  
   153  		// Compare.
   154  		p := s.Prog(x86.AFUCOMP)
   155  		p.From.Type = obj.TYPE_REG
   156  		p.From.Reg = x86.REG_F0
   157  		p.To.Type = obj.TYPE_REG
   158  		p.To.Reg = s.SSEto387[v.Args[1].Reg()] + 1
   159  
   160  		// Save AX.
   161  		p = s.Prog(x86.AMOVL)
   162  		p.From.Type = obj.TYPE_REG
   163  		p.From.Reg = x86.REG_AX
   164  		s.AddrScratch(&p.To)
   165  
   166  		// Move status word into AX.
   167  		p = s.Prog(x86.AFSTSW)
   168  		p.To.Type = obj.TYPE_REG
   169  		p.To.Reg = x86.REG_AX
   170  
   171  		// Then move the flags we need to the integer flags.
   172  		s.Prog(x86.ASAHF)
   173  
   174  		// Restore AX.
   175  		p = s.Prog(x86.AMOVL)
   176  		s.AddrScratch(&p.From)
   177  		p.To.Type = obj.TYPE_REG
   178  		p.To.Reg = x86.REG_AX
   179  
   180  	case ssa.Op386SQRTSD:
   181  		push(s, v.Args[0])
   182  		s.Prog(x86.AFSQRT)
   183  		popAndSave(s, v)
   184  
   185  	case ssa.Op386FCHS:
   186  		push(s, v.Args[0])
   187  		s.Prog(x86.AFCHS)
   188  		popAndSave(s, v)
   189  
   190  	case ssa.Op386CVTSL2SS, ssa.Op386CVTSL2SD:
   191  		p := s.Prog(x86.AMOVL)
   192  		p.From.Type = obj.TYPE_REG
   193  		p.From.Reg = v.Args[0].Reg()
   194  		s.AddrScratch(&p.To)
   195  		p = s.Prog(x86.AFMOVL)
   196  		s.AddrScratch(&p.From)
   197  		p.To.Type = obj.TYPE_REG
   198  		p.To.Reg = x86.REG_F0
   199  		popAndSave(s, v)
   200  
   201  	case ssa.Op386CVTTSD2SL, ssa.Op386CVTTSS2SL:
   202  		push(s, v.Args[0])
   203  
   204  		// Save control word.
   205  		p := s.Prog(x86.AFSTCW)
   206  		s.AddrScratch(&p.To)
   207  		p.To.Offset += 4
   208  
   209  		// Load control word which truncates (rounds towards zero).
   210  		p = s.Prog(x86.AFLDCW)
   211  		p.From.Type = obj.TYPE_MEM
   212  		p.From.Name = obj.NAME_EXTERN
   213  		p.From.Sym = gc.Sysfunc("controlWord64trunc")
   214  
   215  		// Now do the conversion.
   216  		p = s.Prog(x86.AFMOVLP)
   217  		p.From.Type = obj.TYPE_REG
   218  		p.From.Reg = x86.REG_F0
   219  		s.AddrScratch(&p.To)
   220  		p = s.Prog(x86.AMOVL)
   221  		s.AddrScratch(&p.From)
   222  		p.To.Type = obj.TYPE_REG
   223  		p.To.Reg = v.Reg()
   224  
   225  		// Restore control word.
   226  		p = s.Prog(x86.AFLDCW)
   227  		s.AddrScratch(&p.From)
   228  		p.From.Offset += 4
   229  
   230  	case ssa.Op386CVTSS2SD:
   231  		// float32 -> float64 is a nop
   232  		push(s, v.Args[0])
   233  		popAndSave(s, v)
   234  
   235  	case ssa.Op386CVTSD2SS:
   236  		// Round to nearest float32.
   237  		push(s, v.Args[0])
   238  		p := s.Prog(x86.AFMOVFP)
   239  		p.From.Type = obj.TYPE_REG
   240  		p.From.Reg = x86.REG_F0
   241  		s.AddrScratch(&p.To)
   242  		p = s.Prog(x86.AFMOVF)
   243  		s.AddrScratch(&p.From)
   244  		p.To.Type = obj.TYPE_REG
   245  		p.To.Reg = x86.REG_F0
   246  		popAndSave(s, v)
   247  
   248  	case ssa.OpLoadReg:
   249  		if !v.Type.IsFloat() {
   250  			ssaGenValue(s, v)
   251  			return
   252  		}
   253  		// Load+push the value we need.
   254  		p := s.Prog(loadPush(v.Type))
   255  		gc.AddrAuto(&p.From, v.Args[0])
   256  		p.To.Type = obj.TYPE_REG
   257  		p.To.Reg = x86.REG_F0
   258  		// Move the value to its assigned register.
   259  		popAndSave(s, v)
   260  
   261  	case ssa.OpStoreReg:
   262  		if !v.Type.IsFloat() {
   263  			ssaGenValue(s, v)
   264  			return
   265  		}
   266  		push(s, v.Args[0])
   267  		var op obj.As
   268  		switch v.Type.Size() {
   269  		case 4:
   270  			op = x86.AFMOVFP
   271  		case 8:
   272  			op = x86.AFMOVDP
   273  		}
   274  		p := s.Prog(op)
   275  		p.From.Type = obj.TYPE_REG
   276  		p.From.Reg = x86.REG_F0
   277  		gc.AddrAuto(&p.To, v)
   278  
   279  	case ssa.OpCopy:
   280  		if !v.Type.IsFloat() {
   281  			ssaGenValue(s, v)
   282  			return
   283  		}
   284  		push(s, v.Args[0])
   285  		popAndSave(s, v)
   286  
   287  	case ssa.Op386CALLstatic, ssa.Op386CALLclosure, ssa.Op386CALLinter:
   288  		flush387(s) // Calls must empty the FP stack.
   289  		fallthrough // then issue the call as normal
   290  	default:
   291  		ssaGenValue(s, v)
   292  	}
   293  }
   294  
   295  // push pushes v onto the floating-point stack.  v must be in a register.
   296  func push(s *gc.SSAGenState, v *ssa.Value) {
   297  	p := s.Prog(x86.AFMOVD)
   298  	p.From.Type = obj.TYPE_REG
   299  	p.From.Reg = s.SSEto387[v.Reg()]
   300  	p.To.Type = obj.TYPE_REG
   301  	p.To.Reg = x86.REG_F0
   302  }
   303  
   304  // popAndSave pops a value off of the floating-point stack and stores
   305  // it in the reigster assigned to v.
   306  func popAndSave(s *gc.SSAGenState, v *ssa.Value) {
   307  	r := v.Reg()
   308  	if _, ok := s.SSEto387[r]; ok {
   309  		// Pop value, write to correct register.
   310  		p := s.Prog(x86.AFMOVDP)
   311  		p.From.Type = obj.TYPE_REG
   312  		p.From.Reg = x86.REG_F0
   313  		p.To.Type = obj.TYPE_REG
   314  		p.To.Reg = s.SSEto387[v.Reg()] + 1
   315  	} else {
   316  		// Don't actually pop value. This 387 register is now the
   317  		// new home for the not-yet-assigned-a-home SSE register.
   318  		// Increase the register mapping of all other registers by one.
   319  		for rSSE, r387 := range s.SSEto387 {
   320  			s.SSEto387[rSSE] = r387 + 1
   321  		}
   322  		s.SSEto387[r] = x86.REG_F0
   323  	}
   324  }
   325  
   326  // loadPush returns the opcode for load+push of the given type.
   327  func loadPush(t *types.Type) obj.As {
   328  	if t.Size() == 4 {
   329  		return x86.AFMOVF
   330  	}
   331  	return x86.AFMOVD
   332  }
   333  
   334  // flush387 removes all entries from the 387 floating-point stack.
   335  func flush387(s *gc.SSAGenState) {
   336  	for k := range s.SSEto387 {
   337  		p := s.Prog(x86.AFMOVDP)
   338  		p.From.Type = obj.TYPE_REG
   339  		p.From.Reg = x86.REG_F0
   340  		p.To.Type = obj.TYPE_REG
   341  		p.To.Reg = x86.REG_F0
   342  		delete(s.SSEto387, k)
   343  	}
   344  }
   345  
   346  func ssaGenBlock387(s *gc.SSAGenState, b, next *ssa.Block) {
   347  	// Empty the 387's FP stack before the block ends.
   348  	flush387(s)
   349  
   350  	ssaGenBlock(s, b, next)
   351  }