Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cranelift-frontend: Specify stack map behavior when defining variables #8937

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
155 changes: 129 additions & 26 deletions cranelift/frontend/src/frontend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ pub struct FunctionBuilderContext {
ssa: SSABuilder,
status: SecondaryMap<Block, BlockStatus>,
types: SecondaryMap<Variable, Type>,
needs_stack_map: EntitySet<Value>,
stack_map_vars: EntitySet<Variable>,
stack_map_values: EntitySet<Value>,
dfs: Dfs,
}

Expand Down Expand Up @@ -382,9 +383,15 @@ impl<'a> FunctionBuilder<'a> {
self.handle_ssa_side_effects(side_effects);
}

/// Declares the type of a variable, so that it can be used later (by calling
/// [`FunctionBuilder::use_var`]). This function will return an error if the variable
/// has been previously declared.
/// Declares the type of a variable.
///
/// This allows the variable to be used later (by calling
/// [`FunctionBuilder::use_var`]).
///
/// # Errors
///
/// This function will return an error if the variable has been previously
/// declared.
pub fn try_declare_var(&mut self, var: Variable, ty: Type) -> Result<(), DeclareVariableError> {
if self.func_ctx.types[var] != types::INVALID {
return Err(DeclareVariableError::DeclaredMultipleTimes(var));
Expand All @@ -393,13 +400,37 @@ impl<'a> FunctionBuilder<'a> {
Ok(())
}

/// In order to use a variable (by calling [`FunctionBuilder::use_var`]), you need
/// to first declare its type with this method.
/// Declares the type of a variable, panicking if it is already declared.
///
/// # Panics
///
/// Panics if the variable has already been declared.
pub fn declare_var(&mut self, var: Variable, ty: Type) {
self.try_declare_var(var, ty)
.unwrap_or_else(|_| panic!("the variable {:?} has been declared multiple times", var))
}

/// Declare that all uses of the given variable must be included in stack
/// map metadata.
///
/// All values that are uses of this variable will be spilled to the stack
/// before each safepoint and their location on the stack included in stack
/// maps. Stack maps allow the garbage collector to identify the on-stack GC
/// roots.
///
/// This does not affect any pre-existing uses of the variable.
///
/// # Panics
///
/// Panics if the variable's type is larger than 16 bytes or if this
/// variable has not been declared yet.
pub fn declare_var_needs_stack_map(&mut self, var: Variable) {
let ty = self.func_ctx.types[var];
assert!(ty != types::INVALID);
assert!(ty.bytes() <= 16);
self.func_ctx.stack_map_vars.insert(var);
}

/// Returns the Cranelift IR necessary to use a previously defined user
/// variable, returning an error if this is not possible.
pub fn try_use_var(&mut self, var: Variable) -> Result<Value, UseVariableError> {
Expand All @@ -426,6 +457,13 @@ impl<'a> FunctionBuilder<'a> {
.use_var(self.func, var, ty, self.position.unwrap())
};
self.handle_ssa_side_effects(side_effects);

// If the variable was declared as needing stack maps, then propagate
// that requirement to all values derived from using the variable.
if self.func_ctx.stack_map_vars.contains(var) {
self.declare_value_needs_stack_map(val);
}

Ok(val)
}

Expand Down Expand Up @@ -513,13 +551,13 @@ impl<'a> FunctionBuilder<'a> {
/// # Panics
///
/// Panics if `val` is larger than 16 bytes.
pub fn declare_needs_stack_map(&mut self, val: Value) {
pub fn declare_value_needs_stack_map(&mut self, val: Value) {
// We rely on these properties in `insert_safepoint_spills`.
let size = self.func.dfg.value_type(val).bytes();
assert!(size <= 16);
assert!(size.is_power_of_two());

self.func_ctx.needs_stack_map.insert(val);
self.func_ctx.stack_map_values.insert(val);
}

/// Creates a jump table in the function, to be used by [`br_table`](InstBuilder::br_table) instructions.
Expand Down Expand Up @@ -738,7 +776,7 @@ impl<'a> FunctionBuilder<'a> {
// instruction to the live set. This includes branch arguments,
// as mentioned above.
for val in self.func.dfg.inst_values(inst) {
if self.func_ctx.needs_stack_map.contains(val) {
if self.func_ctx.stack_map_values.contains(val) {
live.insert(val);
}
}
Expand Down Expand Up @@ -844,7 +882,7 @@ impl<'a> FunctionBuilder<'a> {
}
}

if !self.func_ctx.needs_stack_map.is_empty() {
if !self.func_ctx.stack_map_values.is_empty() {
self.insert_safepoint_spills();
}

Expand Down Expand Up @@ -2110,8 +2148,8 @@ block0:
builder.append_block_params_for_function_params(block0);
let a = builder.func.dfg.block_params(block0)[0];
let b = builder.func.dfg.block_params(block0)[1];
builder.declare_needs_stack_map(a);
builder.declare_needs_stack_map(b);
builder.declare_value_needs_stack_map(a);
builder.declare_value_needs_stack_map(b);
builder.switch_to_block(block0);
builder.ins().call(func_ref, &[a]);
builder.ins().jump(block0, &[a, b]);
Expand Down Expand Up @@ -2182,13 +2220,13 @@ block0(v0: i32, v1: i32):
builder.append_block_params_for_function_params(block0);
builder.switch_to_block(block0);
let v0 = builder.ins().iconst(ir::types::I32, 0);
builder.declare_needs_stack_map(v0);
builder.declare_value_needs_stack_map(v0);
let v1 = builder.ins().iconst(ir::types::I32, 1);
builder.declare_needs_stack_map(v1);
builder.declare_value_needs_stack_map(v1);
let v2 = builder.ins().iconst(ir::types::I32, 2);
builder.declare_needs_stack_map(v2);
builder.declare_value_needs_stack_map(v2);
let v3 = builder.ins().iconst(ir::types::I32, 3);
builder.declare_needs_stack_map(v3);
builder.declare_value_needs_stack_map(v3);
builder.ins().call(func_ref, &[v0]);
builder.ins().call(func_ref, &[v0]);
builder.ins().call(func_ref, &[v1]);
Expand Down Expand Up @@ -2286,7 +2324,7 @@ block0:

builder.switch_to_block(block1);
let v1 = builder.ins().iconst(ir::types::I64, 0x12345678);
builder.declare_needs_stack_map(v1);
builder.declare_value_needs_stack_map(v1);
builder.ins().jump(block3, &[]);

builder.switch_to_block(block2);
Expand Down Expand Up @@ -2380,7 +2418,7 @@ block3:
builder.switch_to_block(block0);
let v0 = builder.func.dfg.block_params(block0)[0];
let v1 = builder.ins().iconst(ir::types::I64, 0x12345678);
builder.declare_needs_stack_map(v1);
builder.declare_value_needs_stack_map(v1);
builder.ins().brif(v0, block1, &[], block2, &[]);

builder.switch_to_block(block1);
Expand Down Expand Up @@ -2457,7 +2495,7 @@ block2:
builder.switch_to_block(block0);
let v0 = builder.func.dfg.block_params(block0)[0];
let v1 = builder.ins().iconst(ir::types::I64, 0x12345678);
builder.declare_needs_stack_map(v1);
builder.declare_value_needs_stack_map(v1);
builder.ins().brif(v0, block1, &[], block2, &[]);

builder.switch_to_block(block1);
Expand Down Expand Up @@ -2544,7 +2582,7 @@ block2:
builder.switch_to_block(block0);
let v0 = builder.func.dfg.block_params(block0)[0];
let v1 = builder.ins().iconst(ir::types::I64, 0x12345678);
builder.declare_needs_stack_map(v1);
builder.declare_value_needs_stack_map(v1);
builder.ins().brif(v0, block1, &[], block2, &[]);

builder.switch_to_block(block1);
Expand Down Expand Up @@ -2619,7 +2657,7 @@ block2:
builder.switch_to_block(block0);
let v0 = builder.func.dfg.block_params(block0)[0];
let v1 = builder.ins().iconst(ir::types::I64, 0x12345678);
builder.declare_needs_stack_map(v1);
builder.declare_value_needs_stack_map(v1);
builder.ins().brif(v0, block1, &[], block2, &[]);

builder.switch_to_block(block1);
Expand Down Expand Up @@ -2713,17 +2751,17 @@ block2:

builder.switch_to_block(block1);
let v1 = builder.ins().iconst(ir::types::I64, 1);
builder.declare_needs_stack_map(v1);
builder.declare_value_needs_stack_map(v1);
let v2 = builder.ins().iconst(ir::types::I64, 2);
builder.declare_needs_stack_map(v2);
builder.declare_value_needs_stack_map(v2);
builder.ins().call(func_ref, &[]);
builder.ins().jump(block3, &[v1, v2]);

builder.switch_to_block(block2);
let v3 = builder.ins().iconst(ir::types::I64, 3);
builder.declare_needs_stack_map(v3);
builder.declare_value_needs_stack_map(v3);
let v4 = builder.ins().iconst(ir::types::I64, 4);
builder.declare_needs_stack_map(v4);
builder.declare_value_needs_stack_map(v4);
builder.ins().call(func_ref, &[]);
builder.ins().jump(block3, &[v3, v3]);

Expand Down Expand Up @@ -2826,7 +2864,7 @@ block3:
builder.switch_to_block(block0);
let params = builder.func.dfg.block_params(block0).to_vec();
for val in &params {
builder.declare_needs_stack_map(*val);
builder.declare_value_needs_stack_map(*val);
}
builder.ins().call(func_ref, &[]);
builder.ins().return_(&params);
Expand Down Expand Up @@ -2859,6 +2897,71 @@ block0(v0: i8, v1: i16, v2: i32, v3: i64, v4: i128, v5: f32, v6: f64, v7: i8x16,
stack_store v8, ss4+32
call fn0(), stack_map=[i8 @ ss0+0, i16 @ ss1+0, i32 @ ss2+0, f32 @ ss2+4, i64 @ ss3+0, f64 @ ss3+8, i128 @ ss4+0, i8x16 @ ss4+16, i16x8 @ ss4+32]
return v0, v1, v2, v3, v4, v5, v6, v7, v8
}
"#
.trim()
);
}

#[test]
fn var_needs_stack_map() {
let mut sig = Signature::new(CallConv::SystemV);
sig.params
.push(AbiParam::new(cranelift_codegen::ir::types::I32));
sig.returns
.push(AbiParam::new(cranelift_codegen::ir::types::I32));

let mut fn_ctx = FunctionBuilderContext::new();
let mut func = Function::with_name_signature(UserFuncName::testcase("sample"), sig);
let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);

let var = Variable::from_u32(0);
builder.declare_var(var, cranelift_codegen::ir::types::I32);
builder.declare_var_needs_stack_map(var);

let name = builder
.func
.declare_imported_user_function(ir::UserExternalName {
namespace: 0,
index: 0,
});
let signature = builder
.func
.import_signature(Signature::new(CallConv::SystemV));
let func_ref = builder.import_function(ir::ExtFuncData {
name: ir::ExternalName::user(name),
signature,
colocated: true,
});

let block0 = builder.create_block();
builder.append_block_params_for_function_params(block0);
builder.switch_to_block(block0);

let arg = builder.func.dfg.block_params(block0)[0];
builder.def_var(var, arg);

builder.ins().call(func_ref, &[]);

let val = builder.use_var(var);
builder.ins().return_(&[val]);

builder.seal_all_blocks();
builder.finalize();

eprintln!("Actual = {}", func.display());
assert_eq!(
func.display().to_string().trim(),
r#"
function %sample(i32) -> i32 system_v {
ss0 = explicit_slot 4, align = 4
sig0 = () system_v
fn0 = colocated u0:0 sig0

block0(v0: i32):
stack_store v0, ss0
call fn0(), stack_map=[i32 @ ss0+0]
return v0
}
"#
.trim()
Expand Down
3 changes: 0 additions & 3 deletions cranelift/frontend/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,6 @@
//! Here is how you build the corresponding Cranelift IR function using [`FunctionBuilderContext`]:
//!
//! ```rust
//! extern crate cranelift_codegen;
//! extern crate cranelift_frontend;
//!
//! use cranelift_codegen::entity::EntityRef;
//! use cranelift_codegen::ir::types::*;
//! use cranelift_codegen::ir::{AbiParam, UserFuncName, Function, InstBuilder, Signature};
Expand Down