Last active
December 27, 2022 14:06
-
-
Save rroohhh/cb5efdb20b1c52cd7421fd3aafd487a4 to your computer and use it in GitHub Desktop.
building go with mips softfloat support
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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