[meta] Avoid unwrapping instructions several times during legalization;

This avoids doing multiple unpacking of the InstructionData for a single
legalization, improving readability and reducing size of the generated
code. For instance, icmp had to unpack the format once per IntCC
condition code.
This commit is contained in:
Benjamin Bouvier
2019-07-04 18:28:12 +02:00
parent ca53090f1b
commit 350b3b2406
5 changed files with 131 additions and 57 deletions

View File

@@ -63,7 +63,7 @@ impl Def {
format!("({})", results.join(", "))
};
format!("{} << {}", results, self.apply.to_comment_string(var_pool))
format!("{} := {}", results, self.apply.to_comment_string(var_pool))
}
}

View File

@@ -856,14 +856,14 @@ pub enum TypePredicateNode {
}
impl TypePredicateNode {
fn rust_predicate(&self) -> String {
fn rust_predicate(&self, func_str: &str) -> String {
match self {
TypePredicateNode::TypeVarCheck(index, value_type_name) => format!(
"func.dfg.value_type(args[{}]) == {}",
index, value_type_name
"{}.dfg.value_type(args[{}]) == {}",
func_str, index, value_type_name
),
TypePredicateNode::CtrlTypeVarCheck(value_type_name) => {
format!("func.dfg.ctrl_typevar(inst) == {}", value_type_name)
format!("{}.dfg.ctrl_typevar(inst) == {}", func_str, value_type_name)
}
}
}
@@ -884,18 +884,18 @@ pub enum InstructionPredicateNode {
}
impl InstructionPredicateNode {
fn rust_predicate(&self) -> String {
fn rust_predicate(&self, func_str: &str) -> String {
match self {
InstructionPredicateNode::FormatPredicate(node) => node.rust_predicate(),
InstructionPredicateNode::TypePredicate(node) => node.rust_predicate(),
InstructionPredicateNode::TypePredicate(node) => node.rust_predicate(func_str),
InstructionPredicateNode::And(nodes) => nodes
.iter()
.map(|x| x.rust_predicate())
.map(|x| x.rust_predicate(func_str))
.collect::<Vec<_>>()
.join(" && "),
InstructionPredicateNode::Or(nodes) => nodes
.iter()
.map(|x| x.rust_predicate())
.map(|x| x.rust_predicate(func_str))
.collect::<Vec<_>>()
.join(" || "),
}
@@ -1169,9 +1169,9 @@ impl InstructionPredicate {
self
}
pub fn rust_predicate(&self) -> String {
pub fn rust_predicate(&self, func_str: &str) -> String {
match &self.node {
Some(root) => root.rust_predicate(),
Some(root) => root.rust_predicate(func_str),
None => "true".into(),
}
}

View File

@@ -78,7 +78,7 @@ use crate::unique_table::UniqueSeqTable;
fn emit_instp(instp: &InstructionPredicate, has_func: bool, fmt: &mut Formatter) {
if instp.is_type_predicate() {
fmt.line("let args = inst.arguments(&func.dfg.value_lists);");
fmt.line(instp.rust_predicate());
fmt.line(instp.rust_predicate("func"));
return;
}
@@ -127,7 +127,7 @@ fn emit_instp(instp: &InstructionPredicate, has_func: bool, fmt: &mut Formatter)
// Silence dead argument.
fmt.line("let _ = func;");
}
fmtln!(fmt, "return {};", instp.rust_predicate());
fmtln!(fmt, "return {};", instp.rust_predicate("func"));
});
fmtln!(fmt, "}");

View File

@@ -1,6 +1,7 @@
use crate::cdsl::ast::{Def, DefPool, VarPool};
use crate::cdsl::ast::{Def, DefPool, Expr, VarPool};
use crate::cdsl::formats::FormatRegistry;
use crate::cdsl::isa::TargetIsa;
use crate::cdsl::operands::Operand;
use crate::cdsl::type_inference::Constraint;
use crate::cdsl::typevar::{TypeSet, TypeVar};
use crate::cdsl::xform::{Transform, TransformGroup, TransformGroups};
@@ -34,7 +35,7 @@ fn unwrap_inst(
let iform = format_registry.get(inst.format);
fmt.comment(format!(
"Unwrap {}",
"Unwrap fields from instruction format {}",
def.to_comment_string(&transform.var_pool)
));
@@ -42,19 +43,54 @@ fn unwrap_inst(
let arg_names = apply
.args
.iter()
.map(|arg| match arg.maybe_var() {
Some(var_index) => var_pool.get(var_index).name.as_ref(),
None => "_",
.enumerate()
.map(|(arg_num, arg)| match &arg {
Expr::Var(var_index) => var_pool.get(*var_index).name.as_ref(),
Expr::Literal(_) => {
let n = inst.imm_opnums.iter().position(|&i| i == arg_num).unwrap();
iform.imm_fields[n].member
}
})
.collect::<Vec<_>>()
.join(", ");
// May we need "args" in the values consumed by predicates?
let emit_args = iform.num_value_operands >= 1 || iform.has_value_list;
// We need a tuple:
// - if there's at least one value operand, then we emit a variable for the value, and the
// value list as args.
// - otherwise, if there's the count of immediate operands added to the presence of a value list exceeds one.
let need_tuple = if iform.num_value_operands >= 1 {
true
} else {
let mut imm_and_varargs = inst
.operands_in
.iter()
.filter(|op| op.is_immediate())
.count();
if iform.has_value_list {
imm_and_varargs += 1;
}
imm_and_varargs > 1
};
let maybe_args = if emit_args { ", args" } else { "" };
let defined_values = format!("{}{}", arg_names, maybe_args);
let tuple_or_value = if need_tuple {
format!("({})", defined_values)
} else {
defined_values
};
fmtln!(
fmt,
"let ({}, predicate) = if let crate::ir::InstructionData::{} {{",
arg_names,
"let {} = if let ir::InstructionData::{} {{",
tuple_or_value,
iform.name
);
fmt.indent(|fmt| {
// Fields are encoded directly.
for field in &iform.imm_fields {
@@ -69,47 +105,61 @@ fn unwrap_inst(
fmt.line("..");
fmt.outdented_line("} = pos.func.dfg[inst] {");
fmt.line("let func = &pos.func;");
if iform.has_value_list {
fmt.line("let args = args.as_slice(&func.dfg.value_lists);");
fmt.line("let args = args.as_slice(&pos.func.dfg.value_lists);");
} else if iform.num_value_operands == 1 {
fmt.line("let args = [arg];")
}
// Generate the values for the tuple.
fmt.line("(");
fmt.indent(|fmt| {
for (op_num, op) in inst.operands_in.iter().enumerate() {
let emit_one_value =
|fmt: &mut Formatter, needs_comma: bool, op_num: usize, op: &Operand| {
let comma = if needs_comma { "," } else { "" };
if op.is_immediate() {
let n = inst.imm_opnums.iter().position(|&i| i == op_num).unwrap();
fmtln!(fmt, "{},", iform.imm_fields[n].member);
fmtln!(fmt, "{}{}", iform.imm_fields[n].member, comma);
} else if op.is_value() {
let n = inst.value_opnums.iter().position(|&i| i == op_num).unwrap();
fmtln!(fmt, "func.dfg.resolve_aliases(args[{}]),", n);
fmtln!(fmt, "pos.func.dfg.resolve_aliases(args[{}]),", n);
} else if op.is_varargs() {
let n = inst.imm_opnums.iter().chain(inst.value_opnums.iter()).max().map(|n| n + 1).unwrap_or(0);
// We need to create a `Vec` here, as using a slice would result in a borrowck
// error later on.
fmtln!(fmt, "\
args.iter().skip({}).map(|&arg| func.dfg.resolve_aliases(arg)).collect::<Vec<_>>(),\
args.iter().skip({}).map(|&arg| pos.func.dfg.resolve_aliases(arg)).collect::<Vec<_>>(),\
", n);
} else {
// This is a value list argument.
assert!(iform.has_value_list);
}
}
};
// Evaluate the instruction predicate if any.
fmt.multi_line(
&apply
.inst_predicate_with_ctrl_typevar(format_registry, var_pool)
.rust_predicate(),
);
if need_tuple {
fmt.line("(");
fmt.indent(|fmt| {
for (op_num, op) in inst.operands_in.iter().enumerate() {
let needs_comma = emit_args || op_num + 1 < inst.operands_in.len();
emit_one_value(fmt, needs_comma, op_num, op);
}
if emit_args {
fmt.line("args");
}
});
fmt.line(")");
} else {
// Only one of these can be true at the same time, otherwise we'd need a tuple.
emit_one_value(fmt, false, 0, &inst.operands_in[0]);
if emit_args {
fmt.line("args");
}
}
fmt.outdented_line("} else {");
fmt.line(r#"unreachable!("bad instruction format")"#);
});
fmtln!(fmt, "};");
fmt.empty_line();
assert_eq!(inst.operands_in.len(), apply.args.len());
for (i, op) in inst.operands_in.iter().enumerate() {
@@ -385,21 +435,37 @@ fn emit_dst_inst(def: &Def, def_pool: &DefPool, var_pool: &VarPool, fmt: &mut Fo
/// `inst: Inst` is the variable to be replaced. It is pointed to by `pos: Cursor`.
/// `dfg: DataFlowGraph` is available and mutable.
fn gen_transform<'a>(
replace_inst: bool,
transform: &'a Transform,
format_registry: &FormatRegistry,
type_sets: &mut UniqueTable<'a, TypeSet>,
fmt: &mut Formatter,
) {
// Unwrap the source instruction, create local variables for the input variables.
let replace_inst = unwrap_inst(&transform, format_registry, fmt);
// Evaluate the instruction predicate if any.
let apply = &transform.def_pool.get(transform.src).apply;
// Emit any runtime checks; these will rebind `predicate` emitted by unwrap_inst().
let inst_predicate = apply
.inst_predicate_with_ctrl_typevar(format_registry, &transform.var_pool)
.rust_predicate("pos.func");
let has_extra_constraints = !transform.type_env.constraints.is_empty();
if has_extra_constraints {
// Extra constraints rely on the predicate being a variable that we can rebind as we add
// more constraint predicates.
fmt.multi_line(&format!("let predicate = {};", inst_predicate));
}
// Emit any runtime checks; these will rebind `predicate` emitted right above.
for constraint in &transform.type_env.constraints {
emit_runtime_typecheck(constraint, type_sets, fmt);
}
// Guard the actual expansion by `predicate`.
if has_extra_constraints {
fmt.line("if predicate {");
} else {
fmt.multi_line(&format!("if {} {{", inst_predicate))
}
fmt.indent(|fmt| {
// Emit any constants that must be created before use.
for (name, value) in transform.const_pool.iter() {
@@ -525,8 +591,17 @@ fn gen_transform_group<'a>(
for camel_name in sorted_inst_names {
fmtln!(fmt, "ir::Opcode::{} => {{", camel_name);
fmt.indent(|fmt| {
for transform in inst_to_transforms.get(camel_name).unwrap() {
gen_transform(transform, format_registry, type_sets, fmt);
let transforms = inst_to_transforms.get(camel_name).unwrap();
// Unwrap the source instruction, create local variables for the input variables.
let replace_inst = unwrap_inst(&transforms[0], format_registry, fmt);
fmt.empty_line();
for (i, transform) in transforms.into_iter().enumerate() {
if i > 0 {
fmt.empty_line();
}
gen_transform(replace_inst, transform, format_registry, type_sets, fmt);
}
});
fmtln!(fmt, "}");
@@ -540,7 +615,7 @@ fn gen_transform_group<'a>(
for (inst_camel_name, func_name) in sorted_custom_legalizes {
fmtln!(fmt, "ir::Opcode::{} => {{", inst_camel_name);
fmt.indent(|fmt| {
fmtln!(fmt, "{}(inst, pos.func, cfg, isa);", func_name);
fmtln!(fmt, "{}(inst, func, cfg, isa);", func_name);
fmt.line("return true;");
});
fmtln!(fmt, "}");
@@ -558,7 +633,7 @@ fn gen_transform_group<'a>(
match &group.chain_with {
Some(group_id) => fmtln!(
fmt,
"{}(inst, pos.func, cfg, isa)",
"{}(inst, func, cfg, isa)",
transform_groups.get(*group_id).rust_name()
),
None => fmt.line("false"),

View File

@@ -212,7 +212,9 @@ pub(crate) fn define(shared: &mut SharedDefinitions, x86_instructions: &Instruct
);
// Population count for baseline x86_64
let qv1 = var("qv1");
let x = var("x");
let r = var("r");
let qv3 = var("qv3");
let qv4 = var("qv4");
let qv5 = var("qv5");
@@ -226,7 +228,6 @@ pub(crate) fn define(shared: &mut SharedDefinitions, x86_instructions: &Instruct
let qv13 = var("qv13");
let qv14 = var("qv14");
let qv15 = var("qv15");
let qv16 = var("qv16");
let qc77 = var("qc77");
#[allow(non_snake_case)]
let qc0F = var("qc0F");
@@ -235,12 +236,12 @@ pub(crate) fn define(shared: &mut SharedDefinitions, x86_instructions: &Instruct
let imm64_1 = Literal::constant(&imm.imm64, 1);
let imm64_4 = Literal::constant(&imm.imm64, 4);
group.legalize(
def!(qv16 = popcnt.I64(qv1)),
def!(r = popcnt.I64(x)),
vec![
def!(qv3 = ushr_imm(qv1, imm64_1)),
def!(qv3 = ushr_imm(x, imm64_1)),
def!(qc77 = iconst(Literal::constant(&imm.imm64, 0x7777777777777777))),
def!(qv4 = band(qv3, qc77)),
def!(qv5 = isub(qv1, qv4)),
def!(qv5 = isub(x, qv4)),
def!(qv6 = ushr_imm(qv4, imm64_1)),
def!(qv7 = band(qv6, qc77)),
def!(qv8 = isub(qv5, qv7)),
@@ -253,11 +254,10 @@ pub(crate) fn define(shared: &mut SharedDefinitions, x86_instructions: &Instruct
def!(qv14 = band(qv13, qc0F)),
def!(qc01 = iconst(Literal::constant(&imm.imm64, 0x0101010101010101))),
def!(qv15 = imul(qv14, qc01)),
def!(qv16 = ushr_imm(qv15, Literal::constant(&imm.imm64, 56))),
def!(r = ushr_imm(qv15, Literal::constant(&imm.imm64, 56))),
],
);
let lv1 = var("lv1");
let lv3 = var("lv3");
let lv4 = var("lv4");
let lv5 = var("lv5");
@@ -271,19 +271,18 @@ pub(crate) fn define(shared: &mut SharedDefinitions, x86_instructions: &Instruct
let lv13 = var("lv13");
let lv14 = var("lv14");
let lv15 = var("lv15");
let lv16 = var("lv16");
let lc77 = var("lc77");
#[allow(non_snake_case)]
let lc0F = var("lc0F");
let lc01 = var("lc01");
group.legalize(
def!(lv16 = popcnt.I32(lv1)),
def!(r = popcnt.I32(x)),
vec![
def!(lv3 = ushr_imm(lv1, imm64_1)),
def!(lv3 = ushr_imm(x, imm64_1)),
def!(lc77 = iconst(Literal::constant(&imm.imm64, 0x77777777))),
def!(lv4 = band(lv3, lc77)),
def!(lv5 = isub(lv1, lv4)),
def!(lv5 = isub(x, lv4)),
def!(lv6 = ushr_imm(lv4, imm64_1)),
def!(lv7 = band(lv6, lc77)),
def!(lv8 = isub(lv5, lv7)),
@@ -296,7 +295,7 @@ pub(crate) fn define(shared: &mut SharedDefinitions, x86_instructions: &Instruct
def!(lv14 = band(lv13, lc0F)),
def!(lc01 = iconst(Literal::constant(&imm.imm64, 0x01010101))),
def!(lv15 = imul(lv14, lc01)),
def!(lv16 = ushr_imm(lv15, Literal::constant(&imm.imm64, 24))),
def!(r = ushr_imm(lv15, Literal::constant(&imm.imm64, 24))),
],
);