mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-06 20:13:40 +00:00
Compare commits
44 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 71fa131ef9 | |||
| eb0ce8a298 | |||
| 4ae4ef5e12 | |||
| d7460b945e | |||
| 638bc17eeb | |||
| 1113f9ec0c | |||
| 019331e8af | |||
| 4c8c048c49 | |||
| 08ee1916f5 | |||
| 141a70f401 | |||
| a79edf416c | |||
| 2511aefac0 | |||
| 4978065c9c | |||
| 78a26fec13 | |||
| 9cad8bd0c8 | |||
| 9a54ee4241 | |||
| 46d5ea8cf6 | |||
| fb952a722a | |||
| 7e46c1766d | |||
| 1f0595438e | |||
| d594342642 | |||
| 99da328453 | |||
| 387e1340bf | |||
| b1ed8a46b7 | |||
| 35af33ead7 | |||
| 4bd1b9e53d | |||
| 80ee343e6d | |||
| 1d59a960bc | |||
| 5ca2e1322c | |||
| 5b0b35f9e4 | |||
| 9a3d0683b3 | |||
| d155e31b64 | |||
| 45cacda7b3 | |||
| c25fe609a9 | |||
| 6d23809218 | |||
| 024eceb476 | |||
| 0fd90c49cc | |||
| d1efffe96b | |||
| 8e7ea92d44 | |||
| 2f2d62cc0c | |||
| d396abb690 | |||
| b815d3f760 | |||
| 9c46ac4eed | |||
| b2e96fc35a |
+18
-15
@@ -47,6 +47,18 @@ commands:
|
|||||||
command: |
|
command: |
|
||||||
curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh
|
curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh
|
||||||
dep ensure --vendor-only
|
dep ensure --vendor-only
|
||||||
|
llvm-source-linux:
|
||||||
|
steps:
|
||||||
|
- restore_cache:
|
||||||
|
keys:
|
||||||
|
- llvm-source-8-v2
|
||||||
|
- run:
|
||||||
|
name: "Fetch LLVM source"
|
||||||
|
command: make llvm-source
|
||||||
|
- save_cache:
|
||||||
|
key: llvm-source-8-v2
|
||||||
|
paths:
|
||||||
|
- llvm
|
||||||
smoketest:
|
smoketest:
|
||||||
steps:
|
steps:
|
||||||
- smoketest-no-avr
|
- smoketest-no-avr
|
||||||
@@ -86,6 +98,7 @@ commands:
|
|||||||
keys:
|
keys:
|
||||||
- go-cache-{{ checksum "Gopkg.lock" }}-{{ .Environment.CIRCLE_PREVIOUS_BUILD_NUM }}
|
- go-cache-{{ checksum "Gopkg.lock" }}-{{ .Environment.CIRCLE_PREVIOUS_BUILD_NUM }}
|
||||||
- go-cache-{{ checksum "Gopkg.lock" }}
|
- go-cache-{{ checksum "Gopkg.lock" }}
|
||||||
|
- llvm-source-linux
|
||||||
- dep
|
- dep
|
||||||
- run: go install .
|
- run: go install .
|
||||||
- run: go test -v
|
- run: go test -v
|
||||||
@@ -105,7 +118,6 @@ commands:
|
|||||||
name: "Install apt dependencies"
|
name: "Install apt dependencies"
|
||||||
command: |
|
command: |
|
||||||
sudo apt-get install \
|
sudo apt-get install \
|
||||||
libtinfo-dev \
|
|
||||||
python3 \
|
python3 \
|
||||||
gcc-arm-linux-gnueabihf \
|
gcc-arm-linux-gnueabihf \
|
||||||
binutils-arm-none-eabi \
|
binutils-arm-none-eabi \
|
||||||
@@ -121,19 +133,10 @@ commands:
|
|||||||
keys:
|
keys:
|
||||||
- go-cache-{{ checksum "Gopkg.lock" }}-{{ .Environment.CIRCLE_PREVIOUS_BUILD_NUM }}
|
- go-cache-{{ checksum "Gopkg.lock" }}-{{ .Environment.CIRCLE_PREVIOUS_BUILD_NUM }}
|
||||||
- go-cache-{{ checksum "Gopkg.lock" }}
|
- go-cache-{{ checksum "Gopkg.lock" }}
|
||||||
|
- llvm-source-linux
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
keys:
|
keys:
|
||||||
- llvm-source-8-v2
|
- llvm-build-8-linux-v4
|
||||||
- run:
|
|
||||||
name: "Fetch LLVM source"
|
|
||||||
command: make llvm-source
|
|
||||||
- save_cache:
|
|
||||||
key: llvm-source-8-v2
|
|
||||||
paths:
|
|
||||||
- llvm
|
|
||||||
- restore_cache:
|
|
||||||
keys:
|
|
||||||
- llvm-build-8-v2
|
|
||||||
- run:
|
- run:
|
||||||
name: "Build LLVM"
|
name: "Build LLVM"
|
||||||
command: |
|
command: |
|
||||||
@@ -151,7 +154,7 @@ commands:
|
|||||||
make llvm-build
|
make llvm-build
|
||||||
fi
|
fi
|
||||||
- save_cache:
|
- save_cache:
|
||||||
key: llvm-build-8-v2
|
key: llvm-build-8-linux-v4
|
||||||
paths:
|
paths:
|
||||||
llvm-build
|
llvm-build
|
||||||
- run:
|
- run:
|
||||||
@@ -204,7 +207,7 @@ commands:
|
|||||||
- llvm
|
- llvm
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
keys:
|
keys:
|
||||||
- llvm-build-8-macos-v2
|
- llvm-build-8-macos-v3
|
||||||
- run:
|
- run:
|
||||||
name: "Build LLVM"
|
name: "Build LLVM"
|
||||||
command: |
|
command: |
|
||||||
@@ -216,7 +219,7 @@ commands:
|
|||||||
make llvm-build
|
make llvm-build
|
||||||
fi
|
fi
|
||||||
- save_cache:
|
- save_cache:
|
||||||
key: llvm-build-8-macos-v2
|
key: llvm-build-8-macos-v3
|
||||||
paths:
|
paths:
|
||||||
llvm-build
|
llvm-build
|
||||||
- run:
|
- run:
|
||||||
|
|||||||
@@ -10,3 +10,5 @@ src/device/stm32/*.s
|
|||||||
src/device/sam/*.go
|
src/device/sam/*.go
|
||||||
src/device/sam/*.s
|
src/device/sam/*.s
|
||||||
vendor
|
vendor
|
||||||
|
llvm
|
||||||
|
llvm-build
|
||||||
|
|||||||
+4
-1
@@ -10,8 +10,11 @@ RUN wget -O- https://raw.githubusercontent.com/golang/dep/master/install.sh | sh
|
|||||||
|
|
||||||
COPY . /go/src/github.com/tinygo-org/tinygo
|
COPY . /go/src/github.com/tinygo-org/tinygo
|
||||||
|
|
||||||
|
# remove submodules directories and re-init them to fix any hard-coded paths
|
||||||
|
# after copying the tinygo directory in the previous step.
|
||||||
RUN cd /go/src/github.com/tinygo-org/tinygo/ && \
|
RUN cd /go/src/github.com/tinygo-org/tinygo/ && \
|
||||||
git submodule update --init
|
rm -rf ./lib/* && \
|
||||||
|
git submodule update --init --recursive --force
|
||||||
|
|
||||||
RUN cd /go/src/github.com/tinygo-org/tinygo/ && \
|
RUN cd /go/src/github.com/tinygo-org/tinygo/ && \
|
||||||
dep ensure --vendor-only && \
|
dep ensure --vendor-only && \
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
Copyright (c) 2018-2019 TinyGo Authors. All rights reserved.
|
Copyright (c) 2018-2019 TinyGo Authors. All rights reserved.
|
||||||
|
|
||||||
|
TinyGo includes portions of the Go standard library.
|
||||||
|
Copyright (c) 2009-2019 The Go Authors. All rights reserved.
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
Redistribution and use in source and binary forms, with or without
|
||||||
modification, are permitted provided that the following conditions are
|
modification, are permitted provided that the following conditions are
|
||||||
met:
|
met:
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ llvm-source: llvm/README.txt llvm/tools/clang/README.txt llvm/tools/lld/README.m
|
|||||||
|
|
||||||
# Configure LLVM.
|
# Configure LLVM.
|
||||||
llvm-build/build.ninja: llvm-source
|
llvm-build/build.ninja: llvm-source
|
||||||
mkdir -p llvm-build; cd llvm-build; cmake -G Ninja ../llvm "-DLLVM_TARGETS_TO_BUILD=X86;ARM;AArch64;WebAssembly" "-DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD=AVR" -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_ASSERTIONS=OFF -DLIBCLANG_BUILD_STATIC=ON
|
mkdir -p llvm-build; cd llvm-build; cmake -G Ninja ../llvm "-DLLVM_TARGETS_TO_BUILD=X86;ARM;AArch64;WebAssembly" "-DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD=AVR" -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_ASSERTIONS=OFF -DLIBCLANG_BUILD_STATIC=ON -DLLVM_ENABLE_TERMINFO=OFF -DLLVM_ENABLE_ZLIB=OFF
|
||||||
|
|
||||||
# Build LLVM.
|
# Build LLVM.
|
||||||
llvm-build: llvm-build/build.ninja
|
llvm-build: llvm-build/build.ninja
|
||||||
@@ -86,13 +86,16 @@ test:
|
|||||||
|
|
||||||
release: build/tinygo gen-device
|
release: build/tinygo gen-device
|
||||||
@mkdir -p build/release/tinygo/bin
|
@mkdir -p build/release/tinygo/bin
|
||||||
|
@mkdir -p build/release/tinygo/lib/clang/include
|
||||||
@mkdir -p build/release/tinygo/lib/CMSIS/CMSIS
|
@mkdir -p build/release/tinygo/lib/CMSIS/CMSIS
|
||||||
@mkdir -p build/release/tinygo/lib/compiler-rt/lib
|
@mkdir -p build/release/tinygo/lib/compiler-rt/lib
|
||||||
@mkdir -p build/release/tinygo/lib/nrfx
|
@mkdir -p build/release/tinygo/lib/nrfx
|
||||||
@mkdir -p build/release/tinygo/pkg/armv6m-none-eabi
|
@mkdir -p build/release/tinygo/pkg/armv6m-none-eabi
|
||||||
@mkdir -p build/release/tinygo/pkg/armv7m-none-eabi
|
@mkdir -p build/release/tinygo/pkg/armv7m-none-eabi
|
||||||
@mkdir -p build/release/tinygo/pkg/armv7em-none-eabi
|
@mkdir -p build/release/tinygo/pkg/armv7em-none-eabi
|
||||||
|
@echo copying source files
|
||||||
@cp -p build/tinygo build/release/tinygo/bin
|
@cp -p build/tinygo build/release/tinygo/bin
|
||||||
|
@cp -p $(abspath $(CLANG_SRC))/lib/Headers/*.h build/release/tinygo/lib/clang/include
|
||||||
@cp -rp lib/CMSIS/CMSIS/Include build/release/tinygo/lib/CMSIS/CMSIS
|
@cp -rp lib/CMSIS/CMSIS/Include build/release/tinygo/lib/CMSIS/CMSIS
|
||||||
@cp -rp lib/CMSIS/README.md build/release/tinygo/lib/CMSIS
|
@cp -rp lib/CMSIS/README.md build/release/tinygo/lib/CMSIS
|
||||||
@cp -rp lib/compiler-rt/lib/builtins build/release/tinygo/lib/compiler-rt/lib
|
@cp -rp lib/compiler-rt/lib/builtins build/release/tinygo/lib/compiler-rt/lib
|
||||||
|
|||||||
+5
-3
@@ -9,9 +9,11 @@ import (
|
|||||||
|
|
||||||
// Get the cache directory, usually ~/.cache/tinygo
|
// Get the cache directory, usually ~/.cache/tinygo
|
||||||
func cacheDir() string {
|
func cacheDir() string {
|
||||||
home := getHomeDir()
|
dir, err := os.UserCacheDir()
|
||||||
dir := filepath.Join(home, ".cache", "tinygo")
|
if err != nil {
|
||||||
return dir
|
panic("could not find cache dir: " + err.Error())
|
||||||
|
}
|
||||||
|
return filepath.Join(dir, "tinygo")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return the newest timestamp of all the file paths passed in. Used to check
|
// Return the newest timestamp of all the file paths passed in. Used to check
|
||||||
|
|||||||
+4
-8
@@ -5,7 +5,6 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
@@ -192,13 +191,13 @@ func loadBuiltins(target string) (path string, err error) {
|
|||||||
srcs[i] = filepath.Join(builtinsDir, name)
|
srcs[i] = filepath.Join(builtinsDir, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
if path, err := cacheLoad(outfile, commands["clang"], srcs); path != "" || err != nil {
|
if path, err := cacheLoad(outfile, commands["clang"][0], srcs); path != "" || err != nil {
|
||||||
return path, err
|
return path, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var cachepath string
|
var cachepath string
|
||||||
err = compileBuiltins(target, func(path string) error {
|
err = compileBuiltins(target, func(path string) error {
|
||||||
path, err := cacheStore(path, outfile, commands["clang"], srcs)
|
path, err := cacheStore(path, outfile, commands["clang"][0], srcs)
|
||||||
cachepath = path
|
cachepath = path
|
||||||
return err
|
return err
|
||||||
})
|
})
|
||||||
@@ -240,11 +239,7 @@ func compileBuiltins(target string, callback func(path string) error) error {
|
|||||||
// Note: -fdebug-prefix-map is necessary to make the output archive
|
// Note: -fdebug-prefix-map is necessary to make the output archive
|
||||||
// reproducible. Otherwise the temporary directory is stored in the
|
// reproducible. Otherwise the temporary directory is stored in the
|
||||||
// archive itself, which varies each run.
|
// archive itself, which varies each run.
|
||||||
cmd := exec.Command(commands["clang"], "-c", "-Oz", "-g", "-Werror", "-Wall", "-std=c11", "-fshort-enums", "-nostdlibinc", "-ffunction-sections", "-fdata-sections", "--target="+target, "-fdebug-prefix-map="+dir+"="+remapDir, "-o", objpath, srcpath)
|
err := execCommand(commands["clang"], "-c", "-Oz", "-g", "-Werror", "-Wall", "-std=c11", "-fshort-enums", "-nostdlibinc", "-ffunction-sections", "-fdata-sections", "--target="+target, "-fdebug-prefix-map="+dir+"="+remapDir, "-o", objpath, srcpath)
|
||||||
cmd.Stdout = os.Stdout
|
|
||||||
cmd.Stderr = os.Stderr
|
|
||||||
cmd.Dir = dir
|
|
||||||
err = cmd.Run()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &commandError{"failed to build", srcpath, err}
|
return &commandError{"failed to build", srcpath, err}
|
||||||
}
|
}
|
||||||
@@ -294,5 +289,6 @@ func compileBuiltins(target string, callback func(path string) error) error {
|
|||||||
|
|
||||||
// Give the caller the resulting file. The callback must copy the file,
|
// Give the caller the resulting file. The callback must copy the file,
|
||||||
// because after it returns the temporary directory will be removed.
|
// because after it returns the temporary directory will be removed.
|
||||||
|
arfile.Close()
|
||||||
return callback(arpath)
|
return callback(arpath)
|
||||||
}
|
}
|
||||||
|
|||||||
+30
-7
@@ -1,10 +1,33 @@
|
|||||||
// +build !darwin
|
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
// commands used by the compilation process might have different file names on Linux than those used on macOS.
|
import (
|
||||||
var commands = map[string]string{
|
"errors"
|
||||||
"clang": "clang-8",
|
"os"
|
||||||
"ld.lld": "ld.lld-8",
|
"os/exec"
|
||||||
"wasm-ld": "wasm-ld-8",
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Commands used by the compilation process might have different file names
|
||||||
|
// across operating systems and distributions.
|
||||||
|
var commands = map[string][]string{
|
||||||
|
"clang": {"clang-8"},
|
||||||
|
"ld.lld": {"ld.lld-8", "ld.lld"},
|
||||||
|
"wasm-ld": {"wasm-ld-8", "wasm-ld"},
|
||||||
|
}
|
||||||
|
|
||||||
|
func execCommand(cmdNames []string, args ...string) error {
|
||||||
|
for _, cmdName := range cmdNames {
|
||||||
|
cmd := exec.Command(cmdName, args...)
|
||||||
|
cmd.Stdout = os.Stdout
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
err := cmd.Run()
|
||||||
|
if err != nil {
|
||||||
|
if err, ok := err.(*exec.Error); ok && err.Err == exec.ErrNotFound {
|
||||||
|
// this command was not found, try the next
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return errors.New("none of these commands were found in your $PATH: " + strings.Join(cmdNames, " "))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +0,0 @@
|
|||||||
// +build darwin
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
// commands used by the compilation process might have different file names on macOS than those used on Linux.
|
|
||||||
var commands = map[string]string{
|
|
||||||
"clang": "clang-8",
|
|
||||||
"ld.lld": "ld.lld",
|
|
||||||
"wasm-ld": "wasm-ld",
|
|
||||||
}
|
|
||||||
+1
-4
@@ -123,10 +123,7 @@ func (c *Compiler) collapseFormalParamInternal(t llvm.Type, fields []llvm.Value)
|
|||||||
switch t.TypeKind() {
|
switch t.TypeKind() {
|
||||||
case llvm.StructTypeKind:
|
case llvm.StructTypeKind:
|
||||||
if len(c.flattenAggregateType(t)) <= MaxFieldsPerParam {
|
if len(c.flattenAggregateType(t)) <= MaxFieldsPerParam {
|
||||||
value, err := c.getZeroValue(t)
|
value := c.getZeroValue(t)
|
||||||
if err != nil {
|
|
||||||
panic("could not get zero value of struct: " + err.Error())
|
|
||||||
}
|
|
||||||
for i, subtyp := range t.StructElementTypes() {
|
for i, subtyp := range t.StructElementTypes() {
|
||||||
structField, remaining := c.collapseFormalParamInternal(subtyp, fields)
|
structField, remaining := c.collapseFormalParamInternal(subtyp, fields)
|
||||||
fields = remaining
|
fields = remaining
|
||||||
|
|||||||
+37
-49
@@ -12,16 +12,6 @@ import (
|
|||||||
|
|
||||||
// emitMakeChan returns a new channel value for the given channel type.
|
// emitMakeChan returns a new channel value for the given channel type.
|
||||||
func (c *Compiler) emitMakeChan(expr *ssa.MakeChan) (llvm.Value, error) {
|
func (c *Compiler) emitMakeChan(expr *ssa.MakeChan) (llvm.Value, error) {
|
||||||
valueType, err := c.getLLVMType(expr.Type().(*types.Chan).Elem())
|
|
||||||
if err != nil {
|
|
||||||
return llvm.Value{}, err
|
|
||||||
}
|
|
||||||
if c.targetData.TypeAllocSize(valueType) > c.targetData.TypeAllocSize(c.intType) {
|
|
||||||
// Values bigger than int overflow the data part of the coroutine.
|
|
||||||
// TODO: make the coroutine data part big enough to hold these bigger
|
|
||||||
// values.
|
|
||||||
return llvm.Value{}, c.makeError(expr.Pos(), "todo: channel with values bigger than int")
|
|
||||||
}
|
|
||||||
chanType := c.mod.GetTypeByName("runtime.channel")
|
chanType := c.mod.GetTypeByName("runtime.channel")
|
||||||
size := c.targetData.TypeAllocSize(chanType)
|
size := c.targetData.TypeAllocSize(chanType)
|
||||||
sizeValue := llvm.ConstInt(c.uintptrType, size, false)
|
sizeValue := llvm.ConstInt(c.uintptrType, size, false)
|
||||||
@@ -32,66 +22,64 @@ func (c *Compiler) emitMakeChan(expr *ssa.MakeChan) (llvm.Value, error) {
|
|||||||
|
|
||||||
// emitChanSend emits a pseudo chan send operation. It is lowered to the actual
|
// emitChanSend emits a pseudo chan send operation. It is lowered to the actual
|
||||||
// channel send operation during goroutine lowering.
|
// channel send operation during goroutine lowering.
|
||||||
func (c *Compiler) emitChanSend(frame *Frame, instr *ssa.Send) error {
|
func (c *Compiler) emitChanSend(frame *Frame, instr *ssa.Send) {
|
||||||
valueType, err := c.getLLVMType(instr.Chan.Type().(*types.Chan).Elem())
|
valueType := c.getLLVMType(instr.X.Type())
|
||||||
if err != nil {
|
ch := c.getValue(frame, instr.Chan)
|
||||||
return err
|
chanValue := c.getValue(frame, instr.X)
|
||||||
}
|
|
||||||
ch, err := c.parseExpr(frame, instr.Chan)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
chanValue, err := c.parseExpr(frame, instr.X)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
valueSize := llvm.ConstInt(c.uintptrType, c.targetData.TypeAllocSize(chanValue.Type()), false)
|
valueSize := llvm.ConstInt(c.uintptrType, c.targetData.TypeAllocSize(chanValue.Type()), false)
|
||||||
|
coroutine := c.createRuntimeCall("getCoroutine", nil, "")
|
||||||
|
|
||||||
|
// store value-to-send
|
||||||
|
c.builder.SetInsertPointBefore(coroutine.InstructionParent().Parent().EntryBasicBlock().FirstInstruction())
|
||||||
valueAlloca := c.builder.CreateAlloca(valueType, "chan.value")
|
valueAlloca := c.builder.CreateAlloca(valueType, "chan.value")
|
||||||
|
c.builder.SetInsertPointBefore(coroutine)
|
||||||
|
c.builder.SetInsertPointAtEnd(coroutine.InstructionParent())
|
||||||
c.builder.CreateStore(chanValue, valueAlloca)
|
c.builder.CreateStore(chanValue, valueAlloca)
|
||||||
valueAllocaCast := c.builder.CreateBitCast(valueAlloca, c.i8ptrType, "chan.value.i8ptr")
|
valueAllocaCast := c.builder.CreateBitCast(valueAlloca, c.i8ptrType, "chan.value.i8ptr")
|
||||||
c.createRuntimeCall("chanSendStub", []llvm.Value{llvm.Undef(c.i8ptrType), ch, valueAllocaCast, valueSize}, "")
|
|
||||||
return nil
|
// Do the send.
|
||||||
|
c.createRuntimeCall("chanSend", []llvm.Value{coroutine, ch, valueAllocaCast, valueSize}, "")
|
||||||
|
|
||||||
|
// Make sure CoroSplit includes the alloca in the coroutine frame.
|
||||||
|
// This is a bit dirty, but it works (at least in LLVM 8).
|
||||||
|
valueSizeI64 := llvm.ConstInt(c.ctx.Int64Type(), c.targetData.TypeAllocSize(chanValue.Type()), false)
|
||||||
|
c.builder.CreateCall(c.getLifetimeEndFunc(), []llvm.Value{valueSizeI64, valueAllocaCast}, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
// emitChanRecv emits a pseudo chan receive operation. It is lowered to the
|
// emitChanRecv emits a pseudo chan receive operation. It is lowered to the
|
||||||
// actual channel receive operation during goroutine lowering.
|
// actual channel receive operation during goroutine lowering.
|
||||||
func (c *Compiler) emitChanRecv(frame *Frame, unop *ssa.UnOp) (llvm.Value, error) {
|
func (c *Compiler) emitChanRecv(frame *Frame, unop *ssa.UnOp) llvm.Value {
|
||||||
valueType, err := c.getLLVMType(unop.X.Type().(*types.Chan).Elem())
|
valueType := c.getLLVMType(unop.X.Type().(*types.Chan).Elem())
|
||||||
if err != nil {
|
|
||||||
return llvm.Value{}, err
|
|
||||||
}
|
|
||||||
valueSize := llvm.ConstInt(c.uintptrType, c.targetData.TypeAllocSize(valueType), false)
|
valueSize := llvm.ConstInt(c.uintptrType, c.targetData.TypeAllocSize(valueType), false)
|
||||||
ch, err := c.parseExpr(frame, unop.X)
|
ch := c.getValue(frame, unop.X)
|
||||||
if err != nil {
|
coroutine := c.createRuntimeCall("getCoroutine", nil, "")
|
||||||
return llvm.Value{}, err
|
|
||||||
}
|
// Allocate memory to receive into.
|
||||||
|
c.builder.SetInsertPointBefore(coroutine.InstructionParent().Parent().EntryBasicBlock().FirstInstruction())
|
||||||
valueAlloca := c.builder.CreateAlloca(valueType, "chan.value")
|
valueAlloca := c.builder.CreateAlloca(valueType, "chan.value")
|
||||||
|
c.builder.SetInsertPointBefore(coroutine)
|
||||||
|
c.builder.SetInsertPointAtEnd(coroutine.InstructionParent())
|
||||||
valueAllocaCast := c.builder.CreateBitCast(valueAlloca, c.i8ptrType, "chan.value.i8ptr")
|
valueAllocaCast := c.builder.CreateBitCast(valueAlloca, c.i8ptrType, "chan.value.i8ptr")
|
||||||
valueOk := c.builder.CreateAlloca(c.ctx.Int1Type(), "chan.comma-ok.alloca")
|
|
||||||
c.createRuntimeCall("chanRecvStub", []llvm.Value{llvm.Undef(c.i8ptrType), ch, valueAllocaCast, valueOk, valueSize}, "")
|
// Do the receive.
|
||||||
|
c.createRuntimeCall("chanRecv", []llvm.Value{coroutine, ch, valueAllocaCast, valueSize}, "")
|
||||||
received := c.builder.CreateLoad(valueAlloca, "chan.received")
|
received := c.builder.CreateLoad(valueAlloca, "chan.received")
|
||||||
if unop.CommaOk {
|
if unop.CommaOk {
|
||||||
commaOk := c.builder.CreateLoad(valueOk, "chan.comma-ok")
|
commaOk := c.createRuntimeCall("getTaskPromiseData", []llvm.Value{coroutine}, "chan.commaOk.wide")
|
||||||
|
commaOk = c.builder.CreateTrunc(commaOk, c.ctx.Int1Type(), "chan.commaOk")
|
||||||
tuple := llvm.Undef(c.ctx.StructType([]llvm.Type{valueType, c.ctx.Int1Type()}, false))
|
tuple := llvm.Undef(c.ctx.StructType([]llvm.Type{valueType, c.ctx.Int1Type()}, false))
|
||||||
tuple = c.builder.CreateInsertValue(tuple, received, 0, "")
|
tuple = c.builder.CreateInsertValue(tuple, received, 0, "")
|
||||||
tuple = c.builder.CreateInsertValue(tuple, commaOk, 1, "")
|
tuple = c.builder.CreateInsertValue(tuple, commaOk, 1, "")
|
||||||
return tuple, nil
|
return tuple
|
||||||
} else {
|
} else {
|
||||||
return received, nil
|
return received
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// emitChanClose closes the given channel.
|
// emitChanClose closes the given channel.
|
||||||
func (c *Compiler) emitChanClose(frame *Frame, param ssa.Value) error {
|
func (c *Compiler) emitChanClose(frame *Frame, param ssa.Value) {
|
||||||
valueType, err := c.getLLVMType(param.Type().(*types.Chan).Elem())
|
valueType := c.getLLVMType(param.Type().(*types.Chan).Elem())
|
||||||
valueSize := llvm.ConstInt(c.uintptrType, c.targetData.TypeAllocSize(valueType), false)
|
valueSize := llvm.ConstInt(c.uintptrType, c.targetData.TypeAllocSize(valueType), false)
|
||||||
if err != nil {
|
ch := c.getValue(frame, param)
|
||||||
return err
|
|
||||||
}
|
|
||||||
ch, err := c.parseExpr(frame, param)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
c.createRuntimeCall("chanClose", []llvm.Value{ch, valueSize}, "")
|
c.createRuntimeCall("chanClose", []llvm.Value{ch, valueSize}, "")
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|||||||
+348
-601
File diff suppressed because it is too large
Load Diff
+19
-53
@@ -36,7 +36,7 @@ func (c *Compiler) deferInitFunc(frame *Frame) {
|
|||||||
|
|
||||||
// emitDefer emits a single defer instruction, to be run when this function
|
// emitDefer emits a single defer instruction, to be run when this function
|
||||||
// returns.
|
// returns.
|
||||||
func (c *Compiler) emitDefer(frame *Frame, instr *ssa.Defer) error {
|
func (c *Compiler) emitDefer(frame *Frame, instr *ssa.Defer) {
|
||||||
// The pointer to the previous defer struct, which we will replace to
|
// The pointer to the previous defer struct, which we will replace to
|
||||||
// make a linked list.
|
// make a linked list.
|
||||||
next := c.builder.CreateLoad(frame.deferPtr, "defer.next")
|
next := c.builder.CreateLoad(frame.deferPtr, "defer.next")
|
||||||
@@ -56,18 +56,12 @@ func (c *Compiler) emitDefer(frame *Frame, instr *ssa.Defer) error {
|
|||||||
|
|
||||||
// Collect all values to be put in the struct (starting with
|
// Collect all values to be put in the struct (starting with
|
||||||
// runtime._defer fields, followed by the call parameters).
|
// runtime._defer fields, followed by the call parameters).
|
||||||
itf, err := c.parseExpr(frame, instr.Call.Value) // interface
|
itf := c.getValue(frame, instr.Call.Value) // interface
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
receiverValue := c.builder.CreateExtractValue(itf, 1, "invoke.func.receiver")
|
receiverValue := c.builder.CreateExtractValue(itf, 1, "invoke.func.receiver")
|
||||||
values = []llvm.Value{callback, next, receiverValue}
|
values = []llvm.Value{callback, next, receiverValue}
|
||||||
valueTypes = append(valueTypes, c.i8ptrType)
|
valueTypes = append(valueTypes, c.i8ptrType)
|
||||||
for _, arg := range instr.Call.Args {
|
for _, arg := range instr.Call.Args {
|
||||||
val, err := c.parseExpr(frame, arg)
|
val := c.getValue(frame, arg)
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
values = append(values, val)
|
values = append(values, val)
|
||||||
valueTypes = append(valueTypes, val.Type())
|
valueTypes = append(valueTypes, val.Type())
|
||||||
}
|
}
|
||||||
@@ -86,10 +80,7 @@ func (c *Compiler) emitDefer(frame *Frame, instr *ssa.Defer) error {
|
|||||||
// runtime._defer fields).
|
// runtime._defer fields).
|
||||||
values = []llvm.Value{callback, next}
|
values = []llvm.Value{callback, next}
|
||||||
for _, param := range instr.Call.Args {
|
for _, param := range instr.Call.Args {
|
||||||
llvmParam, err := c.parseExpr(frame, param)
|
llvmParam := c.getValue(frame, param)
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
values = append(values, llvmParam)
|
values = append(values, llvmParam)
|
||||||
valueTypes = append(valueTypes, llvmParam.Type())
|
valueTypes = append(valueTypes, llvmParam.Type())
|
||||||
}
|
}
|
||||||
@@ -101,10 +92,7 @@ func (c *Compiler) emitDefer(frame *Frame, instr *ssa.Defer) error {
|
|||||||
// pointer.
|
// pointer.
|
||||||
// TODO: ignore this closure entirely and put pointers to the free
|
// TODO: ignore this closure entirely and put pointers to the free
|
||||||
// variables directly in the defer struct, avoiding a memory allocation.
|
// variables directly in the defer struct, avoiding a memory allocation.
|
||||||
closure, err := c.parseExpr(frame, instr.Call.Value)
|
closure := c.getValue(frame, instr.Call.Value)
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
context := c.builder.CreateExtractValue(closure, 0, "")
|
context := c.builder.CreateExtractValue(closure, 0, "")
|
||||||
|
|
||||||
// Get the callback number.
|
// Get the callback number.
|
||||||
@@ -120,10 +108,7 @@ func (c *Compiler) emitDefer(frame *Frame, instr *ssa.Defer) error {
|
|||||||
// context pointer).
|
// context pointer).
|
||||||
values = []llvm.Value{callback, next}
|
values = []llvm.Value{callback, next}
|
||||||
for _, param := range instr.Call.Args {
|
for _, param := range instr.Call.Args {
|
||||||
llvmParam, err := c.parseExpr(frame, param)
|
llvmParam := c.getValue(frame, param)
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
values = append(values, llvmParam)
|
values = append(values, llvmParam)
|
||||||
valueTypes = append(valueTypes, llvmParam.Type())
|
valueTypes = append(valueTypes, llvmParam.Type())
|
||||||
}
|
}
|
||||||
@@ -131,15 +116,13 @@ func (c *Compiler) emitDefer(frame *Frame, instr *ssa.Defer) error {
|
|||||||
valueTypes = append(valueTypes, context.Type())
|
valueTypes = append(valueTypes, context.Type())
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
return c.makeError(instr.Pos(), "todo: defer on uncommon function call type")
|
c.addError(instr.Pos(), "todo: defer on uncommon function call type")
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make a struct out of the collected values to put in the defer frame.
|
// Make a struct out of the collected values to put in the defer frame.
|
||||||
deferFrameType := c.ctx.StructType(valueTypes, false)
|
deferFrameType := c.ctx.StructType(valueTypes, false)
|
||||||
deferFrame, err := c.getZeroValue(deferFrameType)
|
deferFrame := c.getZeroValue(deferFrameType)
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
for i, value := range values {
|
for i, value := range values {
|
||||||
deferFrame = c.builder.CreateInsertValue(deferFrame, value, i, "")
|
deferFrame = c.builder.CreateInsertValue(deferFrame, value, i, "")
|
||||||
}
|
}
|
||||||
@@ -151,11 +134,10 @@ func (c *Compiler) emitDefer(frame *Frame, instr *ssa.Defer) error {
|
|||||||
// Push it on top of the linked list by replacing deferPtr.
|
// Push it on top of the linked list by replacing deferPtr.
|
||||||
allocaCast := c.builder.CreateBitCast(alloca, next.Type(), "defer.alloca.cast")
|
allocaCast := c.builder.CreateBitCast(alloca, next.Type(), "defer.alloca.cast")
|
||||||
c.builder.CreateStore(allocaCast, frame.deferPtr)
|
c.builder.CreateStore(allocaCast, frame.deferPtr)
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// emitRunDefers emits code to run all deferred functions.
|
// emitRunDefers emits code to run all deferred functions.
|
||||||
func (c *Compiler) emitRunDefers(frame *Frame) error {
|
func (c *Compiler) emitRunDefers(frame *Frame) {
|
||||||
// Add a loop like the following:
|
// Add a loop like the following:
|
||||||
// for stack != nil {
|
// for stack != nil {
|
||||||
// _stack := stack
|
// _stack := stack
|
||||||
@@ -190,13 +172,13 @@ func (c *Compiler) emitRunDefers(frame *Frame) error {
|
|||||||
// stack = stack.next
|
// stack = stack.next
|
||||||
// switch stack.callback {
|
// switch stack.callback {
|
||||||
c.builder.SetInsertPointAtEnd(loop)
|
c.builder.SetInsertPointAtEnd(loop)
|
||||||
nextStackGEP := c.builder.CreateGEP(deferData, []llvm.Value{
|
nextStackGEP := c.builder.CreateInBoundsGEP(deferData, []llvm.Value{
|
||||||
llvm.ConstInt(c.ctx.Int32Type(), 0, false),
|
llvm.ConstInt(c.ctx.Int32Type(), 0, false),
|
||||||
llvm.ConstInt(c.ctx.Int32Type(), 1, false), // .next field
|
llvm.ConstInt(c.ctx.Int32Type(), 1, false), // .next field
|
||||||
}, "stack.next.gep")
|
}, "stack.next.gep")
|
||||||
nextStack := c.builder.CreateLoad(nextStackGEP, "stack.next")
|
nextStack := c.builder.CreateLoad(nextStackGEP, "stack.next")
|
||||||
c.builder.CreateStore(nextStack, frame.deferPtr)
|
c.builder.CreateStore(nextStack, frame.deferPtr)
|
||||||
gep := c.builder.CreateGEP(deferData, []llvm.Value{
|
gep := c.builder.CreateInBoundsGEP(deferData, []llvm.Value{
|
||||||
llvm.ConstInt(c.ctx.Int32Type(), 0, false),
|
llvm.ConstInt(c.ctx.Int32Type(), 0, false),
|
||||||
llvm.ConstInt(c.ctx.Int32Type(), 0, false), // .callback field
|
llvm.ConstInt(c.ctx.Int32Type(), 0, false), // .callback field
|
||||||
}, "callback.gep")
|
}, "callback.gep")
|
||||||
@@ -220,11 +202,7 @@ func (c *Compiler) emitRunDefers(frame *Frame) error {
|
|||||||
// Get the real defer struct type and cast to it.
|
// Get the real defer struct type and cast to it.
|
||||||
valueTypes := []llvm.Type{c.uintptrType, llvm.PointerType(c.mod.GetTypeByName("runtime._defer"), 0), c.i8ptrType}
|
valueTypes := []llvm.Type{c.uintptrType, llvm.PointerType(c.mod.GetTypeByName("runtime._defer"), 0), c.i8ptrType}
|
||||||
for _, arg := range callback.Args {
|
for _, arg := range callback.Args {
|
||||||
llvmType, err := c.getLLVMType(arg.Type())
|
valueTypes = append(valueTypes, c.getLLVMType(arg.Type()))
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
valueTypes = append(valueTypes, llvmType)
|
|
||||||
}
|
}
|
||||||
deferFrameType := c.ctx.StructType(valueTypes, false)
|
deferFrameType := c.ctx.StructType(valueTypes, false)
|
||||||
deferFramePtr := c.builder.CreateBitCast(deferData, llvm.PointerType(deferFrameType, 0), "deferFrame")
|
deferFramePtr := c.builder.CreateBitCast(deferData, llvm.PointerType(deferFrameType, 0), "deferFrame")
|
||||||
@@ -233,7 +211,7 @@ func (c *Compiler) emitRunDefers(frame *Frame) error {
|
|||||||
forwardParams := []llvm.Value{}
|
forwardParams := []llvm.Value{}
|
||||||
zero := llvm.ConstInt(c.ctx.Int32Type(), 0, false)
|
zero := llvm.ConstInt(c.ctx.Int32Type(), 0, false)
|
||||||
for i := 2; i < len(valueTypes); i++ {
|
for i := 2; i < len(valueTypes); i++ {
|
||||||
gep := c.builder.CreateGEP(deferFramePtr, []llvm.Value{zero, llvm.ConstInt(c.ctx.Int32Type(), uint64(i), false)}, "gep")
|
gep := c.builder.CreateInBoundsGEP(deferFramePtr, []llvm.Value{zero, llvm.ConstInt(c.ctx.Int32Type(), uint64(i), false)}, "gep")
|
||||||
forwardParam := c.builder.CreateLoad(gep, "param")
|
forwardParam := c.builder.CreateLoad(gep, "param")
|
||||||
forwardParams = append(forwardParams, forwardParam)
|
forwardParams = append(forwardParams, forwardParam)
|
||||||
}
|
}
|
||||||
@@ -246,10 +224,7 @@ func (c *Compiler) emitRunDefers(frame *Frame) error {
|
|||||||
// Parent coroutine handle.
|
// Parent coroutine handle.
|
||||||
forwardParams = append(forwardParams, llvm.Undef(c.i8ptrType))
|
forwardParams = append(forwardParams, llvm.Undef(c.i8ptrType))
|
||||||
|
|
||||||
fnPtr, _, err := c.getInvokeCall(frame, callback)
|
fnPtr, _ := c.getInvokeCall(frame, callback)
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
c.createCall(fnPtr, forwardParams, "")
|
c.createCall(fnPtr, forwardParams, "")
|
||||||
|
|
||||||
case *ir.Function:
|
case *ir.Function:
|
||||||
@@ -258,11 +233,7 @@ func (c *Compiler) emitRunDefers(frame *Frame) error {
|
|||||||
// Get the real defer struct type and cast to it.
|
// Get the real defer struct type and cast to it.
|
||||||
valueTypes := []llvm.Type{c.uintptrType, llvm.PointerType(c.mod.GetTypeByName("runtime._defer"), 0)}
|
valueTypes := []llvm.Type{c.uintptrType, llvm.PointerType(c.mod.GetTypeByName("runtime._defer"), 0)}
|
||||||
for _, param := range callback.Params {
|
for _, param := range callback.Params {
|
||||||
llvmType, err := c.getLLVMType(param.Type())
|
valueTypes = append(valueTypes, c.getLLVMType(param.Type()))
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
valueTypes = append(valueTypes, llvmType)
|
|
||||||
}
|
}
|
||||||
deferFrameType := c.ctx.StructType(valueTypes, false)
|
deferFrameType := c.ctx.StructType(valueTypes, false)
|
||||||
deferFramePtr := c.builder.CreateBitCast(deferData, llvm.PointerType(deferFrameType, 0), "deferFrame")
|
deferFramePtr := c.builder.CreateBitCast(deferData, llvm.PointerType(deferFrameType, 0), "deferFrame")
|
||||||
@@ -271,7 +242,7 @@ func (c *Compiler) emitRunDefers(frame *Frame) error {
|
|||||||
forwardParams := []llvm.Value{}
|
forwardParams := []llvm.Value{}
|
||||||
zero := llvm.ConstInt(c.ctx.Int32Type(), 0, false)
|
zero := llvm.ConstInt(c.ctx.Int32Type(), 0, false)
|
||||||
for i := range callback.Params {
|
for i := range callback.Params {
|
||||||
gep := c.builder.CreateGEP(deferFramePtr, []llvm.Value{zero, llvm.ConstInt(c.ctx.Int32Type(), uint64(i+2), false)}, "gep")
|
gep := c.builder.CreateInBoundsGEP(deferFramePtr, []llvm.Value{zero, llvm.ConstInt(c.ctx.Int32Type(), uint64(i+2), false)}, "gep")
|
||||||
forwardParam := c.builder.CreateLoad(gep, "param")
|
forwardParam := c.builder.CreateLoad(gep, "param")
|
||||||
forwardParams = append(forwardParams, forwardParam)
|
forwardParams = append(forwardParams, forwardParam)
|
||||||
}
|
}
|
||||||
@@ -292,11 +263,7 @@ func (c *Compiler) emitRunDefers(frame *Frame) error {
|
|||||||
valueTypes := []llvm.Type{c.uintptrType, llvm.PointerType(c.mod.GetTypeByName("runtime._defer"), 0)}
|
valueTypes := []llvm.Type{c.uintptrType, llvm.PointerType(c.mod.GetTypeByName("runtime._defer"), 0)}
|
||||||
params := fn.Signature.Params()
|
params := fn.Signature.Params()
|
||||||
for i := 0; i < params.Len(); i++ {
|
for i := 0; i < params.Len(); i++ {
|
||||||
llvmType, err := c.getLLVMType(params.At(i).Type())
|
valueTypes = append(valueTypes, c.getLLVMType(params.At(i).Type()))
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
valueTypes = append(valueTypes, llvmType)
|
|
||||||
}
|
}
|
||||||
valueTypes = append(valueTypes, c.i8ptrType) // closure
|
valueTypes = append(valueTypes, c.i8ptrType) // closure
|
||||||
deferFrameType := c.ctx.StructType(valueTypes, false)
|
deferFrameType := c.ctx.StructType(valueTypes, false)
|
||||||
@@ -306,7 +273,7 @@ func (c *Compiler) emitRunDefers(frame *Frame) error {
|
|||||||
forwardParams := []llvm.Value{}
|
forwardParams := []llvm.Value{}
|
||||||
zero := llvm.ConstInt(c.ctx.Int32Type(), 0, false)
|
zero := llvm.ConstInt(c.ctx.Int32Type(), 0, false)
|
||||||
for i := 2; i < len(valueTypes); i++ {
|
for i := 2; i < len(valueTypes); i++ {
|
||||||
gep := c.builder.CreateGEP(deferFramePtr, []llvm.Value{zero, llvm.ConstInt(c.ctx.Int32Type(), uint64(i), false)}, "")
|
gep := c.builder.CreateInBoundsGEP(deferFramePtr, []llvm.Value{zero, llvm.ConstInt(c.ctx.Int32Type(), uint64(i), false)}, "")
|
||||||
forwardParam := c.builder.CreateLoad(gep, "param")
|
forwardParam := c.builder.CreateLoad(gep, "param")
|
||||||
forwardParams = append(forwardParams, forwardParam)
|
forwardParams = append(forwardParams, forwardParam)
|
||||||
}
|
}
|
||||||
@@ -334,5 +301,4 @@ func (c *Compiler) emitRunDefers(frame *Frame) error {
|
|||||||
|
|
||||||
// End of loop.
|
// End of loop.
|
||||||
c.builder.SetInsertPointAtEnd(end)
|
c.builder.SetInsertPointAtEnd(end)
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,3 +12,7 @@ func (c *Compiler) makeError(pos token.Pos, msg string) types.Error {
|
|||||||
Msg: msg,
|
Msg: msg,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Compiler) addError(pos token.Pos, msg string) {
|
||||||
|
c.diagnostics = append(c.diagnostics, c.makeError(pos, msg))
|
||||||
|
}
|
||||||
|
|||||||
+22
-85
@@ -41,7 +41,7 @@ func (c *Compiler) funcImplementation() funcValueImplementation {
|
|||||||
|
|
||||||
// createFuncValue creates a function value from a raw function pointer with no
|
// createFuncValue creates a function value from a raw function pointer with no
|
||||||
// context.
|
// context.
|
||||||
func (c *Compiler) createFuncValue(funcPtr, context llvm.Value, sig *types.Signature) (llvm.Value, error) {
|
func (c *Compiler) createFuncValue(funcPtr, context llvm.Value, sig *types.Signature) llvm.Value {
|
||||||
var funcValueScalar llvm.Value
|
var funcValueScalar llvm.Value
|
||||||
switch c.funcImplementation() {
|
switch c.funcImplementation() {
|
||||||
case funcValueDoubleword:
|
case funcValueDoubleword:
|
||||||
@@ -66,14 +66,11 @@ func (c *Compiler) createFuncValue(funcPtr, context llvm.Value, sig *types.Signa
|
|||||||
default:
|
default:
|
||||||
panic("unimplemented func value variant")
|
panic("unimplemented func value variant")
|
||||||
}
|
}
|
||||||
funcValueType, err := c.getFuncType(sig)
|
funcValueType := c.getFuncType(sig)
|
||||||
if err != nil {
|
|
||||||
return llvm.Value{}, err
|
|
||||||
}
|
|
||||||
funcValue := llvm.Undef(funcValueType)
|
funcValue := llvm.Undef(funcValueType)
|
||||||
funcValue = c.builder.CreateInsertValue(funcValue, context, 0, "")
|
funcValue = c.builder.CreateInsertValue(funcValue, context, 0, "")
|
||||||
funcValue = c.builder.CreateInsertValue(funcValue, funcValueScalar, 1, "")
|
funcValue = c.builder.CreateInsertValue(funcValue, funcValueScalar, 1, "")
|
||||||
return funcValue, nil
|
return funcValue
|
||||||
}
|
}
|
||||||
|
|
||||||
// getFuncSignature returns a global for identification of a particular function
|
// getFuncSignature returns a global for identification of a particular function
|
||||||
@@ -112,10 +109,7 @@ func (c *Compiler) decodeFuncValue(funcValue llvm.Value, sig *types.Signature) (
|
|||||||
case funcValueDoubleword:
|
case funcValueDoubleword:
|
||||||
funcPtr = c.builder.CreateExtractValue(funcValue, 1, "")
|
funcPtr = c.builder.CreateExtractValue(funcValue, 1, "")
|
||||||
case funcValueSwitch:
|
case funcValueSwitch:
|
||||||
llvmSig, err := c.getRawFuncType(sig)
|
llvmSig := c.getRawFuncType(sig)
|
||||||
if err != nil {
|
|
||||||
return llvm.Value{}, llvm.Value{}, err
|
|
||||||
}
|
|
||||||
sigGlobal := c.getFuncSignature(sig)
|
sigGlobal := c.getFuncSignature(sig)
|
||||||
funcPtr = c.createRuntimeCall("getFuncPtr", []llvm.Value{funcValue, sigGlobal}, "")
|
funcPtr = c.createRuntimeCall("getFuncPtr", []llvm.Value{funcValue, sigGlobal}, "")
|
||||||
funcPtr = c.builder.CreateIntToPtr(funcPtr, llvmSig, "")
|
funcPtr = c.builder.CreateIntToPtr(funcPtr, llvmSig, "")
|
||||||
@@ -126,25 +120,21 @@ func (c *Compiler) decodeFuncValue(funcValue llvm.Value, sig *types.Signature) (
|
|||||||
}
|
}
|
||||||
|
|
||||||
// getFuncType returns the type of a func value given a signature.
|
// getFuncType returns the type of a func value given a signature.
|
||||||
func (c *Compiler) getFuncType(typ *types.Signature) (llvm.Type, error) {
|
func (c *Compiler) getFuncType(typ *types.Signature) llvm.Type {
|
||||||
switch c.funcImplementation() {
|
switch c.funcImplementation() {
|
||||||
case funcValueDoubleword:
|
case funcValueDoubleword:
|
||||||
rawPtr, err := c.getRawFuncType(typ)
|
rawPtr := c.getRawFuncType(typ)
|
||||||
if err != nil {
|
return c.ctx.StructType([]llvm.Type{c.i8ptrType, rawPtr}, false)
|
||||||
return llvm.Type{}, err
|
|
||||||
}
|
|
||||||
return c.ctx.StructType([]llvm.Type{c.i8ptrType, rawPtr}, false), nil
|
|
||||||
case funcValueSwitch:
|
case funcValueSwitch:
|
||||||
return c.mod.GetTypeByName("runtime.funcValue"), nil
|
return c.mod.GetTypeByName("runtime.funcValue")
|
||||||
default:
|
default:
|
||||||
panic("unimplemented func value variant")
|
panic("unimplemented func value variant")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// getRawFuncType returns a LLVM function pointer type for a given signature.
|
// getRawFuncType returns a LLVM function pointer type for a given signature.
|
||||||
func (c *Compiler) getRawFuncType(typ *types.Signature) (llvm.Type, error) {
|
func (c *Compiler) getRawFuncType(typ *types.Signature) llvm.Type {
|
||||||
// Get the return type.
|
// Get the return type.
|
||||||
var err error
|
|
||||||
var returnType llvm.Type
|
var returnType llvm.Type
|
||||||
switch typ.Results().Len() {
|
switch typ.Results().Len() {
|
||||||
case 0:
|
case 0:
|
||||||
@@ -152,21 +142,14 @@ func (c *Compiler) getRawFuncType(typ *types.Signature) (llvm.Type, error) {
|
|||||||
returnType = c.ctx.VoidType()
|
returnType = c.ctx.VoidType()
|
||||||
case 1:
|
case 1:
|
||||||
// Just one return value.
|
// Just one return value.
|
||||||
returnType, err = c.getLLVMType(typ.Results().At(0).Type())
|
returnType = c.getLLVMType(typ.Results().At(0).Type())
|
||||||
if err != nil {
|
|
||||||
return llvm.Type{}, err
|
|
||||||
}
|
|
||||||
default:
|
default:
|
||||||
// Multiple return values. Put them together in a struct.
|
// Multiple return values. Put them together in a struct.
|
||||||
// This appears to be the common way to handle multiple return values in
|
// This appears to be the common way to handle multiple return values in
|
||||||
// LLVM.
|
// LLVM.
|
||||||
members := make([]llvm.Type, typ.Results().Len())
|
members := make([]llvm.Type, typ.Results().Len())
|
||||||
for i := 0; i < typ.Results().Len(); i++ {
|
for i := 0; i < typ.Results().Len(); i++ {
|
||||||
returnType, err := c.getLLVMType(typ.Results().At(i).Type())
|
members[i] = c.getLLVMType(typ.Results().At(i).Type())
|
||||||
if err != nil {
|
|
||||||
return llvm.Type{}, err
|
|
||||||
}
|
|
||||||
members[i] = returnType
|
|
||||||
}
|
}
|
||||||
returnType = c.ctx.StructType(members, false)
|
returnType = c.ctx.StructType(members, false)
|
||||||
}
|
}
|
||||||
@@ -174,10 +157,7 @@ func (c *Compiler) getRawFuncType(typ *types.Signature) (llvm.Type, error) {
|
|||||||
// Get the parameter types.
|
// Get the parameter types.
|
||||||
var paramTypes []llvm.Type
|
var paramTypes []llvm.Type
|
||||||
if typ.Recv() != nil {
|
if typ.Recv() != nil {
|
||||||
recv, err := c.getLLVMType(typ.Recv().Type())
|
recv := c.getLLVMType(typ.Recv().Type())
|
||||||
if err != nil {
|
|
||||||
return llvm.Type{}, err
|
|
||||||
}
|
|
||||||
if recv.StructName() == "runtime._interface" {
|
if recv.StructName() == "runtime._interface" {
|
||||||
// This is a call on an interface, not a concrete type.
|
// This is a call on an interface, not a concrete type.
|
||||||
// The receiver is not an interface, but a i8* type.
|
// The receiver is not an interface, but a i8* type.
|
||||||
@@ -186,10 +166,7 @@ func (c *Compiler) getRawFuncType(typ *types.Signature) (llvm.Type, error) {
|
|||||||
paramTypes = append(paramTypes, c.expandFormalParamType(recv)...)
|
paramTypes = append(paramTypes, c.expandFormalParamType(recv)...)
|
||||||
}
|
}
|
||||||
for i := 0; i < typ.Params().Len(); i++ {
|
for i := 0; i < typ.Params().Len(); i++ {
|
||||||
subType, err := c.getLLVMType(typ.Params().At(i).Type())
|
subType := c.getLLVMType(typ.Params().At(i).Type())
|
||||||
if err != nil {
|
|
||||||
return llvm.Type{}, err
|
|
||||||
}
|
|
||||||
paramTypes = append(paramTypes, c.expandFormalParamType(subType)...)
|
paramTypes = append(paramTypes, c.expandFormalParamType(subType)...)
|
||||||
}
|
}
|
||||||
// All functions take these parameters at the end.
|
// All functions take these parameters at the end.
|
||||||
@@ -197,7 +174,7 @@ func (c *Compiler) getRawFuncType(typ *types.Signature) (llvm.Type, error) {
|
|||||||
paramTypes = append(paramTypes, c.i8ptrType) // parent coroutine
|
paramTypes = append(paramTypes, c.i8ptrType) // parent coroutine
|
||||||
|
|
||||||
// Make a func type out of the signature.
|
// Make a func type out of the signature.
|
||||||
return llvm.PointerType(llvm.FunctionType(returnType, paramTypes, false), c.funcPtrAddrSpace), nil
|
return llvm.PointerType(llvm.FunctionType(returnType, paramTypes, false), c.funcPtrAddrSpace)
|
||||||
}
|
}
|
||||||
|
|
||||||
// parseMakeClosure makes a function value (with context) from the given
|
// parseMakeClosure makes a function value (with context) from the given
|
||||||
@@ -209,57 +186,17 @@ func (c *Compiler) parseMakeClosure(frame *Frame, expr *ssa.MakeClosure) (llvm.V
|
|||||||
f := c.ir.GetFunction(expr.Fn.(*ssa.Function))
|
f := c.ir.GetFunction(expr.Fn.(*ssa.Function))
|
||||||
|
|
||||||
// Collect all bound variables.
|
// Collect all bound variables.
|
||||||
boundVars := make([]llvm.Value, 0, len(expr.Bindings))
|
boundVars := make([]llvm.Value, len(expr.Bindings))
|
||||||
boundVarTypes := make([]llvm.Type, 0, len(expr.Bindings))
|
for i, binding := range expr.Bindings {
|
||||||
for _, binding := range expr.Bindings {
|
|
||||||
// The context stores the bound variables.
|
// The context stores the bound variables.
|
||||||
llvmBoundVar, err := c.parseExpr(frame, binding)
|
llvmBoundVar := c.getValue(frame, binding)
|
||||||
if err != nil {
|
boundVars[i] = llvmBoundVar
|
||||||
return llvm.Value{}, err
|
|
||||||
}
|
|
||||||
boundVars = append(boundVars, llvmBoundVar)
|
|
||||||
boundVarTypes = append(boundVarTypes, llvmBoundVar.Type())
|
|
||||||
}
|
|
||||||
contextType := c.ctx.StructType(boundVarTypes, false)
|
|
||||||
|
|
||||||
// Allocate memory for the context.
|
|
||||||
contextAlloc := llvm.Value{}
|
|
||||||
contextHeapAlloc := llvm.Value{}
|
|
||||||
if c.targetData.TypeAllocSize(contextType) <= c.targetData.TypeAllocSize(c.i8ptrType) {
|
|
||||||
// Context fits in a pointer - e.g. when it is a pointer. Store it
|
|
||||||
// directly in the stack after a convert.
|
|
||||||
// Because contextType is a struct and we have to cast it to a *i8,
|
|
||||||
// store it in an alloca first for bitcasting (store+bitcast+load).
|
|
||||||
contextAlloc = c.builder.CreateAlloca(contextType, "")
|
|
||||||
} else {
|
|
||||||
// Context is bigger than a pointer, so allocate it on the heap.
|
|
||||||
size := c.targetData.TypeAllocSize(contextType)
|
|
||||||
sizeValue := llvm.ConstInt(c.uintptrType, size, false)
|
|
||||||
contextHeapAlloc = c.createRuntimeCall("alloc", []llvm.Value{sizeValue}, "")
|
|
||||||
contextAlloc = c.builder.CreateBitCast(contextHeapAlloc, llvm.PointerType(contextType, 0), "")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store all bound variables in the alloca or heap pointer.
|
// Store the bound variables in a single object, allocating it on the heap
|
||||||
for i, boundVar := range boundVars {
|
// if necessary.
|
||||||
indices := []llvm.Value{
|
context := c.emitPointerPack(boundVars)
|
||||||
llvm.ConstInt(c.ctx.Int32Type(), 0, false),
|
|
||||||
llvm.ConstInt(c.ctx.Int32Type(), uint64(i), false),
|
|
||||||
}
|
|
||||||
gep := c.builder.CreateInBoundsGEP(contextAlloc, indices, "")
|
|
||||||
c.builder.CreateStore(boundVar, gep)
|
|
||||||
}
|
|
||||||
|
|
||||||
context := llvm.Value{}
|
|
||||||
if c.targetData.TypeAllocSize(contextType) <= c.targetData.TypeAllocSize(c.i8ptrType) {
|
|
||||||
// Load value (as *i8) from the alloca.
|
|
||||||
contextAlloc = c.builder.CreateBitCast(contextAlloc, llvm.PointerType(c.i8ptrType, 0), "")
|
|
||||||
context = c.builder.CreateLoad(contextAlloc, "")
|
|
||||||
} else {
|
|
||||||
// Get the original heap allocation pointer, which already is an
|
|
||||||
// *i8.
|
|
||||||
context = contextHeapAlloc
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create the closure.
|
// Create the closure.
|
||||||
return c.createFuncValue(f.LLVMFn, context, f.Signature)
|
return c.createFuncValue(f.LLVMFn, context, f.Signature), nil
|
||||||
}
|
}
|
||||||
|
|||||||
+94
-121
@@ -10,8 +10,8 @@ package compiler
|
|||||||
// go foo()
|
// go foo()
|
||||||
// time.Sleep(2 * time.Second)
|
// time.Sleep(2 * time.Second)
|
||||||
// println("some other operation")
|
// println("some other operation")
|
||||||
// bar()
|
// i := bar()
|
||||||
// println("done")
|
// println("done", *i)
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
// func foo() {
|
// func foo() {
|
||||||
@@ -21,9 +21,10 @@ package compiler
|
|||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
// func bar() {
|
// func bar() *int {
|
||||||
// time.Sleep(time.Second)
|
// time.Sleep(time.Second)
|
||||||
// println("blocking operation completed)
|
// println("blocking operation completed)
|
||||||
|
// return new(int)
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
// It is transformed by the IR generator in compiler.go into the following
|
// It is transformed by the IR generator in compiler.go into the following
|
||||||
@@ -34,8 +35,8 @@ package compiler
|
|||||||
// fn()
|
// fn()
|
||||||
// time.Sleep(2 * time.Second)
|
// time.Sleep(2 * time.Second)
|
||||||
// println("some other operation")
|
// println("some other operation")
|
||||||
// bar() // imagine an 'await' keyword in front of this call
|
// i := bar() // imagine an 'await' keyword in front of this call
|
||||||
// println("done")
|
// println("done", *i)
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
// func foo() {
|
// func foo() {
|
||||||
@@ -45,9 +46,10 @@ package compiler
|
|||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
// func bar() {
|
// func bar() *int {
|
||||||
// time.Sleep(time.Second)
|
// time.Sleep(time.Second)
|
||||||
// println("blocking operation completed)
|
// println("blocking operation completed)
|
||||||
|
// return new(int)
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
// The pass in this file transforms this code even further, to the following
|
// The pass in this file transforms this code even further, to the following
|
||||||
@@ -59,9 +61,11 @@ package compiler
|
|||||||
// runtime.sleepTask(hdl, 2 * time.Second) // ask the scheduler to re-activate this coroutine at the right time
|
// runtime.sleepTask(hdl, 2 * time.Second) // ask the scheduler to re-activate this coroutine at the right time
|
||||||
// llvm.suspend(hdl) // suspend point
|
// llvm.suspend(hdl) // suspend point
|
||||||
// println("some other operation")
|
// println("some other operation")
|
||||||
|
// var i *int // allocate space on the stack for the return value
|
||||||
|
// runtime.setTaskPromisePtr(hdl, &i) // store return value alloca in our coroutine promise
|
||||||
// bar(hdl) // await, pass a continuation (hdl) to bar
|
// bar(hdl) // await, pass a continuation (hdl) to bar
|
||||||
// llvm.suspend(hdl) // suspend point, wait for the callee to re-activate
|
// llvm.suspend(hdl) // suspend point, wait for the callee to re-activate
|
||||||
// println("done")
|
// println("done", *i)
|
||||||
// runtime.activateTask(parent) // re-activate the parent (nop, there is no parent)
|
// runtime.activateTask(parent) // re-activate the parent (nop, there is no parent)
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
@@ -142,10 +146,9 @@ func (c *Compiler) LowerGoroutines() error {
|
|||||||
realMain.SetLinkage(llvm.InternalLinkage)
|
realMain.SetLinkage(llvm.InternalLinkage)
|
||||||
c.mod.NamedFunction("runtime.alloc").SetLinkage(llvm.InternalLinkage)
|
c.mod.NamedFunction("runtime.alloc").SetLinkage(llvm.InternalLinkage)
|
||||||
c.mod.NamedFunction("runtime.free").SetLinkage(llvm.InternalLinkage)
|
c.mod.NamedFunction("runtime.free").SetLinkage(llvm.InternalLinkage)
|
||||||
c.mod.NamedFunction("runtime.chanSend").SetLinkage(llvm.InternalLinkage)
|
|
||||||
c.mod.NamedFunction("runtime.chanRecv").SetLinkage(llvm.InternalLinkage)
|
|
||||||
c.mod.NamedFunction("runtime.sleepTask").SetLinkage(llvm.InternalLinkage)
|
c.mod.NamedFunction("runtime.sleepTask").SetLinkage(llvm.InternalLinkage)
|
||||||
c.mod.NamedFunction("runtime.activateTask").SetLinkage(llvm.InternalLinkage)
|
c.mod.NamedFunction("runtime.setTaskPromisePtr").SetLinkage(llvm.InternalLinkage)
|
||||||
|
c.mod.NamedFunction("runtime.getTaskPromisePtr").SetLinkage(llvm.InternalLinkage)
|
||||||
c.mod.NamedFunction("runtime.scheduler").SetLinkage(llvm.InternalLinkage)
|
c.mod.NamedFunction("runtime.scheduler").SetLinkage(llvm.InternalLinkage)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@@ -174,13 +177,13 @@ func (c *Compiler) markAsyncFunctions() (needsScheduler bool, err error) {
|
|||||||
if !deadlockStub.IsNil() {
|
if !deadlockStub.IsNil() {
|
||||||
worklist = append(worklist, deadlockStub)
|
worklist = append(worklist, deadlockStub)
|
||||||
}
|
}
|
||||||
chanSendStub := c.mod.NamedFunction("runtime.chanSendStub")
|
chanSend := c.mod.NamedFunction("runtime.chanSend")
|
||||||
if !chanSendStub.IsNil() {
|
if !chanSend.IsNil() {
|
||||||
worklist = append(worklist, chanSendStub)
|
worklist = append(worklist, chanSend)
|
||||||
}
|
}
|
||||||
chanRecvStub := c.mod.NamedFunction("runtime.chanRecvStub")
|
chanRecv := c.mod.NamedFunction("runtime.chanRecv")
|
||||||
if !chanRecvStub.IsNil() {
|
if !chanRecv.IsNil() {
|
||||||
worklist = append(worklist, chanRecvStub)
|
worklist = append(worklist, chanRecv)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(worklist) == 0 {
|
if len(worklist) == 0 {
|
||||||
@@ -278,9 +281,6 @@ func (c *Compiler) markAsyncFunctions() (needsScheduler bool, err error) {
|
|||||||
coroBeginType := llvm.FunctionType(c.i8ptrType, []llvm.Type{c.ctx.TokenType(), c.i8ptrType}, false)
|
coroBeginType := llvm.FunctionType(c.i8ptrType, []llvm.Type{c.ctx.TokenType(), c.i8ptrType}, false)
|
||||||
coroBeginFunc := llvm.AddFunction(c.mod, "llvm.coro.begin", coroBeginType)
|
coroBeginFunc := llvm.AddFunction(c.mod, "llvm.coro.begin", coroBeginType)
|
||||||
|
|
||||||
coroPromiseType := llvm.FunctionType(c.i8ptrType, []llvm.Type{c.i8ptrType, c.ctx.Int32Type(), c.ctx.Int1Type()}, false)
|
|
||||||
coroPromiseFunc := llvm.AddFunction(c.mod, "llvm.coro.promise", coroPromiseType)
|
|
||||||
|
|
||||||
coroSuspendType := llvm.FunctionType(c.ctx.Int8Type(), []llvm.Type{c.ctx.TokenType(), c.ctx.Int1Type()}, false)
|
coroSuspendType := llvm.FunctionType(c.ctx.Int8Type(), []llvm.Type{c.ctx.TokenType(), c.ctx.Int1Type()}, false)
|
||||||
coroSuspendFunc := llvm.AddFunction(c.mod, "llvm.coro.suspend", coroSuspendType)
|
coroSuspendFunc := llvm.AddFunction(c.mod, "llvm.coro.suspend", coroSuspendType)
|
||||||
|
|
||||||
@@ -292,7 +292,7 @@ func (c *Compiler) markAsyncFunctions() (needsScheduler bool, err error) {
|
|||||||
|
|
||||||
// Transform all async functions into coroutines.
|
// Transform all async functions into coroutines.
|
||||||
for _, f := range asyncList {
|
for _, f := range asyncList {
|
||||||
if f == sleep || f == deadlockStub || f == chanSendStub || f == chanRecvStub {
|
if f == sleep || f == deadlockStub || f == chanSend || f == chanRecv {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -309,7 +309,7 @@ func (c *Compiler) markAsyncFunctions() (needsScheduler bool, err error) {
|
|||||||
for inst := bb.FirstInstruction(); !inst.IsNil(); inst = llvm.NextInstruction(inst) {
|
for inst := bb.FirstInstruction(); !inst.IsNil(); inst = llvm.NextInstruction(inst) {
|
||||||
if !inst.IsACallInst().IsNil() {
|
if !inst.IsACallInst().IsNil() {
|
||||||
callee := inst.CalledValue()
|
callee := inst.CalledValue()
|
||||||
if _, ok := asyncFuncs[callee]; !ok || callee == sleep || callee == deadlockStub || callee == chanSendStub || callee == chanRecvStub {
|
if _, ok := asyncFuncs[callee]; !ok || callee == sleep || callee == deadlockStub || callee == chanSend || callee == chanRecv {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
asyncCalls = append(asyncCalls, inst)
|
asyncCalls = append(asyncCalls, inst)
|
||||||
@@ -347,10 +347,18 @@ func (c *Compiler) markAsyncFunctions() (needsScheduler bool, err error) {
|
|||||||
// Split this basic block.
|
// Split this basic block.
|
||||||
await := c.splitBasicBlock(inst, llvm.NextBasicBlock(c.builder.GetInsertBlock()), "task.await")
|
await := c.splitBasicBlock(inst, llvm.NextBasicBlock(c.builder.GetInsertBlock()), "task.await")
|
||||||
|
|
||||||
// Set task state to TASK_STATE_CALL.
|
// Allocate space for the return value.
|
||||||
c.builder.SetInsertPointAtEnd(inst.InstructionParent())
|
var retvalAlloca llvm.Value
|
||||||
|
if inst.Type().TypeKind() != llvm.VoidTypeKind {
|
||||||
|
c.builder.SetInsertPointBefore(inst.InstructionParent().Parent().EntryBasicBlock().FirstInstruction())
|
||||||
|
retvalAlloca = c.builder.CreateAlloca(inst.Type(), "coro.retvalAlloca")
|
||||||
|
c.builder.SetInsertPointBefore(inst)
|
||||||
|
data := c.builder.CreateBitCast(retvalAlloca, c.i8ptrType, "")
|
||||||
|
c.createRuntimeCall("setTaskPromisePtr", []llvm.Value{frame.taskHandle, data}, "")
|
||||||
|
}
|
||||||
|
|
||||||
// Suspend.
|
// Suspend.
|
||||||
|
c.builder.SetInsertPointAtEnd(inst.InstructionParent())
|
||||||
continuePoint := c.builder.CreateCall(coroSuspendFunc, []llvm.Value{
|
continuePoint := c.builder.CreateCall(coroSuspendFunc, []llvm.Value{
|
||||||
llvm.ConstNull(c.ctx.TokenType()),
|
llvm.ConstNull(c.ctx.TokenType()),
|
||||||
llvm.ConstInt(c.ctx.Int1Type(), 0, false),
|
llvm.ConstInt(c.ctx.Int1Type(), 0, false),
|
||||||
@@ -358,44 +366,63 @@ func (c *Compiler) markAsyncFunctions() (needsScheduler bool, err error) {
|
|||||||
sw := c.builder.CreateSwitch(continuePoint, frame.suspendBlock, 2)
|
sw := c.builder.CreateSwitch(continuePoint, frame.suspendBlock, 2)
|
||||||
sw.AddCase(llvm.ConstInt(c.ctx.Int8Type(), 0, false), await)
|
sw.AddCase(llvm.ConstInt(c.ctx.Int8Type(), 0, false), await)
|
||||||
sw.AddCase(llvm.ConstInt(c.ctx.Int8Type(), 1, false), frame.cleanupBlock)
|
sw.AddCase(llvm.ConstInt(c.ctx.Int8Type(), 1, false), frame.cleanupBlock)
|
||||||
|
|
||||||
|
if inst.Type().TypeKind() != llvm.VoidTypeKind {
|
||||||
|
// Load the return value from the alloca. The callee has
|
||||||
|
// written the return value to it.
|
||||||
|
c.builder.SetInsertPointBefore(await.FirstInstruction())
|
||||||
|
retval := c.builder.CreateLoad(retvalAlloca, "coro.retval")
|
||||||
|
inst.ReplaceAllUsesWith(retval)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Replace return instructions with suspend points that should
|
// Replace return instructions with suspend points that should
|
||||||
// reactivate the parent coroutine.
|
// reactivate the parent coroutine.
|
||||||
for _, inst := range returns {
|
for _, inst := range returns {
|
||||||
if inst.OperandsCount() == 0 {
|
// These properties were added by the functionattrs pass. Remove
|
||||||
// These properties were added by the functionattrs pass.
|
// them, because now we start using the parameter.
|
||||||
// Remove them, because now we start using the parameter.
|
// https://llvm.org/docs/Passes.html#functionattrs-deduce-function-attributes
|
||||||
// https://llvm.org/docs/Passes.html#functionattrs-deduce-function-attributes
|
for _, kind := range []string{"nocapture", "readnone"} {
|
||||||
for _, kind := range []string{"nocapture", "readnone"} {
|
kindID := llvm.AttributeKindID(kind)
|
||||||
kindID := llvm.AttributeKindID(kind)
|
f.RemoveEnumAttributeAtIndex(f.ParamsCount(), kindID)
|
||||||
f.RemoveEnumAttributeAtIndex(f.ParamsCount(), kindID)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reactivate the parent coroutine. This adds it back to
|
|
||||||
// the run queue, so it is started again by the
|
|
||||||
// scheduler when possible (possibly right after the
|
|
||||||
// following suspend).
|
|
||||||
c.builder.SetInsertPointBefore(inst)
|
|
||||||
|
|
||||||
parentHandle := f.LastParam()
|
|
||||||
c.createRuntimeCall("activateTask", []llvm.Value{parentHandle}, "")
|
|
||||||
|
|
||||||
// Suspend this coroutine.
|
|
||||||
// It would look like this is unnecessary, but if this
|
|
||||||
// suspend point is left out, it leads to undefined
|
|
||||||
// behavior somehow (with the unreachable instruction).
|
|
||||||
continuePoint := c.builder.CreateCall(coroSuspendFunc, []llvm.Value{
|
|
||||||
llvm.ConstNull(c.ctx.TokenType()),
|
|
||||||
llvm.ConstInt(c.ctx.Int1Type(), 1, false),
|
|
||||||
}, "ret")
|
|
||||||
sw := c.builder.CreateSwitch(continuePoint, frame.suspendBlock, 2)
|
|
||||||
sw.AddCase(llvm.ConstInt(c.ctx.Int8Type(), 0, false), frame.unreachableBlock)
|
|
||||||
sw.AddCase(llvm.ConstInt(c.ctx.Int8Type(), 1, false), frame.cleanupBlock)
|
|
||||||
inst.EraseFromParentAsInstruction()
|
|
||||||
} else {
|
|
||||||
panic("todo: return value from coroutine")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
c.builder.SetInsertPointBefore(inst)
|
||||||
|
|
||||||
|
parentHandle := f.LastParam()
|
||||||
|
|
||||||
|
// Store return values.
|
||||||
|
switch inst.OperandsCount() {
|
||||||
|
case 0:
|
||||||
|
// Nothing to return.
|
||||||
|
case 1:
|
||||||
|
// Return this value by writing to the pointer stored in the
|
||||||
|
// parent handle. The parent coroutine has made an alloca that
|
||||||
|
// we can write to to store our return value.
|
||||||
|
returnValuePtr := c.createRuntimeCall("getTaskPromisePtr", []llvm.Value{parentHandle}, "coro.parentData")
|
||||||
|
alloca := c.builder.CreateBitCast(returnValuePtr, llvm.PointerType(inst.Operand(0).Type(), 0), "coro.parentAlloca")
|
||||||
|
c.builder.CreateStore(inst.Operand(0), alloca)
|
||||||
|
default:
|
||||||
|
panic("unreachable")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reactivate the parent coroutine. This adds it back to the run
|
||||||
|
// queue, so it is started again by the scheduler when possible
|
||||||
|
// (possibly right after the following suspend).
|
||||||
|
c.createRuntimeCall("activateTask", []llvm.Value{parentHandle}, "")
|
||||||
|
|
||||||
|
// Suspend this coroutine.
|
||||||
|
// It would look like this is unnecessary, but if this
|
||||||
|
// suspend point is left out, it leads to undefined
|
||||||
|
// behavior somehow (with the unreachable instruction).
|
||||||
|
continuePoint := c.builder.CreateCall(coroSuspendFunc, []llvm.Value{
|
||||||
|
llvm.ConstNull(c.ctx.TokenType()),
|
||||||
|
llvm.ConstInt(c.ctx.Int1Type(), 1, false),
|
||||||
|
}, "ret")
|
||||||
|
sw := c.builder.CreateSwitch(continuePoint, frame.suspendBlock, 2)
|
||||||
|
sw.AddCase(llvm.ConstInt(c.ctx.Int8Type(), 0, false), frame.unreachableBlock)
|
||||||
|
sw.AddCase(llvm.ConstInt(c.ctx.Int8Type(), 1, false), frame.cleanupBlock)
|
||||||
|
inst.EraseFromParentAsInstruction()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Coroutine cleanup. Free resources associated with this coroutine.
|
// Coroutine cleanup. Free resources associated with this coroutine.
|
||||||
@@ -420,6 +447,14 @@ func (c *Compiler) markAsyncFunctions() (needsScheduler bool, err error) {
|
|||||||
c.builder.CreateUnreachable()
|
c.builder.CreateUnreachable()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Replace calls to runtime.getCoroutineCall with the coroutine of this
|
||||||
|
// frame.
|
||||||
|
for _, getCoroutineCall := range getUses(c.mod.NamedFunction("runtime.getCoroutine")) {
|
||||||
|
frame := asyncFuncs[getCoroutineCall.InstructionParent().Parent()]
|
||||||
|
getCoroutineCall.ReplaceAllUsesWith(frame.taskHandle)
|
||||||
|
getCoroutineCall.EraseFromParentAsInstruction()
|
||||||
|
}
|
||||||
|
|
||||||
// Transform calls to time.Sleep() into coroutine suspend points.
|
// Transform calls to time.Sleep() into coroutine suspend points.
|
||||||
for _, sleepCall := range getUses(sleep) {
|
for _, sleepCall := range getUses(sleep) {
|
||||||
// sleepCall must be a call instruction.
|
// sleepCall must be a call instruction.
|
||||||
@@ -463,37 +498,11 @@ func (c *Compiler) markAsyncFunctions() (needsScheduler bool, err error) {
|
|||||||
deadlockCall.EraseFromParentAsInstruction()
|
deadlockCall.EraseFromParentAsInstruction()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Transform calls to runtime.chanSendStub into channel send operations.
|
// Transform calls to runtime.chanSend into channel send operations.
|
||||||
for _, sendOp := range getUses(chanSendStub) {
|
for _, sendOp := range getUses(chanSend) {
|
||||||
// sendOp must be a call instruction.
|
// sendOp must be a call instruction.
|
||||||
frame := asyncFuncs[sendOp.InstructionParent().Parent()]
|
frame := asyncFuncs[sendOp.InstructionParent().Parent()]
|
||||||
|
|
||||||
// Send the value over the channel, or block.
|
|
||||||
sendOp.SetOperand(0, frame.taskHandle)
|
|
||||||
sendOp.SetOperand(sendOp.OperandsCount()-1, c.mod.NamedFunction("runtime.chanSend"))
|
|
||||||
|
|
||||||
// Use taskState.data to store the value to send:
|
|
||||||
// *(*valueType)(&coroutine.promise().data) = valueToSend
|
|
||||||
// runtime.chanSend(coroutine, ch)
|
|
||||||
bitcast := sendOp.Operand(2)
|
|
||||||
valueAlloca := bitcast.Operand(0)
|
|
||||||
c.builder.SetInsertPointBefore(valueAlloca)
|
|
||||||
promiseType := c.mod.GetTypeByName("runtime.taskState")
|
|
||||||
promiseRaw := c.builder.CreateCall(coroPromiseFunc, []llvm.Value{
|
|
||||||
frame.taskHandle,
|
|
||||||
llvm.ConstInt(c.ctx.Int32Type(), uint64(c.targetData.PrefTypeAlignment(promiseType)), false),
|
|
||||||
llvm.ConstInt(c.ctx.Int1Type(), 0, false),
|
|
||||||
}, "task.promise.raw")
|
|
||||||
promise := c.builder.CreateBitCast(promiseRaw, llvm.PointerType(promiseType, 0), "task.promise")
|
|
||||||
dataPtr := c.builder.CreateGEP(promise, []llvm.Value{
|
|
||||||
llvm.ConstInt(c.ctx.Int32Type(), 0, false),
|
|
||||||
llvm.ConstInt(c.ctx.Int32Type(), 2, false),
|
|
||||||
}, "task.promise.data")
|
|
||||||
sendOp.SetOperand(2, llvm.Undef(c.i8ptrType))
|
|
||||||
valueAlloca.ReplaceAllUsesWith(c.builder.CreateBitCast(dataPtr, valueAlloca.Type(), ""))
|
|
||||||
bitcast.EraseFromParentAsInstruction()
|
|
||||||
valueAlloca.EraseFromParentAsInstruction()
|
|
||||||
|
|
||||||
// Yield to scheduler.
|
// Yield to scheduler.
|
||||||
c.builder.SetInsertPointBefore(llvm.NextInstruction(sendOp))
|
c.builder.SetInsertPointBefore(llvm.NextInstruction(sendOp))
|
||||||
continuePoint := c.builder.CreateCall(coroSuspendFunc, []llvm.Value{
|
continuePoint := c.builder.CreateCall(coroSuspendFunc, []llvm.Value{
|
||||||
@@ -506,21 +515,11 @@ func (c *Compiler) markAsyncFunctions() (needsScheduler bool, err error) {
|
|||||||
sw.AddCase(llvm.ConstInt(c.ctx.Int8Type(), 1, false), frame.cleanupBlock)
|
sw.AddCase(llvm.ConstInt(c.ctx.Int8Type(), 1, false), frame.cleanupBlock)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Transform calls to runtime.chanRecvStub into channel receive operations.
|
// Transform calls to runtime.chanRecv into channel receive operations.
|
||||||
for _, recvOp := range getUses(chanRecvStub) {
|
for _, recvOp := range getUses(chanRecv) {
|
||||||
// recvOp must be a call instruction.
|
// recvOp must be a call instruction.
|
||||||
frame := asyncFuncs[recvOp.InstructionParent().Parent()]
|
frame := asyncFuncs[recvOp.InstructionParent().Parent()]
|
||||||
|
|
||||||
bitcast := recvOp.Operand(2)
|
|
||||||
commaOk := recvOp.Operand(3)
|
|
||||||
valueAlloca := bitcast.Operand(0)
|
|
||||||
|
|
||||||
// Receive the value over the channel, or block.
|
|
||||||
recvOp.SetOperand(0, frame.taskHandle)
|
|
||||||
recvOp.SetOperand(recvOp.OperandsCount()-1, c.mod.NamedFunction("runtime.chanRecv"))
|
|
||||||
recvOp.SetOperand(2, llvm.Undef(c.i8ptrType))
|
|
||||||
bitcast.EraseFromParentAsInstruction()
|
|
||||||
|
|
||||||
// Yield to scheduler.
|
// Yield to scheduler.
|
||||||
c.builder.SetInsertPointBefore(llvm.NextInstruction(recvOp))
|
c.builder.SetInsertPointBefore(llvm.NextInstruction(recvOp))
|
||||||
continuePoint := c.builder.CreateCall(coroSuspendFunc, []llvm.Value{
|
continuePoint := c.builder.CreateCall(coroSuspendFunc, []llvm.Value{
|
||||||
@@ -532,32 +531,6 @@ func (c *Compiler) markAsyncFunctions() (needsScheduler bool, err error) {
|
|||||||
c.builder.SetInsertPointAtEnd(recvOp.InstructionParent())
|
c.builder.SetInsertPointAtEnd(recvOp.InstructionParent())
|
||||||
sw.AddCase(llvm.ConstInt(c.ctx.Int8Type(), 0, false), wakeup)
|
sw.AddCase(llvm.ConstInt(c.ctx.Int8Type(), 0, false), wakeup)
|
||||||
sw.AddCase(llvm.ConstInt(c.ctx.Int8Type(), 1, false), frame.cleanupBlock)
|
sw.AddCase(llvm.ConstInt(c.ctx.Int8Type(), 1, false), frame.cleanupBlock)
|
||||||
|
|
||||||
// The value to receive is stored in taskState.data:
|
|
||||||
// runtime.chanRecv(coroutine, ch)
|
|
||||||
// promise := coroutine.promise()
|
|
||||||
// valueReceived := *(*valueType)(&promise.data)
|
|
||||||
// ok := promise.commaOk
|
|
||||||
c.builder.SetInsertPointBefore(wakeup.FirstInstruction())
|
|
||||||
promiseType := c.mod.GetTypeByName("runtime.taskState")
|
|
||||||
promiseRaw := c.builder.CreateCall(coroPromiseFunc, []llvm.Value{
|
|
||||||
frame.taskHandle,
|
|
||||||
llvm.ConstInt(c.ctx.Int32Type(), uint64(c.targetData.PrefTypeAlignment(promiseType)), false),
|
|
||||||
llvm.ConstInt(c.ctx.Int1Type(), 0, false),
|
|
||||||
}, "task.promise.raw")
|
|
||||||
promise := c.builder.CreateBitCast(promiseRaw, llvm.PointerType(promiseType, 0), "task.promise")
|
|
||||||
dataPtr := c.builder.CreateGEP(promise, []llvm.Value{
|
|
||||||
llvm.ConstInt(c.ctx.Int32Type(), 0, false),
|
|
||||||
llvm.ConstInt(c.ctx.Int32Type(), 2, false),
|
|
||||||
}, "task.promise.data")
|
|
||||||
valueAlloca.ReplaceAllUsesWith(c.builder.CreateBitCast(dataPtr, valueAlloca.Type(), ""))
|
|
||||||
valueAlloca.EraseFromParentAsInstruction()
|
|
||||||
commaOkPtr := c.builder.CreateGEP(promise, []llvm.Value{
|
|
||||||
llvm.ConstInt(c.ctx.Int32Type(), 0, false),
|
|
||||||
llvm.ConstInt(c.ctx.Int32Type(), 1, false),
|
|
||||||
}, "task.promise.comma-ok")
|
|
||||||
commaOk.ReplaceAllUsesWith(commaOkPtr)
|
|
||||||
recvOp.SetOperand(3, llvm.Undef(commaOk.Type()))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true, c.lowerMakeGoroutineCalls()
|
return true, c.lowerMakeGoroutineCalls()
|
||||||
|
|||||||
@@ -68,11 +68,7 @@ func (c *Compiler) emitAsmFull(frame *Frame, instr *ssa.CallCommon) (llvm.Value,
|
|||||||
}
|
}
|
||||||
key := constant.StringVal(r.Key.(*ssa.Const).Value)
|
key := constant.StringVal(r.Key.(*ssa.Const).Value)
|
||||||
//println("value:", r.Value.(*ssa.MakeInterface).X.String())
|
//println("value:", r.Value.(*ssa.MakeInterface).X.String())
|
||||||
value, err := c.parseExpr(frame, r.Value.(*ssa.MakeInterface).X)
|
registers[key] = c.getValue(frame, r.Value.(*ssa.MakeInterface).X)
|
||||||
if err != nil {
|
|
||||||
return llvm.Value{}, err
|
|
||||||
}
|
|
||||||
registers[key] = value
|
|
||||||
case *ssa.Call:
|
case *ssa.Call:
|
||||||
if r.Common() == instr {
|
if r.Common() == instr {
|
||||||
break
|
break
|
||||||
@@ -145,10 +141,7 @@ func (c *Compiler) emitSVCall(frame *Frame, args []ssa.Value) (llvm.Value, error
|
|||||||
} else {
|
} else {
|
||||||
constraints += ",{r" + strconv.Itoa(i) + "}"
|
constraints += ",{r" + strconv.Itoa(i) + "}"
|
||||||
}
|
}
|
||||||
llvmValue, err := c.parseExpr(frame, arg)
|
llvmValue := c.getValue(frame, arg)
|
||||||
if err != nil {
|
|
||||||
return llvm.Value{}, err
|
|
||||||
}
|
|
||||||
llvmArgs = append(llvmArgs, llvmValue)
|
llvmArgs = append(llvmArgs, llvmValue)
|
||||||
argTypes = append(argTypes, llvmValue.Type())
|
argTypes = append(argTypes, llvmValue.Type())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -527,11 +527,40 @@ func (p *lowerInterfacesPass) replaceInvokeWithCall(use llvm.Value, typ *typeInf
|
|||||||
}
|
}
|
||||||
inttoptr := inttoptrs[0]
|
inttoptr := inttoptrs[0]
|
||||||
function := typ.getMethod(signature).function
|
function := typ.getMethod(signature).function
|
||||||
if inttoptr.Type() != function.Type() {
|
if inttoptr.Type() == function.Type() {
|
||||||
p.builder.SetInsertPointBefore(use)
|
// Easy case: the types are the same. Simply replace the inttoptr
|
||||||
function = p.builder.CreateBitCast(function, inttoptr.Type(), "")
|
// result (which is directly called) with the actual function.
|
||||||
|
inttoptr.ReplaceAllUsesWith(function)
|
||||||
|
} else {
|
||||||
|
// Harder case: the type is not actually the same. Go through each call
|
||||||
|
// (of which there should be only one), extract the receiver params for
|
||||||
|
// this call and replace the call with a direct call to the target
|
||||||
|
// function.
|
||||||
|
for _, call := range getUses(inttoptr) {
|
||||||
|
if call.IsACallInst().IsNil() || call.CalledValue() != inttoptr {
|
||||||
|
panic("expected the inttoptr to be called as a method, this is not a method call")
|
||||||
|
}
|
||||||
|
operands := make([]llvm.Value, call.OperandsCount()-1)
|
||||||
|
for i := range operands {
|
||||||
|
operands[i] = call.Operand(i)
|
||||||
|
}
|
||||||
|
paramTypes := function.Type().ElementType().ParamTypes()
|
||||||
|
receiverParamTypes := paramTypes[:len(paramTypes)-(len(operands)-1)]
|
||||||
|
methodParamTypes := paramTypes[len(paramTypes)-(len(operands)-1):]
|
||||||
|
for i, methodParamType := range methodParamTypes {
|
||||||
|
if methodParamType != operands[i+1].Type() {
|
||||||
|
panic("expected method call param type and function param type to be the same")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p.builder.SetInsertPointBefore(call)
|
||||||
|
receiverParams := p.emitPointerUnpack(operands[0], receiverParamTypes)
|
||||||
|
result := p.builder.CreateCall(function, append(receiverParams, operands[1:]...), "")
|
||||||
|
if result.Type().TypeKind() != llvm.VoidTypeKind {
|
||||||
|
call.ReplaceAllUsesWith(result)
|
||||||
|
}
|
||||||
|
call.EraseFromParentAsInstruction()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
inttoptr.ReplaceAllUsesWith(function)
|
|
||||||
inttoptr.EraseFromParentAsInstruction()
|
inttoptr.EraseFromParentAsInstruction()
|
||||||
use.EraseFromParentAsInstruction()
|
use.EraseFromParentAsInstruction()
|
||||||
}
|
}
|
||||||
|
|||||||
+17
-122
@@ -23,37 +23,7 @@ import (
|
|||||||
//
|
//
|
||||||
// An interface value is a {typecode, value} tuple, or {i16, i8*} to be exact.
|
// An interface value is a {typecode, value} tuple, or {i16, i8*} to be exact.
|
||||||
func (c *Compiler) parseMakeInterface(val llvm.Value, typ types.Type, pos token.Pos) (llvm.Value, error) {
|
func (c *Compiler) parseMakeInterface(val llvm.Value, typ types.Type, pos token.Pos) (llvm.Value, error) {
|
||||||
var itfValue llvm.Value
|
itfValue := c.emitPointerPack([]llvm.Value{val})
|
||||||
size := c.targetData.TypeAllocSize(val.Type())
|
|
||||||
if size > c.targetData.TypeAllocSize(c.i8ptrType) {
|
|
||||||
// Allocate on the heap and put a pointer in the interface.
|
|
||||||
// TODO: escape analysis.
|
|
||||||
sizeValue := llvm.ConstInt(c.uintptrType, size, false)
|
|
||||||
alloc := c.createRuntimeCall("alloc", []llvm.Value{sizeValue}, "makeinterface.alloc")
|
|
||||||
itfValueCast := c.builder.CreateBitCast(alloc, llvm.PointerType(val.Type(), 0), "makeinterface.cast.value")
|
|
||||||
c.builder.CreateStore(val, itfValueCast)
|
|
||||||
itfValue = c.builder.CreateBitCast(itfValueCast, c.i8ptrType, "makeinterface.cast.i8ptr")
|
|
||||||
} else if size == 0 {
|
|
||||||
itfValue = llvm.ConstPointerNull(c.i8ptrType)
|
|
||||||
} else {
|
|
||||||
// Directly place the value in the interface.
|
|
||||||
switch val.Type().TypeKind() {
|
|
||||||
case llvm.IntegerTypeKind:
|
|
||||||
itfValue = c.builder.CreateIntToPtr(val, c.i8ptrType, "makeinterface.cast.int")
|
|
||||||
case llvm.PointerTypeKind:
|
|
||||||
itfValue = c.builder.CreateBitCast(val, c.i8ptrType, "makeinterface.cast.ptr")
|
|
||||||
case llvm.StructTypeKind, llvm.FloatTypeKind, llvm.DoubleTypeKind:
|
|
||||||
// A bitcast would be useful here, but bitcast doesn't allow
|
|
||||||
// aggregate types. So we'll bitcast it using an alloca.
|
|
||||||
// Hopefully this will get optimized away.
|
|
||||||
mem := c.builder.CreateAlloca(c.i8ptrType, "makeinterface.cast.struct")
|
|
||||||
memStructPtr := c.builder.CreateBitCast(mem, llvm.PointerType(val.Type(), 0), "makeinterface.cast.struct.cast")
|
|
||||||
c.builder.CreateStore(val, memStructPtr)
|
|
||||||
itfValue = c.builder.CreateLoad(mem, "makeinterface.cast.load")
|
|
||||||
default:
|
|
||||||
return llvm.Value{}, c.makeError(pos, "todo: makeinterface: cast small type to i8*")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
itfTypeCodeGlobal := c.getTypeCode(typ)
|
itfTypeCodeGlobal := c.getTypeCode(typ)
|
||||||
itfMethodSetGlobal, err := c.getTypeMethodSet(typ)
|
itfMethodSetGlobal, err := c.getTypeMethodSet(typ)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -274,19 +244,9 @@ func (c *Compiler) getMethodSignature(method *types.Func) llvm.Value {
|
|||||||
//
|
//
|
||||||
// Type asserts on concrete types are trivial: just compare type numbers. Type
|
// Type asserts on concrete types are trivial: just compare type numbers. Type
|
||||||
// asserts on interfaces are more difficult, see the comments in the function.
|
// asserts on interfaces are more difficult, see the comments in the function.
|
||||||
func (c *Compiler) parseTypeAssert(frame *Frame, expr *ssa.TypeAssert) (llvm.Value, error) {
|
func (c *Compiler) parseTypeAssert(frame *Frame, expr *ssa.TypeAssert) llvm.Value {
|
||||||
itf, err := c.parseExpr(frame, expr.X)
|
itf := c.getValue(frame, expr.X)
|
||||||
if err != nil {
|
assertedType := c.getLLVMType(expr.AssertedType)
|
||||||
return llvm.Value{}, err
|
|
||||||
}
|
|
||||||
assertedType, err := c.getLLVMType(expr.AssertedType)
|
|
||||||
if err != nil {
|
|
||||||
return llvm.Value{}, err
|
|
||||||
}
|
|
||||||
valueNil, err := c.getZeroValue(assertedType)
|
|
||||||
if err != nil {
|
|
||||||
return llvm.Value{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
actualTypeNum := c.builder.CreateExtractValue(itf, 0, "interface.type")
|
actualTypeNum := c.builder.CreateExtractValue(itf, 0, "interface.type")
|
||||||
commaOk := llvm.Value{}
|
commaOk := llvm.Value{}
|
||||||
@@ -339,67 +299,35 @@ func (c *Compiler) parseTypeAssert(frame *Frame, expr *ssa.TypeAssert) (llvm.Val
|
|||||||
// Type assert on concrete type. Extract the underlying type from
|
// Type assert on concrete type. Extract the underlying type from
|
||||||
// the interface (but only after checking it matches).
|
// the interface (but only after checking it matches).
|
||||||
valuePtr := c.builder.CreateExtractValue(itf, 1, "typeassert.value.ptr")
|
valuePtr := c.builder.CreateExtractValue(itf, 1, "typeassert.value.ptr")
|
||||||
size := c.targetData.TypeAllocSize(assertedType)
|
valueOk = c.emitPointerUnpack(valuePtr, []llvm.Type{assertedType})[0]
|
||||||
if size > c.targetData.TypeAllocSize(c.i8ptrType) {
|
|
||||||
// Value was stored in an allocated buffer, load it from there.
|
|
||||||
valuePtrCast := c.builder.CreateBitCast(valuePtr, llvm.PointerType(assertedType, 0), "")
|
|
||||||
valueOk = c.builder.CreateLoad(valuePtrCast, "typeassert.value.ok")
|
|
||||||
} else if size == 0 {
|
|
||||||
valueOk, err = c.getZeroValue(assertedType)
|
|
||||||
if err != nil {
|
|
||||||
return llvm.Value{}, err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Value was stored directly in the interface.
|
|
||||||
switch assertedType.TypeKind() {
|
|
||||||
case llvm.IntegerTypeKind:
|
|
||||||
valueOk = c.builder.CreatePtrToInt(valuePtr, assertedType, "typeassert.value.ok")
|
|
||||||
case llvm.PointerTypeKind:
|
|
||||||
valueOk = c.builder.CreateBitCast(valuePtr, assertedType, "typeassert.value.ok")
|
|
||||||
default: // struct, float, etc.
|
|
||||||
// A bitcast would be useful here, but bitcast doesn't allow
|
|
||||||
// aggregate types. So we'll bitcast it using an alloca.
|
|
||||||
// Hopefully this will get optimized away.
|
|
||||||
mem := c.builder.CreateAlloca(c.i8ptrType, "")
|
|
||||||
c.builder.CreateStore(valuePtr, mem)
|
|
||||||
memCast := c.builder.CreateBitCast(mem, llvm.PointerType(assertedType, 0), "")
|
|
||||||
valueOk = c.builder.CreateLoad(memCast, "typeassert.value.ok")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
c.builder.CreateBr(nextBlock)
|
c.builder.CreateBr(nextBlock)
|
||||||
|
|
||||||
// Continue after the if statement.
|
// Continue after the if statement.
|
||||||
c.builder.SetInsertPointAtEnd(nextBlock)
|
c.builder.SetInsertPointAtEnd(nextBlock)
|
||||||
phi := c.builder.CreatePHI(assertedType, "typeassert.value")
|
phi := c.builder.CreatePHI(assertedType, "typeassert.value")
|
||||||
phi.AddIncoming([]llvm.Value{valueNil, valueOk}, []llvm.BasicBlock{prevBlock, okBlock})
|
phi.AddIncoming([]llvm.Value{c.getZeroValue(assertedType), valueOk}, []llvm.BasicBlock{prevBlock, okBlock})
|
||||||
|
|
||||||
if expr.CommaOk {
|
if expr.CommaOk {
|
||||||
tuple := c.ctx.ConstStruct([]llvm.Value{llvm.Undef(assertedType), llvm.Undef(c.ctx.Int1Type())}, false) // create empty tuple
|
tuple := c.ctx.ConstStruct([]llvm.Value{llvm.Undef(assertedType), llvm.Undef(c.ctx.Int1Type())}, false) // create empty tuple
|
||||||
tuple = c.builder.CreateInsertValue(tuple, phi, 0, "") // insert value
|
tuple = c.builder.CreateInsertValue(tuple, phi, 0, "") // insert value
|
||||||
tuple = c.builder.CreateInsertValue(tuple, commaOk, 1, "") // insert 'comma ok' boolean
|
tuple = c.builder.CreateInsertValue(tuple, commaOk, 1, "") // insert 'comma ok' boolean
|
||||||
return tuple, nil
|
return tuple
|
||||||
} else {
|
} else {
|
||||||
// This is kind of dirty as the branch above becomes mostly useless,
|
// This is kind of dirty as the branch above becomes mostly useless,
|
||||||
// but hopefully this gets optimized away.
|
// but hopefully this gets optimized away.
|
||||||
c.createRuntimeCall("interfaceTypeAssert", []llvm.Value{commaOk}, "")
|
c.createRuntimeCall("interfaceTypeAssert", []llvm.Value{commaOk}, "")
|
||||||
return phi, nil
|
return phi
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// getInvokeCall creates and returns the function pointer and parameters of an
|
// getInvokeCall creates and returns the function pointer and parameters of an
|
||||||
// interface call. It can be used in a call or defer instruction.
|
// interface call. It can be used in a call or defer instruction.
|
||||||
func (c *Compiler) getInvokeCall(frame *Frame, instr *ssa.CallCommon) (llvm.Value, []llvm.Value, error) {
|
func (c *Compiler) getInvokeCall(frame *Frame, instr *ssa.CallCommon) (llvm.Value, []llvm.Value) {
|
||||||
// Call an interface method with dynamic dispatch.
|
// Call an interface method with dynamic dispatch.
|
||||||
itf, err := c.parseExpr(frame, instr.Value) // interface
|
itf := c.getValue(frame, instr.Value) // interface
|
||||||
if err != nil {
|
|
||||||
return llvm.Value{}, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
llvmFnType, err := c.getRawFuncType(instr.Method.Type().(*types.Signature))
|
llvmFnType := c.getRawFuncType(instr.Method.Type().(*types.Signature))
|
||||||
if err != nil {
|
|
||||||
return llvm.Value{}, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
typecode := c.builder.CreateExtractValue(itf, 0, "invoke.typecode")
|
typecode := c.builder.CreateExtractValue(itf, 0, "invoke.typecode")
|
||||||
values := []llvm.Value{
|
values := []llvm.Value{
|
||||||
@@ -413,11 +341,7 @@ func (c *Compiler) getInvokeCall(frame *Frame, instr *ssa.CallCommon) (llvm.Valu
|
|||||||
|
|
||||||
args := []llvm.Value{receiverValue}
|
args := []llvm.Value{receiverValue}
|
||||||
for _, arg := range instr.Args {
|
for _, arg := range instr.Args {
|
||||||
val, err := c.parseExpr(frame, arg)
|
args = append(args, c.getValue(frame, arg))
|
||||||
if err != nil {
|
|
||||||
return llvm.Value{}, nil, err
|
|
||||||
}
|
|
||||||
args = append(args, val)
|
|
||||||
}
|
}
|
||||||
// Add the context parameter. An interface call never takes a context but we
|
// Add the context parameter. An interface call never takes a context but we
|
||||||
// have to supply the parameter anyway.
|
// have to supply the parameter anyway.
|
||||||
@@ -425,7 +349,7 @@ func (c *Compiler) getInvokeCall(frame *Frame, instr *ssa.CallCommon) (llvm.Valu
|
|||||||
// Add the parent goroutine handle.
|
// Add the parent goroutine handle.
|
||||||
args = append(args, llvm.Undef(c.i8ptrType))
|
args = append(args, llvm.Undef(c.i8ptrType))
|
||||||
|
|
||||||
return fnCast, args, nil
|
return fnCast, args
|
||||||
}
|
}
|
||||||
|
|
||||||
// interfaceInvokeWrapper keeps some state between getInterfaceInvokeWrapper and
|
// interfaceInvokeWrapper keeps some state between getInterfaceInvokeWrapper and
|
||||||
@@ -450,10 +374,7 @@ func (c *Compiler) getInterfaceInvokeWrapper(f *ir.Function) (llvm.Value, error)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Get the expanded receiver type.
|
// Get the expanded receiver type.
|
||||||
receiverType, err := c.getLLVMType(f.Params[0].Type())
|
receiverType := c.getLLVMType(f.Params[0].Type())
|
||||||
if err != nil {
|
|
||||||
return llvm.Value{}, err
|
|
||||||
}
|
|
||||||
expandedReceiverType := c.expandFormalParamType(receiverType)
|
expandedReceiverType := c.expandFormalParamType(receiverType)
|
||||||
|
|
||||||
// Does this method even need any wrapping?
|
// Does this method even need any wrapping?
|
||||||
@@ -480,7 +401,7 @@ func (c *Compiler) getInterfaceInvokeWrapper(f *ir.Function) (llvm.Value, error)
|
|||||||
|
|
||||||
// createInterfaceInvokeWrapper finishes the work of getInterfaceInvokeWrapper,
|
// createInterfaceInvokeWrapper finishes the work of getInterfaceInvokeWrapper,
|
||||||
// see that function for details.
|
// see that function for details.
|
||||||
func (c *Compiler) createInterfaceInvokeWrapper(state interfaceInvokeWrapper) error {
|
func (c *Compiler) createInterfaceInvokeWrapper(state interfaceInvokeWrapper) {
|
||||||
wrapper := state.wrapper
|
wrapper := state.wrapper
|
||||||
fn := state.fn
|
fn := state.fn
|
||||||
receiverType := state.receiverType
|
receiverType := state.receiverType
|
||||||
@@ -490,10 +411,7 @@ func (c *Compiler) createInterfaceInvokeWrapper(state interfaceInvokeWrapper) er
|
|||||||
// add debug info if needed
|
// add debug info if needed
|
||||||
if c.Debug {
|
if c.Debug {
|
||||||
pos := c.ir.Program.Fset.Position(fn.Pos())
|
pos := c.ir.Program.Fset.Position(fn.Pos())
|
||||||
difunc, err := c.attachDebugInfoRaw(fn, wrapper, "$invoke", pos.Filename, pos.Line)
|
difunc := c.attachDebugInfoRaw(fn, wrapper, "$invoke", pos.Filename, pos.Line)
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
c.builder.SetCurrentDebugLocation(uint(pos.Line), uint(pos.Column), difunc, llvm.Metadata{})
|
c.builder.SetCurrentDebugLocation(uint(pos.Line), uint(pos.Column), difunc, llvm.Metadata{})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -501,28 +419,7 @@ func (c *Compiler) createInterfaceInvokeWrapper(state interfaceInvokeWrapper) er
|
|||||||
block := c.ctx.AddBasicBlock(wrapper, "entry")
|
block := c.ctx.AddBasicBlock(wrapper, "entry")
|
||||||
c.builder.SetInsertPointAtEnd(block)
|
c.builder.SetInsertPointAtEnd(block)
|
||||||
|
|
||||||
var receiverPtr llvm.Value
|
receiverValue := c.emitPointerUnpack(wrapper.Param(0), []llvm.Type{receiverType})[0]
|
||||||
if c.targetData.TypeAllocSize(receiverType) > c.targetData.TypeAllocSize(c.i8ptrType) {
|
|
||||||
// The receiver is passed in using a pointer. We have to load it here
|
|
||||||
// and pass it by value to the real function.
|
|
||||||
|
|
||||||
// Load the underlying value.
|
|
||||||
receiverPtrType := llvm.PointerType(receiverType, 0)
|
|
||||||
receiverPtr = c.builder.CreateBitCast(wrapper.Param(0), receiverPtrType, "receiver.ptr")
|
|
||||||
} else {
|
|
||||||
// The value is stored in the interface, but it is of type struct which
|
|
||||||
// is expanded to multiple parameters (e.g. {i8, i8}). So we have to
|
|
||||||
// receive the struct as parameter, expand it, and pass it on to the
|
|
||||||
// real function.
|
|
||||||
|
|
||||||
// Cast the passed-in i8* to the struct value (using an alloca) and
|
|
||||||
// extract its values.
|
|
||||||
alloca := c.builder.CreateAlloca(c.i8ptrType, "receiver.alloca")
|
|
||||||
c.builder.CreateStore(wrapper.Param(0), alloca)
|
|
||||||
receiverPtr = c.builder.CreateBitCast(alloca, llvm.PointerType(receiverType, 0), "receiver.ptr")
|
|
||||||
}
|
|
||||||
|
|
||||||
receiverValue := c.builder.CreateLoad(receiverPtr, "receiver")
|
|
||||||
params := append(c.expandFormalParam(receiverValue), wrapper.Params()[1:]...)
|
params := append(c.expandFormalParam(receiverValue), wrapper.Params()[1:]...)
|
||||||
if fn.LLVMFn.Type().ElementType().ReturnType().TypeKind() == llvm.VoidTypeKind {
|
if fn.LLVMFn.Type().ElementType().ReturnType().TypeKind() == llvm.VoidTypeKind {
|
||||||
c.builder.CreateCall(fn.LLVMFn, params, "")
|
c.builder.CreateCall(fn.LLVMFn, params, "")
|
||||||
@@ -531,6 +428,4 @@ func (c *Compiler) createInterfaceInvokeWrapper(state interfaceInvokeWrapper) er
|
|||||||
ret := c.builder.CreateCall(fn.LLVMFn, params, "ret")
|
ret := c.builder.CreateCall(fn.LLVMFn, params, "ret")
|
||||||
c.builder.CreateRet(ret)
|
c.builder.CreateRet(ret)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,17 @@ func getUses(value llvm.Value) []llvm.Value {
|
|||||||
return uses
|
return uses
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// getLifetimeEndFunc returns the llvm.lifetime.end intrinsic and creates it
|
||||||
|
// first if it doesn't exist yet.
|
||||||
|
func (c *Compiler) getLifetimeEndFunc() llvm.Value {
|
||||||
|
fn := c.mod.NamedFunction("llvm.lifetime.end.p0i8")
|
||||||
|
if fn.IsNil() {
|
||||||
|
fnType := llvm.FunctionType(c.ctx.VoidType(), []llvm.Type{c.ctx.Int64Type(), c.i8ptrType}, false)
|
||||||
|
fn = llvm.AddFunction(c.mod, "llvm.lifetime.end.p0i8", fnType)
|
||||||
|
}
|
||||||
|
return fn
|
||||||
|
}
|
||||||
|
|
||||||
// splitBasicBlock splits a LLVM basic block into two parts. All instructions
|
// splitBasicBlock splits a LLVM basic block into two parts. All instructions
|
||||||
// after afterInst are moved into a new basic block (created right after the
|
// after afterInst are moved into a new basic block (created right after the
|
||||||
// current one) with the given name.
|
// current one) with the given name.
|
||||||
|
|||||||
+3
-8
@@ -10,10 +10,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func (c *Compiler) emitMapLookup(keyType, valueType types.Type, m, key llvm.Value, commaOk bool, pos token.Pos) (llvm.Value, error) {
|
func (c *Compiler) emitMapLookup(keyType, valueType types.Type, m, key llvm.Value, commaOk bool, pos token.Pos) (llvm.Value, error) {
|
||||||
llvmValueType, err := c.getLLVMType(valueType)
|
llvmValueType := c.getLLVMType(valueType)
|
||||||
if err != nil {
|
|
||||||
return llvm.Value{}, err
|
|
||||||
}
|
|
||||||
mapValueAlloca := c.builder.CreateAlloca(llvmValueType, "hashmap.value")
|
mapValueAlloca := c.builder.CreateAlloca(llvmValueType, "hashmap.value")
|
||||||
mapValuePtr := c.builder.CreateBitCast(mapValueAlloca, c.i8ptrType, "hashmap.valueptr")
|
mapValuePtr := c.builder.CreateBitCast(mapValueAlloca, c.i8ptrType, "hashmap.valueptr")
|
||||||
var commaOkValue llvm.Value
|
var commaOkValue llvm.Value
|
||||||
@@ -42,7 +39,7 @@ func (c *Compiler) emitMapLookup(keyType, valueType types.Type, m, key llvm.Valu
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Compiler) emitMapUpdate(keyType types.Type, m, key, value llvm.Value, pos token.Pos) error {
|
func (c *Compiler) emitMapUpdate(keyType types.Type, m, key, value llvm.Value, pos token.Pos) {
|
||||||
valueAlloca := c.builder.CreateAlloca(value.Type(), "hashmap.value")
|
valueAlloca := c.builder.CreateAlloca(value.Type(), "hashmap.value")
|
||||||
c.builder.CreateStore(value, valueAlloca)
|
c.builder.CreateStore(value, valueAlloca)
|
||||||
valuePtr := c.builder.CreateBitCast(valueAlloca, c.i8ptrType, "hashmap.valueptr")
|
valuePtr := c.builder.CreateBitCast(valueAlloca, c.i8ptrType, "hashmap.valueptr")
|
||||||
@@ -51,7 +48,6 @@ func (c *Compiler) emitMapUpdate(keyType types.Type, m, key, value llvm.Value, p
|
|||||||
// key is a string
|
// key is a string
|
||||||
params := []llvm.Value{m, key, valuePtr}
|
params := []llvm.Value{m, key, valuePtr}
|
||||||
c.createRuntimeCall("hashmapStringSet", params, "")
|
c.createRuntimeCall("hashmapStringSet", params, "")
|
||||||
return nil
|
|
||||||
} else if hashmapIsBinaryKey(keyType) {
|
} else if hashmapIsBinaryKey(keyType) {
|
||||||
// key can be compared with runtime.memequal
|
// key can be compared with runtime.memequal
|
||||||
keyAlloca := c.builder.CreateAlloca(key.Type(), "hashmap.key")
|
keyAlloca := c.builder.CreateAlloca(key.Type(), "hashmap.key")
|
||||||
@@ -59,9 +55,8 @@ func (c *Compiler) emitMapUpdate(keyType types.Type, m, key, value llvm.Value, p
|
|||||||
keyPtr := c.builder.CreateBitCast(keyAlloca, c.i8ptrType, "hashmap.keyptr")
|
keyPtr := c.builder.CreateBitCast(keyAlloca, c.i8ptrType, "hashmap.keyptr")
|
||||||
params := []llvm.Value{m, keyPtr, valuePtr}
|
params := []llvm.Value{m, keyPtr, valuePtr}
|
||||||
c.createRuntimeCall("hashmapBinarySet", params, "")
|
c.createRuntimeCall("hashmapBinarySet", params, "")
|
||||||
return nil
|
|
||||||
} else {
|
} else {
|
||||||
return c.makeError(pos, "only strings, bools, ints or structs of bools/ints are supported as map keys, but got: "+keyType.String())
|
c.addError(pos, "only strings, bools, ints or structs of bools/ints are supported as map keys, but got: "+keyType.String())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+24
-13
@@ -18,6 +18,10 @@ func (c *Compiler) Optimize(optLevel, sizeLevel int, inlinerThreshold uint) erro
|
|||||||
}
|
}
|
||||||
builder.AddCoroutinePassesToExtensionPoints()
|
builder.AddCoroutinePassesToExtensionPoints()
|
||||||
|
|
||||||
|
if c.PanicStrategy == "trap" {
|
||||||
|
c.replacePanicsWithTrap() // -panic=trap
|
||||||
|
}
|
||||||
|
|
||||||
// Run function passes for each function.
|
// Run function passes for each function.
|
||||||
funcPasses := llvm.NewFunctionPassManagerForModule(c.mod)
|
funcPasses := llvm.NewFunctionPassManagerForModule(c.mod)
|
||||||
defer funcPasses.Dispose()
|
defer funcPasses.Dispose()
|
||||||
@@ -113,6 +117,25 @@ func (c *Compiler) Optimize(optLevel, sizeLevel int, inlinerThreshold uint) erro
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Replace panic calls with calls to llvm.trap, to reduce code size. This is the
|
||||||
|
// -panic=trap intrinsic.
|
||||||
|
func (c *Compiler) replacePanicsWithTrap() {
|
||||||
|
trap := c.mod.NamedFunction("llvm.trap")
|
||||||
|
for _, name := range []string{"runtime._panic", "runtime.runtimePanic"} {
|
||||||
|
fn := c.mod.NamedFunction(name)
|
||||||
|
if fn.IsNil() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for _, use := range getUses(fn) {
|
||||||
|
if use.IsACallInst().IsNil() || use.CalledValue() != fn {
|
||||||
|
panic("expected use of a panic function to be a call")
|
||||||
|
}
|
||||||
|
c.builder.SetInsertPointBefore(use)
|
||||||
|
c.builder.CreateCall(trap, nil, "")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Eliminate created but not used maps.
|
// Eliminate created but not used maps.
|
||||||
//
|
//
|
||||||
// In the future, this should statically allocate created but never modified
|
// In the future, this should statically allocate created but never modified
|
||||||
@@ -249,7 +272,7 @@ func (c *Compiler) OptimizeAllocs() {
|
|||||||
sizeInWords := (size + uint64(alignment) - 1) / uint64(alignment)
|
sizeInWords := (size + uint64(alignment) - 1) / uint64(alignment)
|
||||||
allocaType := llvm.ArrayType(c.ctx.IntType(alignment*8), int(sizeInWords))
|
allocaType := llvm.ArrayType(c.ctx.IntType(alignment*8), int(sizeInWords))
|
||||||
alloca := c.builder.CreateAlloca(allocaType, "stackalloc.alloca")
|
alloca := c.builder.CreateAlloca(allocaType, "stackalloc.alloca")
|
||||||
zero, _ := c.getZeroValue(alloca.Type().ElementType())
|
zero := c.getZeroValue(alloca.Type().ElementType())
|
||||||
c.builder.CreateStore(zero, alloca)
|
c.builder.CreateStore(zero, alloca)
|
||||||
stackalloc := c.builder.CreateBitCast(alloca, bitcast.Type(), "stackalloc")
|
stackalloc := c.builder.CreateBitCast(alloca, bitcast.Type(), "stackalloc")
|
||||||
bitcast.ReplaceAllUsesWith(stackalloc)
|
bitcast.ReplaceAllUsesWith(stackalloc)
|
||||||
@@ -284,18 +307,6 @@ func (c *Compiler) doesEscape(value llvm.Value) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
} else if use.IsACallInst() != nilValue {
|
} else if use.IsACallInst() != nilValue {
|
||||||
// Call only escapes when the (pointer) parameter is not marked
|
|
||||||
// "nocapture". This flag means that the parameter does not escape
|
|
||||||
// the give function.
|
|
||||||
if use.CalledValue().IsAFunction() != nilValue {
|
|
||||||
if use.CalledValue().IsDeclaration() {
|
|
||||||
// Kind of dirty: assume external functions don't let
|
|
||||||
// pointers escape.
|
|
||||||
// TODO: introduce //go:noescape that sets the 'nocapture'
|
|
||||||
// flag on each input parameter.
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !c.hasFlag(use, value, "nocapture") {
|
if !c.hasFlag(use, value, "nocapture") {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -113,6 +113,9 @@ func (s *StdSizes) Sizeof(T types.Type) int64 {
|
|||||||
if k == types.Uintptr {
|
if k == types.Uintptr {
|
||||||
return s.PtrSize
|
return s.PtrSize
|
||||||
}
|
}
|
||||||
|
if k == types.UnsafePointer {
|
||||||
|
return s.PtrSize
|
||||||
|
}
|
||||||
panic("unknown basic type: " + t.String())
|
panic("unknown basic type: " + t.String())
|
||||||
case *types.Array:
|
case *types.Array:
|
||||||
n := t.Len()
|
n := t.Len()
|
||||||
|
|||||||
+3
-12
@@ -51,10 +51,7 @@ func (c *Compiler) emitSyscall(frame *Frame, call *ssa.CallCommon) (llvm.Value,
|
|||||||
"{r12}",
|
"{r12}",
|
||||||
"{r13}",
|
"{r13}",
|
||||||
}[i]
|
}[i]
|
||||||
llvmValue, err := c.parseExpr(frame, arg)
|
llvmValue := c.getValue(frame, arg)
|
||||||
if err != nil {
|
|
||||||
return llvm.Value{}, err
|
|
||||||
}
|
|
||||||
args = append(args, llvmValue)
|
args = append(args, llvmValue)
|
||||||
argTypes = append(argTypes, llvmValue.Type())
|
argTypes = append(argTypes, llvmValue.Type())
|
||||||
}
|
}
|
||||||
@@ -80,10 +77,7 @@ func (c *Compiler) emitSyscall(frame *Frame, call *ssa.CallCommon) (llvm.Value,
|
|||||||
"{r5}",
|
"{r5}",
|
||||||
"{r6}",
|
"{r6}",
|
||||||
}[i]
|
}[i]
|
||||||
llvmValue, err := c.parseExpr(frame, arg)
|
llvmValue := c.getValue(frame, arg)
|
||||||
if err != nil {
|
|
||||||
return llvm.Value{}, err
|
|
||||||
}
|
|
||||||
args = append(args, llvmValue)
|
args = append(args, llvmValue)
|
||||||
argTypes = append(argTypes, llvmValue.Type())
|
argTypes = append(argTypes, llvmValue.Type())
|
||||||
}
|
}
|
||||||
@@ -113,10 +107,7 @@ func (c *Compiler) emitSyscall(frame *Frame, call *ssa.CallCommon) (llvm.Value,
|
|||||||
"{x4}",
|
"{x4}",
|
||||||
"{x5}",
|
"{x5}",
|
||||||
}[i]
|
}[i]
|
||||||
llvmValue, err := c.parseExpr(frame, arg)
|
llvmValue := c.getValue(frame, arg)
|
||||||
if err != nil {
|
|
||||||
return llvm.Value{}, err
|
|
||||||
}
|
|
||||||
args = append(args, llvmValue)
|
args = append(args, llvmValue)
|
||||||
argTypes = append(argTypes, llvmValue.Type())
|
argTypes = append(argTypes, llvmValue.Type())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,108 @@
|
|||||||
|
package compiler
|
||||||
|
|
||||||
|
// This file contains utility functions to pack and unpack sets of values. It
|
||||||
|
// can take in a list of values and tries to store it efficiently in the pointer
|
||||||
|
// itself if possible and legal.
|
||||||
|
|
||||||
|
import (
|
||||||
|
"tinygo.org/x/go-llvm"
|
||||||
|
)
|
||||||
|
|
||||||
|
// emitPointerPack packs the list of values into a single pointer value using
|
||||||
|
// bitcasts, or else allocates a value on the heap if it cannot be packed in the
|
||||||
|
// pointer value directly. It returns the pointer with the packed data.
|
||||||
|
func (c *Compiler) emitPointerPack(values []llvm.Value) llvm.Value {
|
||||||
|
valueTypes := make([]llvm.Type, len(values))
|
||||||
|
for i, value := range values {
|
||||||
|
valueTypes[i] = value.Type()
|
||||||
|
}
|
||||||
|
packedType := c.ctx.StructType(valueTypes, false)
|
||||||
|
|
||||||
|
// Allocate memory for the packed data.
|
||||||
|
var packedAlloc, packedHeapAlloc llvm.Value
|
||||||
|
size := c.targetData.TypeAllocSize(packedType)
|
||||||
|
if size == 0 {
|
||||||
|
return llvm.ConstPointerNull(c.i8ptrType)
|
||||||
|
} else if len(values) == 1 && values[0].Type().TypeKind() == llvm.PointerTypeKind {
|
||||||
|
return c.builder.CreateBitCast(values[0], c.i8ptrType, "pack.ptr")
|
||||||
|
} else if size <= c.targetData.TypeAllocSize(c.i8ptrType) {
|
||||||
|
// Packed data fits in a pointer, so store it directly inside the
|
||||||
|
// pointer.
|
||||||
|
if len(values) == 1 && values[0].Type().TypeKind() == llvm.IntegerTypeKind {
|
||||||
|
// Try to keep this cast in SSA form.
|
||||||
|
return c.builder.CreateIntToPtr(values[0], c.i8ptrType, "pack.int")
|
||||||
|
}
|
||||||
|
// Because packedType is a struct and we have to cast it to a *i8, store
|
||||||
|
// it in an alloca first for bitcasting (store+bitcast+load).
|
||||||
|
packedAlloc = c.builder.CreateAlloca(packedType, "")
|
||||||
|
} else {
|
||||||
|
// Packed data is bigger than a pointer, so allocate it on the heap.
|
||||||
|
sizeValue := llvm.ConstInt(c.uintptrType, size, false)
|
||||||
|
packedHeapAlloc = c.createRuntimeCall("alloc", []llvm.Value{sizeValue}, "")
|
||||||
|
packedAlloc = c.builder.CreateBitCast(packedHeapAlloc, llvm.PointerType(packedType, 0), "")
|
||||||
|
}
|
||||||
|
// Store all values in the alloca or heap pointer.
|
||||||
|
for i, value := range values {
|
||||||
|
indices := []llvm.Value{
|
||||||
|
llvm.ConstInt(c.ctx.Int32Type(), 0, false),
|
||||||
|
llvm.ConstInt(c.ctx.Int32Type(), uint64(i), false),
|
||||||
|
}
|
||||||
|
gep := c.builder.CreateInBoundsGEP(packedAlloc, indices, "")
|
||||||
|
c.builder.CreateStore(value, gep)
|
||||||
|
}
|
||||||
|
|
||||||
|
if packedHeapAlloc.IsNil() {
|
||||||
|
// Load value (as *i8) from the alloca.
|
||||||
|
packedAlloc = c.builder.CreateBitCast(packedAlloc, llvm.PointerType(c.i8ptrType, 0), "")
|
||||||
|
return c.builder.CreateLoad(packedAlloc, "")
|
||||||
|
} else {
|
||||||
|
// Get the original heap allocation pointer, which already is an *i8.
|
||||||
|
return packedHeapAlloc
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// emitPointerUnpack extracts a list of values packed using emitPointerPack.
|
||||||
|
func (c *Compiler) emitPointerUnpack(ptr llvm.Value, valueTypes []llvm.Type) []llvm.Value {
|
||||||
|
packedType := c.ctx.StructType(valueTypes, false)
|
||||||
|
|
||||||
|
// Get a correctly-typed pointer to the packed data.
|
||||||
|
var packedAlloc llvm.Value
|
||||||
|
size := c.targetData.TypeAllocSize(packedType)
|
||||||
|
if size == 0 {
|
||||||
|
// No data to unpack.
|
||||||
|
} else if len(valueTypes) == 1 && valueTypes[0].TypeKind() == llvm.PointerTypeKind {
|
||||||
|
// A single pointer is always stored directly.
|
||||||
|
return []llvm.Value{c.builder.CreateBitCast(ptr, valueTypes[0], "unpack.ptr")}
|
||||||
|
} else if size <= c.targetData.TypeAllocSize(c.i8ptrType) {
|
||||||
|
// Packed data stored directly in pointer.
|
||||||
|
if len(valueTypes) == 1 && valueTypes[0].TypeKind() == llvm.IntegerTypeKind {
|
||||||
|
// Keep this cast in SSA form.
|
||||||
|
return []llvm.Value{c.builder.CreatePtrToInt(ptr, valueTypes[0], "unpack.int")}
|
||||||
|
}
|
||||||
|
// Fallback: load it using an alloca.
|
||||||
|
packedRawAlloc := c.builder.CreateAlloca(llvm.PointerType(c.i8ptrType, 0), "unpack.raw.alloc")
|
||||||
|
packedRawValue := c.builder.CreateBitCast(ptr, llvm.PointerType(c.i8ptrType, 0), "unpack.raw.value")
|
||||||
|
c.builder.CreateStore(packedRawValue, packedRawAlloc)
|
||||||
|
packedAlloc = c.builder.CreateBitCast(packedRawAlloc, llvm.PointerType(packedType, 0), "unpack.alloc")
|
||||||
|
} else {
|
||||||
|
// Packed data stored on the heap. Bitcast the passed-in pointer to the
|
||||||
|
// correct pointer type.
|
||||||
|
packedAlloc = c.builder.CreateBitCast(ptr, llvm.PointerType(packedType, 0), "unpack.raw.ptr")
|
||||||
|
}
|
||||||
|
// Load each value from the packed data.
|
||||||
|
values := make([]llvm.Value, len(valueTypes))
|
||||||
|
for i, valueType := range valueTypes {
|
||||||
|
if c.targetData.TypeAllocSize(valueType) == 0 {
|
||||||
|
// This value has length zero, so there's nothing to load.
|
||||||
|
values[i] = c.getZeroValue(valueType)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
indices := []llvm.Value{
|
||||||
|
llvm.ConstInt(c.ctx.Int32Type(), 0, false),
|
||||||
|
llvm.ConstInt(c.ctx.Int32Type(), uint64(i), false),
|
||||||
|
}
|
||||||
|
gep := c.builder.CreateInBoundsGEP(packedAlloc, indices, "")
|
||||||
|
values[i] = c.builder.CreateLoad(gep, "")
|
||||||
|
}
|
||||||
|
return values
|
||||||
|
}
|
||||||
+5
-2
@@ -24,7 +24,7 @@ import "C"
|
|||||||
// This version uses the built-in linker when trying to use lld.
|
// This version uses the built-in linker when trying to use lld.
|
||||||
func Link(linker string, flags ...string) error {
|
func Link(linker string, flags ...string) error {
|
||||||
switch linker {
|
switch linker {
|
||||||
case "ld.lld", commands["ld.lld"]:
|
case "ld.lld":
|
||||||
flags = append([]string{"tinygo:" + linker}, flags...)
|
flags = append([]string{"tinygo:" + linker}, flags...)
|
||||||
var cflag *C.char
|
var cflag *C.char
|
||||||
buf := C.calloc(C.size_t(len(flags)), C.size_t(unsafe.Sizeof(cflag)))
|
buf := C.calloc(C.size_t(len(flags)), C.size_t(unsafe.Sizeof(cflag)))
|
||||||
@@ -39,7 +39,7 @@ func Link(linker string, flags ...string) error {
|
|||||||
return errors.New("failed to link using built-in ld.lld")
|
return errors.New("failed to link using built-in ld.lld")
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
case "wasm-ld", commands["wasm-ld"]:
|
case "wasm-ld":
|
||||||
flags = append([]string{"tinygo:" + linker}, flags...)
|
flags = append([]string{"tinygo:" + linker}, flags...)
|
||||||
var cflag *C.char
|
var cflag *C.char
|
||||||
buf := C.calloc(C.size_t(len(flags)), C.size_t(unsafe.Sizeof(cflag)))
|
buf := C.calloc(C.size_t(len(flags)), C.size_t(unsafe.Sizeof(cflag)))
|
||||||
@@ -57,6 +57,9 @@ func Link(linker string, flags ...string) error {
|
|||||||
return nil
|
return nil
|
||||||
default:
|
default:
|
||||||
// Fall back to external command.
|
// Fall back to external command.
|
||||||
|
if cmdNames, ok := commands[linker]; ok {
|
||||||
|
return execCommand(cmdNames, flags...)
|
||||||
|
}
|
||||||
cmd := exec.Command(linker, flags...)
|
cmd := exec.Command(linker, flags...)
|
||||||
cmd.Stdout = os.Stdout
|
cmd.Stdout = os.Stdout
|
||||||
cmd.Stderr = os.Stderr
|
cmd.Stderr = os.Stderr
|
||||||
|
|||||||
@@ -14,6 +14,9 @@ import (
|
|||||||
//
|
//
|
||||||
// This version always runs the linker as an external command.
|
// This version always runs the linker as an external command.
|
||||||
func Link(linker string, flags ...string) error {
|
func Link(linker string, flags ...string) error {
|
||||||
|
if cmdNames, ok := commands[linker]; ok {
|
||||||
|
return execCommand(cmdNames, flags...)
|
||||||
|
}
|
||||||
cmd := exec.Command(linker, flags...)
|
cmd := exec.Command(linker, flags...)
|
||||||
cmd.Stdout = os.Stdout
|
cmd.Stdout = os.Stdout
|
||||||
cmd.Stderr = os.Stderr
|
cmd.Stderr = os.Stderr
|
||||||
|
|||||||
+64
-1
@@ -23,6 +23,7 @@ type fileInfo struct {
|
|||||||
typedefs map[string]*typedefInfo
|
typedefs map[string]*typedefInfo
|
||||||
elaboratedTypes map[string]ast.Expr
|
elaboratedTypes map[string]ast.Expr
|
||||||
importCPos token.Pos
|
importCPos token.Pos
|
||||||
|
missingSymbols map[string]struct{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// functionInfo stores some information about a Cgo function found by libclang
|
// functionInfo stores some information about a Cgo function found by libclang
|
||||||
@@ -62,10 +63,27 @@ var cgoAliases = map[string]string{
|
|||||||
"C.uintptr_t": "uintptr",
|
"C.uintptr_t": "uintptr",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// cgoBuiltinAliases are handled specially because they only exist on the Go
|
||||||
|
// side of CGo, not on the CGo (they're prefixed with "_Cgo_" there).
|
||||||
|
var cgoBuiltinAliases = map[string]struct{}{
|
||||||
|
"char": struct{}{},
|
||||||
|
"schar": struct{}{},
|
||||||
|
"uchar": struct{}{},
|
||||||
|
"short": struct{}{},
|
||||||
|
"ushort": struct{}{},
|
||||||
|
"int": struct{}{},
|
||||||
|
"uint": struct{}{},
|
||||||
|
"long": struct{}{},
|
||||||
|
"ulong": struct{}{},
|
||||||
|
"longlong": struct{}{},
|
||||||
|
"ulonglong": struct{}{},
|
||||||
|
}
|
||||||
|
|
||||||
// cgoTypes lists some C types with ambiguous sizes that must be retrieved
|
// cgoTypes lists some C types with ambiguous sizes that must be retrieved
|
||||||
// somehow from C. This is done by adding some typedefs to get the size of each
|
// somehow from C. This is done by adding some typedefs to get the size of each
|
||||||
// type.
|
// type.
|
||||||
const cgoTypes = `
|
const cgoTypes = `
|
||||||
|
typedef char _Cgo_char;
|
||||||
typedef signed char _Cgo_schar;
|
typedef signed char _Cgo_schar;
|
||||||
typedef unsigned char _Cgo_uchar;
|
typedef unsigned char _Cgo_uchar;
|
||||||
typedef short _Cgo_short;
|
typedef short _Cgo_short;
|
||||||
@@ -89,6 +107,13 @@ func (p *Package) processCgo(filename string, f *ast.File, cflags []string) []er
|
|||||||
globals: map[string]*globalInfo{},
|
globals: map[string]*globalInfo{},
|
||||||
typedefs: map[string]*typedefInfo{},
|
typedefs: map[string]*typedefInfo{},
|
||||||
elaboratedTypes: map[string]ast.Expr{},
|
elaboratedTypes: map[string]ast.Expr{},
|
||||||
|
missingSymbols: map[string]struct{}{},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find all C.* symbols.
|
||||||
|
f = astutil.Apply(f, info.findMissingCGoNames, nil).(*ast.File)
|
||||||
|
for name := range cgoBuiltinAliases {
|
||||||
|
info.missingSymbols["_Cgo_"+name] = struct{}{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find `import "C"` statements in the file.
|
// Find `import "C"` statements in the file.
|
||||||
@@ -220,6 +245,9 @@ func (info *fileInfo) addFuncDecls() {
|
|||||||
// // ...
|
// // ...
|
||||||
// )
|
// )
|
||||||
func (info *fileInfo) addFuncPtrDecls() {
|
func (info *fileInfo) addFuncPtrDecls() {
|
||||||
|
if len(info.functions) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
gen := &ast.GenDecl{
|
gen := &ast.GenDecl{
|
||||||
TokPos: info.importCPos,
|
TokPos: info.importCPos,
|
||||||
Tok: token.VAR,
|
Tok: token.VAR,
|
||||||
@@ -268,6 +296,9 @@ func (info *fileInfo) addFuncPtrDecls() {
|
|||||||
// // ...
|
// // ...
|
||||||
// )
|
// )
|
||||||
func (info *fileInfo) addVarDecls() {
|
func (info *fileInfo) addVarDecls() {
|
||||||
|
if len(info.globals) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
gen := &ast.GenDecl{
|
gen := &ast.GenDecl{
|
||||||
TokPos: info.importCPos,
|
TokPos: info.importCPos,
|
||||||
Tok: token.VAR,
|
Tok: token.VAR,
|
||||||
@@ -344,6 +375,9 @@ func (info *fileInfo) addTypeAliases() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (info *fileInfo) addTypedefs() {
|
func (info *fileInfo) addTypedefs() {
|
||||||
|
if len(info.typedefs) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
gen := &ast.GenDecl{
|
gen := &ast.GenDecl{
|
||||||
TokPos: info.importCPos,
|
TokPos: info.importCPos,
|
||||||
Tok: token.TYPE,
|
Tok: token.TYPE,
|
||||||
@@ -356,8 +390,10 @@ func (info *fileInfo) addTypedefs() {
|
|||||||
for _, name := range names {
|
for _, name := range names {
|
||||||
typedef := info.typedefs[name]
|
typedef := info.typedefs[name]
|
||||||
typeName := "C." + name
|
typeName := "C." + name
|
||||||
|
isAlias := true
|
||||||
if strings.HasPrefix(name, "_Cgo_") {
|
if strings.HasPrefix(name, "_Cgo_") {
|
||||||
typeName = "C." + name[len("_Cgo_"):]
|
typeName = "C." + name[len("_Cgo_"):]
|
||||||
|
isAlias = false // C.short etc. should not be aliased to the equivalent Go type (not portable)
|
||||||
}
|
}
|
||||||
if _, ok := cgoAliases[typeName]; ok {
|
if _, ok := cgoAliases[typeName]; ok {
|
||||||
// This is a type that also exists in Go (defined in stdint.h).
|
// This is a type that also exists in Go (defined in stdint.h).
|
||||||
@@ -375,6 +411,9 @@ func (info *fileInfo) addTypedefs() {
|
|||||||
},
|
},
|
||||||
Type: typedef.typeExpr,
|
Type: typedef.typeExpr,
|
||||||
}
|
}
|
||||||
|
if isAlias {
|
||||||
|
typeSpec.Assign = info.importCPos
|
||||||
|
}
|
||||||
obj.Decl = typeSpec
|
obj.Decl = typeSpec
|
||||||
gen.Specs = append(gen.Specs, typeSpec)
|
gen.Specs = append(gen.Specs, typeSpec)
|
||||||
}
|
}
|
||||||
@@ -387,6 +426,9 @@ func (info *fileInfo) addTypedefs() {
|
|||||||
// See also:
|
// See also:
|
||||||
// https://en.cppreference.com/w/cpp/language/elaborated_type_specifier
|
// https://en.cppreference.com/w/cpp/language/elaborated_type_specifier
|
||||||
func (info *fileInfo) addElaboratedTypes() {
|
func (info *fileInfo) addElaboratedTypes() {
|
||||||
|
if len(info.elaboratedTypes) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
gen := &ast.GenDecl{
|
gen := &ast.GenDecl{
|
||||||
TokPos: info.importCPos,
|
TokPos: info.importCPos,
|
||||||
Tok: token.TYPE,
|
Tok: token.TYPE,
|
||||||
@@ -398,7 +440,7 @@ func (info *fileInfo) addElaboratedTypes() {
|
|||||||
sort.Strings(names)
|
sort.Strings(names)
|
||||||
for _, name := range names {
|
for _, name := range names {
|
||||||
typ := info.elaboratedTypes[name]
|
typ := info.elaboratedTypes[name]
|
||||||
typeName := "C.struct_" + name
|
typeName := "C." + name
|
||||||
obj := &ast.Object{
|
obj := &ast.Object{
|
||||||
Kind: ast.Typ,
|
Kind: ast.Typ,
|
||||||
Name: typeName,
|
Name: typeName,
|
||||||
@@ -417,6 +459,27 @@ func (info *fileInfo) addElaboratedTypes() {
|
|||||||
info.Decls = append(info.Decls, gen)
|
info.Decls = append(info.Decls, gen)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// findMissingCGoNames traverses the AST and finds all C.something names. Only
|
||||||
|
// these symbols are extracted from the parsed C AST and converted to the Go
|
||||||
|
// equivalent.
|
||||||
|
func (info *fileInfo) findMissingCGoNames(cursor *astutil.Cursor) bool {
|
||||||
|
switch node := cursor.Node().(type) {
|
||||||
|
case *ast.SelectorExpr:
|
||||||
|
x, ok := node.X.(*ast.Ident)
|
||||||
|
if !ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if x.Name == "C" {
|
||||||
|
name := node.Sel.Name
|
||||||
|
if _, ok := cgoBuiltinAliases[name]; ok {
|
||||||
|
name = "_Cgo_" + name
|
||||||
|
}
|
||||||
|
info.missingSymbols[name] = struct{}{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
// walker replaces all "C".<something> expressions to literal "C.<something>"
|
// walker replaces all "C".<something> expressions to literal "C.<something>"
|
||||||
// expressions. Such expressions are impossible to write in Go (a dot cannot be
|
// expressions. Such expressions are impossible to write in Go (a dot cannot be
|
||||||
// used in the middle of a name) so in practice all C identifiers live in a
|
// used in the middle of a name) so in practice all C identifiers live in a
|
||||||
|
|||||||
+209
-119
@@ -4,6 +4,7 @@ package loader
|
|||||||
// modification. It does not touch the AST itself.
|
// modification. It does not touch the AST itself.
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"go/ast"
|
"go/ast"
|
||||||
"go/scanner"
|
"go/scanner"
|
||||||
"go/token"
|
"go/token"
|
||||||
@@ -45,6 +46,8 @@ CXType tinygo_clang_getTypedefDeclUnderlyingType(GoCXCursor c);
|
|||||||
CXType tinygo_clang_getCursorResultType(GoCXCursor c);
|
CXType tinygo_clang_getCursorResultType(GoCXCursor c);
|
||||||
int tinygo_clang_Cursor_getNumArguments(GoCXCursor c);
|
int tinygo_clang_Cursor_getNumArguments(GoCXCursor c);
|
||||||
GoCXCursor tinygo_clang_Cursor_getArgument(GoCXCursor c, unsigned i);
|
GoCXCursor tinygo_clang_Cursor_getArgument(GoCXCursor c, unsigned i);
|
||||||
|
CXSourceLocation tinygo_clang_getCursorLocation(GoCXCursor c);
|
||||||
|
CXTranslationUnit tinygo_clang_Cursor_getTranslationUnit(GoCXCursor c);
|
||||||
|
|
||||||
int tinygo_clang_globals_visitor(GoCXCursor c, GoCXCursor parent, CXClientData client_data);
|
int tinygo_clang_globals_visitor(GoCXCursor c, GoCXCursor parent, CXClientData client_data);
|
||||||
int tinygo_clang_struct_visitor(GoCXCursor c, GoCXCursor parent, CXClientData client_data);
|
int tinygo_clang_struct_visitor(GoCXCursor c, GoCXCursor parent, CXClientData client_data);
|
||||||
@@ -66,9 +69,13 @@ func (info *fileInfo) parseFragment(fragment string, cflags []string, posFilenam
|
|||||||
index := C.clang_createIndex(0, 0)
|
index := C.clang_createIndex(0, 0)
|
||||||
defer C.clang_disposeIndex(index)
|
defer C.clang_disposeIndex(index)
|
||||||
|
|
||||||
|
// pretend to be a .c file
|
||||||
filenameC := C.CString(posFilename + "!cgo.c")
|
filenameC := C.CString(posFilename + "!cgo.c")
|
||||||
defer C.free(unsafe.Pointer(filenameC))
|
defer C.free(unsafe.Pointer(filenameC))
|
||||||
|
|
||||||
|
// fix up error locations
|
||||||
|
fragment = fmt.Sprintf("# %d %#v\n", posLine+1, posFilename) + fragment
|
||||||
|
|
||||||
fragmentC := C.CString(fragment)
|
fragmentC := C.CString(fragment)
|
||||||
defer C.free(unsafe.Pointer(fragmentC))
|
defer C.free(unsafe.Pointer(fragmentC))
|
||||||
|
|
||||||
@@ -107,20 +114,12 @@ func (info *fileInfo) parseFragment(fragment string, cflags []string, posFilenam
|
|||||||
spelling := getString(C.clang_getDiagnosticSpelling(diagnostic))
|
spelling := getString(C.clang_getDiagnosticSpelling(diagnostic))
|
||||||
severity := diagnosticSeverity[C.clang_getDiagnosticSeverity(diagnostic)]
|
severity := diagnosticSeverity[C.clang_getDiagnosticSeverity(diagnostic)]
|
||||||
location := C.clang_getDiagnosticLocation(diagnostic)
|
location := C.clang_getDiagnosticLocation(diagnostic)
|
||||||
var file C.CXFile
|
var libclangFilename C.CXString
|
||||||
var line C.unsigned
|
var line C.unsigned
|
||||||
var column C.unsigned
|
var column C.unsigned
|
||||||
var offset C.unsigned
|
C.clang_getPresumedLocation(location, &libclangFilename, &line, &column)
|
||||||
C.clang_getExpansionLocation(location, &file, &line, &column, &offset)
|
filename := getString(libclangFilename)
|
||||||
filename := getString(C.clang_getFileName(file))
|
if filepath.IsAbs(filename) {
|
||||||
if filename == posFilename+"!cgo.c" {
|
|
||||||
// Adjust errors from the `import "C"` snippet.
|
|
||||||
// Note: doesn't adjust filenames inside the error message
|
|
||||||
// itself.
|
|
||||||
filename = posFilename
|
|
||||||
line += C.uint(posLine)
|
|
||||||
offset = 0 // hard to calculate
|
|
||||||
} else if filepath.IsAbs(filename) {
|
|
||||||
// Relative paths for readability, like other Go parser errors.
|
// Relative paths for readability, like other Go parser errors.
|
||||||
relpath, err := filepath.Rel(info.Program.Dir, filename)
|
relpath, err := filepath.Rel(info.Program.Dir, filename)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
@@ -130,7 +129,7 @@ func (info *fileInfo) parseFragment(fragment string, cflags []string, posFilenam
|
|||||||
errs = append(errs, &scanner.Error{
|
errs = append(errs, &scanner.Error{
|
||||||
Pos: token.Position{
|
Pos: token.Position{
|
||||||
Filename: filename,
|
Filename: filename,
|
||||||
Offset: int(offset),
|
Offset: 0, // not provided by clang_getPresumedLocation
|
||||||
Line: int(line),
|
Line: int(line),
|
||||||
Column: int(column),
|
Column: int(column),
|
||||||
},
|
},
|
||||||
@@ -162,9 +161,13 @@ func (info *fileInfo) parseFragment(fragment string, cflags []string, posFilenam
|
|||||||
func tinygo_clang_globals_visitor(c, parent C.GoCXCursor, client_data C.CXClientData) C.int {
|
func tinygo_clang_globals_visitor(c, parent C.GoCXCursor, client_data C.CXClientData) C.int {
|
||||||
info := refMap.Get(unsafe.Pointer(client_data)).(*fileInfo)
|
info := refMap.Get(unsafe.Pointer(client_data)).(*fileInfo)
|
||||||
kind := C.tinygo_clang_getCursorKind(c)
|
kind := C.tinygo_clang_getCursorKind(c)
|
||||||
|
pos := info.getCursorPosition(c)
|
||||||
switch kind {
|
switch kind {
|
||||||
case C.CXCursor_FunctionDecl:
|
case C.CXCursor_FunctionDecl:
|
||||||
name := getString(C.tinygo_clang_getCursorSpelling(c))
|
name := getString(C.tinygo_clang_getCursorSpelling(c))
|
||||||
|
if _, required := info.missingSymbols[name]; !required {
|
||||||
|
return C.CXChildVisit_Continue
|
||||||
|
}
|
||||||
cursorType := C.tinygo_clang_getCursorType(c)
|
cursorType := C.tinygo_clang_getCursorType(c)
|
||||||
if C.clang_isFunctionTypeVariadic(cursorType) != 0 {
|
if C.clang_isFunctionTypeVariadic(cursorType) != 0 {
|
||||||
return C.CXChildVisit_Continue // not supported
|
return C.CXChildVisit_Continue // not supported
|
||||||
@@ -181,7 +184,7 @@ func tinygo_clang_globals_visitor(c, parent C.GoCXCursor, client_data C.CXClient
|
|||||||
}
|
}
|
||||||
fn.args = append(fn.args, paramInfo{
|
fn.args = append(fn.args, paramInfo{
|
||||||
name: argName,
|
name: argName,
|
||||||
typeExpr: info.makeASTType(argType),
|
typeExpr: info.makeASTType(argType, pos),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
resultType := C.tinygo_clang_getCursorResultType(c)
|
resultType := C.tinygo_clang_getCursorResultType(c)
|
||||||
@@ -189,53 +192,33 @@ func tinygo_clang_globals_visitor(c, parent C.GoCXCursor, client_data C.CXClient
|
|||||||
fn.results = &ast.FieldList{
|
fn.results = &ast.FieldList{
|
||||||
List: []*ast.Field{
|
List: []*ast.Field{
|
||||||
&ast.Field{
|
&ast.Field{
|
||||||
Type: info.makeASTType(resultType),
|
Type: info.makeASTType(resultType, pos),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
case C.CXCursor_StructDecl:
|
||||||
|
typ := C.tinygo_clang_getCursorType(c)
|
||||||
|
name := getString(C.tinygo_clang_getCursorSpelling(c))
|
||||||
|
if _, required := info.missingSymbols["struct_"+name]; !required {
|
||||||
|
return C.CXChildVisit_Continue
|
||||||
|
}
|
||||||
|
info.makeASTType(typ, pos)
|
||||||
case C.CXCursor_TypedefDecl:
|
case C.CXCursor_TypedefDecl:
|
||||||
typedefType := C.tinygo_clang_getCursorType(c)
|
typedefType := C.tinygo_clang_getCursorType(c)
|
||||||
name := getString(C.clang_getTypedefName(typedefType))
|
name := getString(C.clang_getTypedefName(typedefType))
|
||||||
underlyingType := C.tinygo_clang_getTypedefDeclUnderlyingType(c)
|
if _, required := info.missingSymbols[name]; !required {
|
||||||
expr := info.makeASTType(underlyingType)
|
return C.CXChildVisit_Continue
|
||||||
if strings.HasPrefix(name, "_Cgo_") {
|
|
||||||
expr := expr.(*ast.Ident)
|
|
||||||
typeSize := C.clang_Type_getSizeOf(underlyingType)
|
|
||||||
switch expr.Name {
|
|
||||||
// TODO: plain char (may be signed or unsigned)
|
|
||||||
case "C.schar", "C.short", "C.int", "C.long", "C.longlong":
|
|
||||||
switch typeSize {
|
|
||||||
case 1:
|
|
||||||
expr.Name = "int8"
|
|
||||||
case 2:
|
|
||||||
expr.Name = "int16"
|
|
||||||
case 4:
|
|
||||||
expr.Name = "int32"
|
|
||||||
case 8:
|
|
||||||
expr.Name = "int64"
|
|
||||||
}
|
|
||||||
case "C.uchar", "C.ushort", "C.uint", "C.ulong", "C.ulonglong":
|
|
||||||
switch typeSize {
|
|
||||||
case 1:
|
|
||||||
expr.Name = "uint8"
|
|
||||||
case 2:
|
|
||||||
expr.Name = "uint16"
|
|
||||||
case 4:
|
|
||||||
expr.Name = "uint32"
|
|
||||||
case 8:
|
|
||||||
expr.Name = "uint64"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
info.typedefs[name] = &typedefInfo{
|
|
||||||
typeExpr: expr,
|
|
||||||
}
|
}
|
||||||
|
info.makeASTType(typedefType, pos)
|
||||||
case C.CXCursor_VarDecl:
|
case C.CXCursor_VarDecl:
|
||||||
name := getString(C.tinygo_clang_getCursorSpelling(c))
|
name := getString(C.tinygo_clang_getCursorSpelling(c))
|
||||||
|
if _, required := info.missingSymbols[name]; !required {
|
||||||
|
return C.CXChildVisit_Continue
|
||||||
|
}
|
||||||
cursorType := C.tinygo_clang_getCursorType(c)
|
cursorType := C.tinygo_clang_getCursorType(c)
|
||||||
info.globals[name] = &globalInfo{
|
info.globals[name] = &globalInfo{
|
||||||
typeExpr: info.makeASTType(cursorType),
|
typeExpr: info.makeASTType(cursorType, pos),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return C.CXChildVisit_Continue
|
return C.CXChildVisit_Continue
|
||||||
@@ -248,11 +231,48 @@ func getString(clangString C.CXString) (s string) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// getCursorPosition returns a usable token.Pos from a libclang cursor. If the
|
||||||
|
// file for this cursor has not been seen before, it is read from libclang
|
||||||
|
// (which already has the file in memory) and added to the token.FileSet.
|
||||||
|
func (info *fileInfo) getCursorPosition(cursor C.GoCXCursor) token.Pos {
|
||||||
|
location := C.tinygo_clang_getCursorLocation(cursor)
|
||||||
|
var file C.CXFile
|
||||||
|
var line C.unsigned
|
||||||
|
var column C.unsigned
|
||||||
|
var offset C.unsigned
|
||||||
|
C.clang_getExpansionLocation(location, &file, &line, &column, &offset)
|
||||||
|
if line == 0 || file == nil {
|
||||||
|
// Invalid token.
|
||||||
|
return token.NoPos
|
||||||
|
}
|
||||||
|
filename := getString(C.clang_getFileName(file))
|
||||||
|
if _, ok := info.tokenFiles[filename]; !ok {
|
||||||
|
// File has not been seen before in this package, add line information
|
||||||
|
// now by reading the file from libclang.
|
||||||
|
tu := C.tinygo_clang_Cursor_getTranslationUnit(cursor)
|
||||||
|
var size C.size_t
|
||||||
|
sourcePtr := C.clang_getFileContents(tu, file, &size)
|
||||||
|
source := ((*[1 << 28]byte)(unsafe.Pointer(sourcePtr)))[:size:size]
|
||||||
|
lines := []int{0}
|
||||||
|
for i := 0; i < len(source)-1; i++ {
|
||||||
|
if source[i] == '\n' {
|
||||||
|
lines = append(lines, i+1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
f := info.fset.AddFile(filename, -1, int(size))
|
||||||
|
f.SetLines(lines)
|
||||||
|
info.tokenFiles[filename] = f
|
||||||
|
}
|
||||||
|
return info.tokenFiles[filename].Pos(int(offset))
|
||||||
|
}
|
||||||
|
|
||||||
// makeASTType return the ast.Expr for the given libclang type. In other words,
|
// makeASTType return the ast.Expr for the given libclang type. In other words,
|
||||||
// it converts a libclang type to a type in the Go AST.
|
// it converts a libclang type to a type in the Go AST.
|
||||||
func (info *fileInfo) makeASTType(typ C.CXType) ast.Expr {
|
func (info *fileInfo) makeASTType(typ C.CXType, pos token.Pos) ast.Expr {
|
||||||
var typeName string
|
var typeName string
|
||||||
switch typ.kind {
|
switch typ.kind {
|
||||||
|
case C.CXType_Char_S, C.CXType_Char_U:
|
||||||
|
typeName = "C.char"
|
||||||
case C.CXType_SChar:
|
case C.CXType_SChar:
|
||||||
typeName = "C.schar"
|
typeName = "C.schar"
|
||||||
case C.CXType_UChar:
|
case C.CXType_UChar:
|
||||||
@@ -293,19 +313,33 @@ func (info *fileInfo) makeASTType(typ C.CXType) ast.Expr {
|
|||||||
typeName = "complex128"
|
typeName = "complex128"
|
||||||
}
|
}
|
||||||
case C.CXType_Pointer:
|
case C.CXType_Pointer:
|
||||||
|
pointeeType := C.clang_getPointeeType(typ)
|
||||||
|
if pointeeType.kind == C.CXType_Void {
|
||||||
|
// void* type is translated to Go as unsafe.Pointer
|
||||||
|
return &ast.SelectorExpr{
|
||||||
|
X: &ast.Ident{
|
||||||
|
NamePos: pos,
|
||||||
|
Name: "unsafe",
|
||||||
|
},
|
||||||
|
Sel: &ast.Ident{
|
||||||
|
NamePos: pos,
|
||||||
|
Name: "Pointer",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
return &ast.StarExpr{
|
return &ast.StarExpr{
|
||||||
Star: info.importCPos,
|
Star: pos,
|
||||||
X: info.makeASTType(C.clang_getPointeeType(typ)),
|
X: info.makeASTType(pointeeType, pos),
|
||||||
}
|
}
|
||||||
case C.CXType_ConstantArray:
|
case C.CXType_ConstantArray:
|
||||||
return &ast.ArrayType{
|
return &ast.ArrayType{
|
||||||
Lbrack: info.importCPos,
|
Lbrack: pos,
|
||||||
Len: &ast.BasicLit{
|
Len: &ast.BasicLit{
|
||||||
ValuePos: info.importCPos,
|
ValuePos: pos,
|
||||||
Kind: token.INT,
|
Kind: token.INT,
|
||||||
Value: strconv.FormatInt(int64(C.clang_getArraySize(typ)), 10),
|
Value: strconv.FormatInt(int64(C.clang_getArraySize(typ)), 10),
|
||||||
},
|
},
|
||||||
Elt: info.makeASTType(C.clang_getElementType(typ)),
|
Elt: info.makeASTType(C.clang_getElementType(typ), pos),
|
||||||
}
|
}
|
||||||
case C.CXType_FunctionProto:
|
case C.CXType_FunctionProto:
|
||||||
// Be compatible with gc, which uses the *[0]byte type for function
|
// Be compatible with gc, which uses the *[0]byte type for function
|
||||||
@@ -313,95 +347,151 @@ func (info *fileInfo) makeASTType(typ C.CXType) ast.Expr {
|
|||||||
// Return type [0]byte because this is a function type, not a pointer to
|
// Return type [0]byte because this is a function type, not a pointer to
|
||||||
// this function type.
|
// this function type.
|
||||||
return &ast.ArrayType{
|
return &ast.ArrayType{
|
||||||
Lbrack: info.importCPos,
|
Lbrack: pos,
|
||||||
Len: &ast.BasicLit{
|
Len: &ast.BasicLit{
|
||||||
ValuePos: info.importCPos,
|
ValuePos: pos,
|
||||||
Kind: token.INT,
|
Kind: token.INT,
|
||||||
Value: "0",
|
Value: "0",
|
||||||
},
|
},
|
||||||
Elt: &ast.Ident{
|
Elt: &ast.Ident{
|
||||||
NamePos: info.importCPos,
|
NamePos: pos,
|
||||||
Name: "byte",
|
Name: "byte",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
case C.CXType_Typedef:
|
case C.CXType_Typedef:
|
||||||
typedefName := getString(C.clang_getTypedefName(typ))
|
name := getString(C.clang_getTypedefName(typ))
|
||||||
|
if _, ok := info.typedefs[name]; !ok {
|
||||||
|
info.typedefs[name] = nil // don't recurse
|
||||||
|
c := C.tinygo_clang_getTypeDeclaration(typ)
|
||||||
|
underlyingType := C.tinygo_clang_getTypedefDeclUnderlyingType(c)
|
||||||
|
expr := info.makeASTType(underlyingType, pos)
|
||||||
|
if strings.HasPrefix(name, "_Cgo_") {
|
||||||
|
expr := expr.(*ast.Ident)
|
||||||
|
typeSize := C.clang_Type_getSizeOf(underlyingType)
|
||||||
|
switch expr.Name {
|
||||||
|
case "C.char":
|
||||||
|
if typeSize != 1 {
|
||||||
|
// This happens for some very special purpose architectures
|
||||||
|
// (DSPs etc.) that are not currently targeted.
|
||||||
|
// https://www.embecosm.com/2017/04/18/non-8-bit-char-support-in-clang-and-llvm/
|
||||||
|
panic("unknown char width")
|
||||||
|
}
|
||||||
|
switch underlyingType.kind {
|
||||||
|
case C.CXType_Char_S:
|
||||||
|
expr.Name = "int8"
|
||||||
|
case C.CXType_Char_U:
|
||||||
|
expr.Name = "uint8"
|
||||||
|
}
|
||||||
|
case "C.schar", "C.short", "C.int", "C.long", "C.longlong":
|
||||||
|
switch typeSize {
|
||||||
|
case 1:
|
||||||
|
expr.Name = "int8"
|
||||||
|
case 2:
|
||||||
|
expr.Name = "int16"
|
||||||
|
case 4:
|
||||||
|
expr.Name = "int32"
|
||||||
|
case 8:
|
||||||
|
expr.Name = "int64"
|
||||||
|
}
|
||||||
|
case "C.uchar", "C.ushort", "C.uint", "C.ulong", "C.ulonglong":
|
||||||
|
switch typeSize {
|
||||||
|
case 1:
|
||||||
|
expr.Name = "uint8"
|
||||||
|
case 2:
|
||||||
|
expr.Name = "uint16"
|
||||||
|
case 4:
|
||||||
|
expr.Name = "uint32"
|
||||||
|
case 8:
|
||||||
|
expr.Name = "uint64"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
info.typedefs[name] = &typedefInfo{
|
||||||
|
typeExpr: expr,
|
||||||
|
}
|
||||||
|
}
|
||||||
return &ast.Ident{
|
return &ast.Ident{
|
||||||
NamePos: info.importCPos,
|
NamePos: pos,
|
||||||
Name: "C." + typedefName,
|
Name: "C." + name,
|
||||||
}
|
}
|
||||||
case C.CXType_Elaborated:
|
case C.CXType_Elaborated:
|
||||||
underlying := C.clang_Type_getNamedType(typ)
|
underlying := C.clang_Type_getNamedType(typ)
|
||||||
switch underlying.kind {
|
switch underlying.kind {
|
||||||
case C.CXType_Record:
|
case C.CXType_Record:
|
||||||
cursor := C.tinygo_clang_getTypeDeclaration(typ)
|
return info.makeASTType(underlying, pos)
|
||||||
name := getString(C.tinygo_clang_getCursorSpelling(cursor))
|
|
||||||
// It is possible that this is a recursive definition, for example
|
|
||||||
// in linked lists (structs contain a pointer to the next element
|
|
||||||
// of the same type). If the name exists in info.elaboratedTypes,
|
|
||||||
// it is being processed, although it may not be fully defined yet.
|
|
||||||
if _, ok := info.elaboratedTypes[name]; !ok {
|
|
||||||
info.elaboratedTypes[name] = nil // predeclare (to avoid endless recursion)
|
|
||||||
info.elaboratedTypes[name] = info.makeASTType(underlying)
|
|
||||||
}
|
|
||||||
return &ast.Ident{
|
|
||||||
NamePos: info.importCPos,
|
|
||||||
Name: "C.struct_" + name,
|
|
||||||
}
|
|
||||||
default:
|
default:
|
||||||
panic("unknown elaborated type")
|
panic("unknown elaborated type")
|
||||||
}
|
}
|
||||||
case C.CXType_Record:
|
case C.CXType_Record:
|
||||||
cursor := C.tinygo_clang_getTypeDeclaration(typ)
|
cursor := C.tinygo_clang_getTypeDeclaration(typ)
|
||||||
fieldList := &ast.FieldList{
|
name := getString(C.tinygo_clang_getCursorSpelling(cursor))
|
||||||
Opening: info.importCPos,
|
var cgoName string
|
||||||
Closing: info.importCPos,
|
|
||||||
}
|
|
||||||
ref := refMap.Put(struct {
|
|
||||||
fieldList *ast.FieldList
|
|
||||||
info *fileInfo
|
|
||||||
}{fieldList, info})
|
|
||||||
defer refMap.Remove(ref)
|
|
||||||
C.tinygo_clang_visitChildren(cursor, C.CXCursorVisitor(C.tinygo_clang_struct_visitor), C.CXClientData(ref))
|
|
||||||
switch C.tinygo_clang_getCursorKind(cursor) {
|
switch C.tinygo_clang_getCursorKind(cursor) {
|
||||||
case C.CXCursor_StructDecl:
|
case C.CXCursor_StructDecl:
|
||||||
return &ast.StructType{
|
cgoName = "struct_" + name
|
||||||
Struct: info.importCPos,
|
|
||||||
Fields: fieldList,
|
|
||||||
}
|
|
||||||
case C.CXCursor_UnionDecl:
|
case C.CXCursor_UnionDecl:
|
||||||
if len(fieldList.List) > 1 {
|
cgoName = "union_" + name
|
||||||
// Insert a special field at the front (of zero width) as a
|
default:
|
||||||
// marker that this is struct is actually a union. This is done
|
panic("unknown record declaration")
|
||||||
// by giving the field a name that cannot be expressed directly
|
}
|
||||||
// in Go.
|
if _, ok := info.elaboratedTypes[cgoName]; !ok {
|
||||||
// Other parts of the compiler look at the first element in a
|
info.elaboratedTypes[cgoName] = nil // predeclare (to avoid endless recursion)
|
||||||
// struct (of size > 2) to know whether this is a union.
|
fieldList := &ast.FieldList{
|
||||||
// Note that we don't have to insert it for single-element
|
Opening: pos,
|
||||||
// unions as they're basically equivalent to a struct.
|
Closing: pos,
|
||||||
unionMarker := &ast.Field{
|
}
|
||||||
Type: &ast.StructType{
|
ref := refMap.Put(struct {
|
||||||
Struct: info.importCPos,
|
fieldList *ast.FieldList
|
||||||
},
|
info *fileInfo
|
||||||
|
}{fieldList, info})
|
||||||
|
defer refMap.Remove(ref)
|
||||||
|
C.tinygo_clang_visitChildren(cursor, C.CXCursorVisitor(C.tinygo_clang_struct_visitor), C.CXClientData(ref))
|
||||||
|
switch C.tinygo_clang_getCursorKind(cursor) {
|
||||||
|
case C.CXCursor_StructDecl:
|
||||||
|
info.elaboratedTypes[cgoName] = &ast.StructType{
|
||||||
|
Struct: pos,
|
||||||
|
Fields: fieldList,
|
||||||
}
|
}
|
||||||
unionMarker.Names = []*ast.Ident{
|
case C.CXCursor_UnionDecl:
|
||||||
&ast.Ident{
|
if len(fieldList.List) > 1 {
|
||||||
NamePos: info.importCPos,
|
// Insert a special field at the front (of zero width) as a
|
||||||
Name: "C union",
|
// marker that this is struct is actually a union. This is done
|
||||||
Obj: &ast.Object{
|
// by giving the field a name that cannot be expressed directly
|
||||||
Kind: ast.Var,
|
// in Go.
|
||||||
Name: "C union",
|
// Other parts of the compiler look at the first element in a
|
||||||
Decl: unionMarker,
|
// struct (of size > 2) to know whether this is a union.
|
||||||
|
// Note that we don't have to insert it for single-element
|
||||||
|
// unions as they're basically equivalent to a struct.
|
||||||
|
unionMarker := &ast.Field{
|
||||||
|
Type: &ast.StructType{
|
||||||
|
Struct: pos,
|
||||||
},
|
},
|
||||||
},
|
}
|
||||||
|
unionMarker.Names = []*ast.Ident{
|
||||||
|
&ast.Ident{
|
||||||
|
NamePos: pos,
|
||||||
|
Name: "C union",
|
||||||
|
Obj: &ast.Object{
|
||||||
|
Kind: ast.Var,
|
||||||
|
Name: "C union",
|
||||||
|
Decl: unionMarker,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
fieldList.List = append([]*ast.Field{unionMarker}, fieldList.List...)
|
||||||
}
|
}
|
||||||
fieldList.List = append([]*ast.Field{unionMarker}, fieldList.List...)
|
info.elaboratedTypes[cgoName] = &ast.StructType{
|
||||||
}
|
Struct: pos,
|
||||||
return &ast.StructType{
|
Fields: fieldList,
|
||||||
Struct: info.importCPos,
|
}
|
||||||
Fields: fieldList,
|
default:
|
||||||
|
panic("unreachable")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return &ast.Ident{
|
||||||
|
NamePos: pos,
|
||||||
|
Name: "C." + cgoName,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if typeName == "" {
|
if typeName == "" {
|
||||||
// Fallback, probably incorrect but at least the error points to an odd
|
// Fallback, probably incorrect but at least the error points to an odd
|
||||||
@@ -409,7 +499,7 @@ func (info *fileInfo) makeASTType(typ C.CXType) ast.Expr {
|
|||||||
typeName = "C." + getString(C.clang_getTypeSpelling(typ))
|
typeName = "C." + getString(C.clang_getTypeSpelling(typ))
|
||||||
}
|
}
|
||||||
return &ast.Ident{
|
return &ast.Ident{
|
||||||
NamePos: info.importCPos,
|
NamePos: pos,
|
||||||
Name: typeName,
|
Name: typeName,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -428,11 +518,11 @@ func tinygo_clang_struct_visitor(c, parent C.GoCXCursor, client_data C.CXClientD
|
|||||||
name := getString(C.tinygo_clang_getCursorSpelling(c))
|
name := getString(C.tinygo_clang_getCursorSpelling(c))
|
||||||
typ := C.tinygo_clang_getCursorType(c)
|
typ := C.tinygo_clang_getCursorType(c)
|
||||||
field := &ast.Field{
|
field := &ast.Field{
|
||||||
Type: info.makeASTType(typ),
|
Type: info.makeASTType(typ, info.getCursorPosition(c)),
|
||||||
}
|
}
|
||||||
field.Names = []*ast.Ident{
|
field.Names = []*ast.Ident{
|
||||||
&ast.Ident{
|
&ast.Ident{
|
||||||
NamePos: info.importCPos,
|
NamePos: info.getCursorPosition(c),
|
||||||
Name: name,
|
Name: name,
|
||||||
Obj: &ast.Object{
|
Obj: &ast.Object{
|
||||||
Kind: ast.Var,
|
Kind: ast.Var,
|
||||||
|
|||||||
@@ -44,3 +44,11 @@ int tinygo_clang_Cursor_getNumArguments(CXCursor c) {
|
|||||||
CXCursor tinygo_clang_Cursor_getArgument(CXCursor c, unsigned i) {
|
CXCursor tinygo_clang_Cursor_getArgument(CXCursor c, unsigned i) {
|
||||||
return clang_Cursor_getArgument(c, i);
|
return clang_Cursor_getArgument(c, i);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CXSourceLocation tinygo_clang_getCursorLocation(CXCursor c) {
|
||||||
|
return clang_getCursorLocation(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
CXTranslationUnit tinygo_clang_Cursor_getTranslationUnit(CXCursor c) {
|
||||||
|
return clang_Cursor_getTranslationUnit(c);
|
||||||
|
}
|
||||||
|
|||||||
+18
-5
@@ -22,6 +22,7 @@ type Program struct {
|
|||||||
fset *token.FileSet
|
fset *token.FileSet
|
||||||
TypeChecker types.Config
|
TypeChecker types.Config
|
||||||
Dir string // current working directory (for error reporting)
|
Dir string // current working directory (for error reporting)
|
||||||
|
TINYGOROOT string // root of the TinyGo installation or root of the source code
|
||||||
CFlags []string
|
CFlags []string
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -29,10 +30,11 @@ type Program struct {
|
|||||||
type Package struct {
|
type Package struct {
|
||||||
*Program
|
*Program
|
||||||
*build.Package
|
*build.Package
|
||||||
Imports map[string]*Package
|
Imports map[string]*Package
|
||||||
Importing bool
|
Importing bool
|
||||||
Files []*ast.File
|
Files []*ast.File
|
||||||
Pkg *types.Package
|
tokenFiles map[string]*token.File
|
||||||
|
Pkg *types.Package
|
||||||
types.Info
|
types.Info
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,6 +107,7 @@ func (p *Program) newPackage(pkg *build.Package) *Package {
|
|||||||
Scopes: make(map[ast.Node]*types.Scope),
|
Scopes: make(map[ast.Node]*types.Scope),
|
||||||
Selections: make(map[*ast.SelectorExpr]*types.Selection),
|
Selections: make(map[*ast.SelectorExpr]*types.Selection),
|
||||||
},
|
},
|
||||||
|
tokenFiles: map[string]*token.File{},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -292,6 +295,16 @@ func (p *Package) parseFiles() ([]*ast.File, error) {
|
|||||||
}
|
}
|
||||||
files = append(files, f)
|
files = append(files, f)
|
||||||
}
|
}
|
||||||
|
clangIncludes := ""
|
||||||
|
if len(p.CgoFiles) != 0 {
|
||||||
|
if _, err := os.Stat(filepath.Join(p.TINYGOROOT, "llvm", "tools", "clang", "lib", "Headers")); !os.IsNotExist(err) {
|
||||||
|
// Running from the source directory.
|
||||||
|
clangIncludes = filepath.Join(p.TINYGOROOT, "llvm", "tools", "clang", "lib", "Headers")
|
||||||
|
} else {
|
||||||
|
// Running from the installation directory.
|
||||||
|
clangIncludes = filepath.Join(p.TINYGOROOT, "lib", "clang", "include")
|
||||||
|
}
|
||||||
|
}
|
||||||
for _, file := range p.CgoFiles {
|
for _, file := range p.CgoFiles {
|
||||||
path := filepath.Join(p.Package.Dir, file)
|
path := filepath.Join(p.Package.Dir, file)
|
||||||
f, err := p.parseFile(path, parser.ParseComments)
|
f, err := p.parseFile(path, parser.ParseComments)
|
||||||
@@ -299,7 +312,7 @@ func (p *Package) parseFiles() ([]*ast.File, error) {
|
|||||||
fileErrs = append(fileErrs, err)
|
fileErrs = append(fileErrs, err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
errs := p.processCgo(path, f, append(p.CFlags, "-I"+p.Package.Dir))
|
errs := p.processCgo(path, f, append(p.CFlags, "-I"+p.Package.Dir, "-I"+clangIncludes))
|
||||||
if errs != nil {
|
if errs != nil {
|
||||||
fileErrs = append(fileErrs, errs...)
|
fileErrs = append(fileErrs, errs...)
|
||||||
continue
|
continue
|
||||||
|
|||||||
@@ -33,16 +33,27 @@ func (e *commandError) Error() string {
|
|||||||
return e.Msg + " " + e.File + ": " + e.Err.Error()
|
return e.Msg + " " + e.File + ": " + e.Err.Error()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// multiError is a list of multiple errors (actually: diagnostics) returned
|
||||||
|
// during LLVM IR generation.
|
||||||
|
type multiError struct {
|
||||||
|
Errs []error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *multiError) Error() string {
|
||||||
|
return e.Errs[0].Error()
|
||||||
|
}
|
||||||
|
|
||||||
type BuildConfig struct {
|
type BuildConfig struct {
|
||||||
opt string
|
opt string
|
||||||
gc string
|
gc string
|
||||||
printIR bool
|
panicStrategy string
|
||||||
dumpSSA bool
|
printIR bool
|
||||||
debug bool
|
dumpSSA bool
|
||||||
printSizes string
|
debug bool
|
||||||
cFlags []string
|
printSizes string
|
||||||
ldFlags []string
|
cFlags []string
|
||||||
wasmAbi string
|
ldFlags []string
|
||||||
|
wasmAbi string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper function for Compiler object.
|
// Helper function for Compiler object.
|
||||||
@@ -51,23 +62,39 @@ func Compile(pkgName, outpath string, spec *TargetSpec, config *BuildConfig, act
|
|||||||
config.gc = spec.GC
|
config.gc = spec.GC
|
||||||
}
|
}
|
||||||
|
|
||||||
// Append command line passed CFlags and LDFlags
|
root := sourceDir()
|
||||||
spec.CFlags = append(spec.CFlags, config.cFlags...)
|
|
||||||
spec.LDFlags = append(spec.LDFlags, config.ldFlags...)
|
|
||||||
|
|
||||||
|
// Merge and adjust CFlags.
|
||||||
|
cflags := append([]string{}, config.cFlags...)
|
||||||
|
for _, flag := range spec.CFlags {
|
||||||
|
cflags = append(cflags, strings.Replace(flag, "{root}", root, -1))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Merge and adjust LDFlags.
|
||||||
|
ldflags := append([]string{}, config.ldFlags...)
|
||||||
|
for _, flag := range spec.LDFlags {
|
||||||
|
ldflags = append(ldflags, strings.Replace(flag, "{root}", root, -1))
|
||||||
|
}
|
||||||
|
|
||||||
|
goroot := getGoroot()
|
||||||
|
if goroot == "" {
|
||||||
|
return errors.New("cannot locate $GOROOT, please set it manually")
|
||||||
|
}
|
||||||
compilerConfig := compiler.Config{
|
compilerConfig := compiler.Config{
|
||||||
Triple: spec.Triple,
|
Triple: spec.Triple,
|
||||||
CPU: spec.CPU,
|
CPU: spec.CPU,
|
||||||
GOOS: spec.GOOS,
|
GOOS: spec.GOOS,
|
||||||
GOARCH: spec.GOARCH,
|
GOARCH: spec.GOARCH,
|
||||||
GC: config.gc,
|
GC: config.gc,
|
||||||
CFlags: spec.CFlags,
|
PanicStrategy: config.panicStrategy,
|
||||||
LDFlags: spec.LDFlags,
|
CFlags: cflags,
|
||||||
Debug: config.debug,
|
LDFlags: ldflags,
|
||||||
DumpSSA: config.dumpSSA,
|
Debug: config.debug,
|
||||||
RootDir: sourceDir(),
|
DumpSSA: config.dumpSSA,
|
||||||
GOPATH: getGopath(),
|
TINYGOROOT: root,
|
||||||
BuildTags: spec.BuildTags,
|
GOROOT: goroot,
|
||||||
|
GOPATH: getGopath(),
|
||||||
|
BuildTags: spec.BuildTags,
|
||||||
}
|
}
|
||||||
c, err := compiler.NewCompiler(pkgName, compilerConfig)
|
c, err := compiler.NewCompiler(pkgName, compilerConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -75,12 +102,15 @@ func Compile(pkgName, outpath string, spec *TargetSpec, config *BuildConfig, act
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Compile Go code to IR.
|
// Compile Go code to IR.
|
||||||
err = c.Compile(pkgName)
|
errs := c.Compile(pkgName)
|
||||||
if err != nil {
|
if len(errs) != 0 {
|
||||||
return err
|
if len(errs) == 1 {
|
||||||
|
return errs[0]
|
||||||
|
}
|
||||||
|
return &multiError{errs}
|
||||||
}
|
}
|
||||||
if config.printIR {
|
if config.printIR {
|
||||||
fmt.Println("Generated LLVM IR:")
|
fmt.Println("; Generated LLVM IR:")
|
||||||
fmt.Println(c.IR())
|
fmt.Println(c.IR())
|
||||||
}
|
}
|
||||||
if err := c.Verify(); err != nil {
|
if err := c.Verify(); err != nil {
|
||||||
@@ -191,19 +221,20 @@ func Compile(pkgName, outpath string, spec *TargetSpec, config *BuildConfig, act
|
|||||||
// Prepare link command.
|
// Prepare link command.
|
||||||
executable := filepath.Join(dir, "main")
|
executable := filepath.Join(dir, "main")
|
||||||
tmppath := executable // final file
|
tmppath := executable // final file
|
||||||
ldflags := append(spec.LDFlags, "-o", executable, objfile, "-L", sourceDir())
|
ldflags := append(ldflags, "-o", executable, objfile, "-L", root)
|
||||||
if spec.RTLib == "compiler-rt" {
|
if spec.RTLib == "compiler-rt" {
|
||||||
ldflags = append(ldflags, librt)
|
ldflags = append(ldflags, librt)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compile extra files.
|
// Compile extra files.
|
||||||
for i, path := range spec.ExtraFiles {
|
for i, path := range spec.ExtraFiles {
|
||||||
|
abspath := filepath.Join(root, path)
|
||||||
outpath := filepath.Join(dir, "extra-"+strconv.Itoa(i)+"-"+filepath.Base(path)+".o")
|
outpath := filepath.Join(dir, "extra-"+strconv.Itoa(i)+"-"+filepath.Base(path)+".o")
|
||||||
cmd := exec.Command(spec.Compiler, append(spec.CFlags, "-c", "-o", outpath, path)...)
|
cmdNames := []string{spec.Compiler}
|
||||||
cmd.Stdout = os.Stdout
|
if names, ok := commands[spec.Compiler]; ok {
|
||||||
cmd.Stderr = os.Stderr
|
cmdNames = names
|
||||||
cmd.Dir = sourceDir()
|
}
|
||||||
err := cmd.Run()
|
err := execCommand(cmdNames, append(cflags, "-c", "-o", outpath, abspath)...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &commandError{"failed to build", path, err}
|
return &commandError{"failed to build", path, err}
|
||||||
}
|
}
|
||||||
@@ -215,11 +246,11 @@ func Compile(pkgName, outpath string, spec *TargetSpec, config *BuildConfig, act
|
|||||||
for _, file := range pkg.CFiles {
|
for _, file := range pkg.CFiles {
|
||||||
path := filepath.Join(pkg.Package.Dir, file)
|
path := filepath.Join(pkg.Package.Dir, file)
|
||||||
outpath := filepath.Join(dir, "pkg"+strconv.Itoa(i)+"-"+file+".o")
|
outpath := filepath.Join(dir, "pkg"+strconv.Itoa(i)+"-"+file+".o")
|
||||||
cmd := exec.Command(spec.Compiler, append(spec.CFlags, "-c", "-o", outpath, path)...)
|
cmdNames := []string{spec.Compiler}
|
||||||
cmd.Stdout = os.Stdout
|
if names, ok := commands[spec.Compiler]; ok {
|
||||||
cmd.Stderr = os.Stderr
|
cmdNames = names
|
||||||
cmd.Dir = sourceDir()
|
}
|
||||||
err := cmd.Run()
|
err := execCommand(cmdNames, append(cflags, "-c", "-o", outpath, path)...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &commandError{"failed to build", path, err}
|
return &commandError{"failed to build", path, err}
|
||||||
}
|
}
|
||||||
@@ -228,11 +259,7 @@ func Compile(pkgName, outpath string, spec *TargetSpec, config *BuildConfig, act
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Link the object files together.
|
// Link the object files together.
|
||||||
if linker, ok := commands[spec.Linker]; ok {
|
err = Link(spec.Linker, ldflags...)
|
||||||
err = Link(linker, ldflags...)
|
|
||||||
} else {
|
|
||||||
err = Link(spec.Linker, ldflags...)
|
|
||||||
}
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &commandError{"failed to link", executable, err}
|
return &commandError{"failed to link", executable, err}
|
||||||
}
|
}
|
||||||
@@ -496,6 +523,10 @@ func handleCompilerError(err error) {
|
|||||||
for _, err := range errLoader.Errs {
|
for _, err := range errLoader.Errs {
|
||||||
fmt.Fprintln(os.Stderr, err)
|
fmt.Fprintln(os.Stderr, err)
|
||||||
}
|
}
|
||||||
|
} else if errMulti, ok := err.(*multiError); ok {
|
||||||
|
for _, err := range errMulti.Errs {
|
||||||
|
fmt.Fprintln(os.Stderr, err)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
fmt.Fprintln(os.Stderr, "error:", err)
|
fmt.Fprintln(os.Stderr, "error:", err)
|
||||||
}
|
}
|
||||||
@@ -507,6 +538,7 @@ func main() {
|
|||||||
outpath := flag.String("o", "", "output filename")
|
outpath := flag.String("o", "", "output filename")
|
||||||
opt := flag.String("opt", "z", "optimization level: 0, 1, 2, s, z")
|
opt := flag.String("opt", "z", "optimization level: 0, 1, 2, s, z")
|
||||||
gc := flag.String("gc", "", "garbage collector to use (none, dumb, marksweep)")
|
gc := flag.String("gc", "", "garbage collector to use (none, dumb, marksweep)")
|
||||||
|
panicStrategy := flag.String("panic", "print", "panic strategy (abort, trap)")
|
||||||
printIR := flag.Bool("printir", false, "print LLVM IR")
|
printIR := flag.Bool("printir", false, "print LLVM IR")
|
||||||
dumpSSA := flag.Bool("dumpssa", false, "dump internal Go SSA")
|
dumpSSA := flag.Bool("dumpssa", false, "dump internal Go SSA")
|
||||||
target := flag.String("target", "", "LLVM target")
|
target := flag.String("target", "", "LLVM target")
|
||||||
@@ -527,13 +559,14 @@ func main() {
|
|||||||
|
|
||||||
flag.CommandLine.Parse(os.Args[2:])
|
flag.CommandLine.Parse(os.Args[2:])
|
||||||
config := &BuildConfig{
|
config := &BuildConfig{
|
||||||
opt: *opt,
|
opt: *opt,
|
||||||
gc: *gc,
|
gc: *gc,
|
||||||
printIR: *printIR,
|
panicStrategy: *panicStrategy,
|
||||||
dumpSSA: *dumpSSA,
|
printIR: *printIR,
|
||||||
debug: !*nodebug,
|
dumpSSA: *dumpSSA,
|
||||||
printSizes: *printSize,
|
debug: !*nodebug,
|
||||||
wasmAbi: *wasmAbi,
|
printSizes: *printSize,
|
||||||
|
wasmAbi: *wasmAbi,
|
||||||
}
|
}
|
||||||
|
|
||||||
if *cFlags != "" {
|
if *cFlags != "" {
|
||||||
@@ -544,6 +577,12 @@ func main() {
|
|||||||
config.ldFlags = strings.Split(*ldFlags, " ")
|
config.ldFlags = strings.Split(*ldFlags, " ")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if *panicStrategy != "print" && *panicStrategy != "trap" {
|
||||||
|
fmt.Fprintln(os.Stderr, "Panic strategy must be either print or trap.")
|
||||||
|
usage()
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
os.Setenv("CC", "clang -target="+*target)
|
os.Setenv("CC", "clang -target="+*target)
|
||||||
|
|
||||||
switch command {
|
switch command {
|
||||||
|
|||||||
+15
-7
@@ -13,17 +13,19 @@ import (
|
|||||||
"runtime"
|
"runtime"
|
||||||
"sort"
|
"sort"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/tinygo-org/tinygo/loader"
|
||||||
)
|
)
|
||||||
|
|
||||||
const TESTDATA = "testdata"
|
const TESTDATA = "testdata"
|
||||||
|
|
||||||
func TestCompiler(t *testing.T) {
|
func TestCompiler(t *testing.T) {
|
||||||
matches, err := filepath.Glob(TESTDATA + "/*.go")
|
matches, err := filepath.Glob(filepath.Join(TESTDATA, "*.go"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal("could not read test files:", err)
|
t.Fatal("could not read test files:", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
dirMatches, err := filepath.Glob(TESTDATA + "/*/main.go")
|
dirMatches, err := filepath.Glob(filepath.Join(TESTDATA, "*", "main.go"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal("could not read test packages:", err)
|
t.Fatal("could not read test packages:", err)
|
||||||
}
|
}
|
||||||
@@ -64,7 +66,7 @@ func TestCompiler(t *testing.T) {
|
|||||||
if runtime.GOOS == "linux" {
|
if runtime.GOOS == "linux" {
|
||||||
t.Log("running tests for linux/arm...")
|
t.Log("running tests for linux/arm...")
|
||||||
for _, path := range matches {
|
for _, path := range matches {
|
||||||
if path == "testdata/cgo/" {
|
if path == filepath.Join("testdata", "cgo")+string(filepath.Separator) {
|
||||||
continue // TODO: improve CGo
|
continue // TODO: improve CGo
|
||||||
}
|
}
|
||||||
t.Run(path, func(t *testing.T) {
|
t.Run(path, func(t *testing.T) {
|
||||||
@@ -74,7 +76,7 @@ func TestCompiler(t *testing.T) {
|
|||||||
|
|
||||||
t.Log("running tests for linux/arm64...")
|
t.Log("running tests for linux/arm64...")
|
||||||
for _, path := range matches {
|
for _, path := range matches {
|
||||||
if path == "testdata/cgo/" {
|
if path == filepath.Join("testdata", "cgo")+string(filepath.Separator) {
|
||||||
continue // TODO: improve CGo
|
continue // TODO: improve CGo
|
||||||
}
|
}
|
||||||
t.Run(path, func(t *testing.T) {
|
t.Run(path, func(t *testing.T) {
|
||||||
@@ -84,7 +86,7 @@ func TestCompiler(t *testing.T) {
|
|||||||
|
|
||||||
t.Log("running tests for WebAssembly...")
|
t.Log("running tests for WebAssembly...")
|
||||||
for _, path := range matches {
|
for _, path := range matches {
|
||||||
if path == "testdata/gc.go" {
|
if path == filepath.Join("testdata", "gc.go") {
|
||||||
continue // known to fail
|
continue // known to fail
|
||||||
}
|
}
|
||||||
t.Run(path, func(t *testing.T) {
|
t.Run(path, func(t *testing.T) {
|
||||||
@@ -97,7 +99,7 @@ func TestCompiler(t *testing.T) {
|
|||||||
func runTest(path, tmpdir string, target string, t *testing.T) {
|
func runTest(path, tmpdir string, target string, t *testing.T) {
|
||||||
// Get the expected output for this test.
|
// Get the expected output for this test.
|
||||||
txtpath := path[:len(path)-3] + ".txt"
|
txtpath := path[:len(path)-3] + ".txt"
|
||||||
if path[len(path)-1] == '/' {
|
if path[len(path)-1] == os.PathSeparator {
|
||||||
txtpath = path + "out.txt"
|
txtpath = path + "out.txt"
|
||||||
}
|
}
|
||||||
f, err := os.Open(txtpath)
|
f, err := os.Open(txtpath)
|
||||||
@@ -121,7 +123,13 @@ func runTest(path, tmpdir string, target string, t *testing.T) {
|
|||||||
binary := filepath.Join(tmpdir, "test")
|
binary := filepath.Join(tmpdir, "test")
|
||||||
err = Build("./"+path, binary, target, config)
|
err = Build("./"+path, binary, target, config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Log("failed to build:", err)
|
if errLoader, ok := err.(loader.Errors); ok {
|
||||||
|
for _, err := range errLoader.Errs {
|
||||||
|
t.Log("failed to build:", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
t.Log("failed to build:", err)
|
||||||
|
}
|
||||||
t.Fail()
|
t.Fail()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// blink program for the BBC micro:bit that uses the entire LED matrix
|
// blink program for the BBC micro:bit
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -6,14 +6,19 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// The LED matrix in the micro:bit is a multiplexed display: https://en.wikipedia.org/wiki/Multiplexed_display
|
||||||
|
// Driver for easier control: https://github.com/tinygo-org/drivers/tree/master/microbitmatrix
|
||||||
func main() {
|
func main() {
|
||||||
machine.InitLEDMatrix()
|
ledrow := machine.GPIO{machine.LED_ROW_1}
|
||||||
|
ledrow.Configure(machine.GPIOConfig{Mode: machine.GPIO_OUTPUT})
|
||||||
|
ledcol := machine.GPIO{machine.LED_COL_1}
|
||||||
|
ledcol.Configure(machine.GPIOConfig{Mode: machine.GPIO_OUTPUT})
|
||||||
|
ledcol.Low()
|
||||||
for {
|
for {
|
||||||
machine.ClearLEDMatrix()
|
ledrow.Low()
|
||||||
time.Sleep(time.Millisecond * 500)
|
time.Sleep(time.Millisecond * 500)
|
||||||
|
|
||||||
machine.SetEntireLEDMatrixOn()
|
ledrow.High()
|
||||||
time.Sleep(time.Millisecond * 500)
|
time.Sleep(time.Millisecond * 500)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+36
-16
@@ -1,6 +1,6 @@
|
|||||||
# TinyGo WebAssembly examples
|
# TinyGo WebAssembly examples
|
||||||
|
|
||||||
The examples here show two different ways of using WebAssembly with TinyGo;
|
The examples here show two different ways of using WebAssembly with TinyGo:
|
||||||
|
|
||||||
1. Defining and exporting functions via the `//go:export <name>` directive. See
|
1. Defining and exporting functions via the `//go:export <name>` directive. See
|
||||||
[the export folder](./export) for an example of this.
|
[the export folder](./export) for an example of this.
|
||||||
@@ -31,23 +31,31 @@ $ make main
|
|||||||
|
|
||||||
## Running
|
## Running
|
||||||
|
|
||||||
Start the local webserver:
|
Start the local web server:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ go run main.go
|
$ go run main.go
|
||||||
Serving ./html on http://localhost:8080
|
Serving ./html on http://localhost:8080
|
||||||
```
|
```
|
||||||
|
|
||||||
`fmt.Println` prints to the browser console.
|
Use your web browser to visit http://localhost:8080.
|
||||||
|
|
||||||
|
* The wasm "export" example displays a simple math equation using HTML, with
|
||||||
|
the result calculated dynamically using WebAssembly. Changing any of the
|
||||||
|
values on the left hand side triggers the exported wasm `update` function to
|
||||||
|
recalculate the result.
|
||||||
|
* The wasm "main" example uses `println` to write to your browser JavaScript
|
||||||
|
console. You may need to open the browser development tools console to see it.
|
||||||
|
|
||||||
## How it works
|
## How it works
|
||||||
|
|
||||||
Execution of the contents require a few JS helper functions which are called
|
Execution of the contents require a few JavaScript helper functions which are
|
||||||
from WebAssembly. We have defined these in
|
called from WebAssembly.
|
||||||
[wasm_exec.js](../../../targets/wasm_exec.js). It is based on
|
|
||||||
`$GOROOT/misc/wasm/wasm_exec.js` from the standard library, but is slightly
|
We have defined these in [wasm_exec.js](../../../targets/wasm_exec.js). It is
|
||||||
different. Ensure you are using the same version of `wasm_exec.js` as the
|
based on `$GOROOT/misc/wasm/wasm_exec.js` from the standard library, but is
|
||||||
version of `tinygo` you are using to compile.
|
slightly different. Ensure you are using the same version of `wasm_exec.js` as
|
||||||
|
the version of `tinygo` you are using to compile.
|
||||||
|
|
||||||
The general steps required to run the WebAssembly file in the browser includes
|
The general steps required to run the WebAssembly file in the browser includes
|
||||||
loading it into JavaScript with `WebAssembly.instantiateStreaming`, or
|
loading it into JavaScript with `WebAssembly.instantiateStreaming`, or
|
||||||
@@ -80,8 +88,9 @@ If you have used explicit exports, you can call them by invoking them under the
|
|||||||
`wasm.exports` namespace. See the [`export`](./export/wasm.js) directory for an
|
`wasm.exports` namespace. See the [`export`](./export/wasm.js) directory for an
|
||||||
example of this.
|
example of this.
|
||||||
|
|
||||||
In addition to this piece of JavaScript, it is important that the file is served
|
In addition to the JavaScript, it is important the wasm file is served with the
|
||||||
with the correct `Content-Type` header set.
|
[`Content-Type`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Type)
|
||||||
|
header set to `application/wasm`. Without it, most browsers won't run it.
|
||||||
|
|
||||||
```go
|
```go
|
||||||
package main
|
package main
|
||||||
@@ -98,14 +107,25 @@ func main() {
|
|||||||
fs := http.FileServer(http.Dir(dir))
|
fs := http.FileServer(http.Dir(dir))
|
||||||
log.Print("Serving " + dir + " on http://localhost:8080")
|
log.Print("Serving " + dir + " on http://localhost:8080")
|
||||||
http.ListenAndServe(":8080", http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
|
http.ListenAndServe(":8080", http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
|
||||||
|
resp.Header().Add("Cache-Control", "no-cache")
|
||||||
if strings.HasSuffix(req.URL.Path, ".wasm") {
|
if strings.HasSuffix(req.URL.Path, ".wasm") {
|
||||||
resp.Header().Set("content-type", "application/wasm")
|
resp.Header().Set("content-type", "application/wasm")
|
||||||
}
|
}
|
||||||
|
|
||||||
fs.ServeHTTP(resp, req)
|
fs.ServeHTTP(resp, req)
|
||||||
}))
|
}))}
|
||||||
}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
This simple server serves anything inside the `./html` directory on port `8080`,
|
This simple server serves anything inside the `./html` directory on port
|
||||||
setting any `*.wasm` files `Content-Type` header appropriately.
|
`8080`, setting any `*.wasm` files `Content-Type` header appropriately.
|
||||||
|
|
||||||
|
For development purposes (**only!**), it also sets the `Cache-Control` header
|
||||||
|
so your browser doesn't cache the files. This is useful while developing, to
|
||||||
|
ensure your browser displays the newest wasm when you recompile.
|
||||||
|
|
||||||
|
In a production environment you **probably wouldn't** want to set the
|
||||||
|
`Cache-Control` header like this. Caching is generally beneficial for end
|
||||||
|
users.
|
||||||
|
|
||||||
|
Further information on the `Cache-Control` header can be found here:
|
||||||
|
|
||||||
|
* https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control
|
||||||
|
|||||||
@@ -1,9 +1,5 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
fmt.Println("Hello world!")
|
println("Hello world!")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,10 +12,10 @@ func main() {
|
|||||||
fs := http.FileServer(http.Dir(dir))
|
fs := http.FileServer(http.Dir(dir))
|
||||||
log.Print("Serving " + dir + " on http://localhost:8080")
|
log.Print("Serving " + dir + " on http://localhost:8080")
|
||||||
http.ListenAndServe(":8080", http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
|
http.ListenAndServe(":8080", http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
|
||||||
|
resp.Header().Add("Cache-Control", "no-cache")
|
||||||
if strings.HasSuffix(req.URL.Path, ".wasm") {
|
if strings.HasSuffix(req.URL.Path, ".wasm") {
|
||||||
resp.Header().Set("content-type", "application/wasm")
|
resp.Header().Set("content-type", "application/wasm")
|
||||||
}
|
}
|
||||||
|
|
||||||
fs.ServeHTTP(resp, req)
|
fs.ServeHTTP(resp, req)
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,11 +2,6 @@
|
|||||||
|
|
||||||
package machine
|
package machine
|
||||||
|
|
||||||
import (
|
|
||||||
"device/nrf"
|
|
||||||
"errors"
|
|
||||||
)
|
|
||||||
|
|
||||||
// The micro:bit does not have a 32kHz crystal on board.
|
// The micro:bit does not have a 32kHz crystal on board.
|
||||||
const HasLowFrequencyCrystal = false
|
const HasLowFrequencyCrystal = false
|
||||||
|
|
||||||
@@ -79,65 +74,3 @@ const (
|
|||||||
LED_ROW_2 = 14
|
LED_ROW_2 = 14
|
||||||
LED_ROW_3 = 15
|
LED_ROW_3 = 15
|
||||||
)
|
)
|
||||||
|
|
||||||
// matrixSettings has the legs of the LED grid in the form {row, column} for each LED position.
|
|
||||||
var matrixSettings = [5][5][2]uint8{
|
|
||||||
{{LED_ROW_1, LED_COL_1}, {LED_ROW_2, LED_COL_4}, {LED_ROW_1, LED_COL_2}, {LED_ROW_2, LED_COL_5}, {LED_ROW_1, LED_COL_3}},
|
|
||||||
{{LED_ROW_3, LED_COL_4}, {LED_ROW_3, LED_COL_5}, {LED_ROW_3, LED_COL_6}, {LED_ROW_3, LED_COL_7}, {LED_ROW_3, LED_COL_8}},
|
|
||||||
{{LED_ROW_2, LED_COL_2}, {LED_ROW_1, LED_COL_9}, {LED_ROW_2, LED_COL_3}, {LED_ROW_3, LED_COL_9}, {LED_ROW_2, LED_COL_1}},
|
|
||||||
{{LED_ROW_1, LED_COL_8}, {LED_ROW_1, LED_COL_7}, {LED_ROW_1, LED_COL_6}, {LED_ROW_1, LED_COL_5}, {LED_ROW_1, LED_COL_4}},
|
|
||||||
{{LED_ROW_3, LED_COL_3}, {LED_ROW_2, LED_COL_7}, {LED_ROW_3, LED_COL_1}, {LED_ROW_2, LED_COL_6}, {LED_ROW_3, LED_COL_2}}}
|
|
||||||
|
|
||||||
// InitLEDMatrix initializes the LED matrix, by setting all of the row/col pins to output
|
|
||||||
// then calling ClearLEDMatrix.
|
|
||||||
func InitLEDMatrix() {
|
|
||||||
set := 0
|
|
||||||
for i := LED_COL_1; i <= LED_ROW_3; i++ {
|
|
||||||
set |= 1 << uint8(i)
|
|
||||||
}
|
|
||||||
nrf.GPIO.DIRSET = nrf.RegValue(set)
|
|
||||||
ClearLEDMatrix()
|
|
||||||
}
|
|
||||||
|
|
||||||
// ClearLEDMatrix clears the entire LED matrix.
|
|
||||||
func ClearLEDMatrix() {
|
|
||||||
set := 0
|
|
||||||
for i := LED_COL_1; i <= LED_COL_9; i++ {
|
|
||||||
set |= 1 << uint8(i)
|
|
||||||
}
|
|
||||||
nrf.GPIO.OUTSET = nrf.RegValue(set)
|
|
||||||
nrf.GPIO.OUTCLR = (1 << LED_ROW_1) | (1 << LED_ROW_2) | (1 << LED_ROW_3)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetLEDMatrix turns on a single LED on the LED matrix.
|
|
||||||
// Currently limited to a single LED at a time, it will clear the matrix before setting it.
|
|
||||||
func SetLEDMatrix(x, y uint8) error {
|
|
||||||
if x > 4 || y > 4 {
|
|
||||||
return errors.New("Invalid LED matrix row or column")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clear matrix
|
|
||||||
ClearLEDMatrix()
|
|
||||||
|
|
||||||
nrf.GPIO.OUTSET = (1 << matrixSettings[y][x][0])
|
|
||||||
nrf.GPIO.OUTCLR = (1 << matrixSettings[y][x][1])
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetEntireLEDMatrixOn turns on all of the LEDs on the LED matrix.
|
|
||||||
func SetEntireLEDMatrixOn() error {
|
|
||||||
set := 0
|
|
||||||
for i := LED_ROW_1; i <= LED_ROW_3; i++ {
|
|
||||||
set |= 1 << uint8(i)
|
|
||||||
}
|
|
||||||
nrf.GPIO.OUTSET = nrf.RegValue(set)
|
|
||||||
|
|
||||||
set = 0
|
|
||||||
for i := LED_COL_1; i <= LED_COL_9; i++ {
|
|
||||||
set |= 1 << uint8(i)
|
|
||||||
}
|
|
||||||
nrf.GPIO.OUTCLR = nrf.RegValue(set)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -343,6 +343,12 @@ func (a ADC) Get() uint16 {
|
|||||||
sam.ADC.INPUTCTRL |= sam.RegValue(ch << sam.ADC_INPUTCTRL_MUXPOS_Pos)
|
sam.ADC.INPUTCTRL |= sam.RegValue(ch << sam.ADC_INPUTCTRL_MUXPOS_Pos)
|
||||||
waitADCSync()
|
waitADCSync()
|
||||||
|
|
||||||
|
// Select internal ground for ADC input
|
||||||
|
sam.ADC.INPUTCTRL &^= sam.ADC_INPUTCTRL_MUXNEG_Msk
|
||||||
|
waitADCSync()
|
||||||
|
sam.ADC.INPUTCTRL |= sam.RegValue(sam.ADC_INPUTCTRL_MUXNEG_GND << sam.ADC_INPUTCTRL_MUXNEG_Pos)
|
||||||
|
waitADCSync()
|
||||||
|
|
||||||
// Enable ADC
|
// Enable ADC
|
||||||
sam.ADC.CTRLA |= sam.ADC_CTRLA_ENABLE
|
sam.ADC.CTRLA |= sam.ADC_CTRLA_ENABLE
|
||||||
waitADCSync()
|
waitADCSync()
|
||||||
@@ -368,7 +374,7 @@ func (a ADC) Get() uint16 {
|
|||||||
sam.ADC.CTRLA &^= sam.ADC_CTRLA_ENABLE
|
sam.ADC.CTRLA &^= sam.ADC_CTRLA_ENABLE
|
||||||
waitADCSync()
|
waitADCSync()
|
||||||
|
|
||||||
return uint16(val)
|
return uint16(val) << 4 // scales from 12 to 16-bit result
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a ADC) getADCChannel() uint8 {
|
func (a ADC) getADCChannel() uint8 {
|
||||||
|
|||||||
@@ -12,29 +12,77 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// Fake LED numbers, for testing.
|
// Fake LED numbers, for testing.
|
||||||
const (
|
var (
|
||||||
LED = LED1
|
LED uint8 = LED1
|
||||||
LED1 = 0
|
LED1 uint8 = 0
|
||||||
LED2 = 0
|
LED2 uint8 = 0
|
||||||
LED3 = 0
|
LED3 uint8 = 0
|
||||||
LED4 = 0
|
LED4 uint8 = 0
|
||||||
)
|
)
|
||||||
|
|
||||||
// Fake button numbers, for testing.
|
// Fake button numbers, for testing.
|
||||||
const (
|
var (
|
||||||
BUTTON = BUTTON1
|
BUTTON uint8 = BUTTON1
|
||||||
BUTTON1 = 0
|
BUTTON1 uint8 = 5
|
||||||
BUTTON2 = 0
|
BUTTON2 uint8 = 6
|
||||||
BUTTON3 = 0
|
BUTTON3 uint8 = 7
|
||||||
BUTTON4 = 0
|
BUTTON4 uint8 = 8
|
||||||
|
)
|
||||||
|
|
||||||
|
// Fake SPI interfaces, for testing.
|
||||||
|
var (
|
||||||
|
SPI0 = SPI{0}
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
GPIOConfigure func(pin uint8, config GPIOConfig)
|
||||||
|
GPIOSet func(pin uint8, value bool)
|
||||||
|
GPIOGet func(pin uint8) bool
|
||||||
|
SPIConfigure func(bus uint8, sck uint8, mosi uint8, miso uint8)
|
||||||
|
SPITransfer func(bus uint8, w uint8) uint8
|
||||||
)
|
)
|
||||||
|
|
||||||
func (p GPIO) Configure(config GPIOConfig) {
|
func (p GPIO) Configure(config GPIOConfig) {
|
||||||
|
if GPIOConfigure != nil {
|
||||||
|
GPIOConfigure(p.Pin, config)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p GPIO) Set(value bool) {
|
func (p GPIO) Set(value bool) {
|
||||||
|
if GPIOSet != nil {
|
||||||
|
GPIOSet(p.Pin, value)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p GPIO) Get() bool {
|
func (p GPIO) Get() bool {
|
||||||
|
if GPIOGet != nil {
|
||||||
|
return GPIOGet(p.Pin)
|
||||||
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type SPI struct {
|
||||||
|
Bus uint8
|
||||||
|
}
|
||||||
|
|
||||||
|
type SPIConfig struct {
|
||||||
|
Frequency uint32
|
||||||
|
SCK uint8
|
||||||
|
MOSI uint8
|
||||||
|
MISO uint8
|
||||||
|
Mode uint8
|
||||||
|
}
|
||||||
|
|
||||||
|
func (spi SPI) Configure(config SPIConfig) {
|
||||||
|
if SPIConfigure != nil {
|
||||||
|
SPIConfigure(spi.Bus, config.SCK, config.MOSI, config.MISO)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transfer writes/reads a single byte using the SPI interface.
|
||||||
|
func (spi SPI) Transfer(w byte) (byte, error) {
|
||||||
|
if SPITransfer != nil {
|
||||||
|
return SPITransfer(spi.Bus, w), nil
|
||||||
|
}
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
|||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
// +build nrf stm32f103xx atsamd21g18a
|
// +build js,wasm nrf stm32f103xx atsamd21g18a
|
||||||
|
|
||||||
package machine
|
package machine
|
||||||
|
|
||||||
|
|||||||
+14
-23
@@ -39,33 +39,27 @@ const (
|
|||||||
chanStateClosed
|
chanStateClosed
|
||||||
)
|
)
|
||||||
|
|
||||||
func chanSendStub(caller *coroutine, ch *channel, _ unsafe.Pointer, size uintptr)
|
|
||||||
func chanRecvStub(caller *coroutine, ch *channel, _ unsafe.Pointer, _ *bool, size uintptr)
|
|
||||||
func deadlockStub()
|
func deadlockStub()
|
||||||
|
|
||||||
// chanSend sends a single value over the channel. If this operation can
|
// chanSend sends a single value over the channel. If this operation can
|
||||||
// complete immediately (there is a goroutine waiting for a value), it sends the
|
// complete immediately (there is a goroutine waiting for a value), it sends the
|
||||||
// value and re-activates both goroutines. If not, it sets itself as waiting on
|
// value and re-activates both goroutines. If not, it sets itself as waiting on
|
||||||
// a value.
|
// a value.
|
||||||
//
|
func chanSend(sender *coroutine, ch *channel, value unsafe.Pointer, size uintptr) {
|
||||||
// The unsafe.Pointer value is used during lowering. During IR generation, it
|
|
||||||
// points to the to-be-received value. During coroutine lowering, this value is
|
|
||||||
// replaced with a read from the coroutine promise.
|
|
||||||
func chanSend(sender *coroutine, ch *channel, _ unsafe.Pointer, size uintptr) {
|
|
||||||
if ch == nil {
|
if ch == nil {
|
||||||
// A nil channel blocks forever. Do not scheduler this goroutine again.
|
// A nil channel blocks forever. Do not scheduler this goroutine again.
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
switch ch.state {
|
switch ch.state {
|
||||||
case chanStateEmpty:
|
case chanStateEmpty:
|
||||||
|
sender.promise().ptr = value
|
||||||
ch.state = chanStateSend
|
ch.state = chanStateSend
|
||||||
ch.blocked = sender
|
ch.blocked = sender
|
||||||
case chanStateRecv:
|
case chanStateRecv:
|
||||||
receiver := ch.blocked
|
receiver := ch.blocked
|
||||||
receiverPromise := receiver.promise()
|
receiverPromise := receiver.promise()
|
||||||
senderPromise := sender.promise()
|
memcpy(receiverPromise.ptr, value, size)
|
||||||
memcpy(unsafe.Pointer(&receiverPromise.data), unsafe.Pointer(&senderPromise.data), size)
|
receiverPromise.data = 1 // commaOk = true
|
||||||
receiverPromise.commaOk = true
|
|
||||||
ch.blocked = receiverPromise.next
|
ch.blocked = receiverPromise.next
|
||||||
receiverPromise.next = nil
|
receiverPromise.next = nil
|
||||||
activateTask(receiver)
|
activateTask(receiver)
|
||||||
@@ -76,6 +70,7 @@ func chanSend(sender *coroutine, ch *channel, _ unsafe.Pointer, size uintptr) {
|
|||||||
case chanStateClosed:
|
case chanStateClosed:
|
||||||
runtimePanic("send on closed channel")
|
runtimePanic("send on closed channel")
|
||||||
case chanStateSend:
|
case chanStateSend:
|
||||||
|
sender.promise().ptr = value
|
||||||
sender.promise().next = ch.blocked
|
sender.promise().next = ch.blocked
|
||||||
ch.blocked = sender
|
ch.blocked = sender
|
||||||
}
|
}
|
||||||
@@ -85,11 +80,7 @@ func chanSend(sender *coroutine, ch *channel, _ unsafe.Pointer, size uintptr) {
|
|||||||
// sender, it receives the value immediately and re-activates both coroutines.
|
// sender, it receives the value immediately and re-activates both coroutines.
|
||||||
// If not, it sets itself as available for receiving. If the channel is closed,
|
// If not, it sets itself as available for receiving. If the channel is closed,
|
||||||
// it immediately activates itself with a zero value as the result.
|
// it immediately activates itself with a zero value as the result.
|
||||||
//
|
func chanRecv(receiver *coroutine, ch *channel, value unsafe.Pointer, size uintptr) {
|
||||||
// The two unnamed values exist to help during lowering. The unsafe.Pointer
|
|
||||||
// points to the value, and the *bool points to the comma-ok value. Both are
|
|
||||||
// replaced by reads from the coroutine promise.
|
|
||||||
func chanRecv(receiver *coroutine, ch *channel, _ unsafe.Pointer, _ *bool, size uintptr) {
|
|
||||||
if ch == nil {
|
if ch == nil {
|
||||||
// A nil channel blocks forever. Do not scheduler this goroutine again.
|
// A nil channel blocks forever. Do not scheduler this goroutine again.
|
||||||
return
|
return
|
||||||
@@ -97,10 +88,9 @@ func chanRecv(receiver *coroutine, ch *channel, _ unsafe.Pointer, _ *bool, size
|
|||||||
switch ch.state {
|
switch ch.state {
|
||||||
case chanStateSend:
|
case chanStateSend:
|
||||||
sender := ch.blocked
|
sender := ch.blocked
|
||||||
receiverPromise := receiver.promise()
|
|
||||||
senderPromise := sender.promise()
|
senderPromise := sender.promise()
|
||||||
memcpy(unsafe.Pointer(&receiverPromise.data), unsafe.Pointer(&senderPromise.data), size)
|
memcpy(value, senderPromise.ptr, size)
|
||||||
receiverPromise.commaOk = true
|
receiver.promise().data = 1 // commaOk = true
|
||||||
ch.blocked = senderPromise.next
|
ch.blocked = senderPromise.next
|
||||||
senderPromise.next = nil
|
senderPromise.next = nil
|
||||||
activateTask(receiver)
|
activateTask(receiver)
|
||||||
@@ -109,14 +99,15 @@ func chanRecv(receiver *coroutine, ch *channel, _ unsafe.Pointer, _ *bool, size
|
|||||||
ch.state = chanStateEmpty
|
ch.state = chanStateEmpty
|
||||||
}
|
}
|
||||||
case chanStateEmpty:
|
case chanStateEmpty:
|
||||||
|
receiver.promise().ptr = value
|
||||||
ch.state = chanStateRecv
|
ch.state = chanStateRecv
|
||||||
ch.blocked = receiver
|
ch.blocked = receiver
|
||||||
case chanStateClosed:
|
case chanStateClosed:
|
||||||
receiverPromise := receiver.promise()
|
memzero(value, size)
|
||||||
memzero(unsafe.Pointer(&receiverPromise.data), size)
|
receiver.promise().data = 0 // commaOk = false
|
||||||
receiverPromise.commaOk = false
|
|
||||||
activateTask(receiver)
|
activateTask(receiver)
|
||||||
case chanStateRecv:
|
case chanStateRecv:
|
||||||
|
receiver.promise().ptr = value
|
||||||
receiver.promise().next = ch.blocked
|
receiver.promise().next = ch.blocked
|
||||||
ch.blocked = receiver
|
ch.blocked = receiver
|
||||||
}
|
}
|
||||||
@@ -142,8 +133,8 @@ func chanClose(ch *channel, size uintptr) {
|
|||||||
case chanStateRecv:
|
case chanStateRecv:
|
||||||
// The receiver must be re-activated with a zero value.
|
// The receiver must be re-activated with a zero value.
|
||||||
receiverPromise := ch.blocked.promise()
|
receiverPromise := ch.blocked.promise()
|
||||||
memzero(unsafe.Pointer(&receiverPromise.data), size)
|
memzero(receiverPromise.ptr, size)
|
||||||
receiverPromise.commaOk = false
|
receiverPromise.data = 0 // commaOk = false
|
||||||
activateTask(ch.blocked)
|
activateTask(ch.blocked)
|
||||||
ch.state = chanStateClosed
|
ch.state = chanStateClosed
|
||||||
ch.blocked = nil
|
ch.blocked = nil
|
||||||
|
|||||||
@@ -0,0 +1,65 @@
|
|||||||
|
// Copyright 2010 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package runtime
|
||||||
|
|
||||||
|
// inf2one returns a signed 1 if f is an infinity and a signed 0 otherwise.
|
||||||
|
// The sign of the result is the sign of f.
|
||||||
|
func inf2one(f float64) float64 {
|
||||||
|
g := 0.0
|
||||||
|
if isInf(f) {
|
||||||
|
g = 1.0
|
||||||
|
}
|
||||||
|
return copysign(g, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
func complex64div(n complex64, m complex64) complex64 {
|
||||||
|
return complex64(complex128div(complex128(n), complex128(m)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func complex128div(n complex128, m complex128) complex128 {
|
||||||
|
var e, f float64 // complex(e, f) = n/m
|
||||||
|
|
||||||
|
// Algorithm for robust complex division as described in
|
||||||
|
// Robert L. Smith: Algorithm 116: Complex division. Commun. ACM 5(8): 435 (1962).
|
||||||
|
if abs(real(m)) >= abs(imag(m)) {
|
||||||
|
ratio := imag(m) / real(m)
|
||||||
|
denom := real(m) + ratio*imag(m)
|
||||||
|
e = (real(n) + imag(n)*ratio) / denom
|
||||||
|
f = (imag(n) - real(n)*ratio) / denom
|
||||||
|
} else {
|
||||||
|
ratio := real(m) / imag(m)
|
||||||
|
denom := imag(m) + ratio*real(m)
|
||||||
|
e = (real(n)*ratio + imag(n)) / denom
|
||||||
|
f = (imag(n)*ratio - real(n)) / denom
|
||||||
|
}
|
||||||
|
|
||||||
|
if isNaN(e) && isNaN(f) {
|
||||||
|
// Correct final result to infinities and zeros if applicable.
|
||||||
|
// Matches C99: ISO/IEC 9899:1999 - G.5.1 Multiplicative operators.
|
||||||
|
|
||||||
|
a, b := real(n), imag(n)
|
||||||
|
c, d := real(m), imag(m)
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case m == 0 && (!isNaN(a) || !isNaN(b)):
|
||||||
|
e = copysign(inf, c) * a
|
||||||
|
f = copysign(inf, c) * b
|
||||||
|
|
||||||
|
case (isInf(a) || isInf(b)) && isFinite(c) && isFinite(d):
|
||||||
|
a = inf2one(a)
|
||||||
|
b = inf2one(b)
|
||||||
|
e = inf * (a*c + b*d)
|
||||||
|
f = inf * (b*c - a*d)
|
||||||
|
|
||||||
|
case (isInf(c) || isInf(d)) && isFinite(a) && isFinite(b):
|
||||||
|
c = inf2one(c)
|
||||||
|
d = inf2one(d)
|
||||||
|
e = 0 * (a*c + b*d)
|
||||||
|
f = 0 * (b*c - a*d)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return complex(e, f)
|
||||||
|
}
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
// Copyright 2017 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package runtime
|
||||||
|
|
||||||
|
import "unsafe"
|
||||||
|
|
||||||
|
var inf = float64frombits(0x7FF0000000000000)
|
||||||
|
|
||||||
|
// isNaN reports whether f is an IEEE 754 ``not-a-number'' value.
|
||||||
|
func isNaN(f float64) (is bool) {
|
||||||
|
// IEEE 754 says that only NaNs satisfy f != f.
|
||||||
|
return f != f
|
||||||
|
}
|
||||||
|
|
||||||
|
// isFinite reports whether f is neither NaN nor an infinity.
|
||||||
|
func isFinite(f float64) bool {
|
||||||
|
return !isNaN(f - f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// isInf reports whether f is an infinity.
|
||||||
|
func isInf(f float64) bool {
|
||||||
|
return !isNaN(f) && !isFinite(f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Abs returns the absolute value of x.
|
||||||
|
//
|
||||||
|
// Special cases are:
|
||||||
|
// Abs(±Inf) = +Inf
|
||||||
|
// Abs(NaN) = NaN
|
||||||
|
func abs(x float64) float64 {
|
||||||
|
const sign = 1 << 63
|
||||||
|
return float64frombits(float64bits(x) &^ sign)
|
||||||
|
}
|
||||||
|
|
||||||
|
// copysign returns a value with the magnitude
|
||||||
|
// of x and the sign of y.
|
||||||
|
func copysign(x, y float64) float64 {
|
||||||
|
const sign = 1 << 63
|
||||||
|
return float64frombits(float64bits(x)&^sign | float64bits(y)&sign)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Float64bits returns the IEEE 754 binary representation of f.
|
||||||
|
func float64bits(f float64) uint64 {
|
||||||
|
return *(*uint64)(unsafe.Pointer(&f))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Float64frombits returns the floating point number corresponding
|
||||||
|
// the IEEE 754 binary representation b.
|
||||||
|
func float64frombits(b uint64) float64 {
|
||||||
|
return *(*float64)(unsafe.Pointer(&b))
|
||||||
|
}
|
||||||
@@ -4,6 +4,10 @@ import (
|
|||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type stringer interface {
|
||||||
|
String() string
|
||||||
|
}
|
||||||
|
|
||||||
//go:nobounds
|
//go:nobounds
|
||||||
func printstring(s string) {
|
func printstring(s string) {
|
||||||
for i := 0; i < len(s); i++ {
|
for i := 0; i < len(s); i++ {
|
||||||
@@ -199,8 +203,44 @@ func printnl() {
|
|||||||
|
|
||||||
func printitf(msg interface{}) {
|
func printitf(msg interface{}) {
|
||||||
switch msg := msg.(type) {
|
switch msg := msg.(type) {
|
||||||
|
case bool:
|
||||||
|
print(msg)
|
||||||
|
case int:
|
||||||
|
print(msg)
|
||||||
|
case int8:
|
||||||
|
print(msg)
|
||||||
|
case int16:
|
||||||
|
print(msg)
|
||||||
|
case int32:
|
||||||
|
print(msg)
|
||||||
|
case int64:
|
||||||
|
print(msg)
|
||||||
|
case uint:
|
||||||
|
print(msg)
|
||||||
|
case uint8:
|
||||||
|
print(msg)
|
||||||
|
case uint16:
|
||||||
|
print(msg)
|
||||||
|
case uint32:
|
||||||
|
print(msg)
|
||||||
|
case uint64:
|
||||||
|
print(msg)
|
||||||
|
case uintptr:
|
||||||
|
print(msg)
|
||||||
|
case float32:
|
||||||
|
print(msg)
|
||||||
|
case float64:
|
||||||
|
print(msg)
|
||||||
|
case complex64:
|
||||||
|
print(msg)
|
||||||
|
case complex128:
|
||||||
|
print(msg)
|
||||||
case string:
|
case string:
|
||||||
print(msg)
|
print(msg)
|
||||||
|
case error:
|
||||||
|
print(msg.Error())
|
||||||
|
case stringer:
|
||||||
|
print(msg.String())
|
||||||
default:
|
default:
|
||||||
// cast to underlying type
|
// cast to underlying type
|
||||||
itf := *(*_interface)(unsafe.Pointer(&msg))
|
itf := *(*_interface)(unsafe.Pointer(&msg))
|
||||||
|
|||||||
@@ -49,13 +49,17 @@ func (t *coroutine) promise() *taskState {
|
|||||||
|
|
||||||
func makeGoroutine(*uint8) *uint8
|
func makeGoroutine(*uint8) *uint8
|
||||||
|
|
||||||
|
// Compiler stub to get the current goroutine. Calls to this function are
|
||||||
|
// removed in the goroutine lowering pass.
|
||||||
|
func getCoroutine() *coroutine
|
||||||
|
|
||||||
// State/promise of a task. Internally represented as:
|
// State/promise of a task. Internally represented as:
|
||||||
//
|
//
|
||||||
// {i8* next, i1 commaOk, i32/i64 data}
|
// {i8* next, i1 commaOk, i32/i64 data}
|
||||||
type taskState struct {
|
type taskState struct {
|
||||||
next *coroutine
|
next *coroutine
|
||||||
commaOk bool // 'comma-ok' flag for channel receive operation
|
ptr unsafe.Pointer
|
||||||
data uint
|
data uint
|
||||||
}
|
}
|
||||||
|
|
||||||
// Queues used by the scheduler.
|
// Queues used by the scheduler.
|
||||||
@@ -107,6 +111,24 @@ func activateTask(task *coroutine) {
|
|||||||
runqueuePushBack(task)
|
runqueuePushBack(task)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// getTaskPromisePtr is a helper function to set the current .ptr field of a
|
||||||
|
// coroutine promise.
|
||||||
|
func setTaskPromisePtr(task *coroutine, value unsafe.Pointer) {
|
||||||
|
task.promise().ptr = value
|
||||||
|
}
|
||||||
|
|
||||||
|
// getTaskPromisePtr is a helper function to get the current .ptr field from a
|
||||||
|
// coroutine promise.
|
||||||
|
func getTaskPromisePtr(task *coroutine) unsafe.Pointer {
|
||||||
|
return task.promise().ptr
|
||||||
|
}
|
||||||
|
|
||||||
|
// getTaskPromiseData is a helper function to get the current .data field of a
|
||||||
|
// coroutine promise.
|
||||||
|
func getTaskPromiseData(task *coroutine) uint {
|
||||||
|
return task.promise().data
|
||||||
|
}
|
||||||
|
|
||||||
// Add this task to the end of the run queue. May also destroy the task if it's
|
// Add this task to the end of the run queue. May also destroy the task if it's
|
||||||
// done.
|
// done.
|
||||||
func runqueuePushBack(t *coroutine) {
|
func runqueuePushBack(t *coroutine) {
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
|
"os/exec"
|
||||||
"os/user"
|
"os/user"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
@@ -211,7 +212,7 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
|
|||||||
GOOS: goos,
|
GOOS: goos,
|
||||||
GOARCH: goarch,
|
GOARCH: goarch,
|
||||||
BuildTags: []string{goos, goarch},
|
BuildTags: []string{goos, goarch},
|
||||||
Compiler: commands["clang"],
|
Compiler: "clang",
|
||||||
Linker: "cc",
|
Linker: "cc",
|
||||||
GDB: "gdb",
|
GDB: "gdb",
|
||||||
GDBCmds: []string{"run"},
|
GDBCmds: []string{"run"},
|
||||||
@@ -310,3 +311,60 @@ func getHomeDir() string {
|
|||||||
}
|
}
|
||||||
return u.HomeDir
|
return u.HomeDir
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// getGoroot returns an appropriate GOROOT from various sources. If it can't be
|
||||||
|
// found, it returns an empty string.
|
||||||
|
func getGoroot() string {
|
||||||
|
goroot := os.Getenv("GOROOT")
|
||||||
|
if goroot != "" {
|
||||||
|
// An explicitly set GOROOT always has preference.
|
||||||
|
return goroot
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for the location of the 'go' binary and base GOROOT on that.
|
||||||
|
binpath, err := exec.LookPath("go")
|
||||||
|
if err == nil {
|
||||||
|
binpath, err = filepath.EvalSymlinks(binpath)
|
||||||
|
if err == nil {
|
||||||
|
goroot := filepath.Dir(filepath.Dir(binpath))
|
||||||
|
if isGoroot(goroot) {
|
||||||
|
return goroot
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check what GOROOT was at compile time.
|
||||||
|
if isGoroot(runtime.GOROOT()) {
|
||||||
|
return runtime.GOROOT()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for some standard locations, as a last resort.
|
||||||
|
var candidates []string
|
||||||
|
switch runtime.GOOS {
|
||||||
|
case "linux":
|
||||||
|
candidates = []string{
|
||||||
|
"/usr/local/go", // manually installed
|
||||||
|
"/usr/lib/go", // from the distribution
|
||||||
|
}
|
||||||
|
case "darwin":
|
||||||
|
candidates = []string{
|
||||||
|
"/usr/local/go", // manually installed
|
||||||
|
"/usr/local/opt/go/libexec", // from Homebrew
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, candidate := range candidates {
|
||||||
|
if isGoroot(candidate) {
|
||||||
|
return candidate
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Can't find GOROOT...
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// isGoroot checks whether the given path looks like a GOROOT.
|
||||||
|
func isGoroot(goroot string) bool {
|
||||||
|
_, err := os.Stat(filepath.Join(goroot, "src", "runtime", "internal", "sys", "zversion.go"))
|
||||||
|
return err == nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,14 +2,17 @@
|
|||||||
"build-tags": ["cortexm", "linux", "arm"],
|
"build-tags": ["cortexm", "linux", "arm"],
|
||||||
"goos": "linux",
|
"goos": "linux",
|
||||||
"goarch": "arm",
|
"goarch": "arm",
|
||||||
"compiler": "clang-8",
|
"compiler": "clang",
|
||||||
"gc": "marksweep",
|
"gc": "marksweep",
|
||||||
"linker": "ld.lld",
|
"linker": "ld.lld",
|
||||||
"rtlib": "compiler-rt",
|
"rtlib": "compiler-rt",
|
||||||
"cflags": [
|
"cflags": [
|
||||||
"-Oz",
|
"-Oz",
|
||||||
"-mthumb",
|
"-mthumb",
|
||||||
|
"-Werror",
|
||||||
"-fshort-enums",
|
"-fshort-enums",
|
||||||
|
"-nostdlibinc",
|
||||||
|
"-Wno-macro-redefined",
|
||||||
"-fno-exceptions", "-fno-unwind-tables",
|
"-fno-exceptions", "-fno-unwind-tables",
|
||||||
"-ffunction-sections", "-fdata-sections"
|
"-ffunction-sections", "-fdata-sections"
|
||||||
],
|
],
|
||||||
|
|||||||
+1
-1
@@ -6,7 +6,7 @@
|
|||||||
"--target=armv6m-none-eabi",
|
"--target=armv6m-none-eabi",
|
||||||
"-Qunused-arguments",
|
"-Qunused-arguments",
|
||||||
"-DNRF51",
|
"-DNRF51",
|
||||||
"-Ilib/CMSIS/CMSIS/Include"
|
"-I{root}/lib/CMSIS/CMSIS/Include"
|
||||||
],
|
],
|
||||||
"ldflags": [
|
"ldflags": [
|
||||||
"-T", "targets/nrf51.ld"
|
"-T", "targets/nrf51.ld"
|
||||||
|
|||||||
+1
-1
@@ -7,7 +7,7 @@
|
|||||||
"-mfloat-abi=soft",
|
"-mfloat-abi=soft",
|
||||||
"-Qunused-arguments",
|
"-Qunused-arguments",
|
||||||
"-DNRF52832_XXAA",
|
"-DNRF52832_XXAA",
|
||||||
"-Ilib/CMSIS/CMSIS/Include"
|
"-I{root}/lib/CMSIS/CMSIS/Include"
|
||||||
],
|
],
|
||||||
"ldflags": [
|
"ldflags": [
|
||||||
"-T", "targets/nrf52.ld"
|
"-T", "targets/nrf52.ld"
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
"-mfloat-abi=soft",
|
"-mfloat-abi=soft",
|
||||||
"-Qunused-arguments",
|
"-Qunused-arguments",
|
||||||
"-DNRF52840_XXAA",
|
"-DNRF52840_XXAA",
|
||||||
"-Ilib/CMSIS/CMSIS/Include"
|
"-I{root}/lib/CMSIS/CMSIS/Include"
|
||||||
],
|
],
|
||||||
"ldflags": [
|
"ldflags": [
|
||||||
"-T", "targets/nrf52840.ld"
|
"-T", "targets/nrf52840.ld"
|
||||||
|
|||||||
+3
-1
@@ -3,10 +3,12 @@
|
|||||||
"build-tags": ["js", "wasm"],
|
"build-tags": ["js", "wasm"],
|
||||||
"goos": "js",
|
"goos": "js",
|
||||||
"goarch": "wasm",
|
"goarch": "wasm",
|
||||||
"compiler": "clang-8",
|
"compiler": "clang",
|
||||||
"linker": "wasm-ld",
|
"linker": "wasm-ld",
|
||||||
"cflags": [
|
"cflags": [
|
||||||
"--target=wasm32",
|
"--target=wasm32",
|
||||||
|
"-nostdlibinc",
|
||||||
|
"-Wno-macro-redefined",
|
||||||
"-Oz"
|
"-Oz"
|
||||||
],
|
],
|
||||||
"ldflags": [
|
"ldflags": [
|
||||||
|
|||||||
Vendored
+6
-2
@@ -1,13 +1,17 @@
|
|||||||
#include "main.h"
|
#include "main.h"
|
||||||
|
|
||||||
int global = 3;
|
int global = 3;
|
||||||
_Bool globalBool = 1;
|
bool globalBool = 1;
|
||||||
_Bool globalBool2 = 10; // test narrowing
|
bool globalBool2 = 10; // test narrowing
|
||||||
float globalFloat = 3.1;
|
float globalFloat = 3.1;
|
||||||
double globalDouble = 3.2;
|
double globalDouble = 3.2;
|
||||||
_Complex float globalComplexFloat = 4.1+3.3i;
|
_Complex float globalComplexFloat = 4.1+3.3i;
|
||||||
_Complex double globalComplexDouble = 4.2+3.4i;
|
_Complex double globalComplexDouble = 4.2+3.4i;
|
||||||
_Complex double globalComplexLongDouble = 4.3+3.5i;
|
_Complex double globalComplexLongDouble = 4.3+3.5i;
|
||||||
|
char globalChar = 100;
|
||||||
|
void *globalVoidPtrSet = &global;
|
||||||
|
void *globalVoidPtrNull;
|
||||||
|
int64_t globalInt64 = -(2LL << 40);
|
||||||
collection_t globalStruct = {256, -123456, 3.14, 88};
|
collection_t globalStruct = {256, -123456, 3.14, 88};
|
||||||
int globalStructSize = sizeof(globalStruct);
|
int globalStructSize = sizeof(globalStruct);
|
||||||
short globalArray[3] = {5, 6, 7};
|
short globalArray[3] = {5, 6, 7};
|
||||||
|
|||||||
Vendored
+15
-7
@@ -9,10 +9,6 @@ import "C"
|
|||||||
|
|
||||||
import "unsafe"
|
import "unsafe"
|
||||||
|
|
||||||
func (s C.myint) Int() int {
|
|
||||||
return int(s)
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
println("fortytwo:", C.fortytwo())
|
println("fortytwo:", C.fortytwo())
|
||||||
println("add:", C.add(C.int(3), 5))
|
println("add:", C.add(C.int(3), 5))
|
||||||
@@ -33,6 +29,10 @@ func main() {
|
|||||||
cb = C.binop_t(C.mul)
|
cb = C.binop_t(C.mul)
|
||||||
println("callback 2:", C.doCallback(20, 30, cb))
|
println("callback 2:", C.doCallback(20, 30, cb))
|
||||||
|
|
||||||
|
// 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)
|
||||||
@@ -40,6 +40,10 @@ func main() {
|
|||||||
println("complex float:", C.globalComplexFloat)
|
println("complex float:", C.globalComplexFloat)
|
||||||
println("complex double:", C.globalComplexDouble)
|
println("complex double:", C.globalComplexDouble)
|
||||||
println("complex long double:", C.globalComplexLongDouble)
|
println("complex long double:", C.globalComplexLongDouble)
|
||||||
|
println("char match:", C.globalChar == 100)
|
||||||
|
var voidPtr unsafe.Pointer = C.globalVoidPtrNull
|
||||||
|
println("void* match:", voidPtr == nil, C.globalVoidPtrNull == nil, (*C.int)(C.globalVoidPtrSet) == &C.global)
|
||||||
|
println("int64_t match:", C.globalInt64 == C.int64_t(-(2<<40)))
|
||||||
|
|
||||||
// complex types
|
// complex types
|
||||||
println("struct:", C.int(unsafe.Sizeof(C.globalStruct)) == C.globalStructSize, C.globalStruct.s, C.globalStruct.l, C.globalStruct.f)
|
println("struct:", C.int(unsafe.Sizeof(C.globalStruct)) == C.globalStructSize, C.globalStruct.s, C.globalStruct.l, C.globalStruct.f)
|
||||||
@@ -53,10 +57,14 @@ func main() {
|
|||||||
C.unionSetData(5, 8, 1)
|
C.unionSetData(5, 8, 1)
|
||||||
println("union global data:", C.globalUnion.data[0], C.globalUnion.data[1], C.globalUnion.data[2])
|
println("union global data:", C.globalUnion.data[0], C.globalUnion.data[1], C.globalUnion.data[2])
|
||||||
println("union field:", printUnion(C.globalUnion).f)
|
println("union field:", printUnion(C.globalUnion).f)
|
||||||
|
var _ C.union_joined = C.globalUnion
|
||||||
|
|
||||||
|
// elaborated type
|
||||||
|
p := C.struct_point{x: 3, y: 5}
|
||||||
|
println("struct:", p.x, p.y)
|
||||||
|
|
||||||
// recursive types, test using a linked list
|
// recursive types, test using a linked list
|
||||||
lastElement := &C.list_t{n: 7, next: nil}
|
list := &C.list_t{n: 3, next: &C.struct_list_t{n: 6, next: &C.list_t{n: 7, next: nil}}}
|
||||||
list := &C.list_t{n: 3, next: &C.struct_list_t{n: 6, next: (*C.struct_list_t)(lastElement)}}
|
|
||||||
for list != nil {
|
for list != nil {
|
||||||
println("n in chain:", list.n)
|
println("n in chain:", list.n)
|
||||||
list = (*C.list_t)(list.next)
|
list = (*C.list_t)(list.next)
|
||||||
@@ -66,7 +74,7 @@ func main() {
|
|||||||
func printUnion(union C.joined_t) C.joined_t {
|
func printUnion(union C.joined_t) C.joined_t {
|
||||||
println("union local data: ", union.data[0], union.data[1], union.data[2])
|
println("union local data: ", union.data[0], union.data[1], union.data[2])
|
||||||
union.s = -33
|
union.s = -33
|
||||||
println("union s method:", union.s.Int(), union.data[0] == 5)
|
println("union s:", union.data[0] == -33)
|
||||||
union.f = 6.28
|
union.f = 6.28
|
||||||
println("union f:", union.f)
|
println("union f:", union.f)
|
||||||
return union
|
return union
|
||||||
|
|||||||
Vendored
+21
-3
@@ -1,10 +1,18 @@
|
|||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
typedef short myint;
|
typedef short myint;
|
||||||
|
typedef short unusedTypedef;
|
||||||
int add(int a, int b);
|
int add(int a, int b);
|
||||||
|
int unusedFunction(void);
|
||||||
typedef int (*binop_t) (int, int);
|
typedef int (*binop_t) (int, int);
|
||||||
int doCallback(int a, int b, binop_t cb);
|
int doCallback(int a, int b, binop_t cb);
|
||||||
typedef int * intPointer;
|
typedef int * intPointer;
|
||||||
void store(int value, int *ptr);
|
void store(int value, int *ptr);
|
||||||
|
|
||||||
|
// this signature should not be included by CGo
|
||||||
|
void unusedFunction2(int x, __builtin_va_list args);
|
||||||
|
|
||||||
typedef struct collection {
|
typedef struct collection {
|
||||||
short s;
|
short s;
|
||||||
long l;
|
long l;
|
||||||
@@ -12,6 +20,11 @@ typedef struct collection {
|
|||||||
unsigned char c;
|
unsigned char c;
|
||||||
} collection_t;
|
} collection_t;
|
||||||
|
|
||||||
|
struct point {
|
||||||
|
int x;
|
||||||
|
int y;
|
||||||
|
};
|
||||||
|
|
||||||
// linked list
|
// linked list
|
||||||
typedef struct list_t {
|
typedef struct list_t {
|
||||||
int n;
|
int n;
|
||||||
@@ -27,15 +40,20 @@ void unionSetShort(short s);
|
|||||||
void unionSetFloat(float f);
|
void unionSetFloat(float f);
|
||||||
void unionSetData(short f0, short f1, short f2);
|
void unionSetData(short f0, short f1, short f2);
|
||||||
|
|
||||||
// test globals
|
// test globals and datatypes
|
||||||
extern int global;
|
extern int global;
|
||||||
extern _Bool globalBool;
|
extern int unusedGlobal;
|
||||||
extern _Bool globalBool2;
|
extern bool globalBool;
|
||||||
|
extern bool globalBool2;
|
||||||
extern float globalFloat;
|
extern float globalFloat;
|
||||||
extern double globalDouble;
|
extern double globalDouble;
|
||||||
extern _Complex float globalComplexFloat;
|
extern _Complex float globalComplexFloat;
|
||||||
extern _Complex double globalComplexDouble;
|
extern _Complex double globalComplexDouble;
|
||||||
extern _Complex double globalComplexLongDouble;
|
extern _Complex double globalComplexLongDouble;
|
||||||
|
extern char globalChar;
|
||||||
|
extern void *globalVoidPtrSet;
|
||||||
|
extern void *globalVoidPtrNull;
|
||||||
|
extern int64_t globalInt64;
|
||||||
extern collection_t globalStruct;
|
extern collection_t globalStruct;
|
||||||
extern int globalStructSize;
|
extern int globalStructSize;
|
||||||
extern short globalArray[3];
|
extern short globalArray[3];
|
||||||
|
|||||||
Vendored
+5
-1
@@ -14,6 +14,9 @@ double: +3.200000e+000
|
|||||||
complex float: (+4.100000e+000+3.300000e+000i)
|
complex float: (+4.100000e+000+3.300000e+000i)
|
||||||
complex double: (+4.200000e+000+3.400000e+000i)
|
complex double: (+4.200000e+000+3.400000e+000i)
|
||||||
complex long double: (+4.300000e+000+3.500000e+000i)
|
complex long double: (+4.300000e+000+3.500000e+000i)
|
||||||
|
char match: true
|
||||||
|
void* match: true true true
|
||||||
|
int64_t match: true
|
||||||
struct: true 256 -123456 +3.140000e+000
|
struct: true 256 -123456 +3.140000e+000
|
||||||
array: 5 6 7
|
array: 5 6 7
|
||||||
union: true
|
union: true
|
||||||
@@ -21,9 +24,10 @@ union s: 22
|
|||||||
union f: +3.140000e+000
|
union f: +3.140000e+000
|
||||||
union global data: 5 8 1
|
union global data: 5 8 1
|
||||||
union local data: 5 8 1
|
union local data: 5 8 1
|
||||||
union s method: -33 false
|
union s: true
|
||||||
union f: +6.280000e+000
|
union f: +6.280000e+000
|
||||||
union field: +6.280000e+000
|
union field: +6.280000e+000
|
||||||
|
struct: 3 5
|
||||||
n in chain: 3
|
n in chain: 3
|
||||||
n in chain: 6
|
n in chain: 6
|
||||||
n in chain: 7
|
n in chain: 7
|
||||||
|
|||||||
Vendored
+9
@@ -20,6 +20,11 @@ func main() {
|
|||||||
n, ok = <-ch
|
n, ok = <-ch
|
||||||
println("recv from closed channel:", n, ok)
|
println("recv from closed channel:", n, ok)
|
||||||
|
|
||||||
|
// Test bigger values
|
||||||
|
ch2 := make(chan complex128)
|
||||||
|
go sendComplex(ch2)
|
||||||
|
println("complex128:", <-ch2)
|
||||||
|
|
||||||
// Test multi-sender.
|
// Test multi-sender.
|
||||||
ch = make(chan int)
|
ch = make(chan int)
|
||||||
go fastsender(ch)
|
go fastsender(ch)
|
||||||
@@ -62,6 +67,10 @@ func sender(ch chan int) {
|
|||||||
close(ch)
|
close(ch)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func sendComplex(ch chan complex128) {
|
||||||
|
ch <- 7+10.5i
|
||||||
|
}
|
||||||
|
|
||||||
func fastsender(ch chan int) {
|
func fastsender(ch chan int) {
|
||||||
ch <- 10
|
ch <- 10
|
||||||
ch <- 11
|
ch <- 11
|
||||||
|
|||||||
Vendored
+1
@@ -9,6 +9,7 @@ received num: 6
|
|||||||
received num: 7
|
received num: 7
|
||||||
received num: 8
|
received num: 8
|
||||||
recv from closed channel: 0 false
|
recv from closed channel: 0 false
|
||||||
|
complex128: (+7.000000e+000+1.050000e+001i)
|
||||||
got n: 10
|
got n: 10
|
||||||
got n: 11
|
got n: 11
|
||||||
got n: 10
|
got n: 10
|
||||||
|
|||||||
Vendored
+24
@@ -15,12 +15,19 @@ func main() {
|
|||||||
wait()
|
wait()
|
||||||
println("end waiting")
|
println("end waiting")
|
||||||
|
|
||||||
|
value := delayedValue()
|
||||||
|
println("value produced after some time:", value)
|
||||||
|
|
||||||
// Run a non-blocking call in a goroutine. This should be turned into a
|
// Run a non-blocking call in a goroutine. This should be turned into a
|
||||||
// regular call, so should be equivalent to calling nowait() without 'go'
|
// regular call, so should be equivalent to calling nowait() without 'go'
|
||||||
// prefix.
|
// prefix.
|
||||||
go nowait()
|
go nowait()
|
||||||
time.Sleep(time.Millisecond)
|
time.Sleep(time.Millisecond)
|
||||||
println("done with non-blocking goroutine")
|
println("done with non-blocking goroutine")
|
||||||
|
|
||||||
|
var printer Printer
|
||||||
|
printer = &myPrinter{}
|
||||||
|
printer.Print()
|
||||||
}
|
}
|
||||||
|
|
||||||
func sub() {
|
func sub() {
|
||||||
@@ -35,6 +42,23 @@ func wait() {
|
|||||||
println(" wait end")
|
println(" wait end")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func delayedValue() int {
|
||||||
|
time.Sleep(time.Millisecond)
|
||||||
|
return 42
|
||||||
|
}
|
||||||
|
|
||||||
func nowait() {
|
func nowait() {
|
||||||
println("non-blocking goroutine")
|
println("non-blocking goroutine")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Printer interface {
|
||||||
|
Print()
|
||||||
|
}
|
||||||
|
|
||||||
|
type myPrinter struct{
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *myPrinter) Print() {
|
||||||
|
time.Sleep(time.Millisecond)
|
||||||
|
println("async interface method call")
|
||||||
|
}
|
||||||
|
|||||||
Vendored
+2
@@ -7,5 +7,7 @@ wait:
|
|||||||
wait start
|
wait start
|
||||||
wait end
|
wait end
|
||||||
end waiting
|
end waiting
|
||||||
|
value produced after some time: 42
|
||||||
non-blocking goroutine
|
non-blocking goroutine
|
||||||
done with non-blocking goroutine
|
done with non-blocking goroutine
|
||||||
|
async interface method call
|
||||||
|
|||||||
Vendored
+12
@@ -56,4 +56,16 @@ func main() {
|
|||||||
// cast complex
|
// cast complex
|
||||||
println(complex64(c128))
|
println(complex64(c128))
|
||||||
println(complex128(c64))
|
println(complex128(c64))
|
||||||
|
|
||||||
|
// binops on complex numbers
|
||||||
|
c64 = 5+2i
|
||||||
|
println("complex64 add: ", c64 + -3+8i)
|
||||||
|
println("complex64 sub: ", c64 - -3+8i)
|
||||||
|
println("complex64 mul: ", c64 * -3+8i)
|
||||||
|
println("complex64 div: ", c64 / -3+8i)
|
||||||
|
c128 = -5+2i
|
||||||
|
println("complex128 add:", c128 + 2+6i)
|
||||||
|
println("complex128 sub:", c128 - 2+6i)
|
||||||
|
println("complex128 mul:", c128 * 2+6i)
|
||||||
|
println("complex128 div:", c128 / 2+6i)
|
||||||
}
|
}
|
||||||
|
|||||||
Vendored
+8
@@ -23,3 +23,11 @@
|
|||||||
(+2.000000e+000-2.000000e+000i)
|
(+2.000000e+000-2.000000e+000i)
|
||||||
(+6.666667e-001-2.000000e+000i)
|
(+6.666667e-001-2.000000e+000i)
|
||||||
(+6.666667e-001+1.200000e+000i)
|
(+6.666667e-001+1.200000e+000i)
|
||||||
|
complex64 add: (+2.000000e+000+1.000000e+001i)
|
||||||
|
complex64 sub: (+8.000000e+000+1.000000e+001i)
|
||||||
|
complex64 mul: (-1.500000e+001+2.000000e+000i)
|
||||||
|
complex64 div: (-1.666667e+000+7.333333e+000i)
|
||||||
|
complex128 add: (-3.000000e+000+8.000000e+000i)
|
||||||
|
complex128 sub: (-7.000000e+000+8.000000e+000i)
|
||||||
|
complex128 mul: (-1.000000e+001+1.000000e+001i)
|
||||||
|
complex128 div: (-2.500000e+000+7.000000e+000i)
|
||||||
|
|||||||
Reference in New Issue
Block a user