[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.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 { impl TypePredicateNode {
fn rust_predicate(&self) -> String { fn rust_predicate(&self, func_str: &str) -> String {
match self { match self {
TypePredicateNode::TypeVarCheck(index, value_type_name) => format!( TypePredicateNode::TypeVarCheck(index, value_type_name) => format!(
"func.dfg.value_type(args[{}]) == {}", "{}.dfg.value_type(args[{}]) == {}",
index, value_type_name func_str, index, value_type_name
), ),
TypePredicateNode::CtrlTypeVarCheck(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 { impl InstructionPredicateNode {
fn rust_predicate(&self) -> String { fn rust_predicate(&self, func_str: &str) -> String {
match self { match self {
InstructionPredicateNode::FormatPredicate(node) => node.rust_predicate(), InstructionPredicateNode::FormatPredicate(node) => node.rust_predicate(),
InstructionPredicateNode::TypePredicate(node) => node.rust_predicate(), InstructionPredicateNode::TypePredicate(node) => node.rust_predicate(func_str),
InstructionPredicateNode::And(nodes) => nodes InstructionPredicateNode::And(nodes) => nodes
.iter() .iter()
.map(|x| x.rust_predicate()) .map(|x| x.rust_predicate(func_str))
.collect::<Vec<_>>() .collect::<Vec<_>>()
.join(" && "), .join(" && "),
InstructionPredicateNode::Or(nodes) => nodes InstructionPredicateNode::Or(nodes) => nodes
.iter() .iter()
.map(|x| x.rust_predicate()) .map(|x| x.rust_predicate(func_str))
.collect::<Vec<_>>() .collect::<Vec<_>>()
.join(" || "), .join(" || "),
} }
@@ -1169,9 +1169,9 @@ impl InstructionPredicate {
self self
} }
pub fn rust_predicate(&self) -> String { pub fn rust_predicate(&self, func_str: &str) -> String {
match &self.node { match &self.node {
Some(root) => root.rust_predicate(), Some(root) => root.rust_predicate(func_str),
None => "true".into(), 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) { fn emit_instp(instp: &InstructionPredicate, has_func: bool, fmt: &mut Formatter) {
if instp.is_type_predicate() { if instp.is_type_predicate() {
fmt.line("let args = inst.arguments(&func.dfg.value_lists);"); fmt.line("let args = inst.arguments(&func.dfg.value_lists);");
fmt.line(instp.rust_predicate()); fmt.line(instp.rust_predicate("func"));
return; return;
} }
@@ -127,7 +127,7 @@ fn emit_instp(instp: &InstructionPredicate, has_func: bool, fmt: &mut Formatter)
// Silence dead argument. // Silence dead argument.
fmt.line("let _ = func;"); fmt.line("let _ = func;");
} }
fmtln!(fmt, "return {};", instp.rust_predicate()); fmtln!(fmt, "return {};", instp.rust_predicate("func"));
}); });
fmtln!(fmt, "}"); 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::formats::FormatRegistry;
use crate::cdsl::isa::TargetIsa; use crate::cdsl::isa::TargetIsa;
use crate::cdsl::operands::Operand;
use crate::cdsl::type_inference::Constraint; use crate::cdsl::type_inference::Constraint;
use crate::cdsl::typevar::{TypeSet, TypeVar}; use crate::cdsl::typevar::{TypeSet, TypeVar};
use crate::cdsl::xform::{Transform, TransformGroup, TransformGroups}; use crate::cdsl::xform::{Transform, TransformGroup, TransformGroups};
@@ -34,7 +35,7 @@ fn unwrap_inst(
let iform = format_registry.get(inst.format); let iform = format_registry.get(inst.format);
fmt.comment(format!( fmt.comment(format!(
"Unwrap {}", "Unwrap fields from instruction format {}",
def.to_comment_string(&transform.var_pool) def.to_comment_string(&transform.var_pool)
)); ));
@@ -42,19 +43,54 @@ fn unwrap_inst(
let arg_names = apply let arg_names = apply
.args .args
.iter() .iter()
.map(|arg| match arg.maybe_var() { .enumerate()
Some(var_index) => var_pool.get(var_index).name.as_ref(), .map(|(arg_num, arg)| match &arg {
None => "_", 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<_>>() .collect::<Vec<_>>()
.join(", "); .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!( fmtln!(
fmt, fmt,
"let ({}, predicate) = if let crate::ir::InstructionData::{} {{", "let {} = if let ir::InstructionData::{} {{",
arg_names, tuple_or_value,
iform.name iform.name
); );
fmt.indent(|fmt| { fmt.indent(|fmt| {
// Fields are encoded directly. // Fields are encoded directly.
for field in &iform.imm_fields { for field in &iform.imm_fields {
@@ -69,47 +105,61 @@ fn unwrap_inst(
fmt.line(".."); fmt.line("..");
fmt.outdented_line("} = pos.func.dfg[inst] {"); fmt.outdented_line("} = pos.func.dfg[inst] {");
fmt.line("let func = &pos.func;");
if iform.has_value_list { 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 { } else if iform.num_value_operands == 1 {
fmt.line("let args = [arg];") fmt.line("let args = [arg];")
} }
// Generate the values for the tuple. // Generate the values for the tuple.
fmt.line("("); let emit_one_value =
fmt.indent(|fmt| { |fmt: &mut Formatter, needs_comma: bool, op_num: usize, op: &Operand| {
for (op_num, op) in inst.operands_in.iter().enumerate() { let comma = if needs_comma { "," } else { "" };
if op.is_immediate() { if op.is_immediate() {
let n = inst.imm_opnums.iter().position(|&i| i == op_num).unwrap(); 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() { } else if op.is_value() {
let n = inst.value_opnums.iter().position(|&i| i == op_num).unwrap(); 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() { } else if op.is_varargs() {
let n = inst.imm_opnums.iter().chain(inst.value_opnums.iter()).max().map(|n| n + 1).unwrap_or(0); 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 // We need to create a `Vec` here, as using a slice would result in a borrowck
// error later on. // error later on.
fmtln!(fmt, "\ 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); ", n);
} else {
// This is a value list argument.
assert!(iform.has_value_list);
} }
} };
// Evaluate the instruction predicate if any. if need_tuple {
fmt.multi_line( fmt.line("(");
&apply fmt.indent(|fmt| {
.inst_predicate_with_ctrl_typevar(format_registry, var_pool) for (op_num, op) in inst.operands_in.iter().enumerate() {
.rust_predicate(), let needs_comma = emit_args || op_num + 1 < inst.operands_in.len();
); emit_one_value(fmt, needs_comma, op_num, op);
}); }
fmt.line(")"); 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.outdented_line("} else {");
fmt.line(r#"unreachable!("bad instruction format")"#); fmt.line(r#"unreachable!("bad instruction format")"#);
}); });
fmtln!(fmt, "};"); fmtln!(fmt, "};");
fmt.empty_line();
assert_eq!(inst.operands_in.len(), apply.args.len()); assert_eq!(inst.operands_in.len(), apply.args.len());
for (i, op) in inst.operands_in.iter().enumerate() { 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`. /// `inst: Inst` is the variable to be replaced. It is pointed to by `pos: Cursor`.
/// `dfg: DataFlowGraph` is available and mutable. /// `dfg: DataFlowGraph` is available and mutable.
fn gen_transform<'a>( fn gen_transform<'a>(
replace_inst: bool,
transform: &'a Transform, transform: &'a Transform,
format_registry: &FormatRegistry, format_registry: &FormatRegistry,
type_sets: &mut UniqueTable<'a, TypeSet>, type_sets: &mut UniqueTable<'a, TypeSet>,
fmt: &mut Formatter, fmt: &mut Formatter,
) { ) {
// Unwrap the source instruction, create local variables for the input variables. // Evaluate the instruction predicate if any.
let replace_inst = unwrap_inst(&transform, format_registry, fmt); 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 { for constraint in &transform.type_env.constraints {
emit_runtime_typecheck(constraint, type_sets, fmt); emit_runtime_typecheck(constraint, type_sets, fmt);
} }
// Guard the actual expansion by `predicate`. // Guard the actual expansion by `predicate`.
fmt.line("if predicate {"); if has_extra_constraints {
fmt.line("if predicate {");
} else {
fmt.multi_line(&format!("if {} {{", inst_predicate))
}
fmt.indent(|fmt| { fmt.indent(|fmt| {
// Emit any constants that must be created before use. // Emit any constants that must be created before use.
for (name, value) in transform.const_pool.iter() { for (name, value) in transform.const_pool.iter() {
@@ -525,8 +591,17 @@ fn gen_transform_group<'a>(
for camel_name in sorted_inst_names { for camel_name in sorted_inst_names {
fmtln!(fmt, "ir::Opcode::{} => {{", camel_name); fmtln!(fmt, "ir::Opcode::{} => {{", camel_name);
fmt.indent(|fmt| { fmt.indent(|fmt| {
for transform in inst_to_transforms.get(camel_name).unwrap() { let transforms = inst_to_transforms.get(camel_name).unwrap();
gen_transform(transform, format_registry, type_sets, fmt);
// 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, "}"); fmtln!(fmt, "}");
@@ -540,7 +615,7 @@ fn gen_transform_group<'a>(
for (inst_camel_name, func_name) in sorted_custom_legalizes { for (inst_camel_name, func_name) in sorted_custom_legalizes {
fmtln!(fmt, "ir::Opcode::{} => {{", inst_camel_name); fmtln!(fmt, "ir::Opcode::{} => {{", inst_camel_name);
fmt.indent(|fmt| { fmt.indent(|fmt| {
fmtln!(fmt, "{}(inst, pos.func, cfg, isa);", func_name); fmtln!(fmt, "{}(inst, func, cfg, isa);", func_name);
fmt.line("return true;"); fmt.line("return true;");
}); });
fmtln!(fmt, "}"); fmtln!(fmt, "}");
@@ -558,7 +633,7 @@ fn gen_transform_group<'a>(
match &group.chain_with { match &group.chain_with {
Some(group_id) => fmtln!( Some(group_id) => fmtln!(
fmt, fmt,
"{}(inst, pos.func, cfg, isa)", "{}(inst, func, cfg, isa)",
transform_groups.get(*group_id).rust_name() transform_groups.get(*group_id).rust_name()
), ),
None => fmt.line("false"), 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 // Population count for baseline x86_64
let qv1 = var("qv1"); let x = var("x");
let r = var("r");
let qv3 = var("qv3"); let qv3 = var("qv3");
let qv4 = var("qv4"); let qv4 = var("qv4");
let qv5 = var("qv5"); let qv5 = var("qv5");
@@ -226,7 +228,6 @@ pub(crate) fn define(shared: &mut SharedDefinitions, x86_instructions: &Instruct
let qv13 = var("qv13"); let qv13 = var("qv13");
let qv14 = var("qv14"); let qv14 = var("qv14");
let qv15 = var("qv15"); let qv15 = var("qv15");
let qv16 = var("qv16");
let qc77 = var("qc77"); let qc77 = var("qc77");
#[allow(non_snake_case)] #[allow(non_snake_case)]
let qc0F = var("qc0F"); 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_1 = Literal::constant(&imm.imm64, 1);
let imm64_4 = Literal::constant(&imm.imm64, 4); let imm64_4 = Literal::constant(&imm.imm64, 4);
group.legalize( group.legalize(
def!(qv16 = popcnt.I64(qv1)), def!(r = popcnt.I64(x)),
vec![ vec![
def!(qv3 = ushr_imm(qv1, imm64_1)), def!(qv3 = ushr_imm(x, imm64_1)),
def!(qc77 = iconst(Literal::constant(&imm.imm64, 0x7777777777777777))), def!(qc77 = iconst(Literal::constant(&imm.imm64, 0x7777777777777777))),
def!(qv4 = band(qv3, qc77)), def!(qv4 = band(qv3, qc77)),
def!(qv5 = isub(qv1, qv4)), def!(qv5 = isub(x, qv4)),
def!(qv6 = ushr_imm(qv4, imm64_1)), def!(qv6 = ushr_imm(qv4, imm64_1)),
def!(qv7 = band(qv6, qc77)), def!(qv7 = band(qv6, qc77)),
def!(qv8 = isub(qv5, qv7)), 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!(qv14 = band(qv13, qc0F)),
def!(qc01 = iconst(Literal::constant(&imm.imm64, 0x0101010101010101))), def!(qc01 = iconst(Literal::constant(&imm.imm64, 0x0101010101010101))),
def!(qv15 = imul(qv14, qc01)), 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 lv3 = var("lv3");
let lv4 = var("lv4"); let lv4 = var("lv4");
let lv5 = var("lv5"); let lv5 = var("lv5");
@@ -271,19 +271,18 @@ pub(crate) fn define(shared: &mut SharedDefinitions, x86_instructions: &Instruct
let lv13 = var("lv13"); let lv13 = var("lv13");
let lv14 = var("lv14"); let lv14 = var("lv14");
let lv15 = var("lv15"); let lv15 = var("lv15");
let lv16 = var("lv16");
let lc77 = var("lc77"); let lc77 = var("lc77");
#[allow(non_snake_case)] #[allow(non_snake_case)]
let lc0F = var("lc0F"); let lc0F = var("lc0F");
let lc01 = var("lc01"); let lc01 = var("lc01");
group.legalize( group.legalize(
def!(lv16 = popcnt.I32(lv1)), def!(r = popcnt.I32(x)),
vec![ vec![
def!(lv3 = ushr_imm(lv1, imm64_1)), def!(lv3 = ushr_imm(x, imm64_1)),
def!(lc77 = iconst(Literal::constant(&imm.imm64, 0x77777777))), def!(lc77 = iconst(Literal::constant(&imm.imm64, 0x77777777))),
def!(lv4 = band(lv3, lc77)), def!(lv4 = band(lv3, lc77)),
def!(lv5 = isub(lv1, lv4)), def!(lv5 = isub(x, lv4)),
def!(lv6 = ushr_imm(lv4, imm64_1)), def!(lv6 = ushr_imm(lv4, imm64_1)),
def!(lv7 = band(lv6, lc77)), def!(lv7 = band(lv6, lc77)),
def!(lv8 = isub(lv5, lv7)), 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!(lv14 = band(lv13, lc0F)),
def!(lc01 = iconst(Literal::constant(&imm.imm64, 0x01010101))), def!(lc01 = iconst(Literal::constant(&imm.imm64, 0x01010101))),
def!(lv15 = imul(lv14, lc01)), 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))),
], ],
); );