ssadump
This commit is contained in:
@@ -1,57 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2023 The GoPlus Authors (goplus.org). All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package build
|
||||
|
||||
import (
|
||||
"go/build"
|
||||
)
|
||||
|
||||
// An ImportMode controls the behavior of the Import method.
|
||||
type ImportMode = build.ImportMode
|
||||
|
||||
// A Package describes the Go package found in a directory.
|
||||
type Package struct {
|
||||
*build.Package
|
||||
}
|
||||
|
||||
// A Context specifies the supporting context for a build.
|
||||
type Context struct {
|
||||
*build.Context
|
||||
}
|
||||
|
||||
// Import returns details about the Go package named by the import path,
|
||||
// interpreting local import paths relative to the srcDir directory.
|
||||
// If the path is a local import path naming a package that can be imported
|
||||
// using a standard import path, the returned package will set p.ImportPath
|
||||
// to that path.
|
||||
//
|
||||
// If an error occurs, Import returns a non-nil error and a non-nil
|
||||
// *Package containing partial information.
|
||||
func (ctxt Context) Import(path string, srcDir string, mode ImportMode) (ret Package, err error) {
|
||||
pkg, err := build.Import(path, srcDir, mode)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
ret = Package{pkg}
|
||||
return
|
||||
}
|
||||
|
||||
// ImportDir is like Import but processes the Go package found in
|
||||
// the named directory.
|
||||
func (ctxt *Context) ImportDir(dir string, mode ImportMode) (Package, error) {
|
||||
return ctxt.Import(".", dir, mode)
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2023 The GoPlus Authors (goplus.org). All rights reserved.
|
||||
* Copyright (c) 2024 The GoPlus Authors (goplus.org). All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
201
chore/ssadump/ssadump.go
Normal file
201
chore/ssadump/ssadump.go
Normal file
@@ -0,0 +1,201 @@
|
||||
// Copyright 2013 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// ssadump: a tool for displaying and interpreting the SSA form of Go programs.
|
||||
package main // import "golang.org/x/tools/cmd/ssadump"
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"go/build"
|
||||
"go/types"
|
||||
"os"
|
||||
"runtime"
|
||||
"runtime/pprof"
|
||||
|
||||
"golang.org/x/tools/go/buildutil"
|
||||
"golang.org/x/tools/go/packages"
|
||||
"golang.org/x/tools/go/ssa"
|
||||
"golang.org/x/tools/go/ssa/interp"
|
||||
"golang.org/x/tools/go/ssa/ssautil"
|
||||
)
|
||||
|
||||
const (
|
||||
loadFiles = packages.NeedName | packages.NeedFiles | packages.NeedCompiledGoFiles
|
||||
loadImports = loadFiles | packages.NeedImports
|
||||
loadTypes = loadImports | packages.NeedTypes | packages.NeedTypesSizes
|
||||
loadSyntax = loadTypes | packages.NeedSyntax | packages.NeedTypesInfo
|
||||
)
|
||||
|
||||
// flags
|
||||
var (
|
||||
mode = ssa.BuilderMode(0)
|
||||
|
||||
testFlag = flag.Bool("test", false, "include implicit test packages and executables")
|
||||
|
||||
runFlag = flag.Bool("run", false, "interpret the SSA program")
|
||||
|
||||
interpFlag = flag.String("interp", "", `Options controlling the SSA test interpreter.
|
||||
The value is a sequence of zero or more more of these letters:
|
||||
R disable [R]ecover() from panic; show interpreter crash instead.
|
||||
T [T]race execution of the program. Best for single-threaded programs!
|
||||
`)
|
||||
|
||||
cpuprofile = flag.String("cpuprofile", "", "write cpu profile to file")
|
||||
|
||||
args stringListValue
|
||||
)
|
||||
|
||||
func init() {
|
||||
flag.Var(&mode, "build", ssa.BuilderModeDoc)
|
||||
flag.Var((*buildutil.TagsFlag)(&build.Default.BuildTags), "tags", buildutil.TagsFlagDoc)
|
||||
flag.Var(&args, "arg", "add argument to interpreted program")
|
||||
}
|
||||
|
||||
const usage = `SSA builder and interpreter.
|
||||
Usage: ssadump [-build=[DBCSNFLG]] [-test] [-run] [-interp=[TR]] [-arg=...] package...
|
||||
Use -help flag to display options.
|
||||
|
||||
Examples:
|
||||
% ssadump -build=F hello.go # dump SSA form of a single package
|
||||
% ssadump -build=F -test fmt # dump SSA form of a package and its tests
|
||||
% ssadump -run -interp=T hello.go # interpret a program, with tracing
|
||||
|
||||
The -run flag causes ssadump to build the code in a runnable form and run the first
|
||||
package named main.
|
||||
|
||||
Interpretation of the standard "testing" package is no longer supported.
|
||||
`
|
||||
|
||||
func main() {
|
||||
if err := doMain(); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "ssadump: %s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func doMain() error {
|
||||
flag.Parse()
|
||||
if len(flag.Args()) == 0 {
|
||||
fmt.Fprint(os.Stderr, usage)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
cfg := &packages.Config{
|
||||
Mode: loadSyntax,
|
||||
Tests: *testFlag,
|
||||
}
|
||||
|
||||
// Choose types.Sizes from conf.Build.
|
||||
// TODO(adonovan): remove this when go/packages provides a better way.
|
||||
var wordSize int64 = 8
|
||||
switch build.Default.GOARCH {
|
||||
case "386", "arm":
|
||||
wordSize = 4
|
||||
}
|
||||
sizes := &types.StdSizes{
|
||||
MaxAlign: 8,
|
||||
WordSize: wordSize,
|
||||
}
|
||||
|
||||
var interpMode interp.Mode
|
||||
for _, c := range *interpFlag {
|
||||
switch c {
|
||||
case 'T':
|
||||
interpMode |= interp.EnableTracing
|
||||
case 'R':
|
||||
interpMode |= interp.DisableRecover
|
||||
default:
|
||||
return fmt.Errorf("unknown -interp option: '%c'", c)
|
||||
}
|
||||
}
|
||||
|
||||
// Profiling support.
|
||||
if *cpuprofile != "" {
|
||||
f, err := os.Create(*cpuprofile)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
pprof.StartCPUProfile(f)
|
||||
defer pprof.StopCPUProfile()
|
||||
}
|
||||
|
||||
// Load, parse and type-check the initial packages,
|
||||
// and, if -run, their dependencies.
|
||||
if *runFlag {
|
||||
cfg.Mode = loadSyntax | packages.NeedDeps
|
||||
}
|
||||
initial, err := packages.Load(cfg, flag.Args()...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(initial) == 0 {
|
||||
return fmt.Errorf("no packages")
|
||||
}
|
||||
if packages.PrintErrors(initial) > 0 {
|
||||
return fmt.Errorf("packages contain errors")
|
||||
}
|
||||
|
||||
// Turn on instantiating generics during build if the program will be run.
|
||||
if *runFlag {
|
||||
mode |= ssa.InstantiateGenerics
|
||||
}
|
||||
|
||||
// Create SSA-form program representation.
|
||||
prog, pkgs := ssautil.AllPackages(initial, mode)
|
||||
|
||||
for i, p := range pkgs {
|
||||
if p == nil {
|
||||
return fmt.Errorf("cannot build SSA for package %s", initial[i])
|
||||
}
|
||||
}
|
||||
|
||||
if !*runFlag {
|
||||
// Build and display only the initial packages
|
||||
// (and synthetic wrappers).
|
||||
for _, p := range pkgs {
|
||||
p.Build()
|
||||
}
|
||||
|
||||
} else {
|
||||
// Run the interpreter.
|
||||
// Build SSA for all packages.
|
||||
prog.Build()
|
||||
|
||||
// Earlier versions of the interpreter needed the runtime
|
||||
// package; however, interp cannot handle unsafe constructs
|
||||
// used during runtime's package initialization at the moment.
|
||||
// The key construct blocking support is:
|
||||
// *((*T)(unsafe.Pointer(p)))
|
||||
// Unfortunately, this means only trivial programs can be
|
||||
// interpreted by ssadump.
|
||||
if prog.ImportedPackage("runtime") != nil {
|
||||
return fmt.Errorf("-run: program depends on runtime package (interpreter can run only trivial programs)")
|
||||
}
|
||||
|
||||
if runtime.GOARCH != build.Default.GOARCH {
|
||||
return fmt.Errorf("cross-interpretation is not supported (target has GOARCH %s, interpreter has %s)",
|
||||
build.Default.GOARCH, runtime.GOARCH)
|
||||
}
|
||||
|
||||
// Run first main package.
|
||||
for _, main := range ssautil.MainPackages(pkgs) {
|
||||
fmt.Fprintf(os.Stderr, "Running: %s\n", main.Pkg.Path())
|
||||
os.Exit(interp.Interpret(main, interpMode, sizes, main.Pkg.Path(), args))
|
||||
}
|
||||
return fmt.Errorf("no main package")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// stringListValue is a flag.Value that accumulates strings.
|
||||
// e.g. --flag=one --flag=two would produce []string{"one", "two"}.
|
||||
type stringListValue []string
|
||||
|
||||
func (ss *stringListValue) Get() interface{} { return []string(*ss) }
|
||||
|
||||
func (ss *stringListValue) String() string { return fmt.Sprintf("%q", *ss) }
|
||||
|
||||
func (ss *stringListValue) Set(s string) error { *ss = append(*ss, s); return nil }
|
||||
@@ -1,95 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2024 The GoPlus Authors (goplus.org). All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// Package build implements the “llgo gen command.
|
||||
package gen
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"reflect"
|
||||
|
||||
"github.com/goplus/llgo"
|
||||
"github.com/goplus/llgo/cl"
|
||||
"github.com/goplus/llgo/cmd/internal/base"
|
||||
"github.com/goplus/llgo/internal/projs"
|
||||
"github.com/goplus/llgo/ssa"
|
||||
"github.com/qiniu/x/errors"
|
||||
)
|
||||
|
||||
// llgo gen
|
||||
var Cmd = &base.Command{
|
||||
UsageLine: "llgo gen [-v] [packages|files]",
|
||||
Short: "Convert Go code into LLVM ir (*.ll) code",
|
||||
}
|
||||
|
||||
var (
|
||||
flag = &Cmd.Flag
|
||||
flagVerbose = flag.Bool("v", false, "print verbose information")
|
||||
)
|
||||
|
||||
func init() {
|
||||
Cmd.Run = runCmd
|
||||
}
|
||||
|
||||
func runCmd(cmd *base.Command, args []string) {
|
||||
err := flag.Parse(args)
|
||||
if err != nil {
|
||||
log.Panicln("parse input arguments failed:", err)
|
||||
}
|
||||
pattern := flag.Args()
|
||||
if len(pattern) == 0 {
|
||||
pattern = []string{"."}
|
||||
}
|
||||
|
||||
projects, err := projs.ParseAll(pattern...)
|
||||
if err != nil {
|
||||
log.Panicln("gopprojs.ParseAll:", err)
|
||||
}
|
||||
|
||||
ssa.Initialize(ssa.InitAll)
|
||||
if *flagVerbose {
|
||||
ssa.SetDebug(ssa.DbgFlagAll)
|
||||
cl.SetDebug(cl.DbgFlagAll)
|
||||
}
|
||||
|
||||
for _, proj := range projects {
|
||||
switch v := proj.(type) {
|
||||
case *projs.DirProj:
|
||||
_, _, err = llgo.Gen(v.Dir, nil, true, 0)
|
||||
case *projs.PkgPathProj:
|
||||
_, _, err = llgo.GenPkgPath("", v.Path, nil, true, 0)
|
||||
case *projs.FilesProj:
|
||||
_, err = llgo.GenFiles("", v.Files, nil)
|
||||
default:
|
||||
log.Panicln("`llgo gen` doesn't support", reflect.TypeOf(v))
|
||||
}
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "llgo gen failed: %d errors.\n", errorNum(err))
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func errorNum(err error) int {
|
||||
if e, ok := err.(errors.List); ok {
|
||||
return len(e)
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
@@ -26,7 +26,6 @@ import (
|
||||
|
||||
"github.com/goplus/llgo/cmd/internal/base"
|
||||
"github.com/goplus/llgo/cmd/internal/build"
|
||||
"github.com/goplus/llgo/cmd/internal/gen"
|
||||
"github.com/goplus/llgo/cmd/internal/help"
|
||||
)
|
||||
|
||||
@@ -39,7 +38,6 @@ func init() {
|
||||
flag.Usage = mainUsage
|
||||
base.Llgo.Commands = []*base.Command{
|
||||
build.Cmd,
|
||||
gen.Cmd,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
66
gen.go
66
gen.go
@@ -16,11 +16,74 @@
|
||||
|
||||
package llgo
|
||||
|
||||
/*
|
||||
import (
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/qiniu/x/errors"
|
||||
)
|
||||
|
||||
type GenFlags int
|
||||
|
||||
const (
|
||||
GenFlagCheckOnly GenFlags = 1 << iota
|
||||
GenFlagPrintError
|
||||
GenFlagPrompt
|
||||
)
|
||||
|
||||
// Gen generates llgo_autogen.ll for a Go package directory.
|
||||
func Gen(dir string, conf *Config, genTestPkg bool, flags GenFlags) (string, bool, error) {
|
||||
panic("todo")
|
||||
recursively := strings.HasSuffix(dir, "/...")
|
||||
if recursively {
|
||||
dir = dir[:len(dir)-4]
|
||||
}
|
||||
return dir, recursively, genDir(dir, conf, genTestPkg, recursively, flags)
|
||||
}
|
||||
|
||||
func genDir(dir string, conf *Config, genTestPkg, recursively bool, flags GenFlags) (err error) {
|
||||
if conf == nil {
|
||||
conf = new(Config)
|
||||
}
|
||||
if recursively {
|
||||
var (
|
||||
list errors.List
|
||||
)
|
||||
fn := func(path string, d fs.DirEntry, err error) error {
|
||||
if err == nil && d.IsDir() {
|
||||
if strings.HasPrefix(d.Name(), "_") || (path != dir && hasMod(path)) { // skip _
|
||||
return filepath.SkipDir
|
||||
}
|
||||
if e := genGoIn(path, conf, genTestPkg, flags); e != nil && notIgnNotated(e, conf) {
|
||||
if flags&GenFlagPrintError != 0 {
|
||||
fmt.Fprintln(os.Stderr, e)
|
||||
}
|
||||
list.Add(e)
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
err = filepath.WalkDir(dir, fn)
|
||||
if err != nil {
|
||||
return errors.NewWith(err, `filepath.WalkDir(dir, fn)`, -2, "filepath.WalkDir", dir, fn)
|
||||
}
|
||||
return list.ToError()
|
||||
}
|
||||
if e := genGoIn(dir, conf, genTestPkg, flags); e != nil && notIgnNotated(e, conf) {
|
||||
if (flags & GenFlagPrintError) != 0 {
|
||||
fmt.Fprintln(os.Stderr, e)
|
||||
}
|
||||
err = e
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func hasMod(dir string) bool {
|
||||
_, err := os.Lstat(dir + "/go.mod")
|
||||
return err == nil
|
||||
}
|
||||
|
||||
// GenPkgPath generates llgo_autogen.ll for a Go package.
|
||||
@@ -32,3 +95,4 @@ func GenPkgPath(workDir, pkgPath string, conf *Config, allowExtern bool, flags G
|
||||
func GenFiles(autogen string, files []string, conf *Config) (outFiles []string, err error) {
|
||||
panic("todo")
|
||||
}
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user