mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-11 14:33:41 +00:00
Compare commits
12 Commits
v0.19.0
...
file-embed
| Author | SHA1 | Date | |
|---|---|---|---|
| 6b6b2bfc59 | |||
| 778c79cc85 | |||
| b40703e986 | |||
| 00ea0b1d57 | |||
| efa0410075 | |||
| 607d824211 | |||
| 8cc7c6d572 | |||
| cdba4fa8cc | |||
| 0565b7c0e0 | |||
| 444dded92c | |||
| 42785e08e8 | |||
| 2d633e3a28 |
+44
-30
@@ -470,33 +470,10 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
|||||||
linkerDependencies = append(linkerDependencies, job)
|
linkerDependencies = append(linkerDependencies, job)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add libc dependency if needed.
|
|
||||||
root := goenv.Get("TINYGOROOT")
|
|
||||||
switch config.Target.Libc {
|
|
||||||
case "picolibc":
|
|
||||||
job, err := Picolibc.load(config.Triple(), config.CPU(), dir)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// The library needs to be compiled (cache miss).
|
|
||||||
jobs = append(jobs, job.dependencies...)
|
|
||||||
jobs = append(jobs, job)
|
|
||||||
linkerDependencies = append(linkerDependencies, job)
|
|
||||||
case "wasi-libc":
|
|
||||||
path := filepath.Join(root, "lib/wasi-libc/sysroot/lib/wasm32-wasi/libc.a")
|
|
||||||
if _, err := os.Stat(path); os.IsNotExist(err) {
|
|
||||||
return errors.New("could not find wasi-libc, perhaps you need to run `make wasi-libc`?")
|
|
||||||
}
|
|
||||||
ldflags = append(ldflags, path)
|
|
||||||
case "":
|
|
||||||
// no library specified, so nothing to do
|
|
||||||
default:
|
|
||||||
return fmt.Errorf("unknown libc: %s", config.Target.Libc)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add jobs to compile extra files. These files are in C or assembly and
|
// Add jobs to compile extra files. These files are in C or assembly and
|
||||||
// contain things like the interrupt vector table and low level operations
|
// contain things like the interrupt vector table and low level operations
|
||||||
// such as stack switching.
|
// such as stack switching.
|
||||||
|
root := goenv.Get("TINYGOROOT")
|
||||||
for _, path := range config.ExtraFiles() {
|
for _, path := range config.ExtraFiles() {
|
||||||
abspath := filepath.Join(root, path)
|
abspath := filepath.Join(root, path)
|
||||||
job := &compileJob{
|
job := &compileJob{
|
||||||
@@ -537,6 +514,31 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
|||||||
ldflags = append(ldflags, lprogram.LDFlags...)
|
ldflags = append(ldflags, lprogram.LDFlags...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add libc dependency if needed.
|
||||||
|
switch config.Target.Libc {
|
||||||
|
case "picolibc":
|
||||||
|
job, err := Picolibc.load(config.Triple(), config.CPU(), dir)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// The library needs to be compiled (cache miss).
|
||||||
|
jobs = append(jobs, job.dependencies...)
|
||||||
|
jobs = append(jobs, job)
|
||||||
|
linkerDependencies = append(linkerDependencies, job)
|
||||||
|
case "wasi-libc":
|
||||||
|
path := filepath.Join(root, "lib/wasi-libc/sysroot/lib/wasm32-wasi/libc.a")
|
||||||
|
if _, err := os.Stat(path); os.IsNotExist(err) {
|
||||||
|
return errors.New("could not find wasi-libc, perhaps you need to run `make wasi-libc`?")
|
||||||
|
}
|
||||||
|
job := dummyCompileJob(path)
|
||||||
|
jobs = append(jobs, job)
|
||||||
|
linkerDependencies = append(linkerDependencies, job)
|
||||||
|
case "":
|
||||||
|
// no library specified, so nothing to do
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("unknown libc: %s", config.Target.Libc)
|
||||||
|
}
|
||||||
|
|
||||||
// Create a linker job, which links all object files together and does some
|
// Create a linker job, which links all object files together and does some
|
||||||
// extra stuff that can only be done after linking.
|
// extra stuff that can only be done after linking.
|
||||||
jobs = append(jobs, &compileJob{
|
jobs = append(jobs, &compileJob{
|
||||||
@@ -953,15 +955,19 @@ func modifyStackSizes(executable string, stackSizeLoads []string, stackSizes map
|
|||||||
if fn.stackSizeType == stacksize.Bounded {
|
if fn.stackSizeType == stacksize.Bounded {
|
||||||
stackSize := uint32(fn.stackSize)
|
stackSize := uint32(fn.stackSize)
|
||||||
|
|
||||||
// Adding 4 for the stack canary. Even though the size may be
|
|
||||||
// automatically determined, stack overflow checking is still
|
|
||||||
// important as the stack size cannot be determined for all
|
|
||||||
// goroutines.
|
|
||||||
stackSize += 4
|
|
||||||
|
|
||||||
// Add stack size used by interrupts.
|
// Add stack size used by interrupts.
|
||||||
switch fileHeader.Machine {
|
switch fileHeader.Machine {
|
||||||
case elf.EM_ARM:
|
case elf.EM_ARM:
|
||||||
|
if stackSize%8 != 0 {
|
||||||
|
// If the stack isn't a multiple of 8, it means the leaf
|
||||||
|
// function with the biggest stack depth doesn't have an aligned
|
||||||
|
// stack. If the STKALIGN flag is set (which it is by default)
|
||||||
|
// the interrupt controller will forcibly align the stack before
|
||||||
|
// storing in-use registers. This will thus overwrite one word
|
||||||
|
// past the end of the stack (off-by-one).
|
||||||
|
stackSize += 4
|
||||||
|
}
|
||||||
|
|
||||||
// On Cortex-M (assumed here), this stack size is 8 words or 32
|
// On Cortex-M (assumed here), this stack size is 8 words or 32
|
||||||
// bytes. This is only to store the registers that the interrupt
|
// bytes. This is only to store the registers that the interrupt
|
||||||
// may modify, the interrupt will switch to the interrupt stack
|
// may modify, the interrupt will switch to the interrupt stack
|
||||||
@@ -969,6 +975,14 @@ func modifyStackSizes(executable string, stackSizeLoads []string, stackSizes map
|
|||||||
// Some background:
|
// Some background:
|
||||||
// https://interrupt.memfault.com/blog/cortex-m-rtos-context-switching
|
// https://interrupt.memfault.com/blog/cortex-m-rtos-context-switching
|
||||||
stackSize += 32
|
stackSize += 32
|
||||||
|
|
||||||
|
// Adding 4 for the stack canary, and another 4 to keep the
|
||||||
|
// stack aligned. Even though the size may be automatically
|
||||||
|
// determined, stack overflow checking is still important as the
|
||||||
|
// stack size cannot be determined for all goroutines.
|
||||||
|
stackSize += 8
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("unknown architecture: %s", fileHeader.Machine.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Finally write the stack size to the binary.
|
// Finally write the stack size to the binary.
|
||||||
|
|||||||
@@ -693,6 +693,7 @@ func (c *compilerContext) getDIFile(filename string) llvm.Metadata {
|
|||||||
// createPackage builds the LLVM IR for all types, methods, and global variables
|
// createPackage builds the LLVM IR for all types, methods, and global variables
|
||||||
// in the given package.
|
// in the given package.
|
||||||
func (c *compilerContext) createPackage(irbuilder llvm.Builder, pkg *ssa.Package) {
|
func (c *compilerContext) createPackage(irbuilder llvm.Builder, pkg *ssa.Package) {
|
||||||
|
println("called create package:", pkg.Pkg.Name())
|
||||||
// Sort by position, so that the order of the functions in the IR matches
|
// Sort by position, so that the order of the functions in the IR matches
|
||||||
// the order of functions in the source file. This is useful for testing,
|
// the order of functions in the source file. This is useful for testing,
|
||||||
// for example.
|
// for example.
|
||||||
@@ -758,6 +759,35 @@ func (c *compilerContext) createPackage(irbuilder llvm.Builder, pkg *ssa.Package
|
|||||||
case *ssa.Global:
|
case *ssa.Global:
|
||||||
// Global variable.
|
// Global variable.
|
||||||
info := c.getGlobalInfo(member)
|
info := c.getGlobalInfo(member)
|
||||||
|
// process go:embed pragmas
|
||||||
|
// TODO: skip this if not Go 1.16 or higher
|
||||||
|
if strings.TrimSpace(info.embeds) != "" {
|
||||||
|
embeds, err := parseGoEmbed(info.embeds)
|
||||||
|
if err != nil {
|
||||||
|
c.addError(member.Pos(), `invalid go:embed pragma: `+err.Error())
|
||||||
|
goto getGlobal
|
||||||
|
}
|
||||||
|
importsEmbedPkg := false
|
||||||
|
for _, imprt := range pkg.Pkg.Imports() {
|
||||||
|
if imprt.Name() == "embed" {
|
||||||
|
importsEmbedPkg = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !importsEmbedPkg {
|
||||||
|
// FIXME: technically the "embed" package needs to be imported in every
|
||||||
|
// *source file* that has a //go:embed pragma... that will always be the
|
||||||
|
// case when referencing embed.FS but not necessarily when a go:embed
|
||||||
|
// pragma is added to a string or []byte. For now, as long as the embed
|
||||||
|
// package is imported in at least one source file, TinyGo will process
|
||||||
|
// any go:embed pragmas in the entire package.
|
||||||
|
c.addError(member.Pos(), `//go:embed only allowed in Go files that import "embed"`)
|
||||||
|
goto getGlobal
|
||||||
|
}
|
||||||
|
fmt.Printf("file embed found at %v (import: %v): %v\n", info.linkName, importsEmbedPkg, embeds)
|
||||||
|
}
|
||||||
|
|
||||||
|
getGlobal:
|
||||||
global := c.getGlobal(member)
|
global := c.getGlobal(member)
|
||||||
if !info.extern {
|
if !info.extern {
|
||||||
global.SetInitializer(llvm.ConstNull(global.Type().ElementType()))
|
global.SetInitializer(llvm.ConstNull(global.Type().ElementType()))
|
||||||
@@ -766,6 +796,7 @@ func (c *compilerContext) createPackage(irbuilder llvm.Builder, pkg *ssa.Package
|
|||||||
global.SetSection(info.section)
|
global.SetSection(info.section)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,70 @@
|
|||||||
|
package compiler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"unicode"
|
||||||
|
"unicode/utf8"
|
||||||
|
)
|
||||||
|
|
||||||
|
// parseGoEmbed parses the text following "//go:embed" to extract the glob patterns.
|
||||||
|
// It accepts unquoted space-separated patterns as well as double-quoted and back-quoted Go strings.
|
||||||
|
// Copied from https://github.com/golang/go/blob/219fe9d547d09d3de1b024c6c8b8314dd0bf12e4/src/cmd/compile/internal/noder/noder.go#L1717-L1776
|
||||||
|
func parseGoEmbed(args string) ([]string, error) {
|
||||||
|
var list []string
|
||||||
|
for args = strings.TrimSpace(args); args != ""; args = strings.TrimSpace(args) {
|
||||||
|
var path string
|
||||||
|
Switch:
|
||||||
|
switch args[0] {
|
||||||
|
default:
|
||||||
|
i := len(args)
|
||||||
|
for j, c := range args {
|
||||||
|
if unicode.IsSpace(c) {
|
||||||
|
i = j
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
path = args[:i]
|
||||||
|
args = args[i:]
|
||||||
|
|
||||||
|
case '`':
|
||||||
|
i := strings.Index(args[1:], "`")
|
||||||
|
if i < 0 {
|
||||||
|
return nil, fmt.Errorf("invalid quoted string in //go:embed: %s", args)
|
||||||
|
}
|
||||||
|
path = args[1 : 1+i]
|
||||||
|
args = args[1+i+1:]
|
||||||
|
|
||||||
|
case '"':
|
||||||
|
i := 1
|
||||||
|
for ; i < len(args); i++ {
|
||||||
|
if args[i] == '\\' {
|
||||||
|
i++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if args[i] == '"' {
|
||||||
|
q, err := strconv.Unquote(args[:i+1])
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("invalid quoted string in //go:embed: %s", args[:i+1])
|
||||||
|
}
|
||||||
|
path = q
|
||||||
|
args = args[i+1:]
|
||||||
|
break Switch
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if i >= len(args) {
|
||||||
|
return nil, fmt.Errorf("invalid quoted string in //go:embed: %s", args)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if args != "" {
|
||||||
|
r, _ := utf8.DecodeRuneInString(args)
|
||||||
|
if !unicode.IsSpace(r) {
|
||||||
|
return nil, fmt.Errorf("invalid quoted string in //go:embed: %s", args)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
list = append(list, path)
|
||||||
|
}
|
||||||
|
return list, nil
|
||||||
|
}
|
||||||
@@ -331,6 +331,7 @@ type globalInfo struct {
|
|||||||
extern bool // go:extern
|
extern bool // go:extern
|
||||||
align int // go:align
|
align int // go:align
|
||||||
section string // go:section
|
section string // go:section
|
||||||
|
embeds string // go:embed
|
||||||
}
|
}
|
||||||
|
|
||||||
// loadASTComments loads comments on globals from the AST, for use later in the
|
// loadASTComments loads comments on globals from the AST, for use later in the
|
||||||
@@ -448,6 +449,9 @@ func (info *globalInfo) parsePragmas(doc *ast.CommentGroup) {
|
|||||||
if len(parts) == 2 {
|
if len(parts) == 2 {
|
||||||
info.section = parts[1]
|
info.section = parts[1]
|
||||||
}
|
}
|
||||||
|
case "//go:embed":
|
||||||
|
raw := strings.TrimPrefix(comment.Text, "//go:embed")
|
||||||
|
info.embeds += " " + raw
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
module github.com/tinygo-org/tinygo
|
module github.com/tinygo-org/tinygo
|
||||||
|
|
||||||
go 1.13
|
go 1.16
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/blakesmith/ar v0.0.0-20150311145944-8bd4349a67f2
|
github.com/blakesmith/ar v0.0.0-20150311145944-8bd4349a67f2
|
||||||
|
|||||||
+1
-1
@@ -12,7 +12,7 @@ import (
|
|||||||
|
|
||||||
// Version of TinyGo.
|
// Version of TinyGo.
|
||||||
// Update this value before release of new version of software.
|
// Update this value before release of new version of software.
|
||||||
const Version = "0.19.0"
|
const Version = "0.20.0-dev"
|
||||||
|
|
||||||
// GetGorootVersion returns the major and minor version for a given GOROOT path.
|
// GetGorootVersion returns the major and minor version for a given GOROOT path.
|
||||||
// If the goroot cannot be determined, (0, 0) is returned.
|
// If the goroot cannot be determined, (0, 0) is returned.
|
||||||
|
|||||||
@@ -57,6 +57,7 @@ func (r *runner) errorAt(inst instruction, err error) *Error {
|
|||||||
pos := getPosition(inst.llvmInst)
|
pos := getPosition(inst.llvmInst)
|
||||||
return &Error{
|
return &Error{
|
||||||
ImportPath: r.pkgName,
|
ImportPath: r.pkgName,
|
||||||
|
Inst: inst.llvmInst,
|
||||||
Pos: pos,
|
Pos: pos,
|
||||||
Err: err,
|
Err: err,
|
||||||
Traceback: []ErrorLine{{pos, inst.llvmInst}},
|
Traceback: []ErrorLine{{pos, inst.llvmInst}},
|
||||||
|
|||||||
+27
-2
@@ -15,7 +15,7 @@ import (
|
|||||||
// package is changed in a way that affects the output so that cached package
|
// package is changed in a way that affects the output so that cached package
|
||||||
// builds will be invalidated.
|
// builds will be invalidated.
|
||||||
// This version is independent of the TinyGo version number.
|
// This version is independent of the TinyGo version number.
|
||||||
const Version = 1
|
const Version = 2 // last change: fix GEP on untyped pointers
|
||||||
|
|
||||||
// Enable extra checks, which should be disabled by default.
|
// Enable extra checks, which should be disabled by default.
|
||||||
// This may help track down bugs by adding a few more sanity checks.
|
// This may help track down bugs by adding a few more sanity checks.
|
||||||
@@ -110,17 +110,26 @@ func Run(mod llvm.Module, debug bool) error {
|
|||||||
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, " ")
|
||||||
|
call.EraseFromParentAsInstruction()
|
||||||
if callErr != nil {
|
if callErr != nil {
|
||||||
if isRecoverableError(callErr.Err) {
|
if isRecoverableError(callErr.Err) {
|
||||||
if r.debug {
|
if r.debug {
|
||||||
fmt.Fprintln(os.Stderr, "not interpreting", r.pkgName, "because of error:", callErr.Error())
|
fmt.Fprintln(os.Stderr, "not interpreting", r.pkgName, "because of error:", callErr.Error())
|
||||||
}
|
}
|
||||||
|
// Remove instructions that were created as part of interpreting
|
||||||
|
// the package.
|
||||||
mem.revert()
|
mem.revert()
|
||||||
|
// Create a call to the package initializer (which was
|
||||||
|
// previously deleted).
|
||||||
|
i8undef := llvm.Undef(r.i8ptrType)
|
||||||
|
r.builder.CreateCall(fn, []llvm.Value{i8undef, i8undef}, "")
|
||||||
|
// Make sure that any globals touched by the package
|
||||||
|
// initializer, won't be accessed by later package initializers.
|
||||||
|
r.markExternalLoad(fn)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
return callErr
|
return callErr
|
||||||
}
|
}
|
||||||
call.EraseFromParentAsInstruction()
|
|
||||||
for index, obj := range mem.objects {
|
for index, obj := range mem.objects {
|
||||||
r.objects[index] = obj
|
r.objects[index] = obj
|
||||||
}
|
}
|
||||||
@@ -270,3 +279,19 @@ func (r *runner) getFunction(llvmFn llvm.Value) *function {
|
|||||||
r.functionCache[llvmFn] = fn
|
r.functionCache[llvmFn] = fn
|
||||||
return fn
|
return fn
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// markExternalLoad marks the given llvmValue as being loaded externally. This
|
||||||
|
// is primarily used to mark package initializers that could not be run at
|
||||||
|
// compile time. As an example, a package initialize might store to a global
|
||||||
|
// variable. Another package initializer might read from the same global
|
||||||
|
// variable. By marking this function as being run at runtime, that load
|
||||||
|
// instruction will need to be run at runtime instead of at compile time.
|
||||||
|
func (r *runner) markExternalLoad(llvmValue llvm.Value) {
|
||||||
|
mem := memoryView{r: r}
|
||||||
|
mem.markExternalLoad(llvmValue)
|
||||||
|
for index, obj := range mem.objects {
|
||||||
|
if obj.marked > r.objects[index].marked {
|
||||||
|
r.objects[index].marked = obj.marked
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ func TestInterp(t *testing.T) {
|
|||||||
"slice-copy",
|
"slice-copy",
|
||||||
"consteval",
|
"consteval",
|
||||||
"interface",
|
"interface",
|
||||||
|
"revert",
|
||||||
} {
|
} {
|
||||||
name := name // make tc local to this closure
|
name := name // make tc local to this closure
|
||||||
t.Run(name, func(t *testing.T) {
|
t.Run(name, func(t *testing.T) {
|
||||||
|
|||||||
@@ -572,6 +572,17 @@ func (v pointerValue) toLLVMValue(llvmType llvm.Type, mem *memoryView) (llvm.Val
|
|||||||
}
|
}
|
||||||
|
|
||||||
if llvmType.IsNil() {
|
if llvmType.IsNil() {
|
||||||
|
if v.offset() != 0 {
|
||||||
|
// If there is an offset, make sure to use a GEP to index into the
|
||||||
|
// pointer. Because there is no expected type, we use whatever is
|
||||||
|
// most convenient: an *i8 type. It is trivial to index byte-wise.
|
||||||
|
if llvmValue.Type() != mem.r.i8ptrType {
|
||||||
|
llvmValue = llvm.ConstBitCast(llvmValue, mem.r.i8ptrType)
|
||||||
|
}
|
||||||
|
llvmValue = llvm.ConstInBoundsGEP(llvmValue, []llvm.Value{
|
||||||
|
llvm.ConstInt(llvmValue.Type().Context().Int32Type(), uint64(v.offset()), false),
|
||||||
|
})
|
||||||
|
}
|
||||||
return llvmValue, nil
|
return llvmValue, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Vendored
+32
@@ -0,0 +1,32 @@
|
|||||||
|
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
|
||||||
|
target triple = "x86_64--linux"
|
||||||
|
|
||||||
|
declare void @externalCall(i64)
|
||||||
|
|
||||||
|
@foo.knownAtRuntime = global i64 0
|
||||||
|
@bar.knownAtRuntime = global i64 0
|
||||||
|
|
||||||
|
define void @runtime.initAll() unnamed_addr {
|
||||||
|
entry:
|
||||||
|
call void @foo.init(i8* undef, i8* undef)
|
||||||
|
call void @bar.init(i8* undef, i8* undef)
|
||||||
|
call void @main.init(i8* undef, i8* undef)
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
define internal void @foo.init(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
|
store i64 5, i64* @foo.knownAtRuntime
|
||||||
|
unreachable ; this triggers a revert of @foo.init.
|
||||||
|
}
|
||||||
|
|
||||||
|
define internal void @bar.init(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
|
%val = load i64, i64* @foo.knownAtRuntime
|
||||||
|
store i64 %val, i64* @bar.knownAtRuntime
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
define internal void @main.init(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
|
entry:
|
||||||
|
call void @externalCall(i64 3)
|
||||||
|
ret void
|
||||||
|
}
|
||||||
Vendored
+21
@@ -0,0 +1,21 @@
|
|||||||
|
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
|
||||||
|
target triple = "x86_64--linux"
|
||||||
|
|
||||||
|
@foo.knownAtRuntime = local_unnamed_addr global i64 0
|
||||||
|
@bar.knownAtRuntime = local_unnamed_addr global i64 0
|
||||||
|
|
||||||
|
declare void @externalCall(i64) local_unnamed_addr
|
||||||
|
|
||||||
|
define void @runtime.initAll() unnamed_addr {
|
||||||
|
entry:
|
||||||
|
call fastcc void @foo.init(i8* undef, i8* undef)
|
||||||
|
%val = load i64, i64* @foo.knownAtRuntime, align 8
|
||||||
|
store i64 %val, i64* @bar.knownAtRuntime, align 8
|
||||||
|
call void @externalCall(i64 3)
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
define internal fastcc void @foo.init(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
|
store i64 5, i64* @foo.knownAtRuntime, align 8
|
||||||
|
unreachable
|
||||||
|
}
|
||||||
+13
-14
@@ -125,7 +125,7 @@ func TestCompiler(t *testing.T) {
|
|||||||
// Test with few optimizations enabled (no inlining, etc).
|
// Test with few optimizations enabled (no inlining, etc).
|
||||||
t.Run("opt=1", func(t *testing.T) {
|
t.Run("opt=1", func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
runTestWithConfig("stdlib.go", "", t, &compileopts.Options{
|
runTestWithConfig("stdlib.go", "", t, compileopts.Options{
|
||||||
Opt: "1",
|
Opt: "1",
|
||||||
}, nil, nil)
|
}, nil, nil)
|
||||||
})
|
})
|
||||||
@@ -134,15 +134,14 @@ func TestCompiler(t *testing.T) {
|
|||||||
// TODO: fix this for stdlib.go, which currently fails.
|
// TODO: fix this for stdlib.go, which currently fails.
|
||||||
t.Run("opt=0", func(t *testing.T) {
|
t.Run("opt=0", func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
runTestWithConfig("print.go", "", t, &compileopts.Options{
|
runTestWithConfig("print.go", "", t, compileopts.Options{
|
||||||
Opt: "0",
|
Opt: "0",
|
||||||
}, nil, nil)
|
}, nil, nil)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("ldflags", func(t *testing.T) {
|
t.Run("ldflags", func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
runTestWithConfig("ldflags.go", "", t, &compileopts.Options{
|
runTestWithConfig("ldflags.go", "", t, compileopts.Options{
|
||||||
Opt: "z",
|
|
||||||
GlobalValues: map[string]map[string]string{
|
GlobalValues: map[string]map[string]string{
|
||||||
"main": {
|
"main": {
|
||||||
"someGlobal": "foobar",
|
"someGlobal": "foobar",
|
||||||
@@ -188,20 +187,20 @@ func runBuild(src, out string, opts *compileopts.Options) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func runTest(name, target string, t *testing.T, cmdArgs, environmentVars []string) {
|
func runTest(name, target string, t *testing.T, cmdArgs, environmentVars []string) {
|
||||||
options := &compileopts.Options{
|
options := compileopts.Options{
|
||||||
Target: target,
|
Target: target,
|
||||||
Opt: "z",
|
|
||||||
PrintIR: false,
|
|
||||||
DumpSSA: false,
|
|
||||||
VerifyIR: true,
|
|
||||||
Debug: true,
|
|
||||||
PrintSizes: "",
|
|
||||||
WasmAbi: "",
|
|
||||||
}
|
}
|
||||||
runTestWithConfig(name, target, t, options, cmdArgs, environmentVars)
|
runTestWithConfig(name, target, t, options, cmdArgs, environmentVars)
|
||||||
}
|
}
|
||||||
|
|
||||||
func runTestWithConfig(name, target string, t *testing.T, options *compileopts.Options, cmdArgs, environmentVars []string) {
|
func runTestWithConfig(name, target string, t *testing.T, options compileopts.Options, cmdArgs, environmentVars []string) {
|
||||||
|
// Set default config.
|
||||||
|
options.Debug = true
|
||||||
|
options.VerifyIR = true
|
||||||
|
if options.Opt == "" {
|
||||||
|
options.Opt = "z"
|
||||||
|
}
|
||||||
|
|
||||||
// Get the expected output for this test.
|
// Get the expected output for this test.
|
||||||
// Note: not using filepath.Join as it strips the path separator at the end
|
// Note: not using filepath.Join as it strips the path separator at the end
|
||||||
// of the path.
|
// of the path.
|
||||||
@@ -230,7 +229,7 @@ func runTestWithConfig(name, target string, t *testing.T, options *compileopts.O
|
|||||||
|
|
||||||
// Build the test binary.
|
// Build the test binary.
|
||||||
binary := filepath.Join(tmpdir, "test")
|
binary := filepath.Join(tmpdir, "test")
|
||||||
err = runBuild("./"+path, binary, options)
|
err = runBuild("./"+path, binary, &options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
printCompilerError(t.Log, err)
|
printCompilerError(t.Log, err)
|
||||||
t.Fail()
|
t.Fail()
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"embed"
|
||||||
|
"log"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:embed file*.txt
|
||||||
|
//go:embed index.html styles.css
|
||||||
|
var files embed.FS
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
println(".....")
|
||||||
|
println(msg)
|
||||||
|
contents, err := files.ReadDir(".")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
for _, c := range contents {
|
||||||
|
println("file:", c.Name())
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
file 1
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
file 2
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
file 3
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
hello world!
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import _ "embed"
|
||||||
|
|
||||||
|
//go:embed message.txt
|
||||||
|
var msg string
|
||||||
@@ -7,3 +7,81 @@
|
|||||||
package net
|
package net
|
||||||
|
|
||||||
const hexDigit = "0123456789abcdef"
|
const hexDigit = "0123456789abcdef"
|
||||||
|
|
||||||
|
// A HardwareAddr represents a physical hardware address.
|
||||||
|
type HardwareAddr []byte
|
||||||
|
|
||||||
|
func (a HardwareAddr) String() string {
|
||||||
|
if len(a) == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
buf := make([]byte, 0, len(a)*3-1)
|
||||||
|
for i, b := range a {
|
||||||
|
if i > 0 {
|
||||||
|
buf = append(buf, ':')
|
||||||
|
}
|
||||||
|
buf = append(buf, hexDigit[b>>4])
|
||||||
|
buf = append(buf, hexDigit[b&0xF])
|
||||||
|
}
|
||||||
|
return string(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseMAC parses s as an IEEE 802 MAC-48, EUI-48, EUI-64, or a 20-octet
|
||||||
|
// IP over InfiniBand link-layer address using one of the following formats:
|
||||||
|
// 00:00:5e:00:53:01
|
||||||
|
// 02:00:5e:10:00:00:00:01
|
||||||
|
// 00:00:00:00:fe:80:00:00:00:00:00:00:02:00:5e:10:00:00:00:01
|
||||||
|
// 00-00-5e-00-53-01
|
||||||
|
// 02-00-5e-10-00-00-00-01
|
||||||
|
// 00-00-00-00-fe-80-00-00-00-00-00-00-02-00-5e-10-00-00-00-01
|
||||||
|
// 0000.5e00.5301
|
||||||
|
// 0200.5e10.0000.0001
|
||||||
|
// 0000.0000.fe80.0000.0000.0000.0200.5e10.0000.0001
|
||||||
|
func ParseMAC(s string) (hw HardwareAddr, err error) {
|
||||||
|
if len(s) < 14 {
|
||||||
|
goto err
|
||||||
|
}
|
||||||
|
|
||||||
|
if s[2] == ':' || s[2] == '-' {
|
||||||
|
if (len(s)+1)%3 != 0 {
|
||||||
|
goto err
|
||||||
|
}
|
||||||
|
n := (len(s) + 1) / 3
|
||||||
|
if n != 6 && n != 8 && n != 20 {
|
||||||
|
goto err
|
||||||
|
}
|
||||||
|
hw = make(HardwareAddr, n)
|
||||||
|
for x, i := 0, 0; i < n; i++ {
|
||||||
|
var ok bool
|
||||||
|
if hw[i], ok = xtoi2(s[x:], s[2]); !ok {
|
||||||
|
goto err
|
||||||
|
}
|
||||||
|
x += 3
|
||||||
|
}
|
||||||
|
} else if s[4] == '.' {
|
||||||
|
if (len(s)+1)%5 != 0 {
|
||||||
|
goto err
|
||||||
|
}
|
||||||
|
n := 2 * (len(s) + 1) / 5
|
||||||
|
if n != 6 && n != 8 && n != 20 {
|
||||||
|
goto err
|
||||||
|
}
|
||||||
|
hw = make(HardwareAddr, n)
|
||||||
|
for x, i := 0, 0; i < n; i += 2 {
|
||||||
|
var ok bool
|
||||||
|
if hw[i], ok = xtoi2(s[x:x+2], 0); !ok {
|
||||||
|
goto err
|
||||||
|
}
|
||||||
|
if hw[i+1], ok = xtoi2(s[x+2:], s[4]); !ok {
|
||||||
|
goto err
|
||||||
|
}
|
||||||
|
x += 5
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
goto err
|
||||||
|
}
|
||||||
|
return hw, nil
|
||||||
|
|
||||||
|
err:
|
||||||
|
return nil, &AddrError{Err: "invalid MAC address", Addr: s}
|
||||||
|
}
|
||||||
|
|||||||
@@ -52,6 +52,18 @@ func xtoi(s string) (n int, i int, ok bool) {
|
|||||||
return n, i, true
|
return n, i, true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// xtoi2 converts the next two hex digits of s into a byte.
|
||||||
|
// If s is longer than 2 bytes then the third byte must be e.
|
||||||
|
// If the first two bytes of s are not hex digits or the third byte
|
||||||
|
// does not match e, false is returned.
|
||||||
|
func xtoi2(s string, e byte) (byte, bool) {
|
||||||
|
if len(s) > 2 && s[2] != e {
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
n, ei, ok := xtoi(s[:2])
|
||||||
|
return byte(n), ok && ei == 2
|
||||||
|
}
|
||||||
|
|
||||||
// Convert unsigned integer to decimal string.
|
// Convert unsigned integer to decimal string.
|
||||||
func uitoa(val uint) string {
|
func uitoa(val uint) string {
|
||||||
if val == 0 { // avoid string allocation
|
if val == 0 { // avoid string allocation
|
||||||
|
|||||||
@@ -55,3 +55,51 @@ func growHeap() bool {
|
|||||||
// Heap has grown successfully.
|
// Heap has grown successfully.
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The below functions override the default allocator of wasi-libc.
|
||||||
|
// Most functions are defined but unimplemented to make sure that if there is
|
||||||
|
// any code using them, they will get an error instead of (incorrectly) using
|
||||||
|
// the wasi-libc dlmalloc heap implementation instead. If they are needed by any
|
||||||
|
// program, they can certainly be implemented.
|
||||||
|
|
||||||
|
//export malloc
|
||||||
|
func libc_malloc(size uintptr) unsafe.Pointer {
|
||||||
|
return alloc(size)
|
||||||
|
}
|
||||||
|
|
||||||
|
//export free
|
||||||
|
func libc_free(ptr unsafe.Pointer) {
|
||||||
|
free(ptr)
|
||||||
|
}
|
||||||
|
|
||||||
|
//export calloc
|
||||||
|
func libc_calloc(nmemb, size uintptr) unsafe.Pointer {
|
||||||
|
// Note: we could be even more correct here and check that nmemb * size
|
||||||
|
// doesn't overflow. However the current implementation should normally work
|
||||||
|
// fine.
|
||||||
|
return alloc(nmemb * size)
|
||||||
|
}
|
||||||
|
|
||||||
|
//export realloc
|
||||||
|
func libc_realloc(ptr unsafe.Pointer, size uintptr) unsafe.Pointer {
|
||||||
|
runtimePanic("unimplemented: realloc")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
//export posix_memalign
|
||||||
|
func libc_posix_memalign(memptr *unsafe.Pointer, alignment, size uintptr) int {
|
||||||
|
runtimePanic("unimplemented: posix_memalign")
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
//export aligned_alloc
|
||||||
|
func libc_aligned_alloc(alignment, bytes uintptr) unsafe.Pointer {
|
||||||
|
runtimePanic("unimplemented: aligned_alloc")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
//export malloc_usable_size
|
||||||
|
func libc_malloc_usable_size(ptr unsafe.Pointer) uintptr {
|
||||||
|
runtimePanic("unimplemented: malloc_usable_size")
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|||||||
Vendored
+15
@@ -13,6 +13,8 @@ func main() {
|
|||||||
println("v5:", len(v5), v5 == nil)
|
println("v5:", len(v5), v5 == nil)
|
||||||
println("v6:", v6)
|
println("v6:", v6)
|
||||||
println("v7:", cap(v7), string(v7))
|
println("v7:", cap(v7), string(v7))
|
||||||
|
println("v8:", v8)
|
||||||
|
println("v9:", len(v9), v9[0], v9[1], v9[2])
|
||||||
|
|
||||||
println(uint8SliceSrc[0])
|
println(uint8SliceSrc[0])
|
||||||
println(uint8SliceDst[0])
|
println(uint8SliceDst[0])
|
||||||
@@ -35,6 +37,8 @@ var (
|
|||||||
v5 = map[string]int{}
|
v5 = map[string]int{}
|
||||||
v6 = float64(v1) < 2.6
|
v6 = float64(v1) < 2.6
|
||||||
v7 = []byte("foo")
|
v7 = []byte("foo")
|
||||||
|
v8 string
|
||||||
|
v9 []int
|
||||||
|
|
||||||
uint8SliceSrc = []uint8{3, 100}
|
uint8SliceSrc = []uint8{3, 100}
|
||||||
uint8SliceDst []uint8
|
uint8SliceDst []uint8
|
||||||
@@ -48,4 +52,15 @@ func init() {
|
|||||||
|
|
||||||
intSliceDst = make([]int16, len(intSliceSrc))
|
intSliceDst = make([]int16, len(intSliceSrc))
|
||||||
copy(intSliceDst, intSliceSrc)
|
copy(intSliceDst, intSliceSrc)
|
||||||
|
|
||||||
|
v8 = sliceString("foobarbaz", 3, 8)
|
||||||
|
v9 = sliceSlice([]int{0, 1, 2, 3, 4, 5}, 2, 5)
|
||||||
|
}
|
||||||
|
|
||||||
|
func sliceString(s string, start, end int) string {
|
||||||
|
return s[start:end]
|
||||||
|
}
|
||||||
|
|
||||||
|
func sliceSlice(s []int, start, end int) []int {
|
||||||
|
return s[start:end]
|
||||||
}
|
}
|
||||||
|
|||||||
Vendored
+2
@@ -7,6 +7,8 @@ v4: 0 true
|
|||||||
v5: 0 false
|
v5: 0 false
|
||||||
v6: false
|
v6: false
|
||||||
v7: 3 foo
|
v7: 3 foo
|
||||||
|
v8: barba
|
||||||
|
v9: 3 2 3 4
|
||||||
3
|
3
|
||||||
3
|
3
|
||||||
5
|
5
|
||||||
|
|||||||
Reference in New Issue
Block a user