Hello world!
Этот коммит содержится в:
коммит
bc28975c8c
4 изменённых файлов: 257 добавлений и 0 удалений
1
.gitignore
предоставленный
Обычный файл
1
.gitignore
предоставленный
Обычный файл
|
@ -0,0 +1 @@
|
|||
build
|
22
Makefile
Обычный файл
22
Makefile
Обычный файл
|
@ -0,0 +1,22 @@
|
|||
|
||||
all: tgo
|
||||
tgo: build/tgo
|
||||
test: build/hello.o
|
||||
|
||||
.PHONY: all tgo test test-run clean
|
||||
|
||||
build/tgo: *.go
|
||||
@mkdir -p build
|
||||
@go build -o build/tgo -i .
|
||||
|
||||
build/hello.o: build/tgo hello/hello.go
|
||||
@./build/tgo -printir -target x86_64-pc-linux-gnu -o build/hello.o hello/hello.go
|
||||
|
||||
build/hello: build/hello.o
|
||||
@clang -o build/hello build/hello.o
|
||||
|
||||
test-run: build/hello
|
||||
@./build/hello
|
||||
|
||||
clean:
|
||||
@rm -rf build
|
6
hello/hello.go
Обычный файл
6
hello/hello.go
Обычный файл
|
@ -0,0 +1,6 @@
|
|||
|
||||
package main
|
||||
|
||||
func main() {
|
||||
println("Hello world from Go!")
|
||||
}
|
228
tgo.go
Обычный файл
228
tgo.go
Обычный файл
|
@ -0,0 +1,228 @@
|
|||
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/constant"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"os"
|
||||
|
||||
"llvm.org/llvm/bindings/go/llvm"
|
||||
)
|
||||
|
||||
func init() {
|
||||
llvm.InitializeAllTargets()
|
||||
llvm.InitializeAllTargetMCs()
|
||||
llvm.InitializeAllTargetInfos()
|
||||
llvm.InitializeAllAsmParsers()
|
||||
llvm.InitializeAllAsmPrinters()
|
||||
}
|
||||
|
||||
type Compiler struct {
|
||||
mod llvm.Module
|
||||
ctx llvm.Context
|
||||
builder llvm.Builder
|
||||
machine llvm.TargetMachine
|
||||
putsFunc llvm.Value
|
||||
}
|
||||
|
||||
func NewCompiler(path, triplet string) (*Compiler, error) {
|
||||
c := &Compiler{}
|
||||
|
||||
target, err := llvm.GetTargetFromTriple(triplet)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c.machine = target.CreateTargetMachine(triplet, "", "", llvm.CodeGenLevelDefault, llvm.RelocDefault, llvm.CodeModelDefault)
|
||||
|
||||
c.mod = llvm.NewModule(path)
|
||||
c.ctx = c.mod.Context()
|
||||
c.builder = c.ctx.NewBuilder()
|
||||
|
||||
putsType := llvm.FunctionType(llvm.Int32Type(), []llvm.Type{llvm.PointerType(llvm.Int8Type(), 0)}, false)
|
||||
c.putsFunc = llvm.AddFunction(c.mod, "puts", putsType)
|
||||
|
||||
mainType := llvm.FunctionType(llvm.Int32Type(), nil, false)
|
||||
mainFunc := llvm.AddFunction(c.mod, "main", mainType)
|
||||
start := c.ctx.AddBasicBlock(mainFunc, "start")
|
||||
c.builder.SetInsertPointAtEnd(start)
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func (c *Compiler) Parse(path string) error {
|
||||
fset := token.NewFileSet()
|
||||
file, err := parser.ParseFile(fset, path, nil, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Println("package:", file.Name.Name)
|
||||
|
||||
for _, decl := range file.Decls {
|
||||
switch v := decl.(type) {
|
||||
case *ast.FuncDecl:
|
||||
err := c.parseFunc(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
return errors.New("unknown declaration")
|
||||
}
|
||||
}
|
||||
|
||||
c.builder.CreateRet(llvm.ConstInt(llvm.Int32Type(), 0, false))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Compiler) IR() string {
|
||||
return c.mod.String()
|
||||
}
|
||||
|
||||
func (c *Compiler) parseFunc(f *ast.FuncDecl) error {
|
||||
fmt.Println("func:", f.Name)
|
||||
// TODO: external functions
|
||||
for _, stmt := range f.Body.List {
|
||||
err := c.parseStmt(stmt)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Compiler) parseStmt(stmt ast.Node) error {
|
||||
switch v := stmt.(type) {
|
||||
case *ast.ExprStmt:
|
||||
err := c.parseExpr(v.X)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
return errors.New("unknown stmt")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Compiler) parseExpr(expr ast.Expr) error {
|
||||
switch v := expr.(type) {
|
||||
case *ast.CallExpr:
|
||||
values := []llvm.Value{}
|
||||
|
||||
zero := llvm.ConstInt(llvm.Int32Type(), 0, false)
|
||||
name := v.Fun.(*ast.Ident).Name
|
||||
fmt.Printf(" call: %s\n", name)
|
||||
|
||||
if name != "println" {
|
||||
return errors.New("implement anything other than println()")
|
||||
}
|
||||
|
||||
for _, arg := range v.Args {
|
||||
switch arg := arg.(type) {
|
||||
case *ast.BasicLit:
|
||||
fmt.Printf(" arg: %s\n", arg.Value)
|
||||
val := constant.MakeFromLiteral(arg.Value, arg.Kind, 0)
|
||||
switch arg.Kind {
|
||||
case token.STRING:
|
||||
msg := c.builder.CreateGlobalString(constant.StringVal(val) + "\x00", "")
|
||||
msgCast := c.builder.CreateGEP(msg, []llvm.Value{zero, zero}, "")
|
||||
values = append(values, msgCast)
|
||||
default:
|
||||
return errors.New("todo: print anything other than strings")
|
||||
}
|
||||
default:
|
||||
return errors.New("unknown arg type")
|
||||
}
|
||||
}
|
||||
c.builder.CreateCall(c.putsFunc, values, "")
|
||||
default:
|
||||
return errors.New("unknown expr")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Compiler) Verify() error {
|
||||
return llvm.VerifyModule(c.mod, llvm.PrintMessageAction)
|
||||
}
|
||||
|
||||
func (c *Compiler) Optimize(optLevel int) {
|
||||
builder := llvm.NewPassManagerBuilder()
|
||||
defer builder.Dispose()
|
||||
builder.SetOptLevel(optLevel)
|
||||
builder.UseInlinerWithThreshold(200) // TODO depend on opt level, and -Os
|
||||
|
||||
funcPasses := llvm.NewFunctionPassManagerForModule(c.mod)
|
||||
defer funcPasses.Dispose()
|
||||
builder.PopulateFunc(funcPasses)
|
||||
|
||||
modPasses := llvm.NewPassManager()
|
||||
defer modPasses.Dispose()
|
||||
builder.Populate(modPasses)
|
||||
|
||||
modPasses.Run(c.mod)
|
||||
}
|
||||
|
||||
func (c *Compiler) EmitObject(path string) error {
|
||||
buf, err := c.machine.EmitToMemoryBuffer(c.mod, llvm.ObjectFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
f, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE, 0666)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
f.Write(buf.Bytes())
|
||||
f.Close()
|
||||
return nil
|
||||
}
|
||||
|
||||
func Compile(inpath, outpath, target string, printIR bool) error {
|
||||
c, err := NewCompiler(inpath, target)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = c.Parse(inpath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.Verify()
|
||||
c.Optimize(2)
|
||||
c.Verify()
|
||||
|
||||
if printIR {
|
||||
fmt.Println(c.IR())
|
||||
}
|
||||
|
||||
err = c.EmitObject(outpath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
func main() {
|
||||
outpath := flag.String("o", "", "output filename")
|
||||
target := flag.String("target", "x86_64-pc-linux-gnu", "LLVM target")
|
||||
printIR := flag.Bool("printir", false, "print LLVM IR after optimizing")
|
||||
|
||||
flag.Parse()
|
||||
|
||||
if *outpath == "" || flag.NArg() != 1 {
|
||||
fmt.Fprintf(os.Stderr, "usage: %s [-printir] [-target=<target>] -o <output> <input>", os.Args[0])
|
||||
flag.PrintDefaults()
|
||||
}
|
||||
|
||||
err := Compile(flag.Args()[0], *outpath, *target, *printIR)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, "error:", err)
|
||||
}
|
||||
}
|
Загрузка…
Создание таблицы
Сослаться в новой задаче