Ejemplo n.º 1
0
def gen_format_type(t, resolve_names = True):
    if t.node_type == 'Type_Void':
        #[ void
    elif t.node_type == 'Type_Boolean':
        #[ bool
    elif t.node_type == 'Type_Bits':
        res = 'int' if t.isSigned else 'uint'
        if t.size <= 8:
            res += '8_t'
        elif t.size <= 16:
            res += '16_t'
        elif t.size <= 32:
            res += '32_t'
        else:
            # TODO is making it an array always OK?
            res += '8_t*'
            # name = "bit" if t.isSigned else "int"
        return res
    elif t.node_type == 'Type_Name':
        if t.type_ref.node_type in {'Type_Enum', 'Type_Error'}:
            #[ enum ${t.type_ref.c_name}
        else:
            if not resolve_names:
                return t.type_ref.name

            global type_env

            if t.type_ref.name in type_env:
                return type_env[t.type_ref.name]
            addWarning('using a named type parameter', 'no type found in environment for variable {}, defaulting to int'.format(t.type_ref.name))
            #[ int /*type param ${t.type_ref.name}*/
    elif t.node_type in {'Type_Extern', 'Type_Struct'}:
        #[ ${t.name}
    else:
        addError('formatting type', 'The type %s is not supported yet!' % t.node_type)
Ejemplo n.º 2
0
 def __init__(self, new_type_env):
     global type_env
     self.env_vars = set()
     for v in new_type_env:
         if v in type_env:
             addWarning('adding a type environment', 'variable {} is already bound to type {}'.format(v, type_env[v]))
         else:
             self.env_vars.add(v)
             type_env[v] = new_type_env[v]
Ejemplo n.º 3
0
def convert_component(component):
    if component.node_type == 'Member':
        hdr      = component.expr
        fld_name = component.member
        fld      = hdr.type.fields.get(fld_name)
        return (component.node_type, hdr, fld)

    if component.node_type == 'Constant':
        return (component.node_type, component.value, "")

    addWarning('generating list expression buffer', 'Skipping not supported list element %s' % component)
    return None
Ejemplo n.º 4
0
def register_write(fun, call):
    global rc
    generated_code = ""
    args = call[1]
    register = args[0] # field
    index = args[1]
    src = args[2]
    if isinstance(index, int): # TODO
        #[ res32 = ${index};
    elif isinstance(index, p4_field): # TODO
        #[ ${ extract_int32(index, 'res32') }
    elif isinstance(val, p4_signature_ref):
        #[ res32 = TODO;
    if (register.width+7)/8 < 4:
        #[ uint8_t register_value_${rc}[4];
    else:
        #[ uint8_t register_value_${rc}[${(register.width+7)/8}];
    if isinstance(src, int):
        #[ value32 = ${src};
        #[ memcpy(register_value_${rc}, &value32, 4);
    elif isinstance(src, p4_field):
        if is_vwf(src):
            addError("generating register_write", "Variable width field '" + str(src) + "' in register_write is not supported yet")
        elif register.width <= 32 and src.width <= 32:
            #[ ${ extract_int32(src, 'value32') }
            #[ memcpy(register_value_${rc}, &value32, 4);
        else:
            if src.width == register.width:
                if src.width % 8 == 0 and src.offset % 8 == 0: # and src.instance.metadata == dst.instance.metadata:
                    #[ EXTRACT_BYTEBUF(pd, ${fld_id(src)}, register_value_${rc})
                else:
                    addError("generating register_write", "Improper bytebufs cannot be modified yet.")
            else:
                addError("generating register_write", "Write register-to-field of different widths is not supported yet.")
    #[ write_register(REGISTER_${register.name}, res32, register_value_${rc});
    rc = rc + 1
    return generated_code

# =============================================================================
# GENERATE_DIGEST

def generate_digest(fun, call):
    generated_code = ""
    
    ## TODO make this proper
    extracted_params = []
    for p in call[1]:
        if isinstance(p, int):
            extracted_params += "0" #[str(p)]
        elif isinstance(p, p4_field_list):
            field_list = p
            extracted_params += ["&fields"]
        else:
            addError("generating actions.c", "Unhandled parameter type in generate_digest: " + str(p))
    fun_params = ["bg"] + ["\""+field_list.name+"\""] + extracted_params
    #[  struct type_field_list fields;
    quan = str(len(field_list.fields))
    #[    fields.fields_quantity = ${quan};
    #[    fields.field_offsets = malloc(sizeof(uint8_t*)*fields.fields_quantity);
    #[    fields.field_widths = malloc(sizeof(uint8_t*)*fields.fields_quantity);
    for i,field in enumerate(field_list.fields):
        j = str(i)
        if isinstance(field, p4_field):
            #[    fields.field_offsets[${j}] = (uint8_t*) field_desc(pd, ${fld_id(field)}).byte_addr;
            #[    fields.field_widths[${j}]  =            field_desc(pd, ${fld_id(field)}).bitwidth;
        else:
            addError("generating actions.c", "Unhandled parameter type in field_list: " + name + ", " + str(field))

    params = ",".join(fun_params)
    #[
    #[    generate_digest(${params}); sleep(1);
    return generated_code

# =============================================================================
# DROP

def drop(fun, call):
    generated_code = ""
    #[ debug("    :: SETTING PACKET TO BE DROPPED\n");
    #[ pd->dropped=1;
    return generated_code;

# =============================================================================
# RESUBMIT

def resubmit(fun, call):
    generated_code = ""
    #[ debug("    :: RESUBMITTING PACKET\n");
    #[ handle_packet(pd, tables);
    return generated_code;

# =============================================================================
# NO_OP

def no_op(fun, call):
    return "no_op(); // no_op"

# =============================================================================
# PUSH

def push(fun, call):
    generated_code = ""
    args = call[1]
    i = args[0]
    #[ push(pd, header_stack_${i.base_name});
    return generated_code

# =============================================================================
# POP

def pop(fun, call):
    generated_code = ""
    args = call[1]
    i = args[0]
    #[ pop(pd, header_stack_${i.base_name});
    return generated_code

# =============================================================================

for fun in userActions(hlir):
    hasParam = fun.signature
    modifiers = ""
    ret_val_type = "void"
    name = fun.name
    params = ", struct action_%s_params parameters" % (name) if hasParam else ""
    #[ ${modifiers} ${ret_val_type} action_code_${name}(packet_descriptor_t* pd, lookup_table_t** tables ${params}) {
    #[     uint32_t value32, res32, mask32;
    #[     (void)value32; (void)res32; (void)mask32;
    for i,call in enumerate(fun.call_sequence):
        name = call[0].name 

        # Generates a primitive action call to `name'
        if name in locals().keys():
            #[ ${locals()[name](fun, call)}
        else:
            addWarning("generating actions.c", "Unhandled primitive function: " +  name)
Ejemplo n.º 5
0
        fref = "field_{}_{}".format(f.header.type.type_ref.name, f.field_name)

        if f.width <= 32:
            #{ #ifdef T4P4S_DEBUG
            #{     if (unlikely(pd->headers[header_instance_${hi_name}].pointer == NULL)) {
            #[         debug(" " T4LIT(!!!!,error) " " T4LIT(Lookup on invalid header,error) " " T4LIT(${hi_name},header) "." T4LIT(${f.field_name},field) "\n");
            #}     }
            #} #endif
            #[ EXTRACT_INT32_BITS_PACKET(pd, $href, $fref, *(uint32_t*)key)
            #[ key += sizeof(uint32_t);
        elif f.width > 32 and f.width % 8 == 0:
            byte_width = (f.width+7)/8
            #[ EXTRACT_BYTEBUF_PACKET(pd, $href, $fref, key)
            #[ key += ${byte_width};
        else:
            addWarning("table key calculation", "Skipping unsupported field {} ({} bits): it is over 32 bits long and not byte aligned".format(f.id, f.width))

    if table.match_type == "LPM":
        #[ key -= ${table.key_length_bytes};
        #[ int c, d;
        #[ for(c = ${table.key_length_bytes-1}, d = 0; c >= 0; c--, d++) *(reverse_buffer+d) = *(key+c);
        #[ for(c = 0; c < ${table.key_length_bytes}; c++) *(key+c) = *(reverse_buffer+c);
    #} }

################################################################################
# Table application

def unique_stable(items):
    """Returns only the first occurrence of the items in a list.
    Equivalent to unique_everseen from Python 3."""
    from collections import OrderedDict
Ejemplo n.º 6
0
            generated_code += " .size = " + str(
                table.max_size) + ",// sugar@45\n"
    else:
        generated_code += " .size = 1,// sugar@47\n"
    generated_code += "  .min_width = " + str(
        32 if counter.min_width is None else counter.min_width
    ) + ",// sugar@48\n"
    generated_code += "  .saturating = " + str(
        1 if counter.saturating else 0) + "// sugar@49\n"
    generated_code += " },// sugar@50\n"
generated_code += " };// sugar@51\n"

generated_code += " p4_register_t register_config[NB_REGISTERS] = {// sugar@53\n"
for register in hlir.p4_registers.values():
    if register.binding is not None:
        addWarning(
            "",
            "direct and static registers currently treated as plain registers, no optimization occurs"
        )
        continue
    if register.layout is not None:
        addError("", "registers with custom layouts are not supported yet")
        continue
    generated_code += " {// sugar@61\n"
    generated_code += "  .name= \"" + str(register.name) + "\",// sugar@62\n"
    generated_code += "  .size = " + str(
        register.instance_count) + ",// sugar@63\n"
    generated_code += "  .width = " + str(
        (register.width + 7) / 8) + ",// sugar@64\n"
    generated_code += " },// sugar@65\n"
generated_code += " };// sugar@66\n"
Ejemplo n.º 7
0
def gen_format_expr(e, format_as_value=True, expand_parameters=False):
    simple_binary_ops = {'Div':'/', 'Mod':'%',                                 #Binary arithmetic operators
                         'Grt':'>', 'Geq':'>=', 'Lss':'<', 'Leq':'<=',         #Binary comparison operators
                         'BAnd':'&', 'BOr':'|', 'BXor':'^',                    #Bitwise operators
                         'LAnd':'&&', 'LOr':'||',                              #Boolean operators
                         'Equ':'==', 'Neq':'!='}                               #Equality operators

    complex_binary_ops = {'Add':'+', 'Sub':'-', 'Mul':'*', 'Shl':'<<', 'Shr':'>>'}

    if e is None:
        return "FORMAT_EXPR(None)"
    elif e.node_type == 'DefaultExpression':
        return ""
    elif e.node_type == 'Parameter':
        return format_type(e.type) + " " + e.name
    elif e.node_type == 'Constant':
        if e.type.node_type == 'Type_Bits':
            if e.type.size > 32:
                def split_text(text, n):
                    """Splits the text into chunks that are n characters long."""
                    return [text[i:i+n] for i in range(0, len(text), n)]

                byte_width = (e.type.size+7)/8
                const_str_format = '{:0' + str(2 * byte_width) + 'x}'
                const_str = const_str_format.format(e.value)
                array_const = ", ".join(["0x" + txt for txt in split_text(const_str, 2)])
                var_name = generate_var_name("const", "0x" + const_str)

                prepend_statement("uint8_t " + var_name + "[] = {" + array_const + "};\n")

                return var_name
            else:
                # 4294967136 versus (uint32_t)4294967136
                return "({}){}".format(format_type(e.type), print_with_base(e.value, e.base))
        else:
            return str(e.value)
    elif e.node_type == 'BoolLiteral':
        return 'true' if e.value else 'false'
    elif e.node_type == 'StringLiteral':
        return '"' + e.value + '"';
    elif e.node_type == 'TypeNameExpression':
        return format_expr(e.typeName.type_ref);

    elif e.node_type == 'Neg':
        if e.type.node_type == 'Type_Bits' and not e.type.isSigned:
            return '(' + format_type_mask(e.type) + '(' + str(2**e.type.size) + '-' + format_expr(e.expr) + '))'
        else:
            return '(-' + format_expr(e.expr) + ')'
    elif e.node_type == 'Cmpl':
        return '(' + format_type_mask(e.type) + '(~' + format_expr(e.expr) + '))'
    elif e.node_type == 'LNot':
        return '(!' + format_expr(e.expr) + ')'

    elif e.node_type in simple_binary_ops and e.node_type == 'Equ' and e.left.type.size > 32:
        return "0 == memcmp({}, {}, ({} + 7) / 8)".format(format_expr(e.left), format_expr(e.right), e.left.type.size)

    elif e.node_type in simple_binary_ops:
        return '(' + format_expr(e.left) + simple_binary_ops[e.node_type] + format_expr(e.right) + ')'

    #Subtraction on unsigned values is performed by adding the negation of the second operand
    elif e.node_type == 'Sub' and e.type.node_type == 'Type_Bits' and not e.type.isSigned:
        return '(' + format_type_mask(e.type) + '(' + format_expr(e.left) + '+(' + str(2**e.type.size) + '-' + format_expr(e.right) + ')))'
    #Right shift on signed values is performed with a shift width check
    elif e.node_type == 'Shr' and e.type.node_type == 'Type_Bits' and e.type.isSigned:
        return '(({1}>{2}) ? 0 : ({0} >> {1}))'.format(format_expr(e.left), format_expr(e.right), e.type.size)
    #These formatting rules MUST follow the previous special cases
    elif e.node_type in complex_binary_ops:
        temp_expr = '(' + format_expr(e.left) + complex_binary_ops[e.node_type] + format_expr(e.right) + ')'
        if e.type.node_type == 'Type_InfInt':
            return temp_expr
        elif e.type.node_type == 'Type_Bits':
            if not e.type.isSigned:
                return '(' + format_type_mask(e.type) + temp_expr + ')'
            else:
                if e.type.size in {8,16,32}:
                    return '((' + format_type(e.type) + ') ' + temp_expr + ')'
                else:
                    addError('formatting an expression', 'Expression of type %s is not supported on int<%s>. (Only int<8>, int<16> and int<32> are supported.)' % (e.node_type, e.type.size))
                    return ''

    elif e.node_type == 'Mux':
        return '(' + format_expr(e.e0) + '?' + format_expr(e.e1) + ':' + format_expr(e.e2) + ')'

    elif e.node_type == 'Slice':
        return '(' + format_type_mask(e.type) + '(' + format_expr(e.e0) + '>>' + format_expr(e.e2) + '))'

    elif e.node_type == 'Concat':
        return '((' + format_expr(e.left) + '<<' + str(e.right.type.size) + ') | ' + format_expr(e.right) + ')'

    elif e.node_type == 'Cast':
        if e.expr.type.node_type == 'Type_Bits' and not e.expr.type.isSigned and e.expr.type.size == 1 \
                and e.destType.node_type == 'Type_Boolean':        #Cast from bit<1> to bool
            return '(' + format_expr(e.expr) + ')'
        elif e.expr.type.node_type == 'Type_Boolean' and e.destType.node_type == 'Type_Bits' and not e.destType.isSigned \
                and e.destType.size == 1:                          #Cast from bool to bit<1>
            return '(' + format_expr(e.expr) + '? 1 : 0)'
        elif e.expr.type.node_type == 'Type_Bits' and e.destType.node_type == 'Type_Bits':
            if e.expr.type.isSigned == e.destType.isSigned:
                if not e.expr.type.isSigned:                       #Cast from bit<w> to bit<v>
                    if e.expr.type.size > e.destType.size:
                        return '(' + format_type_mask(e.destType) + format_expr(e.expr) + ')'
                    else:
                        return format_expr(e.expr)
                else:                                              #Cast from int<w> to int<v>
                    return '((' + format_type(e.destType) + ') ' + format_expr(e.expr) + ')'
            elif e.expr.type.isSigned and not e.destType.isSigned: #Cast from int<w> to bit<w>
                return '(' + format_type_mask(e.destType) + format_expr(e.expr) + ')'
            elif not e.expr.type.isSigned and e.destType.isSigned: #Cast from bit<w> to int<w>
                if e.destType.size in {8,16,32}:
                    return '((' + format_type(e.destType) + ')' + format_expr(e.expr) + ')'
                else:
                    addError('formatting an expression', 'Cast from bit<%s> to int<%s> is not supported! (Only int<8>, int<16> and int<32> are supported.)' % e.destType.size)
                    return ''
        #Cast from int to bit<w> and int<w> are performed by P4C
        addError('formatting an expression', 'Cast from %s to %s is not supported!' % (pp_type_16(e.expr.type), pp_type_16(e.destType)))
        return ''

    elif e.node_type == 'ListExpression':
        if e.id not in generated_exprs:
            prepend_statement(listexpression_to_buf(e))
            generated_exprs.add(e.id)
        return '(struct uint8_buffer_s) {{ .buffer =  buffer{}, .buffer_size = buffer{}_size }}'.format(e.id, e.id)
        # return 'buffer{}, buffer{}_size'.format(e.id, e.id)
    elif e.node_type == 'SelectExpression':
        #Generate local variables for select values
        for k in e.select.components:
            if k.type.node_type == 'Type_Bits' and k.type.size <= 32:
                prepend_statement('{} {} = {};'.format(format_type(k.type), gen_var_name(k), format_expr(k)))
            elif k.type.node_type == 'Type_Bits' and k.type.size % 8 == 0:
                prepend_statement('uint8_t {0}[{1}];\n EXTRACT_BYTEBUF_PACKET(pd, {2}, {0});'
                                  .format(gen_var_name(k), k.type.size/8, format_expr(k, False)))
            else:
                addError('formatting select expression', 'Select on type %s is not supported!' % pp_type_16(k.type))

        cases = []
        for case in e.selectCases:
            cases_tmp = case.keyset.components if case.keyset.node_type == 'ListExpression' else [case.keyset]
            conds = []
            for k, c in zip(e.select.components, cases_tmp):
                select_type = k.type.node_type
                size = k.type.size #if k.type.node_type == 'Type_Bits' else 0
                case_type = c.node_type

                if case_type == 'DefaultExpression':
                    conds.append('true /* default */')
                elif case_type == 'Constant' and select_type == 'Type_Bits' and 32 < size and size % 8 == 0:
                    byte_array = int_to_big_endian_byte_array_with_length(c.value, size/8)
                    prepend_statement('uint8_t {}[{}] = {};'.format(gen_var_name(c), size/8, byte_array))
                    conds.append('memcmp({}, {}, {}) == 0'.format(gen_var_name(k), gen_var_name(c), size/8))
                elif size <= 32:
                    if case_type == 'Range':
                        conds.append('{0} <= {1} && {1} <= {2}'.format(format_expr(c.left), gen_var_name(k), format_expr(c.right)))
                    elif case_type == 'Mask':
                        conds.append('{0} & {1} == {2} & {1}'.format(format_expr(c.left), format_expr(c.right), gen_var_name(k)))
                    else:
                        if case_type not in {'Constant'}: #Trusted expressions
                            addWarning('formatting a select case', 'Select statement cases of type %s on %s might not work properly.'
                                       % (case_type, pp_type_16(k.type)))
                        conds.append('{} == {}'.format(gen_var_name(k), format_expr(c)))
                else:
                    addError('formatting a select case', 'Select statement cases of type %s on %s is not supported!'
                             % (case_type, pp_type_16(k.type)))
            cases.append('if({0}){{parser_state_{1}(pd, buf, tables, pstate);}}'.format(' && '.join(conds), format_expr(case.state)))
        return '\nelse\n'.join(cases)
Ejemplo n.º 8
0
def count(fun, call):
    generated_code = ""
    args = call[1]
    counter = args[0]
    index = args[1]
    if isinstance(index, int): # TODO
        #[ value32 = ${val};
    elif isinstance(index, p4_field): # TODO
        #[ EXTRACT_INT32_AUTO(pd, ${fld_id(index)}, value32)
    elif isinstance(val, p4_signature_ref):
        #[ value32 = TODO;
    #[ increase_counter(COUNTER_${counter.name}, value32);
    return generated_code

# =============================================================================
# GENERATE_DIGEST

def generate_digest(fun, call):
    generated_code = ""
    
    ## TODO make this proper
    fun_params = ["bg", "\"mac_learn_digest\""]
    for p in call[1]:
        if isinstance(p, int):
            fun_params += "0" #[str(p)]
        elif isinstance(p, p4_field_list):
            field_list = p
            fun_params += ["&fields"]
        else:
            addError("generating actions.c", "Unhandled parameter type in generate_digest: " + str(p))
 
    #[  struct type_field_list fields;
    quan = str(len(field_list.fields))
    #[    fields.fields_quantity = ${quan};
    #[    fields.field_offsets = malloc(sizeof(uint8_t*)*fields.fields_quantity);
    #[    fields.field_widths = malloc(sizeof(uint8_t*)*fields.fields_quantity);
    for i,field in enumerate(field_list.fields):
        j = str(i)
        if isinstance(field, p4_field):
            #[    fields.field_offsets[${j}] = (uint8_t*) (pd->headers[header_instance_${field.instance}].pointer + field_instance_byte_offset_hdr[field_instance_${field.instance}_${field.name}]);
            #[    fields.field_widths[${j}] = field_instance_bit_width[field_instance_${field.instance}_${field.name}]*8;
        else:
            addError("generating actions.c", "Unhandled parameter type in field_list: " + name + ", " + str(field))

    params = ",".join(fun_params)
    #[
    #[    generate_digest(${params}); sleep(1);
    return generated_code

# =============================================================================
# DROP

def drop(fun, call):
    return "drop(pd);"

# =============================================================================
# PUSH

def push(fun, call):
    generated_code = ""
    args = call[1]
    i = args[0]
    #[ push(pd, header_stack_${i.base_name});
    return generated_code

# =============================================================================
# POP

def pop(fun, call):
    generated_code = ""
    args = call[1]
    i = args[0]
    #[ pop(pd, header_stack_${i.base_name});
    return generated_code

# =============================================================================

for fun in useraction_objs:
    hasParam = fun.signature
    modifiers = ""
    ret_val_type = "void"
    name = fun.name
    params = ", struct action_%s_params parameters" % (name) if hasParam else ""
    #[ ${modifiers} ${ret_val_type} action_code_${name}(packet_descriptor_t* pd, lookup_table_t** tables ${params}) {
    #[     uint32_t value32, res32;
    #[     (void)value32; (void)res32;
    for i,call in enumerate(fun.call_sequence):
        name = call[0].name 

        # Generates a primitive action call to `name'
        if name in locals().keys():
            #[ ${locals()[name](fun, call)}
        else:
            addWarning("generating actions.c", "Unhandled primitive function: " +  name)
    return generated_code


# =============================================================================

for fun in userActions(hlir):
    hasParam = fun.signature
    modifiers = ""
    ret_val_type = "void"
    name = fun.name
    params = ", struct action_%s_params parameters" % (
        name) if hasParam else ""
    generated_code += " " + str(modifiers) + " " + str(
        ret_val_type) + " action_code_" + str(
            name) + "(packet_descriptor_t* pd, lookup_table_t** tables " + str(
                params) + ") {// sugar@439\n"
    generated_code += "     uint32_t value32, res32, mask32;// sugar@440\n"
    generated_code += "     (void)value32; (void)res32; (void)mask32;// sugar@441\n"
    for i, call in enumerate(fun.call_sequence):
        name = call[0].name

        # Generates a primitive action call to `name'
        if name in locals().keys():
            generated_code += " " + str(locals()[name](
                fun, call)) + "// sugar@447\n"
        else:
            addWarning("generating actions.c",
                       "Unhandled primitive function: " + name)

    generated_code += " }// sugar@451\n"
    generated_code += "\n"