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." @echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
autohtml: 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: dirhtml:
$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml

View File

@@ -495,6 +495,62 @@ instructions before instruction selection::
v9 = stack_addr ss3, 16 v9 = stack_addr ss3, 16
v1 = load.f64 v9 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 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. #: A reference to a stack slot declared in the function preamble.
stack_slot = EntityRefKind('stack_slot', 'A stack slot.') 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. #: A reference to a function sugnature declared in the function preamble.
#: Tbis is used to provide the call signature in an indirect call instruction. #: Tbis is used to provide the call signature in an indirect call instruction.
sig_ref = EntityRefKind('sig_ref', 'A function signature.') 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 cdsl.operands import VALUE, VARIABLE_ARGS
from .immediates import imm64, uimm8, ieee32, ieee64, offset32, uoffset32 from .immediates import imm64, uimm8, ieee32, ieee64, offset32, uoffset32
from .immediates import boolean, intcc, floatcc, memflags, regunit 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() Nullary = InstructionFormat()
@@ -19,6 +20,7 @@ UnaryImm = InstructionFormat(imm64)
UnaryIeee32 = InstructionFormat(ieee32) UnaryIeee32 = InstructionFormat(ieee32)
UnaryIeee64 = InstructionFormat(ieee64) UnaryIeee64 = InstructionFormat(ieee64)
UnaryBool = InstructionFormat(boolean) UnaryBool = InstructionFormat(boolean)
UnaryGlobalVar = InstructionFormat(entities.global_var)
Binary = InstructionFormat(VALUE, VALUE) Binary = InstructionFormat(VALUE, VALUE)
BinaryImm = InstructionFormat(VALUE, imm64) BinaryImm = InstructionFormat(VALUE, imm64)
@@ -42,7 +44,7 @@ FloatCompare = InstructionFormat(floatcc, VALUE, VALUE)
Jump = InstructionFormat(ebb, VARIABLE_ARGS) Jump = InstructionFormat(ebb, VARIABLE_ARGS)
Branch = InstructionFormat(VALUE, ebb, VARIABLE_ARGS) Branch = InstructionFormat(VALUE, ebb, VARIABLE_ARGS)
BranchIcmp = InstructionFormat(intcc, VALUE, 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) Call = InstructionFormat(func_ref, VARIABLE_ARGS)
IndirectCall = InstructionFormat(sig_ref, VALUE, VARIABLE_ARGS) IndirectCall = InstructionFormat(sig_ref, VALUE, VARIABLE_ARGS)

View File

@@ -345,6 +345,18 @@ stack_addr = Instruction(
""", """,
ins=(SS, Offset), outs=addr) 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. # WebAssembly bounds-checked heap accesses.
# #

View File

@@ -59,7 +59,7 @@ VARIABLE_ARGS = OperandKind(
'variable_args', """ 'variable_args', """
A variable size list of `value` operands. 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 passed to an extended basic block, or a variable number of results
returned from an instruction. returned from an instruction.
""", """,

View File

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

View File

@@ -65,6 +65,24 @@ entity_impl!(Inst, "inst");
pub struct StackSlot(u32); pub struct StackSlot(u32);
entity_impl!(StackSlot, "ss"); 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. /// An opaque reference to a jump table.
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub struct JumpTable(u32); pub struct JumpTable(u32);
@@ -93,6 +111,8 @@ pub enum AnyEntity {
Value(Value), Value(Value),
/// A stack slot. /// A stack slot.
StackSlot(StackSlot), StackSlot(StackSlot),
/// A Global variable.
GlobalVar(GlobalVar),
/// A jump table. /// A jump table.
JumpTable(JumpTable), JumpTable(JumpTable),
/// An external function. /// An external function.
@@ -109,6 +129,7 @@ impl fmt::Display for AnyEntity {
AnyEntity::Inst(r) => r.fmt(f), AnyEntity::Inst(r) => r.fmt(f),
AnyEntity::Value(r) => r.fmt(f), AnyEntity::Value(r) => r.fmt(f),
AnyEntity::StackSlot(r) => r.fmt(f), AnyEntity::StackSlot(r) => r.fmt(f),
AnyEntity::GlobalVar(r) => r.fmt(f),
AnyEntity::JumpTable(r) => r.fmt(f), AnyEntity::JumpTable(r) => r.fmt(f),
AnyEntity::FuncRef(r) => r.fmt(f), AnyEntity::FuncRef(r) => r.fmt(f),
AnyEntity::SigRef(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 { impl From<JumpTable> for AnyEntity {
fn from(r: JumpTable) -> AnyEntity { fn from(r: JumpTable) -> AnyEntity {
AnyEntity::JumpTable(r) AnyEntity::JumpTable(r)

View File

@@ -4,8 +4,8 @@
//! instructions. //! instructions.
use entity_map::{EntityMap, PrimaryEntityData}; use entity_map::{EntityMap, PrimaryEntityData};
use ir::{FunctionName, CallConv, Signature, JumpTableData, DataFlowGraph, Layout}; use ir::{FunctionName, CallConv, Signature, JumpTableData, GlobalVarData, DataFlowGraph, Layout};
use ir::{JumpTables, InstEncodings, ValueLocations, StackSlots, EbbOffsets}; use ir::{JumpTables, InstEncodings, ValueLocations, StackSlots, GlobalVars, EbbOffsets};
use isa::TargetIsa; use isa::TargetIsa;
use std::fmt; use std::fmt;
use write::write_function; use write::write_function;
@@ -25,6 +25,9 @@ pub struct Function {
/// Stack slots allocated in this function. /// Stack slots allocated in this function.
pub stack_slots: StackSlots, pub stack_slots: StackSlots,
/// Global variables referenced.
pub global_vars: GlobalVars,
/// Jump tables used in this function. /// Jump tables used in this function.
pub jump_tables: JumpTables, pub jump_tables: JumpTables,
@@ -50,6 +53,7 @@ pub struct Function {
} }
impl PrimaryEntityData for JumpTableData {} impl PrimaryEntityData for JumpTableData {}
impl PrimaryEntityData for GlobalVarData {}
impl Function { impl Function {
/// Create a function with the given name and signature. /// Create a function with the given name and signature.
@@ -58,6 +62,7 @@ impl Function {
name, name,
signature: sig, signature: sig,
stack_slots: StackSlots::new(), stack_slots: StackSlots::new(),
global_vars: GlobalVars::new(),
jump_tables: EntityMap::new(), jump_tables: EntityMap::new(),
dfg: DataFlowGraph::new(), dfg: DataFlowGraph::new(),
layout: Layout::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 }, UnaryIeee32 { opcode: Opcode, imm: Ieee32 },
UnaryIeee64 { opcode: Opcode, imm: Ieee64 }, UnaryIeee64 { opcode: Opcode, imm: Ieee64 },
UnaryBool { opcode: Opcode, imm: bool }, UnaryBool { opcode: Opcode, imm: bool },
UnaryGlobalVar {
opcode: Opcode,
global_var: ir::GlobalVar,
},
Binary { opcode: Opcode, args: [Value; 2] }, Binary { opcode: Opcode, args: [Value; 2] },
BinaryImm { BinaryImm {
opcode: Opcode, opcode: Opcode,

View File

@@ -13,17 +13,19 @@ pub mod function;
mod builder; mod builder;
mod extfunc; mod extfunc;
mod funcname; mod funcname;
mod globalvar;
mod memflags; mod memflags;
mod progpoint; mod progpoint;
mod valueloc; mod valueloc;
pub use ir::builder::{InstBuilder, InstBuilderBase, InstInserterBase, InsertBuilder}; pub use ir::builder::{InstBuilder, InstBuilderBase, InstInserterBase, InsertBuilder};
pub use ir::dfg::{DataFlowGraph, ValueDef}; 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, pub use ir::extfunc::{Signature, CallConv, ArgumentType, ArgumentExtension, ArgumentPurpose,
ExtFuncData}; ExtFuncData};
pub use ir::funcname::FunctionName; pub use ir::funcname::FunctionName;
pub use ir::function::Function; pub use ir::function::Function;
pub use ir::globalvar::GlobalVarData;
pub use ir::instructions::{Opcode, InstructionData, VariableArgs, ValueList, ValueListPool}; pub use ir::instructions::{Opcode, InstructionData, VariableArgs, ValueList, ValueListPool};
pub use ir::jumptable::JumpTableData; pub use ir::jumptable::JumpTableData;
pub use ir::layout::{Layout, CursorBase, Cursor}; pub use ir::layout::{Layout, CursorBase, Cursor};
@@ -48,3 +50,6 @@ pub type InstEncodings = EntityMap<Inst, isa::Encoding>;
/// Code offsets for EBBs. /// Code offsets for EBBs.
pub type EbbOffsets = EntityMap<Ebb, binemit::CodeOffset>; 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::entities::AnyEntity;
use ir::instructions::{InstructionFormat, BranchInfo, ResolvedConstraint, CallInfo}; use ir::instructions::{InstructionFormat, BranchInfo, ResolvedConstraint, CallInfo};
use ir::{types, Function, ValueDef, Ebb, Inst, SigRef, FuncRef, ValueList, JumpTable, StackSlot, 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 isa::TargetIsa;
use std::error as std_error; use std::error as std_error;
use std::fmt::{self, Display, Formatter}; use std::fmt::{self, Display, Formatter};
@@ -270,6 +270,9 @@ impl<'a> Verifier<'a> {
StackStore { stack_slot, .. } => { StackStore { stack_slot, .. } => {
self.verify_stack_slot(inst, 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 // Exhaustive list so we can't forget to add new formats
Nullary { .. } | 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 { fn verify_value_list(&self, inst: Inst, l: &ValueList) -> Result {
if !l.is_valid(&self.func.dfg.value_lists) { if !l.is_valid(&self.func.dfg.value_lists) {
err!(inst, "invalid value list reference {:?}", l) 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])?; 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 // Write out all signatures before functions since function declarations can refer to
// signatures. // signatures.
for sig in func.dfg.signatures.keys() { for sig in func.dfg.signatures.keys() {
@@ -244,6 +249,7 @@ pub fn write_operands(w: &mut Write,
UnaryIeee32 { imm, .. } => write!(w, " {}", imm), UnaryIeee32 { imm, .. } => write!(w, " {}", imm),
UnaryIeee64 { imm, .. } => write!(w, " {}", imm), UnaryIeee64 { imm, .. } => write!(w, " {}", imm),
UnaryBool { imm, .. } => write!(w, " {}", imm), UnaryBool { imm, .. } => write!(w, " {}", imm),
UnaryGlobalVar { global_var, .. } => write!(w, " {}", global_var),
Binary { args, .. } => write!(w, " {}, {}", args[0], args[1]), Binary { args, .. } => write!(w, " {}, {}", args[0], args[1]),
BinaryImm { arg, imm, .. } => write!(w, " {}, {}", arg, imm), BinaryImm { arg, imm, .. } => write!(w, " {}, {}", arg, imm),
Ternary { args, .. } => write!(w, " {}, {}, {}", args[0], args[1], args[2]), 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) write!(w, " {}, %{} -> %{}", arg, src, dst)
} }
} }
} }
} }

View File

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

View File

@@ -11,7 +11,8 @@ use std::{u16, u32};
use std::mem; use std::mem;
use cretonne::ir::{Function, Ebb, Opcode, Value, Type, FunctionName, CallConv, StackSlotData, use cretonne::ir::{Function, Ebb, Opcode, Value, Type, FunctionName, CallConv, StackSlotData,
JumpTable, JumpTableData, Signature, ArgumentType, ArgumentExtension, 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::types::VOID;
use cretonne::ir::immediates::{Imm64, Offset32, Uoffset32, Ieee32, Ieee64}; use cretonne::ir::immediates::{Imm64, Offset32, Uoffset32, Ieee32, Ieee64};
use cretonne::ir::entities::AnyEntity; 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. // Allocate a new signature and add a mapping number -> SigRef.
fn add_sig(&mut self, number: u32, data: Signature, loc: &Location) -> Result<()> { fn add_sig(&mut self, number: u32, data: Signature, loc: &Location) -> Result<()> {
self.map 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(()) 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. // Match and consume a function reference.
fn match_fn(&mut self, err_msg: &str) -> Result<u32> { fn match_fn(&mut self, err_msg: &str) -> Result<u32> {
if let Some(Token::FuncRef(fnref)) = self.token() { if let Some(Token::FuncRef(fnref)) = self.token() {
@@ -903,6 +940,11 @@ impl<'a> Parser<'a> {
self.parse_stack_slot_decl() self.parse_stack_slot_decl()
.and_then(|(num, dat)| ctx.add_ss(num, dat, &loc)) .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(..)) => { Some(Token::SigRef(..)) => {
self.gather_comments(ctx.function.dfg.signatures.next_key()); self.gather_comments(ctx.function.dfg.signatures.next_key());
self.parse_signature_decl(ctx.unique_isa) self.parse_signature_decl(ctx.unique_isa)
@@ -959,6 +1001,40 @@ impl<'a> Parser<'a> {
Ok((number, data)) 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. // Parse a signature decl.
// //
// signature-decl ::= SigRef(sigref) "=" signature // signature-decl ::= SigRef(sigref) "=" signature
@@ -1517,6 +1593,13 @@ impl<'a> Parser<'a> {
imm: self.match_bool("expected immediate boolean operand")?, 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 => { InstructionFormat::Binary => {
let lhs = self.match_value("expected SSA value first operand")?; let lhs = self.match_value("expected SSA value first operand")?;
self.match_token(Token::Comma, "expected ',' between operands")?; 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 //! The `SourceMap` struct defined in this module makes the same mapping available to parser
//! clients. //! clients.
use std::collections::HashMap; use cretonne::entity_ref::EntityRef;
use cretonne::ir::{StackSlot, JumpTable, Ebb, Value, SigRef, FuncRef};
use cretonne::ir::entities::AnyEntity; use cretonne::ir::entities::AnyEntity;
use cretonne::ir::{StackSlot, GlobalVar, JumpTable, Ebb, Value, SigRef, FuncRef};
use error::{Result, Location}; use error::{Result, Location};
use lexer::split_entity_name; use lexer::split_entity_name;
use std::collections::HashMap;
/// Mapping from source entity names to entity references that are valid in the parsed function. /// Mapping from source entity names to entity references that are valid in the parsed function.
#[derive(Debug)] #[derive(Debug)]
@@ -19,6 +20,7 @@ pub struct SourceMap {
values: HashMap<Value, Value>, // vNN values: HashMap<Value, Value>, // vNN
ebbs: HashMap<Ebb, Ebb>, // ebbNN ebbs: HashMap<Ebb, Ebb>, // ebbNN
stack_slots: HashMap<u32, StackSlot>, // ssNN stack_slots: HashMap<u32, StackSlot>, // ssNN
global_vars: HashMap<u32, GlobalVar>, // gvNN
signatures: HashMap<u32, SigRef>, // sigNN signatures: HashMap<u32, SigRef>, // sigNN
functions: HashMap<u32, FuncRef>, // fnNN functions: HashMap<u32, FuncRef>, // fnNN
jump_tables: HashMap<u32, JumpTable>, // jtNN jump_tables: HashMap<u32, JumpTable>, // jtNN
@@ -44,6 +46,11 @@ impl SourceMap {
self.stack_slots.get(&src_num).cloned() 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. /// Look up a signature entity by its source number.
pub fn get_sig(&self, src_num: u32) -> Option<SigRef> { pub fn get_sig(&self, src_num: u32) -> Option<SigRef> {
self.signatures.get(&src_num).cloned() self.signatures.get(&src_num).cloned()
@@ -74,6 +81,7 @@ impl SourceMap {
.map(AnyEntity::Ebb) .map(AnyEntity::Ebb)
} }
"ss" => self.get_ss(num).map(AnyEntity::StackSlot), "ss" => self.get_ss(num).map(AnyEntity::StackSlot),
"gv" => self.get_gv(num).map(AnyEntity::GlobalVar),
"sig" => self.get_sig(num).map(AnyEntity::SigRef), "sig" => self.get_sig(num).map(AnyEntity::SigRef),
"fn" => self.get_fn(num).map(AnyEntity::FuncRef), "fn" => self.get_fn(num).map(AnyEntity::FuncRef),
"jt" => self.get_jt(num).map(AnyEntity::JumpTable), "jt" => self.get_jt(num).map(AnyEntity::JumpTable),
@@ -124,6 +132,21 @@ impl SourceMap {
} }
Ok(()) 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_value(&mut self, src: Value, entity: Value, loc: &Location) -> Result<()>;
fn def_ebb(&mut self, src: Ebb, entity: Ebb, 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_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_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_fn(&mut self, src_num: u32, entity: FuncRef, loc: &Location) -> Result<()>;
fn def_jt(&mut self, src_num: u32, entity: JumpTable, 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(), values: HashMap::new(),
ebbs: HashMap::new(), ebbs: HashMap::new(),
stack_slots: HashMap::new(), stack_slots: HashMap::new(),
global_vars: HashMap::new(),
signatures: HashMap::new(), signatures: HashMap::new(),
functions: HashMap::new(), functions: HashMap::new(),
jump_tables: 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<()> { fn def_sig(&mut self, src_num: u32, entity: SigRef, loc: &Location) -> Result<()> {
if self.signatures.insert(src_num, entity).is_some() { if self.signatures.insert(src_num, entity).is_some() {
err!(loc, "duplicate signature: sig{}", src_num) err!(loc, "duplicate signature: sig{}", src_num)