Files
wasmtime/lib/codegen/meta/gen_binemit.py
Tyler McMullen f636d795c5 load_complex and store_complex instructions (#309)
* Start adding the load_complex and store_complex instructions.

N.b.:
The text format is not correct yet. Requires changes to the lexer and parser.
I'm not sure why I needed to change the RuntimeError to Exception yet. Will fix.

* Get first few encodings of load_complex working. Still needs var args type checking.

* Clean up ModRM helper functions in binemit.

* Implement 32-bit displace for load_complex

* Use encoding helpers instead of doing them all by hand

* Initial implementation of store_complex

* Parse value list for load/store_complex with + as delimiter. Looks nice.

* Add sign/zero-extension and size variants for load_complex.

* Add size variants of store_complex.

* Add asm helper lines to load/store complex bin tests.

* Example of length-checking the instruction ValueList for an encoding. Extremely questionable implementation.

* Fix Python linting issues

* First draft of postopt pass to fold adds and loads into load_complex. Just simple loads for now.

* Optimization pass now works with all types of loads.

* Add store+add -> store_complex to postopt pass

* Put complex address optimization behind ISA flag.

* Add load/store complex for f32 and f64

* Fixes changes to lexer that broke NaN parsing.

Abstracts away the repeated checks for whether or not the characters
following a + or - are going to be parsed as a number or not.

* Fix formatting issues

* Fix register restrictions for complex addresses.

* Encoding tests for x86-32.

* Add documentation for newly added instructions, recipes, and cdsl changes.

* Fix python formatting again

* Apply value-list length predicates to all LoadComplex and StoreComplex instructions.

* Add predicate types to new encoding helpers for mypy.

* Import FieldPredicate to satisfy mypy.

* Add and fix some "asm" strings in the encoding tests.

* Line-up 'bin' comments in x86/binary64 test

* Test parsing of offset-less store_complex instruction.

* 'sNaN' not 'sNan'

* Bounds check the lookup for polymorphic typevar operand.

* Fix encodings for istore16_complex.
2018-05-09 14:07:00 -05:00

171 lines
6.3 KiB
Python

"""
Generate binary emission code for each ISA.
"""
from __future__ import absolute_import
from cdsl.registers import RegClass, Stack
import srcgen
try:
from typing import Sequence, List # noqa
from cdsl.isa import TargetISA, EncRecipe, OperandConstraint # noqa
except ImportError:
pass
def gen_recipe(recipe, fmt):
# type: (EncRecipe, srcgen.Formatter) -> None
"""
Generate code to handle a single recipe.
- Unpack the instruction data, knowing the format.
- Determine register locations for operands with register constraints.
- Determine stack slot locations for operands with stack constraints.
- Call hand-written code for the actual emission.
"""
iform = recipe.format
nvops = iform.num_value_operands
want_args = any(isinstance(i, RegClass) or isinstance(i, Stack)
for i in recipe.ins)
assert not want_args or nvops > 0 or iform.has_value_list
want_outs = any(isinstance(o, RegClass) or isinstance(o, Stack)
for o in recipe.outs)
# Regmove instructions get special treatment.
is_regmove = (recipe.format.name in ('RegMove', 'RegSpill', 'RegFill'))
# First unpack the instruction.
with fmt.indented(
'if let InstructionData::{} {{'.format(iform.name),
'}'):
fmt.line('opcode,')
for f in iform.imm_fields:
fmt.line('{},'.format(f.member))
if want_args:
if iform.has_value_list or nvops > 1:
fmt.line('ref args,')
else:
fmt.line('arg,')
fmt.line('..')
fmt.outdented_line('} = func.dfg[inst] {')
# Pass recipe arguments in this order: inputs, imm_fields, outputs.
args = ''
# Normalize to an `args` array.
if want_args and not is_regmove:
if iform.has_value_list:
fmt.line('let args = args.as_slice(&func.dfg.value_lists);')
elif nvops == 1:
fmt.line('let args = [arg];')
args += unwrap_values(recipe.ins, 'in', 'args', fmt)
for f in iform.imm_fields:
args += ', ' + f.member
# Unwrap interesting output arguments.
if want_outs:
if len(recipe.outs) == 1:
fmt.line('let results = [func.dfg.first_result(inst)];')
else:
fmt.line('let results = func.dfg.inst_results(inst);')
args += unwrap_values(recipe.outs, 'out', 'results', fmt)
# Special handling for regmove instructions. Update the register
# diversion tracker.
if recipe.format.name == 'RegMove':
fmt.line('divert.regmove(arg, src, dst);')
elif recipe.format.name == 'RegSpill':
fmt.line('divert.regspill(arg, src, dst);')
elif recipe.format.name == 'RegFill':
fmt.line('divert.regfill(arg, src, dst);')
# Call hand-written code. If the recipe contains a code snippet, use
# that. Otherwise cal a recipe function in the target ISA's binemit
# module.
if recipe.emit is None:
fmt.format(
'return recipe_{}(func, inst, sink, bits{});',
recipe.name.lower(), args)
else:
fmt.multi_line(recipe.emit)
fmt.line('return;')
def unwrap_values(args, prefix, values, fmt):
# type: (Sequence[OperandConstraint], str, str, srcgen.Formatter) -> str # noqa
"""
Emit code that unwraps values living in registers or stack slots.
:param args: Input or output constraints.
:param prefix: Prefix to be used for the generated local variables.
:param values: Name of slice containing the values to be unwrapped.
:returns: Comma separated list of the generated variables
"""
varlist = ''
for i, cst in enumerate(args):
if isinstance(cst, RegClass):
v = '{}_reg{}'.format(prefix, i)
varlist += ', ' + v
fmt.format(
'let {} = divert.reg({}[{}], &func.locations);',
v, values, i)
elif isinstance(cst, Stack):
v = '{}_stk{}'.format(prefix, i)
varlist += ', ' + v
with fmt.indented(
'let {} = StackRef::masked('.format(v),
').unwrap();'):
fmt.format('divert.stack({}[{}], &func.locations),', values, i)
fmt.format('{},', cst.stack_base_mask())
fmt.line('&func.stack_slots,')
return varlist
def gen_isa(isa, fmt):
# type: (TargetISA, srcgen.Formatter) -> None
"""
Generate Binary emission code for `isa`.
"""
fmt.doc_comment(
'''
Emit binary machine code for `inst` for the {} ISA.
'''.format(isa.name))
if len(isa.all_recipes) == 0:
# No encoding recipes: Emit a stub.
with fmt.indented('pub fn emit_inst<CS: CodeSink + ?Sized>('):
fmt.line('func: &Function,')
fmt.line('inst: Inst,')
fmt.line('_divert: &mut RegDiversions,')
fmt.line('_sink: &mut CS,')
with fmt.indented(') {', '}'):
fmt.line('bad_encoding(func, inst)')
else:
fmt.line('#[allow(unused_variables, unreachable_code)]')
with fmt.indented('pub fn emit_inst<CS: CodeSink + ?Sized>('):
fmt.line('func: &Function,')
fmt.line('inst: Inst,')
fmt.line('divert: &mut RegDiversions,')
fmt.line('sink: &mut CS,')
with fmt.indented(') {', '}'):
fmt.line('let encoding = func.encodings[inst];')
fmt.line('let bits = encoding.bits();')
with fmt.indented('match func.encodings[inst].recipe() {', '}'):
for i, recipe in enumerate(isa.all_recipes):
fmt.comment('Recipe {}'.format(recipe.name))
with fmt.indented('{} => {{'.format(i), '}'):
gen_recipe(recipe, fmt)
fmt.line('_ => {},')
# Allow for un-encoded ghost instructions.
# Verifier checks the details.
with fmt.indented('if encoding.is_legal() {', '}'):
fmt.line('bad_encoding(func, inst);')
def generate(isas, out_dir):
# type: (Sequence[TargetISA], str) -> None
for isa in isas:
fmt = srcgen.Formatter()
gen_isa(isa, fmt)
fmt.update_file('binemit-{}.rs'.format(isa.name), out_dir)