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:
27
cranelift/src/libcretonne/ir/builder.rs
Normal file
27
cranelift/src/libcretonne/ir/builder.rs
Normal 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"));
|
||||||
@@ -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;
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
Reference in New Issue
Block a user