From 7784a6a32d9506070fbe994c26a22717468776eb Mon Sep 17 00:00:00 2001 From: xushiwei Date: Mon, 15 Apr 2024 04:00:38 +0800 Subject: [PATCH] llgo/gossa --- go.mod | 3 +- go.sum | 2 + gossa/decl.go | 41 ++++++++++++++++++ gossa/func.go | 69 ++++++++++++++++++++++++++++++ gossa/package.go | 101 ++++++++++++++++++++++++++++++++++++++++++++ gossa/type.go | 106 +++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 321 insertions(+), 1 deletion(-) create mode 100644 gossa/decl.go create mode 100644 gossa/func.go create mode 100644 gossa/package.go create mode 100644 gossa/type.go diff --git a/go.mod b/go.mod index ab2dd97d..68ae8a92 100644 --- a/go.mod +++ b/go.mod @@ -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 ) diff --git a/go.sum b/go.sum index 2872a2df..b1c702b6 100644 --- a/go.sum +++ b/go.sum @@ -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= diff --git a/gossa/decl.go b/gossa/decl.go new file mode 100644 index 00000000..0c049eba --- /dev/null +++ b/gossa/decl.go @@ -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 +} diff --git a/gossa/func.go b/gossa/func.go new file mode 100644 index 00000000..e1c2a226 --- /dev/null +++ b/gossa/func.go @@ -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 { +} diff --git a/gossa/package.go b/gossa/package.go new file mode 100644 index 00000000..2079dc4c --- /dev/null +++ b/gossa/package.go @@ -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) +} diff --git a/gossa/type.go b/gossa/type.go new file mode 100644 index 00000000..d8528880 --- /dev/null +++ b/gossa/type.go @@ -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() +}