Skip to content

Commit f188eaf

Browse files
aykevldeadprogram
authored andcommitted
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 6efc6d2 commit f188eaf

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
@@ -746,6 +746,7 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
746746
ldflags = append(ldflags, dependency.result)
747747
}
748748
ldflags = append(ldflags, "-mllvm", "-mcpu="+config.CPU())
749+
ldflags = append(ldflags, "-mllvm", "-mattr="+config.Features()) // needed for MIPS softfloat
749750
if config.GOOS() == "windows" {
750751
// Options for the MinGW wrapper for the lld COFF linker.
751752
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
@@ -169,6 +169,11 @@ func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJ
169169
case "mips":
170170
args = append(args, "-fno-pic")
171171
}
172+
if config.Target.SoftFloat {
173+
// Use softfloat instead of floating point instructions. This is
174+
// supported on many architectures.
175+
args = append(args, "-msoft-float")
176+
}
172177

173178
var once sync.Once
174179

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)
@@ -240,6 +246,9 @@ func (c *Config) LibcPath(name string) (path string, precompiled bool) {
240246
if c.ABI() != "" {
241247
archname += "-" + c.ABI()
242248
}
249+
if c.Target.SoftFloat {
250+
archname += "-softfloat"
251+
}
243252

244253
// Try to load a precompiled library.
245254
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
@@ -1499,6 +1499,7 @@ func main() {
14991499
GOOS: goenv.Get("GOOS"),
15001500
GOARCH: goenv.Get("GOARCH"),
15011501
GOARM: goenv.Get("GOARM"),
1502+
GOMIPS: goenv.Get("GOMIPS"),
15021503
Target: *target,
15031504
StackSize: stackSize,
15041505
Opt: *opt,
@@ -1737,6 +1738,7 @@ func main() {
17371738
GOOS string `json:"goos"`
17381739
GOARCH string `json:"goarch"`
17391740
GOARM string `json:"goarm"`
1741+
GOMIPS string `json:"gomips"`
17401742
BuildTags []string `json:"build_tags"`
17411743
GC string `json:"garbage_collector"`
17421744
Scheduler string `json:"scheduler"`
@@ -1747,6 +1749,7 @@ func main() {
17471749
GOOS: config.GOOS(),
17481750
GOARCH: config.GOARCH(),
17491751
GOARM: config.GOARM(),
1752+
GOMIPS: config.GOMIPS(),
17501753
BuildTags: config.BuildTags(),
17511754
GC: config.GC(),
17521755
Scheduler: config.Scheduler(),

main_test.go

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

@@ -325,6 +325,7 @@ func optionsFromTarget(target string, sema chan struct{}) compileopts.Options {
325325
GOOS: goenv.Get("GOOS"),
326326
GOARCH: goenv.Get("GOARCH"),
327327
GOARM: goenv.Get("GOARM"),
328+
GOMIPS: goenv.Get("GOMIPS"),
328329
Target: target,
329330
Semaphore: sema,
330331
InterpTimeout: 180 * time.Second,
@@ -348,8 +349,11 @@ func optionsFromOSARCH(osarch string, sema chan struct{}) compileopts.Options {
348349
VerifyIR: true,
349350
Opt: "z",
350351
}
351-
if options.GOARCH == "arm" {
352+
switch options.GOARCH {
353+
case "arm":
352354
options.GOARM = parts[2]
355+
case "mips", "mipsle":
356+
options.GOMIPS = parts[2]
353357
}
354358
return options
355359
}

0 commit comments

Comments
 (0)