diff --git a/cranelift/codegen/meta-python/build.py b/cranelift/codegen/meta-python/build.py index 37c23554b8..28f510c1e9 100644 --- a/cranelift/codegen/meta-python/build.py +++ b/cranelift/codegen/meta-python/build.py @@ -5,13 +5,32 @@ from __future__ import absolute_import import argparse import isa -import gen_instr import gen_settings import gen_build_deps import gen_encoding import gen_legalizer import gen_binemit +try: + from typing import List, Set # noqa + from cdsl.isa import TargetISA # noqa + from cdsl.instructions import InstructionGroup # noqa +except ImportError: + pass + + +def number_all_instructions(isas): + # type: (List[TargetISA]) -> None + seen = set() # type: Set[InstructionGroup] + num_inst = 1 + for target_isa in isas: + for g in target_isa.instruction_groups: + if g not in seen: + for i in g.instructions: + i.number = num_inst + num_inst += 1 + seen.add(g) + def main(): # type: () -> None @@ -23,8 +42,8 @@ def main(): out_dir = args.out_dir isas = isa.all_isas() + number_all_instructions(isas) - gen_instr.generate(isas, out_dir) gen_settings.generate(isas, out_dir) gen_encoding.generate(isas, out_dir) gen_legalizer.generate(isas, out_dir) diff --git a/cranelift/codegen/meta-python/gen_instr.py b/cranelift/codegen/meta-python/gen_instr.py deleted file mode 100644 index 94de93b000..0000000000 --- a/cranelift/codegen/meta-python/gen_instr.py +++ /dev/null @@ -1,812 +0,0 @@ -""" -Generate sources with instruction info. -""" -from __future__ import absolute_import -import srcgen -import constant_hash -from unique_table import UniqueTable, UniqueSeqTable -from cdsl import camel_case -from cdsl.operands import ImmediateKind -from cdsl.formats import InstructionFormat -from cdsl.instructions import Instruction - -# The typing module is only required by mypy, and we don't use these imports -# outside type comments. -try: - from typing import List, Sequence, Set, TYPE_CHECKING # noqa - if TYPE_CHECKING: - from cdsl.isa import TargetISA # noqa - from cdsl.instructions import InstructionGroup # noqa - from cdsl.operands import Operand # noqa - from cdsl.typevar import TypeVar # noqa - -except ImportError: - pass - - -def gen_formats(fmt): - # type: (srcgen.Formatter) -> None - """Generate an instruction format enumeration""" - - fmt.doc_comment(''' - An instruction format - - Every opcode has a corresponding instruction format - which is represented by both the `InstructionFormat` - and the `InstructionData` enums. - ''') - fmt.line('#[derive(Copy, Clone, PartialEq, Eq, Debug)]') - with fmt.indented('pub enum InstructionFormat {', '}'): - for f in InstructionFormat.all_formats: - fmt.doc_comment(str(f)) - fmt.line(f.name + ',') - fmt.line() - - # Emit a From which also serves to verify that - # InstructionFormat and InstructionData are in sync. - with fmt.indented( - "impl<'a> From<&'a InstructionData> for InstructionFormat {", '}'): - with fmt.indented( - "fn from(inst: &'a InstructionData) -> Self {", - '}'): - m = srcgen.Match('*inst') - for f in InstructionFormat.all_formats: - m.arm('InstructionData::' + f.name, ['..'], - 'InstructionFormat::' + f.name) - fmt.match(m) - fmt.line() - - -def gen_arguments_method(fmt, is_mut): - # type: (srcgen.Formatter, bool) -> None - method = 'arguments' - mut = '' - rslice = 'ref_slice' - as_slice = 'as_slice' - if is_mut: - method += '_mut' - mut = 'mut ' - rslice += '_mut' - as_slice = 'as_mut_slice' - - with fmt.indented( - 'pub fn {f}<\'a>(&\'a {m}self, ' - 'pool: &\'a {m}ir::ValueListPool) -> ' - '&{m}[Value] {{' - .format(f=method, m=mut), '}'): - m = srcgen.Match('*self') - for f in InstructionFormat.all_formats: - n = 'InstructionData::' + f.name - - # Formats with a value list put all of their arguments in the - # list. We don't split them up, just return it all as variable - # arguments. (I expect the distinction to go away). - if f.has_value_list: - m.arm(n, ['ref {}args'.format(mut), '..'], - 'args.{}(pool)'.format(as_slice)) - continue - - # Fixed args. - fields = [] - if f.num_value_operands == 0: - arg = '&{}[]'.format(mut) - elif f.num_value_operands == 1: - fields.append('ref {}arg'.format(mut)) - arg = '{}(arg)'.format(rslice) - else: - args = 'args_arity{}'.format(f.num_value_operands) - fields.append('args: ref {}{}'.format(mut, args)) - arg = args - fields.append('..') - m.arm(n, fields, arg) - fmt.match(m) - - -def gen_instruction_data(fmt): - # type: (srcgen.Formatter) -> None - """ - Generate the InstructionData enum. - - Every variant must contain an `opcode` field. The size of `InstructionData` - should be kept at 16 bytes on 64-bit architectures. If more space is needed - to represent an instruction, use a `Box` to store the additional - information out of line. - """ - - fmt.line('#[derive(Clone, Debug)]') - fmt.line('#[allow(missing_docs)]') - with fmt.indented('pub enum InstructionData {', '}'): - for f in InstructionFormat.all_formats: - with fmt.indented('{} {{'.format(f.name), '},'): - fmt.line('opcode: Opcode,') - if f.typevar_operand is None: - pass - elif f.has_value_list: - fmt.line('args: ValueList,') - elif f.num_value_operands == 1: - fmt.line('arg: Value,') - else: - fmt.line('args: [Value; {}],'.format(f.num_value_operands)) - for field in f.imm_fields: - fmt.line( - '{}: {},' - .format(field.member, field.kind.rust_type)) - - -def gen_instruction_data_impl(fmt): - # type: (srcgen.Formatter) -> None - """ - Generate the boring parts of the InstructionData implementation. - - These methods in `impl InstructionData` can be generated automatically from - the instruction formats: - - - `pub fn opcode(&self) -> Opcode` - - `pub fn arguments(&self, &pool) -> &[Value]` - - `pub fn arguments_mut(&mut self, &pool) -> &mut [Value]` - - `pub fn take_value_list(&mut self) -> Option` - - `pub fn put_value_list(&mut self, args: ir::ValueList>` - - `pub fn eq(&self, &other: Self, &pool) -> bool` - - `pub fn hash(&self, state: &mut H, &pool)` - """ - - # The `opcode` method simply reads the `opcode` members. This is really a - # workaround for Rust's enum types missing shared members. - with fmt.indented('impl InstructionData {', '}'): - fmt.doc_comment('Get the opcode of this instruction.') - with fmt.indented('pub fn opcode(&self) -> Opcode {', '}'): - m = srcgen.Match('*self') - for f in InstructionFormat.all_formats: - m.arm('InstructionData::' + f.name, ['opcode', '..'], - 'opcode') - fmt.match(m) - fmt.line() - - fmt.doc_comment('Get the controlling type variable operand.') - with fmt.indented( - 'pub fn typevar_operand(&self, pool: &ir::ValueListPool) -> ' - 'Option {', '}'): - m = srcgen.Match('*self') - for f in InstructionFormat.all_formats: - n = 'InstructionData::' + f.name - if f.typevar_operand is None: - m.arm(n, ['..'], 'None') - elif f.has_value_list: - # We keep all arguments in a value list. - i = f.typevar_operand - m.arm(n, ['ref args', '..'], - 'args.get({}, pool)'.format(i)) - elif f.num_value_operands == 1: - # We have a single value operand called 'arg'. - m.arm(n, ['arg', '..'], 'Some(arg)') - else: - # We have multiple value operands and an array `args`. - # Which `args` index to use? - args = 'args_arity{}'.format(f.num_value_operands) - m.arm(n, ['args: ref {}'.format(args), '..'], - 'Some({}[{}])'.format(args, f.typevar_operand)) - fmt.match(m) - fmt.line() - - fmt.doc_comment( - """ - Get the value arguments to this instruction. - """) - gen_arguments_method(fmt, False) - fmt.line() - - fmt.doc_comment( - """ - Get mutable references to the value arguments to this - instruction. - """) - gen_arguments_method(fmt, True) - fmt.line() - - fmt.doc_comment( - """ - Take out the value list with all the value arguments and return - it. - - This leaves the value list in the instruction empty. Use - `put_value_list` to put the value list back. - """) - with fmt.indented( - 'pub fn take_value_list(&mut self) -> Option {', - '}'): - m = srcgen.Match('*self') - for f in InstructionFormat.all_formats: - n = 'InstructionData::' + f.name - if f.has_value_list: - m.arm(n, ['ref mut args', '..'], 'Some(args.take())') - m.arm('_', [], 'None') - fmt.match(m) - fmt.line() - - fmt.doc_comment( - """ - Put back a value list. - - After removing a value list with `take_value_list()`, use this - method to put it back. It is required that this instruction has - a format that accepts a value list, and that the existing value - list is empty. This avoids leaking list pool memory. - """) - with fmt.indented( - 'pub fn put_value_list(&mut self, vlist: ir::ValueList) {', - '}'): - with fmt.indented('let args = match *self {', '};'): - for f in InstructionFormat.all_formats: - n = 'InstructionData::' + f.name - if f.has_value_list: - fmt.line(n + ' { ref mut args, .. } => args,') - fmt.line('_ => panic!("No value list: {:?}", self),') - fmt.line( - 'debug_assert!(args.is_empty(), "Value list already in use");') - fmt.line('*args = vlist;') - fmt.line() - - fmt.doc_comment( - """ - Compare two `InstructionData` for equality. - - This operation requires a reference to a `ValueListPool` to - determine if the contents of any `ValueLists` are equal. - """) - with fmt.indented( - 'pub fn eq(&self, other: &Self, pool: &ir::ValueListPool)' - ' -> bool {', - '}'): - with fmt.indented('if ::core::mem::discriminant(self) != ' - '::core::mem::discriminant(other) {', '}'): - fmt.line('return false;') - with fmt.indented('match (self, other) {', '}'): - for f in InstructionFormat.all_formats: - n = '&InstructionData::' + f.name - members = ['opcode'] - if f.typevar_operand is None: - args_eq = None - elif f.has_value_list: - members.append('args') - args_eq = 'args1.as_slice(pool) == ' \ - 'args2.as_slice(pool)' - elif f.num_value_operands == 1: - members.append('arg') - args_eq = 'arg1 == arg2' - else: - members.append('args') - args_eq = 'args1 == args2' - for field in f.imm_fields: - members.append(field.member) - pat1 = ', '.join('{}: ref {}1'.format(x, x) - for x in members) - pat2 = ', '.join('{}: ref {}2'.format(x, x) - for x in members) - with fmt.indented('({} {{ {} }}, {} {{ {} }}) => {{' - .format(n, pat1, n, pat2), '}'): - fmt.line('opcode1 == opcode2') - for field in f.imm_fields: - fmt.line('&& {}1 == {}2' - .format(field.member, field.member)) - if args_eq is not None: - fmt.line('&& {}'.format(args_eq)) - fmt.line('_ => unreachable!()') - fmt.line() - - fmt.doc_comment( - """ - Hash an `InstructionData`. - - This operation requires a reference to a `ValueListPool` to - hash the contents of any `ValueLists`. - """) - with fmt.indented( - 'pub fn hash' - '(&self, state: &mut H, pool: &ir::ValueListPool) {', - '}'): - with fmt.indented('match *self {', '}'): - for f in InstructionFormat.all_formats: - n = 'InstructionData::' + f.name - members = ['opcode'] - if f.typevar_operand is None: - args = '&()' - elif f.has_value_list: - members.append('ref args') - args = 'args.as_slice(pool)' - elif f.num_value_operands == 1: - members.append('ref arg') - args = 'arg' - else: - members.append('ref args') - args = 'args' - for field in f.imm_fields: - members.append(field.member) - pat = n + ' { ' + ', '.join(members) + ' }' - with fmt.indented(pat + ' => {', '}'): - fmt.line('::core::hash::Hash::hash( ' - '&::core::mem::discriminant(self), state);') - fmt.line('::core::hash::Hash::hash(&opcode, state);') - for field in f.imm_fields: - fmt.line('::core::hash::Hash::hash(&{}, state);' - .format(field.member)) - fmt.line('::core::hash::Hash::hash({}, state);' - .format(args)) - - -def collect_instr_groups(isas): - # type: (Sequence[TargetISA]) -> List[InstructionGroup] - seen = set() # type: Set[InstructionGroup] - groups = [] - for isa in isas: - for g in isa.instruction_groups: - if g not in seen: - groups.append(g) - seen.add(g) - return groups - - -def gen_opcodes(groups, fmt): - # type: (Sequence[InstructionGroup], srcgen.Formatter) -> Sequence[Instruction] # noqa - """ - Generate opcode enumerations. - - Return a list of all instructions. - """ - - fmt.doc_comment(''' - An instruction opcode. - - All instructions from all supported ISAs are present. - ''') - fmt.line('#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]') - instrs = [] - - # We explicitly set the discriminant of the first variant to 1, which - # allows us to take advantage of the NonZero optimization, meaning that - # wrapping enums can use the 0 discriminant instead of increasing the size - # of the whole type, and so SIZEOF(Option>) == SIZEOF(Opcode) - is_first_opcode = True - with fmt.indented('pub enum Opcode {', '}'): - for g in groups: - for i in g.instructions: - instrs.append(i) - i.number = len(instrs) - fmt.doc_comment('`{}`. ({})'.format(i, i.format.name)) - # Document polymorphism. - if i.is_polymorphic: - if i.use_typevar_operand: - opnum = i.value_opnums[i.format.typevar_operand] - fmt.doc_comment( - 'Type inferred from {}.' - .format(i.ins[opnum])) - # Enum variant itself. - if is_first_opcode: - fmt.line(i.camel_name + ' = 1,') - is_first_opcode = False - else: - fmt.line(i.camel_name + ',') - fmt.line() - - with fmt.indented('impl Opcode {', '}'): - for attr in sorted(Instruction.ATTRIBS.keys()): - fmt.doc_comment(Instruction.ATTRIBS[attr]) - with fmt.indented('pub fn {}(self) -> bool {{' - .format(attr), '}'): - m = srcgen.Match('self') - for i in instrs: - if getattr(i, attr): - m.arm('Opcode::' + i.camel_name, [], 'true') - m.arm('_', [], 'false') - fmt.match(m) - fmt.line() - fmt.line() - - # Generate a private opcode_format table. - with fmt.indented( - 'const OPCODE_FORMAT: [InstructionFormat; {}] = [' - .format(len(instrs)), - '];'): - for i in instrs: - fmt.format( - 'InstructionFormat::{}, // {}', - i.format.name, i.name) - fmt.line() - - # Generate a private opcode_name function. - with fmt.indented('fn opcode_name(opc: Opcode) -> &\'static str {', '}'): - m = srcgen.Match('opc') - for i in instrs: - m.arm('Opcode::' + i.camel_name, [], '"{}"'.format(i.name)) - fmt.match(m) - fmt.line() - - # Generate an opcode hash table for looking up opcodes by name. - hash_table = constant_hash.compute_quadratic( - instrs, - lambda i: constant_hash.simple_hash(i.name)) - with fmt.indented( - 'const OPCODE_HASH_TABLE: [Option; {}] = [' - .format(len(hash_table)), '];'): - for i in hash_table: - if i is None: - fmt.line('None,') - else: - fmt.format('Some(Opcode::{}),', i.camel_name) - fmt.line() - return instrs - - -def get_constraint(op, ctrl_typevar, type_sets): - # type: (Operand, TypeVar, UniqueTable) -> str - """ - Get the value type constraint for an SSA value operand, where - `ctrl_typevar` is the controlling type variable. - - Each operand constraint is represented as a string, one of: - - - `Concrete(vt)`, where `vt` is a value type name. - - `Free(idx)` where `idx` is an index into `type_sets`. - - `Same`, `Lane`, `AsBool` for controlling typevar-derived constraints. - """ - assert op.is_value() - tv = op.typevar - - # A concrete value type. - if tv.singleton_type(): - return 'Concrete({})'.format(tv.singleton_type().rust_name()) - - if tv.free_typevar() is not ctrl_typevar: - assert not tv.is_derived - return 'Free({})'.format(type_sets.add(tv.type_set)) - - if tv.is_derived: - assert tv.base is ctrl_typevar, "Not derived from ctrl_typevar" - return camel_case(tv.derived_func) - - assert tv is ctrl_typevar - return 'Same' - - -# TypeSet indexes are encoded in 8 bits, with `0xff` reserved. -typeset_limit = 0xff - - -def gen_typesets_table(fmt, type_sets): - # type: (srcgen.Formatter, UniqueTable) -> None - """ - Generate the table of ValueTypeSets described by type_sets. - """ - if len(type_sets.table) == 0: - return - fmt.comment('Table of value type sets.') - assert len(type_sets.table) <= typeset_limit, "Too many type sets" - with fmt.indented( - 'const TYPE_SETS: [ir::instructions::ValueTypeSet; {}] = [' - .format(len(type_sets.table)), '];'): - for ts in type_sets.table: - with fmt.indented('ir::instructions::ValueTypeSet {', '},'): - ts.emit_fields(fmt) - - -def gen_type_constraints(fmt, instrs): - # type: (srcgen.Formatter, Sequence[Instruction]) -> None - """ - Generate value type constraints for all instructions. - - - Emit a compact constant table of ValueTypeSet objects. - - Emit a compact constant table of OperandConstraint objects. - - Emit an opcode-indexed table of instruction constraints. - - """ - - # Table of TypeSet instances. - type_sets = UniqueTable() - - # Table of operand constraint sequences (as tuples). Each operand - # constraint is represented as a string, one of: - # - `Concrete(vt)`, where `vt` is a value type name. - # - `Free(idx)` where `idx` is an index into `type_sets`. - # - `Same`, `Lane`, `AsBool` for controlling typevar-derived constraints. - operand_seqs = UniqueSeqTable() - - # Preload table with constraints for typical binops. - operand_seqs.add(['Same'] * 3) - - fmt.comment('Table of opcode constraints.') - with fmt.indented( - 'const OPCODE_CONSTRAINTS: [OpcodeConstraints; {}] = [' - .format(len(instrs)), '];'): - for i in instrs: - # Collect constraints for the value results, not including - # `variable_args` results which are always special cased. - constraints = list() - ctrl_typevar = None - ctrl_typeset = typeset_limit - if i.is_polymorphic: - ctrl_typevar = i.ctrl_typevar - ctrl_typeset = type_sets.add(ctrl_typevar.type_set) - for idx in i.value_results: - constraints.append( - get_constraint(i.outs[idx], ctrl_typevar, type_sets)) - for opnum in i.value_opnums: - constraints.append( - get_constraint(i.ins[opnum], ctrl_typevar, type_sets)) - offset = operand_seqs.add(constraints) - fixed_results = len(i.value_results) - fixed_values = len(i.value_opnums) - # Can the controlling type variable be inferred from the designated - # operand? - use_typevar_operand = i.is_polymorphic and i.use_typevar_operand - # Can the controlling type variable be inferred from the result? - use_result = (fixed_results > 0 and - i.outs[i.value_results[0]].typevar == ctrl_typevar) - # Are we required to use the designated operand instead of the - # result? - requires_typevar_operand = use_typevar_operand and not use_result - fmt.comment( - '{}: fixed_results={}, use_typevar_operand={}, ' - 'requires_typevar_operand={}, fixed_values={}' - .format(i.camel_name, fixed_results, use_typevar_operand, - requires_typevar_operand, fixed_values)) - fmt.comment('Constraints={}'.format(constraints)) - if i.is_polymorphic: - fmt.comment( - 'Polymorphic over {}'.format(ctrl_typevar.type_set)) - # Compute the bit field encoding, c.f. instructions.rs. - assert fixed_results < 8, "Bit field encoding too tight" - flags = fixed_results - if use_typevar_operand: - flags |= 8 - if requires_typevar_operand: - flags |= 0x10 - assert fixed_values < 8, "Bit field encoding too tight" - flags |= fixed_values << 5 - - with fmt.indented('OpcodeConstraints {', '},'): - fmt.line('flags: {:#04x},'.format(flags)) - fmt.line('typeset_offset: {},'.format(ctrl_typeset)) - fmt.line('constraint_offset: {},'.format(offset)) - fmt.line() - - gen_typesets_table(fmt, type_sets) - fmt.line() - - fmt.comment('Table of operand constraint sequences.') - with fmt.indented( - 'const OPERAND_CONSTRAINTS: [OperandConstraint; {}] = [' - .format(len(operand_seqs.table)), '];'): - for c in operand_seqs.table: - fmt.line('OperandConstraint::{},'.format(c)) - - -def gen_format_constructor(iform, fmt): - # type: (InstructionFormat, srcgen.Formatter) -> None - """ - Emit a method for creating and inserting an `iform` instruction, where - `iform` is an instruction format. - - All instruction formats take an `opcode` argument and a `ctrl_typevar` - argument for deducing the result types. - """ - - # Construct method arguments. - args = ['self', 'opcode: Opcode', 'ctrl_typevar: Type'] - - # Normal operand arguments. Start with the immediate operands. - for f in iform.imm_fields: - args.append('{}: {}'.format(f.member, f.kind.rust_type)) - # Then the value operands. - if iform.has_value_list: - # Take all value arguments as a finished value list. The value lists - # are created by the individual instruction constructors. - args.append('args: ir::ValueList') - else: - # Take a fixed number of value operands. - for i in range(iform.num_value_operands): - args.append('arg{}: Value'.format(i)) - - proto = '{}({})'.format(iform.name, ', '.join(args)) - proto += " -> (Inst, &'f mut ir::DataFlowGraph)" - - fmt.doc_comment(str(iform)) - fmt.line('#[allow(non_snake_case)]') - with fmt.indented('fn {} {{'.format(proto), '}'): - # Generate the instruction data. - with fmt.indented( - 'let data = ir::InstructionData::{} {{'.format(iform.name), - '};'): - fmt.line('opcode,') - gen_member_inits(iform, fmt) - - fmt.line('self.build(data, ctrl_typevar)') - - -def gen_member_inits(iform, fmt): - # type: (InstructionFormat, srcgen.Formatter) -> None - """ - Emit member initializers for an `iform` instruction. - """ - - # Immediate operands. - # We have local variables with the same names as the members. - for f in iform.imm_fields: - fmt.line('{},'.format(f.member)) - - # Value operands. - if iform.has_value_list: - fmt.line('args,') - elif iform.num_value_operands == 1: - fmt.line('arg: arg0,') - elif iform.num_value_operands > 1: - args = ('arg{}'.format(i) for i in range(iform.num_value_operands)) - fmt.line('args: [{}],'.format(', '.join(args))) - - -def gen_inst_builder(inst, fmt): - # type: (Instruction, srcgen.Formatter) -> None - """ - 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. - if inst.format.has_value_list: - args = ['mut self'] - else: - args = ['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('{}: crate::ir::Type'.format(inst.ctrl_typevar.name)) - - tmpl_types = list() # type: List[str] - into_args = list() # type: List[str] - for op in inst.ins: - if isinstance(op.kind, 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) - - if len(tmpl_types) > 0: - tmpl = '<{}>'.format(', '.join(tmpl_types)) - else: - tmpl = '' - proto = '{}{}({}) -> {}'.format( - inst.snake_name(), tmpl, ', '.join(args), rtype) - - fmt.doc_comment('`{}`\n\n{}'.format(inst, inst.blurb())) - fmt.line('#[allow(non_snake_case)]') - with fmt.indented('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 not inst.is_polymorphic: - # No controlling type variable needed. - args.append('types::INVALID') - else: - assert inst.is_polymorphic and inst.use_typevar_operand - # Infer the controlling type variable from the input operands. - opnum = inst.value_opnums[inst.format.typevar_operand] - fmt.line( - 'let ctrl_typevar = self.data_flow_graph().value_type({});' - .format(inst.ins[opnum].name)) - # The format constructor will resolve the result types from the - # type var. - args.append('ctrl_typevar') - - # Now add all of the immediate operands to the constructor arguments. - for opnum in inst.imm_opnums: - args.append(inst.ins[opnum].name) - - # Finally, the value operands. - if inst.format.has_value_list: - # We need to build a value list with all the arguments. - fmt.line('let mut vlist = ir::ValueList::default();') - args.append('vlist') - with fmt.indented('{', '}'): - fmt.line( - 'let pool = ' - '&mut self.data_flow_graph_mut().value_lists;') - for op in inst.ins: - if op.is_value(): - fmt.line('vlist.push({}, pool);'.format(op.name)) - elif op.is_varargs(): - fmt.line( - 'vlist.extend({}.iter().cloned(), pool);' - .format(op.name)) - else: - # With no value list, we're guaranteed to just have a set of fixed - # value operands. - for opnum in inst.value_opnums: - args.append(inst.ins[opnum].name) - - # Call to the format constructor, - fcall = 'self.{}({})'.format(inst.format.name, ', '.join(args)) - - if len(inst.value_results) == 0: - fmt.line(fcall + '.0') - return - - fmt.line('let (inst, dfg) = {};'.format(fcall)) - - if len(inst.value_results) == 1: - fmt.line('dfg.first_result(inst)') - return - - fmt.format( - 'let results = &dfg.inst_results(inst)[0..{}];', - len(inst.value_results)) - fmt.format('({})', ', '.join( - 'results[{}]'.format(i) for i in range(len(inst.value_results)))) - - -def gen_builder(insts, fmt): - # type: (Sequence[Instruction], srcgen.Formatter) -> None - """ - Generate a Builder trait with methods for all instructions. - """ - fmt.doc_comment(""" - Convenience methods for building instructions. - - The `InstBuilder` 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 `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<'f>: InstBuilderBase<'f> {", '}'): - for inst in insts: - gen_inst_builder(inst, fmt) - for f in InstructionFormat.all_formats: - gen_format_constructor(f, fmt) - - -def generate(isas, out_dir): - # type: (Sequence[TargetISA], str) -> None - groups = collect_instr_groups(isas) - - # opcodes.rs - fmt = srcgen.Formatter() - gen_formats(fmt) - gen_instruction_data(fmt) - fmt.line() - gen_instruction_data_impl(fmt) - fmt.line() - instrs = gen_opcodes(groups, fmt) - gen_type_constraints(fmt, instrs) - fmt.update_file('opcodes.rs', out_dir) - - # inst_builder.rs - fmt = srcgen.Formatter() - gen_builder(instrs, fmt) - fmt.update_file('inst_builder.rs', out_dir) diff --git a/cranelift/codegen/meta-python/gen_legalizer.py b/cranelift/codegen/meta-python/gen_legalizer.py index 2f78d086f3..b4dfd0089f 100644 --- a/cranelift/codegen/meta-python/gen_legalizer.py +++ b/cranelift/codegen/meta-python/gen_legalizer.py @@ -15,7 +15,6 @@ from cdsl.ast import Var from cdsl.ti import ti_rtl, TypeEnv, get_type_env, TypesEqual,\ InTypeset, WiderOrEq from unique_table import UniqueTable -from gen_instr import gen_typesets_table from cdsl.typevar import TypeVar try: @@ -29,6 +28,27 @@ except ImportError: pass +# TypeSet indexes are encoded in 8 bits, with `0xff` reserved. +typeset_limit = 0xff + + +def gen_typesets_table(fmt, type_sets): + # type: (Formatter, UniqueTable) -> None + """ + Generate the table of ValueTypeSets described by type_sets. + """ + if len(type_sets.table) == 0: + return + fmt.comment('Table of value type sets.') + assert len(type_sets.table) <= typeset_limit, "Too many type sets" + with fmt.indented( + 'const TYPE_SETS: [ir::instructions::ValueTypeSet; {}] = [' + .format(len(type_sets.table)), '];'): + for ts in type_sets.table: + with fmt.indented('ir::instructions::ValueTypeSet {', '},'): + ts.emit_fields(fmt) + + def get_runtime_typechecks(xform): # type: (XForm) -> List[TypeConstraint] """ diff --git a/cranelift/codegen/meta/src/lib.rs b/cranelift/codegen/meta/src/lib.rs index 8118922fb0..fccba327e0 100644 --- a/cranelift/codegen/meta/src/lib.rs +++ b/cranelift/codegen/meta/src/lib.rs @@ -40,8 +40,8 @@ pub fn generate(isas: &Vec, out_dir: &str) -> Result<(), error::Error> gen_inst::generate( all_inst_groups, &shared_defs.format_registry, - "new_opcodes.rs", - "new_inst_builder.rs", + "opcodes.rs", + "inst_builder.rs", &out_dir, )?;