Move library crates under 'lib/'.
Give these crates each a more standard directory layout with sources in a 'src' sub-sirectory and Cargo.toml in the top lib/foo directory. Add license and description fields to each. The build script for the cretonne crate now lives in 'lib/cretonne/build.rs' separating it from the normal library sources under 'lib/cretonne/src'.
This commit is contained in:
243
lib/cretonne/src/write.rs
Normal file
243
lib/cretonne/src/write.rs
Normal file
@@ -0,0 +1,243 @@
|
||||
//! Converting Cretonne IL to text.
|
||||
//!
|
||||
//! The `write` module provides the `write_function` function which converts an IL `Function` to an
|
||||
//! equivalent textual representation. This textual representation can be read back by the
|
||||
//! `cretonne-reader` crate.
|
||||
|
||||
use ir::{Function, Ebb, Inst, Value, Type};
|
||||
use isa::TargetIsa;
|
||||
use std::fmt::{Result, Error, Write};
|
||||
use std::result;
|
||||
|
||||
/// Write `func` to `w` as equivalent text.
|
||||
/// Use `isa` to emit ISA-dependent annotations.
|
||||
pub fn write_function(w: &mut Write, func: &Function, isa: Option<&TargetIsa>) -> Result {
|
||||
try!(write_spec(w, func));
|
||||
try!(writeln!(w, " {{"));
|
||||
let mut any = try!(write_preamble(w, func));
|
||||
for ebb in &func.layout {
|
||||
if any {
|
||||
try!(writeln!(w, ""));
|
||||
}
|
||||
try!(write_ebb(w, func, isa, ebb));
|
||||
any = true;
|
||||
}
|
||||
writeln!(w, "}}")
|
||||
}
|
||||
|
||||
// ====--------------------------------------------------------------------------------------====//
|
||||
//
|
||||
// Function spec.
|
||||
//
|
||||
// ====--------------------------------------------------------------------------------------====//
|
||||
|
||||
fn write_spec(w: &mut Write, func: &Function) -> Result {
|
||||
write!(w, "function {}{}", func.name, func.own_signature())
|
||||
}
|
||||
|
||||
fn write_preamble(w: &mut Write, func: &Function) -> result::Result<bool, Error> {
|
||||
let mut any = false;
|
||||
|
||||
for ss in func.stack_slots.keys() {
|
||||
any = true;
|
||||
try!(writeln!(w, " {} = {}", ss, func.stack_slots[ss]));
|
||||
}
|
||||
|
||||
for jt in func.jump_tables.keys() {
|
||||
any = true;
|
||||
try!(writeln!(w, " {} = {}", jt, func.jump_tables[jt]));
|
||||
}
|
||||
|
||||
Ok(any)
|
||||
}
|
||||
|
||||
// ====--------------------------------------------------------------------------------------====//
|
||||
//
|
||||
// Basic blocks
|
||||
//
|
||||
// ====--------------------------------------------------------------------------------------====//
|
||||
|
||||
pub fn write_arg(w: &mut Write, func: &Function, arg: Value) -> Result {
|
||||
write!(w, "{}: {}", arg, func.dfg.value_type(arg))
|
||||
}
|
||||
|
||||
pub fn write_ebb_header(w: &mut Write, func: &Function, ebb: Ebb) -> Result {
|
||||
// Write out the basic block header, outdented:
|
||||
//
|
||||
// ebb1:
|
||||
// ebb1(vx1: i32):
|
||||
// ebb10(vx4: f64, vx5: b1):
|
||||
//
|
||||
|
||||
// If we're writing encoding annotations, shift by 20.
|
||||
if !func.encodings.is_empty() {
|
||||
try!(write!(w, " "));
|
||||
}
|
||||
|
||||
let mut args = func.dfg.ebb_args(ebb);
|
||||
match args.next() {
|
||||
None => return writeln!(w, "{}:", ebb),
|
||||
Some(arg) => {
|
||||
try!(write!(w, "{}(", ebb));
|
||||
try!(write_arg(w, func, arg));
|
||||
}
|
||||
}
|
||||
// Remaining args.
|
||||
for arg in args {
|
||||
try!(write!(w, ", "));
|
||||
try!(write_arg(w, func, arg));
|
||||
}
|
||||
writeln!(w, "):")
|
||||
}
|
||||
|
||||
pub fn write_ebb(w: &mut Write, func: &Function, isa: Option<&TargetIsa>, ebb: Ebb) -> Result {
|
||||
try!(write_ebb_header(w, func, ebb));
|
||||
for inst in func.layout.ebb_insts(ebb) {
|
||||
try!(write_instruction(w, func, isa, inst));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
// ====--------------------------------------------------------------------------------------====//
|
||||
//
|
||||
// Instructions
|
||||
//
|
||||
// ====--------------------------------------------------------------------------------------====//
|
||||
|
||||
// Should `inst` be printed with a type suffix?
|
||||
//
|
||||
// Polymorphic instructions may need a suffix indicating the value of the controlling type variable
|
||||
// if it can't be trivially inferred.
|
||||
//
|
||||
fn type_suffix(func: &Function, inst: Inst) -> Option<Type> {
|
||||
let constraints = func.dfg[inst].opcode().constraints();
|
||||
|
||||
if !constraints.is_polymorphic() {
|
||||
return None;
|
||||
}
|
||||
|
||||
// If the controlling type variable can be inferred from the type of the designated value input
|
||||
// operand, we don't need the type suffix.
|
||||
// TODO: Should we include the suffix when the input value is defined in another block? The
|
||||
// parser needs to know the type of the value, so it must be defined in a block that lexically
|
||||
// comes before this one.
|
||||
if constraints.use_typevar_operand() {
|
||||
return None;
|
||||
}
|
||||
|
||||
// This polymorphic instruction doesn't support basic type inference.
|
||||
// The controlling type variable is required to be the type of the first result.
|
||||
let rtype = func.dfg.value_type(func.dfg.first_result(inst));
|
||||
assert!(!rtype.is_void(),
|
||||
"Polymorphic instruction must produce a result");
|
||||
Some(rtype)
|
||||
}
|
||||
|
||||
fn write_instruction(w: &mut Write,
|
||||
func: &Function,
|
||||
isa: Option<&TargetIsa>,
|
||||
inst: Inst)
|
||||
-> Result {
|
||||
// Write out encoding info.
|
||||
if let Some(enc) = func.encodings.get(inst).cloned() {
|
||||
let mut s = String::with_capacity(16);
|
||||
if let Some(isa) = isa {
|
||||
try!(write!(s, "[{}]", isa.display_enc(enc)));
|
||||
} else {
|
||||
try!(write!(s, "[{}]", enc));
|
||||
}
|
||||
// Align instruction following ISA annotation to col 24.
|
||||
try!(write!(w, "{:23} ", s));
|
||||
} else {
|
||||
// No annotations, simply indent by 4.
|
||||
try!(write!(w, " "));
|
||||
}
|
||||
|
||||
// Write out the result values, if any.
|
||||
let mut has_results = false;
|
||||
for r in func.dfg.inst_results(inst) {
|
||||
if !has_results {
|
||||
has_results = true;
|
||||
try!(write!(w, "{}", r));
|
||||
} else {
|
||||
try!(write!(w, ", {}", r));
|
||||
}
|
||||
}
|
||||
if has_results {
|
||||
try!(write!(w, " = "));
|
||||
}
|
||||
|
||||
// Then the opcode, possibly with a '.type' suffix.
|
||||
let opcode = func.dfg[inst].opcode();
|
||||
|
||||
match type_suffix(func, inst) {
|
||||
Some(suf) => try!(write!(w, "{}.{}", opcode, suf)),
|
||||
None => try!(write!(w, "{}", opcode)),
|
||||
}
|
||||
|
||||
// Then the operands, depending on format.
|
||||
use ir::instructions::InstructionData::*;
|
||||
match func.dfg[inst] {
|
||||
Nullary { .. } => writeln!(w, ""),
|
||||
Unary { arg, .. } => writeln!(w, " {}", arg),
|
||||
UnaryImm { imm, .. } => writeln!(w, " {}", imm),
|
||||
UnaryIeee32 { imm, .. } => writeln!(w, " {}", imm),
|
||||
UnaryIeee64 { imm, .. } => writeln!(w, " {}", imm),
|
||||
UnaryImmVector { ref data, .. } => writeln!(w, " {}", data),
|
||||
UnarySplit { arg, .. } => writeln!(w, " {}", arg),
|
||||
Binary { args, .. } => writeln!(w, " {}, {}", args[0], args[1]),
|
||||
BinaryImm { arg, imm, .. } => writeln!(w, " {}, {}", arg, imm),
|
||||
BinaryImmRev { imm, arg, .. } => writeln!(w, " {}, {}", imm, arg),
|
||||
BinaryOverflow { args, .. } => writeln!(w, " {}, {}", args[0], args[1]),
|
||||
Ternary { args, .. } => writeln!(w, " {}, {}, {}", args[0], args[1], args[2]),
|
||||
TernaryOverflow { ref data, .. } => writeln!(w, " {}", data),
|
||||
InsertLane { lane, args, .. } => writeln!(w, " {}, {}, {}", args[0], lane, args[1]),
|
||||
ExtractLane { lane, arg, .. } => writeln!(w, " {}, {}", arg, lane),
|
||||
IntCompare { cond, args, .. } => writeln!(w, " {}, {}, {}", cond, args[0], args[1]),
|
||||
FloatCompare { cond, args, .. } => writeln!(w, " {}, {}, {}", cond, args[0], args[1]),
|
||||
Jump { ref data, .. } => writeln!(w, " {}", data),
|
||||
Branch { ref data, .. } => writeln!(w, " {}", data),
|
||||
BranchTable { arg, table, .. } => writeln!(w, " {}, {}", arg, table),
|
||||
Call { ref data, .. } => writeln!(w, " {}", data),
|
||||
Return { ref data, .. } => {
|
||||
if data.varargs.is_empty() {
|
||||
writeln!(w, "")
|
||||
} else {
|
||||
writeln!(w, " {}", data.varargs)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use ir::{Function, FunctionName, StackSlotData};
|
||||
use ir::types;
|
||||
|
||||
#[test]
|
||||
fn basic() {
|
||||
let mut f = Function::new();
|
||||
assert_eq!(f.to_string(), "function \"\"() {\n}\n");
|
||||
|
||||
f.name = FunctionName::new("foo".to_string());
|
||||
assert_eq!(f.to_string(), "function foo() {\n}\n");
|
||||
|
||||
f.stack_slots.push(StackSlotData::new(4));
|
||||
assert_eq!(f.to_string(),
|
||||
"function foo() {\n ss0 = stack_slot 4\n}\n");
|
||||
|
||||
let ebb = f.dfg.make_ebb();
|
||||
f.layout.append_ebb(ebb);
|
||||
assert_eq!(f.to_string(),
|
||||
"function foo() {\n ss0 = stack_slot 4\n\nebb0:\n}\n");
|
||||
|
||||
f.dfg.append_ebb_arg(ebb, types::I8);
|
||||
assert_eq!(f.to_string(),
|
||||
"function foo() {\n ss0 = stack_slot 4\n\nebb0(vx0: i8):\n}\n");
|
||||
|
||||
f.dfg.append_ebb_arg(ebb, types::F32.by(4).unwrap());
|
||||
assert_eq!(f.to_string(),
|
||||
"function foo() {\n ss0 = stack_slot 4\n\nebb0(vx0: i8, vx1: f32x4):\n}\n");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user