Add syntax for cold blocks to CLIF.

This commit adds support for denoting cold blocks in the CLIF text
format as follows:

```plain

function %f() {
block0(...):
  ...

block1 cold:
  ...

block2(...) cold:
  ...

block3:
  ...
```

With this syntax, we are able to see the cold-block flag in CLIF, we can
write tests using it, and it is preserved when round-tripping.

Fixes #3701.
This commit is contained in:
Chris Fallin
2022-01-20 15:37:07 -08:00
parent 90e7cef56c
commit 51649d56b7
3 changed files with 65 additions and 6 deletions

View File

@@ -216,12 +216,18 @@ pub fn write_block_header(
block: Block, block: Block,
indent: usize, indent: usize,
) -> fmt::Result { ) -> fmt::Result {
let cold = if func.layout.is_cold(block) {
" cold"
} else {
""
};
// The `indent` is the instruction indentation. block headers are 4 spaces out from that. // The `indent` is the instruction indentation. block headers are 4 spaces out from that.
write!(w, "{1:0$}{2}", indent - 4, "", block)?; write!(w, "{1:0$}{2}", indent - 4, "", block)?;
let mut args = func.dfg.block_params(block).iter().cloned(); let mut args = func.dfg.block_params(block).iter().cloned();
match args.next() { match args.next() {
None => return writeln!(w, ":"), None => return writeln!(w, "{}:", cold),
Some(arg) => { Some(arg) => {
write!(w, "(")?; write!(w, "(")?;
write_arg(w, func, arg)?; write_arg(w, func, arg)?;
@@ -232,7 +238,7 @@ pub fn write_block_header(
write!(w, ", ")?; write!(w, ", ")?;
write_arg(w, func, arg)?; write_arg(w, func, arg)?;
} }
writeln!(w, "):") writeln!(w, "){}:", cold)
} }
fn decorate_block<FW: FuncWriter>( fn decorate_block<FW: FuncWriter>(
@@ -666,4 +672,26 @@ mod tests {
"function u0:0() fast {\nblock0(v3: i32):\n v0 -> v3\n v2 -> v0\n v4 = iconst.i32 42\n v5 = iadd v0, v0\n v1 -> v5\n v6 = iconst.i32 23\n v7 = iadd v1, v1\n}\n" "function u0:0() fast {\nblock0(v3: i32):\n v0 -> v3\n v2 -> v0\n v4 = iconst.i32 42\n v5 = iadd v0, v0\n v1 -> v5\n v6 = iconst.i32 23\n v7 = iadd v1, v1\n}\n"
); );
} }
#[test]
fn cold_blocks() {
let mut func = Function::new();
{
let mut pos = FuncCursor::new(&mut func);
let block0 = pos.func.dfg.make_block();
pos.insert_block(block0);
pos.func.layout.set_cold(block0);
let block1 = pos.func.dfg.make_block();
pos.insert_block(block1);
pos.func.dfg.append_block_param(block1, types::I32);
pos.func.layout.set_cold(block1);
}
assert_eq!(
func.to_string(),
"function u0:0() fast {\nblock0 cold:\n\nblock1(v0: i32) cold:\n}\n"
);
}
} }

View File

@@ -34,6 +34,7 @@ pub enum Token<'a> {
Type(types::Type), // i32, f32, b32x4, ... Type(types::Type), // i32, f32, b32x4, ...
Value(Value), // v12, v7 Value(Value), // v12, v7
Block(Block), // block3 Block(Block), // block3
Cold, // cold (flag on block)
StackSlot(u32), // ss3 StackSlot(u32), // ss3
GlobalValue(u32), // gv3 GlobalValue(u32), // gv3
Heap(u32), // heap2 Heap(u32), // heap2
@@ -326,6 +327,7 @@ impl<'a> Lexer<'a> {
.unwrap_or_else(|| match text { .unwrap_or_else(|| match text {
"iflags" => Token::Type(types::IFLAGS), "iflags" => Token::Type(types::IFLAGS),
"fflags" => Token::Type(types::FFLAGS), "fflags" => Token::Type(types::FFLAGS),
"cold" => Token::Cold,
_ => Token::Identifier(text), _ => Token::Identifier(text),
}), }),
loc, loc,

View File

@@ -447,6 +447,11 @@ impl Context {
self.function.layout.append_block(block); self.function.layout.append_block(block);
Ok(block) Ok(block)
} }
/// Set a block as cold.
fn set_cold_block(&mut self, block: Block) {
self.function.layout.set_cold(block);
}
} }
impl<'a> Parser<'a> { impl<'a> Parser<'a> {
@@ -1856,7 +1861,8 @@ impl<'a> Parser<'a> {
// Parse a basic block, add contents to `ctx`. // Parse a basic block, add contents to `ctx`.
// //
// extended-basic-block ::= * block-header { instruction } // extended-basic-block ::= * block-header { instruction }
// block-header ::= Block(block) [block-params] ":" // block-header ::= Block(block) [block-params] [block-flags] ":"
// block-flags ::= [Cold]
// //
fn parse_basic_block(&mut self, ctx: &mut Context) -> ParseResult<()> { fn parse_basic_block(&mut self, ctx: &mut Context) -> ParseResult<()> {
// Collect comments for the next block. // Collect comments for the next block.
@@ -1869,12 +1875,16 @@ impl<'a> Parser<'a> {
return Err(self.error("too many blocks")); return Err(self.error("too many blocks"));
} }
if !self.optional(Token::Colon) { if self.token() == Some(Token::LPar) {
// block-header ::= Block(block) [ * block-params ] ":"
self.parse_block_params(ctx, block)?; self.parse_block_params(ctx, block)?;
self.match_token(Token::Colon, "expected ':' after block parameters")?;
} }
if self.optional(Token::Cold) {
ctx.set_cold_block(block);
}
self.match_token(Token::Colon, "expected ':' after block parameters")?;
// Collect any trailing comments. // Collect any trailing comments.
self.token(); self.token();
self.claim_gathered_comments(block); self.claim_gathered_comments(block);
@@ -3728,4 +3738,23 @@ mod tests {
"0x00000003000000020000000100000000" "0x00000003000000020000000100000000"
); );
} }
#[test]
fn parse_cold_blocks() {
let code = "function %test() {
block0 cold:
return
block1(v0: i32) cold:
return
block2(v1: i32):
return
}";
let mut parser = Parser::new(code);
let func = parser.parse_function().unwrap().0;
assert_eq!(func.layout.blocks().count(), 3);
assert!(func.layout.is_cold(Block::from_u32(0)));
assert!(func.layout.is_cold(Block::from_u32(1)));
assert!(!func.layout.is_cold(Block::from_u32(2)));
}
} }