mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-16 02:33:28 +00:00
compiler: add tests
This commit adds a very small test case. More importantly, it adds a framework for other tests to be added in the future.
This commit is contained in:
@@ -85,7 +85,7 @@ commands:
|
|||||||
key: wasi-libc-sysroot-systemclang-v1
|
key: wasi-libc-sysroot-systemclang-v1
|
||||||
paths:
|
paths:
|
||||||
- lib/wasi-libc/sysroot
|
- lib/wasi-libc/sysroot
|
||||||
- run: go test -v -tags=llvm<<parameters.llvm>> ./cgo ./compileopts ./interp ./transform .
|
- run: go test -v -tags=llvm<<parameters.llvm>> ./cgo ./compileopts ./compiler ./interp ./transform .
|
||||||
- run: make gen-device -j4
|
- run: make gen-device -j4
|
||||||
- run: make smoketest
|
- run: make smoketest
|
||||||
- save_cache:
|
- save_cache:
|
||||||
|
|||||||
@@ -111,7 +111,7 @@ endif
|
|||||||
clean:
|
clean:
|
||||||
@rm -rf build
|
@rm -rf build
|
||||||
|
|
||||||
FMT_PATHS = ./*.go builder cgo compiler interp ir loader src/device/arm src/examples src/machine src/os src/reflect src/runtime src/sync src/syscall src/internal/reflectlite transform
|
FMT_PATHS = ./*.go builder cgo compiler compiler/testdata interp ir loader src/device/arm src/examples src/machine src/os src/reflect src/runtime src/sync src/syscall src/internal/reflectlite transform
|
||||||
fmt:
|
fmt:
|
||||||
@gofmt -l -w $(FMT_PATHS)
|
@gofmt -l -w $(FMT_PATHS)
|
||||||
fmt-check:
|
fmt-check:
|
||||||
@@ -174,7 +174,7 @@ tinygo:
|
|||||||
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) build -o build/tinygo$(EXE) -tags byollvm .
|
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) build -o build/tinygo$(EXE) -tags byollvm .
|
||||||
|
|
||||||
test: wasi-libc
|
test: wasi-libc
|
||||||
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) test -v -tags byollvm ./cgo ./compileopts ./interp ./transform .
|
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) test -v -tags byollvm ./cgo ./compileopts ./compiler ./interp ./transform .
|
||||||
|
|
||||||
tinygo-test:
|
tinygo-test:
|
||||||
cd tests/tinygotest && tinygo test
|
cd tests/tinygotest && tinygo test
|
||||||
|
|||||||
+17
-9
@@ -98,15 +98,9 @@ func NewTargetMachine(config *compileopts.Config) (llvm.TargetMachine, error) {
|
|||||||
return machine, nil
|
return machine, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compile the given package path or .go file path. Return an error when this
|
// newCompilerContext builds a new *compilerContext based on the provided
|
||||||
// fails (in any stage). If successful it returns the LLVM module and a list of
|
// configuration, ready to compile Go SSA to LLVM IR.
|
||||||
// extra C files to be compiled. If not, one or more errors will be returned.
|
func newCompilerContext(pkgName string, machine llvm.TargetMachine, config *compileopts.Config) *compilerContext {
|
||||||
//
|
|
||||||
// The fact that it returns a list of filenames to compile is a layering
|
|
||||||
// violation. Eventually, this Compile function should only compile a single
|
|
||||||
// package and not the whole program, and loading of the program (including CGo
|
|
||||||
// processing) should be moved outside the compiler package.
|
|
||||||
func Compile(pkgName string, machine llvm.TargetMachine, config *compileopts.Config) (llvm.Module, []string, []error) {
|
|
||||||
c := &compilerContext{
|
c := &compilerContext{
|
||||||
Config: config,
|
Config: config,
|
||||||
difiles: make(map[string]llvm.Metadata),
|
difiles: make(map[string]llvm.Metadata),
|
||||||
@@ -140,6 +134,20 @@ func Compile(pkgName string, machine llvm.TargetMachine, config *compileopts.Con
|
|||||||
c.funcPtrAddrSpace = dummyFunc.Type().PointerAddressSpace()
|
c.funcPtrAddrSpace = dummyFunc.Type().PointerAddressSpace()
|
||||||
dummyFunc.EraseFromParentAsFunction()
|
dummyFunc.EraseFromParentAsFunction()
|
||||||
|
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compile the given package path or .go file path. Return an error when this
|
||||||
|
// fails (in any stage). If successful it returns the LLVM module and a list of
|
||||||
|
// extra C files to be compiled. If not, one or more errors will be returned.
|
||||||
|
//
|
||||||
|
// The fact that it returns a list of filenames to compile is a layering
|
||||||
|
// violation. Eventually, this Compile function should only compile a single
|
||||||
|
// package and not the whole program, and loading of the program (including CGo
|
||||||
|
// processing) should be moved outside the compiler package.
|
||||||
|
func Compile(pkgName string, machine llvm.TargetMachine, config *compileopts.Config) (llvm.Module, []string, []error) {
|
||||||
|
c := newCompilerContext(pkgName, machine, config)
|
||||||
|
|
||||||
// Prefix the GOPATH with the system GOROOT, as GOROOT is already set to
|
// Prefix the GOPATH with the system GOROOT, as GOROOT is already set to
|
||||||
// the TinyGo root.
|
// the TinyGo root.
|
||||||
overlayGopath := goenv.Get("GOPATH")
|
overlayGopath := goenv.Get("GOPATH")
|
||||||
|
|||||||
@@ -0,0 +1,136 @@
|
|||||||
|
package compiler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"go/ast"
|
||||||
|
"go/parser"
|
||||||
|
"go/token"
|
||||||
|
"go/types"
|
||||||
|
"io/ioutil"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/tinygo-org/tinygo/compileopts"
|
||||||
|
"github.com/tinygo-org/tinygo/compiler/ircheck"
|
||||||
|
"golang.org/x/tools/go/ssa"
|
||||||
|
"golang.org/x/tools/go/ssa/ssautil"
|
||||||
|
"tinygo.org/x/go-llvm"
|
||||||
|
)
|
||||||
|
|
||||||
|
var flagUpdate = flag.Bool("update", false, "update all tests")
|
||||||
|
|
||||||
|
func TestCompiler(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
for _, name := range []string{"basic"} {
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
runCompilerTest(t, name)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func runCompilerTest(t *testing.T, name string) {
|
||||||
|
// Read the AST in memory.
|
||||||
|
path := filepath.Join("testdata", name+".go")
|
||||||
|
fset := token.NewFileSet()
|
||||||
|
f, err := parser.ParseFile(fset, path, nil, parser.ParseComments)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("could not parse Go source file:", err)
|
||||||
|
}
|
||||||
|
files := []*ast.File{f}
|
||||||
|
|
||||||
|
// Create Go SSA from the AST.
|
||||||
|
var typecheckErrors []error
|
||||||
|
var typecheckErrorsLock sync.Mutex
|
||||||
|
typesConfig := types.Config{
|
||||||
|
Error: func(err error) {
|
||||||
|
typecheckErrorsLock.Lock()
|
||||||
|
defer typecheckErrorsLock.Unlock()
|
||||||
|
typecheckErrors = append(typecheckErrors, err)
|
||||||
|
},
|
||||||
|
Importer: simpleImporter{},
|
||||||
|
Sizes: types.SizesFor("gccgo", "arm"),
|
||||||
|
}
|
||||||
|
pkg, _, err := ssautil.BuildPackage(&typesConfig, fset, types.NewPackage("main", ""), files, ssa.SanityCheckFunctions|ssa.BareInits|ssa.GlobalDebug)
|
||||||
|
for _, err := range typecheckErrors {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
if err != nil && len(typecheckErrors) == 0 {
|
||||||
|
// Only report errors when no type errors are found (an
|
||||||
|
// unexpected condition).
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
if t.Failed() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configure the compiler.
|
||||||
|
config := compileopts.Config{
|
||||||
|
Options: &compileopts.Options{},
|
||||||
|
Target: &compileopts.TargetSpec{
|
||||||
|
Triple: "armv7m-none-eabi",
|
||||||
|
BuildTags: []string{"cortexm", "baremetal", "linux", "arm"},
|
||||||
|
Scheduler: "tasks",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
machine, err := NewTargetMachine(&config)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
c := newCompilerContext("main", machine, &config)
|
||||||
|
irbuilder := c.ctx.NewBuilder()
|
||||||
|
defer irbuilder.Dispose()
|
||||||
|
|
||||||
|
// Create LLVM IR from the Go SSA.
|
||||||
|
c.createPackage(pkg, irbuilder)
|
||||||
|
|
||||||
|
// Check the IR with the LLVM verifier.
|
||||||
|
if err := llvm.VerifyModule(c.mod, llvm.PrintMessageAction); err != nil {
|
||||||
|
t.Error("verification error after IR construction")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check the IR with our own verifier (which checks for different things).
|
||||||
|
errs := ircheck.Module(c.mod)
|
||||||
|
for _, err := range errs {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check whether the IR matches the expected IR.
|
||||||
|
ir := c.mod.String()
|
||||||
|
ir = ir[strings.Index(ir, "\ntarget datalayout = ")+1:]
|
||||||
|
outfile := filepath.Join("testdata", name+".ll")
|
||||||
|
if *flagUpdate {
|
||||||
|
err := ioutil.WriteFile(outfile, []byte(ir), 0666)
|
||||||
|
if err != nil {
|
||||||
|
t.Error("could not read output file:", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ir2, err := ioutil.ReadFile(outfile)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("could not read input file:", err)
|
||||||
|
}
|
||||||
|
ir2 = bytes.Replace(ir2, []byte("\r\n"), []byte("\n"), -1)
|
||||||
|
if ir != string(ir2) {
|
||||||
|
t.Error("output did not match")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// simpleImporter implements the types.Importer interface, but only allows
|
||||||
|
// importing the unsafe package.
|
||||||
|
type simpleImporter struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
// Import implements the Importer interface. For testing usage only: it only
|
||||||
|
// supports importing the unsafe package.
|
||||||
|
func (i simpleImporter) Import(path string) (*types.Package, error) {
|
||||||
|
switch path {
|
||||||
|
case "unsafe":
|
||||||
|
return types.Unsafe, nil
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("importer not implemented for package %s", path)
|
||||||
|
}
|
||||||
|
}
|
||||||
Vendored
+5
@@ -0,0 +1,5 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
func add(x, y int) int {
|
||||||
|
return x + y
|
||||||
|
}
|
||||||
Vendored
+13
@@ -0,0 +1,13 @@
|
|||||||
|
target datalayout = "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64"
|
||||||
|
target triple = "armv7m-none-eabi"
|
||||||
|
|
||||||
|
define internal i32 @main.add(i32 %x, i32 %y, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
|
entry:
|
||||||
|
%0 = add i32 %x, %y
|
||||||
|
ret i32 %0
|
||||||
|
}
|
||||||
|
|
||||||
|
define internal void @main.init(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
|
entry:
|
||||||
|
ret void
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user