cranelift_codegen/legalizer/
globalvalue.rs

1//! Legalization of global values.
2//!
3//! This module exports the `expand_global_value` function which transforms a `global_value`
4//! instruction into code that depends on the kind of global value referenced.
5
6use crate::cursor::{Cursor, FuncCursor};
7use crate::ir::{self, pcc::Fact, InstBuilder};
8use crate::isa::TargetIsa;
9
10/// Expand a `global_value` instruction according to the definition of the global value.
11pub fn expand_global_value(
12    inst: ir::Inst,
13    func: &mut ir::Function,
14    isa: &dyn TargetIsa,
15    global_value: ir::GlobalValue,
16) {
17    crate::trace!(
18        "expanding global value: {:?}: {}",
19        inst,
20        func.dfg.display_inst(inst)
21    );
22
23    match func.global_values[global_value] {
24        ir::GlobalValueData::VMContext => vmctx_addr(global_value, inst, func),
25        ir::GlobalValueData::IAddImm {
26            base,
27            offset,
28            global_type,
29        } => iadd_imm_addr(inst, func, base, offset.into(), global_type),
30        ir::GlobalValueData::Load {
31            base,
32            offset,
33            global_type,
34            flags,
35        } => load_addr(inst, func, base, offset, global_type, flags, isa),
36        ir::GlobalValueData::Symbol { tls, .. } => symbol(inst, func, global_value, isa, tls),
37        ir::GlobalValueData::DynScaleTargetConst { vector_type } => {
38            const_vector_scale(inst, func, vector_type, isa)
39        }
40    }
41}
42
43fn const_vector_scale(inst: ir::Inst, func: &mut ir::Function, ty: ir::Type, isa: &dyn TargetIsa) {
44    assert!(ty.bytes() <= 16);
45
46    // Use a minimum of 128-bits for the base type.
47    let base_bytes = std::cmp::max(ty.bytes(), 16);
48    let scale = (isa.dynamic_vector_bytes(ty) / base_bytes) as i64;
49    assert!(scale > 0);
50    let pos = FuncCursor::new(func).at_inst(inst);
51    pos.func.dfg.replace(inst).iconst(isa.pointer_type(), scale);
52}
53
54/// Expand a `global_value` instruction for a vmctx global.
55fn vmctx_addr(global_value: ir::GlobalValue, inst: ir::Inst, func: &mut ir::Function) {
56    // Get the value representing the `vmctx` argument.
57    let vmctx = func
58        .special_param(ir::ArgumentPurpose::VMContext)
59        .expect("Missing vmctx parameter");
60
61    // Replace the `global_value` instruction's value with an alias to the vmctx arg.
62    let result = func.dfg.first_result(inst);
63    func.dfg.clear_results(inst);
64    func.dfg.change_to_alias(result, vmctx);
65    func.layout.remove_inst(inst);
66
67    // If there was a fact on the GV, then copy it to the vmctx arg
68    // blockparam def.
69    if let Some(fact) = &func.global_value_facts[global_value] {
70        if func.dfg.facts[vmctx].is_none() {
71            let fact = fact.clone();
72            func.dfg.facts[vmctx] = Some(fact);
73        }
74    }
75}
76
77/// Expand a `global_value` instruction for an iadd_imm global.
78fn iadd_imm_addr(
79    inst: ir::Inst,
80    func: &mut ir::Function,
81    base: ir::GlobalValue,
82    offset: i64,
83    global_type: ir::Type,
84) {
85    let mut pos = FuncCursor::new(func).at_inst(inst);
86
87    // Get the value for the lhs.
88    let lhs = pos.ins().global_value(global_type, base);
89    if let Some(fact) = &pos.func.global_value_facts[base] {
90        pos.func.dfg.facts[lhs] = Some(fact.clone());
91    }
92
93    // Generate the constant and attach a fact to the constant if
94    // there is a fact on the base.
95    let constant = pos.ins().iconst(global_type, offset);
96    if pos.func.global_value_facts[base].is_some() {
97        let bits = u16::try_from(global_type.bits()).unwrap();
98        let unsigned_offset = offset as u64; // Safety: reinterpret i64 bits as u64.
99        pos.func.dfg.facts[constant] = Some(Fact::constant(bits, unsigned_offset));
100    }
101
102    // Simply replace the `global_value` instruction with an `iadd_imm`, reusing the result value.
103    pos.func.dfg.replace(inst).iadd(lhs, constant);
104}
105
106/// Expand a `global_value` instruction for a load global.
107fn load_addr(
108    inst: ir::Inst,
109    func: &mut ir::Function,
110    base: ir::GlobalValue,
111    offset: ir::immediates::Offset32,
112    global_type: ir::Type,
113    flags: ir::MemFlags,
114    isa: &dyn TargetIsa,
115) {
116    // We need to load a pointer from the `base` global value, so insert a new `global_value`
117    // instruction. This depends on the iterative legalization loop. Note that the IR verifier
118    // detects any cycles in the `load` globals.
119    let ptr_ty = isa.pointer_type();
120    let mut pos = FuncCursor::new(func).at_inst(inst);
121    pos.use_srcloc(inst);
122
123    // Get the value for the base.
124    let base_addr = pos.ins().global_value(ptr_ty, base);
125    if let Some(fact) = &pos.func.global_value_facts[base] {
126        pos.func.dfg.facts[base_addr] = Some(fact.clone());
127    }
128
129    // Perform the load.
130    pos.func
131        .dfg
132        .replace(inst)
133        .load(global_type, flags, base_addr, offset);
134}
135
136/// Expand a `global_value` instruction for a symbolic name global.
137fn symbol(
138    inst: ir::Inst,
139    func: &mut ir::Function,
140    gv: ir::GlobalValue,
141    isa: &dyn TargetIsa,
142    tls: bool,
143) {
144    let ptr_ty = isa.pointer_type();
145
146    if tls {
147        func.dfg.replace(inst).tls_value(ptr_ty, gv);
148    } else {
149        func.dfg.replace(inst).symbol_value(ptr_ty, gv);
150    }
151}