summaryrefslogtreecommitdiff
path: root/tools
diff options
context:
space:
mode:
Diffstat (limited to 'tools')
-rw-r--r--tools/common/debug.cpp4
-rw-r--r--tools/common/target_detect.h8
-rw-r--r--tools/common/toml.cpp3
-rw-r--r--tools/common/toml.h2
-rw-r--r--tools/dump_hirfile/Makefile56
-rw-r--r--tools/dump_hirfile/main.cpp327
-rw-r--r--tools/minicargo/Makefile2
-rw-r--r--tools/minicargo/build.cpp291
-rw-r--r--tools/minicargo/build.h10
-rw-r--r--tools/minicargo/cfg.cpp269
-rw-r--r--tools/minicargo/cfg.hpp9
-rw-r--r--tools/minicargo/main.cpp29
-rw-r--r--tools/minicargo/manifest.cpp340
-rw-r--r--tools/minicargo/manifest.h34
-rw-r--r--tools/minicargo/repository.cpp3
-rw-r--r--tools/minicargo/stringlist.h16
-rw-r--r--tools/standalone_miri/Makefile7
-rw-r--r--tools/standalone_miri/debug.cpp16
-rw-r--r--tools/standalone_miri/debug.hpp19
-rw-r--r--tools/standalone_miri/ffi.cpp141
-rw-r--r--tools/standalone_miri/hir_sim.cpp120
-rw-r--r--tools/standalone_miri/hir_sim.hpp120
-rw-r--r--tools/standalone_miri/lex.cpp4
-rw-r--r--tools/standalone_miri/linux.api50
-rw-r--r--tools/standalone_miri/main.cpp19
-rw-r--r--tools/standalone_miri/mir.cpp100
-rw-r--r--tools/standalone_miri/miri.cpp1547
-rw-r--r--tools/standalone_miri/miri.hpp19
-rw-r--r--tools/standalone_miri/module_tree.cpp110
-rw-r--r--tools/standalone_miri/module_tree.hpp34
-rw-r--r--tools/standalone_miri/u128.hpp170
-rw-r--r--tools/standalone_miri/value.cpp594
-rw-r--r--tools/standalone_miri/value.hpp299
-rw-r--r--tools/standalone_miri/win32.api24
-rw-r--r--tools/testrunner/main.cpp143
35 files changed, 3852 insertions, 1087 deletions
diff --git a/tools/common/debug.cpp b/tools/common/debug.cpp
index 94d8ed99..3884d651 100644
--- a/tools/common/debug.cpp
+++ b/tools/common/debug.cpp
@@ -41,6 +41,10 @@ void Debug_EnablePhase(const char* phase_name)
{
gmDisabledDebug.erase(it);
}
+ else
+ {
+ ::std::cerr << "Unknown debug phase: " << phase_name << ::std::endl;
+ }
}
void Debug_Print(::std::function<void(::std::ostream& os)> cb)
{
diff --git a/tools/common/target_detect.h b/tools/common/target_detect.h
index 1bfc7dd9..dda4bc31 100644
--- a/tools/common/target_detect.h
+++ b/tools/common/target_detect.h
@@ -10,9 +10,9 @@
// - Windows (MSVC)
#ifdef _MSC_VER
# if defined(_WIN64)
-# define DEFAULT_TARGET_NAME "x86_64-windows-msvc"
+# define DEFAULT_TARGET_NAME "x86_64-pc-windows-msvc"
# else
-# define DEFAULT_TARGET_NAME "x86-windows-msvc"
+# define DEFAULT_TARGET_NAME "x86-pc-windows-msvc"
# endif
// - Linux
#elif defined(__linux__)
@@ -32,9 +32,9 @@
// - MinGW
#elif defined(__MINGW32__)
# if defined(_WIN64)
-# define DEFAULT_TARGET_NAME "x86_64-windows-gnu"
+# define DEFAULT_TARGET_NAME "x86_64-pc-windows-gnu"
# else
-# define DEFAULT_TARGET_NAME "i586-windows-gnu"
+# define DEFAULT_TARGET_NAME "i586-pc-windows-gnu"
# endif
// - FreeBSD
#elif defined(__FreeBSD__)
diff --git a/tools/common/toml.cpp b/tools/common/toml.cpp
index 285d22a4..4e8e6da6 100644
--- a/tools/common/toml.cpp
+++ b/tools/common/toml.cpp
@@ -264,8 +264,7 @@ TomlKeyValue TomlFile::get_next_value()
}
if( m_current_composite.empty() )
{
- // TODO: Allow EOF?
- if(t.m_type != Token::Type::Newline)
+ if(t.m_type != Token::Type::Newline && t.m_type != Token::Type::Eof)
throw ::std::runtime_error(::format(m_lexer, ": Unexpected token in TOML file after entry - ", t));
}
else
diff --git a/tools/common/toml.h b/tools/common/toml.h
index 17e05142..5e031803 100644
--- a/tools/common/toml.h
+++ b/tools/common/toml.h
@@ -58,7 +58,7 @@ public:
// Obtain the next value in the file
TomlKeyValue get_next_value();
- const TomlLexer& lexer() const;
+ const TomlLexer& lexer() const { return m_lexer; }
};
struct TomlValue
diff --git a/tools/dump_hirfile/Makefile b/tools/dump_hirfile/Makefile
new file mode 100644
index 00000000..c0ae0513
--- /dev/null
+++ b/tools/dump_hirfile/Makefile
@@ -0,0 +1,56 @@
+#
+ifeq ($(OS),Windows_NT)
+ EXESUF ?= .exe
+endif
+EXESUF ?=
+
+V ?= @
+
+OBJDIR := .obj/
+
+BIN := ../bin/dump_hirfile$(EXESUF)
+OBJS := main.o
+OBJS += debug.o rc_string.o span.o ident.o
+OBJS += parse/parseerror.o # Why is this needed? ast/path.cpp uses it in binding
+OBJS += hir/hir.o hir/type.o hir/deserialise.o hir/serialise_lowlevel.o
+OBJS += hir/crate_ptr.o hir/generic_params.o hir/path.o hir/pattern.o hir/expr_ptr.o
+OBJS += hir/expr.o # Why is this needed?
+OBJS += parse/token.o parse/tokentree.o parse/tokenstream.o
+OBJS += ast/ast.o ast/expr.o ast/path.o ast/types.o ast/pattern.o
+OBJS += mir/mir.o mir/mir_ptr.o mir/dump.o hir/visitor.o
+OBJS += macro_rules/mod.o
+
+LINKFLAGS := -g -lpthread -lz
+CXXFLAGS := -Wall -std=c++14 -g -O2
+CXXFLAGS += -I ../common -I ../../src -I ../../src/include
+
+OBJS := $(OBJS:%=$(OBJDIR)%)
+
+.PHONY: all clean
+
+all: $(BIN)
+
+clean:
+ rm $(BIN) $(OBJS)
+
+$(BIN): $(OBJS) ../bin/common_lib.a
+ @mkdir -p $(dir $@)
+ @echo [CXX] -o $@
+ $V$(CXX) -o $@ $(OBJS) ../bin/common_lib.a $(LINKFLAGS)
+
+$(OBJDIR)%.o: %.cpp
+ @mkdir -p $(dir $@)
+ @echo [CXX] $<
+ $V$(CXX) -o $@ -c $< $(CXXFLAGS) -MMD -MP -MF $@.dep
+
+$(OBJDIR)%.o: ../../src/%.cpp
+ @mkdir -p $(dir $@)
+ @echo [CXX] $<
+ $V$(CXX) -o $@ -c $< $(CXXFLAGS) -MMD -MP -MF $@.dep
+
+../bin/common_lib.a: $(wildcard ../common/*.* ../common/Makefile)
+ make -C ../common
+
+-include $(OBJS:%.o=%.o.dep)
+
+
diff --git a/tools/dump_hirfile/main.cpp b/tools/dump_hirfile/main.cpp
new file mode 100644
index 00000000..47436950
--- /dev/null
+++ b/tools/dump_hirfile/main.cpp
@@ -0,0 +1,327 @@
+#include <hir/hir.hpp>
+#include <hir/item_path.hpp>
+#include <hir/main_bindings.hpp>
+#include <macro_rules/macro_rules.hpp>
+#include <mir/mir.hpp>
+#include <mir/operations.hpp> // MIR_Dump_Fcn
+
+int g_debug_indent_level = 0;
+
+struct Args
+{
+ Args(int argc, const char* const argv[]);
+
+ ::std::string infile;
+};
+
+struct Dumper
+{
+ struct Filters {
+ struct Types {
+ bool macros = true;
+ bool functions = true;
+ bool statics = true;
+ bool types = true;
+ bool traits = true;
+ } types;
+ bool public_only = false;
+ bool no_body = false;
+ } filters;
+
+ void dump_crate(const char* name, const ::HIR::Crate& crate) const;
+ void dump_module(::HIR::ItemPath ip, const ::HIR::Publicity& pub, const ::HIR::Module& mod) const;
+ void dump_function(::HIR::ItemPath ip, const ::HIR::Publicity& pub, const ::HIR::Function& fcn, int indent=0) const;
+ void dump_trait(::HIR::ItemPath ip, const ::HIR::Publicity& pub, const ::HIR::Trait& trait, int indent=0) const;
+};
+
+int main(int argc, const char* argv[])
+{
+ Args args(argc, argv);
+ Dumper dumper;
+
+ dumper.filters.types.functions = true;
+
+ auto hir = HIR_Deserialise(args.infile);
+ dumper.dump_crate("", *hir);
+}
+namespace {
+ template<typename T, typename Fcn>
+ void dump_impl_group(const ::HIR::Crate::ImplGroup<T>& ig, Fcn cb)
+ {
+ for(const auto& named_il : ig.named)
+ {
+ for(const auto& impl : named_il.second)
+ {
+ cb(*impl);
+ }
+ }
+ for(const auto& impl : ig.non_named)
+ {
+ cb(*impl);
+ }
+ for(const auto& impl : ig.generic)
+ {
+ cb(*impl);
+ }
+ }
+}
+void Dumper::dump_crate(const char* name, const ::HIR::Crate& crate) const
+{
+ // Dump macros
+ for(const auto& mac : crate.m_exported_macros)
+ {
+ ::std::cout << "macro_rules! " << mac.first << "{" << std::endl;
+ for(const auto& arm : mac.second->m_rules)
+ {
+ ::std::cout << " (";
+ for(const auto& pat : arm.m_pattern)
+ {
+ TU_MATCH_HDRA( (pat), {)
+ TU_ARMA(End, e)
+ ::std::cout << " EOS";
+ TU_ARMA(LoopStart, e)
+ ::std::cout << " (";
+ TU_ARMA(LoopNext, e)
+ ::std::cout << " ^";
+ TU_ARMA(LoopEnd, e)
+ ::std::cout << " )";
+ TU_ARMA(Jump, e)
+ ::std::cout << " <" << e.jump_target;
+ TU_ARMA(ExpectTok, e)
+ ::std::cout << " =" << e;
+ TU_ARMA(ExpectPat, e)
+ ::std::cout << " " << e.idx << "=" << e.type;
+ TU_ARMA(If, e) {
+ ::std::cout << " ?" << (e.is_equal ? "" : "!") << "{";
+ for(const auto& ent : e.ents) {
+ if(ent.ty == MacroPatEnt::PAT_TOKEN)
+ ::std::cout << " =" << ent.tok;
+ else
+ ::std::cout << " " << ent.ty;
+ }
+ ::std::cout << "}->" << e.jump_target;
+ }
+ }
+ }
+ ::std::cout << " ) => {\n";
+ // TODO: Macro expansion
+ ::std::cout << " }\n";
+ }
+ ::std::cout << "}\n";
+ ::std::cout << ::std::endl;
+ }
+
+ this->dump_module(::HIR::ItemPath(name), ::HIR::Publicity::new_global(), crate.m_root_module);
+
+ for(const auto& i : crate.m_trait_impls)
+ {
+ dump_impl_group(i.second, [&](const ::HIR::TraitImpl& ti) {
+ auto root_ip = ::HIR::ItemPath(ti.m_type, i.first, ti.m_trait_args);
+ ::std::cout << "impl" << ti.m_params.fmt_args() << " " << i.first << ti.m_trait_args << " for " << ti.m_type << "\n";
+ ::std::cout << " where" << ti.m_params.fmt_bounds() << "\n";
+ ::std::cout << "{" << ::std::endl;
+ if( this->filters.types.functions )
+ {
+ for(const auto& m : ti.m_methods)
+ {
+ this->dump_function(root_ip + m.first, ::HIR::Publicity::new_global(), m.second.data, 1);
+ }
+ }
+ ::std::cout << "}" << ::std::endl;
+ });
+ }
+
+ dump_impl_group(crate.m_type_impls, [&](const auto& i) {
+ auto root_ip = ::HIR::ItemPath(i.m_type);
+ ::std::cout << "impl" << i.m_params.fmt_args() << " " << i.m_type << "\n";
+ ::std::cout << " where" << i.m_params.fmt_bounds() << "\n";
+ ::std::cout << "{" << ::std::endl;
+ if( this->filters.types.functions )
+ {
+ for(const auto& m : i.m_methods)
+ {
+ this->dump_function(root_ip + m.first, ::HIR::Publicity::new_global(), m.second.data, 1);
+ }
+ }
+ ::std::cout << "}" << ::std::endl;
+ });
+}
+void Dumper::dump_module(::HIR::ItemPath ip, const ::HIR::Publicity& pub, const ::HIR::Module& mod) const
+{
+ if( filters.public_only && !pub.is_global() )
+ {
+ return ;
+ }
+ ::std::cout << "// mod " << ip << ::std::endl;
+ for(const auto& i : mod.m_mod_items)
+ {
+ auto sub_ip = ip + i.first;
+ //::std::cout << "// " << i.second->ent.tagstr() << " " << sub_ip << "\n";
+ TU_MATCH_HDRA( (i.second->ent), {)
+ TU_ARMA(Module, e) {
+ this->dump_module(sub_ip, i.second->publicity, e);
+ }
+ TU_ARMA(Import, e) {
+ //this->dump_mod_import(sub_ip, e);
+ }
+ TU_ARMA(TypeAlias, e) {
+ //this->dump_type_alias(sub_ip, e);
+ }
+ TU_ARMA(ExternType, e) {
+ //this->dump_ext_type(sub_ip, e);
+ }
+ TU_ARMA(Enum, e) {
+ //this->dump_enum(sub_ip, e);
+ }
+ TU_ARMA(Struct, e) {
+ //this->dump_enum(sub_ip, e);
+ }
+ TU_ARMA(Union, e) {
+ //this->dump_trait(sub_ip, e);
+ }
+ TU_ARMA(Trait, e) {
+ this->dump_trait(sub_ip, i.second->publicity, e);
+ }
+ }
+ }
+ for(const auto& i : mod.m_value_items)
+ {
+ auto sub_ip = ip + i.first;
+ //::std::cout << "// " << i.second->ent.tagstr() << " " << sub_ip << "\n";
+ TU_MATCH_HDRA( (i.second->ent), {)
+ TU_ARMA(Import, e) {
+ //this->dump_val_import(sub_ip, e);
+ }
+ TU_ARMA(Constant, e) {
+ //this->dump_constant(sub_ip, e);
+ }
+ TU_ARMA(Static, e) {
+ //this->dump_constant(sub_ip, e);
+ }
+ TU_ARMA(StructConstant, e) {
+ //this->dump_constant(sub_ip, e);
+ }
+ TU_ARMA(StructConstructor, e) {
+ //this->dump_constant(sub_ip, e);
+ }
+ TU_ARMA(Function, e) {
+ this->dump_function(sub_ip, i.second->publicity, e);
+ }
+ }
+ }
+}
+void Dumper::dump_function(::HIR::ItemPath ip, const ::HIR::Publicity& pub, const ::HIR::Function& fcn, int nindent/*=0*/) const
+{
+ auto indent = RepeatLitStr { " ", nindent };
+ if( !this->filters.types.functions ) {
+ return ;
+ }
+ if( filters.public_only && !pub.is_global() ) {
+ return ;
+ }
+ ::std::cout << indent << "fn " << ip << fcn.m_params.fmt_args() << "(";
+ ::std::cout << " )";
+ if( fcn.m_code.m_mir )
+ {
+ ::std::cout << "\n";
+ ::std::cout << indent << "{\n";
+ if( filters.no_body ) {
+ ::std::cout << indent << "...\n";
+ }
+ else {
+ MIR_Dump_Fcn(::std::cout, *fcn.m_code.m_mir, nindent+1);
+ }
+ ::std::cout << indent << "}\n";
+ ::std::cout << ::std::endl;
+ }
+ else
+ {
+ ::std::cout << ";" << ::std::endl;
+ }
+}
+void Dumper::dump_trait(::HIR::ItemPath ip, const ::HIR::Publicity& pub, const ::HIR::Trait& trait, int nindent/*=0*/) const
+{
+ auto indent = RepeatLitStr { " ", nindent };
+ if( !this->filters.types.functions ) {
+ return ;
+ }
+ if( !filters.public_only && !pub.is_global() ) {
+ return ;
+ }
+ ::std::cout << indent << "trait " << ip << trait.m_params.fmt_args() << "\n";
+ ::std::cout << indent << "{\n";
+ auto indent2 = RepeatLitStr { " ", nindent+1 };
+ // ...
+ ::std::cout << indent << "}\n";
+ ::std::cout << ::std::endl;
+}
+
+bool debug_enabled()
+{
+ return false;
+}
+::std::ostream& debug_output(int indent, const char* function)
+{
+ return ::std::cout << "- " << RepeatLitStr { " ", indent } << function << ": ";
+}
+
+Args::Args(int argc, const char* const argv[])
+{
+ this->infile = argv[1];
+}
+
+// TODO: This is copy-pasted from src/main.cpp, should live somewhere better
+::std::ostream& operator<<(::std::ostream& os, const FmtEscaped& x)
+{
+ os << ::std::hex;
+ for(auto s = x.s; *s != '\0'; s ++)
+ {
+ switch(*s)
+ {
+ case '\0': os << "\\0"; break;
+ case '\n': os << "\\n"; break;
+ case '\\': os << "\\\\"; break;
+ case '"': os << "\\\""; break;
+ default:
+ uint8_t v = *s;
+ if( v < 0x80 )
+ {
+ if( v < ' ' || v > 0x7F )
+ os << "\\u{" << ::std::hex << (unsigned int)v << "}";
+ else
+ os << v;
+ }
+ else if( v < 0xC0 )
+ ;
+ else if( v < 0xE0 )
+ {
+ uint32_t val = (uint32_t)(v & 0x1F) << 6;
+ v = (uint8_t)*++s; if( (v & 0xC0) != 0x80 ) { s--; continue ; } val |= (uint32_t)v << 6;
+ os << "\\u{" << ::std::hex << val << "}";
+ }
+ else if( v < 0xF0 )
+ {
+ uint32_t val = (uint32_t)(v & 0x0F) << 12;
+ v = (uint8_t)*++s; if( (v & 0xC0) != 0x80 ) { s--; continue ; } val |= (uint32_t)v << 12;
+ v = (uint8_t)*++s; if( (v & 0xC0) != 0x80 ) { s--; continue ; } val |= (uint32_t)v << 6;
+ os << "\\u{" << ::std::hex << val << "}";
+ }
+ else if( v < 0xF8 )
+ {
+ uint32_t val = (uint32_t)(v & 0x07) << 18;
+ v = (uint8_t)*++s; if( (v & 0xC0) != 0x80 ) { s--; continue ; } val |= (uint32_t)v << 18;
+ v = (uint8_t)*++s; if( (v & 0xC0) != 0x80 ) { s--; continue ; } val |= (uint32_t)v << 12;
+ v = (uint8_t)*++s; if( (v & 0xC0) != 0x80 ) { s--; continue ; } val |= (uint32_t)v << 6;
+ os << "\\u{" << ::std::hex << val << "}";
+ }
+ break;
+ }
+ }
+ os << ::std::dec;
+ return os;
+}
+
+MIR::EnumCachePtr::~EnumCachePtr()
+{
+ assert(!this->p);
+}
diff --git a/tools/minicargo/Makefile b/tools/minicargo/Makefile
index 363ef4b9..862f6b3c 100644
--- a/tools/minicargo/Makefile
+++ b/tools/minicargo/Makefile
@@ -13,7 +13,7 @@ V ?= @
OBJDIR := .obj/
BIN := ../bin/minicargo$(EXESUF)
-OBJS := main.o build.o manifest.o repository.o
+OBJS := main.o build.o manifest.o repository.o cfg.o
LINKFLAGS := -g -lpthread
CXXFLAGS := -Wall -std=c++14 -g -O2
diff --git a/tools/minicargo/build.cpp b/tools/minicargo/build.cpp
index 53fa1a16..efacb6f8 100644
--- a/tools/minicargo/build.cpp
+++ b/tools/minicargo/build.cpp
@@ -24,6 +24,7 @@ extern int _putenv_s(const char*, const char*);
#include <vector>
#include <algorithm>
#include <sstream> // stringstream
+#include <fstream> // ifstream
#include <cstdlib> // setenv
#ifndef DISABLE_MULTITHREAD
# include <thread>
@@ -52,8 +53,10 @@ extern int _putenv_s(const char*, const char*);
#ifdef _WIN32
# define EXESUF ".exe"
+# define DLLSUF ".dll"
#else
# define EXESUF ""
+# define DLLSUF ".so"
#endif
#include <target_detect.h> // tools/common/target_detect.h
#define HOST_TARGET DEFAULT_TARGET_NAME
@@ -61,17 +64,19 @@ extern int _putenv_s(const char*, const char*);
/// Class abstracting access to the compiler
class Builder
{
- BuildOptions m_opts;
+ const BuildOptions& m_opts;
::helpers::path m_compiler_path;
+ size_t m_total_targets;
+ mutable size_t m_targets_built;
#ifndef _WIN32
mutable ::std::mutex chdir_mutex;
#endif
public:
- Builder(BuildOptions opts);
+ Builder(const BuildOptions& opts, size_t total_targets);
- bool build_target(const PackageManifest& manifest, const PackageTarget& target, bool is_for_host) const;
- bool build_library(const PackageManifest& manifest, bool is_for_host) const;
+ bool build_target(const PackageManifest& manifest, const PackageTarget& target, bool is_for_host, size_t index) const;
+ bool build_library(const PackageManifest& manifest, bool is_for_host, size_t index) const;
::helpers::path build_build_script(const PackageManifest& manifest, bool is_for_host, bool* out_is_rebuilt) const;
private:
@@ -217,6 +222,18 @@ BuildList::BuildList(const PackageManifest& manifest, const BuildOptions& opts):
{
b.m_list.push_back({ &manifest, !cross_compiling, 0 });
}
+ if( opts.mode != BuildOptions::Mode::Normal)
+ {
+ for(const auto& dep : manifest.dev_dependencies())
+ {
+ if( dep.is_disabled() )
+ {
+ continue ;
+ }
+ DEBUG(manifest.name() << ": Dependency " << dep.name());
+ b.add_package(dep.get_package(), 1, !opts.build_script_overrides.is_valid(), !cross_compiling);
+ }
+ }
// TODO: Add the binaries too?
// - They need slightly different treatment.
@@ -260,7 +277,7 @@ BuildList::BuildList(const PackageManifest& manifest, const BuildOptions& opts):
bool BuildList::build(BuildOptions opts, unsigned num_jobs)
{
bool include_build = !opts.build_script_overrides.is_valid();
- Builder builder { ::std::move(opts) };
+ Builder builder { opts, m_list.size() };
// Pre-count how many dependencies are remaining for each package
struct BuildState
@@ -413,7 +430,7 @@ bool BuildList::build(BuildOptions opts, unsigned num_jobs)
}
DEBUG("Thread " << my_idx << ": Starting " << cur << " - " << list[cur].package->name());
- if( ! builder->build_library(*list[cur].package, list[cur].is_host) )
+ if( ! builder->build_library(*list[cur].package, list[cur].is_host, cur) )
{
queue.failure = true;
queue.signal_all();
@@ -474,7 +491,7 @@ bool BuildList::build(BuildOptions opts, unsigned num_jobs)
{
auto cur = state.get_next();
- if( ! builder.build_library(*m_list[cur].package, m_list[cur].is_host) )
+ if( ! builder.build_library(*m_list[cur].package, m_list[cur].is_host, cur) )
{
return false;
}
@@ -488,7 +505,7 @@ bool BuildList::build(BuildOptions opts, unsigned num_jobs)
{
auto cur = state.get_next();
- if( ! builder.build_library(*m_list[cur].package, m_list[cur].is_host) )
+ if( ! builder.build_library(*m_list[cur].package, m_list[cur].is_host, cur) )
{
return false;
}
@@ -532,14 +549,26 @@ bool BuildList::build(BuildOptions opts, unsigned num_jobs)
}
// Now that all libraries are done, build the binaries (if present)
- return this->m_root_manifest.foreach_binaries([&](const auto& bin_target) {
- return builder.build_target(this->m_root_manifest, bin_target, /*is_for_host=*/false);
- });
+ switch(opts.mode)
+ {
+ case BuildOptions::Mode::Normal:
+ return this->m_root_manifest.foreach_binaries([&](const auto& bin_target) {
+ return builder.build_target(this->m_root_manifest, bin_target, /*is_for_host=*/false, ~0u);
+ });
+ case BuildOptions::Mode::Test:
+ // TODO: What about unit tests?
+ return this->m_root_manifest.foreach_ty(PackageTarget::Type::Test, [&](const auto& test_target) {
+ return builder.build_target(this->m_root_manifest, test_target, /*is_for_host=*/true, ~0u);
+ });
+ }
+ throw "unreachable";
}
-Builder::Builder(BuildOptions opts):
- m_opts(::std::move(opts))
+Builder::Builder(const BuildOptions& opts, size_t total_targets):
+ m_opts(opts),
+ m_total_targets(total_targets),
+ m_targets_built(0)
{
if( const char* override_path = getenv("MRUSTC_PATH") ) {
m_compiler_path = override_path;
@@ -614,16 +643,44 @@ Builder::Builder(BuildOptions opts):
switch(target.m_type)
{
case PackageTarget::Type::Lib:
- if(crate_type) {
- *crate_type = target.m_is_proc_macro ? "proc-macro" : "rlib";
+ switch( target.m_crate_types.size() > 0
+ ? target.m_crate_types.front()
+ : (target.m_is_proc_macro
+ ? PackageTarget::CrateType::proc_macro
+ : PackageTarget::CrateType::rlib
+ )
+ )
+ {
+ case PackageTarget::CrateType::proc_macro:
+ if(crate_type) *crate_type = "proc-macro";
+ outfile /= ::format("lib", target.m_name, crate_suffix, "-plugin" EXESUF);
+ break;
+ case PackageTarget::CrateType::dylib:
+ if( getenv("MINICARGO_DYLIB") )
+ {
+ // TODO: Enable this once mrustc can set rpath or absolute paths
+ if(crate_type) *crate_type = "dylib";
+ outfile /= ::format("lib", target.m_name, crate_suffix, DLLSUF);
+ break;
+ }
+ case PackageTarget::CrateType::rlib:
+ if(crate_type) *crate_type = "rlib";
+ outfile /= ::format("lib", target.m_name, crate_suffix, ".rlib");
+ break;
+ default:
+ throw "";
}
- outfile /= ::format("lib", target.m_name, crate_suffix, ".hir");
break;
case PackageTarget::Type::Bin:
if(crate_type)
*crate_type = "bin";
outfile /= ::format(target.m_name, EXESUF);
break;
+ case PackageTarget::Type::Test:
+ if(crate_type)
+ *crate_type = "bin";
+ outfile /= ::format(target.m_name, EXESUF);
+ break;
default:
throw ::std::runtime_error("Unknown target type being built");
}
@@ -632,11 +689,117 @@ Builder::Builder(BuildOptions opts):
return outfile;
}
-bool Builder::build_target(const PackageManifest& manifest, const PackageTarget& target, bool is_for_host) const
+namespace {
+ ::std::map< ::std::string, ::std::vector<helpers::path> > load_depfile(const helpers::path& depfile_path)
+ {
+ ::std::map< ::std::string, ::std::vector<helpers::path> > rv;
+ ::std::ifstream ifp(depfile_path);
+ if( ifp.good() )
+ {
+ // Load space-separated (backslash-escaped) paths
+ struct Lexer {
+ ::std::ifstream ifp;
+ char m_c;
+
+ Lexer(::std::ifstream ifp)
+ :ifp(::std::move(ifp))
+ ,m_c(0)
+ {
+ nextc();
+ }
+
+ bool nextc() {
+ int v = ifp.get();
+ if( v == EOF ) {
+ m_c = '\0';
+ return false;
+ }
+ else {
+ m_c = (char)v;
+ return true;
+ }
+ }
+ ::std::string get_token() {
+ auto t = get_token_int();
+ //DEBUG("get_token '" << t << "'");
+ return t;
+ }
+ ::std::string get_token_int() {
+ if( ifp.eof() )
+ return "";
+ while( m_c == ' ' )
+ {
+ if( !nextc() )
+ return "";
+ }
+ if( m_c == '\n' ) {
+ nextc();
+ return "\n";
+ }
+ if( m_c == '\t' ) {
+ nextc();
+ return "\t";
+ }
+ ::std::string rv;
+ do {
+ if( m_c == '\\' )
+ {
+ nextc();
+ if( m_c == ' ' ) {
+ rv += m_c;
+ }
+ else if( m_c == ':' ) {
+ rv += m_c;
+ }
+ // HACK: Only spaces are escaped this way?
+ else {
+ rv += '\\';
+ rv += m_c;
+ }
+ }
+ else
+ {
+ rv += m_c;
+ }
+ } while( nextc() && m_c != ' ' && m_c != ':' && m_c != '\n' );
+ return rv;
+ }
+ } lexer(::std::move(ifp));
+
+ // Look for <string> ":" [<string>]* "\n"
+ do {
+ auto t = lexer.get_token();
+ if( t == "" )
+ break;
+ if( t == "\n" )
+ continue ;
+
+ auto v = rv.insert(::std::make_pair(t, ::std::vector<helpers::path>()));
+ auto& list = v.first->second;
+ auto target = t;
+ t = lexer.get_token();
+ assert(t == ":");
+
+ do {
+ t = lexer.get_token();
+ if( t == "\n" || t == "" )
+ break ;
+ list.push_back(t);
+ } while(1);
+ } while(1);
+ }
+ return rv;
+ }
+}
+
+bool Builder::build_target(const PackageManifest& manifest, const PackageTarget& target, bool is_for_host, size_t index) const
{
const char* crate_type;
::std::string crate_suffix;
auto outfile = this->get_crate_path(manifest, target, is_for_host, &crate_type, &crate_suffix);
+ auto depfile = outfile + ".d";
+
+ size_t this_target_idx = (index != ~0u ? m_targets_built++ : ~0u);
// TODO: Determine if it needs re-running
// Rerun if:
@@ -658,22 +821,58 @@ bool Builder::build_target(const PackageManifest& manifest, const PackageTarget&
DEBUG("Building " << outfile << " - Older than mrustc ( " << ts_result << " < " << Timestamp::for_file(m_compiler_path) << ")");
}
else {
- // TODO: Check dependencies. (from depfile)
- // Don't rebuild (no need to)
- DEBUG("Not building " << outfile << " - not out of date");
- return true;
+ // Check dependencies. (from depfile)
+ auto depfile_ents = load_depfile(depfile);
+ auto it = depfile_ents.find(outfile);
+ bool has_new_file = false;
+ if( it != depfile_ents.end() )
+ {
+ for(const auto& f : it->second)
+ {
+ auto dep_ts = Timestamp::for_file(f);
+ if( ts_result < dep_ts )
+ {
+ has_new_file = true;
+ DEBUG("Rebuilding " << outfile << ", older than " << f);
+ break;
+ }
+ }
+ }
+
+ if( !has_new_file )
+ {
+ // Don't rebuild (no need to)
+ DEBUG("Not building " << outfile << " - not out of date");
+ return true;
+ }
}
for(const auto& cmd : manifest.build_script_output().pre_build_commands)
{
// TODO: Run commands specified by build script (override)
+ TODO("Run command `" << cmd << "` from build script override");
}
- ::std::cout << "BUILDING " << target.m_name << " from " << manifest.name() << " v" << manifest.version() << " with features [" << manifest.active_features() << "]" << ::std::endl;
+ {
+ // TODO: Determine what number and total targets there are
+ if( index != ~0u ) {
+ //::std::cout << "(" << index << "/" << m_total_targets << ") ";
+ ::std::cout << "(" << this_target_idx << "/" << m_total_targets << ") ";
+ }
+ ::std::cout << "BUILDING ";
+ if(target.m_name != manifest.name())
+ ::std::cout << target.m_name << " from ";
+ ::std::cout << manifest.name() << " v" << manifest.version();
+ if( !manifest.active_features().empty() )
+ ::std::cout << " with features [" << manifest.active_features() << "]";
+ ::std::cout << ::std::endl;
+ }
StringList args;
args.push_back(::helpers::path(manifest.manifest_path()).parent() / ::helpers::path(target.m_path));
+ args.push_back("-o"); args.push_back(outfile);
args.push_back("--crate-name"); args.push_back(target.m_name.c_str());
args.push_back("--crate-type"); args.push_back(crate_type);
+ args.push_back("-C"); args.push_back(format("emit-depfile=",depfile));
if( !crate_suffix.empty() ) {
args.push_back("--crate-tag"); args.push_back(crate_suffix.c_str() + 1);
}
@@ -699,7 +898,11 @@ bool Builder::build_target(const PackageManifest& manifest, const PackageTarget&
args.push_back("-C"); args.push_back("codegen-type=monomir");
}
- args.push_back("-o"); args.push_back(outfile);
+ for(const auto& d : m_opts.lib_search_dirs)
+ {
+ args.push_back("-L");
+ args.push_back(d.str().c_str());
+ }
args.push_back("-L"); args.push_back(this->get_output_dir(is_for_host).str());
for(const auto& dir : manifest.build_script_output().rustc_link_search) {
args.push_back("-L"); args.push_back(dir.second.c_str());
@@ -725,6 +928,10 @@ bool Builder::build_target(const PackageManifest& manifest, const PackageTarget&
args.push_back("--extern");
args.push_back(::format(m.get_library().m_name, "=", path));
}
+ if( target.m_type == PackageTarget::Type::Test )
+ {
+ args.push_back("--test");
+ }
for(const auto& dep : manifest.dependencies())
{
if( ! dep.is_disabled() )
@@ -735,10 +942,18 @@ bool Builder::build_target(const PackageManifest& manifest, const PackageTarget&
args.push_back(::format(m.get_library().m_name, "=", path));
}
}
- for(const auto& d : m_opts.lib_search_dirs)
+ if( target.m_type == PackageTarget::Type::Test )
{
- args.push_back("-L");
- args.push_back(d.str().c_str());
+ for(const auto& dep : manifest.dev_dependencies())
+ {
+ if( ! dep.is_disabled() )
+ {
+ const auto& m = dep.get_package();
+ auto path = this->get_crate_path(m, m.get_library(), is_for_host, nullptr, nullptr);
+ args.push_back("--extern");
+ args.push_back(::format(m.get_library().m_name, "=", path));
+ }
+ }
}
// TODO: Environment variables (rustc_env)
@@ -867,13 +1082,13 @@ bool Builder::build_target(const PackageManifest& manifest, const PackageTarget&
StringListKV env;
env.push_back("CARGO_MANIFEST_DIR", manifest.directory().to_absolute());
//env.push_back("CARGO_MANIFEST_LINKS", manifest.m_links);
- //for(const auto& feat : manifest.m_active_features)
- //{
- // ::std::string fn = "CARGO_FEATURE_";
- // for(char c : feat)
- // fn += c == '-' ? '_' : tolower(c);
- // env.push_back(fn, manifest.m_links);
- //}
+ for(const auto& feat : manifest.active_features())
+ {
+ ::std::string fn = "CARGO_FEATURE_";
+ for(char c : feat)
+ fn += c == '-' ? '_' : toupper(c);
+ env.push_back(fn, "1");
+ }
//env.push_back("CARGO_CFG_RELEASE", "");
env.push_back("OUT_DIR", out_dir);
env.push_back("TARGET", m_opts.target_name ? m_opts.target_name : HOST_TARGET);
@@ -882,6 +1097,11 @@ bool Builder::build_target(const PackageManifest& manifest, const PackageTarget&
env.push_back("OPT_LEVEL", "2");
env.push_back("DEBUG", "0");
env.push_back("PROFILE", "release");
+ // TODO: All cfg(foo_bar) become CARGO_CFG_FOO_BAR
+ env.push_back("CARGO_CFG_TARGET_POINTER_WIDTH", "32");
+ // - Needed for `regex`'s build script, make mrustc pretend to be rustc
+ env.push_back("RUSTC", this->m_compiler_path);
+
for(const auto& dep : manifest.dependencies())
{
if( ! dep.is_disabled() )
@@ -922,7 +1142,7 @@ bool Builder::build_target(const PackageManifest& manifest, const PackageTarget&
return out_file;
}
-bool Builder::build_library(const PackageManifest& manifest, bool is_for_host) const
+bool Builder::build_library(const PackageManifest& manifest, bool is_for_host, size_t index) const
{
if( manifest.build_script() != "" )
{
@@ -948,7 +1168,7 @@ bool Builder::build_library(const PackageManifest& manifest, bool is_for_host) c
}
}
- return this->build_target(manifest, manifest.get_library(), is_for_host);
+ return this->build_target(manifest, manifest.get_library(), is_for_host, index);
}
bool Builder::spawn_process_mrustc(const StringList& args, StringListKV env, const ::helpers::path& logfile) const
{
@@ -986,6 +1206,7 @@ bool Builder::spawn_process(const char* exe_name, const StringList& args, const
#else
for(auto kv : env)
{
+ DEBUG("putenv " << kv.first << "=" << kv.second);
_putenv_s(kv.first, kv.second);
}
#endif
diff --git a/tools/minicargo/build.h b/tools/minicargo/build.h
index bba22964..83af88d6 100644
--- a/tools/minicargo/build.h
+++ b/tools/minicargo/build.h
@@ -20,7 +20,15 @@ struct BuildOptions
::helpers::path build_script_overrides;
::std::vector<::helpers::path> lib_search_dirs;
bool emit_mmir = false;
- const char* target_name = nullptr; // if null, host is used
+ const char* target_name = nullptr; // if null, host is used
+ enum class Mode {
+ /// Build the binary/library
+ Normal,
+ /// Build tests
+ Test,
+ /// Build examples
+ Examples,
+ } mode = Mode::Normal;
};
class BuildList
diff --git a/tools/minicargo/cfg.cpp b/tools/minicargo/cfg.cpp
new file mode 100644
index 00000000..85bff327
--- /dev/null
+++ b/tools/minicargo/cfg.cpp
@@ -0,0 +1,269 @@
+/*
+ * mrustc "minicargo" (minimal cargo clone)
+ * - By John Hodge (Mutabah)
+ *
+ * cfg.cpp
+ * - Handling of target configuration (in manifest nodes)
+ */
+#include <iostream> // cerr
+#include "debug.h"
+#include <cassert>
+#include <algorithm>
+#include <string>
+#include <cstring>
+#include "cfg.hpp"
+
+// TODO: Extract this from the target at runtime (by invoking the compiler on the passed target)
+#ifdef _WIN32
+//# define TARGET_NAME "i586-windows-msvc"
+# define CFG_UNIX false
+# define CFG_WINDOWS true
+#elif defined(__NetBSD__)
+//# define TARGET_NAME "x86_64-unknown-netbsd"
+# define CFG_UNIX true
+# define CFG_WINDOWS false
+#else
+//# define TARGET_NAME "x86_64-unknown-linux-gnu"
+# define CFG_UNIX true
+# define CFG_WINDOWS false
+#endif
+
+class CfgParseLexer
+{
+public:
+ class Tok
+ {
+ friend class CfgParseLexer;
+ public:
+ enum Type {
+ EndOfStream,
+ Operator,
+ Ident,
+ String,
+ };
+ private:
+ Type m_ty;
+ const char* s;
+ const char* e;
+ ::std::string m_val;
+ Tok():
+ m_ty(EndOfStream), s(nullptr),e(nullptr)
+ {
+ }
+ Tok(const char* s):
+ m_ty(Operator), s(s), e(s+1), m_val()
+ {
+ }
+ Tok(const char* s, const char* e):
+ m_ty(Ident), s(s), e(e), m_val()
+ {
+ }
+ Tok(const char* s, const char* e, ::std::string val):
+ m_ty(String), s(s), e(e), m_val(::std::move(val))
+ {
+ }
+ public:
+ bool operator==(char c) const {
+ return (m_ty == Operator && *s == c);
+ }
+ bool operator!=(char c) const { return !(*this == c); }
+ bool operator==(const char* v) const {
+ return strlen(v) == static_cast<unsigned>(e - s) && memcmp(s, v, e-s) == 0;
+ }
+ bool operator!=(const char* v) const { return !(*this == v); }
+
+ const Type ty() const { return m_ty; }
+ const ::std::string& str() const {
+ return m_val;
+ }
+ ::std::string to_string() const {
+ return ::std::string(s, e);
+ }
+ };
+private:
+ const char* m_pos;
+ Tok m_cur;
+
+public:
+ CfgParseLexer(const char* s):
+ m_pos(s),
+ m_cur(nullptr,nullptr)
+ {
+ consume();
+ }
+ const Tok& cur() const {
+ return m_cur;
+ }
+
+ Tok consume() {
+ auto rv = m_cur;
+ m_cur = get_next();
+ //::std::cout << "consume: " << rv.to_string() << " => " << m_cur.to_string() << ::std::endl;
+ return rv;
+ }
+ bool consume_if(char c) {
+ if( cur() == c ) {
+ consume();
+ return true;
+ }
+ else {
+ return false;
+ }
+ }
+ bool consume_if(const char* s) {
+ if( cur() == s ) {
+ consume();
+ return true;
+ }
+ else {
+ return false;
+ }
+ }
+private:
+ Tok get_next();
+};
+
+struct CfgChecker
+{
+ const char* target_env;
+ const char* target_os;
+ const char* target_arch;
+
+ bool check_cfg(CfgParseLexer& p) const;
+};
+
+CfgChecker gCfgChecker {
+ (CFG_WINDOWS ? "msvc" : "gnu"),
+ (CFG_WINDOWS ? "windows" : "linux"),
+ "x86"
+ };
+
+CfgParseLexer::Tok CfgParseLexer::get_next()
+{
+ while(*m_pos == ' ')
+ m_pos ++;
+ if(*m_pos == 0)
+ return Tok();
+ switch(*m_pos)
+ {
+ case '(': case ')':
+ case ',': case '=':
+ return Tok(m_pos++);
+ case '"': {
+ ::std::string str;
+ auto s = m_pos;
+ m_pos ++;
+ while( *m_pos != '"' )
+ {
+ if( *m_pos == '\\' )
+ {
+ TODO("Escape sequences in cfg parser");
+ }
+ str += *m_pos;
+ m_pos ++;
+ }
+ m_pos ++;
+ return Tok(s, m_pos, str); }
+ default:
+ if( isalnum(*m_pos) || *m_pos == '_' )
+ {
+ auto s = m_pos;
+ while(isalnum(*m_pos) || *m_pos == '_')
+ m_pos ++;
+ return Tok(s, m_pos);
+ }
+ else
+ {
+ throw ::std::runtime_error(format("Unexpected character in cfg() - ", *m_pos));
+ }
+ }
+}
+
+bool Cfg_Check(const char* cfg_string)
+{
+ CfgParseLexer p { cfg_string + 4 };
+
+ if( gCfgChecker.target_os == nullptr )
+ {
+ // TODO: If the checker isn't initialised, invoke the compiler and ask it to dump the current target
+ // - It's pre-initialised above currently
+ }
+
+ bool success = gCfgChecker.check_cfg(p);
+ if( !p.consume_if(")") )
+ throw ::std::runtime_error(format("Expected ')' after cfg condition - got", p.cur().to_string()));
+ return success;
+}
+
+bool CfgChecker::check_cfg(CfgParseLexer& p) const
+{
+ auto name = p.consume();
+ if( name.ty() != CfgParseLexer::Tok::Ident )
+ throw ::std::runtime_error("Expected an identifier");
+ // Combinators
+ if( p.consume_if('(') ) {
+ bool rv;
+ if( false ) {
+ }
+ else if( name == "not" ) {
+ rv = !check_cfg(p);
+ }
+ else if( name == "all" ) {
+ rv = true;
+ do
+ {
+ rv &= check_cfg(p);
+ } while(p.consume_if(','));
+ }
+ else if( name == "any" ) {
+ rv = false;
+ do
+ {
+ rv |= check_cfg(p);
+ } while(p.consume_if(','));
+ }
+ else {
+ TODO("Unknown fragment in cfg - " << name.to_string());
+ }
+ if( !p.consume_if(')') )
+ throw ::std::runtime_error("Expected ')' after combinator content");
+ return rv;
+ }
+ // Values
+ else if( p.consume_if('=') ) {
+ auto t = p.consume();
+ if( t.ty() != CfgParseLexer::Tok::String )
+ throw ::std::runtime_error("Expected a string after `=`");
+ const auto& val = t.str();
+
+ if( false ) {
+ }
+ else if( name == "target_env" )
+ return val == this->target_env;
+ else if( name == "target_os" )
+ return val == this->target_os;
+ else if( name == "target_arch" )
+ return val == this->target_arch;
+ else {
+ TODO("Unknown fragment in cfg - " << name.to_string());
+ }
+ }
+ // Flags
+ else {
+ if( false ) {
+ }
+ else if( name == "unix" ) {
+ return CFG_UNIX;
+ }
+ else if( name == "windows" ) {
+ return CFG_WINDOWS;
+ }
+ else if( name == "stage0" ) {
+ return false;
+ }
+ else {
+ TODO("Unknown fragment in cfg - " << name.to_string());
+ }
+ }
+ throw ::std::runtime_error("Hit end of check_cfg");
+}
diff --git a/tools/minicargo/cfg.hpp b/tools/minicargo/cfg.hpp
new file mode 100644
index 00000000..907c8079
--- /dev/null
+++ b/tools/minicargo/cfg.hpp
@@ -0,0 +1,9 @@
+/*
+ * mrustc "minicargo" (minimal cargo clone)
+ * - By John Hodge (Mutabah)
+ *
+ * cfg.cpp
+ * - Handling of target configuration (in manifest nodes)
+ */
+#pragma once
+extern bool Cfg_Check(const char* cfg_string);
diff --git a/tools/minicargo/main.cpp b/tools/minicargo/main.cpp
index ec6b8b45..4e929653 100644
--- a/tools/minicargo/main.cpp
+++ b/tools/minicargo/main.cpp
@@ -44,6 +44,11 @@ struct ProgramOptions
// Pause for user input before quitting (useful for MSVC debugging)
bool pause_before_quit = false;
+ /// Build and run tests?
+ bool test = false;
+
+ ::std::vector<::std::string> features;
+
int parse(int argc, const char* argv[]);
void usage() const;
void help() const;
@@ -97,10 +102,11 @@ int main(int argc, const char* argv[])
Debug_SetPhase("Load Root");
auto dir = ::helpers::path(opts.directory ? opts.directory : ".");
auto m = PackageManifest::load_from_toml( dir / "Cargo.toml" );
+ m.set_features(opts.features, opts.features.empty());
// 2. Load all dependencies
Debug_SetPhase("Load Dependencies");
- m.load_dependencies(repo, !bs_override_dir.is_valid());
+ m.load_dependencies(repo, !bs_override_dir.is_valid(), /*include_dev=*/opts.test);
// 3. Build dependency tree and build program.
BuildOptions build_opts;
@@ -111,6 +117,11 @@ int main(int argc, const char* argv[])
build_opts.target_name = opts.target;
for(const auto* d : opts.lib_search_dirs)
build_opts.lib_search_dirs.push_back( ::helpers::path(d) );
+ // Indicate desire to build tests (or examples) instead of the primary target
+ build_opts.mode =
+ opts.test ? BuildOptions::Mode::Test :
+ BuildOptions::Mode::Normal
+ ;
Debug_SetPhase("Enumerate Build");
auto build_list = BuildList(m, build_opts);
Debug_SetPhase("Run Build");
@@ -251,9 +262,25 @@ int ProgramOptions::parse(int argc, const char* argv[])
}
this->target = argv[++i];
}
+ else if( ::std::strcmp(arg, "--features") == 0 ) {
+ if(i+1 == argc) {
+ ::std::cerr << "Flag " << arg << " takes an argument" << ::std::endl;
+ return 1;
+ }
+ const auto* a = argv[++i];
+ while(const char* e = strchr(a, ','))
+ {
+ this->features.push_back( ::std::string(a, e) );
+ a = e + 1;
+ }
+ this->features.push_back( ::std::string(a) );
+ }
else if( ::std::strcmp(arg, "--pause") == 0 ) {
this->pause_before_quit = true;
}
+ else if( ::std::strcmp(arg, "--test") == 0 ) {
+ this->test = true;
+ }
else {
::std::cerr << "Unknown flag " << arg << ::std::endl;
return 1;
diff --git a/tools/minicargo/manifest.cpp b/tools/minicargo/manifest.cpp
index e47da1bc..ac9c9cb2 100644
--- a/tools/minicargo/manifest.cpp
+++ b/tools/minicargo/manifest.cpp
@@ -13,20 +13,15 @@
#include <algorithm>
#include <cctype> // toupper
#include "repository.h"
+#include "cfg.hpp"
// TODO: Extract this from the target at runtime (by invoking the compiler on the passed target)
#ifdef _WIN32
# define TARGET_NAME "i586-windows-msvc"
-# define CFG_UNIX false
-# define CFG_WINDOWS true
#elif defined(__NetBSD__)
# define TARGET_NAME "x86_64-unknown-netbsd"
-# define CFG_UNIX true
-# define CFG_WINDOWS false
#else
# define TARGET_NAME "x86_64-unknown-linux-gnu"
-# define CFG_UNIX true
-# define CFG_WINDOWS false
#endif
static ::std::vector<::std::shared_ptr<PackageManifest>> g_loaded_manifests;
@@ -135,6 +130,16 @@ PackageManifest PackageManifest::load_from_toml(const ::std::string& path)
}
rv.m_links = key_val.value.as_string();
}
+ else if( key == "autotests" )
+ {
+ // TODO: Fix the outer makefile so it doesn't need `foo-test`
+ // to be created.
+ //rv.m_create_auto_test = key_val.value.as_bool();
+ }
+ else if( key == "autobenches" )
+ {
+ //rv.m_create_auto_bench = key_val.value.as_bool();
+ }
else
{
// Unknown value in `package`
@@ -184,42 +189,28 @@ PackageManifest PackageManifest::load_from_toml(const ::std::string& path)
target_edit_from_kv(*it, key_val, 2);
}
- else if( section == "dependencies" )
+ else if( section == "dependencies" || section == "build-dependencies" || section == "dev-dependencies" )
{
+ ::std::vector<PackageRef>& dep_list =
+ section == "dependencies" ? rv.m_dependencies :
+ section == "build-dependencies" ? rv.m_build_dependencies :
+ /*section == "dev-dependencies" ? */ rv.m_dev_dependencies /*:
+ throw ""*/
+ ;
assert(key_val.path.size() > 1);
const auto& depname = key_val.path[1];
// Find/create dependency descriptor
- auto it = ::std::find_if(rv.m_dependencies.begin(), rv.m_dependencies.end(), [&](const auto& x) { return x.m_name == depname; });
- bool was_added = (it == rv.m_dependencies.end());
+ auto it = ::std::find_if(dep_list.begin(), dep_list.end(), [&](const auto& x) { return x.m_name == depname; });
+ bool was_added = (it == dep_list.end());
if( was_added )
{
- it = rv.m_dependencies.insert(it, PackageRef{ depname });
+ it = dep_list.insert(it, PackageRef{ depname });
}
it->fill_from_kv(was_added, key_val, 2);
}
- else if( section == "build-dependencies" )
- {
- assert(key_val.path.size() > 1);
-
- const auto& depname = key_val.path[1];
-
- // Find/create dependency descriptor
- auto it = ::std::find_if(rv.m_build_dependencies.begin(), rv.m_build_dependencies.end(), [&](const auto& x) { return x.m_name == depname; });
- bool was_added = (it == rv.m_build_dependencies.end());
- if(was_added)
- {
- it = rv.m_build_dependencies.insert(it, PackageRef{ depname });
- }
-
- it->fill_from_kv(was_added, key_val, 2);
- }
- else if( section == "dev-dependencies" )
- {
- // TODO: Developemnt (test/bench) deps
- }
else if( section == "patch" )
{
//const auto& repo = key_val.path[1];
@@ -241,169 +232,7 @@ PackageManifest PackageManifest::load_from_toml(const ::std::string& path)
// - It can be a target spec, or a cfg(foo) same as rustc
bool success;
if( cfg.substr(0, 4) == "cfg(" ) {
- class Parser
- {
- public:
- class Tok
- {
- friend class Parser;
- const char* s;
- const char* e;
- Tok(const char* s, const char* e):
- s(s), e(e)
- {
- }
- public:
- bool operator==(const char* v) const {
- return (strlen(v) == e - s) && memcmp(s, v, e-s) == 0;
- }
- bool operator!=(const char* v) const {
- return (strlen(v) != e - s) || memcmp(s, v, e-s) != 0;
- }
- ::std::string to_string() const {
- return ::std::string(s, e);
- }
- };
- private:
- const char* m_pos;
- Tok m_cur;
-
- public:
- Parser(const char* s):
- m_pos(s),
- m_cur(nullptr,nullptr)
- {
- consume();
- }
- const Tok& cur() const {
- return m_cur;
- }
-
- Tok consume() {
- auto rv = m_cur;
- m_cur = get_next();
- //::std::cout << "consume: " << rv.to_string() << " => " << m_cur.to_string() << ::std::endl;
- return rv;
- }
- bool consume_if(const char* s) {
- if( cur() == s ) {
- consume();
- return true;
- }
- else {
- return false;
- }
- }
- private:
- Tok get_next() {
- while(*m_pos == ' ')
- m_pos ++;
- if(*m_pos == 0)
- return Tok { m_pos, m_pos };
- switch(*m_pos)
- {
- case '(': case ')':
- case ',': case '=':
- return Tok { m_pos++, m_pos };
- case '"': {
- auto s = m_pos;
- m_pos ++;
- while( *m_pos != '"' )
- {
- if( *m_pos == '\\' )
- {
- TODO("Escape sequences in cfg parser");
- }
- m_pos ++;
- }
- m_pos ++;
- return Tok { s, m_pos }; }
- default:
- if( isalnum(*m_pos) || *m_pos == '_' )
- {
- auto s = m_pos;
- while(isalnum(*m_pos) || *m_pos == '_')
- m_pos ++;
- return Tok { s, m_pos };
- }
- else
- {
- throw ::std::runtime_error(format("Unexpected character in cfg() - ", *m_pos));
- }
- }
- }
- };
-
- struct H {
- static bool check_cfg(Parser& p)
- {
- if( p.consume_if("not") ) {
- if( !p.consume_if("(") )
- throw ::std::runtime_error("Expected '(' after `not`");
- auto rv = !check_cfg(p);
- if( !p.consume_if(")") )
- throw ::std::runtime_error("Expected ')' after `not` content");
- return rv;
- }
- else if( p.consume_if("all") ) {
- if( !p.consume_if("(") )
- throw ::std::runtime_error("Expected '(' after `all`");
- bool rv = true;
- do
- {
- rv &= check_cfg(p);
- } while(p.consume_if(","));
- if( !p.consume_if(")") )
- throw ::std::runtime_error("Expected ')' after `all` content");
- return rv;
- }
- // Strings
- else if( p.consume_if("target_os") ) {
- if( !p.consume_if("=") )
- throw ::std::runtime_error("Expected '=' after target_os");
- auto t = p.consume();
- if( t == "\"emscripten\"" ) {
- return false;
- }
- else if( t == "\"macos\"" ) {
- return false;
- }
- else {
- TODO("Handle target_os string - " << t.to_string());
- }
- }
- else if( p.consume_if("target_arch") ) {
- if( !p.consume_if("=") )
- throw ::std::runtime_error("Expected '=' after target");
- auto t = p.consume();
- if( t == "\"wasm32\"" ) {
- return false;
- }
- else{
- TODO("Handle target_arch string - " << t.to_string());
- }
- }
- // Flags
- else if( p.consume_if("unix") ) {
- return CFG_UNIX;
- }
- else if( p.consume_if("windows") ) {
- return CFG_WINDOWS;
- }
- else if( p.consume_if("stage0") ) {
- return false;
- }
- else {
- TODO("Unknown fragment in cfg - " << p.cur().to_string());
- throw ::std::runtime_error("");
- }
- }
- };
-
- Parser p { cfg.data() + 4 };
- success = H::check_cfg(p);
- if( !p.consume_if(")") )
- throw ::std::runtime_error(format("Expected ')' after cfg condition - got", p.cur().to_string()));
+ success = Cfg_Check(cfg.c_str());
}
else {
// It's a target name
@@ -412,25 +241,34 @@ PackageManifest PackageManifest::load_from_toml(const ::std::string& path)
// If so, parse as if the path was `real_section....`
if( success )
{
- if( real_section == "dependencies" )
+ if( real_section == "dependencies"
+ || real_section == "dev-dependencies"
+ || real_section == "build-dependencies"
+ )
{
+ ::std::vector<PackageRef>& dep_list =
+ real_section == "dependencies" ? rv.m_dependencies :
+ real_section == "build-dependencies" ? rv.m_build_dependencies :
+ /*real_section == "dev-dependencies" ? */ rv.m_dev_dependencies /*:
+ throw ""*/
+ ;
assert(key_val.path.size() > 3);
const auto& depname = key_val.path[3];
// Find/create dependency descriptor
- auto it = ::std::find_if(rv.m_dependencies.begin(), rv.m_dependencies.end(), [&](const auto& x) { return x.m_name == depname; });
- bool was_added = (it == rv.m_dependencies.end());
+ auto it = ::std::find_if(dep_list.begin(), dep_list.end(), [&](const auto& x) { return x.m_name == depname; });
+ bool was_added = (it == dep_list.end());
if( was_added )
{
- it = rv.m_dependencies.insert(it, PackageRef{ depname });
+ it = dep_list.insert(it, PackageRef{ depname });
}
it->fill_from_kv(was_added, key_val, 4);
}
else
{
- TODO("Unknown manifest section for target - " << real_section);
+ TODO(toml_file.lexer() << ": Unknown manifest section '" << real_section << "' in `target`");
}
}
}
@@ -514,10 +352,21 @@ PackageManifest PackageManifest::load_from_toml(const ::std::string& path)
tgt.m_path = "src/main.rs";
}
else {
- // TODO: What about src/bin/foo/main.rs?
+ // TODO: Error if both exist
+ // TODO: More complex search rules
tgt.m_path = ::helpers::path("src") / "bin" / tgt.m_name.c_str() + ".rs";
+ if( !::std::ifstream(package_dir / tgt.m_path).good() )
+ tgt.m_path = ::helpers::path("src") / "bin" / tgt.m_name.c_str() / "main.rs";
+ //if( !::std::ifstream(package_dir / tgt.m_path).good() )
+ // throw ::std::runtime_error(format("Unable to find source file for ", tgt.m_name, " - ", package_dir / tgt.m_path));
}
break;
+ case PackageTarget::Type::Test:
+ case PackageTarget::Type::Bench:
+ // no defaults
+ break;
+ case PackageTarget::Type::Example:
+ TODO("Default/implicit path for examples");
}
}
if(tgt.m_name == "")
@@ -528,6 +377,19 @@ PackageManifest PackageManifest::load_from_toml(const ::std::string& path)
}
}
+ // If there's a lib target, add a test target using the same path
+ {
+ auto it = ::std::find_if(rv.m_targets.begin(), rv.m_targets.end(), [&](const auto& t) { return t.m_type == PackageTarget::Type::Lib; });
+ if( it != rv.m_targets.end() )
+ {
+ auto path = it->m_path;
+ auto name = it->m_name + "-test";
+ rv.m_targets.push_back(PackageTarget { PackageTarget::Type::Test });
+ rv.m_targets.back().m_name = name;
+ rv.m_targets.back().m_path = path;
+ }
+ }
+
for(const auto& dep : rv.m_dependencies)
{
if( dep.m_optional )
@@ -542,7 +404,7 @@ PackageManifest PackageManifest::load_from_toml(const ::std::string& path)
// Explicitly disabled `[package] build = false`
rv.m_build_script = "";
}
- else if( rv.m_build_script != "" )
+ else if( rv.m_build_script == "" )
{
// Not set, check for a "build.rs" file
if( ::std::ifstream( package_dir / "build.rs").good() )
@@ -611,7 +473,26 @@ namespace
}
else if( key == "crate-type" )
{
- // TODO: Support crate types
+ //assert_kv_size(kv, base_idx + 1);
+ //assert_type(kv, base_idx + 1);
+ assert(kv.path.size() == base_idx + 1);
+ if( !target.m_crate_types.empty() ) {
+ // TODO: Error, multiple instances
+ }
+ for(const auto& sv : kv.value.m_sub_values)
+ {
+ const auto& s = sv.as_string();
+ if(s == "rlib") {
+ target.m_crate_types.push_back(PackageTarget::CrateType::rlib);
+ }
+ else if(s == "dylib") {
+ target.m_crate_types.push_back(PackageTarget::CrateType::dylib);
+ }
+ // TODO: Other crate types
+ else {
+ throw ::std::runtime_error(format("Unknown crate type - ", s));
+ }
+ }
}
else if( key == "required-features" )
{
@@ -798,12 +679,19 @@ void PackageManifest::set_features(const ::std::vector<::std::string>& features,
it2->m_optional_enabled = true;
}
}
+ {
+ auto it2 = ::std::find_if(m_dev_dependencies.begin(), m_dev_dependencies.end(), [&](const auto& x){ return x.m_name == featname; });
+ if(it2 != m_dev_dependencies.end())
+ {
+ it2->m_optional_enabled = true;
+ }
+ }
}
// Return true if any features were activated
//return start < m_active_features.size();
}
-void PackageManifest::load_dependencies(Repository& repo, bool include_build)
+void PackageManifest::load_dependencies(Repository& repo, bool include_build, bool include_dev)
{
TRACE_FUNCTION_F(m_name);
DEBUG("Loading depencencies for " << m_name);
@@ -819,16 +707,30 @@ void PackageManifest::load_dependencies(Repository& repo, bool include_build)
dep.load_manifest(repo, base_path, include_build);
}
- // TODO: Only enable if build script overrides aren't enabled.
+ // Load build deps if there's a build script AND build scripts are enabled
if( m_build_script != "" && include_build )
{
+ DEBUG("- Build dependencies");
for(auto& dep : m_build_dependencies)
{
if( dep.m_optional && !dep.m_optional_enabled )
{
continue ;
}
- dep.load_manifest(repo, base_path, true);
+ dep.load_manifest(repo, base_path, include_build);
+ }
+ }
+ // Load dev dependencies if the caller has indicated they should be
+ if( include_dev )
+ {
+ DEBUG("- Dev dependencies");
+ for(auto& dep : m_dev_dependencies)
+ {
+ if( dep.m_optional && !dep.m_optional_enabled )
+ {
+ continue ;
+ }
+ dep.load_manifest(repo, base_path, include_build);
}
}
}
@@ -1042,6 +944,10 @@ PackageVersionSpec PackageVersionSpec::from_string(const ::std::string& s)
// Default, compatible
pos ++;
break;
+ case '~':
+ ty = PackageVersionSpec::Bound::Type::MinorCompatible;
+ pos ++;
+ break;
case '=':
ty = PackageVersionSpec::Bound::Type::Equal;
pos ++;
@@ -1090,6 +996,17 @@ PackageVersionSpec PackageVersionSpec::from_string(const ::std::string& s)
{
pos ++;
v.patch = H::parse_i(s, pos);
+
+ if( pos < s.size() && s[pos] == '-' )
+ {
+ // Save tag (sequence of dot-seprated alpha-numeric identifiers)
+ auto tag_start = pos+1;
+ do {
+ // Could check the format, but meh.
+ pos ++;
+ } while(pos < s.size() && !isblank(s[pos]) && s[pos] != ',' );
+ //v.tag = ::std::string(s.c_str() + tag_start, s.c_str() + pos);
+ }
}
else
{
@@ -1098,6 +1015,9 @@ PackageVersionSpec PackageVersionSpec::from_string(const ::std::string& s)
}
else
{
+ // NOTE: This changes the behaviour of ~ rules to be bounded on the major version instead
+ if( ty == PackageVersionSpec::Bound::Type::MinorCompatible )
+ ty = PackageVersionSpec::Bound::Type::Compatible;
v.minor = 0;
v.patch = 0;
}
@@ -1110,7 +1030,7 @@ PackageVersionSpec PackageVersionSpec::from_string(const ::std::string& s)
break ;
} while(pos < s.size() && s[pos++] == ',');
if( pos != s.size() )
- throw ::std::runtime_error(::format( "Bad version string, pos=", pos ));
+ throw ::std::runtime_error(::format( "Bad version string '", s, "', pos=", pos ));
return rv;
}
bool PackageVersionSpec::accepts(const PackageVersion& v) const
@@ -1120,13 +1040,19 @@ bool PackageVersionSpec::accepts(const PackageVersion& v) const
switch(b.ty)
{
case Bound::Type::Compatible:
- // To be compatible, it has to be higher?
- // - TODO: Isn't a patch version compatible?
+ // ^ rules are >= specified, and < next major/breaking
if( !(v >= b.ver) )
return false;
if( !(v < b.ver.next_breaking()) )
return false;
break;
+ case Bound::Type::MinorCompatible:
+ // ~ rules are >= specified, and < next minor
+ if( !(v >= b.ver) )
+ return false;
+ if( !(v < b.ver.next_minor()) )
+ return false;
+ break;
case Bound::Type::GreaterEqual:
if( !(v >= b.ver) )
return false;
diff --git a/tools/minicargo/manifest.h b/tools/minicargo/manifest.h
index 4bb8b843..d0c537e5 100644
--- a/tools/minicargo/manifest.h
+++ b/tools/minicargo/manifest.h
@@ -26,6 +26,14 @@ struct PackageVersion
static PackageVersion from_string(const ::std::string& s);
+ PackageVersion next_minor() const {
+ if(major == 0) {
+ return PackageVersion { 0, minor, patch+1 };
+ }
+ else {
+ return PackageVersion { major, minor+1, 0 };
+ }
+ }
PackageVersion next_breaking() const {
if(major == 0) {
return PackageVersion { 0, minor + 1, 0 };
@@ -87,7 +95,8 @@ struct PackageVersionSpec
{
enum class Type
{
- Compatible,
+ Compatible, // "^" - Allows anything up to the next major version
+ MinorCompatible, // "~X.Y" - Allows anything up to the next minor version
Greater,
GreaterEqual,
Equal,
@@ -116,6 +125,7 @@ struct PackageVersionSpec
switch(b.ty)
{
case Bound::Type::Compatible: os << "^"; break;
+ case Bound::Type::MinorCompatible: os << "~"; break;
case Bound::Type::Greater: os << ">"; break;
case Bound::Type::GreaterEqual: os << ">="; break;
case Bound::Type::Equal: os << "="; break;
@@ -180,6 +190,14 @@ struct PackageTarget
Bench,
Example,
};
+ enum class CrateType
+ {
+ dylib,
+ rlib,
+ staticlib,
+ cdylib,
+ proc_macro,
+ };
Type m_type;
::std::string m_name;
@@ -192,6 +210,7 @@ struct PackageTarget
bool m_is_proc_macro = false;
bool m_is_own_harness = false;
+ ::std::vector<CrateType> m_crate_types;
::std::vector<::std::string> m_required_features;
PackageTarget(Type ty):
@@ -245,6 +264,7 @@ class PackageManifest
::std::vector<PackageRef> m_dependencies;
::std::vector<PackageRef> m_build_dependencies;
+ ::std::vector<PackageRef> m_dev_dependencies;
::std::vector<PackageTarget> m_targets;
@@ -263,15 +283,18 @@ public:
bool has_library() const;
const PackageTarget& get_library() const;
- bool foreach_binaries(::std::function<bool(const PackageTarget&)> cb) const {
+ bool foreach_ty(PackageTarget::Type ty, ::std::function<bool(const PackageTarget&)> cb) const {
for(const auto& t : m_targets ) {
- if( t.m_type == PackageTarget::Type::Bin ) {
+ if( t.m_type == ty ) {
if( !cb(t) )
return false;
}
}
return true;
}
+ bool foreach_binaries(::std::function<bool(const PackageTarget&)> cb) const {
+ return foreach_ty(PackageTarget::Type::Bin, cb);
+ }
const ::helpers::path directory() const {
return ::helpers::path(m_manifest_path).parent();
@@ -294,12 +317,15 @@ public:
const ::std::vector<PackageRef>& build_dependencies() const {
return m_build_dependencies;
}
+ const ::std::vector<PackageRef>& dev_dependencies() const {
+ return m_dev_dependencies;
+ }
const ::std::vector<::std::string>& active_features() const {
return m_active_features;
}
void set_features(const ::std::vector<::std::string>& features, bool enable_default);
- void load_dependencies(Repository& repo, bool include_build);
+ void load_dependencies(Repository& repo, bool include_build, bool include_dev=false);
void load_build_script(const ::std::string& path);
};
diff --git a/tools/minicargo/repository.cpp b/tools/minicargo/repository.cpp
index f5ff5ea8..060c5207 100644
--- a/tools/minicargo/repository.cpp
+++ b/tools/minicargo/repository.cpp
@@ -75,7 +75,7 @@ void Repository::load_vendored(const ::helpers::path& path)
}
}
- //DEBUG("Package '" << name << "' v" << ver);
+ DEBUG("Vendored package '" << name << "' v" << ver);
if(name == "")
continue ;
@@ -91,6 +91,7 @@ void Repository::load_vendored(const ::helpers::path& path)
} while( FindNextFile(find_handle, &find_data) );
FindClose(find_handle);
#endif
+ DEBUG("Loaded " << m_cache.size() << " vendored packages");
}
::std::shared_ptr<PackageManifest> Repository::from_path(::helpers::path in_path)
diff --git a/tools/minicargo/stringlist.h b/tools/minicargo/stringlist.h
index 4381121b..08b74c4b 100644
--- a/tools/minicargo/stringlist.h
+++ b/tools/minicargo/stringlist.h
@@ -78,7 +78,7 @@ public:
};
class StringListKV: private StringList
{
- ::std::vector<const char*> m_keys;
+ StringList m_keys;
public:
StringListKV()
{
@@ -99,6 +99,16 @@ public:
m_keys.push_back(k);
StringList::push_back(v);
}
+ void push_back(::std::string k, ::std::string v)
+ {
+ m_keys.push_back(k);
+ StringList::push_back(v);
+ }
+ void push_back(::std::string k, const char* v)
+ {
+ m_keys.push_back(k);
+ StringList::push_back(v);
+ }
struct Iter {
const StringListKV& v;
@@ -108,7 +118,7 @@ public:
this->i++;
}
::std::pair<const char*,const char*> operator*() {
- return ::std::make_pair(this->v.m_keys[this->i], this->v.get_vec()[this->i]);
+ return ::std::make_pair(this->v.m_keys.get_vec()[this->i], this->v.get_vec()[this->i]);
}
bool operator!=(const Iter& x) const {
return this->i != x.i;
@@ -118,7 +128,7 @@ public:
return Iter { *this, 0 };
}
Iter end() const {
- return Iter { *this, m_keys.size() };
+ return Iter { *this, m_keys.get_vec().size() };
}
friend ::std::ostream& operator<<(::std::ostream& os, const StringListKV& x) {
diff --git a/tools/standalone_miri/Makefile b/tools/standalone_miri/Makefile
index f4dc0d0d..0a8bd672 100644
--- a/tools/standalone_miri/Makefile
+++ b/tools/standalone_miri/Makefile
@@ -11,7 +11,7 @@ V ?= @
OBJDIR := .obj/
BIN := ../bin/standalone_miri$(EXESUF)
-OBJS := main.o debug.o mir.o lex.o value.o module_tree.o hir_sim.o miri.o
+OBJS := main.o debug.o mir.o lex.o value.o module_tree.o hir_sim.o miri.o rc_string.o
LINKFLAGS := -g -lpthread
CXXFLAGS := -Wall -std=c++14 -g -O2
@@ -37,6 +37,11 @@ $(OBJDIR)%.o: %.cpp
@echo [CXX] $<
$V$(CXX) -o $@ -c $< $(CXXFLAGS) -MMD -MP -MF $@.dep
+$(OBJDIR)%.o: ../../src/%.cpp
+ @mkdir -p $(dir $@)
+ @echo [CXX] $<
+ $V$(CXX) -o $@ -c $< $(CXXFLAGS) -MMD -MP -MF $@.dep
+
../bin/common_lib.a:
make -C ../common
diff --git a/tools/standalone_miri/debug.cpp b/tools/standalone_miri/debug.cpp
index c49df960..534790cd 100644
--- a/tools/standalone_miri/debug.cpp
+++ b/tools/standalone_miri/debug.cpp
@@ -11,14 +11,20 @@
unsigned DebugSink::s_indent = 0;
::std::unique_ptr<std::ofstream> DebugSink::s_out_file;
-DebugSink::DebugSink(::std::ostream& inner):
- m_inner(inner)
+DebugSink::DebugSink(::std::ostream& inner, bool stderr_too):
+ m_inner(inner),
+ m_stderr_too(stderr_too)
{
}
DebugSink::~DebugSink()
{
m_inner << "\n";
m_inner.flush();
+ m_inner.flags({});
+ if( m_stderr_too )
+ {
+ ::std::cerr << ::std::endl;
+ }
}
void DebugSink::set_output_file(const ::std::string& s)
{
@@ -30,6 +36,7 @@ bool DebugSink::enabled(const char* fcn_name)
}
DebugSink DebugSink::get(const char* fcn_name, const char* file, unsigned line, DebugLevel lvl)
{
+ bool stderr_too = false;
auto& sink = s_out_file ? *s_out_file : ::std::cout;
for(size_t i = s_indent; i--;)
sink << " ";
@@ -49,15 +56,18 @@ DebugSink DebugSink::get(const char* fcn_name, const char* file, unsigned line,
break;
case DebugLevel::Error:
sink << "ERROR: ";
+ stderr_too = true;
break;
case DebugLevel::Fatal:
sink << "FATAL: ";
+ stderr_too = true;
break;
case DebugLevel::Bug:
sink << "BUG: " << file << ":" << line << ": ";
+ stderr_too = true;
break;
}
- return DebugSink(sink);
+ return DebugSink(sink, stderr_too);
}
void DebugSink::inc_indent()
{
diff --git a/tools/standalone_miri/debug.hpp b/tools/standalone_miri/debug.hpp
index b3b0d76f..9de6231b 100644
--- a/tools/standalone_miri/debug.hpp
+++ b/tools/standalone_miri/debug.hpp
@@ -21,17 +21,26 @@ enum class DebugLevel {
Bug,
};
-class DebugSink
+class DebugSink//:
+ //public ::std::ostream
{
static unsigned s_indent;
static ::std::unique_ptr<std::ofstream> s_out_file;
::std::ostream& m_inner;
- DebugSink(::std::ostream& inner);
+ bool m_stderr_too;
+ DebugSink(::std::ostream& inner, bool stderr_too);
public:
~DebugSink();
template<typename T>
- ::std::ostream& operator<<(const T& v) { return m_inner << v; }
+ DebugSink& operator<<(const T& v) {
+ if( m_stderr_too )
+ {
+ ::std::cerr << v;
+ }
+ m_inner << v;
+ return *this;
+ }
static void set_output_file(const ::std::string& s);
static bool enabled(const char* fcn_name);
@@ -102,4 +111,6 @@ struct DebugExceptionError:
#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; throw DebugExceptionTodo{}; } while(0)
#define LOG_BUG(strm) do { DebugSink::get(__FUNCTION__,__FILE__,__LINE__,DebugLevel::Bug) << "BUG: " << strm; abort(); } while(0)
-#define LOG_ASSERT(cnd,strm) do { if( !(cnd) ) { LOG_BUG("Assertion failure: " #cnd " - " << strm); } } while(0)
+#define LOG_ASSERT(cnd,strm) do { if( !(cnd) ) { LOG_ERROR("Assertion failure: " #cnd " - " << strm); } } while(0)
+
+#define FMT_STRING(...) (dynamic_cast<::std::stringstream&>(::std::stringstream() << __VA_ARGS__).str())
diff --git a/tools/standalone_miri/ffi.cpp b/tools/standalone_miri/ffi.cpp
new file mode 100644
index 00000000..827d862f
--- /dev/null
+++ b/tools/standalone_miri/ffi.cpp
@@ -0,0 +1,141 @@
+/*
+ * mrustc Standalone MIRI
+ * - by John Hodge (Mutabah)
+ *
+ * ffi.cpp
+ * - FFI wrappers
+ */
+
+/// Argument reference (for checking validity)
+struct ArgRef
+{
+ uint8_t idx; // if 255, it's not referencing anything
+
+ static ArgRef null() { return ArgRef { 255 }; }
+};
+
+/// Representation of a FFI type (defined by the .api file)
+/// - These specify various flags used to tag pointers in the MIR
+struct FfiType
+{
+ // Pointer:
+ // - Mutability
+ // - Nullability
+ // - Array size (number of allocated elements)
+ // > Can be either a number, or an argument name
+ // - Valid size (number of initialised elements)
+ // - Allocation source
+ struct Pointer {
+ bool is_mut;
+ bool is_nullable;
+ ArgOrCount alloc_size;
+ ArgOrCount valid_size;
+ ArgRef alloc_source; // Can be "null"
+ };
+ ::std::vector<Pointer> pointers; // Reverse list (last entry is the outermost pointer)
+
+ // Datatypes:
+ // - `void`
+ // - size/alignment
+ // - u8,u16,...
+ // - function
+ // - Name
+ enum class Datatype {
+ Void,
+ Signed,
+ Unsigned,
+ Float,
+ Function,
+ } datatype;
+ union Meta {
+ struct {
+ size_t size;
+ size_t align;
+ ::std::string tag;
+ } void_data;
+ unsigned bits;
+ struct {
+ ArgRef name_source;
+ } function;
+ } meta;
+};
+
+
+struct FfiShim
+{
+ class ValueRef
+ {
+ public:
+ static ValueRef new_global(std::string name);
+ static ValueRef new_local(std::string name);
+ static ValueRef new_deref(ValueRef target);
+ };
+ class Expr
+ {
+ enum {
+ LITERAL,
+ VALUE,
+ CALL,
+ } ty;
+ union {
+ } data;
+ public:
+ static Expr new_lit(uint64_t v);
+ static Expr new_value(ValueRef vr);
+ static Expr new_call_int(::std::vector<::std::string> path, ::std::vector<Expr> args);
+ };
+ struct Stmt;
+ struct Block
+ {
+ ::std::vector<Stmt> statements;
+ Expr val;
+ };
+ class Stmt
+ {
+ enum {
+ DEFINE, // `let foo = bar;`
+ ASSIGN, // `foo ?= bar;`
+ IF,
+ } ty;
+ union {
+ struct {
+ ::std::string slot;
+ Expr value;
+ } define;
+ struct {
+ ValueRef slot;
+ Expr value;
+ } assign;
+ struct {
+ Expr cond;
+ Block true_arm;
+ Block false_arm;
+ } if_block;
+ };
+ };
+};
+
+struct FfiFunction
+{
+ ::std::vector<FfiType> arg_types;
+ FfiType ret_type;
+ ::std::vector<std::string> arg_names;
+
+ // Either directly defers to a function
+ ::std::string library;
+ ::std::string function;
+
+ // Or, it has code for more advanced checking
+ //FfiShimExpr code;
+
+ bool call(Value& rv, ::std::vector<Value> args) const;
+};
+
+bool FfiFunction::call(Value& rv, ::std::vector<Value> args) const
+{
+
+}
+
+bool call_ffi(Value& rv, const ::std::string& link_name, const ::std::string& abi, ::std::vector<Value> args)
+{
+}
diff --git a/tools/standalone_miri/hir_sim.cpp b/tools/standalone_miri/hir_sim.cpp
index 88739730..9d497054 100644
--- a/tools/standalone_miri/hir_sim.cpp
+++ b/tools/standalone_miri/hir_sim.cpp
@@ -42,18 +42,18 @@ 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 )
{
- if( this->composite_type->dst_meta == RawType::Unreachable )
+ if( this->composite_type().dst_meta == RawType::Unreachable )
{
return POINTER_SIZE;
}
// Special case: extern types (which appear when a type is only ever used by pointer)
- if( this->composite_type->dst_meta == RawType::Unit )
+ if( this->composite_type().dst_meta == RawType::Unit )
{
return POINTER_SIZE;
}
// TODO: Ideally, this inner type wouldn't be unsized itself... but checking that would be interesting.
- return POINTER_SIZE + this->composite_type->dst_meta.get_size();
+ return POINTER_SIZE + this->composite_type().dst_meta.get_size();
}
else if( this->inner_type == RawType::Str )
return POINTER_SIZE*2;
@@ -77,7 +77,8 @@ size_t HIR::TypeRef::get_size(size_t ofs) const
return 0;
case RawType::Composite:
// NOTE: Don't care if the type has metadata
- return this->composite_type->size;
+ LOG_ASSERT(this->composite_type().populated, "Getting size of non-defined type - " << *this);
+ return this->composite_type().size;
case RawType::Unreachable:
LOG_BUG("Attempting to get size of an unreachable type, " << *this);
case RawType::TraitObject:
@@ -111,6 +112,56 @@ size_t HIR::TypeRef::get_size(size_t ofs) const
throw "";
}
}
+size_t HIR::TypeRef::get_align(size_t ofs) const
+{
+ if( const auto* w = this->get_wrapper(ofs) )
+ {
+ LOG_TODO("get_align " << *this);
+ }
+ else
+ {
+ switch(this->inner_type)
+ {
+ case RawType::Unit:
+ return 1;
+ case RawType::Composite:
+ // NOTE: Don't care if the type has metadata
+ LOG_ASSERT(this->composite_type().populated, "Getting alignment of non-defined type - " << *this);
+ return this->composite_type().alignment;
+ case RawType::TraitObject:
+ case RawType::Str:
+ return 1;
+ case RawType::U8: case RawType::I8:
+ return 1;
+ case RawType::U16: case RawType::I16:
+ return 2;
+ case RawType::U32: case RawType::I32:
+ return 4;
+ case RawType::U64: case RawType::I64:
+ return 8;
+ 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;
+ case RawType::Unreachable:
+ LOG_BUG("Getting alignment of unreachable type");
+ }
+ throw "";
+ }
+}
bool HIR::TypeRef::has_slice_meta(size_t& out_inner_size) const
{
if( const auto* w = this->get_wrapper() )
@@ -168,7 +219,7 @@ bool HIR::TypeRef::has_pointer() const
if( this->inner_type == RawType::Composite )
{
// Still not sure, check the inner for any pointers.
- for(const auto& fld : this->composite_type->fields)
+ for(const auto& fld : this->composite_type().fields)
{
if( fld.second.has_pointer() )
return true;
@@ -196,13 +247,13 @@ const HIR::TypeRef* HIR::TypeRef::get_unsized_type(size_t& running_inner_size) c
switch(this->inner_type)
{
case RawType::Composite:
- if(!this->composite_type->variants.empty())
+ if(!this->composite_type().variants.empty())
return nullptr;
- if(this->composite_type->fields.empty())
+ if(this->composite_type().fields.empty())
return nullptr;
- running_inner_size = this->composite_type->fields.back().first;
+ running_inner_size = this->composite_type().fields.back().first;
size_t tmp;
- return this->composite_type->fields.back().second.get_unsized_type(tmp);
+ return this->composite_type().fields.back().second.get_unsized_type(tmp);
case RawType::TraitObject:
case RawType::Str:
return this;
@@ -229,11 +280,12 @@ HIR::TypeRef HIR::TypeRef::get_meta_type() const
switch(this->inner_type)
{
case RawType::Composite:
- if( this->composite_type->dst_meta == RawType::Unreachable )
+ if( this->composite_type().dst_meta == RawType::Unreachable )
return TypeRef(RawType::Unreachable);
- return this->composite_type->dst_meta;
+ return this->composite_type().dst_meta;
case RawType::TraitObject:
- return ::HIR::TypeRef(this->composite_type).wrap( TypeWrapper::Ty::Pointer, static_cast<size_t>(BorrowType::Shared) );
+ LOG_ASSERT(this->ptr.composite_type, "get_meta_type - " << *this);
+ return ::HIR::TypeRef(this->ptr.composite_type).wrap( TypeWrapper::Ty::Pointer, static_cast<size_t>(BorrowType::Shared) );
case RawType::Str:
return TypeRef(RawType::USize);
default:
@@ -249,7 +301,7 @@ HIR::TypeRef HIR::TypeRef::get_field(size_t idx, size_t& ofs) const
if( w->type == TypeWrapper::Ty::Slice )
{
// TODO
- throw "TODO";
+ LOG_TODO("Field on slice - " << *this << " #" << idx);
}
else if( w->type == TypeWrapper::Ty::Array )
{
@@ -260,21 +312,20 @@ HIR::TypeRef HIR::TypeRef::get_field(size_t idx, size_t& ofs) const
}
else
{
- throw "ERROR";
+ LOG_ERROR("Field on unknown wrapper type - " << *this << " #" << idx);
}
}
else
{
if( this->inner_type == RawType::Composite )
{
- LOG_ASSERT(idx < this->composite_type->fields.size(), "Field " << idx << " out of bounds in type " << *this);
- ofs = this->composite_type->fields.at(idx).first;
- return this->composite_type->fields.at(idx).second;
+ LOG_ASSERT(idx < this->composite_type().fields.size(), "Field " << idx << " out of bounds in type " << *this);
+ ofs = this->composite_type().fields.at(idx).first;
+ return this->composite_type().fields.at(idx).second;
}
else
{
- ::std::cerr << *this << " doesn't have fields" << ::std::endl;
- throw "ERROR";
+ LOG_ERROR(*this << " doesn't have fields");
}
}
}
@@ -282,14 +333,14 @@ size_t HIR::TypeRef::get_field_ofs(size_t base_idx, const ::std::vector<size_t>&
{
assert(this->wrappers.size() == 0);
assert(this->inner_type == RawType::Composite);
- size_t ofs = this->composite_type->fields.at(base_idx).first;
- const auto* ty_p = &this->composite_type->fields.at(base_idx).second;
+ size_t ofs = this->composite_type().fields.at(base_idx).first;
+ const auto* ty_p = &this->composite_type().fields.at(base_idx).second;
for(auto idx : other_idx)
{
assert(ty_p->wrappers.size() == 0);
assert(ty_p->inner_type == RawType::Composite);
- ofs += ty_p->composite_type->fields.at(idx).first;
- ty_p = &ty_p->composite_type->fields.at(idx).second;
+ ofs += ty_p->composite_type().fields.at(idx).first;
+ ty_p = &ty_p->composite_type().fields.at(idx).second;
}
ty = *ty_p;
return ofs;
@@ -346,19 +397,30 @@ namespace HIR {
os << "()";
break;
case RawType::Composite:
- os << x.composite_type->my_path;
+ os << x.composite_type().my_path;
//os << "composite_" << x.composite_type;
break;
case RawType::Unreachable:
os << "!";
break;
- case RawType::Function:
- os << "function_?";
- break;
+ case RawType::Function: {
+ assert( x.ptr.function_type );
+ const auto& ft = *x.ptr.function_type;
+ if( ft.unsafe )
+ os << "unsafe ";
+ if( ft.abi != "Rust" )
+ os << "extern \"" << ft.abi << "\" ";
+ os << "fn( ";
+ for(const auto& a : ft.args)
+ os << a << ", ";
+ os << ")";
+ if( ft.ret != RawType::Unit )
+ os << "-> " << ft.ret;
+ } break;
case RawType::TraitObject:
os << "dyn ";
- if( x.composite_type )
- os << x.composite_type->my_path;
+ if( x.ptr.composite_type )
+ os << x.composite_type().my_path;
else
os << "?";
break;
diff --git a/tools/standalone_miri/hir_sim.hpp b/tools/standalone_miri/hir_sim.hpp
index 62248fe9..81d3635a 100644
--- a/tools/standalone_miri/hir_sim.hpp
+++ b/tools/standalone_miri/hir_sim.hpp
@@ -9,20 +9,26 @@
#include <string>
#include <vector>
#include <memory>
+#include "../../src/include/rc_string.hpp"
const size_t POINTER_SIZE = 8;
+#define __ORD(fld) do { auto o = ::ord(this->fld, x.fld); if( o != OrdEqual ) return o; } while(0)
+#define __ORD_C(ty, fld) do { auto o = ::ord((ty)this->fld, (ty)x.fld); if( o != OrdEqual ) return o; } while(0)
#define __NE(fld) if(this->fld != x.fld) return true
#define __LT(fld) if(this->fld != x.fld) return this->fld < x.fld
+#if 0
enum Ordering
{
OrdLess,
OrdEqual,
OrdGreater,
};
+#endif
struct DataType;
+struct FunctionType;
enum class RawType
{
@@ -56,22 +62,22 @@ struct TypeWrapper
} type;
size_t size;
+ Ordering ord(const TypeWrapper& x) const {
+ __ORD_C(int, type);
+ __ORD(size);
+ return OrdEqual;
+ }
bool operator==(const TypeWrapper& x) const {
- return !(*this != x);
+ return this->ord(x) == OrdEqual;
}
bool operator!=(const TypeWrapper& x) const {
- __NE(type);
- __NE(size);
- return false;
+ return this->ord(x) != OrdEqual;
}
bool operator<(const TypeWrapper& x) const {
- __LT(type);
- __LT(size);
- return false;
+ return this->ord(x) == OrdLess;
}
};
-
namespace HIR {
enum class BorrowType
@@ -91,24 +97,35 @@ namespace HIR {
// 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;
+ union {
+ const DataType* composite_type;
+ const FunctionType* function_type;
+ } ptr;
TypeRef()
{
+ ptr.composite_type = nullptr;
}
explicit TypeRef(const DataType* dt):
- inner_type(RawType::Composite),
- composite_type(dt)
+ inner_type(RawType::Composite)
+ {
+ ptr.composite_type = dt;
+ }
+ explicit TypeRef(const FunctionType* fp):
+ inner_type(RawType::Function)
{
+ ptr.function_type = fp;
}
explicit TypeRef(RawType rt):
inner_type(rt)
{
+ ptr.composite_type = nullptr;
}
explicit TypeRef(CoreType ct):
inner_type(ct.raw_type)
{
+ ptr.composite_type = nullptr;
}
static TypeRef diverge() {
TypeRef rv;
@@ -122,6 +139,7 @@ namespace HIR {
}
size_t get_size(size_t ofs=0) const;
+ size_t get_align(size_t ofs=0) const;
// Returns true if this (unsized) type is a wrapper around a slice
// - Fills `out_inner_size` with the size of the slice element
@@ -158,6 +176,17 @@ namespace HIR {
// Get the offset and type of a field (recursing using `other_idx`)
size_t get_field_ofs(size_t idx, const ::std::vector<size_t>& other_idx, TypeRef& ty) const;
+ const DataType& composite_type() const {
+ assert(inner_type == RawType::Composite || inner_type == RawType::TraitObject);
+ assert(ptr.composite_type);
+ return *ptr.composite_type;
+ }
+ const FunctionType& function_type() const {
+ assert(inner_type == RawType::Function);
+ assert(ptr.function_type);
+ return *ptr.function_type;
+ }
+
bool operator==(const RawType& x) const {
if( this->wrappers.size() != 0 )
return false;
@@ -166,20 +195,20 @@ namespace HIR {
bool operator!=(const RawType& x) const {
return !(*this == x);
}
+ Ordering ord(const TypeRef& x) const {
+ __ORD(wrappers);
+ __ORD_C(int, inner_type);
+ __ORD_C(uintptr_t, ptr.composite_type); // pointer comparison only
+ return OrdEqual;
+ }
bool operator==(const TypeRef& x) const {
- return !(*this != x);
+ return this->ord(x) == OrdEqual;
}
bool operator!=(const TypeRef& x) const {
- __NE(wrappers);
- __NE(inner_type);
- __NE(composite_type);
- return false;
+ return this->ord(x) != OrdEqual;
}
bool operator<(const TypeRef& x) const {
- __LT(wrappers);
- __LT(inner_type);
- __LT(composite_type);
- return false;
+ return this->ord(x) == OrdLess;
}
friend ::std::ostream& operator<<(::std::ostream& os, const TypeRef& x);
@@ -189,18 +218,19 @@ namespace HIR {
{
::std::string crate_name;
::std::vector<::std::string> ents;
+ Ordering ord(const SimplePath& x) const {
+ __ORD(crate_name);
+ __ORD(ents);
+ return OrdEqual;
+ }
bool operator==(const SimplePath& x) const {
- return !(*this != x);
+ return this->ord(x) == OrdEqual;
}
bool operator!=(const SimplePath& x) const {
- __NE(crate_name);
- __NE(ents);
- return false;
+ return this->ord(x) != OrdEqual;
}
bool operator<(const SimplePath& x) const {
- __LT(crate_name);
- __LT(ents);
- return false;
+ return this->ord(x) == OrdLess;
}
friend ::std::ostream& operator<<(::std::ostream& os, const SimplePath& x);
};
@@ -221,18 +251,19 @@ namespace HIR {
m_simplepath(sp)
{
}
+ Ordering ord(const GenericPath& x) const {
+ __ORD(m_simplepath);
+ __ORD(m_params.tys);
+ return OrdEqual;
+ }
bool operator==(const GenericPath& x) const {
- return !(*this != x);
+ return this->ord(x) == OrdEqual;
}
bool operator!=(const GenericPath& x) const {
- __NE(m_simplepath);
- __NE(m_params.tys);
- return false;
+ return this->ord(x) != OrdEqual;
}
bool operator<(const GenericPath& x) const {
- __LT(m_simplepath);
- __LT(m_params.tys);
- return false;
+ return this->ord(x) == OrdLess;
}
friend ::std::ostream& operator<<(::std::ostream& os, const GenericPath& x);
@@ -263,22 +294,21 @@ namespace HIR {
{
}
+ Ordering ord(const Path& x) const {
+ __ORD(m_type);
+ __ORD(m_trait);
+ __ORD(m_name);
+ __ORD(m_params.tys);
+ return OrdEqual;
+ }
bool operator==(const Path& x) const {
- return !(*this != x);
+ return this->ord(x) == OrdEqual;
}
bool operator!=(const Path& x) const {
- __NE(m_type);
- __NE(m_trait);
- __NE(m_name);
- __NE(m_params.tys);
- return false;
+ return this->ord(x) != OrdEqual;
}
bool operator<(const Path& x) const {
- __LT(m_type);
- __LT(m_trait);
- __LT(m_name);
- __LT(m_params.tys);
- return false;
+ return this->ord(x) == OrdLess;
}
friend ::std::ostream& operator<<(::std::ostream& os, const Path& x);
diff --git a/tools/standalone_miri/lex.cpp b/tools/standalone_miri/lex.cpp
index 07427bde..79e94224 100644
--- a/tools/standalone_miri/lex.cpp
+++ b/tools/standalone_miri/lex.cpp
@@ -7,6 +7,8 @@
*/
#include "lex.hpp"
#include <cctype>
+#include <sstream>
+#include "debug.hpp"
#include <iostream>
bool Token::operator==(TokenClass tc) const
@@ -25,7 +27,7 @@ bool Token::operator==(const char* s) const
uint64_t Token::integer() const
{
if( this->type != TokenClass::Integer )
- throw "";
+ throw ::std::runtime_error(FMT_STRING("Expected interger, got " << *this));
return this->numbers.int_val;
}
double Token::real() const
diff --git a/tools/standalone_miri/linux.api b/tools/standalone_miri/linux.api
new file mode 100644
index 00000000..a4b8dd36
--- /dev/null
+++ b/tools/standalone_miri/linux.api
@@ -0,0 +1,50 @@
+#
+# Expression grammar:
+# `let <name> = <expr>;`
+# `<slot> <op>= <expr>;`
+# `<expr>`
+
+# `signal` - Just ignore it
+fn signal(..) -> *const [null] void [size(0)] {
+ 0
+}
+fn memchr(ptr: *const [count(n)] u8, c: u8, n: usize) -> *const [null,alloc(ptr)] u8 = "":"memchr";
+fn memrchr(ptr: *const [count(n)] u8, c: u8, n: usize) -> *const [null,alloc(ptr)] u8 = "":"memrchr";
+fn strlen(ptr: *const [cstr] u8) -> usize = "":"strlen";
+
+
+#fn write(fd: i32, count: isize, buf: *const void [size(count)]) -> i32 = "":"write";
+fn write(fd: i32, count: isize, buf: *const void) -> i32 {
+ miri::assert("invalid fd passed", fd > 0);
+ miri::ensure_valid_read("source buffer invalid", buf, 0, count);
+ miri::call_i32("", "write", fd, count, buf)
+}
+fn sysconf(name: i32) -> usize = "":"sysconf";
+
+
+# 64-bit linux pthread_attr_t
+type pthread_attr_t = void [size(56),align(8)];
+fn pthread_attr_init(*mut pthread_attr_t) -> i32 = "":"pthread_attr_init";
+fn pthread_attr_destroy(*mut pthread_attr_t) -> i32 = "":"pthread_attr_destroy";
+
+type pthread_key_t = u32;
+static PTHREAD_NEXT_KEY: u32 = 1;
+static PTHREAD_TLS: Map<u32,u64>;
+fn pthread_key_create(ptr: *mut pthread_key_t) -> i32 {
+ let key = PTHREAD_NEXT_KEY;
+ PTHREAD_NEXT_KEY += 1;
+ *ptr = key;
+ 0
+}
+fn pthread_key_delete(key: pthread_key_t) -> i32 {
+ let _ = Map::remove(key);
+ 0
+}
+fn pthread_setspecific(key: pthread_key_t, val: u64) -> i32 {
+ Map::set(PTHREAD_TLS, key, val);
+ 0
+}
+fn pthread_getspecific(key: pthread_key_t) -> u64 {
+ let rv_opt = Map::get(PTHREAD_TLS, key);
+ Option::unwrap_or(rv_opt, 0)
+}
diff --git a/tools/standalone_miri/main.cpp b/tools/standalone_miri/main.cpp
index deed08be..8ee118f7 100644
--- a/tools/standalone_miri/main.cpp
+++ b/tools/standalone_miri/main.cpp
@@ -51,6 +51,7 @@ int main(int argc, const char* argv[])
try
{
tree.load_file(opts.infile);
+ tree.validate();
}
catch(const DebugExceptionTodo& /*e*/)
{
@@ -71,26 +72,28 @@ int main(int argc, const char* argv[])
return 1;
}
+
// Create argc/argv based on input arguments
- auto argv_alloc = Allocation::new_alloc((1 + opts.args.size()) * POINTER_SIZE);
- argv_alloc->write_usize(0 * POINTER_SIZE, 0);
- argv_alloc->relocations.push_back({ 0 * POINTER_SIZE, RelocationPtr::new_ffi(FFIPointer { "", (void*)(opts.infile.c_str()), opts.infile.size() + 1 }) });
+ auto argv_alloc = Allocation::new_alloc((1 + opts.args.size()) * POINTER_SIZE, "argv");
+ argv_alloc->write_usize(0 * POINTER_SIZE, Allocation::PTR_BASE);
+ argv_alloc->relocations.push_back({ 0 * POINTER_SIZE, RelocationPtr::new_ffi(FFIPointer::new_const_bytes("argv0", opts.infile.c_str(), opts.infile.size() + 1)) });
for(size_t i = 0; i < opts.args.size(); i ++)
{
- argv_alloc->write_usize((1 + i) * POINTER_SIZE, 0);
- argv_alloc->relocations.push_back({ (1 + i) * POINTER_SIZE, RelocationPtr::new_ffi({ "", (void*)(opts.args[0]), ::std::strlen(opts.args[0]) + 1 }) });
+ argv_alloc->write_usize((1 + i) * POINTER_SIZE, Allocation::PTR_BASE);
+ argv_alloc->relocations.push_back({ (1 + i) * POINTER_SIZE, RelocationPtr::new_ffi(FFIPointer::new_const_bytes("argv", opts.args[i], ::std::strlen(opts.args[i]) + 1)) });
}
-
+ LOG_DEBUG("argv_alloc = " << *argv_alloc);
+
// Construct argc/argv values
auto val_argc = Value::new_isize(1 + opts.args.size());
auto argv_ty = ::HIR::TypeRef(RawType::I8).wrap(TypeWrapper::Ty::Pointer, 0 ).wrap(TypeWrapper::Ty::Pointer, 0);
- auto val_argv = Value::new_pointer(argv_ty, 0, RelocationPtr::new_alloc(argv_alloc));
+ auto val_argv = Value::new_pointer(argv_ty, Allocation::PTR_BASE, RelocationPtr::new_alloc(argv_alloc));
// Catch various exceptions from the interpreter
try
{
InterpreterThread root_thread(tree);
-
+
::std::vector<Value> args;
args.push_back(::std::move(val_argc));
args.push_back(::std::move(val_argv));
diff --git a/tools/standalone_miri/mir.cpp b/tools/standalone_miri/mir.cpp
index a0601823..bc456ca6 100644
--- a/tools/standalone_miri/mir.cpp
+++ b/tools/standalone_miri/mir.cpp
@@ -5,10 +5,13 @@
* mir/mir.cpp
* - MIR (Middle Intermediate Representation) definitions
*/
+#include "../../src/include/rc_string.hpp"
#include "../../src/mir/mir.hpp"
#include "hir_sim.hpp"
#include <iostream>
+#include <algorithm> // std::min
+#if 0
namespace std {
template <typename T>
inline ::std::ostream& operator<<(::std::ostream& os, const ::std::vector<T>& v) {
@@ -26,6 +29,7 @@ namespace std {
return os;
}
}
+#endif
namespace MIR {
::std::ostream& operator<<(::std::ostream& os, const Constant& v) {
@@ -62,44 +66,95 @@ namespace MIR {
os << "\"" << e << "\"";
),
(Const,
- os << e.p;
+ os << *e.p;
),
(ItemAddr,
- os << "&" << e;
+ os << "&" << *e;
)
)
return os;
}
- ::std::ostream& operator<<(::std::ostream& os, const LValue& x)
+ void LValue::RefCommon::fmt(::std::ostream& os) const
{
- TU_MATCHA( (x), (e),
+ TU_MATCHA( (m_lv->m_root), (e),
(Return,
- os << "Return";
+ os << "retval";
),
(Argument,
- os << "Argument(" << e.idx << ")";
+ os << "a" << e;
),
(Local,
- os << "Local(" << e << ")";
+ os << "_" << e;
),
(Static,
- os << "Static(" << e << ")";
- ),
- (Field,
- os << "Field(" << e.field_index << ", " << *e.val << ")";
- ),
- (Deref,
- os << "Deref(" << *e.val << ")";
- ),
- (Index,
- os << "Index(" << *e.val << ", " << *e.idx << ")";
- ),
- (Downcast,
- os << "Downcast(" << e.variant_index << ", " << *e.val << ")";
+ os << "(" << e << ")";
)
)
+ for(size_t i = 0; i < m_wrapper_count; i ++)
+ {
+ const LValue::Wrapper& w = m_lv->m_wrappers.at(i);
+ TU_MATCHA( (w), (e),
+ (Field,
+ os << "." << e;
+ ),
+ (Deref,
+ os << "*";
+ ),
+ (Index,
+ os << "[_" << e << "]";
+ ),
+ (Downcast,
+ os << "#" << e;
+ )
+ )
+ }
+ }
+
+ ::std::ostream& operator<<(::std::ostream& os, const LValue& x)
+ {
+ LValue::CRef(x).fmt(os);
return os;
}
+
+ Ordering LValue::Storage::ord(const LValue::Storage& x) const
+ {
+ if( x.is_Static() )
+ {
+ if( this->is_Static() )
+ return this->as_Static().ord( x.as_Static() );
+ else
+ return OrdLess;
+ }
+ else
+ {
+ if( this->is_Static() )
+ return OrdGreater;
+ }
+
+ return ::ord(this->val, x.val);
+ }
+ Ordering LValue::ord(const LValue& x) const
+ {
+ auto rv = m_root.ord(x.m_root);
+ if( rv != OrdEqual )
+ return rv;
+ return ::ord(m_wrappers, x.m_wrappers);
+ }
+ Ordering LValue::RefCommon::ord(const LValue::RefCommon& x) const
+ {
+ Ordering rv;
+ //TRACE_FUNCTION_FR(FMT_CB(ss, this->fmt(ss); ss << " ? "; x.fmt(ss);), rv);
+ rv = m_lv->m_root.ord(x.m_lv->m_root);
+ if( rv != OrdEqual )
+ return rv;
+ for(size_t i = 0; i < ::std::min(m_wrapper_count, x.m_wrapper_count); i ++)
+ {
+ rv = m_lv->m_wrappers[i].ord(x.m_lv->m_wrappers[i]);
+ if( rv != OrdEqual )
+ return rv;
+ }
+ return (rv = ::ord(m_wrapper_count, x.m_wrapper_count));
+ }
::std::ostream& operator<<(::std::ostream& os, const Param& x)
{
TU_MATCHA( (x), (e),
@@ -296,5 +351,10 @@ namespace MIR {
)
return os;
}
+
+ EnumCachePtr::~EnumCachePtr()
+ {
+ assert(!this->p);
+ }
}
diff --git a/tools/standalone_miri/miri.cpp b/tools/standalone_miri/miri.cpp
index fd4d45d8..4eadac66 100644
--- a/tools/standalone_miri/miri.cpp
+++ b/tools/standalone_miri/miri.cpp
@@ -5,18 +5,26 @@
* miri.cpp
* - Interpreter core
*/
+#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include "module_tree.hpp"
#include "value.hpp"
+#include "string_view.hpp"
#include <algorithm>
#include <iomanip>
#include "debug.hpp"
#include "miri.hpp"
+// VVV FFI
#include <cstring> // memrchr
+#include <sys/stat.h>
+#include <fcntl.h>
#ifdef _WIN32
# define NOMINMAX
# include <Windows.h>
+#else
+# include <unistd.h>
#endif
+#undef DEBUG
unsigned ThreadState::s_next_tls_key = 1;
@@ -25,6 +33,7 @@ class PrimitiveValue
public:
virtual ~PrimitiveValue() {}
+ virtual bool is_zero() const = 0;
virtual bool add(const PrimitiveValue& v) = 0;
virtual bool subtract(const PrimitiveValue& v) = 0;
virtual bool multiply(const PrimitiveValue& v) = 0;
@@ -50,6 +59,9 @@ struct PrimitiveUInt:
PrimitiveUInt(T v): v(v) {}
~PrimitiveUInt() override {}
+ virtual bool is_zero() const {
+ return this->v == 0;
+ }
bool add(const PrimitiveValue& x) override {
const auto* xp = &x.check<Self>("add");
T newv = this->v + xp->v;
@@ -100,6 +112,20 @@ struct PrimitiveU32: public PrimitiveUInt<uint32_t>
tgt.write_u32(ofs, this->v);
}
};
+struct PrimitiveU16: public PrimitiveUInt<uint16_t>
+{
+ PrimitiveU16(uint16_t v): PrimitiveUInt(v) {}
+ void write_to_value(ValueCommonWrite& tgt, size_t ofs) const override {
+ tgt.write_u16(ofs, this->v);
+ }
+};
+struct PrimitiveU8: public PrimitiveUInt<uint8_t>
+{
+ PrimitiveU8(uint8_t v): PrimitiveUInt(v) {}
+ void write_to_value(ValueCommonWrite& tgt, size_t ofs) const override {
+ tgt.write_u8(ofs, this->v);
+ }
+};
template<typename T>
struct PrimitiveSInt:
public PrimitiveValue
@@ -110,6 +136,9 @@ struct PrimitiveSInt:
PrimitiveSInt(T v): v(v) {}
~PrimitiveSInt() override {}
+ virtual bool is_zero() const {
+ return this->v == 0;
+ }
// TODO: Make this correct.
bool add(const PrimitiveValue& x) override {
const auto* xp = &x.check<Self>("add");
@@ -179,6 +208,12 @@ public:
LOG_ASSERT(t.get_wrapper() == nullptr, "PrimitiveValueVirt::from_value: " << t);
switch(t.inner_type)
{
+ case RawType::U8:
+ new(&rv.buf) PrimitiveU8(v.read_u8(0));
+ break;
+ case RawType::U16:
+ new(&rv.buf) PrimitiveU16(v.read_u16(0));
+ break;
case RawType::U32:
new(&rv.buf) PrimitiveU32(v.read_u32(0));
break;
@@ -254,142 +289,166 @@ struct MirHelpers
{
}
- ValueRef get_value_and_type(const ::MIR::LValue& lv, ::HIR::TypeRef& ty)
+ ValueRef get_value_and_type_root(const ::MIR::LValue::Storage& lv_root, ::HIR::TypeRef& ty)
{
- switch(lv.tag())
+ switch(lv_root.tag())
{
- case ::MIR::LValue::TAGDEAD: throw "";
+ case ::MIR::LValue::Storage::TAGDEAD: throw "";
// --> Slots
- TU_ARM(lv, Return, _e) {
- ty = this->frame.fcn.ret_ty;
+ TU_ARM(lv_root, Return, _e) {
+ ty = this->frame.fcn->ret_ty;
return ValueRef(this->frame.ret);
} break;
- TU_ARM(lv, Local, e) {
- ty = this->frame.fcn.m_mir.locals.at(e);
+ TU_ARM(lv_root, Local, e) {
+ ty = this->frame.fcn->m_mir.locals.at(e);
return ValueRef(this->frame.locals.at(e));
} break;
- TU_ARM(lv, Argument, e) {
- ty = this->frame.fcn.args.at(e.idx);
- return ValueRef(this->frame.args.at(e.idx));
+ TU_ARM(lv_root, Argument, e) {
+ ty = this->frame.fcn->args.at(e);
+ return ValueRef(this->frame.args.at(e));
} break;
- TU_ARM(lv, Static, e) {
+ TU_ARM(lv_root, Static, e) {
/*const*/ auto& s = this->thread.m_modtree.get_static(e);
ty = s.ty;
return ValueRef(s.val);
} break;
- // --> Modifiers
- TU_ARM(lv, Index, e) {
- auto idx = get_value_ref(*e.idx).read_usize(0);
- ::HIR::TypeRef array_ty;
- auto base_val = get_value_and_type(*e.val, array_ty);
- const auto* wrapper = array_ty.get_wrapper();
- if( !wrapper )
- {
- LOG_ERROR("Indexing non-array/slice - " << array_ty);
- }
- else if( wrapper->type == TypeWrapper::Ty::Array )
+ }
+ throw "";
+ }
+ ValueRef get_value_and_type(const ::MIR::LValue& lv, ::HIR::TypeRef& ty)
+ {
+ auto vr = get_value_and_type_root(lv.m_root, ty);
+ for(const auto& w : lv.m_wrappers)
+ {
+ switch(w.tag())
{
- ty = array_ty.get_inner();
- // Check index against array size
- if( idx >= wrapper->size ) {
- LOG_ERROR("Index out of bounds on array " << array_ty << ", idx=" << idx);
+ case ::MIR::LValue::Wrapper::TAGDEAD: throw "";
+ // --> Modifiers
+ TU_ARM(w, Index, idx_var) {
+ auto idx = this->frame.locals.at(idx_var).read_usize(0);
+ const auto* wrapper = ty.get_wrapper();
+ if( !wrapper )
+ {
+ LOG_ERROR("Indexing non-array/slice - " << ty);
throw "ERROR";
}
- base_val.m_offset += static_cast<size_t>(ty.get_size() * idx);
- return base_val;
- }
- else if( wrapper->type == TypeWrapper::Ty::Slice )
- {
- LOG_TODO("Slice index");
- }
- else
- {
- LOG_ERROR("Indexing non-array/slice - " << array_ty);
- throw "ERROR";
- }
- } break;
- TU_ARM(lv, Field, e) {
- ::HIR::TypeRef composite_ty;
- auto base_val = get_value_and_type(*e.val, composite_ty);
- // TODO: if there's metadata present in the base, but the inner doesn't have metadata, clear the metadata
- size_t inner_ofs;
- ty = composite_ty.get_field(e.field_index, inner_ofs);
- LOG_DEBUG("Field - " << composite_ty << "#" << e.field_index << " = @" << inner_ofs << " " << ty);
- base_val.m_offset += inner_ofs;
- if( ty.get_meta_type() == HIR::TypeRef(RawType::Unreachable) )
- {
- LOG_ASSERT(base_val.m_size >= ty.get_size(), "Field didn't fit in the value - " << ty.get_size() << " required, but " << base_val.m_size << " avail");
- base_val.m_size = ty.get_size();
- }
- return base_val;
- }
- 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);
- base_val.m_offset += inner_ofs;
- return base_val;
- }
- TU_ARM(lv, Deref, e) {
- ::HIR::TypeRef ptr_ty;
- auto val = get_value_and_type(*e.val, ptr_ty);
- ty = ptr_ty.get_inner();
- LOG_DEBUG("val = " << val << ", (inner) ty=" << ty);
-
- LOG_ASSERT(val.m_size >= POINTER_SIZE, "Deref of a value that doesn't fit a pointer - " << ty);
- size_t ofs = static_cast<size_t>( val.read_usize(0) ); // TODO: Limits?
-
- // There MUST be a relocation at this point with a valid allocation.
- auto alloc = val.get_relocation(val.m_offset);
- LOG_TRACE("Deref " << alloc << " + " << ofs << " to give value of type " << ty);
- // NOTE: No alloc can happen when dereferencing a zero-sized pointer
- if( alloc.is_alloc() )
- {
- LOG_DEBUG("> " << lv << " alloc=" << alloc.alloc());
- }
- size_t size;
+ else if( wrapper->type == TypeWrapper::Ty::Array )
+ {
+ ty = ty.get_inner();
+ vr.m_offset += ty.get_size() * idx;
+ }
+ else if( wrapper->type == TypeWrapper::Ty::Slice )
+ {
+ ty = ty.get_inner();
+ LOG_ASSERT(vr.m_metadata, "No slice metadata");
+ auto len = vr.m_metadata->read_usize(0);
+ LOG_ASSERT(idx < len, "Slice index out of range");
+ vr.m_offset += ty.get_size() * idx;
+ vr.m_metadata.reset();
+ }
+ else
+ {
+ LOG_ERROR("Indexing non-array/slice - " << ty);
+ throw "ERROR";
+ }
+ } break;
+ TU_ARM(w, Field, fld_idx) {
+ // TODO: if there's metadata present in the base, but the inner doesn't have metadata, clear the metadata
+ size_t inner_ofs;
+ auto inner_ty = ty.get_field(fld_idx, inner_ofs);
+ LOG_DEBUG("Field - " << ty << "#" << fld_idx << " = @" << inner_ofs << " " << inner_ty);
+ vr.m_offset += inner_ofs;
+ if( inner_ty.get_meta_type() == HIR::TypeRef(RawType::Unreachable) )
+ {
+ LOG_ASSERT(vr.m_size >= inner_ty.get_size(), "Field didn't fit in the value - " << inner_ty.get_size() << " required, but " << vr.m_size << " available");
+ vr.m_size = inner_ty.get_size();
+ }
+ ty = ::std::move(inner_ty);
+ }
+ TU_ARM(w, Downcast, variant_index) {
+ auto composite_ty = ::std::move(ty);
+ LOG_DEBUG("Downcast - " << composite_ty);
- const auto meta_ty = ty.get_meta_type();
- ::std::shared_ptr<Value> meta_val;
- // If the type has metadata, store it.
- if( meta_ty != RawType::Unreachable )
- {
- auto meta_size = meta_ty.get_size();
- LOG_ASSERT(val.m_size == POINTER_SIZE + meta_size, "Deref of " << ty << ", but pointer isn't correct size");
- meta_val = ::std::make_shared<Value>( val.read_value(POINTER_SIZE, meta_size) );
+ size_t inner_ofs;
+ ty = composite_ty.get_field(variant_index, inner_ofs);
+ vr.m_offset += inner_ofs;
+ }
+ TU_ARM(w, Deref, _) {
+ auto ptr_ty = ::std::move(ty);
+ ty = ptr_ty.get_inner();
+ LOG_DEBUG("Deref - " << vr << " into " << ty);
+
+ LOG_ASSERT(vr.m_size >= POINTER_SIZE, "Deref pointer isn't large enough to be a pointer");
+ // TODO: Move the metadata machinery into `deref` (or at least the logic needed to get the value size)
+ //auto inner_val = vr.deref(0, ty);
+ size_t ofs = vr.read_usize(0);
+ LOG_ASSERT(ofs != 0, "Dereferencing NULL pointer");
+ auto alloc = vr.get_relocation(0);
+ if( alloc )
+ {
+ // TODO: It's valid to dereference (but not read) a non-null invalid pointer.
+ LOG_ASSERT(ofs >= Allocation::PTR_BASE, "Dereferencing invalid pointer - " << ofs << " into " << alloc);
+ ofs -= Allocation::PTR_BASE;
+ }
+ else
+ {
+ }
- size_t slice_inner_size;
- if( ty.has_slice_meta(slice_inner_size) ) {
- size = (ty.get_wrapper() == nullptr ? ty.get_size() : 0) + static_cast<size_t>(meta_val->read_usize(0)) * slice_inner_size;
+ // There MUST be a relocation at this point with a valid allocation.
+ LOG_TRACE("Interpret " << alloc << " + " << ofs << " as value of type " << ty);
+ // NOTE: No alloc can happen when dereferencing a zero-sized pointer
+ if( alloc.is_alloc() )
+ {
+ //LOG_DEBUG("Deref - lvr=" << ::MIR::LValue::CRef(lv, &w - &lv.m_wrappers.front()) << " alloc=" << alloc.alloc());
}
- //else if( ty == RawType::TraitObject) {
- // // NOTE: Getting the size from the allocation is semi-valid, as you can't sub-slice trait objects
- // size = alloc.get_size() - ofs;
- //}
- else {
- LOG_DEBUG("> Meta " << *meta_val << ", size = " << alloc.get_size() << " - " << ofs);
- size = alloc.get_size() - ofs;
+ else
+ {
+ LOG_ASSERT(ty.get_meta_type() != RawType::Unreachable || ty.get_size() >= 0, "Dereference (giving a non-ZST) with no allocation");
}
- }
- else
- {
- LOG_ASSERT(val.m_size == POINTER_SIZE, "Deref of a value that isn't a pointer-sized value (size=" << val.m_size << ") - " << val << ": " << ptr_ty);
- size = ty.get_size();
- if( !alloc ) {
- LOG_ERROR("Deref of a value with no relocation - " << val);
+ size_t size;
+
+ const auto meta_ty = ty.get_meta_type();
+ ::std::shared_ptr<Value> meta_val;
+ // If the type has metadata, store it.
+ if( meta_ty != RawType::Unreachable )
+ {
+ auto meta_size = meta_ty.get_size();
+ LOG_ASSERT(vr.m_size == POINTER_SIZE + meta_size, "Deref of " << ty << ", but pointer isn't correct size");
+ meta_val = ::std::make_shared<Value>( vr.read_value(POINTER_SIZE, meta_size) );
+
+ size_t slice_inner_size;
+ if( ty.has_slice_meta(slice_inner_size) ) {
+ // Slice metadata, add the base size (if it's a struct) to the variable size
+ // - `get_wrapper` will return non-null for `[T]`, special-case `str`
+ size = (ty != RawType::Str && ty.get_wrapper() == nullptr ? ty.get_size() : 0) + meta_val->read_usize(0) * slice_inner_size;
+ }
+ //else if( ty == RawType::TraitObject) {
+ // // NOTE: Getting the size from the allocation is semi-valid, as you can't sub-slice trait objects
+ // size = alloc.get_size() - ofs;
+ //}
+ else {
+ LOG_DEBUG("> Meta " << *meta_val << ", size = " << alloc.get_size() << " - " << ofs);
+ // TODO: if the inner type is a trait object, then check that it has an allocation.
+ size = alloc.get_size() - ofs;
+ }
+ }
+ else
+ {
+ LOG_DEBUG("sizeof(" << ty << ") = " << ty.get_size());
+ LOG_ASSERT(vr.m_size == POINTER_SIZE, "Deref of a value that isn't a pointer-sized value (size=" << vr << ") - " << vr << ": " << ptr_ty);
+ size = ty.get_size();
+ if( !alloc && size > 0 ) {
+ LOG_ERROR("Deref of a non-ZST pointer with no relocation - " << vr);
+ }
}
- }
- LOG_DEBUG("alloc=" << alloc << ", ofs=" << ofs << ", size=" << size);
- auto rv = ValueRef(::std::move(alloc), ofs, size);
- rv.m_metadata = ::std::move(meta_val);
- return rv;
- } break;
+ LOG_DEBUG("Deref - New VR: alloc=" << alloc << ", ofs=" << ofs << ", size=" << size);
+ vr = ValueRef(::std::move(alloc), ofs, size);
+ vr.m_metadata = ::std::move(meta_val);
+ } break;
+ }
}
- throw "";
+ return vr;
}
ValueRef get_value_ref(const ::MIR::LValue& lv)
{
@@ -422,11 +481,14 @@ struct MirHelpers
::HIR::TypeRef ty;
auto base_value = get_value_and_type(lv, ty);
- 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));
+ if( val.size() > 0 )
+ {
+ if(!base_value.m_value) {
+ 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));
+ }
}
}
@@ -447,7 +509,11 @@ struct MirHelpers
ty = ::HIR::TypeRef(ce.t);
Value val = Value(ty);
val.write_bytes(0, &ce.v, ::std::min(ty.get_size(), sizeof(ce.v))); // TODO: Endian
- // TODO: i128/u128 need the upper bytes cleared+valid
+ // i128/u128 need the upper bytes cleared+valid
+ if( ce.t.raw_type == RawType::U128 ) {
+ uint64_t zero = 0;
+ val.write_bytes(8, &zero, 8);
+ }
return val;
} break;
TU_ARM(c, Bool, ce) {
@@ -474,12 +540,17 @@ struct MirHelpers
LOG_BUG("Constant::Const in mmir");
} break;
TU_ARM(c, Bytes, ce) {
- LOG_TODO("Constant::Bytes");
+ ty = ::HIR::TypeRef(RawType::U8).wrap(TypeWrapper::Ty::Slice, 0).wrap(TypeWrapper::Ty::Borrow, 0);
+ Value val = Value(ty);
+ val.write_ptr(0, Allocation::PTR_BASE + 0, RelocationPtr::new_ffi(FFIPointer::new_const_bytes("Constant::Bytes", ce.data(), ce.size())));
+ val.write_usize(POINTER_SIZE, ce.size());
+ LOG_DEBUG(c << " = " << val);
+ return val;
} break;
TU_ARM(c, StaticString, ce) {
ty = ::HIR::TypeRef(RawType::Str).wrap(TypeWrapper::Ty::Borrow, 0);
Value val = Value(ty);
- val.write_ptr(0, 0, RelocationPtr::new_string(&ce));
+ val.write_ptr(0, Allocation::PTR_BASE + 0, RelocationPtr::new_string(&ce));
val.write_usize(POINTER_SIZE, ce.size());
LOG_DEBUG(c << " = " << val);
return val;
@@ -487,15 +558,16 @@ struct MirHelpers
// --> Accessor
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 =*/ this->thread.m_modtree.get_function_opt(ce) ) {
+ if( /*const auto* fn =*/ this->thread.m_modtree.get_function_opt(*ce) ) {
ty = ::HIR::TypeRef(RawType::Function);
- return Value::new_fnptr(ce);
+ return Value::new_fnptr(*ce);
}
- if( const auto* s = this->thread.m_modtree.get_static_opt(ce) ) {
+ if( const auto* s = this->thread.m_modtree.get_static_opt(*ce) ) {
ty = s->ty.wrapped(TypeWrapper::Ty::Borrow, 0);
- return Value::new_pointer(ty, 0, RelocationPtr::new_alloc(s->val.allocation));
+ LOG_ASSERT(s->val.m_inner.is_alloc, "Statics should already have an allocation assigned");
+ return Value::new_pointer(ty, Allocation::PTR_BASE + 0, RelocationPtr::new_alloc(s->val.m_inner.alloc.alloc));
}
- LOG_ERROR("Constant::ItemAddr - " << ce << " - not found");
+ LOG_ERROR("Constant::ItemAddr - " << *ce << " - not found");
} break;
}
throw "";
@@ -546,15 +618,15 @@ InterpreterThread::~InterpreterThread()
for(size_t i = 0; i < m_stack.size(); i++)
{
const auto& frame = m_stack[m_stack.size() - 1 - i];
- ::std::cout << "#" << i << ": ";
+ ::std::cout << "#" << i << ": F" << frame.frame_index << " ";
if( frame.cb )
{
::std::cout << "WRAPPER";
}
else
{
- ::std::cout << frame.fcn.my_path << " BB" << frame.bb_idx << "/";
- if( frame.stmt_idx == frame.fcn.m_mir.blocks.at(frame.bb_idx).statements.size() )
+ ::std::cout << frame.fcn->my_path << " BB" << frame.bb_idx << "/";
+ if( frame.stmt_idx == frame.fcn->m_mir.blocks.at(frame.bb_idx).statements.size() )
::std::cout << "TERM";
else
::std::cout << frame.stmt_idx;
@@ -576,10 +648,11 @@ bool InterpreterThread::step_one(Value& out_thread_result)
assert( !this->m_stack.empty() );
assert( !this->m_stack.back().cb );
auto& cur_frame = this->m_stack.back();
- TRACE_FUNCTION_R(cur_frame.fcn.my_path, "");
- const auto& bb = cur_frame.fcn.m_mir.blocks.at( cur_frame.bb_idx );
+ auto instr_idx = this->m_instruction_count++;
+ TRACE_FUNCTION_R("#" << instr_idx << " " << cur_frame.fcn->my_path << " BB" << cur_frame.bb_idx << "/" << cur_frame.stmt_idx, "#" << instr_idx);
+ const auto& bb = cur_frame.fcn->m_mir.blocks.at( cur_frame.bb_idx );
- const size_t MAX_STACK_DEPTH = 40;
+ const size_t MAX_STACK_DEPTH = 90;
if( this->m_stack.size() > MAX_STACK_DEPTH )
{
LOG_ERROR("Maximum stack depth of " << MAX_STACK_DEPTH << " exceeded");
@@ -590,7 +663,7 @@ bool InterpreterThread::step_one(Value& out_thread_result)
if( cur_frame.stmt_idx < bb.statements.size() )
{
const auto& stmt = bb.statements[cur_frame.stmt_idx];
- LOG_DEBUG("=== BB" << cur_frame.bb_idx << "/" << cur_frame.stmt_idx << ": " << stmt);
+ LOG_DEBUG("=== F" << cur_frame.frame_index << " BB" << cur_frame.bb_idx << "/" << cur_frame.stmt_idx << ": " << stmt);
switch(stmt.tag())
{
case ::MIR::Statement::TAGDEAD: throw "";
@@ -609,25 +682,24 @@ bool InterpreterThread::step_one(Value& out_thread_result)
::HIR::TypeRef src_ty;
ValueRef src_base_value = state.get_value_and_type(re.val, src_ty);
auto alloc = src_base_value.m_alloc;
+ // If the source doesn't yet have a relocation, give it a backing allocation so we can borrow
if( !alloc && src_base_value.m_value )
{
- if( !src_base_value.m_value->allocation )
- {
- src_base_value.m_value->create_allocation();
- }
- alloc = RelocationPtr::new_alloc( src_base_value.m_value->allocation );
+ LOG_DEBUG("Borrow - Creating allocation for " << src_base_value);
+ alloc = RelocationPtr::new_alloc( src_base_value.m_value->borrow("Borrow") );
}
if( alloc.is_alloc() )
- LOG_DEBUG("- alloc=" << alloc << " (" << alloc.alloc() << ")");
+ LOG_DEBUG("Borrow - alloc=" << alloc << " (" << alloc.alloc() << ")");
else
- LOG_DEBUG("- alloc=" << alloc);
+ LOG_DEBUG("Borrow - alloc=" << alloc);
size_t ofs = src_base_value.m_offset;
const auto meta = src_ty.get_meta_type();
auto dst_ty = src_ty.wrapped(TypeWrapper::Ty::Borrow, static_cast<size_t>(re.type));
+ LOG_DEBUG("Borrow - ofs=" << ofs << ", meta_ty=" << meta);
- // Create the pointer
+ // Create the pointer (can this just store into the target?)
new_val = Value(dst_ty);
- new_val.write_ptr(0, ofs, ::std::move(alloc));
+ new_val.write_ptr(0, Allocation::PTR_BASE + ofs, ::std::move(alloc));
// - Add metadata if required
if( meta != RawType::Unreachable )
{
@@ -687,13 +759,22 @@ bool InterpreterThread::step_one(Value& out_thread_result)
else if( const auto* src_w = src_ty.get_wrapper() )
{
if( src_w->type != TypeWrapper::Ty::Pointer && src_w->type != TypeWrapper::Ty::Borrow ) {
- LOG_ERROR("Attempting to cast to a non-pointer - " << src_ty);
+ LOG_ERROR("Attempting to cast from a non-pointer - " << src_ty);
}
// TODO: MUST be a thin pointer?
// TODO: MUST be an integer (usize only?)
- if( re.type != RawType::USize && re.type != RawType::ISize ) {
- LOG_ERROR("Casting from a pointer to non-usize - " << re.type << " to " << src_ty);
+ switch(re.type.wrappers.empty() ? re.type.inner_type : RawType::Unreachable)
+ {
+ case RawType::USize:
+ case RawType::ISize:
+ break;
+ case RawType::U64:
+ case RawType::I64:
+ // TODO: Only if 64-bit?
+ break;
+ default:
+ LOG_ERROR("Casting from a pointer to non-usize - " << src_ty << " to " << re.type);
throw "ERROR";
}
new_val = src_value.read_value(0, re.type.get_size());
@@ -726,8 +807,8 @@ bool InterpreterThread::step_one(Value& out_thread_result)
case RawType::Bool: throw "ERROR";
case RawType::F32: throw "BUG";
case RawType::F64: dst_val = static_cast<float>( src_value.read_f64(0) ); break;
- case RawType::USize: throw "TODO";// /*dst_val = src_value.read_usize();*/ break;
- case RawType::ISize: throw "TODO";// /*dst_val = src_value.read_isize();*/ break;
+ case RawType::USize: LOG_TODO("f32 from " << src_ty);// /*dst_val = src_value.read_usize();*/ break;
+ case RawType::ISize: LOG_TODO("f32 from " << src_ty);// /*dst_val = src_value.read_isize();*/ break;
case RawType::U8: dst_val = static_cast<float>( src_value.read_u8 (0) ); break;
case RawType::I8: dst_val = static_cast<float>( src_value.read_i8 (0) ); break;
case RawType::U16: dst_val = static_cast<float>( src_value.read_u16(0) ); break;
@@ -736,8 +817,8 @@ bool InterpreterThread::step_one(Value& out_thread_result)
case RawType::I32: dst_val = static_cast<float>( src_value.read_i32(0) ); break;
case RawType::U64: dst_val = static_cast<float>( src_value.read_u64(0) ); break;
case RawType::I64: dst_val = static_cast<float>( src_value.read_i64(0) ); break;
- case RawType::U128: throw "TODO";// /*dst_val = src_value.read_u128();*/ break;
- case RawType::I128: throw "TODO";// /*dst_val = src_value.read_i128();*/ break;
+ case RawType::U128: LOG_TODO("f32 from " << src_ty);// /*dst_val = src_value.read_u128();*/ break;
+ case RawType::I128: LOG_TODO("f32 from " << src_ty);// /*dst_val = src_value.read_i128();*/ break;
}
new_val.write_f32(0, dst_val);
} break;
@@ -774,7 +855,15 @@ bool InterpreterThread::step_one(Value& out_thread_result)
case RawType::Bool:
LOG_TODO("Cast to " << re.type);
case RawType::Char:
- LOG_TODO("Cast to " << re.type);
+ switch(src_ty.inner_type)
+ {
+ case RawType::Char: new_val.write_u32(0, src_value.read_u32(0) ); break;
+ case RawType::U8: new_val.write_u32(0, src_value.read_u8(0) ); break;
+ default:
+ LOG_ERROR("Cast from " << src_ty << " to char isn't valid");
+ break;
+ }
+ break;
case RawType::USize:
case RawType::U8:
case RawType::U16:
@@ -799,14 +888,30 @@ bool InterpreterThread::step_one(Value& out_thread_result)
LOG_ASSERT(re.type.inner_type == RawType::USize, "Function pointers can only be casted to usize, instead " << re.type);
new_val = src_value.read_value(0, re.type.get_size());
break;
- case RawType::Char:
- LOG_ASSERT(re.type.inner_type == RawType::U32, "Char can only be casted to u32, instead " << re.type);
- new_val = src_value.read_value(0, 4);
- break;
+ case RawType::Char: {
+ uint32_t v = src_value.read_u32(0);
+ switch(re.type.inner_type)
+ {
+ case RawType::U8:
+ if( v > 0xFF ) {
+ LOG_NOTICE("Casting to u8 from char above 255");
+ }
+ new_val.write_u8(0, v & 0xFF);
+ break;
+ case RawType::U32:
+ new_val = src_value.read_value(0, 4);
+ break;
+ case RawType::USize:
+ new_val.write_usize(0, v);
+ break;
+ default:
+ LOG_ERROR("Char can only be casted to u32/u8, instead " << re.type);
+ }
+ } break;
case RawType::Unit:
LOG_FATAL("Cast of unit");
case RawType::Composite: {
- const auto& dt = *src_ty.composite_type;
+ const auto& dt = src_ty.composite_type();
if( dt.variants.size() == 0 ) {
LOG_FATAL("Cast of composite - " << src_ty);
}
@@ -893,6 +998,13 @@ bool InterpreterThread::step_one(Value& out_thread_result)
if(0)
case RawType::I64:
dst_val = static_cast<uint64_t>( src_value.read_i64(0) );
+ if(0)
+ case RawType::U128:
+ dst_val = static_cast<uint64_t>( src_value.read_u128(0) );
+ if(0)
+ case RawType::I128:
+ LOG_TODO("Cast i128 to " << re.type);
+ //dst_val = static_cast<uint64_t>( src_value.read_i128(0) );
switch(re.type.inner_type)
{
@@ -930,13 +1042,20 @@ bool InterpreterThread::step_one(Value& out_thread_result)
throw "";
}
break;
- case RawType::U128: throw "TODO"; /*dst_val = src_value.read_u128();*/ break;
- case RawType::I128: throw "TODO"; /*dst_val = src_value.read_i128();*/ break;
}
} break;
case RawType::U128:
- case RawType::I128:
- LOG_TODO("Cast to " << re.type);
+ case RawType::I128: {
+ U128 dst_val;
+ switch(src_ty.inner_type)
+ {
+ case RawType::U8: dst_val = src_value.read_u8 (0); break;
+ case RawType::I8: dst_val = src_value.read_i8 (0); break;
+ default:
+ LOG_TODO("Cast " << src_ty << " to " << re.type);
+ }
+ new_val.write_u128(0, dst_val);
+ } break;
}
}
} break;
@@ -957,18 +1076,65 @@ bool InterpreterThread::step_one(Value& out_thread_result)
case ::MIR::eBinOp::LE: {
LOG_ASSERT(ty_l == ty_r, "BinOp type mismatch - " << ty_l << " != " << ty_r);
int res = 0;
- // TODO: Handle comparison of the relocations too
- //const auto& alloc_l = v_l.m_value ? v_l.m_value->allocation : v_l.m_alloc;
- //const auto& alloc_r = v_r.m_value ? v_r.m_value->allocation : v_r.m_alloc;
- auto reloc_l = /*alloc_l ? */v_l.get_relocation(v_l.m_offset)/* : RelocationPtr()*/;
- auto reloc_r = /*alloc_r ? */v_r.get_relocation(v_r.m_offset)/* : RelocationPtr()*/;
+ auto reloc_l = v_l.get_relocation(0);
+ auto reloc_r = v_r.get_relocation(0);
- if( reloc_l != reloc_r )
+
+ // TODO: Handle comparison of the relocations too
+ // - If both sides have a relocation:
+ // > EQ/NE always valid
+ // > others require the same relocation
+ // - If one side has a relocation:
+ // > EQ/NE only allow zero on the non-reloc side
+ // > others are invalid?
+ if( reloc_l && reloc_r )
{
- res = (reloc_l < reloc_r ? -1 : 1);
+ // Both have relocations, check if they're equal
+ if( reloc_l != reloc_r )
+ {
+ switch(re.op)
+ {
+ case ::MIR::eBinOp::EQ:
+ case ::MIR::eBinOp::NE:
+ res = 1;
+ break;
+ default:
+ LOG_FATAL("Unable to compare " << v_l << " and " << v_r << " - different relocations (" << reloc_l << " != " << reloc_r << ")");
+ }
+ // - Equality will always fail
+ // - Ordering is a bug
+ }
+ else
+ {
+ // Equal: Allow all comparisons
+ }
+ }
+ else if( reloc_l || reloc_r )
+ {
+ // Only one side
+ // - Ordering is a bug
+ // - Equalities are allowed, but only for `0`?
+ // > TODO: If the side with no reloation doesn't have value `0` then error?
+ switch(re.op)
+ {
+ case ::MIR::eBinOp::EQ:
+ case ::MIR::eBinOp::NE:
+ // - Allow success, as addresses can be masked down
+ break;
+ default:
+ if( reloc_l )
+ res = 1;
+ else// if( reloc_r )
+ res = -1;
+ //LOG_FATAL("Unable to order " << v_l << " and " << v_r << " - different relocations");
+ break;
+ }
+ }
+ else
+ {
+ // No relocations, no need to check more
}
- LOG_DEBUG("res=" << res << ", " << reloc_l << " ? " << reloc_r);
if( const auto* w = ty_l.get_wrapper() )
{
@@ -1010,6 +1176,10 @@ bool InterpreterThread::step_one(Value& out_thread_result)
case RawType::I8 : res = res != 0 ? res : Ops::do_compare(v_l.read_i8 (0), v_r.read_i8 (0)); break;
case RawType::USize: res = res != 0 ? res : Ops::do_compare(v_l.read_usize(0), v_r.read_usize(0)); break;
case RawType::ISize: res = res != 0 ? res : Ops::do_compare(v_l.read_isize(0), v_r.read_isize(0)); break;
+ case RawType::Char: res = res != 0 ? res : Ops::do_compare(v_l.read_u32(0), v_r.read_u32(0)); break;
+ case RawType::Bool: res = res != 0 ? res : Ops::do_compare(v_l.read_u8(0), v_r.read_u8(0)); break; // TODO: `read_bool` that checks for bool values?
+ case RawType::U128: res = res != 0 ? res : Ops::do_compare(v_l.read_u128(0), v_r.read_u128(0)); break;
+ case RawType::I128: res = res != 0 ? res : Ops::do_compare(v_l.read_i128(0), v_r.read_i128(0)); break;
default:
LOG_TODO("BinOp comparisons - " << se.src << " w/ " << ty_l);
}
@@ -1034,36 +1204,40 @@ bool InterpreterThread::step_one(Value& out_thread_result)
case ::MIR::eBinOp::BIT_SHR: {
LOG_ASSERT(ty_l.get_wrapper() == nullptr, "Bitwise operator on non-primitive - " << ty_l);
LOG_ASSERT(ty_r.get_wrapper() == nullptr, "Bitwise operator with non-primitive - " << ty_r);
- size_t max_bits = ty_r.get_size() * 8;
+ size_t max_bits = ty_l.get_size() * 8;
uint8_t shift;
- auto check_cast = [&](uint64_t v){ LOG_ASSERT(0 <= v && v <= static_cast<decltype(v)>(max_bits), "Shift out of range - " << v); return static_cast<uint8_t>(v); };
+ auto check_cast_u = [&](auto v){ LOG_ASSERT(0 <= v && v <= max_bits, "Shift out of range - " << v); return static_cast<uint8_t>(v); };
+ auto check_cast_s = [&](auto v){ LOG_ASSERT(v <= static_cast<int64_t>(max_bits), "Shift out of range - " << v); return static_cast<uint8_t>(v); };
switch(ty_r.inner_type)
{
- case RawType::U64: shift = check_cast(v_r.read_u64(0)); break;
- case RawType::U32: shift = check_cast(v_r.read_u32(0)); break;
- case RawType::U16: shift = check_cast(v_r.read_u16(0)); break;
- case RawType::U8 : shift = check_cast(v_r.read_u8 (0)); break;
- case RawType::I64: shift = check_cast(v_r.read_i64(0)); break;
- case RawType::I32: shift = check_cast(v_r.read_i32(0)); break;
- case RawType::I16: shift = check_cast(v_r.read_i16(0)); break;
- case RawType::I8 : shift = check_cast(v_r.read_i8 (0)); break;
- case RawType::USize: shift = check_cast(v_r.read_usize(0)); break;
- case RawType::ISize: shift = check_cast(v_r.read_isize(0)); break;
+ case RawType::U64: shift = check_cast_u(v_r.read_u64(0)); break;
+ case RawType::U32: shift = check_cast_u(v_r.read_u32(0)); break;
+ case RawType::U16: shift = check_cast_u(v_r.read_u16(0)); break;
+ case RawType::U8 : shift = check_cast_u(v_r.read_u8 (0)); break;
+ case RawType::I64: shift = check_cast_s(v_r.read_i64(0)); break;
+ case RawType::I32: shift = check_cast_s(v_r.read_i32(0)); break;
+ case RawType::I16: shift = check_cast_s(v_r.read_i16(0)); break;
+ case RawType::I8 : shift = check_cast_s(v_r.read_i8 (0)); break;
+ case RawType::USize: shift = check_cast_u(v_r.read_usize(0)); break;
+ case RawType::ISize: shift = check_cast_s(v_r.read_isize(0)); break;
default:
- LOG_TODO("BinOp shift rhs unknown type - " << se.src << " w/ " << ty_r);
+ LOG_TODO("BinOp shift RHS unknown type - " << se.src << " w/ " << ty_r);
}
new_val = Value(ty_l);
switch(ty_l.inner_type)
{
// TODO: U128
+ case RawType::U128: new_val.write_u128(0, Ops::do_bitwise(v_l.read_u128(0), U128(shift), re.op)); break;
case RawType::U64: new_val.write_u64(0, Ops::do_bitwise(v_l.read_u64(0), static_cast<uint64_t>(shift), re.op)); break;
case RawType::U32: new_val.write_u32(0, Ops::do_bitwise(v_l.read_u32(0), static_cast<uint32_t>(shift), re.op)); break;
case RawType::U16: new_val.write_u16(0, Ops::do_bitwise(v_l.read_u16(0), static_cast<uint16_t>(shift), re.op)); break;
case RawType::U8 : new_val.write_u8 (0, Ops::do_bitwise(v_l.read_u8 (0), static_cast<uint8_t >(shift), re.op)); break;
case RawType::USize: new_val.write_usize(0, Ops::do_bitwise(v_l.read_usize(0), static_cast<uint64_t>(shift), re.op)); break;
- // TODO: Is signed allowed?
+ // Is signed allowed? (yes)
+ // - What's the exact semantics? For now assuming it's unsigned+reinterpret
+ case RawType::ISize: new_val.write_usize(0, Ops::do_bitwise(v_l.read_usize(0), static_cast<uint64_t>(shift), re.op)); break;
default:
- LOG_TODO("BinOp shift rhs unknown type - " << se.src << " w/ " << ty_r);
+ LOG_TODO("BinOp shift LHS unknown type - " << se.src << " w/ " << ty_l);
}
} break;
case ::MIR::eBinOp::BIT_AND:
@@ -1074,7 +1248,10 @@ bool InterpreterThread::step_one(Value& out_thread_result)
new_val = Value(ty_l);
switch(ty_l.inner_type)
{
- // TODO: U128/I128
+ case RawType::U128:
+ case RawType::I128:
+ new_val.write_u128( 0, Ops::do_bitwise(v_l.read_u128(0), v_r.read_u128(0), re.op) );
+ break;
case RawType::U64:
case RawType::I64:
new_val.write_u64( 0, Ops::do_bitwise(v_l.read_u64(0), v_r.read_u64(0), re.op) );
@@ -1089,6 +1266,7 @@ bool InterpreterThread::step_one(Value& out_thread_result)
break;
case RawType::U8:
case RawType::I8:
+ case RawType::Bool:
new_val.write_u8 ( 0, static_cast<uint8_t >(Ops::do_bitwise(v_l.read_u8 (0), v_r.read_u8 (0), re.op)) );
break;
case RawType::USize:
@@ -1098,16 +1276,44 @@ bool InterpreterThread::step_one(Value& out_thread_result)
default:
LOG_TODO("BinOp bitwise - " << se.src << " w/ " << ty_l);
}
+ // If the LHS had a relocation, propagate it over
+ if( auto r = v_l.get_relocation(0) )
+ {
+ LOG_DEBUG("- Restore relocation " << r);
+ new_val.set_reloc(0, ::std::min(POINTER_SIZE, new_val.size()), r);
+ }
break;
default:
LOG_ASSERT(ty_l == ty_r, "BinOp type mismatch - " << ty_l << " != " << ty_r);
auto val_l = PrimitiveValueVirt::from_value(ty_l, v_l);
auto val_r = PrimitiveValueVirt::from_value(ty_r, v_r);
+ RelocationPtr new_val_reloc;
switch(re.op)
{
- 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::ADD:
+ LOG_ASSERT(!v_r.get_relocation(0), "RHS of `+` has a relocation");
+ new_val_reloc = v_l.get_relocation(0);
+ val_l.get().add( val_r.get() );
+ break;
+ case ::MIR::eBinOp::SUB:
+ if( auto r = v_l.get_relocation(0) )
+ {
+ if( v_r.get_relocation(0) )
+ {
+ // Pointer difference, no relocation in output
+ }
+ else
+ {
+ new_val_reloc = ::std::move(r);
+ }
+ }
+ else
+ {
+ LOG_ASSERT(!v_r.get_relocation(0), "RHS of `-` has a relocation but LHS does not");
+ }
+ 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;
@@ -1117,6 +1323,10 @@ bool InterpreterThread::step_one(Value& out_thread_result)
}
new_val = Value(ty_l);
val_l.get().write_to_value(new_val, 0);
+ if( new_val_reloc )
+ {
+ new_val.set_reloc(0, ::std::min(POINTER_SIZE, new_val.size()), ::std::move(new_val_reloc));
+ }
break;
}
} break;
@@ -1216,10 +1426,18 @@ bool InterpreterThread::step_one(Value& out_thread_result)
state.get_value_and_type(se.dst, dst_ty);
new_val = Value(dst_ty);
- for(size_t i = 0; i < re.vals.size(); i++)
+ if( dst_ty.inner_type == RawType::Unit )
{
- auto fld_ofs = dst_ty.composite_type->fields.at(i).first;
- new_val.write_value(fld_ofs, state.param_to_value(re.vals[i]));
+ LOG_ASSERT(re.vals.size() == 0 , "");
+ }
+ else
+ {
+ LOG_ASSERT(dst_ty.inner_type == RawType::Composite, dst_ty);
+ for(size_t i = 0; i < re.vals.size(); i++)
+ {
+ auto fld_ofs = dst_ty.composite_type().fields.at(i).first;
+ new_val.write_value(fld_ofs, state.param_to_value(re.vals[i]));
+ }
}
} break;
TU_ARM(se.src, Array, re) {
@@ -1287,12 +1505,15 @@ bool InterpreterThread::step_one(Value& out_thread_result)
::HIR::TypeRef dst_ty;
state.get_value_and_type(se.dst, dst_ty);
new_val = Value(dst_ty);
- LOG_ASSERT(dst_ty.composite_type == &data_ty, "Destination type of RValue::Struct isn't the same as the input");
+ LOG_ASSERT(dst_ty.inner_type == RawType::Composite, dst_ty);
+ LOG_ASSERT(dst_ty.ptr.composite_type == &data_ty, "Destination type of RValue::Struct isn't the same as the input");
for(size_t i = 0; i < re.vals.size(); i++)
{
auto fld_ofs = data_ty.fields.at(i).first;
- new_val.write_value(fld_ofs, state.param_to_value(re.vals[i]));
+ auto v = state.param_to_value(re.vals[i]);
+ LOG_DEBUG("Struct - @" << fld_ofs << " = " << v);
+ new_val.write_value(fld_ofs, ::std::move(v));
}
} break;
}
@@ -1309,21 +1530,17 @@ bool InterpreterThread::step_one(Value& out_thread_result)
auto v = state.get_value_and_type(se.slot, ty);
// - Take a pointer to the inner
- auto alloc = v.m_alloc;
- if( !alloc )
- {
- if( !v.m_value->allocation )
- {
- v.m_value->create_allocation();
- }
- alloc = RelocationPtr::new_alloc( v.m_value->allocation );
- }
+ auto alloc = (v.m_value ? RelocationPtr::new_alloc(v.m_value->borrow("drop")) : v.m_alloc);
size_t ofs = v.m_offset;
- assert(ty.get_meta_type() == RawType::Unreachable);
+ //LOG_ASSERT(ty.get_meta_type() == RawType::Unreachable, "Dropping an unsized type with Statement::Drop - " << ty);
- auto ptr_ty = ty.wrapped(TypeWrapper::Ty::Borrow, 2);
+ auto ptr_ty = ty.wrapped(TypeWrapper::Ty::Borrow, /*BorrowTy::Unique*/2);
- auto ptr_val = Value::new_pointer(ptr_ty, ofs, ::std::move(alloc));
+ auto ptr_val = Value::new_pointer(ptr_ty, Allocation::PTR_BASE + ofs, ::std::move(alloc));
+ if( v.m_metadata )
+ {
+ ptr_val.write_value(POINTER_SIZE, *v.m_metadata);
+ }
if( !drop_value(ptr_val, ty, /*shallow=*/se.kind == ::MIR::eDropKind::SHALLOW) )
{
@@ -1345,14 +1562,17 @@ bool InterpreterThread::step_one(Value& out_thread_result)
}
else
{
- LOG_DEBUG("=== BB" << cur_frame.bb_idx << "/TERM: " << bb.terminator);
+ LOG_DEBUG("=== F" << cur_frame.frame_index << " BB" << cur_frame.bb_idx << "/TERM: " << bb.terminator);
switch(bb.terminator.tag())
{
case ::MIR::Terminator::TAGDEAD: throw "";
TU_ARM(bb.terminator, Incomplete, _te)
LOG_TODO("Terminator::Incomplete hit");
TU_ARM(bb.terminator, Diverge, _te)
- LOG_TODO("Terminator::Diverge hit");
+ LOG_DEBUG("DIVERGE (continue panic)");
+ assert(m_thread.panic_count > 0);
+ m_thread.panic_active = true;
+ return this->pop_stack(out_thread_result);
TU_ARM(bb.terminator, Panic, _te)
LOG_TODO("Terminator::Panic");
TU_ARM(bb.terminator, Goto, te)
@@ -1371,13 +1591,14 @@ bool InterpreterThread::step_one(Value& out_thread_result)
auto v = state.get_value_and_type(te.val, ty);
LOG_ASSERT(ty.get_wrapper() == nullptr, "Matching on wrapped value - " << ty);
LOG_ASSERT(ty.inner_type == RawType::Composite, "Matching on non-coposite - " << ty);
+ LOG_DEBUG("Switch v = " << v);
// TODO: Convert the variant list into something that makes it easier to switch on.
size_t found_target = SIZE_MAX;
size_t default_target = SIZE_MAX;
- for(size_t i = 0; i < ty.composite_type->variants.size(); i ++)
+ for(size_t i = 0; i < ty.composite_type().variants.size(); i ++)
{
- const auto& var = ty.composite_type->variants[i];
+ const auto& var = ty.composite_type().variants[i];
if( var.tag_data.size() == 0 )
{
// Save as the default, error for multiple defaults
@@ -1399,6 +1620,7 @@ bool InterpreterThread::step_one(Value& out_thread_result)
continue ;
if( ::std::memcmp(tmp.data(), var.tag_data.data(), tmp.size()) == 0 )
{
+ LOG_DEBUG("Explicit match " << i);
found_target = i;
break ;
}
@@ -1407,6 +1629,7 @@ bool InterpreterThread::step_one(Value& out_thread_result)
if( found_target == SIZE_MAX )
{
+ LOG_DEBUG("Default match " << default_target);
found_target = default_target;
}
if( found_target == SIZE_MAX )
@@ -1415,8 +1638,93 @@ bool InterpreterThread::step_one(Value& out_thread_result)
}
cur_frame.bb_idx = te.targets.at(found_target);
} break;
- TU_ARM(bb.terminator, SwitchValue, _te)
- LOG_TODO("Terminator::SwitchValue");
+ TU_ARM(bb.terminator, SwitchValue, te) {
+ ::HIR::TypeRef ty;
+ auto v = state.get_value_and_type(te.val, ty);
+ TU_MATCH_HDRA( (te.values), {)
+ TU_ARMA(Unsigned, vals) {
+ LOG_ASSERT(vals.size() == te.targets.size(), "Mismatch in SwitchValue target/value list lengths");
+ // Read an unsigned value
+ if( ty.get_wrapper() ) {
+ LOG_ERROR("Terminator::SwitchValue::Unsigned with wrapped type - " << ty);
+ }
+ uint64_t switch_val;
+ switch(ty.inner_type)
+ {
+ case RawType::U8: switch_val = v.read_u8(0); break;
+ case RawType::U16: switch_val = v.read_u16(0); break;
+ case RawType::U32: switch_val = v.read_u32(0); break;
+ case RawType::U64: switch_val = v.read_u64(0); break;
+ case RawType::U128: LOG_TODO("Terminator::SwitchValue::Unsigned with u128");
+ case RawType::USize: switch_val = v.read_usize(0); break;
+ case RawType::Char: switch_val = v.read_u32(0); break;
+ default:
+ LOG_ERROR("Terminator::SwitchValue::Unsigned with unexpected type - " << ty);
+ }
+
+ auto it = ::std::find(vals.begin(), vals.end(), switch_val);
+ if( it != vals.end() )
+ {
+ auto idx = it - vals.begin();
+ LOG_TRACE("- " << switch_val << " matched arm " << idx);
+ cur_frame.bb_idx = te.targets.at(idx);
+ }
+ else
+ {
+ LOG_TRACE("- " << switch_val << " not matched, taking default arm");
+ cur_frame.bb_idx = te.def_target;
+ }
+ }
+ TU_ARMA(Signed, vals) {
+ if( ty.get_wrapper() ) {
+ LOG_ERROR("Terminator::SwitchValue::Signed with wrapped type - " << ty);
+ }
+ int64_t switch_val;
+ switch(ty.inner_type)
+ {
+ case RawType::I8: switch_val = v.read_i8(0); break;
+ case RawType::I16: switch_val = v.read_i16(0); break;
+ case RawType::I32: switch_val = v.read_i32(0); break;
+ case RawType::I64: switch_val = v.read_i64(0); break;
+ case RawType::I128: LOG_TODO("Terminator::SwitchValue::Signed with i128");
+ case RawType::ISize: switch_val = v.read_isize(0); break;
+ default:
+ LOG_ERROR("Terminator::SwitchValue::Signed with unexpected type - " << ty);
+ }
+
+ auto it = ::std::find(vals.begin(), vals.end(), switch_val);
+ if( it != vals.end() )
+ {
+ auto idx = it - vals.begin();
+ LOG_TRACE("- " << switch_val << " matched arm " << idx);
+ cur_frame.bb_idx = te.targets.at(idx);
+ }
+ else
+ {
+ LOG_TRACE("- " << switch_val << " not matched, taking default arm");
+ cur_frame.bb_idx = te.def_target;
+ }
+ }
+ TU_ARMA(String, vals) {
+ auto size = v.read_usize(POINTER_SIZE);
+ const char* sv_ptr = reinterpret_cast<const char*>(v.read_pointer_const(0, size));
+ auto switch_val = ::stdx::string_view(sv_ptr, sv_ptr+size);
+
+ auto it = ::std::find_if(vals.begin(), vals.end(), [&](const ::std::string& x){ return switch_val == x; });
+ if( it != vals.end() )
+ {
+ auto idx = it - vals.begin();
+ LOG_TRACE("- '" << switch_val << "' matched arm " << idx);
+ cur_frame.bb_idx = te.targets.at(idx);
+ }
+ else
+ {
+ LOG_TRACE("- '" << switch_val << "' not matched, taking default arm");
+ cur_frame.bb_idx = te.def_target;
+ }
+ }
+ }
+ }
TU_ARM(bb.terminator, Call, te) {
::std::vector<Value> sub_args; sub_args.reserve(te.args.size());
for(const auto& a : te.args)
@@ -1447,10 +1755,9 @@ bool InterpreterThread::step_one(Value& out_thread_result)
LOG_DEBUG("> Indirect call " << v);
// TODO: Assert type
// TODO: Assert offset/content.
- assert(v.read_usize(0) == 0);
- fcn_alloc_ptr = v.get_relocation(v.m_offset);
- if( !fcn_alloc_ptr )
- LOG_FATAL("Calling value with no relocation - " << v);
+ LOG_ASSERT(v.read_usize(0) == Allocation::PTR_BASE, "Function pointer value invalid - " << v);
+ fcn_alloc_ptr = v.get_relocation(0);
+ LOG_ASSERT(fcn_alloc_ptr, "Calling value with no relocation - " << v);
LOG_ASSERT(fcn_alloc_ptr.get_ty() == RelocationPtr::Ty::Function, "Calling value that isn't a function pointer");
fcn_p = &fcn_alloc_ptr.fcn();
}
@@ -1459,12 +1766,23 @@ bool InterpreterThread::step_one(Value& out_thread_result)
if( !this->call_path(rv, *fcn_p, ::std::move(sub_args)) )
{
// Early return, don't want to update stmt_idx yet
+ LOG_DEBUG("- Non-immediate return, do not advance yet");
return false;
}
}
- LOG_DEBUG(te.ret_val << " = " << rv << " (resume " << cur_frame.fcn.my_path << ")");
- state.write_lvalue(te.ret_val, rv);
- cur_frame.bb_idx = te.ret_block;
+ // If a panic is in progress (in thread state), take the panic block instead
+ if( m_thread.panic_active )
+ {
+ m_thread.panic_active = false;
+ LOG_DEBUG("Panic into " << cur_frame.fcn->my_path);
+ cur_frame.bb_idx = te.panic_block;
+ }
+ else
+ {
+ LOG_DEBUG(te.ret_val << " = " << rv << " (resume " << cur_frame.fcn->my_path << ")");
+ state.write_lvalue(te.ret_val, rv);
+ cur_frame.bb_idx = te.ret_block;
+ }
} break;
}
cur_frame.stmt_idx = 0;
@@ -1502,41 +1820,56 @@ bool InterpreterThread::pop_stack(Value& out_thread_result)
auto& cur_frame = this->m_stack.back();
MirHelpers state { *this, cur_frame };
- const auto& blk = cur_frame.fcn.m_mir.blocks.at( cur_frame.bb_idx );
+ const auto& blk = cur_frame.fcn->m_mir.blocks.at( cur_frame.bb_idx );
if( cur_frame.stmt_idx < blk.statements.size() )
{
assert( blk.statements[cur_frame.stmt_idx].is_Drop() );
cur_frame.stmt_idx ++;
- LOG_DEBUG("DROP complete (resume " << cur_frame.fcn.my_path << ")");
+ LOG_DEBUG("DROP complete (resume " << cur_frame.fcn->my_path << ")");
}
else
{
assert( blk.terminator.is_Call() );
const auto& te = blk.terminator.as_Call();
- LOG_DEBUG(te.ret_val << " = " << res_v << " (resume " << cur_frame.fcn.my_path << ")");
+ LOG_DEBUG("Resume " << cur_frame.fcn->my_path);
+ LOG_DEBUG("F" << cur_frame.frame_index << " " << te.ret_val << " = " << res_v);
- state.write_lvalue(te.ret_val, res_v);
cur_frame.stmt_idx = 0;
- cur_frame.bb_idx = te.ret_block;
+ // If a panic is in progress (in thread state), take the panic block instead
+ if( m_thread.panic_active )
+ {
+ m_thread.panic_active = false;
+ LOG_DEBUG("Panic into " << cur_frame.fcn->my_path);
+ cur_frame.bb_idx = te.panic_block;
+ }
+ else
+ {
+ state.write_lvalue(te.ret_val, res_v);
+ cur_frame.bb_idx = te.ret_block;
+ }
}
return false;
}
}
+unsigned InterpreterThread::StackFrame::s_next_frame_index = 0;
InterpreterThread::StackFrame::StackFrame(const Function& fcn, ::std::vector<Value> args):
- fcn(fcn),
- ret( fcn.ret_ty ),
+ frame_index(s_next_frame_index++),
+ fcn(&fcn),
+ ret( fcn.ret_ty == RawType::Unreachable ? Value() : Value(fcn.ret_ty) ),
args( ::std::move(args) ),
locals( ),
drop_flags( fcn.m_mir.drop_flags ),
bb_idx(0),
stmt_idx(0)
{
+ LOG_DEBUG("F" << frame_index << " - Initializing " << fcn.m_mir.locals.size() << " locals");
this->locals.reserve( fcn.m_mir.locals.size() );
for(const auto& ty : fcn.m_mir.locals)
{
+ LOG_DEBUG("_" << (&ty - &fcn.m_mir.locals.front()) << ": " << ty);
if( ty == RawType::Unreachable ) {
// HACK: Locals can be !, but they can NEVER be accessed
this->locals.push_back( Value() );
@@ -1557,7 +1890,9 @@ bool InterpreterThread::call_path(Value& ret, const ::HIR::Path& path, ::std::ve
}
// - No guard page needed
- if( path == ::HIR::SimplePath { "std", {"sys", "imp", "thread", "guard", "init" } } )
+ if( path == ::HIR::SimplePath { "std", {"sys", "imp", "thread", "guard", "init" } }
+ || path == ::HIR::SimplePath { "std", {"sys", "unix", "thread", "guard", "init" } }
+ )
{
ret = Value::with_size(16, false);
ret.write_u64(0, 0);
@@ -1576,8 +1911,17 @@ bool InterpreterThread::call_path(Value& ret, const ::HIR::Path& path, ::std::ve
if( fcn.external.link_name != "" )
{
- // External function!
- return this->call_extern(ret, fcn.external.link_name, fcn.external.link_abi, ::std::move(args));
+ // TODO: Search for a function with both code and this link name
+ if(const auto* ext_fcn = m_modtree.get_ext_function(fcn.external.link_name.c_str()))
+ {
+ this->m_stack.push_back(StackFrame(*ext_fcn, ::std::move(args)));
+ return false;
+ }
+ else
+ {
+ // External function!
+ return this->call_extern(ret, fcn.external.link_name, fcn.external.link_abi, ::std::move(args));
+ }
}
this->m_stack.push_back(StackFrame(fcn, ::std::move(args)));
@@ -1603,42 +1947,73 @@ extern "C" {
#endif
bool InterpreterThread::call_extern(Value& rv, const ::std::string& link_name, const ::std::string& abi, ::std::vector<Value> args)
{
- if( link_name == "__rust_allocate" )
+ struct FfiHelpers {
+ static const char* read_cstr(const Value& v, size_t ptr_ofs, size_t* out_strlen=nullptr)
+ {
+ bool _is_mut;
+ size_t size;
+ // Get the base pointer and allocation size (checking for at least one valid byte to start with)
+ const char* ptr = reinterpret_cast<const char*>( v.read_pointer_unsafe(0, 1, /*out->*/ size, _is_mut) );
+ size_t len = 0;
+ // Seek until either out of space, or a NUL is found
+ while(size -- && *ptr)
+ {
+ ptr ++;
+ len ++;
+ }
+ if( out_strlen )
+ {
+ *out_strlen = len;
+ }
+ return reinterpret_cast<const char*>(v.read_pointer_const(0, len + 1)); // Final read will trigger an error if the NUL isn't there
+ }
+ };
+ if( link_name == "__rust_allocate" || link_name == "__rust_alloc" || link_name == "__rust_alloc_zeroed" )
{
+ static unsigned s_alloc_count = 0;
+
+ auto alloc_idx = s_alloc_count ++;
+ auto alloc_name = FMT_STRING("__rust_alloc#" << alloc_idx);
auto size = args.at(0).read_usize(0);
auto align = args.at(1).read_usize(0);
- LOG_DEBUG("__rust_allocate(size=" << size << ", align=" << align << ")");
- auto rty = ::HIR::TypeRef(RawType::Unit).wrap( TypeWrapper::Ty::Pointer, 0 );
+ LOG_DEBUG(link_name << "(size=" << size << ", align=" << align << "): name=" << alloc_name);
// TODO: Use the alignment when making an allocation?
- rv = Value::new_pointer(rty, 0, RelocationPtr::new_alloc(Allocation::new_alloc(size)));
+ auto alloc = Allocation::new_alloc(size, ::std::move(alloc_name));
+ LOG_TRACE("- alloc=" << alloc << " (" << alloc->size() << " bytes)");
+ auto rty = ::HIR::TypeRef(RawType::Unit).wrap( TypeWrapper::Ty::Pointer, 0 );
+
+ if( link_name == "__rust_alloc_zeroed" )
+ {
+ alloc->mark_bytes_valid(0, size);
+ }
+
+ rv = Value::new_pointer(rty, Allocation::PTR_BASE, RelocationPtr::new_alloc(::std::move(alloc)));
}
- else if( link_name == "__rust_reallocate" )
+ else if( link_name == "__rust_reallocate" || link_name == "__rust_realloc" )
{
- LOG_ASSERT(args.at(0).allocation, "__rust_reallocate first argument doesn't have an allocation");
auto alloc_ptr = args.at(0).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);
+ // NOTE: The ordering here depends on the rust version (1.19 has: old, new, align - 1.29 has: old, align, new)
+ auto align = args.at(true /*1.29*/ ? 2 : 3).read_usize(0);
+ auto newsize = args.at(true /*1.29*/ ? 3 : 2).read_usize(0);
LOG_DEBUG("__rust_reallocate(ptr=" << alloc_ptr << ", oldsize=" << oldsize << ", newsize=" << newsize << ", align=" << align << ")");
+ LOG_ASSERT(ptr_ofs == Allocation::PTR_BASE, "__rust_reallocate with offset pointer");
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 + 8-1) / 8 );
- alloc.mask.resize( (newsize + 8-1) / 8 );
+ alloc.resize(newsize);
// TODO: Should this instead make a new allocation to catch use-after-free?
rv = ::std::move(args.at(0));
}
- else if( link_name == "__rust_deallocate" )
+ else if( link_name == "__rust_deallocate" || link_name == "__rust_dealloc" )
{
- LOG_ASSERT(args.at(0).allocation, "__rust_deallocate first argument doesn't have an allocation");
auto alloc_ptr = args.at(0).get_relocation(0);
auto ptr_ofs = args.at(0).read_usize(0);
- LOG_ASSERT(ptr_ofs == 0, "__rust_deallocate with offset pointer");
+ LOG_ASSERT(ptr_ofs == Allocation::PTR_BASE, "__rust_deallocate with offset pointer");
LOG_DEBUG("__rust_deallocate(ptr=" << alloc_ptr << ")");
LOG_ASSERT(alloc_ptr, "__rust_deallocate with no backing allocation attached to pointer");
@@ -1675,6 +2050,10 @@ bool InterpreterThread::call_extern(Value& rv, const ::std::string& link_name, c
return false;
}
}
+ else if( link_name == "panic_impl" )
+ {
+ LOG_TODO("panic_impl");
+ }
else if( link_name == "__rust_start_panic" )
{
LOG_TODO("__rust_start_panic");
@@ -1683,6 +2062,19 @@ bool InterpreterThread::call_extern(Value& rv, const ::std::string& link_name, c
{
LOG_TODO("rust_begin_unwind");
}
+ // libunwind
+ else if( link_name == "_Unwind_RaiseException" )
+ {
+ LOG_DEBUG("_Unwind_RaiseException(" << args.at(0) << ")");
+ // Save the first argument in TLS, then return a status that indicates unwinding should commence.
+ m_thread.panic_active = true;
+ m_thread.panic_count += 1;
+ m_thread.panic_value = ::std::move(args.at(0));
+ }
+ else if( link_name == "_Unwind_DeleteException" )
+ {
+ LOG_DEBUG("_Unwind_DeleteException(" << args.at(0) << ")");
+ }
#ifdef _WIN32
// WinAPI functions used by libstd
else if( link_name == "AddVectoredExceptionHandler" )
@@ -1692,7 +2084,6 @@ bool InterpreterThread::call_extern(Value& rv, const ::std::string& link_name, c
}
else if( link_name == "GetModuleHandleW" )
{
- LOG_ASSERT(args.at(0).allocation, "");
const auto& tgt_alloc = args.at(0).get_relocation(0);
const void* arg0 = (tgt_alloc ? tgt_alloc.alloc().data_ptr() : nullptr);
//extern void* GetModuleHandleW(const void* s);
@@ -1706,7 +2097,7 @@ bool InterpreterThread::call_extern(Value& rv, const ::std::string& link_name, c
auto ret = GetModuleHandleW(static_cast<LPCWSTR>(arg0));
if(ret)
{
- rv = Value::new_ffiptr(FFIPointer { "GetModuleHandleW", ret, 0 });
+ rv = Value::new_ffiptr(FFIPointer::new_void("GetModuleHandleW", ret));
}
else
{
@@ -1731,7 +2122,7 @@ bool InterpreterThread::call_extern(Value& rv, const ::std::string& link_name, c
if( ret )
{
- rv = Value::new_ffiptr(FFIPointer { "GetProcAddress", ret, 0 });
+ rv = Value::new_ffiptr(FFIPointer::new_void("GetProcAddress", ret));
}
else
{
@@ -1752,6 +2143,96 @@ bool InterpreterThread::call_extern(Value& rv, const ::std::string& link_name, c
rv = Value::new_isize(val);
}
+ else if( link_name == "read" )
+ {
+ auto fd = args.at(0).read_i32(0);
+ auto count = args.at(2).read_isize(0);
+ auto buf_vr = args.at(1).read_pointer_valref_mut(0, count);
+
+ LOG_DEBUG("read(" << fd << ", " << buf_vr.data_ptr_mut() << ", " << count << ")");
+ ssize_t val = read(fd, buf_vr.data_ptr_mut(), count);
+ LOG_DEBUG("= " << val);
+
+ if( val > 0 )
+ {
+ buf_vr.mark_bytes_valid(0, val);
+ }
+
+ rv = Value::new_isize(val);
+ }
+ else if( link_name == "close" )
+ {
+ auto fd = args.at(0).read_i32(0);
+ LOG_DEBUG("close(" << fd << ")");
+ // TODO: Ensure that this FD is from the set known by the FFI layer
+ close(fd);
+ }
+ else if( link_name == "isatty" )
+ {
+ auto fd = args.at(0).read_i32(0);
+ LOG_DEBUG("isatty(" << fd << ")");
+ int rv_i = isatty(fd);
+ LOG_DEBUG("= " << rv_i);
+ rv = Value::new_i32(rv_i);
+ }
+ else if( link_name == "fcntl" )
+ {
+ // `fcntl` has custom handling for the third argument, as some are pointers
+ int fd = args.at(0).read_i32(0);
+ int command = args.at(1).read_i32(0);
+
+ int rv_i;
+ const char* name;
+ switch(command)
+ {
+ // - No argument
+ case F_GETFD: name = "F_GETFD"; if(0)
+ ;
+ {
+ LOG_DEBUG("fcntl(" << fd << ", " << name << ")");
+ rv_i = fcntl(fd, command);
+ } break;
+ // - Integer arguments
+ case F_DUPFD: name = "F_DUPFD"; if(0)
+ case F_DUPFD_CLOEXEC: name = "F_DUPFD_CLOEXEC"; if(0)
+ case F_SETFD: name = "F_SETFD"; if(0)
+ ;
+ {
+ int arg = args.at(2).read_i32(0);
+ LOG_DEBUG("fcntl(" << fd << ", " << name << ", " << arg << ")");
+ rv_i = fcntl(fd, command, arg);
+ } break;
+ default:
+ if( args.size() > 2 )
+ {
+ LOG_TODO("fnctl(..., " << command << ", " << args[2] << ")");
+ }
+ else
+ {
+ LOG_TODO("fnctl(..., " << command << ")");
+ }
+ }
+
+ LOG_DEBUG("= " << rv_i);
+ rv = Value(::HIR::TypeRef(RawType::I32));
+ rv.write_i32(0, rv_i);
+ }
+ else if( link_name == "prctl" )
+ {
+ auto option = args.at(0).read_i32(0);
+ int rv_i;
+ switch(option)
+ {
+ case 15: { // PR_SET_NAME - set thread name
+ auto name = FfiHelpers::read_cstr(args.at(1), 0);
+ LOG_DEBUG("prctl(PR_SET_NAME, \"" << name << "\"");
+ rv_i = 0;
+ } break;
+ default:
+ LOG_TODO("prctl(" << option << ", ...");
+ }
+ rv = Value::new_i32(rv_i);
+ }
else if( link_name == "sysconf" )
{
auto name = args.at(0).read_i32(0);
@@ -1761,6 +2242,10 @@ bool InterpreterThread::call_extern(Value& rv, const ::std::string& link_name, c
rv = Value::new_usize(val);
}
+ else if( link_name == "pthread_self" )
+ {
+ rv = Value::new_i32(0);
+ }
else if( link_name == "pthread_mutex_init" || link_name == "pthread_mutex_lock" || link_name == "pthread_mutex_unlock" || link_name == "pthread_mutex_destroy" )
{
rv = Value::new_i32(0);
@@ -1769,6 +2254,11 @@ bool InterpreterThread::call_extern(Value& rv, const ::std::string& link_name, c
{
rv = Value::new_i32(0);
}
+ else if( link_name == "pthread_rwlock_unlock" )
+ {
+ // TODO: Check that this thread holds the lock?
+ rv = Value::new_i32(0);
+ }
else if( link_name == "pthread_mutexattr_init" || link_name == "pthread_mutexattr_settype" || link_name == "pthread_mutexattr_destroy" )
{
rv = Value::new_i32(0);
@@ -1777,6 +2267,75 @@ bool InterpreterThread::call_extern(Value& rv, const ::std::string& link_name, c
{
rv = Value::new_i32(0);
}
+ else if( link_name == "pthread_attr_init" || link_name == "pthread_attr_destroy" || link_name == "pthread_getattr_np" )
+ {
+ rv = Value::new_i32(0);
+ }
+ else if( link_name == "pthread_attr_setstacksize" )
+ {
+ // Lie and return succeess
+ rv = Value::new_i32(0);
+ }
+ else if( link_name == "pthread_attr_getguardsize" )
+ {
+ const auto attr_p = args.at(0).read_pointer_const(0, 1);
+ auto out_size = args.at(1).deref(0, HIR::TypeRef(RawType::USize));
+
+ out_size.m_alloc.alloc().write_usize(out_size.m_offset, 0x1000);
+
+ rv = Value::new_i32(0);
+ }
+ else if( link_name == "pthread_attr_getstack" )
+ {
+ const auto attr_p = args.at(0).read_pointer_const(0, 1);
+ auto out_ptr = args.at(2).deref(0, HIR::TypeRef(RawType::USize));
+ auto out_size = args.at(2).deref(0, HIR::TypeRef(RawType::USize));
+
+ out_size.m_alloc.alloc().write_usize(out_size.m_offset, 0x4000);
+
+ rv = Value::new_i32(0);
+ }
+ else if( link_name == "pthread_create" )
+ {
+ auto thread_handle_out = args.at(0).read_pointer_valref_mut(0, sizeof(pthread_t));
+ auto attrs = args.at(1).read_pointer_const(0, sizeof(pthread_attr_t));
+ auto fcn_path = args.at(2).get_relocation(0).fcn();
+ LOG_ASSERT(args.at(2).read_usize(0) == Allocation::PTR_BASE, "");
+ auto arg = args.at(3);
+ LOG_NOTICE("TODO: pthread_create(" << thread_handle_out << ", " << attrs << ", " << fcn_path << ", " << arg << ")");
+ // TODO: Create a new interpreter context with this thread, use co-operative scheduling
+ // HACK: Just run inline
+ if( true )
+ {
+ auto tls = ::std::move(m_thread.tls_values);
+ this->m_stack.push_back(StackFrame::make_wrapper([=](Value& out_rv, Value /*rv*/)mutable ->bool {
+ out_rv = Value::new_i32(0);
+ m_thread.tls_values = ::std::move(tls);
+ return true;
+ }));
+
+ // TODO: Catch the panic out of this.
+ if( this->call_path(rv, fcn_path, { ::std::move(arg) }) )
+ {
+ bool v = this->pop_stack(rv);
+ assert( v == false );
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+ else {
+ //this->m_parent.create_thread(fcn_path, arg);
+ rv = Value::new_i32(EPERM);
+ }
+ }
+ else if( link_name == "pthread_detach" )
+ {
+ // "detach" - Prevent the need to explitly join a thread
+ rv = Value::new_i32(0);
+ }
else if( link_name == "pthread_cond_init" || link_name == "pthread_cond_destroy" )
{
rv = Value::new_i32(0);
@@ -1795,20 +2354,32 @@ bool InterpreterThread::call_extern(Value& rv, const ::std::string& link_name, c
auto key = args.at(0).read_u32(0);
// Get a pointer-sized value from storage
- uint64_t v = key < m_thread.tls_values.size() ? m_thread.tls_values[key] : 0;
-
- rv = Value::new_usize(v);
+ if( key < m_thread.tls_values.size() )
+ {
+ const auto& e = m_thread.tls_values[key];
+ rv = Value::new_usize(e.first);
+ if( e.second )
+ {
+ rv.set_reloc(0, POINTER_SIZE, e.second);
+ }
+ }
+ else
+ {
+ // Return zero until populated
+ rv = Value::new_usize(0);
+ }
}
else if( link_name == "pthread_setspecific" )
{
auto key = args.at(0).read_u32(0);
auto v = args.at(1).read_u64(0);
+ auto v_reloc = args.at(1).get_relocation(0);
- // Get a pointer-sized value from storage
+ // Store a pointer-sized value in storage
if( key >= m_thread.tls_values.size() ) {
m_thread.tls_values.resize(key+1);
}
- m_thread.tls_values[key] = v;
+ m_thread.tls_values[key] = ::std::make_pair(v, v_reloc);
rv = Value::new_i32(0);
}
@@ -1816,6 +2387,72 @@ bool InterpreterThread::call_extern(Value& rv, const ::std::string& link_name, c
{
rv = Value::new_i32(0);
}
+ // - Time
+ else if( link_name == "clock_gettime" )
+ {
+ // int clock_gettime(clockid_t clk_id, struct timespec *tp);
+ auto clk_id = args.at(0).read_u32(0);
+ auto tp_vr = args.at(1).read_pointer_valref_mut(0, sizeof(struct timespec));
+
+ LOG_DEBUG("clock_gettime(" << clk_id << ", " << tp_vr);
+ int rv_i = clock_gettime(clk_id, reinterpret_cast<struct timespec*>(tp_vr.data_ptr_mut()));
+ if(rv_i == 0)
+ tp_vr.mark_bytes_valid(0, tp_vr.m_size);
+ LOG_DEBUG("= " << rv_i << " (" << tp_vr << ")");
+ rv = Value::new_i32(rv_i);
+ }
+ // - Linux extensions
+ else if( link_name == "open64" )
+ {
+ const auto* path = FfiHelpers::read_cstr(args.at(0), 0);
+ auto flags = args.at(1).read_i32(0);
+ auto mode = (args.size() > 2 ? args.at(2).read_i32(0) : 0);
+
+ LOG_DEBUG("open64(\"" << path << "\", " << flags << ")");
+ int rv_i = open(path, flags, mode);
+ LOG_DEBUG("= " << rv_i);
+
+ rv = Value(::HIR::TypeRef(RawType::I32));
+ rv.write_i32(0, rv_i);
+ }
+ else if( link_name == "stat64" )
+ {
+ const auto* path = FfiHelpers::read_cstr(args.at(0), 0);
+ auto outbuf_vr = args.at(1).read_pointer_valref_mut(0, sizeof(struct stat));
+
+ LOG_DEBUG("stat64(\"" << path << "\", " << outbuf_vr << ")");
+ int rv_i = stat(path, reinterpret_cast<struct stat*>(outbuf_vr.data_ptr_mut()));
+ LOG_DEBUG("= " << rv_i);
+
+ if( rv_i == 0 )
+ {
+ // TODO: Mark the buffer as valid?
+ }
+
+ rv = Value(::HIR::TypeRef(RawType::I32));
+ rv.write_i32(0, rv_i);
+ }
+ else if( link_name == "__errno_location" )
+ {
+ rv = Value::new_ffiptr(FFIPointer::new_const_bytes("errno", &errno, sizeof(errno)));
+ }
+ else if( link_name == "syscall" )
+ {
+ auto num = args.at(0).read_u32(0);
+
+ LOG_DEBUG("syscall(" << num << ", ...) - hack return ENOSYS");
+ errno = ENOSYS;
+ rv = Value::new_i64(-1);
+ }
+ else if( link_name == "dlsym" )
+ {
+ auto handle = args.at(0).read_usize(0);
+ const char* name = FfiHelpers::read_cstr(args.at(1), 0);
+
+ LOG_DEBUG("dlsym(0x" << ::std::hex << handle << ", '" << name << "')");
+ LOG_NOTICE("dlsym stubbed to zero");
+ rv = Value::new_usize(0);
+ }
#endif
// std C
else if( link_name == "signal" )
@@ -1824,6 +2461,31 @@ bool InterpreterThread::call_extern(Value& rv, const ::std::string& link_name, c
rv = Value(::HIR::TypeRef(RawType::USize));
rv.write_usize(0, 1);
}
+ else if( link_name == "sigaction" )
+ {
+ rv = Value::new_i32(-1);
+ }
+ else if( link_name == "sigaltstack" ) // POSIX: Set alternate signal stack
+ {
+ rv = Value::new_i32(-1);
+ }
+ else if( link_name == "memcmp" )
+ {
+ auto n = args.at(2).read_usize(0);
+ int rv_i;
+ if( n > 0 )
+ {
+ const void* ptr_b = args.at(1).read_pointer_const(0, n);
+ const void* ptr_a = args.at(0).read_pointer_const(0, n);
+
+ rv_i = memcmp(ptr_a, ptr_b, n);
+ }
+ else
+ {
+ rv_i = 0;
+ }
+ rv = Value::new_i32(rv_i);
+ }
// - `void *memchr(const void *s, int c, size_t n);`
else if( link_name == "memchr" )
{
@@ -1835,11 +2497,10 @@ bool InterpreterThread::call_extern(Value& rv, const ::std::string& link_name, c
const void* ret = memchr(ptr, c, n);
rv = Value(::HIR::TypeRef(RawType::USize));
- rv.create_allocation();
if( ret )
{
- rv.write_usize(0, args.at(0).read_usize(0) + ( static_cast<const uint8_t*>(ret) - static_cast<const uint8_t*>(ptr) ));
- rv.allocation->relocations.push_back({ 0, ptr_alloc });
+ auto rv_ofs = args.at(0).read_usize(0) + ( static_cast<const uint8_t*>(ret) - static_cast<const uint8_t*>(ptr) );
+ rv.write_ptr(0, rv_ofs, ptr_alloc);
}
else
{
@@ -1856,11 +2517,10 @@ bool InterpreterThread::call_extern(Value& rv, const ::std::string& link_name, c
const void* ret = memrchr(ptr, c, n);
rv = Value(::HIR::TypeRef(RawType::USize));
- rv.create_allocation();
if( ret )
{
- rv.write_usize(0, args.at(0).read_usize(0) + ( static_cast<const uint8_t*>(ret) - static_cast<const uint8_t*>(ptr) ));
- rv.allocation->relocations.push_back({ 0, ptr_alloc });
+ auto rv_ofs = args.at(0).read_usize(0) + ( static_cast<const uint8_t*>(ret) - static_cast<const uint8_t*>(ptr) );
+ rv.write_ptr(0, rv_ofs, ptr_alloc);
}
else
{
@@ -1870,21 +2530,35 @@ bool InterpreterThread::call_extern(Value& rv, const ::std::string& link_name, c
else if( link_name == "strlen" )
{
// strlen - custom implementation to ensure validity
- bool _is_mut;
- size_t size;
- const char* ptr = reinterpret_cast<const char*>( args.at(0).read_pointer_unsafe(0, 1, size, _is_mut) );
size_t len = 0;
- while(size -- && *ptr)
- {
- ptr ++;
- len ++;
- }
- args.at(0).read_pointer_const(0, len + 1);
+ FfiHelpers::read_cstr(args.at(0), 0, &len);
//rv = Value::new_usize(len);
rv = Value(::HIR::TypeRef(RawType::USize));
rv.write_usize(0, len);
}
+ else if( link_name == "getenv" )
+ {
+ const auto* name = FfiHelpers::read_cstr(args.at(0), 0);
+ LOG_DEBUG("getenv(\"" << name << "\")");
+ const auto* ret_ptr = getenv(name);
+ if( ret_ptr )
+ {
+ LOG_DEBUG("= \"" << ret_ptr << "\"");
+ rv = Value::new_ffiptr(FFIPointer::new_const_bytes("getenv", ret_ptr, strlen(ret_ptr)+1));
+ }
+ else
+ {
+ LOG_DEBUG("= NULL");
+ rv = Value(::HIR::TypeRef(RawType::USize));
+ rv.create_allocation();
+ rv.write_usize(0,0);
+ }
+ }
+ else if( link_name == "setenv" )
+ {
+ LOG_TODO("Allow `setenv` without incurring thread unsafety");
+ }
// Allocators!
else
{
@@ -1893,7 +2567,7 @@ bool InterpreterThread::call_extern(Value& rv, const ::std::string& link_name, c
return true;
}
-bool InterpreterThread::call_intrinsic(Value& rv, const ::std::string& name, const ::HIR::PathParams& ty_params, ::std::vector<Value> args)
+bool InterpreterThread::call_intrinsic(Value& rv, const RcString& name, const ::HIR::PathParams& ty_params, ::std::vector<Value> args)
{
TRACE_FUNCTION_R(name, rv);
for(const auto& a : args)
@@ -1907,15 +2581,69 @@ bool InterpreterThread::call_intrinsic(Value& rv, const ::std::string& name, con
{
it = type_ids.insert(it, ty_T);
}
-
+
rv = Value::with_size(POINTER_SIZE, false);
rv.write_usize(0, it - type_ids.begin());
}
+ else if( name == "type_name" )
+ {
+ const auto& ty_T = ty_params.tys.at(0);
+
+ static ::std::map<HIR::TypeRef, ::std::string> s_type_names;
+ auto it = s_type_names.find(ty_T);
+ if( it == s_type_names.end() )
+ {
+ it = s_type_names.insert( ::std::make_pair(ty_T, FMT_STRING(ty_T)) ).first;
+ }
+
+ rv = Value::with_size(2*POINTER_SIZE, /*needs_alloc=*/true);
+ rv.write_ptr(0*POINTER_SIZE, Allocation::PTR_BASE, RelocationPtr::new_string(&it->second));
+ rv.write_usize(1*POINTER_SIZE, 0);
+ }
+ else if( name == "discriminant_value" )
+ {
+ const auto& ty = ty_params.tys.at(0);
+ ValueRef val = args.at(0).deref(0, ty);
+
+ size_t fallback = SIZE_MAX;
+ size_t found_index = SIZE_MAX;
+ LOG_ASSERT(ty.inner_type == RawType::Composite, "discriminant_value " << ty);
+ for(size_t i = 0; i < ty.composite_type().variants.size(); i ++)
+ {
+ const auto& var = ty.composite_type().variants[i];
+ if( var.tag_data.size() == 0 )
+ {
+ // Only seen in Option<NonNull>
+ assert(fallback == SIZE_MAX);
+ fallback = i;
+ }
+ else
+ {
+ // Get offset to the tag
+ ::HIR::TypeRef tag_ty;
+ size_t tag_ofs = ty.get_field_ofs(var.base_field, var.field_path, tag_ty);
+ // Compare
+ if( val.compare(tag_ofs, var.tag_data.data(), var.tag_data.size()) == 0 )
+ {
+ found_index = i;
+ break ;
+ }
+ }
+ }
+
+ if( found_index == SIZE_MAX )
+ {
+ LOG_ASSERT(fallback != SIZE_MAX, "Can't find variant of " << ty << " for " << val);
+ found_index = fallback;
+ }
+
+ rv = Value::new_usize(found_index);
+ }
else if( name == "atomic_fence" || name == "atomic_fence_acq" )
{
rv = Value();
}
- else if( name == "atomic_store" )
+ else if( name == "atomic_store" || name == "atomic_store_relaxed" || name == "atomic_store_rel" )
{
auto& ptr_val = args.at(0);
auto& data_val = args.at(1);
@@ -1927,20 +2655,20 @@ bool InterpreterThread::call_intrinsic(Value& rv, const ::std::string& name, con
LOG_ASSERT(alloc, "Deref of a value with no relocation");
// TODO: Atomic side of this?
- size_t ofs = ptr_val.read_usize(0);
+ size_t ofs = ptr_val.read_usize(0) - Allocation::PTR_BASE;
alloc.alloc().write_value(ofs, ::std::move(data_val));
}
- else if( name == "atomic_load" || name == "atomic_load_relaxed" )
+ else if( name == "atomic_load" || name == "atomic_load_relaxed" || name == "atomic_load_acq" )
{
auto& ptr_val = args.at(0);
- LOG_ASSERT(ptr_val.size() == POINTER_SIZE, "atomic_store of a value that isn't a pointer-sized value");
+ LOG_ASSERT(ptr_val.size() == POINTER_SIZE, "atomic_load of a value that isn't a pointer-sized value");
// There MUST be a relocation at this point with a valid allocation.
auto alloc = ptr_val.get_relocation(0);
LOG_ASSERT(alloc, "Deref of a value with no relocation");
// TODO: Atomic lock the allocation.
- size_t ofs = ptr_val.read_usize(0);
+ size_t ofs = ptr_val.read_usize(0) - Allocation::PTR_BASE;
const auto& ty = ty_params.tys.at(0);
rv = alloc.alloc().read_value(ofs, ty.get_size());
@@ -1948,7 +2676,7 @@ bool InterpreterThread::call_intrinsic(Value& rv, const ::std::string& name, con
else if( name == "atomic_xadd" || name == "atomic_xadd_relaxed" )
{
const auto& ty_T = ty_params.tys.at(0);
- auto ptr_ofs = args.at(0).read_usize(0);
+ auto ptr_ofs = args.at(0).read_usize(0) - Allocation::PTR_BASE;
auto ptr_alloc = args.at(0).get_relocation(0);
auto v = args.at(1).read_value(0, ty_T.get_size());
@@ -1969,7 +2697,7 @@ bool InterpreterThread::call_intrinsic(Value& rv, const ::std::string& name, con
else if( name == "atomic_xsub" || name == "atomic_xsub_relaxed" || name == "atomic_xsub_rel" )
{
const auto& ty_T = ty_params.tys.at(0);
- auto ptr_ofs = args.at(0).read_usize(0);
+ auto ptr_ofs = args.at(0).read_usize(0) - Allocation::PTR_BASE;
auto ptr_alloc = args.at(0).get_relocation(0);
auto v = args.at(1).read_value(0, ty_T.get_size());
@@ -1987,7 +2715,7 @@ bool InterpreterThread::call_intrinsic(Value& rv, const ::std::string& name, con
val_l.get().write_to_value( ptr_alloc.alloc(), ptr_ofs );
}
- else if( name == "atomic_xchg" )
+ else if( name == "atomic_xchg" || name == "atomic_xchg_acqrel" )
{
const auto& ty_T = ty_params.tys.at(0);
auto data_ref = args.at(0).read_pointer_valref_mut(0, ty_T.get_size());
@@ -2006,7 +2734,7 @@ bool InterpreterThread::call_intrinsic(Value& rv, const ::std::string& name, con
rv = Value::with_size( ty_T.get_size() + 1, false );
rv.write_value(0, data_ref.read_value(0, old_v.size()));
LOG_DEBUG("> *ptr = " << data_ref);
- if( data_ref.compare(old_v.data_ptr(), old_v.size()) == true ) {
+ if( data_ref.compare(0, old_v.data_ptr(), old_v.size()) == true ) {
data_ref.m_alloc.alloc().write_value( data_ref.m_offset, new_v );
rv.write_u8( old_v.size(), 1 );
}
@@ -2027,6 +2755,27 @@ bool InterpreterThread::call_intrinsic(Value& rv, const ::std::string& name, con
{
auto ptr_alloc = args.at(0).get_relocation(0);
auto ptr_ofs = args.at(0).read_usize(0);
+ LOG_ASSERT(ptr_ofs >= Allocation::PTR_BASE, "`offset` with invalid pointer - " << args.at(0));
+ auto& ofs_val = args.at(1);
+
+ auto delta_counts = ofs_val.read_usize(0);
+ auto ty_size = ty_params.tys.at(0).get_size();
+ LOG_DEBUG("\"offset\": 0x" << ::std::hex << ptr_ofs << " + 0x" << delta_counts << " * 0x" << ty_size);
+ ptr_ofs -= Allocation::PTR_BASE;
+ auto new_ofs = ptr_ofs + delta_counts * ty_size;
+ if(POINTER_SIZE != 8) {
+ new_ofs &= 0xFFFFFFFF;
+ }
+
+ rv = ::std::move(args.at(0));
+ rv.write_ptr(0, Allocation::PTR_BASE + new_ofs, ptr_alloc);
+ }
+ else if( name == "arith_offset" ) // Doesn't check validity, and allows wrapping
+ {
+ auto ptr_alloc = args.at(0).get_relocation(0);
+ auto ptr_ofs = args.at(0).read_usize(0);
+ //LOG_ASSERT(ptr_ofs >= Allocation::PTR_BASE, "`offset` with invalid pointer - " << args.at(0));
+ //ptr_ofs -= Allocation::PTR_BASE;
auto& ofs_val = args.at(1);
auto delta_counts = ofs_val.read_usize(0);
@@ -2034,11 +2783,16 @@ bool InterpreterThread::call_intrinsic(Value& rv, const ::std::string& name, con
if(POINTER_SIZE != 8) {
new_ofs &= 0xFFFFFFFF;
}
+ //new_ofs += Allocation::PTR_BASE;
rv = ::std::move(args.at(0));
- rv.write_usize(0, new_ofs);
- if( ptr_alloc ) {
- rv.allocation->relocations.push_back({ 0, ptr_alloc });
+ if( ptr_alloc )
+ {
+ rv.write_ptr(0, new_ofs, ptr_alloc);
+ }
+ else
+ {
+ rv.write_usize(0, new_ofs);
}
}
// effectively ptr::write
@@ -2047,18 +2801,13 @@ bool InterpreterThread::call_intrinsic(Value& rv, const ::std::string& name, con
auto& ptr_val = args.at(0);
auto& data_val = args.at(1);
- LOG_ASSERT(ptr_val.size() == POINTER_SIZE, "move_val_init of an address that isn't a pointer-sized value");
-
// There MUST be a relocation at this point with a valid allocation.
- LOG_ASSERT(ptr_val.allocation, "Deref of a value with no allocation (hence no relocations)");
- LOG_TRACE("Deref " << ptr_val << " and store " << data_val);
-
- auto ptr_alloc = ptr_val.get_relocation(0);
- LOG_ASSERT(ptr_alloc, "Deref of a value with no relocation");
+ // - TODO: What about FFI? (can't be a string or function though)
+ auto dst_vr = ptr_val.deref(0, ty_params.tys.at(0));
+ LOG_ASSERT(dst_vr.m_alloc, "Deref didn't yeild an allocation (error?)");
+ LOG_ASSERT(dst_vr.m_alloc.is_alloc(), "Deref didn't yield an allocation");
- size_t ofs = ptr_val.read_usize(0);
- ptr_alloc.alloc().write_value(ofs, ::std::move(data_val));
- LOG_DEBUG(ptr_alloc.alloc());
+ dst_vr.m_alloc.alloc().write_value(dst_vr.m_offset, ::std::move(data_val));
}
else if( name == "uninit" )
{
@@ -2069,6 +2818,22 @@ bool InterpreterThread::call_intrinsic(Value& rv, const ::std::string& name, con
rv = Value(ty_params.tys.at(0));
rv.mark_bytes_valid(0, rv.size());
}
+ else if( name == "write_bytes" )
+ {
+ auto& dst_ptr_v = args.at(0);
+ auto byte = args.at(1).read_u8(0);
+ auto count = args.at(2).read_usize(0);
+ auto bytes = count * ty_params.tys.at(0).get_size();
+
+ LOG_DEBUG("'write_bytes'(" << dst_ptr_v << ", " << (int)byte << ", " << count << "): bytes=" << bytes);
+
+ if( count > 0 )
+ {
+ auto dst_vr = dst_ptr_v.read_pointer_valref_mut(0, bytes);
+ memset(dst_vr.data_ptr_mut(), byte, bytes);
+ dst_vr.mark_bytes_valid(0, bytes);
+ }
+ }
// - Unsized stuff
else if( name == "size_of_val" )
{
@@ -2097,7 +2862,12 @@ bool InterpreterThread::call_intrinsic(Value& rv, const ::std::string& name, con
}
else if( ity->inner_type == RawType::TraitObject )
{
- LOG_TODO("size_of_val - Trait Object - " << ty);
+ auto vtable_ty = meta_ty.get_inner();
+ LOG_DEBUG("> vtable_ty = " << vtable_ty << " (size= " << vtable_ty.get_size() << ")");
+ auto vtable = val.deref(POINTER_SIZE, vtable_ty);
+ LOG_DEBUG("> vtable = " << vtable);
+ auto size = vtable.read_usize(1*POINTER_SIZE);
+ flex_size = size;
}
else
{
@@ -2111,12 +2881,86 @@ bool InterpreterThread::call_intrinsic(Value& rv, const ::std::string& name, con
rv.write_usize(0, ty.get_size());
}
}
+ else if( name == "min_align_of_val" )
+ {
+ /*const*/ auto& val = args.at(0);
+ const auto& ty = ty_params.tys.at(0);
+ rv = Value(::HIR::TypeRef(RawType::USize));
+ size_t fixed_size = 0; // unused
+ size_t flex_align = 0;
+ if( const auto* ity = ty.get_unsized_type(fixed_size) )
+ {
+ if( const auto* w = ity->get_wrapper() )
+ {
+ LOG_ASSERT(w->type == TypeWrapper::Ty::Slice, "align_of_val on wrapped type that isn't a slice - " << *ity);
+ flex_align = ity->get_inner().get_align();
+ }
+ else if( ity->inner_type == RawType::Str )
+ {
+ flex_align = 1;
+ }
+ else if( ity->inner_type == RawType::TraitObject )
+ {
+ const auto meta_ty = ty.get_meta_type();
+ auto vtable_ty = meta_ty.get_inner();
+ LOG_DEBUG("> vtable_ty = " << vtable_ty << " (size= " << vtable_ty.get_size() << ")");
+ auto vtable = val.deref(POINTER_SIZE, vtable_ty);
+ LOG_DEBUG("> vtable = " << vtable);
+ flex_align = vtable.read_usize(2*POINTER_SIZE);
+ }
+ else
+ {
+ LOG_BUG("Inner unsized type unknown - " << *ity);
+ }
+ }
+ rv.write_usize(0, ::std::max( ty.get_align(), flex_align ));
+ }
else if( name == "drop_in_place" )
{
auto& val = args.at(0);
const auto& ty = ty_params.tys.at(0);
return drop_value(val, ty);
}
+ else if( name == "try" )
+ {
+ auto fcn_path = args.at(0).get_relocation(0).fcn();
+ auto arg = args.at(1);
+ auto out_panic_value = args.at(2).read_pointer_valref_mut(0, POINTER_SIZE);
+
+ ::std::vector<Value> sub_args;
+ sub_args.push_back( ::std::move(arg) );
+
+ this->m_stack.push_back(StackFrame::make_wrapper([=](Value& out_rv, Value /*rv*/)mutable->bool{
+ if( m_thread.panic_active )
+ {
+ assert(m_thread.panic_count > 0);
+ m_thread.panic_active = false;
+ m_thread.panic_count --;
+ LOG_ASSERT(m_thread.panic_value.size() == out_panic_value.m_size, "Panic value " << m_thread.panic_value << " doesn't fit in " << out_panic_value);
+ out_panic_value.m_alloc.alloc().write_value( out_panic_value.m_offset, ::std::move(m_thread.panic_value) );
+ out_rv = Value::new_u32(1);
+ return true;
+ }
+ else
+ {
+ LOG_ASSERT(m_thread.panic_count == 0, "Panic count non-zero, but previous function returned non-panic");
+ out_rv = Value::new_u32(0);
+ return true;
+ }
+ }));
+
+ // TODO: Catch the panic out of this.
+ if( this->call_path(rv, fcn_path, ::std::move(sub_args)) )
+ {
+ bool v = this->pop_stack(rv);
+ assert( v == false );
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
// ----------------------------------------------------------------
// Checked arithmatic
else if( name == "add_with_overflow" )
@@ -2158,7 +3002,7 @@ bool InterpreterThread::call_intrinsic(Value& rv, const ::std::string& name, con
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() );
@@ -2173,6 +3017,24 @@ bool InterpreterThread::call_intrinsic(Value& rv, const ::std::string& name, con
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
}
+ // - "exact_div" :: Normal divide, but UB if not an exact multiple
+ else if( name == "exact_div" )
+ {
+ 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));
+
+ LOG_ASSERT(!rhs.get().is_zero(), "`exact_div` with zero divisor: " << args.at(0) << " / " << args.at(1));
+ auto rem = lhs;
+ rem.get().modulo( rhs.get() );
+ LOG_ASSERT(rem.get().is_zero(), "`exact_div` with yielded non-zero remainder: " << args.at(0) << " / " << args.at(1));
+ bool didnt_overflow = lhs.get().divide( rhs.get() );
+ LOG_ASSERT(didnt_overflow, "`exact_div` failed for unknown reason: " << args.at(0) << " /" << args.at(1));
+
+ rv = Value(ty);
+ lhs.get().write_to_value(rv, 0);
+ }
// Overflowing artithmatic
else if( name == "overflowing_sub" )
{
@@ -2181,6 +3043,18 @@ bool InterpreterThread::call_intrinsic(Value& rv, const ::std::string& name, con
auto lhs = PrimitiveValueVirt::from_value(ty, args.at(0));
auto rhs = PrimitiveValueVirt::from_value(ty, args.at(1));
lhs.get().subtract( rhs.get() );
+ // TODO: Overflowing part
+
+ rv = Value(ty);
+ lhs.get().write_to_value(rv, 0);
+ }
+ else if( name == "overflowing_add" )
+ {
+ 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().add( rhs.get() );
rv = Value(ty);
lhs.get().write_to_value(rv, 0);
@@ -2189,40 +3063,29 @@ bool InterpreterThread::call_intrinsic(Value& rv, const ::std::string& name, con
// memcpy
else if( name == "copy_nonoverlapping" )
{
- auto src_ofs = args.at(0).read_usize(0);
- auto src_alloc = args.at(0).get_relocation(0);
- auto dst_ofs = args.at(1).read_usize(0);
- auto dst_alloc = args.at(1).get_relocation(0);
+ //auto src_ofs = args.at(0).read_usize(0);
+ //auto src_alloc = args.at(0).get_relocation(0);
+ //auto dst_ofs = args.at(1).read_usize(0);
+ //auto dst_alloc = args.at(1).get_relocation(0);
size_t ent_count = args.at(2).read_usize(0);
size_t ent_size = ty_params.tys.at(0).get_size();
auto byte_count = ent_count * ent_size;
+ LOG_DEBUG("`copy_nonoverlapping`: byte_count=" << byte_count);
- LOG_ASSERT(src_alloc, "Source of copy* must have an allocation");
- LOG_ASSERT(dst_alloc, "Destination of copy* must be a memory allocation");
- LOG_ASSERT(dst_alloc.is_alloc(), "Destination of copy* must be a memory allocation");
-
- switch(src_alloc.get_ty())
+ // A count of zero doesn't need to do any of the checks (TODO: Validate this rule)
+ if( byte_count > 0 )
{
- case RelocationPtr::Ty::Allocation: {
- auto v = src_alloc.alloc().read_value(src_ofs, byte_count);
- LOG_DEBUG("v = " << v);
- dst_alloc.alloc().write_value(dst_ofs, ::std::move(v));
- } break;
- case RelocationPtr::Ty::StdString:
- LOG_ASSERT(src_ofs <= src_alloc.str().size(), "");
- LOG_ASSERT(byte_count <= src_alloc.str().size(), "");
- LOG_ASSERT(src_ofs + byte_count <= src_alloc.str().size(), "");
- dst_alloc.alloc().write_bytes(dst_ofs, src_alloc.str().data() + src_ofs, byte_count);
- break;
- case RelocationPtr::Ty::Function:
- LOG_FATAL("Attempt to copy* a function");
- break;
- case RelocationPtr::Ty::FfiPointer:
- LOG_ASSERT(src_ofs <= src_alloc.ffi().size, "");
- LOG_ASSERT(byte_count <= src_alloc.ffi().size, "");
- LOG_ASSERT(src_ofs + byte_count <= src_alloc.ffi().size, "");
- dst_alloc.alloc().write_bytes(dst_ofs, reinterpret_cast<const char*>(src_alloc.ffi().ptr_value) + src_ofs, byte_count);
- break;
+ auto src_vr = args.at(0).read_pointer_valref_mut(0, byte_count);
+ auto dst_vr = args.at(1).read_pointer_valref_mut(0, byte_count);
+
+ auto& dst_alloc = dst_vr.m_alloc;
+ LOG_ASSERT(dst_alloc, "Destination of copy* must be a memory allocation");
+ LOG_ASSERT(dst_alloc.is_alloc(), "Destination of copy* must be a memory allocation");
+
+ // TODO: is this inefficient?
+ auto src_val = src_vr.read_value(0, byte_count);
+ LOG_DEBUG("src_val = " << src_val);
+ dst_alloc.alloc().write_value(dst_vr.m_offset, ::std::move(src_val));
}
}
else
@@ -2235,6 +3098,7 @@ bool InterpreterThread::call_intrinsic(Value& rv, const ::std::string& name, con
// TODO: Use a ValueRef instead?
bool InterpreterThread::drop_value(Value ptr, const ::HIR::TypeRef& ty, bool is_shallow/*=false*/)
{
+ TRACE_FUNCTION_R(ptr << ": " << ty << (is_shallow ? " (shallow)" : ""), "");
// TODO: After the drop is done, flag the backing allocation for `ptr` as freed
if( is_shallow )
{
@@ -2242,7 +3106,7 @@ bool InterpreterThread::drop_value(Value ptr, const ::HIR::TypeRef& ty, bool is_
auto box_ptr_vr = ptr.read_pointer_valref_mut(0, POINTER_SIZE);
auto ofs = box_ptr_vr.read_usize(0);
auto alloc = box_ptr_vr.get_relocation(0);
- if( ofs != 0 || !alloc || !alloc.is_alloc() ) {
+ if( ofs != Allocation::PTR_BASE || !alloc || !alloc.is_alloc() ) {
LOG_ERROR("Attempting to shallow drop with invalid pointer (no relocation or non-zero offset) - " << box_ptr_vr);
}
@@ -2273,6 +3137,7 @@ bool InterpreterThread::drop_value(Value ptr, const ::HIR::TypeRef& ty, bool is_
case TypeWrapper::Ty::Slice: {
// - Get thin pointer and count
auto ofs = ptr.read_usize(0);
+ LOG_ASSERT(ofs >= Allocation::PTR_BASE, "");
auto ptr_reloc = ptr.get_relocation(0);
auto count = ptr.read_usize(POINTER_SIZE);
@@ -2281,8 +3146,28 @@ bool InterpreterThread::drop_value(Value ptr, const ::HIR::TypeRef& ty, bool is_
for(uint64_t i = 0; i < count; i ++)
{
auto ptr = Value::new_pointer(pty, ofs, ptr_reloc);
- if( !drop_value(ptr, ity) ) {
- LOG_TODO("Handle closure looping when dropping a slice");
+ if( !drop_value(ptr, ity) )
+ {
+ // - This is trying to invoke custom drop glue, need to suspend this operation and come back later
+
+ // > insert a new frame shim BEFORE the current top (which would be the frame created by
+ // `drop_value` calling a function)
+ m_stack.insert( m_stack.end() - 1, StackFrame::make_wrapper([this,pty,ity,ptr_reloc,count, i,ofs](Value& rv, Value drop_rv) mutable {
+ assert(i < count);
+ i ++;
+ ofs += ity.get_size();
+ if( i < count )
+ {
+ auto ptr = Value::new_pointer(pty, ofs, ptr_reloc);
+ assert(!drop_value(ptr, ity));
+ return false;
+ }
+ else
+ {
+ return true;
+ }
+ }) );
+ return false;
}
ofs += ity.get_size();
}
@@ -2297,12 +3182,12 @@ bool InterpreterThread::drop_value(Value ptr, const ::HIR::TypeRef& ty, bool is_
{
if( ty.inner_type == RawType::Composite )
{
- if( ty.composite_type->drop_glue != ::HIR::Path() )
+ if( ty.composite_type().drop_glue != ::HIR::Path() )
{
LOG_DEBUG("Drop - " << ty);
Value tmp;
- return this->call_path(tmp, ty.composite_type->drop_glue, { ptr });
+ return this->call_path(tmp, ty.composite_type().drop_glue, { ptr });
}
else
{
@@ -2311,7 +3196,21 @@ bool InterpreterThread::drop_value(Value ptr, const ::HIR::TypeRef& ty, bool is_
}
else if( ty.inner_type == RawType::TraitObject )
{
- LOG_TODO("Drop - " << ty << " - trait object");
+ // Get the drop glue from the vtable (first entry)
+ auto inner_ptr = ptr.read_value(0, POINTER_SIZE);
+ auto vtable = ptr.deref(POINTER_SIZE, ty.get_meta_type().get_inner());
+ auto drop_r = vtable.get_relocation(0);
+ if( drop_r )
+ {
+ LOG_ASSERT(drop_r.get_ty() == RelocationPtr::Ty::Function, "");
+ auto fcn = drop_r.fcn();
+ static Value tmp;
+ return this->call_path(tmp, fcn, { ::std::move(inner_ptr) });
+ }
+ else
+ {
+ // None
+ }
}
else
{
diff --git a/tools/standalone_miri/miri.hpp b/tools/standalone_miri/miri.hpp
index 6f02ffee..f835fedb 100644
--- a/tools/standalone_miri/miri.hpp
+++ b/tools/standalone_miri/miri.hpp
@@ -13,10 +13,16 @@ struct ThreadState
{
static unsigned s_next_tls_key;
unsigned call_stack_depth;
- ::std::vector<uint64_t> tls_values;
+ ::std::vector< ::std::pair<uint64_t, RelocationPtr> > tls_values;
+
+ unsigned panic_count;
+ bool panic_active;
+ Value panic_value;
ThreadState():
call_stack_depth(0)
+ ,panic_count(0)
+ ,panic_active(false)
{
}
@@ -35,8 +41,11 @@ class InterpreterThread
friend struct MirHelpers;
struct StackFrame
{
+ static unsigned s_next_frame_index;
+ unsigned frame_index;
+
::std::function<bool(Value&,Value)> cb;
- const Function& fcn;
+ const Function* fcn;
Value ret;
::std::vector<Value> args;
::std::vector<Value> locals;
@@ -56,11 +65,13 @@ class InterpreterThread
ModuleTree& m_modtree;
ThreadState m_thread;
+ size_t m_instruction_count;
::std::vector<StackFrame> m_stack;
public:
InterpreterThread(ModuleTree& modtree):
- m_modtree(modtree)
+ m_modtree(modtree),
+ m_instruction_count(0)
{
}
~InterpreterThread();
@@ -77,7 +88,7 @@ private:
// Returns true if the call was resolved instantly
bool call_extern(Value& ret_val, const ::std::string& name, const ::std::string& abi, ::std::vector<Value> args);
// Returns true if the call was resolved instantly
- bool call_intrinsic(Value& ret_val, const ::std::string& name, const ::HIR::PathParams& pp, ::std::vector<Value> args);
+ bool call_intrinsic(Value& ret_val, const RcString& name, const ::HIR::PathParams& pp, ::std::vector<Value> args);
// Returns true if the call was resolved instantly
bool drop_value(Value ptr, const ::HIR::TypeRef& ty, bool is_shallow=false);
diff --git a/tools/standalone_miri/module_tree.cpp b/tools/standalone_miri/module_tree.cpp
index 8e0a231a..91d82d85 100644
--- a/tools/standalone_miri/module_tree.cpp
+++ b/tools/standalone_miri/module_tree.cpp
@@ -57,6 +57,24 @@ void ModuleTree::load_file(const ::std::string& path)
// Keep going!
}
}
+void ModuleTree::validate()
+{
+ TRACE_FUNCTION_R("", "");
+ for(const auto& dt : this->data_types)
+ {
+ //LOG_ASSERT(dt.second->populated, "Type " << dt.first << " never defined");
+ }
+
+ for(const auto& fcn : this->functions)
+ {
+ // TODO: This doesn't actually happen yet (this combination can't be parsed)
+ if( fcn.second.external.link_name != "" && !fcn.second.m_mir.blocks.empty() )
+ {
+ LOG_DEBUG(fcn.first << " = '" << fcn.second.external.link_name << "'");
+ ext_functions.insert(::std::make_pair( fcn.second.external.link_name, &fcn.second ));
+ }
+ }
+}
// Parse a single item from a .mir file
bool Parser::parse_one()
{
@@ -99,25 +117,26 @@ bool Parser::parse_one()
rv_ty = parse_type();
}
+ Function::ExtInfo ext;
if( lex.consume_if('=') )
{
- auto link_name = ::std::move(lex.check_consume(TokenClass::String).strval);
+ ext.link_name = ::std::move(lex.check_consume(TokenClass::String).strval);
lex.check_consume(':');
- auto abi = ::std::move(lex.check_consume(TokenClass::String).strval);
- lex.check_consume(';');
-
+ ext.link_abi = ::std::move(lex.check_consume(TokenClass::String).strval);
+ }
+ ::MIR::Function body;
+ if( lex.consume_if(';') )
+ {
LOG_DEBUG(lex << "extern fn " << p);
- auto p2 = p;
- tree.functions.insert( ::std::make_pair(::std::move(p), Function { ::std::move(p2), ::std::move(arg_tys), rv_ty, {link_name, abi}, {} }) );
}
else
{
- auto body = parse_body();
+ body = parse_body();
LOG_DEBUG(lex << "fn " << p);
- 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) }) );
}
+ auto p2 = p;
+ tree.functions.insert( ::std::make_pair(::std::move(p), Function { ::std::move(p2), ::std::move(arg_tys), rv_ty, ::std::move(ext), ::std::move(body) }) );
}
else if( lex.consume_if("static") )
{
@@ -133,8 +152,7 @@ bool Parser::parse_one()
Static s;
s.val = Value(ty);
// - Statics need to always have an allocation (for references)
- if( !s.val.allocation )
- s.val.create_allocation();
+ s.val.ensure_allocation();
s.val.write_bytes(0, data.data(), data.size());
s.ty = ty;
@@ -153,15 +171,14 @@ bool Parser::parse_one()
{
auto reloc_str = ::std::move(lex.consume().strval);
- auto a = Allocation::new_alloc( reloc_str.size() );
- //a.alloc().set_tag();
+ auto a = Allocation::new_alloc( reloc_str.size(), FMT_STRING("static " << p) );
a->write_bytes(0, reloc_str.data(), reloc_str.size());
- s.val.allocation->relocations.push_back({ static_cast<size_t>(ofs), /*size,*/ RelocationPtr::new_alloc(::std::move(a)) });
+ s.val.set_reloc( ofs, size, RelocationPtr::new_alloc(::std::move(a)) );
}
else if( lex.next() == "::" || lex.next() == "<" )
{
auto reloc_path = parse_path();
- s.val.allocation->relocations.push_back({ static_cast<size_t>(ofs), /*size,*/ RelocationPtr::new_fcn(reloc_path) });
+ s.val.set_reloc( ofs, size, RelocationPtr::new_fcn(reloc_path) );
}
else
{
@@ -185,6 +202,7 @@ bool Parser::parse_one()
//LOG_TRACE("type " << p);
auto rv = DataType {};
+ rv.populated = true;
rv.my_path = p;
lex.check_consume('{');
@@ -332,9 +350,6 @@ bool Parser::parse_one()
struct H
{
- static ::std::unique_ptr<::MIR::LValue> make_lvp(::MIR::LValue&& lv) {
- return ::std::unique_ptr<::MIR::LValue>(new ::MIR::LValue(::std::move(lv)));
- }
//
// Parse a LValue
//
@@ -357,7 +372,7 @@ bool Parser::parse_one()
if( name.substr(0,3) == "arg" ) {
try {
auto idx = static_cast<unsigned>( ::std::stol(name.substr(3)) );
- lv = ::MIR::LValue::make_Argument({ idx });
+ lv = ::MIR::LValue::new_Argument( idx );
}
catch(const ::std::exception& e) {
LOG_ERROR(lex << "Invalid argument name - " << name << " - " << e.what());
@@ -365,7 +380,7 @@ bool Parser::parse_one()
}
// Hard-coded "RETURN" lvalue
else if( name == "RETURN" ) {
- lv = ::MIR::LValue::make_Return({});
+ lv = ::MIR::LValue::new_Return();
}
// Otherwise, look up variable names
else {
@@ -373,13 +388,13 @@ bool Parser::parse_one()
if( it == var_names.end() ) {
LOG_ERROR(lex << "Cannot find variable named '" << name << "'");
}
- lv = ::MIR::LValue::make_Local(static_cast<unsigned>(it - var_names.begin()));
+ lv = ::MIR::LValue::new_Local(static_cast<unsigned>(it - var_names.begin()));
}
}
else if( lex.next() == "::" || lex.next() == '<' )
{
auto path = p.parse_path();
- lv = ::MIR::LValue( ::std::move(path) );
+ lv = ::MIR::LValue::new_Static( ::std::move(path) );
}
else {
LOG_ERROR(lex << "Unexpected token in LValue - " << lex.next());
@@ -390,19 +405,19 @@ bool Parser::parse_one()
{
lex.check(TokenClass::Integer);
auto idx = static_cast<unsigned>( lex.consume().integer() );
- lv = ::MIR::LValue::make_Downcast({ make_lvp(::std::move(lv)), idx });
+ lv = ::MIR::LValue::new_Downcast(::std::move(lv), idx);
}
else if( lex.consume_if('.') )
{
lex.check(TokenClass::Integer);
auto idx = static_cast<unsigned>( lex.consume().integer() );
- lv = ::MIR::LValue::make_Field({ make_lvp(::std::move(lv)), idx });
+ lv = ::MIR::LValue::new_Field( ::std::move(lv), idx );
}
else if( lex.next() == '[' )
{
lex.consume();
auto idx_lv = parse_lvalue(p, var_names);
- lv = ::MIR::LValue::make_Index({ make_lvp(::std::move(lv)), make_lvp(::std::move(idx_lv)) });
+ lv = ::MIR::LValue::new_Index(::std::move(lv), idx_lv.as_Local());
lex.check_consume(']');
}
else
@@ -412,7 +427,7 @@ bool Parser::parse_one()
}
while(deref --)
{
- lv = ::MIR::LValue::make_Deref({ make_lvp(::std::move(lv)) });
+ lv = ::MIR::LValue::new_Deref( ::std::move(lv) );
}
return lv;
}
@@ -464,7 +479,7 @@ bool Parser::parse_one()
else if( p.lex.consume_if("ADDROF") ) {
auto path = p.parse_path();
- return ::MIR::Constant::make_ItemAddr({ ::std::move(path) });
+ return ::MIR::Constant::make_ItemAddr({ ::std::make_unique<HIR::Path>(::std::move(path)) });
}
else {
LOG_BUG(p.lex << "BUG? " << p.lex.next());
@@ -867,7 +882,7 @@ bool Parser::parse_one()
::std::vector<unsigned> targets;
while(lex.next() != '{')
{
- targets.push_back( static_cast<unsigned>(lex.consume().integer()) );
+ targets.push_back( static_cast<unsigned>(lex.check_consume(TokenClass::Integer).integer()) );
if( !lex.consume_if(',') )
break;
}
@@ -936,7 +951,7 @@ bool Parser::parse_one()
lex.check_consume(')');
}
else if( lex.next() == TokenClass::String ) {
- auto name = ::std::move(lex.consume().strval);
+ auto name = RcString::new_interned(lex.consume().strval);
auto params = parse_pathparams();
ct = ::MIR::CallTarget::make_Intrinsic({ ::std::move(name), ::std::move(params) });
}
@@ -1214,7 +1229,14 @@ RawType Parser::parse_core_type()
{
ret_ty = ::HIR::TypeRef::unit();
}
- return ::HIR::TypeRef(RawType::Function);
+ auto ft = FunctionType {
+ is_unsafe,
+ ::std::move(abi),
+ ::std::move(args),
+ ::std::move(ret_ty)
+ };
+ const auto* ft_p = &*tree.function_types.insert(::std::move(ft)).first;
+ return ::HIR::TypeRef(ft_p);
// TODO: Use abi/ret_ty/args as part of that
}
else if( lex.consume_if("dyn") )
@@ -1262,14 +1284,28 @@ RawType Parser::parse_core_type()
}
lex.consume_if(')');
+ // Ignore marker traits.
+
auto rv = ::HIR::TypeRef(RawType::TraitObject);
if( base_trait != ::HIR::GenericPath() )
{
// Generate vtable path
auto vtable_path = base_trait;
vtable_path.m_simplepath.ents.back() += "#vtable";
- // - TODO: Associated types?
- rv.composite_type = this->get_composite( ::std::move(vtable_path) );
+ if( atys.size() > 1 )
+ {
+ LOG_TODO("Handle multiple ATYs in vtable path");
+ }
+ else if( atys.size() == 1 )
+ {
+ vtable_path.m_params.tys.push_back( ::std::move(atys[0].second) );
+ }
+ // - TODO: Associated types? (Need to ensure ordering is correct)
+ rv.ptr.composite_type = this->get_composite( ::std::move(vtable_path) );
+ }
+ else
+ {
+ // TODO: vtable for empty trait?
}
return rv;
}
@@ -1289,6 +1325,7 @@ const DataType* Parser::get_composite(::HIR::GenericPath gp)
{
// TODO: Later on need to check if the type is valid.
auto v = ::std::make_unique<DataType>(DataType {});
+ v->populated = false;
v->my_path = gp;
auto ir = tree.data_types.insert(::std::make_pair( ::std::move(gp), ::std::move(v)) );
it = ir.first;
@@ -1318,6 +1355,15 @@ const Function* ModuleTree::get_function_opt(const ::HIR::Path& p) const
}
return &it->second;
}
+const Function* ModuleTree::get_ext_function(const char* name) const
+{
+ auto it = ext_functions.find(name);
+ if( it == ext_functions.end() )
+ {
+ return nullptr;
+ }
+ return it->second;
+}
Static& ModuleTree::get_static(const ::HIR::Path& p)
{
auto it = statics.find(p);
diff --git a/tools/standalone_miri/module_tree.hpp b/tools/standalone_miri/module_tree.hpp
index efa0a034..299aa51c 100644
--- a/tools/standalone_miri/module_tree.hpp
+++ b/tools/standalone_miri/module_tree.hpp
@@ -11,6 +11,7 @@
#include <map>
#include <set>
+#include "../../src/include/rc_string.hpp"
#include "../../src/mir/mir.hpp"
#include "hir_sim.hpp"
#include "value.hpp"
@@ -22,7 +23,7 @@ struct Function
::HIR::TypeRef ret_ty;
// If `link_name` is non-empty, then the function is an external
- struct {
+ struct ExtInfo {
::std::string link_name;
::std::string link_abi;
} external;
@@ -47,14 +48,20 @@ class ModuleTree
// Hack: Tuples are stored as `::""::<A,B,C,...>`
::std::map<::HIR::GenericPath, ::std::unique_ptr<DataType>> data_types;
+
+ ::std::set<FunctionType> function_types; // note: insertion doesn't invaliate pointers.
+
+ ::std::map<::std::string, const Function*> ext_functions;
public:
ModuleTree();
void load_file(const ::std::string& path);
+ void validate();
::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;
+ const Function* get_ext_function(const char* name) const;
Static& get_static(const ::HIR::Path& p);
Static* get_static_opt(const ::HIR::Path& p);
@@ -66,16 +73,15 @@ public:
// struct/union/enum
struct DataType
{
+ bool populated;
::HIR::GenericPath my_path;
- // TODO: Store the name of this type for logging?
-
- // TODO: Metadata type! (indicates an unsized wrapper)
- // TODO: Drop glue
size_t alignment;
size_t size;
+ // Drop glue
::HIR::Path drop_glue;
+ // Metadata type! (indicates an unsized wrapper)
::HIR::TypeRef dst_meta;
// Offset and datatype
@@ -91,3 +97,21 @@ struct DataType
};
::std::vector<VariantValue> variants;
};
+
+struct FunctionType
+{
+ bool unsafe;
+ ::std::string abi;
+ ::std::vector<HIR::TypeRef> args;
+ HIR::TypeRef ret;
+
+ bool operator<(const FunctionType& x) const {
+ #define _(f) if(f != x.f) return f < x.f
+ _(unsafe);
+ _(abi);
+ _(args);
+ _(ret);
+ #undef _
+ return false;
+ }
+};
diff --git a/tools/standalone_miri/u128.hpp b/tools/standalone_miri/u128.hpp
new file mode 100644
index 00000000..8403b94a
--- /dev/null
+++ b/tools/standalone_miri/u128.hpp
@@ -0,0 +1,170 @@
+#pragma once
+
+class U128
+{
+ friend class I128;
+ uint64_t lo, hi;
+public:
+ U128(): lo(0), hi(0) {}
+
+ U128(uint8_t v): lo(v), hi(0) {}
+ U128(int8_t v): lo(v), hi(v < 0 ? -1 : 0) {}
+
+ void fmt(::std::ostream& os) const { os << hi << ":" << lo; }
+
+ int cmp(U128 v) const {
+ if( hi != v.hi ) {
+ return (hi < v.hi ? -1 : 1);
+ }
+ if( lo != v.lo ) {
+ return (lo < v.lo ? -1 : 1);
+ }
+ return 0;
+ }
+ int cmp(unsigned v) const {
+ if(hi)
+ return 1;
+ if(lo < v)
+ return -1;
+ if(lo > v)
+ return 1;
+ return 0;
+ }
+
+ template<typename T> bool operator< (const T& v) const { return this->cmp(v) < 0; }
+ template<typename T> bool operator<=(const T& v) const { return this->cmp(v) <= 0; }
+ template<typename T> bool operator> (const T& v) const { return this->cmp(v) > 0; }
+ template<typename T> bool operator>=(const T& v) const { return this->cmp(v) >= 0; }
+ template<typename T> bool operator==(const T& v) const { return this->cmp(v) == 0; }
+ template<typename T> bool operator!=(const T& v) const { return this->cmp(v) != 0; }
+
+ operator uint8_t() const { return static_cast<uint8_t>(lo); }
+
+ U128 operator&(U128 x) const {
+ U128 rv;
+ rv.lo = this->lo & x.lo;
+ rv.hi = this->hi & x.hi;
+ return rv;
+ }
+ U128 operator|(U128 x) const {
+ U128 rv;
+ rv.lo = this->lo | x.lo;
+ rv.hi = this->hi | x.hi;
+ return rv;
+ }
+ U128 operator^(U128 x) const {
+ U128 rv;
+ rv.lo = this->lo ^ x.lo;
+ rv.hi = this->hi ^ x.hi;
+ return rv;
+ }
+
+ U128 operator<<(U128 n) const
+ {
+ if( n < 128 )
+ {
+ return *this << static_cast<uint8_t>(n);
+ }
+ else
+ {
+ return U128();
+ }
+ }
+ U128 operator<<(uint8_t n) const
+ {
+ if(n == 0)
+ {
+ return *this;
+ }
+ else if( n < 64 )
+ {
+ U128 rv;
+ rv.lo = lo << n;
+ rv.hi = (hi << n) | (lo >> (64-n));
+ return rv;
+ }
+ else if( n < 128 )
+ {
+ U128 rv;
+ rv.lo = 0;
+ rv.hi = (lo << (n-64));
+ return rv;
+ }
+ else
+ {
+ return U128();
+ }
+ }
+ U128 operator>>(uint8_t n) const
+ {
+ if(n == 0)
+ {
+ return *this;
+ }
+ else if( n < 64 )
+ {
+ U128 rv;
+ rv.lo = (lo >> n) | (hi << (64-n));
+ rv.hi = (hi >> n);
+ return rv;
+ }
+ else if( n < 128 )
+ {
+ U128 rv;
+ rv.lo = (hi >> (n-64));
+ rv.hi = 0;
+ return rv;
+ }
+ else
+ {
+ return U128();
+ }
+ }
+
+ friend ::std::ostream& operator<<(::std::ostream& os, const U128& x) { x.fmt(os); return os; }
+};
+
+class I128
+{
+ U128 v;
+public:
+ I128() {}
+
+ int cmp(I128 x) const {
+ if(v.hi != x.v.hi)
+ return (static_cast<int64_t>(v.hi) < static_cast<int64_t>(x.v.hi) ? -1 : 1);
+ if(v.lo != x.v.lo)
+ {
+ if( static_cast<int64_t>(v.hi) < 0 )
+ {
+ // Negative, so larger raw value is the smaller
+ return (v.lo > x.v.lo ? -1 : 1);
+ }
+ else
+ {
+ return (v.lo < x.v.lo ? -1 : 1);
+ }
+ }
+ return 0;
+ }
+ //int cmp(int v) const {
+ // if(hi)
+ // return 1;
+ // if(lo < v)
+ // return -1;
+ // if(lo > v)
+ // return 1;
+ // return 0;
+ //}
+
+ template<typename T> bool operator< (const T& v) const { return this->cmp(v) < 0; }
+ template<typename T> bool operator<=(const T& v) const { return this->cmp(v) <= 0; }
+ template<typename T> bool operator> (const T& v) const { return this->cmp(v) > 0; }
+ template<typename T> bool operator>=(const T& v) const { return this->cmp(v) >= 0; }
+ template<typename T> bool operator==(const T& v) const { return this->cmp(v) == 0; }
+ template<typename T> bool operator!=(const T& v) const { return this->cmp(v) != 0; }
+
+ void fmt(::std::ostream& os) const { os << v.hi << ":" << v.lo; }
+
+ friend ::std::ostream& operator<<(::std::ostream& os, const I128& x) { x.fmt(os); return os; }
+};
diff --git a/tools/standalone_miri/value.cpp b/tools/standalone_miri/value.cpp
index 5974a172..a497f0bd 100644
--- a/tools/standalone_miri/value.cpp
+++ b/tools/standalone_miri/value.cpp
@@ -13,13 +13,98 @@
#include <algorithm>
#include "debug.hpp"
-AllocationHandle Allocation::new_alloc(size_t size)
+namespace {
+ static bool in_bounds(size_t ofs, size_t size, size_t max_size) {
+ if( !(ofs < max_size) )
+ return false;
+ if( !(size <= max_size) )
+ return false;
+ return ofs + size <= max_size;
+ }
+
+ void set_bit(uint8_t* p, size_t i, bool v)
+ {
+ if(v) {
+ p[i/8] |= 1 << (i%8);
+ }
+ else {
+ p[i/8] &= ~(1 << (i%8));
+ }
+ }
+ bool get_bit(const uint8_t* p, size_t i) {
+ return (p[i/8] & (1 << (i%8))) != 0;
+ }
+ void copy_bits(uint8_t* dst, size_t dst_ofs, const uint8_t* src, size_t src_ofs, size_t len)
+ {
+ // Even number of bytes, fast copy
+ if( dst_ofs % 8 == 0 && src_ofs % 8 == 0 && len % 8 == 0 )
+ {
+ for(size_t i = 0; i < len/8; i ++)
+ {
+ dst[dst_ofs/8 + i] = src[src_ofs/8 + i];
+ }
+ }
+ else
+ {
+ for(size_t i = 0; i < len; i ++)
+ {
+ set_bit( dst, dst_ofs+i, get_bit(src, src_ofs+i) );
+ }
+ }
+ }
+};
+
+::std::ostream& operator<<(::std::ostream& os, const Allocation* x)
+{
+ os << "A(#" << x->m_index << " " << x->tag() /*<< " +" << x->size()*/ << ")";
+ return os;
+}
+
+FfiLayout FfiLayout::new_const_bytes(size_t s)
+{
+ return FfiLayout {
+ { Range {s, true, false} }
+ };
+}
+bool FfiLayout::is_valid_read(size_t o, size_t s) const
+{
+ for(const auto& r : ranges)
+ {
+ if( o < r.len ) {
+ if( !r.is_valid )
+ return false;
+ if( o + s <= r.len )
+ {
+ s = 0;
+ break;
+ }
+ s -= (r.len - o);
+ o = 0;
+ }
+ else {
+ o -= r.len;
+ }
+ }
+ if( s > 0 )
+ {
+ return false;
+ }
+ return true;
+}
+
+uint64_t Allocation::s_next_index = 0;
+
+AllocationHandle Allocation::new_alloc(size_t size, ::std::string tag)
{
Allocation* rv = new Allocation();
+ rv->m_index = s_next_index++;
+ rv->m_tag = ::std::move(tag);
rv->refcount = 1;
- rv->data.resize( (size + 8-1) / 8 ); // QWORDS
- rv->mask.resize( (size + 8-1) / 8 ); // bitmap bytes
+ rv->m_size = size;
+ rv->m_data.resize( (size + 8-1) / 8 ); // QWORDS
+ rv->m_mask.resize( (size + 8-1) / 8 ); // bitmap bytes
//LOG_DEBUG(rv << " ALLOC");
+ LOG_DEBUG(rv);
return AllocationHandle(rv);
}
AllocationHandle::AllocationHandle(const AllocationHandle& x):
@@ -167,7 +252,7 @@ size_t RelocationPtr::get_size() const
os << "\"" << x.str() << "\"";
break;
case RelocationPtr::Ty::FfiPointer:
- os << "FFI " << x.ffi().source_function << " " << x.ffi().ptr_value;
+ os << "FFI '" << x.ffi().tag_name << "' " << x.ffi().ptr_value;
break;
}
}
@@ -191,6 +276,8 @@ void ValueCommonWrite::write_usize(size_t ofs, uint64_t v)
void* ValueCommonRead::read_pointer_unsafe(size_t rd_ofs, size_t req_valid, size_t& out_size, bool& out_is_mut) const
{
auto ofs = read_usize(rd_ofs);
+ LOG_ASSERT(ofs >= Allocation::PTR_BASE, "Deref of invalid pointer");
+ ofs -= Allocation::PTR_BASE;
auto reloc = get_relocation(rd_ofs);
if( !reloc )
{
@@ -210,22 +297,16 @@ void* ValueCommonRead::read_pointer_unsafe(size_t rd_ofs, size_t req_valid, size
{
case RelocationPtr::Ty::Allocation: {
auto& a = reloc.alloc();
- if( ofs > a.size() )
- LOG_FATAL("Out-of-bounds pointer");
- if( ofs + req_valid > a.size() )
- LOG_FATAL("Out-of-bounds pointer (" << ofs << " + " << req_valid << " > " << a.size());
- a.check_bytes_valid( static_cast<size_t>(ofs), req_valid );
- out_size = a.size() - static_cast<size_t>(ofs);
+ LOG_ASSERT(in_bounds(ofs, req_valid, a.size()), "Out-of-bounds pointer (" << ofs << " + " << req_valid << " > " << a.size() << ")");
+ a.check_bytes_valid( ofs, req_valid );
+ out_size = a.size() - ofs;
out_is_mut = true;
return a.data_ptr() + ofs;
}
case RelocationPtr::Ty::StdString: {
const auto& s = reloc.str();
- if( ofs > s.size() )
- LOG_FATAL("Out-of-bounds pointer");
- if( ofs + req_valid > s.size() )
- LOG_FATAL("Out-of-bounds pointer (" << ofs << " + " << req_valid << " > " << s.size());
- out_size = s.size() - static_cast<size_t>(ofs);
+ LOG_ASSERT(in_bounds(ofs, req_valid, s.size()), "Out-of-bounds pointer (" << ofs << " + " << req_valid << " > " << s.size() << ")");
+ out_size = s.size() - ofs;
out_is_mut = false;
return const_cast<void*>( static_cast<const void*>(s.data() + ofs) );
}
@@ -233,11 +314,13 @@ void* ValueCommonRead::read_pointer_unsafe(size_t rd_ofs, size_t req_valid, size
LOG_FATAL("read_pointer w/ function");
case RelocationPtr::Ty::FfiPointer: {
const auto& f = reloc.ffi();
+ size_t size = f.get_size();
+ LOG_ASSERT(in_bounds(ofs, req_valid, size), "Out-of-bounds pointer (" << ofs << " + " << req_valid << " > " << size << ")");
// TODO: Validity?
//if( req_valid )
// LOG_FATAL("Can't request valid data from a FFI pointer");
// TODO: Have an idea of mutability and available size from FFI
- out_size = f.size - static_cast<size_t>(ofs);
+ out_size = size - ofs;
out_is_mut = false;
return reinterpret_cast<char*>(reloc.ffi().ptr_value) + ofs;
}
@@ -248,17 +331,38 @@ void* ValueCommonRead::read_pointer_unsafe(size_t rd_ofs, size_t req_valid, size
ValueRef ValueCommonRead::read_pointer_valref_mut(size_t rd_ofs, size_t size)
{
auto ofs = read_usize(rd_ofs);
+ LOG_ASSERT(ofs >= Allocation::PTR_BASE, "Invalid pointer read");
+ ofs -= Allocation::PTR_BASE;
auto reloc = get_relocation(rd_ofs);
+ LOG_DEBUG("ValueCommonRead::read_pointer_valref_mut(" << ofs << "+" << size << ", reloc=" << reloc << ")");
if( !reloc )
{
LOG_ERROR("Getting ValRef to null pointer (no relocation)");
}
else
{
- // TODO: Validate size
- return ValueRef(reloc, static_cast<size_t>(ofs), size);
+ // Validate size and offset are in bounds
+ switch(reloc.get_ty())
+ {
+ case RelocationPtr::Ty::Allocation:
+ LOG_ASSERT( in_bounds(ofs, size, reloc.alloc().size()), "Deref with OOB size - " << ofs << "+" << size << " > " << reloc.alloc().size() );
+ break;
+ case RelocationPtr::Ty::StdString:
+ LOG_ASSERT( in_bounds(ofs, size, reloc.str().size()), "Deref with OOB size - " << ofs << "+" << size << " > " << reloc.str().size() );
+ break;
+ case RelocationPtr::Ty::Function:
+ LOG_FATAL("read_pointer_valref_mut w/ function");
+ case RelocationPtr::Ty::FfiPointer:
+ LOG_ASSERT( in_bounds(ofs, size, reloc.ffi().get_size()), "Deref with OOB size - " << ofs << "+" << size << " > " << reloc.ffi().get_size() );
+ break;
+ }
+ return ValueRef(reloc, ofs, size);
}
}
+ValueRef ValueCommonRead::deref(size_t ofs, const ::HIR::TypeRef& ty)
+{
+ return read_pointer_valref_mut(ofs, ty.get_size());
+}
void Allocation::resize(size_t new_size)
@@ -268,18 +372,19 @@ 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 );
+ this->m_size = new_size;
+ this->m_data.resize( (new_size + 8-1) / 8 );
+ this->m_mask.resize( (new_size + 8-1) / 8 );
}
void Allocation::check_bytes_valid(size_t ofs, size_t size) const
{
- if( !(ofs + size <= this->size()) ) {
+ if( !in_bounds(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))) )
+ if( !(this->m_mask[i/8] & (1 << (i%8))) )
{
LOG_ERROR("Invalid bytes in value - " << ofs << "+" << size << " - " << *this);
throw "ERROR";
@@ -288,19 +393,20 @@ void Allocation::check_bytes_valid(size_t ofs, size_t size) const
}
void Allocation::mark_bytes_valid(size_t ofs, size_t size)
{
- assert( ofs+size <= this->mask.size() * 8 );
+ assert( ofs+size <= this->m_mask.size() * 8 );
for(size_t i = ofs; i < ofs + size; i++)
{
- this->mask[i/8] |= (1 << (i%8));
+ this->m_mask[i/8] |= (1 << (i%8));
}
}
Value Allocation::read_value(size_t ofs, size_t size) const
{
Value rv;
- TRACE_FUNCTION_R("Allocation::read_value " << this << " " << ofs << "+" << size, *this << " | " << rv);
+ //TRACE_FUNCTION_R("Allocation::read_value " << this << " " << ofs << "+" << size, *this << " | " << size << "=" << rv);
if( this->is_freed )
LOG_ERROR("Use of freed memory " << this);
LOG_DEBUG(*this);
+ LOG_ASSERT( in_bounds(ofs, size, this->size()), "Read out of bounds (" << ofs << "+" << size << " > " << this->size() << ")" );
// Determine if this can become an inline allocation.
bool has_reloc = false;
@@ -308,57 +414,25 @@ Value Allocation::read_value(size_t ofs, size_t size) const
{
if( ofs <= r.slot_ofs && r.slot_ofs < ofs + size )
{
+ // NOTE: A relocation at offset zero is allowed
+ if( r.slot_ofs == ofs )
+ continue ;
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->relocations.push_back({ r.slot_ofs - ofs, r.backing_alloc });
- }
- }
+ rv = Value::with_size(size, has_reloc);
+ rv.write_bytes(0, this->data_ptr() + ofs, size);
- // Copy the mask bits
- for(size_t i = 0; i < size; i ++)
- {
- size_t j = ofs + i;
- const uint8_t test_mask = (1 << (j%8));
- const uint8_t set_mask = (1 << (i%8));
- bool v = (this->mask[j/8] & test_mask) != 0;
- if( v )
- {
- rv.allocation->mask[i/8] |= set_mask;
- }
- }
- }
- else
+ for(const auto& r : this->relocations)
{
- rv.direct_data.size = static_cast<uint8_t>(size);
-
- rv.write_bytes(0, this->data_ptr() + ofs, size);
- rv.direct_data.mask[0] = 0;
- rv.direct_data.mask[1] = 0;
-
- // Copy the mask bits
- for(size_t i = 0; i < size; i ++)
+ if( ofs <= r.slot_ofs && r.slot_ofs < ofs + size )
{
- size_t j = ofs + i;
- const uint8_t tst_mask = 1 << (j%8);
- const uint8_t set_mask = 1 << (i%8);
- bool v = (this->mask[j/8] & tst_mask) != 0;
- if( v )
- {
- rv.direct_data.mask[i/8] |= set_mask;
- }
+ rv.set_reloc(r.slot_ofs - ofs, /*r.size*/POINTER_SIZE, r.backing_alloc);
}
}
+ // Copy the mask bits
+ copy_bits(rv.get_mask_mut(), 0, m_mask.data(), ofs, size);
+
return rv;
}
void Allocation::read_bytes(size_t ofs, void* dst, size_t count) const
@@ -366,19 +440,11 @@ void Allocation::read_bytes(size_t ofs, void* dst, size_t count) const
if( this->is_freed )
LOG_ERROR("Use of freed memory " << this);
- LOG_DEBUG("Allocation::read_bytes " << this << " " << ofs << "+" << count);
+ //LOG_DEBUG("Allocation::read_bytes " << this << " " << ofs << "+" << count);
if(count == 0)
return ;
- if(ofs >= this->size() ) {
- LOG_ERROR("Out of bounds read, " << ofs << "+" << count << " > " << this->size());
- throw "ERROR";
- }
- if(count > this->size() ) {
- LOG_ERROR("Out of bounds read, " << ofs << "+" << count << " > " << this->size());
- throw "ERROR";
- }
- if(ofs+count > this->size() ) {
+ if( !in_bounds(ofs, count, this->size()) ) {
LOG_ERROR("Out of bounds read, " << ofs << "+" << count << " > " << this->size());
throw "ERROR";
}
@@ -394,14 +460,15 @@ void Allocation::write_value(size_t ofs, Value v)
LOG_ERROR("Use of freed memory " << this);
//if( this->is_read_only )
// LOG_ERROR("Writing to read-only allocation " << this);
- if( v.allocation )
+ if( v.m_inner.is_alloc )
{
- size_t v_size = v.allocation->size();
- const auto& src_alloc = *v.allocation;
- // Take a copy of the source mask
- auto s_mask = src_alloc.mask;
+ const auto& src_alloc = *v.m_inner.alloc.alloc;
+ size_t v_size = src_alloc.size();
+ assert(&src_alloc != this); // Shouldn't happen?
- // Save relocations first, because `Foo = Foo` is valid.
+ // Take a copy of the source mask
+ auto s_mask = src_alloc.m_mask;
+ // 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);
@@ -420,39 +487,16 @@ void Allocation::write_value(size_t ofs, Value v)
}
// Set mask in destination
- if( ofs % 8 != 0 || v_size % 8 != 0 )
- {
- // Lazy way, sets/clears individual bits
- for(size_t i = 0; i < v_size; i ++)
- {
- uint8_t dbit = 1 << ((ofs+i) % 8);
- if( s_mask[i/8] & (1 << (i %8)) )
- this->mask[ (ofs+i) / 8 ] |= dbit;
- else
- this->mask[ (ofs+i) / 8 ] &= ~dbit;
- }
- }
- else
- {
- // Copy the mask bytes directly
- for(size_t i = 0; i < v_size / 8; i ++)
- {
- this->mask[ofs/8+i] = s_mask[i];
- }
- }
+ copy_bits(m_mask.data(), ofs, s_mask.data(), 0, v_size);
}
else
{
- this->write_bytes(ofs, v.direct_data.data, v.direct_data.size);
-
- // Lazy way, sets/clears individual bits
- for(size_t i = 0; i < v.direct_data.size; i ++)
+ this->write_bytes(ofs, v.data_ptr(), v.size());
+ copy_bits(m_mask.data(), ofs, v.get_mask(), 0, v.size());
+ // TODO: Copy relocation
+ if( v.m_inner.direct.reloc_0 )
{
- uint8_t dbit = 1 << ((ofs+i) % 8);
- if( v.direct_data.mask[i/8] & (1 << (i %8)) )
- this->mask[ (ofs+i) / 8 ] |= dbit;
- else
- this->mask[ (ofs+i) / 8 ] &= ~dbit;
+ this->set_reloc(ofs, POINTER_SIZE, ::std::move(v.m_inner.direct.reloc_0));
}
}
}
@@ -466,16 +510,8 @@ void Allocation::write_bytes(size_t ofs, const void* src, size_t count)
if(count == 0)
return ;
- TRACE_FUNCTION_R("Allocation::write_bytes " << this << " " << ofs << "+" << count, *this);
- if(ofs >= this->size() ) {
- LOG_ERROR("Out of bounds write, " << ofs << "+" << count << " > " << this->size());
- throw "ERROR";
- }
- if(count > this->size() ) {
- LOG_ERROR("Out of bounds write, " << ofs << "+" << count << " > " << this->size());
- throw "ERROR";
- }
- if(ofs+count > this->size() ) {
+ //TRACE_FUNCTION_R("Allocation::write_bytes " << this << " " << ofs << "+" << count, *this);
+ if( !in_bounds(ofs, count, this->size()) ) {
LOG_ERROR("Out of bounds write, " << ofs << "+" << count << " > " << this->size());
throw "ERROR";
}
@@ -501,8 +537,29 @@ void Allocation::write_bytes(size_t ofs, const void* src, size_t count)
}
void Allocation::write_ptr(size_t ofs, size_t ptr_ofs, RelocationPtr reloc)
{
+ LOG_ASSERT(ptr_ofs >= Allocation::PTR_BASE, "Invalid pointer being written");
this->write_usize(ofs, ptr_ofs);
- this->relocations.push_back(Relocation { ofs, /*POINTER_SIZE,*/ ::std::move(reloc) });
+ this->set_reloc(ofs, POINTER_SIZE, ::std::move(reloc));
+}
+void Allocation::set_reloc(size_t ofs, size_t len, RelocationPtr reloc)
+{
+ LOG_ASSERT(ofs % POINTER_SIZE == 0, "");
+ LOG_ASSERT(len == POINTER_SIZE, "");
+ // Delete any existing relocation at this position
+ for(auto it = this->relocations.begin(); it != this->relocations.end();)
+ {
+ if( ofs <= it->slot_ofs && it->slot_ofs < ofs + len )
+ {
+ // Slot starts in this updated region
+ // - TODO: Split in half?
+ it = this->relocations.erase(it);
+ continue ;
+ }
+ // TODO: What if the slot ends in the new region?
+ // What if the new region is in the middle of the slot
+ ++ it;
+ }
+ this->relocations.push_back(Relocation { ofs, /*len,*/ ::std::move(reloc) });
}
::std::ostream& operator<<(::std::ostream& os, const Allocation& x)
{
@@ -513,7 +570,7 @@ void Allocation::write_ptr(size_t ofs, size_t ptr_ofs, RelocationPtr reloc)
if( i != 0 )
os << " ";
- if( x.mask[i/8] & (1 << (i%8)) )
+ if( x.m_mask[i/8] & (1 << (i%8)) )
{
os << ::std::setw(2) << ::std::setfill('0') << (int)x.data_ptr()[i];
}
@@ -538,72 +595,62 @@ void Allocation::write_ptr(size_t ofs, size_t ptr_ofs, RelocationPtr reloc)
Value::Value()
{
- this->direct_data.size = 0;
- this->direct_data.mask[0] = 0;
- this->direct_data.mask[1] = 0;
+ memset(&m_inner, 0, sizeof(m_inner));
}
Value::Value(::HIR::TypeRef ty)
{
size_t size = ty.get_size();
// 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->direct_data.data) )
+ if( size <= sizeof(m_inner.direct.data) )
{
// AND the type doesn't contain a pointer (of any kind)
- if( ! ty.has_pointer() )
+ // TODO: Pointers _are_ allowed now (but only one)
+ if( true || ! ty.has_pointer() )
{
// Will fit in a inline allocation, nice.
//LOG_TRACE("No pointers in " << ty << ", storing inline");
- this->direct_data.size = static_cast<uint8_t>(size);
- this->direct_data.mask[0] = 0;
- this->direct_data.mask[1] = 0;
+ new(&m_inner.direct) Inner::Direct(size);
return ;
}
}
// Fallback: Make a new allocation
//LOG_TRACE(" Creating allocation for " << ty);
- this->allocation = Allocation::new_alloc(size);
+ new(&m_inner.alloc) Inner::Alloc( Allocation::new_alloc(size, FMT_STRING(ty)) );
+ assert(m_inner.is_alloc);
}
Value Value::with_size(size_t size, bool have_allocation)
{
Value rv;
- if(have_allocation)
+ if(have_allocation || size > sizeof(m_inner.direct.data))
{
- rv.allocation = Allocation::new_alloc(size);
+ new(&rv.m_inner.alloc) Inner::Alloc( Allocation::new_alloc(size, FMT_STRING("with_size(" << size << ")")) );
}
else
{
- rv.direct_data.size = static_cast<uint8_t>(size);
- rv.direct_data.mask[0] = 0;
- rv.direct_data.mask[1] = 0;
+ new(&rv.m_inner.direct) Inner::Direct(size);
}
return rv;
}
Value Value::new_fnptr(const ::HIR::Path& fn_path)
{
Value rv( ::HIR::TypeRef(::HIR::CoreType { RawType::Function }) );
- assert(rv.allocation);
- rv.allocation->relocations.push_back(Relocation { 0, RelocationPtr::new_fcn(fn_path) });
- rv.allocation->data.at(0) = 0;
- rv.allocation->mask.at(0) = (1 << POINTER_SIZE)-1;
+ rv.write_ptr(0, Allocation::PTR_BASE, RelocationPtr::new_fcn(fn_path));
return rv;
}
Value Value::new_ffiptr(FFIPointer ffi)
{
Value rv( ::HIR::TypeRef(::HIR::CoreType { RawType::USize }) );
- rv.create_allocation();
- rv.allocation->relocations.push_back(Relocation { 0, RelocationPtr::new_ffi(ffi) });
- rv.allocation->data.at(0) = 0;
- rv.allocation->mask.at(0) = (1 << POINTER_SIZE)-1;
+ assert( !rv.m_inner.is_alloc );
+ rv.write_ptr(0, Allocation::PTR_BASE, RelocationPtr::new_ffi(ffi));
return rv;
}
Value Value::new_pointer(::HIR::TypeRef ty, uint64_t v, RelocationPtr r) {
assert(ty.get_wrapper());
assert(ty.get_wrapper()->type == TypeWrapper::Ty::Borrow || ty.get_wrapper()->type == TypeWrapper::Ty::Pointer);
Value rv(ty);
- rv.write_usize(0, v);
- rv.allocation->relocations.push_back(Relocation { 0, /*POINTER_SIZE,*/ ::std::move(r) });
+ rv.write_ptr(0, v, ::std::move(r));
return rv;
}
Value Value::new_usize(uint64_t v) {
@@ -626,59 +673,57 @@ Value Value::new_i32(int32_t v) {
rv.write_i32(0, v);
return rv;
}
+Value Value::new_i64(int64_t v) {
+ auto rv = Value( ::HIR::TypeRef(RawType::I64) );
+ rv.write_i64(0, v);
+ return rv;
+}
void Value::create_allocation()
{
- assert(!this->allocation);
- this->allocation = Allocation::new_alloc(this->direct_data.size);
- if( this->direct_data.size > 0 )
- this->allocation->mask[0] = this->direct_data.mask[0];
- if( this->direct_data.size > 8 )
- this->allocation->mask[1] = this->direct_data.mask[1];
- ::std::memcpy(this->allocation->data.data(), this->direct_data.data, this->direct_data.size);
+ assert(!m_inner.is_alloc);
+ auto new_alloc = Allocation::new_alloc(m_inner.direct.size, "create_allocation"); // TODO: Provide a better name?
+ auto& direct = m_inner.direct;
+ if( direct.size > 0 )
+ new_alloc->m_mask[0] = direct.mask[0];
+ if( direct.size > 8 )
+ new_alloc->m_mask[1] = direct.mask[1];
+ ::std::memcpy(new_alloc->data_ptr(), direct.data, direct.size);
+ if( direct.reloc_0 )
+ {
+ new_alloc->set_reloc(0, POINTER_SIZE, ::std::move(direct.reloc_0));
+ }
+
+ new(&m_inner.alloc) Inner::Alloc(::std::move(new_alloc));
}
void Value::check_bytes_valid(size_t ofs, size_t size) const
{
if( size == 0 )
return ;
- if( this->allocation )
- {
- this->allocation->check_bytes_valid(ofs, size);
+ if( !in_bounds(ofs, size, this->size()) ) {
+ LOG_ERROR("Read out of bounds " << ofs+size << " >= " << this->size());
+ throw "ERROR";
}
- else
+ const auto* mask = this->get_mask();
+ for(size_t i = ofs; i < ofs + size; i++)
{
- if( size == 0 && this->direct_data.size > 0 ) {
- return ;
- }
- 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->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( !get_bit(mask, i) )
{
- if( !(this->direct_data.mask[i/8] & (1 << i%8)) )
- {
- LOG_ERROR("Accessing invalid bytes in value");
- throw "ERROR";
- }
+ LOG_ERROR("Accessing invalid bytes in value, offset " << i << " of " << *this);
}
}
}
void Value::mark_bytes_valid(size_t ofs, size_t size)
{
- if( this->allocation )
+ if( m_inner.is_alloc )
{
- this->allocation->mark_bytes_valid(ofs, size);
+ m_inner.alloc.alloc->mark_bytes_valid(ofs, size);
}
else
{
for(size_t i = ofs; i < ofs+size; i++)
{
- this->direct_data.mask[i/8] |= (1 << i%8);
+ m_inner.direct.mask[i/8] |= (1 << i%8);
}
}
}
@@ -686,18 +731,28 @@ void Value::mark_bytes_valid(size_t ofs, size_t size)
Value Value::read_value(size_t ofs, size_t size) const
{
Value rv;
- //TRACE_FUNCTION_R(ofs << ", " << size << ") - " << *this, rv);
- if( this->allocation )
+ TRACE_FUNCTION_R(ofs << ", " << size << " - " << *this, rv);
+ if( m_inner.is_alloc )
{
- rv = this->allocation->read_value(ofs, size);
+ rv = m_inner.alloc.alloc->read_value(ofs, size);
}
else
{
- // Inline can become inline.
- rv.direct_data.size = static_cast<uint8_t>(size);
- rv.write_bytes(0, this->direct_data.data+ofs, size);
- rv.direct_data.mask[0] = this->direct_data.mask[0];
- rv.direct_data.mask[1] = this->direct_data.mask[1];
+ // Inline always fits in inline.
+ if( ofs == 0 && size == this->size() )
+ {
+ rv = Value(*this);
+ }
+ else
+ {
+ rv.m_inner.direct.size = static_cast<uint8_t>(size);
+ memcpy(rv.m_inner.direct.data, this->data_ptr() + ofs, size);
+ copy_bits(rv.m_inner.direct.mask, 0, this->get_mask(), ofs, size);
+ if( ofs == 0 )
+ {
+ rv.m_inner.direct.reloc_0 = RelocationPtr(m_inner.direct.reloc_0);
+ }
+ }
}
return rv;
}
@@ -705,27 +760,14 @@ void Value::read_bytes(size_t ofs, void* dst, size_t count) const
{
if(count == 0)
return ;
- if( this->allocation )
+ if( m_inner.is_alloc )
{
- this->allocation->read_bytes(ofs, dst, count);
+ m_inner.alloc.alloc->read_bytes(ofs, dst, count);
}
else
{
check_bytes_valid(ofs, count);
-
- if(ofs >= this->direct_data.size ) {
- LOG_ERROR("Out of bounds read, " << ofs << "+" << count << " > " << this->size());
- throw "ERROR";
- }
- if(count > this->direct_data.size ) {
- LOG_ERROR("Out of bounds read, " << ofs << "+" << count << " > " << this->size());
- throw "ERROR";
- }
- if(ofs+count > this->direct_data.size ) {
- LOG_ERROR("Out of bounds read, " << ofs << "+" << count << " > " << this->size());
- throw "ERROR";
- }
- ::std::memcpy(dst, this->direct_data.data + ofs, count);
+ ::std::memcpy(dst, m_inner.direct.data + ofs, count);
}
}
@@ -733,88 +775,65 @@ void Value::write_bytes(size_t ofs, const void* src, size_t count)
{
if( count == 0 )
return ;
- if( this->allocation )
+ if( m_inner.is_alloc )
{
- this->allocation->write_bytes(ofs, src, count);
+ m_inner.alloc.alloc->write_bytes(ofs, src, count);
}
else
{
- if(ofs >= this->direct_data.size ) {
- LOG_BUG("Write to offset outside value size (" << ofs << "+" << count << " >= " << (int)this->direct_data.size << ")");
+ auto& direct = m_inner.direct;
+ if( !in_bounds(ofs, count, direct.size) ) {
+ LOG_ERROR("Write extends outside value size (" << ofs << "+" << count << " >= " << (int)direct.size << ")");
}
- if(count > this->direct_data.size ){
- LOG_BUG("Write larger than value size (" << ofs << "+" << count << " >= " << (int)this->direct_data.size << ")");
- }
- if(ofs+count > this->direct_data.size ) {
- LOG_BUG("Write extends outside value size (" << ofs << "+" << count << " >= " << (int)this->direct_data.size << ")");
- }
- ::std::memcpy(this->direct_data.data + ofs, src, count);
+ ::std::memcpy(direct.data + ofs, src, count);
mark_bytes_valid(ofs, count);
+ if( 0 <= ofs && ofs < POINTER_SIZE ) {
+ direct.reloc_0 = RelocationPtr();
+ }
}
}
void Value::write_value(size_t ofs, Value v)
{
- if( this->allocation )
+ if( m_inner.is_alloc )
{
- this->allocation->write_value(ofs, ::std::move(v));
+ m_inner.alloc.alloc->write_value(ofs, ::std::move(v));
}
else
{
- if( v.allocation && !v.allocation->relocations.empty() )
- {
- this->create_allocation();
- this->allocation->write_value(ofs, ::std::move(v));
- }
- else
- {
- write_bytes(ofs, v.direct_data.data, v.direct_data.size);
+ write_bytes(ofs, v.data_ptr(), v.size());
+ // - Copy mask
+ copy_bits(this->get_mask_mut(), ofs, v.get_mask(), 0, v.size());
- // Lazy way, sets/clears individual bits
- for(size_t i = 0; i < v.direct_data.size; i ++)
+ // TODO: Faster way of knowing where there are relocations
+ for(size_t i = 0; i < v.size(); i ++)
+ {
+ if( auto r = v.get_relocation(i) )
{
- uint8_t dbit = 1 << ((ofs+i) % 8);
- if( v.direct_data.mask[i/8] & (1 << (i %8)) )
- this->direct_data.mask[ (ofs+i) / 8 ] |= dbit;
- else
- this->direct_data.mask[ (ofs+i) / 8 ] &= ~dbit;
+ this->set_reloc(ofs + i, POINTER_SIZE, r);
}
}
}
}
void Value::write_ptr(size_t ofs, size_t ptr_ofs, RelocationPtr reloc)
{
- if( !this->allocation )
- {
- LOG_ERROR("Writing a pointer with no allocation");
- }
- this->allocation->write_ptr(ofs, ptr_ofs, ::std::move(reloc));
-}
-
-::std::ostream& operator<<(::std::ostream& os, const Value& v)
-{
- if( v.allocation )
+ if( m_inner.is_alloc )
{
- os << *v.allocation;
+ m_inner.alloc.alloc->write_ptr(ofs, ptr_ofs, ::std::move(reloc));
}
else
{
- auto flags = os.flags();
- os << ::std::hex;
- for(size_t i = 0; i < v.direct_data.size; i++)
+ write_usize(ofs, ptr_ofs);
+ if( ofs != 0 )
{
- if( i != 0 )
- os << " ";
- if( v.direct_data.mask[i/8] & (1 << i%8) )
- {
- os << ::std::setw(2) << ::std::setfill('0') << (int)v.direct_data.data[i];
- }
- else
- {
- os << "--";
- }
+ LOG_ERROR("Writing a pointer with no allocation");
}
- os.setf(flags);
+ m_inner.direct.reloc_0 = ::std::move(reloc);
}
+}
+
+::std::ostream& operator<<(::std::ostream& os, const Value& v)
+{
+ os << ValueRef(const_cast<Value&>(v), 0, v.size());
return os;
}
extern ::std::ostream& operator<<(::std::ostream& os, const ValueRef& v)
@@ -830,6 +849,8 @@ extern ::std::ostream& operator<<(::std::ostream& os, const ValueRef& v)
case RelocationPtr::Ty::Allocation: {
const auto& alloc = alloc_ptr.alloc();
+ os << &alloc << "@" << v.m_offset << "+" << v.m_size << " ";
+
auto flags = os.flags();
os << ::std::hex;
for(size_t i = v.m_offset; i < ::std::min(alloc.size(), v.m_offset + v.m_size); i++)
@@ -837,7 +858,7 @@ extern ::std::ostream& operator<<(::std::ostream& os, const ValueRef& v)
if( i != 0 )
os << " ";
- if( alloc.mask[i/8] & (1 << i%8) )
+ if( alloc.m_mask[i/8] & (1 << i%8) )
{
os << ::std::setw(2) << ::std::setfill('0') << (int)alloc.data_ptr()[i];
}
@@ -863,9 +884,7 @@ extern ::std::ostream& operator<<(::std::ostream& os, const ValueRef& v)
break;
case RelocationPtr::Ty::StdString: {
const auto& s = alloc_ptr.str();
- assert(v.m_offset < s.size());
- assert(v.m_size < s.size());
- assert(v.m_offset + v.m_size <= s.size());
+ assert( in_bounds(v.m_offset, v.m_size, s.size()) );
auto flags = os.flags();
os << ::std::hex;
for(size_t i = v.m_offset; i < v.m_offset + v.m_size; i++)
@@ -879,9 +898,11 @@ extern ::std::ostream& operator<<(::std::ostream& os, const ValueRef& v)
break;
}
}
- else if( v.m_value && v.m_value->allocation )
+ else if( v.m_value && v.m_value->m_inner.is_alloc )
{
- const auto& alloc = *v.m_value->allocation;
+ const auto& alloc = *v.m_value->m_inner.alloc.alloc;
+
+ os << &alloc << "@" << v.m_offset << "+" << v.m_size << " ";
auto flags = os.flags();
os << ::std::hex;
@@ -890,7 +911,7 @@ extern ::std::ostream& operator<<(::std::ostream& os, const ValueRef& v)
if( i != 0 )
os << " ";
- if( alloc.mask[i/8] & (1 << i%8) )
+ if( alloc.m_mask[i/8] & (1 << i%8) )
{
os << ::std::setw(2) << ::std::setfill('0') << (int)alloc.data_ptr()[i];
}
@@ -913,7 +934,7 @@ extern ::std::ostream& operator<<(::std::ostream& os, const ValueRef& v)
}
else if( v.m_value )
{
- const auto& direct = v.m_value->direct_data;
+ const auto& direct = v.m_value->m_inner.direct;
auto flags = os.flags();
os << ::std::hex;
@@ -931,6 +952,10 @@ extern ::std::ostream& operator<<(::std::ostream& os, const ValueRef& v)
}
}
os.setf(flags);
+ if(direct.reloc_0)
+ {
+ os << " { " << direct.reloc_0 << " }";
+ }
}
else
{
@@ -939,11 +964,28 @@ extern ::std::ostream& operator<<(::std::ostream& os, const ValueRef& v)
return os;
}
+void ValueRef::mark_bytes_valid(size_t ofs, size_t size)
+{
+ if( m_alloc ) {
+ switch(m_alloc.get_ty())
+ {
+ case RelocationPtr::Ty::Allocation:
+ m_alloc.alloc().mark_bytes_valid(m_offset + ofs, size);
+ break;
+ default:
+ LOG_TODO("mark_valid in " << m_alloc);
+ }
+ }
+ else {
+ m_value->mark_bytes_valid(m_offset + ofs, size);
+ }
+}
+
Value ValueRef::read_value(size_t ofs, size_t size) const
{
if( size == 0 )
return Value();
- if( !(ofs < m_size && size <= m_size && ofs + size <= m_size) ) {
+ if( !in_bounds(ofs, size, m_size) ) {
LOG_ERROR("Read exceeds bounds, " << ofs << " + " << size << " > " << m_size << " - from " << *this);
}
if( m_alloc ) {
@@ -952,25 +994,27 @@ Value ValueRef::read_value(size_t ofs, size_t size) const
case RelocationPtr::Ty::Allocation:
return m_alloc.alloc().read_value(m_offset + ofs, size);
case RelocationPtr::Ty::StdString: {
+ LOG_ASSERT(in_bounds(m_offset + ofs, size, m_alloc.str().size()), "");
auto rv = Value::with_size(size, false);
- //ASSERT_BUG(ofs <= m_alloc.str().size(), "");
- //ASSERT_BUG(size <= m_alloc.str().size(), "");
- //ASSERT_BUG(ofs+size <= m_alloc.str().size(), "");
- assert(m_offset+ofs <= m_alloc.str().size() && size <= m_alloc.str().size() && m_offset+ofs+size <= m_alloc.str().size());
rv.write_bytes(0, m_alloc.str().data() + m_offset + ofs, size);
return rv;
}
+ case RelocationPtr::Ty::FfiPointer: {
+ LOG_ASSERT(in_bounds(m_offset + ofs, size, m_alloc.ffi().get_size()), "");
+ auto rv = Value::with_size(size, false);
+ rv.write_bytes(0, reinterpret_cast<const char*>(m_alloc.ffi().ptr_value) + m_offset + ofs, size);
+ return rv;
+ }
default:
- //ASSERT_BUG(m_alloc.is_alloc(), "read_value on non-data backed Value - " << );
- throw "TODO";
+ LOG_TODO("read_value from " << m_alloc);
}
}
else {
return m_value->read_value(m_offset + ofs, size);
}
}
-bool ValueRef::compare(const void* other, size_t other_len) const
+bool ValueRef::compare(size_t offset, const void* other, size_t other_len) const
{
- check_bytes_valid(0, other_len);
- return ::std::memcmp(data_ptr(), other, other_len) == 0;
+ check_bytes_valid(offset, other_len);
+ return ::std::memcmp(data_ptr() + offset, other, other_len) == 0;
}
diff --git a/tools/standalone_miri/value.hpp b/tools/standalone_miri/value.hpp
index b057b3c4..03a21970 100644
--- a/tools/standalone_miri/value.hpp
+++ b/tools/standalone_miri/value.hpp
@@ -13,6 +13,9 @@
#include <cstring> // memcpy
#include <cassert>
+#include "debug.hpp"
+#include "u128.hpp"
+
namespace HIR {
struct TypeRef;
struct Path;
@@ -21,11 +24,49 @@ class Allocation;
struct Value;
struct ValueRef;
+struct FfiLayout
+{
+ struct Range {
+ size_t len;
+ bool is_valid;
+ bool is_writable;
+ };
+ ::std::vector<Range> ranges;
+
+ static FfiLayout new_const_bytes(size_t s);
+
+ size_t get_size() const {
+ size_t rv = 0;
+ for(const auto& r : ranges)
+ rv += r.len;
+ return rv;
+ }
+ bool is_valid_read(size_t o, size_t s) const;
+};
struct FFIPointer
{
- const char* source_function;
+ // FFI pointers require the following:
+ // - A tag indicating where they're valid/from
+ // - A data format (e.g. size of allocation, internal data format)
+ // - If the data format is unspecified (null) then it's a void pointer
+ // - An actual pointer
+
+ // Pointer value, returned by the FFI
void* ptr_value;
- size_t size;
+ // Tag name, used for validty checking by FFI hooks
+ const char* tag_name;
+ ::std::shared_ptr<FfiLayout> layout;
+
+ static FFIPointer new_void(const char* name, const void* v) {
+ return FFIPointer { const_cast<void*>(v), name, ::std::make_shared<FfiLayout>() };
+ }
+ static FFIPointer new_const_bytes(const char* name, const void* s, size_t size) {
+ return FFIPointer { const_cast<void*>(s), name, ::std::make_shared<FfiLayout>(FfiLayout::new_const_bytes(size)) };
+ };
+
+ size_t get_size() const {
+ return (layout ? layout->get_size() : 0);
+ }
};
class AllocationHandle
@@ -83,7 +124,7 @@ public:
RelocationPtr(const RelocationPtr& x);
~RelocationPtr();
static RelocationPtr new_alloc(AllocationHandle h);
- static RelocationPtr new_fcn(::HIR::Path p);
+ static RelocationPtr new_fcn(::HIR::Path p); // TODO: What if it's a FFI function? Could be encoded in here.
static RelocationPtr new_string(const ::std::string* s); // NOTE: The string must have a stable pointer
static RelocationPtr new_ffi(FFIPointer info);
@@ -155,15 +196,20 @@ struct ValueCommonRead
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; }
+ U128 read_u128(size_t ofs) const { U128 rv; read_bytes(ofs, &rv, 16); 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)); }
+ I128 read_i128(size_t ofs) const { I128 rv; read_bytes(ofs, &rv, 16); return rv; }
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)); }
+ /// De-reference a pointer (of target type `ty`) at the given offset, and return a reference to it
+ ValueRef deref(size_t ofs, const ::HIR::TypeRef& ty);
+
/// Read a pointer from the value, requiring at least `req_valid` valid bytes, saves avaliable space in `size`
void* read_pointer_unsafe(size_t rd_ofs, size_t req_valid, size_t& size, bool& is_mut) const;
/// Read a pointer, requiring `req_len` valid bytes
@@ -177,8 +223,7 @@ struct ValueCommonRead
bool is_mut;
void* rv = read_pointer_unsafe(rd_ofs, 0, out_size, is_mut);
if(!is_mut)
- throw "";
- //LOG_FATAL("Attempting to get an uninit pointer to immutable data");
+ LOG_FATAL("Attempting to get an uninit pointer to immutable data");
return rv;
}
/// Read a pointer and return a ValueRef to it (mutable data)
@@ -193,10 +238,12 @@ struct ValueCommonWrite:
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_u128(size_t ofs, U128 v) { write_bytes(ofs, &v, 16); }
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_i128(size_t ofs, I128 v) { write_bytes(ofs, &v, 16); }
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);
@@ -208,19 +255,31 @@ class Allocation:
public ValueCommonWrite
{
friend class AllocationHandle;
+
+ static uint64_t s_next_index;
+
+ ::std::string m_tag;
size_t refcount;
+ size_t m_size;
+ uint64_t m_index;
// TODO: Read-only flag?
bool is_freed = false;
+
+ ::std::vector<uint64_t> m_data;
+public:
+ ::std::vector<uint8_t> m_mask;
+ ::std::vector<Relocation> relocations;
public:
- static AllocationHandle new_alloc(size_t size);
+ virtual ~Allocation() {}
+ static AllocationHandle new_alloc(size_t size, ::std::string tag);
- 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; }
+ // NOTE: This should match the value in the MMIR backend
+ static const size_t PTR_BASE = 0x1000;
- ::std::vector<uint64_t> data;
- ::std::vector<uint8_t> mask;
- ::std::vector<Relocation> relocations;
+ const uint8_t* data_ptr() const { return reinterpret_cast<const uint8_t*>(this->m_data.data()); }
+ uint8_t* data_ptr() { return reinterpret_cast< uint8_t*>(this->m_data.data()); }
+ size_t size() const { return m_size; }
+ const ::std::string& tag() const { return m_tag; }
RelocationPtr get_relocation(size_t ofs) const override {
for(const auto& r : relocations) {
@@ -232,7 +291,7 @@ public:
void mark_as_freed() {
is_freed = true;
relocations.clear();
- for(auto& v : mask)
+ for(auto& v : m_mask)
v = 0;
}
@@ -247,23 +306,95 @@ public:
void write_value(size_t ofs, Value v);
void write_bytes(size_t ofs, const void* src, size_t count) override;
void write_ptr(size_t ofs, size_t ptr_ofs, RelocationPtr reloc) override;
+
+ void set_reloc(size_t ofs, size_t len, RelocationPtr reloc);
+ friend ::std::ostream& operator<<(::std::ostream& os, const Allocation* x);
};
extern ::std::ostream& operator<<(::std::ostream& os, const Allocation& x);
+// TODO: Rename to be `StackSlot`
struct Value:
public ValueCommonWrite
{
- // If NULL, data is direct
- AllocationHandle allocation;
- struct {
- // NOTE: Can't pack the mask+size tighter, need 4 bits of size (8-15) leaving 12 bits of mask
- uint8_t data[2*8-3]; // 13 data bytes, plus 16bit mask, plus size = 16 bytes
- uint8_t mask[2];
- uint8_t size;
- } direct_data;
+//private:
+ union Inner {
+ bool is_alloc;
+ struct Alloc {
+ bool is_alloc;
+ AllocationHandle alloc;
+
+ Alloc(AllocationHandle alloc):
+ is_alloc(true),
+ alloc(::std::move(alloc))
+ {
+ }
+ } alloc;
+ // Sizeof = 4+8+8 = 20b (plus vtable?)
+ struct Direct {
+ bool is_alloc;
+ uint8_t size;
+ uint8_t mask[2];
+ uint8_t data[2*8];
+ RelocationPtr reloc_0; // Relocation only for 0+POINTER_SIZE
+
+ Direct(size_t size):
+ is_alloc(false),
+ size(static_cast<uint8_t>(size)),
+ mask{0,0}
+ {
+ }
+ } direct;
+
+ Inner():
+ direct(0)
+ {
+ }
+ ~Inner() {
+ if(is_alloc) {
+ alloc.~Alloc();
+ }
+ else {
+ direct.~Direct();
+ }
+ }
+ Inner(const Inner& x) {
+ if(x.is_alloc) {
+ new(&this->alloc) Alloc(x.alloc);
+ }
+ else {
+ new(&this->direct) Direct(x.direct);
+ }
+ }
+ Inner(Inner&& x) {
+ if(x.is_alloc) {
+ new(&this->alloc) Alloc(::std::move(x.alloc));
+ }
+ else {
+ new(&this->direct) Direct(::std::move(x.direct));
+ }
+ }
+ Inner& operator=(Inner&& x) {
+ this->~Inner();
+ new(this) Inner(x);
+ return *this;
+ }
+ Inner& operator=(const Inner& x) {
+ this->~Inner();
+ new(this) Inner(x);
+ return *this;
+ }
+ } m_inner;
+public:
Value();
Value(::HIR::TypeRef ty);
+ ~Value() {
+ }
+
+ Value(const Value&) = default;
+ Value(Value&&) = default;
+ Value& operator=(const Value& x) = delete;
+ Value& operator=(Value&& x) = default;
static Value with_size(size_t size, bool have_allocation);
static Value new_fnptr(const ::HIR::Path& fn_path);
@@ -273,17 +404,51 @@ struct Value:
static Value new_isize(int64_t v);
static Value new_u32(uint32_t v);
static Value new_i32(int32_t v);
+ static Value new_i64(int64_t v);
+ AllocationHandle borrow(::std::string loc) {
+ if( !m_inner.is_alloc )
+ create_allocation(/*loc*/);
+ return m_inner.alloc.alloc;
+ }
+ void ensure_allocation() {
+ if( !m_inner.is_alloc )
+ create_allocation();
+ }
void create_allocation();
- size_t size() const { return allocation ? allocation->size() : direct_data.size; }
- const uint8_t* data_ptr() const { return allocation ? allocation->data_ptr() : direct_data.data; }
- uint8_t* data_ptr() { return allocation ? allocation->data_ptr() : direct_data.data; }
+ size_t size() const { return m_inner.is_alloc ? m_inner.alloc.alloc->size() : m_inner.direct.size; }
+ const uint8_t* data_ptr() const { return m_inner.is_alloc ? m_inner.alloc.alloc->data_ptr() : m_inner.direct.data; }
+ uint8_t* data_ptr() { return m_inner.is_alloc ? m_inner.alloc.alloc->data_ptr() : m_inner.direct.data; }
+ const uint8_t* get_mask() const { return m_inner.is_alloc ? m_inner.alloc.alloc->m_mask.data() : m_inner.direct.mask; }
+ uint8_t* get_mask_mut() { return m_inner.is_alloc ? m_inner.alloc.alloc->m_mask.data() : m_inner.direct.mask; }
RelocationPtr get_relocation(size_t ofs) const override {
- if( this->allocation && this->allocation )
- return this->allocation->get_relocation(ofs);
+ if( m_inner.is_alloc )
+ return m_inner.alloc.alloc->get_relocation(ofs);
+ else if(ofs == 0)
+ {
+ return m_inner.direct.reloc_0;
+ }
else
+ {
return RelocationPtr();
+ }
+ }
+ void set_reloc(size_t ofs, size_t size, RelocationPtr p) /*override*/ {
+ if( m_inner.is_alloc )
+ {
+ m_inner.alloc.alloc->set_reloc(ofs, size, ::std::move(p));
+ }
+ else if( ofs == 0 /*&& size == POINTER_SIZE*/ )
+ {
+ m_inner.direct.reloc_0 = ::std::move(p);
+ }
+ else
+ {
+ this->create_allocation();
+ assert( m_inner.is_alloc );
+ m_inner.alloc.alloc->set_reloc(ofs, size, ::std::move(p));
+ }
}
void check_bytes_valid(size_t ofs, size_t size) const;
@@ -310,6 +475,17 @@ struct ValueRef:
size_t m_size; // Size in bytes of the referenced value
::std::shared_ptr<Value> m_metadata;
+ static bool in_bounds(size_t ofs, size_t size, size_t max_size) {
+ if( size == 0 ) {
+ return ofs <= max_size;
+ }
+ if( ofs > 0 && !(ofs < max_size) )
+ return false;
+ if( !(size <= max_size) )
+ return false;
+ return ofs + size <= max_size;
+ }
+
ValueRef(RelocationPtr ptr, size_t ofs, size_t size):
m_alloc(ptr),
m_value(nullptr),
@@ -321,22 +497,25 @@ struct ValueRef:
switch(m_alloc.get_ty())
{
case RelocationPtr::Ty::Allocation:
- assert(ofs < m_alloc.alloc().size());
- assert(size <= m_alloc.alloc().size());
- assert(ofs+size <= m_alloc.alloc().size());
+ if( !in_bounds(ofs, size, m_alloc.alloc().size()) )
+ {
+ LOG_NOTICE("ValueRef exceeds bounds of " << m_alloc << " - " << ofs << "+" << size << " > " << m_alloc.alloc().size());
+ }
break;
case RelocationPtr::Ty::StdString:
- assert(ofs < m_alloc.str().size());
- assert(size <= m_alloc.str().size());
- assert(ofs+size <= m_alloc.str().size());
+ if( !in_bounds(ofs, size, m_alloc.str().size()) )
+ {
+ LOG_NOTICE("ValueRef exceeds bounds of string - " << ofs << "+" << size << " > " << m_alloc.str().size());
+ }
break;
case RelocationPtr::Ty::FfiPointer:
- assert(ofs < m_alloc.ffi().size);
- assert(size <= m_alloc.ffi().size);
- assert(ofs+size <= m_alloc.ffi().size);
+ if( !in_bounds(ofs, size, m_alloc.ffi().get_size()) )
+ {
+ LOG_NOTICE("ValueRef exceeds bounds of FFI buffer - " << ofs << "+" << size << " > " << m_alloc.ffi().get_size());
+ }
break;
- default:
- throw "TODO";
+ case RelocationPtr::Ty::Function:
+ LOG_TODO("");
}
}
}
@@ -355,13 +534,13 @@ struct ValueRef:
if(m_alloc)
{
if( m_alloc.is_alloc() )
- return m_alloc.alloc().get_relocation(ofs);
+ return m_alloc.alloc().get_relocation(m_offset + ofs);
else
return RelocationPtr();
}
else if( m_value )
{
- return m_value->get_relocation(ofs);
+ return m_value->get_relocation(m_offset + ofs);
}
else
{
@@ -379,7 +558,27 @@ struct ValueRef:
case RelocationPtr::Ty::StdString:
return reinterpret_cast<const uint8_t*>(m_alloc.str().data() + m_offset);
default:
- throw "TODO";
+ LOG_TODO("");
+ }
+ }
+ else if( m_value ) {
+ return m_value->data_ptr() + m_offset;
+ }
+ else {
+ return nullptr;
+ }
+ }
+
+ // TODO: Remove these two (move to a helper?)
+ uint8_t* data_ptr_mut() {
+ if( m_alloc ) {
+ switch(m_alloc.get_ty())
+ {
+ case RelocationPtr::Ty::Allocation:
+ return m_alloc.alloc().data_ptr() + m_offset;
+ break;
+ default:
+ LOG_TODO("");
}
}
else if( m_value ) {
@@ -389,12 +588,12 @@ struct ValueRef:
return nullptr;
}
}
+ void mark_bytes_valid(size_t ofs, size_t size);
+
void read_bytes(size_t ofs, void* dst, size_t size) const {
if( size == 0 )
return ;
- assert(ofs < m_size);
- assert(size <= m_size);
- assert(ofs+size <= m_size);
+ LOG_ASSERT(in_bounds(ofs, size, m_size), "read_bytes(" << ofs << "+" << size << " > " << m_size <<")");
if( m_alloc ) {
switch(m_alloc.get_ty())
{
@@ -407,7 +606,7 @@ struct ValueRef:
break;
default:
//ASSERT_BUG(m_alloc.is_alloc(), "read_value on non-data backed Value - " << );
- throw "TODO";
+ LOG_TODO("");
}
}
else {
@@ -417,9 +616,7 @@ struct ValueRef:
void check_bytes_valid(size_t ofs, size_t size) const {
if( size == 0 )
return ;
- assert(ofs < m_size);
- assert(size <= m_size);
- assert(ofs+size <= m_size);
+ LOG_ASSERT(in_bounds(ofs, size, m_size), "check_bytes_valid(" << ofs << "+" << size << " > " << m_size <<")");
if( m_alloc ) {
switch(m_alloc.get_ty())
{
@@ -431,7 +628,7 @@ struct ValueRef:
break;
default:
//ASSERT_BUG(m_alloc.is_alloc(), "read_value on non-data backed Value - " << );
- throw "TODO";
+ LOG_TODO("");
}
}
else {
@@ -439,6 +636,10 @@ struct ValueRef:
}
}
- bool compare(const void* other, size_t other_len) const;
+ bool compare(size_t offset, const void* other, size_t other_len) const;
};
extern ::std::ostream& operator<<(::std::ostream& os, const ValueRef& v);
+//struct ValueRefMut:
+// public ValueCommonWrite
+//{
+//};
diff --git a/tools/standalone_miri/win32.api b/tools/standalone_miri/win32.api
new file mode 100644
index 00000000..1f374c1e
--- /dev/null
+++ b/tools/standalone_miri/win32.api
@@ -0,0 +1,24 @@
+#
+# Windows API calls
+#
+
+type HMODULE = void [size(0), name("HMODULE")];
+
+#fn GetModuleHandleW(lpcwsName: *const [cstr,null] u16) -> *const [null] HMODULE = "Kernel32.dll":"GetModuleHandleW";
+fn GetModuleHandleW(lpcwsName: *const [cstr,null] u16) -> *const [null] HMODULE {
+ miri::ensure_valid_nulseq("lpcwsName", lpcwsName);
+ return miri::call_ptr("Kernel32.dll", "GetModuleHandleW", lpcwsName);
+}
+
+# - The returned function pointer is annotated with the passed name
+fn GetProcAddress(hModule: *const HMODULE, name: *const [cstr] u8) -> fn(?) [null] {
+ miri::ensure_valid_nulseq("name", name);
+ let rv = miri::call_ptr("Kernel32.dll", "GetProcAddress", hModule, name);
+ # Create a named function pointer from the raw pointer return, that will go though the .api file
+ return miri::make_named_fn_ptr(rv, miri::cstr8_to_string(name));
+}
+
+fn AddVectoredExceptionHandler(..) -> usize {
+ 1
+}
+
diff --git a/tools/testrunner/main.cpp b/tools/testrunner/main.cpp
index 3823215a..a634d87a 100644
--- a/tools/testrunner/main.cpp
+++ b/tools/testrunner/main.cpp
@@ -37,6 +37,9 @@ struct Options
const char* input_glob = nullptr;
::std::vector<::std::string> test_list;
+ bool debug_enabled;
+ ::std::vector<::std::string> lib_dirs;
+
int debug_level = 0;
const char* exceptions_file = nullptr;
@@ -55,6 +58,12 @@ struct TestDesc
::std::vector<::std::string> m_pre_build;
::std::vector<::std::string> m_extra_flags;
bool ignore;
+ bool no_run;
+ TestDesc()
+ :ignore(false)
+ ,no_run(false)
+ {
+ }
};
struct Timestamp
{
@@ -94,20 +103,27 @@ struct Timestamp
}
};
-bool run_executable(const ::helpers::path& file, const ::std::vector<const char*>& args, const ::helpers::path& outfile);
+bool run_executable(const ::helpers::path& file, const ::std::vector<const char*>& args, const ::helpers::path& outfile, unsigned timeout_seconds);
-bool run_compiler(const ::helpers::path& source_file, const ::helpers::path& output, const ::std::vector<::std::string>& extra_flags, ::helpers::path libdir={}, bool is_dep=false)
+bool run_compiler(const Options& opts, const ::helpers::path& source_file, const ::helpers::path& output, const ::std::vector<::std::string>& extra_flags, ::helpers::path libdir={}, bool is_dep=false)
{
::std::vector<const char*> args;
args.push_back("mrustc");
// Force optimised and debuggable
args.push_back("-O");
- // TODO: Only turn debug on when requested by the caller
- //args.push_back("-g");
- args.push_back("-L");
- args.push_back("output");
+ // Only turn debug on when requested by the caller
+ if( opts.debug_enabled )
+ {
+ args.push_back("-g");
+ }
+
+ for(const auto& d : opts.lib_dirs)
+ {
+ args.push_back("-L");
+ args.push_back(d.c_str());
+ }
if(libdir.is_valid())
{
args.push_back("-L");
@@ -134,7 +150,16 @@ bool run_compiler(const ::helpers::path& source_file, const ::helpers::path& out
for(const auto& s : extra_flags)
args.push_back(s.c_str());
- return run_executable(MRUSTC_PATH, args, logfile);
+ return run_executable(MRUSTC_PATH, args, logfile, 0);
+}
+
+static bool gTimeout = false;
+static bool gInterrupted = false;
+void sigalrm_handler(int) {
+ gTimeout = true;
+}
+void sigint_handler(int) {
+ gInterrupted = true;
}
int main(int argc, const char* argv[])
@@ -145,6 +170,16 @@ int main(int argc, const char* argv[])
return v;
}
+#ifdef _WIN32
+#else
+ {
+ struct sigaction sa = {0};
+ sa.sa_handler = sigalrm_handler;
+ sigaction(SIGALRM, &sa, NULL);
+ signal(SIGINT, sigint_handler);
+ }
+#endif
+
::std::vector<::std::string> skip_list;
// > Filter out tests listed in an exceptions file (newline separated, supports comments)
if( opts.exceptions_file )
@@ -247,6 +282,10 @@ int main(int argc, const char* argv[])
{
td.ignore = true;
}
+ else if( line.substr(start, 4+1+7) == "skip-codegen" )
+ {
+ td.no_run = true;
+ }
else if( line.substr(start, 14) == "compile-flags:" )
{
auto end = line.find(' ', 3+14);
@@ -290,13 +329,19 @@ int main(int argc, const char* argv[])
::std::sort(tests.begin(), tests.end(), [](const auto& a, const auto& b){ return a.m_name < b.m_name; });
// ---
- auto compiler_ts = getenv("TESTRUNNER_NOCOMPILERDEP") ? Timestamp::infinite_past() : Timestamp::for_file(MRUSTC_PATH);
+ const bool SKIP_PASS = (getenv("TESTRUNNER_SKIPPASS") != nullptr);
+ const bool NO_COMPILER_DEP = (getenv("TESTRUNNER_NOCOMPILERDEP") != nullptr);
+ const auto compiler_ts = Timestamp::for_file(MRUSTC_PATH);
unsigned n_skip = 0;
unsigned n_cfail = 0;
unsigned n_fail = 0;
unsigned n_ok = 0;
for(const auto& test : tests)
{
+ if( gInterrupted ) {
+ DEBUG(">> Interrupted");
+ return 1;
+ }
if( !opts.test_list.empty() && ::std::find(opts.test_list.begin(), opts.test_list.end(), test.m_name) == opts.test_list.end() )
{
if( opts.debug_level > 0 )
@@ -319,10 +364,23 @@ int main(int argc, const char* argv[])
//DEBUG(">> " << test.m_name);
auto depdir = outdir / "deps-" + test.m_name.c_str();
- auto outfile = outdir / test.m_name + ".exe";
-
- auto test_output_ts = Timestamp::for_file(outfile);
- if( test_output_ts == Timestamp::infinite_past() || test_output_ts < compiler_ts )
+ auto test_exe = outdir / test.m_name + ".exe";
+ auto test_output = outdir / test.m_name + ".out";
+
+ auto test_exe_ts = Timestamp::for_file(test_exe);
+ auto test_output_ts = Timestamp::for_file(test_output);
+ // (Optional) if the target file doesn't exist, force a re-compile IF the compiler is newer than the
+ // executable.
+ if( SKIP_PASS )
+ {
+ // If output is missing (the last run didn't succeed), and the compiler is newer than the executable
+ if( test_output_ts == Timestamp::infinite_past() && test_exe_ts < compiler_ts )
+ {
+ // Force a recompile
+ test_exe_ts = Timestamp::infinite_past();
+ }
+ }
+ if( test_exe_ts == Timestamp::infinite_past() || (!NO_COMPILER_DEP && !SKIP_PASS && test_exe_ts < compiler_ts) )
{
bool pre_build_failed = false;
for(const auto& file : test.m_pre_build)
@@ -333,7 +391,7 @@ int main(int argc, const char* argv[])
mkdir(depdir.str().c_str(), 0755);
#endif
auto infile = input_path / "auxiliary" / file;
- if( !run_compiler(infile, depdir, {}, depdir, true) )
+ if( !run_compiler(opts, infile, depdir, {}, depdir, true) )
{
DEBUG("COMPILE FAIL " << infile << " (dep of " << test.m_name << ")");
n_cfail ++;
@@ -355,30 +413,36 @@ int main(int argc, const char* argv[])
depdir = ::helpers::path();
}
- auto compile_logfile = outdir / test.m_name + "-build.log";
- if( !run_compiler(test.m_path, outfile, test.m_extra_flags, depdir) )
+ auto compile_logfile = test_exe + "-build.log";
+ if( !run_compiler(opts, test.m_path, test_exe, test.m_extra_flags, depdir) )
{
- DEBUG("COMPILE FAIL " << test.m_name);
+ DEBUG("COMPILE FAIL " << test.m_name << ", log in " << compile_logfile);
n_cfail ++;
if( opts.fail_fast )
return 1;
else
continue;
}
- test_output_ts = Timestamp::for_file(outfile);
+ test_exe_ts = Timestamp::for_file(test_exe);
}
// - Run the test
- auto run_out_file = outdir / test.m_name + ".out";
- if( Timestamp::for_file(run_out_file) < test_output_ts )
+ if( test.no_run )
{
- if( !run_executable(outfile, { outfile.str().c_str() }, run_out_file) )
+ ::std::ofstream(test_output.str()) << "";
+ if( opts.debug_level > 0 )
+ DEBUG("No run " << test.m_name);
+ }
+ else if( test_output_ts < test_exe_ts )
+ {
+ auto run_out_file_tmp = test_output + ".tmp";
+ if( !run_executable(test_exe, { test_exe.str().c_str() }, run_out_file_tmp, 10) )
{
DEBUG("RUN FAIL " << test.m_name);
// Move the failing output file
- auto fail_file = run_out_file + "_failed";
+ auto fail_file = test_output + "_failed";
remove(fail_file.str().c_str());
- rename(run_out_file.str().c_str(), fail_file.str().c_str());
+ rename(run_out_file_tmp.str().c_str(), fail_file.str().c_str());
DEBUG("- Output in " << fail_file);
n_fail ++;
@@ -387,6 +451,11 @@ int main(int argc, const char* argv[])
else
continue;
}
+ else
+ {
+ remove(test_output.str().c_str());
+ rename(run_out_file_tmp.str().c_str(), test_output.str().c_str());
+ }
}
else
{
@@ -442,6 +511,16 @@ int Options::parse(int argc, const char* argv[])
case 'v':
this->debug_level += 1;
break;
+ case 'g':
+ this->debug_enabled = true;
+ break;
+ case 'L':
+ if( i+1 == argc ) {
+ this->usage_short();
+ return 1;
+ }
+ this->lib_dirs.push_back( argv[++i] );
+ break;
default:
this->usage_short();
@@ -501,7 +580,7 @@ void Options::usage_full() const
}
///
-bool run_executable(const ::helpers::path& exe_name, const ::std::vector<const char*>& args, const ::helpers::path& outfile)
+bool run_executable(const ::helpers::path& exe_name, const ::std::vector<const char*>& args, const ::helpers::path& outfile, unsigned timeout_seconds)
{
#ifdef _WIN32
::std::stringstream cmdline;
@@ -530,6 +609,7 @@ bool run_executable(const ::helpers::path& exe_name, const ::std::vector<const c
CreateProcessA(exe_name.str().c_str(), (LPSTR)cmdline_str.c_str(), NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi);
SetErrorMode(em);
CloseHandle(si.hStdOutput);
+ // TODO: Use timeout_seconds
WaitForSingleObject(pi.hProcess, INFINITE);
DWORD status = 1;
GetExitCodeProcess(pi.hProcess, &status);
@@ -567,15 +647,24 @@ bool run_executable(const ::helpers::path& exe_name, const ::std::vector<const c
posix_spawn_file_actions_destroy(&file_actions);
int status = -1;
- waitpid(pid, &status, 0);
+ // NOTE: `alarm(0)` clears any pending alarm, so no need to check before
+ // calling
+ alarm(timeout_seconds);
+ if( waitpid(pid, &status, 0) <= 0 )
+ {
+ DEBUG(exe_name << " timed out, killing it");
+ kill(pid, SIGKILL);
+ return false;
+ }
+ alarm(0);
if( status != 0 )
{
if( WIFEXITED(status) )
- DEBUG(exe_name << " exited with non-zero exit status " << WEXITSTATUS(status) << ", see log " << outfile_str);
+ DEBUG(exe_name << " exited with non-zero exit status " << WEXITSTATUS(status));
else if( WIFSIGNALED(status) )
- DEBUG(exe_name << " was terminated with signal " << WTERMSIG(status) << ", see log " << outfile_str);
+ DEBUG(exe_name << " was terminated with signal " << WTERMSIG(status));
else
- DEBUG(exe_name << " terminated for unknown reason, status=" << status << ", see log " << outfile_str);
+ DEBUG(exe_name << " terminated for unknown reason, status=" << status);
return false;
}
#endif