From 3da1aa3ec6dc0baf2e26cd7af962f8d5d5953abf Mon Sep 17 00:00:00 2001 From: xushiwei Date: Tue, 28 May 2024 12:22:43 +0800 Subject: [PATCH] github.com/goplus/llgo/internal/packages; prog.TypeSizes: todo --- internal/build/build.go | 10 +-- internal/build/clean.go | 4 +- internal/llgen/llgenf.go | 9 +-- internal/packages/load.go | 144 ++++++++++++++++++++++++++++++++++++++ ssa/type.go | 5 ++ 5 files changed, 162 insertions(+), 10 deletions(-) create mode 100644 internal/packages/load.go diff --git a/internal/build/build.go b/internal/build/build.go index 3772dd71..e7da46b7 100644 --- a/internal/build/build.go +++ b/internal/build/build.go @@ -29,10 +29,10 @@ import ( "runtime" "strings" - "golang.org/x/tools/go/packages" "golang.org/x/tools/go/ssa" "github.com/goplus/llgo/cl" + "github.com/goplus/llgo/internal/packages" "github.com/goplus/llgo/xtool/clang" llssa "github.com/goplus/llgo/ssa" @@ -88,6 +88,9 @@ const ( ) func Do(args []string, conf *Config) { + prog := llssa.NewProgram(nil) + sizes := prog.TypeSizes() + flags, patterns, verbose := ParseArgs(args, buildFlags) cfg := &packages.Config{ Mode: loadSyntax | packages.NeedDeps | packages.NeedModule | packages.NeedExportFile, @@ -97,7 +100,7 @@ func Do(args []string, conf *Config) { if patterns == nil { patterns = []string{"."} } - initial, err := packages.Load(cfg, patterns...) + initial, err := packages.LoadEx(sizes, cfg, patterns...) check(err) mode := conf.Mode @@ -122,11 +125,10 @@ func Do(args []string, conf *Config) { var needRt bool var rt []*packages.Package - prog := llssa.NewProgram(nil) load := func() []*packages.Package { if rt == nil { var err error - rt, err = packages.Load(cfg, llssa.PkgRuntime, llssa.PkgPython) + rt, err = packages.LoadEx(sizes, cfg, llssa.PkgRuntime, llssa.PkgPython) check(err) } return rt diff --git a/internal/build/clean.go b/internal/build/clean.go index 44547290..1e5a2e26 100644 --- a/internal/build/clean.go +++ b/internal/build/clean.go @@ -22,7 +22,7 @@ import ( "path" "path/filepath" - "golang.org/x/tools/go/packages" + "github.com/goplus/llgo/internal/packages" ) var ( @@ -42,7 +42,7 @@ func Clean(args []string, conf *Config) { if patterns == nil { patterns = []string{"."} } - initial, err := packages.Load(cfg, patterns...) + initial, err := packages.LoadEx(nil, cfg, patterns...) check(err) cleanPkgs(initial, verbose) diff --git a/internal/llgen/llgenf.go b/internal/llgen/llgenf.go index 7459be55..34489f2d 100644 --- a/internal/llgen/llgenf.go +++ b/internal/llgen/llgenf.go @@ -24,7 +24,7 @@ import ( "strings" "github.com/goplus/llgo/cl" - "golang.org/x/tools/go/packages" + "github.com/goplus/llgo/internal/packages" "golang.org/x/tools/go/ssa" "golang.org/x/tools/go/ssa/ssautil" @@ -43,7 +43,7 @@ func initRtAndPy(prog llssa.Program, cfg *packages.Config) { load := func() []*packages.Package { if pkgRtAndPy == nil { var err error - pkgRtAndPy, err = packages.Load(cfg, llssa.PkgRuntime, llssa.PkgPython) + pkgRtAndPy, err = packages.LoadEx(prog.TypeSizes(), cfg, llssa.PkgRuntime, llssa.PkgPython) check(err) } return pkgRtAndPy @@ -60,10 +60,12 @@ func initRtAndPy(prog llssa.Program, cfg *packages.Config) { } func GenFrom(fileOrPkg string) string { + prog := llssa.NewProgram(nil) + cfg := &packages.Config{ Mode: loadSyntax | packages.NeedDeps, } - initial, err := packages.Load(cfg, fileOrPkg) + initial, err := packages.LoadEx(prog.TypeSizes(), cfg, fileOrPkg) check(err) _, pkgs := ssautil.AllPackages(initial, ssa.SanityCheckFunctions) @@ -72,7 +74,6 @@ func GenFrom(fileOrPkg string) string { ssaPkg := pkgs[0] ssaPkg.Build() - prog := llssa.NewProgram(nil) initRtAndPy(prog, cfg) if Verbose { diff --git a/internal/packages/load.go b/internal/packages/load.go new file mode 100644 index 00000000..0797e099 --- /dev/null +++ b/internal/packages/load.go @@ -0,0 +1,144 @@ +/* + * 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 packages + +import ( + "fmt" + "go/types" + "runtime" + "sync" + "unsafe" + + "golang.org/x/tools/go/packages" +) + +// A LoadMode controls the amount of detail to return when loading. +// The bits below can be combined to specify which fields should be +// filled in the result packages. +// The zero value is a special case, equivalent to combining +// the NeedName, NeedFiles, and NeedCompiledGoFiles bits. +// ID and Errors (if present) will always be filled. +// Load may return more information than requested. +type LoadMode = packages.LoadMode + +const ( + NeedName = packages.NeedName + NeedFiles = packages.NeedFiles + + NeedSyntax = packages.NeedSyntax + NeedImports = packages.NeedImports + NeedDeps = packages.NeedDeps + NeedModule = packages.NeedModule + NeedExportFile = packages.NeedExportFile + + NeedCompiledGoFiles = packages.NeedCompiledGoFiles + + NeedTypes = packages.NeedTypes + NeedTypesSizes = packages.NeedTypesSizes + NeedTypesInfo = packages.NeedTypesInfo +) + +// A Config specifies details about how packages should be loaded. +// The zero value is a valid configuration. +// Calls to Load do not modify this struct. +type Config = packages.Config + +// A Package describes a loaded Go package. +type Package = packages.Package + +// loader holds the working state of a single call to load. +type loader struct { + pkgs map[string]unsafe.Pointer + Config + sizes types.Sizes // non-nil if needed by mode + parseCache map[string]unsafe.Pointer + parseCacheMu sync.Mutex + exportMu sync.Mutex // enforces mutual exclusion of exportdata operations + + // Config.Mode contains the implied mode (see impliedLoadMode). + // Implied mode contains all the fields we need the data for. + // In requestedMode there are the actually requested fields. + // We'll zero them out before returning packages to the user. + // This makes it easier for us to get the conditions where + // we need certain modes right. + requestedMode LoadMode +} + +//go:linkname newLoader golang.org/x/tools/go/packages.newLoader +func newLoader(cfg *Config) *loader + +//go:linkname defaultDriver golang.org/x/tools/go/packages.defaultDriver +func defaultDriver(cfg *Config, patterns ...string) (*packages.DriverResponse, bool, error) + +//go:linkname refine golang.org/x/tools/go/packages.(*loader).refine +func refine(ld *loader, response *packages.DriverResponse) ([]*Package, error) + +// LoadEx loads and returns the Go packages named by the given patterns. +// +// Config specifies loading options; +// nil behaves the same as an empty Config. +// +// If any of the patterns was invalid as defined by the +// underlying build system, Load returns an error. +// It may return an empty list of packages without an error, +// for instance for an empty expansion of a valid wildcard. +// Errors associated with a particular package are recorded in the +// corresponding Package's Errors list, and do not cause Load to +// return an error. Clients may need to handle such errors before +// proceeding with further analysis. The PrintErrors function is +// provided for convenient display of all errors. +func LoadEx(sizes types.Sizes, cfg *Config, patterns ...string) ([]*Package, error) { + ld := newLoader(cfg) + response, external, err := defaultDriver(&ld.Config, patterns...) + if err != nil { + return nil, err + } + + if sizes == nil { + sizes = types.SizesFor(response.Compiler, response.Arch) + } + ld.sizes = sizes + if ld.sizes == nil && ld.Config.Mode&(NeedTypes|NeedTypesSizes|NeedTypesInfo) != 0 { + // Type size information is needed but unavailable. + if external { + // An external driver may fail to populate the Compiler/GOARCH fields, + // especially since they are relatively new (see #63700). + // Provide a sensible fallback in this case. + ld.sizes = types.SizesFor("gc", runtime.GOARCH) + if ld.sizes == nil { // gccgo-only arch + ld.sizes = types.SizesFor("gc", "amd64") + } + } else { + // Go list should never fail to deliver accurate size information. + // Reject the whole Load since the error is the same for every package. + return nil, fmt.Errorf("can't determine type sizes for compiler %q on GOARCH %q", + response.Compiler, response.Arch) + } + } + + return refine(ld, response) +} + +// Visit visits all the packages in the import graph whose roots are +// pkgs, calling the optional pre function the first time each package +// is encountered (preorder), and the optional post function after a +// package's dependencies have been visited (postorder). +// The boolean result of pre(pkg) determines whether +// the imports of package pkg are visited. +// +//go:linkname Visit golang.org/x/tools/go/packages.Visit +func Visit(pkgs []*Package, pre func(*Package) bool, post func(*Package)) diff --git a/ssa/type.go b/ssa/type.go index 6d0228a5..b112fdfb 100644 --- a/ssa/type.go +++ b/ssa/type.go @@ -91,6 +91,11 @@ func (t Type) RawType() types.Type { return t.raw.Type } +// TypeSizes returns the sizes of the types. +func (p Program) TypeSizes() types.Sizes { + return nil // TODO(xsw) +} + // TODO(xsw): // how to generate platform independent code? func (p Program) SizeOf(typ Type, n ...int64) uint64 {