Skip to content

Commit

Permalink
[dev.regabi] cmd/compile: enable rational constant arithmetic
Browse files Browse the repository at this point in the history
This allows more precision and matches types2's behavior.

For backwards compatibility with gcimporter, for now we still need to
write out declared constants as limited-precision floating-point
values. To ensure consistent behavior of constant arithmetic whether
it spans package boundaries or not, we include the full-precision
rational representation in the compiler's extension section of the
export data.

Also, this CL simply uses the math/big.Rat.String text representation
as the encoding. This is inefficient, but because it's only in the
compiler's extension section, we can easily revisit this in the
future.

Declaring exported untyped float and complex constants isn't very
common anyway. Within the standard library, only package math declares
any at all, containing just 15. And those 15 are only imported a total
of 12 times elsewhere in the standard library.

Change-Id: I85ea23ab712e93fd3b68e52d60cbedce9be696a0
Reviewed-on: https://go-review.googlesource.com/c/go/+/286215
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Trust: Matthew Dempsky <mdempsky@google.com>
Trust: Robert Griesemer <gri@golang.org>
Reviewed-by: Robert Griesemer <gri@golang.org>
  • Loading branch information
mdempsky committed Jan 25, 2021
1 parent be9612a commit 6a4739c
Show file tree
Hide file tree
Showing 5 changed files with 76 additions and 17 deletions.
8 changes: 0 additions & 8 deletions src/cmd/compile/internal/noder/noder.go
Original file line number Diff line number Diff line change
Expand Up @@ -1455,14 +1455,6 @@ func (p *noder) basicLit(lit *syntax.BasicLit) constant.Value {
p.errorAt(lit.Pos(), "malformed constant: %s", lit.Value)
}

// go/constant uses big.Rat by default, which is more precise, but
// causes toolstash -cmp and some tests to fail. For now, convert
// to big.Float to match cmd/compile's historical precision.
// TODO(mdempsky): Remove.
if v.Kind() == constant.Float {
v = constant.Make(ir.BigFloat(v))
}

return v
}

Expand Down
51 changes: 48 additions & 3 deletions src/cmd/compile/internal/typecheck/iexport.go
Original file line number Diff line number Diff line change
Expand Up @@ -462,12 +462,16 @@ func (p *iexporter) doDecl(n *ir.Name) {
}

case ir.OLITERAL:
// TODO(mdempsky): Extend check to all declarations.
if n.Typecheck() == 0 {
base.FatalfAt(n.Pos(), "missed typecheck: %v", n)
}

// Constant.
// TODO(mdempsky): Do we still need this typecheck? If so, why?
n = Expr(n).(*ir.Name)
w.tag('C')
w.pos(n.Pos())
w.value(n.Type(), n.Val())
w.constExt(n)

case ir.OTYPE:
if types.IsDotAlias(n.Sym()) {
Expand Down Expand Up @@ -956,6 +960,17 @@ func (w *exportWriter) mpfloat(v constant.Value, typ *types.Type) {
}
}

func (w *exportWriter) mprat(v constant.Value) {
r, ok := constant.Val(v).(*big.Rat)
if !w.bool(ok) {
return
}
// TODO(mdempsky): Come up with a more efficient binary
// encoding before bumping iexportVersion to expose to
// gcimporter.
w.string(r.String())
}

func (w *exportWriter) bool(b bool) bool {
var x uint64
if b {
Expand All @@ -971,7 +986,37 @@ func (w *exportWriter) string(s string) { w.uint64(w.p.stringOff(s)) }

// Compiler-specific extensions.

func (w *exportWriter) varExt(n ir.Node) {
func (w *exportWriter) constExt(n *ir.Name) {
// Internally, we now represent untyped float and complex
// constants with infinite-precision rational numbers using
// go/constant, but the "public" export data format known to
// gcimporter only supports 512-bit floating point constants.
// In case rationals turn out to be a bad idea and we want to
// switch back to fixed-precision constants, for now we
// continue writing out the 512-bit truncation in the public
// data section, and write the exact, rational constant in the
// compiler's extension data. Also, we only need to worry
// about exporting rationals for declared constants, because
// constants that appear in an expression will already have
// been coerced to a concrete, fixed-precision type.
//
// Eventually, assuming we stick with using rationals, we
// should bump iexportVersion to support rationals, and do the
// whole gcimporter update song-and-dance.
//
// TODO(mdempsky): Prepare vocals for that.

switch n.Type() {
case types.UntypedFloat:
w.mprat(n.Val())
case types.UntypedComplex:
v := n.Val()
w.mprat(constant.Real(v))
w.mprat(constant.Imag(v))
}
}

func (w *exportWriter) varExt(n *ir.Name) {
w.linkname(n.Sym())
w.symIdx(n.Sym())
}
Expand Down
27 changes: 25 additions & 2 deletions src/cmd/compile/internal/typecheck/iimport.go
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,9 @@ func (r *importReader) doDecl(sym *types.Sym) *ir.Name {
typ := r.typ()
val := r.value(typ)

return importconst(r.p.ipkg, pos, sym, typ, val)
n := importconst(r.p.ipkg, pos, sym, typ, val)
r.constExt(n)
return n

case 'F':
typ := r.signature(nil)
Expand Down Expand Up @@ -440,6 +442,15 @@ func (p *importReader) float(typ *types.Type) constant.Value {
return constant.Make(&f)
}

func (p *importReader) mprat(orig constant.Value) constant.Value {
if !p.bool() {
return orig
}
var rat big.Rat
rat.SetString(p.string())
return constant.Make(&rat)
}

func (r *importReader) ident(selector bool) *types.Sym {
name := r.string()
if name == "" {
Expand Down Expand Up @@ -641,7 +652,19 @@ func (r *importReader) byte() byte {

// Compiler-specific extensions.

func (r *importReader) varExt(n ir.Node) {
func (r *importReader) constExt(n *ir.Name) {
switch n.Type() {
case types.UntypedFloat:
n.SetVal(r.mprat(n.Val()))
case types.UntypedComplex:
v := n.Val()
re := r.mprat(constant.Real(v))
im := r.mprat(constant.Imag(v))
n.SetVal(makeComplex(re, im))
}
}

func (r *importReader) varExt(n *ir.Name) {
r.linkname(n.Sym())
r.symIdx(n.Sym())
}
Expand Down
2 changes: 1 addition & 1 deletion test/fixedbugs/issue7740.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ func main() {
var prec float64
switch runtime.Compiler {
case "gc":
prec = 512
prec = math.Inf(1) // exact precision using rational arithmetic
case "gccgo":
prec = 256
default:
Expand Down
5 changes: 2 additions & 3 deletions test/float_lit3.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,11 @@ var x = []interface{}{

// If the compiler's internal floating point representation
// is shorter than 1024 bits, it cannot distinguish max64+ulp64/2-1 and max64+ulp64/2.
// gc uses fewer than 1024 bits, so allow it to print the overflow error for the -1 case.
float64(max64 + ulp64/2 - two1024/two256), // ok
float64(max64 + ulp64/2 - 1), // GC_ERROR "constant 1\.79769e\+308 overflows float64"
float64(max64 + ulp64/2 - 1), // ok
float64(max64 + ulp64/2), // ERROR "constant 1\.79769e\+308 overflows float64"

float64(-max64 - ulp64/2 + two1024/two256), // ok
float64(-max64 - ulp64/2 + 1), // GC_ERROR "constant -1\.79769e\+308 overflows float64"
float64(-max64 - ulp64/2 + 1), // ok
float64(-max64 - ulp64/2), // ERROR "constant -1\.79769e\+308 overflows float64"
}

0 comments on commit 6a4739c

Please sign in to comment.