Skip to content

Commit

Permalink
Add a Context::compile() function which runs all compiler passes.
Browse files Browse the repository at this point in the history
This is the main entry point to the code generator. It returns the
computed size of the functions code.

Also add a 'test compile' command which runs the whole code generation
pipeline.
  • Loading branch information
Jakob Stoklund Olesen committed Jul 12, 2017
1 parent 07a96e6 commit 6cc729a
Show file tree
Hide file tree
Showing 5 changed files with 143 additions and 8 deletions.
9 changes: 7 additions & 2 deletions lib/cretonne/src/binemit/relaxation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,12 @@ use binemit::CodeOffset;
use ir::{Function, DataFlowGraph, Cursor, InstructionData, Opcode, InstEncodings};
use isa::{TargetIsa, EncInfo};
use iterators::IteratorExtras;
use result::CtonError;

/// Relax branches and compute the final layout of EBB headers in `func`.
///
/// Fill in the `func.offsets` table so the function is ready for binary emission.
pub fn relax_branches(func: &mut Function, isa: &TargetIsa) {
pub fn relax_branches(func: &mut Function, isa: &TargetIsa) -> Result<CodeOffset, CtonError> {
let encinfo = isa.encoding_info();

// Clear all offsets so we can recognize EBBs that haven't been visited yet.
Expand All @@ -45,13 +46,15 @@ pub fn relax_branches(func: &mut Function, isa: &TargetIsa) {
// Start by inserting fall through instructions.
fallthroughs(func);

let mut offset = 0;

// The relaxation algorithm iterates to convergence.
let mut go_again = true;
while go_again {
go_again = false;
offset = 0;

// Visit all instructions in layout order
let mut offset = 0;
let mut pos = Cursor::new(&mut func.layout);
while let Some(ebb) = pos.next_ebb() {
// Record the offset for `ebb` and make sure we iterate until offsets are stable.
Expand Down Expand Up @@ -90,6 +93,8 @@ pub fn relax_branches(func: &mut Function, isa: &TargetIsa) {
}
}
}

Ok(offset)
}

/// Convert `jump` instructions to `fallthrough` instructions where possible and verify that any
Expand Down
27 changes: 26 additions & 1 deletion lib/cretonne/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,15 @@
//! contexts concurrently. Typically, you would have one context per compilation thread and only a
//! single ISA instance.
use binemit::{CodeOffset, relax_branches};
use dominator_tree::DominatorTree;
use flowgraph::ControlFlowGraph;
use ir::Function;
use loop_analysis::LoopAnalysis;
use isa::TargetIsa;
use legalize_function;
use regalloc;
use result::CtonResult;
use result::{CtonError, CtonResult};
use verifier;
use simple_gvn::do_simple_gvn;
use licm::do_licm;
Expand Down Expand Up @@ -54,6 +55,22 @@ impl Context {
}
}

/// Compile the function.
///
/// Run the function through all the passes necessary to generate code for the target ISA
/// represented by `isa`. This does not include the final step of emitting machine code into a
/// code sink.
///
/// Returns the size of the function's code.
pub fn compile(&mut self, isa: &TargetIsa) -> Result<CodeOffset, CtonError> {
self.flowgraph();
self.verify_if(isa)?;

self.legalize(isa)?;
self.regalloc(isa)?;
self.relax_branches(isa)
}

/// Run the verifier on the function.
///
/// Also check that the dominator tree and control flow graph are consistent with the function.
Expand Down Expand Up @@ -107,4 +124,12 @@ impl Context {
self.regalloc
.run(isa, &mut self.func, &self.cfg, &self.domtree)
}

/// Run the branch relaxation pass and return the final code size.
pub fn relax_branches(&mut self, isa: &TargetIsa) -> Result<CodeOffset, CtonError> {
let code_size = relax_branches(&mut self.func, isa)?;
self.verify_if(isa)?;

Ok(code_size)
}
}
9 changes: 7 additions & 2 deletions src/filetest/binemit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use cretonne::ir::entities::AnyEntity;
use cretonne::isa::TargetIsa;
use cton_reader::TestCommand;
use filetest::subtest::{SubTest, Context, Result};
use utils::match_directive;
use utils::{match_directive, pretty_error};

struct TestBinEmit;

Expand Down Expand Up @@ -117,7 +117,8 @@ impl SubTest for TestBinEmit {
}

// Relax branches and compute EBB offsets based on the encodings.
binemit::relax_branches(&mut func, isa);
let code_size = binemit::relax_branches(&mut func, isa)
.map_err(|e| pretty_error(&func, context.isa, e))?;

// Collect all of the 'bin:' directives on instructions.
let mut bins = HashMap::new();
Expand Down Expand Up @@ -188,6 +189,10 @@ impl SubTest for TestBinEmit {
}
}

if sink.offset != code_size {
return Err(format!("Expected code size {}, got {}", code_size, sink.offset));
}

Ok(())
}
}
98 changes: 98 additions & 0 deletions src/filetest/compile.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
//! Test command for testing the code generator pipeline
//!
//! The `compile` test command runs each function through the full code generator pipeline
use cretonne::binemit;
use cretonne::ir;
use cretonne;
use cton_reader::TestCommand;
use filetest::subtest::{SubTest, Context, Result};
use std::borrow::Cow;
use utils::pretty_error;

struct TestCompile;

pub fn subtest(parsed: &TestCommand) -> Result<Box<SubTest>> {
assert_eq!(parsed.command, "compile");
if !parsed.options.is_empty() {
Err(format!("No options allowed on {}", parsed))
} else {
Ok(Box::new(TestCompile))
}
}

impl SubTest for TestCompile {
fn name(&self) -> Cow<str> {
Cow::from("compile")
}

fn is_mutating(&self) -> bool {
true
}

fn needs_isa(&self) -> bool {
true
}

fn run(&self, func: Cow<ir::Function>, context: &Context) -> Result<()> {
let isa = context.isa.expect("compile needs an ISA");

// Create a compilation context, and drop in the function.
let mut comp_ctx = cretonne::Context::new();
comp_ctx.func = func.into_owned();

let code_size = comp_ctx
.compile(isa)
.map_err(|e| pretty_error(&comp_ctx.func, context.isa, e))?;

dbg!("Generated {} bytes of code:\n{}",
code_size,
comp_ctx.func.display(isa));

// Finally verify that the returned code size matches the emitted bytes.
let mut sink = SizeSink { offset: 0 };
for ebb in comp_ctx.func.layout.ebbs() {
assert_eq!(sink.offset, comp_ctx.func.offsets[ebb]);
for inst in comp_ctx.func.layout.ebb_insts(ebb) {
isa.emit_inst(&comp_ctx.func, inst, &mut sink);
}
}

if sink.offset != code_size {
return Err(format!("Expected code size {}, got {}", code_size, sink.offset));
}

Ok(())
}
}

// Code sink that simply counts bytes.
struct SizeSink {
offset: binemit::CodeOffset,
}

impl binemit::CodeSink for SizeSink {
fn offset(&self) -> binemit::CodeOffset {
self.offset
}

fn put1(&mut self, _: u8) {
self.offset += 1;
}

fn put2(&mut self, _: u16) {
self.offset += 2;
}

fn put4(&mut self, _: u32) {
self.offset += 4;
}

fn put8(&mut self, _: u64) {
self.offset += 8;
}

fn reloc_ebb(&mut self, _reloc: binemit::Reloc, _ebb: ir::Ebb) {}
fn reloc_func(&mut self, _reloc: binemit::Reloc, _fref: ir::FuncRef) {}
fn reloc_jt(&mut self, _reloc: binemit::Reloc, _jt: ir::JumpTable) {}
}
8 changes: 5 additions & 3 deletions src/filetest/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ use filetest::runner::TestRunner;
pub mod subtest;

mod binemit;
mod compile;
mod concurrent;
mod domtree;
mod legalizer;
Expand Down Expand Up @@ -57,15 +58,16 @@ pub fn run(verbose: bool, files: Vec<String>) -> CommandResult {
/// a `.cton` test file.
fn new_subtest(parsed: &TestCommand) -> subtest::Result<Box<subtest::SubTest>> {
match parsed.command {
"binemit" => binemit::subtest(parsed),
"cat" => cat::subtest(parsed),
"print-cfg" => print_cfg::subtest(parsed),
"compile" => compile::subtest(parsed),
"domtree" => domtree::subtest(parsed),
"verifier" => verifier::subtest(parsed),
"legalizer" => legalizer::subtest(parsed),
"licm" => licm::subtest(parsed),
"print-cfg" => print_cfg::subtest(parsed),
"regalloc" => regalloc::subtest(parsed),
"binemit" => binemit::subtest(parsed),
"simple-gvn" => simple_gvn::subtest(parsed),
"verifier" => verifier::subtest(parsed),
_ => Err(format!("unknown test command '{}'", parsed.command)),
}
}

0 comments on commit 6cc729a

Please sign in to comment.