46
cl/alias.go
46
cl/alias.go
@@ -1,46 +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 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")
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
@@ -1,39 +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 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")
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
413
cl/compile.go
413
cl/compile.go
@@ -17,6 +17,7 @@
|
|||||||
package cl
|
package cl
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"sort"
|
"sort"
|
||||||
|
|
||||||
llssa "github.com/goplus/llgo/ssa"
|
llssa "github.com/goplus/llgo/ssa"
|
||||||
@@ -26,84 +27,93 @@ import (
|
|||||||
type Config struct {
|
type Config struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
type instrAndValue interface {
|
||||||
|
ssa.Instruction
|
||||||
|
ssa.Value
|
||||||
|
}
|
||||||
|
|
||||||
type context struct {
|
type context struct {
|
||||||
|
prog llssa.Program
|
||||||
|
pkg llssa.Package
|
||||||
|
fn llssa.Function
|
||||||
|
glbs map[*ssa.Global]llssa.Global
|
||||||
}
|
}
|
||||||
|
|
||||||
// Global variable.
|
// Global variable.
|
||||||
func (p *context) compileGlobal(ret llssa.Package, member *ssa.Global) {
|
func (p *context) compileGlobal(pkg llssa.Package, gbl *ssa.Global) llssa.Global {
|
||||||
ret.NewVar(member.Name(), member.Type())
|
if g, ok := p.glbs[gbl]; ok {
|
||||||
|
return g
|
||||||
|
}
|
||||||
|
g := pkg.NewVar(gbl.Name(), gbl.Type())
|
||||||
|
p.glbs[gbl] = g
|
||||||
|
return g
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *context) compileType(ret llssa.Package, member *ssa.Type) {
|
func (p *context) compileType(pkg llssa.Package, member *ssa.Type) {
|
||||||
panic("todo")
|
panic("todo")
|
||||||
/*
|
|
||||||
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()
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *context) compileFunc(ret llssa.Package, member *ssa.Function) {
|
func (p *context) compileFunc(pkg llssa.Package, f *ssa.Function) {
|
||||||
// panic("todo")
|
fn := pkg.NewFunc(f.Name(), f.Signature)
|
||||||
/*
|
p.fn = fn
|
||||||
// Create the function definition.
|
defer func() {
|
||||||
b := newBuilder(c, irbuilder, member)
|
p.fn = nil
|
||||||
if _, ok := mathToLLVMMapping[member.RelString(nil)]; ok {
|
}()
|
||||||
// The body of this function (if there is one) is ignored and
|
if f.Blocks == nil { // external function
|
||||||
// replaced with a LLVM intrinsic call.
|
return
|
||||||
b.defineMathOp()
|
}
|
||||||
continue
|
b := fn.MakeBody("")
|
||||||
}
|
for _, block := range f.Blocks {
|
||||||
if ok := b.defineMathBitsIntrinsic(); ok {
|
p.compileBlock(b, block)
|
||||||
// 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()
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *context) compileBlock(b llssa.Builder, block *ssa.BasicBlock) {
|
||||||
|
_ = block.Index
|
||||||
|
for _, instr := range block.Instrs {
|
||||||
|
p.compileInstr(b, instr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *context) compileInstrAndValue(b llssa.Builder, iv instrAndValue) llssa.Expr {
|
||||||
|
switch v := iv.(type) {
|
||||||
|
case *ssa.UnOp:
|
||||||
|
x := p.compileValue(b, v.X)
|
||||||
|
return b.UnOp(v.Op, x)
|
||||||
|
}
|
||||||
|
panic(fmt.Sprintf("compileInstrAndValue: unknown instr - %T\n", iv))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *context) compileInstr(b llssa.Builder, instr ssa.Instruction) {
|
||||||
|
if iv, ok := instr.(instrAndValue); ok {
|
||||||
|
p.compileInstrAndValue(b, iv)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
switch v := instr.(type) {
|
||||||
|
case *ssa.If:
|
||||||
|
p.compileValue(b, v.Cond)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
panic(fmt.Sprintf("compileInstr: unknown instr - %T\n", instr))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *context) compileValue(b llssa.Builder, v ssa.Value) llssa.Expr {
|
||||||
|
if iv, ok := v.(instrAndValue); ok {
|
||||||
|
return p.compileInstrAndValue(b, iv)
|
||||||
|
}
|
||||||
|
switch v := v.(type) {
|
||||||
|
case *ssa.Global:
|
||||||
|
g := p.compileGlobal(p.pkg, v)
|
||||||
|
return g.Expr
|
||||||
|
}
|
||||||
|
panic(fmt.Sprintf("compileValue: unknown value - %T\n", v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// 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, conf *Config) (ret llssa.Package, err error) {
|
||||||
type namedMember struct {
|
type namedMember struct {
|
||||||
name string
|
name string
|
||||||
@@ -126,7 +136,11 @@ func NewPackage(prog llssa.Program, pkg *ssa.Package, conf *Config) (ret llssa.P
|
|||||||
pkgTypes := pkg.Pkg
|
pkgTypes := pkg.Pkg
|
||||||
ret = prog.NewPackage(pkgTypes.Name(), pkgTypes.Path())
|
ret = prog.NewPackage(pkgTypes.Name(), pkgTypes.Path())
|
||||||
|
|
||||||
ctx := &context{}
|
ctx := &context{
|
||||||
|
prog: prog,
|
||||||
|
pkg: ret,
|
||||||
|
glbs: make(map[*ssa.Global]llssa.Global),
|
||||||
|
}
|
||||||
for _, m := range members {
|
for _, m := range members {
|
||||||
member := m.val
|
member := m.val
|
||||||
switch member := member.(type) {
|
switch member := member.(type) {
|
||||||
@@ -135,7 +149,9 @@ func NewPackage(prog llssa.Program, pkg *ssa.Package, conf *Config) (ret llssa.P
|
|||||||
// Do not try to build generic (non-instantiated) functions.
|
// Do not try to build generic (non-instantiated) functions.
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
ctx.compileFunc(ret, member)
|
if false {
|
||||||
|
ctx.compileFunc(ret, member)
|
||||||
|
}
|
||||||
case *ssa.Type:
|
case *ssa.Type:
|
||||||
ctx.compileType(ret, member)
|
ctx.compileType(ret, member)
|
||||||
case *ssa.Global:
|
case *ssa.Global:
|
||||||
@@ -145,261 +161,4 @@ func NewPackage(prog llssa.Program, pkg *ssa.Package, conf *Config) (ret llssa.P
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
// -----------------------------------------------------------------------------
|
||||||
import (
|
|
||||||
"go/types"
|
|
||||||
"os"
|
|
||||||
"runtime"
|
|
||||||
"sort"
|
|
||||||
|
|
||||||
"github.com/goplus/llgo/loader"
|
|
||||||
"github.com/qiniu/x/errors"
|
|
||||||
"golang.org/x/tools/go/ssa"
|
|
||||||
llvm "tinygo.org/x/go-llvm"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Config struct {
|
|
||||||
Triple string
|
|
||||||
|
|
||||||
Target llvm.TargetMachine
|
|
||||||
}
|
|
||||||
|
|
||||||
type context struct {
|
|
||||||
mod llvm.Module
|
|
||||||
ctx llvm.Context
|
|
||||||
|
|
||||||
embedGlobals map[string][]*loader.EmbedFile
|
|
||||||
|
|
||||||
errs errors.List
|
|
||||||
}
|
|
||||||
|
|
||||||
func newContext(moduleName string, conf *Config) *context {
|
|
||||||
machine := conf.Target
|
|
||||||
targetData := machine.CreateTargetData()
|
|
||||||
ctx := llvm.NewContext()
|
|
||||||
mod := ctx.NewModule(moduleName)
|
|
||||||
mod.SetTarget(conf.Triple)
|
|
||||||
mod.SetDataLayout(targetData.String())
|
|
||||||
return &context{mod: mod, ctx: ctx}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *context) dispose() {
|
|
||||||
panic("todo")
|
|
||||||
}
|
|
||||||
|
|
||||||
// createPackage builds the LLVM IR for all types, methods, and global variables
|
|
||||||
// in the given 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")
|
|
||||||
}
|
|
||||||
|
|
||||||
type Package struct {
|
|
||||||
llvm.Module
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewPackage(moduleName string, pkg loader.Package, conf *Config) (ret Package, err error) {
|
|
||||||
ssaPkg := pkg.SSA
|
|
||||||
ssaPkg.Build()
|
|
||||||
|
|
||||||
c := newContext(moduleName, conf)
|
|
||||||
defer c.dispose()
|
|
||||||
|
|
||||||
// Load comments such as //go:extern on globals.
|
|
||||||
c.loadASTComments(pkg)
|
|
||||||
|
|
||||||
/* TODO: gc related
|
|
||||||
// Predeclare the runtime.alloc function, which is used by the wordpack
|
|
||||||
// functionality.
|
|
||||||
c.getFunction(c.program.ImportedPackage("runtime").Members["alloc"].(*ssa.Function))
|
|
||||||
if c.NeedsStackObjects {
|
|
||||||
// Predeclare trackPointer, which is used everywhere we use runtime.alloc.
|
|
||||||
c.getFunction(c.program.ImportedPackage("runtime").Members["trackPointer"].(*ssa.Function))
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
/*
|
|
||||||
// Compile all functions, methods, and global variables in this package.
|
|
||||||
irbuilder := c.ctx.NewBuilder()
|
|
||||||
defer irbuilder.Dispose()
|
|
||||||
c.createPackage(irbuilder, ssaPkg)
|
|
||||||
|
|
||||||
/* TODO: risc-v
|
|
||||||
// Add the "target-abi" flag, which is necessary on RISC-V otherwise it will
|
|
||||||
// pick one that doesn't match the -mabi Clang flag.
|
|
||||||
if c.ABI != "" {
|
|
||||||
c.mod.AddNamedMetadataOperand("llvm.module.flags",
|
|
||||||
c.ctx.MDNode([]llvm.Metadata{
|
|
||||||
llvm.ConstInt(c.ctx.Int32Type(), 1, false).ConstantAsMetadata(), // Error on mismatch
|
|
||||||
c.ctx.MDString("target-abi"),
|
|
||||||
c.ctx.MDString(c.ABI),
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
/*
|
|
||||||
ret.Module = c.mod
|
|
||||||
err = c.errs.ToError()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p Package) Dispose() {
|
|
||||||
p.Module.Dispose()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p Package) WriteFile(file string) (err error) {
|
|
||||||
f, err := os.Create(file)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
err = p.WriteTo(f)
|
|
||||||
f.Close()
|
|
||||||
if err != nil {
|
|
||||||
os.Remove(file)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p Package) WriteTo(f *os.File) (err error) {
|
|
||||||
if runtime.GOOS == "windows" {
|
|
||||||
// Work around a problem on Windows.
|
|
||||||
// For some reason, WriteBitcodeToFile causes TinyGo to
|
|
||||||
// exit with the following message:
|
|
||||||
// LLVM ERROR: IO failure on output stream: Bad file descriptor
|
|
||||||
buf := llvm.WriteBitcodeToMemoryBuffer(p.Module)
|
|
||||||
defer buf.Dispose()
|
|
||||||
_, err = f.Write(buf.Bytes())
|
|
||||||
} else {
|
|
||||||
// Otherwise, write bitcode directly to the file (probably
|
|
||||||
// faster).
|
|
||||||
err = llvm.WriteBitcodeToFile(p.Module, f)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import (
|
|||||||
"go/parser"
|
"go/parser"
|
||||||
"go/token"
|
"go/token"
|
||||||
"go/types"
|
"go/types"
|
||||||
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
llssa "github.com/goplus/llgo/ssa"
|
llssa "github.com/goplus/llgo/ssa"
|
||||||
@@ -42,6 +43,12 @@ func testCompile(t *testing.T, src, expected string) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal("BuildPackage failed:", err)
|
t.Fatal("BuildPackage failed:", err)
|
||||||
}
|
}
|
||||||
|
foo.WriteTo(os.Stderr)
|
||||||
|
for _, m := range foo.Members {
|
||||||
|
if f, ok := m.(*ssa.Function); ok {
|
||||||
|
f.WriteTo(os.Stderr)
|
||||||
|
}
|
||||||
|
}
|
||||||
prog := llssa.NewProgram(nil)
|
prog := llssa.NewProgram(nil)
|
||||||
ret, err := NewPackage(prog, foo, nil)
|
ret, err := NewPackage(prog, foo, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
24
ssa/expr.go
24
ssa/expr.go
@@ -149,7 +149,8 @@ func isPredOp(op token.Token) bool {
|
|||||||
return op >= predOpBase && op <= predOpLast
|
return op >= predOpBase && op <= predOpLast
|
||||||
}
|
}
|
||||||
|
|
||||||
// op:
|
// The BinOp instruction yields the result of binary operation (x op y).
|
||||||
|
// op can be:
|
||||||
// ADD SUB MUL QUO REM + - * / %
|
// ADD SUB MUL QUO REM + - * / %
|
||||||
// AND OR XOR SHL SHR AND_NOT & | ^ << >> &^
|
// AND OR XOR SHL SHR AND_NOT & | ^ << >> &^
|
||||||
// EQL NEQ LSS LEQ GTR GEQ == != < <= < >=
|
// EQL NEQ LSS LEQ GTR GEQ == != < <= < >=
|
||||||
@@ -195,6 +196,27 @@ func (b Builder) BinOp(op token.Token, x, y Expr) Expr {
|
|||||||
panic("todo")
|
panic("todo")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The UnOp instruction yields the result of (op x).
|
||||||
|
// ARROW is channel receive.
|
||||||
|
// MUL is pointer indirection (load).
|
||||||
|
// XOR is bitwise complement.
|
||||||
|
// SUB is negation.
|
||||||
|
// NOT is logical negation.
|
||||||
|
func (b Builder) UnOp(op token.Token, x Expr) Expr {
|
||||||
|
switch op {
|
||||||
|
case token.MUL:
|
||||||
|
return b.Load(x)
|
||||||
|
}
|
||||||
|
panic("todo")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load returns the value at the pointer ptr.
|
||||||
|
func (b Builder) Load(ptr Expr) Expr {
|
||||||
|
elem := ptr.t.(*types.Pointer).Elem()
|
||||||
|
telem := b.prog.llvmType(elem)
|
||||||
|
return Expr{llvm.CreateLoad(b.impl, telem.ll, ptr.impl), telem}
|
||||||
|
}
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
func (b Builder) Call(fn Expr, args ...Expr) (ret Expr) {
|
func (b Builder) Call(fn Expr, args ...Expr) (ret Expr) {
|
||||||
|
|||||||
@@ -228,3 +228,25 @@ define i64 @fn(i64 %0, double %1) {
|
|||||||
}
|
}
|
||||||
`)
|
`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestUnOp(t *testing.T) {
|
||||||
|
prog := NewProgram(nil)
|
||||||
|
pkg := prog.NewPackage("bar", "foo/bar")
|
||||||
|
params := types.NewTuple(
|
||||||
|
types.NewVar(0, nil, "p", types.NewPointer(types.Typ[types.Int])),
|
||||||
|
)
|
||||||
|
rets := types.NewTuple(types.NewVar(0, nil, "", types.Typ[types.Int]))
|
||||||
|
sig := types.NewSignatureType(nil, nil, nil, params, rets, false)
|
||||||
|
fn := pkg.NewFunc("fn", sig)
|
||||||
|
b := fn.MakeBody("")
|
||||||
|
ret := b.UnOp(token.MUL, fn.Param(0))
|
||||||
|
b.Return(ret)
|
||||||
|
assertPkg(t, pkg, `; ModuleID = 'foo/bar'
|
||||||
|
source_filename = "foo/bar"
|
||||||
|
|
||||||
|
define i64 @fn(ptr %0) {
|
||||||
|
%2 = load i64, ptr %0, align 4
|
||||||
|
ret i64 %2
|
||||||
|
}
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user