This commit is contained in:
xushiwei
2024-04-23 18:57:46 +08:00
parent aafe5a8600
commit edb7a4e1a5
6 changed files with 267 additions and 156 deletions

View File

@@ -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)
}

View File

@@ -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
View 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 }

View File

@@ -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
}
// -----------------------------------------------------------------------------

View File

@@ -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
View File

@@ -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")
}
*/