Source file src/simd/archsimd/_gen/main.go

     1  // Copyright 2025 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // Run all SIMD-related code generators.
     6  package main
     7  
     8  import (
     9  	"flag"
    10  	"fmt"
    11  	"os"
    12  	"os/exec"
    13  	"path/filepath"
    14  	"strings"
    15  )
    16  
    17  const defaultLocalISA = "ISA_A64_xml_A_profile-2026-03_96/ISA_A64_xml_A_profile_2026-03_96-2026-03_rel" // ISA_A64_xml_A_profile-2025-06"
    18  const defaultXedPath = "$XEDPATH" + string(filepath.ListSeparator) + "./simdgen/xeddata" + string(filepath.ListSeparator) + "$HOME/xed/obj/dgen"
    19  const defaultArm64Path = "$ARM64_ISA_PATH" + string(filepath.ListSeparator) + "./simdgen/armdata" + string(filepath.ListSeparator) + "$HOME/Downloads/" + defaultLocalISA
    20  
    21  var (
    22  	flagTmplgen = flag.Bool("tmplgen", true, "run tmplgen generator")
    23  	flagSimdgen = flag.Bool("simdgen", true, "run simdgen generator")
    24  	flagWasmgen = flag.Bool("wasmgen", true, "run wasmgen generator")
    25  	flagMidway  = flag.Bool("midway", true, "run midway generator")
    26  
    27  	flagN         = flag.Bool("n", false, "dry run")
    28  	flagXedPath   = flag.String("xedPath", defaultXedPath, "load XED datafile from `path`, which must be the XED obj/dgen directory")
    29  	flagArm64Path = flag.String("arm64Path", defaultArm64Path, "load ARM64 ISA XML definitions from `path`")
    30  
    31  	flagGoRoot = flag.String("goroot", "", "destination go dev tree for generated files")
    32  )
    33  
    34  var goRoot string
    35  
    36  func main() {
    37  	flag.Parse()
    38  	if flag.NArg() > 0 {
    39  		flag.Usage()
    40  		os.Exit(1)
    41  	}
    42  
    43  	if *flagXedPath == defaultXedPath {
    44  		// In general we want the shell to do variable expansion, but for the
    45  		// default value we don't get that, so do it ourselves.
    46  		*flagXedPath = os.ExpandEnv(defaultXedPath)
    47  	}
    48  
    49  	if *flagArm64Path == defaultArm64Path {
    50  		// In general we want the shell to do variable expansion, but for the
    51  		// default value we don't get that, so do it ourselves.
    52  		*flagArm64Path = os.ExpandEnv(defaultArm64Path)
    53  	}
    54  
    55  	var err error
    56  	goRoot, err = resolveGOROOT()
    57  	if err != nil {
    58  		fmt.Fprintln(os.Stderr, err)
    59  		os.Exit(1)
    60  	}
    61  
    62  	if *flagSimdgen {
    63  		fmt.Fprintln(os.Stderr, "# This may take a few minutes...")
    64  	}
    65  
    66  	if *flagTmplgen {
    67  		doTmplgen()
    68  	}
    69  
    70  	if *flagWasmgen || *flagSimdgen {
    71  		ssaGenPath := prettyPath(".", filepath.Join(goRoot, "src", "cmd", "compile", "internal", "ssa", "_gen"))
    72  
    73  		// If there is garbage in ssa/_gen/simdgenericOps.go, it can affect the merge in simdgen/wasmgen.
    74  		removeSimdGenericOps(ssaGenPath)
    75  
    76  		if *flagWasmgen {
    77  			doWasmgen()
    78  		}
    79  		if *flagSimdgen {
    80  			doSimdgen()
    81  		}
    82  		ssaGen(ssaGenPath)
    83  	}
    84  
    85  	if *flagMidway {
    86  		doMidway()
    87  	}
    88  }
    89  
    90  func removeSimdGenericOps(ssaGenPath string) {
    91  	ssaSimdGenericOps := filepath.Join(ssaGenPath, "simdgenericOps.go")
    92  	if _, err := os.Stat(ssaSimdGenericOps); err == nil {
    93  		if err = os.Remove(ssaSimdGenericOps); err != nil {
    94  			fmt.Fprintf(os.Stderr, "Failed to delete %s before regenerating it, %v\n", ssaSimdGenericOps, err)
    95  			os.Exit(1)
    96  		}
    97  	}
    98  }
    99  
   100  func ssaGen(ssaGenPath string) {
   101  	// simdgen produces SSA rule files, so update the SSA files
   102  	goRun("-C", ssaGenPath, ".")
   103  
   104  	fmt.Fprintln(os.Stderr, "# Compiler changed. Consider running \"go install cmd/compile\"")
   105  }
   106  
   107  func doTmplgen() {
   108  	goRun("-C", "tmplgen", ".")
   109  }
   110  
   111  func doWasmgen() {
   112  	goRun("-C", "wasmgen", ".")
   113  }
   114  
   115  func doMidway() {
   116  	goRun("-C", "midway", ".")
   117  }
   118  
   119  func doSimdgen() {
   120  	xedPath, err := resolveXEDPath(*flagXedPath)
   121  	if err != nil {
   122  		fmt.Fprintln(os.Stderr, err)
   123  		os.Exit(1)
   124  	}
   125  
   126  	armPath, err := resolveARMPath(*flagArm64Path)
   127  	if err != nil {
   128  		fmt.Fprintln(os.Stderr, err)
   129  		os.Exit(1)
   130  	}
   131  
   132  	goRun("-C", "simdgen", ".", "-o", "godefs", "-goroot", goRoot, "-arch", "arm64", "-arm64Path", prettyPath("./simdgen", armPath), "go_arm64.yaml", "types.yaml", "categories.yaml")
   133  
   134  	// Regenerate the XED-derived SIMD files
   135  	goRun("-C", "simdgen", ".", "-o", "godefs", "-goroot", goRoot, "-arch", "amd64", "-xedPath", prettyPath("./simdgen", xedPath), "go_amd64.yaml", "types.yaml", "categories.yaml")
   136  }
   137  
   138  // simdgen -o godefs -goroot goRoot -arm64Path $ARM64_ISA_PATH arm64/go.yaml arm64/categories.yaml types.yaml
   139  
   140  func resolveXEDPath(pathList string) (xedPath string, err error) {
   141  	for _, path := range filepath.SplitList(pathList) {
   142  		if path == "" {
   143  			// Probably an unknown shell variable. Ignore.
   144  			continue
   145  		}
   146  		if _, err := os.Stat(filepath.Join(path, "all-dec-instructions.txt")); err == nil {
   147  			return filepath.Abs(path)
   148  		}
   149  	}
   150  	return "", fmt.Errorf("set $XEDPATH or -xedPath to the XED obj/dgen directory")
   151  }
   152  
   153  func resolveARMPath(pathList string) (armPath string, err error) {
   154  	for _, path := range filepath.SplitList(pathList) {
   155  		if path == "" {
   156  			// Probably an unknown shell variable. Ignore.
   157  			continue
   158  		}
   159  		if _, err := os.Stat(filepath.Join(path, "abs_advsimd.xml")); err == nil {
   160  			return filepath.Abs(path)
   161  		}
   162  	}
   163  	return "", fmt.Errorf("set $ARM64_ISA_PATH or -armPath to the ARM64 ISA specification directory")
   164  }
   165  
   166  func resolveGOROOT() (goRoot string, err error) {
   167  	goRoot = *flagGoRoot
   168  	if goRoot != "" {
   169  		return
   170  	}
   171  	// Using the current compiler's goroot depends on a working dev compiler,
   172  	// which is not guaranteed.  Instead, assume
   173  	cwd, err := os.Getwd()
   174  	if err != nil {
   175  		return "", fmt.Errorf("Getwd error: %s", err)
   176  	}
   177  	goRoot, err = filepath.Abs(filepath.Join(cwd, "..", "..", "..", ".."))
   178  	if err != nil {
   179  		return "", fmt.Errorf("Abs path error: %s", err)
   180  	}
   181  	_, err = os.Stat(filepath.Join(goRoot, "src", "simd", "archsimd", "_gen"))
   182  	if err != nil {
   183  		return "", fmt.Errorf("-goroot not specified and not run in src/simd/archsimd/_gen")
   184  	}
   185  
   186  	return
   187  }
   188  
   189  func goRun(args ...string) {
   190  	exe := "go" // Use go on the path, not GOROOT.  GOROOT could be broken
   191  	cmd := exec.Command(exe, append([]string{"run"}, args...)...)
   192  	run(cmd)
   193  }
   194  
   195  func run(cmd *exec.Cmd) {
   196  	cmd.Stdout = os.Stdout
   197  	cmd.Stderr = os.Stderr
   198  	fmt.Fprintf(os.Stderr, "%s\n", cmdString(cmd))
   199  	if *flagN {
   200  		return
   201  	}
   202  	if err := cmd.Run(); err != nil {
   203  		fmt.Fprintf(os.Stderr, "%s failed: %s\n", cmd, err)
   204  		os.Exit(1)
   205  	}
   206  }
   207  
   208  func prettyPath(base, path string) string {
   209  	base, err := filepath.Abs(base)
   210  	if err != nil {
   211  		return path
   212  	}
   213  	p, err := filepath.Rel(base, path)
   214  	if err != nil {
   215  		return path
   216  	}
   217  	return p
   218  }
   219  
   220  func cmdString(cmd *exec.Cmd) string {
   221  	// TODO: Shell quoting?
   222  	// TODO: Environment.
   223  
   224  	var buf strings.Builder
   225  
   226  	cmdPath, err := exec.LookPath(filepath.Base(cmd.Path))
   227  	if err == nil && cmdPath == cmd.Path {
   228  		cmdPath = filepath.Base(cmdPath)
   229  	} else {
   230  		cmdPath = prettyPath(".", cmd.Path)
   231  	}
   232  	buf.WriteString(cmdPath)
   233  
   234  	for _, arg := range cmd.Args[1:] {
   235  		buf.WriteByte(' ')
   236  		buf.WriteString(arg)
   237  	}
   238  
   239  	return buf.String()
   240  }
   241  

View as plain text