diff options
Diffstat (limited to 'tools')
-rw-r--r-- | tools/standalone_miri/main.cpp | 337 | ||||
-rw-r--r-- | tools/standalone_miri/value.cpp | 24 | ||||
-rw-r--r-- | tools/standalone_miri/value.hpp | 8 |
3 files changed, 332 insertions, 37 deletions
diff --git a/tools/standalone_miri/main.cpp b/tools/standalone_miri/main.cpp index 186f43c3..8823cf44 100644 --- a/tools/standalone_miri/main.cpp +++ b/tools/standalone_miri/main.cpp @@ -17,7 +17,7 @@ struct ProgramOptions Value MIRI_Invoke(ModuleTree& modtree, ::HIR::Path path, ::std::vector<Value> args); Value MIRI_Invoke_Extern(const ::std::string& link_name, const ::std::string& abi, ::std::vector<Value> args); -Value MIRI_Invoke_Intrinsic(const ::std::string& name, const ::HIR::PathParams& ty_params, ::std::vector<Value> args); +Value MIRI_Invoke_Intrinsic(const ModuleTree& modtree, const ::std::string& name, const ::HIR::PathParams& ty_params, ::std::vector<Value> args); int main(int argc, const char* argv[]) { @@ -48,6 +48,196 @@ int main(int argc, const char* argv[]) return 0; } +class PrimitiveValue +{ +public: + virtual ~PrimitiveValue() {} + + virtual bool add(const PrimitiveValue& v) = 0; + virtual bool subtract(const PrimitiveValue& v) = 0; + virtual bool multiply(const PrimitiveValue& v) = 0; + virtual bool divide(const PrimitiveValue& v) = 0; + virtual bool modulo(const PrimitiveValue& v) = 0; + virtual void write_to_value(Value& tgt, size_t ofs) const = 0; + + template<typename T> + const T& check(const char* opname) const + { + const auto* xp = dynamic_cast<const T*>(this); + LOG_ASSERT(xp, "Attempting to " << opname << " mismatched types, expected " << typeid(T).name() << " got " << typeid(*this).name()); + return *xp; + } +}; +template<typename T> +struct PrimitiveUInt: + public PrimitiveValue +{ + typedef PrimitiveUInt<T> Self; + T v; + + PrimitiveUInt(T v): v(v) {} + ~PrimitiveUInt() override {} + + bool add(const PrimitiveValue& x) override { + const auto* xp = &x.check<Self>("add"); + T newv = this->v + xp->v; + bool did_overflow = newv < this->v; + this->v = newv; + return !did_overflow; + } + bool subtract(const PrimitiveValue& x) override { + const auto* xp = &x.check<Self>("subtract"); + T newv = this->v - xp->v; + bool did_overflow = newv > this->v; + this->v = newv; + return !did_overflow; + } + bool multiply(const PrimitiveValue& x) override { + const auto* xp = &x.check<Self>("multiply"); + T newv = this->v * xp->v; + bool did_overflow = newv < this->v && newv < xp->v; + this->v = newv; + return !did_overflow; + } + bool divide(const PrimitiveValue& x) override { + const auto* xp = &x.check<Self>("divide"); + if(xp->v == 0) return false; + T newv = this->v / xp->v; + this->v = newv; + return true; + } + bool modulo(const PrimitiveValue& x) override { + const auto* xp = &x.check<Self>("modulo"); + if(xp->v == 0) return false; + T newv = this->v % xp->v; + this->v = newv; + return true; + } +}; +struct PrimitiveU64: public PrimitiveUInt<uint64_t> +{ + PrimitiveU64(uint64_t v): PrimitiveUInt(v) {} + void write_to_value(Value& tgt, size_t ofs) const override { + tgt.write_u64(ofs, this->v); + } +}; +struct PrimitiveU32: public PrimitiveUInt<uint32_t> +{ + PrimitiveU32(uint32_t v): PrimitiveUInt(v) {} + void write_to_value(Value& tgt, size_t ofs) const override { + tgt.write_u32(ofs, this->v); + } +}; +template<typename T> +struct PrimitiveSInt: + public PrimitiveValue +{ + typedef PrimitiveSInt<T> Self; + T v; + + PrimitiveSInt(T v): v(v) {} + ~PrimitiveSInt() override {} + + // TODO: Make this correct. + bool add(const PrimitiveValue& x) override { + const auto* xp = &x.check<Self>("add"); + T newv = this->v + xp->v; + bool did_overflow = newv < this->v; + this->v = newv; + return !did_overflow; + } + bool subtract(const PrimitiveValue& x) override { + const auto* xp = &x.check<Self>("subtract"); + T newv = this->v - xp->v; + bool did_overflow = newv > this->v; + this->v = newv; + return !did_overflow; + } + bool multiply(const PrimitiveValue& x) override { + const auto* xp = &x.check<Self>("multiply"); + T newv = this->v * xp->v; + bool did_overflow = newv < this->v && newv < xp->v; + this->v = newv; + return !did_overflow; + } + bool divide(const PrimitiveValue& x) override { + const auto* xp = &x.check<Self>("divide"); + if(xp->v == 0) return false; + T newv = this->v / xp->v; + this->v = newv; + return true; + } + bool modulo(const PrimitiveValue& x) override { + const auto* xp = &x.check<Self>("modulo"); + if(xp->v == 0) return false; + T newv = this->v % xp->v; + this->v = newv; + return true; + } +}; +struct PrimitiveI64: public PrimitiveSInt<int64_t> +{ + PrimitiveI64(int64_t v): PrimitiveSInt(v) {} + void write_to_value(Value& tgt, size_t ofs) const override { + tgt.write_i64(ofs, this->v); + } +}; +struct PrimitiveI32: public PrimitiveSInt<int32_t> +{ + PrimitiveI32(int32_t v): PrimitiveSInt(v) {} + void write_to_value(Value& tgt, size_t ofs) const override { + tgt.write_i32(ofs, this->v); + } +}; + +class PrimitiveValueVirt +{ + uint64_t buf[3]; // Allows i128 plus a vtable pointer + PrimitiveValueVirt() {} +public: + // HACK: No copy/move constructors, assumes that contained data is always POD + ~PrimitiveValueVirt() { + reinterpret_cast<PrimitiveValue*>(&this->buf)->~PrimitiveValue(); + } + PrimitiveValue& get() { return *reinterpret_cast<PrimitiveValue*>(&this->buf); } + const PrimitiveValue& get() const { return *reinterpret_cast<const PrimitiveValue*>(&this->buf); } + + static PrimitiveValueVirt from_value(const ::HIR::TypeRef& t, const ValueRef& v) { + PrimitiveValueVirt rv; + LOG_ASSERT(t.wrappers.empty(), "PrimitiveValueVirt::from_value: " << t); + switch(t.inner_type) + { + case RawType::U32: + new(&rv.buf) PrimitiveU32(v.read_u32(0)); + break; + case RawType::U64: + new(&rv.buf) PrimitiveU64(v.read_u64(0)); + break; + case RawType::USize: + if( POINTER_SIZE == 8 ) + new(&rv.buf) PrimitiveU64(v.read_u64(0)); + else + new(&rv.buf) PrimitiveU32(v.read_u32(0)); + break; + + case RawType::I32: + new(&rv.buf) PrimitiveI32(v.read_i32(0)); + break; + case RawType::I64: + new(&rv.buf) PrimitiveI64(v.read_i64(0)); + break; + case RawType::ISize: + if( POINTER_SIZE == 8 ) + new(&rv.buf) PrimitiveI64(v.read_i64(0)); + else + new(&rv.buf) PrimitiveI32(v.read_i32(0)); + break; + default: + LOG_TODO("PrimitiveValueVirt::from_value: " << t); + } + return rv; + } +}; struct Ops { template<typename T> @@ -109,7 +299,7 @@ Value MIRI_Invoke(ModuleTree& modtree, ::HIR::Path path, ::std::vector<Value> ar State(ModuleTree& modtree, const Function& fcn, ::std::vector<Value> args): modtree(modtree), fcn(fcn), - ret(fcn.ret_ty), + ret(fcn.ret_ty == RawType::Unreachable ? ::HIR::TypeRef() : fcn.ret_ty), args(::std::move(args)), drop_flags(fcn.m_mir.drop_flags) { @@ -180,10 +370,10 @@ Value MIRI_Invoke(ModuleTree& modtree, ::HIR::Path path, ::std::vector<Value> ar TU_ARM(lv, Downcast, e) { ::HIR::TypeRef composite_ty; auto base_val = get_value_and_type(*e.val, composite_ty); + LOG_DEBUG("Downcast - " << composite_ty); size_t inner_ofs; ty = composite_ty.get_field(e.variant_index, inner_ofs); - LOG_TODO("Read from Downcast - " << lv); base_val.m_offset += inner_ofs; return base_val; } @@ -477,7 +667,7 @@ 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( re.type != RawType::USize ) { + if( re.type != RawType::USize && re.type != RawType::ISize ) { LOG_ERROR("Casting from a pointer to non-usize - " << re.type << " to " << src_ty); throw "ERROR"; } @@ -569,7 +759,7 @@ Value MIRI_Invoke(ModuleTree& modtree, ::HIR::Path path, ::std::vector<Value> ar case RawType::I32: case RawType::I64: { - uint64_t dst_val = 0.0; + uint64_t dst_val = 0; // Can be an integer, or F32 (pointer is impossible atm) switch(src_ty.inner_type) { @@ -630,7 +820,7 @@ Value MIRI_Invoke(ModuleTree& modtree, ::HIR::Path path, ::std::vector<Value> ar new_val.write_u16(0, static_cast<uint16_t>(dst_val)); break; case RawType::U32: - new_val.write_u32(0, dst_val); + new_val.write_u32(0, static_cast<uint32_t>(dst_val)); break; case RawType::U64: new_val.write_u64(0, dst_val); @@ -669,7 +859,7 @@ Value MIRI_Invoke(ModuleTree& modtree, ::HIR::Path path, ::std::vector<Value> ar Value tmp_l, tmp_r; auto v_l = state.get_value_ref_param(re.val_l, tmp_l, ty_l); auto v_r = state.get_value_ref_param(re.val_r, tmp_r, ty_r); - //LOG_DEBUG(v_l << " ? " << v_r); + LOG_DEBUG(v_l << " ? " << v_r); switch(re.op) { @@ -729,8 +919,8 @@ Value MIRI_Invoke(ModuleTree& modtree, ::HIR::Path path, ::std::vector<Value> ar // Compare fat metadata. if( res == 0 && v_l.m_size > POINTER_SIZE ) { - reloc_l = v_l.m_alloc ? v_l.m_alloc.alloc().get_relocation(POINTER_SIZE) : AllocationPtr(); - reloc_r = v_r.m_alloc ? v_r.m_alloc.alloc().get_relocation(POINTER_SIZE) : AllocationPtr(); + reloc_l = alloc_l ? alloc_l.alloc().get_relocation(POINTER_SIZE) : AllocationPtr(); + reloc_r = alloc_r ? alloc_r.alloc().get_relocation(POINTER_SIZE) : AllocationPtr(); if( res == 0 && reloc_l != reloc_r ) { @@ -761,33 +951,20 @@ Value MIRI_Invoke(ModuleTree& modtree, ::HIR::Path path, ::std::vector<Value> ar } break; default: LOG_ASSERT(ty_l == ty_r, "BinOp type mismatch - " << ty_l << " != " << ty_r); - new_val = Value(ty_l); - switch(ty_l.inner_type) + auto val_l = PrimitiveValueVirt::from_value(ty_l, v_l); + auto val_r = PrimitiveValueVirt::from_value(ty_r, v_r); + switch(re.op) { - case RawType::U128: - LOG_TODO("BinOp U128"); - case RawType::U64: - new_val.write_u64( 0, Ops::do_unsigned(v_l.read_u64(0), v_r.read_u64(0), re.op) ); - break; - case RawType::U32: - new_val = Value(ty_l); - new_val.write_u32( 0, Ops::do_unsigned(v_l.read_u32(0), v_r.read_u32(0), re.op) ); - break; - case RawType::U16: - new_val = Value(ty_l); - new_val.write_u16( 0, Ops::do_unsigned(v_l.read_u16(0), v_r.read_u16(0), re.op) ); - break; - case RawType::U8: - new_val = Value(ty_l); - new_val.write_u8 ( 0, Ops::do_unsigned(v_l.read_u8 (0), v_r.read_u8 (0), re.op) ); - break; - case RawType::USize: - new_val = Value(ty_l); - new_val.write_usize( 0, Ops::do_unsigned(v_l.read_usize(0), v_r.read_usize(0), re.op) ); - break; + case ::MIR::eBinOp::ADD: val_l.get().add( val_r.get() ); break; + case ::MIR::eBinOp::SUB: val_l.get().subtract( val_r.get() ); break; + case ::MIR::eBinOp::MUL: val_l.get().multiply( val_r.get() ); break; + case ::MIR::eBinOp::DIV: val_l.get().divide( val_r.get() ); break; + case ::MIR::eBinOp::MOD: val_l.get().modulo( val_r.get() ); break; default: - LOG_TODO("Handle BinOp - w/ type " << ty_l); + throw ""; } + new_val = Value(ty_l); + val_l.get().write_to_value(new_val, 0); break; } } break; @@ -1091,7 +1268,7 @@ Value MIRI_Invoke(ModuleTree& modtree, ::HIR::Path path, ::std::vector<Value> ar if( te.fcn.is_Intrinsic() ) { const auto& fe = te.fcn.as_Intrinsic(); - state.write_lvalue(te.ret_val, MIRI_Invoke_Intrinsic(fe.name, fe.params, ::std::move(sub_args))); + state.write_lvalue(te.ret_val, MIRI_Invoke_Intrinsic(modtree, fe.name, fe.params, ::std::move(sub_args))); } else { @@ -1146,15 +1323,36 @@ Value MIRI_Invoke_Extern(const ::std::string& link_name, const ::std::string& ab rv.allocation.alloc().relocations.push_back({ 0, Allocation::new_alloc(size) }); return rv; } + else if( link_name == "__rust_reallocate" ) + { + LOG_ASSERT(args.at(0).allocation, "__rust_reallocate first argument doesn't have an allocation"); + auto alloc_ptr = args.at(0).allocation.alloc().get_relocation(0); + auto ptr_ofs = args.at(0).read_usize(0); + LOG_ASSERT(ptr_ofs == 0, "__rust_reallocate with offset pointer"); + auto oldsize = args.at(1).read_usize(0); + auto newsize = args.at(2).read_usize(0); + auto align = args.at(3).read_usize(0); + LOG_DEBUG("__rust_reallocate(ptr=" << alloc_ptr << ", oldsize=" << oldsize << ", newsize=" << newsize << ", align=" << align << ")"); + + LOG_ASSERT(alloc_ptr, "__rust_reallocate with no backing allocation attached to pointer"); + LOG_ASSERT(alloc_ptr.is_alloc(), "__rust_reallocate with no backing allocation attached to pointer"); + auto& alloc = alloc_ptr.alloc(); + // TODO: Check old size and alignment against allocation. + alloc.data.resize(newsize); + // TODO: Should this instead make a new allocation to catch use-after-free? + return ::std::move(args.at(0)); + } else { LOG_TODO("Call external function " << link_name); } } -Value MIRI_Invoke_Intrinsic(const ::std::string& name, const ::HIR::PathParams& ty_params, ::std::vector<Value> args) +Value MIRI_Invoke_Intrinsic(const ModuleTree& modtree, const ::std::string& name, const ::HIR::PathParams& ty_params, ::std::vector<Value> args) { Value rv; TRACE_FUNCTION_R(name, rv); + for(const auto& a : args) + LOG_DEBUG("#" << (&a - args.data()) << ": " << a); if( name == "atomic_store" ) { auto& ptr_val = args.at(0); @@ -1234,6 +1432,75 @@ Value MIRI_Invoke_Intrinsic(const ::std::string& name, const ::HIR::PathParams& const auto& ty = ty_params.tys.at(0); alloc.alloc().write_value(ofs, ::std::move(data_val)); } + // ---------------------------------------------------------------- + // Checked arithmatic + else if( name == "add_with_overflow" ) + { + const auto& ty = ty_params.tys.at(0); + + auto lhs = PrimitiveValueVirt::from_value(ty, args.at(0)); + auto rhs = PrimitiveValueVirt::from_value(ty, args.at(1)); + bool didnt_overflow = lhs.get().add( rhs.get() ); + + // Get return type - a tuple of `(T, bool,)` + ::HIR::GenericPath gp; + gp.m_params.tys.push_back(ty); + gp.m_params.tys.push_back(::HIR::TypeRef { RawType::Bool }); + const auto& dty = modtree.get_composite(gp); + + rv = Value(::HIR::TypeRef(&dty)); + lhs.get().write_to_value(rv, dty.fields[0].first); + rv.write_u8( dty.fields[1].first, didnt_overflow ? 0 : 1 ); // Returns true if overflow happened + } + else if( name == "sub_with_overflow" ) + { + const auto& ty = ty_params.tys.at(0); + + auto lhs = PrimitiveValueVirt::from_value(ty, args.at(0)); + auto rhs = PrimitiveValueVirt::from_value(ty, args.at(1)); + bool didnt_overflow = lhs.get().subtract( rhs.get() ); + + // Get return type - a tuple of `(T, bool,)` + ::HIR::GenericPath gp; + gp.m_params.tys.push_back(ty); + gp.m_params.tys.push_back(::HIR::TypeRef { RawType::Bool }); + const auto& dty = modtree.get_composite(gp); + + rv = Value(::HIR::TypeRef(&dty)); + lhs.get().write_to_value(rv, dty.fields[0].first); + rv.write_u8( dty.fields[1].first, didnt_overflow ? 0 : 1 ); // Returns true if overflow happened + } + else if( name == "mul_with_overflow" ) + { + const auto& ty = ty_params.tys.at(0); + + auto lhs = PrimitiveValueVirt::from_value(ty, args.at(0)); + auto rhs = PrimitiveValueVirt::from_value(ty, args.at(1)); + bool didnt_overflow = lhs.get().multiply( rhs.get() ); + + // Get return type - a tuple of `(T, bool,)` + ::HIR::GenericPath gp; + gp.m_params.tys.push_back(ty); + gp.m_params.tys.push_back(::HIR::TypeRef { RawType::Bool }); + const auto& dty = modtree.get_composite(gp); + + rv = Value(::HIR::TypeRef(&dty)); + lhs.get().write_to_value(rv, dty.fields[0].first); + rv.write_u8( dty.fields[1].first, didnt_overflow ? 0 : 1 ); // Returns true if overflow happened + } + // Overflowing artithmatic + else if( name == "overflowing_sub" ) + { + const auto& ty = ty_params.tys.at(0); + + auto lhs = PrimitiveValueVirt::from_value(ty, args.at(0)); + auto rhs = PrimitiveValueVirt::from_value(ty, args.at(1)); + lhs.get().subtract( rhs.get() ); + + rv = Value(ty); + lhs.get().write_to_value(rv, 0); + } + // ---------------------------------------------------------------- else { LOG_TODO("Call intrinsic \"" << name << "\""); diff --git a/tools/standalone_miri/value.cpp b/tools/standalone_miri/value.cpp index 45eb1474..51bf6b94 100644 --- a/tools/standalone_miri/value.cpp +++ b/tools/standalone_miri/value.cpp @@ -119,6 +119,15 @@ AllocationPtr::~AllocationPtr() } +void Allocation::resize(size_t new_size) +{ + //size_t old_size = this->size(); + //size_t extra_bytes = (new_size > old_size ? new_size - old_size : 0); + + this->data.resize( (new_size + 8-1) / 8 ); + this->mask.resize( (new_size + 8-1) / 8 ); +} + void Allocation::check_bytes_valid(size_t ofs, size_t size) const { if( !(ofs + size <= this->size()) ) { @@ -452,7 +461,7 @@ void Value::mark_bytes_valid(size_t ofs, size_t size) Value Value::read_value(size_t ofs, size_t size) const { Value rv; - LOG_DEBUG("(" << ofs << ", " << size << ") - " << *this); + //TRACE_FUNCTION_R(ofs << ", " << size << ") - " << *this, rv); if( this->allocation ) { rv = this->allocation.alloc().read_value(ofs, size); @@ -465,7 +474,6 @@ Value Value::read_value(size_t ofs, size_t size) const rv.direct_data.mask[0] = this->direct_data.mask[0]; rv.direct_data.mask[1] = this->direct_data.mask[1]; } - LOG_DEBUG("RETURN " << rv); return rv; } void Value::read_bytes(size_t ofs, void* dst, size_t count) const @@ -571,6 +579,18 @@ void Value::write_usize(size_t ofs, uint64_t v) } return os; } +extern ::std::ostream& operator<<(::std::ostream& os, const ValueRef& v) +{ + if( v.m_alloc ) + { + os << v.m_alloc.alloc(); + } + else + { + os << *v.m_value; + } + return os; +} uint64_t ValueRef::read_usize(size_t ofs) const { diff --git a/tools/standalone_miri/value.hpp b/tools/standalone_miri/value.hpp index baa47e3f..03689215 100644 --- a/tools/standalone_miri/value.hpp +++ b/tools/standalone_miri/value.hpp @@ -119,6 +119,8 @@ public: return AllocationPtr(); } + void resize(size_t new_size); + void check_bytes_valid(size_t ofs, size_t size) const; void mark_bytes_valid(size_t ofs, size_t size); @@ -223,10 +225,15 @@ struct ValueRef ValueRef(AllocationPtr ptr, size_t ofs, size_t size): m_alloc(ptr), + m_value(nullptr), m_offset(ofs), m_size(size) { } + ValueRef(Value& val): + ValueRef(val, 0, val.size()) + { + } ValueRef(Value& val, size_t ofs, size_t size): m_value(&val), m_offset(ofs), @@ -273,3 +280,4 @@ struct ValueRef uint64_t read_usize(size_t ofs) const; int64_t read_isize(size_t ofs) const { return static_cast<int64_t>(read_usize(ofs)); } }; +extern ::std::ostream& operator<<(::std::ostream& os, const ValueRef& v); |