Skip to content

Instantly share code, notes, and snippets.

@rroohhh
Last active December 27, 2022 14:06
Show Gist options
  • Save rroohhh/cb5efdb20b1c52cd7421fd3aafd487a4 to your computer and use it in GitHub Desktop.
Save rroohhh/cb5efdb20b1c52cd7421fd3aafd487a4 to your computer and use it in GitHub Desktop.
building go with mips softfloat support
# clone git
git clone https://github.com/golang/go
cd go
# checkout the correct commit for the patch
git checkout bad5abf64d76f9c302c084c5f62e6f70920d3c81
# apply the patch
git apply mips_softfloat.patch
# build go
cd src
./all.bash
# some test will fail, ignore that
# the resulting binary lies under ../bin
# to build a binaray with softfloat support use
GOOS=linux GOARCH=mips GOMIPS=softfloat ./go build
diff --git a/src/cmd/compile/internal/gc/go.go b/src/cmd/compile/internal/gc/go.go
index 3f1c4221fe..b8e6008bdc 100644
--- a/src/cmd/compile/internal/gc/go.go
+++ b/src/cmd/compile/internal/gc/go.go
@@ -238,9 +238,10 @@ var autogeneratedPos src.XPos
type Arch struct {
LinkArch *obj.LinkArch
- REGSP int
- MAXWIDTH int64
- Use387 bool // should 386 backend use 387 FP instructions instead of sse2.
+ REGSP int
+ MAXWIDTH int64
+ Use387 bool // should 386 backend use 387 FP instructions instead of sse2.
+ SoftFloat bool
PadFrame func(int64) int64
ZeroRange func(*Progs, *obj.Prog, int64, int64, *uint32) *obj.Prog
diff --git a/src/cmd/compile/internal/gc/main.go b/src/cmd/compile/internal/gc/main.go
index 5760fa7796..84ff3fbb54 100644
--- a/src/cmd/compile/internal/gc/main.go
+++ b/src/cmd/compile/internal/gc/main.go
@@ -45,6 +45,7 @@ var (
Debug_wb int
Debug_pctab string
Debug_locationlist int
+ Debug_softfloat int
)
// Debug arguments.
@@ -71,6 +72,7 @@ var debugtab = []struct {
{"export", "print export data", &Debug_export},
{"pctab", "print named pc-value table", &Debug_pctab},
{"locationlists", "print information about DWARF location list creation", &Debug_locationlist},
+ {"softfloat", "force compiler to emit soft-float code", &Debug_softfloat},
}
const debugHelpHeader = `usage: -d arg[,arg]* and arg is <key>[=<value>]
@@ -381,6 +383,10 @@ func Main(archInit func(*Arch)) {
// set via a -d flag
Ctxt.Debugpcln = Debug_pctab
+ if Debug_softfloat != 0 {
+ thearch.SoftFloat = true
+ }
+
// enable inlining. for now:
// default: inlining on. (debug['l'] == 1)
// -l: inlining off (debug['l'] == 0)
diff --git a/src/cmd/compile/internal/gc/ssa.go b/src/cmd/compile/internal/gc/ssa.go
index a0c77d112b..7ae3eb8e29 100644
--- a/src/cmd/compile/internal/gc/ssa.go
+++ b/src/cmd/compile/internal/gc/ssa.go
@@ -49,6 +49,11 @@ func initssaconfig() {
Float64Ptr: types.NewPtr(types.Types[TFLOAT64]),
BytePtrPtr: types.NewPtr(types.NewPtr(types.Types[TUINT8])),
}
+
+ if thearch.SoftFloat {
+ softfloatInit()
+ }
+
// Generate a few pointer types that are uncommon in the frontend but common in the backend.
// Caching is disabled in the backend, so generating these here avoids allocations.
_ = types.NewPtr(types.Types[TINTER]) // *interface{}
@@ -68,6 +73,7 @@ func initssaconfig() {
if thearch.LinkArch.Name == "386" {
ssaConfig.Set387(thearch.Use387)
}
+ ssaConfig.SoftFloat = thearch.SoftFloat
ssaCaches = make([]ssa.Cache, nBackendWorkers)
// Set up some runtime functions we'll need to call.
@@ -143,6 +149,7 @@ func buildssa(fn *Node, worker int) *ssa.Func {
}()
s.exitCode = fn.Func.Exit
s.panics = map[funcLine]*ssa.Block{}
+ s.softFloat = s.config.SoftFloat
if name == os.Getenv("GOSSAFUNC") {
s.f.HTMLWriter = ssa.NewHTMLWriter("ssa.html", s.f.Frontend(), name)
@@ -273,6 +280,7 @@ type state struct {
cgoUnsafeArgs bool
hasdefer bool // whether the function contains a defer statement
+ softFloat bool
}
type funcLine struct {
@@ -506,6 +514,25 @@ func (s *state) constOffPtrSP(t *types.Type, c int64) *ssa.Value {
return s.f.ConstOffPtrSP(s.peekPos(), t, c, s.sp)
}
+// newValueOrSfCall* are wrappers around newValue*, which may create a call to a
+// soft-float runtime function instead (when emitting soft-float code).
+func (s *state) newValueOrSfCall1(op ssa.Op, t *types.Type, arg *ssa.Value) *ssa.Value {
+ if s.softFloat {
+ if c, ok := s.sfcall(op, arg); ok {
+ return c
+ }
+ }
+ return s.newValue1(op, t, arg)
+}
+func (s *state) newValueOrSfCall2(op ssa.Op, t *types.Type, arg0, arg1 *ssa.Value) *ssa.Value {
+ if s.softFloat {
+ if c, ok := s.sfcall(op, arg0, arg1); ok {
+ return c
+ }
+ }
+ return s.newValue2(op, t, arg0, arg1)
+}
+
// stmtList converts the statement list n to SSA and adds it to s.
func (s *state) stmtList(l Nodes) {
for _, n := range l.Slice() {
@@ -1624,18 +1651,18 @@ func (s *state) expr(n *Node) *ssa.Value {
if ft.IsFloat() || tt.IsFloat() {
conv, ok := fpConvOpToSSA[twoTypes{s.concreteEtype(ft), s.concreteEtype(tt)}]
- if s.config.RegSize == 4 && thearch.LinkArch.Family != sys.MIPS {
+ if s.config.RegSize == 4 && thearch.LinkArch.Family != sys.MIPS && !s.softFloat {
if conv1, ok1 := fpConvOpToSSA32[twoTypes{s.concreteEtype(ft), s.concreteEtype(tt)}]; ok1 {
conv = conv1
}
}
- if thearch.LinkArch.Family == sys.ARM64 {
+ if thearch.LinkArch.Family == sys.ARM64 || s.softFloat {
if conv1, ok1 := uint64fpConvOpToSSA[twoTypes{s.concreteEtype(ft), s.concreteEtype(tt)}]; ok1 {
conv = conv1
}
}
- if thearch.LinkArch.Family == sys.MIPS {
+ if thearch.LinkArch.Family == sys.MIPS && !s.softFloat {
if ft.Size() == 4 && ft.IsInteger() && !ft.IsSigned() {
// tt is float32 or float64, and ft is also unsigned
if tt.Size() == 4 {
@@ -1666,12 +1693,12 @@ func (s *state) expr(n *Node) *ssa.Value {
if op2 == ssa.OpCopy {
return x
}
- return s.newValue1(op2, n.Type, x)
+ return s.newValueOrSfCall1(op2, n.Type, x)
}
if op2 == ssa.OpCopy {
- return s.newValue1(op1, n.Type, x)
+ return s.newValueOrSfCall1(op1, n.Type, x)
}
- return s.newValue1(op2, n.Type, s.newValue1(op1, types.Types[it], x))
+ return s.newValueOrSfCall1(op2, n.Type, s.newValueOrSfCall1(op1, types.Types[it], x))
}
// Tricky 64-bit unsigned cases.
if ft.IsInteger() {
@@ -1716,8 +1743,8 @@ func (s *state) expr(n *Node) *ssa.Value {
ftp := floatForComplex(ft)
ttp := floatForComplex(tt)
return s.newValue2(ssa.OpComplexMake, tt,
- s.newValue1(op, ttp, s.newValue1(ssa.OpComplexReal, ftp, x)),
- s.newValue1(op, ttp, s.newValue1(ssa.OpComplexImag, ftp, x)))
+ s.newValueOrSfCall1(op, ttp, s.newValue1(ssa.OpComplexReal, ftp, x)),
+ s.newValueOrSfCall1(op, ttp, s.newValue1(ssa.OpComplexImag, ftp, x)))
}
s.Fatalf("unhandled OCONV %s -> %s", n.Left.Type.Etype, n.Type.Etype)
@@ -1734,8 +1761,8 @@ func (s *state) expr(n *Node) *ssa.Value {
if n.Left.Type.IsComplex() {
pt := floatForComplex(n.Left.Type)
op := s.ssaOp(OEQ, pt)
- r := s.newValue2(op, types.Types[TBOOL], s.newValue1(ssa.OpComplexReal, pt, a), s.newValue1(ssa.OpComplexReal, pt, b))
- i := s.newValue2(op, types.Types[TBOOL], s.newValue1(ssa.OpComplexImag, pt, a), s.newValue1(ssa.OpComplexImag, pt, b))
+ r := s.newValueOrSfCall2(op, types.Types[TBOOL], s.newValue1(ssa.OpComplexReal, pt, a), s.newValue1(ssa.OpComplexReal, pt, b))
+ i := s.newValueOrSfCall2(op, types.Types[TBOOL], s.newValue1(ssa.OpComplexImag, pt, a), s.newValue1(ssa.OpComplexImag, pt, b))
c := s.newValue2(ssa.OpAndB, types.Types[TBOOL], r, i)
switch n.Op {
case OEQ:
@@ -1746,6 +1773,9 @@ func (s *state) expr(n *Node) *ssa.Value {
s.Fatalf("ordered complex compare %v", n.Op)
}
}
+ if n.Left.Type.IsFloat() {
+ return s.newValueOrSfCall2(s.ssaOp(n.Op, n.Left.Type), types.Types[TBOOL], a, b)
+ }
return s.newValue2(s.ssaOp(n.Op, n.Left.Type), types.Types[TBOOL], a, b)
case OMUL:
a := s.expr(n.Left)
@@ -1763,22 +1793,27 @@ func (s *state) expr(n *Node) *ssa.Value {
bimag := s.newValue1(ssa.OpComplexImag, pt, b)
if pt != wt { // Widen for calculation
- areal = s.newValue1(ssa.OpCvt32Fto64F, wt, areal)
- breal = s.newValue1(ssa.OpCvt32Fto64F, wt, breal)
- aimag = s.newValue1(ssa.OpCvt32Fto64F, wt, aimag)
- bimag = s.newValue1(ssa.OpCvt32Fto64F, wt, bimag)
+ areal = s.newValueOrSfCall1(ssa.OpCvt32Fto64F, wt, areal)
+ breal = s.newValueOrSfCall1(ssa.OpCvt32Fto64F, wt, breal)
+ aimag = s.newValueOrSfCall1(ssa.OpCvt32Fto64F, wt, aimag)
+ bimag = s.newValueOrSfCall1(ssa.OpCvt32Fto64F, wt, bimag)
}
- xreal := s.newValue2(subop, wt, s.newValue2(mulop, wt, areal, breal), s.newValue2(mulop, wt, aimag, bimag))
- ximag := s.newValue2(addop, wt, s.newValue2(mulop, wt, areal, bimag), s.newValue2(mulop, wt, aimag, breal))
+ xreal := s.newValueOrSfCall2(subop, wt, s.newValueOrSfCall2(mulop, wt, areal, breal), s.newValueOrSfCall2(mulop, wt, aimag, bimag))
+ ximag := s.newValueOrSfCall2(addop, wt, s.newValueOrSfCall2(mulop, wt, areal, bimag), s.newValueOrSfCall2(mulop, wt, aimag, breal))
if pt != wt { // Narrow to store back
- xreal = s.newValue1(ssa.OpCvt64Fto32F, pt, xreal)
- ximag = s.newValue1(ssa.OpCvt64Fto32F, pt, ximag)
+ xreal = s.newValueOrSfCall1(ssa.OpCvt64Fto32F, pt, xreal)
+ ximag = s.newValueOrSfCall1(ssa.OpCvt64Fto32F, pt, ximag)
}
return s.newValue2(ssa.OpComplexMake, n.Type, xreal, ximag)
}
+
+ if n.Type.IsFloat() {
+ return s.newValueOrSfCall2(s.ssaOp(n.Op, n.Type), a.Type, a, b)
+ }
+
return s.newValue2(s.ssaOp(n.Op, n.Type), a.Type, a, b)
case ODIV:
@@ -1801,31 +1836,31 @@ func (s *state) expr(n *Node) *ssa.Value {
bimag := s.newValue1(ssa.OpComplexImag, pt, b)
if pt != wt { // Widen for calculation
- areal = s.newValue1(ssa.OpCvt32Fto64F, wt, areal)
- breal = s.newValue1(ssa.OpCvt32Fto64F, wt, breal)
- aimag = s.newValue1(ssa.OpCvt32Fto64F, wt, aimag)
- bimag = s.newValue1(ssa.OpCvt32Fto64F, wt, bimag)
+ areal = s.newValueOrSfCall1(ssa.OpCvt32Fto64F, wt, areal)
+ breal = s.newValueOrSfCall1(ssa.OpCvt32Fto64F, wt, breal)
+ aimag = s.newValueOrSfCall1(ssa.OpCvt32Fto64F, wt, aimag)
+ bimag = s.newValueOrSfCall1(ssa.OpCvt32Fto64F, wt, bimag)
}
- denom := s.newValue2(addop, wt, s.newValue2(mulop, wt, breal, breal), s.newValue2(mulop, wt, bimag, bimag))
- xreal := s.newValue2(addop, wt, s.newValue2(mulop, wt, areal, breal), s.newValue2(mulop, wt, aimag, bimag))
- ximag := s.newValue2(subop, wt, s.newValue2(mulop, wt, aimag, breal), s.newValue2(mulop, wt, areal, bimag))
+ denom := s.newValueOrSfCall2(addop, wt, s.newValueOrSfCall2(mulop, wt, breal, breal), s.newValueOrSfCall2(mulop, wt, bimag, bimag))
+ xreal := s.newValueOrSfCall2(addop, wt, s.newValueOrSfCall2(mulop, wt, areal, breal), s.newValueOrSfCall2(mulop, wt, aimag, bimag))
+ ximag := s.newValueOrSfCall2(subop, wt, s.newValueOrSfCall2(mulop, wt, aimag, breal), s.newValueOrSfCall2(mulop, wt, areal, bimag))
// TODO not sure if this is best done in wide precision or narrow
// Double-rounding might be an issue.
// Note that the pre-SSA implementation does the entire calculation
// in wide format, so wide is compatible.
- xreal = s.newValue2(divop, wt, xreal, denom)
- ximag = s.newValue2(divop, wt, ximag, denom)
+ xreal = s.newValueOrSfCall2(divop, wt, xreal, denom)
+ ximag = s.newValueOrSfCall2(divop, wt, ximag, denom)
if pt != wt { // Narrow to store back
- xreal = s.newValue1(ssa.OpCvt64Fto32F, pt, xreal)
- ximag = s.newValue1(ssa.OpCvt64Fto32F, pt, ximag)
+ xreal = s.newValueOrSfCall1(ssa.OpCvt64Fto32F, pt, xreal)
+ ximag = s.newValueOrSfCall1(ssa.OpCvt64Fto32F, pt, ximag)
}
return s.newValue2(ssa.OpComplexMake, n.Type, xreal, ximag)
}
if n.Type.IsFloat() {
- return s.newValue2(s.ssaOp(n.Op, n.Type), a.Type, a, b)
+ return s.newValueOrSfCall2(s.ssaOp(n.Op, n.Type), a.Type, a, b)
}
return s.intDivide(n, a, b)
case OMOD:
@@ -1839,8 +1874,11 @@ func (s *state) expr(n *Node) *ssa.Value {
pt := floatForComplex(n.Type)
op := s.ssaOp(n.Op, pt)
return s.newValue2(ssa.OpComplexMake, n.Type,
- s.newValue2(op, pt, s.newValue1(ssa.OpComplexReal, pt, a), s.newValue1(ssa.OpComplexReal, pt, b)),
- s.newValue2(op, pt, s.newValue1(ssa.OpComplexImag, pt, a), s.newValue1(ssa.OpComplexImag, pt, b)))
+ s.newValueOrSfCall2(op, pt, s.newValue1(ssa.OpComplexReal, pt, a), s.newValue1(ssa.OpComplexReal, pt, b)),
+ s.newValueOrSfCall2(op, pt, s.newValue1(ssa.OpComplexImag, pt, a), s.newValue1(ssa.OpComplexImag, pt, b)))
+ }
+ if n.Type.IsFloat() {
+ return s.newValueOrSfCall2(s.ssaOp(n.Op, n.Type), a.Type, a, b)
}
return s.newValue2(s.ssaOp(n.Op, n.Type), a.Type, a, b)
case OAND, OOR, OXOR:
@@ -2500,6 +2538,79 @@ const (
callGo
)
+type sfRtCallDef struct {
+ rtfn *obj.LSym
+ rtype types.EType
+}
+
+var softFloatOps map[ssa.Op]sfRtCallDef
+
+func softfloatInit() {
+ // Some of these operations get transformed by sfcall.
+ softFloatOps = map[ssa.Op]sfRtCallDef{
+ ssa.OpAdd32F: sfRtCallDef{sysfunc("fadd32"), TFLOAT32},
+ ssa.OpAdd64F: sfRtCallDef{sysfunc("fadd64"), TFLOAT64},
+ ssa.OpSub32F: sfRtCallDef{sysfunc("fadd32"), TFLOAT32},
+ ssa.OpSub64F: sfRtCallDef{sysfunc("fadd64"), TFLOAT64},
+ ssa.OpMul32F: sfRtCallDef{sysfunc("fmul32"), TFLOAT32},
+ ssa.OpMul64F: sfRtCallDef{sysfunc("fmul64"), TFLOAT64},
+ ssa.OpDiv32F: sfRtCallDef{sysfunc("fdiv32"), TFLOAT32},
+ ssa.OpDiv64F: sfRtCallDef{sysfunc("fdiv64"), TFLOAT64},
+
+ ssa.OpEq64F: sfRtCallDef{sysfunc("feq64"), TBOOL},
+ ssa.OpEq32F: sfRtCallDef{sysfunc("feq32"), TBOOL},
+ ssa.OpNeq64F: sfRtCallDef{sysfunc("feq64"), TBOOL},
+ ssa.OpNeq32F: sfRtCallDef{sysfunc("feq32"), TBOOL},
+ ssa.OpLess64F: sfRtCallDef{sysfunc("fgt64"), TBOOL},
+ ssa.OpLess32F: sfRtCallDef{sysfunc("fgt32"), TBOOL},
+ ssa.OpGreater64F: sfRtCallDef{sysfunc("fgt64"), TBOOL},
+ ssa.OpGreater32F: sfRtCallDef{sysfunc("fgt32"), TBOOL},
+ ssa.OpLeq64F: sfRtCallDef{sysfunc("fge64"), TBOOL},
+ ssa.OpLeq32F: sfRtCallDef{sysfunc("fge32"), TBOOL},
+ ssa.OpGeq64F: sfRtCallDef{sysfunc("fge64"), TBOOL},
+ ssa.OpGeq32F: sfRtCallDef{sysfunc("fge32"), TBOOL},
+
+ ssa.OpCvt32to32F: sfRtCallDef{sysfunc("fint32to32"), TFLOAT32},
+ ssa.OpCvt32Fto32: sfRtCallDef{sysfunc("f32toint32"), TINT32},
+ ssa.OpCvt64to32F: sfRtCallDef{sysfunc("fint64to32"), TFLOAT32},
+ ssa.OpCvt32Fto64: sfRtCallDef{sysfunc("f32toint64"), TINT64},
+ ssa.OpCvt64Uto32F: sfRtCallDef{sysfunc("fuint64to32"), TFLOAT32},
+ ssa.OpCvt32Fto64U: sfRtCallDef{sysfunc("f32touint64"), TUINT64},
+ ssa.OpCvt32to64F: sfRtCallDef{sysfunc("fint32to64"), TFLOAT64},
+ ssa.OpCvt64Fto32: sfRtCallDef{sysfunc("f64toint32"), TINT32},
+ ssa.OpCvt64to64F: sfRtCallDef{sysfunc("fint64to64"), TFLOAT64},
+ ssa.OpCvt64Fto64: sfRtCallDef{sysfunc("f64toint64"), TINT64},
+ ssa.OpCvt64Uto64F: sfRtCallDef{sysfunc("fuint64to64"), TFLOAT64},
+ ssa.OpCvt64Fto64U: sfRtCallDef{sysfunc("f64touint64"), TUINT64},
+ ssa.OpCvt32Fto64F: sfRtCallDef{sysfunc("f32to64"), TFLOAT64},
+ ssa.OpCvt64Fto32F: sfRtCallDef{sysfunc("f64to32"), TFLOAT32},
+ }
+}
+
+// TODO: do not emit sfcall if operation can be optimized to constant in later
+// opt phase
+func (s *state) sfcall(op ssa.Op, args ...*ssa.Value) (*ssa.Value, bool) {
+ if callDef, ok := softFloatOps[op]; ok {
+ switch op {
+ case ssa.OpLess32F,
+ ssa.OpLess64F,
+ ssa.OpLeq32F,
+ ssa.OpLeq64F:
+ args[0], args[1] = args[1], args[0]
+ case ssa.OpSub32F,
+ ssa.OpSub64F:
+ args[1] = s.newValue1(s.ssaOp(OMINUS, types.Types[callDef.rtype]), args[1].Type, args[1])
+ }
+
+ result := s.rtcall(callDef.rtfn, true, []*types.Type{types.Types[callDef.rtype]}, args...)[0]
+ if op == ssa.OpNeq32F || op == ssa.OpNeq64F {
+ result = s.newValue1(ssa.OpNot, result.Type, result)
+ }
+ return result, true
+ }
+ return nil, false
+}
+
var intrinsics map[intrinsicKey]intrinsicBuilder
// An intrinsicBuilder converts a call node n into an ssa value that
@@ -3005,6 +3116,12 @@ func findIntrinsic(sym *types.Sym) intrinsicBuilder {
// We can't intrinsify them.
return nil
}
+ // Skip intrinsifying math functions (which may contain hard-float
+ // instructions) when soft-float
+ if thearch.SoftFloat && pkg == "math" {
+ return nil
+ }
+
fn := sym.Name
return intrinsics[intrinsicKey{thearch.LinkArch.Arch, pkg, fn}]
}
diff --git a/src/cmd/compile/internal/gc/subr.go b/src/cmd/compile/internal/gc/subr.go
index 08658af603..5b8504da68 100644
--- a/src/cmd/compile/internal/gc/subr.go
+++ b/src/cmd/compile/internal/gc/subr.go
@@ -1165,6 +1165,24 @@ func updateHasCall(n *Node) {
// before we start marshaling args for a call. See issue 16760.
b = true
goto out
+
+ // These ops might be rewritten to function calls when using soft-float
+ // so we ensure they are evaluated first.
+ case OADD, OSUB, OMINUS:
+ if thearch.SoftFloat && (isFloat[n.Type.Etype] || isComplex[n.Type.Etype]) {
+ b = true
+ goto out
+ }
+ case OLT, OEQ, ONE, OLE, OGE, OGT:
+ if thearch.SoftFloat && (isFloat[n.Left.Type.Etype] || isComplex[n.Left.Type.Etype]) {
+ b = true
+ goto out
+ }
+ case OCONV:
+ if thearch.SoftFloat && ((isFloat[n.Type.Etype] || isComplex[n.Type.Etype]) || (isFloat[n.Left.Type.Etype] || isComplex[n.Left.Type.Etype])) {
+ b = true
+ goto out
+ }
}
if n.Left != nil && n.Left.HasCall() {
diff --git a/src/cmd/compile/internal/gc/walk.go b/src/cmd/compile/internal/gc/walk.go
index 48e6319e43..6035e42b76 100644
--- a/src/cmd/compile/internal/gc/walk.go
+++ b/src/cmd/compile/internal/gc/walk.go
@@ -990,6 +990,10 @@ opswitch:
n = walkexpr(n, init)
case OCONV, OCONVNOP:
+ if thearch.SoftFloat {
+ // For the soft-float case, ssa.go handles these conversions.
+ goto oconv_walkexpr
+ }
if thearch.LinkArch.Family == sys.ARM || thearch.LinkArch.Family == sys.MIPS {
if n.Left.Type.IsFloat() {
if n.Type.Etype == TINT64 {
@@ -1049,6 +1053,7 @@ opswitch:
}
}
+ oconv_walkexpr:
n.Left = walkexpr(n.Left, init)
case OANDNOT:
diff --git a/src/cmd/compile/internal/mips/galign.go b/src/cmd/compile/internal/mips/galign.go
index 77ec78aabf..5435f10361 100644
--- a/src/cmd/compile/internal/mips/galign.go
+++ b/src/cmd/compile/internal/mips/galign.go
@@ -9,6 +9,7 @@ import (
"cmd/compile/internal/ssa"
"cmd/internal/obj/mips"
"cmd/internal/objabi"
+ "strings"
)
func Init(arch *gc.Arch) {
@@ -18,6 +19,7 @@ func Init(arch *gc.Arch) {
}
arch.REGSP = mips.REGSP
arch.MAXWIDTH = (1 << 31) - 1
+ arch.SoftFloat = strings.Contains(objabi.GOMIPS, "softfloat")
arch.ZeroRange = zerorange
arch.ZeroAuto = zeroAuto
arch.Ginsnop = ginsnop
diff --git a/src/cmd/compile/internal/ssa/check.go b/src/cmd/compile/internal/ssa/check.go
index e8a16aec70..50134743dd 100644
--- a/src/cmd/compile/internal/ssa/check.go
+++ b/src/cmd/compile/internal/ssa/check.go
@@ -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 type
}
diff --git a/src/cmd/compile/internal/ssa/compile.go b/src/cmd/compile/internal/ssa/compile.go
index 00a4b04ce5..4ef9f34dca 100644
--- a/src/cmd/compile/internal/ssa/compile.go
+++ b/src/cmd/compile/internal/ssa/compile.go
@@ -345,6 +345,7 @@ var passes = [...]pass{
{name: "loopbce", fn: loopbce},
{name: "decompose builtin", fn: decomposeBuiltIn, required: true},
{name: "dec", fn: dec, required: true},
+ {name: "softfloat", fn: softfloat, required: true},
{name: "late opt", fn: opt, required: true}, // TODO: split required rules and optimizing rules
{name: "generic deadcode", fn: deadcode},
{name: "check bce", fn: checkbce},
@@ -414,6 +415,8 @@ var passOrder = [...]constraint{
{"generic deadcode", "check bce"},
// don't run optimization pass until we've decomposed builtin objects
{"decompose builtin", "late opt"},
+ // dec is the last pass that may introduce new float ops, so run softfloat after it
+ {"dec", "softfloat"},
// don't layout blocks until critical edges have been removed
{"critical", "layout"},
// regalloc requires the removal of all critical edges
diff --git a/src/cmd/compile/internal/ssa/config.go b/src/cmd/compile/internal/ssa/config.go
index c352219523..60acd7591d 100644
--- a/src/cmd/compile/internal/ssa/config.go
+++ b/src/cmd/compile/internal/ssa/config.go
@@ -36,6 +36,7 @@ type Config struct {
useSSE bool // Use SSE for non-float operations
nacl bool // GOOS=nacl
use387 bool // GO386=387
+ SoftFloat bool //
NeedsFpScratch bool // No direct move between GP and FP register sets
BigEndian bool //
sparsePhiCutoff uint64 // Sparse phi location algorithm used above this #blocks*#variables score
diff --git a/src/cmd/compile/internal/ssa/softfloat.go b/src/cmd/compile/internal/ssa/softfloat.go
new file mode 100644
index 0000000000..39829b046c
--- /dev/null
+++ b/src/cmd/compile/internal/ssa/softfloat.go
@@ -0,0 +1,66 @@
+// 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)
+ }
+
+}
diff --git a/src/cmd/dist/build.go b/src/cmd/dist/build.go
index 89474d7678..6b759a1051 100644
--- a/src/cmd/dist/build.go
+++ b/src/cmd/dist/build.go
@@ -28,6 +28,7 @@ var (
goos string
goarm string
go386 string
+ gomips string
goroot string
goroot_final string
goextlinkenabled string
@@ -136,6 +137,12 @@ func xinit() {
}
go386 = b
+ b = os.Getenv("GOMIPS")
+ if b == "" {
+ b = "r1"
+ }
+ gomips = b
+
if p := pathf("%s/src/all.bash", goroot); !isfile(p) {
fatalf("$GOROOT is not set correctly or not exported\n"+
"\tGOROOT=%s\n"+
@@ -220,6 +227,7 @@ func xinit() {
os.Setenv("GOHOSTARCH", gohostarch)
os.Setenv("GOHOSTOS", gohostos)
os.Setenv("GOOS", goos)
+ os.Setenv("GOMIPS", gomips)
os.Setenv("GOROOT", goroot)
os.Setenv("GOROOT_FINAL", goroot_final)
@@ -723,6 +731,14 @@ func install(dir string) {
"-D", "GOOS_GOARCH_" + goos + "_" + goarch,
}
+ if goarch == "mips" || goarch == "mipsle" {
+ // Define GOMIPS_softfloat, GOMIPS_rN from gomips value.
+ if strings.Contains(gomips, "softfloat") {
+ compile = append(compile, "-D", "GOMIPS_softfloat")
+ }
+ compile = append(compile, "-D", "GOMIPS_"+strings.TrimSuffix(gomips, "softfloat"))
+ }
+
doclean := true
b := pathf("%s/%s", workdir, filepath.Base(p))
@@ -970,6 +986,9 @@ func cmdenv() {
if goarch == "386" {
xprintf(format, "GO386", go386)
}
+ if goarch == "mips" || goarch == "mipsle" {
+ xprintf(format, "GOMIPS", gomips)
+ }
if *path {
sep := ":"
diff --git a/src/cmd/dist/buildruntime.go b/src/cmd/dist/buildruntime.go
index 8dd095b82d..2f10fd0237 100644
--- a/src/cmd/dist/buildruntime.go
+++ b/src/cmd/dist/buildruntime.go
@@ -46,6 +46,7 @@ func mkzversion(dir, file string) {
// const defaultGOROOT = <goroot>
// const defaultGO386 = <go386>
// const defaultGOARM = <goarm>
+// const defaultGOMIPS = <gomips>
// const defaultGOOS = runtime.GOOS
// const defaultGOARCH = runtime.GOARCH
// const defaultGO_EXTLINK_ENABLED = <goextlinkenabled>
@@ -73,6 +74,7 @@ func mkzbootstrap(file string) {
fmt.Fprintf(&buf, "const defaultGOROOT = `%s`\n", goroot_final)
fmt.Fprintf(&buf, "const defaultGO386 = `%s`\n", go386)
fmt.Fprintf(&buf, "const defaultGOARM = `%s`\n", goarm)
+ fmt.Fprintf(&buf, "const defaultGOMIPS = `%s`\n", gomips)
fmt.Fprintf(&buf, "const defaultGOOS = runtime.GOOS\n")
fmt.Fprintf(&buf, "const defaultGOARCH = runtime.GOARCH\n")
fmt.Fprintf(&buf, "const defaultGO_EXTLINK_ENABLED = `%s`\n", goextlinkenabled)
diff --git a/src/cmd/dist/test.go b/src/cmd/dist/test.go
index ae7f25cad9..4d4750e978 100644
--- a/src/cmd/dist/test.go
+++ b/src/cmd/dist/test.go
@@ -649,6 +649,18 @@ func (t *tester) registerTests() {
})
}
}
+
+ if !t.compileOnly && goos != "android" && !t.iOS() {
+ t.tests = append(t.tests, distTest{
+ name: "softfloat",
+ heading: "softfloat math",
+ fn: func(dt *distTest) error {
+ t.addCmd(dt, "src", "go", "test", "-a", "-short", t.timeout(300), t.tags(), "math", "-gcflags=-d=softfloat,ssa/check/on", "-installsuffix=softfloat")
+ return nil
+ },
+ })
+ }
+
if goos != "nacl" && goos != "android" && !t.iOS() {
t.tests = append(t.tests, distTest{
name: "api",
diff --git a/src/cmd/go/alldocs.go b/src/cmd/go/alldocs.go
index 7de7e36e25..79acd69b8c 100644
--- a/src/cmd/go/alldocs.go
+++ b/src/cmd/go/alldocs.go
@@ -1134,6 +1134,10 @@
// GO386
// For GOARCH=386, the floating point instruction set.
// Valid values are 387, sse2.
+// GOMIPS
+// For GOARCH=mips{,le}, the MIPS instruction set for which to compile and
+// whether the target has FPU.
+// Valid values are r1, r2, r1softfloat, r2softfloat, softfloat.
//
// Special-purpose environment variables:
//
diff --git a/src/cmd/go/internal/cfg/cfg.go b/src/cmd/go/internal/cfg/cfg.go
index b50074f0af..11369488b4 100644
--- a/src/cmd/go/internal/cfg/cfg.go
+++ b/src/cmd/go/internal/cfg/cfg.go
@@ -80,8 +80,9 @@ var (
GOROOTsrc = filepath.Join(GOROOT, "src")
// Used in envcmd.MkEnv and build ID computations.
- GOARM = fmt.Sprint(objabi.GOARM)
- GO386 = objabi.GO386
+ GOARM = fmt.Sprint(objabi.GOARM)
+ GO386 = objabi.GO386
+ GOMIPS = objabi.GOMIPS
)
// Update build context to use our computed GOROOT.
diff --git a/src/cmd/go/internal/envcmd/env.go b/src/cmd/go/internal/envcmd/env.go
index 43d4334f06..7a42ca8e2c 100644
--- a/src/cmd/go/internal/envcmd/env.go
+++ b/src/cmd/go/internal/envcmd/env.go
@@ -71,6 +71,8 @@ func MkEnv() []cfg.EnvVar {
env = append(env, cfg.EnvVar{Name: "GOARM", Value: cfg.GOARM})
case "386":
env = append(env, cfg.EnvVar{Name: "GO386", Value: cfg.GO386})
+ case "mips", "mipsle":
+ env = append(env, cfg.EnvVar{Name: "GOMIPS", Value: cfg.GOMIPS})
}
cmd := b.GccCmd(".")
diff --git a/src/cmd/go/internal/help/helpdoc.go b/src/cmd/go/internal/help/helpdoc.go
index 508ff60862..655936e86d 100644
--- a/src/cmd/go/internal/help/helpdoc.go
+++ b/src/cmd/go/internal/help/helpdoc.go
@@ -505,6 +505,10 @@ Architecture-specific environment variables:
GO386
For GOARCH=386, the floating point instruction set.
Valid values are 387, sse2.
+ GOMIPS
+ For GOARCH=mips{,le}, the MIPS instruction set for which to compile and
+ whether the target has FPU.
+ Valid values are r1, r2, r1softfloat, r2softfloat, softfloat.
Special-purpose environment variables:
diff --git a/src/cmd/go/internal/load/pkg.go b/src/cmd/go/internal/load/pkg.go
index d10c6974c4..492d141210 100644
--- a/src/cmd/go/internal/load/pkg.go
+++ b/src/cmd/go/internal/load/pkg.go
@@ -1725,6 +1725,8 @@ func computeBuildID(p *Package) {
fmt.Fprintf(h, "GOARM=%s\n", cfg.GOARM)
case "386":
fmt.Fprintf(h, "GO386=%s\n", cfg.GO386)
+ case "mips", "mipsle":
+ fmt.Fprintf(h, "GOMIPS=%s\n", cfg.GOMIPS)
}
}
diff --git a/src/cmd/go/internal/work/build.go b/src/cmd/go/internal/work/build.go
index ce5c71ae5a..6390fb2630 100644
--- a/src/cmd/go/internal/work/build.go
+++ b/src/cmd/go/internal/work/build.go
@@ -2428,6 +2428,13 @@ func (gcToolchain) asm(b *Builder, p *load.Package, objdir string, sfiles []stri
}
}
}
+ if cfg.Goarch == "mips" || cfg.Goarch == "mipsle" {
+ // Define GOMIPS_softfloat, GOMIPS_rN from cfg.GOMIPS value.
+ if strings.Contains(cfg.GOMIPS, "softfloat") {
+ args = append(args, "-D", "GOMIPS_softfloat")
+ }
+ args = append(args, "-D", "GOMIPS_"+strings.TrimSuffix(cfg.GOMIPS, "softfloat"))
+ }
var ofiles []string
for _, sfile := range sfiles {
ofile := objdir + sfile[:len(sfile)-len(".s")] + ".o"
diff --git a/src/cmd/internal/objabi/util.go b/src/cmd/internal/objabi/util.go
index 1da05021f5..ed0036169a 100644
--- a/src/cmd/internal/objabi/util.go
+++ b/src/cmd/internal/objabi/util.go
@@ -24,6 +24,7 @@ var (
GOOS = envOr("GOOS", defaultGOOS)
GO386 = envOr("GO386", defaultGO386)
GOARM = goarm()
+ GOMIPS = gomips()
Version = version
)
@@ -41,6 +42,17 @@ func goarm() int {
panic("unreachable")
}
+func gomips() string {
+ switch v := envOr("GOMIPS", defaultGOMIPS); v {
+ case "softfloat":
+ return "r1softfloat"
+ case "r1", "r2", "r1softfloat", "r2softfloat":
+ return v
+ }
+ log.Fatalf("Invalid GOMIPS value. Must be r1, r2, r1softfloat, r2softfloat or softfloat.")
+ panic("unreachable")
+}
+
func Getgoextlinkenabled() string {
return envOr("GO_EXTLINK_ENABLED", defaultGO_EXTLINK_ENABLED)
}
diff --git a/src/runtime/softfloat64.go b/src/runtime/softfloat64.go
index 1678e8f9f1..62ace5def3 100644
--- a/src/runtime/softfloat64.go
+++ b/src/runtime/softfloat64.go
@@ -483,3 +483,115 @@ again2:
return q1*b + q0, (un21*b + un0 - q0*v) >> s
}
+
+func fadd32(x, y uint32) uint32 {
+ return f64to32(fadd64(f32to64(x), f32to64(y)))
+}
+
+func fmul32(x, y uint32) uint32 {
+ return f64to32(fmul64(f32to64(x), f32to64(y)))
+}
+
+func fdiv32(x, y uint32) uint32 {
+ return f64to32(fdiv64(f32to64(x), f32to64(y)))
+}
+
+func feq32(x, y uint32) bool {
+ cmp, nan := fcmp64(f32to64(x), f32to64(y))
+ return !(cmp != 0 || nan)
+}
+
+func fgt32(x, y uint32) bool {
+ cmp, nan := fcmp64(f32to64(x), f32to64(y))
+ return !(cmp < 1 || nan)
+}
+
+func fge32(x, y uint32) bool {
+ cmp, nan := fcmp64(f32to64(x), f32to64(y))
+ return !(cmp < 0 || nan)
+}
+
+func feq64(x, y uint64) bool {
+ cmp, nan := fcmp64(x, y)
+ return !(cmp != 0 || nan)
+}
+
+func fgt64(x, y uint64) bool {
+ cmp, nan := fcmp64(x, y)
+ return !(cmp < 1 || nan)
+}
+
+func fge64(x, y uint64) bool {
+ cmp, nan := fcmp64(x, y)
+ return !(cmp < 0 || nan)
+}
+
+func fint32to32(x int32) uint32 {
+ return f64to32(fintto64(int64(x)))
+}
+
+func fint32to64(x int32) uint64 {
+ return fintto64(int64(x))
+}
+
+func fint64to32(x int64) uint32 {
+ return f64to32(fintto64(x))
+}
+
+func fint64to64(x int64) uint64 {
+ return fintto64(x)
+}
+
+func f32toint32(x uint32) int32 {
+ val, _ := f64toint(f32to64(x))
+ return int32(val)
+}
+
+func f32toint64(x uint32) int64 {
+ val, _ := f64toint(f32to64(x))
+ return val
+}
+
+func f64toint32(x uint64) int32 {
+ val, _ := f64toint(x)
+ return int32(val)
+}
+
+func f64toint64(x uint64) int64 {
+ val, _ := f64toint(x)
+ return val
+}
+
+func f64touint64(x float64) uint64 {
+ if x < float64(1<<63) {
+ return uint64(int64(x))
+ }
+ y := x - float64(1<<63)
+ z := uint64(int64(y))
+ return z | (1 << 63)
+}
+
+func f32touint64(x float32) uint64 {
+ if x < float32(1<<63) {
+ return uint64(int64(x))
+ }
+ y := x - float32(1<<63)
+ z := uint64(int64(y))
+ return z | (1 << 63)
+}
+
+func fuint64to64(x uint64) float64 {
+ if int64(x) >= 0 {
+ return float64(int64(x))
+ }
+ // See ../cmd/compile/internal/gc/ssa.go:uint64Tofloat
+ y := x & 1
+ z := x >> 1
+ z = z | y
+ r := float64(int64(z))
+ return r + r
+}
+
+func fuint64to32(x uint64) float32 {
+ return float32(fuint64to64(x))
+}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment