compiler: implement complex division

This is hard to do correctly, so copy the relevant files from the Go
compiler itself.

For related discussions:
* https://github.com/golang/go/issues/14644
* https://github.com/golang/go/issues/29846
This commit is contained in:
Ayke van Laethem
2019-05-04 23:02:32 +02:00
committed by Ron Evans
parent d7460b945e
commit 4ae4ef5e12
6 changed files with 138 additions and 1 deletions
+3
View File
@@ -1,5 +1,8 @@
Copyright (c) 2018-2019 TinyGo Authors. All rights reserved. Copyright (c) 2018-2019 TinyGo Authors. All rights reserved.
TinyGo includes portions of the Go standard library.
Copyright (c) 2009-2019 The Go Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are modification, are permitted provided that the following conditions are
met: met:
+13 -1
View File
@@ -1856,8 +1856,20 @@ func (c *Compiler) parseBinOp(op token.Token, typ types.Type, x, y llvm.Value, p
cplx = c.builder.CreateInsertValue(cplx, r, 0, "") cplx = c.builder.CreateInsertValue(cplx, r, 0, "")
cplx = c.builder.CreateInsertValue(cplx, i, 1, "") cplx = c.builder.CreateInsertValue(cplx, i, 1, "")
return cplx, nil return cplx, nil
case token.QUO:
// Complex division.
// Do this in a library call because it's too difficult to do
// inline.
switch r1.Type().TypeKind() {
case llvm.FloatTypeKind:
return c.createRuntimeCall("complex64div", []llvm.Value{x, y}, ""), nil
case llvm.DoubleTypeKind:
return c.createRuntimeCall("complex128div", []llvm.Value{x, y}, ""), nil
default:
panic("unexpected complex type")
}
default: default:
return llvm.Value{}, c.makeError(pos, "todo: binop on complex number: "+op.String()) panic("binop on complex: " + op.String())
} }
} else if typ.Info()&types.IsBoolean != 0 { } else if typ.Info()&types.IsBoolean != 0 {
// Operations on booleans // Operations on booleans
+65
View File
@@ -0,0 +1,65 @@
// Copyright 2010 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package runtime
// inf2one returns a signed 1 if f is an infinity and a signed 0 otherwise.
// The sign of the result is the sign of f.
func inf2one(f float64) float64 {
g := 0.0
if isInf(f) {
g = 1.0
}
return copysign(g, f)
}
func complex64div(n complex64, m complex64) complex64 {
return complex64(complex128div(complex128(n), complex128(m)))
}
func complex128div(n complex128, m complex128) complex128 {
var e, f float64 // complex(e, f) = n/m
// Algorithm for robust complex division as described in
// Robert L. Smith: Algorithm 116: Complex division. Commun. ACM 5(8): 435 (1962).
if abs(real(m)) >= abs(imag(m)) {
ratio := imag(m) / real(m)
denom := real(m) + ratio*imag(m)
e = (real(n) + imag(n)*ratio) / denom
f = (imag(n) - real(n)*ratio) / denom
} else {
ratio := real(m) / imag(m)
denom := imag(m) + ratio*real(m)
e = (real(n)*ratio + imag(n)) / denom
f = (imag(n)*ratio - real(n)) / denom
}
if isNaN(e) && isNaN(f) {
// Correct final result to infinities and zeros if applicable.
// Matches C99: ISO/IEC 9899:1999 - G.5.1 Multiplicative operators.
a, b := real(n), imag(n)
c, d := real(m), imag(m)
switch {
case m == 0 && (!isNaN(a) || !isNaN(b)):
e = copysign(inf, c) * a
f = copysign(inf, c) * b
case (isInf(a) || isInf(b)) && isFinite(c) && isFinite(d):
a = inf2one(a)
b = inf2one(b)
e = inf * (a*c + b*d)
f = inf * (b*c - a*d)
case (isInf(c) || isInf(d)) && isFinite(a) && isFinite(b):
c = inf2one(c)
d = inf2one(d)
e = 0 * (a*c + b*d)
f = 0 * (b*c - a*d)
}
}
return complex(e, f)
}
+53
View File
@@ -0,0 +1,53 @@
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package runtime
import "unsafe"
var inf = float64frombits(0x7FF0000000000000)
// isNaN reports whether f is an IEEE 754 ``not-a-number'' value.
func isNaN(f float64) (is bool) {
// IEEE 754 says that only NaNs satisfy f != f.
return f != f
}
// isFinite reports whether f is neither NaN nor an infinity.
func isFinite(f float64) bool {
return !isNaN(f - f)
}
// isInf reports whether f is an infinity.
func isInf(f float64) bool {
return !isNaN(f) && !isFinite(f)
}
// Abs returns the absolute value of x.
//
// Special cases are:
// Abs(±Inf) = +Inf
// Abs(NaN) = NaN
func abs(x float64) float64 {
const sign = 1 << 63
return float64frombits(float64bits(x) &^ sign)
}
// copysign returns a value with the magnitude
// of x and the sign of y.
func copysign(x, y float64) float64 {
const sign = 1 << 63
return float64frombits(float64bits(x)&^sign | float64bits(y)&sign)
}
// Float64bits returns the IEEE 754 binary representation of f.
func float64bits(f float64) uint64 {
return *(*uint64)(unsafe.Pointer(&f))
}
// Float64frombits returns the floating point number corresponding
// the IEEE 754 binary representation b.
func float64frombits(b uint64) float64 {
return *(*float64)(unsafe.Pointer(&b))
}
+2
View File
@@ -62,8 +62,10 @@ func main() {
println("complex64 add: ", c64 + -3+8i) println("complex64 add: ", c64 + -3+8i)
println("complex64 sub: ", c64 - -3+8i) println("complex64 sub: ", c64 - -3+8i)
println("complex64 mul: ", c64 * -3+8i) println("complex64 mul: ", c64 * -3+8i)
println("complex64 div: ", c64 / -3+8i)
c128 = -5+2i c128 = -5+2i
println("complex128 add:", c128 + 2+6i) println("complex128 add:", c128 + 2+6i)
println("complex128 sub:", c128 - 2+6i) println("complex128 sub:", c128 - 2+6i)
println("complex128 mul:", c128 * 2+6i) println("complex128 mul:", c128 * 2+6i)
println("complex128 div:", c128 / 2+6i)
} }
+2
View File
@@ -26,6 +26,8 @@
complex64 add: (+2.000000e+000+1.000000e+001i) complex64 add: (+2.000000e+000+1.000000e+001i)
complex64 sub: (+8.000000e+000+1.000000e+001i) complex64 sub: (+8.000000e+000+1.000000e+001i)
complex64 mul: (-1.500000e+001+2.000000e+000i) complex64 mul: (-1.500000e+001+2.000000e+000i)
complex64 div: (-1.666667e+000+7.333333e+000i)
complex128 add: (-3.000000e+000+8.000000e+000i) complex128 add: (-3.000000e+000+8.000000e+000i)
complex128 sub: (-7.000000e+000+8.000000e+000i) complex128 sub: (-7.000000e+000+8.000000e+000i)
complex128 mul: (-1.000000e+001+1.000000e+001i) complex128 mul: (-1.000000e+001+1.000000e+001i)
complex128 div: (-2.500000e+000+7.000000e+000i)