From b76979f4b7136ce9673f8dff75463d7b0e64c135 Mon Sep 17 00:00:00 2001 From: Ryan Goodfellow Date: Mon, 9 Sep 2024 23:27:42 -0700 Subject: [PATCH 01/23] basic async/await support in front end --- codegen/rust/src/lib.rs | 2 + codegen/rust/src/pipeline.rs | 3 + p4/src/ast.rs | 26 ++++++++- p4/src/check.rs | 17 +++++- p4/src/hlir.rs | 12 +++- p4/src/lexer.rs | 18 ++++++ p4/src/parser.rs | 104 +++++++++++++++++++++++++++++++---- p4/src/util.rs | 1 + 8 files changed, 166 insertions(+), 17 deletions(-) diff --git a/codegen/rust/src/lib.rs b/codegen/rust/src/lib.rs index a41ba0e7..b1cb5ca5 100644 --- a/codegen/rust/src/lib.rs +++ b/codegen/rust/src/lib.rs @@ -241,6 +241,7 @@ fn rust_type(ty: &Type) -> TokenStream { Type::Action => { todo!("rust type for action"); } + Type::Sync(_) => todo!("rust codegen for sync"), } } @@ -293,6 +294,7 @@ fn type_size(ty: &Type, ast: &AST) -> usize { Type::Action => { todo!("type size for action"); } + Type::Sync(_) => todo!("type size for sync"), } } diff --git a/codegen/rust/src/pipeline.rs b/codegen/rust/src/pipeline.rs index 326f1ee7..2197d922 100644 --- a/codegen/rust/src/pipeline.rs +++ b/codegen/rust/src/pipeline.rs @@ -825,6 +825,9 @@ impl<'a> PipelineGenerator<'a> { Type::List(_) => { todo!(); } + Type::Sync(_) => { + todo!(); + } } } let mut control_params = Vec::new(); diff --git a/p4/src/ast.rs b/p4/src/ast.rs index 19db75f5..22d29dd6 100644 --- a/p4/src/ast.rs +++ b/p4/src/ast.rs @@ -308,6 +308,7 @@ pub enum Type { State, Action, HeaderMethod, + Sync(String), } impl Type { @@ -371,6 +372,7 @@ impl fmt::Display for Type { } write!(f, ">") } + Type::Sync(param) => write!(f, "sync<{param}>"), } } } @@ -1376,10 +1378,11 @@ pub struct Table { pub const_entries: Vec, pub size: usize, pub token: Token, + pub is_async: bool, } impl Table { - pub fn new(name: String, token: Token) -> Self { + pub fn new(name: String, is_async: bool, token: Token) -> Self { Self { name, actions: Vec::new(), @@ -1387,6 +1390,7 @@ impl Table { key: Vec::new(), const_entries: Vec::new(), size: 0, + is_async, token, } } @@ -1886,6 +1890,7 @@ impl ElseIfBlock { pub struct Call { pub lval: Lvalue, pub args: Vec>, + pub with_await: bool, } impl Call { @@ -1970,6 +1975,25 @@ impl Lvalue { }, } } + pub fn pop_await(&self) -> Self { + let parts = self.parts(); + Lvalue { + name: if parts.len() == 1 { + parts[0].to_owned() + } else if parts.last() == Some(&"await") { + parts[..parts.len() - 1].join(".") + } else { + parts.join(".") + }, + token: Token { + kind: self.token.kind.clone(), + line: self.token.line, + col: self.token.col, + file: self.token.file.clone(), + }, + } + } + fn accept(&self, v: &V) { v.lvalue(self); } diff --git a/p4/src/check.rs b/p4/src/check.rs index 443ff06f..1b88e4e5 100644 --- a/p4/src/check.rs +++ b/p4/src/check.rs @@ -218,7 +218,7 @@ fn check_statement_block( for s in &block.statements { match s { Statement::Assignment(lval, xpr) => { - let name_info = match hlir.lvalue_decls.get(lval) { + let name_info = match hlir.lvalue_decls.get(&lval.pop_await()) { Some(info) => info, None => { diags.push(Diagnostic { @@ -261,7 +261,7 @@ fn check_statement_block( Statement::Empty => {} Statement::Call(c) if in_action => { let lval = c.lval.pop_right(); - let name_info = match hlir.lvalue_decls.get(&lval) { + let name_info = match hlir.lvalue_decls.get(&lval.pop_await()) { Some(info) => info, None => { diags.push(Diagnostic { @@ -948,6 +948,19 @@ fn check_lvalue( }); } } + Type::Sync(inner_type) => { + if parts.len() > 1 && parts[1] != "await" { + diags.push(Diagnostic { + level: Level::Error, + message: format!( + "type {} does not have a member {}", + format!("sync<{}>", inner_type).bright_blue(), + parts[1].bright_blue(), + ), + token: lval.token.clone(), + }); + } + } }; diags } diff --git a/p4/src/hlir.rs b/p4/src/hlir.rs index 979cefd9..2ccebd2c 100644 --- a/p4/src/hlir.rs +++ b/p4/src/hlir.rs @@ -367,6 +367,14 @@ impl<'a> HlirGenerator<'a> { }); None } + Type::Sync(inner) => { + self.diags.push(Diagnostic { + level: Level::Error, + message: format!("cannot index a sync<{inner}>"), + token: lval.token.clone(), + }); + None + } } } @@ -441,11 +449,11 @@ impl<'a> HlirGenerator<'a> { lval: &Lvalue, names: &mut HashMap, ) -> Option { - match resolve_lvalue(lval, self.ast, names) { + match resolve_lvalue(&lval.pop_await(), self.ast, names) { Ok(name_info) => { self.hlir .lvalue_decls - .insert(lval.clone(), name_info.clone()); + .insert(lval.pop_await(), name_info.clone()); Some(name_info.ty) } Err(e) => { diff --git a/p4/src/lexer.rs b/p4/src/lexer.rs index 02679d41..fe8dd886 100644 --- a/p4/src/lexer.rs +++ b/p4/src/lexer.rs @@ -38,6 +38,8 @@ pub enum Kind { If, Else, Return, + Async, + Await, // // types @@ -48,6 +50,7 @@ pub enum Kind { Varbit, Int, String, + Sync, // // lexical elements @@ -166,6 +169,8 @@ impl fmt::Display for Kind { Kind::If => write!(f, "keyword if"), Kind::Else => write!(f, "keyword else"), Kind::Return => write!(f, "keyword return"), + Kind::Async => write!(f, "keyword async"), + Kind::Await => write!(f, "keyword await"), // // types @@ -176,6 +181,7 @@ impl fmt::Display for Kind { Kind::Varbit => write!(f, "type varbit"), Kind::Int => write!(f, "type int"), Kind::String => write!(f, "type string"), + Kind::Sync => write!(f, "type sync"), // // lexical elements @@ -389,6 +395,14 @@ impl<'a> Lexer<'a> { return Ok(t); } + if let Some(t) = self.match_token("async", Kind::Async) { + return Ok(t); + } + + if let Some(t) = self.match_token("await", Kind::Await) { + return Ok(t); + } + if let Some(t) = self.match_token("&&", Kind::LogicalAnd) { return Ok(t); } @@ -529,6 +543,10 @@ impl<'a> Lexer<'a> { return Ok(t); } + if let Some(t) = self.match_token("sync", Kind::Sync) { + return Ok(t); + } + if let Some(t) = self.match_token("typedef", Kind::Typedef) { return Ok(t); } diff --git a/p4/src/parser.rs b/p4/src/parser.rs index 61cfd5a2..f92d54da 100644 --- a/p4/src/parser.rs +++ b/p4/src/parser.rs @@ -74,6 +74,23 @@ impl<'a> Parser<'a> { Ok(()) } + fn try_token(&mut self, expected: lexer::Kind) -> Result<(), Error> { + let token = self.next_token()?; + if token.kind != expected { + self.backlog.push(token.clone()); + return Err(ParserError { + at: token.clone(), + message: format!( + "Found {} expected '{}'.", + token.kind, expected, + ), + source: self.lexer.lines[token.line].into(), + } + .into()); + } + Ok(()) + } + fn parse_identifier( &mut self, what: &str, @@ -82,6 +99,10 @@ impl<'a> Parser<'a> { Ok(( match token.kind { Kind::Identifier(ref name) => name.into(), + Kind::Await => { + //TODO this is a bit of a hack + "await".into() + } Kind::Apply => { // sometimes apply is not the keyword but a method called // against tables. @@ -150,7 +171,21 @@ impl<'a> Parser<'a> { lexer::Kind::Identifier(name) => { Type::UserDefined(name.clone()) } - + lexer::Kind::Sync => { + let params = self.parse_type_parameters()?; + if params.len() != 1 { + return Err(ParserError { + at: token.clone(), + message: format!( + "Expected exactly one type parameter, found {}", + params.len(), + ), + source: self.lexer.lines[token.line].into(), + } + .into()); + } + Type::Sync(params[0].clone()) + } _ => { return Err( ParserError { @@ -390,6 +425,12 @@ impl<'a> Parser<'a> { } } + pub fn parse_await(&mut self) -> Result<(), Error> { + self.try_token(lexer::Kind::Dot)?; + self.try_token(lexer::Kind::Await)?; + Ok(()) + } + // parse a tuple of expressions (, ...), used for both tuples // and function call sites pub fn parse_expr_parameters( @@ -474,6 +515,7 @@ impl<'a> Parser<'a> { | lexer::Kind::Error | lexer::Kind::Bit | lexer::Kind::Int + | lexer::Kind::Sync | lexer::Kind::String => { self.backlog.push(token); let var = self.parse_variable()?; @@ -487,9 +529,24 @@ impl<'a> Parser<'a> { result.statements.push(Statement::Constant(c)); } - lexer::Kind::Identifier(_) - | lexer::Kind::If - | lexer::Kind::Return => { + lexer::Kind::Identifier(_) => { + if let Ok(peek) = self.next_token() { + if let lexer::Kind::Identifier(_) = &peek.kind { + self.backlog.push(peek); + self.backlog.push(token); + let var = self.parse_variable()?; + result.statements.push(Statement::Variable(var)); + continue; + } else { + self.backlog.push(peek); + } + } + self.backlog.push(token); + let mut sp = StatementParser::new(self); + let stmt = sp.run()?; + result.statements.push(stmt); + } + lexer::Kind::If | lexer::Kind::Return => { // push the identifier token into the backlog and run the // statement parser self.backlog.push(token); @@ -1023,12 +1080,20 @@ impl<'a, 'b> ControlParser<'a, 'b> { // iterate over body statements loop { + let token = self.parser.next_token()?; + let is_async = if token.kind == lexer::Kind::Async { + true + } else { + self.parser.backlog.push(token); + false + }; + let token = self.parser.next_token()?; match token.kind { lexer::Kind::CurlyClose => break, lexer::Kind::Action => self.parse_action(control)?, - lexer::Kind::Table => self.parse_table(control)?, + lexer::Kind::Table => self.parse_table(control, is_async)?, lexer::Kind::Apply => self.parse_apply(control)?, lexer::Kind::Const => { let c = self.parser.parse_constant()?; @@ -1069,8 +1134,12 @@ impl<'a, 'b> ControlParser<'a, 'b> { Ok(()) } - pub fn parse_table(&mut self, control: &mut Control) -> Result<(), Error> { - let mut tp = TableParser::new(self.parser); + pub fn parse_table( + &mut self, + control: &mut Control, + is_async: bool, + ) -> Result<(), Error> { + let mut tp = TableParser::new(self.parser, is_async); let table = tp.run()?; control.tables.push(table); @@ -1166,16 +1235,17 @@ impl<'a, 'b> ActionParser<'a, 'b> { pub struct TableParser<'a, 'b> { parser: &'b mut Parser<'a>, + is_async: bool, } impl<'a, 'b> TableParser<'a, 'b> { - pub fn new(parser: &'b mut Parser<'a>) -> Self { - Self { parser } + pub fn new(parser: &'b mut Parser<'a>, is_async: bool) -> Self { + Self { parser, is_async } } pub fn run(&mut self) -> Result { let (name, tk) = self.parser.parse_identifier("table name")?; - let mut table = Table::new(name, tk); + let mut table = Table::new(name, self.is_async, tk); self.parse_body(&mut table)?; @@ -1487,7 +1557,12 @@ impl<'a, 'b> StatementParser<'a, 'b> { pub fn parse_call(&mut self, lval: Lvalue) -> Result { let args = self.parser.parse_expr_parameters()?; - Ok(Statement::Call(Call { lval, args })) + let with_await = self.parser.parse_await().is_ok(); + Ok(Statement::Call(Call { + lval, + args, + with_await, + })) } pub fn parse_parameterized_call( @@ -1619,9 +1694,14 @@ impl<'a, 'b> ExpressionParser<'a, 'b> { else if token.kind == lexer::Kind::ParenOpen { self.parser.backlog.push(token.clone()); let args = self.parser.parse_expr_parameters()?; + let with_await = self.parser.parse_await().is_ok(); Expression::new( token, - ExpressionKind::Call(Call { lval, args }), + ExpressionKind::Call(Call { + lval, + args, + with_await, + }), ) } // if it's not an index and it's not a call, it's an lvalue diff --git a/p4/src/util.rs b/p4/src/util.rs index 769e8105..13a830bb 100644 --- a/p4/src/util.rs +++ b/p4/src/util.rs @@ -26,6 +26,7 @@ pub fn resolve_lvalue( Type::List(_) => root.clone(), Type::State => root.clone(), Type::Action => root.clone(), + Type::Sync(_) => root.clone(), Type::UserDefined(name) => { if lval.degree() == 1 { root.clone() From c4563ea87c0c173887cbb0d5e8c16fba21dff86a Mon Sep 17 00:00:00 2001 From: Ryan Goodfellow Date: Tue, 10 Sep 2024 17:22:03 -0700 Subject: [PATCH 02/23] front end working for basic async programs --- codegen/rust/src/control.rs | 12 ++++----- codegen/rust/src/lib.rs | 7 +++--- codegen/rust/src/p4struct.rs | 3 ++- codegen/rust/src/pipeline.rs | 11 +++++--- codegen/rust/src/statement.rs | 6 ++--- p4/src/ast.rs | 37 +++++++++++++++++++-------- p4/src/check.rs | 47 +++++++++++++++++++++++------------ p4/src/hlir.rs | 4 +-- p4/src/parser.rs | 20 ++++++++++----- p4/src/util.rs | 2 +- 10 files changed, 96 insertions(+), 53 deletions(-) diff --git a/codegen/rust/src/control.rs b/codegen/rust/src/control.rs index 6c1bbb36..76f4d73a 100644 --- a/codegen/rust/src/control.rs +++ b/codegen/rust/src/control.rs @@ -52,7 +52,7 @@ impl<'a> ControlGenerator<'a> { let control = cs.last().unwrap().1; let (_, mut param_types) = self.control_parameters(control); for var in &control.variables { - if let Type::UserDefined(typename) = &var.ty { + if let Type::UserDefined(typename, _) = &var.ty { if self.ast.get_extern(typename).is_some() { let extern_type = format_ident!("{}", typename); param_types.push(quote! { @@ -95,7 +95,7 @@ impl<'a> ControlGenerator<'a> { let qtn = qualified_table_function_name(None, &cs, table); let (_, mut param_types) = self.control_parameters(c); for var in &c.variables { - if let Type::UserDefined(typename) = &var.ty { + if let Type::UserDefined(typename, _) = &var.ty { if self.ast.get_extern(typename).is_some() { let extern_type = format_ident!("{}", typename); param_types.push(quote! { @@ -143,7 +143,7 @@ impl<'a> ControlGenerator<'a> { for arg in &control.parameters { match arg.ty { - Type::UserDefined(ref typename) => { + Type::UserDefined(ref typename, _) => { match self.ast.get_user_defined_type(typename) { Some(_udt) => { let name = format_ident!("{}", arg.name); @@ -202,7 +202,7 @@ impl<'a> ControlGenerator<'a> { let (mut params, _) = self.control_parameters(control); for var in &control.variables { - if let Type::UserDefined(typename) = &var.ty { + if let Type::UserDefined(typename, _) = &var.ty { if self.ast.get_extern(typename).is_some() { let name = format_ident!("{}", var.name); let extern_type = format_ident!("{}", typename); @@ -235,7 +235,7 @@ impl<'a> ControlGenerator<'a> { for arg in &action.parameters { // if the type is user defined, check to ensure it's defined - if let Type::UserDefined(ref typename) = arg.ty { + if let Type::UserDefined(ref typename, _) = arg.ty { match self.ast.get_user_defined_type(typename) { Some(_) => { let name = format_ident!("{}", arg.name); @@ -490,7 +490,7 @@ impl<'a> ControlGenerator<'a> { for var in &control.variables { //TODO check in checker that externs are actually defined by //SoftNPU. - if let Type::UserDefined(typename) = &var.ty { + if let Type::UserDefined(typename, _) = &var.ty { if self.ast.get_extern(typename).is_some() { let name = format_ident!("{}", var.name); let extern_type = format_ident!("{}", typename); diff --git a/codegen/rust/src/lib.rs b/codegen/rust/src/lib.rs index b1cb5ca5..3945cee9 100644 --- a/codegen/rust/src/lib.rs +++ b/codegen/rust/src/lib.rs @@ -218,7 +218,8 @@ fn rust_type(ty: &Type) -> TokenStream { Type::Int(_size) => todo!("generate int type"), Type::Varbit(_size) => todo!("generate varbit type"), Type::String => quote! { String }, - Type::UserDefined(name) => { + //TODO generic types + Type::UserDefined(name, _) => { let typename = format_ident!("{}", name); quote! { #typename } } @@ -253,7 +254,7 @@ fn type_size(ty: &Type, ast: &AST) -> usize { Type::Int(size) => *size, Type::Varbit(size) => *size, Type::String => todo!("generate string size"), - Type::UserDefined(name) => { + 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) @@ -334,7 +335,7 @@ fn is_header( ) -> bool { //TODO: get from hlir? let typename = match resolve_lvalue(lval, ast, names).unwrap().ty { - Type::UserDefined(name) => name, + Type::UserDefined(name, _) => name, _ => return false, }; ast.get_header(&typename).is_some() diff --git a/codegen/rust/src/p4struct.rs b/codegen/rust/src/p4struct.rs index 83807e96..1d4474b2 100644 --- a/codegen/rust/src/p4struct.rs +++ b/codegen/rust/src/p4struct.rs @@ -32,7 +32,8 @@ impl<'a> StructGenerator<'a> { let name = format_ident!("{}", member.name); let name_s = &member.name; match &member.ty { - Type::UserDefined(ref typename) => { + //TODO generics + Type::UserDefined(ref typename, _) => { if self.ast.get_header(typename).is_some() { let ty = format_ident!("{}", typename); diff --git a/codegen/rust/src/pipeline.rs b/codegen/rust/src/pipeline.rs index 2197d922..a11884f0 100644 --- a/codegen/rust/src/pipeline.rs +++ b/codegen/rust/src/pipeline.rs @@ -486,7 +486,8 @@ impl<'a> PipelineGenerator<'a> { let (_, mut param_types) = cg.control_parameters(table_control); for var in &table_control.variables { - if let Type::UserDefined(typename) = &var.ty { + //TODO(generics) + if let Type::UserDefined(typename, _) = &var.ty { if self.ast.get_extern(typename).is_some() { let extern_type = format_ident!("{}", typename); param_types.push(quote! { @@ -807,7 +808,7 @@ impl<'a> PipelineGenerator<'a> { Type::String => { todo!(); } - Type::UserDefined(_s) => { + Type::UserDefined(_s, _) => { todo!(); } Type::ExternFunction => { @@ -861,7 +862,8 @@ impl<'a> PipelineGenerator<'a> { for var in &control.variables { let name = format_ident!("{}", var.name); - if let Type::UserDefined(typename) = &var.ty { + //TODO(generics) + if let Type::UserDefined(typename, _) = &var.ty { if self.ast.get_extern(typename).is_some() { control_params.push(quote! { #name }); let extern_type = format_ident!("{}", typename); @@ -967,7 +969,8 @@ impl<'a> PipelineGenerator<'a> { for var in &control.variables { let name = format_ident!("{}", var.name); - if let Type::UserDefined(typename) = &var.ty { + //TODO(generics) + if let Type::UserDefined(typename, _) = &var.ty { if self.ast.get_extern(typename).is_some() { control_params.push(quote! { #name }); let extern_type = format_ident!("{}", typename); diff --git a/codegen/rust/src/statement.rs b/codegen/rust/src/statement.rs index 1537993f..5911f904 100644 --- a/codegen/rust/src/statement.rs +++ b/codegen/rust/src/statement.rs @@ -88,7 +88,7 @@ impl<'a> StatementGenerator<'a> { // TODO eww, to better to figure out precisely when to_owned // and clone are needed quote! { #rhs.to_owned().clone() } - } else if let Type::UserDefined(_) = rhs_ty { + } else if let Type::UserDefined(_, _) = rhs_ty { quote! { #rhs.clone() } } else { rhs @@ -394,7 +394,7 @@ impl<'a> StatementGenerator<'a> { }); let control_instance = match &name_info.ty { - Type::UserDefined(name) => { + Type::UserDefined(name, _) => { self.ast.get_control(name).unwrap_or_else(|| { panic!("codegen: control {} not found in AST", name,) }) @@ -526,7 +526,7 @@ impl<'a> StatementGenerator<'a> { for var in &control.variables { let name = format_ident!("{}", var.name); - if let Type::UserDefined(typename) = &var.ty { + if let Type::UserDefined(typename, _) = &var.ty { if self.ast.get_extern(typename).is_some() { action_args.push(quote! { &#name }); } diff --git a/p4/src/ast.rs b/p4/src/ast.rs index 22d29dd6..a722a95e 100644 --- a/p4/src/ast.rs +++ b/p4/src/ast.rs @@ -217,7 +217,7 @@ impl PackageInstance { #[derive(Debug)] pub struct Package { pub name: String, - pub type_parameters: Vec, + pub type_parameters: Vec, pub parameters: Vec, } @@ -262,7 +262,7 @@ impl Package { #[derive(Debug)] pub struct PackageParameter { pub type_name: String, - pub type_parameters: Vec, + pub type_parameters: Vec, pub name: String, } @@ -300,7 +300,7 @@ pub enum Type { Varbit(usize), Int(usize), String, - UserDefined(String), + UserDefined(String, Vec>), ExternFunction, //TODO actual signature Table, Void, @@ -308,7 +308,7 @@ pub enum Type { State, Action, HeaderMethod, - Sync(String), + Sync(Box), } impl Type { @@ -358,7 +358,22 @@ impl fmt::Display for Type { Type::Varbit(size) => write!(f, "varbit<{}>", size), Type::Int(size) => write!(f, "int<{}>", size), Type::String => write!(f, "string"), - Type::UserDefined(name) => write!(f, "{}", name), + Type::UserDefined(name, params) => { + if params.is_empty() { + write!(f, "{}", name) + } else { + write!( + f, + "{}<{}>", + name, + params + .iter() + .map(|x| x.to_string()) + .collect::>() + .join(",") + ) + } + } Type::ExternFunction => write!(f, "extern function"), Type::Table => write!(f, "table"), Type::Void => write!(f, "void"), @@ -888,7 +903,7 @@ pub struct Control { pub name: String, pub variables: Vec, pub constants: Vec, - pub type_parameters: Vec, + pub type_parameters: Vec, pub parameters: Vec, pub actions: Vec, pub tables: Vec, @@ -944,7 +959,7 @@ impl Control { result.push((chain.clone(), table)); } for v in &self.variables { - if let Type::UserDefined(typename) = &v.ty { + if let Type::UserDefined(typename, _) = &v.ty { if let Some(control_inst) = ast.get_control(typename) { result.extend_from_slice(&control_inst.tables_rec( ast, @@ -959,7 +974,7 @@ impl Control { pub fn is_type_parameter(&self, name: &str) -> bool { for t in &self.type_parameters { - if t == name { + if t.to_string() == name { return true; } } @@ -1114,7 +1129,7 @@ impl PartialEq for Control { #[derive(Debug, Clone)] pub struct Parser { pub name: String, - pub type_parameters: Vec, + pub type_parameters: Vec, pub parameters: Vec, pub states: Vec, pub decl_only: bool, @@ -1137,7 +1152,7 @@ impl Parser { pub fn is_type_parameter(&self, name: &str) -> bool { for t in &self.type_parameters { - if t == name { + if t.to_string() == name { return true; } } @@ -2239,7 +2254,7 @@ impl Extern { pub struct ExternMethod { pub return_type: Type, pub name: String, - pub type_parameters: Vec, + pub type_parameters: Vec, pub parameters: Vec, } diff --git a/p4/src/check.rs b/p4/src/check.rs index 1b88e4e5..8b2ae431 100644 --- a/p4/src/check.rs +++ b/p4/src/check.rs @@ -5,7 +5,7 @@ use std::collections::HashMap; use crate::ast::{ Call, Control, DeclarationInfo, Expression, ExpressionKind, Header, Lvalue, NameInfo, Parser, State, Statement, StatementBlock, Struct, Table, - Transition, Type, VisitorMut, AST, + Transition, Type, Variable, VisitorMut, AST, }; use crate::hlir::{Hlir, HlirGenerator}; use crate::lexer::Token; @@ -96,7 +96,7 @@ impl ControlChecker { pub fn check_params(c: &Control, ast: &AST, diags: &mut Diagnostics) { for p in &c.parameters { - if let Type::UserDefined(typename) = &p.ty { + if let Type::UserDefined(typename, _) = &p.ty { if ast.get_user_defined_type(typename).is_none() { diags.push(Diagnostic { level: Level::Error, @@ -140,7 +140,7 @@ impl ControlChecker { pub fn check_variables(c: &Control, ast: &AST, diags: &mut Diagnostics) { for v in &c.variables { - if let Type::UserDefined(typename) = &v.ty { + if let Type::UserDefined(typename, _) = &v.ty { if ast.get_user_defined_type(typename).is_some() { continue; } @@ -203,6 +203,7 @@ impl ControlChecker { ast, hlir, diags, + local_values: HashMap::new(), }; c.accept_mut(&mut apc); } @@ -285,7 +286,7 @@ fn check_statement_block( token: c.lval.token.clone(), }); } - Type::UserDefined(name) => { + Type::UserDefined(name, _) => { if ast.get_control(name).is_some() { diags.push(Diagnostic { level: Level::Error, @@ -311,26 +312,40 @@ pub struct ApplyCallChecker<'a> { ast: &'a AST, hlir: &'a Hlir, diags: &'a mut Diagnostics, + local_values: HashMap, } impl VisitorMut for ApplyCallChecker<'_> { + fn variable(&mut self, variable: &Variable) { + self.local_values.insert( + variable.name.clone(), + NameInfo { + ty: variable.ty.clone(), + decl: DeclarationInfo::Local, + }, + ); + } + fn call(&mut self, call: &Call) { let name = call.lval.root(); let names = self.c.names(); let name_info = match names.get(name) { Some(info) => info, - None => { - self.diags.push(Diagnostic { - level: Level::Error, - message: format!("{} is undefined", name), - token: call.lval.token.clone(), - }); - return; - } + None => match self.local_values.get(name) { + Some(info) => info, + None => { + self.diags.push(Diagnostic { + level: Level::Error, + message: format!("{} is undefined", name), + token: call.lval.token.clone(), + }); + return; + } + }, }; match &name_info.ty { - Type::UserDefined(name) => { + Type::UserDefined(name, _) => { if let Some(ctl) = self.ast.get_control(name) { self.check_apply_ctl_apply(call, ctl) } @@ -484,7 +499,7 @@ impl StructChecker { pub fn check(s: &Struct, ast: &AST) -> Diagnostics { let mut diags = Diagnostics::new(); for m in &s.members { - if let Type::UserDefined(typename) = &m.ty { + if let Type::UserDefined(typename, _) = &m.ty { if ast.get_user_defined_type(typename).is_none() { diags.push(Diagnostic { level: Level::Error, @@ -507,7 +522,7 @@ impl HeaderChecker { pub fn check(h: &Header, ast: &AST) -> Diagnostics { let mut diags = Diagnostics::new(); for m in &h.members { - if let Type::UserDefined(typename) = &m.ty { + if let Type::UserDefined(typename, _) = &m.ty { if ast.get_user_defined_type(typename).is_none() { diags.push(Diagnostic { level: Level::Error, @@ -868,7 +883,7 @@ fn check_lvalue( }); } } - Type::UserDefined(name) => { + Type::UserDefined(name, _) => { // get the parent type definition from the AST and check for the // referenced member if let Some(parent) = ast.get_struct(&name) { diff --git a/p4/src/hlir.rs b/p4/src/hlir.rs index 2ccebd2c..4b18cc10 100644 --- a/p4/src/hlir.rs +++ b/p4/src/hlir.rs @@ -195,7 +195,7 @@ impl<'a> HlirGenerator<'a> { } // TODO check extern methods in checker before getting here if let Some(name_info) = names.get(call.lval.root()) { - if let Type::UserDefined(typename) = &name_info.ty { + if let Type::UserDefined(typename, _) = &name_info.ty { if let Some(ext) = self.ast.get_extern(typename) { if let Some(m) = ext.get_method(call.lval.leaf()) { self.hlir @@ -335,7 +335,7 @@ impl<'a> HlirGenerator<'a> { }); None } - Type::UserDefined(_) => { + Type::UserDefined(_, _) => { self.diags.push(Diagnostic { level: Level::Error, message: "cannot index a user defined type".into(), diff --git a/p4/src/parser.rs b/p4/src/parser.rs index f92d54da..d9884e3b 100644 --- a/p4/src/parser.rs +++ b/p4/src/parser.rs @@ -169,7 +169,14 @@ impl<'a> Parser<'a> { } lexer::Kind::Identifier(name) => { - Type::UserDefined(name.clone()) + let params = match self.parse_type_parameters() { + Ok(params) => params, + Err(_) => Vec::new(), + }; + Type::UserDefined( + name.clone(), + params.into_iter().map(Box::new).collect(), + ) } lexer::Kind::Sync => { let params = self.parse_type_parameters()?; @@ -184,7 +191,7 @@ impl<'a> Parser<'a> { } .into()); } - Type::Sync(params[0].clone()) + Type::Sync(Box::new(params[0].clone())) } _ => { return Err( @@ -655,14 +662,15 @@ impl<'a> Parser<'a> { Ok(result) } - pub fn parse_type_parameters(&mut self) -> Result, Error> { + pub fn parse_type_parameters(&mut self) -> Result, Error> { let mut result = Vec::new(); - self.expect_token(lexer::Kind::AngleOpen)?; + self.try_token(lexer::Kind::AngleOpen)?; loop { - let (ident, _) = self.parse_identifier("type parameter name")?; - result.push(ident); + //let (ident, _) = self.parse_identifier("type parameter name")?; + let (ty, _) = self.parse_type()?; + result.push(ty); let token = self.next_token()?; match token.kind { diff --git a/p4/src/util.rs b/p4/src/util.rs index 13a830bb..388b05b2 100644 --- a/p4/src/util.rs +++ b/p4/src/util.rs @@ -27,7 +27,7 @@ pub fn resolve_lvalue( Type::State => root.clone(), Type::Action => root.clone(), Type::Sync(_) => root.clone(), - Type::UserDefined(name) => { + Type::UserDefined(name, _) => { if lval.degree() == 1 { root.clone() } else if let Some(parent) = ast.get_struct(name) { From 125b758206d0f7be4e66c7b31e89c833275ba679 Mon Sep 17 00:00:00 2001 From: Ryan Goodfellow Date: Wed, 11 Sep 2024 21:02:28 -0700 Subject: [PATCH 03/23] start htq backend --- Cargo.toml | 2 + codegen/htq/Cargo.toml | 10 +++ codegen/htq/src/error.rs | 18 +++++ codegen/htq/src/lib.rs | 146 +++++++++++++++++++++++++++++++++++++++ p4/src/ast.rs | 1 + x4c/Cargo.toml | 1 + x4c/src/bin/x4c.rs | 6 +- x4c/src/lib.rs | 5 +- 8 files changed, 186 insertions(+), 3 deletions(-) create mode 100644 codegen/htq/Cargo.toml create mode 100644 codegen/htq/src/error.rs create mode 100644 codegen/htq/src/lib.rs diff --git a/Cargo.toml b/Cargo.toml index 61173579..34caf3aa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,7 @@ members = [ "x4c", "x4c_error_codes", "codegen/rust", + "codegen/htq", "lang/p4rs", "lang/p4-macro", "lang/p4-macro-test", @@ -17,6 +18,7 @@ members = [ [workspace.dependencies] p4-macro = { path = "lang/p4-macro" } p4-rust = { path = "codegen/rust" } +p4-htq = { path = "codegen/htq" } p4rs = { path = "lang/p4rs" } tests = { path = "test" } diff --git a/codegen/htq/Cargo.toml b/codegen/htq/Cargo.toml new file mode 100644 index 00000000..2a9db0a4 --- /dev/null +++ b/codegen/htq/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "p4-htq" +version = "0.1.0" +edition = "2021" + +[dependencies] +p4 = { path = "../../p4" } +#htq = { git = "https://github.com/oxidecomputer/htq", branch = "sector-001" } +htq = { path = "/Users/ry/src/htq" } +thiserror = "1.0.63" diff --git a/codegen/htq/src/error.rs b/codegen/htq/src/error.rs new file mode 100644 index 00000000..21ec8b70 --- /dev/null +++ b/codegen/htq/src/error.rs @@ -0,0 +1,18 @@ +// Copyright 2024 Oxide Computer Company + +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum CodegenError { + #[error("There is no equivalent htq type for {0}")] + NoEquivalentType(p4::ast::Type), +} + +#[derive(Error, Debug)] +pub enum EmitError { + #[error("io error {0}")] + Io(#[from] std::io::Error), + + #[error("codegen error {0}")] + Codegen(#[from] CodegenError), +} diff --git a/codegen/htq/src/lib.rs b/codegen/htq/src/lib.rs new file mode 100644 index 00000000..e5ce9e32 --- /dev/null +++ b/codegen/htq/src/lib.rs @@ -0,0 +1,146 @@ +// Copyright 2024 Oxide Computer Company + +use std::io::Write; + +use error::CodegenError; +use htq::emit::Emit; +use p4::hlir::Hlir; + +use crate::error::EmitError; + +mod error; + +pub fn emit( + ast: &p4::ast::AST, + hlir: &Hlir, + filename: &str, +) -> Result<(), EmitError> { + let headers: Vec<_> = + ast.headers + .iter() + .map(p4_header_to_htq_header) + .collect::, CodegenError>>()?; + + let tables: Vec<_> = ast + .controls + .iter() + .flat_map(|c| c.tables.iter().map(|t| (c, t)).collect::>()) + .map(|(c, t)| p4_table_to_htq_table(c, t, hlir)) + .collect::, CodegenError>>()?; + + let mut f = std::fs::File::create(filename)?; + + for h in &headers { + writeln!(f, "{}", h.emit())?; + } + + for t in &tables { + writeln!(f, "{}", t.emit())?; + } + + Ok(()) +} + +fn p4_header_to_htq_header( + p4h: &p4::ast::Header, +) -> Result { + Ok(htq::ast::Header { + name: p4h.name.clone(), + fields: p4h + .members + .iter() + .map(p4_header_member_to_htq_type) + .collect::, CodegenError>>()?, + }) +} + +fn p4_header_member_to_htq_type( + p4f: &p4::ast::HeaderMember, +) -> Result { + p4_type_to_htq_type(&p4f.ty) +} + +fn p4_type_to_htq_type( + p4ty: &p4::ast::Type, +) -> Result { + Ok(match p4ty { + p4::ast::Type::Bool => htq::ast::Type::Bool, + p4::ast::Type::Bit(n) => htq::ast::Type::Bitfield(*n), + p4::ast::Type::Varbit(n) => htq::ast::Type::Bitfield(*n), + p4::ast::Type::Int(n) => htq::ast::Type::Unsigned(*n), + p4::ast::Type::UserDefined(name, _) => { + htq::ast::Type::User(name.clone()) + } + t @ p4::ast::Type::Table => { + return Err(CodegenError::NoEquivalentType(t.clone())) + } + t @ p4::ast::Type::Error => { + return Err(CodegenError::NoEquivalentType(t.clone())) + } + t @ p4::ast::Type::Void => { + return Err(CodegenError::NoEquivalentType(t.clone())) + } + t @ p4::ast::Type::State => { + return Err(CodegenError::NoEquivalentType(t.clone())) + } + t @ p4::ast::Type::Action => { + return Err(CodegenError::NoEquivalentType(t.clone())) + } + t @ p4::ast::Type::ExternFunction => { + return Err(CodegenError::NoEquivalentType(t.clone())) + } + 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"), + }) +} + +fn p4_match_kind_to_htq_match_kind( + p4m: &p4::ast::MatchKind, +) -> htq::ast::LookupType { + match p4m { + p4::ast::MatchKind::Exact => htq::ast::LookupType::Exact, + p4::ast::MatchKind::LongestPrefixMatch => htq::ast::LookupType::Lpm, + p4::ast::MatchKind::Range => htq::ast::LookupType::Range, + p4::ast::MatchKind::Ternary => htq::ast::LookupType::Ternary, + } +} + +fn p4_table_to_htq_table( + p4c: &p4::ast::Control, + p4t: &p4::ast::Table, + hlir: &Hlir, +) -> Result { + let mut action_args = Vec::new(); + for a in &p4t.actions { + let act = p4c.get_action(&a.name).unwrap(); + action_args.push( + act.parameters + .iter() + .map(|x| p4_type_to_htq_type(&x.ty)) + .collect::, CodegenError>>()?, + ); + } + Ok(htq::ast::Table { + name: p4t.name.clone(), + keyset: p4t + .key + .iter() + .map(|(lval, match_kind)| htq::ast::TableKey { + typ: p4_type_to_htq_type( + &hlir.lvalue_decls.get(lval).unwrap().ty, + ) + .unwrap(), + lookup_typ: p4_match_kind_to_htq_match_kind(match_kind), + }) + .collect(), + action_args, + }) +} diff --git a/p4/src/ast.rs b/p4/src/ast.rs index a722a95e..b1b9abbb 100644 --- a/p4/src/ast.rs +++ b/p4/src/ast.rs @@ -7,6 +7,7 @@ use std::hash::{Hash, Hasher}; use crate::lexer::Token; +/// An abstract syntax tree for the P4 language. #[derive(Debug, Default)] pub struct AST { pub constants: Vec, diff --git a/x4c/Cargo.toml b/x4c/Cargo.toml index a4360427..deaf024b 100644 --- a/x4c/Cargo.toml +++ b/x4c/Cargo.toml @@ -10,3 +10,4 @@ anyhow.workspace = true regex.workspace = true p4.workspace = true p4-rust.workspace = true +p4-htq.workspace = true diff --git a/x4c/src/bin/x4c.rs b/x4c/src/bin/x4c.rs index b44db245..e107ab30 100644 --- a/x4c/src/bin/x4c.rs +++ b/x4c/src/bin/x4c.rs @@ -31,12 +31,16 @@ fn run() -> Result<()> { p4_rust::emit( &ast, &hlir, - &opts.out, + &format!("{}.rs", opts.out), p4_rust::Settings { pipeline_name: "main".to_owned(), }, )?; } + x4c::Target::Htq => { + let (hlir, _) = p4::check::all(&ast); + p4_htq::emit(&ast, &hlir, &format!("{}.htq", opts.out))?; + } x4c::Target::RedHawk => { todo!("RedHawk code generator"); } diff --git a/x4c/src/lib.rs b/x4c/src/lib.rs index 62457027..ce4212db 100644 --- a/x4c/src/lib.rs +++ b/x4c/src/lib.rs @@ -33,7 +33,7 @@ pub struct Opts { pub filename: String, /// What target to generate code for. - #[clap(value_enum, default_value_t = Target::Rust)] + #[clap(long, value_enum, default_value_t = Target::Rust)] pub target: Target, /// Just check code, do not compile. @@ -41,7 +41,7 @@ pub struct Opts { pub check: bool, /// Filename to write generated code to. - #[clap(short, long, default_value = "out.rs")] + #[clap(short, long, default_value = "out")] pub out: String, } @@ -49,6 +49,7 @@ pub struct Opts { pub enum Target { Rust, RedHawk, + Htq, Docs, } From 73d79e9d4fab33716043fa9a465ef06cef9b1c26 Mon Sep 17 00:00:00 2001 From: Ryan Goodfellow Date: Thu, 12 Sep 2024 00:01:05 -0700 Subject: [PATCH 04/23] reticulating htq codegen --- codegen/htq/src/expression.rs | 8 ++++ codegen/htq/src/header.rs | 41 ++++++++++++++++ codegen/htq/src/lib.rs | 89 ++++++++++------------------------- codegen/htq/src/parser.rs | 53 +++++++++++++++++++++ codegen/htq/src/statement.rs | 19 ++++++++ codegen/htq/src/table.rs | 48 +++++++++++++++++++ 6 files changed, 195 insertions(+), 63 deletions(-) create mode 100644 codegen/htq/src/expression.rs create mode 100644 codegen/htq/src/header.rs create mode 100644 codegen/htq/src/parser.rs create mode 100644 codegen/htq/src/statement.rs create mode 100644 codegen/htq/src/table.rs diff --git a/codegen/htq/src/expression.rs b/codegen/htq/src/expression.rs new file mode 100644 index 00000000..1237eb42 --- /dev/null +++ b/codegen/htq/src/expression.rs @@ -0,0 +1,8 @@ +// Copyright 2024 Oxide Computer Company +use crate::error::CodegenError; + +pub(crate) fn _emit_expression( + _stmt: &p4::ast::Expression, +) -> Result, CodegenError> { + todo!() +} diff --git a/codegen/htq/src/header.rs b/codegen/htq/src/header.rs new file mode 100644 index 00000000..1c5eef66 --- /dev/null +++ b/codegen/htq/src/header.rs @@ -0,0 +1,41 @@ +// Copyright 2024 Oxide Computer Company + +use crate::{error::CodegenError, p4_type_to_htq_type}; + +pub(crate) fn p4_header_to_htq_header( + p4h: &p4::ast::Header, +) -> Result { + Ok(htq::ast::Header { + name: p4h.name.clone(), + fields: p4h + .members + .iter() + .map(p4_header_member_to_htq_type) + .collect::, CodegenError>>()?, + }) +} + +pub(crate) fn p4_struct_to_htq_header( + p4s: &p4::ast::Struct, +) -> Result { + Ok(htq::ast::Header { + name: p4s.name.clone(), + fields: p4s + .members + .iter() + .map(p4_struct_member_to_htq_type) + .collect::, CodegenError>>()?, + }) +} + +pub(crate) fn p4_header_member_to_htq_type( + p4f: &p4::ast::HeaderMember, +) -> Result { + p4_type_to_htq_type(&p4f.ty) +} + +pub(crate) fn p4_struct_member_to_htq_type( + p4f: &p4::ast::StructMember, +) -> Result { + p4_type_to_htq_type(&p4f.ty) +} diff --git a/codegen/htq/src/lib.rs b/codegen/htq/src/lib.rs index e5ce9e32..6a2a08e9 100644 --- a/codegen/htq/src/lib.rs +++ b/codegen/htq/src/lib.rs @@ -3,24 +3,39 @@ use std::io::Write; use error::CodegenError; +use header::{p4_header_to_htq_header, p4_struct_to_htq_header}; use htq::emit::Emit; use p4::hlir::Hlir; +use parser::emit_parser_functions; +use table::p4_table_to_htq_table; use crate::error::EmitError; mod error; +mod expression; +mod header; +mod parser; +mod statement; +mod table; pub fn emit( ast: &p4::ast::AST, hlir: &Hlir, filename: &str, ) -> Result<(), EmitError> { - let headers: Vec<_> = + let mut headers: Vec<_> = ast.headers .iter() .map(p4_header_to_htq_header) .collect::, CodegenError>>()?; + headers.extend( + ast.structs + .iter() + .map(p4_struct_to_htq_header) + .collect::, CodegenError>>()?, + ); + let tables: Vec<_> = ast .controls .iter() @@ -28,36 +43,27 @@ pub fn emit( .map(|(c, t)| p4_table_to_htq_table(c, t, hlir)) .collect::, CodegenError>>()?; + let parser_functions: Vec<_> = emit_parser_functions(ast, hlir)?; + let mut f = std::fs::File::create(filename)?; for h in &headers { writeln!(f, "{}", h.emit())?; } + writeln!(f)?; for t in &tables { writeln!(f, "{}", t.emit())?; } + writeln!(f)?; - Ok(()) -} - -fn p4_header_to_htq_header( - p4h: &p4::ast::Header, -) -> Result { - Ok(htq::ast::Header { - name: p4h.name.clone(), - fields: p4h - .members - .iter() - .map(p4_header_member_to_htq_type) - .collect::, CodegenError>>()?, - }) -} + for func in &parser_functions { + //TODO htq function emit + writeln!(f, "{}", func.emit())?; + } + writeln!(f)?; -fn p4_header_member_to_htq_type( - p4f: &p4::ast::HeaderMember, -) -> Result { - p4_type_to_htq_type(&p4f.ty) + Ok(()) } fn p4_type_to_htq_type( @@ -101,46 +107,3 @@ fn p4_type_to_htq_type( p4::ast::Type::String => todo!("string types not yet supported"), }) } - -fn p4_match_kind_to_htq_match_kind( - p4m: &p4::ast::MatchKind, -) -> htq::ast::LookupType { - match p4m { - p4::ast::MatchKind::Exact => htq::ast::LookupType::Exact, - p4::ast::MatchKind::LongestPrefixMatch => htq::ast::LookupType::Lpm, - p4::ast::MatchKind::Range => htq::ast::LookupType::Range, - p4::ast::MatchKind::Ternary => htq::ast::LookupType::Ternary, - } -} - -fn p4_table_to_htq_table( - p4c: &p4::ast::Control, - p4t: &p4::ast::Table, - hlir: &Hlir, -) -> Result { - let mut action_args = Vec::new(); - for a in &p4t.actions { - let act = p4c.get_action(&a.name).unwrap(); - action_args.push( - act.parameters - .iter() - .map(|x| p4_type_to_htq_type(&x.ty)) - .collect::, CodegenError>>()?, - ); - } - Ok(htq::ast::Table { - name: p4t.name.clone(), - keyset: p4t - .key - .iter() - .map(|(lval, match_kind)| htq::ast::TableKey { - typ: p4_type_to_htq_type( - &hlir.lvalue_decls.get(lval).unwrap().ty, - ) - .unwrap(), - lookup_typ: p4_match_kind_to_htq_match_kind(match_kind), - }) - .collect(), - action_args, - }) -} diff --git a/codegen/htq/src/parser.rs b/codegen/htq/src/parser.rs new file mode 100644 index 00000000..ccda2d8d --- /dev/null +++ b/codegen/htq/src/parser.rs @@ -0,0 +1,53 @@ +// Copyright 2024 Oxide Computer Company + +use crate::{ + error::CodegenError, p4_type_to_htq_type, statement::emit_statement, +}; +use p4::hlir::Hlir; + +pub(crate) fn emit_parser_functions( + ast: &p4::ast::AST, + hlir: &Hlir, +) -> Result, CodegenError> { + let mut result = Vec::new(); + + for parser in &ast.parsers { + let pf = emit_parser(ast, hlir, parser)?; + result.extend(pf.into_iter()); + } + + Ok(result) +} + +pub(crate) fn emit_parser( + _ast: &p4::ast::AST, + _hlir: &Hlir, + parser: &p4::ast::Parser, +) -> Result, CodegenError> { + let mut result = Vec::new(); + let mut parameters = Vec::new(); + + for x in &parser.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 state in &parser.states { + let mut statements = Vec::new(); + for s in &state.statements.statements { + statements.extend(emit_statement(s)?.into_iter()); + } + let f = htq::ast::Function { + name: format!("{}_{}", parser.name, state.name), + parameters: parameters.clone(), + statements, + }; + result.push(f); + } + + Ok(result) +} diff --git a/codegen/htq/src/statement.rs b/codegen/htq/src/statement.rs new file mode 100644 index 00000000..5ba1773d --- /dev/null +++ b/codegen/htq/src/statement.rs @@ -0,0 +1,19 @@ +// Copyright 2024 Oxide Computer Company + +use crate::error::CodegenError; + +pub(crate) fn emit_statement( + stmt: &p4::ast::Statement, +) -> 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!(), + } +} diff --git a/codegen/htq/src/table.rs b/codegen/htq/src/table.rs new file mode 100644 index 00000000..288c86d9 --- /dev/null +++ b/codegen/htq/src/table.rs @@ -0,0 +1,48 @@ +// Copyright 2024 Oxide Computer Company + +use p4::hlir::Hlir; + +use crate::{error::CodegenError, p4_type_to_htq_type}; + +pub fn p4_match_kind_to_htq_match_kind( + p4m: &p4::ast::MatchKind, +) -> htq::ast::LookupType { + match p4m { + p4::ast::MatchKind::Exact => htq::ast::LookupType::Exact, + p4::ast::MatchKind::LongestPrefixMatch => htq::ast::LookupType::Lpm, + p4::ast::MatchKind::Range => htq::ast::LookupType::Range, + p4::ast::MatchKind::Ternary => htq::ast::LookupType::Ternary, + } +} + +pub(crate) fn p4_table_to_htq_table( + p4c: &p4::ast::Control, + p4t: &p4::ast::Table, + hlir: &Hlir, +) -> Result { + let mut action_args = Vec::new(); + for a in &p4t.actions { + let act = p4c.get_action(&a.name).unwrap(); + action_args.push( + act.parameters + .iter() + .map(|x| p4_type_to_htq_type(&x.ty)) + .collect::, CodegenError>>()?, + ); + } + Ok(htq::ast::Table { + name: p4t.name.clone(), + keyset: p4t + .key + .iter() + .map(|(lval, match_kind)| htq::ast::TableKey { + typ: p4_type_to_htq_type( + &hlir.lvalue_decls.get(lval).unwrap().ty, + ) + .unwrap(), + lookup_typ: p4_match_kind_to_htq_match_kind(match_kind), + }) + .collect(), + action_args, + }) +} From b8a9ac6e780b309fb736367f75dc12d623669b2a Mon Sep 17 00:00:00 2001 From: Ryan Goodfellow Date: Thu, 12 Sep 2024 20:02:31 -0700 Subject: [PATCH 05/23] reticulating --- codegen/htq/src/statement.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/codegen/htq/src/statement.rs b/codegen/htq/src/statement.rs index 5ba1773d..9f7de670 100644 --- a/codegen/htq/src/statement.rs +++ b/codegen/htq/src/statement.rs @@ -17,3 +17,11 @@ pub(crate) fn emit_statement( S::Return(_r) => todo!(), } } + +fn emit_assignment( + target: &p4::ast::Lvalue, + source: &p4::ast::Expression, +) -> Result, CodegenError> { + // Things that can be assigned to are lvalues. + todo!(); +} From 68a518d21b7df7f22f0bcedf11d650f3f11fc4bf Mon Sep 17 00:00:00 2001 From: Ryan Goodfellow Date: Sat, 14 Sep 2024 15:01:51 -0700 Subject: [PATCH 06/23] reticulating codegen --- Cargo.toml | 2 + codegen/common/Cargo.toml | 8 ++ codegen/common/src/lib.rs | 1 + codegen/htq/src/control.rs | 108 +++++++++++++++++ codegen/htq/src/error.rs | 49 +++++++- codegen/htq/src/expression.rs | 98 ++++++++++++++- codegen/htq/src/lib.rs | 74 ++++++++++-- codegen/htq/src/parser.rs | 17 ++- codegen/htq/src/statement.rs | 219 ++++++++++++++++++++++++++++++++-- codegen/rust/Cargo.toml | 1 + codegen/rust/src/header.rs | 4 +- codegen/rust/src/lib.rs | 64 +--------- codegen/rust/src/pipeline.rs | 7 +- p4/src/ast.rs | 102 ++++++++++++++++ 14 files changed, 657 insertions(+), 97 deletions(-) create mode 100644 codegen/common/Cargo.toml create mode 100644 codegen/common/src/lib.rs create mode 100644 codegen/htq/src/control.rs diff --git a/Cargo.toml b/Cargo.toml index 34caf3aa..0b90501f 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", @@ -17,6 +18,7 @@ members = [ [workspace.dependencies] p4-macro = { path = "lang/p4-macro" } +p4-cg = { path = "codegen/common" } p4-rust = { path = "codegen/rust" } p4-htq = { path = "codegen/htq" } p4rs = { path = "lang/p4rs" } diff --git a/codegen/common/Cargo.toml b/codegen/common/Cargo.toml new file mode 100644 index 00000000..325a2029 --- /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 00000000..9647d0e8 --- /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 00000000..86a5dd33 --- /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 21ec8b70..8fee645e 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 1237eb42..a851711a 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 6a2a08e9..84ebe0e4 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 ccda2d8d..672d39f9 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 9f7de670..97d65cdd 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 eb38fa3e..c0dbfa37 100644 --- a/codegen/rust/Cargo.toml +++ b/codegen/rust/Cargo.toml @@ -5,6 +5,7 @@ edition = "2021" [dependencies] p4.workspace = true +p4-cg.workspace = true quote.workspace = true proc-macro2.workspace = true syn.workspace = true diff --git a/codegen/rust/src/header.rs b/codegen/rust/src/header.rs index d36f17be..289f178e 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 3945cee9..4b78525a 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 a11884f0..6f6bf5d7 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 b1b9abbb..8ef675d0 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 +} From 46fee6ae56ec1269888e604c251366c6b8249fe8 Mon Sep 17 00:00:00 2001 From: Ryan Goodfellow Date: Sun, 15 Sep 2024 00:05:44 -0700 Subject: [PATCH 07/23] reticulating codegen --- codegen/htq/src/control.rs | 8 +++ codegen/htq/src/expression.rs | 93 ++++++++++++++++++++++++++++++----- codegen/htq/src/lib.rs | 21 +++++--- codegen/htq/src/statement.rs | 12 ++--- 4 files changed, 109 insertions(+), 25 deletions(-) diff --git a/codegen/htq/src/control.rs b/codegen/htq/src/control.rs index 86a5dd33..d79c17a7 100644 --- a/codegen/htq/src/control.rs +++ b/codegen/htq/src/control.rs @@ -62,6 +62,11 @@ fn emit_control_apply( let mut ra = RegisterAllocator::default(); let mut names = control.names(); let mut statements = Vec::default(); + + for p in parameters { + ra.alloc(&p.reg.0); + } + for s in &control.apply.statements { statements.extend( emit_statement(s, ast, hlir, &mut names, &mut ra)?.into_iter(), @@ -94,6 +99,9 @@ fn emit_control_action( }; parameters.push(p); } + for p in ¶meters { + ra.alloc(&p.reg.0); + } for s in &action.statement_block.statements { statements.extend( emit_statement(s, ast, hlir, &mut names, &mut ra)?.into_iter(), diff --git a/codegen/htq/src/expression.rs b/codegen/htq/src/expression.rs index a851711a..1a7cde72 100644 --- a/codegen/htq/src/expression.rs +++ b/codegen/htq/src/expression.rs @@ -1,15 +1,27 @@ -use htq::ast::{Register, Rset, Statement, Type, Value}; -use p4::ast::{Expression, ExpressionKind}; +use std::collections::HashMap; + +use htq::ast::{Fget, Load, Register, Rset, Statement, Type, Value}; +use p4::{ + ast::{DeclarationInfo, Expression, ExpressionKind, Lvalue, NameInfo}, + hlir::Hlir, +}; // Copyright 2024 Oxide Computer Company -use crate::{error::CodegenError, VersionedRegister}; +use crate::{ + error::CodegenError, p4_type_to_htq_type, statement::member_offsets, + RegisterAllocator, 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, + hlir: &Hlir, + ast: &p4::ast::AST, + ra: &mut RegisterAllocator, + names: &HashMap, ) -> Result<(Vec, Register, Type), CodegenError> { - let r = VersionedRegister::for_expression(expr); + let r = VersionedRegister::for_token(&expr.token); match &expr.kind { ExpressionKind::BoolLit(value) => emit_bool_lit(*value, r), ExpressionKind::BitLit(width, value) => { @@ -19,6 +31,7 @@ pub(crate) fn emit_expression( ExpressionKind::SignedLit(width, value) => { emit_signed_lit(*width, *value, r) } + ExpressionKind::Lvalue(lval) => emit_lval(lval, hlir, ast, ra, names), ExpressionKind::Call(_) => { //TODO Ok(( @@ -27,18 +40,74 @@ pub(crate) fn emit_expression( Type::User("badtype".to_owned()), )) } - ExpressionKind::Lvalue(_) => { - //TODO - Ok(( - Vec::default(), - Register::new("badreg"), - Type::User("badtype".to_owned()), - )) - } xpr => todo!("expression: {xpr:?}"), } } +fn emit_lval( + lval: &Lvalue, + hlir: &Hlir, + ast: &p4::ast::AST, + ra: &mut RegisterAllocator, + names: &HashMap, +) -> Result<(Vec, Register, Type), CodegenError> { + let mut result: Vec = Vec::default(); + + let info = hlir + .lvalue_decls + .get(lval) + .ok_or(CodegenError::UndefinedLvalue(lval.clone()))?; + + let typ = p4_type_to_htq_type(&info.ty)?; + + match &info.decl { + DeclarationInfo::Parameter(_) => { + let treg = VersionedRegister::for_token(&lval.token); + result.push(Statement::Load(Load { + target: treg.to_reg(), + typ: typ.clone(), + source: Register::new(lval.root()), + offset: Value::number(0), + })); + Ok((result, treg.to_reg(), typ)) + } + DeclarationInfo::ActionParameter(_) => { + let treg = VersionedRegister::for_token(&lval.token); + result.push(Statement::Load(Load { + target: treg.to_reg(), + typ: typ.clone(), + source: Register::new(lval.root()), + offset: Value::number(0), + })); + Ok((result, treg.to_reg(), typ)) + } + DeclarationInfo::StructMember | DeclarationInfo::HeaderMember => { + let offsets = member_offsets(ast, names, lval)?; + let treg = VersionedRegister::for_token(&lval.token); + + let src_root = lval.root(); + let source = ra + .get(src_root) + .ok_or(CodegenError::UndefinedLvalue(lval.clone()))?; + + result.push(Statement::Fget(Fget { + target: treg.to_reg(), + typ: typ.clone(), + source, + offsets, + })); + Ok((result, treg.to_reg(), typ)) + } + DeclarationInfo::Local => { + let reg = ra + .get(&lval.name) + .ok_or(CodegenError::UndefinedLvalue(lval.clone()))?; + Ok((result, reg, typ)) + } + other => todo!("emit lval for \n{other:#?}"), + } +} + pub(crate) fn emit_bool_lit( value: bool, ra: VersionedRegister, diff --git a/codegen/htq/src/lib.rs b/codegen/htq/src/lib.rs index 84ebe0e4..a599df7f 100644 --- a/codegen/htq/src/lib.rs +++ b/codegen/htq/src/lib.rs @@ -6,7 +6,7 @@ use control::emit_control_functions; use error::CodegenError; use header::{p4_header_to_htq_header, p4_struct_to_htq_header}; use htq::{ast::Register, emit::Emit}; -use p4::{ast::Expression, hlir::Hlir}; +use p4::{hlir::Hlir, lexer::Token}; use parser::emit_parser_functions; use table::p4_table_to_htq_table; @@ -136,6 +136,12 @@ impl RegisterAllocator { } } } + + pub(crate) fn get(&self, name: &str) -> Option { + self.data + .get(name) + .map(|rev| htq::ast::Register::new(&format!("{}.{}", name, rev))) + } } #[derive(Debug, Clone)] @@ -145,12 +151,9 @@ pub(crate) struct VersionedRegister { } impl VersionedRegister { - pub(crate) fn for_expression(expr: &Expression) -> Self { + pub(crate) fn for_token(tk: &Token) -> Self { Self { - reg: Register::new(&format!( - "tmp{}_{}", - expr.token.line, expr.token.col - )), + reg: Register::new(&format!("tmp{}_{}", tk.line, tk.col)), version: 0, } } @@ -164,6 +167,10 @@ impl VersionedRegister { } pub(crate) fn to_reg(&self) -> Register { - Register::new(&format!("{}.{}", self.reg.0, self.version)) + if self.version == 0 { + Register::new(&self.reg.0) + } else { + Register::new(&format!("{}.{}", self.reg.0, self.version)) + } } } diff --git a/codegen/htq/src/statement.rs b/codegen/htq/src/statement.rs index 97d65cdd..2d60005e 100644 --- a/codegen/htq/src/statement.rs +++ b/codegen/htq/src/statement.rs @@ -83,12 +83,12 @@ fn emit_assignment( // - list // - let target_info = match hlir.lvalue_decls.get(target) { - Some(info) => info, - None => return Err(CodegenError::UndefinedLvalue(target.clone())), - }; + let target_info = hlir + .lvalue_decls + .get(target) + .ok_or(CodegenError::UndefinedLvalue(target.clone()))?; - let (mut instrs, reg, typ) = emit_expression(source)?; + let (mut instrs, reg, typ) = emit_expression(source, hlir, ast, ra, names)?; match &target_info.decl { DeclarationInfo::Parameter(Direction::Out) @@ -143,7 +143,7 @@ fn emit_assignment( Ok(instrs) } -fn member_offsets( +pub(crate) fn member_offsets( ast: &p4::ast::AST, names: &HashMap, lval: &Lvalue, From 93c275d727b4a4e955e4170c9c39ff3c9051a0b6 Mon Sep 17 00:00:00 2001 From: Ryan Goodfellow Date: Sun, 15 Sep 2024 17:57:01 -0700 Subject: [PATCH 08/23] reticulating codegen --- codegen/htq/src/control.rs | 33 +++- codegen/htq/src/error.rs | 29 +++- codegen/htq/src/expression.rs | 297 ++++++++++++++++++++++++++++++---- codegen/htq/src/lib.rs | 67 ++++++-- codegen/htq/src/parser.rs | 17 +- codegen/htq/src/statement.rs | 32 +++- 6 files changed, 416 insertions(+), 59 deletions(-) diff --git a/codegen/htq/src/control.rs b/codegen/htq/src/control.rs index d79c17a7..8ffef83b 100644 --- a/codegen/htq/src/control.rs +++ b/codegen/htq/src/control.rs @@ -5,17 +5,18 @@ use p4::hlir::Hlir; use crate::{ error::CodegenError, p4_type_to_htq_type, statement::emit_statement, - RegisterAllocator, + AsyncFlagAllocator, CgContext, RegisterAllocator, }; pub(crate) fn emit_control_functions( ast: &p4::ast::AST, hlir: &Hlir, + afa: &mut AsyncFlagAllocator, ) -> Result, CodegenError> { let mut result = Vec::default(); for control in &ast.controls { - let cf = emit_control(ast, hlir, control)?; + let cf = emit_control(ast, hlir, control, afa)?; result.extend(cf.into_iter()); } @@ -26,6 +27,7 @@ fn emit_control( ast: &p4::ast::AST, hlir: &Hlir, control: &p4::ast::Control, + afa: &mut AsyncFlagAllocator, ) -> Result, CodegenError> { let mut result = Vec::default(); @@ -39,7 +41,7 @@ fn emit_control( parameters.push(p); } - result.push(emit_control_apply(ast, hlir, control, ¶meters)?); + result.push(emit_control_apply(ast, hlir, control, ¶meters, afa)?); for action in &control.actions { result.push(emit_control_action( @@ -48,6 +50,7 @@ fn emit_control( control, action, ¶meters, + afa, )?); } Ok(result) @@ -58,6 +61,7 @@ fn emit_control_apply( hlir: &Hlir, control: &p4::ast::Control, parameters: &[Parameter], + afa: &mut AsyncFlagAllocator, ) -> Result { let mut ra = RegisterAllocator::default(); let mut names = control.names(); @@ -69,7 +73,16 @@ fn emit_control_apply( for s in &control.apply.statements { statements.extend( - emit_statement(s, ast, hlir, &mut names, &mut ra)?.into_iter(), + emit_statement( + s, + ast, + CgContext::Control(control), + hlir, + &mut names, + &mut ra, + afa, + )? + .into_iter(), ) } let f = htq::ast::Function { @@ -86,6 +99,7 @@ fn emit_control_action( control: &p4::ast::Control, action: &p4::ast::Action, parameters: &[Parameter], + afa: &mut AsyncFlagAllocator, ) -> Result { let mut ra = RegisterAllocator::default(); let mut names = control.names(); @@ -104,7 +118,16 @@ fn emit_control_action( } for s in &action.statement_block.statements { statements.extend( - emit_statement(s, ast, hlir, &mut names, &mut ra)?.into_iter(), + emit_statement( + s, + ast, + CgContext::Control(control), + hlir, + &mut names, + &mut ra, + afa, + )? + .into_iter(), ) } let f = htq::ast::Function { diff --git a/codegen/htq/src/error.rs b/codegen/htq/src/error.rs index 8fee645e..367126d4 100644 --- a/codegen/htq/src/error.rs +++ b/codegen/htq/src/error.rs @@ -1,11 +1,17 @@ // Copyright 2024 Oxide Computer Company use p4::{ - ast::{DeclarationInfo, Expression, Lvalue}, + ast::{Call, Control, DeclarationInfo, Expression, Lvalue, Type}, lexer::Token, }; use thiserror::Error; +#[derive(Error, Debug)] +pub enum FlagAllocationError { + #[error("flag overflow: count exceeds 128")] + Overflow, +} + #[derive(Error, Debug)] pub enum CodegenError { #[error("There is no equivalent htq type for {0}")] @@ -51,6 +57,27 @@ pub enum CodegenError { #[error("cannot calculate offset into extern {0} \n{1:#?}")] CannotOffsetExtern(String, Token), + + #[error("expressions appearing on the RHS of an assignment must have a value\n{0:#?}")] + AssignmentExpressionRequiresValue(Expression), + + #[error("parent for call \n{0:#?}\nnot found")] + CallParentNotFound(Call), + + #[error("cannot make a call on parent type {1}\n{0:#?}")] + InvalidCallParent(Call, Type), + + #[error("calls are not supported in parsers\n{0:#?}")] + CallInParser(Expression), + + #[error("table {0} not found in control \n{1:#?}")] + TableNotFound(String, Control), + + #[error("flag allocation: {0}")] + FlagAllocation(#[from] FlagAllocationError), + + #[error("key extraction produced no value for \n{0:#?}")] + KeyExtractionProducedNoValue(Lvalue), } #[derive(Error, Debug)] diff --git a/codegen/htq/src/expression.rs b/codegen/htq/src/expression.rs index 1a7cde72..afc17d21 100644 --- a/codegen/htq/src/expression.rs +++ b/codegen/htq/src/expression.rs @@ -1,27 +1,62 @@ use std::collections::HashMap; -use htq::ast::{Fget, Load, Register, Rset, Statement, Type, Value}; +use htq::ast::{ + Alloc, Fget, Load, Lookup, Or, Register, Rset, Shl, Statement, Type, Value, +}; use p4::{ - ast::{DeclarationInfo, Expression, ExpressionKind, Lvalue, NameInfo}, + ast::{ + type_size, DeclarationInfo, Expression, ExpressionKind, Lvalue, + NameInfo, + }, hlir::Hlir, }; // Copyright 2024 Oxide Computer Company use crate::{ error::CodegenError, p4_type_to_htq_type, statement::member_offsets, - RegisterAllocator, VersionedRegister, + AsyncFlagAllocator, CgContext, RegisterAllocator, VersionedRegister, }; +pub(crate) struct ExpressionValue { + // register the value of the expression is held in + pub(crate) registers: Vec, + // overall type of the expression + pub(crate) typ: Type, + // sync flag associated with the expression + #[allow(dead_code)] + pub(crate) sync_flag: Option, +} + +impl ExpressionValue { + fn new(register: Register, typ: Type) -> Self { + Self { + registers: vec![register], + typ, + sync_flag: None, + } + } + + fn new_async(register: Register, typ: Type, sync_flag: u128) -> Self { + Self { + registers: vec![register], + typ, + sync_flag: Some(sync_flag), + } + } +} + // Builds a vector of statements that implement the expression. Returns the -// statements and the register the result of the expression is held in. +// statements and the resulting value of the expression, if any. pub(crate) fn emit_expression( expr: &Expression, hlir: &Hlir, ast: &p4::ast::AST, + context: CgContext<'_>, ra: &mut RegisterAllocator, + afa: &mut AsyncFlagAllocator, names: &HashMap, -) -> Result<(Vec, Register, Type), CodegenError> { - let r = VersionedRegister::for_token(&expr.token); +) -> Result<(Vec, Option), CodegenError> { + let r = VersionedRegister::tmp_for_token(&expr.token); match &expr.kind { ExpressionKind::BoolLit(value) => emit_bool_lit(*value, r), ExpressionKind::BitLit(width, value) => { @@ -32,25 +67,226 @@ pub(crate) fn emit_expression( emit_signed_lit(*width, *value, r) } ExpressionKind::Lvalue(lval) => emit_lval(lval, hlir, ast, ra, names), - ExpressionKind::Call(_) => { - //TODO - Ok(( - Vec::default(), - Register::new("badreg"), - Type::User("badtype".to_owned()), - )) - } + ExpressionKind::Call(call) => match context { + CgContext::Control(c) => { + emit_call(call, c, hlir, ast, ra, afa, names) + } + CgContext::Parser(_) => { + Err(CodegenError::CallInParser(expr.clone())) + } + }, xpr => todo!("expression: {xpr:?}"), } } +fn emit_call( + call: &p4::ast::Call, + control: &p4::ast::Control, + hlir: &Hlir, + ast: &p4::ast::AST, + ra: &mut RegisterAllocator, + afa: &mut AsyncFlagAllocator, + names: &HashMap, +) -> Result<(Vec, Option), CodegenError> { + println!("CALL {call:#?}"); + + match call.lval.leaf() { + "apply" => emit_apply_call(call, control, hlir, ast, ra, afa, names), + "setValid" => emit_set_valid_call(call, hlir, ast, ra, names, true), + "setInvalid" => emit_set_valid_call(call, hlir, ast, ra, names, false), + "isValid" => emit_is_valid_call(call, hlir, ast, ra, names), + _ => emit_extern_call(call, hlir, ast, ra, names), + } +} + +fn emit_apply_call( + call: &p4::ast::Call, + control: &p4::ast::Control, + hlir: &Hlir, + ast: &p4::ast::AST, + ra: &mut RegisterAllocator, + afa: &mut AsyncFlagAllocator, + names: &HashMap, +) -> Result<(Vec, Option), CodegenError> { + let parent = call.lval.pop_right(); + let parent = parent.leaf(); + let info = names + .get(parent) + .ok_or(CodegenError::CallParentNotFound(call.clone()))?; + + match &info.ty { + p4::ast::Type::Table => { + emit_table_apply_call(call, control, hlir, ast, ra, afa, names) + } + p4::ast::Type::UserDefined(name, _) => { + // validate user defined type is a control + ast.get_control(name) + .ok_or(CodegenError::InvalidCallParent( + call.clone(), + info.ty.clone(), + ))?; + emit_control_apply_call(call, hlir, ast, ra, names) + } + p4::ast::Type::Action => emit_action_call(call, hlir, ast, ra, names), + p4::ast::Type::ExternFunction => { + emit_extern_call(call, hlir, ast, ra, names) + } + typ => Err(CodegenError::InvalidCallParent(call.clone(), typ.clone())), + } +} + +fn emit_table_apply_call( + call: &p4::ast::Call, + control: &p4::ast::Control, + hlir: &Hlir, + ast: &p4::ast::AST, + ra: &mut RegisterAllocator, + afa: &mut AsyncFlagAllocator, + names: &HashMap, +) -> Result<(Vec, Option), CodegenError> { + // %hit, %variant, %args = async 2 lookup proxy_arp %key; + let table = call.lval.pop_right(); + let table = table.leaf(); + let table = control.get_table(table).ok_or(CodegenError::TableNotFound( + table.to_owned(), + control.clone(), + ))?; + + let sync_flag = afa.allocate()?; + + let hit = VersionedRegister::for_token("hit", &call.lval.token); + let variant = VersionedRegister::for_token("variant", &call.lval.token); + let args = VersionedRegister::for_token("args", &call.lval.token); + let result = VersionedRegister::for_token("result", &call.lval.token); + let mut key = VersionedRegister::for_token("key", &call.lval.token); + let mut instrs = Vec::new(); + + let result_typ = Type::User(String::from("lookup_result")); + let result_alloc_instr = Statement::Alloc(Alloc { + target: result.to_reg(), + typ: result_typ.clone(), + }); + instrs.push(result_alloc_instr); + + let mut total_key_size = 0; + for (k, _) in &table.key { + let info = hlir + .lvalue_decls + .get(k) + .ok_or(CodegenError::UndefinedLvalue(k.clone()))?; + total_key_size += type_size(&info.ty, ast); + } + + let key_typ = Type::Bitfield(total_key_size); + + let mut offset = 0u128; + for (k, _) in &table.key { + let (key_extract_statements, extracted_value) = + emit_lval(k, hlir, ast, ra, names)?; + let extracted_value = extracted_value + .ok_or(CodegenError::KeyExtractionProducedNoValue(k.clone()))?; + let tmp = VersionedRegister::for_token("key", &k.token); + instrs.push(Statement::Shl(Shl { + target: tmp.to_reg(), + typ: key_typ.clone(), + source: Value::register(&extracted_value.registers[0].0), + amount: Value::number(offset as i128), + })); + let curr_key = key.clone(); + instrs.push(Statement::Or(Or { + target: key.next().to_reg(), + typ: key_typ.clone(), + source_a: Value::reg(curr_key.to_reg()), + source_b: Value::reg(tmp.to_reg()), + })); + instrs.extend(key_extract_statements.into_iter()); + offset += extracted_value.typ.bit_size().unwrap() as u128; + } + + let lookup_instr = Statement::Lookup(Lookup { + hit: hit.to_reg(), + variant: variant.to_reg(), + args: args.to_reg(), + asynchronous: if table.is_async { + Some(htq::ast::Async { + identifier: Value::number(sync_flag as i128), + }) + } else { + None + }, + table: table.name.clone(), + key: Value::register(&key.name()), + }); + instrs.push(lookup_instr); + + Ok(( + instrs, + Some(ExpressionValue::new_async( + result.to_reg(), + result_typ, + sync_flag, + )), + )) +} + +fn emit_control_apply_call( + _call: &p4::ast::Call, + _hlir: &Hlir, + _ast: &p4::ast::AST, + _ra: &mut RegisterAllocator, + _names: &HashMap, +) -> Result<(Vec, Option), CodegenError> { + todo!("control apply call"); +} + +fn emit_set_valid_call( + _call: &p4::ast::Call, + _hlir: &Hlir, + _ast: &p4::ast::AST, + _ra: &mut RegisterAllocator, + _names: &HashMap, + _valid: bool, +) -> Result<(Vec, Option), CodegenError> { + todo!("set valid call") +} + +fn emit_is_valid_call( + _call: &p4::ast::Call, + _hlir: &Hlir, + _ast: &p4::ast::AST, + _ra: &mut RegisterAllocator, + _names: &HashMap, +) -> Result<(Vec, Option), CodegenError> { + todo!("is valid call") +} + +fn emit_extern_call( + _call: &p4::ast::Call, + _hlir: &Hlir, + _ast: &p4::ast::AST, + _ra: &mut RegisterAllocator, + _names: &HashMap, +) -> Result<(Vec, Option), CodegenError> { + todo!("extern call") +} + +fn emit_action_call( + _call: &p4::ast::Call, + _hlir: &Hlir, + _ast: &p4::ast::AST, + _ra: &mut RegisterAllocator, + _names: &HashMap, +) -> Result<(Vec, Option), CodegenError> { + todo!("action call") +} + fn emit_lval( lval: &Lvalue, hlir: &Hlir, ast: &p4::ast::AST, ra: &mut RegisterAllocator, names: &HashMap, -) -> Result<(Vec, Register, Type), CodegenError> { +) -> Result<(Vec, Option), CodegenError> { let mut result: Vec = Vec::default(); let info = hlir @@ -62,28 +298,29 @@ fn emit_lval( match &info.decl { DeclarationInfo::Parameter(_) => { - let treg = VersionedRegister::for_token(&lval.token); + //TODO unify VersionedRegister and RegisterAllocator + let treg = VersionedRegister::tmp_for_token(&lval.token); result.push(Statement::Load(Load { target: treg.to_reg(), typ: typ.clone(), source: Register::new(lval.root()), offset: Value::number(0), })); - Ok((result, treg.to_reg(), typ)) + Ok((result, Some(ExpressionValue::new(treg.to_reg(), typ)))) } DeclarationInfo::ActionParameter(_) => { - let treg = VersionedRegister::for_token(&lval.token); + let treg = VersionedRegister::tmp_for_token(&lval.token); result.push(Statement::Load(Load { target: treg.to_reg(), typ: typ.clone(), source: Register::new(lval.root()), offset: Value::number(0), })); - Ok((result, treg.to_reg(), typ)) + Ok((result, Some(ExpressionValue::new(treg.to_reg(), typ)))) } DeclarationInfo::StructMember | DeclarationInfo::HeaderMember => { let offsets = member_offsets(ast, names, lval)?; - let treg = VersionedRegister::for_token(&lval.token); + let treg = VersionedRegister::tmp_for_token(&lval.token); let src_root = lval.root(); let source = ra @@ -96,13 +333,13 @@ fn emit_lval( source, offsets, })); - Ok((result, treg.to_reg(), typ)) + Ok((result, Some(ExpressionValue::new(treg.to_reg(), typ)))) } DeclarationInfo::Local => { let reg = ra .get(&lval.name) .ok_or(CodegenError::UndefinedLvalue(lval.clone()))?; - Ok((result, reg, typ)) + Ok((result, Some(ExpressionValue::new(reg, typ)))) } other => todo!("emit lval for \n{other:#?}"), } @@ -111,13 +348,13 @@ fn emit_lval( pub(crate) fn emit_bool_lit( value: bool, ra: VersionedRegister, -) -> Result<(Vec, Register, Type), CodegenError> { +) -> Result<(Vec, Option), 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)) + Ok((instrs, Some(ExpressionValue::new(ra.to_reg(), Type::Bool)))) } pub(crate) fn emit_bit_lit( @@ -125,7 +362,7 @@ pub(crate) fn emit_bit_lit( value: u128, ra: VersionedRegister, expr: &Expression, -) -> Result<(Vec, Register, Type), CodegenError> { +) -> Result<(Vec, Option), CodegenError> { let value = i128::try_from(value) .map_err(|_| CodegenError::NumericConversion(expr.clone()))?; let typ = Type::Bitfield(usize::from(width)); @@ -134,32 +371,32 @@ pub(crate) fn emit_bit_lit( target: ra.clone().to_reg(), source: Value::number(value), })]; - Ok((instrs, ra.to_reg(), typ)) + Ok((instrs, Some(ExpressionValue::new(ra.to_reg(), typ)))) } pub(crate) fn emit_int_lit( value: i128, ra: VersionedRegister, -) -> Result<(Vec, Register, Type), CodegenError> { +) -> Result<(Vec, Option), 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)) + Ok((instrs, Some(ExpressionValue::new(ra.to_reg(), typ)))) } pub(crate) fn emit_signed_lit( width: u16, value: i128, ra: VersionedRegister, -) -> Result<(Vec, Register, Type), CodegenError> { +) -> Result<(Vec, Option), 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)) + Ok((instrs, Some(ExpressionValue::new(ra.to_reg(), typ)))) } diff --git a/codegen/htq/src/lib.rs b/codegen/htq/src/lib.rs index a599df7f..a306e614 100644 --- a/codegen/htq/src/lib.rs +++ b/codegen/htq/src/lib.rs @@ -3,7 +3,7 @@ use std::{collections::HashMap, io::Write}; use control::emit_control_functions; -use error::CodegenError; +use error::{CodegenError, FlagAllocationError}; use header::{p4_header_to_htq_header, p4_struct_to_htq_header}; use htq::{ast::Register, emit::Emit}; use p4::{hlir::Hlir, lexer::Token}; @@ -25,6 +25,7 @@ pub fn emit( hlir: &Hlir, filename: &str, ) -> Result<(), EmitError> { + let mut afa = AsyncFlagAllocator::default(); let mut headers: Vec<_> = ast.headers .iter() @@ -45,8 +46,9 @@ pub fn emit( .map(|(c, t)| p4_table_to_htq_table(c, t, hlir)) .collect::, CodegenError>>()?; - let parser_functions: Vec<_> = emit_parser_functions(ast, hlir)?; - let control_functions: Vec<_> = emit_control_functions(ast, hlir)?; + let parser_functions: Vec<_> = emit_parser_functions(ast, hlir, &mut afa)?; + let control_functions: Vec<_> = + emit_control_functions(ast, hlir, &mut afa)?; // code generation done, now write out the htq AST to a file @@ -87,7 +89,8 @@ fn p4_type_to_htq_type( htq::ast::Type::User(name.clone()) } p4::ast::Type::Sync(_) => { - htq::ast::Type::Signed(128) + htq::ast::Type::User(String::from("lookup_result")) + //htq::ast::Type::Signed(128) //return Err(CodegenError::NoEquivalentType(t.clone())) } t @ p4::ast::Type::Table => { @@ -144,6 +147,28 @@ impl RegisterAllocator { } } +/// The async flag allocator allocates bitmap entries for asynchronous +/// operations. HTQ supports up to 128 flags through an underlying u128 type. +#[derive(Default)] +pub(crate) struct AsyncFlagAllocator { + flag: u128, +} + +impl AsyncFlagAllocator { + pub(crate) fn allocate(&mut self) -> Result { + if self.flag == u128::MAX { + return Err(FlagAllocationError::Overflow); + } + // simple in-order allocation with no deallocation for now, + // can make this more sophisticated with deallocation and + // a back-filling allocator later ... + let pos = self.flag.leading_ones(); + let value = 1 << pos; + self.flag |= value; + Ok(value) + } +} + #[derive(Debug, Clone)] pub(crate) struct VersionedRegister { pub(crate) reg: Register, @@ -151,26 +176,42 @@ pub(crate) struct VersionedRegister { } impl VersionedRegister { - pub(crate) fn for_token(tk: &Token) -> Self { + pub(crate) fn tmp_for_token(tk: &Token) -> Self { Self { reg: Register::new(&format!("tmp{}_{}", tk.line, tk.col)), version: 0, } } - - #[allow(dead_code)] - pub(crate) fn next(self) -> Self { + pub(crate) fn for_token(prefix: &str, tk: &Token) -> Self { Self { - reg: self.reg, - version: self.version + 1, + reg: Register::new(&format!("{}{}_{}", prefix, tk.line, tk.col)), + version: 0, } } - pub(crate) fn to_reg(&self) -> Register { + #[allow(dead_code)] + pub(crate) fn next(&mut self) -> &Self { + self.version += 1; + self + } + + pub(crate) fn name(&self) -> String { if self.version == 0 { - Register::new(&self.reg.0) + self.reg.0.clone() } else { - Register::new(&format!("{}.{}", self.reg.0, self.version)) + format!("{}.{}", self.reg.0, self.version) } } + + pub(crate) fn to_reg(&self) -> Register { + Register::new(&self.name()) + } +} + +// Codegen context +// TODO more comprehensive .... +pub(crate) enum CgContext<'a> { + #[allow(dead_code)] + Parser(&'a p4::ast::Parser), + Control(&'a p4::ast::Control), } diff --git a/codegen/htq/src/parser.rs b/codegen/htq/src/parser.rs index 672d39f9..de59b795 100644 --- a/codegen/htq/src/parser.rs +++ b/codegen/htq/src/parser.rs @@ -2,18 +2,19 @@ use crate::{ error::CodegenError, p4_type_to_htq_type, statement::emit_statement, - RegisterAllocator, + AsyncFlagAllocator, CgContext, RegisterAllocator, }; use p4::hlir::Hlir; pub(crate) fn emit_parser_functions( ast: &p4::ast::AST, hlir: &Hlir, + afa: &mut AsyncFlagAllocator, ) -> Result, CodegenError> { let mut result = Vec::new(); for parser in &ast.parsers { - let pf = emit_parser(ast, hlir, parser)?; + let pf = emit_parser(ast, hlir, parser, afa)?; result.extend(pf.into_iter()); } @@ -24,6 +25,7 @@ fn emit_parser( ast: &p4::ast::AST, hlir: &Hlir, parser: &p4::ast::Parser, + afa: &mut AsyncFlagAllocator, ) -> Result, CodegenError> { let mut result = Vec::new(); let mut parameters = Vec::new(); @@ -45,7 +47,16 @@ fn emit_parser( let mut statements = Vec::default(); for s in &state.statements.statements { statements.extend( - emit_statement(s, ast, hlir, &mut names, &mut ra)?.into_iter(), + emit_statement( + s, + ast, + CgContext::Parser(parser), + hlir, + &mut names, + &mut ra, + afa, + )? + .into_iter(), ); } let f = htq::ast::Function { diff --git a/codegen/htq/src/statement.rs b/codegen/htq/src/statement.rs index 2d60005e..c569c6e6 100644 --- a/codegen/htq/src/statement.rs +++ b/codegen/htq/src/statement.rs @@ -12,21 +12,24 @@ use p4::{ }; use crate::{ - error::CodegenError, expression::emit_expression, RegisterAllocator, + error::CodegenError, expression::emit_expression, AsyncFlagAllocator, + CgContext, RegisterAllocator, }; pub(crate) fn emit_statement( stmt: &p4::ast::Statement, ast: &p4::ast::AST, + context: CgContext<'_>, hlir: &Hlir, names: &mut HashMap, ra: &mut RegisterAllocator, + afa: &mut AsyncFlagAllocator, ) -> Result, CodegenError> { use p4::ast::Statement as S; match stmt { S::Empty => Ok(Vec::new()), S::Assignment(lval, expr) => { - emit_assignment(hlir, ast, names, lval, expr, ra) + emit_assignment(hlir, ast, context, names, lval, expr, ra, afa) } S::Call(_call) => Ok(Vec::default()), //TODO S::If(_if_block) => Ok(Vec::default()), //TODO, @@ -37,13 +40,16 @@ pub(crate) fn emit_statement( } } +#[allow(clippy::too_many_arguments)] fn emit_assignment( hlir: &Hlir, ast: &p4::ast::AST, + context: CgContext<'_>, names: &mut HashMap, target: &p4::ast::Lvalue, source: &p4::ast::Expression, ra: &mut RegisterAllocator, + afa: &mut AsyncFlagAllocator, ) -> Result, CodegenError> { // Things that can be assigned to are lvalues with the following // declaration kinds. The table shows how each kind is referenced @@ -88,7 +94,19 @@ fn emit_assignment( .get(target) .ok_or(CodegenError::UndefinedLvalue(target.clone()))?; - let (mut instrs, reg, typ) = emit_expression(source, hlir, ast, ra, names)?; + // reg typ + let (mut instrs, expr_value) = + emit_expression(source, hlir, ast, context, ra, afa, names)?; + + let expr_value = expr_value.ok_or( + CodegenError::AssignmentExpressionRequiresValue(source.clone()), + )?; + + if expr_value.registers.is_empty() { + return Err(CodegenError::AssignmentExpressionRequiresValue( + source.clone(), + )); + } match &target_info.decl { DeclarationInfo::Parameter(Direction::Out) @@ -100,10 +118,10 @@ fn emit_assignment( let treg = Register::new(target.root()); let offsets = member_offsets(ast, names, target)?; let instr = Fset { - typ, offsets, + typ: expr_value.typ, target: treg, - source: Value::Register(reg), + source: Value::Register(expr_value.registers[0].clone()), }; instrs.push(htq::ast::Statement::Fset(instr)); } @@ -128,8 +146,8 @@ fn emit_assignment( let instr = Rset { target, - typ, - source: Value::Register(reg), + typ: expr_value.typ, + source: Value::Register(expr_value.registers[0].clone()), }; instrs.push(htq::ast::Statement::Rset(instr)); } From 846419de7a4624be9019dcd749cf88e780deffba Mon Sep 17 00:00:00 2001 From: Ryan Goodfellow Date: Sun, 15 Sep 2024 23:51:21 -0700 Subject: [PATCH 09/23] apply/action/parser block return signatures --- codegen/htq/src/control.rs | 40 ++++++++++-- codegen/htq/src/error.rs | 8 ++- codegen/htq/src/expression.rs | 12 +++- codegen/htq/src/parser.rs | 10 ++- p4/src/ast.rs | 118 ++++++++++++++++++++++++++++++++++ 5 files changed, 178 insertions(+), 10 deletions(-) diff --git a/codegen/htq/src/control.rs b/codegen/htq/src/control.rs index 8ffef83b..d5870606 100644 --- a/codegen/htq/src/control.rs +++ b/codegen/htq/src/control.rs @@ -32,16 +32,44 @@ fn emit_control( let mut result = Vec::default(); let mut parameters = Vec::new(); + let mut return_signature = Vec::new(); for x in &control.parameters { + let typ = p4_type_to_htq_type(&x.ty)?; + if x.direction.is_out() { + // TODO the special case nature of lookup results is quite + // unfortunate + if x.ty.is_lookup_result() { + return_signature.push(htq::ast::Type::Bool); //hit + return_signature.push(htq::ast::Type::Unsigned(16)); //variant + + let args_size = control + .resolve_lookup_result_args_size(&x.name, ast) + .ok_or(CodegenError::LookupResultArgSize(x.clone()))?; + + return_signature.push(htq::ast::Type::Unsigned(args_size)); + + if x.ty.is_sync() { + return_signature.push(htq::ast::Type::Unsigned(128)); //async flag + } + } else { + return_signature.push(typ.clone()); + } + } let p = htq::ast::Parameter { reg: htq::ast::Register::new(x.name.as_str()), - pointer: true, - typ: p4_type_to_htq_type(&x.ty)?, + typ, }; parameters.push(p); } - result.push(emit_control_apply(ast, hlir, control, ¶meters, afa)?); + result.push(emit_control_apply( + ast, + hlir, + control, + ¶meters, + &return_signature, + afa, + )?); for action in &control.actions { result.push(emit_control_action( @@ -50,6 +78,7 @@ fn emit_control( control, action, ¶meters, + &return_signature, afa, )?); } @@ -61,6 +90,7 @@ fn emit_control_apply( hlir: &Hlir, control: &p4::ast::Control, parameters: &[Parameter], + return_signature: &[htq::ast::Type], afa: &mut AsyncFlagAllocator, ) -> Result { let mut ra = RegisterAllocator::default(); @@ -89,6 +119,7 @@ fn emit_control_apply( name: format!("{}_apply", control.name), parameters: parameters.to_owned(), statements, + return_signature: return_signature.to_vec(), }; Ok(f) } @@ -99,6 +130,7 @@ fn emit_control_action( control: &p4::ast::Control, action: &p4::ast::Action, parameters: &[Parameter], + return_signature: &[htq::ast::Type], afa: &mut AsyncFlagAllocator, ) -> Result { let mut ra = RegisterAllocator::default(); @@ -108,7 +140,6 @@ fn emit_control_action( 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); @@ -134,6 +165,7 @@ fn emit_control_action( name: format!("{}_{}", control.name, action.name), parameters, statements, + return_signature: return_signature.to_vec(), }; Ok(f) } diff --git a/codegen/htq/src/error.rs b/codegen/htq/src/error.rs index 367126d4..04b2cd46 100644 --- a/codegen/htq/src/error.rs +++ b/codegen/htq/src/error.rs @@ -1,7 +1,10 @@ // Copyright 2024 Oxide Computer Company use p4::{ - ast::{Call, Control, DeclarationInfo, Expression, Lvalue, Type}, + ast::{ + Call, Control, ControlParameter, DeclarationInfo, Expression, Lvalue, + Type, + }, lexer::Token, }; use thiserror::Error; @@ -78,6 +81,9 @@ pub enum CodegenError { #[error("key extraction produced no value for \n{0:#?}")] KeyExtractionProducedNoValue(Lvalue), + + #[error("could not determine lookup result arg size for \n{0:#?}")] + LookupResultArgSize(ControlParameter), } #[derive(Error, Debug)] diff --git a/codegen/htq/src/expression.rs b/codegen/htq/src/expression.rs index afc17d21..529ad981 100644 --- a/codegen/htq/src/expression.rs +++ b/codegen/htq/src/expression.rs @@ -88,8 +88,6 @@ fn emit_call( afa: &mut AsyncFlagAllocator, names: &HashMap, ) -> Result<(Vec, Option), CodegenError> { - println!("CALL {call:#?}"); - match call.lval.leaf() { "apply" => emit_apply_call(call, control, hlir, ast, ra, afa, names), "setValid" => emit_set_valid_call(call, hlir, ast, ra, names, true), @@ -179,12 +177,20 @@ fn emit_table_apply_call( let key_typ = Type::Bitfield(total_key_size); + instrs.push(Statement::Rset(Rset { + target: key.to_reg(), + typ: key_typ.clone(), + source: Value::number(0), + })); + let mut offset = 0u128; for (k, _) in &table.key { let (key_extract_statements, extracted_value) = emit_lval(k, hlir, ast, ra, names)?; let extracted_value = extracted_value .ok_or(CodegenError::KeyExtractionProducedNoValue(k.clone()))?; + instrs.extend(key_extract_statements.into_iter()); + let tmp = VersionedRegister::for_token("key", &k.token); instrs.push(Statement::Shl(Shl { target: tmp.to_reg(), @@ -192,6 +198,7 @@ fn emit_table_apply_call( source: Value::register(&extracted_value.registers[0].0), amount: Value::number(offset as i128), })); + let curr_key = key.clone(); instrs.push(Statement::Or(Or { target: key.next().to_reg(), @@ -199,7 +206,6 @@ fn emit_table_apply_call( source_a: Value::reg(curr_key.to_reg()), source_b: Value::reg(tmp.to_reg()), })); - instrs.extend(key_extract_statements.into_iter()); offset += extracted_value.typ.bit_size().unwrap() as u128; } diff --git a/codegen/htq/src/parser.rs b/codegen/htq/src/parser.rs index de59b795..45ca9f09 100644 --- a/codegen/htq/src/parser.rs +++ b/codegen/htq/src/parser.rs @@ -30,11 +30,16 @@ fn emit_parser( let mut result = Vec::new(); let mut parameters = Vec::new(); + let mut return_signature = Vec::new(); + for x in &parser.parameters { + let typ = p4_type_to_htq_type(&x.ty)?; + if x.direction.is_out() { + return_signature.push(typ.clone()); + } let p = htq::ast::Parameter { reg: htq::ast::Register::new(x.name.as_str()), - pointer: true, - typ: p4_type_to_htq_type(&x.ty)?, + typ, }; parameters.push(p); } @@ -63,6 +68,7 @@ fn emit_parser( name: format!("{}_{}", parser.name, state.name), parameters: parameters.clone(), statements, + return_signature: return_signature.clone(), }; result.push(f); } diff --git a/p4/src/ast.rs b/p4/src/ast.rs index 8ef675d0..f3c33dad 100644 --- a/p4/src/ast.rs +++ b/p4/src/ast.rs @@ -348,6 +348,18 @@ impl Type { } } } + + pub fn is_lookup_result(&self) -> bool { + match self { + Self::UserDefined(s, _) if s == "lookup_result" => true, + Self::Sync(typ) => typ.is_lookup_result(), + _ => false, + } + } + + pub fn is_sync(&self) -> bool { + matches!(self, Self::Sync(_)) + } } impl fmt::Display for Type { @@ -1159,6 +1171,103 @@ impl Control { s.mut_accept_mut(v); } } + + pub fn resolve_lookup_result_args_size( + &self, + parameter_name: &str, + ast: &AST, + ) -> Option { + self.resolve_lookup_result_args_size_rec( + parameter_name, + ast, + &self.apply, + ) + } + + //TODO this needs to consider the possibility of multiple assignments + pub fn resolve_lookup_result_args_size_rec( + &self, + parameter_name: &str, + ast: &AST, + block: &StatementBlock, + ) -> Option { + let mut size = 0; + let mut found = false; + for x in &block.statements { + match x { + Statement::Assignment(lval, expr) => { + if lval.root() != parameter_name { + continue; + } + match &expr.kind { + ExpressionKind::Call(call) => { + let mut lv = call.lval.clone(); + if lv.leaf() == "await" { + lv = lv.pop_right(); + } + if lv.leaf() != "apply" { + continue; + } + if let Some(tbl) = self.get_table(call.lval.root()) + { + for action in &tbl.actions { + let mut asize = 0; + let action = + self.get_action(&action.name).unwrap(); + for p in &action.parameters { + let psize = type_size(&p.ty, ast); + asize += psize; + } + size = usize::max(size, asize); + found = true; + } + } else { + continue; + } + } + _ => continue, + } + } + Statement::If(if_block) => { + if let Some(sz) = self.resolve_lookup_result_args_size_rec( + parameter_name, + ast, + &if_block.block, + ) { + size = usize::max(size, sz); + } + for x in &if_block.else_ifs { + if let Some(sz) = self + .resolve_lookup_result_args_size_rec( + parameter_name, + ast, + &x.block, + ) + { + size = usize::max(size, sz); + } + } + if let Some(else_block) = &if_block.else_block { + if let Some(sz) = self + .resolve_lookup_result_args_size_rec( + parameter_name, + ast, + else_block, + ) + { + size = usize::max(size, sz); + } + } + } + _ => continue, + } + } + if found { + Some(size) + } else { + None + } + } } impl PartialEq for Control { @@ -1309,6 +1418,15 @@ pub enum Direction { Unspecified, } +impl Direction { + pub fn is_out(&self) -> bool { + *self == Self::Out || *self == Self::InOut + } + pub fn is_in(&self) -> bool { + *self == Self::In || *self == Self::InOut + } +} + #[derive(Debug, Clone, Default)] pub struct StatementBlock { pub statements: Vec, From 7d5ef528fb629f3f6820b4f5df0c939ca475d0e9 Mon Sep 17 00:00:00 2001 From: Ryan Goodfellow Date: Tue, 17 Sep 2024 23:05:41 -0700 Subject: [PATCH 10/23] transforms for user defined types into register sets --- codegen/htq/src/control.rs | 55 +++++++++++++++++++++++++------- codegen/htq/src/expression.rs | 59 ++++++++++++++++++----------------- codegen/htq/src/lib.rs | 8 ++++- codegen/htq/src/parser.rs | 11 +++++-- codegen/htq/src/statement.rs | 52 ++++++++++++++++++++---------- p4/src/ast.rs | 45 +++++++++++++++++--------- 6 files changed, 156 insertions(+), 74 deletions(-) diff --git a/codegen/htq/src/control.rs b/codegen/htq/src/control.rs index d5870606..b18a00a5 100644 --- a/codegen/htq/src/control.rs +++ b/codegen/htq/src/control.rs @@ -1,11 +1,13 @@ // Copyright 2024 Oxide Computer Company -use htq::ast::Parameter; -use p4::hlir::Hlir; +use std::collections::HashMap; + +use htq::ast::{Parameter, Register}; +use p4::{ast::ControlParameter, hlir::Hlir}; use crate::{ error::CodegenError, p4_type_to_htq_type, statement::emit_statement, - AsyncFlagAllocator, CgContext, RegisterAllocator, + AsyncFlagAllocator, P4Context, RegisterAllocator, }; pub(crate) fn emit_control_functions( @@ -59,7 +61,7 @@ fn emit_control( reg: htq::ast::Register::new(x.name.as_str()), typ, }; - parameters.push(p); + parameters.push((p, x.clone())); } result.push(emit_control_apply( @@ -89,15 +91,19 @@ fn emit_control_apply( ast: &p4::ast::AST, hlir: &Hlir, control: &p4::ast::Control, - parameters: &[Parameter], + parameters: &[(Parameter, p4::ast::ControlParameter)], return_signature: &[htq::ast::Type], afa: &mut AsyncFlagAllocator, ) -> Result { let mut ra = RegisterAllocator::default(); let mut names = control.names(); let mut statements = Vec::default(); + let mut psub = HashMap::>::default(); - for p in parameters { + for (p, p4p) in parameters { + if psub.get(p4p).is_some() { + continue; + } ra.alloc(&p.reg.0); } @@ -106,18 +112,38 @@ fn emit_control_apply( emit_statement( s, ast, - CgContext::Control(control), + P4Context::Control(control), hlir, &mut names, &mut ra, afa, + &mut psub, )? .into_iter(), ) } + + let mut signature = Vec::new(); + let mut return_registers = Vec::new(); + + for (p, p4p) in parameters { + if p4p.direction.is_out() { + if let Some(substituted) = psub.get(p4p) { + return_registers.extend(substituted.clone().into_iter()); + } else { + signature.push(p.clone()); + return_registers.push(p.reg.clone()); + } + } + } + + statements.push(htq::ast::Statement::Return(htq::ast::Return { + registers: return_registers, + })); + let f = htq::ast::Function { name: format!("{}_apply", control.name), - parameters: parameters.to_owned(), + parameters: signature, statements, return_signature: return_signature.to_vec(), }; @@ -129,14 +155,17 @@ fn emit_control_action( hlir: &Hlir, control: &p4::ast::Control, action: &p4::ast::Action, - parameters: &[Parameter], + parameters: &[(Parameter, p4::ast::ControlParameter)], return_signature: &[htq::ast::Type], afa: &mut AsyncFlagAllocator, ) -> Result { let mut ra = RegisterAllocator::default(); let mut names = control.names(); let mut statements = Vec::default(); - let mut parameters = parameters.to_owned(); + let mut parameters: Vec = + parameters.to_owned().into_iter().map(|x| x.0).collect(); + let mut psub = HashMap::>::default(); + for x in &action.parameters { let p = htq::ast::Parameter { reg: htq::ast::Register::new(x.name.as_str()), @@ -144,23 +173,27 @@ fn emit_control_action( }; parameters.push(p); } + for p in ¶meters { ra.alloc(&p.reg.0); } + for s in &action.statement_block.statements { statements.extend( emit_statement( s, ast, - CgContext::Control(control), + P4Context::Control(control), hlir, &mut names, &mut ra, afa, + &mut psub, )? .into_iter(), ) } + let f = htq::ast::Function { name: format!("{}_{}", control.name, action.name), parameters, diff --git a/codegen/htq/src/expression.rs b/codegen/htq/src/expression.rs index 529ad981..d6f355f0 100644 --- a/codegen/htq/src/expression.rs +++ b/codegen/htq/src/expression.rs @@ -1,7 +1,7 @@ use std::collections::HashMap; use htq::ast::{ - Alloc, Fget, Load, Lookup, Or, Register, Rset, Shl, Statement, Type, Value, + Fget, Load, Lookup, Or, Register, Rset, Shl, Statement, Type, Value, }; use p4::{ ast::{ @@ -14,14 +14,12 @@ use p4::{ // Copyright 2024 Oxide Computer Company use crate::{ error::CodegenError, p4_type_to_htq_type, statement::member_offsets, - AsyncFlagAllocator, CgContext, RegisterAllocator, VersionedRegister, + AsyncFlagAllocator, P4Context, RegisterAllocator, VersionedRegister, }; pub(crate) struct ExpressionValue { - // register the value of the expression is held in - pub(crate) registers: Vec, - // overall type of the expression - pub(crate) typ: Type, + // register the value of the expression is held in along with their types + pub(crate) registers: Vec<(Register, Type)>, // sync flag associated with the expression #[allow(dead_code)] pub(crate) sync_flag: Option, @@ -30,16 +28,15 @@ pub(crate) struct ExpressionValue { impl ExpressionValue { fn new(register: Register, typ: Type) -> Self { Self { - registers: vec![register], - typ, + registers: vec![(register, typ)], sync_flag: None, } } + #[allow(dead_code)] fn new_async(register: Register, typ: Type, sync_flag: u128) -> Self { Self { - registers: vec![register], - typ, + registers: vec![(register, typ)], sync_flag: Some(sync_flag), } } @@ -51,7 +48,7 @@ pub(crate) fn emit_expression( expr: &Expression, hlir: &Hlir, ast: &p4::ast::AST, - context: CgContext<'_>, + context: &P4Context<'_>, ra: &mut RegisterAllocator, afa: &mut AsyncFlagAllocator, names: &HashMap, @@ -68,10 +65,10 @@ pub(crate) fn emit_expression( } ExpressionKind::Lvalue(lval) => emit_lval(lval, hlir, ast, ra, names), ExpressionKind::Call(call) => match context { - CgContext::Control(c) => { + P4Context::Control(c) => { emit_call(call, c, hlir, ast, ra, afa, names) } - CgContext::Parser(_) => { + P4Context::Parser(_) => { Err(CodegenError::CallInParser(expr.clone())) } }, @@ -155,17 +152,11 @@ fn emit_table_apply_call( let hit = VersionedRegister::for_token("hit", &call.lval.token); let variant = VersionedRegister::for_token("variant", &call.lval.token); let args = VersionedRegister::for_token("args", &call.lval.token); - let result = VersionedRegister::for_token("result", &call.lval.token); + let sync_flag_reg = + VersionedRegister::for_token("sync_flag", &call.lval.token); let mut key = VersionedRegister::for_token("key", &call.lval.token); let mut instrs = Vec::new(); - let result_typ = Type::User(String::from("lookup_result")); - let result_alloc_instr = Statement::Alloc(Alloc { - target: result.to_reg(), - typ: result_typ.clone(), - }); - instrs.push(result_alloc_instr); - let mut total_key_size = 0; for (k, _) in &table.key { let info = hlir @@ -183,6 +174,12 @@ fn emit_table_apply_call( source: Value::number(0), })); + instrs.push(Statement::Rset(Rset { + target: sync_flag_reg.to_reg(), + typ: Type::Bitfield(128), + source: Value::number(sync_flag as i128), + })); + let mut offset = 0u128; for (k, _) in &table.key { let (key_extract_statements, extracted_value) = @@ -195,7 +192,7 @@ fn emit_table_apply_call( instrs.push(Statement::Shl(Shl { target: tmp.to_reg(), typ: key_typ.clone(), - source: Value::register(&extracted_value.registers[0].0), + source: Value::register(&extracted_value.registers[0].0 .0), amount: Value::number(offset as i128), })); @@ -206,7 +203,7 @@ fn emit_table_apply_call( source_a: Value::reg(curr_key.to_reg()), source_b: Value::reg(tmp.to_reg()), })); - offset += extracted_value.typ.bit_size().unwrap() as u128; + offset += extracted_value.registers[0].1.bit_size().unwrap() as u128; } let lookup_instr = Statement::Lookup(Lookup { @@ -225,13 +222,19 @@ fn emit_table_apply_call( }); instrs.push(lookup_instr); + let args_size = control.maximum_action_arg_length_for_table(ast, table); + Ok(( instrs, - Some(ExpressionValue::new_async( - result.to_reg(), - result_typ, - sync_flag, - )), + Some(ExpressionValue { + registers: vec![ + (hit.to_reg(), Type::Bool), + (variant.to_reg(), Type::Unsigned(16)), + (args.to_reg(), Type::Unsigned(args_size)), + (sync_flag_reg.to_reg(), Type::Bitfield(128)), + ], + sync_flag: Some(sync_flag), + }), )) } diff --git a/codegen/htq/src/lib.rs b/codegen/htq/src/lib.rs index a306e614..17243733 100644 --- a/codegen/htq/src/lib.rs +++ b/codegen/htq/src/lib.rs @@ -188,6 +188,12 @@ impl VersionedRegister { version: 0, } } + pub(crate) fn for_name(name: &str) -> Self { + Self { + reg: Register::new(name), + version: 0, + } + } #[allow(dead_code)] pub(crate) fn next(&mut self) -> &Self { @@ -210,7 +216,7 @@ impl VersionedRegister { // Codegen context // TODO more comprehensive .... -pub(crate) enum CgContext<'a> { +pub(crate) enum P4Context<'a> { #[allow(dead_code)] Parser(&'a p4::ast::Parser), Control(&'a p4::ast::Control), diff --git a/codegen/htq/src/parser.rs b/codegen/htq/src/parser.rs index 45ca9f09..9b74b204 100644 --- a/codegen/htq/src/parser.rs +++ b/codegen/htq/src/parser.rs @@ -1,10 +1,13 @@ // Copyright 2024 Oxide Computer Company +use std::collections::HashMap; + use crate::{ error::CodegenError, p4_type_to_htq_type, statement::emit_statement, - AsyncFlagAllocator, CgContext, RegisterAllocator, + AsyncFlagAllocator, P4Context, RegisterAllocator, }; -use p4::hlir::Hlir; +use htq::ast::Register; +use p4::{ast::ControlParameter, hlir::Hlir}; pub(crate) fn emit_parser_functions( ast: &p4::ast::AST, @@ -29,6 +32,7 @@ fn emit_parser( ) -> Result, CodegenError> { let mut result = Vec::new(); let mut parameters = Vec::new(); + let mut psub = HashMap::>::default(); let mut return_signature = Vec::new(); @@ -55,11 +59,12 @@ fn emit_parser( emit_statement( s, ast, - CgContext::Parser(parser), + P4Context::Parser(parser), hlir, &mut names, &mut ra, afa, + &mut psub, )? .into_iter(), ); diff --git a/codegen/htq/src/statement.rs b/codegen/htq/src/statement.rs index c569c6e6..fac4c9bd 100644 --- a/codegen/htq/src/statement.rs +++ b/codegen/htq/src/statement.rs @@ -5,32 +5,33 @@ use std::collections::HashMap; use htq::ast::{Fset, Register, Rset, Value}; use p4::{ ast::{ - DeclarationInfo, Direction, Expression, ExpressionKind, Lvalue, - NameInfo, UserDefinedType, + ControlParameter, DeclarationInfo, Direction, Expression, + ExpressionKind, Lvalue, NameInfo, UserDefinedType, }, hlir::Hlir, }; use crate::{ error::CodegenError, expression::emit_expression, AsyncFlagAllocator, - CgContext, RegisterAllocator, + P4Context, RegisterAllocator, VersionedRegister, }; pub(crate) fn emit_statement( stmt: &p4::ast::Statement, ast: &p4::ast::AST, - context: CgContext<'_>, + context: P4Context<'_>, hlir: &Hlir, names: &mut HashMap, ra: &mut RegisterAllocator, afa: &mut AsyncFlagAllocator, + psub: &mut HashMap>, ) -> Result, CodegenError> { use p4::ast::Statement as S; match stmt { S::Empty => Ok(Vec::new()), - S::Assignment(lval, expr) => { - emit_assignment(hlir, ast, context, names, lval, expr, ra, afa) - } + S::Assignment(lval, expr) => emit_assignment( + hlir, ast, context, names, lval, expr, ra, afa, psub, + ), S::Call(_call) => Ok(Vec::default()), //TODO S::If(_if_block) => Ok(Vec::default()), //TODO, S::Variable(_v) => Ok(Vec::default()), //TODO @@ -44,12 +45,13 @@ pub(crate) fn emit_statement( fn emit_assignment( hlir: &Hlir, ast: &p4::ast::AST, - context: CgContext<'_>, + context: P4Context<'_>, names: &mut HashMap, target: &p4::ast::Lvalue, source: &p4::ast::Expression, ra: &mut RegisterAllocator, afa: &mut AsyncFlagAllocator, + psub: &mut HashMap>, ) -> Result, CodegenError> { // Things that can be assigned to are lvalues with the following // declaration kinds. The table shows how each kind is referenced @@ -96,7 +98,7 @@ fn emit_assignment( // reg typ let (mut instrs, expr_value) = - emit_expression(source, hlir, ast, context, ra, afa, names)?; + emit_expression(source, hlir, ast, &context, ra, afa, names)?; let expr_value = expr_value.ok_or( CodegenError::AssignmentExpressionRequiresValue(source.clone()), @@ -111,17 +113,33 @@ fn emit_assignment( match &target_info.decl { DeclarationInfo::Parameter(Direction::Out) | DeclarationInfo::Parameter(Direction::InOut) => { - let _treg = Register::new(&target.name); + if target_info.ty.is_lookup_result() { + if let P4Context::Control(control) = &context { + if let Some(param) = control.get_parameter(target.root()) { + psub.insert( + param.clone(), + expr_value + .registers + .iter() + .map(|x| x.0.clone()) + .collect(), + ); + } + } + } // TODO store instr } DeclarationInfo::StructMember | DeclarationInfo::HeaderMember => { - let treg = Register::new(target.root()); + let treg = VersionedRegister::for_name(target.root()); + let mut output = treg.clone(); + output.next(); let offsets = member_offsets(ast, names, target)?; let instr = Fset { + output: output.to_reg(), offsets, - typ: expr_value.typ, - target: treg, - source: Value::Register(expr_value.registers[0].clone()), + typ: expr_value.registers[0].1.clone(), + target: treg.to_reg(), + source: Value::Register(expr_value.registers[0].0.clone()), }; instrs.push(htq::ast::Statement::Fset(instr)); } @@ -131,7 +149,7 @@ fn emit_assignment( .get(source) .ok_or(CodegenError::UntypedExpression(source.clone()))?; - //TODO(ry) it's unfortunate that a code generate manually has to + //TODO(ry) it's unfortunate that a codegen module 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 .... @@ -146,8 +164,8 @@ fn emit_assignment( let instr = Rset { target, - typ: expr_value.typ, - source: Value::Register(expr_value.registers[0].clone()), + typ: expr_value.registers[0].1.clone(), + source: Value::Register(expr_value.registers[0].0.clone()), }; instrs.push(htq::ast::Statement::Rset(instr)); } diff --git a/p4/src/ast.rs b/p4/src/ast.rs index f3c33dad..62acea75 100644 --- a/p4/src/ast.rs +++ b/p4/src/ast.rs @@ -293,7 +293,7 @@ impl PackageParameter { } } -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum Type { Bool, Error, @@ -1210,17 +1210,13 @@ impl Control { } if let Some(tbl) = self.get_table(call.lval.root()) { - for action in &tbl.actions { - let mut asize = 0; - let action = - self.get_action(&action.name).unwrap(); - for p in &action.parameters { - let psize = type_size(&p.ty, ast); - asize += psize; - } - size = usize::max(size, asize); - found = true; - } + size = usize::max( + self.maximum_action_arg_length_for_table( + ast, tbl, + ), + size, + ); + found = true; } else { continue; } @@ -1234,6 +1230,7 @@ impl Control { ast, &if_block.block, ) { + found = true; size = usize::max(size, sz); } for x in &if_block.else_ifs { @@ -1244,6 +1241,7 @@ impl Control { &x.block, ) { + found = true; size = usize::max(size, sz); } } @@ -1255,6 +1253,7 @@ impl Control { else_block, ) { + found = true; size = usize::max(size, sz); } } @@ -1268,6 +1267,24 @@ impl Control { None } } + + pub fn maximum_action_arg_length_for_table( + &self, + ast: &AST, + table: &Table, + ) -> usize { + let mut size = 0; + for action in &table.actions { + let mut asize = 0; + let action = self.get_action(&action.name).unwrap(); + for p in &action.parameters { + let psize = type_size(&p.ty, ast); + asize += psize; + } + size = usize::max(size, asize); + } + size + } } impl PartialEq for Control { @@ -1377,7 +1394,7 @@ impl Parser { } } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Hash, Eq)] pub struct ControlParameter { pub direction: Direction, pub ty: Type, @@ -1410,7 +1427,7 @@ impl ControlParameter { } } -#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum Direction { In, Out, From 817b115a5773b131bd123f305c8290286e173c7d Mon Sep 17 00:00:00 2001 From: Ryan Goodfellow Date: Thu, 19 Sep 2024 00:39:36 -0700 Subject: [PATCH 11/23] reticulating splines --- codegen/htq/src/control.rs | 86 ++++++++++++++++----- codegen/htq/src/error.rs | 6 ++ codegen/htq/src/expression.rs | 140 +++++++++++++++++++++++++++++----- codegen/htq/src/lib.rs | 29 ++++++- codegen/htq/src/statement.rs | 56 +++++++++++--- 5 files changed, 264 insertions(+), 53 deletions(-) diff --git a/codegen/htq/src/control.rs b/codegen/htq/src/control.rs index b18a00a5..35911c5f 100644 --- a/codegen/htq/src/control.rs +++ b/codegen/htq/src/control.rs @@ -32,29 +32,33 @@ fn emit_control( afa: &mut AsyncFlagAllocator, ) -> Result, CodegenError> { let mut result = Vec::default(); + let mut psub = HashMap::>::default(); let mut parameters = Vec::new(); - let mut return_signature = Vec::new(); + let mut apply_return_signature = Vec::new(); + let mut action_return_signature = Vec::new(); for x in &control.parameters { let typ = p4_type_to_htq_type(&x.ty)?; if x.direction.is_out() { // TODO the special case nature of lookup results is quite // unfortunate if x.ty.is_lookup_result() { - return_signature.push(htq::ast::Type::Bool); //hit - return_signature.push(htq::ast::Type::Unsigned(16)); //variant + apply_return_signature.push(htq::ast::Type::Bool); //hit + apply_return_signature.push(htq::ast::Type::Unsigned(16)); //variant let args_size = control .resolve_lookup_result_args_size(&x.name, ast) .ok_or(CodegenError::LookupResultArgSize(x.clone()))?; - return_signature.push(htq::ast::Type::Unsigned(args_size)); + apply_return_signature + .push(htq::ast::Type::Unsigned(args_size)); if x.ty.is_sync() { - return_signature.push(htq::ast::Type::Unsigned(128)); //async flag + apply_return_signature.push(htq::ast::Type::Unsigned(128)); //async flag } } else { - return_signature.push(typ.clone()); + apply_return_signature.push(typ.clone()); + action_return_signature.push(typ.clone()); } } let p = htq::ast::Parameter { @@ -69,8 +73,9 @@ fn emit_control( hlir, control, ¶meters, - &return_signature, + &apply_return_signature, afa, + &mut psub, )?); for action in &control.actions { @@ -80,8 +85,9 @@ fn emit_control( control, action, ¶meters, - &return_signature, + &action_return_signature, afa, + &mut psub, )?); } Ok(result) @@ -94,11 +100,11 @@ fn emit_control_apply( parameters: &[(Parameter, p4::ast::ControlParameter)], return_signature: &[htq::ast::Type], afa: &mut AsyncFlagAllocator, + psub: &mut HashMap>, ) -> Result { let mut ra = RegisterAllocator::default(); let mut names = control.names(); let mut statements = Vec::default(); - let mut psub = HashMap::>::default(); for (p, p4p) in parameters { if psub.get(p4p).is_some() { @@ -117,7 +123,7 @@ fn emit_control_apply( &mut names, &mut ra, afa, - &mut psub, + psub, )? .into_iter(), ) @@ -129,10 +135,15 @@ fn emit_control_apply( for (p, p4p) in parameters { if p4p.direction.is_out() { if let Some(substituted) = psub.get(p4p) { - return_registers.extend(substituted.clone().into_iter()); + return_registers.extend( + substituted + .clone() + .into_iter() + .map(|x| ra.get_reg(&x).unwrap()), + ); } else { signature.push(p.clone()); - return_registers.push(p.reg.clone()); + return_registers.push(ra.get_reg(&p.reg).unwrap()); } } } @@ -158,23 +169,31 @@ fn emit_control_action( parameters: &[(Parameter, p4::ast::ControlParameter)], return_signature: &[htq::ast::Type], afa: &mut AsyncFlagAllocator, + psub: &mut HashMap>, ) -> Result { let mut ra = RegisterAllocator::default(); let mut names = control.names(); let mut statements = Vec::default(); - let mut parameters: Vec = - parameters.to_owned().into_iter().map(|x| x.0).collect(); - let mut psub = HashMap::>::default(); + let parameters: Vec<(Parameter, p4::ast::ControlParameter)> = parameters + .iter() + .filter(|(_, p4p)| !p4p.ty.is_lookup_result()) + .cloned() + .collect(); + let mut action_parameters: Vec = + parameters.iter().cloned().map(|x| x.0).collect(); for x in &action.parameters { let p = htq::ast::Parameter { reg: htq::ast::Register::new(x.name.as_str()), typ: p4_type_to_htq_type(&x.ty)?, }; - parameters.push(p); + action_parameters.push(p); } - for p in ¶meters { + for (p, p4p) in ¶meters { + if psub.get(p4p).is_some() { + continue; + } ra.alloc(&p.reg.0); } @@ -188,15 +207,44 @@ fn emit_control_action( &mut names, &mut ra, afa, - &mut psub, + psub, )? .into_iter(), ) } + let mut signature = Vec::new(); + let mut return_registers = Vec::new(); + + for (p, p4p) in ¶meters { + if p4p.direction.is_out() { + if let Some(substituted) = psub.get(p4p) { + for x in substituted { + if let Some(r) = ra.get_reg(x) { + return_registers.push(r.clone()) + } + } + /* + return_registers.extend( + substituted + .clone() + .into_iter() + .map(|x| ra.get_reg(&x).unwrap()), + ); + */ + } else { + signature.push(p.clone()); + return_registers.push(ra.get_reg(&p.reg).unwrap()); + } + } + } + statements.push(htq::ast::Statement::Return(htq::ast::Return { + registers: return_registers, + })); + let f = htq::ast::Function { name: format!("{}_{}", control.name, action.name), - parameters, + parameters: action_parameters, statements, return_signature: return_signature.to_vec(), }; diff --git a/codegen/htq/src/error.rs b/codegen/htq/src/error.rs index 04b2cd46..1ae40221 100644 --- a/codegen/htq/src/error.rs +++ b/codegen/htq/src/error.rs @@ -84,6 +84,12 @@ pub enum CodegenError { #[error("could not determine lookup result arg size for \n{0:#?}")] LookupResultArgSize(ControlParameter), + + #[error("register does not exist for lvalue \n{0:#?}")] + RegisterDoesNotExistForLval(Lvalue), + + #[error("expected control type for \n{0:#?}\nfound \n{1:#?}")] + ExpectedControl(Lvalue, Type), } #[derive(Error, Debug)] diff --git a/codegen/htq/src/expression.rs b/codegen/htq/src/expression.rs index d6f355f0..bcfa5ae0 100644 --- a/codegen/htq/src/expression.rs +++ b/codegen/htq/src/expression.rs @@ -53,7 +53,7 @@ pub(crate) fn emit_expression( afa: &mut AsyncFlagAllocator, names: &HashMap, ) -> Result<(Vec, Option), CodegenError> { - let r = VersionedRegister::tmp_for_token(&expr.token); + let r = ra.alloc_tmp_for_token(&expr.token); match &expr.kind { ExpressionKind::BoolLit(value) => emit_bool_lit(*value, r), ExpressionKind::BitLit(width, value) => { @@ -76,7 +76,7 @@ pub(crate) fn emit_expression( } } -fn emit_call( +pub(crate) fn emit_call( call: &p4::ast::Call, control: &p4::ast::Control, hlir: &Hlir, @@ -239,13 +239,108 @@ fn emit_table_apply_call( } fn emit_control_apply_call( - _call: &p4::ast::Call, + call: &p4::ast::Call, _hlir: &Hlir, - _ast: &p4::ast::AST, - _ra: &mut RegisterAllocator, - _names: &HashMap, + ast: &p4::ast::AST, + ra: &mut RegisterAllocator, + names: &HashMap, ) -> Result<(Vec, Option), CodegenError> { - todo!("control apply call"); + // get the control being called + let info = names + .get(call.lval.root()) + .ok_or(CodegenError::CallParentNotFound(call.clone()))?; + + let control_type_name = match &info.ty { + p4::ast::Type::UserDefined(name, _) => name.to_owned(), + _x => { + return Err(CodegenError::ExpectedControl( + call.lval.clone(), + info.ty.clone(), + )) + } + }; + + let control = ast + .get_control(&control_type_name) + .ok_or(CodegenError::CallParentNotFound(call.clone()))?; + + // determine argument registers + let mut arg_values = Vec::default(); + for a in &call.args { + match &a.kind { + ExpressionKind::Lvalue(lval) => { + let info = names + .get(lval.root()) + .ok_or(CodegenError::UndefinedLvalue(lval.clone()))?; + if info.ty.is_lookup_result() { + continue; + } + let reg = ra.get(&lval.name).ok_or( + CodegenError::RegisterDoesNotExistForLval(lval.clone()), + )?; + arg_values.push(Value::Register(reg)); + } + ExpressionKind::BitLit(_width, value) => { + arg_values.push(Value::number(*value as i128)); + } + ExpressionKind::SignedLit(_width, value) => { + arg_values.push(Value::number(*value)); + } + ExpressionKind::IntegerLit(value) => { + arg_values.push(Value::number(*value)); + } + ExpressionKind::BoolLit(value) => { + arg_values.push(Value::number(*value as i128)); + } + _ => todo!("call argument type {:#?}", a.kind), + }; + } + + // determine return registers + let mut returned_registers = Vec::default(); + for (i, p) in control.parameters.iter().enumerate() { + if p.direction.is_out() { + if p.ty.is_lookup_result() { + let arg = match &call.args[i].kind { + ExpressionKind::Lvalue(lval) => lval.root().to_owned(), + _x => panic!("expected lvalue for out parameter"), + }; + let hit = ra.alloc(&format!("{}_hit", arg)); + returned_registers.push((hit, htq::ast::Type::Bool)); + + let variant = ra.alloc(&format!("{}_variant", arg)); + returned_registers + .push((variant, htq::ast::Type::Unsigned(16))); + + let args = ra.alloc(&format!("{}_args", arg)); + let args_size = control + .resolve_lookup_result_args_size(&p.name, ast) + .ok_or(CodegenError::LookupResultArgSize(p.clone()))?; + returned_registers + .push((args, htq::ast::Type::Bitfield(args_size))); + + let sync = ra.alloc(&format!("{}_sync", arg)); + returned_registers.push((sync, htq::ast::Type::Bitfield(128))); + } else { + returned_registers + .push((ra.alloc(&p.name), p4_type_to_htq_type(&p.ty)?)) + } + } + } + + let call_stmt = Statement::Call(htq::ast::Call { + fname: format!("{}_{}", call.lval.root(), call.lval.leaf()), + args: arg_values, + targets: returned_registers.iter().map(|x| x.0.clone()).collect(), + }); + + Ok(( + vec![call_stmt], + Some(ExpressionValue { + registers: returned_registers, + sync_flag: None, + }), + )) } fn emit_set_valid_call( @@ -256,7 +351,9 @@ fn emit_set_valid_call( _names: &HashMap, _valid: bool, ) -> Result<(Vec, Option), CodegenError> { - todo!("set valid call") + //TODO + Ok((Vec::default(), None)) + //todo!("set valid call") } fn emit_is_valid_call( @@ -266,7 +363,8 @@ fn emit_is_valid_call( _ra: &mut RegisterAllocator, _names: &HashMap, ) -> Result<(Vec, Option), CodegenError> { - todo!("is valid call") + //TODO + Ok((Vec::default(), None)) } fn emit_extern_call( @@ -356,20 +454,20 @@ fn emit_lval( pub(crate) fn emit_bool_lit( value: bool, - ra: VersionedRegister, + ra: Register, ) -> Result<(Vec, Option), CodegenError> { let instrs = vec![Statement::Rset(Rset { - target: ra.clone().to_reg(), + target: ra.clone(), typ: Type::Bool, source: Value::bool(value), })]; - Ok((instrs, Some(ExpressionValue::new(ra.to_reg(), Type::Bool)))) + Ok((instrs, Some(ExpressionValue::new(ra, Type::Bool)))) } pub(crate) fn emit_bit_lit( width: u16, value: u128, - ra: VersionedRegister, + ra: Register, expr: &Expression, ) -> Result<(Vec, Option), CodegenError> { let value = i128::try_from(value) @@ -377,35 +475,35 @@ pub(crate) fn emit_bit_lit( let typ = Type::Bitfield(usize::from(width)); let instrs = vec![Statement::Rset(Rset { typ: typ.clone(), - target: ra.clone().to_reg(), + target: ra.clone(), source: Value::number(value), })]; - Ok((instrs, Some(ExpressionValue::new(ra.to_reg(), typ)))) + Ok((instrs, Some(ExpressionValue::new(ra, typ)))) } pub(crate) fn emit_int_lit( value: i128, - ra: VersionedRegister, + ra: Register, ) -> Result<(Vec, Option), CodegenError> { let typ = Type::Signed(128); let instrs = vec![Statement::Rset(Rset { typ: typ.clone(), - target: ra.clone().to_reg(), + target: ra.clone(), source: Value::number(value), })]; - Ok((instrs, Some(ExpressionValue::new(ra.to_reg(), typ)))) + Ok((instrs, Some(ExpressionValue::new(ra, typ)))) } pub(crate) fn emit_signed_lit( width: u16, value: i128, - ra: VersionedRegister, + ra: Register, ) -> Result<(Vec, Option), CodegenError> { let typ = Type::Signed(usize::from(width)); let instrs = vec![Statement::Rset(Rset { typ: typ.clone(), - target: ra.clone().to_reg(), + target: ra.clone(), source: Value::number(value), })]; - Ok((instrs, Some(ExpressionValue::new(ra.to_reg(), typ)))) + Ok((instrs, Some(ExpressionValue::new(ra, typ)))) } diff --git a/codegen/htq/src/lib.rs b/codegen/htq/src/lib.rs index 17243733..f31cb7d1 100644 --- a/codegen/htq/src/lib.rs +++ b/codegen/htq/src/lib.rs @@ -1,5 +1,7 @@ // Copyright 2024 Oxide Computer Company +#![allow(clippy::too_many_arguments)] + use std::{collections::HashMap, io::Write}; use control::emit_control_functions; @@ -140,10 +142,30 @@ impl RegisterAllocator { } } + pub(crate) fn alloc_next(&mut self, reg: &Register) -> htq::ast::Register { + self.alloc(®.0) + } + + pub(crate) fn alloc_tmp_for_token( + &mut self, + tk: &Token, + ) -> htq::ast::Register { + let name = format!("tmp{}_{}", tk.line, tk.col); + self.alloc(&name) + } + pub(crate) fn get(&self, name: &str) -> Option { - self.data - .get(name) - .map(|rev| htq::ast::Register::new(&format!("{}.{}", name, rev))) + self.data.get(name).map(|rev| { + if *rev > 0 { + htq::ast::Register::new(&format!("{}.{}", name, rev)) + } else { + htq::ast::Register::new(name) + } + }) + } + + pub(crate) fn get_reg(&self, reg: &Register) -> Option { + self.get(®.0) } } @@ -188,6 +210,7 @@ impl VersionedRegister { version: 0, } } + #[allow(dead_code)] pub(crate) fn for_name(name: &str) -> Self { Self { reg: Register::new(name), diff --git a/codegen/htq/src/statement.rs b/codegen/htq/src/statement.rs index fac4c9bd..e27a0bed 100644 --- a/codegen/htq/src/statement.rs +++ b/codegen/htq/src/statement.rs @@ -13,7 +13,7 @@ use p4::{ use crate::{ error::CodegenError, expression::emit_expression, AsyncFlagAllocator, - P4Context, RegisterAllocator, VersionedRegister, + P4Context, RegisterAllocator, }; pub(crate) fn emit_statement( @@ -32,15 +32,52 @@ pub(crate) fn emit_statement( S::Assignment(lval, expr) => emit_assignment( hlir, ast, context, names, lval, expr, ra, afa, psub, ), - S::Call(_call) => Ok(Vec::default()), //TODO + S::Call(call) => { + emit_call(hlir, ast, context, names, call, ra, afa, psub) + } S::If(_if_block) => Ok(Vec::default()), //TODO, - S::Variable(_v) => Ok(Vec::default()), //TODO + S::Variable(v) => { + //TODO(ry) it's unfortunate that a codegen module 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( + v.name.clone(), + NameInfo { + ty: v.ty.clone(), + decl: DeclarationInfo::Local, + }, + ); + Ok(Vec::default()) + } S::Constant(_c) => Ok(Vec::default()), //TODO S::Transition(_t) => Ok(Vec::default()), //TODO - S::Return(_r) => Ok(Vec::default()), //TODO + S::Return(_r) => Ok(Vec::default()), //TODO } } +fn emit_call( + hlir: &Hlir, + ast: &p4::ast::AST, + context: P4Context<'_>, + names: &mut HashMap, + call: &p4::ast::Call, + ra: &mut RegisterAllocator, + afa: &mut AsyncFlagAllocator, + _psub: &mut HashMap>, +) -> Result, CodegenError> { + let (instrs, _result) = match &context { + P4Context::Control(c) => { + crate::expression::emit_call(call, c, hlir, ast, ra, afa, names)? + } + P4Context::Parser(_) => { + //TODO + (Vec::default(), None) + } + }; + Ok(instrs) +} + #[allow(clippy::too_many_arguments)] fn emit_assignment( hlir: &Hlir, @@ -121,7 +158,7 @@ fn emit_assignment( expr_value .registers .iter() - .map(|x| x.0.clone()) + .map(|x| ra.alloc_next(&x.0)) .collect(), ); } @@ -130,15 +167,14 @@ fn emit_assignment( // TODO store instr } DeclarationInfo::StructMember | DeclarationInfo::HeaderMember => { - let treg = VersionedRegister::for_name(target.root()); - let mut output = treg.clone(); - output.next(); + let treg = ra.alloc(target.root()); + let output = ra.alloc(target.root()); let offsets = member_offsets(ast, names, target)?; let instr = Fset { - output: output.to_reg(), + output, offsets, typ: expr_value.registers[0].1.clone(), - target: treg.to_reg(), + target: treg, source: Value::Register(expr_value.registers[0].0.clone()), }; instrs.push(htq::ast::Statement::Fset(instr)); From eeb8f1d83f08885092988a9b5ddcdc4dd6715bdc Mon Sep 17 00:00:00 2001 From: Ryan Goodfellow Date: Thu, 19 Sep 2024 18:52:20 -0700 Subject: [PATCH 12/23] reticulating splines --- codegen/htq/src/statement.rs | 54 ++++++++++++++++++++++++++++-------- 1 file changed, 42 insertions(+), 12 deletions(-) diff --git a/codegen/htq/src/statement.rs b/codegen/htq/src/statement.rs index e27a0bed..98cf70df 100644 --- a/codegen/htq/src/statement.rs +++ b/codegen/htq/src/statement.rs @@ -37,18 +37,7 @@ pub(crate) fn emit_statement( } S::If(_if_block) => Ok(Vec::default()), //TODO, S::Variable(v) => { - //TODO(ry) it's unfortunate that a codegen module 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( - v.name.clone(), - NameInfo { - ty: v.ty.clone(), - decl: DeclarationInfo::Local, - }, - ); - Ok(Vec::default()) + emit_variable(hlir, ast, context, names, v, ra, afa, psub) } S::Constant(_c) => Ok(Vec::default()), //TODO S::Transition(_t) => Ok(Vec::default()), //TODO @@ -56,6 +45,47 @@ pub(crate) fn emit_statement( } } +fn emit_variable( + _hlir: &Hlir, + _ast: &p4::ast::AST, + _context: P4Context<'_>, + names: &mut HashMap, + var: &p4::ast::Variable, + ra: &mut RegisterAllocator, + _afa: &mut AsyncFlagAllocator, + _psub: &mut HashMap>, +) -> Result, CodegenError> { + //TODO(ry) it's unfortunate that a codegen module 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( + var.name.clone(), + NameInfo { + ty: var.ty.clone(), + decl: DeclarationInfo::Local, + }, + ); + + if let Some(init) = &var.initializer { + if let ExpressionKind::Lvalue(lval) = &init.kind { + // TODO this could be modeled better (more explicitly) in the + // AST + if lval.leaf() == "await" { + return Ok(vec![htq::ast::Statement::Await(htq::ast::Await { + source: Value::reg( + // TODO this is extermely fragile relying on a + // register naming convention. + ra.get(&format!("{}_sync", lval.root())).unwrap(), + ), + })]); + } + } + } + + Ok(Vec::default()) +} + fn emit_call( hlir: &Hlir, ast: &p4::ast::AST, From 6cbb31d4d715e92c72b579efb487d3408fc42abb Mon Sep 17 00:00:00 2001 From: Ryan Goodfellow Date: Sat, 21 Sep 2024 11:53:46 -0700 Subject: [PATCH 13/23] reticulating pblocks --- codegen/htq/src/control.rs | 15 ++++- codegen/htq/src/error.rs | 10 ++++ codegen/htq/src/expression.rs | 104 +++++++++++++++++++++++++++++++--- codegen/htq/src/lib.rs | 37 +++++++----- codegen/htq/src/parser.rs | 3 +- codegen/htq/src/statement.rs | 82 +++++++++++++++++++++++++-- p4/src/lexer.rs | 11 ++-- 7 files changed, 228 insertions(+), 34 deletions(-) diff --git a/codegen/htq/src/control.rs b/codegen/htq/src/control.rs index 35911c5f..1a69699c 100644 --- a/codegen/htq/src/control.rs +++ b/codegen/htq/src/control.rs @@ -43,8 +43,10 @@ fn emit_control( // TODO the special case nature of lookup results is quite // unfortunate if x.ty.is_lookup_result() { - apply_return_signature.push(htq::ast::Type::Bool); //hit - apply_return_signature.push(htq::ast::Type::Unsigned(16)); //variant + //hit + apply_return_signature.push(htq::ast::Type::Bool); + //variant + apply_return_signature.push(htq::ast::Type::Unsigned(16)); let args_size = control .resolve_lookup_result_args_size(&x.name, ast) @@ -54,7 +56,8 @@ fn emit_control( .push(htq::ast::Type::Unsigned(args_size)); if x.ty.is_sync() { - apply_return_signature.push(htq::ast::Type::Unsigned(128)); //async flag + //async flag + apply_return_signature.push(htq::ast::Type::Unsigned(128)); } } else { apply_return_signature.push(typ.clone()); @@ -152,6 +155,12 @@ fn emit_control_apply( registers: return_registers, })); + statements.sort_by(|a, b| { + matches!(a, htq::ast::Statement::Label(_, _)) + .partial_cmp(&matches!(b, htq::ast::Statement::Label(_, _))) + .unwrap() + }); + let f = htq::ast::Function { name: format!("{}_apply", control.name), parameters: signature, diff --git a/codegen/htq/src/error.rs b/codegen/htq/src/error.rs index 1ae40221..27a5e58f 100644 --- a/codegen/htq/src/error.rs +++ b/codegen/htq/src/error.rs @@ -1,5 +1,6 @@ // Copyright 2024 Oxide Computer Company +use htq::ast::Register; use p4::{ ast::{ Call, Control, ControlParameter, DeclarationInfo, Expression, Lvalue, @@ -90,6 +91,15 @@ pub enum CodegenError { #[error("expected control type for \n{0:#?}\nfound \n{1:#?}")] ExpectedControl(Lvalue, Type), + + #[error("a value is required for expression \n{0:#?}")] + ExpressionValueNeeded(Expression), + + #[error("a singular value is required for expression \n{0:#?}")] + SingularExpressionValueNeeded(Expression), + + #[error("missing register for lvalue, this is a compiler bug \n{0:#?}\ncurrent registers: \n{1:#?}")] + MissingRegisterForLvalue(Lvalue, Vec), } #[derive(Error, Debug)] diff --git a/codegen/htq/src/expression.rs b/codegen/htq/src/expression.rs index bcfa5ae0..00f943fe 100644 --- a/codegen/htq/src/expression.rs +++ b/codegen/htq/src/expression.rs @@ -5,7 +5,7 @@ use htq::ast::{ }; use p4::{ ast::{ - type_size, DeclarationInfo, Expression, ExpressionKind, Lvalue, + type_size, BinOp, DeclarationInfo, Expression, ExpressionKind, Lvalue, NameInfo, }, hlir::Hlir, @@ -72,10 +72,79 @@ pub(crate) fn emit_expression( Err(CodegenError::CallInParser(expr.clone())) } }, + ExpressionKind::Binary(lhs, op, rhs) => emit_binary_expr( + lhs.as_ref(), + op, + rhs.as_ref(), + hlir, + ast, + ra, + afa, + names, + ), xpr => todo!("expression: {xpr:?}"), } } +pub(crate) fn emit_single_valued_expression( + expr: &Expression, + hlir: &Hlir, + ast: &p4::ast::AST, + context: &P4Context<'_>, + ra: &mut RegisterAllocator, + afa: &mut AsyncFlagAllocator, + names: &HashMap, +) -> Result<(Vec, Register), CodegenError> { + let (stmts, val) = + emit_expression(expr, hlir, ast, context, ra, afa, names)?; + + let val = val.ok_or(CodegenError::ExpressionValueNeeded(expr.clone()))?; + if val.registers.len() != 1 { + return Err(CodegenError::SingularExpressionValueNeeded(expr.clone())); + } + + Ok((stmts, val.registers[0].0.clone())) +} + +pub(crate) fn emit_binary_expr( + lhs: &Expression, + op: &BinOp, + rhs: &Expression, + hlir: &Hlir, + ast: &p4::ast::AST, + ra: &mut RegisterAllocator, + afa: &mut AsyncFlagAllocator, + names: &HashMap, +) -> Result<(Vec, Option), CodegenError> { + match op { + BinOp::Add => todo!("bin op add"), + BinOp::Subtract => todo!("bin op subtract"), + BinOp::Mod => todo!("bin op mod"), + BinOp::Geq => todo!("bin op geq"), + BinOp::Gt => todo!("bin op gt"), + BinOp::Leq => todo!("bin op leq"), + BinOp::Lt => todo!("bin op lt"), + BinOp::Eq => emit_binary_expr_eq(lhs, rhs, hlir, ast, ra, afa, names), + BinOp::Mask => todo!("bin op mask"), + BinOp::NotEq => todo!("bin op not eq"), + BinOp::BitAnd => todo!("bin op bit and"), + BinOp::BitOr => todo!("bin op bit or"), + BinOp::Xor => todo!("bin op xor"), + } +} + +pub(crate) fn emit_binary_expr_eq( + _lhs: &Expression, + _rhs: &Expression, + _hlir: &Hlir, + _ast: &p4::ast::AST, + _ra: &mut RegisterAllocator, + _afa: &mut AsyncFlagAllocator, + _names: &HashMap, +) -> Result<(Vec, Option), CodegenError> { + todo!() +} + pub(crate) fn emit_call( call: &p4::ast::Call, control: &p4::ast::Control, @@ -429,10 +498,22 @@ fn emit_lval( let offsets = member_offsets(ast, names, lval)?; let treg = VersionedRegister::tmp_for_token(&lval.token); - let src_root = lval.root(); - let source = ra - .get(src_root) - .ok_or(CodegenError::UndefinedLvalue(lval.clone()))?; + // TODO this is terrible, we should have one way to lookup the + // register + let name = lval.root(); + let source = if let Ok(source) = + ra.get(name).ok_or(CodegenError::MissingRegisterForLvalue( + lval.clone(), + ra.all_registers(), + )) { + source + } else { + let name = reg_name_for_lval(lval); + ra.get(&name).ok_or(CodegenError::MissingRegisterForLvalue( + lval.clone(), + ra.all_registers(), + ))? + }; result.push(Statement::Fget(Fget { target: treg.to_reg(), @@ -443,15 +524,22 @@ fn emit_lval( Ok((result, Some(ExpressionValue::new(treg.to_reg(), typ)))) } DeclarationInfo::Local => { - let reg = ra - .get(&lval.name) - .ok_or(CodegenError::UndefinedLvalue(lval.clone()))?; + let name = reg_name_for_lval(lval); + let reg = + ra.get(&name).ok_or(CodegenError::MissingRegisterForLvalue( + lval.clone(), + ra.all_registers(), + ))?; Ok((result, Some(ExpressionValue::new(reg, typ)))) } other => todo!("emit lval for \n{other:#?}"), } } +fn reg_name_for_lval(lval: &Lvalue) -> String { + lval.name.replace('.', "_") +} + pub(crate) fn emit_bool_lit( value: bool, ra: Register, diff --git a/codegen/htq/src/lib.rs b/codegen/htq/src/lib.rs index f31cb7d1..212eca92 100644 --- a/codegen/htq/src/lib.rs +++ b/codegen/htq/src/lib.rs @@ -129,20 +129,20 @@ pub(crate) struct RegisterAllocator { } impl RegisterAllocator { - pub(crate) fn alloc(&mut self, name: &str) -> htq::ast::Register { + pub(crate) fn alloc(&mut self, name: &str) -> Register { match self.data.get_mut(name) { Some(rev) => { *rev += 1; - htq::ast::Register::new(&format!("{}.{}", name, *rev)) + Register::new(&format!("{}.{}", name, *rev)) } None => { self.data.insert(name.to_owned(), 0); - htq::ast::Register::new(name) + Register::new(name) } } } - pub(crate) fn alloc_next(&mut self, reg: &Register) -> htq::ast::Register { + pub(crate) fn alloc_next(&mut self, reg: &Register) -> Register { self.alloc(®.0) } @@ -154,19 +154,30 @@ impl RegisterAllocator { self.alloc(&name) } - pub(crate) fn get(&self, name: &str) -> Option { - self.data.get(name).map(|rev| { - if *rev > 0 { - htq::ast::Register::new(&format!("{}.{}", name, rev)) - } else { - htq::ast::Register::new(name) - } - }) + pub(crate) fn get(&self, name: &str) -> Option { + self.data + .get(name) + .map(|rev| Self::register_name(name, *rev)) } - pub(crate) fn get_reg(&self, reg: &Register) -> Option { + pub(crate) fn get_reg(&self, reg: &Register) -> Option { self.get(®.0) } + + pub(crate) fn all_registers(&self) -> Vec { + self.data + .iter() + .map(|(name, rev)| Self::register_name(name, *rev)) + .collect() + } + + fn register_name(name: &str, rev: usize) -> Register { + if rev > 0 { + Register::new(&format!("{}.{}", name, rev)) + } else { + Register::new(name) + } + } } /// The async flag allocator allocates bitmap entries for asynchronous diff --git a/codegen/htq/src/parser.rs b/codegen/htq/src/parser.rs index 9b74b204..081d38d4 100644 --- a/codegen/htq/src/parser.rs +++ b/codegen/htq/src/parser.rs @@ -33,6 +33,7 @@ fn emit_parser( let mut result = Vec::new(); let mut parameters = Vec::new(); let mut psub = HashMap::>::default(); + let mut ra = RegisterAllocator::default(); let mut return_signature = Vec::new(); @@ -45,6 +46,7 @@ fn emit_parser( reg: htq::ast::Register::new(x.name.as_str()), typ, }; + ra.alloc(&p.reg.0); parameters.push(p); } @@ -52,7 +54,6 @@ fn emit_parser( for state in &parser.states { // 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( diff --git a/codegen/htq/src/statement.rs b/codegen/htq/src/statement.rs index 98cf70df..b553d0cf 100644 --- a/codegen/htq/src/statement.rs +++ b/codegen/htq/src/statement.rs @@ -2,18 +2,19 @@ use std::collections::HashMap; -use htq::ast::{Fset, Register, Rset, Value}; +use htq::ast::{Beq, Fset, Register, Rset, Value}; use p4::{ ast::{ - ControlParameter, DeclarationInfo, Direction, Expression, + BinOp, ControlParameter, DeclarationInfo, Direction, Expression, ExpressionKind, Lvalue, NameInfo, UserDefinedType, }, hlir::Hlir, }; use crate::{ - error::CodegenError, expression::emit_expression, AsyncFlagAllocator, - P4Context, RegisterAllocator, + error::CodegenError, + expression::{emit_expression, emit_single_valued_expression}, + AsyncFlagAllocator, P4Context, RegisterAllocator, }; pub(crate) fn emit_statement( @@ -35,7 +36,9 @@ pub(crate) fn emit_statement( S::Call(call) => { emit_call(hlir, ast, context, names, call, ra, afa, psub) } - S::If(_if_block) => Ok(Vec::default()), //TODO, + S::If(if_block) => { + emit_if_block(hlir, ast, context, names, if_block, ra, afa, psub) + } S::Variable(v) => { emit_variable(hlir, ast, context, names, v, ra, afa, psub) } @@ -45,6 +48,75 @@ pub(crate) fn emit_statement( } } +fn emit_if_block( + hlir: &Hlir, + ast: &p4::ast::AST, + context: P4Context<'_>, + names: &mut HashMap, + iblk: &p4::ast::IfBlock, + ra: &mut RegisterAllocator, + afa: &mut AsyncFlagAllocator, + _psub: &mut HashMap>, +) -> Result, CodegenError> { + let mut result = Vec::default(); + + let (source, predicate) = + if let ExpressionKind::Binary(lhs, BinOp::Eq, rhs) = + &iblk.predicate.kind + { + let (source_statements, source) = emit_single_valued_expression( + lhs.as_ref(), + hlir, + ast, + &context, + ra, + afa, + names, + )?; + result.extend(source_statements.clone()); + + let (predicate_statements, predicate) = + emit_single_valued_expression( + rhs.as_ref(), + hlir, + ast, + &context, + ra, + afa, + names, + )?; + result.extend(predicate_statements.clone()); + + (source, Value::reg(predicate)) + } else { + let (predicate_statements, source) = emit_single_valued_expression( + iblk.predicate.as_ref(), + hlir, + ast, + &context, + ra, + afa, + names, + )?; + result.extend(predicate_statements.clone()); + (source, htq::ast::Value::bool(true)) + }; + + let params = ra.all_registers(); + let args = params.clone().into_iter().map(Value::reg).collect(); + let label = format!("{}_hit", source.0); + result.push(htq::ast::Statement::Beq(Beq { + source, + predicate, + label: label.clone(), + args, + })); + + result.push(htq::ast::Statement::Label(label, params)); + + Ok(result) +} + fn emit_variable( _hlir: &Hlir, _ast: &p4::ast::AST, diff --git a/p4/src/lexer.rs b/p4/src/lexer.rs index fe8dd886..446a74ac 100644 --- a/p4/src/lexer.rs +++ b/p4/src/lexer.rs @@ -322,8 +322,8 @@ impl<'a> Lexer<'a> { }); } - while self.skip_whitespace() {} - while self.skip_comment() {} + while self.skip_whitespace_or_comment() {} + //while self.skip_comment() {} if self.line >= self.lines.len() { return Ok(Token { kind: Kind::Eof, @@ -332,8 +332,7 @@ impl<'a> Lexer<'a> { file: self.file.clone(), }); } - self.skip_whitespace(); - //self.skip_comment(); + self.skip_whitespace_or_comment(); if let Some(t) = self.match_token("#include", Kind::PoundInclude) { return Ok(t); @@ -870,6 +869,10 @@ impl<'a> Lexer<'a> { false } + fn skip_whitespace_or_comment(&mut self) -> bool { + self.skip_whitespace() || self.skip_comment() + } + fn skip_block_comment(&mut self) { let mut chars = self.cursor.chars(); match chars.next() { From a0f7cb2e42b88a6e3d0aeb4bb68e4b419dc2e18f Mon Sep 17 00:00:00 2001 From: Ryan Goodfellow Date: Sat, 21 Sep 2024 22:45:08 -0700 Subject: [PATCH 14/23] reticulating blocks --- codegen/htq/src/control.rs | 54 ++++++++++++------------ codegen/htq/src/expression.rs | 3 +- codegen/htq/src/parser.rs | 27 ++++++------ codegen/htq/src/statement.rs | 78 ++++++++++++++++++++++++----------- 4 files changed, 98 insertions(+), 64 deletions(-) diff --git a/codegen/htq/src/control.rs b/codegen/htq/src/control.rs index 1a69699c..790e9c67 100644 --- a/codegen/htq/src/control.rs +++ b/codegen/htq/src/control.rs @@ -108,6 +108,7 @@ fn emit_control_apply( let mut ra = RegisterAllocator::default(); let mut names = control.names(); let mut statements = Vec::default(); + let mut blocks = Vec::default(); for (p, p4p) in parameters { if psub.get(p4p).is_some() { @@ -117,19 +118,18 @@ fn emit_control_apply( } for s in &control.apply.statements { - statements.extend( - emit_statement( - s, - ast, - P4Context::Control(control), - hlir, - &mut names, - &mut ra, - afa, - psub, - )? - .into_iter(), - ) + let (stmts, blks) = emit_statement( + s, + ast, + &P4Context::Control(control), + hlir, + &mut names, + &mut ra, + afa, + psub, + )?; + statements.extend(stmts); + blocks.extend(blks); } let mut signature = Vec::new(); @@ -165,6 +165,7 @@ fn emit_control_apply( name: format!("{}_apply", control.name), parameters: signature, statements, + blocks, return_signature: return_signature.to_vec(), }; Ok(f) @@ -183,6 +184,7 @@ fn emit_control_action( let mut ra = RegisterAllocator::default(); let mut names = control.names(); let mut statements = Vec::default(); + let mut blocks = Vec::default(); let parameters: Vec<(Parameter, p4::ast::ControlParameter)> = parameters .iter() .filter(|(_, p4p)| !p4p.ty.is_lookup_result()) @@ -207,19 +209,18 @@ fn emit_control_action( } for s in &action.statement_block.statements { - statements.extend( - emit_statement( - s, - ast, - P4Context::Control(control), - hlir, - &mut names, - &mut ra, - afa, - psub, - )? - .into_iter(), - ) + let (stmts, blks) = emit_statement( + s, + ast, + &P4Context::Control(control), + hlir, + &mut names, + &mut ra, + afa, + psub, + )?; + statements.extend(stmts); + blocks.extend(blks); } let mut signature = Vec::new(); @@ -255,6 +256,7 @@ fn emit_control_action( name: format!("{}_{}", control.name, action.name), parameters: action_parameters, statements, + blocks, return_signature: return_signature.to_vec(), }; Ok(f) diff --git a/codegen/htq/src/expression.rs b/codegen/htq/src/expression.rs index 00f943fe..13ef385c 100644 --- a/codegen/htq/src/expression.rs +++ b/codegen/htq/src/expression.rs @@ -443,7 +443,8 @@ fn emit_extern_call( _ra: &mut RegisterAllocator, _names: &HashMap, ) -> Result<(Vec, Option), CodegenError> { - todo!("extern call") + //TODO + Ok((Vec::default(), None)) } fn emit_action_call( diff --git a/codegen/htq/src/parser.rs b/codegen/htq/src/parser.rs index 081d38d4..a53c172e 100644 --- a/codegen/htq/src/parser.rs +++ b/codegen/htq/src/parser.rs @@ -55,25 +55,26 @@ fn emit_parser( for state in &parser.states { // keeps track of register revisions for locals let mut statements = Vec::default(); + let mut blocks = Vec::default(); for s in &state.statements.statements { - statements.extend( - emit_statement( - s, - ast, - P4Context::Parser(parser), - hlir, - &mut names, - &mut ra, - afa, - &mut psub, - )? - .into_iter(), - ); + let (stmts, blks) = emit_statement( + s, + ast, + &P4Context::Parser(parser), + hlir, + &mut names, + &mut ra, + afa, + &mut psub, + )?; + statements.extend(stmts); + blocks.extend(blks); } let f = htq::ast::Function { name: format!("{}_{}", parser.name, state.name), parameters: parameters.clone(), statements, + blocks, return_signature: return_signature.clone(), }; result.push(f); diff --git a/codegen/htq/src/statement.rs b/codegen/htq/src/statement.rs index b553d0cf..52bf8e3f 100644 --- a/codegen/htq/src/statement.rs +++ b/codegen/htq/src/statement.rs @@ -2,7 +2,7 @@ use std::collections::HashMap; -use htq::ast::{Beq, Fset, Register, Rset, Value}; +use htq::ast::{Beq, Fset, Register, Rset, StatementBlock, Value}; use p4::{ ast::{ BinOp, ControlParameter, DeclarationInfo, Direction, Expression, @@ -20,45 +20,59 @@ use crate::{ pub(crate) fn emit_statement( stmt: &p4::ast::Statement, ast: &p4::ast::AST, - context: P4Context<'_>, + context: &P4Context<'_>, hlir: &Hlir, names: &mut HashMap, ra: &mut RegisterAllocator, afa: &mut AsyncFlagAllocator, psub: &mut HashMap>, -) -> Result, CodegenError> { +) -> Result< + (Vec, Vec), + CodegenError, +> { use p4::ast::Statement as S; match stmt { - S::Empty => Ok(Vec::new()), - S::Assignment(lval, expr) => emit_assignment( - hlir, ast, context, names, lval, expr, ra, afa, psub, - ), + S::Empty => Ok((Vec::default(), Vec::default())), + S::Assignment(lval, expr) => { + let stmts = emit_assignment( + hlir, ast, context, names, lval, expr, ra, afa, psub, + )?; + Ok((stmts, Vec::default())) + } S::Call(call) => { - emit_call(hlir, ast, context, names, call, ra, afa, psub) + let stmts = + emit_call(hlir, ast, context, names, call, ra, afa, psub)?; + Ok((stmts, Vec::default())) } S::If(if_block) => { emit_if_block(hlir, ast, context, names, if_block, ra, afa, psub) } S::Variable(v) => { - emit_variable(hlir, ast, context, names, v, ra, afa, psub) + let stmts = + emit_variable(hlir, ast, context, names, v, ra, afa, psub)?; + Ok((stmts, Vec::default())) } - S::Constant(_c) => Ok(Vec::default()), //TODO - S::Transition(_t) => Ok(Vec::default()), //TODO - S::Return(_r) => Ok(Vec::default()), //TODO + S::Constant(_c) => Ok((Vec::default(), Vec::default())), //TODO + S::Transition(_t) => Ok((Vec::default(), Vec::default())), //TODO + S::Return(_r) => Ok((Vec::default(), Vec::default())), //TODO } } fn emit_if_block( hlir: &Hlir, ast: &p4::ast::AST, - context: P4Context<'_>, + context: &P4Context<'_>, names: &mut HashMap, iblk: &p4::ast::IfBlock, ra: &mut RegisterAllocator, afa: &mut AsyncFlagAllocator, - _psub: &mut HashMap>, -) -> Result, CodegenError> { + psub: &mut HashMap>, +) -> Result< + (Vec, Vec), + CodegenError, +> { let mut result = Vec::default(); + let mut blocks = Vec::default(); let (source, predicate) = if let ExpressionKind::Binary(lhs, BinOp::Eq, rhs) = @@ -68,7 +82,7 @@ fn emit_if_block( lhs.as_ref(), hlir, ast, - &context, + context, ra, afa, names, @@ -80,7 +94,7 @@ fn emit_if_block( rhs.as_ref(), hlir, ast, - &context, + context, ra, afa, names, @@ -93,7 +107,7 @@ fn emit_if_block( iblk.predicate.as_ref(), hlir, ast, - &context, + context, ra, afa, names, @@ -112,15 +126,31 @@ fn emit_if_block( args, })); - result.push(htq::ast::Statement::Label(label, params)); + let mut blk = StatementBlock { + name: label, + parameters: params, + statements: Vec::default(), + }; + + for stmt in &iblk.block.statements { + // TODO nested if blocks + let (stmts, _) = + emit_statement(stmt, ast, context, hlir, names, ra, afa, psub)?; + blk.statements.extend(stmts); + } + + // XXX + //result.push(htq::ast::Statement::Label(label, params)); + + blocks.push(blk); - Ok(result) + Ok((result, blocks)) } fn emit_variable( _hlir: &Hlir, _ast: &p4::ast::AST, - _context: P4Context<'_>, + _context: &P4Context<'_>, names: &mut HashMap, var: &p4::ast::Variable, ra: &mut RegisterAllocator, @@ -161,7 +191,7 @@ fn emit_variable( fn emit_call( hlir: &Hlir, ast: &p4::ast::AST, - context: P4Context<'_>, + context: &P4Context<'_>, names: &mut HashMap, call: &p4::ast::Call, ra: &mut RegisterAllocator, @@ -184,7 +214,7 @@ fn emit_call( fn emit_assignment( hlir: &Hlir, ast: &p4::ast::AST, - context: P4Context<'_>, + context: &P4Context<'_>, names: &mut HashMap, target: &p4::ast::Lvalue, source: &p4::ast::Expression, @@ -237,7 +267,7 @@ fn emit_assignment( // reg typ let (mut instrs, expr_value) = - emit_expression(source, hlir, ast, &context, ra, afa, names)?; + emit_expression(source, hlir, ast, context, ra, afa, names)?; let expr_value = expr_value.ok_or( CodegenError::AssignmentExpressionRequiresValue(source.clone()), From 794283206908890688a372c17ae54059a5a50a84 Mon Sep 17 00:00:00 2001 From: Ryan Goodfellow Date: Sun, 22 Sep 2024 12:30:50 -0700 Subject: [PATCH 15/23] indirect action call plumbing --- codegen/htq/src/control.rs | 13 +- codegen/htq/src/error.rs | 11 ++ codegen/htq/src/expression.rs | 275 +++++++++++++++++++++++++++++----- codegen/htq/src/lib.rs | 31 +++- codegen/htq/src/parser.rs | 5 + codegen/htq/src/statement.rs | 154 +++++++++++++------ p4/src/ast.rs | 44 ++++-- 7 files changed, 427 insertions(+), 106 deletions(-) diff --git a/codegen/htq/src/control.rs b/codegen/htq/src/control.rs index 790e9c67..7ce7fa9a 100644 --- a/codegen/htq/src/control.rs +++ b/codegen/htq/src/control.rs @@ -7,7 +7,7 @@ use p4::{ast::ControlParameter, hlir::Hlir}; use crate::{ error::CodegenError, p4_type_to_htq_type, statement::emit_statement, - AsyncFlagAllocator, P4Context, RegisterAllocator, + AsyncFlagAllocator, P4Context, RegisterAllocator, TableContext, }; pub(crate) fn emit_control_functions( @@ -33,6 +33,7 @@ fn emit_control( ) -> Result, CodegenError> { let mut result = Vec::default(); let mut psub = HashMap::>::default(); + let mut table_context = TableContext::default(); let mut parameters = Vec::new(); let mut apply_return_signature = Vec::new(); @@ -48,12 +49,12 @@ fn emit_control( //variant apply_return_signature.push(htq::ast::Type::Unsigned(16)); - let args_size = control + let info = control .resolve_lookup_result_args_size(&x.name, ast) .ok_or(CodegenError::LookupResultArgSize(x.clone()))?; apply_return_signature - .push(htq::ast::Type::Unsigned(args_size)); + .push(htq::ast::Type::Unsigned(info.max_arg_size)); if x.ty.is_sync() { //async flag @@ -79,6 +80,7 @@ fn emit_control( &apply_return_signature, afa, &mut psub, + &mut table_context, )?); for action in &control.actions { @@ -91,6 +93,7 @@ fn emit_control( &action_return_signature, afa, &mut psub, + &mut table_context, )?); } Ok(result) @@ -104,6 +107,7 @@ fn emit_control_apply( return_signature: &[htq::ast::Type], afa: &mut AsyncFlagAllocator, psub: &mut HashMap>, + table_context: &mut TableContext, ) -> Result { let mut ra = RegisterAllocator::default(); let mut names = control.names(); @@ -127,6 +131,7 @@ fn emit_control_apply( &mut ra, afa, psub, + table_context, )?; statements.extend(stmts); blocks.extend(blks); @@ -180,6 +185,7 @@ fn emit_control_action( return_signature: &[htq::ast::Type], afa: &mut AsyncFlagAllocator, psub: &mut HashMap>, + table_context: &mut TableContext, ) -> Result { let mut ra = RegisterAllocator::default(); let mut names = control.names(); @@ -218,6 +224,7 @@ fn emit_control_action( &mut ra, afa, psub, + table_context, )?; statements.extend(stmts); blocks.extend(blks); diff --git a/codegen/htq/src/error.rs b/codegen/htq/src/error.rs index 27a5e58f..ae09ccc8 100644 --- a/codegen/htq/src/error.rs +++ b/codegen/htq/src/error.rs @@ -10,6 +10,8 @@ use p4::{ }; use thiserror::Error; +use crate::{RegisterAllocator, TableContext}; + #[derive(Error, Debug)] pub enum FlagAllocationError { #[error("flag overflow: count exceeds 128")] @@ -100,6 +102,15 @@ pub enum CodegenError { #[error("missing register for lvalue, this is a compiler bug \n{0:#?}\ncurrent registers: \n{1:#?}")] MissingRegisterForLvalue(Lvalue, Vec), + + #[error("table not found in context \nlvalue:\n{0:#?}\ncontext:\n{1:#?}")] + TableNotFoundInContext(Lvalue, TableContext), + + #[error("indirect action call in parser for \n{0:#?}")] + IndirectActionCallInParser(Lvalue), + + #[error("no register for parameter {0}\nregisters:\n{1:#?}")] + NoRegisterForParameter(String, RegisterAllocator), } #[derive(Error, Debug)] diff --git a/codegen/htq/src/expression.rs b/codegen/htq/src/expression.rs index 13ef385c..aa3bb067 100644 --- a/codegen/htq/src/expression.rs +++ b/codegen/htq/src/expression.rs @@ -1,7 +1,7 @@ use std::collections::HashMap; use htq::ast::{ - Fget, Load, Lookup, Or, Register, Rset, Shl, Statement, Type, Value, + Beq, Fget, Load, Lookup, Or, Register, Rset, Shl, Statement, Type, Value, }; use p4::{ ast::{ @@ -14,7 +14,8 @@ use p4::{ // Copyright 2024 Oxide Computer Company use crate::{ error::CodegenError, p4_type_to_htq_type, statement::member_offsets, - AsyncFlagAllocator, P4Context, RegisterAllocator, VersionedRegister, + AsyncFlagAllocator, CompiledTableInfo, P4Context, RegisterAllocator, + TableContext, VersionedRegister, }; pub(crate) struct ExpressionValue { @@ -52,36 +53,69 @@ pub(crate) fn emit_expression( ra: &mut RegisterAllocator, afa: &mut AsyncFlagAllocator, names: &HashMap, -) -> Result<(Vec, Option), CodegenError> { + table_context: &mut TableContext, +) -> Result< + ( + Vec, + Vec, + Option, + ), + CodegenError, +> { let r = ra.alloc_tmp_for_token(&expr.token); match &expr.kind { - ExpressionKind::BoolLit(value) => emit_bool_lit(*value, r), + ExpressionKind::BoolLit(value) => { + let (stmts, value) = emit_bool_lit(*value, r)?; + Ok((stmts, Vec::default(), value)) + } ExpressionKind::BitLit(width, value) => { - emit_bit_lit(*width, *value, r, expr) + let (stmts, value) = emit_bit_lit(*width, *value, r, expr)?; + Ok((stmts, Vec::default(), value)) + } + ExpressionKind::IntegerLit(value) => { + let (stmts, value) = emit_int_lit(*value, r)?; + Ok((stmts, Vec::default(), value)) } - ExpressionKind::IntegerLit(value) => emit_int_lit(*value, r), ExpressionKind::SignedLit(width, value) => { - emit_signed_lit(*width, *value, r) + let (stmts, value) = emit_signed_lit(*width, *value, r)?; + Ok((stmts, Vec::default(), value)) + } + ExpressionKind::Lvalue(lval) => { + let (stmts, value) = emit_lval(lval, hlir, ast, ra, names)?; + Ok((stmts, Vec::default(), value)) } - ExpressionKind::Lvalue(lval) => emit_lval(lval, hlir, ast, ra, names), ExpressionKind::Call(call) => match context { P4Context::Control(c) => { - emit_call(call, c, hlir, ast, ra, afa, names) + let (stmts, blks, value) = emit_call( + call, + c, + hlir, + ast, + context, + ra, + afa, + names, + table_context, + )?; + Ok((stmts, blks, value)) } P4Context::Parser(_) => { Err(CodegenError::CallInParser(expr.clone())) } }, - ExpressionKind::Binary(lhs, op, rhs) => emit_binary_expr( - lhs.as_ref(), - op, - rhs.as_ref(), - hlir, - ast, - ra, - afa, - names, - ), + ExpressionKind::Binary(lhs, op, rhs) => { + let (stmts, value) = emit_binary_expr( + lhs.as_ref(), + op, + rhs.as_ref(), + hlir, + ast, + ra, + afa, + names, + )?; + Ok((stmts, Vec::default(), value)) + } xpr => todo!("expression: {xpr:?}"), } } @@ -94,16 +128,28 @@ pub(crate) fn emit_single_valued_expression( ra: &mut RegisterAllocator, afa: &mut AsyncFlagAllocator, names: &HashMap, -) -> Result<(Vec, Register), CodegenError> { - let (stmts, val) = - emit_expression(expr, hlir, ast, context, ra, afa, names)?; + table_context: &mut TableContext, +) -> Result< + (Vec, Vec, Register), + CodegenError, +> { + let (stmts, blocks, val) = emit_expression( + expr, + hlir, + ast, + context, + ra, + afa, + names, + table_context, + )?; let val = val.ok_or(CodegenError::ExpressionValueNeeded(expr.clone()))?; if val.registers.len() != 1 { return Err(CodegenError::SingularExpressionValueNeeded(expr.clone())); } - Ok((stmts, val.registers[0].0.clone())) + Ok((stmts, blocks, val.registers[0].0.clone())) } pub(crate) fn emit_binary_expr( @@ -150,16 +196,61 @@ pub(crate) fn emit_call( control: &p4::ast::Control, hlir: &Hlir, ast: &p4::ast::AST, + context: &P4Context<'_>, ra: &mut RegisterAllocator, afa: &mut AsyncFlagAllocator, names: &HashMap, -) -> Result<(Vec, Option), CodegenError> { + table_context: &mut TableContext, +) -> Result< + ( + Vec, + Vec, + Option, + ), + CodegenError, +> { match call.lval.leaf() { - "apply" => emit_apply_call(call, control, hlir, ast, ra, afa, names), - "setValid" => emit_set_valid_call(call, hlir, ast, ra, names, true), - "setInvalid" => emit_set_valid_call(call, hlir, ast, ra, names, false), - "isValid" => emit_is_valid_call(call, hlir, ast, ra, names), - _ => emit_extern_call(call, hlir, ast, ra, names), + "apply" => { + let (stmts, result) = emit_apply_call( + call, + control, + hlir, + ast, + ra, + afa, + names, + table_context, + )?; + Ok((stmts, Vec::default(), result)) + } + "act" => emit_indirect_action_call( + call, + hlir, + ast, + context, + ra, + names, + table_context, + ), + "setValid" => { + let (stmts, result) = + emit_set_valid_call(call, hlir, ast, ra, names, true)?; + Ok((stmts, Vec::default(), result)) + } + "setInvalid" => { + let (stmts, result) = + emit_set_valid_call(call, hlir, ast, ra, names, false)?; + Ok((stmts, Vec::default(), result)) + } + "isValid" => { + let (stmts, result) = + emit_is_valid_call(call, hlir, ast, ra, names)?; + Ok((stmts, Vec::default(), result)) + } + _ => { + let (stmts, result) = emit_extern_call(call, hlir, ast, ra, names)?; + Ok((stmts, Vec::default(), result)) + } } } @@ -171,6 +262,7 @@ fn emit_apply_call( ra: &mut RegisterAllocator, afa: &mut AsyncFlagAllocator, names: &HashMap, + table_context: &mut TableContext, ) -> Result<(Vec, Option), CodegenError> { let parent = call.lval.pop_right(); let parent = parent.leaf(); @@ -189,9 +281,11 @@ fn emit_apply_call( call.clone(), info.ty.clone(), ))?; - emit_control_apply_call(call, hlir, ast, ra, names) + emit_control_apply_call(call, hlir, ast, ra, names, table_context) + } + p4::ast::Type::Action => { + emit_direct_action_call(call, hlir, ast, ra, names, table_context) } - p4::ast::Type::Action => emit_action_call(call, hlir, ast, ra, names), p4::ast::Type::ExternFunction => { emit_extern_call(call, hlir, ast, ra, names) } @@ -313,6 +407,7 @@ fn emit_control_apply_call( ast: &p4::ast::AST, ra: &mut RegisterAllocator, names: &HashMap, + table_context: &mut TableContext, ) -> Result<(Vec, Option), CodegenError> { // get the control being called let info = names @@ -370,26 +465,42 @@ fn emit_control_apply_call( for (i, p) in control.parameters.iter().enumerate() { if p.direction.is_out() { if p.ty.is_lookup_result() { - let arg = match &call.args[i].kind { - ExpressionKind::Lvalue(lval) => lval.root().to_owned(), + let arg_lval = match &call.args[i].kind { + ExpressionKind::Lvalue(lval) => lval, _x => panic!("expected lvalue for out parameter"), }; + let arg = arg_lval.root().to_owned(); let hit = ra.alloc(&format!("{}_hit", arg)); - returned_registers.push((hit, htq::ast::Type::Bool)); + returned_registers.push((hit.clone(), htq::ast::Type::Bool)); let variant = ra.alloc(&format!("{}_variant", arg)); returned_registers - .push((variant, htq::ast::Type::Unsigned(16))); + .push((variant.clone(), htq::ast::Type::Unsigned(16))); let args = ra.alloc(&format!("{}_args", arg)); - let args_size = control + let info = control .resolve_lookup_result_args_size(&p.name, ast) .ok_or(CodegenError::LookupResultArgSize(p.clone()))?; - returned_registers - .push((args, htq::ast::Type::Bitfield(args_size))); + + returned_registers.push(( + args.clone(), + htq::ast::Type::Bitfield(info.max_arg_size), + )); let sync = ra.alloc(&format!("{}_sync", arg)); - returned_registers.push((sync, htq::ast::Type::Bitfield(128))); + returned_registers + .push((sync.clone(), htq::ast::Type::Bitfield(128))); + table_context.insert( + arg_lval.name.clone(), + CompiledTableInfo { + table: info.table.clone(), + control: control.clone(), + hit, + variant, + args, + sync, + }, + ); } else { returned_registers .push((ra.alloc(&p.name), p4_type_to_htq_type(&p.ty)?)) @@ -447,14 +558,96 @@ fn emit_extern_call( Ok((Vec::default(), None)) } -fn emit_action_call( +fn emit_indirect_action_call( + call: &p4::ast::Call, + _hlir: &Hlir, + _ast: &p4::ast::AST, + context: &P4Context<'_>, + ra: &mut RegisterAllocator, + _names: &HashMap, + table_context: &mut TableContext, +) -> Result< + ( + Vec, + Vec, + Option, + ), + CodegenError, +> { + let mut result = Vec::new(); + let mut blocks = Vec::new(); + + let control = match context { + P4Context::Control(c) => *c, + P4Context::Parser(_) => { + return Err(CodegenError::IndirectActionCallInParser( + call.lval.clone(), + )) + } + }; + + let table_lval = call.lval.pop_right(); + let info = table_context.get(&table_lval.name).ok_or( + CodegenError::TableNotFoundInContext( + table_lval.clone(), + table_context.clone(), + ), + )?; + + let mut block_ra = RegisterAllocator::default(); + let mut control_params = Vec::new(); + let mut block_params = Vec::new(); + for p in &control.parameters { + control_params.push(ra.get(&p.name).ok_or( + CodegenError::NoRegisterForParameter(p.name.clone(), ra.clone()), + )?); + block_params.push(block_ra.alloc(&p.name)); + } + + let control_param_values = control_params + .iter() + .cloned() + .map(Value::reg) + .collect::>(); + + let block_param_values = block_params + .iter() + .cloned() + .map(Value::reg) + .collect::>(); + + for (i, a) in info.table.actions.iter().enumerate() { + result.push(Statement::Beq(Beq { + source: info.variant.clone(), + predicate: Value::number(i as i128), + label: a.name.clone(), + args: control_param_values.clone(), + })); + let mut stmts = Vec::default(); + stmts.push(Statement::Call(htq::ast::Call { + fname: format!("{}_{}", info.control.name, a.name.clone()), + args: block_param_values.clone(), + targets: Vec::default(), //TODO actual targets, + })); + blocks.push(htq::ast::StatementBlock { + name: a.name.clone(), + parameters: block_params.clone(), + statements: stmts, + }); + } + Ok((result, blocks, None)) +} + +fn emit_direct_action_call( _call: &p4::ast::Call, _hlir: &Hlir, _ast: &p4::ast::AST, _ra: &mut RegisterAllocator, _names: &HashMap, + _table_context: &mut TableContext, ) -> Result<(Vec, Option), CodegenError> { - todo!("action call") + //TODO + Ok((Vec::default(), None)) } fn emit_lval( diff --git a/codegen/htq/src/lib.rs b/codegen/htq/src/lib.rs index 212eca92..0eec939f 100644 --- a/codegen/htq/src/lib.rs +++ b/codegen/htq/src/lib.rs @@ -1,8 +1,13 @@ // Copyright 2024 Oxide Computer Company #![allow(clippy::too_many_arguments)] +#![allow(clippy::result_large_err)] +#![allow(clippy::type_complexity)] -use std::{collections::HashMap, io::Write}; +use std::{ + collections::{BTreeMap, HashMap}, + io::Write, +}; use control::emit_control_functions; use error::{CodegenError, FlagAllocationError}; @@ -123,9 +128,9 @@ fn p4_type_to_htq_type( }) } -#[derive(Default)] -pub(crate) struct RegisterAllocator { - data: HashMap, +#[derive(Default, Debug, Clone)] +pub struct RegisterAllocator { + data: BTreeMap, } impl RegisterAllocator { @@ -171,6 +176,12 @@ impl RegisterAllocator { .collect() } + pub(crate) fn rebase(&self) -> Self { + Self { + data: self.data.keys().map(|k| (k.clone(), 0)).collect(), + } + } + fn register_name(name: &str, rev: usize) -> Register { if rev > 0 { Register::new(&format!("{}.{}", name, rev)) @@ -255,3 +266,15 @@ pub(crate) enum P4Context<'a> { Parser(&'a p4::ast::Parser), Control(&'a p4::ast::Control), } + +pub type TableContext = HashMap; + +#[derive(Debug, Clone)] +pub struct CompiledTableInfo { + pub table: p4::ast::Table, + pub control: p4::ast::Control, + pub hit: Register, + pub variant: Register, + pub args: Register, + pub sync: Register, +} diff --git a/codegen/htq/src/parser.rs b/codegen/htq/src/parser.rs index a53c172e..48f4c05f 100644 --- a/codegen/htq/src/parser.rs +++ b/codegen/htq/src/parser.rs @@ -52,6 +52,10 @@ fn emit_parser( let mut names = parser.names(); + // TODO XXX parsers cannot have tables, this indicates broken abstractions + // around code generation control flow. + let mut table_context = HashMap::default(); + for state in &parser.states { // keeps track of register revisions for locals let mut statements = Vec::default(); @@ -66,6 +70,7 @@ fn emit_parser( &mut ra, afa, &mut psub, + &mut table_context, )?; statements.extend(stmts); blocks.extend(blks); diff --git a/codegen/htq/src/statement.rs b/codegen/htq/src/statement.rs index 52bf8e3f..e2d90018 100644 --- a/codegen/htq/src/statement.rs +++ b/codegen/htq/src/statement.rs @@ -14,7 +14,7 @@ use p4::{ use crate::{ error::CodegenError, expression::{emit_expression, emit_single_valued_expression}, - AsyncFlagAllocator, P4Context, RegisterAllocator, + AsyncFlagAllocator, P4Context, RegisterAllocator, TableContext, }; pub(crate) fn emit_statement( @@ -26,6 +26,7 @@ pub(crate) fn emit_statement( ra: &mut RegisterAllocator, afa: &mut AsyncFlagAllocator, psub: &mut HashMap>, + table_context: &mut TableContext, ) -> Result< (Vec, Vec), CodegenError, @@ -35,18 +36,41 @@ pub(crate) fn emit_statement( S::Empty => Ok((Vec::default(), Vec::default())), S::Assignment(lval, expr) => { let stmts = emit_assignment( - hlir, ast, context, names, lval, expr, ra, afa, psub, + hlir, + ast, + context, + names, + lval, + expr, + ra, + afa, + psub, + table_context, )?; Ok((stmts, Vec::default())) } - S::Call(call) => { - let stmts = - emit_call(hlir, ast, context, names, call, ra, afa, psub)?; - Ok((stmts, Vec::default())) - } - S::If(if_block) => { - emit_if_block(hlir, ast, context, names, if_block, ra, afa, psub) - } + S::Call(call) => emit_call( + hlir, + ast, + context, + names, + call, + ra, + afa, + psub, + table_context, + ), + S::If(if_block) => emit_if_block( + hlir, + ast, + context, + names, + if_block, + ra, + afa, + psub, + table_context, + ), S::Variable(v) => { let stmts = emit_variable(hlir, ast, context, names, v, ra, afa, psub)?; @@ -67,6 +91,7 @@ fn emit_if_block( ra: &mut RegisterAllocator, afa: &mut AsyncFlagAllocator, psub: &mut HashMap>, + table_context: &mut TableContext, ) -> Result< (Vec, Vec), CodegenError, @@ -78,18 +103,21 @@ fn emit_if_block( if let ExpressionKind::Binary(lhs, BinOp::Eq, rhs) = &iblk.predicate.kind { - let (source_statements, source) = emit_single_valued_expression( - lhs.as_ref(), - hlir, - ast, - context, - ra, - afa, - names, - )?; + let (source_statements, blks, source) = + emit_single_valued_expression( + lhs.as_ref(), + hlir, + ast, + context, + ra, + afa, + names, + table_context, + )?; + blocks.extend(blks); result.extend(source_statements.clone()); - let (predicate_statements, predicate) = + let (predicate_statements, blks, predicate) = emit_single_valued_expression( rhs.as_ref(), hlir, @@ -98,20 +126,25 @@ fn emit_if_block( ra, afa, names, + table_context, )?; + blocks.extend(blks); result.extend(predicate_statements.clone()); (source, Value::reg(predicate)) } else { - let (predicate_statements, source) = emit_single_valued_expression( - iblk.predicate.as_ref(), - hlir, - ast, - context, - ra, - afa, - names, - )?; + let (predicate_statements, blks, source) = + emit_single_valued_expression( + iblk.predicate.as_ref(), + hlir, + ast, + context, + ra, + afa, + names, + table_context, + )?; + blocks.extend(blks); result.extend(predicate_statements.clone()); (source, htq::ast::Value::bool(true)) }; @@ -126,22 +159,33 @@ fn emit_if_block( args, })); + // create a clean register allocator for the statements in the new block + let mut block_ra = ra.rebase(); + let block_params = block_ra.all_registers(); + let mut blk = StatementBlock { name: label, - parameters: params, + parameters: block_params, statements: Vec::default(), }; for stmt in &iblk.block.statements { // TODO nested if blocks - let (stmts, _) = - emit_statement(stmt, ast, context, hlir, names, ra, afa, psub)?; + let (stmts, blks) = emit_statement( + stmt, + ast, + context, + hlir, + names, + &mut block_ra, + afa, + psub, + table_context, + )?; + blocks.extend(blks); blk.statements.extend(stmts); } - // XXX - //result.push(htq::ast::Statement::Label(label, params)); - blocks.push(blk); Ok((result, blocks)) @@ -197,17 +241,29 @@ fn emit_call( ra: &mut RegisterAllocator, afa: &mut AsyncFlagAllocator, _psub: &mut HashMap>, -) -> Result, CodegenError> { - let (instrs, _result) = match &context { - P4Context::Control(c) => { - crate::expression::emit_call(call, c, hlir, ast, ra, afa, names)? - } + table_context: &mut TableContext, +) -> Result< + (Vec, Vec), + CodegenError, +> { + let (instrs, blocks, _result) = match &context { + P4Context::Control(c) => crate::expression::emit_call( + call, + c, + hlir, + ast, + context, + ra, + afa, + names, + table_context, + )?, P4Context::Parser(_) => { //TODO - (Vec::default(), None) + (Vec::default(), Vec::default(), None) } }; - Ok(instrs) + Ok((instrs, blocks)) } #[allow(clippy::too_many_arguments)] @@ -221,6 +277,7 @@ fn emit_assignment( ra: &mut RegisterAllocator, afa: &mut AsyncFlagAllocator, psub: &mut HashMap>, + table_context: &mut TableContext, ) -> Result, CodegenError> { // Things that can be assigned to are lvalues with the following // declaration kinds. The table shows how each kind is referenced @@ -266,8 +323,17 @@ fn emit_assignment( .ok_or(CodegenError::UndefinedLvalue(target.clone()))?; // reg typ - let (mut instrs, expr_value) = - emit_expression(source, hlir, ast, context, ra, afa, names)?; + // TODO is there an assignment that can result in blocks ....? + let (mut instrs, _blocks, expr_value) = emit_expression( + source, + hlir, + ast, + context, + ra, + afa, + names, + table_context, + )?; let expr_value = expr_value.ok_or( CodegenError::AssignmentExpressionRequiresValue(source.clone()), diff --git a/p4/src/ast.rs b/p4/src/ast.rs index 62acea75..ed8318dd 100644 --- a/p4/src/ast.rs +++ b/p4/src/ast.rs @@ -1176,7 +1176,7 @@ impl Control { &self, parameter_name: &str, ast: &AST, - ) -> Option { + ) -> Option { self.resolve_lookup_result_args_size_rec( parameter_name, ast, @@ -1184,13 +1184,15 @@ impl Control { ) } - //TODO this needs to consider the possibility of multiple assignments + //TODO Does this need to consider the possibility of multiple assignments? + // Should that be an error? pub fn resolve_lookup_result_args_size_rec( &self, parameter_name: &str, ast: &AST, block: &StatementBlock, - ) -> Option { + ) -> Option { + let mut table: Option
= None; let mut size = 0; let mut found = false; for x in &block.statements { @@ -1216,6 +1218,7 @@ impl Control { ), size, ); + table = Some(tbl.clone()); found = true; } else { continue; @@ -1225,16 +1228,19 @@ impl Control { } } Statement::If(if_block) => { - if let Some(sz) = self.resolve_lookup_result_args_size_rec( - parameter_name, - ast, - &if_block.block, - ) { + if let Some(info) = self + .resolve_lookup_result_args_size_rec( + parameter_name, + ast, + &if_block.block, + ) + { found = true; - size = usize::max(size, sz); + size = usize::max(size, info.max_arg_size); + table = Some(info.table); } for x in &if_block.else_ifs { - if let Some(sz) = self + if let Some(info) = self .resolve_lookup_result_args_size_rec( parameter_name, ast, @@ -1242,11 +1248,12 @@ impl Control { ) { found = true; - size = usize::max(size, sz); + size = usize::max(size, info.max_arg_size); + table = Some(info.table); } } if let Some(else_block) = &if_block.else_block { - if let Some(sz) = self + if let Some(info) = self .resolve_lookup_result_args_size_rec( parameter_name, ast, @@ -1254,7 +1261,8 @@ impl Control { ) { found = true; - size = usize::max(size, sz); + size = usize::max(size, info.max_arg_size); + table = Some(info.table); } } } @@ -1262,7 +1270,10 @@ impl Control { } } if found { - Some(size) + Some(TableInfo { + max_arg_size: size, + table: table.unwrap(), + }) } else { None } @@ -1287,6 +1298,11 @@ impl Control { } } +pub struct TableInfo { + pub table: Table, + pub max_arg_size: usize, +} + impl PartialEq for Control { fn eq(&self, other: &Control) -> bool { self.name == other.name From fbc6313fe471067cd816eff451cdd18c06f68bbc Mon Sep 17 00:00:00 2001 From: Ryan Goodfellow Date: Mon, 23 Sep 2024 09:43:16 -0700 Subject: [PATCH 16/23] ensure return on branch --- codegen/htq/src/error.rs | 3 +++ codegen/htq/src/expression.rs | 30 +++++++++++++++++++++++++++++- codegen/htq/src/statement.rs | 19 +++++++++++++++++++ 3 files changed, 51 insertions(+), 1 deletion(-) diff --git a/codegen/htq/src/error.rs b/codegen/htq/src/error.rs index ae09ccc8..06403a3e 100644 --- a/codegen/htq/src/error.rs +++ b/codegen/htq/src/error.rs @@ -111,6 +111,9 @@ pub enum CodegenError { #[error("no register for parameter {0}\nregisters:\n{1:#?}")] NoRegisterForParameter(String, RegisterAllocator), + + #[error("action not found in control\naction:\n{0:#?}\ncontrol:\n{1:#?}")] + ActionNotFound(Lvalue, Control), } #[derive(Error, Debug)] diff --git a/codegen/htq/src/expression.rs b/codegen/htq/src/expression.rs index aa3bb067..7aec3ced 100644 --- a/codegen/htq/src/expression.rs +++ b/codegen/htq/src/expression.rs @@ -617,6 +617,31 @@ fn emit_indirect_action_call( .collect::>(); for (i, a) in info.table.actions.iter().enumerate() { + let action_decl = info.control.get_action(&a.name).ok_or( + CodegenError::ActionNotFound(a.clone(), info.control.clone()), + )?; + let mut control_out_params = Vec::default(); + let mut out_params = Vec::default(); + for x in &action_decl.parameters { + if x.direction.is_out() { + out_params.push(ra.get(&x.name).ok_or( + CodegenError::NoRegisterForParameter( + x.name.clone(), + ra.clone(), + ), + )?) + } + } + for x in &info.control.parameters { + if x.ty.is_lookup_result() { + continue; + } + if x.direction.is_out() { + out_params.push(block_ra.alloc(&x.name)); + control_out_params.push(block_ra.get(&x.name).unwrap()); + } + } + result.push(Statement::Beq(Beq { source: info.variant.clone(), predicate: Value::number(i as i128), @@ -627,7 +652,10 @@ fn emit_indirect_action_call( stmts.push(Statement::Call(htq::ast::Call { fname: format!("{}_{}", info.control.name, a.name.clone()), args: block_param_values.clone(), - targets: Vec::default(), //TODO actual targets, + targets: out_params, + })); + stmts.push(Statement::Return(htq::ast::Return { + registers: control_out_params, })); blocks.push(htq::ast::StatementBlock { name: a.name.clone(), diff --git a/codegen/htq/src/statement.rs b/codegen/htq/src/statement.rs index e2d90018..8b9298a2 100644 --- a/codegen/htq/src/statement.rs +++ b/codegen/htq/src/statement.rs @@ -186,6 +186,25 @@ fn emit_if_block( blk.statements.extend(stmts); } + if let P4Context::Control(control) = &context { + let mut out_params = Vec::default(); + for x in &control.parameters { + if x.ty.is_lookup_result() { + continue; + } + out_params.push(block_ra.get(&x.name).ok_or( + CodegenError::NoRegisterForParameter( + x.name.clone(), + ra.clone(), + ), + )?); + } + blk.statements + .push(htq::ast::Statement::Return(htq::ast::Return { + registers: out_params, + })); + } + blocks.push(blk); Ok((result, blocks)) From 4bb14dbcac9c3c2a4a287628973dca2cf247bfad Mon Sep 17 00:00:00 2001 From: Ryan Goodfellow Date: Mon, 23 Sep 2024 13:37:21 -0700 Subject: [PATCH 17/23] more pblock/call/control-flow plumbing --- codegen/htq/src/expression.rs | 43 ++++++++++++++++++++------ codegen/htq/src/statement.rs | 58 +++++++++++++++++++++++++++++++---- 2 files changed, 86 insertions(+), 15 deletions(-) diff --git a/codegen/htq/src/expression.rs b/codegen/htq/src/expression.rs index 7aec3ced..1127c78d 100644 --- a/codegen/htq/src/expression.rs +++ b/codegen/htq/src/expression.rs @@ -561,7 +561,7 @@ fn emit_extern_call( fn emit_indirect_action_call( call: &p4::ast::Call, _hlir: &Hlir, - _ast: &p4::ast::AST, + ast: &p4::ast::AST, context: &P4Context<'_>, ra: &mut RegisterAllocator, _names: &HashMap, @@ -603,6 +603,7 @@ fn emit_indirect_action_call( )?); block_params.push(block_ra.alloc(&p.name)); } + block_params.push(info.args.clone()); let control_param_values = control_params .iter() @@ -616,21 +617,45 @@ fn emit_indirect_action_call( .map(Value::reg) .collect::>(); + let mut block_args = control_param_values.clone(); + block_args.push(Value::reg(info.args.clone())); + for (i, a) in info.table.actions.iter().enumerate() { + // make a clean lbock for each action + let mut block_ra = block_ra.clone(); + let mut block_param_values = block_param_values.clone(); + // pop the args off, we're going to break it up into components + block_param_values.pop(); + let action_decl = info.control.get_action(&a.name).ok_or( CodegenError::ActionNotFound(a.clone(), info.control.clone()), )?; let mut control_out_params = Vec::default(); let mut out_params = Vec::default(); + let mut stmts = Vec::default(); + + let mut action_params = Vec::default(); + + let mut offset = 0; + let key = info.args.clone(); for x in &action_decl.parameters { if x.direction.is_out() { - out_params.push(ra.get(&x.name).ok_or( - CodegenError::NoRegisterForParameter( - x.name.clone(), - ra.clone(), - ), - )?) + out_params.push(block_ra.alloc(&x.name)); } + + let action_param_reg = block_ra.alloc(&format!("{}_arg", &x.name)); + let sz = type_size(&x.ty, ast); + action_params.push(action_param_reg.clone()); + stmts.push(Statement::Load(htq::ast::Load { + target: action_param_reg.clone(), + typ: Type::Bitfield(sz), + source: key.clone(), + offset: Value::number(offset), + })); + + block_param_values.push(Value::reg(action_param_reg.clone())); + + offset += sz as i128; } for x in &info.control.parameters { if x.ty.is_lookup_result() { @@ -646,9 +671,9 @@ fn emit_indirect_action_call( source: info.variant.clone(), predicate: Value::number(i as i128), label: a.name.clone(), - args: control_param_values.clone(), + args: block_args.clone(), })); - let mut stmts = Vec::default(); + stmts.push(Statement::Call(htq::ast::Call { fname: format!("{}_{}", info.control.name, a.name.clone()), args: block_param_values.clone(), diff --git a/codegen/htq/src/statement.rs b/codegen/htq/src/statement.rs index 8b9298a2..d63fa012 100644 --- a/codegen/htq/src/statement.rs +++ b/codegen/htq/src/statement.rs @@ -95,14 +95,60 @@ fn emit_if_block( ) -> Result< (Vec, Vec), CodegenError, +> { + let (mut statements, mut blocks) = emit_conditional_block( + hlir, + ast, + context, + names, + &iblk.predicate, + &iblk.block, + ra, + afa, + psub, + table_context, + )?; + + for elif in &iblk.else_ifs { + let (stmts, blks) = emit_conditional_block( + hlir, + ast, + context, + names, + &elif.predicate, + &elif.block, + ra, + afa, + psub, + table_context, + )?; + statements.extend(stmts); + blocks.extend(blks); + } + + Ok((statements, blocks)) +} + +fn emit_conditional_block( + hlir: &Hlir, + ast: &p4::ast::AST, + context: &P4Context<'_>, + names: &mut HashMap, + predicate: &p4::ast::Expression, + block: &p4::ast::StatementBlock, + ra: &mut RegisterAllocator, + afa: &mut AsyncFlagAllocator, + psub: &mut HashMap>, + table_context: &mut TableContext, +) -> Result< + (Vec, Vec), + CodegenError, > { let mut result = Vec::default(); let mut blocks = Vec::default(); let (source, predicate) = - if let ExpressionKind::Binary(lhs, BinOp::Eq, rhs) = - &iblk.predicate.kind - { + if let ExpressionKind::Binary(lhs, BinOp::Eq, rhs) = &predicate.kind { let (source_statements, blks, source) = emit_single_valued_expression( lhs.as_ref(), @@ -135,7 +181,7 @@ fn emit_if_block( } else { let (predicate_statements, blks, source) = emit_single_valued_expression( - iblk.predicate.as_ref(), + predicate, hlir, ast, context, @@ -169,7 +215,7 @@ fn emit_if_block( statements: Vec::default(), }; - for stmt in &iblk.block.statements { + for stmt in &block.statements { // TODO nested if blocks let (stmts, blks) = emit_statement( stmt, @@ -195,7 +241,7 @@ fn emit_if_block( out_params.push(block_ra.get(&x.name).ok_or( CodegenError::NoRegisterForParameter( x.name.clone(), - ra.clone(), + block_ra.clone(), ), )?); } From a7a2936671952054c6da009908853f23ea0c79aa Mon Sep 17 00:00:00 2001 From: Ryan Goodfellow Date: Mon, 23 Sep 2024 21:12:14 -0700 Subject: [PATCH 18/23] parser transitions --- codegen/htq/src/error.rs | 5 +- codegen/htq/src/statement.rs | 93 ++++++++++++++++++++++++++++++++++-- 2 files changed, 94 insertions(+), 4 deletions(-) diff --git a/codegen/htq/src/error.rs b/codegen/htq/src/error.rs index 06403a3e..213992de 100644 --- a/codegen/htq/src/error.rs +++ b/codegen/htq/src/error.rs @@ -4,7 +4,7 @@ use htq::ast::Register; use p4::{ ast::{ Call, Control, ControlParameter, DeclarationInfo, Expression, Lvalue, - Type, + Transition, Type, }, lexer::Token, }; @@ -114,6 +114,9 @@ pub enum CodegenError { #[error("action not found in control\naction:\n{0:#?}\ncontrol:\n{1:#?}")] ActionNotFound(Lvalue, Control), + + #[error("transition must be in parser context\n{0:#?}")] + TransitionOutsideParser(Transition), } #[derive(Error, Debug)] diff --git a/codegen/htq/src/statement.rs b/codegen/htq/src/statement.rs index d63fa012..826b8640 100644 --- a/codegen/htq/src/statement.rs +++ b/codegen/htq/src/statement.rs @@ -77,11 +77,96 @@ pub(crate) fn emit_statement( Ok((stmts, Vec::default())) } S::Constant(_c) => Ok((Vec::default(), Vec::default())), //TODO - S::Transition(_t) => Ok((Vec::default(), Vec::default())), //TODO - S::Return(_r) => Ok((Vec::default(), Vec::default())), //TODO + S::Transition(t) => emit_transition( + hlir, + ast, + context, + names, + t, + ra, + afa, + psub, + table_context, + ), + S::Return(_r) => Ok((Vec::default(), Vec::default())), //TODO } } +fn emit_transition( + _hlir: &Hlir, + _ast: &p4::ast::AST, + context: &P4Context<'_>, + _names: &mut HashMap, + transition: &p4::ast::Transition, + ra: &mut RegisterAllocator, + _afa: &mut AsyncFlagAllocator, + _psub: &mut HashMap>, + _table_context: &mut TableContext, +) -> Result< + (Vec, Vec), + CodegenError, +> { + let parser = match &context { + P4Context::Parser(p) => *p, + P4Context::Control(_) => { + return Err(CodegenError::TransitionOutsideParser( + transition.clone(), + )) + } + }; + + let transition_to = match &transition { + p4::ast::Transition::Reference(lval) => lval, + p4::ast::Transition::Select(_) => todo!("transition select"), + }; + + let mut args = Vec::default(); + let mut targets = Vec::default(); + for x in &parser.parameters { + if x.direction.is_out() { + targets.push(ra.alloc(&x.name)); + } + args.push(Value::reg(ra.get(&x.name).ok_or( + CodegenError::NoRegisterForParameter(x.name.clone(), ra.clone()), + )?)); + } + + let hdr = targets[0].clone(); + + let mut stmts = Vec::default(); + match transition_to.name.as_str() { + "accept" => { + stmts.push(htq::ast::Statement::SetValid(htq::ast::SetValid { + output: ra.alloc(&hdr.0), + target: hdr.clone(), + offsets: Vec::default(), + source: htq::ast::Value::bool(true), + })); + } + "reject" => { + stmts.push(htq::ast::Statement::SetValid(htq::ast::SetValid { + output: ra.alloc(&hdr.0), + target: hdr.clone(), + offsets: Vec::default(), + source: htq::ast::Value::bool(false), + })); + } + _ => { + stmts.push(htq::ast::Statement::Call(htq::ast::Call { + fname: format!("{}_{}", parser.name, transition_to.name), + args, + targets: targets.clone(), + })); + } + } + + stmts.push(htq::ast::Statement::Return(htq::ast::Return { + registers: targets, + })); + + Ok((stmts, Vec::default())) +} + fn emit_if_block( hlir: &Hlir, ast: &p4::ast::AST, @@ -430,7 +515,9 @@ fn emit_assignment( // TODO store instr } DeclarationInfo::StructMember | DeclarationInfo::HeaderMember => { - let treg = ra.alloc(target.root()); + let treg = ra.get(target.root()).ok_or( + CodegenError::RegisterDoesNotExistForLval(target.clone()), + )?; let output = ra.alloc(target.root()); let offsets = member_offsets(ast, names, target)?; let instr = Fset { From ae83454bb8fdfb64c03ca7f101c1a36745612751 Mon Sep 17 00:00:00 2001 From: Ryan Goodfellow Date: Fri, 27 Sep 2024 20:43:33 -0700 Subject: [PATCH 19/23] implement parser extract --- codegen/htq/src/error.rs | 15 +++++ codegen/htq/src/expression.rs | 123 +++++++++++++++++++++++++++++++++- codegen/htq/src/parser.rs | 6 ++ codegen/htq/src/statement.rs | 22 ++++-- 4 files changed, 159 insertions(+), 7 deletions(-) diff --git a/codegen/htq/src/error.rs b/codegen/htq/src/error.rs index 213992de..1647d9e9 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 1127c78d..dcc98379 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 48f4c05f..20d2beea 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 826b8640..57efd0b2 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)) } From 17274c160e6966ecbce208e02e47e11b22950e55 Mon Sep 17 00:00:00 2001 From: Ryan Goodfellow Date: Tue, 28 Jan 2025 08:52:09 -0800 Subject: [PATCH 20/23] fix ci --- .github/buildomat/jobs/build-and-test.sh | 3 +++ .gitignore | 1 + codegen/htq/Cargo.toml | 4 ++-- codegen/htq/src/expression.rs | 2 +- 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/.github/buildomat/jobs/build-and-test.sh b/.github/buildomat/jobs/build-and-test.sh index d0331ea4..ded0584b 100755 --- a/.github/buildomat/jobs/build-and-test.sh +++ b/.github/buildomat/jobs/build-and-test.sh @@ -8,6 +8,9 @@ #: "/work/debug/*", #: "/work/release/*", #: ] +#: access_repos = [ +#: "oxidecomputer/htq", +#: ] #: set -o errexit diff --git a/.gitignore b/.gitignore index e05bdad3..4cd416b4 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ *.sw* out.rs tags +!.github diff --git a/codegen/htq/Cargo.toml b/codegen/htq/Cargo.toml index 2a9db0a4..93a57196 100644 --- a/codegen/htq/Cargo.toml +++ b/codegen/htq/Cargo.toml @@ -5,6 +5,6 @@ edition = "2021" [dependencies] p4 = { path = "../../p4" } -#htq = { git = "https://github.com/oxidecomputer/htq", branch = "sector-001" } -htq = { path = "/Users/ry/src/htq" } +htq = { git = "https://github.com/oxidecomputer/htq", branch = "sector-001" } +#htq = { path = "/Users/ry/src/htq" } thiserror = "1.0.63" diff --git a/codegen/htq/src/expression.rs b/codegen/htq/src/expression.rs index dcc98379..28cb9879 100644 --- a/codegen/htq/src/expression.rs +++ b/codegen/htq/src/expression.rs @@ -318,7 +318,7 @@ fn emit_extract_call( let target = ra .get(tgt.root()) .ok_or(CodegenError::RegisterDoesNotExistForLval(tgt.clone()))?; - let output = ra.alloc(&tgt.root()); + let output = ra.alloc(tgt.root()); let info = names .get(tgt.root()) From e207b5506d5a1731571302c04df1978a44cfaf4b Mon Sep 17 00:00:00 2001 From: Ryan Goodfellow Date: Tue, 28 Jan 2025 12:48:26 -0800 Subject: [PATCH 21/23] emit htq entry/parser_entry --- codegen/htq/src/lib.rs | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/codegen/htq/src/lib.rs b/codegen/htq/src/lib.rs index 0eec939f..ecf9a9ea 100644 --- a/codegen/htq/src/lib.rs +++ b/codegen/htq/src/lib.rs @@ -61,6 +61,27 @@ pub fn emit( let mut f = std::fs::File::create(filename)?; + writeln!( + f, + "parser_entry {}_start;", + ast.package_instance + .as_ref() + .unwrap() + .parameters + .first() + .expect("package instance has parser entry point") + )?; + writeln!( + f, + "entry {}_apply;", + ast.package_instance + .as_ref() + .unwrap() + .parameters + .get(1) + .expect("package instance has entry point") + )?; + for h in &headers { writeln!(f, "{}", h.emit())?; } From 83c546423727f1eb9e0456eb0430861b86bdee8c Mon Sep 17 00:00:00 2001 From: Ryan Goodfellow Date: Wed, 29 Jan 2025 19:36:32 -0800 Subject: [PATCH 22/23] htq: fix extract codegen --- codegen/htq/src/expression.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/codegen/htq/src/expression.rs b/codegen/htq/src/expression.rs index 28cb9879..c286ed39 100644 --- a/codegen/htq/src/expression.rs +++ b/codegen/htq/src/expression.rs @@ -301,7 +301,7 @@ fn emit_extract_call( names: &HashMap, _table_context: &mut TableContext, ) -> Result<(Vec, Option), CodegenError> { - let src = &parser.parameters[1].name; + let src = &parser.parameters[0].name; let source = ra.get(src).ok_or(CodegenError::NoRegisterForParameter( src.to_owned(), ra.clone(), From 4dc75f5395c74656ac2d10cf7af6f9c84431103f Mon Sep 17 00:00:00 2001 From: Matt Keeter Date: Mon, 31 Mar 2025 09:29:38 -0400 Subject: [PATCH 23/23] Post-rebase fixes --- Cargo.lock | 36 ++++++++++++++++++++++++++++++++++++ Cargo.toml | 2 ++ codegen/htq/Cargo.toml | 7 +++---- 3 files changed, 41 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6d34545a..2d6eabb1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -288,6 +288,14 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" +[[package]] +name = "htq" +version = "0.1.0" +source = "git+https://github.com/oxidecomputer/htq?branch=sector-001#cc2e6230b13d6d9d3f5edb55201e4a93314503bb" +dependencies = [ + "winnow", +] + [[package]] name = "ipnetwork" version = "0.20.0" @@ -449,6 +457,23 @@ dependencies = [ "regex", ] +[[package]] +name = "p4-cg" +version = "0.1.0" +dependencies = [ + "p4", + "thiserror 1.0.69", +] + +[[package]] +name = "p4-htq" +version = "0.1.0" +dependencies = [ + "htq", + "p4", + "thiserror 1.0.69", +] + [[package]] name = "p4-macro" version = "0.1.0" @@ -477,6 +502,7 @@ name = "p4-rust" version = "0.1.0" dependencies = [ "p4", + "p4-cg", "prettyplease", "proc-macro2", "quote", @@ -1158,6 +1184,15 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "winnow" +version = "0.6.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e90edd2ac1aa278a5c4599b1d89cf03074b610800f866d4026dc199d7929a28" +dependencies = [ + "memchr", +] + [[package]] name = "wit-bindgen-rt" version = "0.39.0" @@ -1184,6 +1219,7 @@ dependencies = [ "clap", "colored", "p4", + "p4-htq", "p4-rust", "regex", ] diff --git a/Cargo.toml b/Cargo.toml index 0b90501f..62e64a62 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,6 +28,7 @@ anyhow = "1" bitvec = "1.0" clap = { version = "4", features = ["color", "derive"] } colored = "3" +htq = { git = "https://github.com/oxidecomputer/htq", branch = "sector-001" } libloading = { version = "0.8" } num = { version = "0.4", features = ["serde"] } p4 = { path = "p4" } @@ -41,5 +42,6 @@ serde = "1.0" serde_tokenstream = "0.2" syn = "2.0" tempfile = "3.3" +thiserror = "1.0.63" usdt = "0.5.0" xfr = { git = "https://github.com/oxidecomputer/xfr" } diff --git a/codegen/htq/Cargo.toml b/codegen/htq/Cargo.toml index 93a57196..56bbcdd7 100644 --- a/codegen/htq/Cargo.toml +++ b/codegen/htq/Cargo.toml @@ -4,7 +4,6 @@ version = "0.1.0" edition = "2021" [dependencies] -p4 = { path = "../../p4" } -htq = { git = "https://github.com/oxidecomputer/htq", branch = "sector-001" } -#htq = { path = "/Users/ry/src/htq" } -thiserror = "1.0.63" +p4.workspace = true +htq.workspace = true +thiserror.workspace = true