Add global variables to Cretonne IL.

See #144 for discussion.

- Add a new GlobalVar entity type both in Python and Rust.
- Define a UnaryGlobalVar instruction format containing a GlobalVar
  reference.
- Add a globalvar.rs module defining the GlobalVarData with support for
  'vmctx' and 'deref' global variable kinds.

Langref:
    Add a section about global variables and the global_addr
    instruction.

Parser:
    Add support for the UnaryGlobalVar instruction format as well as
    global variable declarations in the preamble.
This commit is contained in:
Jakob Stoklund Olesen
2017-08-17 11:30:00 -07:00
parent 7e402a6104
commit bf4ae3bb2e
18 changed files with 336 additions and 14 deletions

View File

@@ -58,7 +58,7 @@ html:
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
autohtml: html
$(SPHINXABUILD) -z ../lib/cretonne/meta --ignore '.*.sw?' -b html -E $(ALLSPHINXOPTS) $(BUILDDIR)/html
$(SPHINXABUILD) -z ../lib/cretonne/meta --ignore '.*' -b html -E $(ALLSPHINXOPTS) $(BUILDDIR)/html
dirhtml:
$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml

View File

@@ -495,6 +495,62 @@ instructions before instruction selection::
v9 = stack_addr ss3, 16
v1 = load.f64 v9
Global variables
----------------
A *global variable* is an object in memory whose address is not known at
compile time. The address is computed at runtime by :inst:`global_addr`,
possibly using information provided by the linker via relocations. There are
multiple kinds of global variables using different methods for determining
their address. Cretonne does not track the type or even the size of global
variables, they are just pointers to non-stack memory.
When Cretonne is generating code for a virtual machine environment, globals can
be used to access data structures in the VM's runtime. This requires functions
to have access to a *VM context pointer* which is used as the base address.
Typically, the VM context pointer is passed as a hidden function argument to
Cretonne functions.
.. inst:: GV = vmctx+Offset
Declare a global variable in the VM context struct.
This declares a global variable whose address is a constant offset from the
VM context pointer which is passed as a hidden argument to all functions
JIT-compiled for the VM.
Typically, the VM context is a C struct, and the declared global variable
is a member of the struct.
:arg Offset: Byte offset from the VM context pointer to the global
variable.
:result GV: Global variable.
The address of a global variable can also be derived by treating another global
variable as a struct pointer. This makes it possible to chase pointers into VM
runtime data structures.
.. inst:: GV = deref(BaseGV)+Offset
Declare a global variable in a struct pointed to by BaseGV.
The address of GV can be computed by first loading a pointer from BaseGV
and adding Offset to it.
It is assumed the BaseGV resides in readable memory with the apropriate
alignment for storing a pointer.
Chains of ``deref`` global variables are possible, but cycles are not
allowed. They will be caught by the IL verifier.
:arg BaseGV: Global variable containing the base pointer.
:arg Offset: Byte offset from the loaded base pointer to the global
variable.
:result GV: Global variable.
.. autoinst:: global_addr
Heaps
-----

View File

@@ -0,0 +1,37 @@
test cat
test verifier
function %vmglobal() -> i32 {
gv3 = vmctx+16
; check: $gv3 = vmctx+16
gv4 = vmctx+0
; check: $gv4 = vmctx
; not: +0
gv5 = vmctx -256
; check: $gv5 = vmctx-256
ebb0:
v1 = global_addr.i32 gv3
; check: $v1 = global_addr.i32 $gv3
return v1
}
function %deref() -> i32 {
gv3 = vmctx+16
gv4 = deref(gv3)-32
; check: $gv4 = deref($gv3)-32
ebb0:
v1 = global_addr.i32 gv4
; check: $v1 = global_addr.i32 $gv4
return v1
}
; Refer to a global variable before it's been declared.
function %backref() -> i32 {
gv1 = deref(gv2)-32
; check: $gv1 = deref($gv2)-32
gv2 = vmctx+16
; check: $gv2 = vmctx+16
ebb0:
v1 = global_addr.i32 gv1
return v1
}

View File

@@ -16,6 +16,9 @@ ebb = EntityRefKind(
#: A reference to a stack slot declared in the function preamble.
stack_slot = EntityRefKind('stack_slot', 'A stack slot.')
#: A reference to a global variable.
global_var = EntityRefKind('global_var', 'A global variable.')
#: A reference to a function sugnature declared in the function preamble.
#: Tbis is used to provide the call signature in an indirect call instruction.
sig_ref = EntityRefKind('sig_ref', 'A function signature.')

View File

@@ -10,7 +10,8 @@ from cdsl.formats import InstructionFormat
from cdsl.operands import VALUE, VARIABLE_ARGS
from .immediates import imm64, uimm8, ieee32, ieee64, offset32, uoffset32
from .immediates import boolean, intcc, floatcc, memflags, regunit
from .entities import ebb, sig_ref, func_ref, jump_table, stack_slot
from . import entities
from .entities import ebb, sig_ref, func_ref, stack_slot
Nullary = InstructionFormat()
@@ -19,6 +20,7 @@ UnaryImm = InstructionFormat(imm64)
UnaryIeee32 = InstructionFormat(ieee32)
UnaryIeee64 = InstructionFormat(ieee64)
UnaryBool = InstructionFormat(boolean)
UnaryGlobalVar = InstructionFormat(entities.global_var)
Binary = InstructionFormat(VALUE, VALUE)
BinaryImm = InstructionFormat(VALUE, imm64)
@@ -42,7 +44,7 @@ FloatCompare = InstructionFormat(floatcc, VALUE, VALUE)
Jump = InstructionFormat(ebb, VARIABLE_ARGS)
Branch = InstructionFormat(VALUE, ebb, VARIABLE_ARGS)
BranchIcmp = InstructionFormat(intcc, VALUE, VALUE, ebb, VARIABLE_ARGS)
BranchTable = InstructionFormat(VALUE, jump_table)
BranchTable = InstructionFormat(VALUE, entities.jump_table)
Call = InstructionFormat(func_ref, VARIABLE_ARGS)
IndirectCall = InstructionFormat(sig_ref, VALUE, VARIABLE_ARGS)

View File

@@ -345,6 +345,18 @@ stack_addr = Instruction(
""",
ins=(SS, Offset), outs=addr)
#
# Global variables.
#
GV = Operand('GV', entities.global_var)
global_addr = Instruction(
'global_addr', r"""
Compute the address of global variable GV.
""",
ins=GV, outs=addr)
#
# WebAssembly bounds-checked heap accesses.
#

View File

@@ -59,7 +59,7 @@ VARIABLE_ARGS = OperandKind(
'variable_args', """
A variable size list of `value` operands.
Use this to represent arguemtns passed to a function call, arguments
Use this to represent arguments passed to a function call, arguments
passed to an extended basic block, or a variable number of results
returned from an instruction.
""",

View File

@@ -5,8 +5,8 @@
use ir::types;
use ir::{InstructionData, DataFlowGraph};
use ir::{Opcode, Type, Inst, Value, Ebb, JumpTable, SigRef, FuncRef, StackSlot, ValueList,
MemFlags};
use ir::{Opcode, Type, Inst, Value, Ebb, JumpTable, SigRef, FuncRef, StackSlot, GlobalVar,
ValueList, MemFlags};
use ir::immediates::{Imm64, Uimm8, Ieee32, Ieee64, Offset32, Uoffset32};
use ir::condcodes::{IntCC, FloatCC};
use isa::RegUnit;

View File

@@ -65,6 +65,24 @@ entity_impl!(Inst, "inst");
pub struct StackSlot(u32);
entity_impl!(StackSlot, "ss");
/// An opaque reference to a global variable.
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub struct GlobalVar(u32);
entity_impl!(GlobalVar, "gv");
impl GlobalVar {
/// Create a new global variable reference from its number.
///
/// This method is for use by the parser.
pub fn with_number(n: u32) -> Option<GlobalVar> {
if n < u32::MAX {
Some(GlobalVar(n))
} else {
None
}
}
}
/// An opaque reference to a jump table.
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub struct JumpTable(u32);
@@ -93,6 +111,8 @@ pub enum AnyEntity {
Value(Value),
/// A stack slot.
StackSlot(StackSlot),
/// A Global variable.
GlobalVar(GlobalVar),
/// A jump table.
JumpTable(JumpTable),
/// An external function.
@@ -109,6 +129,7 @@ impl fmt::Display for AnyEntity {
AnyEntity::Inst(r) => r.fmt(f),
AnyEntity::Value(r) => r.fmt(f),
AnyEntity::StackSlot(r) => r.fmt(f),
AnyEntity::GlobalVar(r) => r.fmt(f),
AnyEntity::JumpTable(r) => r.fmt(f),
AnyEntity::FuncRef(r) => r.fmt(f),
AnyEntity::SigRef(r) => r.fmt(f),
@@ -140,6 +161,12 @@ impl From<StackSlot> for AnyEntity {
}
}
impl From<GlobalVar> for AnyEntity {
fn from(r: GlobalVar) -> AnyEntity {
AnyEntity::GlobalVar(r)
}
}
impl From<JumpTable> for AnyEntity {
fn from(r: JumpTable) -> AnyEntity {
AnyEntity::JumpTable(r)

View File

@@ -4,8 +4,8 @@
//! instructions.
use entity_map::{EntityMap, PrimaryEntityData};
use ir::{FunctionName, CallConv, Signature, JumpTableData, DataFlowGraph, Layout};
use ir::{JumpTables, InstEncodings, ValueLocations, StackSlots, EbbOffsets};
use ir::{FunctionName, CallConv, Signature, JumpTableData, GlobalVarData, DataFlowGraph, Layout};
use ir::{JumpTables, InstEncodings, ValueLocations, StackSlots, GlobalVars, EbbOffsets};
use isa::TargetIsa;
use std::fmt;
use write::write_function;
@@ -25,6 +25,9 @@ pub struct Function {
/// Stack slots allocated in this function.
pub stack_slots: StackSlots,
/// Global variables referenced.
pub global_vars: GlobalVars,
/// Jump tables used in this function.
pub jump_tables: JumpTables,
@@ -50,6 +53,7 @@ pub struct Function {
}
impl PrimaryEntityData for JumpTableData {}
impl PrimaryEntityData for GlobalVarData {}
impl Function {
/// Create a function with the given name and signature.
@@ -58,6 +62,7 @@ impl Function {
name,
signature: sig,
stack_slots: StackSlots::new(),
global_vars: GlobalVars::new(),
jump_tables: EntityMap::new(),
dfg: DataFlowGraph::new(),
layout: Layout::new(),

View File

@@ -0,0 +1,37 @@
//! Global variables.
use ir::GlobalVar;
use ir::immediates::Offset32;
use std::fmt;
/// Information about a global variable declaration.
#[derive(Clone)]
pub enum GlobalVarData {
/// Variable is part of the VM context struct, it's address is a constant offset from the VM
/// context pointer.
VmCtx {
/// Offset from the `vmctx` pointer to this global.
offset: Offset32,
},
/// Variable is part of a struct pointed to by another global variable.
///
/// The `base` global variable is assumed to contain a pointer to a struct. This global
/// variable lives at an offset into the struct.
Deref {
/// The base pointer global variable.
base: GlobalVar,
/// Byte offset to be added to the pointer loaded from `base`.
offset: Offset32,
},
}
impl fmt::Display for GlobalVarData {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
&GlobalVarData::VmCtx { offset } => write!(f, "vmctx{}", offset),
&GlobalVarData::Deref { base, offset } => write!(f, "deref({}){}", base, offset),
}
}
}

View File

@@ -109,6 +109,10 @@ pub enum InstructionData {
UnaryIeee32 { opcode: Opcode, imm: Ieee32 },
UnaryIeee64 { opcode: Opcode, imm: Ieee64 },
UnaryBool { opcode: Opcode, imm: bool },
UnaryGlobalVar {
opcode: Opcode,
global_var: ir::GlobalVar,
},
Binary { opcode: Opcode, args: [Value; 2] },
BinaryImm {
opcode: Opcode,

View File

@@ -13,17 +13,19 @@ pub mod function;
mod builder;
mod extfunc;
mod funcname;
mod globalvar;
mod memflags;
mod progpoint;
mod valueloc;
pub use ir::builder::{InstBuilder, InstBuilderBase, InstInserterBase, InsertBuilder};
pub use ir::dfg::{DataFlowGraph, ValueDef};
pub use ir::entities::{Ebb, Inst, Value, StackSlot, JumpTable, FuncRef, SigRef};
pub use ir::entities::{Ebb, Inst, Value, StackSlot, GlobalVar, JumpTable, FuncRef, SigRef};
pub use ir::extfunc::{Signature, CallConv, ArgumentType, ArgumentExtension, ArgumentPurpose,
ExtFuncData};
pub use ir::funcname::FunctionName;
pub use ir::function::Function;
pub use ir::globalvar::GlobalVarData;
pub use ir::instructions::{Opcode, InstructionData, VariableArgs, ValueList, ValueListPool};
pub use ir::jumptable::JumpTableData;
pub use ir::layout::{Layout, CursorBase, Cursor};
@@ -48,3 +50,6 @@ pub type InstEncodings = EntityMap<Inst, isa::Encoding>;
/// Code offsets for EBBs.
pub type EbbOffsets = EntityMap<Ebb, binemit::CodeOffset>;
/// Map of global variables.
pub type GlobalVars = EntityMap<GlobalVar, GlobalVarData>;

View File

@@ -57,7 +57,7 @@ use flowgraph::ControlFlowGraph;
use ir::entities::AnyEntity;
use ir::instructions::{InstructionFormat, BranchInfo, ResolvedConstraint, CallInfo};
use ir::{types, Function, ValueDef, Ebb, Inst, SigRef, FuncRef, ValueList, JumpTable, StackSlot,
StackSlotKind, Value, Type, Opcode, ValueLoc, ArgumentLoc};
StackSlotKind, GlobalVar, Value, Type, Opcode, ValueLoc, ArgumentLoc};
use isa::TargetIsa;
use std::error as std_error;
use std::fmt::{self, Display, Formatter};
@@ -270,6 +270,9 @@ impl<'a> Verifier<'a> {
StackStore { stack_slot, .. } => {
self.verify_stack_slot(inst, stack_slot)?;
}
UnaryGlobalVar { global_var, .. } => {
self.verify_global_var(inst, global_var)?;
}
// Exhaustive list so we can't forget to add new formats
Nullary { .. } |
@@ -328,6 +331,14 @@ impl<'a> Verifier<'a> {
}
}
fn verify_global_var(&self, inst: Inst, gv: GlobalVar) -> Result {
if !self.func.global_vars.is_valid(gv) {
err!(inst, "invalid global variable {}", gv)
} else {
Ok(())
}
}
fn verify_value_list(&self, inst: Inst, l: &ValueList) -> Result {
if !l.is_valid(&self.func.dfg.value_lists) {
err!(inst, "invalid value list reference {:?}", l)

View File

@@ -49,6 +49,11 @@ fn write_preamble(w: &mut Write,
writeln!(w, " {} = {}", ss, func.stack_slots[ss])?;
}
for gv in func.global_vars.keys() {
any = true;
writeln!(w, " {} = {}", gv, func.global_vars[gv])?;
}
// Write out all signatures before functions since function declarations can refer to
// signatures.
for sig in func.dfg.signatures.keys() {
@@ -244,6 +249,7 @@ pub fn write_operands(w: &mut Write,
UnaryIeee32 { imm, .. } => write!(w, " {}", imm),
UnaryIeee64 { imm, .. } => write!(w, " {}", imm),
UnaryBool { imm, .. } => write!(w, " {}", imm),
UnaryGlobalVar { global_var, .. } => write!(w, " {}", global_var),
Binary { args, .. } => write!(w, " {}, {}", args[0], args[1]),
BinaryImm { arg, imm, .. } => write!(w, " {}, {}", arg, imm),
Ternary { args, .. } => write!(w, " {}, {}, {}", args[0], args[1], args[2]),
@@ -338,7 +344,6 @@ pub fn write_operands(w: &mut Write,
write!(w, " {}, %{} -> %{}", arg, src, dst)
}
}
}
}

View File

@@ -37,6 +37,7 @@ pub enum Token<'a> {
Value(Value), // v12, v7
Ebb(Ebb), // ebb3
StackSlot(u32), // ss3
GlobalVar(u32), // gv3
JumpTable(u32), // jt2
FuncRef(u32), // fn2
SigRef(u32), // sig2
@@ -308,6 +309,7 @@ impl<'a> Lexer<'a> {
"v" => Value::with_number(number).map(Token::Value),
"ebb" => Ebb::with_number(number).map(Token::Ebb),
"ss" => Some(Token::StackSlot(number)),
"gv" => Some(Token::GlobalVar(number)),
"jt" => Some(Token::JumpTable(number)),
"fn" => Some(Token::FuncRef(number)),
"sig" => Some(Token::SigRef(number)),

View File

@@ -11,7 +11,8 @@ use std::{u16, u32};
use std::mem;
use cretonne::ir::{Function, Ebb, Opcode, Value, Type, FunctionName, CallConv, StackSlotData,
JumpTable, JumpTableData, Signature, ArgumentType, ArgumentExtension,
ExtFuncData, SigRef, FuncRef, StackSlot, ValueLoc, ArgumentLoc, MemFlags};
ExtFuncData, SigRef, FuncRef, StackSlot, ValueLoc, ArgumentLoc, MemFlags,
GlobalVar, GlobalVarData};
use cretonne::ir::types::VOID;
use cretonne::ir::immediates::{Imm64, Offset32, Uoffset32, Ieee32, Ieee64};
use cretonne::ir::entities::AnyEntity;
@@ -127,6 +128,20 @@ impl<'a> Context<'a> {
}
}
// Allocate a global variable slot and add a mapping number -> GlobalVar.
fn add_gv(&mut self, number: u32, data: GlobalVarData, loc: &Location) -> Result<()> {
self.map
.def_gv(number, self.function.global_vars.push(data), loc)
}
// Resolve a reference to a global variable.
fn get_gv(&self, number: u32, loc: &Location) -> Result<GlobalVar> {
match self.map.get_gv(number) {
Some(gv) => Ok(gv),
None => err!(loc, "undefined stack slot ss{}", number),
}
}
// Allocate a new signature and add a mapping number -> SigRef.
fn add_sig(&mut self, number: u32, data: Signature, loc: &Location) -> Result<()> {
self.map
@@ -228,6 +243,18 @@ impl<'a> Context<'a> {
}
}
// Rewrite base references in `deref` globals. Other `GlobalVar` references are already
// resolved.
for gv in self.function.global_vars.keys() {
let loc = gv.into();
match self.function.global_vars[gv] {
GlobalVarData::Deref { ref mut base, .. } => {
self.map.rewrite_gv(base, loc)?;
}
_ => {}
}
}
Ok(())
}
}
@@ -344,6 +371,16 @@ impl<'a> Parser<'a> {
}
}
// Match and consume a global variable reference.
fn match_gv(&mut self, err_msg: &str) -> Result<u32> {
if let Some(Token::GlobalVar(gv)) = self.token() {
self.consume();
Ok(gv)
} else {
err!(self.loc, err_msg)
}
}
// Match and consume a function reference.
fn match_fn(&mut self, err_msg: &str) -> Result<u32> {
if let Some(Token::FuncRef(fnref)) = self.token() {
@@ -903,6 +940,11 @@ impl<'a> Parser<'a> {
self.parse_stack_slot_decl()
.and_then(|(num, dat)| ctx.add_ss(num, dat, &loc))
}
Some(Token::GlobalVar(..)) => {
self.gather_comments(ctx.function.global_vars.next_key());
self.parse_global_var_decl()
.and_then(|(num, dat)| ctx.add_gv(num, dat, &self.loc))
}
Some(Token::SigRef(..)) => {
self.gather_comments(ctx.function.dfg.signatures.next_key());
self.parse_signature_decl(ctx.unique_isa)
@@ -959,6 +1001,40 @@ impl<'a> Parser<'a> {
Ok((number, data))
}
// Parse a global variable decl.
//
// global-var-decl ::= * GlobalVar(gv) "=" global-var-desc
// global-var-desc ::= "vmctx" offset32
// | "deref" "(" GlobalVar(base) ")" offset32
//
fn parse_global_var_decl(&mut self) -> Result<(u32, GlobalVarData)> {
let number = self.match_gv("expected global variable number: gv«n»")?;
self.match_token(Token::Equal, "expected '=' in global variable declaration")?;
let data = match self.match_any_identifier("expected global variable kind")? {
"vmctx" => {
let offset = self.optional_offset32()?;
GlobalVarData::VmCtx { offset }
}
"deref" => {
self.match_token(Token::LPar, "expected '(' in 'deref' global variable decl")?;
let base_num = self.match_gv("expected global variable: gv«n»")?;
// The base global variable may not have been declared yet, so create a fake
// reference using the source number. We'll rewrite these later.
let base = match GlobalVar::with_number(base_num) {
Some(gv) => gv,
None => return err!(self.loc, "Invalid global variable number for deref base"),
};
self.match_token(Token::RPar, "expected ')' in 'deref' global variable decl")?;
let offset = self.optional_offset32()?;
GlobalVarData::Deref { base, offset }
}
other => return err!(self.loc, "Unknown global variable kind '{}'", other),
};
Ok((number, data))
}
// Parse a signature decl.
//
// signature-decl ::= SigRef(sigref) "=" signature
@@ -1517,6 +1593,13 @@ impl<'a> Parser<'a> {
imm: self.match_bool("expected immediate boolean operand")?,
}
}
InstructionFormat::UnaryGlobalVar => {
InstructionData::UnaryGlobalVar {
opcode,
global_var: self.match_gv("expected global variable")
.and_then(|num| ctx.get_gv(num, &self.loc))?,
}
}
InstructionFormat::Binary => {
let lhs = self.match_value("expected SSA value first operand")?;
self.match_token(Token::Comma, "expected ',' between operands")?;

View File

@@ -7,11 +7,12 @@
//! The `SourceMap` struct defined in this module makes the same mapping available to parser
//! clients.
use std::collections::HashMap;
use cretonne::ir::{StackSlot, JumpTable, Ebb, Value, SigRef, FuncRef};
use cretonne::entity_ref::EntityRef;
use cretonne::ir::entities::AnyEntity;
use cretonne::ir::{StackSlot, GlobalVar, JumpTable, Ebb, Value, SigRef, FuncRef};
use error::{Result, Location};
use lexer::split_entity_name;
use std::collections::HashMap;
/// Mapping from source entity names to entity references that are valid in the parsed function.
#[derive(Debug)]
@@ -19,6 +20,7 @@ pub struct SourceMap {
values: HashMap<Value, Value>, // vNN
ebbs: HashMap<Ebb, Ebb>, // ebbNN
stack_slots: HashMap<u32, StackSlot>, // ssNN
global_vars: HashMap<u32, GlobalVar>, // gvNN
signatures: HashMap<u32, SigRef>, // sigNN
functions: HashMap<u32, FuncRef>, // fnNN
jump_tables: HashMap<u32, JumpTable>, // jtNN
@@ -44,6 +46,11 @@ impl SourceMap {
self.stack_slots.get(&src_num).cloned()
}
/// Look up a global variable entity by its source number.
pub fn get_gv(&self, src_num: u32) -> Option<GlobalVar> {
self.global_vars.get(&src_num).cloned()
}
/// Look up a signature entity by its source number.
pub fn get_sig(&self, src_num: u32) -> Option<SigRef> {
self.signatures.get(&src_num).cloned()
@@ -74,6 +81,7 @@ impl SourceMap {
.map(AnyEntity::Ebb)
}
"ss" => self.get_ss(num).map(AnyEntity::StackSlot),
"gv" => self.get_gv(num).map(AnyEntity::GlobalVar),
"sig" => self.get_sig(num).map(AnyEntity::SigRef),
"fn" => self.get_fn(num).map(AnyEntity::FuncRef),
"jt" => self.get_jt(num).map(AnyEntity::JumpTable),
@@ -124,6 +132,21 @@ impl SourceMap {
}
Ok(())
}
/// Rewrite a `GlobalVar` reference.
pub fn rewrite_gv(&self, gv: &mut GlobalVar, loc: AnyEntity) -> Result<()> {
match self.get_gv(gv.index() as u32) {
Some(new) => {
*gv = new;
Ok(())
}
None => {
err!(self.location(loc).unwrap_or_default(),
"undefined reference: {}",
gv)
}
}
}
}
@@ -137,6 +160,7 @@ pub trait MutableSourceMap {
fn def_value(&mut self, src: Value, entity: Value, loc: &Location) -> Result<()>;
fn def_ebb(&mut self, src: Ebb, entity: Ebb, loc: &Location) -> Result<()>;
fn def_ss(&mut self, src_num: u32, entity: StackSlot, loc: &Location) -> Result<()>;
fn def_gv(&mut self, src_num: u32, entity: GlobalVar, loc: &Location) -> Result<()>;
fn def_sig(&mut self, src_num: u32, entity: SigRef, loc: &Location) -> Result<()>;
fn def_fn(&mut self, src_num: u32, entity: FuncRef, loc: &Location) -> Result<()>;
fn def_jt(&mut self, src_num: u32, entity: JumpTable, loc: &Location) -> Result<()>;
@@ -152,6 +176,7 @@ impl MutableSourceMap for SourceMap {
values: HashMap::new(),
ebbs: HashMap::new(),
stack_slots: HashMap::new(),
global_vars: HashMap::new(),
signatures: HashMap::new(),
functions: HashMap::new(),
jump_tables: HashMap::new(),
@@ -183,6 +208,14 @@ impl MutableSourceMap for SourceMap {
}
}
fn def_gv(&mut self, src_num: u32, entity: GlobalVar, loc: &Location) -> Result<()> {
if self.global_vars.insert(src_num, entity).is_some() {
err!(loc, "duplicate global variable: gv{}", src_num)
} else {
self.def_entity(entity.into(), loc)
}
}
fn def_sig(&mut self, src_num: u32, entity: SigRef, loc: &Location) -> Result<()> {
if self.signatures.insert(src_num, entity).is_some() {
err!(loc, "duplicate signature: sig{}", src_num)