Commit 6be1c09e authored by Vladimir Stefanovic's avatar Vladimir Stefanovic Committed by Brad Fitzpatrick

cmd/compile: use soft-float routines for soft-float targets

Updates #18162 (mostly fixes)

Change-Id: I35bcb8a688bdaa432adb0ddbb73a2f7adda47b9e
Reviewed-on: https://go-review.googlesource.com/37958
Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: default avatarCherry Zhang <cherryyz@google.com>
parent f0f62fcc
...@@ -243,9 +243,10 @@ var autogeneratedPos src.XPos ...@@ -243,9 +243,10 @@ var autogeneratedPos src.XPos
type Arch struct { type Arch struct {
LinkArch *obj.LinkArch LinkArch *obj.LinkArch
REGSP int REGSP int
MAXWIDTH int64 MAXWIDTH int64
Use387 bool // should 386 backend use 387 FP instructions instead of sse2. Use387 bool // should 386 backend use 387 FP instructions instead of sse2.
SoftFloat bool
PadFrame func(int64) int64 PadFrame func(int64) int64
ZeroRange func(*Progs, *obj.Prog, int64, int64, *uint32) *obj.Prog ZeroRange func(*Progs, *obj.Prog, int64, int64, *uint32) *obj.Prog
......
...@@ -49,6 +49,7 @@ var ( ...@@ -49,6 +49,7 @@ var (
Debug_locationlist int Debug_locationlist int
Debug_typecheckinl int Debug_typecheckinl int
Debug_gendwarfinl int Debug_gendwarfinl int
Debug_softfloat int
) )
// Debug arguments. // Debug arguments.
...@@ -78,6 +79,7 @@ var debugtab = []struct { ...@@ -78,6 +79,7 @@ var debugtab = []struct {
{"locationlists", "print information about DWARF location list creation", &Debug_locationlist}, {"locationlists", "print information about DWARF location list creation", &Debug_locationlist},
{"typecheckinl", "eager typechecking of inline function bodies", &Debug_typecheckinl}, {"typecheckinl", "eager typechecking of inline function bodies", &Debug_typecheckinl},
{"dwarfinl", "print information about DWARF inlined function creation", &Debug_gendwarfinl}, {"dwarfinl", "print information about DWARF inlined function creation", &Debug_gendwarfinl},
{"softfloat", "force compiler to emit soft-float code", &Debug_softfloat},
} }
const debugHelpHeader = `usage: -d arg[,arg]* and arg is <key>[=<value>] const debugHelpHeader = `usage: -d arg[,arg]* and arg is <key>[=<value>]
...@@ -393,6 +395,10 @@ func Main(archInit func(*Arch)) { ...@@ -393,6 +395,10 @@ func Main(archInit func(*Arch)) {
dwarf.EnableLogging(Debug_gendwarfinl != 0) dwarf.EnableLogging(Debug_gendwarfinl != 0)
} }
if Debug_softfloat != 0 {
thearch.SoftFloat = true
}
// enable inlining. for now: // enable inlining. for now:
// default: inlining on. (debug['l'] == 1) // default: inlining on. (debug['l'] == 1)
// -l: inlining off (debug['l'] == 0) // -l: inlining off (debug['l'] == 0)
......
This diff is collapsed.
...@@ -18,20 +18,27 @@ import ( ...@@ -18,20 +18,27 @@ import (
// TODO: move all these tests elsewhere? // TODO: move all these tests elsewhere?
// Perhaps teach test/run.go how to run them with a new action verb. // Perhaps teach test/run.go how to run them with a new action verb.
func runTest(t *testing.T, filename string) { func runTest(t *testing.T, filename string, flags ...string) {
t.Parallel() t.Parallel()
doTest(t, filename, "run") doTest(t, filename, "run", flags...)
} }
func buildTest(t *testing.T, filename string) { func buildTest(t *testing.T, filename string, flags ...string) {
t.Parallel() t.Parallel()
doTest(t, filename, "build") doTest(t, filename, "build", flags...)
} }
func doTest(t *testing.T, filename string, kind string) { func doTest(t *testing.T, filename string, kind string, flags ...string) {
testenv.MustHaveGoBuild(t) testenv.MustHaveGoBuild(t)
gotool := testenv.GoToolPath(t) gotool := testenv.GoToolPath(t)
var stdout, stderr bytes.Buffer var stdout, stderr bytes.Buffer
cmd := exec.Command(gotool, kind, "-gcflags=-d=ssa/check/on", filepath.Join("testdata", filename)) args := []string{kind}
if len(flags) == 0 {
args = append(args, "-gcflags=-d=ssa/check/on")
} else {
args = append(args, flags...)
}
args = append(args, filepath.Join("testdata", filename))
cmd := exec.Command(gotool, args...)
cmd.Stdout = &stdout cmd.Stdout = &stdout
cmd.Stderr = &stderr cmd.Stderr = &stderr
err := cmd.Run() err := cmd.Run()
...@@ -113,6 +120,10 @@ func TestArithmetic(t *testing.T) { runTest(t, "arith.go") } ...@@ -113,6 +120,10 @@ func TestArithmetic(t *testing.T) { runTest(t, "arith.go") }
// TestFP tests that both backends have the same result for floating point expressions. // TestFP tests that both backends have the same result for floating point expressions.
func TestFP(t *testing.T) { runTest(t, "fp.go") } func TestFP(t *testing.T) { runTest(t, "fp.go") }
func TestFPSoftFloat(t *testing.T) {
runTest(t, "fp.go", "-gcflags=-d=softfloat,ssa/check/on")
}
// TestArithmeticBoundary tests boundary results for arithmetic operations. // TestArithmeticBoundary tests boundary results for arithmetic operations.
func TestArithmeticBoundary(t *testing.T) { runTest(t, "arithBoundary.go") } func TestArithmeticBoundary(t *testing.T) { runTest(t, "arithBoundary.go") }
......
...@@ -1165,6 +1165,21 @@ func calcHasCall(n *Node) bool { ...@@ -1165,6 +1165,21 @@ func calcHasCall(n *Node) bool {
// These ops might panic, make sure they are done // These ops might panic, make sure they are done
// before we start marshaling args for a call. See issue 16760. // before we start marshaling args for a call. See issue 16760.
return true return true
// When using soft-float, these ops might be rewritten to function calls
// so we ensure they are evaluated first.
case OADD, OSUB, OMINUS:
if thearch.SoftFloat && (isFloat[n.Type.Etype] || isComplex[n.Type.Etype]) {
return true
}
case OLT, OEQ, ONE, OLE, OGE, OGT:
if thearch.SoftFloat && (isFloat[n.Left.Type.Etype] || isComplex[n.Left.Type.Etype]) {
return true
}
case OCONV:
if thearch.SoftFloat && ((isFloat[n.Type.Etype] || isComplex[n.Type.Etype]) || (isFloat[n.Left.Type.Etype] || isComplex[n.Left.Type.Etype])) {
return true
}
} }
if n.Left != nil && n.Left.HasCall() { if n.Left != nil && n.Left.HasCall() {
......
...@@ -988,6 +988,10 @@ opswitch: ...@@ -988,6 +988,10 @@ opswitch:
n = walkexpr(n, init) n = walkexpr(n, init)
case OCONV, OCONVNOP: case OCONV, OCONVNOP:
if thearch.SoftFloat {
// For the soft-float case, ssa.go handles these conversions.
goto oconv_walkexpr
}
switch thearch.LinkArch.Family { switch thearch.LinkArch.Family {
case sys.ARM, sys.MIPS: case sys.ARM, sys.MIPS:
if n.Left.Type.IsFloat() { if n.Left.Type.IsFloat() {
...@@ -1041,6 +1045,7 @@ opswitch: ...@@ -1041,6 +1045,7 @@ opswitch:
} }
} }
oconv_walkexpr:
n.Left = walkexpr(n.Left, init) n.Left = walkexpr(n.Left, init)
case OANDNOT: case OANDNOT:
......
...@@ -18,6 +18,7 @@ func Init(arch *gc.Arch) { ...@@ -18,6 +18,7 @@ func Init(arch *gc.Arch) {
} }
arch.REGSP = mips.REGSP arch.REGSP = mips.REGSP
arch.MAXWIDTH = (1 << 31) - 1 arch.MAXWIDTH = (1 << 31) - 1
arch.SoftFloat = (objabi.GOMIPS == "softfloat")
arch.ZeroRange = zerorange arch.ZeroRange = zerorange
arch.ZeroAuto = zeroAuto arch.ZeroAuto = zeroAuto
arch.Ginsnop = ginsnop arch.Ginsnop = ginsnop
......
...@@ -203,6 +203,10 @@ func checkFunc(f *Func) { ...@@ -203,6 +203,10 @@ func checkFunc(f *Func) {
} }
} }
if f.RegAlloc != nil && f.Config.SoftFloat && v.Type.IsFloat() {
f.Fatalf("unexpected floating-point type %v", v.LongString())
}
// TODO: check for cycles in values // TODO: check for cycles in values
// TODO: check type // TODO: check type
} }
......
...@@ -344,6 +344,7 @@ var passes = [...]pass{ ...@@ -344,6 +344,7 @@ var passes = [...]pass{
{name: "prove", fn: prove}, {name: "prove", fn: prove},
{name: "loopbce", fn: loopbce}, {name: "loopbce", fn: loopbce},
{name: "decompose builtin", fn: decomposeBuiltIn, required: true}, {name: "decompose builtin", fn: decomposeBuiltIn, required: true},
{name: "softfloat", fn: softfloat, required: true},
{name: "late opt", fn: opt, required: true}, // TODO: split required rules and optimizing rules {name: "late opt", fn: opt, required: true}, // TODO: split required rules and optimizing rules
{name: "generic deadcode", fn: deadcode}, {name: "generic deadcode", fn: deadcode},
{name: "check bce", fn: checkbce}, {name: "check bce", fn: checkbce},
...@@ -413,6 +414,8 @@ var passOrder = [...]constraint{ ...@@ -413,6 +414,8 @@ var passOrder = [...]constraint{
{"generic deadcode", "check bce"}, {"generic deadcode", "check bce"},
// don't run optimization pass until we've decomposed builtin objects // don't run optimization pass until we've decomposed builtin objects
{"decompose builtin", "late opt"}, {"decompose builtin", "late opt"},
// decompose builtin is the last pass that may introduce new float ops, so run softfloat after it
{"decompose builtin", "softfloat"},
// don't layout blocks until critical edges have been removed // don't layout blocks until critical edges have been removed
{"critical", "layout"}, {"critical", "layout"},
// regalloc requires the removal of all critical edges // regalloc requires the removal of all critical edges
......
...@@ -36,6 +36,7 @@ type Config struct { ...@@ -36,6 +36,7 @@ type Config struct {
useSSE bool // Use SSE for non-float operations useSSE bool // Use SSE for non-float operations
nacl bool // GOOS=nacl nacl bool // GOOS=nacl
use387 bool // GO386=387 use387 bool // GO386=387
SoftFloat bool //
NeedsFpScratch bool // No direct move between GP and FP register sets NeedsFpScratch bool // No direct move between GP and FP register sets
BigEndian bool // BigEndian bool //
sparsePhiCutoff uint64 // Sparse phi location algorithm used above this #blocks*#variables score sparsePhiCutoff uint64 // Sparse phi location algorithm used above this #blocks*#variables score
......
// 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 ssa
import "math"
func softfloat(f *Func) {
if !f.Config.SoftFloat {
return
}
newInt64 := false
for _, b := range f.Blocks {
for _, v := range b.Values {
if v.Type.IsFloat() {
switch v.Op {
case OpPhi, OpLoad, OpArg:
if v.Type.Size() == 4 {
v.Type = f.Config.Types.UInt32
} else {
v.Type = f.Config.Types.UInt64
}
case OpConst32F:
v.Op = OpConst32
v.Type = f.Config.Types.UInt32
v.AuxInt = int64(int32(math.Float32bits(i2f32(v.AuxInt))))
case OpConst64F:
v.Op = OpConst64
v.Type = f.Config.Types.UInt64
case OpNeg32F:
arg0 := v.Args[0]
v.reset(OpXor32)
v.Type = f.Config.Types.UInt32
v.AddArg(arg0)
mask := v.Block.NewValue0(v.Pos, OpConst32, v.Type)
mask.AuxInt = -0x80000000
v.AddArg(mask)
case OpNeg64F:
arg0 := v.Args[0]
v.reset(OpXor64)
v.Type = f.Config.Types.UInt64
v.AddArg(arg0)
mask := v.Block.NewValue0(v.Pos, OpConst64, v.Type)
mask.AuxInt = -0x8000000000000000
v.AddArg(mask)
case OpRound32F:
v.Op = OpCopy
v.Type = f.Config.Types.UInt32
case OpRound64F:
v.Op = OpCopy
v.Type = f.Config.Types.UInt64
}
newInt64 = newInt64 || v.Type.Size() == 8
}
}
}
if newInt64 && f.Config.RegSize == 4 {
// On 32bit arch, decompose Uint64 introduced in the switch above.
decomposeBuiltIn(f)
applyRewrite(f, rewriteBlockdec64, rewriteValuedec64)
}
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment