1
0
mirror of https://github.com/golang/go synced 2024-10-02 04:28:33 -06:00

cmd/compile: add wasm architecture

This commit adds the wasm architecture to the compile command.
A later commit will contain the corresponding linker changes.

Design doc: https://docs.google.com/document/d/131vjr4DH6JFnb-blm_uRdaC0_Nv3OUwjEY5qVCxCup4

The following files are generated:
- src/cmd/compile/internal/ssa/opGen.go
- src/cmd/compile/internal/ssa/rewriteWasm.go
- src/cmd/internal/obj/wasm/anames.go

Updates #18892

Change-Id: Ifb4a96a3e427aac2362a1c97967d5667450fba3b
Reviewed-on: https://go-review.googlesource.com/103295
Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Cherry Zhang <cherryyz@google.com>
This commit is contained in:
Richard Musiol 2018-03-29 00:55:53 +02:00 committed by Brad Fitzpatrick
parent a9fc375258
commit 3b137dd2df
25 changed files with 9884 additions and 9 deletions

View File

@ -11,6 +11,7 @@ import (
"cmd/internal/obj/mips" "cmd/internal/obj/mips"
"cmd/internal/obj/ppc64" "cmd/internal/obj/ppc64"
"cmd/internal/obj/s390x" "cmd/internal/obj/s390x"
"cmd/internal/obj/wasm"
"cmd/internal/obj/x86" "cmd/internal/obj/x86"
"fmt" "fmt"
"strings" "strings"
@ -87,6 +88,8 @@ func Set(GOARCH string) *Arch {
a := archS390x() a := archS390x()
a.LinkArch = &s390x.Links390x a.LinkArch = &s390x.Links390x
return a return a
case "wasm":
return archWasm()
} }
return nil return nil
} }
@ -95,6 +98,10 @@ func jumpX86(word string) bool {
return word[0] == 'J' || word == "CALL" || strings.HasPrefix(word, "LOOP") || word == "XBEGIN" return word[0] == 'J' || word == "CALL" || strings.HasPrefix(word, "LOOP") || word == "XBEGIN"
} }
func jumpWasm(word string) bool {
return word == "JMP" || word == "CALL" || word == "Call" || word == "Br" || word == "BrIf"
}
func archX86(linkArch *obj.LinkArch) *Arch { func archX86(linkArch *obj.LinkArch) *Arch {
register := make(map[string]int16) register := make(map[string]int16)
// Create maps for easy lookup of instruction names etc. // Create maps for easy lookup of instruction names etc.
@ -577,3 +584,24 @@ func archS390x() *Arch {
IsJump: jumpS390x, IsJump: jumpS390x,
} }
} }
func archWasm() *Arch {
instructions := make(map[string]obj.As)
for i, s := range obj.Anames {
instructions[s] = obj.As(i)
}
for i, s := range wasm.Anames {
if obj.As(i) >= obj.A_ARCHSPECIFIC {
instructions[s] = obj.As(i) + obj.ABaseWasm
}
}
return &Arch{
LinkArch: &wasm.Linkwasm,
Instructions: instructions,
Register: wasm.Register,
RegisterPrefix: nil,
RegisterNumber: nilRegisterNumber,
IsJump: jumpWasm,
}
}

View File

@ -343,6 +343,13 @@ func (p *Parser) asmJump(op obj.As, cond string, a []obj.Addr) {
As: op, As: op,
} }
switch len(a) { switch len(a) {
case 0:
if p.arch.Family == sys.Wasm {
target = &obj.Addr{Type: obj.TYPE_NONE}
break
}
p.errorf("wrong number of arguments to %s instruction", op)
return
case 1: case 1:
target = &a[0] target = &a[0]
case 2: case 2:
@ -445,6 +452,8 @@ func (p *Parser) asmJump(op obj.As, cond string, a []obj.Addr) {
case target.Type == obj.TYPE_CONST: case target.Type == obj.TYPE_CONST:
// JMP $4 // JMP $4
prog.To = a[0] prog.To = a[0]
case target.Type == obj.TYPE_NONE:
// JMP
default: default:
p.errorf("cannot assemble jump %+v", target) p.errorf("cannot assemble jump %+v", target)
return return

View File

@ -311,4 +311,12 @@ var (
// GO386=387 // GO386=387
ControlWord64trunc, ControlWord64trunc,
ControlWord32 *obj.LSym ControlWord32 *obj.LSym
// Wasm
WasmMove,
WasmZero,
WasmDiv,
WasmTruncS,
WasmTruncU,
SigPanic *obj.LSym
) )

View File

@ -180,6 +180,7 @@ func Main(archInit func(*Arch)) {
gopkg = types.NewPkg("go", "") gopkg = types.NewPkg("go", "")
Nacl = objabi.GOOS == "nacl" Nacl = objabi.GOOS == "nacl"
Wasm := objabi.GOARCH == "wasm"
flag.BoolVar(&compiling_runtime, "+", false, "compiling runtime") flag.BoolVar(&compiling_runtime, "+", false, "compiling runtime")
flag.BoolVar(&compiling_std, "std", false, "compiling standard library") flag.BoolVar(&compiling_std, "std", false, "compiling standard library")
@ -200,7 +201,7 @@ func Main(archInit func(*Arch)) {
flag.IntVar(&nBackendWorkers, "c", 1, "concurrency during compilation, 1 means no concurrency") flag.IntVar(&nBackendWorkers, "c", 1, "concurrency during compilation, 1 means no concurrency")
flag.BoolVar(&pure_go, "complete", false, "compiling complete package (no C or assembly)") flag.BoolVar(&pure_go, "complete", false, "compiling complete package (no C or assembly)")
flag.StringVar(&debugstr, "d", "", "print debug information about items in `list`; try -d help") flag.StringVar(&debugstr, "d", "", "print debug information about items in `list`; try -d help")
flag.BoolVar(&flagDWARF, "dwarf", true, "generate DWARF symbols") flag.BoolVar(&flagDWARF, "dwarf", !Wasm, "generate DWARF symbols")
flag.BoolVar(&Ctxt.Flag_locationlists, "dwarflocationlists", true, "add location lists to DWARF in optimized mode") flag.BoolVar(&Ctxt.Flag_locationlists, "dwarflocationlists", true, "add location lists to DWARF in optimized mode")
flag.IntVar(&genDwarfInline, "gendwarfinl", 2, "generate DWARF inline info records") flag.IntVar(&genDwarfInline, "gendwarfinl", 2, "generate DWARF inline info records")
objabi.Flagcount("e", "no limit on number of errors reported", &Debug['e']) objabi.Flagcount("e", "no limit on number of errors reported", &Debug['e'])
@ -265,6 +266,7 @@ func Main(archInit func(*Arch)) {
} else { } else {
// turn off inline generation if no dwarf at all // turn off inline generation if no dwarf at all
genDwarfInline = 0 genDwarfInline = 0
Ctxt.Flag_locationlists = false
} }
if flag.NArg() < 1 && debugstr != "help" && debugstr != "ssa/help" { if flag.NArg() < 1 && debugstr != "help" && debugstr != "ssa/help" {

View File

@ -87,6 +87,14 @@ func initssaconfig() {
// GO386=387 runtime functions // GO386=387 runtime functions
ControlWord64trunc = sysfunc("controlWord64trunc") ControlWord64trunc = sysfunc("controlWord64trunc")
ControlWord32 = sysfunc("controlWord32") ControlWord32 = sysfunc("controlWord32")
// Wasm
WasmMove = sysfunc("wasmMove")
WasmZero = sysfunc("wasmZero")
WasmDiv = sysfunc("wasmDiv")
WasmTruncS = sysfunc("wasmTruncS")
WasmTruncU = sysfunc("wasmTruncU")
SigPanic = sysfunc("sigpanic")
} }
// buildssa builds an SSA function for fn. // buildssa builds an SSA function for fn.
@ -1794,7 +1802,7 @@ func (s *state) expr(n *Node) *ssa.Value {
conv = conv1 conv = conv1
} }
} }
if thearch.LinkArch.Family == sys.ARM64 || s.softFloat { if thearch.LinkArch.Family == sys.ARM64 || thearch.LinkArch.Family == sys.Wasm || s.softFloat {
if conv1, ok1 := uint64fpConvOpToSSA[twoTypes{s.concreteEtype(ft), s.concreteEtype(tt)}]; ok1 { if conv1, ok1 := uint64fpConvOpToSSA[twoTypes{s.concreteEtype(ft), s.concreteEtype(tt)}]; ok1 {
conv = conv1 conv = conv1
} }
@ -5222,7 +5230,7 @@ func (s *SSAGenState) Call(v *ssa.Value) *obj.Prog {
} else { } else {
// TODO(mdempsky): Can these differences be eliminated? // TODO(mdempsky): Can these differences be eliminated?
switch thearch.LinkArch.Family { switch thearch.LinkArch.Family {
case sys.AMD64, sys.I386, sys.PPC64, sys.S390X: case sys.AMD64, sys.I386, sys.PPC64, sys.S390X, sys.Wasm:
p.To.Type = obj.TYPE_REG p.To.Type = obj.TYPE_REG
case sys.ARM, sys.ARM64, sys.MIPS, sys.MIPS64: case sys.ARM, sys.ARM64, sys.MIPS, sys.MIPS64:
p.To.Type = obj.TYPE_MEM p.To.Type = obj.TYPE_MEM

View File

@ -311,6 +311,20 @@ func NewConfig(arch string, types Types, ctxt *obj.Link, optimize bool) *Config
c.LinkReg = linkRegMIPS c.LinkReg = linkRegMIPS
c.hasGReg = true c.hasGReg = true
c.noDuffDevice = true c.noDuffDevice = true
case "wasm":
c.PtrSize = 8
c.RegSize = 8
c.lowerBlock = rewriteBlockWasm
c.lowerValue = rewriteValueWasm
c.registers = registersWasm[:]
c.gpRegMask = gpRegMaskWasm
c.fpRegMask = fpRegMaskWasm
c.FPReg = framepointerRegWasm
c.LinkReg = linkRegWasm
c.hasGReg = true
c.noDuffDevice = true
c.useAvg = false
c.useHmul = false
default: default:
ctxt.Diag("arch %s not implemented", arch) ctxt.Diag("arch %s not implemented", arch)
} }

View File

@ -0,0 +1,391 @@
// Copyright 2018 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.
// Lowering arithmetic
(Add(64|32|16|8|Ptr) x y) -> (I64Add x y)
(Add(64|32)F x y) -> (F64Add x y)
(Sub(64|32|16|8|Ptr) x y) -> (I64Sub x y)
(Sub(64|32)F x y) -> (F64Sub x y)
(Mul(64|32|16|8) x y) -> (I64Mul x y)
(Mul(64|32)F x y) -> (F64Mul x y)
(Div64 x y) -> (I64DivS x y)
(Div64u x y) -> (I64DivU x y)
(Div32 x y) -> (I64DivS (SignExt32to64 x) (SignExt32to64 y))
(Div32u x y) -> (I64DivU (ZeroExt32to64 x) (ZeroExt32to64 y))
(Div16 x y) -> (I64DivS (SignExt16to64 x) (SignExt16to64 y))
(Div16u x y) -> (I64DivU (ZeroExt16to64 x) (ZeroExt16to64 y))
(Div8 x y) -> (I64DivS (SignExt8to64 x) (SignExt8to64 y))
(Div8u x y) -> (I64DivU (ZeroExt8to64 x) (ZeroExt8to64 y))
(Div(64|32)F x y) -> (F64Div x y)
(Mod64 x y) -> (I64RemS x y)
(Mod64u x y) -> (I64RemU x y)
(Mod32 x y) -> (I64RemS (SignExt32to64 x) (SignExt32to64 y))
(Mod32u x y) -> (I64RemU (ZeroExt32to64 x) (ZeroExt32to64 y))
(Mod16 x y) -> (I64RemS (SignExt16to64 x) (SignExt16to64 y))
(Mod16u x y) -> (I64RemU (ZeroExt16to64 x) (ZeroExt16to64 y))
(Mod8 x y) -> (I64RemS (SignExt8to64 x) (SignExt8to64 y))
(Mod8u x y) -> (I64RemU (ZeroExt8to64 x) (ZeroExt8to64 y))
(And(64|32|16|8|B) x y) -> (I64And x y)
(Or(64|32|16|8|B) x y) -> (I64Or x y)
(Xor(64|32|16|8) x y) -> (I64Xor x y)
(Neg(64|32|16|8) x) -> (I64Sub (I64Const [0]) x)
(Neg32F x) -> (F64Neg x)
(Neg64F x) -> (F64Neg x)
(Com(64|32|16|8) x) -> (I64Xor x (I64Const [-1]))
(Not x) -> (I64Eqz x)
// Lowering pointer arithmetic
(OffPtr [0] ptr) -> ptr
(OffPtr [off] ptr) && off > 0 -> (I64AddConst [off] ptr)
// Lowering extension
(SignExt32to64 x) -> (I64ShrS (I64Shl x (I64Const [32])) (I64Const [32]))
(SignExt16to(64|32) x) -> (I64ShrS (I64Shl x (I64Const [48])) (I64Const [48]))
(SignExt8to(64|32|16) x) -> (I64ShrS (I64Shl x (I64Const [56])) (I64Const [56]))
(ZeroExt32to64 x) -> (I64ShrU (I64Shl x (I64Const [32])) (I64Const [32]))
(ZeroExt16to(64|32) x) -> (I64ShrU (I64Shl x (I64Const [48])) (I64Const [48]))
(ZeroExt8to(64|32|16) x) -> (I64ShrU (I64Shl x (I64Const [56])) (I64Const [56]))
(Slicemask x) -> (I64ShrS (I64Sub (I64Const [0]) x) (I64Const [63]))
// Lowering truncation
// Because we ignore the high parts, truncates are just copies.
(Trunc64to(32|16|8) x) -> x
(Trunc32to(16|8) x) -> x
(Trunc16to8 x) -> x
// Lowering float <-> int
(Cvt32to32F x) -> (LoweredRound32F (F64ConvertSI64 (SignExt32to64 x)))
(Cvt32to64F x) -> (F64ConvertSI64 (SignExt32to64 x))
(Cvt64to32F x) -> (LoweredRound32F (F64ConvertSI64 x))
(Cvt64to64F x) -> (F64ConvertSI64 x)
(Cvt32Uto32F x) -> (LoweredRound32F (F64ConvertUI64 (ZeroExt32to64 x)))
(Cvt32Uto64F x) -> (F64ConvertUI64 (ZeroExt32to64 x))
(Cvt64Uto32F x) -> (LoweredRound32F (F64ConvertUI64 x))
(Cvt64Uto64F x) -> (F64ConvertUI64 x)
(Cvt32Fto32 x) -> (I64TruncSF64 x)
(Cvt32Fto64 x) -> (I64TruncSF64 x)
(Cvt64Fto32 x) -> (I64TruncSF64 x)
(Cvt64Fto64 x) -> (I64TruncSF64 x)
(Cvt32Fto32U x) -> (I64TruncUF64 x)
(Cvt32Fto64U x) -> (I64TruncUF64 x)
(Cvt64Fto32U x) -> (I64TruncUF64 x)
(Cvt64Fto64U x) -> (I64TruncUF64 x)
(Cvt32Fto64F x) -> x
(Cvt64Fto32F x) -> (LoweredRound32F x)
(Round32F x) -> (LoweredRound32F x)
(Round64F x) -> x
// Lowering shifts
// Unsigned shifts need to return 0 if shift amount is >= width of shifted value.
(Lsh64x64 x y) -> (Select (I64Shl x y) (I64Const [0]) (I64LtU y (I64Const [64])))
(Lsh64x32 x y) -> (Lsh64x64 x (ZeroExt32to64 y))
(Lsh64x16 x y) -> (Lsh64x64 x (ZeroExt16to64 y))
(Lsh64x8 x y) -> (Lsh64x64 x (ZeroExt8to64 y))
(Lsh32x64 x y) -> (Lsh64x64 x y)
(Lsh32x32 x y) -> (Lsh64x64 x (ZeroExt32to64 y))
(Lsh32x16 x y) -> (Lsh64x64 x (ZeroExt16to64 y))
(Lsh32x8 x y) -> (Lsh64x64 x (ZeroExt8to64 y))
(Lsh16x64 x y) -> (Lsh64x64 x y)
(Lsh16x32 x y) -> (Lsh64x64 x (ZeroExt32to64 y))
(Lsh16x16 x y) -> (Lsh64x64 x (ZeroExt16to64 y))
(Lsh16x8 x y) -> (Lsh64x64 x (ZeroExt8to64 y))
(Lsh8x64 x y) -> (Lsh64x64 x y)
(Lsh8x32 x y) -> (Lsh64x64 x (ZeroExt32to64 y))
(Lsh8x16 x y) -> (Lsh64x64 x (ZeroExt16to64 y))
(Lsh8x8 x y) -> (Lsh64x64 x (ZeroExt8to64 y))
(Rsh64Ux64 x y) -> (Select (I64ShrU x y) (I64Const [0]) (I64LtU y (I64Const [64])))
(Rsh64Ux32 x y) -> (Rsh64Ux64 x (ZeroExt32to64 y))
(Rsh64Ux16 x y) -> (Rsh64Ux64 x (ZeroExt16to64 y))
(Rsh64Ux8 x y) -> (Rsh64Ux64 x (ZeroExt8to64 y))
(Rsh32Ux64 x y) -> (Rsh64Ux64 (ZeroExt32to64 x) y)
(Rsh32Ux32 x y) -> (Rsh64Ux64 (ZeroExt32to64 x) (ZeroExt32to64 y))
(Rsh32Ux16 x y) -> (Rsh64Ux64 (ZeroExt32to64 x) (ZeroExt16to64 y))
(Rsh32Ux8 x y) -> (Rsh64Ux64 (ZeroExt32to64 x) (ZeroExt8to64 y))
(Rsh16Ux64 x y) -> (Rsh64Ux64 (ZeroExt16to64 x) y)
(Rsh16Ux32 x y) -> (Rsh64Ux64 (ZeroExt16to64 x) (ZeroExt32to64 y))
(Rsh16Ux16 x y) -> (Rsh64Ux64 (ZeroExt16to64 x) (ZeroExt16to64 y))
(Rsh16Ux8 x y) -> (Rsh64Ux64 (ZeroExt16to64 x) (ZeroExt8to64 y))
(Rsh8Ux64 x y) -> (Rsh64Ux64 (ZeroExt8to64 x) y)
(Rsh8Ux32 x y) -> (Rsh64Ux64 (ZeroExt8to64 x) (ZeroExt32to64 y))
(Rsh8Ux16 x y) -> (Rsh64Ux64 (ZeroExt8to64 x) (ZeroExt16to64 y))
(Rsh8Ux8 x y) -> (Rsh64Ux64 (ZeroExt8to64 x) (ZeroExt8to64 y))
// Signed right shift needs to return 0/-1 if shift amount is >= width of shifted value.
// We implement this by setting the shift value to (width - 1) if the shift value is >= width.
(Rsh64x64 x y) -> (I64ShrS x (Select <typ.Int64> y (I64Const [63]) (I64LtU y (I64Const [64]))))
(Rsh64x32 x y) -> (Rsh64x64 x (ZeroExt32to64 y))
(Rsh64x16 x y) -> (Rsh64x64 x (ZeroExt16to64 y))
(Rsh64x8 x y) -> (Rsh64x64 x (ZeroExt8to64 y))
(Rsh32x64 x y) -> (Rsh64x64 (SignExt32to64 x) y)
(Rsh32x32 x y) -> (Rsh64x64 (SignExt32to64 x) (ZeroExt32to64 y))
(Rsh32x16 x y) -> (Rsh64x64 (SignExt32to64 x) (ZeroExt16to64 y))
(Rsh32x8 x y) -> (Rsh64x64 (SignExt32to64 x) (ZeroExt8to64 y))
(Rsh16x64 x y) -> (Rsh64x64 (SignExt16to64 x) y)
(Rsh16x32 x y) -> (Rsh64x64 (SignExt16to64 x) (ZeroExt32to64 y))
(Rsh16x16 x y) -> (Rsh64x64 (SignExt16to64 x) (ZeroExt16to64 y))
(Rsh16x8 x y) -> (Rsh64x64 (SignExt16to64 x) (ZeroExt8to64 y))
(Rsh8x64 x y) -> (Rsh64x64 (SignExt8to64 x) y)
(Rsh8x32 x y) -> (Rsh64x64 (SignExt8to64 x) (ZeroExt32to64 y))
(Rsh8x16 x y) -> (Rsh64x64 (SignExt8to64 x) (ZeroExt16to64 y))
(Rsh8x8 x y) -> (Rsh64x64 (SignExt8to64 x) (ZeroExt8to64 y))
// Lowering comparisons
(Less64 x y) -> (I64LtS x y)
(Less32 x y) -> (I64LtS (SignExt32to64 x) (SignExt32to64 y))
(Less16 x y) -> (I64LtS (SignExt16to64 x) (SignExt16to64 y))
(Less8 x y) -> (I64LtS (SignExt8to64 x) (SignExt8to64 y))
(Less64U x y) -> (I64LtU x y)
(Less32U x y) -> (I64LtU (ZeroExt32to64 x) (ZeroExt32to64 y))
(Less16U x y) -> (I64LtU (ZeroExt16to64 x) (ZeroExt16to64 y))
(Less8U x y) -> (I64LtU (ZeroExt8to64 x) (ZeroExt8to64 y))
(Less64F x y) -> (F64Lt x y)
(Less32F x y) -> (F64Lt (LoweredRound32F x) (LoweredRound32F y))
(Leq64 x y) -> (I64LeS x y)
(Leq32 x y) -> (I64LeS (SignExt32to64 x) (SignExt32to64 y))
(Leq16 x y) -> (I64LeS (SignExt16to64 x) (SignExt16to64 y))
(Leq8 x y) -> (I64LeS (SignExt8to64 x) (SignExt8to64 y))
(Leq64U x y) -> (I64LeU x y)
(Leq32U x y) -> (I64LeU (ZeroExt32to64 x) (ZeroExt32to64 y))
(Leq16U x y) -> (I64LeU (ZeroExt16to64 x) (ZeroExt16to64 y))
(Leq8U x y) -> (I64LeU (ZeroExt8to64 x) (ZeroExt8to64 y))
(Leq64F x y) -> (F64Le x y)
(Leq32F x y) -> (F64Le (LoweredRound32F x) (LoweredRound32F y))
(Greater64 x y) -> (I64GtS x y)
(Greater32 x y) -> (I64GtS (SignExt32to64 x) (SignExt32to64 y))
(Greater16 x y) -> (I64GtS (SignExt16to64 x) (SignExt16to64 y))
(Greater8 x y) -> (I64GtS (SignExt8to64 x) (SignExt8to64 y))
(Greater64U x y) -> (I64GtU x y)
(Greater32U x y) -> (I64GtU (ZeroExt32to64 x) (ZeroExt32to64 y))
(Greater16U x y) -> (I64GtU (ZeroExt16to64 x) (ZeroExt16to64 y))
(Greater8U x y) -> (I64GtU (ZeroExt8to64 x) (ZeroExt8to64 y))
(Greater64F x y) -> (F64Gt x y)
(Greater32F x y) -> (F64Gt (LoweredRound32F x) (LoweredRound32F y))
(Geq64 x y) -> (I64GeS x y)
(Geq32 x y) -> (I64GeS (SignExt32to64 x) (SignExt32to64 y))
(Geq16 x y) -> (I64GeS (SignExt16to64 x) (SignExt16to64 y))
(Geq8 x y) -> (I64GeS (SignExt8to64 x) (SignExt8to64 y))
(Geq64U x y) -> (I64GeU x y)
(Geq32U x y) -> (I64GeU (ZeroExt32to64 x) (ZeroExt32to64 y))
(Geq16U x y) -> (I64GeU (ZeroExt16to64 x) (ZeroExt16to64 y))
(Geq8U x y) -> (I64GeU (ZeroExt8to64 x) (ZeroExt8to64 y))
(Geq64F x y) -> (F64Ge x y)
(Geq32F x y) -> (F64Ge (LoweredRound32F x) (LoweredRound32F y))
(Eq64 x y) -> (I64Eq x y)
(Eq32 x y) -> (I64Eq (ZeroExt32to64 x) (ZeroExt32to64 y))
(Eq16 x y) -> (I64Eq (ZeroExt16to64 x) (ZeroExt16to64 y))
(Eq8 x y) -> (I64Eq (ZeroExt8to64 x) (ZeroExt8to64 y))
(EqB x y) -> (I64Eq x y)
(EqPtr x y) -> (I64Eq x y)
(Eq64F x y) -> (F64Eq x y)
(Eq32F x y) -> (F64Eq (LoweredRound32F x) (LoweredRound32F y))
(Neq64 x y) -> (I64Ne x y)
(Neq32 x y) -> (I64Ne (ZeroExt32to64 x) (ZeroExt32to64 y))
(Neq16 x y) -> (I64Ne (ZeroExt16to64 x) (ZeroExt16to64 y))
(Neq8 x y) -> (I64Ne (ZeroExt8to64 x) (ZeroExt8to64 y))
(NeqB x y) -> (I64Ne x y)
(NeqPtr x y) -> (I64Ne x y)
(Neq64F x y) -> (F64Ne x y)
(Neq32F x y) -> (F64Ne (LoweredRound32F x) (LoweredRound32F y))
// Lowering loads
(Load <t> ptr mem) && is32BitFloat(t) -> (F32Load ptr mem)
(Load <t> ptr mem) && is64BitFloat(t) -> (F64Load ptr mem)
(Load <t> ptr mem) && t.Size() == 8 -> (I64Load ptr mem)
(Load <t> ptr mem) && t.Size() == 4 && !t.IsSigned() -> (I64Load32U ptr mem)
(Load <t> ptr mem) && t.Size() == 4 && t.IsSigned() -> (I64Load32S ptr mem)
(Load <t> ptr mem) && t.Size() == 2 && !t.IsSigned() -> (I64Load16U ptr mem)
(Load <t> ptr mem) && t.Size() == 2 && t.IsSigned() -> (I64Load16S ptr mem)
(Load <t> ptr mem) && t.Size() == 1 && !t.IsSigned() -> (I64Load8U ptr mem)
(Load <t> ptr mem) && t.Size() == 1 && t.IsSigned() -> (I64Load8S ptr mem)
// Lowering stores
(Store {t} ptr val mem) && is64BitFloat(t.(*types.Type)) -> (F64Store ptr val mem)
(Store {t} ptr val mem) && is32BitFloat(t.(*types.Type)) -> (F32Store ptr val mem)
(Store {t} ptr val mem) && t.(*types.Type).Size() == 8 -> (I64Store ptr val mem)
(Store {t} ptr val mem) && t.(*types.Type).Size() == 4 -> (I64Store32 ptr val mem)
(Store {t} ptr val mem) && t.(*types.Type).Size() == 2 -> (I64Store16 ptr val mem)
(Store {t} ptr val mem) && t.(*types.Type).Size() == 1 -> (I64Store8 ptr val mem)
// Lowering moves
(Move [0] _ _ mem) -> mem
(Move [1] dst src mem) -> (I64Store8 dst (I64Load8U src mem) mem)
(Move [2] dst src mem) -> (I64Store16 dst (I64Load16U src mem) mem)
(Move [4] dst src mem) -> (I64Store32 dst (I64Load32U src mem) mem)
(Move [8] dst src mem) -> (I64Store dst (I64Load src mem) mem)
(Move [16] dst src mem) ->
(I64Store [8] dst (I64Load [8] src mem)
(I64Store dst (I64Load src mem) mem))
(Move [3] dst src mem) ->
(I64Store8 [2] dst (I64Load8U [2] src mem)
(I64Store16 dst (I64Load16U src mem) mem))
(Move [5] dst src mem) ->
(I64Store8 [4] dst (I64Load8U [4] src mem)
(I64Store32 dst (I64Load32U src mem) mem))
(Move [6] dst src mem) ->
(I64Store16 [4] dst (I64Load16U [4] src mem)
(I64Store32 dst (I64Load32U src mem) mem))
(Move [7] dst src mem) ->
(I64Store32 [3] dst (I64Load32U [3] src mem)
(I64Store32 dst (I64Load32U src mem) mem))
(Move [s] dst src mem) && s > 8 && s < 16 ->
(I64Store [s-8] dst (I64Load [s-8] src mem)
(I64Store dst (I64Load src mem) mem))
// Adjust moves to be a multiple of 16 bytes.
(Move [s] dst src mem)
&& s > 16 && s%16 != 0 && s%16 <= 8 ->
(Move [s-s%16]
(OffPtr <dst.Type> dst [s%16])
(OffPtr <src.Type> src [s%16])
(I64Store dst (I64Load src mem) mem))
(Move [s] dst src mem)
&& s > 16 && s%16 != 0 && s%16 > 8 ->
(Move [s-s%16]
(OffPtr <dst.Type> dst [s%16])
(OffPtr <src.Type> src [s%16])
(I64Store [8] dst (I64Load [8] src mem)
(I64Store dst (I64Load src mem) mem)))
// Large copying uses helper.
(Move [s] dst src mem) && s%8 == 0 ->
(LoweredMove [s/8] dst src mem)
// Lowering Zero instructions
(Zero [0] _ mem) -> mem
(Zero [1] destptr mem) -> (I64Store8 destptr (I64Const [0]) mem)
(Zero [2] destptr mem) -> (I64Store16 destptr (I64Const [0]) mem)
(Zero [4] destptr mem) -> (I64Store32 destptr (I64Const [0]) mem)
(Zero [8] destptr mem) -> (I64Store destptr (I64Const [0]) mem)
(Zero [3] destptr mem) ->
(I64Store8 [2] destptr (I64Const [0])
(I64Store16 destptr (I64Const [0]) mem))
(Zero [5] destptr mem) ->
(I64Store8 [4] destptr (I64Const [0])
(I64Store32 destptr (I64Const [0]) mem))
(Zero [6] destptr mem) ->
(I64Store16 [4] destptr (I64Const [0])
(I64Store32 destptr (I64Const [0]) mem))
(Zero [7] destptr mem) ->
(I64Store32 [3] destptr (I64Const [0])
(I64Store32 destptr (I64Const [0]) mem))
// Strip off any fractional word zeroing.
(Zero [s] destptr mem) && s%8 != 0 && s > 8 ->
(Zero [s-s%8] (OffPtr <destptr.Type> destptr [s%8])
(I64Store destptr (I64Const [0]) mem))
// Zero small numbers of words directly.
(Zero [16] destptr mem) ->
(I64Store [8] destptr (I64Const [0])
(I64Store destptr (I64Const [0]) mem))
(Zero [24] destptr mem) ->
(I64Store [16] destptr (I64Const [0])
(I64Store [8] destptr (I64Const [0])
(I64Store destptr (I64Const [0]) mem)))
(Zero [32] destptr mem) ->
(I64Store [24] destptr (I64Const [0])
(I64Store [16] destptr (I64Const [0])
(I64Store [8] destptr (I64Const [0])
(I64Store destptr (I64Const [0]) mem))))
// Large zeroing uses helper.
(Zero [s] destptr mem) && s%8 == 0 && s > 32 ->
(LoweredZero [s/8] destptr mem)
// Lowering constants
(Const(64|32|16|8) [val]) -> (I64Const [val])
(Const(64|32)F [val]) -> (F64Const [val])
(ConstNil) -> (I64Const [0])
(ConstBool [b]) -> (I64Const [b])
// Lowering calls
(StaticCall [argwid] {target} mem) -> (LoweredStaticCall [argwid] {target} mem)
(ClosureCall [argwid] entry closure mem) -> (LoweredClosureCall [argwid] entry closure mem)
(InterCall [argwid] entry mem) -> (LoweredInterCall [argwid] entry mem)
// Miscellaneous
(Convert <t> x mem) -> (LoweredConvert <t> x mem)
(IsNonNil p) -> (I64Eqz (I64Eqz p))
(IsInBounds idx len) -> (I64LtU idx len)
(IsSliceInBounds idx len) -> (I64LeU idx len)
(NilCheck ptr mem) -> (LoweredNilCheck ptr mem)
(GetClosurePtr) -> (LoweredGetClosurePtr)
(GetCallerPC) -> (LoweredGetCallerPC)
(GetCallerSP) -> (LoweredGetCallerSP)
(Addr {sym} base) -> (LoweredAddr {sym} base)
// Write barrier.
(WB {fn} destptr srcptr mem) -> (LoweredWB {fn} destptr srcptr mem)
// --- Optimizations ---
(I64Add (I64Const [x]) (I64Const [y])) -> (I64Const [x + y])
(I64Mul (I64Const [x]) (I64Const [y])) -> (I64Const [x * y])
(I64And (I64Const [x]) (I64Const [y])) -> (I64Const [x & y])
(I64Or (I64Const [x]) (I64Const [y])) -> (I64Const [x | y])
(I64Xor (I64Const [x]) (I64Const [y])) -> (I64Const [x ^ y])
(F64Add (F64Const [x]) (F64Const [y])) -> (F64Const [f2i(i2f(x) + i2f(y))])
(F64Mul (F64Const [x]) (F64Const [y])) -> (F64Const [f2i(i2f(x) * i2f(y))])
(I64Eq (I64Const [x]) (I64Const [y])) && x == y -> (I64Const [1])
(I64Eq (I64Const [x]) (I64Const [y])) && x != y -> (I64Const [0])
(I64Ne (I64Const [x]) (I64Const [y])) && x == y -> (I64Const [0])
(I64Ne (I64Const [x]) (I64Const [y])) && x != y -> (I64Const [1])
(I64Add (I64Const [x]) y) -> (I64Add y (I64Const [x]))
(I64Mul (I64Const [x]) y) -> (I64Mul y (I64Const [x]))
(I64And (I64Const [x]) y) -> (I64And y (I64Const [x]))
(I64Or (I64Const [x]) y) -> (I64Or y (I64Const [x]))
(I64Xor (I64Const [x]) y) -> (I64Xor y (I64Const [x]))
(F64Add (F64Const [x]) y) -> (F64Add y (F64Const [x]))
(F64Mul (F64Const [x]) y) -> (F64Mul y (F64Const [x]))
(I64Eq (I64Const [x]) y) -> (I64Eq y (I64Const [x]))
(I64Ne (I64Const [x]) y) -> (I64Ne y (I64Const [x]))
(I64Add x (I64Const [y])) -> (I64AddConst [y] x)
(I64Eqz (I64Eqz (I64Eqz x))) -> (I64Eqz x)
(I64Store8 [off] (I64AddConst [off2] ptr) val mem) && off+off2 >= 0 -> (I64Store8 [off+off2] ptr val mem)
(I64Store16 [off] (I64AddConst [off2] ptr) val mem) && off+off2 >= 0 -> (I64Store16 [off+off2] ptr val mem)
(I64Store32 [off] (I64AddConst [off2] ptr) val mem) && off+off2 >= 0 -> (I64Store32 [off+off2] ptr val mem)
(I64Store [off] (I64AddConst [off2] ptr) val mem) && off+off2 >= 0 -> (I64Store [off+off2] ptr val mem)
(I64Load8U [off] (I64AddConst [off2] ptr) mem) && off+off2 >= 0 -> (I64Load8U [off+off2] ptr mem)
(I64Load8S [off] (I64AddConst [off2] ptr) mem) && off+off2 >= 0 -> (I64Load8S [off+off2] ptr mem)
(I64Load16U [off] (I64AddConst [off2] ptr) mem) && off+off2 >= 0 -> (I64Load16U [off+off2] ptr mem)
(I64Load16S [off] (I64AddConst [off2] ptr) mem) && off+off2 >= 0 -> (I64Load16S [off+off2] ptr mem)
(I64Load32U [off] (I64AddConst [off2] ptr) mem) && off+off2 >= 0 -> (I64Load32U [off+off2] ptr mem)
(I64Load32S [off] (I64AddConst [off2] ptr) mem) && off+off2 >= 0 -> (I64Load32S [off+off2] ptr mem)
(I64Load [off] (I64AddConst [off2] ptr) mem) && off+off2 >= 0 -> (I64Load [off+off2] ptr mem)

View File

@ -0,0 +1,208 @@
// Copyright 2018 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.
// +build ignore
package main
import "strings"
var regNamesWasm = []string{
"R0",
"R1",
"R2",
"R3",
"R4",
"R5",
"R6",
"R7",
"R8",
"R9",
"R10",
"R11",
"R12",
"R13",
"R14",
"R15",
"F0",
"F1",
"F2",
"F3",
"F4",
"F5",
"F6",
"F7",
"F8",
"F9",
"F10",
"F11",
"F12",
"F13",
"F14",
"F15",
"SP",
"g",
// pseudo-registers
"SB",
}
func init() {
// Make map from reg names to reg integers.
if len(regNamesWasm) > 64 {
panic("too many registers")
}
num := map[string]int{}
for i, name := range regNamesWasm {
num[name] = i
}
buildReg := func(s string) regMask {
m := regMask(0)
for _, r := range strings.Split(s, " ") {
if n, ok := num[r]; ok {
m |= regMask(1) << uint(n)
continue
}
panic("register " + r + " not found")
}
return m
}
var (
gp = buildReg("R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15")
fp = buildReg("F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15")
gpsp = gp | buildReg("SP")
gpspsb = gpsp | buildReg("SB")
// The "registers", which are actually local variables, can get clobbered
// if we're switching goroutines, because it unwinds the WebAssembly stack.
callerSave = gp | fp | buildReg("g")
)
// Common regInfo
var (
gp01 = regInfo{inputs: nil, outputs: []regMask{gp}}
gp11 = regInfo{inputs: []regMask{gpsp}, outputs: []regMask{gp}}
gp21 = regInfo{inputs: []regMask{gpsp, gpsp}, outputs: []regMask{gp}}
gp31 = regInfo{inputs: []regMask{gpsp, gpsp, gpsp}, outputs: []regMask{gp}}
fp01 = regInfo{inputs: nil, outputs: []regMask{fp}}
fp11 = regInfo{inputs: []regMask{fp}, outputs: []regMask{fp}}
fp21 = regInfo{inputs: []regMask{fp, fp}, outputs: []regMask{fp}}
fp21gp = regInfo{inputs: []regMask{fp, fp}, outputs: []regMask{gp}}
gpload = regInfo{inputs: []regMask{gpspsb, 0}, outputs: []regMask{gp}}
gpstore = regInfo{inputs: []regMask{gpspsb, gpsp, 0}}
fpload = regInfo{inputs: []regMask{gpspsb, 0}, outputs: []regMask{fp}}
fpstore = regInfo{inputs: []regMask{gpspsb, fp, 0}}
// fpstoreconst = regInfo{inputs: []regMask{fp, 0}}
)
var WasmOps = []opData{
{name: "LoweredStaticCall", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "SymOff", call: true, symEffect: "None"}, // call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem
{name: "LoweredClosureCall", argLength: 3, reg: regInfo{inputs: []regMask{gp, gp, 0}, clobbers: callerSave}, aux: "Int64", call: true}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem
{name: "LoweredInterCall", argLength: 2, reg: regInfo{inputs: []regMask{gp}, clobbers: callerSave}, aux: "Int64", call: true}, // call fn by pointer. arg0=codeptr, arg1=mem, auxint=argsize, returns mem
{name: "LoweredAddr", argLength: 1, reg: gp11, aux: "SymOff", rematerializeable: true, symEffect: "Addr"}, // returns base+aux, arg0=base
{name: "LoweredMove", argLength: 3, reg: regInfo{inputs: []regMask{gp, gp}}, aux: "Int64"}, // large move. arg0=dst, arg1=src, arg2=mem, auxint=len/8, returns mem
{name: "LoweredZero", argLength: 2, reg: regInfo{inputs: []regMask{gp}}, aux: "Int64"}, // large zeroing. arg0=start, arg1=mem, auxint=len/8, returns mem
{name: "LoweredGetClosurePtr", reg: gp01}, // returns wasm.REG_CTXT, the closure pointer
{name: "LoweredGetCallerPC", reg: gp01, rematerializeable: true}, // returns the PC of the caller of the current function
{name: "LoweredGetCallerSP", reg: gp01, rematerializeable: true}, // returns the SP of the caller of the current function
{name: "LoweredNilCheck", argLength: 2, reg: regInfo{inputs: []regMask{gp}}, nilCheck: true, faultOnNilArg0: true}, // panic if arg0 is nil. arg1=mem
{name: "LoweredWB", argLength: 3, reg: regInfo{inputs: []regMask{gp, gp}}, aux: "Sym", symEffect: "None"}, // invokes runtime.gcWriteBarrier. arg0=destptr, arg1=srcptr, arg2=mem, aux=runtime.gcWriteBarrier
{name: "LoweredRound32F", argLength: 1, reg: fp11, typ: "Float32"}, // rounds arg0 to 32-bit float precision. arg0=value
// LoweredConvert converts between pointers and integers.
// We have a special op for this so as to not confuse GC
// (particularly stack maps). It takes a memory arg so it
// gets correctly ordered with respect to GC safepoints.
// arg0=ptr/int arg1=mem, output=int/ptr
//
// TODO(neelance): LoweredConvert should not be necessary any more, since OpConvert does not need to be lowered any more (CL 108496).
{name: "LoweredConvert", argLength: 2, reg: regInfo{inputs: []regMask{gp}, outputs: []regMask{gp}}},
// The following are native WebAssembly instructions, see https://webassembly.github.io/spec/core/syntax/instructions.html
{name: "Select", asm: "Select", argLength: 3, reg: gp31}, // returns arg0 if arg2 != 0, otherwise returns arg1
{name: "I64Load8U", asm: "I64Load8U", argLength: 2, reg: gpload, aux: "Int64", typ: "UInt8"}, // read unsigned 8-bit integer from address arg0+aux, arg1=mem
{name: "I64Load8S", asm: "I64Load8S", argLength: 2, reg: gpload, aux: "Int64", typ: "Int8"}, // read signed 8-bit integer from address arg0+aux, arg1=mem
{name: "I64Load16U", asm: "I64Load16U", argLength: 2, reg: gpload, aux: "Int64", typ: "UInt16"}, // read unsigned 16-bit integer from address arg0+aux, arg1=mem
{name: "I64Load16S", asm: "I64Load16S", argLength: 2, reg: gpload, aux: "Int64", typ: "Int16"}, // read signed 16-bit integer from address arg0+aux, arg1=mem
{name: "I64Load32U", asm: "I64Load32U", argLength: 2, reg: gpload, aux: "Int64", typ: "UInt32"}, // read unsigned 32-bit integer from address arg0+aux, arg1=mem
{name: "I64Load32S", asm: "I64Load32S", argLength: 2, reg: gpload, aux: "Int64", typ: "Int32"}, // read signed 32-bit integer from address arg0+aux, arg1=mem
{name: "I64Load", asm: "I64Load", argLength: 2, reg: gpload, aux: "Int64", typ: "UInt64"}, // read 64-bit integer from address arg0+aux, arg1=mem
{name: "I64Store8", asm: "I64Store8", argLength: 3, reg: gpstore, aux: "Int64", typ: "Mem"}, // store 8-bit integer arg1 at address arg0+aux, arg2=mem, returns mem
{name: "I64Store16", asm: "I64Store16", argLength: 3, reg: gpstore, aux: "Int64", typ: "Mem"}, // store 16-bit integer arg1 at address arg0+aux, arg2=mem, returns mem
{name: "I64Store32", asm: "I64Store32", argLength: 3, reg: gpstore, aux: "Int64", typ: "Mem"}, // store 32-bit integer arg1 at address arg0+aux, arg2=mem, returns mem
{name: "I64Store", asm: "I64Store", argLength: 3, reg: gpstore, aux: "Int64", typ: "Mem"}, // store 64-bit integer arg1 at address arg0+aux, arg2=mem, returns mem
{name: "F32Load", asm: "F32Load", argLength: 2, reg: fpload, aux: "Int64", typ: "Float64"}, // read 32-bit float from address arg0+aux, arg1=mem
{name: "F64Load", asm: "F64Load", argLength: 2, reg: fpload, aux: "Int64", typ: "Float64"}, // read 64-bit float from address arg0+aux, arg1=mem
{name: "F32Store", asm: "F32Store", argLength: 3, reg: fpstore, aux: "Int64", typ: "Mem"}, // store 32-bit float arg1 at address arg0+aux, arg2=mem, returns mem
{name: "F64Store", asm: "F64Store", argLength: 3, reg: fpstore, aux: "Int64", typ: "Mem"}, // store 64-bit float arg1 at address arg0+aux, arg2=mem, returns mem
{name: "I64Const", reg: gp01, aux: "Int64", rematerializeable: true, typ: "Int64"}, // returns the constant integer aux
{name: "F64Const", reg: fp01, aux: "Float64", rematerializeable: true, typ: "Float64"}, // returns the constant float aux
{name: "I64Eqz", asm: "I64Eqz", argLength: 1, reg: gp11, typ: "Bool"}, // arg0 == 0
{name: "I64Eq", asm: "I64Eq", argLength: 2, reg: gp21, typ: "Bool"}, // arg0 == arg1
{name: "I64Ne", asm: "I64Ne", argLength: 2, reg: gp21, typ: "Bool"}, // arg0 != arg1
{name: "I64LtS", asm: "I64LtS", argLength: 2, reg: gp21, typ: "Bool"}, // arg0 < arg1 (signed)
{name: "I64LtU", asm: "I64LtU", argLength: 2, reg: gp21, typ: "Bool"}, // arg0 < arg1 (unsigned)
{name: "I64GtS", asm: "I64GtS", argLength: 2, reg: gp21, typ: "Bool"}, // arg0 > arg1 (signed)
{name: "I64GtU", asm: "I64GtU", argLength: 2, reg: gp21, typ: "Bool"}, // arg0 > arg1 (unsigned)
{name: "I64LeS", asm: "I64LeS", argLength: 2, reg: gp21, typ: "Bool"}, // arg0 <= arg1 (signed)
{name: "I64LeU", asm: "I64LeU", argLength: 2, reg: gp21, typ: "Bool"}, // arg0 <= arg1 (unsigned)
{name: "I64GeS", asm: "I64GeS", argLength: 2, reg: gp21, typ: "Bool"}, // arg0 >= arg1 (signed)
{name: "I64GeU", asm: "I64GeU", argLength: 2, reg: gp21, typ: "Bool"}, // arg0 >= arg1 (unsigned)
{name: "F64Eq", asm: "F64Eq", argLength: 2, reg: fp21gp, typ: "Bool"}, // arg0 == arg1
{name: "F64Ne", asm: "F64Ne", argLength: 2, reg: fp21gp, typ: "Bool"}, // arg0 != arg1
{name: "F64Lt", asm: "F64Lt", argLength: 2, reg: fp21gp, typ: "Bool"}, // arg0 < arg1
{name: "F64Gt", asm: "F64Gt", argLength: 2, reg: fp21gp, typ: "Bool"}, // arg0 > arg1
{name: "F64Le", asm: "F64Le", argLength: 2, reg: fp21gp, typ: "Bool"}, // arg0 <= arg1
{name: "F64Ge", asm: "F64Ge", argLength: 2, reg: fp21gp, typ: "Bool"}, // arg0 >= arg1
{name: "I64Add", asm: "I64Add", argLength: 2, reg: gp21, typ: "Int64"}, // arg0 + arg1
{name: "I64AddConst", asm: "I64Add", argLength: 1, reg: gp11, aux: "Int64", typ: "Int64"}, // arg0 + aux
{name: "I64Sub", asm: "I64Sub", argLength: 2, reg: gp21, typ: "Int64"}, // arg0 - arg1
{name: "I64Mul", asm: "I64Mul", argLength: 2, reg: gp21, typ: "Int64"}, // arg0 * arg1
{name: "I64DivS", asm: "I64DivS", argLength: 2, reg: gp21, typ: "Int64"}, // arg0 / arg1 (signed)
{name: "I64DivU", asm: "I64DivU", argLength: 2, reg: gp21, typ: "Int64"}, // arg0 / arg1 (unsigned)
{name: "I64RemS", asm: "I64RemS", argLength: 2, reg: gp21, typ: "Int64"}, // arg0 % arg1 (signed)
{name: "I64RemU", asm: "I64RemU", argLength: 2, reg: gp21, typ: "Int64"}, // arg0 % arg1 (unsigned)
{name: "I64And", asm: "I64And", argLength: 2, reg: gp21, typ: "Int64"}, // arg0 & arg1
{name: "I64Or", asm: "I64Or", argLength: 2, reg: gp21, typ: "Int64"}, // arg0 | arg1
{name: "I64Xor", asm: "I64Xor", argLength: 2, reg: gp21, typ: "Int64"}, // arg0 ^ arg1
{name: "I64Shl", asm: "I64Shl", argLength: 2, reg: gp21, typ: "Int64"}, // arg0 << (arg1 % 64)
{name: "I64ShrS", asm: "I64ShrS", argLength: 2, reg: gp21, typ: "Int64"}, // arg0 >> (arg1 % 64) (signed)
{name: "I64ShrU", asm: "I64ShrU", argLength: 2, reg: gp21, typ: "Int64"}, // arg0 >> (arg1 % 64) (unsigned)
{name: "F64Neg", asm: "F64Neg", argLength: 1, reg: fp11, typ: "Float64"}, // -arg0
{name: "F64Add", asm: "F64Add", argLength: 2, reg: fp21, typ: "Float64"}, // arg0 + arg1
{name: "F64Sub", asm: "F64Sub", argLength: 2, reg: fp21, typ: "Float64"}, // arg0 - arg1
{name: "F64Mul", asm: "F64Mul", argLength: 2, reg: fp21, typ: "Float64"}, // arg0 * arg1
{name: "F64Div", asm: "F64Div", argLength: 2, reg: fp21, typ: "Float64"}, // arg0 / arg1
{name: "I64TruncSF64", asm: "I64TruncSF64", argLength: 1, reg: regInfo{inputs: []regMask{fp}, outputs: []regMask{gp}}, typ: "Int64"}, // truncates the float arg0 to a signed integer
{name: "I64TruncUF64", asm: "I64TruncUF64", argLength: 1, reg: regInfo{inputs: []regMask{fp}, outputs: []regMask{gp}}, typ: "Int64"}, // truncates the float arg0 to an unsigned integer
{name: "F64ConvertSI64", asm: "F64ConvertSI64", argLength: 1, reg: regInfo{inputs: []regMask{gp}, outputs: []regMask{fp}}, typ: "Float64"}, // converts the signed integer arg0 to a float
{name: "F64ConvertUI64", asm: "F64ConvertUI64", argLength: 1, reg: regInfo{inputs: []regMask{gp}, outputs: []regMask{fp}}, typ: "Float64"}, // converts the unsigned integer arg0 to a float
}
archs = append(archs, arch{
name: "Wasm",
pkg: "cmd/internal/obj/wasm",
genfile: "",
ops: WasmOps,
blocks: nil,
regnames: regNamesWasm,
gpregmask: gp,
fpregmask: fp,
framepointerreg: -1, // not used
linkreg: -1, // not used
})
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -90,7 +90,8 @@ func schedule(f *Func) {
case v.Op == OpAMD64LoweredGetClosurePtr || v.Op == OpPPC64LoweredGetClosurePtr || case v.Op == OpAMD64LoweredGetClosurePtr || v.Op == OpPPC64LoweredGetClosurePtr ||
v.Op == OpARMLoweredGetClosurePtr || v.Op == OpARM64LoweredGetClosurePtr || v.Op == OpARMLoweredGetClosurePtr || v.Op == OpARM64LoweredGetClosurePtr ||
v.Op == Op386LoweredGetClosurePtr || v.Op == OpMIPS64LoweredGetClosurePtr || v.Op == Op386LoweredGetClosurePtr || v.Op == OpMIPS64LoweredGetClosurePtr ||
v.Op == OpS390XLoweredGetClosurePtr || v.Op == OpMIPSLoweredGetClosurePtr: v.Op == OpS390XLoweredGetClosurePtr || v.Op == OpMIPSLoweredGetClosurePtr ||
v.Op == OpWasmLoweredGetClosurePtr:
// We also score GetLoweredClosurePtr as early as possible to ensure that the // We also score GetLoweredClosurePtr as early as possible to ensure that the
// context register is not stomped. GetLoweredClosurePtr should only appear // context register is not stomped. GetLoweredClosurePtr should only appear
// in the entry block where there are no phi functions, so there is no // in the entry block where there are no phi functions, so there is no
@ -102,7 +103,8 @@ func schedule(f *Func) {
case v.Op == OpAMD64LoweredNilCheck || v.Op == OpPPC64LoweredNilCheck || case v.Op == OpAMD64LoweredNilCheck || v.Op == OpPPC64LoweredNilCheck ||
v.Op == OpARMLoweredNilCheck || v.Op == OpARM64LoweredNilCheck || v.Op == OpARMLoweredNilCheck || v.Op == OpARM64LoweredNilCheck ||
v.Op == Op386LoweredNilCheck || v.Op == OpMIPS64LoweredNilCheck || v.Op == Op386LoweredNilCheck || v.Op == OpMIPS64LoweredNilCheck ||
v.Op == OpS390XLoweredNilCheck || v.Op == OpMIPSLoweredNilCheck: v.Op == OpS390XLoweredNilCheck || v.Op == OpMIPSLoweredNilCheck ||
v.Op == OpWasmLoweredNilCheck:
// Nil checks must come before loads from the same address. // Nil checks must come before loads from the same address.
score[v.ID] = ScoreNilCheck score[v.ID] = ScoreNilCheck
case v.Op == OpPhi: case v.Op == OpPhi:

View File

@ -18,7 +18,8 @@ func tighten(f *Func) {
OpAMD64LoweredGetClosurePtr, Op386LoweredGetClosurePtr, OpAMD64LoweredGetClosurePtr, Op386LoweredGetClosurePtr,
OpARMLoweredGetClosurePtr, OpARM64LoweredGetClosurePtr, OpARMLoweredGetClosurePtr, OpARM64LoweredGetClosurePtr,
OpMIPSLoweredGetClosurePtr, OpMIPS64LoweredGetClosurePtr, OpMIPSLoweredGetClosurePtr, OpMIPS64LoweredGetClosurePtr,
OpS390XLoweredGetClosurePtr, OpPPC64LoweredGetClosurePtr: OpS390XLoweredGetClosurePtr, OpPPC64LoweredGetClosurePtr,
OpWasmLoweredGetClosurePtr:
// Phis need to stay in their block. // Phis need to stay in their block.
// GetClosurePtr & Arg must stay in the entry block. // GetClosurePtr & Arg must stay in the entry block.
// Tuple selectors must stay with the tuple generator. // Tuple selectors must stay with the tuple generator.

View File

@ -0,0 +1,430 @@
// Copyright 2018 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 wasm
import (
"cmd/compile/internal/gc"
"cmd/compile/internal/ssa"
"cmd/compile/internal/types"
"cmd/internal/obj"
"cmd/internal/obj/wasm"
)
func Init(arch *gc.Arch) {
arch.LinkArch = &wasm.Linkwasm
arch.REGSP = wasm.REG_SP
arch.MAXWIDTH = 1 << 50
arch.ZeroRange = zeroRange
arch.ZeroAuto = zeroAuto
arch.Ginsnop = ginsnop
arch.SSAMarkMoves = ssaMarkMoves
arch.SSAGenValue = ssaGenValue
arch.SSAGenBlock = ssaGenBlock
}
func zeroRange(pp *gc.Progs, p *obj.Prog, off, cnt int64, state *uint32) *obj.Prog {
if cnt == 0 {
return p
}
if cnt%8 != 0 {
gc.Fatalf("zerorange count not a multiple of widthptr %d", cnt)
}
for i := int64(0); i < cnt; i += 8 {
p = pp.Appendpp(p, wasm.AGet, obj.TYPE_REG, wasm.REG_SP, 0, 0, 0, 0)
p = pp.Appendpp(p, wasm.AI64Const, obj.TYPE_CONST, 0, 0, 0, 0, 0)
p = pp.Appendpp(p, wasm.AI64Store, 0, 0, 0, obj.TYPE_CONST, 0, off+i)
}
return p
}
func zeroAuto(pp *gc.Progs, n *gc.Node) {
sym := n.Sym.Linksym()
size := n.Type.Size()
for i := int64(0); i < size; i += 8 {
p := pp.Prog(wasm.AGet)
p.From = obj.Addr{Type: obj.TYPE_REG, Reg: wasm.REG_SP}
p = pp.Prog(wasm.AI64Const)
p.From = obj.Addr{Type: obj.TYPE_CONST, Offset: 0}
p = pp.Prog(wasm.AI64Store)
p.To = obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_AUTO, Offset: n.Xoffset + i, Sym: sym}
}
}
func ginsnop(pp *gc.Progs) {
pp.Prog(wasm.ANop)
}
func ssaMarkMoves(s *gc.SSAGenState, b *ssa.Block) {
}
func ssaGenBlock(s *gc.SSAGenState, b, next *ssa.Block) {
goToBlock := func(block *ssa.Block, canFallthrough bool) {
if canFallthrough && block == next {
return
}
s.Br(obj.AJMP, block)
}
switch b.Kind {
case ssa.BlockPlain:
goToBlock(b.Succs[0].Block(), true)
case ssa.BlockIf:
getReg32(s, b.Control)
s.Prog(wasm.AI32Eqz)
s.Prog(wasm.AIf)
goToBlock(b.Succs[1].Block(), false)
s.Prog(wasm.AEnd)
goToBlock(b.Succs[0].Block(), true)
case ssa.BlockRet:
s.Prog(obj.ARET)
case ssa.BlockRetJmp:
p := s.Prog(obj.ARET)
p.To.Type = obj.TYPE_MEM
p.To.Name = obj.NAME_EXTERN
p.To.Sym = b.Aux.(*obj.LSym)
case ssa.BlockExit:
s.Prog(obj.AUNDEF)
case ssa.BlockDefer:
p := s.Prog(wasm.AGet)
p.From = obj.Addr{Type: obj.TYPE_REG, Reg: wasm.REG_RET0}
s.Prog(wasm.AI64Eqz)
s.Prog(wasm.AI32Eqz)
s.Prog(wasm.AIf)
goToBlock(b.Succs[1].Block(), false)
s.Prog(wasm.AEnd)
goToBlock(b.Succs[0].Block(), true)
default:
panic("unexpected block")
}
// Entry point for the next block. Used by the JMP in goToBlock.
s.Prog(wasm.ARESUMEPOINT)
}
func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
switch v.Op {
case ssa.OpWasmLoweredStaticCall, ssa.OpWasmLoweredClosureCall, ssa.OpWasmLoweredInterCall:
s.PrepareCall(v)
if v.Aux == gc.Deferreturn {
// add a resume point before call to deferreturn so it can be called again via jmpdefer
s.Prog(wasm.ARESUMEPOINT)
}
if v.Op == ssa.OpWasmLoweredClosureCall {
getReg64(s, v.Args[1])
setReg(s, wasm.REG_CTXT)
}
if sym, ok := v.Aux.(*obj.LSym); ok {
p := s.Prog(obj.ACALL)
p.To = obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_EXTERN, Sym: sym}
} else {
getReg64(s, v.Args[0])
p := s.Prog(obj.ACALL)
p.To = obj.Addr{Type: obj.TYPE_NONE}
}
case ssa.OpWasmLoweredMove:
getReg32(s, v.Args[0])
getReg32(s, v.Args[1])
i32Const(s, int32(v.AuxInt))
p := s.Prog(wasm.ACall)
p.To = obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_EXTERN, Sym: gc.WasmMove}
case ssa.OpWasmLoweredZero:
getReg32(s, v.Args[0])
i32Const(s, int32(v.AuxInt))
p := s.Prog(wasm.ACall)
p.To = obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_EXTERN, Sym: gc.WasmZero}
case ssa.OpWasmLoweredNilCheck:
getReg64(s, v.Args[0])
s.Prog(wasm.AI64Eqz)
s.Prog(wasm.AIf)
p := s.Prog(wasm.ACALLNORESUME)
p.To = obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_EXTERN, Sym: gc.SigPanic}
s.Prog(wasm.AEnd)
if gc.Debug_checknil != 0 && v.Pos.Line() > 1 { // v.Pos.Line()==1 in generated wrappers
gc.Warnl(v.Pos, "generated nil check")
}
case ssa.OpWasmLoweredWB:
getReg64(s, v.Args[0])
getReg64(s, v.Args[1])
p := s.Prog(wasm.ACALLNORESUME) // TODO(neelance): If possible, turn this into a simple wasm.ACall).
p.To = obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_EXTERN, Sym: v.Aux.(*obj.LSym)}
case ssa.OpWasmI64Store8, ssa.OpWasmI64Store16, ssa.OpWasmI64Store32, ssa.OpWasmI64Store, ssa.OpWasmF32Store, ssa.OpWasmF64Store:
getReg32(s, v.Args[0])
getReg64(s, v.Args[1])
if v.Op == ssa.OpWasmF32Store {
s.Prog(wasm.AF32DemoteF64)
}
p := s.Prog(v.Op.Asm())
p.To = obj.Addr{Type: obj.TYPE_CONST, Offset: v.AuxInt}
case ssa.OpStoreReg:
getReg(s, wasm.REG_SP)
getReg64(s, v.Args[0])
if v.Type.Etype == types.TFLOAT32 {
s.Prog(wasm.AF32DemoteF64)
}
p := s.Prog(storeOp(v.Type))
gc.AddrAuto(&p.To, v)
default:
if v.Type.IsMemory() {
return
}
ssaGenValueOnStack(s, v)
setReg(s, v.Reg())
}
}
func ssaGenValueOnStack(s *gc.SSAGenState, v *ssa.Value) {
switch v.Op {
case ssa.OpWasmLoweredGetClosurePtr:
getReg(s, wasm.REG_CTXT)
case ssa.OpWasmLoweredGetCallerPC:
p := s.Prog(wasm.AI64Load)
// Caller PC is stored 8 bytes below first parameter.
p.From = obj.Addr{
Type: obj.TYPE_MEM,
Name: obj.NAME_PARAM,
Offset: -8,
}
case ssa.OpWasmLoweredGetCallerSP:
p := s.Prog(wasm.AGet)
// Caller SP is the address of the first parameter.
p.From = obj.Addr{
Type: obj.TYPE_ADDR,
Name: obj.NAME_PARAM,
Reg: wasm.REG_SP,
Offset: 0,
}
case ssa.OpWasmLoweredAddr:
p := s.Prog(wasm.AGet)
switch n := v.Aux.(type) {
case *obj.LSym:
p.From = obj.Addr{Type: obj.TYPE_ADDR, Name: obj.NAME_EXTERN, Sym: n}
case *gc.Node:
p.From = obj.Addr{
Type: obj.TYPE_ADDR,
Name: obj.NAME_AUTO,
Reg: v.Args[0].Reg(),
Offset: n.Xoffset,
}
if n.Class() == gc.PPARAM || n.Class() == gc.PPARAMOUT {
p.From.Name = obj.NAME_PARAM
}
default:
panic("wasm: bad LoweredAddr")
}
case ssa.OpWasmLoweredRound32F:
getReg64(s, v.Args[0])
s.Prog(wasm.AF32DemoteF64)
s.Prog(wasm.AF64PromoteF32)
case ssa.OpWasmLoweredConvert:
getReg64(s, v.Args[0])
case ssa.OpWasmSelect:
getReg64(s, v.Args[0])
getReg64(s, v.Args[1])
getReg64(s, v.Args[2])
s.Prog(wasm.AI32WrapI64)
s.Prog(v.Op.Asm())
case ssa.OpWasmI64AddConst:
getReg64(s, v.Args[0])
i64Const(s, v.AuxInt)
s.Prog(v.Op.Asm())
case ssa.OpWasmI64Const:
i64Const(s, v.AuxInt)
case ssa.OpWasmF64Const:
f64Const(s, v.AuxFloat())
case ssa.OpWasmI64Load8U, ssa.OpWasmI64Load8S, ssa.OpWasmI64Load16U, ssa.OpWasmI64Load16S, ssa.OpWasmI64Load32U, ssa.OpWasmI64Load32S, ssa.OpWasmI64Load, ssa.OpWasmF32Load, ssa.OpWasmF64Load:
getReg32(s, v.Args[0])
p := s.Prog(v.Op.Asm())
p.From = obj.Addr{Type: obj.TYPE_CONST, Offset: v.AuxInt}
if v.Op == ssa.OpWasmF32Load {
s.Prog(wasm.AF64PromoteF32)
}
case ssa.OpWasmI64Eqz:
getReg64(s, v.Args[0])
s.Prog(v.Op.Asm())
s.Prog(wasm.AI64ExtendUI32)
case ssa.OpWasmI64Eq, ssa.OpWasmI64Ne, ssa.OpWasmI64LtS, ssa.OpWasmI64LtU, ssa.OpWasmI64GtS, ssa.OpWasmI64GtU, ssa.OpWasmI64LeS, ssa.OpWasmI64LeU, ssa.OpWasmI64GeS, ssa.OpWasmI64GeU, ssa.OpWasmF64Eq, ssa.OpWasmF64Ne, ssa.OpWasmF64Lt, ssa.OpWasmF64Gt, ssa.OpWasmF64Le, ssa.OpWasmF64Ge:
getReg64(s, v.Args[0])
getReg64(s, v.Args[1])
s.Prog(v.Op.Asm())
s.Prog(wasm.AI64ExtendUI32)
case ssa.OpWasmI64Add, ssa.OpWasmI64Sub, ssa.OpWasmI64Mul, ssa.OpWasmI64DivU, ssa.OpWasmI64RemS, ssa.OpWasmI64RemU, ssa.OpWasmI64And, ssa.OpWasmI64Or, ssa.OpWasmI64Xor, ssa.OpWasmI64Shl, ssa.OpWasmI64ShrS, ssa.OpWasmI64ShrU, ssa.OpWasmF64Add, ssa.OpWasmF64Sub, ssa.OpWasmF64Mul, ssa.OpWasmF64Div:
getReg64(s, v.Args[0])
getReg64(s, v.Args[1])
s.Prog(v.Op.Asm())
case ssa.OpWasmI64DivS:
getReg64(s, v.Args[0])
getReg64(s, v.Args[1])
if v.Type.Size() == 8 {
// Division of int64 needs helper function wasmDiv to handle the MinInt64 / -1 case.
p := s.Prog(wasm.ACall)
p.To = obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_EXTERN, Sym: gc.WasmDiv}
break
}
s.Prog(wasm.AI64DivS)
case ssa.OpWasmI64TruncSF64:
getReg64(s, v.Args[0])
p := s.Prog(wasm.ACall)
p.To = obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_EXTERN, Sym: gc.WasmTruncS}
case ssa.OpWasmI64TruncUF64:
getReg64(s, v.Args[0])
p := s.Prog(wasm.ACall)
p.To = obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_EXTERN, Sym: gc.WasmTruncU}
case ssa.OpWasmF64Neg, ssa.OpWasmF64ConvertSI64, ssa.OpWasmF64ConvertUI64:
getReg64(s, v.Args[0])
s.Prog(v.Op.Asm())
case ssa.OpLoadReg:
p := s.Prog(loadOp(v.Type))
gc.AddrAuto(&p.From, v.Args[0])
if v.Type.Etype == types.TFLOAT32 {
s.Prog(wasm.AF64PromoteF32)
}
case ssa.OpCopy:
getReg64(s, v.Args[0])
default:
v.Fatalf("unexpected op: %s", v.Op)
}
}
func getReg32(s *gc.SSAGenState, v *ssa.Value) {
reg := v.Reg()
getReg(s, reg)
if reg != wasm.REG_SP {
s.Prog(wasm.AI32WrapI64)
}
}
func getReg64(s *gc.SSAGenState, v *ssa.Value) {
reg := v.Reg()
getReg(s, reg)
if reg == wasm.REG_SP {
s.Prog(wasm.AI64ExtendUI32)
}
}
func i32Const(s *gc.SSAGenState, val int32) {
p := s.Prog(wasm.AI32Const)
p.From = obj.Addr{Type: obj.TYPE_CONST, Offset: int64(val)}
}
func i64Const(s *gc.SSAGenState, val int64) {
p := s.Prog(wasm.AI64Const)
p.From = obj.Addr{Type: obj.TYPE_CONST, Offset: val}
}
func f64Const(s *gc.SSAGenState, val float64) {
p := s.Prog(wasm.AF64Const)
p.From = obj.Addr{Type: obj.TYPE_FCONST, Val: val}
}
func getReg(s *gc.SSAGenState, reg int16) {
p := s.Prog(wasm.AGet)
p.From = obj.Addr{Type: obj.TYPE_REG, Reg: reg}
}
func setReg(s *gc.SSAGenState, reg int16) {
p := s.Prog(wasm.ASet)
p.To = obj.Addr{Type: obj.TYPE_REG, Reg: reg}
}
func loadOp(t *types.Type) obj.As {
if t.IsFloat() {
switch t.Size() {
case 4:
return wasm.AF32Load
case 8:
return wasm.AF64Load
default:
panic("bad load type")
}
}
switch t.Size() {
case 1:
if t.IsSigned() {
return wasm.AI64Load8S
}
return wasm.AI64Load8U
case 2:
if t.IsSigned() {
return wasm.AI64Load16S
}
return wasm.AI64Load16U
case 4:
if t.IsSigned() {
return wasm.AI64Load32S
}
return wasm.AI64Load32U
case 8:
return wasm.AI64Load
default:
panic("bad load type")
}
}
func storeOp(t *types.Type) obj.As {
if t.IsFloat() {
switch t.Size() {
case 4:
return wasm.AF32Store
case 8:
return wasm.AF64Store
default:
panic("bad store type")
}
}
switch t.Size() {
case 1:
return wasm.AI64Store8
case 2:
return wasm.AI64Store16
case 4:
return wasm.AI64Store32
case 8:
return wasm.AI64Store
default:
panic("bad store type")
}
}

View File

@ -13,6 +13,7 @@ import (
"cmd/compile/internal/mips64" "cmd/compile/internal/mips64"
"cmd/compile/internal/ppc64" "cmd/compile/internal/ppc64"
"cmd/compile/internal/s390x" "cmd/compile/internal/s390x"
"cmd/compile/internal/wasm"
"cmd/compile/internal/x86" "cmd/compile/internal/x86"
"cmd/internal/objabi" "cmd/internal/objabi"
"fmt" "fmt"
@ -33,6 +34,7 @@ var archInits = map[string]func(*gc.Arch){
"ppc64": ppc64.Init, "ppc64": ppc64.Init,
"ppc64le": ppc64.Init, "ppc64le": ppc64.Init,
"s390x": s390x.Init, "s390x": s390x.Init,
"wasm": wasm.Init,
} }
func main() { func main() {

View File

@ -49,6 +49,7 @@ var bootstrapDirs = []string{
"cmd/compile/internal/ssa", "cmd/compile/internal/ssa",
"cmd/compile/internal/syntax", "cmd/compile/internal/syntax",
"cmd/compile/internal/x86", "cmd/compile/internal/x86",
"cmd/compile/internal/wasm",
"cmd/internal/bio", "cmd/internal/bio",
"cmd/internal/gcprog", "cmd/internal/gcprog",
"cmd/internal/dwarf", "cmd/internal/dwarf",
@ -61,6 +62,7 @@ var bootstrapDirs = []string{
"cmd/internal/obj/ppc64", "cmd/internal/obj/ppc64",
"cmd/internal/obj/s390x", "cmd/internal/obj/s390x",
"cmd/internal/obj/x86", "cmd/internal/obj/x86",
"cmd/internal/obj/wasm",
"cmd/internal/src", "cmd/internal/src",
"cmd/internal/sys", "cmd/internal/sys",
"cmd/link", "cmd/link",
@ -100,6 +102,8 @@ var ignorePrefixes = []string{
var ignoreSuffixes = []string{ var ignoreSuffixes = []string{
"_arm64.s", "_arm64.s",
"_arm64.go", "_arm64.go",
"_wasm.s",
"_wasm.go",
} }
func bootstrapBuildTools() { func bootstrapBuildTools() {

View File

@ -89,7 +89,7 @@ func (gcToolchain) gc(b *Builder, a *Action, archive string, importcfg []byte, a
gcargs = append(gcargs, "-buildid", a.buildID) gcargs = append(gcargs, "-buildid", a.buildID)
} }
platform := cfg.Goos + "/" + cfg.Goarch platform := cfg.Goos + "/" + cfg.Goarch
if p.Internal.OmitDebug || platform == "nacl/amd64p32" || cfg.Goos == "plan9" { if p.Internal.OmitDebug || platform == "nacl/amd64p32" || cfg.Goos == "plan9" || cfg.Goarch == "wasm" {
gcargs = append(gcargs, "-dwarf=false") gcargs = append(gcargs, "-dwarf=false")
} }
if strings.HasPrefix(runtimeVersion, "go1") && !strings.Contains(os.Args[0], "go_bootstrap") { if strings.HasPrefix(runtimeVersion, "go1") && !strings.Contains(os.Args[0], "go_bootstrap") {

View File

@ -364,6 +364,7 @@ const (
ABaseARM64 ABaseARM64
ABaseMIPS ABaseMIPS
ABaseS390X ABaseS390X
ABaseWasm
AllowedOpCodes = 1 << 11 // The number of opcodes available for any given architecture. AllowedOpCodes = 1 << 11 // The number of opcodes available for any given architecture.
AMask = AllowedOpCodes - 1 // AND with this to use the opcode as an array index. AMask = AllowedOpCodes - 1 // AND with this to use the opcode as an array index.
@ -595,7 +596,7 @@ func (ctxt *Link) Logf(format string, args ...interface{}) {
// the hardware stack pointer and the local variable area. // the hardware stack pointer and the local variable area.
func (ctxt *Link) FixedFrameSize() int64 { func (ctxt *Link) FixedFrameSize() int64 {
switch ctxt.Arch.Family { switch ctxt.Arch.Family {
case sys.AMD64, sys.I386: case sys.AMD64, sys.I386, sys.Wasm:
return 0 return 0
case sys.PPC64: case sys.PPC64:
// PIC code on ppc64le requires 32 bytes of stack, and it's easier to // PIC code on ppc64le requires 32 bytes of stack, and it's easier to

View File

@ -26,7 +26,7 @@ var (
pkg = flag.String("p", "", "package name") pkg = flag.String("p", "", "package name")
) )
var Are = regexp.MustCompile(`^\tA([A-Z0-9]+)`) var Are = regexp.MustCompile(`^\tA([A-Za-z0-9]+)`)
func main() { func main() {
flag.Parse() flag.Parse()

View File

@ -394,6 +394,7 @@ const (
RBaseARM64 = 8 * 1024 // range [8k, 13k) RBaseARM64 = 8 * 1024 // range [8k, 13k)
RBaseMIPS = 13 * 1024 // range [13k, 14k) RBaseMIPS = 13 * 1024 // range [13k, 14k)
RBaseS390X = 14 * 1024 // range [14k, 15k) RBaseS390X = 14 * 1024 // range [14k, 15k)
RBaseWasm = 16 * 1024
) )
// RegisterRegister binds a pretty-printer (Rconv) for register // RegisterRegister binds a pretty-printer (Rconv) for register

View File

@ -0,0 +1,288 @@
// Copyright 2018 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 wasm
import "cmd/internal/obj"
//go:generate go run ../stringer.go -i $GOFILE -o anames.go -p wasm
const (
/* mark flags */
DONE = 1 << iota
PRESERVEFLAGS // not allowed to clobber flags
)
/*
* wasm
*/
const (
ACallImport = obj.ABaseWasm + obj.A_ARCHSPECIFIC + iota
AGet
ASet
ATee
ANot // alias for I32Eqz
// The following are low-level WebAssembly instructions.
// Their order matters, since it matches the opcode encoding.
// Gaps in the encoding are indicated by comments.
AUnreachable // opcode 0x00
ANop
ABlock
ALoop
AIf
AElse
AEnd // opcode 0x0B
ABr
ABrIf
ABrTable
// ACall and AReturn are WebAssembly instructions. obj.ACALL and obj.ARET are higher level instructions
// with Go semantics, e.g. they manipulate the Go stack on the linear memory.
AReturn
ACall
ACallIndirect
ADrop // opcode 0x1A
ASelect
AI32Load // opcode 0x28
AI64Load
AF32Load
AF64Load
AI32Load8S
AI32Load8U
AI32Load16S
AI32Load16U
AI64Load8S
AI64Load8U
AI64Load16S
AI64Load16U
AI64Load32S
AI64Load32U
AI32Store
AI64Store
AF32Store
AF64Store
AI32Store8
AI32Store16
AI64Store8
AI64Store16
AI64Store32
ACurrentMemory
AGrowMemory
AI32Const
AI64Const
AF32Const
AF64Const
AI32Eqz
AI32Eq
AI32Ne
AI32LtS
AI32LtU
AI32GtS
AI32GtU
AI32LeS
AI32LeU
AI32GeS
AI32GeU
AI64Eqz
AI64Eq
AI64Ne
AI64LtS
AI64LtU
AI64GtS
AI64GtU
AI64LeS
AI64LeU
AI64GeS
AI64GeU
AF32Eq
AF32Ne
AF32Lt
AF32Gt
AF32Le
AF32Ge
AF64Eq
AF64Ne
AF64Lt
AF64Gt
AF64Le
AF64Ge
AI32Clz
AI32Ctz
AI32Popcnt
AI32Add
AI32Sub
AI32Mul
AI32DivS
AI32DivU
AI32RemS
AI32RemU
AI32And
AI32Or
AI32Xor
AI32Shl
AI32ShrS
AI32ShrU
AI32Rotl
AI32Rotr
AI64Clz
AI64Ctz
AI64Popcnt
AI64Add
AI64Sub
AI64Mul
AI64DivS
AI64DivU
AI64RemS
AI64RemU
AI64And
AI64Or
AI64Xor
AI64Shl
AI64ShrS
AI64ShrU
AI64Rotl
AI64Rotr
AF32Abs
AF32Neg
AF32Ceil
AF32Floor
AF32Trunc
AF32Nearest
AF32Sqrt
AF32Add
AF32Sub
AF32Mul
AF32Div
AF32Min
AF32Max
AF32Copysign
AF64Abs
AF64Neg
AF64Ceil
AF64Floor
AF64Trunc
AF64Nearest
AF64Sqrt
AF64Add
AF64Sub
AF64Mul
AF64Div
AF64Min
AF64Max
AF64Copysign
AI32WrapI64
AI32TruncSF32
AI32TruncUF32
AI32TruncSF64
AI32TruncUF64
AI64ExtendSI32
AI64ExtendUI32
AI64TruncSF32
AI64TruncUF32
AI64TruncSF64
AI64TruncUF64
AF32ConvertSI32
AF32ConvertUI32
AF32ConvertSI64
AF32ConvertUI64
AF32DemoteF64
AF64ConvertSI32
AF64ConvertUI32
AF64ConvertSI64
AF64ConvertUI64
AF64PromoteF32
AI32ReinterpretF32
AI64ReinterpretF64
AF32ReinterpretI32
AF64ReinterpretI64
// End of low-level WebAssembly instructions.
ARESUMEPOINT
// ACALLNORESUME is a call which is not followed by a resume point.
// It is allowed inside of WebAssembly blocks, whereas obj.ACALL is not.
// However, it is not allowed to switch goroutines while inside of an ACALLNORESUME call.
ACALLNORESUME
AMOVB
AMOVH
AMOVW
AMOVD
AWORD
ALAST
)
const (
REG_NONE = 0
)
const (
// globals
REG_PC_F = obj.RBaseWasm + iota
REG_PC_B
REG_SP // SP is currently 32-bit, until 64-bit memory operations are available
REG_CTXT
REG_g
// RET* are used by runtime.return0 and runtime.reflectcall. These functions pass return values in registers.
REG_RET0
REG_RET1
REG_RET2
REG_RET3
// locals
REG_R0
REG_R1
REG_R2
REG_R3
REG_R4
REG_R5
REG_R6
REG_R7
REG_R8
REG_R9
REG_R10
REG_R11
REG_R12
REG_R13
REG_R14
REG_R15
REG_F0
REG_F1
REG_F2
REG_F3
REG_F4
REG_F5
REG_F6
REG_F7
REG_F8
REG_F9
REG_F10
REG_F11
REG_F12
REG_F13
REG_F14
REG_F15
MAXREG
MINREG = REG_PC_F
REGSP = REG_SP
REGCTXT = REG_CTXT
REGG = REG_g
)

View File

@ -0,0 +1,189 @@
// Generated by stringer -i a.out.go -o anames.go -p wasm
// Do not edit.
package wasm
import "cmd/internal/obj"
var Anames = []string{
obj.A_ARCHSPECIFIC: "CallImport",
"Get",
"Set",
"Tee",
"Not",
"Unreachable",
"Nop",
"Block",
"Loop",
"If",
"Else",
"End",
"Br",
"BrIf",
"BrTable",
"Return",
"Call",
"CallIndirect",
"Drop",
"Select",
"I32Load",
"I64Load",
"F32Load",
"F64Load",
"I32Load8S",
"I32Load8U",
"I32Load16S",
"I32Load16U",
"I64Load8S",
"I64Load8U",
"I64Load16S",
"I64Load16U",
"I64Load32S",
"I64Load32U",
"I32Store",
"I64Store",
"F32Store",
"F64Store",
"I32Store8",
"I32Store16",
"I64Store8",
"I64Store16",
"I64Store32",
"CurrentMemory",
"GrowMemory",
"I32Const",
"I64Const",
"F32Const",
"F64Const",
"I32Eqz",
"I32Eq",
"I32Ne",
"I32LtS",
"I32LtU",
"I32GtS",
"I32GtU",
"I32LeS",
"I32LeU",
"I32GeS",
"I32GeU",
"I64Eqz",
"I64Eq",
"I64Ne",
"I64LtS",
"I64LtU",
"I64GtS",
"I64GtU",
"I64LeS",
"I64LeU",
"I64GeS",
"I64GeU",
"F32Eq",
"F32Ne",
"F32Lt",
"F32Gt",
"F32Le",
"F32Ge",
"F64Eq",
"F64Ne",
"F64Lt",
"F64Gt",
"F64Le",
"F64Ge",
"I32Clz",
"I32Ctz",
"I32Popcnt",
"I32Add",
"I32Sub",
"I32Mul",
"I32DivS",
"I32DivU",
"I32RemS",
"I32RemU",
"I32And",
"I32Or",
"I32Xor",
"I32Shl",
"I32ShrS",
"I32ShrU",
"I32Rotl",
"I32Rotr",
"I64Clz",
"I64Ctz",
"I64Popcnt",
"I64Add",
"I64Sub",
"I64Mul",
"I64DivS",
"I64DivU",
"I64RemS",
"I64RemU",
"I64And",
"I64Or",
"I64Xor",
"I64Shl",
"I64ShrS",
"I64ShrU",
"I64Rotl",
"I64Rotr",
"F32Abs",
"F32Neg",
"F32Ceil",
"F32Floor",
"F32Trunc",
"F32Nearest",
"F32Sqrt",
"F32Add",
"F32Sub",
"F32Mul",
"F32Div",
"F32Min",
"F32Max",
"F32Copysign",
"F64Abs",
"F64Neg",
"F64Ceil",
"F64Floor",
"F64Trunc",
"F64Nearest",
"F64Sqrt",
"F64Add",
"F64Sub",
"F64Mul",
"F64Div",
"F64Min",
"F64Max",
"F64Copysign",
"I32WrapI64",
"I32TruncSF32",
"I32TruncUF32",
"I32TruncSF64",
"I32TruncUF64",
"I64ExtendSI32",
"I64ExtendUI32",
"I64TruncSF32",
"I64TruncUF32",
"I64TruncSF64",
"I64TruncUF64",
"F32ConvertSI32",
"F32ConvertUI32",
"F32ConvertSI64",
"F32ConvertUI64",
"F32DemoteF64",
"F64ConvertSI32",
"F64ConvertUI32",
"F64ConvertSI64",
"F64ConvertUI64",
"F64PromoteF32",
"I32ReinterpretF32",
"I64ReinterpretF64",
"F32ReinterpretI32",
"F64ReinterpretI64",
"RESUMEPOINT",
"CALLNORESUME",
"MOVB",
"MOVH",
"MOVW",
"MOVD",
"WORD",
"LAST",
}

View File

@ -0,0 +1,934 @@
// Copyright 2018 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 wasm
import (
"bytes"
"cmd/internal/obj"
"cmd/internal/objabi"
"cmd/internal/sys"
"encoding/binary"
"fmt"
"io"
"math"
)
var Register = map[string]int16{
"PC_F": REG_PC_F,
"PC_B": REG_PC_B,
"SP": REG_SP,
"CTXT": REG_CTXT,
"g": REG_g,
"RET0": REG_RET0,
"RET1": REG_RET1,
"RET2": REG_RET2,
"RET3": REG_RET3,
"R0": REG_R0,
"R1": REG_R1,
"R2": REG_R2,
"R3": REG_R3,
"R4": REG_R4,
"R5": REG_R5,
"R6": REG_R6,
"R7": REG_R7,
"R8": REG_R8,
"R9": REG_R9,
"R10": REG_R10,
"R11": REG_R11,
"R12": REG_R12,
"R13": REG_R13,
"R14": REG_R14,
"R15": REG_R15,
"F0": REG_F0,
"F1": REG_F1,
"F2": REG_F2,
"F3": REG_F3,
"F4": REG_F4,
"F5": REG_F5,
"F6": REG_F6,
"F7": REG_F7,
"F8": REG_F8,
"F9": REG_F9,
"F10": REG_F10,
"F11": REG_F11,
"F12": REG_F12,
"F13": REG_F13,
"F14": REG_F14,
"F15": REG_F15,
}
var registerNames []string
func init() {
obj.RegisterRegister(MINREG, MAXREG, rconv)
obj.RegisterOpcode(obj.ABaseWasm, Anames)
registerNames = make([]string, MAXREG-MINREG)
for name, reg := range Register {
registerNames[reg-MINREG] = name
}
}
func rconv(r int) string {
return registerNames[r-MINREG]
}
var unaryDst = map[obj.As]bool{
ASet: true,
ATee: true,
ACall: true,
ACallIndirect: true,
ACallImport: true,
ABr: true,
ABrIf: true,
ABrTable: true,
AI32Store: true,
AI64Store: true,
AF32Store: true,
AF64Store: true,
AI32Store8: true,
AI32Store16: true,
AI64Store8: true,
AI64Store16: true,
AI64Store32: true,
ACALLNORESUME: true,
}
var Linkwasm = obj.LinkArch{
Arch: sys.ArchWasm,
Init: instinit,
Preprocess: preprocess,
Assemble: assemble,
UnaryDst: unaryDst,
}
var (
morestack *obj.LSym
morestackNoCtxt *obj.LSym
gcWriteBarrier *obj.LSym
sigpanic *obj.LSym
deferreturn *obj.LSym
jmpdefer *obj.LSym
)
const (
/* mark flags */
WasmImport = 1 << 0
)
func instinit(ctxt *obj.Link) {
morestack = ctxt.Lookup("runtime.morestack")
morestackNoCtxt = ctxt.Lookup("runtime.morestack_noctxt")
gcWriteBarrier = ctxt.Lookup("runtime.gcWriteBarrier")
sigpanic = ctxt.Lookup("runtime.sigpanic")
deferreturn = ctxt.Lookup("runtime.deferreturn")
jmpdefer = ctxt.Lookup(`"".jmpdefer`)
}
func preprocess(ctxt *obj.Link, s *obj.LSym, newprog obj.ProgAlloc) {
appendp := func(p *obj.Prog, as obj.As, args ...obj.Addr) *obj.Prog {
if p.As != obj.ANOP {
p2 := obj.Appendp(p, newprog)
p2.Pc = p.Pc
p = p2
}
p.As = as
switch len(args) {
case 0:
p.From = obj.Addr{}
p.To = obj.Addr{}
case 1:
if unaryDst[as] {
p.From = obj.Addr{}
p.To = args[0]
} else {
p.From = args[0]
p.To = obj.Addr{}
}
case 2:
p.From = args[0]
p.To = args[1]
default:
panic("bad args")
}
return p
}
framesize := s.Func.Text.To.Offset
if framesize < 0 {
panic("bad framesize")
}
s.Func.Args = s.Func.Text.To.Val.(int32)
s.Func.Locals = int32(framesize)
if s.Func.Text.From.Sym.Wrapper() {
// if g._panic != nil && g._panic.argp == FP {
// g._panic.argp = bottom-of-frame
// }
//
// MOVD g_panic(g), R0
// Get R0
// I64Eqz
// Not
// If
// Get SP
// I64ExtendUI32
// I64Const $framesize+8
// I64Add
// I64Load panic_argp(R0)
// I64Eq
// If
// MOVD SP, panic_argp(R0)
// End
// End
gpanic := obj.Addr{
Type: obj.TYPE_MEM,
Reg: REGG,
Offset: 4 * 8, // g_panic
}
panicargp := obj.Addr{
Type: obj.TYPE_MEM,
Reg: REG_R0,
Offset: 0, // panic.argp
}
p := s.Func.Text
p = appendp(p, AMOVD, gpanic, regAddr(REG_R0))
p = appendp(p, AGet, regAddr(REG_R0))
p = appendp(p, AI64Eqz)
p = appendp(p, ANot)
p = appendp(p, AIf)
p = appendp(p, AGet, regAddr(REG_SP))
p = appendp(p, AI64ExtendUI32)
p = appendp(p, AI64Const, constAddr(framesize+8))
p = appendp(p, AI64Add)
p = appendp(p, AI64Load, panicargp)
p = appendp(p, AI64Eq)
p = appendp(p, AIf)
p = appendp(p, AMOVD, regAddr(REG_SP), panicargp)
p = appendp(p, AEnd)
p = appendp(p, AEnd)
}
if framesize > 0 {
p := s.Func.Text
p = appendp(p, AGet, regAddr(REG_SP))
p = appendp(p, AI32Const, constAddr(framesize))
p = appendp(p, AI32Sub)
p = appendp(p, ASet, regAddr(REG_SP))
p.Spadj = int32(framesize)
}
// Introduce resume points for CALL instructions
// and collect other explicit resume points.
numResumePoints := 0
explicitBlockDepth := 0
pc := int64(0) // pc is only incremented when necessary, this avoids bloat of the BrTable instruction
var tableIdxs []uint64
tablePC := int64(0)
base := ctxt.PosTable.Pos(s.Func.Text.Pos).Base()
for p := s.Func.Text; p != nil; p = p.Link {
prevBase := base
base = ctxt.PosTable.Pos(p.Pos).Base()
switch p.As {
case ABlock, ALoop, AIf:
explicitBlockDepth++
case AEnd:
if explicitBlockDepth == 0 {
panic("End without block")
}
explicitBlockDepth--
case ARESUMEPOINT:
if explicitBlockDepth != 0 {
panic("RESUME can only be used on toplevel")
}
p.As = AEnd
for tablePC <= pc {
tableIdxs = append(tableIdxs, uint64(numResumePoints))
tablePC++
}
numResumePoints++
pc++
case obj.ACALL:
if explicitBlockDepth != 0 {
panic("CALL can only be used on toplevel, try CALLNORESUME instead")
}
appendp(p, ARESUMEPOINT)
}
p.Pc = pc
// Increase pc whenever some pc-value table needs a new entry. Don't increase it
// more often to avoid bloat of the BrTable instruction.
// The "base != prevBase" condition detects inlined instructions. They are an
// implicit call, so entering and leaving this section affects the stack trace.
if p.As == ACALLNORESUME || p.As == obj.ANOP || p.Spadj != 0 || base != prevBase {
pc++
}
}
tableIdxs = append(tableIdxs, uint64(numResumePoints))
s.Size = pc + 1
if !s.Func.Text.From.Sym.NoSplit() {
p := s.Func.Text
if framesize <= objabi.StackSmall {
// small stack: SP <= stackguard
// Get SP
// Get g
// I32WrapI64
// I32Load $stackguard0
// I32GtU
p = appendp(p, AGet, regAddr(REG_SP))
p = appendp(p, AGet, regAddr(REGG))
p = appendp(p, AI32WrapI64)
p = appendp(p, AI32Load, constAddr(2*int64(ctxt.Arch.PtrSize))) // G.stackguard0
p = appendp(p, AI32LeU)
} else {
// large stack: SP-framesize <= stackguard-StackSmall
// SP <= stackguard+(framesize-StackSmall)
// Get SP
// Get g
// I32WrapI64
// I32Load $stackguard0
// I32Const $(framesize-StackSmall)
// I32Add
// I32GtU
p = appendp(p, AGet, regAddr(REG_SP))
p = appendp(p, AGet, regAddr(REGG))
p = appendp(p, AI32WrapI64)
p = appendp(p, AI32Load, constAddr(2*int64(ctxt.Arch.PtrSize))) // G.stackguard0
p = appendp(p, AI32Const, constAddr(int64(framesize)-objabi.StackSmall))
p = appendp(p, AI32Add)
p = appendp(p, AI32LeU)
}
// TODO(neelance): handle wraparound case
p = appendp(p, AIf)
p = appendp(p, obj.ACALL, constAddr(0))
if s.Func.Text.From.Sym.NeedCtxt() {
p.To = obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_EXTERN, Sym: morestack}
} else {
p.To = obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_EXTERN, Sym: morestackNoCtxt}
}
p = appendp(p, AEnd)
}
// Add Block instructions for resume points and BrTable to jump to selected resume point.
if numResumePoints > 0 {
p := s.Func.Text
p = appendp(p, ALoop) // entryPointLoop, used to jump between basic blocks
for i := 0; i < numResumePoints+1; i++ {
p = appendp(p, ABlock)
}
p = appendp(p, AGet, regAddr(REG_PC_B)) // read next basic block from PC_B
p = appendp(p, ABrTable, obj.Addr{Val: tableIdxs})
p = appendp(p, AEnd) // end of Block
for p.Link != nil {
p = p.Link
}
p = appendp(p, AEnd) // end of entryPointLoop
p = appendp(p, obj.AUNDEF)
}
p := s.Func.Text
currentDepth := 0
blockDepths := make(map[*obj.Prog]int)
for p != nil {
switch p.As {
case ABlock, ALoop, AIf:
currentDepth++
blockDepths[p] = currentDepth
case AEnd:
currentDepth--
}
switch p.As {
case ABr, ABrIf:
if p.To.Type == obj.TYPE_BRANCH {
blockDepth, ok := blockDepths[p.To.Val.(*obj.Prog)]
if !ok {
panic("label not at block")
}
p.To = constAddr(int64(currentDepth - blockDepth))
}
case obj.AJMP:
jmp := *p
p.As = obj.ANOP
if jmp.To.Type == obj.TYPE_BRANCH {
// jump to basic block
p = appendp(p, AI32Const, constAddr(jmp.To.Val.(*obj.Prog).Pc))
p = appendp(p, ASet, regAddr(REG_PC_B)) // write next basic block to PC_B
p = appendp(p, ABr, constAddr(int64(currentDepth-1))) // jump to beginning of entryPointLoop
break
}
// reset PC_B to function entry
p = appendp(p, AI32Const, constAddr(0))
p = appendp(p, ASet, regAddr(REG_PC_B))
// low-level WebAssembly call to function
switch jmp.To.Type {
case obj.TYPE_MEM:
p = appendp(p, ACall, jmp.To)
case obj.TYPE_NONE:
// (target PC is on stack)
p = appendp(p, AI32WrapI64)
p = appendp(p, AI32Const, constAddr(16)) // only needs PC_F bits (16-31), PC_B bits (0-15) are zero
p = appendp(p, AI32ShrU)
p = appendp(p, ACallIndirect)
default:
panic("bad target for JMP")
}
p = appendp(p, AReturn)
case obj.ACALL, ACALLNORESUME:
call := *p
p.As = obj.ANOP
pcAfterCall := call.Link.Pc
if call.To.Sym == sigpanic {
pcAfterCall-- // sigpanic expects to be called without advancing the pc
}
// jmpdefer manipulates the return address on the stack so deferreturn gets called repeatedly.
// Model this in WebAssembly with a loop.
if call.To.Sym == deferreturn {
p = appendp(p, ALoop)
}
// SP -= 8
p = appendp(p, AGet, regAddr(REG_SP))
p = appendp(p, AI32Const, constAddr(8))
p = appendp(p, AI32Sub)
p = appendp(p, ASet, regAddr(REG_SP))
// write return address to Go stack
p = appendp(p, AGet, regAddr(REG_SP))
p = appendp(p, AI64Const, obj.Addr{
Type: obj.TYPE_ADDR,
Name: obj.NAME_EXTERN,
Sym: s, // PC_F
Offset: pcAfterCall, // PC_B
})
p = appendp(p, AI64Store, constAddr(0))
// reset PC_B to function entry
p = appendp(p, AI32Const, constAddr(0))
p = appendp(p, ASet, regAddr(REG_PC_B))
// low-level WebAssembly call to function
switch call.To.Type {
case obj.TYPE_MEM:
p = appendp(p, ACall, call.To)
case obj.TYPE_NONE:
// (target PC is on stack)
p = appendp(p, AI32WrapI64)
p = appendp(p, AI32Const, constAddr(16)) // only needs PC_F bits (16-31), PC_B bits (0-15) are zero
p = appendp(p, AI32ShrU)
p = appendp(p, ACallIndirect)
default:
panic("bad target for CALL")
}
// gcWriteBarrier has no return value, it never unwinds the stack
if call.To.Sym == gcWriteBarrier {
break
}
// jmpdefer removes the frame of deferreturn from the Go stack.
// However, its WebAssembly function still returns normally,
// so we need to return from deferreturn without removing its
// stack frame (no RET), because the frame is already gone.
if call.To.Sym == jmpdefer {
p = appendp(p, AReturn)
break
}
// return value of call is on the top of the stack, indicating whether to unwind the WebAssembly stack
p = appendp(p, AIf)
if call.As == ACALLNORESUME && call.To.Sym != sigpanic { // sigpanic unwinds the stack, but it never resumes
// trying to unwind WebAssembly stack but call has no resume point, terminate with error
p = appendp(p, obj.AUNDEF)
} else {
// unwinding WebAssembly stack to switch goroutine, return 1
p = appendp(p, AI32Const, constAddr(1))
p = appendp(p, AReturn)
}
p = appendp(p, AEnd)
// jump to before the call if jmpdefer has reset the return address to the call's PC
if call.To.Sym == deferreturn {
p = appendp(p, AGet, regAddr(REG_PC_B))
p = appendp(p, AI32Const, constAddr(call.Pc))
p = appendp(p, AI32Eq)
p = appendp(p, ABrIf, constAddr(0))
p = appendp(p, AEnd) // end of Loop
}
case obj.ARET:
ret := *p
p.As = obj.ANOP
if framesize > 0 {
// SP += framesize
p = appendp(p, AGet, regAddr(REG_SP))
p = appendp(p, AI32Const, constAddr(framesize))
p = appendp(p, AI32Add)
p = appendp(p, ASet, regAddr(REG_SP))
// TODO(neelance): This should theoretically set Spadj, but it only works without.
// p.Spadj = int32(-framesize)
}
if ret.To.Type == obj.TYPE_MEM {
// reset PC_B to function entry
p = appendp(p, AI32Const, constAddr(0))
p = appendp(p, ASet, regAddr(REG_PC_B))
// low-level WebAssembly call to function
p = appendp(p, ACall, ret.To)
p = appendp(p, AReturn)
break
}
// read return PC_F from Go stack
p = appendp(p, AGet, regAddr(REG_SP))
p = appendp(p, AI32Load16U, constAddr(2))
p = appendp(p, ASet, regAddr(REG_PC_F))
// read return PC_B from Go stack
p = appendp(p, AGet, regAddr(REG_SP))
p = appendp(p, AI32Load16U, constAddr(0))
p = appendp(p, ASet, regAddr(REG_PC_B))
// SP += 8
p = appendp(p, AGet, regAddr(REG_SP))
p = appendp(p, AI32Const, constAddr(8))
p = appendp(p, AI32Add)
p = appendp(p, ASet, regAddr(REG_SP))
// not switching goroutine, return 0
p = appendp(p, AI32Const, constAddr(0))
p = appendp(p, AReturn)
}
p = p.Link
}
p = s.Func.Text
for p != nil {
switch p.From.Name {
case obj.NAME_AUTO:
p.From.Offset += int64(framesize)
case obj.NAME_PARAM:
p.From.Reg = REG_SP
p.From.Offset += int64(framesize) + 8 // parameters are after the frame and the 8-byte return address
}
switch p.To.Name {
case obj.NAME_AUTO:
p.To.Offset += int64(framesize)
case obj.NAME_PARAM:
p.To.Reg = REG_SP
p.To.Offset += int64(framesize) + 8 // parameters are after the frame and the 8-byte return address
}
switch p.As {
case AGet:
if p.From.Type == obj.TYPE_ADDR {
get := *p
p.As = obj.ANOP
switch get.From.Name {
case obj.NAME_EXTERN:
p = appendp(p, AI64Const, get.From)
case obj.NAME_AUTO, obj.NAME_PARAM:
p = appendp(p, AGet, regAddr(get.From.Reg))
if get.From.Reg == REG_SP {
p = appendp(p, AI64ExtendUI32)
}
if get.From.Offset != 0 {
p = appendp(p, AI64Const, constAddr(get.From.Offset))
p = appendp(p, AI64Add)
}
default:
panic("bad Get: invalid name")
}
}
case AI32Load, AI64Load, AF32Load, AF64Load, AI32Load8S, AI32Load8U, AI32Load16S, AI32Load16U, AI64Load8S, AI64Load8U, AI64Load16S, AI64Load16U, AI64Load32S, AI64Load32U:
if p.From.Type == obj.TYPE_MEM {
as := p.As
from := p.From
p.As = AGet
p.From = regAddr(from.Reg)
if from.Reg != REG_SP {
p = appendp(p, AI32WrapI64)
}
p = appendp(p, as, constAddr(from.Offset))
}
case AMOVB, AMOVH, AMOVW, AMOVD:
mov := *p
p.As = obj.ANOP
var loadAs obj.As
var storeAs obj.As
switch mov.As {
case AMOVB:
loadAs = AI64Load8U
storeAs = AI64Store8
case AMOVH:
loadAs = AI64Load16U
storeAs = AI64Store16
case AMOVW:
loadAs = AI64Load32U
storeAs = AI64Store32
case AMOVD:
loadAs = AI64Load
storeAs = AI64Store
}
appendValue := func() {
switch mov.From.Type {
case obj.TYPE_CONST:
p = appendp(p, AI64Const, constAddr(mov.From.Offset))
case obj.TYPE_ADDR:
switch mov.From.Name {
case obj.NAME_NONE, obj.NAME_PARAM, obj.NAME_AUTO:
p = appendp(p, AGet, regAddr(mov.From.Reg))
if mov.From.Reg == REG_SP {
p = appendp(p, AI64ExtendUI32)
}
p = appendp(p, AI64Const, constAddr(mov.From.Offset))
p = appendp(p, AI64Add)
case obj.NAME_EXTERN:
p = appendp(p, AI64Const, mov.From)
default:
panic("bad name for MOV")
}
case obj.TYPE_REG:
p = appendp(p, AGet, mov.From)
if mov.From.Reg == REG_SP {
p = appendp(p, AI64ExtendUI32)
}
case obj.TYPE_MEM:
p = appendp(p, AGet, regAddr(mov.From.Reg))
if mov.From.Reg != REG_SP {
p = appendp(p, AI32WrapI64)
}
p = appendp(p, loadAs, constAddr(mov.From.Offset))
default:
panic("bad MOV type")
}
}
switch mov.To.Type {
case obj.TYPE_REG:
appendValue()
if mov.To.Reg == REG_SP {
p = appendp(p, AI32WrapI64)
}
p = appendp(p, ASet, mov.To)
case obj.TYPE_MEM:
switch mov.To.Name {
case obj.NAME_NONE, obj.NAME_PARAM:
p = appendp(p, AGet, regAddr(mov.To.Reg))
if mov.To.Reg != REG_SP {
p = appendp(p, AI32WrapI64)
}
case obj.NAME_EXTERN:
p = appendp(p, AI32Const, obj.Addr{Type: obj.TYPE_ADDR, Name: obj.NAME_EXTERN, Sym: mov.To.Sym})
default:
panic("bad MOV name")
}
appendValue()
p = appendp(p, storeAs, constAddr(mov.To.Offset))
default:
panic("bad MOV type")
}
case ACallImport:
p.As = obj.ANOP
p = appendp(p, AGet, regAddr(REG_SP))
p = appendp(p, ACall, obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_EXTERN, Sym: s})
p.Mark = WasmImport
}
p = p.Link
}
}
func constAddr(value int64) obj.Addr {
return obj.Addr{Type: obj.TYPE_CONST, Offset: value}
}
func regAddr(reg int16) obj.Addr {
return obj.Addr{Type: obj.TYPE_REG, Reg: reg}
}
func assemble(ctxt *obj.Link, s *obj.LSym, newprog obj.ProgAlloc) {
w := new(bytes.Buffer)
// Function starts with declaration of locals: numbers and types.
switch s.Name {
case "memchr":
writeUleb128(w, 1) // number of sets of locals
writeUleb128(w, 3) // number of locals
w.WriteByte(0x7F) // i32
case "memcmp":
writeUleb128(w, 1) // number of sets of locals
writeUleb128(w, 2) // number of locals
w.WriteByte(0x7F) // i32
default:
writeUleb128(w, 2) // number of sets of locals
writeUleb128(w, 16) // number of locals
w.WriteByte(0x7E) // i64
writeUleb128(w, 16) // number of locals
w.WriteByte(0x7C) // f64
}
for p := s.Func.Text; p != nil; p = p.Link {
switch p.As {
case AGet:
if p.From.Type != obj.TYPE_REG {
panic("bad Get: argument is not a register")
}
reg := p.From.Reg
switch {
case reg >= REG_PC_F && reg <= REG_RET3:
w.WriteByte(0x23) // get_global
writeUleb128(w, uint64(reg-REG_PC_F))
case reg >= REG_R0 && reg <= REG_F15:
w.WriteByte(0x20) // get_local
writeUleb128(w, uint64(reg-REG_R0))
default:
panic("bad Get: invalid register")
}
continue
case ASet:
if p.To.Type != obj.TYPE_REG {
panic("bad Set: argument is not a register")
}
reg := p.To.Reg
switch {
case reg >= REG_PC_F && reg <= REG_RET3:
w.WriteByte(0x24) // set_global
writeUleb128(w, uint64(reg-REG_PC_F))
case reg >= REG_R0 && reg <= REG_F15:
if p.Link.As == AGet && p.Link.From.Reg == reg {
w.WriteByte(0x22) // tee_local
p = p.Link
} else {
w.WriteByte(0x21) // set_local
}
writeUleb128(w, uint64(reg-REG_R0))
default:
panic("bad Set: invalid register")
}
continue
case ATee:
if p.To.Type != obj.TYPE_REG {
panic("bad Tee: argument is not a register")
}
reg := p.To.Reg
switch {
case reg >= REG_R0 && reg <= REG_F15:
w.WriteByte(0x22) // tee_local
writeUleb128(w, uint64(reg-REG_R0))
default:
panic("bad Tee: invalid register")
}
continue
case ANot:
w.WriteByte(0x45) // i32.eqz
continue
case obj.AUNDEF:
w.WriteByte(0x00) // unreachable
continue
case obj.ANOP, obj.ATEXT, obj.AFUNCDATA, obj.APCDATA:
// ignore
continue
}
switch {
case p.As < AUnreachable || p.As > AF64ReinterpretI64:
panic(fmt.Sprintf("unexpected assembler op: %s", p.As))
case p.As < AEnd:
w.WriteByte(byte(p.As - AUnreachable + 0x00))
case p.As < ADrop:
w.WriteByte(byte(p.As - AEnd + 0x0B))
case p.As < AI32Load:
w.WriteByte(byte(p.As - ADrop + 0x1A))
default:
w.WriteByte(byte(p.As - AI32Load + 0x28))
}
switch p.As {
case ABlock, ALoop, AIf:
if p.From.Offset != 0 {
// block type, rarely used, e.g. for code compiled with emscripten
w.WriteByte(0x80 - byte(p.From.Offset))
continue
}
w.WriteByte(0x40)
case ABr, ABrIf:
if p.To.Type != obj.TYPE_CONST {
panic("bad Br/BrIf")
}
writeUleb128(w, uint64(p.To.Offset))
case ABrTable:
idxs := p.To.Val.([]uint64)
writeUleb128(w, uint64(len(idxs)-1))
for _, idx := range idxs {
writeUleb128(w, idx)
}
case ACall:
switch p.To.Type {
case obj.TYPE_CONST:
writeUleb128(w, uint64(p.To.Offset))
case obj.TYPE_MEM:
if p.To.Name != obj.NAME_EXTERN && p.To.Name != obj.NAME_STATIC {
fmt.Println(p.To)
panic("bad name for Call")
}
r := obj.Addrel(s)
r.Off = int32(w.Len())
r.Type = objabi.R_CALL
if p.Mark&WasmImport != 0 {
r.Type = objabi.R_WASMIMPORT
}
r.Sym = p.To.Sym
default:
panic("bad type for Call")
}
case ACallIndirect:
writeUleb128(w, uint64(p.To.Offset))
w.WriteByte(0x00) // reserved value
case AI32Const, AI64Const:
if p.From.Name == obj.NAME_EXTERN {
r := obj.Addrel(s)
r.Off = int32(w.Len())
r.Type = objabi.R_ADDR
r.Sym = p.From.Sym
r.Add = p.From.Offset
break
}
writeSleb128(w, p.From.Offset)
case AF64Const:
b := make([]byte, 8)
binary.LittleEndian.PutUint64(b, math.Float64bits(p.From.Val.(float64)))
w.Write(b)
case AI32Load, AI64Load, AF32Load, AF64Load, AI32Load8S, AI32Load8U, AI32Load16S, AI32Load16U, AI64Load8S, AI64Load8U, AI64Load16S, AI64Load16U, AI64Load32S, AI64Load32U:
if p.From.Offset < 0 {
panic("negative offset for *Load")
}
if p.From.Type != obj.TYPE_CONST {
panic("bad type for *Load")
}
writeUleb128(w, align(p.As))
writeUleb128(w, uint64(p.From.Offset))
case AI32Store, AI64Store, AF32Store, AF64Store, AI32Store8, AI32Store16, AI64Store8, AI64Store16, AI64Store32:
if p.To.Offset < 0 {
panic("negative offset")
}
writeUleb128(w, align(p.As))
writeUleb128(w, uint64(p.To.Offset))
case ACurrentMemory, AGrowMemory:
w.WriteByte(0x00)
}
}
w.WriteByte(0x0b) // end
s.P = w.Bytes()
}
func align(as obj.As) uint64 {
switch as {
case AI32Load8S, AI32Load8U, AI64Load8S, AI64Load8U, AI32Store8, AI64Store8:
return 0
case AI32Load16S, AI32Load16U, AI64Load16S, AI64Load16U, AI32Store16, AI64Store16:
return 1
case AI32Load, AF32Load, AI64Load32S, AI64Load32U, AI32Store, AF32Store, AI64Store32:
return 2
case AI64Load, AF64Load, AI64Store, AF64Store:
return 3
default:
panic("align: bad op")
}
}
func writeUleb128(w io.ByteWriter, v uint64) {
more := true
for more {
c := uint8(v & 0x7f)
v >>= 7
more = v != 0
if more {
c |= 0x80
}
w.WriteByte(c)
}
}
func writeSleb128(w io.ByteWriter, v int64) {
more := true
for more {
c := uint8(v & 0x7f)
s := uint8(v & 0x40)
v >>= 7
more = !((v == 0 && s == 0) || (v == -1 && s != 0))
if more {
c |= 0x80
}
w.WriteByte(c)
}
}

View File

@ -40,6 +40,7 @@ const (
Hdarwin Hdarwin
Hdragonfly Hdragonfly
Hfreebsd Hfreebsd
Hjs
Hlinux Hlinux
Hnacl Hnacl
Hnetbsd Hnetbsd
@ -57,6 +58,8 @@ func (h *HeadType) Set(s string) error {
*h = Hdragonfly *h = Hdragonfly
case "freebsd": case "freebsd":
*h = Hfreebsd *h = Hfreebsd
case "js":
*h = Hjs
case "linux", "android": case "linux", "android":
*h = Hlinux *h = Hlinux
case "nacl": case "nacl":
@ -85,6 +88,8 @@ func (h *HeadType) String() string {
return "dragonfly" return "dragonfly"
case Hfreebsd: case Hfreebsd:
return "freebsd" return "freebsd"
case Hjs:
return "js"
case Hlinux: case Hlinux:
return "linux" return "linux"
case Hnacl: case Hnacl:

View File

@ -193,6 +193,9 @@ const (
// R_ADDRCUOFF resolves to a pointer-sized offset from the start of the // R_ADDRCUOFF resolves to a pointer-sized offset from the start of the
// symbol's DWARF compile unit. // symbol's DWARF compile unit.
R_ADDRCUOFF R_ADDRCUOFF
// R_WASMIMPORT resolves to the index of the WebAssembly function import.
R_WASMIMPORT
) )
// IsDirectJump returns whether r is a relocation for a direct jump. // IsDirectJump returns whether r is a relocation for a direct jump.

View File

@ -21,6 +21,7 @@ const (
MIPS64 MIPS64
PPC64 PPC64
S390X S390X
Wasm
) )
// Arch represents an individual architecture. // Arch represents an individual architecture.
@ -160,6 +161,15 @@ var ArchS390X = &Arch{
MinLC: 2, MinLC: 2,
} }
var ArchWasm = &Arch{
Name: "wasm",
Family: Wasm,
ByteOrder: binary.LittleEndian,
PtrSize: 8,
RegSize: 8,
MinLC: 1,
}
var Archs = [...]*Arch{ var Archs = [...]*Arch{
Arch386, Arch386,
ArchAMD64, ArchAMD64,
@ -173,4 +183,5 @@ var Archs = [...]*Arch{
ArchPPC64, ArchPPC64,
ArchPPC64LE, ArchPPC64LE,
ArchS390X, ArchS390X,
ArchWasm,
} }