Source file test/codegen/math.go
1 // asmcheck -gcflags=-d=converthash=qy 2 3 // Copyright 2018 The Go Authors. All rights reserved. 4 // Use of this source code is governed by a BSD-style 5 // license that can be found in the LICENSE file. 6 7 package codegen 8 9 import "math" 10 11 var sink64 [8]float64 12 13 func approx(x float64) { 14 // amd64/v2:-".*x86HasSSE41" amd64/v3:-".*x86HasSSE41" 15 // amd64:"ROUNDSD [$]2" 16 // s390x:"FIDBR [$]6" 17 // arm64:"FRINTPD" 18 // ppc64x:"FRIP" 19 // wasm:"F64Ceil" 20 sink64[0] = math.Ceil(x) 21 22 // amd64/v2:-".*x86HasSSE41" amd64/v3:-".*x86HasSSE41" 23 // amd64:"ROUNDSD [$]1" 24 // s390x:"FIDBR [$]7" 25 // arm64:"FRINTMD" 26 // ppc64x:"FRIM" 27 // wasm:"F64Floor" 28 sink64[1] = math.Floor(x) 29 30 // s390x:"FIDBR [$]1" 31 // arm64:"FRINTAD" 32 // ppc64x:"FRIN" 33 sink64[2] = math.Round(x) 34 35 // amd64/v2:-".*x86HasSSE41" amd64/v3:-".*x86HasSSE41" 36 // amd64:"ROUNDSD [$]3" 37 // s390x:"FIDBR [$]5" 38 // arm64:"FRINTZD" 39 // ppc64x:"FRIZ" 40 // wasm:"F64Trunc" 41 sink64[3] = math.Trunc(x) 42 43 // amd64/v2:-".*x86HasSSE41" amd64/v3:-".*x86HasSSE41" 44 // amd64:"ROUNDSD [$]0" 45 // s390x:"FIDBR [$]4" 46 // arm64:"FRINTND" 47 // wasm:"F64Nearest" 48 sink64[4] = math.RoundToEven(x) 49 } 50 51 func sqrt(x float64) float64 { 52 // amd64:"SQRTSD" 53 // 386/sse2:"SQRTSD" 386/softfloat:-"SQRTD" 54 // arm64:"FSQRTD" 55 // arm/7:"SQRTD" 56 // loong64:"SQRTD" 57 // mips/hardfloat:"SQRTD" mips/softfloat:-"SQRTD" 58 // mips64/hardfloat:"SQRTD" mips64/softfloat:-"SQRTD" 59 // wasm:"F64Sqrt" 60 // ppc64x:"FSQRT" 61 // riscv64: "FSQRTD" 62 return math.Sqrt(x) 63 } 64 65 func sqrt32(x float32) float32 { 66 // amd64:"SQRTSS" 67 // 386/sse2:"SQRTSS" 386/softfloat:-"SQRTS" 68 // arm64:"FSQRTS" 69 // arm/7:"SQRTF" 70 // loong64:"SQRTF" 71 // mips/hardfloat:"SQRTF" mips/softfloat:-"SQRTF" 72 // mips64/hardfloat:"SQRTF" mips64/softfloat:-"SQRTF" 73 // wasm:"F32Sqrt" 74 // ppc64x:"FSQRTS" 75 // riscv64: "FSQRTS" 76 return float32(math.Sqrt(float64(x))) 77 } 78 79 // Check that it's using integer registers 80 func abs(x, y float64) { 81 // amd64:"BTRQ [$]63" 82 // arm64:"FABSD " 83 // loong64:"ABSD " 84 // s390x:"LPDFR " -"MOVD " (no integer load/store) 85 // ppc64x:"FABS " 86 // riscv64:"FABSD " 87 // wasm:"F64Abs" 88 // arm/6:"ABSD " 89 // mips64/hardfloat:"ABSD " 90 // mips/hardfloat:"ABSD " 91 sink64[0] = math.Abs(x) 92 93 // amd64:"BTRQ [$]63" "PXOR" (TODO: this should be BTSQ) 94 // s390x:"LNDFR " -"MOVD " (no integer load/store) 95 // ppc64x:"FNABS " 96 sink64[1] = -math.Abs(y) 97 } 98 99 // Check that it's using integer registers 100 func abs32(x float32) float32 { 101 // s390x:"LPDFR" -"LDEBR" -"LEDBR" (no float64 conversion) 102 return float32(math.Abs(float64(x))) 103 } 104 105 // Check that it's using integer registers 106 func copysign(a, b, c float64) { 107 // amd64:"BTRQ [$]63" "ANDQ" "ORQ" 108 // loong64:"FCOPYSGD" 109 // s390x:"CPSDR" -"MOVD" (no integer load/store) 110 // ppc64x:"FCPSGN" 111 // riscv64:"FSGNJD" 112 // wasm:"F64Copysign" 113 sink64[0] = math.Copysign(a, b) 114 115 // amd64:"BTSQ [$]63" 116 // loong64:"FCOPYSGD" 117 // s390x:"LNDFR " -"MOVD " (no integer load/store) 118 // ppc64x:"FCPSGN" 119 // riscv64:"FSGNJD" 120 // arm64:"ORR", -"AND" 121 sink64[1] = math.Copysign(c, -1) 122 123 // Like math.Copysign(c, -1), but with integer operations. Useful 124 // for platforms that have a copysign opcode to see if it's detected. 125 // s390x:"LNDFR " -"MOVD " (no integer load/store) 126 sink64[2] = math.Float64frombits(math.Float64bits(a) | 1<<63) 127 128 // amd64:"ANDQ" "ORQ" 129 // loong64:"FCOPYSGD" 130 // s390x:"CPSDR " -"MOVD " (no integer load/store) 131 // ppc64x:"FCPSGN" 132 // riscv64:"FSGNJD" 133 sink64[3] = math.Copysign(-1, c) 134 } 135 136 func fma(x, y, z float64) float64 { 137 // amd64/v3:-".*x86HasFMA" 138 // amd64:"VFMADD231SD" 139 // arm/6:"FMULAD" 140 // arm64:"FMADDD" 141 // loong64:"FMADDD" 142 // s390x:"FMADD" 143 // ppc64x:"FMADD" 144 // riscv64:"FMADDD" 145 return math.FMA(x, y, z) 146 } 147 148 func fms(x, y, z float64) float64 { 149 // riscv64:"FMSUBD" 150 return math.FMA(x, y, -z) 151 } 152 153 func fnms(x, y, z float64) float64 { 154 // riscv64:"FNMSUBD" -"FNMADDD" 155 return math.FMA(-x, y, z) 156 } 157 158 func fnma(x, y, z float64) float64 { 159 // riscv64:"FNMADDD" -"FNMSUBD" 160 return math.FMA(x, -y, -z) 161 } 162 163 func isPosInf(x float64) bool { 164 // riscv64:"FCLASSD" 165 return math.IsInf(x, 1) 166 } 167 168 func isPosInfEq(x float64) bool { 169 // riscv64:"FCLASSD" 170 return x == math.Inf(1) 171 } 172 173 func isPosInfCmp(x float64) bool { 174 // riscv64:"FCLASSD" 175 return x > math.MaxFloat64 176 } 177 178 func isNotPosInf(x float64) bool { 179 // riscv64:"FCLASSD" 180 return !math.IsInf(x, 1) 181 } 182 183 func isNotPosInfEq(x float64) bool { 184 // riscv64:"FCLASSD" 185 return x != math.Inf(1) 186 } 187 188 func isNotPosInfCmp(x float64) bool { 189 // riscv64:"FCLASSD" 190 return x <= math.MaxFloat64 191 } 192 193 func isNegInf(x float64) bool { 194 // riscv64:"FCLASSD" 195 return math.IsInf(x, -1) 196 } 197 198 func isNegInfEq(x float64) bool { 199 // riscv64:"FCLASSD" 200 return x == math.Inf(-1) 201 } 202 203 func isNegInfCmp(x float64) bool { 204 // riscv64:"FCLASSD" 205 return x < -math.MaxFloat64 206 } 207 208 func isNotNegInf(x float64) bool { 209 // riscv64:"FCLASSD" 210 return !math.IsInf(x, -1) 211 } 212 213 func isNotNegInfEq(x float64) bool { 214 // riscv64:"FCLASSD" 215 return x != math.Inf(-1) 216 } 217 218 func isNotNegInfCmp(x float64) bool { 219 // riscv64:"FCLASSD" 220 return x >= -math.MaxFloat64 221 } 222 223 func fromFloat64(f64 float64) uint64 { 224 // amd64:"MOVQ X.*, [^X].*" 225 // arm64:"FMOVD F.*, R.*" 226 // loong64:"MOVV F.*, R.*" 227 // ppc64x:"MFVSRD" 228 // mips64/hardfloat:"MOVV F.*, R.*" 229 // riscv64:"FMVXD" 230 return math.Float64bits(f64+1) + 1 231 } 232 233 func fromFloat32(f32 float32) uint32 { 234 // amd64:"MOVL X.*, [^X].*" 235 // arm64:"FMOVS F.*, R.*" 236 // loong64:"MOVW F.*, R.*" 237 // mips64/hardfloat:"MOVW F.*, R.*" 238 // riscv64:"FMVXW" 239 return math.Float32bits(f32+1) + 1 240 } 241 242 func toFloat64(u64 uint64) float64 { 243 // amd64:"MOVQ [^X].*, X.*" 244 // arm64:"FMOVD R.*, F.*" 245 // loong64:"MOVV R.*, F.*" 246 // ppc64x:"MTVSRD" 247 // mips64/hardfloat:"MOVV R.*, F.*" 248 // riscv64:"FMVDX" 249 return math.Float64frombits(u64+1) + 1 250 } 251 252 func toFloat32(u32 uint32) float32 { 253 // amd64:"MOVL [^X].*, X.*" 254 // arm64:"FMOVS R.*, F.*" 255 // loong64:"MOVW R.*, F.*" 256 // mips64/hardfloat:"MOVW R.*, F.*" 257 // riscv64:"FMVWX" 258 return math.Float32frombits(u32+1) + 1 259 } 260 261 // Test that comparisons with constants converted to float 262 // are evaluated at compile-time 263 264 func constantCheck64() bool { 265 // amd64:"(MOVB [$]0)|(XORL [A-Z][A-Z0-9]+, [A-Z][A-Z0-9]+)" -"FCMP" -"MOVB [$]1" 266 // s390x:"MOV(B|BZ|D) [$]0," -"FCMPU" -"MOV(B|BZ|D) [$]1," 267 return 0.5 == float64(uint32(1)) || 1.5 > float64(uint64(1<<63)) 268 } 269 270 func constantCheck32() bool { 271 // amd64:"MOV(B|L) [$]1" -"FCMP" -"MOV(B|L) [$]0" 272 // s390x:"MOV(B|BZ|D) [$]1," -"FCMPU" -"MOV(B|BZ|D) [$]0," 273 return float32(0.5) <= float32(int64(1)) && float32(1.5) >= float32(int32(-1<<31)) 274 } 275 276 // Test that integer constants are converted to floating point constants 277 // at compile-time 278 279 func constantConvert32(x float32) float32 { 280 // amd64:"MOVSS [$]f32.3f800000\\(SB\\)" 281 // s390x:"FMOVS [$]f32.3f800000\\(SB\\)" 282 // ppc64x/power8:"FMOVS [$]f32.3f800000\\(SB\\)" 283 // ppc64x/power9:"FMOVS [$]f32.3f800000\\(SB\\)" 284 // ppc64x/power10:"XXSPLTIDP [$]1065353216, VS0" 285 // arm64:"FMOVS [$]\\(1.0\\)" 286 if x > math.Float32frombits(0x3f800000) { 287 return -x 288 } 289 return x 290 } 291 292 func constantConvertInt32(x uint32) uint32 { 293 // amd64:-"MOVSS" 294 // s390x:-"FMOVS" 295 // ppc64x:-"FMOVS" 296 // arm64:-"FMOVS" 297 if x > math.Float32bits(1) { 298 return -x 299 } 300 return x 301 } 302 303 func nanGenerate64() float64 { 304 // Test to make sure we don't generate a NaN while constant propagating. 305 // See issue 36400. 306 zero := 0.0 307 // amd64:-"DIVSD" 308 inf := 1 / zero // +inf. We can constant propagate this one. 309 negone := -1.0 310 311 // amd64:"DIVSD" 312 z0 := zero / zero 313 // amd64/v1,amd64/v2:"MULSD" 314 z1 := zero * inf 315 // amd64:"SQRTSD" 316 z2 := math.Sqrt(negone) 317 // amd64/v3:"VFMADD231SD" 318 return z0 + z1 + z2 319 } 320 321 func nanGenerate32() float32 { 322 zero := float32(0.0) 323 // amd64:-"DIVSS" 324 inf := 1 / zero // +inf. We can constant propagate this one. 325 326 // amd64:"DIVSS" 327 z0 := zero / zero 328 // amd64/v1,amd64/v2:"MULSS" 329 z1 := zero * inf 330 // amd64/v3:"VFMADD231SS" 331 return z0 + z1 332 } 333 334 func outOfBoundsConv(i32 *[2]int32, u32 *[2]uint32, i64 *[2]int64, u64 *[2]uint64) { 335 // arm64: "FCVTZSDW" 336 // amd64: "CVTTSD2SL", "CVTSD2SS" 337 i32[0] = int32(two40()) 338 // arm64: "FCVTZSDW" 339 // amd64: "CVTTSD2SL", "CVTSD2SS" 340 i32[1] = int32(-two40()) 341 // arm64: "FCVTZSDW" 342 // amd64: "CVTTSD2SL", "CVTSD2SS" 343 u32[0] = uint32(two41()) 344 // on arm64, this uses an explicit <0 comparison, so it constant folds. 345 // on amd64, this uses an explicit <0 comparison, so it constant folds. 346 // amd64: "MOVL [$]0," 347 u32[1] = uint32(minus1()) 348 // arm64: "FCVTZSD" 349 // amd64: "CVTTSD2SQ" 350 i64[0] = int64(two80()) 351 // arm64: "FCVTZSD" 352 // amd64: "CVTTSD2SQ" 353 i64[1] = int64(-two80()) 354 // arm64: "FCVTZUD" 355 // amd64: "CVTTSD2SQ" 356 u64[0] = uint64(two81()) 357 // arm64: "FCVTZUD" 358 // on amd64, this uses an explicit <0 comparison, so it constant folds. 359 // amd64: "MOVQ [$]0," 360 u64[1] = uint64(minus1()) 361 } 362 363 func two40() float64 { 364 return 1 << 40 365 } 366 func two41() float64 { 367 return 1 << 41 368 } 369 func two80() float64 { 370 return 1 << 80 371 } 372 func two81() float64 { 373 return 1 << 81 374 } 375 func minus1() float64 { 376 return -1 377 } 378