wazevo: adds support for call_indirect (#1632)
Signed-off-by: Takeshi Yoneda <t.y.mathetake@gmail.com>
This commit is contained in:
@@ -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]
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
}},
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)}
|
||||
}
|
||||
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
`,
|
||||
},
|
||||
} {
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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") }
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
},
|
||||
} {
|
||||
|
||||
Reference in New Issue
Block a user