Stream: cranelift

Topic: Use a Data Address from cranelift_module in Function


view this post on Zulip Chris Clark (Jul 18 2024 at 18:59):

I have this adding of const data section

    pub fn add_const_data(&mut self, name: &str, contents: Vec<u8>) -> () {
        self.data.define(contents.into_boxed_slice());
        let id = self
            .obj_mod
            .declare_data(name, Linkage::Export, false, false)
            .unwrap();
        self.obj_mod.define_data(id, &self.data).unwrap();
    }

My language code looks like the following.

const z = 1
pub const main = fn() usize {
    const m = 7
    const x = 4
    return x + m + z
}

When everything was defined within the function it was easy for me to just reference it like so.

    pub fn handle_sym(&self, op: &SymbolAccess) -> ResultFir<Variable> {
        Ok(Variable::from_u32(
            *self.scope.table.get(&op.ident).unwrap(),
        ))
    }

But there isn't a symbol table for this since it just has an address.

view this post on Zulip bjorn3 (Jul 18 2024 at 19:59):

You can use module.declare_data_in_func passing in both the id returned from declare_data and a mutable reference to the Function you are creating. This method then returns a GlobalValue you can pass to the global_value instruction to get a Value representing the address of the data object.

view this post on Zulip Chris Clark (Jul 18 2024 at 21:03):

mind.blown() Do you have an example?

Do i need to tell it to dereference that Value that is returned?

view this post on Zulip bjorn3 (Jul 19 2024 at 14:40):

If you want to get the value stored in the global variable you did need to load from the address returned by global_value and if you want to change the value you need to use store.

view this post on Zulip Chris Clark (Jul 21 2024 at 19:33):

I think I see what you mean. I need to do some refactoring then. To pass the object_module to my IR builder.

@bjorn3 Since the IR is built to be at the function level, do you recommend an object file with all the data, and then for every function a separate object file? This should also help with caching in between builds if the function doesn't change but some other does.

view this post on Zulip bjorn3 (Jul 21 2024 at 19:36):

Having a separate object file for each function would likely make the linker slower. The way rustc handles this is by splitting all functions between a set of codegen units each corresponding to a single object file. The amount of codegen units is fixed to 16 or 256 depending on if incr comp is enabled or not. 256 when it is enabled to maximize reuse of object files when none of the functions inside it change, and 16 for better optimizations while still enabling each codegen unit to be compiled in parallel when no reuse would happen anyway due to incr comp being disabled. (You can override this using -Ccodegen-units)

view this post on Zulip Chris Clark (Dec 24 2024 at 16:37):

I finally had some serious time to do the refactors. Usually holidays when family is around to help with the kids. I'm just not sure of the results. this is such an unknown space for me.

My pl

const m = 5

pub const main = fn() usize {
  return 5 - m
}

objdump -D

main.o:     file format elf64-x86-64


Disassembly of section .rodata:

0000000000000000 <m>:
   0:   05 00 00 00 00          add    $0x0,%eax
   5:   00 00                   add    %al,(%rax)
        ...

Disassembly of section .text:

0000000000000000 <main>:
   0:   55                      push   %rbp
   1:   48 89 e5                mov    %rsp,%rbp
   4:   b8 05 00 00 00          mov    $0x5,%eax
   9:   48 8d 0d 00 00 00 00    lea    0x0(%rip),%rcx        # 10 <main+0x10>
  10:   48 2b 01                sub    (%rcx),%rax
  13:   48 89 ec                mov    %rbp,%rsp
  16:   5d                      pop    %rbp
  17:   c3                      ret

const initialization
self.data is DataDescription

    pub fn recurse(&mut self, expr: &TypeTree) -> () {
        match expr {
            TypeTree::I64(x) => self.data.define(Box::from(x.clone().to_ne_bytes())),
            TypeTree::U64(x) => self.data.define(Box::from(x.clone().to_ne_bytes())),
            _ => panic!("unexpected type tree in oir"),
        }
    }

    pub fn const_init(&mut self, init: &TopInitialization, dt: &mut DataTable) -> () {
        let slice = &init.left.into_symbol_init().ident;
        self.recurse(init.right.as_ref());
        let id = self
            .obj_mod
            .declare_data(slice, Linkage::Export, false, false)
            .unwrap();
        self.obj_mod.define_data(id, &self.data).unwrap();
        dt.table.insert(slice.to_string(), id);
    }

symbol referencing

    pub fn handle_sym_access(
        &mut self,
        op: &SymbolAccess,
        dtbl: &DataTable,
        scopes: &Vec<ScopeTable>,
        type_tables: &Vec<TypeTable>,
        oir: &mut Oir,
        builder: &mut FunctionBuilder,
    ) -> ResultFir<Variable> {
        let sym = self.sym.table.get(&op.ident);
        if let Some(s) = sym {
            return Ok(Variable::from_u32(*s));
        }
        let id = dtbl.table.get(&op.ident).unwrap();
        let gv = oir.obj_mod.declare_data_in_func(*id, builder.func);
        let val = builder.ins().global_value(I64, gv);
        let result = self.add_var();
        builder.declare_var(result, I64);
        let loaded = builder
            .ins()
            .load(I64, MemFlags::new(), val, Offset32::new(0));
        builder.def_var(result, loaded);
        Ok(result)
    }

My only question is does this look like the right track? The program executes correctly.

view this post on Zulip bjorn3 (Dec 26 2024 at 12:02):

Looks correct to me.

view this post on Zulip Chris Clark (Dec 26 2024 at 16:52):

As far as knowing to easily inline the m variable of 5 into the program. That is an optimization, is there a pass manager maintained by someone or drop-in optimizations library from anyone?

view this post on Zulip bjorn3 (Dec 26 2024 at 19:43):

Cranelift currently doesn't have any interprocedural optimizations. Neither inlining of functions, nor inlining of constant globals. In general Cranelift doesn't have much optimizations that would benefit anything other than compiling already optimized wasm to machine code.

view this post on Zulip Chris Clark (Dec 26 2024 at 20:16):

I was merely asking if there was anyone doing this somewhere even outside of bytecode alliance/ wasmtime/ cranelift?

view this post on Zulip bjorn3 (Dec 26 2024 at 20:38):

I'm not aware of any external optimizer for Cranelift either.


Last updated: Jan 24 2025 at 00:11 UTC