Move entry_block() into Layout.
The single entry block in a function is simply the first block in the layout. Remove the 'entry' keyword from the textual IL, the lexer and parser.
This commit is contained in:
@@ -26,7 +26,7 @@ class CretonneLexer(RegexLexer):
|
|||||||
(r'[-+]?(\d+\.\d+([eE]\d+)?|[sq]NaN|Inf)', Number.Float),
|
(r'[-+]?(\d+\.\d+([eE]\d+)?|[sq]NaN|Inf)', Number.Float),
|
||||||
(r'[-+]?\d+', Number.Integer),
|
(r'[-+]?\d+', Number.Integer),
|
||||||
# Reserved words.
|
# Reserved words.
|
||||||
(keywords('function', 'entry'), Keyword),
|
(keywords('function'), Keyword),
|
||||||
# Known attributes.
|
# Known attributes.
|
||||||
(keywords('align', 'aligntrap', 'uext', 'sext', 'inreg'), Name.Attribute),
|
(keywords('align', 'aligntrap', 'uext', 'sext', 'inreg'), Name.Attribute),
|
||||||
# Well known value types.
|
# Well known value types.
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
function average(i32, i32) -> f32 {
|
function average(i32, i32) -> f32 {
|
||||||
ss1 = stack_slot 8, align 4 ; Stack slot for ``sum``.
|
ss1 = stack_slot 8, align 4 ; Stack slot for ``sum``.
|
||||||
|
|
||||||
entry ebb1(v1: i32, v2: i32):
|
ebb1(v1: i32, v2: i32):
|
||||||
v3 = f64const 0x0.0
|
v3 = f64const 0x0.0
|
||||||
stack_store v3, ss1
|
stack_store v3, ss1
|
||||||
brz v2, ebb3 ; Handle count == 0.
|
brz v2, ebb3 ; Handle count == 0.
|
||||||
|
|||||||
@@ -41,9 +41,9 @@ that can be referenced inside the function. In the example above, the preamble
|
|||||||
declares a single local variable, ``ss1``.
|
declares a single local variable, ``ss1``.
|
||||||
|
|
||||||
After the preamble follows the :term:`function body` which consists of
|
After the preamble follows the :term:`function body` which consists of
|
||||||
:term:`extended basic block`\s, one of which is marked as the :term:`entry
|
:term:`extended basic block`\s, the first of which is the :term:`entry block`.
|
||||||
block`. Every EBB ends with a :term:`terminator instruction`, so execution can
|
Every EBB ends with a :term:`terminator instruction`, so execution can never
|
||||||
never fall through to the next EBB without an explicit branch.
|
fall through to the next EBB without an explicit branch.
|
||||||
|
|
||||||
A ``.cton`` file consists of a sequence of independent function definitions:
|
A ``.cton`` file consists of a sequence of independent function definitions:
|
||||||
|
|
||||||
@@ -395,7 +395,7 @@ This simple example illustrates direct function calls and signatures::
|
|||||||
function gcd(i32 uext, i32 uext) -> i32 uext "C" {
|
function gcd(i32 uext, i32 uext) -> i32 uext "C" {
|
||||||
f1 = function divmod(i32 uext, i32 uext) -> i32 uext, i32 uext
|
f1 = function divmod(i32 uext, i32 uext) -> i32 uext, i32 uext
|
||||||
|
|
||||||
entry ebb1(v1: i32, v2: i32):
|
ebb1(v1: i32, v2: i32):
|
||||||
brz v2, ebb2
|
brz v2, ebb2
|
||||||
v3, v4 = call f1(v1, v2)
|
v3, v4 = call f1(v1, v2)
|
||||||
br ebb1(v2, v4)
|
br ebb1(v2, v4)
|
||||||
@@ -625,7 +625,7 @@ A small example using heaps::
|
|||||||
function vdup(i32, i32) {
|
function vdup(i32, i32) {
|
||||||
h1 = heap "main"
|
h1 = heap "main"
|
||||||
|
|
||||||
entry ebb1(v1: i32, v2: i32):
|
ebb1(v1: i32, v2: i32):
|
||||||
v3 = heap_load.i32x4 h1, v1, 0
|
v3 = heap_load.i32x4 h1, v1, 0
|
||||||
v4 = heap_addr h1, v2, 32 ; Shared range check for two stores.
|
v4 = heap_addr h1, v2, 32 ; Shared range check for two stores.
|
||||||
store v3, v4, 0
|
store v3, v4, 0
|
||||||
@@ -878,9 +878,9 @@ Glossary
|
|||||||
|
|
||||||
entry block
|
entry block
|
||||||
The :term:`EBB` that is executed first in a function. Currently, a
|
The :term:`EBB` that is executed first in a function. Currently, a
|
||||||
Cretonne function must have exactly one entry block. The types of the
|
Cretonne function must have exactly one entry block which must be the
|
||||||
entry block arguments must match the types of arguments in the function
|
first block in the function. The types of the entry block arguments must
|
||||||
signature.
|
match the types of arguments in the function signature.
|
||||||
|
|
||||||
stack slot
|
stack slot
|
||||||
A fixed size memory allocation in the current function's activation
|
A fixed size memory allocation in the current function's activation
|
||||||
|
|||||||
@@ -104,11 +104,8 @@ impl ControlFlowGraph {
|
|||||||
&self.data[ebb].successors
|
&self.data[ebb].successors
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn postorder_ebbs(&self) -> Vec<Ebb> {
|
pub fn postorder_ebbs(&self, entry: Ebb) -> Vec<Ebb> {
|
||||||
if self.len() < 1 {
|
let mut stack_a = vec![entry];
|
||||||
return Vec::new();
|
|
||||||
}
|
|
||||||
let mut stack_a = vec![Ebb::with_number(0).unwrap()];
|
|
||||||
let mut stack_b = Vec::new();
|
let mut stack_b = Vec::new();
|
||||||
while stack_a.len() > 0 {
|
while stack_a.len() > 0 {
|
||||||
let cur = stack_a.pop().unwrap();
|
let cur = stack_a.pop().unwrap();
|
||||||
@@ -282,7 +279,7 @@ mod tests {
|
|||||||
func.layout.append_inst(jmp_ebb2_ebb5, ebb2);
|
func.layout.append_inst(jmp_ebb2_ebb5, ebb2);
|
||||||
|
|
||||||
let cfg = ControlFlowGraph::new(&func);
|
let cfg = ControlFlowGraph::new(&func);
|
||||||
assert_eq!(cfg.postorder_ebbs(),
|
assert_eq!(cfg.postorder_ebbs(func.layout.entry_block().unwrap()),
|
||||||
vec![ebb0, ebb2, ebb5, ebb4, ebb1, ebb3]);
|
vec![ebb0, ebb2, ebb5, ebb4, ebb1, ebb3]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -309,6 +306,7 @@ mod tests {
|
|||||||
func.layout.append_inst(jmp_ebb2_ebb3, ebb2);
|
func.layout.append_inst(jmp_ebb2_ebb3, ebb2);
|
||||||
|
|
||||||
let cfg = ControlFlowGraph::new(&func);
|
let cfg = ControlFlowGraph::new(&func);
|
||||||
assert_eq!(cfg.postorder_ebbs(), vec![ebb0, ebb1, ebb2, ebb3]);
|
assert_eq!(cfg.postorder_ebbs(func.layout.entry_block().unwrap()),
|
||||||
|
vec![ebb0, ebb1, ebb2, ebb3]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -108,6 +108,12 @@ impl Layout {
|
|||||||
next: self.first_ebb,
|
next: self.first_ebb,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the function's entry block.
|
||||||
|
/// This is simply the first EBB in the layout order.
|
||||||
|
pub fn entry_block(&self) -> Option<Ebb> {
|
||||||
|
self.first_ebb
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
@@ -414,8 +420,11 @@ mod tests {
|
|||||||
let e0 = Ebb::new(0);
|
let e0 = Ebb::new(0);
|
||||||
let e1 = Ebb::new(1);
|
let e1 = Ebb::new(1);
|
||||||
|
|
||||||
|
assert_eq!(layout.entry_block(), None);
|
||||||
layout.append_ebb(e0);
|
layout.append_ebb(e0);
|
||||||
|
assert_eq!(layout.entry_block(), Some(e0));
|
||||||
layout.append_ebb(e1);
|
layout.append_ebb(e1);
|
||||||
|
assert_eq!(layout.entry_block(), Some(e0));
|
||||||
|
|
||||||
let i0 = Inst::new(0);
|
let i0 = Inst::new(0);
|
||||||
let i1 = Inst::new(1);
|
let i1 = Inst::new(1);
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ pub mod layout;
|
|||||||
|
|
||||||
use ir::types::{FunctionName, Signature};
|
use ir::types::{FunctionName, Signature};
|
||||||
use entity_map::EntityRef;
|
use entity_map::EntityRef;
|
||||||
use ir::entities::{Ebb, NO_EBB, StackSlot};
|
use ir::entities::StackSlot;
|
||||||
use ir::dfg::DataFlowGraph;
|
use ir::dfg::DataFlowGraph;
|
||||||
use ir::layout::Layout;
|
use ir::layout::Layout;
|
||||||
use std::fmt::{self, Debug, Display, Formatter};
|
use std::fmt::{self, Debug, Display, Formatter};
|
||||||
@@ -24,9 +24,6 @@ pub struct Function {
|
|||||||
/// Signature of this function.
|
/// Signature of this function.
|
||||||
signature: Signature,
|
signature: Signature,
|
||||||
|
|
||||||
/// The entry block.
|
|
||||||
pub entry_block: Ebb,
|
|
||||||
|
|
||||||
/// Stack slots allocated in this function.
|
/// Stack slots allocated in this function.
|
||||||
stack_slots: Vec<StackSlotData>,
|
stack_slots: Vec<StackSlotData>,
|
||||||
|
|
||||||
@@ -43,7 +40,6 @@ impl Function {
|
|||||||
Function {
|
Function {
|
||||||
name: name,
|
name: name,
|
||||||
signature: sig,
|
signature: sig,
|
||||||
entry_block: NO_EBB,
|
|
||||||
stack_slots: Vec::new(),
|
stack_slots: Vec::new(),
|
||||||
dfg: DataFlowGraph::new(),
|
dfg: DataFlowGraph::new(),
|
||||||
layout: Layout::new(),
|
layout: Layout::new(),
|
||||||
|
|||||||
@@ -32,7 +32,6 @@ pub enum Token<'a> {
|
|||||||
Equal, // '='
|
Equal, // '='
|
||||||
Arrow, // '->'
|
Arrow, // '->'
|
||||||
Function, // 'function'
|
Function, // 'function'
|
||||||
Entry, // 'entry'
|
|
||||||
Float(&'a str), // Floating point immediate
|
Float(&'a str), // Floating point immediate
|
||||||
Integer(&'a str), // Integer immediate
|
Integer(&'a str), // Integer immediate
|
||||||
Type(types::Type), // i32, f32, b32x4, ...
|
Type(types::Type), // i32, f32, b32x4, ...
|
||||||
@@ -270,7 +269,6 @@ impl<'a> Lexer<'a> {
|
|||||||
fn keyword(text: &str) -> Option<Token<'a>> {
|
fn keyword(text: &str) -> Option<Token<'a>> {
|
||||||
match text {
|
match text {
|
||||||
"function" => Some(Token::Function),
|
"function" => Some(Token::Function),
|
||||||
"entry" => Some(Token::Entry),
|
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -444,7 +442,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn lex_identifiers() {
|
fn lex_identifiers() {
|
||||||
let mut lex = Lexer::new("v0 v00 vx01 ebb1234567890 ebb5234567890 entry v1x vx1 vxvx4 \
|
let mut lex = Lexer::new("v0 v00 vx01 ebb1234567890 ebb5234567890 v1x vx1 vxvx4 \
|
||||||
function0 function b1 i32x4 f32x5");
|
function0 function b1 i32x4 f32x5");
|
||||||
assert_eq!(lex.next(),
|
assert_eq!(lex.next(),
|
||||||
token(Token::Value(Value::direct_with_number(0).unwrap()), 1));
|
token(Token::Value(Value::direct_with_number(0).unwrap()), 1));
|
||||||
@@ -453,7 +451,6 @@ mod tests {
|
|||||||
assert_eq!(lex.next(),
|
assert_eq!(lex.next(),
|
||||||
token(Token::Ebb(Ebb::with_number(1234567890).unwrap()), 1));
|
token(Token::Ebb(Ebb::with_number(1234567890).unwrap()), 1));
|
||||||
assert_eq!(lex.next(), token(Token::Identifier("ebb5234567890"), 1));
|
assert_eq!(lex.next(), token(Token::Identifier("ebb5234567890"), 1));
|
||||||
assert_eq!(lex.next(), token(Token::Entry, 1));
|
|
||||||
assert_eq!(lex.next(), token(Token::Identifier("v1x"), 1));
|
assert_eq!(lex.next(), token(Token::Identifier("v1x"), 1));
|
||||||
assert_eq!(lex.next(),
|
assert_eq!(lex.next(),
|
||||||
token(Token::Value(Value::table_with_number(1).unwrap()), 1));
|
token(Token::Value(Value::table_with_number(1).unwrap()), 1));
|
||||||
|
|||||||
@@ -578,22 +578,14 @@ impl<'a> Parser<'a> {
|
|||||||
// Parse an extended basic block, add contents to `ctx`.
|
// Parse an extended basic block, add contents to `ctx`.
|
||||||
//
|
//
|
||||||
// extended-basic-block ::= * ebb-header { instruction }
|
// extended-basic-block ::= * ebb-header { instruction }
|
||||||
// ebb-header ::= ["entry"] Ebb(ebb) [ebb-args] ":"
|
// ebb-header ::= Ebb(ebb) [ebb-args] ":"
|
||||||
//
|
//
|
||||||
fn parse_extended_basic_block(&mut self, ctx: &mut Context) -> Result<()> {
|
fn parse_extended_basic_block(&mut self, ctx: &mut Context) -> Result<()> {
|
||||||
let is_entry = self.optional(Token::Entry);
|
|
||||||
let ebb_num = try!(self.match_ebb("expected EBB header"));
|
let ebb_num = try!(self.match_ebb("expected EBB header"));
|
||||||
let ebb = try!(ctx.add_ebb(ebb_num, &self.loc));
|
let ebb = try!(ctx.add_ebb(ebb_num, &self.loc));
|
||||||
|
|
||||||
if is_entry {
|
|
||||||
if ctx.function.entry_block != NO_EBB {
|
|
||||||
return err!(self.loc, "multiple entry blocks in function");
|
|
||||||
}
|
|
||||||
ctx.function.entry_block = ebb;
|
|
||||||
}
|
|
||||||
|
|
||||||
if !self.optional(Token::Colon) {
|
if !self.optional(Token::Colon) {
|
||||||
// ebb-header ::= ["entry"] Ebb(ebb) [ * ebb-args ] ":"
|
// ebb-header ::= Ebb(ebb) [ * ebb-args ] ":"
|
||||||
try!(self.parse_ebb_args(ctx, ebb));
|
try!(self.parse_ebb_args(ctx, ebb));
|
||||||
try!(self.match_token(Token::Colon, "expected ':' after EBB arguments"));
|
try!(self.match_token(Token::Colon, "expected ':' after EBB arguments"));
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user