Skip to content

Commit 0531d26

Browse files
committed
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.
1 parent 6691f4c commit 0531d26

File tree

6 files changed

+163
-12
lines changed

6 files changed

+163
-12
lines changed

.circleci/config.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ commands:
8585
key: wasi-libc-sysroot-systemclang-v1
8686
paths:
8787
- lib/wasi-libc/sysroot
88-
- run: go test -v -tags=llvm<<parameters.llvm>> ./cgo ./compileopts ./interp ./transform .
88+
- run: go test -v -tags=llvm<<parameters.llvm>> ./cgo ./compileopts ./compiler ./interp ./transform .
8989
- run: make gen-device -j4
9090
- run: make smoketest
9191
- save_cache:

Makefile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ endif
111111
clean:
112112
@rm -rf build
113113

114-
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
114+
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
115115
fmt:
116116
@gofmt -l -w $(FMT_PATHS)
117117
fmt-check:
@@ -174,7 +174,7 @@ tinygo:
174174
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) build -o build/tinygo$(EXE) -tags byollvm .
175175

176176
test: wasi-libc
177-
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) test -v -tags byollvm ./cgo ./compileopts ./interp ./transform .
177+
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) test -v -tags byollvm ./cgo ./compileopts ./compiler ./interp ./transform .
178178

179179
tinygo-test:
180180
cd tests/tinygotest && tinygo test

compiler/compiler.go

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -98,15 +98,9 @@ func NewTargetMachine(config *compileopts.Config) (llvm.TargetMachine, error) {
9898
return machine, nil
9999
}
100100

101-
// Compile the given package path or .go file path. Return an error when this
102-
// fails (in any stage). If successful it returns the LLVM module and a list of
103-
// extra C files to be compiled. If not, one or more errors will be returned.
104-
//
105-
// The fact that it returns a list of filenames to compile is a layering
106-
// violation. Eventually, this Compile function should only compile a single
107-
// package and not the whole program, and loading of the program (including CGo
108-
// processing) should be moved outside the compiler package.
109-
func Compile(pkgName string, machine llvm.TargetMachine, config *compileopts.Config) (llvm.Module, []string, []error) {
101+
// newCompilerContext builds a new *compilerContext based on the provided
102+
// configuration, ready to compile Go SSA to LLVM IR.
103+
func newCompilerContext(pkgName string, machine llvm.TargetMachine, config *compileopts.Config) *compilerContext {
110104
c := &compilerContext{
111105
Config: config,
112106
difiles: make(map[string]llvm.Metadata),
@@ -140,6 +134,20 @@ func Compile(pkgName string, machine llvm.TargetMachine, config *compileopts.Con
140134
c.funcPtrAddrSpace = dummyFunc.Type().PointerAddressSpace()
141135
dummyFunc.EraseFromParentAsFunction()
142136

137+
return c
138+
}
139+
140+
// Compile the given package path or .go file path. Return an error when this
141+
// fails (in any stage). If successful it returns the LLVM module and a list of
142+
// extra C files to be compiled. If not, one or more errors will be returned.
143+
//
144+
// The fact that it returns a list of filenames to compile is a layering
145+
// violation. Eventually, this Compile function should only compile a single
146+
// package and not the whole program, and loading of the program (including CGo
147+
// processing) should be moved outside the compiler package.
148+
func Compile(pkgName string, machine llvm.TargetMachine, config *compileopts.Config) (llvm.Module, []string, []error) {
149+
c := newCompilerContext(pkgName, machine, config)
150+
143151
// Prefix the GOPATH with the system GOROOT, as GOROOT is already set to
144152
// the TinyGo root.
145153
overlayGopath := goenv.Get("GOPATH")

compiler/compiler_test.go

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
package compiler
2+
3+
import (
4+
"bytes"
5+
"flag"
6+
"fmt"
7+
"go/ast"
8+
"go/parser"
9+
"go/token"
10+
"go/types"
11+
"io/ioutil"
12+
"path/filepath"
13+
"strings"
14+
"testing"
15+
16+
"github.com/tinygo-org/tinygo/compileopts"
17+
"github.com/tinygo-org/tinygo/compiler/ircheck"
18+
"golang.org/x/tools/go/ssa"
19+
"golang.org/x/tools/go/ssa/ssautil"
20+
"tinygo.org/x/go-llvm"
21+
)
22+
23+
var flagUpdate = flag.Bool("update", false, "update all tests")
24+
25+
func TestCompiler(t *testing.T) {
26+
for _, name := range []string{"basic"} {
27+
t.Run(name, func(t *testing.T) {
28+
t.Parallel()
29+
30+
runCompilerTest(t, name)
31+
})
32+
}
33+
}
34+
35+
func runCompilerTest(t *testing.T, name string) {
36+
// Read the AST in memory.
37+
path := filepath.Join("testdata", name+".go")
38+
fset := token.NewFileSet()
39+
f, err := parser.ParseFile(fset, path, nil, parser.ParseComments)
40+
if err != nil {
41+
t.Fatal("could not parse Go source file:", err)
42+
}
43+
files := []*ast.File{f}
44+
45+
// Create Go SSA from the AST.
46+
var typecheckErrors []error
47+
typesConfig := types.Config{
48+
Error: func(err error) {
49+
typecheckErrors = append(typecheckErrors, err)
50+
},
51+
Importer: simpleImporter{},
52+
Sizes: types.SizesFor("gccgo", "arm"),
53+
}
54+
pkg, _, err := ssautil.BuildPackage(&typesConfig, fset, types.NewPackage("main", ""), files, ssa.SanityCheckFunctions|ssa.BareInits|ssa.GlobalDebug)
55+
if err != nil && len(typecheckErrors) == 0 {
56+
// Only report errors when no type errors are found (an
57+
// unexpected condition).
58+
t.Error(err)
59+
}
60+
61+
// Configure the compiler.
62+
config := compileopts.Config{
63+
Options: &compileopts.Options{},
64+
Target: &compileopts.TargetSpec{
65+
Triple: "armv7m-none-eabi",
66+
},
67+
}
68+
machine, err := NewTargetMachine(&config)
69+
if err != nil {
70+
t.Fatal(err)
71+
}
72+
c := newCompilerContext("main", machine, &config)
73+
irbuilder := c.ctx.NewBuilder()
74+
defer irbuilder.Dispose()
75+
76+
// Create LLVM IR from the Go SSA.
77+
c.createPackage(pkg, irbuilder)
78+
79+
// Check the IR with the LLVM verifier.
80+
if err := llvm.VerifyModule(c.mod, llvm.PrintMessageAction); err != nil {
81+
t.Error("verification error after IR construction")
82+
}
83+
84+
// Check the IR with our own verifier (which checks for different things).
85+
errs := ircheck.Module(c.mod)
86+
for _, err := range errs {
87+
t.Error(err)
88+
}
89+
90+
// Check whether the IR matches the expected IR.
91+
ir := c.mod.String()
92+
ir = ir[strings.Index(ir, "\ntarget datalayout = ")+1:]
93+
outfile := filepath.Join("testdata", name+".ll")
94+
if *flagUpdate {
95+
err := ioutil.WriteFile(outfile, []byte(ir), 0666)
96+
if err != nil {
97+
t.Error("could not read output file:", err)
98+
}
99+
} else {
100+
ir2, err := ioutil.ReadFile(outfile)
101+
if err != nil {
102+
t.Fatal("could not read input file:", err)
103+
}
104+
ir2 = bytes.Replace(ir2, []byte("\r\n"), []byte("\n"), -1)
105+
if ir != string(ir2) {
106+
t.Error("output did not match")
107+
}
108+
}
109+
}
110+
111+
// simpleImporter implements the types.Importer interface, but only allows
112+
// importing the unsafe package.
113+
type simpleImporter struct {
114+
}
115+
116+
// Import implements the Importer interface. For testing usage only: it only
117+
// supports importing the unsafe package.
118+
func (i simpleImporter) Import(path string) (*types.Package, error) {
119+
switch path {
120+
case "unsafe":
121+
return types.Unsafe, nil
122+
default:
123+
return nil, fmt.Errorf("importer not implemented for package %s", path)
124+
}
125+
}

compiler/testdata/basic.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package main
2+
3+
func add(x, y int) int {
4+
return x + y
5+
}

compiler/testdata/basic.ll

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
target datalayout = "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64"
2+
target triple = "armv7m-none-eabi"
3+
4+
define internal i32 @main.add(i32 %x, i32 %y, i8* %context, i8* %parentHandle) unnamed_addr {
5+
entry:
6+
%0 = add i32 %x, %y
7+
ret i32 %0
8+
}
9+
10+
define internal void @main.init(i8* %context, i8* %parentHandle) unnamed_addr {
11+
entry:
12+
ret void
13+
}

0 commit comments

Comments
 (0)