diff options
| author | Piotr Czarnecki <pioczarn@gmail.com> | 2014-08-25 14:26:18 +0100 |
|---|---|---|
| committer | Piotr Czarnecki <pioczarn@gmail.com> | 2014-09-09 20:34:41 +0100 |
| commit | 5aaa60693222c98c92fbde830f9a85c24f2e3f4f (patch) | |
| tree | 8e9008e1c6e88131acd62ed5c6cb9977962101cf /src/libsyntax | |
| parent | 696367fb8de63a3ff264c65981457b9fbd0e7b06 (diff) | |
| download | rust-5aaa60693222c98c92fbde830f9a85c24f2e3f4f.tar.gz rust-5aaa60693222c98c92fbde830f9a85c24f2e3f4f.zip | |
Optimize for the most common cases of `format!`
Format specs are ignored and not stored in case they're all default. Restore default formatting parameters during iteration. Pass `None` instead of empty slices of format specs to take advantage of non-nullable pointer optimization. Generate a call to one of two functions of `fmt::Argument`.
Diffstat (limited to 'src/libsyntax')
| -rw-r--r-- | src/libsyntax/ext/format.rs | 122 |
1 files changed, 73 insertions, 49 deletions
diff --git a/src/libsyntax/ext/format.rs b/src/libsyntax/ext/format.rs index fc4d8c83c51..0bb32c73ca2 100644 --- a/src/libsyntax/ext/format.rs +++ b/src/libsyntax/ext/format.rs @@ -56,6 +56,9 @@ struct Context<'a, 'b:'a> { pieces: Vec<Gc<ast::Expr>>, /// Collection of string literals str_pieces: Vec<Gc<ast::Expr>>, + /// Stays `true` if all formatting parameters are default (as in "{}{}"). + all_pieces_simple: bool, + name_positions: HashMap<String, uint>, method_statics: Vec<Gc<ast::Item>>, @@ -383,7 +386,6 @@ impl<'a, 'b> Context<'a, 'b> { /// Translate a `parse::Piece` to a static `rt::Argument` or append /// to the `literal` string. fn trans_piece(&mut self, piece: &parse::Piece) -> Option<Gc<ast::Expr>> { - // let mut is_not_default = true; let sp = self.fmtsp; match *piece { parse::String(s) => { @@ -416,8 +418,25 @@ impl<'a, 'b> Context<'a, 'b> { } }; - // Translate the format + let simple_arg = parse::Argument { + position: parse::ArgumentNext, + format: parse::FormatSpec { + fill: arg.format.fill, + align: parse::AlignUnknown, + flags: 0, + precision: parse::CountImplied, + width: parse::CountImplied, + ty: arg.format.ty + } + }; + let fill = match arg.format.fill { Some(c) => c, None => ' ' }; + + if *arg != simple_arg || fill != ' ' { + self.all_pieces_simple = false; + } + + // Translate the format let fill = self.ecx.expr_lit(sp, ast::LitChar(fill)); let align = match arg.format.align { parse::AlignLeft => { @@ -453,6 +472,26 @@ impl<'a, 'b> Context<'a, 'b> { } } + fn item_static_array(&self, + name: ast::Ident, + piece_ty: Gc<ast::Ty>, + pieces: Vec<Gc<ast::Expr>>) + -> ast::Stmt + { + let pieces_len = self.ecx.expr_uint(self.fmtsp, pieces.len()); + let fmt = self.ecx.expr_vec(self.fmtsp, pieces); + let ty = ast::TyFixedLengthVec( + piece_ty, + pieces_len + ); + let ty = self.ecx.ty(self.fmtsp, ty); + let st = ast::ItemStatic(ty, ast::MutImmutable, fmt); + let item = self.ecx.item(self.fmtsp, name, + self.static_attrs(), st); + let decl = respan(self.fmtsp, ast::DeclItem(item)); + respan(self.fmtsp, ast::StmtDecl(box(GC) decl, ast::DUMMY_NODE_ID)) + } + /// Actually builds the expression which the iformat! block will be expanded /// to fn to_expr(&self, invocation: Invocation) -> Gc<ast::Expr> { @@ -471,54 +510,31 @@ impl<'a, 'b> Context<'a, 'b> { // Next, build up the static array which will become our precompiled // format "string" - let fmt = self.ecx.expr_vec(self.fmtsp, self.str_pieces.clone()); - let piece_ty = self.ecx.ty_rptr(self.fmtsp, - self.ecx.ty_ident(self.fmtsp, - self.ecx.ident_of("str")), - Some(self.ecx.lifetime(self.fmtsp, - self.ecx.ident_of( - "'static").name)), - ast::MutImmutable); - - let ty = ast::TyFixedLengthVec( - piece_ty, - self.ecx.expr_uint(self.fmtsp, self.str_pieces.len()) - ); - let ty = self.ecx.ty(self.fmtsp, ty); - let st = ast::ItemStatic(ty, ast::MutImmutable, fmt); let static_str_name = self.ecx.ident_of("__STATIC_FMTSTR"); - let item = self.ecx.item(self.fmtsp, static_str_name, - self.static_attrs(), st); - let decl = respan(self.fmtsp, ast::DeclItem(item)); - lets.push(box(GC) respan(self.fmtsp, - ast::StmtDecl(box(GC) decl, ast::DUMMY_NODE_ID))); - - // Then, build up the static array which will become our precompiled - // format "string" - let fmt = self.ecx.expr_vec(self.fmtsp, self.pieces.clone()); - let piece_ty = self.ecx.ty_path(self.ecx.path_all( + let static_lifetime = self.ecx.lifetime(self.fmtsp, self.ecx.ident_of("'static").name); + let piece_ty = self.ecx.ty_rptr( self.fmtsp, - true, vec!( - self.ecx.ident_of("std"), - self.ecx.ident_of("fmt"), - self.ecx.ident_of("rt"), - self.ecx.ident_of("Argument")), - vec!(self.ecx.lifetime(self.fmtsp, - self.ecx.ident_of("'static").name)), - Vec::new() - ), None); - let ty = ast::TyFixedLengthVec( - piece_ty, - self.ecx.expr_uint(self.fmtsp, self.pieces.len()) - ); - let ty = self.ecx.ty(self.fmtsp, ty); - let st = ast::ItemStatic(ty, ast::MutImmutable, fmt); + self.ecx.ty_ident(self.fmtsp, self.ecx.ident_of("str")), + Some(static_lifetime), + ast::MutImmutable); + lets.push(box(GC) self.item_static_array(static_str_name, + piece_ty, + self.str_pieces.clone())); + + // Then, build up the static array which will store our precompiled + // nonstandard placeholders, if there are any. let static_args_name = self.ecx.ident_of("__STATIC_FMTARGS"); - let item = self.ecx.item(self.fmtsp, static_args_name, - self.static_attrs(), st); - let decl = respan(self.fmtsp, ast::DeclItem(item)); - lets.push(box(GC) respan(self.fmtsp, - ast::StmtDecl(box(GC) decl, ast::DUMMY_NODE_ID))); + if !self.all_pieces_simple { + let piece_ty = self.ecx.ty_path(self.ecx.path_all( + self.fmtsp, + true, self.rtpath("Argument"), + vec![static_lifetime], + vec![] + ), None); + lets.push(box(GC) self.item_static_array(static_args_name, + piece_ty, + self.pieces.clone())); + } // Right now there is a bug such that for the expression: // foo(bar(&1)) @@ -565,13 +581,20 @@ impl<'a, 'b> Context<'a, 'b> { // Now create the fmt::Arguments struct with all our locals we created. let pieces = self.ecx.expr_ident(self.fmtsp, static_str_name); - let fmt = self.ecx.expr_ident(self.fmtsp, static_args_name); let args_slice = self.ecx.expr_ident(self.fmtsp, slicename); + + let (fn_name, fn_args) = if self.all_pieces_simple { + ("new", vec![pieces, args_slice]) + } else { + let fmt = self.ecx.expr_ident(self.fmtsp, static_args_name); + ("with_placeholders", vec![pieces, fmt, args_slice]) + }; + let result = self.ecx.expr_call_global(self.fmtsp, vec!( self.ecx.ident_of("std"), self.ecx.ident_of("fmt"), self.ecx.ident_of("Arguments"), - self.ecx.ident_of("new")), vec!(pieces, fmt, args_slice)); + self.ecx.ident_of(fn_name)), fn_args); // We did all the work of making sure that the arguments // structure is safe, so we can safely have an unsafe block. @@ -741,6 +764,7 @@ pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt, sp: Span, literal: String::new(), pieces: Vec::new(), str_pieces: Vec::new(), + all_pieces_simple: true, method_statics: Vec::new(), fmtsp: sp, }; |
