Files
wasmtime/meta/gen_instr.py
Jakob Stoklund Olesen 1e631fdbd6 Verify restrictions on polymorphism.
Add a typevar_operand argument to the InstructionFormat constructor which
determines the operand used for inferring the controlling type variable.

Identify polymorphic instructions when they are created, determine if the
controlling type variable can be inferred from the typevar_operand, and verify
the use of type variables in the other operands.

Generate type variable summary in the documentation, including how the
controlling type variable is inferred.
2016-05-19 14:59:46 -07:00

207 lines
8.1 KiB
Python

"""
Generate sources with instruction info.
"""
import srcgen
import constant_hash
import cretonne
def gen_formats(fmt):
"""Generate an instruction format enumeration"""
fmt.doc_comment('An instruction format')
fmt.doc_comment('')
fmt.doc_comment('Every opcode has a corresponding instruction format')
fmt.doc_comment('which is represented by both the `InstructionFormat`')
fmt.doc_comment('and the `InstructionData` enums.')
fmt.line('#[derive(Copy, Clone, PartialEq, Eq, Debug)]')
with fmt.indented('pub enum InstructionFormat {', '}'):
for f in cretonne.InstructionFormat.all_formats:
fmt.line(f.name + ',')
fmt.line()
# Emit a From<InstructionData> 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) -> InstructionFormat {",
'}'):
with fmt.indented('match *inst {', '}'):
for f in cretonne.InstructionFormat.all_formats:
fmt.line(('InstructionData::{} {{ .. }} => ' +
'InstructionFormat::{},')
.format(f.name, f.name))
fmt.line()
def gen_instruction_data_impl(fmt):
"""
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 first_type(&self) -> Type`
- `pub fn second_result(&self) -> Option<Value>`
- `pub fn second_result_mut<'a>(&'a mut self) -> Option<&'a mut Value>`
"""
# The `opcode` and `first_type` methods simply read the `opcode` and `ty`
# 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 {', '}'):
with fmt.indented('match *self {', '}'):
for f in cretonne.InstructionFormat.all_formats:
fmt.line(
'InstructionData::{} {{ opcode, .. }} => opcode,'
.format(f.name))
fmt.doc_comment('Type of the first result, or `VOID`.')
with fmt.indented('pub fn first_type(&self) -> Type {', '}'):
with fmt.indented('match *self {', '}'):
for f in cretonne.InstructionFormat.all_formats:
fmt.line(
'InstructionData::{} {{ ty, .. }} => ty,'
.format(f.name))
# Generate shared and mutable accessors for `second_result` which only
# applies to instruction formats that can produce multiple results.
# Everything else returns `None`.
fmt.doc_comment('Second result value, if any.')
with fmt.indented(
'pub fn second_result(&self) -> Option<Value> {', '}'):
with fmt.indented('match *self {', '}'):
for f in cretonne.InstructionFormat.all_formats:
if not f.multiple_results:
# Single or no results.
fmt.line(
'InstructionData::{} {{ .. }} => None,'
.format(f.name))
elif f.boxed_storage:
# Multiple results, boxed storage.
fmt.line(
'InstructionData::' + f.name +
' { ref data, .. }' +
' => Some(data.second_result),')
else:
# Multiple results, inline storage.
fmt.line(
'InstructionData::' + f.name +
' { second_result, .. }' +
' => Some(second_result),')
fmt.doc_comment('Mutable reference to second result value, if any.')
with fmt.indented(
"pub fn second_result_mut<'a>(&'a mut self) -> Option<&'a mut Value> {", '}'):
with fmt.indented('match *self {', '}'):
for f in cretonne.InstructionFormat.all_formats:
if not f.multiple_results:
# Single or no results.
fmt.line(
'InstructionData::{} {{ .. }} => None,'
.format(f.name))
elif f.boxed_storage:
# Multiple results, boxed storage.
fmt.line(
'InstructionData::' + f.name +
' { ref mut data, .. }' +
' => Some(&mut data.second_result),')
else:
# Multiple results, inline storage.
fmt.line(
'InstructionData::' + f.name +
' { ref mut second_result, .. }' +
' => Some(second_result),')
def collect_instr_groups(targets):
seen = set()
groups = []
for t in targets:
for g in t.instruction_groups:
if g not in seen:
groups.append(g)
seen.add(g)
return groups
def gen_opcodes(groups, fmt):
"""Generate opcode enumerations."""
fmt.doc_comment('An instruction opcode.')
fmt.doc_comment('')
fmt.doc_comment('All instructions from all supported targets are present.')
fmt.line('#[derive(Copy, Clone, PartialEq, Eq, Debug)]')
instrs = []
with fmt.indented('pub enum Opcode {', '}'):
fmt.line('NotAnOpcode,')
for g in groups:
for i in g.instructions:
instrs.append(i)
# 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))
# Document polymorphism.
if i.is_polymorphic:
if i.use_typevar_operand:
fmt.doc_comment(
'Type inferred from {}.'
.format(i.ins[i.format.typevar_operand]))
# Enum variant itself.
fmt.line(i.camel_name + ',')
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 {', '}'):
with fmt.indented('match opc {', '}'):
fmt.line('Opcode::NotAnOpcode => "<not an opcode>",')
for i in instrs:
fmt.format('Opcode::{} => "{}",', i.camel_name, i.name)
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: [Opcode; {}] = ['
.format(len(hash_table)), '];'):
for i in hash_table:
if i is None:
fmt.line('Opcode::NotAnOpcode,')
else:
fmt.format('Opcode::{},', i.camel_name)
fmt.line()
def generate(targets, out_dir):
groups = collect_instr_groups(targets)
# opcodes.rs
fmt = srcgen.Formatter()
gen_formats(fmt)
gen_instruction_data_impl(fmt)
gen_opcodes(groups, fmt)
fmt.update_file('opcodes.rs', out_dir)