Allow for multiple legalization patterns for the same opcode.
Each input pattern can have a predicate in addition to an opcode being matched. When an opcode has multiple patterns, execute the first pattern with a true predicate. The predicates can be type checks or instruction predicates checking immediate fields.
This commit is contained in:
@@ -9,6 +9,7 @@ the input instruction.
|
|||||||
"""
|
"""
|
||||||
from __future__ import absolute_import
|
from __future__ import absolute_import
|
||||||
from srcgen import Formatter
|
from srcgen import Formatter
|
||||||
|
from collections import defaultdict
|
||||||
from base import instructions
|
from base import instructions
|
||||||
from cdsl.ast import Var
|
from cdsl.ast import Var
|
||||||
from cdsl.ti import ti_rtl, TypeEnv, get_type_env, TypesEqual,\
|
from cdsl.ti import ti_rtl, TypeEnv, get_type_env, TypesEqual,\
|
||||||
@@ -18,7 +19,7 @@ from gen_instr import gen_typesets_table
|
|||||||
from cdsl.typevar import TypeVar
|
from cdsl.typevar import TypeVar
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from typing import Sequence, List, Dict, Set # noqa
|
from typing import Sequence, List, Dict, Set, DefaultDict # noqa
|
||||||
from cdsl.isa import TargetISA # noqa
|
from cdsl.isa import TargetISA # noqa
|
||||||
from cdsl.ast import Def # noqa
|
from cdsl.ast import Def # noqa
|
||||||
from cdsl.xform import XForm, XFormGroup # noqa
|
from cdsl.xform import XForm, XFormGroup # noqa
|
||||||
@@ -78,6 +79,11 @@ def emit_runtime_typecheck(check, fmt, type_sets):
|
|||||||
# type: (TypeConstraint, Formatter, UniqueTable) -> None
|
# type: (TypeConstraint, Formatter, UniqueTable) -> None
|
||||||
"""
|
"""
|
||||||
Emit rust code for the given check.
|
Emit rust code for the given check.
|
||||||
|
|
||||||
|
The emitted code is a statement redefining the `predicate` variable like
|
||||||
|
this:
|
||||||
|
|
||||||
|
let predicate = predicate && ...
|
||||||
"""
|
"""
|
||||||
def build_derived_expr(tv):
|
def build_derived_expr(tv):
|
||||||
# type: (TypeVar) -> str
|
# type: (TypeVar) -> str
|
||||||
@@ -116,33 +122,26 @@ def emit_runtime_typecheck(check, fmt, type_sets):
|
|||||||
if check.ts not in type_sets.index:
|
if check.ts not in type_sets.index:
|
||||||
type_sets.add(check.ts)
|
type_sets.add(check.ts)
|
||||||
ts = type_sets.index[check.ts]
|
ts = type_sets.index[check.ts]
|
||||||
|
|
||||||
fmt.comment("{} must belong to {}".format(tv, check.ts))
|
fmt.comment("{} must belong to {}".format(tv, check.ts))
|
||||||
with fmt.indented('if !TYPE_SETS[{}].contains({}) {{'.format(ts, tv),
|
fmt.format(
|
||||||
'};'):
|
'let predicate = predicate && TYPE_SETS[{}].contains({});',
|
||||||
fmt.line('return false;')
|
ts, tv)
|
||||||
elif (isinstance(check, TypesEqual)):
|
elif (isinstance(check, TypesEqual)):
|
||||||
with fmt.indented('{', '};'):
|
with fmt.indented(
|
||||||
fmt.line('let a = {};'.format(build_derived_expr(check.tv1)))
|
'let predicate = predicate && match ({}, {}) {{'
|
||||||
fmt.line('let b = {};'.format(build_derived_expr(check.tv2)))
|
.format(build_derived_expr(check.tv1),
|
||||||
|
build_derived_expr(check.tv2)), '};'):
|
||||||
fmt.comment('On overflow constraint doesn\'t appply')
|
fmt.line('(Some(a), Some(b)) => a == b,')
|
||||||
with fmt.indented('if a.is_none() || b.is_none() {', '};'):
|
fmt.comment('On overflow, constraint doesn\'t appply')
|
||||||
fmt.line('return false;')
|
fmt.line('_ => false,')
|
||||||
|
|
||||||
with fmt.indented('if a != b {', '};'):
|
|
||||||
fmt.line('return false;')
|
|
||||||
elif (isinstance(check, WiderOrEq)):
|
elif (isinstance(check, WiderOrEq)):
|
||||||
with fmt.indented('{', '};'):
|
with fmt.indented(
|
||||||
fmt.line('let a = {};'.format(build_derived_expr(check.tv1)))
|
'let predicate = predicate && match ({}, {}) {{'
|
||||||
fmt.line('let b = {};'.format(build_derived_expr(check.tv2)))
|
.format(build_derived_expr(check.tv1),
|
||||||
|
build_derived_expr(check.tv2)), '};'):
|
||||||
fmt.comment('On overflow constraint doesn\'t appply')
|
fmt.line('(Some(a), Some(b)) => a.wider_or_equal(b),')
|
||||||
with fmt.indented('if a.is_none() || b.is_none() {', '};'):
|
fmt.comment('On overflow, constraint doesn\'t appply')
|
||||||
fmt.line('return false;')
|
fmt.line('_ => false,')
|
||||||
|
|
||||||
with fmt.indented('if !a.wider_or_equal(b) {', '};'):
|
|
||||||
fmt.line('return false;')
|
|
||||||
else:
|
else:
|
||||||
assert False, "Unknown check {}".format(check)
|
assert False, "Unknown check {}".format(check)
|
||||||
|
|
||||||
@@ -216,14 +215,12 @@ def unwrap_inst(iref, node, fmt):
|
|||||||
replace_inst = True
|
replace_inst = True
|
||||||
else:
|
else:
|
||||||
# Boring case: Detach the result values, capture them in locals.
|
# Boring case: Detach the result values, capture them in locals.
|
||||||
fmt.comment('Detaching results.')
|
|
||||||
for d in node.defs:
|
for d in node.defs:
|
||||||
fmt.line('let {};'.format(d))
|
fmt.line('let {};'.format(d))
|
||||||
with fmt.indented('{', '}'):
|
with fmt.indented('{', '}'):
|
||||||
fmt.line('let r = dfg.inst_results(inst);')
|
fmt.line('let r = dfg.inst_results(inst);')
|
||||||
for i in range(len(node.defs)):
|
for i in range(len(node.defs)):
|
||||||
fmt.line('{} = r[{}];'.format(node.defs[i], i))
|
fmt.line('{} = r[{}];'.format(node.defs[i], i))
|
||||||
fmt.line('dfg.clear_results(inst);')
|
|
||||||
for d in node.defs:
|
for d in node.defs:
|
||||||
if d.has_free_typevar():
|
if d.has_free_typevar():
|
||||||
fmt.line(
|
fmt.line(
|
||||||
@@ -312,7 +309,7 @@ def emit_dst_inst(node, fmt):
|
|||||||
def gen_xform(xform, fmt, type_sets):
|
def gen_xform(xform, fmt, type_sets):
|
||||||
# type: (XForm, Formatter, UniqueTable) -> None
|
# type: (XForm, Formatter, UniqueTable) -> None
|
||||||
"""
|
"""
|
||||||
Emit code for `xform`, assuming the the opcode of xform's root instruction
|
Emit code for `xform`, assuming that the opcode of xform's root instruction
|
||||||
has already been matched.
|
has already been matched.
|
||||||
|
|
||||||
`inst: Inst` is the variable to be replaced. It is pointed to by `pos:
|
`inst: Inst` is the variable to be replaced. It is pointed to by `pos:
|
||||||
@@ -323,16 +320,24 @@ def gen_xform(xform, fmt, type_sets):
|
|||||||
# variables.
|
# variables.
|
||||||
replace_inst = unwrap_inst('inst', xform.src.rtl[0], fmt)
|
replace_inst = unwrap_inst('inst', xform.src.rtl[0], fmt)
|
||||||
|
|
||||||
# We could support instruction predicates, but not yet. Should we just
|
# Check instruction predicate and emit type checks.
|
||||||
# return false if it fails? What about multiple patterns with different
|
|
||||||
# predicates for the same opcode?
|
|
||||||
instp = xform.src.rtl[0].expr.inst_predicate()
|
instp = xform.src.rtl[0].expr.inst_predicate()
|
||||||
assert instp is None, "Instruction predicates not supported in legalizer"
|
# TODO: The instruction predicate should be evaluated with all the inst
|
||||||
|
# immediate fields available. Probably by unwrap_inst().
|
||||||
|
fmt.format('let predicate = {};',
|
||||||
|
instp.rust_predicate(0) if instp else 'true')
|
||||||
|
|
||||||
# Emit any runtime checks.
|
# Emit any runtime checks.
|
||||||
for check in get_runtime_typechecks(xform):
|
for check in get_runtime_typechecks(xform):
|
||||||
emit_runtime_typecheck(check, fmt, type_sets)
|
emit_runtime_typecheck(check, fmt, type_sets)
|
||||||
|
|
||||||
|
# Guard the actual expansion by `predicate`.
|
||||||
|
with fmt.indented('if predicate {', '}'):
|
||||||
|
# If we're going to delete `inst`, we need to detach its results first
|
||||||
|
# so they can be reattached during pattern expansion.
|
||||||
|
if not replace_inst:
|
||||||
|
fmt.line('dfg.clear_results(inst);')
|
||||||
|
|
||||||
# Emit the destination pattern.
|
# Emit the destination pattern.
|
||||||
for dst in xform.dst.rtl:
|
for dst in xform.dst.rtl:
|
||||||
emit_dst_inst(dst, fmt)
|
emit_dst_inst(dst, fmt)
|
||||||
@@ -341,6 +346,7 @@ def gen_xform(xform, fmt, type_sets):
|
|||||||
# replace it.
|
# replace it.
|
||||||
if not replace_inst:
|
if not replace_inst:
|
||||||
fmt.line('assert_eq!(pos.remove_inst(), inst);')
|
fmt.line('assert_eq!(pos.remove_inst(), inst);')
|
||||||
|
fmt.line('return true;')
|
||||||
|
|
||||||
|
|
||||||
def gen_xform_group(xgrp, fmt, type_sets):
|
def gen_xform_group(xgrp, fmt, type_sets):
|
||||||
@@ -358,19 +364,27 @@ def gen_xform_group(xgrp, fmt, type_sets):
|
|||||||
# pointing at an instruction.
|
# pointing at an instruction.
|
||||||
fmt.line('let inst = pos.current_inst().expect("need instruction");')
|
fmt.line('let inst = pos.current_inst().expect("need instruction");')
|
||||||
|
|
||||||
with fmt.indented('match dfg[inst].opcode() {', '}'):
|
# Group the xforms by opcode so we can generate a big switch.
|
||||||
|
# Preserve ordering.
|
||||||
|
xforms = defaultdict(list) # type: DefaultDict[str, List[XForm]]
|
||||||
for xform in xgrp.xforms:
|
for xform in xgrp.xforms:
|
||||||
inst = xform.src.rtl[0].expr.inst
|
inst = xform.src.rtl[0].expr.inst
|
||||||
|
xforms[inst.camel_name].append(xform)
|
||||||
|
|
||||||
|
with fmt.indented('match dfg[inst].opcode() {', '}'):
|
||||||
|
for camel_name in sorted(xforms.keys()):
|
||||||
with fmt.indented(
|
with fmt.indented(
|
||||||
'ir::Opcode::{} => {{'.format(inst.camel_name), '}'):
|
'ir::Opcode::{} => {{'.format(camel_name), '}'):
|
||||||
|
for xform in xforms[camel_name]:
|
||||||
gen_xform(xform, fmt, type_sets)
|
gen_xform(xform, fmt, type_sets)
|
||||||
# We'll assume there are uncovered opcodes.
|
# We'll assume there are uncovered opcodes.
|
||||||
|
fmt.line('_ => {},')
|
||||||
|
|
||||||
|
# If we fall through, nothing was expanded. Call the chain if any.
|
||||||
if xgrp.chain:
|
if xgrp.chain:
|
||||||
fmt.format('_ => return {}(dfg, cfg, pos),',
|
fmt.format('{}(dfg, cfg, pos)', xgrp.chain.rust_name())
|
||||||
xgrp.chain.rust_name())
|
|
||||||
else:
|
else:
|
||||||
fmt.line('_ => return false,')
|
fmt.line('false')
|
||||||
fmt.line('true')
|
|
||||||
|
|
||||||
|
|
||||||
def gen_isa(isa, fmt, shared_groups):
|
def gen_isa(isa, fmt, shared_groups):
|
||||||
|
|||||||
@@ -49,39 +49,27 @@ def typeset_check(v, ts):
|
|||||||
# type: (Var, TypeSet) -> CheckProducer
|
# type: (Var, TypeSet) -> CheckProducer
|
||||||
return lambda typesets: format_check(
|
return lambda typesets: format_check(
|
||||||
typesets,
|
typesets,
|
||||||
'if !TYPE_SETS[{}].contains(typeof_{}) ' +
|
'let predicate = predicate && TYPE_SETS[{}].contains(typeof_{});\n',
|
||||||
'{{\n return false;\n}};\n', ts, v)
|
ts, v)
|
||||||
|
|
||||||
|
|
||||||
def equiv_check(tv1, tv2):
|
def equiv_check(tv1, tv2):
|
||||||
# type: (TypeVar, TypeVar) -> CheckProducer
|
# type: (str, str) -> CheckProducer
|
||||||
return lambda typesets: format_check(
|
return lambda typesets: format_check(
|
||||||
typesets,
|
typesets,
|
||||||
'{{\n' +
|
'let predicate = predicate && match ({}, {}) {{\n'
|
||||||
' let a = {};\n' +
|
' (Some(a), Some(b)) => a == b,\n'
|
||||||
' let b = {};\n' +
|
' _ => false,\n'
|
||||||
' if a.is_none() || b.is_none() {{\n' +
|
|
||||||
' return false;\n' +
|
|
||||||
' }};\n' +
|
|
||||||
' if a != b {{\n' +
|
|
||||||
' return false;\n' +
|
|
||||||
' }};\n' +
|
|
||||||
'}};\n', tv1, tv2)
|
'}};\n', tv1, tv2)
|
||||||
|
|
||||||
|
|
||||||
def wider_check(tv1, tv2):
|
def wider_check(tv1, tv2):
|
||||||
# type: (TypeVar, TypeVar) -> CheckProducer
|
# type: (str, str) -> CheckProducer
|
||||||
return lambda typesets: format_check(
|
return lambda typesets: format_check(
|
||||||
typesets,
|
typesets,
|
||||||
'{{\n' +
|
'let predicate = predicate && match ({}, {}) {{\n'
|
||||||
' let a = {};\n' +
|
' (Some(a), Some(b)) => a.wider_or_equal(b),\n'
|
||||||
' let b = {};\n' +
|
' _ => false,\n'
|
||||||
' if a.is_none() || b.is_none() {{\n' +
|
|
||||||
' return false;\n' +
|
|
||||||
' }};\n' +
|
|
||||||
' if !a.wider_or_equal(b) {{\n' +
|
|
||||||
' return false;\n' +
|
|
||||||
' }};\n' +
|
|
||||||
'}};\n', tv1, tv2)
|
'}};\n', tv1, tv2)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user