This allows the assertions to be disabled in release builds, so that the code is faster and smaller, at the expense of not performing the checks. Assertions can be re-enabled in release builds with the debug-assertions flag in Cargo.toml, as the top-level Cargo.toml file does.
65 lines
2.7 KiB
Rust
65 lines
2.7 KiB
Rust
//! Legalization of global variables.
|
|
//!
|
|
//! This module exports the `expand_global_addr` function which transforms a `global_addr`
|
|
//! instruction into code that depends on the kind of global variable referenced.
|
|
|
|
use cursor::{Cursor, FuncCursor};
|
|
use flowgraph::ControlFlowGraph;
|
|
use ir::{self, InstBuilder};
|
|
use isa::TargetIsa;
|
|
|
|
/// Expand a `global_addr` instruction according to the definition of the global variable.
|
|
pub fn expand_global_addr(
|
|
inst: ir::Inst,
|
|
func: &mut ir::Function,
|
|
_cfg: &mut ControlFlowGraph,
|
|
_isa: &TargetIsa,
|
|
) {
|
|
// Unpack the instruction.
|
|
let gv = match func.dfg[inst] {
|
|
ir::InstructionData::UnaryGlobalVar { opcode, global_var } => {
|
|
debug_assert_eq!(opcode, ir::Opcode::GlobalAddr);
|
|
global_var
|
|
}
|
|
_ => panic!("Wanted global_addr: {}", func.dfg.display_inst(inst, None)),
|
|
};
|
|
|
|
match func.global_vars[gv] {
|
|
ir::GlobalVarData::VmCtx { offset } => vmctx_addr(inst, func, offset.into()),
|
|
ir::GlobalVarData::Deref { base, offset } => deref_addr(inst, func, base, offset.into()),
|
|
ir::GlobalVarData::Sym { .. } => globalsym(inst, func, gv),
|
|
}
|
|
}
|
|
|
|
/// Expand a `global_addr` instruction for a vmctx global.
|
|
fn vmctx_addr(inst: ir::Inst, func: &mut ir::Function, offset: i64) {
|
|
// Get the value representing the `vmctx` argument.
|
|
let vmctx = func.special_param(ir::ArgumentPurpose::VMContext).expect(
|
|
"Missing vmctx parameter",
|
|
);
|
|
|
|
// Simply replace the `global_addr` instruction with an `iadd_imm`, reusing the result value.
|
|
func.dfg.replace(inst).iadd_imm(vmctx, offset);
|
|
}
|
|
|
|
/// Expand a `global_addr` instruction for a deref global.
|
|
fn deref_addr(inst: ir::Inst, func: &mut ir::Function, base: ir::GlobalVar, offset: i64) {
|
|
// We need to load a pointer from the `base` global variable, so insert a new `global_addr`
|
|
// instruction. This depends on the iterative legalization loop. Note that the IL verifier
|
|
// detects any cycles in the `deref` globals.
|
|
let ptr_ty = func.dfg.value_type(func.dfg.first_result(inst));
|
|
let mut pos = FuncCursor::new(func).at_inst(inst);
|
|
pos.use_srcloc(inst);
|
|
|
|
let base_addr = pos.ins().global_addr(ptr_ty, base);
|
|
// TODO: We could probably set both `notrap` and `aligned` on this load instruction.
|
|
let base_ptr = pos.ins().load(ptr_ty, ir::MemFlags::new(), base_addr, 0);
|
|
pos.func.dfg.replace(inst).iadd_imm(base_ptr, offset);
|
|
}
|
|
|
|
/// Expand a `global_addr` instruction for a symbolic name global.
|
|
fn globalsym(inst: ir::Inst, func: &mut ir::Function, gv: ir::GlobalVar) {
|
|
let ptr_ty = func.dfg.value_type(func.dfg.first_result(inst));
|
|
func.dfg.replace(inst).globalsym_addr(ptr_ty, gv);
|
|
}
|