blob: 05450bde5ec4276a7e31694f3a09636b12e70762 [file] [log] [blame]
local ffi = require('ffi')
local S = require('syscall')
-- Normalize whitespace and remove empty lines
local function normalize_code(c)
local res = {}
for line in string.gmatch(c,'[^\r\n]+') do
local op, d, s, t = line:match('(%S+)%s+(%S+)%s+(%S+)%s*([^-]*)')
if op then
t = t and t:match('^%s*(.-)%s*$')
table.insert(res, string.format('%s\t%s %s %s', op, d, s, t))
end
end
return table.concat(res, '\n')
end
-- Compile code and check result
local function compile(t)
local bpf = require('bpf')
-- require('jit.bc').dump(t.input)
local code, err = bpf(t.input)
assert.truthy(code)
assert.falsy(err)
if code then
if t.expect then
local got = normalize_code(bpf.dump_string(code, 1, true))
-- if normalize_code(t.expect) ~= got then print(bpf.dump_string(code, 1)) end
assert.same(normalize_code(t.expect), got)
end
end
end
-- Make a mock map variable
local function makemap(type, max_entries, key_ctype, val_ctype)
if not key_ctype then key_ctype = ffi.typeof('uint32_t') end
if not val_ctype then val_ctype = ffi.typeof('uint32_t') end
if not max_entries then max_entries = 4096 end
return {
__map = true,
max_entries = max_entries,
key = ffi.new(ffi.typeof('$ [1]', key_ctype)),
val = ffi.new(ffi.typeof('$ [1]', val_ctype)),
map_type = S.c.BPF_MAP[type],
key_type = key_ctype,
val_type = val_ctype,
fd = 42,
}
end
describe('codegen', function()
-- luacheck: ignore 113 211 212 311 511
describe('constants', function()
it('remove dead constant store', function()
compile {
input = function ()
local proto = 5
end,
expect = [[
MOV R0 #0
EXIT R0 #0
]]
}
end)
it('materialize constant', function()
compile {
input = function ()
return 5
end,
expect = [[
MOV R0 #5
EXIT R0 #0
]]
}
end)
it('materialize constant longer than i32', function()
compile {
input = function ()
return 4294967295
end,
expect = [[
LDDW R0 #4294967295
EXIT R0 #0
]]
}
end)
it('materialize cdata constant', function()
compile {
input = function ()
return 5ULL
end,
expect = [[
LDDW R0 #5 -- composed instruction
EXIT R0 #0
]]
}
end)
it('materialize signed cdata constant', function()
compile {
input = function ()
return 5LL
end,
expect = [[
LDDW R0 #5 -- composed instruction
EXIT R0 #0
]]
}
end)
it('materialize coercible numeric cdata constant', function()
compile {
input = function ()
return 0x00005
end,
expect = [[
MOV R0 #5
EXIT R0 #0
]]
}
end)
it('materialize constant through variable', function()
compile {
input = function ()
local proto = 5
return proto
end,
expect = [[
MOV R0 #5
EXIT R0 #0
]]
}
end)
it('eliminate constant expressions', function()
compile {
input = function ()
return 2 + 3 - 0
end,
expect = [[
MOV R0 #5
EXIT R0 #0
]]
}
end)
it('eliminate constant expressions (if block)', function()
compile {
input = function ()
local proto = 5
if proto == 5 then
proto = 1
end
return proto
end,
expect = [[
MOV R0 #1
EXIT R0 #0
]]
}
end)
it('eliminate negative constant expressions (if block) NYI', function()
-- always negative condition is not fully eliminated
compile {
input = function ()
local proto = 5
if false then
proto = 1
end
return proto
end,
expect = [[
MOV R7 #5
STXDW [R10-8] R7
MOV R7 #0
JEQ R7 #0 => 0005
LDXDW R0 [R10-8]
EXIT R0 #0
]]
}
end)
end)
describe('variables', function()
it('classic packet access (fold constant offset)', function()
compile {
input = function (skb)
return eth.ip.tos -- constant expression will fold
end,
expect = [[
LDB R0 skb[15]
EXIT R0 #0
]]
}
end)
it('classic packet access (load non-constant offset)', function()
compile {
input = function (skb)
return eth.ip.udp.src_port -- need to skip variable-length header
end,
expect = [[
LDB R0 skb[14]
AND R0 #15
LSH R0 #2
ADD R0 #14
STXDW [R10-16] R0 -- NYI: erase dead store
LDH R0 skb[R0+0]
END R0 R0
EXIT R0 #0
]]
}
end)
it('classic packet access (manipulate dissector offset)', function()
compile {
input = function (skb)
local ptr = eth.ip.udp.data + 1
return ptr[0] -- dereference dissector pointer
end,
expect = [[
LDB R0 skb[14]
AND R0 #15
LSH R0 #2
ADD R0 #14 -- NYI: fuse commutative operations in second pass
ADD R0 #8
ADD R0 #1
STXDW [R10-16] R0
LDB R0 skb[R0+0]
EXIT R0 #0
]]
}
end)
it('classic packet access (multi-byte load)', function()
compile {
input = function (skb)
local ptr = eth.ip.udp.data
return ptr(1, 5) -- load 4 bytes
end,
expect = [[
LDB R0 skb[14]
AND R0 #15
LSH R0 #2
ADD R0 #14
ADD R0 #8
MOV R7 R0
STXDW [R10-16] R0 -- NYI: erase dead store
LDW R0 skb[R7+1]
END R0 R0
EXIT R0 #0
]]
}
end)
it('direct skb field access', function()
compile {
input = function (skb)
return skb.len
end,
expect = [[
LDXW R7 [R6+0]
MOV R0 R7
EXIT R0 #0
]]
}
end)
it('direct skb data access (manipulate offset)', function()
compile {
input = function (skb)
local ptr = skb.data + 5
return ptr[0]
end,
expect = [[
LDXW R7 [R6+76]
ADD R7 #5
LDXB R8 [R7+0] -- NYI: transform LD + ADD to LD + offset addressing
MOV R0 R8
EXIT R0 #0
]]
}
end)
it('direct skb data access (offset boundary check)', function()
compile {
input = function (skb)
local ptr = skb.data + 5
if ptr < skb.data_end then
return ptr[0]
end
end,
expect = [[
LDXW R7 [R6+76]
ADD R7 #5
LDXW R8 [R6+80]
JGE R7 R8 => 0008
LDXB R8 [R7+0]
MOV R0 R8
EXIT R0 #0
MOV R0 #0
EXIT R0 #0
]]
}
end)
it('access stack memory (array, const load, const store)', function()
compile {
input = function (skb)
local mem = ffi.new('uint8_t [16]')
mem[0] = 5
end,
expect = [[
MOV R0 #0
STXDW [R10-40] R0
STXDW [R10-48] R0 -- NYI: erase zero-fill on allocation when it's loaded later
STB [R10-48] #5
MOV R0 #0
EXIT R0 #0
]]
}
end)
it('access stack memory (array, const load, packet store)', function()
compile {
input = function (skb)
local mem = ffi.new('uint8_t [7]')
mem[0] = eth.ip.tos
end,
expect = [[
MOV R0 #0
STXDW [R10-40] R0 -- NYI: erase zero-fill on allocation when it's loaded later
LDB R0 skb[15]
STXB [R10-40] R0
MOV R0 #0
EXIT R0 #0
]]
}
end)
it('access stack memory (array, packet load, const store)', function()
compile {
input = function (skb)
local mem = ffi.new('uint8_t [1]')
mem[eth.ip.tos] = 5
end,
expect = [[
MOV R0 #0
STXDW [R10-48] R0 -- NYI: erase zero-fill on allocation when it's loaded later
LDB R0 skb[15]
MOV R7 R0
ADD R7 R10
STB [R7-48] #5
MOV R0 #0
EXIT R0 #0
]]
}
end)
it('access stack memory (array, packet load, packet store)', function()
compile {
input = function (skb)
local mem = ffi.new('uint8_t [7]')
local v = eth.ip.tos
mem[v] = v
end,
expect = [[
MOV R0 #0
STXDW [R10-40] R0 -- NYI: erase zero-fill on allocation when it's loaded later
LDB R0 skb[15]
MOV R7 R0
ADD R7 R10
STXB [R7-40] R0
MOV R0 #0
EXIT R0 #0
]]
}
end)
it('access stack memory (struct, const/packet store)', function()
local kv_t = 'struct { uint64_t a; uint64_t b; }'
compile {
input = function (skb)
local mem = ffi.new(kv_t)
mem.a = 5
mem.b = eth.ip.tos
end,
expect = [[
MOV R0 #0
STXDW [R10-40] R0
STXDW [R10-48] R0 -- NYI: erase zero-fill on allocation when it's loaded later
MOV R7 #5
STXDW [R10-48] R7
LDB R0 skb[15]
STXDW [R10-40] R0
MOV R0 #0
EXIT R0 #0
]]
}
end)
it('access stack memory (struct, const/stack store)', function()
local kv_t = 'struct { uint64_t a; uint64_t b; }'
compile {
input = function (skb)
local m1 = ffi.new(kv_t)
local m2 = ffi.new(kv_t)
m1.a = 5
m2.b = m1.a
end,
expect = [[
MOV R0 #0
STXDW [R10-48] R0
STXDW [R10-56] R0 -- NYI: erase zero-fill on allocation when it's loaded later
MOV R0 #0
STXDW [R10-64] R0
STXDW [R10-72] R0 -- NYI: erase zero-fill on allocation when it's loaded later
MOV R7 #5
STXDW [R10-56] R7
LDXDW R7 [R10-56]
STXDW [R10-64] R7
MOV R0 #0
EXIT R0 #0
]]
}
end)
it('array map (u32, const key load)', function()
local array_map = makemap('array', 256)
compile {
input = function (skb)
return array_map[0]
end,
expect = [[
LDDW R1 #42
STW [R10-28] #0
MOV R2 R10
ADD R2 #4294967268
CALL R0 #1 ; map_lookup_elem
JEQ R0 #0 => 0009
LDXW R0 [R0+0]
EXIT R0 #0
]]
}
end)
it('array map (u32, packet key load)', function()
local array_map = makemap('array', 256)
compile {
input = function (skb)
return array_map[eth.ip.tos]
end,
expect = [[
LDB R0 skb[15]
LDDW R1 #42
STXW [R10-36] R0
MOV R2 R10
ADD R2 #4294967260
STXDW [R10-24] R0 -- NYI: erase dead store
CALL R0 #1 ; map_lookup_elem
JEQ R0 #0 => 0011
LDXW R0 [R0+0]
EXIT R0 #0
]]
}
end)
it('array map (u32, const key store, const value)', function()
local array_map = makemap('array', 256)
compile {
input = function (skb)
array_map[0] = 5
end,
expect = [[
LDDW R1 #42
STW [R10-36] #0
MOV R2 R10
ADD R2 #4294967260
MOV R4 #0
STW [R10-40] #5
MOV R3 R10
ADD R3 #4294967256
CALL R0 #2 ; map_update_elem
MOV R0 #0
EXIT R0 #0
]]
}
end)
it('array map (u32, const key store, packet value)', function()
local array_map = makemap('array', 256)
compile {
input = function (skb)
array_map[0] = eth.ip.tos
end,
expect = [[
LDB R0 skb[15]
STXDW [R10-24] R0
LDDW R1 #42
STW [R10-36] #0
MOV R2 R10
ADD R2 #4294967260
MOV R4 #0
MOV R3 R10
ADD R3 #4294967272
CALL R0 #2 ; map_update_elem
MOV R0 #0
EXIT R0 #0
]]
}
end)
it('array map (u32, const key store, map value)', function()
local array_map = makemap('array', 256)
compile {
input = function (skb)
array_map[0] = array_map[1]
end,
expect = [[
LDDW R1 #42
STW [R10-36] #1
MOV R2 R10
ADD R2 #4294967260
CALL R0 #1 ; map_lookup_elem
STXDW [R10-24] R0
LDDW R1 #42
STW [R10-36] #0
MOV R2 R10
ADD R2 #4294967260
MOV R4 #0
LDXDW R3 [R10-24]
JEQ R3 #0 => 0017
LDXW R3 [R3+0]
STXW [R10-40] R3
MOV R3 R10
ADD R3 #4294967256
CALL R0 #2 ; map_update_elem
MOV R0 #0
EXIT R0 #0
]]
}
end)
it('array map (u32, const key replace, const value)', function()
local array_map = makemap('array', 256)
compile {
input = function (skb)
local val = array_map[0]
if val then
val[0] = val[0] + 1
else
array_map[0] = 5
end
end,
expect = [[
LDDW R1 #42
STW [R10-44] #0
MOV R2 R10
ADD R2 #4294967252
CALL R0 #1 ; map_lookup_elem
JEQ R0 #0 => 0013 -- if (map_value ~= NULL)
LDXW R7 [R0+0]
ADD R7 #1
STXW [R0+0] R7
MOV R7 #0
JEQ R7 #0 => 0025 -- skip false branch
STXDW [R10-16] R0
LDDW R1 #42
STW [R10-44] #0
MOV R2 R10
ADD R2 #4294967252
MOV R4 #0
STW [R10-48] #5
MOV R3 R10
ADD R3 #4294967248
CALL R0 #2 ; map_update_elem
LDXDW R0 [R10-16]
MOV R0 #0
EXIT R0 #0
]]
}
end)
it('array map (u32, const key replace xadd, const value)', function()
local array_map = makemap('array', 256)
compile {
input = function (skb)
local val = array_map[0]
if val then
xadd(val, 1)
else
array_map[0] = 5
end
end,
expect = [[
LDDW R1 #42
STW [R10-52] #0
MOV R2 R10
ADD R2 #4294967244
CALL R0 #1 ; map_lookup_elem
JEQ R0 #0 => 0014 -- if (map_value ~= NULL)
MOV R7 #1
MOV R8 R0
STXDW [R10-16] R0
XADDW [R8+0] R7
MOV R7 #0
JEQ R7 #0 => 0025 -- skip false branch
STXDW [R10-16] R0
LDDW R1 #42
STW [R10-52] #0
MOV R2 R10
ADD R2 #4294967244
MOV R4 #0
STW [R10-56] #5
MOV R3 R10
ADD R3 #4294967240
CALL R0 #2 ; map_update_elem
MOV R0 #0
EXIT R0 #0
]]
}
end)
it('array map (u32, const key replace xadd, const value) inverse nil check', function()
local array_map = makemap('array', 256)
compile {
input = function (skb)
local val = array_map[0]
if not val then
array_map[0] = 5
else
xadd(val, 1)
end
end,
expect = [[
LDDW R1 #42
STW [R10-52] #0
MOV R2 R10
ADD R2 #4294967244
CALL R0 #1 ; map_lookup_elem
JNE R0 #0 => 0021
STXDW [R10-16] R0
LDDW R1 #42
STW [R10-52] #0
MOV R2 R10
ADD R2 #4294967244
MOV R4 #0
STW [R10-56] #5
MOV R3 R10
ADD R3 #4294967240
CALL R0 #2 ; map_update_elem
MOV R7 #0
JEQ R7 #0 => 0025
MOV R7 #1
MOV R8 R0
STXDW [R10-16] R0
XADDW [R8+0] R7
MOV R0 #0
EXIT R0 #0
]]
}
end)
it('array map (struct, stack key load)', function()
local kv_t = 'struct { uint64_t a; uint64_t b; }'
local array_map = makemap('array', 256, ffi.typeof(kv_t), ffi.typeof(kv_t))
compile {
input = function (skb)
local key = ffi.new(kv_t)
key.a = 2
key.b = 3
local val = array_map[key] -- Use composite key from stack memory
if val then
return val.a
end
end,
expect = [[
MOV R0 #0
STXDW [R10-48] R0
STXDW [R10-56] R0 -- NYI: erase zero-fill on allocation when it's loaded later
MOV R7 #2
STXDW [R10-56] R7
MOV R7 #3
STXDW [R10-48] R7
LDDW R1 #42
MOV R2 R10
ADD R2 #4294967240
CALL R0 #1 ; map_lookup_elem
JEQ R0 #0 => 0017
LDXDW R7 [R0+0]
MOV R0 R7
EXIT R0 #0
MOV R0 #0
EXIT R0 #0
]]
}
end)
it('array map (struct, stack key store)', function()
local kv_t = 'struct { uint64_t a; uint64_t b; }'
local array_map = makemap('array', 256, ffi.typeof(kv_t), ffi.typeof(kv_t))
compile {
input = function (skb)
local key = ffi.new(kv_t)
key.a = 2
key.b = 3
array_map[key] = key -- Use composite key from stack memory
end,
expect = [[
MOV R0 #0
STXDW [R10-40] R0
STXDW [R10-48] R0 -- NYI: erase zero-fill on allocation when it's loaded later
MOV R7 #2
STXDW [R10-48] R7
MOV R7 #3
STXDW [R10-40] R7
LDDW R1 #42
MOV R2 R10
ADD R2 #4294967248
MOV R4 #0
MOV R3 R10
ADD R3 #4294967248
CALL R0 #2 ; map_update_elem
MOV R0 #0
EXIT R0 #0
]]
}
end)
it('array map (struct, stack/packet key update, const value)', function()
local kv_t = 'struct { uint64_t a; uint64_t b; }'
local array_map = makemap('array', 256, ffi.typeof(kv_t), ffi.typeof(kv_t))
compile {
input = function (skb)
local key = ffi.new(kv_t)
key.a = eth.ip.tos -- Load key part from dissector
local val = array_map[key]
if val then
val.a = 5
end
end,
expect = [[
MOV R0 #0
STXDW [R10-48] R0
STXDW [R10-56] R0 -- NYI: erase zero-fill on allocation when it's loaded later
LDB R0 skb[15]
STXDW [R10-56] R0
LDDW R1 #42
MOV R2 R10
ADD R2 #4294967240
CALL R0 #1 ; map_lookup_elem
JEQ R0 #0 => 0014
MOV R7 #5
STXDW [R0+0] R7
MOV R0 #0
EXIT R0 #0
]]
}
end)
it('array map (struct, stack/packet key update, map value)', function()
local kv_t = 'struct { uint64_t a; uint64_t b; }'
local array_map = makemap('array', 256, ffi.typeof(kv_t), ffi.typeof(kv_t))
compile {
input = function (skb)
local key = ffi.new(kv_t)
key.a = eth.ip.tos -- Load key part from dissector
local val = array_map[key]
if val then
val.a = val.b
end
end,
expect = [[
MOV R0 #0
STXDW [R10-48] R0
STXDW [R10-56] R0 -- NYI: erase zero-fill on allocation when it's loaded later
LDB R0 skb[15]
STXDW [R10-56] R0
LDDW R1 #42
MOV R2 R10
ADD R2 #4294967240
CALL R0 #1 ; map_lookup_elem
JEQ R0 #0 => 0014
LDXDW R7 [R0+8]
STXDW [R0+0] R7
MOV R0 #0
EXIT R0 #0
]]
}
end)
it('array map (struct, stack/packet key update, stack value)', function()
local kv_t = 'struct { uint64_t a; uint64_t b; }'
local array_map = makemap('array', 256, ffi.typeof(kv_t), ffi.typeof(kv_t))
compile {
input = function (skb)
local key = ffi.new(kv_t)
key.a = eth.ip.tos -- Load key part from dissector
local val = array_map[key]
if val then
val.a = key.b
end
end,
expect = [[
MOV R0 #0
STXDW [R10-48] R0
STXDW [R10-56] R0 -- NYI: erase zero-fill on allocation when it's loaded later
LDB R0 skb[15]
STXDW [R10-56] R0
LDDW R1 #42
MOV R2 R10
ADD R2 #4294967240
CALL R0 #1 ; map_lookup_elem
JEQ R0 #0 => 0014
LDXDW R7 [R10-48]
STXDW [R0+0] R7
MOV R0 #0
EXIT R0 #0
]]
}
end)
it('array map (struct, stack/packet key replace, stack value)', function()
local kv_t = 'struct { uint64_t a; uint64_t b; }'
local array_map = makemap('array', 256, ffi.typeof(kv_t), ffi.typeof(kv_t))
compile {
input = function (skb)
local key = ffi.new(kv_t)
key.a = eth.ip.tos -- Load key part from dissector
local val = array_map[key]
if val then
val.a = key.b
else
array_map[key] = key
end
end,
expect = [[
MOV R0 #0
STXDW [R10-48] R0
STXDW [R10-56] R0
LDB R0 skb[15]
STXDW [R10-56] R0
LDDW R1 #42
MOV R2 R10
ADD R2 #4294967240
CALL R0 #1 ; map_lookup_elem
JEQ R0 #0 => 0016 -- if (map_value ~= NULL)
LDXDW R7 [R10-48]
STXDW [R0+0] R7
MOV R7 #0
JEQ R7 #0 => 0026 -- jump over false branch
STXDW [R10-24] R0
LDDW R1 #42
MOV R2 R10
ADD R2 #4294967240
MOV R4 #0
MOV R3 R10
ADD R3 #4294967240
CALL R0 #2 ; map_update_elem
LDXDW R0 [R10-24]
MOV R0 #0
EXIT R0 #0
]]
}
end)
end)
describe('control flow', function()
it('condition with constant return', function()
compile {
input = function (skb)
local v = eth.ip.tos
if v then
return 1
else
return 0
end
end,
expect = [[
LDB R0 skb[15]
JEQ R0 #0 => 0005
MOV R0 #1
EXIT R0 #0
MOV R0 #0 -- 0005 jump target
EXIT R0 #0
]]
}
end)
it('condition with cdata constant return', function()
local cdata = 2ULL
compile {
input = function (skb)
local v = eth.ip.tos
if v then
return cdata + 1
else
return 0
end
end,
expect = [[
LDB R0 skb[15]
JEQ R0 #0 => 0006
LDDW R0 #3
EXIT R0 #0
MOV R0 #0 -- 0006 jump target
EXIT R0 #0
]]
}
end)
it('condition with constant return (inversed)', function()
compile {
input = function (skb)
local v = eth.ip.tos
if not v then
return 1
else
return 0
end
end,
expect = [[
LDB R0 skb[15]
JNE R0 #0 => 0005
MOV R0 #1
EXIT R0 #0
MOV R0 #0 -- 0005 jump target
EXIT R0 #0
]]
}
end)
it('condition with variable mutation', function()
compile {
input = function (skb)
local v = 0
if eth.ip.tos then
v = 1
end
return v
end,
expect = [[
LDB R0 skb[15]
MOV R1 #0
STXDW [R10-16] R1
JEQ R0 #0 => 0007
MOV R7 #1
STXDW [R10-16] R7
LDXDW R0 [R10-16]
EXIT R0 #0
]]
}
end)
it('condition with nil variable mutation', function()
compile {
input = function (skb)
local v -- nil, will be elided
if eth.ip.tos then
v = 1
else
v = 0
end
return v
end,
expect = [[
LDB R0 skb[15]
JEQ R0 #0 => 0007
MOV R7 #1
STXDW [R10-16] R7
MOV R7 #0
JEQ R7 #0 => 0009
MOV R7 #0
STXDW [R10-16] R7
LDXDW R0 [R10-16]
EXIT R0 #0
]]
}
end)
it('nested condition with variable mutation', function()
compile {
input = function (skb)
local v = 0
local tos = eth.ip.tos
if tos then
if tos > 5 then
v = 5
else
v = 1
end
end
return v
end,
expect = [[
LDB R0 skb[15]
MOV R1 #0
STXDW [R10-16] R1 -- materialize v = 0
JEQ R0 #0 => 0013 -- if not tos
MOV R7 #5
JGE R7 R0 => 0011 -- if 5 > tos
MOV R7 #5
STXDW [R10-16] R7 -- materialize v = 5
MOV R7 #0
JEQ R7 #0 => 0013
MOV R7 #1 -- 0011 jump target
STXDW [R10-16] R7 -- materialize v = 1
LDXDW R0 [R10-16]
EXIT R0 #0
]]
}
end)
it('nested condition with variable shadowing', function()
compile {
input = function (skb)
local v = 0
local tos = eth.ip.tos
if tos then
local v = 0 -- luacheck: ignore 231
if tos > 5 then
v = 5 -- changing shadowing variable
end
else
v = 1
end
return v
end,
expect = [[
LDB R0 skb[15]
MOV R1 #0
STXDW [R10-16] R1 -- materialize v = 0
JEQ R0 #0 => 0011 -- if not tos
MOV R7 #5
MOV R1 #0
STXDW [R10-32] R1 -- materialize shadowing variable
JGE R7 R0 => 0013 -- if 5 > tos
MOV R7 #0 -- erased 'v = 5' dead store
JEQ R7 #0 => 0013
MOV R7 #1 -- 0011 jump target
STXDW [R10-16] R7 -- materialize v = 1
LDXDW R0 [R10-16] -- 0013 jump target
EXIT R0 #0
]]
}
end)
it('condition materializes shadowing variable at the end of BB', function()
compile {
input = function (skb)
local v = time()
local v1 = 0 -- luacheck: ignore 231
if eth.ip.tos then
v1 = v
end
end,
expect = [[
CALL R0 #5 ; ktime_get_ns
STXDW [R10-16] R0
LDB R0 skb[15]
MOV R1 #0
STXDW [R10-24] R1 -- materialize v1 = 0
JEQ R0 #0 => 0009
LDXDW R7 [R10-16]
STXDW [R10-24] R7 -- v1 = v0
MOV R0 #0
EXIT R0 #0
]]
}
end)
end)
end)