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