in src/profiler/elastic_apm_profiler/src/cil/method.rs [431:564]
fn update_instructions(&mut self, index: usize, offset: usize, len: i64) {
// update the offsets of control flow instructions and expand any short instructions:
//
// 1. for control flow instructions before the target index,
// if the offset is positive and results in an index after the target index,
// add len to the offset
// 2. for control flow instructions after the target index,
// if the offset is negative and results in an index before the target index,
// subtract len from the offset i.e. offset is further away
let mut map: Vec<usize> = self.instructions.iter().map(|i| i.len()).collect();
let mut updated_instructions = vec![];
for (i, instruction) in self.instructions.iter_mut().enumerate() {
if i < index {
if let ShortInlineBrTarget(target_offset) = &mut instruction.operand {
if *target_offset >= 0 {
let mut sum_len = 0;
let mut j = 1;
while sum_len < *target_offset as usize {
sum_len += map[i + j];
j += 1;
}
if i + j > index {
let n = *target_offset as i32 + len as i32;
if n > i8::MAX as i32 {
let current_len = instruction.len();
// update the instruction
instruction.operand = InlineBrTarget(n);
instruction.opcode = Opcode::short_to_long_form(instruction.opcode);
// update the map with the new instruction len and record
// the original offset and len diff.
let new_len = instruction.len();
map[i] = new_len;
updated_instructions.push((offset, (new_len - current_len) as i64));
} else {
*target_offset = n as i8;
}
}
}
} else if let InlineBrTarget(target_offset) = &mut instruction.operand {
if *target_offset >= 0 {
let mut sum_len = 0;
let mut j = 1;
while sum_len < *target_offset as usize {
sum_len += map[i + j];
j += 1;
}
if i + j > index {
let n = *target_offset + len as i32;
*target_offset = n;
}
}
} else if let InlineSwitch(count, target_offsets) = &mut instruction.operand {
for target_offset in target_offsets {
if *target_offset >= 0 {
let mut sum_len = 0;
let mut j = 1;
while sum_len < *target_offset as usize {
sum_len += map[i + j];
j += 1;
}
if i + j > index {
*target_offset += len as i32;
}
}
}
}
} else {
if let ShortInlineBrTarget(target_offset) = &mut instruction.operand {
if *target_offset < 0 {
let mut sum_len = 0;
let mut j = 0;
while *target_offset < sum_len {
sum_len -= map[i - j] as i8;
j += 1;
}
if i - j < index {
let n = *target_offset as i32 - len as i32;
if n < i8::MIN as i32 {
let current_len = instruction.len();
// update the instruction
instruction.operand = InlineBrTarget(n);
instruction.opcode = Opcode::short_to_long_form(instruction.opcode);
// update the map with the new instruction len and record
// the original offset and len diff.
let new_len = instruction.len();
map[i] = new_len;
updated_instructions.push((offset, (new_len - current_len) as i64));
} else {
*target_offset = n as i8;
}
}
}
} else if let InlineBrTarget(target_offset) = &mut instruction.operand {
if *target_offset < 0 {
let mut sum_len = 0;
let mut j = 0;
while *target_offset < sum_len {
sum_len -= map[i - j] as i32;
j += 1;
}
if i - j < index {
let n = *target_offset - len as i32;
*target_offset = n;
}
}
} else if let InlineSwitch(count, target_offsets) = &mut instruction.operand {
for target_offset in target_offsets {
if *target_offset < 0 {
let mut sum_len = 0;
let mut j = 0;
while *target_offset < sum_len {
sum_len -= map[i - j] as i32;
j += 1;
}
if i - j < index {
*target_offset -= len as i32;
}
}
}
}
}
}
if !updated_instructions.is_empty() {
for (offset, len) in updated_instructions {
self.update_header(len, None).unwrap();
self.update_sections(offset, len).unwrap();
}
}
}