diff options
author | John Hodge <tpg@mutabah.net> | 2018-02-11 21:57:10 +0800 |
---|---|---|
committer | John Hodge <tpg@mutabah.net> | 2018-02-11 21:57:10 +0800 |
commit | a3a0c6437302c60bd6f521fc11e30c0a16bd79fc (patch) | |
tree | 83b536f1d5b170a88417015ec0a7f378ab6ea4c4 /tools | |
parent | 14fea32f414df2d1e0b8e2669c8fe13132210ae9 (diff) | |
download | mrust-a3a0c6437302c60bd6f521fc11e30c0a16bd79fc.tar.gz |
Standalone MIRI - Implementation sprint, statics in process.
Diffstat (limited to 'tools')
-rw-r--r-- | tools/standalone_miri/hir_sim.cpp | 26 | ||||
-rw-r--r-- | tools/standalone_miri/hir_sim.hpp | 1 | ||||
-rw-r--r-- | tools/standalone_miri/main.cpp | 156 | ||||
-rw-r--r-- | tools/standalone_miri/module_tree.cpp | 61 | ||||
-rw-r--r-- | tools/standalone_miri/module_tree.hpp | 9 | ||||
-rw-r--r-- | tools/standalone_miri/value.cpp | 183 | ||||
-rw-r--r-- | tools/standalone_miri/value.hpp | 72 |
7 files changed, 478 insertions, 30 deletions
diff --git a/tools/standalone_miri/hir_sim.cpp b/tools/standalone_miri/hir_sim.cpp index 0010e202..7fccc806 100644 --- a/tools/standalone_miri/hir_sim.cpp +++ b/tools/standalone_miri/hir_sim.cpp @@ -6,6 +6,8 @@ #include "hir_sim.hpp" #include "module_tree.hpp" +const size_t POINTER_SIZE = 8; + //::HIR::Path::Path(::HIR::SimplePath sp) //{ //} @@ -22,6 +24,7 @@ size_t HIR::TypeRef::get_size(size_t ofs) const case RawType::Composite: return this->composite_type->size; case RawType::Unreachable: + case RawType::TraitObject: case RawType::Str: throw "Invalid"; case RawType::U8: case RawType::I8: @@ -35,6 +38,16 @@ size_t HIR::TypeRef::get_size(size_t ofs) const case RawType::U128: case RawType::I128: return 16; + case RawType::Bool: + return 1; + case RawType::Char: + return 4; + + case RawType::F32: + return 4; + case RawType::F64: + return 8; + case RawType::Function: // This should probably be invalid? case RawType::USize: case RawType::ISize: return POINTER_SIZE; @@ -52,7 +65,10 @@ size_t HIR::TypeRef::get_size(size_t ofs) const { // Need to look up the metadata type for the actual type if( this->inner_type == RawType::Composite ) - throw "TODO"; + { + ::std::cerr << "TODO: Check metadata type for pointer " << *this << ", assuming none" << ::std::endl; + return POINTER_SIZE; + } else if( this->inner_type == RawType::Str ) return POINTER_SIZE*2; else if( this->inner_type == RawType::TraitObject ) @@ -175,6 +191,12 @@ namespace HIR { case RawType::Unreachable: os << "!"; break; + case RawType::Function: + os << "function_?"; + break; + case RawType::TraitObject: + os << "traitobject_?"; + break; case RawType::Bool: os << "bool"; break; case RawType::Char: os << "char"; break; case RawType::Str: os << "str"; break; @@ -191,6 +213,8 @@ namespace HIR { case RawType::I128: os << "i128"; break; case RawType::USize: os << "usize"; break; case RawType::ISize: os << "isize"; break; + case RawType::F32: os << "f32"; break; + case RawType::F64: os << "f64"; break; } for(auto it = x.wrappers.rbegin(); it != x.wrappers.rend(); ++it) { diff --git a/tools/standalone_miri/hir_sim.hpp b/tools/standalone_miri/hir_sim.hpp index 1303f077..96887536 100644 --- a/tools/standalone_miri/hir_sim.hpp +++ b/tools/standalone_miri/hir_sim.hpp @@ -82,6 +82,7 @@ namespace HIR { /// Definition of a type struct TypeRef { + // Top to bottom list of wrappers (first entry is the outermost wrapper) ::std::vector<TypeWrapper> wrappers; RawType inner_type = RawType::Unit; const DataType* composite_type = nullptr; diff --git a/tools/standalone_miri/main.cpp b/tools/standalone_miri/main.cpp index a6a58a91..c7069ed2 100644 --- a/tools/standalone_miri/main.cpp +++ b/tools/standalone_miri/main.cpp @@ -7,8 +7,6 @@ #include <algorithm> #include <iomanip> -#pragma warning( error : 4061) - struct ProgramOptions { ::std::string infile; @@ -16,7 +14,7 @@ struct ProgramOptions int parse(int argc, const char* argv[]); }; -Value MIRI_Invoke(const ModuleTree& modtree, ::HIR::Path path, ::std::vector<Value> args); +Value MIRI_Invoke(ModuleTree& modtree, ::HIR::Path path, ::std::vector<Value> args); int main(int argc, const char* argv[]) { @@ -31,15 +29,32 @@ int main(int argc, const char* argv[]) tree.load_file(opts.infile); - auto rv = MIRI_Invoke(tree, tree.find_lang_item("start"), {}); + auto val_argc = Value( ::HIR::TypeRef{RawType::I32} ); + ::HIR::TypeRef argv_ty { RawType::I8 }; + argv_ty.wrappers.push_back(TypeWrapper { TypeWrapper::Ty::Pointer, 0 }); + argv_ty.wrappers.push_back(TypeWrapper { TypeWrapper::Ty::Pointer, 0 }); + auto val_argv = Value(argv_ty); + val_argc.write_bytes(0, "\0\0\0", 4); + val_argv.write_bytes(0, "\0\0\0\0\0\0\0", argv_ty.get_size()); + + ::std::vector<Value> args; + args.push_back(::std::move(val_argc)); + args.push_back(::std::move(val_argv)); + auto rv = MIRI_Invoke( tree, tree.find_lang_item("start"), ::std::move(args) ); ::std::cout << rv << ::std::endl; return 0; } -Value MIRI_Invoke(const ModuleTree& modtree, ::HIR::Path path, ::std::vector<Value> args) +Value MIRI_Invoke(ModuleTree& modtree, ::HIR::Path path, ::std::vector<Value> args) { + //TRACE_FUNCTION_FR(path,path) const auto& fcn = modtree.get_function(path); + for(size_t i = 0; i < args.size(); i ++) + { + //DEBUG(a); + ::std::cout << "Argument(" << i << ") = " << args[i] << ::std::endl; + } ::std::vector<bool> drop_flags = fcn.m_mir.drop_flags; ::std::vector<Value> locals; locals.reserve( fcn.m_mir.locals.size() ); @@ -52,12 +67,14 @@ Value MIRI_Invoke(const ModuleTree& modtree, ::HIR::Path path, ::std::vector<Val struct State { + ModuleTree& modtree; const Function& fcn; Value ret; ::std::vector<Value> args; ::std::vector<Value> locals; - State(const Function& fcn, ::std::vector<Value> args): + State(ModuleTree& modtree, const Function& fcn, ::std::vector<Value> args): + modtree(modtree), fcn(fcn), ret(fcn.ret_ty), args(::std::move(args)) @@ -73,6 +90,7 @@ Value MIRI_Invoke(const ModuleTree& modtree, ::HIR::Path path, ::std::vector<Val { switch(lv.tag()) { + case ::MIR::LValue::TAGDEAD: throw ""; TU_ARM(lv, Return, _e) { ofs = 0; ty = fcn.ret_ty; @@ -88,6 +106,9 @@ Value MIRI_Invoke(const ModuleTree& modtree, ::HIR::Path path, ::std::vector<Val ty = fcn.args.at(e.idx); return args.at(e.idx); } break; + TU_ARM(lv, Static, e) { + return modtree.get_static(e); + } break; TU_ARM(lv, Index, e) { auto idx = read_lvalue(*e.idx).as_usize(); ::HIR::TypeRef array_ty; @@ -118,6 +139,23 @@ Value MIRI_Invoke(const ModuleTree& modtree, ::HIR::Path path, ::std::vector<Val ofs += 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); + + size_t inner_ofs; + ty = composite_ty.get_field(e.variant_index, inner_ofs); + ::std::cerr << "TODO: Read from Downcast - " << lv << ::std::endl; + throw "TODO"; + ofs += inner_ofs; + return base_val; + } + TU_ARM(lv, Deref, e) { + //auto addr = read_lvalue(*e.val); + + ::std::cerr << "TODO: Read from deref - " << lv << ::std::endl; + throw "TODO"; + } break; } throw ""; } @@ -161,6 +199,7 @@ Value MIRI_Invoke(const ModuleTree& modtree, ::HIR::Path path, ::std::vector<Val { switch(c.tag()) { + case ::MIR::Constant::TAGDEAD: throw ""; TU_ARM(c, Int, ce) { ty = ::HIR::TypeRef(ce.t); Value val = Value(ty); @@ -174,6 +213,42 @@ Value MIRI_Invoke(const ModuleTree& modtree, ::HIR::Path path, ::std::vector<Val val.write_bytes(0, &ce.v, ::std::min(ty.get_size(), sizeof(ce.v))); // TODO: Endian return val; } break; + TU_ARM(c, Bool, ce) { + Value val = Value(::HIR::TypeRef { RawType::Bool }); + val.write_bytes(0, &ce.v, 1); + return val; + } break; + TU_ARM(c, Float, ce) { + ty = ::HIR::TypeRef(ce.t); + Value val = Value(ty); + if( ce.t.raw_type == RawType::F64 ) { + val.write_bytes(0, &ce.v, ::std::min(ty.get_size(), sizeof(ce.v))); // TODO: Endian/format? + } + else if( ce.t.raw_type == RawType::F32 ) { + float v = static_cast<float>(ce.v); + val.write_bytes(0, &v, ::std::min(ty.get_size(), sizeof(v))); // TODO: Endian/format? + } + else { + throw ::std::runtime_error("BUG: Invalid type in Constant::Float"); + } + return val; + } break; + TU_ARM(c, Const, ce) { + throw ::std::runtime_error("BUG: Constant::Const in mmir"); + } break; + TU_ARM(c, Bytes, ce) { + throw ::std::runtime_error("TODO: Constant::Bytes"); + } break; + TU_ARM(c, StaticString, ce) { + throw ::std::runtime_error("TODO: Constant::StaticString"); + } break; + TU_ARM(c, ItemAddr, ce) { + // Create a value with a special backing allocation of zero size that references the specified item. + if( const auto* fn = modtree.get_function_opt(ce) ) { + return Value::new_fnptr(ce); + } + throw ::std::runtime_error("TODO: Constant::ItemAddr"); + } break; } throw ""; } @@ -186,6 +261,7 @@ Value MIRI_Invoke(const ModuleTree& modtree, ::HIR::Path path, ::std::vector<Val { switch(p.tag()) { + case ::MIR::Param::TAGDEAD: throw ""; TU_ARM(p, Constant, pe) return const_to_value(pe, ty); TU_ARM(p, LValue, pe) @@ -198,7 +274,7 @@ Value MIRI_Invoke(const ModuleTree& modtree, ::HIR::Path path, ::std::vector<Val ::HIR::TypeRef ty; return param_to_value(p, ty); } - } state { fcn, ::std::move(args) }; + } state { modtree, fcn, ::std::move(args) }; size_t bb_idx = 0; for(;;) @@ -210,16 +286,38 @@ Value MIRI_Invoke(const ModuleTree& modtree, ::HIR::Path path, ::std::vector<Val ::std::cout << "BB" << bb_idx << "/" << (&stmt - bb.statements.data()) << ": " << stmt << ::std::endl; switch(stmt.tag()) { + case ::MIR::Statement::TAGDEAD: throw ""; TU_ARM(stmt, Assign, se) { Value 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)); } break; TU_ARM(se.src, Constant, re) { state.write_lvalue(se.dst, state.const_to_value(re)); } break; + TU_ARM(se.src, Borrow, re) { + ::HIR::TypeRef src_ty; + size_t ofs = 0; + Value& base_value = state.get_value_type_and_ofs(re.val, ofs, src_ty); + if( !base_value.allocation ) + { + // 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(); + } + ofs += base_value.meta.indirect_meta.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, base_value.allocation }); + new_val.write_bytes(0, &ofs, src_ty.get_size()); + ::std::cerr << "TODO: RValue::Borrow - " << se.src << ::std::endl; + throw "TODO"; + } break; TU_ARM(se.src, SizedArray, re) { throw "TODO"; } break; @@ -272,17 +370,61 @@ Value MIRI_Invoke(const ModuleTree& modtree, ::HIR::Path path, ::std::vector<Val case ::MIR::Statement::TAG_SetDropFlag: throw "TODO"; break; + case ::MIR::Statement::TAG_ScopeEnd: + throw "TODO"; + break; } } ::std::cout << "BB" << bb_idx << "/TERM: " << bb.terminator << ::std::endl; switch(bb.terminator.tag()) { + case ::MIR::Terminator::TAGDEAD: throw ""; + TU_ARM(bb.terminator, Incomplete, _te) + throw ::std::runtime_error("BUG: Terminator::Incomplete hit"); + TU_ARM(bb.terminator, Diverge, _te) + throw ::std::runtime_error("BUG: Terminator::Diverge hit"); + TU_ARM(bb.terminator, Panic, _te) + throw ::std::runtime_error("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"); + TU_ARM(bb.terminator, Switch, _te) + throw ::std::runtime_error("TODO: Terminator::Switch"); + TU_ARM(bb.terminator, SwitchValue, _te) + throw ::std::runtime_error("TODO: Terminator::SwitchValue"); + TU_ARM(bb.terminator, Call, te) { + if( te.fcn.is_Intrinsic() ) { + throw ::std::runtime_error("TODO: Terminator::Call - intrinsic"); + } + else { + const ::HIR::Path* fcn_p; + if( te.fcn.is_Path() ) { + fcn_p = &te.fcn.as_Path(); + } + else { + ::HIR::TypeRef ty; + auto v = state.read_lvalue_with_ty(te.fcn.as_Value(), ty); + // TODO: Assert type + // TODO: Assert offset/content. + assert(v.as_usize() == 0); + fcn_p = &v.allocation.alloc().relocations.at(0).backing_alloc.fcn(); + } + + ::std::vector<Value> sub_args; sub_args.reserve(te.args.size()); + for(const auto& a : te.args) + { + sub_args.push_back( state.param_to_value(a) ); + } + ::std::cout << "TODO: 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/module_tree.cpp b/tools/standalone_miri/module_tree.cpp index f056481b..90d599c8 100644 --- a/tools/standalone_miri/module_tree.cpp +++ b/tools/standalone_miri/module_tree.cpp @@ -3,6 +3,7 @@ // #include "module_tree.hpp" #include "lex.hpp" +#include "value.hpp" #include <iostream> ModuleTree::ModuleTree() @@ -73,7 +74,7 @@ bool Parser::parse_one() else if( lex.consume_if("fn") ) { auto p = parse_path(); - //::std::cout << "DEBUG:p arse_one - fn " << p << ::std::endl; + //::std::cout << "DEBUG: parse_one - fn " << p << ::std::endl; lex.check_consume('('); ::std::vector<::HIR::TypeRef> arg_tys; @@ -91,17 +92,33 @@ bool Parser::parse_one() } auto body = parse_body(); - tree.functions.insert( ::std::make_pair(::std::move(p), Function { ::std::move(arg_tys), rv_ty, ::std::move(body) }) ); + auto p2 = p; + tree.functions.insert( ::std::make_pair(::std::move(p), Function { ::std::move(p2), ::std::move(arg_tys), rv_ty, ::std::move(body) }) ); } else if( lex.consume_if("static") ) { auto p = parse_path(); //::std::cout << "DEBUG: parse_one - static " << p << ::std::endl; + lex.check_consume(':'); + auto ty = parse_type(); + // TODO: externs? lex.check_consume('='); - // TODO: Body? Value? - //auto body = parse_body(); - //auto data = ::std::move(lex.consume().strval); - throw "TODO"; + lex.check(TokenClass::String); + auto data = ::std::move(lex.consume().strval); + if( lex.consume_if('{') ) + { + while( !lex.consume_if('}') ) + { + // TODO: Parse relocation entries + throw "TODO"; + } + } + lex.check_consume(';'); + + Value val = Value(ty); + val.write_bytes(0, data.data(), data.size()); + + tree.statics.insert(::std::make_pair( ::std::move(p), ::std::move(val) )); } else if( lex.consume_if("type") ) { @@ -354,7 +371,7 @@ bool Parser::parse_one() else if( p.lex.consume_if("false") ) { return ::MIR::Constant::make_Bool({ false }); } - else if( p.lex.consume_if("&") ) { + else if( p.lex.consume_if("ADDROF") ) { auto path = p.parse_path(); return ::MIR::Constant::make_ItemAddr({ ::std::move(path) }); @@ -371,6 +388,7 @@ bool Parser::parse_one() if( p.lex.next() == TokenClass::Integer || p.lex.next() == TokenClass::String || p.lex.next() == TokenClass::ByteString || p.lex.next() == '+' || p.lex.next() == '-' || p.lex.next() == '&' || p.lex.next() == "true" || p.lex.next() == "false" + || p.lex.next() == "ADDROF" ) { return parse_const(p); @@ -432,6 +450,7 @@ bool Parser::parse_one() if( lex.next() == TokenClass::Integer || lex.next() == TokenClass::String || lex.next() == TokenClass::ByteString || lex.next() == '+' || lex.next() == '-' || lex.next() == "true" || lex.next() == "false" + || lex.next() == "ADDROF" ) { src_rval = H::parse_const(*this); @@ -1203,4 +1222,32 @@ const Function& ModuleTree::get_function(const ::HIR::Path& p) const throw ""; } return it->second; +} +const Function* ModuleTree::get_function_opt(const ::HIR::Path& p) const +{ + auto it = functions.find(p); + if(it == functions.end()) + { + return nullptr; + } + return &it->second; +} +Value& ModuleTree::get_static(const ::HIR::Path& p) +{ + auto it = statics.find(p); + if(it == statics.end()) + { + ::std::cerr << "Unable to find static " << p << " for invoke" << ::std::endl; + throw ""; + } + return it->second; +} +Value* ModuleTree::get_static_opt(const ::HIR::Path& p) +{ + auto it = statics.find(p); + if(it == statics.end()) + { + return nullptr; + } + return &it->second; }
\ No newline at end of file diff --git a/tools/standalone_miri/module_tree.hpp b/tools/standalone_miri/module_tree.hpp index ce831621..96a77718 100644 --- a/tools/standalone_miri/module_tree.hpp +++ b/tools/standalone_miri/module_tree.hpp @@ -10,8 +10,11 @@ #include "../../src/mir/mir.hpp" #include "hir_sim.hpp" +struct Value; + struct Function { + ::HIR::Path my_path; ::std::vector<::HIR::TypeRef> args; ::HIR::TypeRef ret_ty; ::MIR::Function m_mir; @@ -25,6 +28,9 @@ class ModuleTree ::std::set<::std::string> loaded_files; ::std::map<::HIR::Path, Function> functions; + ::std::map<::HIR::Path, Value> statics; + // TODO: statics + // Hack: Tuples are stored as `::""::<A,B,C,...>` ::std::map<::HIR::GenericPath, ::std::unique_ptr<DataType>> data_types; public: @@ -34,6 +40,9 @@ public: ::HIR::SimplePath find_lang_item(const char* name) const; const Function& get_function(const ::HIR::Path& p) const; + const Function* get_function_opt(const ::HIR::Path& p) const; + Value& get_static(const ::HIR::Path& p); + Value* get_static_opt(const ::HIR::Path& p); }; // struct/union/enum diff --git a/tools/standalone_miri/value.cpp b/tools/standalone_miri/value.cpp index e04154f6..59b68ea0 100644 --- a/tools/standalone_miri/value.cpp +++ b/tools/standalone_miri/value.cpp @@ -8,6 +8,54 @@ #include <iomanip> #include <algorithm> + +AllocationPtr Allocation::new_alloc(size_t size) +{ + Allocation* rv = new Allocation(); + rv->refcount = 1; + rv->data.resize( (size + 8-1) / 8 ); // QWORDS + rv->mask.resize( (size + 8-1) / 8 ); // bitmap bytes + return AllocationPtr(rv); +} +AllocationPtr AllocationPtr::new_fcn(::HIR::Path p) +{ + AllocationPtr rv; + auto* ptr = new ::HIR::Path(::std::move(p)); + rv.m_ptr = reinterpret_cast<void*>( reinterpret_cast<uintptr_t>(ptr) + static_cast<uintptr_t>(Ty::Function) ); + return rv; +} +AllocationPtr::AllocationPtr(const AllocationPtr& x): + m_ptr(x.m_ptr) +{ + if( is_alloc() ) { + assert(alloc().refcount != SIZE_MAX); + alloc().refcount += 1; + } +} +AllocationPtr::~AllocationPtr() +{ + if( *this ) + { + switch(get_ty()) + { + case Ty::Allocation: { + auto* ptr = &alloc(); + ptr->refcount -= 1; + if(ptr->refcount == 0) + delete ptr; + } break; + case Ty::Function: { + auto* ptr = const_cast<::HIR::Path*>(&fcn()); + delete ptr; + } break; + case Ty::Unused1: { + } break; + case Ty::Unused2: { + } break; + } + } +} + Value::Value() { this->meta.direct_data.size = 0; @@ -18,6 +66,7 @@ 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) ) { struct H @@ -26,6 +75,11 @@ Value::Value(::HIR::TypeRef ty) { if( ty.wrappers.empty() || ::std::all_of(ty.wrappers.begin(), ty.wrappers.end(), [](const auto& x){ return x.type == TypeWrapper::Ty::Array; }) ) { + // TODO: Function pointers should be _pointers_ + if( ty.inner_type == RawType::Function ) + { + return true; + } // Check the inner type if( ty.inner_type != RawType::Composite ) { @@ -45,28 +99,69 @@ Value::Value(::HIR::TypeRef ty) if( ! H::has_pointer(ty) ) { // Will fit in a inline allocation, nice. + ::std::cout << "Value::Value(): No pointers in " << ty << ", storing inline" << ::std::endl; this->meta.direct_data.size = static_cast<uint8_t>(size); + this->meta.direct_data.mask[0] = 0; + this->meta.direct_data.mask[1] = 0; return ; } } #endif // Fallback: Make a new allocation - throw "TODO"; + ::std::cout << "Value::Value(): Creating allocation for " << ty << ::std::endl; + 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) +{ + Value rv( ::HIR::TypeRef(::HIR::CoreType { RawType::Function }) ); + assert(rv.allocation); + rv.allocation.alloc().relocations.push_back(Relocation { 0, AllocationPtr::new_fcn(fn_path) }); + rv.allocation.alloc().data.at(0) = 0; + rv.allocation.alloc().mask.at(0) = 0xFF; // TODO: Get pointer size and make that much valid instead of 8 bytes + return rv; } void Value::check_bytes_valid(size_t ofs, size_t size) const { if( this->allocation ) { - throw "TODO"; + 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"; + } + } } else { - for(size_t i = 0; i < this->meta.direct_data.size; i++) + if( size == 0 && this->meta.direct_data.size > 0 ) { + return ; + } + if( ofs >= this->meta.direct_data.size ) { + ::std::cerr << "ERROR: OOB read" << ::std::endl; + throw "ERROR"; + } + if( ofs+size > this->meta.direct_data.size ) { + ::std::cerr << "ERROR: OOB read" << ::std::endl; + throw "ERROR"; + } + for(size_t i = ofs; i < ofs + size; i++) { if( !(this->meta.direct_data.mask[i/8] & (1 << i%8)) ) { + ::std::cerr << "ERROR: Invalid bytes in value" << ::std::endl; throw "ERROR"; } } @@ -76,7 +171,14 @@ void Value::mark_bytes_valid(size_t ofs, size_t size) { if( this->allocation ) { - throw "TODO"; + auto& alloc = this->allocation.alloc(); + // TODO: Assert range. + ofs += this->meta.indirect_meta.offset; + assert( (ofs+size+8-1) / 8 < alloc.mask.size() ); + for(size_t i = ofs; i < ofs + size; i++) + { + alloc.mask[i/8] |= (1 << i%8); + } } else { @@ -93,7 +195,29 @@ Value Value::read_value(size_t ofs, size_t size) const check_bytes_valid(ofs, size); if( this->allocation ) { - throw "TODO"; + 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; + } + } + Value rv; + 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; + } + else + { + rv.meta.direct_data.size = static_cast<uint8_t>(size); + } + rv.write_bytes(0, this->data_ptr() + ofs, size); + return rv; } else { @@ -114,7 +238,12 @@ void Value::write_bytes(size_t ofs, const void* src, size_t count) { if( this->allocation ) { - throw "TODO"; + if(ofs >= this->meta.indirect_meta.size ) + throw "ERROR"; + if(count > this->meta.indirect_meta.size ) + throw "ERROR"; + if(ofs+count > this->meta.indirect_meta.size ) + throw "ERROR"; } else { @@ -124,21 +253,36 @@ void Value::write_bytes(size_t ofs, const void* src, size_t count) throw "ERROR"; if(ofs+count > this->meta.direct_data.size ) throw "ERROR"; - ::std::memcpy(this->meta.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 ) { - throw "TODO"; + v.check_bytes_valid(0, v.meta.indirect_meta.size); + const auto& src_alloc = v.allocation.alloc(); + write_bytes(ofs, v.data_ptr(), v.meta.indirect_meta.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.meta.indirect_meta.size` + for(const auto& r : src_alloc.relocations) + { + // TODO: Negative offsets in destination? + if( v.meta.indirect_meta.offset <= r.slot_ofs && r.slot_ofs < v.meta.indirect_meta.offset + v.meta.indirect_meta.size ) + { + // Applicable + if( !this->allocation ) { + throw ::std::runtime_error("TODO: Writing value with a relocation into a slot without a relocation"); + } + this->allocation.alloc().relocations.push_back( r ); + } + } } else { v.check_bytes_valid(0, v.meta.direct_data.size); write_bytes(ofs, v.meta.direct_data.data, v.meta.direct_data.size); - mark_bytes_valid(ofs, meta.direct_data.size); } } @@ -146,7 +290,7 @@ size_t Value::as_usize() const { uint64_t v; this->read_bytes(0, &v, 8); - // TODO: Handle endian + // TODO: Handle endian and different architectures return v; } ::std::ostream& operator<<(::std::ostream& os, const Value& v) @@ -155,7 +299,22 @@ size_t Value::as_usize() const os << ::std::hex; if( v.allocation ) { - throw "TODO"; + 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 << "--"; + } + } } else { diff --git a/tools/standalone_miri/value.hpp b/tools/standalone_miri/value.hpp index 65403a81..9f9e68f4 100644 --- a/tools/standalone_miri/value.hpp +++ b/tools/standalone_miri/value.hpp @@ -6,33 +6,95 @@ #include <vector> #include <memory> #include <cstdint> +#include <cassert> namespace HIR { struct TypeRef; + struct Path; } class Allocation; + class AllocationPtr { friend class Allocation; - Allocation* m_ptr; + void* m_ptr; +public: + + enum class Ty + { + Allocation, + Function, // m_ptr is a pointer to the function. + Unused1, + Unused2, + }; + +private: + AllocationPtr(Allocation* p): + m_ptr(p) + { + } public: AllocationPtr(): m_ptr(nullptr) {} + AllocationPtr(AllocationPtr&& x): m_ptr(x.m_ptr) { + x.m_ptr = nullptr; + } + AllocationPtr(const AllocationPtr& x); + ~AllocationPtr(); + static AllocationPtr new_fcn(::HIR::Path p); + + AllocationPtr& operator=(AllocationPtr&& x) { + this->~AllocationPtr(); + this->m_ptr = x.m_ptr; + x.m_ptr = nullptr; + return *this; + } operator bool() const { return m_ptr != 0; } - Allocation& operator*() { return *m_ptr; } - Allocation* operator->() { return m_ptr; } + bool is_alloc() { + return *this && get_ty() == Ty::Allocation; + } + Allocation& alloc() { + assert(get_ty() == Ty::Allocation); + return *static_cast<Allocation*>(get_ptr()); + } + const Allocation& alloc() const { + assert(get_ty() == Ty::Allocation); + return *static_cast<Allocation*>(get_ptr()); + } + const ::HIR::Path& fcn() const { + assert(get_ty() == Ty::Function); + return *static_cast<const ::HIR::Path*>(get_ptr()); + } + + Ty get_ty() const { + return static_cast<Ty>( reinterpret_cast<uintptr_t>(m_ptr) & 3 ); + } +private: + void* get_ptr() const { + return reinterpret_cast<void*>( reinterpret_cast<uintptr_t>(m_ptr) & ~3 ); + } }; struct Relocation { + // Offset within parent allocation where this relocation is performed. + // TODO: Size? size_t slot_ofs; AllocationPtr backing_alloc; }; class Allocation { + friend class AllocationPtr; size_t refcount; + // TODO: Read-only flag? public: + static AllocationPtr new_alloc(size_t size); + + 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()); } + ::std::vector<uint64_t> data; + ::std::vector<uint8_t> mask; ::std::vector<Relocation> relocations; }; @@ -54,6 +116,7 @@ struct Value Value(); Value(::HIR::TypeRef ty); + static Value new_fnptr(const ::HIR::Path& fn_path); void check_bytes_valid(size_t ofs, size_t size) const; void mark_bytes_valid(size_t ofs, size_t size); @@ -65,5 +128,8 @@ struct Value void write_bytes(size_t ofs, const void* src, size_t count); size_t as_usize() const; +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; } }; extern ::std::ostream& operator<<(::std::ostream& os, const Value& v); |