Skip to content

Commit 9f44f49

Browse files
committed
mips: add GOMIPS=softfloat support
Previously, the compiler would default to hardfloat. This is not supported by some MIPS CPUs. This took me much longer than it should have because of a quirk in the LLVM Mips backend: if the target-features string is not set (like during LTO), the Mips backend picks the first function in the module and uses that. Unfortunately, in the case of TinyGo this first function is `llvm.dbg.value`, which is an LLVM intrinsic and doesn't have the target-features string. I fixed it by adding a `-mllvm -mattr=` flag to the linker.
1 parent ef993bf commit 9f44f49

File tree

9 files changed

+58
-6
lines changed

9 files changed

+58
-6
lines changed

builder/build.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -745,6 +745,7 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
745745
ldflags = append(ldflags, dependency.result)
746746
}
747747
ldflags = append(ldflags, "-mllvm", "-mcpu="+config.CPU())
748+
ldflags = append(ldflags, "-mllvm", "-mattr="+config.Features()) // needed for MIPS softfloat
748749
if config.GOOS() == "windows" {
749750
// Options for the MinGW wrapper for the lld COFF linker.
750751
ldflags = append(ldflags,

builder/builder_test.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,10 @@ func TestClangAttributes(t *testing.T) {
5757
{GOOS: "linux", GOARCH: "arm", GOARM: "6"},
5858
{GOOS: "linux", GOARCH: "arm", GOARM: "7"},
5959
{GOOS: "linux", GOARCH: "arm64"},
60-
{GOOS: "linux", GOARCH: "mips"},
61-
{GOOS: "linux", GOARCH: "mipsle"},
60+
{GOOS: "linux", GOARCH: "mips", GOMIPS: "hardfloat"},
61+
{GOOS: "linux", GOARCH: "mipsle", GOMIPS: "hardfloat"},
62+
{GOOS: "linux", GOARCH: "mips", GOMIPS: "softfloat"},
63+
{GOOS: "linux", GOARCH: "mipsle", GOMIPS: "softfloat"},
6264
{GOOS: "darwin", GOARCH: "amd64"},
6365
{GOOS: "darwin", GOARCH: "arm64"},
6466
{GOOS: "windows", GOARCH: "amd64"},
@@ -69,6 +71,9 @@ func TestClangAttributes(t *testing.T) {
6971
if options.GOARCH == "arm" {
7072
name += ",GOARM=" + options.GOARM
7173
}
74+
if options.GOARCH == "mips" || options.GOARCH == "mipsle" {
75+
name += ",GOMIPS=" + options.GOMIPS
76+
}
7277
t.Run(name, func(t *testing.T) {
7378
testClangAttributes(t, options)
7479
})

builder/library.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,11 @@ func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJ
185185
if strings.HasPrefix(target, "mips") {
186186
args = append(args, "-fno-pic")
187187
}
188+
if config.Target.SoftFloat {
189+
// Use softfloat instead of floating point instructions. This is
190+
// supported on many architectures.
191+
args = append(args, "-msoft-float")
192+
}
188193

189194
var once sync.Once
190195

compileopts/config.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,12 @@ func (c *Config) GOARM() string {
7272
return c.Options.GOARM
7373
}
7474

75+
// GOMIPS will return the GOMIPS environment variable given to the compiler when
76+
// building a program.
77+
func (c *Config) GOMIPS() string {
78+
return c.Options.GOMIPS
79+
}
80+
7581
// BuildTags returns the complete list of build tags used during this build.
7682
func (c *Config) BuildTags() []string {
7783
tags := append([]string(nil), c.Target.BuildTags...) // copy slice (avoid a race)
@@ -231,6 +237,9 @@ func (c *Config) LibcPath(name string) (path string, precompiled bool) {
231237
if c.ABI() != "" {
232238
archname += "-" + c.ABI()
233239
}
240+
if c.Target.SoftFloat {
241+
archname += "-softfloat"
242+
}
234243

235244
// Try to load a precompiled library.
236245
precompiledDir := filepath.Join(goenv.Get("TINYGOROOT"), "pkg", archname, name)

compileopts/options.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ type Options struct {
2323
GOOS string // environment variable
2424
GOARCH string // environment variable
2525
GOARM string // environment variable (only used with GOARCH=arm)
26+
GOMIPS string // environment variable (only used with GOARCH=mips and GOARCH=mipsle)
2627
Directory string // working dir, leave it unset to use the current working dir
2728
Target string
2829
Opt string

compileopts/target.go

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ type TargetSpec struct {
3030
Features string `json:"features,omitempty"`
3131
GOOS string `json:"goos,omitempty"`
3232
GOARCH string `json:"goarch,omitempty"`
33+
SoftFloat bool // used for non-baremetal systems (GOMIPS=softfloat etc)
3334
BuildTags []string `json:"build-tags,omitempty"`
3435
GC string `json:"gc,omitempty"`
3536
Scheduler string `json:"scheduler,omitempty"`
@@ -86,6 +87,10 @@ func (spec *TargetSpec) overrideProperties(child *TargetSpec) error {
8687
if src.Uint() != 0 {
8788
dst.Set(src)
8889
}
90+
case reflect.Bool:
91+
if src.Bool() {
92+
dst.Set(src)
93+
}
8994
case reflect.Ptr: // for pointers, copy if not nil
9095
if !src.IsNil() {
9196
dst.Set(src)
@@ -290,13 +295,22 @@ func defaultTarget(options *Options) (*TargetSpec, error) {
290295
}
291296
case "mips", "mipsle":
292297
spec.CPU = "mips32r2"
293-
spec.Features = "+fpxx,+mips32r2,+nooddspreg,-noabicalls"
294298
spec.CFlags = append(spec.CFlags, "-fno-pic")
295299
if options.GOOS == "mips" {
296300
llvmarch = "mips" // big endian
297301
} else {
298302
llvmarch = "mipsel" // little endian
299303
}
304+
switch options.GOMIPS {
305+
case "hardfloat":
306+
spec.Features = "+fpxx,+mips32r2,+nooddspreg,-noabicalls"
307+
case "softfloat":
308+
spec.SoftFloat = true
309+
spec.Features = "+mips32r2,+soft-float,-noabicalls"
310+
spec.CFlags = append(spec.CFlags, "-msoft-float")
311+
default:
312+
return nil, fmt.Errorf("invalid GOMIPS=%s: must be hardfloat or softfloat", options.GOMIPS)
313+
}
300314
case "wasm":
301315
llvmarch = "wasm32"
302316
spec.CPU = "generic"

goenv/goenv.go

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,11 @@ var Keys = []string{
3030
}
3131

3232
func init() {
33-
if Get("GOARCH") == "arm" {
33+
switch Get("GOARCH") {
34+
case "arm":
3435
Keys = append(Keys, "GOARM")
36+
case "mips", "mipsle":
37+
Keys = append(Keys, "GOMIPS")
3538
}
3639
}
3740

@@ -128,6 +131,13 @@ func Get(name string) string {
128131
// difference between ARMv6 and ARMv7. ARMv6 binaries are much smaller,
129132
// especially when floating point instructions are involved.
130133
return "6"
134+
case "GOMIPS":
135+
gomips := os.Getenv("GOMIPS")
136+
if gomips == "" {
137+
// Default to hardfloat (this matches the Go toolchain).
138+
gomips = "hardfloat"
139+
}
140+
return gomips
131141
case "GOROOT":
132142
readGoEnvVars()
133143
return goEnvVars.GOROOT

main.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1498,6 +1498,7 @@ func main() {
14981498
GOOS: goenv.Get("GOOS"),
14991499
GOARCH: goenv.Get("GOARCH"),
15001500
GOARM: goenv.Get("GOARM"),
1501+
GOMIPS: goenv.Get("GOMIPS"),
15011502
Target: *target,
15021503
StackSize: stackSize,
15031504
Opt: *opt,
@@ -1780,6 +1781,7 @@ func main() {
17801781
GOOS string `json:"goos"`
17811782
GOARCH string `json:"goarch"`
17821783
GOARM string `json:"goarm"`
1784+
GOMIPS string `json:"gomips"`
17831785
BuildTags []string `json:"build_tags"`
17841786
GC string `json:"garbage_collector"`
17851787
Scheduler string `json:"scheduler"`
@@ -1790,6 +1792,7 @@ func main() {
17901792
GOOS: config.GOOS(),
17911793
GOARCH: config.GOARCH(),
17921794
GOARM: config.GOARM(),
1795+
GOMIPS: config.GOMIPS(),
17931796
BuildTags: config.BuildTags(),
17941797
GC: config.GC(),
17951798
Scheduler: config.Scheduler(),

main_test.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ var supportedLinuxArches = map[string]string{
3737
"X86Linux": "linux/386",
3838
"ARMLinux": "linux/arm/6",
3939
"ARM64Linux": "linux/arm64",
40-
"MIPSLinux": "linux/mipsle",
40+
"MIPSLinux": "linux/mipsle/hardfloat",
4141
"WASIp1": "wasip1/wasm",
4242
}
4343

@@ -326,6 +326,7 @@ func optionsFromTarget(target string, sema chan struct{}) compileopts.Options {
326326
GOOS: goenv.Get("GOOS"),
327327
GOARCH: goenv.Get("GOARCH"),
328328
GOARM: goenv.Get("GOARM"),
329+
GOMIPS: goenv.Get("GOMIPS"),
329330
Target: target,
330331
Semaphore: sema,
331332
InterpTimeout: 180 * time.Second,
@@ -349,8 +350,11 @@ func optionsFromOSARCH(osarch string, sema chan struct{}) compileopts.Options {
349350
VerifyIR: true,
350351
Opt: "z",
351352
}
352-
if options.GOARCH == "arm" {
353+
switch options.GOARCH {
354+
case "arm":
353355
options.GOARM = parts[2]
356+
case "mips", "mipsle":
357+
options.GOMIPS = parts[2]
354358
}
355359
return options
356360
}

0 commit comments

Comments
 (0)