summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--tools/standalone_miri/debug.cpp2
-rw-r--r--tools/standalone_miri/debug.hpp2
-rw-r--r--tools/standalone_miri/hir_sim.cpp2
-rw-r--r--tools/standalone_miri/hir_sim.hpp2
-rw-r--r--tools/standalone_miri/main.cpp184
-rw-r--r--tools/standalone_miri/value.cpp451
-rw-r--r--tools/standalone_miri/value.hpp134
7 files changed, 459 insertions, 318 deletions
diff --git a/tools/standalone_miri/debug.cpp b/tools/standalone_miri/debug.cpp
index 503d775a..d2cfb63e 100644
--- a/tools/standalone_miri/debug.cpp
+++ b/tools/standalone_miri/debug.cpp
@@ -35,7 +35,7 @@ DebugSink DebugSink::get(const char* fcn_name, const char* file, unsigned line,
::std::cout << "FATAL: ";
break;
case DebugLevel::Bug:
- ::std::cout << "BUG: " << fcn_name << ": ";
+ ::std::cout << "BUG: " << file << ":" << line << ": ";
break;
}
return DebugSink(::std::cout);
diff --git a/tools/standalone_miri/debug.hpp b/tools/standalone_miri/debug.hpp
index 77295aa2..6102b014 100644
--- a/tools/standalone_miri/debug.hpp
+++ b/tools/standalone_miri/debug.hpp
@@ -35,4 +35,4 @@ public:
#define LOG_FATAL(strm) do { DebugSink::get(__FUNCTION__,__FILE__,__LINE__,DebugLevel::Fatal) << strm; exit(1); } while(0)
#define LOG_TODO(strm) do { DebugSink::get(__FUNCTION__,__FILE__,__LINE__,DebugLevel::Bug) << "TODO: " << strm; abort(); } while(0)
#define LOG_BUG(strm) do { DebugSink::get(__FUNCTION__,__FILE__,__LINE__,DebugLevel::Bug) << "BUG: " << strm; abort(); } while(0)
-#define LOG_ASSERT(cnd) do { if( !(cnd) ) { LOG_BUG("Assertion failure: " #cnd); } } while(0)
+#define LOG_ASSERT(cnd,strm) do { if( !(cnd) ) { LOG_BUG("Assertion failure: " #cnd " - " << strm); } } while(0)
diff --git a/tools/standalone_miri/hir_sim.cpp b/tools/standalone_miri/hir_sim.cpp
index d3ccddc7..4ff3f191 100644
--- a/tools/standalone_miri/hir_sim.cpp
+++ b/tools/standalone_miri/hir_sim.cpp
@@ -6,8 +6,6 @@
#include "hir_sim.hpp"
#include "module_tree.hpp"
-const size_t POINTER_SIZE = 8;
-
//::HIR::Path::Path(::HIR::SimplePath sp)
//{
//}
diff --git a/tools/standalone_miri/hir_sim.hpp b/tools/standalone_miri/hir_sim.hpp
index d0f04578..a81a3f9d 100644
--- a/tools/standalone_miri/hir_sim.hpp
+++ b/tools/standalone_miri/hir_sim.hpp
@@ -6,6 +6,8 @@
#include <vector>
#include <memory>
+const size_t POINTER_SIZE = 8;
+
#define __NE(fld) if(this->fld != x.fld) return true
#define __LT(fld) if(this->fld != x.fld) return this->fld < x.fld
diff --git a/tools/standalone_miri/main.cpp b/tools/standalone_miri/main.cpp
index d1163c14..eb571c64 100644
--- a/tools/standalone_miri/main.cpp
+++ b/tools/standalone_miri/main.cpp
@@ -86,40 +86,37 @@ Value MIRI_Invoke(ModuleTree& modtree, ::HIR::Path path, ::std::vector<Value> ar
}
}
- Value& get_value_type_and_ofs(const ::MIR::LValue& lv, size_t& ofs, ::HIR::TypeRef& ty)
+ ValueRef get_value_and_type(const ::MIR::LValue& lv, ::HIR::TypeRef& ty)
{
switch(lv.tag())
{
case ::MIR::LValue::TAGDEAD: throw "";
TU_ARM(lv, Return, _e) {
- ofs = 0;
ty = fcn.ret_ty;
- return ret;
+ return ValueRef(ret, 0, ret.size());
} break;
TU_ARM(lv, Local, e) {
- ofs = 0;
ty = fcn.m_mir.locals.at(e);
- return locals.at(e);
+ return ValueRef(locals.at(e), 0, locals.at(e).size());
} break;
TU_ARM(lv, Argument, e) {
- ofs = 0;
ty = fcn.args.at(e.idx);
- return args.at(e.idx);
+ return ValueRef(args.at(e.idx), 0, args.at(e.idx).size());
} break;
TU_ARM(lv, Static, e) {
// TODO: Type!
- return modtree.get_static(e);
+ return ValueRef(modtree.get_static(e), 0, modtree.get_static(e).size());
} break;
TU_ARM(lv, Index, e) {
- auto idx = read_lvalue(*e.idx).as_usize();
+ auto idx = get_value_ref(*e.idx).read_usize(0);
::HIR::TypeRef array_ty;
- auto& base_val = get_value_type_and_ofs(*e.val, ofs, array_ty);
+ auto base_val = get_value_and_type(*e.val, array_ty);
if( array_ty.wrappers.empty() )
throw "ERROR";
if( array_ty.wrappers.front().type == TypeWrapper::Ty::Array )
{
ty = array_ty.get_inner();
- ofs += ty.get_size() * idx;
+ base_val.m_offset += ty.get_size() * idx;
return base_val;
}
else if( array_ty.wrappers.front().type == TypeWrapper::Ty::Slice )
@@ -133,55 +130,58 @@ Value MIRI_Invoke(ModuleTree& modtree, ::HIR::Path path, ::std::vector<Value> ar
} break;
TU_ARM(lv, Field, e) {
::HIR::TypeRef composite_ty;
- auto& base_val = get_value_type_and_ofs(*e.val, ofs, composite_ty);
+ auto base_val = get_value_and_type(*e.val, composite_ty);
LOG_DEBUG("Field - " << composite_ty);
size_t inner_ofs;
ty = composite_ty.get_field(e.field_index, inner_ofs);
- ofs += inner_ofs;
+ base_val.m_offset += inner_ofs;
return base_val;
}
TU_ARM(lv, Downcast, e) {
::HIR::TypeRef composite_ty;
- auto& base_val = get_value_type_and_ofs(*e.val, ofs, composite_ty);
+ auto base_val = get_value_and_type(*e.val, composite_ty);
size_t inner_ofs;
ty = composite_ty.get_field(e.variant_index, inner_ofs);
LOG_TODO("Read from Downcast - " << lv);
- ofs += inner_ofs;
+ base_val.m_offset += inner_ofs;
return base_val;
}
TU_ARM(lv, Deref, e) {
::HIR::TypeRef ptr_ty;
- auto addr = read_lvalue_with_ty(*e.val, ptr_ty);
+ auto val = get_value_and_type(*e.val, ptr_ty);
+ LOG_ASSERT(val.m_size == POINTER_SIZE, "Deref of a value that isn't a pointer-sized value");
// There MUST be a relocation at this point with a valid allocation.
- auto& reloc = addr.allocation.alloc().relocations.at(0);
- assert(reloc.slot_ofs == 0);
- ofs = addr.read_usize(0);
- // Need to make a new Value to return as the base value.
- // - This value needs to be stored somewhere.
- // - Shoudl the value directly reference into the pointer?
- //auto rv = Value(ptr_ty.get_inner(), reloc.backing_alloc, ofs);
- LOG_TODO("Read from deref - " << lv << " - " << addr);
-
+ auto& val_alloc = val.m_alloc ? val.m_alloc : val.m_value->allocation;
+ LOG_ASSERT(val_alloc, "Deref of a value with no allocation (hence no relocations)");
+ LOG_TRACE("Deref " << val_alloc.alloc());
+ auto alloc = val_alloc.alloc().get_relocation(val.m_offset);
+ LOG_ASSERT(alloc, "Deref of a value with no relocation");
+ size_t ofs = val.read_usize(0);
+ ty = ptr_ty.get_inner();
+ return ValueRef(::std::move(alloc), ofs, ty.get_size());
} break;
}
throw "";
}
+ ValueRef get_value_ref(const ::MIR::LValue& lv)
+ {
+ ::HIR::TypeRef tmp;
+ return get_value_and_type(lv, tmp);
+ }
::HIR::TypeRef get_lvalue_ty(const ::MIR::LValue& lv)
{
::HIR::TypeRef ty;
- size_t ofs = 0;
- get_value_type_and_ofs(lv, ofs, ty);
+ get_value_and_type(lv, ty);
return ty;
}
Value read_lvalue_with_ty(const ::MIR::LValue& lv, ::HIR::TypeRef& ty)
{
- size_t ofs = 0;
- Value& base_value = get_value_type_and_ofs(lv, ofs, ty);
+ auto base_value = get_value_and_type(lv, ty);
- return base_value.read_value(ofs, ty.get_size());
+ return base_value.read_value(0, ty.get_size());
}
Value read_lvalue(const ::MIR::LValue& lv)
{
@@ -192,10 +192,14 @@ Value MIRI_Invoke(ModuleTree& modtree, ::HIR::Path path, ::std::vector<Value> ar
{
//LOG_DEBUG(lv << " = " << val);
::HIR::TypeRef ty;
- size_t ofs = 0;
- Value& base_value = get_value_type_and_ofs(lv, ofs, ty);
+ auto base_value = get_value_and_type(lv, ty);
- base_value.write_value(ofs, val);
+ if(base_value.m_alloc) {
+ base_value.m_alloc.alloc().write_value(base_value.m_offset, ::std::move(val));
+ }
+ else {
+ base_value.m_value->write_value(base_value.m_offset, ::std::move(val));
+ }
}
Value const_to_value(const ::MIR::Constant& c, ::HIR::TypeRef& ty)
@@ -291,41 +295,37 @@ Value MIRI_Invoke(ModuleTree& modtree, ::HIR::Path path, ::std::vector<Value> ar
{
case ::MIR::Statement::TAGDEAD: throw "";
TU_ARM(stmt, Assign, se) {
- Value val;
+ Value new_val;
switch(se.src.tag())
{
case ::MIR::RValue::TAGDEAD: throw "";
TU_ARM(se.src, Use, re) {
- state.write_lvalue(se.dst, state.read_lvalue(re));
+ new_val = state.read_lvalue(re);
} break;
TU_ARM(se.src, Constant, re) {
- state.write_lvalue(se.dst, state.const_to_value(re));
+ new_val = state.const_to_value(re);
} break;
TU_ARM(se.src, Borrow, re) {
::HIR::TypeRef src_ty;
- size_t ofs = 0;
- Value& src_base_value = state.get_value_type_and_ofs(re.val, ofs, src_ty);
- if( !src_base_value.allocation )
+ ValueRef src_base_value = state.get_value_and_type(re.val, src_ty);
+ auto alloc = src_base_value.m_alloc;
+ if( !alloc )
{
- // TODO: Need to convert this value into an allocation version
- ::std::cerr << "TODO: RValue::Borrow - " << se.src << " - convert to non-inline" << ::std::endl;
- throw "TODO";
- //base_value.to_allocation();
+ if( !src_base_value.m_value->allocation )
+ {
+ src_base_value.m_value->create_allocation();
+ }
+ alloc = AllocationPtr(src_base_value.m_value->allocation);
}
- ofs += src_base_value.meta.indirect_meta.offset;
+ LOG_DEBUG("- alloc=" << alloc << " (" << alloc.alloc() << ")");
+ size_t ofs = src_base_value.m_offset;
src_ty.wrappers.insert(src_ty.wrappers.begin(), TypeWrapper { TypeWrapper::Ty::Borrow, static_cast<size_t>(re.type) });
- Value new_val = Value(src_ty);
- // ^ Pointer value
- new_val.allocation.alloc().relocations.push_back(Relocation { 0, src_base_value.allocation });
- new_val.write_bytes(0, &ofs, src_ty.get_size());
- LOG_DEBUG("- " << new_val);
- ::HIR::TypeRef dst_ty;
- // TODO: Check type equality
- size_t dst_ofs = 0;
- Value& dst_base_value = state.get_value_type_and_ofs(se.dst, dst_ofs, dst_ty);
- dst_base_value.write_value(dst_ofs, ::std::move(new_val));
- LOG_DEBUG("- " << dst_base_value);
+ new_val = Value(src_ty);
+ // ^ Pointer value
+ new_val.write_usize(0, ofs);
+ // - Add the relocation after writing the value (writing clears the relocations)
+ new_val.allocation.alloc().relocations.push_back(Relocation { 0, ::std::move(alloc) });
} break;
TU_ARM(se.src, SizedArray, re) {
throw "TODO";
@@ -334,17 +334,13 @@ Value MIRI_Invoke(ModuleTree& modtree, ::HIR::Path path, ::std::vector<Value> ar
// Determine the type of cast, is it a reinterpret or is it a value transform?
// - Float <-> integer is a transform, anything else should be a reinterpret.
::HIR::TypeRef src_ty;
- Value src_value = state.read_lvalue_with_ty(re.val, src_ty);
+ auto src_value = state.get_value_and_type(re.val, src_ty);
- ::HIR::TypeRef dst_ty;
- size_t dst_ofs = 0;
- Value& dst_base_value = state.get_value_type_and_ofs(se.dst, dst_ofs, dst_ty);
-
- Value new_val = Value(re.type);
+ new_val = Value(re.type);
if( re.type == src_ty )
{
// No-op cast
- new_val = ::std::move(src_value);
+ new_val = src_value.read_value(0, re.type.get_size());
}
else if( !re.type.wrappers.empty() )
{
@@ -366,7 +362,7 @@ Value MIRI_Invoke(ModuleTree& modtree, ::HIR::Path path, ::std::vector<Value> ar
}
else
{
- new_val = ::std::move(src_value);
+ new_val = src_value.read_value(0, re.type.get_size());
}
}
else
@@ -382,7 +378,7 @@ Value MIRI_Invoke(ModuleTree& modtree, ::HIR::Path path, ::std::vector<Value> ar
::std::cerr << "ERROR: Trying to pointer (" << re.type <<" ) from invalid type (" << src_ty << ")\n";
throw "ERROR";
}
- new_val = ::std::move(src_value);
+ new_val = src_value.read_value(0, re.type.get_size());
}
}
else if( !src_ty.wrappers.empty() )
@@ -395,10 +391,11 @@ Value MIRI_Invoke(ModuleTree& modtree, ::HIR::Path path, ::std::vector<Value> ar
// TODO: MUST be a thin pointer
// TODO: MUST be an integer (usize only?)
- if( src_ty != RawType::USize ) {
+ if( re.type != RawType::USize ) {
+ LOG_ERROR("Casting from a pointer to non-usize - " << re.type << " to " << src_ty);
throw "ERROR";
}
- new_val = ::std::move(src_value);
+ new_val = src_value.read_value(0, re.type.get_size());
}
else
{
@@ -490,8 +487,6 @@ Value MIRI_Invoke(ModuleTree& modtree, ::HIR::Path path, ::std::vector<Value> ar
throw "TODO";
}
}
-
- dst_base_value.write_value(dst_ofs, ::std::move(new_val));
} break;
TU_ARM(se.src, BinOp, re) {
throw "TODO";
@@ -510,42 +505,40 @@ Value MIRI_Invoke(ModuleTree& modtree, ::HIR::Path path, ::std::vector<Value> ar
} break;
TU_ARM(se.src, Tuple, re) {
::HIR::TypeRef dst_ty;
- size_t ofs = 0;
- Value& base_value = state.get_value_type_and_ofs(se.dst, ofs, dst_ty);
+ state.get_value_and_type(se.dst, dst_ty);
+ new_val = Value(dst_ty);
for(size_t i = 0; i < re.vals.size(); i++)
{
auto fld_ofs = dst_ty.composite_type->fields.at(i).first;
- base_value.write_value(ofs + fld_ofs, state.param_to_value(re.vals[i]));
+ new_val.write_value(fld_ofs, state.param_to_value(re.vals[i]));
}
} break;
TU_ARM(se.src, Array, re) {
throw "TODO";
} break;
TU_ARM(se.src, Variant, re) {
- ::HIR::TypeRef dst_ty;
- size_t dst_ofs = 0;
- Value& dst_base_value = state.get_value_type_and_ofs(se.dst, dst_ofs, dst_ty);
-
// 1. Get the composite by path.
- const auto& ty = state.modtree.get_composite(re.path);
+ const auto& data_ty = state.modtree.get_composite(re.path);
+ auto dst_ty = ::HIR::TypeRef(&data_ty);
+ new_val = Value(dst_ty);
// Three cases:
// - Unions (no tag)
// - Data enums (tag and data)
// - Value enums (no data)
- const auto& var = ty.variants.at(re.index);
+ const auto& var = data_ty.variants.at(re.index);
if( var.data_field != SIZE_MAX )
{
- const auto& fld = ty.fields.at(re.index);
+ const auto& fld = data_ty.fields.at(re.index);
- dst_base_value.write_value(dst_ofs + fld.first, state.param_to_value(re.val));
+ new_val.write_value(fld.first, state.param_to_value(re.val));
}
if( var.base_field != SIZE_MAX )
{
::HIR::TypeRef tag_ty;
size_t tag_ofs = dst_ty.get_field_ofs(var.base_field, var.field_path, tag_ty);
- LOG_ASSERT(tag_ty.get_size() == var.tag_data.size());
- dst_base_value.write_bytes(dst_ofs + tag_ofs, var.tag_data.data(), var.tag_data.size());
+ LOG_ASSERT(tag_ty.get_size() == var.tag_data.size(), "");
+ new_val.write_bytes(tag_ofs, var.tag_data.data(), var.tag_data.size());
}
else
{
@@ -556,6 +549,8 @@ Value MIRI_Invoke(ModuleTree& modtree, ::HIR::Path path, ::std::vector<Value> ar
throw "TODO";
} break;
}
+ LOG_DEBUG("- " << new_val);
+ state.write_lvalue(se.dst, ::std::move(new_val));
} break;
case ::MIR::Statement::TAG_Asm:
throw "TODO";
@@ -577,25 +572,25 @@ Value MIRI_Invoke(ModuleTree& modtree, ::HIR::Path path, ::std::vector<Value> ar
{
case ::MIR::Terminator::TAGDEAD: throw "";
TU_ARM(bb.terminator, Incomplete, _te)
- throw ::std::runtime_error("BUG: Terminator::Incomplete hit");
+ LOG_TODO("Terminator::Incomplete hit");
TU_ARM(bb.terminator, Diverge, _te)
- throw ::std::runtime_error("BUG: Terminator::Diverge hit");
+ LOG_TODO("Terminator::Diverge hit");
TU_ARM(bb.terminator, Panic, _te)
- throw ::std::runtime_error("TODO: Terminator::Panic");
+ LOG_TODO("Terminator::Panic");
TU_ARM(bb.terminator, Goto, te)
bb_idx = te;
continue;
TU_ARM(bb.terminator, Return, _te)
return state.ret;
TU_ARM(bb.terminator, If, _te)
- throw ::std::runtime_error("TODO: Terminator::If");
+ LOG_TODO("Terminator::If");
TU_ARM(bb.terminator, Switch, _te)
- throw ::std::runtime_error("TODO: Terminator::Switch");
+ LOG_TODO("Terminator::Switch");
TU_ARM(bb.terminator, SwitchValue, _te)
- throw ::std::runtime_error("TODO: Terminator::SwitchValue");
+ LOG_TODO("Terminator::SwitchValue");
TU_ARM(bb.terminator, Call, te) {
if( te.fcn.is_Intrinsic() ) {
- throw ::std::runtime_error("TODO: Terminator::Call - intrinsic");
+ LOG_TODO("Terminator::Call - intrinsic");
}
else {
const ::HIR::Path* fcn_p;
@@ -604,11 +599,15 @@ Value MIRI_Invoke(ModuleTree& modtree, ::HIR::Path path, ::std::vector<Value> ar
}
else {
::HIR::TypeRef ty;
- auto v = state.read_lvalue_with_ty(te.fcn.as_Value(), ty);
+ auto v = state.get_value_and_type(te.fcn.as_Value(), ty);
// TODO: Assert type
// TODO: Assert offset/content.
- assert(v.read_usize(0) == 0);
- fcn_p = &v.allocation.alloc().relocations.at(0).backing_alloc.fcn();
+ assert(v.read_usize(v.m_offset) == 0);
+ auto& alloc_ptr = v.m_alloc ? v.m_alloc : v.m_value->allocation;
+ LOG_ASSERT(alloc_ptr, "");
+ auto& fcn_alloc_ptr = alloc_ptr.alloc().get_relocation(v.m_offset);
+ LOG_ASSERT(fcn_alloc_ptr, "");
+ fcn_p = &fcn_alloc_ptr.fcn();
}
::std::vector<Value> sub_args; sub_args.reserve(te.args.size());
@@ -616,10 +615,9 @@ Value MIRI_Invoke(ModuleTree& modtree, ::HIR::Path path, ::std::vector<Value> ar
{
sub_args.push_back( state.param_to_value(a) );
}
- ::std::cout << "TODO: Call " << *fcn_p << ::std::endl;
+ ::std::cout << "Call " << *fcn_p << ::std::endl;
MIRI_Invoke(modtree, *fcn_p, ::std::move(sub_args));
}
- throw ::std::runtime_error("TODO: Terminator::Call");
} break;
}
throw "";
diff --git a/tools/standalone_miri/value.cpp b/tools/standalone_miri/value.cpp
index e4a19196..44fd2d02 100644
--- a/tools/standalone_miri/value.cpp
+++ b/tools/standalone_miri/value.cpp
@@ -108,18 +108,194 @@ AllocationPtr::~AllocationPtr()
return os;
}
+
+void Allocation::check_bytes_valid(size_t ofs, size_t size) const
+{
+ if( !(ofs + size <= this->size()) ) {
+ LOG_FATAL("Out of range - " << ofs << "+" << size << " > " << this->size());
+ }
+ for(size_t i = ofs; i < ofs + size; i++)
+ {
+ if( !(this->mask[i/8] & (1 << i%8)) )
+ {
+ ::std::cerr << "ERROR: Invalid bytes in value" << ::std::endl;
+ throw "ERROR";
+ }
+ }
+}
+void Allocation::mark_bytes_valid(size_t ofs, size_t size)
+{
+ assert( ofs+size <= this->mask.size() * 8 );
+ for(size_t i = ofs; i < ofs + size; i++)
+ {
+ this->mask[i/8] |= (1 << i%8);
+ }
+}
+Value Allocation::read_value(size_t ofs, size_t size) const
+{
+ Value rv;
+ // TODO: Determine if this can become an inline allocation.
+ bool has_reloc = false;
+ for(const auto& r : this->relocations)
+ {
+ if( ofs <= r.slot_ofs && r.slot_ofs < ofs + size )
+ {
+ has_reloc = true;
+ }
+ }
+ if( has_reloc || size > sizeof(rv.direct_data.data) )
+ {
+ rv.allocation = Allocation::new_alloc(size);
+
+ rv.write_bytes(0, this->data_ptr() + ofs, size);
+
+ for(const auto& r : this->relocations)
+ {
+ if( ofs <= r.slot_ofs && r.slot_ofs < ofs + size )
+ {
+ rv.allocation.alloc().relocations.push_back({ r.slot_ofs - ofs, r.backing_alloc });
+ }
+ }
+ }
+ else
+ {
+ rv.direct_data.size = static_cast<uint8_t>(size);
+ rv.write_bytes(0, this->data_ptr() + ofs, size);
+ }
+ return rv;
+}
+void Allocation::read_bytes(size_t ofs, void* dst, size_t count) const
+{
+ if(ofs >= this->size() ) {
+ ::std::cerr << "Value::write_bytes - Out of bounds read, " << ofs << " >= " << this->size() << ::std::endl;
+ throw "ERROR";
+ }
+ if(count > this->size() ) {
+ ::std::cerr << "Value::write_bytes - Out of bounds read, count " << count << " > size " << this->size() << ::std::endl;
+ throw "ERROR";
+ }
+ if(ofs+count > this->size() ) {
+ ::std::cerr << "Value::write_bytes - Out of bounds read, " << ofs << "+" << count << " > size " << this->size() << ::std::endl;
+ throw "ERROR";
+ }
+ check_bytes_valid(ofs, count);
+
+
+ ::std::memcpy(dst, this->data_ptr() + ofs, count);
+}
+void Allocation::write_value(size_t ofs, Value v)
+{
+ if( v.allocation )
+ {
+ size_t v_size = v.allocation.alloc().size();
+ const auto& src_alloc = v.allocation.alloc();
+ // TODO: Just copy the validity from the source.
+ v.check_bytes_valid(0, v_size);
+
+ // Save relocations first, because `Foo = Foo` is valid.
+ ::std::vector<Relocation> new_relocs = src_alloc.relocations;
+ // - write_bytes removes any relocations in this region.
+ write_bytes(ofs, src_alloc.data_ptr(), v_size);
+
+ // Find any relocations that apply and copy those in.
+ // - Any relocations in the source within `v.meta.indirect_meta.offset` .. `v.meta.indirect_meta.offset + v_size`
+ if( !new_relocs.empty() )
+ {
+ // 2. Move the new relocations into this allocation
+ for(auto& r : new_relocs)
+ {
+ LOG_TRACE("Insert " << r.backing_alloc);
+ r.slot_ofs += ofs;
+ this->relocations.push_back( ::std::move(r) );
+ }
+ }
+ }
+ else
+ {
+ // TODO: Check validity of input, OR just copy the validity
+ v.check_bytes_valid(0, v.direct_data.size);
+ this->write_bytes(ofs, v.direct_data.data, v.direct_data.size);
+ }
+}
+void Allocation::write_bytes(size_t ofs, const void* src, size_t count)
+{
+ if(ofs >= this->size() ) {
+ ::std::cerr << "Value::write_bytes - Out of bounds write, " << ofs << " >= " << this->size() << ::std::endl;
+ throw "ERROR";
+ }
+ if(count > this->size() ) {
+ ::std::cerr << "Value::write_bytes - Out of bounds write, count " << count << " > size " << this->size() << ::std::endl;
+ throw "ERROR";
+ }
+ if(ofs+count > this->size() ) {
+ ::std::cerr << "Value::write_bytes - Out of bounds write, " << ofs << "+" << count << " > size " << this->size() << ::std::endl;
+ throw "ERROR";
+ }
+
+
+ // - Remove any relocations already within this region
+ auto& this_relocs = this->relocations;
+ for(auto it = this_relocs.begin(); it != this_relocs.end(); )
+ {
+ if( ofs <= it->slot_ofs && it->slot_ofs < ofs + count)
+ {
+ LOG_TRACE("Delete " << it->backing_alloc);
+ it = this_relocs.erase(it);
+ }
+ else
+ {
+ ++it;
+ }
+ }
+
+ ::std::memcpy(this->data_ptr() + ofs, src, count);
+ mark_bytes_valid(ofs, count);
+}
+void Allocation::write_usize(size_t ofs, uint64_t v)
+{
+ this->write_bytes(0, &v, POINTER_SIZE);
+}
+::std::ostream& operator<<(::std::ostream& os, const Allocation& x)
+{
+ for(size_t i = 0; i < x.size(); i++)
+ {
+ if( i != 0 )
+ os << " ";
+
+ if( x.mask[i/8] & (1 << i%8) )
+ {
+ os << ::std::setw(2) << ::std::setfill('0') << (int)x.data_ptr()[i];
+ }
+ else
+ {
+ os << "--";
+ }
+ }
+
+ os << " {";
+ for(const auto& r : x.relocations)
+ {
+ if( 0 <= r.slot_ofs && r.slot_ofs < x.size() )
+ {
+ os << " @" << r.slot_ofs << "=" << r.backing_alloc;
+ }
+ }
+ os << " }";
+ return os;
+}
+
Value::Value()
{
- this->meta.direct_data.size = 0;
- this->meta.direct_data.mask[0] = 0;
- this->meta.direct_data.mask[1] = 0;
+ this->direct_data.size = 0;
+ this->direct_data.mask[0] = 0;
+ this->direct_data.mask[1] = 0;
}
Value::Value(::HIR::TypeRef ty)
{
size_t size = ty.get_size();
#if 1
// Support inline data if the data will fit within the inline region (which is the size of the metadata)
- if( ty.get_size() <= sizeof(this->meta.direct_data.data) )
+ if( ty.get_size() <= sizeof(this->direct_data.data) )
{
struct H
{
@@ -152,9 +328,9 @@ Value::Value(::HIR::TypeRef ty)
{
// Will fit in a inline allocation, nice.
//LOG_TRACE("No pointers in " << ty << ", storing inline");
- this->meta.direct_data.size = static_cast<uint8_t>(size);
- this->meta.direct_data.mask[0] = 0;
- this->meta.direct_data.mask[1] = 0;
+ this->direct_data.size = static_cast<uint8_t>(size);
+ this->direct_data.mask[0] = 0;
+ this->direct_data.mask[1] = 0;
return ;
}
}
@@ -163,8 +339,6 @@ Value::Value(::HIR::TypeRef ty)
// Fallback: Make a new allocation
//LOG_TRACE(" Creating allocation for " << ty);
this->allocation = Allocation::new_alloc(size);
- this->meta.indirect_meta.offset = 0;
- this->meta.indirect_meta.size = size;
}
Value Value::new_fnptr(const ::HIR::Path& fn_path)
{
@@ -176,44 +350,40 @@ Value Value::new_fnptr(const ::HIR::Path& fn_path)
return rv;
}
+void Value::create_allocation()
+{
+ assert(!this->allocation);
+ this->allocation = Allocation::new_alloc(this->direct_data.size);
+ this->allocation.alloc().mask[0] = this->direct_data.mask[0];
+ this->allocation.alloc().mask[1] = this->direct_data.mask[1];
+ ::std::memcpy(this->allocation.alloc().data.data(), this->direct_data.data, this->direct_data.size);
+}
void Value::check_bytes_valid(size_t ofs, size_t size) const
{
+ if( size == 0 )
+ return ;
if( this->allocation )
{
- const auto& alloc = this->allocation.alloc();
- if( ofs >= this->meta.indirect_meta.size || ofs+size > this->meta.indirect_meta.size ) {
- ::std::cerr << "ERROR: OOB read" << ::std::endl;
- throw "ERROR";
- }
- ofs += this->meta.indirect_meta.offset;
- //assert(ofs + size < alloc.size());
- for(size_t i = ofs; i < ofs + size; i++)
- {
- if( !(alloc.mask[i/8] & (1 << i%8)) )
- {
- ::std::cerr << "ERROR: Invalid bytes in value" << ::std::endl;
- throw "ERROR";
- }
- }
+ this->allocation.alloc().check_bytes_valid(ofs, size);
}
else
{
- if( size == 0 && this->meta.direct_data.size > 0 ) {
+ if( size == 0 && this->direct_data.size > 0 ) {
return ;
}
- if( ofs >= this->meta.direct_data.size ) {
- ::std::cerr << "ERROR: OOB read" << ::std::endl;
+ if( ofs >= this->direct_data.size ) {
+ LOG_ERROR("Read out of bounds " << ofs << "+" << size << " > " << int(this->direct_data.size));
throw "ERROR";
}
- if( ofs+size > this->meta.direct_data.size ) {
- ::std::cerr << "ERROR: OOB read" << ::std::endl;
+ if( ofs+size > this->direct_data.size ) {
+ LOG_ERROR("Read out of bounds " << ofs+size << " >= " << int(this->direct_data.size));
throw "ERROR";
}
for(size_t i = ofs; i < ofs + size; i++)
{
- if( !(this->meta.direct_data.mask[i/8] & (1 << i%8)) )
+ if( !(this->direct_data.mask[i/8] & (1 << i%8)) )
{
- ::std::cerr << "ERROR: Invalid bytes in value" << ::std::endl;
+ LOG_ERROR("Accessing invalid bytes in value");
throw "ERROR";
}
}
@@ -223,20 +393,13 @@ void Value::mark_bytes_valid(size_t ofs, size_t size)
{
if( this->allocation )
{
- auto& alloc = this->allocation.alloc();
- // TODO: Assert range.
- ofs += this->meta.indirect_meta.offset;
- assert( ofs+size <= alloc.mask.size() * 8 );
- for(size_t i = ofs; i < ofs + size; i++)
- {
- alloc.mask[i/8] |= (1 << i%8);
- }
+ this->allocation.alloc().mark_bytes_valid(ofs, size);
}
else
{
- for(size_t i = 0; i < this->meta.direct_data.size; i++)
+ for(size_t i = 0; i < this->direct_data.size; i++)
{
- this->meta.direct_data.mask[i/8] |= (1 << i%8);
+ this->direct_data.mask[i/8] |= (1 << i%8);
}
}
}
@@ -248,211 +411,113 @@ Value Value::read_value(size_t ofs, size_t size) const
check_bytes_valid(ofs, size);
if( this->allocation )
{
- const auto& alloc = this->allocation.alloc();
- // TODO: Determine if this can become an inline allocation.
- bool has_reloc = false;
- for(const auto& r : alloc.relocations)
- {
- if( this->meta.indirect_meta.offset+ofs <= r.slot_ofs && r.slot_ofs < this->meta.indirect_meta.offset + ofs + size )
- {
- has_reloc = true;
- }
- }
- if( has_reloc || size > sizeof(this->meta.direct_data.data) )
- {
- rv.allocation = Allocation::new_alloc(size);
- rv.meta.indirect_meta.offset = 0;
- rv.meta.indirect_meta.size = size;
-
- for(const auto& r : alloc.relocations)
- {
- if( this->meta.indirect_meta.offset+ofs <= r.slot_ofs && r.slot_ofs < this->meta.indirect_meta.offset + ofs + size )
- {
- rv.allocation.alloc().relocations.push_back({ r.slot_ofs - (this->meta.indirect_meta.offset+ofs), r.backing_alloc });
- }
- }
- }
- else
- {
- rv.meta.direct_data.size = static_cast<uint8_t>(size);
- }
- rv.write_bytes(0, this->data_ptr() + ofs, size);
+ rv = this->allocation.alloc().read_value(ofs, size);
}
else
{
// Inline can become inline.
- rv.meta.direct_data.size = static_cast<uint8_t>(size);
- rv.write_bytes(0, this->meta.direct_data.data+ofs, size);
+ rv.direct_data.size = static_cast<uint8_t>(size);
+ rv.write_bytes(0, this->direct_data.data+ofs, size);
}
LOG_DEBUG("RETURN " << rv);
return rv;
}
void Value::read_bytes(size_t ofs, void* dst, size_t count) const
{
- check_bytes_valid(ofs, count);
- throw "TODO";
-}
-
-void Value::write_bytes(size_t ofs, const void* src, size_t count)
-{
if( this->allocation )
{
- if(ofs >= this->meta.indirect_meta.size ) {
- ::std::cerr << "Value::write_bytes - Out of bounds write, " << ofs << " >= " << this->meta.indirect_meta.size << ::std::endl;
+ this->allocation.alloc().read_bytes(ofs, dst, count);
+ }
+ else
+ {
+ check_bytes_valid(ofs, count);
+
+ if(ofs >= this->direct_data.size )
throw "ERROR";
- }
- if(count > this->meta.indirect_meta.size ) {
- ::std::cerr << "Value::write_bytes - Out of bounds write, count " << count << " > size " << this->meta.indirect_meta.size << ::std::endl;
+ if(count > this->direct_data.size )
throw "ERROR";
- }
- if(ofs+count > this->meta.indirect_meta.size ) {
- ::std::cerr << "Value::write_bytes - Out of bounds write, " << ofs << "+" << count << " > size " << this->meta.indirect_meta.size << ::std::endl;
+ if(ofs+count > this->direct_data.size )
throw "ERROR";
- }
-
-
- // - Remove any relocations already within this region
- auto& this_relocs = this->allocation.alloc().relocations;
- for(auto it = this_relocs.begin(); it != this_relocs.end(); )
- {
- if( this->meta.indirect_meta.offset + ofs <= it->slot_ofs && it->slot_ofs < this->meta.indirect_meta.offset + ofs + count)
- {
- LOG_TRACE("Delete " << it->backing_alloc);
- it = this_relocs.erase(it);
- }
- else
- {
- ++it;
- }
- }
+ ::std::memcpy(dst, this->direct_data.data + ofs, count);
+ }
+}
+void Value::write_bytes(size_t ofs, const void* src, size_t count)
+{
+ if( count == 0 )
+ return ;
+ if( this->allocation )
+ {
+ this->allocation.alloc().write_bytes(ofs, src, count);
}
else
{
- if(ofs >= this->meta.direct_data.size )
+ if(ofs >= this->direct_data.size )
throw "ERROR";
- if(count > this->meta.direct_data.size )
+ if(count > this->direct_data.size )
throw "ERROR";
- if(ofs+count > this->meta.direct_data.size )
+ if(ofs+count > this->direct_data.size )
throw "ERROR";
+ ::std::memcpy(this->direct_data.data + ofs, src, count);
+ mark_bytes_valid(ofs, count);
}
- ::std::memcpy(this->data_ptr() + ofs, src, count);
- mark_bytes_valid(ofs, count);
}
void Value::write_value(size_t ofs, Value v)
{
- if( v.allocation )
+ if( this->allocation )
{
- size_t v_size = v.meta.indirect_meta.size;
- v.check_bytes_valid(0, v_size);
- const auto& src_alloc = v.allocation.alloc();
- write_bytes(ofs, v.data_ptr(), v_size);
- // Find any relocations that apply and copy those in.
- // - Any relocations in the source within `v.meta.indirect_meta.offset` .. `v.meta.indirect_meta.offset + v_size`
- ::std::vector<Relocation> new_relocs;
- for(const auto& r : src_alloc.relocations)
+ this->allocation.alloc().write_value(ofs, ::std::move(v));
+ }
+ else
+ {
+ if( v.allocation && !v.allocation.alloc().relocations.empty() )
{
- // TODO: Negative offsets in destination?
- if( v.meta.indirect_meta.offset <= r.slot_ofs && r.slot_ofs < v.meta.indirect_meta.offset + v_size )
- {
- LOG_TRACE("Copy " << r.backing_alloc);
- // Applicable, save for later
- new_relocs.push_back( r );
- }
+ this->create_allocation();
+ this->allocation.alloc().write_value(ofs, ::std::move(v));
}
- if( !new_relocs.empty() )
+ else
{
- if( !this->allocation ) {
- throw ::std::runtime_error("TODO: Writing value with a relocation into a slot without a relocation");
- }
- // 1. Remove any relocations already within this region
- auto& this_relocs = this->allocation.alloc().relocations;
- for(auto it = this_relocs.begin(); it != this_relocs.end(); )
- {
- if( this->meta.indirect_meta.offset + ofs <= it->slot_ofs && it->slot_ofs < this->meta.indirect_meta.offset + ofs + v_size)
- {
- LOG_TRACE("Delete " << it->backing_alloc);
- it = this_relocs.erase(it);
- }
- else
- {
- ++it;
- }
- }
- // 2. Move the new relocations into this allocation
- for(auto& r : new_relocs)
- {
- LOG_TRACE("Insert " << r.backing_alloc);
- r.slot_ofs -= v.meta.indirect_meta.offset;
- r.slot_ofs += this->meta.indirect_meta.offset + ofs;
- this_relocs.push_back( ::std::move(r) );
- }
+ v.check_bytes_valid(0, v.direct_data.size);
+ write_bytes(ofs, v.direct_data.data, v.direct_data.size);
}
}
- else
- {
- v.check_bytes_valid(0, v.meta.direct_data.size);
- write_bytes(ofs, v.meta.direct_data.data, v.meta.direct_data.size);
- }
}
-
-uint64_t Value::read_usize(size_t ofs) const
+void Value::write_usize(size_t ofs, uint64_t v)
{
- uint64_t v = 0;
- // TODO: Handle different pointer sizes
- this->read_bytes(0, &v, 8);
- return v;
+ this->write_bytes(0, &v, POINTER_SIZE);
}
::std::ostream& operator<<(::std::ostream& os, const Value& v)
{
- auto flags = os.flags();
- os << ::std::hex;
if( v.allocation )
{
- const auto& alloc = v.allocation.alloc();
- for(size_t i = 0; i < v.meta.indirect_meta.size; i++)
- {
- if( i != 0 )
- os << " ";
- size_t j = i + v.meta.indirect_meta.offset;
-
- if( alloc.mask[j/8] & (1 << i%8) )
- {
- os << ::std::setw(2) << ::std::setfill('0') << (int)alloc.data_ptr()[j];
- }
- else
- {
- os << "--";
- }
- }
-
- os << " {";
- for(const auto& r : alloc.relocations)
- {
- if( v.meta.indirect_meta.offset <= r.slot_ofs && r.slot_ofs < v.meta.indirect_meta.offset + v.meta.indirect_meta.size )
- {
- os << " @" << (r.slot_ofs - v.meta.indirect_meta.offset) << "=" << r.backing_alloc;
- }
- }
- os << " }";
+ os << v.allocation.alloc();
}
else
{
- for(size_t i = 0; i < v.meta.direct_data.size; i++)
+ auto flags = os.flags();
+ os << ::std::hex;
+ for(size_t i = 0; i < v.direct_data.size; i++)
{
if( i != 0 )
os << " ";
- if( v.meta.direct_data.mask[i/8] & (1 << i%8) )
+ if( v.direct_data.mask[i/8] & (1 << i%8) )
{
- os << ::std::setw(2) << ::std::setfill('0') << (int)v.meta.direct_data.data[i];
+ os << ::std::setw(2) << ::std::setfill('0') << (int)v.direct_data.data[i];
}
else
{
os << "--";
}
}
+ os.setf(flags);
}
- os.setf(flags);
return os;
+}
+
+uint64_t ValueRef::read_usize(size_t ofs) const
+{
+ uint64_t v = 0;
+ this->read_bytes(0, &v, POINTER_SIZE);
+ return v;
} \ No newline at end of file
diff --git a/tools/standalone_miri/value.hpp b/tools/standalone_miri/value.hpp
index b0c649fc..1971bb6d 100644
--- a/tools/standalone_miri/value.hpp
+++ b/tools/standalone_miri/value.hpp
@@ -13,7 +13,7 @@ namespace HIR {
struct Path;
}
class Allocation;
-
+struct Value;
class AllocationPtr
{
@@ -56,14 +56,17 @@ public:
return *this && get_ty() == Ty::Allocation;
}
Allocation& alloc() {
+ assert(*this);
assert(get_ty() == Ty::Allocation);
return *static_cast<Allocation*>(get_ptr());
}
const Allocation& alloc() const {
+ assert(*this);
assert(get_ty() == Ty::Allocation);
return *static_cast<Allocation*>(get_ptr());
}
const ::HIR::Path& fcn() const {
+ assert(*this);
assert(get_ty() == Ty::Function);
return *static_cast<const ::HIR::Path*>(get_ptr());
}
@@ -95,32 +98,62 @@ public:
const uint8_t* data_ptr() const { return reinterpret_cast<const uint8_t*>(this->data.data()); }
uint8_t* data_ptr() { return reinterpret_cast< uint8_t*>(this->data.data()); }
+ size_t size() const { return this->data.size() * 8; }
::std::vector<uint64_t> data;
::std::vector<uint8_t> mask;
::std::vector<Relocation> relocations;
+
+ AllocationPtr get_relocation(size_t ofs) const {
+ for(const auto& r : relocations) {
+ if(r.slot_ofs == ofs)
+ return r.backing_alloc;
+ }
+ return AllocationPtr();
+ }
+
+ void check_bytes_valid(size_t ofs, size_t size) const;
+ void mark_bytes_valid(size_t ofs, size_t size);
+
+ Value read_value(size_t ofs, size_t size) const;
+ void read_bytes(size_t ofs, void* dst, size_t count) const;
+
+ void write_value(size_t ofs, Value v);
+ void write_bytes(size_t ofs, const void* src, size_t count);
+
+ // TODO: Make this block common
+ void write_u8 (size_t ofs, uint8_t v) { write_bytes(ofs, &v, 1); }
+ void write_u16(size_t ofs, uint16_t v) { write_bytes(ofs, &v, 2); }
+ void write_u32(size_t ofs, uint32_t v) { write_bytes(ofs, &v, 4); }
+ void write_u64(size_t ofs, uint64_t v) { write_bytes(ofs, &v, 8); }
+ void write_i8 (size_t ofs, int8_t v) { write_u8 (ofs, static_cast<uint8_t >(v)); }
+ void write_i16(size_t ofs, int16_t v) { write_u16(ofs, static_cast<uint16_t>(v)); }
+ void write_i32(size_t ofs, int32_t v) { write_u32(ofs, static_cast<uint32_t>(v)); }
+ void write_i64(size_t ofs, int64_t v) { write_u64(ofs, static_cast<uint64_t>(v)); }
+ void write_f32(size_t ofs, float v) { write_bytes(ofs, &v, 4); }
+ void write_f64(size_t ofs, double v) { write_bytes(ofs, &v, 8); }
+ void write_usize(size_t ofs, uint64_t v);
+ void write_isize(size_t ofs, int64_t v) { write_usize(ofs, static_cast<uint64_t>(v)); }
};
+extern ::std::ostream& operator<<(::std::ostream& os, const Allocation& x);
struct Value
{
// If NULL, data is direct
AllocationPtr allocation;
- union {
- struct {
- size_t size;
- size_t offset;
- } indirect_meta;
- struct {
- uint8_t data[2*sizeof(size_t)-3]; // 16-3 = 13, fits in 16 bits of mask
- uint8_t mask[2];
- uint8_t size;
- } direct_data;
- } meta;
+ struct {
+ uint8_t data[2*sizeof(size_t)-3]; // 16-3 = 13, fits in 16 bits of mask
+ uint8_t mask[2];
+ uint8_t size;
+ } direct_data;
Value();
Value(::HIR::TypeRef ty);
static Value new_fnptr(const ::HIR::Path& fn_path);
+ void create_allocation();
+ size_t size() const { return allocation ? allocation.alloc().size() : direct_data.size; }
+
void check_bytes_valid(size_t ofs, size_t size) const;
void mark_bytes_valid(size_t ofs, size_t size);
@@ -130,19 +163,7 @@ struct Value
void write_value(size_t ofs, Value v);
void write_bytes(size_t ofs, const void* src, size_t count);
- uint8_t read_u8(size_t ofs) const { uint8_t rv; read_bytes(ofs, &rv, 1); return rv; }
- int8_t read_i8(size_t ofs) const { return static_cast<int8_t>(read_u8(ofs)); }
- uint16_t read_u16(size_t ofs) const { uint16_t rv; read_bytes(ofs, &rv, 2); return rv; }
- int16_t read_i16(size_t ofs) const { return static_cast<int16_t>(read_u16(ofs)); }
- uint32_t read_u32(size_t ofs) const { uint32_t rv; read_bytes(ofs, &rv, 4); return rv; }
- int32_t read_i32(size_t ofs) const { return static_cast<int32_t>(read_u32(ofs)); }
- uint64_t read_u64(size_t ofs) const { uint64_t rv; read_bytes(ofs, &rv, 8); return rv; }
- int64_t read_i64(size_t ofs) const { return static_cast<int64_t>(read_u64(ofs)); }
- float read_f32(size_t ofs) const { float rv; read_bytes(ofs, &rv, 4); return rv; }
- double read_f64(size_t ofs) const { double rv; read_bytes(ofs, &rv, 8); return rv; }
- uint64_t read_usize(size_t ofs) const;
- int64_t read_isize(size_t ofs) const { return static_cast<int64_t>(read_usize(ofs)); }
-
+ // TODO: Make this block common
void write_u8 (size_t ofs, uint8_t v) { write_bytes(ofs, &v, 1); }
void write_u16(size_t ofs, uint16_t v) { write_bytes(ofs, &v, 2); }
void write_u32(size_t ofs, uint32_t v) { write_bytes(ofs, &v, 4); }
@@ -153,8 +174,65 @@ struct Value
void write_i64(size_t ofs, int64_t v) { write_u64(ofs, static_cast<uint64_t>(v)); }
void write_f32(size_t ofs, float v) { write_bytes(ofs, &v, 4); }
void write_f64(size_t ofs, double v) { write_bytes(ofs, &v, 8); }
-private:
- const uint8_t* data_ptr() const { return allocation ? allocation.alloc().data_ptr() + meta.indirect_meta.offset : meta.direct_data.data; }
- uint8_t* data_ptr() { return allocation ? allocation.alloc().data_ptr() + meta.indirect_meta.offset : meta.direct_data.data; }
+ void write_usize(size_t ofs, uint64_t v);
+ void write_isize(size_t ofs, int64_t v) { write_usize(ofs, static_cast<uint64_t>(v)); }
};
extern ::std::ostream& operator<<(::std::ostream& os, const Value& v);
+
+// A read-only reference to a value (to write, you have to go through it)
+struct ValueRef
+{
+ // Either an AllocationPtr, or a Value pointer
+ AllocationPtr m_alloc;
+ Value* m_value;
+ size_t m_offset;
+ size_t m_size;
+
+ ValueRef(AllocationPtr ptr, size_t ofs, size_t size):
+ m_alloc(ptr),
+ m_offset(ofs),
+ m_size(size)
+ {
+ }
+ ValueRef(Value& val, size_t ofs, size_t size):
+ m_value(&val),
+ m_offset(ofs),
+ m_size(size)
+ {
+ }
+
+ Value read_value(size_t ofs, size_t size) const {
+ assert(ofs < m_size);
+ assert(size <= m_size);
+ assert(ofs+size <= m_size);
+ if( m_alloc ) {
+ return m_alloc.alloc().read_value(m_offset + ofs, size);
+ }
+ else {
+ return m_value->read_value(m_offset + ofs, size);
+ }
+ }
+ void read_bytes(size_t ofs, void* dst, size_t size) const {
+ assert(ofs < m_size);
+ assert(size <= m_size);
+ assert(ofs+size <= m_size);
+ if( m_alloc ) {
+ m_alloc.alloc().read_bytes(m_offset + ofs, dst, size);
+ }
+ else {
+ m_value->read_bytes(m_offset + ofs, dst, size);
+ }
+ }
+ uint8_t read_u8(size_t ofs) const { uint8_t rv; read_bytes(ofs, &rv, 1); return rv; }
+ uint16_t read_u16(size_t ofs) const { uint16_t rv; read_bytes(ofs, &rv, 2); return rv; }
+ uint32_t read_u32(size_t ofs) const { uint32_t rv; read_bytes(ofs, &rv, 4); return rv; }
+ uint64_t read_u64(size_t ofs) const { uint64_t rv; read_bytes(ofs, &rv, 8); return rv; }
+ int8_t read_i8(size_t ofs) const { return static_cast<int8_t>(read_u8(ofs)); }
+ int16_t read_i16(size_t ofs) const { return static_cast<int16_t>(read_u16(ofs)); }
+ int32_t read_i32(size_t ofs) const { return static_cast<int32_t>(read_u32(ofs)); }
+ int64_t read_i64(size_t ofs) const { return static_cast<int64_t>(read_u64(ofs)); }
+ float read_f32(size_t ofs) const { float rv; read_bytes(ofs, &rv, 4); return rv; }
+ double read_f64(size_t ofs) const { double rv; read_bytes(ofs, &rv, 8); return rv; }
+ uint64_t read_usize(size_t ofs) const;
+ int64_t read_isize(size_t ofs) const { return static_cast<int64_t>(read_usize(ofs)); }
+};