cranelift: Rework block instructions to use BlockCall (#5464)
Add a new type BlockCall that represents the pair of a block name with arguments to be passed to it. (The mnemonic here is that it looks a bit like a function call.) Rework the implementation of jump, brz, and brnz to use BlockCall instead of storing the block arguments as varargs in the instruction's ValueList. To ensure that we're processing block arguments from BlockCall values in instructions, three new functions have been introduced on DataFlowGraph that both sets of arguments: inst_values - returns an iterator that traverses values in the instruction and block arguments map_inst_values - applies a function to each value in the instruction and block arguments overwrite_inst_values - overwrite all values in an instruction and block arguments with values from the iterator Co-authored-by: Jamey Sharp <jamey@minilop.net>
This commit is contained in:
@@ -152,6 +152,10 @@ impl OperandKind {
|
||||
| OperandKindFields::VariableArgs => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn is_block(&self) -> bool {
|
||||
self.rust_type == "ir::BlockCall"
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<OperandKind> for &TypeVar {
|
||||
|
||||
@@ -158,8 +158,6 @@ fn gen_arguments_method(formats: &[&InstructionFormat], fmt: &mut Formatter, is_
|
||||
/// - `pub fn opcode(&self) -> Opcode`
|
||||
/// - `pub fn arguments(&self, &pool) -> &[Value]`
|
||||
/// - `pub fn arguments_mut(&mut self, &pool) -> &mut [Value]`
|
||||
/// - `pub fn value_list(&self) -> Option<ir::ValueList>`
|
||||
/// - `pub fn value_list_mut(&mut self) -> Option<&mut ir::ValueList>`
|
||||
/// - `pub fn eq(&self, &other: Self, &pool) -> bool`
|
||||
/// - `pub fn hash<H: Hasher>(&self, state: &mut H, &pool)`
|
||||
fn gen_instruction_data_impl(formats: &[&InstructionFormat], fmt: &mut Formatter) {
|
||||
@@ -213,51 +211,6 @@ fn gen_instruction_data_impl(formats: &[&InstructionFormat], fmt: &mut Formatter
|
||||
gen_arguments_method(formats, fmt, true);
|
||||
fmt.empty_line();
|
||||
|
||||
fmt.doc_comment(r#"
|
||||
The ValueList for the instruction.
|
||||
"#);
|
||||
fmt.line("pub fn value_list(&self) -> Option<ir::ValueList> {");
|
||||
fmt.indent(|fmt| {
|
||||
let mut m = Match::new("*self");
|
||||
|
||||
for format in formats {
|
||||
if format.has_value_list {
|
||||
m.arm(format!("Self::{}", format.name),
|
||||
vec!["args", ".."],
|
||||
"Some(args)".to_string());
|
||||
}
|
||||
}
|
||||
|
||||
m.arm_no_fields("_", "None");
|
||||
|
||||
fmt.add_match(m);
|
||||
});
|
||||
fmt.line("}");
|
||||
fmt.empty_line();
|
||||
|
||||
fmt.doc_comment(r#"
|
||||
A mutable reference to the ValueList for this instruction, if it
|
||||
has one.
|
||||
"#);
|
||||
fmt.line("pub fn value_list_mut(&mut self) -> Option<&mut ir::ValueList> {");
|
||||
fmt.indent(|fmt| {
|
||||
let mut m = Match::new("*self");
|
||||
|
||||
for format in formats {
|
||||
if format.has_value_list {
|
||||
m.arm(format!("Self::{}", format.name),
|
||||
vec!["ref mut args", ".."],
|
||||
"Some(args)".to_string());
|
||||
}
|
||||
}
|
||||
|
||||
m.arm_no_fields("_", "None");
|
||||
|
||||
fmt.add_match(m);
|
||||
});
|
||||
fmt.line("}");
|
||||
fmt.empty_line();
|
||||
|
||||
fmt.doc_comment(r#"
|
||||
Compare two `InstructionData` for equality.
|
||||
|
||||
@@ -898,12 +851,7 @@ fn gen_format_constructor(format: &InstructionFormat, fmt: &mut Formatter) {
|
||||
/// instruction reference itself for instructions that don't have results.
|
||||
fn gen_inst_builder(inst: &Instruction, format: &InstructionFormat, fmt: &mut Formatter) {
|
||||
// Construct method arguments.
|
||||
let mut args = vec![if format.has_value_list {
|
||||
"mut self"
|
||||
} else {
|
||||
"self"
|
||||
}
|
||||
.to_string()];
|
||||
let mut args = vec![String::new()];
|
||||
|
||||
let mut args_doc = Vec::new();
|
||||
let mut rets_doc = Vec::new();
|
||||
@@ -922,17 +870,39 @@ fn gen_inst_builder(inst: &Instruction, format: &InstructionFormat, fmt: &mut Fo
|
||||
|
||||
let mut tmpl_types = Vec::new();
|
||||
let mut into_args = Vec::new();
|
||||
let mut block_args = Vec::new();
|
||||
for op in &inst.operands_in {
|
||||
let t = if op.is_immediate() {
|
||||
let t = format!("T{}", tmpl_types.len() + 1);
|
||||
tmpl_types.push(format!("{}: Into<{}>", t, op.kind.rust_type));
|
||||
into_args.push(op.name);
|
||||
t
|
||||
if op.kind.is_block() {
|
||||
args.push(format!("{}_label: {}", op.name, "ir::Block"));
|
||||
args_doc.push(format!(
|
||||
"- {}_label: {}",
|
||||
op.name, "Destination basic block"
|
||||
));
|
||||
|
||||
args.push(format!("{}_args: {}", op.name, "&[Value]"));
|
||||
args_doc.push(format!("- {}_args: {}", op.name, "Block arguments"));
|
||||
|
||||
block_args.push(op);
|
||||
} else {
|
||||
op.kind.rust_type.to_string()
|
||||
};
|
||||
args.push(format!("{}: {}", op.name, t));
|
||||
args_doc.push(format!("- {}: {}", op.name, op.doc()));
|
||||
let t = if op.is_immediate() {
|
||||
let t = format!("T{}", tmpl_types.len() + 1);
|
||||
tmpl_types.push(format!("{}: Into<{}>", t, op.kind.rust_type));
|
||||
into_args.push(op.name);
|
||||
t
|
||||
} else {
|
||||
op.kind.rust_type.to_string()
|
||||
};
|
||||
args.push(format!("{}: {}", op.name, t));
|
||||
args_doc.push(format!("- {}: {}", op.name, op.doc()));
|
||||
}
|
||||
}
|
||||
|
||||
// We need to mutate `self` if this instruction accepts a value list, or will construct
|
||||
// BlockCall values.
|
||||
if format.has_value_list || !block_args.is_empty() {
|
||||
args[0].push_str("mut self");
|
||||
} else {
|
||||
args[0].push_str("self");
|
||||
}
|
||||
|
||||
for op in &inst.operands_out {
|
||||
@@ -981,10 +951,19 @@ fn gen_inst_builder(inst: &Instruction, format: &InstructionFormat, fmt: &mut Fo
|
||||
fmtln!(fmt, "fn {} {{", proto);
|
||||
fmt.indent(|fmt| {
|
||||
// Convert all of the `Into<>` arguments.
|
||||
for arg in &into_args {
|
||||
for arg in into_args {
|
||||
fmtln!(fmt, "let {} = {}.into();", arg, arg);
|
||||
}
|
||||
|
||||
// Convert block references
|
||||
for op in block_args {
|
||||
fmtln!(
|
||||
fmt,
|
||||
"let {0} = self.data_flow_graph_mut().block_call({0}_label, {0}_args);",
|
||||
op.name
|
||||
);
|
||||
}
|
||||
|
||||
// Arguments for instruction constructor.
|
||||
let first_arg = format!("Opcode::{}", inst.camel_name);
|
||||
let mut args = vec![first_arg.as_str()];
|
||||
|
||||
@@ -11,9 +11,13 @@ fn new(format_field_name: &'static str, rust_type: &'static str, doc: &'static s
|
||||
}
|
||||
|
||||
pub(crate) struct EntityRefs {
|
||||
/// A reference to a basic block in the same function, with its arguments provided.
|
||||
/// This is primarily used in control flow instructions.
|
||||
pub(crate) block_call: OperandKind,
|
||||
|
||||
/// A reference to a basic block in the same function.
|
||||
/// This is primarliy used in control flow instructions.
|
||||
pub(crate) block: OperandKind,
|
||||
/// This is primarily used in control flow instructions.
|
||||
pub(crate) label: OperandKind,
|
||||
|
||||
/// A reference to a stack slot declared in the function preamble.
|
||||
pub(crate) stack_slot: OperandKind,
|
||||
@@ -45,11 +49,18 @@ pub(crate) struct EntityRefs {
|
||||
impl EntityRefs {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
block: new(
|
||||
block_call: new(
|
||||
"destination",
|
||||
"ir::BlockCall",
|
||||
"a basic block in the same function, with its arguments provided.",
|
||||
),
|
||||
|
||||
label: new(
|
||||
"destination",
|
||||
"ir::Block",
|
||||
"a basic block in the same function.",
|
||||
),
|
||||
|
||||
stack_slot: new("stack_slot", "ir::StackSlot", "A stack slot"),
|
||||
|
||||
dynamic_stack_slot: new(
|
||||
|
||||
@@ -111,17 +111,16 @@ impl Formats {
|
||||
.value()
|
||||
.build(),
|
||||
|
||||
jump: Builder::new("Jump").imm(&entities.block).varargs().build(),
|
||||
jump: Builder::new("Jump").imm(&entities.block_call).build(),
|
||||
|
||||
branch: Builder::new("Branch")
|
||||
.value()
|
||||
.imm(&entities.block)
|
||||
.varargs()
|
||||
.imm(&entities.block_call)
|
||||
.build(),
|
||||
|
||||
branch_table: Builder::new("BranchTable")
|
||||
.value()
|
||||
.imm(&entities.block)
|
||||
.imm(&entities.label)
|
||||
.imm(&entities.jump_table)
|
||||
.build(),
|
||||
|
||||
|
||||
@@ -17,8 +17,9 @@ fn define_control_flow(
|
||||
imm: &Immediates,
|
||||
entities: &EntityRefs,
|
||||
) {
|
||||
let block = &Operand::new("block", &entities.block).with_doc("Destination basic block");
|
||||
let args = &Operand::new("args", &entities.varargs).with_doc("block arguments");
|
||||
let block_call = &Operand::new("block_call", &entities.block_call)
|
||||
.with_doc("Destination basic block, with its arguments provided");
|
||||
let label = &Operand::new("label", &entities.label).with_doc("Destination basic block");
|
||||
|
||||
ig.push(
|
||||
Inst::new(
|
||||
@@ -32,7 +33,7 @@ fn define_control_flow(
|
||||
"#,
|
||||
&formats.jump,
|
||||
)
|
||||
.operands_in(vec![block, args])
|
||||
.operands_in(vec![block_call])
|
||||
.is_terminator(true)
|
||||
.is_branch(true),
|
||||
);
|
||||
@@ -56,7 +57,7 @@ fn define_control_flow(
|
||||
"#,
|
||||
&formats.branch,
|
||||
)
|
||||
.operands_in(vec![c, block, args])
|
||||
.operands_in(vec![c, block_call])
|
||||
.is_branch(true),
|
||||
);
|
||||
|
||||
@@ -70,7 +71,7 @@ fn define_control_flow(
|
||||
"#,
|
||||
&formats.branch,
|
||||
)
|
||||
.operands_in(vec![c, block, args])
|
||||
.operands_in(vec![c, block_call])
|
||||
.is_branch(true),
|
||||
);
|
||||
}
|
||||
@@ -105,7 +106,7 @@ fn define_control_flow(
|
||||
"#,
|
||||
&formats.branch_table,
|
||||
)
|
||||
.operands_in(vec![x, block, JT])
|
||||
.operands_in(vec![x, label, JT])
|
||||
.is_terminator(true)
|
||||
.is_branch(true),
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user