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)
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]
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
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)
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
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"
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)
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"