target.go raw

   1  package compileopts
   2  
   3  import (
   4  	"fmt"
   5  	"runtime"
   6  	"strings"
   7  )
   8  
   9  // TargetSpec is the target specification for a given target.
  10  type TargetSpec struct {
  11  	Triple           string   `json:"llvm-target,omitempty"`
  12  	CPU              string   `json:"cpu,omitempty"`
  13  	ABI              string   `json:"target-abi,omitempty"`
  14  	Features         string   `json:"features,omitempty"`
  15  	GOOS             string   `json:"goos,omitempty"`
  16  	GOARCH           string   `json:"goarch,omitempty"`
  17  	BuildTags        []string `json:"build-tags,omitempty"`
  18  	BuildMode        string   `json:"buildmode,omitempty"`
  19  	GC               string   `json:"gc,omitempty"`
  20  	Scheduler        string   `json:"scheduler,omitempty"`
  21  	Linker           string   `json:"linker,omitempty"`
  22  	RTLib            string   `json:"rtlib,omitempty"`
  23  	Libc             string   `json:"libc,omitempty"`
  24  	DefaultStackSize uint64   `json:"default-stack-size,omitempty"`
  25  	CFlags           []string `json:"cflags,omitempty"`
  26  	LDFlags          []string `json:"ldflags,omitempty"`
  27  	LinkerScript     string   `json:"linkerscript,omitempty"`
  28  	ExtraFiles       []string `json:"extra-files,omitempty"`
  29  	BinaryFormat     string   `json:"binary-format,omitempty"`
  30  	CodeModel        string   `json:"code-model,omitempty"`
  31  	RelocationModel  string   `json:"relocation-model,omitempty"`
  32  	Emulator         string   `json:"emulator,omitempty"`
  33  }
  34  
  35  // LoadTarget loads a target specification from options.
  36  func LoadTarget(options *Options) (*TargetSpec, error) {
  37  	// Parse compound target like "js/wasm" into GOOS/GOARCH.
  38  	if strings.Contains(options.Target, "/") {
  39  		parts := strings.SplitN(options.Target, "/", 2)
  40  		options.GOOS = parts[0]
  41  		options.GOARCH = parts[1]
  42  		options.Target = ""
  43  	}
  44  	if options.Target == "wasm" {
  45  		options.GOOS = "js"
  46  		options.GOARCH = "wasm"
  47  		options.Target = ""
  48  	}
  49  	return defaultTarget(options)
  50  }
  51  
  52  // defaultTarget synthesizes a TargetSpec from GOOS/GOARCH.
  53  func defaultTarget(options *Options) (*TargetSpec, error) {
  54  	spec := TargetSpec{
  55  		GOOS:             options.GOOS,
  56  		GOARCH:           options.GOARCH,
  57  		BuildTags:        []string{options.GOOS, options.GOARCH},
  58  		Linker:           "cc",
  59  		DefaultStackSize: 1024 * 64, // 64kB
  60  	}
  61  
  62  	var llvmarch string
  63  	switch options.GOARCH {
  64  	case "amd64":
  65  		llvmarch = "x86_64"
  66  		spec.CPU = "x86-64"
  67  		spec.Features = "+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87"
  68  	case "arm64":
  69  		spec.CPU = "generic"
  70  		llvmarch = "aarch64"
  71  		if options.GOOS == "darwin" {
  72  			spec.Features = "+ete,+fp-armv8,+neon,+trbe,+v8a"
  73  			llvmarch = "arm64"
  74  		} else {
  75  			spec.Features = "+ete,+fp-armv8,+neon,+trbe,+v8a,-fmv,-outline-atomics"
  76  		}
  77  	case "wasm":
  78  		if options.GOOS != "js" {
  79  			return nil, fmt.Errorf("GOARCH=wasm requires GOOS=js")
  80  		}
  81  		llvmarch = "wasm32"
  82  		spec.CPU = "generic"
  83  		spec.Features = "+mutable-globals,+sign-ext"
  84  	default:
  85  		return nil, fmt.Errorf("unsupported GOARCH=%s (moxie supports amd64, arm64, wasm)", options.GOARCH)
  86  	}
  87  
  88  	// Configure target based on GOOS.
  89  	llvmos := options.GOOS
  90  	llvmvendor := "unknown"
  91  	switch options.GOOS {
  92  	case "darwin":
  93  		spec.GC = "boehm"
  94  		platformVersion := "10.12.0"
  95  		if options.GOARCH == "arm64" {
  96  			platformVersion = "11.0.0"
  97  		}
  98  		llvmvendor = "apple"
  99  		spec.Scheduler = "none" // moxie: no goroutines
 100  		spec.Linker = "ld.lld"
 101  		spec.Libc = "darwin-libSystem"
 102  		llvmos = "macosx" + platformVersion
 103  		spec.LDFlags = append(spec.LDFlags,
 104  			"-flavor", "darwin",
 105  			"-dead_strip",
 106  			"-arch", llvmarch,
 107  			"-platform_version", "macos", platformVersion, platformVersion,
 108  		)
 109  		spec.ExtraFiles = append(spec.ExtraFiles,
 110  			"src/runtime/os_darwin.c",
 111  			"src/runtime/runtime_unix.c",
 112  			"src/runtime/signal.c",
 113  			"src/runtime/spawn_unix.c",
 114  			"src/internal/futex/futex_darwin.c")
 115  	case "linux":
 116  		spec.GC = "boehm"
 117  		spec.Scheduler = "none" // moxie: no goroutines
 118  		spec.Linker = "ld.lld"
 119  		spec.RTLib = "compiler-rt"
 120  		spec.Libc = "musl"
 121  		spec.LDFlags = append(spec.LDFlags, "--gc-sections")
 122  		if options.GOARCH == "arm64" {
 123  			spec.CFlags = append(spec.CFlags, "-mno-outline-atomics")
 124  		}
 125  		spec.ExtraFiles = append(spec.ExtraFiles,
 126  			"src/runtime/runtime_unix.c",
 127  			"src/runtime/signal.c",
 128  			"src/runtime/spawn_unix.c",
 129  			"src/internal/futex/futex_linux.c")
 130  	case "js":
 131  		spec.GC = "leaking"
 132  		spec.Scheduler = "none" // moxie: no goroutines
 133  		spec.Linker = "wasm-ld"
 134  		spec.BuildTags = append(spec.BuildTags, "moxie.wasm", "moxie.unicore")
 135  		spec.LDFlags = append(spec.LDFlags,
 136  			"--export-dynamic",
 137  			"--allow-undefined",
 138  			"--gc-sections",
 139  		)
 140  	default:
 141  		return nil, fmt.Errorf("unsupported GOOS=%s (moxie supports linux, darwin, js)", options.GOOS)
 142  	}
 143  
 144  	if spec.GC == "boehm" {
 145  		spec.ExtraFiles = append(spec.ExtraFiles, "src/runtime/gc_boehm.c")
 146  	}
 147  
 148  	// Target triple: arch-vendor-os[-environment]
 149  	spec.Triple = llvmarch + "-" + llvmvendor + "-" + llvmos
 150  	if options.GOOS == "linux" {
 151  		spec.Triple += "-musleabihf"
 152  	}
 153  
 154  	// Assembly files for scheduler and task switching (not for WASM).
 155  	if options.GOARCH != "wasm" {
 156  		spec.ExtraFiles = append(spec.ExtraFiles,
 157  			"src/runtime/asm_"+options.GOARCH+".S",
 158  			"src/internal/task/task_stack_"+options.GOARCH+".S")
 159  	}
 160  
 161  	// Cross-compilation emulator.
 162  	if options.GOARCH != runtime.GOARCH && options.GOOS == "linux" {
 163  		switch options.GOARCH {
 164  		case "amd64":
 165  			spec.Emulator = "qemu-x86_64 {}"
 166  		case "arm64":
 167  			spec.Emulator = "qemu-aarch64 {}"
 168  		}
 169  	}
 170  
 171  	return &spec, nil
 172  }
 173  
 174  // LookupGDB looks up a gdb executable. Returns empty string if not needed.
 175  func (spec *TargetSpec) LookupGDB() string {
 176  	return ""
 177  }
 178  
 179  // GetTargetSpecs returns available target specs. Moxie has no JSON target files.
 180  func GetTargetSpecs() map[string]*TargetSpec {
 181  	goos := []string{"linux", "darwin"}
 182  	goarch := []string{"amd64", "arm64"}
 183  	specs := make(map[string]*TargetSpec)
 184  	for _, os := range goos {
 185  		for _, arch := range goarch {
 186  			name := os + "/" + arch
 187  			t, err := defaultTarget(&Options{GOOS: os, GOARCH: arch})
 188  			if err == nil {
 189  				specs[name] = t
 190  			}
 191  		}
 192  	}
 193  	// Add js/wasm.
 194  	t, err := defaultTarget(&Options{GOOS: "js", GOARCH: "wasm"})
 195  	if err == nil {
 196  		specs["js/wasm"] = t
 197  	}
 198  	return specs
 199  }
 200  
 201  // Triple returns a cleaned version for display.
 202  func (spec *TargetSpec) TripleForDisplay() string {
 203  	return strings.TrimSpace(spec.Triple)
 204  }
 205