mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-11 14:33:41 +00:00
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ced1fa616c | |||
| a73eac22f1 |
+2
-2
@@ -446,7 +446,7 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
|||||||
if pkgInit.IsNil() {
|
if pkgInit.IsNil() {
|
||||||
panic("init not found for " + pkg.Pkg.Path())
|
panic("init not found for " + pkg.Pkg.Path())
|
||||||
}
|
}
|
||||||
err := interp.RunFunc(pkgInit, config.Options.InterpTimeout, config.DumpSSA())
|
err := interp.RunFunc(pkgInit, config.Options.InterpTimeout, config.Options.InterpMaxDepth, config.Options.InterpMaxInstr, config.DumpSSA())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -1043,7 +1043,7 @@ func createEmbedObjectFile(data, hexSum, sourceFile, sourceDir, tmpdir string, c
|
|||||||
// needed to convert a program to its final form. Some transformations are not
|
// needed to convert a program to its final form. Some transformations are not
|
||||||
// optional and must be run as the compiler expects them to run.
|
// optional and must be run as the compiler expects them to run.
|
||||||
func optimizeProgram(mod llvm.Module, config *compileopts.Config) error {
|
func optimizeProgram(mod llvm.Module, config *compileopts.Config) error {
|
||||||
err := interp.Run(mod, config.Options.InterpTimeout, config.DumpSSA())
|
err := interp.Run(mod, config.Options.InterpTimeout, config.Options.InterpMaxDepth, config.Options.InterpMaxInstr, config.DumpSSA())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
+19
-9
@@ -75,10 +75,19 @@ type bitfieldInfo struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// cgoAliases list type aliases between Go and C, for types that are equivalent
|
// cgoAliases list type aliases between Go and C, for types that are equivalent
|
||||||
// in both languages.
|
// in both languages. See addTypeAliases.
|
||||||
var cgoAliases = map[string]string{
|
var cgoAliases = map[string]string{
|
||||||
"float": "float32",
|
"C.int8_t": "int8",
|
||||||
"double": "float64",
|
"C.int16_t": "int16",
|
||||||
|
"C.int32_t": "int32",
|
||||||
|
"C.int64_t": "int64",
|
||||||
|
"C.uint8_t": "uint8",
|
||||||
|
"C.uint16_t": "uint16",
|
||||||
|
"C.uint32_t": "uint32",
|
||||||
|
"C.uint64_t": "uint64",
|
||||||
|
"C.uintptr_t": "uintptr",
|
||||||
|
"C.float": "float32",
|
||||||
|
"C.double": "float64",
|
||||||
}
|
}
|
||||||
|
|
||||||
// builtinAliases are handled specially because they only exist on the Go side
|
// builtinAliases are handled specially because they only exist on the Go side
|
||||||
@@ -302,11 +311,10 @@ func Process(files []*ast.File, dir, importPath string, fset *token.FileSet, cfl
|
|||||||
// Process CGo imports for each file.
|
// Process CGo imports for each file.
|
||||||
for i, f := range files {
|
for i, f := range files {
|
||||||
cf := p.newCGoFile(f, i)
|
cf := p.newCGoFile(f, i)
|
||||||
// These types are aliased with the corresponding types in C. For
|
// Float and double are aliased, meaning that C.float is the same thing
|
||||||
// example, float in C is always float32 in Go.
|
// as float32 in Go.
|
||||||
for name := range cgoAliases {
|
cf.names["float"] = clangCursor{}
|
||||||
cf.names[name] = clangCursor{}
|
cf.names["double"] = clangCursor{}
|
||||||
}
|
|
||||||
// Now read all the names (identifies) that C defines in the header
|
// Now read all the names (identifies) that C defines in the header
|
||||||
// snippet.
|
// snippet.
|
||||||
cf.readNames(p.cgoHeaders[i], cflagsForCGo, filepath.Base(fset.File(f.Pos()).Name()), func(names map[string]clangCursor) {
|
cf.readNames(p.cgoHeaders[i], cflagsForCGo, filepath.Base(fset.File(f.Pos()).Name()), func(names map[string]clangCursor) {
|
||||||
@@ -1121,7 +1129,9 @@ func (p *cgoPackage) getUnnamedDeclName(prefix string, itf interface{}) string {
|
|||||||
// getASTDeclName will declare the given C AST node (if not already defined) and
|
// getASTDeclName will declare the given C AST node (if not already defined) and
|
||||||
// will return its name, in the form of C.foo.
|
// will return its name, in the form of C.foo.
|
||||||
func (f *cgoFile) getASTDeclName(name string, found clangCursor, iscall bool) string {
|
func (f *cgoFile) getASTDeclName(name string, found clangCursor, iscall bool) string {
|
||||||
if alias := cgoAliases[name]; alias != "" {
|
// Some types are defined in stdint.h and map directly to a particular Go
|
||||||
|
// type.
|
||||||
|
if alias := cgoAliases["C."+name]; alias != "" {
|
||||||
return alias
|
return alias
|
||||||
}
|
}
|
||||||
node := f.getASTDeclNode(name, found, iscall)
|
node := f.getASTDeclNode(name, found, iscall)
|
||||||
|
|||||||
Vendored
-8
@@ -22,12 +22,8 @@ import "C"
|
|||||||
// #warning another warning
|
// #warning another warning
|
||||||
import "C"
|
import "C"
|
||||||
|
|
||||||
// #include <stdint.h>
|
|
||||||
import "C"
|
|
||||||
|
|
||||||
// Make sure that errors for the following lines won't change with future
|
// Make sure that errors for the following lines won't change with future
|
||||||
// additions to the CGo preamble.
|
// additions to the CGo preamble.
|
||||||
//
|
|
||||||
//line errors.go:100
|
//line errors.go:100
|
||||||
var (
|
var (
|
||||||
// constant too large
|
// constant too large
|
||||||
@@ -42,8 +38,4 @@ var (
|
|||||||
_ byte = C.SOME_CONST_3
|
_ byte = C.SOME_CONST_3
|
||||||
|
|
||||||
_ = C.SOME_CONST_4
|
_ = C.SOME_CONST_4
|
||||||
|
|
||||||
// This must result in a type error. Previously, TinyGo would allow this
|
|
||||||
// code (which is not allowed by upstream Go).
|
|
||||||
_ int8 = C.int8_t(5)
|
|
||||||
)
|
)
|
||||||
|
|||||||
Vendored
-3
@@ -11,7 +11,6 @@
|
|||||||
// testdata/errors.go:108: undefined: C.SOME_CONST_1
|
// testdata/errors.go:108: undefined: C.SOME_CONST_1
|
||||||
// testdata/errors.go:110: cannot use C.SOME_CONST_3 (untyped int constant 1234) as byte value in variable declaration (overflows)
|
// testdata/errors.go:110: cannot use C.SOME_CONST_3 (untyped int constant 1234) as byte value in variable declaration (overflows)
|
||||||
// testdata/errors.go:112: undefined: C.SOME_CONST_4
|
// testdata/errors.go:112: undefined: C.SOME_CONST_4
|
||||||
// testdata/errors.go:116: cannot use C.int8_t(5) (constant 5 of type C.schar) as int8 value in variable declaration
|
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
@@ -59,5 +58,3 @@ type C.struct_point_t struct {
|
|||||||
type C.point_t = C.struct_point_t
|
type C.point_t = C.struct_point_t
|
||||||
|
|
||||||
const C.SOME_CONST_3 = 1234
|
const C.SOME_CONST_3 = 1234
|
||||||
|
|
||||||
type C.int8_t = C.schar
|
|
||||||
|
|||||||
@@ -32,6 +32,8 @@ type Options struct {
|
|||||||
Serial string
|
Serial string
|
||||||
Work bool // -work flag to print temporary build directory
|
Work bool // -work flag to print temporary build directory
|
||||||
InterpTimeout time.Duration
|
InterpTimeout time.Duration
|
||||||
|
InterpMaxDepth int
|
||||||
|
InterpMaxInstr int
|
||||||
PrintIR bool
|
PrintIR bool
|
||||||
DumpSSA bool
|
DumpSSA bool
|
||||||
VerifyIR bool
|
VerifyIR bool
|
||||||
|
|||||||
+2
-1
@@ -19,6 +19,7 @@ var (
|
|||||||
errUnsupportedRuntimeInst = errors.New("interp: unsupported instruction (to be emitted at runtime)")
|
errUnsupportedRuntimeInst = errors.New("interp: unsupported instruction (to be emitted at runtime)")
|
||||||
errMapAlreadyCreated = errors.New("interp: map already created")
|
errMapAlreadyCreated = errors.New("interp: map already created")
|
||||||
errLoopUnrolled = errors.New("interp: loop unrolled")
|
errLoopUnrolled = errors.New("interp: loop unrolled")
|
||||||
|
errDepthExceeded = errors.New("interp: depth exceeded")
|
||||||
)
|
)
|
||||||
|
|
||||||
// This is one of the errors that can be returned from toLLVMValue when the
|
// This is one of the errors that can be returned from toLLVMValue when the
|
||||||
@@ -29,7 +30,7 @@ var errInvalidPtrToIntSize = errors.New("interp: ptrtoint integer size does not
|
|||||||
func isRecoverableError(err error) bool {
|
func isRecoverableError(err error) bool {
|
||||||
return err == errIntegerAsPointer || err == errUnsupportedInst ||
|
return err == errIntegerAsPointer || err == errUnsupportedInst ||
|
||||||
err == errUnsupportedRuntimeInst || err == errMapAlreadyCreated ||
|
err == errUnsupportedRuntimeInst || err == errMapAlreadyCreated ||
|
||||||
err == errLoopUnrolled
|
err == errLoopUnrolled || err == errDepthExceeded
|
||||||
}
|
}
|
||||||
|
|
||||||
// ErrorLine is one line in a traceback. The position may be missing.
|
// ErrorLine is one line in a traceback. The position may be missing.
|
||||||
|
|||||||
+11
-7
@@ -31,10 +31,12 @@ type runner struct {
|
|||||||
globals map[llvm.Value]int // map from global to index in objects slice
|
globals map[llvm.Value]int // map from global to index in objects slice
|
||||||
start time.Time
|
start time.Time
|
||||||
timeout time.Duration
|
timeout time.Duration
|
||||||
|
maxDepth int
|
||||||
|
maxInstr int
|
||||||
callsExecuted uint64
|
callsExecuted uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
func newRunner(mod llvm.Module, timeout time.Duration, debug bool) *runner {
|
func newRunner(mod llvm.Module, timeout time.Duration, maxDepth int, maxInstr int, debug bool) *runner {
|
||||||
r := runner{
|
r := runner{
|
||||||
mod: mod,
|
mod: mod,
|
||||||
targetData: llvm.NewTargetData(mod.DataLayout()),
|
targetData: llvm.NewTargetData(mod.DataLayout()),
|
||||||
@@ -44,6 +46,8 @@ func newRunner(mod llvm.Module, timeout time.Duration, debug bool) *runner {
|
|||||||
globals: make(map[llvm.Value]int),
|
globals: make(map[llvm.Value]int),
|
||||||
start: time.Now(),
|
start: time.Now(),
|
||||||
timeout: timeout,
|
timeout: timeout,
|
||||||
|
maxDepth: maxDepth,
|
||||||
|
maxInstr: maxInstr,
|
||||||
}
|
}
|
||||||
r.pointerSize = uint32(r.targetData.PointerSize())
|
r.pointerSize = uint32(r.targetData.PointerSize())
|
||||||
r.i8ptrType = llvm.PointerType(mod.Context().Int8Type(), 0)
|
r.i8ptrType = llvm.PointerType(mod.Context().Int8Type(), 0)
|
||||||
@@ -60,8 +64,8 @@ func (r *runner) dispose() {
|
|||||||
|
|
||||||
// Run evaluates runtime.initAll function as much as possible at compile time.
|
// Run evaluates runtime.initAll function as much as possible at compile time.
|
||||||
// Set debug to true if it should print output while running.
|
// Set debug to true if it should print output while running.
|
||||||
func Run(mod llvm.Module, timeout time.Duration, debug bool) error {
|
func Run(mod llvm.Module, timeout time.Duration, maxDepth int, maxInstr int, debug bool) error {
|
||||||
r := newRunner(mod, timeout, debug)
|
r := newRunner(mod, timeout, maxDepth, maxInstr, debug)
|
||||||
defer r.dispose()
|
defer r.dispose()
|
||||||
|
|
||||||
initAll := mod.NamedFunction("runtime.initAll")
|
initAll := mod.NamedFunction("runtime.initAll")
|
||||||
@@ -114,7 +118,7 @@ func Run(mod llvm.Module, timeout time.Duration, debug bool) error {
|
|||||||
if r.debug {
|
if r.debug {
|
||||||
fmt.Fprintln(os.Stderr, "call:", fn.Name())
|
fmt.Fprintln(os.Stderr, "call:", fn.Name())
|
||||||
}
|
}
|
||||||
_, mem, callErr := r.run(r.getFunction(fn), nil, nil, " ")
|
_, mem, callErr := r.run(r.getFunction(fn), nil, nil, 0, " ")
|
||||||
call.EraseFromParentAsInstruction()
|
call.EraseFromParentAsInstruction()
|
||||||
if callErr != nil {
|
if callErr != nil {
|
||||||
if isRecoverableError(callErr.Err) {
|
if isRecoverableError(callErr.Err) {
|
||||||
@@ -201,10 +205,10 @@ func Run(mod llvm.Module, timeout time.Duration, debug bool) error {
|
|||||||
|
|
||||||
// RunFunc evaluates a single package initializer at compile time.
|
// RunFunc evaluates a single package initializer at compile time.
|
||||||
// Set debug to true if it should print output while running.
|
// Set debug to true if it should print output while running.
|
||||||
func RunFunc(fn llvm.Value, timeout time.Duration, debug bool) error {
|
func RunFunc(fn llvm.Value, timeout time.Duration, maxDepth int, maxInstr int, debug bool) error {
|
||||||
// Create and initialize *runner object.
|
// Create and initialize *runner object.
|
||||||
mod := fn.GlobalParent()
|
mod := fn.GlobalParent()
|
||||||
r := newRunner(mod, timeout, debug)
|
r := newRunner(mod, timeout, maxDepth, maxInstr, debug)
|
||||||
defer r.dispose()
|
defer r.dispose()
|
||||||
initName := fn.Name()
|
initName := fn.Name()
|
||||||
if !strings.HasSuffix(initName, ".init") {
|
if !strings.HasSuffix(initName, ".init") {
|
||||||
@@ -235,7 +239,7 @@ func RunFunc(fn llvm.Value, timeout time.Duration, debug bool) error {
|
|||||||
if r.debug {
|
if r.debug {
|
||||||
fmt.Fprintln(os.Stderr, "interp:", fn.Name())
|
fmt.Fprintln(os.Stderr, "interp:", fn.Name())
|
||||||
}
|
}
|
||||||
_, pkgMem, callErr := r.run(r.getFunction(fn), nil, nil, " ")
|
_, pkgMem, callErr := r.run(r.getFunction(fn), nil, nil, 0, " ")
|
||||||
if callErr != nil {
|
if callErr != nil {
|
||||||
if isRecoverableError(callErr.Err) {
|
if isRecoverableError(callErr.Err) {
|
||||||
// Could not finish, but could recover from it.
|
// Could not finish, but could recover from it.
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ func runTest(t *testing.T, pathPrefix string) {
|
|||||||
defer mod.Dispose()
|
defer mod.Dispose()
|
||||||
|
|
||||||
// Perform the transform.
|
// Perform the transform.
|
||||||
err = Run(mod, 10*time.Minute, false)
|
err = Run(mod, 10*time.Minute, 10, 0, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err, match := err.(*Error); match {
|
if err, match := err.(*Error); match {
|
||||||
println(err.Error())
|
println(err.Error())
|
||||||
|
|||||||
+14
-2
@@ -12,7 +12,7 @@ import (
|
|||||||
"tinygo.org/x/go-llvm"
|
"tinygo.org/x/go-llvm"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent string) (value, memoryView, *Error) {
|
func (r *runner) run(fn *function, params []value, parentMem *memoryView, depth int, indent string) (value, memoryView, *Error) {
|
||||||
mem := memoryView{r: r, parent: parentMem}
|
mem := memoryView{r: r, parent: parentMem}
|
||||||
locals := make([]value, len(fn.locals))
|
locals := make([]value, len(fn.locals))
|
||||||
r.callsExecuted++
|
r.callsExecuted++
|
||||||
@@ -33,8 +33,20 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
|
|||||||
lastBB := -1 // last basic block is undefined, only defined after a branch
|
lastBB := -1 // last basic block is undefined, only defined after a branch
|
||||||
var operands []value
|
var operands []value
|
||||||
startRTInsts := len(mem.instructions)
|
startRTInsts := len(mem.instructions)
|
||||||
|
instCount := 0
|
||||||
for instIndex := 0; instIndex < len(bb.instructions); instIndex++ {
|
for instIndex := 0; instIndex < len(bb.instructions); instIndex++ {
|
||||||
|
instCount++
|
||||||
|
if r.maxInstr > 0 && instCount > r.maxInstr {
|
||||||
|
fmt.Printf("interp: excess instructions evaluated in %v\n", fn.name)
|
||||||
|
return nil, mem, r.errorAt(fn.blocks[0].instructions[0], errDepthExceeded)
|
||||||
|
}
|
||||||
|
|
||||||
if instIndex == 0 {
|
if instIndex == 0 {
|
||||||
|
if r.maxDepth > 0 && depth >= r.maxDepth {
|
||||||
|
fmt.Printf("interp: depth exceeded in %v\n", fn.name)
|
||||||
|
return nil, mem, r.errorAt(fn.blocks[0].instructions[0], errDepthExceeded)
|
||||||
|
}
|
||||||
|
|
||||||
// This is the start of a new basic block.
|
// This is the start of a new basic block.
|
||||||
if len(mem.instructions) != startRTInsts {
|
if len(mem.instructions) != startRTInsts {
|
||||||
if _, ok := runtimeBlocks[lastBB]; ok {
|
if _, ok := runtimeBlocks[lastBB]; ok {
|
||||||
@@ -523,7 +535,7 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
|
|||||||
}
|
}
|
||||||
fmt.Fprintln(os.Stderr, indent+"call:", callFn.name+"("+strings.Join(argStrings, ", ")+")")
|
fmt.Fprintln(os.Stderr, indent+"call:", callFn.name+"("+strings.Join(argStrings, ", ")+")")
|
||||||
}
|
}
|
||||||
retval, callMem, callErr := r.run(callFn, operands[1:], &mem, indent+" ")
|
retval, callMem, callErr := r.run(callFn, operands[1:], &mem, depth+1, indent+" ")
|
||||||
if callErr != nil {
|
if callErr != nil {
|
||||||
if isRecoverableError(callErr.Err) {
|
if isRecoverableError(callErr.Err) {
|
||||||
// This error can be recovered by doing the call at
|
// This error can be recovered by doing the call at
|
||||||
|
|||||||
@@ -1418,6 +1418,8 @@ func main() {
|
|||||||
serial := flag.String("serial", "", "which serial output to use (none, uart, usb)")
|
serial := flag.String("serial", "", "which serial output to use (none, uart, usb)")
|
||||||
work := flag.Bool("work", false, "print the name of the temporary build directory and do not delete this directory on exit")
|
work := flag.Bool("work", false, "print the name of the temporary build directory and do not delete this directory on exit")
|
||||||
interpTimeout := flag.Duration("interp-timeout", 180*time.Second, "interp optimization pass timeout")
|
interpTimeout := flag.Duration("interp-timeout", 180*time.Second, "interp optimization pass timeout")
|
||||||
|
interpMaxDepth := flag.Int("interp-maxdepth", 0, "interp optimization max depth (default 0=disabled)")
|
||||||
|
interpMaxInstr := flag.Int("interp-maxinstr", 100_000, "limit interp optimization max instructions (0=disabled)")
|
||||||
var tags buildutil.TagsFlag
|
var tags buildutil.TagsFlag
|
||||||
flag.Var(&tags, "tags", "a space-separated list of extra build tags")
|
flag.Var(&tags, "tags", "a space-separated list of extra build tags")
|
||||||
target := flag.String("target", "", "chip/board name or JSON target specification file")
|
target := flag.String("target", "", "chip/board name or JSON target specification file")
|
||||||
@@ -1528,6 +1530,8 @@ func main() {
|
|||||||
Serial: *serial,
|
Serial: *serial,
|
||||||
Work: *work,
|
Work: *work,
|
||||||
InterpTimeout: *interpTimeout,
|
InterpTimeout: *interpTimeout,
|
||||||
|
InterpMaxDepth: *interpMaxDepth,
|
||||||
|
InterpMaxInstr: *interpMaxInstr,
|
||||||
PrintIR: *printIR,
|
PrintIR: *printIR,
|
||||||
DumpSSA: *dumpSSA,
|
DumpSSA: *dumpSSA,
|
||||||
VerifyIR: *verifyIR,
|
VerifyIR: *verifyIR,
|
||||||
|
|||||||
@@ -296,6 +296,7 @@ func optionsFromTarget(target string, sema chan struct{}) compileopts.Options {
|
|||||||
Target: target,
|
Target: target,
|
||||||
Semaphore: sema,
|
Semaphore: sema,
|
||||||
InterpTimeout: 180 * time.Second,
|
InterpTimeout: 180 * time.Second,
|
||||||
|
InterpMaxDepth: 10,
|
||||||
Debug: true,
|
Debug: true,
|
||||||
VerifyIR: true,
|
VerifyIR: true,
|
||||||
Opt: "z",
|
Opt: "z",
|
||||||
@@ -312,6 +313,7 @@ func optionsFromOSARCH(osarch string, sema chan struct{}) compileopts.Options {
|
|||||||
GOARCH: parts[1],
|
GOARCH: parts[1],
|
||||||
Semaphore: sema,
|
Semaphore: sema,
|
||||||
InterpTimeout: 180 * time.Second,
|
InterpTimeout: 180 * time.Second,
|
||||||
|
InterpMaxDepth: 10,
|
||||||
Debug: true,
|
Debug: true,
|
||||||
VerifyIR: true,
|
VerifyIR: true,
|
||||||
Opt: "z",
|
Opt: "z",
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ func TestTraceback(t *testing.T) {
|
|||||||
GOARCH: runtime.GOARCH,
|
GOARCH: runtime.GOARCH,
|
||||||
Opt: "z",
|
Opt: "z",
|
||||||
InterpTimeout: time.Minute,
|
InterpTimeout: time.Minute,
|
||||||
|
InterpMaxDepth: 10,
|
||||||
Debug: true,
|
Debug: true,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
Vendored
+4
@@ -52,6 +52,10 @@ func main() {
|
|||||||
println("static headerfunc:", C.headerfunc_static(5))
|
println("static headerfunc:", C.headerfunc_static(5))
|
||||||
headerfunc_2()
|
headerfunc_2()
|
||||||
|
|
||||||
|
// equivalent types
|
||||||
|
var goInt8 int8 = 5
|
||||||
|
var _ C.int8_t = goInt8
|
||||||
|
|
||||||
// more globals
|
// more globals
|
||||||
println("bool:", C.globalBool, C.globalBool2 == true)
|
println("bool:", C.globalBool, C.globalBool2 == true)
|
||||||
println("float:", C.globalFloat)
|
println("float:", C.globalFloat)
|
||||||
|
|||||||
Reference in New Issue
Block a user