From bf4ae3bb2e295720bc4d3a5e46ef6fb91369049c Mon Sep 17 00:00:00 2001 From: Jakob Stoklund Olesen Date: Thu, 17 Aug 2017 11:30:00 -0700 Subject: [PATCH] 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. --- cranelift/docs/Makefile | 2 +- cranelift/docs/langref.rst | 56 +++++++++++++++++ cranelift/filetests/parser/memory.cton | 37 +++++++++++ lib/cretonne/meta/base/entities.py | 3 + lib/cretonne/meta/base/formats.py | 6 +- lib/cretonne/meta/base/instructions.py | 12 ++++ lib/cretonne/meta/cdsl/operands.py | 2 +- lib/cretonne/src/ir/builder.rs | 4 +- lib/cretonne/src/ir/entities.rs | 27 ++++++++ lib/cretonne/src/ir/function.rs | 9 ++- lib/cretonne/src/ir/globalvar.rs | 37 +++++++++++ lib/cretonne/src/ir/instructions.rs | 4 ++ lib/cretonne/src/ir/mod.rs | 7 ++- lib/cretonne/src/verifier/mod.rs | 13 +++- lib/cretonne/src/write.rs | 7 ++- lib/reader/src/lexer.rs | 2 + lib/reader/src/parser.rs | 85 +++++++++++++++++++++++++- lib/reader/src/sourcemap.rs | 37 ++++++++++- 18 files changed, 336 insertions(+), 14 deletions(-) create mode 100644 cranelift/filetests/parser/memory.cton create mode 100644 lib/cretonne/src/ir/globalvar.rs diff --git a/cranelift/docs/Makefile b/cranelift/docs/Makefile index 8cec8c36e6..635aafde1c 100644 --- a/cranelift/docs/Makefile +++ b/cranelift/docs/Makefile @@ -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 diff --git a/cranelift/docs/langref.rst b/cranelift/docs/langref.rst index 5ad4d89247..d1d353186a 100644 --- a/cranelift/docs/langref.rst +++ b/cranelift/docs/langref.rst @@ -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 ----- diff --git a/cranelift/filetests/parser/memory.cton b/cranelift/filetests/parser/memory.cton new file mode 100644 index 0000000000..5f300394b3 --- /dev/null +++ b/cranelift/filetests/parser/memory.cton @@ -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 +} diff --git a/lib/cretonne/meta/base/entities.py b/lib/cretonne/meta/base/entities.py index 327d45e8bc..6a9c1ff5d2 100644 --- a/lib/cretonne/meta/base/entities.py +++ b/lib/cretonne/meta/base/entities.py @@ -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.') diff --git a/lib/cretonne/meta/base/formats.py b/lib/cretonne/meta/base/formats.py index 16732c1153..62572f5a02 100644 --- a/lib/cretonne/meta/base/formats.py +++ b/lib/cretonne/meta/base/formats.py @@ -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) diff --git a/lib/cretonne/meta/base/instructions.py b/lib/cretonne/meta/base/instructions.py index a303045331..4e80641d76 100644 --- a/lib/cretonne/meta/base/instructions.py +++ b/lib/cretonne/meta/base/instructions.py @@ -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. # diff --git a/lib/cretonne/meta/cdsl/operands.py b/lib/cretonne/meta/cdsl/operands.py index 2ceb94b0fa..c80632f22a 100644 --- a/lib/cretonne/meta/cdsl/operands.py +++ b/lib/cretonne/meta/cdsl/operands.py @@ -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. """, diff --git a/lib/cretonne/src/ir/builder.rs b/lib/cretonne/src/ir/builder.rs index 697acfebd5..3673788543 100644 --- a/lib/cretonne/src/ir/builder.rs +++ b/lib/cretonne/src/ir/builder.rs @@ -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; diff --git a/lib/cretonne/src/ir/entities.rs b/lib/cretonne/src/ir/entities.rs index 98a29c7e61..628a442fdc 100644 --- a/lib/cretonne/src/ir/entities.rs +++ b/lib/cretonne/src/ir/entities.rs @@ -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 { + 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 for AnyEntity { } } +impl From for AnyEntity { + fn from(r: GlobalVar) -> AnyEntity { + AnyEntity::GlobalVar(r) + } +} + impl From for AnyEntity { fn from(r: JumpTable) -> AnyEntity { AnyEntity::JumpTable(r) diff --git a/lib/cretonne/src/ir/function.rs b/lib/cretonne/src/ir/function.rs index c2b280dc6c..028c376479 100644 --- a/lib/cretonne/src/ir/function.rs +++ b/lib/cretonne/src/ir/function.rs @@ -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(), diff --git a/lib/cretonne/src/ir/globalvar.rs b/lib/cretonne/src/ir/globalvar.rs new file mode 100644 index 0000000000..ad209b0eca --- /dev/null +++ b/lib/cretonne/src/ir/globalvar.rs @@ -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), + } + } +} diff --git a/lib/cretonne/src/ir/instructions.rs b/lib/cretonne/src/ir/instructions.rs index 900dd56e88..b7c5d898e5 100644 --- a/lib/cretonne/src/ir/instructions.rs +++ b/lib/cretonne/src/ir/instructions.rs @@ -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, diff --git a/lib/cretonne/src/ir/mod.rs b/lib/cretonne/src/ir/mod.rs index 443d70bd1c..1e31e34010 100644 --- a/lib/cretonne/src/ir/mod.rs +++ b/lib/cretonne/src/ir/mod.rs @@ -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; /// Code offsets for EBBs. pub type EbbOffsets = EntityMap; + +/// Map of global variables. +pub type GlobalVars = EntityMap; diff --git a/lib/cretonne/src/verifier/mod.rs b/lib/cretonne/src/verifier/mod.rs index be95456212..af8bcd0c7e 100644 --- a/lib/cretonne/src/verifier/mod.rs +++ b/lib/cretonne/src/verifier/mod.rs @@ -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) diff --git a/lib/cretonne/src/write.rs b/lib/cretonne/src/write.rs index c342c4f273..d0166a1063 100644 --- a/lib/cretonne/src/write.rs +++ b/lib/cretonne/src/write.rs @@ -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) } } - } } diff --git a/lib/reader/src/lexer.rs b/lib/reader/src/lexer.rs index d4392a5bd9..7e5fc4f827 100644 --- a/lib/reader/src/lexer.rs +++ b/lib/reader/src/lexer.rs @@ -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)), diff --git a/lib/reader/src/parser.rs b/lib/reader/src/parser.rs index fe2aec3e38..0d96861b51 100644 --- a/lib/reader/src/parser.rs +++ b/lib/reader/src/parser.rs @@ -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 { + 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 { + 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 { 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")?; diff --git a/lib/reader/src/sourcemap.rs b/lib/reader/src/sourcemap.rs index d590a5220f..b085a78c1b 100644 --- a/lib/reader/src/sourcemap.rs +++ b/lib/reader/src/sourcemap.rs @@ -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, // vNN ebbs: HashMap, // ebbNN stack_slots: HashMap, // ssNN + global_vars: HashMap, // gvNN signatures: HashMap, // sigNN functions: HashMap, // fnNN jump_tables: HashMap, // 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 { + 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 { 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)