Repository: tetratelabs/wazero
Pull request #1985 (2024-01-30 21:57:09)

wazevo(amd64): support for DWARF


Patch:
diff --git a/internal/engine/wazevo/backend/isa/amd64/instr.go b/internal/engine/wazevo/backend/isa/amd64/instr.go
index e1194797..3a2589fe 100644
--- a/internal/engine/wazevo/backend/isa/amd64/instr.go
+++ b/internal/engine/wazevo/backend/isa/amd64/instr.go
@@ -329,7 +329,6 @@ func (i *instruction) Uses(regs *[]regalloc.VReg) []regalloc.VReg {
 			panic(fmt.Sprintf("BUG: invalid operand: %s", i))
 		}
 		*regs = append(*regs, raxVReg)
-
 	default:
 		panic(fmt.Sprintf("BUG: invalid useKind %s for %s", uk, i))
 	}
@@ -671,6 +670,10 @@ const (
 	// the destination register and takes no inputs.
 	zeros
 
+	// sourceOffsetInfo is a dummy instruction to emit source offset info.
+	// The existence of this instruction does not affect the execution.
+	sourceOffsetInfo
+
 	instrMax
 )
 
@@ -908,6 +911,16 @@ func (i *instruction) asGprToXmm(op sseOpcode, rm operand, rd regalloc.VReg, _64
 	return i
 }
 
+func (i *instruction) asEmitSourceOffsetInfo(l ssa.SourceOffset) *instruction {
+	i.kind = sourceOffsetInfo
+	i.u1 = uint64(l)
+	return i
+}
+
+func (i *instruction) sourceOffsetInfo() ssa.SourceOffset {
+	return ssa.SourceOffset(i.u1)
+}
+
 func (i *instruction) asMovRM(rm regalloc.VReg, rd operand, size byte) *instruction {
 	if rd.kind != operandKindMem {
 		panic("BUG")
@@ -1745,38 +1758,39 @@ const (
 )
 
 var defKinds = [instrMax]defKind{
-	nop0:            defKindNone,
-	div:             defKindRaxRdx,
-	signExtendData:  defKindRdx,
-	ret:             defKindNone,
-	movRR:           defKindOp2,
-	movRM:           defKindNone,
-	xmmMovRM:        defKindNone,
-	aluRmiR:         defKindNone,
-	shiftR:          defKindNone,
-	imm:             defKindOp2,
-	unaryRmR:        defKindOp2,
-	xmmUnaryRmR:     defKindOp2,
-	xmmUnaryRmRImm:  defKindOp2,
-	xmmCmpRmR:       defKindOp2,
-	xmmRmR:          defKindNone,
-	mov64MR:         defKindOp2,
-	movsxRmR:        defKindOp2,
-	movzxRmR:        defKindOp2,
-	gprToXmm:        defKindOp2,
-	cmove:           defKindNone,
-	call:            defKindCall,
-	callIndirect:    defKindCall,
-	ud2:             defKindNone,
-	jmp:             defKindNone,
-	jmpIf:           defKindNone,
-	jmpTableIsland:  defKindNone,
-	cmpRmiR:         defKindNone,
-	exitSequence:    defKindNone,
-	lea:             defKindOp2,
-	v128ConstIsland: defKindNone,
-	setcc:           defKindOp2,
-	zeros:           defKindOp2,
+	nop0:             defKindNone,
+	div:              defKindRaxRdx,
+	signExtendData:   defKindRdx,
+	ret:              defKindNone,
+	movRR:            defKindOp2,
+	movRM:            defKindNone,
+	xmmMovRM:         defKindNone,
+	aluRmiR:          defKindNone,
+	shiftR:           defKindNone,
+	imm:              defKindOp2,
+	unaryRmR:         defKindOp2,
+	xmmUnaryRmR:      defKindOp2,
+	xmmUnaryRmRImm:   defKindOp2,
+	xmmCmpRmR:        defKindOp2,
+	xmmRmR:           defKindNone,
+	mov64MR:          defKindOp2,
+	movsxRmR:         defKindOp2,
+	movzxRmR:         defKindOp2,
+	gprToXmm:         defKindOp2,
+	cmove:            defKindNone,
+	call:             defKindCall,
+	callIndirect:     defKindCall,
+	ud2:              defKindNone,
+	jmp:              defKindNone,
+	jmpIf:            defKindNone,
+	jmpTableIsland:   defKindNone,
+	cmpRmiR:          defKindNone,
+	exitSequence:     defKindNone,
+	lea:              defKindOp2,
+	v128ConstIsland:  defKindNone,
+	setcc:            defKindOp2,
+	zeros:            defKindOp2,
+	sourceOffsetInfo: defKindNone,
 }
 
 // String implements fmt.Stringer.
@@ -1815,38 +1829,39 @@ const (
 )
 
 var useKinds = [instrMax]useKind{
-	nop0:            useKindNone,
-	div:             useKindOp1Rax,
-	signExtendData:  useKindRax,
-	ret:             useKindNone,
-	movRR:           useKindOp1,
-	movRM:           useKindOp1RegOp2,
-	xmmMovRM:        useKindOp1RegOp2,
-	cmove:           useKindOp1Op2Reg,
-	aluRmiR:         useKindOp1Op2Reg,
-	shiftR:          useKindOp1Op2Reg,
-	imm:             useKindNone,
-	unaryRmR:        useKindOp1,
-	xmmUnaryRmR:     useKindOp1,
-	xmmUnaryRmRImm:  useKindOp1,
-	xmmCmpRmR:       useKindOp1Op2Reg,
-	xmmRmR:          useKindOp1Op2Reg,
-	mov64MR:         useKindOp1,
-	movzxRmR:        useKindOp1,
-	movsxRmR:        useKindOp1,
-	gprToXmm:        useKindOp1,
-	call:            useKindCall,
-	callIndirect:    useKindCallInd,
-	ud2:             useKindNone,
-	jmpIf:           useKindOp1,
-	jmp:             useKindOp1,
-	cmpRmiR:         useKindOp1Op2Reg,
-	exitSequence:    useKindOp1,
-	lea:             useKindOp1,
-	v128ConstIsland: useKindNone,
-	jmpTableIsland:  useKindNone,
-	setcc:           useKindNone,
-	zeros:           useKindNone,
+	nop0:             useKindNone,
+	div:              useKindOp1Rax,
+	signExtendData:   useKindRax,
+	ret:              useKindNone,
+	movRR:            useKindOp1,
+	movRM:            useKindOp1RegOp2,
+	xmmMovRM:         useKindOp1RegOp2,
+	cmove:            useKindOp1Op2Reg,
+	aluRmiR:          useKindOp1Op2Reg,
+	shiftR:           useKindOp1Op2Reg,
+	imm:              useKindNone,
+	unaryRmR:         useKindOp1,
+	xmmUnaryRmR:      useKindOp1,
+	xmmUnaryRmRImm:   useKindOp1,
+	xmmCmpRmR:        useKindOp1Op2Reg,
+	xmmRmR:           useKindOp1Op2Reg,
+	mov64MR:          useKindOp1,
+	movzxRmR:         useKindOp1,
+	movsxRmR:         useKindOp1,
+	gprToXmm:         useKindOp1,
+	call:             useKindCall,
+	callIndirect:     useKindCallInd,
+	ud2:              useKindNone,
+	jmpIf:            useKindOp1,
+	jmp:              useKindOp1,
+	cmpRmiR:          useKindOp1Op2Reg,
+	exitSequence:     useKindOp1,
+	lea:              useKindOp1,
+	v128ConstIsland:  useKindNone,
+	jmpTableIsland:   useKindNone,
+	setcc:            useKindNone,
+	zeros:            useKindNone,
+	sourceOffsetInfo: useKindNone,
 }
 
 func (u useKind) String() string {
diff --git a/internal/engine/wazevo/backend/isa/amd64/instr_encoding.go b/internal/engine/wazevo/backend/isa/amd64/instr_encoding.go
index b088d978..a213a642 100644
--- a/internal/engine/wazevo/backend/isa/amd64/instr_encoding.go
+++ b/internal/engine/wazevo/backend/isa/amd64/instr_encoding.go
@@ -11,7 +11,7 @@ import (
 
 func (i *instruction) encode(c backend.Compiler) (needsLabelResolution bool) {
 	switch i.kind {
-	case nop0:
+	case nop0, sourceOffsetInfo:
 	case ret:
 		encodeRet(c)
 	case imm:
diff --git a/internal/engine/wazevo/backend/isa/amd64/machine.go b/internal/engine/wazevo/backend/isa/amd64/machine.go
index de75a519..910501bb 100644
--- a/internal/engine/wazevo/backend/isa/amd64/machine.go
+++ b/internal/engine/wazevo/backend/isa/amd64/machine.go
@@ -270,6 +270,11 @@ func (m *machine) LowerConditionalBranch(b *ssa.Instruction) {
 
 // LowerInstr implements backend.Machine.
 func (m *machine) LowerInstr(instr *ssa.Instruction) {
+	if l := instr.SourceOffset(); l.Valid() {
+		info := m.allocateInstr().asEmitSourceOffsetInfo(l)
+		m.insert(info)
+	}
+
 	switch op := instr.Opcode(); op {
 	case ssa.OpcodeBrz, ssa.OpcodeBrnz, ssa.OpcodeJump, ssa.OpcodeBrTable:
 		panic("BUG: branching instructions are handled by LowerBranches")
@@ -1212,11 +1217,15 @@ func (m *machine) Encode(context.Context) {
 		pos.BinaryOffset = offset
 		for cur := pos.Begin; cur != pos.End.next; cur = cur.next {
 			offset := int64(len(*bufPtr))
-			if cur.kind == nop0 {
+
+			switch cur.kind {
+			case nop0:
 				l := cur.nop0Label()
 				if pos, ok := ectx.LabelPositions[l]; ok {
 					pos.BinaryOffset = offset
 				}
+			case sourceOffsetInfo:
+				m.c.AddSourceOffsetInfo(offset, cur.sourceOffsetInfo())
 			}
 
 			needLabelResolution := cur.encode(m.c)
diff --git a/internal/engine/wazevo/call_engine.go b/internal/engine/wazevo/call_engine.go
index a302d390..6b97ac32 100644
--- a/internal/engine/wazevo/call_engine.go
+++ b/internal/engine/wazevo/call_engine.go
@@ -281,7 +281,7 @@ func (c *callEngine) callWithStack(ctx context.Context, paramResultStack []uint6
 		case wazevoapi.ExitCodeTableGrow:
 			mod := c.callerModuleInstance()
 			s := goCallStackView(c.execCtx.stackPointerBeforeGoCall)
-			tableIndex, num, ref := s[0], uint32(s[1]), uintptr(s[2])
+			tableIndex, num, ref := uint32(s[0]), uint32(s[1]), uintptr(s[2])
 			table := mod.Tables[tableIndex]
 			s[0] = uint64(uint32(int32(table.Grow(num, ref))))
 			c.execCtx.exitCode = wazevoapi.ExitCodeOK
@@ -344,20 +344,20 @@ func (c *callEngine) callWithStack(ctx context.Context, paramResultStack []uint6
 				uintptr(unsafe.Pointer(c.execCtx.stackPointerBeforeGoCall)), c.execCtx.framePointerBeforeGoCall)
 		case wazevoapi.ExitCodeCallListenerBefore:
 			stack := goCallStackView(c.execCtx.stackPointerBeforeGoCall)
-			index := stack[0]
+			index := wasm.Index(stack[0])
 			mod := c.callerModuleInstance()
 			listener := mod.Engine.(*moduleEngine).listeners[index]
-			def := mod.Source.FunctionDefinition(wasm.Index(index))
+			def := mod.Source.FunctionDefinition(index)
 			listener.Before(ctx, mod, def, stack[1:], c.stackIterator(false))
 			c.execCtx.exitCode = wazevoapi.ExitCodeOK
 			afterGoFunctionCallEntrypoint(c.execCtx.goCallReturnAddress, c.execCtxPtr,
 				uintptr(unsafe.Pointer(c.execCtx.stackPointerBeforeGoCall)), c.execCtx.framePointerBeforeGoCall)
 		case wazevoapi.ExitCodeCallListenerAfter:
 			stack := goCallStackView(c.execCtx.stackPointerBeforeGoCall)
-			index := stack[0]
+			index := wasm.Index(stack[0])
 			mod := c.callerModuleInstance()
 			listener := mod.Engine.(*moduleEngine).listeners[index]
-			def := mod.Source.FunctionDefinition(wasm.Index(index))
+			def := mod.Source.FunctionDefinition(index)
 			listener.After(ctx, mod, def, stack[1:])
 			c.execCtx.exitCode = wazevoapi.ExitCodeOK
 			afterGoFunctionCallEntrypoint(c.execCtx.goCallReturnAddress, c.execCtxPtr,
@@ -375,8 +375,8 @@ func (c *callEngine) callWithStack(ctx context.Context, paramResultStack []uint6
 		case wazevoapi.ExitCodeRefFunc:
 			mod := c.callerModuleInstance()
 			s := goCallStackView(c.execCtx.stackPointerBeforeGoCall)
-			funcIndex := s[0]
-			ref := mod.Engine.FunctionInstanceReference(wasm.Index(funcIndex))
+			funcIndex := wasm.Index(s[0])
+			ref := mod.Engine.FunctionInstanceReference(funcIndex)
 			s[0] = uint64(ref)
 			c.execCtx.exitCode = wazevoapi.ExitCodeOK
 			afterGoFunctionCallEntrypoint(c.execCtx.goCallReturnAddress, c.execCtxPtr,


Test patch:
diff --git a/internal/integration_test/engine/adhoc_test.go b/internal/integration_test/engine/adhoc_test.go
index 703b96be..6d8b1697 100644
--- a/internal/integration_test/engine/adhoc_test.go
+++ b/internal/integration_test/engine/adhoc_test.go
@@ -66,13 +66,13 @@ var tests = map[string]testCase{
 	"many params many results / doubler":                               {f: testManyParamsResultsDoubler},
 	"many params many results / doubler / listener":                    {f: testManyParamsResultsDoublerListener},
 	"many params many results / call_many_consts":                      {f: testManyParamsResultsCallManyConsts},
-	"many params many results / call_many_consts / listener":           {f: testManyParamsResultsCallManyConstsListener},
+	"many params many results / call_many_consts / listener":           {f: testManyParamsResultsCallManyConstsListener, wazevoSkip: runtime.GOARCH == "amd64"},
 	"many params many results / swapper":                               {f: testManyParamsResultsSwapper},
 	"many params many results / swapper / listener":                    {f: testManyParamsResultsSwapperListener},
 	"many params many results / main":                                  {f: testManyParamsResultsMain},
-	"many params many results / main / listener":                       {f: testManyParamsResultsMainListener},
+	"many params many results / main / listener":                       {f: testManyParamsResultsMainListener, wazevoSkip: runtime.GOARCH == "amd64"},
 	"many params many results / call_many_consts_and_pick_last_vector": {f: testManyParamsResultsCallManyConstsAndPickLastVector},
-	"many params many results / call_many_consts_and_pick_last_vector / listener": {f: testManyParamsResultsCallManyConstsAndPickLastVectorListener},
+	"many params many results / call_many_consts_and_pick_last_vector / listener": {f: testManyParamsResultsCallManyConstsAndPickLastVectorListener, wazevoSkip: runtime.GOARCH == "amd64"},
 }
 
 func TestEngineCompiler(t *testing.T) {
@@ -94,7 +94,7 @@ const i32, i64, f32, f64, v128 = wasm.ValueTypeI32, wasm.ValueTypeI64, wasm.Valu
 var memoryCapacityPages = uint32(2)
 
 func TestEngineWazevo(t *testing.T) {
-	if runtime.GOARCH != "arm64" {
+	if !platform.CompilerSupported() {
 		t.Skip()
 	}
 	config := opt.NewRuntimeConfigOptimizingCompiler()
diff --git a/internal/integration_test/engine/dwarf_test.go b/internal/integration_test/engine/dwarf_test.go
index 561ac5c7..b477013f 100644
--- a/internal/integration_test/engine/dwarf_test.go
+++ b/internal/integration_test/engine/dwarf_test.go
@@ -3,7 +3,6 @@ package adhoc
 import (
 	"bufio"
 	_ "embed"
-	"runtime"
 	"strings"
 	"testing"
 
@@ -34,7 +33,7 @@ func TestEngineInterpreter_DWARF(t *testing.T) {
 }
 
 func TestEngineWazevo_DWARF(t *testing.T) {
-	if runtime.GOARCH != "arm64" {
+	if !platform.CompilerSupported() {
 		t.Skip()
 	}
 	config := opt.NewRuntimeConfigOptimizingCompiler()
diff --git a/internal/integration_test/engine/hammer_test.go b/internal/integration_test/engine/hammer_test.go
index 4e70ad36..3189550a 100644
--- a/internal/integration_test/engine/hammer_test.go
+++ b/internal/integration_test/engine/hammer_test.go
@@ -2,7 +2,6 @@ package adhoc
 
 import (
 	"context"
-	"runtime"
 	"sync"
 	"testing"
 
@@ -33,7 +32,7 @@ func TestEngineInterpreter_hammer(t *testing.T) {
 }
 
 func TestEngineWazevo_hammer(t *testing.T) {
-	if runtime.GOARCH != "arm64" {
+	if !platform.CompilerSupported() {
 		t.Skip()
 	}
 	c := opt.NewRuntimeConfigOptimizingCompiler()
diff --git a/internal/integration_test/engine/memleak_test.go b/internal/integration_test/engine/memleak_test.go
index 7b1a627d..8401b997 100644
--- a/internal/integration_test/engine/memleak_test.go
+++ b/internal/integration_test/engine/memleak_test.go
@@ -11,6 +11,7 @@ import (
 
 	"github.com/tetratelabs/wazero"
 	"github.com/tetratelabs/wazero/experimental/opt"
+	"github.com/tetratelabs/wazero/internal/platform"
 )
 
 func TestMemoryLeak(t *testing.T) {
@@ -18,6 +19,10 @@ func TestMemoryLeak(t *testing.T) {
 		t.Skip("skipping memory leak test in short mode.")
 	}
 
+	if !platform.CompilerSupported() {
+		t.Skip()
+	}
+
 	for _, tc := range []struct {
 		name     string
 		isWazevo bool
@@ -27,10 +32,6 @@ func TestMemoryLeak(t *testing.T) {
 	} {
 		tc := tc
 
-		if tc.isWazevo && runtime.GOARCH != "arm64" {
-			t.Skip("skipping wazevo memory leak test on non-arm64.")
-		}
-
 		t.Run(tc.name, func(t *testing.T) {
 			duration := 5 * time.Second
 			t.Logf("running memory leak test for %s", duration)