all: add WebAssembly backend
Этот коммит содержится в:
родитель
a51e04c550
коммит
e5e09747f0
15 изменённых файлов: 199 добавлений и 17 удалений
|
@ -8,7 +8,7 @@ before_install:
|
|||
- echo "deb http://apt.llvm.org/trusty/ llvm-toolchain-trusty-7 main" | sudo tee -a /etc/apt/sources.list
|
||||
- echo "deb http://ppa.launchpad.net/ubuntu-toolchain-r/test/ubuntu trusty main" | sudo tee -a /etc/apt/sources.list
|
||||
- sudo apt-get update -qq
|
||||
- sudo apt-get install llvm-7-dev clang-7 gcc-arm-none-eabi qemu-system-arm --allow-unauthenticated -y
|
||||
- sudo apt-get install llvm-7-dev clang-7 lld-7 gcc-arm-none-eabi qemu-system-arm --allow-unauthenticated -y
|
||||
- sudo ln -s /usr/bin/clang-7 /usr/local/bin/cc # work around missing -no-pie in old GCC version
|
||||
|
||||
install:
|
||||
|
@ -27,3 +27,4 @@ script:
|
|||
- tinygo build -o test.nrf.elf -target=nrf52840-mdk examples/blinky1
|
||||
- tinygo build -o blinky1.stm32.elf -target=bluepill examples/blinky1
|
||||
- tinygo build -o blinky1.avr.o -target=arduino examples/blinky1 # TODO: avr-as/avr-gcc doesn't work
|
||||
- tinygo build -o test.wasm.wasm -target=wasm examples/test
|
||||
|
|
|
@ -22,6 +22,6 @@ COPY --from=0 /go/src/github.com/aykevl/tinygo/targets /go/src/github.com/aykevl
|
|||
RUN wget -O- https://apt.llvm.org/llvm-snapshot.gpg.key| apt-key add - && \
|
||||
echo "deb http://apt.llvm.org/stretch/ llvm-toolchain-stretch-7 main" >> /etc/apt/sources.list && \
|
||||
apt-get update && \
|
||||
apt-get install -y libllvm7
|
||||
apt-get install -y libllvm7 lld-7
|
||||
|
||||
ENTRYPOINT ["/go/bin/tinygo"]
|
||||
|
|
|
@ -44,6 +44,13 @@ needs the following tools:
|
|||
``avr-gcc``.
|
||||
* ``avrdude`` for flashing to an Arduino.
|
||||
|
||||
WebAssembly
|
||||
~~~~~~~~~~~
|
||||
|
||||
The WebAssembly backend only needs a special linker from the LLVM project:
|
||||
|
||||
* LLVM linker (``ld.lld-7``) for linking WebAssembly files together.
|
||||
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
|
27
main.go
27
main.go
|
@ -91,13 +91,15 @@ func Compile(pkgName, outpath, opt string, spec *TargetSpec, printIR, dumpSSA, d
|
|||
}
|
||||
|
||||
// Generate output.
|
||||
if strings.HasSuffix(outpath, ".o") {
|
||||
outext := filepath.Ext(outpath)
|
||||
switch outext {
|
||||
case ".o":
|
||||
return c.EmitObject(outpath)
|
||||
} else if strings.HasSuffix(outpath, ".bc") {
|
||||
case ".bc":
|
||||
return c.EmitBitcode(outpath)
|
||||
} else if strings.HasSuffix(outpath, ".ll") {
|
||||
case ".ll":
|
||||
return c.EmitText(outpath)
|
||||
} else {
|
||||
default:
|
||||
// Act as a compiler driver.
|
||||
|
||||
// Create a temporary directory for intermediary files.
|
||||
|
@ -160,14 +162,13 @@ func Compile(pkgName, outpath, opt string, spec *TargetSpec, printIR, dumpSSA, d
|
|||
}
|
||||
}
|
||||
|
||||
ext := filepath.Ext(outpath)
|
||||
if ext == ".hex" || ext == ".bin" {
|
||||
if outext == ".hex" || outext == ".bin" {
|
||||
// Get an Intel .hex file or .bin file from the .elf file.
|
||||
tmppath = filepath.Join(dir, "main"+ext)
|
||||
tmppath = filepath.Join(dir, "main"+outext)
|
||||
format := map[string]string{
|
||||
".hex": "ihex",
|
||||
".bin": "binary",
|
||||
}[ext]
|
||||
}[outext]
|
||||
cmd := exec.Command(spec.Objcopy, "-O", format, executable, tmppath)
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
|
@ -386,7 +387,7 @@ func main() {
|
|||
opt := flag.String("opt", "z", "optimization level: 0, 1, 2, s, z")
|
||||
printIR := flag.Bool("printir", false, "print LLVM IR")
|
||||
dumpSSA := flag.Bool("dumpssa", false, "dump internal Go SSA")
|
||||
target := flag.String("target", llvm.DefaultTargetTriple(), "LLVM target")
|
||||
target := flag.String("target", "", "LLVM target")
|
||||
printSize := flag.String("size", "", "print sizes (none, short, full)")
|
||||
nodebug := flag.Bool("no-debug", false, "disable DWARF debug symbol generation")
|
||||
ocdOutput := flag.Bool("ocd-output", false, "print OCD daemon output during debug")
|
||||
|
@ -415,7 +416,11 @@ func main() {
|
|||
usage()
|
||||
os.Exit(1)
|
||||
}
|
||||
err := Build(flag.Arg(0), *outpath, *target, *opt, *printIR, *dumpSSA, !*nodebug, *printSize)
|
||||
target := *target
|
||||
if target == "" && filepath.Ext(*outpath) == ".wasm" {
|
||||
target = "wasm"
|
||||
}
|
||||
err := Build(flag.Arg(0), *outpath, target, *opt, *printIR, *dumpSSA, !*nodebug, *printSize)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, "error:", err)
|
||||
os.Exit(1)
|
||||
|
@ -443,7 +448,7 @@ func main() {
|
|||
os.Exit(1)
|
||||
}
|
||||
var err error
|
||||
if *target == llvm.DefaultTargetTriple() {
|
||||
if *target == "" {
|
||||
err = Run(flag.Arg(0))
|
||||
} else {
|
||||
err = Emulate(flag.Arg(0), *target, *opt)
|
||||
|
|
9
src/examples/wasm/wasm.go
Обычный файл
9
src/examples/wasm/wasm.go
Обычный файл
|
@ -0,0 +1,9 @@
|
|||
package main
|
||||
|
||||
func main() {
|
||||
}
|
||||
|
||||
//go:export add
|
||||
func add(a, b int) int {
|
||||
return a + b
|
||||
}
|
15
src/examples/wasm/wasm.html
Обычный файл
15
src/examples/wasm/wasm.html
Обычный файл
|
@ -0,0 +1,15 @@
|
|||
<!DOCTYPE html>
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>Go WebAssembly</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1"/>
|
||||
<script src="wasm.js" async></script>
|
||||
</head>
|
||||
<body>
|
||||
<h1>WebAssembly</h1>
|
||||
<p>Add two numbers, using WebAssembly:</p>
|
||||
<input type="number" id="a" value="2"/> + <input type="number" id="b" value="2"/> = <input type="number" id="result" readonly/>
|
||||
</body>
|
||||
</html>
|
55
src/examples/wasm/wasm.js
Обычный файл
55
src/examples/wasm/wasm.js
Обычный файл
|
@ -0,0 +1,55 @@
|
|||
'use strict';
|
||||
|
||||
const WASM_URL = '../../../wasm.wasm';
|
||||
|
||||
var wasm;
|
||||
var logLine = [];
|
||||
var memory8;
|
||||
|
||||
var importObject = {
|
||||
env: {
|
||||
io_get_stdout: function() {
|
||||
return 1;
|
||||
},
|
||||
resource_write: function(fd, ptr, len) {
|
||||
if (fd == 1) {
|
||||
for (let i=0; i<len; i++) {
|
||||
let c = memory8[ptr+i];
|
||||
if (c == 13) { // CR
|
||||
// ignore
|
||||
} else if (c == 10) { // LF
|
||||
// write line
|
||||
let line = new TextDecoder("utf-8").decode(new Uint8Array(logLine));
|
||||
logLine = [];
|
||||
console.log(line);
|
||||
} else {
|
||||
logLine.push(c);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
console.error('invalid file descriptor:', fd);
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
function updateResult() {
|
||||
let a = parseInt(document.querySelector('#a').value);
|
||||
let b = parseInt(document.querySelector('#b').value);
|
||||
let result = wasm.exports.add(a, b);
|
||||
document.querySelector('#result').value = result;
|
||||
}
|
||||
|
||||
function init() {
|
||||
document.querySelector('#a').oninput = updateResult;
|
||||
document.querySelector('#b').oninput = updateResult;
|
||||
|
||||
WebAssembly.instantiateStreaming(fetch(WASM_URL), importObject).then(function(obj) {
|
||||
wasm = obj.instance;
|
||||
memory8 = new Uint8Array(wasm.exports.memory.buffer);
|
||||
wasm.exports.cwa_main();
|
||||
updateResult();
|
||||
})
|
||||
}
|
||||
|
||||
init();
|
|
@ -2,6 +2,10 @@
|
|||
|
||||
package runtime
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const GOARCH = "arm"
|
||||
|
||||
// The length type used inside strings and slices.
|
||||
|
@ -9,3 +13,6 @@ type lenType uint32
|
|||
|
||||
// The bitness of the CPU (e.g. 8, 32, 64).
|
||||
const TargetBits = 32
|
||||
|
||||
//go:extern _heap_start
|
||||
var heapStart unsafe.Pointer
|
||||
|
|
|
@ -2,6 +2,10 @@
|
|||
|
||||
package runtime
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const GOARCH = "avr"
|
||||
|
||||
// The length type used inside strings and slices.
|
||||
|
@ -9,3 +13,6 @@ type lenType uint16
|
|||
|
||||
// The bitness of the CPU (e.g. 8, 32, 64).
|
||||
const TargetBits = 8
|
||||
|
||||
//go:extern _heap_start
|
||||
var heapStart unsafe.Pointer
|
||||
|
|
|
@ -2,6 +2,10 @@
|
|||
|
||||
package runtime
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const GOARCH = "wasm"
|
||||
|
||||
// The length type used inside strings and slices.
|
||||
|
@ -9,3 +13,6 @@ type lenType uint32
|
|||
|
||||
// The bitness of the CPU (e.g. 8, 32, 64).
|
||||
const TargetBits = 32
|
||||
|
||||
//go:extern __heap_base
|
||||
var heapStart unsafe.Pointer
|
||||
|
|
|
@ -6,9 +6,6 @@ import (
|
|||
"unsafe"
|
||||
)
|
||||
|
||||
//go:extern _heap_start
|
||||
var heapStart unsafe.Pointer
|
||||
|
||||
var heapptr = uintptr(unsafe.Pointer(&heapStart))
|
||||
|
||||
func alloc(size uintptr) unsafe.Pointer {
|
||||
|
|
54
src/runtime/runtime_wasm.go
Обычный файл
54
src/runtime/runtime_wasm.go
Обычный файл
|
@ -0,0 +1,54 @@
|
|||
// +build wasm,!arm,!avr
|
||||
|
||||
package runtime
|
||||
|
||||
type timeUnit int64
|
||||
|
||||
const tickMicros = 1
|
||||
|
||||
var timestamp timeUnit
|
||||
|
||||
// CommonWA: io_get_stdout
|
||||
func _Cfunc_io_get_stdout() int32
|
||||
|
||||
// CommonWA: resource_write
|
||||
func _Cfunc_resource_write(id int32, ptr *uint8, len int32) int32
|
||||
|
||||
var stdout int32
|
||||
|
||||
func init() {
|
||||
stdout = _Cfunc_io_get_stdout()
|
||||
}
|
||||
|
||||
//go:export _start
|
||||
func _start() {
|
||||
initAll()
|
||||
}
|
||||
|
||||
//go:export cwa_main
|
||||
func cwa_main() {
|
||||
initAll() // _start is not called by olin/cwa so has to be called here
|
||||
mainWrapper()
|
||||
}
|
||||
|
||||
func putchar(c byte) {
|
||||
_Cfunc_resource_write(stdout, &c, 1)
|
||||
}
|
||||
|
||||
func sleepTicks(d timeUnit) {
|
||||
// TODO: actually sleep here for the given time.
|
||||
timestamp += d
|
||||
}
|
||||
|
||||
func ticks() timeUnit {
|
||||
return timestamp
|
||||
}
|
||||
|
||||
// Align on word boundary.
|
||||
func align(ptr uintptr) uintptr {
|
||||
return (ptr + 3) &^ 3
|
||||
}
|
||||
|
||||
func abort() {
|
||||
// TODO
|
||||
}
|
|
@ -7,6 +7,8 @@ import (
|
|||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/aykevl/go-llvm"
|
||||
)
|
||||
|
||||
// Target specification for a given target. Used for bare metal targets.
|
||||
|
@ -30,6 +32,10 @@ type TargetSpec struct {
|
|||
|
||||
// Load a target specification
|
||||
func LoadTarget(target string) (*TargetSpec, error) {
|
||||
if target == "" {
|
||||
target = llvm.DefaultTargetTriple()
|
||||
}
|
||||
|
||||
spec := &TargetSpec{
|
||||
Triple: target,
|
||||
BuildTags: []string{runtime.GOOS, runtime.GOARCH},
|
||||
|
@ -54,7 +60,7 @@ func LoadTarget(target string) (*TargetSpec, error) {
|
|||
// Expected a 'file not found' error, got something else.
|
||||
return nil, err
|
||||
} else {
|
||||
// No target spec available. This is fine.
|
||||
// No target spec available. Use the default one.
|
||||
}
|
||||
|
||||
return spec, nil
|
||||
|
|
10
targets/wasm.json
Обычный файл
10
targets/wasm.json
Обычный файл
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"llvm-target": "wasm32-unknown-unknown-wasm",
|
||||
"build-tags": ["js", "wasm"],
|
||||
"linker": "ld.lld-7",
|
||||
"pre-link-args": [
|
||||
"-flavor", "wasm",
|
||||
"-allow-undefined-file", "targets/wasm.syms"
|
||||
],
|
||||
"emulator": ["cwa"]
|
||||
}
|
2
targets/wasm.syms
Обычный файл
2
targets/wasm.syms
Обычный файл
|
@ -0,0 +1,2 @@
|
|||
io_get_stdout
|
||||
resource_write
|
Загрузка…
Создание таблицы
Сослаться в новой задаче