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