I'm working with Cranelift IR for the first time, and I want to make some modifications to the existing IR. I referred to cranelift-fuzzgen
and wrote the following code to traverse each IR instruction in a function. This code can retrieve the opcode of the instruction as well as its args
and results
. However, I found that args
and results
seem to pertain to variables. For an instruction like v2 = iconst.i32 32
, the args
is empty, so how can I retrieve the immediate value 32
in this instruction? Additionally, are there any codes or tutorials related to modifying Cranelift IR that I can refer to?
fn traverse_instrs(func: &mut Function) {
let mut pos = FuncCursor::new(func);
while let Some(_block) = pos.next_block() {
while let Some(inst) = pos.next_inst() {
let instr_data = pos.func.dfg.insts[inst];
let opcode = instr_data.opcode();
let instr_args = pos.func.dfg.inst_args(inst);
let instr_results = pos.func.dfg.inst_results(inst);
}
};
}
In general all of the special information for a given opcode is going to be in the InstructionData struct. We have some functions there to simplify feching values from some instructions, but we don't have anything for fetching the constant value from a const.
So you might have to destructure it. iconst
is of type InstructionData::UnaryImm
so you can probably do something along these lines.
let imm = match instr_data {
InstructionData::UnaryImm { imm, ..} => imm,
_ => panic!("unrecognized instr"),
};
Additionally, are there any codes or tutorials related to modifying Cranelift IR that I can refer to?
Unfortunately I don't think so
For doing more complicated changes in the IR you might want to use FuncCursor that lets you replace whole instructions at certain points
Afonso Bordado said:
For doing more complicated changes in the IR you might want to use FuncCursor that lets you replace whole instructions at certain points
Thank you very much for your answer :+1:
@Afonso Bordado
Hi, I encountered a strange control flow behavior with the following piece of code. The control flow generated seems unusual and not as expected.
let mut func = Function::new();
let block0 = func.dfg.make_block();
let cond = func.dfg.append_block_param(block0, types::I32);
let block1 = func.dfg.make_block();
let block2 = func.dfg.make_block();
let br_block1_block0_block2;
{
let mut cur = FuncCursor::new(&mut func);
cur.insert_block(block0);
cur.insert_block(block1);
br_block1_block0_block2 = cur.ins().brif(cond, block0, &[], block2, &[]);
cur.insert_block(block2);
}
let block3 = func.dfg.make_block();
func.layout.remove_block(block2);
{
let mut cur = FuncCursor::new(&mut func);
let mut cur = cur.at_last_inst(block1);
// let removed_instr = cur.remove_inst();
cur.ins().brif(cond, block0, &[], block3, &[]);
cur.insert_block(block3);
}
The control flow graph generated by this code is as follows:
image.png
In the code, I didn’t add any instructions to block3, but the generated control flow graph shows that block3 contains the br_if instruction, and these instructions were originally in block1. Only when I uncomment the lines in the code can the correct control flow graph be generated.
I'm not entirely sure why that br_if gets placed there, but when inserting the first br_if the cursor is not explicitly placed anywhere, so I'm not sure what the behaviour is there
Last updated: Dec 23 2024 at 12:05 UTC