///|
pub struct FunctionOpcodeDelta {
mnemonic : String
baseline_count : Int
current_count : Int
count_delta : Int
baseline_encoded_bytes : Int
current_encoded_bytes : Int
encoded_bytes_delta : Int
}
///|
/// Signed current-minus-baseline quantities, never file ranges.
pub struct FunctionInstructionsDelta {
entry_bytes : Int
prefix_bytes : Int
body_bytes : Int
locals_bytes : Int
instruction_bytes : Int
instruction_count : Int
}
///|
pub struct FunctionInstructionsComparison {
baseline : FunctionInstructionsReport
current : FunctionInstructionsReport
delta : FunctionInstructionsDelta
opcodes : Array[FunctionOpcodeDelta]
scope : String
}
///|
pub enum FunctionInstructionsComparisonError {
BaselineInstructionsError(Int, FunctionInstructionsError)
CurrentInstructionsError(Int, FunctionInstructionsError)
}
///|
pub fn describe_function_instructions_comparison_error(
error : FunctionInstructionsComparisonError,
) -> String {
match error {
BaselineInstructionsError(index, cause) =>
"baseline function \{index}: " +
describe_function_instructions_error(cause)
CurrentInstructionsError(index, cause) =>
"current function \{index}: " +
describe_function_instructions_error(cause)
}
}
///|
fn instruction_composition_row(
mnemonic : String,
before_count : Int,
before_bytes : Int,
after_count : Int,
after_bytes : Int,
) -> FunctionOpcodeDelta {
{
mnemonic,
baseline_count: before_count,
current_count: after_count,
count_delta: after_count - before_count,
baseline_encoded_bytes: before_bytes,
current_encoded_bytes: after_bytes,
encoded_bytes_delta: after_bytes - before_bytes,
}
}
///|
/// Each side inspects its explicit target once. No identity matching or alignment.
pub fn compare_function_instructions(
baseline : Bytes,
baseline_index : Int,
current : Bytes,
current_index : Int,
) -> Result[FunctionInstructionsComparison, FunctionInstructionsComparisonError] {
let before = match inspect_function_instructions(baseline, baseline_index) {
Ok(report) => report
Err(error) => return Err(BaselineInstructionsError(baseline_index, error))
}
let after = match inspect_function_instructions(current, current_index) {
Ok(report) => report
Err(error) => return Err(CurrentInstructionsError(current_index, error))
}
let rows : Map[String, FunctionOpcodeDelta] = Map([])
for group in before.groups {
rows.set(
group.mnemonic,
instruction_composition_row(
group.mnemonic,
group.count,
group.encoded_bytes,
0,
0,
),
)
}
for group in after.groups {
let (count, bytes) = match rows.get(group.mnemonic) {
Some(row) => (row.baseline_count, row.baseline_encoded_bytes)
None => (0, 0)
}
rows.set(
group.mnemonic,
instruction_composition_row(
group.mnemonic,
count,
bytes,
group.count,
group.encoded_bytes,
),
)
}
let opcodes : Array[FunctionOpcodeDelta] = []
for _, row in rows {
opcodes.push(row)
}
// Deltas of bounded nonnegative counts/sizes cannot be Int::MIN.
opcodes.sort_by(fn(a, b) {
let bytes = b.encoded_bytes_delta.abs().compare(a.encoded_bytes_delta.abs())
if bytes != 0 {
bytes
} else {
let count = b.count_delta.abs().compare(a.count_delta.abs())
if count != 0 {
count
} else {
a.mnemonic.lexical_compare(b.mnemonic)
}
}
})
Ok({
baseline: before,
current: after,
delta: {
entry_bytes: after.entry.size - before.entry.size,
prefix_bytes: after.prefix.size - before.prefix.size,
body_bytes: after.body.size - before.body.size,
locals_bytes: after.locals_encoding.size - before.locals_encoding.size,
instruction_bytes: after.instruction_bytes - before.instruction_bytes,
instruction_count: after.instruction_count - before.instruction_count,
},
opcodes,
scope: "User-selected pair of local functions; full indices belong to their respective inputs. Equal indices or names do not establish cross-build identity; different names are allowed. Only encoding composition statistics are compared; instruction sequences and immediate values are not compared. Zero aggregate deltas do not prove equal sequences, semantics or compatibility. Delta = current - baseline, including actual body-length prefixes and locals vector encodings. Quantities are not file ranges. Excludes section headers/counts, function and name sections and dependency closures. Entry delta is not whole-file delta and predicts neither performance, source removal, rebuilt output nor gzip savings. No automatic matching, instruction alignment, graph or analyze. Top limits text rows only; nested reports and JSON retain complete selected-target evidence.",
})
}