Browse Source

Add load and store instructions.

Define a MemFlags class, currently holding a notrap and aligned flag.
pull/3/head
Jakob Stoklund Olesen 8 years ago
parent
commit
9d9807688c
  1. 36
      docs/langref.rst
  2. 27
      filetests/parser/tiny.cton
  3. 5
      lib/cretonne/meta/base/formats.py
  4. 6
      lib/cretonne/meta/base/immediates.py
  5. 21
      lib/cretonne/meta/base/instructions.py
  6. 3
      lib/cretonne/src/ir/builder.rs
  7. 16
      lib/cretonne/src/ir/instructions.rs
  8. 92
      lib/cretonne/src/ir/memflags.rs
  9. 8
      lib/cretonne/src/ir/mod.rs
  10. 4
      lib/cretonne/src/verifier.rs
  11. 7
      lib/cretonne/src/write.rs
  12. 47
      lib/reader/src/parser.rs

36
docs/langref.rst

@ -428,46 +428,14 @@ accessing memory. However, it can be very complicated to verify the safety of
general loads and stores when compiling code for a sandboxed environment, so
Cretonne also provides more restricted memory operations that are always safe.
.. inst:: a = load p, Offset, Flags...
Load from memory at ``p + Offset``.
This is a polymorphic instruction that can load any value type which has a
memory representation.
:arg iPtr p: Base address.
:arg Offset: Immediate signed offset.
:flag align(N): Expected alignment of ``p + Offset``. Power of two.
:flag aligntrap: Always trap if the memory access is misaligned.
:result T a: Loaded value.
.. inst:: store x, p, Offset, Flags...
Store ``x`` to memory at ``p + Offset``.
This is a polymorphic instruction that can store any value type with a
memory representation.
:arg T x: Value to store.
:arg iPtr p: Base address.
:arg Offset: Immediate signed offset.
:flag align(N): Expected alignment of ``p + Offset``. Power of two.
:flag aligntrap: Always trap if the memory access is misaligned.
.. autoinst:: load
.. autoinst:: store
Loads and stores are *misaligned* if the resultant address is not a multiple of
the expected alignment. Depending on the target architecture, misaligned memory
accesses may trap, or they may work. Sometimes, operating systems catch
alignment traps and emulate the misaligned memory access.
On target architectures like x86 that don't check alignment, Cretonne expands
the `aligntrap` flag into a conditional trap instruction::
v5 = load.i32 v1, 4, align(4), aligntrap
; Becomes:
v10 = and_imm v1, 3
trapnz v10
v5 = load.i32 v1, 4
Local variables
---------------

27
filetests/parser/tiny.cton

@ -128,3 +128,30 @@ ebb0(v1: i32):
; nextln: $v2 = heap_load.f32 $v1
; nextln: $v3 = heap_load.f32 $v1+12
; nextln: heap_store $v3, $v1
; Memory access instructions.
function memory(i32) {
ebb0(v1: i32):
v2 = load.i64 v1
v3 = load.i64 aligned v1
v4 = load.i64 notrap v1
v5 = load.i64 notrap aligned v1
v6 = load.i64 aligned notrap v1
v7 = load.i64 v1-12
v8 = load.i64 notrap v1+0x1_0000
store v2, v1
store aligned v3, v1+12
store notrap aligned v3, v1-12
}
; sameln: function memory(i32) {
; nextln: ebb0($v1: i32):
; nextln: $v2 = load.i64 $v1
; nextln: $v3 = load.i64 aligned $v1
; nextln: $v4 = load.i64 notrap $v1
; nextln: $v5 = load.i64 notrap aligned $v1
; nextln: $v6 = load.i64 notrap aligned $v1
; nextln: $v7 = load.i64 $v1-12
; nextln: $v8 = load.i64 notrap $v1+0x0001_0000
; nextln: store $v2, $v1
; nextln: store aligned $v3, $v1+12
; nextln: store notrap aligned $v3, $v1-12

5
lib/cretonne/meta/base/formats.py

@ -9,7 +9,7 @@ from __future__ import absolute_import
from cdsl.formats import InstructionFormat
from cdsl.operands import VALUE, VARIABLE_ARGS
from .immediates import imm64, uimm8, ieee32, ieee64, offset32, uoffset32
from .immediates import intcc, floatcc
from .immediates import intcc, floatcc, memflags
from .entities import ebb, sig_ref, func_ref, jump_table, stack_slot
Nullary = InstructionFormat()
@ -53,6 +53,9 @@ IndirectCall = InstructionFormat(
sig_ref, VALUE, VARIABLE_ARGS,
multiple_results=True)
Load = InstructionFormat(memflags, VALUE, offset32)
Store = InstructionFormat(memflags, VALUE, VALUE, offset32)
StackLoad = InstructionFormat(stack_slot, offset32)
StackStore = InstructionFormat(VALUE, stack_slot, offset32)

6
lib/cretonne/meta/base/immediates.py

@ -90,3 +90,9 @@ floatcc = ImmediateKind(
'ugt': 'UnorderedOrGreaterThan',
'uge': 'UnorderedOrGreaterThanOrEqual',
})
#: Flags for memory operations like :inst:`load` and :inst:`store`.
memflags = ImmediateKind(
'memflags',
'Memory operation flags',
default_member='flags', rust_type='MemFlags')

21
lib/cretonne/meta/base/instructions.py

@ -10,7 +10,7 @@ from cdsl.typevar import TypeVar
from cdsl.instructions import Instruction, InstructionGroup
from base.types import i8, f32, f64, b1
from base.immediates import imm64, uimm8, ieee32, ieee64, offset32, uoffset32
from base.immediates import intcc, floatcc
from base.immediates import intcc, floatcc, memflags
from base import entities
import base.formats # noqa
@ -211,6 +211,25 @@ x = Operand('x', Mem, doc='Value to be stored')
a = Operand('a', Mem, doc='Value loaded')
p = Operand('p', iAddr)
addr = Operand('addr', iAddr)
Flags = Operand('Flags', memflags)
load = Instruction(
'load', r"""
Load from memory at ``p + Offset``.
This is a polymorphic instruction that can load any value type which
has a memory representation.
""",
ins=(Flags, p, Offset), outs=a)
store = Instruction(
'store', r"""
Store ``x`` to memory at ``p + Offset``.
This is a polymorphic instruction that can store any value type with a
memory representation.
""",
ins=(Flags, x, p, Offset))
stack_load = Instruction(
'stack_load', r"""

3
lib/cretonne/src/ir/builder.rs

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

16
lib/cretonne/src/ir/instructions.rs

@ -10,7 +10,7 @@ use std::fmt::{self, Display, Formatter};
use std::str::FromStr;
use std::ops::{Deref, DerefMut};
use ir::{Value, Type, Ebb, JumpTable, SigRef, FuncRef, StackSlot};
use ir::{Value, Type, Ebb, JumpTable, SigRef, FuncRef, StackSlot, MemFlags};
use ir::immediates::{Imm64, Uimm8, Ieee32, Ieee64, Offset32, Uoffset32};
use ir::condcodes::*;
use ir::types;
@ -252,6 +252,20 @@ pub enum InstructionData {
args: [Value; 2],
offset: Uoffset32,
},
Load {
opcode: Opcode,
ty: Type,
flags: MemFlags,
arg: Value,
offset: Offset32,
},
Store {
opcode: Opcode,
ty: Type,
flags: MemFlags,
args: [Value; 2],
offset: Offset32,
},
}
/// A variable list of `Value` operands used for function call arguments and passing arguments to

92
lib/cretonne/src/ir/memflags.rs

@ -0,0 +1,92 @@
//! Memory operation flags.
use std::fmt;
enum FlagBit {
Notrap,
Aligned,
}
const NAMES: [&'static str; 2] = ["notrap", "aligned"];
/// Flags for memory operations like load/store.
///
/// Each of these flags introduce a limited form of undefined behavior. The flags each enable
/// certain optimizations that need to make additional assumptions. Generally, the semantics of a
/// program does not change when a flag is removed, but adding a flag will.
#[derive(Clone, Copy, Debug)]
pub struct MemFlags {
bits: u8,
}
impl MemFlags {
/// Create a new empty set of flags.
pub fn new() -> MemFlags {
MemFlags { bits: 0 }
}
/// Read a flag bit.
fn read(self, bit: FlagBit) -> bool {
self.bits & (1 << bit as usize) != 0
}
/// Set a flag bit.
fn set(&mut self, bit: FlagBit) {
self.bits |= 1 << bit as usize
}
/// Set a flag bit by name.
///
/// Returns true if the flag was found and set, false for an unknown flag name.
pub fn set_by_name(&mut self, name: &str) -> bool {
match NAMES.iter().position(|&s| s == name) {
Some(bit) => {
self.bits |= 1 << bit;
true
}
None => false,
}
}
/// Test if the `notrap` flag is set.
///
/// Normally, trapping is part of the semantics of a load/store operation. If the platform
/// would cause a trap when accessing the effective address, the Cretonne memory operation is
/// also required to trap.
///
/// The `notrap` flag gives a Cretonne operation permission to not trap. This makes it possible
/// to delete an unused load or a dead store instruction.
pub fn notrap(self) -> bool {
self.read(FlagBit::Notrap)
}
/// Set the `notrap` flag.
pub fn set_notrap(&mut self) {
self.set(FlagBit::Notrap)
}
/// Test if the `aligned` flag is set.
///
/// By default, Cretonne memory instructions work with any unaligned effective address. If the
/// `aligned` flag is set, the instruction is permitted to trap or return a wrong result if the
/// effective address is misaligned.
pub fn aligned(self) -> bool {
self.read(FlagBit::Aligned)
}
/// Set the `aligned` flag.
pub fn set_aligned(&mut self) {
self.set(FlagBit::Aligned)
}
}
impl fmt::Display for MemFlags {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
for (i, n) in NAMES.iter().enumerate() {
if self.bits & (1 << i) != 0 {
write!(f, " {}", n)?;
}
}
Ok(())
}
}

8
lib/cretonne/src/ir/mod.rs

@ -10,11 +10,12 @@ pub mod jumptable;
pub mod dfg;
pub mod layout;
pub mod function;
mod funcname;
mod extfunc;
mod builder;
mod valueloc;
mod extfunc;
mod funcname;
mod memflags;
mod progpoint;
mod valueloc;
pub use ir::funcname::FunctionName;
pub use ir::extfunc::{Signature, ArgumentType, ArgumentExtension, ExtFuncData};
@ -29,3 +30,4 @@ pub use ir::layout::{Layout, Cursor};
pub use ir::function::Function;
pub use ir::builder::InstBuilder;
pub use ir::progpoint::{ProgramPoint, ProgramOrder, ExpandedProgramPoint};
pub use ir::memflags::MemFlags;

4
lib/cretonne/src/verifier.rs

@ -271,7 +271,9 @@ impl<'a> Verifier<'a> {
&IntCompareImm { .. } |
&FloatCompare { .. } |
&HeapLoad { .. } |
&HeapStore { .. } => {}
&HeapStore { .. } |
&Load { .. } |
&Store { .. } => {}
}
Ok(())

7
lib/cretonne/src/write.rs

@ -322,6 +322,13 @@ pub fn write_operands(w: &mut Write, dfg: &DataFlowGraph, inst: Inst) -> Result
} => write!(w, " {}, {}{}", arg, stack_slot, offset),
HeapLoad { arg, offset, .. } => write!(w, " {}{}", arg, offset),
HeapStore { args, offset, .. } => write!(w, " {}, {}{}", args[0], args[1], offset),
Load { flags, arg, offset, .. } => write!(w, "{} {}{}", flags, arg, offset),
Store {
flags,
args,
offset,
..
} => write!(w, "{} {}, {}{}", flags, args[0], args[1], offset),
}
}

47
lib/reader/src/parser.rs

@ -11,7 +11,7 @@ use std::{u16, u32};
use std::mem;
use cretonne::ir::{Function, Ebb, Opcode, Value, Type, FunctionName, StackSlotData, JumpTable,
JumpTableData, Signature, ArgumentType, ArgumentExtension, ExtFuncData, SigRef,
FuncRef, StackSlot, ValueLoc, ArgumentLoc};
FuncRef, StackSlot, ValueLoc, ArgumentLoc, MemFlags};
use cretonne::ir::types::VOID;
use cretonne::ir::immediates::{Imm64, Offset32, Uoffset32, Ieee32, Ieee64};
use cretonne::ir::entities::AnyEntity;
@ -228,7 +228,8 @@ impl<'a> Context<'a> {
InstructionData::Unary { ref mut arg, .. } |
InstructionData::UnarySplit { ref mut arg, .. } |
InstructionData::StackStore { ref mut arg, .. } |
InstructionData::HeapLoad { ref mut arg, .. } => {
InstructionData::HeapLoad { ref mut arg, .. } |
InstructionData::Load { ref mut arg, .. } => {
self.map.rewrite_value(arg, loc)?;
}
@ -238,7 +239,8 @@ impl<'a> Context<'a> {
InstructionData::InsertLane { ref mut args, .. } |
InstructionData::IntCompare { ref mut args, .. } |
InstructionData::FloatCompare { ref mut args, .. } |
InstructionData::HeapStore { ref mut args, .. } => {
InstructionData::HeapStore { ref mut args, .. } |
InstructionData::Store { ref mut args, .. } => {
self.map.rewrite_values(args, loc)?;
}
@ -576,6 +578,19 @@ impl<'a> Parser<'a> {
}
}
// Match and a consume a possibly empty sequence of memory operation flags.
fn optional_memflags(&mut self) -> MemFlags {
let mut flags = MemFlags::new();
while let Some(Token::Identifier(text)) = self.token() {
if flags.set_by_name(text) {
self.consume();
} else {
break;
}
}
flags
}
// Match and consume an identifier.
fn match_any_identifier(&mut self, err_msg: &str) -> Result<&'a str> {
if let Some(Token::Identifier(text)) = self.token() {
@ -1735,6 +1750,32 @@ impl<'a> Parser<'a> {
offset: offset,
}
}
InstructionFormat::Load => {
let flags = self.optional_memflags();
let addr = self.match_value("expected SSA value address")?;
let offset = self.optional_offset32()?;
InstructionData::Load {
opcode: opcode,
ty: VOID,
flags: flags,
arg: addr,
offset: offset,
}
}
InstructionFormat::Store => {
let flags = self.optional_memflags();
let arg = self.match_value("expected SSA value operand")?;
self.match_token(Token::Comma, "expected ',' between operands")?;
let addr = self.match_value("expected SSA value address")?;
let offset = self.optional_offset32()?;
InstructionData::Store {
opcode: opcode,
ty: VOID,
flags: flags,
args: [arg, addr],
offset: offset,
}
}
};
Ok(idata)
}

Loading…
Cancel
Save