Commit d28957ef authored by Marek Vavruša's avatar Marek Vavruša

lua/bpf: implement explicit PTR[0] operation

the BPF maps and pointers were previously
implicitly dereferenced whenever an operation
on them required a value and not a pointer.
the upside is that accessing map element data
didn’t require an explicit operation, the downside
is that it wasn’t possible to get the value
explicitly.

this makes it possible to dereference any pointer
and materialize the value in the register as long
as it’s shorter than register width using `ptr[0]`
element access operator.
parent fefaf32b
...@@ -615,6 +615,7 @@ local function MAP_SET(map_var, key, key_imm, src) ...@@ -615,6 +615,7 @@ local function MAP_SET(map_var, key, key_imm, src)
reg_alloc(stackslots, 4) -- Spill anything in R4 (unnamed tmp variable) reg_alloc(stackslots, 4) -- Spill anything in R4 (unnamed tmp variable)
emit(BPF.ALU64 + BPF.MOV + BPF.K, 4, 0, 0, 0) -- BPF_ANY, create new element or update existing emit(BPF.ALU64 + BPF.MOV + BPF.K, 4, 0, 0, 0) -- BPF_ANY, create new element or update existing
-- Reserve R3 for value pointer -- Reserve R3 for value pointer
reg_alloc(stackslots, 3) -- Spill anything in R3 (unnamed tmp variable)
local val_size = ffi.sizeof(map.val_type) local val_size = ffi.sizeof(map.val_type)
local w = const_width[val_size] or BPF.DW local w = const_width[val_size] or BPF.DW
local pod_type = const_width[val_size] local pod_type = const_width[val_size]
...@@ -627,14 +628,25 @@ local function MAP_SET(map_var, key, key_imm, src) ...@@ -627,14 +628,25 @@ local function MAP_SET(map_var, key, key_imm, src)
emit(BPF.MEM + BPF.ST + w, 10, 0, -sp, base) emit(BPF.MEM + BPF.ST + w, 10, 0, -sp, base)
-- Value is in register, spill it -- Value is in register, spill it
elseif V[src].reg and pod_type then elseif V[src].reg and pod_type then
emit(BPF.MEM + BPF.STX + w, 10, V[src].reg, -sp, 0) -- Value is a pointer, derefernce it and spill it
if cdef.isptr(V[src].type) then
vderef(3, V[src].reg, V[src].const.__dissector)
emit(BPF.MEM + BPF.STX + w, 10, 3, -sp, 0)
else
emit(BPF.MEM + BPF.STX + w, 10, V[src].reg, -sp, 0)
end
-- We get a pointer to spilled register on stack -- We get a pointer to spilled register on stack
elseif V[src].spill then elseif V[src].spill then
-- If variable is a pointer, we can load it to R3 directly (save "LEA") -- If variable is a pointer, we can load it to R3 directly (save "LEA")
if cdef.isptr(V[src].type) then if cdef.isptr(V[src].type) then
reg_fill(src, 3) reg_fill(src, 3)
emit(BPF.JMP + BPF.CALL, 0, 0, 0, HELPER.map_update_elem) -- If variable is a stack pointer, we don't have to check it
return if base.__base then
emit(BPF.JMP + BPF.CALL, 0, 0, 0, HELPER.map_update_elem)
return
end
vderef(3, V[src].reg, V[src].const.__dissector)
emit(BPF.MEM + BPF.STX + w, 10, 3, -sp, 0)
else else
sp = V[src].spill sp = V[src].spill
end end
...@@ -644,9 +656,8 @@ local function MAP_SET(map_var, key, key_imm, src) ...@@ -644,9 +656,8 @@ local function MAP_SET(map_var, key, key_imm, src)
sp = base.__base sp = base.__base
-- Value is constant, materialize it on stack -- Value is constant, materialize it on stack
else else
error('VAR '..key..' is neither const-expr/register/stack/spilled') error('VAR '.. key or key_imm ..' is neither const-expr/register/stack/spilled')
end end
reg_alloc(stackslots, 3) -- Spill anything in R3 (unnamed tmp variable)
emit(BPF.ALU64 + BPF.MOV + BPF.X, 3, 10, 0, 0) emit(BPF.ALU64 + BPF.MOV + BPF.X, 3, 10, 0, 0)
emit(BPF.ALU64 + BPF.ADD + BPF.K, 3, 0, 0, -sp) emit(BPF.ALU64 + BPF.ADD + BPF.K, 3, 0, 0, -sp)
emit(BPF.JMP + BPF.CALL, 0, 0, 0, HELPER.map_update_elem) emit(BPF.JMP + BPF.CALL, 0, 0, 0, HELPER.map_update_elem)
...@@ -731,6 +742,12 @@ local BC = { ...@@ -731,6 +742,12 @@ local BC = {
local base = V[b].const local base = V[b].const
if base.__map then -- BPF map read (constant) if base.__map then -- BPF map read (constant)
MAP_GET(a, b, nil, d) MAP_GET(a, b, nil, d)
-- Specialise PTR[0] as dereference operator
elseif cdef.isptr(V[b].type) and d == 0 then
vcopy(a, b)
local dst_reg = vreg(a)
vderef(dst_reg, dst_reg, V[a].const.__dissector)
V[a].type = V[a].const.__dissector
else else
LOAD(a, b, d, ffi.typeof('uint8_t')) LOAD(a, b, d, ffi.typeof('uint8_t'))
end end
...@@ -912,6 +929,10 @@ local BC = { ...@@ -912,6 +929,10 @@ local BC = {
end, end,
RET1 = function (a, _, _, _) -- RET1 RET1 = function (a, _, _, _) -- RET1
if V[a].reg ~= 0 then vreg(a, 0) end if V[a].reg ~= 0 then vreg(a, 0) end
-- Dereference pointer variables
if cdef.isptr(V[a].type) then
vderef(0, 0, V[a].const.__dissector)
end
emit(BPF.JMP + BPF.EXIT, 0, 0, 0, 0) emit(BPF.JMP + BPF.EXIT, 0, 0, 0, 0)
-- Free optimisation: spilled variable will not be filled again -- Free optimisation: spilled variable will not be filled again
for _,v in pairs(V) do if v.reg == 0 then v.reg = nil end end for _,v in pairs(V) do if v.reg == 0 then v.reg = nil end end
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment