Skip to content

Commit a1f4973

Browse files
committed
auto merge of #16662 : pczarn/rust/format-fmtstr-opt, r=brson
Based on an observation that strings and arguments are always interleaved, thanks to #15832. Additionally optimize invocations where formatting parameters are unspecified for all arguments, e.g. `"{} {:?} {:x}"`, by emptying the `__STATIC_FMTARGS` array. Next, `Arguments::new` replaces an empty slice with `None` so that passing empty `__STATIC_FMTARGS` generates slightly less machine code when `Arguments::new` is inlined. Furthermore, formatting itself treats these cases separately without making redundant copies of formatting parameters. All in all, this adds a single mov instruction per `write!` in most cases. That's why code size has increased.
2 parents 6511064 + fcf88b8 commit a1f4973

File tree

5 files changed

+232
-66
lines changed

5 files changed

+232
-66
lines changed

src/libcore/fmt/mod.rs

+117
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,33 @@ impl<'a> Arguments<'a> {
113113
/// Arguments structure. The compiler inserts an `unsafe` block to call this,
114114
/// which is valid because the compiler performs all necessary validation to
115115
/// ensure that the resulting call to format/write would be safe.
116+
#[cfg(not(stage0))]
117+
#[doc(hidden)] #[inline]
118+
pub unsafe fn new<'a>(pieces: &'static [&'static str],
119+
args: &'a [Argument<'a>]) -> Arguments<'a> {
120+
Arguments {
121+
pieces: mem::transmute(pieces),
122+
fmt: None,
123+
args: args
124+
}
125+
}
126+
127+
/// This function is used to specify nonstandard formatting parameters.
128+
/// The `pieces` array must be at least as long as `fmt` to construct
129+
/// a valid Arguments structure.
130+
#[cfg(not(stage0))]
131+
#[doc(hidden)] #[inline]
132+
pub unsafe fn with_placeholders<'a>(pieces: &'static [&'static str],
133+
fmt: &'static [rt::Argument<'static>],
134+
args: &'a [Argument<'a>]) -> Arguments<'a> {
135+
Arguments {
136+
pieces: mem::transmute(pieces),
137+
fmt: Some(mem::transmute(fmt)),
138+
args: args
139+
}
140+
}
141+
142+
#[cfg(stage0)]
116143
#[doc(hidden)] #[inline]
117144
pub unsafe fn new<'a>(fmt: &'static [rt::Piece<'static>],
118145
args: &'a [Argument<'a>]) -> Arguments<'a> {
@@ -129,6 +156,20 @@ impl<'a> Arguments<'a> {
129156
/// and pass it to a function or closure, passed as the first argument. The
130157
/// macro validates the format string at compile-time so usage of the `write`
131158
/// and `format` functions can be safely performed.
159+
#[cfg(not(stage0))]
160+
pub struct Arguments<'a> {
161+
// Format string pieces to print.
162+
pieces: &'a [&'a str],
163+
164+
// Placeholder specs, or `None` if all specs are default (as in "{}{}").
165+
fmt: Option<&'a [rt::Argument<'a>]>,
166+
167+
// Dynamic arguments for interpolation, to be interleaved with string
168+
// pieces. (Every argument is preceded by a string piece.)
169+
args: &'a [Argument<'a>],
170+
}
171+
172+
#[cfg(stage0)] #[doc(hidden)]
132173
pub struct Arguments<'a> {
133174
fmt: &'a [rt::Piece<'a>],
134175
args: &'a [Argument<'a>],
@@ -255,6 +296,18 @@ uniform_fn_call_workaround! {
255296
secret_upper_exp, UpperExp;
256297
}
257298

299+
#[cfg(not(stage0))]
300+
static DEFAULT_ARGUMENT: rt::Argument<'static> = rt::Argument {
301+
position: rt::ArgumentNext,
302+
format: rt::FormatSpec {
303+
fill: ' ',
304+
align: rt::AlignUnknown,
305+
flags: 0,
306+
precision: rt::CountImplied,
307+
width: rt::CountImplied,
308+
}
309+
};
310+
258311
/// The `write` function takes an output stream, a precompiled format string,
259312
/// and a list of arguments. The arguments will be formatted according to the
260313
/// specified format string into the output stream provided.
@@ -263,6 +316,51 @@ uniform_fn_call_workaround! {
263316
///
264317
/// * output - the buffer to write output to
265318
/// * args - the precompiled arguments generated by `format_args!`
319+
#[cfg(not(stage0))]
320+
pub fn write(output: &mut FormatWriter, args: &Arguments) -> Result {
321+
let mut formatter = Formatter {
322+
flags: 0,
323+
width: None,
324+
precision: None,
325+
buf: output,
326+
align: rt::AlignUnknown,
327+
fill: ' ',
328+
args: args.args,
329+
curarg: args.args.iter(),
330+
};
331+
332+
let mut pieces = args.pieces.iter();
333+
334+
match args.fmt {
335+
None => {
336+
// We can use default formatting parameters for all arguments.
337+
for _ in range(0, args.args.len()) {
338+
try!(formatter.buf.write(pieces.next().unwrap().as_bytes()));
339+
try!(formatter.run(&DEFAULT_ARGUMENT));
340+
}
341+
}
342+
Some(fmt) => {
343+
// Every spec has a corresponding argument that is preceded by
344+
// a string piece.
345+
for (arg, piece) in fmt.iter().zip(pieces.by_ref()) {
346+
try!(formatter.buf.write(piece.as_bytes()));
347+
try!(formatter.run(arg));
348+
}
349+
}
350+
}
351+
352+
// There can be only one trailing string piece left.
353+
match pieces.next() {
354+
Some(piece) => {
355+
try!(formatter.buf.write(piece.as_bytes()));
356+
}
357+
None => {}
358+
}
359+
360+
Ok(())
361+
}
362+
363+
#[cfg(stage0)] #[doc(hidden)]
266364
pub fn write(output: &mut FormatWriter, args: &Arguments) -> Result {
267365
let mut formatter = Formatter {
268366
flags: 0,
@@ -285,7 +383,26 @@ impl<'a> Formatter<'a> {
285383
// First up is the collection of functions used to execute a format string
286384
// at runtime. This consumes all of the compile-time statics generated by
287385
// the format! syntax extension.
386+
#[cfg(not(stage0))]
387+
fn run(&mut self, arg: &rt::Argument) -> Result {
388+
// Fill in the format parameters into the formatter
389+
self.fill = arg.format.fill;
390+
self.align = arg.format.align;
391+
self.flags = arg.format.flags;
392+
self.width = self.getcount(&arg.format.width);
393+
self.precision = self.getcount(&arg.format.precision);
394+
395+
// Extract the correct argument
396+
let value = match arg.position {
397+
rt::ArgumentNext => { *self.curarg.next().unwrap() }
398+
rt::ArgumentIs(i) => self.args[i],
399+
};
400+
401+
// Then actually do some printing
402+
(value.formatter)(value.value, self)
403+
}
288404

405+
#[cfg(stage0)] #[doc(hidden)]
289406
fn run(&mut self, piece: &rt::Piece) -> Result {
290407
match *piece {
291408
rt::String(s) => self.buf.write(s.as_bytes()),

src/libcore/fmt/rt.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
//! These definitions are similar to their `ct` equivalents, but differ in that
1515
//! these can be statically allocated and are slightly optimized for the runtime
1616
17-
17+
#[cfg(stage0)]
1818
#[doc(hidden)]
1919
pub enum Piece<'a> {
2020
String(&'a str),

src/libcoretest/fmt/mod.rs

+7
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,10 @@
99
// except according to those terms.
1010

1111
mod num;
12+
13+
#[test]
14+
fn test_format_flags() {
15+
// No residual flags left by pointer formatting
16+
let p = "".as_ptr();
17+
assert_eq!(format!("{:p} {:x}", p, 16u), format!("{:p} 10", p));
18+
}

0 commit comments

Comments
 (0)