ctx.createPackage
This commit is contained in:
44
cl/alias.go
Normal file
44
cl/alias.go
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
/*
|
||||||
|
* 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 cl
|
||||||
|
|
||||||
|
import (
|
||||||
|
llvm "tinygo.org/x/go-llvm"
|
||||||
|
)
|
||||||
|
|
||||||
|
var stdlibAliases = map[string]string{
|
||||||
|
// crypto packages
|
||||||
|
"crypto/ed25519/internal/edwards25519/field.feMul": "crypto/ed25519/internal/edwards25519/field.feMulGeneric",
|
||||||
|
"crypto/ed25519/internal/edwards25519/field.feSquare": "crypto/ed25519/internal/edwards25519/field.feSquareGeneric",
|
||||||
|
"crypto/md5.block": "crypto/md5.blockGeneric",
|
||||||
|
"crypto/sha1.block": "crypto/sha1.blockGeneric",
|
||||||
|
"crypto/sha1.blockAMD64": "crypto/sha1.blockGeneric",
|
||||||
|
"crypto/sha256.block": "crypto/sha256.blockGeneric",
|
||||||
|
"crypto/sha512.blockAMD64": "crypto/sha512.blockGeneric",
|
||||||
|
|
||||||
|
// math package
|
||||||
|
"math.archHypot": "math.hypot",
|
||||||
|
"math.archMax": "math.max",
|
||||||
|
"math.archMin": "math.min",
|
||||||
|
"math.archModf": "math.modf",
|
||||||
|
}
|
||||||
|
|
||||||
|
// createAlias implements the function (in the builder) as a call to the alias
|
||||||
|
// function.
|
||||||
|
func (b *builder) createAlias(alias llvm.Value) {
|
||||||
|
panic("todo")
|
||||||
|
}
|
||||||
37
cl/builder.go
Normal file
37
cl/builder.go
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
/*
|
||||||
|
* 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 cl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"golang.org/x/tools/go/ssa"
|
||||||
|
llvm "tinygo.org/x/go-llvm"
|
||||||
|
)
|
||||||
|
|
||||||
|
// builder contains all information relevant to build a single function.
|
||||||
|
type builder struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func newBuilder(c *context, irbuilder llvm.Builder, f *ssa.Function) *builder {
|
||||||
|
panic("todo")
|
||||||
|
}
|
||||||
|
|
||||||
|
// createFunction builds the LLVM IR implementation for this function. The
|
||||||
|
// function must not yet be defined, otherwise this function will create a
|
||||||
|
// diagnostic.
|
||||||
|
func (b *builder) createFunction() {
|
||||||
|
panic("todo")
|
||||||
|
}
|
||||||
142
cl/compile.go
142
cl/compile.go
@@ -17,6 +17,9 @@
|
|||||||
package cl
|
package cl
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"go/types"
|
||||||
|
"sort"
|
||||||
|
|
||||||
"github.com/goplus/llgo/loader"
|
"github.com/goplus/llgo/loader"
|
||||||
"github.com/qiniu/x/errors"
|
"github.com/qiniu/x/errors"
|
||||||
"golang.org/x/tools/go/ssa"
|
"golang.org/x/tools/go/ssa"
|
||||||
@@ -30,8 +33,11 @@ type Config struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type context struct {
|
type context struct {
|
||||||
mod llvm.Module
|
mod llvm.Module
|
||||||
ctx llvm.Context
|
ctx llvm.Context
|
||||||
|
|
||||||
|
embedGlobals map[string][]*loader.EmbedFile
|
||||||
|
|
||||||
errs errors.List
|
errs errors.List
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -49,13 +55,137 @@ func (c *context) dispose() {
|
|||||||
panic("todo")
|
panic("todo")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *context) loadASTComments(loader.Package) {
|
|
||||||
panic("todo")
|
|
||||||
}
|
|
||||||
|
|
||||||
// createPackage builds the LLVM IR for all types, methods, and global variables
|
// createPackage builds the LLVM IR for all types, methods, and global variables
|
||||||
// in the given package.
|
// in the given package.
|
||||||
func (c *context) createPackage(irbuilder llvm.Builder, pkg *ssa.Package) {
|
func (c *context) createPackage(irbuilder llvm.Builder, pkg *ssa.Package) {
|
||||||
|
type namedMember struct {
|
||||||
|
name string
|
||||||
|
val ssa.Member
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort by position, so that the order of the functions in the IR matches
|
||||||
|
// the order of functions in the source file. This is useful for testing,
|
||||||
|
// for example.
|
||||||
|
var members []*namedMember
|
||||||
|
for name, v := range pkg.Members {
|
||||||
|
members = append(members, &namedMember{name, v})
|
||||||
|
}
|
||||||
|
sort.Slice(members, func(i, j int) bool {
|
||||||
|
iPos := members[i].val.Pos()
|
||||||
|
jPos := members[j].val.Pos()
|
||||||
|
return iPos < jPos
|
||||||
|
})
|
||||||
|
|
||||||
|
// Define all functions.
|
||||||
|
for _, m := range members {
|
||||||
|
member := m.val
|
||||||
|
switch member := member.(type) {
|
||||||
|
case *ssa.Function:
|
||||||
|
if member.TypeParams() != nil {
|
||||||
|
// Do not try to build generic (non-instantiated) functions.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// Create the function definition.
|
||||||
|
b := newBuilder(c, irbuilder, member)
|
||||||
|
if _, ok := mathToLLVMMapping[member.RelString(nil)]; ok {
|
||||||
|
// The body of this function (if there is one) is ignored and
|
||||||
|
// replaced with a LLVM intrinsic call.
|
||||||
|
b.defineMathOp()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if ok := b.defineMathBitsIntrinsic(); ok {
|
||||||
|
// Like a math intrinsic, the body of this function was replaced
|
||||||
|
// with a LLVM intrinsic.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if member.Blocks == nil {
|
||||||
|
// Try to define this as an intrinsic function.
|
||||||
|
b.defineIntrinsicFunction()
|
||||||
|
// It might not be an intrinsic function but simply an external
|
||||||
|
// function (defined via //go:linkname). Leave it undefined in
|
||||||
|
// that case.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
b.createFunction()
|
||||||
|
case *ssa.Type:
|
||||||
|
if types.IsInterface(member.Type()) {
|
||||||
|
// Interfaces don't have concrete methods.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Named type. We should make sure all methods are created.
|
||||||
|
// This includes both functions with pointer receivers and those
|
||||||
|
// without.
|
||||||
|
methods := getAllMethods(pkg.Prog, member.Type())
|
||||||
|
methods = append(methods, getAllMethods(pkg.Prog, types.NewPointer(member.Type()))...)
|
||||||
|
for _, method := range methods {
|
||||||
|
// Parse this method.
|
||||||
|
fn := pkg.Prog.MethodValue(method)
|
||||||
|
if fn == nil {
|
||||||
|
continue // probably a generic method
|
||||||
|
}
|
||||||
|
if member.Type().String() != member.String() {
|
||||||
|
// This is a member on a type alias. Do not build such a
|
||||||
|
// function.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if fn.Blocks == nil {
|
||||||
|
continue // external function
|
||||||
|
}
|
||||||
|
if fn.Synthetic != "" && fn.Synthetic != "package initializer" {
|
||||||
|
// This function is a kind of wrapper function (created by
|
||||||
|
// the ssa package, not appearing in the source code) that
|
||||||
|
// is created by the getFunction method as needed.
|
||||||
|
// Therefore, don't build it here to avoid "function
|
||||||
|
// redeclared" errors.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// Create the function definition.
|
||||||
|
b := newBuilder(c, irbuilder, fn)
|
||||||
|
b.createFunction()
|
||||||
|
}
|
||||||
|
case *ssa.Global:
|
||||||
|
// Global variable.
|
||||||
|
info := c.getGlobalInfo(member)
|
||||||
|
global := c.getGlobal(member)
|
||||||
|
if files, ok := c.embedGlobals[member.Name()]; ok {
|
||||||
|
c.createEmbedGlobal(member, global, files)
|
||||||
|
} else if !info.extern {
|
||||||
|
global.SetInitializer(llvm.ConstNull(global.GlobalValueType()))
|
||||||
|
global.SetVisibility(llvm.HiddenVisibility)
|
||||||
|
if info.section != "" {
|
||||||
|
global.SetSection(info.section)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add forwarding functions for functions that would otherwise be
|
||||||
|
// implemented in assembly.
|
||||||
|
for _, m := range members {
|
||||||
|
member := m.val
|
||||||
|
switch member := member.(type) {
|
||||||
|
case *ssa.Function:
|
||||||
|
if member.Blocks != nil {
|
||||||
|
continue // external function
|
||||||
|
}
|
||||||
|
info := c.getFunctionInfo(member)
|
||||||
|
if aliasName, ok := stdlibAliases[info.linkName]; ok {
|
||||||
|
alias := c.mod.NamedFunction(aliasName)
|
||||||
|
if alias.IsNil() {
|
||||||
|
// Shouldn't happen, but perhaps best to just ignore.
|
||||||
|
// The error will be a link error, if there is an error.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
b := newBuilder(c, irbuilder, member)
|
||||||
|
b.createAlias(alias)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// createEmbedGlobal creates an initializer for a //go:embed global variable.
|
||||||
|
func (c *context) createEmbedGlobal(member *ssa.Global, global llvm.Value, files []*loader.EmbedFile) {
|
||||||
panic("todo")
|
panic("todo")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
60
cl/intrinsics.go
Normal file
60
cl/intrinsics.go
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
/*
|
||||||
|
* 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 cl
|
||||||
|
|
||||||
|
// Define unimplemented intrinsic functions.
|
||||||
|
//
|
||||||
|
// Some functions are either normally implemented in Go assembly (like
|
||||||
|
// sync/atomic functions) or intentionally left undefined to be implemented
|
||||||
|
// directly in the compiler (like runtime/volatile functions). Either way, look
|
||||||
|
// for these and implement them if this is the case.
|
||||||
|
func (b *builder) defineIntrinsicFunction() {
|
||||||
|
panic("todo")
|
||||||
|
}
|
||||||
|
|
||||||
|
var mathToLLVMMapping = map[string]string{
|
||||||
|
"math.Ceil": "llvm.ceil.f64",
|
||||||
|
"math.Exp": "llvm.exp.f64",
|
||||||
|
"math.Exp2": "llvm.exp2.f64",
|
||||||
|
"math.Floor": "llvm.floor.f64",
|
||||||
|
"math.Log": "llvm.log.f64",
|
||||||
|
"math.Sqrt": "llvm.sqrt.f64",
|
||||||
|
"math.Trunc": "llvm.trunc.f64",
|
||||||
|
}
|
||||||
|
|
||||||
|
// defineMathOp defines a math function body as a call to a LLVM intrinsic,
|
||||||
|
// instead of the regular Go implementation. This allows LLVM to reason about
|
||||||
|
// the math operation and (depending on the architecture) allows it to lower the
|
||||||
|
// operation to very fast floating point instructions. If this is not possible,
|
||||||
|
// LLVM will emit a call to a libm function that implements the same operation.
|
||||||
|
//
|
||||||
|
// One example of an optimization that LLVM can do is to convert
|
||||||
|
// float32(math.Sqrt(float64(v))) to a 32-bit floating point operation, which is
|
||||||
|
// beneficial on architectures where 64-bit floating point operations are (much)
|
||||||
|
// more expensive than 32-bit ones.
|
||||||
|
func (b *builder) defineMathOp() {
|
||||||
|
panic("todo")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Implement most math/bits functions.
|
||||||
|
//
|
||||||
|
// This implements all the functions that operate on bits. It does not yet
|
||||||
|
// implement the arithmetic functions (like bits.Add), which also have LLVM
|
||||||
|
// intrinsics.
|
||||||
|
func (b *builder) defineMathBitsIntrinsic() bool {
|
||||||
|
panic("todo")
|
||||||
|
}
|
||||||
98
cl/symbol.go
Normal file
98
cl/symbol.go
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
/*
|
||||||
|
* 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 cl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go/types"
|
||||||
|
|
||||||
|
"github.com/goplus/llgo/loader"
|
||||||
|
"golang.org/x/tools/go/ssa"
|
||||||
|
llvm "tinygo.org/x/go-llvm"
|
||||||
|
)
|
||||||
|
|
||||||
|
// functionInfo contains some information about a function or method. In
|
||||||
|
// particular, it contains information obtained from pragmas.
|
||||||
|
//
|
||||||
|
// The linkName value contains a valid link name, even if //go:linkname is not
|
||||||
|
// present.
|
||||||
|
type functionInfo struct {
|
||||||
|
wasmModule string // go:wasm-module
|
||||||
|
wasmName string // wasm-export-name or wasm-import-name in the IR
|
||||||
|
linkName string // go:linkname, go:export - the IR function name
|
||||||
|
section string // go:section - object file section name
|
||||||
|
exported bool // go:export, CGo
|
||||||
|
interrupt bool // go:interrupt
|
||||||
|
nobounds bool // go:nobounds
|
||||||
|
variadic bool // go:variadic (CGo only)
|
||||||
|
inline inlineType // go:inline
|
||||||
|
}
|
||||||
|
|
||||||
|
type inlineType int
|
||||||
|
|
||||||
|
// How much to inline.
|
||||||
|
const (
|
||||||
|
// Default behavior. The compiler decides for itself whether any given
|
||||||
|
// function will be inlined. Whether any function is inlined depends on the
|
||||||
|
// optimization level.
|
||||||
|
inlineDefault inlineType = iota
|
||||||
|
|
||||||
|
// Inline hint, just like the C inline keyword (signalled using
|
||||||
|
// //go:inline). The compiler will be more likely to inline this function,
|
||||||
|
// but it is not a guarantee.
|
||||||
|
inlineHint
|
||||||
|
|
||||||
|
// Don't inline, just like the GCC noinline attribute. Signalled using
|
||||||
|
// //go:noinline.
|
||||||
|
inlineNone
|
||||||
|
)
|
||||||
|
|
||||||
|
// getFunctionInfo returns information about a function that is not directly
|
||||||
|
// present in *ssa.Function, such as the link name and whether it should be
|
||||||
|
// exported.
|
||||||
|
func (c *context) getFunctionInfo(f *ssa.Function) functionInfo {
|
||||||
|
panic("todo")
|
||||||
|
}
|
||||||
|
|
||||||
|
// globalInfo contains some information about a specific global. By default,
|
||||||
|
// linkName is equal to .RelString(nil) on a global and extern is false, but for
|
||||||
|
// some symbols this is different (due to //go:extern for example).
|
||||||
|
type globalInfo struct {
|
||||||
|
linkName string // go:extern
|
||||||
|
extern bool // go:extern
|
||||||
|
align int // go:align
|
||||||
|
section string // go:section
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *context) loadASTComments(loader.Package) {
|
||||||
|
panic("todo")
|
||||||
|
}
|
||||||
|
|
||||||
|
// getGlobal returns a LLVM IR global value for a Go SSA global. It is added to
|
||||||
|
// the LLVM IR if it has not been added already.
|
||||||
|
func (c *context) getGlobal(g *ssa.Global) llvm.Value {
|
||||||
|
panic("todo")
|
||||||
|
}
|
||||||
|
|
||||||
|
// getGlobalInfo returns some information about a specific global.
|
||||||
|
func (c *context) getGlobalInfo(g *ssa.Global) globalInfo {
|
||||||
|
panic("todo")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get all methods of a type.
|
||||||
|
func getAllMethods(prog *ssa.Program, typ types.Type) []*types.Selection {
|
||||||
|
panic("todo")
|
||||||
|
}
|
||||||
@@ -21,5 +21,16 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Package struct {
|
type Package struct {
|
||||||
SSA *ssa.Package
|
SSA *ssa.Package
|
||||||
|
EmbedGlobals map[string][]*EmbedFile
|
||||||
|
}
|
||||||
|
|
||||||
|
type EmbedFile struct {
|
||||||
|
/*
|
||||||
|
Name string
|
||||||
|
Size uint64
|
||||||
|
Hash string // hash of the file (as a hex string)
|
||||||
|
NeedsData bool // true if this file is embedded as a byte slice
|
||||||
|
Data []byte // contents of this file (only if NeedsData is set)
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user