llgo/gossa

This commit is contained in:
xushiwei
2024-04-15 04:00:38 +08:00
parent 55b310d266
commit 7784a6a32d
6 changed files with 321 additions and 1 deletions

3
go.mod
View File

@@ -7,11 +7,12 @@ require (
github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb
github.com/goplus/gop v1.2.6
github.com/qiniu/x v1.13.10
golang.org/x/tools v0.19.0
tinygo.org/x/go-llvm v0.0.0-20240106122909-c2c543540318
)
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/tools v0.19.0 // indirect
)

2
go.sum
View File

@@ -57,3 +57,5 @@ 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/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
tinygo.org/x/go-llvm v0.0.0-20240106122909-c2c543540318 h1:4KjZvPtcN1UwobevcGbdzeinx0L1i8HDdJu84bu7NI8=
tinygo.org/x/go-llvm v0.0.0-20240106122909-c2c543540318/go.mod h1:GFbusT2VTA4I+l4j80b17KFK+6whv69Wtny5U+T8RR0=

41
gossa/decl.go Normal file
View File

@@ -0,0 +1,41 @@
/*
* 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 gossa
import (
llvm "tinygo.org/x/go-llvm"
)
// A NamedConst is a Member of a Package representing a package-level
// named constant.
//
// Pos() returns the position of the declaring ast.ValueSpec.Names[*]
// identifier.
//
// NB: a NamedConst is not a Value; it contains a constant Value, which
// it augments with the name and position of its 'const' declaration.
type NamedConst struct {
}
// A Global is a named Value holding the address of a package-level
// variable.
//
// Pos() returns the position of the ast.ValueSpec.Names[*]
// identifier.
type Global struct {
impl llvm.Value
}

69
gossa/func.go Normal file
View File

@@ -0,0 +1,69 @@
/*
* 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 gossa
// Function represents the parameters, results, and code of a function
// or method.
//
// If Blocks is nil, this indicates an external function for which no
// Go source code is available. In this case, FreeVars, Locals, and
// Params are nil too. Clients performing whole-program analysis must
// handle external functions specially.
//
// Blocks contains the function's control-flow graph (CFG).
// Blocks[0] is the function entry point; block order is not otherwise
// semantically significant, though it may affect the readability of
// the disassembly.
// To iterate over the blocks in dominance order, use DomPreorder().
//
// Recover is an optional second entry point to which control resumes
// after a recovered panic. The Recover block may contain only a return
// statement, preceded by a load of the function's named return
// parameters, if any.
//
// A nested function (Parent()!=nil) that refers to one or more
// lexically enclosing local variables ("free variables") has FreeVars.
// Such functions cannot be called directly but require a
// value created by MakeClosure which, via its Bindings, supplies
// values for these parameters.
//
// If the function is a method (Signature.Recv() != nil) then the first
// element of Params is the receiver parameter.
//
// A Go package may declare many functions called "init".
// For each one, Object().Name() returns "init" but Name() returns
// "init#1", etc, in declaration order.
//
// Pos() returns the declaring ast.FuncLit.Type.Func or the position
// of the ast.FuncDecl.Name, if the function was explicit in the
// source. Synthetic wrappers, for which Synthetic != "", may share
// the same position as the function they wrap.
// Syntax.Pos() always returns the position of the declaring "func" token.
//
// Type() returns the function's Signature.
//
// A generic function is a function or method that has uninstantiated type
// parameters (TypeParams() != nil). Consider a hypothetical generic
// method, (*Map[K,V]).Get. It may be instantiated with all ground
// (non-parameterized) types as (*Map[string,int]).Get or with
// parameterized types as (*Map[string,U]).Get, where U is a type parameter.
// In both instantiations, Origin() refers to the instantiated generic
// method, (*Map[K,V]).Get, TypeParams() refers to the parameters [K,V] of
// the generic method. TypeArgs() refers to [string,U] or [string,int],
// respectively, and is nil in the generic method.
type Function struct {
}

101
gossa/package.go Normal file
View File

@@ -0,0 +1,101 @@
/*
* 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 gossa
import (
"go/constant"
"go/types"
"io"
"os"
"runtime"
"golang.org/x/tools/go/types/typeutil"
llvm "tinygo.org/x/go-llvm"
)
// A Program is a partial or complete Go program converted to SSA form.
type Program struct {
ctx llvm.Context
typs typeutil.Map
td llvm.TargetData
intType llvm.Type
int8Type llvm.Type
int16Type llvm.Type
int32Type llvm.Type
int64Type llvm.Type
}
func NewProgram(targetRep string) *Program {
ctx := llvm.NewContext()
runtime.SetFinalizer(ctx.C, (llvm.Context).Dispose)
td := llvm.NewTargetData(targetRep)
return &Program{ctx: ctx, td: td}
}
func (p *Program) NewPackage(pkg *types.Package) *Package {
name := pkg.Path()
mod := p.ctx.NewModule(name)
runtime.SetFinalizer(mod.C, (llvm.Module).Dispose)
return &Package{mod, pkg, p}
}
// A Package is a single analyzed Go package containing Members for
// all package-level functions, variables, constants and types it
// declares. These may be accessed directly via Members, or via the
// type-specific accessor methods Func, Type, Var and Const.
//
// Members also contains entries for "init" (the synthetic package
// initializer) and "init#%d", the nth declared init function,
// and unspecified other things too.
type Package struct {
mod llvm.Module
pkg *types.Package
prog *Program
}
func (p *Package) NewConst(name string, val constant.Value) *NamedConst {
return &NamedConst{}
}
func (p *Package) NewType(name string, typ types.Type) *Type {
return &Type{}
}
func (p *Package) NewVar(name string, typ types.Type) *Global {
gbl := llvm.AddGlobal(p.mod, p.prog.llvmType(typ), name)
return &Global{gbl}
}
func (p *Package) NewFunc(name string, sig *types.Signature) *Function {
return &Function{}
}
func (p *Package) WriteTo(w io.Writer) (int64, error) {
buf := llvm.WriteBitcodeToMemoryBuffer(p.mod)
n, err := w.Write(buf.Bytes()) // TODO(xsw): reduce copy of bytes
return int64(n), err
}
func (p *Package) WriteFile(file string) (err error) {
f, err := os.Create(file)
if err != nil {
return
}
defer f.Close()
return llvm.WriteBitcodeToFile(p.mod, f)
}

106
gossa/type.go Normal file
View File

@@ -0,0 +1,106 @@
/*
* 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 gossa
import (
"go/types"
llvm "tinygo.org/x/go-llvm"
)
// A Type is a Member of a Package representing a package-level named type.
type Type struct {
}
func (p *Program) llvmType(typ types.Type) llvm.Type {
if v := p.typs.At(typ); v != nil {
return v.(llvm.Type)
}
ret := p.toLLVMType(p.ctx, typ)
p.typs.Set(typ, ret)
return ret
}
func (p *Program) tyInt() llvm.Type {
if p.intType.IsNil() {
p.intType = llvmIntType(p.ctx, p.td.PointerSize())
}
return p.intType
}
func (p *Program) tyInt8() llvm.Type {
if p.int8Type.IsNil() {
p.int8Type = p.ctx.Int8Type()
}
return p.int8Type
}
func (p *Program) tyInt16() llvm.Type {
if p.int16Type.IsNil() {
p.int16Type = p.ctx.Int16Type()
}
return p.int16Type
}
func (p *Program) tyInt32() llvm.Type {
if p.int32Type.IsNil() {
p.int32Type = p.ctx.Int32Type()
}
return p.int32Type
}
func (p *Program) tyInt64() llvm.Type {
if p.int64Type.IsNil() {
p.int64Type = p.ctx.Int64Type()
}
return p.int64Type
}
func (p *Program) toLLVMType(ctx llvm.Context, typ types.Type) llvm.Type {
switch t := typ.(type) {
case *types.Basic:
switch t.Kind() {
case types.Int, types.Uint, types.Uintptr:
return p.tyInt()
case types.Bool, types.Uint8, types.Int8:
return p.tyInt8()
case types.Int16, types.Uint16:
return p.tyInt16()
case types.Int32, types.Uint32:
return p.tyInt32()
case types.Int64, types.Uint64:
return p.tyInt64()
case types.Float32:
return ctx.FloatType()
case types.Float64:
return ctx.DoubleType()
case types.Complex64:
case types.Complex128:
case types.String:
case types.UnsafePointer:
return llvm.PointerType(ctx.VoidType(), 0)
}
}
panic("todo")
}
func llvmIntType(ctx llvm.Context, size int) llvm.Type {
if size <= 4 {
return ctx.Int32Type()
}
return ctx.Int64Type()
}