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