github.com/riscv/riscv-go@v0.0.0-20200123204226-124ebd6fcc8e/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 = v.Args[0].Reg() 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 = v.Args[0].Reg() 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 = v.Args[1].Reg() 50 case ssa.Op386MOVSSloadidx4: 51 p.From.Scale = 4 52 p.From.Index = v.Args[1].Reg() 53 case ssa.Op386MOVSDloadidx8: 54 p.From.Scale = 8 55 p.From.Index = v.Args[1].Reg() 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 = v.Args[0].Reg() 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 = v.Args[0].Reg() 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 = v.Args[1].Reg() 101 case ssa.Op386MOVSSstoreidx4: 102 p.To.Scale = 4 103 p.To.Index = v.Args[1].Reg() 104 case ssa.Op386MOVSDstoreidx8: 105 p.To.Scale = 8 106 p.To.Index = v.Args[1].Reg() 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 v.Reg() != v.Args[0].Reg() { 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 s.AddrScratch(&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[v.Reg()] + 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 s.AddrScratch(&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[v.Args[1].Reg()] + 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 s.AddrScratch(&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 s.AddrScratch(&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 = v.Args[0].Reg() 204 s.AddrScratch(&p.To) 205 p = gc.Prog(x86.AFMOVL) 206 s.AddrScratch(&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 s.AddrScratch(&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 s.AddrScratch(&p.To) 231 p = gc.Prog(x86.AMOVL) 232 s.AddrScratch(&p.From) 233 p.To.Type = obj.TYPE_REG 234 p.To.Reg = v.Reg() 235 236 // Restore control word. 237 p = gc.Prog(x86.AFLDCW) 238 s.AddrScratch(&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 s.AddrScratch(&p.To) 255 p = gc.Prog(x86.AFMOVF) 256 s.AddrScratch(&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 gc.AddrAuto(&p.From, v.Args[0]) 269 p.To.Type = obj.TYPE_REG 270 p.To.Reg = x86.REG_F0 271 // Move the value to its assigned register. 272 popAndSave(s, v) 273 return true 274 275 case ssa.OpStoreReg: 276 if !v.Type.IsFloat() { 277 return false 278 } 279 push(s, v.Args[0]) 280 var op obj.As 281 switch v.Type.Size() { 282 case 4: 283 op = x86.AFMOVFP 284 case 8: 285 op = x86.AFMOVDP 286 } 287 p := gc.Prog(op) 288 p.From.Type = obj.TYPE_REG 289 p.From.Reg = x86.REG_F0 290 gc.AddrAuto(&p.To, v) 291 return true 292 293 case ssa.OpCopy: 294 if !v.Type.IsFloat() { 295 return false 296 } 297 push(s, v.Args[0]) 298 popAndSave(s, v) 299 return true 300 301 case ssa.Op386CALLstatic, ssa.Op386CALLclosure, ssa.Op386CALLdefer, ssa.Op386CALLgo, ssa.Op386CALLinter: 302 flush387(s) // Calls must empty the the FP stack. 303 return false // then issue the call as normal 304 } 305 return false 306 } 307 308 // push pushes v onto the floating-point stack. v must be in a register. 309 func push(s *gc.SSAGenState, v *ssa.Value) { 310 p := gc.Prog(x86.AFMOVD) 311 p.From.Type = obj.TYPE_REG 312 p.From.Reg = s.SSEto387[v.Reg()] 313 p.To.Type = obj.TYPE_REG 314 p.To.Reg = x86.REG_F0 315 } 316 317 // popAndSave pops a value off of the floating-point stack and stores 318 // it in the reigster assigned to v. 319 func popAndSave(s *gc.SSAGenState, v *ssa.Value) { 320 r := v.Reg() 321 if _, ok := s.SSEto387[r]; ok { 322 // Pop value, write to correct register. 323 p := gc.Prog(x86.AFMOVDP) 324 p.From.Type = obj.TYPE_REG 325 p.From.Reg = x86.REG_F0 326 p.To.Type = obj.TYPE_REG 327 p.To.Reg = s.SSEto387[v.Reg()] + 1 328 } else { 329 // Don't actually pop value. This 387 register is now the 330 // new home for the not-yet-assigned-a-home SSE register. 331 // Increase the register mapping of all other registers by one. 332 for rSSE, r387 := range s.SSEto387 { 333 s.SSEto387[rSSE] = r387 + 1 334 } 335 s.SSEto387[r] = x86.REG_F0 336 } 337 } 338 339 // loadPush returns the opcode for load+push of the given type. 340 func loadPush(t ssa.Type) obj.As { 341 if t.Size() == 4 { 342 return x86.AFMOVF 343 } 344 return x86.AFMOVD 345 } 346 347 // flush387 removes all entries from the 387 floating-point stack. 348 func flush387(s *gc.SSAGenState) { 349 for k := range s.SSEto387 { 350 p := gc.Prog(x86.AFMOVDP) 351 p.From.Type = obj.TYPE_REG 352 p.From.Reg = x86.REG_F0 353 p.To.Type = obj.TYPE_REG 354 p.To.Reg = x86.REG_F0 355 delete(s.SSEto387, k) 356 } 357 }