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 }