diff --git a/Cargo.lock b/Cargo.lock index a4ee934..5b4879b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -410,6 +410,14 @@ dependencies = [ "regex", ] +[[package]] +name = "p4-cg" +version = "0.1.0" +dependencies = [ + "p4", + "thiserror", +] + [[package]] name = "p4-htq" version = "0.1.0" @@ -447,6 +455,7 @@ name = "p4-rust" version = "0.1.0" dependencies = [ "p4", + "p4-cg", "prettyplease", "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index 584607c..2ce613d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,6 +5,7 @@ members = [ "p4", "x4c", "x4c_error_codes", + "codegen/common", "codegen/rust", "codegen/htq", "lang/p4rs", diff --git a/codegen/common/Cargo.toml b/codegen/common/Cargo.toml new file mode 100644 index 0000000..325a202 --- /dev/null +++ b/codegen/common/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "p4-cg" +version = "0.1.0" +edition = "2021" + +[dependencies] +p4 = { path = "../../p4" } +thiserror = "1.0.63" diff --git a/codegen/common/src/lib.rs b/codegen/common/src/lib.rs new file mode 100644 index 0000000..9647d0e --- /dev/null +++ b/codegen/common/src/lib.rs @@ -0,0 +1 @@ +// Copyright 2024 Oxide Computer Company diff --git a/codegen/htq/src/control.rs b/codegen/htq/src/control.rs new file mode 100644 index 0000000..86a5dd3 --- /dev/null +++ b/codegen/htq/src/control.rs @@ -0,0 +1,108 @@ +// Copyright 2024 Oxide Computer Company + +use htq::ast::Parameter; +use p4::hlir::Hlir; + +use crate::{ + error::CodegenError, p4_type_to_htq_type, statement::emit_statement, + RegisterAllocator, +}; + +pub(crate) fn emit_control_functions( + ast: &p4::ast::AST, + hlir: &Hlir, +) -> Result, CodegenError> { + let mut result = Vec::default(); + + for control in &ast.controls { + let cf = emit_control(ast, hlir, control)?; + result.extend(cf.into_iter()); + } + + Ok(result) +} + +fn emit_control( + ast: &p4::ast::AST, + hlir: &Hlir, + control: &p4::ast::Control, +) -> Result, CodegenError> { + let mut result = Vec::default(); + + let mut parameters = Vec::new(); + for x in &control.parameters { + let p = htq::ast::Parameter { + reg: htq::ast::Register::new(x.name.as_str()), + pointer: true, + typ: p4_type_to_htq_type(&x.ty)?, + }; + parameters.push(p); + } + + result.push(emit_control_apply(ast, hlir, control, ¶meters)?); + + for action in &control.actions { + result.push(emit_control_action( + ast, + hlir, + control, + action, + ¶meters, + )?); + } + Ok(result) +} + +fn emit_control_apply( + ast: &p4::ast::AST, + hlir: &Hlir, + control: &p4::ast::Control, + parameters: &[Parameter], +) -> Result { + let mut ra = RegisterAllocator::default(); + let mut names = control.names(); + let mut statements = Vec::default(); + for s in &control.apply.statements { + statements.extend( + emit_statement(s, ast, hlir, &mut names, &mut ra)?.into_iter(), + ) + } + let f = htq::ast::Function { + name: format!("{}_apply", control.name), + parameters: parameters.to_owned(), + statements, + }; + Ok(f) +} + +fn emit_control_action( + ast: &p4::ast::AST, + hlir: &Hlir, + control: &p4::ast::Control, + action: &p4::ast::Action, + parameters: &[Parameter], +) -> Result { + let mut ra = RegisterAllocator::default(); + let mut names = control.names(); + let mut statements = Vec::default(); + let mut parameters = parameters.to_owned(); + for x in &action.parameters { + let p = htq::ast::Parameter { + reg: htq::ast::Register::new(x.name.as_str()), + pointer: true, + typ: p4_type_to_htq_type(&x.ty)?, + }; + parameters.push(p); + } + for s in &action.statement_block.statements { + statements.extend( + emit_statement(s, ast, hlir, &mut names, &mut ra)?.into_iter(), + ) + } + let f = htq::ast::Function { + name: format!("{}_{}", control.name, action.name), + parameters, + statements, + }; + Ok(f) +} diff --git a/codegen/htq/src/error.rs b/codegen/htq/src/error.rs index 21ec8b7..8fee645 100644 --- a/codegen/htq/src/error.rs +++ b/codegen/htq/src/error.rs @@ -1,18 +1,63 @@ // Copyright 2024 Oxide Computer Company +use p4::{ + ast::{DeclarationInfo, Expression, Lvalue}, + lexer::Token, +}; use thiserror::Error; #[derive(Error, Debug)] pub enum CodegenError { #[error("There is no equivalent htq type for {0}")] NoEquivalentType(p4::ast::Type), + + #[error("undefined lvalue \n{0:#?}")] + UndefinedLvalue(Lvalue), + + #[error("cannot assign to \n{0:#?}")] + InvalidAssignment(DeclarationInfo), + + #[error("cannot convert numeric type \n{0:#?}\n to u128")] + NumericConversion(Expression), + + #[error( + "no type information for \n{0:#?}\nthis is likely a type checking bug" + )] + UntypedExpression(Expression), + + #[error( + "parent {0} for member for \n{1:#?}\nnot found: this is likely a front end bug" + )] + MemberParentNotFound(String, Lvalue), + + #[error( + "expected parent of lvalue \n{0:#?}\nto be a struct: this is likely a front end bug" + )] + ExpectedStructParent(Lvalue), + + #[error( + "expected parent of lvalue \n{0:#?}\nto be a header: this is likely a front end bug" + )] + ExpectedHeaderParent(Lvalue), + + #[error("offset for struct or header member \n{0:#?}\nnot found")] + MemberOffsetNotFound(Lvalue), + + #[error("header member {0}\n{1:#?}\nnot found")] + MemberNotFound(String, Lvalue), + + #[error("user defined type {0} not found \n{1:#?}")] + UserDefinedTypeNotFound(String, Token), + + #[error("cannot calculate offset into extern {0} \n{1:#?}")] + CannotOffsetExtern(String, Token), } #[derive(Error, Debug)] pub enum EmitError { - #[error("io error {0}")] + #[error("io error: {0}")] Io(#[from] std::io::Error), - #[error("codegen error {0}")] + #[error("codegen error: {0}")] Codegen(#[from] CodegenError), } diff --git a/codegen/htq/src/expression.rs b/codegen/htq/src/expression.rs index 1237eb4..a851711 100644 --- a/codegen/htq/src/expression.rs +++ b/codegen/htq/src/expression.rs @@ -1,8 +1,96 @@ +use htq::ast::{Register, Rset, Statement, Type, Value}; +use p4::ast::{Expression, ExpressionKind}; + // Copyright 2024 Oxide Computer Company -use crate::error::CodegenError; +use crate::{error::CodegenError, VersionedRegister}; + +// Builds a vector of statements that implement the expression. Returns the +// statements and the register the result of the expression is held in. +pub(crate) fn emit_expression( + expr: &Expression, +) -> Result<(Vec, Register, Type), CodegenError> { + let r = VersionedRegister::for_expression(expr); + match &expr.kind { + ExpressionKind::BoolLit(value) => emit_bool_lit(*value, r), + ExpressionKind::BitLit(width, value) => { + emit_bit_lit(*width, *value, r, expr) + } + ExpressionKind::IntegerLit(value) => emit_int_lit(*value, r), + ExpressionKind::SignedLit(width, value) => { + emit_signed_lit(*width, *value, r) + } + ExpressionKind::Call(_) => { + //TODO + Ok(( + Vec::default(), + Register::new("badreg"), + Type::User("badtype".to_owned()), + )) + } + ExpressionKind::Lvalue(_) => { + //TODO + Ok(( + Vec::default(), + Register::new("badreg"), + Type::User("badtype".to_owned()), + )) + } + xpr => todo!("expression: {xpr:?}"), + } +} + +pub(crate) fn emit_bool_lit( + value: bool, + ra: VersionedRegister, +) -> Result<(Vec, Register, Type), CodegenError> { + let instrs = vec![Statement::Rset(Rset { + target: ra.clone().to_reg(), + typ: Type::Bool, + source: Value::bool(value), + })]; + Ok((instrs, ra.to_reg(), Type::Bool)) +} + +pub(crate) fn emit_bit_lit( + width: u16, + value: u128, + ra: VersionedRegister, + expr: &Expression, +) -> Result<(Vec, Register, Type), CodegenError> { + let value = i128::try_from(value) + .map_err(|_| CodegenError::NumericConversion(expr.clone()))?; + let typ = Type::Bitfield(usize::from(width)); + let instrs = vec![Statement::Rset(Rset { + typ: typ.clone(), + target: ra.clone().to_reg(), + source: Value::number(value), + })]; + Ok((instrs, ra.to_reg(), typ)) +} + +pub(crate) fn emit_int_lit( + value: i128, + ra: VersionedRegister, +) -> Result<(Vec, Register, Type), CodegenError> { + let typ = Type::Signed(128); + let instrs = vec![Statement::Rset(Rset { + typ: typ.clone(), + target: ra.clone().to_reg(), + source: Value::number(value), + })]; + Ok((instrs, ra.to_reg(), typ)) +} -pub(crate) fn _emit_expression( - _stmt: &p4::ast::Expression, -) -> Result, CodegenError> { - todo!() +pub(crate) fn emit_signed_lit( + width: u16, + value: i128, + ra: VersionedRegister, +) -> Result<(Vec, Register, Type), CodegenError> { + let typ = Type::Signed(usize::from(width)); + let instrs = vec![Statement::Rset(Rset { + typ: typ.clone(), + target: ra.clone().to_reg(), + source: Value::number(value), + })]; + Ok((instrs, ra.to_reg(), typ)) } diff --git a/codegen/htq/src/lib.rs b/codegen/htq/src/lib.rs index 6a2a08e..84ebe0e 100644 --- a/codegen/htq/src/lib.rs +++ b/codegen/htq/src/lib.rs @@ -1,16 +1,18 @@ // Copyright 2024 Oxide Computer Company -use std::io::Write; +use std::{collections::HashMap, io::Write}; +use control::emit_control_functions; use error::CodegenError; use header::{p4_header_to_htq_header, p4_struct_to_htq_header}; -use htq::emit::Emit; -use p4::hlir::Hlir; +use htq::{ast::Register, emit::Emit}; +use p4::{ast::Expression, hlir::Hlir}; use parser::emit_parser_functions; use table::p4_table_to_htq_table; use crate::error::EmitError; +mod control; mod error; mod expression; mod header; @@ -44,6 +46,9 @@ pub fn emit( .collect::, CodegenError>>()?; let parser_functions: Vec<_> = emit_parser_functions(ast, hlir)?; + let control_functions: Vec<_> = emit_control_functions(ast, hlir)?; + + // code generation done, now write out the htq AST to a file let mut f = std::fs::File::create(filename)?; @@ -58,7 +63,11 @@ pub fn emit( writeln!(f)?; for func in &parser_functions { - //TODO htq function emit + writeln!(f, "{}", func.emit())?; + } + writeln!(f)?; + + for func in &control_functions { writeln!(f, "{}", func.emit())?; } writeln!(f)?; @@ -77,6 +86,10 @@ fn p4_type_to_htq_type( p4::ast::Type::UserDefined(name, _) => { htq::ast::Type::User(name.clone()) } + p4::ast::Type::Sync(_) => { + htq::ast::Type::Signed(128) + //return Err(CodegenError::NoEquivalentType(t.clone())) + } t @ p4::ast::Type::Table => { return Err(CodegenError::NoEquivalentType(t.clone())) } @@ -98,12 +111,59 @@ fn p4_type_to_htq_type( t @ p4::ast::Type::HeaderMethod => { return Err(CodegenError::NoEquivalentType(t.clone())) } - t @ p4::ast::Type::Sync(_) => { - return Err(CodegenError::NoEquivalentType(t.clone())) - } t @ p4::ast::Type::List(_) => { return Err(CodegenError::NoEquivalentType(t.clone())) } p4::ast::Type::String => todo!("string types not yet supported"), }) } + +#[derive(Default)] +pub(crate) struct RegisterAllocator { + data: HashMap, +} + +impl RegisterAllocator { + pub(crate) fn alloc(&mut self, name: &str) -> htq::ast::Register { + match self.data.get_mut(name) { + Some(rev) => { + *rev += 1; + htq::ast::Register::new(&format!("{}.{}", name, *rev)) + } + None => { + self.data.insert(name.to_owned(), 0); + htq::ast::Register::new(name) + } + } + } +} + +#[derive(Debug, Clone)] +pub(crate) struct VersionedRegister { + pub(crate) reg: Register, + pub(crate) version: usize, +} + +impl VersionedRegister { + pub(crate) fn for_expression(expr: &Expression) -> Self { + Self { + reg: Register::new(&format!( + "tmp{}_{}", + expr.token.line, expr.token.col + )), + version: 0, + } + } + + #[allow(dead_code)] + pub(crate) fn next(self) -> Self { + Self { + reg: self.reg, + version: self.version + 1, + } + } + + pub(crate) fn to_reg(&self) -> Register { + Register::new(&format!("{}.{}", self.reg.0, self.version)) + } +} diff --git a/codegen/htq/src/parser.rs b/codegen/htq/src/parser.rs index ccda2d8..672d39f 100644 --- a/codegen/htq/src/parser.rs +++ b/codegen/htq/src/parser.rs @@ -2,6 +2,7 @@ use crate::{ error::CodegenError, p4_type_to_htq_type, statement::emit_statement, + RegisterAllocator, }; use p4::hlir::Hlir; @@ -19,9 +20,9 @@ pub(crate) fn emit_parser_functions( Ok(result) } -pub(crate) fn emit_parser( - _ast: &p4::ast::AST, - _hlir: &Hlir, +fn emit_parser( + ast: &p4::ast::AST, + hlir: &Hlir, parser: &p4::ast::Parser, ) -> Result, CodegenError> { let mut result = Vec::new(); @@ -36,10 +37,16 @@ pub(crate) fn emit_parser( parameters.push(p); } + let mut names = parser.names(); + for state in &parser.states { - let mut statements = Vec::new(); + // keeps track of register revisions for locals + let mut ra = RegisterAllocator::default(); + let mut statements = Vec::default(); for s in &state.statements.statements { - statements.extend(emit_statement(s)?.into_iter()); + statements.extend( + emit_statement(s, ast, hlir, &mut names, &mut ra)?.into_iter(), + ); } let f = htq::ast::Function { name: format!("{}_{}", parser.name, state.name), diff --git a/codegen/htq/src/statement.rs b/codegen/htq/src/statement.rs index 9f7de67..97d65cd 100644 --- a/codegen/htq/src/statement.rs +++ b/codegen/htq/src/statement.rs @@ -1,27 +1,226 @@ // Copyright 2024 Oxide Computer Company -use crate::error::CodegenError; +use std::collections::HashMap; + +use htq::ast::{Fset, Register, Rset, Value}; +use p4::{ + ast::{ + DeclarationInfo, Direction, Expression, ExpressionKind, Lvalue, + NameInfo, UserDefinedType, + }, + hlir::Hlir, +}; + +use crate::{ + error::CodegenError, expression::emit_expression, RegisterAllocator, +}; pub(crate) fn emit_statement( stmt: &p4::ast::Statement, + ast: &p4::ast::AST, + hlir: &Hlir, + names: &mut HashMap, + ra: &mut RegisterAllocator, ) -> Result, CodegenError> { use p4::ast::Statement as S; match stmt { S::Empty => Ok(Vec::new()), - S::Assignment(_lval, _expr) => todo!(), - S::Call(_call) => todo!(), - S::If(_if_block) => todo!(), - S::Variable(_v) => todo!(), - S::Constant(_c) => todo!(), - S::Transition(_t) => todo!(), - S::Return(_r) => todo!(), + S::Assignment(lval, expr) => { + emit_assignment(hlir, ast, names, lval, expr, ra) + } + S::Call(_call) => Ok(Vec::default()), //TODO + S::If(_if_block) => Ok(Vec::default()), //TODO, + S::Variable(_v) => Ok(Vec::default()), //TODO + S::Constant(_c) => Ok(Vec::default()), //TODO + S::Transition(_t) => Ok(Vec::default()), //TODO + S::Return(_r) => Ok(Vec::default()), //TODO } } fn emit_assignment( + hlir: &Hlir, + ast: &p4::ast::AST, + names: &mut HashMap, target: &p4::ast::Lvalue, source: &p4::ast::Expression, + ra: &mut RegisterAllocator, ) -> Result, CodegenError> { - // Things that can be assigned to are lvalues. - todo!(); + // Things that can be assigned to are lvalues with the following + // declaration kinds. The table shows how each kind is referenced + // in htq code. + // + // *==========================================* + // | Decl Kind | Htq Kind | Instrs | + // |---------------+-------------+------------| + // | Parameter | Pointer | load/store | + // | Struct member | Pointer | fget/fset | + // | Header member | Pointer | fget/fset | + // | Local | Register(s) | arithmetic | + // *==========================================* + // + // Things that can be assigned from are expressions which have the + // following kinds. + // + // Concrete types + // + // *=================================* + // | Expr Kind | Htq Kind | + // |----------------------+----------| + // | Bool literal | bool | + // | Integer literal | i128 | + // | Bitfield Literal (N) | uN | + // | Signed Literal (N) | iN | + // *=================================* + // + // Types that need to be resolved that eventually decay to a concrete + // type. + // + // - lvalue + // - binary + // - index + // - slice + // - call + // - list + // + + let target_info = match hlir.lvalue_decls.get(target) { + Some(info) => info, + None => return Err(CodegenError::UndefinedLvalue(target.clone())), + }; + + let (mut instrs, reg, typ) = emit_expression(source)?; + + match &target_info.decl { + DeclarationInfo::Parameter(Direction::Out) + | DeclarationInfo::Parameter(Direction::InOut) => { + let _treg = Register::new(&target.name); + // TODO store instr + } + DeclarationInfo::StructMember | DeclarationInfo::HeaderMember => { + let treg = Register::new(target.root()); + let offsets = member_offsets(ast, names, target)?; + let instr = Fset { + typ, + offsets, + target: treg, + source: Value::Register(reg), + }; + instrs.push(htq::ast::Statement::Fset(instr)); + } + DeclarationInfo::Local => { + let ty = hlir + .expression_types + .get(source) + .ok_or(CodegenError::UntypedExpression(source.clone()))?; + + //TODO(ry) it's unfortunate that a code generate manually has to + // manually maintain scope information. Perhaps we should be using + // the AST visitor ... although I'm not sure if the AST visitor + // maintains a scope either, if not it probably should .... + names.insert( + target.name.clone(), + NameInfo { + ty: ty.clone(), + decl: DeclarationInfo::Local, + }, + ); + let target = ra.alloc(&target.name); + + let instr = Rset { + target, + typ, + source: Value::Register(reg), + }; + instrs.push(htq::ast::Statement::Rset(instr)); + } + _ => { + return Err(CodegenError::InvalidAssignment( + target_info.decl.clone(), + )) + } + }; + + Ok(instrs) +} + +fn member_offsets( + ast: &p4::ast::AST, + names: &HashMap, + lval: &Lvalue, +) -> Result, CodegenError> { + let root = lval.root(); + let info = names.get(root).ok_or(CodegenError::MemberParentNotFound( + root.to_owned(), + lval.clone(), + ))?; + let mut offsets = Vec::default(); + member_offsets_rec(ast, &info.ty, &mut offsets, lval.clone())?; + Ok(offsets) +} + +fn member_offsets_rec( + ast: &p4::ast::AST, + ty: &p4::ast::Type, + offsets: &mut Vec, + mut lval: Lvalue, +) -> Result<(), CodegenError> { + match ty { + p4::ast::Type::UserDefined(name, _) => { + let root_type = ast.get_user_defined_type(name).ok_or( + CodegenError::UserDefinedTypeNotFound( + name.clone(), + lval.token.clone(), + ), + )?; + lval = lval.pop_left(); + let member = lval.root(); + //TODO + let (offset, member_ty) = match &root_type { + UserDefinedType::Struct(s) => { + let off = s.index_of(member).ok_or( + CodegenError::MemberOffsetNotFound(lval.clone()), + )?; + let member_info = + s.members.iter().find(|x| x.name == member).ok_or( + CodegenError::MemberNotFound( + member.to_owned(), + lval.clone(), + ), + )?; + (off, member_info.ty.clone()) + } + UserDefinedType::Header(h) => { + let off = h.index_of(member).ok_or( + CodegenError::MemberOffsetNotFound(lval.clone()), + )?; + let member_info = + h.members.iter().find(|x| x.name == member).ok_or( + CodegenError::MemberNotFound( + member.to_owned(), + lval.clone(), + ), + )?; + (off, member_info.ty.clone()) + } + UserDefinedType::Extern(e) => { + return Err(CodegenError::CannotOffsetExtern( + e.name.clone(), + lval.token.clone(), + )); + } + }; + let offset = i128::try_from(offset).map_err(|_| { + CodegenError::NumericConversion(Expression { + token: lval.token.clone(), + kind: ExpressionKind::Lvalue(lval.clone()), + }) + })?; + offsets.push(Value::number(offset)); + if lval.degree() > 1 { + member_offsets_rec(ast, &member_ty, offsets, lval.clone())?; + } + } + _ => return Err(CodegenError::ExpectedStructParent(lval.clone())), + } + Ok(()) } diff --git a/codegen/rust/Cargo.toml b/codegen/rust/Cargo.toml index 1a9a90a..ad1fe6b 100644 --- a/codegen/rust/Cargo.toml +++ b/codegen/rust/Cargo.toml @@ -5,6 +5,7 @@ edition = "2021" [dependencies] p4 = { path = "../../p4" } +p4-cg = { path = "../common" } quote = "1.0" proc-macro2 = "1.0" syn = "1.0" diff --git a/codegen/rust/src/header.rs b/codegen/rust/src/header.rs index d36f17b..289f178 100644 --- a/codegen/rust/src/header.rs +++ b/codegen/rust/src/header.rs @@ -1,7 +1,7 @@ // Copyright 2022 Oxide Computer Company -use crate::{rust_type, type_size, Context}; -use p4::ast::{Header, AST}; +use crate::{rust_type, Context}; +use p4::ast::{type_size, Header, AST}; use quote::{format_ident, quote}; pub(crate) struct HeaderGenerator<'a> { diff --git a/codegen/rust/src/lib.rs b/codegen/rust/src/lib.rs index 3945cee..4b78525 100644 --- a/codegen/rust/src/lib.rs +++ b/codegen/rust/src/lib.rs @@ -10,7 +10,7 @@ use quote::{format_ident, quote}; use p4::ast::{ ActionParameter, Control, ControlParameter, DeclarationInfo, Direction, Expression, ExpressionKind, HeaderMember, Lvalue, MutVisitor, NameInfo, - Parser, StructMember, Table, Type, UserDefinedType, AST, + Parser, StructMember, Table, Type, AST, }; use p4::hlir::Hlir; use p4::util::resolve_lvalue; @@ -246,68 +246,6 @@ fn rust_type(ty: &Type) -> TokenStream { } } -fn type_size(ty: &Type, ast: &AST) -> usize { - match ty { - Type::Bool => 1, - Type::Error => todo!("generate error size"), - Type::Bit(size) => *size, - Type::Int(size) => *size, - Type::Varbit(size) => *size, - Type::String => todo!("generate string size"), - Type::UserDefined(name, _) => { - let mut sz: usize = 0; - let udt = ast.get_user_defined_type(name).unwrap_or_else(|| { - panic!("expect user defined type: {}", name) - }); - - match udt { - UserDefinedType::Struct(s) => { - for m in &s.members { - sz += type_size(&m.ty, ast); - } - sz - } - UserDefinedType::Header(h) => { - for m in &h.members { - sz += type_size(&m.ty, ast); - } - sz - } - UserDefinedType::Extern(_) => { - todo!("size for extern?"); - } - } - } - Type::ExternFunction => { - todo!("type size for extern function"); - } - Type::HeaderMethod => { - todo!("type size for header method"); - } - Type::Table => { - todo!("type size for table"); - } - Type::Void => 0, - Type::List(_) => todo!("type size for list"), - Type::State => { - todo!("type size for state"); - } - Type::Action => { - todo!("type size for action"); - } - Type::Sync(_) => todo!("type size for sync"), - } -} - -fn type_size_bytes(ty: &Type, ast: &AST) -> usize { - let s = type_size(ty, ast); - let mut b = s >> 3; - if s % 8 != 0 { - b += 1 - } - b -} - // in the case of an expression // // a &&& b diff --git a/codegen/rust/src/pipeline.rs b/codegen/rust/src/pipeline.rs index a11884f..6f6bf5d 100644 --- a/codegen/rust/src/pipeline.rs +++ b/codegen/rust/src/pipeline.rs @@ -1,11 +1,12 @@ // Copyright 2022 Oxide Computer Company use crate::{ - qualified_table_function_name, qualified_table_name, rust_type, - type_size_bytes, Context, Settings, + qualified_table_function_name, qualified_table_name, rust_type, Context, + Settings, }; use p4::ast::{ - Control, Direction, MatchKind, PackageInstance, Parser, Table, Type, AST, + type_size_bytes, Control, Direction, MatchKind, PackageInstance, Parser, + Table, Type, AST, }; use p4::hlir::Hlir; use proc_macro2::TokenStream; diff --git a/p4/src/ast.rs b/p4/src/ast.rs index a3ce431..3a13b27 100644 --- a/p4/src/ast.rs +++ b/p4/src/ast.rs @@ -783,6 +783,26 @@ impl Header { m.mut_accept_mut(v); } } + + pub fn index_of(&self, member_name: &str) -> Option { + for (i, m) in self.members.iter().enumerate() { + if m.name == member_name { + return Some(i); + } + } + None + } + + pub fn offset_of(&self, ast: &AST, member_name: &str) -> Option { + let mut offset = 0; + for m in &self.members { + if m.name == member_name { + return Some(offset); + } + offset += type_size(&m.ty, ast); + } + None + } } #[derive(Debug, Clone)] @@ -868,6 +888,26 @@ impl Struct { m.mut_accept_mut(v); } } + + pub fn index_of(&self, member_name: &str) -> Option { + for (i, m) in self.members.iter().enumerate() { + if m.name == member_name { + return Some(i); + } + } + None + } + + pub fn offset_of(&self, ast: &AST, member_name: &str) -> Option { + let mut offset = 0; + for m in &self.members { + if m.name == member_name { + return Some(offset); + } + offset += type_size(&m.ty, ast); + } + None + } } #[derive(Debug, Clone)] @@ -2468,3 +2508,65 @@ pub trait MutVisitorMut { fn package_parameter(&mut self, _: &mut PackageParameter) {} fn extern_method(&mut self, _: &mut ExternMethod) {} } + +pub fn type_size(ty: &Type, ast: &AST) -> usize { + match ty { + Type::Bool => 1, + Type::Error => todo!("generate error size"), + Type::Bit(size) => *size, + Type::Int(size) => *size, + Type::Varbit(size) => *size, + Type::String => todo!("generate string size"), + Type::UserDefined(name, _) => { + let mut sz: usize = 0; + let udt = ast.get_user_defined_type(name).unwrap_or_else(|| { + panic!("expect user defined type: {}", name) + }); + + match udt { + UserDefinedType::Struct(s) => { + for m in &s.members { + sz += type_size(&m.ty, ast); + } + sz + } + UserDefinedType::Header(h) => { + for m in &h.members { + sz += type_size(&m.ty, ast); + } + sz + } + UserDefinedType::Extern(_) => { + todo!("size for extern?"); + } + } + } + Type::ExternFunction => { + todo!("type size for extern function"); + } + Type::HeaderMethod => { + todo!("type size for header method"); + } + Type::Table => { + todo!("type size for table"); + } + Type::Void => 0, + Type::List(_) => todo!("type size for list"), + Type::State => { + todo!("type size for state"); + } + Type::Action => { + todo!("type size for action"); + } + Type::Sync(_) => todo!("type size for sync"), + } +} + +pub fn type_size_bytes(ty: &Type, ast: &AST) -> usize { + let s = type_size(ty, ast); + let mut b = s >> 3; + if s % 8 != 0 { + b += 1 + } + b +}