Implement initial emission of constants

This approach suffers from memory-size bloat during compile time due to the desire to de-duplicate the constants emitted and reduce runtime memory-size. As a first step, though, this provides an end-to-end mechanism for constants to be emitted in the MachBuffer islands.
This commit is contained in:
Andrew Brown
2020-10-27 11:54:15 -07:00
parent efa87d4c17
commit 83f182b390
7 changed files with 251 additions and 64 deletions

View File

@@ -142,8 +142,9 @@
use crate::binemit::{Addend, CodeOffset, CodeSink, Reloc, StackMap};
use crate::ir::{ExternalName, Opcode, SourceLoc, TrapCode};
use crate::machinst::{BlockIndex, MachInstLabelUse, VCodeInst};
use crate::machinst::{BlockIndex, MachInstLabelUse, VCodeConstant, VCodeConstants, VCodeInst};
use crate::timing;
use cranelift_entity::{entity_impl, SecondaryMap};
use log::trace;
use smallvec::SmallVec;
@@ -218,6 +219,8 @@ pub struct MachBuffer<I: VCodeInst> {
/// when the offset has grown past this (`labels_at_tail_off`) point.
/// Always <= `cur_offset()`.
labels_at_tail_off: CodeOffset,
/// Map used constants to their [MachLabel].
constant_labels: SecondaryMap<VCodeConstant, MachLabel>,
}
/// A `MachBuffer` once emission is completed: holds generated code and records,
@@ -248,6 +251,7 @@ static UNKNOWN_LABEL: MachLabel = MachLabel(0xffff_ffff);
/// appropriately when the label's location is eventually known.
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct MachLabel(u32);
entity_impl!(MachLabel);
impl MachLabel {
/// Get a label for a block. (The first N MachLabels are always reseved for
@@ -267,6 +271,12 @@ impl MachLabel {
}
}
impl Default for MachLabel {
fn default() -> Self {
UNKNOWN_LABEL
}
}
/// A stack map extent, when creating a stack map.
pub enum StackMapExtent {
/// The stack map starts at this instruction, and ends after the number of upcoming bytes
@@ -299,6 +309,7 @@ impl<I: VCodeInst> MachBuffer<I> {
latest_branches: SmallVec::new(),
labels_at_tail: SmallVec::new(),
labels_at_tail_off: 0,
constant_labels: SecondaryMap::new(),
}
}
@@ -468,6 +479,24 @@ impl<I: VCodeInst> MachBuffer<I> {
// Post-invariant: as for `get_label()`.
}
/// Reserve the next N MachLabels for constants.
pub fn reserve_labels_for_constants(&mut self, constants: &VCodeConstants) {
trace!(
"MachBuffer: next {} labels are for constants",
constants.len()
);
for c in constants.keys() {
self.constant_labels[c] = self.get_label();
}
// Post-invariant: as for `get_label()`.
}
/// Retrieve the reserved label for a constant.
pub fn get_label_for_constant(&self, constant: VCodeConstant) -> MachLabel {
self.constant_labels[constant]
}
/// Bind a label to the current offset. A label can only be bound once.
pub fn bind_label(&mut self, label: MachLabel) {
trace!(
@@ -998,7 +1027,13 @@ impl<I: VCodeInst> MachBuffer<I> {
data: &[u8],
max_distance: CodeOffset,
) {
let deadline = self.cur_offset() + max_distance;
trace!(
"defer_constant: eventually emit {} bytes aligned to {} at label {:?}",
data.len(),
align,
label
);
let deadline = self.cur_offset().saturating_add(max_distance);
self.island_worst_case_size += data.len() as CodeOffset;
self.island_worst_case_size &= !(I::LabelUse::ALIGN - 1);
self.pending_constants.push(MachLabelConstant {
@@ -1136,14 +1171,6 @@ impl<I: VCodeInst> MachBuffer<I> {
pub fn finish(mut self) -> MachBufferFinalized {
let _tt = timing::vcode_emit_finish();
// Ensure that all labels are defined. This is a full (release-mode)
// assert because we must avoid looping indefinitely below; an
// unresolved label will prevent the fixup_records vec from emptying.
assert!(self
.label_offsets
.iter()
.all(|&off| off != UNKNOWN_LABEL_OFFSET));
while !self.pending_constants.is_empty() || !self.fixup_records.is_empty() {
// `emit_island()` will emit any pending veneers and constants, and
// as a side-effect, will also take care of any fixups with resolved
@@ -1151,6 +1178,11 @@ impl<I: VCodeInst> MachBuffer<I> {
self.emit_island();
}
// Ensure that all labels have been fixed up after the last island is emitted. This is a
// full (release-mode) assert because an unresolved label means the emitted code is
// incorrect.
assert!(self.fixup_records.is_empty());
MachBufferFinalized {
data: self.data,
relocs: self.relocs,