diff options
author | John Hodge <tpg@mutabah.net> | 2018-02-17 10:30:13 +0800 |
---|---|---|
committer | John Hodge <tpg@mutabah.net> | 2018-02-17 10:30:13 +0800 |
commit | 0650f7867c2ef3a867c94df0a0dd361b6055c833 (patch) | |
tree | 7f1ce639aeb7dd79acd69c0038dfcd679dcc89c0 | |
parent | d61601143b167d48e50f8cfc81d51770da39fed1 (diff) | |
download | mrust-0650f7867c2ef3a867c94df0a0dd361b6055c833.tar.gz |
Standalone MIRI - Refactor Value/Allocation handling to allow deref to work.
-rw-r--r-- | tools/standalone_miri/debug.cpp | 2 | ||||
-rw-r--r-- | tools/standalone_miri/debug.hpp | 2 | ||||
-rw-r--r-- | tools/standalone_miri/hir_sim.cpp | 2 | ||||
-rw-r--r-- | tools/standalone_miri/hir_sim.hpp | 2 | ||||
-rw-r--r-- | tools/standalone_miri/main.cpp | 184 | ||||
-rw-r--r-- | tools/standalone_miri/value.cpp | 451 | ||||
-rw-r--r-- | tools/standalone_miri/value.hpp | 134 |
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)); } +}; |