Custom legalization for global_addr.

The code to compute the address of a global variable depends on the kind
of variable, so custom legalization is required.

- Add a legalizer::globalvar module which exposes an
  expand_global_addr() function. This module is likely to grow as we add
  more types of global variables.
- Add a ArgumentPurpose::VMContext enumerator. This is used to represent
  special 'vmctx' arguments that are used as base pointers for vmctx
  globals.
This commit is contained in:
Jakob Stoklund Olesen
2017-08-18 09:08:41 -07:00
parent 5dba00b761
commit f2ebabaf5f
7 changed files with 127 additions and 12 deletions

View File

@@ -0,0 +1,59 @@
//! 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};
/// 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) {
// Unpack the instruction.
let gv = match &func.dfg[inst] {
&ir::InstructionData::UnaryGlobalVar { opcode, global_var } => {
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()),
}
}
/// Expand a `global_addr` instruction for a vmctx global.
fn vmctx_addr(inst: ir::Inst, func: &mut ir::Function, offset: i64) {
// Find the incoming `vmctx` function argument. Start searching from the back since the special
// arguments are appended by signature legalization.
//
// This argument must exist; `vmctx` global variables can not be used in functions with calling
// conventions that don't add a `vmctx` argument.
let argidx = func.signature
.argument_types
.iter()
.rposition(|abi| abi.purpose == ir::ArgumentPurpose::VMContext)
.expect("Need vmctx argument for vmctx global");
// Get the value representing the `vmctx` argument.
let vmctx = func.dfg.ebb_args(func.layout.entry_block().unwrap())[argidx];
// 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);
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);
}