Generate a Builder data type. WIP.

The Builder provides a convenient interface for inserting instructions
into an extended basic block.

The bulk of the builder methods are generated automatically from the
meta language instruction descriptions.

Still TODO: Keep track of an insertion position.
This commit is contained in:
Jakob Stoklund Olesen
2016-10-12 10:51:05 -07:00
parent 93f79d7a48
commit 6ce6f25626
4 changed files with 213 additions and 2 deletions

View File

@@ -0,0 +1,27 @@
//! Cretonne instruction builder.
//!
//! A `Builder` provides a convenient interface for inserting instructions into a Cretonne
//! function. Many of its methods are generated from the meta language instruction definitions.
use ir::{types, instructions};
use ir::{InstructionData, DataFlowGraph};
use ir::{Opcode, Type, Inst, Value, Ebb, JumpTable, VariableArgs, FuncRef};
use ir::immediates::{Imm64, Uimm8, Ieee32, Ieee64, ImmVector};
use ir::condcodes::{IntCC, FloatCC};
pub struct Builder<'a> {
dfg: &'a mut DataFlowGraph,
}
impl<'a> Builder<'a> {
// Create and insert an instruction.
// This method is used by the generated format-specific methods.
fn insert_inst(&mut self, data: InstructionData) -> Inst {
let inst = self.dfg.make_inst(data);
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"));

View File

@@ -12,14 +12,16 @@ pub mod layout;
pub mod function; pub mod function;
mod funcname; mod funcname;
mod extfunc; mod extfunc;
mod builder;
pub use ir::funcname::FunctionName; pub use ir::funcname::FunctionName;
pub use ir::extfunc::{Signature, ArgumentType, ArgumentExtension}; pub use ir::extfunc::{Signature, ArgumentType, ArgumentExtension};
pub use ir::types::Type; pub use ir::types::Type;
pub use ir::entities::{Ebb, Inst, Value, StackSlot, JumpTable, FuncRef, SigRef}; pub use ir::entities::{Ebb, Inst, Value, StackSlot, JumpTable, FuncRef, SigRef};
pub use ir::instructions::{Opcode, InstructionData}; pub use ir::instructions::{Opcode, InstructionData, VariableArgs};
pub use ir::stackslot::StackSlotData; pub use ir::stackslot::StackSlotData;
pub use ir::jumptable::JumpTableData; pub use ir::jumptable::JumpTableData;
pub use ir::dfg::{DataFlowGraph, ValueDef}; pub use ir::dfg::{DataFlowGraph, ValueDef};
pub use ir::layout::Layout; pub use ir::layout::Layout;
pub use ir::function::Function; pub use ir::function::Function;
pub use ir::builder::Builder;

View File

@@ -1057,7 +1057,7 @@ fdemote = Instruction(
ins=x, outs=a) ins=x, outs=a)
x = Operand('x', Float) x = Operand('x', Float)
a = Operand('a', Int) a = Operand('a', IntTo)
fcvt_to_uint = Instruction( fcvt_to_uint = Instruction(
'fcvt_to_uint', r""" 'fcvt_to_uint', r"""
@@ -1083,6 +1083,9 @@ fcvt_to_sint = Instruction(
""", """,
ins=x, outs=a) ins=x, outs=a)
x = Operand('x', Int)
a = Operand('a', FloatTo)
fcvt_from_uint = Instruction( fcvt_from_uint = Instruction(
'fcvt_from_uint', r""" 'fcvt_from_uint', r"""
Convert unsigned integer to floating point. Convert unsigned integer to floating point.

View File

@@ -343,6 +343,180 @@ def gen_type_constraints(fmt, instrs):
fmt.line('OperandConstraint::{},'.format(c)) fmt.line('OperandConstraint::{},'.format(c))
def gen_format_constructor(iform, fmt):
"""
Emit a method for creating and inserting inserting an `iform` instruction,
where `iform` is an instruction format.
Instruction formats that can produce multiple results take a `ctrl_typevar`
argument for deducing the result types. Others take a `result_type`
argument.
"""
# Construct method arguments.
args = ['&mut self', 'opcode: Opcode']
if iform.multiple_results:
args.append('ctrl_typevar: Type')
# `dfg::make_inst_results` will compute the result type.
result_type = 'types::VOID'
else:
args.append('result_type: Type')
result_type = 'result_type'
# Normal operand arguments.
for idx, kind in enumerate(iform.kinds):
args.append('op{}: {}'.format(idx, kind.rust_type))
proto = '{}({}) -> Inst'.format(iform.name, ', '.join(args))
fmt.line('#[allow(non_snake_case)]')
with fmt.indented('pub fn {} {{'.format(proto), '}'):
# Generate the instruction data.
with fmt.indented(
'let data = InstructionData::{} {{'.format(iform.name), '};'):
fmt.line('opcode: opcode,')
fmt.line('ty: {},'.format(result_type))
if iform.multiple_results:
fmt.line('second_result: Value::default(),')
if iform.boxed_storage:
with fmt.indented(
'data: Box::new(instructions::{}Data {{'
.format(iform.name), '}),'):
gen_member_inits(iform, fmt)
else:
gen_member_inits(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')
else:
fmt.line('self.insert_inst(data)')
def gen_member_inits(iform, fmt):
"""
Emit member initializers for an `iform` instruction.
"""
# Values first.
if len(iform.value_operands) == 1:
fmt.line('arg: op{},'.format(iform.value_operands[0]))
elif len(iform.value_operands) > 1:
fmt.line('args: [{}],'.format(
', '.join('op{}'.format(i) for i in iform.value_operands)))
# Immediates and entity references.
for idx, member in enumerate(iform.members):
if member:
fmt.line('{}: op{},'.format(member, idx))
def gen_inst_builder(inst, fmt):
"""
Emit a method for generating the instruction `inst`.
The method will create and insert an instruction, then return the result
values, or the instruction reference itself for instructions that don't
have results.
"""
# Construct method arguments.
args = ['&mut self']
# The controlling type variable will be inferred from the input values if
# possible. Otherwise, it is the first method argument.
if inst.is_polymorphic and not inst.use_typevar_operand:
args.append('{}: Type'.format(inst.ctrl_typevar.name))
tmpl_types = list()
into_args = list()
for op in inst.ins:
if isinstance(op.kind, cretonne.ImmediateKind):
t = 'T{}{}'.format(1 + len(tmpl_types), op.kind.name)
tmpl_types.append('{}: Into<{}>'.format(t, op.kind.rust_type))
into_args.append(op.name)
else:
t = op.kind.rust_type
args.append('{}: {}'.format(op.name, t))
# Return the inst reference for result-less instructions.
if len(inst.value_results) == 0:
rtype = 'Inst'
elif len(inst.value_results) == 1:
rtype = 'Value'
else:
rvals = ', '.join(len(inst.value_results) * ['Value'])
rtype = '({})'.format(rvals)
method = inst.name
if method == 'return':
# Avoid Rust keywords
method = '_' + method
if len(tmpl_types) > 0:
tmpl = '<{}>'.format(', '.join(tmpl_types))
else:
tmpl = ''
proto = '{}{}({}) -> {}'.format(method, tmpl, ', '.join(args), rtype)
fmt.line('#[allow(non_snake_case)]')
with fmt.indented('pub fn {} {{'.format(proto), '}'):
# Convert all of the `Into<>` arguments.
for arg in into_args:
fmt.line('let {} = {}.into();'.format(arg, arg))
# Arguments for instruction constructor.
args = ['Opcode::' + inst.camel_name]
if inst.is_polymorphic and not inst.use_typevar_operand:
# This was an explicit method argument.
args.append(inst.ctrl_typevar.name)
elif len(inst.value_results) == 0:
args.append('types::VOID')
elif inst.is_polymorphic:
# Infer the controlling type variable from the input operands.
fmt.line(
'let ctrl_typevar = self.dfg.value_type({});'
.format(inst.ins[inst.format.typevar_operand].name))
args.append('ctrl_typevar')
else:
# This non-polymorphic instruction has a fixed result type.
args.append(
'types::' +
inst.outs[inst.value_results[0]].typ.name.upper())
args.extend(op.name for op in inst.ins)
args = ', '.join(args)
fmt.line('let inst = self.{}({});'.format(inst.format.name, args))
if len(inst.value_results) == 0:
fmt.line('inst')
elif len(inst.value_results) == 1:
fmt.line('self.dfg.first_result(inst)')
else:
fmt.line('let mut results = self.dfg.inst_results(inst);')
fmt.line('({})'.format(', '.join(
len(inst.value_results) * ['results.next().unwrap()'])))
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('Methods for inserting instructions by opcode.')
with fmt.indented("impl<'a> Builder<'a> {", '}'):
for inst in insts:
gen_inst_builder(inst, fmt)
def generate(isas, out_dir): def generate(isas, out_dir):
groups = collect_instr_groups(isas) groups = collect_instr_groups(isas)
@@ -353,3 +527,8 @@ def generate(isas, out_dir):
instrs = gen_opcodes(groups, fmt) instrs = gen_opcodes(groups, fmt)
gen_type_constraints(fmt, instrs) gen_type_constraints(fmt, instrs)
fmt.update_file('opcodes.rs', out_dir) fmt.update_file('opcodes.rs', out_dir)
# builder.rs
fmt = srcgen.Formatter()
gen_builder(instrs, fmt)
fmt.update_file('builder.rs', out_dir)