github.com/megatontech/mynoteforgo@v0.0.0-20200507084910-5d0c6ea6e890/源码/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 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 }