Compare commits

..

1 Commits

Author SHA1 Message Date
sago35 8a3f96b221 builder: fixed a problem with multiple process build cases 2021-04-24 09:59:14 +09:00
36 changed files with 3378 additions and 869 deletions
+14 -14
View File
@@ -119,7 +119,7 @@ commands:
- lib/wasi-libc/sysroot
- run: go test -v -tags=llvm<<parameters.llvm>> ./cgo ./compileopts ./compiler ./interp ./transform .
- run: make gen-device -j4
- run: make smoketest XTENSA=0 RUN_SMOKETEST_JOBS=4
- run: make smoketest XTENSA=0
- run: make tinygo-test
- run: make wasmtest
- save_cache:
@@ -195,7 +195,7 @@ commands:
- ~/.cache/go-build
- /go/pkg/mod
- run: make gen-device -j4
- run: make smoketest TINYGO=build/tinygo RUN_SMOKETEST_JOBS=4
- run: make smoketest TINYGO=build/tinygo
build-linux:
steps:
- checkout
@@ -278,7 +278,7 @@ commands:
tar -C ~/lib -xf /tmp/tinygo.linux-amd64.tar.gz
ln -s ~/lib/tinygo/bin/tinygo /go/bin/tinygo
tinygo version
- run: make smoketest RUN_SMOKETEST_JOBS=4
- run: make smoketest
build-macos:
steps:
- checkout
@@ -294,16 +294,16 @@ commands:
variant: "macos"
- restore_cache:
keys:
- go-cache-macos-v3-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_PREVIOUS_BUILD_NUM }}
- go-cache-macos-v3-{{ checksum "go.mod" }}
- go-cache-macos-v2-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_PREVIOUS_BUILD_NUM }}
- go-cache-macos-v2-{{ checksum "go.mod" }}
- restore_cache:
keys:
- llvm-source-11-macos-v3
- llvm-source-11-macos-v2
- run:
name: "Fetch LLVM source"
command: make llvm-source
- save_cache:
key: llvm-source-11-macos-v3
key: llvm-source-11-macos-v2
paths:
- llvm-project/clang/lib/Headers
- llvm-project/clang/include
@@ -311,7 +311,7 @@ commands:
- llvm-project/llvm/include
- restore_cache:
keys:
- llvm-build-11-macos-v4
- llvm-build-11-macos-v3
- run:
name: "Build LLVM"
command: |
@@ -327,17 +327,17 @@ commands:
find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \;
fi
- save_cache:
key: llvm-build-11-macos-v4
key: llvm-build-11-macos-v3
paths:
llvm-build
- restore_cache:
keys:
- wasi-libc-sysroot-macos-v4
- wasi-libc-sysroot-macos-v3
- run:
name: "Build wasi-libc"
command: make wasi-libc
- save_cache:
key: wasi-libc-sysroot-macos-v4
key: wasi-libc-sysroot-macos-v3
paths:
- lib/wasi-libc/sysroot
- run:
@@ -357,9 +357,9 @@ commands:
tar -C /usr/local/opt -xf /tmp/tinygo.darwin-amd64.tar.gz
ln -s /usr/local/opt/tinygo/bin/tinygo /usr/local/bin/tinygo
tinygo version
- run: make smoketest AVR=0 RUN_SMOKETEST_JOBS=4
- run: make smoketest AVR=0
- save_cache:
key: go-cache-macos-v3-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_BUILD_NUM }}
key: go-cache-macos-v2-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_BUILD_NUM }}
paths:
- ~/.cache/go-build
- /go/pkg/mod
@@ -401,7 +401,7 @@ jobs:
- build-linux
build-macos:
macos:
xcode: "11.1.0" # macOS 10.14
xcode: "10.1.0"
steps:
- build-macos
+1 -4
View File
@@ -202,11 +202,8 @@ tinygo-test:
$(TINYGO) test text/scanner
$(TINYGO) test unicode/utf8
.PHONY: smoketest smoketest-commands
.PHONY: smoketest
smoketest:
@go run ./src/cmd/run-smoketest make smoketest-commands
smoketest-commands:
$(TINYGO) version
# test all examples (except pwm)
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/blinky1
+2 -7
View File
@@ -84,7 +84,7 @@ func copyFile(src, dst string) error {
return err
}
defer inf.Close()
outpath := src + ".tmp"
outpath := dst + ".tmp"
outf, err := os.Create(outpath)
if err != nil {
return err
@@ -101,10 +101,5 @@ func copyFile(src, dst string) error {
return err
}
// Rename may fail if another process is trying to write to
// the same file. However, in this case, the failure is
// acceptable because the result of the other process can be
// used.
os.Rename(outpath, dst)
return nil
return os.Rename(dst+".tmp", dst)
}
+4 -1
View File
@@ -167,7 +167,10 @@ func compileAndCacheCFile(abspath, tmpdir string, cflags []string, printCommands
if err != nil {
return "", err
}
os.Rename(f.Name(), depfileCachePath)
err = os.Rename(f.Name(), depfileCachePath)
if err != nil {
return "", err
}
// Move temporary object file to final location.
outpath, err := makeCFileCachePath(dependencySlice, depfileNameHash)
-1
View File
@@ -249,7 +249,6 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
PortReset: "false",
}
if goos == "darwin" {
spec.CFlags = append(spec.CFlags, "-isysroot", "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk")
spec.LDFlags = append(spec.LDFlags, "-Wl,-dead_strip")
} else {
spec.LDFlags = append(spec.LDFlags, "-no-pie", "-Wl,--gc-sections") // WARNING: clang < 5.0 requires -nopie
+1 -49
View File
@@ -964,59 +964,11 @@ func (b *builder) createFunction() {
}
}
// posser is an interface that's implemented by both ssa.Value and
// ssa.Instruction. It is implemented by everything that has a Pos() method,
// which is all that getPos() needs.
type posser interface {
Pos() token.Pos
}
// getPos returns position information for a ssa.Value or ssa.Instruction.
//
// Not all instructions have position information, especially when they're
// implicit (such as implicit casts or implicit returns at the end of a
// function). In these cases, it makes sense to try a bit harder to guess what
// the position really should be.
func getPos(val posser) token.Pos {
pos := val.Pos()
if pos != token.NoPos {
// Easy: position is known.
return pos
}
// No position information is known.
switch val := val.(type) {
case *ssa.MakeInterface:
return getPos(val.X)
case *ssa.Return:
syntax := val.Parent().Syntax()
if syntax != nil {
// non-synthetic
return syntax.End()
}
return token.NoPos
case *ssa.FieldAddr:
return getPos(val.X)
case *ssa.IndexAddr:
return getPos(val.X)
case *ssa.Slice:
return getPos(val.X)
case *ssa.Store:
return getPos(val.Addr)
case *ssa.Extract:
return getPos(val.Tuple)
default:
// This is reachable, for example with *ssa.Const, *ssa.If, and
// *ssa.Jump. They might be implemented in some way in the future.
return token.NoPos
}
}
// createInstruction builds the LLVM IR equivalent instructions for the
// particular Go SSA instruction.
func (b *builder) createInstruction(instr ssa.Instruction) {
if b.Debug {
pos := b.program.Fset.Position(getPos(instr))
pos := b.program.Fset.Position(instr.Pos())
b.SetCurrentDebugLocation(uint(pos.Line), uint(pos.Column), b.difunc, llvm.Metadata{})
}
-2
View File
@@ -9,9 +9,7 @@ require (
github.com/google/shlex v0.0.0-20181106134648-c34317bd91bf
github.com/marcinbor85/gohex v0.0.0-20200531091804-343a4b548892
github.com/mattn/go-colorable v0.1.8
github.com/sago35/ochan v0.0.0-20200313013834-e0f46da4579f // indirect
go.bug.st/serial v1.1.2
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
golang.org/x/sys v0.0.0-20210113181707-4bcb84eeeb78
golang.org/x/tools v0.0.0-20200216192241-b320d3a0f5a2
tinygo.org/x/go-llvm v0.0.0-20210325115028-e7b85195e81c
-4
View File
@@ -28,8 +28,6 @@ github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/sago35/ochan v0.0.0-20200313013834-e0f46da4579f h1:bHOcIl3AODQMzleLeSGs3xbIvAzhcgLTUwd8IUKqLMI=
github.com/sago35/ochan v0.0.0-20200313013834-e0f46da4579f/go.mod h1:55Pg0jnassdnFxlhS8HIU5pdYhEBYftGaknIP4bBUq8=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
go.bug.st/serial v1.0.0 h1:ogEPzrllCsnG00EqKRjeYvPRsO7NJW6DqykzkdD6E/k=
@@ -43,8 +41,6 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191128015809-6d18c012aee9 h1:ZBzSG/7F4eNKz2L3GE9o300RX0Az1Bw5HF7PDraD+qU=
+6 -22
View File
@@ -5,7 +5,6 @@ import (
"fmt"
"math"
"os"
"strconv"
"strings"
"time"
@@ -931,31 +930,16 @@ func (r *runner) runAtRuntime(fn *function, inst instruction, locals []value, me
result = r.builder.CreateBitCast(operands[0], inst.llvmInst.Type(), inst.name)
case llvm.ExtractValue:
indices := inst.llvmInst.Indices()
// Note: the Go LLVM API doesn't support multiple indices, so simulate
// this operation with some extra extractvalue instructions. Hopefully
// this is optimized to a single instruction.
agg := operands[0]
for i := 0; i < len(indices)-1; i++ {
agg = r.builder.CreateExtractValue(agg, int(indices[i]), inst.name+".agg")
if len(indices) != 1 {
panic("expected exactly one index")
}
result = r.builder.CreateExtractValue(agg, int(indices[len(indices)-1]), inst.name)
result = r.builder.CreateExtractValue(operands[0], int(indices[0]), inst.name)
case llvm.InsertValue:
indices := inst.llvmInst.Indices()
// Similar to extractvalue, we're working around a limitation in the Go
// LLVM API here by splitting the insertvalue into multiple instructions
// if there is more than one operand.
agg := operands[0]
aggregates := []llvm.Value{agg}
for i := 0; i < len(indices)-1; i++ {
agg = r.builder.CreateExtractValue(agg, int(indices[i]), inst.name+".agg"+strconv.Itoa(i))
aggregates = append(aggregates, agg)
if len(indices) != 1 {
panic("expected exactly one index")
}
result = operands[1]
for i := len(indices) - 1; i >= 0; i-- {
agg := aggregates[i]
result = r.builder.CreateInsertValue(agg, result, int(indices[i]), inst.name+".insertvalue"+strconv.Itoa(i))
}
result = r.builder.CreateInsertValue(operands[0], operands[1], int(indices[0]), inst.name)
case llvm.Add:
result = r.builder.CreateAdd(operands[0], operands[1], inst.name)
case llvm.Sub:
-10
View File
@@ -8,7 +8,6 @@ target triple = "x86_64--linux"
@main.exportedValue = global [1 x i16*] [i16* @main.exposedValue1]
@main.exposedValue1 = global i16 0
@main.exposedValue2 = global i16 0
@main.insertedValue = global {i8, i32, {float, {i64, i16}}} zeroinitializer
declare void @runtime.printint64(i64) unnamed_addr
@@ -72,13 +71,6 @@ entry:
call void @runtime.printint64(i64 %switch1)
call void @runtime.printint64(i64 %switch2)
; Test extractvalue/insertvalue with multiple operands.
%agg = call {i8, i32, {float, {i64, i16}}} @nestedStruct()
%elt = extractvalue {i8, i32, {float, {i64, i16}}} %agg, 2, 1, 0
call void @runtime.printint64(i64 %elt)
%agg2 = insertvalue {i8, i32, {float, {i64, i16}}} %agg, i64 5, 2, 1, 0
store {i8, i32, {float, {i64, i16}}} %agg2, {i8, i32, {float, {i64, i16}}}* @main.insertedValue
ret void
}
@@ -120,5 +112,3 @@ two:
otherwise:
ret i64 -1
}
declare {i8, i32, {float, {i64, i16}}} @nestedStruct()
-14
View File
@@ -7,7 +7,6 @@ target triple = "x86_64--linux"
@main.exportedValue = global [1 x i16*] [i16* @main.exposedValue1]
@main.exposedValue1 = global i16 0
@main.exposedValue2 = local_unnamed_addr global i16 0
@main.insertedValue = local_unnamed_addr global { i8, i32, { float, { i64, i16 } } } zeroinitializer
declare void @runtime.printint64(i64) unnamed_addr
@@ -28,17 +27,6 @@ entry:
store i16 7, i16* @main.exposedValue2
call void @runtime.printint64(i64 6)
call void @runtime.printint64(i64 -1)
%agg = call { i8, i32, { float, { i64, i16 } } } @nestedStruct()
%elt.agg = extractvalue { i8, i32, { float, { i64, i16 } } } %agg, 2
%elt.agg1 = extractvalue { float, { i64, i16 } } %elt.agg, 1
%elt = extractvalue { i64, i16 } %elt.agg1, 0
call void @runtime.printint64(i64 %elt)
%agg2.agg0 = extractvalue { i8, i32, { float, { i64, i16 } } } %agg, 2
%agg2.agg1 = extractvalue { float, { i64, i16 } } %agg2.agg0, 1
%agg2.insertvalue2 = insertvalue { i64, i16 } %agg2.agg1, i64 5, 0
%agg2.insertvalue1 = insertvalue { float, { i64, i16 } } %agg2.agg0, { i64, i16 } %agg2.insertvalue2, 1
%agg2.insertvalue0 = insertvalue { i8, i32, { float, { i64, i16 } } } %agg, { float, { i64, i16 } } %agg2.insertvalue1, 2
store { i8, i32, { float, { i64, i16 } } } %agg2.insertvalue0, { i8, i32, { float, { i64, i16 } } }* @main.insertedValue
ret void
}
@@ -79,5 +67,3 @@ two: ; preds = %entry
otherwise: ; preds = %entry
ret i64 -1
}
declare { i8, i32, { float, { i64, i16 } } } @nestedStruct() local_unnamed_addr
-219
View File
@@ -1,219 +0,0 @@
package main
import (
"bufio"
"bytes"
"crypto/md5"
"flag"
"fmt"
"io"
"io/ioutil"
"log"
"os"
"os/exec"
"path/filepath"
"runtime"
"strconv"
"strings"
"github.com/sago35/ochan"
"golang.org/x/sync/errgroup"
)
func main() {
threads := runtime.NumCPU()
if j, ok := os.LookupEnv(`RUN_SMOKETEST_JOBS`); ok {
n, err := strconv.ParseInt(j, 0, 0)
if err == nil {
threads = int(n)
}
}
flag.IntVar(&threads, "threads", threads, "threads of make smoketest")
flag.Parse()
if len(os.Args) < 2 {
fmt.Printf("usage: %s make smoketest\n", filepath.Base(os.Args[0]))
os.Exit(1)
}
commands, err := getMakeCommands(flag.Args())
if err != nil {
log.Fatalf(err.Error())
}
err = run(commands, threads)
if err != nil {
log.Fatalf(err.Error())
}
}
func getMakeCommands(args []string) ([]string, error) {
var buf bytes.Buffer
cmd := exec.Command(args[0], args[1:]...)
cmd.Args = append(cmd.Args, "-n")
cmd.Stdout = &buf
cmd.Stderr = &buf
err := cmd.Run()
if err != nil {
return nil, err
}
commands := []string{}
scanner := bufio.NewScanner(&buf)
for scanner.Scan() {
line := scanner.Text()
fields := strings.Fields(line)
if filepath.Base(fields[0]) == "tinygo" && (fields[1] == "build" || fields[1] == "version") {
commands = append(commands, line)
} else if strings.HasPrefix(line, "#") {
commands = append(commands, line)
}
}
return commands, nil
}
func run(commands []string, threads int) error {
ch := make(chan string, 1000)
o := ochan.NewOchan(ch, 100)
go func() {
limit := make(chan struct{}, threads)
var eg errgroup.Group
for _, command := range commands {
command := command
select {
case limit <- struct{}{}:
och := o.GetCh()
eg.Go(func() error {
var err error
if runtime.GOOS == `windows` {
command = convertToWindowsPath(command)
}
fields := strings.Fields(command)
if filepath.Base(fields[0]) == "tinygo" && fields[1] == "build" {
err = buildAndmd5sum(command, och)
} else if strings.HasPrefix(command, "#") {
och <- fmt.Sprintf("%s\n", command)
} else {
err = runCommand(command, och)
}
close(och)
<-limit
return err
})
}
}
if err := eg.Wait(); err != nil {
log.Fatal(err)
}
o.Wait()
close(ch)
}()
for s := range ch {
fmt.Print(s)
}
return nil
}
func runCommand(command string, ch chan string) error {
var buf bytes.Buffer
fmt.Fprintf(&buf, "%s\n", command)
args := strings.Fields(command)
cmd := exec.Command(args[0], args[1:]...)
cmd.Stdout = &buf
cmd.Stderr = &buf
err := cmd.Run()
if err != nil {
ch <- buf.String()
return err
}
ch <- buf.String()
return nil
}
func buildAndmd5sum(command string, ch chan string) error {
var buf bytes.Buffer
fmt.Fprintf(&buf, "%s\n", command)
args := strings.Fields(command)
output := ""
tmpOutput := ""
tmpdir, err := ioutil.TempDir("", "")
if err != nil {
return err
}
for i := range args {
if args[i] == "-o" {
output = args[i+1]
ext := filepath.Ext(output)
tmpOutput = filepath.Join(tmpdir, fmt.Sprintf("%s.%s", filepath.Base(output), ext))
args[i+1] = tmpOutput
} else if strings.HasPrefix(args[i], "-o=") {
output = args[i][3:]
ext := filepath.Ext(output)
tmpOutput = filepath.Join(tmpdir, fmt.Sprintf("%s.%s", filepath.Base(output), ext))
args[i] = fmt.Sprintf("-o=%s", tmpOutput)
}
}
cmd := exec.Command(args[0], args[1:]...)
cmd.Stdout = &buf
cmd.Stderr = &buf
err = cmd.Run()
if err != nil {
ch <- buf.String()
return err
}
md5str, err := calcMD5(tmpOutput)
if err != nil {
ch <- buf.String()
return err
}
fmt.Fprintf(&buf, "%s %s\n", md5str, output)
ch <- buf.String()
return nil
}
func calcMD5(f string) (string, error) {
r, err := os.Open(f)
if err != nil {
return "", err
}
defer r.Close()
h := md5.New()
if _, err := io.Copy(h, r); err != nil {
return "", err
}
return fmt.Sprintf("%x", h.Sum(nil)), nil
}
func convertToWindowsPath(command string) string {
if !strings.HasPrefix(command, `/`) {
return command
}
command = fmt.Sprintf("%c:%s", command[1], command[2:])
return command
}
+6 -15
View File
@@ -26,21 +26,12 @@ type SCB_Type struct {
SHPR2 volatile.Register32 // 0xD1C: System Handler Priority Register 2
SHPR3 volatile.Register32 // 0xD20: System Handler Priority Register 3
// the following are only applicable for Cortex-M3/M33/M4/M7
SHCSR volatile.Register32 // 0xD24: System Handler Control and State Register
CFSR volatile.Register32 // 0xD28: Configurable Fault Status Register
HFSR volatile.Register32 // 0xD2C: HardFault Status Register
DFSR volatile.Register32 // 0xD30: Debug Fault Status Register
MMFAR volatile.Register32 // 0xD34: MemManage Fault Address Register
BFAR volatile.Register32 // 0xD38: BusFault Address Register
AFSR volatile.Register32 // 0xD3C: Auxiliary Fault Status Register
PFR [2]volatile.Register32 // 0xD40: Processor Feature Register
DFR volatile.Register32 // 0xD48: Debug Feature Register
ADR volatile.Register32 // 0xD4C: Auxiliary Feature Register
MMFR [4]volatile.Register32 // 0xD50: Memory Model Feature Register
ISAR [5]volatile.Register32 // 0xD60: Instruction Set Attributes Register
_ [5]uint32 // reserved
CPACR volatile.Register32 // 0xD88: Coprocessor Access Control Register
SHCSR volatile.Register32 // 0xD24: System Handler Control and State Register
CFSR volatile.Register32 // 0xD28: Configurable Fault Status Register
HFSR volatile.Register32 // 0xD2C: HardFault Status Register
DFSR volatile.Register32 // 0xD30: Debug Fault Status Register
MMFAR volatile.Register32 // 0xD34: MemManage Fault Address Register
BFAR volatile.Register32 // 0xD38: BusFault Address Register
}
var SCB = (*SCB_Type)(unsafe.Pointer(uintptr(SCB_BASE)))
+1 -12
View File
@@ -433,18 +433,7 @@ func (a ADC) Get() uint16 {
sam.ADC.CTRLA.ClearBits(sam.ADC_CTRLA_ENABLE)
waitADCSync()
// scales to 16-bit result
switch (sam.ADC.CTRLB.Get() & sam.ADC_CTRLB_RESSEL_Msk) >> sam.ADC_CTRLB_RESSEL_Pos {
case sam.ADC_CTRLB_RESSEL_8BIT:
val = val << 8
case sam.ADC_CTRLB_RESSEL_10BIT:
val = val << 6
case sam.ADC_CTRLB_RESSEL_16BIT:
val = val << 4
case sam.ADC_CTRLB_RESSEL_12BIT:
val = val << 4
}
return val
return uint16(val) << 4 // scales from 12 to 16-bit result
}
func (a ADC) getADCChannel() uint8 {
+2 -28
View File
@@ -1,4 +1,4 @@
// +build sam,atsamd51 sam,atsame5x
// +build sam,atsamd51
// Peripheral abstraction layer for the atsamd51.
//
@@ -44,9 +44,6 @@ const (
PinTCCF PinMode = PinTimerAlt
PinTCCG PinMode = PinTCCPDEC
PinInputPulldown PinMode = 18
PinCAN PinMode = 19
PinCAN0 PinMode = PinSDHC
PinCAN1 PinMode = PinCom
)
type PinChange uint8
@@ -630,18 +627,6 @@ func (p Pin) Configure(config PinConfig) {
}
// enable port config
p.setPinCfg(sam.PORT_GROUP_PINCFG_PMUXEN | sam.PORT_GROUP_PINCFG_DRVSTR)
case PinSDHC:
if p&1 > 0 {
// odd pin, so save the even pins
val := p.getPMux() & sam.PORT_GROUP_PMUX_PMUXE_Msk
p.setPMux(val | (uint8(PinSDHC) << sam.PORT_GROUP_PMUX_PMUXO_Pos))
} else {
// even pin, so save the odd pins
val := p.getPMux() & sam.PORT_GROUP_PMUX_PMUXO_Msk
p.setPMux(val | (uint8(PinSDHC) << sam.PORT_GROUP_PMUX_PMUXE_Pos))
}
// enable port config
p.setPinCfg(sam.PORT_GROUP_PINCFG_PMUXEN)
}
}
@@ -863,18 +848,7 @@ func (a ADC) Get() uint16 {
for bus.SYNCBUSY.HasBits(sam.ADC_SYNCBUSY_ENABLE) {
}
// scales to 16-bit result
switch (bus.CTRLB.Get() & sam.ADC_CTRLB_RESSEL_Msk) >> sam.ADC_CTRLB_RESSEL_Pos {
case sam.ADC_CTRLB_RESSEL_8BIT:
val = val << 8
case sam.ADC_CTRLB_RESSEL_10BIT:
val = val << 6
case sam.ADC_CTRLB_RESSEL_16BIT:
val = val << 4
case sam.ADC_CTRLB_RESSEL_12BIT:
val = val << 4
}
return val
return uint16(val) << 4 // scales from 12 to 16-bit result
}
func (a ADC) getADCBus() *sam.ADC_Type {
File diff suppressed because it is too large Load Diff
+4 -5
View File
@@ -204,16 +204,15 @@ func (spi SPI) configurePins(config SPIConfig) {
// There are 2 I2C interfaces on the STM32F103xx.
// Since the first interface is named I2C1, both I2C0 and I2C1 refer to I2C1.
// TODO: implement I2C2.
var (
I2C1 = (*I2C)(unsafe.Pointer(stm32.I2C1))
I2C0 = I2C1
)
type I2C struct {
Bus *stm32.I2C_Type
}
var (
I2C1 = &I2C{Bus: stm32.I2C1}
I2C0 = I2C1
)
func (i2c *I2C) configurePins(config I2CConfig) {
if config.SDA == PB9 {
// use alternate I2C1 pins PB8/PB9 via AFIO mapping
+1 -2
View File
@@ -1,4 +1,4 @@
// +build sam,atsamd51 sam,atsame5x
// +build sam,atsamd51
package runtime
@@ -16,7 +16,6 @@ func postinit() {}
//export Reset_Handler
func main() {
arm.SCB.CPACR.Set(0) // disable FPU if it is enabled
preinit()
run()
abort()
+338
View File
@@ -0,0 +1,338 @@
// +build sam,atsame5x
package runtime
import (
"device/arm"
"device/sam"
"machine"
"runtime/interrupt"
"runtime/volatile"
)
type timeUnit int64
func postinit() {}
//export Reset_Handler
func main() {
preinit()
run()
abort()
}
func init() {
initClocks()
initRTC()
initSERCOMClocks()
initUSBClock()
initADCClock()
// connect to USB CDC interface
machine.UART0.Configure(machine.UARTConfig{})
}
func putchar(c byte) {
machine.UART0.WriteByte(c)
}
func initClocks() {
// set flash wait state
sam.NVMCTRL.CTRLA.SetBits(0 << sam.NVMCTRL_CTRLA_RWS_Pos)
// software reset
sam.GCLK.CTRLA.SetBits(sam.GCLK_CTRLA_SWRST)
for sam.GCLK.SYNCBUSY.HasBits(sam.GCLK_SYNCBUSY_SWRST) {
}
// Set OSCULP32K as source of Generic Clock Generator 3
// GCLK->GENCTRL[GENERIC_CLOCK_GENERATOR_XOSC32K].reg = GCLK_GENCTRL_SRC(GCLK_GENCTRL_SRC_OSCULP32K) | GCLK_GENCTRL_GENEN; //generic clock gen 3
sam.GCLK.GENCTRL[3].Set((sam.GCLK_GENCTRL_SRC_OSCULP32K << sam.GCLK_GENCTRL_SRC_Pos) |
sam.GCLK_GENCTRL_GENEN)
for sam.GCLK.SYNCBUSY.HasBits(sam.GCLK_SYNCBUSY_GENCTRL_GCLK3) {
}
// Set OSCULP32K as source of Generic Clock Generator 0
sam.GCLK.GENCTRL[0].Set((sam.GCLK_GENCTRL_SRC_OSCULP32K << sam.GCLK_GENCTRL_SRC_Pos) |
sam.GCLK_GENCTRL_GENEN)
for sam.GCLK.SYNCBUSY.HasBits(sam.GCLK_SYNCBUSY_GENCTRL_GCLK0) {
}
// Enable DFLL48M clock
sam.OSCCTRL.DFLLCTRLA.Set(0)
sam.OSCCTRL.DFLLMUL.Set((0x1 << sam.OSCCTRL_DFLLMUL_CSTEP_Pos) |
(0x1 << sam.OSCCTRL_DFLLMUL_FSTEP_Pos) |
(0x0 << sam.OSCCTRL_DFLLMUL_MUL_Pos))
for sam.OSCCTRL.DFLLSYNC.HasBits(sam.OSCCTRL_DFLLSYNC_DFLLMUL) {
}
sam.OSCCTRL.DFLLCTRLB.Set(0)
for sam.OSCCTRL.DFLLSYNC.HasBits(sam.OSCCTRL_DFLLSYNC_DFLLCTRLB) {
}
sam.OSCCTRL.DFLLCTRLA.SetBits(sam.OSCCTRL_DFLLCTRLA_ENABLE)
for sam.OSCCTRL.DFLLSYNC.HasBits(sam.OSCCTRL_DFLLSYNC_ENABLE) {
}
sam.OSCCTRL.DFLLVAL.Set(sam.OSCCTRL.DFLLVAL.Get())
for sam.OSCCTRL.DFLLSYNC.HasBits(sam.OSCCTRL_DFLLSYNC_DFLLVAL) {
}
sam.OSCCTRL.DFLLCTRLB.Set(sam.OSCCTRL_DFLLCTRLB_WAITLOCK |
sam.OSCCTRL_DFLLCTRLB_CCDIS |
sam.OSCCTRL_DFLLCTRLB_USBCRM)
for !sam.OSCCTRL.STATUS.HasBits(sam.OSCCTRL_STATUS_DFLLRDY) {
}
// set GCLK7 to run at 2MHz, using DFLL48M as clock source
// GCLK7 = 48MHz / 24 = 2MHz
sam.GCLK.GENCTRL[7].Set((sam.GCLK_GENCTRL_SRC_DFLL << sam.GCLK_GENCTRL_SRC_Pos) |
(24 << sam.GCLK_GENCTRL_DIV_Pos) |
sam.GCLK_GENCTRL_GENEN)
for sam.GCLK.SYNCBUSY.HasBits(sam.GCLK_SYNCBUSY_GENCTRL_GCLK7) {
}
// Set up the PLLs
// Set PLL0 to run at 120MHz, using GCLK7 as clock source
sam.GCLK.PCHCTRL[1].Set(sam.GCLK_PCHCTRL_CHEN |
(sam.GCLK_PCHCTRL_GEN_GCLK7 << sam.GCLK_PCHCTRL_GEN_Pos))
// multiplier = 59 + 1 + (0/32) = 60
// PLL0 = 2MHz * 60 = 120MHz
sam.OSCCTRL.DPLL[0].DPLLRATIO.Set((0x0 << sam.OSCCTRL_DPLL_DPLLRATIO_LDRFRAC_Pos) |
(59 << sam.OSCCTRL_DPLL_DPLLRATIO_LDR_Pos))
for sam.OSCCTRL.DPLL[0].DPLLSYNCBUSY.HasBits(sam.OSCCTRL_DPLL_DPLLSYNCBUSY_DPLLRATIO) {
}
// MUST USE LBYPASS DUE TO BUG IN REV A OF SAMD51, via Adafruit lib.
sam.OSCCTRL.DPLL[0].DPLLCTRLB.Set((sam.OSCCTRL_DPLL_DPLLCTRLB_REFCLK_GCLK << sam.OSCCTRL_DPLL_DPLLCTRLB_REFCLK_Pos) |
sam.OSCCTRL_DPLL_DPLLCTRLB_LBYPASS)
sam.OSCCTRL.DPLL[0].DPLLCTRLA.Set(sam.OSCCTRL_DPLL_DPLLCTRLA_ENABLE)
for !sam.OSCCTRL.DPLL[0].DPLLSTATUS.HasBits(sam.OSCCTRL_DPLL_DPLLSTATUS_CLKRDY) ||
!sam.OSCCTRL.DPLL[0].DPLLSTATUS.HasBits(sam.OSCCTRL_DPLL_DPLLSTATUS_LOCK) {
}
// Set PLL1 to run at 100MHz, using GCLK7 as clock source
sam.GCLK.PCHCTRL[2].Set(sam.GCLK_PCHCTRL_CHEN |
(sam.GCLK_PCHCTRL_GEN_GCLK7 << sam.GCLK_PCHCTRL_GEN_Pos))
// multiplier = 49 + 1 + (0/32) = 50
// PLL1 = 2MHz * 50 = 100MHz
sam.OSCCTRL.DPLL[1].DPLLRATIO.Set((0x0 << sam.OSCCTRL_DPLL_DPLLRATIO_LDRFRAC_Pos) |
(49 << sam.OSCCTRL_DPLL_DPLLRATIO_LDR_Pos))
for sam.OSCCTRL.DPLL[1].DPLLSYNCBUSY.HasBits(sam.OSCCTRL_DPLL_DPLLSYNCBUSY_DPLLRATIO) {
}
// // MUST USE LBYPASS DUE TO BUG IN REV A OF SAMD51
sam.OSCCTRL.DPLL[1].DPLLCTRLB.Set((sam.OSCCTRL_DPLL_DPLLCTRLB_REFCLK_GCLK << sam.OSCCTRL_DPLL_DPLLCTRLB_REFCLK_Pos) |
sam.OSCCTRL_DPLL_DPLLCTRLB_LBYPASS)
sam.OSCCTRL.DPLL[1].DPLLCTRLA.Set(sam.OSCCTRL_DPLL_DPLLCTRLA_ENABLE)
// for !sam.OSCCTRL.DPLLSTATUS1.HasBits(sam.OSCCTRL_DPLLSTATUS_CLKRDY) ||
// !sam.OSCCTRL.DPLLSTATUS1.HasBits(sam.OSCCTRL_DPLLSTATUS_LOCK) {
// }
// Set up the peripheral clocks
// Set 48MHZ CLOCK FOR USB
sam.GCLK.GENCTRL[1].Set((sam.GCLK_GENCTRL_SRC_DFLL << sam.GCLK_GENCTRL_SRC_Pos) |
sam.GCLK_GENCTRL_IDC |
sam.GCLK_GENCTRL_GENEN)
for sam.GCLK.SYNCBUSY.HasBits(sam.GCLK_SYNCBUSY_GENCTRL_GCLK1) {
}
// // Set 100MHZ CLOCK FOR OTHER PERIPHERALS
// sam.GCLK.GENCTRL2.Set((sam.GCLK_GENCTRL_SRC_DPLL1 << sam.GCLK_GENCTRL_SRC_Pos) |
// sam.GCLK_GENCTRL_IDC |
// sam.GCLK_GENCTRL_GENEN)
// for sam.GCLK.SYNCBUSY.HasBits(sam.GCLK_SYNCBUSY_GENCTRL2) {
// }
// // Set 12MHZ CLOCK FOR DAC
sam.GCLK.GENCTRL[4].Set((sam.GCLK_GENCTRL_SRC_DFLL << sam.GCLK_GENCTRL_SRC_Pos) |
sam.GCLK_GENCTRL_IDC |
(4 << sam.GCLK_GENCTRL_DIVSEL_Pos) |
sam.GCLK_GENCTRL_GENEN)
for sam.GCLK.SYNCBUSY.HasBits(sam.GCLK_SYNCBUSY_GENCTRL_GCLK4) {
}
// // Set up main clock
sam.GCLK.GENCTRL[0].Set((sam.GCLK_GENCTRL_SRC_DPLL0 << sam.GCLK_GENCTRL_SRC_Pos) |
sam.GCLK_GENCTRL_IDC |
sam.GCLK_GENCTRL_GENEN)
for sam.GCLK.SYNCBUSY.HasBits(sam.GCLK_SYNCBUSY_GENCTRL_GCLK0) {
}
sam.MCLK.CPUDIV.Set(sam.MCLK_CPUDIV_DIV_DIV1)
// Use the LDO regulator by default
sam.SUPC.VREG.ClearBits(sam.SUPC_VREG_SEL)
// Start up the "Debug Watchpoint and Trace" unit, so that we can use
// it's 32bit cycle counter for timing.
//CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
//DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;
}
func initRTC() {
// turn on digital interface clock
sam.MCLK.APBAMASK.SetBits(sam.MCLK_APBAMASK_RTC_)
// disable RTC
sam.RTC_MODE0.CTRLA.ClearBits(sam.RTC_MODE0_CTRLA_ENABLE)
//sam.RTC_MODE0.CTRLA.Set(0)
for sam.RTC_MODE0.SYNCBUSY.HasBits(sam.RTC_MODE0_SYNCBUSY_ENABLE) {
}
// reset RTC
sam.RTC_MODE0.CTRLA.SetBits(sam.RTC_MODE0_CTRLA_SWRST)
for sam.RTC_MODE0.SYNCBUSY.HasBits(sam.RTC_MODE0_SYNCBUSY_SWRST) {
}
// set to use ulp 32k oscillator
sam.OSC32KCTRL.OSCULP32K.SetBits(sam.OSC32KCTRL_OSCULP32K_EN32K)
sam.OSC32KCTRL.RTCCTRL.Set(sam.OSC32KCTRL_RTCCTRL_RTCSEL_ULP32K)
// set Mode0 to 32-bit counter (mode 0) with prescaler 1 and GCLK2 is 32KHz/1
sam.RTC_MODE0.CTRLA.Set((sam.RTC_MODE0_CTRLA_MODE_COUNT32 << sam.RTC_MODE0_CTRLA_MODE_Pos) |
(sam.RTC_MODE0_CTRLA_PRESCALER_DIV1 << sam.RTC_MODE0_CTRLA_PRESCALER_Pos) |
(sam.RTC_MODE0_CTRLA_COUNTSYNC))
// re-enable RTC
sam.RTC_MODE0.CTRLA.SetBits(sam.RTC_MODE0_CTRLA_ENABLE)
for sam.RTC_MODE0.SYNCBUSY.HasBits(sam.RTC_MODE0_SYNCBUSY_ENABLE) {
}
irq := interrupt.New(sam.IRQ_RTC, func(interrupt.Interrupt) {
// disable IRQ for CMP0 compare
sam.RTC_MODE0.INTFLAG.SetBits(sam.RTC_MODE0_INTENSET_CMP0)
timerWakeup.Set(1)
})
irq.SetPriority(0xc0)
irq.Enable()
}
func waitForSync() {
for sam.RTC_MODE0.SYNCBUSY.HasBits(sam.RTC_MODE0_SYNCBUSY_COUNT) {
}
}
var (
timestamp timeUnit // ticks since boottime
timerLastCounter uint64
)
var timerWakeup volatile.Register8
const asyncScheduler = false
// ticksToNanoseconds converts RTC ticks (at 32768Hz) to nanoseconds.
func ticksToNanoseconds(ticks timeUnit) int64 {
// The following calculation is actually the following, but with both sides
// reduced to reduce the risk of overflow:
// ticks * 1e9 / 32768
return int64(ticks) * 1953125 / 64
}
// nanosecondsToTicks converts nanoseconds to RTC ticks (running at 32768Hz).
func nanosecondsToTicks(ns int64) timeUnit {
// The following calculation is actually the following, but with both sides
// reduced to reduce the risk of overflow:
// ns * 32768 / 1e9
return timeUnit(ns * 64 / 1953125)
}
// sleepTicks should sleep for d number of microseconds.
func sleepTicks(d timeUnit) {
for d != 0 {
ticks() // update timestamp
ticks := uint32(d)
if !timerSleep(ticks) {
return
}
d -= timeUnit(ticks)
}
}
// ticks returns number of microseconds since start.
func ticks() timeUnit {
waitForSync()
rtcCounter := uint64(sam.RTC_MODE0.COUNT.Get())
offset := (rtcCounter - timerLastCounter) // change since last measurement
timerLastCounter = rtcCounter
timestamp += timeUnit(offset)
return timestamp
}
// ticks are in microseconds
// Returns true if the timer completed.
// Returns false if another interrupt occured which requires an early return to scheduler.
func timerSleep(ticks uint32) bool {
timerWakeup.Set(0)
if ticks < 8 {
// due to delay waiting for the register value to sync, the minimum sleep value
// for the SAMD51 is 260us.
// For related info for SAMD21, see:
// https://community.atmel.com/comment/2507091#comment-2507091
ticks = 8
}
// request read of count
waitForSync()
// set compare value
cnt := sam.RTC_MODE0.COUNT.Get()
sam.RTC_MODE0.COMP[0].Set(uint32(cnt) + ticks)
// enable IRQ for CMP0 compare
sam.RTC_MODE0.INTENSET.SetBits(sam.RTC_MODE0_INTENSET_CMP0)
wait:
waitForEvents()
if timerWakeup.Get() != 0 {
return true
}
if hasScheduler {
// The interurpt may have awoken a goroutine, so bail out early.
// Disable IRQ for CMP0 compare.
sam.RTC_MODE0.INTENCLR.SetBits(sam.RTC_MODE0_INTENSET_CMP0)
return false
} else {
// This is running without a scheduler.
// The application expects this to sleep the whole time.
goto wait
}
}
func initUSBClock() {
// Turn on clock(s) for USB
//MCLK->APBBMASK.reg |= MCLK_APBBMASK_USB;
//MCLK->AHBMASK.reg |= MCLK_AHBMASK_USB;
sam.MCLK.APBBMASK.SetBits(sam.MCLK_APBBMASK_USB_)
sam.MCLK.AHBMASK.SetBits(sam.MCLK_AHBMASK_USB_)
// Put Generic Clock Generator 1 as source for USB
//GCLK->PCHCTRL[USB_GCLK_ID].reg = GCLK_PCHCTRL_GEN_GCLK1_Val | (1 << GCLK_PCHCTRL_CHEN_Pos);
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_USB].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
sam.GCLK_PCHCTRL_CHEN)
}
func initADCClock() {
// Turn on clocks for ADC0/ADC1.
sam.MCLK.APBDMASK.SetBits(sam.MCLK_APBDMASK_ADC0_)
sam.MCLK.APBDMASK.SetBits(sam.MCLK_APBDMASK_ADC1_)
// Put Generic Clock Generator 1 as source for ADC0 and ADC1.
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_ADC0].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
sam.GCLK_PCHCTRL_CHEN)
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_ADC1].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
sam.GCLK_PCHCTRL_CHEN)
}
func waitForEvents() {
arm.Asm("wfe")
}
+5
View File
@@ -102,6 +102,11 @@ func initSystem() {
func initPeripherals() {
// enable FPU - set CP10, CP11 full access
nxp.SystemControl.CPACR.SetBits(
((nxp.SCB_CPACR_CP10_CP10_3 << nxp.SCB_CPACR_CP10_Pos) & nxp.SCB_CPACR_CP10_Msk) |
((nxp.SCB_CPACR_CP11_CP11_3 << nxp.SCB_CPACR_CP11_Pos) & nxp.SCB_CPACR_CP11_Msk))
enableTimerClocks() // activate GPT/PIT clock gates
initSysTick() // enable SysTick
initRTC() // enable real-time clock
-4
View File
@@ -3,7 +3,6 @@
package runtime
import (
"device/arm"
"device/nrf"
"machine"
"runtime/interrupt"
@@ -19,9 +18,6 @@ func postinit() {}
//export Reset_Handler
func main() {
if nrf.FPUPresent {
arm.SCB.CPACR.Set(0) // disable FPU if it is enabled
}
systemInit()
preinit()
run()
+2 -2
View File
@@ -24,10 +24,10 @@ func waitForEvents() {
if enabled != 0 {
// Now pick the appropriate SVCall number. Hopefully they won't change
// in the future with a different SoftDevice version.
if nrf.Device == "nrf51" {
if nrf.DEVICE == "nrf51" {
// sd_app_evt_wait: SOC_SVC_BASE_NOT_AVAILABLE + 29
arm.SVCall0(0x2B + 29)
} else if nrf.Device == "nrf52" || nrf.Device == "nrf52840" || nrf.Device == "nrf52833" {
} else if nrf.DEVICE == "nrf52" || nrf.DEVICE == "nrf52840" || nrf.DEVICE == "nrf52833" {
// sd_app_evt_wait: SOC_SVC_BASE_NOT_AVAILABLE + 21
arm.SVCall0(0x2C + 21)
} else {
+1
View File
@@ -75,6 +75,7 @@ func initSystem() {
nxp.SIM.SCGC3.Set(nxp.SIM_SCGC3_ADC1 | nxp.SIM_SCGC3_FTM2 | nxp.SIM_SCGC3_FTM3)
nxp.SIM.SCGC5.Set(0x00043F82) // clocks active to all GPIO
nxp.SIM.SCGC6.Set(nxp.SIM_SCGC6_RTC | nxp.SIM_SCGC6_FTM0 | nxp.SIM_SCGC6_FTM1 | nxp.SIM_SCGC6_ADC0 | nxp.SIM_SCGC6_FTF)
nxp.SystemControl.CPACR.Set(0x00F00000)
nxp.LMEM.PCCCR.Set(0x85000003)
// release I/O pins hold, if we woke up from VLLS mode
-4
View File
@@ -1,4 +0,0 @@
{
"inherits": ["arduino-nano"],
"flash-command": "avrdude -c arduino -p atmega328p -b 115200 -P {port} -U flash:w:{hex}:i"
}
+13 -35
View File
@@ -19,15 +19,10 @@ var validName = regexp.MustCompile("^[a-zA-Z0-9_]+$")
var enumBitSpecifier = regexp.MustCompile("^#[x01]+$")
type SVDFile struct {
XMLName xml.Name `xml:"device"`
Name string `xml:"name"`
Description string `xml:"description"`
LicenseText string `xml:"licenseText"`
CPU *struct {
Name string `xml:"name"`
FPUPresent bool `xml:"fpuPresent"`
NVICPrioBits int `xml:"nvicPrioBits"`
} `xml:"cpu"`
XMLName xml.Name `xml:"device"`
Name string `xml:"name"`
Description string `xml:"description"`
LicenseText string `xml:"licenseText"`
Peripherals []SVDPeripheral `xml:"peripherals>peripheral"`
}
@@ -100,11 +95,6 @@ type Metadata struct {
NameLower string
Description string
LicenseBlock string
HasCPUInfo bool // set if the following fields are populated
CPUName string
FPUPresent bool
NVICPrioBits int
}
type Interrupt struct {
@@ -428,22 +418,15 @@ func readSVD(path, sourceURL string) (*Device, error) {
licenseBlock = regexp.MustCompile(`\s+\n`).ReplaceAllString(licenseBlock, "\n")
}
metadata := &Metadata{
File: filepath.Base(path),
DescriptorSource: sourceURL,
Name: device.Name,
NameLower: strings.ToLower(device.Name),
Description: strings.TrimSpace(device.Description),
LicenseBlock: licenseBlock,
}
if device.CPU != nil {
metadata.HasCPUInfo = true
metadata.CPUName = device.CPU.Name
metadata.FPUPresent = device.CPU.FPUPresent
metadata.NVICPrioBits = device.CPU.NVICPrioBits
}
return &Device{
Metadata: metadata,
Metadata: &Metadata{
File: filepath.Base(path),
DescriptorSource: sourceURL,
Name: device.Name,
NameLower: strings.ToLower(device.Name),
Description: strings.TrimSpace(device.Description),
LicenseBlock: licenseBlock,
},
Interrupts: interruptList,
Peripherals: peripheralsList,
}, nil
@@ -850,12 +833,7 @@ import (
// Some information about this device.
const (
Device = "{{.device.Metadata.Name}}"
{{- if .device.Metadata.HasCPUInfo }}
CPU = "{{.device.Metadata.CPUName}}"
FPUPresent = {{.device.Metadata.FPUPresent}}
NVICPrioBits = {{.device.Metadata.NVICPrioBits}}
{{- end }}
DEVICE = "{{.device.Metadata.Name}}"
)
// Interrupt numbers.
+50 -1
View File
@@ -2,6 +2,7 @@ package transform_test
import (
"go/token"
"go/types"
"io/ioutil"
"path/filepath"
"regexp"
@@ -10,6 +11,9 @@ import (
"strings"
"testing"
"github.com/tinygo-org/tinygo/compileopts"
"github.com/tinygo-org/tinygo/compiler"
"github.com/tinygo-org/tinygo/loader"
"github.com/tinygo-org/tinygo/transform"
"tinygo.org/x/go-llvm"
)
@@ -35,7 +39,52 @@ func (out allocsTestOutput) String() string {
func TestAllocs2(t *testing.T) {
t.Parallel()
mod := compileGoFileForTesting(t, "./testdata/allocs2.go")
target, err := compileopts.LoadTarget("i686--linux")
if err != nil {
t.Fatal("failed to load target:", err)
}
config := &compileopts.Config{
Options: &compileopts.Options{},
Target: target,
}
compilerConfig := &compiler.Config{
Triple: config.Triple(),
GOOS: config.GOOS(),
GOARCH: config.GOARCH(),
CodeModel: config.CodeModel(),
RelocationModel: config.RelocationModel(),
Scheduler: config.Scheduler(),
FuncImplementation: config.FuncImplementation(),
AutomaticStackSize: config.AutomaticStackSize(),
Debug: true,
}
machine, err := compiler.NewTargetMachine(compilerConfig)
if err != nil {
t.Fatal("failed to create target machine:", err)
}
// Load entire program AST into memory.
lprogram, err := loader.Load(config, []string{"./testdata/allocs2.go"}, config.ClangHeaders, types.Config{
Sizes: compiler.Sizes(machine),
})
if err != nil {
t.Fatal("failed to create target machine:", err)
}
err = lprogram.Parse()
if err != nil {
t.Fatal("could not parse", err)
}
// Compile AST to IR.
program := lprogram.LoadSSA()
pkg := lprogram.MainPkg()
mod, errs := compiler.CompilePackage("allocs2.go", pkg, program.Package(pkg.Pkg), machine, compilerConfig, false)
if errs != nil {
for _, err := range errs {
t.Error(err)
}
return
}
// Run functionattrs pass, which is necessary for escape analysis.
pm := llvm.NewPassManager()
+190 -74
View File
@@ -74,10 +74,12 @@ type methodInfo struct {
// typeInfo describes a single concrete Go type, which can be a basic or a named
// type. If it is a named type, it may have methods.
type typeInfo struct {
name string
typecode llvm.Value
methodSet llvm.Value
methods []*methodInfo
name string
typecode llvm.Value
methodSet llvm.Value
num uint64 // the type number after lowering
countTypeAsserts int // how often a type assert happens on this method
methods []*methodInfo
}
// getMethod looks up the method on this type with the given signature and
@@ -92,13 +94,27 @@ func (t *typeInfo) getMethod(signature *signatureInfo) *methodInfo {
panic("could not find method")
}
// typeInfoSlice implements sort.Slice, sorting the most commonly used types
// first.
type typeInfoSlice []*typeInfo
func (t typeInfoSlice) Len() int { return len(t) }
func (t typeInfoSlice) Less(i, j int) bool {
// Try to sort the most commonly used types first.
if t[i].countTypeAsserts != t[j].countTypeAsserts {
return t[i].countTypeAsserts < t[j].countTypeAsserts
}
return t[i].name < t[j].name
}
func (t typeInfoSlice) Swap(i, j int) { t[i], t[j] = t[j], t[i] }
// interfaceInfo keeps information about a Go interface type, including all
// methods it has.
type interfaceInfo struct {
name string // name with $interface suffix
methodSet llvm.Value // global which this interfaceInfo describes
signatures []*signatureInfo // method set
types []*typeInfo // types this interface implements
types typeInfoSlice // types this interface implements
assertFunc llvm.Value // runtime.interfaceImplements replacement
methodFuncs map[*signatureInfo]llvm.Value // runtime.interfaceMethod replacements for each signature
}
@@ -147,6 +163,7 @@ func LowerInterfaces(mod llvm.Module, sizeLevel int) error {
// run runs the pass itself.
func (p *lowerInterfacesPass) run() error {
// Collect all type codes.
var typecodeIDs []llvm.Value
for global := p.mod.FirstGlobal(); !global.IsNil(); global = llvm.NextGlobal(global) {
if strings.HasPrefix(global.Name(), "reflect/types.type:") {
// Retrieve Go type information based on an opaque global variable.
@@ -154,6 +171,7 @@ func (p *lowerInterfacesPass) run() error {
// discarded afterwards.
name := strings.TrimPrefix(global.Name(), "reflect/types.type:")
if _, ok := p.types[name]; !ok {
typecodeIDs = append(typecodeIDs, global)
t := &typeInfo{
name: name,
typecode: global,
@@ -169,6 +187,18 @@ func (p *lowerInterfacesPass) run() error {
}
}
// Count per type how often it is type asserted on (e.g. in a switch
// statement).
typeAssert := p.mod.NamedFunction("runtime.typeAssert")
typeAssertUses := getUses(typeAssert)
for _, use := range typeAssertUses {
typecode := use.Operand(1)
name := strings.TrimPrefix(typecode.Name(), "reflect/types.typeid:")
if t, ok := p.types[name]; ok {
t.countTypeAsserts++
}
}
// Find all interface method calls.
interfaceMethod := p.mod.NamedFunction("runtime.interfaceMethod")
interfaceMethodUses := getUses(interfaceMethod)
@@ -244,11 +274,10 @@ func (p *lowerInterfacesPass) run() error {
}
}
// Sort all types added to the interfaces.
// Sort all types added to the interfaces, to check for more common types
// first.
for _, itf := range p.interfaces {
sort.Slice(itf.types, func(i, j int) bool {
return itf.types[i].name > itf.types[j].name
})
sort.Sort(itf.types)
}
// Replace all interface methods with their uses, if possible.
@@ -310,10 +339,43 @@ func (p *lowerInterfacesPass) run() error {
use.EraseFromParentAsInstruction()
}
// Make a slice of types sorted by frequency of use.
typeSlice := make(typeInfoSlice, 0, len(p.types))
for _, t := range p.types {
typeSlice = append(typeSlice, t)
}
sort.Sort(sort.Reverse(typeSlice))
// Assign a type code for each type.
assignTypeCodes(p.mod, typeSlice)
// Replace each use of a ptrtoint runtime.typecodeID with the constant type
// code.
for _, global := range typecodeIDs {
for _, use := range getUses(global) {
if use.IsAConstantExpr().IsNil() {
continue
}
t := p.types[strings.TrimPrefix(global.Name(), "reflect/types.type:")]
typecode := llvm.ConstInt(p.uintptrType, t.num, false)
switch use.Opcode() {
case llvm.PtrToInt:
// Already of the correct type.
case llvm.BitCast:
// Could happen when stored in an interface (which is of type
// i8*).
typecode = llvm.ConstIntToPtr(typecode, use.Type())
default:
panic("unexpected constant expression")
}
use.ReplaceAllUsesWith(typecode)
}
}
// Replace each type assert with an actual type comparison or (if the type
// assert is impossible) the constant false.
llvmFalse := llvm.ConstInt(p.ctx.Int1Type(), 0, false)
for _, use := range getUses(p.mod.NamedFunction("runtime.typeAssert")) {
for _, use := range typeAssertUses {
actualType := use.Operand(0)
name := strings.TrimPrefix(use.Operand(1).Name(), "reflect/types.typeid:")
if t, ok := p.types[name]; ok {
@@ -333,13 +395,54 @@ func (p *lowerInterfacesPass) run() error {
use.EraseFromParentAsInstruction()
}
// Remove all method sets, which are now unnecessary and inhibit later
// optimizations if they are left in place.
for _, t := range p.types {
initializer := t.typecode.Initializer()
methodSet := llvm.ConstExtractValue(initializer, []uint32{2})
initializer = llvm.ConstInsertValue(initializer, llvm.ConstNull(methodSet.Type()), []uint32{2})
t.typecode.SetInitializer(initializer)
// Fill in each helper function for type asserts on interfaces
// (interface-to-interface matches).
for _, itf := range p.interfaces {
if !itf.assertFunc.IsNil() {
p.createInterfaceImplementsFunc(itf)
}
for signature := range itf.methodFuncs {
p.createInterfaceMethodFunc(itf, signature)
}
}
// Replace all ptrtoint typecode placeholders with their final type code
// numbers.
for _, typ := range p.types {
for _, use := range getUses(typ.typecode) {
if !use.IsAConstantExpr().IsNil() && use.Opcode() == llvm.PtrToInt {
use.ReplaceAllUsesWith(llvm.ConstInt(p.uintptrType, typ.num, false))
}
}
}
// Remove most objects created for interface and reflect lowering.
// Unnecessary, but cleans up the IR for inspection and testing.
for _, typ := range p.types {
// Only some typecodes have an initializer.
initializer := typ.typecode.Initializer()
if !initializer.IsNil() {
references := llvm.ConstExtractValue(initializer, []uint32{0})
typ.typecode.SetInitializer(llvm.ConstNull(initializer.Type()))
if strings.HasPrefix(typ.name, "reflect/types.type:struct:") {
// Structs have a 'references' field that is not a typecode but
// a pointer to a runtime.structField array and therefore a
// bitcast. This global should be erased separately, otherwise
// typecode objects cannot be erased.
structFields := references.Operand(0)
structFields.EraseFromParentAsGlobal()
}
}
if !typ.methodSet.IsNil() {
typ.methodSet.EraseFromParentAsGlobal()
typ.methodSet = llvm.Value{}
}
}
for _, itf := range p.interfaces {
// Remove method sets of interfaces.
itf.methodSet.EraseFromParentAsGlobal()
itf.methodSet = llvm.Value{}
}
return nil
@@ -456,10 +559,6 @@ func (p *lowerInterfacesPass) replaceInvokeWithCall(use llvm.Value, typ *typeInf
// getInterfaceImplementsFunc returns a function that checks whether a given
// interface type implements a given interface, by checking all possible types
// that implement this interface.
//
// The type match is implemented using an if/else chain over all possible types.
// This if/else chain is easily converted to a big switch over all possible
// types by the LLVM simplifycfg pass.
func (p *lowerInterfacesPass) getInterfaceImplementsFunc(itf *interfaceInfo) llvm.Value {
if !itf.assertFunc.IsNil() {
return itf.assertFunc
@@ -469,49 +568,60 @@ func (p *lowerInterfacesPass) getInterfaceImplementsFunc(itf *interfaceInfo) llv
// TODO: debug info
fnName := itf.id() + "$typeassert"
fnType := llvm.FunctionType(p.ctx.Int1Type(), []llvm.Type{p.uintptrType}, false)
fn := llvm.AddFunction(p.mod, fnName, fnType)
itf.assertFunc = fn
fn.Param(0).SetName("actualType")
itf.assertFunc = llvm.AddFunction(p.mod, fnName, fnType)
itf.assertFunc.Param(0).SetName("actualType")
// Type asserts will be made for each type, so increment the counter for
// those.
for _, typ := range itf.types {
typ.countTypeAsserts++
}
return itf.assertFunc
}
// createInterfaceImplementsFunc finishes the work of
// getInterfaceImplementsFunc, because it needs to run after types have a type
// code assigned.
//
// The type match is implemented using a big type switch over all possible
// types.
func (p *lowerInterfacesPass) createInterfaceImplementsFunc(itf *interfaceInfo) {
fn := itf.assertFunc
fn.SetLinkage(llvm.InternalLinkage)
fn.SetUnnamedAddr(true)
if p.sizeLevel >= 2 {
fn.AddFunctionAttr(p.ctx.CreateEnumAttribute(llvm.AttributeKindID("optsize"), 0))
}
// Start the if/else chain at the entry block.
// TODO: debug info
// Create all used basic blocks.
entry := p.ctx.AddBasicBlock(fn, "entry")
thenBlock := p.ctx.AddBasicBlock(fn, "then")
elseBlock := p.ctx.AddBasicBlock(fn, "else")
// Add all possible types as cases.
p.builder.SetInsertPointAtEnd(entry)
// Iterate over all possible types. Each iteration creates a new branch
// either to the 'then' block (success) or the .next block, for the next
// check.
actualType := fn.Param(0)
sw := p.builder.CreateSwitch(actualType, elseBlock, len(itf.types))
for _, typ := range itf.types {
nextBlock := p.ctx.AddBasicBlock(fn, typ.name+".next")
cmp := p.builder.CreateICmp(llvm.IntEQ, actualType, llvm.ConstPtrToInt(typ.typecode, p.uintptrType), typ.name+".icmp")
p.builder.CreateCondBr(cmp, thenBlock, nextBlock)
p.builder.SetInsertPointAtEnd(nextBlock)
sw.AddCase(llvm.ConstInt(p.uintptrType, typ.num, false), thenBlock)
}
// The builder is now inserting at the last *.next block. Once we reach
// this point, all types have been checked so the type assert will have
// failed.
p.builder.CreateRet(llvm.ConstInt(p.ctx.Int1Type(), 0, false))
// Fill 'then' block (type assert was successful).
p.builder.SetInsertPointAtEnd(thenBlock)
p.builder.CreateRet(llvm.ConstInt(p.ctx.Int1Type(), 1, false))
return itf.assertFunc
// Fill 'else' block (type asserted failed).
p.builder.SetInsertPointAtEnd(elseBlock)
p.builder.CreateRet(llvm.ConstInt(p.ctx.Int1Type(), 0, false))
}
// getInterfaceMethodFunc returns a thunk for calling a method on an interface.
//
// Matching the actual type is implemented using an if/else chain over all
// possible types. This is later converted to a switch statement by the LLVM
// simplifycfg pass.
func (p *lowerInterfacesPass) getInterfaceMethodFunc(itf *interfaceInfo, signature *signatureInfo, returnType llvm.Type, paramTypes []llvm.Type) llvm.Value {
// It only declares the function, createInterfaceMethodFunc actually defines the
// function.
func (p *lowerInterfacesPass) getInterfaceMethodFunc(itf *interfaceInfo, signature *signatureInfo, returnType llvm.Type, params []llvm.Type) llvm.Value {
if fn, ok := itf.methodFuncs[signature]; ok {
// This function has already been created.
return fn
@@ -524,11 +634,22 @@ func (p *lowerInterfacesPass) getInterfaceMethodFunc(itf *interfaceInfo, signatu
// Construct the function name, which is of the form:
// (main.Stringer).String
fnName := "(" + itf.id() + ")." + signature.methodName()
fnType := llvm.FunctionType(returnType, append(paramTypes, llvm.PointerType(p.ctx.Int8Type(), 0)), false)
fnType := llvm.FunctionType(returnType, append(params, llvm.PointerType(p.ctx.Int8Type(), 0)), false)
fn := llvm.AddFunction(p.mod, fnName, fnType)
llvm.PrevParam(fn.LastParam()).SetName("actualType")
fn.LastParam().SetName("parentHandle")
itf.methodFuncs[signature] = fn
return fn
}
// createInterfaceMethodFunc finishes the work of getInterfaceMethodFunc,
// because it needs to run after type codes have been assigned to concrete
// types.
//
// Matching the actual type is implemented using a big type switch over all
// possible types.
func (p *lowerInterfacesPass) createInterfaceMethodFunc(itf *interfaceInfo, signature *signatureInfo) {
fn := itf.methodFuncs[signature]
fn.SetLinkage(llvm.InternalLinkage)
fn.SetUnnamedAddr(true)
if p.sizeLevel >= 2 {
@@ -537,6 +658,29 @@ func (p *lowerInterfacesPass) getInterfaceMethodFunc(itf *interfaceInfo, signatu
// TODO: debug info
// Create entry block.
entry := p.ctx.AddBasicBlock(fn, "entry")
// Create default block and call runtime.nilPanic.
// The only other possible value remaining is nil for nil interfaces. We
// could panic with a different message here such as "nil interface" but
// that would increase code size and "nil panic" is close enough. Most
// importantly, it avoids undefined behavior when accidentally calling a
// method on a nil interface.
defaultBlock := p.ctx.AddBasicBlock(fn, "default")
p.builder.SetInsertPointAtEnd(defaultBlock)
nilPanic := p.mod.NamedFunction("runtime.nilPanic")
p.builder.CreateCall(nilPanic, []llvm.Value{
llvm.Undef(llvm.PointerType(p.ctx.Int8Type(), 0)),
llvm.Undef(llvm.PointerType(p.ctx.Int8Type(), 0)),
}, "")
p.builder.CreateUnreachable()
// Create type switch in entry block.
p.builder.SetInsertPointAtEnd(entry)
actualType := llvm.PrevParam(fn.LastParam())
sw := p.builder.CreateSwitch(actualType, defaultBlock, len(itf.types))
// Collect the params that will be passed to the functions to call.
// These params exclude the receiver (which may actually consist of multiple
// parts).
@@ -545,18 +689,10 @@ func (p *lowerInterfacesPass) getInterfaceMethodFunc(itf *interfaceInfo, signatu
params[i] = fn.Param(i + 1)
}
// Start chain in the entry block.
entry := p.ctx.AddBasicBlock(fn, "entry")
p.builder.SetInsertPointAtEnd(entry)
// Define all possible functions that can be called.
actualType := llvm.PrevParam(fn.LastParam())
for _, typ := range itf.types {
// Create type check (if/else).
bb := p.ctx.AddBasicBlock(fn, typ.name)
next := p.ctx.AddBasicBlock(fn, typ.name+".next")
cmp := p.builder.CreateICmp(llvm.IntEQ, actualType, llvm.ConstPtrToInt(typ.typecode, p.uintptrType), typ.name+".icmp")
p.builder.CreateCondBr(cmp, bb, next)
sw.AddCase(llvm.ConstInt(p.uintptrType, typ.num, false), bb)
// The function we will redirect to when the interface has this type.
function := typ.getMethod(signature).function
@@ -589,25 +725,5 @@ func (p *lowerInterfacesPass) getInterfaceMethodFunc(itf *interfaceInfo, signatu
} else {
p.builder.CreateRet(retval)
}
// Start next comparison in the 'next' block (which is jumped to when
// the type doesn't match).
p.builder.SetInsertPointAtEnd(next)
}
// The builder now points to the last *.then block, after all types have
// been checked. Call runtime.nilPanic here.
// The only other possible value remaining is nil for nil interfaces. We
// could panic with a different message here such as "nil interface" but
// that would increase code size and "nil panic" is close enough. Most
// importantly, it avoids undefined behavior when accidentally calling a
// method on a nil interface.
nilPanic := p.mod.NamedFunction("runtime.nilPanic")
p.builder.CreateCall(nilPanic, []llvm.Value{
llvm.Undef(llvm.PointerType(p.ctx.Int8Type(), 0)),
llvm.Undef(llvm.PointerType(p.ctx.Int8Type(), 0)),
}, "")
p.builder.CreateUnreachable()
return fn
}
-5
View File
@@ -14,10 +14,5 @@ func TestInterfaceLowering(t *testing.T) {
if err != nil {
t.Error(err)
}
pm := llvm.NewPassManager()
defer pm.Dispose()
pm.AddGlobalDCEPass()
pm.Run(mod)
})
}
+1 -3
View File
@@ -63,7 +63,7 @@ func Optimize(mod llvm.Module, config *compileopts.Config, optLevel, sizeLevel i
goPasses.AddFunctionAttrsPass()
goPasses.Run(mod)
// Run TinyGo-specific optimization passes.
// Run Go-specific optimization passes.
OptimizeMaps(mod)
OptimizeStringToBytes(mod)
OptimizeReflectImplements(mod)
@@ -88,7 +88,6 @@ func Optimize(mod llvm.Module, config *compileopts.Config, optLevel, sizeLevel i
goPasses.Run(mod)
// Run TinyGo-specific interprocedural optimizations.
LowerReflect(mod)
OptimizeAllocs(mod, config.Options.PrintAllocs, func(pos token.Position, msg string) {
fmt.Fprintln(os.Stderr, pos.String()+": "+msg)
})
@@ -101,7 +100,6 @@ func Optimize(mod llvm.Module, config *compileopts.Config, optLevel, sizeLevel i
if err != nil {
return []error{err}
}
LowerReflect(mod)
if config.FuncImplementation() == "switch" {
LowerFuncValues(mod)
}
+4 -71
View File
@@ -31,7 +31,6 @@ import (
"encoding/binary"
"go/ast"
"math/big"
"sort"
"strings"
"tinygo.org/x/go-llvm"
@@ -123,45 +122,14 @@ type typeCodeAssignmentState struct {
needsNamedNonBasicTypesSidetable bool
}
// LowerReflect is used to assign a type code to each type in the program
// assignTypeCodes is used to assign a type code to each type in the program
// that is ever stored in an interface. It tries to use the smallest possible
// numbers to make the code that works with interfaces as small as possible.
func LowerReflect(mod llvm.Module) {
func assignTypeCodes(mod llvm.Module, typeSlice typeInfoSlice) {
// if reflect were not used, we could skip generating the sidetable
// this does not help in practice, and is difficult to do correctly
// Obtain slice of all types in the program.
type typeInfo struct {
typecode llvm.Value
name string
numUses int
}
var types []*typeInfo
for global := mod.FirstGlobal(); !global.IsNil(); global = llvm.NextGlobal(global) {
if strings.HasPrefix(global.Name(), "reflect/types.type:") {
types = append(types, &typeInfo{
typecode: global,
name: global.Name(),
numUses: len(getUses(global)),
})
}
}
// Sort the slice in a way that often used types are assigned a type code
// first.
sort.Slice(types, func(i, j int) bool {
if types[i].numUses != types[j].numUses {
return types[i].numUses < types[j].numUses
}
// It would make more sense to compare the name in the other direction,
// but for some reason that increases binary size. Could be a fluke, but
// could also have some good reason (and possibly hint at a small
// optimization).
return types[i].name > types[j].name
})
// Assign typecodes the way the reflect package expects.
uintptrType := mod.Context().IntType(llvm.NewTargetData(mod.DataLayout()).PointerSize() * 8)
state := typeCodeAssignmentState{
fallbackIndex: 1,
uintptrLen: llvm.NewTargetData(mod.DataLayout()).PointerSize() * 8,
@@ -175,7 +143,7 @@ func LowerReflect(mod llvm.Module) {
needsStructNamesSidetable: len(getUses(mod.NamedGlobal("reflect.structNamesSidetable"))) != 0,
needsArrayTypesSidetable: len(getUses(mod.NamedGlobal("reflect.arrayTypesSidetable"))) != 0,
}
for _, t := range types {
for _, t := range typeSlice {
num := state.getTypeCodeNum(t.typecode)
if num.BitLen() > state.uintptrLen || !num.IsUint64() {
// TODO: support this in some way, using a side table for example.
@@ -184,25 +152,7 @@ func LowerReflect(mod llvm.Module) {
// AVR).
panic("compiler: could not store type code number inside interface type code")
}
// Replace each use of the type code global with the constant type code.
for _, use := range getUses(t.typecode) {
if use.IsAConstantExpr().IsNil() {
continue
}
typecode := llvm.ConstInt(uintptrType, num.Uint64(), false)
switch use.Opcode() {
case llvm.PtrToInt:
// Already of the correct type.
case llvm.BitCast:
// Could happen when stored in an interface (which is of type
// i8*).
typecode = llvm.ConstIntToPtr(typecode, use.Type())
default:
panic("unexpected constant expression")
}
use.ReplaceAllUsesWith(typecode)
}
t.num = num.Uint64()
}
// Only create this sidetable when it is necessary.
@@ -230,23 +180,6 @@ func LowerReflect(mod llvm.Module) {
global.SetUnnamedAddr(true)
global.SetGlobalConstant(true)
}
// Remove most objects created for interface and reflect lowering.
// They would normally be removed anyway in later passes, but not always.
// It also cleans up the IR for testing.
for _, typ := range types {
initializer := typ.typecode.Initializer()
references := llvm.ConstExtractValue(initializer, []uint32{0})
typ.typecode.SetInitializer(llvm.ConstNull(initializer.Type()))
if strings.HasPrefix(typ.name, "reflect/types.type:struct:") {
// Structs have a 'references' field that is not a typecode but
// a pointer to a runtime.structField array and therefore a
// bitcast. This global should be erased separately, otherwise
// typecode objects cannot be erased.
structFields := references.Operand(0)
structFields.EraseFromParentAsGlobal()
}
}
}
// getTypeCodeNum returns the typecode for a given type as expected by the
-77
View File
@@ -1,77 +0,0 @@
package transform_test
import (
"testing"
"github.com/tinygo-org/tinygo/transform"
"tinygo.org/x/go-llvm"
)
type reflectAssert struct {
call llvm.Value
name string
expectedNumber uint64
}
// Test reflect lowering. This code looks at IR like this:
//
// call void @main.assertType(i32 ptrtoint (%runtime.typecodeID* @"reflect/types.type:basic:int" to i32), i8* inttoptr (i32 3 to i8*), i32 4, i8* undef, i8* undef)
//
// and verifies that the ptrtoint constant (the first parameter of
// @main.assertType) is replaced with the correct type code. The expected
// output is this:
//
// call void @main.assertType(i32 4, i8* inttoptr (i32 3 to i8*), i32 4, i8* undef, i8* undef)
//
// The first and third parameter are compared and must match, the second
// parameter is ignored.
func TestReflect(t *testing.T) {
t.Parallel()
mod := compileGoFileForTesting(t, "./testdata/reflect.go")
// Run the instcombine pass, to clean up the IR a bit (especially
// insertvalue/extractvalue instructions).
pm := llvm.NewPassManager()
defer pm.Dispose()
pm.AddInstructionCombiningPass()
pm.Run(mod)
// Get a list of all the asserts in the source code.
assertType := mod.NamedFunction("main.assertType")
var asserts []reflectAssert
for user := assertType.FirstUse(); !user.IsNil(); user = user.NextUse() {
use := user.User()
if use.IsACallInst().IsNil() {
t.Fatal("expected call use of main.assertType")
}
global := use.Operand(0).Operand(0)
expectedNumber := use.Operand(2).ZExtValue()
asserts = append(asserts, reflectAssert{
call: use,
name: global.Name(),
expectedNumber: expectedNumber,
})
}
// Sanity check to show that the test is actually testing anything.
if len(asserts) < 3 {
t.Errorf("expected at least 3 test cases, got %d", len(asserts))
}
// Now lower the type codes.
transform.LowerReflect(mod)
// Check whether the values are as expected.
for _, assert := range asserts {
actualNumberValue := assert.call.Operand(0)
if actualNumberValue.IsAConstantInt().IsNil() {
t.Errorf("expected to see a constant for %s, got something else", assert.name)
continue
}
actualNumber := actualNumberValue.ZExtValue()
if actualNumber != assert.expectedNumber {
t.Errorf("%s: expected number 0b%b, got 0b%b", assert.name, assert.expectedNumber, actualNumber)
}
}
}
+1 -20
View File
@@ -24,24 +24,11 @@ func main() {
readByteSlice(s4)
s5 := make([]int, 4) // OUT: object allocated on the heap: escapes at line 27
_ = append(s5, 5)
s5 = append(s5, 5)
s6 := make([]int, 3)
s7 := []int{1, 2, 3}
copySlice(s6, s7)
c1 := getComplex128() // OUT: object allocated on the heap: escapes at line 34
useInterface(c1)
n3 := 5 // OUT: object allocated on the heap: escapes at line 39
func() int {
return n3
}()
callVariadic(3, 5, 8) // OUT: object allocated on the heap: escapes at line 41
s8 := []int{3, 5, 8} // OUT: object allocated on the heap: escapes at line 44
callVariadic(s8...)
}
func derefInt(x *int) int {
@@ -69,9 +56,3 @@ func getUnknownNumber() int
func copySlice(out, in []int) {
copy(out, in)
}
func getComplex128() complex128
func useInterface(interface{})
func callVariadic(...int)
+2 -2
View File
@@ -4,10 +4,10 @@ target triple = "armv7m-none-eabi"
%runtime.typecodeID = type { %runtime.typecodeID*, i32, %runtime.interfaceMethodInfo* }
%runtime.interfaceMethodInfo = type { i8*, i32 }
@"reflect/types.type:basic:uint8" = private constant %runtime.typecodeID zeroinitializer
@"reflect/types.type:basic:uint8" = external constant %runtime.typecodeID
@"reflect/types.typeid:basic:uint8" = external constant i8
@"reflect/types.typeid:basic:int16" = external constant i8
@"reflect/types.type:basic:int" = private constant %runtime.typecodeID zeroinitializer
@"reflect/types.type:basic:int" = external constant %runtime.typecodeID
@"func NeverImplementedMethod()" = external constant i8
@"Unmatched$interface" = private constant [1 x i8*] [i8* @"func NeverImplementedMethod()"]
@"func Double() int" = external constant i8
+33 -17
View File
@@ -4,9 +4,19 @@ target triple = "armv7m-none-eabi"
%runtime.typecodeID = type { %runtime.typecodeID*, i32, %runtime.interfaceMethodInfo* }
%runtime.interfaceMethodInfo = type { i8*, i32 }
@"reflect/types.type:basic:uint8" = private constant %runtime.typecodeID zeroinitializer
@"reflect/types.type:basic:int" = private constant %runtime.typecodeID zeroinitializer
@"reflect/types.type:named:Number" = private constant %runtime.typecodeID { %runtime.typecodeID* @"reflect/types.type:basic:int", i32 0, %runtime.interfaceMethodInfo* null }
@"reflect/types.type:basic:uint8" = external constant %runtime.typecodeID
@"reflect/types.typeid:basic:uint8" = external constant i8
@"reflect/types.typeid:basic:int16" = external constant i8
@"reflect/types.type:basic:int" = external constant %runtime.typecodeID
@"func NeverImplementedMethod()" = external constant i8
@"func Double() int" = external constant i8
@"reflect/types.type:named:Number" = private constant %runtime.typecodeID zeroinitializer
declare i1 @runtime.interfaceImplements(i32, i8**)
declare i1 @runtime.typeAssert(i32, i8*)
declare i32 @runtime.interfaceMethod(i32, i8**, i8*)
declare void @runtime.printuint8(i8)
@@ -21,9 +31,9 @@ declare void @runtime.printnl()
declare void @runtime.nilPanic(i8*, i8*)
define void @printInterfaces() {
call void @printInterface(i32 ptrtoint (%runtime.typecodeID* @"reflect/types.type:basic:int" to i32), i8* inttoptr (i32 5 to i8*))
call void @printInterface(i32 ptrtoint (%runtime.typecodeID* @"reflect/types.type:basic:uint8" to i32), i8* inttoptr (i8 120 to i8*))
call void @printInterface(i32 ptrtoint (%runtime.typecodeID* @"reflect/types.type:named:Number" to i32), i8* inttoptr (i32 3 to i8*))
call void @printInterface(i32 4, i8* inttoptr (i32 5 to i8*))
call void @printInterface(i32 16, i8* inttoptr (i8 120 to i8*))
call void @printInterface(i32 68, i8* inttoptr (i32 3 to i8*))
ret void
}
@@ -47,7 +57,7 @@ typeswitch.Doubler: ; preds = %typeswitch.notUnmat
ret void
typeswitch.notDoubler: ; preds = %typeswitch.notUnmatched
%typeassert.ok2 = icmp eq i32 ptrtoint (%runtime.typecodeID* @"reflect/types.type:basic:uint8" to i32), %typecode
%typeassert.ok2 = icmp eq i32 16, %typecode
br i1 %typeassert.ok2, label %typeswitch.byte, label %typeswitch.notByte
typeswitch.byte: ; preds = %typeswitch.notDoubler
@@ -82,34 +92,40 @@ define i32 @"(Number).Double$invoke"(i8* %receiverPtr, i8* %parentHandle) {
define internal i32 @"(Doubler).Double"(i8* %0, i8* %1, i32 %actualType, i8* %parentHandle) unnamed_addr {
entry:
%"named:Number.icmp" = icmp eq i32 %actualType, ptrtoint (%runtime.typecodeID* @"reflect/types.type:named:Number" to i32)
br i1 %"named:Number.icmp", label %"named:Number", label %"named:Number.next"
switch i32 %actualType, label %default [
i32 68, label %"named:Number"
]
default: ; preds = %entry
call void @runtime.nilPanic(i8* undef, i8* undef)
unreachable
"named:Number": ; preds = %entry
%2 = call i32 @"(Number).Double$invoke"(i8* %0, i8* %1)
ret i32 %2
"named:Number.next": ; preds = %entry
call void @runtime.nilPanic(i8* undef, i8* undef)
unreachable
}
define internal i1 @"Doubler$typeassert"(i32 %actualType) unnamed_addr {
entry:
%"named:Number.icmp" = icmp eq i32 %actualType, ptrtoint (%runtime.typecodeID* @"reflect/types.type:named:Number" to i32)
br i1 %"named:Number.icmp", label %then, label %"named:Number.next"
switch i32 %actualType, label %else [
i32 68, label %then
]
then: ; preds = %entry
ret i1 true
"named:Number.next": ; preds = %entry
else: ; preds = %entry
ret i1 false
}
define internal i1 @"Unmatched$typeassert"(i32 %actualType) unnamed_addr {
entry:
ret i1 false
switch i32 %actualType, label %else [
]
then: ; No predecessors!
ret i1 true
else: ; preds = %entry
ret i1 false
}
-56
View File
@@ -1,56 +0,0 @@
package main
// This file tests the type codes assigned by the reflect lowering pass.
// This test is not complete, most importantly, sidetables are not currently
// being tested.
import (
"reflect"
"unsafe"
)
const (
// See the top of src/reflect/type.go
prefixChan = 0b0001
prefixInterface = 0b0011
prefixPtr = 0b0101
prefixSlice = 0b0111
prefixArray = 0b1001
prefixFunc = 0b1011
prefixMap = 0b1101
prefixStruct = 0b1111
)
func main() {
// Check for some basic types.
assertType(3, uintptr(reflect.Int)<<1)
assertType(uint8(3), uintptr(reflect.Uint8)<<1)
assertType(byte(3), uintptr(reflect.Uint8)<<1)
assertType(int64(3), uintptr(reflect.Int64)<<1)
assertType("", uintptr(reflect.String)<<1)
assertType(3.5, uintptr(reflect.Float64)<<1)
assertType(unsafe.Pointer(nil), uintptr(reflect.UnsafePointer)<<1)
// Check for named types: they are given names in order.
// They are sorted in reverse, for no good reason.
const intNum = uintptr(reflect.Int) << 1
assertType(namedInt1(0), (3<<6)|intNum)
assertType(namedInt2(0), (2<<6)|intNum)
assertType(namedInt3(0), (1<<6)|intNum)
// Check for some "prefix-style" types.
assertType(make(chan int), (intNum<<5)|prefixChan)
assertType(new(int), (intNum<<5)|prefixPtr)
assertType([]int{}, (intNum<<5)|prefixSlice)
}
type (
namedInt1 int
namedInt2 int
namedInt3 int
)
// Pseudo call that is being checked by the code in reflect_test.go.
// After reflect lowering, the type code as part of the interface should match
// the asserted type code.
func assertType(itf interface{}, assertedTypeCode uintptr)
-89
View File
@@ -4,19 +4,13 @@ package transform_test
import (
"flag"
"go/token"
"go/types"
"io/ioutil"
"os"
"path/filepath"
"regexp"
"strconv"
"strings"
"testing"
"github.com/tinygo-org/tinygo/compileopts"
"github.com/tinygo-org/tinygo/compiler"
"github.com/tinygo-org/tinygo/loader"
"tinygo.org/x/go-llvm"
)
@@ -134,86 +128,3 @@ func filterIrrelevantIRLines(lines []string) []string {
}
return out
}
// compileGoFileForTesting compiles the given Go file to run tests against.
// Only the given Go file is compiled (no dependencies) and no optimizations are
// run.
// If there are any errors, they are reported via the *testing.T instance.
func compileGoFileForTesting(t *testing.T, filename string) llvm.Module {
target, err := compileopts.LoadTarget("i686--linux")
if err != nil {
t.Fatal("failed to load target:", err)
}
config := &compileopts.Config{
Options: &compileopts.Options{},
Target: target,
}
compilerConfig := &compiler.Config{
Triple: config.Triple(),
GOOS: config.GOOS(),
GOARCH: config.GOARCH(),
CodeModel: config.CodeModel(),
RelocationModel: config.RelocationModel(),
Scheduler: config.Scheduler(),
FuncImplementation: config.FuncImplementation(),
AutomaticStackSize: config.AutomaticStackSize(),
Debug: true,
}
machine, err := compiler.NewTargetMachine(compilerConfig)
if err != nil {
t.Fatal("failed to create target machine:", err)
}
// Load entire program AST into memory.
lprogram, err := loader.Load(config, []string{filename}, config.ClangHeaders, types.Config{
Sizes: compiler.Sizes(machine),
})
if err != nil {
t.Fatal("failed to create target machine:", err)
}
err = lprogram.Parse()
if err != nil {
t.Fatal("could not parse", err)
}
// Compile AST to IR.
program := lprogram.LoadSSA()
pkg := lprogram.MainPkg()
mod, errs := compiler.CompilePackage(filename, pkg, program.Package(pkg.Pkg), machine, compilerConfig, false)
if errs != nil {
for _, err := range errs {
t.Error(err)
}
t.FailNow()
}
return mod
}
// getPosition returns the position information for the given value, as far as
// it is available.
func getPosition(val llvm.Value) token.Position {
if !val.IsAInstruction().IsNil() {
loc := val.InstructionDebugLoc()
if loc.IsNil() {
return token.Position{}
}
file := loc.LocationScope().ScopeFile()
return token.Position{
Filename: filepath.Join(file.FileDirectory(), file.FileFilename()),
Line: int(loc.LocationLine()),
Column: int(loc.LocationColumn()),
}
} else if !val.IsAFunction().IsNil() {
loc := val.Subprogram()
if loc.IsNil() {
return token.Position{}
}
file := loc.ScopeFile()
return token.Position{
Filename: filepath.Join(file.FileDirectory(), file.FileFilename()),
Line: int(loc.SubprogramLine()),
}
} else {
return token.Position{}
}
}