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