blob: e4ab51fbf08532aac528442f8d5713e784972c17 [file] [log] [blame]
# Copyright (c) Barefoot Networks, Inc.
# Licensed under the Apache License, Version 2.0 (the "License")
from collections import defaultdict, OrderedDict
from p4_hlir.hlir import parse_call, p4_field, p4_parse_value_set, \
P4_DEFAULT, p4_parse_state, p4_table, \
p4_conditional_node, p4_parser_exception, \
p4_header_instance, P4_NEXT
import ebpfProgram
import ebpfInstance
import ebpfType
import ebpfStructType
from topoSorting import Graph
from programSerializer import ProgramSerializer
def produce_parser_topo_sorting(hlir):
# This function is copied from the P4 behavioral model implementation
header_graph = Graph()
def walk_rec(hlir, parse_state, prev_hdr_node, tag_stacks_index):
assert(isinstance(parse_state, p4_parse_state))
for call in parse_state.call_sequence:
call_type = call[0]
if call_type == parse_call.extract:
hdr = call[1]
if hdr.virtual:
base_name = hdr.base_name
current_index = tag_stacks_index[base_name]
if current_index > hdr.max_index:
return
tag_stacks_index[base_name] += 1
name = base_name + "[%d]" % current_index
hdr = hlir.p4_header_instances[name]
if hdr not in header_graph:
header_graph.add_node(hdr)
hdr_node = header_graph.get_node(hdr)
if prev_hdr_node:
prev_hdr_node.add_edge_to(hdr_node)
else:
header_graph.root = hdr
prev_hdr_node = hdr_node
for branch_case, next_state in parse_state.branch_to.items():
if not next_state:
continue
if not isinstance(next_state, p4_parse_state):
continue
walk_rec(hlir, next_state, prev_hdr_node, tag_stacks_index.copy())
start_state = hlir.p4_parse_states["start"]
walk_rec(hlir, start_state, None, defaultdict(int))
header_topo_sorting = header_graph.produce_topo_sorting()
return header_topo_sorting
class EbpfDeparser(object):
def __init__(self, hlir):
header_topo_sorting = produce_parser_topo_sorting(hlir)
self.headerOrder = [hdr.name for hdr in header_topo_sorting]
def serialize(self, serializer, program):
assert isinstance(serializer, ProgramSerializer)
assert isinstance(program, ebpfProgram.EbpfProgram)
serializer.emitIndent()
serializer.blockStart()
serializer.emitIndent()
serializer.appendLine("/* Deparser */")
serializer.emitIndent()
serializer.appendFormat("{0} = 0;", program.offsetVariableName)
serializer.newline()
for h in self.headerOrder:
header = program.getHeaderInstance(h)
self.serializeHeaderEmit(header, serializer, program)
serializer.blockEnd(True)
def serializeHeaderEmit(self, header, serializer, program):
assert isinstance(header, ebpfInstance.EbpfHeader)
assert isinstance(serializer, ProgramSerializer)
assert isinstance(program, ebpfProgram.EbpfProgram)
p4header = header.hlirInstance
assert isinstance(p4header, p4_header_instance)
serializer.emitIndent()
serializer.appendFormat("if ({0}.{1}.valid) ",
program.headerStructName, header.name)
serializer.blockStart()
if ebpfProgram.EbpfProgram.isArrayElementInstance(p4header):
ebpfStack = program.getStackInstance(p4header.base_name)
assert isinstance(ebpfStack, ebpfInstance.EbpfHeaderStack)
if isinstance(p4header.index, int):
index = "[" + str(headerInstance.index) + "]"
elif p4header.index is P4_NEXT:
index = "[" + ebpfStack.indexVar + "]"
else:
raise CompilationException(
True, "Unexpected index for array {0}",
p4header.index)
basetype = ebpfStack.basetype
else:
ebpfHeader = program.getHeaderInstance(p4header.name)
basetype = ebpfHeader.type
index = ""
alignment = 0
for field in basetype.fields:
assert isinstance(field, ebpfStructType.EbpfField)
self.serializeFieldEmit(serializer, p4header.base_name,
index, field, alignment, program)
alignment += field.widthInBits()
alignment = alignment % 8
serializer.blockEnd(True)
def serializeFieldEmit(self, serializer, name, index,
field, alignment, program):
assert isinstance(index, str)
assert isinstance(name, str)
assert isinstance(field, ebpfStructType.EbpfField)
assert isinstance(serializer, ProgramSerializer)
assert isinstance(alignment, int)
assert isinstance(program, ebpfProgram.EbpfProgram)
if field.name == "valid":
return
fieldToEmit = (program.headerStructName + "." + name +
index + "." + field.name)
width = field.widthInBits()
if width <= 32:
store = self.generatePacketStore(fieldToEmit, 0, alignment,
width, program)
serializer.emitIndent()
serializer.appendLine(store)
else:
# Destination is bigger than 4 bytes and
# represented as a byte array.
b = (width + 7) / 8
for i in range(0, b):
serializer.emitIndent()
store = self.generatePacketStore(fieldToEmit + "["+str(i)+"]",
i,
alignment,
8, program)
serializer.appendLine(store)
serializer.emitIndent()
serializer.appendFormat("{0} += {1};",
program.offsetVariableName, width)
serializer.newline()
def generatePacketStore(self, value, offset, alignment, width, program):
assert width > 0
assert alignment < 8
assert isinstance(width, int)
assert isinstance(alignment, int)
return "bpf_dins_pkt({0}, {1} / 8 + {2}, {3}, {4}, {5});".format(
program.packetName,
program.offsetVariableName,
offset,
alignment,
width,
value
)