Compare commits
40 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bc1821e7a5 | ||
|
|
3f49fe9e98 | ||
|
|
6aee76be72 | ||
|
|
a2c6e5d7fc | ||
|
|
cbaf9e21b2 | ||
|
|
81b3add443 | ||
|
|
c3e681a7b3 | ||
|
|
1567989142 | ||
|
|
5b73480540 | ||
|
|
b1342d8d97 | ||
|
|
2cbcc53c54 | ||
|
|
b4da61df86 | ||
|
|
edb7a4e1a5 | ||
|
|
0387a35112 | ||
|
|
aafe5a8600 | ||
|
|
67af68ae10 | ||
|
|
bc472a4fac | ||
|
|
2d8f5dbc51 | ||
|
|
9b7d5e2c57 | ||
|
|
f1e676a14f | ||
|
|
34d83813ec | ||
|
|
92b7d61b55 | ||
|
|
5d48e42069 | ||
|
|
ead09d94aa | ||
|
|
7240da07b4 | ||
|
|
72084b5648 | ||
|
|
2d75c55d36 | ||
|
|
da71e7c01b | ||
|
|
5343a55395 | ||
|
|
bbfe2a051c | ||
|
|
c1b0751ea5 | ||
|
|
2ee1f3373b | ||
|
|
088995088c | ||
|
|
b9ab96c89b | ||
|
|
576c276f33 | ||
|
|
0a879cb0be | ||
|
|
177ba6de48 | ||
|
|
4d1079261b | ||
|
|
f6ab8834fa | ||
|
|
2142e14b6d |
19
.github/dependabot.yml
vendored
Normal file
19
.github/dependabot.yml
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
# To get started with Dependabot version updates, you'll need to specify which
|
||||
# package ecosystems to update and where the package manifests are located.
|
||||
# Please see the documentation for all configuration options:
|
||||
# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
|
||||
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: github-actions
|
||||
directory: /
|
||||
labels:
|
||||
- dependabot
|
||||
- actions
|
||||
schedule:
|
||||
interval: daily
|
||||
|
||||
- package-ecosystem: "gomod" # See documentation for possible values
|
||||
directory: "/" # Location of package manifests
|
||||
schedule:
|
||||
interval: "daily"
|
||||
8
.github/workflows/go.yml
vendored
8
.github/workflows/go.yml
vendored
@@ -17,7 +17,7 @@ jobs:
|
||||
matrix:
|
||||
llvm: [17]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Update Homebrew
|
||||
if: matrix.llvm == 17 # needed as long as LLVM 17 is still fresh
|
||||
@@ -26,7 +26,7 @@ jobs:
|
||||
run: HOMEBREW_NO_AUTO_UPDATE=1 brew install llvm@${{ matrix.llvm }}
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v4
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: '1.20'
|
||||
|
||||
@@ -42,7 +42,7 @@ jobs:
|
||||
matrix:
|
||||
llvm: [17]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Install LLVM ${{ matrix.llvm }}
|
||||
run: |
|
||||
@@ -52,7 +52,7 @@ jobs:
|
||||
sudo apt-get install --no-install-recommends llvm-${{ matrix.llvm }}-dev
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v4
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: '1.20'
|
||||
|
||||
|
||||
@@ -3,8 +3,8 @@ llgo - A Go compiler based on LLVM
|
||||
|
||||
[](https://github.com/goplus/llgo/actions/workflows/go.yml)
|
||||
[](https://goreportcard.com/report/github.com/goplus/llgo)
|
||||
[](https://github.com/goplus/llgo/releases)
|
||||
[](https://pkg.go.dev/github.com/goplus/llgo)
|
||||
<!--
|
||||
[](https://github.com/goplus/llgo/releases)
|
||||
[](https://codecov.io/gh/goplus/llgo)
|
||||
-->
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
@@ -21,12 +21,12 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/goplus/llgo/x/llgen"
|
||||
"github.com/goplus/llgo/internal/llgen"
|
||||
)
|
||||
|
||||
func main() {
|
||||
if len(os.Args) != 2 {
|
||||
fmt.Fprintln(os.Stderr, "Usage: llgen xxx.go")
|
||||
if len(os.Args) < 2 {
|
||||
fmt.Fprintln(os.Stderr, "Usage: llgen xxx.go [pkgPath]")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -35,6 +35,13 @@ func main() {
|
||||
dir, _ := filepath.Split(inFile)
|
||||
outFile := dir + "out.ll"
|
||||
|
||||
pkgPath := ""
|
||||
if len(os.Args) == 3 {
|
||||
pkgPath = os.Args[2]
|
||||
} else {
|
||||
pkgPath = llgen.PkgPath(dir)
|
||||
}
|
||||
|
||||
llgen.Init()
|
||||
llgen.Do(inFile, outFile)
|
||||
llgen.Do(pkgPath, inFile, outFile)
|
||||
}
|
||||
|
||||
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 }
|
||||
8
cl/_testdata/apkg/in.go
Normal file
8
cl/_testdata/apkg/in.go
Normal file
@@ -0,0 +1,8 @@
|
||||
package apkg
|
||||
|
||||
func Max(a, b float64) float64 {
|
||||
if a > b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
29
cl/_testdata/apkg/out.ll
Normal file
29
cl/_testdata/apkg/out.ll
Normal file
@@ -0,0 +1,29 @@
|
||||
; ModuleID = 'apkg'
|
||||
source_filename = "apkg"
|
||||
|
||||
@"apkg.init$guard" = global ptr null
|
||||
|
||||
define void @apkg.init() {
|
||||
_llgo_0:
|
||||
%0 = load i1, ptr @"apkg.init$guard", align 1
|
||||
br i1 %0, label %_llgo_2, label %_llgo_1
|
||||
|
||||
_llgo_1: ; preds = %_llgo_0
|
||||
store i1 true, ptr @"apkg.init$guard", align 1
|
||||
br label %_llgo_2
|
||||
|
||||
_llgo_2: ; preds = %_llgo_1, %_llgo_0
|
||||
ret void
|
||||
}
|
||||
|
||||
define double @apkg.Max(double %0, double %1) {
|
||||
_llgo_0:
|
||||
%2 = fcmp ogt double %0, %1
|
||||
br i1 %2, label %_llgo_1, label %_llgo_2
|
||||
|
||||
_llgo_1: ; preds = %_llgo_0
|
||||
ret double %0
|
||||
|
||||
_llgo_2: ; preds = %_llgo_0
|
||||
ret double %1
|
||||
}
|
||||
@@ -1,22 +1,22 @@
|
||||
; ModuleID = 'main'
|
||||
source_filename = "main"
|
||||
|
||||
@"init$guard" = global ptr null
|
||||
@"main.init$guard" = global ptr null
|
||||
|
||||
define void @init() {
|
||||
define void @main.init() {
|
||||
_llgo_0:
|
||||
%0 = load i1, ptr @"init$guard", align 1
|
||||
%0 = load i1, ptr @"main.init$guard", align 1
|
||||
br i1 %0, label %_llgo_2, label %_llgo_1
|
||||
|
||||
_llgo_1: ; preds = %_llgo_0
|
||||
store i1 true, ptr @"init$guard", align 1
|
||||
store i1 true, ptr @"main.init$guard", align 1
|
||||
br label %_llgo_2
|
||||
|
||||
_llgo_2: ; preds = %_llgo_1, %_llgo_0
|
||||
ret void
|
||||
}
|
||||
|
||||
define i64 @max(i64 %0, i64 %1) {
|
||||
define i64 @main.max(i64 %0, i64 %1) {
|
||||
_llgo_0:
|
||||
%2 = icmp sgt i64 %0, %1
|
||||
br i1 %2, label %_llgo_1, label %_llgo_2
|
||||
@@ -30,7 +30,7 @@ _llgo_2: ; preds = %_llgo_0
|
||||
|
||||
define void @main() {
|
||||
_llgo_0:
|
||||
call void @init()
|
||||
%0 = call i64 @max(i64 1, i64 2)
|
||||
call void @main.init()
|
||||
%0 = call i64 @main.max(i64 1, i64 2)
|
||||
ret void
|
||||
}
|
||||
|
||||
10
cl/_testdata/importpkg/in.go
Normal file
10
cl/_testdata/importpkg/in.go
Normal file
@@ -0,0 +1,10 @@
|
||||
package main
|
||||
|
||||
import "github.com/goplus/llgo/cl/internal/stdio"
|
||||
|
||||
var hello = [...]int8{'H', 'e', 'l', 'l', 'o', '\n', 0}
|
||||
|
||||
func main() {
|
||||
_ = stdio.Max(2, 100)
|
||||
stdio.Printf(&hello[0])
|
||||
}
|
||||
40
cl/_testdata/importpkg/out.ll
Normal file
40
cl/_testdata/importpkg/out.ll
Normal file
@@ -0,0 +1,40 @@
|
||||
; ModuleID = 'main'
|
||||
source_filename = "main"
|
||||
|
||||
@"main.init$guard" = global ptr null
|
||||
@main.hello = global ptr null
|
||||
|
||||
define void @main.init() {
|
||||
_llgo_0:
|
||||
%0 = load i1, ptr @"main.init$guard", align 1
|
||||
br i1 %0, label %_llgo_2, label %_llgo_1
|
||||
|
||||
_llgo_1: ; preds = %_llgo_0
|
||||
store i1 true, ptr @"main.init$guard", align 1
|
||||
call void @"github.com/goplus/llgo/cl/internal/stdio.init"()
|
||||
store i8 72, ptr @main.hello, align 1
|
||||
store i8 101, ptr getelementptr inbounds (i8, ptr @main.hello, i64 1), align 1
|
||||
store i8 108, ptr getelementptr inbounds (i8, ptr @main.hello, i64 2), align 1
|
||||
store i8 108, ptr getelementptr inbounds (i8, ptr @main.hello, i64 3), align 1
|
||||
store i8 111, ptr getelementptr inbounds (i8, ptr @main.hello, i64 4), align 1
|
||||
store i8 10, ptr getelementptr inbounds (i8, ptr @main.hello, i64 5), align 1
|
||||
store i8 0, ptr getelementptr inbounds (i8, ptr @main.hello, i64 6), align 1
|
||||
br label %_llgo_2
|
||||
|
||||
_llgo_2: ; preds = %_llgo_1, %_llgo_0
|
||||
ret void
|
||||
}
|
||||
|
||||
define void @main() {
|
||||
_llgo_0:
|
||||
call void @main.init()
|
||||
%0 = call i64 @"github.com/goplus/llgo/cl/internal/stdio.Max"(i64 2, i64 100)
|
||||
call void (ptr, ...) @printf(ptr @main.hello)
|
||||
ret void
|
||||
}
|
||||
|
||||
declare void @"github.com/goplus/llgo/cl/internal/stdio.init"()
|
||||
|
||||
declare i64 @"github.com/goplus/llgo/cl/internal/stdio.Max"(i64, i64)
|
||||
|
||||
declare void @printf(ptr, ...)
|
||||
@@ -2,7 +2,7 @@ package main
|
||||
|
||||
import _ "unsafe"
|
||||
|
||||
//go:linkname printf _printf
|
||||
//go:linkname printf printf
|
||||
func printf(format *int8, __llgo_va_list ...any)
|
||||
|
||||
var hello = [...]int8{'H', 'e', 'l', 'l', 'o', '\n', 0}
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
; ModuleID = 'main'
|
||||
source_filename = "main"
|
||||
|
||||
@"init$guard" = global ptr null
|
||||
@hello = global ptr null
|
||||
@"main.init$guard" = global ptr null
|
||||
@main.hello = global ptr null
|
||||
|
||||
define void @init() {
|
||||
define void @main.init() {
|
||||
_llgo_0:
|
||||
%0 = load i1, ptr @"init$guard", align 1
|
||||
%0 = load i1, ptr @"main.init$guard", align 1
|
||||
br i1 %0, label %_llgo_2, label %_llgo_1
|
||||
|
||||
_llgo_1: ; preds = %_llgo_0
|
||||
store i1 true, ptr @"init$guard", align 1
|
||||
store i8 72, ptr @hello, align 1
|
||||
store i8 101, ptr getelementptr inbounds (i8, ptr @hello, i64 1), align 1
|
||||
store i8 108, ptr getelementptr inbounds (i8, ptr @hello, i64 2), align 1
|
||||
store i8 108, ptr getelementptr inbounds (i8, ptr @hello, i64 3), align 1
|
||||
store i8 111, ptr getelementptr inbounds (i8, ptr @hello, i64 4), align 1
|
||||
store i8 10, ptr getelementptr inbounds (i8, ptr @hello, i64 5), align 1
|
||||
store i8 0, ptr getelementptr inbounds (i8, ptr @hello, i64 6), align 1
|
||||
store i1 true, ptr @"main.init$guard", align 1
|
||||
store i8 72, ptr @main.hello, align 1
|
||||
store i8 101, ptr getelementptr inbounds (i8, ptr @main.hello, i64 1), align 1
|
||||
store i8 108, ptr getelementptr inbounds (i8, ptr @main.hello, i64 2), align 1
|
||||
store i8 108, ptr getelementptr inbounds (i8, ptr @main.hello, i64 3), align 1
|
||||
store i8 111, ptr getelementptr inbounds (i8, ptr @main.hello, i64 4), align 1
|
||||
store i8 10, ptr getelementptr inbounds (i8, ptr @main.hello, i64 5), align 1
|
||||
store i8 0, ptr getelementptr inbounds (i8, ptr @main.hello, i64 6), align 1
|
||||
br label %_llgo_2
|
||||
|
||||
_llgo_2: ; preds = %_llgo_1, %_llgo_0
|
||||
@@ -28,7 +28,7 @@ declare void @printf(ptr, ...)
|
||||
|
||||
define void @main() {
|
||||
_llgo_0:
|
||||
call void @init()
|
||||
call void (ptr, ...) @printf(ptr @hello)
|
||||
call void @main.init()
|
||||
call void (ptr, ...) @printf(ptr @main.hello)
|
||||
ret void
|
||||
}
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
; ModuleID = 'main'
|
||||
source_filename = "main"
|
||||
|
||||
@"init$guard" = global ptr null
|
||||
@a = global ptr null
|
||||
@"main.init$guard" = global ptr null
|
||||
@main.a = global ptr null
|
||||
|
||||
define void @init() {
|
||||
define void @main.init() {
|
||||
_llgo_0:
|
||||
%0 = load i1, ptr @"init$guard", align 1
|
||||
%0 = load i1, ptr @"main.init$guard", align 1
|
||||
br i1 %0, label %_llgo_2, label %_llgo_1
|
||||
|
||||
_llgo_1: ; preds = %_llgo_0
|
||||
store i1 true, ptr @"init$guard", align 1
|
||||
store i64 100, ptr @a, align 4
|
||||
store i1 true, ptr @"main.init$guard", align 1
|
||||
store i64 100, ptr @main.a, align 4
|
||||
br label %_llgo_2
|
||||
|
||||
_llgo_2: ; preds = %_llgo_1, %_llgo_0
|
||||
@@ -20,10 +20,10 @@ _llgo_2: ; preds = %_llgo_1, %_llgo_0
|
||||
|
||||
define void @main() {
|
||||
_llgo_0:
|
||||
call void @init()
|
||||
%0 = load i64, ptr @a, align 4
|
||||
call void @main.init()
|
||||
%0 = load i64, ptr @main.a, align 4
|
||||
%1 = add i64 %0, 1
|
||||
store i64 %1, ptr @a, align 4
|
||||
%2 = load i64, ptr @a, align 4
|
||||
store i64 %1, ptr @main.a, align 4
|
||||
%2 = load i64, ptr @main.a, align 4
|
||||
ret void
|
||||
}
|
||||
|
||||
@@ -18,6 +18,9 @@ package cl
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"log"
|
||||
"os"
|
||||
"sort"
|
||||
@@ -57,14 +60,15 @@ const (
|
||||
)
|
||||
|
||||
func funcKind(vfn ssa.Value) int {
|
||||
if fn, ok := vfn.(*ssa.Function); ok {
|
||||
n := len(fn.Params)
|
||||
if fn, ok := vfn.(*ssa.Function); ok && fn.Signature.Recv() == nil {
|
||||
params := fn.Signature.Params()
|
||||
n := params.Len()
|
||||
if n == 0 {
|
||||
if fn.Name() == "init" && fn.Pkg.Pkg.Path() == "unsafe" {
|
||||
return fnUnsafeInit
|
||||
}
|
||||
} else {
|
||||
last := fn.Params[n-1]
|
||||
last := params.At(n - 1)
|
||||
if last.Name() == llssa.NameValist {
|
||||
return fnHasVArg
|
||||
}
|
||||
@@ -73,24 +77,26 @@ func funcKind(vfn ssa.Value) int {
|
||||
return fnNormal
|
||||
}
|
||||
|
||||
func isMainFunc(fn *ssa.Function) bool {
|
||||
return fn.Name() == "main" && fn.Pkg.Pkg.Path() == "main"
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
type none = struct{}
|
||||
|
||||
type instrAndValue interface {
|
||||
ssa.Instruction
|
||||
ssa.Value
|
||||
}
|
||||
|
||||
type context struct {
|
||||
prog llssa.Program
|
||||
pkg llssa.Package
|
||||
fn llssa.Function
|
||||
goPkg *ssa.Package
|
||||
bvals map[ssa.Value]llssa.Expr // block values
|
||||
inits []func()
|
||||
prog llssa.Program
|
||||
pkg llssa.Package
|
||||
fn llssa.Function
|
||||
fset *token.FileSet
|
||||
goTyps *types.Package
|
||||
goPkg *ssa.Package
|
||||
link map[string]string // pkgPath.nameInPkg => linkname
|
||||
loaded map[*types.Package]none // loaded packages
|
||||
bvals map[ssa.Value]llssa.Expr // block values
|
||||
inits []func()
|
||||
}
|
||||
|
||||
func (p *context) compileType(pkg llssa.Package, member *ssa.Type) {
|
||||
@@ -99,7 +105,8 @@ func (p *context) compileType(pkg llssa.Package, member *ssa.Type) {
|
||||
|
||||
// Global variable.
|
||||
func (p *context) compileGlobal(pkg llssa.Package, gbl *ssa.Global) {
|
||||
name, typ := gbl.Name(), gbl.Type()
|
||||
typ := gbl.Type()
|
||||
name := fullName(gbl.Pkg.Pkg, gbl.Name())
|
||||
if debugInstr {
|
||||
log.Println("==> NewVar", name, typ)
|
||||
}
|
||||
@@ -108,7 +115,10 @@ func (p *context) compileGlobal(pkg llssa.Package, gbl *ssa.Global) {
|
||||
}
|
||||
|
||||
func (p *context) compileFunc(pkg llssa.Package, f *ssa.Function) {
|
||||
name := f.Name()
|
||||
name := p.funcName(f.Pkg.Pkg, f)
|
||||
if name == "unsafe.init" {
|
||||
return
|
||||
}
|
||||
if debugInstr {
|
||||
log.Println("==> NewFunc", name)
|
||||
}
|
||||
@@ -130,9 +140,8 @@ func (p *context) compileFunc(pkg llssa.Package, f *ssa.Function) {
|
||||
}
|
||||
fn.MakeBlocks(nblk)
|
||||
b := fn.NewBuilder()
|
||||
isMain := isMainFunc(f)
|
||||
for i, block := range f.Blocks {
|
||||
p.compileBlock(b, block, isMain && i == 0)
|
||||
p.compileBlock(b, block, i == 0 && name == "main")
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -142,7 +151,7 @@ func (p *context) compileBlock(b llssa.Builder, block *ssa.BasicBlock, doInit bo
|
||||
b.SetBlock(ret)
|
||||
p.bvals = make(map[ssa.Value]llssa.Expr)
|
||||
if doInit {
|
||||
fn := p.pkg.FuncOf("init")
|
||||
fn := p.pkg.FuncOf("main.init")
|
||||
b.Call(fn.Expr)
|
||||
}
|
||||
for _, instr := range block.Instrs {
|
||||
@@ -238,16 +247,10 @@ func (p *context) compileValue(b llssa.Builder, v ssa.Value) llssa.Expr {
|
||||
}
|
||||
}
|
||||
case *ssa.Function:
|
||||
if v.Pkg != p.goPkg {
|
||||
panic("todo")
|
||||
}
|
||||
fn := p.pkg.FuncOf(v.Name())
|
||||
fn := p.funcOf(v)
|
||||
return fn.Expr
|
||||
case *ssa.Global:
|
||||
if v.Pkg != p.goPkg {
|
||||
panic("todo")
|
||||
}
|
||||
g := p.pkg.VarOf(v.Name())
|
||||
g := p.varOf(v)
|
||||
return g.Expr
|
||||
case *ssa.Const:
|
||||
t := v.Type()
|
||||
@@ -281,11 +284,8 @@ func (p *context) compileValues(b llssa.Builder, vals []ssa.Value, hasVArg int)
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
type Config struct {
|
||||
}
|
||||
|
||||
// NewPackage compiles a Go package to LLVM IR package.
|
||||
func NewPackage(prog llssa.Program, pkg *ssa.Package, conf *Config) (ret llssa.Package, err error) {
|
||||
func NewPackage(prog llssa.Program, pkg *ssa.Package, files []*ast.File) (ret llssa.Package, err error) {
|
||||
type namedMember struct {
|
||||
name string
|
||||
val ssa.Member
|
||||
@@ -305,13 +305,19 @@ func NewPackage(prog llssa.Program, pkg *ssa.Package, conf *Config) (ret llssa.P
|
||||
})
|
||||
|
||||
pkgTypes := pkg.Pkg
|
||||
ret = prog.NewPackage(pkgTypes.Name(), pkgTypes.Path())
|
||||
pkgName, pkgPath := pkgTypes.Name(), pkgTypes.Path()
|
||||
ret = prog.NewPackage(pkgName, pkgPath)
|
||||
|
||||
ctx := &context{
|
||||
prog: prog,
|
||||
pkg: ret,
|
||||
goPkg: pkg,
|
||||
prog: prog,
|
||||
pkg: ret,
|
||||
fset: pkg.Prog.Fset,
|
||||
goTyps: pkgTypes,
|
||||
goPkg: pkg,
|
||||
link: make(map[string]string),
|
||||
loaded: make(map[*types.Package]none),
|
||||
}
|
||||
ctx.initFiles(pkgPath, files)
|
||||
for _, m := range members {
|
||||
member := m.val
|
||||
switch member := member.(type) {
|
||||
|
||||
@@ -18,7 +18,6 @@ package cl
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/importer"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"go/types"
|
||||
@@ -28,9 +27,11 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
llssa "github.com/goplus/llgo/ssa"
|
||||
"github.com/goplus/gogen/packages"
|
||||
"golang.org/x/tools/go/ssa"
|
||||
"golang.org/x/tools/go/ssa/ssautil"
|
||||
|
||||
llssa "github.com/goplus/llgo/ssa"
|
||||
)
|
||||
|
||||
func TestFromTestdata(t *testing.T) {
|
||||
@@ -88,14 +89,15 @@ func testCompileEx(t *testing.T, src any, fname, expected string) {
|
||||
files := []*ast.File{f}
|
||||
name := f.Name.Name
|
||||
pkg := types.NewPackage(name, name)
|
||||
imp := packages.NewImporter(fset)
|
||||
foo, _, err := ssautil.BuildPackage(
|
||||
&types.Config{Importer: importer.Default()}, fset, pkg, files, ssa.SanityCheckFunctions)
|
||||
&types.Config{Importer: imp}, fset, pkg, files, ssa.SanityCheckFunctions)
|
||||
if err != nil {
|
||||
t.Fatal("BuildPackage failed:", err)
|
||||
}
|
||||
foo.WriteTo(os.Stderr)
|
||||
prog := llssa.NewProgram(nil)
|
||||
ret, err := NewPackage(prog, foo, nil)
|
||||
ret, err := NewPackage(prog, foo, files)
|
||||
if err != nil {
|
||||
t.Fatal("cl.NewPackage failed:", err)
|
||||
}
|
||||
@@ -116,16 +118,16 @@ var a int
|
||||
`, `; ModuleID = 'foo'
|
||||
source_filename = "foo"
|
||||
|
||||
@"init$guard" = global ptr null
|
||||
@a = global ptr null
|
||||
@"foo.init$guard" = global ptr null
|
||||
@foo.a = global ptr null
|
||||
|
||||
define void @init() {
|
||||
define void @foo.init() {
|
||||
_llgo_0:
|
||||
%0 = load i1, ptr @"init$guard", align 1
|
||||
%0 = load i1, ptr @"foo.init$guard", align 1
|
||||
br i1 %0, label %_llgo_2, label %_llgo_1
|
||||
|
||||
_llgo_1: ; preds = %_llgo_0
|
||||
store i1 true, ptr @"init$guard", align 1
|
||||
store i1 true, ptr @"foo.init$guard", align 1
|
||||
br label %_llgo_2
|
||||
|
||||
_llgo_2: ; preds = %_llgo_1, %_llgo_0
|
||||
@@ -143,22 +145,22 @@ func fn(a int, b float64) int {
|
||||
`, `; ModuleID = 'foo'
|
||||
source_filename = "foo"
|
||||
|
||||
@"init$guard" = global ptr null
|
||||
@"foo.init$guard" = global ptr null
|
||||
|
||||
define void @init() {
|
||||
define void @foo.init() {
|
||||
_llgo_0:
|
||||
%0 = load i1, ptr @"init$guard", align 1
|
||||
%0 = load i1, ptr @"foo.init$guard", align 1
|
||||
br i1 %0, label %_llgo_2, label %_llgo_1
|
||||
|
||||
_llgo_1: ; preds = %_llgo_0
|
||||
store i1 true, ptr @"init$guard", align 1
|
||||
store i1 true, ptr @"foo.init$guard", align 1
|
||||
br label %_llgo_2
|
||||
|
||||
_llgo_2: ; preds = %_llgo_1, %_llgo_0
|
||||
ret void
|
||||
}
|
||||
|
||||
define i64 @fn(i64 %0, double %1) {
|
||||
define i64 @foo.fn(i64 %0, double %1) {
|
||||
_llgo_0:
|
||||
ret i64 1
|
||||
}
|
||||
|
||||
160
cl/import.go
Normal file
160
cl/import.go
Normal file
@@ -0,0 +1,160 @@
|
||||
/*
|
||||
* 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 cl
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
llssa "github.com/goplus/llgo/ssa"
|
||||
"golang.org/x/tools/go/ssa"
|
||||
)
|
||||
|
||||
type contentLines = [][]byte
|
||||
type contentMap = map[string]contentLines
|
||||
|
||||
func contentOf(m contentMap, file string) (lines contentLines, err error) {
|
||||
if v, ok := m[file]; ok {
|
||||
return v, nil
|
||||
}
|
||||
b, err := os.ReadFile(file)
|
||||
if err == nil {
|
||||
lines = bytes.Split(b, []byte{'\n'})
|
||||
m[file] = lines
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (p *context) importPkg(pkg *types.Package) {
|
||||
scope := pkg.Scope()
|
||||
if scope.Lookup("LLGoPackage") == nil {
|
||||
return
|
||||
}
|
||||
fset := p.fset
|
||||
names := scope.Names()
|
||||
contents := make(contentMap)
|
||||
pkgPath := pkg.Path()
|
||||
for _, name := range names {
|
||||
if token.IsExported(name) {
|
||||
obj := scope.Lookup(name)
|
||||
if obj, ok := obj.(*types.Func); ok {
|
||||
if pos := obj.Pos(); pos != token.NoPos {
|
||||
f := fset.File(pos)
|
||||
if fp := f.Position(pos); fp.Line > 2 {
|
||||
lines, err := contentOf(contents, fp.Filename)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if i := fp.Line - 2; i < len(lines) {
|
||||
line := string(lines[i])
|
||||
p.initLinkname(pkgPath, line)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (p *context) initFiles(pkgPath string, files []*ast.File) {
|
||||
for _, file := range files {
|
||||
for _, decl := range file.Decls {
|
||||
if decl, ok := decl.(*ast.FuncDecl); ok {
|
||||
if decl.Recv == nil {
|
||||
if doc := decl.Doc; doc != nil {
|
||||
if n := len(doc.List); n > 0 {
|
||||
line := doc.List[n-1].Text
|
||||
p.initLinkname(pkgPath, line)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (p *context) initLinkname(pkgPath, line string) {
|
||||
const (
|
||||
linkname = "//go:linkname "
|
||||
)
|
||||
if strings.HasPrefix(line, linkname) {
|
||||
text := strings.TrimSpace(line[len(linkname):])
|
||||
if idx := strings.IndexByte(text, ' '); idx > 0 {
|
||||
name := pkgPath + "." + text[:idx]
|
||||
link := strings.TrimLeft(text[idx+1:], " ")
|
||||
p.link[name] = link
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func fullName(pkg *types.Package, name string) string {
|
||||
pkgPath := pkg.Name()
|
||||
if pkgPath != "main" {
|
||||
pkgPath = pkg.Path()
|
||||
}
|
||||
return pkgPath + "." + name
|
||||
}
|
||||
|
||||
func funcName(pkg *types.Package, fn *ssa.Function) string {
|
||||
ret := fullName(pkg, fn.Name())
|
||||
if ret == "main.main" {
|
||||
ret = "main"
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func (p *context) funcName(pkg *types.Package, fn *ssa.Function) string {
|
||||
name := funcName(pkg, fn)
|
||||
if v, ok := p.link[name]; ok {
|
||||
return v
|
||||
}
|
||||
return name
|
||||
}
|
||||
|
||||
func (p *context) funcOf(fn *ssa.Function) llssa.Function {
|
||||
pkgTypes := p.ensureLoaded(fn.Pkg.Pkg)
|
||||
pkg := p.pkg
|
||||
name := p.funcName(pkgTypes, fn)
|
||||
if ret := pkg.FuncOf(name); ret != nil {
|
||||
return ret
|
||||
}
|
||||
return pkg.NewFunc(name, fn.Signature)
|
||||
}
|
||||
|
||||
func (p *context) varOf(v *ssa.Global) llssa.Global {
|
||||
pkgTypes := p.ensureLoaded(v.Pkg.Pkg)
|
||||
pkg := p.pkg
|
||||
name := fullName(pkgTypes, v.Name())
|
||||
if ret := pkg.VarOf(name); ret != nil {
|
||||
return ret
|
||||
}
|
||||
return pkg.NewVar(name, v.Type())
|
||||
}
|
||||
|
||||
func (p *context) ensureLoaded(pkgTypes *types.Package) *types.Package {
|
||||
if p.goTyps != pkgTypes {
|
||||
if _, ok := p.loaded[pkgTypes]; !ok {
|
||||
p.loaded[pkgTypes] = none{}
|
||||
p.importPkg(pkgTypes)
|
||||
}
|
||||
}
|
||||
return pkgTypes
|
||||
}
|
||||
17
cl/internal/stdio/printf.go
Normal file
17
cl/internal/stdio/printf.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package stdio
|
||||
|
||||
import _ "unsafe"
|
||||
|
||||
const (
|
||||
LLGoPackage = true
|
||||
)
|
||||
|
||||
//go:linkname Printf printf
|
||||
func Printf(format *int8, __llgo_va_list ...any)
|
||||
|
||||
func Max(a, b int) int {
|
||||
if a > b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
@@ -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.
|
||||
@@ -14,93 +14,27 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// Package build implements the “llgo build” command.
|
||||
// Package build implements the "llgo build" command.
|
||||
package build
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
|
||||
"github.com/goplus/gop"
|
||||
"github.com/goplus/gop/x/gopprojs"
|
||||
"github.com/goplus/llgo"
|
||||
"github.com/goplus/llgo/cmd/internal/base"
|
||||
"github.com/goplus/llgo/x/gocmd"
|
||||
"github.com/goplus/llgo/internal/build"
|
||||
)
|
||||
|
||||
// llgo build
|
||||
var Cmd = &base.Command{
|
||||
UsageLine: "llgo build [flags] [packages]",
|
||||
Short: "Build Go files",
|
||||
UsageLine: "llgo build [-o output] [build flags] [packages]",
|
||||
Short: "Compile packages and dependencies",
|
||||
}
|
||||
|
||||
var (
|
||||
flagOutput = flag.String("o", "", "build output file")
|
||||
_ = flag.Bool("v", false, "print verbose information")
|
||||
flag = &Cmd.Flag
|
||||
)
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
args = flag.Args()
|
||||
if len(args) == 0 {
|
||||
args = []string{"."}
|
||||
}
|
||||
|
||||
proj, args, err := gopprojs.ParseOne(args...)
|
||||
if err != nil {
|
||||
log.Panicln(err)
|
||||
}
|
||||
if len(args) != 0 {
|
||||
log.Panicln("too many arguments:", args)
|
||||
}
|
||||
|
||||
conf := &llgo.Config{}
|
||||
confCmd := &gocmd.BuildConfig{}
|
||||
if *flagOutput != "" {
|
||||
output, err := filepath.Abs(*flagOutput)
|
||||
if err != nil {
|
||||
log.Panicln(err)
|
||||
}
|
||||
confCmd.Output = output
|
||||
}
|
||||
build(proj, conf, confCmd)
|
||||
build.Do(args, &build.Config{
|
||||
Mode: build.ModeBuild,
|
||||
AppExt: build.DefaultAppExt(),
|
||||
})
|
||||
}
|
||||
|
||||
func build(proj gopprojs.Proj, conf *llgo.Config, build *gocmd.BuildConfig) {
|
||||
var obj string
|
||||
var err error
|
||||
switch v := proj.(type) {
|
||||
case *gopprojs.DirProj:
|
||||
obj = v.Dir
|
||||
err = llgo.BuildDir(obj, conf, build)
|
||||
case *gopprojs.PkgPathProj:
|
||||
obj = v.Path
|
||||
err = llgo.BuildPkgPath("", obj, conf, build)
|
||||
case *gopprojs.FilesProj:
|
||||
err = llgo.BuildFiles(v.Files, conf, build)
|
||||
default:
|
||||
log.Panicln("`llgo build` doesn't support", reflect.TypeOf(v))
|
||||
}
|
||||
if gop.NotFound(err) {
|
||||
fmt.Fprintf(os.Stderr, "llgo build %v: not found\n", obj)
|
||||
} else if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
} else {
|
||||
return
|
||||
}
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
@@ -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.
|
||||
@@ -14,11 +14,25 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package llgo
|
||||
// Package install implements the "llgo install" command.
|
||||
package install
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
import (
|
||||
"github.com/goplus/llgo/cmd/internal/base"
|
||||
"github.com/goplus/llgo/internal/build"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
// llgo install
|
||||
var Cmd = &base.Command{
|
||||
UsageLine: "llgo install [build flags] [packages]",
|
||||
Short: "Compile and install packages and dependencies",
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
func init() {
|
||||
Cmd.Run = runCmd
|
||||
}
|
||||
|
||||
func runCmd(cmd *base.Command, args []string) {
|
||||
conf := build.NewDefaultConf(build.ModeInstall)
|
||||
build.Do(args, conf)
|
||||
}
|
||||
85
cmd/internal/run/run.go
Normal file
85
cmd/internal/run/run.go
Normal file
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* 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 run implements the "llgo run" command.
|
||||
package run
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/goplus/llgo/cmd/internal/base"
|
||||
"github.com/goplus/llgo/internal/build"
|
||||
)
|
||||
|
||||
var (
|
||||
errNoProj = errors.New("llgo: no go files listed")
|
||||
)
|
||||
|
||||
// llgo run
|
||||
var Cmd = &base.Command{
|
||||
UsageLine: "llgo run [build flags] package [arguments...]",
|
||||
Short: "Compile and run Go program",
|
||||
}
|
||||
|
||||
func init() {
|
||||
Cmd.Run = runCmd
|
||||
}
|
||||
|
||||
func runCmd(cmd *base.Command, args []string) {
|
||||
args, runArgs, err := parseRunArgs(args)
|
||||
check(err)
|
||||
conf := build.NewDefaultConf(build.ModeRun)
|
||||
conf.RunArgs = runArgs
|
||||
build.Do(args, conf)
|
||||
}
|
||||
|
||||
func parseRunArgs(args []string) ([]string, []string, error) {
|
||||
n := parseArgs(args)
|
||||
if n < 0 {
|
||||
return nil, nil, errNoProj
|
||||
}
|
||||
|
||||
arg := args[n]
|
||||
if isGoFile(arg) {
|
||||
n++
|
||||
for n < len(args) && isGoFile(args[n]) {
|
||||
n++
|
||||
}
|
||||
return args[:n], args[n:], nil
|
||||
}
|
||||
return args[:n+1], args[n+1:], nil
|
||||
}
|
||||
|
||||
func parseArgs(args []string) int {
|
||||
for i, arg := range args {
|
||||
if !strings.HasPrefix(arg, "-") {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
func isGoFile(fname string) bool {
|
||||
return filepath.Ext(fname) == ".go"
|
||||
}
|
||||
|
||||
func check(err error) {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
@@ -27,6 +27,8 @@ import (
|
||||
"github.com/goplus/llgo/cmd/internal/base"
|
||||
"github.com/goplus/llgo/cmd/internal/build"
|
||||
"github.com/goplus/llgo/cmd/internal/help"
|
||||
"github.com/goplus/llgo/cmd/internal/install"
|
||||
"github.com/goplus/llgo/cmd/internal/run"
|
||||
)
|
||||
|
||||
func mainUsage() {
|
||||
@@ -38,6 +40,8 @@ func init() {
|
||||
flag.Usage = mainUsage
|
||||
base.Llgo.Commands = []*base.Command{
|
||||
build.Cmd,
|
||||
install.Cmd,
|
||||
run.Cmd,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
10
go.mod
10
go.mod
@@ -4,14 +4,14 @@ go 1.18
|
||||
|
||||
require (
|
||||
github.com/aykevl/go-wasm v0.0.1
|
||||
github.com/goplus/gop v1.2.6
|
||||
github.com/goplus/llvm v0.7.1-0.20240420180312-6230a4ea7a47
|
||||
github.com/goplus/gogen v1.15.2
|
||||
github.com/goplus/llvm v0.7.1
|
||||
github.com/goplus/mod v0.13.10
|
||||
github.com/qiniu/x v1.13.10
|
||||
golang.org/x/tools v0.19.0
|
||||
golang.org/x/tools v0.20.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/goplus/gogen v1.15.2 // indirect
|
||||
github.com/goplus/mod v0.13.10 // indirect
|
||||
golang.org/x/mod v0.17.0 // indirect
|
||||
golang.org/x/sync v0.7.0 // indirect
|
||||
)
|
||||
|
||||
15
go.sum
15
go.sum
@@ -2,12 +2,8 @@ github.com/aykevl/go-wasm v0.0.1 h1:lPxy8l48P39W7I0tLrtCrLfZBOUq9IWZ7odGdyJP2AM=
|
||||
github.com/aykevl/go-wasm v0.0.1/go.mod h1:b4nggwg3lEkNKOU4wzhtLKz2q2sLxSHFnc98aGt6z/Y=
|
||||
github.com/goplus/gogen v1.15.2 h1:Q6XaSx/Zi5tWnjfAziYsQI6Jv6MgODRpFtOYqNkiiqM=
|
||||
github.com/goplus/gogen v1.15.2/go.mod h1:92qEzVgv7y8JEFICWG9GvYI5IzfEkxYdsA1DbmnTkqk=
|
||||
github.com/goplus/gop v1.2.6 h1:kog3c5Js+8EopqmI4+CwueXsqibnBwYVt5q5N7juRVY=
|
||||
github.com/goplus/gop v1.2.6/go.mod h1:uREWbR1MrFaviZ4Mbx4ZCcAYDoqzO0iv1Qo6Np0Xx4E=
|
||||
github.com/goplus/llvm v0.7.1-0.20240418160956-6233231cbcc9 h1:E/NBN5tDh6COcJmygdBb9RAJhE4uIHfT51VBlP3tglU=
|
||||
github.com/goplus/llvm v0.7.1-0.20240418160956-6233231cbcc9/go.mod h1:PeVK8GgzxwAYCiMiUAJb5wJR6xbhj989tu9oulKLLT4=
|
||||
github.com/goplus/llvm v0.7.1-0.20240420180312-6230a4ea7a47 h1:B3nWTLOQh4+Yqt6NryE/cVQdo/+NLiT8AtD4YaeKScg=
|
||||
github.com/goplus/llvm v0.7.1-0.20240420180312-6230a4ea7a47/go.mod h1:PeVK8GgzxwAYCiMiUAJb5wJR6xbhj989tu9oulKLLT4=
|
||||
github.com/goplus/llvm v0.7.1 h1:B12Fr/wc3pAsq5PLuac9u9IuKpLRuCufdVAeGDP/MRw=
|
||||
github.com/goplus/llvm v0.7.1/go.mod h1:PeVK8GgzxwAYCiMiUAJb5wJR6xbhj989tu9oulKLLT4=
|
||||
github.com/goplus/mod v0.13.10 h1:5Om6KOvo31daN7N30kWU1vC5zhsJPM+uPbcEN/FnlzE=
|
||||
github.com/goplus/mod v0.13.10/go.mod h1:HDuPZgpWiaTp3PUolFgsiX+Q77cbUWB/mikVHfYND3c=
|
||||
github.com/qiniu/x v1.13.10 h1:J4Z3XugYzAq85SlyAfqlKVrbf05glMbAOh+QncsDQpE=
|
||||
@@ -31,7 +27,8 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
|
||||
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
|
||||
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
@@ -56,6 +53,6 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
|
||||
golang.org/x/tools v0.19.0 h1:tfGCXNR1OsFG+sVdLAitlpjAvD/I6dHDKnYrpEZUHkw=
|
||||
golang.org/x/tools v0.19.0/go.mod h1:qoJWxmGSIBmAeriMx19ogtrEPrGtDbPK634QFIcLAhc=
|
||||
golang.org/x/tools v0.20.0 h1:hz/CVckiOxybQvFw6h7b/q80NTr9IUQb4s1IIzW7KNY=
|
||||
golang.org/x/tools v0.20.0/go.mod h1:WvitBU7JJf6A4jOdg4S1tviW9bhUxkgeCui/0JHctQg=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
|
||||
216
internal/build/build.go
Normal file
216
internal/build/build.go
Normal file
@@ -0,0 +1,216 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/token"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/tools/go/packages"
|
||||
"golang.org/x/tools/go/ssa"
|
||||
|
||||
"github.com/goplus/llgo/cl"
|
||||
"github.com/goplus/llgo/x/clang"
|
||||
|
||||
llssa "github.com/goplus/llgo/ssa"
|
||||
)
|
||||
|
||||
type Mode int
|
||||
|
||||
const (
|
||||
ModeBuild Mode = iota
|
||||
ModeInstall
|
||||
ModeRun
|
||||
)
|
||||
|
||||
func needLLFile(mode Mode) bool {
|
||||
return mode != ModeBuild
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
BinPath string
|
||||
AppExt string // ".exe" on Windows, empty on Unix
|
||||
RunArgs []string
|
||||
Mode Mode
|
||||
}
|
||||
|
||||
func NewDefaultConf(mode Mode) *Config {
|
||||
bin := os.Getenv("GOBIN")
|
||||
if bin == "" {
|
||||
bin = filepath.Join(runtime.GOROOT(), "bin")
|
||||
}
|
||||
conf := &Config{
|
||||
BinPath: bin,
|
||||
Mode: mode,
|
||||
AppExt: DefaultAppExt(),
|
||||
}
|
||||
return conf
|
||||
}
|
||||
|
||||
func DefaultAppExt() string {
|
||||
if runtime.GOOS == "windows" {
|
||||
return ".exe"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
const (
|
||||
loadFiles = packages.NeedName | packages.NeedFiles | packages.NeedCompiledGoFiles
|
||||
loadImports = loadFiles | packages.NeedImports
|
||||
loadTypes = loadImports | packages.NeedTypes | packages.NeedTypesSizes
|
||||
loadSyntax = loadTypes | packages.NeedSyntax | packages.NeedTypesInfo
|
||||
)
|
||||
|
||||
func Do(args []string, conf *Config) {
|
||||
flags, patterns, verbose := parseArgs(args)
|
||||
cfg := &packages.Config{
|
||||
Mode: loadSyntax | packages.NeedDeps | packages.NeedExportFile,
|
||||
BuildFlags: flags,
|
||||
}
|
||||
|
||||
if patterns == nil {
|
||||
patterns = []string{"."}
|
||||
}
|
||||
initial, err := packages.Load(cfg, patterns...)
|
||||
check(err)
|
||||
|
||||
// Create SSA-form program representation.
|
||||
ssaProg, pkgs, errPkgs := allPkgs(initial, ssa.SanityCheckFunctions)
|
||||
ssaProg.Build()
|
||||
for _, errPkg := range errPkgs {
|
||||
log.Println("cannot build SSA for package", errPkg)
|
||||
}
|
||||
|
||||
llssa.Initialize(llssa.InitAll)
|
||||
if verbose {
|
||||
llssa.SetDebug(llssa.DbgFlagAll)
|
||||
cl.SetDebug(cl.DbgFlagAll)
|
||||
}
|
||||
|
||||
prog := llssa.NewProgram(nil)
|
||||
mode := conf.Mode
|
||||
for _, pkg := range pkgs {
|
||||
buildPkg(prog, pkg, mode)
|
||||
}
|
||||
|
||||
if mode != ModeBuild || len(initial) == 1 {
|
||||
for _, pkg := range initial {
|
||||
if pkg.Name == "main" {
|
||||
linkMainPkg(pkg, conf, mode)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func linkMainPkg(pkg *packages.Package, conf *Config, mode Mode) {
|
||||
pkgPath := pkg.PkgPath
|
||||
name := path.Base(pkgPath)
|
||||
app := filepath.Join(conf.BinPath, name+conf.AppExt)
|
||||
args := make([]string, 2, len(pkg.Imports)+3)
|
||||
args[0] = "-o"
|
||||
args[1] = app
|
||||
packages.Visit([]*packages.Package{pkg}, nil, func(p *packages.Package) {
|
||||
if p.PkgPath != "unsafe" { // TODO(xsw): remove this special case
|
||||
args = append(args, p.ExportFile+".ll")
|
||||
}
|
||||
})
|
||||
|
||||
// TODO(xsw): show work
|
||||
// fmt.Fprintln(os.Stderr, "clang", args)
|
||||
fmt.Fprintln(os.Stderr, "#", pkgPath)
|
||||
err := clang.New("").Exec(args...)
|
||||
check(err)
|
||||
|
||||
if mode == ModeRun {
|
||||
cmd := exec.Command(app, conf.RunArgs...)
|
||||
cmd.Stdin = os.Stdin
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
cmd.Run()
|
||||
}
|
||||
}
|
||||
|
||||
func buildPkg(prog llssa.Program, aPkg aPackage, mode Mode) {
|
||||
pkg := aPkg.Package
|
||||
pkgPath := pkg.PkgPath
|
||||
if mode != ModeRun {
|
||||
fmt.Fprintln(os.Stderr, pkgPath)
|
||||
}
|
||||
if pkgPath == "unsafe" { // TODO(xsw): remove this special case
|
||||
return
|
||||
}
|
||||
ret, err := cl.NewPackage(prog, aPkg.SSA, pkg.Syntax)
|
||||
check(err)
|
||||
if needLLFile(mode) {
|
||||
file := pkg.ExportFile + ".ll"
|
||||
os.WriteFile(file, []byte(ret.String()), 0644)
|
||||
}
|
||||
}
|
||||
|
||||
type aPackage struct {
|
||||
*packages.Package
|
||||
SSA *ssa.Package
|
||||
}
|
||||
|
||||
func allPkgs(initial []*packages.Package, mode ssa.BuilderMode) (prog *ssa.Program, all []aPackage, errs []*packages.Package) {
|
||||
var fset *token.FileSet
|
||||
if len(initial) > 0 {
|
||||
fset = initial[0].Fset
|
||||
}
|
||||
|
||||
prog = ssa.NewProgram(fset, mode)
|
||||
packages.Visit(initial, nil, func(p *packages.Package) {
|
||||
if p.Types != nil && !p.IllTyped {
|
||||
ssaPkg := prog.CreatePackage(p.Types, p.Syntax, p.TypesInfo, true)
|
||||
all = append(all, aPackage{p, ssaPkg})
|
||||
} else {
|
||||
errs = append(errs, p)
|
||||
}
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
func parseArgs(args []string) (flags, patterns []string, verbose bool) {
|
||||
for i, arg := range args {
|
||||
if !strings.HasPrefix(arg, "-") {
|
||||
flags, patterns = args[:i], args[i:]
|
||||
return
|
||||
}
|
||||
if arg == "-v" {
|
||||
verbose = true
|
||||
}
|
||||
}
|
||||
flags = args
|
||||
return
|
||||
}
|
||||
|
||||
func check(err error) {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
@@ -18,13 +18,14 @@ package llgen
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/importer"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"os"
|
||||
|
||||
"github.com/goplus/gogen/packages"
|
||||
"github.com/goplus/llgo/cl"
|
||||
"github.com/goplus/llgo/internal/mod"
|
||||
"golang.org/x/tools/go/ssa"
|
||||
"golang.org/x/tools/go/ssa/ssautil"
|
||||
|
||||
@@ -37,28 +38,38 @@ func Init() {
|
||||
cl.SetDebug(cl.DbgFlagAll)
|
||||
}
|
||||
|
||||
func Do(inFile, outFile string) {
|
||||
ret := Gen(inFile, nil)
|
||||
func PkgPath(dir string) string {
|
||||
_, pkgPath, err := mod.Load(dir)
|
||||
check(err)
|
||||
return pkgPath
|
||||
}
|
||||
|
||||
func Do(pkgPath, inFile, outFile string) {
|
||||
ret := Gen(pkgPath, inFile, nil)
|
||||
err := os.WriteFile(outFile, []byte(ret), 0644)
|
||||
check(err)
|
||||
}
|
||||
|
||||
func Gen(inFile string, src any) string {
|
||||
func Gen(pkgPath, inFile string, src any) string {
|
||||
fset := token.NewFileSet()
|
||||
f, err := parser.ParseFile(fset, inFile, src, parser.ParseComments)
|
||||
check(err)
|
||||
|
||||
files := []*ast.File{f}
|
||||
name := f.Name.Name
|
||||
pkg := types.NewPackage(name, name)
|
||||
if pkgPath == "" {
|
||||
pkgPath = name
|
||||
}
|
||||
pkg := types.NewPackage(pkgPath, name)
|
||||
imp := packages.NewImporter(fset)
|
||||
ssaPkg, _, err := ssautil.BuildPackage(
|
||||
&types.Config{Importer: importer.Default()}, fset, pkg, files, ssa.SanityCheckFunctions)
|
||||
&types.Config{Importer: imp}, fset, pkg, files, ssa.SanityCheckFunctions)
|
||||
check(err)
|
||||
|
||||
ssaPkg.WriteTo(os.Stderr)
|
||||
|
||||
prog := llssa.NewProgram(nil)
|
||||
ret, err := cl.NewPackage(prog, ssaPkg, nil)
|
||||
ret, err := cl.NewPackage(prog, ssaPkg, files)
|
||||
check(err)
|
||||
|
||||
return ret.String()
|
||||
48
internal/mod/mod.go
Normal file
48
internal/mod/mod.go
Normal file
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* 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 mod
|
||||
|
||||
import (
|
||||
"path"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/goplus/mod"
|
||||
"github.com/goplus/mod/gopmod"
|
||||
)
|
||||
|
||||
// Module represents a Go module.
|
||||
type Module = gopmod.Module
|
||||
|
||||
// Load loads a Go module from a directory.
|
||||
func Load(dir string) (ret *Module, pkgPath string, err error) {
|
||||
if dir, err = filepath.Abs(dir); err != nil {
|
||||
return
|
||||
}
|
||||
_, gomod, err := mod.FindGoMod(dir)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if ret, err = gopmod.LoadFrom(gomod, ""); err != nil {
|
||||
return
|
||||
}
|
||||
relPath, err := filepath.Rel(ret.Root(), dir)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
pkgPath = path.Join(ret.Path(), filepath.ToSlash(relPath))
|
||||
return
|
||||
}
|
||||
110
internal/projs/proj.go
Normal file
110
internal/projs/proj.go
Normal file
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* 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 projs
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"path/filepath"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
type Proj = interface {
|
||||
projObj()
|
||||
}
|
||||
|
||||
type FilesProj struct {
|
||||
Files []string
|
||||
}
|
||||
|
||||
type PkgPathProj struct {
|
||||
Path string
|
||||
}
|
||||
|
||||
type DirProj struct {
|
||||
Dir string
|
||||
}
|
||||
|
||||
func (p *FilesProj) projObj() {}
|
||||
func (p *PkgPathProj) projObj() {}
|
||||
func (p *DirProj) projObj() {}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
func ParseOne(args ...string) (proj Proj, next []string, err error) {
|
||||
if len(args) == 0 {
|
||||
return nil, nil, syscall.ENOENT
|
||||
}
|
||||
arg := args[0]
|
||||
if isFile(arg) {
|
||||
n := 1
|
||||
for n < len(args) && isFile(args[n]) {
|
||||
n++
|
||||
}
|
||||
return &FilesProj{Files: args[:n]}, args[n:], nil
|
||||
}
|
||||
if isLocal(arg) {
|
||||
return &DirProj{Dir: arg}, args[1:], nil
|
||||
}
|
||||
return &PkgPathProj{Path: arg}, args[1:], nil
|
||||
}
|
||||
|
||||
func isFile(fname string) bool {
|
||||
n := len(filepath.Ext(fname))
|
||||
return n > 1
|
||||
}
|
||||
|
||||
func isLocal(ns string) bool {
|
||||
if len(ns) > 0 {
|
||||
switch c := ns[0]; c {
|
||||
case '/', '\\', '.':
|
||||
return true
|
||||
default:
|
||||
return len(ns) >= 2 && ns[1] == ':' && ('A' <= c && c <= 'Z' || 'a' <= c && c <= 'z')
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
func ParseAll(args ...string) (projs []Proj, err error) {
|
||||
var hasFiles, hasNotFiles bool
|
||||
for {
|
||||
proj, next, e := ParseOne(args...)
|
||||
if e != nil {
|
||||
if hasFiles && hasNotFiles {
|
||||
return nil, ErrMixedFilesProj
|
||||
}
|
||||
return
|
||||
}
|
||||
if _, ok := proj.(*FilesProj); ok {
|
||||
hasFiles = true
|
||||
} else {
|
||||
hasNotFiles = true
|
||||
}
|
||||
projs = append(projs, proj)
|
||||
args = next
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
ErrMixedFilesProj = errors.New("mixed files project")
|
||||
)
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
@@ -230,7 +230,7 @@ func (b Builder) BinOp(op token.Token, x, y Expr) Expr {
|
||||
return Expr{llvm.CreateICmp(b.impl, pred, x.impl, y.impl), tret}
|
||||
case vkFloat:
|
||||
pred := floatPredOpToLLVM[op-predOpBase]
|
||||
return Expr{llvm.ConstFCmp(pred, x.impl, y.impl), tret}
|
||||
return Expr{llvm.CreateFCmp(b.impl, pred, x.impl, y.impl), tret}
|
||||
case vkString, vkComplex, vkBool:
|
||||
panic("todo")
|
||||
}
|
||||
|
||||
@@ -87,6 +87,9 @@ func TestNamedStruct(t *testing.T) {
|
||||
prog := NewProgram(nil)
|
||||
pkg := prog.NewPackage("bar", "foo/bar")
|
||||
pkg.NewVar("a", empty)
|
||||
if pkg.VarOf("a") == nil {
|
||||
t.Fatal("VarOf failed")
|
||||
}
|
||||
assertPkg(t, pkg, `; ModuleID = 'foo/bar'
|
||||
source_filename = "foo/bar"
|
||||
|
||||
@@ -102,6 +105,12 @@ func TestDeclFunc(t *testing.T) {
|
||||
params := types.NewTuple(types.NewVar(0, nil, "a", types.Typ[types.Int]))
|
||||
sig := types.NewSignatureType(nil, nil, nil, params, nil, false)
|
||||
pkg.NewFunc("fn", sig)
|
||||
if pkg.FuncOf("fn") == nil {
|
||||
t.Fatal("FuncOf failed")
|
||||
}
|
||||
if prog.retType(sig) != prog.Void() {
|
||||
t.Fatal("retType failed")
|
||||
}
|
||||
assertPkg(t, pkg, `; ModuleID = 'foo/bar'
|
||||
source_filename = "foo/bar"
|
||||
|
||||
|
||||
@@ -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.
|
||||
@@ -14,24 +14,33 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package llgo
|
||||
package clang
|
||||
|
||||
import (
|
||||
"github.com/goplus/llgo/x/gocmd"
|
||||
"os"
|
||||
"os/exec"
|
||||
)
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
func BuildDir(dir string, conf *Config, build *gocmd.BuildConfig) (err error) {
|
||||
panic("todo")
|
||||
// Cmd represents a nm command.
|
||||
type Cmd struct {
|
||||
app string
|
||||
}
|
||||
|
||||
func BuildPkgPath(workDir, pkgPath string, conf *Config, build *gocmd.BuildConfig) (err error) {
|
||||
panic("todo")
|
||||
// New creates a new nm command.
|
||||
func New(app string) *Cmd {
|
||||
if app == "" {
|
||||
app = "clang"
|
||||
}
|
||||
return &Cmd{app}
|
||||
}
|
||||
|
||||
func BuildFiles(files []string, conf *Config, build *gocmd.BuildConfig) (err error) {
|
||||
panic("todo")
|
||||
func (p *Cmd) Exec(args ...string) error {
|
||||
cmd := exec.Command(p.app, args...)
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
return cmd.Run()
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
@@ -1,40 +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 llexportdata
|
||||
|
||||
import (
|
||||
"go/token"
|
||||
"go/types"
|
||||
"io"
|
||||
)
|
||||
|
||||
// Read reads export data from in, decodes it, and returns type information for the package.
|
||||
//
|
||||
// The package path (effectively its linker symbol prefix) is specified by path, since unlike
|
||||
// the package name, this information may not be recorded in the export data.
|
||||
//
|
||||
// File position information is added to fset.
|
||||
//
|
||||
// Read may inspect and add to the imports map to ensure that references within the export data
|
||||
// to other packages are consistent. The caller must ensure that imports[path] does not exist,
|
||||
// or exists but is incomplete (see types.Package.Complete), and Read inserts the resulting package
|
||||
// into this map entry.
|
||||
//
|
||||
// On return, the state of the reader is undefined.
|
||||
func Read(in io.Reader, fset *token.FileSet, imports map[string]*types.Package, path string) (*types.Package, error) {
|
||||
panic("todo")
|
||||
}
|
||||
Reference in New Issue
Block a user