From eef5de1cf03a67e03ccbbe66d054bd90ad4aa7c5 Mon Sep 17 00:00:00 2001 From: Jakob Stoklund Olesen Date: Wed, 19 Oct 2016 18:13:37 -0700 Subject: [PATCH] Generate an InstBuilder trait. All of the instruction format an opcode methods are emitted as an InstBuilder trait instead of adding them to the Bulder struct directly. The methods only make use of the InstBuilderBase methods to create new instructions. This makes it possible to reuse the InstBuilder trait for different ways of inserting instructions. --- lib/cretonne/meta/cretonne/__init__.py | 14 ++++++- lib/cretonne/meta/gen_instr.py | 56 +++++++++++++------------ lib/cretonne/src/cfg.rs | 2 +- lib/cretonne/src/dominator_tree.rs | 2 +- lib/cretonne/src/ir/builder.rs | 57 ++++++++++++++++++++++---- lib/cretonne/src/ir/mod.rs | 2 +- 6 files changed, 96 insertions(+), 37 deletions(-) diff --git a/lib/cretonne/meta/cretonne/__init__.py b/lib/cretonne/meta/cretonne/__init__.py index a38126f98f..d9005d6572 100644 --- a/lib/cretonne/meta/cretonne/__init__.py +++ b/lib/cretonne/meta/cretonne/__init__.py @@ -795,7 +795,19 @@ class Instruction(object): InstructionGroup.append(self) def __str__(self): - return self.name + prefix = ', '.join(o.name for o in self.outs) + if prefix: + prefix = prefix + ' = ' + suffix = ', '.join(o.name for o in self.ins) + return '{}{} {}'.format(prefix, self.name, suffix) + + def blurb(self): + """Get the first line of the doc comment""" + for line in self.__doc__.split('\n'): + line = line.strip() + if line: + return line + return "" def _verify_polymorphic(self): """ diff --git a/lib/cretonne/meta/gen_instr.py b/lib/cretonne/meta/gen_instr.py index 8ae6d47ef7..4e854fe32c 100644 --- a/lib/cretonne/meta/gen_instr.py +++ b/lib/cretonne/meta/gen_instr.py @@ -177,14 +177,7 @@ def gen_opcodes(groups, fmt): for i in g.instructions: instrs.append(i) i.number = len(instrs) - # Build a doc comment. - prefix = ', '.join(o.name for o in i.outs) - if prefix: - prefix = prefix + ' = ' - suffix = ', '.join(o.name for o in i.ins) - fmt.doc_comment( - '`{}{} {}`. ({})' - .format(prefix, i.name, suffix, i.format.name)) + fmt.doc_comment('`{}`. ({})'.format(i, i.format.name)) # Document polymorphism. if i.is_polymorphic: if i.use_typevar_operand: @@ -370,7 +363,7 @@ def gen_format_constructor(iform, fmt): proto = '{}({}) -> Inst'.format(iform.name, ', '.join(args)) fmt.line('#[allow(non_snake_case)]') - with fmt.indented('pub fn {} {{'.format(proto), '}'): + with fmt.indented('fn {} {{'.format(proto), '}'): # Generate the instruction data. with fmt.indented( 'let data = InstructionData::{} {{'.format(iform.name), '};'): @@ -388,11 +381,9 @@ def gen_format_constructor(iform, fmt): # Create result values if necessary. if iform.multiple_results: - fmt.line('let inst = self.insert_inst(data);') - fmt.line('self.dfg.make_inst_results(inst, ctrl_typevar);') - fmt.line('inst') + fmt.line('self.complex_instruction(data, ctrl_typevar)') else: - fmt.line('self.insert_inst(data)') + fmt.line('self.simple_instruction(data)') def gen_member_inits(iform, fmt): @@ -452,8 +443,8 @@ def gen_inst_builder(inst, fmt): method = inst.name if method == 'return': - # Avoid Rust keywords - method = '_' + method + # Avoid Rust keywords by appending '_'. + method += '_' if len(tmpl_types) > 0: tmpl = '<{}>'.format(', '.join(tmpl_types)) @@ -461,8 +452,9 @@ def gen_inst_builder(inst, fmt): tmpl = '' proto = '{}{}({}) -> {}'.format(method, tmpl, ', '.join(args), rtype) + fmt.doc_comment('`{}`\n\n{}'.format(inst, inst.blurb())) fmt.line('#[allow(non_snake_case)]') - with fmt.indented('pub fn {} {{'.format(proto), '}'): + with fmt.indented('fn {} {{'.format(proto), '}'): # Convert all of the `Into<>` arguments. for arg in into_args: fmt.line('let {} = {}.into();'.format(arg, arg)) @@ -478,7 +470,7 @@ def gen_inst_builder(inst, fmt): elif inst.is_polymorphic: # Infer the controlling type variable from the input operands. fmt.line( - 'let ctrl_typevar = self.dfg.value_type({});' + 'let ctrl_typevar = self.data_flow_graph().value_type({});' .format(inst.ins[inst.format.typevar_operand].name)) args.append('ctrl_typevar') else: @@ -494,9 +486,11 @@ def gen_inst_builder(inst, fmt): if len(inst.value_results) == 0: fmt.line('inst') elif len(inst.value_results) == 1: - fmt.line('self.dfg.first_result(inst)') + fmt.line('self.data_flow_graph().first_result(inst)') else: - fmt.line('let mut results = self.dfg.inst_results(inst);') + fmt.line( + 'let mut results = ' + + 'self.data_flow_graph().inst_results(inst);') fmt.line('({})'.format(', '.join( len(inst.value_results) * ['results.next().unwrap()']))) @@ -505,16 +499,26 @@ def gen_builder(insts, fmt): """ Generate a Builder trait with methods for all instructions. """ - fmt.doc_comment( - 'Methods for inserting instructions by instruction format.') - with fmt.indented("impl<'a> Builder<'a> {", '}'): - for f in cretonne.InstructionFormat.all_formats: - gen_format_constructor(f, fmt) + fmt.doc_comment(""" + Convenience methods for building instructions. - fmt.doc_comment('Methods for inserting instructions by opcode.') - with fmt.indented("impl<'a> Builder<'a> {", '}'): + The `InstrBuilder` trait has one method per instruction opcode for + conveniently constructing the instruction with minimum arguments. + Polymorphic instructions infer their result types from the input + arguments when possible. In some cases, an explicit `result_type` + or `ctrl_typevar` argument is required. + + The opcode methods return the new instruction's result values, or + the `Inst` itself for instructions that don't have any results. + + There is also a method per instruction format. These methods all + return an `Inst`. + """) + with fmt.indented("pub trait InstBuilder: InstBuilderBase {", '}'): for inst in insts: gen_inst_builder(inst, fmt) + for f in cretonne.InstructionFormat.all_formats: + gen_format_constructor(f, fmt) def generate(isas, out_dir): diff --git a/lib/cretonne/src/cfg.rs b/lib/cretonne/src/cfg.rs index aab576ae82..59ced57d82 100644 --- a/lib/cretonne/src/cfg.rs +++ b/lib/cretonne/src/cfg.rs @@ -139,7 +139,7 @@ impl ControlFlowGraph { #[cfg(test)] mod tests { use super::*; - use ir::{Function, Builder, Cursor, VariableArgs, types}; + use ir::{Function, Builder, InstBuilder, Cursor, VariableArgs, types}; #[test] fn empty() { diff --git a/lib/cretonne/src/dominator_tree.rs b/lib/cretonne/src/dominator_tree.rs index 0e34a2c021..85d224060d 100644 --- a/lib/cretonne/src/dominator_tree.rs +++ b/lib/cretonne/src/dominator_tree.rs @@ -116,7 +116,7 @@ impl DominatorTree { #[cfg(test)] mod test { use super::*; - use ir::{Function, Builder, Cursor, VariableArgs, types}; + use ir::{Function, Builder, InstBuilder, Cursor, VariableArgs, types}; use ir::entities::NO_INST; use cfg::ControlFlowGraph; diff --git a/lib/cretonne/src/ir/builder.rs b/lib/cretonne/src/ir/builder.rs index f8510bb7f4..e8646d0dd1 100644 --- a/lib/cretonne/src/ir/builder.rs +++ b/lib/cretonne/src/ir/builder.rs @@ -9,6 +9,42 @@ use ir::{Opcode, Type, Inst, Value, Ebb, JumpTable, VariableArgs, SigRef, FuncRe use ir::immediates::{Imm64, Uimm8, Ieee32, Ieee64, ImmVector}; use ir::condcodes::{IntCC, FloatCC}; +/// Base trait for instruction builders. +/// +/// The `InstBuilderBase` trait provides the basic functionality required by the methods of the +/// generated `InstBuilder` trait. These methods should not normally be used directly. Use the +/// methods in the `InstBuilder trait instead. +/// +/// Any data type that implements `InstBuilderBase` also gets all the methods of the `InstBuilder` +/// trait. +pub trait InstBuilderBase { + /// Get an immutable reference to the data flow graph that will hold the constructed + /// instructions. + fn data_flow_graph(&self) -> &DataFlowGraph; + + /// Insert a simple instruction and return a reference to it. + /// + /// A 'simple' instruction has at most one result, and the `data.ty` field must contain the + /// result type or `VOID` for an instruction with no result values. + fn simple_instruction(&mut self, data: InstructionData) -> Inst; + + /// Insert a simple instruction and return a reference to it. + /// + /// A 'complex' instruction may produce multiple results, and the result types may depend on a + /// controlling type variable. For non-polymorphic instructions with multiple results, pass + /// `VOID` for the `ctrl_typevar` argument. + fn complex_instruction(&mut self, data: InstructionData, ctrl_typevar: Type) -> Inst; +} + +// Include trait code generated by `meta/gen_instr.py`. +// +// This file defines the `InstBuilder` trait as an extension of `InstBuilderBase` with methods per +// instruction format and per opcode. +include!(concat!(env!("OUT_DIR"), "/builder.rs")); + +/// Any type implementing `InstBuilderBase` gets all the `InstBuilder` methods for free. +impl InstBuilder for T {} + /// Instruction builder. /// /// A `Builder` holds mutable references to a data flow graph and a layout cursor. It provides @@ -40,16 +76,23 @@ impl<'a> Builder<'a> { pub fn insert_ebb(&mut self, ebb: Ebb) { self.pos.insert_ebb(ebb); } +} - // Create and insert an instruction. - // This method is used by the generated format-specific methods. - fn insert_inst(&mut self, data: InstructionData) -> Inst { +impl<'a> InstBuilderBase for Builder<'a> { + fn data_flow_graph(&self) -> &DataFlowGraph { + self.dfg + } + + fn simple_instruction(&mut self, data: InstructionData) -> Inst { let inst = self.dfg.make_inst(data); self.pos.insert_inst(inst); inst } -} -// Include code generated by `meta/gen_instr.py`. This file includes `Builder` methods per -// instruction format and per opcode for inserting instructions. -include!(concat!(env!("OUT_DIR"), "/builder.rs")); + fn complex_instruction(&mut self, data: InstructionData, ctrl_typevar: Type) -> Inst { + let inst = self.dfg.make_inst(data); + self.dfg.make_inst_results(inst, ctrl_typevar); + self.pos.insert_inst(inst); + inst + } +} diff --git a/lib/cretonne/src/ir/mod.rs b/lib/cretonne/src/ir/mod.rs index 5056d9b29e..0812496032 100644 --- a/lib/cretonne/src/ir/mod.rs +++ b/lib/cretonne/src/ir/mod.rs @@ -24,4 +24,4 @@ pub use ir::jumptable::JumpTableData; pub use ir::dfg::{DataFlowGraph, ValueDef}; pub use ir::layout::{Layout, Cursor}; pub use ir::function::Function; -pub use ir::builder::Builder; +pub use ir::builder::{Builder, InstBuilder};