From 5488cc84e7a4b0e5d92d75d49a3fd944c3b7efc3 Mon Sep 17 00:00:00 2001 From: Matthias Kurtenacker Date: Tue, 31 Jan 2023 15:26:31 +0100 Subject: [PATCH 01/51] Support plugin execution. Very limited capabilities for now. --- src/thorin/CMakeLists.txt | 14 ++++--- src/thorin/continuation.cpp | 12 ++++++ src/thorin/continuation.h | 1 + src/thorin/rec_stream.cpp | 3 ++ src/thorin/transform/closure_conversion.cpp | 2 +- src/thorin/transform/plugin_execute.cpp | 45 +++++++++++++++++++++ src/thorin/transform/plugin_execute.h | 10 +++++ src/thorin/world.cpp | 37 +++++++++++++++++ src/thorin/world.h | 6 +++ 9 files changed, 123 insertions(+), 7 deletions(-) create mode 100644 src/thorin/transform/plugin_execute.cpp create mode 100644 src/thorin/transform/plugin_execute.h diff --git a/src/thorin/CMakeLists.txt b/src/thorin/CMakeLists.txt index 944c785d2..5f691d5f4 100644 --- a/src/thorin/CMakeLists.txt +++ b/src/thorin/CMakeLists.txt @@ -52,6 +52,10 @@ set(THORIN_SOURCES transform/codegen_prepare.cpp transform/dead_load_opt.cpp transform/dead_load_opt.h + transform/hls_channels.cpp + transform/hls_channels.h + transform/hls_kernel_launch.h + transform/hls_kernel_launch.cpp transform/hoist_enters.cpp transform/hoist_enters.h transform/flatten_tuples.cpp @@ -64,16 +68,14 @@ set(THORIN_SOURCES transform/lift_builtins.h transform/mangle.cpp transform/mangle.h - transform/resolve_loads.cpp - transform/resolve_loads.h transform/partial_evaluation.cpp transform/partial_evaluation.h + transform/plugin_execute.cpp + transform/plugin_execute.h + transform/resolve_loads.cpp + transform/resolve_loads.h transform/split_slots.cpp transform/split_slots.h - transform/hls_channels.cpp - transform/hls_channels.h - transform/hls_kernel_launch.h - transform/hls_kernel_launch.cpp util/array.h util/cast.h util/hash.h diff --git a/src/thorin/continuation.cpp b/src/thorin/continuation.cpp index f729937f8..5c4db2796 100644 --- a/src/thorin/continuation.cpp +++ b/src/thorin/continuation.cpp @@ -39,6 +39,18 @@ void App::verify() const { } } +void App::jump(const Def* callee, Defs args, Debug dbg) { + unset_ops(); + resize(args.size() + 1); + + set_op(0, callee); + for (int i = 0, e = args.size(); i < e; i++) { + set_op(i + 1, args[i]); + } + + verify(); +} + //------------------------------------------------------------------------------ Filter::Filter(World& world, const Defs defs, Debug dbg) : Def(Node_Filter, world.bottom_type(), defs, dbg) {} diff --git a/src/thorin/continuation.h b/src/thorin/continuation.h index 9f34fe992..30c1408fa 100644 --- a/src/thorin/continuation.h +++ b/src/thorin/continuation.h @@ -111,6 +111,7 @@ enum class Intrinsic : uint8_t { Branch, ///< branch(cond, T, F). Match, ///< match(val, otherwise, (case1, cont1), (case2, cont2), ...) PeInfo, ///< Partial evaluation debug info. + Plugin, ///< Some plugin derived intrinsic. Indentified by its name. EndScope ///< Dummy function which marks the end of a @p Scope. }; diff --git a/src/thorin/rec_stream.cpp b/src/thorin/rec_stream.cpp index 03d6a8c20..7e91fefe9 100644 --- a/src/thorin/rec_stream.cpp +++ b/src/thorin/rec_stream.cpp @@ -51,6 +51,9 @@ void RecStreamer::run() { if (cont->world().is_external(cont)) s.fmt("extern "); + if (cont->is_intrinsic() && cont->intrinsic() == Intrinsic::Plugin) + s.fmt("plugin "); + if (cont->has_body()) { std::vector param_names; for (auto param : cont->params()) param_names.push_back(param->unique_name()); diff --git a/src/thorin/transform/closure_conversion.cpp b/src/thorin/transform/closure_conversion.cpp index bb118c725..a1e105727 100644 --- a/src/thorin/transform/closure_conversion.cpp +++ b/src/thorin/transform/closure_conversion.cpp @@ -63,7 +63,7 @@ class ClosureConversion { // prevent conversion of calls to vectorize() or cuda(), but allow graph intrinsics auto callee = body->callee()->isa_nom(); if (callee == continuation) return; - if (!callee || !callee->is_intrinsic()) { + if (!callee || !callee->is_intrinsic() || callee->intrinsic() == Intrinsic::Plugin) { Array new_args(body->num_args()); for (size_t i = 0, e = body->num_args(); i != e; ++i) new_args[i] = convert(body->arg(i)); diff --git a/src/thorin/transform/plugin_execute.cpp b/src/thorin/transform/plugin_execute.cpp new file mode 100644 index 000000000..595edcfc5 --- /dev/null +++ b/src/thorin/transform/plugin_execute.cpp @@ -0,0 +1,45 @@ +#include "thorin/world.h" +#include "thorin/transform/plugin_execute.h" +#include "thorin/analyses/scope.h" + +namespace thorin { + +void plugin_execute(World& world) { + world.VLOG("start plugin_execute"); + for (auto cont : world.copy_continuations()) { + if (cont->is_intrinsic() && cont->intrinsic() == Intrinsic::Plugin) { + void * function_handle = world.search_plugin_function(cont->name()); + if (!function_handle) { + world.ELOG("Plugin function not found for: {}", cont->name()); + continue; + } + auto plugin_function = (void*(*)(void*)) function_handle; + + for (auto use : cont->copy_uses()) { + if (!use.def()->isa()) { + continue; + } + + auto app = const_cast(use.def()->as()); + + void * input = (void*) app->arg(1); + void * output = plugin_function(input); + if (input != output) { + world.ELOG("Plugin changed stuff"); + } + + Continuation* y = world.continuation(world.fn_type({world.mem_type(), world.fn_type({world.mem_type()})})); + y->jump(y->param(1), {y->param(0)}); + + Continuation* x = world.continuation(world.fn_type({world.mem_type()})); + x->jump(app->arg(2), {x->param(0), y}); + + app->jump((Def*)output, {app->arg(0), x}); + } + } + } + + world.VLOG("end plugin_execute"); +} + +} diff --git a/src/thorin/transform/plugin_execute.h b/src/thorin/transform/plugin_execute.h new file mode 100644 index 000000000..49cd9dec3 --- /dev/null +++ b/src/thorin/transform/plugin_execute.h @@ -0,0 +1,10 @@ +#ifndef THORIN_TRANSFORM_PLUGIN_EXECUTE_H +#define THORIN_TRANSFORM_PLUGIN_EXECUTE_H + +namespace thorin { + +void plugin_execute(World&); + +} + +#endif diff --git a/src/thorin/world.cpp b/src/thorin/world.cpp index 47f34b6aa..d45fdfdc3 100644 --- a/src/thorin/world.cpp +++ b/src/thorin/world.cpp @@ -10,6 +10,8 @@ #endif #include +#include +#include #include "thorin/def.h" #include "thorin/primop.h" @@ -17,6 +19,7 @@ #include "thorin/type.h" #include "thorin/analyses/scope.h" #include "thorin/analyses/verify.h" +#include "thorin/transform/plugin_execute.h" #include "thorin/transform/cleanup_world.h" #include "thorin/transform/clone_bodies.h" #include "thorin/transform/closure_conversion.h" @@ -1291,6 +1294,10 @@ void World::opt() { RUN_PASS(flatten_tuples(*this)) RUN_PASS(clone_bodies(*this)) RUN_PASS(split_slots(*this)) + if (plugin_handles.size() > 0) { + RUN_PASS(plugin_execute(*this)); + RUN_PASS(cleanup()); + } RUN_PASS(closure_conversion(*this)) RUN_PASS(lift_builtins(*this)) RUN_PASS(inliner(*this)) @@ -1300,4 +1307,34 @@ void World::opt() { RUN_PASS(codegen_prepare(*this)) } +bool World::register_plugin(std::string plugin_name) { + void *handle = dlopen(plugin_name.c_str(), RTLD_LAZY); + if (!handle) { + ELOG("Error loading plugin {}: {}", plugin_name, dlerror()); + ELOG("Is plugin contained in LD_LIBRARY_PATH?"); + return false; + } + dlerror(); + + void (*initfunc)(void); + char *error; + initfunc = (void(*)())(dlsym(handle, "init")); + if ((error = dlerror()) != NULL) { + ILOG("Plugin {} did not supply an init function", plugin_name); + } else { + initfunc(); + } + + plugin_handles.push_back(handle); + return true; +} + +void * World::search_plugin_function(std::string function_name) { + for (auto plugin : plugin_handles) { + if (void * plugin_function = dlsym(plugin, function_name.c_str())) { + return plugin_function; + } + } + return nullptr; +} } diff --git a/src/thorin/world.h b/src/thorin/world.h index 96e30af2b..09dc10919 100644 --- a/src/thorin/world.h +++ b/src/thorin/world.h @@ -318,6 +318,9 @@ class World : public TypeTable, public Streamable { swap(w1.stream_, w2.stream_); } + bool register_plugin(std::string plugin_name); + void * search_plugin_function(std::string function_name); + private: const Param* param(const Type* type, Continuation* continuation, size_t index, Debug dbg); const App* app(const Def* callee, const Defs args, Debug dbg = {}); @@ -325,6 +328,9 @@ class World : public TypeTable, public Streamable { template const Def* transcendental(MathOpTag, const Def*, Debug, F&&); template const Def* transcendental(MathOpTag, const Def*, const Def*, Debug, F&&); + std::unique_ptr world_; + std::vector plugin_handles; + /// @name put into see of nodes //@{ template const T* cse(const T* primop) { return cse_base(primop)->template as(); } From 0e1b6090de27e48dcd630df377c07fae5020c828 Mon Sep 17 00:00:00 2001 From: Matthias Kurtenacker Date: Thu, 28 Jul 2022 12:35:30 +0200 Subject: [PATCH 02/51] Support for multiple input arguments. --- src/thorin/transform/plugin_execute.cpp | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/thorin/transform/plugin_execute.cpp b/src/thorin/transform/plugin_execute.cpp index 595edcfc5..272cb7436 100644 --- a/src/thorin/transform/plugin_execute.cpp +++ b/src/thorin/transform/plugin_execute.cpp @@ -6,6 +6,7 @@ namespace thorin { void plugin_execute(World& world) { world.VLOG("start plugin_execute"); + for (auto cont : world.copy_continuations()) { if (cont->is_intrinsic() && cont->intrinsic() == Intrinsic::Plugin) { void * function_handle = world.search_plugin_function(cont->name()); @@ -13,7 +14,7 @@ void plugin_execute(World& world) { world.ELOG("Plugin function not found for: {}", cont->name()); continue; } - auto plugin_function = (void*(*)(void*)) function_handle; + auto plugin_function = (void*(*)(size_t, void**)) function_handle; for (auto use : cont->copy_uses()) { if (!use.def()->isa()) { @@ -22,17 +23,19 @@ void plugin_execute(World& world) { auto app = const_cast(use.def()->as()); - void * input = (void*) app->arg(1); - void * output = plugin_function(input); - if (input != output) { - world.ELOG("Plugin changed stuff"); + Def* input_array[app->num_args() - 2]; + for (size_t i = 1, e = app->num_args() - 1; i < e; i++) { + Def * input = const_cast(app->arg(i)); + input_array[i - 1] = input; } + void * output = plugin_function(app->num_args() - 2, (void **)input_array); + Continuation* y = world.continuation(world.fn_type({world.mem_type(), world.fn_type({world.mem_type()})})); y->jump(y->param(1), {y->param(0)}); Continuation* x = world.continuation(world.fn_type({world.mem_type()})); - x->jump(app->arg(2), {x->param(0), y}); + x->jump(app->arg(app->num_args() - 1), {x->param(0), y}); app->jump((Def*)output, {app->arg(0), x}); } From 3f0f38da3cfc4ca7c7ee41bb9f91fb5a7a5c9717 Mon Sep 17 00:00:00 2001 From: Matthias Kurtenacker Date: Tue, 13 Sep 2022 16:26:27 +0200 Subject: [PATCH 03/51] Use RTLD_GLOBAL to enable loading dependent plugins. --- src/thorin/world.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/thorin/world.cpp b/src/thorin/world.cpp index d45fdfdc3..e37b5a868 100644 --- a/src/thorin/world.cpp +++ b/src/thorin/world.cpp @@ -1308,7 +1308,7 @@ void World::opt() { } bool World::register_plugin(std::string plugin_name) { - void *handle = dlopen(plugin_name.c_str(), RTLD_LAZY); + void *handle = dlopen(plugin_name.c_str(), RTLD_LAZY | RTLD_GLOBAL); if (!handle) { ELOG("Error loading plugin {}: {}", plugin_name, dlerror()); ELOG("Is plugin contained in LD_LIBRARY_PATH?"); From 21e090e11db19086eb101dd59b2536263d37bf8c Mon Sep 17 00:00:00 2001 From: Matthias Kurtenacker Date: Fri, 30 Sep 2022 16:01:47 +0200 Subject: [PATCH 04/51] Add plugin dependencies + option to return values from plugin intrinsics. --- src/thorin/continuation.h | 1 + src/thorin/rec_stream.cpp | 5 +- src/thorin/transform/importer.cpp | 3 ++ src/thorin/transform/plugin_execute.cpp | 62 +++++++++++++++---------- 4 files changed, 46 insertions(+), 25 deletions(-) diff --git a/src/thorin/continuation.h b/src/thorin/continuation.h index 30c1408fa..f24b3f7d5 100644 --- a/src/thorin/continuation.h +++ b/src/thorin/continuation.h @@ -125,6 +125,7 @@ class Continuation : public Def { struct Attributes { Intrinsic intrinsic = Intrinsic::None; CC cc = CC::C; + const Continuation* depends = nullptr; Attributes(Intrinsic intrinsic) : intrinsic(intrinsic) {} Attributes(CC cc = CC::C) : cc(cc) {} diff --git a/src/thorin/rec_stream.cpp b/src/thorin/rec_stream.cpp index 7e91fefe9..19080ab9d 100644 --- a/src/thorin/rec_stream.cpp +++ b/src/thorin/rec_stream.cpp @@ -51,8 +51,11 @@ void RecStreamer::run() { if (cont->world().is_external(cont)) s.fmt("extern "); - if (cont->is_intrinsic() && cont->intrinsic() == Intrinsic::Plugin) + if (cont->is_intrinsic() && cont->intrinsic() == Intrinsic::Plugin) { s.fmt("plugin "); + if (cont->attributes().depends) + s.fmt("[depends {}] ", cont->attributes().depends->unique_name()); + } if (cont->has_body()) { std::vector param_names; diff --git a/src/thorin/transform/importer.cpp b/src/thorin/transform/importer.cpp index f125585de..af7f5c1c8 100644 --- a/src/thorin/transform/importer.cpp +++ b/src/thorin/transform/importer.cpp @@ -68,6 +68,9 @@ const Def* Importer::import(const Def* odef) { def_old2new_[ocontinuation->param(i)] = ncontinuation->param(i); } + if (ocontinuation->attributes().depends) + ncontinuation->attributes().depends = import(ocontinuation->attributes().depends)->as(); + def_old2new_[ocontinuation] = ncontinuation; if (ocontinuation->is_external()) diff --git a/src/thorin/transform/plugin_execute.cpp b/src/thorin/transform/plugin_execute.cpp index 272cb7436..636731776 100644 --- a/src/thorin/transform/plugin_execute.cpp +++ b/src/thorin/transform/plugin_execute.cpp @@ -2,43 +2,57 @@ #include "thorin/transform/plugin_execute.h" #include "thorin/analyses/scope.h" +#include + namespace thorin { void plugin_execute(World& world) { world.VLOG("start plugin_execute"); + std::vector plugin_intrinsics; + for (auto cont : world.copy_continuations()) { if (cont->is_intrinsic() && cont->intrinsic() == Intrinsic::Plugin) { - void * function_handle = world.search_plugin_function(cont->name()); - if (!function_handle) { - world.ELOG("Plugin function not found for: {}", cont->name()); - continue; + plugin_intrinsics.push_back(cont); + if (cont->attributes().depends) { + cont->dump(); + cont->attributes().depends->dump(); } - auto plugin_function = (void*(*)(size_t, void**)) function_handle; - - for (auto use : cont->copy_uses()) { - if (!use.def()->isa()) { - continue; - } - - auto app = const_cast(use.def()->as()); - - Def* input_array[app->num_args() - 2]; - for (size_t i = 1, e = app->num_args() - 1; i < e; i++) { - Def * input = const_cast(app->arg(i)); - input_array[i - 1] = input; - } + } + } - void * output = plugin_function(app->num_args() - 2, (void **)input_array); + sort(plugin_intrinsics.begin(), plugin_intrinsics.end(), [&](const Continuation* a, const Continuation* b) { + const Continuation* depends = a; + while (depends) { + if (depends == b) return false; + depends = depends->attributes().depends; + } + return true; + }); + + for (auto cont : plugin_intrinsics) { + void * function_handle = world.search_plugin_function(cont->name()); + if (!function_handle) { + world.ELOG("Plugin function not found for: {}", cont->name()); + continue; + } + auto plugin_function = (void*(*)(size_t, void**)) function_handle; - Continuation* y = world.continuation(world.fn_type({world.mem_type(), world.fn_type({world.mem_type()})})); - y->jump(y->param(1), {y->param(0)}); + for (auto use : cont->copy_uses()) { + if (!use.def()->isa()) { + continue; + } - Continuation* x = world.continuation(world.fn_type({world.mem_type()})); - x->jump(app->arg(app->num_args() - 1), {x->param(0), y}); + auto app = const_cast(use.def()->as()); - app->jump((Def*)output, {app->arg(0), x}); + Def* input_array[app->num_args() - 2]; + for (size_t i = 1, e = app->num_args() - 1; i < e; i++) { + Def * input = const_cast(app->arg(i)); + input_array[i - 1] = input; } + + void * output = plugin_function(app->num_args() - 2, (void **)input_array); + app->jump(app->arg(app->num_args() - 1), {app->arg(0), (Def*)output}); } } From eaf6b75570dd7bf1d0b57a02a7381eee496252fa Mon Sep 17 00:00:00 2001 From: Matthias Kurtenacker Date: Fri, 20 Jan 2023 13:56:31 +0100 Subject: [PATCH 05/51] Partial Evaluation between plugin steps. --- src/thorin/transform/plugin_execute.cpp | 84 +++++++++++++++---------- 1 file changed, 52 insertions(+), 32 deletions(-) diff --git a/src/thorin/transform/plugin_execute.cpp b/src/thorin/transform/plugin_execute.cpp index 636731776..9f60fcfdb 100644 --- a/src/thorin/transform/plugin_execute.cpp +++ b/src/thorin/transform/plugin_execute.cpp @@ -1,5 +1,6 @@ #include "thorin/world.h" #include "thorin/transform/plugin_execute.h" +#include "thorin/transform/partial_evaluation.h" #include "thorin/analyses/scope.h" #include @@ -11,51 +12,70 @@ void plugin_execute(World& world) { std::vector plugin_intrinsics; - for (auto cont : world.copy_continuations()) { - if (cont->is_intrinsic() && cont->intrinsic() == Intrinsic::Plugin) { - plugin_intrinsics.push_back(cont); - if (cont->attributes().depends) { - cont->dump(); - cont->attributes().depends->dump(); - } - } - } + while (true) { + plugin_intrinsics.clear(); - sort(plugin_intrinsics.begin(), plugin_intrinsics.end(), [&](const Continuation* a, const Continuation* b) { - const Continuation* depends = a; - while (depends) { - if (depends == b) return false; - depends = depends->attributes().depends; + for (auto cont : world.copy_continuations()) { + if (cont->is_intrinsic() && cont->intrinsic() == Intrinsic::Plugin) { + plugin_intrinsics.push_back(cont); } - return true; - }); - - for (auto cont : plugin_intrinsics) { - void * function_handle = world.search_plugin_function(cont->name()); - if (!function_handle) { - world.ELOG("Plugin function not found for: {}", cont->name()); - continue; } - auto plugin_function = (void*(*)(size_t, void**)) function_handle; - for (auto use : cont->copy_uses()) { - if (!use.def()->isa()) { + if (plugin_intrinsics.empty()) + break; + + sort(plugin_intrinsics.begin(), plugin_intrinsics.end(), [&](const Continuation* a, const Continuation* b) { + const Continuation* depends = a; + while (depends) { + if (depends == b) return false; + depends = depends->attributes().depends; + } + return true; + }); + + for (auto cont : plugin_intrinsics) { + void * function_handle = world.search_plugin_function(cont->name()); + if (!function_handle) { + world.ELOG("Plugin function not found for: {}", cont->name()); continue; } + auto plugin_function = (void*(*)(size_t, void**)) function_handle; + + bool evaluated = false; + for (auto use : cont->copy_uses()) { + if (!use.def()->isa()) { + continue; + } - auto app = const_cast(use.def()->as()); + auto app = const_cast(use.def()->as()); - Def* input_array[app->num_args() - 2]; - for (size_t i = 1, e = app->num_args() - 1; i < e; i++) { - Def * input = const_cast(app->arg(i)); - input_array[i - 1] = input; + if (app->num_uses() == 0) { + continue; + } + + Def* input_array[app->num_args() - 2]; + for (size_t i = 1, e = app->num_args() - 1; i < e; i++) { + Def * input = const_cast(app->arg(i)); + input_array[i - 1] = input; + } + + void * output = plugin_function(app->num_args() - 2, (void **)input_array); + app->jump(app->arg(app->num_args() - 1), {app->arg(0), (Def*)output}); + + //partial_evaluation(world); //TODO: Some form of cleanup would be advisable here. + evaluated = true; } - void * output = plugin_function(app->num_args() - 2, (void **)input_array); - app->jump(app->arg(app->num_args() - 1), {app->arg(0), (Def*)output}); + if (evaluated) + break; } + + world.cleanup(); //Warning: This must not change the world, there are still references to intrinsics being maintained here. } + world.mark_pe_done(false); + world.cleanup(); + world.VLOG("end plugin_execute"); } From da72529d0c7306c6afcc048d0f231ffd6f389dc4 Mon Sep 17 00:00:00 2001 From: Matthias Kurtenacker Date: Fri, 23 Jun 2023 16:22:47 +0200 Subject: [PATCH 06/51] Implement world.release to emit anydsl_release calls in backend, similar to alloc. --- src/thorin/be/llvm/amdgpu.h | 1 + src/thorin/be/llvm/cpu.h | 1 + src/thorin/be/llvm/llvm.cpp | 11 +++++++++++ src/thorin/be/llvm/llvm.h | 2 ++ src/thorin/be/llvm/nvvm.h | 1 + src/thorin/primop.cpp | 11 +++++++++++ src/thorin/primop.h | 14 ++++++++++++++ src/thorin/tables/nodetable.h | 1 + src/thorin/world.cpp | 4 ++++ src/thorin/world.h | 1 + 10 files changed, 47 insertions(+) diff --git a/src/thorin/be/llvm/amdgpu.h b/src/thorin/be/llvm/amdgpu.h index 640fc5b69..ed76968a1 100644 --- a/src/thorin/be/llvm/amdgpu.h +++ b/src/thorin/be/llvm/amdgpu.h @@ -24,6 +24,7 @@ class AMDGPUCodeGen : public CodeGen { llvm::Value* emit_mathop(llvm::IRBuilder<>&, const MathOp*) override; Continuation* emit_reserve(llvm::IRBuilder<>&, const Continuation*) override; std::string get_alloc_name() const override { return "malloc"; } + std::string get_release_name() const override { return "free"; } const Cont2Config& kernel_config_; }; diff --git a/src/thorin/be/llvm/cpu.h b/src/thorin/be/llvm/cpu.h index 7724ccc91..0fe842fc1 100644 --- a/src/thorin/be/llvm/cpu.h +++ b/src/thorin/be/llvm/cpu.h @@ -13,6 +13,7 @@ class CPUCodeGen : public CodeGen { protected: std::string get_alloc_name() const override { return "anydsl_alloc"; } + std::string get_release_name() const override { return "anydsl_release"; } }; } diff --git a/src/thorin/be/llvm/llvm.cpp b/src/thorin/be/llvm/llvm.cpp index 6f4e83191..8e62b73e3 100644 --- a/src/thorin/be/llvm/llvm.cpp +++ b/src/thorin/be/llvm/llvm.cpp @@ -905,6 +905,9 @@ llvm::Value* CodeGen::emit_bb(BB& bb, const Def* def) { } else if (auto alloc = def->isa()) { emit_unsafe(alloc->mem()); return emit_alloc(irbuilder, alloc->alloced_type(), alloc->extra()); + } else if (auto release = def->isa()) { + emit_unsafe(release->mem()); + return emit_release(irbuilder, release->alloc()); } else if (auto slot = def->isa()) { return emit_alloca(irbuilder, convert(slot->type()->as()->pointee()), slot->unique_name()); } else if (auto vector = def->isa()) { @@ -950,6 +953,14 @@ llvm::Value* CodeGen::emit_alloc(llvm::IRBuilder<>& irbuilder, const Type* type, return irbuilder.CreatePointerCast(void_ptr, llvm::PointerType::get(context(), 0)); } +llvm::Value* CodeGen::emit_release(llvm::IRBuilder<>& irbuilder, const Def* alloc) { + auto llvm_release = runtime_->get(*this, get_release_name().c_str()); + llvm::Value* llvm_alloc = emit(alloc); + llvm::Value* release_args[] = { irbuilder.getInt32(0), llvm_alloc }; + irbuilder.CreateCall(llvm_release, release_args); + return nullptr; +} + llvm::AllocaInst* CodeGen::emit_alloca(llvm::IRBuilder<>& irbuilder, llvm::Type* type, const std::string& name) { // Emit the alloca in the entry block auto entry = &irbuilder.GetInsertBlock()->getParent()->getEntryBlock(); diff --git a/src/thorin/be/llvm/llvm.h b/src/thorin/be/llvm/llvm.h index 43ceb5a75..1449c2c02 100644 --- a/src/thorin/be/llvm/llvm.h +++ b/src/thorin/be/llvm/llvm.h @@ -72,6 +72,7 @@ class CodeGen : public thorin::CodeGen, public thorin::Emitter&, llvm::Type*, const std::string&); llvm::Value* emit_alloc (llvm::IRBuilder<>&, const Type*, const Def*); + llvm::Value* emit_release (llvm::IRBuilder<>&, const Def*); virtual void emit_fun_decl_hook(Continuation*, llvm::Function*) {} virtual llvm::Value* map_param(llvm::Function*, llvm::Argument* a, const Param*) { return a; } @@ -85,6 +86,7 @@ class CodeGen : public thorin::CodeGen, public thorin::Emitter&, const Continuation*, bool=false); virtual std::string get_alloc_name() const = 0; + virtual std::string get_release_name() const = 0; llvm::BasicBlock* cont2bb(Continuation* cont) { return cont2bb_[cont].first; } virtual llvm::Value* emit_global(const Global*); diff --git a/src/thorin/be/llvm/nvvm.h b/src/thorin/be/llvm/nvvm.h index ee6f5239f..dfe75307c 100644 --- a/src/thorin/be/llvm/nvvm.h +++ b/src/thorin/be/llvm/nvvm.h @@ -33,6 +33,7 @@ class NVVMCodeGen : public CodeGen { llvm::Value* emit_global(const Global*) override; std::string get_alloc_name() const override { return "malloc"; } + std::string get_release_name() const override { return "free"; } private: llvm::Function* get_texture_handle_fun(llvm::IRBuilder<>&); diff --git a/src/thorin/primop.cpp b/src/thorin/primop.cpp index ddad23575..ce5439ee3 100644 --- a/src/thorin/primop.cpp +++ b/src/thorin/primop.cpp @@ -113,6 +113,13 @@ Alloc::Alloc(const Type* type, const Def* mem, const Def* extra, Debug dbg) set_type(w.tuple_type({w.mem_type(), w.ptr_type(type)})); } +Release::Release(const Def* mem, const Def* alloc, Debug dbg) + : MemOp(Node_Release, nullptr, {mem, alloc}, dbg) +{ + World& w = mem->world(); + set_type(w.mem_type()); +} + Load::Load(const Def* mem, const Def* ptr, Debug dbg) : Access(Node_Load, nullptr, {mem, ptr}, dbg) { @@ -226,6 +233,10 @@ const Def* Alloc::rebuild(World& w, const Type* t, Defs o) const { return w.alloc(t->as()->op(1)->as()->pointee(), o[0], o[1], debug()); } +const Def* Release::rebuild(World& w, const Type* t, Defs o) const { + return w.release(o[0], o[1], debug()); +} + const Def* Assembly::rebuild(World& w, const Type* t, Defs o) const { return w.assembly(t, o, asm_template(), output_constraints(), input_constraints(), clobbers(), flags(), debug()); } diff --git a/src/thorin/primop.h b/src/thorin/primop.h index ad4e2efcb..7eddcd4bf 100644 --- a/src/thorin/primop.h +++ b/src/thorin/primop.h @@ -582,6 +582,20 @@ class Alloc : public MemOp { friend class World; }; +class Release : public MemOp { +private: + Release(const Def* mem, const Def* alloc, Debug dbg); + +public: + const Def* alloc() const { return op(1); } + +private: + const Def* rebuild(World&, const Type*, Defs) const override; + + friend class World; +}; + + /// Base class for @p Load and @p Store. class Access : public MemOp { protected: diff --git a/src/thorin/tables/nodetable.h b/src/thorin/tables/nodetable.h index a302d270f..389f534a1 100644 --- a/src/thorin/tables/nodetable.h +++ b/src/thorin/tables/nodetable.h @@ -12,6 +12,7 @@ THORIN_NODE(BlobPtr, mem_blob) // MemOp THORIN_NODE(Alloc, alloc) + THORIN_NODE(Release, release) // Access THORIN_NODE(Load, load) THORIN_NODE(Store, store) diff --git a/src/thorin/world.cpp b/src/thorin/world.cpp index e37b5a868..6523c227b 100644 --- a/src/thorin/world.cpp +++ b/src/thorin/world.cpp @@ -1047,6 +1047,10 @@ const Def* World::alloc(const Type* type, const Def* mem, const Def* extra, Debu return cse(new Alloc(type, mem, extra, dbg)); } +const Def* World::release(const Def* mem, const Def* alloc, Debug dbg) { + return cse(new Release(mem, alloc, dbg)); +} + const Def* World::global(const Def* init, bool is_mutable, Debug dbg) { return cse(new Global(init, is_mutable, dbg)); } diff --git a/src/thorin/world.h b/src/thorin/world.h index 09dc10919..d77261812 100644 --- a/src/thorin/world.h +++ b/src/thorin/world.h @@ -219,6 +219,7 @@ class World : public TypeTable, public Streamable { const Def* slot(const Type* type, const Def* frame, Debug dbg = {}) { return cse(new Slot(type, frame, dbg)); } const Def* alloc(const Type* type, const Def* mem, const Def* extra, Debug dbg = {}); const Def* alloc(const Type* type, const Def* mem, Debug dbg = {}) { return alloc(type, mem, literal_qu64(0, dbg), dbg); } + const Def* release(const Def* mem, const Def* alloc, Debug dbg = {}); const Def* global(const Def* init, bool is_mutable = true, Debug dbg = {}); const Def* global_immutable_string(const std::string& str, Debug dbg = {}); const Def* lea(const Def* ptr, const Def* index, Debug dbg); From 5d2c468a8c7a992c4fb3cd6ff23cc6d5193426bb Mon Sep 17 00:00:00 2001 From: Matthias Kurtenacker Date: Mon, 26 Jun 2023 17:00:58 +0200 Subject: [PATCH 07/51] Bugfix: llvm 14 needs pointer cast for release. --- src/thorin/be/llvm/llvm.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/thorin/be/llvm/llvm.cpp b/src/thorin/be/llvm/llvm.cpp index 8e62b73e3..a0d0c4a46 100644 --- a/src/thorin/be/llvm/llvm.cpp +++ b/src/thorin/be/llvm/llvm.cpp @@ -956,7 +956,8 @@ llvm::Value* CodeGen::emit_alloc(llvm::IRBuilder<>& irbuilder, const Type* type, llvm::Value* CodeGen::emit_release(llvm::IRBuilder<>& irbuilder, const Def* alloc) { auto llvm_release = runtime_->get(*this, get_release_name().c_str()); llvm::Value* llvm_alloc = emit(alloc); - llvm::Value* release_args[] = { irbuilder.getInt32(0), llvm_alloc }; + llvm::Value* cast_alloc = irbuilder.CreatePointerCast(llvm_alloc, irbuilder.getInt8PtrTy()); + llvm::Value* release_args[] = { irbuilder.getInt32(0), cast_alloc }; irbuilder.CreateCall(llvm_release, release_args); return nullptr; } From 59aa49f7536572e402b5b11b7b3329a05edb51a8 Mon Sep 17 00:00:00 2001 From: Stefan Lemme Date: Tue, 27 Jun 2023 12:06:35 +0200 Subject: [PATCH 08/51] Fix MSVC build by disabling plugins --- src/thorin/CMakeLists.txt | 4 ++++ src/thorin/transform/plugin_execute.cpp | 4 ++-- src/thorin/world.cpp | 11 +++++++++++ 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/thorin/CMakeLists.txt b/src/thorin/CMakeLists.txt index 5f691d5f4..4859dfc2d 100644 --- a/src/thorin/CMakeLists.txt +++ b/src/thorin/CMakeLists.txt @@ -123,3 +123,7 @@ if(LLVM_FOUND) endif() llvm_config(thorin ${AnyDSL_LLVM_LINK_SHARED} ${Thorin_LLVM_COMPONENTS}) endif() + +if(NOT MSVC) + target_link_libraries(thorin PRIVATE dl) +endif() diff --git a/src/thorin/transform/plugin_execute.cpp b/src/thorin/transform/plugin_execute.cpp index 9f60fcfdb..524d092f0 100644 --- a/src/thorin/transform/plugin_execute.cpp +++ b/src/thorin/transform/plugin_execute.cpp @@ -53,13 +53,13 @@ void plugin_execute(World& world) { continue; } - Def* input_array[app->num_args() - 2]; + std::vector input_array(app->num_args() - 2); for (size_t i = 1, e = app->num_args() - 1; i < e; i++) { Def * input = const_cast(app->arg(i)); input_array[i - 1] = input; } - void * output = plugin_function(app->num_args() - 2, (void **)input_array); + void * output = plugin_function(app->num_args() - 2, (void **)input_array.data()); app->jump(app->arg(app->num_args() - 1), {app->arg(0), (Def*)output}); //partial_evaluation(world); //TODO: Some form of cleanup would be advisable here. diff --git a/src/thorin/world.cpp b/src/thorin/world.cpp index 6523c227b..ee3148e5d 100644 --- a/src/thorin/world.cpp +++ b/src/thorin/world.cpp @@ -10,8 +10,12 @@ #endif #include +#ifdef _MSC_VER +#include +#else #include #include +#endif #include "thorin/def.h" #include "thorin/primop.h" @@ -1312,6 +1316,9 @@ void World::opt() { } bool World::register_plugin(std::string plugin_name) { +#ifdef _MSC_VER + return false; +#else // _MSC_VER void *handle = dlopen(plugin_name.c_str(), RTLD_LAZY | RTLD_GLOBAL); if (!handle) { ELOG("Error loading plugin {}: {}", plugin_name, dlerror()); @@ -1331,14 +1338,18 @@ bool World::register_plugin(std::string plugin_name) { plugin_handles.push_back(handle); return true; +#endif // _MSC_VER } void * World::search_plugin_function(std::string function_name) { +#ifdef _MSC_VER +#else // _MSC_VER for (auto plugin : plugin_handles) { if (void * plugin_function = dlsym(plugin, function_name.c_str())) { return plugin_function; } } +#endif // _MSC_VER return nullptr; } } From c706d3aaa0842359f62de519e7ff042c952f090b Mon Sep 17 00:00:00 2001 From: Stefan Lemme Date: Tue, 27 Jun 2023 12:26:06 +0200 Subject: [PATCH 09/51] use proper CMAKE_DL_LIBS variable --- src/thorin/CMakeLists.txt | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/thorin/CMakeLists.txt b/src/thorin/CMakeLists.txt index 4859dfc2d..16dbcb97f 100644 --- a/src/thorin/CMakeLists.txt +++ b/src/thorin/CMakeLists.txt @@ -124,6 +124,4 @@ if(LLVM_FOUND) llvm_config(thorin ${AnyDSL_LLVM_LINK_SHARED} ${Thorin_LLVM_COMPONENTS}) endif() -if(NOT MSVC) - target_link_libraries(thorin PRIVATE dl) -endif() +target_link_libraries(thorin PRIVATE ${CMAKE_DL_LIBS}) From a3d90920fcdc546427fc9277ee3a8c1bbaeed3ba Mon Sep 17 00:00:00 2001 From: Matthias Kurtenacker Date: Thu, 29 Jun 2023 12:20:34 +0200 Subject: [PATCH 10/51] Support return nullptr in plugins for void returns. --- src/thorin/transform/plugin_execute.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/thorin/transform/plugin_execute.cpp b/src/thorin/transform/plugin_execute.cpp index 524d092f0..aeaff9931 100644 --- a/src/thorin/transform/plugin_execute.cpp +++ b/src/thorin/transform/plugin_execute.cpp @@ -60,7 +60,10 @@ void plugin_execute(World& world) { } void * output = plugin_function(app->num_args() - 2, (void **)input_array.data()); - app->jump(app->arg(app->num_args() - 1), {app->arg(0), (Def*)output}); + if (output) + app->jump(app->arg(app->num_args() - 1), {app->arg(0), (Def*)output}); + else + app->jump(app->arg(app->num_args() - 1), {app->arg(0)}); //partial_evaluation(world); //TODO: Some form of cleanup would be advisable here. evaluated = true; From 4d5b1b8d72892ef0519f9462e0a50279a8141963 Mon Sep 17 00:00:00 2001 From: Michael Kenzel Date: Tue, 4 Jul 2023 18:22:57 +0200 Subject: [PATCH 11/51] pass World and App to plugin function --- src/thorin/transform/plugin_execute.cpp | 13 +++---------- src/thorin/world.cpp | 15 +++++++-------- src/thorin/world.h | 9 +++++++-- 3 files changed, 17 insertions(+), 20 deletions(-) diff --git a/src/thorin/transform/plugin_execute.cpp b/src/thorin/transform/plugin_execute.cpp index aeaff9931..8495e1e70 100644 --- a/src/thorin/transform/plugin_execute.cpp +++ b/src/thorin/transform/plugin_execute.cpp @@ -34,12 +34,11 @@ void plugin_execute(World& world) { }); for (auto cont : plugin_intrinsics) { - void * function_handle = world.search_plugin_function(cont->name()); - if (!function_handle) { + auto plugin_function = world.search_plugin_function(cont->name().c_str()); + if (!plugin_function) { world.ELOG("Plugin function not found for: {}", cont->name()); continue; } - auto plugin_function = (void*(*)(size_t, void**)) function_handle; bool evaluated = false; for (auto use : cont->copy_uses()) { @@ -53,13 +52,7 @@ void plugin_execute(World& world) { continue; } - std::vector input_array(app->num_args() - 2); - for (size_t i = 1, e = app->num_args() - 1; i < e; i++) { - Def * input = const_cast(app->arg(i)); - input_array[i - 1] = input; - } - - void * output = plugin_function(app->num_args() - 2, (void **)input_array.data()); + void* output = plugin_function(&world, app); if (output) app->jump(app->arg(app->num_args() - 1), {app->arg(0), (Def*)output}); else diff --git a/src/thorin/world.cpp b/src/thorin/world.cpp index ee3148e5d..62af0d519 100644 --- a/src/thorin/world.cpp +++ b/src/thorin/world.cpp @@ -1315,11 +1315,11 @@ void World::opt() { RUN_PASS(codegen_prepare(*this)) } -bool World::register_plugin(std::string plugin_name) { +bool World::register_plugin(const char* plugin_name) { #ifdef _MSC_VER return false; #else // _MSC_VER - void *handle = dlopen(plugin_name.c_str(), RTLD_LAZY | RTLD_GLOBAL); + void *handle = dlopen(plugin_name, RTLD_LAZY | RTLD_GLOBAL); if (!handle) { ELOG("Error loading plugin {}: {}", plugin_name, dlerror()); ELOG("Is plugin contained in LD_LIBRARY_PATH?"); @@ -1327,13 +1327,12 @@ bool World::register_plugin(std::string plugin_name) { } dlerror(); - void (*initfunc)(void); char *error; - initfunc = (void(*)())(dlsym(handle, "init")); + auto initfunc = reinterpret_cast(dlsym(handle, "init")); if ((error = dlerror()) != NULL) { ILOG("Plugin {} did not supply an init function", plugin_name); } else { - initfunc(); + initfunc(this); } plugin_handles.push_back(handle); @@ -1341,12 +1340,12 @@ bool World::register_plugin(std::string plugin_name) { #endif // _MSC_VER } -void * World::search_plugin_function(std::string function_name) { +World::plugin_func_t* World::search_plugin_function(const char* function_name) const { #ifdef _MSC_VER #else // _MSC_VER for (auto plugin : plugin_handles) { - if (void * plugin_function = dlsym(plugin, function_name.c_str())) { - return plugin_function; + if (void* plugin_function = dlsym(plugin, function_name)) { + return reinterpret_cast(plugin_function); } } #endif // _MSC_VER diff --git a/src/thorin/world.h b/src/thorin/world.h index d77261812..f7c7594e0 100644 --- a/src/thorin/world.h +++ b/src/thorin/world.h @@ -319,8 +319,13 @@ class World : public TypeTable, public Streamable { swap(w1.stream_, w2.stream_); } - bool register_plugin(std::string plugin_name); - void * search_plugin_function(std::string function_name); + // plugins + + using plugin_init_func_t = void(World*); + using plugin_func_t = void*(World*, const App*); + + bool register_plugin(const char* plugin_name); + plugin_func_t* search_plugin_function(const char* function_name) const; private: const Param* param(const Type* type, Continuation* continuation, size_t index, Debug dbg); From aea8051934d09ad3ff3bec712bbd25f841003bc9 Mon Sep 17 00:00:00 2001 From: Matthias Kurtenacker Date: Wed, 5 Jul 2023 11:04:08 +0200 Subject: [PATCH 12/51] Make plugins explicitly return a Def* to avoid UB. --- src/thorin/transform/plugin_execute.cpp | 4 ++-- src/thorin/world.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/thorin/transform/plugin_execute.cpp b/src/thorin/transform/plugin_execute.cpp index 8495e1e70..ea81c7c59 100644 --- a/src/thorin/transform/plugin_execute.cpp +++ b/src/thorin/transform/plugin_execute.cpp @@ -52,9 +52,9 @@ void plugin_execute(World& world) { continue; } - void* output = plugin_function(&world, app); + const Def* output = plugin_function(&world, app); if (output) - app->jump(app->arg(app->num_args() - 1), {app->arg(0), (Def*)output}); + app->jump(app->arg(app->num_args() - 1), {app->arg(0), output}); else app->jump(app->arg(app->num_args() - 1), {app->arg(0)}); diff --git a/src/thorin/world.h b/src/thorin/world.h index f7c7594e0..ef27e0c67 100644 --- a/src/thorin/world.h +++ b/src/thorin/world.h @@ -322,7 +322,7 @@ class World : public TypeTable, public Streamable { // plugins using plugin_init_func_t = void(World*); - using plugin_func_t = void*(World*, const App*); + using plugin_func_t = const Def*(World*, const App*); bool register_plugin(const char* plugin_name); plugin_func_t* search_plugin_function(const char* function_name) const; From 986dc96b4a09b88dfdace78463e2f6aebd8a23ec Mon Sep 17 00:00:00 2001 From: Matthias Kurtenacker Date: Wed, 5 Jul 2023 11:34:55 +0200 Subject: [PATCH 13/51] Do not const_cast the app node, use rebuild and replace_uses instead. --- src/thorin/transform/plugin_execute.cpp | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/thorin/transform/plugin_execute.cpp b/src/thorin/transform/plugin_execute.cpp index ea81c7c59..b37bf7363 100644 --- a/src/thorin/transform/plugin_execute.cpp +++ b/src/thorin/transform/plugin_execute.cpp @@ -46,17 +46,20 @@ void plugin_execute(World& world) { continue; } - auto app = const_cast(use.def()->as()); + auto app = use.def()->as(); if (app->num_uses() == 0) { continue; } const Def* output = plugin_function(&world, app); - if (output) - app->jump(app->arg(app->num_args() - 1), {app->arg(0), output}); - else - app->jump(app->arg(app->num_args() - 1), {app->arg(0)}); + const Def* app_rebuild = nullptr; + if (output) { + app_rebuild = app->rebuild(world, world.bottom_type(), {app->arg(app->num_args() - 1), app->arg(0), output}); + } else { + app_rebuild = app->rebuild(world, world.bottom_type(), {app->arg(app->num_args() - 1), app->arg(0)}); + } + app->replace_uses(app_rebuild); //partial_evaluation(world); //TODO: Some form of cleanup would be advisable here. evaluated = true; From dd51c1716c49076f17ff59c7ca46acc921c85c5d Mon Sep 17 00:00:00 2001 From: Matthias Kurtenacker Date: Wed, 5 Jul 2023 12:11:02 +0200 Subject: [PATCH 14/51] Gather plugins without copying continuations. --- src/thorin/transform/plugin_execute.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/thorin/transform/plugin_execute.cpp b/src/thorin/transform/plugin_execute.cpp index b37bf7363..9c81356a8 100644 --- a/src/thorin/transform/plugin_execute.cpp +++ b/src/thorin/transform/plugin_execute.cpp @@ -15,7 +15,10 @@ void plugin_execute(World& world) { while (true) { plugin_intrinsics.clear(); - for (auto cont : world.copy_continuations()) { + for (auto def : world.defs()) { + auto cont = def->isa_nom(); + if (!cont) continue; + if (cont->is_intrinsic() && cont->intrinsic() == Intrinsic::Plugin) { plugin_intrinsics.push_back(cont); } From 84fe231c0bb15315e2182a6f0db8d20e86ba5e40 Mon Sep 17 00:00:00 2001 From: Matthias Kurtenacker Date: Wed, 5 Jul 2023 13:32:46 +0200 Subject: [PATCH 15/51] Clearification on app nodes and world.cleanup(). --- src/thorin/transform/plugin_execute.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/thorin/transform/plugin_execute.cpp b/src/thorin/transform/plugin_execute.cpp index 9c81356a8..580fae468 100644 --- a/src/thorin/transform/plugin_execute.cpp +++ b/src/thorin/transform/plugin_execute.cpp @@ -50,6 +50,7 @@ void plugin_execute(World& world) { } auto app = use.def()->as(); + assert(app->callee() == cont); if (app->num_uses() == 0) { continue; @@ -72,7 +73,7 @@ void plugin_execute(World& world) { break; } - world.cleanup(); //Warning: This must not change the world, there are still references to intrinsics being maintained here. + world.cleanup(); } world.mark_pe_done(false); From c4423a0e04a0dbbbcf01c7e0b42da78d43b94487 Mon Sep 17 00:00:00 2001 From: Matthias Kurtenacker Date: Wed, 5 Jul 2023 14:56:28 +0200 Subject: [PATCH 16/51] Change plugin sorting comparisson to be asymetric. --- src/thorin/transform/plugin_execute.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/thorin/transform/plugin_execute.cpp b/src/thorin/transform/plugin_execute.cpp index 580fae468..054a7c41f 100644 --- a/src/thorin/transform/plugin_execute.cpp +++ b/src/thorin/transform/plugin_execute.cpp @@ -28,12 +28,12 @@ void plugin_execute(World& world) { break; sort(plugin_intrinsics.begin(), plugin_intrinsics.end(), [&](const Continuation* a, const Continuation* b) { - const Continuation* depends = a; + const Continuation* depends = b; while (depends) { - if (depends == b) return false; depends = depends->attributes().depends; + if (a == depends) return true; } - return true; + return false; }); for (auto cont : plugin_intrinsics) { From 3d73ba675a28d82bb05c3ade6684f51b6a2f0b5f Mon Sep 17 00:00:00 2001 From: Matthias Kurtenacker Date: Fri, 19 Apr 2024 13:33:50 +0200 Subject: [PATCH 17/51] Do not build nvvm in debug mode. It produces broken ptx in some cases. --- src/thorin/be/llvm/nvvm.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/thorin/be/llvm/nvvm.cpp b/src/thorin/be/llvm/nvvm.cpp index 88c488da8..9e630cdb4 100644 --- a/src/thorin/be/llvm/nvvm.cpp +++ b/src/thorin/be/llvm/nvvm.cpp @@ -19,8 +19,8 @@ namespace thorin::llvm { -NVVMCodeGen::NVVMCodeGen(Thorin& thorin, const Cont2Config& kernel_config, int opt, bool debug) - : CodeGen(thorin, llvm::CallingConv::C, llvm::CallingConv::PTX_Device, llvm::CallingConv::PTX_Kernel, 0, debug) +NVVMCodeGen::NVVMCodeGen(Thorin& thorin, const Cont2Config& kernel_config, int /* opt */, bool /* debug */) + : CodeGen(thorin, llvm::CallingConv::C, llvm::CallingConv::PTX_Device, llvm::CallingConv::PTX_Kernel, 0, false) , kernel_config_(kernel_config) { auto triple = llvm::Triple(llvm::sys::getDefaultTargetTriple()); From a81f7fc9e3e9e57249ed3c730e5367861de19edf Mon Sep 17 00:00:00 2001 From: Matthias Kurtenacker Date: Tue, 31 Jan 2023 15:26:31 +0100 Subject: [PATCH 18/51] Support plugin execution. Very limited capabilities for now. --- src/thorin/CMakeLists.txt | 14 ++++--- src/thorin/continuation.cpp | 12 ++++++ src/thorin/continuation.h | 1 + src/thorin/rec_stream.cpp | 3 ++ src/thorin/transform/closure_conversion.cpp | 2 +- src/thorin/transform/plugin_execute.cpp | 45 +++++++++++++++++++++ src/thorin/transform/plugin_execute.h | 12 ++++++ src/thorin/world.cpp | 35 ++++++++++++++++ src/thorin/world.h | 4 ++ 9 files changed, 121 insertions(+), 7 deletions(-) create mode 100644 src/thorin/transform/plugin_execute.cpp create mode 100644 src/thorin/transform/plugin_execute.h diff --git a/src/thorin/CMakeLists.txt b/src/thorin/CMakeLists.txt index 15817401d..041aaead2 100644 --- a/src/thorin/CMakeLists.txt +++ b/src/thorin/CMakeLists.txt @@ -47,6 +47,10 @@ set(THORIN_SOURCES transform/codegen_prepare.cpp transform/dead_load_opt.cpp transform/dead_load_opt.h + transform/hls_channels.cpp + transform/hls_channels.h + transform/hls_kernel_launch.h + transform/hls_kernel_launch.cpp transform/hoist_enters.cpp transform/hoist_enters.h transform/flatten_tuples.cpp @@ -59,18 +63,16 @@ set(THORIN_SOURCES transform/lift_builtins.h transform/mangle.cpp transform/mangle.h - transform/resolve_loads.cpp - transform/resolve_loads.h transform/partial_evaluation.cpp transform/partial_evaluation.h transform/rewrite.cpp transform/rewrite.h + transform/plugin_execute.cpp + transform/plugin_execute.h + transform/resolve_loads.cpp + transform/resolve_loads.h transform/split_slots.cpp transform/split_slots.h - transform/hls_channels.cpp - transform/hls_channels.h - transform/hls_kernel_launch.h - transform/hls_kernel_launch.cpp util/array.h util/cast.h util/hash.h diff --git a/src/thorin/continuation.cpp b/src/thorin/continuation.cpp index 9001206b4..7fb708537 100644 --- a/src/thorin/continuation.cpp +++ b/src/thorin/continuation.cpp @@ -59,6 +59,18 @@ bool App::verify() const { return true; } +void App::jump(const Def* callee, Defs args, Debug dbg) { + unset_ops(); + resize(args.size() + 1); + + set_op(0, callee); + for (int i = 0, e = args.size(); i < e; i++) { + set_op(i + 1, args[i]); + } + + verify(); +} + //------------------------------------------------------------------------------ Filter::Filter(World& world, const Defs defs, Debug dbg) : Def(world, Node_Filter, world.bottom_type(), defs, dbg) {} diff --git a/src/thorin/continuation.h b/src/thorin/continuation.h index 73e47e36a..e0f5c5f4d 100644 --- a/src/thorin/continuation.h +++ b/src/thorin/continuation.h @@ -118,6 +118,7 @@ enum class Intrinsic : uint8_t { Branch, ///< branch(mem, cond, T, F). Match, ///< match(mem, val, otherwise, (case1, cont1), (case2, cont2), ...) PeInfo, ///< Partial evaluation debug info. + Plugin, ///< Some plugin derived intrinsic. Indentified by its name. EndScope ///< Dummy function which marks the end of a @p Scope. }; diff --git a/src/thorin/rec_stream.cpp b/src/thorin/rec_stream.cpp index cb7c5ecac..64930872b 100644 --- a/src/thorin/rec_stream.cpp +++ b/src/thorin/rec_stream.cpp @@ -63,6 +63,9 @@ void RecStreamer::run() { s.fmt("// free frontier: {, }\n", scope.free_frontier()); } + if (cont->is_intrinsic() && cont->intrinsic() == Intrinsic::Plugin) + s.fmt("plugin "); + if (cont->has_body()) { std::vector param_names; for (auto param : cont->params()) param_names.push_back(param->unique_name()); diff --git a/src/thorin/transform/closure_conversion.cpp b/src/thorin/transform/closure_conversion.cpp index 2048d77a5..d92490ab9 100644 --- a/src/thorin/transform/closure_conversion.cpp +++ b/src/thorin/transform/closure_conversion.cpp @@ -74,7 +74,7 @@ class ClosureConversion { } // prevent conversion of calls to vectorize() or cuda(), but allow graph intrinsics - if (!callee || !callee->is_intrinsic()) { + if (!callee || !callee->is_intrinsic() || callee->intrinsic() == Intrinsic::Plugin) { Array new_args(body->num_args()); for (size_t i = 0, e = body->num_args(); i != e; ++i) new_args[i] = convert_def(body->arg(i)); diff --git a/src/thorin/transform/plugin_execute.cpp b/src/thorin/transform/plugin_execute.cpp new file mode 100644 index 000000000..595edcfc5 --- /dev/null +++ b/src/thorin/transform/plugin_execute.cpp @@ -0,0 +1,45 @@ +#include "thorin/world.h" +#include "thorin/transform/plugin_execute.h" +#include "thorin/analyses/scope.h" + +namespace thorin { + +void plugin_execute(World& world) { + world.VLOG("start plugin_execute"); + for (auto cont : world.copy_continuations()) { + if (cont->is_intrinsic() && cont->intrinsic() == Intrinsic::Plugin) { + void * function_handle = world.search_plugin_function(cont->name()); + if (!function_handle) { + world.ELOG("Plugin function not found for: {}", cont->name()); + continue; + } + auto plugin_function = (void*(*)(void*)) function_handle; + + for (auto use : cont->copy_uses()) { + if (!use.def()->isa()) { + continue; + } + + auto app = const_cast(use.def()->as()); + + void * input = (void*) app->arg(1); + void * output = plugin_function(input); + if (input != output) { + world.ELOG("Plugin changed stuff"); + } + + Continuation* y = world.continuation(world.fn_type({world.mem_type(), world.fn_type({world.mem_type()})})); + y->jump(y->param(1), {y->param(0)}); + + Continuation* x = world.continuation(world.fn_type({world.mem_type()})); + x->jump(app->arg(2), {x->param(0), y}); + + app->jump((Def*)output, {app->arg(0), x}); + } + } + } + + world.VLOG("end plugin_execute"); +} + +} diff --git a/src/thorin/transform/plugin_execute.h b/src/thorin/transform/plugin_execute.h new file mode 100644 index 000000000..e66d547f4 --- /dev/null +++ b/src/thorin/transform/plugin_execute.h @@ -0,0 +1,12 @@ +#ifndef THORIN_TRANSFORM_PLUGIN_EXECUTE_H +#define THORIN_TRANSFORM_PLUGIN_EXECUTE_H + +namespace thorin { + +class World; + +void plugin_execute(World&); + +} + +#endif diff --git a/src/thorin/world.cpp b/src/thorin/world.cpp index 96b3322f5..2d7ebd056 100644 --- a/src/thorin/world.cpp +++ b/src/thorin/world.cpp @@ -10,6 +10,7 @@ #endif #include +#include #if THORIN_ENABLE_CREATION_CONTEXT #include @@ -25,6 +26,7 @@ #include "thorin/type.h" #include "thorin/analyses/scope.h" #include "thorin/analyses/verify.h" +#include "thorin/transform/plugin_execute.h" #include "thorin/transform/closure_conversion.h" #include "thorin/transform/codegen_prepare.h" #include "thorin/transform/dead_load_opt.h" @@ -1313,6 +1315,10 @@ void Thorin::opt() { RUN_PASS(while (partial_evaluation(world(), true))); // lower2cff RUN_PASS(flatten_tuples(*this)) RUN_PASS(split_slots(*this)) + if (world().plugin_handles.size() > 0) { + RUN_PASS(plugin_execute(world())); + RUN_PASS(cleanup()); + } RUN_PASS(closure_conversion(world())) RUN_PASS(lift_builtins(*this)) RUN_PASS(inliner(*this)) @@ -1338,5 +1344,34 @@ bool Thorin::ensure_stack_size(size_t new_size) { #endif } +bool World::register_plugin(std::string plugin_name) { + void *handle = dlopen(plugin_name.c_str(), RTLD_LAZY); + if (!handle) { + ELOG("Error loading plugin {}: {}", plugin_name, dlerror()); + ELOG("Is plugin contained in LD_LIBRARY_PATH?"); + return false; + } + dlerror(); + + void (*initfunc)(void); + char *error; + initfunc = (void(*)())(dlsym(handle, "init")); + if ((error = dlerror()) != NULL) { + ILOG("Plugin {} did not supply an init function", plugin_name); + } else { + initfunc(); + } + plugin_handles.push_back(handle); + return true; +} + +void * World::search_plugin_function(std::string function_name) { + for (auto plugin : plugin_handles) { + if (void * plugin_function = dlsym(plugin, function_name.c_str())) { + return plugin_function; + } + } + return nullptr; +} } diff --git a/src/thorin/world.h b/src/thorin/world.h index 547dfdca6..20b18e5a4 100644 --- a/src/thorin/world.h +++ b/src/thorin/world.h @@ -339,6 +339,9 @@ class World : public Streamable { static std::string colorize(const std::string& str, int color); //@} + bool register_plugin(std::string plugin_name); + void * search_plugin_function(std::string function_name); + private: const Param* param(const Type* type, const Continuation*, size_t index, Debug dbg); const Def* try_fold_aggregate(const Aggregate*); @@ -387,6 +390,7 @@ class World : public Streamable { } data_; TypeTable types_; + std::vector plugin_handles; std::shared_ptr stream_; From 51a0ae043bdba9e95297dc10dc0dbfce387a4fa7 Mon Sep 17 00:00:00 2001 From: Matthias Kurtenacker Date: Thu, 28 Jul 2022 12:35:30 +0200 Subject: [PATCH 19/51] Support for multiple input arguments. --- src/thorin/transform/plugin_execute.cpp | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/thorin/transform/plugin_execute.cpp b/src/thorin/transform/plugin_execute.cpp index 595edcfc5..272cb7436 100644 --- a/src/thorin/transform/plugin_execute.cpp +++ b/src/thorin/transform/plugin_execute.cpp @@ -6,6 +6,7 @@ namespace thorin { void plugin_execute(World& world) { world.VLOG("start plugin_execute"); + for (auto cont : world.copy_continuations()) { if (cont->is_intrinsic() && cont->intrinsic() == Intrinsic::Plugin) { void * function_handle = world.search_plugin_function(cont->name()); @@ -13,7 +14,7 @@ void plugin_execute(World& world) { world.ELOG("Plugin function not found for: {}", cont->name()); continue; } - auto plugin_function = (void*(*)(void*)) function_handle; + auto plugin_function = (void*(*)(size_t, void**)) function_handle; for (auto use : cont->copy_uses()) { if (!use.def()->isa()) { @@ -22,17 +23,19 @@ void plugin_execute(World& world) { auto app = const_cast(use.def()->as()); - void * input = (void*) app->arg(1); - void * output = plugin_function(input); - if (input != output) { - world.ELOG("Plugin changed stuff"); + Def* input_array[app->num_args() - 2]; + for (size_t i = 1, e = app->num_args() - 1; i < e; i++) { + Def * input = const_cast(app->arg(i)); + input_array[i - 1] = input; } + void * output = plugin_function(app->num_args() - 2, (void **)input_array); + Continuation* y = world.continuation(world.fn_type({world.mem_type(), world.fn_type({world.mem_type()})})); y->jump(y->param(1), {y->param(0)}); Continuation* x = world.continuation(world.fn_type({world.mem_type()})); - x->jump(app->arg(2), {x->param(0), y}); + x->jump(app->arg(app->num_args() - 1), {x->param(0), y}); app->jump((Def*)output, {app->arg(0), x}); } From aa74f039ad228d96dbdb59ad0b3dd8233ea4971b Mon Sep 17 00:00:00 2001 From: Matthias Kurtenacker Date: Tue, 13 Sep 2022 16:26:27 +0200 Subject: [PATCH 20/51] Use RTLD_GLOBAL to enable loading dependent plugins. --- src/thorin/world.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/thorin/world.cpp b/src/thorin/world.cpp index 2d7ebd056..c51731ebb 100644 --- a/src/thorin/world.cpp +++ b/src/thorin/world.cpp @@ -1345,7 +1345,7 @@ bool Thorin::ensure_stack_size(size_t new_size) { } bool World::register_plugin(std::string plugin_name) { - void *handle = dlopen(plugin_name.c_str(), RTLD_LAZY); + void *handle = dlopen(plugin_name.c_str(), RTLD_LAZY | RTLD_GLOBAL); if (!handle) { ELOG("Error loading plugin {}: {}", plugin_name, dlerror()); ELOG("Is plugin contained in LD_LIBRARY_PATH?"); From 7d37d32d6fba64954963b93757bae019e87ac8da Mon Sep 17 00:00:00 2001 From: Matthias Kurtenacker Date: Fri, 30 Sep 2022 16:01:47 +0200 Subject: [PATCH 21/51] Add plugin dependencies + option to return values from plugin intrinsics. --- src/thorin/continuation.h | 1 + src/thorin/rec_stream.cpp | 5 +- src/thorin/transform/plugin_execute.cpp | 62 +++++++++++++++---------- src/thorin/transform/rewrite.cpp | 6 ++- 4 files changed, 48 insertions(+), 26 deletions(-) diff --git a/src/thorin/continuation.h b/src/thorin/continuation.h index e0f5c5f4d..4fc3699b8 100644 --- a/src/thorin/continuation.h +++ b/src/thorin/continuation.h @@ -132,6 +132,7 @@ class Continuation : public Def { struct Attributes { Intrinsic intrinsic = Intrinsic::None; CC cc = CC::Thorin; + const Continuation* depends = nullptr; Attributes(Intrinsic intrinsic) : intrinsic(intrinsic) {} Attributes(CC cc = CC::Thorin) : cc(cc) {} diff --git a/src/thorin/rec_stream.cpp b/src/thorin/rec_stream.cpp index 64930872b..53b0004b5 100644 --- a/src/thorin/rec_stream.cpp +++ b/src/thorin/rec_stream.cpp @@ -63,8 +63,11 @@ void RecStreamer::run() { s.fmt("// free frontier: {, }\n", scope.free_frontier()); } - if (cont->is_intrinsic() && cont->intrinsic() == Intrinsic::Plugin) + if (cont->is_intrinsic() && cont->intrinsic() == Intrinsic::Plugin) { s.fmt("plugin "); + if (cont->attributes().depends) + s.fmt("[depends {}] ", cont->attributes().depends->unique_name()); + } if (cont->has_body()) { std::vector param_names; diff --git a/src/thorin/transform/plugin_execute.cpp b/src/thorin/transform/plugin_execute.cpp index 272cb7436..636731776 100644 --- a/src/thorin/transform/plugin_execute.cpp +++ b/src/thorin/transform/plugin_execute.cpp @@ -2,43 +2,57 @@ #include "thorin/transform/plugin_execute.h" #include "thorin/analyses/scope.h" +#include + namespace thorin { void plugin_execute(World& world) { world.VLOG("start plugin_execute"); + std::vector plugin_intrinsics; + for (auto cont : world.copy_continuations()) { if (cont->is_intrinsic() && cont->intrinsic() == Intrinsic::Plugin) { - void * function_handle = world.search_plugin_function(cont->name()); - if (!function_handle) { - world.ELOG("Plugin function not found for: {}", cont->name()); - continue; + plugin_intrinsics.push_back(cont); + if (cont->attributes().depends) { + cont->dump(); + cont->attributes().depends->dump(); } - auto plugin_function = (void*(*)(size_t, void**)) function_handle; - - for (auto use : cont->copy_uses()) { - if (!use.def()->isa()) { - continue; - } - - auto app = const_cast(use.def()->as()); - - Def* input_array[app->num_args() - 2]; - for (size_t i = 1, e = app->num_args() - 1; i < e; i++) { - Def * input = const_cast(app->arg(i)); - input_array[i - 1] = input; - } + } + } - void * output = plugin_function(app->num_args() - 2, (void **)input_array); + sort(plugin_intrinsics.begin(), plugin_intrinsics.end(), [&](const Continuation* a, const Continuation* b) { + const Continuation* depends = a; + while (depends) { + if (depends == b) return false; + depends = depends->attributes().depends; + } + return true; + }); + + for (auto cont : plugin_intrinsics) { + void * function_handle = world.search_plugin_function(cont->name()); + if (!function_handle) { + world.ELOG("Plugin function not found for: {}", cont->name()); + continue; + } + auto plugin_function = (void*(*)(size_t, void**)) function_handle; - Continuation* y = world.continuation(world.fn_type({world.mem_type(), world.fn_type({world.mem_type()})})); - y->jump(y->param(1), {y->param(0)}); + for (auto use : cont->copy_uses()) { + if (!use.def()->isa()) { + continue; + } - Continuation* x = world.continuation(world.fn_type({world.mem_type()})); - x->jump(app->arg(app->num_args() - 1), {x->param(0), y}); + auto app = const_cast(use.def()->as()); - app->jump((Def*)output, {app->arg(0), x}); + Def* input_array[app->num_args() - 2]; + for (size_t i = 1, e = app->num_args() - 1; i < e; i++) { + Def * input = const_cast(app->arg(i)); + input_array[i - 1] = input; } + + void * output = plugin_function(app->num_args() - 2, (void **)input_array); + app->jump(app->arg(app->num_args() - 1), {app->arg(0), (Def*)output}); } } diff --git a/src/thorin/transform/rewrite.cpp b/src/thorin/transform/rewrite.cpp index ac352568f..faadaf083 100644 --- a/src/thorin/transform/rewrite.cpp +++ b/src/thorin/transform/rewrite.cpp @@ -48,6 +48,10 @@ const Def* Rewriter::rewrite(const Def* odef) { if (odef->isa_nom()) { stub = odef->stub(*this, ntype); insert(odef, stub); + + if (auto ocont = odef->isa_nom()) + if (ocont->attributes().depends) + stub->as_nom()->attributes().depends = instantiate(ocont->attributes().depends)->as(); } if (odef->isa_structural()) { @@ -67,4 +71,4 @@ const Def* Rewriter::rewrite(const Def* odef) { } } -} \ No newline at end of file +} From 4aed9b0daa36e1ac5c4fc7e2b2fe62b4cf65ed44 Mon Sep 17 00:00:00 2001 From: Matthias Kurtenacker Date: Fri, 20 Jan 2023 13:56:31 +0100 Subject: [PATCH 22/51] Partial Evaluation between plugin steps. --- src/thorin/transform/plugin_execute.cpp | 87 +++++++++++++++---------- src/thorin/transform/plugin_execute.h | 4 +- src/thorin/world.cpp | 14 ++-- src/thorin/world.h | 7 +- 4 files changed, 66 insertions(+), 46 deletions(-) diff --git a/src/thorin/transform/plugin_execute.cpp b/src/thorin/transform/plugin_execute.cpp index 636731776..25061ad46 100644 --- a/src/thorin/transform/plugin_execute.cpp +++ b/src/thorin/transform/plugin_execute.cpp @@ -1,61 +1,82 @@ #include "thorin/world.h" #include "thorin/transform/plugin_execute.h" +#include "thorin/transform/partial_evaluation.h" #include "thorin/analyses/scope.h" #include namespace thorin { -void plugin_execute(World& world) { +void plugin_execute(Thorin& thorin) { + World& world = thorin.world(); world.VLOG("start plugin_execute"); std::vector plugin_intrinsics; - for (auto cont : world.copy_continuations()) { - if (cont->is_intrinsic() && cont->intrinsic() == Intrinsic::Plugin) { - plugin_intrinsics.push_back(cont); - if (cont->attributes().depends) { - cont->dump(); - cont->attributes().depends->dump(); - } - } - } + while (true) { + plugin_intrinsics.clear(); - sort(plugin_intrinsics.begin(), plugin_intrinsics.end(), [&](const Continuation* a, const Continuation* b) { - const Continuation* depends = a; - while (depends) { - if (depends == b) return false; - depends = depends->attributes().depends; + for (auto cont : world.copy_continuations()) { + if (cont->is_intrinsic() && cont->intrinsic() == Intrinsic::Plugin) { + plugin_intrinsics.push_back(cont); } - return true; - }); - - for (auto cont : plugin_intrinsics) { - void * function_handle = world.search_plugin_function(cont->name()); - if (!function_handle) { - world.ELOG("Plugin function not found for: {}", cont->name()); - continue; } - auto plugin_function = (void*(*)(size_t, void**)) function_handle; - for (auto use : cont->copy_uses()) { - if (!use.def()->isa()) { + if (plugin_intrinsics.empty()) + break; + + sort(plugin_intrinsics.begin(), plugin_intrinsics.end(), [&](const Continuation* a, const Continuation* b) { + const Continuation* depends = a; + while (depends) { + if (depends == b) return false; + depends = depends->attributes().depends; + } + return true; + }); + + for (auto cont : plugin_intrinsics) { + void * function_handle = thorin.search_plugin_function(cont->name()); + if (!function_handle) { + world.ELOG("Plugin function not found for: {}", cont->name()); continue; } + auto plugin_function = (void*(*)(size_t, void**)) function_handle; + + bool evaluated = false; + for (auto use : cont->copy_uses()) { + if (!use.def()->isa()) { + continue; + } - auto app = const_cast(use.def()->as()); + auto app = const_cast(use.def()->as()); - Def* input_array[app->num_args() - 2]; - for (size_t i = 1, e = app->num_args() - 1; i < e; i++) { - Def * input = const_cast(app->arg(i)); - input_array[i - 1] = input; + if (app->num_uses() == 0) { + continue; + } + + Def* input_array[app->num_args() - 2]; + for (size_t i = 1, e = app->num_args() - 1; i < e; i++) { + Def * input = const_cast(app->arg(i)); + input_array[i - 1] = input; + } + + void * output = plugin_function(app->num_args() - 2, (void **)input_array); + app->jump(app->arg(app->num_args() - 1), {app->arg(0), (Def*)output}); + + //partial_evaluation(world); //TODO: Some form of cleanup would be advisable here. + evaluated = true; } - void * output = plugin_function(app->num_args() - 2, (void **)input_array); - app->jump(app->arg(app->num_args() - 1), {app->arg(0), (Def*)output}); + if (evaluated) + break; } + + thorin.cleanup(); //Warning: This must not change the world, there are still references to intrinsics being maintained here. } + world.mark_pe_done(false); + thorin.cleanup(); + world.VLOG("end plugin_execute"); } diff --git a/src/thorin/transform/plugin_execute.h b/src/thorin/transform/plugin_execute.h index e66d547f4..9f37fc224 100644 --- a/src/thorin/transform/plugin_execute.h +++ b/src/thorin/transform/plugin_execute.h @@ -3,9 +3,9 @@ namespace thorin { -class World; +class Thorin; -void plugin_execute(World&); +void plugin_execute(Thorin&); } diff --git a/src/thorin/world.cpp b/src/thorin/world.cpp index c51731ebb..17009850f 100644 --- a/src/thorin/world.cpp +++ b/src/thorin/world.cpp @@ -1315,8 +1315,8 @@ void Thorin::opt() { RUN_PASS(while (partial_evaluation(world(), true))); // lower2cff RUN_PASS(flatten_tuples(*this)) RUN_PASS(split_slots(*this)) - if (world().plugin_handles.size() > 0) { - RUN_PASS(plugin_execute(world())); + if (plugin_handles.size() > 0) { + RUN_PASS(plugin_execute(*this)); RUN_PASS(cleanup()); } RUN_PASS(closure_conversion(world())) @@ -1344,11 +1344,11 @@ bool Thorin::ensure_stack_size(size_t new_size) { #endif } -bool World::register_plugin(std::string plugin_name) { +bool Thorin::register_plugin(std::string plugin_name) { void *handle = dlopen(plugin_name.c_str(), RTLD_LAZY | RTLD_GLOBAL); if (!handle) { - ELOG("Error loading plugin {}: {}", plugin_name, dlerror()); - ELOG("Is plugin contained in LD_LIBRARY_PATH?"); + world().ELOG("Error loading plugin {}: {}", plugin_name, dlerror()); + world().ELOG("Is plugin contained in LD_LIBRARY_PATH?"); return false; } dlerror(); @@ -1357,7 +1357,7 @@ bool World::register_plugin(std::string plugin_name) { char *error; initfunc = (void(*)())(dlsym(handle, "init")); if ((error = dlerror()) != NULL) { - ILOG("Plugin {} did not supply an init function", plugin_name); + world().ILOG("Plugin {} did not supply an init function", plugin_name); } else { initfunc(); } @@ -1366,7 +1366,7 @@ bool World::register_plugin(std::string plugin_name) { return true; } -void * World::search_plugin_function(std::string function_name) { +void * Thorin::search_plugin_function(std::string function_name) { for (auto plugin : plugin_handles) { if (void * plugin_function = dlsym(plugin, function_name.c_str())) { return plugin_function; diff --git a/src/thorin/world.h b/src/thorin/world.h index 20b18e5a4..bca042294 100644 --- a/src/thorin/world.h +++ b/src/thorin/world.h @@ -339,9 +339,6 @@ class World : public Streamable { static std::string colorize(const std::string& str, int color); //@} - bool register_plugin(std::string plugin_name); - void * search_plugin_function(std::string function_name); - private: const Param* param(const Type* type, const Continuation*, size_t index, Debug dbg); const Def* try_fold_aggregate(const Aggregate*); @@ -390,7 +387,6 @@ class World : public Streamable { } data_; TypeTable types_; - std::vector plugin_handles; std::shared_ptr stream_; @@ -419,8 +415,11 @@ class Thorin { bool ensure_stack_size(size_t new_size); + bool register_plugin(std::string plugin_name); + void * search_plugin_function(std::string function_name); private: std::unique_ptr world_; + std::vector plugin_handles; }; } From df0ceacc510368661b06708c1314189be3c88871 Mon Sep 17 00:00:00 2001 From: Matthias Kurtenacker Date: Fri, 23 Jun 2023 16:22:47 +0200 Subject: [PATCH 23/51] Implement world.release to emit anydsl_release calls in backend, similar to alloc. --- src/thorin/be/llvm/amdgpu.h | 1 + src/thorin/be/llvm/cpu.h | 1 + src/thorin/be/llvm/llvm.cpp | 11 +++++++++++ src/thorin/be/llvm/llvm.h | 2 ++ src/thorin/be/llvm/nvvm.h | 1 + src/thorin/primop.cpp | 10 ++++++++++ src/thorin/primop.h | 14 ++++++++++++++ src/thorin/tables/nodetable.h | 1 + src/thorin/world.cpp | 4 ++++ src/thorin/world.h | 1 + 10 files changed, 46 insertions(+) diff --git a/src/thorin/be/llvm/amdgpu.h b/src/thorin/be/llvm/amdgpu.h index 277b8d595..2ac56a175 100644 --- a/src/thorin/be/llvm/amdgpu.h +++ b/src/thorin/be/llvm/amdgpu.h @@ -22,6 +22,7 @@ class AMDGPUCodeGen : public CodeGen { llvm::Value* emit_mathop(llvm::IRBuilder<>&, const MathOp*) override; llvm::Value* emit_reserve(llvm::IRBuilder<>&, const Continuation*) override; std::string get_alloc_name() const override { return "malloc"; } + std::string get_release_name() const override { return "free"; } const Cont2Config& kernel_config_; }; diff --git a/src/thorin/be/llvm/cpu.h b/src/thorin/be/llvm/cpu.h index 78db51cd9..73908f54e 100644 --- a/src/thorin/be/llvm/cpu.h +++ b/src/thorin/be/llvm/cpu.h @@ -13,6 +13,7 @@ class CPUCodeGen : public CodeGen { protected: std::string get_alloc_name() const override { return "anydsl_alloc"; } + std::string get_release_name() const override { return "anydsl_release"; } }; } diff --git a/src/thorin/be/llvm/llvm.cpp b/src/thorin/be/llvm/llvm.cpp index ecea16edd..b4796d05e 100644 --- a/src/thorin/be/llvm/llvm.cpp +++ b/src/thorin/be/llvm/llvm.cpp @@ -1013,6 +1013,9 @@ llvm::Value* CodeGen::emit_builder(llvm::IRBuilder<>& irbuilder, const Def* def) } else if (auto alloc = def->isa()) { emit_unsafe(alloc->mem()); return emit_alloc(irbuilder, alloc->alloced_type(), alloc->extra()); + } else if (auto release = def->isa()) { + emit_unsafe(release->mem()); + return emit_release(irbuilder, release->alloc()); } else if (auto slot = def->isa()) { return emit_alloca(irbuilder, convert(slot->type()->as()->pointee()), slot->unique_name()); } else if (auto vector = def->isa()) { @@ -1058,6 +1061,14 @@ llvm::Value* CodeGen::emit_alloc(llvm::IRBuilder<>& irbuilder, const Type* type, return irbuilder.CreatePointerCast(void_ptr, llvm::PointerType::get(context(), 0)); } +llvm::Value* CodeGen::emit_release(llvm::IRBuilder<>& irbuilder, const Def* alloc) { + auto llvm_release = runtime_->get(*this, get_release_name().c_str()); + llvm::Value* llvm_alloc = emit(alloc); + llvm::Value* release_args[] = { irbuilder.getInt32(0), llvm_alloc }; + irbuilder.CreateCall(llvm_release, release_args); + return nullptr; +} + llvm::AllocaInst* CodeGen::emit_alloca(llvm::IRBuilder<>& irbuilder, llvm::Type* type, const std::string& name) { // Emit the alloca in the entry block auto entry = &irbuilder.GetInsertBlock()->getParent()->getEntryBlock(); diff --git a/src/thorin/be/llvm/llvm.h b/src/thorin/be/llvm/llvm.h index 7d5414331..66f679f3a 100644 --- a/src/thorin/be/llvm/llvm.h +++ b/src/thorin/be/llvm/llvm.h @@ -74,6 +74,7 @@ class CodeGen : public thorin::CodeGen, public thorin::Emitter&, llvm::Type*, const std::string&); llvm::Value* emit_alloc (llvm::IRBuilder<>&, const Type*, const Def*); + llvm::Value* emit_release (llvm::IRBuilder<>&, const Def*); virtual void emit_fun_decl_hook(Continuation*, llvm::Function*) {} virtual llvm::Value* map_param(llvm::Function*, llvm::Argument* a, const Param*) { return a; } @@ -87,6 +88,7 @@ class CodeGen : public thorin::CodeGen, public thorin::Emitter&, const Continuation*, bool=false); virtual std::string get_alloc_name() const = 0; + virtual std::string get_release_name() const = 0; llvm::BasicBlock* cont2bb(Continuation* cont) { return cont2bb_[cont].first; } virtual llvm::Value* emit_global(const Global*); diff --git a/src/thorin/be/llvm/nvvm.h b/src/thorin/be/llvm/nvvm.h index 3c5b8596d..f00d20d08 100644 --- a/src/thorin/be/llvm/nvvm.h +++ b/src/thorin/be/llvm/nvvm.h @@ -33,6 +33,7 @@ class NVVMCodeGen : public CodeGen { llvm::Value* emit_global(const Global*) override; std::string get_alloc_name() const override { return "malloc"; } + std::string get_release_name() const override { return "free"; } private: llvm::Function* get_texture_handle_fun(llvm::IRBuilder<>&); diff --git a/src/thorin/primop.cpp b/src/thorin/primop.cpp index a1222f4fe..000e17fc3 100644 --- a/src/thorin/primop.cpp +++ b/src/thorin/primop.cpp @@ -112,6 +112,12 @@ Alloc::Alloc(World& world, const Type* type, const Def* mem, const Def* extra, D set_type(world.tuple_type({world.mem_type(), world.ptr_type(type)})); } +Release::Release(World& world, const Def* mem, const Def* alloc, Debug dbg) + : MemOp(world, Node_Release, nullptr, {mem, alloc}, dbg) +{ + set_type(world.mem_type()); +} + Load::Load(World& world, const Def* mem, const Def* ptr, Debug dbg) : Access(world, Node_Load, nullptr, {mem, ptr}, dbg) { @@ -223,6 +229,10 @@ const Def* Alloc::rebuild(World& w, const Type* t, Defs o) const { return w.alloc(t->as()->op(1)->as()->pointee(), o[0], o[1], debug()); } +const Def* Release::rebuild(World& w, const Type* t, Defs o) const { + return w.release(o[0], o[1], debug()); +} + const Def* Assembly::rebuild(World& w, const Type* t, Defs o) const { return w.assembly(t, o, asm_template(), output_constraints(), input_constraints(), clobbers(), flags(), debug()); } diff --git a/src/thorin/primop.h b/src/thorin/primop.h index 5b2a7eb33..487865cb4 100644 --- a/src/thorin/primop.h +++ b/src/thorin/primop.h @@ -585,6 +585,20 @@ class Alloc : public MemOp { friend class World; }; +class Release : public MemOp { +private: + Release(World& world, const Def* mem, const Def* alloc, Debug dbg); + +public: + const Def* alloc() const { return op(1); } + +private: + const Def* rebuild(World&, const Type*, Defs) const override; + + friend class World; +}; + + /// Base class for @p Load and @p Store. class Access : public MemOp { protected: diff --git a/src/thorin/tables/nodetable.h b/src/thorin/tables/nodetable.h index 8c17d04dc..55e0bcbd0 100644 --- a/src/thorin/tables/nodetable.h +++ b/src/thorin/tables/nodetable.h @@ -12,6 +12,7 @@ THORIN_NODE(BlobPtr, mem_blob) // MemOp THORIN_NODE(Alloc, alloc) + THORIN_NODE(Release, release) // Access THORIN_NODE(Load, load) THORIN_NODE(Store, store) diff --git a/src/thorin/world.cpp b/src/thorin/world.cpp index 17009850f..a98ebc445 100644 --- a/src/thorin/world.cpp +++ b/src/thorin/world.cpp @@ -1055,6 +1055,10 @@ const Def* World::alloc(const Type* type, const Def* mem, const Def* extra, Debu return cse(new Alloc(*this, type, mem, extra, dbg)); } +const Def* World::release(const Def* mem, const Def* alloc, Debug dbg) { + return cse(new Release(*this, mem, alloc, dbg)); +} + const Def* World::global(const Def* init, bool is_mutable, Debug dbg) { return cse(new Global(*this, init, is_mutable, dbg)); } diff --git a/src/thorin/world.h b/src/thorin/world.h index bca042294..33c58ff4b 100644 --- a/src/thorin/world.h +++ b/src/thorin/world.h @@ -248,6 +248,7 @@ class World : public Streamable { const Def* slot(const Type* type, const Def* frame, Debug dbg = {}) { return cse(new Slot(*this, type, frame, dbg)); } const Def* alloc(const Type* type, const Def* mem, const Def* extra, Debug dbg = {}); const Def* alloc(const Type* type, const Def* mem, Debug dbg = {}) { return alloc(type, mem, literal_qu64(0, dbg), dbg); } + const Def* release(const Def* mem, const Def* alloc, Debug dbg = {}); const Def* global(const Def* init, bool is_mutable = true, Debug dbg = {}); const Def* global_immutable_string(const std::string& str, Debug dbg = {}); const Def* lea(const Def* ptr, const Def* index, Debug dbg); From d4c4e1300765325b321b38d4b14021877c14dad1 Mon Sep 17 00:00:00 2001 From: Matthias Kurtenacker Date: Mon, 26 Jun 2023 17:00:58 +0200 Subject: [PATCH 24/51] Bugfix: llvm 14 needs pointer cast for release. --- src/thorin/be/llvm/llvm.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/thorin/be/llvm/llvm.cpp b/src/thorin/be/llvm/llvm.cpp index b4796d05e..8e790eb53 100644 --- a/src/thorin/be/llvm/llvm.cpp +++ b/src/thorin/be/llvm/llvm.cpp @@ -1064,7 +1064,8 @@ llvm::Value* CodeGen::emit_alloc(llvm::IRBuilder<>& irbuilder, const Type* type, llvm::Value* CodeGen::emit_release(llvm::IRBuilder<>& irbuilder, const Def* alloc) { auto llvm_release = runtime_->get(*this, get_release_name().c_str()); llvm::Value* llvm_alloc = emit(alloc); - llvm::Value* release_args[] = { irbuilder.getInt32(0), llvm_alloc }; + llvm::Value* cast_alloc = irbuilder.CreatePointerCast(llvm_alloc, irbuilder.getInt8PtrTy()); + llvm::Value* release_args[] = { irbuilder.getInt32(0), cast_alloc }; irbuilder.CreateCall(llvm_release, release_args); return nullptr; } From e6cb9779e3109bd370a37c53eda8c84cafe3e3a0 Mon Sep 17 00:00:00 2001 From: Stefan Lemme Date: Tue, 27 Jun 2023 12:06:35 +0200 Subject: [PATCH 25/51] Fix MSVC build by disabling plugins --- src/thorin/CMakeLists.txt | 4 ++++ src/thorin/transform/plugin_execute.cpp | 4 ++-- src/thorin/world.cpp | 11 +++++++++++ 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/thorin/CMakeLists.txt b/src/thorin/CMakeLists.txt index 041aaead2..1595928d8 100644 --- a/src/thorin/CMakeLists.txt +++ b/src/thorin/CMakeLists.txt @@ -152,3 +152,7 @@ endif() if(THORIN_ENABLE_JSON) target_link_libraries(thorin PRIVATE nlohmann_json::nlohmann_json) endif() + +if(NOT MSVC) + target_link_libraries(thorin PRIVATE dl) +endif() diff --git a/src/thorin/transform/plugin_execute.cpp b/src/thorin/transform/plugin_execute.cpp index 25061ad46..656b20bd1 100644 --- a/src/thorin/transform/plugin_execute.cpp +++ b/src/thorin/transform/plugin_execute.cpp @@ -54,13 +54,13 @@ void plugin_execute(Thorin& thorin) { continue; } - Def* input_array[app->num_args() - 2]; + std::vector input_array(app->num_args() - 2); for (size_t i = 1, e = app->num_args() - 1; i < e; i++) { Def * input = const_cast(app->arg(i)); input_array[i - 1] = input; } - void * output = plugin_function(app->num_args() - 2, (void **)input_array); + void * output = plugin_function(app->num_args() - 2, (void **)input_array.data()); app->jump(app->arg(app->num_args() - 1), {app->arg(0), (Def*)output}); //partial_evaluation(world); //TODO: Some form of cleanup would be advisable here. diff --git a/src/thorin/world.cpp b/src/thorin/world.cpp index a98ebc445..e15c0b561 100644 --- a/src/thorin/world.cpp +++ b/src/thorin/world.cpp @@ -10,7 +10,11 @@ #endif #include +#ifdef _MSC_VER +#include +#else #include +#endif #if THORIN_ENABLE_CREATION_CONTEXT #include @@ -1349,6 +1353,9 @@ bool Thorin::ensure_stack_size(size_t new_size) { } bool Thorin::register_plugin(std::string plugin_name) { +#ifdef _MSC_VER + return false; +#else // _MSC_VER void *handle = dlopen(plugin_name.c_str(), RTLD_LAZY | RTLD_GLOBAL); if (!handle) { world().ELOG("Error loading plugin {}: {}", plugin_name, dlerror()); @@ -1368,14 +1375,18 @@ bool Thorin::register_plugin(std::string plugin_name) { plugin_handles.push_back(handle); return true; +#endif // _MSC_VER } void * Thorin::search_plugin_function(std::string function_name) { +#ifdef _MSC_VER +#else // _MSC_VER for (auto plugin : plugin_handles) { if (void * plugin_function = dlsym(plugin, function_name.c_str())) { return plugin_function; } } +#endif // _MSC_VER return nullptr; } } From afb7d636eb0aa669ef0c04d5b63ea52ca9ed366f Mon Sep 17 00:00:00 2001 From: Stefan Lemme Date: Tue, 27 Jun 2023 12:26:06 +0200 Subject: [PATCH 26/51] use proper CMAKE_DL_LIBS variable --- src/thorin/CMakeLists.txt | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/thorin/CMakeLists.txt b/src/thorin/CMakeLists.txt index 1595928d8..6abf54430 100644 --- a/src/thorin/CMakeLists.txt +++ b/src/thorin/CMakeLists.txt @@ -153,6 +153,4 @@ if(THORIN_ENABLE_JSON) target_link_libraries(thorin PRIVATE nlohmann_json::nlohmann_json) endif() -if(NOT MSVC) - target_link_libraries(thorin PRIVATE dl) -endif() +target_link_libraries(thorin PRIVATE ${CMAKE_DL_LIBS}) From 6c704f02808e57b5a934cfa71803f1500aed52db Mon Sep 17 00:00:00 2001 From: Matthias Kurtenacker Date: Thu, 29 Jun 2023 12:20:34 +0200 Subject: [PATCH 27/51] Support return nullptr in plugins for void returns. --- src/thorin/transform/plugin_execute.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/thorin/transform/plugin_execute.cpp b/src/thorin/transform/plugin_execute.cpp index 656b20bd1..1669c2e85 100644 --- a/src/thorin/transform/plugin_execute.cpp +++ b/src/thorin/transform/plugin_execute.cpp @@ -61,7 +61,10 @@ void plugin_execute(Thorin& thorin) { } void * output = plugin_function(app->num_args() - 2, (void **)input_array.data()); - app->jump(app->arg(app->num_args() - 1), {app->arg(0), (Def*)output}); + if (output) + app->jump(app->arg(app->num_args() - 1), {app->arg(0), (Def*)output}); + else + app->jump(app->arg(app->num_args() - 1), {app->arg(0)}); //partial_evaluation(world); //TODO: Some form of cleanup would be advisable here. evaluated = true; From e45ae79564a19f1d9abedae5159d3ab024e9697b Mon Sep 17 00:00:00 2001 From: Michael Kenzel Date: Tue, 4 Jul 2023 18:22:57 +0200 Subject: [PATCH 28/51] pass World and App to plugin function --- src/thorin/transform/plugin_execute.cpp | 13 +++---------- src/thorin/world.cpp | 15 +++++++-------- src/thorin/world.h | 9 +++++++-- 3 files changed, 17 insertions(+), 20 deletions(-) diff --git a/src/thorin/transform/plugin_execute.cpp b/src/thorin/transform/plugin_execute.cpp index 1669c2e85..9c2dfa4eb 100644 --- a/src/thorin/transform/plugin_execute.cpp +++ b/src/thorin/transform/plugin_execute.cpp @@ -35,12 +35,11 @@ void plugin_execute(Thorin& thorin) { }); for (auto cont : plugin_intrinsics) { - void * function_handle = thorin.search_plugin_function(cont->name()); - if (!function_handle) { + auto plugin_function = thorin.search_plugin_function(cont->name().c_str()); + if (!plugin_function) { world.ELOG("Plugin function not found for: {}", cont->name()); continue; } - auto plugin_function = (void*(*)(size_t, void**)) function_handle; bool evaluated = false; for (auto use : cont->copy_uses()) { @@ -54,13 +53,7 @@ void plugin_execute(Thorin& thorin) { continue; } - std::vector input_array(app->num_args() - 2); - for (size_t i = 1, e = app->num_args() - 1; i < e; i++) { - Def * input = const_cast(app->arg(i)); - input_array[i - 1] = input; - } - - void * output = plugin_function(app->num_args() - 2, (void **)input_array.data()); + void* output = plugin_function(&world, app); if (output) app->jump(app->arg(app->num_args() - 1), {app->arg(0), (Def*)output}); else diff --git a/src/thorin/world.cpp b/src/thorin/world.cpp index e15c0b561..31115b60b 100644 --- a/src/thorin/world.cpp +++ b/src/thorin/world.cpp @@ -1352,11 +1352,11 @@ bool Thorin::ensure_stack_size(size_t new_size) { #endif } -bool Thorin::register_plugin(std::string plugin_name) { +bool Thorin::register_plugin(const char* plugin_name) { #ifdef _MSC_VER return false; #else // _MSC_VER - void *handle = dlopen(plugin_name.c_str(), RTLD_LAZY | RTLD_GLOBAL); + void *handle = dlopen(plugin_name, RTLD_LAZY | RTLD_GLOBAL); if (!handle) { world().ELOG("Error loading plugin {}: {}", plugin_name, dlerror()); world().ELOG("Is plugin contained in LD_LIBRARY_PATH?"); @@ -1364,13 +1364,12 @@ bool Thorin::register_plugin(std::string plugin_name) { } dlerror(); - void (*initfunc)(void); char *error; - initfunc = (void(*)())(dlsym(handle, "init")); + auto initfunc = reinterpret_cast(dlsym(handle, "init")); if ((error = dlerror()) != NULL) { world().ILOG("Plugin {} did not supply an init function", plugin_name); } else { - initfunc(); + initfunc(&world()); } plugin_handles.push_back(handle); @@ -1378,12 +1377,12 @@ bool Thorin::register_plugin(std::string plugin_name) { #endif // _MSC_VER } -void * Thorin::search_plugin_function(std::string function_name) { +Thorin::plugin_func_t* Thorin::search_plugin_function(const char* function_name) const { #ifdef _MSC_VER #else // _MSC_VER for (auto plugin : plugin_handles) { - if (void * plugin_function = dlsym(plugin, function_name.c_str())) { - return plugin_function; + if (void* plugin_function = dlsym(plugin, function_name)) { + return reinterpret_cast(plugin_function); } } #endif // _MSC_VER diff --git a/src/thorin/world.h b/src/thorin/world.h index 33c58ff4b..75dc9f827 100644 --- a/src/thorin/world.h +++ b/src/thorin/world.h @@ -416,8 +416,13 @@ class Thorin { bool ensure_stack_size(size_t new_size); - bool register_plugin(std::string plugin_name); - void * search_plugin_function(std::string function_name); + // plugins + + using plugin_init_func_t = void(World*); + using plugin_func_t = void*(World*, const App*); + + bool register_plugin(const char* plugin_name); + plugin_func_t* search_plugin_function(const char* function_name) const; private: std::unique_ptr world_; std::vector plugin_handles; From 471d80b37242bd4267c6e588bc30006c5447c1f8 Mon Sep 17 00:00:00 2001 From: Matthias Kurtenacker Date: Wed, 5 Jul 2023 11:04:08 +0200 Subject: [PATCH 29/51] Make plugins explicitly return a Def* to avoid UB. --- src/thorin/transform/plugin_execute.cpp | 4 ++-- src/thorin/world.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/thorin/transform/plugin_execute.cpp b/src/thorin/transform/plugin_execute.cpp index 9c2dfa4eb..086b10d7d 100644 --- a/src/thorin/transform/plugin_execute.cpp +++ b/src/thorin/transform/plugin_execute.cpp @@ -53,9 +53,9 @@ void plugin_execute(Thorin& thorin) { continue; } - void* output = plugin_function(&world, app); + const Def* output = plugin_function(&world, app); if (output) - app->jump(app->arg(app->num_args() - 1), {app->arg(0), (Def*)output}); + app->jump(app->arg(app->num_args() - 1), {app->arg(0), output}); else app->jump(app->arg(app->num_args() - 1), {app->arg(0)}); diff --git a/src/thorin/world.h b/src/thorin/world.h index 75dc9f827..19191387b 100644 --- a/src/thorin/world.h +++ b/src/thorin/world.h @@ -419,7 +419,7 @@ class Thorin { // plugins using plugin_init_func_t = void(World*); - using plugin_func_t = void*(World*, const App*); + using plugin_func_t = const Def*(World*, const App*); bool register_plugin(const char* plugin_name); plugin_func_t* search_plugin_function(const char* function_name) const; From 845578789d7a5cdf01bed2f21a6eda43b49f98a5 Mon Sep 17 00:00:00 2001 From: Matthias Kurtenacker Date: Wed, 5 Jul 2023 11:34:55 +0200 Subject: [PATCH 30/51] Do not const_cast the app node, use rebuild and replace_uses instead. --- src/thorin/transform/plugin_execute.cpp | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/thorin/transform/plugin_execute.cpp b/src/thorin/transform/plugin_execute.cpp index 086b10d7d..ac93580eb 100644 --- a/src/thorin/transform/plugin_execute.cpp +++ b/src/thorin/transform/plugin_execute.cpp @@ -47,17 +47,20 @@ void plugin_execute(Thorin& thorin) { continue; } - auto app = const_cast(use.def()->as()); + auto app = use.def()->as(); if (app->num_uses() == 0) { continue; } const Def* output = plugin_function(&world, app); - if (output) - app->jump(app->arg(app->num_args() - 1), {app->arg(0), output}); - else - app->jump(app->arg(app->num_args() - 1), {app->arg(0)}); + const Def* app_rebuild = nullptr; + if (output) { + app_rebuild = app->rebuild(world, world.bottom_type(), {app->arg(app->num_args() - 1), app->arg(0), output}); + } else { + app_rebuild = app->rebuild(world, world.bottom_type(), {app->arg(app->num_args() - 1), app->arg(0)}); + } + app->replace_uses(app_rebuild); //partial_evaluation(world); //TODO: Some form of cleanup would be advisable here. evaluated = true; From 807592ebfc2c194fe598ab6761e33fc7f67d9f86 Mon Sep 17 00:00:00 2001 From: Matthias Kurtenacker Date: Wed, 5 Jul 2023 12:11:02 +0200 Subject: [PATCH 31/51] Gather plugins without copying continuations. --- src/thorin/transform/plugin_execute.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/thorin/transform/plugin_execute.cpp b/src/thorin/transform/plugin_execute.cpp index ac93580eb..e8235cd77 100644 --- a/src/thorin/transform/plugin_execute.cpp +++ b/src/thorin/transform/plugin_execute.cpp @@ -16,7 +16,10 @@ void plugin_execute(Thorin& thorin) { while (true) { plugin_intrinsics.clear(); - for (auto cont : world.copy_continuations()) { + for (auto def : world.defs()) { + auto cont = def->isa_nom(); + if (!cont) continue; + if (cont->is_intrinsic() && cont->intrinsic() == Intrinsic::Plugin) { plugin_intrinsics.push_back(cont); } From 1eebe0d2b3ac056f63a8e47f8138bf3b1f10be83 Mon Sep 17 00:00:00 2001 From: Matthias Kurtenacker Date: Wed, 5 Jul 2023 13:32:46 +0200 Subject: [PATCH 32/51] Clearification on app nodes and world.cleanup(). --- src/thorin/transform/plugin_execute.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/thorin/transform/plugin_execute.cpp b/src/thorin/transform/plugin_execute.cpp index e8235cd77..1d77a6cf3 100644 --- a/src/thorin/transform/plugin_execute.cpp +++ b/src/thorin/transform/plugin_execute.cpp @@ -51,6 +51,7 @@ void plugin_execute(Thorin& thorin) { } auto app = use.def()->as(); + assert(app->callee() == cont); if (app->num_uses() == 0) { continue; @@ -73,7 +74,7 @@ void plugin_execute(Thorin& thorin) { break; } - thorin.cleanup(); //Warning: This must not change the world, there are still references to intrinsics being maintained here. + thorin.cleanup(); } world.mark_pe_done(false); From 0c37e8d4a7160c9618c2f8486876a96e17e79feb Mon Sep 17 00:00:00 2001 From: Matthias Kurtenacker Date: Wed, 5 Jul 2023 14:56:28 +0200 Subject: [PATCH 33/51] Change plugin sorting comparisson to be asymetric. --- src/thorin/transform/plugin_execute.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/thorin/transform/plugin_execute.cpp b/src/thorin/transform/plugin_execute.cpp index 1d77a6cf3..ee8d43150 100644 --- a/src/thorin/transform/plugin_execute.cpp +++ b/src/thorin/transform/plugin_execute.cpp @@ -29,12 +29,12 @@ void plugin_execute(Thorin& thorin) { break; sort(plugin_intrinsics.begin(), plugin_intrinsics.end(), [&](const Continuation* a, const Continuation* b) { - const Continuation* depends = a; + const Continuation* depends = b; while (depends) { - if (depends == b) return false; depends = depends->attributes().depends; + if (a == depends) return true; } - return true; + return false; }); for (auto cont : plugin_intrinsics) { From d99f07cae7aa99b23fb76d85c637c82b9bdefe43 Mon Sep 17 00:00:00 2001 From: Matthias Kurtenacker Date: Fri, 20 Jan 2023 14:17:02 +0100 Subject: [PATCH 34/51] Json: Export plugins and plugin dependencies. --- src/thorin/be/json/json.cpp | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/thorin/be/json/json.cpp b/src/thorin/be/json/json.cpp index 8188caa3f..e1288437a 100644 --- a/src/thorin/be/json/json.cpp +++ b/src/thorin/be/json/json.cpp @@ -227,6 +227,28 @@ class DefTable { result["intrinsic"] = "match"; result["variant_type"] = variant_type; result["num_patterns"] = num_patterns; + } else if (cont->intrinsic() == Intrinsic::Plugin) { + auto intrinsic_name = cont->name(); + auto intrinsic_type = type_table_.translate_type(cont->type()); + auto name = "_plugin_" + std::to_string(decl_table.size()); + known_defs[def] = name; + + json forward_decl; + forward_decl["name"] = name; + forward_decl["type"] = "continuation"; + forward_decl["intrinsic"] = intrinsic_name; + forward_decl["fn_type"] = intrinsic_type; + forward_decl["plugin"] = true; + decl_table.push_back(forward_decl); + + if (cont->attributes().depends) { + result["name"] = name; + result["type"] = "continuation"; + result["plugin"] = true; + result["depends"] = translate_def(cont->attributes().depends); + } else { + return name; + } } else { auto intrinsic_name = cont->name(); auto intrinsic_type = type_table_.translate_type(cont->type()); From 968114c44acad1421028055f959933c02dccfffe Mon Sep 17 00:00:00 2001 From: Matthias Kurtenacker Date: Fri, 20 Jan 2023 16:05:38 +0100 Subject: [PATCH 35/51] [Json]: Export filters on plugins without dependencies. --- src/thorin/be/json/json.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/thorin/be/json/json.cpp b/src/thorin/be/json/json.cpp index e1288437a..88eb3df7f 100644 --- a/src/thorin/be/json/json.cpp +++ b/src/thorin/be/json/json.cpp @@ -246,6 +246,10 @@ class DefTable { result["type"] = "continuation"; result["plugin"] = true; result["depends"] = translate_def(cont->attributes().depends); + } else if (cont->filter() && !cont->filter()->empty()) { + result["name"] = name; + result["type"] = "continuation"; + result["plugin"] = true; } else { return name; } From 20ab0c67ff66dfa1600d89c632e61d4e0c8c715c Mon Sep 17 00:00:00 2001 From: Matthias Kurtenacker Date: Mon, 4 Sep 2023 13:52:01 +0200 Subject: [PATCH 36/51] Plugin execution order: total ordering by calculating dependency depth. --- src/thorin/transform/plugin_execute.cpp | 27 ++++++++++++++++++++----- src/thorin/world.cpp | 2 +- 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/src/thorin/transform/plugin_execute.cpp b/src/thorin/transform/plugin_execute.cpp index ee8d43150..30fbf4e7c 100644 --- a/src/thorin/transform/plugin_execute.cpp +++ b/src/thorin/transform/plugin_execute.cpp @@ -29,14 +29,31 @@ void plugin_execute(Thorin& thorin) { break; sort(plugin_intrinsics.begin(), plugin_intrinsics.end(), [&](const Continuation* a, const Continuation* b) { - const Continuation* depends = b; - while (depends) { - depends = depends->attributes().depends; - if (a == depends) return true; + //Plugins with more dependencies go to the end. + //If a plugin depends on another, then the depth is clearly higher. + + int depth_a = 0; //TODO: cache those numbers. + const Continuation* depends_a = a; + while (depends_a->attributes().depends) { + depends_a = depends_a->attributes().depends; + depth_a++; } - return false; + + int depth_b = 0; + const Continuation* depends_b = b; + while (depends_b->attributes().depends) { + depends_b = depends_b->attributes().depends; + depth_b++; + } + + return depth_a < depth_b; }); + world.VLOG("Plugin execution order:"); + for (auto cont : plugin_intrinsics) { + world.VLOG("{}", cont->unique_name()); + } + for (auto cont : plugin_intrinsics) { auto plugin_function = thorin.search_plugin_function(cont->name().c_str()); if (!plugin_function) { diff --git a/src/thorin/world.cpp b/src/thorin/world.cpp index 31115b60b..bfeb4abb0 100644 --- a/src/thorin/world.cpp +++ b/src/thorin/world.cpp @@ -1367,7 +1367,7 @@ bool Thorin::register_plugin(const char* plugin_name) { char *error; auto initfunc = reinterpret_cast(dlsym(handle, "init")); if ((error = dlerror()) != NULL) { - world().ILOG("Plugin {} did not supply an init function", plugin_name); + world().ILOG("Plugin {} did not provide an init function", plugin_name); } else { initfunc(&world()); } From 4eb19d5d4e41be55a0b7d73fa6843c7296c085be Mon Sep 17 00:00:00 2001 From: Matthias Kurtenacker Date: Fri, 6 Oct 2023 13:18:09 +0200 Subject: [PATCH 37/51] Bugfix Plugins: Work with new semantics for thorin.cleanup. --- src/thorin/transform/plugin_execute.cpp | 148 +++++++++++++----------- 1 file changed, 80 insertions(+), 68 deletions(-) diff --git a/src/thorin/transform/plugin_execute.cpp b/src/thorin/transform/plugin_execute.cpp index 30fbf4e7c..9e5862399 100644 --- a/src/thorin/transform/plugin_execute.cpp +++ b/src/thorin/transform/plugin_execute.cpp @@ -7,97 +7,109 @@ namespace thorin { -void plugin_execute(Thorin& thorin) { - World& world = thorin.world(); - world.VLOG("start plugin_execute"); +class PluginExecute { +public: + PluginExecute(Thorin& thorin) + : thorin(thorin) + {} - std::vector plugin_intrinsics; +private: + Thorin& thorin; - while (true) { - plugin_intrinsics.clear(); + World& world() { return thorin.world(); } - for (auto def : world.defs()) { - auto cont = def->isa_nom(); - if (!cont) continue; +public: + void run() { + std::vector plugin_intrinsics; - if (cont->is_intrinsic() && cont->intrinsic() == Intrinsic::Plugin) { - plugin_intrinsics.push_back(cont); - } - } + while (true) { + plugin_intrinsics.clear(); - if (plugin_intrinsics.empty()) - break; + for (auto def : world().defs()) { + auto cont = def->isa_nom(); + if (!cont) continue; - sort(plugin_intrinsics.begin(), plugin_intrinsics.end(), [&](const Continuation* a, const Continuation* b) { - //Plugins with more dependencies go to the end. - //If a plugin depends on another, then the depth is clearly higher. - - int depth_a = 0; //TODO: cache those numbers. - const Continuation* depends_a = a; - while (depends_a->attributes().depends) { - depends_a = depends_a->attributes().depends; - depth_a++; + if (cont->is_intrinsic() && cont->intrinsic() == Intrinsic::Plugin) { + plugin_intrinsics.push_back(cont); } - - int depth_b = 0; - const Continuation* depends_b = b; - while (depends_b->attributes().depends) { - depends_b = depends_b->attributes().depends; - depth_b++; - } - - return depth_a < depth_b; - }); - - world.VLOG("Plugin execution order:"); - for (auto cont : plugin_intrinsics) { - world.VLOG("{}", cont->unique_name()); - } - - for (auto cont : plugin_intrinsics) { - auto plugin_function = thorin.search_plugin_function(cont->name().c_str()); - if (!plugin_function) { - world.ELOG("Plugin function not found for: {}", cont->name()); - continue; } - bool evaluated = false; - for (auto use : cont->copy_uses()) { - if (!use.def()->isa()) { - continue; - } + if (plugin_intrinsics.empty()) + break; - auto app = use.def()->as(); - assert(app->callee() == cont); + sort(plugin_intrinsics.begin(), plugin_intrinsics.end(), [&](const Continuation* a, const Continuation* b) { + //Plugins with more dependencies go to the end. + //If a plugin depends on another, then the depth is clearly higher. + + int depth_a = 0; //TODO: cache those numbers. + const Continuation* depends_a = a; + while (depends_a->attributes().depends) { + depends_a = depends_a->attributes().depends; + depth_a++; + } + + int depth_b = 0; + const Continuation* depends_b = b; + while (depends_b->attributes().depends) { + depends_b = depends_b->attributes().depends; + depth_b++; + } + + return depth_a < depth_b; + }); + + world().VLOG("Plugin execution order:"); + for (auto cont : plugin_intrinsics) { + world().VLOG("{}", cont->unique_name()); + } - if (app->num_uses() == 0) { + for (auto cont : plugin_intrinsics) { + auto plugin_function = thorin.search_plugin_function(cont->name().c_str()); + if (!plugin_function) { + world().ELOG("Plugin function not found for: {}", cont->name()); continue; } - const Def* output = plugin_function(&world, app); - const Def* app_rebuild = nullptr; - if (output) { - app_rebuild = app->rebuild(world, world.bottom_type(), {app->arg(app->num_args() - 1), app->arg(0), output}); - } else { - app_rebuild = app->rebuild(world, world.bottom_type(), {app->arg(app->num_args() - 1), app->arg(0)}); + bool evaluated = false; + for (auto use : cont->copy_uses()) { + if (!use.def()->isa()) { + continue; + } + + auto app = use.def()->as(); + assert(app->callee() == cont); + + if (app->num_uses() == 0) { + continue; + } + + const Def* output = plugin_function(&world(), app); + const Def* app_rebuild = nullptr; + if (output) { + app_rebuild = app->rebuild(world(), world().bottom_type(), {app->arg(app->num_args() - 1), app->arg(0), output}); + } else { + app_rebuild = app->rebuild(world(), world().bottom_type(), {app->arg(app->num_args() - 1), app->arg(0)}); + } + app->replace_uses(app_rebuild); + + //partial_evaluation(world()); //TODO: Some form of cleanup would be advisable here. + evaluated = true; } - app->replace_uses(app_rebuild); - //partial_evaluation(world); //TODO: Some form of cleanup would be advisable here. - evaluated = true; + if (evaluated) + break; } - if (evaluated) - break; + thorin.cleanup(); } + world().mark_pe_done(false); thorin.cleanup(); } +}; - world.mark_pe_done(false); - thorin.cleanup(); - - world.VLOG("end plugin_execute"); +void plugin_execute(Thorin& thorin) { + PluginExecute(thorin).run(); } } From b44fbe40c3d6fa3650a84f44553fbf494d7708be Mon Sep 17 00:00:00 2001 From: Matthias Kurtenacker Date: Tue, 21 Nov 2023 18:29:21 +0100 Subject: [PATCH 38/51] Small improvements for internal continuations. Probably still buggy. --- src/thorin/transform/cleanup_world.cpp | 3 ++- src/thorin/transform/codegen_prepare.cpp | 5 ++++- src/thorin/util/scoped_dump.cpp | 8 ++++++-- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/thorin/transform/cleanup_world.cpp b/src/thorin/transform/cleanup_world.cpp index 65a860e48..67abda6f1 100644 --- a/src/thorin/transform/cleanup_world.cpp +++ b/src/thorin/transform/cleanup_world.cpp @@ -258,7 +258,8 @@ void Cleaner::cleanup() { world().mark_pe_done(); for (auto def : world().defs()) { if (auto cont = def->isa_nom()) - cont->destroy_filter(); + if (cont->cc() != CC::Thorin) + cont->destroy_filter(); } todo_ = true; diff --git a/src/thorin/transform/codegen_prepare.cpp b/src/thorin/transform/codegen_prepare.cpp index e8218a571..81942de72 100644 --- a/src/thorin/transform/codegen_prepare.cpp +++ b/src/thorin/transform/codegen_prepare.cpp @@ -43,8 +43,11 @@ void codegen_prepare(Thorin& thorin) { auto destination = std::make_unique(src); CodegenPrepare pass(src, *destination.get()); - for (auto& external : src.externals()) + for (auto& external : src.externals()) { + if (auto cont = external.second->isa(); cont && cont->cc() == CC::Thorin) + continue; pass.instantiate(external.second); + } thorin.world_container().swap(destination); thorin.world().VLOG("end codegen_prepare"); diff --git a/src/thorin/util/scoped_dump.cpp b/src/thorin/util/scoped_dump.cpp index 3b76427d7..c3ab0a1b7 100644 --- a/src/thorin/util/scoped_dump.cpp +++ b/src/thorin/util/scoped_dump.cpp @@ -4,8 +4,12 @@ namespace thorin { void ScopedWorld::stream_cont(thorin::Stream& s, Continuation* cont) const { s.fmt(Magenta); - if (cont->is_external()) - s.fmt("extern "); + if (cont->is_external()) { + if (cont->cc() == CC::Thorin) + s.fmt("intern "); + else + s.fmt("extern "); + } if (cont->is_intrinsic()) s.fmt("intrinsic "); From b345444673422ece32c8809c27878048c485ef46 Mon Sep 17 00:00:00 2001 From: Matthias Kurtenacker Date: Tue, 21 Nov 2023 18:29:42 +0100 Subject: [PATCH 39/51] dump_scoped: Dump filters. --- src/thorin/util/scoped_dump.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/thorin/util/scoped_dump.cpp b/src/thorin/util/scoped_dump.cpp index c3ab0a1b7..438de422e 100644 --- a/src/thorin/util/scoped_dump.cpp +++ b/src/thorin/util/scoped_dump.cpp @@ -16,6 +16,10 @@ void ScopedWorld::stream_cont(thorin::Stream& s, Continuation* cont) const { s.fmt(Red); s.fmt("{}", cont->unique_name()); s.fmt(Reset); + s.fmt(Green); + s.fmt("@"); + stream_def(s, cont->filter()); + s.fmt(Reset); s.fmt("("); const FnType* t = cont->type(); for (size_t i = 0; i < cont->num_params(); i++) { From 3ffeb3b9675e05b0d89c7f4ba68808df0f80ec21 Mon Sep 17 00:00:00 2001 From: Matthias Kurtenacker Date: Wed, 6 Dec 2023 13:25:05 +0100 Subject: [PATCH 40/51] Fix handling and emission of external globals. --- src/thorin/be/emitter.h | 2 +- src/thorin/transform/rewrite.cpp | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/thorin/be/emitter.h b/src/thorin/be/emitter.h index c138ad5e7..6fa7d8407 100644 --- a/src/thorin/be/emitter.h +++ b/src/thorin/be/emitter.h @@ -41,7 +41,7 @@ class Emitter { } //auto place = def->no_dep() ? entry_ : scheduler_.smart(def); - auto place = !scheduler_.scope().contains(def) ? entry_ : scheduler_.smart(def); + auto place = !(&scheduler_.scope()) ? nullptr : (!scheduler_.scope().contains(def) ? entry_ : scheduler_.smart(def)); if (place) { auto& bb = cont2bb_[place]; diff --git a/src/thorin/transform/rewrite.cpp b/src/thorin/transform/rewrite.cpp index faadaf083..f40ae173a 100644 --- a/src/thorin/transform/rewrite.cpp +++ b/src/thorin/transform/rewrite.cpp @@ -63,6 +63,11 @@ const Def* Rewriter::rewrite(const Def* odef) { assert(&nops[i]->world() == &dst()); } auto ndef = odef->rebuild(dst(), ntype, nops); + + if (auto global = odef->isa(); global && global->is_external()) { + dst().make_external(const_cast(ndef)); + } + return ndef; } else { assert(odef->isa_nom() && stub); From 3c25ecc82d337de4753932575aa58b910e56df85 Mon Sep 17 00:00:00 2001 From: Matthias Kurtenacker Date: Tue, 12 Dec 2023 14:09:06 +0100 Subject: [PATCH 41/51] Small bugfix in PE for multiple stacked run operations. --- src/thorin/transform/partial_evaluation.cpp | 2 +- src/thorin/util/scoped_dump.cpp | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/thorin/transform/partial_evaluation.cpp b/src/thorin/transform/partial_evaluation.cpp index f0595640e..78eeab1c5 100644 --- a/src/thorin/transform/partial_evaluation.cpp +++ b/src/thorin/transform/partial_evaluation.cpp @@ -130,7 +130,7 @@ bool PartialEvaluator::run() { const App* body = continuation->body(); const Def* callee_def = continuation->body()->callee(); - if (auto run = callee_def->isa()) { + while (auto run = callee_def->isa()) { force_fold = true; callee_def = run->def(); } diff --git a/src/thorin/util/scoped_dump.cpp b/src/thorin/util/scoped_dump.cpp index 438de422e..945fd3f33 100644 --- a/src/thorin/util/scoped_dump.cpp +++ b/src/thorin/util/scoped_dump.cpp @@ -54,6 +54,7 @@ void ScopedWorld::stream_cont(thorin::Stream& s, Continuation* cont) const { } prepare_def(cont, cont->body()); + prepare_def(cont, cont->filter()); auto defs = *scopes_to_defs_[cont]; stream_defs(s, defs); From e3f8e458dd9c7f452fbd1413c407e87cd0493b46 Mon Sep 17 00:00:00 2001 From: Matthias Kurtenacker Date: Tue, 12 Dec 2023 14:09:37 +0100 Subject: [PATCH 42/51] Bugfix if no process can be made in plugin execution. --- src/thorin/transform/plugin_execute.cpp | 29 +++++++++++++++---------- 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/src/thorin/transform/plugin_execute.cpp b/src/thorin/transform/plugin_execute.cpp index 9e5862399..fe1b0f533 100644 --- a/src/thorin/transform/plugin_execute.cpp +++ b/src/thorin/transform/plugin_execute.cpp @@ -63,6 +63,8 @@ class PluginExecute { world().VLOG("{}", cont->unique_name()); } + bool evaluated = false; + for (auto cont : plugin_intrinsics) { auto plugin_function = thorin.search_plugin_function(cont->name().c_str()); if (!plugin_function) { @@ -70,7 +72,6 @@ class PluginExecute { continue; } - bool evaluated = false; for (auto use : cont->copy_uses()) { if (!use.def()->isa()) { continue; @@ -83,23 +84,27 @@ class PluginExecute { continue; } - const Def* output = plugin_function(&world(), app); - const Def* app_rebuild = nullptr; - if (output) { - app_rebuild = app->rebuild(world(), world().bottom_type(), {app->arg(app->num_args() - 1), app->arg(0), output}); - } else { - app_rebuild = app->rebuild(world(), world().bottom_type(), {app->arg(app->num_args() - 1), app->arg(0)}); + try { + const Def* output = plugin_function(&world(), app); + const Def* app_rebuild = nullptr; + if (output) { + app_rebuild = app->rebuild(world(), world().bottom_type(), {app->arg(app->num_args() - 1), app->arg(0), output}); + } else { + app_rebuild = app->rebuild(world(), world().bottom_type(), {app->arg(app->num_args() - 1), app->arg(0)}); + } + app->replace_uses(app_rebuild); + + //partial_evaluation(world()); //TODO: Some form of cleanup would be advisable here. + evaluated = true; + } catch (const std::runtime_error& e) { + std::cerr << "Error in plugin function: " << e.what() << "\n"; } - app->replace_uses(app_rebuild); - - //partial_evaluation(world()); //TODO: Some form of cleanup would be advisable here. - evaluated = true; } if (evaluated) break; } - + if (!evaluated) break; thorin.cleanup(); } From 366f6b35fdb4b459aca20258fd4a6ca0d8f4074b Mon Sep 17 00:00:00 2001 From: Matthias Kurtenacker Date: Fri, 19 Jan 2024 12:41:27 +0100 Subject: [PATCH 43/51] [JSON] Add support for inf and nan floating point. --- src/thorin/be/json/json.cpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/thorin/be/json/json.cpp b/src/thorin/be/json/json.cpp index 88eb3df7f..7e5e6e6d7 100644 --- a/src/thorin/be/json/json.cpp +++ b/src/thorin/be/json/json.cpp @@ -321,11 +321,19 @@ class DefTable { result["name"] = name; result["type"] = "const"; result["const_type"] = type; - //result["value"] = lit->value().get_s32(); //TODO: this looks wrong. What I get should depend on the lit type. + switch (lit->primtype_tag()) { #define THORIN_I_TYPE(T, M) case PrimType_##T: { result["value"] = lit->value().get_##M(); break; } #define THORIN_BOOL_TYPE(T, M) case PrimType_##T: { result["value"] = lit->value().get_##M(); break; } -#define THORIN_F_TYPE(T, M) case PrimType_##T: { result["value"] = (double)lit->value().get_##M(); break; } +#define THORIN_F_TYPE(T, M) case PrimType_##T: { \ + double value = (double)lit->value().get_##M(); \ + result["value"] = value; \ + if (value == INFINITY) { result["special"] = "inf"; } \ + if (value == - INFINITY) { result["special"] = "-inf"; } \ + if (value == NAN) { result["special"] = "nan"; } \ + if (value == - NAN) { result["special"] = "- nan"; } \ + break; \ +} #include default: assert(false && "not implemented"); From 63498d2c264584d8b37a1982d837d5a0514a38ba Mon Sep 17 00:00:00 2001 From: Matthias Kurtenacker Date: Tue, 13 Feb 2024 16:28:49 +0100 Subject: [PATCH 44/51] Some small changes to support edge cases created in certain plugins. --- src/thorin/continuation.h | 2 +- src/thorin/transform/mangle.cpp | 21 ++++++++++++--------- src/thorin/world.h | 3 ++- 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/src/thorin/continuation.h b/src/thorin/continuation.h index 4fc3699b8..995fb8a36 100644 --- a/src/thorin/continuation.h +++ b/src/thorin/continuation.h @@ -138,7 +138,7 @@ class Continuation : public Def { Attributes(CC cc = CC::Thorin) : cc(cc) {} }; -private: +protected: Continuation(World&, const FnType* pi, const Attributes& attributes, Debug dbg); virtual ~Continuation() { for (auto param : params()) delete param; } diff --git a/src/thorin/transform/mangle.cpp b/src/thorin/transform/mangle.cpp index 98b3a559c..99f844fcb 100644 --- a/src/thorin/transform/mangle.cpp +++ b/src/thorin/transform/mangle.cpp @@ -74,15 +74,18 @@ Continuation* Mangler::mangle() { insert(old_entry(), old_entry()); else { // if we're only adding parameters, we can replace the entry by a small wrapper calling into the lifted entry - auto recursion_wrapper = dst().continuation(old_entry()->type()); - insert(old_entry(), recursion_wrapper); - std::vector args; - for (auto p : recursion_wrapper->params_as_defs()) - args.push_back(p); - size_t i = 0; - for ([[maybe_unused]] auto def : lift_) - args.push_back(new_entry()->param(recursion_wrapper->num_params() + i++)); - recursion_wrapper->jump(new_entry(), args); + // only do this if the entry is not also lifted, otherwise this would overwrite the newly generated parameter. + if (!lookup(old_entry())) { + auto recursion_wrapper = dst().continuation(old_entry()->type()); + insert(old_entry(), recursion_wrapper); + std::vector args; + for (auto p : recursion_wrapper->params_as_defs()) + args.push_back(p); + size_t i = 0; + for ([[maybe_unused]] auto def : lift_) + args.push_back(new_entry()->param(recursion_wrapper->num_params() + i++)); + recursion_wrapper->jump(new_entry(), args); + } } // cut/widen filter diff --git a/src/thorin/world.h b/src/thorin/world.h index 19191387b..2637f2690 100644 --- a/src/thorin/world.h +++ b/src/thorin/world.h @@ -340,7 +340,8 @@ class World : public Streamable { static std::string colorize(const std::string& str, int color); //@} -private: +//TODO: Some example plugins need access to cse and data_.defs_ to put new defs in, there has to be a better way than eposing this direcly though. +//private: const Param* param(const Type* type, const Continuation*, size_t index, Debug dbg); const Def* try_fold_aggregate(const Aggregate*); template const Def* transcendental(MathOpTag, const Def*, Debug, F&&); From f4ef1d38de1e1ecf00bc9d90e449351571c9fb8f Mon Sep 17 00:00:00 2001 From: Matthias Kurtenacker Date: Tue, 19 Mar 2024 18:55:08 +0100 Subject: [PATCH 45/51] Allow disabling collored scoped_dump through environment variable. --- src/thorin/util/scoped_dump.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/thorin/util/scoped_dump.h b/src/thorin/util/scoped_dump.h index 388c4f60d..fbbdabe5e 100644 --- a/src/thorin/util/scoped_dump.h +++ b/src/thorin/util/scoped_dump.h @@ -20,7 +20,7 @@ struct ScopedWorld : public Streamable { bool use_color; }; - ScopedWorld(World& w, Config cfg = { true }) : world_(w), forest_(w), config_(cfg) { + ScopedWorld(World& w, Config cfg = { getenv("THORIN_NO_COLOR") ? false : true }) : world_(w), forest_(w), config_(cfg) { #define T(n, c) n = cfg.use_color ? c : ""; COLORS(T) #undef T From 417d8f365d8dcb1918a53254e594651ecfb76d6f Mon Sep 17 00:00:00 2001 From: Matthias Kurtenacker Date: Tue, 19 Mar 2024 18:55:51 +0100 Subject: [PATCH 46/51] WIP: evaluate plugins during PE, not after. This is not very stable for now. Especially the dependency management can still break at any point. Thorins uses tracking is not very well maintained, and can't really deal with dependencies that are dropped by replacing a continuation with a specialized one. This can lead to dead code being interpreted as a remaining dependency that will only be removed when the entire world is being rewritten. --- src/thorin/transform/cleanup_world.cpp | 2 +- src/thorin/transform/partial_evaluation.cpp | 85 ++++++++++++++++++--- src/thorin/transform/partial_evaluation.h | 2 +- src/thorin/world.cpp | 10 +-- 4 files changed, 83 insertions(+), 16 deletions(-) diff --git a/src/thorin/transform/cleanup_world.cpp b/src/thorin/transform/cleanup_world.cpp index 67abda6f1..6e60e417a 100644 --- a/src/thorin/transform/cleanup_world.cpp +++ b/src/thorin/transform/cleanup_world.cpp @@ -244,7 +244,7 @@ void Cleaner::cleanup_fix_point() { todo_ |= resolve_loads(world()); rebuild(); //if (!world().is_pe_done()) - todo_ |= partial_evaluation(world()); + todo_ |= partial_evaluation(thorin_); //else // clean_pe_infos(); } diff --git a/src/thorin/transform/partial_evaluation.cpp b/src/thorin/transform/partial_evaluation.cpp index 78eeab1c5..a359ba881 100644 --- a/src/thorin/transform/partial_evaluation.cpp +++ b/src/thorin/transform/partial_evaluation.cpp @@ -16,13 +16,13 @@ struct HashApp { class PartialEvaluator { public: - PartialEvaluator(World& world, bool lower2cff) - : world_(world) + PartialEvaluator(Thorin& thorin, bool lower2cff) + : thorin_(thorin) , lower2cff_(lower2cff) , boundary_(Def::gid_counter()) {} - World& world() { return world_; } + World& world() { return thorin_.world(); } bool run(); void enqueue(Continuation* continuation) { if (continuation->gid() < 2 * boundary_ && done_.emplace(continuation).second) @@ -31,7 +31,7 @@ class PartialEvaluator { void eat_pe_info(Continuation*); private: - World& world_; + Thorin& thorin_; bool lower2cff_; HashMap cache_; ContinuationSet done_; @@ -141,6 +141,73 @@ bool PartialEvaluator::run() { continue; } + if (callee->intrinsic() == Intrinsic::Plugin) { + if (callee->attributes().depends) { + size_t num_dependend_uses = 0; + for (auto use : callee->attributes().depends->uses()) { + num_dependend_uses += use.def()->num_uses(); + } + //std::cerr << "Analyzing " << callee->unique_name() << " with dependency " << callee->attributes().depends->unique_name() << "\n"; + //std::cerr << " => has " << num_dependend_uses << " real dependencies\n"; + if (num_dependend_uses > 0) { + //Push the next continue so that other plugins get executed. + for (auto arg : body->args()) { + if (auto cont = arg->isa()) { + queue_.push(const_cast(cont)); + } + } + continue; + } + } + + ScopesForest forest(world()); + CondEval cond_eval(callee, forest, body->args()); + + //TODO: build specialize here to allow for parameter hiding. + bool fold = false; + for (size_t i = 0, e = body->num_args(); i != e; ++i) { + if (cond_eval.eval(i, lower2cff_)) { + fold = true; + break; + } + } + + if (fold) { + std::vector specialize(body->arg(body->num_args() - 1)->as()->num_params()); + specialize[0] = body->arg(0); + + const auto& p = cache_.emplace(body, nullptr); + const Continuation* target = p.first->second; + // create new specialization if not found in cache + if (p.second) { + world().idef(continuation, "Plugin execute: {}", callee); + + auto plugin_function = thorin_.search_plugin_function(callee->name().c_str()); + if (!plugin_function) { + world().ELOG("Plugin function not found for: {}", callee->name()); + continue; + } + + const Def* output = plugin_function(&world(), body); + if (output) + specialize[1] = output; + + target = body->arg(body->num_args() - 1)->as(); + todo = true; + } + continuation->jump(target, specialize); + + if (lower2cff_ && fold) { + // re-examine next iteration: + // maybe the specialization is not top-level anymore which might need further specialization + queue_.push(continuation); + continue; + } + } + + continue; + } + if (callee->has_body()) { // TODO cache the forest and only rebuild it when we need to ScopesForest forest(world()); @@ -162,7 +229,7 @@ bool PartialEvaluator::run() { Continuation*& target = p.first->second; // create new specialization if not found in cache if (p.second) { - world_.ddef(continuation, "Specializing call to {}", callee); + world().ddef(continuation, "Specializing call to {}", callee); target = drop(callee, specialize); todo = true; } @@ -198,11 +265,11 @@ bool PartialEvaluator::run() { //------------------------------------------------------------------------------ -bool partial_evaluation(World& world, bool lower2cff) { +bool partial_evaluation(Thorin& thorin, bool lower2cff) { auto name = lower2cff ? "lower2cff" : "partial_evaluation"; - world.VLOG("start {}", name); - auto res = PartialEvaluator(world, lower2cff).run(); - world.VLOG("end {}", name); + thorin.world().VLOG("start {}", name); + auto res = PartialEvaluator(thorin, lower2cff).run(); + thorin.world().VLOG("end {}", name); return res; } diff --git a/src/thorin/transform/partial_evaluation.h b/src/thorin/transform/partial_evaluation.h index fd5c2f908..ffa8ec5cd 100644 --- a/src/thorin/transform/partial_evaluation.h +++ b/src/thorin/transform/partial_evaluation.h @@ -19,7 +19,7 @@ class BetaReducer : public Rewriter { const Def* rewrite(const Def* odef) override; }; -bool partial_evaluation(World&, bool lower2cff = false); +bool partial_evaluation(Thorin&, bool lower2cff = false); } diff --git a/src/thorin/world.cpp b/src/thorin/world.cpp index bfeb4abb0..76df6d340 100644 --- a/src/thorin/world.cpp +++ b/src/thorin/world.cpp @@ -1320,13 +1320,13 @@ void Thorin::opt() { } RUN_PASS(cleanup()) - RUN_PASS(while (partial_evaluation(world(), true))); // lower2cff + RUN_PASS(while (partial_evaluation(*this, true))); // lower2cff RUN_PASS(flatten_tuples(*this)) RUN_PASS(split_slots(*this)) - if (plugin_handles.size() > 0) { - RUN_PASS(plugin_execute(*this)); - RUN_PASS(cleanup()); - } + //if (plugin_handles.size() > 0) { + // RUN_PASS(plugin_execute(*this)); + // RUN_PASS(cleanup()); + //} RUN_PASS(closure_conversion(world())) RUN_PASS(lift_builtins(*this)) RUN_PASS(inliner(*this)) From f4e655feb3d06efb1b1818114bcee5478f508899 Mon Sep 17 00:00:00 2001 From: Matthias Kurtenacker Date: Wed, 27 Mar 2024 13:29:38 +0100 Subject: [PATCH 47/51] Improve plugin dependency management, allow filters with intrinsics. --- src/thorin/be/json/json.cpp | 32 +++++++++++++++------ src/thorin/continuation.cpp | 4 +-- src/thorin/transform/partial_evaluation.cpp | 7 ++--- 3 files changed, 28 insertions(+), 15 deletions(-) diff --git a/src/thorin/be/json/json.cpp b/src/thorin/be/json/json.cpp index 7e5e6e6d7..c8275da3c 100644 --- a/src/thorin/be/json/json.cpp +++ b/src/thorin/be/json/json.cpp @@ -239,14 +239,27 @@ class DefTable { forward_decl["intrinsic"] = intrinsic_name; forward_decl["fn_type"] = intrinsic_type; forward_decl["plugin"] = true; - decl_table.push_back(forward_decl); + bool emit_node = false; if (cont->attributes().depends) { - result["name"] = name; - result["type"] = "continuation"; - result["plugin"] = true; result["depends"] = translate_def(cont->attributes().depends); - } else if (cont->filter() && !cont->filter()->empty()) { + + emit_node = true; + } + if (cont->filter() && !cont->filter()->empty()) { + //The filter will most certainly rely on these parameters. + json arg_names = json::array(); + for (auto arg : cont->params()) { + arg_names.push_back(translate_def(arg)); + } + forward_decl["arg_names"] = arg_names; + + emit_node = true; + } + + decl_table.push_back(forward_decl); + + if (emit_node) { result["name"] = name; result["type"] = "continuation"; result["plugin"] = true; @@ -309,9 +322,12 @@ class DefTable { }; } else { //Early return. We do not have a body, so there is no point in writing something to the def table. - if (cont->filter() && !cont->filter()->empty()) - assert(false && "These filters cannot be generated RN"); - return name; + if (cont->filter() && !cont->filter()->empty()) { + result["name"] = name; + result["type"] = "continuation"; + result["filter"] = translate_def(cont->filter()); + } else + return name; } } } else if (auto lit = def->isa()) { diff --git a/src/thorin/continuation.cpp b/src/thorin/continuation.cpp index 7fb708537..80c982a8b 100644 --- a/src/thorin/continuation.cpp +++ b/src/thorin/continuation.cpp @@ -312,9 +312,7 @@ void Continuation::match(const Def* mem, const Def* val, Continuation* otherwise bool Continuation::verify() const { bool ok = true; - if (!has_body()) - assertf(filter()->is_empty(), "continuations with no body should have an empty (no) filter"); - else { + if (has_body()) { ok &= body()->verify(); assert(!dead_); // destroy() should remove the body assert(intrinsic() == Intrinsic::None); diff --git a/src/thorin/transform/partial_evaluation.cpp b/src/thorin/transform/partial_evaluation.cpp index a359ba881..1e725fc0f 100644 --- a/src/thorin/transform/partial_evaluation.cpp +++ b/src/thorin/transform/partial_evaluation.cpp @@ -143,10 +143,8 @@ bool PartialEvaluator::run() { if (callee->intrinsic() == Intrinsic::Plugin) { if (callee->attributes().depends) { - size_t num_dependend_uses = 0; - for (auto use : callee->attributes().depends->uses()) { - num_dependend_uses += use.def()->num_uses(); - } + size_t num_dependend_uses = callee->attributes().depends->num_uses() - callee->attributes().depends->num_params(); + //std::cerr << "Analyzing " << callee->unique_name() << " with dependency " << callee->attributes().depends->unique_name() << "\n"; //std::cerr << " => has " << num_dependend_uses << " real dependencies\n"; if (num_dependend_uses > 0) { @@ -156,6 +154,7 @@ bool PartialEvaluator::run() { queue_.push(const_cast(cont)); } } + todo = true; continue; } } From 138b6f462c71273c75777b94229729417dd1d5ec Mon Sep 17 00:00:00 2001 From: Matthias Kurtenacker Date: Thu, 28 Mar 2024 12:36:56 +0100 Subject: [PATCH 48/51] Exceptions in plugins added back in. --- src/thorin/transform/partial_evaluation.cpp | 36 ++++++++++++++------- 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/src/thorin/transform/partial_evaluation.cpp b/src/thorin/transform/partial_evaluation.cpp index 1e725fc0f..a5434008b 100644 --- a/src/thorin/transform/partial_evaluation.cpp +++ b/src/thorin/transform/partial_evaluation.cpp @@ -178,23 +178,35 @@ bool PartialEvaluator::run() { const auto& p = cache_.emplace(body, nullptr); const Continuation* target = p.first->second; // create new specialization if not found in cache - if (p.second) { - world().idef(continuation, "Plugin execute: {}", callee); + try { + if (p.second) { + world().idef(continuation, "Plugin execute: {}", callee); + + auto plugin_function = thorin_.search_plugin_function(callee->name().c_str()); + if (!plugin_function) { + world().ELOG("Plugin function not found for: {}", callee->name()); + continue; + } - auto plugin_function = thorin_.search_plugin_function(callee->name().c_str()); - if (!plugin_function) { - world().ELOG("Plugin function not found for: {}", callee->name()); - continue; - } + const Def* output = plugin_function(&world(), body); + if (output) + specialize[1] = output; - const Def* output = plugin_function(&world(), body); - if (output) - specialize[1] = output; + target = body->arg(body->num_args() - 1)->as(); + todo = true; + } - target = body->arg(body->num_args() - 1)->as(); + continuation->jump(target, specialize); + } catch (const std::runtime_error& e) { + std::cerr << "Error in plugin function: " << e.what() << "\n"; + for (auto arg : body->args()) { + if (auto cont = arg->isa()) { + queue_.push(const_cast(cont)); + } + } todo = true; + continue; } - continuation->jump(target, specialize); if (lower2cff_ && fold) { // re-examine next iteration: From 2776537ae197b81415aac92b40d85d7895c4dd51 Mon Sep 17 00:00:00 2001 From: Matthias Kurtenacker Date: Thu, 28 Mar 2024 12:37:12 +0100 Subject: [PATCH 49/51] Added Thorin::cleanup_fix_point as a less intrusive cleanup option. --- src/thorin/transform/cleanup_world.cpp | 2 ++ src/thorin/world.h | 1 + 2 files changed, 3 insertions(+) diff --git a/src/thorin/transform/cleanup_world.cpp b/src/thorin/transform/cleanup_world.cpp index 6e60e417a..a2ce30e0d 100644 --- a/src/thorin/transform/cleanup_world.cpp +++ b/src/thorin/transform/cleanup_world.cpp @@ -30,6 +30,7 @@ class Cleaner { void clean_pe_info(std::queue, Continuation*); Thorin& thorin_; bool todo_ = true; +friend class Thorin; }; void Cleaner::eliminate_tail_rec() { @@ -274,5 +275,6 @@ void Cleaner::cleanup() { } void Thorin::cleanup() { Cleaner(*this).cleanup(); } +void Thorin::cleanup_fix_point() { Cleaner(*this).cleanup_fix_point(); } } diff --git a/src/thorin/world.h b/src/thorin/world.h index 2637f2690..2d8b6e738 100644 --- a/src/thorin/world.h +++ b/src/thorin/world.h @@ -413,6 +413,7 @@ class Thorin { /// Performs dead code, unreachable code and unused type elimination. void cleanup(); + void cleanup_fix_point(); void opt(); bool ensure_stack_size(size_t new_size); From a4c8aa0fed8cd42f3bf67a706667fa139150867c Mon Sep 17 00:00:00 2001 From: Matthias Kurtenacker Date: Fri, 19 Apr 2024 16:46:24 +0200 Subject: [PATCH 50/51] Do not destroy external continuations during PE. --- src/thorin/transform/partial_evaluation.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/thorin/transform/partial_evaluation.cpp b/src/thorin/transform/partial_evaluation.cpp index a5434008b..e2bfd05bd 100644 --- a/src/thorin/transform/partial_evaluation.cpp +++ b/src/thorin/transform/partial_evaluation.cpp @@ -247,7 +247,7 @@ bool PartialEvaluator::run() { jump_to_dropped_call(continuation, target, specialize); - while (callee && callee->never_called()) { + while (callee && callee->never_called() && !callee->is_external()) { if (callee->has_body()) { auto new_callee = const_cast(callee->body()->callee()->isa()); callee->destroy("partial_evaluation"); From 53e3a87b7cc3775ee8c7641e61606b09198ee53a Mon Sep 17 00:00:00 2001 From: Matthias Kurtenacker Date: Wed, 24 Jul 2024 19:36:23 +0200 Subject: [PATCH 51/51] Fix todo flag handling with plugins. Plugins can now return bottom. --- src/thorin/transform/partial_evaluation.cpp | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/thorin/transform/partial_evaluation.cpp b/src/thorin/transform/partial_evaluation.cpp index e2bfd05bd..c8e3bace6 100644 --- a/src/thorin/transform/partial_evaluation.cpp +++ b/src/thorin/transform/partial_evaluation.cpp @@ -154,7 +154,6 @@ bool PartialEvaluator::run() { queue_.push(const_cast(cont)); } } - todo = true; continue; } } @@ -171,6 +170,9 @@ bool PartialEvaluator::run() { } } + if (not body->arg(body->num_args() - 1)->isa()) + fold = false; //Cannot execute plugin if the target is not a continuation. + if (fold) { std::vector specialize(body->arg(body->num_args() - 1)->as()->num_params()); specialize[0] = body->arg(0); @@ -189,6 +191,17 @@ bool PartialEvaluator::run() { } const Def* output = plugin_function(&world(), body); + if (output->isa()) { //The plugin cannot produce an output, but we should run another iteration. + world().ddef(continuation, "Plugin did not produce a usable output: {}", callee); + for (auto arg : body->args()) { + if (auto cont = arg->isa()) { + queue_.push(const_cast(cont)); + } + } + todo = true; + continue; + } + if (output) specialize[1] = output; @@ -197,14 +210,13 @@ bool PartialEvaluator::run() { } continuation->jump(target, specialize); - } catch (const std::runtime_error& e) { + } catch (const std::runtime_error& e) { //The plugin is unhappy about the general state. We should not use it to determine the fixed-point state. std::cerr << "Error in plugin function: " << e.what() << "\n"; for (auto arg : body->args()) { if (auto cont = arg->isa()) { queue_.push(const_cast(cont)); } } - todo = true; continue; }