Repository: tetratelabs/wazero
Pull request #1739 (2023-09-29 16:54:17)

wazevo: passes simd_lane spectests
continues #1496 

evacchi: not finished, still needs some encoding tests, cleanup, refactoring of some ugly-named functions, and docs. But the tests pass!

Patch:
diff --git a/.github/workflows/examples.yaml b/.github/workflows/examples.yaml
index 9e725288..5607bef9 100644
--- a/.github/workflows/examples.yaml
+++ b/.github/workflows/examples.yaml
@@ -18,7 +18,7 @@ on:
 env:
   EMSDK_VERSION: "3.1.40"
   TINYGO_VERSION: "0.30.0"
-  ZIG_VERSION: "0.11.0-dev.3334+cd1417dbd"
+  ZIG_VERSION: "0.11.0"
 
 concurrency:
   # https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#example-using-concurrency-to-cancel-any-in-progress-job-or-run
@@ -55,7 +55,7 @@ jobs:
       - name: Install Zig
         run: |
           sudo apt install xz-utils
-          sudo sh -c 'wget -c https://ziglang.org/builds/zig-linux-x86_64-${{ env.ZIG_VERSION }}.tar.xz -O - | tar -xJ --strip-components=1 -C /usr/local/bin'
+          sudo sh -c 'wget -c https://ziglang.org/download/${{ env.ZIG_VERSION }}/zig-linux-x86_64-${{ env.ZIG_VERSION }}.tar.xz -O - | tar -xJ --strip-components=1 -C /usr/local/bin'
 
       - name: Cache Emscripten
         id: cache-emsdk
diff --git a/.github/workflows/integration.yaml b/.github/workflows/integration.yaml
index 18f4c886..bc3df268 100644
--- a/.github/workflows/integration.yaml
+++ b/.github/workflows/integration.yaml
@@ -20,7 +20,7 @@ defaults:
 env:  # Update this prior to requiring a higher minor version in go.mod
   GO_VERSION: "1.21"  # 1.xx == latest patch of 1.xx
   TINYGO_VERSION: "0.30.0"
-  ZIG_VERSION: "0.11.0-dev.3334+cd1417dbd"
+  ZIG_VERSION: "0.11.0"
 
 concurrency:
   # https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#example-using-concurrency-to-cancel-any-in-progress-job-or-run
@@ -46,7 +46,7 @@ jobs:
         with:
           # Use share the cache containing archives across OSes.
           enableCrossOsArchive: true
-          key: zig-test-binary-${{ env.ZIG_VERSION }}
+          key: zig-stdlib-test-binary-${{ env.ZIG_VERSION }}
           path:
             ./zigbin/
 
@@ -54,13 +54,13 @@ jobs:
         if: steps.binary-cache.outputs.cache-hit != 'true'
         run: |
           mkdir -p ${{ env.ZIG_INSTALL }}
-          curl -sSL https://ziglang.org/builds/zig-linux-x86_64-${{ env.ZIG_VERSION }}.tar.xz | tar -xJ --strip-components=1 -C ${{ env.ZIG_INSTALL }}
+          curl -sSL https://ziglang.org/download/${{ env.ZIG_VERSION }}/zig-linux-x86_64-${{ env.ZIG_VERSION }}.tar.xz | tar -xJ --strip-components=1 -C ${{ env.ZIG_INSTALL }}
 
       - name: Download Zig source code
         if: steps.binary-cache.outputs.cache-hit != 'true'
         run: |
           mkdir -p ${{ env.ZIG_SOURCE }}
-          curl -sSL https://ziglang.org/builds/zig-${{ env.ZIG_VERSION }}.tar.xz | tar -xJ --strip-components=1 -C ${{ env.ZIG_SOURCE }}
+          curl -sSL https://ziglang.org/download/${{ env.ZIG_VERSION }}/zig-${{ env.ZIG_VERSION }}.tar.xz | tar -xJ --strip-components=1 -C ${{ env.ZIG_SOURCE }}
 
       - name: Build Stdlib test binary
         if: steps.binary-cache.outputs.cache-hit != 'true'
@@ -93,7 +93,7 @@ jobs:
           enableCrossOsArchive: true
           # We need this cache to run tests.
           fail-on-cache-miss: true
-          key: zig-test-binary-${{ env.ZIG_VERSION }}
+          key: zig-stdlib-test-binary-${{ env.ZIG_VERSION }}
           path:
             ./zigbin/
 
diff --git a/internal/engine/compiler/engine.go b/internal/engine/compiler/engine.go
index 8fc9d52b..d4e8dcd0 100644
--- a/internal/engine/compiler/engine.go
+++ b/internal/engine/compiler/engine.go
@@ -404,7 +404,7 @@ const (
 	dataInstanceStructSize = 24
 
 	// Consts for wasm.ElementInstance.
-	elementInstanceStructSize = 32
+	elementInstanceStructSize = 24
 
 	// pointerSizeLog2 satisfies: 1 << pointerSizeLog2 = sizeOf(uintptr)
 	pointerSizeLog2 = 3
diff --git a/internal/engine/interpreter/interpreter.go b/internal/engine/interpreter/interpreter.go
index 9adb6afe..d47e7c31 100644
--- a/internal/engine/interpreter/interpreter.go
+++ b/internal/engine/interpreter/interpreter.go
@@ -1653,15 +1653,15 @@ func (ce *callEngine) callNativeFunc(ctx context.Context, m *wasm.ModuleInstance
 			inElementOffset := ce.popValue()
 			inTableOffset := ce.popValue()
 			table := tables[op.U2]
-			if inElementOffset+copySize > uint64(len(elementInstance.References)) ||
+			if inElementOffset+copySize > uint64(len(elementInstance)) ||
 				inTableOffset+copySize > uint64(len(table.References)) {
 				panic(wasmruntime.ErrRuntimeInvalidTableAccess)
 			} else if copySize != 0 {
-				copy(table.References[inTableOffset:inTableOffset+copySize], elementInstance.References[inElementOffset:])
+				copy(table.References[inTableOffset:inTableOffset+copySize], elementInstance[inElementOffset:])
 			}
 			frame.pc++
 		case wazeroir.OperationKindElemDrop:
-			elementInstances[op.U1].References = nil
+			elementInstances[op.U1] = nil
 			frame.pc++
 		case wazeroir.OperationKindTableCopy:
 			srcTable, dstTable := tables[op.U1].References, tables[op.U2].References
diff --git a/internal/engine/wazevo/backend/isa/arm64/instr.go b/internal/engine/wazevo/backend/isa/arm64/instr.go
index f0d6138d..ebd158c9 100644
--- a/internal/engine/wazevo/backend/isa/arm64/instr.go
+++ b/internal/engine/wazevo/backend/isa/arm64/instr.go
@@ -99,11 +99,16 @@ var defKinds = [numInstructionKinds]defKind{
 	fpuCSel:              defKindRD,
 	movToVec:             defKindRD,
 	movFromVec:           defKindRD,
+	movFromVecSigned:     defKindRD,
 	vecDup:               defKindRD,
+	vecDupElement:        defKindRD,
 	vecExtract:           defKindRD,
 	vecMisc:              defKindRD,
+	vecMovElement:        defKindRD,
 	vecLanes:             defKindRD,
 	vecShiftImm:          defKindRD,
+	vecTbl:               defKindRD,
+	vecTbl2:              defKindRD,
 	vecPermute:           defKindRD,
 	vecRRR:               defKindRD,
 	fpuToInt:             defKindRD,
@@ -148,6 +153,7 @@ const (
 	useKindRN
 	useKindRNRM
 	useKindRNRMRA
+	useKindRNRN1RM
 	useKindRet
 	useKindCall
 	useKindCallInd
@@ -210,12 +216,17 @@ var useKinds = [numInstructionKinds]useKind{
 	fpuCSel:              useKindRNRM,
 	movToVec:             useKindRN,
 	movFromVec:           useKindRN,
+	movFromVecSigned:     useKindRN,
 	vecDup:               useKindRN,
+	vecDupElement:        useKindRN,
 	vecExtract:           useKindRNRM,
 	cCmpImm:              useKindRN,
 	vecMisc:              useKindRN,
+	vecMovElement:        useKindRN,
 	vecLanes:             useKindRN,
 	vecShiftImm:          useKindRN,
+	vecTbl:               useKindRNRM,
+	vecTbl2:              useKindRNRN1RM,
 	vecRRR:               useKindRNRM,
 	vecPermute:           useKindRNRM,
 	fpuToInt:             useKindRN,
@@ -252,6 +263,14 @@ func (i *instruction) uses(regs []regalloc.VReg) []regalloc.VReg {
 		if ra := i.ra.reg(); ra.Valid() {
 			regs = append(regs, ra)
 		}
+	case useKindRNRN1RM:
+		if rn := i.rn.reg(); rn.Valid() && rn.IsRealReg() {
+			rn1 := regalloc.FromRealReg(rn.RealReg()+1, rn.RegType())
+			regs = append(regs, rn, rn1)
+		}
+		if rm := i.rm.reg(); rm.Valid() {
+			regs = append(regs, rm)
+		}
 	case useKindRet:
 		regs = append(regs, i.abi.retRealRegs...)
 	case useKindAMode:
@@ -302,6 +321,19 @@ func (i *instruction) assignUse(index int, reg regalloc.VReg) {
 				i.rm = i.rm.assignReg(reg)
 			}
 		}
+	case useKindRNRN1RM:
+		if index == 0 {
+			if rn := i.rn.reg(); rn.Valid() {
+				i.rn = i.rn.assignReg(reg)
+			}
+			if rn1 := i.rn.reg() + 1; rn1.Valid() {
+				i.rm = i.rm.assignReg(reg + 1)
+			}
+		} else {
+			if rm := i.rm.reg(); rm.Valid() {
+				i.rm = i.rm.assignReg(reg)
+			}
+		}
 	case useKindRNRMRA:
 		if index == 0 {
 			if rn := i.rn.reg(); rn.Valid() {
@@ -778,8 +810,12 @@ func (i *instruction) asMovToVec(rd, rn operand, arr vecArrangement, index vecIn
 	i.u1, i.u2 = uint64(arr), uint64(index)
 }
 
-func (i *instruction) asMovFromVec(rd, rn operand, arr vecArrangement, index vecIndex) {
-	i.kind = movFromVec
+func (i *instruction) asMovFromVec(rd, rn operand, arr vecArrangement, index vecIndex, signed bool) {
+	if signed {
+		i.kind = movFromVecSigned
+	} else {
+		i.kind = movFromVec
+	}
 	i.rd = rd
 	i.rn = rn
 	i.u1, i.u2 = uint64(arr), uint64(index)
@@ -791,6 +827,13 @@ func (i *instruction) asVecDup(rd, rn operand, arr vecArrangement) {
 	i.rn, i.rd = rn, rd
 }
 
+func (i *instruction) asVecDupElement(rd, rn operand, arr vecArrangement, index vecIndex) {
+	i.kind = vecDupElement
+	i.u1 = uint64(arr)
+	i.rn, i.rd = rn, rd
+	i.u2 = uint64(index)
+}
+
 func (i *instruction) asVecExtract(rd, rn, rm operand, arr vecArrangement, index uint32) {
 	i.kind = vecExtract
 	i.u1 = uint64(arr)
@@ -798,6 +841,13 @@ func (i *instruction) asVecExtract(rd, rn, rm operand, arr vecArrangement, index
 	i.u2 = uint64(index)
 }
 
+func (i *instruction) asVecMovElement(rd, rn operand, arr vecArrangement, rdIndex, rnIndex vecIndex) {
+	i.kind = vecMovElement
+	i.u1 = uint64(arr)
+	i.u2, i.u3 = uint64(rdIndex), uint64(rnIndex)
+	i.rn, i.rd = rn, rd
+}
+
 func (i *instruction) asVecMisc(op vecOp, rd, rn operand, arr vecArrangement) {
 	i.kind = vecMisc
 	i.u1 = uint64(op)
@@ -819,6 +869,25 @@ func (i *instruction) asVecShiftImm(op vecOp, rd, rn, rm operand, arr vecArrange
 	i.u2 = uint64(arr)
 }
 
+func (i *instruction) asVecTbl(nregs byte, rd, rn, rm operand, arr vecArrangement) {
+	switch nregs {
+	case 0, 1:
+		i.kind = vecTbl
+	case 2:
+		i.kind = vecTbl2
+		if !rn.reg().IsRealReg() {
+			panic("rn is not a RealReg")
+		}
+		if rn.realReg() == v31 {
+			panic("rn cannot be v31")
+		}
+	default:
+		panic(fmt.Sprintf("unsupported number of registers %d", nregs))
+	}
+	i.rn, i.rm, i.rd = rn, rm, rd
+	i.u2 = uint64(arr)
+}
+
 func (i *instruction) asVecPermute(op vecOp, rd, rn, rm operand, arr vecArrangement) {
 	i.kind = vecPermute
 	i.u1 = uint64(op)
@@ -835,7 +904,9 @@ func (i *instruction) asVecRRR(op vecOp, rd, rn, rm operand, arr vecArrangement)
 
 func (i *instruction) isCopy() bool {
 	op := i.kind
-	return op == mov64 || op == mov32 || op == fpuMov64 || op == fpuMov128
+	// We do not include mov32 as it is not a copy instruction in the sense that it does not preserve the upper 32 bits,
+	// and it is only used in the translation of IReduce, not the actual copy indeed.
+	return op == mov64 || op == fpuMov64 || op == fpuMov128
 }
 
 // String implements fmt.Stringer.
@@ -1095,28 +1166,41 @@ func (i *instruction) String() (str string) {
 			panic("unsupported arrangement " + arr.String())
 		}
 		str = fmt.Sprintf("ins %s, %s", formatVRegVec(i.rd.nr(), arr, vecIndex(i.u2)), formatVRegSized(i.rn.nr(), size))
-	case movFromVec:
+	case movFromVec, movFromVecSigned:
 		var size byte
 		var opcode string
 		arr := vecArrangement(i.u1)
+		signed := i.kind == movFromVecSigned
 		switch arr {
 		case vecArrangementB, vecArrangementH, vecArrangementS:
 			size = 32
-			opcode = "umov"
+			if signed {
+				opcode = "smov"
+			} else {
+				opcode = "umov"
+			}
 		case vecArrangementD:
 			size = 64
-			opcode = "mov"
+			if signed {
+				opcode = "smov"
+			} else {
+				opcode = "mov"
+			}
 		default:
 			panic("unsupported arrangement " + arr.String())
 		}
 		str = fmt.Sprintf("%s %s, %s", opcode, formatVRegSized(i.rd.nr(), size), formatVRegVec(i.rn.nr(), arr, vecIndex(i.u2)))
-	case movFromVecSigned:
-		panic("TODO")
 	case vecDup:
 		str = fmt.Sprintf("dup %s, %s",
 			formatVRegVec(i.rd.nr(), vecArrangement(i.u1), vecIndexNone),
 			formatVRegSized(i.rn.nr(), 64),
 		)
+	case vecDupElement:
+		arr := vecArrangement(i.u1)
+		str = fmt.Sprintf("dup %s, %s",
+			formatVRegVec(i.rd.nr(), arr, vecIndexNone),
+			formatVRegVec(i.rn.nr(), arr, vecIndex(i.u2)),
+		)
 	case vecDupFromFpu:
 		panic("TODO")
 	case vecExtract:
@@ -1129,7 +1213,10 @@ func (i *instruction) String() (str string) {
 	case vecExtend:
 		panic("TODO")
 	case vecMovElement:
-		panic("TODO")
+		str = fmt.Sprintf("mov %s, %s",
+			formatVRegVec(i.rd.nr(), vecArrangement(i.u1), vecIndex(i.u2)),
+			formatVRegVec(i.rn.nr(), vecArrangement(i.u1), vecIndex(i.u3)),
+		)
 	case vecMiscNarrow:
 		panic("TODO")
 	case vecRRR:
@@ -1176,9 +1263,20 @@ func (i *instruction) String() (str string) {
 			formatVRegVec(i.rn.nr(), arr, vecIndexNone),
 			i.rm.shiftImm())
 	case vecTbl:
-		panic("TODO")
+		arr := vecArrangement(i.u2)
+		str = fmt.Sprintf("tbl %s, { %s }, %s",
+			formatVRegVec(i.rd.nr(), arr, vecIndexNone),
+			formatVRegVec(i.rn.nr(), vecArrangement16B, vecIndexNone),
+			formatVRegVec(i.rm.nr(), arr, vecIndexNone))
 	case vecTbl2:
-		panic("TODO")
+		arr := vecArrangement(i.u2)
+		rd, rn, rm := i.rd.nr(), i.rn.nr(), i.rm.nr()
+		rn1 := regalloc.FromRealReg(rn.RealReg()+1, rn.RegType())
+		str = fmt.Sprintf("tbl %s, { %s, %s }, %s",
+			formatVRegVec(rd, arr, vecIndexNone),
+			formatVRegVec(rn, vecArrangement16B, vecIndexNone),
+			formatVRegVec(rn1, vecArrangement16B, vecIndexNone),
+			formatVRegVec(rm, arr, vecIndexNone))
 	case vecPermute:
 		arr := vecArrangement(i.u2)
 		str = fmt.Sprintf("%s %s, %s, %s",
@@ -1390,6 +1488,8 @@ const (
 	movFromVecSigned
 	// vecDup represents a duplication of general-purpose register to vector.
 	vecDup
+	// vecDupElement represents a duplication of a vector element to vector or scalar.
+	vecDupElement
 	// vecDupFromFpu represents a duplication of scalar to vector.
 	vecDupFromFpu
 	// vecExtract represents a vector extraction operation.
diff --git a/internal/engine/wazevo/backend/isa/arm64/instr_encoding.go b/internal/engine/wazevo/backend/isa/arm64/instr_encoding.go
index 8116f367..7573a64a 100644
--- a/internal/engine/wazevo/backend/isa/arm64/instr_encoding.go
+++ b/internal/engine/wazevo/backend/isa/arm64/instr_encoding.go
@@ -255,18 +255,25 @@ func (i *instruction) encode(c backend.Compiler) {
 			vecArrangement(byte(i.u1)),
 			vecIndex(i.u2),
 		))
-	case movFromVec:
+	case movFromVec, movFromVecSigned:
 		c.Emit4Bytes(encodeMoveFromVec(
 			regNumberInEncoding[i.rd.realReg()],
 			regNumberInEncoding[i.rn.realReg()],
 			vecArrangement(byte(i.u1)),
 			vecIndex(i.u2),
+			i.kind == movFromVecSigned,
 		))
 	case vecDup:
 		c.Emit4Bytes(encodeVecDup(
 			regNumberInEncoding[i.rd.realReg()],
 			regNumberInEncoding[i.rn.realReg()],
 			vecArrangement(byte(i.u1))))
+	case vecDupElement:
+		c.Emit4Bytes(encodeVecDupElement(
+			regNumberInEncoding[i.rd.realReg()],
+			regNumberInEncoding[i.rn.realReg()],
+			vecArrangement(byte(i.u1)),
+			vecIndex(i.u2)))
 	case vecExtract:
 		c.Emit4Bytes(encodeVecExtract(
 			regNumberInEncoding[i.rd.realReg()],
@@ -281,6 +288,13 @@ func (i *instruction) encode(c backend.Compiler) {
 			regNumberInEncoding[i.rn.realReg()],
 			regNumberInEncoding[i.rm.realReg()],
 			vecArrangement(byte(i.u2))))
+	case vecMovElement:
+		c.Emit4Bytes(encodeVecMovElement(
+			regNumberInEncoding[i.rd.realReg()],
+			regNumberInEncoding[i.rn.realReg()],
+			vecArrangement(i.u1),
+			uint32(i.u2), uint32(i.u3),
+		))
 	case vecMisc:
 		c.Emit4Bytes(encodeAdvancedSIMDTwoMisc(
 			vecOp(i.u1),
@@ -288,7 +302,6 @@ func (i *instruction) encode(c backend.Compiler) {
 			regNumberInEncoding[i.rn.realReg()],
 			vecArrangement(i.u2),
 		))
-
 	case vecLanes:
 		c.Emit4Bytes(encodeVecLanes(
 			vecOp(i.u1),
@@ -304,6 +317,22 @@ func (i *instruction) encode(c backend.Compiler) {
 			uint32(i.rm.shiftImm()),
 			vecArrangement(i.u2),
 		))
+	case vecTbl:
+		c.Emit4Bytes(encodeVecTbl(
+			1,
+			regNumberInEncoding[i.rd.realReg()],
+			regNumberInEncoding[i.rn.realReg()],
+			regNumberInEncoding[i.rm.realReg()],
+			vecArrangement(i.u2)),
+		)
+	case vecTbl2:
+		c.Emit4Bytes(encodeVecTbl(
+			2,
+			regNumberInEncoding[i.rd.realReg()],
+			regNumberInEncoding[i.rn.realReg()],
+			regNumberInEncoding[i.rm.realReg()],
+			vecArrangement(i.u2)),
+		)
 	case brTableSequence:
 		encodeBrTableSequence(c, i.rn.reg(), i.targets)
 	case fpuToInt, intToFpu:
@@ -819,6 +848,47 @@ func encodeMoveToVec(rd, rn uint32, arr vecArrangement, index vecIndex) uint32 {
 	return 0b01001110000<<21 | imm5<<16 | 0b000111<<10 | rn<<5 | rd
 }
 
+// encodeMoveToVec encodes as "Move vector element to another vector element, mov (element)" (represented as `ins`) in
+// https://developer.arm.com/documentation/ddi0596/2020-12/SIMD-FP-Instructions/MOV--element---Move-vector-element-to-another-vector-element--an-alias-of-INS--element--?lang=en
+// https://developer.arm.com/documentation/ddi0596/2020-12/SIMD-FP-Instructions/INS--element---Insert-vector-element-from-another-vector-element-?lang=en
+func encodeVecMovElement(rd, rn uint32, arr vecArrangement, srcIndex, dstIndex uint32) uint32 {
+	var imm4, imm5 uint32
+	switch arr {
+	case vecArrangementB:
+		imm5 |= 0b1
+		imm5 |= srcIndex << 1
+		imm4 = dstIndex
+		if srcIndex > 0b1111 {
+			panic(fmt.Sprintf("vector index is larger than the allowed bound: %d > 15", srcIndex))
+		}
+	case vecArrangementH:
+		imm5 |= 0b10
+		imm5 |= srcIndex << 2
+		imm4 = dstIndex << 1
+		if srcIndex > 0b111 {
+			panic(fmt.Sprintf("vector index is larger than the allowed bound: %d > 7", srcIndex))
+		}
+	case vecArrangementS:
+		imm5 |= 0b100
+		imm5 |= srcIndex << 3
+		imm4 = dstIndex << 2
+		if srcIndex > 0b11 {
+			panic(fmt.Sprintf("vector index is larger than the allowed bound: %d > 3", srcIndex))
+		}
+	case vecArrangementD:
+		imm5 |= 0b1000
+		imm5 |= srcIndex << 4
+		imm4 = dstIndex << 3
+		if srcIndex > 0b1 {
+			panic(fmt.Sprintf("vector index is larger than the allowed bound: %d > 1", srcIndex))
+		}
+	default:
+		panic("Unsupported arrangement " + arr.String())
+	}
+
+	return 0b01101110000<<21 | imm5<<16 | imm4<<11 | 0b1<<10 | rn<<5 | rd
+}
+
 // encodeUnconditionalBranchReg encodes as "Unconditional branch (register)" in:
 // https://developer.arm.com/documentation/ddi0596/2020-12/Index-by-Encoding/Branches--Exception-Generating-and-System-instructions?lang=en
 func encodeUnconditionalBranchReg(rn uint32, link bool) uint32 {
@@ -833,29 +903,31 @@ func encodeUnconditionalBranchReg(rn uint32, link bool) uint32 {
 // (represented as `umov` when dest is 32-bit, `umov` otherwise) in
 // https://developer.arm.com/documentation/ddi0596/2020-12/SIMD-FP-Instructions/UMOV--Unsigned-Move-vector-element-to-general-purpose-register-?lang=en
 // https://developer.arm.com/documentation/ddi0596/2020-12/SIMD-FP-Instructions/MOV--to-general---Move-vector-element-to-general-purpose-register--an-alias-of-UMOV-?lang=en
-func encodeMoveFromVec(rd, rn uint32, arr vecArrangement, index vecIndex) uint32 {
-	var q uint32
-	var imm5 uint32
-	switch arr {
-	case vecArrangementB:
+func encodeMoveFromVec(rd, rn uint32, arr vecArrangement, index vecIndex, signed bool) uint32 {
+	var op, imm4, q, imm5 uint32
+	switch {
+	case arr == vecArrangementB:
 		imm5 |= 0b1
 		imm5 |= uint32(index) << 1
 		if index > 0b1111 {
 			panic(fmt.Sprintf("vector index is larger than the allowed bound: %d > 15", index))
 		}
-	case vecArrangementH:
+	case arr == vecArrangementH:
 		imm5 |= 0b10
 		imm5 |= uint32(index) << 2
 		if index > 0b111 {
 			panic(fmt.Sprintf("vector index is larger than the allowed bound: %d > 7", index))
 		}
-	case vecArrangementS:
+	case arr == vecArrangementS && signed:
+		q = 0b1
+		fallthrough
+	case arr == vecArrangementS:
 		imm5 |= 0b100
 		imm5 |= uint32(index) << 3
 		if index > 0b11 {
 			panic(fmt.Sprintf("vector index is larger than the allowed bound: %d > 3", index))
 		}
-	case vecArrangementD:
+	case arr == vecArrangementD && !signed:
 		imm5 |= 0b1000
 		imm5 |= uint32(index) << 4
 		q = 0b1
@@ -865,10 +937,15 @@ func encodeMoveFromVec(rd, rn uint32, arr vecArrangement, index vecIndex) uint32
 	default:
 		panic("Unsupported arrangement " + arr.String())
 	}
-	return 0b0_001110000<<21 | q<<30 | imm5<<16 | 0b001111<<10 | rn<<5 | rd
+	if signed {
+		op, imm4 = 0, 0b0101
+	} else {
+		op, imm4 = 0, 0b0111
+	}
+	return op<<29 | 0b01110000<<21 | q<<30 | imm5<<16 | imm4<<11 | 1<<10 | rn<<5 | rd
 }
 
-// encodeVecDup encodes as "Duplicate general-purpose register to vector."
+// encodeVecDup encodes as "Duplicate general-purpose register to vector" DUP (general)
 // (represented as `dup`)
 // https://developer.arm.com/documentation/ddi0596/2020-12/SIMD-FP-Instructions/DUP--general---Duplicate-general-purpose-register-to-vector-?lang=en
 func encodeVecDup(rd, rn uint32, arr vecArrangement) uint32 {
@@ -894,6 +971,32 @@ func encodeVecDup(rd, rn uint32, arr vecArrangement) uint32 {
 	return q<<30 | 0b001110000<<21 | imm5<<16 | 0b000011<<10 | rn<<5 | rd
 }
 
+// encodeVecDup encodes as "Duplicate vector element to vector or scalar" DUP (element).
+// (represented as `dup`)
+// https://developer.arm.com/documentation/ddi0596/2020-12/SIMD-FP-Instructions/DUP--element---Duplicate-vector-element-to-vector-or-scalar-
+func encodeVecDupElement(rd, rn uint32, arr vecArrangement, srcIndex vecIndex) uint32 {
+	var q, imm5 uint32
+	q = 0b1
+	switch arr {
+	case vecArrangementB:
+		imm5 |= 0b1
+		imm5 |= uint32(srcIndex) << 1
+	case vecArrangementH:
+		imm5 |= 0b10
+		imm5 |= uint32(srcIndex) << 2
+	case vecArrangementS:
+		imm5 |= 0b100
+		imm5 |= uint32(srcIndex) << 3
+	case vecArrangementD:
+		imm5 |= 0b1000
+		imm5 |= uint32(srcIndex) << 4
+	default:
+		panic("unsupported arrangement" + arr.String())
+	}
+
+	return q<<30 | 0b001110000<<21 | imm5<<16 | 0b1<<10 | rn<<5 | rd
+}
+
 // encodeVecExtract encodes as "Advanced SIMD extract."
 // Currently only `ext` is defined.
 // https://developer.arm.com/documentation/ddi0602/2023-06/Index-by-Encoding/Data-Processing----Scalar-Floating-Point-and-Advanced-SIMD?lang=en#simd-dp
@@ -1718,6 +1821,35 @@ func encodeVecShiftImm(op vecOp, rd uint32, rn, amount uint32, arr vecArrangemen
 	return q<<30 | u<<29 | 0b011110<<23 | immh<<19 | immb<<16 | 0b000001<<10 | opcode<<11 | 0b1<<10 | rn<<5 | rd
 }
 
+// encodeVecTbl encodes as Data Processing (Advanced SIMD table lookup) in
+// https://developer.arm.com/documentation/ddi0596/2020-12/Index-by-Encoding/Data-Processing----Scalar-Floating-Point-and-Advanced-SIMD?lang=en#simd-dp
+//
+// Note: tblOp may encode tbl1, tbl2... in the future. Currently, it is ignored.
+func encodeVecTbl(nregs, rd, rn, rm uint32, arr vecArrangement) uint32 {
+	var q, op2, len, op uint32
+
+	switch nregs {
+	case 1:
+		// tbl: single-register
+		len = 0b00
+	case 2:
+		// tbl2: 2-register table
+		len = 0b01
+	default:
+		panic(fmt.Sprintf("unsupported number or registers %d", nregs))
+	}
+	switch arr {
+	case vecArrangement8B:
+		q = 0b0
+	case vecArrangement16B:
+		q = 0b1
+	default:
+		panic("unsupported arrangement: " + arr.String())
+	}
+
+	return q<<30 | 0b001110<<24 | op2<<22 | rm<<16 | len<<13 | op<<12 | rn<<5 | rd
+}
+
 // encodeVecMisc encodes as Data Processing (Advanced SIMD two-register miscellaneous) depending on vecOp in
 // https://developer.arm.com/documentation/ddi0596/2020-12/Index-by-Encoding/Data-Processing----Scalar-Floating-Point-and-Advanced-SIMD?lang=en#simd-dp
 func encodeAdvancedSIMDTwoMisc(op vecOp, rd, rn uint32, arr vecArrangement) uint32 {
diff --git a/internal/engine/wazevo/backend/isa/arm64/lower_instr.go b/internal/engine/wazevo/backend/isa/arm64/lower_instr.go
index 1fac1dc1..2bf8f2e2 100644
--- a/internal/engine/wazevo/backend/isa/arm64/lower_instr.go
+++ b/internal/engine/wazevo/backend/isa/arm64/lower_instr.go
@@ -563,12 +563,139 @@ func (m *machine) LowerInstr(instr *ssa.Instruction) {
 		rd := operandNR(m.compiler.VRegOf(instr.Return()))
 		ins.asVecMisc(vecOpFcvtn, rd, rn, vecArrangement2S)
 		m.insert(ins)
+	case ssa.OpcodeExtractlane:
+		x, index, signed, lane := instr.ExtractlaneData()
+
+		rn := m.getOperand_NR(m.compiler.ValueDefinition(x), extModeNone)
+		rd := operandNR(m.compiler.VRegOf(instr.Return()))
+
+		mov := m.allocateInstr()
+		switch lane {
+		case ssa.VecLaneI8x16:
+			mov.asMovFromVec(rd, rn, vecArrangementB, vecIndex(index), signed)
+		case ssa.VecLaneI16x8:
+			mov.asMovFromVec(rd, rn, vecArrangementH, vecIndex(index), signed)
+		case ssa.VecLaneI32x4:
+			mov.asMovFromVec(rd, rn, vecArrangementS, vecIndex(index), signed)
+		case ssa.VecLaneI64x2:
+			mov.asMovFromVec(rd, rn, vecArrangementD, vecIndex(index), signed)
+		case ssa.VecLaneF32x4:
+			mov.asVecMovElement(rd, rn, vecArrangementS, vecIndex(0), vecIndex(index))
+		case ssa.VecLaneF64x2:
+			mov.asVecMovElement(rd, rn, vecArrangementD, vecIndex(0), vecIndex(index))
+		default:
+			panic("unsupported lane: " + lane.String())
+		}
+
+		m.insert(mov)
+
+	case ssa.OpcodeInsertlane:
+		x, y, index, lane := instr.InsertlaneData()
+		rn := m.getOperand_NR(m.compiler.ValueDefinition(x), extModeNone)
+		rm := m.getOperand_NR(m.compiler.ValueDefinition(y), extModeNone)
+		rd := operandNR(m.compiler.VRegOf(instr.Return()))
+
+		// Initially mov rn to rd.
+		mov0 := m.allocateInstr()
+		mov0.asFpuMov128(rd.nr(), rn.nr())
+		m.insert(mov0)
+
+		// movToVec and vecMovElement do not clear the remaining bits to zero,
+		// thus, we can mov rm in-place to rd.
+		mov := m.allocateInstr()
+		switch lane {
+		case ssa.VecLaneI8x16:
+			mov.asMovToVec(rd, rm, vecArrangementB, vecIndex(index))
+		case ssa.VecLaneI16x8:
+			mov.asMovToVec(rd, rm, vecArrangementH, vecIndex(index))
+		case ssa.VecLaneI32x4:
+			mov.asMovToVec(rd, rm, vecArrangementS, vecIndex(index))
+		case ssa.VecLaneI64x2:
+			mov.asMovToVec(rd, rm, vecArrangementD, vecIndex(index))
+		case ssa.VecLaneF32x4:
+			mov.asVecMovElement(rd, rm, vecArrangementS, vecIndex(index), vecIndex(0))
+		case ssa.VecLaneF64x2:
+			mov.asVecMovElement(rd, rm, vecArrangementD, vecIndex(index), vecIndex(0))
+		}
+		m.insert(mov)
+
+	case ssa.OpcodeSwizzle:
+		x, y, lane := instr.Arg2WithLane()
+		rn := m.getOperand_NR(m.compiler.ValueDefinition(x), extModeNone)
+		rm := m.getOperand_NR(m.compiler.ValueDefinition(y), extModeNone)
+		rd := operandNR(m.compiler.VRegOf(instr.Return()))
+
+		arr := ssaLaneToArrangement(lane)
+
+		// tbl <rd>.<arr>, { <rn>.<arr> }, <rm>.<arr>
+		tbl1 := m.allocateInstr()
+		tbl1.asVecTbl(1, rd, rn, rm, arr)
+		m.insert(tbl1)
+
+	case ssa.OpcodeShuffle:
+		x, y, lane1, lane2 := instr.ShuffleData()
+		rn := m.getOperand_NR(m.compiler.ValueDefinition(x), extModeNone)
+		rm := m.getOperand_NR(m.compiler.ValueDefinition(y), extModeNone)
+		rd := operandNR(m.compiler.VRegOf(instr.Return()))
+
+		m.lowerShuffle(rd, rn, rm, lane1, lane2)
+
+	case ssa.OpcodeSplat:
+		x, lane := instr.ArgWithLane()
+		rn := m.getOperand_NR(m.compiler.ValueDefinition(x), extModeNone)
+		rd := operandNR(m.compiler.VRegOf(instr.Return()))
+
+		dup := m.allocateInstr()
+		switch lane {
+		case ssa.VecLaneI8x16:
+			dup.asVecDup(rd, rn, vecArrangement16B)
+		case ssa.VecLaneI16x8:
+			dup.asVecDup(rd, rn, vecArrangement8H)
+		case ssa.VecLaneI32x4:
+			dup.asVecDup(rd, rn, vecArrangement4S)
+		case ssa.VecLaneI64x2:
+			dup.asVecDup(rd, rn, vecArrangement2D)
+		case ssa.VecLaneF32x4:
+			dup.asVecDupElement(rd, rn, vecArrangementS, vecIndex(0))
+		case ssa.VecLaneF64x2:
+			dup.asVecDupElement(rd, rn, vecArrangementD, vecIndex(0))
+		}
+		m.insert(dup)
+
 	default:
 		panic("TODO: lowering " + op.String())
 	}
 	m.FlushPendingInstructions()
 }
 
+func (m *machine) lowerShuffle(rd, rn, rm operand, lane1, lane2 uint64) {
+	// `tbl2` requires 2 consecutive registers, so we arbitrarily pick v29, v30.
+	vReg, wReg := v29VReg, v30VReg
+
+	// Initialize v29, v30 to rn, rm.
+	movv := m.allocateInstr()
+	movv.asFpuMov128(vReg, rn.nr())
+	m.insert(movv)
+
+	movw := m.allocateInstr()
+	movw.asFpuMov128(wReg, rm.nr())
+	m.insert(movw)
+
+	// `lane1`, `lane2` are already encoded as two u64s with the right layout:
+	//     lane1 := lane[7]<<56 | ... | lane[1]<<8 | lane[0]
+	//     lane2 := lane[15]<<56 | ... | lane[9]<<8 | lane[8]
+	// Thus, we can use loadFpuConst128.
+	tmp := operandNR(m.compiler.AllocateVReg(regalloc.RegTypeFloat))
+	lfc := m.allocateInstr()
+	lfc.asLoadFpuConst128(tmp.nr(), lane1, lane2)
+	m.insert(lfc)
+
+	// tbl <rd>.16b, { <vReg>.16B, <wReg>.16b }, <tmp>.16b
+	tbl2 := m.allocateInstr()
+	tbl2.asVecTbl(2, rd, operandNR(vReg), tmp, vecArrangement16B)
+	m.insert(tbl2)
+}
+
 func (m *machine) lowerVShift(op ssa.Opcode, rd, rn, rm operand, arr vecArrangement) {
 	var modulo byte
 	switch arr {
@@ -659,7 +786,7 @@ func (m *machine) lowerVcheckTrue(op ssa.Opcode, rm, rd operand, arr vecArrangem
 	//	mov x0, x3?
 
 	movv := m.allocateInstr()
-	movv.asMovFromVec(rd, tmp, vecArrangementD, vecIndex(0))
+	movv.asMovFromVec(rd, tmp, vecArrangementD, vecIndex(0), false)
 	m.insert(movv)
 
 	fc := m.allocateInstr()
@@ -734,7 +861,7 @@ func (m *machine) lowerVhighBits(rm, rd operand, arr vecArrangement) {
 
 		// Extract the v.h[0] as the result.
 		movfv := m.allocateInstr()
-		movfv.asMovFromVec(rd, v0, vecArrangementH, vecIndex(0))
+		movfv.asMovFromVec(rd, v0, vecArrangementH, vecIndex(0), false)
 		m.insert(movfv)
 	case vecArrangement8H: // ssa.VecLaneI16x8
 		//	sshr v6?.8h, v2?.8h, #15
@@ -783,7 +910,7 @@ func (m *machine) lowerVhighBits(rm, rd operand, arr vecArrangement) {
 		m.insert(addv)
 
 		movfv := m.allocateInstr()
-		movfv.asMovFromVec(rd, v0, vecArrangementH, vecIndex(0))
+		movfv.asMovFromVec(rd, v0, vecArrangementH, vecIndex(0), false)
 		m.insert(movfv)
 	case vecArrangement4S: // ssa.VecLaneI32x4
 		// 	sshr v6?.8h, v2?.8h, #15
@@ -832,7 +959,7 @@ func (m *machine) lowerVhighBits(rm, rd operand, arr vecArrangement) {
 		m.insert(addv)
 
 		movfv := m.allocateInstr()
-		movfv.asMovFromVec(rd, v0, vecArrangementS, vecIndex(0))
+		movfv.asMovFromVec(rd, v0, vecArrangementS, vecIndex(0), false)
 		m.insert(movfv)
 	case vecArrangement2D: // ssa.VecLaneI64x2
 		// 	mov d3?, v2?.d[0]
@@ -843,12 +970,12 @@ func (m *machine) lowerVhighBits(rm, rd operand, arr vecArrangement) {
 
 		// Move the lower 64-bit int into result.
 		movv0 := m.allocateInstr()
-		movv0.asMovFromVec(rd, rm, vecArrangementD, vecIndex(0))
+		movv0.asMovFromVec(rd, rm, vecArrangementD, vecIndex(0), false)
 		m.insert(movv0)
 
 		// Move the higher 64-bit int into r0.
 		movv1 := m.allocateInstr()
-		movv1.asMovFromVec(r0, rm, vecArrangementD, vecIndex(1))
+		movv1.asMovFromVec(r0, rm, vecArrangementD, vecIndex(1), false)
 		m.insert(movv1)
 
 		// Move the sign bit into the least significant bit.
@@ -962,7 +1089,7 @@ func (m *machine) lowerIRem(execCtxVReg regalloc.VReg, rd, rn, rm operand, _64bi
 	m.insert(div)
 
 	// Check if rm is zero:
-	m.exitIfNot(execCtxVReg, registerAsRegNotZeroCond(rm.nr()), wazevoapi.ExitCodeIntegerDivisionByZero)
+	m.exitIfNot(execCtxVReg, registerAsRegNotZeroCond(rm.nr()), _64bit, wazevoapi.ExitCodeIntegerDivisionByZero)
 
 	// rd = rn-rd*rm by MSUB instruction.
 	msub := m.allocateInstr()
@@ -981,7 +1108,7 @@ func (m *machine) lowerIDiv(execCtxVReg regalloc.VReg, rd, rn, rm operand, _64bi
 	m.insert(div)
 
 	// Check if rm is zero:
-	m.exitIfNot(execCtxVReg, registerAsRegNotZeroCond(rm.nr()), wazevoapi.ExitCodeIntegerDivisionByZero)
+	m.exitIfNot(execCtxVReg, registerAsRegNotZeroCond(rm.nr()), _64bit, wazevoapi.ExitCodeIntegerDivisionByZero)
 
 	if signed {
 		// We need to check the signed overflow which happens iff "math.MinInt{32,64} / -1"
@@ -996,17 +1123,20 @@ func (m *machine) lowerIDiv(execCtxVReg regalloc.VReg, rd, rn, rm operand, _64bi
 		m.insert(ccmp)
 
 		// Check the overflow flag.
-		m.exitIfNot(execCtxVReg, vs.invert().asCond(), wazevoapi.ExitCodeIntegerOverflow)
+		m.exitIfNot(execCtxVReg, vs.invert().asCond(), false, wazevoapi.ExitCodeIntegerOverflow)
 	}
 }
 
-func (m *machine) exitIfNot(execCtxVReg regalloc.VReg, c cond, code wazevoapi.ExitCode) {
+// exitIfNot emits a conditional branch to exit if the condition is not met.
+// If `c` (cond type) is a register, `cond64bit` must be chosen to indicate whether the register is 32-bit or 64-bit.
+// Otherwise, `cond64bit` is ignored.
+func (m *machine) exitIfNot(execCtxVReg regalloc.VReg, c cond, cond64bit bool, code wazevoapi.ExitCode) {
 	cbr := m.allocateInstr()
 	m.insert(cbr)
 	m.lowerExitWithCode(execCtxVReg, code)
 	// Conditional branch target is after exit.
 	l := m.insertBrTargetLabel()
-	cbr.asCondBr(c, l, false /* ignored */)
+	cbr.asCondBr(c, l, cond64bit)
 }
 
 func (m *machine) lowerFcopysign(x, y, ret ssa.Value) {
@@ -1067,7 +1197,7 @@ func (m *machine) lowerBitcast(instr *ssa.Instruction) {
 		} else {
 			arr = vecArrangementS
 		}
-		mov.asMovFromVec(rd, rn, arr, vecIndex(0))
+		mov.asMovFromVec(rd, rn, arr, vecIndex(0), false)
 		m.insert(mov)
 	default:
 		panic("TODO?BUG?")
@@ -1121,7 +1251,7 @@ func (m *machine) lowerFpuToInt(rd, rn operand, ctx regalloc.VReg, signed, src64
 		fpuCmp.asFpuCmp(rn, rn, src64bit)
 		m.insert(fpuCmp)
 		// If the VC flag is not set (== VS flag is set), it is a NaN.
-		m.exitIfNot(ctx, vc.asCond(), wazevoapi.ExitCodeInvalidConversionToInteger)
+		m.exitIfNot(ctx, vc.asCond(), false, wazevoapi.ExitCodeInvalidConversionToInteger)
 		// Otherwise, it is an overflow.
 		m.lowerExitWithCode(ctx, wazevoapi.ExitCodeIntegerOverflow)
 
@@ -1523,7 +1653,7 @@ func (m *machine) lowerPopcnt(x, result ssa.Value) {
 	m.insert(uaddlv)
 
 	mov := m.allocateInstr()
-	mov.asMovFromVec(rd, rf3, vecArrangementD, vecIndex(0))
+	mov.asMovFromVec(rd, rf3, vecArrangementD, vecIndex(0), false)
 	m.insert(mov)
 }
 
diff --git a/internal/engine/wazevo/call_engine.go b/internal/engine/wazevo/call_engine.go
index 401433e4..26f658c0 100644
--- a/internal/engine/wazevo/call_engine.go
+++ b/internal/engine/wazevo/call_engine.go
@@ -74,6 +74,12 @@ type (
 		savedRegisters [64][2]uint64
 		// goFunctionCallCalleeModuleContextOpaque is the pointer to the target Go function's moduleContextOpaque.
 		goFunctionCallCalleeModuleContextOpaque uintptr
+		// tableGrowTrampolineAddress holds the address of table grow trampoline function.
+		tableGrowTrampolineAddress *byte
+		// refFuncTrampolineAddress holds the address of ref-func trampoline function.
+		refFuncTrampolineAddress *byte
+		// memmoveAddress holds the address of memmove function implemented by Go runtime. See memmove.go.
+		memmoveAddress uintptr
 	}
 )
 
@@ -260,6 +266,14 @@ func (c *callEngine) callWithStack(ctx context.Context, paramResultStack []uint6
 			}
 			c.execCtx.exitCode = wazevoapi.ExitCodeOK
 			afterGoFunctionCallEntrypoint(c.execCtx.goCallReturnAddress, c.execCtxPtr, uintptr(unsafe.Pointer(c.execCtx.stackPointerBeforeGoCall)))
+		case wazevoapi.ExitCodeTableGrow:
+			mod := c.callerModuleInstance()
+			s := goCallStackView(c.execCtx.stackPointerBeforeGoCall)
+			tableIndex, num, ref := 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
+			afterGoFunctionCallEntrypoint(c.execCtx.goCallReturnAddress, c.execCtxPtr, uintptr(unsafe.Pointer(c.execCtx.stackPointerBeforeGoCall)))
 		case wazevoapi.ExitCodeCallGoFunction:
 			index := wazevoapi.GoFunctionIndexFromExitCode(ec)
 			f := hostModuleGoFuncFromOpaque[api.GoFunction](index, c.execCtx.goFunctionCallCalleeModuleContextOpaque)
@@ -338,6 +352,14 @@ func (c *callEngine) callWithStack(ctx context.Context, paramResultStack []uint6
 			}
 			c.execCtx.exitCode = wazevoapi.ExitCodeOK
 			afterGoFunctionCallEntrypoint(c.execCtx.goCallReturnAddress, c.execCtxPtr, uintptr(unsafe.Pointer(c.execCtx.stackPointerBeforeGoCall)))
+		case wazevoapi.ExitCodeRefFunc:
+			mod := c.callerModuleInstance()
+			s := goCallStackView(c.execCtx.stackPointerBeforeGoCall)
+			funcIndex := s[0]
+			ref := mod.Engine.FunctionInstanceReference(wasm.Index(funcIndex))
+			s[0] = uint64(ref)
+			c.execCtx.exitCode = wazevoapi.ExitCodeOK
+			afterGoFunctionCallEntrypoint(c.execCtx.goCallReturnAddress, c.execCtxPtr, uintptr(unsafe.Pointer(c.execCtx.stackPointerBeforeGoCall)))
 		case wazevoapi.ExitCodeUnreachable:
 			panic(wasmruntime.ErrRuntimeUnreachable)
 		case wazevoapi.ExitCodeMemoryOutOfBounds:
diff --git a/internal/engine/wazevo/engine.go b/internal/engine/wazevo/engine.go
index 94c38cce..aaabf9ef 100644
--- a/internal/engine/wazevo/engine.go
+++ b/internal/engine/wazevo/engine.go
@@ -46,26 +46,28 @@ type (
 	}
 
 	sharedFunctions struct {
-		// memoryGrowExecutable is a compiled executable for memory.grow builtin function.
+		// memoryGrowExecutable is a compiled trampoline executable for memory.grow builtin function.
 		memoryGrowExecutable []byte
-		// checkModuleExitCode is a compiled executable for checking module instance exit code. This
+		// checkModuleExitCode is a compiled trampoline executable for checking module instance exit code. This
 		// is used when ensureTermination is true.
 		checkModuleExitCode []byte
 		// stackGrowExecutable is a compiled executable for growing stack builtin function.
-		stackGrowExecutable       []byte
-		entryPreambles            map[*wasm.FunctionType][]byte
+		stackGrowExecutable []byte
+		// tableGrowExecutable is a compiled trampoline executable for table.grow builtin function.
+		tableGrowExecutable []byte
+		// refFuncExecutable is a compiled trampoline executable for ref.func builtin function.
+		refFuncExecutable         []byte
 		listenerBeforeTrampolines map[*wasm.FunctionType][]byte
 		listenerAfterTrampolines  map[*wasm.FunctionType][]byte
 	}
 
 	// compiledModule is a compiled variant of a wasm.Module and ready to be used for instantiation.
 	compiledModule struct {
-		executable []byte
+		*executables
 		// functionOffsets maps a local function index to the offset in the executable.
 		functionOffsets           []int
 		parent                    *engine
 		module                    *wasm.Module
-		entryPreambles            []*byte // indexed-correlated with the type index.
 		ensureTermination         bool
 		listeners                 []experimental.FunctionListener
 		listenerBeforeTrampolines []*byte
@@ -77,6 +79,11 @@ type (
 		sharedFunctions *sharedFunctions
 		sourceMap       sourceMap
 	}
+
+	executables struct {
+		executable     []byte
+		entryPreambles [][]byte
+	}
 )
 
 // sourceMap is a mapping from the offset of the executable to the offset of the original wasm binary.
@@ -149,23 +156,31 @@ func (e *engine) CompileModule(ctx context.Context, module *wasm.Module, listene
 	return nil
 }
 
+func (exec *executables) compileEntryPreambles(m *wasm.Module, machine backend.Machine, be backend.Compiler) {
+	exec.entryPreambles = make([][]byte, len(m.TypeSection))
+	for i := range m.TypeSection {
+		typ := &m.TypeSection[i]
+		sig := frontend.SignatureForWasmFunctionType(typ)
+		be.Init()
+		buf := machine.CompileEntryPreamble(&sig)
+		executable := mmapExecutable(buf)
+		exec.entryPreambles[i] = executable
+	}
+}
+
 func (e *engine) compileModule(ctx context.Context, module *wasm.Module, listeners []experimental.FunctionListener, ensureTermination bool) (*compiledModule, error) {
 	withListener := len(listeners) > 0
 	e.rels = e.rels[:0]
 	cm := &compiledModule{
 		offsets: wazevoapi.NewModuleContextOffsetData(module, withListener), parent: e, module: module,
 		ensureTermination: ensureTermination,
+		executables:       &executables{},
 	}
 
 	if module.IsHostModule {
 		return e.compileHostModule(ctx, module, listeners)
 	}
 
-	cm.entryPreambles = make([]*byte, len(module.TypeSection))
-	for i := range cm.entryPreambles {
-		cm.entryPreambles[i] = e.getEntryPreambleForType(&module.TypeSection[i])
-	}
-
 	importedFns, localFns := int(module.ImportFunctionCount), len(module.FunctionSection)
 	if localFns == 0 {
 		return cm, nil
@@ -184,6 +199,8 @@ func (e *engine) compileModule(ctx context.Context, module *wasm.Module, listene
 	machine := newMachine()
 	be := backend.NewCompiler(ctx, machine, ssaBuilder)
 
+	cm.executables.compileEntryPreambles(module, machine, be)
+
 	totalSize := 0 // Total binary size of the executable.
 	cm.functionOffsets = make([]int, localFns)
 	bodies := make([][]byte, localFns)
@@ -269,9 +286,8 @@ func (e *engine) compileModule(ctx context.Context, module *wasm.Module, listene
 			return nil, err
 		}
 	}
-	e.compiledModules[module.ID] = cm
 	cm.sharedFunctions = e.sharedFunctions
-	e.setFinalizer(cm, compiledModuleFinalizer)
+	e.setFinalizer(cm.executables, executablesFinalizer)
 	return cm, nil
 }
 
@@ -342,7 +358,7 @@ func (e *engine) compileHostModule(ctx context.Context, module *wasm.Module, lis
 	be := backend.NewCompiler(ctx, machine, ssa.NewBuilder())
 
 	num := len(module.CodeSection)
-	cm := &compiledModule{module: module, listeners: listeners}
+	cm := &compiledModule{module: module, listeners: listeners, executables: &executables{}}
 	cm.functionOffsets = make([]int, num)
 	totalSize := 0 // Total binary size of the executable.
 	bodies := make([][]byte, num)
@@ -420,6 +436,7 @@ func (e *engine) compileHostModule(ctx context.Context, module *wasm.Module, lis
 			return nil, err
 		}
 	}
+	e.setFinalizer(cm.executables, executablesFinalizer)
 	return cm, nil
 }
 
@@ -429,10 +446,10 @@ func (e *engine) Close() (err error) {
 	defer e.mux.Unlock()
 
 	for _, cm := range e.compiledModules {
-		cm.executable = nil
 		cm.functionOffsets = nil
 		cm.module = nil
 		cm.parent = nil
+		cm.executables = nil
 	}
 	e.sortedCompiledModules = nil
 	e.compiledModules = nil
@@ -528,7 +545,6 @@ func (e *engine) NewModuleEngine(m *wasm.Module, mi *wasm.ModuleInstance) (wasm.
 
 func (e *engine) compileSharedFunctions() {
 	e.sharedFunctions = &sharedFunctions{
-		entryPreambles:            make(map[*wasm.FunctionType][]byte),
 		listenerBeforeTrampolines: make(map[*wasm.FunctionType][]byte),
 		listenerAfterTrampolines:  make(map[*wasm.FunctionType][]byte),
 	}
@@ -536,12 +552,21 @@ func (e *engine) compileSharedFunctions() {
 	e.be.Init()
 	{
 		src := e.machine.CompileGoFunctionTrampoline(wazevoapi.ExitCodeGrowMemory, &ssa.Signature{
-			Params:  []ssa.Type{ssa.TypeI32 /* exec context */, ssa.TypeI32},
+			Params:  []ssa.Type{ssa.TypeI64 /* exec context */, ssa.TypeI32},
 			Results: []ssa.Type{ssa.TypeI32},
 		}, false)
 		e.sharedFunctions.memoryGrowExecutable = mmapExecutable(src)
 	}
 
+	e.be.Init()
+	{
+		src := e.machine.CompileGoFunctionTrampoline(wazevoapi.ExitCodeTableGrow, &ssa.Signature{
+			Params:  []ssa.Type{ssa.TypeI64 /* exec context */, ssa.TypeI32 /* table index */, ssa.TypeI32 /* num */, ssa.TypeI64 /* ref */},
+			Results: []ssa.Type{ssa.TypeI32},
+		}, false)
+		e.sharedFunctions.tableGrowExecutable = mmapExecutable(src)
+	}
+
 	e.be.Init()
 	{
 		src := e.machine.CompileGoFunctionTrampoline(wazevoapi.ExitCodeCheckModuleExitCode, &ssa.Signature{
@@ -551,7 +576,14 @@ func (e *engine) compileSharedFunctions() {
 		e.sharedFunctions.checkModuleExitCode = mmapExecutable(src)
 	}
 
-	// TODO: table grow, etc.
+	e.be.Init()
+	{
+		src := e.machine.CompileGoFunctionTrampoline(wazevoapi.ExitCodeRefFunc, &ssa.Signature{
+			Params:  []ssa.Type{ssa.TypeI64 /* exec context */, ssa.TypeI32 /* function index */},
+			Results: []ssa.Type{ssa.TypeI64}, // returns the function reference.
+		}, false)
+		e.sharedFunctions.refFuncExecutable = mmapExecutable(src)
+	}
 
 	e.be.Init()
 	{
@@ -572,10 +604,11 @@ func sharedFunctionsFinalizer(sf *sharedFunctions) {
 	if err := platform.MunmapCodeSegment(sf.stackGrowExecutable); err != nil {
 		panic(err)
 	}
-	for _, f := range sf.entryPreambles {
-		if err := platform.MunmapCodeSegment(f); err != nil {
-			panic(err)
-		}
+	if err := platform.MunmapCodeSegment(sf.tableGrowExecutable); err != nil {
+		panic(err)
+	}
+	if err := platform.MunmapCodeSegment(sf.refFuncExecutable); err != nil {
+		panic(err)
 	}
 	for _, f := range sf.listenerBeforeTrampolines {
 		if err := platform.MunmapCodeSegment(f); err != nil {
@@ -591,18 +624,26 @@ func sharedFunctionsFinalizer(sf *sharedFunctions) {
 	sf.memoryGrowExecutable = nil
 	sf.checkModuleExitCode = nil
 	sf.stackGrowExecutable = nil
-	sf.entryPreambles = nil
+	sf.tableGrowExecutable = nil
+	sf.refFuncExecutable = nil
 	sf.listenerBeforeTrampolines = nil
 	sf.listenerAfterTrampolines = nil
 }
 
-func compiledModuleFinalizer(cm *compiledModule) {
-	if len(cm.executable) > 0 {
-		if err := platform.MunmapCodeSegment(cm.executable); err != nil {
+func executablesFinalizer(exec *executables) {
+	if len(exec.executable) > 0 {
+		if err := platform.MunmapCodeSegment(exec.executable); err != nil {
+			panic(err)
+		}
+	}
+	exec.executable = nil
+
+	for _, f := range exec.entryPreambles {
+		if err := platform.MunmapCodeSegment(f); err != nil {
 			panic(err)
 		}
 	}
-	cm.executable = nil
+	exec.entryPreambles = nil
 }
 
 func mmapExecutable(src []byte) []byte {
@@ -635,23 +676,6 @@ func (cm *compiledModule) functionIndexOf(addr uintptr) wasm.Index {
 	return wasm.Index(index)
 }
 
-func (e *engine) getEntryPreambleForType(functionType *wasm.FunctionType) *byte {
-	e.mux.Lock()
-	defer e.mux.Unlock()
-	executable, ok := e.sharedFunctions.entryPreambles[functionType]
-	if ok {
-		return &executable[0]
-	}
-
-	sig := frontend.SignatureForWasmFunctionType(functionType)
-	e.be.Init()
-	buf := e.machine.CompileEntryPreamble(&sig)
-	executable = mmapExecutable(buf)
-
-	e.sharedFunctions.entryPreambles[functionType] = executable
-	return &executable[0]
-}
-
 func (e *engine) getListenerTrampolineForType(functionType *wasm.FunctionType) (before, after *byte) {
 	e.mux.Lock()
 	defer e.mux.Unlock()
diff --git a/internal/engine/wazevo/engine_cache.go b/internal/engine/wazevo/engine_cache.go
index a392398f..fedc4d05 100644
--- a/internal/engine/wazevo/engine_cache.go
+++ b/internal/engine/wazevo/engine_cache.go
@@ -2,19 +2,36 @@ package wazevo
 
 import (
 	"bytes"
+	"context"
+	"crypto/sha256"
 	"encoding/binary"
 	"fmt"
 	"io"
 	"runtime"
+	"unsafe"
 
 	"github.com/tetratelabs/wazero/experimental"
+	"github.com/tetratelabs/wazero/internal/engine/wazevo/backend"
+	"github.com/tetratelabs/wazero/internal/engine/wazevo/ssa"
 	"github.com/tetratelabs/wazero/internal/engine/wazevo/wazevoapi"
+	"github.com/tetratelabs/wazero/internal/filecache"
 	"github.com/tetratelabs/wazero/internal/platform"
 	"github.com/tetratelabs/wazero/internal/u32"
 	"github.com/tetratelabs/wazero/internal/u64"
 	"github.com/tetratelabs/wazero/internal/wasm"
 )
 
+// fileCacheKey returns a key for the file cache.
+// In order to avoid collisions with the existing compiler, we do not use m.ID directly,
+// but instead we rehash it with magic.
+func fileCacheKey(m *wasm.Module) (ret filecache.Key) {
+	s := sha256.New()
+	s.Write(m.ID[:])
+	s.Write(magic)
+	s.Sum(ret[:0])
+	return
+}
+
 func (e *engine) addCompiledModule(module *wasm.Module, cm *compiledModule) (err error) {
 	e.addCompiledModuleToMemory(module, cm)
 	if !module.IsHostModule && e.fileCache != nil {
@@ -47,10 +64,13 @@ func (e *engine) getCompiledModule(module *wasm.Module, listeners []experimental
 			}
 		}
 		e.addCompiledModuleToMemory(module, cm)
-		cm.entryPreambles = make([]*byte, len(module.TypeSection))
-		for i := range cm.entryPreambles {
-			cm.entryPreambles[i] = e.getEntryPreambleForType(&module.TypeSection[i])
-		}
+		ssaBuilder := ssa.NewBuilder()
+		machine := newMachine()
+		be := backend.NewCompiler(context.Background(), machine, ssaBuilder)
+		cm.executables.compileEntryPreambles(module, machine, be)
+
+		// Set the finalizer.
+		e.setFinalizer(cm.executables, executablesFinalizer)
 	}
 	return
 }
@@ -75,7 +95,7 @@ func (e *engine) addCompiledModuleToCache(module *wasm.Module, cm *compiledModul
 	if e.fileCache == nil || module.IsHostModule {
 		return
 	}
-	err = e.fileCache.Add(module.ID, serializeCompiledModule(e.wazeroVersion, cm))
+	err = e.fileCache.Add(fileCacheKey(module), serializeCompiledModule(e.wazeroVersion, cm))
 	return
 }
 
@@ -86,7 +106,7 @@ func (e *engine) getCompiledModuleFromCache(module *wasm.Module) (cm *compiledMo
 
 	// Check if the entries exist in the external cache.
 	var cached io.ReadCloser
-	cached, hit, err = e.fileCache.Get(module.ID)
+	cached, hit, err = e.fileCache.Get(fileCacheKey(module))
 	if !hit || err != nil {
 		return
 	}
@@ -100,17 +120,17 @@ func (e *engine) getCompiledModuleFromCache(module *wasm.Module) (cm *compiledMo
 		hit = false
 		return
 	} else if staleCache {
-		return nil, false, e.fileCache.Delete(module.ID)
+		return nil, false, e.fileCache.Delete(fileCacheKey(module))
 	}
 	return
 }
 
-const magic = "WAZEVO"
+var magic = []byte{'W', 'A', 'Z', 'E', 'V', 'O'}
 
 func serializeCompiledModule(wazeroVersion string, cm *compiledModule) io.Reader {
 	buf := bytes.NewBuffer(nil)
 	// First 6 byte: WAZEVO header.
-	buf.WriteString(magic)
+	buf.Write(magic)
 	// Next 1 byte: length of version:
 	buf.WriteByte(byte(len(wazeroVersion)))
 	// Version of wazero.
@@ -125,6 +145,19 @@ func serializeCompiledModule(wazeroVersion string, cm *compiledModule) io.Reader
 	buf.Write(u64.LeBytes(uint64(len(cm.executable))))
 	// Append the native code.
 	buf.Write(cm.executable)
+	if sm := cm.sourceMap; len(sm.executableOffsets) > 0 {
+		buf.WriteByte(1) // indicates that source map is present.
+		l := len(sm.wasmBinaryOffsets)
+		buf.Write(u64.LeBytes(uint64(l)))
+		executableAddr := uintptr(unsafe.Pointer(&cm.executable[0]))
+		for i := 0; i < l; i++ {
+			buf.Write(u64.LeBytes(sm.wasmBinaryOffsets[i]))
+			// executableOffsets is absolute address, so we need to subtract executableAddr.
+			buf.Write(u64.LeBytes(uint64(sm.executableOffsets[i] - executableAddr)))
+		}
+	} else {
+		buf.WriteByte(0) // indicates that source map is not present.
+	}
 	return bytes.NewReader(buf.Bytes())
 }
 
@@ -143,11 +176,9 @@ func deserializeCompiledModule(wazeroVersion string, reader io.ReadCloser) (cm *
 		return nil, false, fmt.Errorf("compilationcache: invalid header length: %d", n)
 	}
 
-	for i := 0; i < len(magic); i++ {
-		if magic[i] != header[i] {
-			return nil, false, fmt.Errorf(
-				"compilationcache: invalid magic number: got %s but want %s", magic, header[:len(magic)])
-		}
+	if !bytes.Equal(header[:len(magic)], magic) {
+		return nil, false, fmt.Errorf(
+			"compilationcache: invalid magic number: got %s but want %s", magic, header[:len(magic)])
 	}
 
 	// Check the version compatibility.
@@ -163,7 +194,7 @@ func deserializeCompiledModule(wazeroVersion string, reader io.ReadCloser) (cm *
 	}
 
 	functionsNum := binary.LittleEndian.Uint32(header[len(header)-4:])
-	cm = &compiledModule{functionOffsets: make([]int, functionsNum)}
+	cm = &compiledModule{functionOffsets: make([]int, functionsNum), executables: &executables{}}
 
 	var eightBytes [8]byte
 	for i := uint32(0); i < functionsNum; i++ {
@@ -203,6 +234,35 @@ func deserializeCompiledModule(wazeroVersion string, reader io.ReadCloser) (cm *
 		}
 		cm.executable = executable
 	}
+
+	if _, err := io.ReadFull(reader, eightBytes[:1]); err != nil {
+		return nil, false, fmt.Errorf("compilationcache: error reading source map presence: %v", err)
+	}
+
+	if eightBytes[0] == 1 {
+		sm := &cm.sourceMap
+		sourceMapLen, err := readUint64(reader, &eightBytes)
+		if err != nil {
+			err = fmt.Errorf("compilationcache: error reading source map length: %v", err)
+			return nil, false, err
+		}
+		executableOffset := uintptr(unsafe.Pointer(&cm.executable[0]))
+		for i := uint64(0); i < sourceMapLen; i++ {
+			wasmBinaryOffset, err := readUint64(reader, &eightBytes)
+			if err != nil {
+				err = fmt.Errorf("compilationcache: error reading source map[%d] wasm binary offset: %v", i, err)
+				return nil, false, err
+			}
+			executableRelativeOffset, err := readUint64(reader, &eightBytes)
+			if err != nil {
+				err = fmt.Errorf("compilationcache: error reading source map[%d] executable offset: %v", i, err)
+				return nil, false, err
+			}
+			sm.wasmBinaryOffsets = append(sm.wasmBinaryOffsets, wasmBinaryOffset)
+			// executableOffsets is absolute address, so we need to add executableOffset.
+			sm.executableOffsets = append(sm.executableOffsets, uintptr(executableRelativeOffset)+executableOffset)
+		}
+	}
 	return
 }
 
diff --git a/internal/engine/wazevo/frontend/frontend.go b/internal/engine/wazevo/frontend/frontend.go
index 0ce80220..1c54c8b4 100644
--- a/internal/engine/wazevo/frontend/frontend.go
+++ b/internal/engine/wazevo/frontend/frontend.go
@@ -22,6 +22,9 @@ type Compiler struct {
 	listenerSignatures     map[*wasm.FunctionType][2]*ssa.Signature
 	memoryGrowSig          ssa.Signature
 	checkModuleExitCodeSig ssa.Signature
+	tableGrowSig           ssa.Signature
+	refFuncSig             ssa.Signature
+	memmoveSig             ssa.Signature
 	checkModuleExitCodeArg [1]ssa.Value
 	ensureTermination      bool
 
@@ -96,7 +99,7 @@ func (c *Compiler) declareSignatures(listenerOn bool) {
 		ID: begin,
 		// Takes execution context and the page size to grow.
 		Params: []ssa.Type{ssa.TypeI64, ssa.TypeI32},
-		// Returns the new size.
+		// Returns the previous page size.
 		Results: []ssa.Type{ssa.TypeI32},
 	}
 	c.ssaBuilder.DeclareSignature(&c.memoryGrowSig)
@@ -107,6 +110,29 @@ func (c *Compiler) declareSignatures(listenerOn bool) {
 		Params: []ssa.Type{ssa.TypeI64},
 	}
 	c.ssaBuilder.DeclareSignature(&c.checkModuleExitCodeSig)
+
+	c.tableGrowSig = ssa.Signature{
+		ID:     c.checkModuleExitCodeSig.ID + 1,
+		Params: []ssa.Type{ssa.TypeI64 /* exec context */, ssa.TypeI32 /* table index */, ssa.TypeI32 /* num */, ssa.TypeI64 /* ref */},
+		// Returns the previous size.
+		Results: []ssa.Type{ssa.TypeI32},
+	}
+	c.ssaBuilder.DeclareSignature(&c.tableGrowSig)
+
+	c.refFuncSig = ssa.Signature{
+		ID:     c.tableGrowSig.ID + 1,
+		Params: []ssa.Type{ssa.TypeI64 /* exec context */, ssa.TypeI32 /* func index */},
+		// Returns the function reference.
+		Results: []ssa.Type{ssa.TypeI64},
+	}
+	c.ssaBuilder.DeclareSignature(&c.refFuncSig)
+
+	c.memmoveSig = ssa.Signature{
+		ID: c.refFuncSig.ID + 1,
+		// dst, src, and the byte count.
+		Params: []ssa.Type{ssa.TypeI64, ssa.TypeI64, ssa.TypeI32},
+	}
+	c.ssaBuilder.DeclareSignature(&c.memmoveSig)
 }
 
 // SignatureForWasmFunctionType returns the ssa.Signature for the given wasm.FunctionType.
@@ -255,7 +281,9 @@ func (c *Compiler) declareWasmGlobal(typ wasm.ValueType, mutable bool) {
 	switch typ {
 	case wasm.ValueTypeI32:
 		st = ssa.TypeI32
-	case wasm.ValueTypeI64:
+	case wasm.ValueTypeI64,
+		// Both externref and funcref are represented as I64 since we only support 64-bit platforms.
+		wasm.ValueTypeExternref, wasm.ValueTypeFuncref:
 		st = ssa.TypeI64
 	case wasm.ValueTypeF32:
 		st = ssa.TypeF32
@@ -280,7 +308,9 @@ func WasmTypeToSSAType(vt wasm.ValueType) ssa.Type {
 	switch vt {
 	case wasm.ValueTypeI32:
 		return ssa.TypeI32
-	case wasm.ValueTypeI64:
+	case wasm.ValueTypeI64,
+		// Both externref and funcref are represented as I64 since we only support 64-bit platforms.
+		wasm.ValueTypeExternref, wasm.ValueTypeFuncref:
 		return ssa.TypeI64
 	case wasm.ValueTypeF32:
 		return ssa.TypeF32
diff --git a/internal/engine/wazevo/frontend/lower.go b/internal/engine/wazevo/frontend/lower.go
index 55816d97..fda107bd 100644
--- a/internal/engine/wazevo/frontend/lower.go
+++ b/internal/engine/wazevo/frontend/lower.go
@@ -4,7 +4,6 @@ import (
 	"encoding/binary"
 	"fmt"
 	"math"
-	"strconv"
 	"strings"
 
 	"github.com/tetratelabs/wazero/api"
@@ -540,8 +539,343 @@ func (c *Compiler) lowerCurrentOpcode() {
 				true,
 			).Insert(builder).Return()
 			state.push(ret)
+
+		case wasm.OpcodeMiscTableSize:
+			tableIndex := c.readI32u()
+			if state.unreachable {
+				break
+			}
+
+			// Load the table.
+			loadTableInstancePtr := builder.AllocateInstruction()
+			loadTableInstancePtr.AsLoad(c.moduleCtxPtrValue, c.offset.TableOffset(int(tableIndex)).U32(), ssa.TypeI64)
+			builder.InsertInstruction(loadTableInstancePtr)
+			tableInstancePtr := loadTableInstancePtr.Return()
+
+			// Load the table's length.
+			loadTableLen := builder.AllocateInstruction().
+				AsLoad(tableInstancePtr, tableInstanceLenOffset, ssa.TypeI32).
+				Insert(builder)
+			state.push(loadTableLen.Return())
+
+		case wasm.OpcodeMiscTableGrow:
+			tableIndex := c.readI32u()
+			if state.unreachable {
+				break
+			}
+
+			c.storeCallerModuleContext()
+
+			tableIndexVal := builder.AllocateInstruction().AsIconst32(tableIndex).Insert(builder).Return()
+
+			num := state.pop()
+			r := state.pop()
+
+			tableGrowPtr := builder.AllocateInstruction().
+				AsLoad(c.execCtxPtrValue,
+					wazevoapi.ExecutionContextOffsetTableGrowTrampolineAddress.U32(),
+					ssa.TypeI64,
+				).Insert(builder).Return()
+
+			// TODO: reuse the slice.
+			args := []ssa.Value{c.execCtxPtrValue, tableIndexVal, num, r}
+			callGrowRet := builder.
+				AllocateInstruction().
+				AsCallIndirect(tableGrowPtr, &c.tableGrowSig, args).
+				Insert(builder).Return()
+			state.push(callGrowRet)
+
+		case wasm.OpcodeMiscTableCopy:
+			dstTableIndex := c.readI32u()
+			srcTableIndex := c.readI32u()
+			if state.unreachable {
+				break
+			}
+
+			copySize := builder.
+				AllocateInstruction().AsUExtend(state.pop(), 32, 64).Insert(builder).Return()
+			srcOffset := builder.
+				AllocateInstruction().AsUExtend(state.pop(), 32, 64).Insert(builder).Return()
+			dstOffset := builder.
+				AllocateInstruction().AsUExtend(state.pop(), 32, 64).Insert(builder).Return()
+
+			// Out of bounds check.
+			dstTableInstancePtr := c.boundsCheckInTable(dstTableIndex, dstOffset, copySize)
+			srcTableInstancePtr := c.boundsCheckInTable(srcTableIndex, srcOffset, copySize)
+
+			dstTableBaseAddr := c.loadTableBaseAddr(dstTableInstancePtr)
+			srcTableBaseAddr := c.loadTableBaseAddr(srcTableInstancePtr)
+
+			three := builder.AllocateInstruction().AsIconst64(3).Insert(builder).Return()
+
+			dstOffsetInBytes := builder.AllocateInstruction().AsIshl(dstOffset, three).Insert(builder).Return()
+			dstAddr := builder.AllocateInstruction().AsIadd(dstTableBaseAddr, dstOffsetInBytes).Insert(builder).Return()
+			srcOffsetInBytes := builder.AllocateInstruction().AsIshl(srcOffset, three).Insert(builder).Return()
+			srcAddr := builder.AllocateInstruction().AsIadd(srcTableBaseAddr, srcOffsetInBytes).Insert(builder).Return()
+
+			copySizeInBytes := builder.AllocateInstruction().AsIshl(copySize, three).Insert(builder).Return()
+			c.callMemmove(dstAddr, srcAddr, copySizeInBytes)
+
+		case wasm.OpcodeMiscMemoryCopy:
+			state.pc += 2 // +2 to skip two memory indexes which are fixed to zero.
+			if state.unreachable {
+				break
+			}
+
+			copySize := builder.
+				AllocateInstruction().AsUExtend(state.pop(), 32, 64).Insert(builder).Return()
+			srcOffset := builder.
+				AllocateInstruction().AsUExtend(state.pop(), 32, 64).Insert(builder).Return()
+			dstOffset := builder.
+				AllocateInstruction().AsUExtend(state.pop(), 32, 64).Insert(builder).Return()
+
+			// Out of bounds check.
+			memLen := c.getMemoryLenValue(false)
+			c.boundsCheckInMemory(memLen, dstOffset, copySize)
+			c.boundsCheckInMemory(memLen, srcOffset, copySize)
+
+			memBase := c.getMemoryBaseValue(false)
+			dstAddr := builder.AllocateInstruction().AsIadd(memBase, dstOffset).Insert(builder).Return()
+			srcAddr := builder.AllocateInstruction().AsIadd(memBase, srcOffset).Insert(builder).Return()
+
+			c.callMemmove(dstAddr, srcAddr, copySize)
+
+		case wasm.OpcodeMiscTableFill:
+			tableIndex := c.readI32u()
+			if state.unreachable {
+				break
+			}
+			fillSize := state.pop()
+			value := state.pop()
+			offset := state.pop()
+
+			fillSizeExt := builder.
+				AllocateInstruction().AsUExtend(fillSize, 32, 64).Insert(builder).Return()
+			offsetExt := builder.
+				AllocateInstruction().AsUExtend(offset, 32, 64).Insert(builder).Return()
+			tableInstancePtr := c.boundsCheckInTable(tableIndex, offsetExt, fillSizeExt)
+
+			three := builder.AllocateInstruction().AsIconst64(3).Insert(builder).Return()
+			offsetInBytes := builder.AllocateInstruction().AsIshl(offsetExt, three).Insert(builder).Return()
+			fillSizeInBytes := builder.AllocateInstruction().AsIshl(fillSizeExt, three).Insert(builder).Return()
+
+			// Calculate the base address of the table.
+			tableBaseAddr := c.loadTableBaseAddr(tableInstancePtr)
+			addr := builder.AllocateInstruction().AsIadd(tableBaseAddr, offsetInBytes).Insert(builder).Return()
+
+			// Prepare the loop and following block.
+			beforeLoop := builder.AllocateBasicBlock()
+			loopBlk := builder.AllocateBasicBlock()
+			loopVar := loopBlk.AddParam(builder, ssa.TypeI64)
+			followingBlk := builder.AllocateBasicBlock()
+
+			// Uses the copy trick for faster filling buffer like memory.fill, but in this case we copy 8 bytes at a time.
+			// 	buf := memoryInst.Buffer[offset : offset+fillSize]
+			// 	buf[0:8] = value
+			// 	for i := 8; i < fillSize; i *= 2 { Begin with 8 bytes.
+			// 		copy(buf[i:], buf[:i])
+			// 	}
+
+			// Insert the jump to the beforeLoop block; If the fillSize is zero, then jump to the following block to skip entire logics.
+			zero := builder.AllocateInstruction().AsIconst64(0).Insert(builder).Return()
+			ifFillSizeZero := builder.AllocateInstruction().AsIcmp(fillSizeExt, zero, ssa.IntegerCmpCondEqual).
+				Insert(builder).Return()
+			builder.AllocateInstruction().AsBrnz(ifFillSizeZero, nil, followingBlk).Insert(builder)
+			c.insertJumpToBlock(nil, beforeLoop)
+
+			// buf[0:8] = value
+			builder.SetCurrentBlock(beforeLoop)
+			builder.AllocateInstruction().AsStore(ssa.OpcodeStore, value, addr, 0).Insert(builder)
+			initValue := builder.AllocateInstruction().AsIconst64(8).Insert(builder).Return()
+			c.insertJumpToBlock([]ssa.Value{initValue}, loopBlk) // TODO: reuse the slice.
+
+			builder.SetCurrentBlock(loopBlk)
+			dstAddr := builder.AllocateInstruction().AsIadd(addr, loopVar).Insert(builder).Return()
+
+			// If loopVar*2 > fillSizeInBytes, then count must be fillSizeInBytes-loopVar.
+			var count ssa.Value
+			{
+				loopVarDoubled := builder.AllocateInstruction().AsIadd(loopVar, loopVar).Insert(builder).Return()
+				loopVarDoubledLargerThanFillSize := builder.
+					AllocateInstruction().AsIcmp(loopVarDoubled, fillSizeInBytes, ssa.IntegerCmpCondUnsignedGreaterThanOrEqual).
+					Insert(builder).Return()
+				diff := builder.AllocateInstruction().AsIsub(fillSizeInBytes, loopVar).Insert(builder).Return()
+				count = builder.AllocateInstruction().AsSelect(loopVarDoubledLargerThanFillSize, diff, loopVar).Insert(builder).Return()
+			}
+
+			c.callMemmove(dstAddr, addr, count)
+
+			shiftAmount := builder.AllocateInstruction().AsIconst64(1).Insert(builder).Return()
+			newLoopVar := builder.AllocateInstruction().AsIshl(loopVar, shiftAmount).Insert(builder).Return()
+			loopVarLessThanFillSize := builder.AllocateInstruction().
+				AsIcmp(newLoopVar, fillSizeInBytes, ssa.IntegerCmpCondUnsignedLessThan).Insert(builder).Return()
+
+			builder.AllocateInstruction().
+				AsBrnz(loopVarLessThanFillSize, []ssa.Value{newLoopVar}, loopBlk). // TODO: reuse the slice.
+				Insert(builder)
+
+			c.insertJumpToBlock(nil, followingBlk)
+			builder.SetCurrentBlock(followingBlk)
+
+			builder.Seal(beforeLoop)
+			builder.Seal(loopBlk)
+			builder.Seal(followingBlk)
+
+		case wasm.OpcodeMiscMemoryFill:
+			state.pc++ // Skip the memory index which is fixed to zero.
+			if state.unreachable {
+				break
+			}
+
+			fillSize := builder.
+				AllocateInstruction().AsUExtend(state.pop(), 32, 64).Insert(builder).Return()
+			value := state.pop()
+			offset := builder.
+				AllocateInstruction().AsUExtend(state.pop(), 32, 64).Insert(builder).Return()
+
+			// Out of bounds check.
+			c.boundsCheckInMemory(c.getMemoryLenValue(false), offset, fillSize)
+
+			// Calculate the base address:
+			addr := builder.AllocateInstruction().AsIadd(c.getMemoryBaseValue(false), offset).Insert(builder).Return()
+
+			// Uses the copy trick for faster filling buffer: https://gist.github.com/taylorza/df2f89d5f9ab3ffd06865062a4cf015d
+			// 	buf := memoryInst.Buffer[offset : offset+fillSize]
+			// 	buf[0] = value
+			// 	for i := 1; i < fillSize; i *= 2 {
+			// 		copy(buf[i:], buf[:i])
+			// 	}
+
+			// Prepare the loop and following block.
+			beforeLoop := builder.AllocateBasicBlock()
+			loopBlk := builder.AllocateBasicBlock()
+			loopVar := loopBlk.AddParam(builder, ssa.TypeI64)
+			followingBlk := builder.AllocateBasicBlock()
+
+			// Insert the jump to the beforeLoop block; If the fillSize is zero, then jump to the following block to skip entire logics.
+			zero := builder.AllocateInstruction().AsIconst64(0).Insert(builder).Return()
+			ifFillSizeZero := builder.AllocateInstruction().AsIcmp(fillSize, zero, ssa.IntegerCmpCondEqual).
+				Insert(builder).Return()
+			builder.AllocateInstruction().AsBrnz(ifFillSizeZero, nil, followingBlk).Insert(builder)
+			c.insertJumpToBlock(nil, beforeLoop)
+
+			// buf[0] = value
+			builder.SetCurrentBlock(beforeLoop)
+			builder.AllocateInstruction().AsStore(ssa.OpcodeIstore8, value, addr, 0).Insert(builder)
+			initValue := builder.AllocateInstruction().AsIconst64(1).Insert(builder).Return()
+			c.insertJumpToBlock([]ssa.Value{initValue}, loopBlk) // TODO: reuse the slice.
+
+			builder.SetCurrentBlock(loopBlk)
+			dstAddr := builder.AllocateInstruction().AsIadd(addr, loopVar).Insert(builder).Return()
+
+			// If loopVar*2 > fillSizeExt, then count must be fillSizeExt-loopVar.
+			var count ssa.Value
+			{
+				loopVarDoubled := builder.AllocateInstruction().AsIadd(loopVar, loopVar).Insert(builder).Return()
+				loopVarDoubledLargerThanFillSize := builder.
+					AllocateInstruction().AsIcmp(loopVarDoubled, fillSize, ssa.IntegerCmpCondUnsignedGreaterThanOrEqual).
+					Insert(builder).Return()
+				diff := builder.AllocateInstruction().AsIsub(fillSize, loopVar).Insert(builder).Return()
+				count = builder.AllocateInstruction().AsSelect(loopVarDoubledLargerThanFillSize, diff, loopVar).Insert(builder).Return()
+			}
+
+			c.callMemmove(dstAddr, addr, count)
+
+			shiftAmount := builder.AllocateInstruction().AsIconst64(1).Insert(builder).Return()
+			newLoopVar := builder.AllocateInstruction().AsIshl(loopVar, shiftAmount).Insert(builder).Return()
+			loopVarLessThanFillSize := builder.AllocateInstruction().
+				AsIcmp(newLoopVar, fillSize, ssa.IntegerCmpCondUnsignedLessThan).Insert(builder).Return()
+
+			builder.AllocateInstruction().
+				AsBrnz(loopVarLessThanFillSize, []ssa.Value{newLoopVar}, loopBlk). // TODO: reuse the slice.
+				Insert(builder)
+
+			c.insertJumpToBlock(nil, followingBlk)
+			builder.SetCurrentBlock(followingBlk)
+
+			builder.Seal(beforeLoop)
+			builder.Seal(loopBlk)
+			builder.Seal(followingBlk)
+
+		case wasm.OpcodeMiscMemoryInit:
+			index := c.readI32u()
+			state.pc++ // Skip the memory index which is fixed to zero.
+			if state.unreachable {
+				break
+			}
+
+			copySize := builder.
+				AllocateInstruction().AsUExtend(state.pop(), 32, 64).Insert(builder).Return()
+			offsetInDataInstance := builder.
+				AllocateInstruction().AsUExtend(state.pop(), 32, 64).Insert(builder).Return()
+			offsetInMemory := builder.
+				AllocateInstruction().AsUExtend(state.pop(), 32, 64).Insert(builder).Return()
+
+			dataInstPtr := c.dataOrElementInstanceAddr(index, c.offset.DataInstances1stElement)
+
+			// Bounds check.
+			c.boundsCheckInMemory(c.getMemoryLenValue(false), offsetInMemory, copySize)
+			c.boundsCheckInDataOrElementInstance(dataInstPtr, offsetInDataInstance, copySize, wazevoapi.ExitCodeMemoryOutOfBounds)
+
+			dataInstBaseAddr := builder.AllocateInstruction().AsLoad(dataInstPtr, 0, ssa.TypeI64).Insert(builder).Return()
+			srcAddr := builder.AllocateInstruction().AsIadd(dataInstBaseAddr, offsetInDataInstance).Insert(builder).Return()
+
+			memBase := c.getMemoryBaseValue(false)
+			dstAddr := builder.AllocateInstruction().AsIadd(memBase, offsetInMemory).Insert(builder).Return()
+
+			c.callMemmove(dstAddr, srcAddr, copySize)
+
+		case wasm.OpcodeMiscTableInit:
+			elemIndex := c.readI32u()
+			tableIndex := c.readI32u()
+			if state.unreachable {
+				break
+			}
+
+			copySize := builder.
+				AllocateInstruction().AsUExtend(state.pop(), 32, 64).Insert(builder).Return()
+			offsetInElementInstance := builder.
+				AllocateInstruction().AsUExtend(state.pop(), 32, 64).Insert(builder).Return()
+			offsetInTable := builder.
+				AllocateInstruction().AsUExtend(state.pop(), 32, 64).Insert(builder).Return()
+
+			elemInstPtr := c.dataOrElementInstanceAddr(elemIndex, c.offset.ElementInstances1stElement)
+
+			// Bounds check.
+			tableInstancePtr := c.boundsCheckInTable(tableIndex, offsetInTable, copySize)
+			c.boundsCheckInDataOrElementInstance(elemInstPtr, offsetInElementInstance, copySize, wazevoapi.ExitCodeTableOutOfBounds)
+
+			three := builder.AllocateInstruction().AsIconst64(3).Insert(builder).Return()
+			// Calculates the destination address in the table.
+			tableOffsetInBytes := builder.AllocateInstruction().AsIshl(offsetInTable, three).Insert(builder).Return()
+			tableBaseAddr := c.loadTableBaseAddr(tableInstancePtr)
+			dstAddr := builder.AllocateInstruction().AsIadd(tableBaseAddr, tableOffsetInBytes).Insert(builder).Return()
+
+			// Calculates the source address in the element instance.
+			srcOffsetInBytes := builder.AllocateInstruction().AsIshl(offsetInElementInstance, three).Insert(builder).Return()
+			elemInstBaseAddr := builder.AllocateInstruction().AsLoad(elemInstPtr, 0, ssa.TypeI64).Insert(builder).Return()
+			srcAddr := builder.AllocateInstruction().AsIadd(elemInstBaseAddr, srcOffsetInBytes).Insert(builder).Return()
+
+			copySizeInBytes := builder.AllocateInstruction().AsIshl(copySize, three).Insert(builder).Return()
+			c.callMemmove(dstAddr, srcAddr, copySizeInBytes)
+
+		case wasm.OpcodeMiscElemDrop:
+			index := c.readI32u()
+			if state.unreachable {
+				break
+			}
+
+			c.dropDataOrElementInstance(index, c.offset.ElementInstances1stElement)
+
+		case wasm.OpcodeMiscDataDrop:
+			index := c.readI32u()
+			if state.unreachable {
+				break
+			}
+			c.dropDataOrElementInstance(index, c.offset.DataInstances1stElement)
+
 		default:
-			panic("Unknown MiscOp " + strconv.Itoa(int(miscOpUint)))
+			panic("Unknown MiscOp " + wasm.MiscInstructionName(miscOp))
 		}
 
 	case wasm.OpcodeI32ReinterpretF32:
@@ -831,7 +1165,7 @@ func (c *Compiler) lowerCurrentOpcode() {
 		c.storeCallerModuleContext()
 
 		pages := state.pop()
-		loadPtr := builder.AllocateInstruction().
+		memoryGrowPtr := builder.AllocateInstruction().
 			AsLoad(c.execCtxPtrValue,
 				wazevoapi.ExecutionContextOffsetMemoryGrowTrampolineAddress.U32(),
 				ssa.TypeI64,
@@ -842,7 +1176,7 @@ func (c *Compiler) lowerCurrentOpcode() {
 
 		callGrowRet := builder.
 			AllocateInstruction().
-			AsCallIndirect(loadPtr, &c.memoryGrowSig, args).
+			AsCallIndirect(memoryGrowPtr, &c.memoryGrowSig, args).
 			Insert(builder).Return()
 		state.push(callGrowRet)
 
@@ -863,7 +1197,6 @@ func (c *Compiler) lowerCurrentOpcode() {
 		if state.unreachable {
 			break
 		}
-
 		var opSize uint64
 		var opcode ssa.Opcode
 		switch op {
@@ -1372,6 +1705,18 @@ func (c *Compiler) lowerCurrentOpcode() {
 			load.AsLoad(addr, offset, ssa.TypeV128)
 			builder.InsertInstruction(load)
 			state.push(load.Return())
+		case wasm.OpcodeVecV128Store:
+			_, offset := c.readMemArg()
+			if state.unreachable {
+				break
+			}
+			value := state.pop()
+			baseAddr := state.pop()
+			addr := c.memOpSetup(baseAddr, uint64(offset), 16)
+			builder.AllocateInstruction().
+				AsStore(ssa.OpcodeStore, value, addr, offset).
+				Insert(builder)
+
 		case wasm.OpcodeVecV128Not:
 			if state.unreachable {
 				break
@@ -2399,9 +2744,188 @@ func (c *Compiler) lowerCurrentOpcode() {
 			v1 := state.pop()
 			ret := builder.AllocateInstruction().AsVUshr(v1, v2, lane).Insert(builder).Return()
 			state.push(ret)
+		case wasm.OpcodeVecI8x16ExtractLaneS, wasm.OpcodeVecI16x8ExtractLaneS:
+			state.pc++
+			if state.unreachable {
+				break
+			}
+			var lane ssa.VecLane
+			switch vecOp {
+			case wasm.OpcodeVecI8x16ExtractLaneS:
+				lane = ssa.VecLaneI8x16
+			case wasm.OpcodeVecI16x8ExtractLaneS:
+				lane = ssa.VecLaneI16x8
+			}
+			v1 := state.pop()
+			index := c.wasmFunctionBody[state.pc]
+			ext := builder.AllocateInstruction().AsExtractlane(v1, index, lane, true).Insert(builder).Return()
+			state.push(ext)
+		case wasm.OpcodeVecI8x16ExtractLaneU, wasm.OpcodeVecI16x8ExtractLaneU,
+			wasm.OpcodeVecI32x4ExtractLane, wasm.OpcodeVecI64x2ExtractLane,
+			wasm.OpcodeVecF32x4ExtractLane, wasm.OpcodeVecF64x2ExtractLane:
+			state.pc++ // Skip the immediate value.
+			if state.unreachable {
+				break
+			}
+			var lane ssa.VecLane
+			switch vecOp {
+			case wasm.OpcodeVecI8x16ExtractLaneU:
+				lane = ssa.VecLaneI8x16
+			case wasm.OpcodeVecI16x8ExtractLaneU:
+				lane = ssa.VecLaneI16x8
+			case wasm.OpcodeVecI32x4ExtractLane:
+				lane = ssa.VecLaneI32x4
+			case wasm.OpcodeVecI64x2ExtractLane:
+				lane = ssa.VecLaneI64x2
+			case wasm.OpcodeVecF32x4ExtractLane:
+				lane = ssa.VecLaneF32x4
+			case wasm.OpcodeVecF64x2ExtractLane:
+				lane = ssa.VecLaneF64x2
+			}
+			v1 := state.pop()
+			index := c.wasmFunctionBody[state.pc]
+			ext := builder.AllocateInstruction().AsExtractlane(v1, index, lane, false).Insert(builder).Return()
+			state.push(ext)
+		case wasm.OpcodeVecI8x16ReplaceLane, wasm.OpcodeVecI16x8ReplaceLane,
+			wasm.OpcodeVecI32x4ReplaceLane, wasm.OpcodeVecI64x2ReplaceLane,
+			wasm.OpcodeVecF32x4ReplaceLane, wasm.OpcodeVecF64x2ReplaceLane:
+			state.pc++
+			if state.unreachable {
+				break
+			}
+			var lane ssa.VecLane
+			switch vecOp {
+			case wasm.OpcodeVecI8x16ReplaceLane:
+				lane = ssa.VecLaneI8x16
+			case wasm.OpcodeVecI16x8ReplaceLane:
+				lane = ssa.VecLaneI16x8
+			case wasm.OpcodeVecI32x4ReplaceLane:
+				lane = ssa.VecLaneI32x4
+			case wasm.OpcodeVecI64x2ReplaceLane:
+				lane = ssa.VecLaneI64x2
+			case wasm.OpcodeVecF32x4ReplaceLane:
+				lane = ssa.VecLaneF32x4
+			case wasm.OpcodeVecF64x2ReplaceLane:
+				lane = ssa.VecLaneF64x2
+			}
+			v2 := state.pop()
+			v1 := state.pop()
+			index := c.wasmFunctionBody[state.pc]
+			ret := builder.AllocateInstruction().AsInsertlane(v1, v2, index, lane).Insert(builder).Return()
+			state.push(ret)
+		case wasm.OpcodeVecV128i8x16Shuffle:
+			state.pc++
+			if state.unreachable {
+				break
+			}
+			v2 := state.pop()
+			v1 := state.pop()
+			ret := builder.AllocateInstruction().AsShuffle(v1, v2, c.wasmFunctionBody[state.pc:state.pc+16]).Insert(builder).Return()
+			state.pc += 15
+			state.push(ret)
+
+		case wasm.OpcodeVecI8x16Swizzle:
+			if state.unreachable {
+				break
+			}
+			v2 := state.pop()
+			v1 := state.pop()
+			ret := builder.AllocateInstruction().AsSwizzle(v1, v2, ssa.VecLaneI8x16).Insert(builder).Return()
+			state.push(ret)
+
+		case wasm.OpcodeVecI8x16Splat,
+			wasm.OpcodeVecI16x8Splat,
+			wasm.OpcodeVecI32x4Splat,
+			wasm.OpcodeVecI64x2Splat,
+			wasm.OpcodeVecF32x4Splat,
+			wasm.OpcodeVecF64x2Splat:
+			if state.unreachable {
+				break
+			}
+			var lane ssa.VecLane
+			switch vecOp {
+			case wasm.OpcodeVecI8x16Splat:
+				lane = ssa.VecLaneI8x16
+			case wasm.OpcodeVecI16x8Splat:
+				lane = ssa.VecLaneI16x8
+			case wasm.OpcodeVecI32x4Splat:
+				lane = ssa.VecLaneI32x4
+			case wasm.OpcodeVecI64x2Splat:
+				lane = ssa.VecLaneI64x2
+			case wasm.OpcodeVecF32x4Splat:
+				lane = ssa.VecLaneF32x4
+			case wasm.OpcodeVecF64x2Splat:
+				lane = ssa.VecLaneF64x2
+			}
+			v1 := state.pop()
+			ret := builder.AllocateInstruction().AsSplat(v1, lane).Insert(builder).Return()
+			state.push(ret)
+
 		default:
 			panic("TODO: unsupported vector instruction: " + wasm.VectorInstructionName(vecOp))
 		}
+	case wasm.OpcodeRefFunc:
+		funcIndex := c.readI32u()
+		if state.unreachable {
+			break
+		}
+
+		c.storeCallerModuleContext()
+
+		funcIndexVal := builder.AllocateInstruction().AsIconst32(funcIndex).Insert(builder).Return()
+
+		refFuncPtr := builder.AllocateInstruction().
+			AsLoad(c.execCtxPtrValue,
+				wazevoapi.ExecutionContextOffsetRefFuncTrampolineAddress.U32(),
+				ssa.TypeI64,
+			).Insert(builder).Return()
+
+		// TODO: reuse the slice.
+		args := []ssa.Value{c.execCtxPtrValue, funcIndexVal}
+		refFuncRet := builder.
+			AllocateInstruction().
+			AsCallIndirect(refFuncPtr, &c.refFuncSig, args).
+			Insert(builder).Return()
+		state.push(refFuncRet)
+
+	case wasm.OpcodeRefNull:
+		c.loweringState.pc++ // skips the reference type as we treat both of them as i64(0).
+		if state.unreachable {
+			break
+		}
+		ret := builder.AllocateInstruction().AsIconst64(0).Insert(builder).Return()
+		state.push(ret)
+	case wasm.OpcodeRefIsNull:
+		if state.unreachable {
+			break
+		}
+		r := state.pop()
+		zero := builder.AllocateInstruction().AsIconst64(0).Insert(builder)
+		icmp := builder.AllocateInstruction().
+			AsIcmp(r, zero.Return(), ssa.IntegerCmpCondEqual).
+			Insert(builder).
+			Return()
+		state.push(icmp)
+	case wasm.OpcodeTableSet:
+		tableIndex := c.readI32u()
+		if state.unreachable {
+			break
+		}
+		r := state.pop()
+		targetOffsetInTable := state.pop()
+
+		elementAddr := c.lowerAccessTableWithBoundsCheck(tableIndex, targetOffsetInTable)
+		builder.AllocateInstruction().AsStore(ssa.OpcodeStore, r, elementAddr, 0).Insert(builder)
+
+	case wasm.OpcodeTableGet:
+		tableIndex := c.readI32u()
+		if state.unreachable {
+			break
+		}
+		targetOffsetInTable := state.pop()
+		elementAddr := c.lowerAccessTableWithBoundsCheck(tableIndex, targetOffsetInTable)
+		loaded := builder.AllocateInstruction().AsLoad(elementAddr, 0, ssa.TypeI64).Insert(builder).Return()
+		state.push(loaded)
 	default:
 		panic("TODO: unsupported in wazevo yet: " + wasm.InstructionName(op))
 	}
@@ -2420,16 +2944,12 @@ const (
 	tableInstanceLenOffset         = tableInstanceBaseAddressOffset + 8
 )
 
-func (c *Compiler) lowerCallIndirect(typeIndex, tableIndex uint32) {
+func (c *Compiler) lowerAccessTableWithBoundsCheck(tableIndex uint32, elementOffsetInTable ssa.Value) (elementAddress ssa.Value) {
 	builder := c.ssaBuilder
-	state := c.state()
-
-	targetOffsetInTable := state.pop()
 
 	// Load the table.
-	tableOffset := c.offset.TableOffset(int(tableIndex))
 	loadTableInstancePtr := builder.AllocateInstruction()
-	loadTableInstancePtr.AsLoad(c.moduleCtxPtrValue, tableOffset.U32(), ssa.TypeI64)
+	loadTableInstancePtr.AsLoad(c.moduleCtxPtrValue, c.offset.TableOffset(int(tableIndex)).U32(), ssa.TypeI64)
 	builder.InsertInstruction(loadTableInstancePtr)
 	tableInstancePtr := loadTableInstancePtr.Return()
 
@@ -2441,7 +2961,7 @@ func (c *Compiler) lowerCallIndirect(typeIndex, tableIndex uint32) {
 
 	// Compare the length and the target, and trap if out of bounds.
 	checkOOB := builder.AllocateInstruction()
-	checkOOB.AsIcmp(targetOffsetInTable, tableLen, ssa.IntegerCmpCondUnsignedGreaterThanOrEqual)
+	checkOOB.AsIcmp(elementOffsetInTable, tableLen, ssa.IntegerCmpCondUnsignedGreaterThanOrEqual)
 	builder.InsertInstruction(checkOOB)
 	exitIfOOB := builder.AllocateInstruction()
 	exitIfOOB.AsExitIfTrueWithCode(c.execCtxPtrValue, checkOOB.Return(), wazevoapi.ExitCodeTableOutOfBounds)
@@ -2458,14 +2978,23 @@ func (c *Compiler) lowerCallIndirect(typeIndex, tableIndex uint32) {
 	three := builder.AllocateInstruction()
 	three.AsIconst64(3)
 	builder.InsertInstruction(three)
-	multiplyBy8.AsIshl(targetOffsetInTable, three.Return())
+	multiplyBy8.AsIshl(elementOffsetInTable, three.Return())
 	builder.InsertInstruction(multiplyBy8)
 	targetOffsetInTableMultipliedBy8 := multiplyBy8.Return()
+
 	// Then add the multiplied value to the base which results in the address of the target function (*wazevo.functionInstance)
-	calcFunctionInstancePtrAddressInTable := builder.AllocateInstruction()
-	calcFunctionInstancePtrAddressInTable.AsIadd(tableBase, targetOffsetInTableMultipliedBy8)
-	builder.InsertInstruction(calcFunctionInstancePtrAddressInTable)
-	functionInstancePtrAddress := calcFunctionInstancePtrAddressInTable.Return()
+	calcElementAddressInTable := builder.AllocateInstruction()
+	calcElementAddressInTable.AsIadd(tableBase, targetOffsetInTableMultipliedBy8)
+	builder.InsertInstruction(calcElementAddressInTable)
+	return calcElementAddressInTable.Return()
+}
+
+func (c *Compiler) lowerCallIndirect(typeIndex, tableIndex uint32) {
+	builder := c.ssaBuilder
+	state := c.state()
+
+	elementOffsetInTable := state.pop()
+	functionInstancePtrAddress := c.lowerAccessTableWithBoundsCheck(tableIndex, elementOffsetInTable)
 	loadFunctionInstancePtr := builder.AllocateInstruction()
 	loadFunctionInstancePtr.AsLoad(functionInstancePtrAddress, 0, ssa.TypeI64)
 	builder.InsertInstruction(loadFunctionInstancePtr)
@@ -2582,6 +3111,18 @@ func (c *Compiler) memOpSetup(baseAddr ssa.Value, constOffset, operationSizeInBy
 	return addrCalc.Return()
 }
 
+func (c *Compiler) callMemmove(dst, src, size ssa.Value) {
+	args := []ssa.Value{dst, src, size} // TODO: reuse the slice.
+
+	builder := c.ssaBuilder
+	memmovePtr := builder.AllocateInstruction().
+		AsLoad(c.execCtxPtrValue,
+			wazevoapi.ExecutionContextOffsetMemmoveAddress.U32(),
+			ssa.TypeI64,
+		).Insert(builder).Return()
+	builder.AllocateInstruction().AsCallIndirect(memmovePtr, &c.memmoveSig, args).Insert(builder)
+}
+
 func (c *Compiler) reloadAfterCall() {
 	// Note that when these are not used in the following instructions, they will be optimized out.
 	// So in any ways, we define them!
@@ -2983,3 +3524,97 @@ func (c *Compiler) callListenerAfter() {
 		AsCallIndirect(afterListenerPtr, afterSig, args).
 		Insert(builder)
 }
+
+const (
+	elementOrDataInstanceLenOffset = 8
+	elementOrDataInstanceSize      = 24
+)
+
+// dropInstance inserts instructions to drop the element/data instance specified by the given index.
+func (c *Compiler) dropDataOrElementInstance(index uint32, firstItemOffset wazevoapi.Offset) {
+	builder := c.ssaBuilder
+	instPtr := c.dataOrElementInstanceAddr(index, firstItemOffset)
+
+	zero := builder.AllocateInstruction().AsIconst64(0).Insert(builder).Return()
+
+	// Clear the instance.
+	builder.AllocateInstruction().AsStore(ssa.OpcodeStore, zero, instPtr, 0).Insert(builder)
+	builder.AllocateInstruction().AsStore(ssa.OpcodeStore, zero, instPtr, elementOrDataInstanceLenOffset).Insert(builder)
+	builder.AllocateInstruction().AsStore(ssa.OpcodeStore, zero, instPtr, elementOrDataInstanceLenOffset+8).Insert(builder)
+}
+
+func (c *Compiler) dataOrElementInstanceAddr(index uint32, firstItemOffset wazevoapi.Offset) ssa.Value {
+	builder := c.ssaBuilder
+
+	_1stItemPtr := builder.
+		AllocateInstruction().
+		AsLoad(c.moduleCtxPtrValue, firstItemOffset.U32(), ssa.TypeI64).
+		Insert(builder).Return()
+
+	// Each data/element instance is a slice, so we need to multiply index by 16 to get the offset of the target instance.
+	index = index * elementOrDataInstanceSize
+	indexExt := builder.AllocateInstruction().AsIconst64(uint64(index)).Insert(builder).Return()
+	// Then, add the offset to the address of the instance.
+	instPtr := builder.AllocateInstruction().AsIadd(_1stItemPtr, indexExt).Insert(builder).Return()
+	return instPtr
+}
+
+func (c *Compiler) boundsCheckInDataOrElementInstance(instPtr, offsetInInstance, copySize ssa.Value, exitCode wazevoapi.ExitCode) {
+	builder := c.ssaBuilder
+	dataInstLen := builder.AllocateInstruction().
+		AsLoad(instPtr, elementOrDataInstanceLenOffset, ssa.TypeI64).
+		Insert(builder).Return()
+	ceil := builder.AllocateInstruction().AsIadd(offsetInInstance, copySize).Insert(builder).Return()
+	cmp := builder.AllocateInstruction().
+		AsIcmp(dataInstLen, ceil, ssa.IntegerCmpCondUnsignedLessThan).
+		Insert(builder).
+		Return()
+	builder.AllocateInstruction().
+		AsExitIfTrueWithCode(c.execCtxPtrValue, cmp, exitCode).
+		Insert(builder)
+}
+
+func (c *Compiler) boundsCheckInTable(tableIndex uint32, offset, size ssa.Value) (tableInstancePtr ssa.Value) {
+	builder := c.ssaBuilder
+	dstCeil := builder.AllocateInstruction().AsIadd(offset, size).Insert(builder).Return()
+
+	// Load the table.
+	tableInstancePtr = builder.AllocateInstruction().
+		AsLoad(c.moduleCtxPtrValue, c.offset.TableOffset(int(tableIndex)).U32(), ssa.TypeI64).
+		Insert(builder).Return()
+
+	// Load the table's length.
+	tableLen := builder.AllocateInstruction().
+		AsLoad(tableInstancePtr, tableInstanceLenOffset, ssa.TypeI32).Insert(builder).Return()
+	tableLenExt := builder.AllocateInstruction().AsUExtend(tableLen, 32, 64).Insert(builder).Return()
+
+	// Compare the length and the target, and trap if out of bounds.
+	checkOOB := builder.AllocateInstruction()
+	checkOOB.AsIcmp(tableLenExt, dstCeil, ssa.IntegerCmpCondUnsignedLessThan)
+	builder.InsertInstruction(checkOOB)
+	exitIfOOB := builder.AllocateInstruction()
+	exitIfOOB.AsExitIfTrueWithCode(c.execCtxPtrValue, checkOOB.Return(), wazevoapi.ExitCodeTableOutOfBounds)
+	builder.InsertInstruction(exitIfOOB)
+	return
+}
+
+func (c *Compiler) loadTableBaseAddr(tableInstancePtr ssa.Value) ssa.Value {
+	builder := c.ssaBuilder
+	loadTableBaseAddress := builder.
+		AllocateInstruction().
+		AsLoad(tableInstancePtr, tableInstanceBaseAddressOffset, ssa.TypeI64).
+		Insert(builder)
+	return loadTableBaseAddress.Return()
+}
+
+func (c *Compiler) boundsCheckInMemory(memLen, offset, size ssa.Value) {
+	builder := c.ssaBuilder
+	ceil := builder.AllocateInstruction().AsIadd(offset, size).Insert(builder).Return()
+	cmp := builder.AllocateInstruction().
+		AsIcmp(memLen, ceil, ssa.IntegerCmpCondUnsignedLessThan).
+		Insert(builder).
+		Return()
+	builder.AllocateInstruction().
+		AsExitIfTrueWithCode(c.execCtxPtrValue, cmp, wazevoapi.ExitCodeMemoryOutOfBounds).
+		Insert(builder)
+}
diff --git a/internal/engine/wazevo/memmove.go b/internal/engine/wazevo/memmove.go
new file mode 100644
index 00000000..88992210
--- /dev/null
+++ b/internal/engine/wazevo/memmove.go
@@ -0,0 +1,11 @@
+package wazevo
+
+import (
+	"reflect"
+	"unsafe"
+)
+
+//go:linkname memmove runtime.memmove
+func memmove(_, _ unsafe.Pointer, _ uintptr)
+
+var memmovPtr = reflect.ValueOf(memmove).Pointer()
diff --git a/internal/engine/wazevo/module_engine.go b/internal/engine/wazevo/module_engine.go
index 226181d0..7d202c77 100644
--- a/internal/engine/wazevo/module_engine.go
+++ b/internal/engine/wazevo/module_engine.go
@@ -52,7 +52,10 @@ type (
 	//      globals                                   []*wasm.GlobalInstance (optional)
 	//      typeIDsBegin                              &wasm.ModuleInstance.TypeIDs[0]  (optional)
 	//      tables                                    []*wasm.TableInstance  (optional)
-	// 	    TODO: add more fields, like tables, etc.
+	// 	    beforeListenerTrampolines1stElement       **byte                 (optional)
+	// 	    afterListenerTrampolines1stElement        **byte                 (optional)
+	//      dataInstances1stElement                   []wasm.DataInstance    (optional)
+	//      elementInstances1stElement                []wasm.ElementInstance (optional)
 	// 	}
 	//
 	// See wazevoapi.NewModuleContextOffsetData for the details of the offsets.
@@ -114,6 +117,12 @@ func (m *moduleEngine) setupOpaque() {
 	if afterListenerOffset := offsets.AfterListenerTrampolines1stElement; afterListenerOffset >= 0 {
 		binary.LittleEndian.PutUint64(opaque[afterListenerOffset:], uint64(uintptr(unsafe.Pointer(&m.parent.listenerAfterTrampolines[0]))))
 	}
+	if len(inst.DataInstances) > 0 {
+		binary.LittleEndian.PutUint64(opaque[offsets.DataInstances1stElement:], uint64(uintptr(unsafe.Pointer(&inst.DataInstances[0]))))
+	}
+	if len(inst.ElementInstances) > 0 {
+		binary.LittleEndian.PutUint64(opaque[offsets.ElementInstances1stElement:], uint64(uintptr(unsafe.Pointer(&inst.ElementInstances[0]))))
+	}
 }
 
 // NewFunction implements wasm.ModuleEngine.
@@ -144,7 +153,7 @@ func (m *moduleEngine) NewFunction(index wasm.Index) api.Function {
 		indexInModule:          index,
 		executable:             &p.executable[offset],
 		parent:                 m,
-		preambleExecutable:     m.parent.entryPreambles[typIndex],
+		preambleExecutable:     &m.parent.entryPreambles[typIndex][0],
 		sizeOfParamResultSlice: sizeOfParamResultSlice,
 		requiredParams:         typ.ParamNumInUint64,
 		numberOfResults:        typ.ResultNumInUint64,
@@ -153,6 +162,9 @@ func (m *moduleEngine) NewFunction(index wasm.Index) api.Function {
 	ce.execCtx.memoryGrowTrampolineAddress = &m.parent.sharedFunctions.memoryGrowExecutable[0]
 	ce.execCtx.stackGrowCallTrampolineAddress = &m.parent.sharedFunctions.stackGrowExecutable[0]
 	ce.execCtx.checkModuleExitCodeTrampolineAddress = &m.parent.sharedFunctions.checkModuleExitCode[0]
+	ce.execCtx.tableGrowTrampolineAddress = &m.parent.sharedFunctions.tableGrowExecutable[0]
+	ce.execCtx.refFuncTrampolineAddress = &m.parent.sharedFunctions.refFuncExecutable[0]
+	ce.execCtx.memmoveAddress = memmovPtr
 	ce.init()
 	return ce
 }
diff --git a/internal/engine/wazevo/ssa/instructions.go b/internal/engine/wazevo/ssa/instructions.go
index 1f94911a..7d60c35b 100644
--- a/internal/engine/wazevo/ssa/instructions.go
+++ b/internal/engine/wazevo/ssa/instructions.go
@@ -126,6 +126,15 @@ func (i *Instruction) Arg2WithLane() (Value, Value, VecLane) {
 	return i.v, i.v2, VecLane(i.u1)
 }
 
+// ShuffleData returns the first two arguments to this instruction and 2 uint64s `lo`, `hi`.
+//
+// Note: Each uint64 encodes a sequence of 8 bytes where each byte encodes a VecLane,
+// so that the 128bit integer `hi<<64|lo` packs a slice `[16]VecLane`,
+// where `lane[0]` is the least significant byte, and `lane[n]` is shifted to offset `n*8`.
+func (i *Instruction) ShuffleData() (v Value, v2 Value, lo uint64, hi uint64) {
+	return i.v, i.v2, i.u1, i.u2
+}
+
 // Arg3 returns the first three arguments to this instruction.
 func (i *Instruction) Arg3() (Value, Value, Value) {
 	return i.v, i.v2, i.v3
@@ -330,8 +339,9 @@ const (
 	// and y when 0: `v = bitselect c, x, y`.
 	OpcodeVbitselect
 
-	// OpcodeShuffle ...
-	// `v = shuffle a, b, mask`.
+	// OpcodeShuffle shuffles two vectors using the given 128-bit immediate: `v = shuffle imm, x, y`.
+	// For each byte in the immediate, a value i in [0, 15] selects the i-th byte in vector x;
+	// i in [16, 31] selects the (i-16)-th byte in vector y.
 	OpcodeShuffle
 
 	// OpcodeSelect chooses between two values based on a condition `c`: `v = Select c, x, y`.
@@ -898,6 +908,8 @@ var instructionSideEffects = [opcodeEnd]sideEffect{
 	OpcodeImul:               sideEffectNone,
 	OpcodeIsub:               sideEffectNone,
 	OpcodeIcmp:               sideEffectNone,
+	OpcodeExtractlane:        sideEffectNone,
+	OpcodeInsertlane:         sideEffectNone,
 	OpcodeBand:               sideEffectNone,
 	OpcodeBor:                sideEffectNone,
 	OpcodeBxor:               sideEffectNone,
@@ -923,6 +935,9 @@ var instructionSideEffects = [opcodeEnd]sideEffect{
 	OpcodeUwidenHigh:         sideEffectNone,
 	OpcodeSnarrow:            sideEffectNone,
 	OpcodeUnarrow:            sideEffectNone,
+	OpcodeSwizzle:            sideEffectNone,
+	OpcodeShuffle:            sideEffectNone,
+	OpcodeSplat:              sideEffectNone,
 	OpcodeFsub:               sideEffectNone,
 	OpcodeF32const:           sideEffectNone,
 	OpcodeF64const:           sideEffectNone,
@@ -1030,67 +1045,72 @@ func (i *Instruction) sideEffect() sideEffect {
 
 // instructionReturnTypes provides the function to determine the return types of an instruction.
 var instructionReturnTypes = [opcodeEnd]returnTypesFn{
-	OpcodeVbor:       returnTypesFnV128,
-	OpcodeVbxor:      returnTypesFnV128,
-	OpcodeVband:      returnTypesFnV128,
-	OpcodeVbnot:      returnTypesFnV128,
-	OpcodeVbandnot:   returnTypesFnV128,
-	OpcodeVbitselect: returnTypesFnV128,
-	OpcodeVanyTrue:   returnTypesFnI32,
-	OpcodeVallTrue:   returnTypesFnI32,
-	OpcodeVhighBits:  returnTypesFnV128,
-	OpcodeVIadd:      returnTypesFnV128,
-	OpcodeVSaddSat:   returnTypesFnV128,
-	OpcodeVUaddSat:   returnTypesFnV128,
-	OpcodeVIsub:      returnTypesFnV128,
-	OpcodeVSsubSat:   returnTypesFnV128,
-	OpcodeVUsubSat:   returnTypesFnV128,
-	OpcodeVIcmp:      returnTypesFnI32,
-	OpcodeVImin:      returnTypesFnV128,
-	OpcodeVUmin:      returnTypesFnV128,
-	OpcodeVImax:      returnTypesFnV128,
-	OpcodeVUmax:      returnTypesFnV128,
-	OpcodeVImul:      returnTypesFnV128,
-	OpcodeVAvgRound:  returnTypesFnV128,
-	OpcodeVIabs:      returnTypesFnV128,
-	OpcodeVIneg:      returnTypesFnV128,
-	OpcodeVIpopcnt:   returnTypesFnV128,
-	OpcodeVIshl:      returnTypesFnV128,
-	OpcodeVSshr:      returnTypesFnV128,
-	OpcodeVUshr:      returnTypesFnV128,
-	OpcodeBand:       returnTypesFnSingle,
-	OpcodeFcopysign:  returnTypesFnSingle,
-	OpcodeBitcast:    returnTypesFnSingle,
-	OpcodeBor:        returnTypesFnSingle,
-	OpcodeBxor:       returnTypesFnSingle,
-	OpcodeRotl:       returnTypesFnSingle,
-	OpcodeRotr:       returnTypesFnSingle,
-	OpcodeIshl:       returnTypesFnSingle,
-	OpcodeSshr:       returnTypesFnSingle,
-	OpcodeSdiv:       returnTypesFnSingle,
-	OpcodeSrem:       returnTypesFnSingle,
-	OpcodeUdiv:       returnTypesFnSingle,
-	OpcodeUrem:       returnTypesFnSingle,
-	OpcodeUshr:       returnTypesFnSingle,
-	OpcodeJump:       returnTypesFnNoReturns,
-	OpcodeUndefined:  returnTypesFnNoReturns,
-	OpcodeIconst:     returnTypesFnSingle,
-	OpcodeSelect:     returnTypesFnSingle,
-	OpcodeSExtend:    returnTypesFnSingle,
-	OpcodeUExtend:    returnTypesFnSingle,
-	OpcodeSwidenLow:  returnTypesFnV128,
-	OpcodeUwidenLow:  returnTypesFnV128,
-	OpcodeSwidenHigh: returnTypesFnV128,
-	OpcodeUwidenHigh: returnTypesFnV128,
-	OpcodeSnarrow:    returnTypesFnV128,
-	OpcodeUnarrow:    returnTypesFnV128,
-	OpcodeIreduce:    returnTypesFnSingle,
-	OpcodeFabs:       returnTypesFnSingle,
-	OpcodeSqrt:       returnTypesFnSingle,
-	OpcodeCeil:       returnTypesFnSingle,
-	OpcodeFloor:      returnTypesFnSingle,
-	OpcodeTrunc:      returnTypesFnSingle,
-	OpcodeNearest:    returnTypesFnSingle,
+	OpcodeVbor:        returnTypesFnV128,
+	OpcodeVbxor:       returnTypesFnV128,
+	OpcodeVband:       returnTypesFnV128,
+	OpcodeVbnot:       returnTypesFnV128,
+	OpcodeVbandnot:    returnTypesFnV128,
+	OpcodeVbitselect:  returnTypesFnV128,
+	OpcodeVanyTrue:    returnTypesFnI32,
+	OpcodeVallTrue:    returnTypesFnI32,
+	OpcodeVhighBits:   returnTypesFnV128,
+	OpcodeVIadd:       returnTypesFnV128,
+	OpcodeVSaddSat:    returnTypesFnV128,
+	OpcodeVUaddSat:    returnTypesFnV128,
+	OpcodeVIsub:       returnTypesFnV128,
+	OpcodeVSsubSat:    returnTypesFnV128,
+	OpcodeVUsubSat:    returnTypesFnV128,
+	OpcodeVIcmp:       returnTypesFnI32,
+	OpcodeVImin:       returnTypesFnV128,
+	OpcodeVUmin:       returnTypesFnV128,
+	OpcodeVImax:       returnTypesFnV128,
+	OpcodeVUmax:       returnTypesFnV128,
+	OpcodeVImul:       returnTypesFnV128,
+	OpcodeVAvgRound:   returnTypesFnV128,
+	OpcodeVIabs:       returnTypesFnV128,
+	OpcodeVIneg:       returnTypesFnV128,
+	OpcodeVIpopcnt:    returnTypesFnV128,
+	OpcodeVIshl:       returnTypesFnV128,
+	OpcodeVSshr:       returnTypesFnV128,
+	OpcodeVUshr:       returnTypesFnV128,
+	OpcodeExtractlane: returnTypesFnSingle,
+	OpcodeInsertlane:  returnTypesFnV128,
+	OpcodeBand:        returnTypesFnSingle,
+	OpcodeFcopysign:   returnTypesFnSingle,
+	OpcodeBitcast:     returnTypesFnSingle,
+	OpcodeBor:         returnTypesFnSingle,
+	OpcodeBxor:        returnTypesFnSingle,
+	OpcodeRotl:        returnTypesFnSingle,
+	OpcodeRotr:        returnTypesFnSingle,
+	OpcodeIshl:        returnTypesFnSingle,
+	OpcodeSshr:        returnTypesFnSingle,
+	OpcodeSdiv:        returnTypesFnSingle,
+	OpcodeSrem:        returnTypesFnSingle,
+	OpcodeUdiv:        returnTypesFnSingle,
+	OpcodeUrem:        returnTypesFnSingle,
+	OpcodeUshr:        returnTypesFnSingle,
+	OpcodeJump:        returnTypesFnNoReturns,
+	OpcodeUndefined:   returnTypesFnNoReturns,
+	OpcodeIconst:      returnTypesFnSingle,
+	OpcodeSelect:      returnTypesFnSingle,
+	OpcodeSExtend:     returnTypesFnSingle,
+	OpcodeUExtend:     returnTypesFnSingle,
+	OpcodeSwidenLow:   returnTypesFnV128,
+	OpcodeUwidenLow:   returnTypesFnV128,
+	OpcodeSwidenHigh:  returnTypesFnV128,
+	OpcodeUwidenHigh:  returnTypesFnV128,
+	OpcodeSnarrow:     returnTypesFnV128,
+	OpcodeUnarrow:     returnTypesFnV128,
+	OpcodeSwizzle:     returnTypesFnSingle,
+	OpcodeShuffle:     returnTypesFnV128,
+	OpcodeSplat:       returnTypesFnV128,
+	OpcodeIreduce:     returnTypesFnSingle,
+	OpcodeFabs:        returnTypesFnSingle,
+	OpcodeSqrt:        returnTypesFnSingle,
+	OpcodeCeil:        returnTypesFnSingle,
+	OpcodeFloor:       returnTypesFnSingle,
+	OpcodeTrunc:       returnTypesFnSingle,
+	OpcodeNearest:     returnTypesFnSingle,
 	OpcodeCallIndirect: func(b *builder, instr *Instruction) (t1 Type, ts []Type) {
 		sigID := SignatureID(instr.u1)
 		sig, ok := b.signatures[sigID]
@@ -1515,11 +1535,12 @@ func (i *Instruction) AsVFdiv(x, y Value, lane VecLane) *Instruction {
 }
 
 // AsImul initializes this instruction as an integer addition instruction with OpcodeImul.
-func (i *Instruction) AsImul(x, y Value) {
+func (i *Instruction) AsImul(x, y Value) *Instruction {
 	i.opcode = OpcodeImul
 	i.v = x
 	i.v2 = y
 	i.typ = x.Type()
+	return i
 }
 
 func (i *Instruction) Insert(b Builder) *Instruction {
@@ -1753,6 +1774,72 @@ func (i *Instruction) AsVSshr(x, amount Value, lane VecLane) *Instruction {
 	return i
 }
 
+// AsExtractlane initializes this instruction as an extract lane instruction with OpcodeExtractlane on vector.
+func (i *Instruction) AsExtractlane(x Value, index byte, lane VecLane, signed bool) *Instruction {
+	i.opcode = OpcodeExtractlane
+	i.v = x
+	// We do not have a field for signedness, but `index` is a byte,
+	// so we just encode the flag in the high bits of `u1`.
+	i.u1 = uint64(index)
+	if signed {
+		i.u1 = i.u1 | 1<<32
+	}
+	i.u2 = uint64(lane)
+	switch lane {
+	case VecLaneI8x16, VecLaneI16x8, VecLaneI32x4:
+		i.typ = TypeI32
+	case VecLaneI64x2:
+		i.typ = TypeI64
+	case VecLaneF32x4:
+		i.typ = TypeF32
+	case VecLaneF64x2:
+		i.typ = TypeF64
+	}
+	return i
+}
+
+// AsInsertlane initializes this instruction as an insert lane instruction with OpcodeInsertlane on vector.
+func (i *Instruction) AsInsertlane(x, y Value, index byte, lane VecLane) *Instruction {
+	i.opcode = OpcodeInsertlane
+	i.v = x
+	i.v2 = y
+	i.u1 = uint64(index)
+	i.u2 = uint64(lane)
+	i.typ = TypeV128
+	return i
+}
+
+// AsShuffle initializes this instruction as a shuffle instruction with OpcodeShuffle on vector.
+func (i *Instruction) AsShuffle(x, y Value, lane []byte) *Instruction {
+	i.opcode = OpcodeShuffle
+	i.v = x
+	i.v2 = y
+	// Encode the 16 bytes as 8 bytes in u1, and 8 bytes in u2.
+	i.u1 = uint64(lane[7])<<56 | uint64(lane[6])<<48 | uint64(lane[5])<<40 | uint64(lane[4])<<32 | uint64(lane[3])<<24 | uint64(lane[2])<<16 | uint64(lane[1])<<8 | uint64(lane[0])
+	i.u2 = uint64(lane[15])<<56 | uint64(lane[14])<<48 | uint64(lane[13])<<40 | uint64(lane[12])<<32 | uint64(lane[11])<<24 | uint64(lane[10])<<16 | uint64(lane[9])<<8 | uint64(lane[8])
+	i.typ = TypeV128
+	return i
+}
+
+// AsSwizzle initializes this instruction as an insert lane instruction with OpcodeSwizzle on vector.
+func (i *Instruction) AsSwizzle(x, y Value, lane VecLane) *Instruction {
+	i.opcode = OpcodeSwizzle
+	i.v = x
+	i.v2 = y
+	i.u1 = uint64(lane)
+	i.typ = TypeV128
+	return i
+}
+
+// AsSplat initializes this instruction as an insert lane instruction with OpcodeSplat on vector.
+func (i *Instruction) AsSplat(x Value, lane VecLane) *Instruction {
+	i.opcode = OpcodeSplat
+	i.v = x
+	i.u1 = uint64(lane)
+	i.typ = TypeV128
+	return i
+}
+
 // AsRotl initializes this instruction as a word rotate left instruction with OpcodeRotl.
 func (i *Instruction) AsRotl(x, amount Value) {
 	i.opcode = OpcodeRotl
@@ -1789,6 +1876,24 @@ func (i *Instruction) VFcmpData() (x, y Value, c FloatCmpCond, l VecLane) {
 	return i.v, i.v2, FloatCmpCond(i.u1), VecLane(i.u2)
 }
 
+// ExtractlaneData returns the operands and sign flag of Extractlane on vector.
+func (i *Instruction) ExtractlaneData() (x Value, index byte, signed bool, l VecLane) {
+	x = i.v
+	index = byte(0b00001111 & i.u1)
+	signed = i.u1>>32 != 0
+	l = VecLane(i.u2)
+	return
+}
+
+// InsertlaneData returns the operands and sign flag of Insertlane on vector.
+func (i *Instruction) InsertlaneData() (x, y Value, index byte, l VecLane) {
+	x = i.v
+	y = i.v2
+	index = byte(i.u1)
+	l = VecLane(i.u2)
+	return
+}
+
 // AsFadd initializes this instruction as a floating-point addition instruction with OpcodeFadd.
 func (i *Instruction) AsFadd(x, y Value) {
 	i.opcode = OpcodeFadd
@@ -1993,11 +2098,12 @@ func (i *Instruction) AsExitWithCode(ctx Value, code wazevoapi.ExitCode) {
 }
 
 // AsExitIfTrueWithCode initializes this instruction as a trap instruction with OpcodeExitIfTrueWithCode.
-func (i *Instruction) AsExitIfTrueWithCode(ctx, c Value, code wazevoapi.ExitCode) {
+func (i *Instruction) AsExitIfTrueWithCode(ctx, c Value, code wazevoapi.ExitCode) *Instruction {
 	i.opcode = OpcodeExitIfTrueWithCode
 	i.v = ctx
 	i.v2 = c
 	i.u1 = uint64(code)
+	return i
 }
 
 // ExitWithCodeData returns the context and exit code of OpcodeExitWithCode.
@@ -2079,11 +2185,12 @@ func (i *Instruction) AsBrz(v Value, args []Value, target BasicBlock) {
 }
 
 // AsBrnz initializes this instruction as a branch-if-not-zero instruction with OpcodeBrnz.
-func (i *Instruction) AsBrnz(v Value, args []Value, target BasicBlock) {
+func (i *Instruction) AsBrnz(v Value, args []Value, target BasicBlock) *Instruction {
 	i.opcode = OpcodeBrnz
 	i.v = v
 	i.vs = args
 	i.blk = target
+	return i
 }
 
 // AsBrTable initializes this instruction as a branch-table instruction with OpcodeBrTable.
@@ -2340,7 +2447,7 @@ func (i *Instruction) AsFvdemote(x Value, lane VecLane) *Instruction {
 }
 
 // AsSExtend initializes this instruction as a sign extension instruction with OpcodeSExtend.
-func (i *Instruction) AsSExtend(v Value, from, to byte) {
+func (i *Instruction) AsSExtend(v Value, from, to byte) *Instruction {
 	i.opcode = OpcodeSExtend
 	i.v = v
 	i.u1 = uint64(from)<<8 | uint64(to)
@@ -2349,10 +2456,11 @@ func (i *Instruction) AsSExtend(v Value, from, to byte) {
 	} else {
 		i.typ = TypeI32
 	}
+	return i
 }
 
 // AsUExtend initializes this instruction as an unsigned extension instruction with OpcodeUExtend.
-func (i *Instruction) AsUExtend(v Value, from, to byte) {
+func (i *Instruction) AsUExtend(v Value, from, to byte) *Instruction {
 	i.opcode = OpcodeUExtend
 	i.v = v
 	i.u1 = uint64(from)<<8 | uint64(to)
@@ -2361,6 +2469,7 @@ func (i *Instruction) AsUExtend(v Value, from, to byte) {
 	} else {
 		i.typ = TypeI32
 	}
+	return i
 }
 
 func (i *Instruction) ExtendData() (from, to byte, signed bool) {
@@ -2502,13 +2611,35 @@ func (i *Instruction) Format(b Builder) string {
 		OpcodeVFadd, OpcodeVFsub, OpcodeVFmul, OpcodeVFdiv,
 		OpcodeVIshl, OpcodeVSshr, OpcodeVUshr,
 		OpcodeVFmin, OpcodeVFmax, OpcodeVMinPseudo, OpcodeVMaxPseudo,
-		OpcodeSnarrow, OpcodeUnarrow:
+		OpcodeSnarrow, OpcodeUnarrow, OpcodeSwizzle:
 		instSuffix = fmt.Sprintf(".%s %s, %s", VecLane(i.u1), i.v.Format(b), i.v2.Format(b))
 	case OpcodeVIabs, OpcodeVIneg, OpcodeVIpopcnt, OpcodeVhighBits, OpcodeVallTrue, OpcodeVanyTrue,
 		OpcodeVFabs, OpcodeVFneg, OpcodeVSqrt, OpcodeVCeil, OpcodeVFloor, OpcodeVTrunc, OpcodeVNearest,
 		OpcodeVFcvtToUintSat, OpcodeVFcvtToSintSat, OpcodeVFcvtFromUint, OpcodeVFcvtFromSint,
-		OpcodeFvpromoteLow, OpcodeFvdemote, OpcodeSwidenLow, OpcodeUwidenLow, OpcodeSwidenHigh, OpcodeUwidenHigh:
+		OpcodeFvpromoteLow, OpcodeFvdemote, OpcodeSwidenLow, OpcodeUwidenLow, OpcodeSwidenHigh, OpcodeUwidenHigh,
+		OpcodeSplat:
 		instSuffix = fmt.Sprintf(".%s %s", VecLane(i.u1), i.v.Format(b))
+	case OpcodeExtractlane:
+		var signedness string
+		if i.u1 != 0 {
+			signedness = "signed"
+		} else {
+			signedness = "unsigned"
+		}
+		instSuffix = fmt.Sprintf(".%s %d, %s (%s)", VecLane(i.u2), 0x0000FFFF&i.u1, i.v.Format(b), signedness)
+	case OpcodeInsertlane:
+		instSuffix = fmt.Sprintf(".%s %d, %s, %s", VecLane(i.u2), i.u1, i.v.Format(b), i.v2.Format(b))
+	case OpcodeShuffle:
+		lanes := make([]byte, 16)
+		for idx := 0; idx < 8; idx++ {
+			lanes[idx] = byte(i.u1 >> (8 * idx))
+		}
+		for idx := 0; idx < 8; idx++ {
+			lanes[idx+8] = byte(i.u2 >> (8 * idx))
+		}
+		// Prints Shuffle.[0 1 2 3 4 5 6 7 ...] v2, v3
+		instSuffix = fmt.Sprintf(".%v %s, %s", lanes, i.v.Format(b), i.v2.Format(b))
+
 	default:
 		panic(fmt.Sprintf("TODO: format for %s", i.opcode))
 	}
diff --git a/internal/engine/wazevo/wazevoapi/debug_options.go b/internal/engine/wazevo/wazevoapi/debug_options.go
index a46031d6..69e9909a 100644
--- a/internal/engine/wazevo/wazevoapi/debug_options.go
+++ b/internal/engine/wazevo/wazevoapi/debug_options.go
@@ -76,9 +76,10 @@ const (
 
 type (
 	verifierState struct {
-		randomizedIndexes []int
-		r                 *rand.Rand
-		values            map[string]string
+		initialCompilationDone bool
+		maybeRandomizedIndexes []int
+		r                      *rand.Rand
+		values                 map[string]string
 	}
 	verifierStateContextKey struct{}
 	currentFunctionNameKey  struct{}
@@ -86,31 +87,36 @@ type (
 
 // NewDeterministicCompilationVerifierContext creates a new context with the deterministic compilation verifier used per wasm.Module.
 func NewDeterministicCompilationVerifierContext(ctx context.Context, localFunctions int) context.Context {
-	randomizedIndexes := make([]int, localFunctions)
-	for i := range randomizedIndexes {
-		randomizedIndexes[i] = i
+	maybeRandomizedIndexes := make([]int, localFunctions)
+	for i := range maybeRandomizedIndexes {
+		maybeRandomizedIndexes[i] = i
 	}
 	r := rand.New(rand.NewSource(time.Now().UnixNano()))
 	return context.WithValue(ctx, verifierStateContextKey{}, &verifierState{
-		r: r, randomizedIndexes: randomizedIndexes, values: map[string]string{},
+		r: r, maybeRandomizedIndexes: maybeRandomizedIndexes, values: map[string]string{},
 	})
 }
 
 // DeterministicCompilationVerifierRandomizeIndexes randomizes the indexes for the deterministic compilation verifier.
 // To get the randomized index, use DeterministicCompilationVerifierGetRandomizedLocalFunctionIndex.
 func DeterministicCompilationVerifierRandomizeIndexes(ctx context.Context) {
-	verifierCtx := ctx.Value(verifierStateContextKey{}).(*verifierState)
-	r := verifierCtx.r
-	r.Shuffle(len(verifierCtx.randomizedIndexes), func(i, j int) {
-		verifierCtx.randomizedIndexes[i], verifierCtx.randomizedIndexes[j] = verifierCtx.randomizedIndexes[j], verifierCtx.randomizedIndexes[i]
+	state := ctx.Value(verifierStateContextKey{}).(*verifierState)
+	if !state.initialCompilationDone {
+		// If this is the first attempt, we use the index as-is order.
+		state.initialCompilationDone = true
+		return
+	}
+	r := state.r
+	r.Shuffle(len(state.maybeRandomizedIndexes), func(i, j int) {
+		state.maybeRandomizedIndexes[i], state.maybeRandomizedIndexes[j] = state.maybeRandomizedIndexes[j], state.maybeRandomizedIndexes[i]
 	})
 }
 
 // DeterministicCompilationVerifierGetRandomizedLocalFunctionIndex returns the randomized index for the given `index`
 // which is assigned by DeterministicCompilationVerifierRandomizeIndexes.
 func DeterministicCompilationVerifierGetRandomizedLocalFunctionIndex(ctx context.Context, index int) int {
-	verifierCtx := ctx.Value(verifierStateContextKey{}).(*verifierState)
-	ret := verifierCtx.randomizedIndexes[index]
+	state := ctx.Value(verifierStateContextKey{}).(*verifierState)
+	ret := state.maybeRandomizedIndexes[index]
 	return ret
 }
 
diff --git a/internal/engine/wazevo/wazevoapi/exitcode.go b/internal/engine/wazevo/wazevoapi/exitcode.go
index 0ca364db..fd7c3989 100644
--- a/internal/engine/wazevo/wazevoapi/exitcode.go
+++ b/internal/engine/wazevo/wazevoapi/exitcode.go
@@ -24,6 +24,8 @@ const (
 	ExitCodeCallListenerAfter
 	ExitCodeCallGoModuleFunctionWithListener
 	ExitCodeCallGoFunctionWithListener
+	ExitCodeTableGrow
+	ExitCodeRefFunc
 	exitCodeMax
 )
 
@@ -68,6 +70,10 @@ func (e ExitCode) String() string {
 		return "call_go_function_with_listener"
 	case ExitCodeGrowMemory:
 		return "grow_memory"
+	case ExitCodeTableGrow:
+		return "table_grow"
+	case ExitCodeRefFunc:
+		return "ref_func"
 	}
 	panic("TODO")
 }
diff --git a/internal/engine/wazevo/wazevoapi/offsetdata.go b/internal/engine/wazevo/wazevoapi/offsetdata.go
index 372c5250..d21489b1 100644
--- a/internal/engine/wazevo/wazevoapi/offsetdata.go
+++ b/internal/engine/wazevo/wazevoapi/offsetdata.go
@@ -44,6 +44,11 @@ const (
 	ExecutionContextOffsetSavedRegistersBegin Offset = 96
 	// ExecutionContextOffsetGoFunctionCallCalleeModuleContextOpaque is an offset of `goFunctionCallCalleeModuleContextOpaque` field in wazevo.executionContext
 	ExecutionContextOffsetGoFunctionCallCalleeModuleContextOpaque Offset = 1120
+	// ExecutionContextOffsetTableGrowTrampolineAddress is an offset of `tableGrowTrampolineAddress` field in wazevo.executionContext
+	ExecutionContextOffsetTableGrowTrampolineAddress Offset = 1128
+	// ExecutionContextOffsetRefFuncTrampolineAddress is an offset of `refFuncTrampolineAddress` field in wazevo.executionContext
+	ExecutionContextOffsetRefFuncTrampolineAddress Offset = 1136
+	ExecutionContextOffsetMemmoveAddress           Offset = 1144
 )
 
 // ModuleContextOffsetData allows the compilers to get the information about offsets to the fields of wazevo.moduleContextOpaque,
@@ -58,7 +63,9 @@ type ModuleContextOffsetData struct {
 	TypeIDs1stElement,
 	TablesBegin,
 	BeforeListenerTrampolines1stElement,
-	AfterListenerTrampolines1stElement Offset
+	AfterListenerTrampolines1stElement,
+	DataInstances1stElement,
+	ElementInstances1stElement Offset
 }
 
 // ImportedFunctionOffset returns an offset of the i-th imported function.
@@ -180,6 +187,12 @@ func NewModuleContextOffsetData(m *wasm.Module, withListener bool) ModuleContext
 		ret.AfterListenerTrampolines1stElement = -1
 	}
 
+	ret.DataInstances1stElement = offset
+	offset += 8 // First element of DataInstances.
+
+	ret.ElementInstances1stElement = offset
+	offset += 8 // First element of ElementInstances.
+
 	ret.TotalSize = int(offset)
 	return ret
 }
diff --git a/internal/wasm/memory.go b/internal/wasm/memory.go
index e2d994f9..bce037e4 100644
--- a/internal/wasm/memory.go
+++ b/internal/wasm/memory.go
@@ -5,7 +5,6 @@ import (
 	"fmt"
 	"math"
 	"reflect"
-	"sync"
 	"unsafe"
 
 	"github.com/tetratelabs/wazero/api"
@@ -37,8 +36,6 @@ type MemoryInstance struct {
 
 	Buffer        []byte
 	Min, Cap, Max uint32
-	// mux is used to prevent overlapping calls to Grow.
-	mux sync.RWMutex
 	// definition is known at compile time.
 	definition api.MemoryDefinition
 }
@@ -180,10 +177,6 @@ func MemoryPagesToBytesNum(pages uint32) (bytesNum uint64) {
 
 // Grow implements the same method as documented on api.Memory.
 func (m *MemoryInstance) Grow(delta uint32) (result uint32, ok bool) {
-	// We take write-lock here as the following might result in a new slice
-	m.mux.Lock()
-	defer m.mux.Unlock()
-
 	currentPages := memoryBytesNumToPages(uint64(len(m.Buffer)))
 	if delta == 0 {
 		return currentPages, true
diff --git a/internal/wasm/store.go b/internal/wasm/store.go
index 4574b491..f15c19fc 100644
--- a/internal/wasm/store.go
+++ b/internal/wasm/store.go
@@ -164,18 +164,17 @@ func (m *ModuleInstance) GetFunctionTypeID(t *FunctionType) FunctionTypeID {
 }
 
 func (m *ModuleInstance) buildElementInstances(elements []ElementSegment) {
-	m.ElementInstances = make([]ElementInstance, len(elements))
+	m.ElementInstances = make([][]Reference, len(elements))
 	for i, elm := range elements {
 		if elm.Type == RefTypeFuncref && elm.Mode == ElementModePassive {
 			// Only passive elements can be access as element instances.
 			// See https://www.w3.org/TR/2022/WD-wasm-core-2-20220419/syntax/modules.html#element-segments
 			inits := elm.Init
-			elemInst := &m.ElementInstances[i]
-			elemInst.References = make([]Reference, len(inits))
-			elemInst.Type = RefTypeFuncref
+			inst := make([]Reference, len(inits))
+			m.ElementInstances[i] = inst
 			for j, idx := range inits {
 				if idx != ElementInitNullReference {
-					elemInst.References[j] = m.Engine.FunctionInstanceReference(idx)
+					inst[j] = m.Engine.FunctionInstanceReference(idx)
 				}
 			}
 		}
diff --git a/internal/wasm/table.go b/internal/wasm/table.go
index b202e8af..ac9fc498 100644
--- a/internal/wasm/table.go
+++ b/internal/wasm/table.go
@@ -3,7 +3,6 @@ package wasm
 import (
 	"fmt"
 	"math"
-	"sync"
 
 	"github.com/tetratelabs/wazero/api"
 	"github.com/tetratelabs/wazero/internal/leb128"
@@ -126,20 +125,12 @@ type TableInstance struct {
 
 	// Type is either RefTypeFuncref or RefTypeExternRef.
 	Type RefType
-
-	// mux is used to prevent overlapping calls to Grow.
-	mux sync.RWMutex
 }
 
 // ElementInstance represents an element instance in a module.
 //
 // See https://www.w3.org/TR/2022/WD-wasm-core-2-20220419/exec/runtime.html#element-instances
-type ElementInstance struct {
-	// References holds references whose type is either RefTypeFuncref or RefTypeExternref (unsupported).
-	References []Reference
-	// Type is the RefType of the references in this instance's References.
-	Type RefType
-}
+type ElementInstance = []Reference
 
 // Reference is the runtime representation of RefType which is either RefTypeFuncref or RefTypeExternref.
 type Reference = uintptr
@@ -314,10 +305,6 @@ func (m *Module) verifyImportGlobalI32(sectionID SectionID, sectionIdx Index, id
 //
 // https://www.w3.org/TR/2022/WD-wasm-core-2-20220419/exec/instructions.html#xref-syntax-instructions-syntax-instr-table-mathsf-table-grow-x
 func (t *TableInstance) Grow(delta uint32, initialRef Reference) (currentLen uint32) {
-	// We take write-lock here as the following might result in a new slice
-	t.mux.Lock()
-	defer t.mux.Unlock()
-
 	currentLen = uint32(len(t.References))
 	if delta == 0 {
 		return


Test patch:
diff --git a/internal/engine/compiler/compiler_post1_0_test.go b/internal/engine/compiler/compiler_post1_0_test.go
index 10a96d3b..7632b354 100644
--- a/internal/engine/compiler/compiler_post1_0_test.go
+++ b/internal/engine/compiler/compiler_post1_0_test.go
@@ -476,13 +476,7 @@ func TestCompiler_compileMemoryInit(t *testing.T) {
 }
 
 func TestCompiler_compileElemDrop(t *testing.T) {
-	origins := []wasm.ElementInstance{
-		{References: []wasm.Reference{1}},
-		{References: []wasm.Reference{2}},
-		{References: []wasm.Reference{3}},
-		{References: []wasm.Reference{4}},
-		{References: []wasm.Reference{5}},
-	}
+	origins := []wasm.ElementInstance{{1}, {2}, {3}, {4}, {5}}
 
 	for i := 0; i < len(origins); i++ {
 		t.Run(strconv.Itoa(i), func(t *testing.T) {
@@ -494,7 +488,7 @@ func TestCompiler_compileElemDrop(t *testing.T) {
 
 			// Verify assumption that before Drop instruction, all the element instances are not empty.
 			for _, inst := range insts {
-				require.NotEqual(t, 0, len(inst.References))
+				require.NotEqual(t, 0, len(inst))
 			}
 
 			compiler := env.requireNewCompiler(t, &wasm.FunctionType{}, newCompiler, &wazeroir.CompilationResult{
@@ -523,9 +517,9 @@ func TestCompiler_compileElemDrop(t *testing.T) {
 
 			for j := 0; j < len(insts); j++ {
 				if i == j {
-					require.Zero(t, len(env.module().ElementInstances[j].References))
+					require.Zero(t, len(env.module().ElementInstances[j]))
 				} else {
-					require.NotEqual(t, 0, len(env.module().ElementInstances[j].References))
+					require.NotEqual(t, 0, len(env.module().ElementInstances[j]))
 				}
 			}
 		})
@@ -627,7 +621,7 @@ func TestCompiler_compileTableCopy(t *testing.T) {
 
 func TestCompiler_compileTableInit(t *testing.T) {
 	elementInstances := []wasm.ElementInstance{
-		{}, {References: []wasm.Reference{1, 2, 3, 4, 5}},
+		{}, {1, 2, 3, 4, 5},
 	}
 
 	const tableSize = 100
@@ -707,8 +701,8 @@ func TestCompiler_compileTableInit(t *testing.T) {
 				for i := 0; i < tableSize; i++ {
 					exp[i] = uintptr(i)
 				}
-				if inst := elementInstances[tc.elemIndex]; inst.References != nil {
-					copy(exp[tc.destOffset:], inst.References[tc.sourceOffset:tc.sourceOffset+tc.copySize])
+				if inst := elementInstances[tc.elemIndex]; inst != nil {
+					copy(exp[tc.destOffset:], inst[tc.sourceOffset:tc.sourceOffset+tc.copySize])
 				}
 				require.Equal(t, exp, table)
 			} else {
diff --git a/internal/engine/compiler/compiler_vec_test.go b/internal/engine/compiler/compiler_vec_test.go
index f7b74f99..946f104b 100644
--- a/internal/engine/compiler/compiler_vec_test.go
+++ b/internal/engine/compiler/compiler_vec_test.go
@@ -1863,22 +1863,6 @@ func TestCompiler_compileV128Shuffle(t *testing.T) {
 				0x5, 0x15, 0x6, 0x16, 0x7, 0x17, 0x8, 0x18,
 			},
 		},
-		{
-			name:  "mix",
-			lanes: []uint64{0, 17, 2, 19, 4, 21, 6, 23, 8, 25, 10, 27, 12, 29, 14, 31},
-			v: [16]byte{
-				0x1, 0xff, 0x2, 0xff, 0x3, 0xff, 0x4, 0xff,
-				0x5, 0xff, 0x6, 0xff, 0x7, 0xff, 0x8, 0xff,
-			},
-			w: [16]byte{
-				0xff, 0x11, 0xff, 0x12, 0xff, 0x13, 0xff, 0x14,
-				0xff, 0x15, 0xff, 0x16, 0xff, 0x17, 0xff, 0x18,
-			},
-			exp: [16]byte{
-				0x1, 0x11, 0x2, 0x12, 0x3, 0x13, 0x4, 0x14,
-				0x5, 0x15, 0x6, 0x16, 0x7, 0x17, 0x8, 0x18,
-			},
-		},
 	}
 
 	for _, tc := range tests {
diff --git a/internal/engine/wazevo/backend/backend_test.go b/internal/engine/wazevo/backend/backend_test.go
index 84fbdf9a..4f06c5b2 100644
--- a/internal/engine/wazevo/backend/backend_test.go
+++ b/internal/engine/wazevo/backend/backend_test.go
@@ -2047,6 +2047,22 @@ L9 (SSA Block: blk6):
 	add sp, sp, #0x10
 	ldr x30, [sp], #0x10
 	ret
+`,
+		},
+		{
+			name: "VecShuffle",
+			m:    testcases.VecShuffle.Module,
+			afterFinalizeARM64: `
+L1 (SSA Block: blk0):
+	stp x30, xzr, [sp, #-0x10]!
+	str xzr, [sp, #-0x10]!
+	mov v29.16b, v0.16b
+	mov v30.16b, v1.16b
+	ldr q8, #8; b 32; data.v128  0706050403020100 1f1e1d1c1b1a1918
+	tbl v0.16b, { v29.16b, v30.16b }, v8.16b
+	add sp, sp, #0x10
+	ldr x30, [sp], #0x10
+	ret
 `,
 		},
 	} {
diff --git a/internal/engine/wazevo/backend/isa/arm64/instr_encoding_test.go b/internal/engine/wazevo/backend/isa/arm64/instr_encoding_test.go
index 574ca40a..d4cf2ed0 100644
--- a/internal/engine/wazevo/backend/isa/arm64/instr_encoding_test.go
+++ b/internal/engine/wazevo/backend/isa/arm64/instr_encoding_test.go
@@ -34,12 +34,20 @@ func TestInstruction_encode(t *testing.T) {
 		{want: "410c040e", setup: func(i *instruction) { i.asVecDup(operandNR(v1VReg), operandNR(v2VReg), vecArrangement2S) }},
 		{want: "410c044e", setup: func(i *instruction) { i.asVecDup(operandNR(v1VReg), operandNR(v2VReg), vecArrangement4S) }},
 		{want: "410c084e", setup: func(i *instruction) { i.asVecDup(operandNR(v1VReg), operandNR(v2VReg), vecArrangement2D) }},
+		{want: "4104034e", setup: func(i *instruction) { i.asVecDupElement(operandNR(v1VReg), operandNR(v2VReg), vecArrangementB, 1) }},
+		{want: "4104064e", setup: func(i *instruction) { i.asVecDupElement(operandNR(v1VReg), operandNR(v2VReg), vecArrangementH, 1) }},
+		{want: "41040c4e", setup: func(i *instruction) { i.asVecDupElement(operandNR(v1VReg), operandNR(v2VReg), vecArrangementS, 1) }},
+		{want: "4104184e", setup: func(i *instruction) { i.asVecDupElement(operandNR(v1VReg), operandNR(v2VReg), vecArrangementD, 1) }},
 		{want: "4138032e", setup: func(i *instruction) {
 			i.asVecExtract(operandNR(v1VReg), operandNR(v2VReg), operandNR(v3VReg), vecArrangement8B, 7)
 		}},
 		{want: "4138036e", setup: func(i *instruction) {
 			i.asVecExtract(operandNR(v1VReg), operandNR(v2VReg), operandNR(v3VReg), vecArrangement16B, 7)
 		}},
+		{want: "410c036e", setup: func(i *instruction) { i.asVecMovElement(operandNR(v1VReg), operandNR(v2VReg), vecArrangementB, 1, 1) }},
+		{want: "4114066e", setup: func(i *instruction) { i.asVecMovElement(operandNR(v1VReg), operandNR(v2VReg), vecArrangementH, 1, 1) }},
+		{want: "41240c6e", setup: func(i *instruction) { i.asVecMovElement(operandNR(v1VReg), operandNR(v2VReg), vecArrangementS, 1, 1) }},
+		{want: "4144186e", setup: func(i *instruction) { i.asVecMovElement(operandNR(v1VReg), operandNR(v2VReg), vecArrangementD, 1, 1) }},
 		{want: "4104090f", setup: func(i *instruction) {
 			i.asVecShiftImm(vecOpSshr, operandNR(v1VReg), operandNR(v2VReg), operandShiftImm(7), vecArrangement8B)
 		}},
@@ -98,6 +106,18 @@ func TestInstruction_encode(t *testing.T) {
 		{want: "41a43d6f", setup: func(i *instruction) { // ushll2
 			i.asVecShiftImm(vecOpUshll, operandNR(v1VReg), operandNR(v2VReg), operandShiftImm(3), vecArrangement4S)
 		}},
+		{want: "4100030e", setup: func(i *instruction) {
+			i.asVecTbl(1, operandNR(v1VReg), operandNR(v2VReg), operandNR(v3VReg), vecArrangement8B)
+		}},
+		{want: "4100034e", setup: func(i *instruction) {
+			i.asVecTbl(1, operandNR(v1VReg), operandNR(v2VReg), operandNR(v3VReg), vecArrangement16B)
+		}},
+		{want: "4120040e", setup: func(i *instruction) {
+			i.asVecTbl(2, operandNR(v1VReg), operandNR(v2VReg), operandNR(v4VReg), vecArrangement8B)
+		}},
+		{want: "4120044e", setup: func(i *instruction) {
+			i.asVecTbl(2, operandNR(v1VReg), operandNR(v2VReg), operandNR(v4VReg), vecArrangement16B)
+		}},
 		{want: "4138030e", setup: func(i *instruction) {
 			i.asVecPermute(vecOpZip1, operandNR(v1VReg), operandNR(v2VReg), operandNR(v3VReg), vecArrangement8B)
 		}},
@@ -893,10 +913,13 @@ func TestInstruction_encode(t *testing.T) {
 		{want: "411c024e", setup: func(i *instruction) { i.asMovToVec(operandNR(v1VReg), operandNR(x2VReg), vecArrangementH, 0) }},
 		{want: "411c044e", setup: func(i *instruction) { i.asMovToVec(operandNR(v1VReg), operandNR(x2VReg), vecArrangementS, 0) }},
 		{want: "411c084e", setup: func(i *instruction) { i.asMovToVec(operandNR(v1VReg), operandNR(x2VReg), vecArrangementD, 0) }},
-		{want: "413c010e", setup: func(i *instruction) { i.asMovFromVec(operandNR(x1VReg), operandNR(v2VReg), vecArrangementB, 0) }},
-		{want: "413c020e", setup: func(i *instruction) { i.asMovFromVec(operandNR(x1VReg), operandNR(v2VReg), vecArrangementH, 0) }},
-		{want: "413c040e", setup: func(i *instruction) { i.asMovFromVec(operandNR(x1VReg), operandNR(v2VReg), vecArrangementS, 0) }},
-		{want: "413c084e", setup: func(i *instruction) { i.asMovFromVec(operandNR(x1VReg), operandNR(v2VReg), vecArrangementD, 0) }},
+		{want: "413c010e", setup: func(i *instruction) { i.asMovFromVec(operandNR(x1VReg), operandNR(v2VReg), vecArrangementB, 0, false) }},
+		{want: "413c020e", setup: func(i *instruction) { i.asMovFromVec(operandNR(x1VReg), operandNR(v2VReg), vecArrangementH, 0, false) }},
+		{want: "413c040e", setup: func(i *instruction) { i.asMovFromVec(operandNR(x1VReg), operandNR(v2VReg), vecArrangementS, 0, false) }},
+		{want: "413c084e", setup: func(i *instruction) { i.asMovFromVec(operandNR(x1VReg), operandNR(v2VReg), vecArrangementD, 0, false) }},
+		{want: "412c030e", setup: func(i *instruction) { i.asMovFromVec(operandNR(x1VReg), operandNR(v2VReg), vecArrangementB, 1, true) }},
+		{want: "412c060e", setup: func(i *instruction) { i.asMovFromVec(operandNR(x1VReg), operandNR(v2VReg), vecArrangementH, 1, true) }},
+		{want: "412c0c4e", setup: func(i *instruction) { i.asMovFromVec(operandNR(x1VReg), operandNR(v2VReg), vecArrangementS, 1, true) }},
 		{want: "410c084e", setup: func(i *instruction) { i.asVecDup(operandNR(x1VReg), operandNR(v2VReg), vecArrangement2D) }},
 		{want: "4140036e", setup: func(i *instruction) { // 4140036e
 			i.asVecExtract(operandNR(x1VReg), operandNR(v2VReg), operandNR(v3VReg), vecArrangement16B, 8)
diff --git a/internal/engine/wazevo/backend/isa/arm64/instr_test.go b/internal/engine/wazevo/backend/isa/arm64/instr_test.go
index 69464472..65d27287 100644
--- a/internal/engine/wazevo/backend/isa/arm64/instr_test.go
+++ b/internal/engine/wazevo/backend/isa/arm64/instr_test.go
@@ -67,3 +67,10 @@ func TestInstruction_String(t *testing.T) {
 		t.Run(tc.exp, func(t *testing.T) { require.Equal(t, tc.exp, tc.i.String()) })
 	}
 }
+
+func TestInstruction_isCopy(t *testing.T) {
+	require.False(t, (&instruction{kind: mov32}).isCopy())
+	require.True(t, (&instruction{kind: mov64}).isCopy())
+	require.True(t, (&instruction{kind: fpuMov64}).isCopy())
+	require.True(t, (&instruction{kind: fpuMov128}).isCopy())
+}
diff --git a/internal/engine/wazevo/backend/isa/arm64/lower_instr_test.go b/internal/engine/wazevo/backend/isa/arm64/lower_instr_test.go
index f0db9ea5..4e9b7081 100644
--- a/internal/engine/wazevo/backend/isa/arm64/lower_instr_test.go
+++ b/internal/engine/wazevo/backend/isa/arm64/lower_instr_test.go
@@ -336,7 +336,7 @@ L2:
 `},
 		{name: "64bit unsigned", _64bit: true, signed: false, exp: `
 udiv x1?, x2?, x3?
-cbnz w3?, L1
+cbnz x3?, L1
 movz x1?, #0xa, lsl 0
 str w1?, [x65535?]
 mov x2?, sp
@@ -348,7 +348,7 @@ L1:
 `},
 		{name: "64bit signed", _64bit: true, signed: true, exp: `
 sdiv x1?, x2?, x3?
-cbnz w3?, L1
+cbnz x3?, L1
 movz x1?, #0xa, lsl 0
 str w1?, [x65535?]
 mov x2?, sp
@@ -726,6 +726,55 @@ add w15, w15, w1?, lsl #1
 	}
 }
 
+func TestMachine_lowerShuffle(t *testing.T) {
+	for _, tc := range []struct {
+		name          string
+		lanes         []uint64
+		expectedAsm   string
+		expectedBytes string
+	}{
+		{
+			name:  "lanes 0..15",
+			lanes: []uint64{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},
+			expectedAsm: `
+mov v29.16b, x2.16b
+mov v30.16b, x15.16b
+ldr q1?, #8; b 32; data.v128  0706050403020100 0f0e0d0c0b0a0908
+tbl x1.16b, { v29.16b, v30.16b }, v1?.16b
+`,
+			expectedBytes: "5d1ca24efe1daf4e4000009c05000014000102030405060708090a0b0c0d0e0fa123004e",
+		},
+		{
+			name:  "lanes 0101...",
+			lanes: []uint64{0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1},
+			expectedAsm: `
+mov v29.16b, x2.16b
+mov v30.16b, x15.16b
+ldr q1?, #8; b 32; data.v128  0100010001000100 0100010001000100
+tbl x1.16b, { v29.16b, v30.16b }, v1?.16b
+`,
+			expectedBytes: "5d1ca24efe1daf4e4000009c0500001400010001000100010001000100010001a123004e",
+		},
+	} {
+		t.Run(tc.name, func(t *testing.T) {
+			_, _, m := newSetupWithMockContext()
+			lanes := tc.lanes
+
+			// Encode the 16 bytes as 8 bytes in u1, and 8 bytes in u2.
+			lane1 := lanes[7]<<56 | lanes[6]<<48 | lanes[5]<<40 | lanes[4]<<32 | lanes[3]<<24 | lanes[2]<<16 | lanes[1]<<8 | lanes[0]
+			lane2 := lanes[15]<<56 | lanes[14]<<48 | lanes[13]<<40 | lanes[12]<<32 | lanes[11]<<24 | lanes[10]<<16 | lanes[9]<<8 | lanes[8]
+
+			m.lowerShuffle(operandNR(x1VReg), operandNR(x2VReg), operandNR(x15VReg), lane1, lane2)
+			require.Equal(t, tc.expectedAsm, "\n"+formatEmittedInstructionsInCurrentBlock(m)+"\n")
+
+			m.FlushPendingInstructions()
+			m.encode(m.perBlockHead)
+			buf := m.compiler.Buf()
+			require.Equal(t, tc.expectedBytes, hex.EncodeToString(buf))
+		})
+	}
+}
+
 func TestMachine_lowerVShift(t *testing.T) {
 	for _, tc := range []struct {
 		name          string
diff --git a/internal/engine/wazevo/e2e_test.go b/internal/engine/wazevo/e2e_test.go
index 88004906..2fd7d164 100644
--- a/internal/engine/wazevo/e2e_test.go
+++ b/internal/engine/wazevo/e2e_test.go
@@ -20,6 +20,7 @@ import (
 	v2 "github.com/tetratelabs/wazero/internal/integration_test/spectest/v2"
 	"github.com/tetratelabs/wazero/internal/leb128"
 	"github.com/tetratelabs/wazero/internal/testing/binaryencoding"
+	"github.com/tetratelabs/wazero/internal/testing/dwarftestdata"
 	"github.com/tetratelabs/wazero/internal/testing/require"
 	"github.com/tetratelabs/wazero/internal/wasm"
 )
@@ -135,12 +136,31 @@ func TestSpectestV2(t *testing.T) {
 		name string
 	}{
 		{"block"},
+		{"bulk"},
+		{"i32"},
+		{"i64"},
 		{"br"},
 		{"call"},
 		{"call_indirect"},
 		{"conversions"},
+		{"global"},
 		{"if"},
+		{"linking"},
 		{"loop"},
+		{"memory_copy"},
+		{"memory_fill"},
+		{"memory_init"},
+		{"memory_trap"},
+		{"ref_null"},
+		{"ref_is_null"},
+		{"ref_func"},
+		{"table_copy"},
+		{"table_fill"},
+		{"table_get"},
+		{"table_init"},
+		{"table_set"},
+		{"table_size"},
+		{"select"},
 		{"simd_boolean"},
 		{"simd_bit_shift"},
 		{"simd_bitwise"},
@@ -172,6 +192,7 @@ func TestSpectestV2(t *testing.T) {
 		{"simd_f64x2_pmin_pmax"},
 		{"simd_i32x4_trunc_sat_f32x4"},
 		{"simd_i32x4_trunc_sat_f64x2"},
+		{"simd_lane"},
 	} {
 		t.Run(tc.name, func(t *testing.T) {
 			t.Run("normal", func(t *testing.T) {
@@ -407,6 +428,53 @@ func TestE2E(t *testing.T) {
 				{params: []uint64{1, 2, 3, 4, 5, 6}, expResults: []uint64{0x3, 0x2, 0x5, 0x6}},
 			},
 		},
+		{
+			name: "vector_shuffle",
+			m:    testcases.VecShuffle.Module,
+			calls: []callCase{
+				{params: []uint64{0x01010101, 0x02020202, 0x03030303, 0x04040404}, expResults: []uint64{0x01010101, 0x04040404}},
+				{params: []uint64{0x03030303, 0x04040404, 0x01010101, 0x02020202}, expResults: []uint64{0x03030303, 0x02020202}},
+				{params: []uint64{0x00000000, 0xffffffff, 0xffffffff, 0x00000000}, expResults: []uint64{0x00000000, 0x00000000}},
+				{params: []uint64{0xffffffff, 0x00000000, 0x00000000, 0xffffffff}, expResults: []uint64{0xffffffff, 0xffffffff}},
+				{params: []uint64{0x00000000, 0x11111111, 0x11111111, 0xffffffff}, expResults: []uint64{0x00000000, 0xffffffff}},
+			},
+		},
+		{
+			name: "vector_shuffle (1st only)",
+			m:    testcases.VecShuffleWithLane(1, 1, 1, 1, 0, 0, 0, 0, 10, 10, 10, 10, 0, 0, 0, 0),
+			calls: []callCase{
+				{params: []uint64{0x0000000000000b0a, 0x0c0000, 0xffffffffffffffff, 0xffffffffffffffff}, expResults: []uint64{0x0a0a0a0a0b0b0b0b, 0x0a0a0a0a0c0c0c0c}},
+				{params: []uint64{0x01010101, 0x02020202, 0x03030303, 0x04040404}, expResults: []uint64{0x0101010101010101, 0x101010102020202}},
+				{params: []uint64{0x03030303, 0x04040404, 0x01010101, 0x02020202}, expResults: []uint64{0x0303030303030303, 0x303030304040404}},
+				{params: []uint64{0x00000000, 0xffffffff, 0xffffffff, 0x00000000}, expResults: []uint64{0x0000000000000000, 0x0000000ffffffff}},
+				{params: []uint64{0xffffffff, 0x00000000, 0x00000000, 0xffffffff}, expResults: []uint64{0xffffffffffffffff, 0xffffffff00000000}},
+				{params: []uint64{0x00000000, 0x11111111, 0x11111111, 0xffffffff}, expResults: []uint64{0x0000000000000000, 0x0000000011111111}},
+			},
+		},
+		{
+			name: "vector_shuffle (2nd only)",
+			m:    testcases.VecShuffleWithLane(17, 17, 17, 17, 16, 16, 16, 16, 26, 26, 26, 26, 16, 16, 16, 16),
+			calls: []callCase{
+				{params: []uint64{0xffffffffffffffff, 0xffffffffffffffff, 0x0000000000000b0a, 0x0c0000}, expResults: []uint64{0x0a0a0a0a0b0b0b0b, 0x0a0a0a0a0c0c0c0c}},
+				{params: []uint64{0x01010101, 0x02020202, 0x03030303, 0x04040404}, expResults: []uint64{0x303030303030303, 0x303030304040404}},
+				{params: []uint64{0x03030303, 0x04040404, 0x01010101, 0x02020202}, expResults: []uint64{0x101010101010101, 0x101010102020202}},
+				{params: []uint64{0x00000000, 0xffffffff, 0xffffffff, 0x00000000}, expResults: []uint64{0xffffffffffffffff, 0xffffffff00000000}},
+				{params: []uint64{0xffffffff, 0x00000000, 0x00000000, 0xffffffff}, expResults: []uint64{0x0000000000000000, 0xffffffff}},
+				{params: []uint64{0x00000000, 0x11111111, 0x11111111, 0xffffffff}, expResults: []uint64{0x1111111111111111, 0x11111111ffffffff}},
+			},
+		},
+		{
+			name: "vector_shuffle (mixed)",
+			m:    testcases.VecShuffleWithLane(0, 17, 2, 19, 4, 21, 6, 23, 8, 25, 10, 27, 12, 29, 14, 31),
+			calls: []callCase{
+				{params: []uint64{0xff08ff07ff06ff05, 0xff04ff03ff02ff01, 0x18ff17ff16ff15ff, 0x14ff13ff12ff11ff}, expResults: []uint64{0x1808170716061505, 0x1404130312021101}},
+				{params: []uint64{0x01010101, 0x02020202, 0x03030303, 0x04040404}, expResults: []uint64{0x3010301, 0x4020402}},
+				{params: []uint64{0x03030303, 0x04040404, 0x01010101, 0x02020202}, expResults: []uint64{0x1030103, 0x2040204}},
+				{params: []uint64{0x00000000, 0xffffffff, 0xffffffff, 0x00000000}, expResults: []uint64{0xff00ff00, 0xff00ff}},
+				{params: []uint64{0xffffffff, 0x00000000, 0x00000000, 0xffffffff}, expResults: []uint64{0xff00ff, 0xff00ff00}},
+				{params: []uint64{0x00000000, 0x11111111, 0x11111111, 0xffffffff}, expResults: []uint64{0x11001100, 0xff11ff11}},
+			},
+		},
 	} {
 		tc := tc
 		t.Run(tc.name, func(t *testing.T) {
@@ -1010,3 +1078,41 @@ func TestListener_long_many_consts(t *testing.T) {
 <-- (0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60)
 `, "\n"+buf.String())
 }
+
+// TestDWARF verifies that the DWARF based stack traces work as expected before/after compilation cache.
+func TestDWARF(t *testing.T) {
+	config := wazero.NewRuntimeConfigCompiler()
+	ctx := context.Background()
+
+	// Configure the new optimizing backend!
+	wazevo.ConfigureWazevo(config)
+
+	bin := dwarftestdata.ZigWasm
+
+	dir := t.TempDir()
+
+	var expErr error
+	{
+		cc, err := wazero.NewCompilationCacheWithDir(dir)
+		require.NoError(t, err)
+		rc := config.WithCompilationCache(cc)
+
+		r := wazero.NewRuntimeWithConfig(ctx, rc)
+		_, expErr = r.Instantiate(ctx, bin)
+		require.Error(t, expErr)
+
+		err = r.Close(ctx)
+		require.NoError(t, err)
+	}
+
+	cc, err := wazero.NewCompilationCacheWithDir(dir)
+	require.NoError(t, err)
+	rc := config.WithCompilationCache(cc)
+	r := wazero.NewRuntimeWithConfig(ctx, rc)
+	_, err = r.Instantiate(ctx, bin)
+	require.Error(t, err)
+	require.Equal(t, expErr.Error(), err.Error())
+
+	err = r.Close(ctx)
+	require.NoError(t, err)
+}
diff --git a/internal/engine/wazevo/engine_cache_test.go b/internal/engine/wazevo/engine_cache_test.go
index 59ed554a..8402f301 100644
--- a/internal/engine/wazevo/engine_cache_test.go
+++ b/internal/engine/wazevo/engine_cache_test.go
@@ -2,12 +2,14 @@ package wazevo
 
 import (
 	"bytes"
+	"crypto/sha256"
 	"io"
 	"testing"
 
 	"github.com/tetratelabs/wazero/internal/testing/require"
 	"github.com/tetratelabs/wazero/internal/u32"
 	"github.com/tetratelabs/wazero/internal/u64"
+	"github.com/tetratelabs/wazero/internal/wasm"
 )
 
 var testVersion = "0.0.1"
@@ -19,41 +21,43 @@ func TestSerializeCompiledModule(t *testing.T) {
 	}{
 		{
 			in: &compiledModule{
-				executable:      []byte{1, 2, 3, 4, 5},
+				executables:     &executables{executable: []byte{1, 2, 3, 4, 5}},
 				functionOffsets: []int{0},
 			},
 			exp: concat(
-				[]byte(magic),
+				magic,
 				[]byte{byte(len(testVersion))},
 				[]byte(testVersion),
 				u32.LeBytes(1),        // number of functions.
 				u64.LeBytes(0),        // offset.
 				u64.LeBytes(5),        // length of code.
 				[]byte{1, 2, 3, 4, 5}, // code.
+				[]byte{0},             // no source map.
 			),
 		},
 		{
 			in: &compiledModule{
-				executable:      []byte{1, 2, 3, 4, 5},
+				executables:     &executables{executable: []byte{1, 2, 3, 4, 5}},
 				functionOffsets: []int{0},
 			},
 			exp: concat(
-				[]byte(magic),
+				magic,
 				[]byte{byte(len(testVersion))},
 				[]byte(testVersion),
 				u32.LeBytes(1),        // number of functions.
 				u64.LeBytes(0),        // offset.
 				u64.LeBytes(5),        // length of code.
 				[]byte{1, 2, 3, 4, 5}, // code.
+				[]byte{0},             // no source map.
 			),
 		},
 		{
 			in: &compiledModule{
-				executable:      []byte{1, 2, 3, 4, 5, 1, 2, 3},
+				executables:     &executables{executable: []byte{1, 2, 3, 4, 5, 1, 2, 3}},
 				functionOffsets: []int{0, 5},
 			},
 			exp: concat(
-				[]byte(magic),
+				magic,
 				[]byte{byte(len(testVersion))},
 				[]byte(testVersion),
 				u32.LeBytes(2), // number of functions.
@@ -64,6 +68,7 @@ func TestSerializeCompiledModule(t *testing.T) {
 				// Executable.
 				u64.LeBytes(8),                 // length of code.
 				[]byte{1, 2, 3, 4, 5, 1, 2, 3}, // code.
+				[]byte{0},                      // no source map.
 			),
 		},
 	}
@@ -96,10 +101,20 @@ func TestDeserializeCompiledModule(t *testing.T) {
 			in:     []byte{1},
 			expErr: "compilationcache: invalid header length: 1",
 		},
+		{
+			name: "invalid magic",
+			in: concat(
+				[]byte{'a', 'b', 'c', 'd', 'e', 'f'},
+				[]byte{byte(len(testVersion))},
+				[]byte(testVersion),
+				u32.LeBytes(1), // number of functions.
+			),
+			expErr: "compilationcache: invalid magic number: got WAZEVO but want abcdef",
+		},
 		{
 			name: "version mismatch",
 			in: concat(
-				[]byte(magic),
+				magic,
 				[]byte{byte(len("1233123.1.1"))},
 				[]byte("1233123.1.1"),
 				u32.LeBytes(1), // number of functions.
@@ -109,7 +124,7 @@ func TestDeserializeCompiledModule(t *testing.T) {
 		{
 			name: "version mismatch",
 			in: concat(
-				[]byte(magic),
+				magic,
 				[]byte{byte(len("0.0.0"))},
 				[]byte("0.0.0"),
 				u32.LeBytes(1), // number of functions.
@@ -119,7 +134,7 @@ func TestDeserializeCompiledModule(t *testing.T) {
 		{
 			name: "one function",
 			in: concat(
-				[]byte(magic),
+				magic,
 				[]byte{byte(len(testVersion))},
 				[]byte(testVersion),
 				u32.LeBytes(1), // number of functions.
@@ -127,9 +142,10 @@ func TestDeserializeCompiledModule(t *testing.T) {
 				// Executable.
 				u64.LeBytes(5),        // size.
 				[]byte{1, 2, 3, 4, 5}, // machine code.
+				[]byte{0},             // no source map.
 			),
 			expCompiledModule: &compiledModule{
-				executable:      []byte{1, 2, 3, 4, 5},
+				executables:     &executables{executable: []byte{1, 2, 3, 4, 5}},
 				functionOffsets: []int{0},
 			},
 			expStaleCache: false,
@@ -138,7 +154,7 @@ func TestDeserializeCompiledModule(t *testing.T) {
 		{
 			name: "two functions",
 			in: concat(
-				[]byte(magic),
+				magic,
 				[]byte{byte(len(testVersion))},
 				[]byte(testVersion),
 				u32.LeBytes(2), // number of functions.
@@ -149,10 +165,11 @@ func TestDeserializeCompiledModule(t *testing.T) {
 				// Executable.
 				u64.LeBytes(10),                       // size.
 				[]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, // machine code.
+				[]byte{0},                             // no source map.
 			),
 			importedFunctionCount: 1,
 			expCompiledModule: &compiledModule{
-				executable:      []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10},
+				executables:     &executables{executable: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}},
 				functionOffsets: []int{0, 7},
 			},
 			expStaleCache: false,
@@ -188,6 +205,25 @@ func TestDeserializeCompiledModule(t *testing.T) {
 			),
 			expErr: "compilationcache: error reading executable (len=5): EOF",
 		},
+		{
+			name: "no source map presence",
+			in: concat(
+				magic,
+				[]byte{byte(len(testVersion))},
+				[]byte(testVersion),
+				u32.LeBytes(1), // number of functions.
+				u64.LeBytes(0), // offset.
+				// Executable.
+				u64.LeBytes(5),        // size.
+				[]byte{1, 2, 3, 4, 5}, // machine code.
+			),
+			expCompiledModule: &compiledModule{
+				executables:     &executables{executable: []byte{1, 2, 3, 4, 5}},
+				functionOffsets: []int{0},
+			},
+			expStaleCache: false,
+			expErr:        "compilationcache: error reading source map presence: EOF",
+		},
 	}
 
 	for _, tc := range tests {
@@ -206,3 +242,14 @@ func TestDeserializeCompiledModule(t *testing.T) {
 		})
 	}
 }
+
+func Test_fileCacheKey(t *testing.T) {
+	s := sha256.New()
+	s.Write([]byte("hello world"))
+	m := &wasm.Module{}
+	s.Sum(m.ID[:0])
+	original := m.ID
+	result := fileCacheKey(m)
+	require.Equal(t, original, m.ID)
+	require.NotEqual(t, original, result)
+}
diff --git a/internal/engine/wazevo/engine_test.go b/internal/engine/wazevo/engine_test.go
index 8d0781bf..853da938 100644
--- a/internal/engine/wazevo/engine_test.go
+++ b/internal/engine/wazevo/engine_test.go
@@ -17,53 +17,47 @@ func Test_sharedFunctionsFinalizer(t *testing.T) {
 
 	b1, err := platform.MmapCodeSegment(100)
 	require.NoError(t, err)
-
 	b2, err := platform.MmapCodeSegment(100)
 	require.NoError(t, err)
-
 	b3, err := platform.MmapCodeSegment(100)
 	require.NoError(t, err)
-
-	b4, err := platform.MmapCodeSegment(100)
+	b6, err := platform.MmapCodeSegment(100)
 	require.NoError(t, err)
-	b5, err := platform.MmapCodeSegment(100)
+	b7, err := platform.MmapCodeSegment(100)
 	require.NoError(t, err)
 
-	preabmles := map[*wasm.FunctionType][]byte{
-		{Params: []wasm.ValueType{}}:                  b4,
-		{Params: []wasm.ValueType{wasm.ValueTypeI32}}: b5,
-	}
-
 	sf.memoryGrowExecutable = b1
 	sf.stackGrowExecutable = b2
 	sf.checkModuleExitCode = b3
-	sf.entryPreambles = preabmles
+	sf.tableGrowExecutable = b6
+	sf.refFuncExecutable = b7
 
 	sharedFunctionsFinalizer(sf)
 	require.Nil(t, sf.memoryGrowExecutable)
 	require.Nil(t, sf.stackGrowExecutable)
 	require.Nil(t, sf.checkModuleExitCode)
-	require.Nil(t, sf.entryPreambles)
+	require.Nil(t, sf.tableGrowExecutable)
+	require.Nil(t, sf.refFuncExecutable)
 }
 
-func Test_compiledModuleFinalizer(t *testing.T) {
-	cm := &compiledModule{}
-
+func Test_executablesFinalizer(t *testing.T) {
 	b, err := platform.MmapCodeSegment(100)
 	require.NoError(t, err)
-	cm.executable = b
-	compiledModuleFinalizer(cm)
-	require.Nil(t, cm.executable)
+
+	exec := &executables{}
+	exec.executable = b
+	executablesFinalizer(exec)
+	require.Nil(t, exec.executable)
 }
 
-type fakeFinalizer map[*compiledModule]func(module *compiledModule)
+type fakeFinalizer map[*executables]func(module *executables)
 
 func (f fakeFinalizer) setFinalizer(obj interface{}, finalizer interface{}) {
-	cf := obj.(*compiledModule)
+	cf := obj.(*executables)
 	if _, ok := f[cf]; ok { // easier than adding a field for testing.T
 		panic(fmt.Sprintf("BUG: %v already had its finalizer set", cf))
 	}
-	f[cf] = finalizer.(func(*compiledModule))
+	f[cf] = finalizer.(func(*executables))
 }
 
 func TestEngine_CompileModule(t *testing.T) {
@@ -107,7 +101,7 @@ func TestEngine_sortedCompiledModules(t *testing.T) {
 			hdr.Len = 1
 			hdr.Cap = 1
 		}
-		cm := &compiledModule{executable: buf}
+		cm := &compiledModule{executables: &executables{executable: buf}}
 		return cm
 	}
 
@@ -183,7 +177,7 @@ func TestCompiledModule_functionIndexOf(t *testing.T) {
 	}
 
 	cm := &compiledModule{
-		executable:      executable,
+		executables:     &executables{executable: executable},
 		functionOffsets: []int{0, 500, 1000, 2000},
 	}
 	require.Equal(t, wasm.Index(0), cm.functionIndexOf(executableAddr))
diff --git a/internal/engine/wazevo/frontend/frontend_test.go b/internal/engine/wazevo/frontend/frontend_test.go
index 73c2c731..1d7fc9f8 100644
--- a/internal/engine/wazevo/frontend/frontend_test.go
+++ b/internal/engine/wazevo/frontend/frontend_test.go
@@ -1779,6 +1779,15 @@ blk3: () <-- (blk0)
 
 blk4: () <-- (blk2,blk3)
 	Jump blk1
+`,
+		},
+		{
+			name: "VecShuffle",
+			m:    testcases.VecShuffle.Module,
+			exp: `
+blk0: (exec_ctx:i64, module_ctx:i64, v2:v128, v3:v128)
+	v4:v128 = Shuffle.[0 1 2 3 4 5 6 7 24 25 26 27 28 29 30 31] v2, v3
+	Jump blk_ret, v4
 `,
 		},
 	} {
@@ -1875,12 +1884,6 @@ func TestCompiler_declareSignatures(t *testing.T) {
 		c.declareSignatures(false)
 
 		declaredSigs := builder.Signatures()
-		require.Equal(t,
-			4+
-				2, // memoryGrowSig and checkModuleExitCodeSig.
-			len(declaredSigs),
-		)
-
 		expected := []*ssa.Signature{
 			{ID: 0, Params: []ssa.Type{ssa.TypeI64, ssa.TypeI64}},
 			{ID: 1, Params: []ssa.Type{ssa.TypeI64, ssa.TypeI64, ssa.TypeI64, ssa.TypeI32}},
@@ -1888,9 +1891,13 @@ func TestCompiler_declareSignatures(t *testing.T) {
 			{ID: 3, Params: []ssa.Type{ssa.TypeI64, ssa.TypeI64}, Results: []ssa.Type{ssa.TypeI64, ssa.TypeI32}},
 			{ID: 4, Params: []ssa.Type{ssa.TypeI64, ssa.TypeI32}, Results: []ssa.Type{ssa.TypeI32}},
 			{ID: 5, Params: []ssa.Type{ssa.TypeI64}},
+			{ID: 6, Params: []ssa.Type{ssa.TypeI64, ssa.TypeI32, ssa.TypeI32, ssa.TypeI64}, Results: []ssa.Type{ssa.TypeI32}},
+			{ID: 7, Params: []ssa.Type{ssa.TypeI64, ssa.TypeI32}, Results: []ssa.Type{ssa.TypeI64}},
+			{ID: 8, Params: []ssa.Type{ssa.TypeI64, ssa.TypeI64, ssa.TypeI32}},
 		}
 
-		for i := 0; i < 6; i++ {
+		require.Equal(t, len(expected), len(declaredSigs))
+		for i := 0; i < len(expected); i++ {
 			require.Equal(t, expected[i].String(), declaredSigs[i].String(), i)
 		}
 	})
@@ -1901,11 +1908,6 @@ func TestCompiler_declareSignatures(t *testing.T) {
 		c.declareSignatures(true)
 
 		declaredSigs := builder.Signatures()
-		require.Equal(t,
-			4*3+
-				2, // memoryGrowSig and checkModuleExitCodeSig.
-			len(declaredSigs),
-		)
 
 		expected := []*ssa.Signature{
 			{ID: 0, Params: []ssa.Type{ssa.TypeI64, ssa.TypeI64}},
@@ -1925,8 +1927,11 @@ func TestCompiler_declareSignatures(t *testing.T) {
 			// Misc.
 			{ID: 12, Params: []ssa.Type{ssa.TypeI64, ssa.TypeI32}, Results: []ssa.Type{ssa.TypeI32}},
 			{ID: 13, Params: []ssa.Type{ssa.TypeI64}},
+			{ID: 14, Params: []ssa.Type{ssa.TypeI64, ssa.TypeI32, ssa.TypeI32, ssa.TypeI64}, Results: []ssa.Type{ssa.TypeI32}},
+			{ID: 15, Params: []ssa.Type{ssa.TypeI64, ssa.TypeI32}, Results: []ssa.Type{ssa.TypeI64}},
+			{ID: 16, Params: []ssa.Type{ssa.TypeI64, ssa.TypeI64, ssa.TypeI32}},
 		}
-
+		require.Equal(t, len(expected), len(declaredSigs))
 		for i := 0; i < len(declaredSigs); i++ {
 			require.Equal(t, expected[i].String(), declaredSigs[i].String(), i)
 		}
diff --git a/internal/engine/wazevo/frontend/lower_test.go b/internal/engine/wazevo/frontend/lower_test.go
index 268915ff..0bb8fe9a 100644
--- a/internal/engine/wazevo/frontend/lower_test.go
+++ b/internal/engine/wazevo/frontend/lower_test.go
@@ -15,4 +15,10 @@ func Test_Offsets(t *testing.T) {
 	require.Equal(t, int(unsafe.Offsetof(memInstance.Buffer)), memoryInstanceBufOffset)
 	var tableInstance wasm.TableInstance
 	require.Equal(t, int(unsafe.Offsetof(tableInstance.References)), tableInstanceBaseAddressOffset)
+
+	var dataInstance wasm.DataInstance
+	var elementInstance wasm.ElementInstance
+
+	require.Equal(t, int(unsafe.Sizeof(dataInstance)), elementOrDataInstanceSize)
+	require.Equal(t, int(unsafe.Sizeof(elementInstance)), elementOrDataInstanceSize)
 }
diff --git a/internal/engine/wazevo/module_engine_test.go b/internal/engine/wazevo/module_engine_test.go
index 3aba2dfb..86c1461d 100644
--- a/internal/engine/wazevo/module_engine_test.go
+++ b/internal/engine/wazevo/module_engine_test.go
@@ -145,7 +145,7 @@ func TestModuleEngine_ResolveImportedFunction(t *testing.T) {
 	im1 := &moduleEngine{
 		opaquePtr: &op1,
 		parent: &compiledModule{
-			executable:      make([]byte, 1000),
+			executables:     &executables{executable: make([]byte, 1000)},
 			functionOffsets: []int{1, 5, 10},
 		},
 		module: &wasm.ModuleInstance{
@@ -156,7 +156,7 @@ func TestModuleEngine_ResolveImportedFunction(t *testing.T) {
 	im2 := &moduleEngine{
 		opaquePtr: &op2,
 		parent: &compiledModule{
-			executable:      make([]byte, 1000),
+			executables:     &executables{executable: make([]byte, 1000)},
 			functionOffsets: []int{50, 4},
 		},
 		module: &wasm.ModuleInstance{
@@ -208,15 +208,21 @@ func TestModuleEngine_ResolveImportedFunction_recursive(t *testing.T) {
 	var importingOp, importedOp byte = 0xaa, 0xbb
 	imported := &moduleEngine{
 		opaquePtr: &importedOp,
-		parent:    &compiledModule{executable: make([]byte, 50), functionOffsets: []int{10}},
+		parent: &compiledModule{
+			executables:     &executables{executable: make([]byte, 50)},
+			functionOffsets: []int{10},
+		},
 		module: &wasm.ModuleInstance{
 			TypeIDs: []wasm.FunctionTypeID{111},
 			Source:  &wasm.Module{FunctionSection: []wasm.Index{0}},
 		},
 	}
 	importing := &moduleEngine{
-		opaquePtr:         &importingOp,
-		parent:            &compiledModule{executable: make([]byte, 1000), functionOffsets: []int{500}},
+		opaquePtr: &importingOp,
+		parent: &compiledModule{
+			executables:     &executables{executable: make([]byte, 1000)},
+			functionOffsets: []int{500},
+		},
 		importedFunctions: []importedFunction{{me: imported, indexInModule: 0}},
 		module: &wasm.ModuleInstance{
 			TypeIDs: []wasm.FunctionTypeID{0, 222, 0},
diff --git a/internal/engine/wazevo/testcases/testcases.go b/internal/engine/wazevo/testcases/testcases.go
index 8ff7fb44..210f158b 100644
--- a/internal/engine/wazevo/testcases/testcases.go
+++ b/internal/engine/wazevo/testcases/testcases.go
@@ -1532,8 +1532,30 @@ var (
 			}}},
 		},
 	}
+
+	VecShuffle = TestCase{
+		Name:   "shuffle",
+		Module: VecShuffleWithLane(0, 1, 2, 3, 4, 5, 6, 7, 24, 25, 26, 27, 28, 29, 30, 31),
+	}
 )
 
+// VecShuffleWithLane returns a VecShuffle test with a custom 16-bytes immediate (lane indexes).
+func VecShuffleWithLane(lane ...byte) *wasm.Module {
+	return &wasm.Module{
+		TypeSection:     []wasm.FunctionType{{Params: []wasm.ValueType{v128, v128}, Results: []wasm.ValueType{v128}}},
+		ExportSection:   []wasm.Export{{Name: ExportedFunctionName, Type: wasm.ExternTypeFunc, Index: 0}},
+		FunctionSection: []wasm.Index{0},
+		CodeSection: []wasm.Code{{
+			Body: append(append([]byte{
+				wasm.OpcodeLocalGet, 0,
+				wasm.OpcodeLocalGet, 1,
+				wasm.OpcodeVecPrefix, wasm.OpcodeVecV128i8x16Shuffle,
+			}, lane...),
+				wasm.OpcodeEnd),
+		}},
+	}
+}
+
 type TestCase struct {
 	Name             string
 	Imported, Module *wasm.Module
diff --git a/internal/engine/wazevo/wazevo_test.go b/internal/engine/wazevo/wazevo_test.go
index 22cfaa99..8aeae7d6 100644
--- a/internal/engine/wazevo/wazevo_test.go
+++ b/internal/engine/wazevo/wazevo_test.go
@@ -39,7 +39,9 @@ func TestEngine_DeleteCompiledModule(t *testing.T) {
 	e, ok := NewEngine(ctx, api.CoreFeaturesV1, nil).(*engine)
 	require.True(t, ok)
 	id := wasm.ModuleID{0xaa}
-	err := e.addCompiledModule(&wasm.Module{ID: id}, &compiledModule{executable: make([]byte, 1)})
+	err := e.addCompiledModule(&wasm.Module{ID: id}, &compiledModule{
+		executables: &executables{executable: make([]byte, 1)},
+	})
 	require.NoError(t, err)
 	require.Equal(t, uint32(1), e.CompiledModuleCount())
 	e.DeleteCompiledModule(&wasm.Module{ID: id})
@@ -63,4 +65,7 @@ func Test_ExecutionContextOffsets(t *testing.T) {
 		"SavedRegistersBegin must be aligned to 16 bytes")
 	require.Equal(t, wazevoapi.Offset(unsafe.Offsetof(execCtx.savedRegisters)), wazevoapi.ExecutionContextOffsetSavedRegistersBegin)
 	require.Equal(t, wazevoapi.Offset(unsafe.Offsetof(execCtx.goFunctionCallCalleeModuleContextOpaque)), wazevoapi.ExecutionContextOffsetGoFunctionCallCalleeModuleContextOpaque)
+	require.Equal(t, wazevoapi.Offset(unsafe.Offsetof(execCtx.tableGrowTrampolineAddress)), wazevoapi.ExecutionContextOffsetTableGrowTrampolineAddress)
+	require.Equal(t, wazevoapi.Offset(unsafe.Offsetof(execCtx.refFuncTrampolineAddress)), wazevoapi.ExecutionContextOffsetRefFuncTrampolineAddress)
+	require.Equal(t, wazevoapi.Offset(unsafe.Offsetof(execCtx.memmoveAddress)), wazevoapi.ExecutionContextOffsetMemmoveAddress)
 }
diff --git a/internal/engine/wazevo/wazevoapi/offsetdata_test.go b/internal/engine/wazevo/wazevoapi/offsetdata_test.go
index c4028634..ea64a985 100644
--- a/internal/engine/wazevo/wazevoapi/offsetdata_test.go
+++ b/internal/engine/wazevo/wazevoapi/offsetdata_test.go
@@ -26,7 +26,9 @@ func TestNewModuleContextOffsetData(t *testing.T) {
 				TablesBegin:                         -1,
 				BeforeListenerTrampolines1stElement: -1,
 				AfterListenerTrampolines1stElement:  -1,
-				TotalSize:                           8,
+				DataInstances1stElement:             8,
+				ElementInstances1stElement:          16,
+				TotalSize:                           24,
 			},
 		},
 		{
@@ -41,7 +43,9 @@ func TestNewModuleContextOffsetData(t *testing.T) {
 				TablesBegin:                         -1,
 				BeforeListenerTrampolines1stElement: -1,
 				AfterListenerTrampolines1stElement:  -1,
-				TotalSize:                           24,
+				DataInstances1stElement:             24,
+				ElementInstances1stElement:          32,
+				TotalSize:                           40,
 			},
 		},
 		{
@@ -56,7 +60,9 @@ func TestNewModuleContextOffsetData(t *testing.T) {
 				TablesBegin:                         -1,
 				BeforeListenerTrampolines1stElement: -1,
 				AfterListenerTrampolines1stElement:  -1,
-				TotalSize:                           24,
+				DataInstances1stElement:             24,
+				ElementInstances1stElement:          32,
+				TotalSize:                           40,
 			},
 		},
 		{
@@ -71,7 +77,9 @@ func TestNewModuleContextOffsetData(t *testing.T) {
 				TablesBegin:                         -1,
 				BeforeListenerTrampolines1stElement: -1,
 				AfterListenerTrampolines1stElement:  -1,
-				TotalSize:                           10*FunctionInstanceSize + 8,
+				DataInstances1stElement:             10*FunctionInstanceSize + 8,
+				ElementInstances1stElement:          10*FunctionInstanceSize + 16,
+				TotalSize:                           10*FunctionInstanceSize + 24,
 			},
 		},
 		{
@@ -86,7 +94,9 @@ func TestNewModuleContextOffsetData(t *testing.T) {
 				TablesBegin:                         -1,
 				BeforeListenerTrampolines1stElement: -1,
 				AfterListenerTrampolines1stElement:  -1,
-				TotalSize:                           10*FunctionInstanceSize + 24,
+				DataInstances1stElement:             10*FunctionInstanceSize + 24,
+				ElementInstances1stElement:          10*FunctionInstanceSize + 32,
+				TotalSize:                           10*FunctionInstanceSize + 40,
 			},
 		},
 		{
@@ -108,7 +118,9 @@ func TestNewModuleContextOffsetData(t *testing.T) {
 				TablesBegin:                         24 + 10*FunctionInstanceSize + 8*30 + 8,
 				BeforeListenerTrampolines1stElement: -1,
 				AfterListenerTrampolines1stElement:  -1,
-				TotalSize:                           24 + 10*FunctionInstanceSize + 8*30 + 8 + 8*15,
+				DataInstances1stElement:             24 + 10*FunctionInstanceSize + 8*30 + 8 + 8*15,
+				ElementInstances1stElement:          24 + 10*FunctionInstanceSize + 8*30 + 8 + 8*15 + 8,
+				TotalSize:                           24 + 10*FunctionInstanceSize + 8*30 + 8 + 8*15 + 16,
 			},
 		},
 		{
@@ -131,7 +143,9 @@ func TestNewModuleContextOffsetData(t *testing.T) {
 				TablesBegin:                         24 + 10*FunctionInstanceSize + 8*30 + 8,
 				BeforeListenerTrampolines1stElement: 24 + 10*FunctionInstanceSize + 8*30 + 8 + 8*15,
 				AfterListenerTrampolines1stElement:  24 + 10*FunctionInstanceSize + 8*30 + 8 + 8*15 + 8,
-				TotalSize:                           24 + 10*FunctionInstanceSize + 8*30 + 8 + 8*15 + 16,
+				DataInstances1stElement:             24 + 10*FunctionInstanceSize + 8*30 + 8 + 8*15 + 16,
+				ElementInstances1stElement:          24 + 10*FunctionInstanceSize + 8*30 + 8 + 8*15 + 24,
+				TotalSize:                           24 + 10*FunctionInstanceSize + 8*30 + 8 + 8*15 + 32,
 			},
 		},
 	} {
diff --git a/internal/integration_test/engine/adhoc_test.go b/internal/integration_test/engine/adhoc_test.go
index ff743c0c..823e0ca2 100644
--- a/internal/integration_test/engine/adhoc_test.go
+++ b/internal/integration_test/engine/adhoc_test.go
@@ -48,7 +48,7 @@ var tests = map[string]testCase{
 	"close module with in-flight calls":                                {f: testCloseInFlight},
 	"multiple instantiation from same source":                          {f: testMultipleInstantiation},
 	"exported function that grows memory":                              {f: testMemOps},
-	"import functions with reference type in signature":                {f: testReftypeImports, wazevoSkip: true},
+	"import functions with reference type in signature":                {f: testReftypeImports},
 	"overflow integer addition":                                        {f: testOverflow},
 	"un-signed extend global":                                          {f: testGlobalExtend},
 	"user-defined primitive in host func":                              {f: testUserDefinedPrimitiveHostFunc},
@@ -57,7 +57,7 @@ var tests = map[string]testCase{
 	"lookup function":                                                  {f: testLookupFunction},
 	"memory grow in recursive call":                                    {f: testMemoryGrowInRecursiveCall},
 	"call":                                                             {f: testCall},
-	"module memory":                                                    {f: testModuleMemory, wazevoSkip: true},
+	"module memory":                                                    {f: testModuleMemory},
 	"two indirection to host":                                          {f: testTwoIndirection},
 	"before listener globals":                                          {f: testBeforeListenerGlobals},
 	"before listener stack iterator":                                   {f: testBeforeListenerStackIterator},
diff --git a/internal/integration_test/engine/dwarf_test.go b/internal/integration_test/engine/dwarf_test.go
index a097582d..cc93e4f6 100644
--- a/internal/integration_test/engine/dwarf_test.go
+++ b/internal/integration_test/engine/dwarf_test.go
@@ -16,7 +16,7 @@ import (
 )
 
 var dwarfTests = map[string]testCase{
-	"tinygo": {f: testTinyGoDWARF, wazevoSkip: true}, // TODO: Skip wazevo because of some v2 instructions.
+	"tinygo": {f: testTinyGoDWARF},
 	"zig":    {f: testZigDWARF},
 	"cc":     {f: testCCDWARF},
 	"rust":   {f: testRustDWARF},
diff --git a/internal/integration_test/engine/memleak_test.go b/internal/integration_test/engine/memleak_test.go
index 01301f74..a807a318 100644
--- a/internal/integration_test/engine/memleak_test.go
+++ b/internal/integration_test/engine/memleak_test.go
@@ -2,8 +2,10 @@ package adhoc
 
 import (
 	"context"
+	"fmt"
 	"log"
 	"runtime"
+	"strconv"
 	"testing"
 	"time"
 
@@ -30,7 +32,7 @@ func TestMemoryLeak(t *testing.T) {
 		}
 
 		t.Run(tc.name, func(t *testing.T) {
-			duration := 5 * time.Second
+			duration := 15 * time.Second
 			t.Logf("running memory leak test for %s", duration)
 
 			ctx, cancel := context.WithTimeout(context.Background(), duration)
@@ -49,6 +51,7 @@ func TestMemoryLeak(t *testing.T) {
 			if stats.Alloc > (100 * 1024 * 1024) {
 				t.Errorf("wazero used more than 100 MiB after running the test for %s (alloc=%d)", duration, stats.Alloc)
 			}
+			fmt.Println(stats.Alloc)
 		})
 	}
 }
@@ -66,6 +69,20 @@ func testMemoryLeakInstantiateRuntimeAndModule(isWazevo bool) error {
 	}
 	defer r.Close(ctx)
 
+	hostBuilder := r.NewHostModuleBuilder("test")
+
+	for i := 0; i < 100; i++ {
+		hostBuilder.NewFunctionBuilder().WithFunc(func() uint32 { return uint32(i) }).Export(strconv.Itoa(i))
+	}
+
+	hostMod, err := hostBuilder.Instantiate(ctx)
+	if err != nil {
+		return err
+	}
+	if err = hostMod.Close(ctx); err != nil {
+		return err
+	}
+
 	mod, err := r.InstantiateWithConfig(ctx, memoryWasm,
 		wazero.NewModuleConfig().WithStartFunctions())
 	if err != nil {
diff --git a/internal/integration_test/fuzz/fuzz/fuzz_targets/memory_no_diff.rs b/internal/integration_test/fuzz/fuzz/fuzz_targets/memory_no_diff.rs
index f824ffb3..5c4d3ed1 100644
--- a/internal/integration_test/fuzz/fuzz/fuzz_targets/memory_no_diff.rs
+++ b/internal/integration_test/fuzz/fuzz/fuzz_targets/memory_no_diff.rs
@@ -45,7 +45,7 @@ fn run(data: &[u8]) -> Result<()> {
     config.min_funcs = 1;
     config.max_funcs = config.max_funcs.max(1);
 
-    wazero_abi::maybe_disable_v2(&mut config);
+    wazero_abi::maybe_disable_simd(&mut config);
 
     // Generate the random module via wasm-smith.
     let mut module = wasm_smith::Module::new(config.clone(), &mut u)?;
diff --git a/internal/integration_test/fuzz/fuzz/fuzz_targets/no_diff.rs b/internal/integration_test/fuzz/fuzz/fuzz_targets/no_diff.rs
index 9c4170b5..3db3917c 100644
--- a/internal/integration_test/fuzz/fuzz/fuzz_targets/no_diff.rs
+++ b/internal/integration_test/fuzz/fuzz/fuzz_targets/no_diff.rs
@@ -43,7 +43,7 @@ fn run(data: &[u8]) -> Result<()> {
     config.min_funcs = 1;
     config.max_funcs = config.max_funcs.max(1);
 
-    wazero_abi::maybe_disable_v2(&mut config);
+    wazero_abi::maybe_disable_simd(&mut config);
 
     // Generate the random module via wasm-smith.
     let mut module = wasm_smith::Module::new(config.clone(), &mut u)?;
diff --git a/internal/integration_test/fuzz/fuzz/fuzz_targets/wazero_abi.rs b/internal/integration_test/fuzz/fuzz/fuzz_targets/wazero_abi.rs
index 07f14dbc..418b38c2 100644
--- a/internal/integration_test/fuzz/fuzz/fuzz_targets/wazero_abi.rs
+++ b/internal/integration_test/fuzz/fuzz/fuzz_targets/wazero_abi.rs
@@ -19,13 +19,9 @@ extern "C" {
 }
 
 #[allow(dead_code)]
-pub fn maybe_disable_v2(config: &mut SwarmConfig) {
+pub fn maybe_disable_simd(config: &mut SwarmConfig) {
     if std::env::var("WAZERO_FUZZ_WAZEVO").is_ok() {
+        // Wazevo doesn't support SIMD yet.
         config.simd_enabled = false;
-        config.multi_value_enabled = false;
-        config.bulk_memory_enabled = false;
-        config.reference_types_enabled = false;
-        config.saturating_float_to_int_enabled = false;
-        config.max_tables = 1;
     }
 }
diff --git a/internal/integration_test/fuzz/wazerolib/lib.go b/internal/integration_test/fuzz/wazerolib/lib.go
index ca78be4f..70fc86dc 100644
--- a/internal/integration_test/fuzz/wazerolib/lib.go
+++ b/internal/integration_test/fuzz/wazerolib/lib.go
@@ -9,7 +9,6 @@ import (
 	"path"
 
 	"github.com/tetratelabs/wazero"
-	"github.com/tetratelabs/wazero/api"
 	"github.com/tetratelabs/wazero/internal/engine/wazevo"
 )
 
@@ -78,7 +77,6 @@ To reproduce the failure, execute: WASM_BINARY_PATH=%s go test -run=%s ./wazerol
 func newCompilerConfig() wazero.RuntimeConfig {
 	c := wazero.NewRuntimeConfigCompiler()
 	if os.Getenv("WAZERO_FUZZ_WAZEVO") != "" {
-		c = c.WithCoreFeatures(api.CoreFeaturesV1) // Currently only V1 is supported.
 		wazevo.ConfigureWazevo(c)
 	}
 	return c