compiler: defer to middle-end for complex division

Go used to use slightly different semantics than C99 for complex division,
so we used runtime routines to handle the different.  The gc compiler
has changes its behavior to match C99, so changes ours as well.

For golang/go#14644

Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/274213
This commit is contained in:
Ian Lance Taylor 2020-12-01 18:59:18 -08:00
parent 65af6bc9f8
commit cd34d5f2c4
9 changed files with 4176 additions and 2543 deletions

View File

@ -1,4 +1,4 @@
6b01f8cdc11d86bd98165c91d6ae101bcf6b9e1a
5364d15082de77d2759a01f254208d4cb4f579e3
The first line of this file holds the git revision number of the last
merge done from the gofrontend repository.

View File

@ -6979,27 +6979,6 @@ Binary_expression::do_get_backend(Translate_context* context)
// been converted to a String_concat_expression in do_lower.
go_assert(!left_type->is_string_type());
// For complex division Go might want slightly different results than the
// backend implementation provides, so we have our own runtime routine.
if (this->op_ == OPERATOR_DIV && this->left_->type()->complex_type() != NULL)
{
Runtime::Function complex_code;
switch (this->left_->type()->complex_type()->bits())
{
case 64:
complex_code = Runtime::COMPLEX64_DIV;
break;
case 128:
complex_code = Runtime::COMPLEX128_DIV;
break;
default:
go_unreachable();
}
Expression* complex_div =
Runtime::make_call(complex_code, loc, 2, this->left_, this->right_);
return complex_div->get_backend(context);
}
Bexpression* left = this->left_->get_backend(context);
Bexpression* right = this->right_->get_backend(context);

View File

@ -62,12 +62,6 @@ DEF_GO_RUNTIME(STRINGTOSLICERUNE, "runtime.stringtoslicerune",
P2(POINTER, STRING), R1(SLICE))
// Complex division.
DEF_GO_RUNTIME(COMPLEX64_DIV, "__go_complex64_div",
P2(COMPLEX64, COMPLEX64), R1(COMPLEX64))
DEF_GO_RUNTIME(COMPLEX128_DIV, "__go_complex128_div",
P2(COMPLEX128, COMPLEX128), R1(COMPLEX128))
// Make a slice.
DEF_GO_RUNTIME(MAKESLICE, "runtime.makeslice", P3(TYPE, INT, INT),
R1(POINTER))

View File

@ -1,8 +1,19 @@
// Copyright 2010 The Go Authors. All rights reserved.
// 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.
// gcc '-std=c99' cmplxdivide.c && a.out >cmplxdivide1.go
// This C program generates the file cmplxdivide1.go. It uses the
// output of the operations by C99 as the reference to check
// the implementation of complex numbers in Go.
// The generated file, cmplxdivide1.go, is compiled along
// with the driver cmplxdivide.go (the names are confusing
// and unimaginative) to run the actual test. This is done by
// the usual test runner.
//
// The file cmplxdivide1.go is checked in to the repository, but
// if it needs to be regenerated, compile and run this C program
// like this:
// gcc '-std=c99' cmplxdivide.c && a.out >cmplxdivide1.go
#include <complex.h>
#include <math.h>
@ -12,50 +23,63 @@
#define nelem(x) (sizeof(x)/sizeof((x)[0]))
double f[] = {
0,
1,
-1,
2,
0.0,
-0.0,
1.0,
-1.0,
2.0,
NAN,
INFINITY,
-INFINITY,
};
char*
fmt(double g)
{
char* fmt(double g) {
static char buf[10][30];
static int n;
char *p;
p = buf[n++];
if(n == 10)
if(n == 10) {
n = 0;
}
sprintf(p, "%g", g);
if(strcmp(p, "-0") == 0)
strcpy(p, "negzero");
if(strcmp(p, "0") == 0) {
strcpy(p, "zero");
return p;
}
if(strcmp(p, "-0") == 0) {
strcpy(p, "-zero");
return p;
}
return p;
}
int
iscnan(double complex d)
{
return !isinf(creal(d)) && !isinf(cimag(d)) && (isnan(creal(d)) || isnan(cimag(d)));
}
double complex zero; // attempt to hide zero division from gcc
int
main(void)
{
int main(void) {
int i, j, k, l;
double complex n, d, q;
printf("// skip\n");
printf("// # generated by cmplxdivide.c\n");
printf("\n");
printf("package main\n");
printf("var tests = []Test{\n");
printf("\n");
printf("import \"math\"\n");
printf("\n");
printf("var (\n");
printf("\tnan = math.NaN()\n");
printf("\tinf = math.Inf(1)\n");
printf("\tzero = 0.0\n");
printf(")\n");
printf("\n");
printf("var tests = []struct {\n");
printf("\tf, g complex128\n");
printf("\tout complex128\n");
printf("}{\n");
for(i=0; i<nelem(f); i++)
for(j=0; j<nelem(f); j++)
for(k=0; k<nelem(f); k++)
@ -63,17 +87,8 @@ main(void)
n = f[i] + f[j]*I;
d = f[k] + f[l]*I;
q = n/d;
// BUG FIX.
// Gcc gets the wrong answer for NaN/0 unless both sides are NaN.
// That is, it treats (NaN+NaN*I)/0 = NaN+NaN*I (a complex NaN)
// but it then computes (1+NaN*I)/0 = Inf+NaN*I (a complex infinity).
// Since both numerators are complex NaNs, it seems that the
// results should agree in kind. Override the gcc computation in this case.
if(iscnan(n) && d == 0)
q = (NAN+NAN*I) / zero;
printf("\tTest{complex(%s, %s), complex(%s, %s), complex(%s, %s)},\n",
printf("\t{complex(%s, %s), complex(%s, %s), complex(%s, %s)},\n",
fmt(creal(n)), fmt(cimag(n)),
fmt(creal(d)), fmt(cimag(d)),
fmt(creal(q)), fmt(cimag(q)));

View File

@ -1,36 +1,29 @@
// run cmplxdivide1.go
// Copyright 2010 The Go Authors. All rights reserved.
// 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.
// Driver for complex division table defined in cmplxdivide1.go
// For details, see the comment at the top of cmplxdivide.c.
package main
import (
"fmt"
"math"
"math/cmplx"
)
type Test struct {
f, g complex128
out complex128
}
var nan = math.NaN()
var inf = math.Inf(1)
var negzero = math.Copysign(0, -1)
func calike(a, b complex128) bool {
switch {
case cmplx.IsInf(a) && cmplx.IsInf(b):
return true
case cmplx.IsNaN(a) && cmplx.IsNaN(b):
return true
if imag(a) != imag(b) && !(math.IsNaN(imag(a)) && math.IsNaN(imag(b))) {
return false
}
return a == b
if real(a) != real(b) && !(math.IsNaN(real(a)) && math.IsNaN(real(b))) {
return false
}
return true
}
func main() {

File diff suppressed because it is too large Load Diff

View File

@ -439,7 +439,6 @@ runtime_files = \
runtime/go-assert.c \
runtime/go-caller.c \
runtime/go-callers.c \
runtime/go-cdiv.c \
runtime/go-cgo.c \
runtime/go-construct-map.c \
runtime/go-ffi.c \

View File

@ -240,12 +240,11 @@ am__objects_2 = $(am__objects_1)
@LIBGO_IS_RTEMS_TRUE@am__objects_3 = \
@LIBGO_IS_RTEMS_TRUE@ runtime/rtems-task-variable-add.lo
am__objects_4 = runtime/aeshash.lo runtime/go-assert.lo \
runtime/go-caller.lo runtime/go-callers.lo runtime/go-cdiv.lo \
runtime/go-cgo.lo runtime/go-construct-map.lo \
runtime/go-ffi.lo runtime/go-fieldtrack.lo \
runtime/go-matherr.lo runtime/go-memclr.lo \
runtime/go-memequal.lo runtime/go-nanotime.lo \
runtime/go-now.lo runtime/go-nosys.lo \
runtime/go-caller.lo runtime/go-callers.lo runtime/go-cgo.lo \
runtime/go-construct-map.lo runtime/go-ffi.lo \
runtime/go-fieldtrack.lo runtime/go-matherr.lo \
runtime/go-memclr.lo runtime/go-memequal.lo \
runtime/go-nanotime.lo runtime/go-now.lo runtime/go-nosys.lo \
runtime/go-reflect-call.lo runtime/go-setenv.lo \
runtime/go-signal.lo runtime/go-unsafe-pointer.lo \
runtime/go-unsetenv.lo runtime/go-unwind.lo \
@ -892,7 +891,6 @@ runtime_files = \
runtime/go-assert.c \
runtime/go-caller.c \
runtime/go-callers.c \
runtime/go-cdiv.c \
runtime/go-cgo.c \
runtime/go-construct-map.c \
runtime/go-ffi.c \
@ -1343,8 +1341,6 @@ runtime/go-caller.lo: runtime/$(am__dirstamp) \
runtime/$(DEPDIR)/$(am__dirstamp)
runtime/go-callers.lo: runtime/$(am__dirstamp) \
runtime/$(DEPDIR)/$(am__dirstamp)
runtime/go-cdiv.lo: runtime/$(am__dirstamp) \
runtime/$(DEPDIR)/$(am__dirstamp)
runtime/go-cgo.lo: runtime/$(am__dirstamp) \
runtime/$(DEPDIR)/$(am__dirstamp)
runtime/go-construct-map.lo: runtime/$(am__dirstamp) \
@ -1417,7 +1413,6 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@runtime/$(DEPDIR)/go-assert.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@runtime/$(DEPDIR)/go-caller.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@runtime/$(DEPDIR)/go-callers.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@runtime/$(DEPDIR)/go-cdiv.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@runtime/$(DEPDIR)/go-cgo.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@runtime/$(DEPDIR)/go-construct-map.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@runtime/$(DEPDIR)/go-context.Plo@am__quote@

View File

@ -1,49 +0,0 @@
/* go-cdiv.c -- complex division routines
Copyright 2013 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. */
#include <complex.h>
#include <math.h>
/* Calls to these functions are generated by the Go frontend for
division of complex64 or complex128. We use these because Go's
complex division expects slightly different results from the GCC
default. When dividing NaN+1.0i / 0+0i, Go expects NaN+NaNi but
GCC generates NaN+Infi. NaN+Infi seems wrong seems the rules of
C99 Annex G specify that if either side of a complex number is Inf,
the the whole number is Inf, but an operation involving NaN ought
to result in NaN, not Inf. */
complex float
__go_complex64_div (complex float a, complex float b)
{
if (__builtin_expect (b == 0, 0))
{
if (!isinf (crealf (a))
&& !isinf (cimagf (a))
&& (isnan (crealf (a)) || isnan (cimagf (a))))
{
/* Pass "1" to nanf to match math/bits.go. */
return nanf("1") + nanf("1")*I;
}
}
return a / b;
}
complex double
__go_complex128_div (complex double a, complex double b)
{
if (__builtin_expect (b == 0, 0))
{
if (!isinf (creal (a))
&& !isinf (cimag (a))
&& (isnan (creal (a)) || isnan (cimag (a))))
{
/* Pass "1" to nan to match math/bits.go. */
return nan("1") + nan("1")*I;
}
}
return a / b;
}