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