llgo/gossa => llgo/ssa
This commit is contained in:
41
ssa/decl.go
Normal file
41
ssa/decl.go
Normal 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 ssa
|
||||
|
||||
import (
|
||||
"github.com/goplus/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
ssa/func.go
Normal file
69
ssa/func.go
Normal 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 ssa
|
||||
|
||||
// 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 {
|
||||
}
|
||||
127
ssa/package.go
Normal file
127
ssa/package.go
Normal file
@@ -0,0 +1,127 @@
|
||||
/*
|
||||
* 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 ssa
|
||||
|
||||
import (
|
||||
"go/constant"
|
||||
"go/types"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"github.com/goplus/llvm"
|
||||
"golang.org/x/tools/go/types/typeutil"
|
||||
)
|
||||
|
||||
// A Program is a partial or complete Go program converted to SSA form.
|
||||
type Program struct {
|
||||
ctx llvm.Context
|
||||
typs typeutil.Map
|
||||
|
||||
target *Target
|
||||
td llvm.TargetData
|
||||
tm llvm.TargetMachine
|
||||
|
||||
intType llvm.Type
|
||||
int8Type llvm.Type
|
||||
int16Type llvm.Type
|
||||
int32Type llvm.Type
|
||||
int64Type llvm.Type
|
||||
}
|
||||
|
||||
func NewProgram(target *Target) *Program {
|
||||
if target == nil {
|
||||
target = &Target{}
|
||||
}
|
||||
ctx := llvm.NewContext()
|
||||
ctx.Finalize()
|
||||
td := llvm.NewTargetData("") // TODO(xsw): target config
|
||||
return &Program{ctx: ctx, target: target, td: td}
|
||||
}
|
||||
|
||||
func (p *Program) NewPackage(name, pkgPath string) *Package {
|
||||
mod := p.ctx.NewModule(pkgPath)
|
||||
mod.Finalize()
|
||||
return &Package{mod, 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
|
||||
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{}
|
||||
}
|
||||
|
||||
type CodeGenFileType = llvm.CodeGenFileType
|
||||
|
||||
const (
|
||||
AssemblyFile = llvm.AssemblyFile
|
||||
ObjectFile = llvm.ObjectFile
|
||||
)
|
||||
|
||||
func (p *Package) CodeGen(ft CodeGenFileType) (ret []byte, err error) {
|
||||
buf, err := p.prog.targetMachine().EmitToMemoryBuffer(p.mod, ft)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
ret = buf.Bytes()
|
||||
buf.Dispose()
|
||||
return
|
||||
}
|
||||
|
||||
func (p *Package) Bitcode() []byte {
|
||||
buf := llvm.WriteBitcodeToMemoryBuffer(p.mod)
|
||||
ret := buf.Bytes()
|
||||
buf.Dispose()
|
||||
return ret
|
||||
}
|
||||
|
||||
func (p *Package) WriteTo(w io.Writer) (int64, error) {
|
||||
n, err := w.Write(p.Bitcode())
|
||||
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)
|
||||
}
|
||||
39
ssa/ssa_test.go
Normal file
39
ssa/ssa_test.go
Normal file
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* 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 ssa
|
||||
|
||||
import (
|
||||
"go/types"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func assertPkg(t *testing.T, p *Package) {
|
||||
b, err := p.CodeGen(AssemblyFile)
|
||||
if err != nil {
|
||||
t.Fatal("ctx.ParseIR:", err)
|
||||
}
|
||||
if v := string(b); v != "" {
|
||||
t.Log(v)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVar(t *testing.T) {
|
||||
prog := NewProgram(nil)
|
||||
pkg := prog.NewPackage("foo", "foo")
|
||||
pkg.NewVar("a", types.Typ[types.Int])
|
||||
assertPkg(t, pkg)
|
||||
}
|
||||
152
ssa/target.go
Normal file
152
ssa/target.go
Normal file
@@ -0,0 +1,152 @@
|
||||
/*
|
||||
* 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 ssa
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
|
||||
"github.com/goplus/llvm"
|
||||
)
|
||||
|
||||
func init() {
|
||||
llvm.InitializeAllTargetInfos()
|
||||
llvm.InitializeAllTargets()
|
||||
llvm.InitializeAllTargetMCs()
|
||||
llvm.InitializeAllAsmParsers()
|
||||
llvm.InitializeAllAsmPrinters()
|
||||
}
|
||||
|
||||
type Target struct {
|
||||
GOOS string
|
||||
GOARCH string
|
||||
GOARM string // "5", "6", "7" (default)
|
||||
}
|
||||
|
||||
func (p *Program) targetMachine() llvm.TargetMachine {
|
||||
if p.tm.C == nil {
|
||||
spec := p.target.toSpec()
|
||||
target, err := llvm.GetTargetFromTriple(spec.triple)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
p.tm = target.CreateTargetMachine(
|
||||
spec.triple,
|
||||
spec.cpu,
|
||||
spec.features,
|
||||
llvm.CodeGenLevelDefault,
|
||||
llvm.RelocDefault,
|
||||
llvm.CodeModelDefault,
|
||||
)
|
||||
}
|
||||
return p.tm
|
||||
}
|
||||
|
||||
type targetSpec struct {
|
||||
triple string
|
||||
cpu string
|
||||
features string
|
||||
}
|
||||
|
||||
func (p *Target) toSpec() (spec targetSpec) {
|
||||
// Configure based on GOOS/GOARCH environment variables (falling back to
|
||||
// runtime.GOOS/runtime.GOARCH), and generate a LLVM target based on it.
|
||||
var llvmarch string
|
||||
var goarch = p.GOARCH
|
||||
var goos = p.GOOS
|
||||
if goarch == "" {
|
||||
goarch = runtime.GOARCH
|
||||
}
|
||||
if goos == "" {
|
||||
goos = runtime.GOOS
|
||||
}
|
||||
switch goarch {
|
||||
case "386":
|
||||
llvmarch = "i386"
|
||||
case "amd64":
|
||||
llvmarch = "x86_64"
|
||||
case "arm64":
|
||||
llvmarch = "aarch64"
|
||||
case "arm":
|
||||
switch p.GOARM {
|
||||
case "5":
|
||||
llvmarch = "armv5"
|
||||
case "6":
|
||||
llvmarch = "armv6"
|
||||
default:
|
||||
llvmarch = "armv7"
|
||||
}
|
||||
case "wasm":
|
||||
llvmarch = "wasm32"
|
||||
default:
|
||||
llvmarch = goarch
|
||||
}
|
||||
llvmvendor := "unknown"
|
||||
llvmos := goos
|
||||
switch goos {
|
||||
case "darwin":
|
||||
// Use macosx* instead of darwin, otherwise darwin/arm64 will refer
|
||||
// to iOS!
|
||||
llvmos = "macosx10.12.0"
|
||||
if llvmarch == "aarch64" {
|
||||
// Looks like Apple prefers to call this architecture ARM64
|
||||
// instead of AArch64.
|
||||
llvmarch = "arm64"
|
||||
llvmos = "macosx11.0.0"
|
||||
}
|
||||
llvmvendor = "apple"
|
||||
case "wasip1":
|
||||
llvmos = "wasi"
|
||||
}
|
||||
// Target triples (which actually have four components, but are called
|
||||
// triples for historical reasons) have the form:
|
||||
// arch-vendor-os-environment
|
||||
spec.triple = llvmarch + "-" + llvmvendor + "-" + llvmos
|
||||
if llvmos == "windows" {
|
||||
spec.triple += "-gnu"
|
||||
} else if goarch == "arm" {
|
||||
spec.triple += "-gnueabihf"
|
||||
}
|
||||
switch goarch {
|
||||
case "386":
|
||||
spec.cpu = "pentium4"
|
||||
spec.features = "+cx8,+fxsr,+mmx,+sse,+sse2,+x87"
|
||||
case "amd64":
|
||||
spec.cpu = "x86-64"
|
||||
spec.features = "+cx8,+fxsr,+mmx,+sse,+sse2,+x87"
|
||||
case "arm":
|
||||
spec.cpu = "generic"
|
||||
switch llvmarch {
|
||||
case "armv5":
|
||||
spec.features = "+armv5t,+strict-align,-aes,-bf16,-d32,-dotprod,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-mve.fp,-neon,-sha2,-thumb-mode,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp"
|
||||
case "armv6":
|
||||
spec.features = "+armv6,+dsp,+fp64,+strict-align,+vfp2,+vfp2sp,-aes,-d32,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fullfp16,-neon,-sha2,-thumb-mode,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp"
|
||||
case "armv7":
|
||||
spec.features = "+armv7-a,+d32,+dsp,+fp64,+neon,+vfp2,+vfp2sp,+vfp3,+vfp3d16,+vfp3d16sp,+vfp3sp,-aes,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fullfp16,-sha2,-thumb-mode,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp"
|
||||
}
|
||||
case "arm64":
|
||||
spec.cpu = "generic"
|
||||
if goos == "darwin" {
|
||||
spec.features = "+neon"
|
||||
} else { // windows, linux
|
||||
spec.features = "+neon,-fmv"
|
||||
}
|
||||
case "wasm":
|
||||
spec.cpu = "generic"
|
||||
spec.features = "+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext"
|
||||
}
|
||||
return
|
||||
}
|
||||
106
ssa/type.go
Normal file
106
ssa/type.go
Normal 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 ssa
|
||||
|
||||
import (
|
||||
"go/types"
|
||||
|
||||
"github.com/goplus/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()
|
||||
}
|
||||
Reference in New Issue
Block a user