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

Resolve aliases before inserting values into the live set #8945

Merged
merged 3 commits into from
Jul 12, 2024
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
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

71 changes: 60 additions & 11 deletions cranelift/frontend/src/frontend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,8 @@ impl fmt::Display for DefVariableError {
}
}

const LOG2_SIZE_CAPACITY: usize = (16u8.ilog2() as usize) + 1;

/// This module allows you to create a function in Cranelift IR in a straightforward way, hiding
/// all the complexity of its internal representation.
///
Expand Down Expand Up @@ -686,9 +688,40 @@ impl<'a> FunctionBuilder<'a> {
/// (currently this is just non-tail calls).
///
/// Second, take those results, add stack slots so we have a place to spill
/// to, and then finally spill the live needs-stack-map values at each
/// safepoint.
/// to.
///
/// Third, and finally, we spill the live needs-stack-map values at each
/// safepoint into those stack slots.
fn insert_safepoint_spills(&mut self) {
// Find all the GC values that are live across each safepoint.
let (safepoints, max_vals_in_stack_map_by_log2_size) =
self.find_live_stack_map_values_at_each_safepoint();

// Create the stack slots to spill them into.
let stack_slots = self.create_safepoint_slots(max_vals_in_stack_map_by_log2_size);

// Insert spills to our new stack slots before each safepoint
// instruction.
self.insert_safepoint_spills_to_stack_slots(safepoints, stack_slots);
}

/// Find the live GC references for each safepoint instruction in this
/// function.
///
/// Returns a pair of
///
/// 1. A map from each safepoint instruction to the set of GC references
/// that are live across it
///
/// 2. The maximum number of values we need to store in a stack map at the
/// same time, bucketed by their type's size. This array is indexed by
/// the log2 of the type's size.
fn find_live_stack_map_values_at_each_safepoint(
&mut self,
) -> (
crate::HashMap<ir::Inst, SmallVec<[ir::Value; 4]>>,
[usize; LOG2_SIZE_CAPACITY],
) {
// A map from each safepoint to the set of GC references that are live
// across it.
let mut safepoints: crate::HashMap<ir::Inst, SmallVec<[ir::Value; 4]>> =
Expand All @@ -698,7 +731,6 @@ impl<'a> FunctionBuilder<'a> {
// same time, bucketed by their type's size. This array is indexed by
// the log2 of the type's size. We do not support recording values whose
// size is greater than 16 in stack maps.
const LOG2_SIZE_CAPACITY: usize = (16u8.ilog2() as usize) + 1;
let mut max_vals_in_stack_map_by_log2_size = [0; LOG2_SIZE_CAPACITY];

// The set of needs-stack-maps values that are currently live in our
Expand Down Expand Up @@ -776,6 +808,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) {
let val = self.func.dfg.resolve_aliases(val);
if self.func_ctx.stack_map_values.contains(val) {
live.insert(val);
}
Expand All @@ -791,17 +824,26 @@ impl<'a> FunctionBuilder<'a> {
}
}

// Create a stack slot for each size of needs-stack-map value. These
// slots are arrays capable of holding the maximum number of same-sized
// values that must appear in the same stack map at the same time.
//
// This is indexed by the log2 of the type size.
(safepoints, max_vals_in_stack_map_by_log2_size)
}

/// Create a stack slot for each size of needs-stack-map value.
///
/// These slots are arrays capable of holding the maximum number of
/// same-sized values that must appear in the same stack map at the same
/// time.
///
/// The resulting array of stack slots is indexed by the log2 of the type
/// size.
fn create_safepoint_slots(
&mut self,
max_vals_in_stack_map_by_log2_size: [usize; LOG2_SIZE_CAPACITY],
) -> [PackedOption<ir::StackSlot>; LOG2_SIZE_CAPACITY] {
let mut stack_slots = [PackedOption::<ir::StackSlot>::default(); LOG2_SIZE_CAPACITY];
for (log2_size, capacity) in max_vals_in_stack_map_by_log2_size.into_iter().enumerate() {
if capacity == 0 {
continue;
}

let size = 1usize << log2_size;
let slot = self.func.create_sized_stack_slot(ir::StackSlotData::new(
ir::StackSlotKind::ExplicitSlot,
Expand All @@ -810,9 +852,16 @@ impl<'a> FunctionBuilder<'a> {
));
stack_slots[log2_size] = Some(slot).into();
}
stack_slots
}

// Insert spills to our new stack slots before each safepoint
// instruction.
/// Insert spills to the given stack slots before each safepoint
/// instruction.
fn insert_safepoint_spills_to_stack_slots(
&mut self,
safepoints: crate::HashMap<ir::Inst, SmallVec<[ir::Value; 4]>>,
stack_slots: [PackedOption<ir::StackSlot>; LOG2_SIZE_CAPACITY],
) {
let mut cursor = FuncCursor::new(self.func);
for (inst, live_vals) in safepoints {
cursor = cursor.at_inst(inst);
Expand Down
1 change: 1 addition & 0 deletions fuzz/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ cargo-fuzz = true

[dependencies]
anyhow = { workspace = true }
env_logger = { workspace = true }
once_cell = { workspace = true }
cranelift-codegen = { workspace = true, features = ["incremental-cache", "x86", "arm64", "s390x", "riscv64"] }
cranelift-reader = { workspace = true }
Expand Down
1 change: 1 addition & 0 deletions fuzz/fuzz_targets/cranelift-fuzzgen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,7 @@ impl fmt::Debug for TestCase {

impl<'a> Arbitrary<'a> for TestCase {
fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
let _ = env_logger::try_init();
Self::generate(u).map_err(|_| {
STATISTICS.invalid_inputs.fetch_add(1, Ordering::SeqCst);
arbitrary::Error::IncorrectFormat
Expand Down