wazevo: adds support for call_indirect (#1632)

Signed-off-by: Takeshi Yoneda <t.y.mathetake@gmail.com>
This commit is contained in:
Takeshi Yoneda
2023-08-16 13:07:13 +09:00
committed by GitHub
parent dbdd3f5541
commit 01dd74ce73
21 changed files with 770 additions and 262 deletions

View File

@@ -231,7 +231,7 @@ L3 (SSA Block: blk1):
L2 (SSA Block: blk2):
movz x27, #0x3, LSL 0
str w27, [x0?]
exit_sequence w0?
exit_sequence x0?
`,
afterFinalizeARM64: `
L1 (SSA Block: blk0):
@@ -244,7 +244,7 @@ L3 (SSA Block: blk1):
L2 (SSA Block: blk2):
movz x27, #0x3, LSL 0
str w27, [x0]
exit_sequence w0
exit_sequence x0
`,
},
{
@@ -1891,7 +1891,7 @@ L1 (SSA Block: blk0):
b.hs #0x20
movz x27, #0x4, LSL 0
str w27, [x0?]
exit_sequence w0?
exit_sequence x0?
ldr x8?, [x1?, #0x8]
add x11?, x8?, x4?
ldr w10?, [x11?]
@@ -1908,7 +1908,7 @@ L1 (SSA Block: blk0):
b.hs #0x20
movz x27, #0x4, LSL 0
str w27, [x0]
exit_sequence w0
exit_sequence x0
ldr x9, [x1, #0x8]
add x8, x9, x8
ldr w0, [x8]
@@ -1930,7 +1930,7 @@ L1 (SSA Block: blk0):
b.hs #0x20
movz x27, #0x4, LSL 0
str w27, [x0?]
exit_sequence w0?
exit_sequence x0?
ldr x8?, [x1?, #0x8]
add x200?, x8?, x4?
ldr w10?, [x200?]
@@ -1940,7 +1940,7 @@ L1 (SSA Block: blk0):
b.hs #0x20
movz x27, #0x4, LSL 0
str w27, [x0?]
exit_sequence w0?
exit_sequence x0?
add x199?, x8?, x12?
ldr x16?, [x199?]
uxtw x18?, w2?
@@ -1949,7 +1949,7 @@ L1 (SSA Block: blk0):
b.hs #0x20
movz x27, #0x4, LSL 0
str w27, [x0?]
exit_sequence w0?
exit_sequence x0?
add x198?, x8?, x18?
ldr s22?, [x198?]
uxtw x24?, w2?
@@ -1958,7 +1958,7 @@ L1 (SSA Block: blk0):
b.hs #0x20
movz x27, #0x4, LSL 0
str w27, [x0?]
exit_sequence w0?
exit_sequence x0?
add x197?, x8?, x24?
ldr d28?, [x197?]
uxtw x30?, w2?
@@ -1967,7 +1967,7 @@ L1 (SSA Block: blk0):
b.hs #0x20
movz x27, #0x4, LSL 0
str w27, [x0?]
exit_sequence w0?
exit_sequence x0?
add x196?, x8?, x30?
ldr w34?, [x196?, #0xf]
uxtw x36?, w2?
@@ -1976,7 +1976,7 @@ L1 (SSA Block: blk0):
b.hs #0x20
movz x27, #0x4, LSL 0
str w27, [x0?]
exit_sequence w0?
exit_sequence x0?
add x195?, x8?, x36?
ldr x40?, [x195?, #0xf]
uxtw x42?, w2?
@@ -1985,7 +1985,7 @@ L1 (SSA Block: blk0):
b.hs #0x20
movz x27, #0x4, LSL 0
str w27, [x0?]
exit_sequence w0?
exit_sequence x0?
add x194?, x8?, x42?
ldr s46?, [x194?, #0xf]
uxtw x48?, w2?
@@ -1994,7 +1994,7 @@ L1 (SSA Block: blk0):
b.hs #0x20
movz x27, #0x4, LSL 0
str w27, [x0?]
exit_sequence w0?
exit_sequence x0?
add x193?, x8?, x48?
ldr d52?, [x193?, #0xf]
uxtw x54?, w2?
@@ -2003,7 +2003,7 @@ L1 (SSA Block: blk0):
b.hs #0x20
movz x27, #0x4, LSL 0
str w27, [x0?]
exit_sequence w0?
exit_sequence x0?
add x192?, x8?, x54?
ldrsb w58?, [x192?]
uxtw x60?, w2?
@@ -2012,7 +2012,7 @@ L1 (SSA Block: blk0):
b.hs #0x20
movz x27, #0x4, LSL 0
str w27, [x0?]
exit_sequence w0?
exit_sequence x0?
add x191?, x8?, x60?
ldrsb w64?, [x191?, #0xf]
uxtw x66?, w2?
@@ -2021,7 +2021,7 @@ L1 (SSA Block: blk0):
b.hs #0x20
movz x27, #0x4, LSL 0
str w27, [x0?]
exit_sequence w0?
exit_sequence x0?
add x190?, x8?, x66?
ldrb w70?, [x190?]
uxtw x72?, w2?
@@ -2030,7 +2030,7 @@ L1 (SSA Block: blk0):
b.hs #0x20
movz x27, #0x4, LSL 0
str w27, [x0?]
exit_sequence w0?
exit_sequence x0?
add x189?, x8?, x72?
ldrb w76?, [x189?, #0xf]
uxtw x78?, w2?
@@ -2039,7 +2039,7 @@ L1 (SSA Block: blk0):
b.hs #0x20
movz x27, #0x4, LSL 0
str w27, [x0?]
exit_sequence w0?
exit_sequence x0?
add x188?, x8?, x78?
ldrsh w82?, [x188?]
uxtw x84?, w2?
@@ -2048,7 +2048,7 @@ L1 (SSA Block: blk0):
b.hs #0x20
movz x27, #0x4, LSL 0
str w27, [x0?]
exit_sequence w0?
exit_sequence x0?
add x187?, x8?, x84?
ldrsh w88?, [x187?, #0xf]
uxtw x90?, w2?
@@ -2057,7 +2057,7 @@ L1 (SSA Block: blk0):
b.hs #0x20
movz x27, #0x4, LSL 0
str w27, [x0?]
exit_sequence w0?
exit_sequence x0?
add x186?, x8?, x90?
ldrh w94?, [x186?]
uxtw x96?, w2?
@@ -2066,7 +2066,7 @@ L1 (SSA Block: blk0):
b.hs #0x20
movz x27, #0x4, LSL 0
str w27, [x0?]
exit_sequence w0?
exit_sequence x0?
add x185?, x8?, x96?
ldrh w100?, [x185?, #0xf]
uxtw x102?, w2?
@@ -2075,7 +2075,7 @@ L1 (SSA Block: blk0):
b.hs #0x20
movz x27, #0x4, LSL 0
str w27, [x0?]
exit_sequence w0?
exit_sequence x0?
add x184?, x8?, x102?
ldrsb w106?, [x184?]
uxtw x108?, w2?
@@ -2084,7 +2084,7 @@ L1 (SSA Block: blk0):
b.hs #0x20
movz x27, #0x4, LSL 0
str w27, [x0?]
exit_sequence w0?
exit_sequence x0?
add x183?, x8?, x108?
ldrsb w112?, [x183?, #0xf]
uxtw x114?, w2?
@@ -2093,7 +2093,7 @@ L1 (SSA Block: blk0):
b.hs #0x20
movz x27, #0x4, LSL 0
str w27, [x0?]
exit_sequence w0?
exit_sequence x0?
add x182?, x8?, x114?
ldrb w118?, [x182?]
uxtw x120?, w2?
@@ -2102,7 +2102,7 @@ L1 (SSA Block: blk0):
b.hs #0x20
movz x27, #0x4, LSL 0
str w27, [x0?]
exit_sequence w0?
exit_sequence x0?
add x181?, x8?, x120?
ldrb w124?, [x181?, #0xf]
uxtw x126?, w2?
@@ -2111,7 +2111,7 @@ L1 (SSA Block: blk0):
b.hs #0x20
movz x27, #0x4, LSL 0
str w27, [x0?]
exit_sequence w0?
exit_sequence x0?
add x180?, x8?, x126?
ldrsh w130?, [x180?]
uxtw x132?, w2?
@@ -2120,7 +2120,7 @@ L1 (SSA Block: blk0):
b.hs #0x20
movz x27, #0x4, LSL 0
str w27, [x0?]
exit_sequence w0?
exit_sequence x0?
add x179?, x8?, x132?
ldrsh w136?, [x179?, #0xf]
uxtw x138?, w2?
@@ -2129,7 +2129,7 @@ L1 (SSA Block: blk0):
b.hs #0x20
movz x27, #0x4, LSL 0
str w27, [x0?]
exit_sequence w0?
exit_sequence x0?
add x178?, x8?, x138?
ldrh w142?, [x178?]
uxtw x144?, w2?
@@ -2138,7 +2138,7 @@ L1 (SSA Block: blk0):
b.hs #0x20
movz x27, #0x4, LSL 0
str w27, [x0?]
exit_sequence w0?
exit_sequence x0?
add x177?, x8?, x144?
ldrh w148?, [x177?, #0xf]
uxtw x150?, w2?
@@ -2147,7 +2147,7 @@ L1 (SSA Block: blk0):
b.hs #0x20
movz x27, #0x4, LSL 0
str w27, [x0?]
exit_sequence w0?
exit_sequence x0?
add x176?, x8?, x150?
ldrs w154?, [x176?]
uxtw x156?, w2?
@@ -2156,7 +2156,7 @@ L1 (SSA Block: blk0):
b.hs #0x20
movz x27, #0x4, LSL 0
str w27, [x0?]
exit_sequence w0?
exit_sequence x0?
add x175?, x8?, x156?
ldrs w160?, [x175?, #0xf]
uxtw x162?, w2?
@@ -2165,7 +2165,7 @@ L1 (SSA Block: blk0):
b.hs #0x20
movz x27, #0x4, LSL 0
str w27, [x0?]
exit_sequence w0?
exit_sequence x0?
add x174?, x8?, x162?
ldr w166?, [x174?]
uxtw x168?, w2?
@@ -2174,7 +2174,7 @@ L1 (SSA Block: blk0):
b.hs #0x20
movz x27, #0x4, LSL 0
str w27, [x0?]
exit_sequence w0?
exit_sequence x0?
add x173?, x8?, x168?
ldr w172?, [x173?, #0xf]
str x172?, [#ret_space, #0x78]
@@ -2229,7 +2229,7 @@ L1 (SSA Block: blk0):
b.hs #0x20
movz x27, #0x4, LSL 0
str w27, [x8]
exit_sequence w8
exit_sequence x8
ldr x10, [x1, #0x8]
add x11, x10, x11
ldr w0, [x11]
@@ -2239,7 +2239,7 @@ L1 (SSA Block: blk0):
b.hs #0x20
movz x27, #0x4, LSL 0
str w27, [x8]
exit_sequence w8
exit_sequence x8
add x11, x10, x12
ldr x1, [x11]
uxtw x12, w2
@@ -2248,7 +2248,7 @@ L1 (SSA Block: blk0):
b.hs #0x20
movz x27, #0x4, LSL 0
str w27, [x8]
exit_sequence w8
exit_sequence x8
add x11, x10, x12
ldr s0, [x11]
uxtw x12, w2
@@ -2257,7 +2257,7 @@ L1 (SSA Block: blk0):
b.hs #0x20
movz x27, #0x4, LSL 0
str w27, [x8]
exit_sequence w8
exit_sequence x8
add x11, x10, x12
ldr d1, [x11]
uxtw x12, w2
@@ -2266,7 +2266,7 @@ L1 (SSA Block: blk0):
b.hs #0x20
movz x27, #0x4, LSL 0
str w27, [x8]
exit_sequence w8
exit_sequence x8
add x11, x10, x12
ldr w11, [x11, #0xf]
uxtw x13, w2
@@ -2275,7 +2275,7 @@ L1 (SSA Block: blk0):
b.hs #0x20
movz x27, #0x4, LSL 0
str w27, [x8]
exit_sequence w8
exit_sequence x8
add x12, x10, x13
ldr x3, [x12, #0xf]
uxtw x13, w2
@@ -2284,7 +2284,7 @@ L1 (SSA Block: blk0):
b.hs #0x20
movz x27, #0x4, LSL 0
str w27, [x8]
exit_sequence w8
exit_sequence x8
add x12, x10, x13
ldr s2, [x12, #0xf]
uxtw x13, w2
@@ -2293,7 +2293,7 @@ L1 (SSA Block: blk0):
b.hs #0x20
movz x27, #0x4, LSL 0
str w27, [x8]
exit_sequence w8
exit_sequence x8
add x12, x10, x13
ldr d3, [x12, #0xf]
uxtw x13, w2
@@ -2302,7 +2302,7 @@ L1 (SSA Block: blk0):
b.hs #0x20
movz x27, #0x4, LSL 0
str w27, [x8]
exit_sequence w8
exit_sequence x8
add x12, x10, x13
ldrsb w4, [x12]
uxtw x13, w2
@@ -2311,7 +2311,7 @@ L1 (SSA Block: blk0):
b.hs #0x20
movz x27, #0x4, LSL 0
str w27, [x8]
exit_sequence w8
exit_sequence x8
add x12, x10, x13
ldrsb w5, [x12, #0xf]
uxtw x13, w2
@@ -2320,7 +2320,7 @@ L1 (SSA Block: blk0):
b.hs #0x20
movz x27, #0x4, LSL 0
str w27, [x8]
exit_sequence w8
exit_sequence x8
add x12, x10, x13
ldrb w6, [x12]
uxtw x13, w2
@@ -2329,7 +2329,7 @@ L1 (SSA Block: blk0):
b.hs #0x20
movz x27, #0x4, LSL 0
str w27, [x8]
exit_sequence w8
exit_sequence x8
add x12, x10, x13
ldrb w7, [x12, #0xf]
uxtw x13, w2
@@ -2338,7 +2338,7 @@ L1 (SSA Block: blk0):
b.hs #0x20
movz x27, #0x4, LSL 0
str w27, [x8]
exit_sequence w8
exit_sequence x8
add x12, x10, x13
ldrsh w12, [x12]
uxtw x14, w2
@@ -2347,7 +2347,7 @@ L1 (SSA Block: blk0):
b.hs #0x20
movz x27, #0x4, LSL 0
str w27, [x8]
exit_sequence w8
exit_sequence x8
add x13, x10, x14
ldrsh w13, [x13, #0xf]
uxtw x15, w2
@@ -2356,7 +2356,7 @@ L1 (SSA Block: blk0):
b.hs #0x20
movz x27, #0x4, LSL 0
str w27, [x8]
exit_sequence w8
exit_sequence x8
add x14, x10, x15
ldrh w14, [x14]
uxtw x16, w2
@@ -2365,7 +2365,7 @@ L1 (SSA Block: blk0):
b.hs #0x20
movz x27, #0x4, LSL 0
str w27, [x8]
exit_sequence w8
exit_sequence x8
add x15, x10, x16
ldrh w15, [x15, #0xf]
uxtw x17, w2
@@ -2374,7 +2374,7 @@ L1 (SSA Block: blk0):
b.hs #0x20
movz x27, #0x4, LSL 0
str w27, [x8]
exit_sequence w8
exit_sequence x8
add x16, x10, x17
ldrsb w16, [x16]
uxtw x18, w2
@@ -2383,7 +2383,7 @@ L1 (SSA Block: blk0):
b.hs #0x20
movz x27, #0x4, LSL 0
str w27, [x8]
exit_sequence w8
exit_sequence x8
add x17, x10, x18
ldrsb w17, [x17, #0xf]
uxtw x19, w2
@@ -2392,7 +2392,7 @@ L1 (SSA Block: blk0):
b.hs #0x20
movz x27, #0x4, LSL 0
str w27, [x8]
exit_sequence w8
exit_sequence x8
add x18, x10, x19
ldrb w18, [x18]
uxtw x20, w2
@@ -2401,7 +2401,7 @@ L1 (SSA Block: blk0):
b.hs #0x20
movz x27, #0x4, LSL 0
str w27, [x8]
exit_sequence w8
exit_sequence x8
add x19, x10, x20
ldrb w19, [x19, #0xf]
uxtw x21, w2
@@ -2410,7 +2410,7 @@ L1 (SSA Block: blk0):
b.hs #0x20
movz x27, #0x4, LSL 0
str w27, [x8]
exit_sequence w8
exit_sequence x8
add x20, x10, x21
ldrsh w20, [x20]
uxtw x22, w2
@@ -2419,7 +2419,7 @@ L1 (SSA Block: blk0):
b.hs #0x20
movz x27, #0x4, LSL 0
str w27, [x8]
exit_sequence w8
exit_sequence x8
add x21, x10, x22
ldrsh w21, [x21, #0xf]
uxtw x23, w2
@@ -2428,7 +2428,7 @@ L1 (SSA Block: blk0):
b.hs #0x20
movz x27, #0x4, LSL 0
str w27, [x8]
exit_sequence w8
exit_sequence x8
add x22, x10, x23
ldrh w22, [x22]
uxtw x24, w2
@@ -2437,7 +2437,7 @@ L1 (SSA Block: blk0):
b.hs #0x20
movz x27, #0x4, LSL 0
str w27, [x8]
exit_sequence w8
exit_sequence x8
add x23, x10, x24
ldrh w23, [x23, #0xf]
uxtw x25, w2
@@ -2446,7 +2446,7 @@ L1 (SSA Block: blk0):
b.hs #0x20
movz x27, #0x4, LSL 0
str w27, [x8]
exit_sequence w8
exit_sequence x8
add x24, x10, x25
ldrs w24, [x24]
uxtw x26, w2
@@ -2455,7 +2455,7 @@ L1 (SSA Block: blk0):
b.hs #0x20
movz x27, #0x4, LSL 0
str w27, [x8]
exit_sequence w8
exit_sequence x8
add x25, x10, x26
ldrs w25, [x25, #0xf]
uxtw x28, w2
@@ -2464,7 +2464,7 @@ L1 (SSA Block: blk0):
b.hs #0x20
movz x27, #0x4, LSL 0
str w27, [x8]
exit_sequence w8
exit_sequence x8
add x26, x10, x28
ldr w26, [x26]
uxtw x29, w2
@@ -2473,7 +2473,7 @@ L1 (SSA Block: blk0):
b.hs #0x20
movz x27, #0x4, LSL 0
str w27, [x8]
exit_sequence w8
exit_sequence x8
add x8, x10, x29
ldr w8, [x8, #0xf]
str x8, [sp, #0x128]

View File

@@ -187,10 +187,10 @@ func (c *compiler) assignVirtualRegisters() {
need := len(refCounts)
if need >= len(c.ssaValueToVRegs) {
c.ssaValueToVRegs = append(c.ssaValueToVRegs, make([]regalloc.VReg, need)...)
c.ssaValueToVRegs = append(c.ssaValueToVRegs, make([]regalloc.VReg, need+1)...)
}
if need >= len(c.ssaValueDefinitions) {
c.ssaValueDefinitions = append(c.ssaValueDefinitions, make([]SSAValueDefinition, need)...)
c.ssaValueDefinitions = append(c.ssaValueDefinitions, make([]SSAValueDefinition, need+1)...)
}
for blk := builder.BlockIteratorReversePostOrderBegin(); blk != nil; blk = builder.BlockIteratorReversePostOrderNext() {

View File

@@ -76,7 +76,7 @@ func TestMachine_CompileGoFunctionTrampoline(t *testing.T) {
str x27, [x0, #0x38]
adr x27, #0x1c
str x27, [x0, #0x30]
exit_sequence w0
exit_sequence x0
ldr x18, [x0, #0x50]
ldr x19, [x0, #0x60]
ldr x20, [x0, #0x70]
@@ -160,7 +160,7 @@ func TestMachine_CompileGoFunctionTrampoline(t *testing.T) {
str x27, [x0, #0x38]
adr x27, #0x1c
str x27, [x0, #0x30]
exit_sequence w0
exit_sequence x0
ldr x18, [x0, #0x50]
ldr x19, [x0, #0x60]
ldr x20, [x0, #0x70]
@@ -231,7 +231,7 @@ func TestMachine_CompileGoFunctionTrampoline(t *testing.T) {
str x27, [x0, #0x38]
adr x27, #0x1c
str x27, [x0, #0x30]
exit_sequence w0
exit_sequence x0
ldr x18, [x0, #0x50]
ldr x19, [x0, #0x60]
ldr x20, [x0, #0x70]

View File

@@ -898,7 +898,7 @@ func (i *instruction) String() (str string) {
case loadAddr:
panic("TODO")
case exitSequence:
str = fmt.Sprintf("exit_sequence %s", formatVRegSized(i.rn.nr(), 32))
str = fmt.Sprintf("exit_sequence %s", formatVRegSized(i.rn.nr(), 64))
case udf:
str = "udf"
default:

View File

@@ -138,6 +138,17 @@ func (i *instruction) encode(c backend.Compiler) {
i.u3 == 1,
rn == sp,
))
case aluRRRShift:
r, amt, sop := i.rm.sr()
c.Emit4Bytes(encodeAluRRRShift(
aluOp(i.u1),
regNumberInEncoding[i.rd.realReg()],
regNumberInEncoding[i.rn.realReg()],
regNumberInEncoding[r.RealReg()],
uint32(amt),
sop,
i.u3 == 1,
))
case aluRRBitmaskImm:
c.Emit4Bytes(encodeAluBitmaskImmediate(
aluOp(i.u1),
@@ -560,6 +571,41 @@ func encodeAluRRImm12(op aluOp, rd, rn uint32, imm12 uint16, shiftBit byte, _64b
return _31to24<<24 | uint32(shiftBit)<<22 | uint32(imm12&0b111111111111)<<10 | rn<<5 | rd
}
// encodeAluRRR encodes as Data Processing (shifted register), depending on aluOp.
// https://developer.arm.com/documentation/ddi0596/2020-12/Index-by-Encoding/Data-Processing----Register?lang=en#addsub_shift
func encodeAluRRRShift(op aluOp, rd, rn, rm, amount uint32, shiftOp shiftOp, _64bit bool) uint32 {
var _31to24 uint32
switch op {
case aluOpAdd:
_31to24 = 0b00001011
case aluOpAddS:
_31to24 = 0b00101011
case aluOpSub:
_31to24 = 0b01001011
case aluOpSubS:
_31to24 = 0b01101011
default:
panic(op.String())
}
if _64bit {
_31to24 |= 0b1 << 7
}
var shift uint32
switch shiftOp {
case shiftOpLSL:
shift = 0b00
case shiftOpLSR:
shift = 0b01
case shiftOpASR:
shift = 0b10
default:
panic(shiftOp.String())
}
return _31to24<<24 | shift<<22 | rm<<16 | (amount << 10) | (rn << 5) | rd
}
// encodeAluRRR encodes as Data Processing (register), depending on aluOp.
// https://developer.arm.com/documentation/ddi0596/2020-12/Index-by-Encoding/Data-Processing----Register?lang=en
func encodeAluRRR(op aluOp, rd, rn, rm uint32, _64bit, isRnSp bool) uint32 {

View File

@@ -17,6 +17,78 @@ func TestInstruction_encode(t *testing.T) {
setup func(*instruction)
want string
}{
{want: "5b28030b", setup: func(i *instruction) {
i.asALU(aluOpAdd, operandNR(tmpRegVReg), operandNR(x2VReg), operandSR(x3VReg, 10, shiftOpLSL), false)
}},
{want: "5b28038b", setup: func(i *instruction) {
i.asALU(aluOpAdd, operandNR(tmpRegVReg), operandNR(x2VReg), operandSR(x3VReg, 10, shiftOpLSL), true)
}},
{want: "5b28032b", setup: func(i *instruction) {
i.asALU(aluOpAddS, operandNR(tmpRegVReg), operandNR(x2VReg), operandSR(x3VReg, 10, shiftOpLSL), false)
}},
{want: "5b2803ab", setup: func(i *instruction) {
i.asALU(aluOpAddS, operandNR(tmpRegVReg), operandNR(x2VReg), operandSR(x3VReg, 10, shiftOpLSL), true)
}},
{want: "5b28430b", setup: func(i *instruction) {
i.asALU(aluOpAdd, operandNR(tmpRegVReg), operandNR(x2VReg), operandSR(x3VReg, 10, shiftOpLSR), false)
}},
{want: "5b28438b", setup: func(i *instruction) {
i.asALU(aluOpAdd, operandNR(tmpRegVReg), operandNR(x2VReg), operandSR(x3VReg, 10, shiftOpLSR), true)
}},
{want: "5b28432b", setup: func(i *instruction) {
i.asALU(aluOpAddS, operandNR(tmpRegVReg), operandNR(x2VReg), operandSR(x3VReg, 10, shiftOpLSR), false)
}},
{want: "5b2843ab", setup: func(i *instruction) {
i.asALU(aluOpAddS, operandNR(tmpRegVReg), operandNR(x2VReg), operandSR(x3VReg, 10, shiftOpLSR), true)
}},
{want: "5b28830b", setup: func(i *instruction) {
i.asALU(aluOpAdd, operandNR(tmpRegVReg), operandNR(x2VReg), operandSR(x3VReg, 10, shiftOpASR), false)
}},
{want: "5b28838b", setup: func(i *instruction) {
i.asALU(aluOpAdd, operandNR(tmpRegVReg), operandNR(x2VReg), operandSR(x3VReg, 10, shiftOpASR), true)
}},
{want: "5b28832b", setup: func(i *instruction) {
i.asALU(aluOpAddS, operandNR(tmpRegVReg), operandNR(x2VReg), operandSR(x3VReg, 10, shiftOpASR), false)
}},
{want: "5b2883ab", setup: func(i *instruction) {
i.asALU(aluOpAddS, operandNR(tmpRegVReg), operandNR(x2VReg), operandSR(x3VReg, 10, shiftOpASR), true)
}},
{want: "5b28034b", setup: func(i *instruction) {
i.asALU(aluOpSub, operandNR(tmpRegVReg), operandNR(x2VReg), operandSR(x3VReg, 10, shiftOpLSL), false)
}},
{want: "5b2803cb", setup: func(i *instruction) {
i.asALU(aluOpSub, operandNR(tmpRegVReg), operandNR(x2VReg), operandSR(x3VReg, 10, shiftOpLSL), true)
}},
{want: "5b28036b", setup: func(i *instruction) {
i.asALU(aluOpSubS, operandNR(tmpRegVReg), operandNR(x2VReg), operandSR(x3VReg, 10, shiftOpLSL), false)
}},
{want: "5b2803eb", setup: func(i *instruction) {
i.asALU(aluOpSubS, operandNR(tmpRegVReg), operandNR(x2VReg), operandSR(x3VReg, 10, shiftOpLSL), true)
}},
{want: "5b28434b", setup: func(i *instruction) {
i.asALU(aluOpSub, operandNR(tmpRegVReg), operandNR(x2VReg), operandSR(x3VReg, 10, shiftOpLSR), false)
}},
{want: "5b2843cb", setup: func(i *instruction) {
i.asALU(aluOpSub, operandNR(tmpRegVReg), operandNR(x2VReg), operandSR(x3VReg, 10, shiftOpLSR), true)
}},
{want: "5b28436b", setup: func(i *instruction) {
i.asALU(aluOpSubS, operandNR(tmpRegVReg), operandNR(x2VReg), operandSR(x3VReg, 10, shiftOpLSR), false)
}},
{want: "5b2843eb", setup: func(i *instruction) {
i.asALU(aluOpSubS, operandNR(tmpRegVReg), operandNR(x2VReg), operandSR(x3VReg, 10, shiftOpLSR), true)
}},
{want: "5b28834b", setup: func(i *instruction) {
i.asALU(aluOpSub, operandNR(tmpRegVReg), operandNR(x2VReg), operandSR(x3VReg, 10, shiftOpASR), false)
}},
{want: "5b2883cb", setup: func(i *instruction) {
i.asALU(aluOpSub, operandNR(tmpRegVReg), operandNR(x2VReg), operandSR(x3VReg, 10, shiftOpASR), true)
}},
{want: "5b28836b", setup: func(i *instruction) {
i.asALU(aluOpSubS, operandNR(tmpRegVReg), operandNR(x2VReg), operandSR(x3VReg, 10, shiftOpASR), false)
}},
{want: "5b2883eb", setup: func(i *instruction) {
i.asALU(aluOpSubS, operandNR(tmpRegVReg), operandNR(x2VReg), operandSR(x3VReg, 10, shiftOpASR), true)
}},
{want: "60033fd6", setup: func(i *instruction) {
i.asCallIndirect(tmpRegVReg, nil)
}},

View File

@@ -130,9 +130,9 @@ func (m *machine) LowerInstr(instr *ssa.Instruction) {
case ssa.OpcodeExitWithCode:
execCtx, code := instr.ExitWithCodeData()
m.lowerExitWithCode(m.compiler.VRegOf(execCtx), code)
case ssa.OpcodeExitIfNotZeroWithCode:
execCtx, c, code := instr.ExitIfNotZeroWithCodeData()
m.lowerExitIfNotZeroWithCode(m.compiler.VRegOf(execCtx), c, code)
case ssa.OpcodeExitIfTrueWithCode:
execCtx, c, code := instr.ExitIfTrueWithCodeData()
m.lowerExitIfTrueWithCode(m.compiler.VRegOf(execCtx), c, code)
case ssa.OpcodeStore, ssa.OpcodeIstore8, ssa.OpcodeIstore16, ssa.OpcodeIstore32:
m.lowerStore(instr)
case ssa.OpcodeLoad:
@@ -341,11 +341,11 @@ func (m *machine) lowerExitWithCode(execCtxVReg regalloc.VReg, code wazevoapi.Ex
m.insert(exitSeq)
}
func (m *machine) lowerExitIfNotZeroWithCode(execCtxVReg regalloc.VReg, cond ssa.Value, code wazevoapi.ExitCode) {
func (m *machine) lowerExitIfTrueWithCode(execCtxVReg regalloc.VReg, cond ssa.Value, code wazevoapi.ExitCode) {
condDef := m.compiler.ValueDefinition(cond)
if !m.compiler.MatchInstr(condDef, ssa.OpcodeIcmp) {
// We can have general case just like cachine.LowerConditionalBranch.
panic("TODO: OpcodeExitIfNotZeroWithCode must come after Icmp at the moment")
panic("TODO: OpcodeExitIfTrueWithCode must come after Icmp at the moment")
}
m.compiler.MarkLowered(condDef.Instr)
@@ -378,7 +378,7 @@ func (m *machine) lowerExitIfNotZeroWithCode(execCtxVReg regalloc.VReg, cond ssa
// We have to skip the entire exit sequence if the condition is false.
cbr := m.allocateInstr()
cbr.asCondBr(cc.asCond(), invalidLabel, false /* ignored */)
cbr.asCondBr(cc.invert().asCond(), invalidLabel, false /* ignored */)
cbr.condBrOffsetResolve(exitWithCodeEncodingSize + 4 /* br offset is from the beginning of this instruction */)
m.insert(cbr)
m.lowerExitWithCode(execCtxVReg, code)

View File

@@ -80,9 +80,6 @@ func (o operand) er() (r regalloc.VReg, eop extendOp, to byte) {
// operandSR encodes the given VReg as an operand of operandKindSR.
func operandSR(r regalloc.VReg, amt byte, sop shiftOp) operand {
if sop != shiftOpLSL {
panic("TODO: do we need to support other shift operations?")
}
return operand{kind: operandKindSR, data: uint64(r), data2: uint64(amt)<<32 | uint64(sop)}
}

View File

@@ -223,7 +223,7 @@ func TestMachine_insertStackBoundsCheck(t *testing.T) {
str x27, [x0, #0x40]
adr x27, #0x1c
str x27, [x0, #0x30]
exit_sequence w0
exit_sequence x0
ldr x1, [x0, #0x50]
ldr x2, [x0, #0x60]
ldr x3, [x0, #0x70]
@@ -321,7 +321,7 @@ func TestMachine_insertStackBoundsCheck(t *testing.T) {
str x27, [x0, #0x40]
adr x27, #0x1c
str x27, [x0, #0x30]
exit_sequence w0
exit_sequence x0
ldr x1, [x0, #0x50]
ldr x2, [x0, #0x60]
ldr x3, [x0, #0x70]

View File

@@ -162,6 +162,12 @@ func (c *callEngine) CallWithStack(ctx context.Context, paramResultStack []uint6
f.Call(ctx, mod, c.execCtx.goFunctionCallStack[:])
c.execCtx.exitCode = wazevoapi.ExitCodeOK
afterGoFunctionCallEntrypoint(c.execCtx.goCallReturnAddress, c.execCtxPtr, c.execCtx.stackPointerBeforeGoCall)
case wazevoapi.ExitCodeTableOutOfBounds:
return wasmruntime.ErrRuntimeInvalidTableAccess
case wazevoapi.ExitCodeIndirectCallNullPointer:
return wasmruntime.ErrRuntimeInvalidTableAccess
case wazevoapi.ExitCodeIndirectCallTypeMismatch:
return wasmruntime.ErrRuntimeIndirectCallTypeMismatch
default:
panic("BUG")
}

View File

@@ -153,6 +153,18 @@ func TestE2E(t *testing.T) {
m: testcases.ImportedMemoryGrow.Module,
calls: []callCase{{expResults: []uint64{1, 1, 11, 11}}},
},
{
name: "call_indirect",
m: testcases.CallIndirect.Module,
// parameter == table offset.
calls: []callCase{
{params: []uint64{0}, expErr: "indirect call type mismatch"},
{params: []uint64{1}, expResults: []uint64{10}},
{params: []uint64{2}, expErr: "indirect call type mismatch"},
{params: []uint64{10}, expErr: "invalid table access"}, // Null pointer.
{params: []uint64{math.MaxUint32}, expErr: "invalid table access"}, // Out of bounds.
},
},
} {
tc := tc
t.Run(tc.name, func(t *testing.T) {

View File

@@ -869,8 +869,8 @@ blk0: (exec_ctx:i64, module_ctx:i64, v2:i32)
v4:i64 = UExtend v2, 32->64
v5:i64 = Uload32 module_ctx, 0x10
v6:i64 = Iadd v4, v3
v7:i32 = Icmp ge_u, v5, v6
ExitIfNotZero v7, exec_ctx, memory_out_of_bounds
v7:i32 = Icmp lt_u, v5, v6
ExitIfTrue v7, exec_ctx, memory_out_of_bounds
v8:i64 = Load module_ctx, 0x8
v9:i64 = Iadd v8, v4
v10:i32 = Load v9, 0x0
@@ -904,8 +904,8 @@ blk3: (v7:i32) <-- (blk1,blk2)
v9:i64 = UExtend v7, 32->64
v10:i64 = Uload32 module_ctx, 0x10
v11:i64 = Iadd v9, v8
v12:i32 = Icmp ge_u, v10, v11
ExitIfNotZero v12, exec_ctx, memory_out_of_bounds
v12:i32 = Icmp lt_u, v10, v11
ExitIfTrue v12, exec_ctx, memory_out_of_bounds
v13:i64 = Load module_ctx, 0x8
v14:i64 = Iadd v13, v9
v15:i32 = Load v14, 0x0
@@ -934,8 +934,8 @@ blk3: () <-- (blk1,blk2)
v9:i64 = UExtend v2, 32->64
v10:i64 = Uload32 module_ctx, 0x10
v11:i64 = Iadd v9, v8
v12:i32 = Icmp ge_u, v10, v11
ExitIfNotZero v12, exec_ctx, memory_out_of_bounds
v12:i32 = Icmp lt_u, v10, v11
ExitIfTrue v12, exec_ctx, memory_out_of_bounds
v13:i64 = Load module_ctx, 0x8
v14:i64 = Iadd v13, v9
v15:i32 = Load v14, 0x0
@@ -964,198 +964,198 @@ blk0: (exec_ctx:i64, module_ctx:i64, v2:i32)
v4:i64 = UExtend v2, 32->64
v5:i64 = Uload32 module_ctx, 0x10
v6:i64 = Iadd v4, v3
v7:i32 = Icmp ge_u, v5, v6
ExitIfNotZero v7, exec_ctx, memory_out_of_bounds
v7:i32 = Icmp lt_u, v5, v6
ExitIfTrue v7, exec_ctx, memory_out_of_bounds
v8:i64 = Load module_ctx, 0x8
v9:i64 = Iadd v8, v4
v10:i32 = Load v9, 0x0
v11:i64 = Iconst_64 0x8
v12:i64 = UExtend v2, 32->64
v13:i64 = Iadd v12, v11
v14:i32 = Icmp ge_u, v5, v13
ExitIfNotZero v14, exec_ctx, memory_out_of_bounds
v14:i32 = Icmp lt_u, v5, v13
ExitIfTrue v14, exec_ctx, memory_out_of_bounds
v15:i64 = Iadd v8, v12
v16:i64 = Load v15, 0x0
v17:i64 = Iconst_64 0x4
v18:i64 = UExtend v2, 32->64
v19:i64 = Iadd v18, v17
v20:i32 = Icmp ge_u, v5, v19
ExitIfNotZero v20, exec_ctx, memory_out_of_bounds
v20:i32 = Icmp lt_u, v5, v19
ExitIfTrue v20, exec_ctx, memory_out_of_bounds
v21:i64 = Iadd v8, v18
v22:f32 = Load v21, 0x0
v23:i64 = Iconst_64 0x8
v24:i64 = UExtend v2, 32->64
v25:i64 = Iadd v24, v23
v26:i32 = Icmp ge_u, v5, v25
ExitIfNotZero v26, exec_ctx, memory_out_of_bounds
v26:i32 = Icmp lt_u, v5, v25
ExitIfTrue v26, exec_ctx, memory_out_of_bounds
v27:i64 = Iadd v8, v24
v28:f64 = Load v27, 0x0
v29:i64 = Iconst_64 0x13
v30:i64 = UExtend v2, 32->64
v31:i64 = Iadd v30, v29
v32:i32 = Icmp ge_u, v5, v31
ExitIfNotZero v32, exec_ctx, memory_out_of_bounds
v32:i32 = Icmp lt_u, v5, v31
ExitIfTrue v32, exec_ctx, memory_out_of_bounds
v33:i64 = Iadd v8, v30
v34:i32 = Load v33, 0xf
v35:i64 = Iconst_64 0x17
v36:i64 = UExtend v2, 32->64
v37:i64 = Iadd v36, v35
v38:i32 = Icmp ge_u, v5, v37
ExitIfNotZero v38, exec_ctx, memory_out_of_bounds
v38:i32 = Icmp lt_u, v5, v37
ExitIfTrue v38, exec_ctx, memory_out_of_bounds
v39:i64 = Iadd v8, v36
v40:i64 = Load v39, 0xf
v41:i64 = Iconst_64 0x13
v42:i64 = UExtend v2, 32->64
v43:i64 = Iadd v42, v41
v44:i32 = Icmp ge_u, v5, v43
ExitIfNotZero v44, exec_ctx, memory_out_of_bounds
v44:i32 = Icmp lt_u, v5, v43
ExitIfTrue v44, exec_ctx, memory_out_of_bounds
v45:i64 = Iadd v8, v42
v46:f32 = Load v45, 0xf
v47:i64 = Iconst_64 0x17
v48:i64 = UExtend v2, 32->64
v49:i64 = Iadd v48, v47
v50:i32 = Icmp ge_u, v5, v49
ExitIfNotZero v50, exec_ctx, memory_out_of_bounds
v50:i32 = Icmp lt_u, v5, v49
ExitIfTrue v50, exec_ctx, memory_out_of_bounds
v51:i64 = Iadd v8, v48
v52:f64 = Load v51, 0xf
v53:i64 = Iconst_64 0x1
v54:i64 = UExtend v2, 32->64
v55:i64 = Iadd v54, v53
v56:i32 = Icmp ge_u, v5, v55
ExitIfNotZero v56, exec_ctx, memory_out_of_bounds
v56:i32 = Icmp lt_u, v5, v55
ExitIfTrue v56, exec_ctx, memory_out_of_bounds
v57:i64 = Iadd v8, v54
v58:i32 = Sload8 v57, 0x0
v59:i64 = Iconst_64 0x10
v60:i64 = UExtend v2, 32->64
v61:i64 = Iadd v60, v59
v62:i32 = Icmp ge_u, v5, v61
ExitIfNotZero v62, exec_ctx, memory_out_of_bounds
v62:i32 = Icmp lt_u, v5, v61
ExitIfTrue v62, exec_ctx, memory_out_of_bounds
v63:i64 = Iadd v8, v60
v64:i32 = Sload8 v63, 0xf
v65:i64 = Iconst_64 0x1
v66:i64 = UExtend v2, 32->64
v67:i64 = Iadd v66, v65
v68:i32 = Icmp ge_u, v5, v67
ExitIfNotZero v68, exec_ctx, memory_out_of_bounds
v68:i32 = Icmp lt_u, v5, v67
ExitIfTrue v68, exec_ctx, memory_out_of_bounds
v69:i64 = Iadd v8, v66
v70:i32 = Uload8 v69, 0x0
v71:i64 = Iconst_64 0x10
v72:i64 = UExtend v2, 32->64
v73:i64 = Iadd v72, v71
v74:i32 = Icmp ge_u, v5, v73
ExitIfNotZero v74, exec_ctx, memory_out_of_bounds
v74:i32 = Icmp lt_u, v5, v73
ExitIfTrue v74, exec_ctx, memory_out_of_bounds
v75:i64 = Iadd v8, v72
v76:i32 = Uload8 v75, 0xf
v77:i64 = Iconst_64 0x2
v78:i64 = UExtend v2, 32->64
v79:i64 = Iadd v78, v77
v80:i32 = Icmp ge_u, v5, v79
ExitIfNotZero v80, exec_ctx, memory_out_of_bounds
v80:i32 = Icmp lt_u, v5, v79
ExitIfTrue v80, exec_ctx, memory_out_of_bounds
v81:i64 = Iadd v8, v78
v82:i32 = Sload16 v81, 0x0
v83:i64 = Iconst_64 0x11
v84:i64 = UExtend v2, 32->64
v85:i64 = Iadd v84, v83
v86:i32 = Icmp ge_u, v5, v85
ExitIfNotZero v86, exec_ctx, memory_out_of_bounds
v86:i32 = Icmp lt_u, v5, v85
ExitIfTrue v86, exec_ctx, memory_out_of_bounds
v87:i64 = Iadd v8, v84
v88:i32 = Sload16 v87, 0xf
v89:i64 = Iconst_64 0x2
v90:i64 = UExtend v2, 32->64
v91:i64 = Iadd v90, v89
v92:i32 = Icmp ge_u, v5, v91
ExitIfNotZero v92, exec_ctx, memory_out_of_bounds
v92:i32 = Icmp lt_u, v5, v91
ExitIfTrue v92, exec_ctx, memory_out_of_bounds
v93:i64 = Iadd v8, v90
v94:i32 = Uload16 v93, 0x0
v95:i64 = Iconst_64 0x11
v96:i64 = UExtend v2, 32->64
v97:i64 = Iadd v96, v95
v98:i32 = Icmp ge_u, v5, v97
ExitIfNotZero v98, exec_ctx, memory_out_of_bounds
v98:i32 = Icmp lt_u, v5, v97
ExitIfTrue v98, exec_ctx, memory_out_of_bounds
v99:i64 = Iadd v8, v96
v100:i32 = Uload16 v99, 0xf
v101:i64 = Iconst_64 0x1
v102:i64 = UExtend v2, 32->64
v103:i64 = Iadd v102, v101
v104:i32 = Icmp ge_u, v5, v103
ExitIfNotZero v104, exec_ctx, memory_out_of_bounds
v104:i32 = Icmp lt_u, v5, v103
ExitIfTrue v104, exec_ctx, memory_out_of_bounds
v105:i64 = Iadd v8, v102
v106:i64 = Sload8 v105, 0x0
v107:i64 = Iconst_64 0x10
v108:i64 = UExtend v2, 32->64
v109:i64 = Iadd v108, v107
v110:i32 = Icmp ge_u, v5, v109
ExitIfNotZero v110, exec_ctx, memory_out_of_bounds
v110:i32 = Icmp lt_u, v5, v109
ExitIfTrue v110, exec_ctx, memory_out_of_bounds
v111:i64 = Iadd v8, v108
v112:i64 = Sload8 v111, 0xf
v113:i64 = Iconst_64 0x1
v114:i64 = UExtend v2, 32->64
v115:i64 = Iadd v114, v113
v116:i32 = Icmp ge_u, v5, v115
ExitIfNotZero v116, exec_ctx, memory_out_of_bounds
v116:i32 = Icmp lt_u, v5, v115
ExitIfTrue v116, exec_ctx, memory_out_of_bounds
v117:i64 = Iadd v8, v114
v118:i64 = Uload8 v117, 0x0
v119:i64 = Iconst_64 0x10
v120:i64 = UExtend v2, 32->64
v121:i64 = Iadd v120, v119
v122:i32 = Icmp ge_u, v5, v121
ExitIfNotZero v122, exec_ctx, memory_out_of_bounds
v122:i32 = Icmp lt_u, v5, v121
ExitIfTrue v122, exec_ctx, memory_out_of_bounds
v123:i64 = Iadd v8, v120
v124:i64 = Uload8 v123, 0xf
v125:i64 = Iconst_64 0x2
v126:i64 = UExtend v2, 32->64
v127:i64 = Iadd v126, v125
v128:i32 = Icmp ge_u, v5, v127
ExitIfNotZero v128, exec_ctx, memory_out_of_bounds
v128:i32 = Icmp lt_u, v5, v127
ExitIfTrue v128, exec_ctx, memory_out_of_bounds
v129:i64 = Iadd v8, v126
v130:i64 = Sload16 v129, 0x0
v131:i64 = Iconst_64 0x11
v132:i64 = UExtend v2, 32->64
v133:i64 = Iadd v132, v131
v134:i32 = Icmp ge_u, v5, v133
ExitIfNotZero v134, exec_ctx, memory_out_of_bounds
v134:i32 = Icmp lt_u, v5, v133
ExitIfTrue v134, exec_ctx, memory_out_of_bounds
v135:i64 = Iadd v8, v132
v136:i64 = Sload16 v135, 0xf
v137:i64 = Iconst_64 0x2
v138:i64 = UExtend v2, 32->64
v139:i64 = Iadd v138, v137
v140:i32 = Icmp ge_u, v5, v139
ExitIfNotZero v140, exec_ctx, memory_out_of_bounds
v140:i32 = Icmp lt_u, v5, v139
ExitIfTrue v140, exec_ctx, memory_out_of_bounds
v141:i64 = Iadd v8, v138
v142:i64 = Uload16 v141, 0x0
v143:i64 = Iconst_64 0x11
v144:i64 = UExtend v2, 32->64
v145:i64 = Iadd v144, v143
v146:i32 = Icmp ge_u, v5, v145
ExitIfNotZero v146, exec_ctx, memory_out_of_bounds
v146:i32 = Icmp lt_u, v5, v145
ExitIfTrue v146, exec_ctx, memory_out_of_bounds
v147:i64 = Iadd v8, v144
v148:i64 = Uload16 v147, 0xf
v149:i64 = Iconst_64 0x4
v150:i64 = UExtend v2, 32->64
v151:i64 = Iadd v150, v149
v152:i32 = Icmp ge_u, v5, v151
ExitIfNotZero v152, exec_ctx, memory_out_of_bounds
v152:i32 = Icmp lt_u, v5, v151
ExitIfTrue v152, exec_ctx, memory_out_of_bounds
v153:i64 = Iadd v8, v150
v154:i64 = Sload32 v153, 0x0
v155:i64 = Iconst_64 0x13
v156:i64 = UExtend v2, 32->64
v157:i64 = Iadd v156, v155
v158:i32 = Icmp ge_u, v5, v157
ExitIfNotZero v158, exec_ctx, memory_out_of_bounds
v158:i32 = Icmp lt_u, v5, v157
ExitIfTrue v158, exec_ctx, memory_out_of_bounds
v159:i64 = Iadd v8, v156
v160:i64 = Sload32 v159, 0xf
v161:i64 = Iconst_64 0x4
v162:i64 = UExtend v2, 32->64
v163:i64 = Iadd v162, v161
v164:i32 = Icmp ge_u, v5, v163
ExitIfNotZero v164, exec_ctx, memory_out_of_bounds
v164:i32 = Icmp lt_u, v5, v163
ExitIfTrue v164, exec_ctx, memory_out_of_bounds
v165:i64 = Iadd v8, v162
v166:i64 = Uload32 v165, 0x0
v167:i64 = Iconst_64 0x13
v168:i64 = UExtend v2, 32->64
v169:i64 = Iadd v168, v167
v170:i32 = Icmp ge_u, v5, v169
ExitIfNotZero v170, exec_ctx, memory_out_of_bounds
v170:i32 = Icmp lt_u, v5, v169
ExitIfTrue v170, exec_ctx, memory_out_of_bounds
v171:i64 = Iadd v8, v168
v172:i64 = Uload32 v171, 0xf
Jump blk_ret, v10, v16, v22, v28, v34, v40, v46, v52, v58, v64, v70, v76, v82, v88, v94, v100, v106, v112, v118, v124, v130, v136, v142, v148, v154, v160, v166, v172
@@ -1365,6 +1365,37 @@ blk0: (exec_ctx:i64, module_ctx:i64)
v11:i64 = Load exec_ctx, 0x48
v12:i32 = CallIndirect v11:sig1, exec_ctx, v10
Jump blk_ret, v4, v9, v12
`,
},
{
name: "call_indirect", m: testcases.CallIndirect.Module,
exp: `
signatures:
sig2: i64i64_i32
blk0: (exec_ctx:i64, module_ctx:i64, v2:i32)
v3:i64 = Load module_ctx, 0x10
v4:i32 = Load v3, 0x8
v5:i32 = Icmp ge_u, v2, v4
ExitIfTrue v5, exec_ctx, table_out_of_bounds
v6:i64 = Load v3, 0x0
v7:i64 = Iconst_64 0x3
v8:i32 = Ishl v2, v7
v9:i64 = Iadd v6, v8
v10:i64 = Load v9, 0x0
v11:i64 = Iconst_64 0x0
v12:i32 = Icmp eq, v10, v11
ExitIfTrue v12, exec_ctx, indirect_call_null_pointer
v13:i32 = Load v10, 0x10
v14:i64 = Load module_ctx, 0x8
v15:i32 = Load v14, 0x8
v16:i32 = Icmp neq, v13, v15
ExitIfTrue v16, exec_ctx, indirect_call_type_mismatch
v17:i64 = Load v10, 0x0
v18:i64 = Load v10, 0x8
Store module_ctx, exec_ctx, 0x8
v19:i32 = CallIndirect v17:sig2, exec_ctx, v18
Jump blk_ret, v19
`,
},
} {

View File

@@ -144,9 +144,13 @@ func (c *Compiler) lowerBody(entryBlk ssa.BasicBlock) {
}
}
func (c *Compiler) state() *loweringState {
return &c.loweringState
}
func (c *Compiler) lowerOpcode(op wasm.Opcode) {
builder := c.ssaBuilder
state := &c.loweringState
state := c.state()
switch op {
case wasm.OpcodeI32Const:
c := c.readI32s()
@@ -564,10 +568,10 @@ func (c *Compiler) lowerOpcode(op wasm.Opcode) {
// Check for out of bounds memory access: `memLen >= baseAddrPlusCeil`.
cmp := builder.AllocateInstruction()
cmp.AsIcmp(memLen, baseAddrPlusCeil.Return(), ssa.IntegerCmpCondUnsignedGreaterThanOrEqual)
cmp.AsIcmp(memLen, baseAddrPlusCeil.Return(), ssa.IntegerCmpCondUnsignedLessThan)
builder.InsertInstruction(cmp)
exitIfNZ := builder.AllocateInstruction()
exitIfNZ.AsExitIfNotZeroWithCode(c.execCtxPtrValue, cmp.Return(), wazevoapi.ExitCodeMemoryOutOfBounds)
exitIfNZ.AsExitIfTrueWithCode(c.execCtxPtrValue, cmp.Return(), wazevoapi.ExitCodeMemoryOutOfBounds)
builder.InsertInstruction(exitIfNZ)
// Load the value from memBase + extBaseAddr.
@@ -842,6 +846,14 @@ func (c *Compiler) lowerOpcode(op wasm.Opcode) {
builder.InsertInstruction(exit)
state.unreachable = true
case wasm.OpcodeCallIndirect:
typeIndex := c.readI32u()
tableIndex := c.readI32u()
if state.unreachable {
return
}
c.lowerCallIndirect(typeIndex, tableIndex)
case wasm.OpcodeCall:
fnIndex := c.readI32u()
if state.unreachable {
@@ -889,7 +901,7 @@ func (c *Compiler) lowerOpcode(op wasm.Opcode) {
// This case we have to read the address of the imported function from the module context.
moduleCtx := c.moduleCtxPtrValue
loadFuncPtr, loadModuleCtxPtr := builder.AllocateInstruction(), builder.AllocateInstruction()
funcPtrOffset, moduleCtxPtrOffset := c.offset.ImportedFunctionOffset(fnIndex)
funcPtrOffset, moduleCtxPtrOffset, _ := c.offset.ImportedFunctionOffset(fnIndex)
loadFuncPtr.AsLoad(moduleCtx, funcPtrOffset.U32(), ssa.TypeI64)
loadModuleCtxPtr.AsLoad(moduleCtx, moduleCtxPtrOffset.U32(), ssa.TypeI64)
builder.InsertInstruction(loadFuncPtr)
@@ -918,6 +930,135 @@ func (c *Compiler) lowerOpcode(op wasm.Opcode) {
}
}
const (
tableInstanceBaseAddressOffset = 0
tableInstanceLenOffset = tableInstanceBaseAddressOffset + 8
)
func (c *Compiler) lowerCallIndirect(typeIndex, tableIndex uint32) {
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)
builder.InsertInstruction(loadTableInstancePtr)
tableInstancePtr := loadTableInstancePtr.Return()
// Load the table's length.
loadTableLen := builder.AllocateInstruction()
loadTableLen.AsLoad(tableInstancePtr, tableInstanceLenOffset, ssa.TypeI32)
builder.InsertInstruction(loadTableLen)
tableLen := loadTableLen.Return()
// Compare the length and the target, and trap if out of bounds.
checkOOB := builder.AllocateInstruction()
checkOOB.AsIcmp(targetOffsetInTable, tableLen, ssa.IntegerCmpCondUnsignedGreaterThanOrEqual)
builder.InsertInstruction(checkOOB)
exitIfOOB := builder.AllocateInstruction()
exitIfOOB.AsExitIfTrueWithCode(c.execCtxPtrValue, checkOOB.Return(), wazevoapi.ExitCodeTableOutOfBounds)
builder.InsertInstruction(exitIfOOB)
// Get the base address of wasm.TableInstance.References.
loadTableBaseAddress := builder.AllocateInstruction()
loadTableBaseAddress.AsLoad(tableInstancePtr, tableInstanceBaseAddressOffset, ssa.TypeI64)
builder.InsertInstruction(loadTableBaseAddress)
tableBase := loadTableBaseAddress.Return()
// Calculate the address of the target function. First we need to multiply targetOffsetInTable by 8 (pointer size).
multiplyBy8 := builder.AllocateInstruction()
three := builder.AllocateInstruction()
three.AsIconst64(3)
builder.InsertInstruction(three)
multiplyBy8.AsIshl(targetOffsetInTable, 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()
loadFunctionInstancePtr := builder.AllocateInstruction()
loadFunctionInstancePtr.AsLoad(functionInstancePtrAddress, 0, ssa.TypeI64)
builder.InsertInstruction(loadFunctionInstancePtr)
functionInstancePtr := loadFunctionInstancePtr.Return()
// Check if it is not the null pointer.
zero := builder.AllocateInstruction()
zero.AsIconst64(0)
builder.InsertInstruction(zero)
checkNull := builder.AllocateInstruction()
checkNull.AsIcmp(functionInstancePtr, zero.Return(), ssa.IntegerCmpCondEqual)
builder.InsertInstruction(checkNull)
exitIfNull := builder.AllocateInstruction()
exitIfNull.AsExitIfTrueWithCode(c.execCtxPtrValue, checkNull.Return(), wazevoapi.ExitCodeIndirectCallNullPointer)
builder.InsertInstruction(exitIfNull)
// We need to do the type check. First, load the target function instance's typeID.
loadTypeID := builder.AllocateInstruction()
loadTypeID.AsLoad(functionInstancePtr, wazevoapi.FunctionInstanceTypeIDOffset, ssa.TypeI32)
builder.InsertInstruction(loadTypeID)
actualTypeID := loadTypeID.Return()
// Next, we load the expected TypeID:
loadTypeIDsBegin := builder.AllocateInstruction()
loadTypeIDsBegin.AsLoad(c.moduleCtxPtrValue, c.offset.TypeIDs1stElement.U32(), ssa.TypeI64)
builder.InsertInstruction(loadTypeIDsBegin)
typeIDsBegin := loadTypeIDsBegin.Return()
loadExpectedTypeID := builder.AllocateInstruction()
loadExpectedTypeID.AsLoad(typeIDsBegin, uint32(typeIndex)*4 /* size of wasm.FunctionTypeID */, ssa.TypeI32)
builder.InsertInstruction(loadExpectedTypeID)
expectedTypeID := loadExpectedTypeID.Return()
// Check if the type ID matches.
checkTypeID := builder.AllocateInstruction()
checkTypeID.AsIcmp(actualTypeID, expectedTypeID, ssa.IntegerCmpCondNotEqual)
builder.InsertInstruction(checkTypeID)
exitIfNotMatch := builder.AllocateInstruction()
exitIfNotMatch.AsExitIfTrueWithCode(c.execCtxPtrValue, checkTypeID.Return(), wazevoapi.ExitCodeIndirectCallTypeMismatch)
builder.InsertInstruction(exitIfNotMatch)
// Now ready to call the function. Load the executable and moduleContextOpaquePtr from the function instance.
loadExecutablePtr := builder.AllocateInstruction()
loadExecutablePtr.AsLoad(functionInstancePtr, wazevoapi.FunctionInstanceExecutableOffset, ssa.TypeI64)
builder.InsertInstruction(loadExecutablePtr)
executablePtr := loadExecutablePtr.Return()
loadModuleContextOpaquePtr := builder.AllocateInstruction()
loadModuleContextOpaquePtr.AsLoad(functionInstancePtr, wazevoapi.FunctionInstanceModuleContextOpaquePtrOffset, ssa.TypeI64)
builder.InsertInstruction(loadModuleContextOpaquePtr)
moduleContextOpaquePtr := loadModuleContextOpaquePtr.Return()
// TODO: reuse slice?
typ := &c.m.TypeSection[typeIndex]
argN := len(typ.Params)
args := make([]ssa.Value, argN+2)
args[0] = c.execCtxPtrValue
args[1] = moduleContextOpaquePtr
state.nPopInto(argN, args[2:])
// Before transfer the control to the callee, we have to store the current module's moduleContextPtr
// into execContext.callerModuleContextPtr in case when the callee is a Go function.
c.storeCallerModuleContext()
call := builder.AllocateInstruction()
call.AsCallIndirect(executablePtr, c.signatures[typ], args)
builder.InsertInstruction(call)
first, rest := call.Returns()
if first.Valid() {
state.push(first)
}
for _, v := range rest {
state.push(v)
}
c.reloadAfterCall()
}
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!
@@ -1051,7 +1192,7 @@ func (c *Compiler) getMemoryLenValue(forceReload bool) ssa.Value {
}
func (c *Compiler) insertIcmp(cond ssa.IntegerCmpCond) {
state, builder := &c.loweringState, c.ssaBuilder
state, builder := c.state(), c.ssaBuilder
y, x := state.pop(), state.pop()
cmp := builder.AllocateInstruction()
cmp.AsIcmp(x, y, cond)
@@ -1061,7 +1202,7 @@ func (c *Compiler) insertIcmp(cond ssa.IntegerCmpCond) {
}
func (c *Compiler) insertFcmp(cond ssa.FloatCmpCond) {
state, builder := &c.loweringState, c.ssaBuilder
state, builder := c.state(), c.ssaBuilder
y, x := state.pop(), state.pop()
cmp := builder.AllocateInstruction()
cmp.AsFcmp(x, y, cond)
@@ -1120,7 +1261,7 @@ func (c *Compiler) readF64() float64 {
// readBlockType reads the block type from the current position of the bytecode reader.
func (c *Compiler) readBlockType() *wasm.FunctionType {
state := &c.loweringState
state := c.state()
c.br.Reset(c.wasmFunctionBody[state.pc+1:])
bt, num, err := wasm.DecodeBlockType(c.m.TypeSection, c.br, api.CoreFeaturesV2)
@@ -1133,7 +1274,7 @@ func (c *Compiler) readBlockType() *wasm.FunctionType {
}
func (c *Compiler) readMemArg() (align, offset uint32) {
state := &c.loweringState
state := c.state()
align, num, err := leb128.LoadUint32(c.wasmFunctionBody[state.pc+1:])
if err != nil {
@@ -1159,7 +1300,7 @@ func (c *Compiler) insertJumpToBlock(args []ssa.Value, targetBlk ssa.BasicBlock)
}
func (c *Compiler) insertIntegerExtend(signed bool, from, to byte) {
state := &c.loweringState
state := c.state()
builder := c.ssaBuilder
v := state.pop()
extend := builder.AllocateInstruction()

View File

@@ -8,9 +8,11 @@ import (
"github.com/tetratelabs/wazero/internal/wasm"
)
func TestGlobalInstanceValueOffset(t *testing.T) {
func Test_Offsets(t *testing.T) {
var globalInstance wasm.GlobalInstance
require.Equal(t, int(unsafe.Offsetof(globalInstance.Val)), globalInstanceValueOffset)
var memInstance wasm.MemoryInstance
require.Equal(t, int(unsafe.Offsetof(memInstance.Buffer)), memoryInstanceBufOffset)
var tableInstance wasm.TableInstance
require.Equal(t, int(unsafe.Offsetof(tableInstance.References)), tableInstanceBaseAddressOffset)
}

View File

@@ -13,10 +13,17 @@ type (
// moduleEngine implements wasm.ModuleEngine.
moduleEngine struct {
// opaquePtr equals &opaque[0].
opaquePtr *byte
parent *compiledModule
module *wasm.ModuleInstance
opaque moduleContextOpaque
opaquePtr *byte
parent *compiledModule
module *wasm.ModuleInstance
opaque moduleContextOpaque
localFunctionInstances []*functionInstance
}
functionInstance struct {
executable *byte
moduleContextOpaquePtr *byte
typeID wasm.FunctionTypeID
}
// moduleContextOpaque is the opaque byte slice of Module instance specific contents whose size
@@ -31,11 +38,10 @@ type (
// localMemoryLength uint64 (optional)
// importedMemoryInstance *wasm.MemoryInstance (optional)
// importedMemoryOwnerOpaqueCtx *byte (optional)
// importedFunctions [importedFunctions] struct { the total size depends on # of imported functions.
// executable *byte
// opaqueCtx *moduleContextOpaque
// }
// globals []*wasm.GlobalInstance (optional)
// importedFunctions [# of importedFunctions]functionInstance
// globals []*wasm.GlobalInstance (optional)
// typeIDsBegin &wasm.ModuleInstance.TypeIDs[0] (optional)
// tables []*wasm.TableInstance (optional)
// TODO: add more fields, like tables, etc.
// }
//
@@ -71,11 +77,21 @@ func (m *moduleEngine) setupOpaque() {
if globalOffset := offsets.GlobalsBegin; globalOffset >= 0 {
for _, g := range inst.Globals {
b := uint64(uintptr(unsafe.Pointer(g)))
binary.LittleEndian.PutUint64(opaque[globalOffset:], b)
binary.LittleEndian.PutUint64(opaque[globalOffset:], uint64(uintptr(unsafe.Pointer(g))))
globalOffset += 8
}
}
if tableOffset := offsets.TablesBegin; tableOffset >= 0 {
// First we write the first element's address of typeIDs.
binary.LittleEndian.PutUint64(opaque[offsets.TypeIDs1stElement:], uint64(uintptr(unsafe.Pointer(&inst.TypeIDs[0]))))
// Then we write the table addresses.
for _, table := range inst.Tables {
binary.LittleEndian.PutUint64(opaque[tableOffset:], uint64(uintptr(unsafe.Pointer(table))))
tableOffset += 8
}
}
}
// NewFunction implements wasm.ModuleEngine.
@@ -110,14 +126,38 @@ func (m *moduleEngine) NewFunction(index wasm.Index) api.Function {
// ResolveImportedFunction implements wasm.ModuleEngine.
func (m *moduleEngine) ResolveImportedFunction(index, indexInImportedModule wasm.Index, importedModuleEngine wasm.ModuleEngine) {
ptr, moduleCtx := m.parent.offsets.ImportedFunctionOffset(index)
executableOffset, moduleCtxOffset, typeIDOffset := m.parent.offsets.ImportedFunctionOffset(index)
importedME := importedModuleEngine.(*moduleEngine)
offset := importedME.parent.functionOffsets[indexInImportedModule]
typeID := getTypeIDOf(indexInImportedModule, importedME.module)
// When calling imported function from the machine code, we need to skip the Go preamble.
executable := &importedME.parent.executable[offset.offset+offset.goPreambleSize]
binary.LittleEndian.PutUint64(m.opaque[ptr:], uint64(uintptr(unsafe.Pointer(executable))))
binary.LittleEndian.PutUint64(m.opaque[moduleCtx:], uint64(uintptr(unsafe.Pointer(importedME.opaquePtr))))
// Write functionInstance.
binary.LittleEndian.PutUint64(m.opaque[executableOffset:], uint64(uintptr(unsafe.Pointer(executable))))
binary.LittleEndian.PutUint64(m.opaque[moduleCtxOffset:], uint64(uintptr(unsafe.Pointer(importedME.opaquePtr))))
binary.LittleEndian.PutUint64(m.opaque[typeIDOffset:], uint64(typeID))
}
func getTypeIDOf(funcIndex wasm.Index, m *wasm.ModuleInstance) wasm.FunctionTypeID {
source := m.Source
var typeIndex wasm.Index
if funcIndex >= source.ImportFunctionCount {
funcIndex -= source.ImportFunctionCount
typeIndex = source.FunctionSection[funcIndex]
} else {
var cnt wasm.Index
for i := range source.ImportSection {
if source.ImportSection[i].Type == wasm.ExternTypeFunc {
if cnt == funcIndex {
return m.TypeIDs[source.ImportSection[i].DescFunc]
}
cnt++
}
}
}
return m.TypeIDs[typeIndex]
}
// ResolveImportedMemory implements wasm.ModuleEngine.
@@ -143,10 +183,27 @@ func (m *moduleEngine) DoneInstantiation() {
}
}
// FunctionInstanceReference implements wasm.ModuleEngine.
func (m *moduleEngine) FunctionInstanceReference(funcIndex wasm.Index) wasm.Reference {
if funcIndex < m.module.Source.ImportFunctionCount {
begin, _, _ := m.parent.offsets.ImportedFunctionOffset(funcIndex)
return uintptr(unsafe.Pointer(&m.opaque[begin]))
}
p := m.parent
executable := &p.executable[p.functionOffsets[funcIndex].offset]
typeID := m.module.TypeIDs[m.module.Source.FunctionSection[funcIndex]]
lf := &functionInstance{
executable: executable,
moduleContextOpaquePtr: m.opaquePtr,
typeID: typeID,
}
m.localFunctionInstances = append(m.localFunctionInstances, lf)
return uintptr(unsafe.Pointer(lf))
}
// LookupFunction implements wasm.ModuleEngine.
func (m *moduleEngine) LookupFunction(t *wasm.TableInstance, typeId wasm.FunctionTypeID, tableOffset wasm.Index) (api.Function, error) {
panic("TODO")
}
// FunctionInstanceReference implements wasm.ModuleEngine.
func (m *moduleEngine) FunctionInstanceReference(funcIndex wasm.Index) wasm.Reference { panic("TODO") }

View File

@@ -22,6 +22,7 @@ func TestModuleEngine_setupOpaque(t *testing.T) {
LocalMemoryBegin: 10,
ImportedMemoryBegin: -1,
ImportedFunctionsBegin: -1,
TablesBegin: -1,
GlobalsBegin: -1,
},
m: &wasm.ModuleInstance{MemoryInstance: &wasm.MemoryInstance{
@@ -33,6 +34,7 @@ func TestModuleEngine_setupOpaque(t *testing.T) {
LocalMemoryBegin: -1,
ImportedMemoryBegin: 30,
GlobalsBegin: -1,
TablesBegin: -1,
ImportedFunctionsBegin: -1,
},
m: &wasm.ModuleInstance{MemoryInstance: &wasm.MemoryInstance{
@@ -45,9 +47,12 @@ func TestModuleEngine_setupOpaque(t *testing.T) {
ImportedMemoryBegin: -1,
ImportedFunctionsBegin: -1,
GlobalsBegin: 30,
TablesBegin: 100,
},
m: &wasm.ModuleInstance{
Globals: []*wasm.GlobalInstance{{}, {}, {}, {}, {}, {}},
Tables: []*wasm.TableInstance{{}, {}, {}},
TypeIDs: make([]wasm.FunctionTypeID, 50),
},
},
} {
@@ -91,6 +96,17 @@ func TestModuleEngine_setupOpaque(t *testing.T) {
require.Equal(t, expPtr, actualPtr)
}
}
if tc.offset.TablesBegin >= 0 {
typeIDsPtr := uintptr(binary.LittleEndian.Uint64(m.opaque[int(tc.offset.TypeIDs1stElement):]))
expPtr := uintptr(unsafe.Pointer(&tc.m.TypeIDs[0]))
require.Equal(t, expPtr, typeIDsPtr)
for i, table := range tc.m.Tables {
actualPtr := uintptr(binary.LittleEndian.Uint64(m.opaque[int(tc.offset.TablesBegin)+8*i:]))
expPtr := uintptr(unsafe.Pointer(table))
require.Equal(t, expPtr, actualPtr)
}
}
})
}
}
@@ -108,6 +124,10 @@ func TestModuleEngine_ResolveImportedFunction(t *testing.T) {
executable: make([]byte, 1000),
functionOffsets: []compiledFunctionOffset{{offset: 1, goPreambleSize: 4}, {offset: 5, goPreambleSize: 4}, {offset: 10, goPreambleSize: 4}},
},
module: &wasm.ModuleInstance{
TypeIDs: []wasm.FunctionTypeID{0, 0, 0, 0, 111, 222, 333},
Source: &wasm.Module{FunctionSection: []wasm.Index{4, 5, 6}},
},
}
im2 := &moduleEngine{
opaquePtr: &op2,
@@ -115,6 +135,10 @@ func TestModuleEngine_ResolveImportedFunction(t *testing.T) {
executable: make([]byte, 1000),
functionOffsets: []compiledFunctionOffset{{offset: 50, goPreambleSize: 4}},
},
module: &wasm.ModuleInstance{
TypeIDs: []wasm.FunctionTypeID{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 999},
Source: &wasm.Module{FunctionSection: []wasm.Index{10}},
},
}
m.ResolveImportedFunction(0, 0, im1)
@@ -122,22 +146,61 @@ func TestModuleEngine_ResolveImportedFunction(t *testing.T) {
m.ResolveImportedFunction(2, 2, im1)
m.ResolveImportedFunction(3, 1, im1)
for _, tc := range []struct {
for i, tc := range []struct {
index int
op *byte
executable *byte
expTypeID wasm.FunctionTypeID
}{
{index: 0, op: &op1, executable: &im1.parent.executable[1+4]},
{index: 1, op: &op2, executable: &im2.parent.executable[50+4]},
{index: 2, op: &op1, executable: &im1.parent.executable[10+4]},
{index: 3, op: &op1, executable: &im1.parent.executable[5+4]},
{index: 0, op: &op1, executable: &im1.parent.executable[1+4], expTypeID: 111},
{index: 1, op: &op2, executable: &im2.parent.executable[50+4], expTypeID: 999},
{index: 2, op: &op1, executable: &im1.parent.executable[10+4], expTypeID: 333},
{index: 3, op: &op1, executable: &im1.parent.executable[5+4], expTypeID: 222},
} {
buf := m.opaque[begin+16*tc.index:]
actualExecutable := binary.LittleEndian.Uint64(buf)
actualOpaquePtr := binary.LittleEndian.Uint64(buf[8:])
expExecutable := uint64(uintptr(unsafe.Pointer(tc.executable)))
expOpaquePtr := uint64(uintptr(unsafe.Pointer(tc.op)))
require.Equal(t, expExecutable, actualExecutable)
require.Equal(t, expOpaquePtr, actualOpaquePtr)
t.Run(strconv.Itoa(i), func(t *testing.T) {
buf := m.opaque[begin+wazevoapi.FunctionInstanceSize*tc.index:]
actualExecutable := binary.LittleEndian.Uint64(buf)
actualOpaquePtr := binary.LittleEndian.Uint64(buf[8:])
actualTypeID := binary.LittleEndian.Uint64(buf[16:])
expExecutable := uint64(uintptr(unsafe.Pointer(tc.executable)))
expOpaquePtr := uint64(uintptr(unsafe.Pointer(tc.op)))
require.Equal(t, expExecutable, actualExecutable)
require.Equal(t, expOpaquePtr, actualOpaquePtr)
require.Equal(t, uint64(tc.expTypeID), actualTypeID)
})
}
}
func Test_functionInstance_offsets(t *testing.T) {
var fi functionInstance
require.Equal(t, wazevoapi.FunctionInstanceSize, int(unsafe.Sizeof(fi)))
require.Equal(t, wazevoapi.FunctionInstanceExecutableOffset, int(unsafe.Offsetof(fi.executable)))
require.Equal(t, wazevoapi.FunctionInstanceModuleContextOpaquePtrOffset, int(unsafe.Offsetof(fi.moduleContextOpaquePtr)))
require.Equal(t, wazevoapi.FunctionInstanceTypeIDOffset, int(unsafe.Offsetof(fi.typeID)))
m := wazevoapi.ModuleContextOffsetData{ImportedFunctionsBegin: 100}
ptr, moduleCtx, typeID := m.ImportedFunctionOffset(10)
require.Equal(t, 100+10*wazevoapi.FunctionInstanceSize, int(ptr))
require.Equal(t, moduleCtx, ptr+8)
require.Equal(t, typeID, ptr+16)
}
func Test_getTypeIDOf(t *testing.T) {
m := &wasm.ModuleInstance{
TypeIDs: []wasm.FunctionTypeID{111, 222, 333, 444},
Source: &wasm.Module{
ImportFunctionCount: 1,
ImportSection: []wasm.Import{
{Type: wasm.ExternTypeMemory},
{Type: wasm.ExternTypeTable},
{Type: wasm.ExternTypeFunc, DescFunc: 3},
},
FunctionSection: []wasm.Index{2, 1, 0},
},
}
require.Equal(t, wasm.FunctionTypeID(444), getTypeIDOf(0, m))
require.Equal(t, wasm.FunctionTypeID(333), getTypeIDOf(1, m))
require.Equal(t, wasm.FunctionTypeID(222), getTypeIDOf(2, m))
require.Equal(t, wasm.FunctionTypeID(111), getTypeIDOf(3, m))
}

View File

@@ -136,8 +136,8 @@ const (
// OpcodeExitWithCode exit the execution immediately.
OpcodeExitWithCode
// OpcodeExitIfNotZeroWithCode exits the execution immediately if the value `c` is not zero.
OpcodeExitIfNotZeroWithCode
// OpcodeExitIfTrueWithCode exits the execution immediately if the value `c` is not zero.
OpcodeExitIfTrueWithCode
// OpcodeReturn returns from the function: `return rvalues`.
OpcodeReturn
@@ -830,42 +830,42 @@ const (
// instructionSideEffects provides the info to determine if an instruction has side effects.
// Instructions with side effects must not be eliminated regardless whether the result is used or not.
var instructionSideEffects = [opcodeEnd]sideEffect{
OpcodeUndefined: sideEffectTrue,
OpcodeJump: sideEffectTrue,
OpcodeIconst: sideEffectFalse,
OpcodeCall: sideEffectTrue,
OpcodeCallIndirect: sideEffectTrue,
OpcodeIadd: sideEffectFalse,
OpcodeImul: sideEffectFalse,
OpcodeIsub: sideEffectFalse,
OpcodeIcmp: sideEffectFalse,
OpcodeFcmp: sideEffectFalse,
OpcodeFadd: sideEffectFalse,
OpcodeLoad: sideEffectFalse,
OpcodeUload8: sideEffectFalse,
OpcodeUload16: sideEffectFalse,
OpcodeUload32: sideEffectFalse,
OpcodeSload8: sideEffectFalse,
OpcodeSload16: sideEffectFalse,
OpcodeSload32: sideEffectFalse,
OpcodeSExtend: sideEffectFalse,
OpcodeUExtend: sideEffectFalse,
OpcodeFsub: sideEffectFalse,
OpcodeF32const: sideEffectFalse,
OpcodeF64const: sideEffectFalse,
OpcodeIshl: sideEffectFalse,
OpcodeSshr: sideEffectFalse,
OpcodeUshr: sideEffectFalse,
OpcodeStore: sideEffectTrue,
OpcodeExitWithCode: sideEffectTrue,
OpcodeExitIfNotZeroWithCode: sideEffectTrue,
OpcodeReturn: sideEffectTrue,
OpcodeBrz: sideEffectTrue,
OpcodeBrnz: sideEffectTrue,
OpcodeFdiv: sideEffectFalse,
OpcodeFmul: sideEffectFalse,
OpcodeFmax: sideEffectFalse,
OpcodeFmin: sideEffectFalse,
OpcodeUndefined: sideEffectTrue,
OpcodeJump: sideEffectTrue,
OpcodeIconst: sideEffectFalse,
OpcodeCall: sideEffectTrue,
OpcodeCallIndirect: sideEffectTrue,
OpcodeIadd: sideEffectFalse,
OpcodeImul: sideEffectFalse,
OpcodeIsub: sideEffectFalse,
OpcodeIcmp: sideEffectFalse,
OpcodeFcmp: sideEffectFalse,
OpcodeFadd: sideEffectFalse,
OpcodeLoad: sideEffectFalse,
OpcodeUload8: sideEffectFalse,
OpcodeUload16: sideEffectFalse,
OpcodeUload32: sideEffectFalse,
OpcodeSload8: sideEffectFalse,
OpcodeSload16: sideEffectFalse,
OpcodeSload32: sideEffectFalse,
OpcodeSExtend: sideEffectFalse,
OpcodeUExtend: sideEffectFalse,
OpcodeFsub: sideEffectFalse,
OpcodeF32const: sideEffectFalse,
OpcodeF64const: sideEffectFalse,
OpcodeIshl: sideEffectFalse,
OpcodeSshr: sideEffectFalse,
OpcodeUshr: sideEffectFalse,
OpcodeStore: sideEffectTrue,
OpcodeExitWithCode: sideEffectTrue,
OpcodeExitIfTrueWithCode: sideEffectTrue,
OpcodeReturn: sideEffectTrue,
OpcodeBrz: sideEffectTrue,
OpcodeBrnz: sideEffectTrue,
OpcodeFdiv: sideEffectFalse,
OpcodeFmul: sideEffectFalse,
OpcodeFmax: sideEffectFalse,
OpcodeFmin: sideEffectFalse,
}
// HasSideEffects returns true if this instruction has side effects.
@@ -919,32 +919,32 @@ var instructionReturnTypes = [opcodeEnd]returnTypesFn{
}
return
},
OpcodeLoad: returnTypesFnSingle,
OpcodeIadd: returnTypesFnSingle,
OpcodeIsub: returnTypesFnSingle,
OpcodeImul: returnTypesFnSingle,
OpcodeIcmp: returnTypesFnI32,
OpcodeFcmp: returnTypesFnI32,
OpcodeFadd: returnTypesFnSingle,
OpcodeFsub: returnTypesFnSingle,
OpcodeFdiv: returnTypesFnSingle,
OpcodeFmul: returnTypesFnSingle,
OpcodeFmax: returnTypesFnSingle,
OpcodeFmin: returnTypesFnSingle,
OpcodeF32const: returnTypesFnF32,
OpcodeF64const: returnTypesFnF64,
OpcodeStore: returnTypesFnNoReturns,
OpcodeExitWithCode: returnTypesFnNoReturns,
OpcodeExitIfNotZeroWithCode: returnTypesFnNoReturns,
OpcodeReturn: returnTypesFnNoReturns,
OpcodeBrz: returnTypesFnNoReturns,
OpcodeBrnz: returnTypesFnNoReturns,
OpcodeUload8: returnTypesFnSingle,
OpcodeUload16: returnTypesFnSingle,
OpcodeUload32: returnTypesFnSingle,
OpcodeSload8: returnTypesFnSingle,
OpcodeSload16: returnTypesFnSingle,
OpcodeSload32: returnTypesFnSingle,
OpcodeLoad: returnTypesFnSingle,
OpcodeIadd: returnTypesFnSingle,
OpcodeIsub: returnTypesFnSingle,
OpcodeImul: returnTypesFnSingle,
OpcodeIcmp: returnTypesFnI32,
OpcodeFcmp: returnTypesFnI32,
OpcodeFadd: returnTypesFnSingle,
OpcodeFsub: returnTypesFnSingle,
OpcodeFdiv: returnTypesFnSingle,
OpcodeFmul: returnTypesFnSingle,
OpcodeFmax: returnTypesFnSingle,
OpcodeFmin: returnTypesFnSingle,
OpcodeF32const: returnTypesFnF32,
OpcodeF64const: returnTypesFnF64,
OpcodeStore: returnTypesFnNoReturns,
OpcodeExitWithCode: returnTypesFnNoReturns,
OpcodeExitIfTrueWithCode: returnTypesFnNoReturns,
OpcodeReturn: returnTypesFnNoReturns,
OpcodeBrz: returnTypesFnNoReturns,
OpcodeBrnz: returnTypesFnNoReturns,
OpcodeUload8: returnTypesFnSingle,
OpcodeUload16: returnTypesFnSingle,
OpcodeUload32: returnTypesFnSingle,
OpcodeSload8: returnTypesFnSingle,
OpcodeSload16: returnTypesFnSingle,
OpcodeSload32: returnTypesFnSingle,
}
// AsLoad initializes this instruction as a store instruction with OpcodeLoad.
@@ -1160,9 +1160,9 @@ func (i *Instruction) AsExitWithCode(ctx Value, code wazevoapi.ExitCode) {
i.u64 = uint64(code)
}
// AsExitIfNotZeroWithCode initializes this instruction as a trap instruction with OpcodeExitIfNotZeroWithCode.
func (i *Instruction) AsExitIfNotZeroWithCode(ctx, c Value, code wazevoapi.ExitCode) {
i.opcode = OpcodeExitIfNotZeroWithCode
// AsExitIfTrueWithCode initializes this instruction as a trap instruction with OpcodeExitIfTrueWithCode.
func (i *Instruction) AsExitIfTrueWithCode(ctx, c Value, code wazevoapi.ExitCode) {
i.opcode = OpcodeExitIfTrueWithCode
i.v = ctx
i.v2 = c
i.u64 = uint64(code)
@@ -1173,8 +1173,8 @@ func (i *Instruction) ExitWithCodeData() (ctx Value, code wazevoapi.ExitCode) {
return i.v, wazevoapi.ExitCode(i.u64)
}
// ExitIfNotZeroWithCodeData returns the context and exit code of OpcodeExitWithCode.
func (i *Instruction) ExitIfNotZeroWithCodeData() (ctx, c Value, code wazevoapi.ExitCode) {
// ExitIfTrueWithCodeData returns the context and exit code of OpcodeExitWithCode.
func (i *Instruction) ExitIfTrueWithCodeData() (ctx, c Value, code wazevoapi.ExitCode) {
return i.v, i.v2, wazevoapi.ExitCode(i.u64)
}
@@ -1333,7 +1333,7 @@ func (i *Instruction) Format(b Builder) string {
switch i.opcode {
case OpcodeExitWithCode:
instSuffix = fmt.Sprintf(" %s, %s", i.v.Format(b), wazevoapi.ExitCode(i.u64))
case OpcodeExitIfNotZeroWithCode:
case OpcodeExitIfTrueWithCode:
instSuffix = fmt.Sprintf(" %s, %s, %s", i.v2.Format(b), i.v.Format(b), wazevoapi.ExitCode(i.u64))
case OpcodeIadd, OpcodeIsub, OpcodeImul, OpcodeFadd, OpcodeFsub, OpcodeFmin, OpcodeFmax, OpcodeFdiv, OpcodeFmul:
instSuffix = fmt.Sprintf(" %s, %s", i.v.Format(b), i.v2.Format(b))
@@ -1471,8 +1471,8 @@ func (o Opcode) String() (ret string) {
return "BrTable"
case OpcodeExitWithCode:
return "Exit"
case OpcodeExitIfNotZeroWithCode:
return "ExitIfNotZero"
case OpcodeExitIfTrueWithCode:
return "ExitIfTrue"
case OpcodeReturn:
return "Return"
case OpcodeCall:

View File

@@ -1101,6 +1101,32 @@ var (
DataSection: []wasm.DataSegment{{OffsetExpression: constExprI32(0), Init: maskedBuf(int(wasm.MemoryPageSize))}},
},
}
CallIndirect = TestCase{
Module: &wasm.Module{
TypeSection: []wasm.FunctionType{i32_i32, {}, v_i32, v_i32i32},
ExportSection: []wasm.Export{{Name: ExportName, Type: wasm.ExternTypeFunc, Index: 0}},
FunctionSection: []wasm.Index{0, 1, 2, 3},
TableSection: []wasm.Table{{Type: wasm.RefTypeFuncref, Min: 1000}},
ElementSection: []wasm.ElementSegment{
{
OffsetExpr: constExprI32(0), TableIndex: 0, Type: wasm.RefTypeFuncref, Mode: wasm.ElementModeActive,
// Set the function 1, 2, 3 at the beginning of the table.
Init: []wasm.Index{1, 2, 3},
},
},
CodeSection: []wasm.Code{
{Body: []byte{
wasm.OpcodeLocalGet, 0,
wasm.OpcodeCallIndirect, 2, 0, // Expecting type 2 (v_i32), in tables[0]
wasm.OpcodeEnd,
}},
{Body: []byte{wasm.OpcodeEnd}},
{Body: []byte{wasm.OpcodeI32Const, 10, wasm.OpcodeEnd}},
{Body: []byte{wasm.OpcodeI32Const, 1, wasm.OpcodeI32Const, 1, wasm.OpcodeEnd}},
},
},
}
)
type TestCase struct {

View File

@@ -13,7 +13,9 @@ const (
ExitCodeCallGoModuleFunction
// ExitCodeCallGoFunction is an exit code for a call to an api.GoFunction.
ExitCodeCallGoFunction
ExitCodeTableOutOfBounds
ExitCodeIndirectCallNullPointer
ExitCodeIndirectCallTypeMismatch
exitCodeMax
)
@@ -34,6 +36,12 @@ func (e ExitCode) String() string {
return "unreachable"
case ExitCodeMemoryOutOfBounds:
return "memory_out_of_bounds"
case ExitCodeTableOutOfBounds:
return "table_out_of_bounds"
case ExitCodeIndirectCallNullPointer:
return "indirect_call_null_pointer"
case ExitCodeIndirectCallTypeMismatch:
return "indirect_call_type_mismatch"
}
panic("TODO")
}

View File

@@ -2,6 +2,17 @@ package wazevoapi
import "github.com/tetratelabs/wazero/internal/wasm"
const (
// FunctionInstanceSize is the size of wazevo.functionInstance.
FunctionInstanceSize = 24
// FunctionInstanceExecutableOffset is an offset of `executable` field in wazevo.functionInstance
FunctionInstanceExecutableOffset = 0
// FunctionInstanceModuleContextOpaquePtrOffset is an offset of `moduleContextOpaquePtr` field in wazevo.functionInstance
FunctionInstanceModuleContextOpaquePtrOffset = 8
// FunctionInstanceTypeIDOffset is an offset of `typeID` field in wazevo.functionInstance
FunctionInstanceTypeIDOffset = 16
)
var ExecutionContextOffsets = ExecutionContextOffsetData{
ExitCodeOffset: 0,
CallerModuleContextPtr: 8,
@@ -57,13 +68,18 @@ type ModuleContextOffsetData struct {
LocalMemoryBegin,
ImportedMemoryBegin,
ImportedFunctionsBegin,
GlobalsBegin Offset
GlobalsBegin,
TypeIDs1stElement,
TablesBegin Offset
}
// ImportedFunctionOffset returns an offset of the i-th imported function.
func (m *ModuleContextOffsetData) ImportedFunctionOffset(i wasm.Index) (ptr, moduleCtx Offset) {
base := m.ImportedFunctionsBegin + Offset(i)*16
return base, base + 8
// Each item is stored as wazevo.functionInstance whose size matches FunctionInstanceSize.
func (m *ModuleContextOffsetData) ImportedFunctionOffset(i wasm.Index) (
executableOffset, moduleCtxOffset, typeIDOffset Offset,
) {
base := m.ImportedFunctionsBegin + Offset(i)*FunctionInstanceSize
return base, base + 8, base + 16
}
// GlobalInstanceOffset returns an offset of the i-th global instance.
@@ -102,6 +118,11 @@ func (m *ModuleContextOffsetData) LocalMemoryLen() Offset {
return -1
}
// TableOffset returns an offset of the i-th table instance.
func (m *ModuleContextOffsetData) TableOffset(tableIndex int) Offset {
return m.TablesBegin + Offset(tableIndex)*8
}
// NewModuleContextOffsetData creates a ModuleContextOffsetData determining the structure of moduleContextOpaque for the given Module.
// The structure is described in the comment of wazevo.moduleContextOpaque.
func NewModuleContextOffsetData(m *wasm.Module) ModuleContextOffsetData {
@@ -133,8 +154,8 @@ func NewModuleContextOffsetData(m *wasm.Module) ModuleContextOffsetData {
if m.ImportFunctionCount > 0 {
ret.ImportedFunctionsBegin = offset
// Each function consists of the pointer to the executable and the pointer to its moduleContextOpaque (16 bytes).
size := int(m.ImportFunctionCount) * 16
// Each function is stored wazevo.functionInstance.
size := int(m.ImportFunctionCount) * FunctionInstanceSize
offset += Offset(size)
} else {
ret.ImportedFunctionsBegin = -1
@@ -148,6 +169,18 @@ func NewModuleContextOffsetData(m *wasm.Module) ModuleContextOffsetData {
ret.GlobalsBegin = -1
}
if tables := len(m.TableSection) + int(m.ImportTableCount); tables > 0 {
ret.TypeIDs1stElement = offset
offset += 8 // First element of TypeIDs.
ret.TablesBegin = offset
// Pointers to *wasm.TableInstance.
offset += Offset(tables) * 8
} else {
ret.TypeIDs1stElement = -1
ret.TablesBegin = -1
}
ret.TotalSize = int(offset)
return ret
}

View File

@@ -21,6 +21,8 @@ func TestNewModuleContextOffsetData(t *testing.T) {
ImportedMemoryBegin: -1,
ImportedFunctionsBegin: -1,
GlobalsBegin: -1,
TypeIDs1stElement: -1,
TablesBegin: -1,
TotalSize: 8,
},
},
@@ -32,6 +34,8 @@ func TestNewModuleContextOffsetData(t *testing.T) {
ImportedMemoryBegin: -1,
ImportedFunctionsBegin: -1,
GlobalsBegin: -1,
TypeIDs1stElement: -1,
TablesBegin: -1,
TotalSize: 24,
},
},
@@ -43,6 +47,8 @@ func TestNewModuleContextOffsetData(t *testing.T) {
ImportedMemoryBegin: 8,
ImportedFunctionsBegin: -1,
GlobalsBegin: -1,
TypeIDs1stElement: -1,
TablesBegin: -1,
TotalSize: 24,
},
},
@@ -54,7 +60,9 @@ func TestNewModuleContextOffsetData(t *testing.T) {
ImportedMemoryBegin: -1,
ImportedFunctionsBegin: 8,
GlobalsBegin: -1,
TotalSize: 168,
TypeIDs1stElement: -1,
TablesBegin: -1,
TotalSize: 10*FunctionInstanceSize + 8,
},
},
{
@@ -65,14 +73,18 @@ func TestNewModuleContextOffsetData(t *testing.T) {
ImportedMemoryBegin: 8,
ImportedFunctionsBegin: 24,
GlobalsBegin: -1,
TotalSize: 184,
TypeIDs1stElement: -1,
TablesBegin: -1,
TotalSize: 10*FunctionInstanceSize + 24,
},
},
{
name: "local mem / imported func / globals",
name: "local mem / imported func / globals / tables",
m: &wasm.Module{
ImportGlobalCount: 10,
ImportFunctionCount: 10,
ImportTableCount: 5,
TableSection: make([]wasm.Table, 10),
MemorySection: &wasm.Memory{},
GlobalSection: make([]wasm.Global, 20),
},
@@ -80,8 +92,10 @@ func TestNewModuleContextOffsetData(t *testing.T) {
LocalMemoryBegin: 8,
ImportedMemoryBegin: -1,
ImportedFunctionsBegin: 24,
GlobalsBegin: 24 + 16*10,
TotalSize: 24 + 16*10 + 8*30,
GlobalsBegin: 24 + 10*FunctionInstanceSize,
TypeIDs1stElement: 24 + 10*FunctionInstanceSize + 8*30,
TablesBegin: 24 + 10*FunctionInstanceSize + 8*30 + 8,
TotalSize: 24 + 10*FunctionInstanceSize + 8*30 + 8 + 8*15,
},
},
} {