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:
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
-----
|
-----
|
||||||
|
|
||||||
|
|||||||
37
cranelift/filetests/parser/memory.cton
Normal file
37
cranelift/filetests/parser/memory.cton
Normal 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
|
||||||
|
}
|
||||||
@@ -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.')
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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.
|
||||||
#
|
#
|
||||||
|
|||||||
@@ -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.
|
||||||
""",
|
""",
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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(),
|
||||||
|
|||||||
37
lib/cretonne/src/ir/globalvar.rs
Normal file
37
lib/cretonne/src/ir/globalvar.rs
Normal 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),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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,
|
||||||
|
|||||||
@@ -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>;
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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)),
|
||||||
|
|||||||
@@ -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")?;
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
Reference in New Issue
Block a user