...

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

View as plain text