cranelift_codegen/legalizer/
globalvalue.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
//! Legalization of global values.
//!
//! This module exports the `expand_global_value` function which transforms a `global_value`
//! instruction into code that depends on the kind of global value referenced.

use crate::cursor::{Cursor, FuncCursor};
use crate::ir::{self, pcc::Fact, InstBuilder};
use crate::isa::TargetIsa;

/// Expand a `global_value` instruction according to the definition of the global value.
pub fn expand_global_value(
    inst: ir::Inst,
    func: &mut ir::Function,
    isa: &dyn TargetIsa,
    global_value: ir::GlobalValue,
) {
    crate::trace!(
        "expanding global value: {:?}: {}",
        inst,
        func.dfg.display_inst(inst)
    );

    match func.global_values[global_value] {
        ir::GlobalValueData::VMContext => vmctx_addr(global_value, inst, func),
        ir::GlobalValueData::IAddImm {
            base,
            offset,
            global_type,
        } => iadd_imm_addr(inst, func, base, offset.into(), global_type),
        ir::GlobalValueData::Load {
            base,
            offset,
            global_type,
            flags,
        } => load_addr(inst, func, base, offset, global_type, flags, isa),
        ir::GlobalValueData::Symbol { tls, .. } => symbol(inst, func, global_value, isa, tls),
        ir::GlobalValueData::DynScaleTargetConst { vector_type } => {
            const_vector_scale(inst, func, vector_type, isa)
        }
    }
}

fn const_vector_scale(inst: ir::Inst, func: &mut ir::Function, ty: ir::Type, isa: &dyn TargetIsa) {
    assert!(ty.bytes() <= 16);

    // Use a minimum of 128-bits for the base type.
    let base_bytes = std::cmp::max(ty.bytes(), 16);
    let scale = (isa.dynamic_vector_bytes(ty) / base_bytes) as i64;
    assert!(scale > 0);
    let pos = FuncCursor::new(func).at_inst(inst);
    pos.func.dfg.replace(inst).iconst(isa.pointer_type(), scale);
}

/// Expand a `global_value` instruction for a vmctx global.
fn vmctx_addr(global_value: ir::GlobalValue, inst: ir::Inst, func: &mut ir::Function) {
    // Get the value representing the `vmctx` argument.
    let vmctx = func
        .special_param(ir::ArgumentPurpose::VMContext)
        .expect("Missing vmctx parameter");

    // Replace the `global_value` instruction's value with an alias to the vmctx arg.
    let result = func.dfg.first_result(inst);
    func.dfg.clear_results(inst);
    func.dfg.change_to_alias(result, vmctx);
    func.layout.remove_inst(inst);

    // If there was a fact on the GV, then copy it to the vmctx arg
    // blockparam def.
    if let Some(fact) = &func.global_value_facts[global_value] {
        if func.dfg.facts[vmctx].is_none() {
            let fact = fact.clone();
            func.dfg.facts[vmctx] = Some(fact);
        }
    }
}

/// Expand a `global_value` instruction for an iadd_imm global.
fn iadd_imm_addr(
    inst: ir::Inst,
    func: &mut ir::Function,
    base: ir::GlobalValue,
    offset: i64,
    global_type: ir::Type,
) {
    let mut pos = FuncCursor::new(func).at_inst(inst);

    // Get the value for the lhs.
    let lhs = pos.ins().global_value(global_type, base);
    if let Some(fact) = &pos.func.global_value_facts[base] {
        pos.func.dfg.facts[lhs] = Some(fact.clone());
    }

    // Generate the constant and attach a fact to the constant if
    // there is a fact on the base.
    let constant = pos.ins().iconst(global_type, offset);
    if pos.func.global_value_facts[base].is_some() {
        let bits = u16::try_from(global_type.bits()).unwrap();
        let unsigned_offset = offset as u64; // Safety: reinterpret i64 bits as u64.
        pos.func.dfg.facts[constant] = Some(Fact::constant(bits, unsigned_offset));
    }

    // Simply replace the `global_value` instruction with an `iadd_imm`, reusing the result value.
    pos.func.dfg.replace(inst).iadd(lhs, constant);
}

/// Expand a `global_value` instruction for a load global.
fn load_addr(
    inst: ir::Inst,
    func: &mut ir::Function,
    base: ir::GlobalValue,
    offset: ir::immediates::Offset32,
    global_type: ir::Type,
    flags: ir::MemFlags,
    isa: &dyn TargetIsa,
) {
    // We need to load a pointer from the `base` global value, so insert a new `global_value`
    // instruction. This depends on the iterative legalization loop. Note that the IR verifier
    // detects any cycles in the `load` globals.
    let ptr_ty = isa.pointer_type();
    let mut pos = FuncCursor::new(func).at_inst(inst);
    pos.use_srcloc(inst);

    // Get the value for the base.
    let base_addr = pos.ins().global_value(ptr_ty, base);
    if let Some(fact) = &pos.func.global_value_facts[base] {
        pos.func.dfg.facts[base_addr] = Some(fact.clone());
    }

    // Perform the load.
    pos.func
        .dfg
        .replace(inst)
        .load(global_type, flags, base_addr, offset);
}

/// Expand a `global_value` instruction for a symbolic name global.
fn symbol(
    inst: ir::Inst,
    func: &mut ir::Function,
    gv: ir::GlobalValue,
    isa: &dyn TargetIsa,
    tls: bool,
) {
    let ptr_ty = isa.pointer_type();

    if tls {
        func.dfg.replace(inst).tls_value(ptr_ty, gv);
    } else {
        func.dfg.replace(inst).symbol_value(ptr_ty, gv);
    }
}