diff --git a/codegen/htq/src/error.rs b/codegen/htq/src/error.rs index 213992d..1647d9e 100644 --- a/codegen/htq/src/error.rs +++ b/codegen/htq/src/error.rs @@ -117,6 +117,21 @@ pub enum CodegenError { #[error("transition must be in parser context\n{0:#?}")] TransitionOutsideParser(Transition), + + #[error("call does not have enough arguments\n{0:#?}")] + NotEnoughArgs(Lvalue), + + #[error("expected expression got\n{0:#?}")] + ExpectedLvalue(Expression), + + #[error("header declaration not found\n{0:#?}")] + HeaderDeclNotFound(Lvalue), + + #[error("expected header type for lvalue\n{0:#?}")] + ExpectedHeaderType(Lvalue), + + #[error("header definition for type {0} not found for lvalue\n{1:#?}")] + HeaderDefnNotFound(String, Lvalue), } #[derive(Error, Debug)] diff --git a/codegen/htq/src/expression.rs b/codegen/htq/src/expression.rs index 1127c78..dcc9837 100644 --- a/codegen/htq/src/expression.rs +++ b/codegen/htq/src/expression.rs @@ -86,7 +86,7 @@ pub(crate) fn emit_expression( } ExpressionKind::Call(call) => match context { P4Context::Control(c) => { - let (stmts, blks, value) = emit_call( + let (stmts, blks, value) = emit_call_in_control( call, c, hlir, @@ -191,7 +191,44 @@ pub(crate) fn emit_binary_expr_eq( todo!() } -pub(crate) fn emit_call( +pub(crate) fn emit_call_in_parser( + call: &p4::ast::Call, + parser: &p4::ast::Parser, + hlir: &Hlir, + ast: &p4::ast::AST, + ra: &mut RegisterAllocator, + afa: &mut AsyncFlagAllocator, + names: &HashMap, + table_context: &mut TableContext, +) -> Result< + ( + Vec, + Vec, + Option, + ), + CodegenError, +> { + match call.lval.leaf() { + "extract" => { + let (stmts, result) = emit_extract_call( + call, + parser, + hlir, + ast, + ra, + afa, + names, + table_context, + )?; + Ok((stmts, Vec::default(), result)) + } + x => { + todo!("unhandled parser function: {x:#?}"); + } + } +} + +pub(crate) fn emit_call_in_control( call: &p4::ast::Call, control: &p4::ast::Control, hlir: &Hlir, @@ -254,6 +291,88 @@ pub(crate) fn emit_call( } } +fn emit_extract_call( + call: &p4::ast::Call, + parser: &p4::ast::Parser, + _hlir: &Hlir, + ast: &p4::ast::AST, + ra: &mut RegisterAllocator, + _afa: &mut AsyncFlagAllocator, + names: &HashMap, + _table_context: &mut TableContext, +) -> Result<(Vec, Option), CodegenError> { + let src = &parser.parameters[1].name; + let source = ra.get(src).ok_or(CodegenError::NoRegisterForParameter( + src.to_owned(), + ra.clone(), + ))?; + + let tgt = call + .args + .first() + .ok_or(CodegenError::NotEnoughArgs(call.lval.clone()))?; + let tgt = match &tgt.kind { + ExpressionKind::Lvalue(lval) => lval, + _ => return Err(CodegenError::ExpectedLvalue(tgt.as_ref().clone())), + }; + let target = ra + .get(tgt.root()) + .ok_or(CodegenError::RegisterDoesNotExistForLval(tgt.clone()))?; + let output = ra.alloc(&tgt.root()); + + let info = names + .get(tgt.root()) + .ok_or(CodegenError::HeaderDeclNotFound(tgt.clone()))?; + + let typename = match &info.ty { + p4::ast::Type::UserDefined(name, _) => name.clone(), + _ => return Err(CodegenError::ExpectedHeaderType(tgt.clone())), + }; + + let offset = if let Some(hdr) = ast.get_header(&typename) { + hdr.index_of(tgt.leaf()) + .ok_or(CodegenError::MemberOffsetNotFound(tgt.clone()))? + } else { + let st = ast.get_struct(&typename).ok_or( + CodegenError::HeaderDefnNotFound(typename.clone(), tgt.clone()), + )?; + st.index_of(tgt.leaf()) + .ok_or(CodegenError::MemberOffsetNotFound(tgt.clone()))? + }; + + let sz = type_size(&info.ty, ast); + + let offset_reg = + ra.get("offset") + .ok_or(CodegenError::NoRegisterForParameter( + String::from("offset"), + ra.clone(), + ))?; + + let extract_stmt = htq::ast::Extract { + output, + target, + target_offset: Value::number(offset as i128), + source, + source_offset: Value::reg(offset_reg.clone()), //TODO + }; + + let add_offset_stmt = htq::ast::Add { + target: ra.alloc(&offset_reg.0), + typ: Type::Unsigned(32), + source_a: Value::reg(offset_reg), + source_b: Value::number(sz as i128), + }; + + Ok(( + vec![ + Statement::Extract(extract_stmt), + Statement::Add(add_offset_stmt), + ], + None, + )) +} + fn emit_apply_call( call: &p4::ast::Call, control: &p4::ast::Control, diff --git a/codegen/htq/src/parser.rs b/codegen/htq/src/parser.rs index 48f4c05..20d2bee 100644 --- a/codegen/htq/src/parser.rs +++ b/codegen/htq/src/parser.rs @@ -50,6 +50,11 @@ fn emit_parser( parameters.push(p); } + parameters.push(htq::ast::Parameter { + reg: ra.alloc("offset"), + typ: htq::ast::Type::Unsigned(32), + }); + let mut names = parser.names(); // TODO XXX parsers cannot have tables, this indicates broken abstractions @@ -57,6 +62,7 @@ fn emit_parser( let mut table_context = HashMap::default(); for state in &parser.states { + let mut ra = ra.clone(); // keeps track of register revisions for locals let mut statements = Vec::default(); let mut blocks = Vec::default(); diff --git a/codegen/htq/src/statement.rs b/codegen/htq/src/statement.rs index 826b864..57efd0b 100644 --- a/codegen/htq/src/statement.rs +++ b/codegen/htq/src/statement.rs @@ -130,6 +130,12 @@ fn emit_transition( CodegenError::NoRegisterForParameter(x.name.clone(), ra.clone()), )?)); } + args.push(Value::reg(ra.get("offset").ok_or( + CodegenError::NoRegisterForParameter( + String::from("offset"), + ra.clone(), + ), + )?)); let hdr = targets[0].clone(); @@ -397,7 +403,7 @@ fn emit_call( CodegenError, > { let (instrs, blocks, _result) = match &context { - P4Context::Control(c) => crate::expression::emit_call( + P4Context::Control(c) => crate::expression::emit_call_in_control( call, c, hlir, @@ -408,10 +414,16 @@ fn emit_call( names, table_context, )?, - P4Context::Parser(_) => { - //TODO - (Vec::default(), Vec::default(), None) - } + P4Context::Parser(p) => crate::expression::emit_call_in_parser( + call, + p, + hlir, + ast, + ra, + afa, + names, + table_context, + )?, }; Ok((instrs, blocks)) }