From 86d8a2d184dcd902a5bf0b25099e67471f8e7b08 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Mon, 15 Feb 2021 12:11:01 +0100 Subject: [PATCH 001/342] introduced namespaces & common superclass for BEs --- CMakeLists.txt | 3 + src/thorin/CMakeLists.txt | 12 +- src/thorin/be/backends.cpp | 201 ++++++++++++++++++++++++++ src/thorin/be/backends.h | 62 ++++++++ src/thorin/be/{ => c}/c.cpp | 4 +- src/thorin/be/{ => c}/c.h | 7 +- src/thorin/be/{llvm => c}/opencl.cpp | 11 +- src/thorin/be/{llvm => c}/opencl.h | 9 +- src/thorin/be/llvm/amdgpu.cpp | 2 +- src/thorin/be/llvm/amdgpu.h | 4 + src/thorin/be/llvm/cpu.cpp | 2 +- src/thorin/be/llvm/cpu.h | 2 +- src/thorin/be/llvm/cuda.cpp | 2 +- src/thorin/be/llvm/cuda.h | 2 +- src/thorin/be/llvm/hls.cpp | 2 +- src/thorin/be/llvm/hls.h | 2 +- src/thorin/be/llvm/llvm.cpp | 204 ++------------------------- src/thorin/be/llvm/llvm.h | 33 +---- src/thorin/be/llvm/nvvm.cpp | 2 +- src/thorin/be/llvm/nvvm.h | 4 + src/thorin/be/llvm/parallel.cpp | 8 +- src/thorin/be/llvm/runtime.cpp | 2 +- src/thorin/be/llvm/runtime.h | 14 +- src/thorin/be/llvm/vectorize.cpp | 2 +- src/thorin/config.h.in | 1 + 25 files changed, 328 insertions(+), 269 deletions(-) create mode 100644 src/thorin/be/backends.cpp create mode 100644 src/thorin/be/backends.h rename src/thorin/be/{ => c}/c.cpp (99%) rename src/thorin/be/{ => c}/c.h (85%) rename src/thorin/be/{llvm => c}/opencl.cpp (53%) rename src/thorin/be/{llvm => c}/opencl.h (55%) diff --git a/CMakeLists.txt b/CMakeLists.txt index d60f032e2..4c7d8b0f7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -54,6 +54,9 @@ endif() if(THORIN_PROFILE) set(THORIN_ENABLE_PROFILING TRUE) endif() +if(LLVM_FOUND) + set(THORIN_ENABLE_LLVM TRUE) +endif() if(RV_FOUND) set(THORIN_ENABLE_RV TRUE) endif() diff --git a/src/thorin/CMakeLists.txt b/src/thorin/CMakeLists.txt index 6d15a8cd2..15e11073e 100644 --- a/src/thorin/CMakeLists.txt +++ b/src/thorin/CMakeLists.txt @@ -30,8 +30,8 @@ set(THORIN_SOURCES analyses/scope.h analyses/verify.cpp analyses/verify.h - be/c.cpp - be/c.h + be/c/c.cpp + be/c/c.h be/kernel_config.h tables/allnodes.h tables/arithoptable.h @@ -82,7 +82,9 @@ set(THORIN_SOURCES util/symbol.h util/types.h util/utility.h -) + be/backends.cpp + be/backends.h + ) if(LLVM_FOUND) list(APPEND THORIN_SOURCES @@ -98,8 +100,8 @@ if(LLVM_FOUND) be/llvm/amdgpu.h be/llvm/nvvm.cpp be/llvm/nvvm.h - #be/llvm/opencl.cpp - #be/llvm/opencl.h + be/c/opencl.cpp + be/c/opencl.h be/llvm/parallel.cpp be/llvm/runtime.inc be/llvm/runtime.cpp diff --git a/src/thorin/be/backends.cpp b/src/thorin/be/backends.cpp new file mode 100644 index 000000000..6349c4e90 --- /dev/null +++ b/src/thorin/be/backends.cpp @@ -0,0 +1,201 @@ +#include "backends.h" + +#include "thorin/analyses/scope.h" + +#ifdef THORIN_ENABLE_LLVM +#include "thorin/be/llvm/cpu.h" +#include "thorin/be/llvm/nvvm.h" +#include "thorin/be/llvm/amdgpu.h" +#include "thorin/be/llvm/cuda.h" +#include "thorin/be/llvm/hls.h" +#include "thorin/be/c/opencl.h" +#include "thorin/transform/codegen_prepare.h" +#endif + +namespace thorin { + +static void get_kernel_configs(Importer& importer, + const std::vector& kernels, + Cont2Config& kernel_config, + std::function (Continuation*, Continuation*)> use_callback) +{ + importer.world().opt(); + + auto exported_continuations = importer.world().exported_continuations(); + for (auto continuation : kernels) { + // recover the imported continuation (lost after the call to opt) + Continuation* imported = nullptr; + for (auto exported : exported_continuations) { + if (exported->name() == continuation->name()) + imported = exported; + } + if (!imported) continue; + + visit_uses(continuation, [&] (Continuation* use) { + auto config = use_callback(use, imported); + if (config) { + auto p = kernel_config.emplace(imported, std::move(config)); + assert_unused(p.second && "single kernel config entry expected"); + } + return false; + }, true); + + continuation->destroy_body(); + } +} + +static const Continuation* get_alloc_call(const Def* def) { + // look through casts + while (auto conv_op = def->isa()) + def = conv_op->op(0); + + auto param = def->isa(); + if (!param) return nullptr; + + auto ret = param->continuation(); + if (ret->num_uses() != 1) return nullptr; + + auto use = *(ret->uses().begin()); + auto call = use.def()->isa_continuation(); + if (!call || use.index() == 0) return nullptr; + + auto callee = call->callee(); + if (callee->name() != "anydsl_alloc") return nullptr; + + return call; +} + +static uint64_t get_alloc_size(const Def* def) { + auto call = get_alloc_call(def); + if (!call) return 0; + + // signature: anydsl_alloc(mem, i32, i64, fn(mem, &[i8])) + auto size = call->arg(2)->isa(); + return size ? static_cast(size->value().get_qu64()) : 0_u64; +} + +Backends::Backends(World& world, int opt, bool debug) +: cuda(world) +, nvvm(world) +, opencl(world) +, amdgpu(world) +, hls(world) +{ + // determine different parts of the world which need to be compiled differently + Scope::for_each(world, [&] (const Scope& scope) { + auto continuation = scope.entry(); + Continuation* imported = nullptr; + if (is_passed_to_intrinsic(continuation, Intrinsic::CUDA)) + imported = cuda.import(continuation)->as_continuation(); + else if (is_passed_to_intrinsic(continuation, Intrinsic::NVVM)) + imported = nvvm.import(continuation)->as_continuation(); + else if (is_passed_to_intrinsic(continuation, Intrinsic::OpenCL)) + imported = opencl.import(continuation)->as_continuation(); + else if (is_passed_to_intrinsic(continuation, Intrinsic::AMDGPU)) + imported = amdgpu.import(continuation)->as_continuation(); + else if (is_passed_to_intrinsic(continuation, Intrinsic::HLS)) + imported = hls.import(continuation)->as_continuation(); + else + return; + + imported->set_name(continuation->unique_name()); + imported->make_exported(); + continuation->set_name(continuation->unique_name()); + + for (size_t i = 0, e = continuation->num_params(); i != e; ++i) + imported->param(i)->set_name(continuation->param(i)->unique_name()); + + kernels.emplace_back(continuation); + }); + + // get the GPU kernel configurations + if (!cuda.world().empty() || + !nvvm.world().empty() || + !opencl.world().empty() || + !amdgpu.world().empty()) { + auto get_gpu_config = [&] (Continuation* use, Continuation* /* imported */) { + // determine whether or not this kernel uses restrict pointers + bool has_restrict = true; + DefSet allocs; + for (size_t i = LaunchArgs::Num, e = use->num_args(); has_restrict && i != e; ++i) { + auto arg = use->arg(i); + if (!arg->type()->isa()) continue; + auto alloc = get_alloc_call(arg); + if (!alloc) has_restrict = false; + auto p = allocs.insert(alloc); + has_restrict &= p.second; + } + + auto it_config = use->arg(LaunchArgs::Config)->as(); + if (it_config->op(0)->isa() && + it_config->op(1)->isa() && + it_config->op(2)->isa()) { + return std::make_unique(std::tuple { + it_config->op(0)->as()->qu32_value().data(), + it_config->op(1)->as()->qu32_value().data(), + it_config->op(2)->as()->qu32_value().data() + }, has_restrict); + } + return std::make_unique(std::tuple { -1, -1, -1 }, has_restrict); + }; + get_kernel_configs(cuda, kernels, kernel_config, get_gpu_config); + get_kernel_configs(nvvm, kernels, kernel_config, get_gpu_config); + get_kernel_configs(opencl, kernels, kernel_config, get_gpu_config); + get_kernel_configs(amdgpu, kernels, kernel_config, get_gpu_config); + } + + // get the HLS kernel configurations + if (!hls.world().empty()) { + auto get_hls_config = [&] (Continuation* use, Continuation* imported) { + HLSKernelConfig::Param2Size param_sizes; + for (size_t i = 3, e = use->num_args(); i != e; ++i) { + auto arg = use->arg(i); + auto ptr_type = arg->type()->isa(); + if (!ptr_type) continue; + auto size = get_alloc_size(arg); + if (size == 0) + world.edef(arg, "array size is not known at compile time"); + auto elem_type = ptr_type->pointee(); + size_t multiplier = 1; + if (!elem_type->isa()) { + if (auto array_type = elem_type->isa()) + elem_type = array_type->elem_type(); + } + if (!elem_type->isa()) { + if (auto def_array_type = elem_type->isa()) { + elem_type = def_array_type->elem_type(); + multiplier = def_array_type->dim(); + } + } + auto prim_type = elem_type->isa(); + if (!prim_type) + world.edef(arg, "only pointers to arrays of primitive types are supported"); + auto num_elems = size / (multiplier * num_bits(prim_type->primtype_tag()) / 8); + // imported has type: fn (mem, fn (mem), ...) + param_sizes.emplace(imported->param(i - 3 + 2), num_elems); + } + return std::make_unique(param_sizes); + }; + get_kernel_configs(hls, kernels, kernel_config, get_hls_config); + } + +#ifdef THORIN_ENABLE_LLVM + cpu_cg = std::make_unique(world, opt, debug); + + if (!nvvm. world().empty()) nvvm_cg = std::make_unique(nvvm .world(), kernel_config, debug); + if (!amdgpu.world().empty()) amdgpu_cg = std::make_unique(amdgpu.world(), kernel_config, opt, debug); +#else + // TODO: maybe use the C backend as a fallback when LLVM is not present for host codegen ? +#endif + // TODO: Fix the C-based backends + //if (!cuda. world().empty()) cuda_cg = std::make_unique(cuda .world(), kernel_config, opt, debug); + //if (!opencl.world().empty()) opencl_cg = std::make_unique(opencl.world(), kernel_config, opt, debug); + //if (!hls. world().empty()) hls_cg = std::make_unique(hls .world(), kernel_config, opt, debug); +} + +CodeGen::CodeGen(World& world, bool debug) +: world_(world) +, debug_(debug) +{} + +} diff --git a/src/thorin/be/backends.h b/src/thorin/be/backends.h new file mode 100644 index 000000000..b9c309889 --- /dev/null +++ b/src/thorin/be/backends.h @@ -0,0 +1,62 @@ +#ifndef THORIN_BACKENDS_H +#define THORIN_BACKENDS_H + +#include "thorin/transform/importer.h" +#include "thorin/be/kernel_config.h" + +namespace thorin { + +class CodeGen { +protected: + CodeGen(World& world, bool debug); +public: + virtual void emit(std::ostream& stream) = 0; + + /// @name getters + //@{ + World& world() const { return world_; } + bool debug() const { return debug_; } + //@} + +private: + World& world_; + bool debug_; +}; + +struct LaunchArgs { + enum { + Mem = 0, + Device, + Space, + Config, + Body, + Return, + Num + }; +}; + +struct Backends { + Backends(World& world, int opt, bool debug); + + Cont2Config kernel_config; + std::vector kernels; + + // TODO use arrays + loops for this + Importer cuda; + Importer nvvm; + Importer opencl; + Importer amdgpu; + Importer hls; + + // TODO use arrays + loops for this + std::unique_ptr cpu_cg; + std::unique_ptr cuda_cg; + std::unique_ptr nvvm_cg; + std::unique_ptr opencl_cg; + std::unique_ptr amdgpu_cg; + std::unique_ptr hls_cg; +}; + +} + +#endif diff --git a/src/thorin/be/c.cpp b/src/thorin/be/c/c.cpp similarity index 99% rename from src/thorin/be/c.cpp rename to src/thorin/be/c/c.cpp index 2c6c580af..0a822ed83 100644 --- a/src/thorin/be/c.cpp +++ b/src/thorin/be/c/c.cpp @@ -7,7 +7,7 @@ #include "thorin/analyses/schedule.h" #include "thorin/analyses/scope.h" #include "thorin/util/stream.h" -#include "thorin/be/c.h" +#include "c.h" #include #include @@ -15,7 +15,7 @@ #include #include -namespace thorin { +namespace thorin::c_be { class CCodeGen; diff --git a/src/thorin/be/c.h b/src/thorin/be/c/c.h similarity index 85% rename from src/thorin/be/c.h rename to src/thorin/be/c/c.h index 54bfcc944..01fe38354 100644 --- a/src/thorin/be/c.h +++ b/src/thorin/be/c/c.h @@ -4,13 +4,14 @@ #include #include -#include "thorin/world.h" #include "thorin/be/kernel_config.h" namespace thorin { class World; +namespace c_be { + enum class Lang : uint8_t { C99, ///< Flag for C99 HLS, ///< Flag for HLS @@ -19,7 +20,9 @@ enum class Lang : uint8_t { }; void emit_c(World&, const Cont2Config& kernel_config, std::ostream& stream, Lang lang, bool debug); -void emit_c_int(World&, std::ostream& stream); +void emit_c_int(World&, Stream& stream); + +} } diff --git a/src/thorin/be/llvm/opencl.cpp b/src/thorin/be/c/opencl.cpp similarity index 53% rename from src/thorin/be/llvm/opencl.cpp rename to src/thorin/be/c/opencl.cpp index 225123993..c93910f55 100644 --- a/src/thorin/be/llvm/opencl.cpp +++ b/src/thorin/be/c/opencl.cpp @@ -1,22 +1,21 @@ -#include "thorin/be/llvm/opencl.h" +#include "opencl.h" #include #include #include "thorin/primop.h" #include "thorin/world.h" -#include "thorin/be/c.h" +#include "thorin/be/c/c.h" - -namespace thorin { +namespace thorin::c_be { OpenCLCodeGen::OpenCLCodeGen(World& world, const Cont2Config& kernel_config, int opt, bool debug) - : CodeGen(world, llvm::CallingConv::C, llvm::CallingConv::C, llvm::CallingConv::C, opt, debug) + : CodeGen(world, debug) , kernel_config_(kernel_config) {} void OpenCLCodeGen::emit(std::ostream& stream) { - thorin::emit_c(world(), kernel_config_, stream, Lang::OPENCL, debug()); + emit_c(world(), kernel_config_, stream, Lang::OPENCL, debug()); } } diff --git a/src/thorin/be/llvm/opencl.h b/src/thorin/be/c/opencl.h similarity index 55% rename from src/thorin/be/llvm/opencl.h rename to src/thorin/be/c/opencl.h index 87815bb23..3b472777e 100644 --- a/src/thorin/be/llvm/opencl.h +++ b/src/thorin/be/c/opencl.h @@ -1,19 +1,18 @@ #ifndef THORIN_BE_LLVM_OPENCL_H #define THORIN_BE_LLVM_OPENCL_H -#include "thorin/be/llvm/llvm.h" +#include "../backends.h" +#include "../llvm/llvm.h" -namespace thorin { +namespace thorin::c_be { -class OpenCLCodeGen : public CodeGen { +class OpenCLCodeGen : public thorin::CodeGen { public: OpenCLCodeGen(World& world, const Cont2Config&, int opt, bool debug); void emit(std::ostream& stream) override; protected: - virtual std::string get_alloc_name() const override { THORIN_UNREACHABLE; /*alloc not supported in OpenCL*/; } - const Cont2Config& kernel_config_; }; diff --git a/src/thorin/be/llvm/amdgpu.cpp b/src/thorin/be/llvm/amdgpu.cpp index a4dd61954..859948be4 100644 --- a/src/thorin/be/llvm/amdgpu.cpp +++ b/src/thorin/be/llvm/amdgpu.cpp @@ -3,7 +3,7 @@ #include "thorin/primop.h" #include "thorin/world.h" -namespace thorin { +namespace thorin::llvm_be { AMDGPUCodeGen::AMDGPUCodeGen(World& world, const Cont2Config& kernel_config, int opt, bool debug) : CodeGen(world, llvm::CallingConv::C, llvm::CallingConv::C, llvm::CallingConv::AMDGPU_KERNEL, opt, debug) diff --git a/src/thorin/be/llvm/amdgpu.h b/src/thorin/be/llvm/amdgpu.h index ce1f97d9a..33866fc63 100644 --- a/src/thorin/be/llvm/amdgpu.h +++ b/src/thorin/be/llvm/amdgpu.h @@ -7,6 +7,8 @@ namespace thorin { class Load; +namespace llvm_be { + class AMDGPUCodeGen : public CodeGen { public: AMDGPUCodeGen(World& world, const Cont2Config&, int opt, bool debug); @@ -23,4 +25,6 @@ class AMDGPUCodeGen : public CodeGen { } +} + #endif diff --git a/src/thorin/be/llvm/cpu.cpp b/src/thorin/be/llvm/cpu.cpp index d98e394a2..ed6e92772 100644 --- a/src/thorin/be/llvm/cpu.cpp +++ b/src/thorin/be/llvm/cpu.cpp @@ -8,7 +8,7 @@ #include #include -namespace thorin { +namespace thorin::llvm_be { CPUCodeGen::CPUCodeGen(World& world, int opt, bool debug) : CodeGen(world, llvm::CallingConv::C, llvm::CallingConv::C, llvm::CallingConv::C, opt, debug) diff --git a/src/thorin/be/llvm/cpu.h b/src/thorin/be/llvm/cpu.h index 0a348a3e4..7607cdcb6 100644 --- a/src/thorin/be/llvm/cpu.h +++ b/src/thorin/be/llvm/cpu.h @@ -3,7 +3,7 @@ #include "thorin/be/llvm/llvm.h" -namespace thorin { +namespace thorin::llvm_be { class CPUCodeGen : public CodeGen { public: diff --git a/src/thorin/be/llvm/cuda.cpp b/src/thorin/be/llvm/cuda.cpp index 1711f43a1..28b9824e7 100644 --- a/src/thorin/be/llvm/cuda.cpp +++ b/src/thorin/be/llvm/cuda.cpp @@ -7,7 +7,7 @@ #include "thorin/world.h" #include "thorin/be/c.h" -namespace thorin { +namespace thorin::llvm_be { CUDACodeGen::CUDACodeGen(World& world, const Cont2Config& kernel_config, int opt, bool debug) : CodeGen(world, llvm::CallingConv::C, llvm::CallingConv::C, llvm::CallingConv::C, opt, debug) diff --git a/src/thorin/be/llvm/cuda.h b/src/thorin/be/llvm/cuda.h index bc2d84321..3fe4472eb 100644 --- a/src/thorin/be/llvm/cuda.h +++ b/src/thorin/be/llvm/cuda.h @@ -3,7 +3,7 @@ #include "thorin/be/llvm/llvm.h" -namespace thorin { +namespace thorin::llvm_be { class CUDACodeGen : public CodeGen { public: diff --git a/src/thorin/be/llvm/hls.cpp b/src/thorin/be/llvm/hls.cpp index 3427b464b..125722774 100644 --- a/src/thorin/be/llvm/hls.cpp +++ b/src/thorin/be/llvm/hls.cpp @@ -7,7 +7,7 @@ #include "thorin/world.h" #include "thorin/be/c.h" -namespace thorin { +namespace thorin::llvm_be { HLSCodeGen::HLSCodeGen(World& world, const Cont2Config& kernel_config, int opt, bool debug) : CodeGen(world, llvm::CallingConv::C, llvm::CallingConv::C, llvm::CallingConv::C, opt, debug) diff --git a/src/thorin/be/llvm/hls.h b/src/thorin/be/llvm/hls.h index 5b033fef0..7143a675e 100644 --- a/src/thorin/be/llvm/hls.h +++ b/src/thorin/be/llvm/hls.h @@ -3,7 +3,7 @@ #include "thorin/be/llvm/llvm.h" -namespace thorin { +namespace thorin::llvm_be { class HLSCodeGen : public CodeGen { public: diff --git a/src/thorin/be/llvm/llvm.cpp b/src/thorin/be/llvm/llvm.cpp index 43101097c..5f267f947 100644 --- a/src/thorin/be/llvm/llvm.cpp +++ b/src/thorin/be/llvm/llvm.cpp @@ -36,27 +36,19 @@ #include "thorin/type.h" #include "thorin/world.h" #include "thorin/analyses/scope.h" -#include "thorin/be/llvm/cpu.h" -#include "thorin/be/llvm/nvvm.h" -#include "thorin/be/llvm/amdgpu.h" -#include "thorin/be/llvm/cuda.h" -#include "thorin/be/llvm/hls.h" -#include "thorin/be/llvm/opencl.h" -#include "thorin/transform/codegen_prepare.h" #include "thorin/util/array.h" -namespace thorin { +namespace thorin::llvm_be { CodeGen::CodeGen(World& world, llvm::CallingConv::ID function_calling_convention, llvm::CallingConv::ID device_calling_convention, llvm::CallingConv::ID kernel_calling_convention, int opt, bool debug) - : world_(world) + : thorin::CodeGen(world, debug) , context_(new llvm::LLVMContext()) , module_(new llvm::Module(world.name(), *context_)) , opt_(opt) - , debug_(debug) , dibuilder_(module()) , function_calling_convention_(function_calling_convention) , device_calling_convention_(device_calling_convention) @@ -183,7 +175,7 @@ llvm::Type* CodeGen::convert(const Type* type) { return types_[type] = llvm_type; } - auto env_type = convert(Closure::environment_type(world_)); + auto env_type = convert(Closure::environment_type(world())); ops.push_back(env_type); auto fn_type = llvm::FunctionType::get(ret, ops, false); auto ptr_type = llvm::PointerType::get(fn_type, 0); @@ -288,10 +280,10 @@ std::unique_ptr& CodeGen::emit() { // Darwin only supports dwarf2 if (llvm::Triple(llvm::sys::getProcessTriple()).isOSDarwin()) module().addModuleFlag(llvm::Module::Warning, "Dwarf Version", 2); - dicompile_unit_ = dibuilder_.createCompileUnit(llvm::dwarf::DW_LANG_C, dibuilder_.createFile(world_.name(), llvm::StringRef()), "Impala", opt() > 0, llvm::StringRef(), 0); + dicompile_unit_ = dibuilder_.createCompileUnit(llvm::dwarf::DW_LANG_C, dibuilder_.createFile(world().name(), llvm::StringRef()), "Impala", opt() > 0, llvm::StringRef(), 0); } - Scope::for_each(world_, [&] (const Scope& scope) { emit(scope); }); + Scope::for_each(world(), [&] (const Scope& scope) { emit(scope); }); if (debug()) dibuilder_.finalize(); @@ -785,15 +777,15 @@ llvm::Value* CodeGen::emit_(const Def* def) { llvm::Value* env = nullptr; if (is_thin(closure->op(1)->type())) { if (is_type_unit(val->type())) { - env = emit(world_.bottom(Closure::environment_type(world_))); + env = emit(world().bottom(Closure::environment_type(world()))); } else { - env = emit(world_.cast(Closure::environment_type(world_), val)); + env = emit(world().cast(Closure::environment_type(world()), val)); } } else { world().wdef(def, "closure '{}' is leaking memory, type '{}' is too large", def, agg->op(1)->type()); auto alloc = emit_alloc(irbuilder, val->type(), nullptr); irbuilder.CreateStore(emit(val), alloc); - env = irbuilder.CreatePtrToInt(alloc, convert(Closure::environment_type(world_))); + env = irbuilder.CreatePtrToInt(alloc, convert(Closure::environment_type(world()))); } llvm_agg = irbuilder.CreateInsertValue(llvm_agg, closure_fn, 0); llvm_agg = irbuilder.CreateInsertValue(llvm_agg, env, 1); @@ -946,7 +938,7 @@ llvm::Value* CodeGen::emit_(const Def* def) { if (auto vector = def->isa()) { llvm::Value* vec = llvm::UndefValue::get(convert(vector->type())); for (size_t i = 0, e = vector->num_ops(); i != e; ++i) - vec = irbuilder.CreateInsertElement(vec, emit(vector->op(i)), emit(world_.literal_pu32(i, vector->loc()))); + vec = irbuilder.CreateInsertElement(vec, emit(vector->op(i)), emit(world().literal_pu32(i, vector->loc()))); return vec; } @@ -1295,182 +1287,4 @@ llvm::Value* CodeGen::create_tmp_alloca(llvm::IRBuilder<>& irbuilder, llvm::Type //------------------------------------------------------------------------------ -static void get_kernel_configs(Importer& importer, - const std::vector& kernels, - Cont2Config& kernel_config, - std::function (Continuation*, Continuation*)> use_callback) -{ - importer.world().opt(); - - auto exported_continuations = importer.world().exported_continuations(); - for (auto continuation : kernels) { - // recover the imported continuation (lost after the call to opt) - Continuation* imported = nullptr; - for (auto exported : exported_continuations) { - if (exported->name() == continuation->name()) - imported = exported; - } - if (!imported) continue; - - visit_uses(continuation, [&] (Continuation* use) { - auto config = use_callback(use, imported); - if (config) { - auto p = kernel_config.emplace(imported, std::move(config)); - assert_unused(p.second && "single kernel config entry expected"); - } - return false; - }, true); - - continuation->destroy_body(); - } -} - -static const Continuation* get_alloc_call(const Def* def) { - // look through casts - while (auto conv_op = def->isa()) - def = conv_op->op(0); - - auto param = def->isa(); - if (!param) return nullptr; - - auto ret = param->continuation(); - if (ret->num_uses() != 1) return nullptr; - - auto use = *(ret->uses().begin()); - auto call = use.def()->isa_continuation(); - if (!call || use.index() == 0) return nullptr; - - auto callee = call->callee(); - if (callee->name() != "anydsl_alloc") return nullptr; - - return call; -} - -static uint64_t get_alloc_size(const Def* def) { - auto call = get_alloc_call(def); - if (!call) return 0; - - // signature: anydsl_alloc(mem, i32, i64, fn(mem, &[i8])) - auto size = call->arg(2)->isa(); - return size ? static_cast(size->value().get_qu64()) : 0_u64; -} - -Backends::Backends(World& world, int opt, bool debug) - : cuda(world) - , nvvm(world) - , opencl(world) - , amdgpu(world) - , hls(world) -{ - // determine different parts of the world which need to be compiled differently - Scope::for_each(world, [&] (const Scope& scope) { - auto continuation = scope.entry(); - Continuation* imported = nullptr; - if (is_passed_to_intrinsic(continuation, Intrinsic::CUDA)) - imported = cuda.import(continuation)->as_continuation(); - else if (is_passed_to_intrinsic(continuation, Intrinsic::NVVM)) - imported = nvvm.import(continuation)->as_continuation(); - else if (is_passed_to_intrinsic(continuation, Intrinsic::OpenCL)) - imported = opencl.import(continuation)->as_continuation(); - else if (is_passed_to_intrinsic(continuation, Intrinsic::AMDGPU)) - imported = amdgpu.import(continuation)->as_continuation(); - else if (is_passed_to_intrinsic(continuation, Intrinsic::HLS)) - imported = hls.import(continuation)->as_continuation(); - else - return; - - imported->set_name(continuation->unique_name()); - imported->make_exported(); - continuation->set_name(continuation->unique_name()); - - for (size_t i = 0, e = continuation->num_params(); i != e; ++i) - imported->param(i)->set_name(continuation->param(i)->unique_name()); - - kernels.emplace_back(continuation); - }); - - // get the GPU kernel configurations - if (!cuda.world().empty() || - !nvvm.world().empty() || - !opencl.world().empty() || - !amdgpu.world().empty()) { - auto get_gpu_config = [&] (Continuation* use, Continuation* /* imported */) { - // determine whether or not this kernel uses restrict pointers - bool has_restrict = true; - DefSet allocs; - for (size_t i = LaunchArgs::Num, e = use->num_args(); has_restrict && i != e; ++i) { - auto arg = use->arg(i); - if (!arg->type()->isa()) continue; - auto alloc = get_alloc_call(arg); - if (!alloc) has_restrict = false; - auto p = allocs.insert(alloc); - has_restrict &= p.second; - } - - auto it_config = use->arg(LaunchArgs::Config)->as(); - if (it_config->op(0)->isa() && - it_config->op(1)->isa() && - it_config->op(2)->isa()) { - return std::make_unique(std::tuple { - it_config->op(0)->as()->qu32_value().data(), - it_config->op(1)->as()->qu32_value().data(), - it_config->op(2)->as()->qu32_value().data() - }, has_restrict); - } - return std::make_unique(std::tuple { -1, -1, -1 }, has_restrict); - }; - get_kernel_configs(cuda, kernels, kernel_config, get_gpu_config); - get_kernel_configs(nvvm, kernels, kernel_config, get_gpu_config); - get_kernel_configs(opencl, kernels, kernel_config, get_gpu_config); - get_kernel_configs(amdgpu, kernels, kernel_config, get_gpu_config); - } - - // get the HLS kernel configurations - if (!hls.world().empty()) { - auto get_hls_config = [&] (Continuation* use, Continuation* imported) { - HLSKernelConfig::Param2Size param_sizes; - for (size_t i = 3, e = use->num_args(); i != e; ++i) { - auto arg = use->arg(i); - auto ptr_type = arg->type()->isa(); - if (!ptr_type) continue; - auto size = get_alloc_size(arg); - if (size == 0) - world.edef(arg, "array size is not known at compile time"); - auto elem_type = ptr_type->pointee(); - size_t multiplier = 1; - if (!elem_type->isa()) { - if (auto array_type = elem_type->isa()) - elem_type = array_type->elem_type(); - } - if (!elem_type->isa()) { - if (auto def_array_type = elem_type->isa()) { - elem_type = def_array_type->elem_type(); - multiplier = def_array_type->dim(); - } - } - auto prim_type = elem_type->isa(); - if (!prim_type) - world.edef(arg, "only pointers to arrays of primitive types are supported"); - auto num_elems = size / (multiplier * num_bits(prim_type->primtype_tag()) / 8); - // imported has type: fn (mem, fn (mem), ...) - param_sizes.emplace(imported->param(i - 3 + 2), num_elems); - } - return std::make_unique(param_sizes); - }; - get_kernel_configs(hls, kernels, kernel_config, get_hls_config); - } - - cpu_cg = std::make_unique(world, opt, debug); - - if (!nvvm. world().empty()) nvvm_cg = std::make_unique(nvvm .world(), kernel_config, debug); - if (!amdgpu.world().empty()) amdgpu_cg = std::make_unique(amdgpu.world(), kernel_config, opt, debug); - - // TODO - //if (!cuda. world().empty()) cuda_cg = std::make_unique(cuda .world(), kernel_config, opt, debug); - //if (!opencl.world().empty()) opencl_cg = std::make_unique(opencl.world(), kernel_config, opt, debug); - //if (!hls. world().empty()) hls_cg = std::make_unique(hls .world(), kernel_config, opt, debug); -} - -//------------------------------------------------------------------------------ - } diff --git a/src/thorin/be/llvm/llvm.h b/src/thorin/be/llvm/llvm.h index 855249b65..7f41d51e8 100644 --- a/src/thorin/be/llvm/llvm.h +++ b/src/thorin/be/llvm/llvm.h @@ -9,6 +9,7 @@ #include "thorin/config.h" #include "thorin/continuation.h" #include "thorin/analyses/schedule.h" +#include "thorin/be/backends.h" #include "thorin/be/llvm/runtime.h" #include "thorin/be/kernel_config.h" #include "thorin/transform/importer.h" @@ -17,7 +18,9 @@ namespace thorin { class World; -class CodeGen { +namespace llvm_be { + +class CodeGen : public thorin::CodeGen { protected: CodeGen(World& world, llvm::CallingConv::ID function_calling_convention, @@ -29,15 +32,13 @@ class CodeGen { /// @name getters //@{ - World& world() const { return world_; } llvm::LLVMContext& context() { return *context_; } llvm::Module& module() { return *module_; } const llvm::Module& module() const { return *module_; } - virtual void emit(std::ostream& stream); int opt() const { return opt_; } - bool debug() const { return debug_; } //@} + void emit(std::ostream& stream) override; std::unique_ptr& emit(); protected: @@ -96,11 +97,9 @@ class CodeGen { void emit_vectorize(u32, llvm::Function*, llvm::CallInst*); void emit_phi_arg(llvm::IRBuilder<>&, const Param*, llvm::Value*); - World& world_; std::unique_ptr context_; std::unique_ptr module_; int opt_; - bool debug_; protected: std::unique_ptr machine_; @@ -128,27 +127,7 @@ class CodeGen { template llvm::ArrayRef llvm_ref(const Array& array) { return llvm::ArrayRef(array.begin(), array.end()); } -struct Backends { - Backends(World& world, int opt, bool debug); - - Cont2Config kernel_config; - std::vector kernels; - - // TODO use arrays + loops for this - Importer cuda; - Importer nvvm; - Importer opencl; - Importer amdgpu; - Importer hls; - - // TODO use arrays + loops for this - std::unique_ptr cpu_cg; - std::unique_ptr cuda_cg; - std::unique_ptr nvvm_cg; - std::unique_ptr opencl_cg; - std::unique_ptr amdgpu_cg; - std::unique_ptr hls_cg; -}; +} // namespace llvm_be } // namespace thorin diff --git a/src/thorin/be/llvm/nvvm.cpp b/src/thorin/be/llvm/nvvm.cpp index ab3923f16..1fd8c7818 100644 --- a/src/thorin/be/llvm/nvvm.cpp +++ b/src/thorin/be/llvm/nvvm.cpp @@ -16,7 +16,7 @@ #include "thorin/primop.h" #include "thorin/world.h" -namespace thorin { +namespace thorin::llvm_be { NVVMCodeGen::NVVMCodeGen(World& world, const Cont2Config& kernel_config, bool debug) : CodeGen(world, llvm::CallingConv::C, llvm::CallingConv::PTX_Device, llvm::CallingConv::PTX_Kernel, 0, debug) diff --git a/src/thorin/be/llvm/nvvm.h b/src/thorin/be/llvm/nvvm.h index ee1f84485..f637304c4 100644 --- a/src/thorin/be/llvm/nvvm.h +++ b/src/thorin/be/llvm/nvvm.h @@ -7,6 +7,8 @@ namespace thorin { class Load; +namespace llvm_be { + class NVVMCodeGen : public CodeGen { public: NVVMCodeGen(World& world, const Cont2Config&, bool debug); // NVVM-specific optimizations are run in the runtime @@ -37,4 +39,6 @@ class NVVMCodeGen : public CodeGen { } +} + #endif diff --git a/src/thorin/be/llvm/parallel.cpp b/src/thorin/be/llvm/parallel.cpp index 764a7869f..128728fad 100644 --- a/src/thorin/be/llvm/parallel.cpp +++ b/src/thorin/be/llvm/parallel.cpp @@ -1,6 +1,6 @@ #include "thorin/be/llvm/llvm.h" -namespace thorin { +namespace thorin::llvm_be { enum { PAR_ARG_MEM, @@ -31,7 +31,7 @@ Continuation* CodeGen::emit_parallel(llvm::IRBuilder<>& irbuilder, Continuation* } // fetch values and create a unified struct which contains all values (closure) - auto closure_type = convert(world_.tuple_type(continuation->arg_fn_type()->ops().skip_front(PAR_NUM_ARGS))); + auto closure_type = convert(world().tuple_type(continuation->arg_fn_type()->ops().skip_front(PAR_NUM_ARGS))); llvm::Value* closure = llvm::UndefValue::get(closure_type); if (num_kernel_args != 1) { for (size_t i = 0; i < num_kernel_args; ++i) @@ -119,7 +119,7 @@ Continuation* CodeGen::emit_fibers(llvm::IRBuilder<>& irbuilder, Continuation* c } // fetch values and create a unified struct which contains all values (closure) - auto closure_type = convert(world_.tuple_type(continuation->arg_fn_type()->ops().skip_front(FIB_NUM_ARGS))); + auto closure_type = convert(world().tuple_type(continuation->arg_fn_type()->ops().skip_front(FIB_NUM_ARGS))); llvm::Value* closure = llvm::UndefValue::get(closure_type); if (num_kernel_args != 1) { for (size_t i = 0; i < num_kernel_args; ++i) @@ -195,7 +195,7 @@ Continuation* CodeGen::emit_spawn(llvm::IRBuilder<>& irbuilder, Continuation* co } // fetch values and create a unified struct which contains all values (closure) - auto closure_type = convert(world_.tuple_type(continuation->arg_fn_type()->ops().skip_front(SPAWN_NUM_ARGS))); + auto closure_type = convert(world().tuple_type(continuation->arg_fn_type()->ops().skip_front(SPAWN_NUM_ARGS))); llvm::Value* closure = nullptr; if (closure_type->isStructTy()) { closure = llvm::UndefValue::get(closure_type); diff --git a/src/thorin/be/llvm/runtime.cpp b/src/thorin/be/llvm/runtime.cpp index 7344605a0..b7b38070c 100644 --- a/src/thorin/be/llvm/runtime.cpp +++ b/src/thorin/be/llvm/runtime.cpp @@ -13,7 +13,7 @@ #include "thorin/be/llvm/llvm.h" #include "thorin/be/llvm/runtime.inc" -namespace thorin { +namespace thorin::llvm_be { Runtime::Runtime(llvm::LLVMContext& context, llvm::Module& target) diff --git a/src/thorin/be/llvm/runtime.h b/src/thorin/be/llvm/runtime.h index 84a7cb78d..c125deed5 100644 --- a/src/thorin/be/llvm/runtime.h +++ b/src/thorin/be/llvm/runtime.h @@ -8,22 +8,10 @@ #include "thorin/world.h" -namespace thorin { +namespace thorin::llvm_be { class CodeGen; -struct LaunchArgs { - enum { - Mem = 0, - Device, - Space, - Config, - Body, - Return, - Num - }; -}; - class Runtime { public: Runtime(llvm::LLVMContext&, llvm::Module& target); diff --git a/src/thorin/be/llvm/vectorize.cpp b/src/thorin/be/llvm/vectorize.cpp index 0a9e5e578..529ac844b 100644 --- a/src/thorin/be/llvm/vectorize.cpp +++ b/src/thorin/be/llvm/vectorize.cpp @@ -36,7 +36,7 @@ #include "thorin/world.h" #include "thorin/analyses/scope.h" -namespace thorin { +namespace thorin::llvm_be { struct VectorizeArgs { enum { diff --git a/src/thorin/config.h.in b/src/thorin/config.h.in index bb6f494ec..5e061fe28 100644 --- a/src/thorin/config.h.in +++ b/src/thorin/config.h.in @@ -3,6 +3,7 @@ #cmakedefine01 THORIN_ENABLE_CHECKS #cmakedefine01 THORIN_ENABLE_PROFILING +#cmakedefine01 THORIN_ENABLE_LLVM #cmakedefine01 THORIN_ENABLE_RV #endif From 07127e553e418b4da1a6b7cb74b28e18010e7678 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Mon, 15 Feb 2021 15:40:14 +0100 Subject: [PATCH 002/342] make the C-based backends not based on llvm_be::CodeGen --- src/thorin/CMakeLists.txt | 12 ++++++------ src/thorin/be/backends.cpp | 14 ++++++-------- src/thorin/be/c/c.cpp | 5 +++-- src/thorin/be/c/c.h | 19 +++++++++++++++++-- src/thorin/be/c/cuda.cpp | 9 +++++++++ src/thorin/be/c/cuda.h | 15 +++++++++++++++ src/thorin/be/c/hls.cpp | 9 +++++++++ src/thorin/be/c/hls.h | 15 +++++++++++++++ src/thorin/be/c/opencl.cpp | 14 +------------- src/thorin/be/c/opencl.h | 10 ++-------- src/thorin/be/llvm/cuda.cpp | 21 --------------------- src/thorin/be/llvm/cuda.h | 22 ---------------------- src/thorin/be/llvm/hls.cpp | 21 --------------------- src/thorin/be/llvm/hls.h | 22 ---------------------- 14 files changed, 83 insertions(+), 125 deletions(-) create mode 100644 src/thorin/be/c/cuda.cpp create mode 100644 src/thorin/be/c/cuda.h create mode 100644 src/thorin/be/c/hls.cpp create mode 100644 src/thorin/be/c/hls.h delete mode 100644 src/thorin/be/llvm/cuda.cpp delete mode 100644 src/thorin/be/llvm/cuda.h delete mode 100644 src/thorin/be/llvm/hls.cpp delete mode 100644 src/thorin/be/llvm/hls.h diff --git a/src/thorin/CMakeLists.txt b/src/thorin/CMakeLists.txt index 15e11073e..b0c18ea7c 100644 --- a/src/thorin/CMakeLists.txt +++ b/src/thorin/CMakeLists.txt @@ -32,6 +32,12 @@ set(THORIN_SOURCES analyses/verify.h be/c/c.cpp be/c/c.h + be/c/cuda.cpp + be/c/cuda.h + be/c/hls.cpp + be/c/hls.h + be/c/opencl.cpp + be/c/opencl.h be/kernel_config.h tables/allnodes.h tables/arithoptable.h @@ -90,18 +96,12 @@ if(LLVM_FOUND) list(APPEND THORIN_SOURCES be/llvm/cpu.cpp be/llvm/cpu.h - #be/llvm/cuda.cpp - #be/llvm/cuda.h - #be/llvm/hls.cpp - #be/llvm/hls.h be/llvm/llvm.cpp be/llvm/llvm.h be/llvm/amdgpu.cpp be/llvm/amdgpu.h be/llvm/nvvm.cpp be/llvm/nvvm.h - be/c/opencl.cpp - be/c/opencl.h be/llvm/parallel.cpp be/llvm/runtime.inc be/llvm/runtime.cpp diff --git a/src/thorin/be/backends.cpp b/src/thorin/be/backends.cpp index 6349c4e90..6458bfca1 100644 --- a/src/thorin/be/backends.cpp +++ b/src/thorin/be/backends.cpp @@ -6,11 +6,10 @@ #include "thorin/be/llvm/cpu.h" #include "thorin/be/llvm/nvvm.h" #include "thorin/be/llvm/amdgpu.h" -#include "thorin/be/llvm/cuda.h" -#include "thorin/be/llvm/hls.h" -#include "thorin/be/c/opencl.h" -#include "thorin/transform/codegen_prepare.h" #endif +#include "thorin/be/c/cuda.h" +#include "thorin/be/c/hls.h" +#include "thorin/be/c/opencl.h" namespace thorin { @@ -187,10 +186,9 @@ Backends::Backends(World& world, int opt, bool debug) #else // TODO: maybe use the C backend as a fallback when LLVM is not present for host codegen ? #endif - // TODO: Fix the C-based backends - //if (!cuda. world().empty()) cuda_cg = std::make_unique(cuda .world(), kernel_config, opt, debug); - //if (!opencl.world().empty()) opencl_cg = std::make_unique(opencl.world(), kernel_config, opt, debug); - //if (!hls. world().empty()) hls_cg = std::make_unique(hls .world(), kernel_config, opt, debug); + if (!cuda. world().empty()) cuda_cg = std::make_unique(cuda .world(), kernel_config, opt, debug); + if (!opencl.world().empty()) opencl_cg = std::make_unique(opencl.world(), kernel_config, opt, debug); + if (!hls. world().empty()) hls_cg = std::make_unique(hls .world(), kernel_config, opt, debug); } CodeGen::CodeGen(World& world, bool debug) diff --git a/src/thorin/be/c/c.cpp b/src/thorin/be/c/c.cpp index 0a822ed83..daf1b4f18 100644 --- a/src/thorin/be/c/c.cpp +++ b/src/thorin/be/c/c.cpp @@ -1554,8 +1554,9 @@ std::string CCodeGen::tuple_name(const TupleType* tuple_type) { //------------------------------------------------------------------------------ -void emit_c(World& world, const Cont2Config& kernel_config, Stream& stream, Lang lang, bool debug) { - CCodeGen(world, kernel_config, stream, lang, debug).emit(); +void CodeGen::emit(std::ostream &stream) { + Stream s(stream); + CCodeGen(world(), kernel_config_, s, lang_, debug_).emit(); } void emit_c_int(World& world, Stream& stream) { diff --git a/src/thorin/be/c/c.h b/src/thorin/be/c/c.h index 01fe38354..818eaed81 100644 --- a/src/thorin/be/c/c.h +++ b/src/thorin/be/c/c.h @@ -4,7 +4,7 @@ #include #include -#include "thorin/be/kernel_config.h" +#include "thorin/be/backends.h" namespace thorin { @@ -19,7 +19,22 @@ enum class Lang : uint8_t { OPENCL ///< Flag for OpenCL }; -void emit_c(World&, const Cont2Config& kernel_config, std::ostream& stream, Lang lang, bool debug); +class CodeGen : public thorin::CodeGen { +public: + CodeGen(World& world, const Cont2Config& kernel_config, Lang lang, bool debug) + : thorin::CodeGen(world, debug) + , kernel_config_(kernel_config) + , lang_(lang) + , debug_(debug) {} + + void emit(std::ostream& stream) override; + +private: + const Cont2Config& kernel_config_; + Lang lang_; + bool debug_; +}; + void emit_c_int(World&, Stream& stream); } diff --git a/src/thorin/be/c/cuda.cpp b/src/thorin/be/c/cuda.cpp new file mode 100644 index 000000000..815173a1b --- /dev/null +++ b/src/thorin/be/c/cuda.cpp @@ -0,0 +1,9 @@ +#include "cuda.h" + +namespace thorin::c_be { + +CUDACodeGen::CUDACodeGen(World& world, const Cont2Config& kernel_config, int opt, bool debug) + : CodeGen(world, kernel_config, Lang::CUDA, debug) +{} + +} diff --git a/src/thorin/be/c/cuda.h b/src/thorin/be/c/cuda.h new file mode 100644 index 000000000..a6200e294 --- /dev/null +++ b/src/thorin/be/c/cuda.h @@ -0,0 +1,15 @@ +#ifndef THORIN_BE_LLVM_CUDA_H +#define THORIN_BE_LLVM_CUDA_H + +#include "c.h" + +namespace thorin::c_be { + +class CUDACodeGen : public CodeGen { +public: + CUDACodeGen(World &world, const Cont2Config &, int opt, bool debug); +}; + +} + +#endif diff --git a/src/thorin/be/c/hls.cpp b/src/thorin/be/c/hls.cpp new file mode 100644 index 000000000..66660f431 --- /dev/null +++ b/src/thorin/be/c/hls.cpp @@ -0,0 +1,9 @@ +#include "hls.h" + +namespace thorin::c_be { + +HLSCodeGen::HLSCodeGen(World& world, const Cont2Config& kernel_config, int opt, bool debug) + : CodeGen(world, kernel_config, Lang::HLS, debug) +{} + +} diff --git a/src/thorin/be/c/hls.h b/src/thorin/be/c/hls.h new file mode 100644 index 000000000..53ea1ad6a --- /dev/null +++ b/src/thorin/be/c/hls.h @@ -0,0 +1,15 @@ +#ifndef THORIN_BE_LLVM_HLS_H +#define THORIN_BE_LLVM_HLS_H + +#include "c.h" + +namespace thorin::c_be { + +class HLSCodeGen : public CodeGen { +public: + HLSCodeGen(World& world, const Cont2Config&, int opt, bool debug); +}; + +} + +#endif diff --git a/src/thorin/be/c/opencl.cpp b/src/thorin/be/c/opencl.cpp index c93910f55..5d54ad4d5 100644 --- a/src/thorin/be/c/opencl.cpp +++ b/src/thorin/be/c/opencl.cpp @@ -1,21 +1,9 @@ #include "opencl.h" -#include -#include - -#include "thorin/primop.h" -#include "thorin/world.h" -#include "thorin/be/c/c.h" - namespace thorin::c_be { OpenCLCodeGen::OpenCLCodeGen(World& world, const Cont2Config& kernel_config, int opt, bool debug) - : CodeGen(world, debug) - , kernel_config_(kernel_config) + : CodeGen(world, kernel_config, Lang::OPENCL, debug) {} -void OpenCLCodeGen::emit(std::ostream& stream) { - emit_c(world(), kernel_config_, stream, Lang::OPENCL, debug()); -} - } diff --git a/src/thorin/be/c/opencl.h b/src/thorin/be/c/opencl.h index 3b472777e..3d082077f 100644 --- a/src/thorin/be/c/opencl.h +++ b/src/thorin/be/c/opencl.h @@ -1,19 +1,13 @@ #ifndef THORIN_BE_LLVM_OPENCL_H #define THORIN_BE_LLVM_OPENCL_H -#include "../backends.h" -#include "../llvm/llvm.h" +#include "c.h" namespace thorin::c_be { -class OpenCLCodeGen : public thorin::CodeGen { +class OpenCLCodeGen : public CodeGen { public: OpenCLCodeGen(World& world, const Cont2Config&, int opt, bool debug); - - void emit(std::ostream& stream) override; - -protected: - const Cont2Config& kernel_config_; }; } diff --git a/src/thorin/be/llvm/cuda.cpp b/src/thorin/be/llvm/cuda.cpp deleted file mode 100644 index 28b9824e7..000000000 --- a/src/thorin/be/llvm/cuda.cpp +++ /dev/null @@ -1,21 +0,0 @@ -#include "thorin/be/llvm/cuda.h" - -#include -#include - -#include "thorin/primop.h" -#include "thorin/world.h" -#include "thorin/be/c.h" - -namespace thorin::llvm_be { - -CUDACodeGen::CUDACodeGen(World& world, const Cont2Config& kernel_config, int opt, bool debug) - : CodeGen(world, llvm::CallingConv::C, llvm::CallingConv::C, llvm::CallingConv::C, opt, debug) - , kernel_config_(kernel_config) -{} - -void CUDACodeGen::emit(std::ostream& stream) { - thorin::emit_c(world(), kernel_config_, stream, Lang::CUDA, debug()); -} - -} diff --git a/src/thorin/be/llvm/cuda.h b/src/thorin/be/llvm/cuda.h deleted file mode 100644 index 3fe4472eb..000000000 --- a/src/thorin/be/llvm/cuda.h +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef THORIN_BE_LLVM_CUDA_H -#define THORIN_BE_LLVM_CUDA_H - -#include "thorin/be/llvm/llvm.h" - -namespace thorin::llvm_be { - -class CUDACodeGen : public CodeGen { -public: - CUDACodeGen(World& world, const Cont2Config&, int opt, bool debug); - - void emit(std::ostream& stream) override; - -protected: - virtual std::string get_alloc_name() const override { return "malloc"; } - - const Cont2Config& kernel_config_; -}; - -} - -#endif diff --git a/src/thorin/be/llvm/hls.cpp b/src/thorin/be/llvm/hls.cpp deleted file mode 100644 index 125722774..000000000 --- a/src/thorin/be/llvm/hls.cpp +++ /dev/null @@ -1,21 +0,0 @@ -#include "thorin/be/llvm/hls.h" - -#include -#include - -#include "thorin/primop.h" -#include "thorin/world.h" -#include "thorin/be/c.h" - -namespace thorin::llvm_be { - -HLSCodeGen::HLSCodeGen(World& world, const Cont2Config& kernel_config, int opt, bool debug) - : CodeGen(world, llvm::CallingConv::C, llvm::CallingConv::C, llvm::CallingConv::C, opt, debug) - , kernel_config_(kernel_config) -{} - -void HLSCodeGen::emit(std::ostream& stream) { - thorin::emit_c(world(), kernel_config_, stream, Lang::HLS, debug()); -} - -} diff --git a/src/thorin/be/llvm/hls.h b/src/thorin/be/llvm/hls.h deleted file mode 100644 index 7143a675e..000000000 --- a/src/thorin/be/llvm/hls.h +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef THORIN_BE_LLVM_HLS_H -#define THORIN_BE_LLVM_HLS_H - -#include "thorin/be/llvm/llvm.h" - -namespace thorin::llvm_be { - -class HLSCodeGen : public CodeGen { -public: - HLSCodeGen(World& world, const Cont2Config&, int opt, bool debug); - - void emit(std::ostream& stream) override; - -protected: - virtual std::string get_alloc_name() const override { THORIN_UNREACHABLE; /*alloc not supported in HLS*/; } - - const Cont2Config& kernel_config_; -}; - -} - -#endif From ed29b0e84f033068b6b026f403212442b6b01fba Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Mon, 15 Feb 2021 16:22:48 +0100 Subject: [PATCH 003/342] remove _be suffix from backend namespaces --- src/thorin/be/backends.cpp | 12 ++++++------ src/thorin/be/c/c.cpp | 2 +- src/thorin/be/c/c.h | 2 +- src/thorin/be/c/cuda.cpp | 2 +- src/thorin/be/c/cuda.h | 2 +- src/thorin/be/c/hls.cpp | 2 +- src/thorin/be/c/hls.h | 2 +- src/thorin/be/c/opencl.cpp | 2 +- src/thorin/be/c/opencl.h | 2 +- src/thorin/be/llvm/amdgpu.cpp | 2 +- src/thorin/be/llvm/amdgpu.h | 4 +++- src/thorin/be/llvm/cpu.cpp | 2 +- src/thorin/be/llvm/cpu.h | 4 +++- src/thorin/be/llvm/llvm.cpp | 2 +- src/thorin/be/llvm/llvm.h | 4 +++- src/thorin/be/llvm/nvvm.cpp | 2 +- src/thorin/be/llvm/nvvm.h | 4 +++- src/thorin/be/llvm/parallel.cpp | 2 +- src/thorin/be/llvm/runtime.cpp | 2 +- src/thorin/be/llvm/runtime.h | 4 +++- src/thorin/be/llvm/vectorize.cpp | 2 +- 21 files changed, 36 insertions(+), 26 deletions(-) diff --git a/src/thorin/be/backends.cpp b/src/thorin/be/backends.cpp index 6458bfca1..e3cbf14a7 100644 --- a/src/thorin/be/backends.cpp +++ b/src/thorin/be/backends.cpp @@ -179,16 +179,16 @@ Backends::Backends(World& world, int opt, bool debug) } #ifdef THORIN_ENABLE_LLVM - cpu_cg = std::make_unique(world, opt, debug); + cpu_cg = std::make_unique(world, opt, debug); - if (!nvvm. world().empty()) nvvm_cg = std::make_unique(nvvm .world(), kernel_config, debug); - if (!amdgpu.world().empty()) amdgpu_cg = std::make_unique(amdgpu.world(), kernel_config, opt, debug); + if (!nvvm. world().empty()) nvvm_cg = std::make_unique(nvvm .world(), kernel_config, debug); + if (!amdgpu.world().empty()) amdgpu_cg = std::make_unique(amdgpu.world(), kernel_config, opt, debug); #else // TODO: maybe use the C backend as a fallback when LLVM is not present for host codegen ? #endif - if (!cuda. world().empty()) cuda_cg = std::make_unique(cuda .world(), kernel_config, opt, debug); - if (!opencl.world().empty()) opencl_cg = std::make_unique(opencl.world(), kernel_config, opt, debug); - if (!hls. world().empty()) hls_cg = std::make_unique(hls .world(), kernel_config, opt, debug); + if (!cuda. world().empty()) cuda_cg = std::make_unique(cuda .world(), kernel_config, opt, debug); + if (!opencl.world().empty()) opencl_cg = std::make_unique(opencl.world(), kernel_config, opt, debug); + if (!hls. world().empty()) hls_cg = std::make_unique(hls .world(), kernel_config, opt, debug); } CodeGen::CodeGen(World& world, bool debug) diff --git a/src/thorin/be/c/c.cpp b/src/thorin/be/c/c.cpp index daf1b4f18..ce93ef399 100644 --- a/src/thorin/be/c/c.cpp +++ b/src/thorin/be/c/c.cpp @@ -15,7 +15,7 @@ #include #include -namespace thorin::c_be { +namespace thorin::c { class CCodeGen; diff --git a/src/thorin/be/c/c.h b/src/thorin/be/c/c.h index 818eaed81..368ceb94e 100644 --- a/src/thorin/be/c/c.h +++ b/src/thorin/be/c/c.h @@ -10,7 +10,7 @@ namespace thorin { class World; -namespace c_be { +namespace c { enum class Lang : uint8_t { C99, ///< Flag for C99 diff --git a/src/thorin/be/c/cuda.cpp b/src/thorin/be/c/cuda.cpp index 815173a1b..ec7c92ab1 100644 --- a/src/thorin/be/c/cuda.cpp +++ b/src/thorin/be/c/cuda.cpp @@ -1,6 +1,6 @@ #include "cuda.h" -namespace thorin::c_be { +namespace thorin::c { CUDACodeGen::CUDACodeGen(World& world, const Cont2Config& kernel_config, int opt, bool debug) : CodeGen(world, kernel_config, Lang::CUDA, debug) diff --git a/src/thorin/be/c/cuda.h b/src/thorin/be/c/cuda.h index a6200e294..d55b8e9de 100644 --- a/src/thorin/be/c/cuda.h +++ b/src/thorin/be/c/cuda.h @@ -3,7 +3,7 @@ #include "c.h" -namespace thorin::c_be { +namespace thorin::c { class CUDACodeGen : public CodeGen { public: diff --git a/src/thorin/be/c/hls.cpp b/src/thorin/be/c/hls.cpp index 66660f431..ec2bbb43e 100644 --- a/src/thorin/be/c/hls.cpp +++ b/src/thorin/be/c/hls.cpp @@ -1,6 +1,6 @@ #include "hls.h" -namespace thorin::c_be { +namespace thorin::c { HLSCodeGen::HLSCodeGen(World& world, const Cont2Config& kernel_config, int opt, bool debug) : CodeGen(world, kernel_config, Lang::HLS, debug) diff --git a/src/thorin/be/c/hls.h b/src/thorin/be/c/hls.h index 53ea1ad6a..3f05ebf79 100644 --- a/src/thorin/be/c/hls.h +++ b/src/thorin/be/c/hls.h @@ -3,7 +3,7 @@ #include "c.h" -namespace thorin::c_be { +namespace thorin::c { class HLSCodeGen : public CodeGen { public: diff --git a/src/thorin/be/c/opencl.cpp b/src/thorin/be/c/opencl.cpp index 5d54ad4d5..295dd9b47 100644 --- a/src/thorin/be/c/opencl.cpp +++ b/src/thorin/be/c/opencl.cpp @@ -1,6 +1,6 @@ #include "opencl.h" -namespace thorin::c_be { +namespace thorin::c { OpenCLCodeGen::OpenCLCodeGen(World& world, const Cont2Config& kernel_config, int opt, bool debug) : CodeGen(world, kernel_config, Lang::OPENCL, debug) diff --git a/src/thorin/be/c/opencl.h b/src/thorin/be/c/opencl.h index 3d082077f..0e4cab1da 100644 --- a/src/thorin/be/c/opencl.h +++ b/src/thorin/be/c/opencl.h @@ -3,7 +3,7 @@ #include "c.h" -namespace thorin::c_be { +namespace thorin::c { class OpenCLCodeGen : public CodeGen { public: diff --git a/src/thorin/be/llvm/amdgpu.cpp b/src/thorin/be/llvm/amdgpu.cpp index 859948be4..bd80736d0 100644 --- a/src/thorin/be/llvm/amdgpu.cpp +++ b/src/thorin/be/llvm/amdgpu.cpp @@ -3,7 +3,7 @@ #include "thorin/primop.h" #include "thorin/world.h" -namespace thorin::llvm_be { +namespace thorin::llvm { AMDGPUCodeGen::AMDGPUCodeGen(World& world, const Cont2Config& kernel_config, int opt, bool debug) : CodeGen(world, llvm::CallingConv::C, llvm::CallingConv::C, llvm::CallingConv::AMDGPU_KERNEL, opt, debug) diff --git a/src/thorin/be/llvm/amdgpu.h b/src/thorin/be/llvm/amdgpu.h index 33866fc63..80e325023 100644 --- a/src/thorin/be/llvm/amdgpu.h +++ b/src/thorin/be/llvm/amdgpu.h @@ -7,7 +7,9 @@ namespace thorin { class Load; -namespace llvm_be { +namespace llvm { + +namespace llvm = ::llvm; class AMDGPUCodeGen : public CodeGen { public: diff --git a/src/thorin/be/llvm/cpu.cpp b/src/thorin/be/llvm/cpu.cpp index ed6e92772..ffd06af06 100644 --- a/src/thorin/be/llvm/cpu.cpp +++ b/src/thorin/be/llvm/cpu.cpp @@ -8,7 +8,7 @@ #include #include -namespace thorin::llvm_be { +namespace thorin::llvm { CPUCodeGen::CPUCodeGen(World& world, int opt, bool debug) : CodeGen(world, llvm::CallingConv::C, llvm::CallingConv::C, llvm::CallingConv::C, opt, debug) diff --git a/src/thorin/be/llvm/cpu.h b/src/thorin/be/llvm/cpu.h index 7607cdcb6..462ca3e21 100644 --- a/src/thorin/be/llvm/cpu.h +++ b/src/thorin/be/llvm/cpu.h @@ -3,7 +3,9 @@ #include "thorin/be/llvm/llvm.h" -namespace thorin::llvm_be { +namespace thorin::llvm { + +namespace llvm = ::llvm; class CPUCodeGen : public CodeGen { public: diff --git a/src/thorin/be/llvm/llvm.cpp b/src/thorin/be/llvm/llvm.cpp index 5f267f947..5a2c867ce 100644 --- a/src/thorin/be/llvm/llvm.cpp +++ b/src/thorin/be/llvm/llvm.cpp @@ -38,7 +38,7 @@ #include "thorin/analyses/scope.h" #include "thorin/util/array.h" -namespace thorin::llvm_be { +namespace thorin::llvm { CodeGen::CodeGen(World& world, llvm::CallingConv::ID function_calling_convention, diff --git a/src/thorin/be/llvm/llvm.h b/src/thorin/be/llvm/llvm.h index 7f41d51e8..518db9ef6 100644 --- a/src/thorin/be/llvm/llvm.h +++ b/src/thorin/be/llvm/llvm.h @@ -18,7 +18,9 @@ namespace thorin { class World; -namespace llvm_be { +namespace llvm { + +namespace llvm = ::llvm; class CodeGen : public thorin::CodeGen { protected: diff --git a/src/thorin/be/llvm/nvvm.cpp b/src/thorin/be/llvm/nvvm.cpp index 1fd8c7818..3a109ab84 100644 --- a/src/thorin/be/llvm/nvvm.cpp +++ b/src/thorin/be/llvm/nvvm.cpp @@ -16,7 +16,7 @@ #include "thorin/primop.h" #include "thorin/world.h" -namespace thorin::llvm_be { +namespace thorin::llvm { NVVMCodeGen::NVVMCodeGen(World& world, const Cont2Config& kernel_config, bool debug) : CodeGen(world, llvm::CallingConv::C, llvm::CallingConv::PTX_Device, llvm::CallingConv::PTX_Kernel, 0, debug) diff --git a/src/thorin/be/llvm/nvvm.h b/src/thorin/be/llvm/nvvm.h index f637304c4..541530868 100644 --- a/src/thorin/be/llvm/nvvm.h +++ b/src/thorin/be/llvm/nvvm.h @@ -7,7 +7,9 @@ namespace thorin { class Load; -namespace llvm_be { +namespace llvm { + +namespace llvm = ::llvm; class NVVMCodeGen : public CodeGen { public: diff --git a/src/thorin/be/llvm/parallel.cpp b/src/thorin/be/llvm/parallel.cpp index 128728fad..16247bf47 100644 --- a/src/thorin/be/llvm/parallel.cpp +++ b/src/thorin/be/llvm/parallel.cpp @@ -1,6 +1,6 @@ #include "thorin/be/llvm/llvm.h" -namespace thorin::llvm_be { +namespace thorin::llvm { enum { PAR_ARG_MEM, diff --git a/src/thorin/be/llvm/runtime.cpp b/src/thorin/be/llvm/runtime.cpp index b7b38070c..ee653bccb 100644 --- a/src/thorin/be/llvm/runtime.cpp +++ b/src/thorin/be/llvm/runtime.cpp @@ -13,7 +13,7 @@ #include "thorin/be/llvm/llvm.h" #include "thorin/be/llvm/runtime.inc" -namespace thorin::llvm_be { +namespace thorin::llvm { Runtime::Runtime(llvm::LLVMContext& context, llvm::Module& target) diff --git a/src/thorin/be/llvm/runtime.h b/src/thorin/be/llvm/runtime.h index c125deed5..3035704ef 100644 --- a/src/thorin/be/llvm/runtime.h +++ b/src/thorin/be/llvm/runtime.h @@ -8,7 +8,9 @@ #include "thorin/world.h" -namespace thorin::llvm_be { +namespace thorin::llvm { + +namespace llvm = ::llvm; class CodeGen; diff --git a/src/thorin/be/llvm/vectorize.cpp b/src/thorin/be/llvm/vectorize.cpp index 529ac844b..12ba8b840 100644 --- a/src/thorin/be/llvm/vectorize.cpp +++ b/src/thorin/be/llvm/vectorize.cpp @@ -36,7 +36,7 @@ #include "thorin/world.h" #include "thorin/analyses/scope.h" -namespace thorin::llvm_be { +namespace thorin::llvm { struct VectorizeArgs { enum { From c0bdbed96d423cdcbea43316c4cbdb8f6c4b5e68 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Mon, 15 Feb 2021 16:26:32 +0100 Subject: [PATCH 004/342] nuke redundant wrapper classes --- src/thorin/CMakeLists.txt | 6 ------ src/thorin/be/backends.cpp | 10 ++++------ src/thorin/be/c/cuda.cpp | 9 --------- src/thorin/be/c/cuda.h | 15 --------------- src/thorin/be/c/hls.cpp | 9 --------- src/thorin/be/c/hls.h | 15 --------------- src/thorin/be/c/opencl.cpp | 9 --------- src/thorin/be/c/opencl.h | 15 --------------- 8 files changed, 4 insertions(+), 84 deletions(-) delete mode 100644 src/thorin/be/c/cuda.cpp delete mode 100644 src/thorin/be/c/cuda.h delete mode 100644 src/thorin/be/c/hls.cpp delete mode 100644 src/thorin/be/c/hls.h delete mode 100644 src/thorin/be/c/opencl.cpp delete mode 100644 src/thorin/be/c/opencl.h diff --git a/src/thorin/CMakeLists.txt b/src/thorin/CMakeLists.txt index b0c18ea7c..81bf603a8 100644 --- a/src/thorin/CMakeLists.txt +++ b/src/thorin/CMakeLists.txt @@ -32,12 +32,6 @@ set(THORIN_SOURCES analyses/verify.h be/c/c.cpp be/c/c.h - be/c/cuda.cpp - be/c/cuda.h - be/c/hls.cpp - be/c/hls.h - be/c/opencl.cpp - be/c/opencl.h be/kernel_config.h tables/allnodes.h tables/arithoptable.h diff --git a/src/thorin/be/backends.cpp b/src/thorin/be/backends.cpp index e3cbf14a7..3ad7a55b8 100644 --- a/src/thorin/be/backends.cpp +++ b/src/thorin/be/backends.cpp @@ -7,9 +7,7 @@ #include "thorin/be/llvm/nvvm.h" #include "thorin/be/llvm/amdgpu.h" #endif -#include "thorin/be/c/cuda.h" -#include "thorin/be/c/hls.h" -#include "thorin/be/c/opencl.h" +#include "thorin/be/c/c.h" namespace thorin { @@ -186,9 +184,9 @@ Backends::Backends(World& world, int opt, bool debug) #else // TODO: maybe use the C backend as a fallback when LLVM is not present for host codegen ? #endif - if (!cuda. world().empty()) cuda_cg = std::make_unique(cuda .world(), kernel_config, opt, debug); - if (!opencl.world().empty()) opencl_cg = std::make_unique(opencl.world(), kernel_config, opt, debug); - if (!hls. world().empty()) hls_cg = std::make_unique(hls .world(), kernel_config, opt, debug); + if (!cuda. world().empty()) cuda_cg = std::make_unique(cuda .world(), kernel_config, c::Lang::CUDA , debug); + if (!opencl.world().empty()) opencl_cg = std::make_unique(opencl.world(), kernel_config, c::Lang::OPENCL, debug); + if (!hls. world().empty()) hls_cg = std::make_unique(hls .world(), kernel_config, c::Lang::HLS , debug); } CodeGen::CodeGen(World& world, bool debug) diff --git a/src/thorin/be/c/cuda.cpp b/src/thorin/be/c/cuda.cpp deleted file mode 100644 index ec7c92ab1..000000000 --- a/src/thorin/be/c/cuda.cpp +++ /dev/null @@ -1,9 +0,0 @@ -#include "cuda.h" - -namespace thorin::c { - -CUDACodeGen::CUDACodeGen(World& world, const Cont2Config& kernel_config, int opt, bool debug) - : CodeGen(world, kernel_config, Lang::CUDA, debug) -{} - -} diff --git a/src/thorin/be/c/cuda.h b/src/thorin/be/c/cuda.h deleted file mode 100644 index d55b8e9de..000000000 --- a/src/thorin/be/c/cuda.h +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef THORIN_BE_LLVM_CUDA_H -#define THORIN_BE_LLVM_CUDA_H - -#include "c.h" - -namespace thorin::c { - -class CUDACodeGen : public CodeGen { -public: - CUDACodeGen(World &world, const Cont2Config &, int opt, bool debug); -}; - -} - -#endif diff --git a/src/thorin/be/c/hls.cpp b/src/thorin/be/c/hls.cpp deleted file mode 100644 index ec2bbb43e..000000000 --- a/src/thorin/be/c/hls.cpp +++ /dev/null @@ -1,9 +0,0 @@ -#include "hls.h" - -namespace thorin::c { - -HLSCodeGen::HLSCodeGen(World& world, const Cont2Config& kernel_config, int opt, bool debug) - : CodeGen(world, kernel_config, Lang::HLS, debug) -{} - -} diff --git a/src/thorin/be/c/hls.h b/src/thorin/be/c/hls.h deleted file mode 100644 index 3f05ebf79..000000000 --- a/src/thorin/be/c/hls.h +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef THORIN_BE_LLVM_HLS_H -#define THORIN_BE_LLVM_HLS_H - -#include "c.h" - -namespace thorin::c { - -class HLSCodeGen : public CodeGen { -public: - HLSCodeGen(World& world, const Cont2Config&, int opt, bool debug); -}; - -} - -#endif diff --git a/src/thorin/be/c/opencl.cpp b/src/thorin/be/c/opencl.cpp deleted file mode 100644 index 295dd9b47..000000000 --- a/src/thorin/be/c/opencl.cpp +++ /dev/null @@ -1,9 +0,0 @@ -#include "opencl.h" - -namespace thorin::c { - -OpenCLCodeGen::OpenCLCodeGen(World& world, const Cont2Config& kernel_config, int opt, bool debug) - : CodeGen(world, kernel_config, Lang::OPENCL, debug) -{} - -} diff --git a/src/thorin/be/c/opencl.h b/src/thorin/be/c/opencl.h deleted file mode 100644 index 0e4cab1da..000000000 --- a/src/thorin/be/c/opencl.h +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef THORIN_BE_LLVM_OPENCL_H -#define THORIN_BE_LLVM_OPENCL_H - -#include "c.h" - -namespace thorin::c { - -class OpenCLCodeGen : public CodeGen { -public: - OpenCLCodeGen(World& world, const Cont2Config&, int opt, bool debug); -}; - -} - -#endif From e9b6930ddd17db414f6d639022537c1748a53315 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Mon, 15 Feb 2021 17:42:50 +0100 Subject: [PATCH 005/342] fix formatting --- src/thorin/be/backends.cpp | 14 +++++++------- src/thorin/be/c/c.h | 9 +++++---- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/src/thorin/be/backends.cpp b/src/thorin/be/backends.cpp index 3ad7a55b8..c8bf65f98 100644 --- a/src/thorin/be/backends.cpp +++ b/src/thorin/be/backends.cpp @@ -72,11 +72,11 @@ static uint64_t get_alloc_size(const Def* def) { } Backends::Backends(World& world, int opt, bool debug) -: cuda(world) -, nvvm(world) -, opencl(world) -, amdgpu(world) -, hls(world) + : cuda(world) + , nvvm(world) + , opencl(world) + , amdgpu(world) + , hls(world) { // determine different parts of the world which need to be compiled differently Scope::for_each(world, [&] (const Scope& scope) { @@ -190,8 +190,8 @@ Backends::Backends(World& world, int opt, bool debug) } CodeGen::CodeGen(World& world, bool debug) -: world_(world) -, debug_(debug) + : world_(world) + , debug_(debug) {} } diff --git a/src/thorin/be/c/c.h b/src/thorin/be/c/c.h index 368ceb94e..bfb98e307 100644 --- a/src/thorin/be/c/c.h +++ b/src/thorin/be/c/c.h @@ -22,10 +22,11 @@ enum class Lang : uint8_t { class CodeGen : public thorin::CodeGen { public: CodeGen(World& world, const Cont2Config& kernel_config, Lang lang, bool debug) - : thorin::CodeGen(world, debug) - , kernel_config_(kernel_config) - , lang_(lang) - , debug_(debug) {} + : thorin::CodeGen(world, debug) + , kernel_config_(kernel_config) + , lang_(lang) + , debug_(debug) + {} void emit(std::ostream& stream) override; From 6bbed3a9c8c921921e63fa43c595bfc5f1ad734d Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Mon, 15 Feb 2021 18:29:07 +0100 Subject: [PATCH 006/342] generalize backends logic --- src/thorin/be/backends.cpp | 58 +++++++++++++++++--------------------- src/thorin/be/backends.h | 18 ++++-------- 2 files changed, 31 insertions(+), 45 deletions(-) diff --git a/src/thorin/be/backends.cpp b/src/thorin/be/backends.cpp index c8bf65f98..dc4fd6953 100644 --- a/src/thorin/be/backends.cpp +++ b/src/thorin/be/backends.cpp @@ -72,28 +72,22 @@ static uint64_t get_alloc_size(const Def* def) { } Backends::Backends(World& world, int opt, bool debug) - : cuda(world) - , nvvm(world) - , opencl(world) - , amdgpu(world) - , hls(world) + : device_cgs({}) { + for (int backend = 0; backend < BackendCount; backend++) + importers_.emplace_back(world); + // determine different parts of the world which need to be compiled differently Scope::for_each(world, [&] (const Scope& scope) { auto continuation = scope.entry(); Continuation* imported = nullptr; - if (is_passed_to_intrinsic(continuation, Intrinsic::CUDA)) - imported = cuda.import(continuation)->as_continuation(); - else if (is_passed_to_intrinsic(continuation, Intrinsic::NVVM)) - imported = nvvm.import(continuation)->as_continuation(); - else if (is_passed_to_intrinsic(continuation, Intrinsic::OpenCL)) - imported = opencl.import(continuation)->as_continuation(); - else if (is_passed_to_intrinsic(continuation, Intrinsic::AMDGPU)) - imported = amdgpu.import(continuation)->as_continuation(); - else if (is_passed_to_intrinsic(continuation, Intrinsic::HLS)) - imported = hls.import(continuation)->as_continuation(); - else - return; + for (int backend = 0; backend <= BackendCount; backend++) { + if (backend == BackendCount) return; + if (is_passed_to_intrinsic(continuation, Intrinsic(int(Intrinsic::AcceleratorBegin) + backend))) { + imported = importers_[backend].import(continuation)->as_continuation(); + break; + } + } imported->set_name(continuation->unique_name()); imported->make_exported(); @@ -106,10 +100,10 @@ Backends::Backends(World& world, int opt, bool debug) }); // get the GPU kernel configurations - if (!cuda.world().empty() || - !nvvm.world().empty() || - !opencl.world().empty() || - !amdgpu.world().empty()) { + if (!importers_[Cuda ].world().empty() || + !importers_[NVVM ].world().empty() || + !importers_[OpenCL].world().empty() || + !importers_[AMDGPU].world().empty()) { auto get_gpu_config = [&] (Continuation* use, Continuation* /* imported */) { // determine whether or not this kernel uses restrict pointers bool has_restrict = true; @@ -135,14 +129,14 @@ Backends::Backends(World& world, int opt, bool debug) } return std::make_unique(std::tuple { -1, -1, -1 }, has_restrict); }; - get_kernel_configs(cuda, kernels, kernel_config, get_gpu_config); - get_kernel_configs(nvvm, kernels, kernel_config, get_gpu_config); - get_kernel_configs(opencl, kernels, kernel_config, get_gpu_config); - get_kernel_configs(amdgpu, kernels, kernel_config, get_gpu_config); + get_kernel_configs(importers_[Cuda ], kernels, kernel_config, get_gpu_config); + get_kernel_configs(importers_[NVVM ], kernels, kernel_config, get_gpu_config); + get_kernel_configs(importers_[OpenCL], kernels, kernel_config, get_gpu_config); + get_kernel_configs(importers_[AMDGPU], kernels, kernel_config, get_gpu_config); } // get the HLS kernel configurations - if (!hls.world().empty()) { + if (!importers_[HLS].world().empty()) { auto get_hls_config = [&] (Continuation* use, Continuation* imported) { HLSKernelConfig::Param2Size param_sizes; for (size_t i = 3, e = use->num_args(); i != e; ++i) { @@ -173,20 +167,20 @@ Backends::Backends(World& world, int opt, bool debug) } return std::make_unique(param_sizes); }; - get_kernel_configs(hls, kernels, kernel_config, get_hls_config); + get_kernel_configs(importers_[HLS], kernels, kernel_config, get_hls_config); } #ifdef THORIN_ENABLE_LLVM cpu_cg = std::make_unique(world, opt, debug); - if (!nvvm. world().empty()) nvvm_cg = std::make_unique(nvvm .world(), kernel_config, debug); - if (!amdgpu.world().empty()) amdgpu_cg = std::make_unique(amdgpu.world(), kernel_config, opt, debug); + if (!importers_[NVVM ].world().empty()) device_cgs[NVVM ] = std::make_unique(importers_[NVVM ].world(), kernel_config, debug); + if (!importers_[AMDGPU].world().empty()) device_cgs[AMDGPU] = std::make_unique(importers_[AMDGPU].world(), kernel_config, opt, debug); #else // TODO: maybe use the C backend as a fallback when LLVM is not present for host codegen ? #endif - if (!cuda. world().empty()) cuda_cg = std::make_unique(cuda .world(), kernel_config, c::Lang::CUDA , debug); - if (!opencl.world().empty()) opencl_cg = std::make_unique(opencl.world(), kernel_config, c::Lang::OPENCL, debug); - if (!hls. world().empty()) hls_cg = std::make_unique(hls .world(), kernel_config, c::Lang::HLS , debug); + if (!importers_[Cuda ].world().empty()) device_cgs[Cuda ] = std::make_unique(importers_[Cuda ].world(), kernel_config, c::Lang::CUDA , debug); + if (!importers_[OpenCL].world().empty()) device_cgs[OpenCL] = std::make_unique(importers_[OpenCL].world(), kernel_config, c::Lang::OPENCL, debug); + if (!importers_[HLS ].world().empty()) device_cgs[HLS ] = std::make_unique(importers_[HLS ].world(), kernel_config, c::Lang::HLS , debug); } CodeGen::CodeGen(World& world, bool debug) diff --git a/src/thorin/be/backends.h b/src/thorin/be/backends.h index b9c309889..0879b4426 100644 --- a/src/thorin/be/backends.h +++ b/src/thorin/be/backends.h @@ -41,20 +41,12 @@ struct Backends { Cont2Config kernel_config; std::vector kernels; - // TODO use arrays + loops for this - Importer cuda; - Importer nvvm; - Importer opencl; - Importer amdgpu; - Importer hls; - - // TODO use arrays + loops for this std::unique_ptr cpu_cg; - std::unique_ptr cuda_cg; - std::unique_ptr nvvm_cg; - std::unique_ptr opencl_cg; - std::unique_ptr amdgpu_cg; - std::unique_ptr hls_cg; + + enum { Cuda, NVVM, OpenCL, AMDGPU, HLS, BackendCount }; + std::array, BackendCount> device_cgs; +private: + std::vector importers_; }; } From 0f279243f27be9ab5d3c5e3e3f126ee943c16387 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Mon, 15 Feb 2021 18:43:22 +0100 Subject: [PATCH 007/342] generalize gpu kernel setup --- src/thorin/be/backends.cpp | 61 +++++++++++++++++--------------------- src/thorin/be/backends.h | 3 ++ 2 files changed, 31 insertions(+), 33 deletions(-) diff --git a/src/thorin/be/backends.cpp b/src/thorin/be/backends.cpp index dc4fd6953..a7b5d0eab 100644 --- a/src/thorin/be/backends.cpp +++ b/src/thorin/be/backends.cpp @@ -99,40 +99,35 @@ Backends::Backends(World& world, int opt, bool debug) kernels.emplace_back(continuation); }); - // get the GPU kernel configurations - if (!importers_[Cuda ].world().empty() || - !importers_[NVVM ].world().empty() || - !importers_[OpenCL].world().empty() || - !importers_[AMDGPU].world().empty()) { - auto get_gpu_config = [&] (Continuation* use, Continuation* /* imported */) { - // determine whether or not this kernel uses restrict pointers - bool has_restrict = true; - DefSet allocs; - for (size_t i = LaunchArgs::Num, e = use->num_args(); has_restrict && i != e; ++i) { - auto arg = use->arg(i); - if (!arg->type()->isa()) continue; - auto alloc = get_alloc_call(arg); - if (!alloc) has_restrict = false; - auto p = allocs.insert(alloc); - has_restrict &= p.second; - } + for (auto backend : gpu_compute_backends) { + if (!importers_[backend].world().empty()) { + auto get_gpu_config = [&](Continuation *use, Continuation * /* imported */) { + // determine whether or not this kernel uses restrict pointers + bool has_restrict = true; + DefSet allocs; + for (size_t i = LaunchArgs::Num, e = use->num_args(); has_restrict && i != e; ++i) { + auto arg = use->arg(i); + if (!arg->type()->isa()) continue; + auto alloc = get_alloc_call(arg); + if (!alloc) has_restrict = false; + auto p = allocs.insert(alloc); + has_restrict &= p.second; + } - auto it_config = use->arg(LaunchArgs::Config)->as(); - if (it_config->op(0)->isa() && - it_config->op(1)->isa() && - it_config->op(2)->isa()) { - return std::make_unique(std::tuple { - it_config->op(0)->as()->qu32_value().data(), - it_config->op(1)->as()->qu32_value().data(), - it_config->op(2)->as()->qu32_value().data() - }, has_restrict); - } - return std::make_unique(std::tuple { -1, -1, -1 }, has_restrict); - }; - get_kernel_configs(importers_[Cuda ], kernels, kernel_config, get_gpu_config); - get_kernel_configs(importers_[NVVM ], kernels, kernel_config, get_gpu_config); - get_kernel_configs(importers_[OpenCL], kernels, kernel_config, get_gpu_config); - get_kernel_configs(importers_[AMDGPU], kernels, kernel_config, get_gpu_config); + auto it_config = use->arg(LaunchArgs::Config)->as(); + if (it_config->op(0)->isa() && + it_config->op(1)->isa() && + it_config->op(2)->isa()) { + return std::make_unique(std::tuple{ + it_config->op(0)->as()->qu32_value().data(), + it_config->op(1)->as()->qu32_value().data(), + it_config->op(2)->as()->qu32_value().data() + }, has_restrict); + } + return std::make_unique(std::tuple{-1, -1, -1}, has_restrict); + }; + get_kernel_configs(importers_[backend], kernels, kernel_config, get_gpu_config); + } } // get the HLS kernel configurations diff --git a/src/thorin/be/backends.h b/src/thorin/be/backends.h index 0879b4426..d774db009 100644 --- a/src/thorin/be/backends.h +++ b/src/thorin/be/backends.h @@ -45,6 +45,9 @@ struct Backends { enum { Cuda, NVVM, OpenCL, AMDGPU, HLS, BackendCount }; std::array, BackendCount> device_cgs; + + /// Backends that need GPUKernelConfig + static constexpr auto gpu_compute_backends = { Cuda, NVVM, OpenCL, AMDGPU }; private: std::vector importers_; }; From 97196bb6af50673ee3e13c81e6a64850a33fa1f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ars=C3=A8ne=20P=C3=A9rard-Gayot?= Date: Mon, 15 Feb 2021 19:11:07 +0100 Subject: [PATCH 008/342] Fix #ifdefs --- src/thorin/be/backends.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/thorin/be/backends.cpp b/src/thorin/be/backends.cpp index a7b5d0eab..9982da433 100644 --- a/src/thorin/be/backends.cpp +++ b/src/thorin/be/backends.cpp @@ -2,7 +2,7 @@ #include "thorin/analyses/scope.h" -#ifdef THORIN_ENABLE_LLVM +#if THORIN_ENABLE_LLVM #include "thorin/be/llvm/cpu.h" #include "thorin/be/llvm/nvvm.h" #include "thorin/be/llvm/amdgpu.h" @@ -165,7 +165,7 @@ Backends::Backends(World& world, int opt, bool debug) get_kernel_configs(importers_[HLS], kernels, kernel_config, get_hls_config); } -#ifdef THORIN_ENABLE_LLVM +#if THORIN_ENABLE_LLVM cpu_cg = std::make_unique(world, opt, debug); if (!importers_[NVVM ].world().empty()) device_cgs[NVVM ] = std::make_unique(importers_[NVVM ].world(), kernel_config, debug); From fc8e684e7cf81a0a40a0cd216b31f2ef244c3fba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ars=C3=A8ne=20P=C3=A9rard-Gayot?= Date: Mon, 15 Feb 2021 19:11:31 +0100 Subject: [PATCH 009/342] Inline lambdas that are used once --- src/thorin/be/backends.cpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/thorin/be/backends.cpp b/src/thorin/be/backends.cpp index 9982da433..df4c6e9d3 100644 --- a/src/thorin/be/backends.cpp +++ b/src/thorin/be/backends.cpp @@ -101,7 +101,7 @@ Backends::Backends(World& world, int opt, bool debug) for (auto backend : gpu_compute_backends) { if (!importers_[backend].world().empty()) { - auto get_gpu_config = [&](Continuation *use, Continuation * /* imported */) { + get_kernel_configs(importers_[backend], kernels, kernel_config, [&](Continuation *use, Continuation * /* imported */) { // determine whether or not this kernel uses restrict pointers bool has_restrict = true; DefSet allocs; @@ -125,14 +125,13 @@ Backends::Backends(World& world, int opt, bool debug) }, has_restrict); } return std::make_unique(std::tuple{-1, -1, -1}, has_restrict); - }; - get_kernel_configs(importers_[backend], kernels, kernel_config, get_gpu_config); + }); } } // get the HLS kernel configurations if (!importers_[HLS].world().empty()) { - auto get_hls_config = [&] (Continuation* use, Continuation* imported) { + get_kernel_configs(importers_[HLS], kernels, kernel_config, [&] (Continuation* use, Continuation* imported) { HLSKernelConfig::Param2Size param_sizes; for (size_t i = 3, e = use->num_args(); i != e; ++i) { auto arg = use->arg(i); @@ -161,8 +160,7 @@ Backends::Backends(World& world, int opt, bool debug) param_sizes.emplace(imported->param(i - 3 + 2), num_elems); } return std::make_unique(param_sizes); - }; - get_kernel_configs(importers_[HLS], kernels, kernel_config, get_hls_config); + }); } #if THORIN_ENABLE_LLVM From 9665f670ebe661173123444af876faf13c6fe3a2 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Mon, 15 Feb 2021 19:17:46 +0100 Subject: [PATCH 010/342] add file extensions for backends --- src/thorin/be/backends.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/thorin/be/backends.h b/src/thorin/be/backends.h index d774db009..dce12fd55 100644 --- a/src/thorin/be/backends.h +++ b/src/thorin/be/backends.h @@ -48,6 +48,8 @@ struct Backends { /// Backends that need GPUKernelConfig static constexpr auto gpu_compute_backends = { Cuda, NVVM, OpenCL, AMDGPU }; + + static constexpr std::array backends_extensions = { ".cu", ".nvvm", ".cl", ".amdgpu", ".hls" }; private: std::vector importers_; }; From b0a7af5a9512121a9b19fae78a73291d7e14b27a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ars=C3=A8ne=20P=C3=A9rard-Gayot?= Date: Mon, 15 Feb 2021 19:25:19 +0100 Subject: [PATCH 011/342] Cleanup --- src/thorin/be/backends.cpp | 2 +- src/thorin/be/backends.h | 5 +---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/src/thorin/be/backends.cpp b/src/thorin/be/backends.cpp index df4c6e9d3..8a838f203 100644 --- a/src/thorin/be/backends.cpp +++ b/src/thorin/be/backends.cpp @@ -99,7 +99,7 @@ Backends::Backends(World& world, int opt, bool debug) kernels.emplace_back(continuation); }); - for (auto backend : gpu_compute_backends) { + for (auto backend : std::array { Cuda, NVVM, OpenCL, AMDGPU }) { if (!importers_[backend].world().empty()) { get_kernel_configs(importers_[backend], kernels, kernel_config, [&](Continuation *use, Continuation * /* imported */) { // determine whether or not this kernel uses restrict pointers diff --git a/src/thorin/be/backends.h b/src/thorin/be/backends.h index dce12fd55..d0093e9e0 100644 --- a/src/thorin/be/backends.h +++ b/src/thorin/be/backends.h @@ -46,10 +46,7 @@ struct Backends { enum { Cuda, NVVM, OpenCL, AMDGPU, HLS, BackendCount }; std::array, BackendCount> device_cgs; - /// Backends that need GPUKernelConfig - static constexpr auto gpu_compute_backends = { Cuda, NVVM, OpenCL, AMDGPU }; - - static constexpr std::array backends_extensions = { ".cu", ".nvvm", ".cl", ".amdgpu", ".hls" }; + static constexpr const char* file_exts[] = { ".cu", ".nvvm", ".cl", ".amdgpu", ".hls" }; private: std::vector importers_; }; From 8c49aaed74b35f43e85cfe02000375fa47b2b1a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ars=C3=A8ne=20P=C3=A9rard-Gayot?= Date: Mon, 15 Feb 2021 19:26:20 +0100 Subject: [PATCH 012/342] Indentation --- src/thorin/be/backends.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/thorin/be/backends.cpp b/src/thorin/be/backends.cpp index 8a838f203..10904cd80 100644 --- a/src/thorin/be/backends.cpp +++ b/src/thorin/be/backends.cpp @@ -119,9 +119,9 @@ Backends::Backends(World& world, int opt, bool debug) it_config->op(1)->isa() && it_config->op(2)->isa()) { return std::make_unique(std::tuple{ - it_config->op(0)->as()->qu32_value().data(), - it_config->op(1)->as()->qu32_value().data(), - it_config->op(2)->as()->qu32_value().data() + it_config->op(0)->as()->qu32_value().data(), + it_config->op(1)->as()->qu32_value().data(), + it_config->op(2)->as()->qu32_value().data() }, has_restrict); } return std::make_unique(std::tuple{-1, -1, -1}, has_restrict); From 76314568aede9aa7119a7caf91ee973052be5a46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ars=C3=A8ne=20P=C3=A9rard-Gayot?= Date: Mon, 15 Feb 2021 19:31:43 +0100 Subject: [PATCH 013/342] Simplify --- src/thorin/be/backends.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/thorin/be/backends.cpp b/src/thorin/be/backends.cpp index 10904cd80..df31fbb36 100644 --- a/src/thorin/be/backends.cpp +++ b/src/thorin/be/backends.cpp @@ -171,9 +171,8 @@ Backends::Backends(World& world, int opt, bool debug) #else // TODO: maybe use the C backend as a fallback when LLVM is not present for host codegen ? #endif - if (!importers_[Cuda ].world().empty()) device_cgs[Cuda ] = std::make_unique(importers_[Cuda ].world(), kernel_config, c::Lang::CUDA , debug); - if (!importers_[OpenCL].world().empty()) device_cgs[OpenCL] = std::make_unique(importers_[OpenCL].world(), kernel_config, c::Lang::OPENCL, debug); - if (!importers_[HLS ].world().empty()) device_cgs[HLS ] = std::make_unique(importers_[HLS ].world(), kernel_config, c::Lang::HLS , debug); + for (auto [backend, lang] : std::array { std::pair { Cuda, c::Lang::CUDA }, std::pair { OpenCL, c::Lang::OPENCL }, std::pair { HLS, c::Lang::HLS } }) + if (!importers_[backend].world().empty()) device_cgs[backend] = std::make_unique(importers_[backend].world(), kernel_config, lang, debug); } CodeGen::CodeGen(World& world, bool debug) From ea1e8b285a113e4e6ea1b02d9b29b9ad15df60f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ars=C3=A8ne=20P=C3=A9rard-Gayot?= Date: Mon, 15 Feb 2021 19:37:18 +0100 Subject: [PATCH 014/342] Remove file extensions --- src/thorin/be/backends.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/thorin/be/backends.h b/src/thorin/be/backends.h index d0093e9e0..0879b4426 100644 --- a/src/thorin/be/backends.h +++ b/src/thorin/be/backends.h @@ -45,8 +45,6 @@ struct Backends { enum { Cuda, NVVM, OpenCL, AMDGPU, HLS, BackendCount }; std::array, BackendCount> device_cgs; - - static constexpr const char* file_exts[] = { ".cu", ".nvvm", ".cl", ".amdgpu", ".hls" }; private: std::vector importers_; }; From eaf4555e83d17c78a5587f14f751853214f82b06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ars=C3=A8ne=20P=C3=A9rard-Gayot?= Date: Mon, 15 Feb 2021 20:05:46 +0100 Subject: [PATCH 015/342] Fix connection between intrinsic name and backend name --- src/thorin/be/backends.cpp | 21 ++++++++++++++------- src/thorin/be/backends.h | 2 +- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/src/thorin/be/backends.cpp b/src/thorin/be/backends.cpp index df31fbb36..b843e96cc 100644 --- a/src/thorin/be/backends.cpp +++ b/src/thorin/be/backends.cpp @@ -72,18 +72,25 @@ static uint64_t get_alloc_size(const Def* def) { } Backends::Backends(World& world, int opt, bool debug) - : device_cgs({}) + : device_cgs {} { - for (int backend = 0; backend < BackendCount; backend++) + for (size_t i = 0; i < device_cgs.size(); ++i) importers_.emplace_back(world); // determine different parts of the world which need to be compiled differently Scope::for_each(world, [&] (const Scope& scope) { auto continuation = scope.entry(); Continuation* imported = nullptr; - for (int backend = 0; backend <= BackendCount; backend++) { - if (backend == BackendCount) return; - if (is_passed_to_intrinsic(continuation, Intrinsic(int(Intrinsic::AcceleratorBegin) + backend))) { + + static const auto backend_intrinsics = std::array { + std::pair { CUDA, Intrinsic::CUDA }, + std::pair { NVVM, Intrinsic::NVVM }, + std::pair { OpenCL, Intrinsic::OpenCL }, + std::pair { AMDGPU, Intrinsic::AMDGPU }, + std::pair { HLS, Intrinsic::HLS } + }; + for (auto [backend, intrinsic] : backend_intrinsics) { + if (is_passed_to_intrinsic(continuation, intrinsic)) { imported = importers_[backend].import(continuation)->as_continuation(); break; } @@ -99,7 +106,7 @@ Backends::Backends(World& world, int opt, bool debug) kernels.emplace_back(continuation); }); - for (auto backend : std::array { Cuda, NVVM, OpenCL, AMDGPU }) { + for (auto backend : std::array { CUDA, NVVM, OpenCL, AMDGPU }) { if (!importers_[backend].world().empty()) { get_kernel_configs(importers_[backend], kernels, kernel_config, [&](Continuation *use, Continuation * /* imported */) { // determine whether or not this kernel uses restrict pointers @@ -171,7 +178,7 @@ Backends::Backends(World& world, int opt, bool debug) #else // TODO: maybe use the C backend as a fallback when LLVM is not present for host codegen ? #endif - for (auto [backend, lang] : std::array { std::pair { Cuda, c::Lang::CUDA }, std::pair { OpenCL, c::Lang::OPENCL }, std::pair { HLS, c::Lang::HLS } }) + for (auto [backend, lang] : std::array { std::pair { CUDA, c::Lang::CUDA }, std::pair { OpenCL, c::Lang::OPENCL }, std::pair { HLS, c::Lang::HLS } }) if (!importers_[backend].world().empty()) device_cgs[backend] = std::make_unique(importers_[backend].world(), kernel_config, lang, debug); } diff --git a/src/thorin/be/backends.h b/src/thorin/be/backends.h index 0879b4426..fa45d52f1 100644 --- a/src/thorin/be/backends.h +++ b/src/thorin/be/backends.h @@ -43,7 +43,7 @@ struct Backends { std::unique_ptr cpu_cg; - enum { Cuda, NVVM, OpenCL, AMDGPU, HLS, BackendCount }; + enum { CUDA, NVVM, OpenCL, AMDGPU, HLS, BackendCount }; std::array, BackendCount> device_cgs; private: std::vector importers_; From 70fef148f6d67866b8b35128829500f16300c064 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Tue, 16 Feb 2021 11:44:55 +0100 Subject: [PATCH 016/342] fix nullptr dereference --- src/thorin/be/backends.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/thorin/be/backends.cpp b/src/thorin/be/backends.cpp index b843e96cc..369b14880 100644 --- a/src/thorin/be/backends.cpp +++ b/src/thorin/be/backends.cpp @@ -96,6 +96,9 @@ Backends::Backends(World& world, int opt, bool debug) } } + if (imported == nullptr) + return; + imported->set_name(continuation->unique_name()); imported->make_exported(); continuation->set_name(continuation->unique_name()); From feb622fa6b35e2df0b8b377a6ff071df0b41d8d0 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Fri, 12 Feb 2021 11:50:31 +0100 Subject: [PATCH 017/342] initial stub --- CMakeLists.txt | 4 +++- src/thorin/CMakeLists.txt | 7 +++++++ src/thorin/be/spirv/spirv.cpp | 37 +++++++++++++++++++++++++++++++++++ src/thorin/be/spirv/spirv.h | 20 +++++++++++++++++++ 4 files changed, 67 insertions(+), 1 deletion(-) create mode 100644 src/thorin/be/spirv/spirv.cpp create mode 100644 src/thorin/be/spirv/spirv.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 4c7d8b0f7..3bcb33f56 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,7 +10,7 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON) option(BUILD_SHARED_LIBS "Build shared libraries" ON) option(THORIN_PROFILE "profile complexity in thorin::HashTable - only works in Debug build" ON) - +option(SPIRV_ENABLED "Enable spir-v backend" ON) if(CMAKE_BUILD_TYPE STREQUAL "") set(CMAKE_BUILD_TYPE Debug CACHE STRING "Debug or Release" FORCE) @@ -41,6 +41,8 @@ else() message(STATUS "Building without LLVM and RV. Specify LLVM_DIR to compile with LLVM.") endif() +find_package(SPIRV-Headers) + message(STATUS "Using Debug flags: ${CMAKE_CXX_FLAGS_DEBUG}") message(STATUS "Using Release flags: ${CMAKE_CXX_FLAGS_RELEASE}") if(DEFINED CMAKE_BUILD_TYPE) diff --git a/src/thorin/CMakeLists.txt b/src/thorin/CMakeLists.txt index 81bf603a8..c1c13a5ec 100644 --- a/src/thorin/CMakeLists.txt +++ b/src/thorin/CMakeLists.txt @@ -104,6 +104,13 @@ if(LLVM_FOUND) ) endif() +if(SPIRV_ENABLED) + list(APPEND THORIN_SOURCES + be/spirv/spirv.cpp + be/spirv/spirv.h + ) +endif() + add_library(thorin ${THORIN_SOURCES}) if(LLVM_FOUND) diff --git a/src/thorin/be/spirv/spirv.cpp b/src/thorin/be/spirv/spirv.cpp new file mode 100644 index 000000000..33940c27e --- /dev/null +++ b/src/thorin/be/spirv/spirv.cpp @@ -0,0 +1,37 @@ +#include "thorin/be/spirv/spirv.h" + +#include + +#include +#include + +struct SpvFileBuilder { + SpvFileBuilder(std::ostream& output) : output(output) {} + + std::ostream& output; + uint32_t bound = 0; + + void finish() { + output << spv::MagicNumber; + output << spv::Version; // TODO: target a specific spirv version + output << uint32_t(0); // TODO get a magic number ? + output << bound; + output << uint32_t(0); // instruction schema padding + } +}; + +struct SpvMethodBuilder { + +}; + +thorin::SpirVCodeGen::SpirVCodeGen(thorin::World& world) + : world_(world) +{} + +void thorin::SpirVCodeGen::emit() { + std::ofstream myfile; + myfile.open ("test.spv"); + auto builder = SpvFileBuilder(myfile); + builder.finish(); + myfile.close(); +} \ No newline at end of file diff --git a/src/thorin/be/spirv/spirv.h b/src/thorin/be/spirv/spirv.h new file mode 100644 index 000000000..419953f5c --- /dev/null +++ b/src/thorin/be/spirv/spirv.h @@ -0,0 +1,20 @@ +#ifndef THORIN_SPIRV_H +#define THORIN_SPIRV_H + +#include "thorin/continuation.h" + +namespace thorin { + +class SpirVCodeGen { +public: + SpirVCodeGen(World& world); + + void emit(); + +protected: + World& world_; +}; + +} + +#endif //THORIN_SPIRV_H From 56cdbfbc0b6808813e72b3e5d3c4ca6d8774be3e Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Fri, 12 Feb 2021 14:19:54 +0100 Subject: [PATCH 018/342] builds legal empty spir-v module --- src/thorin/be/spirv/spirv.cpp | 159 ++++++++++++++++++++++++++++++---- src/thorin/be/spirv/spirv.h | 3 +- 2 files changed, 146 insertions(+), 16 deletions(-) diff --git a/src/thorin/be/spirv/spirv.cpp b/src/thorin/be/spirv/spirv.cpp index 33940c27e..10f21c0a9 100644 --- a/src/thorin/be/spirv/spirv.cpp +++ b/src/thorin/be/spirv/spirv.cpp @@ -1,37 +1,166 @@ #include "thorin/be/spirv/spirv.h" +#include "thorin/analyses/scope.h" #include #include #include -struct SpvFileBuilder { - SpvFileBuilder(std::ostream& output) : output(output) {} + int div_roundup(int a, int b) { + if (a % b == 0) + return a / b; + else + return (a / b) + 1; +} - std::ostream& output; - uint32_t bound = 0; +namespace thorin { - void finish() { - output << spv::MagicNumber; - output << spv::Version; // TODO: target a specific spirv version - output << uint32_t(0); // TODO get a magic number ? - output << bound; - output << uint32_t(0); // instruction schema padding +struct SpvId { uint32_t id; }; + +struct SpvSectionBuilder { + std::vector data_; + +private: + void output_word(uint32_t word) { + data_.push_back(word); + } +public: + void op(spv::Op op, int ops_size) { + uint32_t lower = op & 0xFFFFu; + uint32_t upper = (ops_size << 16) & 0xFFFF0000u; + output_word(lower | upper); + } + + void ref_id(SpvId id) { + output_word(id.id); + } + + void literal_name(std::string_view str) { + int i = 0; + uint32_t cword = 0; + for (char c : str) { + cword = cword | (c & 0xFF) << (i * 8); + i++; + if (i == 4) { + output_word(cword); + cword = 0; + i = 0; + } + } + output_word(cword); } }; -struct SpvMethodBuilder { +struct SpvFileBuilder { + SpvFileBuilder(std::ostream& output) : output_(output) {} + + SpvId fresh_id() { return { bound++ }; } + + void name(SpvId id, std::string_view str) { + assert(id.id < bound); + debug_names.op(spv::Op::OpName, 2 + div_roundup(str.size() + 1, 4)); + debug_names.ref_id(id); + debug_names.literal_name(str); + } + + SpvId declare_bool_type() { + types_constants.op(spv::Op::OpTypeBool, 2); + auto id = fresh_id(); + types_constants.ref_id(id); + return id; + } + + void capability(spv::Capability cap) { + capabilities.op(spv::Op::OpCapability, 2); + capabilities.data_.push_back(cap); + } + + spv::AddressingModel addressing_model = spv::AddressingModel::AddressingModelLogical; + spv::MemoryModel memory_model = spv::MemoryModel::MemoryModelSimple; + +private: + std::ostream& output_; + uint32_t bound = 1; + + // Ordered as per https://www.khronos.org/registry/spir-v/specs/unified1/SPIRV.pdf#subsection.2.4 + SpvSectionBuilder capabilities; + SpvSectionBuilder extensions; + SpvSectionBuilder ext_inst_import; + SpvSectionBuilder entry_points; + SpvSectionBuilder execution_modes; + SpvSectionBuilder debug_string_source; + SpvSectionBuilder debug_names; + SpvSectionBuilder debug_module_processed; + SpvSectionBuilder annotations; + SpvSectionBuilder types_constants; + std::vector fn_decls; + std::vector fn_defs; + + void output_word_le(uint32_t word) { + output_.put((word >> 0) & 0xFFu); + output_.put((word >> 8) & 0xFFu); + output_.put((word >> 16) & 0xFFu); + output_.put((word >> 24) & 0xFFu); + } + + void output_section(SpvSectionBuilder& section) { + for (auto& word : section.data_) { + output_word_le(word); + } + } +public: + void finish() { + SpvSectionBuilder memory_model_section; + memory_model_section.op(spv::Op::OpMemoryModel, 3); + memory_model_section.data_.push_back(addressing_model); + memory_model_section.data_.push_back(memory_model); + + output_word_le(spv::MagicNumber); + output_word_le(spv::Version); // TODO: target a specific spirv version + output_word_le(uint32_t(0)); // TODO get a magic number ? + output_word_le(bound); + output_word_le(uint32_t(0)); // instruction schema padding + output_section(capabilities); + output_section(extensions); + output_section(ext_inst_import); + output_section(memory_model_section); + output_section(entry_points); + output_section(execution_modes); + output_section(debug_string_source); + output_section(debug_names); + output_section(debug_module_processed); + output_section(annotations); + output_section(types_constants); + for (auto& decl : fn_decls) + output_section(decl); + for (auto& def : fn_defs) + output_section(def); + } }; thorin::SpirVCodeGen::SpirVCodeGen(thorin::World& world) - : world_(world) -{} +: world_(world) {} void thorin::SpirVCodeGen::emit() { std::ofstream myfile; - myfile.open ("test.spv"); + myfile.open("test.spv"); auto builder = SpvFileBuilder(myfile); + + builder.capability(spv::Capability::CapabilityShader); + builder.capability(spv::Capability::CapabilityLinkage); + + builder.name(builder.declare_bool_type(), "test"); + + Scope::for_each(world_, [&](const Scope& scope) { emit(scope); }); + builder.finish(); + myfile.flush(); myfile.close(); -} \ No newline at end of file +} + +void thorin::SpirVCodeGen::emit(const thorin::Scope& scope) { + +} + +} diff --git a/src/thorin/be/spirv/spirv.h b/src/thorin/be/spirv/spirv.h index 419953f5c..ffebd6735 100644 --- a/src/thorin/be/spirv/spirv.h +++ b/src/thorin/be/spirv/spirv.h @@ -10,9 +10,10 @@ class SpirVCodeGen { SpirVCodeGen(World& world); void emit(); - protected: World& world_; + + void emit(const Scope& scope); }; } From 3a91cb71725a90939e95c04bac6539a1fb87249c Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Fri, 12 Feb 2021 14:44:30 +0100 Subject: [PATCH 019/342] report spirv configuration to cmake --- CMakeLists.txt | 5 ++++- cmake/thorin-config.cmake.in | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3bcb33f56..c1e89e2ec 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -41,7 +41,10 @@ else() message(STATUS "Building without LLVM and RV. Specify LLVM_DIR to compile with LLVM.") endif() -find_package(SPIRV-Headers) +if (SPIRV_ENABLED) + find_package(SPIRV-Headers REQUIRED) + message(STATUS "Enabled SPIR-V backend") +endif() message(STATUS "Using Debug flags: ${CMAKE_CXX_FLAGS_DEBUG}") message(STATUS "Using Release flags: ${CMAKE_CXX_FLAGS_RELEASE}") diff --git a/cmake/thorin-config.cmake.in b/cmake/thorin-config.cmake.in index fe7e3220c..f1dc27bd6 100644 --- a/cmake/thorin-config.cmake.in +++ b/cmake/thorin-config.cmake.in @@ -30,6 +30,7 @@ find_package(Half REQUIRED) set(Thorin_HAS_LLVM_SUPPORT @LLVM_FOUND@) set(Thorin_HAS_RV_SUPPORT @RV_FOUND@) +set(Thorin_HAS_SPIRV_SUPPORT @SPIRV_ENABLED@) set(AnyDSL_LLVM_LINK_SHARED @AnyDSL_LLVM_LINK_SHARED@) if(Thorin_HAS_LLVM_SUPPORT) From e2008654f0a44beb2f2363522400bf3fce001db0 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Fri, 12 Feb 2021 15:28:55 +0100 Subject: [PATCH 020/342] add vk_compute intrinsic --- src/thorin/continuation.cpp | 1 + src/thorin/continuation.h | 1 + 2 files changed, 2 insertions(+) diff --git a/src/thorin/continuation.cpp b/src/thorin/continuation.cpp index 81e26b025..0797dbe5e 100644 --- a/src/thorin/continuation.cpp +++ b/src/thorin/continuation.cpp @@ -167,6 +167,7 @@ void Continuation::set_intrinsic() { else if (name() == "nvvm") attributes().intrinsic = Intrinsic::NVVM; else if (name() == "opencl") attributes().intrinsic = Intrinsic::OpenCL; else if (name() == "amdgpu") attributes().intrinsic = Intrinsic::AMDGPU; + else if (name() == "vk_compute") attributes().intrinsic = Intrinsic::VulkanCompute; else if (name() == "hls") attributes().intrinsic = Intrinsic::HLS; else if (name() == "parallel") attributes().intrinsic = Intrinsic::Parallel; else if (name() == "fibers") attributes().intrinsic = Intrinsic::Fibers; diff --git a/src/thorin/continuation.h b/src/thorin/continuation.h index 978c39f15..8b3085319 100644 --- a/src/thorin/continuation.h +++ b/src/thorin/continuation.h @@ -62,6 +62,7 @@ enum class Intrinsic : uint8_t { NVVM, ///< Internal NNVM-Backend. OpenCL, ///< Internal OpenCL-Backend. AMDGPU, ///< Internal AMDGPU-Backend. + VulkanCompute, ///< Internal Vulkan-Compute-Shader-Backend. HLS, ///< Internal HLS-Backend. Parallel, ///< Internal Parallel-CPU-Backend. Fibers, ///< Internal Parallel-CPU-Backend using resumable fibers. From 24cc66c23ed5577cbd8dfbc3ff06c4aa656a47f4 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Tue, 16 Feb 2021 09:56:52 +0100 Subject: [PATCH 021/342] adapt to backends refactor --- CMakeLists.txt | 3 +++ src/thorin/be/backends.cpp | 17 ++++++++++++----- src/thorin/be/backends.h | 2 +- src/thorin/be/spirv/spirv.cpp | 22 +++++++++------------- src/thorin/be/spirv/spirv.h | 12 +++++------- src/thorin/config.h.in | 1 + src/thorin/continuation.cpp | 2 +- src/thorin/continuation.h | 2 +- 8 files changed, 33 insertions(+), 28 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c1e89e2ec..58177d42c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -65,6 +65,9 @@ endif() if(RV_FOUND) set(THORIN_ENABLE_RV TRUE) endif() +if(LLVM_FOUND) + set(THORIN_ENABLE_SPIRV TRUE) +endif() configure_file(src/thorin/config.h.in ${CMAKE_BINARY_DIR}/include/thorin/config.h @ONLY) include_directories(${CMAKE_BINARY_DIR}/include) diff --git a/src/thorin/be/backends.cpp b/src/thorin/be/backends.cpp index 369b14880..2808c74e3 100644 --- a/src/thorin/be/backends.cpp +++ b/src/thorin/be/backends.cpp @@ -7,6 +7,9 @@ #include "thorin/be/llvm/nvvm.h" #include "thorin/be/llvm/amdgpu.h" #endif +#if THORIN_ENABLE_SPIRV +#include "thorin/be/spirv/spirv.h" +#endif #include "thorin/be/c/c.h" namespace thorin { @@ -83,11 +86,12 @@ Backends::Backends(World& world, int opt, bool debug) Continuation* imported = nullptr; static const auto backend_intrinsics = std::array { - std::pair { CUDA, Intrinsic::CUDA }, - std::pair { NVVM, Intrinsic::NVVM }, - std::pair { OpenCL, Intrinsic::OpenCL }, - std::pair { AMDGPU, Intrinsic::AMDGPU }, - std::pair { HLS, Intrinsic::HLS } + std::pair { CUDA, Intrinsic::CUDA }, + std::pair { NVVM, Intrinsic::NVVM }, + std::pair { OpenCL, Intrinsic::OpenCL }, + std::pair { AMDGPU, Intrinsic::AMDGPU }, + std::pair { HLS, Intrinsic::HLS }, + std::pair { VkCompute, Intrinsic::VkCompute } }; for (auto [backend, intrinsic] : backend_intrinsics) { if (is_passed_to_intrinsic(continuation, intrinsic)) { @@ -180,6 +184,9 @@ Backends::Backends(World& world, int opt, bool debug) if (!importers_[AMDGPU].world().empty()) device_cgs[AMDGPU] = std::make_unique(importers_[AMDGPU].world(), kernel_config, opt, debug); #else // TODO: maybe use the C backend as a fallback when LLVM is not present for host codegen ? +#endif +#if THORIN_ENABLE_SPIRV + if (!importers_[VkCompute].world().empty()) device_cgs[VkCompute] = std::make_unique(importers_[VkCompute].world(), kernel_config, debug); #endif for (auto [backend, lang] : std::array { std::pair { CUDA, c::Lang::CUDA }, std::pair { OpenCL, c::Lang::OPENCL }, std::pair { HLS, c::Lang::HLS } }) if (!importers_[backend].world().empty()) device_cgs[backend] = std::make_unique(importers_[backend].world(), kernel_config, lang, debug); diff --git a/src/thorin/be/backends.h b/src/thorin/be/backends.h index fa45d52f1..2d366b23a 100644 --- a/src/thorin/be/backends.h +++ b/src/thorin/be/backends.h @@ -43,7 +43,7 @@ struct Backends { std::unique_ptr cpu_cg; - enum { CUDA, NVVM, OpenCL, AMDGPU, HLS, BackendCount }; + enum { CUDA, NVVM, OpenCL, AMDGPU, HLS, VkCompute, BackendCount }; std::array, BackendCount> device_cgs; private: std::vector importers_; diff --git a/src/thorin/be/spirv/spirv.cpp b/src/thorin/be/spirv/spirv.cpp index 10f21c0a9..34c1bed97 100644 --- a/src/thorin/be/spirv/spirv.cpp +++ b/src/thorin/be/spirv/spirv.cpp @@ -4,16 +4,15 @@ #include #include -#include - int div_roundup(int a, int b) { +int div_roundup(int a, int b) { if (a % b == 0) return a / b; else return (a / b) + 1; } -namespace thorin { +namespace thorin::spirv { struct SpvId { uint32_t id; }; @@ -139,27 +138,24 @@ struct SpvFileBuilder { } }; -thorin::SpirVCodeGen::SpirVCodeGen(thorin::World& world) -: world_(world) {} +CodeGen::CodeGen(thorin::World& world, Cont2Config& kernel_config, bool debug) + : thorin::CodeGen(world, debug) +{} -void thorin::SpirVCodeGen::emit() { - std::ofstream myfile; - myfile.open("test.spv"); - auto builder = SpvFileBuilder(myfile); +void CodeGen::emit(std::ostream& out) { + auto builder = SpvFileBuilder(out); builder.capability(spv::Capability::CapabilityShader); builder.capability(spv::Capability::CapabilityLinkage); builder.name(builder.declare_bool_type(), "test"); - Scope::for_each(world_, [&](const Scope& scope) { emit(scope); }); + Scope::for_each(world(), [&](const Scope& scope) { emit(scope); }); builder.finish(); - myfile.flush(); - myfile.close(); } -void thorin::SpirVCodeGen::emit(const thorin::Scope& scope) { +void CodeGen::emit(const thorin::Scope& scope) { } diff --git a/src/thorin/be/spirv/spirv.h b/src/thorin/be/spirv/spirv.h index ffebd6735..e7b309f01 100644 --- a/src/thorin/be/spirv/spirv.h +++ b/src/thorin/be/spirv/spirv.h @@ -1,18 +1,16 @@ #ifndef THORIN_SPIRV_H #define THORIN_SPIRV_H -#include "thorin/continuation.h" +#include "thorin/be/backends.h" -namespace thorin { +namespace thorin::spirv { -class SpirVCodeGen { +class CodeGen : public thorin::CodeGen { public: - SpirVCodeGen(World& world); + CodeGen(World&, Cont2Config&, bool debug); - void emit(); + void emit(std::ostream& stream) override; protected: - World& world_; - void emit(const Scope& scope); }; diff --git a/src/thorin/config.h.in b/src/thorin/config.h.in index 5e061fe28..c594de907 100644 --- a/src/thorin/config.h.in +++ b/src/thorin/config.h.in @@ -5,5 +5,6 @@ #cmakedefine01 THORIN_ENABLE_PROFILING #cmakedefine01 THORIN_ENABLE_LLVM #cmakedefine01 THORIN_ENABLE_RV +#cmakedefine01 THORIN_ENABLE_SPIRV #endif diff --git a/src/thorin/continuation.cpp b/src/thorin/continuation.cpp index 0797dbe5e..b354e8647 100644 --- a/src/thorin/continuation.cpp +++ b/src/thorin/continuation.cpp @@ -167,7 +167,7 @@ void Continuation::set_intrinsic() { else if (name() == "nvvm") attributes().intrinsic = Intrinsic::NVVM; else if (name() == "opencl") attributes().intrinsic = Intrinsic::OpenCL; else if (name() == "amdgpu") attributes().intrinsic = Intrinsic::AMDGPU; - else if (name() == "vk_compute") attributes().intrinsic = Intrinsic::VulkanCompute; + else if (name() == "vk_compute") attributes().intrinsic = Intrinsic::VkCompute; else if (name() == "hls") attributes().intrinsic = Intrinsic::HLS; else if (name() == "parallel") attributes().intrinsic = Intrinsic::Parallel; else if (name() == "fibers") attributes().intrinsic = Intrinsic::Fibers; diff --git a/src/thorin/continuation.h b/src/thorin/continuation.h index 8b3085319..3f6697d1b 100644 --- a/src/thorin/continuation.h +++ b/src/thorin/continuation.h @@ -62,7 +62,7 @@ enum class Intrinsic : uint8_t { NVVM, ///< Internal NNVM-Backend. OpenCL, ///< Internal OpenCL-Backend. AMDGPU, ///< Internal AMDGPU-Backend. - VulkanCompute, ///< Internal Vulkan-Compute-Shader-Backend. + VkCompute, ///< Internal Vulkan-Compute-Shader-Backend. HLS, ///< Internal HLS-Backend. Parallel, ///< Internal Parallel-CPU-Backend. Fibers, ///< Internal Parallel-CPU-Backend using resumable fibers. From 4a0db929f3e9aa9e6dfb75d1770aea855f6e77f0 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Wed, 17 Feb 2021 12:48:49 +0100 Subject: [PATCH 022/342] working stub for function definition --- src/thorin/be/llvm/llvm.cpp | 1 + src/thorin/be/spirv/spirv.cpp | 468 ++++++++++++++++++++++++++++++++-- src/thorin/be/spirv/spirv.h | 15 ++ 3 files changed, 461 insertions(+), 23 deletions(-) diff --git a/src/thorin/be/llvm/llvm.cpp b/src/thorin/be/llvm/llvm.cpp index 5a2c867ce..e11e5d43a 100644 --- a/src/thorin/be/llvm/llvm.cpp +++ b/src/thorin/be/llvm/llvm.cpp @@ -1115,6 +1115,7 @@ Continuation* CodeGen::emit_intrinsic(llvm::IRBuilder<>& irbuilder, Continuation case Intrinsic::NVVM: return runtime_->emit_host_code(*this, irbuilder, Runtime::CUDA_PLATFORM, ".nvvm", continuation); case Intrinsic::OpenCL: return runtime_->emit_host_code(*this, irbuilder, Runtime::OPENCL_PLATFORM, ".cl", continuation); case Intrinsic::AMDGPU: return runtime_->emit_host_code(*this, irbuilder, Runtime::HSA_PLATFORM, ".amdgpu", continuation); + case Intrinsic::VkCompute: return runtime_->emit_host_code(*this, irbuilder, Runtime::OPENCL_PLATFORM, ".spv", continuation); // TODO have a real runtime component case Intrinsic::HLS: return emit_hls(irbuilder, continuation); case Intrinsic::Parallel: return emit_parallel(irbuilder, continuation); case Intrinsic::Fibers: return emit_fibers(irbuilder, continuation); diff --git a/src/thorin/be/spirv/spirv.cpp b/src/thorin/be/spirv/spirv.cpp index 34c1bed97..1e5bf883a 100644 --- a/src/thorin/be/spirv/spirv.cpp +++ b/src/thorin/be/spirv/spirv.cpp @@ -4,6 +4,7 @@ #include #include +#include int div_roundup(int a, int b) { if (a % b == 0) @@ -14,8 +15,6 @@ int div_roundup(int a, int b) { namespace thorin::spirv { -struct SpvId { uint32_t id; }; - struct SpvSectionBuilder { std::vector data_; @@ -31,6 +30,7 @@ struct SpvSectionBuilder { } void ref_id(SpvId id) { + assert(id.id != 0); output_word(id.id); } @@ -48,12 +48,46 @@ struct SpvSectionBuilder { } output_word(cword); } + + void literal_int(uint32_t i) { + output_word(i); + } +}; + +struct SpvBasicBlockBuilder : public SpvSectionBuilder { + explicit SpvBasicBlockBuilder(SpvFileBuilder& file_builder) + : file_builder(file_builder) + {} + + SpvFileBuilder& file_builder; + + SpvId label() { + op(spv::Op::OpLabel, 2); + auto id = generate_fresh_id(); + ref_id(id); + return id; + } + + void return_void() { + op(spv::Op::OpReturn, 1); + } +private: + SpvId generate_fresh_id(); +}; + +struct SpvFnBuilder { + SpvId fn_type; + SpvId fn_ret_type; + ContinuationMap> bbs; + ContinuationMap labels; }; struct SpvFileBuilder { - SpvFileBuilder(std::ostream& output) : output_(output) {} + SpvFileBuilder() + : void_type(declare_void_type()) + {} - SpvId fresh_id() { return { bound++ }; } + SpvId generate_fresh_id() { return {bound++ }; } void name(SpvId id, std::string_view str) { assert(id.id < bound); @@ -64,11 +98,56 @@ struct SpvFileBuilder { SpvId declare_bool_type() { types_constants.op(spv::Op::OpTypeBool, 2); - auto id = fresh_id(); + auto id = generate_fresh_id(); types_constants.ref_id(id); return id; } + SpvId declare_int_type(int width, bool signed_) { + types_constants.op(spv::Op::OpTypeInt, 4); + auto id = generate_fresh_id(); + types_constants.ref_id(id); + types_constants.literal_int(width); + types_constants.literal_int(signed_ ? 1 : 0); + return id; + } + + SpvId declare_float_type(int width) { + types_constants.op(spv::Op::OpTypeFloat, 3); + auto id = generate_fresh_id(); + types_constants.ref_id(id); + types_constants.literal_int(width); + return id; + } + + SpvId declare_fn_type(std::vector& dom, SpvId codom) { + types_constants.op(spv::Op::OpTypeFunction, 3 + dom.size()); + auto id = generate_fresh_id(); + types_constants.ref_id(id); + types_constants.ref_id(codom); + for (auto arg : dom) + types_constants.ref_id(arg); + return id; + } + + SpvId define_function(SpvFnBuilder& fn_builder) { + fn_defs.op(spv::Op::OpFunction, 5); + fn_defs.ref_id(fn_builder.fn_ret_type); + auto id = generate_fresh_id(); + fn_defs.ref_id(id); + fn_defs.data_.push_back(spv::FunctionControlMaskNone); + fn_defs.ref_id(fn_builder.fn_type); + + // TODO OpFunctionParameters + for (auto& [cont, bb] : fn_builder.bbs) { + for (auto w : bb->data_) + fn_defs.data_.push_back(w); + } + + fn_defs.op(spv::Op::OpFunctionEnd, 1); + return id; + } + void capability(spv::Capability cap) { capabilities.op(spv::Op::OpCapability, 2); capabilities.data_.push_back(cap); @@ -78,7 +157,7 @@ struct SpvFileBuilder { spv::MemoryModel memory_model = spv::MemoryModel::MemoryModelSimple; private: - std::ostream& output_; + std::ostream* output_ = nullptr; uint32_t bound = 1; // Ordered as per https://www.khronos.org/registry/spir-v/specs/unified1/SPIRV.pdf#subsection.2.4 @@ -92,14 +171,21 @@ struct SpvFileBuilder { SpvSectionBuilder debug_module_processed; SpvSectionBuilder annotations; SpvSectionBuilder types_constants; - std::vector fn_decls; - std::vector fn_defs; + SpvSectionBuilder fn_decls; + SpvSectionBuilder fn_defs; + + SpvId declare_void_type() { + types_constants.op(spv::Op::OpTypeVoid, 2); + auto id = generate_fresh_id(); + types_constants.ref_id(id); + return id; + } void output_word_le(uint32_t word) { - output_.put((word >> 0) & 0xFFu); - output_.put((word >> 8) & 0xFFu); - output_.put((word >> 16) & 0xFFu); - output_.put((word >> 24) & 0xFFu); + output_->put((word >> 0) & 0xFFu); + output_->put((word >> 8) & 0xFFu); + output_->put((word >> 16) & 0xFFu); + output_->put((word >> 24) & 0xFFu); } void output_section(SpvSectionBuilder& section) { @@ -108,7 +194,10 @@ struct SpvFileBuilder { } } public: - void finish() { + const SpvId void_type; + + void finish(std::ostream& output) { + output_ = &output; SpvSectionBuilder memory_model_section; memory_model_section.op(spv::Op::OpMemoryModel, 3); memory_model_section.data_.push_back(addressing_model); @@ -131,32 +220,365 @@ struct SpvFileBuilder { output_section(debug_module_processed); output_section(annotations); output_section(types_constants); - for (auto& decl : fn_decls) - output_section(decl); - for (auto& def : fn_defs) - output_section(def); + output_section(fn_decls); + output_section(fn_defs); } }; +SpvId SpvBasicBlockBuilder::generate_fresh_id() { + return file_builder.generate_fresh_id(); +} + CodeGen::CodeGen(thorin::World& world, Cont2Config& kernel_config, bool debug) : thorin::CodeGen(world, debug) {} void CodeGen::emit(std::ostream& out) { - auto builder = SpvFileBuilder(out); + SpvFileBuilder builder; + builder_ = &builder; + builder_->capability(spv::Capability::CapabilityShader); + builder_->capability(spv::Capability::CapabilityLinkage); + + Scope::for_each(world(), [&](const Scope& scope) { emit(scope); }); - builder.capability(spv::Capability::CapabilityShader); - builder.capability(spv::Capability::CapabilityLinkage); + builder_->finish(out); + builder_ = nullptr; +} - builder.name(builder.declare_bool_type(), "test"); +SpvId CodeGen::convert(const Type* type) { + if (auto llvm_type = types_.lookup(type)) return *llvm_type; - Scope::for_each(world(), [&](const Scope& scope) { emit(scope); }); + assert(!type->isa()); + SpvId spv_type; + switch (type->tag()) { + case PrimType_bool: spv_type = builder_->declare_bool_type(); break; + case PrimType_ps8: case PrimType_qs8: case PrimType_pu8: case PrimType_qu8: assert(false && "TODO: look into capabilities to enable this"); + case PrimType_ps16: case PrimType_qs16: case PrimType_pu16: case PrimType_qu16: assert(false && "TODO: look into capabilities to enable this"); + case PrimType_ps32: case PrimType_qs32: spv_type = builder_->declare_int_type(32, true ); break; + case PrimType_pu32: case PrimType_qu32: spv_type = builder_->declare_int_type(32, false); break; + case PrimType_ps64: case PrimType_qs64: case PrimType_pu64: case PrimType_qu64: assert(false && "TODO: look into capabilities to enable this"); + case PrimType_pf16: case PrimType_qf16: assert(false && "TODO: look into capabilities to enable this"); + case PrimType_pf32: case PrimType_qf32: spv_type = builder_->declare_float_type(32); break; + case PrimType_pf64: case PrimType_qf64: assert(false && "TODO: look into capabilities to enable this"); + case Node_PtrType: { + auto ptr = type->as(); + assert(false && "TODO"); + //llvm_type = llvm::PointerType::get(convert(ptr->pointee()), convert_addr_space(ptr->addr_space())); + break; + } + case Node_IndefiniteArrayType: { + assert(false && "TODO"); + //llvm_type = llvm::ArrayType::get(convert(type->as()->elem_type()), 0); + //return types_[type] = llvm_type; + } + case Node_DefiniteArrayType: { + assert(false && "TODO"); + auto array = type->as(); + //llvm_type = llvm::ArrayType::get(convert(array->elem_type()), array->dim()); + //return types_[type] = llvm_type; + } + + case Node_ClosureType: + case Node_FnType: { + // extract "return" type, collect all other types + auto fn = type->as(); + std::unique_ptr ret; + std::vector ops; + for (auto op : fn->ops()) { + if (op->isa() || op == world().unit()) continue; + auto fn = op->isa(); + if (fn && !op->isa()) { + assert(!ret && "only one 'return' supported"); + std::vector ret_types; + for (auto fn_op : fn->ops()) { + if (fn_op->isa() || fn_op == world().unit()) continue; + ret_types.push_back(convert(fn_op)); + } + if (ret_types.size() == 0) ret = std::make_unique(builder_->void_type); + else if (ret_types.size() == 1) ret = std::make_unique(ret_types.back()); + else assert(false && "Didn't we refactor this out yet by making functions single-argument ?"); //ret = llvm::StructType::get(context(), ret_types); + } else + ops.push_back(convert(op)); + } + assert(ret); + + if (type->tag() == Node_FnType) { + return types_[type] = builder_->declare_fn_type(ops, *ret); + } + + assert(false && "TODO: handle closure mess"); + /* auto env_type = convert(Closure::environment_type(world())); + ops.push_back(env_type); + auto fn_type = llvm::FunctionType::get(ret, ops, false); + auto ptr_type = llvm::PointerType::get(fn_type, 0); + llvm_type = llvm::StructType::get(context(), { ptr_type, env_type }); + return types_[type] = llvm_type;*/ + } + + case Node_StructType: { + assert(false && "TODO"); + /*auto struct_type = type->as(); + auto llvm_struct = llvm::StructType::create(context()); - builder.finish(); + // important: memoize before recursing into element types to avoid endless recursion + assert(!types_.contains(struct_type) && "type already converted"); + types_[struct_type] = llvm_struct; + + Array llvm_types(struct_type->num_ops()); + for (size_t i = 0, e = llvm_types.size(); i != e; ++i) + llvm_types[i] = convert(struct_type->op(i)); + llvm_struct->setBody(llvm_ref(llvm_types)); + return llvm_struct;*/ + } + + case Node_TupleType: { + assert(false && "TODO"); + /*auto tuple = type->as(); + Array llvm_types(tuple->num_ops()); + for (size_t i = 0, e = llvm_types.size(); i != e; ++i) + llvm_types[i] = convert(tuple->op(i)); + llvm_type = llvm::StructType::get(context(), llvm_ref(llvm_types)); + return types_[tuple] = llvm_type;*/ + } + + case Node_VariantType: { + assert(false && "TODO"); + /*assert(type->num_ops() > 0); + + // Max alignment/size constraints respectively in the variant type alternatives dictate the ones to use for the overall type + size_t max_align = 0, max_size = 0; + + auto layout = module().getDataLayout(); + llvm::Type* max_align_type; + for (auto op : type->ops()) { + auto op_type = convert(op); + size_t size = layout.getTypeAllocSize(op_type); + size_t align = layout.getABITypeAlignment(op_type); + // Favor types that are not empty + if (align > max_align || (align == max_align && max_align_type->isEmptyTy())) { + max_align_type = op_type; + max_align = align; + } + max_size = std::max(max_size, size); + } + + auto rem_size = max_size - layout.getTypeAllocSize(max_align_type); + auto union_type = rem_size > 0 + ? llvm::StructType::get(context(), llvm::ArrayRef { max_align_type, llvm::ArrayType::get(llvm::Type::getInt8Ty(context()), rem_size)}) + : llvm::StructType::get(context(), llvm::ArrayRef { max_align_type }); + + auto tag_type = type->num_ops() < (1_u64 << 8) ? llvm::Type::getInt8Ty (context()) : + type->num_ops() < (1_u64 << 16) ? llvm::Type::getInt16Ty(context()) : + type->num_ops() < (1_u64 << 32) ? llvm::Type::getInt32Ty(context()) : + llvm::Type::getInt64Ty(context()); + + return llvm::StructType::get(context(), { union_type, tag_type });*/ + } + + default: + THORIN_UNREACHABLE; + } + + return types_[type] = spv_type; } void CodeGen::emit(const thorin::Scope& scope) { + entry_ = scope.entry(); + assert(entry_->is_returning()); + //auto fct = llvm::cast(emit(entry_)); + auto fn = SpvFnBuilder { }; + fn.fn_type = convert(entry_->type()); + fn.fn_ret_type = builder_->void_type; // TODO !!! + + current_fn_ = &fn; + + //cont2llvm_.clear(); + auto conts = schedule(scope); + + // map all bb-like continuations to llvm bb stubs and handle params/phis + for (auto cont : conts) { + if (cont->intrinsic() == Intrinsic::EndScope) continue; + + auto [i, b] = fn.bbs.emplace(cont, std::make_unique(*builder_)); + SpvBasicBlockBuilder& bb = *i->second; + SpvId label = bb.label(); + builder_->name(label, cont->name().c_str()); + fn.labels.emplace(cont, label); + //auto bb = llvm::BasicBlock::Create(context(), cont->name().c_str(), fct); + //auto [i, succ] = cont2llvm_.emplace(cont, std::pair(bb, std::make_unique>(context()))); + assert(b); + // auto& irbuilder = *i->second.second; + // irbuilder.SetInsertPoint(bb); + + //if (debug()) + // irbuilder.SetCurrentDebugLocation(llvm::DebugLoc::get(cont->loc().begin.row, cont->loc().begin.row, discope)); + + /*if (entry_ == cont) { + auto arg = fct->arg_begin(); + for (auto param : entry_->params()) { + if (is_mem(param) || is_unit(param)) { + def2llvm_[param] = nullptr; + } else if (param->order() == 0) { + auto argv = &*arg; + auto value = map_param(fct, argv, param); + if (value == argv) { + arg->setName(param->unique_name()); // use param + def2llvm_[param] = &*arg++; + } else { + def2llvm_[param] = value; // use provided value + } + } + } + } else { + for (auto param : cont->params()) { + if (is_mem(param) || is_unit(param)) { + def2llvm_[param] = nullptr; + } else { + // do not bother reserving anything (the 0 below) - it's a tiny optimization nobody cares about + auto phi = irbuilder.CreatePHI(convert(param->type()), 0, param->name().c_str()); + def2llvm_[param] = phi; + } + } + }*/ + } + + Scheduler new_scheduler(scope); + swap(scheduler_, new_scheduler); + + for (auto cont : conts) { + if (cont->intrinsic() == Intrinsic::EndScope) continue; + assert(cont == entry_ || cont->is_basicblock()); + emit_epilogue(cont, *fn.bbs[cont]->get()); + } + + builder_->define_function(fn); +} + +void CodeGen::emit_epilogue(Continuation* continuation, SpvBasicBlockBuilder& bb) { + /*auto&& bb_ib = cont2llvm_[continuation]; + auto bb = bb_ib->first; + auto& irbuilder = *bb_ib->second;*/ + + if (continuation->callee() == entry_->ret_param()) { // return + std::vector values; + std::vector types; + + for (auto arg : continuation->args()) { + /*if (auto val = emit_unsafe(arg)) { + values.emplace_back(val); + types.emplace_back(val->getType()); + }*/ + } + + switch (values.size()) { + case 0: bb.return_void(); break; + //case 1: irbuilder.CreateRet(values[0]); break; + default: + assert(false && "TODO handle non-void returns"); + /*llvm::Value* agg = llvm::UndefValue::get(llvm::StructType::get(context(), types)); + + for (size_t i = 0, e = values.size(); i != e; ++i) + agg = irbuilder.CreateInsertValue(agg, values[i], { unsigned(i) }); + + irbuilder.CreateRet(agg);*/ + } + } /*else if (continuation->callee() == world().branch()) { + auto cond = emit(continuation->arg(0)); + auto tbb = cont2bb(continuation->arg(1)->as_continuation()); + auto fbb = cont2bb(continuation->arg(2)->as_continuation()); + irbuilder.CreateCondBr(cond, tbb, fbb); + } else if (continuation->callee()->isa() && + continuation->callee()->as()->intrinsic() == Intrinsic::Match) { + auto val = emit(continuation->arg(0)); + auto otherwise_bb = cont2bb(continuation->arg(1)->as_continuation()); + auto match = irbuilder.CreateSwitch(val, otherwise_bb, continuation->num_args() - 2); + for (size_t i = 2; i < continuation->num_args(); i++) { + auto arg = continuation->arg(i)->as(); + auto case_const = llvm::cast(emit(arg->op(0))); + auto case_bb = cont2bb(arg->op(1)->as_continuation()); + match->addCase(case_const, case_bb); + } + } else if (continuation->callee()->isa()) { + irbuilder.CreateUnreachable(); + } else if (auto callee = continuation->callee()->isa_continuation(); callee && callee->is_basicblock()) { // ordinary jump + for (size_t i = 0, e = continuation->num_args(); i != e; ++i) { + if (auto val = emit_unsafe(continuation->arg(i))) emit_phi_arg(irbuilder, callee->param(i), val); + } + irbuilder.CreateBr(cont2bb(callee)); + } else if (auto callee = continuation->callee()->isa_continuation(); callee && callee->is_intrinsic()) { + auto ret_continuation = emit_intrinsic(irbuilder, continuation); + irbuilder.CreateBr(cont2bb(ret_continuation)); + } else { // function/closure call + // put all first-order args into an array + std::vector args; + const Def* ret_arg = nullptr; + for (auto arg : continuation->args()) { + if (arg->order() == 0) { + if (auto val = emit_unsafe(arg)) + args.push_back(val); + } else { + assert(!ret_arg); + ret_arg = arg; + } + } + + llvm::CallInst* call = nullptr; + if (auto callee = continuation->callee()->isa_continuation()) { + call = irbuilder.CreateCall(emit(callee), args); + if (callee->is_exported()) + call->setCallingConv(kernel_calling_convention_); + else if (callee->cc() == CC::Device) + call->setCallingConv(device_calling_convention_); + else + call->setCallingConv(function_calling_convention_); + } else { + // must be a closure + auto closure = emit(callee); + args.push_back(irbuilder.CreateExtractValue(closure, 1)); + call = irbuilder.CreateCall(irbuilder.CreateExtractValue(closure, 0), args); + } + + // must be call + continuation --- call + return has been removed by codegen_prepare + auto succ = ret_arg->as_continuation(); + + size_t n = 0; + const Param* last_param = nullptr; + for (auto param : succ->params()) { + if (is_mem(param) || is_unit(param)) + continue; + last_param = param; + n++; + } + + if (n == 0) { + irbuilder.CreateBr(cont2bb(succ)); + } else if (n == 1) { + irbuilder.CreateBr(cont2bb(succ)); + emit_phi_arg(irbuilder, last_param, call); + } else { + Array extracts(n); + for (size_t i = 0, j = 0; i != succ->num_params(); ++i) { + auto param = succ->param(i); + if (is_mem(param) || is_unit(param)) + continue; + extracts[j] = irbuilder.CreateExtractValue(call, unsigned(j)); + j++; + } + + irbuilder.CreateBr(cont2bb(succ)); + + for (size_t i = 0, j = 0; i != succ->num_params(); ++i) { + auto param = succ->param(i); + if (is_mem(param) || is_unit(param)) + continue; + emit_phi_arg(irbuilder, param, extracts[j]); + j++; + } + } + }*/ + // new insert point is just before the terminator for all other instructions we have to add later on + // irbuilder.SetInsertPoint(bb->getTerminator()); } } diff --git a/src/thorin/be/spirv/spirv.h b/src/thorin/be/spirv/spirv.h index e7b309f01..903921be4 100644 --- a/src/thorin/be/spirv/spirv.h +++ b/src/thorin/be/spirv/spirv.h @@ -1,17 +1,32 @@ #ifndef THORIN_SPIRV_H #define THORIN_SPIRV_H +#include #include "thorin/be/backends.h" namespace thorin::spirv { +struct SpvSectionBuilder; +struct SpvBasicBlockBuilder; +struct SpvFnBuilder; +struct SpvFileBuilder; +struct SpvId { uint32_t id; }; + class CodeGen : public thorin::CodeGen { public: CodeGen(World&, Cont2Config&, bool debug); void emit(std::ostream& stream) override; protected: + SpvId convert(const Type*); void emit(const Scope& scope); + void emit_epilogue(Continuation*, SpvBasicBlockBuilder& bb); + + SpvFileBuilder* builder_ = nullptr; + Continuation* entry_ = nullptr; + SpvFnBuilder* current_fn_ = nullptr; + Scheduler scheduler_; + TypeMap types_; }; } From e5021342a5ce4e44025fb2fb821639dbcb250299 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Wed, 17 Feb 2021 14:36:16 +0100 Subject: [PATCH 023/342] remove hack for function codom type --- src/thorin/be/spirv/spirv.cpp | 149 ++++++++++------------------------ src/thorin/be/spirv/spirv.h | 2 + 2 files changed, 43 insertions(+), 108 deletions(-) diff --git a/src/thorin/be/spirv/spirv.cpp b/src/thorin/be/spirv/spirv.cpp index 1e5bf883a..b08e71ebb 100644 --- a/src/thorin/be/spirv/spirv.cpp +++ b/src/thorin/be/spirv/spirv.cpp @@ -130,6 +130,15 @@ struct SpvFileBuilder { return id; } + SpvId declare_struct_type(std::vector& elements) { + types_constants.op(spv::Op::OpTypeStruct, 2 + elements.size()); + auto id = generate_fresh_id(); + types_constants.ref_id(id); + for (auto arg : elements) + types_constants.ref_id(arg); + return id; + } + SpvId define_function(SpvFnBuilder& fn_builder) { fn_defs.op(spv::Op::OpFunction, 5); fn_defs.ref_id(fn_builder.fn_ret_type); @@ -246,7 +255,7 @@ void CodeGen::emit(std::ostream& out) { } SpvId CodeGen::convert(const Type* type) { - if (auto llvm_type = types_.lookup(type)) return *llvm_type; + if (auto spv_type = types_.lookup(type)) return *spv_type; assert(!type->isa()); SpvId spv_type; @@ -263,19 +272,17 @@ SpvId CodeGen::convert(const Type* type) { case Node_PtrType: { auto ptr = type->as(); assert(false && "TODO"); - //llvm_type = llvm::PointerType::get(convert(ptr->pointee()), convert_addr_space(ptr->addr_space())); break; } case Node_IndefiniteArrayType: { assert(false && "TODO"); - //llvm_type = llvm::ArrayType::get(convert(type->as()->elem_type()), 0); - //return types_[type] = llvm_type; + auto array = type->as(); + //return types_[type] = spv_type; } case Node_DefiniteArrayType: { assert(false && "TODO"); auto array = type->as(); - //llvm_type = llvm::ArrayType::get(convert(array->elem_type()), array->dim()); - //return types_[type] = llvm_type; + //return types_[type] = spv_type; } case Node_ClosureType: @@ -296,7 +303,7 @@ SpvId CodeGen::convert(const Type* type) { } if (ret_types.size() == 0) ret = std::make_unique(builder_->void_type); else if (ret_types.size() == 1) ret = std::make_unique(ret_types.back()); - else assert(false && "Didn't we refactor this out yet by making functions single-argument ?"); //ret = llvm::StructType::get(context(), ret_types); + else assert(false && "Didn't we refactor this out yet by making functions single-argument ?"); } else ops.push_back(convert(op)); } @@ -307,72 +314,18 @@ SpvId CodeGen::convert(const Type* type) { } assert(false && "TODO: handle closure mess"); - /* auto env_type = convert(Closure::environment_type(world())); - ops.push_back(env_type); - auto fn_type = llvm::FunctionType::get(ret, ops, false); - auto ptr_type = llvm::PointerType::get(fn_type, 0); - llvm_type = llvm::StructType::get(context(), { ptr_type, env_type }); - return types_[type] = llvm_type;*/ } case Node_StructType: { assert(false && "TODO"); - /*auto struct_type = type->as(); - auto llvm_struct = llvm::StructType::create(context()); - - // important: memoize before recursing into element types to avoid endless recursion - assert(!types_.contains(struct_type) && "type already converted"); - types_[struct_type] = llvm_struct; - - Array llvm_types(struct_type->num_ops()); - for (size_t i = 0, e = llvm_types.size(); i != e; ++i) - llvm_types[i] = convert(struct_type->op(i)); - llvm_struct->setBody(llvm_ref(llvm_types)); - return llvm_struct;*/ } case Node_TupleType: { assert(false && "TODO"); - /*auto tuple = type->as(); - Array llvm_types(tuple->num_ops()); - for (size_t i = 0, e = llvm_types.size(); i != e; ++i) - llvm_types[i] = convert(tuple->op(i)); - llvm_type = llvm::StructType::get(context(), llvm_ref(llvm_types)); - return types_[tuple] = llvm_type;*/ } case Node_VariantType: { assert(false && "TODO"); - /*assert(type->num_ops() > 0); - - // Max alignment/size constraints respectively in the variant type alternatives dictate the ones to use for the overall type - size_t max_align = 0, max_size = 0; - - auto layout = module().getDataLayout(); - llvm::Type* max_align_type; - for (auto op : type->ops()) { - auto op_type = convert(op); - size_t size = layout.getTypeAllocSize(op_type); - size_t align = layout.getABITypeAlignment(op_type); - // Favor types that are not empty - if (align > max_align || (align == max_align && max_align_type->isEmptyTy())) { - max_align_type = op_type; - max_align = align; - } - max_size = std::max(max_size, size); - } - - auto rem_size = max_size - layout.getTypeAllocSize(max_align_type); - auto union_type = rem_size > 0 - ? llvm::StructType::get(context(), llvm::ArrayRef { max_align_type, llvm::ArrayType::get(llvm::Type::getInt8Ty(context()), rem_size)}) - : llvm::StructType::get(context(), llvm::ArrayRef { max_align_type }); - - auto tag_type = type->num_ops() < (1_u64 << 8) ? llvm::Type::getInt8Ty (context()) : - type->num_ops() < (1_u64 << 16) ? llvm::Type::getInt16Ty(context()) : - type->num_ops() < (1_u64 << 32) ? llvm::Type::getInt32Ty(context()) : - llvm::Type::getInt64Ty(context()); - - return llvm::StructType::get(context(), { union_type, tag_type });*/ } default: @@ -385,61 +338,29 @@ SpvId CodeGen::convert(const Type* type) { void CodeGen::emit(const thorin::Scope& scope) { entry_ = scope.entry(); assert(entry_->is_returning()); - //auto fct = llvm::cast(emit(entry_)); + auto fn = SpvFnBuilder { }; fn.fn_type = convert(entry_->type()); - fn.fn_ret_type = builder_->void_type; // TODO !!! + fn.fn_ret_type = get_codom_type(entry_); current_fn_ = &fn; - //cont2llvm_.clear(); auto conts = schedule(scope); - // map all bb-like continuations to llvm bb stubs and handle params/phis for (auto cont : conts) { if (cont->intrinsic() == Intrinsic::EndScope) continue; auto [i, b] = fn.bbs.emplace(cont, std::make_unique(*builder_)); + assert(b); + SpvBasicBlockBuilder& bb = *i->second; SpvId label = bb.label(); - builder_->name(label, cont->name().c_str()); + + if (debug()) + builder_->name(label, cont->name().c_str()); fn.labels.emplace(cont, label); - //auto bb = llvm::BasicBlock::Create(context(), cont->name().c_str(), fct); - //auto [i, succ] = cont2llvm_.emplace(cont, std::pair(bb, std::make_unique>(context()))); - assert(b); - // auto& irbuilder = *i->second.second; - // irbuilder.SetInsertPoint(bb); - - //if (debug()) - // irbuilder.SetCurrentDebugLocation(llvm::DebugLoc::get(cont->loc().begin.row, cont->loc().begin.row, discope)); - - /*if (entry_ == cont) { - auto arg = fct->arg_begin(); - for (auto param : entry_->params()) { - if (is_mem(param) || is_unit(param)) { - def2llvm_[param] = nullptr; - } else if (param->order() == 0) { - auto argv = &*arg; - auto value = map_param(fct, argv, param); - if (value == argv) { - arg->setName(param->unique_name()); // use param - def2llvm_[param] = &*arg++; - } else { - def2llvm_[param] = value; // use provided value - } - } - } - } else { - for (auto param : cont->params()) { - if (is_mem(param) || is_unit(param)) { - def2llvm_[param] = nullptr; - } else { - // do not bother reserving anything (the 0 below) - it's a tiny optimization nobody cares about - auto phi = irbuilder.CreatePHI(convert(param->type()), 0, param->name().c_str()); - def2llvm_[param] = phi; - } - } - }*/ + + // TODO prepare phis/params } Scheduler new_scheduler(scope); @@ -454,14 +375,26 @@ void CodeGen::emit(const thorin::Scope& scope) { builder_->define_function(fn); } -void CodeGen::emit_epilogue(Continuation* continuation, SpvBasicBlockBuilder& bb) { - /*auto&& bb_ib = cont2llvm_[continuation]; - auto bb = bb_ib->first; - auto& irbuilder = *bb_ib->second;*/ +SpvId CodeGen::get_codom_type(const Continuation* fn) { + auto ret_cont_type = fn->ret_param()->type(); + std::vector types; + for (auto& op : ret_cont_type->ops()) { + if (op->isa() || is_type_unit(op)) + continue; + assert(op->order() == 0); + types.push_back(convert(op)); + } + if (types.empty()) + return builder_->void_type; + if (types.size() == 1) + return types[0]; + return builder_->declare_struct_type(types); +} +void CodeGen::emit_epilogue(Continuation* continuation, SpvBasicBlockBuilder& bb) { if (continuation->callee() == entry_->ret_param()) { // return - std::vector values; std::vector types; + std::vector values; for (auto arg : continuation->args()) { /*if (auto val = emit_unsafe(arg)) { @@ -472,7 +405,7 @@ void CodeGen::emit_epilogue(Continuation* continuation, SpvBasicBlockBuilder& bb switch (values.size()) { case 0: bb.return_void(); break; - //case 1: irbuilder.CreateRet(values[0]); break; + //case 1: irbuilder.CreateRet(values[0]); break; default: assert(false && "TODO handle non-void returns"); /*llvm::Value* agg = llvm::UndefValue::get(llvm::StructType::get(context(), types)); diff --git a/src/thorin/be/spirv/spirv.h b/src/thorin/be/spirv/spirv.h index 903921be4..13de7ae12 100644 --- a/src/thorin/be/spirv/spirv.h +++ b/src/thorin/be/spirv/spirv.h @@ -22,6 +22,8 @@ class CodeGen : public thorin::CodeGen { void emit(const Scope& scope); void emit_epilogue(Continuation*, SpvBasicBlockBuilder& bb); + SpvId get_codom_type(const Continuation* fn); + SpvFileBuilder* builder_ = nullptr; Continuation* entry_ = nullptr; SpvFnBuilder* current_fn_ = nullptr; From 4a4dbb3cd8ac3556cef36efeedbf4546ebe33ea9 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Wed, 17 Feb 2021 15:21:12 +0100 Subject: [PATCH 024/342] handle function arguments/return properly --- src/thorin/be/spirv/spirv.cpp | 95 ++++++++++++++++++++++++++++------- src/thorin/be/spirv/spirv.h | 2 + 2 files changed, 78 insertions(+), 19 deletions(-) diff --git a/src/thorin/be/spirv/spirv.cpp b/src/thorin/be/spirv/spirv.cpp index b08e71ebb..2957892cc 100644 --- a/src/thorin/be/spirv/spirv.cpp +++ b/src/thorin/be/spirv/spirv.cpp @@ -68,9 +68,25 @@ struct SpvBasicBlockBuilder : public SpvSectionBuilder { return id; } + SpvId composite(SpvId aggregate_t, std::vector& elements) { + op(spv::Op::OpLabel, 3 + elements.size()); + ref_id(aggregate_t); + auto id = generate_fresh_id(); + ref_id(id); + for (auto e : elements) + ref_id(e); + return id; + } + void return_void() { op(spv::Op::OpReturn, 1); } + + void return_value(SpvId value) { + op(spv::Op::OpReturnValue, 2); + ref_id(value); + } + private: SpvId generate_fresh_id(); }; @@ -80,6 +96,10 @@ struct SpvFnBuilder { SpvId fn_ret_type; ContinuationMap> bbs; ContinuationMap labels; + DefMap params; + + // Contains OpFunctionParams + SpvSectionBuilder header; }; struct SpvFileBuilder { @@ -147,6 +167,9 @@ struct SpvFileBuilder { fn_defs.data_.push_back(spv::FunctionControlMaskNone); fn_defs.ref_id(fn_builder.fn_type); + for (auto w : fn_builder.header.data_) + fn_defs.data_.push_back(w); + // TODO OpFunctionParameters for (auto& [cont, bb] : fn_builder.bbs) { for (auto w : bb->data_) @@ -314,14 +337,25 @@ SpvId CodeGen::convert(const Type* type) { } assert(false && "TODO: handle closure mess"); + break; } case Node_StructType: { - assert(false && "TODO"); + std::vector types; + for (auto elem : type->as()->ops()) + types.push_back(convert(elem)); + spv_type = builder_->declare_struct_type(types); + // TODO debug info + break; } case Node_TupleType: { - assert(false && "TODO"); + std::vector types; + for (auto elem : type->as()->ops()) + types.push_back(convert(elem)); + spv_type = builder_->declare_struct_type(types); + // TODO debug info + break; } case Node_VariantType: { @@ -360,7 +394,30 @@ void CodeGen::emit(const thorin::Scope& scope) { builder_->name(label, cont->name().c_str()); fn.labels.emplace(cont, label); - // TODO prepare phis/params + if (entry_ == cont) { + int arg = 0; + for (auto param : entry_->params()) { + if (is_mem(param) || is_unit(param)) { + // Nothing + } else if (param->order() == 0) { + auto param_t = convert(param->type()); + fn.header.op(spv::Op::OpFunctionParameter, 3); + auto id = builder_->generate_fresh_id(); + fn.header.ref_id(param_t); + fn.header.ref_id(id); + fn.params[param] = id; + } + } + } else { + for (auto param : cont->params()) { + if (is_mem(param) || is_unit(param)) { + // Nothing + } else { + // TODO OpPhi requires the full list of predecessors when emitting + assert(false); + } + } + } } Scheduler new_scheduler(scope); @@ -392,30 +449,26 @@ SpvId CodeGen::get_codom_type(const Continuation* fn) { } void CodeGen::emit_epilogue(Continuation* continuation, SpvBasicBlockBuilder& bb) { - if (continuation->callee() == entry_->ret_param()) { // return - std::vector types; + if (continuation->callee() == entry_->ret_param()) { std::vector values; for (auto arg : continuation->args()) { - /*if (auto val = emit_unsafe(arg)) { - values.emplace_back(val); - types.emplace_back(val->getType()); - }*/ + assert(arg->order() == 0); + if (is_mem(arg) || is_unit(arg)) + continue; + auto val = emit(arg); + values.emplace_back(val); } switch (values.size()) { case 0: bb.return_void(); break; - //case 1: irbuilder.CreateRet(values[0]); break; - default: - assert(false && "TODO handle non-void returns"); - /*llvm::Value* agg = llvm::UndefValue::get(llvm::StructType::get(context(), types)); - - for (size_t i = 0, e = values.size(); i != e; ++i) - agg = irbuilder.CreateInsertValue(agg, values[i], { unsigned(i) }); - - irbuilder.CreateRet(agg);*/ + case 1: bb.return_value(values[0]); break; + default: bb.return_value(bb.composite(current_fn_->fn_ret_type, values)); } - } /*else if (continuation->callee() == world().branch()) { + } else { + assert(false && "epilogue not implemented"); + } + /*else if (continuation->callee() == world().branch()) { auto cond = emit(continuation->arg(0)); auto tbb = cont2bb(continuation->arg(1)->as_continuation()); auto fbb = cont2bb(continuation->arg(2)->as_continuation()); @@ -514,4 +567,8 @@ void CodeGen::emit_epilogue(Continuation* continuation, SpvBasicBlockBuilder& bb // irbuilder.SetInsertPoint(bb->getTerminator()); } +SpvId CodeGen::emit(const Def* def) { + return SpvId(); +} + } diff --git a/src/thorin/be/spirv/spirv.h b/src/thorin/be/spirv/spirv.h index 13de7ae12..ff2288f9b 100644 --- a/src/thorin/be/spirv/spirv.h +++ b/src/thorin/be/spirv/spirv.h @@ -21,6 +21,7 @@ class CodeGen : public thorin::CodeGen { SpvId convert(const Type*); void emit(const Scope& scope); void emit_epilogue(Continuation*, SpvBasicBlockBuilder& bb); + SpvId emit(const Def* def); SpvId get_codom_type(const Continuation* fn); @@ -29,6 +30,7 @@ class CodeGen : public thorin::CodeGen { SpvFnBuilder* current_fn_ = nullptr; Scheduler scheduler_; TypeMap types_; + DefMap defs_; }; } From cfb5a2b162d08f543e2d5c9435d16f921336d2d1 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Wed, 17 Feb 2021 16:30:50 +0100 Subject: [PATCH 025/342] infrastructure for branching --- src/thorin/be/spirv/spirv.cpp | 89 ++++++++++++++++++++++++++--------- src/thorin/be/spirv/spirv.h | 2 +- 2 files changed, 67 insertions(+), 24 deletions(-) diff --git a/src/thorin/be/spirv/spirv.cpp b/src/thorin/be/spirv/spirv.cpp index 2957892cc..81e31fa5d 100644 --- a/src/thorin/be/spirv/spirv.cpp +++ b/src/thorin/be/spirv/spirv.cpp @@ -61,6 +61,14 @@ struct SpvBasicBlockBuilder : public SpvSectionBuilder { SpvFileBuilder& file_builder; + struct Phi { + SpvId type; + SpvId value; + std::vector> preds; + }; + GIDMap phis; + DefMap args; + SpvId label() { op(spv::Op::OpLabel, 2); auto id = generate_fresh_id(); @@ -78,6 +86,18 @@ struct SpvBasicBlockBuilder : public SpvSectionBuilder { return id; } + void branch(SpvId target) { + op(spv::Op::OpBranch, 2); + ref_id(target); + } + + void branch_conditional(SpvId condition, SpvId true_target, SpvId false_target) { + op(spv::Op::OpBranchConditional, 4); + ref_id(condition); + ref_id(true_target); + ref_id(false_target); + } + void return_void() { op(spv::Op::OpReturn, 1); } @@ -167,11 +187,20 @@ struct SpvFileBuilder { fn_defs.data_.push_back(spv::FunctionControlMaskNone); fn_defs.ref_id(fn_builder.fn_type); + // Includes stuff like OpFunctionParameters for (auto w : fn_builder.header.data_) fn_defs.data_.push_back(w); - // TODO OpFunctionParameters for (auto& [cont, bb] : fn_builder.bbs) { + for (auto [param, phi] : bb->phis) { + fn_defs.op(spv::Op::OpPhi, 3 + 2 * phi.preds.size()); + fn_defs.ref_id(phi.type); + fn_defs.ref_id(phi.value); + for (auto& [pred_value, pred_label] : phi.preds) { + fn_defs.ref_id(pred_value); + fn_defs.ref_id(pred_label); + } + } for (auto w : bb->data_) fn_defs.data_.push_back(w); } @@ -395,7 +424,6 @@ void CodeGen::emit(const thorin::Scope& scope) { fn.labels.emplace(cont, label); if (entry_ == cont) { - int arg = 0; for (auto param : entry_->params()) { if (is_mem(param) || is_unit(param)) { // Nothing @@ -413,8 +441,10 @@ void CodeGen::emit(const thorin::Scope& scope) { if (is_mem(param) || is_unit(param)) { // Nothing } else { - // TODO OpPhi requires the full list of predecessors when emitting - assert(false); + // OpPhi requires the full list of predecessors (values, labels) + // We don't have that yet! But we will need the Phi node identifier to build the basic blocks ... + // To solve this we generate an id for the phi node now, but defer emission of it to a later stage + bb.phis[param] = { convert(param->type()), builder_->generate_fresh_id(), {} }; } } } @@ -429,6 +459,18 @@ void CodeGen::emit(const thorin::Scope& scope) { emit_epilogue(cont, *fn.bbs[cont]->get()); } + // Wire up Phi nodes + for (auto& [cont, bb]: fn.bbs) { + for (auto [param, phi] : bb->phis) { + assert(param->order() == 0); + for (auto pred : cont->preds()) { + auto& pred_bb = *fn.bbs[pred]; + auto arg = pred->arg(param->index()); + phi.preds.emplace_back(*pred_bb->args[arg], *fn.labels[pred]); + } + } + } + builder_->define_function(fn); } @@ -456,7 +498,7 @@ void CodeGen::emit_epilogue(Continuation* continuation, SpvBasicBlockBuilder& bb assert(arg->order() == 0); if (is_mem(arg) || is_unit(arg)) continue; - auto val = emit(arg); + auto val = emit(arg, bb); values.emplace_back(val); } @@ -465,15 +507,14 @@ void CodeGen::emit_epilogue(Continuation* continuation, SpvBasicBlockBuilder& bb case 1: bb.return_value(values[0]); break; default: bb.return_value(bb.composite(current_fn_->fn_ret_type, values)); } - } else { - assert(false && "epilogue not implemented"); } - /*else if (continuation->callee() == world().branch()) { - auto cond = emit(continuation->arg(0)); - auto tbb = cont2bb(continuation->arg(1)->as_continuation()); - auto fbb = cont2bb(continuation->arg(2)->as_continuation()); - irbuilder.CreateCondBr(cond, tbb, fbb); - } else if (continuation->callee()->isa() && + else if (continuation->callee() == world().branch()) { + auto cond = emit(continuation->arg(0), bb); + bb.args[continuation->arg(0)] = cond; + auto tbb = *current_fn_->labels[continuation->arg(1)->as_continuation()]; + auto fbb = *current_fn_->labels[continuation->arg(2)->as_continuation()]; + bb.branch_conditional(cond, tbb, fbb); + } /*else if (continuation->callee()->isa() && continuation->callee()->as()->intrinsic() == Intrinsic::Match) { auto val = emit(continuation->arg(0)); auto otherwise_bb = cont2bb(continuation->arg(1)->as_continuation()); @@ -486,12 +527,14 @@ void CodeGen::emit_epilogue(Continuation* continuation, SpvBasicBlockBuilder& bb } } else if (continuation->callee()->isa()) { irbuilder.CreateUnreachable(); - } else if (auto callee = continuation->callee()->isa_continuation(); callee && callee->is_basicblock()) { // ordinary jump - for (size_t i = 0, e = continuation->num_args(); i != e; ++i) { - if (auto val = emit_unsafe(continuation->arg(i))) emit_phi_arg(irbuilder, callee->param(i), val); + } */ + else if (auto callee = continuation->callee()->isa_continuation(); callee && callee->is_basicblock()) { // ordinary jump + for (auto& arg : continuation->args()) { + if (is_mem(arg) || is_unit(arg)) continue; + bb.args[arg] = emit(arg, bb); } - irbuilder.CreateBr(cont2bb(callee)); - } else if (auto callee = continuation->callee()->isa_continuation(); callee && callee->is_intrinsic()) { + bb.branch(*current_fn_->labels[callee]); + } /*else if (auto callee = continuation->callee()->isa_continuation(); callee && callee->is_intrinsic()) { auto ret_continuation = emit_intrinsic(irbuilder, continuation); irbuilder.CreateBr(cont2bb(ret_continuation)); } else { // function/closure call @@ -562,13 +605,13 @@ void CodeGen::emit_epilogue(Continuation* continuation, SpvBasicBlockBuilder& bb } } }*/ - - // new insert point is just before the terminator for all other instructions we have to add later on - // irbuilder.SetInsertPoint(bb->getTerminator()); + else { + assert(false && "epilogue not implemented for this"); + } } -SpvId CodeGen::emit(const Def* def) { - return SpvId(); +SpvId CodeGen::emit(const Def* def, SpvBasicBlockBuilder& bb) { + assertf(false, "Incomplete emit(def) definition"); } } diff --git a/src/thorin/be/spirv/spirv.h b/src/thorin/be/spirv/spirv.h index ff2288f9b..a2535fdce 100644 --- a/src/thorin/be/spirv/spirv.h +++ b/src/thorin/be/spirv/spirv.h @@ -21,7 +21,7 @@ class CodeGen : public thorin::CodeGen { SpvId convert(const Type*); void emit(const Scope& scope); void emit_epilogue(Continuation*, SpvBasicBlockBuilder& bb); - SpvId emit(const Def* def); + SpvId emit(const Def* def, SpvBasicBlockBuilder& bb); SpvId get_codom_type(const Continuation* fn); From 7eca24416b9c9e4a2f1e112edcdd1d61dcc407df Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Wed, 17 Feb 2021 17:23:43 +0100 Subject: [PATCH 026/342] added codegen for most binops --- src/thorin/be/spirv/spirv.cpp | 154 +++++++++++++++++++++++++++++++++- 1 file changed, 153 insertions(+), 1 deletion(-) diff --git a/src/thorin/be/spirv/spirv.cpp b/src/thorin/be/spirv/spirv.cpp index 81e31fa5d..aec18d3f4 100644 --- a/src/thorin/be/spirv/spirv.cpp +++ b/src/thorin/be/spirv/spirv.cpp @@ -86,6 +86,16 @@ struct SpvBasicBlockBuilder : public SpvSectionBuilder { return id; } + SpvId binop(spv::Op op_, SpvId result_type, SpvId lhs, SpvId rhs) { + op(op_, 5); + auto id = generate_fresh_id(); + ref_id(result_type); + ref_id(id); + ref_id(lhs); + ref_id(rhs); + return id; + } + void branch(SpvId target) { op(spv::Op::OpBranch, 2); ref_id(target); @@ -179,6 +189,24 @@ struct SpvFileBuilder { return id; } + SpvId bool_constant(SpvId type, bool value) { + types_constants.op(value ? spv::Op::OpConstantTrue : spv::Op::OpConstantFalse, 3); + auto id = generate_fresh_id(); + types_constants.ref_id(type); + types_constants.ref_id(id); + return id; + } + + SpvId constant(SpvId type, std::vector&& bit_pattern) { + types_constants.op(spv::Op::OpConstant, 4 + bit_pattern.size()); + auto id = generate_fresh_id(); + types_constants.ref_id(type); + types_constants.ref_id(id); + for (auto arg : bit_pattern) + types_constants.data_.push_back(arg); + return id; + } + SpvId define_function(SpvFnBuilder& fn_builder) { fn_defs.op(spv::Op::OpFunction, 5); fn_defs.ref_id(fn_builder.fn_ret_type); @@ -290,7 +318,7 @@ SpvId SpvBasicBlockBuilder::generate_fresh_id() { return file_builder.generate_fresh_id(); } -CodeGen::CodeGen(thorin::World& world, Cont2Config& kernel_config, bool debug) +CodeGen::CodeGen(thorin::World& world, Cont2Config&, bool debug) : thorin::CodeGen(world, debug) {} @@ -611,6 +639,130 @@ void CodeGen::emit_epilogue(Continuation* continuation, SpvBasicBlockBuilder& bb } SpvId CodeGen::emit(const Def* def, SpvBasicBlockBuilder& bb) { + if (auto bin = def->isa()) { + SpvId lhs = emit(bin->lhs(), bb); + SpvId rhs = emit(bin->rhs(), bb); + SpvId result_type = convert(def->type()); + + if (auto cmp = bin->isa()) { + auto type = cmp->lhs()->type(); + if (is_type_s(type)) { + switch (cmp->cmp_tag()) { + case Cmp_eq: return bb.binop(spv::Op::OpIEqual , result_type, lhs, rhs); + case Cmp_ne: return bb.binop(spv::Op::OpINotEqual , result_type, lhs, rhs); + case Cmp_gt: return bb.binop(spv::Op::OpSGreaterThan , result_type, lhs, rhs); + case Cmp_ge: return bb.binop(spv::Op::OpSGreaterThanEqual , result_type, lhs, rhs); + case Cmp_lt: return bb.binop(spv::Op::OpSLessThan , result_type, lhs, rhs); + case Cmp_le: return bb.binop(spv::Op::OpSLessThanEqual , result_type, lhs, rhs); + } + } else if (is_type_u(type)) { + switch (cmp->cmp_tag()) { + case Cmp_eq: return bb.binop(spv::Op::OpIEqual , result_type, lhs, rhs); + case Cmp_ne: return bb.binop(spv::Op::OpINotEqual , result_type, lhs, rhs); + case Cmp_gt: return bb.binop(spv::Op::OpUGreaterThan , result_type, lhs, rhs); + case Cmp_ge: return bb.binop(spv::Op::OpUGreaterThanEqual , result_type, lhs, rhs); + case Cmp_lt: return bb.binop(spv::Op::OpULessThan , result_type, lhs, rhs); + case Cmp_le: return bb.binop(spv::Op::OpULessThanEqual , result_type, lhs, rhs); + } + } else if (is_type_f(type)) { + switch (cmp->cmp_tag()) { + // TODO look into the NaN story + case Cmp_eq: return bb.binop(spv::Op::OpFOrdEqual , result_type, lhs, rhs); + case Cmp_ne: return bb.binop(spv::Op::OpFOrdNotEqual , result_type, lhs, rhs); + case Cmp_gt: return bb.binop(spv::Op::OpFOrdGreaterThan , result_type, lhs, rhs); + case Cmp_ge: return bb.binop(spv::Op::OpFOrdGreaterThanEqual , result_type, lhs, rhs); + case Cmp_lt: return bb.binop(spv::Op::OpFOrdLessThan , result_type, lhs, rhs); + case Cmp_le: return bb.binop(spv::Op::OpFOrdLessThanEqual , result_type, lhs, rhs); + } + } else if (type->isa()) { + assertf(false, "Physical pointers are unsupported"); + } else if(is_type_bool(type)) { + switch (cmp->cmp_tag()) { + // TODO look into the NaN story + case Cmp_eq: return bb.binop(spv::Op::OpLogicalEqual , result_type, lhs, rhs); + case Cmp_ne: return bb.binop(spv::Op::OpLogicalNotEqual , result_type, lhs, rhs); + default: THORIN_UNREACHABLE; + } + assertf(false, "TODO: should we emulate the other comparison ops ?"); + } + } + + if (auto arithop = bin->isa()) { + auto type = arithop->type(); + + if (is_type_f(type)) { + switch (arithop->arithop_tag()) { + case ArithOp_add: return bb.binop(spv::Op::OpFAdd, result_type, lhs, rhs); + case ArithOp_sub: return bb.binop(spv::Op::OpFSub, result_type, lhs, rhs); + case ArithOp_mul: return bb.binop(spv::Op::OpFMul, result_type, lhs, rhs); + case ArithOp_div: return bb.binop(spv::Op::OpFDiv, result_type, lhs, rhs); + case ArithOp_rem: return bb.binop(spv::Op::OpFRem, result_type, lhs, rhs); + case ArithOp_and: + case ArithOp_or: + case ArithOp_xor: + case ArithOp_shl: + case ArithOp_shr: THORIN_UNREACHABLE; + } + } + + if (is_type_s(type)) { + switch (arithop->arithop_tag()) { + case ArithOp_add: return bb.binop(spv::Op::OpIAdd , result_type, lhs, rhs); + case ArithOp_sub: return bb.binop(spv::Op::OpISub , result_type, lhs, rhs); + case ArithOp_mul: return bb.binop(spv::Op::OpIMul , result_type, lhs, rhs); + case ArithOp_div: return bb.binop(spv::Op::OpSDiv , result_type, lhs, rhs); + case ArithOp_rem: return bb.binop(spv::Op::OpSRem , result_type, lhs, rhs); + case ArithOp_and: return bb.binop(spv::Op::OpBitwiseAnd , result_type, lhs, rhs); + case ArithOp_or: return bb.binop(spv::Op::OpBitwiseOr , result_type, lhs, rhs); + case ArithOp_xor: return bb.binop(spv::Op::OpBitwiseXor , result_type, lhs, rhs); + case ArithOp_shl: return bb.binop(spv::Op::OpShiftLeftLogical , result_type, lhs, rhs); + case ArithOp_shr: return bb.binop(spv::Op::OpShiftRightArithmetic , result_type, lhs, rhs); + } + } else if (is_type_u(type)) { + switch (arithop->arithop_tag()) { + case ArithOp_add: return bb.binop(spv::Op::OpIAdd , result_type, lhs, rhs); + case ArithOp_sub: return bb.binop(spv::Op::OpISub , result_type, lhs, rhs); + case ArithOp_mul: return bb.binop(spv::Op::OpIMul , result_type, lhs, rhs); + case ArithOp_div: return bb.binop(spv::Op::OpUDiv , result_type, lhs, rhs); + case ArithOp_rem: return bb.binop(spv::Op::OpUMod , result_type, lhs, rhs); + case ArithOp_and: return bb.binop(spv::Op::OpBitwiseAnd , result_type, lhs, rhs); + case ArithOp_or: return bb.binop(spv::Op::OpBitwiseOr , result_type, lhs, rhs); + case ArithOp_xor: return bb.binop(spv::Op::OpBitwiseXor , result_type, lhs, rhs); + case ArithOp_shl: return bb.binop(spv::Op::OpShiftLeftLogical , result_type, lhs, rhs); + case ArithOp_shr: return bb.binop(spv::Op::OpShiftRightLogical , result_type, lhs, rhs); + } + } else if(is_type_bool(type)) { + switch (arithop->arithop_tag()) { + case ArithOp_and: return bb.binop(spv::Op::OpLogicalAnd , result_type, lhs, rhs); + case ArithOp_or: return bb.binop(spv::Op::OpLogicalOr , result_type, lhs, rhs); + // Note: there is no OpLogicalXor + case ArithOp_xor: return bb.binop(spv::Op::OpLogicalNotEqual , result_type, lhs, rhs); + default: THORIN_UNREACHABLE; + } + } + THORIN_UNREACHABLE; + } + } + if (auto primlit = def->isa()) { + Box box = primlit->value(); + auto type = convert(def->type()); + SpvId constant; + switch (primlit->primtype_tag()) { + case PrimType_bool: constant = bb.file_builder.bool_constant(type, box.get_bool()); break; + case PrimType_ps8: case PrimType_qs8: assertf(false, "not implemented yet"); + case PrimType_pu8: case PrimType_qu8: assertf(false, "not implemented yet"); + case PrimType_ps16: case PrimType_qs16: assertf(false, "not implemented yet"); + case PrimType_pu16: case PrimType_qu16: assertf(false, "not implemented yet"); + case PrimType_ps32: case PrimType_qs32: constant = bb.file_builder.constant(type, { static_cast(box.get_s32()) }); break; + case PrimType_pu32: case PrimType_qu32: constant = bb.file_builder.constant(type, { static_cast(box.get_u32()) }); break; + case PrimType_ps64: case PrimType_qs64: assertf(false, "not implemented yet"); + case PrimType_pu64: case PrimType_qu64: assertf(false, "not implemented yet"); + case PrimType_pf16: case PrimType_qf16: assertf(false, "not implemented yet"); + case PrimType_pf32: case PrimType_qf32: assertf(false, "not implemented yet"); + case PrimType_pf64: case PrimType_qf64: assertf(false, "not implemented yet"); + } + return constant; + } assertf(false, "Incomplete emit(def) definition"); } From e0bd48b5cacde56dce3f650fd81821ab2c0cdf7e Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Wed, 17 Feb 2021 20:43:42 +0100 Subject: [PATCH 027/342] ordering, fixing phi nodes ... --- src/thorin/be/spirv/spirv.cpp | 189 +++++++++++++++++----------------- src/thorin/be/spirv/spirv.h | 4 +- 2 files changed, 96 insertions(+), 97 deletions(-) diff --git a/src/thorin/be/spirv/spirv.cpp b/src/thorin/be/spirv/spirv.cpp index aec18d3f4..314afee95 100644 --- a/src/thorin/be/spirv/spirv.cpp +++ b/src/thorin/be/spirv/spirv.cpp @@ -66,15 +66,9 @@ struct SpvBasicBlockBuilder : public SpvSectionBuilder { SpvId value; std::vector> preds; }; - GIDMap phis; + std::unordered_map phis; DefMap args; - - SpvId label() { - op(spv::Op::OpLabel, 2); - auto id = generate_fresh_id(); - ref_id(id); - return id; - } + SpvId label; SpvId composite(SpvId aggregate_t, std::vector& elements) { op(spv::Op::OpLabel, 3 + elements.size()); @@ -124,7 +118,8 @@ struct SpvBasicBlockBuilder : public SpvSectionBuilder { struct SpvFnBuilder { SpvId fn_type; SpvId fn_ret_type; - ContinuationMap> bbs; + std::vector bbs; + std::unordered_map bbs_map; ContinuationMap labels; DefMap params; @@ -198,7 +193,7 @@ struct SpvFileBuilder { } SpvId constant(SpvId type, std::vector&& bit_pattern) { - types_constants.op(spv::Op::OpConstant, 4 + bit_pattern.size()); + types_constants.op(spv::Op::OpConstant, 3 + bit_pattern.size()); auto id = generate_fresh_id(); types_constants.ref_id(type); types_constants.ref_id(id); @@ -219,8 +214,11 @@ struct SpvFileBuilder { for (auto w : fn_builder.header.data_) fn_defs.data_.push_back(w); - for (auto& [cont, bb] : fn_builder.bbs) { - for (auto [param, phi] : bb->phis) { + for (auto& bb : fn_builder.bbs) { + fn_defs.op(spv::Op::OpLabel, 2); + fn_defs.ref_id(bb.label); + + for (auto [param, phi] : bb.phis) { fn_defs.op(spv::Op::OpPhi, 3 + 2 * phi.preds.size()); fn_defs.ref_id(phi.type); fn_defs.ref_id(phi.value); @@ -229,7 +227,8 @@ struct SpvFileBuilder { fn_defs.ref_id(pred_label); } } - for (auto w : bb->data_) + + for (auto w : bb.data_) fn_defs.data_.push_back(w); } @@ -438,18 +437,20 @@ void CodeGen::emit(const thorin::Scope& scope) { auto conts = schedule(scope); + fn.bbs.reserve(conts.size()); + for (auto cont : conts) { if (cont->intrinsic() == Intrinsic::EndScope) continue; - auto [i, b] = fn.bbs.emplace(cont, std::make_unique(*builder_)); + SpvBasicBlockBuilder* bb = &fn.bbs.emplace_back(*builder_); + auto [i, b] = fn.bbs_map.emplace(cont, bb); assert(b); - SpvBasicBlockBuilder& bb = *i->second; - SpvId label = bb.label(); + bb->label = builder_->generate_fresh_id(); if (debug()) - builder_->name(label, cont->name().c_str()); - fn.labels.emplace(cont, label); + builder_->name(bb->label, cont->name().c_str()); + fn.labels.emplace(cont, bb->label); if (entry_ == cont) { for (auto param : entry_->params()) { @@ -472,7 +473,7 @@ void CodeGen::emit(const thorin::Scope& scope) { // OpPhi requires the full list of predecessors (values, labels) // We don't have that yet! But we will need the Phi node identifier to build the basic blocks ... // To solve this we generate an id for the phi node now, but defer emission of it to a later stage - bb.phis[param] = { convert(param->type()), builder_->generate_fresh_id(), {} }; + bb->phis[param] = { convert(param->type()), builder_->generate_fresh_id(), {} }; } } } @@ -484,21 +485,9 @@ void CodeGen::emit(const thorin::Scope& scope) { for (auto cont : conts) { if (cont->intrinsic() == Intrinsic::EndScope) continue; assert(cont == entry_ || cont->is_basicblock()); - emit_epilogue(cont, *fn.bbs[cont]->get()); + emit_epilogue(cont, fn.bbs_map[cont]); } - - // Wire up Phi nodes - for (auto& [cont, bb]: fn.bbs) { - for (auto [param, phi] : bb->phis) { - assert(param->order() == 0); - for (auto pred : cont->preds()) { - auto& pred_bb = *fn.bbs[pred]; - auto arg = pred->arg(param->index()); - phi.preds.emplace_back(*pred_bb->args[arg], *fn.labels[pred]); - } - } - } - + builder_->define_function(fn); } @@ -518,7 +507,7 @@ SpvId CodeGen::get_codom_type(const Continuation* fn) { return builder_->declare_struct_type(types); } -void CodeGen::emit_epilogue(Continuation* continuation, SpvBasicBlockBuilder& bb) { +void CodeGen::emit_epilogue(Continuation* continuation, SpvBasicBlockBuilder* bb) { if (continuation->callee() == entry_->ret_param()) { std::vector values; @@ -531,17 +520,17 @@ void CodeGen::emit_epilogue(Continuation* continuation, SpvBasicBlockBuilder& bb } switch (values.size()) { - case 0: bb.return_void(); break; - case 1: bb.return_value(values[0]); break; - default: bb.return_value(bb.composite(current_fn_->fn_ret_type, values)); + case 0: bb->return_void(); break; + case 1: bb->return_value(values[0]); break; + default: bb->return_value(bb->composite(current_fn_->fn_ret_type, values)); } } else if (continuation->callee() == world().branch()) { auto cond = emit(continuation->arg(0), bb); - bb.args[continuation->arg(0)] = cond; + bb->args[continuation->arg(0)] = cond; auto tbb = *current_fn_->labels[continuation->arg(1)->as_continuation()]; auto fbb = *current_fn_->labels[continuation->arg(2)->as_continuation()]; - bb.branch_conditional(cond, tbb, fbb); + bb->branch_conditional(cond, tbb, fbb); } /*else if (continuation->callee()->isa() && continuation->callee()->as()->intrinsic() == Intrinsic::Match) { auto val = emit(continuation->arg(0)); @@ -557,11 +546,16 @@ void CodeGen::emit_epilogue(Continuation* continuation, SpvBasicBlockBuilder& bb irbuilder.CreateUnreachable(); } */ else if (auto callee = continuation->callee()->isa_continuation(); callee && callee->is_basicblock()) { // ordinary jump + int index = -1; for (auto& arg : continuation->args()) { + index++; if (is_mem(arg) || is_unit(arg)) continue; - bb.args[arg] = emit(arg, bb); + bb->args[arg] = emit(arg, bb); + auto* param = callee->param(index); + auto& phi = current_fn_->bbs_map[callee]->phis[param]; + phi.preds.emplace_back(*bb->args[arg], *current_fn_->labels[continuation]); } - bb.branch(*current_fn_->labels[callee]); + bb->branch(*current_fn_->labels[callee]); } /*else if (auto callee = continuation->callee()->isa_continuation(); callee && callee->is_intrinsic()) { auto ret_continuation = emit_intrinsic(irbuilder, continuation); irbuilder.CreateBr(cont2bb(ret_continuation)); @@ -638,7 +632,7 @@ void CodeGen::emit_epilogue(Continuation* continuation, SpvBasicBlockBuilder& bb } } -SpvId CodeGen::emit(const Def* def, SpvBasicBlockBuilder& bb) { +SpvId CodeGen::emit(const Def* def, SpvBasicBlockBuilder* bb) { if (auto bin = def->isa()) { SpvId lhs = emit(bin->lhs(), bb); SpvId rhs = emit(bin->rhs(), bb); @@ -648,39 +642,39 @@ SpvId CodeGen::emit(const Def* def, SpvBasicBlockBuilder& bb) { auto type = cmp->lhs()->type(); if (is_type_s(type)) { switch (cmp->cmp_tag()) { - case Cmp_eq: return bb.binop(spv::Op::OpIEqual , result_type, lhs, rhs); - case Cmp_ne: return bb.binop(spv::Op::OpINotEqual , result_type, lhs, rhs); - case Cmp_gt: return bb.binop(spv::Op::OpSGreaterThan , result_type, lhs, rhs); - case Cmp_ge: return bb.binop(spv::Op::OpSGreaterThanEqual , result_type, lhs, rhs); - case Cmp_lt: return bb.binop(spv::Op::OpSLessThan , result_type, lhs, rhs); - case Cmp_le: return bb.binop(spv::Op::OpSLessThanEqual , result_type, lhs, rhs); + case Cmp_eq: return bb->binop(spv::Op::OpIEqual , result_type, lhs, rhs); + case Cmp_ne: return bb->binop(spv::Op::OpINotEqual , result_type, lhs, rhs); + case Cmp_gt: return bb->binop(spv::Op::OpSGreaterThan , result_type, lhs, rhs); + case Cmp_ge: return bb->binop(spv::Op::OpSGreaterThanEqual , result_type, lhs, rhs); + case Cmp_lt: return bb->binop(spv::Op::OpSLessThan , result_type, lhs, rhs); + case Cmp_le: return bb->binop(spv::Op::OpSLessThanEqual , result_type, lhs, rhs); } } else if (is_type_u(type)) { switch (cmp->cmp_tag()) { - case Cmp_eq: return bb.binop(spv::Op::OpIEqual , result_type, lhs, rhs); - case Cmp_ne: return bb.binop(spv::Op::OpINotEqual , result_type, lhs, rhs); - case Cmp_gt: return bb.binop(spv::Op::OpUGreaterThan , result_type, lhs, rhs); - case Cmp_ge: return bb.binop(spv::Op::OpUGreaterThanEqual , result_type, lhs, rhs); - case Cmp_lt: return bb.binop(spv::Op::OpULessThan , result_type, lhs, rhs); - case Cmp_le: return bb.binop(spv::Op::OpULessThanEqual , result_type, lhs, rhs); + case Cmp_eq: return bb->binop(spv::Op::OpIEqual , result_type, lhs, rhs); + case Cmp_ne: return bb->binop(spv::Op::OpINotEqual , result_type, lhs, rhs); + case Cmp_gt: return bb->binop(spv::Op::OpUGreaterThan , result_type, lhs, rhs); + case Cmp_ge: return bb->binop(spv::Op::OpUGreaterThanEqual , result_type, lhs, rhs); + case Cmp_lt: return bb->binop(spv::Op::OpULessThan , result_type, lhs, rhs); + case Cmp_le: return bb->binop(spv::Op::OpULessThanEqual , result_type, lhs, rhs); } } else if (is_type_f(type)) { switch (cmp->cmp_tag()) { // TODO look into the NaN story - case Cmp_eq: return bb.binop(spv::Op::OpFOrdEqual , result_type, lhs, rhs); - case Cmp_ne: return bb.binop(spv::Op::OpFOrdNotEqual , result_type, lhs, rhs); - case Cmp_gt: return bb.binop(spv::Op::OpFOrdGreaterThan , result_type, lhs, rhs); - case Cmp_ge: return bb.binop(spv::Op::OpFOrdGreaterThanEqual , result_type, lhs, rhs); - case Cmp_lt: return bb.binop(spv::Op::OpFOrdLessThan , result_type, lhs, rhs); - case Cmp_le: return bb.binop(spv::Op::OpFOrdLessThanEqual , result_type, lhs, rhs); + case Cmp_eq: return bb->binop(spv::Op::OpFOrdEqual , result_type, lhs, rhs); + case Cmp_ne: return bb->binop(spv::Op::OpFOrdNotEqual , result_type, lhs, rhs); + case Cmp_gt: return bb->binop(spv::Op::OpFOrdGreaterThan , result_type, lhs, rhs); + case Cmp_ge: return bb->binop(spv::Op::OpFOrdGreaterThanEqual , result_type, lhs, rhs); + case Cmp_lt: return bb->binop(spv::Op::OpFOrdLessThan , result_type, lhs, rhs); + case Cmp_le: return bb->binop(spv::Op::OpFOrdLessThanEqual , result_type, lhs, rhs); } } else if (type->isa()) { assertf(false, "Physical pointers are unsupported"); } else if(is_type_bool(type)) { switch (cmp->cmp_tag()) { // TODO look into the NaN story - case Cmp_eq: return bb.binop(spv::Op::OpLogicalEqual , result_type, lhs, rhs); - case Cmp_ne: return bb.binop(spv::Op::OpLogicalNotEqual , result_type, lhs, rhs); + case Cmp_eq: return bb->binop(spv::Op::OpLogicalEqual , result_type, lhs, rhs); + case Cmp_ne: return bb->binop(spv::Op::OpLogicalNotEqual , result_type, lhs, rhs); default: THORIN_UNREACHABLE; } assertf(false, "TODO: should we emulate the other comparison ops ?"); @@ -692,11 +686,11 @@ SpvId CodeGen::emit(const Def* def, SpvBasicBlockBuilder& bb) { if (is_type_f(type)) { switch (arithop->arithop_tag()) { - case ArithOp_add: return bb.binop(spv::Op::OpFAdd, result_type, lhs, rhs); - case ArithOp_sub: return bb.binop(spv::Op::OpFSub, result_type, lhs, rhs); - case ArithOp_mul: return bb.binop(spv::Op::OpFMul, result_type, lhs, rhs); - case ArithOp_div: return bb.binop(spv::Op::OpFDiv, result_type, lhs, rhs); - case ArithOp_rem: return bb.binop(spv::Op::OpFRem, result_type, lhs, rhs); + case ArithOp_add: return bb->binop(spv::Op::OpFAdd, result_type, lhs, rhs); + case ArithOp_sub: return bb->binop(spv::Op::OpFSub, result_type, lhs, rhs); + case ArithOp_mul: return bb->binop(spv::Op::OpFMul, result_type, lhs, rhs); + case ArithOp_div: return bb->binop(spv::Op::OpFDiv, result_type, lhs, rhs); + case ArithOp_rem: return bb->binop(spv::Op::OpFRem, result_type, lhs, rhs); case ArithOp_and: case ArithOp_or: case ArithOp_xor: @@ -707,54 +701,53 @@ SpvId CodeGen::emit(const Def* def, SpvBasicBlockBuilder& bb) { if (is_type_s(type)) { switch (arithop->arithop_tag()) { - case ArithOp_add: return bb.binop(spv::Op::OpIAdd , result_type, lhs, rhs); - case ArithOp_sub: return bb.binop(spv::Op::OpISub , result_type, lhs, rhs); - case ArithOp_mul: return bb.binop(spv::Op::OpIMul , result_type, lhs, rhs); - case ArithOp_div: return bb.binop(spv::Op::OpSDiv , result_type, lhs, rhs); - case ArithOp_rem: return bb.binop(spv::Op::OpSRem , result_type, lhs, rhs); - case ArithOp_and: return bb.binop(spv::Op::OpBitwiseAnd , result_type, lhs, rhs); - case ArithOp_or: return bb.binop(spv::Op::OpBitwiseOr , result_type, lhs, rhs); - case ArithOp_xor: return bb.binop(spv::Op::OpBitwiseXor , result_type, lhs, rhs); - case ArithOp_shl: return bb.binop(spv::Op::OpShiftLeftLogical , result_type, lhs, rhs); - case ArithOp_shr: return bb.binop(spv::Op::OpShiftRightArithmetic , result_type, lhs, rhs); + case ArithOp_add: return bb->binop(spv::Op::OpIAdd , result_type, lhs, rhs); + case ArithOp_sub: return bb->binop(spv::Op::OpISub , result_type, lhs, rhs); + case ArithOp_mul: return bb->binop(spv::Op::OpIMul , result_type, lhs, rhs); + case ArithOp_div: return bb->binop(spv::Op::OpSDiv , result_type, lhs, rhs); + case ArithOp_rem: return bb->binop(spv::Op::OpSRem , result_type, lhs, rhs); + case ArithOp_and: return bb->binop(spv::Op::OpBitwiseAnd , result_type, lhs, rhs); + case ArithOp_or: return bb->binop(spv::Op::OpBitwiseOr , result_type, lhs, rhs); + case ArithOp_xor: return bb->binop(spv::Op::OpBitwiseXor , result_type, lhs, rhs); + case ArithOp_shl: return bb->binop(spv::Op::OpShiftLeftLogical , result_type, lhs, rhs); + case ArithOp_shr: return bb->binop(spv::Op::OpShiftRightArithmetic , result_type, lhs, rhs); } } else if (is_type_u(type)) { switch (arithop->arithop_tag()) { - case ArithOp_add: return bb.binop(spv::Op::OpIAdd , result_type, lhs, rhs); - case ArithOp_sub: return bb.binop(spv::Op::OpISub , result_type, lhs, rhs); - case ArithOp_mul: return bb.binop(spv::Op::OpIMul , result_type, lhs, rhs); - case ArithOp_div: return bb.binop(spv::Op::OpUDiv , result_type, lhs, rhs); - case ArithOp_rem: return bb.binop(spv::Op::OpUMod , result_type, lhs, rhs); - case ArithOp_and: return bb.binop(spv::Op::OpBitwiseAnd , result_type, lhs, rhs); - case ArithOp_or: return bb.binop(spv::Op::OpBitwiseOr , result_type, lhs, rhs); - case ArithOp_xor: return bb.binop(spv::Op::OpBitwiseXor , result_type, lhs, rhs); - case ArithOp_shl: return bb.binop(spv::Op::OpShiftLeftLogical , result_type, lhs, rhs); - case ArithOp_shr: return bb.binop(spv::Op::OpShiftRightLogical , result_type, lhs, rhs); + case ArithOp_add: return bb->binop(spv::Op::OpIAdd , result_type, lhs, rhs); + case ArithOp_sub: return bb->binop(spv::Op::OpISub , result_type, lhs, rhs); + case ArithOp_mul: return bb->binop(spv::Op::OpIMul , result_type, lhs, rhs); + case ArithOp_div: return bb->binop(spv::Op::OpUDiv , result_type, lhs, rhs); + case ArithOp_rem: return bb->binop(spv::Op::OpUMod , result_type, lhs, rhs); + case ArithOp_and: return bb->binop(spv::Op::OpBitwiseAnd , result_type, lhs, rhs); + case ArithOp_or: return bb->binop(spv::Op::OpBitwiseOr , result_type, lhs, rhs); + case ArithOp_xor: return bb->binop(spv::Op::OpBitwiseXor , result_type, lhs, rhs); + case ArithOp_shl: return bb->binop(spv::Op::OpShiftLeftLogical , result_type, lhs, rhs); + case ArithOp_shr: return bb->binop(spv::Op::OpShiftRightLogical , result_type, lhs, rhs); } } else if(is_type_bool(type)) { switch (arithop->arithop_tag()) { - case ArithOp_and: return bb.binop(spv::Op::OpLogicalAnd , result_type, lhs, rhs); - case ArithOp_or: return bb.binop(spv::Op::OpLogicalOr , result_type, lhs, rhs); + case ArithOp_and: return bb->binop(spv::Op::OpLogicalAnd , result_type, lhs, rhs); + case ArithOp_or: return bb->binop(spv::Op::OpLogicalOr , result_type, lhs, rhs); // Note: there is no OpLogicalXor - case ArithOp_xor: return bb.binop(spv::Op::OpLogicalNotEqual , result_type, lhs, rhs); + case ArithOp_xor: return bb->binop(spv::Op::OpLogicalNotEqual , result_type, lhs, rhs); default: THORIN_UNREACHABLE; } } THORIN_UNREACHABLE; } - } - if (auto primlit = def->isa()) { + } else if (auto primlit = def->isa()) { Box box = primlit->value(); auto type = convert(def->type()); SpvId constant; switch (primlit->primtype_tag()) { - case PrimType_bool: constant = bb.file_builder.bool_constant(type, box.get_bool()); break; + case PrimType_bool: constant = bb->file_builder.bool_constant(type, box.get_bool()); break; case PrimType_ps8: case PrimType_qs8: assertf(false, "not implemented yet"); case PrimType_pu8: case PrimType_qu8: assertf(false, "not implemented yet"); case PrimType_ps16: case PrimType_qs16: assertf(false, "not implemented yet"); case PrimType_pu16: case PrimType_qu16: assertf(false, "not implemented yet"); - case PrimType_ps32: case PrimType_qs32: constant = bb.file_builder.constant(type, { static_cast(box.get_s32()) }); break; - case PrimType_pu32: case PrimType_qu32: constant = bb.file_builder.constant(type, { static_cast(box.get_u32()) }); break; + case PrimType_ps32: case PrimType_qs32: constant = bb->file_builder.constant(type, { static_cast(box.get_s32()) }); break; + case PrimType_pu32: case PrimType_qu32: constant = bb->file_builder.constant(type, { static_cast(box.get_u32()) }); break; case PrimType_ps64: case PrimType_qs64: assertf(false, "not implemented yet"); case PrimType_pu64: case PrimType_qu64: assertf(false, "not implemented yet"); case PrimType_pf16: case PrimType_qf16: assertf(false, "not implemented yet"); @@ -762,6 +755,12 @@ SpvId CodeGen::emit(const Def* def, SpvBasicBlockBuilder& bb) { case PrimType_pf64: case PrimType_qf64: assertf(false, "not implemented yet"); } return constant; + } else if(auto param = def->isa()) { + if (auto param_id = current_fn_->params.lookup(param)) { + return *param_id; + } else { + return (*current_fn_->bbs_map[param->continuation()]).phis[param].value; + } } assertf(false, "Incomplete emit(def) definition"); } diff --git a/src/thorin/be/spirv/spirv.h b/src/thorin/be/spirv/spirv.h index a2535fdce..8cbc42072 100644 --- a/src/thorin/be/spirv/spirv.h +++ b/src/thorin/be/spirv/spirv.h @@ -20,8 +20,8 @@ class CodeGen : public thorin::CodeGen { protected: SpvId convert(const Type*); void emit(const Scope& scope); - void emit_epilogue(Continuation*, SpvBasicBlockBuilder& bb); - SpvId emit(const Def* def, SpvBasicBlockBuilder& bb); + void emit_epilogue(Continuation*, SpvBasicBlockBuilder* bb); + SpvId emit(const Def* def, SpvBasicBlockBuilder* bb); SpvId get_codom_type(const Continuation* fn); From 4d70f1384f4430c21e383a0c1b7699f98c12dfdf Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Thu, 18 Feb 2021 10:49:22 +0100 Subject: [PATCH 028/342] renamed vkcompute to spirv --- src/thorin/be/backends.cpp | 14 +++++++------- src/thorin/be/backends.h | 2 +- src/thorin/be/llvm/llvm.cpp | 2 +- src/thorin/continuation.cpp | 2 +- src/thorin/continuation.h | 2 +- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/thorin/be/backends.cpp b/src/thorin/be/backends.cpp index 2808c74e3..ed0de4c5d 100644 --- a/src/thorin/be/backends.cpp +++ b/src/thorin/be/backends.cpp @@ -86,12 +86,12 @@ Backends::Backends(World& world, int opt, bool debug) Continuation* imported = nullptr; static const auto backend_intrinsics = std::array { - std::pair { CUDA, Intrinsic::CUDA }, - std::pair { NVVM, Intrinsic::NVVM }, - std::pair { OpenCL, Intrinsic::OpenCL }, - std::pair { AMDGPU, Intrinsic::AMDGPU }, - std::pair { HLS, Intrinsic::HLS }, - std::pair { VkCompute, Intrinsic::VkCompute } + std::pair { CUDA, Intrinsic::CUDA }, + std::pair { NVVM, Intrinsic::NVVM }, + std::pair { OpenCL, Intrinsic::OpenCL }, + std::pair { AMDGPU, Intrinsic::AMDGPU }, + std::pair { HLS, Intrinsic::HLS }, + std::pair { SpirV , Intrinsic::SpirV } }; for (auto [backend, intrinsic] : backend_intrinsics) { if (is_passed_to_intrinsic(continuation, intrinsic)) { @@ -186,7 +186,7 @@ Backends::Backends(World& world, int opt, bool debug) // TODO: maybe use the C backend as a fallback when LLVM is not present for host codegen ? #endif #if THORIN_ENABLE_SPIRV - if (!importers_[VkCompute].world().empty()) device_cgs[VkCompute] = std::make_unique(importers_[VkCompute].world(), kernel_config, debug); + if (!importers_[SpirV].world().empty()) device_cgs[SpirV] = std::make_unique(importers_[SpirV].world(), kernel_config, debug); #endif for (auto [backend, lang] : std::array { std::pair { CUDA, c::Lang::CUDA }, std::pair { OpenCL, c::Lang::OPENCL }, std::pair { HLS, c::Lang::HLS } }) if (!importers_[backend].world().empty()) device_cgs[backend] = std::make_unique(importers_[backend].world(), kernel_config, lang, debug); diff --git a/src/thorin/be/backends.h b/src/thorin/be/backends.h index 2d366b23a..c369606e5 100644 --- a/src/thorin/be/backends.h +++ b/src/thorin/be/backends.h @@ -43,7 +43,7 @@ struct Backends { std::unique_ptr cpu_cg; - enum { CUDA, NVVM, OpenCL, AMDGPU, HLS, VkCompute, BackendCount }; + enum { CUDA, NVVM, OpenCL, AMDGPU, HLS, SpirV, BackendCount }; std::array, BackendCount> device_cgs; private: std::vector importers_; diff --git a/src/thorin/be/llvm/llvm.cpp b/src/thorin/be/llvm/llvm.cpp index e11e5d43a..c1a365b19 100644 --- a/src/thorin/be/llvm/llvm.cpp +++ b/src/thorin/be/llvm/llvm.cpp @@ -1115,7 +1115,7 @@ Continuation* CodeGen::emit_intrinsic(llvm::IRBuilder<>& irbuilder, Continuation case Intrinsic::NVVM: return runtime_->emit_host_code(*this, irbuilder, Runtime::CUDA_PLATFORM, ".nvvm", continuation); case Intrinsic::OpenCL: return runtime_->emit_host_code(*this, irbuilder, Runtime::OPENCL_PLATFORM, ".cl", continuation); case Intrinsic::AMDGPU: return runtime_->emit_host_code(*this, irbuilder, Runtime::HSA_PLATFORM, ".amdgpu", continuation); - case Intrinsic::VkCompute: return runtime_->emit_host_code(*this, irbuilder, Runtime::OPENCL_PLATFORM, ".spv", continuation); // TODO have a real runtime component + case Intrinsic::SpirV: return runtime_->emit_host_code(*this, irbuilder, Runtime::OPENCL_PLATFORM, ".spv", continuation); // TODO have a real runtime component case Intrinsic::HLS: return emit_hls(irbuilder, continuation); case Intrinsic::Parallel: return emit_parallel(irbuilder, continuation); case Intrinsic::Fibers: return emit_fibers(irbuilder, continuation); diff --git a/src/thorin/continuation.cpp b/src/thorin/continuation.cpp index b354e8647..75f298991 100644 --- a/src/thorin/continuation.cpp +++ b/src/thorin/continuation.cpp @@ -167,7 +167,7 @@ void Continuation::set_intrinsic() { else if (name() == "nvvm") attributes().intrinsic = Intrinsic::NVVM; else if (name() == "opencl") attributes().intrinsic = Intrinsic::OpenCL; else if (name() == "amdgpu") attributes().intrinsic = Intrinsic::AMDGPU; - else if (name() == "vk_compute") attributes().intrinsic = Intrinsic::VkCompute; + else if (name() == "spirv") attributes().intrinsic = Intrinsic::SpirV; else if (name() == "hls") attributes().intrinsic = Intrinsic::HLS; else if (name() == "parallel") attributes().intrinsic = Intrinsic::Parallel; else if (name() == "fibers") attributes().intrinsic = Intrinsic::Fibers; diff --git a/src/thorin/continuation.h b/src/thorin/continuation.h index 3f6697d1b..bff19a11e 100644 --- a/src/thorin/continuation.h +++ b/src/thorin/continuation.h @@ -62,7 +62,7 @@ enum class Intrinsic : uint8_t { NVVM, ///< Internal NNVM-Backend. OpenCL, ///< Internal OpenCL-Backend. AMDGPU, ///< Internal AMDGPU-Backend. - VkCompute, ///< Internal Vulkan-Compute-Shader-Backend. + SpirV, ///< Internal Vulkan-Compute-Shader-Backend. HLS, ///< Internal HLS-Backend. Parallel, ///< Internal Parallel-CPU-Backend. Fibers, ///< Internal Parallel-CPU-Backend using resumable fibers. From 2b166ca4eadddf586f633c0d949fcb11b45f12ba Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Wed, 10 Mar 2021 15:57:40 +0100 Subject: [PATCH 029/342] fix whitespace --- src/thorin/be/llvm/llvm.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/thorin/be/llvm/llvm.cpp b/src/thorin/be/llvm/llvm.cpp index c1a365b19..0676f1bcd 100644 --- a/src/thorin/be/llvm/llvm.cpp +++ b/src/thorin/be/llvm/llvm.cpp @@ -1115,7 +1115,7 @@ Continuation* CodeGen::emit_intrinsic(llvm::IRBuilder<>& irbuilder, Continuation case Intrinsic::NVVM: return runtime_->emit_host_code(*this, irbuilder, Runtime::CUDA_PLATFORM, ".nvvm", continuation); case Intrinsic::OpenCL: return runtime_->emit_host_code(*this, irbuilder, Runtime::OPENCL_PLATFORM, ".cl", continuation); case Intrinsic::AMDGPU: return runtime_->emit_host_code(*this, irbuilder, Runtime::HSA_PLATFORM, ".amdgpu", continuation); - case Intrinsic::SpirV: return runtime_->emit_host_code(*this, irbuilder, Runtime::OPENCL_PLATFORM, ".spv", continuation); // TODO have a real runtime component + case Intrinsic::SpirV: return runtime_->emit_host_code(*this, irbuilder, Runtime::OPENCL_PLATFORM, ".spv", continuation); // TODO have a real runtime component case Intrinsic::HLS: return emit_hls(irbuilder, continuation); case Intrinsic::Parallel: return emit_parallel(irbuilder, continuation); case Intrinsic::Fibers: return emit_fibers(irbuilder, continuation); From 282ab46df98dc1cb0f005f994329d8065a508432 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Fri, 12 Mar 2021 11:38:19 +0100 Subject: [PATCH 030/342] move builder stuff out --- src/thorin/CMakeLists.txt | 3 +- src/thorin/be/spirv/spirv.cpp | 333 ++---------------------- src/thorin/be/spirv/spirv.h | 36 ++- src/thorin/be/spirv/spirv_builder.hpp | 325 +++++++++++++++++++++++ src/thorin/be/spirv/spirv_transform.cpp | 14 + 5 files changed, 383 insertions(+), 328 deletions(-) create mode 100644 src/thorin/be/spirv/spirv_builder.hpp create mode 100644 src/thorin/be/spirv/spirv_transform.cpp diff --git a/src/thorin/CMakeLists.txt b/src/thorin/CMakeLists.txt index c1c13a5ec..e38158fac 100644 --- a/src/thorin/CMakeLists.txt +++ b/src/thorin/CMakeLists.txt @@ -84,7 +84,8 @@ set(THORIN_SOURCES util/utility.h be/backends.cpp be/backends.h - ) + be/spirv/spirv_transform.cpp +) if(LLVM_FOUND) list(APPEND THORIN_SOURCES diff --git a/src/thorin/be/spirv/spirv.cpp b/src/thorin/be/spirv/spirv.cpp index 314afee95..6a3de9306 100644 --- a/src/thorin/be/spirv/spirv.cpp +++ b/src/thorin/be/spirv/spirv.cpp @@ -1,332 +1,25 @@ #include "thorin/be/spirv/spirv.h" #include "thorin/analyses/scope.h" +#include "thorin/analyses/schedule.h" -#include +#include "thorin/transform/cleanup_world.h" #include -#include - -int div_roundup(int a, int b) { - if (a % b == 0) - return a / b; - else - return (a / b) + 1; -} namespace thorin::spirv { -struct SpvSectionBuilder { - std::vector data_; - -private: - void output_word(uint32_t word) { - data_.push_back(word); - } -public: - void op(spv::Op op, int ops_size) { - uint32_t lower = op & 0xFFFFu; - uint32_t upper = (ops_size << 16) & 0xFFFF0000u; - output_word(lower | upper); - } - - void ref_id(SpvId id) { - assert(id.id != 0); - output_word(id.id); - } - - void literal_name(std::string_view str) { - int i = 0; - uint32_t cword = 0; - for (char c : str) { - cword = cword | (c & 0xFF) << (i * 8); - i++; - if (i == 4) { - output_word(cword); - cword = 0; - i = 0; - } - } - output_word(cword); - } - - void literal_int(uint32_t i) { - output_word(i); - } -}; - -struct SpvBasicBlockBuilder : public SpvSectionBuilder { - explicit SpvBasicBlockBuilder(SpvFileBuilder& file_builder) - : file_builder(file_builder) - {} - - SpvFileBuilder& file_builder; - - struct Phi { - SpvId type; - SpvId value; - std::vector> preds; - }; - std::unordered_map phis; - DefMap args; - SpvId label; - - SpvId composite(SpvId aggregate_t, std::vector& elements) { - op(spv::Op::OpLabel, 3 + elements.size()); - ref_id(aggregate_t); - auto id = generate_fresh_id(); - ref_id(id); - for (auto e : elements) - ref_id(e); - return id; - } - - SpvId binop(spv::Op op_, SpvId result_type, SpvId lhs, SpvId rhs) { - op(op_, 5); - auto id = generate_fresh_id(); - ref_id(result_type); - ref_id(id); - ref_id(lhs); - ref_id(rhs); - return id; - } - - void branch(SpvId target) { - op(spv::Op::OpBranch, 2); - ref_id(target); - } - - void branch_conditional(SpvId condition, SpvId true_target, SpvId false_target) { - op(spv::Op::OpBranchConditional, 4); - ref_id(condition); - ref_id(true_target); - ref_id(false_target); - } - - void return_void() { - op(spv::Op::OpReturn, 1); - } - - void return_value(SpvId value) { - op(spv::Op::OpReturnValue, 2); - ref_id(value); - } - -private: - SpvId generate_fresh_id(); -}; - -struct SpvFnBuilder { - SpvId fn_type; - SpvId fn_ret_type; - std::vector bbs; - std::unordered_map bbs_map; - ContinuationMap labels; - DefMap params; - - // Contains OpFunctionParams - SpvSectionBuilder header; -}; - -struct SpvFileBuilder { - SpvFileBuilder() - : void_type(declare_void_type()) - {} - - SpvId generate_fresh_id() { return {bound++ }; } - - void name(SpvId id, std::string_view str) { - assert(id.id < bound); - debug_names.op(spv::Op::OpName, 2 + div_roundup(str.size() + 1, 4)); - debug_names.ref_id(id); - debug_names.literal_name(str); - } - - SpvId declare_bool_type() { - types_constants.op(spv::Op::OpTypeBool, 2); - auto id = generate_fresh_id(); - types_constants.ref_id(id); - return id; - } - - SpvId declare_int_type(int width, bool signed_) { - types_constants.op(spv::Op::OpTypeInt, 4); - auto id = generate_fresh_id(); - types_constants.ref_id(id); - types_constants.literal_int(width); - types_constants.literal_int(signed_ ? 1 : 0); - return id; - } - - SpvId declare_float_type(int width) { - types_constants.op(spv::Op::OpTypeFloat, 3); - auto id = generate_fresh_id(); - types_constants.ref_id(id); - types_constants.literal_int(width); - return id; - } - - SpvId declare_fn_type(std::vector& dom, SpvId codom) { - types_constants.op(spv::Op::OpTypeFunction, 3 + dom.size()); - auto id = generate_fresh_id(); - types_constants.ref_id(id); - types_constants.ref_id(codom); - for (auto arg : dom) - types_constants.ref_id(arg); - return id; - } - - SpvId declare_struct_type(std::vector& elements) { - types_constants.op(spv::Op::OpTypeStruct, 2 + elements.size()); - auto id = generate_fresh_id(); - types_constants.ref_id(id); - for (auto arg : elements) - types_constants.ref_id(arg); - return id; - } - - SpvId bool_constant(SpvId type, bool value) { - types_constants.op(value ? spv::Op::OpConstantTrue : spv::Op::OpConstantFalse, 3); - auto id = generate_fresh_id(); - types_constants.ref_id(type); - types_constants.ref_id(id); - return id; - } - - SpvId constant(SpvId type, std::vector&& bit_pattern) { - types_constants.op(spv::Op::OpConstant, 3 + bit_pattern.size()); - auto id = generate_fresh_id(); - types_constants.ref_id(type); - types_constants.ref_id(id); - for (auto arg : bit_pattern) - types_constants.data_.push_back(arg); - return id; - } - - SpvId define_function(SpvFnBuilder& fn_builder) { - fn_defs.op(spv::Op::OpFunction, 5); - fn_defs.ref_id(fn_builder.fn_ret_type); - auto id = generate_fresh_id(); - fn_defs.ref_id(id); - fn_defs.data_.push_back(spv::FunctionControlMaskNone); - fn_defs.ref_id(fn_builder.fn_type); - - // Includes stuff like OpFunctionParameters - for (auto w : fn_builder.header.data_) - fn_defs.data_.push_back(w); - - for (auto& bb : fn_builder.bbs) { - fn_defs.op(spv::Op::OpLabel, 2); - fn_defs.ref_id(bb.label); - - for (auto [param, phi] : bb.phis) { - fn_defs.op(spv::Op::OpPhi, 3 + 2 * phi.preds.size()); - fn_defs.ref_id(phi.type); - fn_defs.ref_id(phi.value); - for (auto& [pred_value, pred_label] : phi.preds) { - fn_defs.ref_id(pred_value); - fn_defs.ref_id(pred_label); - } - } - - for (auto w : bb.data_) - fn_defs.data_.push_back(w); - } - - fn_defs.op(spv::Op::OpFunctionEnd, 1); - return id; - } - - void capability(spv::Capability cap) { - capabilities.op(spv::Op::OpCapability, 2); - capabilities.data_.push_back(cap); - } - - spv::AddressingModel addressing_model = spv::AddressingModel::AddressingModelLogical; - spv::MemoryModel memory_model = spv::MemoryModel::MemoryModelSimple; - -private: - std::ostream* output_ = nullptr; - uint32_t bound = 1; - - // Ordered as per https://www.khronos.org/registry/spir-v/specs/unified1/SPIRV.pdf#subsection.2.4 - SpvSectionBuilder capabilities; - SpvSectionBuilder extensions; - SpvSectionBuilder ext_inst_import; - SpvSectionBuilder entry_points; - SpvSectionBuilder execution_modes; - SpvSectionBuilder debug_string_source; - SpvSectionBuilder debug_names; - SpvSectionBuilder debug_module_processed; - SpvSectionBuilder annotations; - SpvSectionBuilder types_constants; - SpvSectionBuilder fn_decls; - SpvSectionBuilder fn_defs; - - SpvId declare_void_type() { - types_constants.op(spv::Op::OpTypeVoid, 2); - auto id = generate_fresh_id(); - types_constants.ref_id(id); - return id; - } - - void output_word_le(uint32_t word) { - output_->put((word >> 0) & 0xFFu); - output_->put((word >> 8) & 0xFFu); - output_->put((word >> 16) & 0xFFu); - output_->put((word >> 24) & 0xFFu); - } - - void output_section(SpvSectionBuilder& section) { - for (auto& word : section.data_) { - output_word_le(word); - } - } -public: - const SpvId void_type; - - void finish(std::ostream& output) { - output_ = &output; - SpvSectionBuilder memory_model_section; - memory_model_section.op(spv::Op::OpMemoryModel, 3); - memory_model_section.data_.push_back(addressing_model); - memory_model_section.data_.push_back(memory_model); - - output_word_le(spv::MagicNumber); - output_word_le(spv::Version); // TODO: target a specific spirv version - output_word_le(uint32_t(0)); // TODO get a magic number ? - output_word_le(bound); - output_word_le(uint32_t(0)); // instruction schema padding - - output_section(capabilities); - output_section(extensions); - output_section(ext_inst_import); - output_section(memory_model_section); - output_section(entry_points); - output_section(execution_modes); - output_section(debug_string_source); - output_section(debug_names); - output_section(debug_module_processed); - output_section(annotations); - output_section(types_constants); - output_section(fn_decls); - output_section(fn_defs); - } -}; - -SpvId SpvBasicBlockBuilder::generate_fresh_id() { - return file_builder.generate_fresh_id(); -} - CodeGen::CodeGen(thorin::World& world, Cont2Config&, bool debug) : thorin::CodeGen(world, debug) {} void CodeGen::emit(std::ostream& out) { - SpvFileBuilder builder; + builder::SpvFileBuilder builder; builder_ = &builder; builder_->capability(spv::Capability::CapabilityShader); builder_->capability(spv::Capability::CapabilityLinkage); + cleanup_world(world()); + Scope::for_each(world(), [&](const Scope& scope) { emit(scope); }); builder_->finish(out); @@ -429,7 +122,7 @@ void CodeGen::emit(const thorin::Scope& scope) { entry_ = scope.entry(); assert(entry_->is_returning()); - auto fn = SpvFnBuilder { }; + FnBuilder fn; fn.fn_type = convert(entry_->type()); fn.fn_ret_type = get_codom_type(entry_); @@ -438,11 +131,14 @@ void CodeGen::emit(const thorin::Scope& scope) { auto conts = schedule(scope); fn.bbs.reserve(conts.size()); + std::vector bbs; + bbs.reserve(conts.size()); for (auto cont : conts) { if (cont->intrinsic() == Intrinsic::EndScope) continue; - SpvBasicBlockBuilder* bb = &fn.bbs.emplace_back(*builder_); + BasicBlockBuilder* bb = &bbs.emplace_back(BasicBlockBuilder(*builder_)); + fn.bbs.emplace_back(bb); auto [i, b] = fn.bbs_map.emplace(cont, bb); assert(b); @@ -507,7 +203,7 @@ SpvId CodeGen::get_codom_type(const Continuation* fn) { return builder_->declare_struct_type(types); } -void CodeGen::emit_epilogue(Continuation* continuation, SpvBasicBlockBuilder* bb) { +void CodeGen::emit_epilogue(Continuation* continuation, BasicBlockBuilder* bb) { if (continuation->callee() == entry_->ret_param()) { std::vector values; @@ -632,7 +328,7 @@ void CodeGen::emit_epilogue(Continuation* continuation, SpvBasicBlockBuilder* bb } } -SpvId CodeGen::emit(const Def* def, SpvBasicBlockBuilder* bb) { +SpvId CodeGen::emit(const Def* def, BasicBlockBuilder* bb) { if (auto bin = def->isa()) { SpvId lhs = emit(bin->lhs(), bb); SpvId rhs = emit(bin->rhs(), bb); @@ -757,9 +453,12 @@ SpvId CodeGen::emit(const Def* def, SpvBasicBlockBuilder* bb) { return constant; } else if(auto param = def->isa()) { if (auto param_id = current_fn_->params.lookup(param)) { + assert((*param_id).id != 0); return *param_id; } else { - return (*current_fn_->bbs_map[param->continuation()]).phis[param].value; + auto val = (*current_fn_->bbs_map[param->continuation()]).phis[param].value; + assert(val.id != 0); + return val; } } assertf(false, "Incomplete emit(def) definition"); diff --git a/src/thorin/be/spirv/spirv.h b/src/thorin/be/spirv/spirv.h index 8cbc42072..89d4a0451 100644 --- a/src/thorin/be/spirv/spirv.h +++ b/src/thorin/be/spirv/spirv.h @@ -1,16 +1,29 @@ #ifndef THORIN_SPIRV_H #define THORIN_SPIRV_H -#include +#include "thorin/be/spirv/spirv_builder.hpp" #include "thorin/be/backends.h" +#include "thorin/analyses/schedule.h" + namespace thorin::spirv { -struct SpvSectionBuilder; -struct SpvBasicBlockBuilder; -struct SpvFnBuilder; -struct SpvFileBuilder; -struct SpvId { uint32_t id; }; +using SpvId = builder::SpvId; + +struct BasicBlockBuilder : public builder::SpvBasicBlockBuilder { + explicit BasicBlockBuilder(builder::SpvFileBuilder& file_builder) + : builder::SpvBasicBlockBuilder(file_builder) + {} + + std::unordered_map phis; + DefMap args; +}; + +struct FnBuilder : public builder::SpvFnBuilder { + std::unordered_map bbs_map; + ContinuationMap labels; + DefMap params; +}; class CodeGen : public thorin::CodeGen { public: @@ -18,16 +31,19 @@ class CodeGen : public thorin::CodeGen { void emit(std::ostream& stream) override; protected: + void structure_loops(); + void structure_flow(); + SpvId convert(const Type*); void emit(const Scope& scope); - void emit_epilogue(Continuation*, SpvBasicBlockBuilder* bb); - SpvId emit(const Def* def, SpvBasicBlockBuilder* bb); + void emit_epilogue(Continuation*, BasicBlockBuilder* bb); + SpvId emit(const Def* def, BasicBlockBuilder* bb); SpvId get_codom_type(const Continuation* fn); - SpvFileBuilder* builder_ = nullptr; + builder::SpvFileBuilder* builder_ = nullptr; Continuation* entry_ = nullptr; - SpvFnBuilder* current_fn_ = nullptr; + FnBuilder* current_fn_ = nullptr; Scheduler scheduler_; TypeMap types_; DefMap defs_; diff --git a/src/thorin/be/spirv/spirv_builder.hpp b/src/thorin/be/spirv/spirv_builder.hpp new file mode 100644 index 000000000..fb19b4af6 --- /dev/null +++ b/src/thorin/be/spirv/spirv_builder.hpp @@ -0,0 +1,325 @@ +#include + +#include +#include +#include +#include +#include +#include + +namespace thorin::spirv::builder { + +struct SpvId { uint32_t id; }; + +struct SpvSectionBuilder; +struct SpvBasicBlockBuilder; +struct SpvFnBuilder; +struct SpvFileBuilder; + +inline int div_roundup(int a, int b) { + if (a % b == 0) + return a / b; + else + return (a / b) + 1; +} + +struct SpvSectionBuilder { + std::vector data_; + +private: + void output_word(uint32_t word) { + data_.push_back(word); + } +public: + void op(spv::Op op, int ops_size) { + uint32_t lower = op & 0xFFFFu; + uint32_t upper = (ops_size << 16) & 0xFFFF0000u; + output_word(lower | upper); + } + + void ref_id(SpvId id) { + assert(id.id != 0); + output_word(id.id); + } + + void literal_name(std::string_view str) { + int i = 0; + uint32_t cword = 0; + for (char c : str) { + cword = cword | (c & 0xFF) << (i * 8); + i++; + if (i == 4) { + output_word(cword); + cword = 0; + i = 0; + } + } + output_word(cword); + } + + void literal_int(uint32_t i) { + output_word(i); + } +}; + +struct SpvBasicBlockBuilder : public SpvSectionBuilder { + explicit SpvBasicBlockBuilder(SpvFileBuilder& file_builder) + : file_builder(file_builder) + {} + + SpvFileBuilder& file_builder; + + struct Phi { + SpvId type; + SpvId value; + std::vector> preds; + }; + std::vector phis; + SpvId label; + + SpvId composite(SpvId aggregate_t, std::vector& elements) { + op(spv::Op::OpLabel, 3 + elements.size()); + ref_id(aggregate_t); + auto id = generate_fresh_id(); + ref_id(id); + for (auto e : elements) + ref_id(e); + return id; + } + + SpvId binop(spv::Op op_, SpvId result_type, SpvId lhs, SpvId rhs) { + op(op_, 5); + auto id = generate_fresh_id(); + ref_id(result_type); + ref_id(id); + ref_id(lhs); + ref_id(rhs); + return id; + } + + void branch(SpvId target) { + op(spv::Op::OpBranch, 2); + ref_id(target); + } + + void branch_conditional(SpvId condition, SpvId true_target, SpvId false_target) { + op(spv::Op::OpBranchConditional, 4); + ref_id(condition); + ref_id(true_target); + ref_id(false_target); + } + + void return_void() { + op(spv::Op::OpReturn, 1); + } + + void return_value(SpvId value) { + op(spv::Op::OpReturnValue, 2); + ref_id(value); + } + +private: + SpvId generate_fresh_id(); +}; + +struct SpvFnBuilder { +public: + SpvId fn_type; + SpvId fn_ret_type; + std::vector bbs; + + // Contains OpFunctionParams + SpvSectionBuilder header; +}; + +struct SpvFileBuilder { + SpvFileBuilder() + : void_type(declare_void_type()) + {} + + SpvId generate_fresh_id() { return {bound++ }; } + + void name(SpvId id, std::string_view str) { + assert(id.id < bound); + debug_names.op(spv::Op::OpName, 2 + div_roundup(str.size() + 1, 4)); + debug_names.ref_id(id); + debug_names.literal_name(str); + } + + SpvId declare_bool_type() { + types_constants.op(spv::Op::OpTypeBool, 2); + auto id = generate_fresh_id(); + types_constants.ref_id(id); + return id; + } + + SpvId declare_int_type(int width, bool signed_) { + types_constants.op(spv::Op::OpTypeInt, 4); + auto id = generate_fresh_id(); + types_constants.ref_id(id); + types_constants.literal_int(width); + types_constants.literal_int(signed_ ? 1 : 0); + return id; + } + + SpvId declare_float_type(int width) { + types_constants.op(spv::Op::OpTypeFloat, 3); + auto id = generate_fresh_id(); + types_constants.ref_id(id); + types_constants.literal_int(width); + return id; + } + + SpvId declare_fn_type(std::vector& dom, SpvId codom) { + types_constants.op(spv::Op::OpTypeFunction, 3 + dom.size()); + auto id = generate_fresh_id(); + types_constants.ref_id(id); + types_constants.ref_id(codom); + for (auto arg : dom) + types_constants.ref_id(arg); + return id; + } + + SpvId declare_struct_type(std::vector& elements) { + types_constants.op(spv::Op::OpTypeStruct, 2 + elements.size()); + auto id = generate_fresh_id(); + types_constants.ref_id(id); + for (auto arg : elements) + types_constants.ref_id(arg); + return id; + } + + SpvId bool_constant(SpvId type, bool value) { + types_constants.op(value ? spv::Op::OpConstantTrue : spv::Op::OpConstantFalse, 3); + auto id = generate_fresh_id(); + types_constants.ref_id(type); + types_constants.ref_id(id); + return id; + } + + SpvId constant(SpvId type, std::vector&& bit_pattern) { + types_constants.op(spv::Op::OpConstant, 3 + bit_pattern.size()); + auto id = generate_fresh_id(); + types_constants.ref_id(type); + types_constants.ref_id(id); + for (auto arg : bit_pattern) + types_constants.data_.push_back(arg); + return id; + } + + SpvId define_function(SpvFnBuilder& fn_builder) { + fn_defs.op(spv::Op::OpFunction, 5); + fn_defs.ref_id(fn_builder.fn_ret_type); + auto id = generate_fresh_id(); + fn_defs.ref_id(id); + fn_defs.data_.push_back(spv::FunctionControlMaskNone); + fn_defs.ref_id(fn_builder.fn_type); + + // Includes stuff like OpFunctionParameters + for (auto w : fn_builder.header.data_) + fn_defs.data_.push_back(w); + + for (auto& bb : fn_builder.bbs) { + fn_defs.op(spv::Op::OpLabel, 2); + fn_defs.ref_id(bb->label); + + for (auto& phi : bb->phis) { + fn_defs.op(spv::Op::OpPhi, 3 + 2 * phi.preds.size()); + fn_defs.ref_id(phi.type); + fn_defs.ref_id(phi.value); + for (auto& [pred_value, pred_label] : phi.preds) { + fn_defs.ref_id(pred_value); + fn_defs.ref_id(pred_label); + } + } + + for (auto w : bb->data_) + fn_defs.data_.push_back(w); + } + + fn_defs.op(spv::Op::OpFunctionEnd, 1); + return id; + } + + void capability(spv::Capability cap) { + capabilities.op(spv::Op::OpCapability, 2); + capabilities.data_.push_back(cap); + } + + spv::AddressingModel addressing_model = spv::AddressingModel::AddressingModelLogical; + spv::MemoryModel memory_model = spv::MemoryModel::MemoryModelSimple; + +private: + std::ostream* output_ = nullptr; + uint32_t bound = 1; + + // Ordered as per https://www.khronos.org/registry/spir-v/specs/unified1/SPIRV.pdf#subsection.2.4 + SpvSectionBuilder capabilities; + SpvSectionBuilder extensions; + SpvSectionBuilder ext_inst_import; + SpvSectionBuilder entry_points; + SpvSectionBuilder execution_modes; + SpvSectionBuilder debug_string_source; + SpvSectionBuilder debug_names; + SpvSectionBuilder debug_module_processed; + SpvSectionBuilder annotations; + SpvSectionBuilder types_constants; + SpvSectionBuilder fn_decls; + SpvSectionBuilder fn_defs; + + SpvId declare_void_type() { + types_constants.op(spv::Op::OpTypeVoid, 2); + auto id = generate_fresh_id(); + types_constants.ref_id(id); + return id; + } + + void output_word_le(uint32_t word) { + output_->put((word >> 0) & 0xFFu); + output_->put((word >> 8) & 0xFFu); + output_->put((word >> 16) & 0xFFu); + output_->put((word >> 24) & 0xFFu); + } + + void output_section(SpvSectionBuilder& section) { + for (auto& word : section.data_) { + output_word_le(word); + } + } +public: + const SpvId void_type; + + void finish(std::ostream& output) { + output_ = &output; + SpvSectionBuilder memory_model_section; + memory_model_section.op(spv::Op::OpMemoryModel, 3); + memory_model_section.data_.push_back(addressing_model); + memory_model_section.data_.push_back(memory_model); + + output_word_le(spv::MagicNumber); + output_word_le(spv::Version); // TODO: target a specific spirv version + output_word_le(uint32_t(0)); // TODO get a magic number ? + output_word_le(bound); + output_word_le(uint32_t(0)); // instruction schema padding + + output_section(capabilities); + output_section(extensions); + output_section(ext_inst_import); + output_section(memory_model_section); + output_section(entry_points); + output_section(execution_modes); + output_section(debug_string_source); + output_section(debug_names); + output_section(debug_module_processed); + output_section(annotations); + output_section(types_constants); + output_section(fn_decls); + output_section(fn_defs); + } +}; + +inline SpvId SpvBasicBlockBuilder::generate_fresh_id() { + return file_builder.generate_fresh_id(); +} + +} \ No newline at end of file diff --git a/src/thorin/be/spirv/spirv_transform.cpp b/src/thorin/be/spirv/spirv_transform.cpp new file mode 100644 index 000000000..85e7ac21f --- /dev/null +++ b/src/thorin/be/spirv/spirv_transform.cpp @@ -0,0 +1,14 @@ +#include "thorin/be/spirv/spirv.h" +#include "thorin/analyses/scope.h" + +namespace thorin::spirv { + +void CodeGen::structure_loops() { + // TODO +} + +void CodeGen::structure_flow() { + // TODO +} + +} \ No newline at end of file From b8c2c705b76da0e74dfe615e65b065aeec62cd09 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Mon, 15 Mar 2021 15:10:13 +0100 Subject: [PATCH 031/342] (initial stuff (does not work)) --- src/thorin/CMakeLists.txt | 1 + src/thorin/be/spirv/spirv.cpp | 1 + src/thorin/be/spirv/spirv_transform.cpp | 126 +++++++++++++++++++++++- 3 files changed, 127 insertions(+), 1 deletion(-) diff --git a/src/thorin/CMakeLists.txt b/src/thorin/CMakeLists.txt index e38158fac..5f55499a6 100644 --- a/src/thorin/CMakeLists.txt +++ b/src/thorin/CMakeLists.txt @@ -82,6 +82,7 @@ set(THORIN_SOURCES util/symbol.h util/types.h util/utility.h + util/dot_dump.cpp be/backends.cpp be/backends.h be/spirv/spirv_transform.cpp diff --git a/src/thorin/be/spirv/spirv.cpp b/src/thorin/be/spirv/spirv.cpp index 6a3de9306..746c034d7 100644 --- a/src/thorin/be/spirv/spirv.cpp +++ b/src/thorin/be/spirv/spirv.cpp @@ -18,6 +18,7 @@ void CodeGen::emit(std::ostream& out) { builder_->capability(spv::Capability::CapabilityShader); builder_->capability(spv::Capability::CapabilityLinkage); + structure_loops(); cleanup_world(world()); Scope::for_each(world(), [&](const Scope& scope) { emit(scope); }); diff --git a/src/thorin/be/spirv/spirv_transform.cpp b/src/thorin/be/spirv/spirv_transform.cpp index 85e7ac21f..5ceb4185c 100644 --- a/src/thorin/be/spirv/spirv_transform.cpp +++ b/src/thorin/be/spirv/spirv_transform.cpp @@ -1,10 +1,134 @@ +#include #include "thorin/be/spirv/spirv.h" #include "thorin/analyses/scope.h" +#include "thorin/analyses/cfg.h" + +#include namespace thorin::spirv { +using Head = LoopTree::Head; +using Base = LoopTree::Base; +using Leaf = LoopTree::Leaf; + +struct StructuredLoop { + const Head* old_head; + const std::string name; + Continuation* new_header; + Continuation* inner_dispatch; + Continuation* outer_dispatch; + + std::vector inner_destinations; + std::vector outer_destinations; +}; + +inline int get_or_create_destination(std::vector& vec, Continuation* destination) { + if (auto i = std::find(vec.begin(), vec.end(), destination); i != vec.end()) + return i - vec.begin(); + vec.emplace_back(destination); + return vec.size() - 1; +} + +struct ScopeContext { + explicit ScopeContext(const Scope& scope) + : cfa(scope) + {} + + CFA cfa; + ContinuationMap def2loop; + std::unordered_map rewritten_loops; +}; + +/// Visits the forest and fills def2loop +inline void tagContinuations(ScopeContext& ctx, const Base* base, const Head* parent) { + for (int i = 0; i < base->depth(); i++) + printf(" "); + if (auto* head = base->isa()) { + printf("loop: header = "); + } + for (auto& node : base->cf_nodes()) { + printf("%s ", node->continuation()->to_string().c_str()); + } + printf("\n"); + + if (auto* head = base->isa()) { + for (auto& children : head->children()) { + tagContinuations(ctx, &*children, head); + } + } else if(auto* leaf = base->isa()) { + for (auto& node : base->cf_nodes()) { + auto[i, result] = ctx.def2loop.emplace(node->continuation(), parent); + assert(result); + } + } else { + assert(false); + } +} + +inline std::string safe_name(const Head* head) { + if (head == nullptr || head->is_root()) { + return "root"; + } else { + std::stringstream s; + s << "loop_"; + for (auto& node : head->cf_nodes()) { + s << node->continuation()->to_string(); + s << "_"; + } + return s.str(); + } +} + +inline void augmentLoops(World& world, ScopeContext& ctx, const Base* base) { + if (const Head* head = base->isa()) { + StructuredLoop loop { + head, + safe_name(head), + world.continuation({ safe_name(head) + "_header"}), + world.continuation({ safe_name(head) + "_inner_dispatch"}), + world.continuation({ safe_name(head) + "_outer_dispatch"}), + {}, + {}, + }; + + for (auto& header_node : base->cf_nodes()) { + const Head* dest_loop = *ctx.def2loop[header_node->continuation()]; + for (auto& pred : header_node->continuation()->preds()) { + const Head* src_loop = *ctx.def2loop[pred]; + printf("%s -> %s\n", safe_name(src_loop).c_str(), safe_name(dest_loop).c_str()); + + // Backedge ! + if (src_loop == dest_loop) { + get_or_create_destination(loop.inner_destinations, header_node->continuation()); + + // point backedge to loop header + for (int i = 0; i < pred->num_ops(); i++) { + if (pred->op(i) == header_node->continuation()) { + pred->unset_op(i); + pred->set_op(i, loop.new_header); + } + } + } + } + } + + ctx.rewritten_loops.emplace(head, loop); + + for (auto& children : head->children()) { + augmentLoops(world, ctx, &*children); + } + } +} + void CodeGen::structure_loops() { - // TODO + Scope::for_each(world(), [&](const Scope& scope) { + ScopeContext context(scope); + + const LoopTree& looptree = context.cfa.f_cfg().looptree(); + tagContinuations(context, looptree.root(), nullptr); + + augmentLoops(world(), context, looptree.root()); + }); } void CodeGen::structure_flow() { From 3e70c3024ec6b1a80af3f0c8be0640b92a590be3 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Mon, 15 Mar 2021 15:59:23 +0100 Subject: [PATCH 032/342] more nonsense --- src/thorin/be/spirv/spirv_transform.cpp | 50 +++++++++++++++++++++---- 1 file changed, 42 insertions(+), 8 deletions(-) diff --git a/src/thorin/be/spirv/spirv_transform.cpp b/src/thorin/be/spirv/spirv_transform.cpp index 5ceb4185c..62b747768 100644 --- a/src/thorin/be/spirv/spirv_transform.cpp +++ b/src/thorin/be/spirv/spirv_transform.cpp @@ -79,33 +79,67 @@ inline std::string safe_name(const Head* head) { } } +inline const Type* dom_to_tuple(World& world, const thorin::FnType* fn_type) { + std::vector t; + t.resize(fn_type->num_ops()); + for (size_t i = 0; i < fn_type->num_ops(); i++) + t[i] = fn_type->op(i); + return world.tuple_type(t); +} + inline void augmentLoops(World& world, ScopeContext& ctx, const Base* base) { if (const Head* head = base->isa()) { + auto name = safe_name(head); + + const thorin::Type* unified_heads_param; + const thorin::VariantType* header_variant_type = nullptr; + if (base->cf_nodes().size() >= 1) { + header_variant_type = world.variant_type(name, base->cf_nodes().size()); + int i = 0; + for (auto& header_node : base->cf_nodes()) { + header_variant_type->set(i++, dom_to_tuple(world, header_node->continuation()->type())); + } + unified_heads_param = header_variant_type; + //} else if (base->cf_nodes().size() == 1) { + // unified_heads_param = dom_to_tuple(world, base->cf_nodes()[0]->continuation()->type()); + } else { + unified_heads_param = world.unit(); + } + + auto fn_type = world.fn_type( { unified_heads_param } ); + StructuredLoop loop { head, - safe_name(head), - world.continuation({ safe_name(head) + "_header"}), - world.continuation({ safe_name(head) + "_inner_dispatch"}), - world.continuation({ safe_name(head) + "_outer_dispatch"}), + name, + world.continuation(fn_type, { name + "_header"}), + world.continuation({ name + "_inner_dispatch"}), + world.continuation({ name + "_outer_dispatch"}), {}, {}, }; - for (auto& header_node : base->cf_nodes()) { + // Handle internal backedges: re-wire them to go through header + for (size_t header_index = 0; header_index < base->num_cf_nodes(); header_index++) { + auto& header_node = base->cf_nodes()[header_index]; + const Head* dest_loop = *ctx.def2loop[header_node->continuation()]; for (auto& pred : header_node->continuation()->preds()) { const Head* src_loop = *ctx.def2loop[pred]; printf("%s -> %s\n", safe_name(src_loop).c_str(), safe_name(dest_loop).c_str()); - // Backedge ! if (src_loop == dest_loop) { get_or_create_destination(loop.inner_destinations, header_node->continuation()); - // point backedge to loop header for (int i = 0; i < pred->num_ops(); i++) { if (pred->op(i) == header_node->continuation()) { pred->unset_op(i); - pred->set_op(i, loop.new_header); + + // Oops except we can't do that! The new header has a mismatched signature, so we must go through a synthetic wrapper + //pred->set_op(i, loop.new_header); + + auto wrapper = world.continuation(fn_type, {"synthetic_backedge_wrapper"}); + wrapper->jump(loop.new_header, { world.variant(header_variant_type, world.tuple(pred->args()), header_index) }); + pred->set_op(i, wrapper); } } } From 1582a65efbd8c7f87e4e8c0f7355c9257656b89d Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Wed, 17 Mar 2021 10:38:32 +0100 Subject: [PATCH 033/342] proper types for headers/epilogues --- src/thorin/be/spirv/spirv_transform.cpp | 290 +++++++++++++++++++----- 1 file changed, 236 insertions(+), 54 deletions(-) diff --git a/src/thorin/be/spirv/spirv_transform.cpp b/src/thorin/be/spirv/spirv_transform.cpp index 62b747768..8b8373c72 100644 --- a/src/thorin/be/spirv/spirv_transform.cpp +++ b/src/thorin/be/spirv/spirv_transform.cpp @@ -11,23 +11,35 @@ using Head = LoopTree::Head; using Base = LoopTree::Base; using Leaf = LoopTree::Leaf; +struct StructuredLoop; + +// Dispatch targets may dispatch to other dispatching nodes, and we can't actually create those until we know all their destinations, +// because their fn type takes a variant type with a case for each target. So we symbolically refer to these yet-to-be dispatch nodes via their loop +struct DispatchTarget { + bool operator==(const DispatchTarget& rhs) const { + return cont == rhs.cont && + entry == rhs.entry && + exit == rhs.exit; + } + + Continuation* cont = nullptr; + StructuredLoop* entry = nullptr; + StructuredLoop* exit = nullptr; +}; + +// Represents one loop in the loop forest, that we then augment with a new loop header and epilogue, each dispatching +// respectively to nodes inside of the loop, and nodes outside of the loop once we break out struct StructuredLoop { - const Head* old_head; + const Head* parent_head; + const Head* head; const std::string name; - Continuation* new_header; - Continuation* inner_dispatch; - Continuation* outer_dispatch; - std::vector inner_destinations; - std::vector outer_destinations; -}; + std::vector inner_destinations = {}; + std::vector outer_destinations = {}; -inline int get_or_create_destination(std::vector& vec, Continuation* destination) { - if (auto i = std::find(vec.begin(), vec.end(), destination); i != vec.end()) - return i - vec.begin(); - vec.emplace_back(destination); - return vec.size() - 1; -} + Continuation* new_header = nullptr; + Continuation* new_epilogue = nullptr; +}; struct ScopeContext { explicit ScopeContext(const Scope& scope) @@ -39,8 +51,22 @@ struct ScopeContext { std::unordered_map rewritten_loops; }; +inline std::string safe_name(const Head* head) { + if (head == nullptr || head->is_root()) { + return "root"; + } else { + std::stringstream s; + s << "loop_"; + for (auto& node : head->cf_nodes()) { + s << node->continuation()->to_string(); + s << "_"; + } + return s.str(); + } +} + /// Visits the forest and fills def2loop -inline void tagContinuations(ScopeContext& ctx, const Base* base, const Head* parent) { +inline void tag_continuations(ScopeContext& ctx, const Base* base, const Head* parent) { for (int i = 0; i < base->depth(); i++) printf(" "); if (auto* head = base->isa()) { @@ -51,10 +77,18 @@ inline void tagContinuations(ScopeContext& ctx, const Base* base, const Head* pa } printf("\n"); - if (auto* head = base->isa()) { + if (const Head* head = base->isa()) { + auto name = safe_name(head); + for (auto& children : head->children()) { - tagContinuations(ctx, &*children, head); + tag_continuations(ctx, &*children, head); } + + if (parent != nullptr) { + StructuredLoop loop{parent, head, name}; + ctx.rewritten_loops.emplace(head, loop); + } + } else if(auto* leaf = base->isa()) { for (auto& node : base->cf_nodes()) { auto[i, result] = ctx.def2loop.emplace(node->continuation(), parent); @@ -65,17 +99,117 @@ inline void tagContinuations(ScopeContext& ctx, const Base* base, const Head* pa } } -inline std::string safe_name(const Head* head) { - if (head == nullptr || head->is_root()) { - return "root"; +inline int record_destination(std::vector& vec, DispatchTarget dest) { + auto i = std::find(vec.begin(), vec.end(), dest); + if (i == vec.end()) { + vec.emplace_back(dest); + return vec.size() - 1; + } else return i - vec.begin(); +} + +inline std::vector&& get_path(ScopeContext& ctx, const Head* head) { + std::vector path; + do { + if (head != nullptr) { + auto* loop = &ctx.rewritten_loops[head]; + path.emplace(path.begin(), loop); + head = loop->parent_head; + } else { + path.emplace(path.begin(), nullptr); + head = nullptr; + } + } while (head != nullptr); + return std::move(path); +} + +inline void collect_dispatch_targets(World& world, ScopeContext& ctx, const Base* base, const Head* parent) { + if (const Head* head = base->isa()) { + for (auto& children : head->children()) { + collect_dispatch_targets(world, ctx, &*children, head); + } } else { - std::stringstream s; - s << "loop_"; - for (auto& node : head->cf_nodes()) { - s << node->continuation()->to_string(); - s << "_"; + const Leaf* leaf = base->as(); + auto cont = leaf->cf_node()->continuation(); + for (size_t i = 0; i < cont->num_ops(); i++) { + auto def = cont->op(i); + if (auto dest = def->isa_continuation()) { + const Head* source_loop = *ctx.def2loop[cont]; + const Head* dest_loop = *ctx.def2loop[cont]; + + if (source_loop != dest_loop) { + // We found a non-local jump + + auto source_path = get_path(ctx, source_loop); + auto dest_path = get_path(ctx, dest_loop); + size_t bi = 0; + while (bi < std::min(source_path.size(), dest_path.size())) { + if (source_path[bi] == dest_path[bi]) + bi++; + else break; + } + + // The path is made out of a sequence of loops to break out of, and a sequence of loops to jump into + // these two sequences cannot be both empty (that wouldn't be a non-local jump then!) + std::vector leave; + std::vector enter; + for (size_t j = source_path.size() - 1; j >= bi; j--) + leave.emplace_back(source_path[j]); + for (size_t j = bi; j < dest_path.size(); j++) + enter.emplace_back(dest_path[j]); + + // 0 = this is the first step of the path + // 1 = last step was to break out of a loop + // 2 = last step was to enter a loop + int last = 0; + StructuredLoop* prev; + + auto record_step = [&](StructuredLoop* loop, DispatchTarget destination) { + if (last == 0) { + // nothing to do, this node isn't a dispatching one + } else { + if (last == 1) + record_destination(prev->outer_destinations, destination); + else + record_destination(prev->inner_destinations, destination); + } + }; + + for (auto loop : leave) { + DispatchTarget destination; + destination.exit = loop; + + record_step(loop, destination); + last = 1; + prev = loop; + } + for (auto loop : enter) { + DispatchTarget destination; + destination.entry = loop; + + record_step(loop, destination); + last = 2; + prev = loop; + } + + assert(last != 0); + DispatchTarget destination; + destination.cont = dest; + record_step(prev, destination); + } else if (source_loop == dest_loop && source_loop != nullptr) { + for (auto head : source_loop->cf_nodes()) { + if (head->continuation() == dest) { + // We found a backedge + auto loop = ctx.rewritten_loops[source_loop]; + + DispatchTarget destination; + destination.cont = dest; + record_destination(loop.inner_destinations, destination); + break; + } + } + } + } } - return s.str(); } } @@ -87,37 +221,83 @@ inline const Type* dom_to_tuple(World& world, const thorin::FnType* fn_type) { return world.tuple_type(t); } -inline void augmentLoops(World& world, ScopeContext& ctx, const Base* base) { +inline void create_headers(World& world, ScopeContext& ctx, const Base* base) { + if (const Head* head = base->isa()) { + for (auto& children : head->children()) { + create_headers(world, ctx, &*children); + } + + if (head->num_cf_nodes() == 0) + return; + StructuredLoop& loop = ctx.rewritten_loops[head]; + + // here, parent headers need to know what they're jumping *into* + std::vector dest_types; + for (auto& target : loop.inner_destinations) { + const thorin::FnType* target_type; + if (target.cont != nullptr) { + target_type = target.cont->type(); + } else if (target.entry != nullptr) { + assert(target.entry->new_header != nullptr); + target_type = target.entry->new_header->type(); + } else { + assert(false && "Header dispatches may not exit loops"); + } + dest_types.emplace_back(dom_to_tuple(world, target_type)); + } + auto variant_type = world.variant_type(loop.name + "_param", dest_types.size()); + for (size_t i = 0; i < dest_types.size(); i++) + variant_type->set(i, dest_types[i]); + auto fn_type = world.fn_type( { variant_type } ); + loop.new_header = world.continuation(fn_type, { loop.name + "_new_header"}); + } +} + +inline void create_epilogues(World& world, ScopeContext& ctx, const Base* base) { if (const Head* head = base->isa()) { + StructuredLoop& loop = ctx.rewritten_loops[head]; + + if (head->num_cf_nodes() > 0) { + // here, children epilogues need to know what they're jumping *out to* + std::vector dest_types; + for (auto& target : loop.inner_destinations) { + const thorin::FnType* target_type; + if (target.cont != nullptr) { + target_type = target.cont->type(); + } else if (target.entry != nullptr) { + assert(target.entry->new_header != nullptr); + target_type = target.entry->new_header->type(); + } else { + assert(target.exit != nullptr); + assert(target.exit->new_epilogue != nullptr); + target_type = target.exit->new_epilogue->type(); + } + dest_types.emplace_back(dom_to_tuple(world, target_type)); + } + auto variant_type = world.variant_type(loop.name + "_param", dest_types.size()); + for (size_t i = 0; i < dest_types.size(); i++) + variant_type->set(i, dest_types[i]); + auto fn_type = world.fn_type({variant_type}); + loop.new_header = world.continuation(fn_type, {loop.name + "_new_epilogue"}); + } + + for (auto& children : head->children()) { + create_epilogues(world, ctx, &*children); + } + } +} + +// This creates the header/epilogue nodes for the loops +inline void augment_loops(World& world, ScopeContext& ctx, const Base* base) { + if (const Head* head = base->isa()) { + auto& loop = ctx.rewritten_loops[head]; auto name = safe_name(head); const thorin::Type* unified_heads_param; const thorin::VariantType* header_variant_type = nullptr; - if (base->cf_nodes().size() >= 1) { - header_variant_type = world.variant_type(name, base->cf_nodes().size()); - int i = 0; - for (auto& header_node : base->cf_nodes()) { - header_variant_type->set(i++, dom_to_tuple(world, header_node->continuation()->type())); - } - unified_heads_param = header_variant_type; - //} else if (base->cf_nodes().size() == 1) { - // unified_heads_param = dom_to_tuple(world, base->cf_nodes()[0]->continuation()->type()); - } else { - unified_heads_param = world.unit(); - } auto fn_type = world.fn_type( { unified_heads_param } ); - StructuredLoop loop { - head, - name, - world.continuation(fn_type, { name + "_header"}), - world.continuation({ name + "_inner_dispatch"}), - world.continuation({ name + "_outer_dispatch"}), - {}, - {}, - }; - // Handle internal backedges: re-wire them to go through header for (size_t header_index = 0; header_index < base->num_cf_nodes(); header_index++) { auto& header_node = base->cf_nodes()[header_index]; @@ -128,7 +308,7 @@ inline void augmentLoops(World& world, ScopeContext& ctx, const Base* base) { printf("%s -> %s\n", safe_name(src_loop).c_str(), safe_name(dest_loop).c_str()); if (src_loop == dest_loop) { - get_or_create_destination(loop.inner_destinations, header_node->continuation()); + // get_or_create_destination(loop.inner_destinations, header_node->continuation()); for (int i = 0; i < pred->num_ops(); i++) { if (pred->op(i) == header_node->continuation()) { @@ -146,10 +326,8 @@ inline void augmentLoops(World& world, ScopeContext& ctx, const Base* base) { } } - ctx.rewritten_loops.emplace(head, loop); - for (auto& children : head->children()) { - augmentLoops(world, ctx, &*children); + augment_loops(world, ctx, &*children); } } } @@ -159,9 +337,13 @@ void CodeGen::structure_loops() { ScopeContext context(scope); const LoopTree& looptree = context.cfa.f_cfg().looptree(); - tagContinuations(context, looptree.root(), nullptr); + tag_continuations(context, looptree.root(), nullptr); + collect_dispatch_targets(world(), context, looptree.root(), nullptr); + + create_headers(world(), context, looptree.root()); + create_epilogues(world(), context, looptree.root()); - augmentLoops(world(), context, looptree.root()); + augment_loops(world(), context, looptree.root()); }); } From 739a23fb2680bf5d7fe4639c5227d33efeef9ce9 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Wed, 17 Mar 2021 16:31:14 +0100 Subject: [PATCH 034/342] handle NLJs --- src/thorin/be/spirv/spirv_transform.cpp | 234 ++++++++++++++++++------ 1 file changed, 178 insertions(+), 56 deletions(-) diff --git a/src/thorin/be/spirv/spirv_transform.cpp b/src/thorin/be/spirv/spirv_transform.cpp index 8b8373c72..a6687fb81 100644 --- a/src/thorin/be/spirv/spirv_transform.cpp +++ b/src/thorin/be/spirv/spirv_transform.cpp @@ -27,6 +27,19 @@ struct DispatchTarget { StructuredLoop* exit = nullptr; }; +struct RewireMe { + RewireMe(Continuation* cont, int op) : cont(cont), op(op) {} + Continuation* cont; + int op; + + Continuation* backedge = nullptr; + struct { + std::vector exits; + std::vector enters; + Continuation* final_destination = nullptr; + } non_local_jump; +}; + // Represents one loop in the loop forest, that we then augment with a new loop header and epilogue, each dispatching // respectively to nodes inside of the loop, and nodes outside of the loop once we break out struct StructuredLoop { @@ -39,6 +52,8 @@ struct StructuredLoop { Continuation* new_header = nullptr; Continuation* new_epilogue = nullptr; + + std::vector rewire; }; struct ScopeContext { @@ -84,10 +99,8 @@ inline void tag_continuations(ScopeContext& ctx, const Base* base, const Head* p tag_continuations(ctx, &*children, head); } - if (parent != nullptr) { - StructuredLoop loop{parent, head, name}; - ctx.rewritten_loops.emplace(head, loop); - } + StructuredLoop loop{parent, head, name}; + ctx.rewritten_loops.emplace(head, loop); } else if(auto* leaf = base->isa()) { for (auto& node : base->cf_nodes()) { @@ -107,19 +120,23 @@ inline int record_destination(std::vector& vec, DispatchTarget d } else return i - vec.begin(); } -inline std::vector&& get_path(ScopeContext& ctx, const Head* head) { - std::vector path; - do { - if (head != nullptr) { - auto* loop = &ctx.rewritten_loops[head]; - path.emplace(path.begin(), loop); - head = loop->parent_head; - } else { - path.emplace(path.begin(), nullptr); - head = nullptr; - } - } while (head != nullptr); - return std::move(path); +inline int index_of_destination(std::vector& vec, DispatchTarget dest) { + auto i = std::find(vec.begin(), vec.end(), dest); + if (i == vec.end()) { + assert(false && "Missing destination"); + } else return i - vec.begin(); +} + +inline std::vector get_path(ScopeContext& ctx, const Head* head) { + std::vector path = {}; + assert(head != nullptr); + while (head != nullptr) { + auto* loop = &ctx.rewritten_loops[head]; + assert(loop != nullptr); + path.emplace(path.begin(), loop); + head = loop->parent_head; + } + return path; } inline void collect_dispatch_targets(World& world, ScopeContext& ctx, const Base* base, const Head* parent) { @@ -134,14 +151,22 @@ inline void collect_dispatch_targets(World& world, ScopeContext& ctx, const Base auto def = cont->op(i); if (auto dest = def->isa_continuation()) { const Head* source_loop = *ctx.def2loop[cont]; - const Head* dest_loop = *ctx.def2loop[cont]; + + if (dest->intrinsic() == Intrinsic::Branch) { + continue; + } + + assert(ctx.def2loop.find(dest) != ctx.def2loop.end()); + const Head* dest_loop = *ctx.def2loop[dest]; if (source_loop != dest_loop) { // We found a non-local jump + assert(ctx.rewritten_loops.find(source_loop) != ctx.rewritten_loops.end()); + auto& loop = ctx.rewritten_loops[source_loop]; - auto source_path = get_path(ctx, source_loop); - auto dest_path = get_path(ctx, dest_loop); - size_t bi = 0; + std::vector source_path = get_path(ctx, source_loop); + std::vector dest_path = get_path(ctx, dest_loop); + int bi = 0; while (bi < std::min(source_path.size(), dest_path.size())) { if (source_path[bi] == dest_path[bi]) bi++; @@ -152,9 +177,9 @@ inline void collect_dispatch_targets(World& world, ScopeContext& ctx, const Base // these two sequences cannot be both empty (that wouldn't be a non-local jump then!) std::vector leave; std::vector enter; - for (size_t j = source_path.size() - 1; j >= bi; j--) + for (int j = source_path.size() - 1; j >= bi; j--) leave.emplace_back(source_path[j]); - for (size_t j = bi; j < dest_path.size(); j++) + for (int j = bi; j < dest_path.size(); j++) enter.emplace_back(dest_path[j]); // 0 = this is the first step of the path @@ -181,6 +206,7 @@ inline void collect_dispatch_targets(World& world, ScopeContext& ctx, const Base record_step(loop, destination); last = 1; prev = loop; + assert(prev != nullptr); } for (auto loop : enter) { DispatchTarget destination; @@ -189,21 +215,37 @@ inline void collect_dispatch_targets(World& world, ScopeContext& ctx, const Base record_step(loop, destination); last = 2; prev = loop; + assert(prev != nullptr); } assert(last != 0); DispatchTarget destination; destination.cont = dest; record_step(prev, destination); + + RewireMe rewire(cont, i); + rewire.non_local_jump = { + std::move(leave), + std::move(enter), + dest + }; + loop.rewire.emplace_back(rewire); + printf("nlj %s %d!\n", loop.name.c_str(), loop.rewire.size()); } else if (source_loop == dest_loop && source_loop != nullptr) { - for (auto head : source_loop->cf_nodes()) { + for (auto& head : source_loop->cf_nodes()) { if (head->continuation() == dest) { // We found a backedge - auto loop = ctx.rewritten_loops[source_loop]; + assert(ctx.rewritten_loops.find(source_loop) != ctx.rewritten_loops.end()); + auto& loop = ctx.rewritten_loops[source_loop]; DispatchTarget destination; destination.cont = dest; record_destination(loop.inner_destinations, destination); + + RewireMe rewire(cont, i); + rewire.backedge = head->continuation(); + loop.rewire.emplace_back(rewire); + printf("backedge %s %d!\n", loop.name.c_str(), loop.rewire.size()); break; } } @@ -221,6 +263,14 @@ inline const Type* dom_to_tuple(World& world, const thorin::FnType* fn_type) { return world.tuple_type(t); } +inline const Def* tuple_from_params(World& world, const ArrayRef params) { + std::vector t; + t.resize(params.size()); + for (size_t i = 0; i < params.size(); i++) + t[i] = params[i]; + return world.tuple(t); +} + inline void create_headers(World& world, ScopeContext& ctx, const Base* base) { if (const Head* head = base->isa()) { for (auto& children : head->children()) { @@ -260,7 +310,7 @@ inline void create_epilogues(World& world, ScopeContext& ctx, const Base* base) if (head->num_cf_nodes() > 0) { // here, children epilogues need to know what they're jumping *out to* std::vector dest_types; - for (auto& target : loop.inner_destinations) { + for (auto& target : loop.outer_destinations) { const thorin::FnType* target_type; if (target.cont != nullptr) { target_type = target.cont->type(); @@ -278,7 +328,7 @@ inline void create_epilogues(World& world, ScopeContext& ctx, const Base* base) for (size_t i = 0; i < dest_types.size(); i++) variant_type->set(i, dest_types[i]); auto fn_type = world.fn_type({variant_type}); - loop.new_header = world.continuation(fn_type, {loop.name + "_new_epilogue"}); + loop.new_epilogue = world.continuation(fn_type, {loop.name + "_new_epilogue"}); } for (auto& children : head->children()) { @@ -287,47 +337,118 @@ inline void create_epilogues(World& world, ScopeContext& ctx, const Base* base) } } -// This creates the header/epilogue nodes for the loops -inline void augment_loops(World& world, ScopeContext& ctx, const Base* base) { +inline void rewire_loops(World& world, ScopeContext& ctx, const Base* base) { if (const Head* head = base->isa()) { + assert(ctx.rewritten_loops.find(head) != ctx.rewritten_loops.end()); auto& loop = ctx.rewritten_loops[head]; - auto name = safe_name(head); - const thorin::Type* unified_heads_param; - const thorin::VariantType* header_variant_type = nullptr; + for (auto& children : head->children()) { + rewire_loops(world, ctx, &*children); + } - auto fn_type = world.fn_type( { unified_heads_param } ); + printf("rewires %s %d!\n", loop.name.c_str(), loop.rewire.size()); + for (auto& rewire : loop.rewire) { + printf("rewire!\n"); + if (rewire.backedge != nullptr) { + printf("handling BE!\n"); + DispatchTarget destination; + destination.cont = rewire.backedge; + auto variant_index = index_of_destination(loop.inner_destinations, destination); - // Handle internal backedges: re-wire them to go through header - for (size_t header_index = 0; header_index < base->num_cf_nodes(); header_index++) { - auto& header_node = base->cf_nodes()[header_index]; + auto old_fn_type = rewire.backedge->type(); + auto wrapper = world.continuation(old_fn_type, {"synthetic_backedge_wrapper"}); - const Head* dest_loop = *ctx.def2loop[header_node->continuation()]; - for (auto& pred : header_node->continuation()->preds()) { - const Head* src_loop = *ctx.def2loop[pred]; - printf("%s -> %s\n", safe_name(src_loop).c_str(), safe_name(dest_loop).c_str()); + auto header_variant_type = loop.new_header->type()->op(0)->as(); + wrapper->jump(loop.new_header, { world.variant(header_variant_type, tuple_from_params(world, wrapper->params()), variant_index) }); - if (src_loop == dest_loop) { - // get_or_create_destination(loop.inner_destinations, header_node->continuation()); + rewire.cont->unset_op(rewire.op); + rewire.cont->set_op(rewire.op, wrapper); + } else { + printf("handling NLJ!\n"); - for (int i = 0; i < pred->num_ops(); i++) { - if (pred->op(i) == header_node->continuation()) { - pred->unset_op(i); + auto& nlj = rewire.non_local_jump; + auto old_fn_type = nlj.final_destination->type(); + auto wrapper = world.continuation(old_fn_type, {"synthetic_nlj_wrapper"}); - // Oops except we can't do that! The new header has a mismatched signature, so we must go through a synthetic wrapper - //pred->set_op(i, loop.new_header); + const Def* argument = tuple_from_params(world, wrapper->params()); + Continuation* first_jump = nullptr; - auto wrapper = world.continuation(fn_type, {"synthetic_backedge_wrapper"}); - wrapper->jump(loop.new_header, { world.variant(header_variant_type, world.tuple(pred->args()), header_index) }); - pred->set_op(i, wrapper); - } + DispatchTarget destination; + destination.cont = nlj.final_destination; + + for (int i = nlj.enters.size() - 1; i >= 0; i--) { + StructuredLoop* loop_to_enter = nlj.enters[i]; + + auto variant_index = index_of_destination(loop_to_enter->inner_destinations, destination); + auto header_variant_type = loop_to_enter->new_header->type()->op(0)->as(); + argument = world.variant(header_variant_type, argument, variant_index); + + first_jump = loop_to_enter->new_header; + destination = {}; + destination.entry = loop_to_enter; + } + + for (int i = nlj.exits.size() - 1; i >= 0; i--) { + StructuredLoop* loop_to_exit = nlj.exits[i]; + + auto variant_index = index_of_destination(loop_to_exit->outer_destinations, destination); + auto header_variant_type = loop_to_exit->new_epilogue->type()->op(0)->as(); + argument = world.variant(header_variant_type, argument, variant_index); + + first_jump = loop_to_exit->new_epilogue; + destination = {}; + destination.exit = loop_to_exit; + } + + assert(first_jump != nullptr); + wrapper->jump(first_jump, { argument }); + + rewire.cont->unset_op(rewire.op); + rewire.cont->set_op(rewire.op, wrapper); + + // --------------------------------------------------------------------------------------- + + /*// 0 = this is the first step of the path + // 1 = last step was to break out of a loop + // 2 = last step was to enter a loop + int last = 0; + StructuredLoop* prev; + + auto record_step = [&](StructuredLoop* loop, DispatchTarget destination) { + if (last == 0) { + // nothing to do, this node isn't a dispatching one + } else { + if (last == 1) + record_destination(prev->outer_destinations, destination); + else + record_destination(prev->inner_destinations, destination); } + }; + + for (auto loop : leave) { + DispatchTarget destination; + destination.exit = loop; + + record_step(loop, destination); + last = 1; + prev = loop; + assert(prev != nullptr); } - } - } + for (auto loop : enter) { + DispatchTarget destination; + destination.entry = loop; - for (auto& children : head->children()) { - augment_loops(world, ctx, &*children); + record_step(loop, destination); + last = 2; + prev = loop; + assert(prev != nullptr); + } + + assert(last != 0); + DispatchTarget destination; + destination.cont = dest; + record_step(prev, destination);*/ + } } } } @@ -343,7 +464,8 @@ void CodeGen::structure_loops() { create_headers(world(), context, looptree.root()); create_epilogues(world(), context, looptree.root()); - augment_loops(world(), context, looptree.root()); + rewire_loops(world(), context, looptree.root()); + printf("done\n"); }); } From cbdfe3ac5209c1e07915b156bb27c1b9615535ba Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Thu, 18 Mar 2021 13:06:51 +0100 Subject: [PATCH 035/342] use intrinsics --- src/thorin/be/spirv/spirv_transform.cpp | 95 +++++++++++-------------- src/thorin/continuation.cpp | 25 +++++++ src/thorin/continuation.h | 6 ++ 3 files changed, 72 insertions(+), 54 deletions(-) diff --git a/src/thorin/be/spirv/spirv_transform.cpp b/src/thorin/be/spirv/spirv_transform.cpp index a6687fb81..5be442d66 100644 --- a/src/thorin/be/spirv/spirv_transform.cpp +++ b/src/thorin/be/spirv/spirv_transform.cpp @@ -17,9 +17,7 @@ struct StructuredLoop; // because their fn type takes a variant type with a case for each target. So we symbolically refer to these yet-to-be dispatch nodes via their loop struct DispatchTarget { bool operator==(const DispatchTarget& rhs) const { - return cont == rhs.cont && - entry == rhs.entry && - exit == rhs.exit; + return cont == rhs.cont &&entry == rhs.entry &&exit == rhs.exit; } Continuation* cont = nullptr; @@ -50,8 +48,14 @@ struct StructuredLoop { std::vector inner_destinations = {}; std::vector outer_destinations = {}; + // Created to serve as codegen helpers Continuation* new_header = nullptr; Continuation* new_epilogue = nullptr; + Continuation* new_continue = nullptr; + + // Same as the inner/outer destinations, but entry/exits instead now point to the corresponding header/epilogue nodes + std::vector header_destination_conts; + std::vector epilogue_destination_conts; std::vector rewire; }; @@ -284,15 +288,17 @@ inline void create_headers(World& world, ScopeContext& ctx, const Base* base) { // here, parent headers need to know what they're jumping *into* std::vector dest_types; for (auto& target : loop.inner_destinations) { - const thorin::FnType* target_type; + const thorin::Continuation* target_cont; if (target.cont != nullptr) { - target_type = target.cont->type(); + target_cont = target.cont; } else if (target.entry != nullptr) { assert(target.entry->new_header != nullptr); - target_type = target.entry->new_header->type(); + target_cont = target.entry->new_header; } else { assert(false && "Header dispatches may not exit loops"); } + loop.header_destination_conts.push_back(target_cont); + const thorin::FnType* target_type = target_cont->type(); dest_types.emplace_back(dom_to_tuple(world, target_type)); } auto variant_type = world.variant_type(loop.name + "_param", dest_types.size()); @@ -300,6 +306,7 @@ inline void create_headers(World& world, ScopeContext& ctx, const Base* base) { variant_type->set(i, dest_types[i]); auto fn_type = world.fn_type( { variant_type } ); loop.new_header = world.continuation(fn_type, { loop.name + "_new_header"}); + loop.new_continue = world.continuation(fn_type, { loop.name + "_new_continue"}); } } @@ -311,17 +318,19 @@ inline void create_epilogues(World& world, ScopeContext& ctx, const Base* base) // here, children epilogues need to know what they're jumping *out to* std::vector dest_types; for (auto& target : loop.outer_destinations) { - const thorin::FnType* target_type; + const thorin::Continuation* target_cont; if (target.cont != nullptr) { - target_type = target.cont->type(); + target_cont = target.cont; } else if (target.entry != nullptr) { assert(target.entry->new_header != nullptr); - target_type = target.entry->new_header->type(); + target_cont = target.entry->new_header; } else { assert(target.exit != nullptr); assert(target.exit->new_epilogue != nullptr); - target_type = target.exit->new_epilogue->type(); + target_cont = target.exit->new_epilogue; } + loop.epilogue_destination_conts.push_back(target_cont); + const thorin::FnType* target_type = target_cont->type(); dest_types.emplace_back(dom_to_tuple(world, target_type)); } auto variant_type = world.variant_type(loop.name + "_param", dest_types.size()); @@ -337,11 +346,23 @@ inline void create_epilogues(World& world, ScopeContext& ctx, const Base* base) } } +// Finishes loop headers & epilogues, and re-wires backedges and non-local jumps to go through structured CF intrinsics inline void rewire_loops(World& world, ScopeContext& ctx, const Base* base) { if (const Head* head = base->isa()) { assert(ctx.rewritten_loops.find(head) != ctx.rewritten_loops.end()); auto& loop = ctx.rewritten_loops[head]; + if (head->num_cf_nodes() > 0) { + loop.new_epilogue->structured_loop_epilogue(loop.new_header, loop.epilogue_destination_conts); + loop.new_continue->structured_loop_continue(loop.new_header); + loop.new_header->structured_loop_header(loop.new_epilogue, loop.new_continue, loop.header_destination_conts); + printf("Loop %s!\n", loop.name.c_str()); + for (auto c : loop.header_destination_conts) + printf(" header target: %s!\n", c->unique_name().c_str()); + for (auto c : loop.epilogue_destination_conts) + printf(" epilogue target: %s!\n", c->unique_name().c_str()); + } + for (auto& children : head->children()) { rewire_loops(world, ctx, &*children); } @@ -359,7 +380,7 @@ inline void rewire_loops(World& world, ScopeContext& ctx, const Base* base) { auto wrapper = world.continuation(old_fn_type, {"synthetic_backedge_wrapper"}); auto header_variant_type = loop.new_header->type()->op(0)->as(); - wrapper->jump(loop.new_header, { world.variant(header_variant_type, tuple_from_params(world, wrapper->params()), variant_index) }); + wrapper->jump(loop.new_continue, { world.variant(header_variant_type, tuple_from_params(world, wrapper->params()), variant_index) }); rewire.cont->unset_op(rewire.op); rewire.cont->set_op(rewire.op, wrapper); @@ -367,9 +388,18 @@ inline void rewire_loops(World& world, ScopeContext& ctx, const Base* base) { printf("handling NLJ!\n"); auto& nlj = rewire.non_local_jump; + auto old_fn_type = nlj.final_destination->type(); auto wrapper = world.continuation(old_fn_type, {"synthetic_nlj_wrapper"}); + printf("nlj = %s\n", wrapper->unique_name().c_str()); + printf("src = %s\n", rewire.cont->name().c_str()); + for (auto exit : nlj.exits) + printf("exit = %s\n", exit->name.c_str()); + for (auto enter : nlj.enters) + printf("enter = %s\n", enter->name.c_str()); + printf("dst = %s\n", nlj.final_destination->name().c_str()); + const Def* argument = tuple_from_params(world, wrapper->params()); Continuation* first_jump = nullptr; @@ -405,49 +435,6 @@ inline void rewire_loops(World& world, ScopeContext& ctx, const Base* base) { rewire.cont->unset_op(rewire.op); rewire.cont->set_op(rewire.op, wrapper); - - // --------------------------------------------------------------------------------------- - - /*// 0 = this is the first step of the path - // 1 = last step was to break out of a loop - // 2 = last step was to enter a loop - int last = 0; - StructuredLoop* prev; - - auto record_step = [&](StructuredLoop* loop, DispatchTarget destination) { - if (last == 0) { - // nothing to do, this node isn't a dispatching one - } else { - if (last == 1) - record_destination(prev->outer_destinations, destination); - else - record_destination(prev->inner_destinations, destination); - } - }; - - for (auto loop : leave) { - DispatchTarget destination; - destination.exit = loop; - - record_step(loop, destination); - last = 1; - prev = loop; - assert(prev != nullptr); - } - for (auto loop : enter) { - DispatchTarget destination; - destination.entry = loop; - - record_step(loop, destination); - last = 2; - prev = loop; - assert(prev != nullptr); - } - - assert(last != 0); - DispatchTarget destination; - destination.cont = dest; - record_step(prev, destination);*/ } } } diff --git a/src/thorin/continuation.cpp b/src/thorin/continuation.cpp index 75f298991..b52e5535e 100644 --- a/src/thorin/continuation.cpp +++ b/src/thorin/continuation.cpp @@ -252,6 +252,31 @@ void Continuation::match(const Def* val, Continuation* otherwise, Defs patterns, return jump(world().match(val->type(), patterns.size()), args, dbg); } +void Continuation::structured_loop_epilogue(const Continuation* loop_header, ArrayRef targets) { + attributes_.intrinsic = Intrinsic::StructuredLoopMerge; + resize(1 + targets.size()); + set_op(0, loop_header); + size_t x = 1; + for (auto target : targets) + set_op(x++, target); +} + +void Continuation::structured_loop_continue(const Continuation* loop_header) { + attributes_.intrinsic = Intrinsic::StructuredLoopContinue; + resize(1); + set_op(0, loop_header); +} + +void Continuation::structured_loop_header(const Continuation* loop_epilogue, const Continuation* loop_continue, ArrayRef targets) { + attributes_.intrinsic = Intrinsic::StructuredLoopHeader; + resize(2 + targets.size()); + set_op(0, loop_epilogue); + set_op(1, loop_continue); + size_t x = 2; + for (auto target : targets) + set_op(x++, target); +} + void jump_to_dropped_call(Continuation* src, Continuation* dst, const Call& call) { std::vector nargs; for (size_t i = 0, e = src->num_args(); i != e; ++i) { diff --git a/src/thorin/continuation.h b/src/thorin/continuation.h index bff19a11e..f7e481da7 100644 --- a/src/thorin/continuation.h +++ b/src/thorin/continuation.h @@ -84,6 +84,9 @@ enum class Intrinsic : uint8_t { Pipeline, ///< Intrinsic loop-pipelining-HLS-Backend Branch, ///< branch(cond, T, F). Match, ///< match(val, otherwise, (case1, cont1), (case2, cont2), ...) + StructuredLoopHeader, ///< A header for a structured loop, + StructuredLoopMerge, ///< A merge block for a structured loop, + StructuredLoopContinue, ///< A continue block in a structured loop, PeInfo, ///< Partial evaluation debug info. EndScope ///< Dummy function which marks the end of a @p Scope. }; @@ -154,6 +157,9 @@ class Continuation : public Def { void jump(const Def* callee, Defs args, Debug dbg = {}); void branch(const Def* cond, const Def* t, const Def* f, Debug dbg = {}); void match(const Def* val, Continuation* otherwise, Defs patterns, ArrayRef continuations, Debug dbg = {}); + void structured_loop_epilogue(const Continuation* loop_header, ArrayRef targets); + void structured_loop_continue(const Continuation* loop_header); + void structured_loop_header(const Continuation* loop_epilogue, const Continuation* loop_continue, ArrayRef targets); void verify() const { #if THORIN_ENABLE_CHECKS auto c = callee_fn_type(); From 76970827fe47c2859a22bbf71b1a8ea5dfea7def Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Thu, 18 Mar 2021 16:07:58 +0100 Subject: [PATCH 036/342] more intrinsics --- src/thorin/be/spirv/spirv.cpp | 2 +- src/thorin/be/spirv/spirv_transform.cpp | 8 +++++++- src/thorin/continuation.cpp | 8 ++++---- src/thorin/continuation.h | 14 +++++++++----- 4 files changed, 21 insertions(+), 11 deletions(-) diff --git a/src/thorin/be/spirv/spirv.cpp b/src/thorin/be/spirv/spirv.cpp index 746c034d7..d34a33f78 100644 --- a/src/thorin/be/spirv/spirv.cpp +++ b/src/thorin/be/spirv/spirv.cpp @@ -19,7 +19,7 @@ void CodeGen::emit(std::ostream& out) { builder_->capability(spv::Capability::CapabilityLinkage); structure_loops(); - cleanup_world(world()); + // cleanup_world(world()); Scope::for_each(world(), [&](const Scope& scope) { emit(scope); }); diff --git a/src/thorin/be/spirv/spirv_transform.cpp b/src/thorin/be/spirv/spirv_transform.cpp index 5be442d66..6969980fb 100644 --- a/src/thorin/be/spirv/spirv_transform.cpp +++ b/src/thorin/be/spirv/spirv_transform.cpp @@ -151,6 +151,9 @@ inline void collect_dispatch_targets(World& world, ScopeContext& ctx, const Base } else { const Leaf* leaf = base->as(); auto cont = leaf->cf_node()->continuation(); + // For some nonsense reason, synthetic nodes created during scopes iteration leak in next iterations >:( + if (cont->intrinsic() >= Intrinsic::SCFBegin && cont->intrinsic() < Intrinsic::SCFEnd) + return; for (size_t i = 0; i < cont->num_ops(); i++) { auto def = cont->op(i); if (auto dest = def->isa_continuation()) { @@ -353,7 +356,7 @@ inline void rewire_loops(World& world, ScopeContext& ctx, const Base* base) { auto& loop = ctx.rewritten_loops[head]; if (head->num_cf_nodes() > 0) { - loop.new_epilogue->structured_loop_epilogue(loop.new_header, loop.epilogue_destination_conts); + loop.new_epilogue->structured_loop_merge(loop.new_header, loop.epilogue_destination_conts); loop.new_continue->structured_loop_continue(loop.new_header); loop.new_header->structured_loop_header(loop.new_epilogue, loop.new_continue, loop.header_destination_conts); printf("Loop %s!\n", loop.name.c_str()); @@ -378,6 +381,7 @@ inline void rewire_loops(World& world, ScopeContext& ctx, const Base* base) { auto old_fn_type = rewire.backedge->type(); auto wrapper = world.continuation(old_fn_type, {"synthetic_backedge_wrapper"}); + wrapper->attributes_.intrinsic = Intrinsic::SCFBackEdge; auto header_variant_type = loop.new_header->type()->op(0)->as(); wrapper->jump(loop.new_continue, { world.variant(header_variant_type, tuple_from_params(world, wrapper->params()), variant_index) }); @@ -391,6 +395,7 @@ inline void rewire_loops(World& world, ScopeContext& ctx, const Base* base) { auto old_fn_type = nlj.final_destination->type(); auto wrapper = world.continuation(old_fn_type, {"synthetic_nlj_wrapper"}); + wrapper->attributes_.intrinsic = Intrinsic::SCFNonLocalJump; printf("nlj = %s\n", wrapper->unique_name().c_str()); printf("src = %s\n", rewire.cont->name().c_str()); @@ -443,6 +448,7 @@ inline void rewire_loops(World& world, ScopeContext& ctx, const Base* base) { void CodeGen::structure_loops() { Scope::for_each(world(), [&](const Scope& scope) { ScopeContext context(scope); + printf("top: %d\n", scope.has_free_params()); const LoopTree& looptree = context.cfa.f_cfg().looptree(); tag_continuations(context, looptree.root(), nullptr); diff --git a/src/thorin/continuation.cpp b/src/thorin/continuation.cpp index b52e5535e..bcdcdec9e 100644 --- a/src/thorin/continuation.cpp +++ b/src/thorin/continuation.cpp @@ -252,8 +252,8 @@ void Continuation::match(const Def* val, Continuation* otherwise, Defs patterns, return jump(world().match(val->type(), patterns.size()), args, dbg); } -void Continuation::structured_loop_epilogue(const Continuation* loop_header, ArrayRef targets) { - attributes_.intrinsic = Intrinsic::StructuredLoopMerge; +void Continuation::structured_loop_merge(const Continuation* loop_header, ArrayRef targets) { + attributes_.intrinsic = Intrinsic::SCFLoopMerge; resize(1 + targets.size()); set_op(0, loop_header); size_t x = 1; @@ -262,13 +262,13 @@ void Continuation::structured_loop_epilogue(const Continuation* loop_header, Arr } void Continuation::structured_loop_continue(const Continuation* loop_header) { - attributes_.intrinsic = Intrinsic::StructuredLoopContinue; + attributes_.intrinsic = Intrinsic::SCFLoopContinue; resize(1); set_op(0, loop_header); } void Continuation::structured_loop_header(const Continuation* loop_epilogue, const Continuation* loop_continue, ArrayRef targets) { - attributes_.intrinsic = Intrinsic::StructuredLoopHeader; + attributes_.intrinsic = Intrinsic::SCFLoopHeader; resize(2 + targets.size()); set_op(0, loop_epilogue); set_op(1, loop_continue); diff --git a/src/thorin/continuation.h b/src/thorin/continuation.h index f7e481da7..9124536ed 100644 --- a/src/thorin/continuation.h +++ b/src/thorin/continuation.h @@ -84,10 +84,14 @@ enum class Intrinsic : uint8_t { Pipeline, ///< Intrinsic loop-pipelining-HLS-Backend Branch, ///< branch(cond, T, F). Match, ///< match(val, otherwise, (case1, cont1), (case2, cont2), ...) - StructuredLoopHeader, ///< A header for a structured loop, - StructuredLoopMerge, ///< A merge block for a structured loop, - StructuredLoopContinue, ///< A continue block in a structured loop, - PeInfo, ///< Partial evaluation debug info. + SCFBegin, + SCFLoopHeader = SCFBegin, ///< A header for a structured loop + SCFLoopMerge, ///< A merge block for a structured loop + SCFLoopContinue, ///< A continue block in a structured loop + SCFNonLocalJump, ///< A non-local jump in a structured control flow graph + SCFBackEdge, ///< A back edge a structured loop, + SCFEnd, + PeInfo = SCFEnd, ///< Partial evaluation debug info. EndScope ///< Dummy function which marks the end of a @p Scope. }; @@ -157,7 +161,7 @@ class Continuation : public Def { void jump(const Def* callee, Defs args, Debug dbg = {}); void branch(const Def* cond, const Def* t, const Def* f, Debug dbg = {}); void match(const Def* val, Continuation* otherwise, Defs patterns, ArrayRef continuations, Debug dbg = {}); - void structured_loop_epilogue(const Continuation* loop_header, ArrayRef targets); + void structured_loop_merge(const Continuation* loop_header, ArrayRef targets); void structured_loop_continue(const Continuation* loop_header); void structured_loop_header(const Continuation* loop_epilogue, const Continuation* loop_continue, ArrayRef targets); void verify() const { From c274cbe919eb24012dd6bb686d198f6b9ca54077 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Thu, 18 Mar 2021 17:09:49 +0100 Subject: [PATCH 037/342] assert structured CF --- src/thorin/be/spirv/spirv.cpp | 1 + src/thorin/be/spirv/spirv_transform.cpp | 27 ++++++++++++++++++++++++- 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/src/thorin/be/spirv/spirv.cpp b/src/thorin/be/spirv/spirv.cpp index d34a33f78..4902750ee 100644 --- a/src/thorin/be/spirv/spirv.cpp +++ b/src/thorin/be/spirv/spirv.cpp @@ -19,6 +19,7 @@ void CodeGen::emit(std::ostream& out) { builder_->capability(spv::Capability::CapabilityLinkage); structure_loops(); + structure_flow(); // cleanup_world(world()); Scope::for_each(world(), [&](const Scope& scope) { emit(scope); }); diff --git a/src/thorin/be/spirv/spirv_transform.cpp b/src/thorin/be/spirv/spirv_transform.cpp index 6969980fb..873e3991a 100644 --- a/src/thorin/be/spirv/spirv_transform.cpp +++ b/src/thorin/be/spirv/spirv_transform.cpp @@ -4,6 +4,7 @@ #include "thorin/analyses/cfg.h" #include +#include namespace thorin::spirv { @@ -463,7 +464,31 @@ void CodeGen::structure_loops() { } void CodeGen::structure_flow() { - // TODO + Scope::for_each(world(), [&](const Scope& scope) { + CFA cfa(scope); + auto& post_dom_tree = cfa.b_cfg().domtree(); + + for (auto def : scope.defs()) { + if (auto cont = def->isa_continuation()) { + auto cfn = cfa[cont]; + /*if (cont->callee() == world().branch()) { + printf("xd: %s\n", cont->unique_name().c_str()); + }*/ + + if (cont->intrinsic() >= Intrinsic::SCFBegin && cont->intrinsic() < Intrinsic::SCFEnd) + continue; + if (cont->preds().size() <= 1) + continue; + + for (auto pred : cont->preds()) { + auto& pred_dominators = post_dom_tree.children(cfa[cont]); + // TODO insert join nodes when this assert breaks + // TODO inspect postdom tree recursively + assert(std::find(pred_dominators.begin(), pred_dominators.end(), cfa[pred]) != pred_dominators.end()); + } + } + } + }); } } \ No newline at end of file From 5d16699bf34314d72ac1109b865772b96d1e7a78 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Thu, 18 Mar 2021 18:12:59 +0100 Subject: [PATCH 038/342] improved convert() --- src/thorin/be/spirv/spirv.cpp | 99 +++++++++++++++++++-------- src/thorin/be/spirv/spirv.h | 11 ++- src/thorin/be/spirv/spirv_builder.hpp | 9 +++ 3 files changed, 88 insertions(+), 31 deletions(-) diff --git a/src/thorin/be/spirv/spirv.cpp b/src/thorin/be/spirv/spirv.cpp index 4902750ee..d2417c3d0 100644 --- a/src/thorin/be/spirv/spirv.cpp +++ b/src/thorin/be/spirv/spirv.cpp @@ -28,20 +28,23 @@ void CodeGen::emit(std::ostream& out) { builder_ = nullptr; } -SpvId CodeGen::convert(const Type* type) { +SpvType CodeGen::convert(const Type* type) { if (auto spv_type = types_.lookup(type)) return *spv_type; assert(!type->isa()); - SpvId spv_type; + SpvType spv_type; switch (type->tag()) { - case PrimType_bool: spv_type = builder_->declare_bool_type(); break; + // Boolean types are typically packed intelligently when declaring in local variables, however with vanilla Vulkan 1.0 they can only be represented via 32-bit integers + // Using extensions, we could use 16 or 8-bit ints instead + // We can also pack them inside structures using bit-twiddling tricks, if the need arises + case PrimType_bool: spv_type.id = builder_->declare_bool_type(); spv_type.size = 4; spv_type.alignment = 4; break; case PrimType_ps8: case PrimType_qs8: case PrimType_pu8: case PrimType_qu8: assert(false && "TODO: look into capabilities to enable this"); case PrimType_ps16: case PrimType_qs16: case PrimType_pu16: case PrimType_qu16: assert(false && "TODO: look into capabilities to enable this"); - case PrimType_ps32: case PrimType_qs32: spv_type = builder_->declare_int_type(32, true ); break; - case PrimType_pu32: case PrimType_qu32: spv_type = builder_->declare_int_type(32, false); break; + case PrimType_ps32: case PrimType_qs32: spv_type.id = builder_->declare_int_type(32, true ); spv_type.size = 4; spv_type.alignment = 4; break; + case PrimType_pu32: case PrimType_qu32: spv_type.id = builder_->declare_int_type(32, false); spv_type.size = 4; spv_type.alignment = 4; break; case PrimType_ps64: case PrimType_qs64: case PrimType_pu64: case PrimType_qu64: assert(false && "TODO: look into capabilities to enable this"); case PrimType_pf16: case PrimType_qf16: assert(false && "TODO: look into capabilities to enable this"); - case PrimType_pf32: case PrimType_qf32: spv_type = builder_->declare_float_type(32); break; + case PrimType_pf32: case PrimType_qf32: spv_type.id = builder_->declare_float_type(32); spv_type.size = 4; spv_type.alignment = 4; break; case PrimType_pf64: case PrimType_qf64: assert(false && "TODO: look into capabilities to enable this"); case Node_PtrType: { auto ptr = type->as(); @@ -54,37 +57,41 @@ SpvId CodeGen::convert(const Type* type) { //return types_[type] = spv_type; } case Node_DefiniteArrayType: { - assert(false && "TODO"); auto array = type->as(); - //return types_[type] = spv_type; + SpvType element = convert(array->elem_type()); + SpvId size = builder_->constant(convert(world().type_pu32()).id, { (uint32_t) array->dim() }); + spv_type.id = builder_->declare_array_type(element.id, size); + spv_type.size = element.size * array->dim(); + spv_type.alignment = element.alignment; + break; } case Node_ClosureType: case Node_FnType: { // extract "return" type, collect all other types auto fn = type->as(); - std::unique_ptr ret; + std::unique_ptr ret; std::vector ops; for (auto op : fn->ops()) { if (op->isa() || op == world().unit()) continue; auto fn = op->isa(); if (fn && !op->isa()) { assert(!ret && "only one 'return' supported"); - std::vector ret_types; + std::vector ret_types; for (auto fn_op : fn->ops()) { if (fn_op->isa() || fn_op == world().unit()) continue; ret_types.push_back(convert(fn_op)); } - if (ret_types.size() == 0) ret = std::make_unique(builder_->void_type); - else if (ret_types.size() == 1) ret = std::make_unique(ret_types.back()); + if (ret_types.empty()) ret = std::make_unique( SpvType { { builder_->void_type }, 0, 1} ); + else if (ret_types.size() == 1) ret = std::make_unique(ret_types.back()); else assert(false && "Didn't we refactor this out yet by making functions single-argument ?"); } else - ops.push_back(convert(op)); + ops.push_back(convert(op).id); } assert(ret); if (type->tag() == Node_FnType) { - return types_[type] = builder_->declare_fn_type(ops, *ret); + return types_[type] = { builder_->declare_fn_type(ops, ret->id), 0, 0 }; } assert(false && "TODO: handle closure mess"); @@ -93,24 +100,57 @@ SpvId CodeGen::convert(const Type* type) { case Node_StructType: { std::vector types; - for (auto elem : type->as()->ops()) - types.push_back(convert(elem)); - spv_type = builder_->declare_struct_type(types); - // TODO debug info + for (auto elem : type->as()->ops()) { + auto member_type = convert(elem); + types.push_back(member_type.id); + spv_type.size += member_type.size; + + // TODO handle alignment for real + assert(member_type.alignment == 4 || (member_type.size == 0 && member_type.alignment == 1)); + spv_type.alignment = 4; + } + if (spv_type.size == 0) + spv_type.alignment = 1; + spv_type.id = builder_->declare_struct_type(types); + builder_->name(spv_type.id, type->to_string()); break; } case Node_TupleType: { std::vector types; - for (auto elem : type->as()->ops()) - types.push_back(convert(elem)); - spv_type = builder_->declare_struct_type(types); - // TODO debug info + for (auto elem : type->as()->ops()){ + auto member_type = convert(elem); + types.push_back(member_type.id); + spv_type.size += member_type.size; + + // TODO handle alignment for real + assert(member_type.alignment == 4 || (member_type.size == 0 && member_type.alignment == 1)); + spv_type.alignment = 4; + } + if (spv_type.size == 0) + spv_type.alignment = 1; + spv_type.id = builder_->declare_struct_type(types); + builder_->name(spv_type.id, type->to_string()); break; } case Node_VariantType: { - assert(false && "TODO"); + std::vector types; + types.push_back(convert(world().type_pu32()).id); + for (auto elem : type->as()->ops()){ + auto member_type = convert(elem); + spv_type.size = std::max(spv_type.size, member_type.size); + + // TODO handle alignment for real + assert(member_type.alignment == 4 || (member_type.size == 0 && member_type.alignment == 1)); + spv_type.alignment = 4; + } + types.push_back(convert(world().definite_array_type(world().type_pu32(), (spv_type.size + 3) / 4)).id); + if (spv_type.size == 0) + spv_type.alignment = 1; + spv_type.id = builder_->declare_struct_type(types); + builder_->name(spv_type.id, type->to_string()); + break; } default: @@ -125,7 +165,7 @@ void CodeGen::emit(const thorin::Scope& scope) { assert(entry_->is_returning()); FnBuilder fn; - fn.fn_type = convert(entry_->type()); + fn.fn_type = convert(entry_->type()).id; fn.fn_ret_type = get_codom_type(entry_); current_fn_ = &fn; @@ -158,7 +198,7 @@ void CodeGen::emit(const thorin::Scope& scope) { auto param_t = convert(param->type()); fn.header.op(spv::Op::OpFunctionParameter, 3); auto id = builder_->generate_fresh_id(); - fn.header.ref_id(param_t); + fn.header.ref_id(param_t.id); fn.header.ref_id(id); fn.params[param] = id; } @@ -171,7 +211,7 @@ void CodeGen::emit(const thorin::Scope& scope) { // OpPhi requires the full list of predecessors (values, labels) // We don't have that yet! But we will need the Phi node identifier to build the basic blocks ... // To solve this we generate an id for the phi node now, but defer emission of it to a later stage - bb->phis[param] = { convert(param->type()), builder_->generate_fresh_id(), {} }; + bb->phis[param] = { convert(param->type()).id, builder_->generate_fresh_id(), {} }; } } } @@ -196,7 +236,7 @@ SpvId CodeGen::get_codom_type(const Continuation* fn) { if (op->isa() || is_type_unit(op)) continue; assert(op->order() == 0); - types.push_back(convert(op)); + types.push_back(convert(op).id); } if (types.empty()) return builder_->void_type; @@ -334,7 +374,8 @@ SpvId CodeGen::emit(const Def* def, BasicBlockBuilder* bb) { if (auto bin = def->isa()) { SpvId lhs = emit(bin->lhs(), bb); SpvId rhs = emit(bin->rhs(), bb); - SpvId result_type = convert(def->type()); + SpvType result_types = convert(def->type()); + SpvId result_type = result_types.id; if (auto cmp = bin->isa()) { auto type = cmp->lhs()->type(); @@ -436,7 +477,7 @@ SpvId CodeGen::emit(const Def* def, BasicBlockBuilder* bb) { } } else if (auto primlit = def->isa()) { Box box = primlit->value(); - auto type = convert(def->type()); + auto type = convert(def->type()).id; SpvId constant; switch (primlit->primtype_tag()) { case PrimType_bool: constant = bb->file_builder.bool_constant(type, box.get_bool()); break; diff --git a/src/thorin/be/spirv/spirv.h b/src/thorin/be/spirv/spirv.h index 89d4a0451..61dae73de 100644 --- a/src/thorin/be/spirv/spirv.h +++ b/src/thorin/be/spirv/spirv.h @@ -10,6 +10,13 @@ namespace thorin::spirv { using SpvId = builder::SpvId; +struct SpvType { + SpvId id; + size_t size = 0; + // TODO: Alignment rules are complicated and client API dependant + size_t alignment = 0; +}; + struct BasicBlockBuilder : public builder::SpvBasicBlockBuilder { explicit BasicBlockBuilder(builder::SpvFileBuilder& file_builder) : builder::SpvBasicBlockBuilder(file_builder) @@ -34,7 +41,7 @@ class CodeGen : public thorin::CodeGen { void structure_loops(); void structure_flow(); - SpvId convert(const Type*); + SpvType convert(const Type*); void emit(const Scope& scope); void emit_epilogue(Continuation*, BasicBlockBuilder* bb); SpvId emit(const Def* def, BasicBlockBuilder* bb); @@ -45,7 +52,7 @@ class CodeGen : public thorin::CodeGen { Continuation* entry_ = nullptr; FnBuilder* current_fn_ = nullptr; Scheduler scheduler_; - TypeMap types_; + TypeMap types_; DefMap defs_; }; diff --git a/src/thorin/be/spirv/spirv_builder.hpp b/src/thorin/be/spirv/spirv_builder.hpp index fb19b4af6..725ec466d 100644 --- a/src/thorin/be/spirv/spirv_builder.hpp +++ b/src/thorin/be/spirv/spirv_builder.hpp @@ -170,6 +170,15 @@ struct SpvFileBuilder { return id; } + SpvId declare_array_type(SpvId element_type, SpvId dim) { + types_constants.op(spv::Op::OpTypeArray, 4); + auto id = generate_fresh_id(); + types_constants.ref_id(id); + types_constants.ref_id(element_type); + types_constants.ref_id(dim); + return id; + } + SpvId declare_fn_type(std::vector& dom, SpvId codom) { types_constants.op(spv::Op::OpTypeFunction, 3 + dom.size()); auto id = generate_fresh_id(); From ea29fd7c9218e83861e1c07e5e0c0a3075db6804 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Fri, 19 Mar 2021 16:09:27 +0100 Subject: [PATCH 039/342] WIP loop emission --- src/thorin/be/spirv/spirv.cpp | 122 +++++++++++++++++++++++--- src/thorin/be/spirv/spirv.h | 15 +++- src/thorin/be/spirv/spirv_builder.hpp | 31 +++++-- 3 files changed, 142 insertions(+), 26 deletions(-) diff --git a/src/thorin/be/spirv/spirv.cpp b/src/thorin/be/spirv/spirv.cpp index d2417c3d0..89d60f405 100644 --- a/src/thorin/be/spirv/spirv.cpp +++ b/src/thorin/be/spirv/spirv.cpp @@ -135,6 +135,7 @@ SpvType CodeGen::convert(const Type* type) { } case Node_VariantType: { + assert(type->num_ops() > 0 && "empty variants not supported"); std::vector types; types.push_back(convert(world().type_pu32()).id); for (auto elem : type->as()->ops()){ @@ -145,9 +146,12 @@ SpvType CodeGen::convert(const Type* type) { assert(member_type.alignment == 4 || (member_type.size == 0 && member_type.alignment == 1)); spv_type.alignment = 4; } - types.push_back(convert(world().definite_array_type(world().type_pu32(), (spv_type.size + 3) / 4)).id); + spv_type.variant_data_size = (spv_type.size + 3) / 4; if (spv_type.size == 0) spv_type.alignment = 1; + else + types.push_back(convert(world().definite_array_type(world().type_pu32(), spv_type.variant_data_size)).id); + spv_type.variant_trivial = type->num_ops() == 1; spv_type.id = builder_->declare_struct_type(types); builder_->name(spv_type.id, type->to_string()); break; @@ -165,6 +169,7 @@ void CodeGen::emit(const thorin::Scope& scope) { assert(entry_->is_returning()); FnBuilder fn; + fn.file_builder = builder_; fn.fn_type = convert(entry_->type()).id; fn.fn_ret_type = get_codom_type(entry_); @@ -172,20 +177,18 @@ void CodeGen::emit(const thorin::Scope& scope) { auto conts = schedule(scope); + fn.bbs_to_emit.reserve(conts.size()); fn.bbs.reserve(conts.size()); - std::vector bbs; - bbs.reserve(conts.size()); + auto& bbs = fn.bbs; for (auto cont : conts) { if (cont->intrinsic() == Intrinsic::EndScope) continue; - BasicBlockBuilder* bb = &bbs.emplace_back(BasicBlockBuilder(*builder_)); - fn.bbs.emplace_back(bb); + BasicBlockBuilder* bb = &bbs.emplace_back(fn); + fn.bbs_to_emit.emplace_back(bb); auto [i, b] = fn.bbs_map.emplace(cont, bb); assert(b); - bb->label = builder_->generate_fresh_id(); - if (debug()) builder_->name(bb->label, cont->name().c_str()); fn.labels.emplace(cont, bb->label); @@ -211,7 +214,7 @@ void CodeGen::emit(const thorin::Scope& scope) { // OpPhi requires the full list of predecessors (values, labels) // We don't have that yet! But we will need the Phi node identifier to build the basic blocks ... // To solve this we generate an id for the phi node now, but defer emission of it to a later stage - bb->phis[param] = { convert(param->type()).id, builder_->generate_fresh_id(), {} }; + bb->phis_map[param] = { convert(param->type()).id, builder_->generate_fresh_id(), {} }; } } } @@ -225,6 +228,12 @@ void CodeGen::emit(const thorin::Scope& scope) { assert(cont == entry_ || cont->is_basicblock()); emit_epilogue(cont, fn.bbs_map[cont]); } + + for(auto& bb : fn.bbs) { + for (auto& [param, phi] : bb.phis_map) { + bb.phis.emplace_back(&phi); + } + } builder_->define_function(fn); } @@ -283,18 +292,76 @@ void CodeGen::emit_epilogue(Continuation* continuation, BasicBlockBuilder* bb) { } else if (continuation->callee()->isa()) { irbuilder.CreateUnreachable(); } */ - else if (auto callee = continuation->callee()->isa_continuation(); callee && callee->is_basicblock()) { // ordinary jump + else if (continuation->intrinsic() == Intrinsic::SCFLoopHeader) { + auto merge_label = current_fn_->bbs_map[continuation->op(0)->as_continuation()]->label; + auto continue_label = current_fn_->bbs_map[continuation->op(1)->as_continuation()]->label; + bb->loop_merge(merge_label, continue_label, spv::LoopControlMaskNone, {}); + + BasicBlockBuilder* dispatch_bb = ¤t_fn_->bbs.emplace_back(*current_fn_); + current_fn_->bbs_to_emit.emplace_back(dispatch_bb); + builder_->name(dispatch_bb->label, "inner_dispatch"); + + bb->branch(dispatch_bb->label); + int targets = continuation->num_ops() - 2; + assert(targets > 0); + + assert(targets == 1); + auto callee = continuation->op(2)->as_continuation(); + // Extract the relevant variant & expand the tuple if necessary + auto arg = world().variant_extract(continuation->param(0), 0); + bb->args[arg] = emit(arg, bb); + + if (callee->param(0)->type()->equal(arg->type())) { + auto* param = callee->param(0); + auto& phi = current_fn_->bbs_map[callee]->phis_map[param]; + phi.preds.emplace_back(*bb->args[arg], bb->label); + } else { + assert(false && "TODO destructure argument"); + } + + dispatch_bb->branch(current_fn_->bbs_map[callee]->label); + + } else if (continuation->intrinsic() == Intrinsic::SCFLoopContinue) { + auto loop_header =continuation->op(0)->as_continuation(); + auto header_label = current_fn_->bbs_map[loop_header]->label; + + auto arg = continuation->param(0); + bb->args[arg] = emit(arg, bb); + auto* param = loop_header->param(0); + auto& phi = current_fn_->bbs_map[loop_header]->phis_map[param]; + phi.preds.emplace_back(*bb->args[arg], *current_fn_->labels[continuation]); + + bb->branch(header_label); + } else if (continuation->intrinsic() == Intrinsic::SCFLoopMerge) { + auto header_cont = continuation->op(0)->as_continuation(); + + int targets = continuation->num_ops() - 1; + assert(targets > 0); + + assert(targets == 1); + auto callee = continuation->op(1)->as_continuation(); + // TODO phis + bb->branch(current_fn_->bbs_map[callee]->label); + } /*else if (continuation->intrinsic() == Intrinsic::SCFNonLocalJump) { + auto header_cont = continuation->op(0)->as_continuation(); + // TODO setup arguments & stuff + bb->branch(current_fn_->bbs_map[continuation->op(0)->as_continuation()]->label); + } else if (continuation->intrinsic() == Intrinsic::SCFBackEdge) { + // TODO setup arguments & stuff + bb->branch(current_fn_->bbs_map[continuation->op(0)->as_continuation()]->label); + } */ else if (auto callee = continuation->callee()->isa_continuation(); callee && callee->is_basicblock()) { // ordinary jump int index = -1; for (auto& arg : continuation->args()) { index++; if (is_mem(arg) || is_unit(arg)) continue; bb->args[arg] = emit(arg, bb); auto* param = callee->param(index); - auto& phi = current_fn_->bbs_map[callee]->phis[param]; + auto& phi = current_fn_->bbs_map[callee]->phis_map[param]; phi.preds.emplace_back(*bb->args[arg], *current_fn_->labels[continuation]); } bb->branch(*current_fn_->labels[callee]); - } /*else if (auto callee = continuation->callee()->isa_continuation(); callee && callee->is_intrinsic()) { + } + /*else if (auto callee = continuation->callee()->isa_continuation(); callee && callee->is_intrinsic()) { auto ret_continuation = emit_intrinsic(irbuilder, continuation); irbuilder.CreateBr(cont2bb(ret_continuation)); } else { // function/closure call @@ -494,17 +561,46 @@ SpvId CodeGen::emit(const Def* def, BasicBlockBuilder* bb) { case PrimType_pf64: case PrimType_qf64: assertf(false, "not implemented yet"); } return constant; - } else if(auto param = def->isa()) { + } else if (auto param = def->isa()) { if (auto param_id = current_fn_->params.lookup(param)) { assert((*param_id).id != 0); return *param_id; } else { - auto val = (*current_fn_->bbs_map[param->continuation()]).phis[param].value; + auto val = (*current_fn_->bbs_map[param->continuation()]).phis_map[param].value; assert(val.id != 0); return val; } + } else if (auto variant = def->isa()) { + auto type = convert(def->type()); + assert(type.variant_trivial); // TODO ! + return emit(variant->value(), bb); + } else if (auto vextract = def->isa()) { + auto type = convert(def->op(0)->type()); + assert(type.variant_trivial); // TODO ! + return emit(vextract->value(), bb); + } else if (auto vindex = def->isa()) { + assert(false && "Missing variant index"); + } else if (auto tuple = def->isa()) { + std::vector elements; + size_t x = 0; + for (auto& e : tuple->ops()) { + elements[x++] = emit(e, bb); + } + return bb->composite(convert(tuple->type()).id, elements); + } else if (auto structagg = def->isa()) { + std::vector elements; + size_t x = 0; + for (auto& e : structagg->ops()) { + elements[x++] = emit(e, bb); + } + return bb->composite(convert(structagg->type()).id, elements); } assertf(false, "Incomplete emit(def) definition"); } +BasicBlockBuilder::BasicBlockBuilder(FnBuilder& fn_builder) +: builder::SpvBasicBlockBuilder(*fn_builder.file_builder) { + label = file_builder.generate_fresh_id(); +} + } diff --git a/src/thorin/be/spirv/spirv.h b/src/thorin/be/spirv/spirv.h index 61dae73de..f552e75de 100644 --- a/src/thorin/be/spirv/spirv.h +++ b/src/thorin/be/spirv/spirv.h @@ -13,20 +13,27 @@ using SpvId = builder::SpvId; struct SpvType { SpvId id; size_t size = 0; + // TODO: Alignment rules are complicated and client API dependant size_t alignment = 0; + + // Only set for variant types + bool variant_trivial = false; + size_t variant_data_size = -1; }; +struct FnBuilder; + struct BasicBlockBuilder : public builder::SpvBasicBlockBuilder { - explicit BasicBlockBuilder(builder::SpvFileBuilder& file_builder) - : builder::SpvBasicBlockBuilder(file_builder) - {} + explicit BasicBlockBuilder(FnBuilder& fn_builder); - std::unordered_map phis; + std::unordered_map phis_map; DefMap args; }; struct FnBuilder : public builder::SpvFnBuilder { + builder::SpvFileBuilder* file_builder; + std::vector bbs; std::unordered_map bbs_map; ContinuationMap labels; DefMap params; diff --git a/src/thorin/be/spirv/spirv_builder.hpp b/src/thorin/be/spirv/spirv_builder.hpp index 725ec466d..ca0168938 100644 --- a/src/thorin/be/spirv/spirv_builder.hpp +++ b/src/thorin/be/spirv/spirv_builder.hpp @@ -74,11 +74,11 @@ struct SpvBasicBlockBuilder : public SpvSectionBuilder { SpvId value; std::vector> preds; }; - std::vector phis; + std::vector phis; SpvId label; SpvId composite(SpvId aggregate_t, std::vector& elements) { - op(spv::Op::OpLabel, 3 + elements.size()); + op(spv::Op::OpCompositeConstruct, 3 + elements.size()); ref_id(aggregate_t); auto id = generate_fresh_id(); ref_id(id); @@ -109,6 +109,16 @@ struct SpvBasicBlockBuilder : public SpvSectionBuilder { ref_id(false_target); } + void loop_merge(SpvId merge_bb, SpvId continue_bb, spv::LoopControlMask loop_control, std::vector loop_control_ops) { + op(spv::Op::OpLoopMerge, 4 + loop_control_ops.size()); + ref_id(merge_bb); + ref_id(continue_bb); + literal_int(loop_control); + + for (auto e : loop_control_ops) + literal_int(e); + } + void return_void() { op(spv::Op::OpReturn, 1); } @@ -126,7 +136,7 @@ struct SpvFnBuilder { public: SpvId fn_type; SpvId fn_ret_type; - std::vector bbs; + std::vector bbs_to_emit; // Contains OpFunctionParams SpvSectionBuilder header; @@ -136,8 +146,9 @@ struct SpvFileBuilder { SpvFileBuilder() : void_type(declare_void_type()) {} + SpvFileBuilder(const SpvFileBuilder&) = delete; - SpvId generate_fresh_id() { return {bound++ }; } + SpvId generate_fresh_id() { return { bound++ }; } void name(SpvId id, std::string_view str) { assert(id.id < bound); @@ -228,15 +239,17 @@ struct SpvFileBuilder { for (auto w : fn_builder.header.data_) fn_defs.data_.push_back(w); - for (auto& bb : fn_builder.bbs) { + for (auto& bb : fn_builder.bbs_to_emit) { fn_defs.op(spv::Op::OpLabel, 2); fn_defs.ref_id(bb->label); for (auto& phi : bb->phis) { - fn_defs.op(spv::Op::OpPhi, 3 + 2 * phi.preds.size()); - fn_defs.ref_id(phi.type); - fn_defs.ref_id(phi.value); - for (auto& [pred_value, pred_label] : phi.preds) { + fn_defs.op(spv::Op::OpPhi, 3 + 2 * phi->preds.size()); + fn_defs.ref_id(phi->type); + fn_defs.ref_id(phi->value); + printf("Phi %d\n", phi->value); + assert(phi->preds.size() > 0); + for (auto& [pred_value, pred_label] : phi->preds) { fn_defs.ref_id(pred_value); fn_defs.ref_id(pred_label); } From f2416ee57338a23c7332823c193b385632bf6dfc Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Fri, 19 Mar 2021 18:02:05 +0100 Subject: [PATCH 040/342] spirv doesn't like bitcasted allocas --- src/thorin/be/spirv/spirv.cpp | 55 ++++++++++++++++++++++----- src/thorin/be/spirv/spirv_builder.hpp | 54 ++++++++++++++++++++++++++ src/thorin/type.h | 2 + 3 files changed, 101 insertions(+), 10 deletions(-) diff --git a/src/thorin/be/spirv/spirv.cpp b/src/thorin/be/spirv/spirv.cpp index 89d60f405..e63e2f561 100644 --- a/src/thorin/be/spirv/spirv.cpp +++ b/src/thorin/be/spirv/spirv.cpp @@ -48,7 +48,16 @@ SpvType CodeGen::convert(const Type* type) { case PrimType_pf64: case PrimType_qf64: assert(false && "TODO: look into capabilities to enable this"); case Node_PtrType: { auto ptr = type->as(); - assert(false && "TODO"); + spv::StorageClass storage_class; + switch (ptr->addr_space()) { + case AddrSpace::Function: storage_class = spv::StorageClassFunction; break; + case AddrSpace::Private: storage_class = spv::StorageClassPrivate; break; + default: + assert(false && "This address space is not supported"); + break; + } + SpvType element = convert(ptr->pointee()); + spv_type.id = builder_->declare_ptr_type(storage_class, element.id); break; } case Node_IndefiniteArrayType: { @@ -300,7 +309,6 @@ void CodeGen::emit_epilogue(Continuation* continuation, BasicBlockBuilder* bb) { BasicBlockBuilder* dispatch_bb = ¤t_fn_->bbs.emplace_back(*current_fn_); current_fn_->bbs_to_emit.emplace_back(dispatch_bb); builder_->name(dispatch_bb->label, "inner_dispatch"); - bb->branch(dispatch_bb->label); int targets = continuation->num_ops() - 2; assert(targets > 0); @@ -309,12 +317,12 @@ void CodeGen::emit_epilogue(Continuation* continuation, BasicBlockBuilder* bb) { auto callee = continuation->op(2)->as_continuation(); // Extract the relevant variant & expand the tuple if necessary auto arg = world().variant_extract(continuation->param(0), 0); - bb->args[arg] = emit(arg, bb); + auto extracted = emit(arg, dispatch_bb); if (callee->param(0)->type()->equal(arg->type())) { auto* param = callee->param(0); auto& phi = current_fn_->bbs_map[callee]->phis_map[param]; - phi.preds.emplace_back(*bb->args[arg], bb->label); + phi.preds.emplace_back(extracted, dispatch_bb->label); } else { assert(false && "TODO destructure argument"); } @@ -572,14 +580,41 @@ SpvId CodeGen::emit(const Def* def, BasicBlockBuilder* bb) { } } else if (auto variant = def->isa()) { auto type = convert(def->type()); - assert(type.variant_trivial); // TODO ! - return emit(variant->value(), bb); + auto value = emit(variant->value(), bb); + std::vector elements; + elements.emplace_back(builder_->constant(convert(world().type_pu32()).id, { (uint32_t) variant->index() })); + if (type.variant_data_size > 0) { + auto variant_payload_type = world().definite_array_type(world().type_pu32(), type.variant_data_size); + auto payload_ptr_type = world().ptr_type(variant_payload_type, 1, -1, AddrSpace::Function); + auto alloca = bb->variable(convert(payload_ptr_type).id, spv::StorageClass::StorageClassFunction); + + auto casted_ptr_type = world().ptr_type(variant->value()->type(), 1, -1, AddrSpace::Function); + auto casted = bb->bitcast(convert(casted_ptr_type).id, alloca); + + bb->store(value, casted); + elements.push_back(bb->load(convert(variant_payload_type).id, alloca)); + } + return bb->composite(convert(variant->type()).id, elements); } else if (auto vextract = def->isa()) { - auto type = convert(def->op(0)->type()); - assert(type.variant_trivial); // TODO ! - return emit(vextract->value(), bb); + auto type = convert(def->type()); + assert(type.variant_data_size > 0); // TODO is it legal to extract () ? + if (type.variant_data_size > 0) { + auto payload = bb->extract(convert(world().type_pu32()).id, emit(vextract->value(), bb), {1}); + + auto variant_payload_type = world().definite_array_type(world().type_pu32(), type.variant_data_size); + auto payload_ptr_type = world().ptr_type(variant_payload_type, 1, -1, AddrSpace::Function); + auto alloca = bb->variable(convert(payload_ptr_type).id, spv::StorageClass::StorageClassFunction); + + auto casted_ptr_type = world().ptr_type(def->type(), 1, -1, AddrSpace::Function); + auto casted = bb->bitcast(convert(casted_ptr_type).id, alloca); + + bb->store(payload, alloca); + return bb->load(convert(def->type()).id, casted); + } + THORIN_UNREACHABLE; } else if (auto vindex = def->isa()) { - assert(false && "Missing variant index"); + auto value = emit(vindex->op(0), bb); + return bb->extract(convert(world().type_pu32()).id, value, { 0 }); } else if (auto tuple = def->isa()) { std::vector elements; size_t x = 0; diff --git a/src/thorin/be/spirv/spirv_builder.hpp b/src/thorin/be/spirv/spirv_builder.hpp index ca0168938..adc34fe01 100644 --- a/src/thorin/be/spirv/spirv_builder.hpp +++ b/src/thorin/be/spirv/spirv_builder.hpp @@ -87,6 +87,51 @@ struct SpvBasicBlockBuilder : public SpvSectionBuilder { return id; } + SpvId extract(SpvId target_type, SpvId composite, std::vector indices) { + op(spv::Op::OpCompositeExtract, 4 + indices.size()); + ref_id(target_type); + auto id = generate_fresh_id(); + ref_id(id); + ref_id(composite); + for (auto i : indices) + literal_int(i); + return id; + } + + SpvId variable(SpvId type, spv::StorageClass storage_class) { + op(spv::Op::OpVariable, 4); + ref_id(type); + auto id = generate_fresh_id(); + ref_id(id); + literal_int(storage_class); + return id; + } + + SpvId bitcast(SpvId target_type, SpvId value) { + op(spv::Op::OpBitcast, 4); + auto id = generate_fresh_id(); + ref_id(target_type); + ref_id(id); + ref_id(value); + return id; + } + + SpvId load(SpvId target_type, SpvId pointer) { + op(spv::Op::OpLoad, 4); + auto id = generate_fresh_id(); + ref_id(target_type); + ref_id(id); + ref_id(pointer); + return id; + } + + void store(SpvId value, SpvId pointer) { + op(spv::Op::OpStore, 3); + auto id = generate_fresh_id(); + ref_id(pointer); + ref_id(value); + } + SpvId binop(spv::Op op_, SpvId result_type, SpvId lhs, SpvId rhs) { op(op_, 5); auto id = generate_fresh_id(); @@ -181,6 +226,15 @@ struct SpvFileBuilder { return id; } + SpvId declare_ptr_type(spv::StorageClass storage_class, SpvId element_type) { + types_constants.op(spv::Op::OpTypePointer, 4); + auto id = generate_fresh_id(); + types_constants.ref_id(id); + types_constants.literal_int(storage_class); + types_constants.ref_id(element_type); + return id; + } + SpvId declare_array_type(SpvId element_type, SpvId dim) { types_constants.op(spv::Op::OpTypeArray, 4); auto id = generate_fresh_id(); diff --git a/src/thorin/type.h b/src/thorin/type.h index d8fbe7138..815f3e902 100644 --- a/src/thorin/type.h +++ b/src/thorin/type.h @@ -233,6 +233,8 @@ enum class AddrSpace : uint32_t { Texture = 2, Shared = 3, Constant = 4, + Private = 5, // Corresponds to the 'private' storage class in SPIR-V + Function = 6, // Corresponds to the 'function' storage class in SPIR-V }; /// Pointer type. From 456c3eb1da51397947f038a515f3dfdd62f9c248 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Mon, 22 Mar 2021 17:56:04 +0100 Subject: [PATCH 041/342] doing variants the stupid way for now --- src/thorin/be/spirv/spirv.cpp | 64 ++++++++++++--------------- src/thorin/be/spirv/spirv.h | 4 +- src/thorin/be/spirv/spirv_builder.hpp | 8 ++++ 3 files changed, 37 insertions(+), 39 deletions(-) diff --git a/src/thorin/be/spirv/spirv.cpp b/src/thorin/be/spirv/spirv.cpp index e63e2f561..3754b4646 100644 --- a/src/thorin/be/spirv/spirv.cpp +++ b/src/thorin/be/spirv/spirv.cpp @@ -145,23 +145,23 @@ SpvType CodeGen::convert(const Type* type) { case Node_VariantType: { assert(type->num_ops() > 0 && "empty variants not supported"); - std::vector types; - types.push_back(convert(world().type_pu32()).id); + std::vector payload_type; for (auto elem : type->as()->ops()){ auto member_type = convert(elem); - spv_type.size = std::max(spv_type.size, member_type.size); + payload_type.push_back(member_type.id); + spv_type.size += member_type.size; // TODO handle alignment for real assert(member_type.alignment == 4 || (member_type.size == 0 && member_type.alignment == 1)); spv_type.alignment = 4; } - spv_type.variant_data_size = (spv_type.size + 3) / 4; if (spv_type.size == 0) spv_type.alignment = 1; - else - types.push_back(convert(world().definite_array_type(world().type_pu32(), spv_type.variant_data_size)).id); - spv_type.variant_trivial = type->num_ops() == 1; - spv_type.id = builder_->declare_struct_type(types); + spv_type.payload_id = builder_->declare_struct_type(payload_type); + builder_->name(spv_type.payload_id, type->to_string() + "_payload"); + + std::vector with_tag = { convert(world().type_pu32()).id, spv_type.payload_id}; + spv_type.id = builder_->declare_struct_type(with_tag); builder_->name(spv_type.id, type->to_string()); break; } @@ -579,44 +579,35 @@ SpvId CodeGen::emit(const Def* def, BasicBlockBuilder* bb) { return val; } } else if (auto variant = def->isa()) { - auto type = convert(def->type()); - auto value = emit(variant->value(), bb); + auto struct_type = def->type()->as(); + auto type = convert(struct_type); std::vector elements; - elements.emplace_back(builder_->constant(convert(world().type_pu32()).id, { (uint32_t) variant->index() })); - if (type.variant_data_size > 0) { - auto variant_payload_type = world().definite_array_type(world().type_pu32(), type.variant_data_size); - auto payload_ptr_type = world().ptr_type(variant_payload_type, 1, -1, AddrSpace::Function); - auto alloca = bb->variable(convert(payload_ptr_type).id, spv::StorageClass::StorageClassFunction); - - auto casted_ptr_type = world().ptr_type(variant->value()->type(), 1, -1, AddrSpace::Function); - auto casted = bb->bitcast(convert(casted_ptr_type).id, alloca); - - bb->store(value, casted); - elements.push_back(bb->load(convert(variant_payload_type).id, alloca)); + elements.resize(struct_type->num_ops()); + size_t x = 0; + for (auto& e : struct_type->ops()) { + if (x == variant->index()) + elements[x] = emit(variant->value(), bb); + else + elements[x] = bb->undef(convert(e).id); + x++; } - return bb->composite(convert(variant->type()).id, elements); + auto payload = bb->composite(convert(variant->type()).payload_id, elements); + auto tag = builder_->constant(convert(world().type_pu32()).id, { static_cast(variant->index()) }); + std::vector with_tag = { tag, payload }; + return bb->composite(convert(variant->type()).id, with_tag); } else if (auto vextract = def->isa()) { - auto type = convert(def->type()); - assert(type.variant_data_size > 0); // TODO is it legal to extract () ? - if (type.variant_data_size > 0) { - auto payload = bb->extract(convert(world().type_pu32()).id, emit(vextract->value(), bb), {1}); + auto variant_type = vextract->value()->type()->as(); - auto variant_payload_type = world().definite_array_type(world().type_pu32(), type.variant_data_size); - auto payload_ptr_type = world().ptr_type(variant_payload_type, 1, -1, AddrSpace::Function); - auto alloca = bb->variable(convert(payload_ptr_type).id, spv::StorageClass::StorageClassFunction); + auto target_type = convert(def->type()); + auto payload = bb->extract(convert(variant_type).payload_id, emit(vextract->value(), bb), {1}); - auto casted_ptr_type = world().ptr_type(def->type(), 1, -1, AddrSpace::Function); - auto casted = bb->bitcast(convert(casted_ptr_type).id, alloca); - - bb->store(payload, alloca); - return bb->load(convert(def->type()).id, casted); - } - THORIN_UNREACHABLE; + return bb->extract(target_type.id, payload, { static_cast(vextract->index()) }); } else if (auto vindex = def->isa()) { auto value = emit(vindex->op(0), bb); return bb->extract(convert(world().type_pu32()).id, value, { 0 }); } else if (auto tuple = def->isa()) { std::vector elements; + elements.resize(tuple->num_ops()); size_t x = 0; for (auto& e : tuple->ops()) { elements[x++] = emit(e, bb); @@ -624,6 +615,7 @@ SpvId CodeGen::emit(const Def* def, BasicBlockBuilder* bb) { return bb->composite(convert(tuple->type()).id, elements); } else if (auto structagg = def->isa()) { std::vector elements; + elements.resize(structagg->num_ops()); size_t x = 0; for (auto& e : structagg->ops()) { elements[x++] = emit(e, bb); diff --git a/src/thorin/be/spirv/spirv.h b/src/thorin/be/spirv/spirv.h index f552e75de..a464a9905 100644 --- a/src/thorin/be/spirv/spirv.h +++ b/src/thorin/be/spirv/spirv.h @@ -17,9 +17,7 @@ struct SpvType { // TODO: Alignment rules are complicated and client API dependant size_t alignment = 0; - // Only set for variant types - bool variant_trivial = false; - size_t variant_data_size = -1; + SpvId payload_id; }; struct FnBuilder; diff --git a/src/thorin/be/spirv/spirv_builder.hpp b/src/thorin/be/spirv/spirv_builder.hpp index adc34fe01..cd6318971 100644 --- a/src/thorin/be/spirv/spirv_builder.hpp +++ b/src/thorin/be/spirv/spirv_builder.hpp @@ -77,6 +77,14 @@ struct SpvBasicBlockBuilder : public SpvSectionBuilder { std::vector phis; SpvId label; + SpvId undef(SpvId type) { + op(spv::Op::OpUndef, 3); + ref_id(type); + auto id = generate_fresh_id(); + ref_id(id); + return id; + } + SpvId composite(SpvId aggregate_t, std::vector& elements) { op(spv::Op::OpCompositeConstruct, 3 + elements.size()); ref_id(aggregate_t); From e6bffda09f3b77c108a5726894aed2ab80f8fa6e Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Wed, 24 Mar 2021 12:19:01 +0100 Subject: [PATCH 042/342] hacky but passes validation (needs cleanup) --- src/thorin/analyses/cfg.cpp | 6 +++ src/thorin/be/spirv/spirv.cpp | 50 +++++++++++++++++++++---- src/thorin/be/spirv/spirv.h | 4 +- src/thorin/be/spirv/spirv_builder.hpp | 6 +++ src/thorin/be/spirv/spirv_transform.cpp | 6 ++- src/thorin/continuation.cpp | 12 ++++++ src/thorin/continuation.h | 1 + 7 files changed, 73 insertions(+), 12 deletions(-) diff --git a/src/thorin/analyses/cfg.cpp b/src/thorin/analyses/cfg.cpp index 8f5361c5f..636232734 100644 --- a/src/thorin/analyses/cfg.cpp +++ b/src/thorin/analyses/cfg.cpp @@ -61,6 +61,12 @@ CFA::CFA(const Scope& scope) while (!queue.empty()) { auto def = pop(queue); + // Hacky ? + if (auto cont = def->isa_continuation()) { + for (auto op : cont->potential_succs()) + enqueue(op); + continue; + } for (auto op : def->ops()) enqueue(op); } diff --git a/src/thorin/be/spirv/spirv.cpp b/src/thorin/be/spirv/spirv.cpp index 3754b4646..3cca081bb 100644 --- a/src/thorin/be/spirv/spirv.cpp +++ b/src/thorin/be/spirv/spirv.cpp @@ -1,7 +1,8 @@ #include "thorin/be/spirv/spirv.h" + #include "thorin/analyses/scope.h" #include "thorin/analyses/schedule.h" - +#include "thorin/analyses/domtree.h" #include "thorin/transform/cleanup_world.h" #include @@ -173,11 +174,36 @@ SpvType CodeGen::convert(const Type* type) { return types_[type] = spv_type; } +inline Schedule schedule_structured(const Scope& scope) { + // until we have sth better simply use the RPO of the CFG + Schedule result; + for (auto n : scope.f_cfg().reverse_post_order()) + result.emplace_back(n->continuation()); + + auto schedule = [&](const Continuation* cont) { + printf("scheduled: %s\n", cont->unique_name().c_str()); + }; + + auto visit = [&](const Continuation* cont) { + if (cont->intrinsic() == Intrinsic::SCFLoopHeader) { + // Write continue block FIRST + schedule(cont->op(1)->as_continuation()); + // Then write the header + schedule(cont); + + schedule(cont->op(0)->as_continuation()); + } + }; + + return result; +} + void CodeGen::emit(const thorin::Scope& scope) { entry_ = scope.entry(); assert(entry_->is_returning()); FnBuilder fn; + fn.scope = &scope; fn.file_builder = builder_; fn.fn_type = convert(entry_->type()).id; fn.fn_ret_type = get_codom_type(entry_); @@ -229,9 +255,6 @@ void CodeGen::emit(const thorin::Scope& scope) { } } - Scheduler new_scheduler(scope); - swap(scheduler_, new_scheduler); - for (auto cont : conts) { if (cont->intrinsic() == Intrinsic::EndScope) continue; assert(cont == entry_ || cont->is_basicblock()); @@ -282,10 +305,20 @@ void CodeGen::emit_epilogue(Continuation* continuation, BasicBlockBuilder* bb) { } } else if (continuation->callee() == world().branch()) { + auto& domtree = current_fn_->scope->b_cfg().domtree(); + auto merge_cont = domtree.idom(current_fn_->scope->f_cfg().operator[](continuation))->continuation(); + + printf("Merge @%s\n", merge_cont->unique_name().c_str()); + /*BasicBlockBuilder* merge_bb = ¤t_fn_->bbs.emplace_back(*current_fn_); + auto merge_bb_location = std::find(current_fn_->bbs_to_emit.begin(), current_fn_->bbs_to_emit.end(), merge_cont); + current_fn_->bbs_to_emit.emplace(merge_bb_location + 1, merge_bb); + builder_->name(merge_bb->label, "merge_" + merge_cont->name());*/ + auto cond = emit(continuation->arg(0), bb); bb->args[continuation->arg(0)] = cond; auto tbb = *current_fn_->labels[continuation->arg(1)->as_continuation()]; auto fbb = *current_fn_->labels[continuation->arg(2)->as_continuation()]; + bb->selection_merge(*current_fn_->labels[merge_cont],spv::SelectionControlMaskNone); bb->branch_conditional(cond, tbb, fbb); } /*else if (continuation->callee()->isa() && continuation->callee()->as()->intrinsic() == Intrinsic::Match) { @@ -307,8 +340,11 @@ void CodeGen::emit_epilogue(Continuation* continuation, BasicBlockBuilder* bb) { bb->loop_merge(merge_label, continue_label, spv::LoopControlMaskNone, {}); BasicBlockBuilder* dispatch_bb = ¤t_fn_->bbs.emplace_back(*current_fn_); - current_fn_->bbs_to_emit.emplace_back(dispatch_bb); - builder_->name(dispatch_bb->label, "inner_dispatch"); + + auto header_bb_location = std::find(current_fn_->bbs_to_emit.begin(), current_fn_->bbs_to_emit.end(), bb); + + current_fn_->bbs_to_emit.emplace(header_bb_location + 1, dispatch_bb); + builder_->name(dispatch_bb->label, "dispatch_" + continuation->name()); bb->branch(dispatch_bb->label); int targets = continuation->num_ops() - 2; assert(targets > 0); @@ -330,7 +366,7 @@ void CodeGen::emit_epilogue(Continuation* continuation, BasicBlockBuilder* bb) { dispatch_bb->branch(current_fn_->bbs_map[callee]->label); } else if (continuation->intrinsic() == Intrinsic::SCFLoopContinue) { - auto loop_header =continuation->op(0)->as_continuation(); + auto loop_header = continuation->op(0)->as_continuation(); auto header_label = current_fn_->bbs_map[loop_header]->label; auto arg = continuation->param(0); diff --git a/src/thorin/be/spirv/spirv.h b/src/thorin/be/spirv/spirv.h index a464a9905..4e85a426b 100644 --- a/src/thorin/be/spirv/spirv.h +++ b/src/thorin/be/spirv/spirv.h @@ -4,8 +4,6 @@ #include "thorin/be/spirv/spirv_builder.hpp" #include "thorin/be/backends.h" -#include "thorin/analyses/schedule.h" - namespace thorin::spirv { using SpvId = builder::SpvId; @@ -30,6 +28,7 @@ struct BasicBlockBuilder : public builder::SpvBasicBlockBuilder { }; struct FnBuilder : public builder::SpvFnBuilder { + const Scope* scope; builder::SpvFileBuilder* file_builder; std::vector bbs; std::unordered_map bbs_map; @@ -56,7 +55,6 @@ class CodeGen : public thorin::CodeGen { builder::SpvFileBuilder* builder_ = nullptr; Continuation* entry_ = nullptr; FnBuilder* current_fn_ = nullptr; - Scheduler scheduler_; TypeMap types_; DefMap defs_; }; diff --git a/src/thorin/be/spirv/spirv_builder.hpp b/src/thorin/be/spirv/spirv_builder.hpp index cd6318971..cacb9f2b3 100644 --- a/src/thorin/be/spirv/spirv_builder.hpp +++ b/src/thorin/be/spirv/spirv_builder.hpp @@ -162,6 +162,12 @@ struct SpvBasicBlockBuilder : public SpvSectionBuilder { ref_id(false_target); } + void selection_merge(SpvId merge_bb, spv::SelectionControlMask selection_control) { + op(spv::Op::OpSelectionMerge, 3); + ref_id(merge_bb); + literal_int(selection_control); + } + void loop_merge(SpvId merge_bb, SpvId continue_bb, spv::LoopControlMask loop_control, std::vector loop_control_ops) { op(spv::Op::OpLoopMerge, 4 + loop_control_ops.size()); ref_id(merge_bb); diff --git a/src/thorin/be/spirv/spirv_transform.cpp b/src/thorin/be/spirv/spirv_transform.cpp index 873e3991a..43ebcfe1b 100644 --- a/src/thorin/be/spirv/spirv_transform.cpp +++ b/src/thorin/be/spirv/spirv_transform.cpp @@ -381,7 +381,7 @@ inline void rewire_loops(World& world, ScopeContext& ctx, const Base* base) { auto variant_index = index_of_destination(loop.inner_destinations, destination); auto old_fn_type = rewire.backedge->type(); - auto wrapper = world.continuation(old_fn_type, {"synthetic_backedge_wrapper"}); + auto wrapper = world.continuation(old_fn_type, {"synthetic_backedge_wrapper_to" + destination.cont->unique_name() }); wrapper->attributes_.intrinsic = Intrinsic::SCFBackEdge; auto header_variant_type = loop.new_header->type()->op(0)->as(); @@ -395,7 +395,7 @@ inline void rewire_loops(World& world, ScopeContext& ctx, const Base* base) { auto& nlj = rewire.non_local_jump; auto old_fn_type = nlj.final_destination->type(); - auto wrapper = world.continuation(old_fn_type, {"synthetic_nlj_wrapper"}); + auto wrapper = world.continuation(old_fn_type, {"synthetic_nlj_wrapper_to" + nlj.final_destination->unique_name() }); wrapper->attributes_.intrinsic = Intrinsic::SCFNonLocalJump; printf("nlj = %s\n", wrapper->unique_name().c_str()); @@ -480,6 +480,8 @@ void CodeGen::structure_flow() { if (cont->preds().size() <= 1) continue; + printf("has more than 1 incoming branch: %s\n", cont->unique_name().c_str()); + for (auto pred : cont->preds()) { auto& pred_dominators = post_dom_tree.children(cfa[cont]); // TODO insert join nodes when this assert breaks diff --git a/src/thorin/continuation.cpp b/src/thorin/continuation.cpp index bcdcdec9e..38d1f9663 100644 --- a/src/thorin/continuation.cpp +++ b/src/thorin/continuation.cpp @@ -15,6 +15,18 @@ const Def* Continuation::callee() const { return empty() ? world().bottom(world().fn_type(), debug()) : op(0); } +Defs Continuation::potential_succs() const { + if (intrinsic() == Intrinsic::SCFLoopHeader) + return ops().skip_front(2); + else if (intrinsic() == Intrinsic::SCFLoopMerge) + return ops().skip_front(); + + //else if (intrinsic() == Intrinsic::SCFLoopContinue) + //return std::vector { op(0) }; + + return ops(); +} + Continuation* Continuation::stub() const { Rewriter rewriter; diff --git a/src/thorin/continuation.h b/src/thorin/continuation.h index 9124536ed..3770c63ee 100644 --- a/src/thorin/continuation.h +++ b/src/thorin/continuation.h @@ -133,6 +133,7 @@ class Continuation : public Def { const Param* ret_param() const; const Def* callee() const; Defs args() const { return num_ops() == 0 ? Defs(0, 0) : ops().skip_front(); } + Defs potential_succs() const; const Def* arg(size_t i) const { return args()[i]; } const FnType* type() const { return Def::type()->as(); } const FnType* callee_fn_type() const { return callee()->type()->as(); } From f7e44007adf037b2a7e40d49bc9235d9c8c89664 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Thu, 25 Mar 2021 09:31:57 +0100 Subject: [PATCH 043/342] including debug stuff, commit history will be cleaned up afterwards --- src/thorin/be/spirv/spirv.cpp | 5 + src/thorin/util/dot_dump.cpp | 235 ++++++++++++++++++++++++++++++++++ 2 files changed, 240 insertions(+) create mode 100644 src/thorin/util/dot_dump.cpp diff --git a/src/thorin/be/spirv/spirv.cpp b/src/thorin/be/spirv/spirv.cpp index 3cca081bb..c4b40f949 100644 --- a/src/thorin/be/spirv/spirv.cpp +++ b/src/thorin/be/spirv/spirv.cpp @@ -7,6 +7,10 @@ #include +namespace thorin { + void dump_dot(thorin::World &world); +} + namespace thorin::spirv { CodeGen::CodeGen(thorin::World& world, Cont2Config&, bool debug) @@ -22,6 +26,7 @@ void CodeGen::emit(std::ostream& out) { structure_loops(); structure_flow(); // cleanup_world(world()); + dump_dot(world()); Scope::for_each(world(), [&](const Scope& scope) { emit(scope); }); diff --git a/src/thorin/util/dot_dump.cpp b/src/thorin/util/dot_dump.cpp new file mode 100644 index 000000000..21ebb0bcf --- /dev/null +++ b/src/thorin/util/dot_dump.cpp @@ -0,0 +1,235 @@ +#ifndef DOT_DUMP_H +#define DOT_DUMP_H + +#include "thorin/world.h" + +namespace thorin { + +/// Outputs the raw thorin IR as a graph without performing any scope or scheduling analysis +struct DotPrinter { + DotPrinter(World& world, const char* filename = "world.dot") : world(world) { + file = std::ofstream(filename); + } + + private: + void dump_def(const Def* def); + void dump_def_generic(const Def* def, const char* color, const char* shape); + void dump_literal(const Literal* cont); + void dump_primop(const PrimOp* cont); + void dump_continuation(const Continuation* cont); + + #define up "" + #define down "" + #define endl "\n" + + public: + void print() { + file << "digraph " << world.name() << " {" << up; + file << endl << "bgcolor=transparent;"; + for (Continuation* continuation : world.continuations()) { + // Ignore those if they are not referenced elsewhere... + if (continuation == world.branch() || continuation == world.end_scope()) + continue; + dump_def(continuation); + } + file << down << endl << "}" << endl; + } + + private: + thorin::World& world; + + DefSet done; + std::ofstream file; +}; + +void DotPrinter::dump_def(const Def* def) { + if (done.contains(def)) + return; + + if (def->isa_continuation()) + dump_continuation(def->as_continuation()); + else if (def->isa()) + dump_literal(def->as()); + else if (def->isa()) + dump_primop(def->as()); + else if (def->isa()) + dump_def_generic(def, "grey", "oval"); + else + dump_def_generic(def, "red", "star"); +} + +void DotPrinter::dump_def_generic(const Def* def, const char* color, const char* shape) { + file << endl << def->unique_name() << " [" << up; + + file << endl << "label = \""; + + file << def->unique_name() << " : " << def->type()->to_string(); + + file << "\";"; + + file << endl << "shape = " << shape << ";"; + file << endl << "color = " << color << ";"; + + file << down << endl << "]"; + + done.emplace(def); + + for (size_t i = 0; i < def->num_ops(); i++) { + const auto& op = def->op(i); + dump_def(op); + file << endl << def->unique_name() << " -> " << op->unique_name() << " [arrowhead=vee,label=\"o" << i << "\",fontsize=8,fontcolor=grey];"; + } +} + +void DotPrinter::dump_literal(const Literal* def) { + file << endl << def->unique_name() << " [" << up; + + file << endl << "label = \""; + file << def->to_string(); + file << "\";"; + + file << endl << "style = dotted;"; + + file << down << endl << "]"; + + done.emplace(def); + + assert(def->num_ops() == 0); +} + +void DotPrinter::dump_primop(const PrimOp* def) { + file << endl << def->unique_name() << " [" << up; + + file << endl << "label = \""; + + file << def->op_name(); + + auto variant = def->isa(); + auto variant_extract = def->isa(); + if (variant || variant_extract ) + file << "(" << (variant ? variant->index() : variant_extract->index()) << ")"; + + file << "\";"; + + file << endl << "color = darkseagreen1;"; + file << endl << "style = filled;"; + + file << down << endl << "]"; + + done.emplace(def); + + for (size_t i = 0; i < def->num_ops(); i++) { + const auto& op = def->op(i); + dump_def(op); + file << endl << def->unique_name() << " -> " << op->unique_name() << " [arrowhead=vee,label=\"o" << i << "\",fontsize=8,fontcolor=grey];"; + } +} + +void DotPrinter::dump_continuation(const Continuation* cont) { + done.emplace(cont); + auto intrinsic = cont->intrinsic(); + file << endl << cont->unique_name() << " [" << up; + + file << endl << "label = \""; + if (cont->is_exported()) + file << "[extern]\\n"; + auto name = cont->name(); + if (!cont->is_exported()) + name = cont->unique_name(); + file << name << "("; + for (size_t i = 0; i < cont->num_params(); i++) { + file << cont->param(i)->type()->to_string() << (i + 1 == cont->num_params() ? "" : ", "); + } + file << ")"; + file << "\";"; + + file << endl << "shape = rectangle;"; + if (intrinsic != Intrinsic::None) { + file << endl << "color = lightblue;"; + file << endl << "style = filled;"; + } + if (cont->is_exported()) { + file << endl << "color = pink;"; + file << endl << "style = filled;"; + } + + file << down << endl << "]"; + + int x = 1; + switch (intrinsic) { + case Intrinsic::SCFLoopHeader: + dump_def(cont->op(1)); + file << endl << cont->unique_name() << " -> " << cont->op(1)->unique_name() << " [arrowhead=none];"; + x = 2; + case Intrinsic::SCFLoopContinue: + case Intrinsic::SCFLoopMerge: + dump_def(cont->op(0)); + file << endl << cont->unique_name() << " -> " << cont->op(0)->unique_name() << " [arrowhead=none];"; + for (size_t i = x; i < cont->num_ops(); i++) { + auto op = cont->op(i); + dump_def(op); + file << endl << cont->unique_name() << " -> " << op->unique_name() << " [arrowhead=normal];"; + } + return; + + default: break; + } + + if (auto callee_cont = cont->callee()->isa_continuation()) { + switch (callee_cont->intrinsic()) { + case Intrinsic::Branch: { + auto condition = cont->arg(0); + dump_def(condition); + file << endl << condition->unique_name() << " -> " << cont->unique_name() + << " [arrowhead=onormal,label=\"condition\",fontsize=8,fontcolor=grey];"; + auto if_true = cont->arg(1); + dump_def(if_true); + file << endl << cont->unique_name() << " -> " << if_true->unique_name() + << " [arrowhead=normal,label=\"if_true\",fontsize=8,fontcolor=grey];"; + auto if_false = cont->arg(2); + dump_def(if_false); + file << endl << cont->unique_name() << " -> " << if_false->unique_name() + << " [arrowhead=normal,label=\"if_false\",fontsize=8,fontcolor=grey];"; + return; + } + default: + break; + } + } + + for (size_t i = 0; i < cont->num_args(); i++) { + auto arg = cont->arg(i); + dump_def(arg); + + if (cont->callee()->uses().size() > 1) + file << endl << arg->unique_name() << " -> " << cont->callee()->unique_name() << " [arrowhead=onormal,label=\"a" << i << " from " << cont->unique_name() << "\",fontsize=8,fontcolor=grey];"; + else + file << endl << arg->unique_name() << " -> " << cont->callee()->unique_name() << " [arrowhead=onormal,label=\"a" << i << "\",fontsize=8,fontcolor=grey];"; + } + + switch (intrinsic) { + // We don't care about the params for these, or the callee + case Intrinsic::Match: + case Intrinsic::Branch: + return; + default: + break; + } + + for (size_t i = 0; i < cont->num_params(); i++) { + auto param = cont->param(i); + dump_def(param); + file << endl << param->unique_name() << " -> " << cont->unique_name() << " [arrowhead=none,label=\"p" << i << "\",fontsize=8,fontcolor=grey];"; + } + dump_def(cont->callee()); + file << endl << cont->unique_name() << " -> " << cont->callee()->unique_name() << " [arrowhead=normal];"; +} + +void dump_dot(World& world) { + auto p = DotPrinter(world); + p.print(); +} + +} + +#endif //DOT_DUMP_H From 0f585b4a7b94cd2ed32981ae3e241ea6215d3524 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Thu, 25 Mar 2021 09:48:32 +0100 Subject: [PATCH 044/342] ancestors --- src/thorin/be/spirv/spirv_transform.cpp | 33 ++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/src/thorin/be/spirv/spirv_transform.cpp b/src/thorin/be/spirv/spirv_transform.cpp index 43ebcfe1b..db0b0887d 100644 --- a/src/thorin/be/spirv/spirv_transform.cpp +++ b/src/thorin/be/spirv/spirv_transform.cpp @@ -463,6 +463,28 @@ void CodeGen::structure_loops() { }); } +template +inline void iterate_ancestors(Continuation* cont, Fn fn) { + ContinuationSet done; + + Continuations stack; + stack.push_back(cont); + while (!stack.empty()) { + Continuation* top = stack.back(); + stack.pop_back(); + if (done.contains(top)) continue; + if (top != cont) fn(top); + done.insert(top); + for (auto pred : top->preds()) { + auto pred_cont = pred->isa_continuation(); + if (!pred_cont) continue; + if (!done.contains(pred_cont)) { + stack.push_back(pred_cont); + } + } + } +} + void CodeGen::structure_flow() { Scope::for_each(world(), [&](const Scope& scope) { CFA cfa(scope); @@ -475,19 +497,22 @@ void CodeGen::structure_flow() { printf("xd: %s\n", cont->unique_name().c_str()); }*/ - if (cont->intrinsic() >= Intrinsic::SCFBegin && cont->intrinsic() < Intrinsic::SCFEnd) - continue; + //if (cont->intrinsic() >= Intrinsic::SCFBegin && cont->intrinsic() < Intrinsic::SCFEnd) + // continue; if (cont->preds().size() <= 1) continue; printf("has more than 1 incoming branch: %s\n", cont->unique_name().c_str()); - for (auto pred : cont->preds()) { + /*for (auto pred : cont->preds()) { auto& pred_dominators = post_dom_tree.children(cfa[cont]); // TODO insert join nodes when this assert breaks // TODO inspect postdom tree recursively assert(std::find(pred_dominators.begin(), pred_dominators.end(), cfa[pred]) != pred_dominators.end()); - } + }*/ + iterate_ancestors(cont, [&](Continuation* ancestor) { + printf(" ancestor: %s\n", ancestor->unique_name().c_str()); + }); } } }); From 270147c07338ba433cd6dcb603c5ee1257846aef Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Mon, 29 Mar 2021 10:30:44 +0200 Subject: [PATCH 045/342] some exp stuff --- src/thorin/analyses/cfg.cpp | 6 -- src/thorin/be/spirv/spirv.cpp | 14 +-- src/thorin/be/spirv/spirv_transform.cpp | 110 +++++++++++++++++++++--- src/thorin/continuation.cpp | 26 ++---- src/thorin/continuation.h | 16 +++- src/thorin/util/dot_dump.cpp | 12 ++- 6 files changed, 132 insertions(+), 52 deletions(-) diff --git a/src/thorin/analyses/cfg.cpp b/src/thorin/analyses/cfg.cpp index 636232734..8f5361c5f 100644 --- a/src/thorin/analyses/cfg.cpp +++ b/src/thorin/analyses/cfg.cpp @@ -61,12 +61,6 @@ CFA::CFA(const Scope& scope) while (!queue.empty()) { auto def = pop(queue); - // Hacky ? - if (auto cont = def->isa_continuation()) { - for (auto op : cont->potential_succs()) - enqueue(op); - continue; - } for (auto op : def->ops()) enqueue(op); } diff --git a/src/thorin/be/spirv/spirv.cpp b/src/thorin/be/spirv/spirv.cpp index c4b40f949..324b41133 100644 --- a/src/thorin/be/spirv/spirv.cpp +++ b/src/thorin/be/spirv/spirv.cpp @@ -340,8 +340,8 @@ void CodeGen::emit_epilogue(Continuation* continuation, BasicBlockBuilder* bb) { irbuilder.CreateUnreachable(); } */ else if (continuation->intrinsic() == Intrinsic::SCFLoopHeader) { - auto merge_label = current_fn_->bbs_map[continuation->op(0)->as_continuation()]->label; - auto continue_label = current_fn_->bbs_map[continuation->op(1)->as_continuation()]->label; + auto merge_label = current_fn_->bbs_map[const_cast(continuation->attributes_.scf_metadata.loop_header.merge_target)]->label; + auto continue_label = current_fn_->bbs_map[const_cast(continuation->attributes_.scf_metadata.loop_header.continue_target)]->label; bb->loop_merge(merge_label, continue_label, spv::LoopControlMaskNone, {}); BasicBlockBuilder* dispatch_bb = ¤t_fn_->bbs.emplace_back(*current_fn_); @@ -351,11 +351,11 @@ void CodeGen::emit_epilogue(Continuation* continuation, BasicBlockBuilder* bb) { current_fn_->bbs_to_emit.emplace(header_bb_location + 1, dispatch_bb); builder_->name(dispatch_bb->label, "dispatch_" + continuation->name()); bb->branch(dispatch_bb->label); - int targets = continuation->num_ops() - 2; + int targets = continuation->num_ops(); assert(targets > 0); assert(targets == 1); - auto callee = continuation->op(2)->as_continuation(); + auto callee = continuation->op(0)->as_continuation(); // Extract the relevant variant & expand the tuple if necessary auto arg = world().variant_extract(continuation->param(0), 0); auto extracted = emit(arg, dispatch_bb); @@ -382,13 +382,13 @@ void CodeGen::emit_epilogue(Continuation* continuation, BasicBlockBuilder* bb) { bb->branch(header_label); } else if (continuation->intrinsic() == Intrinsic::SCFLoopMerge) { - auto header_cont = continuation->op(0)->as_continuation(); + // auto header_cont = continuation->op(0)->as_continuation(); - int targets = continuation->num_ops() - 1; + int targets = continuation->num_ops(); assert(targets > 0); assert(targets == 1); - auto callee = continuation->op(1)->as_continuation(); + auto callee = continuation->op(0)->as_continuation(); // TODO phis bb->branch(current_fn_->bbs_map[callee]->label); } /*else if (continuation->intrinsic() == Intrinsic::SCFNonLocalJump) { diff --git a/src/thorin/be/spirv/spirv_transform.cpp b/src/thorin/be/spirv/spirv_transform.cpp index db0b0887d..413ede8ce 100644 --- a/src/thorin/be/spirv/spirv_transform.cpp +++ b/src/thorin/be/spirv/spirv_transform.cpp @@ -473,7 +473,7 @@ inline void iterate_ancestors(Continuation* cont, Fn fn) { Continuation* top = stack.back(); stack.pop_back(); if (done.contains(top)) continue; - if (top != cont) fn(top); + if (top != cont && fn(top)) return; done.insert(top); for (auto pred : top->preds()) { auto pred_cont = pred->isa_continuation(); @@ -484,35 +484,121 @@ inline void iterate_ancestors(Continuation* cont, Fn fn) { } } } +template +inline void visit_children(const DomTreeBase& tree, const CFNode* n, Fn fn, bool is_children = false) { + if (is_children) + fn(n); + for (auto children : tree.children(n)) { + visit_children(tree, children, fn, true); + } +} void CodeGen::structure_flow() { Scope::for_each(world(), [&](const Scope& scope) { CFA cfa(scope); + auto& dom_tree = cfa.f_cfg().domtree(); auto& post_dom_tree = cfa.b_cfg().domtree(); for (auto def : scope.defs()) { if (auto cont = def->isa_continuation()) { - auto cfn = cfa[cont]; - /*if (cont->callee() == world().branch()) { - printf("xd: %s\n", cont->unique_name().c_str()); - }*/ - //if (cont->intrinsic() >= Intrinsic::SCFBegin && cont->intrinsic() < Intrinsic::SCFEnd) // continue; if (cont->preds().size() <= 1) continue; + auto dominator = dom_tree.idom(cfa[cont]); + printf("has more than 1 incoming branch: %s\n", cont->unique_name().c_str()); + printf(" dominator: %s\n", dominator->continuation()->unique_name().c_str()); + + auto dominator_post_dominator = post_dom_tree.idom(dominator); + if (dominator_post_dominator != nullptr) + printf(" dominator post dominator: %s\n", dominator_post_dominator->continuation()->unique_name().c_str()); + else + printf(" dominator post dominator: NONE lmao\n"); - /*for (auto pred : cont->preds()) { - auto& pred_dominators = post_dom_tree.children(cfa[cont]); - // TODO insert join nodes when this assert breaks - // TODO inspect postdom tree recursively - assert(std::find(pred_dominators.begin(), pred_dominators.end(), cfa[pred]) != pred_dominators.end()); - }*/ + visit_children(dom_tree, dominator, [&](const CFNode* n) { + printf(" dominator child: %s\n", n->continuation()->unique_name().c_str()); + }); + + bool needs_join = false; + Continuation* selection_dominator = nullptr; iterate_ancestors(cont, [&](Continuation* ancestor) { printf(" ancestor: %s\n", ancestor->unique_name().c_str()); + + /*for (auto post_dom : post_dom_tree.children(cfa[ancestor])) { + printf(" post-dominator: %s\n", post_dom->continuation()->unique_name().c_str()); + }*/ + auto post_dom = cfa[ancestor]; + while(true) { + post_dom = post_dom_tree.idom(post_dom); + if (post_dom == nullptr) break; + printf(" post-dominator: %s\n", post_dom->continuation()->unique_name().c_str()); + } + + /*bool ancestor_post_dominated = false; + Continuation* post_dom = ancestor; + while (true) { + auto dom_cfn = post_dom_tree.idom(cfa[post_dom]); + if (dom_cfn == nullptr) break; + post_dom = dom_cfn->continuation(); + printf(" post-dominator: %s\n", post_dom->unique_name().c_str()); + if (post_dom == cont) { + // Wrong. + ancestor_post_dominated = true; + continue; + } + } + needs_join |= !ancestor_post_dominated; + if (needs_join) + return true; + + bool dominate_all_preds = true; + for (auto pred : cont->preds()) { + printf(" pred: %s\n", ancestor->unique_name().c_str()); + bool dominated = false; + Continuation* dom = pred; + while (true) { + auto dom_cfn = dom_tree.idom(cfa[dom]); + if (dom_cfn == nullptr) break; + dom = dom_cfn->continuation(); + printf(" pred dominator: %s\n", dom->unique_name().c_str()); + if (dom == ancestor) { + dominated = true; + break; + } + } + dominate_all_preds &= dominated; + } + if (dominate_all_preds) { + selection_dominator = ancestor; + printf("This one dominates all preds: %s!\n", selection_dominator->unique_name().c_str()); + return true; + }*/ + + /* + ContinuationSet preds; + for (auto pred : cont->preds()) + preds.insert(pred); + Continuation* dom = ancestor; + while (true) { + auto dom_cfn = dom_tree.idom(cfa[dom]); + if (dom_cfn == nullptr) break; + dom = dom_cfn->continuation(); + printf(" dominator: %s\n", dom->unique_name().c_str()); + if (preds.contains(dom)) { + preds.erase(dom); + } + } + printf("%d\n", preds.size()); + if (preds.empty()) { + printf("This one dominates all preds!\n"); + }*/ + + return false; }); + + assert(!needs_join); } } }); diff --git a/src/thorin/continuation.cpp b/src/thorin/continuation.cpp index 38d1f9663..1da0c5194 100644 --- a/src/thorin/continuation.cpp +++ b/src/thorin/continuation.cpp @@ -15,18 +15,6 @@ const Def* Continuation::callee() const { return empty() ? world().bottom(world().fn_type(), debug()) : op(0); } -Defs Continuation::potential_succs() const { - if (intrinsic() == Intrinsic::SCFLoopHeader) - return ops().skip_front(2); - else if (intrinsic() == Intrinsic::SCFLoopMerge) - return ops().skip_front(); - - //else if (intrinsic() == Intrinsic::SCFLoopContinue) - //return std::vector { op(0) }; - - return ops(); -} - Continuation* Continuation::stub() const { Rewriter rewriter; @@ -266,9 +254,9 @@ void Continuation::match(const Def* val, Continuation* otherwise, Defs patterns, void Continuation::structured_loop_merge(const Continuation* loop_header, ArrayRef targets) { attributes_.intrinsic = Intrinsic::SCFLoopMerge; - resize(1 + targets.size()); - set_op(0, loop_header); - size_t x = 1; + attributes_.scf_metadata.loop_epilogue.loop_header = loop_header; + resize(targets.size()); + size_t x = 0; for (auto target : targets) set_op(x++, target); } @@ -281,10 +269,10 @@ void Continuation::structured_loop_continue(const Continuation* loop_header) { void Continuation::structured_loop_header(const Continuation* loop_epilogue, const Continuation* loop_continue, ArrayRef targets) { attributes_.intrinsic = Intrinsic::SCFLoopHeader; - resize(2 + targets.size()); - set_op(0, loop_epilogue); - set_op(1, loop_continue); - size_t x = 2; + resize(targets.size()); + attributes_.scf_metadata.loop_header.continue_target = loop_continue; + attributes_.scf_metadata.loop_header.merge_target = loop_epilogue; + size_t x = 0; for (auto target : targets) set_op(x++, target); } diff --git a/src/thorin/continuation.h b/src/thorin/continuation.h index 3770c63ee..8f6ea6c50 100644 --- a/src/thorin/continuation.h +++ b/src/thorin/continuation.h @@ -102,10 +102,25 @@ enum class Intrinsic : uint8_t { */ class Continuation : public Def { public: + /// Stores information about structured control flow that should not be encoded in ops, as ops encode control flow + union SCFMetadata { + struct { + const Continuation* continue_target; + const Continuation* merge_target; + } loop_header; + struct { + const Continuation* loop_header; + } loop_epilogue; + struct { + const Continuation* merge_target; + } selection_header; + }; + struct Attributes { Intrinsic intrinsic = Intrinsic::None; Visibility visibility = Visibility::Internal; CC cc = CC::C; + SCFMetadata scf_metadata = {}; Attributes() = default; Attributes(Intrinsic intrinsic) : intrinsic(intrinsic) {} @@ -133,7 +148,6 @@ class Continuation : public Def { const Param* ret_param() const; const Def* callee() const; Defs args() const { return num_ops() == 0 ? Defs(0, 0) : ops().skip_front(); } - Defs potential_succs() const; const Def* arg(size_t i) const { return args()[i]; } const FnType* type() const { return Def::type()->as(); } const FnType* callee_fn_type() const { return callee()->type()->as(); } diff --git a/src/thorin/util/dot_dump.cpp b/src/thorin/util/dot_dump.cpp index 21ebb0bcf..6fd419517 100644 --- a/src/thorin/util/dot_dump.cpp +++ b/src/thorin/util/dot_dump.cpp @@ -155,16 +155,14 @@ void DotPrinter::dump_continuation(const Continuation* cont) { file << down << endl << "]"; - int x = 1; + int x = 0; switch (intrinsic) { - case Intrinsic::SCFLoopHeader: - dump_def(cont->op(1)); - file << endl << cont->unique_name() << " -> " << cont->op(1)->unique_name() << " [arrowhead=none];"; - x = 2; case Intrinsic::SCFLoopContinue: - case Intrinsic::SCFLoopMerge: dump_def(cont->op(0)); - file << endl << cont->unique_name() << " -> " << cont->op(0)->unique_name() << " [arrowhead=none];"; + file << endl << cont->unique_name() << " -> " << cont->op(0)->unique_name() << " [arrowhead=normal];"; + return; + case Intrinsic::SCFLoopHeader: + case Intrinsic::SCFLoopMerge: for (size_t i = x; i < cont->num_ops(); i++) { auto op = cont->op(i); dump_def(op); From 45e0f9b19196e292dad103da67dd283537bdac63 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Mon, 29 Mar 2021 11:31:33 +0200 Subject: [PATCH 046/342] cleanup --- src/thorin/be/spirv/spirv_transform.cpp | 229 +++++------------------- 1 file changed, 43 insertions(+), 186 deletions(-) diff --git a/src/thorin/be/spirv/spirv_transform.cpp b/src/thorin/be/spirv/spirv_transform.cpp index 413ede8ce..bbdfc4018 100644 --- a/src/thorin/be/spirv/spirv_transform.cpp +++ b/src/thorin/be/spirv/spirv_transform.cpp @@ -45,6 +45,8 @@ struct StructuredLoop { const Head* parent_head; const Head* head; const std::string name; + StructuredLoop(const Head* parent_head, const Head* head, std::string&& name) + : parent_head(parent_head), head(head), name(name) {} std::vector inner_destinations = {}; std::vector outer_destinations = {}; @@ -71,7 +73,7 @@ struct ScopeContext { std::unordered_map rewritten_loops; }; -inline std::string safe_name(const Head* head) { +inline std::string loop_name(const Head* head) { if (head == nullptr || head->is_root()) { return "root"; } else { @@ -87,27 +89,17 @@ inline std::string safe_name(const Head* head) { /// Visits the forest and fills def2loop inline void tag_continuations(ScopeContext& ctx, const Base* base, const Head* parent) { - for (int i = 0; i < base->depth(); i++) - printf(" "); - if (auto* head = base->isa()) { - printf("loop: header = "); - } - for (auto& node : base->cf_nodes()) { - printf("%s ", node->continuation()->to_string().c_str()); - } - printf("\n"); - if (const Head* head = base->isa()) { - auto name = safe_name(head); + auto name = loop_name(head); for (auto& children : head->children()) { tag_continuations(ctx, &*children, head); } - StructuredLoop loop{parent, head, name}; + StructuredLoop loop(parent, head, std::move(name)); ctx.rewritten_loops.emplace(head, loop); - } else if(auto* leaf = base->isa()) { + } else if(base->isa()) { for (auto& node : base->cf_nodes()) { auto[i, result] = ctx.def2loop.emplace(node->continuation(), parent); assert(result); @@ -121,7 +113,7 @@ inline int record_destination(std::vector& vec, DispatchTarget d auto i = std::find(vec.begin(), vec.end(), dest); if (i == vec.end()) { vec.emplace_back(dest); - return vec.size() - 1; + return static_cast(vec.size()) - 1; } else return i - vec.begin(); } @@ -136,7 +128,7 @@ inline std::vector get_path(ScopeContext& ctx, const Head* head std::vector path = {}; assert(head != nullptr); while (head != nullptr) { - auto* loop = &ctx.rewritten_loops[head]; + StructuredLoop* loop = &ctx.rewritten_loops.find(head)->second; assert(loop != nullptr); path.emplace(path.begin(), loop); head = loop->parent_head; @@ -158,22 +150,22 @@ inline void collect_dispatch_targets(World& world, ScopeContext& ctx, const Base for (size_t i = 0; i < cont->num_ops(); i++) { auto def = cont->op(i); if (auto dest = def->isa_continuation()) { - const Head* source_loop = *ctx.def2loop[cont]; + const Head* source_loop_head = *ctx.def2loop[cont]; if (dest->intrinsic() == Intrinsic::Branch) { continue; } assert(ctx.def2loop.find(dest) != ctx.def2loop.end()); - const Head* dest_loop = *ctx.def2loop[dest]; + const Head* dest_loop_head = *ctx.def2loop[dest]; - if (source_loop != dest_loop) { + if (source_loop_head != dest_loop_head) { // We found a non-local jump - assert(ctx.rewritten_loops.find(source_loop) != ctx.rewritten_loops.end()); - auto& loop = ctx.rewritten_loops[source_loop]; + assert(ctx.rewritten_loops.find(source_loop_head) != ctx.rewritten_loops.end()); + auto& loop = ctx.rewritten_loops.find(source_loop_head)->second; - std::vector source_path = get_path(ctx, source_loop); - std::vector dest_path = get_path(ctx, dest_loop); + std::vector source_path = get_path(ctx, source_loop_head); + std::vector dest_path = get_path(ctx, dest_loop_head); int bi = 0; while (bi < std::min(source_path.size(), dest_path.size())) { if (source_path[bi] == dest_path[bi]) @@ -207,22 +199,22 @@ inline void collect_dispatch_targets(World& world, ScopeContext& ctx, const Base } }; - for (auto loop : leave) { + for (auto dest_loop : leave) { DispatchTarget destination; - destination.exit = loop; + destination.exit = dest_loop; - record_step(loop, destination); + record_step(dest_loop, destination); last = 1; - prev = loop; + prev = dest_loop; assert(prev != nullptr); } - for (auto loop : enter) { + for (auto dest_loop : enter) { DispatchTarget destination; - destination.entry = loop; + destination.entry = dest_loop; - record_step(loop, destination); + record_step(dest_loop, destination); last = 2; - prev = loop; + prev = dest_loop; assert(prev != nullptr); } @@ -238,22 +230,20 @@ inline void collect_dispatch_targets(World& world, ScopeContext& ctx, const Base dest }; loop.rewire.emplace_back(rewire); - printf("nlj %s %d!\n", loop.name.c_str(), loop.rewire.size()); - } else if (source_loop == dest_loop && source_loop != nullptr) { - for (auto& head : source_loop->cf_nodes()) { - if (head->continuation() == dest) { + } else if (source_loop_head == dest_loop_head && source_loop_head != nullptr) { + for (auto& cf_node : source_loop_head->cf_nodes()) { + if (cf_node->continuation() == dest) { // We found a backedge - assert(ctx.rewritten_loops.find(source_loop) != ctx.rewritten_loops.end()); - auto& loop = ctx.rewritten_loops[source_loop]; + assert(ctx.rewritten_loops.find(source_loop_head) != ctx.rewritten_loops.end()); + auto& loop = ctx.rewritten_loops.find(source_loop_head)->second; DispatchTarget destination; destination.cont = dest; record_destination(loop.inner_destinations, destination); RewireMe rewire(cont, i); - rewire.backedge = head->continuation(); + rewire.backedge = cf_node->continuation(); loop.rewire.emplace_back(rewire); - printf("backedge %s %d!\n", loop.name.c_str(), loop.rewire.size()); break; } } @@ -287,7 +277,7 @@ inline void create_headers(World& world, ScopeContext& ctx, const Base* base) { if (head->num_cf_nodes() == 0) return; - StructuredLoop& loop = ctx.rewritten_loops[head]; + StructuredLoop& loop = ctx.rewritten_loops.find(head)->second; // here, parent headers need to know what they're jumping *into* std::vector dest_types; @@ -316,7 +306,7 @@ inline void create_headers(World& world, ScopeContext& ctx, const Base* base) { inline void create_epilogues(World& world, ScopeContext& ctx, const Base* base) { if (const Head* head = base->isa()) { - StructuredLoop& loop = ctx.rewritten_loops[head]; + StructuredLoop& loop = ctx.rewritten_loops.find(head)->second; if (head->num_cf_nodes() > 0) { // here, children epilogues need to know what they're jumping *out to* @@ -354,34 +344,29 @@ inline void create_epilogues(World& world, ScopeContext& ctx, const Base* base) inline void rewire_loops(World& world, ScopeContext& ctx, const Base* base) { if (const Head* head = base->isa()) { assert(ctx.rewritten_loops.find(head) != ctx.rewritten_loops.end()); - auto& loop = ctx.rewritten_loops[head]; + auto& loop = ctx.rewritten_loops.find(head)->second; if (head->num_cf_nodes() > 0) { loop.new_epilogue->structured_loop_merge(loop.new_header, loop.epilogue_destination_conts); loop.new_continue->structured_loop_continue(loop.new_header); loop.new_header->structured_loop_header(loop.new_epilogue, loop.new_continue, loop.header_destination_conts); - printf("Loop %s!\n", loop.name.c_str()); - for (auto c : loop.header_destination_conts) - printf(" header target: %s!\n", c->unique_name().c_str()); - for (auto c : loop.epilogue_destination_conts) - printf(" epilogue target: %s!\n", c->unique_name().c_str()); } for (auto& children : head->children()) { rewire_loops(world, ctx, &*children); } - printf("rewires %s %d!\n", loop.name.c_str(), loop.rewire.size()); for (auto& rewire : loop.rewire) { - printf("rewire!\n"); + assert(loop.head != nullptr); if (rewire.backedge != nullptr) { - printf("handling BE!\n"); + DispatchTarget destination; destination.cont = rewire.backedge; auto variant_index = index_of_destination(loop.inner_destinations, destination); auto old_fn_type = rewire.backedge->type(); auto wrapper = world.continuation(old_fn_type, {"synthetic_backedge_wrapper_to" + destination.cont->unique_name() }); + ctx.def2loop[wrapper] = loop.head; wrapper->attributes_.intrinsic = Intrinsic::SCFBackEdge; auto header_variant_type = loop.new_header->type()->op(0)->as(); @@ -390,22 +375,13 @@ inline void rewire_loops(World& world, ScopeContext& ctx, const Base* base) { rewire.cont->unset_op(rewire.op); rewire.cont->set_op(rewire.op, wrapper); } else { - printf("handling NLJ!\n"); - auto& nlj = rewire.non_local_jump; auto old_fn_type = nlj.final_destination->type(); auto wrapper = world.continuation(old_fn_type, {"synthetic_nlj_wrapper_to" + nlj.final_destination->unique_name() }); + ctx.def2loop[wrapper] = loop.head; wrapper->attributes_.intrinsic = Intrinsic::SCFNonLocalJump; - printf("nlj = %s\n", wrapper->unique_name().c_str()); - printf("src = %s\n", rewire.cont->name().c_str()); - for (auto exit : nlj.exits) - printf("exit = %s\n", exit->name.c_str()); - for (auto enter : nlj.enters) - printf("enter = %s\n", enter->name.c_str()); - printf("dst = %s\n", nlj.final_destination->name().c_str()); - const Def* argument = tuple_from_params(world, wrapper->params()); Continuation* first_jump = nullptr; @@ -447,9 +423,8 @@ inline void rewire_loops(World& world, ScopeContext& ctx, const Base* base) { } void CodeGen::structure_loops() { - Scope::for_each(world(), [&](const Scope& scope) { + Scope::for_each(world(), [&](Scope& scope) { ScopeContext context(scope); - printf("top: %d\n", scope.has_free_params()); const LoopTree& looptree = context.cfa.f_cfg().looptree(); tag_continuations(context, looptree.root(), nullptr); @@ -459,40 +434,10 @@ void CodeGen::structure_loops() { create_epilogues(world(), context, looptree.root()); rewire_loops(world(), context, looptree.root()); - printf("done\n"); + scope.update(); }); } -template -inline void iterate_ancestors(Continuation* cont, Fn fn) { - ContinuationSet done; - - Continuations stack; - stack.push_back(cont); - while (!stack.empty()) { - Continuation* top = stack.back(); - stack.pop_back(); - if (done.contains(top)) continue; - if (top != cont && fn(top)) return; - done.insert(top); - for (auto pred : top->preds()) { - auto pred_cont = pred->isa_continuation(); - if (!pred_cont) continue; - if (!done.contains(pred_cont)) { - stack.push_back(pred_cont); - } - } - } -} -template -inline void visit_children(const DomTreeBase& tree, const CFNode* n, Fn fn, bool is_children = false) { - if (is_children) - fn(n); - for (auto children : tree.children(n)) { - visit_children(tree, children, fn, true); - } -} - void CodeGen::structure_flow() { Scope::for_each(world(), [&](const Scope& scope) { CFA cfa(scope); @@ -501,104 +446,16 @@ void CodeGen::structure_flow() { for (auto def : scope.defs()) { if (auto cont = def->isa_continuation()) { - //if (cont->intrinsic() >= Intrinsic::SCFBegin && cont->intrinsic() < Intrinsic::SCFEnd) - // continue; if (cont->preds().size() <= 1) continue; auto dominator = dom_tree.idom(cfa[cont]); - - printf("has more than 1 incoming branch: %s\n", cont->unique_name().c_str()); - printf(" dominator: %s\n", dominator->continuation()->unique_name().c_str()); - auto dominator_post_dominator = post_dom_tree.idom(dominator); - if (dominator_post_dominator != nullptr) - printf(" dominator post dominator: %s\n", dominator_post_dominator->continuation()->unique_name().c_str()); - else - printf(" dominator post dominator: NONE lmao\n"); - - visit_children(dom_tree, dominator, [&](const CFNode* n) { - printf(" dominator child: %s\n", n->continuation()->unique_name().c_str()); - }); - - bool needs_join = false; - Continuation* selection_dominator = nullptr; - iterate_ancestors(cont, [&](Continuation* ancestor) { - printf(" ancestor: %s\n", ancestor->unique_name().c_str()); - - /*for (auto post_dom : post_dom_tree.children(cfa[ancestor])) { - printf(" post-dominator: %s\n", post_dom->continuation()->unique_name().c_str()); - }*/ - auto post_dom = cfa[ancestor]; - while(true) { - post_dom = post_dom_tree.idom(post_dom); - if (post_dom == nullptr) break; - printf(" post-dominator: %s\n", post_dom->continuation()->unique_name().c_str()); - } - - /*bool ancestor_post_dominated = false; - Continuation* post_dom = ancestor; - while (true) { - auto dom_cfn = post_dom_tree.idom(cfa[post_dom]); - if (dom_cfn == nullptr) break; - post_dom = dom_cfn->continuation(); - printf(" post-dominator: %s\n", post_dom->unique_name().c_str()); - if (post_dom == cont) { - // Wrong. - ancestor_post_dominated = true; - continue; - } - } - needs_join |= !ancestor_post_dominated; - if (needs_join) - return true; - - bool dominate_all_preds = true; - for (auto pred : cont->preds()) { - printf(" pred: %s\n", ancestor->unique_name().c_str()); - bool dominated = false; - Continuation* dom = pred; - while (true) { - auto dom_cfn = dom_tree.idom(cfa[dom]); - if (dom_cfn == nullptr) break; - dom = dom_cfn->continuation(); - printf(" pred dominator: %s\n", dom->unique_name().c_str()); - if (dom == ancestor) { - dominated = true; - break; - } - } - dominate_all_preds &= dominated; - } - if (dominate_all_preds) { - selection_dominator = ancestor; - printf("This one dominates all preds: %s!\n", selection_dominator->unique_name().c_str()); - return true; - }*/ - - /* - ContinuationSet preds; - for (auto pred : cont->preds()) - preds.insert(pred); - Continuation* dom = ancestor; - while (true) { - auto dom_cfn = dom_tree.idom(cfa[dom]); - if (dom_cfn == nullptr) break; - dom = dom_cfn->continuation(); - printf(" dominator: %s\n", dom->unique_name().c_str()); - if (preds.contains(dom)) { - preds.erase(dom); - } - } - printf("%d\n", preds.size()); - if (preds.empty()) { - printf("This one dominates all preds!\n"); - }*/ - - return false; - }); - - assert(!needs_join); + bool needs_join = dominator_post_dominator->continuation() != cont; + if (needs_join) { + assert(false && "Not structured CF !"); + // TODO: insert join node into dominator and redirect dominated nodes to take it + } } } }); From 6f722e51a394533afb079e22e4d61b97ab562c64 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Mon, 29 Mar 2021 11:42:12 +0200 Subject: [PATCH 047/342] create dummy merge block when needed --- src/thorin/be/spirv/spirv.cpp | 21 +++++++++++++-------- src/thorin/be/spirv/spirv_builder.hpp | 8 +++++--- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/src/thorin/be/spirv/spirv.cpp b/src/thorin/be/spirv/spirv.cpp index 324b41133..37de4d114 100644 --- a/src/thorin/be/spirv/spirv.cpp +++ b/src/thorin/be/spirv/spirv.cpp @@ -312,18 +312,23 @@ void CodeGen::emit_epilogue(Continuation* continuation, BasicBlockBuilder* bb) { else if (continuation->callee() == world().branch()) { auto& domtree = current_fn_->scope->b_cfg().domtree(); auto merge_cont = domtree.idom(current_fn_->scope->f_cfg().operator[](continuation))->continuation(); - - printf("Merge @%s\n", merge_cont->unique_name().c_str()); - /*BasicBlockBuilder* merge_bb = ¤t_fn_->bbs.emplace_back(*current_fn_); - auto merge_bb_location = std::find(current_fn_->bbs_to_emit.begin(), current_fn_->bbs_to_emit.end(), merge_cont); - current_fn_->bbs_to_emit.emplace(merge_bb_location + 1, merge_bb); - builder_->name(merge_bb->label, "merge_" + merge_cont->name());*/ + SpvId merge_bb; + if (merge_cont == current_fn_->scope->exit()) { + BasicBlockBuilder* unreachable_merge_bb = ¤t_fn_->bbs.emplace_back(*current_fn_); + current_fn_->bbs_to_emit.emplace_back(unreachable_merge_bb); + builder_->name(unreachable_merge_bb->label, "merge_unreachable" + continuation->name()); + unreachable_merge_bb->unreachable(); + merge_bb = unreachable_merge_bb->label; + } else { + // TODO create a dedicated merge bb if this one is the merge blocks for more than 1 selection construct + *current_fn_->labels[merge_cont]; + } auto cond = emit(continuation->arg(0), bb); - bb->args[continuation->arg(0)] = cond; + bb->args.emplace(continuation->arg(0), cond); auto tbb = *current_fn_->labels[continuation->arg(1)->as_continuation()]; auto fbb = *current_fn_->labels[continuation->arg(2)->as_continuation()]; - bb->selection_merge(*current_fn_->labels[merge_cont],spv::SelectionControlMaskNone); + bb->selection_merge(merge_bb,spv::SelectionControlMaskNone); bb->branch_conditional(cond, tbb, fbb); } /*else if (continuation->callee()->isa() && continuation->callee()->as()->intrinsic() == Intrinsic::Match) { diff --git a/src/thorin/be/spirv/spirv_builder.hpp b/src/thorin/be/spirv/spirv_builder.hpp index cacb9f2b3..773af85a2 100644 --- a/src/thorin/be/spirv/spirv_builder.hpp +++ b/src/thorin/be/spirv/spirv_builder.hpp @@ -135,7 +135,6 @@ struct SpvBasicBlockBuilder : public SpvSectionBuilder { void store(SpvId value, SpvId pointer) { op(spv::Op::OpStore, 3); - auto id = generate_fresh_id(); ref_id(pointer); ref_id(value); } @@ -187,6 +186,10 @@ struct SpvBasicBlockBuilder : public SpvSectionBuilder { ref_id(value); } + void unreachable() { + op(spv::Op::OpUnreachable, 1); + } + private: SpvId generate_fresh_id(); }; @@ -315,8 +318,7 @@ struct SpvFileBuilder { fn_defs.op(spv::Op::OpPhi, 3 + 2 * phi->preds.size()); fn_defs.ref_id(phi->type); fn_defs.ref_id(phi->value); - printf("Phi %d\n", phi->value); - assert(phi->preds.size() > 0); + assert(!phi->preds.empty()); for (auto& [pred_value, pred_label] : phi->preds) { fn_defs.ref_id(pred_value); fn_defs.ref_id(pred_label); From 6f37d8869642bb61f34277030ad58fc77b076548 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Mon, 29 Mar 2021 12:02:33 +0200 Subject: [PATCH 048/342] more cleanup --- src/thorin/be/spirv/spirv.cpp | 51 +++++-------------------- src/thorin/be/spirv/spirv.h | 4 +- src/thorin/be/spirv/spirv_transform.cpp | 20 +++++----- 3 files changed, 22 insertions(+), 53 deletions(-) diff --git a/src/thorin/be/spirv/spirv.cpp b/src/thorin/be/spirv/spirv.cpp index 37de4d114..66fa5b9df 100644 --- a/src/thorin/be/spirv/spirv.cpp +++ b/src/thorin/be/spirv/spirv.cpp @@ -68,8 +68,9 @@ SpvType CodeGen::convert(const Type* type) { } case Node_IndefiniteArrayType: { assert(false && "TODO"); - auto array = type->as(); - //return types_[type] = spv_type; + // auto array = type->as(); + // return types_[type] = spv_type; + THORIN_UNREACHABLE; } case Node_DefiniteArrayType: { auto array = type->as(); @@ -110,7 +111,7 @@ SpvType CodeGen::convert(const Type* type) { } assert(false && "TODO: handle closure mess"); - break; + THORIN_UNREACHABLE; } case Node_StructType: { @@ -179,30 +180,6 @@ SpvType CodeGen::convert(const Type* type) { return types_[type] = spv_type; } -inline Schedule schedule_structured(const Scope& scope) { - // until we have sth better simply use the RPO of the CFG - Schedule result; - for (auto n : scope.f_cfg().reverse_post_order()) - result.emplace_back(n->continuation()); - - auto schedule = [&](const Continuation* cont) { - printf("scheduled: %s\n", cont->unique_name().c_str()); - }; - - auto visit = [&](const Continuation* cont) { - if (cont->intrinsic() == Intrinsic::SCFLoopHeader) { - // Write continue block FIRST - schedule(cont->op(1)->as_continuation()); - // Then write the header - schedule(cont); - - schedule(cont->op(0)->as_continuation()); - } - }; - - return result; -} - void CodeGen::emit(const thorin::Scope& scope) { entry_ = scope.entry(); assert(entry_->is_returning()); @@ -224,7 +201,7 @@ void CodeGen::emit(const thorin::Scope& scope) { for (auto cont : conts) { if (cont->intrinsic() == Intrinsic::EndScope) continue; - BasicBlockBuilder* bb = &bbs.emplace_back(fn); + BasicBlockBuilder* bb = bbs.emplace_back(std::make_unique(fn)).get(); fn.bbs_to_emit.emplace_back(bb); auto [i, b] = fn.bbs_map.emplace(cont, bb); assert(b); @@ -267,8 +244,8 @@ void CodeGen::emit(const thorin::Scope& scope) { } for(auto& bb : fn.bbs) { - for (auto& [param, phi] : bb.phis_map) { - bb.phis.emplace_back(&phi); + for (auto& [param, phi] : bb->phis_map) { + bb->phis.emplace_back(&phi); } } @@ -314,7 +291,7 @@ void CodeGen::emit_epilogue(Continuation* continuation, BasicBlockBuilder* bb) { auto merge_cont = domtree.idom(current_fn_->scope->f_cfg().operator[](continuation))->continuation(); SpvId merge_bb; if (merge_cont == current_fn_->scope->exit()) { - BasicBlockBuilder* unreachable_merge_bb = ¤t_fn_->bbs.emplace_back(*current_fn_); + BasicBlockBuilder* unreachable_merge_bb = current_fn_->bbs.emplace_back(std::make_unique(*current_fn_)).get(); current_fn_->bbs_to_emit.emplace_back(unreachable_merge_bb); builder_->name(unreachable_merge_bb->label, "merge_unreachable" + continuation->name()); unreachable_merge_bb->unreachable(); @@ -349,7 +326,7 @@ void CodeGen::emit_epilogue(Continuation* continuation, BasicBlockBuilder* bb) { auto continue_label = current_fn_->bbs_map[const_cast(continuation->attributes_.scf_metadata.loop_header.continue_target)]->label; bb->loop_merge(merge_label, continue_label, spv::LoopControlMaskNone, {}); - BasicBlockBuilder* dispatch_bb = ¤t_fn_->bbs.emplace_back(*current_fn_); + BasicBlockBuilder* dispatch_bb = current_fn_->bbs.emplace_back(std::make_unique(*current_fn_)).get(); auto header_bb_location = std::find(current_fn_->bbs_to_emit.begin(), current_fn_->bbs_to_emit.end(), bb); @@ -396,14 +373,7 @@ void CodeGen::emit_epilogue(Continuation* continuation, BasicBlockBuilder* bb) { auto callee = continuation->op(0)->as_continuation(); // TODO phis bb->branch(current_fn_->bbs_map[callee]->label); - } /*else if (continuation->intrinsic() == Intrinsic::SCFNonLocalJump) { - auto header_cont = continuation->op(0)->as_continuation(); - // TODO setup arguments & stuff - bb->branch(current_fn_->bbs_map[continuation->op(0)->as_continuation()]->label); - } else if (continuation->intrinsic() == Intrinsic::SCFBackEdge) { - // TODO setup arguments & stuff - bb->branch(current_fn_->bbs_map[continuation->op(0)->as_continuation()]->label); - } */ else if (auto callee = continuation->callee()->isa_continuation(); callee && callee->is_basicblock()) { // ordinary jump + } else if (auto callee = continuation->callee()->isa_continuation(); callee && callee->is_basicblock()) { // ordinary jump int index = -1; for (auto& arg : continuation->args()) { index++; @@ -626,7 +596,6 @@ SpvId CodeGen::emit(const Def* def, BasicBlockBuilder* bb) { } } else if (auto variant = def->isa()) { auto struct_type = def->type()->as(); - auto type = convert(struct_type); std::vector elements; elements.resize(struct_type->num_ops()); size_t x = 0; diff --git a/src/thorin/be/spirv/spirv.h b/src/thorin/be/spirv/spirv.h index 4e85a426b..074876e26 100644 --- a/src/thorin/be/spirv/spirv.h +++ b/src/thorin/be/spirv/spirv.h @@ -15,7 +15,7 @@ struct SpvType { // TODO: Alignment rules are complicated and client API dependant size_t alignment = 0; - SpvId payload_id; + SpvId payload_id = { 0 }; }; struct FnBuilder; @@ -30,7 +30,7 @@ struct BasicBlockBuilder : public builder::SpvBasicBlockBuilder { struct FnBuilder : public builder::SpvFnBuilder { const Scope* scope; builder::SpvFileBuilder* file_builder; - std::vector bbs; + std::vector> bbs; std::unordered_map bbs_map; ContinuationMap labels; DefMap params; diff --git a/src/thorin/be/spirv/spirv_transform.cpp b/src/thorin/be/spirv/spirv_transform.cpp index bbdfc4018..392baa92d 100644 --- a/src/thorin/be/spirv/spirv_transform.cpp +++ b/src/thorin/be/spirv/spirv_transform.cpp @@ -136,10 +136,10 @@ inline std::vector get_path(ScopeContext& ctx, const Head* head return path; } -inline void collect_dispatch_targets(World& world, ScopeContext& ctx, const Base* base, const Head* parent) { +inline void collect_dispatch_targets(World& world, ScopeContext& ctx, const Base* base) { if (const Head* head = base->isa()) { for (auto& children : head->children()) { - collect_dispatch_targets(world, ctx, &*children, head); + collect_dispatch_targets(world, ctx, &*children); } } else { const Leaf* leaf = base->as(); @@ -167,7 +167,7 @@ inline void collect_dispatch_targets(World& world, ScopeContext& ctx, const Base std::vector source_path = get_path(ctx, source_loop_head); std::vector dest_path = get_path(ctx, dest_loop_head); int bi = 0; - while (bi < std::min(source_path.size(), dest_path.size())) { + while (bi < static_cast(std::min(source_path.size(), dest_path.size()))) { if (source_path[bi] == dest_path[bi]) bi++; else break; @@ -177,9 +177,9 @@ inline void collect_dispatch_targets(World& world, ScopeContext& ctx, const Base // these two sequences cannot be both empty (that wouldn't be a non-local jump then!) std::vector leave; std::vector enter; - for (int j = source_path.size() - 1; j >= bi; j--) + for (int j = static_cast(source_path.size()) - 1; j >= bi; j--) leave.emplace_back(source_path[j]); - for (int j = bi; j < dest_path.size(); j++) + for (int j = bi; j < static_cast(dest_path.size()); j++) enter.emplace_back(dest_path[j]); // 0 = this is the first step of the path @@ -188,7 +188,7 @@ inline void collect_dispatch_targets(World& world, ScopeContext& ctx, const Base int last = 0; StructuredLoop* prev; - auto record_step = [&](StructuredLoop* loop, DispatchTarget destination) { + auto record_step = [&](DispatchTarget destination) { if (last == 0) { // nothing to do, this node isn't a dispatching one } else { @@ -203,7 +203,7 @@ inline void collect_dispatch_targets(World& world, ScopeContext& ctx, const Base DispatchTarget destination; destination.exit = dest_loop; - record_step(dest_loop, destination); + record_step(destination); last = 1; prev = dest_loop; assert(prev != nullptr); @@ -212,7 +212,7 @@ inline void collect_dispatch_targets(World& world, ScopeContext& ctx, const Base DispatchTarget destination; destination.entry = dest_loop; - record_step(dest_loop, destination); + record_step(destination); last = 2; prev = dest_loop; assert(prev != nullptr); @@ -221,7 +221,7 @@ inline void collect_dispatch_targets(World& world, ScopeContext& ctx, const Base assert(last != 0); DispatchTarget destination; destination.cont = dest; - record_step(prev, destination); + record_step(destination); RewireMe rewire(cont, i); rewire.non_local_jump = { @@ -428,7 +428,7 @@ void CodeGen::structure_loops() { const LoopTree& looptree = context.cfa.f_cfg().looptree(); tag_continuations(context, looptree.root(), nullptr); - collect_dispatch_targets(world(), context, looptree.root(), nullptr); + collect_dispatch_targets(world(), context, looptree.root()); create_headers(world(), context, looptree.root()); create_epilogues(world(), context, looptree.root()); From e34254966157c754d728ff6cdc48286ace66a967 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Tue, 30 Mar 2021 11:05:14 +0200 Subject: [PATCH 049/342] merge llvm_rewrite --- src/thorin/be/codegen.cpp | 11 +++++++++-- src/thorin/be/spirv/spirv.cpp | 14 +++++++------- src/thorin/be/spirv/spirv.h | 5 +++-- src/thorin/be/spirv/spirv_transform.cpp | 4 ++-- 4 files changed, 21 insertions(+), 13 deletions(-) diff --git a/src/thorin/be/codegen.cpp b/src/thorin/be/codegen.cpp index fa8078175..f6bec2238 100644 --- a/src/thorin/be/codegen.cpp +++ b/src/thorin/be/codegen.cpp @@ -6,6 +6,9 @@ #include "thorin/be/llvm/nvvm.h" #include "thorin/be/llvm/amdgpu.h" #endif +#if THORIN_ENABLE_SPIRV +#include "thorin/be/spirv/spirv.h" +#endif #include "thorin/be/c/c.h" namespace thorin { @@ -87,7 +90,8 @@ DeviceBackends::DeviceBackends(World& world, int opt, bool debug) std::pair { NVVM, Intrinsic::NVVM }, std::pair { OpenCL, Intrinsic::OpenCL }, std::pair { AMDGPU, Intrinsic::AMDGPU }, - std::pair { HLS, Intrinsic::HLS } + std::pair { HLS, Intrinsic::HLS }, + std::pair { SpirV, Intrinsic::SpirV } }; for (auto [backend, intrinsic] : backend_intrinsics) { if (is_passed_to_intrinsic(continuation, intrinsic)) { @@ -109,7 +113,7 @@ DeviceBackends::DeviceBackends(World& world, int opt, bool debug) kernels.emplace_back(continuation); }); - for (auto backend : std::array { CUDA, NVVM, OpenCL, AMDGPU }) { + for (auto backend : std::array { CUDA, NVVM, OpenCL, AMDGPU, SpirV }) { if (!importers_[backend].world().empty()) { get_kernel_configs(importers_[backend], kernels, kernel_config, [&](Continuation *use, Continuation * /* imported */) { // determine whether or not this kernel uses restrict pointers @@ -178,6 +182,9 @@ DeviceBackends::DeviceBackends(World& world, int opt, bool debug) if (!importers_[AMDGPU].world().empty()) cgs[AMDGPU] = std::make_unique(importers_[AMDGPU].world(), kernel_config, opt, debug); #else (void)opt; +#endif +#if THORIN_ENABLE_SPIRV + if (!importers_[SpirV].world().empty()) cgs[SpirV] = std::make_unique(importers_[SpirV].world(), kernel_config, debug); #endif for (auto [backend, lang] : std::array { std::pair { CUDA, c::Lang::CUDA }, std::pair { OpenCL, c::Lang::OpenCL }, std::pair { HLS, c::Lang::HLS } }) if (!importers_[backend].world().empty()) cgs[backend] = std::make_unique(importers_[backend].world(), kernel_config, lang, debug); diff --git a/src/thorin/be/spirv/spirv.cpp b/src/thorin/be/spirv/spirv.cpp index 66fa5b9df..6b2bd7943 100644 --- a/src/thorin/be/spirv/spirv.cpp +++ b/src/thorin/be/spirv/spirv.cpp @@ -17,7 +17,7 @@ CodeGen::CodeGen(thorin::World& world, Cont2Config&, bool debug) : thorin::CodeGen(world, debug) {} -void CodeGen::emit(std::ostream& out) { +void CodeGen::emit_stream(std::ostream& out) { builder::SpvFileBuilder builder; builder_ = &builder; builder_->capability(spv::Capability::CapabilityShader); @@ -298,13 +298,13 @@ void CodeGen::emit_epilogue(Continuation* continuation, BasicBlockBuilder* bb) { merge_bb = unreachable_merge_bb->label; } else { // TODO create a dedicated merge bb if this one is the merge blocks for more than 1 selection construct - *current_fn_->labels[merge_cont]; + merge_bb = current_fn_->labels[merge_cont]; } auto cond = emit(continuation->arg(0), bb); bb->args.emplace(continuation->arg(0), cond); - auto tbb = *current_fn_->labels[continuation->arg(1)->as_continuation()]; - auto fbb = *current_fn_->labels[continuation->arg(2)->as_continuation()]; + auto tbb = current_fn_->labels[continuation->arg(1)->as_continuation()]; + auto fbb = current_fn_->labels[continuation->arg(2)->as_continuation()]; bb->selection_merge(merge_bb,spv::SelectionControlMaskNone); bb->branch_conditional(cond, tbb, fbb); } /*else if (continuation->callee()->isa() && @@ -360,7 +360,7 @@ void CodeGen::emit_epilogue(Continuation* continuation, BasicBlockBuilder* bb) { bb->args[arg] = emit(arg, bb); auto* param = loop_header->param(0); auto& phi = current_fn_->bbs_map[loop_header]->phis_map[param]; - phi.preds.emplace_back(*bb->args[arg], *current_fn_->labels[continuation]); + phi.preds.emplace_back(bb->args[arg], current_fn_->labels[continuation]); bb->branch(header_label); } else if (continuation->intrinsic() == Intrinsic::SCFLoopMerge) { @@ -381,9 +381,9 @@ void CodeGen::emit_epilogue(Continuation* continuation, BasicBlockBuilder* bb) { bb->args[arg] = emit(arg, bb); auto* param = callee->param(index); auto& phi = current_fn_->bbs_map[callee]->phis_map[param]; - phi.preds.emplace_back(*bb->args[arg], *current_fn_->labels[continuation]); + phi.preds.emplace_back(bb->args[arg], current_fn_->labels[continuation]); } - bb->branch(*current_fn_->labels[callee]); + bb->branch(current_fn_->labels[callee]); } /*else if (auto callee = continuation->callee()->isa_continuation(); callee && callee->is_intrinsic()) { auto ret_continuation = emit_intrinsic(irbuilder, continuation); diff --git a/src/thorin/be/spirv/spirv.h b/src/thorin/be/spirv/spirv.h index 074876e26..fda32aa2d 100644 --- a/src/thorin/be/spirv/spirv.h +++ b/src/thorin/be/spirv/spirv.h @@ -2,7 +2,7 @@ #define THORIN_SPIRV_H #include "thorin/be/spirv/spirv_builder.hpp" -#include "thorin/be/backends.h" +#include "thorin/be/codegen.h" namespace thorin::spirv { @@ -40,7 +40,8 @@ class CodeGen : public thorin::CodeGen { public: CodeGen(World&, Cont2Config&, bool debug); - void emit(std::ostream& stream) override; + void emit_stream(std::ostream& stream) override; + const char* file_ext() const override { return ".spv"; } protected: void structure_loops(); void structure_flow(); diff --git a/src/thorin/be/spirv/spirv_transform.cpp b/src/thorin/be/spirv/spirv_transform.cpp index 392baa92d..51f613f2a 100644 --- a/src/thorin/be/spirv/spirv_transform.cpp +++ b/src/thorin/be/spirv/spirv_transform.cpp @@ -150,14 +150,14 @@ inline void collect_dispatch_targets(World& world, ScopeContext& ctx, const Base for (size_t i = 0; i < cont->num_ops(); i++) { auto def = cont->op(i); if (auto dest = def->isa_continuation()) { - const Head* source_loop_head = *ctx.def2loop[cont]; + const Head* source_loop_head = ctx.def2loop[cont]; if (dest->intrinsic() == Intrinsic::Branch) { continue; } assert(ctx.def2loop.find(dest) != ctx.def2loop.end()); - const Head* dest_loop_head = *ctx.def2loop[dest]; + const Head* dest_loop_head = ctx.def2loop[dest]; if (source_loop_head != dest_loop_head) { // We found a non-local jump From 49135bc57e85aff1f7d3a02d34aed2dc83b87506 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Thu, 1 Apr 2021 16:15:19 +0200 Subject: [PATCH 050/342] remember to delete later --- src/thorin/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/src/thorin/CMakeLists.txt b/src/thorin/CMakeLists.txt index c7dd05f05..0e9915a48 100644 --- a/src/thorin/CMakeLists.txt +++ b/src/thorin/CMakeLists.txt @@ -82,6 +82,7 @@ set(THORIN_SOURCES util/symbol.h util/types.h util/utility.h + util/dot_dump.cpp ) if(LLVM_FOUND) From 0f74c581cd970882885d1940bbfaf310e7533a6b Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Thu, 15 Apr 2021 15:10:18 +0200 Subject: [PATCH 051/342] datatypes refactor --- src/thorin/CMakeLists.txt | 1 + src/thorin/be/spirv/spirv.cpp | 208 ++++------------------ src/thorin/be/spirv/spirv.h | 70 +++++++- src/thorin/be/spirv/spirv_builder.hpp | 12 ++ src/thorin/be/spirv/spirv_datatypes.cpp | 219 ++++++++++++++++++++++++ 5 files changed, 329 insertions(+), 181 deletions(-) create mode 100644 src/thorin/be/spirv/spirv_datatypes.cpp diff --git a/src/thorin/CMakeLists.txt b/src/thorin/CMakeLists.txt index 0e9915a48..8fbcec8fd 100644 --- a/src/thorin/CMakeLists.txt +++ b/src/thorin/CMakeLists.txt @@ -108,6 +108,7 @@ if(SPIRV_ENABLED) be/spirv/spirv.cpp be/spirv/spirv.h be/spirv/spirv_transform.cpp + be/spirv/spirv_datatypes.cpp ) endif() diff --git a/src/thorin/be/spirv/spirv.cpp b/src/thorin/be/spirv/spirv.cpp index 6b2bd7943..15a1cdb05 100644 --- a/src/thorin/be/spirv/spirv.cpp +++ b/src/thorin/be/spirv/spirv.cpp @@ -22,6 +22,8 @@ void CodeGen::emit_stream(std::ostream& out) { builder_ = &builder; builder_->capability(spv::Capability::CapabilityShader); builder_->capability(spv::Capability::CapabilityLinkage); + builder_->capability(spv::Capability::CapabilityVariablePointers); + builder_->capability(spv::Capability::CapabilityPhysicalStorageBufferAddresses); structure_loops(); structure_flow(); @@ -30,154 +32,14 @@ void CodeGen::emit_stream(std::ostream& out) { Scope::for_each(world(), [&](const Scope& scope) { emit(scope); }); - builder_->finish(out); - builder_ = nullptr; -} - -SpvType CodeGen::convert(const Type* type) { - if (auto spv_type = types_.lookup(type)) return *spv_type; - - assert(!type->isa()); - SpvType spv_type; - switch (type->tag()) { - // Boolean types are typically packed intelligently when declaring in local variables, however with vanilla Vulkan 1.0 they can only be represented via 32-bit integers - // Using extensions, we could use 16 or 8-bit ints instead - // We can also pack them inside structures using bit-twiddling tricks, if the need arises - case PrimType_bool: spv_type.id = builder_->declare_bool_type(); spv_type.size = 4; spv_type.alignment = 4; break; - case PrimType_ps8: case PrimType_qs8: case PrimType_pu8: case PrimType_qu8: assert(false && "TODO: look into capabilities to enable this"); - case PrimType_ps16: case PrimType_qs16: case PrimType_pu16: case PrimType_qu16: assert(false && "TODO: look into capabilities to enable this"); - case PrimType_ps32: case PrimType_qs32: spv_type.id = builder_->declare_int_type(32, true ); spv_type.size = 4; spv_type.alignment = 4; break; - case PrimType_pu32: case PrimType_qu32: spv_type.id = builder_->declare_int_type(32, false); spv_type.size = 4; spv_type.alignment = 4; break; - case PrimType_ps64: case PrimType_qs64: case PrimType_pu64: case PrimType_qu64: assert(false && "TODO: look into capabilities to enable this"); - case PrimType_pf16: case PrimType_qf16: assert(false && "TODO: look into capabilities to enable this"); - case PrimType_pf32: case PrimType_qf32: spv_type.id = builder_->declare_float_type(32); spv_type.size = 4; spv_type.alignment = 4; break; - case PrimType_pf64: case PrimType_qf64: assert(false && "TODO: look into capabilities to enable this"); - case Node_PtrType: { - auto ptr = type->as(); - spv::StorageClass storage_class; - switch (ptr->addr_space()) { - case AddrSpace::Function: storage_class = spv::StorageClassFunction; break; - case AddrSpace::Private: storage_class = spv::StorageClassPrivate; break; - default: - assert(false && "This address space is not supported"); - break; - } - SpvType element = convert(ptr->pointee()); - spv_type.id = builder_->declare_ptr_type(storage_class, element.id); - break; - } - case Node_IndefiniteArrayType: { - assert(false && "TODO"); - // auto array = type->as(); - // return types_[type] = spv_type; - THORIN_UNREACHABLE; - } - case Node_DefiniteArrayType: { - auto array = type->as(); - SpvType element = convert(array->elem_type()); - SpvId size = builder_->constant(convert(world().type_pu32()).id, { (uint32_t) array->dim() }); - spv_type.id = builder_->declare_array_type(element.id, size); - spv_type.size = element.size * array->dim(); - spv_type.alignment = element.alignment; - break; - } - - case Node_ClosureType: - case Node_FnType: { - // extract "return" type, collect all other types - auto fn = type->as(); - std::unique_ptr ret; - std::vector ops; - for (auto op : fn->ops()) { - if (op->isa() || op == world().unit()) continue; - auto fn = op->isa(); - if (fn && !op->isa()) { - assert(!ret && "only one 'return' supported"); - std::vector ret_types; - for (auto fn_op : fn->ops()) { - if (fn_op->isa() || fn_op == world().unit()) continue; - ret_types.push_back(convert(fn_op)); - } - if (ret_types.empty()) ret = std::make_unique( SpvType { { builder_->void_type }, 0, 1} ); - else if (ret_types.size() == 1) ret = std::make_unique(ret_types.back()); - else assert(false && "Didn't we refactor this out yet by making functions single-argument ?"); - } else - ops.push_back(convert(op).id); - } - assert(ret); - - if (type->tag() == Node_FnType) { - return types_[type] = { builder_->declare_fn_type(ops, ret->id), 0, 0 }; - } - - assert(false && "TODO: handle closure mess"); - THORIN_UNREACHABLE; - } - - case Node_StructType: { - std::vector types; - for (auto elem : type->as()->ops()) { - auto member_type = convert(elem); - types.push_back(member_type.id); - spv_type.size += member_type.size; - - // TODO handle alignment for real - assert(member_type.alignment == 4 || (member_type.size == 0 && member_type.alignment == 1)); - spv_type.alignment = 4; - } - if (spv_type.size == 0) - spv_type.alignment = 1; - spv_type.id = builder_->declare_struct_type(types); - builder_->name(spv_type.id, type->to_string()); - break; + for (auto& cont : world().continuations()) { + if (cont->is_exported()) { + // TODO create entry point } - - case Node_TupleType: { - std::vector types; - for (auto elem : type->as()->ops()){ - auto member_type = convert(elem); - types.push_back(member_type.id); - spv_type.size += member_type.size; - - // TODO handle alignment for real - assert(member_type.alignment == 4 || (member_type.size == 0 && member_type.alignment == 1)); - spv_type.alignment = 4; - } - if (spv_type.size == 0) - spv_type.alignment = 1; - spv_type.id = builder_->declare_struct_type(types); - builder_->name(spv_type.id, type->to_string()); - break; - } - - case Node_VariantType: { - assert(type->num_ops() > 0 && "empty variants not supported"); - std::vector payload_type; - for (auto elem : type->as()->ops()){ - auto member_type = convert(elem); - payload_type.push_back(member_type.id); - spv_type.size += member_type.size; - - // TODO handle alignment for real - assert(member_type.alignment == 4 || (member_type.size == 0 && member_type.alignment == 1)); - spv_type.alignment = 4; - } - if (spv_type.size == 0) - spv_type.alignment = 1; - spv_type.payload_id = builder_->declare_struct_type(payload_type); - builder_->name(spv_type.payload_id, type->to_string() + "_payload"); - - std::vector with_tag = { convert(world().type_pu32()).id, spv_type.payload_id}; - spv_type.id = builder_->declare_struct_type(with_tag); - builder_->name(spv_type.id, type->to_string()); - break; - } - - default: - THORIN_UNREACHABLE; } - return types_[type] = spv_type; + builder_->finish(out); + builder_ = nullptr; } void CodeGen::emit(const thorin::Scope& scope) { @@ -187,7 +49,7 @@ void CodeGen::emit(const thorin::Scope& scope) { FnBuilder fn; fn.scope = &scope; fn.file_builder = builder_; - fn.fn_type = convert(entry_->type()).id; + fn.fn_type = convert(entry_->type()).type_id; fn.fn_ret_type = get_codom_type(entry_); current_fn_ = &fn; @@ -215,10 +77,10 @@ void CodeGen::emit(const thorin::Scope& scope) { if (is_mem(param) || is_unit(param)) { // Nothing } else if (param->order() == 0) { - auto param_t = convert(param->type()); + auto& param_t = convert(param->type()); fn.header.op(spv::Op::OpFunctionParameter, 3); auto id = builder_->generate_fresh_id(); - fn.header.ref_id(param_t.id); + fn.header.ref_id(param_t.type_id); fn.header.ref_id(id); fn.params[param] = id; } @@ -231,7 +93,7 @@ void CodeGen::emit(const thorin::Scope& scope) { // OpPhi requires the full list of predecessors (values, labels) // We don't have that yet! But we will need the Phi node identifier to build the basic blocks ... // To solve this we generate an id for the phi node now, but defer emission of it to a later stage - bb->phis_map[param] = { convert(param->type()).id, builder_->generate_fresh_id(), {} }; + bb->phis_map[param] = {convert(param->type()).type_id, builder_->generate_fresh_id(), {} }; } } } @@ -259,7 +121,7 @@ SpvId CodeGen::get_codom_type(const Continuation* fn) { if (op->isa() || is_type_unit(op)) continue; assert(op->order() == 0); - types.push_back(convert(op).id); + types.push_back(convert(op).type_id); } if (types.empty()) return builder_->void_type; @@ -465,8 +327,8 @@ SpvId CodeGen::emit(const Def* def, BasicBlockBuilder* bb) { if (auto bin = def->isa()) { SpvId lhs = emit(bin->lhs(), bb); SpvId rhs = emit(bin->rhs(), bb); - SpvType result_types = convert(def->type()); - SpvId result_type = result_types.id; + ConvertedType& result_types = convert(def->type()); + SpvId result_type = result_types.type_id; if (auto cmp = bin->isa()) { auto type = cmp->lhs()->type(); @@ -568,7 +430,7 @@ SpvId CodeGen::emit(const Def* def, BasicBlockBuilder* bb) { } } else if (auto primlit = def->isa()) { Box box = primlit->value(); - auto type = convert(def->type()).id; + auto type = convert(def->type()).type_id; SpvId constant; switch (primlit->primtype_tag()) { case PrimType_bool: constant = bb->file_builder.bool_constant(type, box.get_bool()); break; @@ -595,31 +457,31 @@ SpvId CodeGen::emit(const Def* def, BasicBlockBuilder* bb) { return val; } } else if (auto variant = def->isa()) { - auto struct_type = def->type()->as(); - std::vector elements; - elements.resize(struct_type->num_ops()); - size_t x = 0; - for (auto& e : struct_type->ops()) { - if (x == variant->index()) - elements[x] = emit(variant->value(), bb); - else - elements[x] = bb->undef(convert(e).id); - x++; - } - auto payload = bb->composite(convert(variant->type()).payload_id, elements); - auto tag = builder_->constant(convert(world().type_pu32()).id, { static_cast(variant->index()) }); + auto variant_type = def->type()->as(); + auto& variant_datatype = (ProductDatatype&) convert(variant_type).datatype; + + auto payload_arr = bb->variable(variant_datatype.elements_types[1]->type_id, spv::StorageClassFunction); + auto& converted_payload_type = convert(variant_type->op(variant->index())); + converted_payload_type.datatype->emit_serialization(*bb, payload_arr, emit(variant->value(), bb)); + auto payload = bb->load(variant_datatype.elements_types[1]->type_id, payload_arr); + + auto tag = builder_->constant(convert(world().type_pu32()).type_id, {static_cast(variant->index()) }); std::vector with_tag = { tag, payload }; - return bb->composite(convert(variant->type()).id, with_tag); + return bb->composite(convert(variant->type()).type_id, with_tag); } else if (auto vextract = def->isa()) { auto variant_type = vextract->value()->type()->as(); + auto& variant_datatype = (ProductDatatype&) convert(variant_type).datatype; + + auto& target_type = convert(def->type()); - auto target_type = convert(def->type()); - auto payload = bb->extract(convert(variant_type).payload_id, emit(vextract->value(), bb), {1}); + auto payload_arr = bb->variable(variant_datatype.elements_types[1]->type_id, spv::StorageClassFunction); + auto payload = bb->extract(variant_datatype.elements_types[1]->type_id, emit(vextract->value(), bb), {1}); + bb->store(payload, payload_arr); - return bb->extract(target_type.id, payload, { static_cast(vextract->index()) }); + return target_type.datatype->emit_deserialization(*bb, payload_arr); } else if (auto vindex = def->isa()) { auto value = emit(vindex->op(0), bb); - return bb->extract(convert(world().type_pu32()).id, value, { 0 }); + return bb->extract(convert(world().type_pu32()).type_id, value, { 0 }); } else if (auto tuple = def->isa()) { std::vector elements; elements.resize(tuple->num_ops()); @@ -627,7 +489,7 @@ SpvId CodeGen::emit(const Def* def, BasicBlockBuilder* bb) { for (auto& e : tuple->ops()) { elements[x++] = emit(e, bb); } - return bb->composite(convert(tuple->type()).id, elements); + return bb->composite(convert(tuple->type()).type_id, elements); } else if (auto structagg = def->isa()) { std::vector elements; elements.resize(structagg->num_ops()); @@ -635,7 +497,7 @@ SpvId CodeGen::emit(const Def* def, BasicBlockBuilder* bb) { for (auto& e : structagg->ops()) { elements[x++] = emit(e, bb); } - return bb->composite(convert(structagg->type()).id, elements); + return bb->composite(convert(structagg->type()).type_id, elements); } assertf(false, "Incomplete emit(def) definition"); } diff --git a/src/thorin/be/spirv/spirv.h b/src/thorin/be/spirv/spirv.h index fda32aa2d..07765d5ee 100644 --- a/src/thorin/be/spirv/spirv.h +++ b/src/thorin/be/spirv/spirv.h @@ -8,14 +8,18 @@ namespace thorin::spirv { using SpvId = builder::SpvId; -struct SpvType { - SpvId id; - size_t size = 0; +class CodeGen; +struct Datatype; - // TODO: Alignment rules are complicated and client API dependant - size_t alignment = 0; +struct ConvertedType { + CodeGen* code_gen; + SpvId type_id { 0 }; + std::unique_ptr datatype; - SpvId payload_id = { 0 }; + // TODO: delete + // SpvId payload_id { 0 }; + + bool is_known_size() { return datatype != nullptr; } }; struct FnBuilder; @@ -42,11 +46,12 @@ class CodeGen : public thorin::CodeGen { void emit_stream(std::ostream& stream) override; const char* file_ext() const override { return ".spv"; } + + ConvertedType& convert(const Type*); protected: void structure_loops(); void structure_flow(); - SpvType convert(const Type*); void emit(const Scope& scope); void emit_epilogue(Continuation*, BasicBlockBuilder* bb); SpvId emit(const Def* def, BasicBlockBuilder* bb); @@ -56,8 +61,57 @@ class CodeGen : public thorin::CodeGen { builder::SpvFileBuilder* builder_ = nullptr; Continuation* entry_ = nullptr; FnBuilder* current_fn_ = nullptr; - TypeMap types_; + TypeMap types_; DefMap defs_; + +}; + +/// Thorin data types are mapped to SPIR-V in non-trivial ways, this interface is used by the emission code to abstract over +/// potentially different mappings, depending on the capabilities of the target platform. The serdes code deals with pointers +/// in arrays of unsigned 32 bit words, and is there to get around the limitation of not being able to bitcast pointers in the +/// logical addressing mode. +struct Datatype { +public: + ConvertedType& type; + Datatype(ConvertedType& type) : type(type) {} + + virtual size_t serialized_size() = 0; + virtual void emit_serialization(BasicBlockBuilder& bb, SpvId output, SpvId data) = 0; + virtual SpvId emit_deserialization(BasicBlockBuilder& bb, SpvId input) = 0; +}; + +/// For scalar datatypes +struct ScalarDatatype : public Datatype { + int type_tag; + size_t size_in_bytes; + size_t alignment; + ScalarDatatype(ConvertedType& type, int type_tag, size_t size_in_bytes, size_t alignment_in_bytes); + + size_t serialized_size() override { return size_in_bytes / 4; }; + SpvId emit_deserialization(BasicBlockBuilder& bb, SpvId input) override; + void emit_serialization(BasicBlockBuilder& bb, SpvId output, SpvId data) override; +}; + +struct DefiniteArrayDatatype : public Datatype { + ConvertedType& element_type; + size_t length; + + DefiniteArrayDatatype(ConvertedType& type, ConvertedType& element_type, size_t length); + + size_t serialized_size() override { return element_type.datatype->serialized_size(); }; + SpvId emit_deserialization(BasicBlockBuilder& bb, SpvId input) override; + void emit_serialization(BasicBlockBuilder& bb, SpvId output, SpvId data) override; +}; + +struct ProductDatatype : public Datatype { + std::vector elements_types; + size_t total_size = 0; + + ProductDatatype(ConvertedType& type, const std::vector&& elements_types); + + size_t serialized_size() override { return total_size; }; + SpvId emit_deserialization(BasicBlockBuilder& bb, SpvId input) override; + void emit_serialization(BasicBlockBuilder& bb, SpvId output, SpvId data) override; }; } diff --git a/src/thorin/be/spirv/spirv_builder.hpp b/src/thorin/be/spirv/spirv_builder.hpp index 773af85a2..da1cc6eca 100644 --- a/src/thorin/be/spirv/spirv_builder.hpp +++ b/src/thorin/be/spirv/spirv_builder.hpp @@ -124,6 +124,18 @@ struct SpvBasicBlockBuilder : public SpvSectionBuilder { return id; } + SpvId ptr_access_chain(SpvId target_type, SpvId base, SpvId element, std::vector& indexes) { + op(spv::Op::OpPtrAccessChain, 5 + indexes.size()); + auto id = generate_fresh_id(); + ref_id(target_type); + ref_id(id); + ref_id(base); + ref_id(element); + for (auto index : indexes) + ref_id(index); + return id; + } + SpvId load(SpvId target_type, SpvId pointer) { op(spv::Op::OpLoad, 4); auto id = generate_fresh_id(); diff --git a/src/thorin/be/spirv/spirv_datatypes.cpp b/src/thorin/be/spirv/spirv_datatypes.cpp new file mode 100644 index 000000000..60bf2c013 --- /dev/null +++ b/src/thorin/be/spirv/spirv_datatypes.cpp @@ -0,0 +1,219 @@ +#include "thorin/be/spirv/spirv.h" + +namespace thorin::spirv { + +ScalarDatatype::ScalarDatatype(ConvertedType& type, int type_tag, size_t size_in_bytes, size_t alignment_in_bytes) +: Datatype(type), type_tag(type_tag), size_in_bytes(size_in_bytes), alignment(alignment_in_bytes) +{ + /// currently limited to 32-bit + assert(size_in_bytes == 4); +} + +SpvId ScalarDatatype::emit_deserialization(BasicBlockBuilder& bb, SpvId input) { + auto loaded = bb.load(type.type_id, input); + return bb.bitcast(type.type_id, loaded); +} + +void ScalarDatatype::emit_serialization(BasicBlockBuilder& bb, SpvId output, SpvId data) { + SpvId u32_tid = type.code_gen->convert(type.code_gen->world().type_pu32()).type_id; + auto casted = bb.bitcast(u32_tid, data); + bb.store(casted, output); +} + +DefiniteArrayDatatype::DefiniteArrayDatatype(ConvertedType& type, ConvertedType& element_type, size_t length) : Datatype(type), element_type(element_type), length(length) { + assert(element_type.datatype.get() != nullptr); +} + +SpvId DefiniteArrayDatatype::emit_deserialization(BasicBlockBuilder& bb, SpvId input) { + SpvId i32_tid = type.code_gen->convert(type.code_gen->world().type_ps32()).type_id; + std::vector indices; + std::vector elements; + for (size_t i = 0; i < length; i++) { + SpvId element_ptr = bb.ptr_access_chain(element_type.type_id, input, bb.file_builder.constant(i32_tid, { (uint32_t) (i * element_type.datatype->serialized_size()) }), indices); + SpvId element = element_type.datatype->emit_deserialization(bb, element_ptr); + elements.push_back(element); + } + return bb.composite(type.type_id, elements); +} +void DefiniteArrayDatatype::emit_serialization(BasicBlockBuilder& bb, SpvId output, SpvId data) { + THORIN_UNREACHABLE; +} + +ProductDatatype::ProductDatatype(ConvertedType& type, const std::vector&& elements_types) : Datatype(type), elements_types(elements_types) { + for (auto& element_type : elements_types) { + total_size += element_type->datatype->serialized_size(); + } +} + +SpvId ProductDatatype::emit_deserialization(BasicBlockBuilder& bb, SpvId input) { + SpvId i32_tid = type.code_gen->convert(type.code_gen->world().type_pu32()).type_id; + std::vector indices; + std::vector elements; + size_t offset = 0; + for (auto& element_type : elements_types) { + SpvId element_ptr = bb.ptr_access_chain(element_type->type_id, input, bb.file_builder.constant(i32_tid, { (uint32_t) offset }), indices); + SpvId element = element_type->datatype->emit_deserialization(bb, element_ptr); + offset += element_type->datatype->serialized_size(); + elements.push_back(element); + } + return bb.composite(type.type_id, elements); +} +void ProductDatatype::emit_serialization(BasicBlockBuilder& bb, SpvId output, SpvId data) { + THORIN_UNREACHABLE; +} + +ConvertedType& CodeGen::convert(const Type* type) { + if (auto iter = types_.find(type); iter != types_.end()) return iter->second; + + assert(!type->isa()); + ConvertedType& converted = types_.emplace(type, ConvertedType {this } ).first->second; + switch (type->tag()) { + // Boolean types are typically packed intelligently when declaring in local variables, however with vanilla Vulkan 1.0 they can only be represented via 32-bit integers + // Using extensions, we could use 16 or 8-bit ints instead + // We can also pack them inside structures using bit-twiddling tricks, if the need arises + case PrimType_bool: + converted.type_id = builder_->declare_bool_type(); + converted.datatype = std::make_unique(converted, type->tag(), 1, 4); + break; + case PrimType_ps8: case PrimType_qs8: case PrimType_pu8: case PrimType_qu8: assert(false && "TODO: look into capabilities to enable this"); + case PrimType_ps16: case PrimType_qs16: case PrimType_pu16: case PrimType_qu16: assert(false && "TODO: look into capabilities to enable this"); + case PrimType_ps32: case PrimType_qs32: + converted.type_id = builder_->declare_int_type(32, true ); + converted.datatype = std::make_unique(converted, type->tag(), 4, 4); + break; + case PrimType_pu32: case PrimType_qu32: + converted.type_id = builder_->declare_int_type(32, false); + converted.datatype = std::make_unique(converted, type->tag(), 4, 4); + break; + case PrimType_ps64: case PrimType_qs64: case PrimType_pu64: case PrimType_qu64: assert(false && "TODO: look into capabilities to enable this"); + case PrimType_pf16: case PrimType_qf16: assert(false && "TODO: look into capabilities to enable this"); + case PrimType_pf32: case PrimType_qf32: + converted.type_id = builder_->declare_float_type(32); + converted.datatype = std::make_unique(converted, type->tag(), 4, 4); + break; + case PrimType_pf64: case PrimType_qf64: assert(false && "TODO: look into capabilities to enable this"); + case Node_PtrType: { + auto ptr = type->as(); + spv::StorageClass storage_class; + switch (ptr->addr_space()) { + case AddrSpace::Function: storage_class = spv::StorageClassFunction; break; + case AddrSpace::Private: storage_class = spv::StorageClassPrivate; break; + default: + assert(false && "This address space is not supported"); + break; + } + ConvertedType& element = convert(ptr->pointee()); + converted.type_id = builder_->declare_ptr_type(storage_class, element.type_id); + break; + } + case Node_IndefiniteArrayType: { + assert(false && "TODO"); + // auto array = type->as(); + // return types_[type] = spv_type; + THORIN_UNREACHABLE; + } + case Node_DefiniteArrayType: { + auto array = type->as(); + ConvertedType& element = convert(array->elem_type()); + SpvId size = builder_->constant(convert(world().type_pu32()).type_id, {(uint32_t) array->dim() }); + converted.type_id = builder_->declare_array_type(element.type_id, size); + converted.datatype = std::make_unique(converted, element, array->dim()); + break; + } + + case Node_ClosureType: + case Node_FnType: { + // extract "return" type, collect all other types + auto fn = type->as(); + ConvertedType* ret = nullptr; + std::vector ops; + for (auto op : fn->ops()) { + if (op->isa() || op == world().unit()) continue; + auto fn_type = op->isa(); + if (fn_type && !op->isa()) { + assert(!ret && "only one 'return' supported"); + std::vector ret_types; + for (auto fn_op : fn_type->ops()) { + if (fn_op->isa() || fn_op == world().unit()) continue; + ret_types.push_back(&convert(fn_op)); + } + if (ret_types.empty()) ret = &convert(world().tuple_type({})); + else if (ret_types.size() == 1) ret = ret_types.back(); + else assert(false && "Didn't we refactor this out yet by making functions single-argument ?"); + } else + ops.push_back(convert(op).type_id); + } + assert(ret); + + if (type->tag() == Node_FnType) { + converted.type_id = builder_->declare_fn_type(ops, ret->type_id); + } else { + assert(false && "TODO: handle closure mess"); + THORIN_UNREACHABLE; + } + break; + } + + case Node_StructType: { + std::vector types; + std::vector spv_types; + for (auto elem : type->as()->ops()) { + auto& member_type = convert(elem); + types.push_back(&member_type); + spv_types.push_back(member_type.type_id); + } + converted.type_id = builder_->declare_struct_type(spv_types); + builder_->name(converted.type_id, type->to_string()); + converted.datatype = std::make_unique(converted, std::move(types)); + break; + } + + case Node_TupleType: { + std::vector types; + std::vector spv_types; + for (auto elem : type->as()->ops()){ + auto& member_type = convert(elem); + types.push_back(&member_type); + spv_types.push_back(member_type.type_id); + } + converted.type_id = builder_->declare_struct_type(spv_types); + builder_->name(converted.type_id, type->to_string()); + converted.datatype = std::make_unique(converted, std::move(types)); + break; + } + + case Node_VariantType: { + assert(type->num_ops() > 0 && "empty variants not supported"); + auto tag_type = world().type_pu32(); + ConvertedType& converted_tag_type = convert(tag_type); + + size_t max_serialized_size = 0; + //std::vector types; + //std::vector spv_types; + for (auto elem : type->as()->ops()){ + auto& member_type = convert(elem); + //types.push_back(&member_type); + //spv_types.push_back(member_type.type_id); + + if (member_type.datatype->serialized_size() > max_serialized_size) + max_serialized_size = member_type.datatype->serialized_size(); + } + + auto payload_type = world().definite_array_type(world().type_pu32(), max_serialized_size); + auto& converted_payload_type = convert(payload_type); + + std::vector spv_pair = {converted_tag_type.type_id, converted_payload_type.type_id }; + converted.type_id = builder_->declare_struct_type(spv_pair); + converted.datatype = std::make_unique(converted, std::vector { &converted_tag_type, &converted_payload_type }); + builder_->name(converted.type_id, type->to_string()); + break; + } + + default: + THORIN_UNREACHABLE; + } + + return converted; +} + +} \ No newline at end of file From 5e1b55ac64f46d88359a8d03801d4370510aa5a4 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Fri, 16 Apr 2021 14:19:52 +0200 Subject: [PATCH 052/342] use pointers for ConvertedType --- src/thorin/be/spirv/spirv.cpp | 46 ++++----- src/thorin/be/spirv/spirv.h | 22 ++-- src/thorin/be/spirv/spirv_datatypes.cpp | 131 +++++++++++++----------- 3 files changed, 105 insertions(+), 94 deletions(-) diff --git a/src/thorin/be/spirv/spirv.cpp b/src/thorin/be/spirv/spirv.cpp index 15a1cdb05..20207ea06 100644 --- a/src/thorin/be/spirv/spirv.cpp +++ b/src/thorin/be/spirv/spirv.cpp @@ -49,7 +49,7 @@ void CodeGen::emit(const thorin::Scope& scope) { FnBuilder fn; fn.scope = &scope; fn.file_builder = builder_; - fn.fn_type = convert(entry_->type()).type_id; + fn.fn_type = convert(entry_->type())->type_id; fn.fn_ret_type = get_codom_type(entry_); current_fn_ = &fn; @@ -77,10 +77,10 @@ void CodeGen::emit(const thorin::Scope& scope) { if (is_mem(param) || is_unit(param)) { // Nothing } else if (param->order() == 0) { - auto& param_t = convert(param->type()); + auto param_t = convert(param->type()); fn.header.op(spv::Op::OpFunctionParameter, 3); auto id = builder_->generate_fresh_id(); - fn.header.ref_id(param_t.type_id); + fn.header.ref_id(param_t->type_id); fn.header.ref_id(id); fn.params[param] = id; } @@ -93,7 +93,7 @@ void CodeGen::emit(const thorin::Scope& scope) { // OpPhi requires the full list of predecessors (values, labels) // We don't have that yet! But we will need the Phi node identifier to build the basic blocks ... // To solve this we generate an id for the phi node now, but defer emission of it to a later stage - bb->phis_map[param] = {convert(param->type()).type_id, builder_->generate_fresh_id(), {} }; + bb->phis_map[param] = {convert(param->type())->type_id, builder_->generate_fresh_id(), {} }; } } } @@ -121,7 +121,7 @@ SpvId CodeGen::get_codom_type(const Continuation* fn) { if (op->isa() || is_type_unit(op)) continue; assert(op->order() == 0); - types.push_back(convert(op).type_id); + types.push_back(convert(op)->type_id); } if (types.empty()) return builder_->void_type; @@ -327,8 +327,8 @@ SpvId CodeGen::emit(const Def* def, BasicBlockBuilder* bb) { if (auto bin = def->isa()) { SpvId lhs = emit(bin->lhs(), bb); SpvId rhs = emit(bin->rhs(), bb); - ConvertedType& result_types = convert(def->type()); - SpvId result_type = result_types.type_id; + ConvertedType* result_types = convert(def->type()); + SpvId result_type = result_types->type_id; if (auto cmp = bin->isa()) { auto type = cmp->lhs()->type(); @@ -430,7 +430,7 @@ SpvId CodeGen::emit(const Def* def, BasicBlockBuilder* bb) { } } else if (auto primlit = def->isa()) { Box box = primlit->value(); - auto type = convert(def->type()).type_id; + auto type = convert(def->type())->type_id; SpvId constant; switch (primlit->primtype_tag()) { case PrimType_bool: constant = bb->file_builder.bool_constant(type, box.get_bool()); break; @@ -458,30 +458,30 @@ SpvId CodeGen::emit(const Def* def, BasicBlockBuilder* bb) { } } else if (auto variant = def->isa()) { auto variant_type = def->type()->as(); - auto& variant_datatype = (ProductDatatype&) convert(variant_type).datatype; + auto variant_datatype = (ProductDatatype*) convert(variant_type)->datatype.get(); - auto payload_arr = bb->variable(variant_datatype.elements_types[1]->type_id, spv::StorageClassFunction); - auto& converted_payload_type = convert(variant_type->op(variant->index())); - converted_payload_type.datatype->emit_serialization(*bb, payload_arr, emit(variant->value(), bb)); - auto payload = bb->load(variant_datatype.elements_types[1]->type_id, payload_arr); + auto payload_arr = bb->variable(variant_datatype->elements_types[1]->type_id, spv::StorageClassFunction); + auto converted_payload_type = convert(variant_type->op(variant->index())); + converted_payload_type->datatype->emit_serialization(*bb, payload_arr, emit(variant->value(), bb)); + auto payload = bb->load(variant_datatype->elements_types[1]->type_id, payload_arr); - auto tag = builder_->constant(convert(world().type_pu32()).type_id, {static_cast(variant->index()) }); + auto tag = builder_->constant(convert(world().type_pu32())->type_id, {static_cast(variant->index()) }); std::vector with_tag = { tag, payload }; - return bb->composite(convert(variant->type()).type_id, with_tag); + return bb->composite(convert(variant->type())->type_id, with_tag); } else if (auto vextract = def->isa()) { auto variant_type = vextract->value()->type()->as(); - auto& variant_datatype = (ProductDatatype&) convert(variant_type).datatype; + auto variant_datatype = (ProductDatatype*) convert(variant_type)->datatype.get(); - auto& target_type = convert(def->type()); + auto target_type = convert(def->type()); - auto payload_arr = bb->variable(variant_datatype.elements_types[1]->type_id, spv::StorageClassFunction); - auto payload = bb->extract(variant_datatype.elements_types[1]->type_id, emit(vextract->value(), bb), {1}); + auto payload_arr = bb->variable(variant_datatype->elements_types[1]->type_id, spv::StorageClassFunction); + auto payload = bb->extract(variant_datatype->elements_types[1]->type_id, emit(vextract->value(), bb), {1}); bb->store(payload, payload_arr); - return target_type.datatype->emit_deserialization(*bb, payload_arr); + return target_type->datatype->emit_deserialization(*bb, payload_arr); } else if (auto vindex = def->isa()) { auto value = emit(vindex->op(0), bb); - return bb->extract(convert(world().type_pu32()).type_id, value, { 0 }); + return bb->extract(convert(world().type_pu32())->type_id, value, { 0 }); } else if (auto tuple = def->isa()) { std::vector elements; elements.resize(tuple->num_ops()); @@ -489,7 +489,7 @@ SpvId CodeGen::emit(const Def* def, BasicBlockBuilder* bb) { for (auto& e : tuple->ops()) { elements[x++] = emit(e, bb); } - return bb->composite(convert(tuple->type()).type_id, elements); + return bb->composite(convert(tuple->type())->type_id, elements); } else if (auto structagg = def->isa()) { std::vector elements; elements.resize(structagg->num_ops()); @@ -497,7 +497,7 @@ SpvId CodeGen::emit(const Def* def, BasicBlockBuilder* bb) { for (auto& e : structagg->ops()) { elements[x++] = emit(e, bb); } - return bb->composite(convert(structagg->type()).type_id, elements); + return bb->composite(convert(structagg->type())->type_id, elements); } assertf(false, "Incomplete emit(def) definition"); } diff --git a/src/thorin/be/spirv/spirv.h b/src/thorin/be/spirv/spirv.h index 07765d5ee..baf2c4909 100644 --- a/src/thorin/be/spirv/spirv.h +++ b/src/thorin/be/spirv/spirv.h @@ -16,9 +16,7 @@ struct ConvertedType { SpvId type_id { 0 }; std::unique_ptr datatype; - // TODO: delete - // SpvId payload_id { 0 }; - + ConvertedType(CodeGen* cg) : code_gen(cg) {} bool is_known_size() { return datatype != nullptr; } }; @@ -47,7 +45,7 @@ class CodeGen : public thorin::CodeGen { void emit_stream(std::ostream& stream) override; const char* file_ext() const override { return ".spv"; } - ConvertedType& convert(const Type*); + ConvertedType* convert(const Type*); protected: void structure_loops(); void structure_flow(); @@ -61,7 +59,7 @@ class CodeGen : public thorin::CodeGen { builder::SpvFileBuilder* builder_ = nullptr; Continuation* entry_ = nullptr; FnBuilder* current_fn_ = nullptr; - TypeMap types_; + TypeMap> types_; DefMap defs_; }; @@ -72,8 +70,8 @@ class CodeGen : public thorin::CodeGen { /// logical addressing mode. struct Datatype { public: - ConvertedType& type; - Datatype(ConvertedType& type) : type(type) {} + ConvertedType* type; + Datatype(ConvertedType* type) : type(type) {} virtual size_t serialized_size() = 0; virtual void emit_serialization(BasicBlockBuilder& bb, SpvId output, SpvId data) = 0; @@ -85,7 +83,7 @@ struct ScalarDatatype : public Datatype { int type_tag; size_t size_in_bytes; size_t alignment; - ScalarDatatype(ConvertedType& type, int type_tag, size_t size_in_bytes, size_t alignment_in_bytes); + ScalarDatatype(ConvertedType* type, int type_tag, size_t size_in_bytes, size_t alignment_in_bytes); size_t serialized_size() override { return size_in_bytes / 4; }; SpvId emit_deserialization(BasicBlockBuilder& bb, SpvId input) override; @@ -93,12 +91,12 @@ struct ScalarDatatype : public Datatype { }; struct DefiniteArrayDatatype : public Datatype { - ConvertedType& element_type; + ConvertedType* element_type; size_t length; - DefiniteArrayDatatype(ConvertedType& type, ConvertedType& element_type, size_t length); + DefiniteArrayDatatype(ConvertedType* type, ConvertedType* element_type, size_t length); - size_t serialized_size() override { return element_type.datatype->serialized_size(); }; + size_t serialized_size() override { return element_type->datatype->serialized_size(); }; SpvId emit_deserialization(BasicBlockBuilder& bb, SpvId input) override; void emit_serialization(BasicBlockBuilder& bb, SpvId output, SpvId data) override; }; @@ -107,7 +105,7 @@ struct ProductDatatype : public Datatype { std::vector elements_types; size_t total_size = 0; - ProductDatatype(ConvertedType& type, const std::vector&& elements_types); + ProductDatatype(ConvertedType* type, const std::vector&& elements_types); size_t serialized_size() override { return total_size; }; SpvId emit_deserialization(BasicBlockBuilder& bb, SpvId input) override; diff --git a/src/thorin/be/spirv/spirv_datatypes.cpp b/src/thorin/be/spirv/spirv_datatypes.cpp index 60bf2c013..04c5c18f0 100644 --- a/src/thorin/be/spirv/spirv_datatypes.cpp +++ b/src/thorin/be/spirv/spirv_datatypes.cpp @@ -2,7 +2,7 @@ namespace thorin::spirv { -ScalarDatatype::ScalarDatatype(ConvertedType& type, int type_tag, size_t size_in_bytes, size_t alignment_in_bytes) +ScalarDatatype::ScalarDatatype(ConvertedType* type, int type_tag, size_t size_in_bytes, size_t alignment_in_bytes) : Datatype(type), type_tag(type_tag), size_in_bytes(size_in_bytes), alignment(alignment_in_bytes) { /// currently limited to 32-bit @@ -10,43 +10,48 @@ ScalarDatatype::ScalarDatatype(ConvertedType& type, int type_tag, size_t size_in } SpvId ScalarDatatype::emit_deserialization(BasicBlockBuilder& bb, SpvId input) { - auto loaded = bb.load(type.type_id, input); - return bb.bitcast(type.type_id, loaded); + auto loaded = bb.load(type->type_id, input); + return bb.bitcast(type->type_id, loaded); } void ScalarDatatype::emit_serialization(BasicBlockBuilder& bb, SpvId output, SpvId data) { - SpvId u32_tid = type.code_gen->convert(type.code_gen->world().type_pu32()).type_id; + SpvId u32_tid = type->code_gen->convert(type->code_gen->world().type_pu32())->type_id; auto casted = bb.bitcast(u32_tid, data); bb.store(casted, output); } -DefiniteArrayDatatype::DefiniteArrayDatatype(ConvertedType& type, ConvertedType& element_type, size_t length) : Datatype(type), element_type(element_type), length(length) { - assert(element_type.datatype.get() != nullptr); +DefiniteArrayDatatype::DefiniteArrayDatatype(ConvertedType* type, ConvertedType* element_type, size_t length) : Datatype(type), element_type(element_type), length(length) { + assert(element_type->datatype.get() != nullptr); } SpvId DefiniteArrayDatatype::emit_deserialization(BasicBlockBuilder& bb, SpvId input) { - SpvId i32_tid = type.code_gen->convert(type.code_gen->world().type_ps32()).type_id; + SpvId i32_tid = type->code_gen->convert(type->code_gen->world().type_ps32())->type_id; std::vector indices; std::vector elements; for (size_t i = 0; i < length; i++) { - SpvId element_ptr = bb.ptr_access_chain(element_type.type_id, input, bb.file_builder.constant(i32_tid, { (uint32_t) (i * element_type.datatype->serialized_size()) }), indices); - SpvId element = element_type.datatype->emit_deserialization(bb, element_ptr); + SpvId element_ptr = bb.ptr_access_chain(element_type->type_id, input, bb.file_builder.constant(i32_tid, { (uint32_t) (i * element_type->datatype->serialized_size()) }), indices); + SpvId element = element_type->datatype->emit_deserialization(bb, element_ptr); elements.push_back(element); } - return bb.composite(type.type_id, elements); + return bb.composite(type->type_id, elements); } void DefiniteArrayDatatype::emit_serialization(BasicBlockBuilder& bb, SpvId output, SpvId data) { - THORIN_UNREACHABLE; + std::vector indices; + SpvId i32_tid = type->code_gen->convert(type->code_gen->world().type_ps32())->type_id; + for (size_t i = 0; i < length; i++) { + SpvId element_ptr = bb.ptr_access_chain(element_type->type_id, output, bb.file_builder.constant(i32_tid, { (uint32_t) (i * element_type->datatype->serialized_size()) }), indices); + element_type->datatype->emit_serialization(bb, element_ptr, bb.extract(element_type->type_id, data, { (uint32_t) i })); + } } -ProductDatatype::ProductDatatype(ConvertedType& type, const std::vector&& elements_types) : Datatype(type), elements_types(elements_types) { +ProductDatatype::ProductDatatype(ConvertedType* type, const std::vector&& elements_types) : Datatype(type), elements_types(elements_types) { for (auto& element_type : elements_types) { total_size += element_type->datatype->serialized_size(); } } SpvId ProductDatatype::emit_deserialization(BasicBlockBuilder& bb, SpvId input) { - SpvId i32_tid = type.code_gen->convert(type.code_gen->world().type_pu32()).type_id; + SpvId i32_tid = type->code_gen->convert(type->code_gen->world().type_pu32())->type_id; std::vector indices; std::vector elements; size_t offset = 0; @@ -56,40 +61,47 @@ SpvId ProductDatatype::emit_deserialization(BasicBlockBuilder& bb, SpvId input) offset += element_type->datatype->serialized_size(); elements.push_back(element); } - return bb.composite(type.type_id, elements); + return bb.composite(type->type_id, elements); } void ProductDatatype::emit_serialization(BasicBlockBuilder& bb, SpvId output, SpvId data) { - THORIN_UNREACHABLE; + SpvId i32_tid = type->code_gen->convert(type->code_gen->world().type_pu32())->type_id; + std::vector indices; + size_t offset = 0; + int i = 0; + for (auto& element_type : elements_types) { + SpvId element_ptr = bb.ptr_access_chain(element_type->type_id, output, bb.file_builder.constant(i32_tid, { (uint32_t) offset }), indices); + element_type->datatype->emit_serialization(bb, element_ptr, bb.extract(element_type->type_id, data, { (uint32_t) i++ })); + } } -ConvertedType& CodeGen::convert(const Type* type) { - if (auto iter = types_.find(type); iter != types_.end()) return iter->second; +ConvertedType* CodeGen::convert(const Type* type) { + if (auto iter = types_.find(type); iter != types_.end()) return iter->second.get(); assert(!type->isa()); - ConvertedType& converted = types_.emplace(type, ConvertedType {this } ).first->second; + ConvertedType* converted = types_.emplace(type, std::make_unique(this) ).first->second.get(); switch (type->tag()) { // Boolean types are typically packed intelligently when declaring in local variables, however with vanilla Vulkan 1.0 they can only be represented via 32-bit integers // Using extensions, we could use 16 or 8-bit ints instead // We can also pack them inside structures using bit-twiddling tricks, if the need arises case PrimType_bool: - converted.type_id = builder_->declare_bool_type(); - converted.datatype = std::make_unique(converted, type->tag(), 1, 4); + converted->type_id = builder_->declare_bool_type(); + converted->datatype = std::make_unique(converted, type->tag(), 4, 4); break; case PrimType_ps8: case PrimType_qs8: case PrimType_pu8: case PrimType_qu8: assert(false && "TODO: look into capabilities to enable this"); case PrimType_ps16: case PrimType_qs16: case PrimType_pu16: case PrimType_qu16: assert(false && "TODO: look into capabilities to enable this"); case PrimType_ps32: case PrimType_qs32: - converted.type_id = builder_->declare_int_type(32, true ); - converted.datatype = std::make_unique(converted, type->tag(), 4, 4); + converted->type_id = builder_->declare_int_type(32, true ); + converted->datatype = std::make_unique(converted, type->tag(), 4, 4); break; case PrimType_pu32: case PrimType_qu32: - converted.type_id = builder_->declare_int_type(32, false); - converted.datatype = std::make_unique(converted, type->tag(), 4, 4); + converted->type_id = builder_->declare_int_type(32, false); + converted->datatype = std::make_unique(converted, type->tag(), 4, 4); break; case PrimType_ps64: case PrimType_qs64: case PrimType_pu64: case PrimType_qu64: assert(false && "TODO: look into capabilities to enable this"); case PrimType_pf16: case PrimType_qf16: assert(false && "TODO: look into capabilities to enable this"); case PrimType_pf32: case PrimType_qf32: - converted.type_id = builder_->declare_float_type(32); - converted.datatype = std::make_unique(converted, type->tag(), 4, 4); + converted->type_id = builder_->declare_float_type(32); + converted->datatype = std::make_unique(converted, type->tag(), 4, 4); break; case PrimType_pf64: case PrimType_qf64: assert(false && "TODO: look into capabilities to enable this"); case Node_PtrType: { @@ -102,8 +114,8 @@ ConvertedType& CodeGen::convert(const Type* type) { assert(false && "This address space is not supported"); break; } - ConvertedType& element = convert(ptr->pointee()); - converted.type_id = builder_->declare_ptr_type(storage_class, element.type_id); + ConvertedType* element = convert(ptr->pointee()); + converted->type_id = builder_->declare_ptr_type(storage_class, element->type_id); break; } case Node_IndefiniteArrayType: { @@ -114,10 +126,10 @@ ConvertedType& CodeGen::convert(const Type* type) { } case Node_DefiniteArrayType: { auto array = type->as(); - ConvertedType& element = convert(array->elem_type()); - SpvId size = builder_->constant(convert(world().type_pu32()).type_id, {(uint32_t) array->dim() }); - converted.type_id = builder_->declare_array_type(element.type_id, size); - converted.datatype = std::make_unique(converted, element, array->dim()); + ConvertedType* element = convert(array->elem_type()); + SpvId size = builder_->constant(convert(world().type_pu32())->type_id, {(uint32_t) array->dim() }); + converted->type_id = builder_->declare_array_type(element->type_id, size); + converted->datatype = std::make_unique(converted, element, array->dim()); break; } @@ -135,18 +147,18 @@ ConvertedType& CodeGen::convert(const Type* type) { std::vector ret_types; for (auto fn_op : fn_type->ops()) { if (fn_op->isa() || fn_op == world().unit()) continue; - ret_types.push_back(&convert(fn_op)); + ret_types.push_back(convert(fn_op)); } - if (ret_types.empty()) ret = &convert(world().tuple_type({})); + if (ret_types.empty()) ret = convert(world().tuple_type({})); else if (ret_types.size() == 1) ret = ret_types.back(); else assert(false && "Didn't we refactor this out yet by making functions single-argument ?"); } else - ops.push_back(convert(op).type_id); + ops.push_back(convert(op)->type_id); } assert(ret); if (type->tag() == Node_FnType) { - converted.type_id = builder_->declare_fn_type(ops, ret->type_id); + converted->type_id = builder_->declare_fn_type(ops, ret->type_id); } else { assert(false && "TODO: handle closure mess"); THORIN_UNREACHABLE; @@ -158,13 +170,13 @@ ConvertedType& CodeGen::convert(const Type* type) { std::vector types; std::vector spv_types; for (auto elem : type->as()->ops()) { - auto& member_type = convert(elem); - types.push_back(&member_type); - spv_types.push_back(member_type.type_id); + auto member_type = convert(elem); + types.push_back(member_type); + spv_types.push_back(member_type->type_id); } - converted.type_id = builder_->declare_struct_type(spv_types); - builder_->name(converted.type_id, type->to_string()); - converted.datatype = std::make_unique(converted, std::move(types)); + converted->type_id = builder_->declare_struct_type(spv_types); + builder_->name(converted->type_id, type->to_string()); + converted->datatype = std::make_unique(converted, std::move(types)); break; } @@ -172,40 +184,41 @@ ConvertedType& CodeGen::convert(const Type* type) { std::vector types; std::vector spv_types; for (auto elem : type->as()->ops()){ - auto& member_type = convert(elem); - types.push_back(&member_type); - spv_types.push_back(member_type.type_id); + auto member_type = convert(elem); + types.push_back(member_type); + spv_types.push_back(member_type->type_id); } - converted.type_id = builder_->declare_struct_type(spv_types); - builder_->name(converted.type_id, type->to_string()); - converted.datatype = std::make_unique(converted, std::move(types)); + converted->type_id = builder_->declare_struct_type(spv_types); + builder_->name(converted->type_id, type->to_string()); + converted->datatype = std::make_unique(converted, std::move(types)); break; } case Node_VariantType: { assert(type->num_ops() > 0 && "empty variants not supported"); auto tag_type = world().type_pu32(); - ConvertedType& converted_tag_type = convert(tag_type); + ConvertedType* converted_tag_type = convert(tag_type); size_t max_serialized_size = 0; //std::vector types; //std::vector spv_types; for (auto elem : type->as()->ops()){ - auto& member_type = convert(elem); - //types.push_back(&member_type); - //spv_types.push_back(member_type.type_id); + auto member_type = convert(elem); - if (member_type.datatype->serialized_size() > max_serialized_size) - max_serialized_size = member_type.datatype->serialized_size(); + if (member_type->datatype->serialized_size() > max_serialized_size) + max_serialized_size = member_type->datatype->serialized_size(); } auto payload_type = world().definite_array_type(world().type_pu32(), max_serialized_size); - auto& converted_payload_type = convert(payload_type); + auto* converted_payload_type = convert(payload_type); + + std::vector spv_pair = {converted_tag_type->type_id, converted_payload_type->type_id }; + converted->type_id = builder_->declare_struct_type(spv_pair); + + // auto oh_god_why = std::vector ( &converted_tag_type, &converted_payload_type ); - std::vector spv_pair = {converted_tag_type.type_id, converted_payload_type.type_id }; - converted.type_id = builder_->declare_struct_type(spv_pair); - converted.datatype = std::make_unique(converted, std::vector { &converted_tag_type, &converted_payload_type }); - builder_->name(converted.type_id, type->to_string()); + converted->datatype = std::make_unique(converted, std::vector { converted_tag_type, converted_payload_type }); + builder_->name(converted->type_id, type->to_string()); break; } From 93ffecae1189dbba7f2505a66054e2c19a1f3ead Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Mon, 19 Apr 2021 09:03:38 +0200 Subject: [PATCH 053/342] fixup serialization --- src/thorin/be/spirv/spirv.cpp | 36 +++++++---- src/thorin/be/spirv/spirv.h | 3 +- src/thorin/be/spirv/spirv_builder.hpp | 2 +- src/thorin/be/spirv/spirv_datatypes.cpp | 79 ++++++++++++++----------- 4 files changed, 75 insertions(+), 45 deletions(-) diff --git a/src/thorin/be/spirv/spirv.cpp b/src/thorin/be/spirv/spirv.cpp index 20207ea06..257220349 100644 --- a/src/thorin/be/spirv/spirv.cpp +++ b/src/thorin/be/spirv/spirv.cpp @@ -460,25 +460,41 @@ SpvId CodeGen::emit(const Def* def, BasicBlockBuilder* bb) { auto variant_type = def->type()->as(); auto variant_datatype = (ProductDatatype*) convert(variant_type)->datatype.get(); - auto payload_arr = bb->variable(variant_datatype->elements_types[1]->type_id, spv::StorageClassFunction); - auto converted_payload_type = convert(variant_type->op(variant->index())); - converted_payload_type->datatype->emit_serialization(*bb, payload_arr, emit(variant->value(), bb)); - auto payload = bb->load(variant_datatype->elements_types[1]->type_id, payload_arr); - - auto tag = builder_->constant(convert(world().type_pu32())->type_id, {static_cast(variant->index()) }); - std::vector with_tag = { tag, payload }; - return bb->composite(convert(variant->type())->type_id, with_tag); + if (variant_datatype->elements_types.size() > 1) { + auto ptr_type = convert(world().ptr_type(variant_datatype->elements_types[1]->src_type, 1, 4, AddrSpace::Function))->type_id; + auto payload_arr = bb->variable(ptr_type, spv::StorageClassFunction); + auto converted_payload_type = convert(variant_type->op(variant->index())); + + auto zero = bb->file_builder.constant(convert(world().type_ps32())->type_id, { 0 }); + auto ptr_arr = bb->ptr_access_chain(convert(world().type_pu32())->type_id, payload_arr, zero, { zero }); + + converted_payload_type->datatype->emit_serialization(*bb, ptr_arr, emit(variant->value(), bb)); + auto payload = bb->load(variant_datatype->elements_types[1]->type_id, payload_arr); + + auto tag = builder_->constant(convert(world().type_pu32())->type_id, {static_cast(variant->index())}); + std::vector with_tag = {tag, payload}; + return bb->composite(convert(variant->type())->type_id, with_tag); + } else { + // Zero-sized payload case + auto tag = builder_->constant(convert(world().type_pu32())->type_id, {static_cast(variant->index())}); + std::vector with_tag = { tag }; + return bb->composite(convert(variant->type())->type_id, with_tag); + } } else if (auto vextract = def->isa()) { auto variant_type = vextract->value()->type()->as(); auto variant_datatype = (ProductDatatype*) convert(variant_type)->datatype.get(); auto target_type = convert(def->type()); - auto payload_arr = bb->variable(variant_datatype->elements_types[1]->type_id, spv::StorageClassFunction); + assert(variant_datatype->elements_types.size() > 1 && "Can't extract zero-sized datatypes"); + auto ptr_type = convert(world().ptr_type(variant_datatype->elements_types[1]->src_type, 1, 4, AddrSpace::Function))->type_id; + auto payload_arr = bb->variable(ptr_type, spv::StorageClassFunction); auto payload = bb->extract(variant_datatype->elements_types[1]->type_id, emit(vextract->value(), bb), {1}); bb->store(payload, payload_arr); - return target_type->datatype->emit_deserialization(*bb, payload_arr); + auto zero = bb->file_builder.constant(convert(world().type_ps32())->type_id, { 0 }); + auto ptr_arr = bb->ptr_access_chain(convert(world().type_pu32())->type_id, payload_arr, zero, { zero }); + return target_type->datatype->emit_deserialization(*bb, ptr_arr); } else if (auto vindex = def->isa()) { auto value = emit(vindex->op(0), bb); return bb->extract(convert(world().type_pu32())->type_id, value, { 0 }); diff --git a/src/thorin/be/spirv/spirv.h b/src/thorin/be/spirv/spirv.h index baf2c4909..c45232939 100644 --- a/src/thorin/be/spirv/spirv.h +++ b/src/thorin/be/spirv/spirv.h @@ -12,7 +12,8 @@ class CodeGen; struct Datatype; struct ConvertedType { - CodeGen* code_gen; + spirv::CodeGen* code_gen; + const thorin::Type* src_type; SpvId type_id { 0 }; std::unique_ptr datatype; diff --git a/src/thorin/be/spirv/spirv_builder.hpp b/src/thorin/be/spirv/spirv_builder.hpp index da1cc6eca..9454dc618 100644 --- a/src/thorin/be/spirv/spirv_builder.hpp +++ b/src/thorin/be/spirv/spirv_builder.hpp @@ -124,7 +124,7 @@ struct SpvBasicBlockBuilder : public SpvSectionBuilder { return id; } - SpvId ptr_access_chain(SpvId target_type, SpvId base, SpvId element, std::vector& indexes) { + SpvId ptr_access_chain(SpvId target_type, SpvId base, SpvId element, std::vector indexes) { op(spv::Op::OpPtrAccessChain, 5 + indexes.size()); auto id = generate_fresh_id(); ref_id(target_type); diff --git a/src/thorin/be/spirv/spirv_datatypes.cpp b/src/thorin/be/spirv/spirv_datatypes.cpp index 04c5c18f0..85763df97 100644 --- a/src/thorin/be/spirv/spirv_datatypes.cpp +++ b/src/thorin/be/spirv/spirv_datatypes.cpp @@ -1,4 +1,5 @@ #include "thorin/be/spirv/spirv.h" +#include "thorin/util/stream.h" namespace thorin::spirv { @@ -22,6 +23,7 @@ void ScalarDatatype::emit_serialization(BasicBlockBuilder& bb, SpvId output, Spv DefiniteArrayDatatype::DefiniteArrayDatatype(ConvertedType* type, ConvertedType* element_type, size_t length) : Datatype(type), element_type(element_type), length(length) { assert(element_type->datatype.get() != nullptr); + assert(length > 0 && "Array lengths of zero are not supported"); } SpvId DefiniteArrayDatatype::emit_deserialization(BasicBlockBuilder& bb, SpvId input) { @@ -51,6 +53,7 @@ ProductDatatype::ProductDatatype(ConvertedType* type, const std::vector 0 && "It doesn't make sense to de-serialize Unit!"); SpvId i32_tid = type->code_gen->convert(type->code_gen->world().type_pu32())->type_id; std::vector indices; std::vector elements; @@ -64,6 +67,7 @@ SpvId ProductDatatype::emit_deserialization(BasicBlockBuilder& bb, SpvId input) return bb.composite(type->type_id, elements); } void ProductDatatype::emit_serialization(BasicBlockBuilder& bb, SpvId output, SpvId data) { + assert(total_size > 0 && "It doesn't make sense to serialize Unit!"); SpvId i32_tid = type->code_gen->convert(type->code_gen->world().type_pu32())->type_id; std::vector indices; size_t offset = 0; @@ -79,6 +83,7 @@ ConvertedType* CodeGen::convert(const Type* type) { assert(!type->isa()); ConvertedType* converted = types_.emplace(type, std::make_unique(this) ).first->second.get(); + converted->src_type = type; switch (type->tag()) { // Boolean types are typically packed intelligently when declaring in local variables, however with vanilla Vulkan 1.0 they can only be represented via 32-bit integers // Using extensions, we could use 16 or 8-bit ints instead @@ -166,28 +171,27 @@ ConvertedType* CodeGen::convert(const Type* type) { break; } - case Node_StructType: { - std::vector types; - std::vector spv_types; - for (auto elem : type->as()->ops()) { - auto member_type = convert(elem); - types.push_back(member_type); - spv_types.push_back(member_type->type_id); - } - converted->type_id = builder_->declare_struct_type(spv_types); - builder_->name(converted->type_id, type->to_string()); - converted->datatype = std::make_unique(converted, std::move(types)); - break; - } - + case Node_StructType: case Node_TupleType: { std::vector types; std::vector spv_types; - for (auto elem : type->as()->ops()){ - auto member_type = convert(elem); - types.push_back(member_type); - spv_types.push_back(member_type->type_id); + size_t total_serialized_size = 0; + for (auto member_type : type->ops()) { + if (member_type == world().unit() || member_type == world().mem_type()) { + outf("skipped one"); + continue; + } + auto converted_member_type = convert(member_type); + types.push_back(converted_member_type); + spv_types.push_back(converted_member_type->type_id); + total_serialized_size = converted_member_type->datatype->serialized_size(); } + if (total_serialized_size == 0) { + outf("this one is void"); + converted->type_id = builder_->void_type; + break; + } + converted->type_id = builder_->declare_struct_type(spv_types); builder_->name(converted->type_id, type->to_string()); converted->datatype = std::make_unique(converted, std::move(types)); @@ -200,28 +204,37 @@ ConvertedType* CodeGen::convert(const Type* type) { ConvertedType* converted_tag_type = convert(tag_type); size_t max_serialized_size = 0; - //std::vector types; - //std::vector spv_types; - for (auto elem : type->as()->ops()){ - auto member_type = convert(elem); - - if (member_type->datatype->serialized_size() > max_serialized_size) - max_serialized_size = member_type->datatype->serialized_size(); + for (auto member_type : type->as()->ops()) { + if (member_type == world().unit() || member_type == world().mem_type()) { + outf("skipped one"); + continue; + } + auto converted_member_type = convert(member_type); + if (converted_member_type->datatype->serialized_size() > max_serialized_size) + max_serialized_size = converted_member_type->datatype->serialized_size(); } - auto payload_type = world().definite_array_type(world().type_pu32(), max_serialized_size); - auto* converted_payload_type = convert(payload_type); - - std::vector spv_pair = {converted_tag_type->type_id, converted_payload_type->type_id }; - converted->type_id = builder_->declare_struct_type(spv_pair); - - // auto oh_god_why = std::vector ( &converted_tag_type, &converted_payload_type ); + if (max_serialized_size > 0) { + auto payload_type = world().definite_array_type(world().type_pu32(), max_serialized_size); + auto* converted_payload_type = convert(payload_type); - converted->datatype = std::make_unique(converted, std::vector { converted_tag_type, converted_payload_type }); + std::vector spv_pair = {converted_tag_type->type_id, converted_payload_type->type_id}; + converted->type_id = builder_->declare_struct_type(spv_pair); + converted->datatype = std::make_unique(converted, std::vector{ converted_tag_type, converted_payload_type }); + } else { + // We keep this useless level of struct so the rest of the code doesn't need a special path to extract the tag + std::vector spv_singleton = { converted_tag_type->type_id }; + converted->type_id = builder_->declare_struct_type(spv_singleton); + converted->datatype = std::make_unique(converted, std::vector{ converted_tag_type }); + } builder_->name(converted->type_id, type->to_string()); break; } + case Node_MemType: { + assert(false && "TODO: get arround this"); + } + default: THORIN_UNREACHABLE; } From 76652387aab8000e7c08a4cf22b105ebbdf76cdb Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Mon, 19 Apr 2021 09:14:36 +0200 Subject: [PATCH 054/342] map precise/quick types to the same thing --- src/thorin/be/spirv/spirv_datatypes.cpp | 32 ++++++++++++++++++------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/src/thorin/be/spirv/spirv_datatypes.cpp b/src/thorin/be/spirv/spirv_datatypes.cpp index 85763df97..35e1b8c54 100644 --- a/src/thorin/be/spirv/spirv_datatypes.cpp +++ b/src/thorin/be/spirv/spirv_datatypes.cpp @@ -79,6 +79,18 @@ void ProductDatatype::emit_serialization(BasicBlockBuilder& bb, SpvId output, Sp } ConvertedType* CodeGen::convert(const Type* type) { + // Spir-V requires each primitive type to be "unique", it doesn't allow for example two 32-bit signed integer types. + // Therefore we must enforce that precise/quick types map to the same thing. + switch (type->tag()) { +#define THORIN_Q_TYPE(T, M) \ + case PrimType_##T: \ + type = world().prim_type(PrimType_p##M, 1); \ + break; +#include "thorin/tables/primtypetable.h" +#undef THORIN_Q_TYPE + default: break; + } + if (auto iter = types_.find(type); iter != types_.end()) return iter->second.get(); assert(!type->isa()); @@ -88,27 +100,31 @@ ConvertedType* CodeGen::convert(const Type* type) { // Boolean types are typically packed intelligently when declaring in local variables, however with vanilla Vulkan 1.0 they can only be represented via 32-bit integers // Using extensions, we could use 16 or 8-bit ints instead // We can also pack them inside structures using bit-twiddling tricks, if the need arises + // Note: this only affects storing booleans inside structures, for regular variables the actual spir-v bool type is used. case PrimType_bool: converted->type_id = builder_->declare_bool_type(); converted->datatype = std::make_unique(converted, type->tag(), 4, 4); break; - case PrimType_ps8: case PrimType_qs8: case PrimType_pu8: case PrimType_qu8: assert(false && "TODO: look into capabilities to enable this"); - case PrimType_ps16: case PrimType_qs16: case PrimType_pu16: case PrimType_qu16: assert(false && "TODO: look into capabilities to enable this"); - case PrimType_ps32: case PrimType_qs32: + case PrimType_ps8: assert(false && "TODO: look into capabilities to enable this"); + case PrimType_pu8: assert(false && "TODO: look into capabilities to enable this"); + case PrimType_ps16: assert(false && "TODO: look into capabilities to enable this"); + case PrimType_pu16: assert(false && "TODO: look into capabilities to enable this"); + case PrimType_ps32: converted->type_id = builder_->declare_int_type(32, true ); converted->datatype = std::make_unique(converted, type->tag(), 4, 4); break; - case PrimType_pu32: case PrimType_qu32: + case PrimType_pu32: converted->type_id = builder_->declare_int_type(32, false); converted->datatype = std::make_unique(converted, type->tag(), 4, 4); break; - case PrimType_ps64: case PrimType_qs64: case PrimType_pu64: case PrimType_qu64: assert(false && "TODO: look into capabilities to enable this"); - case PrimType_pf16: case PrimType_qf16: assert(false && "TODO: look into capabilities to enable this"); - case PrimType_pf32: case PrimType_qf32: + case PrimType_ps64: assert(false && "TODO: look into capabilities to enable this"); + case PrimType_pu64: assert(false && "TODO: look into capabilities to enable this"); + case PrimType_pf16: assert(false && "TODO: look into capabilities to enable this"); + case PrimType_pf32: converted->type_id = builder_->declare_float_type(32); converted->datatype = std::make_unique(converted, type->tag(), 4, 4); break; - case PrimType_pf64: case PrimType_qf64: assert(false && "TODO: look into capabilities to enable this"); + case PrimType_pf64: assert(false && "TODO: look into capabilities to enable this"); case Node_PtrType: { auto ptr = type->as(); spv::StorageClass storage_class; From 4bc32dd4ba709009c780b9dbc6f42d22aa220a5d Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Mon, 19 Apr 2021 09:19:00 +0200 Subject: [PATCH 055/342] more serdes fixes --- src/thorin/be/spirv/spirv.cpp | 14 ++++++++------ src/thorin/be/spirv/spirv_datatypes.cpp | 3 ++- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/thorin/be/spirv/spirv.cpp b/src/thorin/be/spirv/spirv.cpp index 257220349..a6ec26b99 100644 --- a/src/thorin/be/spirv/spirv.cpp +++ b/src/thorin/be/spirv/spirv.cpp @@ -461,12 +461,13 @@ SpvId CodeGen::emit(const Def* def, BasicBlockBuilder* bb) { auto variant_datatype = (ProductDatatype*) convert(variant_type)->datatype.get(); if (variant_datatype->elements_types.size() > 1) { - auto ptr_type = convert(world().ptr_type(variant_datatype->elements_types[1]->src_type, 1, 4, AddrSpace::Function))->type_id; - auto payload_arr = bb->variable(ptr_type, spv::StorageClassFunction); + auto ptr_type = convert(world().ptr_type(world().type_pu32(), 1, 4, AddrSpace::Function))->type_id; + auto alloc_type = convert(world().ptr_type(variant_datatype->elements_types[1]->src_type, 1, 4, AddrSpace::Function))->type_id; + auto payload_arr = bb->variable(alloc_type, spv::StorageClassFunction); auto converted_payload_type = convert(variant_type->op(variant->index())); auto zero = bb->file_builder.constant(convert(world().type_ps32())->type_id, { 0 }); - auto ptr_arr = bb->ptr_access_chain(convert(world().type_pu32())->type_id, payload_arr, zero, { zero }); + auto ptr_arr = bb->ptr_access_chain(ptr_type, payload_arr, zero, { zero }); converted_payload_type->datatype->emit_serialization(*bb, ptr_arr, emit(variant->value(), bb)); auto payload = bb->load(variant_datatype->elements_types[1]->type_id, payload_arr); @@ -487,13 +488,14 @@ SpvId CodeGen::emit(const Def* def, BasicBlockBuilder* bb) { auto target_type = convert(def->type()); assert(variant_datatype->elements_types.size() > 1 && "Can't extract zero-sized datatypes"); - auto ptr_type = convert(world().ptr_type(variant_datatype->elements_types[1]->src_type, 1, 4, AddrSpace::Function))->type_id; - auto payload_arr = bb->variable(ptr_type, spv::StorageClassFunction); + auto ptr_type = convert(world().ptr_type(world().type_pu32(), 1, 4, AddrSpace::Function))->type_id; + auto alloc_type = convert(world().ptr_type(variant_datatype->elements_types[1]->src_type, 1, 4, AddrSpace::Function))->type_id; + auto payload_arr = bb->variable(alloc_type, spv::StorageClassFunction); auto payload = bb->extract(variant_datatype->elements_types[1]->type_id, emit(vextract->value(), bb), {1}); bb->store(payload, payload_arr); auto zero = bb->file_builder.constant(convert(world().type_ps32())->type_id, { 0 }); - auto ptr_arr = bb->ptr_access_chain(convert(world().type_pu32())->type_id, payload_arr, zero, { zero }); + auto ptr_arr = bb->ptr_access_chain(ptr_type, payload_arr, zero, { zero }); return target_type->datatype->emit_deserialization(*bb, ptr_arr); } else if (auto vindex = def->isa()) { auto value = emit(vindex->op(0), bb); diff --git a/src/thorin/be/spirv/spirv_datatypes.cpp b/src/thorin/be/spirv/spirv_datatypes.cpp index 35e1b8c54..2d2badffb 100644 --- a/src/thorin/be/spirv/spirv_datatypes.cpp +++ b/src/thorin/be/spirv/spirv_datatypes.cpp @@ -11,7 +11,8 @@ ScalarDatatype::ScalarDatatype(ConvertedType* type, int type_tag, size_t size_in } SpvId ScalarDatatype::emit_deserialization(BasicBlockBuilder& bb, SpvId input) { - auto loaded = bb.load(type->type_id, input); + SpvId u32_tid = type->code_gen->convert(type->code_gen->world().type_pu32())->type_id; + auto loaded = bb.load(u32_tid, input); return bb.bitcast(type->type_id, loaded); } From 69e5e89f23249459b4b71ac92326abbd587b4e40 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Mon, 19 Apr 2021 09:33:31 +0200 Subject: [PATCH 056/342] moves variables decls to function start --- src/thorin/be/spirv/spirv.cpp | 6 ++-- src/thorin/be/spirv/spirv.h | 2 ++ src/thorin/be/spirv/spirv_builder.hpp | 41 ++++++++++++++++++++------- 3 files changed, 36 insertions(+), 13 deletions(-) diff --git a/src/thorin/be/spirv/spirv.cpp b/src/thorin/be/spirv/spirv.cpp index a6ec26b99..9c16c9e3f 100644 --- a/src/thorin/be/spirv/spirv.cpp +++ b/src/thorin/be/spirv/spirv.cpp @@ -46,7 +46,7 @@ void CodeGen::emit(const thorin::Scope& scope) { entry_ = scope.entry(); assert(entry_->is_returning()); - FnBuilder fn; + FnBuilder fn(*builder_); fn.scope = &scope; fn.file_builder = builder_; fn.fn_type = convert(entry_->type())->type_id; @@ -463,7 +463,7 @@ SpvId CodeGen::emit(const Def* def, BasicBlockBuilder* bb) { if (variant_datatype->elements_types.size() > 1) { auto ptr_type = convert(world().ptr_type(world().type_pu32(), 1, 4, AddrSpace::Function))->type_id; auto alloc_type = convert(world().ptr_type(variant_datatype->elements_types[1]->src_type, 1, 4, AddrSpace::Function))->type_id; - auto payload_arr = bb->variable(alloc_type, spv::StorageClassFunction); + auto payload_arr = current_fn_->variable(alloc_type, spv::StorageClassFunction); auto converted_payload_type = convert(variant_type->op(variant->index())); auto zero = bb->file_builder.constant(convert(world().type_ps32())->type_id, { 0 }); @@ -490,7 +490,7 @@ SpvId CodeGen::emit(const Def* def, BasicBlockBuilder* bb) { assert(variant_datatype->elements_types.size() > 1 && "Can't extract zero-sized datatypes"); auto ptr_type = convert(world().ptr_type(world().type_pu32(), 1, 4, AddrSpace::Function))->type_id; auto alloc_type = convert(world().ptr_type(variant_datatype->elements_types[1]->src_type, 1, 4, AddrSpace::Function))->type_id; - auto payload_arr = bb->variable(alloc_type, spv::StorageClassFunction); + auto payload_arr = current_fn_->variable(alloc_type, spv::StorageClassFunction); auto payload = bb->extract(variant_datatype->elements_types[1]->type_id, emit(vextract->value(), bb), {1}); bb->store(payload, payload_arr); diff --git a/src/thorin/be/spirv/spirv.h b/src/thorin/be/spirv/spirv.h index c45232939..500c41499 100644 --- a/src/thorin/be/spirv/spirv.h +++ b/src/thorin/be/spirv/spirv.h @@ -37,6 +37,8 @@ struct FnBuilder : public builder::SpvFnBuilder { std::unordered_map bbs_map; ContinuationMap labels; DefMap params; + + explicit FnBuilder(builder::SpvFileBuilder& file_builder) : builder::SpvFnBuilder(file_builder) {} }; class CodeGen : public thorin::CodeGen { diff --git a/src/thorin/be/spirv/spirv_builder.hpp b/src/thorin/be/spirv/spirv_builder.hpp index 9454dc618..953ea0cfb 100644 --- a/src/thorin/be/spirv/spirv_builder.hpp +++ b/src/thorin/be/spirv/spirv_builder.hpp @@ -106,15 +106,6 @@ struct SpvBasicBlockBuilder : public SpvSectionBuilder { return id; } - SpvId variable(SpvId type, spv::StorageClass storage_class) { - op(spv::Op::OpVariable, 4); - ref_id(type); - auto id = generate_fresh_id(); - ref_id(id); - literal_int(storage_class); - return id; - } - SpvId bitcast(SpvId target_type, SpvId value) { op(spv::Op::OpBitcast, 4); auto id = generate_fresh_id(); @@ -207,13 +198,32 @@ struct SpvBasicBlockBuilder : public SpvSectionBuilder { }; struct SpvFnBuilder { -public: + explicit SpvFnBuilder(SpvFileBuilder& file_builder) + : file_builder(file_builder) + {} + + SpvFileBuilder& file_builder; + SpvId fn_type; SpvId fn_ret_type; std::vector bbs_to_emit; // Contains OpFunctionParams SpvSectionBuilder header; + + SpvSectionBuilder variables; + + SpvId variable(SpvId type, spv::StorageClass storage_class) { + variables.op(spv::Op::OpVariable, 4); + variables.ref_id(type); + auto id = generate_fresh_id(); + variables.ref_id(id); + variables.literal_int(storage_class); + return id; + } + +private: + SpvId generate_fresh_id(); }; struct SpvFileBuilder { @@ -322,10 +332,17 @@ struct SpvFileBuilder { for (auto w : fn_builder.header.data_) fn_defs.data_.push_back(w); + bool first = true; for (auto& bb : fn_builder.bbs_to_emit) { fn_defs.op(spv::Op::OpLabel, 2); fn_defs.ref_id(bb->label); + if (first) { + for (auto w : fn_builder.variables.data_) + fn_defs.data_.push_back(w); + first = false; + } + for (auto& phi : bb->phis) { fn_defs.op(spv::Op::OpPhi, 3 + 2 * phi->preds.size()); fn_defs.ref_id(phi->type); @@ -426,4 +443,8 @@ inline SpvId SpvBasicBlockBuilder::generate_fresh_id() { return file_builder.generate_fresh_id(); } +inline SpvId SpvFnBuilder::generate_fresh_id() { + return file_builder.generate_fresh_id(); +} + } \ No newline at end of file From 939c1ba631916d44fd3c86590424958441dff71b Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Thu, 22 Apr 2021 13:48:17 +0200 Subject: [PATCH 057/342] push-constant reading entry points --- src/thorin/be/spirv/spirv.cpp | 44 +++++++++++++++++++++++++-- src/thorin/be/spirv/spirv.h | 5 ++- src/thorin/be/spirv/spirv_builder.hpp | 37 +++++++++++++++++----- 3 files changed, 73 insertions(+), 13 deletions(-) diff --git a/src/thorin/be/spirv/spirv.cpp b/src/thorin/be/spirv/spirv.cpp index 9c16c9e3f..09f09cac3 100644 --- a/src/thorin/be/spirv/spirv.cpp +++ b/src/thorin/be/spirv/spirv.cpp @@ -32,9 +32,47 @@ void CodeGen::emit_stream(std::ostream& out) { Scope::for_each(world(), [&](const Scope& scope) { emit(scope); }); + auto push_constant_arr_type = world().definite_array_type(world().type_pu32(), 128); + auto push_constant_ptr_type = builder.declare_ptr_type(spv::StorageClassPushConstant, convert(push_constant_arr_type)->type_id); + auto push_constant_ptr = builder_->variable(push_constant_ptr_type, spv::StorageClassPushConstant); + + auto entry_pt_signature = builder_->declare_fn_type({}, builder_->void_type); for (auto& cont : world().continuations()) { if (cont->is_exported()) { - // TODO create entry point + assert(defs_.contains(cont)); + SpvId callee = defs_[cont]; + + // TODO name entry points + FnBuilder fn_builder(builder_); + fn_builder.fn_type = entry_pt_signature; + fn_builder.fn_ret_type = builder_->void_type; + + BasicBlockBuilder* bb = fn_builder.bbs.emplace_back(std::make_unique(fn_builder)).get(); + fn_builder.bbs_to_emit.push_back(bb); + + // iterate on cont type and extract the + auto ptr_type = convert(world().ptr_type(world().type_pu32(), 1, 4, AddrSpace::Function))->type_id; + auto zero = bb->file_builder.constant(convert(world().type_ps32())->type_id, { 0 }); + auto ptr_arr = bb->ptr_access_chain(ptr_type, push_constant_ptr, zero, { zero }); + size_t offset = 0; + std::vector args; + for (size_t i = 0; i < cont->num_ops(); i++) { + auto op = cont->op(i); + auto op_type = op->type(); + if (op_type == world().unit() || op_type == world().mem_type() || op_type->isa()) continue; + assert(op_type->order() == 0); + auto converted = convert(op_type); + assert(converted->datatype != nullptr); + SpvId arg = converted->datatype->emit_deserialization(*bb, ptr_arr); + args.push_back(arg); + bb->ptr_access_chain(ptr_type, push_constant_ptr, bb->file_builder.constant(convert(world().type_ps32())->type_id, { (uint32_t) offset }), { }); + offset += converted->datatype->serialized_size(); + } + + bb->call(builder_->void_type, callee, args); + bb->return_void(); + + builder_->define_function(fn_builder); } } @@ -46,11 +84,11 @@ void CodeGen::emit(const thorin::Scope& scope) { entry_ = scope.entry(); assert(entry_->is_returning()); - FnBuilder fn(*builder_); + FnBuilder fn(builder_); fn.scope = &scope; - fn.file_builder = builder_; fn.fn_type = convert(entry_->type())->type_id; fn.fn_ret_type = get_codom_type(entry_); + defs_.emplace(scope.entry(), fn.function_id); current_fn_ = &fn; diff --git a/src/thorin/be/spirv/spirv.h b/src/thorin/be/spirv/spirv.h index 500c41499..3d97a6475 100644 --- a/src/thorin/be/spirv/spirv.h +++ b/src/thorin/be/spirv/spirv.h @@ -31,14 +31,13 @@ struct BasicBlockBuilder : public builder::SpvBasicBlockBuilder { }; struct FnBuilder : public builder::SpvFnBuilder { - const Scope* scope; - builder::SpvFileBuilder* file_builder; + const Scope* scope = nullptr; std::vector> bbs; std::unordered_map bbs_map; ContinuationMap labels; DefMap params; - explicit FnBuilder(builder::SpvFileBuilder& file_builder) : builder::SpvFnBuilder(file_builder) {} + explicit FnBuilder(builder::SpvFileBuilder* file_builder) : builder::SpvFnBuilder(file_builder) {} }; class CodeGen : public thorin::CodeGen { diff --git a/src/thorin/be/spirv/spirv_builder.hpp b/src/thorin/be/spirv/spirv_builder.hpp index 953ea0cfb..5b23e1d91 100644 --- a/src/thorin/be/spirv/spirv_builder.hpp +++ b/src/thorin/be/spirv/spirv_builder.hpp @@ -180,6 +180,18 @@ struct SpvBasicBlockBuilder : public SpvSectionBuilder { literal_int(e); } + SpvId call(SpvId return_type, SpvId callee, std::vector arguments) { + op(spv::Op::OpFunctionCall, 4 + arguments.size()); + auto id = generate_fresh_id(); + ref_id(return_type); + ref_id(id); + ref_id(callee); + + for (auto a : arguments) + ref_id(a); + return id; + } + void return_void() { op(spv::Op::OpReturn, 1); } @@ -198,11 +210,14 @@ struct SpvBasicBlockBuilder : public SpvSectionBuilder { }; struct SpvFnBuilder { - explicit SpvFnBuilder(SpvFileBuilder& file_builder) + explicit SpvFnBuilder(SpvFileBuilder* file_builder) : file_builder(file_builder) - {} + { + function_id = generate_fresh_id(); + } - SpvFileBuilder& file_builder; + SpvFileBuilder* file_builder; + SpvId function_id; SpvId fn_type; SpvId fn_ret_type; @@ -283,7 +298,7 @@ struct SpvFileBuilder { return id; } - SpvId declare_fn_type(std::vector& dom, SpvId codom) { + SpvId declare_fn_type(std::vector dom, SpvId codom) { types_constants.op(spv::Op::OpTypeFunction, 3 + dom.size()); auto id = generate_fresh_id(); types_constants.ref_id(id); @@ -323,8 +338,7 @@ struct SpvFileBuilder { SpvId define_function(SpvFnBuilder& fn_builder) { fn_defs.op(spv::Op::OpFunction, 5); fn_defs.ref_id(fn_builder.fn_ret_type); - auto id = generate_fresh_id(); - fn_defs.ref_id(id); + fn_defs.ref_id(fn_builder.function_id); fn_defs.data_.push_back(spv::FunctionControlMaskNone); fn_defs.ref_id(fn_builder.fn_type); @@ -359,6 +373,15 @@ struct SpvFileBuilder { } fn_defs.op(spv::Op::OpFunctionEnd, 1); + return fn_builder.function_id; + } + + SpvId variable(SpvId type, spv::StorageClass storage_class) { + types_constants.op(spv::Op::OpVariable, 4); + types_constants.ref_id(type); + auto id = generate_fresh_id(); + types_constants.ref_id(id); + types_constants.literal_int(storage_class); return id; } @@ -444,7 +467,7 @@ inline SpvId SpvBasicBlockBuilder::generate_fresh_id() { } inline SpvId SpvFnBuilder::generate_fresh_id() { - return file_builder.generate_fresh_id(); + return file_builder->generate_fresh_id(); } } \ No newline at end of file From 93cae89069d9103f677a7af4b1f19050de102fd4 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Thu, 22 Apr 2021 14:25:12 +0200 Subject: [PATCH 058/342] avoid duplicate definitions --- src/thorin/be/spirv/spirv_builder.hpp | 33 +++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/src/thorin/be/spirv/spirv_builder.hpp b/src/thorin/be/spirv/spirv_builder.hpp index 5b23e1d91..921ac936c 100644 --- a/src/thorin/be/spirv/spirv_builder.hpp +++ b/src/thorin/be/spirv/spirv_builder.hpp @@ -241,7 +241,32 @@ struct SpvFnBuilder { SpvId generate_fresh_id(); }; +inline bool operator==(const SpvId &a, const SpvId &b) { return a.id == b.id; } + struct SpvFileBuilder { + enum UniqueTypeTag { + NONE, + FN_TYPE + }; + + struct UniqueTypeKey { + UniqueTypeTag tag; + std::vector members; + + bool operator==(const UniqueTypeKey &b) const { + return tag == b.tag && members == b.members; + } + }; + + struct UniqueTypeKeyHasher { + size_t operator() (const UniqueTypeKey& key) const { + size_t acc = 0; + for (auto id : key.members) + acc ^= std::hash{}(id.id); + return std::hash{}(key.tag) ^ acc; + } + }; + SpvFileBuilder() : void_type(declare_void_type()) {} @@ -299,12 +324,17 @@ struct SpvFileBuilder { } SpvId declare_fn_type(std::vector dom, SpvId codom) { + auto key = UniqueTypeKey { FN_TYPE, dom }; + key.members.push_back(codom); + if (auto iter = unique_decls.find(key); iter != unique_decls.end()) return iter->second; + types_constants.op(spv::Op::OpTypeFunction, 3 + dom.size()); auto id = generate_fresh_id(); types_constants.ref_id(id); types_constants.ref_id(codom); for (auto arg : dom) types_constants.ref_id(arg); + unique_decls[key] = id; return id; } @@ -411,6 +441,9 @@ struct SpvFileBuilder { SpvSectionBuilder fn_decls; SpvSectionBuilder fn_defs; + // SPIR-V disallows duplicate non-aggregate type declarations, we protect against these with this + std::unordered_map unique_decls; + SpvId declare_void_type() { types_constants.op(spv::Op::OpTypeVoid, 2); auto id = generate_fresh_id(); From 896277fe8d87d069e39a62b3358a8094b5e01f3b Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Thu, 22 Apr 2021 15:54:27 +0200 Subject: [PATCH 059/342] valid entry points --- src/thorin/be/spirv/spirv.cpp | 28 +++++++++++--------- src/thorin/be/spirv/spirv.h | 16 +++++------ src/thorin/be/spirv/spirv_builder.hpp | 23 +++++++++++----- src/thorin/be/spirv/spirv_datatypes.cpp | 35 ++++++++++++++----------- src/thorin/type.h | 1 + 5 files changed, 60 insertions(+), 43 deletions(-) diff --git a/src/thorin/be/spirv/spirv.cpp b/src/thorin/be/spirv/spirv.cpp index 09f09cac3..f44b0429a 100644 --- a/src/thorin/be/spirv/spirv.cpp +++ b/src/thorin/be/spirv/spirv.cpp @@ -21,7 +21,7 @@ void CodeGen::emit_stream(std::ostream& out) { builder::SpvFileBuilder builder; builder_ = &builder; builder_->capability(spv::Capability::CapabilityShader); - builder_->capability(spv::Capability::CapabilityLinkage); + // builder_->capability(spv::Capability::CapabilityLinkage); builder_->capability(spv::Capability::CapabilityVariablePointers); builder_->capability(spv::Capability::CapabilityPhysicalStorageBufferAddresses); @@ -51,28 +51,31 @@ void CodeGen::emit_stream(std::ostream& out) { fn_builder.bbs_to_emit.push_back(bb); // iterate on cont type and extract the - auto ptr_type = convert(world().ptr_type(world().type_pu32(), 1, 4, AddrSpace::Function))->type_id; + auto ptr_type = convert(world().ptr_type(world().type_pu32(), 1, 4, AddrSpace::Push))->type_id; auto zero = bb->file_builder.constant(convert(world().type_ps32())->type_id, { 0 }); auto ptr_arr = bb->ptr_access_chain(ptr_type, push_constant_ptr, zero, { zero }); size_t offset = 0; std::vector args; - for (size_t i = 0; i < cont->num_ops(); i++) { - auto op = cont->op(i); - auto op_type = op->type(); - if (op_type == world().unit() || op_type == world().mem_type() || op_type->isa()) continue; - assert(op_type->order() == 0); - auto converted = convert(op_type); + for (size_t i = 0; i < cont->num_params(); i++) { + auto param = cont->param(i); + auto param_type = param->type(); + if (param_type == world().unit() || param_type == world().mem_type() || param_type->isa()) continue; + assert(param_type->order() == 0); + auto converted = convert(param_type); assert(converted->datatype != nullptr); - SpvId arg = converted->datatype->emit_deserialization(*bb, ptr_arr); + SpvId arg = converted->datatype->emit_deserialization(*bb, spv::StorageClassPushConstant, ptr_arr); args.push_back(arg); - bb->ptr_access_chain(ptr_type, push_constant_ptr, bb->file_builder.constant(convert(world().type_ps32())->type_id, { (uint32_t) offset }), { }); offset += converted->datatype->serialized_size(); + ptr_arr = bb->ptr_access_chain(ptr_type, ptr_arr, bb->file_builder.constant(convert(world().type_ps32())->type_id, { (uint32_t) offset }), { }); } bb->call(builder_->void_type, callee, args); bb->return_void(); builder_->define_function(fn_builder); + builder_->name(fn_builder.function_id, "entry_point_" + cont->name()); + + builder_->declare_entry_point(spv::ExecutionModelGLCompute, fn_builder.function_id, "main", { push_constant_ptr }); } } @@ -150,6 +153,7 @@ void CodeGen::emit(const thorin::Scope& scope) { } builder_->define_function(fn); + builder_->name(fn.function_id, scope.entry()->name()); } SpvId CodeGen::get_codom_type(const Continuation* fn) { @@ -507,7 +511,7 @@ SpvId CodeGen::emit(const Def* def, BasicBlockBuilder* bb) { auto zero = bb->file_builder.constant(convert(world().type_ps32())->type_id, { 0 }); auto ptr_arr = bb->ptr_access_chain(ptr_type, payload_arr, zero, { zero }); - converted_payload_type->datatype->emit_serialization(*bb, ptr_arr, emit(variant->value(), bb)); + converted_payload_type->datatype->emit_serialization(*bb, spv::StorageClassFunction, ptr_arr, emit(variant->value(), bb)); auto payload = bb->load(variant_datatype->elements_types[1]->type_id, payload_arr); auto tag = builder_->constant(convert(world().type_pu32())->type_id, {static_cast(variant->index())}); @@ -534,7 +538,7 @@ SpvId CodeGen::emit(const Def* def, BasicBlockBuilder* bb) { auto zero = bb->file_builder.constant(convert(world().type_ps32())->type_id, { 0 }); auto ptr_arr = bb->ptr_access_chain(ptr_type, payload_arr, zero, { zero }); - return target_type->datatype->emit_deserialization(*bb, ptr_arr); + return target_type->datatype->emit_deserialization(*bb, spv::StorageClassFunction, ptr_arr); } else if (auto vindex = def->isa()) { auto value = emit(vindex->op(0), bb); return bb->extract(convert(world().type_pu32())->type_id, value, { 0 }); diff --git a/src/thorin/be/spirv/spirv.h b/src/thorin/be/spirv/spirv.h index 3d97a6475..1c3718070 100644 --- a/src/thorin/be/spirv/spirv.h +++ b/src/thorin/be/spirv/spirv.h @@ -76,8 +76,8 @@ struct Datatype { Datatype(ConvertedType* type) : type(type) {} virtual size_t serialized_size() = 0; - virtual void emit_serialization(BasicBlockBuilder& bb, SpvId output, SpvId data) = 0; - virtual SpvId emit_deserialization(BasicBlockBuilder& bb, SpvId input) = 0; + virtual void emit_serialization(BasicBlockBuilder& bb, spv::StorageClass storage_class, SpvId output, SpvId data) = 0; + virtual SpvId emit_deserialization(BasicBlockBuilder& bb, spv::StorageClass storage_class, SpvId input) = 0; }; /// For scalar datatypes @@ -88,8 +88,8 @@ struct ScalarDatatype : public Datatype { ScalarDatatype(ConvertedType* type, int type_tag, size_t size_in_bytes, size_t alignment_in_bytes); size_t serialized_size() override { return size_in_bytes / 4; }; - SpvId emit_deserialization(BasicBlockBuilder& bb, SpvId input) override; - void emit_serialization(BasicBlockBuilder& bb, SpvId output, SpvId data) override; + SpvId emit_deserialization(BasicBlockBuilder& bb, spv::StorageClass storage_class, SpvId input) override; + void emit_serialization(BasicBlockBuilder& bb, spv::StorageClass storage_class, SpvId output, SpvId data) override; }; struct DefiniteArrayDatatype : public Datatype { @@ -99,8 +99,8 @@ struct DefiniteArrayDatatype : public Datatype { DefiniteArrayDatatype(ConvertedType* type, ConvertedType* element_type, size_t length); size_t serialized_size() override { return element_type->datatype->serialized_size(); }; - SpvId emit_deserialization(BasicBlockBuilder& bb, SpvId input) override; - void emit_serialization(BasicBlockBuilder& bb, SpvId output, SpvId data) override; + SpvId emit_deserialization(BasicBlockBuilder& bb, spv::StorageClass storage_class, SpvId input) override; + void emit_serialization(BasicBlockBuilder& bb, spv::StorageClass storage_class, SpvId output, SpvId data) override; }; struct ProductDatatype : public Datatype { @@ -110,8 +110,8 @@ struct ProductDatatype : public Datatype { ProductDatatype(ConvertedType* type, const std::vector&& elements_types); size_t serialized_size() override { return total_size; }; - SpvId emit_deserialization(BasicBlockBuilder& bb, SpvId input) override; - void emit_serialization(BasicBlockBuilder& bb, SpvId output, SpvId data) override; + SpvId emit_deserialization(BasicBlockBuilder& bb, spv::StorageClass storage_class, SpvId input) override; + void emit_serialization(BasicBlockBuilder& bb, spv::StorageClass storage_class, SpvId output, SpvId data) override; }; } diff --git a/src/thorin/be/spirv/spirv_builder.hpp b/src/thorin/be/spirv/spirv_builder.hpp index 921ac936c..069df70d2 100644 --- a/src/thorin/be/spirv/spirv_builder.hpp +++ b/src/thorin/be/spirv/spirv_builder.hpp @@ -365,6 +365,15 @@ struct SpvFileBuilder { return id; } + SpvId variable(SpvId type, spv::StorageClass storage_class) { + types_constants.op(spv::Op::OpVariable, 4); + types_constants.ref_id(type); + auto id = generate_fresh_id(); + types_constants.ref_id(id); + types_constants.literal_int(storage_class); + return id; + } + SpvId define_function(SpvFnBuilder& fn_builder) { fn_defs.op(spv::Op::OpFunction, 5); fn_defs.ref_id(fn_builder.fn_ret_type); @@ -406,13 +415,13 @@ struct SpvFileBuilder { return fn_builder.function_id; } - SpvId variable(SpvId type, spv::StorageClass storage_class) { - types_constants.op(spv::Op::OpVariable, 4); - types_constants.ref_id(type); - auto id = generate_fresh_id(); - types_constants.ref_id(id); - types_constants.literal_int(storage_class); - return id; + void declare_entry_point(spv::ExecutionModel execution_model, SpvId entry_point, std::string name, std::vector interface) { + entry_points.op(spv::Op::OpEntryPoint, 3 + div_roundup(name.size() + 1, 4) + interface.size()); + entry_points.literal_int(execution_model); + entry_points.ref_id(entry_point); + entry_points.literal_name(name); + for (auto i : interface) + entry_points.ref_id(i); } void capability(spv::Capability cap) { diff --git a/src/thorin/be/spirv/spirv_datatypes.cpp b/src/thorin/be/spirv/spirv_datatypes.cpp index 2d2badffb..2ba0c6974 100644 --- a/src/thorin/be/spirv/spirv_datatypes.cpp +++ b/src/thorin/be/spirv/spirv_datatypes.cpp @@ -10,13 +10,13 @@ ScalarDatatype::ScalarDatatype(ConvertedType* type, int type_tag, size_t size_in assert(size_in_bytes == 4); } -SpvId ScalarDatatype::emit_deserialization(BasicBlockBuilder& bb, SpvId input) { +SpvId ScalarDatatype::emit_deserialization(BasicBlockBuilder& bb, spv::StorageClass storage_class, SpvId input) { SpvId u32_tid = type->code_gen->convert(type->code_gen->world().type_pu32())->type_id; auto loaded = bb.load(u32_tid, input); return bb.bitcast(type->type_id, loaded); } -void ScalarDatatype::emit_serialization(BasicBlockBuilder& bb, SpvId output, SpvId data) { +void ScalarDatatype::emit_serialization(BasicBlockBuilder& bb, spv::StorageClass storage_class, SpvId output, SpvId data) { SpvId u32_tid = type->code_gen->convert(type->code_gen->world().type_pu32())->type_id; auto casted = bb.bitcast(u32_tid, data); bb.store(casted, output); @@ -27,23 +27,23 @@ DefiniteArrayDatatype::DefiniteArrayDatatype(ConvertedType* type, ConvertedType* assert(length > 0 && "Array lengths of zero are not supported"); } -SpvId DefiniteArrayDatatype::emit_deserialization(BasicBlockBuilder& bb, SpvId input) { +SpvId DefiniteArrayDatatype::emit_deserialization(BasicBlockBuilder& bb, spv::StorageClass storage_class, SpvId input) { SpvId i32_tid = type->code_gen->convert(type->code_gen->world().type_ps32())->type_id; std::vector indices; std::vector elements; for (size_t i = 0; i < length; i++) { SpvId element_ptr = bb.ptr_access_chain(element_type->type_id, input, bb.file_builder.constant(i32_tid, { (uint32_t) (i * element_type->datatype->serialized_size()) }), indices); - SpvId element = element_type->datatype->emit_deserialization(bb, element_ptr); + SpvId element = element_type->datatype->emit_deserialization(bb, storage_class, element_ptr); elements.push_back(element); } return bb.composite(type->type_id, elements); } -void DefiniteArrayDatatype::emit_serialization(BasicBlockBuilder& bb, SpvId output, SpvId data) { +void DefiniteArrayDatatype::emit_serialization(BasicBlockBuilder& bb, spv::StorageClass storage_class, SpvId output, SpvId data) { std::vector indices; SpvId i32_tid = type->code_gen->convert(type->code_gen->world().type_ps32())->type_id; for (size_t i = 0; i < length; i++) { SpvId element_ptr = bb.ptr_access_chain(element_type->type_id, output, bb.file_builder.constant(i32_tid, { (uint32_t) (i * element_type->datatype->serialized_size()) }), indices); - element_type->datatype->emit_serialization(bb, element_ptr, bb.extract(element_type->type_id, data, { (uint32_t) i })); + element_type->datatype->emit_serialization(bb, storage_class, element_ptr, bb.extract(element_type->type_id, data, { (uint32_t) i })); } } @@ -53,29 +53,31 @@ ProductDatatype::ProductDatatype(ConvertedType* type, const std::vector 0 && "It doesn't make sense to de-serialize Unit!"); - SpvId i32_tid = type->code_gen->convert(type->code_gen->world().type_pu32())->type_id; + SpvId u32_tid = type->code_gen->convert(type->code_gen->world().type_pu32())->type_id; + SpvId arr_cell_tid = bb.file_builder.declare_ptr_type(storage_class, u32_tid); std::vector indices; std::vector elements; size_t offset = 0; for (auto& element_type : elements_types) { - SpvId element_ptr = bb.ptr_access_chain(element_type->type_id, input, bb.file_builder.constant(i32_tid, { (uint32_t) offset }), indices); - SpvId element = element_type->datatype->emit_deserialization(bb, element_ptr); + SpvId element_ptr = bb.ptr_access_chain(arr_cell_tid, input, bb.file_builder.constant(u32_tid, { (uint32_t) offset }), indices); + SpvId element = element_type->datatype->emit_deserialization(bb, storage_class, element_ptr); offset += element_type->datatype->serialized_size(); elements.push_back(element); } return bb.composite(type->type_id, elements); } -void ProductDatatype::emit_serialization(BasicBlockBuilder& bb, SpvId output, SpvId data) { +void ProductDatatype::emit_serialization(BasicBlockBuilder& bb, spv::StorageClass storage_class, SpvId output, SpvId data) { assert(total_size > 0 && "It doesn't make sense to serialize Unit!"); - SpvId i32_tid = type->code_gen->convert(type->code_gen->world().type_pu32())->type_id; + SpvId u32_tid = type->code_gen->convert(type->code_gen->world().type_pu32())->type_id; + SpvId arr_cell_tid = bb.file_builder.declare_ptr_type(storage_class, u32_tid); std::vector indices; size_t offset = 0; int i = 0; for (auto& element_type : elements_types) { - SpvId element_ptr = bb.ptr_access_chain(element_type->type_id, output, bb.file_builder.constant(i32_tid, { (uint32_t) offset }), indices); - element_type->datatype->emit_serialization(bb, element_ptr, bb.extract(element_type->type_id, data, { (uint32_t) i++ })); + SpvId element_ptr = bb.ptr_access_chain(arr_cell_tid, output, bb.file_builder.constant(u32_tid, { (uint32_t) offset }), indices); + element_type->datatype->emit_serialization(bb, storage_class, element_ptr, bb.extract(element_type->type_id, data, { (uint32_t) i++ })); } } @@ -130,8 +132,9 @@ ConvertedType* CodeGen::convert(const Type* type) { auto ptr = type->as(); spv::StorageClass storage_class; switch (ptr->addr_space()) { - case AddrSpace::Function: storage_class = spv::StorageClassFunction; break; - case AddrSpace::Private: storage_class = spv::StorageClassPrivate; break; + case AddrSpace::Function: storage_class = spv::StorageClassFunction; break; + case AddrSpace::Private: storage_class = spv::StorageClassPrivate; break; + case AddrSpace::Push : storage_class = spv::StorageClassPushConstant; break; default: assert(false && "This address space is not supported"); break; diff --git a/src/thorin/type.h b/src/thorin/type.h index b5916c7f5..97baf4d39 100644 --- a/src/thorin/type.h +++ b/src/thorin/type.h @@ -235,6 +235,7 @@ enum class AddrSpace : uint32_t { Constant = 4, Private = 5, // Corresponds to the 'private' storage class in SPIR-V Function = 6, // Corresponds to the 'function' storage class in SPIR-V + Push = 7, // Corresponds to the 'push constant' storage class in SPIR-V }; /// Pointer type. From 135cc8939621e9ea429f1270221cf1a65b6d6a83 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Thu, 22 Apr 2021 16:55:05 +0200 Subject: [PATCH 060/342] "support" generic pointers in spir-v --- src/thorin/be/spirv/spirv_datatypes.cpp | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/src/thorin/be/spirv/spirv_datatypes.cpp b/src/thorin/be/spirv/spirv_datatypes.cpp index 2ba0c6974..2a8b9ace5 100644 --- a/src/thorin/be/spirv/spirv_datatypes.cpp +++ b/src/thorin/be/spirv/spirv_datatypes.cpp @@ -134,13 +134,27 @@ ConvertedType* CodeGen::convert(const Type* type) { switch (ptr->addr_space()) { case AddrSpace::Function: storage_class = spv::StorageClassFunction; break; case AddrSpace::Private: storage_class = spv::StorageClassPrivate; break; - case AddrSpace::Push : storage_class = spv::StorageClassPushConstant; break; + case AddrSpace::Push: storage_class = spv::StorageClassPushConstant; break; + case AddrSpace::Global: { + storage_class = spv::StorageClassPhysicalStorageBuffer; + // TODO datatype code for stuffing into push constants + break; + } + case AddrSpace::Generic: { + world().WLOG("Passing a generic pointer to a SPIR-V module. SpirV doesn't know about these, and so this will be passed as a 64 bit integer. Tread carefully !"); + ConvertedType* conv_u64 = convert(world().type_pu64()); + converted->type_id = conv_u64->type_id; + goto ptr_done; + } default: assert(false && "This address space is not supported"); break; } - ConvertedType* element = convert(ptr->pointee()); - converted->type_id = builder_->declare_ptr_type(storage_class, element->type_id); + { + ConvertedType* element = convert(ptr->pointee()); + converted->type_id = builder_->declare_ptr_type(storage_class, element->type_id); + } + ptr_done: break; } case Node_IndefiniteArrayType: { From 368815158bfe79f96a5132c6d2c0cb890e6c47be Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Fri, 23 Apr 2021 13:34:56 +0200 Subject: [PATCH 061/342] reading pointers --- src/thorin/be/spirv/spirv.h | 9 +++++++ src/thorin/be/spirv/spirv_builder.hpp | 29 ++++++++++++++++++++++ src/thorin/be/spirv/spirv_datatypes.cpp | 33 ++++++++++++++++++++----- 3 files changed, 65 insertions(+), 6 deletions(-) diff --git a/src/thorin/be/spirv/spirv.h b/src/thorin/be/spirv/spirv.h index 1c3718070..78a22536e 100644 --- a/src/thorin/be/spirv/spirv.h +++ b/src/thorin/be/spirv/spirv.h @@ -92,6 +92,15 @@ struct ScalarDatatype : public Datatype { void emit_serialization(BasicBlockBuilder& bb, spv::StorageClass storage_class, SpvId output, SpvId data) override; }; +struct PtrDatatype : public Datatype { + static constexpr size_t bitwidth = 64; + PtrDatatype(ConvertedType* type) : Datatype(type) {} + + size_t serialized_size() override { return bitwidth / 32; }; + SpvId emit_deserialization(BasicBlockBuilder& bb, spv::StorageClass storage_class, SpvId input) override; + void emit_serialization(BasicBlockBuilder& bb, spv::StorageClass storage_class, SpvId output, SpvId data) override; +}; + struct DefiniteArrayDatatype : public Datatype { ConvertedType* element_type; size_t length; diff --git a/src/thorin/be/spirv/spirv_builder.hpp b/src/thorin/be/spirv/spirv_builder.hpp index 069df70d2..893075916 100644 --- a/src/thorin/be/spirv/spirv_builder.hpp +++ b/src/thorin/be/spirv/spirv_builder.hpp @@ -115,6 +115,35 @@ struct SpvBasicBlockBuilder : public SpvSectionBuilder { return id; } + /// Change bit-width + SpvId u_convert(SpvId target_type, SpvId value) { + op(spv::Op::OpUConvert, 4); + auto id = generate_fresh_id(); + ref_id(target_type); + ref_id(id); + ref_id(value); + return id; + } + + /// Change bit-width + SpvId s_convert(SpvId target_type, SpvId value) { + op(spv::Op::OpSConvert, 4); + auto id = generate_fresh_id(); + ref_id(target_type); + ref_id(id); + ref_id(value); + return id; + } + + SpvId convert_u_ptr(SpvId target_type, SpvId value) { + op(spv::Op::OpConvertUToPtr, 4); + auto id = generate_fresh_id(); + ref_id(target_type); + ref_id(id); + ref_id(value); + return id; + } + SpvId ptr_access_chain(SpvId target_type, SpvId base, SpvId element, std::vector indexes) { op(spv::Op::OpPtrAccessChain, 5 + indexes.size()); auto id = generate_fresh_id(); diff --git a/src/thorin/be/spirv/spirv_datatypes.cpp b/src/thorin/be/spirv/spirv_datatypes.cpp index 2a8b9ace5..7e8443b62 100644 --- a/src/thorin/be/spirv/spirv_datatypes.cpp +++ b/src/thorin/be/spirv/spirv_datatypes.cpp @@ -22,6 +22,26 @@ void ScalarDatatype::emit_serialization(BasicBlockBuilder& bb, spv::StorageClass bb.store(casted, output); } +SpvId PtrDatatype::emit_deserialization(BasicBlockBuilder& bb, spv::StorageClass storage_class, SpvId input) { + assert(type->src_type->as()->addr_space() == AddrSpace::Global && "Only buffer device address (global memory) pointers supported"); + SpvId u32_tid = type->code_gen->convert(type->code_gen->world().type_pu32())->type_id; + SpvId u64_tid = type->code_gen->convert(type->code_gen->world().type_pu64())->type_id; + SpvId arr_cell_tid = bb.file_builder.declare_ptr_type(storage_class, u32_tid); + + auto input2 = bb.ptr_access_chain(arr_cell_tid, input, bb.file_builder.constant(u32_tid, { (uint32_t) 1 }), { }); + auto upper = bb.u_convert(u64_tid, bb.load(u32_tid, input)); + auto lower = bb.u_convert(u64_tid, bb.load(u32_tid, input2)); + + SpvId c32 = bb.file_builder.constant(u32_tid, { 32 }); + auto merged = bb.binop(spv::OpBitwiseOr, u64_tid, bb.binop(spv::OpShiftLeftLogical, u64_tid, upper, c32), lower); + + return bb.convert_u_ptr(type->type_id, merged); +} + +void PtrDatatype::emit_serialization(BasicBlockBuilder& bb, spv::StorageClass storage_class, SpvId output, SpvId data) { + assert(false && "TODO"); +} + DefiniteArrayDatatype::DefiniteArrayDatatype(ConvertedType* type, ConvertedType* element_type, size_t length) : Datatype(type), element_type(element_type), length(length) { assert(element_type->datatype.get() != nullptr); assert(length > 0 && "Array lengths of zero are not supported"); @@ -137,7 +157,7 @@ ConvertedType* CodeGen::convert(const Type* type) { case AddrSpace::Push: storage_class = spv::StorageClassPushConstant; break; case AddrSpace::Global: { storage_class = spv::StorageClassPhysicalStorageBuffer; - // TODO datatype code for stuffing into push constants + converted->datatype = std::make_unique(converted); break; } case AddrSpace::Generic: { @@ -151,17 +171,18 @@ ConvertedType* CodeGen::convert(const Type* type) { break; } { - ConvertedType* element = convert(ptr->pointee()); + const Type* pointee = ptr->pointee(); + while (auto arr = pointee->isa()) + pointee = arr->elem_type(); + ConvertedType* element = convert(pointee); converted->type_id = builder_->declare_ptr_type(storage_class, element->type_id); } ptr_done: break; } case Node_IndefiniteArrayType: { - assert(false && "TODO"); - // auto array = type->as(); - // return types_[type] = spv_type; - THORIN_UNREACHABLE; + world().ELOG("Using indefinite types directly is not permitted - they may only be pointed to"); + std::abort(); } case Node_DefiniteArrayType: { auto array = type->as(); From 9368bbb82819e533361b6c9ab71bd92cd3631761 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Fri, 23 Apr 2021 13:56:36 +0200 Subject: [PATCH 062/342] loads 64b bda pointers --- src/thorin/be/spirv/spirv.cpp | 13 ++++++----- src/thorin/be/spirv/spirv.h | 2 +- src/thorin/be/spirv/spirv_builder.hpp | 14 ++++++++++++ src/thorin/be/spirv/spirv_datatypes.cpp | 29 +++++++++++++++++++------ 4 files changed, 45 insertions(+), 13 deletions(-) diff --git a/src/thorin/be/spirv/spirv.cpp b/src/thorin/be/spirv/spirv.cpp index f44b0429a..152ff40fe 100644 --- a/src/thorin/be/spirv/spirv.cpp +++ b/src/thorin/be/spirv/spirv.cpp @@ -21,9 +21,12 @@ void CodeGen::emit_stream(std::ostream& out) { builder::SpvFileBuilder builder; builder_ = &builder; builder_->capability(spv::Capability::CapabilityShader); - // builder_->capability(spv::Capability::CapabilityLinkage); builder_->capability(spv::Capability::CapabilityVariablePointers); builder_->capability(spv::Capability::CapabilityPhysicalStorageBufferAddresses); + builder_->capability(spv::Capability::CapabilityInt16); + builder_->capability(spv::Capability::CapabilityInt64); + + builder_->addressing_model = spv::AddressingModelPhysicalStorageBuffer64; structure_loops(); structure_flow(); @@ -119,11 +122,11 @@ void CodeGen::emit(const thorin::Scope& scope) { // Nothing } else if (param->order() == 0) { auto param_t = convert(param->type()); - fn.header.op(spv::Op::OpFunctionParameter, 3); - auto id = builder_->generate_fresh_id(); - fn.header.ref_id(param_t->type_id); - fn.header.ref_id(id); + auto id = fn.parameter(param_t->type_id); fn.params[param] = id; + if (param->type()->isa()) { + builder_->decorate(id, spv::DecorationAliased); + } } } } else { diff --git a/src/thorin/be/spirv/spirv.h b/src/thorin/be/spirv/spirv.h index 78a22536e..56568a9bc 100644 --- a/src/thorin/be/spirv/spirv.h +++ b/src/thorin/be/spirv/spirv.h @@ -87,7 +87,7 @@ struct ScalarDatatype : public Datatype { size_t alignment; ScalarDatatype(ConvertedType* type, int type_tag, size_t size_in_bytes, size_t alignment_in_bytes); - size_t serialized_size() override { return size_in_bytes / 4; }; + size_t serialized_size() override { return (size_in_bytes + 3) / 4; }; SpvId emit_deserialization(BasicBlockBuilder& bb, spv::StorageClass storage_class, SpvId input) override; void emit_serialization(BasicBlockBuilder& bb, spv::StorageClass storage_class, SpvId output, SpvId data) override; }; diff --git a/src/thorin/be/spirv/spirv_builder.hpp b/src/thorin/be/spirv/spirv_builder.hpp index 893075916..8a5f38ae0 100644 --- a/src/thorin/be/spirv/spirv_builder.hpp +++ b/src/thorin/be/spirv/spirv_builder.hpp @@ -257,6 +257,14 @@ struct SpvFnBuilder { SpvSectionBuilder variables; + SpvId parameter(SpvId param_type) { + header.op(spv::Op::OpFunctionParameter, 3); + auto id = generate_fresh_id(); + header.ref_id(param_type); + header.ref_id(id); + return id; + } + SpvId variable(SpvId type, spv::StorageClass storage_class) { variables.op(spv::Op::OpVariable, 4); variables.ref_id(type); @@ -376,6 +384,12 @@ struct SpvFileBuilder { return id; } + void decorate(SpvId target, spv::Decoration decoration) { + annotations.op(spv::Op::OpDecorate, 3); + annotations.ref_id(target); + annotations.literal_int(decoration); + } + SpvId bool_constant(SpvId type, bool value) { types_constants.op(value ? spv::Op::OpConstantTrue : spv::Op::OpConstantFalse, 3); auto id = generate_fresh_id(); diff --git a/src/thorin/be/spirv/spirv_datatypes.cpp b/src/thorin/be/spirv/spirv_datatypes.cpp index 7e8443b62..ec9247fb7 100644 --- a/src/thorin/be/spirv/spirv_datatypes.cpp +++ b/src/thorin/be/spirv/spirv_datatypes.cpp @@ -6,17 +6,20 @@ namespace thorin::spirv { ScalarDatatype::ScalarDatatype(ConvertedType* type, int type_tag, size_t size_in_bytes, size_t alignment_in_bytes) : Datatype(type), type_tag(type_tag), size_in_bytes(size_in_bytes), alignment(alignment_in_bytes) { - /// currently limited to 32-bit - assert(size_in_bytes == 4); + } SpvId ScalarDatatype::emit_deserialization(BasicBlockBuilder& bb, spv::StorageClass storage_class, SpvId input) { + /// currently limited to 32-bit + assert(size_in_bytes == 4); SpvId u32_tid = type->code_gen->convert(type->code_gen->world().type_pu32())->type_id; auto loaded = bb.load(u32_tid, input); return bb.bitcast(type->type_id, loaded); } void ScalarDatatype::emit_serialization(BasicBlockBuilder& bb, spv::StorageClass storage_class, SpvId output, SpvId data) { + /// currently limited to 32-bit + assert(size_in_bytes == 4); SpvId u32_tid = type->code_gen->convert(type->code_gen->world().type_pu32())->type_id; auto casted = bb.bitcast(u32_tid, data); bb.store(casted, output); @@ -126,12 +129,18 @@ ConvertedType* CodeGen::convert(const Type* type) { // Note: this only affects storing booleans inside structures, for regular variables the actual spir-v bool type is used. case PrimType_bool: converted->type_id = builder_->declare_bool_type(); - converted->datatype = std::make_unique(converted, type->tag(), 4, 4); + converted->datatype = std::make_unique(converted, type->tag(), 1, 1); break; case PrimType_ps8: assert(false && "TODO: look into capabilities to enable this"); case PrimType_pu8: assert(false && "TODO: look into capabilities to enable this"); - case PrimType_ps16: assert(false && "TODO: look into capabilities to enable this"); - case PrimType_pu16: assert(false && "TODO: look into capabilities to enable this"); + case PrimType_ps16: + converted->type_id = builder_->declare_int_type(16, true); + converted->datatype = std::make_unique(converted, type->tag(), 2, 2); + break; + case PrimType_pu16: + converted->type_id = builder_->declare_int_type(16, false); + converted->datatype = std::make_unique(converted, type->tag(), 2, 2); + break; case PrimType_ps32: converted->type_id = builder_->declare_int_type(32, true ); converted->datatype = std::make_unique(converted, type->tag(), 4, 4); @@ -140,8 +149,14 @@ ConvertedType* CodeGen::convert(const Type* type) { converted->type_id = builder_->declare_int_type(32, false); converted->datatype = std::make_unique(converted, type->tag(), 4, 4); break; - case PrimType_ps64: assert(false && "TODO: look into capabilities to enable this"); - case PrimType_pu64: assert(false && "TODO: look into capabilities to enable this"); + case PrimType_ps64: + converted->type_id = builder_->declare_int_type(64, true); + converted->datatype = std::make_unique(converted, type->tag(), 8, 8); + break; + case PrimType_pu64: + converted->type_id = builder_->declare_int_type(64, false); + converted->datatype = std::make_unique(converted, type->tag(), 8, 8); + break; case PrimType_pf16: assert(false && "TODO: look into capabilities to enable this"); case PrimType_pf32: converted->type_id = builder_->declare_float_type(32); From 0df66859247b3fdb0ec277bb87230568ff6435b0 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Fri, 23 Apr 2021 14:10:38 +0200 Subject: [PATCH 063/342] do emit mem/unit arguments! --- src/thorin/be/spirv/spirv.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/thorin/be/spirv/spirv.cpp b/src/thorin/be/spirv/spirv.cpp index 152ff40fe..f6a239678 100644 --- a/src/thorin/be/spirv/spirv.cpp +++ b/src/thorin/be/spirv/spirv.cpp @@ -181,9 +181,9 @@ void CodeGen::emit_epilogue(Continuation* continuation, BasicBlockBuilder* bb) { for (auto arg : continuation->args()) { assert(arg->order() == 0); + auto val = emit(arg, bb); if (is_mem(arg) || is_unit(arg)) continue; - auto val = emit(arg, bb); values.emplace_back(val); } @@ -284,8 +284,9 @@ void CodeGen::emit_epilogue(Continuation* continuation, BasicBlockBuilder* bb) { int index = -1; for (auto& arg : continuation->args()) { index++; + auto val = emit(arg, bb); if (is_mem(arg) || is_unit(arg)) continue; - bb->args[arg] = emit(arg, bb); + bb->args[arg] = val; auto* param = callee->param(index); auto& phi = current_fn_->bbs_map[callee]->phis_map[param]; phi.preds.emplace_back(bb->args[arg], current_fn_->labels[continuation]); From d7ee73e218aa0a658f1da5a675dfa2aec895876e Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Fri, 23 Apr 2021 15:19:01 +0200 Subject: [PATCH 064/342] handle lea/store/load --- src/thorin/be/spirv/spirv.cpp | 19 +++++++++++++++++++ src/thorin/be/spirv/spirv_builder.hpp | 12 ++++++++---- 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/src/thorin/be/spirv/spirv.cpp b/src/thorin/be/spirv/spirv.cpp index f6a239678..3953e62c7 100644 --- a/src/thorin/be/spirv/spirv.cpp +++ b/src/thorin/be/spirv/spirv.cpp @@ -369,6 +369,8 @@ void CodeGen::emit_epilogue(Continuation* continuation, BasicBlockBuilder* bb) { } } +constexpr SpvId spv_none { 0 }; + SpvId CodeGen::emit(const Def* def, BasicBlockBuilder* bb) { if (auto bin = def->isa()) { SpvId lhs = emit(bin->lhs(), bb); @@ -562,6 +564,23 @@ SpvId CodeGen::emit(const Def* def, BasicBlockBuilder* bb) { elements[x++] = emit(e, bb); } return bb->composite(convert(structagg->type())->type_id, elements); + } else if (auto access = def->isa()) { + std::vector operands; + auto ptr_type = access->ptr()->type()->as(); + if (ptr_type->addr_space() == AddrSpace::Global) { + operands.push_back(spv::MemoryAccessAlignedMask); + operands.push_back( 4 ); // TODO: SPIR-V docs say to consult client API for valid values. + } + if (auto load = def->isa()) { + return bb->load(convert(load->out_val_type())->type_id, emit(load->ptr(), bb), operands); + } else if (auto store = def->isa()) { + bb->store(emit(store->val(), bb), emit(store->ptr(), bb), operands); + return spv_none; + } else THORIN_UNREACHABLE; + } else if (auto lea = def->isa()) { + auto type = convert(lea->ptr_type()); + auto offset = emit(lea->index(), bb); + return bb->ptr_access_chain(type->type_id, emit(lea->ptr(), bb), offset, {}); } assertf(false, "Incomplete emit(def) definition"); } diff --git a/src/thorin/be/spirv/spirv_builder.hpp b/src/thorin/be/spirv/spirv_builder.hpp index 8a5f38ae0..93aa72ba2 100644 --- a/src/thorin/be/spirv/spirv_builder.hpp +++ b/src/thorin/be/spirv/spirv_builder.hpp @@ -156,19 +156,23 @@ struct SpvBasicBlockBuilder : public SpvSectionBuilder { return id; } - SpvId load(SpvId target_type, SpvId pointer) { - op(spv::Op::OpLoad, 4); + SpvId load(SpvId target_type, SpvId pointer, std::vector operands = {}) { + op(spv::Op::OpLoad, 4 + operands.size()); auto id = generate_fresh_id(); ref_id(target_type); ref_id(id); ref_id(pointer); + for (auto op : operands) + literal_int(op); return id; } - void store(SpvId value, SpvId pointer) { - op(spv::Op::OpStore, 3); + void store(SpvId value, SpvId pointer, std::vector operands = {}) { + op(spv::Op::OpStore, 3 + operands.size()); ref_id(pointer); ref_id(value); + for (auto op : operands) + literal_int(op); } SpvId binop(spv::Op op_, SpvId result_type, SpvId lhs, SpvId rhs) { From 81d27bcf4235614bbbd6cecff03f7f5a20a3b444 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Fri, 23 Apr 2021 19:07:35 +0200 Subject: [PATCH 065/342] expose workgroup size in entry point --- src/thorin/be/llvm/llvm.cpp | 2 +- src/thorin/be/llvm/runtime.h | 3 ++- src/thorin/be/spirv/spirv.cpp | 19 ++++++++++++++----- src/thorin/be/spirv/spirv.h | 2 +- src/thorin/be/spirv/spirv_builder.hpp | 8 ++++++++ 5 files changed, 26 insertions(+), 8 deletions(-) diff --git a/src/thorin/be/llvm/llvm.cpp b/src/thorin/be/llvm/llvm.cpp index e36a33267..028d9edf0 100644 --- a/src/thorin/be/llvm/llvm.cpp +++ b/src/thorin/be/llvm/llvm.cpp @@ -1082,7 +1082,7 @@ Continuation* CodeGen::emit_intrinsic(llvm::IRBuilder<>& irbuilder, Continuation case Intrinsic::NVVM: return runtime_->emit_host_code(*this, irbuilder, Runtime::CUDA_PLATFORM, ".nvvm", continuation); case Intrinsic::OpenCL: return runtime_->emit_host_code(*this, irbuilder, Runtime::OPENCL_PLATFORM, ".cl", continuation); case Intrinsic::AMDGPU: return runtime_->emit_host_code(*this, irbuilder, Runtime::HSA_PLATFORM, ".amdgpu", continuation); - case Intrinsic::SpirV: return runtime_->emit_host_code(*this, irbuilder, Runtime::OPENCL_PLATFORM, ".spv", continuation); // TODO have a real runtime component + case Intrinsic::SpirV: return runtime_->emit_host_code(*this, irbuilder, Runtime::VULKAN_PLATFORM, ".spv", continuation); case Intrinsic::HLS: return emit_hls(irbuilder, continuation); case Intrinsic::Parallel: return emit_parallel(irbuilder, continuation); case Intrinsic::Fibers: return emit_fibers(irbuilder, continuation); diff --git a/src/thorin/be/llvm/runtime.h b/src/thorin/be/llvm/runtime.h index 616595415..9ecf7746b 100644 --- a/src/thorin/be/llvm/runtime.h +++ b/src/thorin/be/llvm/runtime.h @@ -22,7 +22,8 @@ class Runtime { CPU_PLATFORM, CUDA_PLATFORM, OPENCL_PLATFORM, - HSA_PLATFORM + HSA_PLATFORM, + VULKAN_PLATFORM, }; /// Emits a call to anydsl_launch_kernel. diff --git a/src/thorin/be/spirv/spirv.cpp b/src/thorin/be/spirv/spirv.cpp index 3953e62c7..751415c84 100644 --- a/src/thorin/be/spirv/spirv.cpp +++ b/src/thorin/be/spirv/spirv.cpp @@ -13,8 +13,8 @@ namespace thorin { namespace thorin::spirv { -CodeGen::CodeGen(thorin::World& world, Cont2Config&, bool debug) - : thorin::CodeGen(world, debug) +CodeGen::CodeGen(thorin::World& world, Cont2Config& kernel_config, bool debug) + : thorin::CodeGen(world, debug), kernel_config_(kernel_config) {} void CodeGen::emit_stream(std::ostream& out) { @@ -42,10 +42,11 @@ void CodeGen::emit_stream(std::ostream& out) { auto entry_pt_signature = builder_->declare_fn_type({}, builder_->void_type); for (auto& cont : world().continuations()) { if (cont->is_exported()) { - assert(defs_.contains(cont)); + assert(defs_.contains(cont) && kernel_config_.contains(cont)); + auto config = kernel_config_.find(cont); + SpvId callee = defs_[cont]; - // TODO name entry points FnBuilder fn_builder(builder_); fn_builder.fn_type = entry_pt_signature; fn_builder.fn_ret_type = builder_->void_type; @@ -78,7 +79,15 @@ void CodeGen::emit_stream(std::ostream& out) { builder_->define_function(fn_builder); builder_->name(fn_builder.function_id, "entry_point_" + cont->name()); - builder_->declare_entry_point(spv::ExecutionModelGLCompute, fn_builder.function_id, "main", { push_constant_ptr }); + builder_->declare_entry_point(spv::ExecutionModelGLCompute, fn_builder.function_id, "kernel_main", { push_constant_ptr }); + + auto block = config->second->as()->block_size(); + std::vector local_size = { + (uint32_t) std::get<0>(block), + (uint32_t) std::get<1>(block), + (uint32_t) std::get<2>(block), + }; + builder_->execution_mode(fn_builder.function_id, spv::ExecutionModeLocalSize, local_size); } } diff --git a/src/thorin/be/spirv/spirv.h b/src/thorin/be/spirv/spirv.h index 56568a9bc..d5a0d5649 100644 --- a/src/thorin/be/spirv/spirv.h +++ b/src/thorin/be/spirv/spirv.h @@ -63,7 +63,7 @@ class CodeGen : public thorin::CodeGen { FnBuilder* current_fn_ = nullptr; TypeMap> types_; DefMap defs_; - + const Cont2Config& kernel_config_; }; /// Thorin data types are mapped to SPIR-V in non-trivial ways, this interface is used by the emission code to abstract over diff --git a/src/thorin/be/spirv/spirv_builder.hpp b/src/thorin/be/spirv/spirv_builder.hpp index 93aa72ba2..7bfbd4451 100644 --- a/src/thorin/be/spirv/spirv_builder.hpp +++ b/src/thorin/be/spirv/spirv_builder.hpp @@ -471,6 +471,14 @@ struct SpvFileBuilder { entry_points.ref_id(i); } + void execution_mode(SpvId entry_point, spv::ExecutionMode execution_mode, std::vector payloads) { + entry_points.op(spv::Op::OpExecutionMode, 3 + payloads.size()); + entry_points.ref_id(entry_point); + entry_points.literal_int(execution_mode); + for (auto d : payloads) + entry_points.literal_int(d); + } + void capability(spv::Capability cap) { capabilities.op(spv::Op::OpCapability, 2); capabilities.data_.push_back(cap); From 743961c1040e2baafb0edc1a112ac5b160099438 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Fri, 23 Apr 2021 19:40:18 +0200 Subject: [PATCH 066/342] success: validation passes, shader kills NIR --- src/thorin/be/spirv/spirv.cpp | 18 ++++++++++++------ src/thorin/be/spirv/spirv_builder.hpp | 17 ++++++++++++++--- 2 files changed, 26 insertions(+), 9 deletions(-) diff --git a/src/thorin/be/spirv/spirv.cpp b/src/thorin/be/spirv/spirv.cpp index 751415c84..d7f6c765d 100644 --- a/src/thorin/be/spirv/spirv.cpp +++ b/src/thorin/be/spirv/spirv.cpp @@ -35,9 +35,15 @@ void CodeGen::emit_stream(std::ostream& out) { Scope::for_each(world(), [&](const Scope& scope) { emit(scope); }); - auto push_constant_arr_type = world().definite_array_type(world().type_pu32(), 128); - auto push_constant_ptr_type = builder.declare_ptr_type(spv::StorageClassPushConstant, convert(push_constant_arr_type)->type_id); - auto push_constant_ptr = builder_->variable(push_constant_ptr_type, spv::StorageClassPushConstant); + auto push_constant_arr_type = convert(world().definite_array_type(world().type_pu32(), 128))->type_id; + auto push_constant_struct_type = builder.declare_struct_type({ push_constant_arr_type }); + auto push_constant_struct_ptr_type = builder.declare_ptr_type(spv::StorageClassPushConstant, push_constant_struct_type); + builder.name(push_constant_struct_type, "[i32 * 128]"); + builder.decorate(push_constant_struct_type, spv::DecorationBlock); + builder.decorate_member(push_constant_struct_type, 0, spv::DecorationOffset, { 0 }); + builder.decorate(push_constant_arr_type, spv::DecorationArrayStride, { 4 }); + auto push_constant_struct_ptr = builder_->variable(push_constant_struct_ptr_type, spv::StorageClassPushConstant); + builder.name(push_constant_struct_ptr, "push_constant_data"); auto entry_pt_signature = builder_->declare_fn_type({}, builder_->void_type); for (auto& cont : world().continuations()) { @@ -54,10 +60,10 @@ void CodeGen::emit_stream(std::ostream& out) { BasicBlockBuilder* bb = fn_builder.bbs.emplace_back(std::make_unique(fn_builder)).get(); fn_builder.bbs_to_emit.push_back(bb); - // iterate on cont type and extract the + // iterate on cont type and extract the arguments auto ptr_type = convert(world().ptr_type(world().type_pu32(), 1, 4, AddrSpace::Push))->type_id; auto zero = bb->file_builder.constant(convert(world().type_ps32())->type_id, { 0 }); - auto ptr_arr = bb->ptr_access_chain(ptr_type, push_constant_ptr, zero, { zero }); + auto ptr_arr = bb->ptr_access_chain(ptr_type, push_constant_struct_ptr, zero, { zero, zero }); size_t offset = 0; std::vector args; for (size_t i = 0; i < cont->num_params(); i++) { @@ -79,7 +85,7 @@ void CodeGen::emit_stream(std::ostream& out) { builder_->define_function(fn_builder); builder_->name(fn_builder.function_id, "entry_point_" + cont->name()); - builder_->declare_entry_point(spv::ExecutionModelGLCompute, fn_builder.function_id, "kernel_main", { push_constant_ptr }); + builder_->declare_entry_point(spv::ExecutionModelGLCompute, fn_builder.function_id, "kernel_main", { push_constant_struct_ptr }); auto block = config->second->as()->block_size(); std::vector local_size = { diff --git a/src/thorin/be/spirv/spirv_builder.hpp b/src/thorin/be/spirv/spirv_builder.hpp index 7bfbd4451..b52ae61d8 100644 --- a/src/thorin/be/spirv/spirv_builder.hpp +++ b/src/thorin/be/spirv/spirv_builder.hpp @@ -379,7 +379,7 @@ struct SpvFileBuilder { return id; } - SpvId declare_struct_type(std::vector& elements) { + SpvId declare_struct_type(std::vector elements) { types_constants.op(spv::Op::OpTypeStruct, 2 + elements.size()); auto id = generate_fresh_id(); types_constants.ref_id(id); @@ -388,10 +388,21 @@ struct SpvFileBuilder { return id; } - void decorate(SpvId target, spv::Decoration decoration) { - annotations.op(spv::Op::OpDecorate, 3); + void decorate(SpvId target, spv::Decoration decoration, std::vector extra = {}) { + annotations.op(spv::Op::OpDecorate, 3 + extra.size()); annotations.ref_id(target); annotations.literal_int(decoration); + for (auto e : extra) + annotations.literal_int(e); + } + + void decorate_member(SpvId target, uint32_t member, spv::Decoration decoration, std::vector extra = {}) { + annotations.op(spv::Op::OpMemberDecorate, 4 + extra.size()); + annotations.ref_id(target); + annotations.literal_int(member); + annotations.literal_int(decoration); + for (auto e : extra) + annotations.literal_int(e); } SpvId bool_constant(SpvId type, bool value) { From 81f84ea8822510f1d3748238a562f164b098610d Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Tue, 27 Apr 2021 16:35:50 +0200 Subject: [PATCH 067/342] limit usage of OpPtrAccessChain as much as possible --- src/thorin/be/spirv/spirv.cpp | 33 ++++++----- src/thorin/be/spirv/spirv.h | 20 +++---- src/thorin/be/spirv/spirv_builder.hpp | 11 ++++ src/thorin/be/spirv/spirv_datatypes.cpp | 79 ++++++++++++++----------- 4 files changed, 85 insertions(+), 58 deletions(-) diff --git a/src/thorin/be/spirv/spirv.cpp b/src/thorin/be/spirv/spirv.cpp index d7f6c765d..a853bd0f1 100644 --- a/src/thorin/be/spirv/spirv.cpp +++ b/src/thorin/be/spirv/spirv.cpp @@ -38,12 +38,12 @@ void CodeGen::emit_stream(std::ostream& out) { auto push_constant_arr_type = convert(world().definite_array_type(world().type_pu32(), 128))->type_id; auto push_constant_struct_type = builder.declare_struct_type({ push_constant_arr_type }); auto push_constant_struct_ptr_type = builder.declare_ptr_type(spv::StorageClassPushConstant, push_constant_struct_type); - builder.name(push_constant_struct_type, "[i32 * 128]"); + builder.name(push_constant_struct_type, "ThorinPushConstant"); builder.decorate(push_constant_struct_type, spv::DecorationBlock); builder.decorate_member(push_constant_struct_type, 0, spv::DecorationOffset, { 0 }); builder.decorate(push_constant_arr_type, spv::DecorationArrayStride, { 4 }); auto push_constant_struct_ptr = builder_->variable(push_constant_struct_ptr_type, spv::StorageClassPushConstant); - builder.name(push_constant_struct_ptr, "push_constant_data"); + builder.name(push_constant_struct_ptr, "thorin_push_constant_data"); auto entry_pt_signature = builder_->declare_fn_type({}, builder_->void_type); for (auto& cont : world().continuations()) { @@ -61,10 +61,10 @@ void CodeGen::emit_stream(std::ostream& out) { fn_builder.bbs_to_emit.push_back(bb); // iterate on cont type and extract the arguments - auto ptr_type = convert(world().ptr_type(world().type_pu32(), 1, 4, AddrSpace::Push))->type_id; - auto zero = bb->file_builder.constant(convert(world().type_ps32())->type_id, { 0 }); - auto ptr_arr = bb->ptr_access_chain(ptr_type, push_constant_struct_ptr, zero, { zero, zero }); - size_t offset = 0; + auto ptr_type = convert(world().ptr_type(world().definite_array_type(world().type_pu32(), 128), 1, 4, AddrSpace::Push))->type_id; + auto zero = bb->file_builder.constant(convert(world().type_pu32())->type_id, { 0 }); + auto arr_ref = bb->access_chain(ptr_type, push_constant_struct_ptr, { zero }); + uint32_t offset = 0; std::vector args; for (size_t i = 0; i < cont->num_params(); i++) { auto param = cont->param(i); @@ -73,10 +73,9 @@ void CodeGen::emit_stream(std::ostream& out) { assert(param_type->order() == 0); auto converted = convert(param_type); assert(converted->datatype != nullptr); - SpvId arg = converted->datatype->emit_deserialization(*bb, spv::StorageClassPushConstant, ptr_arr); + SpvId arg = converted->datatype->emit_deserialization(*bb, spv::StorageClassPushConstant, arr_ref, bb->file_builder.constant(convert(world().type_pu32())->type_id, { offset })); args.push_back(arg); offset += converted->datatype->serialized_size(); - ptr_arr = bb->ptr_access_chain(ptr_type, ptr_arr, bb->file_builder.constant(convert(world().type_ps32())->type_id, { (uint32_t) offset }), { }); } bb->call(builder_->void_type, callee, args); @@ -529,10 +528,9 @@ SpvId CodeGen::emit(const Def* def, BasicBlockBuilder* bb) { auto payload_arr = current_fn_->variable(alloc_type, spv::StorageClassFunction); auto converted_payload_type = convert(variant_type->op(variant->index())); - auto zero = bb->file_builder.constant(convert(world().type_ps32())->type_id, { 0 }); - auto ptr_arr = bb->ptr_access_chain(ptr_type, payload_arr, zero, { zero }); + auto zero = bb->file_builder.constant(convert(world().type_pu32())->type_id, { 0 }); - converted_payload_type->datatype->emit_serialization(*bb, spv::StorageClassFunction, ptr_arr, emit(variant->value(), bb)); + converted_payload_type->datatype->emit_serialization(*bb, spv::StorageClassFunction, payload_arr, zero, emit(variant->value(), bb)); auto payload = bb->load(variant_datatype->elements_types[1]->type_id, payload_arr); auto tag = builder_->constant(convert(world().type_pu32())->type_id, {static_cast(variant->index())}); @@ -557,9 +555,8 @@ SpvId CodeGen::emit(const Def* def, BasicBlockBuilder* bb) { auto payload = bb->extract(variant_datatype->elements_types[1]->type_id, emit(vextract->value(), bb), {1}); bb->store(payload, payload_arr); - auto zero = bb->file_builder.constant(convert(world().type_ps32())->type_id, { 0 }); - auto ptr_arr = bb->ptr_access_chain(ptr_type, payload_arr, zero, { zero }); - return target_type->datatype->emit_deserialization(*bb, spv::StorageClassFunction, ptr_arr); + auto zero = bb->file_builder.constant(convert(world().type_pu32())->type_id, { 0 }); + return target_type->datatype->emit_deserialization(*bb, spv::StorageClassFunction, payload_arr, zero); } else if (auto vindex = def->isa()) { auto value = emit(vindex->op(0), bb); return bb->extract(convert(world().type_pu32())->type_id, value, { 0 }); @@ -593,6 +590,14 @@ SpvId CodeGen::emit(const Def* def, BasicBlockBuilder* bb) { return spv_none; } else THORIN_UNREACHABLE; } else if (auto lea = def->isa()) { + switch (lea->ptr_type()->addr_space()) { + case AddrSpace::Global: + case AddrSpace::Shared: + break; + default: + world().ELOG("LEA is only allowed in global & shared address spaces"); + break; + } auto type = convert(lea->ptr_type()); auto offset = emit(lea->index(), bb); return bb->ptr_access_chain(type->type_id, emit(lea->ptr(), bb), offset, {}); diff --git a/src/thorin/be/spirv/spirv.h b/src/thorin/be/spirv/spirv.h index d5a0d5649..630ef6153 100644 --- a/src/thorin/be/spirv/spirv.h +++ b/src/thorin/be/spirv/spirv.h @@ -76,8 +76,8 @@ struct Datatype { Datatype(ConvertedType* type) : type(type) {} virtual size_t serialized_size() = 0; - virtual void emit_serialization(BasicBlockBuilder& bb, spv::StorageClass storage_class, SpvId output, SpvId data) = 0; - virtual SpvId emit_deserialization(BasicBlockBuilder& bb, spv::StorageClass storage_class, SpvId input) = 0; + virtual SpvId emit_deserialization(BasicBlockBuilder& bb, spv::StorageClass storage_class, SpvId array, SpvId base_offset) = 0; + virtual void emit_serialization(BasicBlockBuilder& bb, spv::StorageClass storage_class, SpvId array, SpvId base_offset, SpvId data) = 0; }; /// For scalar datatypes @@ -88,8 +88,8 @@ struct ScalarDatatype : public Datatype { ScalarDatatype(ConvertedType* type, int type_tag, size_t size_in_bytes, size_t alignment_in_bytes); size_t serialized_size() override { return (size_in_bytes + 3) / 4; }; - SpvId emit_deserialization(BasicBlockBuilder& bb, spv::StorageClass storage_class, SpvId input) override; - void emit_serialization(BasicBlockBuilder& bb, spv::StorageClass storage_class, SpvId output, SpvId data) override; + SpvId emit_deserialization(BasicBlockBuilder& bb, spv::StorageClass storage_class, SpvId array, SpvId base_offset) override; + void emit_serialization(BasicBlockBuilder& bb, spv::StorageClass storage_class, SpvId array, SpvId base_offset, SpvId data) override; }; struct PtrDatatype : public Datatype { @@ -97,8 +97,8 @@ struct PtrDatatype : public Datatype { PtrDatatype(ConvertedType* type) : Datatype(type) {} size_t serialized_size() override { return bitwidth / 32; }; - SpvId emit_deserialization(BasicBlockBuilder& bb, spv::StorageClass storage_class, SpvId input) override; - void emit_serialization(BasicBlockBuilder& bb, spv::StorageClass storage_class, SpvId output, SpvId data) override; + SpvId emit_deserialization(BasicBlockBuilder& bb, spv::StorageClass storage_class, SpvId array, SpvId base_offset) override; + void emit_serialization(BasicBlockBuilder& bb, spv::StorageClass storage_class, SpvId array, SpvId base_offset, SpvId data) override; }; struct DefiniteArrayDatatype : public Datatype { @@ -108,8 +108,8 @@ struct DefiniteArrayDatatype : public Datatype { DefiniteArrayDatatype(ConvertedType* type, ConvertedType* element_type, size_t length); size_t serialized_size() override { return element_type->datatype->serialized_size(); }; - SpvId emit_deserialization(BasicBlockBuilder& bb, spv::StorageClass storage_class, SpvId input) override; - void emit_serialization(BasicBlockBuilder& bb, spv::StorageClass storage_class, SpvId output, SpvId data) override; + SpvId emit_deserialization(BasicBlockBuilder& bb, spv::StorageClass storage_class, SpvId array, SpvId base_offset) override; + void emit_serialization(BasicBlockBuilder& bb, spv::StorageClass storage_class, SpvId array, SpvId base_offset, SpvId data) override; }; struct ProductDatatype : public Datatype { @@ -119,8 +119,8 @@ struct ProductDatatype : public Datatype { ProductDatatype(ConvertedType* type, const std::vector&& elements_types); size_t serialized_size() override { return total_size; }; - SpvId emit_deserialization(BasicBlockBuilder& bb, spv::StorageClass storage_class, SpvId input) override; - void emit_serialization(BasicBlockBuilder& bb, spv::StorageClass storage_class, SpvId output, SpvId data) override; + SpvId emit_deserialization(BasicBlockBuilder& bb, spv::StorageClass storage_class, SpvId array, SpvId base_offset) override; + void emit_serialization(BasicBlockBuilder& bb, spv::StorageClass storage_class, SpvId array, SpvId base_offset, SpvId data) override; }; } diff --git a/src/thorin/be/spirv/spirv_builder.hpp b/src/thorin/be/spirv/spirv_builder.hpp index b52ae61d8..3aa1bd5ae 100644 --- a/src/thorin/be/spirv/spirv_builder.hpp +++ b/src/thorin/be/spirv/spirv_builder.hpp @@ -144,6 +144,17 @@ struct SpvBasicBlockBuilder : public SpvSectionBuilder { return id; } + SpvId access_chain(SpvId target_type, SpvId element, std::vector indexes) { + op(spv::Op::OpAccessChain, 4 + indexes.size()); + auto id = generate_fresh_id(); + ref_id(target_type); + ref_id(id); + ref_id(element); + for (auto index : indexes) + ref_id(index); + return id; + } + SpvId ptr_access_chain(SpvId target_type, SpvId base, SpvId element, std::vector indexes) { op(spv::Op::OpPtrAccessChain, 5 + indexes.size()); auto id = generate_fresh_id(); diff --git a/src/thorin/be/spirv/spirv_datatypes.cpp b/src/thorin/be/spirv/spirv_datatypes.cpp index ec9247fb7..4b52f79ce 100644 --- a/src/thorin/be/spirv/spirv_datatypes.cpp +++ b/src/thorin/be/spirv/spirv_datatypes.cpp @@ -9,31 +9,39 @@ ScalarDatatype::ScalarDatatype(ConvertedType* type, int type_tag, size_t size_in } -SpvId ScalarDatatype::emit_deserialization(BasicBlockBuilder& bb, spv::StorageClass storage_class, SpvId input) { +/// All serialization/deserialization methods use this so into a macro it goes +#define serialization_types \ +SpvId u32_tid = type->code_gen->convert(type->code_gen->world().type_pu32())->type_id; \ +SpvId arr_cell_tid = bb.file_builder.declare_ptr_type(storage_class, u32_tid); + +SpvId ScalarDatatype::emit_deserialization(BasicBlockBuilder& bb, spv::StorageClass storage_class, SpvId array, SpvId base_offset) { /// currently limited to 32-bit assert(size_in_bytes == 4); - SpvId u32_tid = type->code_gen->convert(type->code_gen->world().type_pu32())->type_id; - auto loaded = bb.load(u32_tid, input); + serialization_types; + auto cell = bb.access_chain(arr_cell_tid, array, { base_offset }); + auto loaded = bb.load(u32_tid, cell); return bb.bitcast(type->type_id, loaded); } -void ScalarDatatype::emit_serialization(BasicBlockBuilder& bb, spv::StorageClass storage_class, SpvId output, SpvId data) { +void ScalarDatatype::emit_serialization(BasicBlockBuilder& bb, spv::StorageClass storage_class, SpvId array, SpvId base_offset, SpvId data) { /// currently limited to 32-bit assert(size_in_bytes == 4); - SpvId u32_tid = type->code_gen->convert(type->code_gen->world().type_pu32())->type_id; + serialization_types; + auto cell = bb.access_chain(arr_cell_tid, array, { base_offset }); auto casted = bb.bitcast(u32_tid, data); - bb.store(casted, output); + bb.store(casted, cell); } -SpvId PtrDatatype::emit_deserialization(BasicBlockBuilder& bb, spv::StorageClass storage_class, SpvId input) { +SpvId PtrDatatype::emit_deserialization(BasicBlockBuilder& bb, spv::StorageClass storage_class, SpvId array, SpvId base_offset) { assert(type->src_type->as()->addr_space() == AddrSpace::Global && "Only buffer device address (global memory) pointers supported"); - SpvId u32_tid = type->code_gen->convert(type->code_gen->world().type_pu32())->type_id; + serialization_types; SpvId u64_tid = type->code_gen->convert(type->code_gen->world().type_pu64())->type_id; - SpvId arr_cell_tid = bb.file_builder.declare_ptr_type(storage_class, u32_tid); - auto input2 = bb.ptr_access_chain(arr_cell_tid, input, bb.file_builder.constant(u32_tid, { (uint32_t) 1 }), { }); - auto upper = bb.u_convert(u64_tid, bb.load(u32_tid, input)); - auto lower = bb.u_convert(u64_tid, bb.load(u32_tid, input2)); + auto cell0 = bb.access_chain(arr_cell_tid, array, { base_offset }); + auto cell1 = bb.access_chain(arr_cell_tid, array, { bb.binop(spv::OpIAdd, u32_tid, base_offset, bb.file_builder.constant(u32_tid, { (uint32_t) 1 })) }); + + auto upper = bb.u_convert(u64_tid, bb.load(u32_tid, cell0)); + auto lower = bb.u_convert(u64_tid, bb.load(u32_tid, cell1)); SpvId c32 = bb.file_builder.constant(u32_tid, { 32 }); auto merged = bb.binop(spv::OpBitwiseOr, u64_tid, bb.binop(spv::OpShiftLeftLogical, u64_tid, upper, c32), lower); @@ -41,7 +49,7 @@ SpvId PtrDatatype::emit_deserialization(BasicBlockBuilder& bb, spv::StorageClass return bb.convert_u_ptr(type->type_id, merged); } -void PtrDatatype::emit_serialization(BasicBlockBuilder& bb, spv::StorageClass storage_class, SpvId output, SpvId data) { +void PtrDatatype::emit_serialization(BasicBlockBuilder& bb, spv::StorageClass storage_class, SpvId array, SpvId base_offset, SpvId data) { assert(false && "TODO"); } @@ -50,57 +58,60 @@ DefiniteArrayDatatype::DefiniteArrayDatatype(ConvertedType* type, ConvertedType* assert(length > 0 && "Array lengths of zero are not supported"); } -SpvId DefiniteArrayDatatype::emit_deserialization(BasicBlockBuilder& bb, spv::StorageClass storage_class, SpvId input) { - SpvId i32_tid = type->code_gen->convert(type->code_gen->world().type_ps32())->type_id; +SpvId DefiniteArrayDatatype::emit_deserialization(BasicBlockBuilder& bb, spv::StorageClass storage_class, SpvId array, SpvId base_offset) { + serialization_types; std::vector indices; std::vector elements; + SpvId offset = base_offset; + SpvId stride = bb.file_builder.constant(u32_tid, { (uint32_t) element_type->datatype->serialized_size() }); for (size_t i = 0; i < length; i++) { - SpvId element_ptr = bb.ptr_access_chain(element_type->type_id, input, bb.file_builder.constant(i32_tid, { (uint32_t) (i * element_type->datatype->serialized_size()) }), indices); - SpvId element = element_type->datatype->emit_deserialization(bb, storage_class, element_ptr); + SpvId element = element_type->datatype->emit_deserialization(bb, storage_class, array, offset); elements.push_back(element); + offset = bb.binop(spv::OpIAdd, u32_tid, offset, stride); } return bb.composite(type->type_id, elements); } -void DefiniteArrayDatatype::emit_serialization(BasicBlockBuilder& bb, spv::StorageClass storage_class, SpvId output, SpvId data) { +void DefiniteArrayDatatype::emit_serialization(BasicBlockBuilder& bb, spv::StorageClass storage_class, SpvId array, SpvId base_offset, SpvId data) { + serialization_types; std::vector indices; - SpvId i32_tid = type->code_gen->convert(type->code_gen->world().type_ps32())->type_id; + SpvId offset = base_offset; + SpvId stride = bb.file_builder.constant(u32_tid, { (uint32_t) element_type->datatype->serialized_size() }); for (size_t i = 0; i < length; i++) { - SpvId element_ptr = bb.ptr_access_chain(element_type->type_id, output, bb.file_builder.constant(i32_tid, { (uint32_t) (i * element_type->datatype->serialized_size()) }), indices); - element_type->datatype->emit_serialization(bb, storage_class, element_ptr, bb.extract(element_type->type_id, data, { (uint32_t) i })); + element_type->datatype->emit_serialization(bb, storage_class, array, offset, bb.extract(element_type->type_id, data, { (uint32_t) i })); + offset = bb.binop(spv::OpIAdd, u32_tid, offset, stride); } } ProductDatatype::ProductDatatype(ConvertedType* type, const std::vector&& elements_types) : Datatype(type), elements_types(elements_types) { + // Unit datatype is acceptable, but serdes methods should never be invoked. for (auto& element_type : elements_types) { + assert(element_type->datatype != nullptr); total_size += element_type->datatype->serialized_size(); } } -SpvId ProductDatatype::emit_deserialization(BasicBlockBuilder& bb, spv::StorageClass storage_class, SpvId input) { +SpvId ProductDatatype::emit_deserialization(BasicBlockBuilder& bb, spv::StorageClass storage_class, SpvId array, SpvId base_offset) { assert(total_size > 0 && "It doesn't make sense to de-serialize Unit!"); - SpvId u32_tid = type->code_gen->convert(type->code_gen->world().type_pu32())->type_id; - SpvId arr_cell_tid = bb.file_builder.declare_ptr_type(storage_class, u32_tid); + serialization_types; std::vector indices; std::vector elements; - size_t offset = 0; + SpvId offset = base_offset; for (auto& element_type : elements_types) { - SpvId element_ptr = bb.ptr_access_chain(arr_cell_tid, input, bb.file_builder.constant(u32_tid, { (uint32_t) offset }), indices); - SpvId element = element_type->datatype->emit_deserialization(bb, storage_class, element_ptr); - offset += element_type->datatype->serialized_size(); + SpvId element = element_type->datatype->emit_deserialization(bb, storage_class, array, offset); + offset = bb.binop(spv::OpIAdd, u32_tid, offset, bb.file_builder.constant(u32_tid, { (uint32_t) element_type->datatype->serialized_size() })); elements.push_back(element); } return bb.composite(type->type_id, elements); } -void ProductDatatype::emit_serialization(BasicBlockBuilder& bb, spv::StorageClass storage_class, SpvId output, SpvId data) { +void ProductDatatype::emit_serialization(BasicBlockBuilder& bb, spv::StorageClass storage_class, SpvId array, SpvId base_offset, SpvId data) { assert(total_size > 0 && "It doesn't make sense to serialize Unit!"); - SpvId u32_tid = type->code_gen->convert(type->code_gen->world().type_pu32())->type_id; - SpvId arr_cell_tid = bb.file_builder.declare_ptr_type(storage_class, u32_tid); + serialization_types; std::vector indices; - size_t offset = 0; + SpvId offset = base_offset; int i = 0; for (auto& element_type : elements_types) { - SpvId element_ptr = bb.ptr_access_chain(arr_cell_tid, output, bb.file_builder.constant(u32_tid, { (uint32_t) offset }), indices); - element_type->datatype->emit_serialization(bb, storage_class, element_ptr, bb.extract(element_type->type_id, data, { (uint32_t) i++ })); + element_type->datatype->emit_serialization(bb, storage_class, array, offset, bb.extract(element_type->type_id, data, { (uint32_t) i++ })); + offset = bb.binop(spv::OpIAdd, u32_tid, offset, bb.file_builder.constant(u32_tid, { (uint32_t) element_type->datatype->serialized_size() })); } } From 54749f8a771d508f346e51fd326981a8c2882711 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Wed, 28 Apr 2021 12:42:56 +0200 Subject: [PATCH 068/342] ignore imported defs in transformations --- src/thorin/be/spirv/spirv_transform.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/thorin/be/spirv/spirv_transform.cpp b/src/thorin/be/spirv/spirv_transform.cpp index 51f613f2a..f210a8a7a 100644 --- a/src/thorin/be/spirv/spirv_transform.cpp +++ b/src/thorin/be/spirv/spirv_transform.cpp @@ -150,12 +150,11 @@ inline void collect_dispatch_targets(World& world, ScopeContext& ctx, const Base for (size_t i = 0; i < cont->num_ops(); i++) { auto def = cont->op(i); if (auto dest = def->isa_continuation()) { - const Head* source_loop_head = ctx.def2loop[cont]; - - if (dest->intrinsic() == Intrinsic::Branch) { + if (dest->intrinsic() == Intrinsic::Branch || dest->is_imported()) { continue; } + const Head* source_loop_head = ctx.def2loop[cont]; assert(ctx.def2loop.find(dest) != ctx.def2loop.end()); const Head* dest_loop_head = ctx.def2loop[dest]; From d36ada78175cda15de7f0374377929c1237ae260 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Wed, 28 Apr 2021 13:28:34 +0200 Subject: [PATCH 069/342] added spirv.nonsemantic.printf --- src/thorin/be/spirv/spirv.cpp | 25 ++++++++++++++++++++ src/thorin/be/spirv/spirv.h | 2 ++ src/thorin/be/spirv/spirv_builder.hpp | 33 +++++++++++++++++++++++++++ 3 files changed, 60 insertions(+) diff --git a/src/thorin/be/spirv/spirv.cpp b/src/thorin/be/spirv/spirv.cpp index a853bd0f1..8c88d2916 100644 --- a/src/thorin/be/spirv/spirv.cpp +++ b/src/thorin/be/spirv/spirv.cpp @@ -28,6 +28,9 @@ void CodeGen::emit_stream(std::ostream& out) { builder_->addressing_model = spv::AddressingModelPhysicalStorageBuffer64; + builder.extension("SPV_KHR_non_semantic_info"); + non_semantic_info = builder_->extended_import("NonSemantic.DebugPrintf"); + structure_loops(); structure_flow(); // cleanup_world(world()); @@ -306,6 +309,28 @@ void CodeGen::emit_epilogue(Continuation* continuation, BasicBlockBuilder* bb) { phi.preds.emplace_back(bb->args[arg], current_fn_->labels[continuation]); } bb->branch(current_fn_->labels[callee]); + } else if (auto callee = continuation->callee()->isa_continuation(); callee->is_imported()) { + if (callee->name() == "spirv.nonsemantic.printf") { + std::vector args; + auto string = continuation->arg(1); + if (auto arr_type = string->type()->isa(); arr_type->elem_type() == world().type_pu8()) { + auto arr = string->as(); + std::vector the_string; + for (int i = 0; i < arr_type->dim(); i++) + the_string.push_back(arr->op(i)->as()->value().get_u8()); + the_string.push_back('\0'); + args.push_back(builder_->debug_string(the_string.data())); + } else world().ELOG("spirv.nonsemantic.printf takes a string literal"); + + // TODO handle printing values + + auto values = continuation->arg(2); + bb->ext_instruction(bb->file_builder.void_type, non_semantic_info, 1, args); + } else { + world().ELOG("This spir-v builtin isn't recognised: %s", callee->name()); + } + auto next = continuation->args().back()->as_continuation(); + emit_epilogue(next, bb); } /*else if (auto callee = continuation->callee()->isa_continuation(); callee && callee->is_intrinsic()) { auto ret_continuation = emit_intrinsic(irbuilder, continuation); diff --git a/src/thorin/be/spirv/spirv.h b/src/thorin/be/spirv/spirv.h index 630ef6153..73f1671f3 100644 --- a/src/thorin/be/spirv/spirv.h +++ b/src/thorin/be/spirv/spirv.h @@ -64,6 +64,8 @@ class CodeGen : public thorin::CodeGen { TypeMap> types_; DefMap defs_; const Cont2Config& kernel_config_; + + SpvId non_semantic_info; }; /// Thorin data types are mapped to SPIR-V in non-trivial ways, this interface is used by the emission code to abstract over diff --git a/src/thorin/be/spirv/spirv_builder.hpp b/src/thorin/be/spirv/spirv_builder.hpp index 3aa1bd5ae..2c5a83529 100644 --- a/src/thorin/be/spirv/spirv_builder.hpp +++ b/src/thorin/be/spirv/spirv_builder.hpp @@ -236,6 +236,18 @@ struct SpvBasicBlockBuilder : public SpvSectionBuilder { return id; } + SpvId ext_instruction(SpvId return_type, SpvId set, uint32_t instruction, std::vector arguments) { + op(spv::Op::OpExtInst, 5 + arguments.size()); + auto id = generate_fresh_id(); + ref_id(return_type); + ref_id(id); + ref_id(set); + literal_int(instruction); + for (auto a : arguments) + ref_id(a); + return id; + } + void return_void() { op(spv::Op::OpReturn, 1); } @@ -416,6 +428,14 @@ struct SpvFileBuilder { annotations.literal_int(e); } + SpvId debug_string(std::string string) { + debug_string_source.op(spv::Op::OpString, 2 + div_roundup(string.size() + 1, 4)); + auto id = generate_fresh_id(); + debug_string_source.ref_id(id); + debug_string_source.literal_name(string); + return id; + } + SpvId bool_constant(SpvId type, bool value) { types_constants.op(value ? spv::Op::OpConstantTrue : spv::Op::OpConstantFalse, 3); auto id = generate_fresh_id(); @@ -506,6 +526,19 @@ struct SpvFileBuilder { capabilities.data_.push_back(cap); } + void extension(std::string name) { + extensions.op(spv::Op::OpExtension, 1 + div_roundup(name.size() + 1, 4)); + extensions.literal_name(name); + } + + SpvId extended_import(std::string name) { + ext_inst_import.op(spv::Op::OpExtInstImport, 2 + div_roundup(name.size() + 1, 4)); + auto id = generate_fresh_id(); + ext_inst_import.ref_id(id); + ext_inst_import.literal_name(name); + return id; + } + spv::AddressingModel addressing_model = spv::AddressingModel::AddressingModelLogical; spv::MemoryModel memory_model = spv::MemoryModel::MemoryModelSimple; From b1fc726d8b14fbf952f038970f88efb6996aad04 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Wed, 28 Apr 2021 20:53:09 +0200 Subject: [PATCH 070/342] messing with caps --- src/thorin/be/spirv/spirv.cpp | 3 ++- src/thorin/be/spirv/spirv_builder.hpp | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/thorin/be/spirv/spirv.cpp b/src/thorin/be/spirv/spirv.cpp index 8c88d2916..c23d58f93 100644 --- a/src/thorin/be/spirv/spirv.cpp +++ b/src/thorin/be/spirv/spirv.cpp @@ -23,7 +23,7 @@ void CodeGen::emit_stream(std::ostream& out) { builder_->capability(spv::Capability::CapabilityShader); builder_->capability(spv::Capability::CapabilityVariablePointers); builder_->capability(spv::Capability::CapabilityPhysicalStorageBufferAddresses); - builder_->capability(spv::Capability::CapabilityInt16); + // builder_->capability(spv::Capability::CapabilityInt16); builder_->capability(spv::Capability::CapabilityInt64); builder_->addressing_model = spv::AddressingModelPhysicalStorageBuffer64; @@ -535,6 +535,7 @@ SpvId CodeGen::emit(const Def* def, BasicBlockBuilder* bb) { } return constant; } else if (auto param = def->isa()) { + if (is_mem(param)) return spv_none; if (auto param_id = current_fn_->params.lookup(param)) { assert((*param_id).id != 0); return *param_id; diff --git a/src/thorin/be/spirv/spirv_builder.hpp b/src/thorin/be/spirv/spirv_builder.hpp index 2c5a83529..afefcb984 100644 --- a/src/thorin/be/spirv/spirv_builder.hpp +++ b/src/thorin/be/spirv/spirv_builder.hpp @@ -540,7 +540,7 @@ struct SpvFileBuilder { } spv::AddressingModel addressing_model = spv::AddressingModel::AddressingModelLogical; - spv::MemoryModel memory_model = spv::MemoryModel::MemoryModelSimple; + spv::MemoryModel memory_model = spv::MemoryModel::MemoryModelGLSL450 ; private: std::ostream* output_ = nullptr; From 557aa2f893fb485cc67f7c1853297ff227d28975 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Wed, 28 Apr 2021 20:58:19 +0200 Subject: [PATCH 071/342] handles printing values --- src/thorin/be/spirv/spirv.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/thorin/be/spirv/spirv.cpp b/src/thorin/be/spirv/spirv.cpp index c23d58f93..e6178180c 100644 --- a/src/thorin/be/spirv/spirv.cpp +++ b/src/thorin/be/spirv/spirv.cpp @@ -322,7 +322,9 @@ void CodeGen::emit_epilogue(Continuation* continuation, BasicBlockBuilder* bb) { args.push_back(builder_->debug_string(the_string.data())); } else world().ELOG("spirv.nonsemantic.printf takes a string literal"); - // TODO handle printing values + for (int i = 2; i < continuation->num_args() - 1; i++) { + args.push_back(emit(continuation->arg(i), bb)); + } auto values = continuation->arg(2); bb->ext_instruction(bb->file_builder.void_type, non_semantic_info, 1, args); From 85cfd46878bbe33d7e706310330cd8282e849ac4 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Wed, 28 Apr 2021 22:13:29 +0200 Subject: [PATCH 072/342] u64 literals --- src/thorin/be/spirv/spirv.cpp | 16 ++++++++++++++-- src/thorin/be/spirv/spirv_datatypes.cpp | 6 +++--- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/src/thorin/be/spirv/spirv.cpp b/src/thorin/be/spirv/spirv.cpp index e6178180c..6dfa3f244 100644 --- a/src/thorin/be/spirv/spirv.cpp +++ b/src/thorin/be/spirv/spirv.cpp @@ -77,6 +77,10 @@ void CodeGen::emit_stream(std::ostream& out) { auto converted = convert(param_type); assert(converted->datatype != nullptr); SpvId arg = converted->datatype->emit_deserialization(*bb, spv::StorageClassPushConstant, arr_ref, bb->file_builder.constant(convert(world().type_pu32())->type_id, { offset })); + std::vector printf_args; + printf_args.push_back(builder_->debug_string("arg " + std::to_string((int)i) + " = %ul\n")); + printf_args.push_back(arg); + bb->ext_instruction(bb->file_builder.void_type, non_semantic_info, 1, printf_args); args.push_back(arg); offset += converted->datatype->serialized_size(); } @@ -529,8 +533,14 @@ SpvId CodeGen::emit(const Def* def, BasicBlockBuilder* bb) { case PrimType_pu16: case PrimType_qu16: assertf(false, "not implemented yet"); case PrimType_ps32: case PrimType_qs32: constant = bb->file_builder.constant(type, { static_cast(box.get_s32()) }); break; case PrimType_pu32: case PrimType_qu32: constant = bb->file_builder.constant(type, { static_cast(box.get_u32()) }); break; - case PrimType_ps64: case PrimType_qs64: assertf(false, "not implemented yet"); - case PrimType_pu64: case PrimType_qu64: assertf(false, "not implemented yet"); + case PrimType_ps64: case PrimType_qs64: + case PrimType_pu64: case PrimType_qu64: { + uint64_t value = static_cast(box.get_u64()); + uint64_t upper = value >> 32U; + uint64_t lower = value & 0xFFFFFFFFU; + constant = bb->file_builder.constant(type, { (uint32_t) lower, (uint32_t) upper }); + break; + } case PrimType_pf16: case PrimType_qf16: assertf(false, "not implemented yet"); case PrimType_pf32: case PrimType_qf32: assertf(false, "not implemented yet"); case PrimType_pf64: case PrimType_qf64: assertf(false, "not implemented yet"); @@ -629,6 +639,8 @@ SpvId CodeGen::emit(const Def* def, BasicBlockBuilder* bb) { auto type = convert(lea->ptr_type()); auto offset = emit(lea->index(), bb); return bb->ptr_access_chain(type->type_id, emit(lea->ptr(), bb), offset, {}); + } else if (auto bitcast = def->isa()) { + return bb->bitcast(convert(bitcast->type())->type_id, emit(bitcast->from(), bb)); } assertf(false, "Incomplete emit(def) definition"); } diff --git a/src/thorin/be/spirv/spirv_datatypes.cpp b/src/thorin/be/spirv/spirv_datatypes.cpp index 4b52f79ce..ae9eca006 100644 --- a/src/thorin/be/spirv/spirv_datatypes.cpp +++ b/src/thorin/be/spirv/spirv_datatypes.cpp @@ -40,11 +40,11 @@ SpvId PtrDatatype::emit_deserialization(BasicBlockBuilder& bb, spv::StorageClass auto cell0 = bb.access_chain(arr_cell_tid, array, { base_offset }); auto cell1 = bb.access_chain(arr_cell_tid, array, { bb.binop(spv::OpIAdd, u32_tid, base_offset, bb.file_builder.constant(u32_tid, { (uint32_t) 1 })) }); - auto upper = bb.u_convert(u64_tid, bb.load(u32_tid, cell0)); - auto lower = bb.u_convert(u64_tid, bb.load(u32_tid, cell1)); + auto lower = bb.u_convert(u64_tid, bb.load(u32_tid, cell0)); + auto upper = bb.u_convert(u64_tid, bb.load(u32_tid, cell1)); SpvId c32 = bb.file_builder.constant(u32_tid, { 32 }); - auto merged = bb.binop(spv::OpBitwiseOr, u64_tid, bb.binop(spv::OpShiftLeftLogical, u64_tid, upper, c32), lower); + auto merged = bb.binop(spv::OpBitwiseOr, u64_tid, lower, bb.binop(spv::OpShiftLeftLogical, u64_tid, upper, c32)); return bb.convert_u_ptr(type->type_id, merged); } From 3678dfb9a7a8564ce5c9ca3e71f8cf7945d873cd Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Thu, 29 Apr 2021 10:57:07 +0200 Subject: [PATCH 073/342] implement extract and fix up codegen --- src/thorin/be/spirv/spirv.cpp | 88 ++++++++++++++++++++++--- src/thorin/be/spirv/spirv.h | 7 +- src/thorin/be/spirv/spirv_datatypes.cpp | 7 ++ 3 files changed, 93 insertions(+), 9 deletions(-) diff --git a/src/thorin/be/spirv/spirv.cpp b/src/thorin/be/spirv/spirv.cpp index 6dfa3f244..7d09ab60b 100644 --- a/src/thorin/be/spirv/spirv.cpp +++ b/src/thorin/be/spirv/spirv.cpp @@ -56,7 +56,7 @@ void CodeGen::emit_stream(std::ostream& out) { SpvId callee = defs_[cont]; - FnBuilder fn_builder(builder_); + FnBuilder fn_builder(this, builder_); fn_builder.fn_type = entry_pt_signature; fn_builder.fn_ret_type = builder_->void_type; @@ -77,10 +77,6 @@ void CodeGen::emit_stream(std::ostream& out) { auto converted = convert(param_type); assert(converted->datatype != nullptr); SpvId arg = converted->datatype->emit_deserialization(*bb, spv::StorageClassPushConstant, arr_ref, bb->file_builder.constant(convert(world().type_pu32())->type_id, { offset })); - std::vector printf_args; - printf_args.push_back(builder_->debug_string("arg " + std::to_string((int)i) + " = %ul\n")); - printf_args.push_back(arg); - bb->ext_instruction(bb->file_builder.void_type, non_semantic_info, 1, printf_args); args.push_back(arg); offset += converted->datatype->serialized_size(); } @@ -111,7 +107,7 @@ void CodeGen::emit(const thorin::Scope& scope) { entry_ = scope.entry(); assert(entry_->is_returning()); - FnBuilder fn(builder_); + FnBuilder fn(this, builder_); fn.scope = &scope; fn.fn_type = convert(entry_->type())->type_id; fn.fn_ret_type = get_codom_type(entry_); @@ -336,7 +332,8 @@ void CodeGen::emit_epilogue(Continuation* continuation, BasicBlockBuilder* bb) { world().ELOG("This spir-v builtin isn't recognised: %s", callee->name()); } auto next = continuation->args().back()->as_continuation(); - emit_epilogue(next, bb); + bb->branch(current_fn_->bbs_map[next]->label); + //emit_epilogue(next, bb); } /*else if (auto callee = continuation->callee()->isa_continuation(); callee && callee->is_intrinsic()) { auto ret_continuation = emit_intrinsic(irbuilder, continuation); @@ -615,6 +612,9 @@ SpvId CodeGen::emit(const Def* def, BasicBlockBuilder* bb) { } return bb->composite(convert(structagg->type())->type_id, elements); } else if (auto access = def->isa()) { + // emit dependent operations first + emit(access->mem(), bb); + std::vector operands; auto ptr_type = access->ptr()->type()->as(); if (ptr_type->addr_space() == AddrSpace::Global) { @@ -641,12 +641,84 @@ SpvId CodeGen::emit(const Def* def, BasicBlockBuilder* bb) { return bb->ptr_access_chain(type->type_id, emit(lea->ptr(), bb), offset, {}); } else if (auto bitcast = def->isa()) { return bb->bitcast(convert(bitcast->type())->type_id, emit(bitcast->from(), bb)); + } else if (auto aggop = def->isa()) { + auto spv_agg = emit(aggop->agg(), bb); + auto agg_type = convert(aggop->agg()->type())->type_id; + + bool mem = false; + if (auto tt = aggop->agg()->type()->isa(); tt && tt->op(0)->isa()) mem = true; + + auto copy_to_alloca = [&] (SpvId target_type) { + world().wdef(def, "slow: alloca and loads/stores needed for aggregate '{}'", def); + auto agg_ptr_type = builder_->declare_ptr_type(spv::StorageClassFunction, agg_type); + + auto variable = bb->fn_builder.variable(agg_ptr_type, spv::StorageClassFunction); + bb->store(spv_agg, variable); + + auto cell_ptr_type = builder_->declare_ptr_type(spv::StorageClassFunction, target_type); + auto cell = bb->access_chain(cell_ptr_type, variable, { emit(aggop->index(), bb)} ); + return std::make_pair(variable, cell); + }; + /*auto copy_to_alloca_or_global = [&] () -> llvm::Value* { + if (auto constant = llvm::dyn_cast(llvm_agg)) { + auto global = llvm::cast(module().getOrInsertGlobal(aggop->agg()->unique_name().c_str(), llvm_agg->getType())); + global->setLinkage(llvm::GlobalValue::InternalLinkage); + global->setInitializer(constant); + return irbuilder.CreateInBoundsGEP(global, { irbuilder.getInt64(0), llvm_idx }); + } + return copy_to_alloca().second; + };*/ + + if (auto extract = aggop->isa()) { + auto target_type = convert(extract->type())->type_id; + auto constant_index = aggop->index()->isa(); + + // skip if the index is a constant + if (aggop->agg()->type()->isa() && constant_index == nullptr) { + return bb->load(target_type, copy_to_alloca(target_type).second); + } + + // TODO: evaluate what to do with those + // if (extract->agg()->type()->isa()) + // return irbuilder.CreateExtractElement(llvm_agg, llvm_idx); + + // tuple/struct + if (is_mem(extract)) return spv_none; + + // index *must* be constant + assert(constant_index != nullptr); + uint32_t index = constant_index->value().get_u32(); + + unsigned offset = 0; + if (mem) { + if (aggop->agg()->type()->num_ops() == 2) return spv_agg; + offset = 1; + } + + return bb->extract(target_type, spv_agg, { index - offset }); + } + + THORIN_UNREACHABLE; + /*auto insert = def->as(); + auto value = emit(insert->value()); + + // TODO deal with mem - but I think for now this case shouldn't happen + + if (insert->agg()->type()->isa()) { + auto p = copy_to_alloca(); + irbuilder.CreateStore(emit(aggop->as()->value()), p.second); + return irbuilder.CreateLoad(p.first); + } + if (insert->agg()->type()->isa()) + return irbuilder.CreateInsertElement(llvm_agg, emit(aggop->as()->value()), llvm_idx); + // tuple/struct + return irbuilder.CreateInsertValue(llvm_agg, value, {primlit_value(aggop->index())});*/ } assertf(false, "Incomplete emit(def) definition"); } BasicBlockBuilder::BasicBlockBuilder(FnBuilder& fn_builder) -: builder::SpvBasicBlockBuilder(*fn_builder.file_builder) { +: builder::SpvBasicBlockBuilder(*fn_builder.file_builder), fn_builder(fn_builder) { label = file_builder.generate_fresh_id(); } diff --git a/src/thorin/be/spirv/spirv.h b/src/thorin/be/spirv/spirv.h index 73f1671f3..9ecc0f8e0 100644 --- a/src/thorin/be/spirv/spirv.h +++ b/src/thorin/be/spirv/spirv.h @@ -10,6 +10,7 @@ using SpvId = builder::SpvId; class CodeGen; struct Datatype; +struct PtrDatatype; struct ConvertedType { spirv::CodeGen* code_gen; @@ -26,18 +27,20 @@ struct FnBuilder; struct BasicBlockBuilder : public builder::SpvBasicBlockBuilder { explicit BasicBlockBuilder(FnBuilder& fn_builder); + FnBuilder& fn_builder; std::unordered_map phis_map; DefMap args; }; struct FnBuilder : public builder::SpvFnBuilder { + CodeGen* cg; const Scope* scope = nullptr; std::vector> bbs; std::unordered_map bbs_map; ContinuationMap labels; DefMap params; - explicit FnBuilder(builder::SpvFileBuilder* file_builder) : builder::SpvFnBuilder(file_builder) {} + explicit FnBuilder(CodeGen* cg, builder::SpvFileBuilder* file_builder) : builder::SpvFnBuilder(file_builder), cg(cg) {} }; class CodeGen : public thorin::CodeGen { @@ -66,6 +69,8 @@ class CodeGen : public thorin::CodeGen { const Cont2Config& kernel_config_; SpvId non_semantic_info; + + friend PtrDatatype; }; /// Thorin data types are mapped to SPIR-V in non-trivial ways, this interface is used by the emission code to abstract over diff --git a/src/thorin/be/spirv/spirv_datatypes.cpp b/src/thorin/be/spirv/spirv_datatypes.cpp index ae9eca006..3f9039425 100644 --- a/src/thorin/be/spirv/spirv_datatypes.cpp +++ b/src/thorin/be/spirv/spirv_datatypes.cpp @@ -46,6 +46,13 @@ SpvId PtrDatatype::emit_deserialization(BasicBlockBuilder& bb, spv::StorageClass SpvId c32 = bb.file_builder.constant(u32_tid, { 32 }); auto merged = bb.binop(spv::OpBitwiseOr, u64_tid, lower, bb.binop(spv::OpShiftLeftLogical, u64_tid, upper, c32)); + std::vector printf_args; + printf_args.push_back(bb.file_builder.debug_string("lower = %ul\nupper = %ul\nmerged = %ul\n")); + printf_args.push_back(lower); + printf_args.push_back(upper); + printf_args.push_back(merged); + bb.ext_instruction(bb.file_builder.void_type, bb.fn_builder.cg->non_semantic_info, 1, printf_args); + return bb.convert_u_ptr(type->type_id, merged); } From 5785ce533ddd1cb058d93180c5698260c1e583e0 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Thu, 29 Apr 2021 12:43:24 +0200 Subject: [PATCH 074/342] removing debug print from PtrDatatype --- src/thorin/be/spirv/spirv_datatypes.cpp | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/thorin/be/spirv/spirv_datatypes.cpp b/src/thorin/be/spirv/spirv_datatypes.cpp index 3f9039425..ae9eca006 100644 --- a/src/thorin/be/spirv/spirv_datatypes.cpp +++ b/src/thorin/be/spirv/spirv_datatypes.cpp @@ -46,13 +46,6 @@ SpvId PtrDatatype::emit_deserialization(BasicBlockBuilder& bb, spv::StorageClass SpvId c32 = bb.file_builder.constant(u32_tid, { 32 }); auto merged = bb.binop(spv::OpBitwiseOr, u64_tid, lower, bb.binop(spv::OpShiftLeftLogical, u64_tid, upper, c32)); - std::vector printf_args; - printf_args.push_back(bb.file_builder.debug_string("lower = %ul\nupper = %ul\nmerged = %ul\n")); - printf_args.push_back(lower); - printf_args.push_back(upper); - printf_args.push_back(merged); - bb.ext_instruction(bb.file_builder.void_type, bb.fn_builder.cg->non_semantic_info, 1, printf_args); - return bb.convert_u_ptr(type->type_id, merged); } From c820a6814a92fb21ee37db386e5f5ff7db1f03b7 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Mon, 3 May 2021 15:33:18 +0200 Subject: [PATCH 075/342] handle Bottom --- src/thorin/be/spirv/spirv.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/thorin/be/spirv/spirv.cpp b/src/thorin/be/spirv/spirv.cpp index 7d09ab60b..87a3abfe8 100644 --- a/src/thorin/be/spirv/spirv.cpp +++ b/src/thorin/be/spirv/spirv.cpp @@ -713,6 +713,8 @@ SpvId CodeGen::emit(const Def* def, BasicBlockBuilder* bb) { return irbuilder.CreateInsertElement(llvm_agg, emit(aggop->as()->value()), llvm_idx); // tuple/struct return irbuilder.CreateInsertValue(llvm_agg, value, {primlit_value(aggop->index())});*/ + } else if (def->isa()) { + return bb->undef(convert(def->type())->type_id); } assertf(false, "Incomplete emit(def) definition"); } From c536b1523db0d702602bd43f23887ab591eee9c8 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Mon, 3 May 2021 16:25:22 +0200 Subject: [PATCH 076/342] support Insert --- src/thorin/be/spirv/spirv.cpp | 50 +++++++++++++------------ src/thorin/be/spirv/spirv_builder.hpp | 12 ++++++ src/thorin/be/spirv/spirv_datatypes.cpp | 4 +- 3 files changed, 39 insertions(+), 27 deletions(-) diff --git a/src/thorin/be/spirv/spirv.cpp b/src/thorin/be/spirv/spirv.cpp index 87a3abfe8..0054c9b36 100644 --- a/src/thorin/be/spirv/spirv.cpp +++ b/src/thorin/be/spirv/spirv.cpp @@ -659,22 +659,17 @@ SpvId CodeGen::emit(const Def* def, BasicBlockBuilder* bb) { auto cell = bb->access_chain(cell_ptr_type, variable, { emit(aggop->index(), bb)} ); return std::make_pair(variable, cell); }; - /*auto copy_to_alloca_or_global = [&] () -> llvm::Value* { - if (auto constant = llvm::dyn_cast(llvm_agg)) { - auto global = llvm::cast(module().getOrInsertGlobal(aggop->agg()->unique_name().c_str(), llvm_agg->getType())); - global->setLinkage(llvm::GlobalValue::InternalLinkage); - global->setInitializer(constant); - return irbuilder.CreateInBoundsGEP(global, { irbuilder.getInt64(0), llvm_idx }); - } - return copy_to_alloca().second; - };*/ if (auto extract = aggop->isa()) { + if (is_mem(extract)) return spv_none; + auto target_type = convert(extract->type())->type_id; auto constant_index = aggop->index()->isa(); // skip if the index is a constant if (aggop->agg()->type()->isa() && constant_index == nullptr) { + assert(aggop->agg()->type()->isa()); + assert(!is_mem(extract)); return bb->load(target_type, copy_to_alloca(target_type).second); } @@ -683,7 +678,6 @@ SpvId CodeGen::emit(const Def* def, BasicBlockBuilder* bb) { // return irbuilder.CreateExtractElement(llvm_agg, llvm_idx); // tuple/struct - if (is_mem(extract)) return spv_none; // index *must* be constant assert(constant_index != nullptr); @@ -696,23 +690,31 @@ SpvId CodeGen::emit(const Def* def, BasicBlockBuilder* bb) { } return bb->extract(target_type, spv_agg, { index - offset }); - } + } else if (auto insert = def->isa()) { + auto value = emit(insert->value(), bb); + auto constant_index = aggop->index()->isa(); - THORIN_UNREACHABLE; - /*auto insert = def->as(); - auto value = emit(insert->value()); + // TODO deal with mem - but I think for now this case shouldn't happen - // TODO deal with mem - but I think for now this case shouldn't happen + if (insert->agg()->type()->isa() && constant_index == nullptr) { + assert(aggop->agg()->type()->isa()); + auto [variable, cell] = copy_to_alloca(agg_type); + bb->store(value, cell); + return bb->load(agg_type, variable); + } - if (insert->agg()->type()->isa()) { - auto p = copy_to_alloca(); - irbuilder.CreateStore(emit(aggop->as()->value()), p.second); - return irbuilder.CreateLoad(p.first); - } - if (insert->agg()->type()->isa()) - return irbuilder.CreateInsertElement(llvm_agg, emit(aggop->as()->value()), llvm_idx); - // tuple/struct - return irbuilder.CreateInsertValue(llvm_agg, value, {primlit_value(aggop->index())});*/ + // TODO: evaluate what to do with those + //if (insert->agg()->type()->isa()) + // return irbuilder.CreateInsertElement(llvm_agg, emit(aggop->as()->value()), llvm_idx); + + // tuple/struct + + // index *must* be constant + assert(constant_index != nullptr); + uint32_t index = constant_index->value().get_u32(); + + return bb->insert(agg_type, value, spv_agg, { index }); + } else THORIN_UNREACHABLE; } else if (def->isa()) { return bb->undef(convert(def->type())->type_id); } diff --git a/src/thorin/be/spirv/spirv_builder.hpp b/src/thorin/be/spirv/spirv_builder.hpp index afefcb984..254fb7cfb 100644 --- a/src/thorin/be/spirv/spirv_builder.hpp +++ b/src/thorin/be/spirv/spirv_builder.hpp @@ -106,6 +106,18 @@ struct SpvBasicBlockBuilder : public SpvSectionBuilder { return id; } + SpvId insert(SpvId target_type, SpvId object, SpvId composite, std::vector indices) { + op(spv::Op::OpCompositeInsert, 5 + indices.size()); + ref_id(target_type); + auto id = generate_fresh_id(); + ref_id(id); + ref_id(object); + ref_id(composite); + for (auto i : indices) + literal_int(i); + return id; + } + SpvId bitcast(SpvId target_type, SpvId value) { op(spv::Op::OpBitcast, 4); auto id = generate_fresh_id(); diff --git a/src/thorin/be/spirv/spirv_datatypes.cpp b/src/thorin/be/spirv/spirv_datatypes.cpp index ae9eca006..60d037388 100644 --- a/src/thorin/be/spirv/spirv_datatypes.cpp +++ b/src/thorin/be/spirv/spirv_datatypes.cpp @@ -129,8 +129,6 @@ ConvertedType* CodeGen::convert(const Type* type) { } if (auto iter = types_.find(type); iter != types_.end()) return iter->second.get(); - - assert(!type->isa()); ConvertedType* converted = types_.emplace(type, std::make_unique(this) ).first->second.get(); converted->src_type = type; switch (type->tag()) { @@ -313,7 +311,7 @@ ConvertedType* CodeGen::convert(const Type* type) { } case Node_MemType: { - assert(false && "TODO: get arround this"); + assert(false && "MemType cannot be converted to SPIR-V"); } default: From 34f3da58810e9ab8159eca87af427294b9899a8a Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Mon, 3 May 2021 16:58:23 +0200 Subject: [PATCH 077/342] naive fn call implem --- src/thorin/be/spirv/spirv.cpp | 71 ++++++++++++------------- src/thorin/be/spirv/spirv_datatypes.cpp | 10 +--- 2 files changed, 37 insertions(+), 44 deletions(-) diff --git a/src/thorin/be/spirv/spirv.cpp b/src/thorin/be/spirv/spirv.cpp index 0054c9b36..65a9cbc1e 100644 --- a/src/thorin/be/spirv/spirv.cpp +++ b/src/thorin/be/spirv/spirv.cpp @@ -231,9 +231,8 @@ void CodeGen::emit_epilogue(Continuation* continuation, BasicBlockBuilder* bb) { auto fbb = current_fn_->labels[continuation->arg(2)->as_continuation()]; bb->selection_merge(merge_bb,spv::SelectionControlMaskNone); bb->branch_conditional(cond, tbb, fbb); - } /*else if (continuation->callee()->isa() && - continuation->callee()->as()->intrinsic() == Intrinsic::Match) { - auto val = emit(continuation->arg(0)); + } else if (continuation->callee()->isa() && continuation->callee()->as()->intrinsic() == Intrinsic::Match) { + /*auto val = emit(continuation->arg(0)); auto otherwise_bb = cont2bb(continuation->arg(1)->as_continuation()); auto match = irbuilder.CreateSwitch(val, otherwise_bb, continuation->num_args() - 2); for (size_t i = 2; i < continuation->num_args(); i++) { @@ -241,11 +240,11 @@ void CodeGen::emit_epilogue(Continuation* continuation, BasicBlockBuilder* bb) { auto case_const = llvm::cast(emit(arg->op(0))); auto case_bb = cont2bb(arg->op(1)->as_continuation()); match->addCase(case_const, case_bb); - } + }*/ + THORIN_UNREACHABLE; } else if (continuation->callee()->isa()) { - irbuilder.CreateUnreachable(); - } */ - else if (continuation->intrinsic() == Intrinsic::SCFLoopHeader) { + bb->unreachable(); + } else if (continuation->intrinsic() == Intrinsic::SCFLoopHeader) { auto merge_label = current_fn_->bbs_map[const_cast(continuation->attributes_.scf_metadata.loop_header.merge_target)]->label; auto continue_label = current_fn_->bbs_map[const_cast(continuation->attributes_.scf_metadata.loop_header.continue_target)]->label; bb->loop_merge(merge_label, continue_label, spv::LoopControlMaskNone, {}); @@ -333,39 +332,37 @@ void CodeGen::emit_epilogue(Continuation* continuation, BasicBlockBuilder* bb) { } auto next = continuation->args().back()->as_continuation(); bb->branch(current_fn_->bbs_map[next]->label); - //emit_epilogue(next, bb); - } - /*else if (auto callee = continuation->callee()->isa_continuation(); callee && callee->is_intrinsic()) { - auto ret_continuation = emit_intrinsic(irbuilder, continuation); - irbuilder.CreateBr(cont2bb(ret_continuation)); + } else if (auto callee = continuation->callee()->isa_continuation(); callee && callee->is_intrinsic()) { + THORIN_UNREACHABLE; + //auto ret_continuation = emit_intrinsic(irbuilder, continuation); + //irbuilder.CreateBr(cont2bb(ret_continuation)); } else { // function/closure call // put all first-order args into an array - std::vector args; + std::vector args; const Def* ret_arg = nullptr; for (auto arg : continuation->args()) { if (arg->order() == 0) { - if (auto val = emit_unsafe(arg)) - args.push_back(val); + auto arg_type = arg->type(); + if (arg_type == world().unit() || arg_type == world().mem_type()) continue; + args.push_back(emit(arg, bb)); } else { assert(!ret_arg); ret_arg = arg; } } - llvm::CallInst* call = nullptr; + auto ret_type = get_codom_type(continuation); + + SpvId call_result; if (auto callee = continuation->callee()->isa_continuation()) { - call = irbuilder.CreateCall(emit(callee), args); - if (callee->is_exported()) - call->setCallingConv(kernel_calling_convention_); - else if (callee->cc() == CC::Device) - call->setCallingConv(device_calling_convention_); - else - call->setCallingConv(function_calling_convention_); + call_result = bb->call(ret_type, emit(callee, bb), args); } else { // must be a closure - auto closure = emit(callee); - args.push_back(irbuilder.CreateExtractValue(closure, 1)); - call = irbuilder.CreateCall(irbuilder.CreateExtractValue(closure, 0), args); + THORIN_UNREACHABLE; + + // auto closure = emit(callee); + // args.push_back(irbuilder.CreateExtractValue(closure, 1)); + // call = irbuilder.CreateCall(irbuilder.CreateExtractValue(closure, 0), args); } // must be call + continuation --- call + return has been removed by codegen_prepare @@ -381,33 +378,35 @@ void CodeGen::emit_epilogue(Continuation* continuation, BasicBlockBuilder* bb) { } if (n == 0) { - irbuilder.CreateBr(cont2bb(succ)); + bb->branch(current_fn_->labels[succ]); } else if (n == 1) { - irbuilder.CreateBr(cont2bb(succ)); - emit_phi_arg(irbuilder, last_param, call); + bb->branch(current_fn_->labels[succ]); + + auto& phi = current_fn_->bbs_map[succ]->phis_map[last_param]; + phi.preds.emplace_back(call_result, current_fn_->labels[continuation]); } else { - Array extracts(n); + Array extracts(n); for (size_t i = 0, j = 0; i != succ->num_params(); ++i) { auto param = succ->param(i); if (is_mem(param) || is_unit(param)) continue; - extracts[j] = irbuilder.CreateExtractValue(call, unsigned(j)); + extracts[j] = bb->extract(convert(param->type())->type_id, call_result, { (uint32_t) j }); j++; } - irbuilder.CreateBr(cont2bb(succ)); + bb->branch(current_fn_->labels[succ]); for (size_t i = 0, j = 0; i != succ->num_params(); ++i) { auto param = succ->param(i); if (is_mem(param) || is_unit(param)) continue; - emit_phi_arg(irbuilder, param, extracts[j]); + + auto& phi = current_fn_->bbs_map[succ]->phis_map[param]; + phi.preds.emplace_back(extracts[j], current_fn_->labels[continuation]); + j++; } } - }*/ - else { - assert(false && "epilogue not implemented for this"); } } diff --git a/src/thorin/be/spirv/spirv_datatypes.cpp b/src/thorin/be/spirv/spirv_datatypes.cpp index 60d037388..4a11b756c 100644 --- a/src/thorin/be/spirv/spirv_datatypes.cpp +++ b/src/thorin/be/spirv/spirv_datatypes.cpp @@ -256,10 +256,7 @@ ConvertedType* CodeGen::convert(const Type* type) { std::vector spv_types; size_t total_serialized_size = 0; for (auto member_type : type->ops()) { - if (member_type == world().unit() || member_type == world().mem_type()) { - outf("skipped one"); - continue; - } + if (member_type == world().unit() || member_type == world().mem_type()) continue; auto converted_member_type = convert(member_type); types.push_back(converted_member_type); spv_types.push_back(converted_member_type->type_id); @@ -284,10 +281,7 @@ ConvertedType* CodeGen::convert(const Type* type) { size_t max_serialized_size = 0; for (auto member_type : type->as()->ops()) { - if (member_type == world().unit() || member_type == world().mem_type()) { - outf("skipped one"); - continue; - } + if (member_type == world().unit() || member_type == world().mem_type()) continue; auto converted_member_type = convert(member_type); if (converted_member_type->datatype->serialized_size() > max_serialized_size) max_serialized_size = converted_member_type->datatype->serialized_size(); From eae1f8e776dc3850ff2e56a38ef4e08bb8e87c92 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Wed, 5 May 2021 11:01:05 +0200 Subject: [PATCH 078/342] moved builtins to their own function --- src/thorin/be/spirv/spirv.cpp | 52 +++++++++++++++++++---------------- src/thorin/be/spirv/spirv.h | 1 + 2 files changed, 29 insertions(+), 24 deletions(-) diff --git a/src/thorin/be/spirv/spirv.cpp b/src/thorin/be/spirv/spirv.cpp index 65a9cbc1e..a652631f8 100644 --- a/src/thorin/be/spirv/spirv.cpp +++ b/src/thorin/be/spirv/spirv.cpp @@ -308,30 +308,8 @@ void CodeGen::emit_epilogue(Continuation* continuation, BasicBlockBuilder* bb) { phi.preds.emplace_back(bb->args[arg], current_fn_->labels[continuation]); } bb->branch(current_fn_->labels[callee]); - } else if (auto callee = continuation->callee()->isa_continuation(); callee->is_imported()) { - if (callee->name() == "spirv.nonsemantic.printf") { - std::vector args; - auto string = continuation->arg(1); - if (auto arr_type = string->type()->isa(); arr_type->elem_type() == world().type_pu8()) { - auto arr = string->as(); - std::vector the_string; - for (int i = 0; i < arr_type->dim(); i++) - the_string.push_back(arr->op(i)->as()->value().get_u8()); - the_string.push_back('\0'); - args.push_back(builder_->debug_string(the_string.data())); - } else world().ELOG("spirv.nonsemantic.printf takes a string literal"); - - for (int i = 2; i < continuation->num_args() - 1; i++) { - args.push_back(emit(continuation->arg(i), bb)); - } - - auto values = continuation->arg(2); - bb->ext_instruction(bb->file_builder.void_type, non_semantic_info, 1, args); - } else { - world().ELOG("This spir-v builtin isn't recognised: %s", callee->name()); - } - auto next = continuation->args().back()->as_continuation(); - bb->branch(current_fn_->bbs_map[next]->label); + } else if (auto builtin = continuation->callee()->isa_continuation(); builtin->is_imported()) { + emit_builtin(continuation, builtin, bb); } else if (auto callee = continuation->callee()->isa_continuation(); callee && callee->is_intrinsic()) { THORIN_UNREACHABLE; //auto ret_continuation = emit_intrinsic(irbuilder, continuation); @@ -720,6 +698,32 @@ SpvId CodeGen::emit(const Def* def, BasicBlockBuilder* bb) { assertf(false, "Incomplete emit(def) definition"); } +void CodeGen::emit_builtin(const Continuation* source_cont, const Continuation* builtin, BasicBlockBuilder* bb) { + if (builtin->name() == "spirv.nonsemantic.printf") { + std::vector args; + auto string = source_cont->arg(1); + if (auto arr_type = string->type()->isa(); arr_type->elem_type() == world().type_pu8()) { + auto arr = string->as(); + std::vector the_string; + for (int i = 0; i < arr_type->dim(); i++) + the_string.push_back(arr->op(i)->as()->value().get_u8()); + the_string.push_back('\0'); + args.push_back(builder_->debug_string(the_string.data())); + } else world().ELOG("spirv.nonsemantic.printf takes a string literal"); + + for (int i = 2; i < source_cont->num_args() - 1; i++) { + args.push_back(emit(source_cont->arg(i), bb)); + } + + auto values = source_cont->arg(2); + bb->ext_instruction(bb->file_builder.void_type, non_semantic_info, 1, args); + } else { + world().ELOG("This spir-v builtin isn't recognised: %s", builtin->name()); + } + auto next = source_cont->args().back()->as_continuation(); + bb->branch(current_fn_->bbs_map[next]->label); +} + BasicBlockBuilder::BasicBlockBuilder(FnBuilder& fn_builder) : builder::SpvBasicBlockBuilder(*fn_builder.file_builder), fn_builder(fn_builder) { label = file_builder.generate_fresh_id(); diff --git a/src/thorin/be/spirv/spirv.h b/src/thorin/be/spirv/spirv.h index 9ecc0f8e0..201cfae33 100644 --- a/src/thorin/be/spirv/spirv.h +++ b/src/thorin/be/spirv/spirv.h @@ -58,6 +58,7 @@ class CodeGen : public thorin::CodeGen { void emit(const Scope& scope); void emit_epilogue(Continuation*, BasicBlockBuilder* bb); SpvId emit(const Def* def, BasicBlockBuilder* bb); + void emit_builtin(const Continuation*, const Continuation*, BasicBlockBuilder*); SpvId get_codom_type(const Continuation* fn); From 7fc55fb7f349e815d53a6c3d9365231d0665146b Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Wed, 5 May 2021 11:20:50 +0200 Subject: [PATCH 079/342] move to a FileBuilder --- src/thorin/be/spirv/spirv.cpp | 33 ++++++++++++++++--------- src/thorin/be/spirv/spirv.h | 35 +++++++++++++++++++++++---- src/thorin/be/spirv/spirv_builder.hpp | 2 +- 3 files changed, 53 insertions(+), 17 deletions(-) diff --git a/src/thorin/be/spirv/spirv.cpp b/src/thorin/be/spirv/spirv.cpp index a652631f8..3d2f65803 100644 --- a/src/thorin/be/spirv/spirv.cpp +++ b/src/thorin/be/spirv/spirv.cpp @@ -17,19 +17,28 @@ CodeGen::CodeGen(thorin::World& world, Cont2Config& kernel_config, bool debug) : thorin::CodeGen(world, debug), kernel_config_(kernel_config) {} -void CodeGen::emit_stream(std::ostream& out) { - builder::SpvFileBuilder builder; - builder_ = &builder; - builder_->capability(spv::Capability::CapabilityShader); - builder_->capability(spv::Capability::CapabilityVariablePointers); - builder_->capability(spv::Capability::CapabilityPhysicalStorageBufferAddresses); - // builder_->capability(spv::Capability::CapabilityInt16); - builder_->capability(spv::Capability::CapabilityInt64); +FileBuilder::FileBuilder(CodeGen* cg) : builder::SpvFileBuilder(), cg(cg), builtins(*this), imported_instrs(*this) { + capability(spv::Capability::CapabilityShader); + capability(spv::Capability::CapabilityVariablePointers); + capability(spv::Capability::CapabilityPhysicalStorageBufferAddresses); + // capability(spv::Capability::CapabilityInt16); + capability(spv::Capability::CapabilityInt64); + + addressing_model = spv::AddressingModelPhysicalStorageBuffer64; + memory_model = spv::MemoryModel::MemoryModelGLSL450; +} - builder_->addressing_model = spv::AddressingModelPhysicalStorageBuffer64; +Builtins::Builtins(FileBuilder&) { +} +ImportedInstructions::ImportedInstructions(FileBuilder& builder) { builder.extension("SPV_KHR_non_semantic_info"); - non_semantic_info = builder_->extended_import("NonSemantic.DebugPrintf"); + shader_printf = builder.extended_import("NonSemantic.DebugPrintf"); +} + +void CodeGen::emit_stream(std::ostream& out) { + FileBuilder builder(this); + builder_ = &builder; structure_loops(); structure_flow(); @@ -716,7 +725,9 @@ void CodeGen::emit_builtin(const Continuation* source_cont, const Continuation* } auto values = source_cont->arg(2); - bb->ext_instruction(bb->file_builder.void_type, non_semantic_info, 1, args); + bb->ext_instruction(bb->file_builder.void_type, builder_->imported_instrs.shader_printf, 1, args); + } if (builtin->name() == "get_local_id") { + } else { world().ELOG("This spir-v builtin isn't recognised: %s", builtin->name()); } diff --git a/src/thorin/be/spirv/spirv.h b/src/thorin/be/spirv/spirv.h index 201cfae33..a3d947d87 100644 --- a/src/thorin/be/spirv/spirv.h +++ b/src/thorin/be/spirv/spirv.h @@ -12,6 +12,9 @@ class CodeGen; struct Datatype; struct PtrDatatype; +struct FileBuilder; +struct FnBuilder; + struct ConvertedType { spirv::CodeGen* code_gen; const thorin::Type* src_type; @@ -22,8 +25,6 @@ struct ConvertedType { bool is_known_size() { return datatype != nullptr; } }; -struct FnBuilder; - struct BasicBlockBuilder : public builder::SpvBasicBlockBuilder { explicit BasicBlockBuilder(FnBuilder& fn_builder); @@ -43,6 +44,32 @@ struct FnBuilder : public builder::SpvFnBuilder { explicit FnBuilder(CodeGen* cg, builder::SpvFileBuilder* file_builder) : builder::SpvFnBuilder(file_builder), cg(cg) {} }; +struct Builtins { + SpvId workgroup_size; + SpvId num_workgroups; + SpvId workgroup_id; + SpvId local_id; + SpvId global_id; + SpvId local_invocation_index; + + explicit Builtins(FileBuilder&); +}; + +struct ImportedInstructions { + SpvId shader_printf; + + explicit ImportedInstructions(FileBuilder&); +}; + +struct FileBuilder : public builder::SpvFileBuilder { + CodeGen* cg; + + Builtins builtins; + ImportedInstructions imported_instrs; + + explicit FileBuilder(CodeGen* cg); +}; + class CodeGen : public thorin::CodeGen { public: CodeGen(World&, Cont2Config&, bool debug); @@ -62,15 +89,13 @@ class CodeGen : public thorin::CodeGen { SpvId get_codom_type(const Continuation* fn); - builder::SpvFileBuilder* builder_ = nullptr; + FileBuilder* builder_ = nullptr; Continuation* entry_ = nullptr; FnBuilder* current_fn_ = nullptr; TypeMap> types_; DefMap defs_; const Cont2Config& kernel_config_; - SpvId non_semantic_info; - friend PtrDatatype; }; diff --git a/src/thorin/be/spirv/spirv_builder.hpp b/src/thorin/be/spirv/spirv_builder.hpp index 254fb7cfb..371ab53ab 100644 --- a/src/thorin/be/spirv/spirv_builder.hpp +++ b/src/thorin/be/spirv/spirv_builder.hpp @@ -552,7 +552,7 @@ struct SpvFileBuilder { } spv::AddressingModel addressing_model = spv::AddressingModel::AddressingModelLogical; - spv::MemoryModel memory_model = spv::MemoryModel::MemoryModelGLSL450 ; + spv::MemoryModel memory_model = spv::MemoryModel::MemoryModelSimple; private: std::ostream* output_ = nullptr; From 1555d263c426cf8d82e12e8dd853749397df52e4 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Wed, 5 May 2021 13:07:51 +0200 Subject: [PATCH 080/342] start implementing intrinsics properly --- src/thorin/be/spirv/spirv.cpp | 113 +++++++++++++++++++----- src/thorin/be/spirv/spirv.h | 8 +- src/thorin/be/spirv/spirv_builder.hpp | 30 +++++++ src/thorin/be/spirv/spirv_datatypes.cpp | 10 ++- 4 files changed, 135 insertions(+), 26 deletions(-) diff --git a/src/thorin/be/spirv/spirv.cpp b/src/thorin/be/spirv/spirv.cpp index 3d2f65803..c80bafe38 100644 --- a/src/thorin/be/spirv/spirv.cpp +++ b/src/thorin/be/spirv/spirv.cpp @@ -17,7 +17,7 @@ CodeGen::CodeGen(thorin::World& world, Cont2Config& kernel_config, bool debug) : thorin::CodeGen(world, debug), kernel_config_(kernel_config) {} -FileBuilder::FileBuilder(CodeGen* cg) : builder::SpvFileBuilder(), cg(cg), builtins(*this), imported_instrs(*this) { +FileBuilder::FileBuilder(CodeGen* cg) : builder::SpvFileBuilder(), cg(cg) { capability(spv::Capability::CapabilityShader); capability(spv::Capability::CapabilityVariablePointers); capability(spv::Capability::CapabilityPhysicalStorageBufferAddresses); @@ -28,7 +28,36 @@ FileBuilder::FileBuilder(CodeGen* cg) : builder::SpvFileBuilder(), cg(cg), built memory_model = spv::MemoryModel::MemoryModelGLSL450; } -Builtins::Builtins(FileBuilder&) { +Builtins::Builtins(FileBuilder& builder) { + auto& world = builder.cg->world(); + auto spv_uvec3_t = builder.cg->convert(world.type_pu32(3)); + auto spv_uint_t = builder.cg->convert(world.type_pu32()); + auto spv_uvec3_pt = builder.declare_ptr_type(spv::StorageClassInput, spv_uvec3_t->type_id); + auto spv_uint_pt = builder.declare_ptr_type(spv::StorageClassInput, spv_uint_t->type_id); + + // workgroup_size = builder.constant(spv_uvec3_pt, spv::StorageClassInput); + // builder.decorate(workgroup_size, spv::DecorationBuiltIn, { spv::BuiltInWorkgroupSize }); + // builder.name(workgroup_size, "BuiltInWorkgroupSize"); + + num_workgroups = builder.variable(spv_uvec3_pt, spv::StorageClassInput); + builder.decorate(num_workgroups, spv::DecorationBuiltIn, { spv::BuiltInNumWorkgroups }); + builder.name(num_workgroups, "BuiltInNumWorkgroups"); + + workgroup_id = builder.variable(spv_uvec3_pt, spv::StorageClassInput); + builder.decorate(workgroup_id, spv::DecorationBuiltIn, { spv::BuiltInWorkgroupId }); + builder.name(workgroup_id, "BuiltInWorkgroupId"); + + local_id = builder.variable(spv_uvec3_pt, spv::StorageClassInput); + builder.decorate(local_id, spv::DecorationBuiltIn, { spv::BuiltInLocalInvocationId }); + builder.name(local_id, "BuiltInLocalInvocationId"); + + global_id = builder.variable(spv_uvec3_pt, spv::StorageClassInput); + builder.decorate(global_id, spv::DecorationBuiltIn, { spv::BuiltInGlobalInvocationId }); + builder.name(global_id, "BuiltInGlobalInvocationId"); + + local_invocation_index = builder.variable(spv_uint_pt, spv::StorageClassInput); + builder.decorate(local_invocation_index, spv::DecorationBuiltIn, { spv::BuiltInLocalInvocationIndex }); + builder.name(local_invocation_index, "BuiltInLocalInvocationIndex"); } ImportedInstructions::ImportedInstructions(FileBuilder& builder) { @@ -37,8 +66,10 @@ ImportedInstructions::ImportedInstructions(FileBuilder& builder) { } void CodeGen::emit_stream(std::ostream& out) { - FileBuilder builder(this); - builder_ = &builder; + builder_ = std::make_unique(this); + + builder_->builtins = std::make_unique(*builder_); + builder_->imported_instrs = std::make_unique(*builder_); structure_loops(); structure_flow(); @@ -48,14 +79,14 @@ void CodeGen::emit_stream(std::ostream& out) { Scope::for_each(world(), [&](const Scope& scope) { emit(scope); }); auto push_constant_arr_type = convert(world().definite_array_type(world().type_pu32(), 128))->type_id; - auto push_constant_struct_type = builder.declare_struct_type({ push_constant_arr_type }); - auto push_constant_struct_ptr_type = builder.declare_ptr_type(spv::StorageClassPushConstant, push_constant_struct_type); - builder.name(push_constant_struct_type, "ThorinPushConstant"); - builder.decorate(push_constant_struct_type, spv::DecorationBlock); - builder.decorate_member(push_constant_struct_type, 0, spv::DecorationOffset, { 0 }); - builder.decorate(push_constant_arr_type, spv::DecorationArrayStride, { 4 }); + auto push_constant_struct_type = builder_->declare_struct_type({ push_constant_arr_type }); + auto push_constant_struct_ptr_type = builder_->declare_ptr_type(spv::StorageClassPushConstant, push_constant_struct_type); + builder_->name(push_constant_struct_type, "ThorinPushConstant"); + builder_->decorate(push_constant_struct_type, spv::DecorationBlock); + builder_->decorate_member(push_constant_struct_type, 0, spv::DecorationOffset, { 0 }); + builder_->decorate(push_constant_arr_type, spv::DecorationArrayStride, { 4 }); auto push_constant_struct_ptr = builder_->variable(push_constant_struct_ptr_type, spv::StorageClassPushConstant); - builder.name(push_constant_struct_ptr, "thorin_push_constant_data"); + builder_->name(push_constant_struct_ptr, "thorin_push_constant_data"); auto entry_pt_signature = builder_->declare_fn_type({}, builder_->void_type); for (auto& cont : world().continuations()) { @@ -65,7 +96,7 @@ void CodeGen::emit_stream(std::ostream& out) { SpvId callee = defs_[cont]; - FnBuilder fn_builder(this, builder_); + FnBuilder fn_builder(this, builder_.get()); fn_builder.fn_type = entry_pt_signature; fn_builder.fn_ret_type = builder_->void_type; @@ -96,7 +127,7 @@ void CodeGen::emit_stream(std::ostream& out) { builder_->define_function(fn_builder); builder_->name(fn_builder.function_id, "entry_point_" + cont->name()); - builder_->declare_entry_point(spv::ExecutionModelGLCompute, fn_builder.function_id, "kernel_main", { push_constant_struct_ptr }); + builder_->declare_entry_point(spv::ExecutionModelGLCompute, fn_builder.function_id, "kernel_main", { push_constant_struct_ptr, builder_->builtins->local_id }); auto block = config->second->as()->block_size(); std::vector local_size = { @@ -116,7 +147,7 @@ void CodeGen::emit(const thorin::Scope& scope) { entry_ = scope.entry(); assert(entry_->is_returning()); - FnBuilder fn(this, builder_); + FnBuilder fn(this, builder_.get()); fn.scope = &scope; fn.fn_type = convert(entry_->type())->type_id; fn.fn_ret_type = get_codom_type(entry_); @@ -163,7 +194,9 @@ void CodeGen::emit(const thorin::Scope& scope) { // OpPhi requires the full list of predecessors (values, labels) // We don't have that yet! But we will need the Phi node identifier to build the basic blocks ... // To solve this we generate an id for the phi node now, but defer emission of it to a later stage - bb->phis_map[param] = {convert(param->type())->type_id, builder_->generate_fresh_id(), {} }; + auto type = convert(param->type())->type_id; + assert(type.id != 0); + bb->phis_map[param] = { type, builder_->generate_fresh_id(), {} }; } } } @@ -202,6 +235,37 @@ SpvId CodeGen::get_codom_type(const Continuation* fn) { } void CodeGen::emit_epilogue(Continuation* continuation, BasicBlockBuilder* bb) { + // Handles the potential nuances of jumping to another continuation + auto jump_to_next_cont_with_args = [&](Continuation* succ, std::vector args) { + if (args.empty()) { + bb->branch(current_fn_->labels[succ]); + } else if (args.size() == 1) { + bb->branch(current_fn_->labels[succ]); + + int i = 0; + while (is_mem(succ->param(i)) || is_unit(succ->param(i))) { + i++; + assert(i < succ->num_params()); + } + + auto& phi = current_fn_->bbs_map[succ]->phis_map[succ->param(i)]; + phi.preds.emplace_back(args[0], current_fn_->labels[continuation]); + } else { + bb->branch(current_fn_->labels[succ]); + + for (size_t i = 0, j = 0; i != succ->num_params(); ++i) { + auto param = succ->param(i); + if (is_mem(param) || is_unit(param)) + continue; + + auto& phi = current_fn_->bbs_map[succ]->phis_map[param]; + phi.preds.emplace_back(args[j], current_fn_->labels[continuation]); + + j++; + } + } + }; + if (continuation->callee() == entry_->ret_param()) { std::vector values; @@ -318,7 +382,9 @@ void CodeGen::emit_epilogue(Continuation* continuation, BasicBlockBuilder* bb) { } bb->branch(current_fn_->labels[callee]); } else if (auto builtin = continuation->callee()->isa_continuation(); builtin->is_imported()) { - emit_builtin(continuation, builtin, bb); + auto productions = emit_builtin(continuation, builtin, bb); + auto succ = continuation->args().back()->as_continuation(); + jump_to_next_cont_with_args(succ, productions); } else if (auto callee = continuation->callee()->isa_continuation(); callee && callee->is_intrinsic()) { THORIN_UNREACHABLE; //auto ret_continuation = emit_intrinsic(irbuilder, continuation); @@ -707,7 +773,11 @@ SpvId CodeGen::emit(const Def* def, BasicBlockBuilder* bb) { assertf(false, "Incomplete emit(def) definition"); } -void CodeGen::emit_builtin(const Continuation* source_cont, const Continuation* builtin, BasicBlockBuilder* bb) { +std::vector CodeGen::emit_builtin(const Continuation* source_cont, const Continuation* builtin, BasicBlockBuilder* bb) { + std::vector productions; + auto uvec3_t = convert(world().type_pu32(3)); + auto u32_t = convert(world().type_pu32()); + auto i32_t = convert(world().type_ps32()); if (builtin->name() == "spirv.nonsemantic.printf") { std::vector args; auto string = source_cont->arg(1); @@ -725,14 +795,15 @@ void CodeGen::emit_builtin(const Continuation* source_cont, const Continuation* } auto values = source_cont->arg(2); - bb->ext_instruction(bb->file_builder.void_type, builder_->imported_instrs.shader_printf, 1, args); + bb->ext_instruction(bb->file_builder.void_type, builder_->imported_instrs->shader_printf, 1, args); } if (builtin->name() == "get_local_id") { - + auto vector = bb->load(uvec3_t->type_id, builder_->builtins->local_id); + auto extracted = bb->vector_extract_dynamic(u32_t->type_id, vector, emit(source_cont->arg(1), bb)); + productions.push_back(bb->bitcast(i32_t->type_id, extracted)); } else { world().ELOG("This spir-v builtin isn't recognised: %s", builtin->name()); } - auto next = source_cont->args().back()->as_continuation(); - bb->branch(current_fn_->bbs_map[next]->label); + return productions; } BasicBlockBuilder::BasicBlockBuilder(FnBuilder& fn_builder) diff --git a/src/thorin/be/spirv/spirv.h b/src/thorin/be/spirv/spirv.h index a3d947d87..91f5b6533 100644 --- a/src/thorin/be/spirv/spirv.h +++ b/src/thorin/be/spirv/spirv.h @@ -64,8 +64,8 @@ struct ImportedInstructions { struct FileBuilder : public builder::SpvFileBuilder { CodeGen* cg; - Builtins builtins; - ImportedInstructions imported_instrs; + std::unique_ptr builtins; + std::unique_ptr imported_instrs; explicit FileBuilder(CodeGen* cg); }; @@ -85,11 +85,11 @@ class CodeGen : public thorin::CodeGen { void emit(const Scope& scope); void emit_epilogue(Continuation*, BasicBlockBuilder* bb); SpvId emit(const Def* def, BasicBlockBuilder* bb); - void emit_builtin(const Continuation*, const Continuation*, BasicBlockBuilder*); + std::vector emit_builtin(const Continuation*, const Continuation*, BasicBlockBuilder*); SpvId get_codom_type(const Continuation* fn); - FileBuilder* builder_ = nullptr; + std::unique_ptr builder_; Continuation* entry_ = nullptr; FnBuilder* current_fn_ = nullptr; TypeMap> types_; diff --git a/src/thorin/be/spirv/spirv_builder.hpp b/src/thorin/be/spirv/spirv_builder.hpp index 371ab53ab..8881bc369 100644 --- a/src/thorin/be/spirv/spirv_builder.hpp +++ b/src/thorin/be/spirv/spirv_builder.hpp @@ -118,6 +118,27 @@ struct SpvBasicBlockBuilder : public SpvSectionBuilder { return id; } + SpvId vector_extract_dynamic(SpvId target_type, SpvId vector, SpvId index) { + op(spv::Op::OpVectorExtractDynamic, 5); + ref_id(target_type); + auto id = generate_fresh_id(); + ref_id(id); + ref_id(vector); + ref_id(index); + return id; + } + + SpvId vector_insert_dynamic(SpvId target_type, SpvId vector, SpvId component, SpvId index) { + op(spv::Op::OpVectorInsertDynamic, 6); + ref_id(target_type); + auto id = generate_fresh_id(); + ref_id(id); + ref_id(vector); + ref_id(component); + ref_id(index); + return id; + } + SpvId bitcast(SpvId target_type, SpvId value) { op(spv::Op::OpBitcast, 4); auto id = generate_fresh_id(); @@ -423,6 +444,15 @@ struct SpvFileBuilder { return id; } + SpvId declare_vector_type(SpvId component_type, uint32_t dim) { + types_constants.op(spv::Op::OpTypeVector, 4); + auto id = generate_fresh_id(); + types_constants.ref_id(id); + types_constants.ref_id(component_type); + types_constants.literal_int(dim); + return id; + } + void decorate(SpvId target, spv::Decoration decoration, std::vector extra = {}) { annotations.op(spv::Op::OpDecorate, 3 + extra.size()); annotations.ref_id(target); diff --git a/src/thorin/be/spirv/spirv_datatypes.cpp b/src/thorin/be/spirv/spirv_datatypes.cpp index 4a11b756c..3e6b27d7b 100644 --- a/src/thorin/be/spirv/spirv_datatypes.cpp +++ b/src/thorin/be/spirv/spirv_datatypes.cpp @@ -121,7 +121,7 @@ ConvertedType* CodeGen::convert(const Type* type) { switch (type->tag()) { #define THORIN_Q_TYPE(T, M) \ case PrimType_##T: \ - type = world().prim_type(PrimType_p##M, 1); \ + type = world().prim_type(PrimType_p##M, type->as()->length()); \ break; #include "thorin/tables/primtypetable.h" #undef THORIN_Q_TYPE @@ -131,6 +131,14 @@ ConvertedType* CodeGen::convert(const Type* type) { if (auto iter = types_.find(type); iter != types_.end()) return iter->second.get(); ConvertedType* converted = types_.emplace(type, std::make_unique(this) ).first->second.get(); converted->src_type = type; + + if (auto vec = type->isa(); vec && vec->length() > 1) { + auto component = vec->scalarize(); + auto conv_comp = convert(component); + converted->type_id = builder_->declare_vector_type(conv_comp->type_id, (uint32_t)vec->length()); + return converted; + } + switch (type->tag()) { // Boolean types are typically packed intelligently when declaring in local variables, however with vanilla Vulkan 1.0 they can only be represented via 32-bit integers // Using extensions, we could use 16 or 8-bit ints instead From 96a346aefb6c4a8a952e7866e47b115ec3c56a49 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Wed, 5 May 2021 14:59:14 +0200 Subject: [PATCH 081/342] cleanup --- src/thorin/be/spirv/spirv.cpp | 76 ++++++++++++++--------------------- 1 file changed, 31 insertions(+), 45 deletions(-) diff --git a/src/thorin/be/spirv/spirv.cpp b/src/thorin/be/spirv/spirv.cpp index c80bafe38..40de8c2af 100644 --- a/src/thorin/be/spirv/spirv.cpp +++ b/src/thorin/be/spirv/spirv.cpp @@ -237,32 +237,14 @@ SpvId CodeGen::get_codom_type(const Continuation* fn) { void CodeGen::emit_epilogue(Continuation* continuation, BasicBlockBuilder* bb) { // Handles the potential nuances of jumping to another continuation auto jump_to_next_cont_with_args = [&](Continuation* succ, std::vector args) { - if (args.empty()) { - bb->branch(current_fn_->labels[succ]); - } else if (args.size() == 1) { - bb->branch(current_fn_->labels[succ]); - - int i = 0; - while (is_mem(succ->param(i)) || is_unit(succ->param(i))) { - i++; - assert(i < succ->num_params()); - } - - auto& phi = current_fn_->bbs_map[succ]->phis_map[succ->param(i)]; - phi.preds.emplace_back(args[0], current_fn_->labels[continuation]); - } else { - bb->branch(current_fn_->labels[succ]); - - for (size_t i = 0, j = 0; i != succ->num_params(); ++i) { - auto param = succ->param(i); - if (is_mem(param) || is_unit(param)) - continue; - - auto& phi = current_fn_->bbs_map[succ]->phis_map[param]; - phi.preds.emplace_back(args[j], current_fn_->labels[continuation]); - - j++; - } + bb->branch(current_fn_->labels[succ]); + for (size_t i = 0, j = 0; i != succ->num_params(); ++i) { + auto param = succ->param(i); + if (is_mem(param) || is_unit(param)) + continue; + auto& phi = current_fn_->bbs_map[succ]->phis_map[param]; + phi.preds.emplace_back(args[j], current_fn_->labels[continuation]); + j++; } }; @@ -282,8 +264,19 @@ void CodeGen::emit_epilogue(Continuation* continuation, BasicBlockBuilder* bb) { case 1: bb->return_value(values[0]); break; default: bb->return_value(bb->composite(current_fn_->fn_ret_type, values)); } - } - else if (continuation->callee() == world().branch()) { + } else if (auto callee = continuation->callee()->isa_continuation(); callee && callee->is_basicblock()) { // ordinary jump + int index = -1; + for (auto& arg : continuation->args()) { + index++; + auto val = emit(arg, bb); + if (is_mem(arg) || is_unit(arg)) continue; + bb->args[arg] = val; + auto* param = callee->param(index); + auto& phi = current_fn_->bbs_map[callee]->phis_map[param]; + phi.preds.emplace_back(bb->args[arg], current_fn_->labels[continuation]); + } + bb->branch(current_fn_->labels[callee]); + } else if (continuation->callee() == world().branch()) { auto& domtree = current_fn_->scope->b_cfg().domtree(); auto merge_cont = domtree.idom(current_fn_->scope->f_cfg().operator[](continuation))->continuation(); SpvId merge_bb; @@ -369,35 +362,28 @@ void CodeGen::emit_epilogue(Continuation* continuation, BasicBlockBuilder* bb) { auto callee = continuation->op(0)->as_continuation(); // TODO phis bb->branch(current_fn_->bbs_map[callee]->label); - } else if (auto callee = continuation->callee()->isa_continuation(); callee && callee->is_basicblock()) { // ordinary jump - int index = -1; - for (auto& arg : continuation->args()) { - index++; - auto val = emit(arg, bb); - if (is_mem(arg) || is_unit(arg)) continue; - bb->args[arg] = val; - auto* param = callee->param(index); - auto& phi = current_fn_->bbs_map[callee]->phis_map[param]; - phi.preds.emplace_back(bb->args[arg], current_fn_->labels[continuation]); - } - bb->branch(current_fn_->labels[callee]); } else if (auto builtin = continuation->callee()->isa_continuation(); builtin->is_imported()) { + // Ensure we emit previous memory operations + assert(is_mem(continuation->arg(0))); + emit(continuation->arg(0), bb); + auto productions = emit_builtin(continuation, builtin, bb); auto succ = continuation->args().back()->as_continuation(); jump_to_next_cont_with_args(succ, productions); - } else if (auto callee = continuation->callee()->isa_continuation(); callee && callee->is_intrinsic()) { + } else if (auto intrinsic = continuation->callee()->isa_continuation(); callee && callee->is_intrinsic()) { THORIN_UNREACHABLE; //auto ret_continuation = emit_intrinsic(irbuilder, continuation); //irbuilder.CreateBr(cont2bb(ret_continuation)); } else { // function/closure call // put all first-order args into an array - std::vector args; + std::vector call_args; const Def* ret_arg = nullptr; for (auto arg : continuation->args()) { if (arg->order() == 0) { auto arg_type = arg->type(); + auto arg_val = emit(arg, bb); if (arg_type == world().unit() || arg_type == world().mem_type()) continue; - args.push_back(emit(arg, bb)); + call_args.push_back(arg_val); } else { assert(!ret_arg); ret_arg = arg; @@ -407,8 +393,8 @@ void CodeGen::emit_epilogue(Continuation* continuation, BasicBlockBuilder* bb) { auto ret_type = get_codom_type(continuation); SpvId call_result; - if (auto callee = continuation->callee()->isa_continuation()) { - call_result = bb->call(ret_type, emit(callee, bb), args); + if (auto called_continuation = continuation->callee()->isa_continuation()) { + call_result = bb->call(ret_type, emit(called_continuation, bb), call_args); } else { // must be a closure THORIN_UNREACHABLE; From 1fcc707e6b656a431a7339e87757ebb64182e808 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Wed, 5 May 2021 15:14:01 +0200 Subject: [PATCH 082/342] make a few more declaration types unique --- src/thorin/be/spirv/spirv_builder.hpp | 44 ++++++++++++++++++--------- 1 file changed, 29 insertions(+), 15 deletions(-) diff --git a/src/thorin/be/spirv/spirv_builder.hpp b/src/thorin/be/spirv/spirv_builder.hpp index 8881bc369..921b80189 100644 --- a/src/thorin/be/spirv/spirv_builder.hpp +++ b/src/thorin/be/spirv/spirv_builder.hpp @@ -338,28 +338,31 @@ struct SpvFnBuilder { SpvId generate_fresh_id(); }; -inline bool operator==(const SpvId &a, const SpvId &b) { return a.id == b.id; } - struct SpvFileBuilder { - enum UniqueTypeTag { + + enum UniqueDeclTag { NONE, - FN_TYPE + FN_TYPE, + PTR_TYPE, + DEF_ARR_TYPE, + CONSTANT, }; - struct UniqueTypeKey { - UniqueTypeTag tag; - std::vector members; + /// Prevents duplicate declarations + struct UniqueDeclKey { + UniqueDeclTag tag; + std::vector members; - bool operator==(const UniqueTypeKey &b) const { + bool operator==(const UniqueDeclKey &b) const { return tag == b.tag && members == b.members; } }; - struct UniqueTypeKeyHasher { - size_t operator() (const UniqueTypeKey& key) const { + struct UniqueDeclKeyHasher { + size_t operator() (const UniqueDeclKey& key) const { size_t acc = 0; for (auto id : key.members) - acc ^= std::hash{}(id.id); + acc ^= std::hash{}(id); return std::hash{}(key.tag) ^ acc; } }; @@ -403,26 +406,33 @@ struct SpvFileBuilder { } SpvId declare_ptr_type(spv::StorageClass storage_class, SpvId element_type) { + auto key = UniqueDeclKey { PTR_TYPE, { element_type.id, (uint32_t) storage_class } }; + if (auto iter = unique_decls.find(key); iter != unique_decls.end()) return iter->second; types_constants.op(spv::Op::OpTypePointer, 4); auto id = generate_fresh_id(); types_constants.ref_id(id); types_constants.literal_int(storage_class); types_constants.ref_id(element_type); + unique_decls[key] = id; return id; } SpvId declare_array_type(SpvId element_type, SpvId dim) { + auto key = UniqueDeclKey { DEF_ARR_TYPE, { element_type.id, dim.id } }; + if (auto iter = unique_decls.find(key); iter != unique_decls.end()) return iter->second; types_constants.op(spv::Op::OpTypeArray, 4); auto id = generate_fresh_id(); types_constants.ref_id(id); types_constants.ref_id(element_type); types_constants.ref_id(dim); + unique_decls[key] = id; return id; } SpvId declare_fn_type(std::vector dom, SpvId codom) { - auto key = UniqueTypeKey { FN_TYPE, dom }; - key.members.push_back(codom); + auto key = UniqueDeclKey { FN_TYPE, {} }; + for (auto d : dom) key.members.push_back(d.id); + key.members.push_back(codom.id); if (auto iter = unique_decls.find(key); iter != unique_decls.end()) return iter->second; types_constants.op(spv::Op::OpTypeFunction, 3 + dom.size()); @@ -486,13 +496,17 @@ struct SpvFileBuilder { return id; } - SpvId constant(SpvId type, std::vector&& bit_pattern) { + SpvId constant(SpvId type, std::vector bit_pattern) { + auto key = UniqueDeclKey { CONSTANT, bit_pattern }; + key.members.push_back(type.id); + if (auto iter = unique_decls.find(key); iter != unique_decls.end()) return iter->second; types_constants.op(spv::Op::OpConstant, 3 + bit_pattern.size()); auto id = generate_fresh_id(); types_constants.ref_id(type); types_constants.ref_id(id); for (auto arg : bit_pattern) types_constants.data_.push_back(arg); + unique_decls[key] = id; return id; } @@ -603,7 +617,7 @@ struct SpvFileBuilder { SpvSectionBuilder fn_defs; // SPIR-V disallows duplicate non-aggregate type declarations, we protect against these with this - std::unordered_map unique_decls; + std::unordered_map unique_decls; SpvId declare_void_type() { types_constants.op(spv::Op::OpTypeVoid, 2); From 3430fe44acd2e88d1b4bf0aae3f1f3dfccbeda35 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Wed, 5 May 2021 20:42:04 +0200 Subject: [PATCH 083/342] decorate datatype pointers for GEP --- src/thorin/be/spirv/spirv_datatypes.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/thorin/be/spirv/spirv_datatypes.cpp b/src/thorin/be/spirv/spirv_datatypes.cpp index 3e6b27d7b..7d6d98e9f 100644 --- a/src/thorin/be/spirv/spirv_datatypes.cpp +++ b/src/thorin/be/spirv/spirv_datatypes.cpp @@ -208,6 +208,11 @@ ConvertedType* CodeGen::convert(const Type* type) { pointee = arr->elem_type(); ConvertedType* element = convert(pointee); converted->type_id = builder_->declare_ptr_type(storage_class, element->type_id); + + if (ptr->addr_space() == AddrSpace::Global) { + assert(element->datatype && "Can only have physical pointers to known-size types"); + builder_->decorate(converted->type_id, spv::DecorationArrayStride, {(uint32_t) element->datatype->serialized_size()}); + } } ptr_done: break; From ec1c6fce3e9a82b5c19e845ca832068bc19652e6 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Thu, 6 May 2021 12:56:58 +0200 Subject: [PATCH 084/342] fancy casting --- src/thorin/be/spirv/spirv.cpp | 157 +++++++++++++++++++++++- src/thorin/be/spirv/spirv_builder.hpp | 34 +---- src/thorin/be/spirv/spirv_datatypes.cpp | 10 +- 3 files changed, 159 insertions(+), 42 deletions(-) diff --git a/src/thorin/be/spirv/spirv.cpp b/src/thorin/be/spirv/spirv.cpp index 40de8c2af..b26f8ed98 100644 --- a/src/thorin/be/spirv/spirv.cpp +++ b/src/thorin/be/spirv/spirv.cpp @@ -13,6 +13,73 @@ namespace thorin { namespace thorin::spirv { +/// Used as a dummy SSA value for emitting things like mem/unit +/// Should never make it in the binary files ! +constexpr SpvId spv_none { 0 }; + +// SPIR-V has 3 "kinds" of primitives, and the user may declare arbitrary bitwidths, the following helps in translation: +enum class PrimTypeKind { + Signed, Unsigned, Float +}; +inline PrimTypeKind classify_primtype(const PrimType* type) { + switch (type->tag()) { +#define THORIN_QS_TYPE(T, M) THORIN_PS_TYPE(T, M) +#define THORIN_PS_TYPE(T, M) \ +case PrimType_##T: \ + return PrimTypeKind::Signed; \ + break; +#include "thorin/tables/primtypetable.h" +#undef THORIN_QS_TYPE +#undef THORIN_PS_TYPE + +#define THORIN_QU_TYPE(T, M) THORIN_PU_TYPE(T, M) +#define THORIN_PU_TYPE(T, M) \ +case PrimType_##T: \ + return PrimTypeKind::Unsigned; \ + break; +#include "thorin/tables/primtypetable.h" +#undef THORIN_QU_TYPE +#undef THORIN_PU_TYPE + +#define THORIN_QF_TYPE(T, M) THORIN_PF_TYPE(T, M) +#define THORIN_PF_TYPE(T, M) \ +case PrimType_##T: \ + return PrimTypeKind::Float; \ + break; +#include "thorin/tables/primtypetable.h" +#undef THORIN_QF_TYPE +#undef THORIN_PF_TYPE + default: THORIN_UNREACHABLE; + } +} +inline const PrimType* get_primtype(World& world, PrimTypeKind kind, int bitwidth, int length) { +#define GET_PRIMTYPE_WITH_KIND(kind) \ +switch (bitwidth) { \ + case 8: return world.type_p##kind##8(); \ + case 16: return world.type_p##kind##16(); \ + case 32: return world.type_p##kind##32(); \ + case 64: return world.type_p##kind##64(); \ +} + +#define GET_PRIMTYPE_WITH_KIND_F(kind) \ +switch (bitwidth) { \ + case 8: world.ELOG("8-bit floats do not exist"); \ + case 16: return world.type_p##kind##16(); \ + case 32: return world.type_p##kind##32(); \ + case 64: return world.type_p##kind##64(); \ +} + + switch (kind) { + case PrimTypeKind::Signed: GET_PRIMTYPE_WITH_KIND(s); THORIN_UNREACHABLE; + case PrimTypeKind::Unsigned: GET_PRIMTYPE_WITH_KIND(u); THORIN_UNREACHABLE; + case PrimTypeKind::Float: GET_PRIMTYPE_WITH_KIND_F(f); THORIN_UNREACHABLE; + default: THORIN_UNREACHABLE; + } + +#undef GET_PRIMTYPE_WITH_KIND +#undef GET_PRIMTYPE_WITH_KIND_F +} + CodeGen::CodeGen(thorin::World& world, Cont2Config& kernel_config, bool debug) : thorin::CodeGen(world, debug), kernel_config_(kernel_config) {} @@ -449,8 +516,6 @@ void CodeGen::emit_epilogue(Continuation* continuation, BasicBlockBuilder* bb) { } } -constexpr SpvId spv_none { 0 }; - SpvId CodeGen::emit(const Def* def, BasicBlockBuilder* bb) { if (auto bin = def->isa()) { SpvId lhs = emit(bin->lhs(), bb); @@ -677,8 +742,6 @@ SpvId CodeGen::emit(const Def* def, BasicBlockBuilder* bb) { auto type = convert(lea->ptr_type()); auto offset = emit(lea->index(), bb); return bb->ptr_access_chain(type->type_id, emit(lea->ptr(), bb), offset, {}); - } else if (auto bitcast = def->isa()) { - return bb->bitcast(convert(bitcast->type())->type_id, emit(bitcast->from(), bb)); } else if (auto aggop = def->isa()) { auto spv_agg = emit(aggop->agg(), bb); auto agg_type = convert(aggop->agg()->type())->type_id; @@ -753,6 +816,88 @@ SpvId CodeGen::emit(const Def* def, BasicBlockBuilder* bb) { return bb->insert(agg_type, value, spv_agg, { index }); } else THORIN_UNREACHABLE; + } else if (auto conv = def->isa()) { + auto src_type = conv->from()->type(); + auto dst_type = conv->type(); + + auto conv_src_type = convert(src_type); + auto conv_dst_type = convert(dst_type); + + if (auto bitcast = def->isa()) { + if (conv_src_type->datatype->serialized_size() != conv_dst_type->datatype->serialized_size()) + world().ELOG("Source (%) and destination (%) datatypes sizes do not match (% vs % bytes)", src_type->to_string(), dst_type->to_string(), conv_src_type->datatype->serialized_size(), conv_dst_type->datatype->serialized_size()); + + return bb->convert(spv::OpBitcast, convert(bitcast->type())->type_id, emit(bitcast->from(), bb)); + } else if (auto cast = def->isa()) { + // NB: all ops used here are scalar/vector agnostic + auto src_prim = src_type->isa(); + auto dst_prim = dst_type->isa(); + if (!src_prim || !dst_prim || src_prim->length() != dst_prim->length()) + world().ELOG("Illegal cast: % to %, casts are only supported between primitives with identical vector length", src_type->to_string(), dst_type->to_string()); + + auto length = src_prim->length(); + + auto src_kind = classify_primtype(src_prim); + auto dst_kind = classify_primtype(dst_prim); + size_t src_bitwidth = conv_src_type->datatype->serialized_size(); + size_t dst_bitwidth = conv_src_type->datatype->serialized_size(); + + SpvId data = emit(cast->from(), bb); + + // If floating point is involved (src or dst), OpConvert*ToF and OpConvertFTo* can take care of the bit width transformation so no need for any chopping/expanding + if (src_kind == PrimTypeKind::Float || dst_kind == PrimTypeKind::Float) { + auto target_type = convert(get_primtype(world(), dst_kind, dst_bitwidth, length))->type_id; + switch (src_kind) { + case PrimTypeKind::Signed: data = bb->convert(spv::OpConvertSToF, target_type, data); break; + case PrimTypeKind::Unsigned: data = bb->convert(spv::OpConvertUToF, target_type, data); break; + case PrimTypeKind::Float: + switch (dst_kind) { + case PrimTypeKind::Signed: data = bb->convert(spv::OpConvertFToS, target_type, data); break; + case PrimTypeKind::Unsigned: data = bb->convert(spv::OpConvertFToU, target_type, data); break; + default: THORIN_UNREACHABLE; + } + break; + } + } else { + // we expand first and shrink last to minimize precision losses, with bitcast in the middle + bool needs_chopping = src_bitwidth > dst_bitwidth; + bool needs_expanding = src_bitwidth < dst_bitwidth; + + if (needs_expanding) { + auto target_type = convert(get_primtype(world(), src_kind, src_bitwidth, length))->type_id; + switch (src_kind) { + case PrimTypeKind::Signed: + data = bb->convert(spv::OpSConvert, target_type, data); + break; + case PrimTypeKind::Unsigned: + data = bb->convert(spv::OpUConvert, target_type, data); + break; + case PrimTypeKind::Float: + data = bb->convert(spv::OpFConvert, target_type, data); + break; + } + } + + auto expanded_bitwidth = needs_expanding ? dst_bitwidth : src_bitwidth; + auto bitcast_target_type = convert(get_primtype(world(), dst_kind, expanded_bitwidth, length))->type_id; + data = bb->convert(spv::OpBitcast, bitcast_target_type, data); + + if (needs_chopping) { + auto target_type = convert(get_primtype(world(), dst_kind, dst_bitwidth, length))->type_id; + switch (dst_kind) { + case PrimTypeKind::Signed: + data = bb->convert(spv::OpSConvert, target_type, data); + break; + case PrimTypeKind::Unsigned: + data = bb->convert(spv::OpUConvert, target_type, data); + break; + case PrimTypeKind::Float: + data = bb->convert(spv::OpFConvert, target_type, data); + break; + } + } + } + } else THORIN_UNREACHABLE; } else if (def->isa()) { return bb->undef(convert(def->type())->type_id); } @@ -782,10 +927,10 @@ std::vector CodeGen::emit_builtin(const Continuation* source_cont, const auto values = source_cont->arg(2); bb->ext_instruction(bb->file_builder.void_type, builder_->imported_instrs->shader_printf, 1, args); - } if (builtin->name() == "get_local_id") { + } else if (builtin->name() == "get_local_id") { auto vector = bb->load(uvec3_t->type_id, builder_->builtins->local_id); auto extracted = bb->vector_extract_dynamic(u32_t->type_id, vector, emit(source_cont->arg(1), bb)); - productions.push_back(bb->bitcast(i32_t->type_id, extracted)); + productions.push_back(bb->convert(spv::OpBitcast, i32_t->type_id, extracted)); } else { world().ELOG("This spir-v builtin isn't recognised: %s", builtin->name()); } diff --git a/src/thorin/be/spirv/spirv_builder.hpp b/src/thorin/be/spirv/spirv_builder.hpp index 921b80189..44a2f287f 100644 --- a/src/thorin/be/spirv/spirv_builder.hpp +++ b/src/thorin/be/spirv/spirv_builder.hpp @@ -139,37 +139,9 @@ struct SpvBasicBlockBuilder : public SpvSectionBuilder { return id; } - SpvId bitcast(SpvId target_type, SpvId value) { - op(spv::Op::OpBitcast, 4); - auto id = generate_fresh_id(); - ref_id(target_type); - ref_id(id); - ref_id(value); - return id; - } - - /// Change bit-width - SpvId u_convert(SpvId target_type, SpvId value) { - op(spv::Op::OpUConvert, 4); - auto id = generate_fresh_id(); - ref_id(target_type); - ref_id(id); - ref_id(value); - return id; - } - - /// Change bit-width - SpvId s_convert(SpvId target_type, SpvId value) { - op(spv::Op::OpSConvert, 4); - auto id = generate_fresh_id(); - ref_id(target_type); - ref_id(id); - ref_id(value); - return id; - } - - SpvId convert_u_ptr(SpvId target_type, SpvId value) { - op(spv::Op::OpConvertUToPtr, 4); + // Used for almost all conversion operations + SpvId convert(spv::Op op_, SpvId target_type, SpvId value) { + op(op_, 4); auto id = generate_fresh_id(); ref_id(target_type); ref_id(id); diff --git a/src/thorin/be/spirv/spirv_datatypes.cpp b/src/thorin/be/spirv/spirv_datatypes.cpp index 7d6d98e9f..aaa255844 100644 --- a/src/thorin/be/spirv/spirv_datatypes.cpp +++ b/src/thorin/be/spirv/spirv_datatypes.cpp @@ -20,7 +20,7 @@ SpvId ScalarDatatype::emit_deserialization(BasicBlockBuilder& bb, spv::StorageCl serialization_types; auto cell = bb.access_chain(arr_cell_tid, array, { base_offset }); auto loaded = bb.load(u32_tid, cell); - return bb.bitcast(type->type_id, loaded); + return bb.convert(spv::OpBitcast, type->type_id, loaded); } void ScalarDatatype::emit_serialization(BasicBlockBuilder& bb, spv::StorageClass storage_class, SpvId array, SpvId base_offset, SpvId data) { @@ -28,7 +28,7 @@ void ScalarDatatype::emit_serialization(BasicBlockBuilder& bb, spv::StorageClass assert(size_in_bytes == 4); serialization_types; auto cell = bb.access_chain(arr_cell_tid, array, { base_offset }); - auto casted = bb.bitcast(u32_tid, data); + auto casted = bb.convert(spv::OpBitcast, u32_tid, data); bb.store(casted, cell); } @@ -40,13 +40,13 @@ SpvId PtrDatatype::emit_deserialization(BasicBlockBuilder& bb, spv::StorageClass auto cell0 = bb.access_chain(arr_cell_tid, array, { base_offset }); auto cell1 = bb.access_chain(arr_cell_tid, array, { bb.binop(spv::OpIAdd, u32_tid, base_offset, bb.file_builder.constant(u32_tid, { (uint32_t) 1 })) }); - auto lower = bb.u_convert(u64_tid, bb.load(u32_tid, cell0)); - auto upper = bb.u_convert(u64_tid, bb.load(u32_tid, cell1)); + auto lower = bb.convert(spv::OpUConvert, u64_tid, bb.load(u32_tid, cell0)); + auto upper = bb.convert(spv::OpUConvert, u64_tid, bb.load(u32_tid, cell1)); SpvId c32 = bb.file_builder.constant(u32_tid, { 32 }); auto merged = bb.binop(spv::OpBitwiseOr, u64_tid, lower, bb.binop(spv::OpShiftLeftLogical, u64_tid, upper, c32)); - return bb.convert_u_ptr(type->type_id, merged); + return bb.convert(spv::OpConvertUToPtr, type->type_id, merged); } void PtrDatatype::emit_serialization(BasicBlockBuilder& bb, spv::StorageClass storage_class, SpvId array, SpvId base_offset, SpvId data) { From 2c187dc68d3bfbda4ececbb5bf004ec8dd24a11f Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Thu, 6 May 2021 13:07:33 +0200 Subject: [PATCH 085/342] implement insert/extract for vectors --- src/thorin/be/spirv/spirv.cpp | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/src/thorin/be/spirv/spirv.cpp b/src/thorin/be/spirv/spirv.cpp index b26f8ed98..c91fe17f7 100644 --- a/src/thorin/be/spirv/spirv.cpp +++ b/src/thorin/be/spirv/spirv.cpp @@ -767,20 +767,17 @@ SpvId CodeGen::emit(const Def* def, BasicBlockBuilder* bb) { auto target_type = convert(extract->type())->type_id; auto constant_index = aggop->index()->isa(); - // skip if the index is a constant + // We have a fast-path: if the index is constant, we can simply use OpCompositeExtract if (aggop->agg()->type()->isa() && constant_index == nullptr) { assert(aggop->agg()->type()->isa()); assert(!is_mem(extract)); return bb->load(target_type, copy_to_alloca(target_type).second); } - // TODO: evaluate what to do with those - // if (extract->agg()->type()->isa()) - // return irbuilder.CreateExtractElement(llvm_agg, llvm_idx); + if (extract->agg()->type()->isa()) + return bb->vector_extract_dynamic(target_type, spv_agg, emit(extract->index(), bb)); - // tuple/struct - - // index *must* be constant + // index *must* be constant for the remaining possible cases assert(constant_index != nullptr); uint32_t index = constant_index->value().get_u32(); @@ -804,13 +801,10 @@ SpvId CodeGen::emit(const Def* def, BasicBlockBuilder* bb) { return bb->load(agg_type, variable); } - // TODO: evaluate what to do with those - //if (insert->agg()->type()->isa()) - // return irbuilder.CreateInsertElement(llvm_agg, emit(aggop->as()->value()), llvm_idx); - - // tuple/struct + if (insert->agg()->type()->isa()) + return bb->vector_insert_dynamic(agg_type, spv_agg, value, emit(insert->index(), bb)); - // index *must* be constant + // index *must* be constant for the remaining possible cases assert(constant_index != nullptr); uint32_t index = constant_index->value().get_u32(); From 62b4cb841ad57b92b2f00f882f3506b563e9d1bb Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Thu, 6 May 2021 13:08:25 +0200 Subject: [PATCH 086/342] fix get_primtype with vectors --- src/thorin/be/spirv/spirv.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/thorin/be/spirv/spirv.cpp b/src/thorin/be/spirv/spirv.cpp index c91fe17f7..032be25db 100644 --- a/src/thorin/be/spirv/spirv.cpp +++ b/src/thorin/be/spirv/spirv.cpp @@ -55,18 +55,18 @@ case PrimType_##T: \ inline const PrimType* get_primtype(World& world, PrimTypeKind kind, int bitwidth, int length) { #define GET_PRIMTYPE_WITH_KIND(kind) \ switch (bitwidth) { \ - case 8: return world.type_p##kind##8(); \ - case 16: return world.type_p##kind##16(); \ - case 32: return world.type_p##kind##32(); \ - case 64: return world.type_p##kind##64(); \ + case 8: return world.type_p##kind##8 (length); \ + case 16: return world.type_p##kind##16(length); \ + case 32: return world.type_p##kind##32(length); \ + case 64: return world.type_p##kind##64(length); \ } #define GET_PRIMTYPE_WITH_KIND_F(kind) \ switch (bitwidth) { \ case 8: world.ELOG("8-bit floats do not exist"); \ - case 16: return world.type_p##kind##16(); \ - case 32: return world.type_p##kind##32(); \ - case 64: return world.type_p##kind##64(); \ + case 16: return world.type_p##kind##16(length); \ + case 32: return world.type_p##kind##32(length); \ + case 64: return world.type_p##kind##64(length); \ } switch (kind) { From a0a2b004722a3232521d88f7eea4a925b4525b33 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Thu, 6 May 2021 13:11:30 +0200 Subject: [PATCH 087/342] small cleanup --- src/thorin/be/spirv/spirv.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/thorin/be/spirv/spirv.cpp b/src/thorin/be/spirv/spirv.cpp index 032be25db..23149d150 100644 --- a/src/thorin/be/spirv/spirv.cpp +++ b/src/thorin/be/spirv/spirv.cpp @@ -661,7 +661,6 @@ SpvId CodeGen::emit(const Def* def, BasicBlockBuilder* bb) { auto variant_datatype = (ProductDatatype*) convert(variant_type)->datatype.get(); if (variant_datatype->elements_types.size() > 1) { - auto ptr_type = convert(world().ptr_type(world().type_pu32(), 1, 4, AddrSpace::Function))->type_id; auto alloc_type = convert(world().ptr_type(variant_datatype->elements_types[1]->src_type, 1, 4, AddrSpace::Function))->type_id; auto payload_arr = current_fn_->variable(alloc_type, spv::StorageClassFunction); auto converted_payload_type = convert(variant_type->op(variant->index())); @@ -687,7 +686,6 @@ SpvId CodeGen::emit(const Def* def, BasicBlockBuilder* bb) { auto target_type = convert(def->type()); assert(variant_datatype->elements_types.size() > 1 && "Can't extract zero-sized datatypes"); - auto ptr_type = convert(world().ptr_type(world().type_pu32(), 1, 4, AddrSpace::Function))->type_id; auto alloc_type = convert(world().ptr_type(variant_datatype->elements_types[1]->src_type, 1, 4, AddrSpace::Function))->type_id; auto payload_arr = current_fn_->variable(alloc_type, spv::StorageClassFunction); auto payload = bb->extract(variant_datatype->elements_types[1]->type_id, emit(vextract->value(), bb), {1}); @@ -909,17 +907,16 @@ std::vector CodeGen::emit_builtin(const Continuation* source_cont, const if (auto arr_type = string->type()->isa(); arr_type->elem_type() == world().type_pu8()) { auto arr = string->as(); std::vector the_string; - for (int i = 0; i < arr_type->dim(); i++) + for (size_t i = 0; i < arr_type->dim(); i++) the_string.push_back(arr->op(i)->as()->value().get_u8()); the_string.push_back('\0'); args.push_back(builder_->debug_string(the_string.data())); } else world().ELOG("spirv.nonsemantic.printf takes a string literal"); - for (int i = 2; i < source_cont->num_args() - 1; i++) { + for (size_t i = 2; i < source_cont->num_args() - 1; i++) { args.push_back(emit(source_cont->arg(i), bb)); } - auto values = source_cont->arg(2); bb->ext_instruction(bb->file_builder.void_type, builder_->imported_instrs->shader_printf, 1, args); } else if (builtin->name() == "get_local_id") { auto vector = bb->load(uvec3_t->type_id, builder_->builtins->local_id); From c34cdb913c86711831d67cfdca0bf18b4d2b63b7 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Thu, 6 May 2021 13:19:24 +0200 Subject: [PATCH 088/342] implement ptr serialization --- src/thorin/be/spirv/spirv_datatypes.cpp | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/src/thorin/be/spirv/spirv_datatypes.cpp b/src/thorin/be/spirv/spirv_datatypes.cpp index aaa255844..5260b0dae 100644 --- a/src/thorin/be/spirv/spirv_datatypes.cpp +++ b/src/thorin/be/spirv/spirv_datatypes.cpp @@ -50,7 +50,22 @@ SpvId PtrDatatype::emit_deserialization(BasicBlockBuilder& bb, spv::StorageClass } void PtrDatatype::emit_serialization(BasicBlockBuilder& bb, spv::StorageClass storage_class, SpvId array, SpvId base_offset, SpvId data) { - assert(false && "TODO"); + assert(type->src_type->as()->addr_space() == AddrSpace::Global && "Only buffer device address (global memory) pointers supported"); + serialization_types; + SpvId u64_tid = type->code_gen->convert(type->code_gen->world().type_pu64())->type_id; + + auto u64_ptr = bb.convert(spv::OpConvertPtrToU, u64_tid, data); + + auto cell0 = bb.access_chain(arr_cell_tid, array, { base_offset }); + auto cell1 = bb.access_chain(arr_cell_tid, array, { bb.binop(spv::OpIAdd, u32_tid, base_offset, bb.file_builder.constant(u32_tid, { (uint32_t) 1 })) }); + + SpvId c32 = bb.file_builder.constant(u32_tid, { 32 }); + + auto lower = bb.convert(spv::OpUConvert, u64_tid, u64_ptr); + auto upper = bb.convert(spv::OpUConvert, u64_tid, bb.binop(spv::OpShiftRightLogical, u64_tid, u64_ptr, c32)); + + bb.store(lower, cell0); + bb.store(upper, cell1); } DefiniteArrayDatatype::DefiniteArrayDatatype(ConvertedType* type, ConvertedType* element_type, size_t length) : Datatype(type), element_type(element_type), length(length) { @@ -59,7 +74,7 @@ DefiniteArrayDatatype::DefiniteArrayDatatype(ConvertedType* type, ConvertedType* } SpvId DefiniteArrayDatatype::emit_deserialization(BasicBlockBuilder& bb, spv::StorageClass storage_class, SpvId array, SpvId base_offset) { - serialization_types; + SpvId u32_tid = type->code_gen->convert(type->code_gen->world().type_pu32())->type_id; std::vector indices; std::vector elements; SpvId offset = base_offset; @@ -72,7 +87,7 @@ SpvId DefiniteArrayDatatype::emit_deserialization(BasicBlockBuilder& bb, spv::St return bb.composite(type->type_id, elements); } void DefiniteArrayDatatype::emit_serialization(BasicBlockBuilder& bb, spv::StorageClass storage_class, SpvId array, SpvId base_offset, SpvId data) { - serialization_types; + SpvId u32_tid = type->code_gen->convert(type->code_gen->world().type_pu32())->type_id; std::vector indices; SpvId offset = base_offset; SpvId stride = bb.file_builder.constant(u32_tid, { (uint32_t) element_type->datatype->serialized_size() }); @@ -92,7 +107,7 @@ ProductDatatype::ProductDatatype(ConvertedType* type, const std::vector 0 && "It doesn't make sense to de-serialize Unit!"); - serialization_types; + SpvId u32_tid = type->code_gen->convert(type->code_gen->world().type_pu32())->type_id; std::vector indices; std::vector elements; SpvId offset = base_offset; @@ -105,7 +120,7 @@ SpvId ProductDatatype::emit_deserialization(BasicBlockBuilder& bb, spv::StorageC } void ProductDatatype::emit_serialization(BasicBlockBuilder& bb, spv::StorageClass storage_class, SpvId array, SpvId base_offset, SpvId data) { assert(total_size > 0 && "It doesn't make sense to serialize Unit!"); - serialization_types; + SpvId u32_tid = type->code_gen->convert(type->code_gen->world().type_pu32())->type_id; std::vector indices; SpvId offset = base_offset; int i = 0; From f34532a845d40f2fa79ae5cd087935862e2c733f Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Thu, 6 May 2021 13:42:52 +0200 Subject: [PATCH 089/342] add helper u32_t and u32_constant methods --- src/thorin/be/spirv/spirv.cpp | 46 +++++++++++++++---------- src/thorin/be/spirv/spirv.h | 29 +++++++++++++--- src/thorin/be/spirv/spirv_datatypes.cpp | 22 +++++------- 3 files changed, 61 insertions(+), 36 deletions(-) diff --git a/src/thorin/be/spirv/spirv.cpp b/src/thorin/be/spirv/spirv.cpp index 23149d150..9fda1080c 100644 --- a/src/thorin/be/spirv/spirv.cpp +++ b/src/thorin/be/spirv/spirv.cpp @@ -80,9 +80,12 @@ switch (bitwidth) { \ #undef GET_PRIMTYPE_WITH_KIND_F } -CodeGen::CodeGen(thorin::World& world, Cont2Config& kernel_config, bool debug) - : thorin::CodeGen(world, debug), kernel_config_(kernel_config) -{} +BasicBlockBuilder::BasicBlockBuilder(FnBuilder& fn_builder) + : builder::SpvBasicBlockBuilder(fn_builder.file_builder), fn_builder(fn_builder), file_builder(fn_builder.file_builder) { + label = file_builder.generate_fresh_id(); +} + +FnBuilder::FnBuilder(CodeGen* cg, FileBuilder& file_builder) : builder::SpvFnBuilder(&file_builder), cg(cg), file_builder(file_builder) {} FileBuilder::FileBuilder(CodeGen* cg) : builder::SpvFileBuilder(), cg(cg) { capability(spv::Capability::CapabilityShader); @@ -95,6 +98,16 @@ FileBuilder::FileBuilder(CodeGen* cg) : builder::SpvFileBuilder(), cg(cg) { memory_model = spv::MemoryModel::MemoryModelGLSL450; } +SpvId FileBuilder::u32_t() { + if (u32_t_.id == 0) + u32_t_ = cg->convert(cg->world().type_pu32())->type_id; + return u32_t_; +} + +SpvId FileBuilder::u32_constant(uint32_t pattern) { + return constant(u32_t(), { pattern }); +} + Builtins::Builtins(FileBuilder& builder) { auto& world = builder.cg->world(); auto spv_uvec3_t = builder.cg->convert(world.type_pu32(3)); @@ -132,6 +145,10 @@ ImportedInstructions::ImportedInstructions(FileBuilder& builder) { shader_printf = builder.extended_import("NonSemantic.DebugPrintf"); } +CodeGen::CodeGen(thorin::World& world, Cont2Config& kernel_config, bool debug) + : thorin::CodeGen(world, debug), kernel_config_(kernel_config) +{} + void CodeGen::emit_stream(std::ostream& out) { builder_ = std::make_unique(this); @@ -163,7 +180,7 @@ void CodeGen::emit_stream(std::ostream& out) { SpvId callee = defs_[cont]; - FnBuilder fn_builder(this, builder_.get()); + FnBuilder fn_builder(this, *builder_.get()); fn_builder.fn_type = entry_pt_signature; fn_builder.fn_ret_type = builder_->void_type; @@ -172,7 +189,7 @@ void CodeGen::emit_stream(std::ostream& out) { // iterate on cont type and extract the arguments auto ptr_type = convert(world().ptr_type(world().definite_array_type(world().type_pu32(), 128), 1, 4, AddrSpace::Push))->type_id; - auto zero = bb->file_builder.constant(convert(world().type_pu32())->type_id, { 0 }); + auto zero = bb->file_builder.u32_constant(0); auto arr_ref = bb->access_chain(ptr_type, push_constant_struct_ptr, { zero }); uint32_t offset = 0; std::vector args; @@ -183,7 +200,7 @@ void CodeGen::emit_stream(std::ostream& out) { assert(param_type->order() == 0); auto converted = convert(param_type); assert(converted->datatype != nullptr); - SpvId arg = converted->datatype->emit_deserialization(*bb, spv::StorageClassPushConstant, arr_ref, bb->file_builder.constant(convert(world().type_pu32())->type_id, { offset })); + SpvId arg = converted->datatype->emit_deserialization(*bb, spv::StorageClassPushConstant, arr_ref, bb->file_builder.u32_constant(offset)); args.push_back(arg); offset += converted->datatype->serialized_size(); } @@ -214,7 +231,7 @@ void CodeGen::emit(const thorin::Scope& scope) { entry_ = scope.entry(); assert(entry_->is_returning()); - FnBuilder fn(this, builder_.get()); + FnBuilder fn(this, *builder_.get()); fn.scope = &scope; fn.fn_type = convert(entry_->type())->type_id; fn.fn_ret_type = get_codom_type(entry_); @@ -659,23 +676,20 @@ SpvId CodeGen::emit(const Def* def, BasicBlockBuilder* bb) { } else if (auto variant = def->isa()) { auto variant_type = def->type()->as(); auto variant_datatype = (ProductDatatype*) convert(variant_type)->datatype.get(); + auto tag = builder_->u32_constant(variant->index()); if (variant_datatype->elements_types.size() > 1) { auto alloc_type = convert(world().ptr_type(variant_datatype->elements_types[1]->src_type, 1, 4, AddrSpace::Function))->type_id; auto payload_arr = current_fn_->variable(alloc_type, spv::StorageClassFunction); auto converted_payload_type = convert(variant_type->op(variant->index())); - auto zero = bb->file_builder.constant(convert(world().type_pu32())->type_id, { 0 }); - - converted_payload_type->datatype->emit_serialization(*bb, spv::StorageClassFunction, payload_arr, zero, emit(variant->value(), bb)); + converted_payload_type->datatype->emit_serialization(*bb, spv::StorageClassFunction, payload_arr, bb->file_builder.u32_constant(0), emit(variant->value(), bb)); auto payload = bb->load(variant_datatype->elements_types[1]->type_id, payload_arr); - auto tag = builder_->constant(convert(world().type_pu32())->type_id, {static_cast(variant->index())}); std::vector with_tag = {tag, payload}; return bb->composite(convert(variant->type())->type_id, with_tag); } else { // Zero-sized payload case - auto tag = builder_->constant(convert(world().type_pu32())->type_id, {static_cast(variant->index())}); std::vector with_tag = { tag }; return bb->composite(convert(variant->type())->type_id, with_tag); } @@ -691,8 +705,7 @@ SpvId CodeGen::emit(const Def* def, BasicBlockBuilder* bb) { auto payload = bb->extract(variant_datatype->elements_types[1]->type_id, emit(vextract->value(), bb), {1}); bb->store(payload, payload_arr); - auto zero = bb->file_builder.constant(convert(world().type_pu32())->type_id, { 0 }); - return target_type->datatype->emit_deserialization(*bb, spv::StorageClassFunction, payload_arr, zero); + return target_type->datatype->emit_deserialization(*bb, spv::StorageClassFunction, payload_arr, bb->file_builder.u32_constant(0)); } else if (auto vindex = def->isa()) { auto value = emit(vindex->op(0), bb); return bb->extract(convert(world().type_pu32())->type_id, value, { 0 }); @@ -928,9 +941,4 @@ std::vector CodeGen::emit_builtin(const Continuation* source_cont, const return productions; } -BasicBlockBuilder::BasicBlockBuilder(FnBuilder& fn_builder) -: builder::SpvBasicBlockBuilder(*fn_builder.file_builder), fn_builder(fn_builder) { - label = file_builder.generate_fresh_id(); -} - } diff --git a/src/thorin/be/spirv/spirv.h b/src/thorin/be/spirv/spirv.h index 91f5b6533..938c41193 100644 --- a/src/thorin/be/spirv/spirv.h +++ b/src/thorin/be/spirv/spirv.h @@ -16,32 +16,39 @@ struct FileBuilder; struct FnBuilder; struct ConvertedType { + ConvertedType(CodeGen* cg) : code_gen(cg) {} + ConvertedType(const ConvertedType&) = delete; + spirv::CodeGen* code_gen; const thorin::Type* src_type; SpvId type_id { 0 }; std::unique_ptr datatype; - ConvertedType(CodeGen* cg) : code_gen(cg) {} bool is_known_size() { return datatype != nullptr; } }; struct BasicBlockBuilder : public builder::SpvBasicBlockBuilder { explicit BasicBlockBuilder(FnBuilder& fn_builder); + BasicBlockBuilder(const BasicBlockBuilder&) = delete; FnBuilder& fn_builder; + FileBuilder& file_builder; std::unordered_map phis_map; DefMap args; }; struct FnBuilder : public builder::SpvFnBuilder { + explicit FnBuilder(CodeGen* cg, FileBuilder& file_builder); + FnBuilder(const FnBuilder&) = delete; + CodeGen* cg; + FileBuilder& file_builder; + const Scope* scope = nullptr; std::vector> bbs; std::unordered_map bbs_map; ContinuationMap labels; DefMap params; - - explicit FnBuilder(CodeGen* cg, builder::SpvFileBuilder* file_builder) : builder::SpvFnBuilder(file_builder), cg(cg) {} }; struct Builtins { @@ -62,12 +69,26 @@ struct ImportedInstructions { }; struct FileBuilder : public builder::SpvFileBuilder { + explicit FileBuilder(CodeGen* cg); + FileBuilder(const FileBuilder&) = delete; + CodeGen* cg; std::unique_ptr builtins; std::unique_ptr imported_instrs; - explicit FileBuilder(CodeGen* cg); + SpvId u32_t(); + SpvId u32_constant(uint32_t); + +private: + SpvId u32_t_ { 0 }; + /*SpvId i32_t; + SpvId u32_t; + SpvId i64_t; + SpvId u64_t; + SpvId i32_constant(int32_t); + SpvId i64_constant(int64_t); + SpvId u64_constant(uint64_t);*/ }; class CodeGen : public thorin::CodeGen { diff --git a/src/thorin/be/spirv/spirv_datatypes.cpp b/src/thorin/be/spirv/spirv_datatypes.cpp index 5260b0dae..af4479d9d 100644 --- a/src/thorin/be/spirv/spirv_datatypes.cpp +++ b/src/thorin/be/spirv/spirv_datatypes.cpp @@ -38,13 +38,12 @@ SpvId PtrDatatype::emit_deserialization(BasicBlockBuilder& bb, spv::StorageClass SpvId u64_tid = type->code_gen->convert(type->code_gen->world().type_pu64())->type_id; auto cell0 = bb.access_chain(arr_cell_tid, array, { base_offset }); - auto cell1 = bb.access_chain(arr_cell_tid, array, { bb.binop(spv::OpIAdd, u32_tid, base_offset, bb.file_builder.constant(u32_tid, { (uint32_t) 1 })) }); + auto cell1 = bb.access_chain(arr_cell_tid, array, { bb.binop(spv::OpIAdd, u32_tid, base_offset, bb.file_builder.u32_constant(1)) }); auto lower = bb.convert(spv::OpUConvert, u64_tid, bb.load(u32_tid, cell0)); auto upper = bb.convert(spv::OpUConvert, u64_tid, bb.load(u32_tid, cell1)); - SpvId c32 = bb.file_builder.constant(u32_tid, { 32 }); - auto merged = bb.binop(spv::OpBitwiseOr, u64_tid, lower, bb.binop(spv::OpShiftLeftLogical, u64_tid, upper, c32)); + auto merged = bb.binop(spv::OpBitwiseOr, u64_tid, lower, bb.binop(spv::OpShiftLeftLogical, u64_tid, upper, bb.file_builder.u32_constant(32))); return bb.convert(spv::OpConvertUToPtr, type->type_id, merged); } @@ -57,12 +56,10 @@ void PtrDatatype::emit_serialization(BasicBlockBuilder& bb, spv::StorageClass st auto u64_ptr = bb.convert(spv::OpConvertPtrToU, u64_tid, data); auto cell0 = bb.access_chain(arr_cell_tid, array, { base_offset }); - auto cell1 = bb.access_chain(arr_cell_tid, array, { bb.binop(spv::OpIAdd, u32_tid, base_offset, bb.file_builder.constant(u32_tid, { (uint32_t) 1 })) }); - - SpvId c32 = bb.file_builder.constant(u32_tid, { 32 }); + auto cell1 = bb.access_chain(arr_cell_tid, array, { bb.binop(spv::OpIAdd, u32_tid, base_offset, bb.file_builder.u32_constant(1)) }); auto lower = bb.convert(spv::OpUConvert, u64_tid, u64_ptr); - auto upper = bb.convert(spv::OpUConvert, u64_tid, bb.binop(spv::OpShiftRightLogical, u64_tid, u64_ptr, c32)); + auto upper = bb.convert(spv::OpUConvert, u64_tid, bb.binop(spv::OpShiftRightLogical, u64_tid, u64_ptr, bb.file_builder.u32_constant(32))); bb.store(lower, cell0); bb.store(upper, cell1); @@ -78,7 +75,7 @@ SpvId DefiniteArrayDatatype::emit_deserialization(BasicBlockBuilder& bb, spv::St std::vector indices; std::vector elements; SpvId offset = base_offset; - SpvId stride = bb.file_builder.constant(u32_tid, { (uint32_t) element_type->datatype->serialized_size() }); + SpvId stride = bb.file_builder.u32_constant(element_type->datatype->serialized_size()); for (size_t i = 0; i < length; i++) { SpvId element = element_type->datatype->emit_deserialization(bb, storage_class, array, offset); elements.push_back(element); @@ -90,7 +87,7 @@ void DefiniteArrayDatatype::emit_serialization(BasicBlockBuilder& bb, spv::Stora SpvId u32_tid = type->code_gen->convert(type->code_gen->world().type_pu32())->type_id; std::vector indices; SpvId offset = base_offset; - SpvId stride = bb.file_builder.constant(u32_tid, { (uint32_t) element_type->datatype->serialized_size() }); + SpvId stride = bb.file_builder.u32_constant(element_type->datatype->serialized_size()); for (size_t i = 0; i < length; i++) { element_type->datatype->emit_serialization(bb, storage_class, array, offset, bb.extract(element_type->type_id, data, { (uint32_t) i })); offset = bb.binop(spv::OpIAdd, u32_tid, offset, stride); @@ -113,7 +110,7 @@ SpvId ProductDatatype::emit_deserialization(BasicBlockBuilder& bb, spv::StorageC SpvId offset = base_offset; for (auto& element_type : elements_types) { SpvId element = element_type->datatype->emit_deserialization(bb, storage_class, array, offset); - offset = bb.binop(spv::OpIAdd, u32_tid, offset, bb.file_builder.constant(u32_tid, { (uint32_t) element_type->datatype->serialized_size() })); + offset = bb.binop(spv::OpIAdd, u32_tid, offset, bb.file_builder.u32_constant(element_type->datatype->serialized_size())); elements.push_back(element); } return bb.composite(type->type_id, elements); @@ -126,7 +123,7 @@ void ProductDatatype::emit_serialization(BasicBlockBuilder& bb, spv::StorageClas int i = 0; for (auto& element_type : elements_types) { element_type->datatype->emit_serialization(bb, storage_class, array, offset, bb.extract(element_type->type_id, data, { (uint32_t) i++ })); - offset = bb.binop(spv::OpIAdd, u32_tid, offset, bb.file_builder.constant(u32_tid, { (uint32_t) element_type->datatype->serialized_size() })); + offset = bb.binop(spv::OpIAdd, u32_tid, offset, bb.file_builder.u32_constant(element_type->datatype->serialized_size())); } } @@ -239,8 +236,7 @@ ConvertedType* CodeGen::convert(const Type* type) { case Node_DefiniteArrayType: { auto array = type->as(); ConvertedType* element = convert(array->elem_type()); - SpvId size = builder_->constant(convert(world().type_pu32())->type_id, {(uint32_t) array->dim() }); - converted->type_id = builder_->declare_array_type(element->type_id, size); + converted->type_id = builder_->declare_array_type(element->type_id, builder_->u32_constant(array->dim())); converted->datatype = std::make_unique(converted, element, array->dim()); break; } From d319a06aa367c75dc965db625a931ee05ff88a34 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Thu, 6 May 2021 13:53:01 +0200 Subject: [PATCH 090/342] minor cleanup --- src/thorin/be/spirv/spirv.cpp | 17 ++++++++--------- src/thorin/be/spirv/spirv_datatypes.cpp | 5 +---- 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/src/thorin/be/spirv/spirv.cpp b/src/thorin/be/spirv/spirv.cpp index 9fda1080c..612c99744 100644 --- a/src/thorin/be/spirv/spirv.cpp +++ b/src/thorin/be/spirv/spirv.cpp @@ -394,7 +394,7 @@ void CodeGen::emit_epilogue(Continuation* continuation, BasicBlockBuilder* bb) { THORIN_UNREACHABLE; } else if (continuation->callee()->isa()) { bb->unreachable(); - } else if (continuation->intrinsic() == Intrinsic::SCFLoopHeader) { + } else if (continuation->intrinsic() == Intrinsic::SCFLoopHeader) { auto merge_label = current_fn_->bbs_map[const_cast(continuation->attributes_.scf_metadata.loop_header.merge_target)]->label; auto continue_label = current_fn_->bbs_map[const_cast(continuation->attributes_.scf_metadata.loop_header.continue_target)]->label; bb->loop_merge(merge_label, continue_label, spv::LoopControlMaskNone, {}); @@ -409,21 +409,22 @@ void CodeGen::emit_epilogue(Continuation* continuation, BasicBlockBuilder* bb) { int targets = continuation->num_ops(); assert(targets > 0); + // TODO handle dispatching to multiple targets assert(targets == 1); - auto callee = continuation->op(0)->as_continuation(); + auto dispatch_target = continuation->op(0)->as_continuation(); // Extract the relevant variant & expand the tuple if necessary auto arg = world().variant_extract(continuation->param(0), 0); auto extracted = emit(arg, dispatch_bb); - if (callee->param(0)->type()->equal(arg->type())) { - auto* param = callee->param(0); - auto& phi = current_fn_->bbs_map[callee]->phis_map[param]; + if (dispatch_target->param(0)->type()->equal(arg->type())) { + auto* param = dispatch_target->param(0); + auto& phi = current_fn_->bbs_map[dispatch_target]->phis_map[param]; phi.preds.emplace_back(extracted, dispatch_bb->label); } else { assert(false && "TODO destructure argument"); } - dispatch_bb->branch(current_fn_->bbs_map[callee]->label); + dispatch_bb->branch(current_fn_->bbs_map[dispatch_target]->label); } else if (continuation->intrinsic() == Intrinsic::SCFLoopContinue) { auto loop_header = continuation->op(0)->as_continuation(); @@ -437,11 +438,11 @@ void CodeGen::emit_epilogue(Continuation* continuation, BasicBlockBuilder* bb) { bb->branch(header_label); } else if (continuation->intrinsic() == Intrinsic::SCFLoopMerge) { - // auto header_cont = continuation->op(0)->as_continuation(); int targets = continuation->num_ops(); assert(targets > 0); + // TODO handle dispatching to multiple targets assert(targets == 1); auto callee = continuation->op(0)->as_continuation(); // TODO phis @@ -456,8 +457,6 @@ void CodeGen::emit_epilogue(Continuation* continuation, BasicBlockBuilder* bb) { jump_to_next_cont_with_args(succ, productions); } else if (auto intrinsic = continuation->callee()->isa_continuation(); callee && callee->is_intrinsic()) { THORIN_UNREACHABLE; - //auto ret_continuation = emit_intrinsic(irbuilder, continuation); - //irbuilder.CreateBr(cont2bb(ret_continuation)); } else { // function/closure call // put all first-order args into an array std::vector call_args; diff --git a/src/thorin/be/spirv/spirv_datatypes.cpp b/src/thorin/be/spirv/spirv_datatypes.cpp index af4479d9d..f807415ae 100644 --- a/src/thorin/be/spirv/spirv_datatypes.cpp +++ b/src/thorin/be/spirv/spirv_datatypes.cpp @@ -4,10 +4,7 @@ namespace thorin::spirv { ScalarDatatype::ScalarDatatype(ConvertedType* type, int type_tag, size_t size_in_bytes, size_t alignment_in_bytes) -: Datatype(type), type_tag(type_tag), size_in_bytes(size_in_bytes), alignment(alignment_in_bytes) -{ - -} +: Datatype(type), type_tag(type_tag), size_in_bytes(size_in_bytes), alignment(alignment_in_bytes) {} /// All serialization/deserialization methods use this so into a macro it goes #define serialization_types \ From f09de92de067c88fe8cd4b4c9a28407e5658abf5 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Thu, 6 May 2021 14:14:15 +0200 Subject: [PATCH 091/342] implement most invocation id intrinsics --- src/thorin/be/spirv/spirv.cpp | 58 ++++++++++++++++++++++----- src/thorin/be/spirv/spirv_builder.hpp | 18 ++++++++- 2 files changed, 65 insertions(+), 11 deletions(-) diff --git a/src/thorin/be/spirv/spirv.cpp b/src/thorin/be/spirv/spirv.cpp index 612c99744..928d33812 100644 --- a/src/thorin/be/spirv/spirv.cpp +++ b/src/thorin/be/spirv/spirv.cpp @@ -113,11 +113,13 @@ Builtins::Builtins(FileBuilder& builder) { auto spv_uvec3_t = builder.cg->convert(world.type_pu32(3)); auto spv_uint_t = builder.cg->convert(world.type_pu32()); auto spv_uvec3_pt = builder.declare_ptr_type(spv::StorageClassInput, spv_uvec3_t->type_id); + auto spv_uvec3_ptp = builder.declare_ptr_type(spv::StorageClassPrivate, spv_uvec3_t->type_id); auto spv_uint_pt = builder.declare_ptr_type(spv::StorageClassInput, spv_uint_t->type_id); - // workgroup_size = builder.constant(spv_uvec3_pt, spv::StorageClassInput); - // builder.decorate(workgroup_size, spv::DecorationBuiltIn, { spv::BuiltInWorkgroupSize }); - // builder.name(workgroup_size, "BuiltInWorkgroupSize"); + // Because we technically can have multiple entry points, we take the easy way out and make each entry point + // write to a private variable the actual workgroup size for that specific kernel. Dirty, but simple. + workgroup_size = builder.variable(spv_uvec3_ptp, spv::StorageClassPrivate); + builder.name(workgroup_size, "BuiltInWorkgroupSize"); num_workgroups = builder.variable(spv_uvec3_pt, spv::StorageClassInput); builder.decorate(num_workgroups, spv::DecorationBuiltIn, { spv::BuiltInNumWorkgroups }); @@ -187,6 +189,21 @@ void CodeGen::emit_stream(std::ostream& out) { BasicBlockBuilder* bb = fn_builder.bbs.emplace_back(std::make_unique(fn_builder)).get(); fn_builder.bbs_to_emit.push_back(bb); + auto block = config->second->as()->block_size(); + std::vector local_size = { + (uint32_t) std::get<0>(block), + (uint32_t) std::get<1>(block), + (uint32_t) std::get<2>(block), + }; + + auto spv_uvec3_t = convert(world().type_pu32(3)); + SpvId wg_size_constant = builder_->constant_composite(spv_uvec3_t->type_id, { + builder_->u32_constant(local_size[0]), + builder_->u32_constant(local_size[1]), + builder_->u32_constant(local_size[2]), + }); + bb->store(wg_size_constant, builder_->builtins->workgroup_size); + // iterate on cont type and extract the arguments auto ptr_type = convert(world().ptr_type(world().definite_array_type(world().type_pu32(), 128), 1, 4, AddrSpace::Push))->type_id; auto zero = bb->file_builder.u32_constant(0); @@ -211,14 +228,17 @@ void CodeGen::emit_stream(std::ostream& out) { builder_->define_function(fn_builder); builder_->name(fn_builder.function_id, "entry_point_" + cont->name()); - builder_->declare_entry_point(spv::ExecutionModelGLCompute, fn_builder.function_id, "kernel_main", { push_constant_struct_ptr, builder_->builtins->local_id }); - - auto block = config->second->as()->block_size(); - std::vector local_size = { - (uint32_t) std::get<0>(block), - (uint32_t) std::get<1>(block), - (uint32_t) std::get<2>(block), + std::vector interface = { + push_constant_struct_ptr, + builder_->builtins->workgroup_size, + builder_->builtins->num_workgroups, + builder_->builtins->workgroup_id, + builder_->builtins->local_id, + builder_->builtins->global_id, + builder_->builtins->local_invocation_index, }; + builder_->declare_entry_point(spv::ExecutionModelGLCompute, fn_builder.function_id, "kernel_main", interface); + builder_->execution_mode(fn_builder.function_id, spv::ExecutionModeLocalSize, local_size); } } @@ -930,10 +950,28 @@ std::vector CodeGen::emit_builtin(const Continuation* source_cont, const } bb->ext_instruction(bb->file_builder.void_type, builder_->imported_instrs->shader_printf, 1, args); + } else if (builtin->name() == "get_work_dim") { + THORIN_UNREACHABLE; + } else if (builtin->name() == "get_global_id") { + auto vector = bb->load(uvec3_t->type_id, builder_->builtins->global_id); + auto extracted = bb->vector_extract_dynamic(u32_t->type_id, vector, emit(source_cont->arg(1), bb)); + productions.push_back(bb->convert(spv::OpBitcast, i32_t->type_id, extracted)); + } else if (builtin->name() == "get_local_size") { + auto vector = bb->load(uvec3_t->type_id, builder_->builtins->workgroup_size); + auto extracted = bb->vector_extract_dynamic(u32_t->type_id, vector, emit(source_cont->arg(1), bb)); + productions.push_back(bb->convert(spv::OpBitcast, i32_t->type_id, extracted)); } else if (builtin->name() == "get_local_id") { auto vector = bb->load(uvec3_t->type_id, builder_->builtins->local_id); auto extracted = bb->vector_extract_dynamic(u32_t->type_id, vector, emit(source_cont->arg(1), bb)); productions.push_back(bb->convert(spv::OpBitcast, i32_t->type_id, extracted)); + } else if (builtin->name() == "get_num_groups") { + auto vector = bb->load(uvec3_t->type_id, builder_->builtins->num_workgroups); + auto extracted = bb->vector_extract_dynamic(u32_t->type_id, vector, emit(source_cont->arg(1), bb)); + productions.push_back(bb->convert(spv::OpBitcast, i32_t->type_id, extracted)); + } else if (builtin->name() == "get_group_id") { + auto vector = bb->load(uvec3_t->type_id, builder_->builtins->workgroup_id); + auto extracted = bb->vector_extract_dynamic(u32_t->type_id, vector, emit(source_cont->arg(1), bb)); + productions.push_back(bb->convert(spv::OpBitcast, i32_t->type_id, extracted)); } else { world().ELOG("This spir-v builtin isn't recognised: %s", builtin->name()); } diff --git a/src/thorin/be/spirv/spirv_builder.hpp b/src/thorin/be/spirv/spirv_builder.hpp index 44a2f287f..7c1840cf9 100644 --- a/src/thorin/be/spirv/spirv_builder.hpp +++ b/src/thorin/be/spirv/spirv_builder.hpp @@ -318,6 +318,7 @@ struct SpvFileBuilder { PTR_TYPE, DEF_ARR_TYPE, CONSTANT, + CONSTANT_COMPOSITE, }; /// Prevents duplicate declarations @@ -477,7 +478,22 @@ struct SpvFileBuilder { types_constants.ref_id(type); types_constants.ref_id(id); for (auto arg : bit_pattern) - types_constants.data_.push_back(arg); + types_constants.literal_int(arg); + unique_decls[key] = id; + return id; + } + + SpvId constant_composite(SpvId type, std::vector ops) { + auto key = UniqueDeclKey { CONSTANT_COMPOSITE, {} }; + key.members.push_back(type.id); + for (auto op : ops) key.members.push_back(op.id); + if (auto iter = unique_decls.find(key); iter != unique_decls.end()) return iter->second; + types_constants.op(spv::Op::OpConstantComposite, 3 + ops.size()); + auto id = generate_fresh_id(); + types_constants.ref_id(type); + types_constants.ref_id(id); + for (auto op : ops) + types_constants.ref_id(op); unique_decls[key] = id; return id; } From cd483a1d08a4beb7b2c0aacb00d039b678729b93 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Tue, 11 May 2021 10:45:55 +0200 Subject: [PATCH 092/342] fix incorrect stride --- src/thorin/be/spirv/spirv.h | 4 ++++ src/thorin/be/spirv/spirv_datatypes.cpp | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/thorin/be/spirv/spirv.h b/src/thorin/be/spirv/spirv.h index 938c41193..7d31df5de 100644 --- a/src/thorin/be/spirv/spirv.h +++ b/src/thorin/be/spirv/spirv.h @@ -129,6 +129,10 @@ struct Datatype { ConvertedType* type; Datatype(ConvertedType* type) : type(type) {} + // Datatypes are serialized using a base element, for now it is hardcoded to use 32-bit scalar unsigned integers + static constexpr size_t base_element_bitwidth = 32; + static constexpr size_t base_element_bytes = base_element_bitwidth / 8; + virtual size_t serialized_size() = 0; virtual SpvId emit_deserialization(BasicBlockBuilder& bb, spv::StorageClass storage_class, SpvId array, SpvId base_offset) = 0; virtual void emit_serialization(BasicBlockBuilder& bb, spv::StorageClass storage_class, SpvId array, SpvId base_offset, SpvId data) = 0; diff --git a/src/thorin/be/spirv/spirv_datatypes.cpp b/src/thorin/be/spirv/spirv_datatypes.cpp index f807415ae..7c11a56c4 100644 --- a/src/thorin/be/spirv/spirv_datatypes.cpp +++ b/src/thorin/be/spirv/spirv_datatypes.cpp @@ -220,7 +220,7 @@ ConvertedType* CodeGen::convert(const Type* type) { if (ptr->addr_space() == AddrSpace::Global) { assert(element->datatype && "Can only have physical pointers to known-size types"); - builder_->decorate(converted->type_id, spv::DecorationArrayStride, {(uint32_t) element->datatype->serialized_size()}); + builder_->decorate(converted->type_id, spv::DecorationArrayStride, {(uint32_t) (element->datatype->serialized_size() * Datatype::base_element_bytes)}); } } ptr_done: From b37998f256cd38b20ec329e109aae6597f2959db Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Tue, 5 Oct 2021 15:34:13 +0200 Subject: [PATCH 093/342] enable spirv backend iff headers are found --- CMakeLists.txt | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e59c13a86..9ad593a62 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,7 +10,6 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON) option(BUILD_SHARED_LIBS "Build shared libraries" ON) option(THORIN_PROFILE "profile complexity in thorin::HashTable - only works in Debug build" ON) -option(SPIRV_ENABLED "Enable spir-v backend" ON) if(CMAKE_BUILD_TYPE STREQUAL "") set(CMAKE_BUILD_TYPE Debug CACHE STRING "Debug or Release" FORCE) @@ -41,9 +40,10 @@ else() message(STATUS "Building without LLVM and RV. Specify LLVM_DIR to compile with LLVM.") endif() -if (SPIRV_ENABLED) - find_package(SPIRV-Headers REQUIRED) - message(STATUS "Enabled SPIR-V backend") +find_package(SPIRV-Headers QUIET CONFIG) +if (SPIRV-Headers_FOUND) + message(STATUS "Found SPIRV-Headers at ${SPIRV-Headers_DIR}") + set(THORIN_ENABLE_SPIRV TRUE) endif() message(STATUS "Using Debug flags: ${CMAKE_CXX_FLAGS_DEBUG}") @@ -65,9 +65,6 @@ endif() if(RV_FOUND) set(THORIN_ENABLE_RV TRUE) endif() -if(LLVM_FOUND) - set(THORIN_ENABLE_SPIRV TRUE) -endif() configure_file(src/thorin/config.h.in ${CMAKE_BINARY_DIR}/include/thorin/config.h @ONLY) include_directories(${CMAKE_BINARY_DIR}/include) From 3d19b722dd571c571b938e4876498568c3232716 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Tue, 19 Apr 2022 11:42:32 +0200 Subject: [PATCH 094/342] renamed spirv_transform to structurize --- src/thorin/CMakeLists.txt | 2 + src/thorin/be/shady/shady.cpp | 0 src/thorin/be/shady/shady.h | 0 src/thorin/be/spirv/spirv_builder.hpp | 669 ------------------ .../structurize.cpp} | 27 +- src/thorin/transform/structurize.h | 8 + 6 files changed, 23 insertions(+), 683 deletions(-) create mode 100644 src/thorin/be/shady/shady.cpp create mode 100644 src/thorin/be/shady/shady.h delete mode 100644 src/thorin/be/spirv/spirv_builder.hpp rename src/thorin/{be/spirv/spirv_transform.cpp => transform/structurize.cpp} (96%) create mode 100644 src/thorin/transform/structurize.h diff --git a/src/thorin/CMakeLists.txt b/src/thorin/CMakeLists.txt index 5b4704492..5791dd247 100644 --- a/src/thorin/CMakeLists.txt +++ b/src/thorin/CMakeLists.txt @@ -70,6 +70,8 @@ set(THORIN_SOURCES transform/partial_evaluation.h transform/split_slots.cpp transform/split_slots.h + transform/structurize.cpp + transform/structurize.h util/array.h util/cast.h util/hash.h diff --git a/src/thorin/be/shady/shady.cpp b/src/thorin/be/shady/shady.cpp new file mode 100644 index 000000000..e69de29bb diff --git a/src/thorin/be/shady/shady.h b/src/thorin/be/shady/shady.h new file mode 100644 index 000000000..e69de29bb diff --git a/src/thorin/be/spirv/spirv_builder.hpp b/src/thorin/be/spirv/spirv_builder.hpp deleted file mode 100644 index 7c1840cf9..000000000 --- a/src/thorin/be/spirv/spirv_builder.hpp +++ /dev/null @@ -1,669 +0,0 @@ -#include - -#include -#include -#include -#include -#include -#include - -namespace thorin::spirv::builder { - -struct SpvId { uint32_t id; }; - -struct SpvSectionBuilder; -struct SpvBasicBlockBuilder; -struct SpvFnBuilder; -struct SpvFileBuilder; - -inline int div_roundup(int a, int b) { - if (a % b == 0) - return a / b; - else - return (a / b) + 1; -} - -struct SpvSectionBuilder { - std::vector data_; - -private: - void output_word(uint32_t word) { - data_.push_back(word); - } -public: - void op(spv::Op op, int ops_size) { - uint32_t lower = op & 0xFFFFu; - uint32_t upper = (ops_size << 16) & 0xFFFF0000u; - output_word(lower | upper); - } - - void ref_id(SpvId id) { - assert(id.id != 0); - output_word(id.id); - } - - void literal_name(std::string_view str) { - int i = 0; - uint32_t cword = 0; - for (char c : str) { - cword = cword | (c & 0xFF) << (i * 8); - i++; - if (i == 4) { - output_word(cword); - cword = 0; - i = 0; - } - } - output_word(cword); - } - - void literal_int(uint32_t i) { - output_word(i); - } -}; - -struct SpvBasicBlockBuilder : public SpvSectionBuilder { - explicit SpvBasicBlockBuilder(SpvFileBuilder& file_builder) - : file_builder(file_builder) - {} - - SpvFileBuilder& file_builder; - - struct Phi { - SpvId type; - SpvId value; - std::vector> preds; - }; - std::vector phis; - SpvId label; - - SpvId undef(SpvId type) { - op(spv::Op::OpUndef, 3); - ref_id(type); - auto id = generate_fresh_id(); - ref_id(id); - return id; - } - - SpvId composite(SpvId aggregate_t, std::vector& elements) { - op(spv::Op::OpCompositeConstruct, 3 + elements.size()); - ref_id(aggregate_t); - auto id = generate_fresh_id(); - ref_id(id); - for (auto e : elements) - ref_id(e); - return id; - } - - SpvId extract(SpvId target_type, SpvId composite, std::vector indices) { - op(spv::Op::OpCompositeExtract, 4 + indices.size()); - ref_id(target_type); - auto id = generate_fresh_id(); - ref_id(id); - ref_id(composite); - for (auto i : indices) - literal_int(i); - return id; - } - - SpvId insert(SpvId target_type, SpvId object, SpvId composite, std::vector indices) { - op(spv::Op::OpCompositeInsert, 5 + indices.size()); - ref_id(target_type); - auto id = generate_fresh_id(); - ref_id(id); - ref_id(object); - ref_id(composite); - for (auto i : indices) - literal_int(i); - return id; - } - - SpvId vector_extract_dynamic(SpvId target_type, SpvId vector, SpvId index) { - op(spv::Op::OpVectorExtractDynamic, 5); - ref_id(target_type); - auto id = generate_fresh_id(); - ref_id(id); - ref_id(vector); - ref_id(index); - return id; - } - - SpvId vector_insert_dynamic(SpvId target_type, SpvId vector, SpvId component, SpvId index) { - op(spv::Op::OpVectorInsertDynamic, 6); - ref_id(target_type); - auto id = generate_fresh_id(); - ref_id(id); - ref_id(vector); - ref_id(component); - ref_id(index); - return id; - } - - // Used for almost all conversion operations - SpvId convert(spv::Op op_, SpvId target_type, SpvId value) { - op(op_, 4); - auto id = generate_fresh_id(); - ref_id(target_type); - ref_id(id); - ref_id(value); - return id; - } - - SpvId access_chain(SpvId target_type, SpvId element, std::vector indexes) { - op(spv::Op::OpAccessChain, 4 + indexes.size()); - auto id = generate_fresh_id(); - ref_id(target_type); - ref_id(id); - ref_id(element); - for (auto index : indexes) - ref_id(index); - return id; - } - - SpvId ptr_access_chain(SpvId target_type, SpvId base, SpvId element, std::vector indexes) { - op(spv::Op::OpPtrAccessChain, 5 + indexes.size()); - auto id = generate_fresh_id(); - ref_id(target_type); - ref_id(id); - ref_id(base); - ref_id(element); - for (auto index : indexes) - ref_id(index); - return id; - } - - SpvId load(SpvId target_type, SpvId pointer, std::vector operands = {}) { - op(spv::Op::OpLoad, 4 + operands.size()); - auto id = generate_fresh_id(); - ref_id(target_type); - ref_id(id); - ref_id(pointer); - for (auto op : operands) - literal_int(op); - return id; - } - - void store(SpvId value, SpvId pointer, std::vector operands = {}) { - op(spv::Op::OpStore, 3 + operands.size()); - ref_id(pointer); - ref_id(value); - for (auto op : operands) - literal_int(op); - } - - SpvId binop(spv::Op op_, SpvId result_type, SpvId lhs, SpvId rhs) { - op(op_, 5); - auto id = generate_fresh_id(); - ref_id(result_type); - ref_id(id); - ref_id(lhs); - ref_id(rhs); - return id; - } - - void branch(SpvId target) { - op(spv::Op::OpBranch, 2); - ref_id(target); - } - - void branch_conditional(SpvId condition, SpvId true_target, SpvId false_target) { - op(spv::Op::OpBranchConditional, 4); - ref_id(condition); - ref_id(true_target); - ref_id(false_target); - } - - void selection_merge(SpvId merge_bb, spv::SelectionControlMask selection_control) { - op(spv::Op::OpSelectionMerge, 3); - ref_id(merge_bb); - literal_int(selection_control); - } - - void loop_merge(SpvId merge_bb, SpvId continue_bb, spv::LoopControlMask loop_control, std::vector loop_control_ops) { - op(spv::Op::OpLoopMerge, 4 + loop_control_ops.size()); - ref_id(merge_bb); - ref_id(continue_bb); - literal_int(loop_control); - - for (auto e : loop_control_ops) - literal_int(e); - } - - SpvId call(SpvId return_type, SpvId callee, std::vector arguments) { - op(spv::Op::OpFunctionCall, 4 + arguments.size()); - auto id = generate_fresh_id(); - ref_id(return_type); - ref_id(id); - ref_id(callee); - - for (auto a : arguments) - ref_id(a); - return id; - } - - SpvId ext_instruction(SpvId return_type, SpvId set, uint32_t instruction, std::vector arguments) { - op(spv::Op::OpExtInst, 5 + arguments.size()); - auto id = generate_fresh_id(); - ref_id(return_type); - ref_id(id); - ref_id(set); - literal_int(instruction); - for (auto a : arguments) - ref_id(a); - return id; - } - - void return_void() { - op(spv::Op::OpReturn, 1); - } - - void return_value(SpvId value) { - op(spv::Op::OpReturnValue, 2); - ref_id(value); - } - - void unreachable() { - op(spv::Op::OpUnreachable, 1); - } - -private: - SpvId generate_fresh_id(); -}; - -struct SpvFnBuilder { - explicit SpvFnBuilder(SpvFileBuilder* file_builder) - : file_builder(file_builder) - { - function_id = generate_fresh_id(); - } - - SpvFileBuilder* file_builder; - SpvId function_id; - - SpvId fn_type; - SpvId fn_ret_type; - std::vector bbs_to_emit; - - // Contains OpFunctionParams - SpvSectionBuilder header; - - SpvSectionBuilder variables; - - SpvId parameter(SpvId param_type) { - header.op(spv::Op::OpFunctionParameter, 3); - auto id = generate_fresh_id(); - header.ref_id(param_type); - header.ref_id(id); - return id; - } - - SpvId variable(SpvId type, spv::StorageClass storage_class) { - variables.op(spv::Op::OpVariable, 4); - variables.ref_id(type); - auto id = generate_fresh_id(); - variables.ref_id(id); - variables.literal_int(storage_class); - return id; - } - -private: - SpvId generate_fresh_id(); -}; - -struct SpvFileBuilder { - - enum UniqueDeclTag { - NONE, - FN_TYPE, - PTR_TYPE, - DEF_ARR_TYPE, - CONSTANT, - CONSTANT_COMPOSITE, - }; - - /// Prevents duplicate declarations - struct UniqueDeclKey { - UniqueDeclTag tag; - std::vector members; - - bool operator==(const UniqueDeclKey &b) const { - return tag == b.tag && members == b.members; - } - }; - - struct UniqueDeclKeyHasher { - size_t operator() (const UniqueDeclKey& key) const { - size_t acc = 0; - for (auto id : key.members) - acc ^= std::hash{}(id); - return std::hash{}(key.tag) ^ acc; - } - }; - - SpvFileBuilder() - : void_type(declare_void_type()) - {} - SpvFileBuilder(const SpvFileBuilder&) = delete; - - SpvId generate_fresh_id() { return { bound++ }; } - - void name(SpvId id, std::string_view str) { - assert(id.id < bound); - debug_names.op(spv::Op::OpName, 2 + div_roundup(str.size() + 1, 4)); - debug_names.ref_id(id); - debug_names.literal_name(str); - } - - SpvId declare_bool_type() { - types_constants.op(spv::Op::OpTypeBool, 2); - auto id = generate_fresh_id(); - types_constants.ref_id(id); - return id; - } - - SpvId declare_int_type(int width, bool signed_) { - types_constants.op(spv::Op::OpTypeInt, 4); - auto id = generate_fresh_id(); - types_constants.ref_id(id); - types_constants.literal_int(width); - types_constants.literal_int(signed_ ? 1 : 0); - return id; - } - - SpvId declare_float_type(int width) { - types_constants.op(spv::Op::OpTypeFloat, 3); - auto id = generate_fresh_id(); - types_constants.ref_id(id); - types_constants.literal_int(width); - return id; - } - - SpvId declare_ptr_type(spv::StorageClass storage_class, SpvId element_type) { - auto key = UniqueDeclKey { PTR_TYPE, { element_type.id, (uint32_t) storage_class } }; - if (auto iter = unique_decls.find(key); iter != unique_decls.end()) return iter->second; - types_constants.op(spv::Op::OpTypePointer, 4); - auto id = generate_fresh_id(); - types_constants.ref_id(id); - types_constants.literal_int(storage_class); - types_constants.ref_id(element_type); - unique_decls[key] = id; - return id; - } - - SpvId declare_array_type(SpvId element_type, SpvId dim) { - auto key = UniqueDeclKey { DEF_ARR_TYPE, { element_type.id, dim.id } }; - if (auto iter = unique_decls.find(key); iter != unique_decls.end()) return iter->second; - types_constants.op(spv::Op::OpTypeArray, 4); - auto id = generate_fresh_id(); - types_constants.ref_id(id); - types_constants.ref_id(element_type); - types_constants.ref_id(dim); - unique_decls[key] = id; - return id; - } - - SpvId declare_fn_type(std::vector dom, SpvId codom) { - auto key = UniqueDeclKey { FN_TYPE, {} }; - for (auto d : dom) key.members.push_back(d.id); - key.members.push_back(codom.id); - if (auto iter = unique_decls.find(key); iter != unique_decls.end()) return iter->second; - - types_constants.op(spv::Op::OpTypeFunction, 3 + dom.size()); - auto id = generate_fresh_id(); - types_constants.ref_id(id); - types_constants.ref_id(codom); - for (auto arg : dom) - types_constants.ref_id(arg); - unique_decls[key] = id; - return id; - } - - SpvId declare_struct_type(std::vector elements) { - types_constants.op(spv::Op::OpTypeStruct, 2 + elements.size()); - auto id = generate_fresh_id(); - types_constants.ref_id(id); - for (auto arg : elements) - types_constants.ref_id(arg); - return id; - } - - SpvId declare_vector_type(SpvId component_type, uint32_t dim) { - types_constants.op(spv::Op::OpTypeVector, 4); - auto id = generate_fresh_id(); - types_constants.ref_id(id); - types_constants.ref_id(component_type); - types_constants.literal_int(dim); - return id; - } - - void decorate(SpvId target, spv::Decoration decoration, std::vector extra = {}) { - annotations.op(spv::Op::OpDecorate, 3 + extra.size()); - annotations.ref_id(target); - annotations.literal_int(decoration); - for (auto e : extra) - annotations.literal_int(e); - } - - void decorate_member(SpvId target, uint32_t member, spv::Decoration decoration, std::vector extra = {}) { - annotations.op(spv::Op::OpMemberDecorate, 4 + extra.size()); - annotations.ref_id(target); - annotations.literal_int(member); - annotations.literal_int(decoration); - for (auto e : extra) - annotations.literal_int(e); - } - - SpvId debug_string(std::string string) { - debug_string_source.op(spv::Op::OpString, 2 + div_roundup(string.size() + 1, 4)); - auto id = generate_fresh_id(); - debug_string_source.ref_id(id); - debug_string_source.literal_name(string); - return id; - } - - SpvId bool_constant(SpvId type, bool value) { - types_constants.op(value ? spv::Op::OpConstantTrue : spv::Op::OpConstantFalse, 3); - auto id = generate_fresh_id(); - types_constants.ref_id(type); - types_constants.ref_id(id); - return id; - } - - SpvId constant(SpvId type, std::vector bit_pattern) { - auto key = UniqueDeclKey { CONSTANT, bit_pattern }; - key.members.push_back(type.id); - if (auto iter = unique_decls.find(key); iter != unique_decls.end()) return iter->second; - types_constants.op(spv::Op::OpConstant, 3 + bit_pattern.size()); - auto id = generate_fresh_id(); - types_constants.ref_id(type); - types_constants.ref_id(id); - for (auto arg : bit_pattern) - types_constants.literal_int(arg); - unique_decls[key] = id; - return id; - } - - SpvId constant_composite(SpvId type, std::vector ops) { - auto key = UniqueDeclKey { CONSTANT_COMPOSITE, {} }; - key.members.push_back(type.id); - for (auto op : ops) key.members.push_back(op.id); - if (auto iter = unique_decls.find(key); iter != unique_decls.end()) return iter->second; - types_constants.op(spv::Op::OpConstantComposite, 3 + ops.size()); - auto id = generate_fresh_id(); - types_constants.ref_id(type); - types_constants.ref_id(id); - for (auto op : ops) - types_constants.ref_id(op); - unique_decls[key] = id; - return id; - } - - SpvId variable(SpvId type, spv::StorageClass storage_class) { - types_constants.op(spv::Op::OpVariable, 4); - types_constants.ref_id(type); - auto id = generate_fresh_id(); - types_constants.ref_id(id); - types_constants.literal_int(storage_class); - return id; - } - - SpvId define_function(SpvFnBuilder& fn_builder) { - fn_defs.op(spv::Op::OpFunction, 5); - fn_defs.ref_id(fn_builder.fn_ret_type); - fn_defs.ref_id(fn_builder.function_id); - fn_defs.data_.push_back(spv::FunctionControlMaskNone); - fn_defs.ref_id(fn_builder.fn_type); - - // Includes stuff like OpFunctionParameters - for (auto w : fn_builder.header.data_) - fn_defs.data_.push_back(w); - - bool first = true; - for (auto& bb : fn_builder.bbs_to_emit) { - fn_defs.op(spv::Op::OpLabel, 2); - fn_defs.ref_id(bb->label); - - if (first) { - for (auto w : fn_builder.variables.data_) - fn_defs.data_.push_back(w); - first = false; - } - - for (auto& phi : bb->phis) { - fn_defs.op(spv::Op::OpPhi, 3 + 2 * phi->preds.size()); - fn_defs.ref_id(phi->type); - fn_defs.ref_id(phi->value); - assert(!phi->preds.empty()); - for (auto& [pred_value, pred_label] : phi->preds) { - fn_defs.ref_id(pred_value); - fn_defs.ref_id(pred_label); - } - } - - for (auto w : bb->data_) - fn_defs.data_.push_back(w); - } - - fn_defs.op(spv::Op::OpFunctionEnd, 1); - return fn_builder.function_id; - } - - void declare_entry_point(spv::ExecutionModel execution_model, SpvId entry_point, std::string name, std::vector interface) { - entry_points.op(spv::Op::OpEntryPoint, 3 + div_roundup(name.size() + 1, 4) + interface.size()); - entry_points.literal_int(execution_model); - entry_points.ref_id(entry_point); - entry_points.literal_name(name); - for (auto i : interface) - entry_points.ref_id(i); - } - - void execution_mode(SpvId entry_point, spv::ExecutionMode execution_mode, std::vector payloads) { - entry_points.op(spv::Op::OpExecutionMode, 3 + payloads.size()); - entry_points.ref_id(entry_point); - entry_points.literal_int(execution_mode); - for (auto d : payloads) - entry_points.literal_int(d); - } - - void capability(spv::Capability cap) { - capabilities.op(spv::Op::OpCapability, 2); - capabilities.data_.push_back(cap); - } - - void extension(std::string name) { - extensions.op(spv::Op::OpExtension, 1 + div_roundup(name.size() + 1, 4)); - extensions.literal_name(name); - } - - SpvId extended_import(std::string name) { - ext_inst_import.op(spv::Op::OpExtInstImport, 2 + div_roundup(name.size() + 1, 4)); - auto id = generate_fresh_id(); - ext_inst_import.ref_id(id); - ext_inst_import.literal_name(name); - return id; - } - - spv::AddressingModel addressing_model = spv::AddressingModel::AddressingModelLogical; - spv::MemoryModel memory_model = spv::MemoryModel::MemoryModelSimple; - -private: - std::ostream* output_ = nullptr; - uint32_t bound = 1; - - // Ordered as per https://www.khronos.org/registry/spir-v/specs/unified1/SPIRV.pdf#subsection.2.4 - SpvSectionBuilder capabilities; - SpvSectionBuilder extensions; - SpvSectionBuilder ext_inst_import; - SpvSectionBuilder entry_points; - SpvSectionBuilder execution_modes; - SpvSectionBuilder debug_string_source; - SpvSectionBuilder debug_names; - SpvSectionBuilder debug_module_processed; - SpvSectionBuilder annotations; - SpvSectionBuilder types_constants; - SpvSectionBuilder fn_decls; - SpvSectionBuilder fn_defs; - - // SPIR-V disallows duplicate non-aggregate type declarations, we protect against these with this - std::unordered_map unique_decls; - - SpvId declare_void_type() { - types_constants.op(spv::Op::OpTypeVoid, 2); - auto id = generate_fresh_id(); - types_constants.ref_id(id); - return id; - } - - void output_word_le(uint32_t word) { - output_->put((word >> 0) & 0xFFu); - output_->put((word >> 8) & 0xFFu); - output_->put((word >> 16) & 0xFFu); - output_->put((word >> 24) & 0xFFu); - } - - void output_section(SpvSectionBuilder& section) { - for (auto& word : section.data_) { - output_word_le(word); - } - } -public: - const SpvId void_type; - - void finish(std::ostream& output) { - output_ = &output; - SpvSectionBuilder memory_model_section; - memory_model_section.op(spv::Op::OpMemoryModel, 3); - memory_model_section.data_.push_back(addressing_model); - memory_model_section.data_.push_back(memory_model); - - output_word_le(spv::MagicNumber); - output_word_le(spv::Version); // TODO: target a specific spirv version - output_word_le(uint32_t(0)); // TODO get a magic number ? - output_word_le(bound); - output_word_le(uint32_t(0)); // instruction schema padding - - output_section(capabilities); - output_section(extensions); - output_section(ext_inst_import); - output_section(memory_model_section); - output_section(entry_points); - output_section(execution_modes); - output_section(debug_string_source); - output_section(debug_names); - output_section(debug_module_processed); - output_section(annotations); - output_section(types_constants); - output_section(fn_decls); - output_section(fn_defs); - } -}; - -inline SpvId SpvBasicBlockBuilder::generate_fresh_id() { - return file_builder.generate_fresh_id(); -} - -inline SpvId SpvFnBuilder::generate_fresh_id() { - return file_builder->generate_fresh_id(); -} - -} \ No newline at end of file diff --git a/src/thorin/be/spirv/spirv_transform.cpp b/src/thorin/transform/structurize.cpp similarity index 96% rename from src/thorin/be/spirv/spirv_transform.cpp rename to src/thorin/transform/structurize.cpp index f210a8a7a..ee72a64e9 100644 --- a/src/thorin/be/spirv/spirv_transform.cpp +++ b/src/thorin/transform/structurize.cpp @@ -1,10 +1,9 @@ -#include -#include "thorin/be/spirv/spirv.h" -#include "thorin/analyses/scope.h" -#include "thorin/analyses/cfg.h" +#include "structurize.h" -#include -#include +//#include + +#include "thorin/analyses/domtree.h" +#include "thorin/world.h" namespace thorin::spirv { @@ -421,24 +420,24 @@ inline void rewire_loops(World& world, ScopeContext& ctx, const Base* base) { } } -void CodeGen::structure_loops() { - Scope::for_each(world(), [&](Scope& scope) { +void structure_loops(World& world) { + Scope::for_each(world, [&](Scope& scope) { ScopeContext context(scope); const LoopTree& looptree = context.cfa.f_cfg().looptree(); tag_continuations(context, looptree.root(), nullptr); - collect_dispatch_targets(world(), context, looptree.root()); + collect_dispatch_targets(world, context, looptree.root()); - create_headers(world(), context, looptree.root()); - create_epilogues(world(), context, looptree.root()); + create_headers(world, context, looptree.root()); + create_epilogues(world, context, looptree.root()); - rewire_loops(world(), context, looptree.root()); + rewire_loops(world, context, looptree.root()); scope.update(); }); } -void CodeGen::structure_flow() { - Scope::for_each(world(), [&](const Scope& scope) { +void structure_flow(World& world) { + Scope::for_each(world, [&](const Scope& scope) { CFA cfa(scope); auto& dom_tree = cfa.f_cfg().domtree(); auto& post_dom_tree = cfa.b_cfg().domtree(); diff --git a/src/thorin/transform/structurize.h b/src/thorin/transform/structurize.h new file mode 100644 index 000000000..6c6e23c70 --- /dev/null +++ b/src/thorin/transform/structurize.h @@ -0,0 +1,8 @@ +#include "thorin/analyses/looptree.h" +#include "thorin/analyses/scope.h" +#include "thorin/analyses/cfg.h" + +class World; + +void structure_loops(World& world); +void structure_flow(World& world); From 40f042c3b27fcbb8e5490bf9ec3a579df7cbcc55 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Tue, 19 Apr 2022 12:08:01 +0200 Subject: [PATCH 095/342] stub of a shady backend replacing the spirv one --- CMakeLists.txt | 10 +- src/thorin/CMakeLists.txt | 11 +- src/thorin/be/codegen.cpp | 8 +- src/thorin/be/shady/shady.cpp | 57 ++ src/thorin/be/shady/shady.h | 32 + src/thorin/be/spirv/spirv.cpp | 976 ------------------------ src/thorin/be/spirv/spirv.h | 186 ----- src/thorin/be/spirv/spirv_datatypes.cpp | 339 -------- src/thorin/config.h.in | 2 +- src/thorin/transform/structurize.cpp | 2 +- src/thorin/transform/structurize.h | 4 + 11 files changed, 111 insertions(+), 1516 deletions(-) delete mode 100644 src/thorin/be/spirv/spirv.cpp delete mode 100644 src/thorin/be/spirv/spirv.h delete mode 100644 src/thorin/be/spirv/spirv_datatypes.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 9ad593a62..81074796d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -40,10 +40,12 @@ else() message(STATUS "Building without LLVM and RV. Specify LLVM_DIR to compile with LLVM.") endif() -find_package(SPIRV-Headers QUIET CONFIG) -if (SPIRV-Headers_FOUND) - message(STATUS "Found SPIRV-Headers at ${SPIRV-Headers_DIR}") - set(THORIN_ENABLE_SPIRV TRUE) +find_package(shady REQUIRED CONFIG) +if (shady_FOUND) + message(STATUS "Found shady at ${shady_DIR}") + message(STATUS "Found shady headers ${shady_INCLUDE_DIRS}") + include_directories(${shady_INCLUDE_DIRS}) + set(THORIN_ENABLE_SHADY TRUE) endif() message(STATUS "Using Debug flags: ${CMAKE_CXX_FLAGS_DEBUG}") diff --git a/src/thorin/CMakeLists.txt b/src/thorin/CMakeLists.txt index 5791dd247..c894813a3 100644 --- a/src/thorin/CMakeLists.txt +++ b/src/thorin/CMakeLists.txt @@ -105,12 +105,9 @@ if(LLVM_FOUND) ) endif() -if(SPIRV_ENABLED) +if (shady_FOUND) list(APPEND THORIN_SOURCES - be/spirv/spirv.cpp - be/spirv/spirv.h - be/spirv/spirv_transform.cpp - be/spirv/spirv_datatypes.cpp + be/shady/shady.cpp ) endif() @@ -124,3 +121,7 @@ if(LLVM_FOUND) endif() llvm_config(thorin ${AnyDSL_LLVM_LINK_SHARED} ${Thorin_LLVM_COMPONENTS}) endif() + +if (shady_FOUND) + target_link_libraries(thorin PRIVATE shady) +endif() diff --git a/src/thorin/be/codegen.cpp b/src/thorin/be/codegen.cpp index 6a4c08161..f15de6d9c 100644 --- a/src/thorin/be/codegen.cpp +++ b/src/thorin/be/codegen.cpp @@ -6,8 +6,8 @@ #include "thorin/be/llvm/nvvm.h" #include "thorin/be/llvm/amdgpu.h" #endif -#if THORIN_ENABLE_SPIRV -#include "thorin/be/spirv/spirv.h" +#if THORIN_ENABLE_SHADY +#include "thorin/be/shady/shady.h" #endif #include "thorin/be/c/c.h" @@ -182,8 +182,8 @@ DeviceBackends::DeviceBackends(World& world, int opt, bool debug) #else (void)opt; #endif -#if THORIN_ENABLE_SPIRV - if (!importers_[SpirV].world().empty()) cgs[SpirV] = std::make_unique(importers_[SpirV].world(), kernel_config, debug); +#if THORIN_ENABLE_SHADY + if (!importers_[SpirV].world().empty()) cgs[SpirV] = std::make_unique(importers_[SpirV].world(), kernel_config, debug); #endif for (auto [backend, lang] : std::array { std::pair { CUDA, c::Lang::CUDA }, std::pair { OpenCL, c::Lang::OpenCL }, std::pair { HLS, c::Lang::HLS } }) if (!importers_[backend].world().empty()) cgs[backend] = std::make_unique(importers_[backend].world(), kernel_config, lang, debug); diff --git a/src/thorin/be/shady/shady.cpp b/src/thorin/be/shady/shady.cpp index e69de29bb..9062753de 100644 --- a/src/thorin/be/shady/shady.cpp +++ b/src/thorin/be/shady/shady.cpp @@ -0,0 +1,57 @@ +#include "shady.h" + +#include "thorin/analyses/scope.h" +#include "thorin/transform/structurize.h" + +namespace thorin::shady { + +CodeGen::CodeGen(thorin::World& world, Cont2Config& kernel_config, bool debug) + : thorin::CodeGen(world, debug), kernel_config_(kernel_config) +{} + +void CodeGen::emit_stream(std::ostream& out) { + assert(top_level.empty()); + + structure_loops(world()); + structure_flow(world()); + + auto config = shady::IrConfig { + .check_types = true, + }; + arena = shady::new_arena(config); + + Scope::for_each(world(), [&](const Scope& scope) { emit(scope); }); + + // build root node with the top level stuff that got emitted + auto defs = std::vector(top_level.size(), nullptr); + auto vars = std::vector(top_level.size(), nullptr); + for (size_t i = 0; i < top_level.size(); i++) { + defs[i] = top_level[i].first; + vars[i] = top_level[i].second; + } + auto root = shady::root(arena, (shady::Root) { + .variables = shady::nodes(arena, top_level.size(), const_cast(vars.data())), + .definitions = shady::nodes(arena, top_level.size(), const_cast(defs.data())), + }); + + shady::print_node(root); + + out << "todo"; + + shady::destroy_arena(arena); + arena = nullptr; + top_level.clear(); +} + +void CodeGen::emit(const thorin::Scope& scope) { + entry_ = scope.entry(); + assert(entry_->is_returning()); + + assert(false && "TODO"); +} + +shady::Type* CodeGen::convert(const Type *) { + return nullptr; +} + +} diff --git a/src/thorin/be/shady/shady.h b/src/thorin/be/shady/shady.h index e69de29bb..d1c2d9a70 100644 --- a/src/thorin/be/shady/shady.h +++ b/src/thorin/be/shady/shady.h @@ -0,0 +1,32 @@ +#include "thorin/be/codegen.h" + +namespace thorin::shady { + +#include + +class CodeGen : public thorin::CodeGen { +public: + CodeGen(World&, Cont2Config&, bool debug); + + void emit_stream(std::ostream& stream) override; + const char* file_ext() const override { return ".shady"; } + + shady::Type* convert(const Type*); +protected: + void emit(const Scope& scope); + //void emit_epilogue(Continuation*, BasicBlockBuilder* bb); + //shady::Node* emit(const Def* def, BasicBlockBuilder* bb); + //std::vector emit_builtin(const Continuation*, const Continuation*, BasicBlockBuilder*); + + //SpvId get_codom_type(const Continuation* fn); + shady::IrArena* arena = nullptr; + std::vector> top_level; + + Continuation* entry_ = nullptr; + TypeMap> types_; + DefMap defs_; + const Cont2Config& kernel_config_; + +}; + +} diff --git a/src/thorin/be/spirv/spirv.cpp b/src/thorin/be/spirv/spirv.cpp deleted file mode 100644 index da925e174..000000000 --- a/src/thorin/be/spirv/spirv.cpp +++ /dev/null @@ -1,976 +0,0 @@ -#include "thorin/be/spirv/spirv.h" - -#include "thorin/analyses/scope.h" -#include "thorin/analyses/schedule.h" -#include "thorin/analyses/domtree.h" -#include "thorin/transform/cleanup_world.h" - -#include - -namespace thorin::spirv { - -/// Used as a dummy SSA value for emitting things like mem/unit -/// Should never make it in the binary files ! -constexpr SpvId spv_none { 0 }; - -// SPIR-V has 3 "kinds" of primitives, and the user may declare arbitrary bitwidths, the following helps in translation: -enum class PrimTypeKind { - Signed, Unsigned, Float -}; -inline PrimTypeKind classify_primtype(const PrimType* type) { - switch (type->tag()) { -#define THORIN_QS_TYPE(T, M) THORIN_PS_TYPE(T, M) -#define THORIN_PS_TYPE(T, M) \ -case PrimType_##T: \ - return PrimTypeKind::Signed; \ - break; -#include "thorin/tables/primtypetable.h" -#undef THORIN_QS_TYPE -#undef THORIN_PS_TYPE - -#define THORIN_QU_TYPE(T, M) THORIN_PU_TYPE(T, M) -#define THORIN_PU_TYPE(T, M) \ -case PrimType_##T: \ - return PrimTypeKind::Unsigned; \ - break; -#include "thorin/tables/primtypetable.h" -#undef THORIN_QU_TYPE -#undef THORIN_PU_TYPE - -#define THORIN_QF_TYPE(T, M) THORIN_PF_TYPE(T, M) -#define THORIN_PF_TYPE(T, M) \ -case PrimType_##T: \ - return PrimTypeKind::Float; \ - break; -#include "thorin/tables/primtypetable.h" -#undef THORIN_QF_TYPE -#undef THORIN_PF_TYPE - default: THORIN_UNREACHABLE; - } -} -inline const PrimType* get_primtype(World& world, PrimTypeKind kind, int bitwidth, int length) { -#define GET_PRIMTYPE_WITH_KIND(kind) \ -switch (bitwidth) { \ - case 8: return world.type_p##kind##8 (length); \ - case 16: return world.type_p##kind##16(length); \ - case 32: return world.type_p##kind##32(length); \ - case 64: return world.type_p##kind##64(length); \ -} - -#define GET_PRIMTYPE_WITH_KIND_F(kind) \ -switch (bitwidth) { \ - case 8: world.ELOG("8-bit floats do not exist"); \ - case 16: return world.type_p##kind##16(length); \ - case 32: return world.type_p##kind##32(length); \ - case 64: return world.type_p##kind##64(length); \ -} - - switch (kind) { - case PrimTypeKind::Signed: GET_PRIMTYPE_WITH_KIND(s); THORIN_UNREACHABLE; - case PrimTypeKind::Unsigned: GET_PRIMTYPE_WITH_KIND(u); THORIN_UNREACHABLE; - case PrimTypeKind::Float: GET_PRIMTYPE_WITH_KIND_F(f); THORIN_UNREACHABLE; - default: THORIN_UNREACHABLE; - } - -#undef GET_PRIMTYPE_WITH_KIND -#undef GET_PRIMTYPE_WITH_KIND_F -} - -BasicBlockBuilder::BasicBlockBuilder(FnBuilder& fn_builder) - : builder::SpvBasicBlockBuilder(fn_builder.file_builder), fn_builder(fn_builder), file_builder(fn_builder.file_builder) { - label = file_builder.generate_fresh_id(); -} - -FnBuilder::FnBuilder(CodeGen* cg, FileBuilder& file_builder) : builder::SpvFnBuilder(&file_builder), cg(cg), file_builder(file_builder) {} - -FileBuilder::FileBuilder(CodeGen* cg) : builder::SpvFileBuilder(), cg(cg) { - capability(spv::Capability::CapabilityShader); - capability(spv::Capability::CapabilityVariablePointers); - capability(spv::Capability::CapabilityPhysicalStorageBufferAddresses); - // capability(spv::Capability::CapabilityInt16); - capability(spv::Capability::CapabilityInt64); - - addressing_model = spv::AddressingModelPhysicalStorageBuffer64; - memory_model = spv::MemoryModel::MemoryModelGLSL450; -} - -SpvId FileBuilder::u32_t() { - if (u32_t_.id == 0) - u32_t_ = cg->convert(cg->world().type_pu32())->type_id; - return u32_t_; -} - -SpvId FileBuilder::u32_constant(uint32_t pattern) { - return constant(u32_t(), { pattern }); -} - -Builtins::Builtins(FileBuilder& builder) { - auto& world = builder.cg->world(); - auto spv_uvec3_t = builder.cg->convert(world.type_pu32(3)); - auto spv_uint_t = builder.cg->convert(world.type_pu32()); - auto spv_uvec3_pt = builder.declare_ptr_type(spv::StorageClassInput, spv_uvec3_t->type_id); - auto spv_uvec3_ptp = builder.declare_ptr_type(spv::StorageClassPrivate, spv_uvec3_t->type_id); - auto spv_uint_pt = builder.declare_ptr_type(spv::StorageClassInput, spv_uint_t->type_id); - - // Because we technically can have multiple entry points, we take the easy way out and make each entry point - // write to a private variable the actual workgroup size for that specific kernel. Dirty, but simple. - workgroup_size = builder.variable(spv_uvec3_ptp, spv::StorageClassPrivate); - builder.name(workgroup_size, "BuiltInWorkgroupSize"); - - num_workgroups = builder.variable(spv_uvec3_pt, spv::StorageClassInput); - builder.decorate(num_workgroups, spv::DecorationBuiltIn, { spv::BuiltInNumWorkgroups }); - builder.name(num_workgroups, "BuiltInNumWorkgroups"); - - workgroup_id = builder.variable(spv_uvec3_pt, spv::StorageClassInput); - builder.decorate(workgroup_id, spv::DecorationBuiltIn, { spv::BuiltInWorkgroupId }); - builder.name(workgroup_id, "BuiltInWorkgroupId"); - - local_id = builder.variable(spv_uvec3_pt, spv::StorageClassInput); - builder.decorate(local_id, spv::DecorationBuiltIn, { spv::BuiltInLocalInvocationId }); - builder.name(local_id, "BuiltInLocalInvocationId"); - - global_id = builder.variable(spv_uvec3_pt, spv::StorageClassInput); - builder.decorate(global_id, spv::DecorationBuiltIn, { spv::BuiltInGlobalInvocationId }); - builder.name(global_id, "BuiltInGlobalInvocationId"); - - local_invocation_index = builder.variable(spv_uint_pt, spv::StorageClassInput); - builder.decorate(local_invocation_index, spv::DecorationBuiltIn, { spv::BuiltInLocalInvocationIndex }); - builder.name(local_invocation_index, "BuiltInLocalInvocationIndex"); -} - -ImportedInstructions::ImportedInstructions(FileBuilder& builder) { - builder.extension("SPV_KHR_non_semantic_info"); - shader_printf = builder.extended_import("NonSemantic.DebugPrintf"); -} - -CodeGen::CodeGen(thorin::World& world, Cont2Config& kernel_config, bool debug) - : thorin::CodeGen(world, debug), kernel_config_(kernel_config) -{} - -void CodeGen::emit_stream(std::ostream& out) { - builder_ = std::make_unique(this); - - builder_->builtins = std::make_unique(*builder_); - builder_->imported_instrs = std::make_unique(*builder_); - - structure_loops(); - structure_flow(); - // cleanup_world(world()); - - Scope::for_each(world(), [&](const Scope& scope) { emit(scope); }); - - auto push_constant_arr_type = convert(world().definite_array_type(world().type_pu32(), 128))->type_id; - auto push_constant_struct_type = builder_->declare_struct_type({ push_constant_arr_type }); - auto push_constant_struct_ptr_type = builder_->declare_ptr_type(spv::StorageClassPushConstant, push_constant_struct_type); - builder_->name(push_constant_struct_type, "ThorinPushConstant"); - builder_->decorate(push_constant_struct_type, spv::DecorationBlock); - builder_->decorate_member(push_constant_struct_type, 0, spv::DecorationOffset, { 0 }); - builder_->decorate(push_constant_arr_type, spv::DecorationArrayStride, { 4 }); - auto push_constant_struct_ptr = builder_->variable(push_constant_struct_ptr_type, spv::StorageClassPushConstant); - builder_->name(push_constant_struct_ptr, "thorin_push_constant_data"); - - auto entry_pt_signature = builder_->declare_fn_type({}, builder_->void_type); - for (auto& cont : world().continuations()) { - if (cont->is_exported()) { - assert(defs_.contains(cont) && kernel_config_.contains(cont)); - auto config = kernel_config_.find(cont); - - SpvId callee = defs_[cont]; - - FnBuilder fn_builder(this, *builder_.get()); - fn_builder.fn_type = entry_pt_signature; - fn_builder.fn_ret_type = builder_->void_type; - - BasicBlockBuilder* bb = fn_builder.bbs.emplace_back(std::make_unique(fn_builder)).get(); - fn_builder.bbs_to_emit.push_back(bb); - - auto block = config->second->as()->block_size(); - std::vector local_size = { - (uint32_t) std::get<0>(block), - (uint32_t) std::get<1>(block), - (uint32_t) std::get<2>(block), - }; - - auto spv_uvec3_t = convert(world().type_pu32(3)); - SpvId wg_size_constant = builder_->constant_composite(spv_uvec3_t->type_id, { - builder_->u32_constant(local_size[0]), - builder_->u32_constant(local_size[1]), - builder_->u32_constant(local_size[2]), - }); - bb->store(wg_size_constant, builder_->builtins->workgroup_size); - - // iterate on cont type and extract the arguments - auto ptr_type = convert(world().ptr_type(world().definite_array_type(world().type_pu32(), 128), 1, 4, AddrSpace::Push))->type_id; - auto zero = bb->file_builder.u32_constant(0); - auto arr_ref = bb->access_chain(ptr_type, push_constant_struct_ptr, { zero }); - uint32_t offset = 0; - std::vector args; - for (size_t i = 0; i < cont->num_params(); i++) { - auto param = cont->param(i); - auto param_type = param->type(); - if (param_type == world().unit() || param_type == world().mem_type() || param_type->isa()) continue; - assert(param_type->order() == 0); - auto converted = convert(param_type); - assert(converted->datatype != nullptr); - SpvId arg = converted->datatype->emit_deserialization(*bb, spv::StorageClassPushConstant, arr_ref, bb->file_builder.u32_constant(offset)); - args.push_back(arg); - offset += converted->datatype->serialized_size(); - } - - bb->call(builder_->void_type, callee, args); - bb->return_void(); - - builder_->define_function(fn_builder); - builder_->name(fn_builder.function_id, "entry_point_" + cont->name()); - - std::vector interface = { - push_constant_struct_ptr, - builder_->builtins->workgroup_size, - builder_->builtins->num_workgroups, - builder_->builtins->workgroup_id, - builder_->builtins->local_id, - builder_->builtins->global_id, - builder_->builtins->local_invocation_index, - }; - builder_->declare_entry_point(spv::ExecutionModelGLCompute, fn_builder.function_id, "kernel_main", interface); - - builder_->execution_mode(fn_builder.function_id, spv::ExecutionModeLocalSize, local_size); - } - } - - builder_->finish(out); - builder_ = nullptr; -} - -void CodeGen::emit(const thorin::Scope& scope) { - entry_ = scope.entry(); - assert(entry_->is_returning()); - - FnBuilder fn(this, *builder_.get()); - fn.scope = &scope; - fn.fn_type = convert(entry_->type())->type_id; - fn.fn_ret_type = get_codom_type(entry_); - defs_.emplace(scope.entry(), fn.function_id); - - current_fn_ = &fn; - - auto conts = schedule(scope); - - fn.bbs_to_emit.reserve(conts.size()); - fn.bbs.reserve(conts.size()); - auto& bbs = fn.bbs; - - for (auto cont : conts) { - if (cont->intrinsic() == Intrinsic::EndScope) continue; - - BasicBlockBuilder* bb = bbs.emplace_back(std::make_unique(fn)).get(); - fn.bbs_to_emit.emplace_back(bb); - auto [i, b] = fn.bbs_map.emplace(cont, bb); - assert(b); - - if (debug()) - builder_->name(bb->label, cont->name().c_str()); - fn.labels.emplace(cont, bb->label); - - if (entry_ == cont) { - for (auto param : entry_->params()) { - if (is_mem(param) || is_unit(param)) { - // Nothing - } else if (param->order() == 0) { - auto param_t = convert(param->type()); - auto id = fn.parameter(param_t->type_id); - fn.params[param] = id; - if (param->type()->isa()) { - builder_->decorate(id, spv::DecorationAliased); - } - } - } - } else { - for (auto param : cont->params()) { - if (is_mem(param) || is_unit(param)) { - // Nothing - } else { - // OpPhi requires the full list of predecessors (values, labels) - // We don't have that yet! But we will need the Phi node identifier to build the basic blocks ... - // To solve this we generate an id for the phi node now, but defer emission of it to a later stage - auto type = convert(param->type())->type_id; - assert(type.id != 0); - bb->phis_map[param] = { type, builder_->generate_fresh_id(), {} }; - } - } - } - } - - for (auto cont : conts) { - if (cont->intrinsic() == Intrinsic::EndScope) continue; - assert(cont == entry_ || cont->is_basicblock()); - emit_epilogue(cont, fn.bbs_map[cont]); - } - - for(auto& bb : fn.bbs) { - for (auto& [param, phi] : bb->phis_map) { - bb->phis.emplace_back(&phi); - } - } - - builder_->define_function(fn); - builder_->name(fn.function_id, scope.entry()->name()); -} - -SpvId CodeGen::get_codom_type(const Continuation* fn) { - auto ret_cont_type = fn->ret_param()->type(); - std::vector types; - for (auto& op : ret_cont_type->ops()) { - if (op->isa() || is_type_unit(op)) - continue; - assert(op->order() == 0); - types.push_back(convert(op)->type_id); - } - if (types.empty()) - return builder_->void_type; - if (types.size() == 1) - return types[0]; - return builder_->declare_struct_type(types); -} - -void CodeGen::emit_epilogue(Continuation* continuation, BasicBlockBuilder* bb) { - // Handles the potential nuances of jumping to another continuation - auto jump_to_next_cont_with_args = [&](Continuation* succ, std::vector args) { - bb->branch(current_fn_->labels[succ]); - for (size_t i = 0, j = 0; i != succ->num_params(); ++i) { - auto param = succ->param(i); - if (is_mem(param) || is_unit(param)) - continue; - auto& phi = current_fn_->bbs_map[succ]->phis_map[param]; - phi.preds.emplace_back(args[j], current_fn_->labels[continuation]); - j++; - } - }; - - if (continuation->callee() == entry_->ret_param()) { - std::vector values; - - for (auto arg : continuation->args()) { - assert(arg->order() == 0); - auto val = emit(arg, bb); - if (is_mem(arg) || is_unit(arg)) - continue; - values.emplace_back(val); - } - - switch (values.size()) { - case 0: bb->return_void(); break; - case 1: bb->return_value(values[0]); break; - default: bb->return_value(bb->composite(current_fn_->fn_ret_type, values)); - } - } else if (auto callee = continuation->callee()->isa_continuation(); callee && callee->is_basicblock()) { // ordinary jump - int index = -1; - for (auto& arg : continuation->args()) { - index++; - auto val = emit(arg, bb); - if (is_mem(arg) || is_unit(arg)) continue; - bb->args[arg] = val; - auto* param = callee->param(index); - auto& phi = current_fn_->bbs_map[callee]->phis_map[param]; - phi.preds.emplace_back(bb->args[arg], current_fn_->labels[continuation]); - } - bb->branch(current_fn_->labels[callee]); - } else if (continuation->callee() == world().branch()) { - auto& domtree = current_fn_->scope->b_cfg().domtree(); - auto merge_cont = domtree.idom(current_fn_->scope->f_cfg().operator[](continuation))->continuation(); - SpvId merge_bb; - if (merge_cont == current_fn_->scope->exit()) { - BasicBlockBuilder* unreachable_merge_bb = current_fn_->bbs.emplace_back(std::make_unique(*current_fn_)).get(); - current_fn_->bbs_to_emit.emplace_back(unreachable_merge_bb); - builder_->name(unreachable_merge_bb->label, "merge_unreachable" + continuation->name()); - unreachable_merge_bb->unreachable(); - merge_bb = unreachable_merge_bb->label; - } else { - // TODO create a dedicated merge bb if this one is the merge blocks for more than 1 selection construct - merge_bb = current_fn_->labels[merge_cont]; - } - - auto cond = emit(continuation->arg(0), bb); - bb->args.emplace(continuation->arg(0), cond); - auto tbb = current_fn_->labels[continuation->arg(1)->as_continuation()]; - auto fbb = current_fn_->labels[continuation->arg(2)->as_continuation()]; - bb->selection_merge(merge_bb,spv::SelectionControlMaskNone); - bb->branch_conditional(cond, tbb, fbb); - } else if (continuation->callee()->isa() && continuation->callee()->as()->intrinsic() == Intrinsic::Match) { - /*auto val = emit(continuation->arg(0)); - auto otherwise_bb = cont2bb(continuation->arg(1)->as_continuation()); - auto match = irbuilder.CreateSwitch(val, otherwise_bb, continuation->num_args() - 2); - for (size_t i = 2; i < continuation->num_args(); i++) { - auto arg = continuation->arg(i)->as(); - auto case_const = llvm::cast(emit(arg->op(0))); - auto case_bb = cont2bb(arg->op(1)->as_continuation()); - match->addCase(case_const, case_bb); - }*/ - THORIN_UNREACHABLE; - } else if (continuation->callee()->isa()) { - bb->unreachable(); - } else if (continuation->intrinsic() == Intrinsic::SCFLoopHeader) { - auto merge_label = current_fn_->bbs_map[const_cast(continuation->attributes_.scf_metadata.loop_header.merge_target)]->label; - auto continue_label = current_fn_->bbs_map[const_cast(continuation->attributes_.scf_metadata.loop_header.continue_target)]->label; - bb->loop_merge(merge_label, continue_label, spv::LoopControlMaskNone, {}); - - BasicBlockBuilder* dispatch_bb = current_fn_->bbs.emplace_back(std::make_unique(*current_fn_)).get(); - - auto header_bb_location = std::find(current_fn_->bbs_to_emit.begin(), current_fn_->bbs_to_emit.end(), bb); - - current_fn_->bbs_to_emit.emplace(header_bb_location + 1, dispatch_bb); - builder_->name(dispatch_bb->label, "dispatch_" + continuation->name()); - bb->branch(dispatch_bb->label); - int targets = continuation->num_ops(); - assert(targets > 0); - - // TODO handle dispatching to multiple targets - assert(targets == 1); - auto dispatch_target = continuation->op(0)->as_continuation(); - // Extract the relevant variant & expand the tuple if necessary - auto arg = world().variant_extract(continuation->param(0), 0); - auto extracted = emit(arg, dispatch_bb); - - if (dispatch_target->param(0)->type()->equal(arg->type())) { - auto* param = dispatch_target->param(0); - auto& phi = current_fn_->bbs_map[dispatch_target]->phis_map[param]; - phi.preds.emplace_back(extracted, dispatch_bb->label); - } else { - assert(false && "TODO destructure argument"); - } - - dispatch_bb->branch(current_fn_->bbs_map[dispatch_target]->label); - - } else if (continuation->intrinsic() == Intrinsic::SCFLoopContinue) { - auto loop_header = continuation->op(0)->as_continuation(); - auto header_label = current_fn_->bbs_map[loop_header]->label; - - auto arg = continuation->param(0); - bb->args[arg] = emit(arg, bb); - auto* param = loop_header->param(0); - auto& phi = current_fn_->bbs_map[loop_header]->phis_map[param]; - phi.preds.emplace_back(bb->args[arg], current_fn_->labels[continuation]); - - bb->branch(header_label); - } else if (continuation->intrinsic() == Intrinsic::SCFLoopMerge) { - - int targets = continuation->num_ops(); - assert(targets > 0); - - // TODO handle dispatching to multiple targets - assert(targets == 1); - auto callee = continuation->op(0)->as_continuation(); - // TODO phis - bb->branch(current_fn_->bbs_map[callee]->label); - } else if (auto builtin = continuation->callee()->isa_continuation(); builtin->is_imported()) { - // Ensure we emit previous memory operations - assert(is_mem(continuation->arg(0))); - emit(continuation->arg(0), bb); - - auto productions = emit_builtin(continuation, builtin, bb); - auto succ = continuation->args().back()->as_continuation(); - jump_to_next_cont_with_args(succ, productions); - } else if (auto intrinsic = continuation->callee()->isa_continuation(); callee && callee->is_intrinsic()) { - THORIN_UNREACHABLE; - } else { // function/closure call - // put all first-order args into an array - std::vector call_args; - const Def* ret_arg = nullptr; - for (auto arg : continuation->args()) { - if (arg->order() == 0) { - auto arg_type = arg->type(); - auto arg_val = emit(arg, bb); - if (arg_type == world().unit() || arg_type == world().mem_type()) continue; - call_args.push_back(arg_val); - } else { - assert(!ret_arg); - ret_arg = arg; - } - } - - auto ret_type = get_codom_type(continuation); - - SpvId call_result; - if (auto called_continuation = continuation->callee()->isa_continuation()) { - call_result = bb->call(ret_type, emit(called_continuation, bb), call_args); - } else { - // must be a closure - THORIN_UNREACHABLE; - - // auto closure = emit(callee); - // args.push_back(irbuilder.CreateExtractValue(closure, 1)); - // call = irbuilder.CreateCall(irbuilder.CreateExtractValue(closure, 0), args); - } - - // must be call + continuation --- call + return has been removed by codegen_prepare - auto succ = ret_arg->as_continuation(); - - size_t n = 0; - const Param* last_param = nullptr; - for (auto param : succ->params()) { - if (is_mem(param) || is_unit(param)) - continue; - last_param = param; - n++; - } - - if (n == 0) { - bb->branch(current_fn_->labels[succ]); - } else if (n == 1) { - bb->branch(current_fn_->labels[succ]); - - auto& phi = current_fn_->bbs_map[succ]->phis_map[last_param]; - phi.preds.emplace_back(call_result, current_fn_->labels[continuation]); - } else { - Array extracts(n); - for (size_t i = 0, j = 0; i != succ->num_params(); ++i) { - auto param = succ->param(i); - if (is_mem(param) || is_unit(param)) - continue; - extracts[j] = bb->extract(convert(param->type())->type_id, call_result, { (uint32_t) j }); - j++; - } - - bb->branch(current_fn_->labels[succ]); - - for (size_t i = 0, j = 0; i != succ->num_params(); ++i) { - auto param = succ->param(i); - if (is_mem(param) || is_unit(param)) - continue; - - auto& phi = current_fn_->bbs_map[succ]->phis_map[param]; - phi.preds.emplace_back(extracts[j], current_fn_->labels[continuation]); - - j++; - } - } - } -} - -SpvId CodeGen::emit(const Def* def, BasicBlockBuilder* bb) { - if (auto bin = def->isa()) { - SpvId lhs = emit(bin->lhs(), bb); - SpvId rhs = emit(bin->rhs(), bb); - ConvertedType* result_types = convert(def->type()); - SpvId result_type = result_types->type_id; - - if (auto cmp = bin->isa()) { - auto type = cmp->lhs()->type(); - if (is_type_s(type)) { - switch (cmp->cmp_tag()) { - case Cmp_eq: return bb->binop(spv::Op::OpIEqual , result_type, lhs, rhs); - case Cmp_ne: return bb->binop(spv::Op::OpINotEqual , result_type, lhs, rhs); - case Cmp_gt: return bb->binop(spv::Op::OpSGreaterThan , result_type, lhs, rhs); - case Cmp_ge: return bb->binop(spv::Op::OpSGreaterThanEqual , result_type, lhs, rhs); - case Cmp_lt: return bb->binop(spv::Op::OpSLessThan , result_type, lhs, rhs); - case Cmp_le: return bb->binop(spv::Op::OpSLessThanEqual , result_type, lhs, rhs); - } - } else if (is_type_u(type)) { - switch (cmp->cmp_tag()) { - case Cmp_eq: return bb->binop(spv::Op::OpIEqual , result_type, lhs, rhs); - case Cmp_ne: return bb->binop(spv::Op::OpINotEqual , result_type, lhs, rhs); - case Cmp_gt: return bb->binop(spv::Op::OpUGreaterThan , result_type, lhs, rhs); - case Cmp_ge: return bb->binop(spv::Op::OpUGreaterThanEqual , result_type, lhs, rhs); - case Cmp_lt: return bb->binop(spv::Op::OpULessThan , result_type, lhs, rhs); - case Cmp_le: return bb->binop(spv::Op::OpULessThanEqual , result_type, lhs, rhs); - } - } else if (is_type_f(type)) { - switch (cmp->cmp_tag()) { - // TODO look into the NaN story - case Cmp_eq: return bb->binop(spv::Op::OpFOrdEqual , result_type, lhs, rhs); - case Cmp_ne: return bb->binop(spv::Op::OpFOrdNotEqual , result_type, lhs, rhs); - case Cmp_gt: return bb->binop(spv::Op::OpFOrdGreaterThan , result_type, lhs, rhs); - case Cmp_ge: return bb->binop(spv::Op::OpFOrdGreaterThanEqual , result_type, lhs, rhs); - case Cmp_lt: return bb->binop(spv::Op::OpFOrdLessThan , result_type, lhs, rhs); - case Cmp_le: return bb->binop(spv::Op::OpFOrdLessThanEqual , result_type, lhs, rhs); - } - } else if (type->isa()) { - assertf(false, "Physical pointers are unsupported"); - } else if(is_type_bool(type)) { - switch (cmp->cmp_tag()) { - // TODO look into the NaN story - case Cmp_eq: return bb->binop(spv::Op::OpLogicalEqual , result_type, lhs, rhs); - case Cmp_ne: return bb->binop(spv::Op::OpLogicalNotEqual , result_type, lhs, rhs); - default: THORIN_UNREACHABLE; - } - assertf(false, "TODO: should we emulate the other comparison ops ?"); - } - } - - if (auto arithop = bin->isa()) { - auto type = arithop->type(); - - if (is_type_f(type)) { - switch (arithop->arithop_tag()) { - case ArithOp_add: return bb->binop(spv::Op::OpFAdd, result_type, lhs, rhs); - case ArithOp_sub: return bb->binop(spv::Op::OpFSub, result_type, lhs, rhs); - case ArithOp_mul: return bb->binop(spv::Op::OpFMul, result_type, lhs, rhs); - case ArithOp_div: return bb->binop(spv::Op::OpFDiv, result_type, lhs, rhs); - case ArithOp_rem: return bb->binop(spv::Op::OpFRem, result_type, lhs, rhs); - case ArithOp_and: - case ArithOp_or: - case ArithOp_xor: - case ArithOp_shl: - case ArithOp_shr: THORIN_UNREACHABLE; - } - } - - if (is_type_s(type)) { - switch (arithop->arithop_tag()) { - case ArithOp_add: return bb->binop(spv::Op::OpIAdd , result_type, lhs, rhs); - case ArithOp_sub: return bb->binop(spv::Op::OpISub , result_type, lhs, rhs); - case ArithOp_mul: return bb->binop(spv::Op::OpIMul , result_type, lhs, rhs); - case ArithOp_div: return bb->binop(spv::Op::OpSDiv , result_type, lhs, rhs); - case ArithOp_rem: return bb->binop(spv::Op::OpSRem , result_type, lhs, rhs); - case ArithOp_and: return bb->binop(spv::Op::OpBitwiseAnd , result_type, lhs, rhs); - case ArithOp_or: return bb->binop(spv::Op::OpBitwiseOr , result_type, lhs, rhs); - case ArithOp_xor: return bb->binop(spv::Op::OpBitwiseXor , result_type, lhs, rhs); - case ArithOp_shl: return bb->binop(spv::Op::OpShiftLeftLogical , result_type, lhs, rhs); - case ArithOp_shr: return bb->binop(spv::Op::OpShiftRightArithmetic , result_type, lhs, rhs); - } - } else if (is_type_u(type)) { - switch (arithop->arithop_tag()) { - case ArithOp_add: return bb->binop(spv::Op::OpIAdd , result_type, lhs, rhs); - case ArithOp_sub: return bb->binop(spv::Op::OpISub , result_type, lhs, rhs); - case ArithOp_mul: return bb->binop(spv::Op::OpIMul , result_type, lhs, rhs); - case ArithOp_div: return bb->binop(spv::Op::OpUDiv , result_type, lhs, rhs); - case ArithOp_rem: return bb->binop(spv::Op::OpUMod , result_type, lhs, rhs); - case ArithOp_and: return bb->binop(spv::Op::OpBitwiseAnd , result_type, lhs, rhs); - case ArithOp_or: return bb->binop(spv::Op::OpBitwiseOr , result_type, lhs, rhs); - case ArithOp_xor: return bb->binop(spv::Op::OpBitwiseXor , result_type, lhs, rhs); - case ArithOp_shl: return bb->binop(spv::Op::OpShiftLeftLogical , result_type, lhs, rhs); - case ArithOp_shr: return bb->binop(spv::Op::OpShiftRightLogical , result_type, lhs, rhs); - } - } else if(is_type_bool(type)) { - switch (arithop->arithop_tag()) { - case ArithOp_and: return bb->binop(spv::Op::OpLogicalAnd , result_type, lhs, rhs); - case ArithOp_or: return bb->binop(spv::Op::OpLogicalOr , result_type, lhs, rhs); - // Note: there is no OpLogicalXor - case ArithOp_xor: return bb->binop(spv::Op::OpLogicalNotEqual , result_type, lhs, rhs); - default: THORIN_UNREACHABLE; - } - } - THORIN_UNREACHABLE; - } - } else if (auto primlit = def->isa()) { - Box box = primlit->value(); - auto type = convert(def->type())->type_id; - SpvId constant; - switch (primlit->primtype_tag()) { - case PrimType_bool: constant = bb->file_builder.bool_constant(type, box.get_bool()); break; - case PrimType_ps8: case PrimType_qs8: assertf(false, "not implemented yet"); - case PrimType_pu8: case PrimType_qu8: assertf(false, "not implemented yet"); - case PrimType_ps16: case PrimType_qs16: assertf(false, "not implemented yet"); - case PrimType_pu16: case PrimType_qu16: assertf(false, "not implemented yet"); - case PrimType_ps32: case PrimType_qs32: constant = bb->file_builder.constant(type, { static_cast(box.get_s32()) }); break; - case PrimType_pu32: case PrimType_qu32: constant = bb->file_builder.constant(type, { static_cast(box.get_u32()) }); break; - case PrimType_ps64: case PrimType_qs64: - case PrimType_pu64: case PrimType_qu64: { - uint64_t value = static_cast(box.get_u64()); - uint64_t upper = value >> 32U; - uint64_t lower = value & 0xFFFFFFFFU; - constant = bb->file_builder.constant(type, { (uint32_t) lower, (uint32_t) upper }); - break; - } - case PrimType_pf16: case PrimType_qf16: assertf(false, "not implemented yet"); - case PrimType_pf32: case PrimType_qf32: assertf(false, "not implemented yet"); - case PrimType_pf64: case PrimType_qf64: assertf(false, "not implemented yet"); - } - return constant; - } else if (auto param = def->isa()) { - if (is_mem(param)) return spv_none; - if (auto param_id = current_fn_->params.lookup(param)) { - assert((*param_id).id != 0); - return *param_id; - } else { - auto val = (*current_fn_->bbs_map[param->continuation()]).phis_map[param].value; - assert(val.id != 0); - return val; - } - } else if (auto variant = def->isa()) { - auto variant_type = def->type()->as(); - auto variant_datatype = (ProductDatatype*) convert(variant_type)->datatype.get(); - auto tag = builder_->u32_constant(variant->index()); - - if (variant_datatype->elements_types.size() > 1) { - auto alloc_type = convert(world().ptr_type(variant_datatype->elements_types[1]->src_type, 1, 4, AddrSpace::Function))->type_id; - auto payload_arr = current_fn_->variable(alloc_type, spv::StorageClassFunction); - auto converted_payload_type = convert(variant_type->op(variant->index())); - - converted_payload_type->datatype->emit_serialization(*bb, spv::StorageClassFunction, payload_arr, bb->file_builder.u32_constant(0), emit(variant->value(), bb)); - auto payload = bb->load(variant_datatype->elements_types[1]->type_id, payload_arr); - - std::vector with_tag = {tag, payload}; - return bb->composite(convert(variant->type())->type_id, with_tag); - } else { - // Zero-sized payload case - std::vector with_tag = { tag }; - return bb->composite(convert(variant->type())->type_id, with_tag); - } - } else if (auto vextract = def->isa()) { - auto variant_type = vextract->value()->type()->as(); - auto variant_datatype = (ProductDatatype*) convert(variant_type)->datatype.get(); - - auto target_type = convert(def->type()); - - assert(variant_datatype->elements_types.size() > 1 && "Can't extract zero-sized datatypes"); - auto alloc_type = convert(world().ptr_type(variant_datatype->elements_types[1]->src_type, 1, 4, AddrSpace::Function))->type_id; - auto payload_arr = current_fn_->variable(alloc_type, spv::StorageClassFunction); - auto payload = bb->extract(variant_datatype->elements_types[1]->type_id, emit(vextract->value(), bb), {1}); - bb->store(payload, payload_arr); - - return target_type->datatype->emit_deserialization(*bb, spv::StorageClassFunction, payload_arr, bb->file_builder.u32_constant(0)); - } else if (auto vindex = def->isa()) { - auto value = emit(vindex->op(0), bb); - return bb->extract(convert(world().type_pu32())->type_id, value, { 0 }); - } else if (auto tuple = def->isa()) { - std::vector elements; - elements.resize(tuple->num_ops()); - size_t x = 0; - for (auto& e : tuple->ops()) { - elements[x++] = emit(e, bb); - } - return bb->composite(convert(tuple->type())->type_id, elements); - } else if (auto structagg = def->isa()) { - std::vector elements; - elements.resize(structagg->num_ops()); - size_t x = 0; - for (auto& e : structagg->ops()) { - elements[x++] = emit(e, bb); - } - return bb->composite(convert(structagg->type())->type_id, elements); - } else if (auto access = def->isa()) { - // emit dependent operations first - emit(access->mem(), bb); - - std::vector operands; - auto ptr_type = access->ptr()->type()->as(); - if (ptr_type->addr_space() == AddrSpace::Global) { - operands.push_back(spv::MemoryAccessAlignedMask); - operands.push_back( 4 ); // TODO: SPIR-V docs say to consult client API for valid values. - } - if (auto load = def->isa()) { - return bb->load(convert(load->out_val_type())->type_id, emit(load->ptr(), bb), operands); - } else if (auto store = def->isa()) { - bb->store(emit(store->val(), bb), emit(store->ptr(), bb), operands); - return spv_none; - } else THORIN_UNREACHABLE; - } else if (auto lea = def->isa()) { - switch (lea->ptr_type()->addr_space()) { - case AddrSpace::Global: - case AddrSpace::Shared: - break; - default: - world().ELOG("LEA is only allowed in global & shared address spaces"); - break; - } - auto type = convert(lea->ptr_type()); - auto offset = emit(lea->index(), bb); - return bb->ptr_access_chain(type->type_id, emit(lea->ptr(), bb), offset, {}); - } else if (auto aggop = def->isa()) { - auto spv_agg = emit(aggop->agg(), bb); - auto agg_type = convert(aggop->agg()->type())->type_id; - - bool mem = false; - if (auto tt = aggop->agg()->type()->isa(); tt && tt->op(0)->isa()) mem = true; - - auto copy_to_alloca = [&] (SpvId target_type) { - world().wdef(def, "slow: alloca and loads/stores needed for aggregate '{}'", def); - auto agg_ptr_type = builder_->declare_ptr_type(spv::StorageClassFunction, agg_type); - - auto variable = bb->fn_builder.variable(agg_ptr_type, spv::StorageClassFunction); - bb->store(spv_agg, variable); - - auto cell_ptr_type = builder_->declare_ptr_type(spv::StorageClassFunction, target_type); - auto cell = bb->access_chain(cell_ptr_type, variable, { emit(aggop->index(), bb)} ); - return std::make_pair(variable, cell); - }; - - if (auto extract = aggop->isa()) { - if (is_mem(extract)) return spv_none; - - auto target_type = convert(extract->type())->type_id; - auto constant_index = aggop->index()->isa(); - - // We have a fast-path: if the index is constant, we can simply use OpCompositeExtract - if (aggop->agg()->type()->isa() && constant_index == nullptr) { - assert(aggop->agg()->type()->isa()); - assert(!is_mem(extract)); - return bb->load(target_type, copy_to_alloca(target_type).second); - } - - if (extract->agg()->type()->isa()) - return bb->vector_extract_dynamic(target_type, spv_agg, emit(extract->index(), bb)); - - // index *must* be constant for the remaining possible cases - assert(constant_index != nullptr); - uint32_t index = constant_index->value().get_u32(); - - unsigned offset = 0; - if (mem) { - if (aggop->agg()->type()->num_ops() == 2) return spv_agg; - offset = 1; - } - - return bb->extract(target_type, spv_agg, { index - offset }); - } else if (auto insert = def->isa()) { - auto value = emit(insert->value(), bb); - auto constant_index = aggop->index()->isa(); - - // TODO deal with mem - but I think for now this case shouldn't happen - - if (insert->agg()->type()->isa() && constant_index == nullptr) { - assert(aggop->agg()->type()->isa()); - auto [variable, cell] = copy_to_alloca(agg_type); - bb->store(value, cell); - return bb->load(agg_type, variable); - } - - if (insert->agg()->type()->isa()) - return bb->vector_insert_dynamic(agg_type, spv_agg, value, emit(insert->index(), bb)); - - // index *must* be constant for the remaining possible cases - assert(constant_index != nullptr); - uint32_t index = constant_index->value().get_u32(); - - return bb->insert(agg_type, value, spv_agg, { index }); - } else THORIN_UNREACHABLE; - } else if (auto conv = def->isa()) { - auto src_type = conv->from()->type(); - auto dst_type = conv->type(); - - auto conv_src_type = convert(src_type); - auto conv_dst_type = convert(dst_type); - - if (auto bitcast = def->isa()) { - if (conv_src_type->datatype->serialized_size() != conv_dst_type->datatype->serialized_size()) - world().ELOG("Source (%) and destination (%) datatypes sizes do not match (% vs % bytes)", src_type->to_string(), dst_type->to_string(), conv_src_type->datatype->serialized_size(), conv_dst_type->datatype->serialized_size()); - - return bb->convert(spv::OpBitcast, convert(bitcast->type())->type_id, emit(bitcast->from(), bb)); - } else if (auto cast = def->isa()) { - // NB: all ops used here are scalar/vector agnostic - auto src_prim = src_type->isa(); - auto dst_prim = dst_type->isa(); - if (!src_prim || !dst_prim || src_prim->length() != dst_prim->length()) - world().ELOG("Illegal cast: % to %, casts are only supported between primitives with identical vector length", src_type->to_string(), dst_type->to_string()); - - auto length = src_prim->length(); - - auto src_kind = classify_primtype(src_prim); - auto dst_kind = classify_primtype(dst_prim); - size_t src_bitwidth = conv_src_type->datatype->serialized_size(); - size_t dst_bitwidth = conv_src_type->datatype->serialized_size(); - - SpvId data = emit(cast->from(), bb); - - // If floating point is involved (src or dst), OpConvert*ToF and OpConvertFTo* can take care of the bit width transformation so no need for any chopping/expanding - if (src_kind == PrimTypeKind::Float || dst_kind == PrimTypeKind::Float) { - auto target_type = convert(get_primtype(world(), dst_kind, dst_bitwidth, length))->type_id; - switch (src_kind) { - case PrimTypeKind::Signed: data = bb->convert(spv::OpConvertSToF, target_type, data); break; - case PrimTypeKind::Unsigned: data = bb->convert(spv::OpConvertUToF, target_type, data); break; - case PrimTypeKind::Float: - switch (dst_kind) { - case PrimTypeKind::Signed: data = bb->convert(spv::OpConvertFToS, target_type, data); break; - case PrimTypeKind::Unsigned: data = bb->convert(spv::OpConvertFToU, target_type, data); break; - default: THORIN_UNREACHABLE; - } - break; - } - } else { - // we expand first and shrink last to minimize precision losses, with bitcast in the middle - bool needs_chopping = src_bitwidth > dst_bitwidth; - bool needs_expanding = src_bitwidth < dst_bitwidth; - - if (needs_expanding) { - auto target_type = convert(get_primtype(world(), src_kind, src_bitwidth, length))->type_id; - switch (src_kind) { - case PrimTypeKind::Signed: - data = bb->convert(spv::OpSConvert, target_type, data); - break; - case PrimTypeKind::Unsigned: - data = bb->convert(spv::OpUConvert, target_type, data); - break; - case PrimTypeKind::Float: - data = bb->convert(spv::OpFConvert, target_type, data); - break; - } - } - - auto expanded_bitwidth = needs_expanding ? dst_bitwidth : src_bitwidth; - auto bitcast_target_type = convert(get_primtype(world(), dst_kind, expanded_bitwidth, length))->type_id; - data = bb->convert(spv::OpBitcast, bitcast_target_type, data); - - if (needs_chopping) { - auto target_type = convert(get_primtype(world(), dst_kind, dst_bitwidth, length))->type_id; - switch (dst_kind) { - case PrimTypeKind::Signed: - data = bb->convert(spv::OpSConvert, target_type, data); - break; - case PrimTypeKind::Unsigned: - data = bb->convert(spv::OpUConvert, target_type, data); - break; - case PrimTypeKind::Float: - data = bb->convert(spv::OpFConvert, target_type, data); - break; - } - } - } - } else THORIN_UNREACHABLE; - } else if (def->isa()) { - return bb->undef(convert(def->type())->type_id); - } - assertf(false, "Incomplete emit(def) definition"); -} - -std::vector CodeGen::emit_builtin(const Continuation* source_cont, const Continuation* builtin, BasicBlockBuilder* bb) { - std::vector productions; - auto uvec3_t = convert(world().type_pu32(3)); - auto u32_t = convert(world().type_pu32()); - auto i32_t = convert(world().type_ps32()); - if (builtin->name() == "spirv.nonsemantic.printf") { - std::vector args; - auto string = source_cont->arg(1); - if (auto arr_type = string->type()->isa(); arr_type->elem_type() == world().type_pu8()) { - auto arr = string->as(); - std::vector the_string; - for (size_t i = 0; i < arr_type->dim(); i++) - the_string.push_back(arr->op(i)->as()->value().get_u8()); - the_string.push_back('\0'); - args.push_back(builder_->debug_string(the_string.data())); - } else world().ELOG("spirv.nonsemantic.printf takes a string literal"); - - for (size_t i = 2; i < source_cont->num_args() - 1; i++) { - args.push_back(emit(source_cont->arg(i), bb)); - } - - bb->ext_instruction(bb->file_builder.void_type, builder_->imported_instrs->shader_printf, 1, args); - } else if (builtin->name() == "get_work_dim") { - THORIN_UNREACHABLE; - } else if (builtin->name() == "get_global_id") { - auto vector = bb->load(uvec3_t->type_id, builder_->builtins->global_id); - auto extracted = bb->vector_extract_dynamic(u32_t->type_id, vector, emit(source_cont->arg(1), bb)); - productions.push_back(bb->convert(spv::OpBitcast, i32_t->type_id, extracted)); - } else if (builtin->name() == "get_local_size") { - auto vector = bb->load(uvec3_t->type_id, builder_->builtins->workgroup_size); - auto extracted = bb->vector_extract_dynamic(u32_t->type_id, vector, emit(source_cont->arg(1), bb)); - productions.push_back(bb->convert(spv::OpBitcast, i32_t->type_id, extracted)); - } else if (builtin->name() == "get_local_id") { - auto vector = bb->load(uvec3_t->type_id, builder_->builtins->local_id); - auto extracted = bb->vector_extract_dynamic(u32_t->type_id, vector, emit(source_cont->arg(1), bb)); - productions.push_back(bb->convert(spv::OpBitcast, i32_t->type_id, extracted)); - } else if (builtin->name() == "get_num_groups") { - auto vector = bb->load(uvec3_t->type_id, builder_->builtins->num_workgroups); - auto extracted = bb->vector_extract_dynamic(u32_t->type_id, vector, emit(source_cont->arg(1), bb)); - productions.push_back(bb->convert(spv::OpBitcast, i32_t->type_id, extracted)); - } else if (builtin->name() == "get_group_id") { - auto vector = bb->load(uvec3_t->type_id, builder_->builtins->workgroup_id); - auto extracted = bb->vector_extract_dynamic(u32_t->type_id, vector, emit(source_cont->arg(1), bb)); - productions.push_back(bb->convert(spv::OpBitcast, i32_t->type_id, extracted)); - } else { - world().ELOG("This spir-v builtin isn't recognised: %s", builtin->name()); - } - return productions; -} - -} diff --git a/src/thorin/be/spirv/spirv.h b/src/thorin/be/spirv/spirv.h deleted file mode 100644 index 7d31df5de..000000000 --- a/src/thorin/be/spirv/spirv.h +++ /dev/null @@ -1,186 +0,0 @@ -#ifndef THORIN_SPIRV_H -#define THORIN_SPIRV_H - -#include "thorin/be/spirv/spirv_builder.hpp" -#include "thorin/be/codegen.h" - -namespace thorin::spirv { - -using SpvId = builder::SpvId; - -class CodeGen; -struct Datatype; -struct PtrDatatype; - -struct FileBuilder; -struct FnBuilder; - -struct ConvertedType { - ConvertedType(CodeGen* cg) : code_gen(cg) {} - ConvertedType(const ConvertedType&) = delete; - - spirv::CodeGen* code_gen; - const thorin::Type* src_type; - SpvId type_id { 0 }; - std::unique_ptr datatype; - - bool is_known_size() { return datatype != nullptr; } -}; - -struct BasicBlockBuilder : public builder::SpvBasicBlockBuilder { - explicit BasicBlockBuilder(FnBuilder& fn_builder); - BasicBlockBuilder(const BasicBlockBuilder&) = delete; - - FnBuilder& fn_builder; - FileBuilder& file_builder; - std::unordered_map phis_map; - DefMap args; -}; - -struct FnBuilder : public builder::SpvFnBuilder { - explicit FnBuilder(CodeGen* cg, FileBuilder& file_builder); - FnBuilder(const FnBuilder&) = delete; - - CodeGen* cg; - FileBuilder& file_builder; - - const Scope* scope = nullptr; - std::vector> bbs; - std::unordered_map bbs_map; - ContinuationMap labels; - DefMap params; -}; - -struct Builtins { - SpvId workgroup_size; - SpvId num_workgroups; - SpvId workgroup_id; - SpvId local_id; - SpvId global_id; - SpvId local_invocation_index; - - explicit Builtins(FileBuilder&); -}; - -struct ImportedInstructions { - SpvId shader_printf; - - explicit ImportedInstructions(FileBuilder&); -}; - -struct FileBuilder : public builder::SpvFileBuilder { - explicit FileBuilder(CodeGen* cg); - FileBuilder(const FileBuilder&) = delete; - - CodeGen* cg; - - std::unique_ptr builtins; - std::unique_ptr imported_instrs; - - SpvId u32_t(); - SpvId u32_constant(uint32_t); - -private: - SpvId u32_t_ { 0 }; - /*SpvId i32_t; - SpvId u32_t; - SpvId i64_t; - SpvId u64_t; - SpvId i32_constant(int32_t); - SpvId i64_constant(int64_t); - SpvId u64_constant(uint64_t);*/ -}; - -class CodeGen : public thorin::CodeGen { -public: - CodeGen(World&, Cont2Config&, bool debug); - - void emit_stream(std::ostream& stream) override; - const char* file_ext() const override { return ".spv"; } - - ConvertedType* convert(const Type*); -protected: - void structure_loops(); - void structure_flow(); - - void emit(const Scope& scope); - void emit_epilogue(Continuation*, BasicBlockBuilder* bb); - SpvId emit(const Def* def, BasicBlockBuilder* bb); - std::vector emit_builtin(const Continuation*, const Continuation*, BasicBlockBuilder*); - - SpvId get_codom_type(const Continuation* fn); - - std::unique_ptr builder_; - Continuation* entry_ = nullptr; - FnBuilder* current_fn_ = nullptr; - TypeMap> types_; - DefMap defs_; - const Cont2Config& kernel_config_; - - friend PtrDatatype; -}; - -/// Thorin data types are mapped to SPIR-V in non-trivial ways, this interface is used by the emission code to abstract over -/// potentially different mappings, depending on the capabilities of the target platform. The serdes code deals with pointers -/// in arrays of unsigned 32 bit words, and is there to get around the limitation of not being able to bitcast pointers in the -/// logical addressing mode. -struct Datatype { -public: - ConvertedType* type; - Datatype(ConvertedType* type) : type(type) {} - - // Datatypes are serialized using a base element, for now it is hardcoded to use 32-bit scalar unsigned integers - static constexpr size_t base_element_bitwidth = 32; - static constexpr size_t base_element_bytes = base_element_bitwidth / 8; - - virtual size_t serialized_size() = 0; - virtual SpvId emit_deserialization(BasicBlockBuilder& bb, spv::StorageClass storage_class, SpvId array, SpvId base_offset) = 0; - virtual void emit_serialization(BasicBlockBuilder& bb, spv::StorageClass storage_class, SpvId array, SpvId base_offset, SpvId data) = 0; -}; - -/// For scalar datatypes -struct ScalarDatatype : public Datatype { - int type_tag; - size_t size_in_bytes; - size_t alignment; - ScalarDatatype(ConvertedType* type, int type_tag, size_t size_in_bytes, size_t alignment_in_bytes); - - size_t serialized_size() override { return (size_in_bytes + 3) / 4; }; - SpvId emit_deserialization(BasicBlockBuilder& bb, spv::StorageClass storage_class, SpvId array, SpvId base_offset) override; - void emit_serialization(BasicBlockBuilder& bb, spv::StorageClass storage_class, SpvId array, SpvId base_offset, SpvId data) override; -}; - -struct PtrDatatype : public Datatype { - static constexpr size_t bitwidth = 64; - PtrDatatype(ConvertedType* type) : Datatype(type) {} - - size_t serialized_size() override { return bitwidth / 32; }; - SpvId emit_deserialization(BasicBlockBuilder& bb, spv::StorageClass storage_class, SpvId array, SpvId base_offset) override; - void emit_serialization(BasicBlockBuilder& bb, spv::StorageClass storage_class, SpvId array, SpvId base_offset, SpvId data) override; -}; - -struct DefiniteArrayDatatype : public Datatype { - ConvertedType* element_type; - size_t length; - - DefiniteArrayDatatype(ConvertedType* type, ConvertedType* element_type, size_t length); - - size_t serialized_size() override { return element_type->datatype->serialized_size(); }; - SpvId emit_deserialization(BasicBlockBuilder& bb, spv::StorageClass storage_class, SpvId array, SpvId base_offset) override; - void emit_serialization(BasicBlockBuilder& bb, spv::StorageClass storage_class, SpvId array, SpvId base_offset, SpvId data) override; -}; - -struct ProductDatatype : public Datatype { - std::vector elements_types; - size_t total_size = 0; - - ProductDatatype(ConvertedType* type, const std::vector&& elements_types); - - size_t serialized_size() override { return total_size; }; - SpvId emit_deserialization(BasicBlockBuilder& bb, spv::StorageClass storage_class, SpvId array, SpvId base_offset) override; - void emit_serialization(BasicBlockBuilder& bb, spv::StorageClass storage_class, SpvId array, SpvId base_offset, SpvId data) override; -}; - -} - -#endif //THORIN_SPIRV_H diff --git a/src/thorin/be/spirv/spirv_datatypes.cpp b/src/thorin/be/spirv/spirv_datatypes.cpp deleted file mode 100644 index 7c11a56c4..000000000 --- a/src/thorin/be/spirv/spirv_datatypes.cpp +++ /dev/null @@ -1,339 +0,0 @@ -#include "thorin/be/spirv/spirv.h" -#include "thorin/util/stream.h" - -namespace thorin::spirv { - -ScalarDatatype::ScalarDatatype(ConvertedType* type, int type_tag, size_t size_in_bytes, size_t alignment_in_bytes) -: Datatype(type), type_tag(type_tag), size_in_bytes(size_in_bytes), alignment(alignment_in_bytes) {} - -/// All serialization/deserialization methods use this so into a macro it goes -#define serialization_types \ -SpvId u32_tid = type->code_gen->convert(type->code_gen->world().type_pu32())->type_id; \ -SpvId arr_cell_tid = bb.file_builder.declare_ptr_type(storage_class, u32_tid); - -SpvId ScalarDatatype::emit_deserialization(BasicBlockBuilder& bb, spv::StorageClass storage_class, SpvId array, SpvId base_offset) { - /// currently limited to 32-bit - assert(size_in_bytes == 4); - serialization_types; - auto cell = bb.access_chain(arr_cell_tid, array, { base_offset }); - auto loaded = bb.load(u32_tid, cell); - return bb.convert(spv::OpBitcast, type->type_id, loaded); -} - -void ScalarDatatype::emit_serialization(BasicBlockBuilder& bb, spv::StorageClass storage_class, SpvId array, SpvId base_offset, SpvId data) { - /// currently limited to 32-bit - assert(size_in_bytes == 4); - serialization_types; - auto cell = bb.access_chain(arr_cell_tid, array, { base_offset }); - auto casted = bb.convert(spv::OpBitcast, u32_tid, data); - bb.store(casted, cell); -} - -SpvId PtrDatatype::emit_deserialization(BasicBlockBuilder& bb, spv::StorageClass storage_class, SpvId array, SpvId base_offset) { - assert(type->src_type->as()->addr_space() == AddrSpace::Global && "Only buffer device address (global memory) pointers supported"); - serialization_types; - SpvId u64_tid = type->code_gen->convert(type->code_gen->world().type_pu64())->type_id; - - auto cell0 = bb.access_chain(arr_cell_tid, array, { base_offset }); - auto cell1 = bb.access_chain(arr_cell_tid, array, { bb.binop(spv::OpIAdd, u32_tid, base_offset, bb.file_builder.u32_constant(1)) }); - - auto lower = bb.convert(spv::OpUConvert, u64_tid, bb.load(u32_tid, cell0)); - auto upper = bb.convert(spv::OpUConvert, u64_tid, bb.load(u32_tid, cell1)); - - auto merged = bb.binop(spv::OpBitwiseOr, u64_tid, lower, bb.binop(spv::OpShiftLeftLogical, u64_tid, upper, bb.file_builder.u32_constant(32))); - - return bb.convert(spv::OpConvertUToPtr, type->type_id, merged); -} - -void PtrDatatype::emit_serialization(BasicBlockBuilder& bb, spv::StorageClass storage_class, SpvId array, SpvId base_offset, SpvId data) { - assert(type->src_type->as()->addr_space() == AddrSpace::Global && "Only buffer device address (global memory) pointers supported"); - serialization_types; - SpvId u64_tid = type->code_gen->convert(type->code_gen->world().type_pu64())->type_id; - - auto u64_ptr = bb.convert(spv::OpConvertPtrToU, u64_tid, data); - - auto cell0 = bb.access_chain(arr_cell_tid, array, { base_offset }); - auto cell1 = bb.access_chain(arr_cell_tid, array, { bb.binop(spv::OpIAdd, u32_tid, base_offset, bb.file_builder.u32_constant(1)) }); - - auto lower = bb.convert(spv::OpUConvert, u64_tid, u64_ptr); - auto upper = bb.convert(spv::OpUConvert, u64_tid, bb.binop(spv::OpShiftRightLogical, u64_tid, u64_ptr, bb.file_builder.u32_constant(32))); - - bb.store(lower, cell0); - bb.store(upper, cell1); -} - -DefiniteArrayDatatype::DefiniteArrayDatatype(ConvertedType* type, ConvertedType* element_type, size_t length) : Datatype(type), element_type(element_type), length(length) { - assert(element_type->datatype.get() != nullptr); - assert(length > 0 && "Array lengths of zero are not supported"); -} - -SpvId DefiniteArrayDatatype::emit_deserialization(BasicBlockBuilder& bb, spv::StorageClass storage_class, SpvId array, SpvId base_offset) { - SpvId u32_tid = type->code_gen->convert(type->code_gen->world().type_pu32())->type_id; - std::vector indices; - std::vector elements; - SpvId offset = base_offset; - SpvId stride = bb.file_builder.u32_constant(element_type->datatype->serialized_size()); - for (size_t i = 0; i < length; i++) { - SpvId element = element_type->datatype->emit_deserialization(bb, storage_class, array, offset); - elements.push_back(element); - offset = bb.binop(spv::OpIAdd, u32_tid, offset, stride); - } - return bb.composite(type->type_id, elements); -} -void DefiniteArrayDatatype::emit_serialization(BasicBlockBuilder& bb, spv::StorageClass storage_class, SpvId array, SpvId base_offset, SpvId data) { - SpvId u32_tid = type->code_gen->convert(type->code_gen->world().type_pu32())->type_id; - std::vector indices; - SpvId offset = base_offset; - SpvId stride = bb.file_builder.u32_constant(element_type->datatype->serialized_size()); - for (size_t i = 0; i < length; i++) { - element_type->datatype->emit_serialization(bb, storage_class, array, offset, bb.extract(element_type->type_id, data, { (uint32_t) i })); - offset = bb.binop(spv::OpIAdd, u32_tid, offset, stride); - } -} - -ProductDatatype::ProductDatatype(ConvertedType* type, const std::vector&& elements_types) : Datatype(type), elements_types(elements_types) { - // Unit datatype is acceptable, but serdes methods should never be invoked. - for (auto& element_type : elements_types) { - assert(element_type->datatype != nullptr); - total_size += element_type->datatype->serialized_size(); - } -} - -SpvId ProductDatatype::emit_deserialization(BasicBlockBuilder& bb, spv::StorageClass storage_class, SpvId array, SpvId base_offset) { - assert(total_size > 0 && "It doesn't make sense to de-serialize Unit!"); - SpvId u32_tid = type->code_gen->convert(type->code_gen->world().type_pu32())->type_id; - std::vector indices; - std::vector elements; - SpvId offset = base_offset; - for (auto& element_type : elements_types) { - SpvId element = element_type->datatype->emit_deserialization(bb, storage_class, array, offset); - offset = bb.binop(spv::OpIAdd, u32_tid, offset, bb.file_builder.u32_constant(element_type->datatype->serialized_size())); - elements.push_back(element); - } - return bb.composite(type->type_id, elements); -} -void ProductDatatype::emit_serialization(BasicBlockBuilder& bb, spv::StorageClass storage_class, SpvId array, SpvId base_offset, SpvId data) { - assert(total_size > 0 && "It doesn't make sense to serialize Unit!"); - SpvId u32_tid = type->code_gen->convert(type->code_gen->world().type_pu32())->type_id; - std::vector indices; - SpvId offset = base_offset; - int i = 0; - for (auto& element_type : elements_types) { - element_type->datatype->emit_serialization(bb, storage_class, array, offset, bb.extract(element_type->type_id, data, { (uint32_t) i++ })); - offset = bb.binop(spv::OpIAdd, u32_tid, offset, bb.file_builder.u32_constant(element_type->datatype->serialized_size())); - } -} - -ConvertedType* CodeGen::convert(const Type* type) { - // Spir-V requires each primitive type to be "unique", it doesn't allow for example two 32-bit signed integer types. - // Therefore we must enforce that precise/quick types map to the same thing. - switch (type->tag()) { -#define THORIN_Q_TYPE(T, M) \ - case PrimType_##T: \ - type = world().prim_type(PrimType_p##M, type->as()->length()); \ - break; -#include "thorin/tables/primtypetable.h" -#undef THORIN_Q_TYPE - default: break; - } - - if (auto iter = types_.find(type); iter != types_.end()) return iter->second.get(); - ConvertedType* converted = types_.emplace(type, std::make_unique(this) ).first->second.get(); - converted->src_type = type; - - if (auto vec = type->isa(); vec && vec->length() > 1) { - auto component = vec->scalarize(); - auto conv_comp = convert(component); - converted->type_id = builder_->declare_vector_type(conv_comp->type_id, (uint32_t)vec->length()); - return converted; - } - - switch (type->tag()) { - // Boolean types are typically packed intelligently when declaring in local variables, however with vanilla Vulkan 1.0 they can only be represented via 32-bit integers - // Using extensions, we could use 16 or 8-bit ints instead - // We can also pack them inside structures using bit-twiddling tricks, if the need arises - // Note: this only affects storing booleans inside structures, for regular variables the actual spir-v bool type is used. - case PrimType_bool: - converted->type_id = builder_->declare_bool_type(); - converted->datatype = std::make_unique(converted, type->tag(), 1, 1); - break; - case PrimType_ps8: assert(false && "TODO: look into capabilities to enable this"); - case PrimType_pu8: assert(false && "TODO: look into capabilities to enable this"); - case PrimType_ps16: - converted->type_id = builder_->declare_int_type(16, true); - converted->datatype = std::make_unique(converted, type->tag(), 2, 2); - break; - case PrimType_pu16: - converted->type_id = builder_->declare_int_type(16, false); - converted->datatype = std::make_unique(converted, type->tag(), 2, 2); - break; - case PrimType_ps32: - converted->type_id = builder_->declare_int_type(32, true ); - converted->datatype = std::make_unique(converted, type->tag(), 4, 4); - break; - case PrimType_pu32: - converted->type_id = builder_->declare_int_type(32, false); - converted->datatype = std::make_unique(converted, type->tag(), 4, 4); - break; - case PrimType_ps64: - converted->type_id = builder_->declare_int_type(64, true); - converted->datatype = std::make_unique(converted, type->tag(), 8, 8); - break; - case PrimType_pu64: - converted->type_id = builder_->declare_int_type(64, false); - converted->datatype = std::make_unique(converted, type->tag(), 8, 8); - break; - case PrimType_pf16: assert(false && "TODO: look into capabilities to enable this"); - case PrimType_pf32: - converted->type_id = builder_->declare_float_type(32); - converted->datatype = std::make_unique(converted, type->tag(), 4, 4); - break; - case PrimType_pf64: assert(false && "TODO: look into capabilities to enable this"); - case Node_PtrType: { - auto ptr = type->as(); - spv::StorageClass storage_class; - switch (ptr->addr_space()) { - case AddrSpace::Function: storage_class = spv::StorageClassFunction; break; - case AddrSpace::Private: storage_class = spv::StorageClassPrivate; break; - case AddrSpace::Push: storage_class = spv::StorageClassPushConstant; break; - case AddrSpace::Global: { - storage_class = spv::StorageClassPhysicalStorageBuffer; - converted->datatype = std::make_unique(converted); - break; - } - case AddrSpace::Generic: { - world().WLOG("Passing a generic pointer to a SPIR-V module. SpirV doesn't know about these, and so this will be passed as a 64 bit integer. Tread carefully !"); - ConvertedType* conv_u64 = convert(world().type_pu64()); - converted->type_id = conv_u64->type_id; - goto ptr_done; - } - default: - assert(false && "This address space is not supported"); - break; - } - { - const Type* pointee = ptr->pointee(); - while (auto arr = pointee->isa()) - pointee = arr->elem_type(); - ConvertedType* element = convert(pointee); - converted->type_id = builder_->declare_ptr_type(storage_class, element->type_id); - - if (ptr->addr_space() == AddrSpace::Global) { - assert(element->datatype && "Can only have physical pointers to known-size types"); - builder_->decorate(converted->type_id, spv::DecorationArrayStride, {(uint32_t) (element->datatype->serialized_size() * Datatype::base_element_bytes)}); - } - } - ptr_done: - break; - } - case Node_IndefiniteArrayType: { - world().ELOG("Using indefinite types directly is not permitted - they may only be pointed to"); - std::abort(); - } - case Node_DefiniteArrayType: { - auto array = type->as(); - ConvertedType* element = convert(array->elem_type()); - converted->type_id = builder_->declare_array_type(element->type_id, builder_->u32_constant(array->dim())); - converted->datatype = std::make_unique(converted, element, array->dim()); - break; - } - - case Node_ClosureType: - case Node_FnType: { - // extract "return" type, collect all other types - auto fn = type->as(); - ConvertedType* ret = nullptr; - std::vector ops; - for (auto op : fn->ops()) { - if (op->isa() || op == world().unit()) continue; - auto fn_type = op->isa(); - if (fn_type && !op->isa()) { - assert(!ret && "only one 'return' supported"); - std::vector ret_types; - for (auto fn_op : fn_type->ops()) { - if (fn_op->isa() || fn_op == world().unit()) continue; - ret_types.push_back(convert(fn_op)); - } - if (ret_types.empty()) ret = convert(world().tuple_type({})); - else if (ret_types.size() == 1) ret = ret_types.back(); - else assert(false && "Didn't we refactor this out yet by making functions single-argument ?"); - } else - ops.push_back(convert(op)->type_id); - } - assert(ret); - - if (type->tag() == Node_FnType) { - converted->type_id = builder_->declare_fn_type(ops, ret->type_id); - } else { - assert(false && "TODO: handle closure mess"); - THORIN_UNREACHABLE; - } - break; - } - - case Node_StructType: - case Node_TupleType: { - std::vector types; - std::vector spv_types; - size_t total_serialized_size = 0; - for (auto member_type : type->ops()) { - if (member_type == world().unit() || member_type == world().mem_type()) continue; - auto converted_member_type = convert(member_type); - types.push_back(converted_member_type); - spv_types.push_back(converted_member_type->type_id); - total_serialized_size = converted_member_type->datatype->serialized_size(); - } - if (total_serialized_size == 0) { - outf("this one is void"); - converted->type_id = builder_->void_type; - break; - } - - converted->type_id = builder_->declare_struct_type(spv_types); - builder_->name(converted->type_id, type->to_string()); - converted->datatype = std::make_unique(converted, std::move(types)); - break; - } - - case Node_VariantType: { - assert(type->num_ops() > 0 && "empty variants not supported"); - auto tag_type = world().type_pu32(); - ConvertedType* converted_tag_type = convert(tag_type); - - size_t max_serialized_size = 0; - for (auto member_type : type->as()->ops()) { - if (member_type == world().unit() || member_type == world().mem_type()) continue; - auto converted_member_type = convert(member_type); - if (converted_member_type->datatype->serialized_size() > max_serialized_size) - max_serialized_size = converted_member_type->datatype->serialized_size(); - } - - if (max_serialized_size > 0) { - auto payload_type = world().definite_array_type(world().type_pu32(), max_serialized_size); - auto* converted_payload_type = convert(payload_type); - - std::vector spv_pair = {converted_tag_type->type_id, converted_payload_type->type_id}; - converted->type_id = builder_->declare_struct_type(spv_pair); - converted->datatype = std::make_unique(converted, std::vector{ converted_tag_type, converted_payload_type }); - } else { - // We keep this useless level of struct so the rest of the code doesn't need a special path to extract the tag - std::vector spv_singleton = { converted_tag_type->type_id }; - converted->type_id = builder_->declare_struct_type(spv_singleton); - converted->datatype = std::make_unique(converted, std::vector{ converted_tag_type }); - } - builder_->name(converted->type_id, type->to_string()); - break; - } - - case Node_MemType: { - assert(false && "MemType cannot be converted to SPIR-V"); - } - - default: - THORIN_UNREACHABLE; - } - - return converted; -} - -} \ No newline at end of file diff --git a/src/thorin/config.h.in b/src/thorin/config.h.in index c594de907..44750da4a 100644 --- a/src/thorin/config.h.in +++ b/src/thorin/config.h.in @@ -5,6 +5,6 @@ #cmakedefine01 THORIN_ENABLE_PROFILING #cmakedefine01 THORIN_ENABLE_LLVM #cmakedefine01 THORIN_ENABLE_RV -#cmakedefine01 THORIN_ENABLE_SPIRV +#cmakedefine01 THORIN_ENABLE_SHADY #endif diff --git a/src/thorin/transform/structurize.cpp b/src/thorin/transform/structurize.cpp index ee72a64e9..230e26b80 100644 --- a/src/thorin/transform/structurize.cpp +++ b/src/thorin/transform/structurize.cpp @@ -5,7 +5,7 @@ #include "thorin/analyses/domtree.h" #include "thorin/world.h" -namespace thorin::spirv { +namespace thorin { using Head = LoopTree::Head; using Base = LoopTree::Base; diff --git a/src/thorin/transform/structurize.h b/src/thorin/transform/structurize.h index 6c6e23c70..0f82cf9ae 100644 --- a/src/thorin/transform/structurize.h +++ b/src/thorin/transform/structurize.h @@ -2,7 +2,11 @@ #include "thorin/analyses/scope.h" #include "thorin/analyses/cfg.h" +namespace thorin { + class World; void structure_loops(World& world); void structure_flow(World& world); + +} \ No newline at end of file From d37167051026dc326ff063737b07dd9b7d499fb7 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Fri, 13 May 2022 08:52:21 +0200 Subject: [PATCH 096/342] started reworking structuriser to use Loop intrinsic in shd --- src/thorin/continuation.cpp | 4 +- src/thorin/continuation.h | 18 +- src/thorin/transform/structurize.cpp | 256 ++++++++++++++------------- src/thorin/world.h | 4 + 4 files changed, 143 insertions(+), 139 deletions(-) diff --git a/src/thorin/continuation.cpp b/src/thorin/continuation.cpp index d86338edc..00ed20a0e 100644 --- a/src/thorin/continuation.cpp +++ b/src/thorin/continuation.cpp @@ -260,7 +260,7 @@ void Continuation::match(const Def* val, Continuation* otherwise, Defs patterns, verify(); } -void Continuation::structured_loop_merge(const Continuation* loop_header, ArrayRef targets) { +/*void Continuation::structured_loop_merge(const Continuation* loop_header, ArrayRef targets) { attributes_.intrinsic = Intrinsic::SCFLoopMerge; attributes_.scf_metadata.loop_epilogue.loop_header = loop_header; resize(targets.size()); @@ -283,7 +283,7 @@ void Continuation::structured_loop_header(const Continuation* loop_epilogue, con size_t x = 0; for (auto target : targets) set_op(x++, target); -} +}*/ void Continuation::verify() const { if (!has_body()) diff --git a/src/thorin/continuation.h b/src/thorin/continuation.h index 1a9e54d27..ed0ed113a 100644 --- a/src/thorin/continuation.h +++ b/src/thorin/continuation.h @@ -111,14 +111,10 @@ enum class Intrinsic : uint8_t { Pipeline, ///< Intrinsic loop-pipelining-HLS-Backend Branch, ///< branch(cond, T, F). Match, ///< match(val, otherwise, (case1, cont1), (case2, cont2), ...) - SCFBegin, - SCFLoopHeader = SCFBegin, ///< A header for a structured loop - SCFLoopMerge, ///< A merge block for a structured loop - SCFLoopContinue, ///< A continue block in a structured loop - SCFNonLocalJump, ///< A non-local jump in a structured control flow graph - SCFBackEdge, ///< A back edge a structured loop, - SCFEnd, - PeInfo = SCFEnd, ///< Partial evaluation debug info. + LoopBegin, ///< + LoopBreak, ///< + LoopContinue, ///< + PeInfo, ///< Partial evaluation debug info. EndScope ///< Dummy function which marks the end of a @p Scope. }; @@ -211,9 +207,9 @@ class Continuation : public Def { void jump(const Def* callee, Defs args, Debug dbg = {}); void branch(const Def* cond, const Def* t, const Def* f, Debug dbg = {}); void match(const Def* val, Continuation* otherwise, Defs patterns, ArrayRef continuations, Debug dbg = {}); - void structured_loop_merge(const Continuation* loop_header, ArrayRef targets); - void structured_loop_continue(const Continuation* loop_header); - void structured_loop_header(const Continuation* loop_epilogue, const Continuation* loop_continue, ArrayRef targets); + //void structured_loop_merge(const Continuation* loop_header, ArrayRef targets); + //void structured_loop_continue(const Continuation* loop_header); + //void structured_loop_header(const Continuation* loop_epilogue, const Continuation* loop_continue, ArrayRef targets); void verify() const; const Filter* filter() const { return op(1)->as(); } diff --git a/src/thorin/transform/structurize.cpp b/src/thorin/transform/structurize.cpp index 4ebd018a9..38ca453bd 100644 --- a/src/thorin/transform/structurize.cpp +++ b/src/thorin/transform/structurize.cpp @@ -26,9 +26,8 @@ struct DispatchTarget { }; struct RewireMe { - RewireMe(Continuation* cont, int op) : cont(cont), op(op) {} + explicit RewireMe(Continuation* cont) : cont(cont) {} Continuation* cont; - int op; Continuation* backedge = nullptr; struct { @@ -50,10 +49,11 @@ struct StructuredLoop { std::vector inner_destinations = {}; std::vector outer_destinations = {}; - // Created to serve as codegen helpers - Continuation* new_header = nullptr; - Continuation* new_epilogue = nullptr; - Continuation* new_continue = nullptr; + /// Just a regular continuation that simply calls into the real header + Continuation* pre_header; + /// Calls the loop_enter intrinsic with the set of internal and external dispatch nodes + Continuation* real_header; + Continuation* exit; // Same as the inner/outer destinations, but entry/exits instead now point to the corresponding header/epilogue nodes std::vector header_destination_conts; @@ -97,7 +97,6 @@ inline void tag_continuations(ScopeContext& ctx, const Base* base, const Head* p StructuredLoop loop(parent, head, std::move(name)); ctx.rewritten_loops.emplace(head, loop); - } else if(base->isa()) { for (auto& node : base->cf_nodes()) { auto[i, result] = ctx.def2loop.emplace(node->continuation(), parent); @@ -144,109 +143,106 @@ inline void collect_dispatch_targets(World& world, ScopeContext& ctx, const Base const Leaf* leaf = base->as(); auto cont = leaf->cf_node()->continuation(); // For some nonsense reason, synthetic nodes created during scopes iteration leak in next iterations >:( - if (cont->intrinsic() >= Intrinsic::SCFBegin && cont->intrinsic() < Intrinsic::SCFEnd) + if (!cont->has_body() /*|| (cont->intrinsic() >= Intrinsic::SCFBegin && cont->intrinsic() < Intrinsic::SCFEnd)*/) return; - for (size_t i = 0; i < cont->num_ops(); i++) { - auto def = cont->op(i); - if (auto dest = def->isa_nom()) { - if (dest->intrinsic() == Intrinsic::Branch || dest->is_imported()) { - continue; + auto app = cont->body(); + auto callee = app->callee()->isa_nom(); + if (!callee || callee->intrinsic() == Intrinsic::Branch || callee->is_imported()) + return; + + const Head* source_loop_head = ctx.def2loop[cont]; + assert(ctx.def2loop.find(callee) != ctx.def2loop.end()); + const Head* dest_loop_head = ctx.def2loop[callee]; + + if (source_loop_head != dest_loop_head) { + // We found a non-local jump + assert(ctx.rewritten_loops.find(source_loop_head) != ctx.rewritten_loops.end()); + auto& loop = ctx.rewritten_loops.find(source_loop_head)->second; + + std::vector source_path = get_path(ctx, source_loop_head); + std::vector dest_path = get_path(ctx, dest_loop_head); + int bi = 0; + while (bi < static_cast(std::min(source_path.size(), dest_path.size()))) { + if (source_path[bi] == dest_path[bi]) + bi++; + else break; + } + + // The path is made out of a sequence of loops to break out of, and a sequence of loops to jump into + // these two sequences cannot be both empty (that wouldn't be a non-local jump then!) + std::vector leave; + std::vector enter; + for (int j = static_cast(source_path.size()) - 1; j >= bi; j--) + leave.emplace_back(source_path[j]); + for (int j = bi; j < static_cast(dest_path.size()); j++) + enter.emplace_back(dest_path[j]); + + // 0 = this is the first step of the path + // 1 = last step was to break out of a loop + // 2 = last step was to enter a loop + int last = 0; + StructuredLoop* prev; + + auto record_step = [&](DispatchTarget destination) { + if (last == 0) { + // nothing to do, this node isn't a dispatching one + } else { + if (last == 1) + record_destination(prev->outer_destinations, destination); + else + record_destination(prev->inner_destinations, destination); } + }; + + for (auto dest_loop : leave) { + DispatchTarget destination; + destination.exit = dest_loop; - const Head* source_loop_head = ctx.def2loop[cont]; - assert(ctx.def2loop.find(dest) != ctx.def2loop.end()); - const Head* dest_loop_head = ctx.def2loop[dest]; + record_step(destination); + last = 1; + prev = dest_loop; + assert(prev != nullptr); + } + for (auto dest_loop : enter) { + DispatchTarget destination; + destination.entry = dest_loop; - if (source_loop_head != dest_loop_head) { - // We found a non-local jump + record_step(destination); + last = 2; + prev = dest_loop; + assert(prev != nullptr); + } + + assert(last != 0); + DispatchTarget destination; + destination.cont = callee; + record_step(destination); + + RewireMe rewire(cont); + rewire.non_local_jump = { + std::move(leave), + std::move(enter), + callee + }; + loop.rewire.emplace_back(rewire); + } else if (source_loop_head == dest_loop_head && source_loop_head != nullptr) { + for (auto& cf_node : source_loop_head->cf_nodes()) { + if (cf_node->continuation() == callee) { + // We found a backedge assert(ctx.rewritten_loops.find(source_loop_head) != ctx.rewritten_loops.end()); auto& loop = ctx.rewritten_loops.find(source_loop_head)->second; - std::vector source_path = get_path(ctx, source_loop_head); - std::vector dest_path = get_path(ctx, dest_loop_head); - int bi = 0; - while (bi < static_cast(std::min(source_path.size(), dest_path.size()))) { - if (source_path[bi] == dest_path[bi]) - bi++; - else break; - } - - // The path is made out of a sequence of loops to break out of, and a sequence of loops to jump into - // these two sequences cannot be both empty (that wouldn't be a non-local jump then!) - std::vector leave; - std::vector enter; - for (int j = static_cast(source_path.size()) - 1; j >= bi; j--) - leave.emplace_back(source_path[j]); - for (int j = bi; j < static_cast(dest_path.size()); j++) - enter.emplace_back(dest_path[j]); - - // 0 = this is the first step of the path - // 1 = last step was to break out of a loop - // 2 = last step was to enter a loop - int last = 0; - StructuredLoop* prev; - - auto record_step = [&](DispatchTarget destination) { - if (last == 0) { - // nothing to do, this node isn't a dispatching one - } else { - if (last == 1) - record_destination(prev->outer_destinations, destination); - else - record_destination(prev->inner_destinations, destination); - } - }; - - for (auto dest_loop : leave) { - DispatchTarget destination; - destination.exit = dest_loop; - - record_step(destination); - last = 1; - prev = dest_loop; - assert(prev != nullptr); - } - for (auto dest_loop : enter) { - DispatchTarget destination; - destination.entry = dest_loop; - - record_step(destination); - last = 2; - prev = dest_loop; - assert(prev != nullptr); - } - - assert(last != 0); DispatchTarget destination; - destination.cont = dest; - record_step(destination); - - RewireMe rewire(cont, i); - rewire.non_local_jump = { - std::move(leave), - std::move(enter), - dest - }; + destination.cont = callee; + record_destination(loop.inner_destinations, destination); + + RewireMe rewire(cont); + rewire.backedge = cf_node->continuation(); loop.rewire.emplace_back(rewire); - } else if (source_loop_head == dest_loop_head && source_loop_head != nullptr) { - for (auto& cf_node : source_loop_head->cf_nodes()) { - if (cf_node->continuation() == dest) { - // We found a backedge - assert(ctx.rewritten_loops.find(source_loop_head) != ctx.rewritten_loops.end()); - auto& loop = ctx.rewritten_loops.find(source_loop_head)->second; - - DispatchTarget destination; - destination.cont = dest; - record_destination(loop.inner_destinations, destination); - - RewireMe rewire(cont, i); - rewire.backedge = cf_node->continuation(); - loop.rewire.emplace_back(rewire); - break; - } - } + return; } } + assert(false); } } } @@ -284,8 +280,8 @@ inline void create_headers(World& world, ScopeContext& ctx, const Base* base) { if (target.cont != nullptr) { target_cont = target.cont; } else if (target.entry != nullptr) { - assert(target.entry->new_header != nullptr); - target_cont = target.entry->new_header; + assert(target.entry->pre_header != nullptr); + target_cont = target.entry->pre_header; } else { assert(false && "Header dispatches may not exit loops"); } @@ -296,9 +292,13 @@ inline void create_headers(World& world, ScopeContext& ctx, const Base* base) { auto variant_type = world.variant_type(loop.name + "_param", dest_types.size()); for (size_t i = 0; i < dest_types.size(); i++) variant_type->set(i, dest_types[i]); - auto fn_type = world.fn_type( { variant_type } ); - loop.new_header = world.continuation(fn_type, { loop.name + "_new_header"}); - loop.new_continue = world.continuation(fn_type, { loop.name + "_new_continue"}); + Types enter_intrinsic_types = { variant_type }; + + //loop.enter_intrinsic = world.loop_enter(enter_intrinsic_types); + //loop.continue_intrinsic = world.loop_continue(enter_intrinsic_types); + + loop.pre_header = world.continuation(world.fn_type(), { loop.name + "_new_header"}); + // loop.new_continue = world.continuation(fn_type, { loop.name + "_new_continue"}); } } @@ -314,12 +314,12 @@ inline void create_epilogues(World& world, ScopeContext& ctx, const Base* base) if (target.cont != nullptr) { target_cont = target.cont; } else if (target.entry != nullptr) { - assert(target.entry->new_header != nullptr); - target_cont = target.entry->new_header; + assert(target.entry->pre_header != nullptr); + target_cont = target.entry->pre_header; } else { assert(target.exit != nullptr); - assert(target.exit->new_epilogue != nullptr); - target_cont = target.exit->new_epilogue; + assert(target.exit->exit != nullptr); + target_cont = target.exit->exit; } loop.epilogue_destination_conts.push_back(target_cont); const thorin::FnType* target_type = target_cont->type(); @@ -328,8 +328,10 @@ inline void create_epilogues(World& world, ScopeContext& ctx, const Base* base) auto variant_type = world.variant_type(loop.name + "_param", dest_types.size()); for (size_t i = 0; i < dest_types.size(); i++) variant_type->set(i, dest_types[i]); - auto fn_type = world.fn_type({variant_type}); - loop.new_epilogue = world.continuation(fn_type, {loop.name + "_new_epilogue"}); + + //Types break_types = {variant_type}; + //loop.break_intrinsic = world.loop_break(break_types); + loop.exit = world.continuation(world.fn_type(), {loop.name + "_new_epilogue"}); } for (auto& children : head->children()) { @@ -345,9 +347,8 @@ inline void rewire_loops(World& world, ScopeContext& ctx, const Base* base) { auto& loop = ctx.rewritten_loops.find(head)->second; if (head->num_cf_nodes() > 0) { - loop.new_epilogue->structured_loop_merge(loop.new_header, loop.epilogue_destination_conts); - loop.new_continue->structured_loop_continue(loop.new_header); - loop.new_header->structured_loop_header(loop.new_epilogue, loop.new_continue, loop.header_destination_conts); + //loop.header->structured_loop_merge(loop.new_header, loop.epilogue_destination_conts); + //loop.exit->structured_loop_header(loop.new_epilogue, loop.new_continue, loop.header_destination_conts); } for (auto& children : head->children()) { @@ -365,20 +366,22 @@ inline void rewire_loops(World& world, ScopeContext& ctx, const Base* base) { auto old_fn_type = rewire.backedge->type(); auto wrapper = world.continuation(old_fn_type, {"synthetic_backedge_wrapper_to" + destination.cont->unique_name() }); ctx.def2loop[wrapper] = loop.head; - wrapper->attributes_.intrinsic = Intrinsic::SCFBackEdge; + //wrapper->attributes_.intrinsic = Intrinsic::SCFBackEdge; - auto header_variant_type = loop.new_header->type()->op(0)->as(); - wrapper->jump(loop.new_continue, { world.variant(header_variant_type, tuple_from_params(world, wrapper->params()), variant_index) }); + //TODO + //auto header_variant_type = loop.continue_intrinsic->type()->op(0)->as(); + //wrapper->jump(loop.continue_intrinsic, { world.variant(header_variant_type, tuple_from_params(world, wrapper->params()), variant_index) }); - rewire.cont->unset_op(rewire.op); - rewire.cont->set_op(rewire.op, wrapper); + auto old_app = rewire.cont->body(); + assert(old_app); + rewire.cont->jump(wrapper, old_app->args(), old_app->debug()); } else { auto& nlj = rewire.non_local_jump; auto old_fn_type = nlj.final_destination->type(); auto wrapper = world.continuation(old_fn_type, {"synthetic_nlj_wrapper_to" + nlj.final_destination->unique_name() }); ctx.def2loop[wrapper] = loop.head; - wrapper->attributes_.intrinsic = Intrinsic::SCFNonLocalJump; + // wrapper->attributes_.intrinsic = Intrinsic::SCFNonLocalJump; const Def* argument = tuple_from_params(world, wrapper->params()); Continuation* first_jump = nullptr; @@ -390,10 +393,10 @@ inline void rewire_loops(World& world, ScopeContext& ctx, const Base* base) { StructuredLoop* loop_to_enter = nlj.enters[i]; auto variant_index = index_of_destination(loop_to_enter->inner_destinations, destination); - auto header_variant_type = loop_to_enter->new_header->type()->op(0)->as(); + auto header_variant_type = loop_to_enter->pre_header->type()->op(0)->as(); argument = world.variant(header_variant_type, argument, variant_index); - first_jump = loop_to_enter->new_header; + first_jump = loop_to_enter->pre_header; destination = {}; destination.entry = loop_to_enter; } @@ -402,10 +405,10 @@ inline void rewire_loops(World& world, ScopeContext& ctx, const Base* base) { StructuredLoop* loop_to_exit = nlj.exits[i]; auto variant_index = index_of_destination(loop_to_exit->outer_destinations, destination); - auto header_variant_type = loop_to_exit->new_epilogue->type()->op(0)->as(); + auto header_variant_type = loop_to_exit->exit->type()->op(0)->as(); argument = world.variant(header_variant_type, argument, variant_index); - first_jump = loop_to_exit->new_epilogue; + first_jump = loop_to_exit->exit; destination = {}; destination.exit = loop_to_exit; } @@ -413,8 +416,9 @@ inline void rewire_loops(World& world, ScopeContext& ctx, const Base* base) { assert(first_jump != nullptr); wrapper->jump(first_jump, { argument }); - rewire.cont->unset_op(rewire.op); - rewire.cont->set_op(rewire.op, wrapper); + auto old_app = rewire.cont->body(); + assert(old_app); + rewire.cont->jump(wrapper, old_app->args(), old_app->debug()); } } } @@ -459,4 +463,4 @@ void structure_flow(World& world) { }); } -} \ No newline at end of file +} diff --git a/src/thorin/world.h b/src/thorin/world.h index bddecbd6c..32035bf40 100644 --- a/src/thorin/world.h +++ b/src/thorin/world.h @@ -245,6 +245,10 @@ class World : public TypeTable, public Streamable { Continuation* end_scope() const { return data_.end_scope_; } const Filter* filter(const Defs, Debug dbg = {}); + Continuation* loop_enter(Types types, Continuations, Continuations); + Continuation* loop_continue(Types types); + Continuation* loop_break(Types types); + /// Performs dead code, unreachable code and unused type elimination. void cleanup(); void opt(); From f5f34e544a736f6d8e186bdc064e95746b0b70e9 Mon Sep 17 00:00:00 2001 From: Matthias Kurtenacker Date: Tue, 8 Mar 2022 14:11:01 +0100 Subject: [PATCH 097/342] Add mem to branch instructions. Will break artic and impala as a result of added parameters to branch and match intrinsics. There are patches for those as well that need to be merged together with this change. --- src/thorin/be/c/c.cpp | 15 +++++++++------ src/thorin/be/llvm/llvm.cpp | 20 +++++++++++++------- src/thorin/continuation.cpp | 15 ++++++++------- src/thorin/continuation.h | 8 ++++---- src/thorin/world.cpp | 34 ++++++++++++++++++---------------- 5 files changed, 52 insertions(+), 40 deletions(-) diff --git a/src/thorin/be/c/c.cpp b/src/thorin/be/c/c.cpp index 56009879e..b0f0e2fc2 100644 --- a/src/thorin/be/c/c.cpp +++ b/src/thorin/be/c/c.cpp @@ -712,19 +712,22 @@ void CCodeGen::emit_epilogue(Continuation* cont) { break; } } else if (body->callee() == world().branch()) { - auto c = emit(body->arg(0)); - auto t = label_name(body->arg(1)); - auto f = label_name(body->arg(2)); + emit_unsafe(body->arg(0)); + auto c = emit(body->arg(1)); + auto t = label_name(body->arg(2)); + auto f = label_name(body->arg(3)); bb.tail.fmt("if ({}) goto {}; else goto {};", c, t, f); } else if (auto callee = body->callee()->as_nom(); callee && callee->intrinsic() == Intrinsic::Match) { - bb.tail.fmt("switch ({}) {{\t\n", emit(body->arg(0))); + emit_unsafe(body->arg(0)); - for (size_t i = 2; i < body->num_args(); i++) { + bb.tail.fmt("switch ({}) {{\t\n", emit(body->arg(1))); + + for (size_t i = 3; i < body->num_args(); i++) { auto arg = body->arg(i)->as(); bb.tail.fmt("case {}: goto {};\n", emit_constant(arg->op(0)), label_name(arg->op(1))); } - bb.tail.fmt("default: goto {};", label_name(body->arg(1))); + bb.tail.fmt("default: goto {};", label_name(body->arg(2))); bb.tail.fmt("\b\n}}"); } else if (body->callee()->isa()) { bb.tail.fmt("return; // bottom: unreachable"); diff --git a/src/thorin/be/llvm/llvm.cpp b/src/thorin/be/llvm/llvm.cpp index daa03569d..287cffce3 100644 --- a/src/thorin/be/llvm/llvm.cpp +++ b/src/thorin/be/llvm/llvm.cpp @@ -436,15 +436,21 @@ void CodeGen::emit_epilogue(Continuation* continuation) { irbuilder.CreateRet(agg); } } else if (body->callee() == world().branch()) { - auto cond = emit(body->arg(0)); - auto tbb = cont2bb(body->arg(1)->as_nom()); - auto fbb = cont2bb(body->arg(2)->as_nom()); + auto mem = body->arg(0); + emit_unsafe(mem); + + auto cond = emit(body->arg(1)); + auto tbb = cont2bb(body->arg(2)->as_nom()); + auto fbb = cont2bb(body->arg(3)->as_nom()); irbuilder.CreateCondBr(cond, tbb, fbb); } else if (body->callee()->isa() && body->callee()->as()->intrinsic() == Intrinsic::Match) { - auto val = emit(body->arg(0)); - auto otherwise_bb = cont2bb(body->arg(1)->as_nom()); - auto match = irbuilder.CreateSwitch(val, otherwise_bb, body->num_args() - 2); - for (size_t i = 2; i < body->num_args(); i++) { + auto mem = body->arg(0); + emit_unsafe(mem); + + auto val = emit(body->arg(1)); + auto otherwise_bb = cont2bb(body->arg(2)->as_nom()); + auto match = irbuilder.CreateSwitch(val, otherwise_bb, body->num_args() - 3); + for (size_t i = 3; i < body->num_args(); i++) { auto arg = body->arg(i)->as(); auto case_const = llvm::cast(emit(arg->op(0))); auto case_bb = cont2bb(arg->op(1)->as_nom()); diff --git a/src/thorin/continuation.cpp b/src/thorin/continuation.cpp index 719bf3477..04ec649ea 100644 --- a/src/thorin/continuation.cpp +++ b/src/thorin/continuation.cpp @@ -241,19 +241,20 @@ void Continuation::jump(const Def* callee, Defs args, Debug dbg) { verify(); } -void Continuation::branch(const Def* cond, const Def* t, const Def* f, Debug dbg) { - set_body(world().app(world().branch(), {cond, t, f}, dbg)); +void Continuation::branch(const Def* mem, const Def* cond, const Def* t, const Def* f, Debug dbg) { + set_body(world().app(world().branch(), {mem, cond, t, f}, dbg)); verify(); } -void Continuation::match(const Def* val, Continuation* otherwise, Defs patterns, ArrayRef continuations, Debug dbg) { - Array args(patterns.size() + 2); +void Continuation::match(const Def* mem, const Def* val, Continuation* otherwise, Defs patterns, ArrayRef continuations, Debug dbg) { + Array args(patterns.size() + 3); - args[0] = val; - args[1] = otherwise; + args[0] = mem; + args[1] = val; + args[2] = otherwise; assert(patterns.size() == continuations.size()); for (size_t i = 0; i < patterns.size(); i++) - args[i + 2] = world().tuple({patterns[i], continuations[i]}, dbg); + args[i + 3] = world().tuple({patterns[i], continuations[i]}, dbg); set_body(world().app(world().match(val->type(), patterns.size()), args, dbg)); verify(); diff --git a/src/thorin/continuation.h b/src/thorin/continuation.h index 9f34fe992..02f203a03 100644 --- a/src/thorin/continuation.h +++ b/src/thorin/continuation.h @@ -108,8 +108,8 @@ enum class Intrinsic : uint8_t { Undef, ///< Intrinsic undef function PipelineContinue, ///< Intrinsic loop-pipelining-HLS-Backend Pipeline, ///< Intrinsic loop-pipelining-HLS-Backend - Branch, ///< branch(cond, T, F). - Match, ///< match(val, otherwise, (case1, cont1), (case2, cont2), ...) + Branch, ///< branch(mem, cond, T, F). + Match, ///< match(mem, val, otherwise, (case1, cont1), (case2, cont2), ...) PeInfo, ///< Partial evaluation debug info. EndScope ///< Dummy function which marks the end of a @p Scope. }; @@ -186,8 +186,8 @@ class Continuation : public Def { void destroy(const char*); void jump(const Def* callee, Defs args, Debug dbg = {}); - void branch(const Def* cond, const Def* t, const Def* f, Debug dbg = {}); - void match(const Def* val, Continuation* otherwise, Defs patterns, ArrayRef continuations, Debug dbg = {}); + void branch(const Def* mem, const Def* cond, const Def* t, const Def* f, Debug dbg = {}); + void match(const Def* mem, const Def* val, Continuation* otherwise, Defs patterns, ArrayRef continuations, Debug dbg = {}); void verify() const; const Filter* filter() const { return op(1)->as(); } diff --git a/src/thorin/world.cpp b/src/thorin/world.cpp index 47f34b6aa..eb05a4ff4 100644 --- a/src/thorin/world.cpp +++ b/src/thorin/world.cpp @@ -44,7 +44,7 @@ namespace thorin { World::World(const std::string& name) { data_.name_ = name; - data_.branch_ = continuation(fn_type({type_bool(), fn_type(), fn_type()}), Intrinsic::Branch, {"br"}); + data_.branch_ = continuation(fn_type({mem_type(), type_bool(), fn_type(), fn_type()}), Intrinsic::Branch, {"br"}); data_.end_scope_ = continuation(fn_type(), Intrinsic::EndScope, {"end_scope"}); } @@ -1114,11 +1114,13 @@ Continuation* World::continuation(const FnType* fn, Continuation::Attributes att } Continuation* World::match(const Type* type, size_t num_patterns) { - Array arg_types(num_patterns + 2); - arg_types[0] = type; - arg_types[1] = fn_type(); - for (size_t i = 0; i < num_patterns; i++) - arg_types[i + 2] = tuple_type({type, fn_type()}); + Array arg_types(num_patterns + 3); + arg_types[0] = mem_type(); + arg_types[1] = type; + arg_types[2] = fn_type({mem_type()}); + for (size_t i = 0; i < num_patterns; i++) { + arg_types[i + 3] = tuple_type({type, fn_type({mem_type()})}); + } return continuation(fn_type(arg_types), Intrinsic::Match, {"match"}); } @@ -1139,26 +1141,26 @@ const App* World::app(const Def* callee, const Defs args, Debug dbg) { if (auto continuation = callee->isa()) { switch (continuation->intrinsic()) { case Intrinsic::Branch: { - assert(args.size() == 3); - auto cond = args[0], t = args[1], f = args[2]; + assert(args.size() == 4); + auto mem = args[0], cond = args[1], t = args[2], f = args[3]; if (auto lit = cond->isa()) - return app(lit->value().get_bool() ? t : f, {}, dbg); + return app(lit->value().get_bool() ? t : f, { mem }, dbg); if (t == f) - return app(t, {}, dbg); + return app(t, { mem }, dbg); if (is_not(cond)) { auto inverted = cond->as()->rhs(); - return app(branch(), {inverted, f, t}, dbg); + return app(branch(), {mem, inverted, f, t}, dbg); } break; } case Intrinsic::Match: - if (args.size() == 2) return app(args[1], {}, dbg); - if (auto lit = args[0]->isa()) { - for (size_t i = 2; i < args.size(); i++) { + if (args.size() == 3) return app(args[2], { args[0] }, dbg); + if (auto lit = args[1]->isa()) { + for (size_t i = 3; i < args.size(); i++) { if (extract(args[i], 0_s)->as() == lit) - return app(extract(args[i], 1), {}, dbg); + return app(extract(args[i], 1), { args[0] }, dbg); } - return app(args[1], {}, dbg); + return app(args[2], { args[0] }, dbg); } break; default: From bd1ffe341daab334c5d9189c6dbcd91eeda80800 Mon Sep 17 00:00:00 2001 From: Matthias Kurtenacker Date: Tue, 14 Jun 2022 14:27:02 +0200 Subject: [PATCH 098/342] Fix branch continuation type. --- 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 eb05a4ff4..605a924ca 100644 --- a/src/thorin/world.cpp +++ b/src/thorin/world.cpp @@ -44,7 +44,7 @@ namespace thorin { World::World(const std::string& name) { data_.name_ = name; - data_.branch_ = continuation(fn_type({mem_type(), type_bool(), fn_type(), fn_type()}), Intrinsic::Branch, {"br"}); + data_.branch_ = continuation(fn_type({mem_type(), type_bool(), fn_type({mem_type()}), fn_type({mem_type()})}), Intrinsic::Branch, {"br"}); data_.end_scope_ = continuation(fn_type(), Intrinsic::EndScope, {"end_scope"}); } From c90a46e870e526445e29ff54979833c10c09ad71 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Thu, 25 Aug 2022 13:58:14 +0200 Subject: [PATCH 099/342] follow changes in shady's cmake target --- CMakeLists.txt | 2 -- src/thorin/CMakeLists.txt | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a6452c04c..c47d74578 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -47,8 +47,6 @@ endif() find_package(shady REQUIRED CONFIG) if (shady_FOUND) message(STATUS "Found shady at ${shady_DIR}") - message(STATUS "Found shady headers ${shady_INCLUDE_DIRS}") - include_directories(${shady_INCLUDE_DIRS}) set(THORIN_ENABLE_SHADY TRUE) endif() diff --git a/src/thorin/CMakeLists.txt b/src/thorin/CMakeLists.txt index ae04e530d..de8280c81 100644 --- a/src/thorin/CMakeLists.txt +++ b/src/thorin/CMakeLists.txt @@ -131,5 +131,5 @@ if(LLVM_FOUND) endif() if (shady_FOUND) - target_link_libraries(thorin PRIVATE shady) + target_link_libraries(thorin PRIVATE shady::shady) endif() From 694644707c1182528d1e4fd14b1cf48d028f068d Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Thu, 25 Aug 2022 22:34:10 +0200 Subject: [PATCH 100/342] shady backend: convert types --- src/thorin/be/shady/shady.cpp | 67 +++++++++++++++++++++++++++++++++-- src/thorin/be/shady/shady.h | 14 +++++--- src/thorin/type.h | 2 -- 3 files changed, 73 insertions(+), 10 deletions(-) diff --git a/src/thorin/be/shady/shady.cpp b/src/thorin/be/shady/shady.cpp index 814caae36..b74544527 100644 --- a/src/thorin/be/shady/shady.cpp +++ b/src/thorin/be/shady/shady.cpp @@ -3,7 +3,7 @@ #include "thorin/analyses/scope.h" #include "thorin/transform/structurize.h" -namespace thorin::shady { +namespace thorin::shady_be { CodeGen::CodeGen(thorin::World& world, Cont2Config& kernel_config, bool debug) : thorin::CodeGen(world, debug), kernel_config_(kernel_config) @@ -47,8 +47,69 @@ void CodeGen::emit(const thorin::Scope& scope) { assert(false && "TODO"); } -shady::Type* CodeGen::convert(const Type *) { - return nullptr; +shady::AddressSpace CodeGen::convert_address_space(AddrSpace as) { + switch(as) { + case AddrSpace::Generic: assert(false); break; + case AddrSpace::Global: return shady::AsGlobalPhysical; + case AddrSpace::Texture: assert(false); break; + case AddrSpace::Shared: return shady::AsSharedPhysical; + case AddrSpace::Constant: assert(false); break; + case AddrSpace::Private: return shady::AsPrivatePhysical; + } + assert(false); +} + +const shady::Type* CodeGen::convert(const Type* type) { + if (auto res = types_.lookup(type)) return *res; + const shady::Type* t; + if (auto prim = type->isa()) { + switch (prim->primtype_tag()) { + case PrimType_bool: t = shady::bool_type(arena); break; + case PrimType_ps8: case PrimType_qs8: + case PrimType_pu8: case PrimType_qu8: t = shady::int8_type(arena); break; + case PrimType_ps16: case PrimType_qs16: + case PrimType_pu16: case PrimType_qu16: t = shady::int16_type(arena); break; + case PrimType_ps32: case PrimType_qs32: + case PrimType_pu32: case PrimType_qu32: t = shady::int32_type(arena); break; + case PrimType_ps64: case PrimType_qs64: + case PrimType_pu64: case PrimType_qu64: t = shady::int64_type(arena); break; + case PrimType_pf16: case PrimType_qf16: assert(false && "TODO"); + case PrimType_pf32: case PrimType_qf32: t = shady::float_type(arena); break; + case PrimType_pf64: case PrimType_qf64: assert(false && "TODO"); + default: THORIN_UNREACHABLE; + } + } else if (auto ptr = type->isa()) { + t = shady::ptr_type(arena, (shady::PtrType) { + convert_address_space(ptr->addr_space()), + convert(ptr->pointee()) + }); + } else if (auto arr = type->isa()) { + shady::ArrType payload = {}; + payload.element_type = convert(arr->elem_type()); + payload.size = nullptr; + if (auto definite = arr->isa()) { + payload.size = shady::int32_literal(arena, static_cast(definite->dim())); + } + t = shady::arr_type(arena, payload); + } else if (auto strct = type->isa()) { + auto members = std::vector(strct->num_ops()); + for (size_t i = 0; i < strct->num_ops(); i++) { + members[i] = convert(strct->op(i)); + } + shady::RecordType payload = {}; + payload.members = shady::nodes(arena, strct->num_ops(), members.data()); + payload.names = shady::strings(arena, 0, nullptr); + payload.special = shady::RecordType::NotSpecial; + t = shady::record_type(arena, payload); + } else if (auto variant = type->isa()) { + assert(false && "TODO"); + } else { + assert(false); + } + + assert(t); + types_[type] = t; + return t; } } diff --git a/src/thorin/be/shady/shady.h b/src/thorin/be/shady/shady.h index df4ca0d77..848f43a3a 100644 --- a/src/thorin/be/shady/shady.h +++ b/src/thorin/be/shady/shady.h @@ -1,10 +1,12 @@ #include "thorin/be/codegen.h" -namespace thorin::shady { - +namespace shady { extern "C" { #include } +} + +namespace thorin::shady_be { class CodeGen : public thorin::CodeGen { public: @@ -13,8 +15,10 @@ class CodeGen : public thorin::CodeGen { void emit_stream(std::ostream& stream) override; const char* file_ext() const override { return ".shady"; } - shady::Type* convert(const Type*); + const shady::Type* convert(const Type*); protected: + shady::AddressSpace convert_address_space(AddrSpace); + void emit(const Scope& scope); //void emit_epilogue(Continuation*, BasicBlockBuilder* bb); //shady::Node* emit(const Def* def, BasicBlockBuilder* bb); @@ -25,8 +29,8 @@ class CodeGen : public thorin::CodeGen { std::vector> top_level; Continuation* entry_ = nullptr; - TypeMap> types_; - DefMap defs_; + TypeMap types_; + DefMap defs_; const Cont2Config& kernel_config_; }; diff --git a/src/thorin/type.h b/src/thorin/type.h index cb93682c6..5b96929bc 100644 --- a/src/thorin/type.h +++ b/src/thorin/type.h @@ -236,8 +236,6 @@ enum class AddrSpace : uint32_t { Shared = 3, Constant = 4, Private = 5, // Corresponds to the 'private' storage class in SPIR-V - Function = 6, // Corresponds to the 'function' storage class in SPIR-V - Push = 7, // Corresponds to the 'push constant' storage class in SPIR-V }; /// Pointer type. From 03b25a3d0af0d4322d395d02320644ffbfa88562 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Fri, 26 Aug 2022 14:03:18 +0200 Subject: [PATCH 101/342] shady: can codegen an empty hello world --- src/thorin/be/codegen.cpp | 6 +- src/thorin/be/codegen.h | 2 +- src/thorin/be/shady/shady.cpp | 204 +++++++++++++++++++++++++++++++--- src/thorin/be/shady/shady.h | 49 ++++++-- src/thorin/continuation.cpp | 2 +- 5 files changed, 231 insertions(+), 32 deletions(-) diff --git a/src/thorin/be/codegen.cpp b/src/thorin/be/codegen.cpp index 87edaff30..517cb0232 100644 --- a/src/thorin/be/codegen.cpp +++ b/src/thorin/be/codegen.cpp @@ -95,7 +95,7 @@ DeviceBackends::DeviceBackends(World& world, int opt, bool debug, std::string& f std::pair { OpenCL, Intrinsic::OpenCL }, std::pair { AMDGPU, Intrinsic::AMDGPU }, std::pair { HLS, Intrinsic::HLS }, - std::pair { SpirV, Intrinsic::SpirV } + std::pair { Shady, Intrinsic::SpirV } }; for (auto [backend, intrinsic] : backend_intrinsics) { if (is_passed_to_intrinsic(continuation, intrinsic)) { @@ -116,7 +116,7 @@ DeviceBackends::DeviceBackends(World& world, int opt, bool debug, std::string& f kernels.emplace_back(continuation); }); - for (auto backend : std::array { CUDA, NVVM, OpenCL, AMDGPU, SpirV }) { + for (auto backend : std::array { CUDA, NVVM, OpenCL, AMDGPU, Shady }) { if (!importers_[backend].world().empty()) { get_kernel_configs(importers_[backend], kernels, kernel_config, [&](Continuation *use, Continuation * /* imported */) { auto app = use->body(); @@ -195,7 +195,7 @@ DeviceBackends::DeviceBackends(World& world, int opt, bool debug, std::string& f (void)opt; #endif #if THORIN_ENABLE_SHADY - if (!importers_[SpirV].world().empty()) cgs[SpirV] = std::make_unique(importers_[SpirV].world(), kernel_config, debug); + if (!importers_[Shady].world().empty()) cgs[Shady] = std::make_unique(importers_[Shady].world(), kernel_config, debug); #endif for (auto [backend, lang] : std::array { std::pair { CUDA, c::Lang::CUDA }, std::pair { OpenCL, c::Lang::OpenCL }, std::pair { HLS, c::Lang::HLS } }) if (!importers_[backend].world().empty()) cgs[backend] = std::make_unique(importers_[backend].world(), kernel_config, lang, debug, flags); diff --git a/src/thorin/be/codegen.h b/src/thorin/be/codegen.h index b7477ef2f..8123e29a3 100644 --- a/src/thorin/be/codegen.h +++ b/src/thorin/be/codegen.h @@ -44,7 +44,7 @@ struct DeviceBackends { Cont2Config kernel_config; std::vector kernels; - enum { CUDA, NVVM, OpenCL, AMDGPU, HLS, SpirV, BackendCount }; + enum { CUDA, NVVM, OpenCL, AMDGPU, HLS, Shady, BackendCount }; std::array, BackendCount> cgs; private: std::vector importers_; diff --git a/src/thorin/be/shady/shady.cpp b/src/thorin/be/shady/shady.cpp index b74544527..f60ae0ed8 100644 --- a/src/thorin/be/shady/shady.cpp +++ b/src/thorin/be/shady/shady.cpp @@ -20,33 +20,24 @@ void CodeGen::emit_stream(std::ostream& out) { }; arena = shady::new_arena(config); - Scope::for_each(world(), [&](const Scope& scope) { emit(scope); }); + Scope::for_each(world(), [&](const Scope& scope) { emit_scope(scope); }); // build root node with the top level stuff that got emitted - auto decls = std::vector(top_level.size(), nullptr); - for (size_t i = 0; i < top_level.size(); i++) { - decls[i] = top_level[i].first; - } auto root = shady::root(arena, (shady::Root) { - .declarations = shady::nodes(arena, top_level.size(), const_cast(decls.data())), + .declarations = shady::nodes(arena, top_level.size(), const_cast(top_level.data())), }); - shady::print_node(root); - - out << "todo"; + char* bufptr; + size_t size; + shady::print_node_into_str(root, &bufptr, &size); + out.write(bufptr, static_cast(size)); + free(bufptr); shady::destroy_arena(arena); arena = nullptr; top_level.clear(); } -void CodeGen::emit(const thorin::Scope& scope) { - entry_ = scope.entry(); - assert(entry_->is_returning()); - - assert(false && "TODO"); -} - shady::AddressSpace CodeGen::convert_address_space(AddrSpace as) { switch(as) { case AddrSpace::Generic: assert(false); break; @@ -59,10 +50,22 @@ shady::AddressSpace CodeGen::convert_address_space(AddrSpace as) { assert(false); } +static inline int find_return_parameter(const FnType* type) { + for (size_t i = 0; i < type->num_ops(); i++) { + auto t = type->op(i); + if (t->order() % 2 == 1) + return static_cast(i); + } + return -1; +} + const shady::Type* CodeGen::convert(const Type* type) { if (auto res = types_.lookup(type)) return *res; const shady::Type* t; - if (auto prim = type->isa()) { + if (type == world().mem_type()) { + t = nullptr; + goto skip_check; + } else if (auto prim = type->isa()) { switch (prim->primtype_tag()) { case PrimType_bool: t = shady::bool_type(arena); break; case PrimType_ps8: case PrimType_qs8: @@ -103,13 +106,180 @@ const shady::Type* CodeGen::convert(const Type* type) { t = shady::record_type(arena, payload); } else if (auto variant = type->isa()) { assert(false && "TODO"); + } else if (auto fn_type = type->isa()) { + shady::FnType payload = {}; + payload.is_basic_block = fn_type->is_basicblock(); + NodeVec dom, codom; + + int return_param_i = find_return_parameter(fn_type); + for (size_t i = 0; i < fn_type->num_ops(); i++) { + // Skip the return param + if (return_param_i != -1 && i == static_cast(return_param_i)) continue; + auto converted = convert(fn_type->op(i)); + if (!converted) + continue; // Eliminate mem params + dom.push_back(converted); + } + + if (return_param_i != -1) { + auto ret_fn_type = fn_type->op(return_param_i); + for (size_t i = 0; i < ret_fn_type->num_ops(); i++) { + auto converted = convert(ret_fn_type->op(i)); + if (!converted) + continue; // Eliminate mem params + codom.push_back(converted); + } + } + + if (fn_type->is_basicblock()) + assert(codom.empty()); + payload.param_types = vec2nodes(dom); + payload.return_types = vec2nodes(codom); + t = shady::fn_type(arena, payload); } else { assert(false); } assert(t); + skip_check: types_[type] = t; return t; } +shady::Node* CodeGen::def_to_decl(Def* def) { + NodeVec annotations; + if (auto cont = def->isa_nom()) { + NodeVec params; + NodeVec returns; + + int ret_param_i = find_return_parameter(cont->type()); + + for (size_t i = 0; i < cont->num_params(); i++) { + if (ret_param_i != -1 && i == static_cast(ret_param_i)) + continue; // Skip the return parameter + auto type = convert(cont->param(i)->type()); + if (!type) continue; // Eliminate mem tokens + auto param = shady::var(arena, type, cont->param(i)->name().c_str()); + defs_[cont->param(i)] = param; // Register the param as emitted already + params.push_back(param); + } + + if (!cont->is_basicblock() && ret_param_i >= 0) { + auto ret_fn_type = cont->type()->op(ret_param_i); + + for (auto t : ret_fn_type->ops()) { + auto ret_type = convert(t); + if (!ret_type) + continue; // Eliminate mem types + returns.push_back(ret_type); + } + } + + return shady::fn(arena, vec2nodes(annotations), def->unique_name().c_str(), cont->is_basicblock(), vec2nodes(params), vec2nodes(returns)); + } else if (auto global = def->isa()) { + if (global->is_mutable()) { + return shady::global_var(arena, vec2nodes(annotations), convert(global->alloced_type()), global->unique_name().c_str(), convert_address_space(AddrSpace::Private)); + } else { + // Tentatively make those things constants... + auto constant = shady::constant(arena, vec2nodes(annotations), global->unique_name().c_str());; + constant->payload.constant.type_hint = convert(global->alloced_type()); + return constant; + } + } else { + assert(false && "This doesn't map to a decl !"); + } +} + +shady::Node* CodeGen::get_decl(Def* def) { + for (auto& e : top_level) { + if (shady::get_decl_name(e) == def->unique_name()) + return e; + } + + auto decl = def_to_decl(def); + top_level.push_back(decl); + return decl; +} + +shady::Node* CodeGen::prepare(const Scope& scope) { + cont2bb_[scope.entry()].head = curr_fn = get_decl(scope.entry()); +} + +void CodeGen::prepare(Continuation* cont, shady::Node*) { + BB& bb = cont2bb_[cont]; + if (cont->is_basicblock()) + bb.head = emit_fun_decl(cont); + else + assert(bb.head); + + // Register params + // for (size_t i = 0; i < cont->num_params(); i++) + // defs_[cont->param(i)] = bb.head->payload.fn.params.nodes[i]; + + bb.builder = shady::begin_block(arena); +} + +void CodeGen::emit_epilogue(Continuation* cont) { + BB& bb = cont2bb_[cont]; + assert(cont->has_body()); + auto body = cont->body(); + NodeVec args; + for (auto& arg : body->args()) { + if (convert(arg->type()) == nullptr) continue; + args.push_back(emit(arg)); + } + + if (body->callee() == entry_->ret_param()) { + shady::Return payload = {}; + payload.fn = curr_fn; + payload.values = vec2nodes(args); + bb.terminator = shady::fn_ret(arena, payload); + } else if (body->callee() == world().branch()) { + shady::Branch payload = {}; + payload.branch_mode = shady::Branch::BrIfElse; + payload.args = shady::nodes(arena, 0, nullptr); + payload.branch_condition = args[0]; + payload.true_target = args[1]; + payload.false_target = args[2]; + bb.terminator = shady::branch(arena, payload); + } else if (auto match = body->callee()->as_nom(); match && match->intrinsic() == Intrinsic::Match) { + assert(false); + } else if (auto destination = body->callee()->isa_nom(); destination && destination->is_basicblock()) { + shady::Branch payload = {}; + payload.args = vec2nodes(args); + payload.branch_mode = shady::Branch_::BrJump; + bb.terminator = shady::branch(arena, payload); + } else if (auto intrinsic = body->callee()->isa_nom(); intrinsic && intrinsic->is_intrinsic()) { + assert(false); + } else if (auto callee = body->callee()->isa_nom()) { + shady::Callc payload = {}; + int ret_param = find_return_parameter(callee->type()); + payload.ret_cont = args[ret_param]; + args.erase(args.begin() + ret_param); + payload.args = vec2nodes(args); + payload.is_return_indirect = false; + payload.callee = emit(callee); + bb.terminator = shady::callc(arena, payload); + } else { + assert(false); + } +} + +void CodeGen::finalize(Continuation* cont) { + BB& bb = cont2bb_[cont]; + assert(bb.head && bb.builder && bb.terminator); + bb.block = shady::finish_block(bb.builder, bb.terminator); + bb.head->payload.fn.block = bb.block; +} + +void CodeGen::finalize(const Scope& scope) { + BB& bb = cont2bb_[scope.entry()]; + assert(bb.head->payload.fn.block != nullptr); + curr_fn = nullptr; +} + +const shady::Node* CodeGen::emit_bb(BB& bb, const Def* def) { + assert("TODO"); +} + } diff --git a/src/thorin/be/shady/shady.h b/src/thorin/be/shady/shady.h index 848f43a3a..ece795be4 100644 --- a/src/thorin/be/shady/shady.h +++ b/src/thorin/be/shady/shady.h @@ -1,4 +1,6 @@ #include "thorin/be/codegen.h" +#include "thorin/analyses/schedule.h" +#include "thorin/be/emitter.h" namespace shady { extern "C" { @@ -8,29 +10,56 @@ extern "C" { namespace thorin::shady_be { -class CodeGen : public thorin::CodeGen { +// using BB = std::pair; + +struct BB { + /// For an entry BB, this will also be the head of the entire function + shady::Node* head; + shady::BlockBuilder* builder; + const shady::Node* terminator; + const shady::Node* block; +}; + +class CodeGen : public thorin::CodeGen, public thorin::Emitter { public: CodeGen(World&, Cont2Config&, bool debug); void emit_stream(std::ostream& stream) override; const char* file_ext() const override { return ".shady"; } + shady::Node* prepare(const Scope&); + void prepare(Continuation*, shady::Node*); + void emit_epilogue(Continuation*); + const shady::Node* emit_(const Def* def); + void finalize(const Scope&); + void finalize(Continuation*); + const shady::Type* convert(const Type*); + const shady::Node* emit_bb(BB&, const Def*); + + bool is_valid(const shady::Node* n) { + return n; + } + + shady::Node* emit_fun_decl(Def* def) { + return get_decl(def); + } protected: shady::AddressSpace convert_address_space(AddrSpace); + shady::Node* def_to_decl(Def*); + shady::Node* get_decl(Def*); - void emit(const Scope& scope); - //void emit_epilogue(Continuation*, BasicBlockBuilder* bb); - //shady::Node* emit(const Def* def, BasicBlockBuilder* bb); - //std::vector emit_builtin(const Continuation*, const Continuation*, BasicBlockBuilder*); + using NodeVec = std::vector; + + inline shady::Nodes vec2nodes(NodeVec& vec) { + return shady::nodes(arena, vec.size(), vec.data()); + } - //SpvId get_codom_type(const Continuation* fn); shady::IrArena* arena = nullptr; - std::vector> top_level; + std::vector top_level; + + shady::Node* curr_fn; - Continuation* entry_ = nullptr; - TypeMap types_; - DefMap defs_; const Cont2Config& kernel_config_; }; diff --git a/src/thorin/continuation.cpp b/src/thorin/continuation.cpp index 1fa4a85c4..e6429b480 100644 --- a/src/thorin/continuation.cpp +++ b/src/thorin/continuation.cpp @@ -215,7 +215,7 @@ void Continuation::set_intrinsic() { else if (name() == "nvvm") attributes().intrinsic = Intrinsic::NVVM; else if (name() == "opencl") attributes().intrinsic = Intrinsic::OpenCL; else if (name() == "amdgpu") attributes().intrinsic = Intrinsic::AMDGPU; - else if (name() == "spirv") attributes().intrinsic = Intrinsic::SpirV; + else if (name() == "spirv") attributes().intrinsic = Intrinsic::SpirV; else if (name() == "hls") attributes().intrinsic = Intrinsic::HLS; else if (name() == "parallel") attributes().intrinsic = Intrinsic::Parallel; else if (name() == "fibers") attributes().intrinsic = Intrinsic::Fibers; From 8dcfdee2c0c54dcde7093890fc30a48526ebe02d Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Fri, 26 Aug 2022 17:58:57 +0200 Subject: [PATCH 102/342] can emit a few literals --- src/thorin/be/shady/shady.cpp | 66 ++++++++++++++++++++++++++++++++--- src/thorin/be/shady/shady.h | 6 ++-- 2 files changed, 64 insertions(+), 8 deletions(-) diff --git a/src/thorin/be/shady/shady.cpp b/src/thorin/be/shady/shady.cpp index f60ae0ed8..a267388df 100644 --- a/src/thorin/be/shady/shady.cpp +++ b/src/thorin/be/shady/shady.cpp @@ -118,6 +118,10 @@ const shady::Type* CodeGen::convert(const Type* type) { auto converted = convert(fn_type->op(i)); if (!converted) continue; // Eliminate mem params + shady::QualifiedType qtype; + qtype.type = converted; + qtype.is_uniform = false; + converted = shady::qualified_type(arena, qtype); dom.push_back(converted); } @@ -159,6 +163,10 @@ shady::Node* CodeGen::def_to_decl(Def* def) { continue; // Skip the return parameter auto type = convert(cont->param(i)->type()); if (!type) continue; // Eliminate mem tokens + shady::QualifiedType qtype; + qtype.type = type; + qtype.is_uniform = false; + type = shady::qualified_type(arena, qtype); auto param = shady::var(arena, type, cont->param(i)->name().c_str()); defs_[cont->param(i)] = param; // Register the param as emitted already params.push_back(param); @@ -208,7 +216,7 @@ shady::Node* CodeGen::prepare(const Scope& scope) { void CodeGen::prepare(Continuation* cont, shady::Node*) { BB& bb = cont2bb_[cont]; if (cont->is_basicblock()) - bb.head = emit_fun_decl(cont); + bb.head = def_to_decl(cont); else assert(bb.head); @@ -225,13 +233,22 @@ void CodeGen::emit_epilogue(Continuation* cont) { auto body = cont->body(); NodeVec args; for (auto& arg : body->args()) { - if (convert(arg->type()) == nullptr) continue; - args.push_back(emit(arg)); + if (convert(arg->type()) == nullptr) { + args.push_back(nullptr); + } else if (auto callee = arg->isa_nom(); callee && callee->is_basicblock()) { + // Emitting basic blocks as values isn't legal - but for convenience we'll put them in our list. + BB& callee_bb = cont2bb_[callee]; + assert(callee_bb.head && callee_bb.head->payload.fn.is_basic_block); + args.push_back(callee_bb.head); + } else { + args.push_back(emit(arg)); + } } if (body->callee() == entry_->ret_param()) { shady::Return payload = {}; payload.fn = curr_fn; + args.erase(std::remove_if(args.begin(), args.end(), [&](const auto& item){ return item == nullptr || !shady::is_value(item); }), args.end()); payload.values = vec2nodes(args); bb.terminator = shady::fn_ret(arena, payload); } else if (body->callee() == world().branch()) { @@ -246,6 +263,7 @@ void CodeGen::emit_epilogue(Continuation* cont) { assert(false); } else if (auto destination = body->callee()->isa_nom(); destination && destination->is_basicblock()) { shady::Branch payload = {}; + args.erase(std::remove_if(args.begin(), args.end(), [&](const auto& item){ return item == nullptr || !shady::is_value(item); }), args.end()); payload.args = vec2nodes(args); payload.branch_mode = shady::Branch_::BrJump; bb.terminator = shady::branch(arena, payload); @@ -254,8 +272,10 @@ void CodeGen::emit_epilogue(Continuation* cont) { } else if (auto callee = body->callee()->isa_nom()) { shady::Callc payload = {}; int ret_param = find_return_parameter(callee->type()); + assert(ret_param >= 0); payload.ret_cont = args[ret_param]; args.erase(args.begin() + ret_param); + args.erase(std::remove_if(args.begin(), args.end(), [&](const auto& item){ return item == nullptr || !shady::is_value(item); }), args.end()); payload.args = vec2nodes(args); payload.is_return_indirect = false; payload.callee = emit(callee); @@ -278,8 +298,46 @@ void CodeGen::finalize(const Scope& scope) { curr_fn = nullptr; } +const shady::Node* CodeGen::emit_fun_decl(Continuation* cont) { + assert(!cont->is_basicblock()); + shady::FnAddr payload; + payload.fn = get_decl(cont); + return shady::fn_addr(arena, payload); +} + const shady::Node* CodeGen::emit_bb(BB& bb, const Def* def) { - assert("TODO"); + const shady::Node* v = nullptr; + if (auto prim_lit = def->isa()) { + const auto& box = prim_lit->value(); + switch (prim_lit->primtype_tag()) { + case PrimType_bool: v = box.get_bool() ? shady::true_lit(arena) : shady::false_lit(arena); break; + case PrimType_ps8: case PrimType_qs8: v = shady::int8_literal (arena, box.get_s8()); break; + case PrimType_pu8: case PrimType_qu8: v = shady::uint8_literal (arena, box.get_u8()); break; + case PrimType_ps16: case PrimType_qs16: v = shady::int16_literal(arena, box.get_s16()); break; + case PrimType_pu16: case PrimType_qu16: v = shady::uint16_literal(arena, box.get_u16()); break; + case PrimType_ps32: case PrimType_qs32: v = shady::int32_literal(arena, box.get_s32()); break; + case PrimType_pu32: case PrimType_qu32: v = shady::uint32_literal(arena, box.get_u32()); break; + case PrimType_ps64: case PrimType_qs64: v = shady::int64_literal(arena, box.get_s64()); break; + case PrimType_pu64: case PrimType_qu64: v = shady::uint64_literal(arena, box.get_u64()); break; + case PrimType_pf16: case PrimType_qf16: assert(false && "TODO"); + case PrimType_pf32: case PrimType_qf32: v = shady::float_type(arena); break; + case PrimType_pf64: case PrimType_qf64: assert(false && "TODO"); + default: THORIN_UNREACHABLE; + } + } else if (auto arr = def->isa()) { + NodeVec contents; + for (auto& e : arr->ops()) { + assert(emit(e)); + contents.push_back(emit(e)); + } + shady::ArrayLiteral payload; + payload.element_type = convert(arr->elem_type()); + payload.contents = vec2nodes(contents); + v = shady::arr_lit(arena, payload); + } + assert(v && shady::is_value(v)); + defs_[def] = v; + return v; } } diff --git a/src/thorin/be/shady/shady.h b/src/thorin/be/shady/shady.h index ece795be4..bd4c8026b 100644 --- a/src/thorin/be/shady/shady.h +++ b/src/thorin/be/shady/shady.h @@ -30,7 +30,7 @@ class CodeGen : public thorin::CodeGen, public thorin::Emitter Date: Tue, 30 Aug 2022 18:36:16 +0200 Subject: [PATCH 103/342] fixes for msvc --- src/thorin/be/shady/shady.cpp | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/thorin/be/shady/shady.cpp b/src/thorin/be/shady/shady.cpp index a267388df..84136734a 100644 --- a/src/thorin/be/shady/shady.cpp +++ b/src/thorin/be/shady/shady.cpp @@ -15,17 +15,17 @@ void CodeGen::emit_stream(std::ostream& out) { structure_loops(world()); structure_flow(world()); - auto config = shady::ArenaConfig { - .check_types = true, - }; + shady::ArenaConfig config = { 0 }; + config.check_types = true; arena = shady::new_arena(config); Scope::for_each(world(), [&](const Scope& scope) { emit_scope(scope); }); // build root node with the top level stuff that got emitted - auto root = shady::root(arena, (shady::Root) { - .declarations = shady::nodes(arena, top_level.size(), const_cast(top_level.data())), - }); + shady::Root root_payload = { + shady::nodes(arena, top_level.size(), const_cast(top_level.data())) + }; + auto root = shady::root(arena, root_payload); char* bufptr; size_t size; @@ -82,10 +82,11 @@ const shady::Type* CodeGen::convert(const Type* type) { default: THORIN_UNREACHABLE; } } else if (auto ptr = type->isa()) { - t = shady::ptr_type(arena, (shady::PtrType) { + shady::PtrType payload = { convert_address_space(ptr->addr_space()), convert(ptr->pointee()) - }); + }; + t = shady::ptr_type(arena, payload); } else if (auto arr = type->isa()) { shady::ArrType payload = {}; payload.element_type = convert(arr->elem_type()); @@ -210,7 +211,7 @@ shady::Node* CodeGen::get_decl(Def* def) { } shady::Node* CodeGen::prepare(const Scope& scope) { - cont2bb_[scope.entry()].head = curr_fn = get_decl(scope.entry()); + return cont2bb_[scope.entry()].head = curr_fn = get_decl(scope.entry()); } void CodeGen::prepare(Continuation* cont, shady::Node*) { @@ -273,7 +274,7 @@ void CodeGen::emit_epilogue(Continuation* cont) { shady::Callc payload = {}; int ret_param = find_return_parameter(callee->type()); assert(ret_param >= 0); - payload.ret_cont = args[ret_param]; + payload.join_at = args[ret_param]; args.erase(args.begin() + ret_param); args.erase(std::remove_if(args.begin(), args.end(), [&](const auto& item){ return item == nullptr || !shady::is_value(item); }), args.end()); payload.args = vec2nodes(args); From 2c0ce6c68db2ebd70ec40c4c3229cd04f2ea7ad1 Mon Sep 17 00:00:00 2001 From: Matthias Kurtenacker Date: Tue, 13 Sep 2022 16:45:52 +0200 Subject: [PATCH 104/342] Remove recursion in schedule, hoist_enters and importer. Using recursion in these passes can lead to stack overflows if large programs are being compiled. --- src/thorin/analyses/schedule.cpp | 67 ++++++++++++++-- src/thorin/analyses/schedule.h | 3 + src/thorin/transform/hoist_enters.cpp | 17 +++- src/thorin/transform/importer.cpp | 108 +++++++++++++++++++++++--- src/thorin/transform/importer.h | 5 +- 5 files changed, 180 insertions(+), 20 deletions(-) diff --git a/src/thorin/analyses/schedule.cpp b/src/thorin/analyses/schedule.cpp index 798de7255..03a6eb23f 100644 --- a/src/thorin/analyses/schedule.cpp +++ b/src/thorin/analyses/schedule.cpp @@ -9,6 +9,8 @@ #include "thorin/analyses/looptree.h" #include "thorin/analyses/scope.h" +#include + namespace thorin { Scheduler::Scheduler(const Scope& s) @@ -44,25 +46,73 @@ Scheduler::Scheduler(const Scope& s) } } -Continuation* Scheduler::early(const Def* def) { +std::stack early_todo; + +Continuation* Scheduler::early(const Def * def) { if (auto cont = early_.lookup(def)) return *cont; - if (auto param = def->isa()) return early_[def] = param->continuation(); + + early_todo.push(def); + Continuation *return_cont = nullptr; + while (!early_todo.empty()) { + return_cont = early_intern(); + } + assert(return_cont); + return return_cont; +} + +Continuation* Scheduler::early_intern() { + const Def* def = early_todo.top(); + + if (auto cont = early_.lookup(def)) { + early_todo.pop(); + return *cont; + } + if (auto param = def->isa()) { + early_todo.pop(); + return early_[def] = param->continuation(); + } auto result = scope().entry(); for (auto op : def->as_structural()->ops()) { if (!op->isa_nom() && def2uses_.find(op) != def2uses_.end()) { - auto cont = early(op); + Continuation *cont; + if (early_.lookup(op)) { + cont = *early_.lookup(op); + } else { + early_todo.push(op); + return nullptr; + } if (domtree().depth(cfg(cont)) > domtree().depth(cfg(result))) result = cont; } } + early_todo.pop(); return early_[def] = result; } -Continuation* Scheduler::late(const Def* def) { +std::stack late_todo; + +Continuation* Scheduler::late(const Def * def) { if (auto cont = late_.lookup(def)) return *cont; + late_todo.push(def); + Continuation *return_cont = nullptr; + while (!late_todo.empty()) { + return_cont = late_intern(); + } + assert(return_cont); + return return_cont; +} + +Continuation* Scheduler::late_intern() { + const Def* def = late_todo.top(); + + if (auto cont = late_.lookup(def)) { + late_todo.pop(); + return *cont; + } + Continuation* result = nullptr; if (auto continuation = def->isa_nom()) { result = continuation; @@ -70,11 +120,18 @@ Continuation* Scheduler::late(const Def* def) { result = param->continuation(); } else { for (auto use : uses(def)) { - auto cont = late(use); + Continuation* cont; + if (late_.lookup(use)) { + cont = *late_.lookup(use); + } else { + late_todo.push(use); + return nullptr; + } result = result ? domtree().least_common_ancestor(cfg(result), cfg(cont))->continuation() : cont; } } + late_todo.pop(); return late_[def] = result; } diff --git a/src/thorin/analyses/schedule.h b/src/thorin/analyses/schedule.h index b2cfa65aa..57d874274 100644 --- a/src/thorin/analyses/schedule.h +++ b/src/thorin/analyses/schedule.h @@ -48,6 +48,9 @@ class Scheduler { DefMap late_; DefMap smart_; DefMap def2uses_; + + Continuation* early_intern(); + Continuation* late_intern(); }; using Schedule = std::vector; diff --git a/src/thorin/transform/hoist_enters.cpp b/src/thorin/transform/hoist_enters.cpp index c052498e2..b44a8e22a 100644 --- a/src/thorin/transform/hoist_enters.cpp +++ b/src/thorin/transform/hoist_enters.cpp @@ -4,8 +4,12 @@ #include "thorin/analyses/scope.h" #include "thorin/analyses/verify.h" +#include + namespace thorin { +std::stack todo; + static void find_enters(std::deque& enters, const Def* def) { if (auto enter = def->isa()) enters.push_back(enter); @@ -15,13 +19,20 @@ static void find_enters(std::deque& enters, const Def* def) { for (auto use : def->uses()) { if (auto memop = use->isa()) - find_enters(enters, memop); + todo.push(memop); } } static void find_enters(std::deque& enters, Continuation* continuation) { - if (auto mem_param = continuation->mem_param()) - find_enters(enters, mem_param); + if (auto mem_param = continuation->mem_param()) { + todo.push(mem_param); + while (!todo.empty()) { + auto next_item = todo.top(); + todo.pop(); + + find_enters(enters, next_item); + } + } } static void hoist_enters(const Scope& scope) { diff --git a/src/thorin/transform/importer.cpp b/src/thorin/transform/importer.cpp index f125585de..d476fb718 100644 --- a/src/thorin/transform/importer.cpp +++ b/src/thorin/transform/importer.cpp @@ -1,8 +1,12 @@ #include "thorin/transform/importer.h" +#include +#include +#include + namespace thorin { -const Type* Importer::import(const Type* otype) { +const Type* Importer::import_type(const Type* otype) { if (auto ntype = type_old2new_.lookup(otype)) { assert(&(*ntype)->table() == &world_); return *ntype; @@ -13,13 +17,13 @@ const Type* Importer::import(const Type* otype) { auto ntype = nominal_type->stub(world_); type_old2new_[otype] = ntype; for (size_t i = 0; i != size; ++i) - ntype->set(i, import(otype->op(i))); + ntype->set(i, import_type(otype->op(i))); return ntype; } Array nops(size); for (size_t i = 0; i != size; ++i) - nops[i] = import(otype->op(i)); + nops[i] = import_type(otype->op(i)); auto ntype = otype->rebuild(world_, nops); type_old2new_[otype] = ntype; @@ -28,38 +32,108 @@ const Type* Importer::import(const Type* otype) { return ntype; } +std::stack> required_defs; +std::set analyzed_conts; + +void enqueue(const Def* elem) { + if (elem->isa_nom()) { + if (analyzed_conts.find(elem) != analyzed_conts.end()) { + required_defs.push(std::pair(elem, false)); + } else { + analyzed_conts.insert(elem); + required_defs.push(std::pair(elem, true)); + } + } else { + required_defs.push(std::pair(elem, false)); + } +} + const Def* Importer::import(const Def* odef) { if (auto ndef = def_old2new_.lookup(odef)) { assert(&(*ndef)->world() == &world_); return *ndef; } - auto ntype = import(odef->type()); + assert(required_defs.empty()); + enqueue(odef); + + const Def* return_def = nullptr; + while (!required_defs.empty()) { + return_def = import_nonrecursive(); + } + + assert(return_def); + assert (return_def == def_old2new_.lookup(odef)); + + analyzed_conts.clear(); + + return return_def; +} + +const Def* Importer::import_nonrecursive() { + const Def* odef = required_defs.top().first; + bool jump_to_analyze = required_defs.top().second; + + Continuation* ncontinuation = nullptr; + + if (auto ndef = def_old2new_.lookup(odef)) { + assert(&(*ndef)->world() == &world_); + if (odef->isa_nom()) { + if (!jump_to_analyze) { + required_defs.pop(); + return *ndef; + } + ncontinuation = (*ndef)->as_nom(); + } else { + required_defs.pop(); + return *ndef; + } + } + + auto ntype = import_type(odef->type()); if (auto oparam = odef->isa()) { + if (!def_old2new_.lookup(oparam->continuation())) { + enqueue(oparam->continuation()); + return nullptr; + } import(oparam->continuation())->as_nom(); auto nparam = def_old2new_[oparam]; assert(nparam && &nparam->world() == &world_); - return nparam; + required_defs.pop(); + return def_old2new_[oparam] = nparam; } if (auto ofilter = odef->isa()) { Array new_conditions(ofilter->num_ops()); + + bool unfinished_business = false; + for (size_t i = 0, e = ofilter->size(); i != e; ++i) + if (!def_old2new_.lookup(ofilter->condition(i))) { + enqueue(ofilter->condition(i)); + unfinished_business = true; + } + if (unfinished_business) + return nullptr; + for (size_t i = 0, e = ofilter->size(); i != e; ++i) new_conditions[i] = import(ofilter->condition(i)); auto nfilter = world().filter(new_conditions, ofilter->debug()); - return nfilter; + required_defs.pop(); + return def_old2new_[ofilter] = nfilter; } - Continuation* ncontinuation = nullptr; - if (auto ocontinuation = odef->isa_nom()) { // create stub in new world + if (auto ocontinuation = odef->isa_nom(); ocontinuation && !ncontinuation) { // create stub in new world assert(!ocontinuation->dead_); // TODO maybe we want to deal with intrinsics in a more streamlined way - if (ocontinuation == ocontinuation->world().branch()) + if (ocontinuation == ocontinuation->world().branch()) { + required_defs.pop(); return def_old2new_[ocontinuation] = world().branch(); - if (ocontinuation == ocontinuation->world().end_scope()) + } else if (ocontinuation == ocontinuation->world().end_scope()) { + required_defs.pop(); return def_old2new_[ocontinuation] = world().end_scope(); - auto npi = import(ocontinuation->type())->as(); + } + auto npi = import_type(ocontinuation->type())->as(); ncontinuation = world().continuation(npi, ocontinuation->attributes(), ocontinuation->debug_history()); assert(&ncontinuation->world() == &world()); assert(&npi->table() == &world()); @@ -76,6 +150,16 @@ const Def* Importer::import(const Def* odef) { size_t size = odef->num_ops(); Array nops(size); + + bool unfinished = false; + for (size_t i = 0; i != size; ++i) + if (!def_old2new_.lookup(odef->op(i))) { + enqueue(odef->op(i)); + unfinished = true; + } + if (unfinished) + return nullptr; + for (size_t i = 0; i != size; ++i) { assert(odef->op(i) != odef); nops[i] = import(odef->op(i)); @@ -85,6 +169,7 @@ const Def* Importer::import(const Def* odef) { if (odef->isa_structural()) { auto ndef = odef->rebuild(world(), ntype, nops); todo_ |= odef->tag() != ndef->tag(); + required_defs.pop(); return def_old2new_[odef] = ndef; } @@ -94,6 +179,7 @@ const Def* Importer::import(const Def* odef) { ncontinuation->set_body(napp); ncontinuation->set_filter(nops[1]->as()); ncontinuation->verify(); + required_defs.pop(); return ncontinuation; } diff --git a/src/thorin/transform/importer.h b/src/thorin/transform/importer.h index c34db9ef3..eb7d5bc42 100644 --- a/src/thorin/transform/importer.h +++ b/src/thorin/transform/importer.h @@ -20,10 +20,13 @@ class Importer { } World& world() { return world_; } - const Type* import(const Type*); + const Type* import_type(const Type*); const Def* import(const Def*); bool todo() const { return todo_; } +private: + const Def* import_nonrecursive(); + public: Type2Type type_old2new_; Def2Def def_old2new_; From 0374889ca135b5f0f5c099c4652d2a92a345dc61 Mon Sep 17 00:00:00 2001 From: Matthias Kurtenacker Date: Fri, 16 Sep 2022 16:02:57 +0200 Subject: [PATCH 105/342] Initialize HashTable array before using it. --- src/thorin/util/hash.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/thorin/util/hash.h b/src/thorin/util/hash.h index 32bf6d6bd..007c7fbb4 100644 --- a/src/thorin/util/hash.h +++ b/src/thorin/util/hash.h @@ -217,6 +217,7 @@ class HashTable { HashTable() : capacity_(StackCapacity) , size_(0) + , array_() , nodes_(array_.data()) #if THORIN_ENABLE_CHECKS , id_(0) From dc54ccaac495d898f0dfb6338953df2aa6d944a6 Mon Sep 17 00:00:00 2001 From: Matthias Kurtenacker Date: Thu, 22 Sep 2022 16:18:56 +0200 Subject: [PATCH 106/342] Scheduler performance enhancement --- src/thorin/analyses/schedule.cpp | 33 +++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/src/thorin/analyses/schedule.cpp b/src/thorin/analyses/schedule.cpp index 03a6eb23f..38a67a4b6 100644 --- a/src/thorin/analyses/schedule.cpp +++ b/src/thorin/analyses/schedule.cpp @@ -72,16 +72,23 @@ Continuation* Scheduler::early_intern() { return early_[def] = param->continuation(); } - auto result = scope().entry(); + bool todo_empty = true; for (auto op : def->as_structural()->ops()) { if (!op->isa_nom() && def2uses_.find(op) != def2uses_.end()) { - Continuation *cont; - if (early_.lookup(op)) { - cont = *early_.lookup(op); - } else { + if (!early_.lookup(op)) { early_todo.push(op); - return nullptr; + todo_empty = false; } + } + } + if (!todo_empty) + return nullptr; + + auto result = scope().entry(); + for (auto op : def->as_structural()->ops()) { + if (!op->isa_nom() && def2uses_.find(op) != def2uses_.end()) { + Continuation *cont = *early_.lookup(op); + assert(cont); if (domtree().depth(cfg(cont)) > domtree().depth(cfg(result))) result = cont; } @@ -119,14 +126,18 @@ Continuation* Scheduler::late_intern() { } else if (auto param = def->isa()) { result = param->continuation(); } else { + bool todo_empty = true; for (auto use : uses(def)) { - Continuation* cont; - if (late_.lookup(use)) { - cont = *late_.lookup(use); - } else { + if (!late_.lookup(use)) { late_todo.push(use); - return nullptr; + todo_empty = false; } + } + if (!todo_empty) + return nullptr; + for (auto use : uses(def)) { + Continuation* cont = *late_.lookup(use); + assert(cont); result = result ? domtree().least_common_ancestor(cfg(result), cfg(cont))->continuation() : cont; } } From 624294b0b3ba0ec2d3091fbed46d7608bbcdb301 Mon Sep 17 00:00:00 2001 From: Matthias Kurtenacker Date: Thu, 22 Sep 2022 16:22:43 +0200 Subject: [PATCH 107/342] Backend remove recursion on memory. --- src/thorin/be/emitter.h | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/src/thorin/be/emitter.h b/src/thorin/be/emitter.h index 72300ba06..655e8f544 100644 --- a/src/thorin/be/emitter.h +++ b/src/thorin/be/emitter.h @@ -1,6 +1,9 @@ #ifndef THORIN_BE_EMITTER_H #define THORIN_BE_EMITTER_H +#include +#include + namespace thorin { template @@ -11,6 +14,32 @@ class Emitter { /// Internal wrapper for @p emit that checks and retrieves/puts the @c Value from @p defs_. Value emit_(const Def* def) { + std::stack required_defs; + std::queue todo; + todo.push(def); + + while (!todo.empty()) { + auto def = todo.front(); + todo.pop(); + if (defs_.lookup(def)) continue; + + if (auto memop = def->isa()) { + todo.push(memop->mem()); + required_defs.push(memop->mem()); + } else if (auto extract = def->isa()) { + if (is_mem(extract)) { + todo.push(extract->agg()); + required_defs.push(extract->agg()); + } + } + } + + while (!required_defs.empty()) { + auto r = required_defs.top(); + required_defs.pop(); + emit_unsafe(r); + } + auto place = def->no_dep() ? entry_ : scheduler_.smart(def); auto& bb = cont2bb_[place]; return child().emit_bb(bb, def); From a9ad15284133a0d5b6a5594f71457c250b539337 Mon Sep 17 00:00:00 2001 From: Matthias Kurtenacker Date: Fri, 7 Oct 2022 13:47:35 +0200 Subject: [PATCH 108/342] use pop(x) instead of x.top(); x.pop(); --- src/thorin/be/emitter.h | 3 +-- src/thorin/transform/hoist_enters.cpp | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/thorin/be/emitter.h b/src/thorin/be/emitter.h index 655e8f544..1b396804f 100644 --- a/src/thorin/be/emitter.h +++ b/src/thorin/be/emitter.h @@ -35,8 +35,7 @@ class Emitter { } while (!required_defs.empty()) { - auto r = required_defs.top(); - required_defs.pop(); + auto r = pop(required_defs); emit_unsafe(r); } diff --git a/src/thorin/transform/hoist_enters.cpp b/src/thorin/transform/hoist_enters.cpp index b44a8e22a..5cd7fcfb4 100644 --- a/src/thorin/transform/hoist_enters.cpp +++ b/src/thorin/transform/hoist_enters.cpp @@ -27,8 +27,7 @@ static void find_enters(std::deque& enters, Continuation* continua if (auto mem_param = continuation->mem_param()) { todo.push(mem_param); while (!todo.empty()) { - auto next_item = todo.top(); - todo.pop(); + auto next_item = pop(todo); find_enters(enters, next_item); } From 72d2deed2b07094d41b01d606ff5dce6450d6ddb Mon Sep 17 00:00:00 2001 From: Matthias Kurtenacker Date: Fri, 7 Oct 2022 13:48:32 +0200 Subject: [PATCH 109/342] Mangler remove recursion on body. --- src/thorin/transform/mangle.cpp | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/thorin/transform/mangle.cpp b/src/thorin/transform/mangle.cpp index 0dcc0d909..629134fb7 100644 --- a/src/thorin/transform/mangle.cpp +++ b/src/thorin/transform/mangle.cpp @@ -5,6 +5,9 @@ #include "thorin/world.h" #include "thorin/analyses/scope.h" +#include +#include + namespace thorin { const Def* Rewriter::instantiate(const Def* odef) { @@ -54,6 +57,8 @@ Mangler::Mangler(const Scope& scope, Defs args, Defs lift) } } +std::queue> bodies_to_mangle; + Continuation* Mangler::mangle() { // create new_entry - but first collect and specialize all param types std::vector param_types; @@ -97,7 +102,17 @@ Continuation* Mangler::mangle() { new_entry()->set_filter(world().filter(new_conditions, old_entry()->filter()->debug())); } - new_entry()->set_body(mangle_body(old_entry()->body())); + bodies_to_mangle.push(std::pair(new_entry(), old_entry())); + + while (!bodies_to_mangle.empty()) { + auto task = pop(bodies_to_mangle); + + auto new_cont = task.first; + auto old_cont = task.second; + + assert(!new_cont->has_body()); + new_cont->set_body(mangle_body(old_cont->body())); + } new_entry()->verify(); @@ -155,7 +170,7 @@ const Def* Mangler::mangle(const Def* old_def) { else if (auto old_continuation = old_def->isa_nom()) { auto new_continuation = mangle_head(old_continuation); if (old_continuation->has_body()) - new_continuation->set_body(mangle_body(old_continuation->body())); + bodies_to_mangle.push(std::pair(new_continuation, old_continuation)); return new_continuation; } else if (auto param = old_def->isa()) { assert(within(param->continuation())); From fcbd30423b842e9352138eef9a0f1192dbe95eef Mon Sep 17 00:00:00 2001 From: Matthias Kurtenacker Date: Tue, 18 Oct 2022 14:37:16 +0200 Subject: [PATCH 110/342] Improve verification. --- src/thorin/analyses/verify.cpp | 30 ++++++++++++++++++++++++------ src/thorin/continuation.cpp | 9 ++++++--- src/thorin/continuation.h | 4 ++-- 3 files changed, 32 insertions(+), 11 deletions(-) diff --git a/src/thorin/analyses/verify.cpp b/src/thorin/analyses/verify.cpp index 33ba03a19..bbd034e8c 100644 --- a/src/thorin/analyses/verify.cpp +++ b/src/thorin/analyses/verify.cpp @@ -2,16 +2,19 @@ #include "thorin/type.h" #include "thorin/world.h" #include "thorin/analyses/scope.h" +#include "thorin/analyses/free_defs.h" namespace thorin { // TODO this needs serious rewriting -static void verify_calls(World& world) { +static bool verify_calls(World& world) { + bool ok = true; for (auto def : world.defs()) { if (auto cont = def->isa()) - cont->verify(); + ok &= cont->verify(); } + return ok; } static bool verify_top_level(World& world) { @@ -24,14 +27,29 @@ static bool verify_top_level(World& world) { ok = false; } }); - if (!ok) - world.dump(); + return ok; +} + +static bool verify_mem(World& world) { + bool ok = true; + Scope::for_each(world, [&] (const Scope& scope) { + for (auto def : free_defs(scope)) { + if (is_mem(def)) { + world.ELOG("scope '{}' got free mem '{}' with {} uses", scope.entry(), def, def->num_uses()); + ok = false; + } + } + }); return ok; } void verify(World& world) { - verify_calls(world); - verify_top_level(world); + bool ok = true; + ok &= verify_calls(world); + ok &= verify_top_level(world); + ok &= verify_mem(world); + if (!ok) + world.dump(); } } diff --git a/src/thorin/continuation.cpp b/src/thorin/continuation.cpp index 048e0449b..a33183d17 100644 --- a/src/thorin/continuation.cpp +++ b/src/thorin/continuation.cpp @@ -28,7 +28,7 @@ App::App(const Defs ops, Debug dbg) : Def(Node_App, ops[0]->world().bottom_type( #endif } -void App::verify() const { +bool App::verify() const { auto callee_type = callee()->type()->isa(); // works for closures too, no need for a special case assertf(callee_type, "callee type must be a FnType"); assertf(callee_type->num_ops() == num_args(), "app node '{}' has fn type {} with {} parameters, but is supplied {} arguments", this, callee_type, callee_type->num_ops(), num_args()); @@ -37,6 +37,7 @@ void App::verify() const { auto at = arg(i)->type(); assertf(pt == at, "app node argument {} has type {} but the callee was expecting {}", this, at, pt); } + return true; } //------------------------------------------------------------------------------ @@ -262,15 +263,17 @@ void Continuation::match(const Def* mem, const Def* val, Continuation* otherwise verify(); } -void Continuation::verify() const { +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 { - body()->verify(); + ok &= body()->verify(); assert(!dead_); // destroy() should remove the body assert(intrinsic() == Intrinsic::None); assertf(filter()->is_empty() || num_params() == filter()->size(), "The filter needs to be either empty, or match the param count"); } + return ok; } /// Rewrites the body to only keep the non-specialized arguments diff --git a/src/thorin/continuation.h b/src/thorin/continuation.h index 02f203a03..ded1fd25d 100644 --- a/src/thorin/continuation.h +++ b/src/thorin/continuation.h @@ -72,7 +72,7 @@ class App : public Def { } void jump(const Def* callee, Defs args, Debug dbg = {}); - void verify() const; + bool verify() const; friend class World; }; @@ -188,7 +188,7 @@ class Continuation : public Def { void jump(const Def* callee, Defs args, Debug dbg = {}); void branch(const Def* mem, const Def* cond, const Def* t, const Def* f, Debug dbg = {}); void match(const Def* mem, const Def* val, Continuation* otherwise, Defs patterns, ArrayRef continuations, Debug dbg = {}); - void verify() const; + bool verify() const; const Filter* filter() const { return op(1)->as(); } void set_filter(const Filter* f) { From 3dd3f974a10ea916b3424c7797e97a1b6e0ffd51 Mon Sep 17 00:00:00 2001 From: Matthias Kurtenacker Date: Tue, 18 Oct 2022 14:40:45 +0200 Subject: [PATCH 111/342] World::continuation: Add caller name to debug history, for debugging. --- src/thorin/debug.h | 11 +++++++++++ src/thorin/rec_stream.cpp | 5 ++++- src/thorin/type.cpp | 4 ++-- src/thorin/world.cpp | 9 +++++++++ 4 files changed, 26 insertions(+), 3 deletions(-) diff --git a/src/thorin/debug.h b/src/thorin/debug.h index b4d19d14b..ef35f2a36 100644 --- a/src/thorin/debug.h +++ b/src/thorin/debug.h @@ -43,18 +43,29 @@ class Debug { Debug() = default; // TODO remove Debug(std::string name, Loc loc = {}, const Def* meta = nullptr) : name(name) + , creation_context("") , loc(loc) , meta(meta) {} Debug(const char* name, Loc loc = {}, const Def* meta = nullptr) : Debug(std::string(name), loc, meta) {} + Debug(std::string name, std::string creation_context, Loc loc = {}, const Def* meta = nullptr) + : name(name) + , creation_context(creation_context) + , loc(loc) + , meta(meta) + {} + Debug(const char* name, const char* creation_context, Loc loc = {}, const Def* meta = nullptr) + : Debug(std::string(name), std::string(creation_context), loc, meta) + {} Debug(Loc loc) : Debug("", loc) {} //Debug(const Def*); std::string name; + std::string creation_context; Loc loc; const Def* meta = nullptr; }; diff --git a/src/thorin/rec_stream.cpp b/src/thorin/rec_stream.cpp index 03d6a8c20..df983be34 100644 --- a/src/thorin/rec_stream.cpp +++ b/src/thorin/rec_stream.cpp @@ -99,7 +99,10 @@ Stream& Def::stream1(Stream& s) const { if (auto param = isa()) { return s.fmt("{}.{}", param->continuation(), param->unique_name()); } else if (isa()) { - return s.fmt("cont {}", unique_name()); + if (debug().creation_context != "") + return s.fmt("cont {} [{}]", unique_name(), debug().creation_context); + else + return s.fmt("cont {}", unique_name()); } else if (auto app = isa()) { return s.fmt("{}({, })", app->callee(), app->args()); } else if (isa()) { diff --git a/src/thorin/type.cpp b/src/thorin/type.cpp index f8cc33751..7918b0e45 100644 --- a/src/thorin/type.cpp +++ b/src/thorin/type.cpp @@ -143,10 +143,10 @@ Stream& Type::stream(Stream& s) const { else if (isa< FrameType>()) return s.fmt("frame"); else if (auto t = isa()) { return s.fmt("[{} x {}]", t->dim(), t->elem_type()); + } else if (auto t = isa()) { + return s.fmt("closure[{, }]", t->ops()); } else if (auto t = isa()) { return s.fmt("fn[{, }]", t->ops()); - } else if (auto t = isa()) { - return s.fmt("closure [{, }]", t->ops()); } else if (auto t = isa()) { return s.fmt("[{}]", t->elem_type()); } else if (auto t = isa()) { diff --git a/src/thorin/world.cpp b/src/thorin/world.cpp index 605a924ca..d2c9d17a2 100644 --- a/src/thorin/world.cpp +++ b/src/thorin/world.cpp @@ -10,6 +10,7 @@ #endif #include +#include #include "thorin/def.h" #include "thorin/primop.h" @@ -1102,7 +1103,15 @@ const Def* World::run(const Def* def, Debug dbg) { */ Continuation* World::continuation(const FnType* fn, Continuation::Attributes attributes, Debug dbg) { + void *array[10]; + size_t size = backtrace(array, 10); + assert(size >= 2); + char ** symbols = backtrace_symbols(array, 10); + + dbg.creation_context = symbols[1]; + auto cont = put(fn, attributes, dbg); + free(symbols); size_t i = 0; for (auto op : fn->ops()) { From da1fe76d0f03a5b04862dec11f97a3abe736c9c8 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Mon, 31 Oct 2022 11:13:18 +0100 Subject: [PATCH 112/342] adapted API --- src/thorin/be/shady/shady.cpp | 145 +++++++++++++++++++++------------- src/thorin/be/shady/shady.h | 8 +- 2 files changed, 93 insertions(+), 60 deletions(-) diff --git a/src/thorin/be/shady/shady.cpp b/src/thorin/be/shady/shady.cpp index 84136734a..4da148a44 100644 --- a/src/thorin/be/shady/shady.cpp +++ b/src/thorin/be/shady/shady.cpp @@ -10,32 +10,26 @@ CodeGen::CodeGen(thorin::World& world, Cont2Config& kernel_config, bool debug) {} void CodeGen::emit_stream(std::ostream& out) { - assert(top_level.empty()); + // structure_loops(world()); + // structure_flow(world()); - structure_loops(world()); - structure_flow(world()); + assert(!module); shady::ArenaConfig config = { 0 }; config.check_types = true; - arena = shady::new_arena(config); + arena = shady::new_ir_arena(config); + module = shady::new_module(arena, world().name().c_str()); Scope::for_each(world(), [&](const Scope& scope) { emit_scope(scope); }); - // build root node with the top level stuff that got emitted - shady::Root root_payload = { - shady::nodes(arena, top_level.size(), const_cast(top_level.data())) - }; - auto root = shady::root(arena, root_payload); - char* bufptr; size_t size; - shady::print_node_into_str(root, &bufptr, &size); + shady::print_module_into_str(module, &bufptr, &size); out.write(bufptr, static_cast(size)); free(bufptr); - shady::destroy_arena(arena); + shady::destroy_ir_arena(arena); arena = nullptr; - top_level.clear(); } shady::AddressSpace CodeGen::convert_address_space(AddrSpace as) { @@ -103,13 +97,12 @@ const shady::Type* CodeGen::convert(const Type* type) { shady::RecordType payload = {}; payload.members = shady::nodes(arena, strct->num_ops(), members.data()); payload.names = shady::strings(arena, 0, nullptr); - payload.special = shady::RecordType::NotSpecial; + payload.special = shady::NotSpecial; t = shady::record_type(arena, payload); } else if (auto variant = type->isa()) { assert(false && "TODO"); } else if (auto fn_type = type->isa()) { shady::FnType payload = {}; - payload.is_basic_block = fn_type->is_basicblock(); NodeVec dom, codom; int return_param_i = find_return_parameter(fn_type); @@ -151,9 +144,10 @@ const shady::Type* CodeGen::convert(const Type* type) { return t; } -shady::Node* CodeGen::def_to_decl(Def* def) { +shady::Node* CodeGen::emit_decl_head(Def* def) { NodeVec annotations; if (auto cont = def->isa_nom()) { + assert(!cont->is_basicblock()); NodeVec params; NodeVec returns; @@ -173,26 +167,22 @@ shady::Node* CodeGen::def_to_decl(Def* def) { params.push_back(param); } - if (!cont->is_basicblock() && ret_param_i >= 0) { - auto ret_fn_type = cont->type()->op(ret_param_i); + auto ret_fn_type = cont->type()->op(ret_param_i); - for (auto t : ret_fn_type->ops()) { - auto ret_type = convert(t); - if (!ret_type) - continue; // Eliminate mem types - returns.push_back(ret_type); - } + for (auto t : ret_fn_type->ops()) { + auto ret_type = convert(t); + if (!ret_type) + continue; // Eliminate mem types + returns.push_back(ret_type); } - return shady::fn(arena, vec2nodes(annotations), def->unique_name().c_str(), cont->is_basicblock(), vec2nodes(params), vec2nodes(returns)); + return shady::function(module, vec2nodes(params), def->unique_name().c_str(), vec2nodes(annotations), vec2nodes(returns)); } else if (auto global = def->isa()) { if (global->is_mutable()) { - return shady::global_var(arena, vec2nodes(annotations), convert(global->alloced_type()), global->unique_name().c_str(), convert_address_space(AddrSpace::Private)); + return shady::global_var(module, vec2nodes(annotations), convert(global->alloced_type()), global->unique_name().c_str(), convert_address_space(AddrSpace::Private)); } else { // Tentatively make those things constants... - auto constant = shady::constant(arena, vec2nodes(annotations), global->unique_name().c_str());; - constant->payload.constant.type_hint = convert(global->alloced_type()); - return constant; + return shady::constant(module, vec2nodes(annotations), convert(global->alloced_type()), global->unique_name().c_str()); } } else { assert(false && "This doesn't map to a decl !"); @@ -200,14 +190,14 @@ shady::Node* CodeGen::def_to_decl(Def* def) { } shady::Node* CodeGen::get_decl(Def* def) { - for (auto& e : top_level) { + shady::Nodes already_done = shady::get_module_declarations(module); + for (size_t i = 0; i < already_done.count; i++) { + auto& e = already_done.nodes[i]; if (shady::get_decl_name(e) == def->unique_name()) - return e; + return (shady::Node*) e; } - auto decl = def_to_decl(def); - top_level.push_back(decl); - return decl; + return emit_decl_head(def); } shady::Node* CodeGen::prepare(const Scope& scope) { @@ -216,18 +206,38 @@ shady::Node* CodeGen::prepare(const Scope& scope) { void CodeGen::prepare(Continuation* cont, shady::Node*) { BB& bb = cont2bb_[cont]; - if (cont->is_basicblock()) - bb.head = def_to_decl(cont); - else + if (cont->is_basicblock()) { + NodeVec params; + + for (size_t i = 0; i < cont->num_params(); i++) { + auto type = convert(cont->param(i)->type()); + if (!type) continue; // Eliminate mem tokens + shady::QualifiedType qtype; + qtype.type = type; + qtype.is_uniform = false; + type = shady::qualified_type(arena, qtype); + auto param = shady::var(arena, type, cont->param(i)->name().c_str()); + defs_[cont->param(i)] = param; // Register the param as emitted already + params.push_back(param); + } + + bb.head = shady::basic_block(arena, curr_fn, vec2nodes(params), cont->name().c_str()); + } else assert(bb.head); - // Register params - // for (size_t i = 0; i < cont->num_params(); i++) - // defs_[cont->param(i)] = bb.head->payload.fn.params.nodes[i]; + bb.builder = shady::begin_body(arena); +} - bb.builder = shady::begin_block(arena); +static std::optional is_shady_prim_op(const Continuation* cont) { + for (int i = 0; i < shady::PRIMOPS_COUNT; i++) { + if (cont->name() == shady::primop_names[i]) + return std::make_optional((shady::Op) i); + } + return std::nullopt; } +static std::vector emit_instruction(shady::BodyBuilder* builder, const shady::Node* instruction); + void CodeGen::emit_epilogue(Continuation* cont) { BB& bb = cont2bb_[cont]; assert(cont->has_body()); @@ -236,10 +246,11 @@ void CodeGen::emit_epilogue(Continuation* cont) { for (auto& arg : body->args()) { if (convert(arg->type()) == nullptr) { args.push_back(nullptr); - } else if (auto callee = arg->isa_nom(); callee && callee->is_basicblock()) { + } else if (auto target = arg->isa_nom(); target && target->is_basicblock()) { // Emitting basic blocks as values isn't legal - but for convenience we'll put them in our list. - BB& callee_bb = cont2bb_[callee]; - assert(callee_bb.head && callee_bb.head->payload.fn.is_basic_block); + assert(cont2bb_.contains(target)); + BB& callee_bb = cont2bb_[target]; + assert(callee_bb.head && shady::is_basic_block(callee_bb.head)); args.push_back(callee_bb.head); } else { args.push_back(emit(arg)); @@ -250,11 +261,10 @@ void CodeGen::emit_epilogue(Continuation* cont) { shady::Return payload = {}; payload.fn = curr_fn; args.erase(std::remove_if(args.begin(), args.end(), [&](const auto& item){ return item == nullptr || !shady::is_value(item); }), args.end()); - payload.values = vec2nodes(args); + payload.args = vec2nodes(args); bb.terminator = shady::fn_ret(arena, payload); } else if (body->callee() == world().branch()) { shady::Branch payload = {}; - payload.branch_mode = shady::Branch::BrIfElse; payload.args = shady::nodes(arena, 0, nullptr); payload.branch_condition = args[0]; payload.true_target = args[1]; @@ -263,24 +273,43 @@ void CodeGen::emit_epilogue(Continuation* cont) { } else if (auto match = body->callee()->as_nom(); match && match->intrinsic() == Intrinsic::Match) { assert(false); } else if (auto destination = body->callee()->isa_nom(); destination && destination->is_basicblock()) { - shady::Branch payload = {}; + shady::Jump payload = {}; args.erase(std::remove_if(args.begin(), args.end(), [&](const auto& item){ return item == nullptr || !shady::is_value(item); }), args.end()); payload.args = vec2nodes(args); - payload.branch_mode = shady::Branch_::BrJump; - bb.terminator = shady::branch(arena, payload); + payload.target = args[0]; + bb.terminator = shady::jump(arena, payload); } else if (auto intrinsic = body->callee()->isa_nom(); intrinsic && intrinsic->is_intrinsic()) { assert(false); } else if (auto callee = body->callee()->isa_nom()) { - shady::Callc payload = {}; int ret_param = find_return_parameter(callee->type()); assert(ret_param >= 0); - payload.join_at = args[ret_param]; + args.erase(args.begin() + ret_param); args.erase(std::remove_if(args.begin(), args.end(), [&](const auto& item){ return item == nullptr || !shady::is_value(item); }), args.end()); + + // shady primop called as imported continuations look like continuation calls to thorin, but not to shady + // we just need to carefully emit the primop as an instruction, then jump to the target BB, passing the stuff as we do + if (auto op = is_shady_prim_op(callee); op.has_value()) { + shady::bind_instruction(bb.builder, shady::prim_op(arena, (shady::PrimOp) { + .op = op.value(), + .type_arguments = empty(arena), + .operands = vec2nodes(args), + })); + shady::Jump jump; + jump.target = args[ret_param]; + bb.terminator = shady::jump(arena, jump); + return; + } + + shady::Call payload; payload.args = vec2nodes(args); - payload.is_return_indirect = false; payload.callee = emit(callee); - bb.terminator = shady::callc(arena, payload); + auto call = shady::call_instr(arena, payload); + + shady::LetInto payload2; + payload2.instruction = call; + payload2.tail = args[ret_param]; + bb.terminator = shady::let_into(arena, payload2); } else { assert(false); } @@ -289,13 +318,17 @@ void CodeGen::emit_epilogue(Continuation* cont) { void CodeGen::finalize(Continuation* cont) { BB& bb = cont2bb_[cont]; assert(bb.head && bb.builder && bb.terminator); - bb.block = shady::finish_block(bb.builder, bb.terminator); - bb.head->payload.fn.block = bb.block; + if (shady::is_basic_block(bb.head)) + bb.head->payload.basic_block.body = shady::finish_body(bb.builder, bb.terminator); + else if (shady::is_function(bb.head)) + bb.head->payload.fun.body = shady::finish_body(bb.builder, bb.terminator); + else + assert(false); } void CodeGen::finalize(const Scope& scope) { BB& bb = cont2bb_[scope.entry()]; - assert(bb.head->payload.fn.block != nullptr); + assert(bb.head->payload.fun.body != nullptr); curr_fn = nullptr; } diff --git a/src/thorin/be/shady/shady.h b/src/thorin/be/shady/shady.h index bd4c8026b..c918f4fe5 100644 --- a/src/thorin/be/shady/shady.h +++ b/src/thorin/be/shady/shady.h @@ -15,9 +15,8 @@ namespace thorin::shady_be { struct BB { /// For an entry BB, this will also be the head of the entire function shady::Node* head; - shady::BlockBuilder* builder; + shady::BodyBuilder* builder; const shady::Node* terminator; - const shady::Node* block; }; class CodeGen : public thorin::CodeGen, public thorin::Emitter { @@ -44,7 +43,7 @@ class CodeGen : public thorin::CodeGen, public thorin::Emitter; @@ -54,7 +53,8 @@ class CodeGen : public thorin::CodeGen, public thorin::Emitter top_level; + shady::Module* module = nullptr; + //std::vector top_level; shady::Node* curr_fn; From 9afa2d4114229e253c7dc00d639afaa91511b1e5 Mon Sep 17 00:00:00 2001 From: Matthias Kurtenacker Date: Wed, 9 Nov 2022 14:31:24 +0100 Subject: [PATCH 113/342] Update closure conversion to maintain a "converted" mapping for already converted definitions. --- src/thorin/analyses/verify.cpp | 16 +++-- src/thorin/transform/closure_conversion.cpp | 80 +++++++++++++++------ src/thorin/type.cpp | 2 +- src/thorin/world.cpp | 3 + 4 files changed, 70 insertions(+), 31 deletions(-) diff --git a/src/thorin/analyses/verify.cpp b/src/thorin/analyses/verify.cpp index bbd034e8c..f68af86fe 100644 --- a/src/thorin/analyses/verify.cpp +++ b/src/thorin/analyses/verify.cpp @@ -30,16 +30,17 @@ static bool verify_top_level(World& world) { return ok; } -static bool verify_mem(World& world) { +static bool verify_param(World& world) { bool ok = true; - Scope::for_each(world, [&] (const Scope& scope) { - for (auto def : free_defs(scope)) { - if (is_mem(def)) { - world.ELOG("scope '{}' got free mem '{}' with {} uses", scope.entry(), def, def->num_uses()); + for (auto def : world.defs()) { + if (auto param = def->isa()) { + auto cont = param->continuation(); + if (cont->dead_) { + world.ELOG("param '{}' originates in dead continuation {}", param, cont); ok = false; } } - }); + } return ok; } @@ -47,9 +48,10 @@ void verify(World& world) { bool ok = true; ok &= verify_calls(world); ok &= verify_top_level(world); - ok &= verify_mem(world); + ok &= verify_param(world); if (!ok) world.dump(); + assert(ok); } } diff --git a/src/thorin/transform/closure_conversion.cpp b/src/thorin/transform/closure_conversion.cpp index bb118c725..7d9250589 100644 --- a/src/thorin/transform/closure_conversion.cpp +++ b/src/thorin/transform/closure_conversion.cpp @@ -26,27 +26,28 @@ class ClosureConversion { auto new_type = world_.fn_type(convert(continuation->type())->ops()); if (new_type != continuation->type()) { + //The function type was changed, so the continuation takes another function as a parameter. auto new_continuation = world_.continuation(new_type->as(), continuation->debug()); if (continuation->is_intrinsic()) new_continuation->set_intrinsic(); + converted.emplace_back(continuation, new_continuation); + std::cerr << "Creating new conversion: " << continuation->unique_name() << " to " << new_continuation->unique_name() << "\n"; + continuation->type()->dump(); + new_continuation->type()->dump(); new_defs_[continuation] = new_continuation; - if (continuation->has_body()) { - auto body = continuation->body(); - for (size_t i = 0, e = continuation->num_params(); i != e; ++i) - new_defs_[continuation->param(i)] = new_continuation->param(i); - // copy existing call from old continuation - new_continuation->jump(body->callee(), body->args(), continuation->debug()); - converted.emplace_back(continuation, new_continuation); - } - } else if (continuation->has_body()) { + for (size_t i = 0, e = continuation->num_params(); i != e; ++i) + new_defs_[continuation->param(i)] = new_continuation->param(i); + } else { + //The type remains unchanged, do not generate a new continuation. + //We still need to add the continuation to converted, to ensure the jump will be converted later on. converted.emplace_back(continuation, continuation); } } // convert the calls to each continuation for (auto pair : converted) - convert_jump(pair.second); + convert_jump(pair.first, pair.second); // remove old continuations for (auto pair : converted) { @@ -57,31 +58,59 @@ class ClosureConversion { } } - void convert_jump(Continuation* continuation) { - assert(continuation->has_body()); - auto body = continuation->body(); - // prevent conversion of calls to vectorize() or cuda(), but allow graph intrinsics + //Convert jump and all arguments. + void convert_jump(Continuation* source, Continuation* target) { + assert(source); + assert(target); + + if (!converted_.emplace(source).second) + return; //Was already converted once before. + + assert(source->has_body()); + auto body = source->body(); + auto callee = body->callee()->isa_nom(); - if (callee == continuation) return; + + if (callee == source) { + target->jump(callee, body->args(), source->debug()); + return; + } + + // prevent conversion of calls to vectorize() or cuda(), but allow graph intrinsics if (!callee || !callee->is_intrinsic()) { 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)); - continuation->jump(convert(body->callee(), true), new_args, continuation->debug()); + target->jump(convert(body->callee(), true), new_args, source->debug()); + } else { + Array new_args(body->num_args()); + for (size_t i = 0, e = body->num_args(); i != e; ++i) { + if (body->arg(i)->type()->isa()) + new_args[i] = body->arg(i); + else if (callee->intrinsic() == Intrinsic::Match && i > 2) + new_args[i] = body->arg(i); + else + new_args[i] = convert(body->arg(i)); + } + target->jump(callee, new_args, source->debug()); } } const Def* convert(const Def* def, bool as_callee = false) { - if (new_defs_.count(def)) def = new_defs_[def]; - if (def->order() <= 1) - return def; + if (auto * source = def->isa_nom()) { + if (new_defs_.count(def)) def = new_defs_[def]; + auto continuation = def->isa_nom(); + assert(continuation); - if (auto continuation = def->isa_nom()) { - if (!continuation->has_body()) - return continuation; - convert_jump(continuation); if (as_callee) return continuation; + if (!source->has_body()) + return continuation; + if (continuation->order() <= 1) + return continuation; + + convert_jump(source, continuation); //convert_jump must be executed so that continuation has a body. + assert(continuation->has_body()); world_.WLOG("slow: closure generated for '{}'", continuation); @@ -148,6 +177,10 @@ class ClosureConversion { auto closure_type = convert(continuation->type()); return world_.closure(closure_type->as(), wrapper, thin_env ? free_vars[0] : world_.tuple(free_vars), continuation->debug()); } else { + if (new_defs_.count(def)) return new_defs_[def]; + if (def->isa() || def->isa()) + return def; + // TODO need to consider Params? Array ops(def->ops()); for (auto& op : ops) op = convert(op); @@ -201,6 +234,7 @@ class ClosureConversion { World& world_; Def2Def new_defs_; Type2Type new_types_; + ContinuationSet converted_; }; diff --git a/src/thorin/type.cpp b/src/thorin/type.cpp index 7918b0e45..9b8f8b49f 100644 --- a/src/thorin/type.cpp +++ b/src/thorin/type.cpp @@ -199,7 +199,7 @@ TypeTable::TypeTable() } const Type* TypeTable::tuple_type(Types ops) { - return ops.size() == 1 ? ops.front() : insert(*this, ops); + return (ops.size() == 1 && is_thin(ops[0])) ? ops.front() : insert(*this, ops); } const StructType* TypeTable::struct_type(Symbol name, size_t size) { diff --git a/src/thorin/world.cpp b/src/thorin/world.cpp index d2c9d17a2..f465fba27 100644 --- a/src/thorin/world.cpp +++ b/src/thorin/world.cpp @@ -1299,10 +1299,13 @@ void World::opt() { RUN_PASS(cleanup()) RUN_PASS(while (partial_evaluation(*this, true))); // lower2cff + RUN_PASS(verify(*this)); RUN_PASS(flatten_tuples(*this)) RUN_PASS(clone_bodies(*this)) RUN_PASS(split_slots(*this)) + RUN_PASS(verify(*this)); RUN_PASS(closure_conversion(*this)) + RUN_PASS(verify(*this)); RUN_PASS(lift_builtins(*this)) RUN_PASS(inliner(*this)) RUN_PASS(hoist_enters(*this)) From b14f556af512c65b00ccd8c2edf472d4a64a3c06 Mon Sep 17 00:00:00 2001 From: Matthias Kurtenacker Date: Fri, 16 Sep 2022 16:02:57 +0200 Subject: [PATCH 114/342] Initialize HashTable array before using it. --- src/thorin/util/hash.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/thorin/util/hash.h b/src/thorin/util/hash.h index 32bf6d6bd..007c7fbb4 100644 --- a/src/thorin/util/hash.h +++ b/src/thorin/util/hash.h @@ -217,6 +217,7 @@ class HashTable { HashTable() : capacity_(StackCapacity) , size_(0) + , array_() , nodes_(array_.data()) #if THORIN_ENABLE_CHECKS , id_(0) From 1476d1d6a8084fbe6ea70f462c76673570805905 Mon Sep 17 00:00:00 2001 From: Matthias Kurtenacker Date: Wed, 9 Nov 2022 15:44:26 +0100 Subject: [PATCH 115/342] Add json output backend module. No actual content generated for now. --- CMakeLists.txt | 6 ++++++ cmake/thorin-config.cmake.in | 1 + src/thorin/CMakeLists.txt | 11 ++++++++++ src/thorin/be/json/json.cpp | 10 +++++++++ src/thorin/be/json/json.h | 39 ++++++++++++++++++++++++++++++++++++ src/thorin/config.h.in | 1 + 6 files changed, 68 insertions(+) create mode 100644 src/thorin/be/json/json.cpp create mode 100644 src/thorin/be/json/json.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 60c708319..ea48e171c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -26,6 +26,12 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules") find_package(Half REQUIRED) message(STATUS "Building with Half library from ${Half_INCLUDE_DIRS}.") +# find json package for json output support. +find_package(nlohmann_json 3.2.0) +if(nlohmann_json_FOUND) + set(THORIN_ENABLE_JSON TRUE) +endif() + # check for possible llvm extension find_package(LLVM QUIET CONFIG) if(LLVM_FOUND) diff --git a/cmake/thorin-config.cmake.in b/cmake/thorin-config.cmake.in index c279f3127..b5fcc4719 100644 --- a/cmake/thorin-config.cmake.in +++ b/cmake/thorin-config.cmake.in @@ -28,6 +28,7 @@ list(APPEND CMAKE_MODULE_PATH "${Thorin_ROOT_DIR}/cmake/modules") find_path(Half_DIR NAMES half.hpp PATHS ${Half_DIR} $ENV{Half_DIR} "@Half_DIR@" "@Half_INCLUDE_DIR@") find_package(Half REQUIRED) +set(Thorin_HAS_JSON_SUPPORT @nlohmann_json_FOUND@) set(Thorin_HAS_LLVM_SUPPORT @LLVM_FOUND@) set(Thorin_HAS_RV_SUPPORT @RV_FOUND@) set(AnyDSL_LLVM_LINK_SHARED @AnyDSL_LLVM_LINK_SHARED@) diff --git a/src/thorin/CMakeLists.txt b/src/thorin/CMakeLists.txt index 944c785d2..ba6a2a106 100644 --- a/src/thorin/CMakeLists.txt +++ b/src/thorin/CMakeLists.txt @@ -107,6 +107,13 @@ if(LLVM_FOUND) ) endif() +if(nlohmann_json_FOUND) + list(APPEND THORIN_SOURCES + be/json/json.cpp + be/json/json.h + ) +endif() + add_library(thorin ${THORIN_SOURCES}) target_include_directories(thorin PUBLIC ${Half_INCLUDE_DIRS} ${Thorin_ROOT_DIR}/src ${CMAKE_BINARY_DIR}/include) @@ -121,3 +128,7 @@ if(LLVM_FOUND) endif() llvm_config(thorin ${AnyDSL_LLVM_LINK_SHARED} ${Thorin_LLVM_COMPONENTS}) endif() + +if(nlohmann_json_FOUND) + target_link_libraries(thorin PRIVATE nlohmann_json::nlohmann_json) +endif() diff --git a/src/thorin/be/json/json.cpp b/src/thorin/be/json/json.cpp new file mode 100644 index 000000000..a8d1b0f0d --- /dev/null +++ b/src/thorin/be/json/json.cpp @@ -0,0 +1,10 @@ +#include "json.h" + +namespace thorin::json { + +void CodeGen::emit_stream(std::ostream& stream) { + Stream s(stream); + s << "Currently not implemented\n"; +} + +} diff --git a/src/thorin/be/json/json.h b/src/thorin/be/json/json.h new file mode 100644 index 000000000..ee221ea96 --- /dev/null +++ b/src/thorin/be/json/json.h @@ -0,0 +1,39 @@ +#ifndef THORIN_BE_JSON_H +#define THORIN_BE_JSON_H + +#include +#include +#include + +#include "thorin/be/codegen.h" + +namespace thorin { + +class World; + +namespace json { + +using json = nlohmann::json; + +class CodeGen : public thorin::CodeGen { +public: + CodeGen(World& world, const Cont2Config& kernel_config, bool debug) + : thorin::CodeGen(world, debug) + , kernel_config_(kernel_config) + {} + + void emit_stream(std::ostream& stream) override; + + const char* file_ext() const override { + return ".thorin.json"; + } + +private: + const Cont2Config& kernel_config_; +}; + +} + +} + +#endif diff --git a/src/thorin/config.h.in b/src/thorin/config.h.in index 5e061fe28..9216ecd82 100644 --- a/src/thorin/config.h.in +++ b/src/thorin/config.h.in @@ -4,6 +4,7 @@ #cmakedefine01 THORIN_ENABLE_CHECKS #cmakedefine01 THORIN_ENABLE_PROFILING #cmakedefine01 THORIN_ENABLE_LLVM +#cmakedefine01 THORIN_ENABLE_JSON #cmakedefine01 THORIN_ENABLE_RV #endif From cc290203bfa5bea826e86237b9b502198d0017c5 Mon Sep 17 00:00:00 2001 From: Matthias Kurtenacker Date: Thu, 10 Nov 2022 17:19:13 +0100 Subject: [PATCH 116/342] Emit json files for some simple cases. --- src/thorin/be/json/json.cpp | 140 +++++++++++++++++++++++++++++++++++- 1 file changed, 139 insertions(+), 1 deletion(-) diff --git a/src/thorin/be/json/json.cpp b/src/thorin/be/json/json.cpp index a8d1b0f0d..6cb8eb346 100644 --- a/src/thorin/be/json/json.cpp +++ b/src/thorin/be/json/json.cpp @@ -2,9 +2,147 @@ namespace thorin::json { +class TypeTable { +public: + json type_table = json::array(); + + TypeMap known_types; + + std::string translate_type (const Type * type) { + auto it = known_types.find(type); + if (it != known_types.end()) { + return it->second; + } + + json result; + if (type->isa()) { + result["name"] = "mem_t"; + result["type"] = "mem"; + } else if (auto prim = type->isa()) { + result["name"] = "_" + std::to_string(type_table.size()); + result["length"] = prim->length(); + result["type"] = "prim"; + switch (prim->primtype_tag()) { +#define THORIN_ALL_TYPE(T, M) case PrimTypeTag::PrimType_##T: { result["tag"] = #T; break; } +#include + } + } else if (auto fntype = type->isa()) { + json arg_types = json::array(); + for (auto arg : fntype->ops()) { + arg_types.push_back(translate_type(arg)); + } + + result["type"] = "fn"; + result["name"] = "_" + std::to_string(type_table.size()); + result["args"] = arg_types; + } else if (auto ptrtype = type->isa()) { + auto pointee_type = translate_type(ptrtype->pointee()); + + result["type"] = "ptr"; + result["args"] = { pointee_type }; + result["name"] = pointee_type + "_p"; + result["length"] = ptrtype->length(); + } else if (auto arr = type->isa()) { + auto elem_type = translate_type(arr->elem_type()); + + result["type"] = "indef_array"; + result["args"] = { elem_type }; + result["name"] = elem_type + "_iarr"; + } else { + THORIN_UNREACHABLE; + } + known_types[type] = result["name"]; + type_table.push_back(result); + return result["name"]; + } +}; + +class DefTable { +public: + DefTable(TypeTable& type_table) : type_table_(type_table) {} + + json decl_table = json::array(); + json def_table = json::array(); + TypeTable& type_table_; + + DefMap known_defs; + + std::string translate_def (const Def * def, std::string expected_name = "") { + json result; + if (auto cont = def->isa()) { + auto type = type_table_.translate_type(def->type()); + auto name = expected_name != "" ? expected_name : "_" + std::to_string(def_table.size()); + json arg_names; + for (auto arg : cont->params()) { + arg_names.push_back(translate_def(arg)); + } + + json forward_decl; + forward_decl["name"] = name; + forward_decl["type"] = "continuation"; + forward_decl["fn_type"] = type; + forward_decl["arg_names"] = arg_names; + forward_decl["external"] = cont->is_external(); + decl_table.push_back(forward_decl); //TODO: should be pushed to the front instead. + + assert(cont->has_body()); + auto app = cont->body(); + auto target = translate_def(app->callee()); + json args = json::array(); + for (auto arg : app->args()) { + args.push_back(translate_def(arg)); + } + + result["name"] = name; + result["type"] = "continuation"; + result["app"] = { + {"target", target}, + {"args", args} + }; + } else if (auto lit = def->isa()) { + auto name = expected_name != "" ? expected_name : "_" + std::to_string(def_table.size()); + auto type = type_table_.translate_type(def->type()); + + 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. + } else if (auto param = def->isa()) { + auto name = expected_name != "" ? expected_name : param->continuation()->unique_name() + "." + std::to_string(param->index()); + known_defs[def] = name; + return name; + } else { + THORIN_UNREACHABLE; + } + known_defs[def] = result["name"]; + def_table.push_back(result); + return result["name"]; + } +}; + void CodeGen::emit_stream(std::ostream& stream) { + json j; + + j["module"] = world().name(); + + TypeTable type_table; + DefTable def_table(type_table); + + for (auto external : world().externals()) { + const Continuation* continuation = external.second; + auto expected_name = continuation->name(); + def_table.translate_def(continuation, expected_name); + } + + j["type_table"] = type_table.type_table; + j["defs"] = def_table.decl_table; + for (auto it : def_table.def_table) + j["defs"] += it; + + std::cerr << j.dump(2) << std::endl; + Stream s(stream); - s << "Currently not implemented\n"; + s << j.dump(2) << "\n"; } } From 373a6e0e562ac10477d90f9fcc5b37117ee2f203 Mon Sep 17 00:00:00 2001 From: Matthias Kurtenacker Date: Thu, 10 Nov 2022 18:00:08 +0100 Subject: [PATCH 117/342] [JSON]: Convert enought defs to convert a simple test program. --- src/thorin/be/json/json.cpp | 47 +++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/src/thorin/be/json/json.cpp b/src/thorin/be/json/json.cpp index 6cb8eb346..7eadba442 100644 --- a/src/thorin/be/json/json.cpp +++ b/src/thorin/be/json/json.cpp @@ -111,6 +111,53 @@ class DefTable { auto name = expected_name != "" ? expected_name : param->continuation()->unique_name() + "." + std::to_string(param->index()); known_defs[def] = name; return name; + } else if (auto load = def->isa()) { + auto name = expected_name != "" ? expected_name : "_" + std::to_string(def_table.size()); + json args = json::array(); + args.push_back(translate_def(load->mem())); + args.push_back(translate_def(load->ptr())); + + result["name"] = name; + result["type"] = "load"; + result["args"] = args; + } else if (auto cast = def->isa()) { + auto name = expected_name != "" ? expected_name : "_" + std::to_string(def_table.size()); + auto source = translate_def(cast->from()); + auto target_type = type_table_.translate_type(cast->type()); + + result["name"] = name; + result["type"] = "cast"; + result["source"] = source; + result["target_type"] = target_type; + } else if (auto lea = def->isa()) { + auto name = expected_name != "" ? expected_name : "_" + std::to_string(def_table.size()); + json args = json::array(); + args.push_back(translate_def(lea->ptr())); + args.push_back(translate_def(lea->index())); + + result["name"] = name; + result["type"] = "lea"; + result["args"] = args; + } else if (auto extract = def->isa()) { + auto name = expected_name != "" ? expected_name : "_" + std::to_string(def_table.size()); + json args = json::array(); + args.push_back(translate_def(extract->agg())); + args.push_back(translate_def(extract->index())); + + result["name"] = name; + result["type"] = "extract"; + result["args"] = args; + } else if (auto arithop = def->isa()) { + auto name = expected_name != "" ? expected_name : "_" + std::to_string(def_table.size()); + auto op = arithop->op_name(); + json args = json::array(); + args.push_back(translate_def(arithop->lhs())); + args.push_back(translate_def(arithop->rhs())); + + result["name"] = name; + result["type"] = "arithop"; + result["op"] = op; + result["args"] = args; } else { THORIN_UNREACHABLE; } From 841c2086d37f627e23a0139f31ef82f6d1d3910b Mon Sep 17 00:00:00 2001 From: Matthias Kurtenacker Date: Mon, 14 Nov 2022 15:18:40 +0100 Subject: [PATCH 118/342] [Json] various improvements: * Avoid name collisions, generate names after args have been created. * Querry known_defs to avoid multiple instances of the same definition. * Deal with intrinsics. Limited to branch and named intrinsics for now. * Create Cmp defs. --- src/thorin/be/json/json.cpp | 94 +++++++++++++++++++++++-------------- 1 file changed, 59 insertions(+), 35 deletions(-) diff --git a/src/thorin/be/json/json.cpp b/src/thorin/be/json/json.cpp index 7eadba442..0cd4837a6 100644 --- a/src/thorin/be/json/json.cpp +++ b/src/thorin/be/json/json.cpp @@ -68,37 +68,52 @@ class DefTable { DefMap known_defs; std::string translate_def (const Def * def, std::string expected_name = "") { + auto it = known_defs.find(def); + if (it != known_defs.end()) { + return it->second; + } + json result; if (auto cont = def->isa()) { - auto type = type_table_.translate_type(def->type()); - auto name = expected_name != "" ? expected_name : "_" + std::to_string(def_table.size()); - json arg_names; - for (auto arg : cont->params()) { - arg_names.push_back(translate_def(arg)); - } - - json forward_decl; - forward_decl["name"] = name; - forward_decl["type"] = "continuation"; - forward_decl["fn_type"] = type; - forward_decl["arg_names"] = arg_names; - forward_decl["external"] = cont->is_external(); - decl_table.push_back(forward_decl); //TODO: should be pushed to the front instead. - - assert(cont->has_body()); - auto app = cont->body(); - auto target = translate_def(app->callee()); - json args = json::array(); - for (auto arg : app->args()) { - args.push_back(translate_def(arg)); + if (cont->is_intrinsic()) { + assert(cont->intrinsic() == Intrinsic::Branch && "TODO: anything else is unsupported RN"); + + result["name"] = "branch"; + result["type"] = "continuation"; + result["intrinsic"] = "branch"; + } else { + assert(cont->has_body()); + + auto type = type_table_.translate_type(def->type()); + json arg_names = json::array(); + for (auto arg : cont->params()) { + arg_names.push_back(translate_def(arg)); + } + + auto name = expected_name != "" ? expected_name : "_cont_" + std::to_string(decl_table.size()); + + json forward_decl; + forward_decl["name"] = name; + forward_decl["type"] = "continuation"; + forward_decl["fn_type"] = type; + forward_decl["arg_names"] = arg_names; + forward_decl["external"] = cont->is_external(); + decl_table.push_back(forward_decl); + + auto app = cont->body(); + auto target = translate_def(app->callee()); + json args = json::array(); + for (auto arg : app->args()) { + args.push_back(translate_def(arg)); + } + + result["name"] = name; + result["type"] = "continuation"; + result["app"] = { + {"target", target}, + {"args", args} + }; } - - result["name"] = name; - result["type"] = "continuation"; - result["app"] = { - {"target", target}, - {"args", args} - }; } else if (auto lit = def->isa()) { auto name = expected_name != "" ? expected_name : "_" + std::to_string(def_table.size()); auto type = type_table_.translate_type(def->type()); @@ -112,52 +127,63 @@ class DefTable { known_defs[def] = name; return name; } else if (auto load = def->isa()) { - auto name = expected_name != "" ? expected_name : "_" + std::to_string(def_table.size()); json args = json::array(); args.push_back(translate_def(load->mem())); args.push_back(translate_def(load->ptr())); + auto name = expected_name != "" ? expected_name : "_" + std::to_string(def_table.size()); result["name"] = name; result["type"] = "load"; result["args"] = args; } else if (auto cast = def->isa()) { - auto name = expected_name != "" ? expected_name : "_" + std::to_string(def_table.size()); auto source = translate_def(cast->from()); auto target_type = type_table_.translate_type(cast->type()); + auto name = expected_name != "" ? expected_name : "_" + std::to_string(def_table.size()); result["name"] = name; result["type"] = "cast"; result["source"] = source; result["target_type"] = target_type; } else if (auto lea = def->isa()) { - auto name = expected_name != "" ? expected_name : "_" + std::to_string(def_table.size()); json args = json::array(); args.push_back(translate_def(lea->ptr())); args.push_back(translate_def(lea->index())); + auto name = expected_name != "" ? expected_name : "_" + std::to_string(def_table.size()); result["name"] = name; result["type"] = "lea"; result["args"] = args; } else if (auto extract = def->isa()) { - auto name = expected_name != "" ? expected_name : "_" + std::to_string(def_table.size()); json args = json::array(); args.push_back(translate_def(extract->agg())); args.push_back(translate_def(extract->index())); + auto name = expected_name != "" ? expected_name : "_" + std::to_string(def_table.size()); result["name"] = name; result["type"] = "extract"; result["args"] = args; } else if (auto arithop = def->isa()) { - auto name = expected_name != "" ? expected_name : "_" + std::to_string(def_table.size()); auto op = arithop->op_name(); json args = json::array(); args.push_back(translate_def(arithop->lhs())); args.push_back(translate_def(arithop->rhs())); + auto name = expected_name != "" ? expected_name : "_" + std::to_string(def_table.size()); result["name"] = name; result["type"] = "arithop"; result["op"] = op; result["args"] = args; + } else if (auto cmp = def->isa()) { + auto op = cmp->op_name(); + json args = json::array(); + args.push_back(translate_def(cmp->lhs())); + args.push_back(translate_def(cmp->rhs())); + auto name = expected_name != "" ? expected_name : "_" + std::to_string(def_table.size()); + + result["name"] = name; + result["type"] = "cmp"; + result["op"] = op; + result["args"] = args; } else { THORIN_UNREACHABLE; } @@ -186,8 +212,6 @@ void CodeGen::emit_stream(std::ostream& stream) { for (auto it : def_table.def_table) j["defs"] += it; - std::cerr << j.dump(2) << std::endl; - Stream s(stream); s << j.dump(2) << "\n"; } From acb6e139430a6ff535f1f6ab8ce55283339d2ed7 Mon Sep 17 00:00:00 2001 From: Matthias Kurtenacker Date: Tue, 15 Nov 2022 19:29:03 +0100 Subject: [PATCH 119/342] [Json] create additional defs and types: * Run * Hlt * Store * Enter * Slot * Definite Array Type Fix runaway recursion on continuation self refference. --- src/thorin/be/json/json.cpp | 64 +++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/src/thorin/be/json/json.cpp b/src/thorin/be/json/json.cpp index 0cd4837a6..0b9e42429 100644 --- a/src/thorin/be/json/json.cpp +++ b/src/thorin/be/json/json.cpp @@ -48,7 +48,16 @@ class TypeTable { result["type"] = "indef_array"; result["args"] = { elem_type }; result["name"] = elem_type + "_iarr"; + } else if (auto arr = type->isa()) { + auto elem_type = translate_type(arr->elem_type()); + + result["type"] = "def_array"; + result["args"] = { elem_type }; + result["length"] = arr->dim(); + result["name"] = elem_type + "_iarr"; } else { + std::cerr << "type cannot be translated\n"; + type->dump(); THORIN_UNREACHABLE; } known_types[type] = result["name"]; @@ -81,6 +90,15 @@ class DefTable { result["name"] = "branch"; result["type"] = "continuation"; result["intrinsic"] = "branch"; + } else if (cont->is_imported()) { + auto name = cont->name(); + auto type = type_table_.translate_type(def->type()); + + result["name"] = name; + result["type"] = "continuation"; + result["fn_type"] = type; + result["imported"] = true; + result["external"] = cont->is_external(); } else { assert(cont->has_body()); @@ -100,6 +118,8 @@ class DefTable { forward_decl["external"] = cont->is_external(); decl_table.push_back(forward_decl); + known_defs[def] = name; + auto app = cont->body(); auto target = translate_def(app->callee()); json args = json::array(); @@ -135,6 +155,16 @@ class DefTable { result["name"] = name; result["type"] = "load"; result["args"] = args; + } else if (auto store = def->isa()) { + json args = json::array(); + args.push_back(translate_def(store->mem())); + args.push_back(translate_def(store->ptr())); + args.push_back(translate_def(store->val())); + auto name = expected_name != "" ? expected_name : "_" + std::to_string(def_table.size()); + + result["name"] = name; + result["type"] = "store"; + result["args"] = args; } else if (auto cast = def->isa()) { auto source = translate_def(cast->from()); auto target_type = type_table_.translate_type(cast->type()); @@ -184,7 +214,41 @@ class DefTable { result["type"] = "cmp"; result["op"] = op; result["args"] = args; + } else if (auto run = def->isa()) { + auto name = expected_name != "" ? expected_name : "_" + std::to_string(def_table.size()); + auto target = translate_def(run->def()); + + result["name"] = name; + result["type"] = "run"; + result["target"] = target; + } else if (auto hlt = def->isa()) { + auto name = expected_name != "" ? expected_name : "_" + std::to_string(def_table.size()); + json args = json::array(); + auto target = translate_def(hlt->def()); + + result["name"] = name; + result["type"] = "hlt"; + result["target"] = target; + } else if (auto enter = def->isa()) { + auto name = expected_name != "" ? expected_name : "_" + std::to_string(def_table.size()); + json args = json::array(); + auto mem = translate_def(enter->mem()); + + result["name"] = name; + result["type"] = "enter"; + result["mem"] = mem; + } else if (auto slot = def->isa()) { + auto name = expected_name != "" ? expected_name : "_" + std::to_string(def_table.size()); + auto frame = translate_def(slot->frame()); + auto target_type = type_table_.translate_type(slot->alloced_type()); + + result["name"] = name; + result["type"] = "slot"; + result["frame"] = frame; + result["target_type"] = target_type; } else { + def->dump(2); + std::cerr << "cannot be translated\n"; THORIN_UNREACHABLE; } known_defs[def] = result["name"]; From 6c55fac7d5e0813190eba8da7c7e43c21a3cc471 Mon Sep 17 00:00:00 2001 From: Matthias Kurtenacker Date: Thu, 17 Nov 2022 13:15:15 +0100 Subject: [PATCH 120/342] [Json] More defs added: * Insert * Bitcast * Global * Aggregates: Closure, StructAgg, Tuple, Vector * Filter * Known * Alloc * Select * Top, Bottom * SizeOf, AlignOf * DefiniteArray, IndefiniteArray --- src/thorin/be/json/json.cpp | 182 ++++++++++++++++++++++++++++++++++-- 1 file changed, 174 insertions(+), 8 deletions(-) diff --git a/src/thorin/be/json/json.cpp b/src/thorin/be/json/json.cpp index 0b9e42429..e28531161 100644 --- a/src/thorin/be/json/json.cpp +++ b/src/thorin/be/json/json.cpp @@ -136,12 +136,34 @@ class DefTable { } } else if (auto lit = def->isa()) { auto name = expected_name != "" ? expected_name : "_" + std::to_string(def_table.size()); - auto type = type_table_.translate_type(def->type()); + auto type = type_table_.translate_type(lit->type()); 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. + //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; } +#include + default: + assert(false && "not implemented"); + } + } else if (def->isa()) { + auto name = expected_name != "" ? expected_name : "_" + std::to_string(def_table.size()); + auto type = type_table_.translate_type(def->type()); + + result["name"] = name; + result["type"] = "top"; + result["const_type"] = type; + } else if (def->isa()) { + auto name = expected_name != "" ? expected_name : "_" + std::to_string(def_table.size()); + auto type = type_table_.translate_type(def->type()); + + result["name"] = name; + result["type"] = "bottom"; + result["const_type"] = type; } else if (auto param = def->isa()) { auto name = expected_name != "" ? expected_name : param->continuation()->unique_name() + "." + std::to_string(param->index()); known_defs[def] = name; @@ -165,6 +187,20 @@ class DefTable { result["name"] = name; result["type"] = "store"; result["args"] = args; + } else if (auto size_of = def->isa()) { + auto target_type = type_table_.translate_type(size_of->of()); + auto name = expected_name != "" ? expected_name : "_" + std::to_string(def_table.size()); + + result["name"] = name; + result["type"] = "sizeof"; + result["target_type"] = target_type; + } else if (auto align_of = def->isa()) { + auto target_type = type_table_.translate_type(align_of->of()); + auto name = expected_name != "" ? expected_name : "_" + std::to_string(def_table.size()); + + result["name"] = name; + result["type"] = "alignof"; + result["target_type"] = target_type; } else if (auto cast = def->isa()) { auto source = translate_def(cast->from()); auto target_type = type_table_.translate_type(cast->type()); @@ -174,6 +210,37 @@ class DefTable { result["type"] = "cast"; result["source"] = source; result["target_type"] = target_type; + } else if (auto bitcast = def->isa()) { + auto source = translate_def(bitcast->from()); + auto target_type = type_table_.translate_type(bitcast->type()); + auto name = expected_name != "" ? expected_name : "_" + std::to_string(def_table.size()); + + result["name"] = name; + result["type"] = "bitcast"; + result["source"] = source; + result["target_type"] = target_type; + } else if (auto indef_array = def->isa()) { + auto dim = translate_def(indef_array->op(0)); + auto name = expected_name != "" ? expected_name : "_" + std::to_string(def_table.size()); + auto element_type = type_table_.translate_type(indef_array->elem_type()); + + result["name"] = name; + result["type"] = "indef_array"; + result["elem_type"] = element_type; + result["dim"] = dim; + } else if (auto def_array = def->isa()) { + json args = json::array(); + for (auto arg : def_array->ops()) { + args.push_back(translate_def(arg)); + } + + auto name = expected_name != "" ? expected_name : "_" + std::to_string(def_table.size()); + auto element_type = type_table_.translate_type(def_array->elem_type()); + + result["name"] = name; + result["type"] = "def_array"; + result["elem_type"] = element_type; + result["args"] = args; } else if (auto lea = def->isa()) { json args = json::array(); args.push_back(translate_def(lea->ptr())); @@ -192,6 +259,69 @@ class DefTable { result["name"] = name; result["type"] = "extract"; result["args"] = args; + } else if (auto insert = def->isa()) { + json args = json::array(); + args.push_back(translate_def(insert->agg())); + args.push_back(translate_def(insert->index())); + args.push_back(translate_def(insert->value())); + auto name = expected_name != "" ? expected_name : "_" + std::to_string(def_table.size()); + + result["name"] = name; + result["type"] = "insert"; + result["args"] = args; + } else if (auto closure = def->isa()) { + json args = json::array(); + args.push_back(translate_def(closure->op(0))); + args.push_back(translate_def(closure->op(1))); + auto closure_type = type_table_.translate_type(closure->type()); + auto name = expected_name != "" ? expected_name : "_" + std::to_string(def_table.size()); + + result["name"] = name; + result["type"] = "closure"; + result["args"] = args; + result["closure_type"] = closure_type; + } else if (auto struct_agg = def->isa()) { + json args = json::array(); + for (auto arg : struct_agg->ops()) { + args.push_back(translate_def(arg)); + } + auto struct_type = type_table_.translate_type(struct_agg->type()); + auto name = expected_name != "" ? expected_name : "_" + std::to_string(def_table.size()); + + result["name"] = name; + result["type"] = "struct"; + result["args"] = args; + result["struct_type"] = struct_type; + } else if (auto tuple = def->isa()) { + json args = json::array(); + for (auto arg : tuple->ops()) { + args.push_back(translate_def(arg)); + } + auto name = expected_name != "" ? expected_name : "_" + std::to_string(def_table.size()); + + result["name"] = name; + result["type"] = "tuple"; + result["args"] = args; + } else if (auto vector = def->isa()) { + json args = json::array(); + for (auto arg : vector->ops()) { + args.push_back(translate_def(arg)); + } + auto name = expected_name != "" ? expected_name : "_" + std::to_string(def_table.size()); + + result["name"] = name; + result["type"] = "vector"; + result["args"] = args; + } else if (auto filter = def->isa()) { + json args = json::array(); + for (auto arg : filter->ops()) { + args.push_back(translate_def(arg)); + } + auto name = expected_name != "" ? expected_name : "_" + std::to_string(def_table.size()); + + result["name"] = name; + result["type"] = "filter"; + result["args"] = args; } else if (auto arithop = def->isa()) { auto op = arithop->op_name(); json args = json::array(); @@ -203,6 +333,16 @@ class DefTable { result["type"] = "arithop"; result["op"] = op; result["args"] = args; + } else if (auto select = def->isa()) { json args = json::array(); args.push_back(translate_def(select->cond())); @@ -411,6 +488,79 @@ class DefTable { result["type"] = "global"; result["init"] = init; result["mutable"] = is_mutable; + } else if (auto variant = def->isa()) { + auto variant_type = type_table_.translate_type(variant->type()); + auto value = translate_def(variant->value()); + size_t index = variant->index(); + auto name = expected_name != "" ? expected_name : "_" + std::to_string(def_table.size()); + + result["name"] = name; + result["type"] = "variant"; + result["variant_type"] = variant_type; + result["value"] = value; + result["index"] = index; + } else if (auto variant_extract = def->isa()) { + auto value = translate_def(variant_extract->value()); + size_t index = variant_extract->index(); + auto name = expected_name != "" ? expected_name : "_" + std::to_string(def_table.size()); + + result["name"] = name; + result["type"] = "variant_extract"; + result["value"] = value; + result["index"] = index; + } else if (auto variant_index = def->isa()) { + auto value = translate_def(variant_index->op(0)); + auto name = expected_name != "" ? expected_name : "_" + std::to_string(def_table.size()); + + result["name"] = name; + result["type"] = "variant_index"; + result["value"] = value; + } else if (auto assembly = def->isa()) { + auto asm_type = type_table_.translate_type(assembly->type()); + json inputs = json::array(); + for (auto input : assembly->inputs()) { + inputs.push_back(translate_def(input)); + } + auto asm_template = assembly->asm_template(); + json out_constraints = json::array(); + for (auto constraint : assembly->output_constraints()) { + out_constraints.push_back(constraint); + } + json in_constraints = json::array(); + for (auto constraint : assembly->input_constraints()) { + in_constraints.push_back(constraint); + } + json clobbers = json::array(); + for (auto c : assembly->clobbers()) { + clobbers.push_back(c); + } + + auto name = expected_name != "" ? expected_name : "_" + std::to_string(def_table.size()); + + result["name"] = name; + result["type"] = "assembly"; + + result["asm_type"] = asm_type; + result["inputs"] = inputs; + result["asm_template"] = asm_template; + + result["output_constraints"] = out_constraints; + result["input_constraints"] = in_constraints; + result["clobbers"] = clobbers; + switch (assembly->flags()) { + case Assembly::Flags::NoFlag: + result["flags"] = "noflag"; + break; + case Assembly::Flags::HasSideEffects: + result["flags"] = "hassideeffects"; + break; + case Assembly::Flags::IsAlignStack: + result["flags"] = "isalignstack"; + break; + case Assembly::Flags::IsIntelDialect: + result["flags"] = "isinteldialect"; + break; + } } else { def->dump(); def->dump(2); From c7dc7b8d1ed8b53c9c492ef1565ac0550fcd83f5 Mon Sep 17 00:00:00 2001 From: Matthias Kurtenacker Date: Wed, 23 Nov 2022 13:16:14 +0100 Subject: [PATCH 122/342] [Json]: Improved continuation generation: * Support named intrinsics. * Generate filters. * Improve support for external functions. * Remove expected_name from defs generation. --- src/thorin/be/json/json.cpp | 141 +++++++++++++++++++----------------- 1 file changed, 75 insertions(+), 66 deletions(-) diff --git a/src/thorin/be/json/json.cpp b/src/thorin/be/json/json.cpp index ff9905558..db13db2ce 100644 --- a/src/thorin/be/json/json.cpp +++ b/src/thorin/be/json/json.cpp @@ -126,7 +126,7 @@ class DefTable { DefMap known_defs; - std::string translate_def (const Def * def, std::string expected_name = "") { + std::string translate_def (const Def * def) { auto it = known_defs.find(def); if (it != known_defs.end()) { return it->second; @@ -145,7 +145,7 @@ class DefTable { size_t num_patterns = cont->num_params() - 2; auto variant_type = type_table_.translate_type(cont->param(0)->type()); - auto name = expected_name != "" ? expected_name : "_match_" + std::to_string(def_table.size()); + auto name = "_match_" + std::to_string(def_table.size()); result["name"] = name; result["type"] = "continuation"; @@ -153,54 +153,64 @@ class DefTable { result["variant_type"] = variant_type; result["num_patterns"] = num_patterns; } else { - assert(false && "TODO: only Branch and Match supported RN"); + auto intrinsic_name = cont->name(); + auto intrinsic_type = type_table_.translate_type(cont->type()); + auto name = "_in_" + std::to_string(def_table.size()); + + result["name"] = name; + result["type"] = "continuation"; + result["intrinsic"] = intrinsic_name; + result["fn_type"] = intrinsic_type; } - } else if (cont->is_imported()) { - auto name = cont->name(); + if (cont->filter() && !cont->filter()->empty()) + result["filter"] = translate_def(cont->filter()); + } else { auto type = type_table_.translate_type(def->type()); - result["name"] = name; - result["type"] = "continuation"; - result["fn_type"] = type; - result["imported"] = true; - result["external"] = cont->is_external(); - } else { - assert(cont->has_body()); + //Make the name available in known_defs as early as possible to prevent recursion issues. + auto name = "_cont_" + std::to_string(decl_table.size()); + known_defs[def] = name; - auto type = type_table_.translate_type(def->type()); + //TODO: Is this actually required for imported functions? json arg_names = json::array(); for (auto arg : cont->params()) { arg_names.push_back(translate_def(arg)); } - auto name = expected_name != "" ? expected_name : "_cont_" + std::to_string(decl_table.size()); - json forward_decl; forward_decl["name"] = name; forward_decl["type"] = "continuation"; forward_decl["fn_type"] = type; forward_decl["arg_names"] = arg_names; - forward_decl["external"] = cont->is_external(); + if (cont->is_external()) + forward_decl["external"] = cont->name(); decl_table.push_back(forward_decl); - known_defs[def] = name; + if(cont->has_body()) { + auto app = cont->body(); + auto target = translate_def(app->callee()); + json args = json::array(); + for (auto arg : app->args()) { + args.push_back(translate_def(arg)); + } - auto app = cont->body(); - auto target = translate_def(app->callee()); - json args = json::array(); - for (auto arg : app->args()) { - args.push_back(translate_def(arg)); + result["name"] = name; + result["type"] = "continuation"; + if (cont->filter() && !cont->filter()->empty()) + result["filter"] = translate_def(cont->filter()); + result["app"] = { + {"target", target}, + {"args", args} + }; + } 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; } - - result["name"] = name; - result["type"] = "continuation"; - result["app"] = { - {"target", target}, - {"args", args} - }; } } else if (auto lit = def->isa()) { - auto name = expected_name != "" ? expected_name : "_" + std::to_string(def_table.size()); + auto name = "_" + std::to_string(def_table.size()); auto type = type_table_.translate_type(lit->type()); result["name"] = name; @@ -216,28 +226,28 @@ class DefTable { assert(false && "not implemented"); } } else if (def->isa()) { - auto name = expected_name != "" ? expected_name : "_" + std::to_string(def_table.size()); + auto name = "_" + std::to_string(def_table.size()); auto type = type_table_.translate_type(def->type()); result["name"] = name; result["type"] = "top"; result["const_type"] = type; } else if (def->isa()) { - auto name = expected_name != "" ? expected_name : "_" + std::to_string(def_table.size()); + auto name = "_" + std::to_string(def_table.size()); auto type = type_table_.translate_type(def->type()); result["name"] = name; result["type"] = "bottom"; result["const_type"] = type; } else if (auto param = def->isa()) { - auto name = expected_name != "" ? expected_name : param->continuation()->unique_name() + "." + std::to_string(param->index()); + auto name = param->continuation()->unique_name() + "." + std::to_string(param->index()); known_defs[def] = name; return name; } else if (auto load = def->isa()) { json args = json::array(); args.push_back(translate_def(load->mem())); args.push_back(translate_def(load->ptr())); - auto name = expected_name != "" ? expected_name : "_" + std::to_string(def_table.size()); + auto name = "_" + std::to_string(def_table.size()); result["name"] = name; result["type"] = "load"; @@ -247,21 +257,21 @@ class DefTable { args.push_back(translate_def(store->mem())); args.push_back(translate_def(store->ptr())); args.push_back(translate_def(store->val())); - auto name = expected_name != "" ? expected_name : "_" + std::to_string(def_table.size()); + auto name = "_" + std::to_string(def_table.size()); result["name"] = name; result["type"] = "store"; result["args"] = args; } else if (auto size_of = def->isa()) { auto target_type = type_table_.translate_type(size_of->of()); - auto name = expected_name != "" ? expected_name : "_" + std::to_string(def_table.size()); + auto name = "_" + std::to_string(def_table.size()); result["name"] = name; result["type"] = "sizeof"; result["target_type"] = target_type; } else if (auto align_of = def->isa()) { auto target_type = type_table_.translate_type(align_of->of()); - auto name = expected_name != "" ? expected_name : "_" + std::to_string(def_table.size()); + auto name = "_" + std::to_string(def_table.size()); result["name"] = name; result["type"] = "alignof"; @@ -269,7 +279,7 @@ class DefTable { } else if (auto cast = def->isa()) { auto source = translate_def(cast->from()); auto target_type = type_table_.translate_type(cast->type()); - auto name = expected_name != "" ? expected_name : "_" + std::to_string(def_table.size()); + auto name = "_" + std::to_string(def_table.size()); result["name"] = name; result["type"] = "cast"; @@ -278,7 +288,7 @@ class DefTable { } else if (auto bitcast = def->isa()) { auto source = translate_def(bitcast->from()); auto target_type = type_table_.translate_type(bitcast->type()); - auto name = expected_name != "" ? expected_name : "_" + std::to_string(def_table.size()); + auto name = "_" + std::to_string(def_table.size()); result["name"] = name; result["type"] = "bitcast"; @@ -286,7 +296,7 @@ class DefTable { result["target_type"] = target_type; } else if (auto indef_array = def->isa()) { auto dim = translate_def(indef_array->op(0)); - auto name = expected_name != "" ? expected_name : "_" + std::to_string(def_table.size()); + auto name = "_" + std::to_string(def_table.size()); auto element_type = type_table_.translate_type(indef_array->elem_type()); result["name"] = name; @@ -299,7 +309,7 @@ class DefTable { args.push_back(translate_def(arg)); } - auto name = expected_name != "" ? expected_name : "_" + std::to_string(def_table.size()); + auto name = "_" + std::to_string(def_table.size()); auto element_type = type_table_.translate_type(def_array->elem_type()); result["name"] = name; @@ -310,7 +320,7 @@ class DefTable { json args = json::array(); args.push_back(translate_def(lea->ptr())); args.push_back(translate_def(lea->index())); - auto name = expected_name != "" ? expected_name : "_" + std::to_string(def_table.size()); + auto name = "_" + std::to_string(def_table.size()); result["name"] = name; result["type"] = "lea"; @@ -319,7 +329,7 @@ class DefTable { json args = json::array(); args.push_back(translate_def(extract->agg())); args.push_back(translate_def(extract->index())); - auto name = expected_name != "" ? expected_name : "_" + std::to_string(def_table.size()); + auto name = "_" + std::to_string(def_table.size()); result["name"] = name; result["type"] = "extract"; @@ -329,7 +339,7 @@ class DefTable { args.push_back(translate_def(insert->agg())); args.push_back(translate_def(insert->index())); args.push_back(translate_def(insert->value())); - auto name = expected_name != "" ? expected_name : "_" + std::to_string(def_table.size()); + auto name = "_" + std::to_string(def_table.size()); result["name"] = name; result["type"] = "insert"; @@ -339,7 +349,7 @@ class DefTable { args.push_back(translate_def(closure->op(0))); args.push_back(translate_def(closure->op(1))); auto closure_type = type_table_.translate_type(closure->type()); - auto name = expected_name != "" ? expected_name : "_" + std::to_string(def_table.size()); + auto name = "_" + std::to_string(def_table.size()); result["name"] = name; result["type"] = "closure"; @@ -351,7 +361,7 @@ class DefTable { args.push_back(translate_def(arg)); } auto struct_type = type_table_.translate_type(struct_agg->type()); - auto name = expected_name != "" ? expected_name : "_" + std::to_string(def_table.size()); + auto name = "_" + std::to_string(def_table.size()); result["name"] = name; result["type"] = "struct"; @@ -362,7 +372,7 @@ class DefTable { for (auto arg : tuple->ops()) { args.push_back(translate_def(arg)); } - auto name = expected_name != "" ? expected_name : "_" + std::to_string(def_table.size()); + auto name = "_" + std::to_string(def_table.size()); result["name"] = name; result["type"] = "tuple"; @@ -372,7 +382,7 @@ class DefTable { for (auto arg : vector->ops()) { args.push_back(translate_def(arg)); } - auto name = expected_name != "" ? expected_name : "_" + std::to_string(def_table.size()); + auto name = "_" + std::to_string(def_table.size()); result["name"] = name; result["type"] = "vector"; @@ -382,7 +392,7 @@ class DefTable { for (auto arg : filter->ops()) { args.push_back(translate_def(arg)); } - auto name = expected_name != "" ? expected_name : "_" + std::to_string(def_table.size()); + auto name = "_" + std::to_string(def_table.size()); result["name"] = name; result["type"] = "filter"; @@ -392,7 +402,7 @@ class DefTable { json args = json::array(); args.push_back(translate_def(arithop->lhs())); args.push_back(translate_def(arithop->rhs())); - auto name = expected_name != "" ? expected_name : "_" + std::to_string(def_table.size()); + auto name = "_" + std::to_string(def_table.size()); result["name"] = name; result["type"] = "arithop"; @@ -404,7 +414,7 @@ class DefTable { for (auto arg : mathop->ops()) { args.push_back(translate_def(arg)); } - auto name = expected_name != "" ? expected_name : "_" + std::to_string(def_table.size()); + auto name = "_" + std::to_string(def_table.size()); result["name"] = name; result["type"] = "mathop"; @@ -415,7 +425,7 @@ class DefTable { args.push_back(translate_def(select->cond())); args.push_back(translate_def(select->tval())); args.push_back(translate_def(select->fval())); - auto name = expected_name != "" ? expected_name : "_" + std::to_string(def_table.size()); + auto name = "_" + std::to_string(def_table.size()); result["name"] = name; result["type"] = "select"; @@ -425,7 +435,7 @@ class DefTable { json args = json::array(); args.push_back(translate_def(cmp->lhs())); args.push_back(translate_def(cmp->rhs())); - auto name = expected_name != "" ? expected_name : "_" + std::to_string(def_table.size()); + auto name = "_" + std::to_string(def_table.size()); result["name"] = name; result["type"] = "cmp"; @@ -433,28 +443,28 @@ class DefTable { result["args"] = args; } else if (auto run = def->isa()) { auto target = translate_def(run->def()); - auto name = expected_name != "" ? expected_name : "_" + std::to_string(def_table.size()); + auto name = "_" + std::to_string(def_table.size()); result["name"] = name; result["type"] = "run"; result["target"] = target; } else if (auto hlt = def->isa()) { auto target = translate_def(hlt->def()); - auto name = expected_name != "" ? expected_name : "_" + std::to_string(def_table.size()); + auto name = "_" + std::to_string(def_table.size()); result["name"] = name; result["type"] = "hlt"; result["target"] = target; } else if (auto known = def->isa()) { auto def = translate_def(known->def()); - auto name = expected_name != "" ? expected_name : "_" + std::to_string(def_table.size()); + auto name = "_" + std::to_string(def_table.size()); result["name"] = name; result["type"] = "known"; result["def"] = def; } else if (auto enter = def->isa()) { auto mem = translate_def(enter->mem()); - auto name = expected_name != "" ? expected_name : "_" + std::to_string(def_table.size()); + auto name = "_" + std::to_string(def_table.size()); result["name"] = name; result["type"] = "enter"; @@ -462,7 +472,7 @@ class DefTable { } else if (auto slot = def->isa()) { auto frame = translate_def(slot->frame()); auto target_type = type_table_.translate_type(slot->alloced_type()); - auto name = expected_name != "" ? expected_name : "_" + std::to_string(def_table.size()); + auto name = "_" + std::to_string(def_table.size()); result["name"] = name; result["type"] = "slot"; @@ -473,7 +483,7 @@ class DefTable { args.push_back(translate_def(alloc->mem())); args.push_back(translate_def(alloc->extra())); auto target_type = type_table_.translate_type(alloc->alloced_type()); - auto name = expected_name != "" ? expected_name : "_" + std::to_string(def_table.size()); + auto name = "_" + std::to_string(def_table.size()); result["name"] = name; result["type"] = "alloc"; @@ -482,7 +492,7 @@ class DefTable { } else if (auto global = def->isa()) { auto init = translate_def(global->init()); bool is_mutable = global->is_mutable(); - auto name = expected_name != "" ? expected_name : "_" + std::to_string(def_table.size()); + auto name = "_" + std::to_string(def_table.size()); result["name"] = name; result["type"] = "global"; @@ -492,7 +502,7 @@ class DefTable { auto variant_type = type_table_.translate_type(variant->type()); auto value = translate_def(variant->value()); size_t index = variant->index(); - auto name = expected_name != "" ? expected_name : "_" + std::to_string(def_table.size()); + auto name = "_" + std::to_string(def_table.size()); result["name"] = name; result["type"] = "variant"; @@ -502,7 +512,7 @@ class DefTable { } else if (auto variant_extract = def->isa()) { auto value = translate_def(variant_extract->value()); size_t index = variant_extract->index(); - auto name = expected_name != "" ? expected_name : "_" + std::to_string(def_table.size()); + auto name = "_" + std::to_string(def_table.size()); result["name"] = name; result["type"] = "variant_extract"; @@ -510,7 +520,7 @@ class DefTable { result["index"] = index; } else if (auto variant_index = def->isa()) { auto value = translate_def(variant_index->op(0)); - auto name = expected_name != "" ? expected_name : "_" + std::to_string(def_table.size()); + auto name = "_" + std::to_string(def_table.size()); result["name"] = name; result["type"] = "variant_index"; @@ -535,7 +545,7 @@ class DefTable { clobbers.push_back(c); } - auto name = expected_name != "" ? expected_name : "_" + std::to_string(def_table.size()); + auto name = "_" + std::to_string(def_table.size()); result["name"] = name; result["type"] = "assembly"; @@ -583,8 +593,7 @@ void CodeGen::emit_stream(std::ostream& stream) { for (auto external : world().externals()) { const Continuation* continuation = external.second; - auto expected_name = continuation->name(); - def_table.translate_def(continuation, expected_name); + def_table.translate_def(continuation); } j["type_table"] = type_table.type_table; From 09853d507d543dd312bf8a8fdee76fef228aa965 Mon Sep 17 00:00:00 2001 From: Matthias Kurtenacker Date: Thu, 24 Nov 2022 17:47:12 +0100 Subject: [PATCH 123/342] [Json]: Bugfix: Assembly needs a memory input. --- src/thorin/be/json/json.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/thorin/be/json/json.cpp b/src/thorin/be/json/json.cpp index db13db2ce..39d3ca96d 100644 --- a/src/thorin/be/json/json.cpp +++ b/src/thorin/be/json/json.cpp @@ -528,6 +528,7 @@ class DefTable { } else if (auto assembly = def->isa()) { auto asm_type = type_table_.translate_type(assembly->type()); json inputs = json::array(); + inputs.push_back(translate_def(assembly->mem())); for (auto input : assembly->inputs()) { inputs.push_back(translate_def(input)); } From a44eff8d7d299be41a9d30e6d5712c794cbe4311 Mon Sep 17 00:00:00 2001 From: Matthias Kurtenacker Date: Fri, 25 Nov 2022 14:27:51 +0100 Subject: [PATCH 124/342] [Json]: Add device and addrspace to pointer types. --- src/thorin/be/json/json.cpp | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/src/thorin/be/json/json.cpp b/src/thorin/be/json/json.cpp index 39d3ca96d..612e6d3ba 100644 --- a/src/thorin/be/json/json.cpp +++ b/src/thorin/be/json/json.cpp @@ -100,11 +100,31 @@ class TypeTable { } } else if (auto ptrtype = type->isa()) { auto pointee_type = translate_type(ptrtype->pointee()); + auto device = ptrtype->device(); result["type"] = "ptr"; result["args"] = { pointee_type }; - result["name"] = pointee_type + "_p"; + result["name"] = pointee_type + "_p_" + std::to_string(type_table.size()); result["length"] = ptrtype->length(); + if (device != -1) + result["device"] = device; + switch (ptrtype->addr_space()) { + case AddrSpace::Generic: + //result["addrspace"] = "generic"; //Default + break; + case AddrSpace::Global: + result["addrspace"] = "global"; + break; + case AddrSpace::Texture: + result["addrspace"] = "texture"; + break; + case AddrSpace::Shared: + result["addrspace"] = "shared"; + break; + case AddrSpace::Constant: + result["addrspace"] = "constant"; + break; + } } else { std::cerr << "type cannot be translated\n"; type->dump(); From 7667117ac71c9a54e144cb43b1defb9decd3d54d Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Tue, 29 Nov 2022 14:29:11 +0100 Subject: [PATCH 125/342] updated to latest shady API --- src/thorin/be/codegen.cpp | 2 ++ src/thorin/be/shady/shady.cpp | 60 +++++++++++++++++++---------------- 2 files changed, 35 insertions(+), 27 deletions(-) diff --git a/src/thorin/be/codegen.cpp b/src/thorin/be/codegen.cpp index 517cb0232..b804fa6e6 100644 --- a/src/thorin/be/codegen.cpp +++ b/src/thorin/be/codegen.cpp @@ -10,6 +10,8 @@ #endif #if THORIN_ENABLE_SHADY #include "thorin/be/shady/shady.h" +#undef empty +#undef nodes #endif #include "thorin/be/c/c.h" diff --git a/src/thorin/be/shady/shady.cpp b/src/thorin/be/shady/shady.cpp index 4da148a44..7ca15a317 100644 --- a/src/thorin/be/shady/shady.cpp +++ b/src/thorin/be/shady/shady.cpp @@ -1,4 +1,5 @@ #include "shady.h" +#undef empty #include "thorin/analyses/scope.h" #include "thorin/transform/structurize.h" @@ -15,7 +16,7 @@ void CodeGen::emit_stream(std::ostream& out) { assert(!module); - shady::ArenaConfig config = { 0 }; + shady::ArenaConfig config = { }; config.check_types = true; arena = shady::new_ir_arena(config); module = shady::new_module(arena, world().name().c_str()); @@ -225,7 +226,7 @@ void CodeGen::prepare(Continuation* cont, shady::Node*) { } else assert(bb.head); - bb.builder = shady::begin_body(arena); + bb.builder = shady::begin_body(module); } static std::optional is_shady_prim_op(const Continuation* cont) { @@ -280,38 +281,43 @@ void CodeGen::emit_epilogue(Continuation* cont) { bb.terminator = shady::jump(arena, payload); } else if (auto intrinsic = body->callee()->isa_nom(); intrinsic && intrinsic->is_intrinsic()) { assert(false); - } else if (auto callee = body->callee()->isa_nom()) { - int ret_param = find_return_parameter(callee->type()); + } else { + int ret_param = find_return_parameter(body->callee()->type()->as()); + // TODO handle tail calls ? assert(ret_param >= 0); args.erase(args.begin() + ret_param); args.erase(std::remove_if(args.begin(), args.end(), [&](const auto& item){ return item == nullptr || !shady::is_value(item); }), args.end()); - // shady primop called as imported continuations look like continuation calls to thorin, but not to shady - // we just need to carefully emit the primop as an instruction, then jump to the target BB, passing the stuff as we do - if (auto op = is_shady_prim_op(callee); op.has_value()) { - shady::bind_instruction(bb.builder, shady::prim_op(arena, (shady::PrimOp) { - .op = op.value(), - .type_arguments = empty(arena), - .operands = vec2nodes(args), - })); - shady::Jump jump; - jump.target = args[ret_param]; - bb.terminator = shady::jump(arena, jump); - return; - } + const shady::Node* call; + if (auto callee = body->callee()->isa_nom()) { + // shady primop called as imported continuations look like continuation calls to thorin, but not to shady + // we just need to carefully emit the primop as an instruction, then jump to the target BB, passing the stuff as we do + if (auto op = is_shady_prim_op(callee); op.has_value()) { + shady::bind_instruction(bb.builder, shady::prim_op(arena, (shady::PrimOp) { + .op = op.value(), + .type_arguments = shady::nodes(arena, 0, nullptr), + .operands = vec2nodes(args), + })); + shady::Jump jump; + jump.target = args[ret_param]; + bb.terminator = shady::jump(arena, jump); + return; + } - shady::Call payload; - payload.args = vec2nodes(args); - payload.callee = emit(callee); - auto call = shady::call_instr(arena, payload); + shady::LeafCall payload; + payload.args = vec2nodes(args); + payload.callee = emit(callee); + call = shady::leaf_call(arena, payload); + } else { + shady::IndirectCall payload; + payload.args = vec2nodes(args); + payload.callee = emit(callee); + call = shady::indirect_call(arena, payload); + } - shady::LetInto payload2; - payload2.instruction = call; - payload2.tail = args[ret_param]; - bb.terminator = shady::let_into(arena, payload2); - } else { - assert(false); + assert(args[ret_param]->tag == shady::BasicBlock_TAG); + bb.terminator = shady::let_into(arena, call, args[ret_param]); } } From 4357af61b41651ce2c5a6628f0e04b787650df6a Mon Sep 17 00:00:00 2001 From: Matthias Kurtenacker Date: Tue, 29 Nov 2022 16:02:46 +0100 Subject: [PATCH 126/342] Mark Generated Device Code with the correct name to call. --- src/thorin/be/codegen.cpp | 4 +++- src/thorin/be/json/json.cpp | 8 ++++++++ src/thorin/be/json/json.h | 11 +++++++---- src/thorin/be/llvm/runtime.cpp | 3 ++- src/thorin/continuation.h | 5 +++-- 5 files changed, 23 insertions(+), 8 deletions(-) diff --git a/src/thorin/be/codegen.cpp b/src/thorin/be/codegen.cpp index db6f6d802..0e82fe2ce 100644 --- a/src/thorin/be/codegen.cpp +++ b/src/thorin/be/codegen.cpp @@ -26,7 +26,7 @@ static void get_kernel_configs( Continuation* imported = nullptr; for (auto [_, exported] : externals) { if (!exported->has_body()) continue; - if (exported->name() == continuation->unique_name()) + if (exported->name() == continuation->name()) imported = exported; } if (!imported) continue; @@ -41,6 +41,7 @@ static void get_kernel_configs( return false; }, true); + continuation->attributes().cc = CC::DeviceHostCode; continuation->destroy("codegen"); } } @@ -105,6 +106,7 @@ DeviceBackends::DeviceBackends(World& world, int opt, bool debug, std::string& f // Necessary so that the names match in the original and imported worlds imported->set_name(continuation->unique_name()); + continuation->set_name(continuation->unique_name()); for (size_t i = 0, e = continuation->num_params(); i != e; ++i) imported->param(i)->set_name(continuation->param(i)->name()); imported->world().make_external(imported); diff --git a/src/thorin/be/json/json.cpp b/src/thorin/be/json/json.cpp index 612e6d3ba..0b120e621 100644 --- a/src/thorin/be/json/json.cpp +++ b/src/thorin/be/json/json.cpp @@ -204,6 +204,8 @@ class DefTable { forward_decl["arg_names"] = arg_names; if (cont->is_external()) forward_decl["external"] = cont->name(); + if (cont->cc() == CC::DeviceHostCode) + forward_decl["device"] = cont->name(); decl_table.push_back(forward_decl); if(cont->has_body()) { @@ -608,6 +610,12 @@ void CodeGen::emit_stream(std::ostream& stream) { json j; j["module"] = world().name(); + if (target_triple != "") + j["target_triple"] = target_triple; + if (target_cpu != "") + j["target_cpu"] = target_cpu; + if (target_attr != "") + j["target_attr"] = target_attr; TypeTable type_table; DefTable def_table(type_table); diff --git a/src/thorin/be/json/json.h b/src/thorin/be/json/json.h index ee221ea96..23744480d 100644 --- a/src/thorin/be/json/json.h +++ b/src/thorin/be/json/json.h @@ -17,9 +17,11 @@ using json = nlohmann::json; class CodeGen : public thorin::CodeGen { public: - CodeGen(World& world, const Cont2Config& kernel_config, bool debug) + CodeGen(World& world, bool debug, std::string& target_triple, std::string& target_cpu, std::string& target_attr) : thorin::CodeGen(world, debug) - , kernel_config_(kernel_config) + , target_triple(target_triple) + , target_cpu(target_cpu) + , target_attr(target_attr) {} void emit_stream(std::ostream& stream) override; @@ -27,9 +29,10 @@ class CodeGen : public thorin::CodeGen { const char* file_ext() const override { return ".thorin.json"; } - private: - const Cont2Config& kernel_config_; + std::string& target_triple; + std::string& target_cpu; + std::string& target_attr; }; } diff --git a/src/thorin/be/llvm/runtime.cpp b/src/thorin/be/llvm/runtime.cpp index 47e7dda8f..070e0fb6c 100644 --- a/src/thorin/be/llvm/runtime.cpp +++ b/src/thorin/be/llvm/runtime.cpp @@ -79,7 +79,8 @@ Continuation* Runtime::emit_host_code(CodeGen& code_gen, llvm::IRBuilder<>& buil auto kernel = body->arg(LaunchArgs::Body)->as()->init()->as(); auto& world = continuation->world(); - auto kernel_name = builder.CreateGlobalStringPtr(kernel->name() == "hls_top" ? kernel->name() : kernel->unique_name()); + //auto kernel_name = builder.CreateGlobalStringPtr(kernel->name() == "hls_top" ? kernel->name() : kernel->name()); + auto kernel_name = builder.CreateGlobalStringPtr(kernel->name()); auto file_name = builder.CreateGlobalStringPtr(world.name() + ext); const size_t num_kernel_args = body->num_args() - LaunchArgs::Num; diff --git a/src/thorin/continuation.h b/src/thorin/continuation.h index 9f34fe992..763dbcaad 100644 --- a/src/thorin/continuation.h +++ b/src/thorin/continuation.h @@ -80,8 +80,9 @@ class App : public Def { //------------------------------------------------------------------------------ enum class CC : uint8_t { - C, ///< C calling convention. - Device, ///< Device calling convention. These are special functions only available on a particular device. + C, ///< C calling convention. + Device, ///< Device calling convention. These are special functions only available on a particular device. + DeviceHostCode, ///< Calling convention to denote continuations that are generated as device code. }; enum class Intrinsic : uint8_t { From b04b2faff74178f34abc64883adaa7c7cca574b4 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Wed, 30 Nov 2022 16:53:37 +0100 Subject: [PATCH 127/342] fixed undefined jump arguments in shady be --- src/thorin/be/shady/shady.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/thorin/be/shady/shady.cpp b/src/thorin/be/shady/shady.cpp index 7ca15a317..7f6e0a20e 100644 --- a/src/thorin/be/shady/shady.cpp +++ b/src/thorin/be/shady/shady.cpp @@ -301,6 +301,7 @@ void CodeGen::emit_epilogue(Continuation* cont) { })); shady::Jump jump; jump.target = args[ret_param]; + jump.args = shady::nodes(arena, 0, NULL); bb.terminator = shady::jump(arena, jump); return; } From 00e50649f8ee5808bf73fbad55c00aa34c0da524 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Wed, 30 Nov 2022 18:19:29 +0100 Subject: [PATCH 128/342] shady: gpu entry point annotations --- src/thorin/be/shady/shady.cpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/thorin/be/shady/shady.cpp b/src/thorin/be/shady/shady.cpp index 7f6e0a20e..b86fb5613 100644 --- a/src/thorin/be/shady/shady.cpp +++ b/src/thorin/be/shady/shady.cpp @@ -17,6 +17,7 @@ void CodeGen::emit_stream(std::ostream& out) { assert(!module); shady::ArenaConfig config = { }; + config.name_bound = true; config.check_types = true; arena = shady::new_ir_arena(config); module = shady::new_module(arena, world().name().c_str()); @@ -177,6 +178,20 @@ shady::Node* CodeGen::emit_decl_head(Def* def) { returns.push_back(ret_type); } + auto config = kernel_config_.find(cont); + if (config != kernel_config_.end()) { + if (auto gpu_config = config->second->isa()) { + annotations.push_back(shady::annotation_value(arena, { .name = "EntryPoint", .value = shady::string_lit(arena, { .string = "compute" })})); + std::vector block_size; + block_size.emplace_back(shady::int32_literal(arena, get<0>(gpu_config->block_size()))); + block_size.emplace_back(shady::int32_literal(arena, get<1>(gpu_config->block_size()))); + block_size.emplace_back(shady::int32_literal(arena, get<2>(gpu_config->block_size()))); + annotations.push_back(shady::annotation_values(arena, { .name = "WorkgroupSize", .values = vec2nodes(block_size) })); + } else { + assert(false && "Only GPU kernel configs are currently supported"); + } + } + return shady::function(module, vec2nodes(params), def->unique_name().c_str(), vec2nodes(annotations), vec2nodes(returns)); } else if (auto global = def->isa()) { if (global->is_mutable()) { From 9eb16a3a9ac1315ed56ae0a25f9fd84d5454a309 Mon Sep 17 00:00:00 2001 From: Matthias Kurtenacker Date: Thu, 1 Dec 2022 14:53:50 +0100 Subject: [PATCH 129/342] Add support for external glbobals. --- src/thorin/analyses/scope.cpp | 5 +-- src/thorin/be/codegen.cpp | 4 ++- src/thorin/be/emitter.h | 9 ++++-- src/thorin/be/json/json.cpp | 6 ++-- src/thorin/be/llvm/llvm.cpp | 34 ++++++++++++++++++--- src/thorin/be/llvm/llvm.h | 2 ++ src/thorin/primop.cpp | 2 ++ src/thorin/primop.h | 2 ++ src/thorin/rec_stream.cpp | 16 ++++++++-- src/thorin/transform/cleanup_world.cpp | 10 +++--- src/thorin/transform/importer.cpp | 7 +++++ src/thorin/transform/partial_evaluation.cpp | 4 ++- src/thorin/world.h | 10 +++--- 13 files changed, 86 insertions(+), 25 deletions(-) diff --git a/src/thorin/analyses/scope.cpp b/src/thorin/analyses/scope.cpp index 37d34114c..8fd518885 100644 --- a/src/thorin/analyses/scope.cpp +++ b/src/thorin/analyses/scope.cpp @@ -111,8 +111,9 @@ template void Scope::for_each(const World& world, std::function f) { unique_queue continuation_queue; - for (auto&& [_, cont] : world.externals()) { - if (cont->has_body()) continuation_queue.push(cont); + for (auto&& [_, def] : world.externals()) { + if (auto cont = def->template isa()) + if (cont->has_body()) continuation_queue.push(cont); } while (!continuation_queue.empty()) { diff --git a/src/thorin/be/codegen.cpp b/src/thorin/be/codegen.cpp index 0e82fe2ce..5a0300eb4 100644 --- a/src/thorin/be/codegen.cpp +++ b/src/thorin/be/codegen.cpp @@ -24,7 +24,9 @@ static void get_kernel_configs( for (auto continuation : kernels) { // recover the imported continuation (lost after the call to opt) Continuation* imported = nullptr; - for (auto [_, exported] : externals) { + for (auto [_, def] : externals) { + auto exported = def->isa(); + if (!exported) continue; if (!exported->has_body()) continue; if (exported->name() == continuation->name()) imported = exported; diff --git a/src/thorin/be/emitter.h b/src/thorin/be/emitter.h index 72300ba06..073c76f34 100644 --- a/src/thorin/be/emitter.h +++ b/src/thorin/be/emitter.h @@ -12,8 +12,13 @@ class Emitter { /// Internal wrapper for @p emit that checks and retrieves/puts the @c Value from @p defs_. Value emit_(const Def* def) { auto place = def->no_dep() ? entry_ : scheduler_.smart(def); - auto& bb = cont2bb_[place]; - return child().emit_bb(bb, def); + + if (place) { + auto& bb = cont2bb_[place]; + return child().emit_bb(bb, def); + } else { + return child().emit_constant(def); + } } protected: diff --git a/src/thorin/be/json/json.cpp b/src/thorin/be/json/json.cpp index 0b120e621..ae9d9d1de 100644 --- a/src/thorin/be/json/json.cpp +++ b/src/thorin/be/json/json.cpp @@ -514,12 +514,15 @@ class DefTable { } else if (auto global = def->isa()) { auto init = translate_def(global->init()); bool is_mutable = global->is_mutable(); + bool is_external = global->is_external(); auto name = "_" + std::to_string(def_table.size()); result["name"] = name; result["type"] = "global"; result["init"] = init; result["mutable"] = is_mutable; + if (is_external) + result["external"] = global->name(); } else if (auto variant = def->isa()) { auto variant_type = type_table_.translate_type(variant->type()); auto value = translate_def(variant->value()); @@ -621,8 +624,7 @@ void CodeGen::emit_stream(std::ostream& stream) { DefTable def_table(type_table); for (auto external : world().externals()) { - const Continuation* continuation = external.second; - def_table.translate_def(continuation); + def_table.translate_def(external.second); } j["type_table"] = type_table.type_table; diff --git a/src/thorin/be/llvm/llvm.cpp b/src/thorin/be/llvm/llvm.cpp index ce9fb159e..977893f75 100644 --- a/src/thorin/be/llvm/llvm.cpp +++ b/src/thorin/be/llvm/llvm.cpp @@ -276,6 +276,12 @@ CodeGen::emit_module() { dicompile_unit_ = dibuilder_.createCompileUnit(llvm::dwarf::DW_LANG_C, dibuilder_.createFile(world().name(), llvm::StringRef()), "Impala", opt() > 0, llvm::StringRef(), 0); } + for (auto&& [_, def] : world().externals()) { + if (auto global = def->isa()) { + emit(global); + } + } + Scope::for_each(world(), [&] (const Scope& scope) { emit_scope(scope); }); if (debug()) dibuilder_.finalize(); @@ -538,9 +544,17 @@ void CodeGen::emit_epilogue(Continuation* continuation) { irbuilder.SetInsertPoint(bb->getTerminator()); } +llvm::Value* CodeGen::emit_constant(const Def* def) { + auto irbuilder = llvm::IRBuilder(context()); + return emit_builder(irbuilder, def); +} + llvm::Value* CodeGen::emit_bb(BB& bb, const Def* def) { auto& irbuilder = *bb.second; + return emit_builder(irbuilder, def); +} +llvm::Value* CodeGen::emit_builder(llvm::IRBuilder<>& irbuilder, const Def* def) { // TODO //if (debug()) //irbuilder.SetCurrentDebugLocation(llvm::DILocation::get(discope_->getContext(), def->loc().begin.row, def->loc().begin.col, discope_)); @@ -955,13 +969,23 @@ llvm::Value* CodeGen::emit_global(const Global* global) { val = emit(continuation); else { auto llvm_type = convert(global->alloced_type()); - auto var = llvm::cast(module().getOrInsertGlobal(global->unique_name().c_str(), llvm_type)); + auto var = llvm::cast(module().getOrInsertGlobal(global->is_external() ? global->name().c_str() : global->unique_name().c_str(), llvm_type)); var->setConstant(!global->is_mutable()); - var->setLinkage(llvm::GlobalValue::InternalLinkage); - if (global->init()->isa()) - var->setInitializer(llvm::Constant::getNullValue(llvm_type)); // HACK - else + + if (global->init()->isa()) { + if (global->is_external()) + var->setExternallyInitialized(true); + else + var->setInitializer(llvm::Constant::getNullValue(llvm_type)); // HACK + } else var->setInitializer(llvm::cast(emit(global->init()))); + + if (global->is_external()) { + var->setAlignment(llvm::Align(4)); + var->setDSOLocal(true); + var->setUnnamedAddr(llvm::GlobalVariable::UnnamedAddr::None); + } else + var->setLinkage(llvm::GlobalValue::InternalLinkage); val = var; } return val; diff --git a/src/thorin/be/llvm/llvm.h b/src/thorin/be/llvm/llvm.h index 93e9aeb7e..4c41ba40c 100644 --- a/src/thorin/be/llvm/llvm.h +++ b/src/thorin/be/llvm/llvm.h @@ -54,7 +54,9 @@ class CodeGen : public thorin::CodeGen, public thorin::Emitter> emit_module(); llvm::Function* prepare(const Scope&); virtual void prepare(Continuation*, llvm::Function*); + llvm::Value* emit_constant(const Def* def); llvm::Value* emit_bb(BB&, const Def* def); + llvm::Value* emit_builder(llvm::IRBuilder<>&, const Def* def); virtual llvm::Function* emit_fun_decl(Continuation*); bool is_valid(llvm::Value* value) { return value != nullptr; } void finalize(const Scope&); diff --git a/src/thorin/primop.cpp b/src/thorin/primop.cpp index ddad23575..453db0bc2 100644 --- a/src/thorin/primop.cpp +++ b/src/thorin/primop.cpp @@ -288,6 +288,8 @@ const char* Global::op_name() const { return is_mutable() ? "global_mutable" : " * misc */ +bool Global::is_external() const { return world().is_external(this); } + std::string DefiniteArray::as_string() const { std::string res; for (auto op : ops()) { diff --git a/src/thorin/primop.h b/src/thorin/primop.h index ad4e2efcb..f12fc74c0 100644 --- a/src/thorin/primop.h +++ b/src/thorin/primop.h @@ -534,6 +534,8 @@ class Global : public Def { const Type* alloced_type() const { return type()->pointee(); } const char* op_name() const override; + bool is_external() const; + private: hash_t vhash() const override { return murmur3(gid()); } bool equal(const Def* other) const override { return this == other; } diff --git a/src/thorin/rec_stream.cpp b/src/thorin/rec_stream.cpp index 03d6a8c20..b021544bc 100644 --- a/src/thorin/rec_stream.cpp +++ b/src/thorin/rec_stream.cpp @@ -125,6 +125,11 @@ Stream& Def::stream1(Stream& s) const { s.fmt(": ({, })\n", ass->clobbers()); s.fmt(": ({, })\b", ass->ops()); return s; + } else if (auto global = isa()) { + if (global->is_external()) + return s.fmt("{}", unique_name()); + else + return s.fmt("{}({, }))", op_name(), ops()); } return s.fmt("{}({, }))", op_name(), ops()); @@ -138,9 +143,14 @@ Stream& World::stream(Stream& s) const { RecStreamer rec(s, std::numeric_limits::max()); s << "module '" << name() << "'"; - for (auto&& [_, cont] : externals()) { - rec.conts.push(cont); - rec.run(); + for (auto&& [_, def] : externals()) { + auto cont = def->isa(); + if (cont) { + rec.conts.push(cont); + rec.run(); + } else { + s.fmt("\n{} = {}({, })\n", def->unique_name(), def->op_name(), def->ops()); + } } return s.endl(); diff --git a/src/thorin/transform/cleanup_world.cpp b/src/thorin/transform/cleanup_world.cpp index 2748d25d9..510c2139b 100644 --- a/src/thorin/transform/cleanup_world.cpp +++ b/src/thorin/transform/cleanup_world.cpp @@ -236,9 +236,11 @@ void Cleaner::rebuild() { importer.type_old2new_.rehash(world_.types().capacity()); importer.def_old2new_.rehash(world_.defs().capacity()); - for (auto&& [_, cont] : world().externals()) { - if (cont->is_exported()) + for (auto&& [_, def] : world().externals()) { + if (auto cont = def->isa(); cont && cont->is_exported()) importer.import(cont); + if (auto global = def->isa(); global && global->is_external()) + importer.import(global); } swap(importer.world(), world_); @@ -296,8 +298,8 @@ void Cleaner::clean_pe_infos() { queue.push(continuation); }; - for (auto&& [_, cont] : world().externals()) - if (cont->has_body()) enqueue(cont); + for (auto&& [_, def] : world().externals()) + if (auto cont = def->isa(); cont && cont->has_body()) enqueue(cont); while (!queue.empty()) { auto continuation = pop(queue); diff --git a/src/thorin/transform/importer.cpp b/src/thorin/transform/importer.cpp index f125585de..72518b5a5 100644 --- a/src/thorin/transform/importer.cpp +++ b/src/thorin/transform/importer.cpp @@ -84,6 +84,13 @@ const Def* Importer::import(const Def* odef) { if (odef->isa_structural()) { auto ndef = odef->rebuild(world(), ntype, nops); + + if (auto oglobal = odef->isa()) { + if (oglobal->is_external()) + world().make_external(const_cast(ndef)); + } + + todo_ |= odef->tag() != ndef->tag(); return def_old2new_[odef] = ndef; } diff --git a/src/thorin/transform/partial_evaluation.cpp b/src/thorin/transform/partial_evaluation.cpp index 28d383a38..1b0739a7e 100644 --- a/src/thorin/transform/partial_evaluation.cpp +++ b/src/thorin/transform/partial_evaluation.cpp @@ -146,7 +146,9 @@ void PartialEvaluator::eat_pe_info(Continuation* cur) { bool PartialEvaluator::run() { bool todo = false; - for (auto&& [_, cont] : world().externals()) { + for (auto&& [_, def] : world().externals()) { + auto cont = def->isa(); + if (!cont) continue; if (!cont->has_body()) continue; enqueue(cont); top_level_[cont] = true; diff --git a/src/thorin/world.h b/src/thorin/world.h index 96e30af2b..311279a30 100644 --- a/src/thorin/world.h +++ b/src/thorin/world.h @@ -65,7 +65,7 @@ class World : public TypeTable, public Streamable { using Sea = HashSet;///< This @p HashSet contains Thorin's "sea of nodes". using Breakpoints = HashSet; - using Externals = HashMap; + using Externals = HashMap; World(World&&) = delete; World& operator=(const World&) = delete; @@ -90,10 +90,10 @@ class World : public TypeTable, public Streamable { //@{ bool empty() { return data_.externals_.empty(); } const Externals& externals() const { return data_.externals_; } - void make_external(Continuation* cont) { data_.externals_.emplace(cont->unique_name(), cont); } - void make_internal(Continuation* cont) { data_.externals_.erase(cont->unique_name()); } - bool is_external(const Continuation* cont) { return data_.externals_.contains(cont->unique_name()); } - Continuation* lookup(const std::string& name) { return data_.externals_.lookup(name).value_or(nullptr); } + void make_external(Def* cont) { data_.externals_.emplace(cont->unique_name(), cont).second; } + void make_internal(Def* cont) { data_.externals_.erase(cont->unique_name()); } + bool is_external(const Def* cont) { return data_.externals_.contains(cont->unique_name()); } + Def* lookup(const std::string& name) { return const_cast(data_.externals_.lookup(name).value_or(nullptr)); } //@} // literals From 2fbe0e7727a13047a8f7dda18612354618780a9e Mon Sep 17 00:00:00 2001 From: Matthias Kurtenacker Date: Thu, 1 Dec 2022 17:15:22 +0100 Subject: [PATCH 130/342] Do not evaluate external globals if the initializer is bottom. --- src/thorin/transform/resolve_loads.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/thorin/transform/resolve_loads.cpp b/src/thorin/transform/resolve_loads.cpp index d0a687ce4..02b5bcc1b 100644 --- a/src/thorin/transform/resolve_loads.cpp +++ b/src/thorin/transform/resolve_loads.cpp @@ -95,7 +95,7 @@ class ResolveLoads { return it->second; if (auto global = alloc->isa()) { // Immutable globals will remain set to their initial value - if (!global->is_mutable()) + if (!global->is_mutable() && (!global->is_external()) || !global->init()->isa()) return mapping[alloc] = global->init(); } // Nothing is known about this allocation yet @@ -233,7 +233,7 @@ public: \ while (true) { while (auto bitcast = ptr->isa()) ptr = bitcast->from(); - if (ptr->isa() && !ptr->as()->is_mutable()) + if (ptr->isa() && !ptr->as()->is_mutable() && (!ptr->as()->is_external() || !ptr->as()->init()->isa())) return ptr; // If first == ptr, we are looking at the pointed value. // In that case, we need to make sure the pointer does not escape. From 9d4b4afb2f62eafbe2e08b84ab42081ed89102d6 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Fri, 2 Dec 2022 14:56:45 +0100 Subject: [PATCH 131/342] shady files are .shady --- src/thorin/be/llvm/llvm.cpp | 2 +- src/thorin/be/llvm/runtime.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/thorin/be/llvm/llvm.cpp b/src/thorin/be/llvm/llvm.cpp index ff2d95ec4..c1ea92dc4 100644 --- a/src/thorin/be/llvm/llvm.cpp +++ b/src/thorin/be/llvm/llvm.cpp @@ -1157,7 +1157,7 @@ Continuation* CodeGen::emit_intrinsic(llvm::IRBuilder<>& irbuilder, Continuation case Intrinsic::NVVM: return runtime_->emit_host_code(*this, irbuilder, Runtime::CUDA_PLATFORM, ".nvvm", continuation); case Intrinsic::OpenCL: return runtime_->emit_host_code(*this, irbuilder, Runtime::OPENCL_PLATFORM, ".cl", continuation); case Intrinsic::AMDGPU: return runtime_->emit_host_code(*this, irbuilder, Runtime::HSA_PLATFORM, ".amdgpu", continuation); - case Intrinsic::SpirV: return runtime_->emit_host_code(*this, irbuilder, Runtime::VULKAN_PLATFORM, ".spv", continuation); + case Intrinsic::SpirV: return runtime_->emit_host_code(*this, irbuilder, Runtime::SHADY_PLATFORM, ".shady", continuation); case Intrinsic::HLS: return emit_hls(irbuilder, continuation); case Intrinsic::Parallel: return emit_parallel(irbuilder, continuation); case Intrinsic::Fibers: return emit_fibers(irbuilder, continuation); diff --git a/src/thorin/be/llvm/runtime.h b/src/thorin/be/llvm/runtime.h index 19779b6a3..d99de5c27 100644 --- a/src/thorin/be/llvm/runtime.h +++ b/src/thorin/be/llvm/runtime.h @@ -23,7 +23,7 @@ class Runtime { CUDA_PLATFORM, OPENCL_PLATFORM, HSA_PLATFORM, - VULKAN_PLATFORM, + SHADY_PLATFORM, }; /// Emits a call to anydsl_launch_kernel. From ee3f679bd7f411a69a33e86009d3ae4ecf0ad4d8 Mon Sep 17 00:00:00 2001 From: Matthias Kurtenacker Date: Fri, 2 Dec 2022 15:01:25 +0100 Subject: [PATCH 132/342] Bugfix: bad parentheses in resolve_loads. --- src/thorin/transform/resolve_loads.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/thorin/transform/resolve_loads.cpp b/src/thorin/transform/resolve_loads.cpp index 02b5bcc1b..ba916b837 100644 --- a/src/thorin/transform/resolve_loads.cpp +++ b/src/thorin/transform/resolve_loads.cpp @@ -95,7 +95,7 @@ class ResolveLoads { return it->second; if (auto global = alloc->isa()) { // Immutable globals will remain set to their initial value - if (!global->is_mutable() && (!global->is_external()) || !global->init()->isa()) + if (!global->is_mutable() && (!global->is_external() || !global->init()->isa())) return mapping[alloc] = global->init(); } // Nothing is known about this allocation yet From 21bc0f6f96e75645c85611eb24d38d17cb8a2dd8 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Fri, 2 Dec 2022 16:08:29 +0100 Subject: [PATCH 133/342] fix a few issues with the shady codegen --- src/thorin/be/shady/shady.cpp | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/thorin/be/shady/shady.cpp b/src/thorin/be/shady/shady.cpp index b86fb5613..17589cea8 100644 --- a/src/thorin/be/shady/shady.cpp +++ b/src/thorin/be/shady/shady.cpp @@ -178,6 +178,8 @@ shady::Node* CodeGen::emit_decl_head(Def* def) { returns.push_back(ret_type); } + std::string name = def->unique_name(); + auto config = kernel_config_.find(cont); if (config != kernel_config_.end()) { if (auto gpu_config = config->second->isa()) { @@ -187,12 +189,13 @@ shady::Node* CodeGen::emit_decl_head(Def* def) { block_size.emplace_back(shady::int32_literal(arena, get<1>(gpu_config->block_size()))); block_size.emplace_back(shady::int32_literal(arena, get<2>(gpu_config->block_size()))); annotations.push_back(shady::annotation_values(arena, { .name = "WorkgroupSize", .values = vec2nodes(block_size) })); + name = "main"; } else { assert(false && "Only GPU kernel configs are currently supported"); } } - return shady::function(module, vec2nodes(params), def->unique_name().c_str(), vec2nodes(annotations), vec2nodes(returns)); + return shady::function(module, vec2nodes(params), name.c_str(), vec2nodes(annotations), vec2nodes(returns)); } else if (auto global = def->isa()) { if (global->is_mutable()) { return shady::global_var(module, vec2nodes(annotations), convert(global->alloced_type()), global->unique_name().c_str(), convert_address_space(AddrSpace::Private)); @@ -237,7 +240,7 @@ void CodeGen::prepare(Continuation* cont, shady::Node*) { params.push_back(param); } - bb.head = shady::basic_block(arena, curr_fn, vec2nodes(params), cont->name().c_str()); + bb.head = shady::basic_block(arena, curr_fn, vec2nodes(params), cont->unique_name().c_str()); } else assert(bb.head); @@ -309,14 +312,13 @@ void CodeGen::emit_epilogue(Continuation* cont) { // shady primop called as imported continuations look like continuation calls to thorin, but not to shady // we just need to carefully emit the primop as an instruction, then jump to the target BB, passing the stuff as we do if (auto op = is_shady_prim_op(callee); op.has_value()) { - shady::bind_instruction(bb.builder, shady::prim_op(arena, (shady::PrimOp) { + shady::Jump jump; + jump.target = args[ret_param]; + jump.args = shady::bind_instruction(bb.builder, shady::prim_op(arena, (shady::PrimOp) { .op = op.value(), .type_arguments = shady::nodes(arena, 0, nullptr), .operands = vec2nodes(args), })); - shady::Jump jump; - jump.target = args[ret_param]; - jump.args = shady::nodes(arena, 0, NULL); bb.terminator = shady::jump(arena, jump); return; } From e4073382d6198eefb653b9bb985eb971ae148102 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Fri, 2 Dec 2022 16:49:34 +0100 Subject: [PATCH 134/342] shady: cmp, arithm --- src/thorin/be/shady/shady.cpp | 43 ++++++++++++++++++++++++++++++++--- 1 file changed, 40 insertions(+), 3 deletions(-) diff --git a/src/thorin/be/shady/shady.cpp b/src/thorin/be/shady/shady.cpp index 17589cea8..e1485bf46 100644 --- a/src/thorin/be/shady/shady.cpp +++ b/src/thorin/be/shady/shady.cpp @@ -315,9 +315,9 @@ void CodeGen::emit_epilogue(Continuation* cont) { shady::Jump jump; jump.target = args[ret_param]; jump.args = shady::bind_instruction(bb.builder, shady::prim_op(arena, (shady::PrimOp) { - .op = op.value(), - .type_arguments = shady::nodes(arena, 0, nullptr), - .operands = vec2nodes(args), + .op = op.value(), + .type_arguments = shady::nodes(arena, 0, nullptr), + .operands = vec2nodes(args), })); bb.terminator = shady::jump(arena, jump); return; @@ -365,6 +365,21 @@ const shady::Node* CodeGen::emit_fun_decl(Continuation* cont) { const shady::Node* CodeGen::emit_bb(BB& bb, const Def* def) { const shady::Node* v = nullptr; + + auto mk_primop = [&](shady::Op op, std::vector args, std::vector types = {}) -> const shady::Node* { + shady::PrimOp payload = {}; + payload.op = op; + std::vector operands; + for (auto arg : args) + operands.push_back(emit_bb(bb, arg)); + std::vector type_arguments; + for (auto type_arg : types) + type_arguments.push_back(convert(type_arg)); + payload.operands = vec2nodes(operands); + payload.type_arguments = vec2nodes(type_arguments); + return shady::first(shady::bind_instruction(bb.builder, shady::prim_op(arena, payload))); + }; + if (auto prim_lit = def->isa()) { const auto& box = prim_lit->value(); switch (prim_lit->primtype_tag()) { @@ -392,6 +407,28 @@ const shady::Node* CodeGen::emit_bb(BB& bb, const Def* def) { payload.element_type = convert(arr->elem_type()); payload.contents = vec2nodes(contents); v = shady::arr_lit(arena, payload); + } else if (auto cmp = def->isa()) { + switch (cmp->cmp_tag()) { + case Cmp_eq: v = mk_primop(shady::Op::eq_op, { cmp->lhs(), cmp->rhs() }); break; + case Cmp_ne: v = mk_primop(shady::Op::neq_op, { cmp->lhs(), cmp->rhs() }); break; + case Cmp_gt: v = mk_primop(shady::Op::gt_op, { cmp->lhs(), cmp->rhs() }); break; + case Cmp_ge: v = mk_primop(shady::Op::gte_op, { cmp->lhs(), cmp->rhs() }); break; + case Cmp_lt: v = mk_primop(shady::Op::lt_op, { cmp->lhs(), cmp->rhs() }); break; + case Cmp_le: v = mk_primop(shady::Op::lte_op, { cmp->lhs(), cmp->rhs() }); break; + } + } else if (auto arith = def->isa()) { + switch (arith->arithop_tag()) { + case ArithOp_add: v = mk_primop(shady::Op::add_op, { arith->lhs(), arith->rhs() }); break; + case ArithOp_sub: v = mk_primop(shady::Op::sub_op, { arith->lhs(), arith->rhs() }); break; + case ArithOp_mul: v = mk_primop(shady::Op::mul_op, { arith->lhs(), arith->rhs() }); break; + case ArithOp_div: v = mk_primop(shady::Op::div_op, { arith->lhs(), arith->rhs() }); break; + case ArithOp_rem: v = mk_primop(shady::Op::mod_op, { arith->lhs(), arith->rhs() }); break; + case ArithOp_and: v = mk_primop(shady::Op::and_op, { arith->lhs(), arith->rhs() }); break; + case ArithOp_or: v = mk_primop(shady::Op::or_op, { arith->lhs(), arith->rhs() }); break; + case ArithOp_xor: v = mk_primop(shady::Op::xor_op, { arith->lhs(), arith->rhs() }); break; + case ArithOp_shl: v = mk_primop(shady::Op::lshift_op, { arith->lhs(), arith->rhs() }); break; + case ArithOp_shr: v = mk_primop(shady::Op::rshift_logical_op, { arith->lhs(), arith->rhs() }); break; + } } assert(v && shady::is_value(v)); defs_[def] = v; From 494598c4328e8eca093ca588b549b7b94652127e Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Sat, 3 Dec 2022 12:13:47 +0100 Subject: [PATCH 135/342] fix some broken codegen --- src/thorin/be/shady/shady.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/thorin/be/shady/shady.cpp b/src/thorin/be/shady/shady.cpp index e1485bf46..2f1d04a1d 100644 --- a/src/thorin/be/shady/shady.cpp +++ b/src/thorin/be/shady/shady.cpp @@ -371,7 +371,7 @@ const shady::Node* CodeGen::emit_bb(BB& bb, const Def* def) { payload.op = op; std::vector operands; for (auto arg : args) - operands.push_back(emit_bb(bb, arg)); + operands.push_back(emit(arg)); std::vector type_arguments; for (auto type_arg : types) type_arguments.push_back(convert(type_arg)); @@ -429,6 +429,10 @@ const shady::Node* CodeGen::emit_bb(BB& bb, const Def* def) { case ArithOp_shl: v = mk_primop(shady::Op::lshift_op, { arith->lhs(), arith->rhs() }); break; case ArithOp_shr: v = mk_primop(shady::Op::rshift_logical_op, { arith->lhs(), arith->rhs() }); break; } + } else if (auto param = def->isa()) { + assert(param->type() == world().mem_type()); + defs_[def] = nullptr; + return nullptr; } assert(v && shady::is_value(v)); defs_[def] = v; From f801e8812daf4d632f2014c597b9d059403f5a96 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Sat, 3 Dec 2022 16:30:41 +0100 Subject: [PATCH 136/342] changed references to 'spirv' to 'shady' --- src/thorin/be/codegen.cpp | 12 ++++++------ src/thorin/be/llvm/llvm.cpp | 34 +++++++++++++++++----------------- src/thorin/continuation.cpp | 2 +- src/thorin/continuation.h | 2 +- src/thorin/type.h | 2 +- 5 files changed, 26 insertions(+), 26 deletions(-) diff --git a/src/thorin/be/codegen.cpp b/src/thorin/be/codegen.cpp index b804fa6e6..fbbc29c4b 100644 --- a/src/thorin/be/codegen.cpp +++ b/src/thorin/be/codegen.cpp @@ -92,12 +92,12 @@ DeviceBackends::DeviceBackends(World& world, int opt, bool debug, std::string& f Continuation* imported = nullptr; static const auto backend_intrinsics = std::array { - std::pair { CUDA, Intrinsic::CUDA }, - std::pair { NVVM, Intrinsic::NVVM }, - std::pair { OpenCL, Intrinsic::OpenCL }, - std::pair { AMDGPU, Intrinsic::AMDGPU }, - std::pair { HLS, Intrinsic::HLS }, - std::pair { Shady, Intrinsic::SpirV } + std::pair { CUDA, Intrinsic::CUDA }, + std::pair { NVVM, Intrinsic::NVVM }, + std::pair { OpenCL, Intrinsic::OpenCL }, + std::pair { AMDGPU, Intrinsic::AMDGPU }, + std::pair { HLS, Intrinsic::HLS }, + std::pair { Shady, Intrinsic::ShadyCompute } }; for (auto [backend, intrinsic] : backend_intrinsics) { if (is_passed_to_intrinsic(continuation, intrinsic)) { diff --git a/src/thorin/be/llvm/llvm.cpp b/src/thorin/be/llvm/llvm.cpp index c1ea92dc4..df1f0e876 100644 --- a/src/thorin/be/llvm/llvm.cpp +++ b/src/thorin/be/llvm/llvm.cpp @@ -1146,23 +1146,23 @@ Continuation* CodeGen::emit_intrinsic(llvm::IRBuilder<>& irbuilder, Continuation } switch (callee->intrinsic()) { - case Intrinsic::Atomic: return emit_atomic(irbuilder, continuation); - case Intrinsic::AtomicLoad: return emit_atomic_load(irbuilder, continuation); - case Intrinsic::AtomicStore: return emit_atomic_store(irbuilder, continuation); - case Intrinsic::CmpXchg: return emit_cmpxchg(irbuilder, continuation, false); - case Intrinsic::CmpXchgWeak: return emit_cmpxchg(irbuilder, continuation, true); - case Intrinsic::Fence: return emit_fence(irbuilder, continuation); - case Intrinsic::Reserve: return emit_reserve(irbuilder, continuation); - case Intrinsic::CUDA: return runtime_->emit_host_code(*this, irbuilder, Runtime::CUDA_PLATFORM, ".cu", continuation); - case Intrinsic::NVVM: return runtime_->emit_host_code(*this, irbuilder, Runtime::CUDA_PLATFORM, ".nvvm", continuation); - case Intrinsic::OpenCL: return runtime_->emit_host_code(*this, irbuilder, Runtime::OPENCL_PLATFORM, ".cl", continuation); - case Intrinsic::AMDGPU: return runtime_->emit_host_code(*this, irbuilder, Runtime::HSA_PLATFORM, ".amdgpu", continuation); - case Intrinsic::SpirV: return runtime_->emit_host_code(*this, irbuilder, Runtime::SHADY_PLATFORM, ".shady", continuation); - case Intrinsic::HLS: return emit_hls(irbuilder, continuation); - case Intrinsic::Parallel: return emit_parallel(irbuilder, continuation); - case Intrinsic::Fibers: return emit_fibers(irbuilder, continuation); - case Intrinsic::Spawn: return emit_spawn(irbuilder, continuation); - case Intrinsic::Sync: return emit_sync(irbuilder, continuation); + case Intrinsic::Atomic: return emit_atomic(irbuilder, continuation); + case Intrinsic::AtomicLoad: return emit_atomic_load(irbuilder, continuation); + case Intrinsic::AtomicStore: return emit_atomic_store(irbuilder, continuation); + case Intrinsic::CmpXchg: return emit_cmpxchg(irbuilder, continuation, false); + case Intrinsic::CmpXchgWeak: return emit_cmpxchg(irbuilder, continuation, true); + case Intrinsic::Fence: return emit_fence(irbuilder, continuation); + case Intrinsic::Reserve: return emit_reserve(irbuilder, continuation); + case Intrinsic::CUDA: return runtime_->emit_host_code(*this, irbuilder, Runtime::CUDA_PLATFORM, ".cu", continuation); + case Intrinsic::NVVM: return runtime_->emit_host_code(*this, irbuilder, Runtime::CUDA_PLATFORM, ".nvvm", continuation); + case Intrinsic::OpenCL: return runtime_->emit_host_code(*this, irbuilder, Runtime::OPENCL_PLATFORM, ".cl", continuation); + case Intrinsic::AMDGPU: return runtime_->emit_host_code(*this, irbuilder, Runtime::HSA_PLATFORM, ".amdgpu", continuation); + case Intrinsic::ShadyCompute: return runtime_->emit_host_code(*this, irbuilder, Runtime::SHADY_PLATFORM, ".shady", continuation); + case Intrinsic::HLS: return emit_hls(irbuilder, continuation); + case Intrinsic::Parallel: return emit_parallel(irbuilder, continuation); + case Intrinsic::Fibers: return emit_fibers(irbuilder, continuation); + case Intrinsic::Spawn: return emit_spawn(irbuilder, continuation); + case Intrinsic::Sync: return emit_sync(irbuilder, continuation); #if THORIN_ENABLE_RV case Intrinsic::Vectorize: return emit_vectorize_continuation(irbuilder, continuation); #else diff --git a/src/thorin/continuation.cpp b/src/thorin/continuation.cpp index e6429b480..ca7675235 100644 --- a/src/thorin/continuation.cpp +++ b/src/thorin/continuation.cpp @@ -215,7 +215,7 @@ void Continuation::set_intrinsic() { else if (name() == "nvvm") attributes().intrinsic = Intrinsic::NVVM; else if (name() == "opencl") attributes().intrinsic = Intrinsic::OpenCL; else if (name() == "amdgpu") attributes().intrinsic = Intrinsic::AMDGPU; - else if (name() == "spirv") attributes().intrinsic = Intrinsic::SpirV; + else if (name() == "shady_compute") attributes().intrinsic = Intrinsic::ShadyCompute; else if (name() == "hls") attributes().intrinsic = Intrinsic::HLS; else if (name() == "parallel") attributes().intrinsic = Intrinsic::Parallel; else if (name() == "fibers") attributes().intrinsic = Intrinsic::Fibers; diff --git a/src/thorin/continuation.h b/src/thorin/continuation.h index ed0ed113a..353d04576 100644 --- a/src/thorin/continuation.h +++ b/src/thorin/continuation.h @@ -91,7 +91,7 @@ enum class Intrinsic : uint8_t { NVVM, ///< Internal NNVM-Backend. OpenCL, ///< Internal OpenCL-Backend. AMDGPU, ///< Internal AMDGPU-Backend. - SpirV, ///< Internal Vulkan-Compute-Shader-Backend. + ShadyCompute, ///< Internal Shady Compute Backend. HLS, ///< Internal HLS-Backend. Parallel, ///< Internal Parallel-CPU-Backend. Fibers, ///< Internal Parallel-CPU-Backend using resumable fibers. diff --git a/src/thorin/type.h b/src/thorin/type.h index 5b96929bc..0d313d9f7 100644 --- a/src/thorin/type.h +++ b/src/thorin/type.h @@ -235,7 +235,7 @@ enum class AddrSpace : uint32_t { Texture = 2, Shared = 3, Constant = 4, - Private = 5, // Corresponds to the 'private' storage class in SPIR-V + Private = 5, // Corresponds to the 'private' storage class in compute kernels/shaders, as in thread-private }; /// Pointer type. From 0853d32d573d4d9ac9b3c4a8a0799b130df92f0e Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Sun, 4 Dec 2022 18:14:47 +0100 Subject: [PATCH 137/342] qualify fn return types --- src/thorin/be/shady/shady.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/thorin/be/shady/shady.cpp b/src/thorin/be/shady/shady.cpp index 2f1d04a1d..0dc62c600 100644 --- a/src/thorin/be/shady/shady.cpp +++ b/src/thorin/be/shady/shady.cpp @@ -175,6 +175,11 @@ shady::Node* CodeGen::emit_decl_head(Def* def) { auto ret_type = convert(t); if (!ret_type) continue; // Eliminate mem types + + shady::QualifiedType qtype; + qtype.type = ret_type; + qtype.is_uniform = false; + ret_type = shady::qualified_type(arena, qtype); returns.push_back(ret_type); } From f94c74ad052bc7a87846f51ecf40b7bb6672b601 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Sun, 4 Dec 2022 18:14:52 +0100 Subject: [PATCH 138/342] use a jump instead of let_into --- src/thorin/be/shady/shady.cpp | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/src/thorin/be/shady/shady.cpp b/src/thorin/be/shady/shady.cpp index 0dc62c600..80be8bab0 100644 --- a/src/thorin/be/shady/shady.cpp +++ b/src/thorin/be/shady/shady.cpp @@ -312,7 +312,6 @@ void CodeGen::emit_epilogue(Continuation* cont) { args.erase(args.begin() + ret_param); args.erase(std::remove_if(args.begin(), args.end(), [&](const auto& item){ return item == nullptr || !shady::is_value(item); }), args.end()); - const shady::Node* call; if (auto callee = body->callee()->isa_nom()) { // shady primop called as imported continuations look like continuation calls to thorin, but not to shady // we just need to carefully emit the primop as an instruction, then jump to the target BB, passing the stuff as we do @@ -327,20 +326,20 @@ void CodeGen::emit_epilogue(Continuation* cont) { bb.terminator = shady::jump(arena, jump); return; } - - shady::LeafCall payload; - payload.args = vec2nodes(args); - payload.callee = emit(callee); - call = shady::leaf_call(arena, payload); - } else { - shady::IndirectCall payload; - payload.args = vec2nodes(args); - payload.callee = emit(callee); - call = shady::indirect_call(arena, payload); } + shady::BodyBuilder* builder = shady::begin_body(module); + + shady::IndirectCall icall_payload; + icall_payload.args = vec2nodes(args); + icall_payload.callee = emit(body->callee()); + shady::Nodes results = shady::bind_instruction(builder, shady::indirect_call(arena, icall_payload)); + assert(args[ret_param]->tag == shady::BasicBlock_TAG); - bb.terminator = shady::let_into(arena, call, args[ret_param]); + shady::Jump jump_payload; + jump_payload.target = args[ret_param]; + jump_payload.args = results; + bb.terminator = shady::finish_body(builder, shady::jump(arena, jump_payload)); } } From e6bd868bb5fe72f8245fa9660a5376d7bc6ba0a2 Mon Sep 17 00:00:00 2001 From: Matthias Kurtenacker Date: Thu, 8 Dec 2022 11:19:27 +0100 Subject: [PATCH 139/342] Allow changing the init definition of a Global. --- src/thorin/primop.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/thorin/primop.h b/src/thorin/primop.h index f12fc74c0..db117779a 100644 --- a/src/thorin/primop.h +++ b/src/thorin/primop.h @@ -535,6 +535,7 @@ class Global : public Def { const char* op_name() const override; bool is_external() const; + void set_init(const Def* new_init) { unset_op(0); set_op(0, new_init); } private: hash_t vhash() const override { return murmur3(gid()); } From c59f311abd0ada73fe3fd35c6aa04f03d7c7d6c8 Mon Sep 17 00:00:00 2001 From: Matthias Kurtenacker Date: Thu, 8 Dec 2022 15:53:12 +0100 Subject: [PATCH 140/342] Resolve naming conflicts for definite and indefinite array types. --- src/thorin/be/json/json.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/thorin/be/json/json.cpp b/src/thorin/be/json/json.cpp index ae9d9d1de..8e3665a65 100644 --- a/src/thorin/be/json/json.cpp +++ b/src/thorin/be/json/json.cpp @@ -21,13 +21,13 @@ class TypeTable { result["type"] = "def_array"; result["args"] = { elem_type }; result["length"] = arr->dim(); - result["name"] = elem_type + "_darr"; + result["name"] = elem_type + "_darr_" + std::to_string(type_table.size()); } else if (auto arr = type->isa()) { auto elem_type = translate_type(arr->elem_type()); result["type"] = "indef_array"; result["args"] = { elem_type }; - result["name"] = elem_type + "_iarr"; + result["name"] = elem_type + "_iarr_" + std::to_string(type_table.size()); } else if (type->isa()) { result["name"] = "bottom_t"; result["type"] = "bottom"; From bda2713094147b4640c07e072ae2a43faf0849f6 Mon Sep 17 00:00:00 2001 From: Matthias Kurtenacker Date: Thu, 8 Dec 2022 20:30:46 +0100 Subject: [PATCH 141/342] Json backend fix for newmem. --- src/thorin/be/json/json.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/thorin/be/json/json.cpp b/src/thorin/be/json/json.cpp index 8e3665a65..1ab646965 100644 --- a/src/thorin/be/json/json.cpp +++ b/src/thorin/be/json/json.cpp @@ -160,11 +160,8 @@ class DefTable { result["type"] = "continuation"; result["intrinsic"] = "branch"; } else if (cont->intrinsic() == Intrinsic::Match) { - //TODO: These will change in the memory branch! - assert(!is_mem(cont->param(0))); - - size_t num_patterns = cont->num_params() - 2; - auto variant_type = type_table_.translate_type(cont->param(0)->type()); + size_t num_patterns = cont->num_params() - 3; + auto variant_type = type_table_.translate_type(cont->param(1)->type()); auto name = "_match_" + std::to_string(def_table.size()); result["name"] = name; From 1b91c0379d6a9406e7260895d59ac1b9f208a2e9 Mon Sep 17 00:00:00 2001 From: Matthias Kurtenacker Date: Thu, 15 Dec 2022 13:55:04 +0100 Subject: [PATCH 142/342] Some small mistakes in world.h. --- src/thorin/world.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/thorin/world.h b/src/thorin/world.h index 311279a30..fbdb2c3c5 100644 --- a/src/thorin/world.h +++ b/src/thorin/world.h @@ -90,10 +90,10 @@ class World : public TypeTable, public Streamable { //@{ bool empty() { return data_.externals_.empty(); } const Externals& externals() const { return data_.externals_; } - void make_external(Def* cont) { data_.externals_.emplace(cont->unique_name(), cont).second; } + void make_external(Def* cont) { data_.externals_.emplace(cont->unique_name(), cont); } void make_internal(Def* cont) { data_.externals_.erase(cont->unique_name()); } bool is_external(const Def* cont) { return data_.externals_.contains(cont->unique_name()); } - Def* lookup(const std::string& name) { return const_cast(data_.externals_.lookup(name).value_or(nullptr)); } + Def* lookup(const std::string& name) { return data_.externals_.lookup(name).value_or(nullptr); } //@} // literals From 278c4fa2904e950cdd40e05e86104343d99a8e44 Mon Sep 17 00:00:00 2001 From: Matthias Kurtenacker Date: Tue, 3 Jan 2023 15:43:53 +0100 Subject: [PATCH 143/342] [Json]: Forward declarations for nominal types. --- src/thorin/be/json/json.cpp | 46 ++++++++++++++++++++++++++++++------- 1 file changed, 38 insertions(+), 8 deletions(-) diff --git a/src/thorin/be/json/json.cpp b/src/thorin/be/json/json.cpp index 8e3665a65..44df89063 100644 --- a/src/thorin/be/json/json.cpp +++ b/src/thorin/be/json/json.cpp @@ -4,6 +4,7 @@ namespace thorin::json { class TypeTable { public: + json nominal_fwd_table = json::array(); json type_table = json::array(); TypeMap known_types; @@ -56,28 +57,54 @@ class TypeTable { result["name"] = "mem_t"; result["type"] = "mem"; } else if (auto structtype = type->isa()) { - json args = json::array(); + auto name = "_struct_" + std::to_string(nominal_fwd_table.size()); + known_types[type] = name; + json arg_names = json::array(); for (size_t i = 0; i < structtype->num_ops(); ++i) { - args.push_back(translate_type(structtype->op(i))); arg_names.push_back(structtype->op_name(i).str()); } + json forward_decl; + forward_decl["name"] = name; + forward_decl["type"] = "struct"; + forward_decl["struct_name"] = structtype->name().str(); + forward_decl["arg_names"] = arg_names; + nominal_fwd_table.push_back(forward_decl); + + json args = json::array(); + for (size_t i = 0; i < structtype->num_ops(); ++i) { + args.push_back(translate_type(structtype->op(i))); + } + result["type"] = "struct"; - result["name"] = "_" + std::to_string(type_table.size()); + result["name"] = name; result["struct_name"] = structtype->name().str(); - result["args"] = args; result["arg_names"] = arg_names; + result["args"] = args; } else if (auto varianttype = type->isa()) { - json args = json::array(); + auto name = "_variant_" + std::to_string(nominal_fwd_table.size()); + known_types[type] = name; + json arg_names = json::array(); for (size_t i = 0; i < varianttype->num_ops(); ++i) { - args.push_back(translate_type(varianttype->op(i))); arg_names.push_back(varianttype->op_name(i).str()); } + json forward_decl; + forward_decl["name"] = name; + forward_decl["type"] = "variant"; + forward_decl["variant_name"] = varianttype->name().str(); + forward_decl["arg_names"] = arg_names; + nominal_fwd_table.push_back(forward_decl); + + json args = json::array(); + for (size_t i = 0; i < varianttype->num_ops(); ++i) { + args.push_back(translate_type(varianttype->op(i))); + } + result["type"] = "variant"; - result["name"] = "_" + std::to_string(type_table.size()); + result["name"] = name; result["variant_name"] = varianttype->name().str(); result["args"] = args; result["arg_names"] = arg_names; @@ -627,7 +654,10 @@ void CodeGen::emit_stream(std::ostream& stream) { def_table.translate_def(external.second); } - j["type_table"] = type_table.type_table; + j["type_table"] = type_table.nominal_fwd_table; + for (auto it : type_table.type_table) + j["type_table"] += it; + j["defs"] = def_table.decl_table; for (auto it : def_table.def_table) j["defs"] += it; From 8fa238d591abdeda7c316b37843e494c99fae009 Mon Sep 17 00:00:00 2001 From: Matthias Kurtenacker Date: Tue, 3 Jan 2023 17:30:06 +0100 Subject: [PATCH 144/342] Increase inliner limits to prevent global structs from ocurring in tests. --- src/thorin/transform/inliner.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/thorin/transform/inliner.cpp b/src/thorin/transform/inliner.cpp index 175f2a518..37d858807 100644 --- a/src/thorin/transform/inliner.cpp +++ b/src/thorin/transform/inliner.cpp @@ -39,8 +39,8 @@ void force_inline(Scope& scope, int threshold) { void inliner(World& world) { world.VLOG("start inliner"); - static const int factor = 4; - static const int offset = 4; + static const int factor = 8; + static const int offset = 8; ContinuationMap> continuation2scope; From 6ec9748baba499f352dd58e62927514eba6aaba9 Mon Sep 17 00:00:00 2001 From: Matthias Kurtenacker Date: Wed, 4 Jan 2023 17:18:18 +0100 Subject: [PATCH 145/342] Allow generation of global structs by constructing ConstantStruct when possible. --- src/thorin/be/llvm/llvm.cpp | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/thorin/be/llvm/llvm.cpp b/src/thorin/be/llvm/llvm.cpp index b3967860e..a369de7b4 100644 --- a/src/thorin/be/llvm/llvm.cpp +++ b/src/thorin/be/llvm/llvm.cpp @@ -753,6 +753,25 @@ llvm::Value* CodeGen::emit_builder(llvm::IRBuilder<>& irbuilder, const Def* def) assert(def->isa() || def->isa() || def->isa() || def->isa()); if (is_unit(agg)) return nullptr; + if (def->isa() || def->isa()) { + // Try to emit it as a constant first + Array consts(agg->num_ops()); + bool all_consts = true; + for (size_t i = 0, n = consts.size(); i != n; ++i) { + consts[i] = llvm::dyn_cast(emit(agg->op(i))); + if (!consts[i]) { + all_consts = false; + break; + } + } + if (all_consts) { + if (def->isa()) + return llvm::ConstantStruct::get(llvm::cast(convert(agg->type())), llvm_ref(consts)); + else + return llvm::ConstantVector::get(llvm_ref(consts)); + } + } + llvm::Value* llvm_agg = llvm::UndefValue::get(convert(agg->type())); if (def->isa()) { for (size_t i = 0, e = agg->num_ops(); i != e; ++i) @@ -863,6 +882,11 @@ llvm::Value* CodeGen::emit_builder(llvm::IRBuilder<>& irbuilder, const Def* def) auto llvm_type = convert(variant_ctor->type()); auto tag_value = irbuilder.getIntN(llvm_type->getStructElementType(1)->getScalarSizeInBits(), variant_ctor->index()); + //Unit type variants can be emitted as constants. + if (is_type_unit(variant_ctor->op(0)->type())) { + return llvm::ConstantStruct::get(llvm::cast(llvm_type), {llvm::UndefValue::get(llvm_type->getStructElementType(0)), tag_value}); + } + return create_tmp_alloca(irbuilder, llvm_type, [&] (llvm::AllocaInst* alloca) { auto tag_addr = irbuilder.CreateInBoundsGEP(llvm_type, alloca, { irbuilder.getInt32(0), irbuilder.getInt32(1) }); irbuilder.CreateStore(tag_value, tag_addr); From 9eaba5daceac48e191683268aa3d8a4ed28feeb9 Mon Sep 17 00:00:00 2001 From: Matthias Kurtenacker Date: Wed, 4 Jan 2023 17:50:11 +0100 Subject: [PATCH 146/342] hoist_enters + importer cleanup. --- src/thorin/transform/hoist_enters.cpp | 10 +++++----- src/thorin/transform/importer.cpp | 9 +-------- src/thorin/transform/importer.h | 8 ++++++++ 3 files changed, 14 insertions(+), 13 deletions(-) diff --git a/src/thorin/transform/hoist_enters.cpp b/src/thorin/transform/hoist_enters.cpp index 5cd7fcfb4..7e8ab216e 100644 --- a/src/thorin/transform/hoist_enters.cpp +++ b/src/thorin/transform/hoist_enters.cpp @@ -8,7 +8,7 @@ namespace thorin { -std::stack todo; +static std::stack hoist_enters_todo; static void find_enters(std::deque& enters, const Def* def) { if (auto enter = def->isa()) @@ -19,15 +19,15 @@ static void find_enters(std::deque& enters, const Def* def) { for (auto use : def->uses()) { if (auto memop = use->isa()) - todo.push(memop); + hoist_enters_todo.push(memop); } } static void find_enters(std::deque& enters, Continuation* continuation) { if (auto mem_param = continuation->mem_param()) { - todo.push(mem_param); - while (!todo.empty()) { - auto next_item = pop(todo); + hoist_enters_todo.push(mem_param); + while (!hoist_enters_todo.empty()) { + auto next_item = pop(hoist_enters_todo); find_enters(enters, next_item); } diff --git a/src/thorin/transform/importer.cpp b/src/thorin/transform/importer.cpp index d476fb718..976e1c77f 100644 --- a/src/thorin/transform/importer.cpp +++ b/src/thorin/transform/importer.cpp @@ -1,9 +1,5 @@ #include "thorin/transform/importer.h" -#include -#include -#include - namespace thorin { const Type* Importer::import_type(const Type* otype) { @@ -32,10 +28,7 @@ const Type* Importer::import_type(const Type* otype) { return ntype; } -std::stack> required_defs; -std::set analyzed_conts; - -void enqueue(const Def* elem) { +void Importer::enqueue(const Def* elem) { if (elem->isa_nom()) { if (analyzed_conts.find(elem) != analyzed_conts.end()) { required_defs.push(std::pair(elem, false)); diff --git a/src/thorin/transform/importer.h b/src/thorin/transform/importer.h index eb7d5bc42..8d132a75b 100644 --- a/src/thorin/transform/importer.h +++ b/src/thorin/transform/importer.h @@ -4,6 +4,10 @@ #include "thorin/world.h" #include "thorin/config.h" +#include +#include +#include + namespace thorin { class Importer { @@ -26,6 +30,10 @@ class Importer { private: const Def* import_nonrecursive(); + void enqueue(const Def* elem); + + std::stack> required_defs; + std::set analyzed_conts; public: Type2Type type_old2new_; From 1de509b5486bcf5b9b33ce8c8737288a9f56aa56 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Tue, 10 Jan 2023 15:32:06 +0100 Subject: [PATCH 147/342] update to latest shady API --- src/thorin/be/shady/shady.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/thorin/be/shady/shady.cpp b/src/thorin/be/shady/shady.cpp index 80be8bab0..f3d060e43 100644 --- a/src/thorin/be/shady/shady.cpp +++ b/src/thorin/be/shady/shady.cpp @@ -407,10 +407,11 @@ const shady::Node* CodeGen::emit_bb(BB& bb, const Def* def) { assert(emit(e)); contents.push_back(emit(e)); } - shady::ArrayLiteral payload; + shady::ArrType payload; + const shady::Type* arr_type = shady::arr_type(arena, payload); payload.element_type = convert(arr->elem_type()); - payload.contents = vec2nodes(contents); - v = shady::arr_lit(arena, payload); + payload.size = shady::int32_literal(arena, contents.size()); + v = shady::composite(arena, arr_type, vec2nodes(contents)); } else if (auto cmp = def->isa()) { switch (cmp->cmp_tag()) { case Cmp_eq: v = mk_primop(shady::Op::eq_op, { cmp->lhs(), cmp->rhs() }); break; From c0961acab3fef38fb88139a219771943fdfbe144 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Wed, 11 Jan 2023 13:22:11 +0100 Subject: [PATCH 148/342] remove structurizer --- src/thorin/CMakeLists.txt | 2 - src/thorin/be/shady/shady.cpp | 6 +- src/thorin/transform/structurize.cpp | 466 --------------------------- src/thorin/transform/structurize.h | 12 - 4 files changed, 1 insertion(+), 485 deletions(-) delete mode 100644 src/thorin/transform/structurize.cpp delete mode 100644 src/thorin/transform/structurize.h diff --git a/src/thorin/CMakeLists.txt b/src/thorin/CMakeLists.txt index de8280c81..43537af49 100644 --- a/src/thorin/CMakeLists.txt +++ b/src/thorin/CMakeLists.txt @@ -70,8 +70,6 @@ set(THORIN_SOURCES transform/partial_evaluation.h transform/split_slots.cpp transform/split_slots.h - transform/structurize.cpp - transform/structurize.h transform/hls_channels.cpp transform/hls_channels.h transform/hls_kernel_launch.h diff --git a/src/thorin/be/shady/shady.cpp b/src/thorin/be/shady/shady.cpp index f3d060e43..89dab73f0 100644 --- a/src/thorin/be/shady/shady.cpp +++ b/src/thorin/be/shady/shady.cpp @@ -2,7 +2,6 @@ #undef empty #include "thorin/analyses/scope.h" -#include "thorin/transform/structurize.h" namespace thorin::shady_be { @@ -11,12 +10,9 @@ CodeGen::CodeGen(thorin::World& world, Cont2Config& kernel_config, bool debug) {} void CodeGen::emit_stream(std::ostream& out) { - // structure_loops(world()); - // structure_flow(world()); - assert(!module); - shady::ArenaConfig config = { }; + shady::ArenaConfig config = shady::default_arena_config(); config.name_bound = true; config.check_types = true; arena = shady::new_ir_arena(config); diff --git a/src/thorin/transform/structurize.cpp b/src/thorin/transform/structurize.cpp deleted file mode 100644 index 38ca453bd..000000000 --- a/src/thorin/transform/structurize.cpp +++ /dev/null @@ -1,466 +0,0 @@ -#include "structurize.h" - -#include - -#include "thorin/analyses/domtree.h" -#include "thorin/world.h" - -namespace thorin { - -using Head = LoopTree::Head; -using Base = LoopTree::Base; -using Leaf = LoopTree::Leaf; - -struct StructuredLoop; - -// Dispatch targets may dispatch to other dispatching nodes, and we can't actually create those until we know all their destinations, -// because their fn type takes a variant type with a case for each target. So we symbolically refer to these yet-to-be dispatch nodes via their loop -struct DispatchTarget { - bool operator==(const DispatchTarget& rhs) const { - return cont == rhs.cont &&entry == rhs.entry &&exit == rhs.exit; - } - - Continuation* cont = nullptr; - StructuredLoop* entry = nullptr; - StructuredLoop* exit = nullptr; -}; - -struct RewireMe { - explicit RewireMe(Continuation* cont) : cont(cont) {} - Continuation* cont; - - Continuation* backedge = nullptr; - struct { - std::vector exits; - std::vector enters; - Continuation* final_destination = nullptr; - } non_local_jump; -}; - -// Represents one loop in the loop forest, that we then augment with a new loop header and epilogue, each dispatching -// respectively to nodes inside of the loop, and nodes outside of the loop once we break out -struct StructuredLoop { - const Head* parent_head; - const Head* head; - const std::string name; - StructuredLoop(const Head* parent_head, const Head* head, std::string&& name) - : parent_head(parent_head), head(head), name(name) {} - - std::vector inner_destinations = {}; - std::vector outer_destinations = {}; - - /// Just a regular continuation that simply calls into the real header - Continuation* pre_header; - /// Calls the loop_enter intrinsic with the set of internal and external dispatch nodes - Continuation* real_header; - Continuation* exit; - - // Same as the inner/outer destinations, but entry/exits instead now point to the corresponding header/epilogue nodes - std::vector header_destination_conts; - std::vector epilogue_destination_conts; - - std::vector rewire; -}; - -struct ScopeContext { - explicit ScopeContext(const Scope& scope) - : cfa(scope) - {} - - CFA cfa; - ContinuationMap def2loop; - std::unordered_map rewritten_loops; -}; - -inline std::string loop_name(const Head* head) { - if (head == nullptr || head->is_root()) { - return "root"; - } else { - std::stringstream s; - s << "loop_"; - for (auto& node : head->cf_nodes()) { - s << node->continuation()->to_string(); - s << "_"; - } - return s.str(); - } -} - -/// Visits the forest and fills def2loop -inline void tag_continuations(ScopeContext& ctx, const Base* base, const Head* parent) { - if (const Head* head = base->isa()) { - auto name = loop_name(head); - - for (auto& children : head->children()) { - tag_continuations(ctx, &*children, head); - } - - StructuredLoop loop(parent, head, std::move(name)); - ctx.rewritten_loops.emplace(head, loop); - } else if(base->isa()) { - for (auto& node : base->cf_nodes()) { - auto[i, result] = ctx.def2loop.emplace(node->continuation(), parent); - assert(result); - } - } else { - assert(false); - } -} - -inline int record_destination(std::vector& vec, DispatchTarget dest) { - auto i = std::find(vec.begin(), vec.end(), dest); - if (i == vec.end()) { - vec.emplace_back(dest); - return static_cast(vec.size()) - 1; - } else return i - vec.begin(); -} - -inline int index_of_destination(std::vector& vec, DispatchTarget dest) { - auto i = std::find(vec.begin(), vec.end(), dest); - if (i == vec.end()) { - assert(false && "Missing destination"); - } else return i - vec.begin(); -} - -inline std::vector get_path(ScopeContext& ctx, const Head* head) { - std::vector path = {}; - assert(head != nullptr); - while (head != nullptr) { - StructuredLoop* loop = &ctx.rewritten_loops.find(head)->second; - assert(loop != nullptr); - path.emplace(path.begin(), loop); - head = loop->parent_head; - } - return path; -} - -inline void collect_dispatch_targets(World& world, ScopeContext& ctx, const Base* base) { - if (const Head* head = base->isa()) { - for (auto& children : head->children()) { - collect_dispatch_targets(world, ctx, &*children); - } - } else { - const Leaf* leaf = base->as(); - auto cont = leaf->cf_node()->continuation(); - // For some nonsense reason, synthetic nodes created during scopes iteration leak in next iterations >:( - if (!cont->has_body() /*|| (cont->intrinsic() >= Intrinsic::SCFBegin && cont->intrinsic() < Intrinsic::SCFEnd)*/) - return; - auto app = cont->body(); - auto callee = app->callee()->isa_nom(); - if (!callee || callee->intrinsic() == Intrinsic::Branch || callee->is_imported()) - return; - - const Head* source_loop_head = ctx.def2loop[cont]; - assert(ctx.def2loop.find(callee) != ctx.def2loop.end()); - const Head* dest_loop_head = ctx.def2loop[callee]; - - if (source_loop_head != dest_loop_head) { - // We found a non-local jump - assert(ctx.rewritten_loops.find(source_loop_head) != ctx.rewritten_loops.end()); - auto& loop = ctx.rewritten_loops.find(source_loop_head)->second; - - std::vector source_path = get_path(ctx, source_loop_head); - std::vector dest_path = get_path(ctx, dest_loop_head); - int bi = 0; - while (bi < static_cast(std::min(source_path.size(), dest_path.size()))) { - if (source_path[bi] == dest_path[bi]) - bi++; - else break; - } - - // The path is made out of a sequence of loops to break out of, and a sequence of loops to jump into - // these two sequences cannot be both empty (that wouldn't be a non-local jump then!) - std::vector leave; - std::vector enter; - for (int j = static_cast(source_path.size()) - 1; j >= bi; j--) - leave.emplace_back(source_path[j]); - for (int j = bi; j < static_cast(dest_path.size()); j++) - enter.emplace_back(dest_path[j]); - - // 0 = this is the first step of the path - // 1 = last step was to break out of a loop - // 2 = last step was to enter a loop - int last = 0; - StructuredLoop* prev; - - auto record_step = [&](DispatchTarget destination) { - if (last == 0) { - // nothing to do, this node isn't a dispatching one - } else { - if (last == 1) - record_destination(prev->outer_destinations, destination); - else - record_destination(prev->inner_destinations, destination); - } - }; - - for (auto dest_loop : leave) { - DispatchTarget destination; - destination.exit = dest_loop; - - record_step(destination); - last = 1; - prev = dest_loop; - assert(prev != nullptr); - } - for (auto dest_loop : enter) { - DispatchTarget destination; - destination.entry = dest_loop; - - record_step(destination); - last = 2; - prev = dest_loop; - assert(prev != nullptr); - } - - assert(last != 0); - DispatchTarget destination; - destination.cont = callee; - record_step(destination); - - RewireMe rewire(cont); - rewire.non_local_jump = { - std::move(leave), - std::move(enter), - callee - }; - loop.rewire.emplace_back(rewire); - } else if (source_loop_head == dest_loop_head && source_loop_head != nullptr) { - for (auto& cf_node : source_loop_head->cf_nodes()) { - if (cf_node->continuation() == callee) { - // We found a backedge - assert(ctx.rewritten_loops.find(source_loop_head) != ctx.rewritten_loops.end()); - auto& loop = ctx.rewritten_loops.find(source_loop_head)->second; - - DispatchTarget destination; - destination.cont = callee; - record_destination(loop.inner_destinations, destination); - - RewireMe rewire(cont); - rewire.backedge = cf_node->continuation(); - loop.rewire.emplace_back(rewire); - return; - } - } - assert(false); - } - } -} - -inline const Type* dom_to_tuple(World& world, const thorin::FnType* fn_type) { - std::vector t; - t.resize(fn_type->num_ops()); - for (size_t i = 0; i < fn_type->num_ops(); i++) - t[i] = fn_type->op(i); - return world.tuple_type(t); -} - -inline const Def* tuple_from_params(World& world, const ArrayRef params) { - std::vector t; - t.resize(params.size()); - for (size_t i = 0; i < params.size(); i++) - t[i] = params[i]; - return world.tuple(t); -} - -inline void create_headers(World& world, ScopeContext& ctx, const Base* base) { - if (const Head* head = base->isa()) { - for (auto& children : head->children()) { - create_headers(world, ctx, &*children); - } - - if (head->num_cf_nodes() == 0) - return; - StructuredLoop& loop = ctx.rewritten_loops.find(head)->second; - - // here, parent headers need to know what they're jumping *into* - std::vector dest_types; - for (auto& target : loop.inner_destinations) { - const thorin::Continuation* target_cont; - if (target.cont != nullptr) { - target_cont = target.cont; - } else if (target.entry != nullptr) { - assert(target.entry->pre_header != nullptr); - target_cont = target.entry->pre_header; - } else { - assert(false && "Header dispatches may not exit loops"); - } - loop.header_destination_conts.push_back(target_cont); - const thorin::FnType* target_type = target_cont->type(); - dest_types.emplace_back(dom_to_tuple(world, target_type)); - } - auto variant_type = world.variant_type(loop.name + "_param", dest_types.size()); - for (size_t i = 0; i < dest_types.size(); i++) - variant_type->set(i, dest_types[i]); - Types enter_intrinsic_types = { variant_type }; - - //loop.enter_intrinsic = world.loop_enter(enter_intrinsic_types); - //loop.continue_intrinsic = world.loop_continue(enter_intrinsic_types); - - loop.pre_header = world.continuation(world.fn_type(), { loop.name + "_new_header"}); - // loop.new_continue = world.continuation(fn_type, { loop.name + "_new_continue"}); - } -} - -inline void create_epilogues(World& world, ScopeContext& ctx, const Base* base) { - if (const Head* head = base->isa()) { - StructuredLoop& loop = ctx.rewritten_loops.find(head)->second; - - if (head->num_cf_nodes() > 0) { - // here, children epilogues need to know what they're jumping *out to* - std::vector dest_types; - for (auto& target : loop.outer_destinations) { - const thorin::Continuation* target_cont; - if (target.cont != nullptr) { - target_cont = target.cont; - } else if (target.entry != nullptr) { - assert(target.entry->pre_header != nullptr); - target_cont = target.entry->pre_header; - } else { - assert(target.exit != nullptr); - assert(target.exit->exit != nullptr); - target_cont = target.exit->exit; - } - loop.epilogue_destination_conts.push_back(target_cont); - const thorin::FnType* target_type = target_cont->type(); - dest_types.emplace_back(dom_to_tuple(world, target_type)); - } - auto variant_type = world.variant_type(loop.name + "_param", dest_types.size()); - for (size_t i = 0; i < dest_types.size(); i++) - variant_type->set(i, dest_types[i]); - - //Types break_types = {variant_type}; - //loop.break_intrinsic = world.loop_break(break_types); - loop.exit = world.continuation(world.fn_type(), {loop.name + "_new_epilogue"}); - } - - for (auto& children : head->children()) { - create_epilogues(world, ctx, &*children); - } - } -} - -// Finishes loop headers & epilogues, and re-wires backedges and non-local jumps to go through structured CF intrinsics -inline void rewire_loops(World& world, ScopeContext& ctx, const Base* base) { - if (const Head* head = base->isa()) { - assert(ctx.rewritten_loops.find(head) != ctx.rewritten_loops.end()); - auto& loop = ctx.rewritten_loops.find(head)->second; - - if (head->num_cf_nodes() > 0) { - //loop.header->structured_loop_merge(loop.new_header, loop.epilogue_destination_conts); - //loop.exit->structured_loop_header(loop.new_epilogue, loop.new_continue, loop.header_destination_conts); - } - - for (auto& children : head->children()) { - rewire_loops(world, ctx, &*children); - } - - for (auto& rewire : loop.rewire) { - assert(loop.head != nullptr); - if (rewire.backedge != nullptr) { - - DispatchTarget destination; - destination.cont = rewire.backedge; - auto variant_index = index_of_destination(loop.inner_destinations, destination); - - auto old_fn_type = rewire.backedge->type(); - auto wrapper = world.continuation(old_fn_type, {"synthetic_backedge_wrapper_to" + destination.cont->unique_name() }); - ctx.def2loop[wrapper] = loop.head; - //wrapper->attributes_.intrinsic = Intrinsic::SCFBackEdge; - - //TODO - //auto header_variant_type = loop.continue_intrinsic->type()->op(0)->as(); - //wrapper->jump(loop.continue_intrinsic, { world.variant(header_variant_type, tuple_from_params(world, wrapper->params()), variant_index) }); - - auto old_app = rewire.cont->body(); - assert(old_app); - rewire.cont->jump(wrapper, old_app->args(), old_app->debug()); - } else { - auto& nlj = rewire.non_local_jump; - - auto old_fn_type = nlj.final_destination->type(); - auto wrapper = world.continuation(old_fn_type, {"synthetic_nlj_wrapper_to" + nlj.final_destination->unique_name() }); - ctx.def2loop[wrapper] = loop.head; - // wrapper->attributes_.intrinsic = Intrinsic::SCFNonLocalJump; - - const Def* argument = tuple_from_params(world, wrapper->params()); - Continuation* first_jump = nullptr; - - DispatchTarget destination; - destination.cont = nlj.final_destination; - - for (int i = nlj.enters.size() - 1; i >= 0; i--) { - StructuredLoop* loop_to_enter = nlj.enters[i]; - - auto variant_index = index_of_destination(loop_to_enter->inner_destinations, destination); - auto header_variant_type = loop_to_enter->pre_header->type()->op(0)->as(); - argument = world.variant(header_variant_type, argument, variant_index); - - first_jump = loop_to_enter->pre_header; - destination = {}; - destination.entry = loop_to_enter; - } - - for (int i = nlj.exits.size() - 1; i >= 0; i--) { - StructuredLoop* loop_to_exit = nlj.exits[i]; - - auto variant_index = index_of_destination(loop_to_exit->outer_destinations, destination); - auto header_variant_type = loop_to_exit->exit->type()->op(0)->as(); - argument = world.variant(header_variant_type, argument, variant_index); - - first_jump = loop_to_exit->exit; - destination = {}; - destination.exit = loop_to_exit; - } - - assert(first_jump != nullptr); - wrapper->jump(first_jump, { argument }); - - auto old_app = rewire.cont->body(); - assert(old_app); - rewire.cont->jump(wrapper, old_app->args(), old_app->debug()); - } - } - } -} - -void structure_loops(World& world) { - Scope::for_each(world, [&](Scope& scope) { - ScopeContext context(scope); - - const LoopTree& looptree = context.cfa.f_cfg().looptree(); - tag_continuations(context, looptree.root(), nullptr); - collect_dispatch_targets(world, context, looptree.root()); - - create_headers(world, context, looptree.root()); - create_epilogues(world, context, looptree.root()); - - rewire_loops(world, context, looptree.root()); - scope.update(); - }); -} - -void structure_flow(World& world) { - Scope::for_each(world, [&](const Scope& scope) { - CFA cfa(scope); - auto& dom_tree = cfa.f_cfg().domtree(); - auto& post_dom_tree = cfa.b_cfg().domtree(); - - for (auto def : scope.defs()) { - if (auto cont = def->isa_nom()) { - if (cont->preds().size() <= 1) - continue; - - auto dominator = dom_tree.idom(cfa[cont]); - auto dominator_post_dominator = post_dom_tree.idom(dominator); - bool needs_join = dominator_post_dominator->continuation() != cont; - if (needs_join) { - assert(false && "Not structured CF !"); - // TODO: insert join node into dominator and redirect dominated nodes to take it - } - } - } - }); -} - -} diff --git a/src/thorin/transform/structurize.h b/src/thorin/transform/structurize.h deleted file mode 100644 index 0f82cf9ae..000000000 --- a/src/thorin/transform/structurize.h +++ /dev/null @@ -1,12 +0,0 @@ -#include "thorin/analyses/looptree.h" -#include "thorin/analyses/scope.h" -#include "thorin/analyses/cfg.h" - -namespace thorin { - -class World; - -void structure_loops(World& world); -void structure_flow(World& world); - -} \ No newline at end of file From 12890d82be31931f786851019695bc1316192bce Mon Sep 17 00:00:00 2001 From: Matthias Kurtenacker Date: Fri, 13 Jan 2023 13:46:43 +0100 Subject: [PATCH 149/342] Destroy obsolete continuations during partial evaluation. --- src/thorin/continuation.h | 12 ++++++++++++ src/thorin/rec_stream.cpp | 1 + src/thorin/transform/partial_evaluation.cpp | 10 ++++++++++ 3 files changed, 23 insertions(+) diff --git a/src/thorin/continuation.h b/src/thorin/continuation.h index ddfed253e..fc3be19c4 100644 --- a/src/thorin/continuation.h +++ b/src/thorin/continuation.h @@ -214,6 +214,18 @@ class Continuation : public Def { } return true; } + bool never_called() const { + for (auto use : uses()) { + if (auto app = use->isa()) { + if (app->num_uses() != 0) { + return false; + } + } else if (!use->isa()) { + return false; + } + } + return true; + } bool dead_ = false; std::vector params_; diff --git a/src/thorin/rec_stream.cpp b/src/thorin/rec_stream.cpp index d4507d775..fa4547eb6 100644 --- a/src/thorin/rec_stream.cpp +++ b/src/thorin/rec_stream.cpp @@ -55,6 +55,7 @@ void RecStreamer::run() { std::vector param_names; for (auto param : cont->params()) param_names.push_back(param->unique_name()); s.fmt("{}: {} = ({, }) => {{\t\n", cont->unique_name(), cont->type(), param_names); + run(cont->filter()); run(cont->body()); // TODO app node s.fmt("\b\n}}"); } else { diff --git a/src/thorin/transform/partial_evaluation.cpp b/src/thorin/transform/partial_evaluation.cpp index 1b0739a7e..9f1124734 100644 --- a/src/thorin/transform/partial_evaluation.cpp +++ b/src/thorin/transform/partial_evaluation.cpp @@ -200,6 +200,16 @@ bool PartialEvaluator::run() { jump_to_dropped_call(continuation, target, specialize); + while (callee && callee->never_called()) { + if (callee->has_body()) { + auto new_callee = const_cast(callee->body()->callee()->isa()); + callee->destroy("partial_evaluation"); + callee = new_callee; + } else { + callee = nullptr; + } + } + if (lower2cff_ && fold) { // re-examine next iteration: // maybe the specialization is not top-level anymore which might need further specialization From 3053806191939678acb6947d68a033bf9ff59555 Mon Sep 17 00:00:00 2001 From: Matthias Kurtenacker Date: Mon, 16 Jan 2023 16:59:02 +0100 Subject: [PATCH 150/342] Rewrite filter when stubs are created in mangler. --- src/thorin/continuation.cpp | 3 +++ src/thorin/continuation.h | 2 ++ src/thorin/transform/mangle.cpp | 3 ++- 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/thorin/continuation.cpp b/src/thorin/continuation.cpp index a33183d17..1f98867ec 100644 --- a/src/thorin/continuation.cpp +++ b/src/thorin/continuation.cpp @@ -61,7 +61,10 @@ Continuation::Continuation(const FnType* fn, const Attributes& attributes, Debug Continuation* Continuation::stub() const { Rewriter rewriter; + stub(rewriter); +} +Continuation* Continuation::stub(Rewriter& rewriter) const { auto result = world().continuation(type(), attributes(), debug_history()); for (size_t i = 0, e = num_params(); i != e; ++i) { result->param(i)->set_name(debug_history().name); diff --git a/src/thorin/continuation.h b/src/thorin/continuation.h index fc3be19c4..6963a6672 100644 --- a/src/thorin/continuation.h +++ b/src/thorin/continuation.h @@ -13,6 +13,7 @@ namespace thorin { class Continuation; class Scope; +struct Rewriter; typedef std::vector Continuations; @@ -138,6 +139,7 @@ class Continuation : public Def { const FnType* type() const { return Def::type()->as(); } Continuation* stub() const; + Continuation* stub(Rewriter& rewriter) const; const Param* append_param(const Type* type, Debug dbg = {}); Continuations preds() const; Continuations succs() const; diff --git a/src/thorin/transform/mangle.cpp b/src/thorin/transform/mangle.cpp index 629134fb7..6ac5bda6e 100644 --- a/src/thorin/transform/mangle.cpp +++ b/src/thorin/transform/mangle.cpp @@ -122,7 +122,8 @@ Continuation* Mangler::mangle() { Continuation* Mangler::mangle_head(Continuation* old_continuation) { assert(!def2def_.contains(old_continuation)); assert(old_continuation->has_body()); - Continuation* new_continuation = old_continuation->stub(); + Rewriter rewriter{def2def_}; + Continuation* new_continuation = old_continuation->stub(rewriter); def2def_[old_continuation] = new_continuation; for (size_t i = 0, e = old_continuation->num_params(); i != e; ++i) From d00f3bd1e954588b3e25df1715c00a4ae3fc8d2e Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Tue, 17 Jan 2023 13:52:01 +0100 Subject: [PATCH 151/342] nuke last remnants of structured control flow support --- src/thorin/continuation.cpp | 25 ------------------------- src/thorin/continuation.h | 21 --------------------- src/thorin/world.h | 4 ---- 3 files changed, 50 deletions(-) diff --git a/src/thorin/continuation.cpp b/src/thorin/continuation.cpp index ca7675235..76a030c4a 100644 --- a/src/thorin/continuation.cpp +++ b/src/thorin/continuation.cpp @@ -262,31 +262,6 @@ void Continuation::match(const Def* val, Continuation* otherwise, Defs patterns, verify(); } -/*void Continuation::structured_loop_merge(const Continuation* loop_header, ArrayRef targets) { - attributes_.intrinsic = Intrinsic::SCFLoopMerge; - attributes_.scf_metadata.loop_epilogue.loop_header = loop_header; - resize(targets.size()); - size_t x = 0; - for (auto target : targets) - set_op(x++, target); -} - -void Continuation::structured_loop_continue(const Continuation* loop_header) { - attributes_.intrinsic = Intrinsic::SCFLoopContinue; - resize(1); - set_op(0, loop_header); -} - -void Continuation::structured_loop_header(const Continuation* loop_epilogue, const Continuation* loop_continue, ArrayRef targets) { - attributes_.intrinsic = Intrinsic::SCFLoopHeader; - resize(targets.size()); - attributes_.scf_metadata.loop_header.continue_target = loop_continue; - attributes_.scf_metadata.loop_header.merge_target = loop_epilogue; - size_t x = 0; - for (auto target : targets) - set_op(x++, target); -}*/ - void Continuation::verify() const { if (!has_body()) assertf(filter()->is_empty(), "continuations with no body should have an empty (no) filter"); diff --git a/src/thorin/continuation.h b/src/thorin/continuation.h index 353d04576..2bf830bc5 100644 --- a/src/thorin/continuation.h +++ b/src/thorin/continuation.h @@ -111,9 +111,6 @@ enum class Intrinsic : uint8_t { Pipeline, ///< Intrinsic loop-pipelining-HLS-Backend Branch, ///< branch(cond, T, F). Match, ///< match(val, otherwise, (case1, cont1), (case2, cont2), ...) - LoopBegin, ///< - LoopBreak, ///< - LoopContinue, ///< PeInfo, ///< Partial evaluation debug info. EndScope ///< Dummy function which marks the end of a @p Scope. }; @@ -125,24 +122,9 @@ enum class Intrinsic : uint8_t { */ class Continuation : public Def { public: - /// Stores information about structured control flow that should not be encoded in ops, as ops encode control flow - union SCFMetadata { - struct { - const Continuation* continue_target; - const Continuation* merge_target; - } loop_header; - struct { - const Continuation* loop_header; - } loop_epilogue; - struct { - const Continuation* merge_target; - } selection_header; - }; - struct Attributes { Intrinsic intrinsic = Intrinsic::None; CC cc = CC::C; - SCFMetadata scf_metadata = {}; Attributes(Intrinsic intrinsic) : intrinsic(intrinsic) {} Attributes(CC cc = CC::C) : cc(cc) {} @@ -207,9 +189,6 @@ class Continuation : public Def { void jump(const Def* callee, Defs args, Debug dbg = {}); void branch(const Def* cond, const Def* t, const Def* f, Debug dbg = {}); void match(const Def* val, Continuation* otherwise, Defs patterns, ArrayRef continuations, Debug dbg = {}); - //void structured_loop_merge(const Continuation* loop_header, ArrayRef targets); - //void structured_loop_continue(const Continuation* loop_header); - //void structured_loop_header(const Continuation* loop_epilogue, const Continuation* loop_continue, ArrayRef targets); void verify() const; const Filter* filter() const { return op(1)->as(); } diff --git a/src/thorin/world.h b/src/thorin/world.h index 8449a5758..96e30af2b 100644 --- a/src/thorin/world.h +++ b/src/thorin/world.h @@ -245,10 +245,6 @@ class World : public TypeTable, public Streamable { Continuation* end_scope() const { return data_.end_scope_; } const Filter* filter(const Defs, Debug dbg = {}); - Continuation* loop_enter(Types types, Continuations, Continuations); - Continuation* loop_continue(Types types); - Continuation* loop_break(Types types); - /// Performs dead code, unreachable code and unused type elimination. void cleanup(); void opt(); From a4f048d5f378cb2723a2f200dbf58e28a15efd01 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Tue, 17 Jan 2023 13:57:36 +0100 Subject: [PATCH 152/342] made shady dependency optional --- CMakeLists.txt | 2 +- cmake/thorin-config.cmake.in | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c47d74578..67189ba7c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -44,7 +44,7 @@ else() message(STATUS "Building without LLVM and RV. Specify LLVM_DIR to compile with LLVM.") endif() -find_package(shady REQUIRED CONFIG) +find_package(shady CONFIG) if (shady_FOUND) message(STATUS "Found shady at ${shady_DIR}") set(THORIN_ENABLE_SHADY TRUE) diff --git a/cmake/thorin-config.cmake.in b/cmake/thorin-config.cmake.in index b55145178..833c1abbe 100644 --- a/cmake/thorin-config.cmake.in +++ b/cmake/thorin-config.cmake.in @@ -30,7 +30,7 @@ find_package(Half REQUIRED) set(Thorin_HAS_LLVM_SUPPORT @LLVM_FOUND@) set(Thorin_HAS_RV_SUPPORT @RV_FOUND@) -set(Thorin_HAS_SPIRV_SUPPORT @SPIRV_ENABLED@) +set(Thorin_HAS_SHADY_SUPPORT @shady_FOUND@) set(AnyDSL_LLVM_LINK_SHARED @AnyDSL_LLVM_LINK_SHARED@) if(Thorin_HAS_LLVM_SUPPORT) From 698f559ef6442fc50e03942c2a7eb3639d495aad Mon Sep 17 00:00:00 2001 From: Matthias Kurtenacker Date: Tue, 17 Jan 2023 23:24:32 +0100 Subject: [PATCH 153/342] Small fixes. --- src/thorin/continuation.cpp | 2 +- src/thorin/rec_stream.cpp | 12 ++++++++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/thorin/continuation.cpp b/src/thorin/continuation.cpp index 1f98867ec..a7a56242d 100644 --- a/src/thorin/continuation.cpp +++ b/src/thorin/continuation.cpp @@ -61,7 +61,7 @@ Continuation::Continuation(const FnType* fn, const Attributes& attributes, Debug Continuation* Continuation::stub() const { Rewriter rewriter; - stub(rewriter); + return stub(rewriter); } Continuation* Continuation::stub(Rewriter& rewriter) const { diff --git a/src/thorin/rec_stream.cpp b/src/thorin/rec_stream.cpp index fa4547eb6..4a17429c8 100644 --- a/src/thorin/rec_stream.cpp +++ b/src/thorin/rec_stream.cpp @@ -56,7 +56,15 @@ void RecStreamer::run() { for (auto param : cont->params()) param_names.push_back(param->unique_name()); s.fmt("{}: {} = ({, }) => {{\t\n", cont->unique_name(), cont->type(), param_names); run(cont->filter()); - run(cont->body()); // TODO app node + if (defs.contains(cont->body())) { + auto body = cont->body(); + if (auto cont2 = body->isa_nom()) { + s.fmt("{}: {} = {}({, })", cont2->unique_name(), cont2->type(), cont2->body()->callee(), cont2->body()->args()); + } else if (!body->no_dep() && !body->isa()) + body->stream_let(s); + } else { + run(cont->body()); // TODO app node + } s.fmt("\b\n}}"); } else { s.fmt("{}: {} = {{ }}", cont->unique_name(), cont->type()); @@ -140,7 +148,7 @@ Stream& Def::stream1(Stream& s) const { } Stream& Def::stream_let(Stream& s) const { - return stream1(s.fmt("{}: {} = ", this, type())).endl(); + return stream1(s.fmt("{}: {} = ", this->unique_name(), type())).endl(); } Stream& World::stream(Stream& s) const { From 87e7cdb8a66419a2641ccb38ca229a8420325937 Mon Sep 17 00:00:00 2001 From: Matthias Kurtenacker Date: Wed, 18 Jan 2023 12:01:16 +0100 Subject: [PATCH 154/342] Guard continuation creation context, as it takes eons to compute. --- src/thorin/config.h.in | 1 + src/thorin/debug.h | 6 ++++++ src/thorin/rec_stream.cpp | 2 ++ src/thorin/world.cpp | 5 +++++ 4 files changed, 14 insertions(+) diff --git a/src/thorin/config.h.in b/src/thorin/config.h.in index 9216ecd82..78f9ea402 100644 --- a/src/thorin/config.h.in +++ b/src/thorin/config.h.in @@ -3,6 +3,7 @@ #cmakedefine01 THORIN_ENABLE_CHECKS #cmakedefine01 THORIN_ENABLE_PROFILING +#cmakedefine01 THORIN_ENABLE_CREATION_CONTEXT #cmakedefine01 THORIN_ENABLE_LLVM #cmakedefine01 THORIN_ENABLE_JSON #cmakedefine01 THORIN_ENABLE_RV diff --git a/src/thorin/debug.h b/src/thorin/debug.h index ef35f2a36..05613fccb 100644 --- a/src/thorin/debug.h +++ b/src/thorin/debug.h @@ -43,13 +43,16 @@ class Debug { Debug() = default; // TODO remove Debug(std::string name, Loc loc = {}, const Def* meta = nullptr) : name(name) +#if THORIN_ENABLE_CREATION_CONTEXT , creation_context("") +#endif , loc(loc) , meta(meta) {} Debug(const char* name, Loc loc = {}, const Def* meta = nullptr) : Debug(std::string(name), loc, meta) {} +#if THORIN_ENABLE_CREATION_CONTEXT Debug(std::string name, std::string creation_context, Loc loc = {}, const Def* meta = nullptr) : name(name) , creation_context(creation_context) @@ -59,13 +62,16 @@ class Debug { Debug(const char* name, const char* creation_context, Loc loc = {}, const Def* meta = nullptr) : Debug(std::string(name), std::string(creation_context), loc, meta) {} +#endif Debug(Loc loc) : Debug("", loc) {} //Debug(const Def*); std::string name; +#if THORIN_ENABLE_CREATION_CONTEXT std::string creation_context; +#endif Loc loc; const Def* meta = nullptr; }; diff --git a/src/thorin/rec_stream.cpp b/src/thorin/rec_stream.cpp index 4a17429c8..9b800127e 100644 --- a/src/thorin/rec_stream.cpp +++ b/src/thorin/rec_stream.cpp @@ -108,9 +108,11 @@ Stream& Def::stream1(Stream& s) const { if (auto param = isa()) { return s.fmt("{}.{}", param->continuation(), param->unique_name()); } else if (isa()) { +#if THORIN_ENABLE_CREATION_CONTEXT if (debug().creation_context != "") return s.fmt("cont {} [{}]", unique_name(), debug().creation_context); else +#endif return s.fmt("cont {}", unique_name()); } else if (auto app = isa()) { return s.fmt("{}({, })", app->callee(), app->args()); diff --git a/src/thorin/world.cpp b/src/thorin/world.cpp index f465fba27..944dd1b83 100644 --- a/src/thorin/world.cpp +++ b/src/thorin/world.cpp @@ -1103,15 +1103,20 @@ const Def* World::run(const Def* def, Debug dbg) { */ Continuation* World::continuation(const FnType* fn, Continuation::Attributes attributes, Debug dbg) { +#if THORIN_ENABLE_CREATION_CONTEXT void *array[10]; size_t size = backtrace(array, 10); assert(size >= 2); char ** symbols = backtrace_symbols(array, 10); dbg.creation_context = symbols[1]; +#endif auto cont = put(fn, attributes, dbg); + +#if THORIN_ENABLE_CREATION_CONTEXT free(symbols); +#endif size_t i = 0; for (auto op : fn->ops()) { From d4b30a103c50c52ebfc6ce3f10352f2688d7fa6b Mon Sep 17 00:00:00 2001 From: Matthias Kurtenacker Date: Wed, 18 Jan 2023 20:17:04 +0100 Subject: [PATCH 155/342] Fix: Tuple type with single element. --- src/thorin/type.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/thorin/type.cpp b/src/thorin/type.cpp index 9b8f8b49f..7918b0e45 100644 --- a/src/thorin/type.cpp +++ b/src/thorin/type.cpp @@ -199,7 +199,7 @@ TypeTable::TypeTable() } const Type* TypeTable::tuple_type(Types ops) { - return (ops.size() == 1 && is_thin(ops[0])) ? ops.front() : insert(*this, ops); + return ops.size() == 1 ? ops.front() : insert(*this, ops); } const StructType* TypeTable::struct_type(Symbol name, size_t size) { From 562992b5953d1dbe457ae2676e910c4d628a8aa0 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Sun, 22 Jan 2023 13:23:59 +0100 Subject: [PATCH 156/342] added a World& field to every Def --- src/thorin/continuation.cpp | 12 ++-- src/thorin/continuation.h | 6 +- src/thorin/def.cpp | 8 ++- src/thorin/def.h | 5 +- src/thorin/primop.cpp | 64 +++++++++---------- src/thorin/primop.h | 118 ++++++++++++++++++------------------ src/thorin/world.cpp | 52 ++++++++-------- src/thorin/world.h | 12 ++-- 8 files changed, 138 insertions(+), 139 deletions(-) diff --git a/src/thorin/continuation.cpp b/src/thorin/continuation.cpp index f729937f8..c66302040 100644 --- a/src/thorin/continuation.cpp +++ b/src/thorin/continuation.cpp @@ -10,8 +10,8 @@ namespace thorin { //------------------------------------------------------------------------------ -Param::Param(const Type* type, Continuation* continuation, size_t index, Debug dbg) - : Def(Node_Param, type, 1, dbg) +Param::Param(World& world, const Type* type, Continuation* continuation, size_t index, Debug dbg) + : Def(Node_Param, world, type, 1, dbg) , index_(index) { set_op(0, continuation); @@ -19,7 +19,7 @@ Param::Param(const Type* type, Continuation* continuation, size_t index, Debug d //------------------------------------------------------------------------------ -App::App(const Defs ops, Debug dbg) : Def(Node_App, ops[0]->world().bottom_type(), ops, dbg) { +App::App(World& world, const Defs ops, Debug dbg) : Def(Node_App, world, ops[0]->world().bottom_type(), ops, dbg) { #if THORIN_ENABLE_CHECKS verify(); if (auto cont = callee()->isa_nom()) @@ -41,7 +41,7 @@ void App::verify() const { //------------------------------------------------------------------------------ -Filter::Filter(World& world, const Defs defs, Debug dbg) : Def(Node_Filter, world.bottom_type(), defs, dbg) {} +Filter::Filter(World& world, const Defs defs, Debug dbg) : Def(Node_Filter, world, world.bottom_type(), defs, dbg) {} const Filter* Filter::cut(ArrayRef indices) const { return world().filter(ops().cut(indices), debug()); @@ -49,8 +49,8 @@ const Filter* Filter::cut(ArrayRef indices) const { //------------------------------------------------------------------------------ -Continuation::Continuation(const FnType* fn, const Attributes& attributes, Debug dbg) - : Def(Node_Continuation, fn, 2, dbg) +Continuation::Continuation(World& w, const FnType* fn, const Attributes& attributes, Debug dbg) + : Def(Node_Continuation, w, fn, 2, dbg) , attributes_(attributes) { params_.reserve(fn->num_ops()); diff --git a/src/thorin/continuation.h b/src/thorin/continuation.h index 9f34fe992..f88e24885 100644 --- a/src/thorin/continuation.h +++ b/src/thorin/continuation.h @@ -24,7 +24,7 @@ typedef std::vector Continuations; */ class Param : public Def { private: - Param(const Type* type, Continuation* continuation, size_t index, Debug dbg); + Param(World&, const Type* type, Continuation* continuation, size_t index, Debug dbg); public: Continuation* continuation() const { return op(0)->as_nom(); } @@ -53,7 +53,7 @@ class Filter : public Def { class App : public Def { private: - App(const Defs ops, Debug dbg); + App(World&, const Defs ops, Debug dbg); public: const Def* callee() const { return op(0); } @@ -130,7 +130,7 @@ class Continuation : public Def { }; private: - Continuation(const FnType* fn, const Attributes& attributes, Debug dbg); + Continuation(World&, const FnType* fn, const Attributes& attributes, Debug dbg); virtual ~Continuation() { for (auto param : params()) delete param; } public: diff --git a/src/thorin/def.cpp b/src/thorin/def.cpp index 2c01a687d..03516f1d9 100644 --- a/src/thorin/def.cpp +++ b/src/thorin/def.cpp @@ -14,9 +14,10 @@ namespace thorin { size_t Def::gid_counter_ = 1; -Def::Def(NodeTag tag, const Type* type, Defs ops, Debug dbg) +Def::Def(NodeTag tag, World& world, const Type* type, Defs ops, Debug dbg) : tag_(tag) , ops_(ops.size()) + , world_(world) , type_(type) , debug_(dbg) , gid_(gid_counter_++) @@ -29,9 +30,10 @@ Def::Def(NodeTag tag, const Type* type, Defs ops, Debug dbg) set_op(i, ops[i]); } -Def::Def(NodeTag tag, const Type* type, size_t size, Debug dbg) +Def::Def(NodeTag tag, World& world, const Type* type, size_t size, Debug dbg) : tag_(tag) , ops_(size) + , world_(world) , type_(type) , debug_(dbg) , gid_(gid_counter_++) @@ -145,7 +147,7 @@ void Def::replace_uses(const Def* with) const { } } -World& Def::world() const { return *static_cast(&type()->table()); } +World& Def::world() const { return world_; } uint64_t UseHash::hash(Use use) { assert(use->gid() != uint32_t(-1)); diff --git a/src/thorin/def.h b/src/thorin/def.h index a40a63738..18118e9d4 100644 --- a/src/thorin/def.h +++ b/src/thorin/def.h @@ -106,9 +106,9 @@ class Def : public RuntimeCast, public Streamable { protected: /// Constructor for a @em structural Def. - Def(NodeTag tag, const Type* type, Defs args, Debug dbg); + Def(NodeTag tag, World&, const Type* type, Defs args, Debug dbg); /// Constructor for a @em nom Def. - Def(NodeTag tag, const Type* type, size_t size, Debug); + Def(NodeTag tag, World&, const Type* type, size_t size, Debug); virtual ~Def() {} void clear_type() { type_ = nullptr; } @@ -230,6 +230,7 @@ class Def : public RuntimeCast, public Streamable { private: const NodeTag tag_; std::vector ops_; + World& world_; const Type* type_; mutable Uses uses_; mutable Debug debug_; diff --git a/src/thorin/primop.cpp b/src/thorin/primop.cpp index ddad23575..9a06a93a1 100644 --- a/src/thorin/primop.cpp +++ b/src/thorin/primop.cpp @@ -15,16 +15,16 @@ namespace thorin { */ PrimLit::PrimLit(World& world, PrimTypeTag tag, Box box, Debug dbg) - : Literal((NodeTag) tag, world.prim_type(tag), dbg) + : Literal((NodeTag) tag, world, world.prim_type(tag), dbg) , box_(box) {} -Cmp::Cmp(CmpTag tag, const Def* lhs, const Def* rhs, Debug dbg) - : BinOp((NodeTag) tag, lhs->world().type_bool(vector_length(lhs->type())), lhs, rhs, dbg) +Cmp::Cmp(CmpTag tag, World& world, const Def* lhs, const Def* rhs, Debug dbg) + : BinOp((NodeTag) tag, world, world.type_bool(vector_length(lhs->type())), lhs, rhs, dbg) {} DefiniteArray::DefiniteArray(World& world, const Type* elem, Defs args, Debug dbg) - : Aggregate(Node_DefiniteArray, args, dbg) + : Aggregate(Node_DefiniteArray, world, args, dbg) { set_type(world.definite_array_type(elem, args.size())); #if THORIN_ENABLE_CHECKS @@ -34,13 +34,13 @@ DefiniteArray::DefiniteArray(World& world, const Type* elem, Defs args, Debug db } IndefiniteArray::IndefiniteArray(World& world, const Type* elem, const Def* dim, Debug dbg) - : Aggregate(Node_IndefiniteArray, {dim}, dbg) + : Aggregate(Node_IndefiniteArray, world, {dim}, dbg) { set_type(world.indefinite_array_type(elem)); } Tuple::Tuple(World& world, Defs args, Debug dbg) - : Aggregate(Node_Tuple, args, dbg) + : Aggregate(Node_Tuple, world, args, dbg) { Array elems(num_ops()); for (size_t i = 0, e = num_ops(); i != e; ++i) @@ -50,7 +50,7 @@ Tuple::Tuple(World& world, Defs args, Debug dbg) } Vector::Vector(World& world, Defs args, Debug dbg) - : Aggregate(Node_Vector, args, dbg) + : Aggregate(Node_Vector, world, args, dbg) { if (auto primtype = args.front()->type()->isa()) { assert(primtype->length() == 1); @@ -62,10 +62,9 @@ Vector::Vector(World& world, Defs args, Debug dbg) } } -LEA::LEA(const Def* ptr, const Def* index, Debug dbg) - : Def(Node_LEA, nullptr, {ptr, index}, dbg) +LEA::LEA(World& world, const Def* ptr, const Def* index, Debug dbg) + : Def(Node_LEA, world, nullptr, {ptr, index}, dbg) { - auto& world = index->world(); auto type = ptr_type(); if (auto tuple = ptr_pointee()->isa()) { set_type(world.ptr_type(get(tuple->ops(), index), type->length(), type->device(), type->addr_space())); @@ -81,54 +80,51 @@ LEA::LEA(const Def* ptr, const Def* index, Debug dbg) } } -Known::Known(const Def* def, Debug dbg) - : Def(Node_Known, def->world().type_bool(), {def}, dbg) +Known::Known(World& world, const Def* def, Debug dbg) + : Def(Node_Known, world, world.type_bool(), {def}, dbg) {} -AlignOf::AlignOf(const Def* def, Debug dbg) - : Def(Node_AlignOf, def->world().type_qs64(), {def}, dbg) +AlignOf::AlignOf(World& world, const Def* def, Debug dbg) + : Def(Node_AlignOf, world, world.type_qs64(), {def}, dbg) {} -SizeOf::SizeOf(const Def* def, Debug dbg) - : Def(Node_SizeOf, def->world().type_qs64(), {def}, dbg) +SizeOf::SizeOf(World& world, const Def* def, Debug dbg) + : Def(Node_SizeOf, world, world.type_qs64(), {def}, dbg) {} -Slot::Slot(const Type* type, const Def* frame, Debug dbg) - : Def(Node_Slot, type->table().ptr_type(type), {frame}, dbg) +Slot::Slot(World& world, const Type* type, const Def* frame, Debug dbg) + : Def(Node_Slot, world, type->table().ptr_type(type), {frame}, dbg) { assert(frame->type()->isa()); } -Global::Global(const Def* init, bool is_mutable, Debug dbg) - : Def(Node_Global, init->type()->table().ptr_type(init->type()), {init}, dbg) +Global::Global(World& world, const Def* init, bool is_mutable, Debug dbg) + : Def(Node_Global, world, init->type()->table().ptr_type(init->type()), {init}, dbg) , is_mutable_(is_mutable) { assert(!init->has_dep(Dep::Param)); } -Alloc::Alloc(const Type* type, const Def* mem, const Def* extra, Debug dbg) - : MemOp(Node_Alloc, nullptr, {mem, extra}, dbg) +Alloc::Alloc(World& world, const Type* type, const Def* mem, const Def* extra, Debug dbg) + : MemOp(Node_Alloc, world, nullptr, {mem, extra}, dbg) { - World& w = mem->world(); - set_type(w.tuple_type({w.mem_type(), w.ptr_type(type)})); + set_type(world.tuple_type({world.mem_type(), world.ptr_type(type)})); } -Load::Load(const Def* mem, const Def* ptr, Debug dbg) - : Access(Node_Load, nullptr, {mem, ptr}, dbg) +Load::Load(World& world, const Def* mem, const Def* ptr, Debug dbg) + : Access(Node_Load, world, nullptr, {mem, ptr}, dbg) { - World& w = mem->world(); - set_type(w.tuple_type({w.mem_type(), ptr->type()->as()->pointee()})); + set_type(world.tuple_type({world.mem_type(), ptr->type()->as()->pointee()})); } -Enter::Enter(const Def* mem, Debug dbg) - : MemOp(Node_Enter, nullptr, {mem}, dbg) +Enter::Enter(World& world, const Def* mem, Debug dbg) + : MemOp(Node_Enter, world, nullptr, {mem}, dbg) { - World& w = mem->world(); - set_type(w.tuple_type({w.mem_type(), w.frame_type()})); + set_type(world.tuple_type({world.mem_type(), world.frame_type()})); } -Assembly::Assembly(const Type *type, Defs inputs, std::string asm_template, ArrayRef output_constraints, ArrayRef input_constraints, ArrayRef clobbers, Flags flags, Debug dbg) - : MemOp(Node_Assembly, type, inputs, dbg) +Assembly::Assembly(World& world, const Type *type, Defs inputs, std::string asm_template, ArrayRef output_constraints, ArrayRef input_constraints, ArrayRef clobbers, Flags flags, Debug dbg) + : MemOp(Node_Assembly, world, type, inputs, dbg) , asm_template_(asm_template) , output_constraints_(output_constraints) , input_constraints_(input_constraints) diff --git a/src/thorin/primop.h b/src/thorin/primop.h index ad4e2efcb..db2aaf175 100644 --- a/src/thorin/primop.h +++ b/src/thorin/primop.h @@ -10,16 +10,16 @@ namespace thorin { class Literal : public Def { protected: - Literal(NodeTag tag, const Type* type, Debug dbg) - : Def(tag, type, Defs{}, dbg) + Literal(NodeTag tag, World& world, const Type* type, Debug dbg) + : Def(tag, world, type, Defs{}, dbg) {} }; /// This literal represents 'no value'. class Bottom : public Literal { private: - Bottom(const Type* type, Debug dbg) - : Literal(Node_Bottom, type, dbg) + Bottom(World& world, const Type* type, Debug dbg) + : Literal(Node_Bottom, world, type, dbg) {} const Def* rebuild(World&, const Type*, Defs) const override; @@ -30,8 +30,8 @@ class Bottom : public Literal { /// This literal represents 'any value'. class Top : public Literal { private: - Top(const Type* type, Debug dbg) - : Literal(Node_Top, type, dbg) + Top(World& world, const Type* type, Debug dbg) + : Literal(Node_Top, world, type, dbg) {} const Def* rebuild(World&, const Type*, Defs) const override; @@ -79,8 +79,8 @@ T get(ArrayRef array, const Def* def) { return array[primlit_value(de /// Akin to cond ? tval : fval. class Select : public Def { private: - Select(const Def* cond, const Def* tval, const Def* fval, Debug dbg) - : Def(Node_Select, tval->type(), {cond, tval, fval}, dbg) + Select(World& world, const Def* cond, const Def* tval, const Def* fval, Debug dbg) + : Def(Node_Select, world, tval->type(), {cond, tval, fval}, dbg) { assert(is_type_bool(cond->type())); assert(tval->type() == fval->type() && "types of both values must be equal"); @@ -100,7 +100,7 @@ class Select : public Def { /// Get the alignment in number of bytes needed for any value (including bottom) of a given @p Type. class AlignOf : public Def { private: - AlignOf(const Def* def, Debug dbg); + AlignOf(World& world, const Def* def, Debug dbg); const Def* rebuild(World&, const Type*, Defs) const override; @@ -113,7 +113,7 @@ class AlignOf : public Def { /// Get number of bytes needed for any value (including bottom) of a given @p Type. class SizeOf : public Def { private: - SizeOf(const Def* def, Debug dbg); + SizeOf(World& world, const Def* def, Debug dbg); const Def* rebuild(World&, const Type*, Defs) const override; @@ -126,8 +126,8 @@ class SizeOf : public Def { /// Base class for all side-effect free binary \p Def%s. class BinOp : public Def { protected: - BinOp(NodeTag tag, const Type* type, const Def* lhs, const Def* rhs, Debug dbg) - : Def(tag, type, {lhs, rhs}, dbg) + BinOp(NodeTag tag, World& world, const Type* type, const Def* lhs, const Def* rhs, Debug dbg) + : Def(tag, world, type, {lhs, rhs}, dbg) { assert(lhs->type() == rhs->type() && "types are not equal"); } @@ -140,8 +140,8 @@ class BinOp : public Def { /// One of \p ArithOpTag arithmetic operation. class ArithOp : public BinOp { private: - ArithOp(ArithOpTag tag, const Def* lhs, const Def* rhs, Debug dbg) - : BinOp((NodeTag) tag, lhs->type(), lhs, rhs, dbg) + ArithOp(ArithOpTag tag, World& world, const Def* lhs, const Def* rhs, Debug dbg) + : BinOp((NodeTag) tag, world, lhs->type(), lhs, rhs, dbg) {} const Def* rebuild(World&, const Type*, Defs) const override; @@ -157,7 +157,7 @@ class ArithOp : public BinOp { /// One of \p CmpTag compare. class Cmp : public BinOp { private: - Cmp(CmpTag tag, const Def* lhs, const Def* rhs, Debug dbg); + Cmp(CmpTag tag, World& world, const Def* lhs, const Def* rhs, Debug dbg); const Def* rebuild(World&, const Type*, Defs) const override; @@ -172,8 +172,8 @@ class Cmp : public BinOp { /// Common mathematical function such as `sin()` or `cos()`. class MathOp : public Def { private: - MathOp(MathOpTag tag, const Type* type, Defs args, Debug dbg) - : Def((NodeTag)tag, type, args, dbg) + MathOp(MathOpTag tag, World& world, const Type* type, Defs args, Debug dbg) + : Def((NodeTag)tag, world, type, args, dbg) {} const Def* rebuild(World&, const Type*, Defs) const override; @@ -189,8 +189,8 @@ class MathOp : public Def { /// Base class for @p Bitcast and @p Cast. class ConvOp : public Def { protected: - ConvOp(NodeTag tag, const Def* from, const Type* to, Debug dbg) - : Def(tag, to, {from}, dbg) + ConvOp(NodeTag tag, World& world, const Def* from, const Type* to, Debug dbg) + : Def(tag, world, to, {from}, dbg) {} public: @@ -200,8 +200,8 @@ class ConvOp : public Def { /// Converts from to type to. class Cast : public ConvOp { private: - Cast(const Type* to, const Def* from, Debug dbg) - : ConvOp(Node_Cast, from, to, dbg) + Cast(World& world, const Type* to, const Def* from, Debug dbg) + : ConvOp(Node_Cast, world, from, to, dbg) {} const Def* rebuild(World&, const Type*, Defs) const override; @@ -212,8 +212,8 @@ class Cast : public ConvOp { /// Reinterprets the bits of from as type to. class Bitcast : public ConvOp { private: - Bitcast(const Type* to, const Def* from, Debug dbg) - : ConvOp(Node_Bitcast, from, to, dbg) + Bitcast(World& world, const Type* to, const Def* from, Debug dbg) + : ConvOp(Node_Bitcast, world, from, to, dbg) {} const Def* rebuild(World&, const Type*, Defs) const override; @@ -224,8 +224,8 @@ class Bitcast : public ConvOp { /// Base class for all aggregate data constructers. class Aggregate : public Def { protected: - Aggregate(NodeTag tag, Defs args, Debug dbg) - : Def(tag, nullptr /*set later*/, args, dbg) + Aggregate(NodeTag tag, World& world, Defs args, Debug dbg) + : Def(tag, world, nullptr /*set later*/, args, dbg) {} }; @@ -274,8 +274,8 @@ class Tuple : public Aggregate { /// Data constructor for a @p VariantType. class Variant : public Def { private: - Variant(const VariantType* variant_type, const Def* value, size_t index, Debug dbg) - : Def(Node_Variant, variant_type, {value}, dbg), index_(index) + Variant(World& world, const VariantType* variant_type, const Def* value, size_t index, Debug dbg) + : Def(Node_Variant, world, variant_type, {value}, dbg), index_(index) { assert(variant_type->op(index) == value->type()); } @@ -297,8 +297,8 @@ class Variant : public Def { /// Yields the tag/index for this variant in the supplied integer type class VariantIndex : public Def { private: - VariantIndex(const Type* int_type, const Def* value, Debug dbg) - : Def(Node_VariantIndex, int_type, {value}, dbg) + VariantIndex(World& world, const Type* int_type, const Def* value, Debug dbg) + : Def(Node_VariantIndex, world, int_type, {value}, dbg) { assert(value->type()->isa()); assert(is_type_s(int_type) || is_type_u(int_type)); @@ -311,8 +311,8 @@ class VariantIndex : public Def { class VariantExtract : public Def { private: - VariantExtract(const Type* type, const Def* value, size_t index, Debug dbg) - : Def(Node_VariantExtract, type, {value}, dbg), index_(index) + VariantExtract(World& world, const Type* type, const Def* value, size_t index, Debug dbg) + : Def(Node_VariantExtract, world, type, {value}, dbg), index_(index) { assert(value->type()->as()->op(index) == type); } @@ -333,8 +333,8 @@ class VariantExtract : public Def { /// Data constructor for a @p ClosureType. class Closure : public Aggregate { private: - Closure(const ClosureType* closure_type, const Def* fn, const Def* env, Debug dbg) - : Aggregate(Node_Closure, {fn, env}, dbg) + Closure(World& world, const ClosureType* closure_type, const Def* fn, const Def* env, Debug dbg) + : Aggregate(Node_Closure, world, {fn, env}, dbg) { set_type(closure_type); } @@ -351,8 +351,8 @@ class Closure : public Aggregate { /// Data constructor for a @p StructType. class StructAgg : public Aggregate { private: - StructAgg(const StructType* struct_type, Defs args, Debug dbg) - : Aggregate(Node_StructAgg, args, dbg) + StructAgg(World& world, const StructType* struct_type, Defs args, Debug dbg) + : Aggregate(Node_StructAgg, world, args, dbg) { #if THORIN_ENABLE_CHECKS assert(struct_type->num_ops() == args.size()); @@ -383,8 +383,8 @@ class Vector : public Aggregate { /// Base class for functional @p Insert and @p Extract. class AggOp : public Def { protected: - AggOp(NodeTag tag, const Type* type, Defs args, Debug dbg) - : Def(tag, type, args, dbg) + AggOp(NodeTag tag, World& world, const Type* type, Defs args, Debug dbg) + : Def(tag, world, type, args, dbg) {} public: @@ -397,8 +397,8 @@ class AggOp : public Def { /// Extracts from aggregate agg the element at position index. class Extract : public AggOp { private: - Extract(const Def* agg, const Def* index, Debug dbg) - : AggOp(Node_Extract, extracted_type(agg, index), {agg, index}, dbg) + Extract(World& world, const Def* agg, const Def* index, Debug dbg) + : AggOp(Node_Extract, world, extracted_type(agg, index), {agg, index}, dbg) {} const Def* rebuild(World&, const Type*, Defs) const override; @@ -417,8 +417,8 @@ class Extract : public AggOp { */ class Insert : public AggOp { private: - Insert(const Def* agg, const Def* index, const Def* value, Debug dbg) - : AggOp(Node_Insert, agg->type(), {agg, index, value}, dbg) + Insert(World& world, const Def* agg, const Def* index, const Def* value, Debug dbg) + : AggOp(Node_Insert, world, agg->type(), {agg, index, value}, dbg) {} const Def* rebuild(World&, const Type*, Defs) const override; @@ -437,7 +437,7 @@ class Insert : public AggOp { */ class LEA : public Def { private: - LEA(const Def* ptr, const Def* index, Debug dbg); + LEA(World& world, const Def* ptr, const Def* index, Debug dbg); const Def* rebuild(World&, const Type*, Defs) const override; @@ -454,8 +454,8 @@ class LEA : public Def { /// Casts the underlying @p def to a dynamic value during @p partial_evaluation. class Hlt : public Def { private: - Hlt(const Def* def, Debug dbg) - : Def(Node_Hlt, def->type(), {def}, dbg) + Hlt(World& world, const Def* def, Debug dbg) + : Def(Node_Hlt, world, def->type(), {def}, dbg) {} const Def* rebuild(World&, const Type*, Defs) const override; @@ -469,7 +469,7 @@ class Hlt : public Def { /// Evaluates to @c true, if @p def is a literal. class Known : public Def { private: - Known(const Def* def, Debug dbg); + Known(World& world, const Def* def, Debug dbg); const Def* rebuild(World&, const Type*, Defs) const override; @@ -485,8 +485,8 @@ class Known : public Def { */ class Run : public Def { private: - Run(const Def* def, Debug dbg) - : Def(Node_Run, def->type(), {def}, dbg) + Run(World& world, const Def* def, Debug dbg) + : Def(Node_Run, world, def->type(), {def}, dbg) {} const Def* rebuild(World&, const Type*, Defs) const override; @@ -504,7 +504,7 @@ class Run : public Def { */ class Slot : public Def { private: - Slot(const Type* type, const Def* frame, Debug dbg); + Slot(World& world, const Type* type, const Def* frame, Debug dbg); public: const Def* frame() const { return op(0); } @@ -525,7 +525,7 @@ class Slot : public Def { */ class Global : public Def { private: - Global(const Def* init, bool is_mutable, Debug dbg); + Global(World& world, const Def* init, bool is_mutable, Debug dbg); public: const Def* init() const { return op(0); } @@ -547,8 +547,8 @@ class Global : public Def { /// Base class for all \p Def%s taking and producing side-effects. class MemOp : public Def { protected: - MemOp(NodeTag tag, const Type* type, Defs args, Debug dbg) - : Def(tag, type, args, dbg) + MemOp(NodeTag tag, World& world, const Type* type, Defs args, Debug dbg) + : Def(tag, world, type, args, dbg) { assert(mem()->type()->isa()); assert(args.size() >= 1); @@ -566,7 +566,7 @@ class MemOp : public Def { /// Allocates memory on the heap. class Alloc : public MemOp { private: - Alloc(const Type* type, const Def* mem, const Def* extra, Debug dbg); + Alloc(World& world, const Type* type, const Def* mem, const Def* extra, Debug dbg); public: const Def* extra() const { return op(1); } @@ -585,8 +585,8 @@ class Alloc : public MemOp { /// Base class for @p Load and @p Store. class Access : public MemOp { protected: - Access(NodeTag tag, const Type* type, Defs args, Debug dbg) - : MemOp(tag, type, args, dbg) + Access(NodeTag tag, World& world, const Type* type, Defs args, Debug dbg) + : MemOp(tag, world, type, args, dbg) { assert(args.size() >= 2); } @@ -598,7 +598,7 @@ class Access : public MemOp { /// Loads with current effect mem from ptr to produce a pair of a new effect and the loaded value. class Load : public Access { private: - Load(const Def* mem, const Def* ptr, Debug dbg); + Load(World& world, const Def* mem, const Def* ptr, Debug dbg); public: bool has_multiple_outs() const override { return true; } @@ -615,8 +615,8 @@ class Load : public Access { /// Stores with current effect mem value into ptr while producing a new effect. class Store : public Access { private: - Store(const Def* mem, const Def* ptr, const Def* value, Debug dbg) - : Access(Node_Store, mem->type(), {mem, ptr, value}, dbg) + Store(World& world, const Def* mem, const Def* ptr, const Def* value, Debug dbg) + : Access(Node_Store, world, mem->type(), {mem, ptr, value}, dbg) {} const Def* rebuild(World&, const Type*, Defs) const override; @@ -631,7 +631,7 @@ class Store : public Access { /// Creates a stack \p Frame with current effect mem. class Enter : public MemOp { private: - Enter(const Def* mem, Debug dbg); + Enter(World& world, const Def* mem, Debug dbg); const Def* rebuild(World&, const Type*, Defs) const override; @@ -655,7 +655,7 @@ class Assembly : public MemOp { }; private: - Assembly(const Type *type, Defs inputs, std::string asm_template, ArrayRef output_constraints, + Assembly(World& world, const Type *type, Defs inputs, std::string asm_template, ArrayRef output_constraints, ArrayRef input_constraints, ArrayRef clobbers, Flags flags, Debug dbg); public: diff --git a/src/thorin/world.cpp b/src/thorin/world.cpp index 47f34b6aa..013d09544 100644 --- a/src/thorin/world.cpp +++ b/src/thorin/world.cpp @@ -55,14 +55,14 @@ World::~World() { const Def* World::variant_index(const Def* value, Debug dbg) { if (auto variant = value->isa()) return literal_qu64(variant->index(), dbg); - return cse(new VariantIndex(type_qu64(), value, dbg)); + return cse(new VariantIndex(*this, type_qu64(), value, dbg)); } const Def* World::variant_extract(const Def* value, size_t index, Debug dbg) { auto type = value->type()->as()->op(index); if (auto variant = value->isa()) return variant->index() == index ? variant->value() : bottom(type); - return cse(new VariantExtract(type, value, index, dbg)); + return cse(new VariantExtract(*this, type, value, index, dbg)); } /* @@ -391,7 +391,7 @@ const Def* World::arithop(ArithOpTag tag, const Def* a, const Def* b, Debug dbg) return arithop(tag, a_lhs_lv, arithop(tag, a_same->rhs(), b, dbg), dbg); } - return cse(new ArithOp(tag, a, b, dbg)); + return cse(new ArithOp(tag, *this, a, b, dbg)); } const Def* World::arithop_not(const Def* def, Debug dbg) { return arithop_xor(allset(def->type(), dbg, vector_length(def)), def, dbg); } @@ -483,7 +483,7 @@ const Def* World::cmp(CmpTag tag, const Def* a, const Def* b, Debug dbg) { } } - return cse(new Cmp(tag, a, b, dbg)); + return cse(new Cmp(tag, *this, a, b, dbg)); } /* @@ -616,7 +616,7 @@ const Def* World::cast(const Type* to, const Def* from, Debug dbg) { } } - return cse(new Cast(to, from, dbg)); + return cse(new Cast(*this, to, from, dbg)); } const Def* World::bitcast(const Type* to, const Def* from, Debug dbg) { @@ -655,7 +655,7 @@ const Def* World::bitcast(const Type* to, const Def* from, Debug dbg) { return vector(ops, dbg); } - return cse(new Bitcast(to, from, dbg)); + return cse(new Bitcast(*this, to, from, dbg)); } /* @@ -712,7 +712,7 @@ const Def* World::extract(const Def* agg, const Def* index, Debug dbg) { } } - return cse(new Extract(agg, index, dbg)); + return cse(new Extract(*this, agg, index, dbg)); } const Def* World::insert(const Def* agg, const Def* index, const Def* value, Debug dbg) { @@ -760,14 +760,14 @@ const Def* World::insert(const Def* agg, const Def* index, const Def* value, Deb } } - return cse(new Insert(agg, index, value, dbg)); + return cse(new Insert(*this, agg, index, value, dbg)); } const Def* World::lea(const Def* ptr, const Def* index, Debug dbg) { if (fold_1_tuple(ptr->type()->as()->pointee(), index)) return ptr; - return cse(new LEA(ptr, index, dbg)); + return cse(new LEA(*this, ptr, index, dbg)); } const Def* World::select(const Def* cond, const Def* a, const Def* b, Debug dbg) { @@ -785,21 +785,21 @@ const Def* World::select(const Def* cond, const Def* a, const Def* b, Debug dbg) if (a == b) return a; - return cse(new Select(cond, a, b, dbg)); + return cse(new Select(*this, cond, a, b, dbg)); } const Def* World::align_of(const Type* type, Debug dbg) { if (auto ptype = type->isa()) return literal(qs64(num_bits(ptype->primtype_tag()) / 8), dbg); - return cse(new AlignOf(bottom(type, dbg), dbg)); + return cse(new AlignOf(*this, bottom(type, dbg), dbg)); } const Def* World::size_of(const Type* type, Debug dbg) { if (auto ptype = type->isa()) return literal(qs64(num_bits(ptype->primtype_tag()) / 8), dbg); - return cse(new SizeOf(bottom(type, dbg), dbg)); + return cse(new SizeOf(*this, bottom(type, dbg), dbg)); } /* @@ -824,7 +824,7 @@ const Def* World::transcendental(MathOpTag tag, const Def* arg, Debug dbg, F&& f THORIN_UNREACHABLE; } } - return cse(new MathOp(tag, arg->type(), { arg }, dbg)); + return cse(new MathOp(tag, *this, arg->type(), { arg }, dbg)); } template @@ -846,7 +846,7 @@ const Def* World::transcendental(MathOpTag tag, const Def* left, const Def* righ THORIN_UNREACHABLE; } } - return cse(new MathOp(tag, left->type(), { left, right }, dbg)); + return cse(new MathOp(tag, *this, left->type(), { left, right }, dbg)); } template @@ -1019,7 +1019,7 @@ const Def* World::load(const Def* mem, const Def* ptr, Debug dbg) { return tuple({mem, tuple({}, dbg)}); } } - return cse(new Load(mem, ptr, dbg)); + return cse(new Load(*this, mem, ptr, dbg)); } bool is_agg_const(const Def* def) { @@ -1029,7 +1029,7 @@ bool is_agg_const(const Def* def) { const Def* World::store(const Def* mem, const Def* ptr, const Def* value, Debug dbg) { if (value->isa()) return mem; - return cse(new Store(mem, ptr, value, dbg)); + return cse(new Store(*this, mem, ptr, value, dbg)); } const Def* World::enter(const Def* mem, Debug dbg) { @@ -1037,15 +1037,15 @@ const Def* World::enter(const Def* mem, Debug dbg) { // in order to simplify as we go and prevent code size from exploding if (auto e = Enter::is_out_mem(mem)) return e; - return cse(new Enter(mem, dbg)); + return cse(new Enter(*this, mem, dbg)); } const Def* World::alloc(const Type* type, const Def* mem, const Def* extra, Debug dbg) { - return cse(new Alloc(type, mem, extra, dbg)); + return cse(new Alloc(*this, type, mem, extra, dbg)); } const Def* World::global(const Def* init, bool is_mutable, Debug dbg) { - return cse(new Global(init, is_mutable, dbg)); + return cse(new Global(*this, init, is_mutable, dbg)); } const Def* World::global_immutable_string(const std::string& str, Debug dbg) { @@ -1060,7 +1060,7 @@ const Def* World::global_immutable_string(const std::string& str, Debug dbg) { } const Assembly* World::assembly(const Type* type, Defs inputs, std::string asm_template, ArrayRef output_constraints, ArrayRef input_constraints, ArrayRef clobbers, Assembly::Flags flags, Debug dbg) { - return cse(new Assembly(type, inputs, asm_template, output_constraints, input_constraints, clobbers, flags, dbg))->as();; + return cse(new Assembly(*this, type, inputs, asm_template, output_constraints, input_constraints, clobbers, flags, dbg))->as();; } const Assembly* World::assembly(Types types, const Def* mem, Defs inputs, std::string asm_template, ArrayRef output_constraints, ArrayRef input_constraints, ArrayRef clobbers, Assembly::Flags flags, Debug dbg) { @@ -1081,7 +1081,7 @@ const Assembly* World::assembly(Types types, const Def* mem, Defs inputs, std::s const Def* World::hlt(const Def* def, Debug dbg) { if (is_pe_done()) return def; - return cse(new Hlt(def, dbg)); + return cse(new Hlt(*this, def, dbg)); } const Def* World::known(const Def* def, Debug dbg) { @@ -1089,12 +1089,12 @@ const Def* World::known(const Def* def, Debug dbg) { return literal_bool(false, dbg); if (!def->has_dep(Dep::Param)) return literal_bool(true, dbg); - return cse(new Known(def, dbg)); + return cse(new Known(*this, def, dbg)); } const Def* World::run(const Def* def, Debug dbg) { if (is_pe_done()) return def; - return cse(new Run(def, dbg)); + return cse(new Run(*this, def, dbg)); } /* @@ -1102,7 +1102,7 @@ const Def* World::run(const Def* def, Debug dbg) { */ Continuation* World::continuation(const FnType* fn, Continuation::Attributes attributes, Debug dbg) { - auto cont = put(fn, attributes, dbg); + auto cont = put(*this, fn, attributes, dbg); size_t i = 0; for (auto op : fn->ops()) { @@ -1123,7 +1123,7 @@ Continuation* World::match(const Type* type, size_t num_patterns) { } const Param* World::param(const Type* type, Continuation* continuation, size_t index, Debug dbg) { - auto param = new Param(type, continuation, index, dbg); + auto param = new Param(*this, type, continuation, index, dbg); #if THORIN_ENABLE_CHECKS if (state_.breakpoints.contains(param->gid())) THORIN_BREAK; #endif @@ -1171,7 +1171,7 @@ const App* World::app(const Def* callee, const Defs args, Debug dbg) { for (size_t i = 0; i < args.size(); i++) ops[i + 1] = args[i]; - return cse(new App(ops, dbg)); + return cse(new App(*this, ops, dbg)); } /* diff --git a/src/thorin/world.h b/src/thorin/world.h index 96e30af2b..940680ef7 100644 --- a/src/thorin/world.h +++ b/src/thorin/world.h @@ -110,8 +110,8 @@ class World : public TypeTable, public Streamable { const Def* one(const Type* type, Debug dbg = {}, size_t length = 1) { return one(type->as()->primtype_tag(), dbg, length); } const Def* allset(PrimTypeTag tag, Debug dbg = {}, size_t length = 1); const Def* allset(const Type* type, Debug dbg = {}, size_t length = 1) { return allset(type->as()->primtype_tag(), dbg, length); } - const Def* top(const Type* type, Debug dbg = {}, size_t length = 1) { return splat(cse(new Top(type, dbg)), length); } - const Def* bottom(const Type* type, Debug dbg = {}, size_t length = 1) { return splat(cse(new Bottom(type, dbg)), length); } + const Def* top(const Type* type, Debug dbg = {}, size_t length = 1) { return splat(cse(new Top(*this, type, dbg)), length); } + const Def* bottom(const Type* type, Debug dbg = {}, size_t length = 1) { return splat(cse(new Bottom(*this, type, dbg)), length); } const Def* bottom(PrimTypeTag tag, Debug dbg = {}, size_t length = 1) { return bottom(prim_type(tag), dbg, length); } // arithops @@ -156,15 +156,15 @@ class World : public TypeTable, public Streamable { return cse(new IndefiniteArray(*this, elem, dim, dbg)); } const Def* struct_agg(const StructType* struct_type, Defs args, Debug dbg = {}) { - return try_fold_aggregate(cse(new StructAgg(struct_type, args, dbg))); + return try_fold_aggregate(cse(new StructAgg(*this, struct_type, args, dbg))); } const Def* tuple(Defs args, Debug dbg = {}) { return args.size() == 1 ? args.front() : try_fold_aggregate(cse(new Tuple(*this, args, dbg))); } - const Def* variant(const VariantType* variant_type, const Def* value, size_t index, Debug dbg = {}) { return cse(new Variant(variant_type, value, index, dbg)); } + const Def* variant(const VariantType* variant_type, const Def* value, size_t index, Debug dbg = {}) { return cse(new Variant(*this, variant_type, value, index, dbg)); } const Def* variant_index (const Def* value, Debug dbg = {}); const Def* variant_extract(const Def* value, size_t index, Debug dbg = {}); - const Def* closure(const ClosureType* closure_type, const Def* fn, const Def* env, Debug dbg = {}) { return cse(new Closure(closure_type, fn, env, dbg)); } + const Def* closure(const ClosureType* closure_type, const Def* fn, const Def* env, Debug dbg = {}) { return cse(new Closure(*this, closure_type, fn, env, dbg)); } const Def* vector(Defs args, Debug dbg = {}) { if (args.size() == 1) return args[0]; return try_fold_aggregate(cse(new Vector(*this, args, dbg))); @@ -216,7 +216,7 @@ class World : public TypeTable, public Streamable { const Def* load(const Def* mem, const Def* ptr, Debug dbg = {}); const Def* store(const Def* mem, const Def* ptr, const Def* val, Debug dbg = {}); const Def* enter(const Def* mem, Debug dbg = {}); - const Def* slot(const Type* type, const Def* frame, Debug dbg = {}) { return cse(new Slot(type, frame, dbg)); } + 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* global(const Def* init, bool is_mutable = true, Debug dbg = {}); From 654d93577e7820ec0aa822d41aa5de24acd48e79 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Sun, 22 Jan 2023 15:28:55 +0100 Subject: [PATCH 157/342] rebuild() uses new worlds instead of recycling the same one --- src/thorin/be/c/c.cpp | 26 ++++++++--------- src/thorin/be/c/c.h | 6 ++-- src/thorin/be/codegen.cpp | 37 +++++++++++++------------ src/thorin/be/codegen.h | 10 ++++--- src/thorin/be/llvm/amdgpu.cpp | 4 +-- src/thorin/be/llvm/amdgpu.h | 2 +- src/thorin/be/llvm/cpu.cpp | 4 +-- src/thorin/be/llvm/cpu.h | 2 +- src/thorin/be/llvm/llvm.cpp | 6 ++-- src/thorin/be/llvm/llvm.h | 2 +- src/thorin/be/llvm/nvvm.cpp | 4 +-- src/thorin/be/llvm/nvvm.h | 2 +- src/thorin/def.cpp | 2 ++ src/thorin/transform/cleanup_world.cpp | 33 +++++++++++----------- src/thorin/transform/cleanup_world.h | 2 +- src/thorin/transform/flatten_tuples.cpp | 16 +++++------ src/thorin/transform/flatten_tuples.h | 2 +- src/thorin/transform/hls_channels.cpp | 8 +++--- src/thorin/transform/hls_channels.h | 2 +- src/thorin/transform/hoist_enters.cpp | 6 ++-- src/thorin/transform/hoist_enters.h | 2 +- src/thorin/transform/importer.cpp | 12 ++++---- src/thorin/transform/importer.h | 14 ++++++---- src/thorin/transform/inliner.cpp | 5 ++-- src/thorin/transform/inliner.h | 2 +- src/thorin/transform/lift_builtins.cpp | 5 ++-- src/thorin/transform/lift_builtins.h | 2 +- src/thorin/transform/split_slots.cpp | 6 ++-- src/thorin/transform/split_slots.h | 2 +- src/thorin/world.cpp | 22 +++++++++------ src/thorin/world.h | 27 ++++++++++-------- 31 files changed, 147 insertions(+), 128 deletions(-) diff --git a/src/thorin/be/c/c.cpp b/src/thorin/be/c/c.cpp index 3f77582fd..33ac9405f 100644 --- a/src/thorin/be/c/c.cpp +++ b/src/thorin/be/c/c.cpp @@ -73,17 +73,17 @@ enum class HlsInterface : uint8_t { class CCodeGen : public thorin::Emitter { public: - CCodeGen(World& world, const Cont2Config& kernel_config, Stream& stream, Lang lang, bool debug, std::string& flags) - : world_(world) + CCodeGen(Thorin& thorin, const Cont2Config& kernel_config, Stream& stream, Lang lang, bool debug, std::string& flags) + : thorin_(thorin) , kernel_config_(kernel_config) , lang_(lang) - , fn_mem_(world.fn_type({world.mem_type()})) + , fn_mem_(world().fn_type({world().mem_type()})) , debug_(debug) , flags_(flags) , stream_(stream) {} - World& world() const { return world_; } + World& world() const { return thorin_.world(); } void emit_module(); void emit_c_int(); void emit_epilogue(Continuation*); @@ -115,7 +115,7 @@ class CCodeGen : public thorin::Emitter std::string array_name(const DefiniteArrayType*); std::string tuple_name(const TupleType*); - World& world_; + Thorin& thorin_; const Cont2Config& kernel_config_; Lang lang_; const FnType* fn_mem_; @@ -257,10 +257,10 @@ std::string CCodeGen::convert(const Type* type) { } else if (auto variant = type->isa()) { types_[variant] = name = variant->name().str(); auto tag_type = - variant->num_ops() < (UINT64_C(1) << 8u) ? world_.type_qu8() : - variant->num_ops() < (UINT64_C(1) << 16u) ? world_.type_qu16() : - variant->num_ops() < (UINT64_C(1) << 32u) ? world_.type_qu32() : - world_.type_qu64(); + variant->num_ops() < (UINT64_C(1) << 8u) ? world().type_qu8() : + variant->num_ops() < (UINT64_C(1) << 16u) ? world().type_qu16() : + variant->num_ops() < (UINT64_C(1) << 32u) ? world().type_qu32() : + world().type_qu64(); s.fmt("typedef struct {{\t\n"); // This is required because we have zero-sized types but C/C++ do not @@ -1453,7 +1453,7 @@ Stream& CCodeGen::emit_debug_info(Stream& s, const Def* def) { void CCodeGen::emit_c_int() { // Do not emit C interfaces for definitions that are not used - world().cleanup(); + thorin_.cleanup(); for (auto def : world().defs()) { auto cont = def->isa_nom(); @@ -1586,12 +1586,12 @@ std::string CCodeGen::tuple_name(const TupleType* tuple_type) { void CodeGen::emit_stream(std::ostream& stream) { Stream s(stream); - CCodeGen(world(), kernel_config_, s, lang_, debug_, flags_).emit_module(); + CCodeGen(thorin(), kernel_config_, s, lang_, debug_, flags_).emit_module(); } -void emit_c_int(World& world, Stream& stream) { +void emit_c_int(Thorin& thorin, Stream& stream) { std::string flags; - CCodeGen(world, {}, stream, Lang::C99, false, flags).emit_c_int(); + CCodeGen(thorin, {}, stream, Lang::C99, false, flags).emit_c_int(); } //------------------------------------------------------------------------------ diff --git a/src/thorin/be/c/c.h b/src/thorin/be/c/c.h index 6ebe5595f..f33781ec2 100644 --- a/src/thorin/be/c/c.h +++ b/src/thorin/be/c/c.h @@ -16,8 +16,8 @@ enum class Lang : uint8_t { C99, HLS, CUDA, OpenCL }; class CodeGen : public thorin::CodeGen { public: - CodeGen(World& world, const Cont2Config& kernel_config, Lang lang, bool debug, std::string& flags) - : thorin::CodeGen(world, debug) + CodeGen(Thorin& thorin, const Cont2Config& kernel_config, Lang lang, bool debug, std::string& flags) + : thorin::CodeGen(thorin, debug) , kernel_config_(kernel_config) , lang_(lang) , debug_(debug) @@ -43,7 +43,7 @@ class CodeGen : public thorin::CodeGen { std::string flags_; }; -void emit_c_int(World&, Stream& stream); +void emit_c_int(Thorin&, Stream& stream); } diff --git a/src/thorin/be/codegen.cpp b/src/thorin/be/codegen.cpp index db6f6d802..afd8afd58 100644 --- a/src/thorin/be/codegen.cpp +++ b/src/thorin/be/codegen.cpp @@ -13,14 +13,14 @@ namespace thorin { static void get_kernel_configs( - Importer& importer, + Thorin& thorin, const std::vector& kernels, Cont2Config& kernel_configs, std::function (Continuation*, Continuation*)> use_callback) { - importer.world().opt(); + thorin.opt(); - auto externals = importer.world().externals(); + auto externals = thorin.world().externals(); for (auto continuation : kernels) { // recover the imported continuation (lost after the call to opt) Continuation* imported = nullptr; @@ -78,8 +78,11 @@ static uint64_t get_alloc_size(const Def* def) { DeviceBackends::DeviceBackends(World& world, int opt, bool debug, std::string& flags) : cgs {} { - for (size_t i = 0; i < cgs.size(); ++i) - importers_.emplace_back(world); + std::vector importers; + for (auto& name : backend_names) { + accelerator_code.emplace_back(name); + importers.emplace_back(world, accelerator_code.back().world()); + } // determine different parts of the world which need to be compiled differently Scope::for_each(world, [&] (const Scope& scope) { @@ -95,7 +98,7 @@ DeviceBackends::DeviceBackends(World& world, int opt, bool debug, std::string& f }; for (auto [backend, intrinsic] : backend_intrinsics) { if (is_passed_to_intrinsic(continuation, intrinsic)) { - imported = importers_[backend].import(continuation)->as_nom(); + imported = importers[backend].import(continuation)->as_nom(); break; } } @@ -113,8 +116,8 @@ DeviceBackends::DeviceBackends(World& world, int opt, bool debug, std::string& f }); for (auto backend : std::array { CUDA, NVVM, OpenCL, AMDGPU }) { - if (!importers_[backend].world().empty()) { - get_kernel_configs(importers_[backend], kernels, kernel_config, [&](Continuation *use, Continuation * /* imported */) { + if (!accelerator_code[backend].world().empty()) { + get_kernel_configs(accelerator_code[backend], kernels, kernel_config, [&](Continuation *use, Continuation * /* imported */) { auto app = use->body(); // determine whether or not this kernel uses restrict pointers bool has_restrict = true; @@ -146,10 +149,10 @@ DeviceBackends::DeviceBackends(World& world, int opt, bool debug, std::string& f // get the HLS kernel configurations Top2Kernel top2kernel; DeviceParams hls_host_params; - if (!importers_[HLS].world().empty()) { - hls_host_params = hls_channels(importers_[HLS], top2kernel, world); + if (!accelerator_code[HLS].world().empty()) { + hls_host_params = hls_channels(accelerator_code[HLS], importers[HLS], top2kernel, world); - get_kernel_configs(importers_[HLS], kernels, kernel_config, [&] (Continuation* use, Continuation* imported) { + get_kernel_configs(accelerator_code[HLS], kernels, kernel_config, [&] (Continuation* use, Continuation* imported) { auto app = use->body(); HLSKernelConfig::Param2Size param_sizes; for (size_t i = hls_free_vars_offset, e = app->num_args(); i != e; ++i) { @@ -180,22 +183,22 @@ DeviceBackends::DeviceBackends(World& world, int opt, bool debug, std::string& f } return std::make_unique(param_sizes); }); - hls_annotate_top(importers_[HLS].world(), top2kernel, kernel_config); + hls_annotate_top(importers[HLS].world(), top2kernel, kernel_config); } hls_kernel_launch(world, hls_host_params); #if THORIN_ENABLE_LLVM - if (!importers_[NVVM ].world().empty()) cgs[NVVM ] = std::make_unique(importers_[NVVM ].world(), kernel_config, debug); - if (!importers_[AMDGPU].world().empty()) cgs[AMDGPU] = std::make_unique(importers_[AMDGPU].world(), kernel_config, opt, debug); + if (!accelerator_code[NVVM ].world().empty()) cgs[NVVM ] = std::make_unique(accelerator_code[NVVM ], kernel_config, debug); + if (!accelerator_code[AMDGPU].world().empty()) cgs[AMDGPU] = std::make_unique(accelerator_code[AMDGPU], kernel_config, opt, debug); #else (void)opt; #endif for (auto [backend, lang] : std::array { std::pair { CUDA, c::Lang::CUDA }, std::pair { OpenCL, c::Lang::OpenCL }, std::pair { HLS, c::Lang::HLS } }) - if (!importers_[backend].world().empty()) cgs[backend] = std::make_unique(importers_[backend].world(), kernel_config, lang, debug, flags); + if (!accelerator_code[backend].world().empty()) cgs[backend] = std::make_unique(accelerator_code[backend], kernel_config, lang, debug, flags); } -CodeGen::CodeGen(World& world, bool debug) - : world_(world) +CodeGen::CodeGen(Thorin& thorin, bool debug) + : thorin_(thorin) , debug_(debug) {} diff --git a/src/thorin/be/codegen.h b/src/thorin/be/codegen.h index cd7c5689f..e124f875d 100644 --- a/src/thorin/be/codegen.h +++ b/src/thorin/be/codegen.h @@ -8,7 +8,7 @@ namespace thorin { class CodeGen { protected: - CodeGen(World& world, bool debug); + CodeGen(Thorin& thorin, bool debug); public: virtual ~CodeGen() {} @@ -17,12 +17,13 @@ class CodeGen { /// @name getters //@{ - World& world() const { return world_; } + Thorin& thorin() const { return thorin_; } + World& world() const { return thorin().world(); } bool debug() const { return debug_; } //@} private: - World& world_; + Thorin& thorin_; bool debug_; }; @@ -47,7 +48,8 @@ struct DeviceBackends { enum { CUDA, NVVM, OpenCL, AMDGPU, HLS, BackendCount }; std::array, BackendCount> cgs; private: - std::vector importers_; + std::array backend_names = { "CUDA", "NVVM", "OpenCL", "AMDGPU", "HLS" }; + std::vector accelerator_code; }; } diff --git a/src/thorin/be/llvm/amdgpu.cpp b/src/thorin/be/llvm/amdgpu.cpp index 1d19ff7a9..1f515a1bb 100644 --- a/src/thorin/be/llvm/amdgpu.cpp +++ b/src/thorin/be/llvm/amdgpu.cpp @@ -7,8 +7,8 @@ namespace thorin::llvm { -AMDGPUCodeGen::AMDGPUCodeGen(World& world, const Cont2Config& kernel_config, int opt, bool debug) - : CodeGen(world, llvm::CallingConv::C, llvm::CallingConv::C, llvm::CallingConv::AMDGPU_KERNEL, opt, debug) +AMDGPUCodeGen::AMDGPUCodeGen(Thorin& thorin, const Cont2Config& kernel_config, int opt, bool debug) + : CodeGen(thorin, llvm::CallingConv::C, llvm::CallingConv::C, llvm::CallingConv::AMDGPU_KERNEL, opt, debug) , kernel_config_(kernel_config) { module().setDataLayout("e-p:64:64-p1:64:64-p2:32:32-p3:32:32-p4:64:64-p5:32:32-p6:32:32-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024-v2048:2048-n32:64-S32-A5-ni:7"); diff --git a/src/thorin/be/llvm/amdgpu.h b/src/thorin/be/llvm/amdgpu.h index 640fc5b69..fd5bd5779 100644 --- a/src/thorin/be/llvm/amdgpu.h +++ b/src/thorin/be/llvm/amdgpu.h @@ -13,7 +13,7 @@ namespace llvm = ::llvm; class AMDGPUCodeGen : public CodeGen { public: - AMDGPUCodeGen(World& world, const Cont2Config&, int opt, bool debug); + AMDGPUCodeGen(Thorin&, const Cont2Config&, int opt, bool debug); const char* file_ext() const override { return ".amdgpu"; } diff --git a/src/thorin/be/llvm/cpu.cpp b/src/thorin/be/llvm/cpu.cpp index 237a42a99..b7f5dae3b 100644 --- a/src/thorin/be/llvm/cpu.cpp +++ b/src/thorin/be/llvm/cpu.cpp @@ -8,8 +8,8 @@ namespace thorin::llvm { -CPUCodeGen::CPUCodeGen(World& world, int opt, bool debug, std::string& target_triple, std::string& target_cpu, std::string& target_attr) - : CodeGen(world, llvm::CallingConv::C, llvm::CallingConv::C, llvm::CallingConv::C, opt, debug) +CPUCodeGen::CPUCodeGen(Thorin& thorin, int opt, bool debug, std::string& target_triple, std::string& target_cpu, std::string& target_attr) + : CodeGen(thorin, llvm::CallingConv::C, llvm::CallingConv::C, llvm::CallingConv::C, opt, debug) { llvm::InitializeNativeTarget(); auto triple_str = llvm::sys::getDefaultTargetTriple(); diff --git a/src/thorin/be/llvm/cpu.h b/src/thorin/be/llvm/cpu.h index 7724ccc91..78db51cd9 100644 --- a/src/thorin/be/llvm/cpu.h +++ b/src/thorin/be/llvm/cpu.h @@ -9,7 +9,7 @@ namespace llvm = ::llvm; class CPUCodeGen : public CodeGen { public: - CPUCodeGen(World& world, int opt, bool debug, std::string& target_triple, std::string& target_cpu, std::string& target_attr); + CPUCodeGen(Thorin&, int opt, bool debug, std::string& target_triple, std::string& target_cpu, std::string& target_attr); protected: std::string get_alloc_name() const override { return "anydsl_alloc"; } diff --git a/src/thorin/be/llvm/llvm.cpp b/src/thorin/be/llvm/llvm.cpp index ce9fb159e..9b04de2a1 100644 --- a/src/thorin/be/llvm/llvm.cpp +++ b/src/thorin/be/llvm/llvm.cpp @@ -42,14 +42,14 @@ namespace thorin::llvm { CodeGen::CodeGen( - World& world, + Thorin& thorin, llvm::CallingConv::ID function_calling_convention, llvm::CallingConv::ID device_calling_convention, llvm::CallingConv::ID kernel_calling_convention, int opt, bool debug) - : thorin::CodeGen(world, debug) + : thorin::CodeGen(thorin, debug) , context_(std::make_unique()) - , module_(std::make_unique(world.name(), context())) + , module_(std::make_unique(world().name(), context())) , opt_(opt) , dibuilder_(module()) , function_calling_convention_(function_calling_convention) diff --git a/src/thorin/be/llvm/llvm.h b/src/thorin/be/llvm/llvm.h index 93e9aeb7e..7dc6f6b90 100644 --- a/src/thorin/be/llvm/llvm.h +++ b/src/thorin/be/llvm/llvm.h @@ -28,7 +28,7 @@ using BB = std::pair>>; class CodeGen : public thorin::CodeGen, public thorin::Emitter { protected: CodeGen( - World& world, + Thorin&, llvm::CallingConv::ID function_calling_convention, llvm::CallingConv::ID device_calling_convention, llvm::CallingConv::ID kernel_calling_convention, diff --git a/src/thorin/be/llvm/nvvm.cpp b/src/thorin/be/llvm/nvvm.cpp index 8a80e9a07..180f3ac2e 100644 --- a/src/thorin/be/llvm/nvvm.cpp +++ b/src/thorin/be/llvm/nvvm.cpp @@ -19,8 +19,8 @@ namespace thorin::llvm { -NVVMCodeGen::NVVMCodeGen(World& world, const Cont2Config& kernel_config, bool debug) - : CodeGen(world, llvm::CallingConv::C, llvm::CallingConv::PTX_Device, llvm::CallingConv::PTX_Kernel, 0, debug) +NVVMCodeGen::NVVMCodeGen(Thorin& thorin, const Cont2Config& kernel_config, bool debug) + : CodeGen(thorin, llvm::CallingConv::C, llvm::CallingConv::PTX_Device, llvm::CallingConv::PTX_Kernel, 0, debug) , kernel_config_(kernel_config) { auto triple = llvm::Triple(llvm::sys::getDefaultTargetTriple()); diff --git a/src/thorin/be/llvm/nvvm.h b/src/thorin/be/llvm/nvvm.h index ee6f5239f..204ce6648 100644 --- a/src/thorin/be/llvm/nvvm.h +++ b/src/thorin/be/llvm/nvvm.h @@ -13,7 +13,7 @@ namespace llvm = ::llvm; class NVVMCodeGen : public CodeGen { public: - NVVMCodeGen(World& world, const Cont2Config&, bool debug); // NVVM-specific optimizations are run in the runtime + NVVMCodeGen(Thorin&, const Cont2Config&, bool debug); // NVVM-specific optimizations are run in the runtime const char* file_ext() const override { return ".nvvm"; } diff --git a/src/thorin/def.cpp b/src/thorin/def.cpp index 03516f1d9..a0d14e59c 100644 --- a/src/thorin/def.cpp +++ b/src/thorin/def.cpp @@ -56,6 +56,7 @@ void Def::set_name(const std::string& name) const { debug_.name = name; } void Def::set_op(size_t i, const Def* def) { assert(!op(i) && "already set"); assert(def && "setting null pointer"); + assert(&def->world() == &world()); ops_[i] = def; // A Param/Continuation should not have other bits than its own set. // (Right now, Param doesn't have ops, but this will change in the future). @@ -136,6 +137,7 @@ bool is_minus_zero(const Def* def) { void Def::replace_uses(const Def* with) const { world().DLOG("replace uses: {} -> {}", this, with); if (this != with) { + assert(&with->world() == &this->world()); for (auto& use : copy_uses()) { auto def = const_cast(use.def()); auto index = use.index(); diff --git a/src/thorin/transform/cleanup_world.cpp b/src/thorin/transform/cleanup_world.cpp index 2748d25d9..041db668a 100644 --- a/src/thorin/transform/cleanup_world.cpp +++ b/src/thorin/transform/cleanup_world.cpp @@ -14,11 +14,11 @@ namespace thorin { class Cleaner { public: - Cleaner(World& world) + Cleaner(std::unique_ptr& world) : world_(world) {} - World& world() { return world_; } + World& world() { return *world_; } void cleanup(); void eliminate_tail_rec(); void eta_conversion(); @@ -31,12 +31,12 @@ class Cleaner { private: void cleanup_fix_point(); void clean_pe_info(std::queue, Continuation*); - World& world_; + std::unique_ptr& world_; bool todo_ = true; }; void Cleaner::eliminate_tail_rec() { - Scope::for_each(world_, [&](Scope& scope) { + Scope::for_each(*world_, [&](Scope& scope) { auto entry = scope.entry(); bool only_tail_calls = true; @@ -232,16 +232,17 @@ next_continuation:; } void Cleaner::rebuild() { - Importer importer(world_); - importer.type_old2new_.rehash(world_.types().capacity()); - importer.def_old2new_.rehash(world_.defs().capacity()); + auto fresh_world = std::make_unique(world()); + Importer importer(*world_, *fresh_world); + importer.type_old2new_.rehash(world_->types().capacity()); + importer.def_old2new_.rehash(world_->defs().capacity()); for (auto&& [_, cont] : world().externals()) { if (cont->is_exported()) importer.import(cont); } - swap(importer.world(), world_); + std::swap(world_, fresh_world); // verify(world()); @@ -279,7 +280,7 @@ void Cleaner::clean_pe_info(std::queue queue, Continuation* cur) auto next = body->arg(3); auto msg = body->arg(1)->as()->from()->as()->init()->as(); - world_.idef(body->callee(), "pe_info was not constant: {}: {}", msg->as_string(), body->arg(2)); + world_->idef(body->callee(), "pe_info was not constant: {}: {}", msg->as_string(), body->arg(2)); cur->jump(next, {body->arg(0)}, cur->debug()); // TODO debug todo_ = true; @@ -288,7 +289,7 @@ void Cleaner::clean_pe_info(std::queue queue, Continuation* cur) } void Cleaner::clean_pe_infos() { - world_.VLOG("cleaning remaining pe_infos"); + world_->VLOG("cleaning remaining pe_infos"); std::queue queue; ContinuationSet done; auto enqueue = [&](Continuation* continuation) { @@ -319,9 +320,9 @@ void Cleaner::clean_pe_infos() { void Cleaner::cleanup_fix_point() { int i = 0; for (; todo_; ++i) { - world_.VLOG("iteration: {}", i); + world_->VLOG("iteration: {}", i); todo_ = false; - if (world_.is_pe_done()) + if (world_->is_pe_done()) eliminate_tail_rec(); eta_conversion(); eliminate_params(); @@ -329,14 +330,14 @@ void Cleaner::cleanup_fix_point() { todo_ |= resolve_loads(world()); rebuild(); if (!world().is_pe_done()) - todo_ |= partial_evaluation(world_); + todo_ |= partial_evaluation(*world_); else clean_pe_infos(); } } void Cleaner::cleanup() { - world_.VLOG("start cleanup"); + world_->VLOG("start cleanup"); cleanup_fix_point(); if (!world().is_pe_done()) { @@ -350,13 +351,13 @@ void Cleaner::cleanup() { cleanup_fix_point(); } - world_.VLOG("end cleanup"); + world_->VLOG("end cleanup"); #if THORIN_ENABLE_CHECKS verify_closedness(); debug_verify(world()); #endif } -void cleanup_world(World& world) { Cleaner(world).cleanup(); } +void cleanup_world(std::unique_ptr& world) { Cleaner(world).cleanup(); } } diff --git a/src/thorin/transform/cleanup_world.h b/src/thorin/transform/cleanup_world.h index 22c91b0b6..afcb68b75 100644 --- a/src/thorin/transform/cleanup_world.h +++ b/src/thorin/transform/cleanup_world.h @@ -5,7 +5,7 @@ namespace thorin { class World; -void cleanup_world(World& world); +void cleanup_world(std::unique_ptr& world); } diff --git a/src/thorin/transform/flatten_tuples.cpp b/src/thorin/transform/flatten_tuples.cpp index 0b4529c25..8f99bd220 100644 --- a/src/thorin/transform/flatten_tuples.cpp +++ b/src/thorin/transform/flatten_tuples.cpp @@ -166,7 +166,7 @@ static Continuation* unwrap_def(Def2Def& wrapped, Def2Def& unwrapped, const Def* return jump(old_cont, call_args); } -static void flatten_tuples(World& world, size_t max_tuple_size) { +static void flatten_tuples(Thorin& thorin, size_t max_tuple_size) { // flatten tuples passed as arguments to functions bool todo = true; Def2Def wrapped, unwrapped; @@ -177,11 +177,11 @@ static void flatten_tuples(World& world, size_t max_tuple_size) { for (auto pair : unwrapped) unwrapped_codom.emplace(pair.second); - for (auto cont : world.copy_continuations()) { + for (auto cont : thorin.world().copy_continuations()) { // do not change the signature of intrinsic/external functions if (!cont->has_body() || cont->is_intrinsic() || - world.is_external(cont) || + thorin.world().is_external(cont) || is_passed_to_accelerator(cont)) continue; @@ -196,7 +196,7 @@ static void flatten_tuples(World& world, size_t max_tuple_size) { todo = true; - world.DLOG("flattened {}", cont); + thorin.world().DLOG("flattened {}", cont); } // remove original versions of wrapped functions @@ -218,12 +218,12 @@ static void flatten_tuples(World& world, size_t max_tuple_size) { for (auto unwrap_pair : unwrapped) inline_calls(unwrap_pair.second->as_nom()); - world.cleanup(); - debug_verify(world); + thorin.cleanup(); + debug_verify(thorin.world()); } -void flatten_tuples(World& world) { - flatten_tuples(world, std::numeric_limits::max()); +void flatten_tuples(Thorin& thorin) { + flatten_tuples(thorin, std::numeric_limits::max()); } } diff --git a/src/thorin/transform/flatten_tuples.h b/src/thorin/transform/flatten_tuples.h index 74b16c792..0e2cbb91d 100644 --- a/src/thorin/transform/flatten_tuples.h +++ b/src/thorin/transform/flatten_tuples.h @@ -2,6 +2,6 @@ namespace thorin { -void flatten_tuples(World& world); +void flatten_tuples(Thorin& thorin); } diff --git a/src/thorin/transform/hls_channels.cpp b/src/thorin/transform/hls_channels.cpp index 954375b10..c778baf8f 100644 --- a/src/thorin/transform/hls_channels.cpp +++ b/src/thorin/transform/hls_channels.cpp @@ -145,14 +145,14 @@ bool dependency_resolver(Dependencies& dependencies, const size_t dependent_kern } /** - * @param importer hls world + * @param thorin hls world * @param Top2Kernel annonating hls_top configuration * @param old_world to connect with runtime (host) world * @return corresponding hls_top parameter for hls_launch_kernel in another world (params before rewriting kernels) */ -DeviceParams hls_channels(Importer& importer, Top2Kernel& top2kernel, World& old_world) { - auto& world = importer.world(); +DeviceParams hls_channels(Thorin& thorin, Importer& importer, Top2Kernel& top2kernel, World& old_world) { + auto& world = thorin.world(); std::vector kernels_ch_modes; // vector of channel->mode maps for kernels which use channel(s) std::vector new_kernels; Def2Def kernel_new2old; @@ -403,7 +403,7 @@ DeviceParams hls_channels(Importer& importer, Top2Kernel& top2kernel, World& old world.make_external(hls_top); debug_verify(world); - world.cleanup(); + thorin.cleanup(); return old_kernels_params; } diff --git a/src/thorin/transform/hls_channels.h b/src/thorin/transform/hls_channels.h index 21a50f873..b2296fa44 100644 --- a/src/thorin/transform/hls_channels.h +++ b/src/thorin/transform/hls_channels.h @@ -23,7 +23,7 @@ class World; * resolves all dependency requirements between kernel calls * provides hls_top parameters for hls runtime */ -DeviceParams hls_channels(Importer&, Top2Kernel&, World&); +DeviceParams hls_channels(Thorin&, Importer&, Top2Kernel&, World&); void hls_annotate_top(World&, const Top2Kernel&, Cont2Config&); } diff --git a/src/thorin/transform/hoist_enters.cpp b/src/thorin/transform/hoist_enters.cpp index c052498e2..a51cf8d64 100644 --- a/src/thorin/transform/hoist_enters.cpp +++ b/src/thorin/transform/hoist_enters.cpp @@ -57,9 +57,9 @@ static void hoist_enters(const Scope& scope) { entry_enter->out_mem()->replace_uses(entry_enter->mem()); } -void hoist_enters(World& world) { - Scope::for_each(world, [] (const Scope& scope) { hoist_enters(scope); }); - world.cleanup(); +void hoist_enters(Thorin& thorin) { + Scope::for_each(thorin.world(), [] (const Scope& scope) { hoist_enters(scope); }); + thorin.cleanup(); } } diff --git a/src/thorin/transform/hoist_enters.h b/src/thorin/transform/hoist_enters.h index bff4d9492..5d3155f76 100644 --- a/src/thorin/transform/hoist_enters.h +++ b/src/thorin/transform/hoist_enters.h @@ -5,7 +5,7 @@ namespace thorin { class World; -void hoist_enters(World&); +void hoist_enters(Thorin&); } diff --git a/src/thorin/transform/importer.cpp b/src/thorin/transform/importer.cpp index f125585de..ceaabb6f2 100644 --- a/src/thorin/transform/importer.cpp +++ b/src/thorin/transform/importer.cpp @@ -4,13 +4,13 @@ namespace thorin { const Type* Importer::import(const Type* otype) { if (auto ntype = type_old2new_.lookup(otype)) { - assert(&(*ntype)->table() == &world_); + assert(&(*ntype)->table() == &world()); return *ntype; } size_t size = otype->num_ops(); if (auto nominal_type = otype->isa()) { - auto ntype = nominal_type->stub(world_); + auto ntype = nominal_type->stub(world()); type_old2new_[otype] = ntype; for (size_t i = 0; i != size; ++i) ntype->set(i, import(otype->op(i))); @@ -21,16 +21,16 @@ const Type* Importer::import(const Type* otype) { for (size_t i = 0; i != size; ++i) nops[i] = import(otype->op(i)); - auto ntype = otype->rebuild(world_, nops); + auto ntype = otype->rebuild(world(), nops); type_old2new_[otype] = ntype; - assert(&ntype->table() == &world_); + assert(&ntype->table() == &world()); return ntype; } const Def* Importer::import(const Def* odef) { if (auto ndef = def_old2new_.lookup(odef)) { - assert(&(*ndef)->world() == &world_); + assert(&(*ndef)->world() == &world()); return *ndef; } @@ -39,7 +39,7 @@ const Def* Importer::import(const Def* odef) { if (auto oparam = odef->isa()) { import(oparam->continuation())->as_nom(); auto nparam = def_old2new_[oparam]; - assert(nparam && &nparam->world() == &world_); + assert(nparam && &nparam->world() == &world()); return nparam; } diff --git a/src/thorin/transform/importer.h b/src/thorin/transform/importer.h index c34db9ef3..db41b3161 100644 --- a/src/thorin/transform/importer.h +++ b/src/thorin/transform/importer.h @@ -8,18 +8,19 @@ namespace thorin { class Importer { public: - Importer(World& src) - : world_(src) + explicit Importer(World& src, World& dst) + : src(src) + , dst(dst) { if (src.is_pe_done()) - world_.mark_pe_done(); + world().mark_pe_done(); #if THORIN_ENABLE_CHECKS if (src.track_history()) - world_.enable_history(true); + world().enable_history(true); #endif } - World& world() { return world_; } + World& world() { return dst; } const Type* import(const Type*); const Def* import(const Def*); bool todo() const { return todo_; } @@ -27,7 +28,8 @@ class Importer { public: Type2Type type_old2new_; Def2Def def_old2new_; - World world_; + World& src; + World& dst; bool todo_ = false; }; diff --git a/src/thorin/transform/inliner.cpp b/src/thorin/transform/inliner.cpp index 175f2a518..2205ad4cc 100644 --- a/src/thorin/transform/inliner.cpp +++ b/src/thorin/transform/inliner.cpp @@ -36,7 +36,8 @@ void force_inline(Scope& scope, int threshold) { } } -void inliner(World& world) { +void inliner(Thorin& thorin) { + World& world = thorin.world(); world.VLOG("start inliner"); static const int factor = 4; @@ -95,7 +96,7 @@ void inliner(World& world) { world.VLOG("stop inliner"); debug_verify(world); - world.cleanup(); + thorin.cleanup(); } diff --git a/src/thorin/transform/inliner.h b/src/thorin/transform/inliner.h index 89b1dcfd4..dc22de086 100644 --- a/src/thorin/transform/inliner.h +++ b/src/thorin/transform/inliner.h @@ -11,7 +11,7 @@ class World; * If there still remain functions to be inlined, warnings will be emitted */ void force_inline(Scope& scope, int threshold); -void inliner(World& world); +void inliner(Thorin&); } diff --git a/src/thorin/transform/lift_builtins.cpp b/src/thorin/transform/lift_builtins.cpp index 82dd10d43..cfc65a1de 100644 --- a/src/thorin/transform/lift_builtins.cpp +++ b/src/thorin/transform/lift_builtins.cpp @@ -63,8 +63,9 @@ void lift_pipeline(World& world) { } -void lift_builtins(World& world) { +void lift_builtins(Thorin& thorin) { // This must be run first + World& world = thorin.world(); lift_pipeline(world); while (true) { @@ -125,7 +126,7 @@ void lift_builtins(World& world) { } } - world.cleanup(); + thorin.cleanup(); } } diff --git a/src/thorin/transform/lift_builtins.h b/src/thorin/transform/lift_builtins.h index 7da4d5e05..e9c413e0b 100644 --- a/src/thorin/transform/lift_builtins.h +++ b/src/thorin/transform/lift_builtins.h @@ -5,7 +5,7 @@ namespace thorin { class World; -void lift_builtins(World&); +void lift_builtins(Thorin&); } diff --git a/src/thorin/transform/split_slots.cpp b/src/thorin/transform/split_slots.cpp index 5289777c9..1f81e763d 100644 --- a/src/thorin/transform/split_slots.cpp +++ b/src/thorin/transform/split_slots.cpp @@ -86,12 +86,12 @@ static bool split_slots(const Scope& scope) { return todo; } -void split_slots(World& world) { +void split_slots(Thorin& thorin) { bool todo = true; while (todo) { todo = false; - Scope::for_each(world, [&] (const Scope& scope) { todo |= split_slots(scope); }); - world.cleanup(); + Scope::for_each(thorin.world(), [&] (const Scope& scope) { todo |= split_slots(scope); }); + thorin.cleanup(); } } diff --git a/src/thorin/transform/split_slots.h b/src/thorin/transform/split_slots.h index dff363e43..767ac1ae8 100644 --- a/src/thorin/transform/split_slots.h +++ b/src/thorin/transform/split_slots.h @@ -8,7 +8,7 @@ class World; /** * Tries to split @p Slot%s that are accessed through constant @p LEA%s. */ -void split_slots(World&); +void split_slots(Thorin&); } diff --git a/src/thorin/world.cpp b/src/thorin/world.cpp index 013d09544..a45ac14a4 100644 --- a/src/thorin/world.cpp +++ b/src/thorin/world.cpp @@ -1276,28 +1276,32 @@ const Def* World::cse_base(const Def* def) { * optimizations */ -void World::cleanup() { cleanup_world(*this); } +Thorin::Thorin(const std::string& name) + : world_(std::make_unique(name)) +{} -void World::opt() { +void Thorin::cleanup() { cleanup_world(world_); } + +void Thorin::opt() { #define RUN_PASS(pass) \ { \ - VLOG("running pass {}", #pass); \ + world().VLOG("running pass {}", #pass); \ pass; \ - debug_verify(*this); \ + debug_verify(world()); \ } RUN_PASS(cleanup()) - RUN_PASS(while (partial_evaluation(*this, true))); // lower2cff + RUN_PASS(while (partial_evaluation(world(), true))); // lower2cff RUN_PASS(flatten_tuples(*this)) - RUN_PASS(clone_bodies(*this)) + RUN_PASS(clone_bodies(world())) RUN_PASS(split_slots(*this)) - RUN_PASS(closure_conversion(*this)) + RUN_PASS(closure_conversion(world())) RUN_PASS(lift_builtins(*this)) RUN_PASS(inliner(*this)) RUN_PASS(hoist_enters(*this)) - RUN_PASS(dead_load_opt(*this)) + RUN_PASS(dead_load_opt(world())) RUN_PASS(cleanup()) - RUN_PASS(codegen_prepare(*this)) + RUN_PASS(codegen_prepare(world())) } } diff --git a/src/thorin/world.h b/src/thorin/world.h index 940680ef7..3a0d11be0 100644 --- a/src/thorin/world.h +++ b/src/thorin/world.h @@ -245,10 +245,6 @@ class World : public TypeTable, public Streamable { Continuation* end_scope() const { return data_.end_scope_; } const Filter* filter(const Defs, Debug dbg = {}); - /// Performs dead code, unreachable code and unused type elimination. - void cleanup(); - void opt(); - // getters const std::string& name() const { return data_.name_; } @@ -310,14 +306,6 @@ class World : public TypeTable, public Streamable { static std::string colorize(const std::string& str, int color); //@} - friend void swap(World& w1, World& w2) { - using std::swap; - swap(static_cast(w1), static_cast(w2)); - swap(w1.state_, w2.state_); - swap(w1.data_, w2.data_); - swap(w1.stream_, w2.stream_); - } - 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 = {}); @@ -369,6 +357,21 @@ class World : public TypeTable, public Streamable { friend class Filter; friend class App; friend class Importer; + friend class Thorin; +}; + +class Thorin { +public: + /// Initial world constructor + explicit Thorin(const std::string& name); + + World& world() { return *world_; }; + + /// Performs dead code, unreachable code and unused type elimination. + void cleanup(); + void opt(); +private: + std::unique_ptr world_; }; } From 9aeac583e614db344e52559e2fefef979d66ba70 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Mon, 23 Jan 2023 17:31:48 +0100 Subject: [PATCH 158/342] merged Type and Def --- src/thorin/be/c/c.cpp | 30 +- src/thorin/be/emitter.h | 2 +- src/thorin/be/llvm/llvm.cpp | 25 +- src/thorin/be/llvm/nvvm.cpp | 2 +- src/thorin/be/llvm/parallel.cpp | 6 +- src/thorin/be/llvm/runtime.cpp | 8 +- src/thorin/continuation.cpp | 76 ++++- src/thorin/continuation.h | 11 +- src/thorin/def.cpp | 18 +- src/thorin/def.h | 33 ++- src/thorin/primop.cpp | 40 +-- src/thorin/primop.h | 67 ++--- src/thorin/rec_stream.cpp | 2 - src/thorin/tables/nodetable.h | 1 + src/thorin/transform/cleanup_world.cpp | 7 +- src/thorin/transform/closure_conversion.cpp | 35 +-- src/thorin/transform/flatten_tuples.cpp | 6 +- src/thorin/transform/hls_channels.cpp | 6 +- src/thorin/transform/importer.cpp | 29 +- src/thorin/transform/importer.h | 3 +- src/thorin/transform/mangle.cpp | 2 +- src/thorin/transform/mangle.h | 1 - src/thorin/type.cpp | 144 ++++------ src/thorin/type.h | 304 ++++++++------------ src/thorin/world.cpp | 32 +-- src/thorin/world.h | 41 ++- 26 files changed, 474 insertions(+), 457 deletions(-) diff --git a/src/thorin/be/c/c.cpp b/src/thorin/be/c/c.cpp index 33ac9405f..ba109b4af 100644 --- a/src/thorin/be/c/c.cpp +++ b/src/thorin/be/c/c.cpp @@ -218,7 +218,7 @@ std::string CCodeGen::convert(const Type* type) { StringStream s; std::string name; - if (type == world().unit() || type->isa() || type->isa()) + if (type == world().unit_type() || type->isa() || type->isa()) s << "void"; else if (auto primtype = type->isa()) { switch (primtype->primtype_tag()) { @@ -252,7 +252,7 @@ std::string CCodeGen::convert(const Type* type) { } else if (auto tuple = type->isa()) { name = tuple_name(tuple); s.fmt("typedef struct {{\t\n"); - s.rangei(tuple->ops(), "\n", [&](size_t i) { s.fmt("{} e{};", convert(tuple->op(i)), i); }); + s.rangei(tuple->ops(), "\n", [&](size_t i) { s.fmt("{} e{};", convert(tuple->types()[i]), i); }); s.fmt("\b\n}} {};\n", name); } else if (auto variant = type->isa()) { types_[variant] = name = variant->name().str(); @@ -267,9 +267,9 @@ std::string CCodeGen::convert(const Type* type) { if (variant->has_payload()) { s.fmt("union {{\t\n"); s.rangei(variant->ops(), "\n", [&] (size_t i) { - if (is_type_unit(variant->op(i))) + if (is_type_unit(variant->types()[i])) s << "// "; - s.fmt("{} {};", convert(variant->op(i)), variant->op_name(i)); + s.fmt("{} {};", convert(variant->types()[i]), variant->op_name(i)); }); s.fmt("\b\n}} data;\n"); } @@ -281,14 +281,14 @@ std::string CCodeGen::convert(const Type* type) { if ((lang_ == Lang::OpenCL || lang_ == Lang::HLS) && is_channel_type(struct_type)) use_channels_ = true; if (lang_ == Lang::OpenCL && use_channels_) { - s.fmt("typedef {} {}_{};\n", convert(struct_type->op(0)), name, struct_type->gid()); + s.fmt("typedef {} {}_{};\n", convert(struct_type->types()[0]), name, struct_type->gid()); name = (struct_type->name().str() + "_" + std::to_string(type->gid())); } else if (is_channel_type(struct_type) && lang_ == Lang::HLS) { - s.fmt("typedef {} {}_{};\n", convert(struct_type->op(0)), name, struct_type->gid()); + s.fmt("typedef {} {}_{};\n", convert(struct_type->types()[0]), name, struct_type->gid()); name = ("hls::stream<" + name + "_" + std::to_string(type->gid()) + ">"); } else { s.fmt("typedef struct {{\t\n"); - s.rangei(struct_type->ops(), "\n", [&] (size_t i) { s.fmt("{} {};", convert(struct_type->op(i)), struct_type->op_name(i)); }); + s.rangei(struct_type->ops(), "\n", [&] (size_t i) { s.fmt("{} {};", convert(struct_type->types()[i]), struct_type->op_name(i)); }); s.fmt("\b\n}} {};\n", name); } } else { @@ -526,15 +526,15 @@ static inline bool is_passed_via_buffer(const Param* param) { static inline const Type* ret_type(const FnType* fn_type) { auto ret_fn_type = (*std::find_if( - fn_type->ops().begin(), fn_type->ops().end(), [] (const Type* op) { + fn_type->types().begin(), fn_type->types().end(), [] (const Type* op) { return op->order() % 2 == 1; }))->as(); std::vector types; - for (auto op : ret_fn_type->ops()) { + for (auto op : ret_fn_type->types()) { if (op->isa() || is_type_unit(op) || op->order() > 0) continue; types.push_back(op); } - return fn_type->table().tuple_type(types); + return fn_type->world().tuple_type(types); } static inline const Type* pointee_or_elem_type(const PtrType* ptr_type) { @@ -913,12 +913,12 @@ std::string CCodeGen::emit_bottom(const Type* type) { StringStream s; s.fmt("{} ", constructor_prefix(type)); s << "{ "; - s.range(type->ops(), ", ", [&] (const Type* op) { s << emit_bottom(op); }); + s.range(type->ops(), ", ", [&] (const Def* op) { s << emit_bottom(op->as()); }); s << " }"; return s.str(); } else if (auto variant_type = type->isa()) { if (variant_type->has_payload()) { - auto non_unit = *std::find_if(variant_type->ops().begin(), variant_type->ops().end(), + auto non_unit = *std::find_if(variant_type->types().begin(), variant_type->types().end(), [] (const Type* op) { return !is_type_unit(op); }); return constructor_prefix(type) + " { { " + emit_bottom(non_unit) + " }, 0 }"; } @@ -1252,7 +1252,7 @@ std::string CCodeGen::emit_def(BB* bb, const Def* def) { if (auto tup = ass->type()->isa()) { for (size_t i = 1, e = tup->num_ops(); i != e; ++i) { auto name = ass->out(i)->unique_name(); - func_impls_.fmt("{} {};\n", convert(tup->op(i)), name); + func_impls_.fmt("{} {};\n", convert(tup->types()[i]), name); func_defs_.insert(ass->out(i)); outputs.emplace_back(name); defs_[ass->out(i)] = name; @@ -1465,10 +1465,10 @@ void CCodeGen::emit_c_int() { continue; // Generate C types for structs used by imported or exported functions - for (auto op : cont->type()->ops()) { + for (auto op : cont->type()->types()) { if (auto fn_type = op->isa()) { // Convert the return types as well (those are embedded in return continuations) - for (auto other_op : fn_type->ops()) { + for (auto other_op : fn_type->types()) { if (!other_op->isa()) convert(other_op); } diff --git a/src/thorin/be/emitter.h b/src/thorin/be/emitter.h index 72300ba06..90caef62f 100644 --- a/src/thorin/be/emitter.h +++ b/src/thorin/be/emitter.h @@ -66,7 +66,7 @@ class Emitter { Scheduler scheduler_; DefMap defs_; - TypeMap types_; + DefMap types_; ContinuationMap cont2bb_; Continuation* entry_ = nullptr; }; diff --git a/src/thorin/be/llvm/llvm.cpp b/src/thorin/be/llvm/llvm.cpp index 9b04de2a1..cdcc260aa 100644 --- a/src/thorin/be/llvm/llvm.cpp +++ b/src/thorin/be/llvm/llvm.cpp @@ -144,14 +144,14 @@ llvm::Type* CodeGen::convert(const Type* type) { auto fn = type->as(); llvm::Type* ret = nullptr; std::vector ops; - for (auto op : fn->ops()) { - if (op->isa() || op == world().unit()) continue; + for (auto op : fn->types()) { + if (op->isa() || op == world().unit_type()) continue; auto fn = op->isa(); if (fn && !op->isa()) { assert(!ret && "only one 'return' supported"); std::vector ret_types; - for (auto fn_op : fn->ops()) { - if (fn_op->isa() || fn_op == world().unit()) continue; + for (auto fn_op : fn->types()) { + if (fn_op->isa() || fn_op == world().unit_type()) continue; ret_types.push_back(convert(fn_op)); } if (ret_types.size() == 0) ret = llvm::Type::getVoidTy(context()); @@ -185,7 +185,7 @@ llvm::Type* CodeGen::convert(const Type* type) { Array llvm_types(struct_type->num_ops()); for (size_t i = 0, e = llvm_types.size(); i != e; ++i) - llvm_types[i] = convert(struct_type->op(i)); + llvm_types[i] = convert(struct_type->types()[i]); llvm_struct->setBody(llvm_ref(llvm_types)); return llvm_struct; } @@ -194,19 +194,20 @@ llvm::Type* CodeGen::convert(const Type* type) { auto tuple = type->as(); Array llvm_types(tuple->num_ops()); for (size_t i = 0, e = llvm_types.size(); i != e; ++i) - llvm_types[i] = convert(tuple->op(i)); + llvm_types[i] = convert(tuple->types()[i]); llvm_type = llvm::StructType::get(context(), llvm_ref(llvm_types)); return types_[tuple] = llvm_type; } case Node_VariantType: { + auto variant_type = type->as(); assert(type->num_ops() > 0); // Max alignment/size constraints respectively in the variant type alternatives dictate the ones to use for the overall type size_t max_align = 0, max_size = 0; auto layout = module().getDataLayout(); llvm::Type* max_align_type; - for (auto op : type->ops()) { + for (auto op : variant_type->types()) { auto op_type = convert(op); size_t size = layout.getTypeAllocSize(op_type); size_t align = layout.getABITypeAlignment(op_type); @@ -828,7 +829,7 @@ llvm::Value* CodeGen::emit_bb(BB& bb, const Def* def) { } else if (auto variant_extract = def->isa()) { auto variant_value = variant_extract->op(0); auto llvm_value = emit(variant_value); - auto target_type = variant_value->type()->op(variant_extract->index()); + auto target_type = variant_value->type()->op(variant_extract->index())->as(); if (is_type_unit(target_type)) return nullptr; @@ -1081,15 +1082,15 @@ llvm::Value* CodeGen::emit_lea(llvm::IRBuilder<>& irbuilder, const LEA* lea) { llvm::Value* CodeGen::emit_assembly(llvm::IRBuilder<>& irbuilder, const Assembly* assembly) { emit_unsafe(assembly->mem()); - auto out_type = assembly->type(); + auto out_type = assembly->type()->isa(); llvm::Type* res_type; bool mem_only = false; - if (out_type->isa()) { + if (out_type) { if (out_type->num_ops() == 2) - res_type = convert(assembly->type()->op(1)); + res_type = convert(out_type->types()[1]); else - res_type = convert(world().tuple_type(assembly->type()->ops().skip_front())); + res_type = convert(world().tuple_type(out_type->types().skip_front())); } else { res_type = llvm::Type::getVoidTy(context()); mem_only = true; diff --git a/src/thorin/be/llvm/nvvm.cpp b/src/thorin/be/llvm/nvvm.cpp index 180f3ac2e..2374b48b8 100644 --- a/src/thorin/be/llvm/nvvm.cpp +++ b/src/thorin/be/llvm/nvvm.cpp @@ -54,7 +54,7 @@ static AddrSpace resolve_addr_space(const Def* def) { llvm::FunctionType* NVVMCodeGen::convert_fn_type(Continuation* continuation) { // skip non-global address-space parameters std::vector types; - for (auto type : continuation->type()->ops()) { + for (auto type : continuation->type()->types()) { if (auto ptr = type->isa()) if (ptr->addr_space() == AddrSpace::Texture) continue; diff --git a/src/thorin/be/llvm/parallel.cpp b/src/thorin/be/llvm/parallel.cpp index 5494ddbe1..ed3dac954 100644 --- a/src/thorin/be/llvm/parallel.cpp +++ b/src/thorin/be/llvm/parallel.cpp @@ -36,7 +36,7 @@ Continuation* CodeGen::emit_parallel(llvm::IRBuilder<>& irbuilder, Continuation* } // fetch values and create a unified struct which contains all values (closure) - auto closure_type = convert(world().tuple_type(continuation->arg_fn_type()->ops().skip_front(PAR_NUM_ARGS))); + auto closure_type = convert(world().tuple_type(continuation->arg_fn_type()->types().skip_front(PAR_NUM_ARGS))); llvm::Value* closure = llvm::UndefValue::get(closure_type); if (num_kernel_args != 1) { for (size_t i = 0; i < num_kernel_args; ++i) @@ -131,7 +131,7 @@ Continuation* CodeGen::emit_fibers(llvm::IRBuilder<>& irbuilder, Continuation* c } // fetch values and create a unified struct which contains all values (closure) - auto closure_type = convert(world().tuple_type(continuation->arg_fn_type()->ops().skip_front(FIB_NUM_ARGS))); + auto closure_type = convert(world().tuple_type(continuation->arg_fn_type()->types().skip_front(FIB_NUM_ARGS))); llvm::Value* closure = llvm::UndefValue::get(closure_type); if (num_kernel_args != 1) { for (size_t i = 0; i < num_kernel_args; ++i) @@ -215,7 +215,7 @@ Continuation* CodeGen::emit_spawn(llvm::IRBuilder<>& irbuilder, Continuation* co } // fetch values and create a unified struct which contains all values (closure) - auto closure_type = convert(world().tuple_type(continuation->arg_fn_type()->ops().skip_front(SPAWN_NUM_ARGS))); + auto closure_type = convert(world().tuple_type(continuation->arg_fn_type()->types().skip_front(SPAWN_NUM_ARGS))); llvm::Value* closure = nullptr; if (closure_type->isStructTy()) { closure = llvm::UndefValue::get(closure_type); diff --git a/src/thorin/be/llvm/runtime.cpp b/src/thorin/be/llvm/runtime.cpp index 47e7dda8f..04f9a6d0b 100644 --- a/src/thorin/be/llvm/runtime.cpp +++ b/src/thorin/be/llvm/runtime.cpp @@ -45,15 +45,15 @@ static bool contains_ptrtype(const Type* type) { case Node_StructType: { bool good = true; auto struct_type = type->as(); - for (size_t i = 0, e = struct_type->num_ops(); i != e; ++i) - good &= contains_ptrtype(struct_type->op(i)); + for (auto& t : struct_type->types()) + good &= contains_ptrtype(t); return good; } case Node_TupleType: { bool good = true; auto tuple = type->as(); - for (size_t i = 0, e = tuple->num_ops(); i != e; ++i) - good &= contains_ptrtype(tuple->op(i)); + for (auto& t : tuple->types()) + good &= contains_ptrtype(t); return good; } default: return true; diff --git a/src/thorin/continuation.cpp b/src/thorin/continuation.cpp index c66302040..ccf1e6dc0 100644 --- a/src/thorin/continuation.cpp +++ b/src/thorin/continuation.cpp @@ -10,16 +10,27 @@ namespace thorin { //------------------------------------------------------------------------------ -Param::Param(World& world, const Type* type, Continuation* continuation, size_t index, Debug dbg) - : Def(Node_Param, world, type, 1, dbg) +Param::Param(World& world, const Type* type, const Continuation* continuation, size_t index, Debug dbg) + : Def(world, Node_Param, type, { continuation }, dbg) , index_(index) -{ - set_op(0, continuation); +{} + +const Def* Param::rebuild(World& world, const Type* t, Defs defs) const { + assert(defs.size() == 1 && defs[0]->isa()); + return world.param(t, defs[0]->as(), index(), debug()); +} + +hash_t Param::vhash() const { + return hash_combine(Def::vhash(), (hash_t) index()); +} + +bool Param::equal(const Def* other) const { + return Def::equal(other) && other->as()->index() == index(); } //------------------------------------------------------------------------------ -App::App(World& world, const Defs ops, Debug dbg) : Def(Node_App, world, ops[0]->world().bottom_type(), ops, dbg) { +App::App(World& world, const Defs ops, Debug dbg) : Def(world, Node_App, ops[0]->world().bottom_type(), ops, dbg) { #if THORIN_ENABLE_CHECKS verify(); if (auto cont = callee()->isa_nom()) @@ -41,7 +52,7 @@ void App::verify() const { //------------------------------------------------------------------------------ -Filter::Filter(World& world, const Defs defs, Debug dbg) : Def(Node_Filter, world, world.bottom_type(), defs, dbg) {} +Filter::Filter(World& world, const Defs defs, Debug dbg) : Def(world, Node_Filter, world.bottom_type(), defs, dbg) {} const Filter* Filter::cut(ArrayRef indices) const { return world().filter(ops().cut(indices), debug()); @@ -49,16 +60,23 @@ const Filter* Filter::cut(ArrayRef indices) const { //------------------------------------------------------------------------------ -Continuation::Continuation(World& w, const FnType* fn, const Attributes& attributes, Debug dbg) - : Def(Node_Continuation, w, fn, 2, dbg) +Continuation::Continuation(World& w, const FnType* pi, const Attributes& attributes, Debug dbg) + : Def(w, Node_Continuation, pi, 2, dbg) , attributes_(attributes) { - params_.reserve(fn->num_ops()); + params_.reserve(pi->num_ops()); set_op(0, world().bottom(world().bottom_type())); set_op(1, world().filter({}, dbg)); + + size_t i = 0; + for (auto op : pi->types()) { + auto p = w.param(op, this, i++, dbg); + params_.emplace_back(p); + } } -Continuation* Continuation::stub() const { +// TODO: merge with regular stub() +Continuation* Continuation::mangle_stub() const { Rewriter rewriter; auto result = world().continuation(type(), attributes(), debug_history()); @@ -78,6 +96,40 @@ Continuation* Continuation::stub() const { return result; } +Continuation* Continuation::stub(World& nworld, const Type* t) const { + assert(!dead_); + // TODO maybe we want to deal with intrinsics in a more streamlined way + if (this == world().branch()) + return nworld.branch(); + if (this == world().end_scope()) + return nworld.end_scope(); + + auto npi = t->isa(); + assert(npi); + Continuation* ncontinuation = nworld.continuation(npi, attributes(), debug_history()); + assert(&ncontinuation->world() == &nworld); + assert(&npi->world() == &nworld); + for (size_t i = 0, e = num_params(); i != e; ++i) + ncontinuation->param(i)->set_name(param(i)->debug_history().name); + + if (is_external()) + nworld.make_external(ncontinuation); + return ncontinuation; +} + +void Continuation::rebuild_from(const Def*, Defs nops) { + if (this == world().branch()) + return; + if (this == world().end_scope()) + return; + + auto napp = nops[0]->isa(); + if (napp) + set_body(napp); + set_filter(nops[1]->as()); + verify(); +} + Array Continuation::params_as_defs() const { Array params(num_params()); for (size_t i = 0, e = num_params(); i != e; ++i) @@ -126,9 +178,9 @@ const FnType* Continuation::arg_fn_type() const { const Param* Continuation::append_param(const Type* param_type, Debug dbg) { size_t size = type()->num_ops(); Array ops(size + 1); - *std::copy(type()->ops().begin(), type()->ops().end(), ops.begin()) = param_type; + *std::copy(type()->types().begin(), type()->types().end(), ops.begin()) = param_type; clear_type(); - set_type(param_type->table().fn_type(ops)); // update type + set_type(world().fn_type(ops)); // update type auto param = world().param(param_type, this, size, dbg); // append new param params_.push_back(param); diff --git a/src/thorin/continuation.h b/src/thorin/continuation.h index f88e24885..32bbecd69 100644 --- a/src/thorin/continuation.h +++ b/src/thorin/continuation.h @@ -24,12 +24,15 @@ typedef std::vector Continuations; */ class Param : public Def { private: - Param(World&, const Type* type, Continuation* continuation, size_t index, Debug dbg); + Param(World&, const Type* type, const Continuation* continuation, size_t index, Debug dbg); public: Continuation* continuation() const { return op(0)->as_nom(); } size_t index() const { return index_; } + const Def * rebuild(World&, const Type*, Defs) const override; + bool equal(const Def*) const override; + hash_t vhash() const override; private: const size_t index_; @@ -130,13 +133,15 @@ class Continuation : public Def { }; private: - Continuation(World&, const FnType* fn, const Attributes& attributes, Debug dbg); + Continuation(World&, const FnType* pi, const Attributes& attributes, Debug dbg); virtual ~Continuation() { for (auto param : params()) delete param; } public: const FnType* type() const { return Def::type()->as(); } - Continuation* stub() const; + Continuation* mangle_stub() const; + Continuation* stub(World&, const Type*) const override; + void rebuild_from(const Def* old, Defs new_ops) override; const Param* append_param(const Type* type, Debug dbg = {}); Continuations preds() const; Continuations succs() const; diff --git a/src/thorin/def.cpp b/src/thorin/def.cpp index a0d14e59c..b3b84d731 100644 --- a/src/thorin/def.cpp +++ b/src/thorin/def.cpp @@ -14,7 +14,7 @@ namespace thorin { size_t Def::gid_counter_ = 1; -Def::Def(NodeTag tag, World& world, const Type* type, Defs ops, Debug dbg) +Def::Def(World& world, NodeTag tag, const Type* type, Defs ops, Debug dbg) : tag_(tag) , ops_(ops.size()) , world_(world) @@ -30,7 +30,7 @@ Def::Def(NodeTag tag, World& world, const Type* type, Defs ops, Debug dbg) set_op(i, ops[i]); } -Def::Def(NodeTag tag, World& world, const Type* type, size_t size, Debug dbg) +Def::Def(World& world, NodeTag tag, const Type* type, size_t size, Debug dbg) : tag_(tag) , ops_(size) , world_(world) @@ -43,6 +43,10 @@ Def::Def(NodeTag tag, World& world, const Type* type, size_t size, Debug dbg) Dep::Bot ) {} +int Def::order() const { + return type()->order(); +} + Debug Def::debug_history() const { #if THORIN_ENABLE_CHECKS return world().track_history() ? Debug(unique_name(), loc()) : debug(); @@ -96,7 +100,7 @@ std::string Def::unique_name() const { } bool is_unit(const Def* def) { - return def->type() == def->world().unit(); + return def->type() == def->world().unit_type(); } size_t vector_length(const Def* def) { return def->type()->as()->length(); } @@ -134,6 +138,12 @@ bool is_minus_zero(const Def* def) { return false; } +void Def::rebuild_from(const Def* old, Defs new_ops) { + assert(new_ops.size() == num_ops()); + for (size_t i = 0; i < num_ops(); i++) + set_op(i, new_ops[i]); +} + void Def::replace_uses(const Def* with) const { world().DLOG("replace uses: {} -> {}", this, with); if (this != with) { @@ -149,8 +159,6 @@ void Def::replace_uses(const Def* with) const { } } -World& Def::world() const { return world_; } - uint64_t UseHash::hash(Use use) { assert(use->gid() != uint32_t(-1)); hash_t seed = hash_begin(use.index()); diff --git a/src/thorin/def.h b/src/thorin/def.h index 18118e9d4..b3254d704 100644 --- a/src/thorin/def.h +++ b/src/thorin/def.h @@ -5,8 +5,8 @@ #include #include "thorin/enums.h" -#include "thorin/type.h" #include "thorin/debug.h" +#include "thorin/util/array.h" namespace thorin { @@ -17,6 +17,7 @@ class Def; class Tracker; class Use; class World; +class Type; typedef ArrayRef Defs; @@ -76,6 +77,23 @@ struct UseHash { // using a StackCapacity of 8 covers almost 99% of all real-world use-lists typedef HashSet Uses; +template +struct GIDLt { + bool operator()(T a, T b) const { return a->gid() < b->gid(); } +}; + +template +struct GIDHash { + static hash_t hash(T n) { return thorin::murmur3(n->gid()); } + static bool eq(T a, T b) { return a == b; } + static T sentinel() { return T(1); } +}; + +template +using GIDMap = thorin::HashMap>; +template +using GIDSet = thorin::HashSet>; + template using DefMap = GIDMap; using DefSet = GIDSet; @@ -106,9 +124,9 @@ class Def : public RuntimeCast, public Streamable { protected: /// Constructor for a @em structural Def. - Def(NodeTag tag, World&, const Type* type, Defs args, Debug dbg); + Def(World&, NodeTag tag, const Type* type, Defs args, Debug dbg); /// Constructor for a @em nom Def. - Def(NodeTag tag, World&, const Type* type, size_t size, Debug); + Def(World&, NodeTag tag, const Type* type, size_t size, Debug); virtual ~Def() {} void clear_type() { type_ = nullptr; } @@ -122,7 +140,7 @@ class Def : public RuntimeCast, public Streamable { //@{ NodeTag tag() const { return tag_; } size_t gid() const { return gid_; } - World& world() const; + World& world() const { return world_; }; //@} /// @name ops @@ -155,7 +173,8 @@ class Def : public RuntimeCast, public Streamable { /// @name type //@{ const Type* type() const { return type_; } - int order() const { return type()->order(); } + + virtual int order() const; //@} /// @name dependence checks @@ -202,7 +221,8 @@ class Def : public RuntimeCast, public Streamable { /// @name rebuild/stub //@{ virtual const Def* rebuild(World&, const Type*, Defs) const { THORIN_UNREACHABLE; } - // TODO stub + virtual Def* stub(World&, const Type*) const { THORIN_UNREACHABLE; } + virtual void rebuild_from(const Def* old, Defs new_ops); //@} void replace_uses(const Def*) const; @@ -251,7 +271,6 @@ size_t vector_length(const Def*); bool is_unit(const Def*); bool is_primlit(const Def*, int64_t); bool is_minus_zero(const Def*); -inline bool is_mem (const Def* def) { return def->type()->isa(); } inline bool is_zero (const Def* def) { return is_primlit(def, 0); } inline bool is_one (const Def* def) { return is_primlit(def, 1); } inline bool is_allset (const Def* def) { return is_primlit(def, -1); } diff --git a/src/thorin/primop.cpp b/src/thorin/primop.cpp index 9a06a93a1..23d5ba859 100644 --- a/src/thorin/primop.cpp +++ b/src/thorin/primop.cpp @@ -15,16 +15,16 @@ namespace thorin { */ PrimLit::PrimLit(World& world, PrimTypeTag tag, Box box, Debug dbg) - : Literal((NodeTag) tag, world, world.prim_type(tag), dbg) + : Literal(world, (NodeTag) tag, world.prim_type(tag), dbg) , box_(box) {} Cmp::Cmp(CmpTag tag, World& world, const Def* lhs, const Def* rhs, Debug dbg) - : BinOp((NodeTag) tag, world, world.type_bool(vector_length(lhs->type())), lhs, rhs, dbg) + : BinOp(world, (NodeTag) tag, world.type_bool(vector_length(lhs->type())), lhs, rhs, dbg) {} DefiniteArray::DefiniteArray(World& world, const Type* elem, Defs args, Debug dbg) - : Aggregate(Node_DefiniteArray, world, args, dbg) + : Aggregate(world, Node_DefiniteArray, args, dbg) { set_type(world.definite_array_type(elem, args.size())); #if THORIN_ENABLE_CHECKS @@ -34,13 +34,13 @@ DefiniteArray::DefiniteArray(World& world, const Type* elem, Defs args, Debug db } IndefiniteArray::IndefiniteArray(World& world, const Type* elem, const Def* dim, Debug dbg) - : Aggregate(Node_IndefiniteArray, world, {dim}, dbg) + : Aggregate(world, Node_IndefiniteArray, {dim}, dbg) { set_type(world.indefinite_array_type(elem)); } Tuple::Tuple(World& world, Defs args, Debug dbg) - : Aggregate(Node_Tuple, world, args, dbg) + : Aggregate(world, Node_Tuple, args, dbg) { Array elems(num_ops()); for (size_t i = 0, e = num_ops(); i != e; ++i) @@ -50,7 +50,7 @@ Tuple::Tuple(World& world, Defs args, Debug dbg) } Vector::Vector(World& world, Defs args, Debug dbg) - : Aggregate(Node_Vector, world, args, dbg) + : Aggregate(world, Node_Vector, args, dbg) { if (auto primtype = args.front()->type()->isa()) { assert(primtype->length() == 1); @@ -63,15 +63,15 @@ Vector::Vector(World& world, Defs args, Debug dbg) } LEA::LEA(World& world, const Def* ptr, const Def* index, Debug dbg) - : Def(Node_LEA, world, nullptr, {ptr, index}, dbg) + : Def(world, Node_LEA, nullptr, {ptr, index}, dbg) { auto type = ptr_type(); if (auto tuple = ptr_pointee()->isa()) { - set_type(world.ptr_type(get(tuple->ops(), index), type->length(), type->device(), type->addr_space())); + set_type(world.ptr_type(get(tuple->types(), index), type->length(), type->device(), type->addr_space())); } else if (auto array = ptr_pointee()->isa()) { set_type(world.ptr_type(array->elem_type(), type->length(), type->device(), type->addr_space())); } else if (auto struct_type = ptr_pointee()->isa()) { - set_type(world.ptr_type(get(struct_type->ops(), index), type->length(), type->device(), type->addr_space())); + set_type(world.ptr_type(get(struct_type->types(), index), type->length(), type->device(), type->addr_space())); } else if (auto prim_type = ptr_pointee()->isa()) { assert(prim_type->length() > 1); set_type(world.ptr_type(world.prim_type(prim_type->primtype_tag()), type->length(), type->device(), type->addr_space())); @@ -81,50 +81,50 @@ LEA::LEA(World& world, const Def* ptr, const Def* index, Debug dbg) } Known::Known(World& world, const Def* def, Debug dbg) - : Def(Node_Known, world, world.type_bool(), {def}, dbg) + : Def(world, Node_Known, world.type_bool(), {def}, dbg) {} AlignOf::AlignOf(World& world, const Def* def, Debug dbg) - : Def(Node_AlignOf, world, world.type_qs64(), {def}, dbg) + : Def(world, Node_AlignOf, world.type_qs64(), {def}, dbg) {} SizeOf::SizeOf(World& world, const Def* def, Debug dbg) - : Def(Node_SizeOf, world, world.type_qs64(), {def}, dbg) + : Def(world, Node_SizeOf, world.type_qs64(), {def}, dbg) {} Slot::Slot(World& world, const Type* type, const Def* frame, Debug dbg) - : Def(Node_Slot, world, type->table().ptr_type(type), {frame}, dbg) + : Def(world, Node_Slot, world.ptr_type(type), {frame}, dbg) { assert(frame->type()->isa()); } Global::Global(World& world, const Def* init, bool is_mutable, Debug dbg) - : Def(Node_Global, world, init->type()->table().ptr_type(init->type()), {init}, dbg) + : Def(world, Node_Global, world.ptr_type(init->type()), {init}, dbg) , is_mutable_(is_mutable) { assert(!init->has_dep(Dep::Param)); } Alloc::Alloc(World& world, const Type* type, const Def* mem, const Def* extra, Debug dbg) - : MemOp(Node_Alloc, world, nullptr, {mem, extra}, dbg) + : MemOp(world, Node_Alloc, nullptr, {mem, extra}, dbg) { set_type(world.tuple_type({world.mem_type(), world.ptr_type(type)})); } Load::Load(World& world, const Def* mem, const Def* ptr, Debug dbg) - : Access(Node_Load, world, nullptr, {mem, ptr}, dbg) + : Access(world, Node_Load, nullptr, {mem, ptr}, dbg) { set_type(world.tuple_type({world.mem_type(), ptr->type()->as()->pointee()})); } Enter::Enter(World& world, const Def* mem, Debug dbg) - : MemOp(Node_Enter, world, nullptr, {mem}, dbg) + : MemOp(world, Node_Enter, nullptr, {mem}, dbg) { set_type(world.tuple_type({world.mem_type(), world.frame_type()})); } Assembly::Assembly(World& world, const Type *type, Defs inputs, std::string asm_template, ArrayRef output_constraints, ArrayRef input_constraints, ArrayRef clobbers, Flags flags, Debug dbg) - : MemOp(Node_Assembly, world, type, inputs, dbg) + : MemOp(world, Node_Assembly, type, inputs, dbg) , asm_template_(asm_template) , output_constraints_(output_constraints) , input_constraints_(input_constraints) @@ -301,13 +301,13 @@ const Def* Def::out(size_t i) const { const Type* Extract::extracted_type(const Def* agg, const Def* index) { if (auto tuple = agg->type()->isa()) - return get(tuple->ops(), index); + return get(tuple->types(), index); else if (auto array = agg->type()->isa()) return array->elem_type(); else if (auto vector = agg->type()->isa()) return vector->scalarize(); else if (auto struct_type = agg->type()->isa()) - return get(struct_type->ops(), index); + return get(struct_type->types(), index); THORIN_UNREACHABLE; } diff --git a/src/thorin/primop.h b/src/thorin/primop.h index db2aaf175..e82d2c218 100644 --- a/src/thorin/primop.h +++ b/src/thorin/primop.h @@ -3,6 +3,7 @@ #include "thorin/config.h" #include "thorin/def.h" +#include "thorin/type.h" #include "thorin/enums.h" #include "thorin/util/hash.h" @@ -10,8 +11,8 @@ namespace thorin { class Literal : public Def { protected: - Literal(NodeTag tag, World& world, const Type* type, Debug dbg) - : Def(tag, world, type, Defs{}, dbg) + Literal(World& world, NodeTag tag, const Type* type, Debug dbg) + : Def(world, tag, type, Defs{}, dbg) {} }; @@ -19,7 +20,7 @@ class Literal : public Def { class Bottom : public Literal { private: Bottom(World& world, const Type* type, Debug dbg) - : Literal(Node_Bottom, world, type, dbg) + : Literal(world, Node_Bottom, type, dbg) {} const Def* rebuild(World&, const Type*, Defs) const override; @@ -31,7 +32,7 @@ class Bottom : public Literal { class Top : public Literal { private: Top(World& world, const Type* type, Debug dbg) - : Literal(Node_Top, world, type, dbg) + : Literal(world, Node_Top, type, dbg) {} const Def* rebuild(World&, const Type*, Defs) const override; @@ -80,7 +81,7 @@ T get(ArrayRef array, const Def* def) { return array[primlit_value(de class Select : public Def { private: Select(World& world, const Def* cond, const Def* tval, const Def* fval, Debug dbg) - : Def(Node_Select, world, tval->type(), {cond, tval, fval}, dbg) + : Def(world, Node_Select, tval->type(), {cond, tval, fval}, dbg) { assert(is_type_bool(cond->type())); assert(tval->type() == fval->type() && "types of both values must be equal"); @@ -126,8 +127,8 @@ class SizeOf : public Def { /// Base class for all side-effect free binary \p Def%s. class BinOp : public Def { protected: - BinOp(NodeTag tag, World& world, const Type* type, const Def* lhs, const Def* rhs, Debug dbg) - : Def(tag, world, type, {lhs, rhs}, dbg) + BinOp(World& world, NodeTag tag, const Type* type, const Def* lhs, const Def* rhs, Debug dbg) + : Def(world, tag, type, {lhs, rhs}, dbg) { assert(lhs->type() == rhs->type() && "types are not equal"); } @@ -141,7 +142,7 @@ class BinOp : public Def { class ArithOp : public BinOp { private: ArithOp(ArithOpTag tag, World& world, const Def* lhs, const Def* rhs, Debug dbg) - : BinOp((NodeTag) tag, world, lhs->type(), lhs, rhs, dbg) + : BinOp(world, (NodeTag) tag, lhs->type(), lhs, rhs, dbg) {} const Def* rebuild(World&, const Type*, Defs) const override; @@ -172,8 +173,8 @@ class Cmp : public BinOp { /// Common mathematical function such as `sin()` or `cos()`. class MathOp : public Def { private: - MathOp(MathOpTag tag, World& world, const Type* type, Defs args, Debug dbg) - : Def((NodeTag)tag, world, type, args, dbg) + MathOp(World& world, MathOpTag tag, const Type* type, Defs args, Debug dbg) + : Def(world, (NodeTag)tag, type, args, dbg) {} const Def* rebuild(World&, const Type*, Defs) const override; @@ -189,8 +190,8 @@ class MathOp : public Def { /// Base class for @p Bitcast and @p Cast. class ConvOp : public Def { protected: - ConvOp(NodeTag tag, World& world, const Def* from, const Type* to, Debug dbg) - : Def(tag, world, to, {from}, dbg) + ConvOp(World& world, NodeTag tag, const Def* from, const Type* to, Debug dbg) + : Def(world, tag, to, {from}, dbg) {} public: @@ -201,7 +202,7 @@ class ConvOp : public Def { class Cast : public ConvOp { private: Cast(World& world, const Type* to, const Def* from, Debug dbg) - : ConvOp(Node_Cast, world, from, to, dbg) + : ConvOp(world, Node_Cast, from, to, dbg) {} const Def* rebuild(World&, const Type*, Defs) const override; @@ -213,7 +214,7 @@ class Cast : public ConvOp { class Bitcast : public ConvOp { private: Bitcast(World& world, const Type* to, const Def* from, Debug dbg) - : ConvOp(Node_Bitcast, world, from, to, dbg) + : ConvOp(world, Node_Bitcast, from, to, dbg) {} const Def* rebuild(World&, const Type*, Defs) const override; @@ -224,8 +225,8 @@ class Bitcast : public ConvOp { /// Base class for all aggregate data constructers. class Aggregate : public Def { protected: - Aggregate(NodeTag tag, World& world, Defs args, Debug dbg) - : Def(tag, world, nullptr /*set later*/, args, dbg) + Aggregate(World& world, NodeTag tag, Defs args, Debug dbg) + : Def(world, tag, nullptr /*set later*/, args, dbg) {} }; @@ -275,7 +276,7 @@ class Tuple : public Aggregate { class Variant : public Def { private: Variant(World& world, const VariantType* variant_type, const Def* value, size_t index, Debug dbg) - : Def(Node_Variant, world, variant_type, {value}, dbg), index_(index) + : Def(world, Node_Variant, variant_type, {value}, dbg), index_(index) { assert(variant_type->op(index) == value->type()); } @@ -298,7 +299,7 @@ class Variant : public Def { class VariantIndex : public Def { private: VariantIndex(World& world, const Type* int_type, const Def* value, Debug dbg) - : Def(Node_VariantIndex, world, int_type, {value}, dbg) + : Def(world, Node_VariantIndex, int_type, {value}, dbg) { assert(value->type()->isa()); assert(is_type_s(int_type) || is_type_u(int_type)); @@ -312,7 +313,7 @@ class VariantIndex : public Def { class VariantExtract : public Def { private: VariantExtract(World& world, const Type* type, const Def* value, size_t index, Debug dbg) - : Def(Node_VariantExtract, world, type, {value}, dbg), index_(index) + : Def(world, Node_VariantExtract, type, {value}, dbg), index_(index) { assert(value->type()->as()->op(index) == type); } @@ -334,7 +335,7 @@ class VariantExtract : public Def { class Closure : public Aggregate { private: Closure(World& world, const ClosureType* closure_type, const Def* fn, const Def* env, Debug dbg) - : Aggregate(Node_Closure, world, {fn, env}, dbg) + : Aggregate(world, Node_Closure, {fn, env}, dbg) { set_type(closure_type); } @@ -352,7 +353,7 @@ class Closure : public Aggregate { class StructAgg : public Aggregate { private: StructAgg(World& world, const StructType* struct_type, Defs args, Debug dbg) - : Aggregate(Node_StructAgg, world, args, dbg) + : Aggregate(world, Node_StructAgg, args, dbg) { #if THORIN_ENABLE_CHECKS assert(struct_type->num_ops() == args.size()); @@ -383,8 +384,8 @@ class Vector : public Aggregate { /// Base class for functional @p Insert and @p Extract. class AggOp : public Def { protected: - AggOp(NodeTag tag, World& world, const Type* type, Defs args, Debug dbg) - : Def(tag, world, type, args, dbg) + AggOp(World& world, NodeTag tag, const Type* type, Defs args, Debug dbg) + : Def(world, tag, type, args, dbg) {} public: @@ -398,7 +399,7 @@ class AggOp : public Def { class Extract : public AggOp { private: Extract(World& world, const Def* agg, const Def* index, Debug dbg) - : AggOp(Node_Extract, world, extracted_type(agg, index), {agg, index}, dbg) + : AggOp(world, Node_Extract, extracted_type(agg, index), {agg, index}, dbg) {} const Def* rebuild(World&, const Type*, Defs) const override; @@ -418,7 +419,7 @@ class Extract : public AggOp { class Insert : public AggOp { private: Insert(World& world, const Def* agg, const Def* index, const Def* value, Debug dbg) - : AggOp(Node_Insert, world, agg->type(), {agg, index, value}, dbg) + : AggOp(world, Node_Insert, agg->type(), {agg, index, value}, dbg) {} const Def* rebuild(World&, const Type*, Defs) const override; @@ -455,7 +456,7 @@ class LEA : public Def { class Hlt : public Def { private: Hlt(World& world, const Def* def, Debug dbg) - : Def(Node_Hlt, world, def->type(), {def}, dbg) + : Def(world, Node_Hlt, def->type(), {def}, dbg) {} const Def* rebuild(World&, const Type*, Defs) const override; @@ -486,7 +487,7 @@ class Known : public Def { class Run : public Def { private: Run(World& world, const Def* def, Debug dbg) - : Def(Node_Run, world, def->type(), {def}, dbg) + : Def(world, Node_Run, def->type(), {def}, dbg) {} const Def* rebuild(World&, const Type*, Defs) const override; @@ -547,8 +548,8 @@ class Global : public Def { /// Base class for all \p Def%s taking and producing side-effects. class MemOp : public Def { protected: - MemOp(NodeTag tag, World& world, const Type* type, Defs args, Debug dbg) - : Def(tag, world, type, args, dbg) + MemOp(World& world, NodeTag tag, const Type* type, Defs args, Debug dbg) + : Def(world, tag, type, args, dbg) { assert(mem()->type()->isa()); assert(args.size() >= 1); @@ -585,8 +586,8 @@ class Alloc : public MemOp { /// Base class for @p Load and @p Store. class Access : public MemOp { protected: - Access(NodeTag tag, World& world, const Type* type, Defs args, Debug dbg) - : MemOp(tag, world, type, args, dbg) + Access(World& world, NodeTag tag, const Type* type, Defs args, Debug dbg) + : MemOp(world, tag, type, args, dbg) { assert(args.size() >= 2); } @@ -604,7 +605,7 @@ class Load : public Access { bool has_multiple_outs() const override { return true; } const Def* out_val() const { return out(1); } const TupleType* type() const { return MemOp::type()->as(); } - const Type* out_val_type() const { return type()->op(1); } + const Type* out_val_type() const { return type()->op(1)->as(); } private: const Def* rebuild(World&, const Type*, Defs) const override; @@ -616,7 +617,7 @@ class Load : public Access { class Store : public Access { private: Store(World& world, const Def* mem, const Def* ptr, const Def* value, Debug dbg) - : Access(Node_Store, world, mem->type(), {mem, ptr, value}, dbg) + : Access(world, Node_Store, mem->type(), {mem, ptr, value}, dbg) {} const Def* rebuild(World&, const Type*, Defs) const override; diff --git a/src/thorin/rec_stream.cpp b/src/thorin/rec_stream.cpp index 03d6a8c20..4980cd91d 100644 --- a/src/thorin/rec_stream.cpp +++ b/src/thorin/rec_stream.cpp @@ -68,8 +68,6 @@ void RecStreamer::run() { void Def::dump() const { dump(0); } void Def::dump(size_t max) const { Stream s(std::cout); stream(s, max).endl(); } -void Type::dump() const { Stream s(std::cout); stream(s).endl(); } - Stream& Def::stream(Stream& s) const { if (isa() || isa() || no_dep()) return stream1(s); return s << unique_name(); diff --git a/src/thorin/tables/nodetable.h b/src/thorin/tables/nodetable.h index a302d270f..8c17d04dc 100644 --- a/src/thorin/tables/nodetable.h +++ b/src/thorin/tables/nodetable.h @@ -47,6 +47,7 @@ THORIN_NODE(Filter, filter) // Type // PrimType + THORIN_NODE(Star, star) THORIN_NODE(App, app) THORIN_NODE(DefiniteArrayType, definite_array_type) THORIN_NODE(FnType, fn) diff --git a/src/thorin/transform/cleanup_world.cpp b/src/thorin/transform/cleanup_world.cpp index 041db668a..3ea2f26e2 100644 --- a/src/thorin/transform/cleanup_world.cpp +++ b/src/thorin/transform/cleanup_world.cpp @@ -46,7 +46,7 @@ void Cleaner::eliminate_tail_rec() { if (use.index() == 0 && use->isa()) { recursive = true; continue; - } else if (use->isa_nom()) + } else if (use->isa()) continue; // ignore params world().ELOG("non-recursive usage of {} index:{} use:{}", scope.entry()->name(), use.index(), use.def()->to_string()); @@ -202,7 +202,7 @@ void Cleaner::eliminate_params() { if (!proxy_idx.empty()) { auto ncontinuation = world().continuation( - world().fn_type(ocontinuation->type()->ops().cut(proxy_idx)), + world().fn_type(ocontinuation->type()->types().cut(proxy_idx)), ocontinuation->attributes(), ocontinuation->debug_history()); size_t j = 0; for (auto i : param_idx) { @@ -234,7 +234,6 @@ next_continuation:; void Cleaner::rebuild() { auto fresh_world = std::make_unique(world()); Importer importer(*world_, *fresh_world); - importer.type_old2new_.rehash(world_->types().capacity()); importer.def_old2new_.rehash(world_->defs().capacity()); for (auto&& [_, cont] : world().externals()) { @@ -269,7 +268,7 @@ void Cleaner::verify_closedness() { void Cleaner::within(const Def* def) { if (def->isa()) return; // TODO remove once Params are within World's sea of nodes - assert(world().types().contains(def->type())); + assert(&def->type()->world() == &world()); assert_unused(world().defs().contains(def)); } diff --git a/src/thorin/transform/closure_conversion.cpp b/src/thorin/transform/closure_conversion.cpp index bb118c725..d50d6d1ff 100644 --- a/src/thorin/transform/closure_conversion.cpp +++ b/src/thorin/transform/closure_conversion.cpp @@ -24,7 +24,7 @@ class ClosureConversion { continue; } - auto new_type = world_.fn_type(convert(continuation->type())->ops()); + auto new_type = world_.fn_type(defs2types(convert_type(continuation->type())->ops())); if (new_type != continuation->type()) { auto new_continuation = world_.continuation(new_type->as(), continuation->debug()); if (continuation->is_intrinsic()) @@ -66,12 +66,15 @@ class ClosureConversion { if (!callee || !callee->is_intrinsic()) { 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)); - continuation->jump(convert(body->callee(), true), new_args, continuation->debug()); + new_args[i] = convert_def(body->arg(i)); + continuation->jump(convert_def(body->callee(), true), new_args, continuation->debug()); } } - const Def* convert(const Def* def, bool as_callee = false) { + const Def* convert_def(const Def* def, bool as_callee = false) { + if (auto t = def->isa()) + return convert_type(t); + if (new_defs_.count(def)) def = new_defs_[def]; if (def->order() <= 1) return def; @@ -145,13 +148,13 @@ class ClosureConversion { } wrapper->jump(lifted, wrapper_args); - auto closure_type = convert(continuation->type()); + auto closure_type = convert_type(continuation->type()); return world_.closure(closure_type->as(), wrapper, thin_env ? free_vars[0] : world_.tuple(free_vars), continuation->debug()); } else { // TODO need to consider Params? Array ops(def->ops()); - for (auto& op : ops) op = convert(op); - return new_defs_[def] = def->rebuild(world_, convert(def->type()), ops); + for (auto& op : ops) op = convert_def(op); + return new_defs_[def] = def->rebuild(world_, convert_type(def->type()), ops); } THORIN_UNREACHABLE; } @@ -160,10 +163,10 @@ class ClosureConversion { // - fn (A, B, fn(C), fn(D)) => closure(fn (A, B, fn(convert(C)), closure(fn(convert(D))))) // - struct S { fn (X, fn(Y)) } => struct T { closure(fn (X, fn(Y))) } // - ... - const Type* convert(const Type* type) { + const Type* convert_type(const Type* type) { if (new_types_.count(type)) return new_types_[type]; if (type->order() <= 1) return type; - Array ops(type->ops()); + Array ops(type->ops()); const Type* new_type = nullptr; if (type->isa()) { @@ -175,32 +178,32 @@ class ClosureConversion { // accept one parameter of order 1 (the return continuation) for function types bool ret = !type->isa(); for (auto& op : ops) { - op = convert(op); + op = convert_def(op); if (!ret && op->isa() && op->as()->inner_order() == 1) { ret = true; - op = world_.fn_type(op->ops()); + op = world_.fn_type(defs2types(op->ops())); } } if (type->isa()) { - auto struct_type = new_type->as(); + StructType* struct_type = const_cast(new_type->as()); for (size_t i = 0, e = ops.size(); i != e; ++i) - struct_type->set(i, ops[i]); + struct_type->set_op(i, ops[i]); } else { - new_type = type->rebuild(type->table(), ops); + new_type = type->rebuild(world_, type->type(), ops)->as(); } if (new_type->order() <= 1) return new_types_[type] = new_type; else - return new_types_[type] = world_.closure_type(new_type->ops()); + return new_types_[type] = world_.closure_type(defs2types(new_type->ops())); } private: World& world_; Def2Def new_defs_; - Type2Type new_types_; + DefMap new_types_; }; diff --git a/src/thorin/transform/flatten_tuples.cpp b/src/thorin/transform/flatten_tuples.cpp index 8f99bd220..8187b1001 100644 --- a/src/thorin/transform/flatten_tuples.cpp +++ b/src/thorin/transform/flatten_tuples.cpp @@ -13,10 +13,10 @@ static Continuation* unwrap_def(Def2Def&, Def2Def&, const Def*, const FnType*, s // Computes the type of the wrapped function static const Type* wrapped_type(const FnType* fn_type, size_t max_tuple_size) { std::vector nops; - for (auto op : fn_type->ops()) { + for (auto op : fn_type->types()) { if (auto tuple_type = op->isa()) { if (tuple_type->num_ops() <= max_tuple_size) { - for (auto arg : tuple_type->ops()) + for (auto arg : tuple_type->types()) nops.push_back(arg); } else nops.push_back(op); @@ -26,7 +26,7 @@ static const Type* wrapped_type(const FnType* fn_type, size_t max_tuple_size) { nops.push_back(op); } } - return fn_type->table().fn_type(nops); + return fn_type->world().fn_type(nops); } static Continuation* jump(Continuation* cont, Array& args) { diff --git a/src/thorin/transform/hls_channels.cpp b/src/thorin/transform/hls_channels.cpp index c778baf8f..bae0f5291 100644 --- a/src/thorin/transform/hls_channels.cpp +++ b/src/thorin/transform/hls_channels.cpp @@ -166,8 +166,8 @@ DeviceParams hls_channels(Thorin& thorin, Importer& importer, Top2Kernel& top2ke extract_kernel_channels(schedule(scope), def2mode); Array new_param_types(def2mode.size() + old_kernel->num_params()); - std::copy(old_kernel->type()->ops().begin(), - old_kernel->type()->ops().end(), + std::copy(old_kernel->type()->types().begin(), + old_kernel->type()->types().end(), new_param_types.begin()); size_t i = old_kernel->num_params(); // This vector records pairs containing: @@ -205,7 +205,7 @@ DeviceParams hls_channels(Thorin& thorin, Importer& importer, Top2Kernel& top2ke if (auto cont = def->isa_nom()) { // Copy the basic block by calling stub // Or reuse the newly created kernel copy if def is the old kernel - auto new_cont = def == old_kernel ? new_kernel : cont->stub(); + auto new_cont = def == old_kernel ? new_kernel : cont->mangle_stub(); rewriter.old2new[cont] = new_cont; for (size_t i = 0; i < cont->num_params(); ++i) rewriter.old2new[cont->param(i)] = new_cont->param(i); diff --git a/src/thorin/transform/importer.cpp b/src/thorin/transform/importer.cpp index ceaabb6f2..a1ff8bffa 100644 --- a/src/thorin/transform/importer.cpp +++ b/src/thorin/transform/importer.cpp @@ -2,7 +2,7 @@ namespace thorin { -const Type* Importer::import(const Type* otype) { +/*const Type* Importer::import(const Type* otype) { if (auto ntype = type_old2new_.lookup(otype)) { assert(&(*ntype)->table() == &world()); return *ntype; @@ -26,7 +26,7 @@ const Type* Importer::import(const Type* otype) { assert(&ntype->table() == &world()); return ntype; -} +}*/ const Def* Importer::import(const Def* odef) { if (auto ndef = def_old2new_.lookup(odef)) { @@ -34,9 +34,14 @@ const Def* Importer::import(const Def* odef) { return *ndef; } - auto ntype = import(odef->type()); + if (odef == odef->world().star()) { + def_old2new_[odef] = world().star(); + return world().star(); + } + + auto ntype = import(odef->type())->as(); - if (auto oparam = odef->isa()) { + /*if (auto oparam = odef->isa()) { import(oparam->continuation())->as_nom(); auto nparam = def_old2new_[oparam]; assert(nparam && &nparam->world() == &world()); @@ -62,7 +67,7 @@ const Def* Importer::import(const Def* odef) { auto npi = import(ocontinuation->type())->as(); ncontinuation = world().continuation(npi, ocontinuation->attributes(), ocontinuation->debug_history()); assert(&ncontinuation->world() == &world()); - assert(&npi->table() == &world()); + assert(&npi->world() == &world()); for (size_t i = 0, e = ocontinuation->num_params(); i != e; ++i) { ncontinuation->param(i)->set_name(ocontinuation->param(i)->debug_history().name); def_old2new_[ocontinuation->param(i)] = ncontinuation->param(i); @@ -72,6 +77,12 @@ const Def* Importer::import(const Def* odef) { if (ocontinuation->is_external()) world().make_external(ncontinuation); + }*/ + + Def* stub = nullptr; + if (odef->isa_nom()) { + stub = odef->stub(world(), ntype); + def_old2new_[odef] = stub; } size_t size = odef->num_ops(); @@ -86,15 +97,19 @@ const Def* Importer::import(const Def* odef) { auto ndef = odef->rebuild(world(), ntype, nops); todo_ |= odef->tag() != ndef->tag(); return def_old2new_[odef] = ndef; + } else { + assert(odef->isa_nom() && stub); + stub->rebuild_from(odef, nops); + return stub; } - assert(ncontinuation && &ncontinuation->world() == &world()); + /*assert(ncontinuation && &ncontinuation->world() == &world()); auto napp = nops[0]->isa(); if (napp) ncontinuation->set_body(napp); ncontinuation->set_filter(nops[1]->as()); ncontinuation->verify(); - return ncontinuation; + return ncontinuation;*/ } } diff --git a/src/thorin/transform/importer.h b/src/thorin/transform/importer.h index db41b3161..279dadeaf 100644 --- a/src/thorin/transform/importer.h +++ b/src/thorin/transform/importer.h @@ -21,12 +21,11 @@ class Importer { } World& world() { return dst; } - const Type* import(const Type*); + //const Type* import(const Type*); const Def* import(const Def*); bool todo() const { return todo_; } public: - Type2Type type_old2new_; Def2Def def_old2new_; World& src; World& dst; diff --git a/src/thorin/transform/mangle.cpp b/src/thorin/transform/mangle.cpp index 0dcc0d909..e6523f21f 100644 --- a/src/thorin/transform/mangle.cpp +++ b/src/thorin/transform/mangle.cpp @@ -107,7 +107,7 @@ Continuation* Mangler::mangle() { Continuation* Mangler::mangle_head(Continuation* old_continuation) { assert(!def2def_.contains(old_continuation)); assert(old_continuation->has_body()); - Continuation* new_continuation = old_continuation->stub(); + Continuation* new_continuation = old_continuation->mangle_stub(); def2def_[old_continuation] = new_continuation; for (size_t i = 0, e = old_continuation->num_params(); i != e; ++i) diff --git a/src/thorin/transform/mangle.h b/src/thorin/transform/mangle.h index 52621968d..162da6071 100644 --- a/src/thorin/transform/mangle.h +++ b/src/thorin/transform/mangle.h @@ -30,7 +30,6 @@ class Mangler { const Scope& scope_; Defs args_; Defs lift_; - Type2Type type2type_; Continuation* old_entry_; Continuation* new_entry_; DefSet defs_; diff --git a/src/thorin/type.cpp b/src/thorin/type.cpp index f8cc33751..b317806fe 100644 --- a/src/thorin/type.cpp +++ b/src/thorin/type.cpp @@ -11,15 +11,23 @@ namespace thorin { -Type::Type(TypeTable& table, int tag, Types ops) - : table_(&table) - , tag_(tag) - , ops_(ops.size()) -{ - for (size_t i = 0, e = num_ops(); i != e; ++i) { - if (auto op = ops[i]) - set(i, op); - } +Type::Type(World& w, NodeTag tag, Defs args, Debug dbg) : Type(w, tag, w.star(), args, dbg) {} +Type::Type(World& w, NodeTag tag, size_t size, Debug dbg) : Type(w, tag, w.star(), size, dbg) {} + +Array types2defs(ArrayRef types) { + Array defs(types.size()); + size_t i = 0; + for (auto type : types) + defs[i++] = type->as(); + return defs; +} + +Array defs2types(ArrayRef defs) { + Array types(defs.size()); + size_t i = 0; + for (auto type : defs) + types[i++] = type->as(); + return types; } //------------------------------------------------------------------------------ @@ -28,34 +36,33 @@ Type::Type(TypeTable& table, int tag, Types ops) * rebuild */ -const Type* NominalType::rebuild(TypeTable&, Types) const { +const Type* NominalType::rebuild(World& w, const Type* t, Defs o) const { THORIN_UNREACHABLE; - return this; } -const Type* BottomType ::rebuild(TypeTable& t, Types ) const { return t.bottom_type(); } -const Type* ClosureType ::rebuild(TypeTable& t, Types o) const { return t.closure_type(o); } -const Type* DefiniteArrayType ::rebuild(TypeTable& t, Types o) const { return t.definite_array_type(o[0], dim()); } -const Type* FnType ::rebuild(TypeTable& t, Types o) const { return t.fn_type(o); } -const Type* FrameType ::rebuild(TypeTable& t, Types ) const { return t.frame_type(); } -const Type* IndefiniteArrayType::rebuild(TypeTable& t, Types o) const { return t.indefinite_array_type(o[0]); } -const Type* MemType ::rebuild(TypeTable& t, Types ) const { return t.mem_type(); } -const Type* PrimType ::rebuild(TypeTable& t, Types ) const { return t.prim_type(primtype_tag(), length()); } -const Type* PtrType ::rebuild(TypeTable& t, Types o) const { return t.ptr_type(o.front(), length(), device(), addr_space()); } -const Type* TupleType ::rebuild(TypeTable& t, Types o) const { return t.tuple_type(o); } +const Type* BottomType ::rebuild(World& w, const Type* t, Defs o) const { return w.bottom_type(); } +const Type* ClosureType ::rebuild(World& w, const Type* t, Defs o) const { return w.closure_type(defs2types(o)); } +const Type* DefiniteArrayType ::rebuild(World& w, const Type* t, Defs o) const { return w.definite_array_type(o[0]->as(), dim()); } +const Type* FnType ::rebuild(World& w, const Type* t, Defs o) const { return w.fn_type(defs2types(o)); } +const Type* FrameType ::rebuild(World& w, const Type* t, Defs o) const { return w.frame_type(); } +const Type* IndefiniteArrayType::rebuild(World& w, const Type* t, Defs o) const { return w.indefinite_array_type(o[0]->as()); } +const Type* MemType ::rebuild(World& w, const Type* t, Defs o) const { return w.mem_type(); } +const Type* PrimType ::rebuild(World& w, const Type* t, Defs o) const { return w.prim_type(primtype_tag(), length()); } +const Type* PtrType ::rebuild(World& w, const Type* t, Defs o) const { return w.ptr_type(o[0]->as(), length(), device(), addr_space()); } +const Type* TupleType ::rebuild(World& w, const Type* t, Defs o) const { return w.tuple_type(defs2types(o)); } /* * stub */ -const NominalType* StructType::stub(TypeTable& to) const { - auto type = to.struct_type(name(), num_ops()); +StructType* StructType::stub(World& world, const Type*) const { + auto type = world.struct_type(name(), num_ops()); std::copy(op_names_.begin(), op_names_.end(), type->op_names().begin()); return type; } -const NominalType* VariantType::stub(TypeTable& to) const { - auto type = to.variant_type(name(), num_ops()); +VariantType* VariantType::stub(World& world, const Type*) const { + auto type = world.variant_type(name(), num_ops()); std::copy(op_names_.begin(), op_names_.end(), type->op_names().begin()); return type; } @@ -64,8 +71,8 @@ const NominalType* VariantType::stub(TypeTable& to) const { const VectorType* VectorType::scalarize() const { if (auto ptr = isa()) - return table().ptr_type(ptr->pointee()); - return table().prim_type(as()->primtype_tag()); + return world().ptr_type(ptr->pointee()); + return world().prim_type(as()->primtype_tag()); } bool FnType::is_returning() const { @@ -85,7 +92,7 @@ bool FnType::is_returning() const { } bool VariantType::has_payload() const { - return !std::all_of(ops().begin(), ops().end(), is_type_unit); + return !std::all_of(types().begin(), types().end(), is_type_unit); } bool use_lea(const Type* type) { return type->isa() || type->isa(); } @@ -96,15 +103,6 @@ bool use_lea(const Type* type) { return type->isa() || type->isagid())); - return seed; -} - hash_t PtrType::vhash() const { return hash_combine(VectorType::vhash(), (hash_t)device(), (hash_t)addr_space()); } @@ -115,15 +113,7 @@ hash_t PtrType::vhash() const { * equal */ -bool Type::equal(const Type* other) const { - if (is_nominal()) - return this == other; - if (tag() == other->tag() && num_ops() == other->num_ops()) - return std::equal(ops().begin(), ops().end(), other->ops().begin()); - return false; -} - -bool PtrType::equal(const Type* other) const { +bool PtrType::equal(const Def* other) const { if (!VectorType::equal(other)) return false; auto ptr = other->as(); @@ -186,62 +176,46 @@ Stream& Type::stream(Stream& s) const { //------------------------------------------------------------------------------ -TypeTable::TypeTable() - : unit_ (insert(*this, Types())) - , fn0_ (insert(*this, Types())) - , bottom_ty_(insert(*this)) - , mem_ (insert(*this)) - , frame_ (insert(*this)) +TypeTable::TypeTable(World& world) + : world_(world) + , star_ (world.put((world))) + , unit_ (world.put(world, Defs(), Debug())) + , fn0_ (world.put(world, Defs(), Node_FnType, Debug())) + , bottom_ty_(world.put(world, Debug())) + , mem_ (world.put(world, Debug())) + , frame_ (world.put(world, Debug())) { #define THORIN_ALL_TYPE(T, M) \ - primtypes_[PrimType_##T - Begin_PrimType] = insert(*this, PrimType_##T, 1); + primtypes_[PrimType_##T - Begin_PrimType] = world.make(world, PrimType_##T, 1, Debug()); #include "thorin/tables/primtypetable.h" } -const Type* TypeTable::tuple_type(Types ops) { - return ops.size() == 1 ? ops.front() : insert(*this, ops); +const Type* World::tuple_type(Types ops) { + return ops.size() == 1 ? ops.front()->as() : make(*this, types2defs(ops), Debug()); } -const StructType* TypeTable::struct_type(Symbol name, size_t size) { - auto type = new StructType(*this, name, size, types_.size()); - const auto& p = types_.insert(type); - assert_unused(p.second && "hash/equal broken"); - return type; +StructType* World::struct_type(Symbol name, size_t size) { + return put(*this, name, size, Debug()); } -const VariantType* TypeTable::variant_type(Symbol name, size_t size) { - auto type = new VariantType(*this, name, size, types_.size()); - const auto& p = types_.insert(type); - assert_unused(p.second && "hash/equal broken"); - return type; +VariantType* World::variant_type(Symbol name, size_t size) { + return put(*this, name, size, Debug()); } -const PrimType* TypeTable::prim_type(PrimTypeTag tag, size_t length) { +const PrimType* World::prim_type(PrimTypeTag tag, size_t length) { size_t i = tag - Begin_PrimType; assert(i < (size_t) Num_PrimTypes); - return length == 1 ? primtypes_[i] : insert(*this, tag, length); + return length == 1 ? types_.primtypes_[i] : make(*this, tag, length, Debug()); } -const PtrType* TypeTable::ptr_type(const Type* pointee, size_t length, int32_t device, AddrSpace addr_space) { - return insert(*this, pointee, length, device, addr_space); +const PtrType* World::ptr_type(const Type* pointee, size_t length, int32_t device, AddrSpace addr_space) { + return make(*this, pointee, length, device, addr_space, Debug()); } -const FnType* TypeTable::fn_type(Types args) { return insert(*this, args); } -const ClosureType* TypeTable::closure_type(Types args) { return insert(*this, args); } -const DefiniteArrayType* TypeTable::definite_array_type(const Type* elem, u64 dim) { return insert(*this, elem, dim); } -const IndefiniteArrayType* TypeTable::indefinite_array_type(const Type* elem) { return insert(*this, elem); } - -template -const T* TypeTable::insert(Args&&... args) { - T t(std::forward(args)...); - auto it = types_.find(&t); - if (it != types_.end()) - return (*it)->template as(); - auto new_t = new T(std::move(t)); - new_t->gid_ = types_.size(); - types_.emplace(new_t); - return new_t; -} +const FnType* World::fn_type(Types args) { return make(*this, types2defs(args), Node_FnType, Debug()); } +const ClosureType* World::closure_type(Types args) { return make(*this, types2defs(args), Debug()); } +const DefiniteArrayType* World::definite_array_type(const Type* elem, u64 dim) { return make(*this, elem, dim, Debug()); } +const IndefiniteArrayType* World::indefinite_array_type(const Type* elem) { return make(*this, elem, Debug()); } //------------------------------------------------------------------------------ diff --git a/src/thorin/type.h b/src/thorin/type.h index 2ce899290..84383e250 100644 --- a/src/thorin/type.h +++ b/src/thorin/type.h @@ -1,6 +1,7 @@ #ifndef THORIN_TYPE_H #define THORIN_TYPE_H +#include "thorin/def.h" #include "thorin/enums.h" #include "thorin/util/hash.h" #include "thorin/util/cast.h" @@ -15,174 +16,168 @@ class Type; using Types = ArrayRef; /// Base class for all \p Type%s. -class Type : public RuntimeCast, public Streamable { +class Type : public Def, public Streamable { protected: - Type(TypeTable& table, int tag, Types ops); - - void set(size_t i, const Type* type) { - ops_[i] = type; - order_ = std::max(order_, type->order()); - } + /// Constructor for a @em structural Type. + Type(World& w, NodeTag tag, const Type* type, Defs args, Debug dbg) : Def(w, tag, type, args, dbg) {} + Type(World& w, NodeTag tag, Defs args, Debug dbg); + /// Constructor for a @em nom Type. + Type(World& w, NodeTag tag, const Type* type, size_t size, Debug dbg) : Def(w, tag, type, size, dbg) {} + Type(World& w, NodeTag tag, size_t size, Debug dbg); public: - int tag() const { return tag_; } - TypeTable& table() const { return *table_; } - - Types ops() const { return ops_; } - const Type* op(size_t i) const { return ops()[i]; } - size_t num_ops() const { return ops_.size(); } - bool empty() const { return ops_.empty(); } - - bool is_nominal() const { return nominal_; } ///< A nominal @p Type is always different from each other @p Type. - int order() const { return order_; } - size_t gid() const { return gid_; } - hash_t hash() const { return hash_ == 0 ? hash_ = vhash() : hash_; } - virtual bool equal(const Type*) const; - virtual const Type* rebuild(TypeTable&, Types) const = 0; + int order() const override { return order_; } Stream& stream(Stream&) const; - void dump() const; + std::vector filter_type_ops() const { + std::vector type_ops; + for (auto& op : ops()) { + if (auto t = op->isa()) + type_ops.push_back(t); + } + return type_ops; + } protected: - virtual hash_t vhash() const; - - mutable hash_t hash_ = 0; - mutable bool nominal_ = false; int order_ = 0; - size_t gid_; + friend class World; +}; -private: - mutable TypeTable* table_; +class Star : public Type { +protected: + explicit Star(World& w) : Type(w, Node_Star, nullptr, 0, {}) { + set_type(this); + } + + friend class World; +}; - int tag_; - thorin::Array ops_; +Array types2defs(ArrayRef types); +Array defs2types(ArrayRef types); - friend TypeTable; +template +class TypeOpsMixin { +public: + Types types() const { + Defs defs = static_cast(this)->ops(); + const Def* const* ptr = defs.begin(); + auto ptr2 = reinterpret_cast(ptr); + auto types = Types(ptr2, defs.size()); + return types; + } }; /// Type of a tuple (structurally typed). -class TupleType : public Type { +class TupleType : public Type, public TypeOpsMixin { private: - TupleType(TypeTable& table, Types ops) - : Type(table, Node_TupleType, ops) + TupleType(World& world, Defs ops, Debug dbg) + : Type(world, Node_TupleType, ops, dbg) {} public: - const Type* rebuild(TypeTable&, Types) const override; - - friend class TypeTable; + const Type* rebuild(World&, const Type*, Defs) const override; + friend class World; }; /// Base class for nominal types (types that have /// a name that uniquely identifies them). class NominalType : public Type { protected: - NominalType(TypeTable& table, int tag, Symbol name, size_t size, size_t gid) - : Type(table, tag, thorin::Array(size)) + NominalType(World& world, NodeTag tag, Symbol name, size_t size, Debug dbg) + : Type(world, tag, size, dbg) , name_(name) , op_names_(size) - { - nominal_ = true; - gid_ = gid; - } + {} Symbol name_; Array op_names_; private: - const Type* rebuild(TypeTable&, Types) const override; + const Type* rebuild(World&, const Type*, Defs) const override; public: Symbol name() const { return name_; } Symbol op_name(size_t i) const { return op_names_[i]; } - void set(size_t i, const Type* type) const { - return const_cast(this)->Type::set(i, type); - } void set_op_name(size_t i, Symbol name) const { const_cast(this)->op_names_[i] = name; } Array& op_names() const { return const_cast(this)->op_names_; } - - /// Recreates a fresh new nominal type of the - /// same kind with the same number of operands, - /// initially all unset. - virtual const NominalType* stub(TypeTable&) const = 0; }; -class StructType : public NominalType { +class StructType : public NominalType, public TypeOpsMixin { private: - StructType(TypeTable& table, Symbol name, size_t size, size_t gid) - : NominalType(table, Node_StructType, name, size, gid) + StructType(World& world, Symbol name, size_t size, Debug dbg) + : NominalType(world, Node_StructType, name, size, dbg) {} public: - const NominalType* stub(TypeTable&) const override; + virtual StructType* stub(World&, const Type*) const override; - friend class TypeTable; + friend class World; }; -class VariantType : public NominalType { +class VariantType : public NominalType, public TypeOpsMixin { private: - VariantType(TypeTable& table, Symbol name, size_t size, size_t gid) - : NominalType(table, Node_VariantType, name, size, gid) + VariantType(World& world, Symbol name, size_t size, Debug dbg) + : NominalType(world, Node_VariantType, name, size, dbg) {} public: - const NominalType* stub(TypeTable&) const override; + virtual VariantType* stub(World&, const Type*) const override; bool has_payload() const; - friend class TypeTable; + friend class World; }; /// The type of the memory monad. class MemType : public Type { private: - MemType(TypeTable& table) - : Type(table, Node_MemType, {}) + MemType(World& world, Debug dbg) + : Type(world, Node_MemType, Defs(), dbg) {} - const Type* rebuild(TypeTable&, Types) const override; + const Type* rebuild(World&, const Type*, Defs) const override; - friend class TypeTable; + friend class World; }; /// The type of App nodes. class BottomType : public Type { private: - BottomType(TypeTable& table) - : Type(table, Node_BotType, {}) + BottomType(World& world, Debug dbg) + : Type(world, Node_BotType, Defs(), dbg) {} - const Type* rebuild(TypeTable& to, Types ops) const override; + const Type* rebuild(World&, const Type*, Defs) const override; - friend class TypeTable; + friend class World; }; /// The type of a stack frame. class FrameType : public Type { private: - FrameType(TypeTable& table) - : Type(table, Node_FrameType, {}) + FrameType(World& world, Debug dbg) + : Type(world, Node_FrameType, Defs(), dbg) {} - const Type* rebuild(TypeTable&, Types) const override; + const Type* rebuild(World&, const Type*, Defs) const override; - friend class TypeTable; + friend class World; }; /// Base class for all SIMD types. class VectorType : public Type { protected: - VectorType(TypeTable& table, int tag, Types ops, size_t length) - : Type(table, tag, ops) + VectorType(World& world, NodeTag tag, Defs ops, size_t length, Debug dbg) + : Type(world, tag, ops, dbg) , length_(length) {} hash_t vhash() const override { return hash_combine(Type::vhash(), length()); } - bool equal(const Type* other) const override { - return Type::equal(other) && this->length() == other->as()->length(); + bool equal(const Def* other) const override { + return Def::equal(other) && this->length() == other->as()->length(); } public: @@ -202,15 +197,15 @@ inline size_t vector_length(const Type* type) { return type->as()->l /// Primitive type. class PrimType : public VectorType { private: - PrimType(TypeTable& table, PrimTypeTag tag, size_t length) - : VectorType(table, (int) tag, {}, length) + PrimType(World& world, PrimTypeTag tag, size_t length, Debug dbg) + : VectorType(world, (NodeTag) tag, Defs(), length, dbg) {} public: PrimTypeTag primtype_tag() const { return (PrimTypeTag) tag(); } - const Type* rebuild(TypeTable&, Types) const override; + const Type* rebuild(World&, const Type*, Defs) const override; - friend class TypeTable; + friend class World; }; inline bool is_primtype (const Type* t) { return thorin::is_primtype(t->tag()); } @@ -238,30 +233,30 @@ enum class AddrSpace : uint32_t { }; /// Pointer type. -class PtrType : public VectorType { +class PtrType : public VectorType, public TypeOpsMixin { private: - PtrType(TypeTable& table, const Type* pointee, size_t length, int32_t device, AddrSpace addr_space) - : VectorType(table, Node_PtrType, {pointee}, length) + PtrType(World& world, const Type* pointee, size_t length, int32_t device, AddrSpace addr_space, Debug dbg) + : VectorType(world, Node_PtrType, {pointee}, length, dbg) , addr_space_(addr_space) , device_(device) {} public: - const Type* pointee() const { return op(0); } + const Type* pointee() const { return op(0)->as(); } AddrSpace addr_space() const { return addr_space_; } int32_t device() const { return device_; } bool is_host_device() const { return device_ == -1; } hash_t vhash() const override; - bool equal(const Type* other) const override; + bool equal(const Def* other) const override; private: - const Type* rebuild(TypeTable&, Types) const override; + const Type* rebuild(World&, const Type*, Defs) const override; AddrSpace addr_space_; int32_t device_; - friend class TypeTable; + friend class World; }; /// Returns true if the given type is small enough to fit in a closure environment @@ -269,10 +264,10 @@ inline bool is_thin(const Type* type) { return type->isa() || type->isa() || is_type_unit(type); } -class FnType : public Type { +class FnType : public Type, public TypeOpsMixin { protected: - FnType(TypeTable& table, Types ops, int tag = Node_FnType) - : Type(table, tag, ops) + FnType(World& world, Defs ops, NodeTag tag, Debug dbg) + : Type(world, tag, ops, dbg) { ++order_; } @@ -282,15 +277,15 @@ class FnType : public Type { bool is_returning() const; private: - const Type* rebuild(TypeTable&, Types) const override; + const Type* rebuild(World&, const Type*, Defs) const override; - friend class TypeTable; + friend class World; }; class ClosureType : public FnType { private: - ClosureType(TypeTable& table, Types ops) - : FnType(table, ops, Node_ClosureType) + ClosureType(World& world, Defs ops, Debug dbg) + : FnType(world, ops, Node_ClosureType, dbg) { inner_order_ = order_; order_ = 0; @@ -298,155 +293,84 @@ class ClosureType : public FnType { public: int inner_order() const { return inner_order_; } - const Type* rebuild(TypeTable&, Types) const override; + const Type* rebuild(World&, const Type*, Defs) const override; private: int inner_order_; - friend class TypeTable; + friend class World; }; //------------------------------------------------------------------------------ -class ArrayType : public Type { +class ArrayType : public Type, public TypeOpsMixin { protected: - ArrayType(TypeTable& table, int tag, const Type* elem_type) - : Type(table, tag, {elem_type}) + ArrayType(World& world, NodeTag tag, const Type* elem_type, Debug dbg) + : Type(world, tag, {elem_type}, dbg) {} public: - const Type* elem_type() const { return op(0); } + const Type* elem_type() const { return op(0)->as(); } }; class IndefiniteArrayType : public ArrayType { public: - IndefiniteArrayType(TypeTable& table, const Type* elem_type) - : ArrayType(table, Node_IndefiniteArrayType, elem_type) + IndefiniteArrayType(World& world, const Type* elem_type, Debug dbg) + : ArrayType(world, Node_IndefiniteArrayType, elem_type, dbg) {} private: - const Type* rebuild(TypeTable&, Types) const override; + const Type* rebuild(World&, const Type*, Defs) const override; - friend class TypeTable; + friend class World; }; class DefiniteArrayType : public ArrayType { public: - DefiniteArrayType(TypeTable& table, const Type* elem_type, u64 dim) - : ArrayType(table, Node_DefiniteArrayType, elem_type) + DefiniteArrayType(World& world, const Type* elem_type, u64 dim, Debug dbg) + : ArrayType(world, Node_DefiniteArrayType, elem_type, dbg) , dim_(dim) {} u64 dim() const { return dim_; } hash_t vhash() const override { return hash_combine(Type::vhash(), dim()); } - bool equal(const Type* other) const override { - return Type::equal(other) && this->dim() == other->as()->dim(); + bool equal(const Def* other) const override { + return Def::equal(other) && this->dim() == other->as()->dim(); } private: - const Type* rebuild(TypeTable&, Types) const override; + const Type* rebuild(World&, const Type*, Defs) const override; u64 dim_; - friend class TypeTable; + friend class World; }; bool use_lea(const Type*); //------------------------------------------------------------------------------ -/// Container for all types. Types are hashed and can be compared using pointer equality. class TypeTable { -private: - struct TypeHash { - static hash_t hash(const Type* t) { return t->hash(); } - static bool eq(const Type* t1, const Type* t2) { return t2->equal(t1); } - static const Type* sentinel() { return (const Type*)(1); } - }; - - typedef thorin::HashSet TypeSet; - public: - TypeTable(); - - const Type* tuple_type(Types ops); - const TupleType* unit() { return unit_; } ///< Returns unit, i.e., an empty @p TupleType. - const VariantType* variant_type(Symbol name, size_t size); - const StructType* struct_type(Symbol name, size_t size); - -#define THORIN_ALL_TYPE(T, M) \ - const PrimType* type_##T(size_t length = 1) { return prim_type(PrimType_##T, length); } -#include "thorin/tables/primtypetable.h" - const PrimType* prim_type(PrimTypeTag tag, size_t length = 1); - const BottomType* bottom_type() const { return bottom_ty_; } - const MemType* mem_type() const { return mem_; } - const FrameType* frame_type() const { return frame_; } - const PtrType* ptr_type(const Type* pointee, size_t length = 1, int32_t device = -1, AddrSpace addr_space = AddrSpace::Generic); - const FnType* fn_type() { return fn0_; } ///< Returns an empty @p FnType. - const FnType* fn_type(Types args); - const ClosureType* closure_type(Types args); - const DefiniteArrayType* definite_array_type(const Type* elem, u64 dim); - const IndefiniteArrayType* indefinite_array_type(const Type* elem); - - const TypeSet& types() const { return types_; } - - friend void swap(TypeTable& t1, TypeTable& t2) { - using std::swap; - swap(t1.types_, t2.types_); - swap(t1.unit_, t2.unit_); - swap(t1.fn0_, t2.fn0_); - swap(t1.bottom_ty_, t2.bottom_ty_); - swap(t1.mem_, t2.mem_); - swap(t1.frame_, t2.frame_); - std::swap_ranges(t1.primtypes_, t1.primtypes_ + Num_PrimTypes, t2.primtypes_); - - t1.fix(); - t2.fix(); - } - -private: - void fix() { - for (auto type : types_) - type->table_ = this; - } - - template - const T* insert(Args&&... args); + explicit TypeTable(World& world); private: - TypeSet types_; + World& world_; + const Type* star_; const TupleType* unit_; ///< tuple(). const FnType* fn0_; const BottomType* bottom_ty_; const MemType* mem_; const FrameType* frame_; const PrimType* primtypes_[Num_PrimTypes]; -}; - -//------------------------------------------------------------------------------ - -template -struct GIDLt { - bool operator()(T a, T b) const { return a->gid() < b->gid(); } -}; -template -struct GIDHash { - static hash_t hash(T n) { return thorin::murmur3(n->gid()); } - static bool eq(T a, T b) { return a == b; } - static T sentinel() { return T(1); } + friend class World; }; -template -using GIDMap = thorin::HashMap>; -template -using GIDSet = thorin::HashSet>; +//------------------------------------------------------------------------------ -template -using TypeMap = GIDMap; -using Type2Type = TypeMap; -using TypeSet = GIDSet; +inline bool is_mem (const Def* def) { return def->type()->isa(); } //------------------------------------------------------------------------------ diff --git a/src/thorin/world.cpp b/src/thorin/world.cpp index a45ac14a4..1550493b3 100644 --- a/src/thorin/world.cpp +++ b/src/thorin/world.cpp @@ -42,16 +42,12 @@ namespace thorin { * constructor and destructor */ -World::World(const std::string& name) { +World::World(const std::string& name) : types_(TypeTable(*this)) { data_.name_ = name; data_.branch_ = continuation(fn_type({type_bool(), fn_type(), fn_type()}), Intrinsic::Branch, {"br"}); data_.end_scope_ = continuation(fn_type(), Intrinsic::EndScope, {"end_scope"}); } -World::~World() { - for (auto def : data_.defs_) delete def; -} - const Def* World::variant_index(const Def* value, Debug dbg) { if (auto variant = value->isa()) return literal_qu64(variant->index(), dbg); @@ -59,7 +55,7 @@ const Def* World::variant_index(const Def* value, Debug dbg) { } const Def* World::variant_extract(const Def* value, size_t index, Debug dbg) { - auto type = value->type()->as()->op(index); + auto type = value->type()->as()->op(index)->as(); if (auto variant = value->isa()) return variant->index() == index ? variant->value() : bottom(type); return cse(new VariantExtract(*this, type, value, index, dbg)); @@ -500,7 +496,7 @@ const Def* World::convert(const Type* dst_type, const Def* src, Debug dbg) { Array new_tuple(dst_tuple_type->num_ops()); for (size_t i = 0, e = new_tuple.size(); i != e; ++i) - new_tuple[i] = convert(dst_type->op(i), extract(src, i, dbg), dbg); + new_tuple[i] = convert(dst_tuple_type->types()[i], extract(src, i, dbg), dbg); return tuple(new_tuple, dbg); } @@ -730,13 +726,13 @@ const Def* World::insert(const Def* agg, const Def* index, const Def* value, Deb } else if (auto tuple_type = agg->type()->isa()) { Array args(tuple_type->num_ops()); size_t i = 0; - for (auto type : tuple_type->ops()) + for (auto type : tuple_type->types()) args[i++] = agg->isa() ? bottom(type, dbg) : top(type, dbg); agg = tuple(args, dbg); } else if (auto struct_type = agg->type()->isa()) { Array args(struct_type->num_ops()); size_t i = 0; - for (auto type : struct_type->ops()) + for (auto type : struct_type->types()) args[i++] = agg->isa() ? bottom(type, dbg) : top(type, dbg); agg = struct_agg(struct_type, args, dbg); } @@ -824,7 +820,7 @@ const Def* World::transcendental(MathOpTag tag, const Def* arg, Debug dbg, F&& f THORIN_UNREACHABLE; } } - return cse(new MathOp(tag, *this, arg->type(), { arg }, dbg)); + return cse(new MathOp(*this, tag, arg->type(), { arg }, dbg)); } template @@ -846,7 +842,7 @@ const Def* World::transcendental(MathOpTag tag, const Def* left, const Def* righ THORIN_UNREACHABLE; } } - return cse(new MathOp(tag, *this, left->type(), { left, right }, dbg)); + return cse(new MathOp(*this, tag, left->type(), { left, right }, dbg)); } template @@ -1102,15 +1098,7 @@ const Def* World::run(const Def* def, Debug dbg) { */ Continuation* World::continuation(const FnType* fn, Continuation::Attributes attributes, Debug dbg) { - auto cont = put(*this, fn, attributes, dbg); - - size_t i = 0; - for (auto op : fn->ops()) { - auto p = param(op, cont, i++, dbg); - cont->params_.emplace_back(p); - } - - return cont; + return put(*this, fn, attributes, dbg); } Continuation* World::match(const Type* type, size_t num_patterns) { @@ -1122,8 +1110,8 @@ Continuation* World::match(const Type* type, size_t num_patterns) { return continuation(fn_type(arg_types), Intrinsic::Match, {"match"}); } -const Param* World::param(const Type* type, Continuation* continuation, size_t index, Debug dbg) { - auto param = new Param(*this, type, continuation, index, dbg); +const Param* World::param(const Type* type, const Continuation* continuation, size_t index, Debug dbg) { + auto param = cse(new Param(*this, type, continuation, index, dbg)); #if THORIN_ENABLE_CHECKS if (state_.breakpoints.contains(param->gid())) THORIN_BREAK; #endif diff --git a/src/thorin/world.h b/src/thorin/world.h index 3a0d11be0..e6f4d98f5 100644 --- a/src/thorin/world.h +++ b/src/thorin/world.h @@ -43,7 +43,7 @@ enum class LogLevel { Debug, Verbose, Info, Warn, Error }; * All worlds are completely independent from each other. * This is particular useful for multi-threading. */ -class World : public TypeTable, public Streamable { +class World : public Streamable { public: struct SeaHash { static hash_t hash(const Def* def) { return def->hash(); } @@ -78,7 +78,6 @@ class World : public TypeTable, public Streamable { stream_ = other.stream_; state_ = other.state_; } - ~World(); /// @name manage global identifier - a unique number for each Def //@{ @@ -90,12 +89,35 @@ class World : public TypeTable, public Streamable { //@{ bool empty() { return data_.externals_.empty(); } const Externals& externals() const { return data_.externals_; } - void make_external(Continuation* cont) { data_.externals_.emplace(cont->unique_name(), cont); } - void make_internal(Continuation* cont) { data_.externals_.erase(cont->unique_name()); } + void make_external(Continuation* cont) { assert(&cont->world() == this); data_.externals_.emplace(cont->unique_name(), cont); } + void make_internal(Continuation* cont) { assert(&cont->world() == this); data_.externals_.erase(cont->unique_name()); } bool is_external(const Continuation* cont) { return data_.externals_.contains(cont->unique_name()); } Continuation* lookup(const std::string& name) { return data_.externals_.lookup(name).value_or(nullptr); } //@} + // types + + const Type* star() { return types_.star_; } + + const Type* tuple_type(Types ops); + const TupleType* unit_type() { return tuple_type({})->as(); } ///< Returns unit, i.e., an empty @p TupleType. + VariantType* variant_type(Symbol name, size_t size); + StructType* struct_type(Symbol name, size_t size); + +#define THORIN_ALL_TYPE(T, M) \ + const PrimType* type_##T(size_t length = 1) { return prim_type(PrimType_##T, length); } +#include "thorin/tables/primtypetable.h" + const PrimType* prim_type(PrimTypeTag tag, size_t length = 1); + const BottomType* bottom_type() { return make(*this, Debug()); } + const MemType* mem_type() { return make(*this, Debug()); } + const FrameType* frame_type() { return make(*this, Debug()); } + const PtrType* ptr_type(const Type* pointee, size_t length = 1, int32_t device = -1, AddrSpace addr_space = AddrSpace::Generic); + const FnType* fn_type() { return fn_type({}); } ///< Returns an empty @p FnType. + const FnType* fn_type(Types args); + const ClosureType* closure_type(Types args); + const DefiniteArrayType* definite_array_type(const Type* elem, u64 dim); + const IndefiniteArrayType* indefinite_array_type(const Type* elem); + // literals #define THORIN_ALL_TYPE(T, M) \ @@ -307,7 +329,7 @@ class World : public TypeTable, public Streamable { //@} private: - const Param* param(const Type* type, Continuation* continuation, size_t index, Debug dbg); + const Param* param(const Type* type, const Continuation*, size_t index, Debug dbg); const App* app(const Def* callee, const Defs args, Debug dbg = {}); const Def* try_fold_aggregate(const Aggregate*); template const Def* transcendental(MathOpTag, const Def*, Debug, F&&); @@ -317,6 +339,11 @@ class World : public TypeTable, public Streamable { //@{ template const T* cse(const T* primop) { return cse_base(primop)->template as(); } const Def* cse_base(const Def*); + template + const T* make(Args&&... args) { + auto def = new T(args...); + return cse(def); + } template T* put(Args&&... args) { @@ -349,15 +376,19 @@ class World : public TypeTable, public Streamable { Continuation* end_scope_; } data_; + TypeTable types_; + std::shared_ptr stream_; friend class Mangler; friend class Cleaner; friend class Continuation; + friend class Param; friend class Filter; friend class App; friend class Importer; friend class Thorin; + friend class TypeTable; }; class Thorin { From afd08d77ba6298e0311d7b1ce3a67f05311069eb Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Mon, 23 Jan 2023 17:48:05 +0100 Subject: [PATCH 159/342] eliminate commented out code from the importer --- src/thorin/transform/importer.cpp | 72 ------------------------------- src/thorin/transform/importer.h | 1 - 2 files changed, 73 deletions(-) diff --git a/src/thorin/transform/importer.cpp b/src/thorin/transform/importer.cpp index a1ff8bffa..4691e9795 100644 --- a/src/thorin/transform/importer.cpp +++ b/src/thorin/transform/importer.cpp @@ -2,32 +2,6 @@ namespace thorin { -/*const Type* Importer::import(const Type* otype) { - if (auto ntype = type_old2new_.lookup(otype)) { - assert(&(*ntype)->table() == &world()); - return *ntype; - } - size_t size = otype->num_ops(); - - if (auto nominal_type = otype->isa()) { - auto ntype = nominal_type->stub(world()); - type_old2new_[otype] = ntype; - for (size_t i = 0; i != size; ++i) - ntype->set(i, import(otype->op(i))); - return ntype; - } - - Array nops(size); - for (size_t i = 0; i != size; ++i) - nops[i] = import(otype->op(i)); - - auto ntype = otype->rebuild(world(), nops); - type_old2new_[otype] = ntype; - assert(&ntype->table() == &world()); - - return ntype; -}*/ - const Def* Importer::import(const Def* odef) { if (auto ndef = def_old2new_.lookup(odef)) { assert(&(*ndef)->world() == &world()); @@ -41,44 +15,6 @@ const Def* Importer::import(const Def* odef) { auto ntype = import(odef->type())->as(); - /*if (auto oparam = odef->isa()) { - import(oparam->continuation())->as_nom(); - auto nparam = def_old2new_[oparam]; - assert(nparam && &nparam->world() == &world()); - return nparam; - } - - if (auto ofilter = odef->isa()) { - Array new_conditions(ofilter->num_ops()); - for (size_t i = 0, e = ofilter->size(); i != e; ++i) - new_conditions[i] = import(ofilter->condition(i)); - auto nfilter = world().filter(new_conditions, ofilter->debug()); - return nfilter; - } - - Continuation* ncontinuation = nullptr; - if (auto ocontinuation = odef->isa_nom()) { // create stub in new world - assert(!ocontinuation->dead_); - // TODO maybe we want to deal with intrinsics in a more streamlined way - if (ocontinuation == ocontinuation->world().branch()) - return def_old2new_[ocontinuation] = world().branch(); - if (ocontinuation == ocontinuation->world().end_scope()) - return def_old2new_[ocontinuation] = world().end_scope(); - auto npi = import(ocontinuation->type())->as(); - ncontinuation = world().continuation(npi, ocontinuation->attributes(), ocontinuation->debug_history()); - assert(&ncontinuation->world() == &world()); - assert(&npi->world() == &world()); - for (size_t i = 0, e = ocontinuation->num_params(); i != e; ++i) { - ncontinuation->param(i)->set_name(ocontinuation->param(i)->debug_history().name); - def_old2new_[ocontinuation->param(i)] = ncontinuation->param(i); - } - - def_old2new_[ocontinuation] = ncontinuation; - - if (ocontinuation->is_external()) - world().make_external(ncontinuation); - }*/ - Def* stub = nullptr; if (odef->isa_nom()) { stub = odef->stub(world(), ntype); @@ -102,14 +38,6 @@ const Def* Importer::import(const Def* odef) { stub->rebuild_from(odef, nops); return stub; } - - /*assert(ncontinuation && &ncontinuation->world() == &world()); - auto napp = nops[0]->isa(); - if (napp) - ncontinuation->set_body(napp); - ncontinuation->set_filter(nops[1]->as()); - ncontinuation->verify(); - return ncontinuation;*/ } } diff --git a/src/thorin/transform/importer.h b/src/thorin/transform/importer.h index 279dadeaf..10941b915 100644 --- a/src/thorin/transform/importer.h +++ b/src/thorin/transform/importer.h @@ -21,7 +21,6 @@ class Importer { } World& world() { return dst; } - //const Type* import(const Type*); const Def* import(const Def*); bool todo() const { return todo_; } From fc366745a796ec687d957a0a0ce6372d4d0af48e Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Mon, 23 Jan 2023 21:26:08 +0100 Subject: [PATCH 160/342] simplify op_name() --- src/thorin/primop.cpp | 25 +++---------------------- src/thorin/primop.h | 3 --- 2 files changed, 3 insertions(+), 25 deletions(-) diff --git a/src/thorin/primop.cpp b/src/thorin/primop.cpp index 23d5ba859..6c5ab6b26 100644 --- a/src/thorin/primop.cpp +++ b/src/thorin/primop.cpp @@ -246,32 +246,13 @@ const Def* IndefiniteArray::rebuild(World& w, const Type* t, Defs o) const { const char* Def::op_name() const { switch (tag()) { +#define THORIN_GLUE(pre, next) #define THORIN_NODE(op, abbr) case Node_##op: return #abbr; -#include "thorin/tables/nodetable.h" - default: THORIN_UNREACHABLE; - } -} - -const char* ArithOp::op_name() const { - switch (tag()) { +#define THORIN_PRIMTYPE(T) case Node_PrimType_##T: return #T; #define THORIN_ARITHOP(op) case ArithOp_##op: return #op; -#include "thorin/tables/arithoptable.h" - default: THORIN_UNREACHABLE; - } -} - -const char* Cmp::op_name() const { - switch (tag()) { #define THORIN_CMP(op) case Cmp_##op: return #op; -#include "thorin/tables/cmptable.h" - default: THORIN_UNREACHABLE; - } -} - -const char* MathOp::op_name() const { - switch (tag()) { #define THORIN_MATHOP(op) case MathOp_##op: return #op; -#include "thorin/tables/mathoptable.h" +#include "thorin/tables/allnodes.h" default: THORIN_UNREACHABLE; } } diff --git a/src/thorin/primop.h b/src/thorin/primop.h index e82d2c218..4f9ad4efe 100644 --- a/src/thorin/primop.h +++ b/src/thorin/primop.h @@ -150,7 +150,6 @@ class ArithOp : public BinOp { public: const PrimType* type() const { return BinOp::type()->as(); } ArithOpTag arithop_tag() const { return (ArithOpTag) tag(); } - const char* op_name() const override; friend class World; }; @@ -165,7 +164,6 @@ class Cmp : public BinOp { public: const PrimType* type() const { return BinOp::type()->as(); } CmpTag cmp_tag() const { return (CmpTag) tag(); } - const char* op_name() const override; friend class World; }; @@ -182,7 +180,6 @@ class MathOp : public Def { public: const PrimType* type() const { return Def::type()->as(); } MathOpTag mathop_tag() const { return (MathOpTag) tag(); } - const char* op_name() const override; friend class World; }; From 50cdbdf7cfb076f2c0417a7929a97f15304ce49d Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Mon, 23 Jan 2023 21:59:53 +0100 Subject: [PATCH 161/342] fix order --- src/thorin/def.h | 2 +- src/thorin/type.cpp | 12 +++++++++++- src/thorin/type.h | 1 + 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/thorin/def.h b/src/thorin/def.h index b3254d704..a58b2a216 100644 --- a/src/thorin/def.h +++ b/src/thorin/def.h @@ -157,7 +157,7 @@ class Def : public RuntimeCast, public Streamable { //@{ const Def* out(size_t i) const; bool empty() const { return ops_.empty(); } - void set_op(size_t i, const Def* def); + virtual void set_op(size_t i, const Def* def); void unset_op(size_t i); void unset_ops(); virtual bool has_multiple_outs() const { return false; } diff --git a/src/thorin/type.cpp b/src/thorin/type.cpp index b317806fe..76f393d93 100644 --- a/src/thorin/type.cpp +++ b/src/thorin/type.cpp @@ -11,9 +11,19 @@ namespace thorin { -Type::Type(World& w, NodeTag tag, Defs args, Debug dbg) : Type(w, tag, w.star(), args, dbg) {} +Type::Type(World& w, NodeTag tag, Defs args, Debug dbg) : Type(w, tag, w.star(), args, dbg) { + // The overridden version of set_op is ignored in the Def ctor, because according to the C++ spec, virtuals are disabled in ctors (!) + // So this is not actually duplicate code - for the nominal types Type::set_op will do what we want. + for (auto& def : args) + order_ = std::max(order_, def->order()); +} Type::Type(World& w, NodeTag tag, size_t size, Debug dbg) : Type(w, tag, w.star(), size, dbg) {} +void Type::set_op(size_t i, const Def* def) { + Def::set_op(i, def); + order_ = std::max(order_, def->order()); +} + Array types2defs(ArrayRef types) { Array defs(types.size()); size_t i = 0; diff --git a/src/thorin/type.h b/src/thorin/type.h index 84383e250..8af1bfda2 100644 --- a/src/thorin/type.h +++ b/src/thorin/type.h @@ -27,6 +27,7 @@ class Type : public Def, public Streamable { public: int order() const override { return order_; } + void set_op(size_t i, const Def *def) override; Stream& stream(Stream&) const; std::vector filter_type_ops() const { From f8f309ec7b15aadbb100699e723ceb8d2982f925 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Fri, 27 Jan 2023 16:15:22 +0100 Subject: [PATCH 162/342] fix issues with params --- src/thorin/analyses/free_defs.cpp | 2 +- src/thorin/continuation.cpp | 10 +++++++--- src/thorin/continuation.h | 2 +- src/thorin/transform/cleanup_world.cpp | 5 ++--- src/thorin/transform/lift_builtins.cpp | 4 ++-- src/thorin/transform/mangle.cpp | 3 ++- src/thorin/transform/partial_evaluation.cpp | 2 +- 7 files changed, 16 insertions(+), 12 deletions(-) diff --git a/src/thorin/analyses/free_defs.cpp b/src/thorin/analyses/free_defs.cpp index 31bfd28e0..bef50546e 100644 --- a/src/thorin/analyses/free_defs.cpp +++ b/src/thorin/analyses/free_defs.cpp @@ -23,7 +23,7 @@ DefSet free_defs(const Scope& scope, bool include_closures) { while (!queue.empty()) { auto def = pop(queue); - if (def->isa_structural()) { + if (def->isa_structural() && !def->isa()) { if (!include_closures && def->isa()) { result.emplace(def); queue.push(def->op(1)); diff --git a/src/thorin/continuation.cpp b/src/thorin/continuation.cpp index ccf1e6dc0..7b5bad32b 100644 --- a/src/thorin/continuation.cpp +++ b/src/thorin/continuation.cpp @@ -12,12 +12,16 @@ namespace thorin { Param::Param(World& world, const Type* type, const Continuation* continuation, size_t index, Debug dbg) : Def(world, Node_Param, type, { continuation }, dbg) + //: Def(world, Node_Param, type, 1, dbg) , index_(index) -{} +{ + //set_op(0, continuation); +} const Def* Param::rebuild(World& world, const Type* t, Defs defs) const { - assert(defs.size() == 1 && defs[0]->isa()); - return world.param(t, defs[0]->as(), index(), debug()); + assert(defs.size() == 1); + auto cont = defs[0]->as(); + return cont->param(index()); } hash_t Param::vhash() const { diff --git a/src/thorin/continuation.h b/src/thorin/continuation.h index 32bbecd69..7d1e58cd4 100644 --- a/src/thorin/continuation.h +++ b/src/thorin/continuation.h @@ -30,7 +30,7 @@ class Param : public Def { Continuation* continuation() const { return op(0)->as_nom(); } size_t index() const { return index_; } - const Def * rebuild(World&, const Type*, Defs) const override; + const Def* rebuild(World&, const Type*, Defs) const override; bool equal(const Def*) const override; hash_t vhash() const override; private: diff --git a/src/thorin/transform/cleanup_world.cpp b/src/thorin/transform/cleanup_world.cpp index 3ea2f26e2..84e995d91 100644 --- a/src/thorin/transform/cleanup_world.cpp +++ b/src/thorin/transform/cleanup_world.cpp @@ -36,7 +36,7 @@ class Cleaner { }; void Cleaner::eliminate_tail_rec() { - Scope::for_each(*world_, [&](Scope& scope) { + Scope::for_each(world(), [&](Scope& scope) { auto entry = scope.entry(); bool only_tail_calls = true; @@ -233,7 +233,7 @@ next_continuation:; void Cleaner::rebuild() { auto fresh_world = std::make_unique(world()); - Importer importer(*world_, *fresh_world); + Importer importer(world(), *fresh_world); importer.def_old2new_.rehash(world_->defs().capacity()); for (auto&& [_, cont] : world().externals()) { @@ -267,7 +267,6 @@ void Cleaner::verify_closedness() { } void Cleaner::within(const Def* def) { - if (def->isa()) return; // TODO remove once Params are within World's sea of nodes assert(&def->type()->world() == &world()); assert_unused(world().defs().contains(def)); } diff --git a/src/thorin/transform/lift_builtins.cpp b/src/thorin/transform/lift_builtins.cpp index cfc65a1de..0ececb949 100644 --- a/src/thorin/transform/lift_builtins.cpp +++ b/src/thorin/transform/lift_builtins.cpp @@ -65,10 +65,10 @@ void lift_pipeline(World& world) { void lift_builtins(Thorin& thorin) { // This must be run first - World& world = thorin.world(); - lift_pipeline(world); + lift_pipeline(thorin.world()); while (true) { + World& world = thorin.world(); Continuation* cur = nullptr; Scope::for_each(world, [&] (const Scope& scope) { if (cur) return; diff --git a/src/thorin/transform/mangle.cpp b/src/thorin/transform/mangle.cpp index e6523f21f..4bc733c5c 100644 --- a/src/thorin/transform/mangle.cpp +++ b/src/thorin/transform/mangle.cpp @@ -10,7 +10,7 @@ namespace thorin { const Def* Rewriter::instantiate(const Def* odef) { if (auto ndef = old2new.lookup(odef)) return *ndef; - if (odef->isa_structural()) { + if (odef->isa_structural() && !odef->isa()) { Array nops(odef->num_ops()); for (size_t i = 0; i != odef->num_ops(); ++i) nops[i] = instantiate(odef->op(i)); @@ -168,6 +168,7 @@ const Def* Mangler::mangle(const Def* old_def) { nops[i] = mangle(old_def->op(i)); auto type = old_def->type(); // TODO reduce + assert(!old_def->isa()); return def2def_[old_def] = old_def->rebuild(world(), type, nops); } } diff --git a/src/thorin/transform/partial_evaluation.cpp b/src/thorin/transform/partial_evaluation.cpp index 28d383a38..2bbf8d049 100644 --- a/src/thorin/transform/partial_evaluation.cpp +++ b/src/thorin/transform/partial_evaluation.cpp @@ -57,7 +57,7 @@ class CondEval { if (auto ndef = old2new_.lookup(odef)) return *ndef; - if (odef->isa_structural()) { + if (odef->isa_structural() && !odef->isa()) { Array nops(odef->num_ops()); for (size_t i = 0; i != odef->num_ops(); ++i) nops[i] = instantiate(odef->op(i)); From d1bce61b74ab92431da545bd96b2c843c11d7e7a Mon Sep 17 00:00:00 2001 From: Matthias Kurtenacker Date: Mon, 30 Jan 2023 19:26:37 +0100 Subject: [PATCH 163/342] Fix issues with Type::stream. This patch moves Type::stream to rec_stream.cpp and calls Type::stream in Def::stream. --- src/thorin/rec_stream.cpp | 49 ++++++++++++++++++++++++++++++++++ src/thorin/type.cpp | 56 --------------------------------------- src/thorin/type.h | 2 +- 3 files changed, 50 insertions(+), 57 deletions(-) diff --git a/src/thorin/rec_stream.cpp b/src/thorin/rec_stream.cpp index 4980cd91d..ddf2eb067 100644 --- a/src/thorin/rec_stream.cpp +++ b/src/thorin/rec_stream.cpp @@ -69,6 +69,7 @@ void Def::dump() const { dump(0); } void Def::dump(size_t max) const { Stream s(std::cout); stream(s, max).endl(); } Stream& Def::stream(Stream& s) const { + if (isa()) return ((Type*)this)->stream(s); if (isa() || isa() || no_dep()) return stream1(s); return s << unique_name(); } @@ -148,4 +149,52 @@ Stream& Scope::stream(Stream& s) const { THORIN_UNREACHABLE; } +Stream& Type::stream(Stream& s) const { + if (false) {} + else if (isa()) return s.fmt("!!"); + else if (isa< MemType>()) return s.fmt("mem"); + else if (isa< FrameType>()) return s.fmt("frame"); + else if (auto t = isa()) { + return s.fmt("[{} x {}]", t->dim(), t->elem_type()); + } else if (auto t = isa()) { + return s.fmt("fn[{, }]", t->ops()); + } else if (auto t = isa()) { + return s.fmt("closure [{, }]", t->ops()); + } else if (auto t = isa()) { + return s.fmt("[{}]", t->elem_type()); + } else if (auto t = isa()) { + return s.fmt("struct {}", t->name()); + } else if (auto t = isa()) { + return s.fmt("variant {}", t->name()); + } else if (auto t = isa()) { + return s.fmt("[{, }]", t->ops()); + } else if (auto t = isa()) { + if (t->is_vector()) s.fmt("<{} x", t->length()); + s.fmt("{}*", t->pointee()); + if (t->is_vector()) s.fmt(">"); + if (t->device() != -1) s.fmt("[{}]", t->device()); + + switch (t->addr_space()) { + case AddrSpace::Global: s.fmt("[Global]"); break; + case AddrSpace::Texture: s.fmt("[Tex]"); break; + case AddrSpace::Shared: s.fmt("[Shared]"); break; + case AddrSpace::Constant: s.fmt("[Constant]"); break; + default: /* ignore unknown address space */ break; + } + return s; + } else if (auto t = isa()) { + if (t->is_vector()) s.fmt("<{} x", t->length()); + + switch (t->primtype_tag()) { +#define THORIN_ALL_TYPE(T, M) case Node_PrimType_##T: s.fmt(#T); break; +#include "thorin/tables/primtypetable.h" + default: THORIN_UNREACHABLE; + } + + if (t->is_vector()) s.fmt(">"); + return s; + } + THORIN_UNREACHABLE; +} + } diff --git a/src/thorin/type.cpp b/src/thorin/type.cpp index 76f393d93..de6bb3bcb 100644 --- a/src/thorin/type.cpp +++ b/src/thorin/type.cpp @@ -130,62 +130,6 @@ bool PtrType::equal(const Def* other) const { return ptr->device() == device() && ptr->addr_space() == addr_space(); } -//------------------------------------------------------------------------------ - -/* - * stream - */ - -Stream& Type::stream(Stream& s) const { - if (false) {} - else if (isa()) return s.fmt("!!"); - else if (isa< MemType>()) return s.fmt("mem"); - else if (isa< FrameType>()) return s.fmt("frame"); - else if (auto t = isa()) { - return s.fmt("[{} x {}]", t->dim(), t->elem_type()); - } else if (auto t = isa()) { - return s.fmt("fn[{, }]", t->ops()); - } else if (auto t = isa()) { - return s.fmt("closure [{, }]", t->ops()); - } else if (auto t = isa()) { - return s.fmt("[{}]", t->elem_type()); - } else if (auto t = isa()) { - return s.fmt("struct {}", t->name()); - } else if (auto t = isa()) { - return s.fmt("variant {}", t->name()); - } else if (auto t = isa()) { - return s.fmt("[{, }]", t->ops()); - } else if (auto t = isa()) { - if (t->is_vector()) s.fmt("<{} x", t->length()); - s.fmt("{}*", t->pointee()); - if (t->is_vector()) s.fmt(">"); - if (t->device() != -1) s.fmt("[{}]", t->device()); - - switch (t->addr_space()) { - case AddrSpace::Global: s.fmt("[Global]"); break; - case AddrSpace::Texture: s.fmt("[Tex]"); break; - case AddrSpace::Shared: s.fmt("[Shared]"); break; - case AddrSpace::Constant: s.fmt("[Constant]"); break; - default: /* ignore unknown address space */ break; - } - return s; - } else if (auto t = isa()) { - if (t->is_vector()) s.fmt("<{} x", t->length()); - - switch (t->primtype_tag()) { -#define THORIN_ALL_TYPE(T, M) case Node_PrimType_##T: s.fmt(#T); break; -#include "thorin/tables/primtypetable.h" - default: THORIN_UNREACHABLE; - } - - if (t->is_vector()) s.fmt(">"); - return s; - } - THORIN_UNREACHABLE; -} - -//------------------------------------------------------------------------------ - TypeTable::TypeTable(World& world) : world_(world) , star_ (world.put((world))) diff --git a/src/thorin/type.h b/src/thorin/type.h index 8af1bfda2..b2e387f78 100644 --- a/src/thorin/type.h +++ b/src/thorin/type.h @@ -16,7 +16,7 @@ class Type; using Types = ArrayRef; /// Base class for all \p Type%s. -class Type : public Def, public Streamable { +class Type : public Def { protected: /// Constructor for a @em structural Type. Type(World& w, NodeTag tag, const Type* type, Defs args, Debug dbg) : Def(w, tag, type, args, dbg) {} From 45920464a8cc70423e82c1a1c8dedda59c6ebb2b Mon Sep 17 00:00:00 2001 From: Matthias Kurtenacker Date: Wed, 1 Feb 2023 17:10:32 +0100 Subject: [PATCH 164/342] Revert most changes on norecursion branch. This reverts commit 9eaba5daceac48e191683268aa3d8a4ed28feeb9. This reverts commit 72d2deed2b07094d41b01d606ff5dce6450d6ddb. This reverts commit a9ad15284133a0d5b6a5594f71457c250b539337. This reverts commit dc54ccaac495d898f0dfb6338953df2aa6d944a6. This reverts commit 2c0ce6c68db2ebd70ec40c4c3229cd04f2ea7ad1. --- src/thorin/analyses/schedule.cpp | 78 ++------------------------- src/thorin/analyses/schedule.h | 3 -- src/thorin/be/emitter.h | 3 +- src/thorin/transform/hoist_enters.cpp | 16 ++---- src/thorin/transform/importer.cpp | 69 ++---------------------- src/thorin/transform/importer.h | 9 ---- src/thorin/transform/mangle.cpp | 19 +------ 7 files changed, 17 insertions(+), 180 deletions(-) diff --git a/src/thorin/analyses/schedule.cpp b/src/thorin/analyses/schedule.cpp index 38a67a4b6..798de7255 100644 --- a/src/thorin/analyses/schedule.cpp +++ b/src/thorin/analyses/schedule.cpp @@ -9,8 +9,6 @@ #include "thorin/analyses/looptree.h" #include "thorin/analyses/scope.h" -#include - namespace thorin { Scheduler::Scheduler(const Scope& s) @@ -46,103 +44,37 @@ Scheduler::Scheduler(const Scope& s) } } -std::stack early_todo; - -Continuation* Scheduler::early(const Def * def) { +Continuation* Scheduler::early(const Def* def) { if (auto cont = early_.lookup(def)) return *cont; - - early_todo.push(def); - Continuation *return_cont = nullptr; - while (!early_todo.empty()) { - return_cont = early_intern(); - } - assert(return_cont); - return return_cont; -} - -Continuation* Scheduler::early_intern() { - const Def* def = early_todo.top(); - - if (auto cont = early_.lookup(def)) { - early_todo.pop(); - return *cont; - } - if (auto param = def->isa()) { - early_todo.pop(); - return early_[def] = param->continuation(); - } - - bool todo_empty = true; - for (auto op : def->as_structural()->ops()) { - if (!op->isa_nom() && def2uses_.find(op) != def2uses_.end()) { - if (!early_.lookup(op)) { - early_todo.push(op); - todo_empty = false; - } - } - } - if (!todo_empty) - return nullptr; + if (auto param = def->isa()) return early_[def] = param->continuation(); auto result = scope().entry(); for (auto op : def->as_structural()->ops()) { if (!op->isa_nom() && def2uses_.find(op) != def2uses_.end()) { - Continuation *cont = *early_.lookup(op); - assert(cont); + auto cont = early(op); if (domtree().depth(cfg(cont)) > domtree().depth(cfg(result))) result = cont; } } - early_todo.pop(); return early_[def] = result; } -std::stack late_todo; - -Continuation* Scheduler::late(const Def * def) { +Continuation* Scheduler::late(const Def* def) { if (auto cont = late_.lookup(def)) return *cont; - late_todo.push(def); - Continuation *return_cont = nullptr; - while (!late_todo.empty()) { - return_cont = late_intern(); - } - assert(return_cont); - return return_cont; -} - -Continuation* Scheduler::late_intern() { - const Def* def = late_todo.top(); - - if (auto cont = late_.lookup(def)) { - late_todo.pop(); - return *cont; - } - Continuation* result = nullptr; if (auto continuation = def->isa_nom()) { result = continuation; } else if (auto param = def->isa()) { result = param->continuation(); } else { - bool todo_empty = true; - for (auto use : uses(def)) { - if (!late_.lookup(use)) { - late_todo.push(use); - todo_empty = false; - } - } - if (!todo_empty) - return nullptr; for (auto use : uses(def)) { - Continuation* cont = *late_.lookup(use); - assert(cont); + auto cont = late(use); result = result ? domtree().least_common_ancestor(cfg(result), cfg(cont))->continuation() : cont; } } - late_todo.pop(); return late_[def] = result; } diff --git a/src/thorin/analyses/schedule.h b/src/thorin/analyses/schedule.h index 57d874274..b2cfa65aa 100644 --- a/src/thorin/analyses/schedule.h +++ b/src/thorin/analyses/schedule.h @@ -48,9 +48,6 @@ class Scheduler { DefMap late_; DefMap smart_; DefMap def2uses_; - - Continuation* early_intern(); - Continuation* late_intern(); }; using Schedule = std::vector; diff --git a/src/thorin/be/emitter.h b/src/thorin/be/emitter.h index f41d4fec8..a13ad8d49 100644 --- a/src/thorin/be/emitter.h +++ b/src/thorin/be/emitter.h @@ -35,7 +35,8 @@ class Emitter { } while (!required_defs.empty()) { - auto r = pop(required_defs); + auto r = required_defs.top(); + required_defs.pop(); emit_unsafe(r); } diff --git a/src/thorin/transform/hoist_enters.cpp b/src/thorin/transform/hoist_enters.cpp index 7595ca1fb..a51cf8d64 100644 --- a/src/thorin/transform/hoist_enters.cpp +++ b/src/thorin/transform/hoist_enters.cpp @@ -4,12 +4,8 @@ #include "thorin/analyses/scope.h" #include "thorin/analyses/verify.h" -#include - namespace thorin { -static std::stack hoist_enters_todo; - static void find_enters(std::deque& enters, const Def* def) { if (auto enter = def->isa()) enters.push_back(enter); @@ -19,19 +15,13 @@ static void find_enters(std::deque& enters, const Def* def) { for (auto use : def->uses()) { if (auto memop = use->isa()) - hoist_enters_todo.push(memop); + find_enters(enters, memop); } } static void find_enters(std::deque& enters, Continuation* continuation) { - if (auto mem_param = continuation->mem_param()) { - hoist_enters_todo.push(mem_param); - while (!hoist_enters_todo.empty()) { - auto next_item = pop(hoist_enters_todo); - - find_enters(enters, next_item); - } - } + if (auto mem_param = continuation->mem_param()) + find_enters(enters, mem_param); } static void hoist_enters(const Scope& scope) { diff --git a/src/thorin/transform/importer.cpp b/src/thorin/transform/importer.cpp index f746c9c46..4691e9795 100644 --- a/src/thorin/transform/importer.cpp +++ b/src/thorin/transform/importer.cpp @@ -8,70 +8,20 @@ const Def* Importer::import(const Def* odef) { return *ndef; } - assert(required_defs.empty()); - required_defs.push(std::pair(odef, false)); - - const Def* return_def = nullptr; - while (!required_defs.empty()) { - return_def = import_nonrecursive(); - } - - assert(return_def); - assert(return_def == def_old2new_.lookup(odef)); - - return return_def; -} - -const Def* Importer::import_nonrecursive() { - const Def* odef = required_defs.top().first; - bool jump_to_analyze = required_defs.top().second; - - std::optional ndef = std::nullopt; - if (ndef = def_old2new_.lookup(odef)) { - assert(&(*ndef)->world() == &world()); - if (!jump_to_analyze) { - required_defs.pop(); - return *ndef; - } - } - - if (!jump_to_analyze) { - required_defs.pop(); - required_defs.push(std::pair(odef, true)); - } - if (odef == odef->world().star()) { def_old2new_[odef] = world().star(); - required_defs.pop(); return world().star(); } - if (!def_old2new_.lookup(odef->type())) { - required_defs.push(std::pair(odef->type(), false)); - return nullptr; - } auto ntype = import(odef->type())->as(); Def* stub = nullptr; if (odef->isa_nom()) { - if (ndef) { - stub = (*ndef)->as_nom(); - } else { - stub = odef->stub(world(), ntype); - def_old2new_[odef] = stub; - } + stub = odef->stub(world(), ntype); + def_old2new_[odef] = stub; } size_t size = odef->num_ops(); - bool unfinished = false; - for (size_t i = 0; i != size; ++i) - if (!def_old2new_.lookup(odef->op(i))) { - required_defs.push(std::pair(odef->op(i), false)); - unfinished = true; - } - if (unfinished) - return nullptr; - Array nops(size); for (size_t i = 0; i != size; ++i) { assert(odef->op(i) != odef); @@ -80,21 +30,12 @@ const Def* Importer::import_nonrecursive() { } if (odef->isa_structural()) { - if (!ndef) - ndef = odef->rebuild(world(), ntype, nops); - - if (auto oglobal = odef->isa()) { - if (oglobal->is_external()) - world().make_external(const_cast(*ndef)); - } - - todo_ |= odef->tag() != (*ndef)->tag(); - required_defs.pop(); - return def_old2new_[odef] = *ndef; + auto ndef = odef->rebuild(world(), ntype, nops); + todo_ |= odef->tag() != ndef->tag(); + return def_old2new_[odef] = ndef; } else { assert(odef->isa_nom() && stub); stub->rebuild_from(odef, nops); - required_defs.pop(); return stub; } } diff --git a/src/thorin/transform/importer.h b/src/thorin/transform/importer.h index 0f3db273c..10941b915 100644 --- a/src/thorin/transform/importer.h +++ b/src/thorin/transform/importer.h @@ -4,10 +4,6 @@ #include "thorin/world.h" #include "thorin/config.h" -#include -#include -#include - namespace thorin { class Importer { @@ -28,11 +24,6 @@ class Importer { const Def* import(const Def*); bool todo() const { return todo_; } -private: - const Def* import_nonrecursive(); - - std::stack> required_defs; - public: Def2Def def_old2new_; World& src; diff --git a/src/thorin/transform/mangle.cpp b/src/thorin/transform/mangle.cpp index 3a417c34d..b184d97c5 100644 --- a/src/thorin/transform/mangle.cpp +++ b/src/thorin/transform/mangle.cpp @@ -5,9 +5,6 @@ #include "thorin/world.h" #include "thorin/analyses/scope.h" -#include -#include - namespace thorin { const Def* Rewriter::instantiate(const Def* odef) { @@ -57,8 +54,6 @@ Mangler::Mangler(const Scope& scope, Defs args, Defs lift) } } -std::queue> bodies_to_mangle; - Continuation* Mangler::mangle() { // create new_entry - but first collect and specialize all param types std::vector param_types; @@ -102,17 +97,7 @@ Continuation* Mangler::mangle() { new_entry()->set_filter(world().filter(new_conditions, old_entry()->filter()->debug())); } - bodies_to_mangle.push(std::pair(new_entry(), old_entry())); - - while (!bodies_to_mangle.empty()) { - auto task = pop(bodies_to_mangle); - - auto new_cont = task.first; - auto old_cont = task.second; - - assert(!new_cont->has_body()); - new_cont->set_body(mangle_body(old_cont->body())); - } + new_entry()->set_body(mangle_body(old_entry()->body())); new_entry()->verify(); @@ -171,7 +156,7 @@ const Def* Mangler::mangle(const Def* old_def) { else if (auto old_continuation = old_def->isa_nom()) { auto new_continuation = mangle_head(old_continuation); if (old_continuation->has_body()) - bodies_to_mangle.push(std::pair(new_continuation, old_continuation)); + new_continuation->set_body(mangle_body(old_continuation->body())); return new_continuation; } else if (auto param = old_def->isa()) { assert(within(param->continuation())); From f5b51a12dbb675d0490bf6b03b5ab435c2c09b9b Mon Sep 17 00:00:00 2001 From: Matthias Kurtenacker Date: Fri, 3 Feb 2023 17:34:23 +0100 Subject: [PATCH 165/342] [JSON]: AddrSpace Private for ptrtpye added. --- src/thorin/be/json/json.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/thorin/be/json/json.cpp b/src/thorin/be/json/json.cpp index 6e8621334..d289603df 100644 --- a/src/thorin/be/json/json.cpp +++ b/src/thorin/be/json/json.cpp @@ -152,6 +152,9 @@ class TypeTable { case AddrSpace::Constant: result["addrspace"] = "constant"; break; + case AddrSpace::Private: + result["addrspace"] = "private"; + break; } } else { std::cerr << "type cannot be translated\n"; From d3bafe1b8aa5758f6c2b9788adc918362d54e56a Mon Sep 17 00:00:00 2001 From: Matthias Kurtenacker Date: Tue, 28 Feb 2023 14:37:45 +0100 Subject: [PATCH 166/342] Add Star to type dump. --- src/thorin/rec_stream.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/thorin/rec_stream.cpp b/src/thorin/rec_stream.cpp index f48bf3039..0965e5318 100644 --- a/src/thorin/rec_stream.cpp +++ b/src/thorin/rec_stream.cpp @@ -217,6 +217,8 @@ Stream& Type::stream(Stream& s) const { if (t->is_vector()) s.fmt(">"); return s; + } else if (isa()) { + return s.fmt("★"); } THORIN_UNREACHABLE; } From 82a287c0f5f5aec6973c62b91cae3474499b3490 Mon Sep 17 00:00:00 2001 From: Richard Membarth Date: Fri, 26 May 2023 15:05:07 +0200 Subject: [PATCH 167/342] Add CodeGen for AMDGPU + PAL. --- src/thorin/CMakeLists.txt | 6 +- src/thorin/be/codegen.cpp | 21 +++-- src/thorin/be/codegen.h | 2 +- .../be/llvm/{amdgpu.cpp => amdgpu_hsa.cpp} | 14 +-- src/thorin/be/llvm/{amdgpu.h => amdgpu_hsa.h} | 8 +- src/thorin/be/llvm/amdgpu_pal.cpp | 91 +++++++++++++++++++ src/thorin/be/llvm/amdgpu_pal.h | 35 +++++++ src/thorin/be/llvm/llvm.cpp | 3 +- src/thorin/be/llvm/runtime.h | 3 +- src/thorin/continuation.cpp | 3 +- src/thorin/continuation.h | 3 +- 11 files changed, 162 insertions(+), 27 deletions(-) rename src/thorin/be/llvm/{amdgpu.cpp => amdgpu_hsa.cpp} (85%) rename src/thorin/be/llvm/{amdgpu.h => amdgpu_hsa.h} (77%) create mode 100644 src/thorin/be/llvm/amdgpu_pal.cpp create mode 100644 src/thorin/be/llvm/amdgpu_pal.h diff --git a/src/thorin/CMakeLists.txt b/src/thorin/CMakeLists.txt index 944c785d2..440d7cafc 100644 --- a/src/thorin/CMakeLists.txt +++ b/src/thorin/CMakeLists.txt @@ -95,8 +95,10 @@ if(LLVM_FOUND) be/llvm/cpu.h be/llvm/llvm.cpp be/llvm/llvm.h - be/llvm/amdgpu.cpp - be/llvm/amdgpu.h + be/llvm/amdgpu_hsa.cpp + be/llvm/amdgpu_hsa.h + be/llvm/amdgpu_pal.cpp + be/llvm/amdgpu_pal.h be/llvm/nvvm.cpp be/llvm/nvvm.h be/llvm/parallel.cpp diff --git a/src/thorin/be/codegen.cpp b/src/thorin/be/codegen.cpp index db6f6d802..12247b032 100644 --- a/src/thorin/be/codegen.cpp +++ b/src/thorin/be/codegen.cpp @@ -6,7 +6,8 @@ #if THORIN_ENABLE_LLVM #include "thorin/be/llvm/cpu.h" #include "thorin/be/llvm/nvvm.h" -#include "thorin/be/llvm/amdgpu.h" +#include "thorin/be/llvm/amdgpu_hsa.h" +#include "thorin/be/llvm/amdgpu_pal.h" #endif #include "thorin/be/c/c.h" @@ -87,11 +88,12 @@ DeviceBackends::DeviceBackends(World& world, int opt, bool debug, std::string& f Continuation* imported = nullptr; static const auto backend_intrinsics = std::array { - std::pair { CUDA, Intrinsic::CUDA }, - std::pair { NVVM, Intrinsic::NVVM }, - std::pair { OpenCL, Intrinsic::OpenCL }, - std::pair { AMDGPU, Intrinsic::AMDGPU }, - std::pair { HLS, Intrinsic::HLS } + std::pair { CUDA, Intrinsic::CUDA }, + std::pair { NVVM, Intrinsic::NVVM }, + std::pair { OpenCL, Intrinsic::OpenCL }, + std::pair { AMDGPU_HSA, Intrinsic::AMDGPUHSA }, + std::pair { AMDGPU_PAL, Intrinsic::AMDGPUPAL }, + std::pair { HLS, Intrinsic::HLS } }; for (auto [backend, intrinsic] : backend_intrinsics) { if (is_passed_to_intrinsic(continuation, intrinsic)) { @@ -112,7 +114,7 @@ DeviceBackends::DeviceBackends(World& world, int opt, bool debug, std::string& f kernels.emplace_back(continuation); }); - for (auto backend : std::array { CUDA, NVVM, OpenCL, AMDGPU }) { + for (auto backend : std::array { CUDA, NVVM, OpenCL, AMDGPU_HSA, AMDGPU_PAL }) { if (!importers_[backend].world().empty()) { get_kernel_configs(importers_[backend], kernels, kernel_config, [&](Continuation *use, Continuation * /* imported */) { auto app = use->body(); @@ -185,8 +187,9 @@ DeviceBackends::DeviceBackends(World& world, int opt, bool debug, std::string& f hls_kernel_launch(world, hls_host_params); #if THORIN_ENABLE_LLVM - if (!importers_[NVVM ].world().empty()) cgs[NVVM ] = std::make_unique(importers_[NVVM ].world(), kernel_config, debug); - if (!importers_[AMDGPU].world().empty()) cgs[AMDGPU] = std::make_unique(importers_[AMDGPU].world(), kernel_config, opt, debug); + if (!importers_[NVVM ].world().empty()) cgs[NVVM ] = std::make_unique(importers_[NVVM ].world(), kernel_config, debug); + if (!importers_[AMDGPU_HSA].world().empty()) cgs[AMDGPU_HSA] = std::make_unique(importers_[AMDGPU_HSA].world(), kernel_config, opt, debug); + if (!importers_[AMDGPU_PAL].world().empty()) cgs[AMDGPU_PAL] = std::make_unique(importers_[AMDGPU_PAL].world(), kernel_config, opt, debug); #else (void)opt; #endif diff --git a/src/thorin/be/codegen.h b/src/thorin/be/codegen.h index cd7c5689f..3a615d4ae 100644 --- a/src/thorin/be/codegen.h +++ b/src/thorin/be/codegen.h @@ -44,7 +44,7 @@ struct DeviceBackends { Cont2Config kernel_config; std::vector kernels; - enum { CUDA, NVVM, OpenCL, AMDGPU, HLS, BackendCount }; + enum { CUDA, NVVM, OpenCL, AMDGPU_HSA, AMDGPU_PAL, HLS, BackendCount }; std::array, BackendCount> cgs; private: std::vector importers_; diff --git a/src/thorin/be/llvm/amdgpu.cpp b/src/thorin/be/llvm/amdgpu_hsa.cpp similarity index 85% rename from src/thorin/be/llvm/amdgpu.cpp rename to src/thorin/be/llvm/amdgpu_hsa.cpp index 1d19ff7a9..b245010ac 100644 --- a/src/thorin/be/llvm/amdgpu.cpp +++ b/src/thorin/be/llvm/amdgpu_hsa.cpp @@ -1,4 +1,4 @@ -#include "thorin/be/llvm/amdgpu.h" +#include "thorin/be/llvm/amdgpu_hsa.h" #include // TODO don't use std::unordered_* @@ -7,7 +7,7 @@ namespace thorin::llvm { -AMDGPUCodeGen::AMDGPUCodeGen(World& world, const Cont2Config& kernel_config, int opt, bool debug) +AMDGPUHSACodeGen::AMDGPUHSACodeGen(World& world, const Cont2Config& kernel_config, int opt, bool debug) : CodeGen(world, llvm::CallingConv::C, llvm::CallingConv::C, llvm::CallingConv::AMDGPU_KERNEL, opt, debug) , kernel_config_(kernel_config) { @@ -19,7 +19,7 @@ AMDGPUCodeGen::AMDGPUCodeGen(World& world, const Cont2Config& kernel_config, int // Kernel code //------------------------------------------------------------------------------ -void AMDGPUCodeGen::emit_fun_decl_hook(Continuation* continuation, llvm::Function* f) { +void AMDGPUHSACodeGen::emit_fun_decl_hook(Continuation* continuation, llvm::Function* f) { auto config = kernel_config_.find(continuation); if (config != kernel_config_.end()) { auto block = config->second->as()->block_size(); @@ -34,7 +34,7 @@ void AMDGPUCodeGen::emit_fun_decl_hook(Continuation* continuation, llvm::Functio } } -llvm::Function* AMDGPUCodeGen::emit_fun_decl(Continuation* continuation) { +llvm::Function* AMDGPUHSACodeGen::emit_fun_decl(Continuation* continuation) { if (continuation->name() == "llvm.amdgcn.implicitarg.ptr") if (auto f = defs_.lookup(entry_); f && llvm::isa(*f)) llvm::cast(*f)->addFnAttr("amdgpu-implicitarg-ptr"); @@ -44,13 +44,13 @@ llvm::Function* AMDGPUCodeGen::emit_fun_decl(Continuation* continuation) { return CodeGen::emit_fun_decl(continuation); } -llvm::Value* AMDGPUCodeGen::emit_global(const Global* global) { +llvm::Value* AMDGPUHSACodeGen::emit_global(const Global* global) { if (global->is_mutable()) world().wdef(global, "AMDGPU: Global variable '{}' will not be synced with host", global); return CodeGen::emit_global(global); } -llvm::Value* AMDGPUCodeGen::emit_mathop(llvm::IRBuilder<>& irbuilder, const MathOp* mathop) { +llvm::Value* AMDGPUHSACodeGen::emit_mathop(llvm::IRBuilder<>& irbuilder, const MathOp* mathop) { auto make_key = [] (MathOpTag tag, unsigned bitwidth) { return (static_cast(tag) << 16) | bitwidth; }; static const std::unordered_map ocml_functions = { #define MATH_FUNCTION(name) \ @@ -84,7 +84,7 @@ llvm::Value* AMDGPUCodeGen::emit_mathop(llvm::IRBuilder<>& irbuilder, const Math return call_math_function(irbuilder, mathop, ocml_functions.at(key)); } -Continuation* AMDGPUCodeGen::emit_reserve(llvm::IRBuilder<>& irbuilder, const Continuation* continuation) { +Continuation* AMDGPUHSACodeGen::emit_reserve(llvm::IRBuilder<>& irbuilder, const Continuation* continuation) { return emit_reserve_shared(irbuilder, continuation, true); } diff --git a/src/thorin/be/llvm/amdgpu.h b/src/thorin/be/llvm/amdgpu_hsa.h similarity index 77% rename from src/thorin/be/llvm/amdgpu.h rename to src/thorin/be/llvm/amdgpu_hsa.h index 640fc5b69..05fb39044 100644 --- a/src/thorin/be/llvm/amdgpu.h +++ b/src/thorin/be/llvm/amdgpu_hsa.h @@ -1,5 +1,5 @@ -#ifndef THORIN_BE_LLVM_AMDGPU_H -#define THORIN_BE_LLVM_AMDGPU_H +#ifndef THORIN_BE_LLVM_AMDGPU_HSA_H +#define THORIN_BE_LLVM_AMDGPU_HSA_H #include "thorin/be/llvm/llvm.h" @@ -11,9 +11,9 @@ namespace llvm { namespace llvm = ::llvm; -class AMDGPUCodeGen : public CodeGen { +class AMDGPUHSACodeGen : public CodeGen { public: - AMDGPUCodeGen(World& world, const Cont2Config&, int opt, bool debug); + AMDGPUHSACodeGen(World& world, const Cont2Config&, int opt, bool debug); const char* file_ext() const override { return ".amdgpu"; } diff --git a/src/thorin/be/llvm/amdgpu_pal.cpp b/src/thorin/be/llvm/amdgpu_pal.cpp new file mode 100644 index 000000000..77ed8c838 --- /dev/null +++ b/src/thorin/be/llvm/amdgpu_pal.cpp @@ -0,0 +1,91 @@ +#include "thorin/be/llvm/amdgpu_pal.h" + +#include // TODO don't use std::unordered_* + +#include "thorin/primop.h" +#include "thorin/world.h" + +namespace thorin::llvm { + +AMDGPUPALCodeGen::AMDGPUPALCodeGen(World& world, const Cont2Config& kernel_config, int opt, bool debug) + : CodeGen(world, llvm::CallingConv::C, llvm::CallingConv::C, llvm::CallingConv::AMDGPU_KERNEL, opt, debug) + , kernel_config_(kernel_config) +{ + module().setDataLayout("e-p:64:64-p1:64:64-p2:32:32-p3:32:32-p4:64:64-p5:32:32-p6:32:32-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024-v2048:2048-n32:64-S32-A5-ni:7"); + module().setTargetTriple("amdgcn-amd-amdpal"); +} + +//------------------------------------------------------------------------------ +// Kernel code +//------------------------------------------------------------------------------ + +void AMDGPUPALCodeGen::emit_fun_decl_hook(Continuation* continuation, llvm::Function* f) { + auto config = kernel_config_.find(continuation); + if (config != kernel_config_.end()) { + auto block = config->second->as()->block_size(); + if (std::get<0>(block) > 0 && std::get<1>(block) > 0 && std::get<2>(block) > 0) { + Array annotation_values_wgsize(3); + auto int32_type = llvm::IntegerType::get(context(), 32); + annotation_values_wgsize[0] = llvm::ConstantAsMetadata::get(llvm::ConstantInt::get(int32_type, std::get<0>(block))); + annotation_values_wgsize[1] = llvm::ConstantAsMetadata::get(llvm::ConstantInt::get(int32_type, std::get<1>(block))); + annotation_values_wgsize[2] = llvm::ConstantAsMetadata::get(llvm::ConstantInt::get(int32_type, std::get<2>(block))); + f->setMetadata(llvm::StringRef("reqd_work_group_size"), llvm::MDNode::get(context(), llvm_ref(annotation_values_wgsize))); + } + } +} + +llvm::Function* AMDGPUPALCodeGen::emit_fun_decl(Continuation* continuation) { + if (continuation->name() == "llvm.amdgcn.implicitarg.ptr") + if (auto f = defs_.lookup(entry_); f && llvm::isa(*f)) + llvm::cast(*f)->addFnAttr("amdgpu-implicitarg-ptr"); + if (continuation->name() == "__ockl_printf_begin") + if (auto f = defs_.lookup(entry_); f && llvm::isa(*f)) + llvm::cast(*f)->addFnAttr("amdgpu-implicitarg-num-bytes", "32"); + return CodeGen::emit_fun_decl(continuation); +} + +llvm::Value* AMDGPUPALCodeGen::emit_global(const Global* global) { + if (global->is_mutable()) + world().wdef(global, "AMDGPU: Global variable '{}' will not be synced with host", global); + return CodeGen::emit_global(global); +} + +llvm::Value* AMDGPUPALCodeGen::emit_mathop(llvm::IRBuilder<>& irbuilder, const MathOp* mathop) { + auto make_key = [] (MathOpTag tag, unsigned bitwidth) { return (static_cast(tag) << 16) | bitwidth; }; + static const std::unordered_map ocml_functions = { +#define MATH_FUNCTION(name) \ + { make_key(MathOp_##name, 32), "__ocml_" #name "_f32" }, \ + { make_key(MathOp_##name, 64), "__ocml_" #name "_f64" }, + MATH_FUNCTION(fabs) + MATH_FUNCTION(copysign) + MATH_FUNCTION(round) + MATH_FUNCTION(floor) + MATH_FUNCTION(ceil) + MATH_FUNCTION(fmin) + MATH_FUNCTION(fmax) + MATH_FUNCTION(cos) + MATH_FUNCTION(sin) + MATH_FUNCTION(tan) + MATH_FUNCTION(acos) + MATH_FUNCTION(asin) + MATH_FUNCTION(atan) + MATH_FUNCTION(atan2) + MATH_FUNCTION(sqrt) + MATH_FUNCTION(cbrt) + MATH_FUNCTION(pow) + MATH_FUNCTION(exp) + MATH_FUNCTION(exp2) + MATH_FUNCTION(log) + MATH_FUNCTION(log2) + MATH_FUNCTION(log10) +#undef MATH_FUNCTION + }; + auto key = make_key(mathop->mathop_tag(), num_bits(mathop->type()->primtype_tag())); + return call_math_function(irbuilder, mathop, ocml_functions.at(key)); +} + +Continuation* AMDGPUPALCodeGen::emit_reserve(llvm::IRBuilder<>& irbuilder, const Continuation* continuation) { + return emit_reserve_shared(irbuilder, continuation, true); +} + +} diff --git a/src/thorin/be/llvm/amdgpu_pal.h b/src/thorin/be/llvm/amdgpu_pal.h new file mode 100644 index 000000000..ab2e7fcdd --- /dev/null +++ b/src/thorin/be/llvm/amdgpu_pal.h @@ -0,0 +1,35 @@ +#ifndef THORIN_BE_LLVM_AMDGPU_PAL_H +#define THORIN_BE_LLVM_AMDGPU_PAL_H + +#include "thorin/be/llvm/llvm.h" + +namespace thorin { + +class Load; + +namespace llvm { + +namespace llvm = ::llvm; + +class AMDGPUPALCodeGen : public CodeGen { +public: + AMDGPUPALCodeGen(World& world, const Cont2Config&, int opt, bool debug); + + const char* file_ext() const override { return ".amdgpu"; } + +protected: + void emit_fun_decl_hook(Continuation*, llvm::Function*) override; + llvm::Function* emit_fun_decl(Continuation*) override; + llvm::Value* emit_global(const Global*) override; + 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"; } + + const Cont2Config& kernel_config_; +}; + +} + +} + +#endif diff --git a/src/thorin/be/llvm/llvm.cpp b/src/thorin/be/llvm/llvm.cpp index 49d923bf2..d33300f15 100644 --- a/src/thorin/be/llvm/llvm.cpp +++ b/src/thorin/be/llvm/llvm.cpp @@ -1158,7 +1158,8 @@ Continuation* CodeGen::emit_intrinsic(llvm::IRBuilder<>& irbuilder, Continuation case Intrinsic::CUDA: return runtime_->emit_host_code(*this, irbuilder, Runtime::CUDA_PLATFORM, ".cu", continuation); case Intrinsic::NVVM: return runtime_->emit_host_code(*this, irbuilder, Runtime::CUDA_PLATFORM, ".nvvm", continuation); case Intrinsic::OpenCL: return runtime_->emit_host_code(*this, irbuilder, Runtime::OPENCL_PLATFORM, ".cl", continuation); - case Intrinsic::AMDGPU: return runtime_->emit_host_code(*this, irbuilder, Runtime::HSA_PLATFORM, ".amdgpu", continuation); + case Intrinsic::AMDGPUHSA: return runtime_->emit_host_code(*this, irbuilder, Runtime::HSA_PLATFORM, ".amdgpu", continuation); + case Intrinsic::AMDGPUPAL: return runtime_->emit_host_code(*this, irbuilder, Runtime::PAL_PLATFORM, ".amdgpu", continuation); case Intrinsic::HLS: return emit_hls(irbuilder, continuation); case Intrinsic::Parallel: return emit_parallel(irbuilder, continuation); case Intrinsic::Fibers: return emit_fibers(irbuilder, continuation); diff --git a/src/thorin/be/llvm/runtime.h b/src/thorin/be/llvm/runtime.h index 4ebf5f5ef..aed5736b3 100644 --- a/src/thorin/be/llvm/runtime.h +++ b/src/thorin/be/llvm/runtime.h @@ -22,7 +22,8 @@ class Runtime { CPU_PLATFORM, CUDA_PLATFORM, OPENCL_PLATFORM, - HSA_PLATFORM + HSA_PLATFORM, + PAL_PLATFORM }; /// Emits a call to anydsl_launch_kernel. diff --git a/src/thorin/continuation.cpp b/src/thorin/continuation.cpp index f729937f8..37724ad1a 100644 --- a/src/thorin/continuation.cpp +++ b/src/thorin/continuation.cpp @@ -214,7 +214,8 @@ void Continuation::set_intrinsic() { if (name() == "cuda") attributes().intrinsic = Intrinsic::CUDA; else if (name() == "nvvm") attributes().intrinsic = Intrinsic::NVVM; else if (name() == "opencl") attributes().intrinsic = Intrinsic::OpenCL; - else if (name() == "amdgpu") attributes().intrinsic = Intrinsic::AMDGPU; + else if (name() == "amdgpu_hsa") attributes().intrinsic = Intrinsic::AMDGPUHSA; + else if (name() == "amdgpu_pal") attributes().intrinsic = Intrinsic::AMDGPUPAL; else if (name() == "hls") attributes().intrinsic = Intrinsic::HLS; else if (name() == "parallel") attributes().intrinsic = Intrinsic::Parallel; else if (name() == "fibers") attributes().intrinsic = Intrinsic::Fibers; diff --git a/src/thorin/continuation.h b/src/thorin/continuation.h index 9f34fe992..6fa0329ed 100644 --- a/src/thorin/continuation.h +++ b/src/thorin/continuation.h @@ -90,7 +90,8 @@ enum class Intrinsic : uint8_t { CUDA = AcceleratorBegin, ///< Internal CUDA-Backend. NVVM, ///< Internal NNVM-Backend. OpenCL, ///< Internal OpenCL-Backend. - AMDGPU, ///< Internal AMDGPU-Backend. + AMDGPUHSA, ///< Internal AMDGPU-HSA-Backend. + AMDGPUPAL, ///< Internal AMDGPU-PAL-Backend. HLS, ///< Internal HLS-Backend. Parallel, ///< Internal Parallel-CPU-Backend. Fibers, ///< Internal Parallel-CPU-Backend using resumable fibers. From 4eefa459a5e90b88ec747419be43c8c96d5e5ad7 Mon Sep 17 00:00:00 2001 From: Richard Membarth Date: Tue, 13 Jun 2023 11:11:48 +0200 Subject: [PATCH 168/342] PAL: set kernel calling convention to AMDGPU_CS. --- src/thorin/be/llvm/amdgpu_pal.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/thorin/be/llvm/amdgpu_pal.cpp b/src/thorin/be/llvm/amdgpu_pal.cpp index 77ed8c838..3fa0d2a04 100644 --- a/src/thorin/be/llvm/amdgpu_pal.cpp +++ b/src/thorin/be/llvm/amdgpu_pal.cpp @@ -8,7 +8,7 @@ namespace thorin::llvm { AMDGPUPALCodeGen::AMDGPUPALCodeGen(World& world, const Cont2Config& kernel_config, int opt, bool debug) - : CodeGen(world, llvm::CallingConv::C, llvm::CallingConv::C, llvm::CallingConv::AMDGPU_KERNEL, opt, debug) + : CodeGen(world, llvm::CallingConv::C, llvm::CallingConv::C, llvm::CallingConv::AMDGPU_CS, opt, debug) , kernel_config_(kernel_config) { module().setDataLayout("e-p:64:64-p1:64:64-p2:32:32-p3:32:32-p4:64:64-p5:32:32-p6:32:32-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024-v2048:2048-n32:64-S32-A5-ni:7"); From ec5691c417523b3802070f0ae6733a9feab38dd3 Mon Sep 17 00:00:00 2001 From: Richard Membarth Date: Tue, 1 Aug 2023 17:45:29 +0200 Subject: [PATCH 169/342] PAL: set function calling convention to AMDGPU_Gfx. --- src/thorin/be/llvm/amdgpu_pal.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/thorin/be/llvm/amdgpu_pal.cpp b/src/thorin/be/llvm/amdgpu_pal.cpp index 3a7459d55..76a8b909a 100644 --- a/src/thorin/be/llvm/amdgpu_pal.cpp +++ b/src/thorin/be/llvm/amdgpu_pal.cpp @@ -8,7 +8,7 @@ namespace thorin::llvm { AMDGPUPALCodeGen::AMDGPUPALCodeGen(World& world, const Cont2Config& kernel_config, int opt, bool debug) - : CodeGen(world, llvm::CallingConv::C, llvm::CallingConv::C, llvm::CallingConv::AMDGPU_CS, opt, debug) + : CodeGen(world, llvm::CallingConv::AMDGPU_Gfx, llvm::CallingConv::C, llvm::CallingConv::AMDGPU_CS, opt, debug) , kernel_config_(kernel_config) { module().setDataLayout("e-p:64:64-p1:64:64-p2:32:32-p3:32:32-p4:64:64-p5:32:32-p6:32:32-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024-v2048:2048-n32:64-S32-A5-G1-ni:7"); From 5098a739efd2e596443710bafaaf3c8eb6b79729 Mon Sep 17 00:00:00 2001 From: Richard Membarth Date: Tue, 1 Aug 2023 17:53:51 +0200 Subject: [PATCH 170/342] PAL: remove HSA code. --- src/thorin/be/llvm/amdgpu_pal.cpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/thorin/be/llvm/amdgpu_pal.cpp b/src/thorin/be/llvm/amdgpu_pal.cpp index 76a8b909a..9ea8562c2 100644 --- a/src/thorin/be/llvm/amdgpu_pal.cpp +++ b/src/thorin/be/llvm/amdgpu_pal.cpp @@ -35,12 +35,6 @@ void AMDGPUPALCodeGen::emit_fun_decl_hook(Continuation* continuation, llvm::Func } llvm::Function* AMDGPUPALCodeGen::emit_fun_decl(Continuation* continuation) { - if (continuation->name() == "llvm.amdgcn.implicitarg.ptr") - if (auto f = defs_.lookup(entry_); f && llvm::isa(*f)) - llvm::cast(*f)->addFnAttr("amdgpu-implicitarg-ptr"); - if (continuation->name() == "__ockl_printf_begin") - if (auto f = defs_.lookup(entry_); f && llvm::isa(*f)) - llvm::cast(*f)->addFnAttr("amdgpu-implicitarg-num-bytes", "32"); return CodeGen::emit_fun_decl(continuation); } From 9d212b634a2ed4ee26b6096acaeb3d88b79f1cdb Mon Sep 17 00:00:00 2001 From: Matthias Kurtenacker Date: Mon, 4 Sep 2023 17:06:48 +0200 Subject: [PATCH 171/342] Fix convert_closure_type in llvm be. --- src/thorin/be/llvm/llvm.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/thorin/be/llvm/llvm.cpp b/src/thorin/be/llvm/llvm.cpp index 7002c0d80..dcbe63a6c 100644 --- a/src/thorin/be/llvm/llvm.cpp +++ b/src/thorin/be/llvm/llvm.cpp @@ -250,14 +250,14 @@ llvm::FunctionType* CodeGen::convert_closure_type(const Type* type) { auto fn = type->as(); llvm::Type* ret = nullptr; std::vector ops; - for (auto op : fn->ops()) { - if (op->isa() || op == world().unit()) continue; + for (auto op : fn->types()) { + if (op->isa() || op == world().unit_type()) continue; auto fn = op->isa(); if (fn && !op->isa()) { assert(!ret && "only one 'return' supported"); std::vector ret_types; - for (auto fn_op : fn->ops()) { - if (fn_op->isa() || fn_op == world().unit()) continue; + for (auto fn_op : fn->types()) { + if (fn_op->isa() || fn_op == world().unit_type()) continue; ret_types.push_back(convert(fn_op)); } if (ret_types.size() == 0) ret = llvm::Type::getVoidTy(context()); From 135aa4e6a7c651465a110365e5410da6157408e1 Mon Sep 17 00:00:00 2001 From: Matthias Kurtenacker Date: Tue, 14 Feb 2023 22:05:22 +0100 Subject: [PATCH 172/342] Fix CMake to allow for cmake based meta repository. Remove CMAKE_CONFIGURATION_TYPES to ensure LLVM can be build. --- CMakeLists.txt | 10 +++++++--- cmake/thorin-config.cmake.in | 2 +- src/thorin/CMakeLists.txt | 4 ++-- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3d8f55c1e..616e2877a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,7 +3,7 @@ cmake_minimum_required(VERSION 3.13.4 FATAL_ERROR) project(Thorin) set(PACKAGE_VERSION "0.3.9") -set(CMAKE_CONFIGURATION_TYPES "Debug;Release" CACHE STRING "limited config" FORCE) +#set(CMAKE_CONFIGURATION_TYPES "Debug;Release" CACHE STRING "limited config" FORCE) set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS 1) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) @@ -26,9 +26,13 @@ find_package(Half REQUIRED) message(STATUS "Building with Half library from ${Half_INCLUDE_DIRS}.") # find json package for json output support. -find_package(nlohmann_json 3.2.0) -if(nlohmann_json_FOUND) +if(TARGET nlohmann_json) set(THORIN_ENABLE_JSON TRUE) +else() + find_package(nlohmann_json 3.2.0 QUIET) + if(nlohmann_json_FOUND) + set(THORIN_ENABLE_JSON TRUE) + endif() endif() # check for possible llvm extension diff --git a/cmake/thorin-config.cmake.in b/cmake/thorin-config.cmake.in index 121db87e8..60fa5ccc4 100644 --- a/cmake/thorin-config.cmake.in +++ b/cmake/thorin-config.cmake.in @@ -28,7 +28,7 @@ list(APPEND CMAKE_MODULE_PATH "${Thorin_ROOT_DIR}/cmake/modules") find_path(Half_DIR NAMES half.hpp PATHS ${Half_DIR} $ENV{Half_DIR} "@Half_DIR@" "@Half_INCLUDE_DIR@") find_package(Half REQUIRED) -set(Thorin_HAS_JSON_SUPPORT @nlohmann_json_FOUND@) +set(Thorin_HAS_JSON_SUPPORT @THORIN_ENABLE_JSON@) set(Thorin_HAS_LLVM_SUPPORT @LLVM_FOUND@) set(Thorin_HAS_RV_SUPPORT @RV_FOUND@) set(Thorin_HAS_SHADY_SUPPORT @shady_FOUND@) diff --git a/src/thorin/CMakeLists.txt b/src/thorin/CMakeLists.txt index c1adc632e..07e28605e 100644 --- a/src/thorin/CMakeLists.txt +++ b/src/thorin/CMakeLists.txt @@ -113,7 +113,7 @@ if (shady_FOUND) ) endif() -if(nlohmann_json_FOUND) +if(THORIN_ENABLE_JSON) list(APPEND THORIN_SOURCES be/json/json.cpp be/json/json.h @@ -139,6 +139,6 @@ if (shady_FOUND) target_link_libraries(thorin PRIVATE shady::shady) endif() -if(nlohmann_json_FOUND) +if(THORIN_ENABLE_JSON) target_link_libraries(thorin PRIVATE nlohmann_json::nlohmann_json) endif() From f8177297eb6fa850e48192e7b7c85f36f107b92e Mon Sep 17 00:00:00 2001 From: Matthias Kurtenacker Date: Thu, 16 Feb 2023 16:20:05 +0100 Subject: [PATCH 173/342] Shady as global subproject needs special support. --- CMakeLists.txt | 11 ++++++++--- cmake/thorin-config.cmake.in | 2 +- src/thorin/CMakeLists.txt | 10 +++++++--- 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 616e2877a..99f4f2948 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -54,9 +54,14 @@ else() message(STATUS "Building without LLVM and RV. Specify LLVM_DIR to compile with LLVM.") endif() -find_package(shady CONFIG) -if (shady_FOUND) - message(STATUS "Found shady at ${shady_DIR}") +if (NOT TARGET shady) + find_package(shady CONFIG) + if (shady_FOUND) + message(STATUS "Found shady at ${shady_DIR}") + set(THORIN_ENABLE_SHADY TRUE) + endif() +else() + export(TARGETS shady api FILE ${CMAKE_BINARY_DIR}/share/anydsl/cmake/shady-exports.cmake) set(THORIN_ENABLE_SHADY TRUE) endif() diff --git a/cmake/thorin-config.cmake.in b/cmake/thorin-config.cmake.in index 60fa5ccc4..3a53346dd 100644 --- a/cmake/thorin-config.cmake.in +++ b/cmake/thorin-config.cmake.in @@ -31,7 +31,7 @@ find_package(Half REQUIRED) set(Thorin_HAS_JSON_SUPPORT @THORIN_ENABLE_JSON@) set(Thorin_HAS_LLVM_SUPPORT @LLVM_FOUND@) set(Thorin_HAS_RV_SUPPORT @RV_FOUND@) -set(Thorin_HAS_SHADY_SUPPORT @shady_FOUND@) +set(Thorin_HAS_SHADY_SUPPORT @THORIN_ENABLE_SHADY@) set(AnyDSL_LLVM_LINK_SHARED @AnyDSL_LLVM_LINK_SHARED@) if(Thorin_HAS_LLVM_SUPPORT) diff --git a/src/thorin/CMakeLists.txt b/src/thorin/CMakeLists.txt index 07e28605e..f459e9ca8 100644 --- a/src/thorin/CMakeLists.txt +++ b/src/thorin/CMakeLists.txt @@ -107,7 +107,7 @@ if(LLVM_FOUND) ) endif() -if (shady_FOUND) +if (THORIN_ENABLE_SHADY) list(APPEND THORIN_SOURCES be/shady/shady.cpp ) @@ -135,8 +135,12 @@ if(LLVM_FOUND) llvm_config(thorin ${AnyDSL_LLVM_LINK_SHARED} ${Thorin_LLVM_COMPONENTS}) endif() -if (shady_FOUND) - target_link_libraries(thorin PRIVATE shady::shady) +if (THORIN_ENABLE_SHADY) + if (shady_FOUND) + target_link_libraries(thorin PRIVATE shady::shady) + else() + target_link_libraries(thorin PRIVATE shady) + endif() endif() if(THORIN_ENABLE_JSON) From 91703e0c10855b2eec93c24b0e747b9df307e4fa Mon Sep 17 00:00:00 2001 From: Matthias Kurtenacker Date: Fri, 27 Jan 2023 18:19:12 +0100 Subject: [PATCH 174/342] Add support for anyopt based linking by adding a CC "internal". Externals with CC == Internal will only be exported as such in the json backend. The intended use is to be able to combine multiple json files during compilation. --- src/thorin/be/c/c.cpp | 2 +- src/thorin/be/json/json.cpp | 8 ++++++-- src/thorin/be/llvm/llvm.cpp | 7 ++++++- src/thorin/be/shady/shady.cpp | 7 ++++++- src/thorin/continuation.h | 1 + src/thorin/rec_stream.cpp | 8 ++++++-- src/thorin/transform/partial_evaluation.cpp | 2 +- 7 files changed, 27 insertions(+), 8 deletions(-) diff --git a/src/thorin/be/c/c.cpp b/src/thorin/be/c/c.cpp index 62131e68a..f46c1f458 100644 --- a/src/thorin/be/c/c.cpp +++ b/src/thorin/be/c/c.cpp @@ -358,7 +358,7 @@ void CCodeGen::emit_module() { Scope::for_each(world(), [&] (const Scope& scope) { if (scope.entry()->name() == "hls_top") hls_top = scope.entry(); - else + else if (scope.entry()->cc() != CC::Internal) emit_scope(scope); }); if (hls_top) { diff --git a/src/thorin/be/json/json.cpp b/src/thorin/be/json/json.cpp index d289603df..e8ead38c8 100644 --- a/src/thorin/be/json/json.cpp +++ b/src/thorin/be/json/json.cpp @@ -230,8 +230,12 @@ class DefTable { forward_decl["type"] = "continuation"; forward_decl["fn_type"] = type; forward_decl["arg_names"] = arg_names; - if (cont->is_external()) - forward_decl["external"] = cont->name(); + if (cont->is_external()) { + if (cont->cc() == CC::Internal) + forward_decl["internal"] = cont->name(); + else + forward_decl["external"] = cont->name(); + } if (cont->cc() == CC::DeviceHostCode) forward_decl["device"] = cont->name(); decl_table.push_back(forward_decl); diff --git a/src/thorin/be/llvm/llvm.cpp b/src/thorin/be/llvm/llvm.cpp index dcbe63a6c..c21a4fdca 100644 --- a/src/thorin/be/llvm/llvm.cpp +++ b/src/thorin/be/llvm/llvm.cpp @@ -309,7 +309,12 @@ CodeGen::emit_module() { } } - Scope::for_each(world(), [&] (const Scope& scope) { emit_scope(scope); }); + Scope::for_each(world(), [&] (const Scope& scope) { + if(scope.entry()->cc() == CC::Internal) { + return; + } + emit_scope(scope); + }); if (debug()) dibuilder_.finalize(); diff --git a/src/thorin/be/shady/shady.cpp b/src/thorin/be/shady/shady.cpp index cd79f70c1..4654d7ac9 100644 --- a/src/thorin/be/shady/shady.cpp +++ b/src/thorin/be/shady/shady.cpp @@ -18,7 +18,12 @@ void CodeGen::emit_stream(std::ostream& out) { arena = shady::new_ir_arena(config); module = shady::new_module(arena, world().name().c_str()); - Scope::for_each(world(), [&](const Scope& scope) { emit_scope(scope); }); + Scope::for_each(world(), [&](const Scope& scope) { + if(scope.entry()->cc() == CC::Internal) { + return; + } + emit_scope(scope); + }); char* bufptr; size_t size; diff --git a/src/thorin/continuation.h b/src/thorin/continuation.h index 155e899f0..010d9bd8f 100644 --- a/src/thorin/continuation.h +++ b/src/thorin/continuation.h @@ -87,6 +87,7 @@ enum class CC : uint8_t { C, ///< C calling convention. Device, ///< Device calling convention. These are special functions only available on a particular device. DeviceHostCode, ///< Calling convention to denote continuations that are generated as device code. + Internal, ///< External, but only for linking with artic or anyopt. }; enum class Intrinsic : uint8_t { diff --git a/src/thorin/rec_stream.cpp b/src/thorin/rec_stream.cpp index 0965e5318..343ad4bd6 100644 --- a/src/thorin/rec_stream.cpp +++ b/src/thorin/rec_stream.cpp @@ -48,8 +48,12 @@ void RecStreamer::run() { auto cont = conts.pop(); s.endl().endl(); - if (cont->world().is_external(cont)) - s.fmt("extern "); + if (cont->world().is_external(cont)) { + if (cont->attributes().cc == CC::Internal) + s.fmt("intern "); + else + s.fmt("extern "); + } if (cont->has_body()) { std::vector param_names; diff --git a/src/thorin/transform/partial_evaluation.cpp b/src/thorin/transform/partial_evaluation.cpp index 5a18d3495..08c8d07ae 100644 --- a/src/thorin/transform/partial_evaluation.cpp +++ b/src/thorin/transform/partial_evaluation.cpp @@ -81,7 +81,7 @@ class CondEval { return true; } - return (!callee_->is_exported() && callee_->can_be_inlined()) || is_one(instantiate(filter(i))); + return ((!callee_->is_exported() || callee_->attributes().cc == CC::Internal) && callee_->can_be_inlined()) || is_one(instantiate(filter(i))); //return is_one(instantiate(filter(i))); } From c292516ed178b9c934dc91e81845aa3652d48d07 Mon Sep 17 00:00:00 2001 From: Matthias Kurtenacker Date: Tue, 21 Feb 2023 16:25:29 +0100 Subject: [PATCH 175/342] [JSON] divide emit_stream and emit_json for better runtime usability. --- src/thorin/be/json/json.cpp | 10 +++++++--- src/thorin/be/json/json.h | 1 + 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/thorin/be/json/json.cpp b/src/thorin/be/json/json.cpp index e8ead38c8..2271625d0 100644 --- a/src/thorin/be/json/json.cpp +++ b/src/thorin/be/json/json.cpp @@ -641,9 +641,7 @@ class DefTable { } }; -void CodeGen::emit_stream(std::ostream& stream) { - json j; - +void CodeGen::emit_json(json& j) { j["module"] = world().name(); if (target_triple != "") j["target_triple"] = target_triple; @@ -666,6 +664,12 @@ void CodeGen::emit_stream(std::ostream& stream) { j["defs"] = def_table.decl_table; for (auto it : def_table.def_table) j["defs"] += it; +} + +void CodeGen::emit_stream(std::ostream& stream) { + json j; + + emit_json(j); Stream s(stream); s << j.dump(2) << "\n"; diff --git a/src/thorin/be/json/json.h b/src/thorin/be/json/json.h index db976094b..ba11199ad 100644 --- a/src/thorin/be/json/json.h +++ b/src/thorin/be/json/json.h @@ -24,6 +24,7 @@ class CodeGen : public thorin::CodeGen { , target_attr(target_attr) {} + void emit_json(json& j); void emit_stream(std::ostream& stream) override; const char* file_ext() const override { From 01a4a46e48be38866824fa07b37e2329a0c62a41 Mon Sep 17 00:00:00 2001 From: Matthias Kurtenacker Date: Tue, 14 Mar 2023 17:48:24 +0100 Subject: [PATCH 176/342] Shady: Emit additional ops. --- src/thorin/be/shady/shady.cpp | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/thorin/be/shady/shady.cpp b/src/thorin/be/shady/shady.cpp index 4654d7ac9..a2d83740f 100644 --- a/src/thorin/be/shady/shady.cpp +++ b/src/thorin/be/shady/shady.cpp @@ -270,6 +270,8 @@ void CodeGen::emit_epilogue(Continuation* cont) { NodeVec args; for (auto& arg : body->args()) { if (convert(arg->type()) == nullptr) { + if (is_mem(arg)) + emit_unsafe(arg); args.push_back(nullptr); } else if (auto target = arg->isa_nom(); target && target->is_basicblock()) { // Emitting basic blocks as values isn't legal - but for convenience we'll put them in our list. @@ -382,7 +384,8 @@ const shady::Node* CodeGen::emit_bb(BB& bb, const Def* def) { type_arguments.push_back(convert(type_arg)); payload.operands = vec2nodes(operands); payload.type_arguments = vec2nodes(type_arguments); - return shady::first(shady::bind_instruction(bb.builder, shady::prim_op(arena, payload))); + auto ret = shady::bind_instruction(bb.builder, shady::prim_op(arena, payload)); + return ret.count ? shady::first(ret) : nullptr; }; if (auto prim_lit = def->isa()) { @@ -409,9 +412,9 @@ const shady::Node* CodeGen::emit_bb(BB& bb, const Def* def) { contents.push_back(emit(e)); } shady::ArrType payload; - const shady::Type* arr_type = shady::arr_type(arena, payload); payload.element_type = convert(arr->elem_type()); payload.size = shady::int32_literal(arena, contents.size()); + const shady::Type* arr_type = shady::arr_type(arena, payload); v = shady::composite(arena, arr_type, vec2nodes(contents)); } else if (auto cmp = def->isa()) { switch (cmp->cmp_tag()) { @@ -435,10 +438,21 @@ const shady::Node* CodeGen::emit_bb(BB& bb, const Def* def) { case ArithOp_shl: v = mk_primop(shady::Op::lshift_op, { arith->lhs(), arith->rhs() }); break; case ArithOp_shr: v = mk_primop(shady::Op::rshift_logical_op, { arith->lhs(), arith->rhs() }); break; } + } else if (auto store = def->isa()) { + mk_primop(shady::Op::store_op, { store->ptr(), store->val() }); + defs_[def] = nullptr; + return nullptr; + } else if (auto lea = def->isa()) { + v = mk_primop(shady::Op::lea_op, { lea->ptr(), world().zero(lea->index()->type()), lea->index() }); } else if (auto param = def->isa()) { assert(param->type() == world().mem_type()); defs_[def] = nullptr; return nullptr; + } else if (auto bitcast = def->isa()) { + v = emit(bitcast->from()); + } else { + def->dump(); + THORIN_UNREACHABLE; } assert(v && shady::is_value(v)); defs_[def] = v; From 2d400e8248c38863f91416f7e4a0cbc502e534b6 Mon Sep 17 00:00:00 2001 From: Matthias Kurtenacker Date: Mon, 23 Jan 2023 17:15:41 +0100 Subject: [PATCH 177/342] Fixed long memop chains in json emitter. --- src/thorin/be/json/json.cpp | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/thorin/be/json/json.cpp b/src/thorin/be/json/json.cpp index 2271625d0..2bd09fb21 100644 --- a/src/thorin/be/json/json.cpp +++ b/src/thorin/be/json/json.cpp @@ -183,6 +183,33 @@ class DefTable { return it->second; } + if (def->isa()) { + std::stack required_defs; + std::queue todo; + todo.push(def); + + while (!todo.empty()) { + auto def = todo.front(); + todo.pop(); + if (known_defs.lookup(def)) continue; + + if (auto memop = def->isa()) { + todo.push(memop->mem()); + required_defs.push(memop->mem()); + } else if (auto extract = def->isa()) { + if (is_mem(extract)) { + todo.push(extract->agg()); + required_defs.push(extract->agg()); + } + } + } + + while (!required_defs.empty()) { + auto r = pop(required_defs); + translate_def(r); + } + } + json result; if (auto cont = def->isa()) { if (cont->is_intrinsic()) { From 8d4f09b413342f2780adee585a5e100e94cc286c Mon Sep 17 00:00:00 2001 From: Matthias Kurtenacker Date: Thu, 2 Mar 2023 12:05:22 +0100 Subject: [PATCH 178/342] Support stack size changes. --- CMakeLists.txt | 3 +++ src/thorin/config.h.in | 1 + src/thorin/world.cpp | 21 +++++++++++++++++++++ src/thorin/world.h | 3 +++ 4 files changed, 28 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 99f4f2948..7939482bb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -54,6 +54,9 @@ else() message(STATUS "Building without LLVM and RV. Specify LLVM_DIR to compile with LLVM.") endif() +include(CheckIncludeFile) +check_include_file(sys/resource.h THORIN_ENABLE_RLIMITS) + if (NOT TARGET shady) find_package(shady CONFIG) if (shady_FOUND) diff --git a/src/thorin/config.h.in b/src/thorin/config.h.in index bfbe79d18..e8f2dfc42 100644 --- a/src/thorin/config.h.in +++ b/src/thorin/config.h.in @@ -8,5 +8,6 @@ #cmakedefine01 THORIN_ENABLE_JSON #cmakedefine01 THORIN_ENABLE_RV #cmakedefine01 THORIN_ENABLE_SHADY +#cmakedefine01 THORIN_ENABLE_RLIMITS #endif diff --git a/src/thorin/world.cpp b/src/thorin/world.cpp index 33ff8d2fa..8574c8eda 100644 --- a/src/thorin/world.cpp +++ b/src/thorin/world.cpp @@ -12,6 +12,10 @@ #include #include +#ifdef THORIN_ENABLE_RLIMITS +#include +#endif + #include "thorin/def.h" #include "thorin/primop.h" #include "thorin/continuation.h" @@ -1310,4 +1314,21 @@ void Thorin::opt() { RUN_PASS(codegen_prepare(world())) } +bool Thorin::ensure_stack_size(size_t new_size) { +#ifdef THORIN_ENABLE_RLIMITS + struct rlimit rl; + int result = getrlimit(RLIMIT_STACK, &rl); + if(result != 0) return false; + + rl.rlim_cur = new_size; + result = setrlimit(RLIMIT_STACK, &rl); + if(result != 0) return false; + + return true; +#else + return false; +#endif +} + + } diff --git a/src/thorin/world.h b/src/thorin/world.h index 4dc253ee5..56dda1c30 100644 --- a/src/thorin/world.h +++ b/src/thorin/world.h @@ -401,6 +401,9 @@ class Thorin { /// Performs dead code, unreachable code and unused type elimination. void cleanup(); void opt(); + + bool ensure_stack_size(size_t new_size); + private: std::unique_ptr world_; }; From ed05cc01568b720c19048b09e8a0c2040ba405b4 Mon Sep 17 00:00:00 2001 From: Matthias Kurtenacker Date: Tue, 1 Aug 2023 12:24:36 +0200 Subject: [PATCH 179/342] Fix warnings across the compiler. --- src/thorin/be/c/c.cpp | 2 +- src/thorin/be/llvm/llvm.cpp | 2 +- src/thorin/continuation.cpp | 2 +- src/thorin/def.cpp | 2 +- src/thorin/rec_stream.cpp | 2 +- src/thorin/transform/hls_kernel_launch.cpp | 2 +- src/thorin/transform/split_slots.cpp | 4 +++- src/thorin/type.cpp | 22 +++++++++++----------- src/thorin/type.h | 1 + src/thorin/world.cpp | 4 +++- 10 files changed, 24 insertions(+), 19 deletions(-) diff --git a/src/thorin/be/c/c.cpp b/src/thorin/be/c/c.cpp index f46c1f458..61e47d3e0 100644 --- a/src/thorin/be/c/c.cpp +++ b/src/thorin/be/c/c.cpp @@ -976,7 +976,7 @@ std::string CCodeGen::emit_def(BB* bb, const Def* def) { if (is_unit(def)) return ""; else if (auto bin = def->isa()) { - const char* op; + const char* op = ""; if (auto cmp = bin->isa()) { switch (cmp->cmp_tag()) { case Cmp_eq: op = "=="; break; diff --git a/src/thorin/be/llvm/llvm.cpp b/src/thorin/be/llvm/llvm.cpp index c21a4fdca..045763c3b 100644 --- a/src/thorin/be/llvm/llvm.cpp +++ b/src/thorin/be/llvm/llvm.cpp @@ -204,7 +204,7 @@ llvm::Type* CodeGen::convert(const Type* type) { size_t max_align = 0, max_size = 0; auto layout = module().getDataLayout(); - llvm::Type* max_align_type; + llvm::Type* max_align_type = llvm::Type::getVoidTy(context()); for (auto op : variant_type->types()) { auto op_type = convert(op); size_t size = layout.getTypeAllocSize(op_type); diff --git a/src/thorin/continuation.cpp b/src/thorin/continuation.cpp index 509cdfcd7..d82041aa8 100644 --- a/src/thorin/continuation.cpp +++ b/src/thorin/continuation.cpp @@ -18,7 +18,7 @@ Param::Param(World& world, const Type* type, const Continuation* continuation, s //set_op(0, continuation); } -const Def* Param::rebuild(World& world, const Type* t, Defs defs) const { +const Def* Param::rebuild(World&, const Type*, Defs defs) const { assert(defs.size() == 1); auto cont = defs[0]->as(); return cont->param(index()); diff --git a/src/thorin/def.cpp b/src/thorin/def.cpp index eb74c00a4..8915d42ba 100644 --- a/src/thorin/def.cpp +++ b/src/thorin/def.cpp @@ -138,7 +138,7 @@ bool is_minus_zero(const Def* def) { return false; } -void Def::rebuild_from(const Def* old, Defs new_ops) { +void Def::rebuild_from(const Def*, Defs new_ops) { assert(new_ops.size() == num_ops()); for (size_t i = 0; i < num_ops(); i++) set_op(i, new_ops[i]); diff --git a/src/thorin/rec_stream.cpp b/src/thorin/rec_stream.cpp index 343ad4bd6..29ae570cc 100644 --- a/src/thorin/rec_stream.cpp +++ b/src/thorin/rec_stream.cpp @@ -173,7 +173,7 @@ Stream& World::stream(Stream& s) const { return s.endl(); } -Stream& Scope::stream(Stream& s) const { +Stream& Scope::stream(Stream&) const { THORIN_UNREACHABLE; } diff --git a/src/thorin/transform/hls_kernel_launch.cpp b/src/thorin/transform/hls_kernel_launch.cpp index cab3bd7e3..413c24506 100644 --- a/src/thorin/transform/hls_kernel_launch.cpp +++ b/src/thorin/transform/hls_kernel_launch.cpp @@ -87,7 +87,7 @@ void hls_kernel_launch(World& world, DeviceParams& device_params) { if (auto hls_callee = has_hls_callee(block)) { auto cont_mem_obj = block->mem_param(); auto callee_continuation = hls_callee->isa_nom(); - Continuation* last_hls_cont; + Continuation* last_hls_cont = nullptr; if (!last_hls_found) { // TODO I'm at a loss for what is intended here. This is an assignment - not a check, the net result // is the _only the first_ block with an HLS callee will enter this, which means the first block in the schedule diff --git a/src/thorin/transform/split_slots.cpp b/src/thorin/transform/split_slots.cpp index 1f81e763d..87f643352 100644 --- a/src/thorin/transform/split_slots.cpp +++ b/src/thorin/transform/split_slots.cpp @@ -13,6 +13,7 @@ struct IndexHash { static u32 sentinel() { return 0xFFFFFFFF; } }; +#if 0 static void split(const Slot* slot) { auto array_type = slot->alloced_type()->as(); auto dim = array_type->dim(); @@ -67,8 +68,9 @@ static bool can_split(const Slot* slot) { return true; } +#endif -static bool split_slots(const Scope& scope) { +static bool split_slots(const Scope& /* scope */) { bool todo = false; // TODO #if 0 diff --git a/src/thorin/type.cpp b/src/thorin/type.cpp index de6bb3bcb..b940e5f11 100644 --- a/src/thorin/type.cpp +++ b/src/thorin/type.cpp @@ -46,20 +46,20 @@ Array defs2types(ArrayRef defs) { * rebuild */ -const Type* NominalType::rebuild(World& w, const Type* t, Defs o) const { +const Type* NominalType::rebuild(World& , const Type* , Defs ) const { THORIN_UNREACHABLE; } -const Type* BottomType ::rebuild(World& w, const Type* t, Defs o) const { return w.bottom_type(); } -const Type* ClosureType ::rebuild(World& w, const Type* t, Defs o) const { return w.closure_type(defs2types(o)); } -const Type* DefiniteArrayType ::rebuild(World& w, const Type* t, Defs o) const { return w.definite_array_type(o[0]->as(), dim()); } -const Type* FnType ::rebuild(World& w, const Type* t, Defs o) const { return w.fn_type(defs2types(o)); } -const Type* FrameType ::rebuild(World& w, const Type* t, Defs o) const { return w.frame_type(); } -const Type* IndefiniteArrayType::rebuild(World& w, const Type* t, Defs o) const { return w.indefinite_array_type(o[0]->as()); } -const Type* MemType ::rebuild(World& w, const Type* t, Defs o) const { return w.mem_type(); } -const Type* PrimType ::rebuild(World& w, const Type* t, Defs o) const { return w.prim_type(primtype_tag(), length()); } -const Type* PtrType ::rebuild(World& w, const Type* t, Defs o) const { return w.ptr_type(o[0]->as(), length(), device(), addr_space()); } -const Type* TupleType ::rebuild(World& w, const Type* t, Defs o) const { return w.tuple_type(defs2types(o)); } +const Type* BottomType ::rebuild(World& w, const Type* , Defs ) const { return w.bottom_type(); } +const Type* ClosureType ::rebuild(World& w, const Type* , Defs o) const { return w.closure_type(defs2types(o)); } +const Type* DefiniteArrayType ::rebuild(World& w, const Type* , Defs o) const { return w.definite_array_type(o[0]->as(), dim()); } +const Type* FnType ::rebuild(World& w, const Type* , Defs o) const { return w.fn_type(defs2types(o)); } +const Type* FrameType ::rebuild(World& w, const Type* , Defs ) const { return w.frame_type(); } +const Type* IndefiniteArrayType::rebuild(World& w, const Type* , Defs o) const { return w.indefinite_array_type(o[0]->as()); } +const Type* MemType ::rebuild(World& w, const Type* , Defs ) const { return w.mem_type(); } +const Type* PrimType ::rebuild(World& w, const Type* , Defs ) const { return w.prim_type(primtype_tag(), length()); } +const Type* PtrType ::rebuild(World& w, const Type* , Defs o) const { return w.ptr_type(o[0]->as(), length(), device(), addr_space()); } +const Type* TupleType ::rebuild(World& w, const Type* , Defs o) const { return w.tuple_type(defs2types(o)); } /* * stub diff --git a/src/thorin/type.h b/src/thorin/type.h index 96cdf49fa..1018a95c3 100644 --- a/src/thorin/type.h +++ b/src/thorin/type.h @@ -97,6 +97,7 @@ class NominalType : public Type { public: Symbol name() const { return name_; } + using Type::op_name; //Would be hidden otherwise. Symbol op_name(size_t i) const { return op_names_[i]; } void set_op_name(size_t i, Symbol name) const { const_cast(this)->op_names_[i] = name; diff --git a/src/thorin/world.cpp b/src/thorin/world.cpp index 8574c8eda..51de82e52 100644 --- a/src/thorin/world.cpp +++ b/src/thorin/world.cpp @@ -140,7 +140,9 @@ const Def* World::arithop(ArithOpTag tag, const Def* a, const Def* b, Debug dbg) } case ArithOp_mul: switch (type) { -#define THORIN_ALL_TYPE(T, M) case PrimType_##T: return literal(type, Box(T(l.get_##T() * r.get_##T())), dbg); +#define THORIN_P_TYPE(T, M) case PrimType_##T: return literal(type, Box(T(l.get_##T() * r.get_##T())), dbg); +#define THORIN_Q_TYPE(T, M) case PrimType_##T: return literal(type, Box(T(l.get_##T() * r.get_##T())), dbg); +#define THORIN_BOOL_TYPE(T, M) case PrimType_##T: return literal(type, Box(T(l.get_##T() && r.get_##T())), dbg); #include "thorin/tables/primtypetable.h" default: THORIN_UNREACHABLE; } From e2c74bb3ffc6ac69a568b7a22e38f188344db02f Mon Sep 17 00:00:00 2001 From: Matthias Kurtenacker Date: Fri, 30 Jun 2023 15:37:36 +0200 Subject: [PATCH 180/342] Fix World::extract. Indeces of different types need to be taken into account. This fixes an issue where some insertvalue instructions could be lost. --- src/thorin/world.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/thorin/world.cpp b/src/thorin/world.cpp index 51de82e52..53e0270da 100644 --- a/src/thorin/world.cpp +++ b/src/thorin/world.cpp @@ -709,9 +709,14 @@ const Def* World::extract(const Def* agg, const Def* index, Debug dbg) { if (auto insert = agg->isa()) { if (index == insert->index()) return insert->value(); - else if (index->template isa()) { - if (insert->index()->template isa()) - return extract(insert->agg(), index, dbg); + else if (auto index_lit = index->isa()) { + if (auto insert_index_lit = insert->index()->isa()) { + if (index_lit->value() == insert_index_lit->value()) { + return insert->value(); + } else { + return extract(insert->agg(), index, dbg); + } + } } } From 07602459177795916dd47f76404fe32ef1a3ed29 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Mon, 4 Sep 2023 15:49:30 +0200 Subject: [PATCH 181/342] Rewrote scoping analysis, added new debugging facilities --- src/thorin/CMakeLists.txt | 7 +- src/thorin/analyses/cfg.cpp | 4 +- src/thorin/analyses/cfg.h | 2 - src/thorin/analyses/domfrontier.cpp | 25 -- src/thorin/analyses/domfrontier.h | 50 --- src/thorin/analyses/schedule.cpp | 36 +- src/thorin/analyses/schedule.h | 10 +- src/thorin/analyses/scope.cpp | 364 ++++++++++++++++---- src/thorin/analyses/scope.h | 61 +++- src/thorin/analyses/verify.cpp | 39 ++- src/thorin/be/c/c.cpp | 8 +- src/thorin/be/codegen.cpp | 3 +- src/thorin/be/emitter.h | 14 +- src/thorin/be/llvm/llvm.cpp | 5 +- src/thorin/be/shady/shady.cpp | 5 +- src/thorin/rec_stream.cpp | 13 +- src/thorin/transform/cleanup_world.cpp | 3 +- src/thorin/transform/clone_bodies.cpp | 46 --- src/thorin/transform/clone_bodies.h | 12 - src/thorin/transform/codegen_prepare.cpp | 3 +- src/thorin/transform/dead_load_opt.cpp | 3 +- src/thorin/transform/hls_channels.cpp | 2 +- src/thorin/transform/hls_kernel_launch.cpp | 2 +- src/thorin/transform/hoist_enters.cpp | 32 +- src/thorin/transform/inliner.cpp | 2 +- src/thorin/transform/lift_builtins.cpp | 3 +- src/thorin/transform/partial_evaluation.cpp | 60 +--- src/thorin/transform/split_slots.cpp | 2 +- src/thorin/util/graphviz_dump.cpp | 315 +++++++++++++++++ src/thorin/util/scoped_dump.cpp | 161 +++++++++ src/thorin/util/scoped_dump.h | 52 +++ src/thorin/world.cpp | 2 - src/thorin/world.h | 9 + 33 files changed, 1018 insertions(+), 337 deletions(-) delete mode 100644 src/thorin/analyses/domfrontier.cpp delete mode 100644 src/thorin/analyses/domfrontier.h delete mode 100644 src/thorin/transform/clone_bodies.cpp delete mode 100644 src/thorin/transform/clone_bodies.h create mode 100644 src/thorin/util/graphviz_dump.cpp create mode 100644 src/thorin/util/scoped_dump.cpp create mode 100644 src/thorin/util/scoped_dump.h diff --git a/src/thorin/CMakeLists.txt b/src/thorin/CMakeLists.txt index f459e9ca8..e3247e5f7 100644 --- a/src/thorin/CMakeLists.txt +++ b/src/thorin/CMakeLists.txt @@ -16,8 +16,6 @@ set(THORIN_SOURCES world.h analyses/cfg.cpp analyses/cfg.h - analyses/domfrontier.cpp - analyses/domfrontier.h analyses/domtree.cpp analyses/domtree.h analyses/free_defs.cpp @@ -44,8 +42,6 @@ set(THORIN_SOURCES tables/mathoptable.h transform/cleanup_world.cpp transform/cleanup_world.h - transform/clone_bodies.cpp - transform/clone_bodies.h transform/closure_conversion.cpp transform/closure_conversion.h transform/codegen_prepare.h @@ -87,6 +83,9 @@ set(THORIN_SOURCES util/symbol.h util/types.h util/utility.h + util/graphviz_dump.cpp + util/scoped_dump.h + util/scoped_dump.cpp ) if(LLVM_FOUND) diff --git a/src/thorin/analyses/cfg.cpp b/src/thorin/analyses/cfg.cpp index e95b261f3..809944b60 100644 --- a/src/thorin/analyses/cfg.cpp +++ b/src/thorin/analyses/cfg.cpp @@ -6,7 +6,6 @@ #include #include "thorin/world.h" -#include "thorin/analyses/domfrontier.h" #include "thorin/analyses/domtree.h" #include "thorin/analyses/looptree.h" #include "thorin/analyses/scope.h" @@ -30,7 +29,7 @@ Stream& CFNode::stream(Stream& s) const { return s << continuation(); } CFA::CFA(const Scope& scope) : scope_(scope) , entry_(node(scope.entry())) - , exit_ (node(scope.exit() )) + , exit_ (node(scope.entry()->world().end_scope())) { std::queue cfg_queue; ContinuationSet cfg_done; @@ -194,7 +193,6 @@ template const CFNodes& CFG::preds(const CFNode* n) const template const CFNodes& CFG::succs(const CFNode* n) const { assert(n != nullptr); return forward ? n->succs() : n->preds(); } template const DomTreeBase& CFG::domtree() const { return lazy_init(this, domtree_); } template const LoopTree& CFG::looptree() const { return lazy_init(this, looptree_); } -template const DomFrontierBase& CFG::domfrontier() const { return lazy_init(this, domfrontier_); } template class CFG; template class CFG; diff --git a/src/thorin/analyses/cfg.h b/src/thorin/analyses/cfg.h index 1d1786d78..c61207553 100644 --- a/src/thorin/analyses/cfg.h +++ b/src/thorin/analyses/cfg.h @@ -132,7 +132,6 @@ class CFG { const CFNode* operator [] (Continuation* continuation) const { return cfa()[continuation]; } ///< Maps from @p l to @p CFNode. const DomTreeBase& domtree() const; const LoopTree& looptree() const; - const DomFrontierBase& domfrontier() const; static size_t index(const CFNode* n) { return forward ? n->f_index_ : n->b_index_; } @@ -143,7 +142,6 @@ class CFG { Map rpo_; mutable std::unique_ptr> domtree_; mutable std::unique_ptr> looptree_; - mutable std::unique_ptr> domfrontier_; }; //------------------------------------------------------------------------------ diff --git a/src/thorin/analyses/domfrontier.cpp b/src/thorin/analyses/domfrontier.cpp deleted file mode 100644 index 98e4d4f1d..000000000 --- a/src/thorin/analyses/domfrontier.cpp +++ /dev/null @@ -1,25 +0,0 @@ -#include "thorin/analyses/domfrontier.h" - -#include "thorin/analyses/domtree.h" - -namespace thorin { - -template -void DomFrontierBase::create() { - const auto& domtree = cfg().domtree(); - for (auto n : cfg().reverse_post_order().skip_front()) { - const auto& preds = cfg().preds(n); - if (preds.size() > 1) { - auto idom = domtree.idom(n); - for (auto pred : preds) { - for (auto i = pred; i != idom; i = domtree.idom(i)) - link(i, n); - } - } - } -} - -template class DomFrontierBase; -template class DomFrontierBase; - -} diff --git a/src/thorin/analyses/domfrontier.h b/src/thorin/analyses/domfrontier.h deleted file mode 100644 index 3014e1be8..000000000 --- a/src/thorin/analyses/domfrontier.h +++ /dev/null @@ -1,50 +0,0 @@ -#ifndef THORIN_ANALYSES_DOMFRONTIER_H -#define THORIN_ANALYSES_DOMFRONTIER_H - -#include "thorin/analyses/cfg.h" - -namespace thorin { - -/** - * A Dominance Frontier Graph. - * The template parameter @p forward determines whether to compute - * regular dominance frontiers or post-dominance frontiers (i.e. control dependence). - * This template parameter is associated with @p CFG's @c forward parameter. - * See Cooper et al, 2001. A Simple, Fast Dominance Algorithm: http://www.cs.rice.edu/~keith/EMBED/dom.pdf - */ -template -class DomFrontierBase { -public: - DomFrontierBase(const DomFrontierBase &) = delete; - DomFrontierBase& operator=(DomFrontierBase) = delete; - - explicit DomFrontierBase(const CFG &cfg) - : cfg_(cfg) - , preds_(cfg) - , succs_(cfg) - { - create(); - } - - const CFG& cfg() const { return cfg_; } - const std::vector& preds(const CFNode* n) const { return preds_[n]; } - const std::vector& succs(const CFNode* n) const { return succs_[n]; } - -private: - void create(); - void link(const CFNode* src, const CFNode* dst) { - succs_[src].push_back(dst); - preds_[dst].push_back(src); - } - - const CFG& cfg_; - typename CFG::template Map> preds_; - typename CFG::template Map> succs_; -}; - -typedef DomFrontierBase DomFrontiers; -typedef DomFrontierBase ControlDeps; - -} - -#endif diff --git a/src/thorin/analyses/schedule.cpp b/src/thorin/analyses/schedule.cpp index 798de7255..999d5da85 100644 --- a/src/thorin/analyses/schedule.cpp +++ b/src/thorin/analyses/schedule.cpp @@ -1,18 +1,17 @@ #include "thorin/analyses/schedule.h" -#include "thorin/config.h" #include "thorin/continuation.h" #include "thorin/primop.h" #include "thorin/world.h" -#include "thorin/analyses/cfg.h" #include "thorin/analyses/domtree.h" #include "thorin/analyses/looptree.h" #include "thorin/analyses/scope.h" namespace thorin { -Scheduler::Scheduler(const Scope& s) - : scope_(&s) +Scheduler::Scheduler(const Scope& s, ScopesForest& forest) + : forest_(&forest) + , scope_(&s) , cfg_(&scope().f_cfg()) , domtree_(&cfg().domtree()) { @@ -42,22 +41,26 @@ Scheduler::Scheduler(const Scope& s) enqueue(def, i, def->op(i)); } } + + register_defs(s); } -Continuation* Scheduler::early(const Def* def) { - if (auto cont = early_.lookup(def)) return *cont; - if (auto param = def->isa()) return early_[def] = param->continuation(); +void Scheduler::register_defs(const Scope& s) { + for (auto child : s.children_scopes()) { + Scope& cs = forest_->get_scope(child); + register_defs(cs); + } - auto result = scope().entry(); - for (auto op : def->as_structural()->ops()) { - if (!op->isa_nom() && def2uses_.find(op) != def2uses_.end()) { - auto cont = early(op); - if (domtree().depth(cfg(cont)) > domtree().depth(cfg(result))) - result = cont; - } + for (auto def : s.defs()) { + if (!early_.lookup(def)) + early_[def] = s.entry(); } +} - return early_[def] = result; +Continuation* Scheduler::early(const Def* def, DefSet* seen) { + if (auto cont = early_.lookup(def)) return *cont; + if (auto param = def->isa()) return early_[def] = param->continuation(); + assert(false); } Continuation* Scheduler::late(const Def* def) { @@ -68,6 +71,9 @@ Continuation* Scheduler::late(const Def* def) { result = continuation; } else if (auto param = def->isa()) { result = param->continuation(); + } else if (auto rec = def->isa_nom()) { + // don't try to late-schedule recursive nodes for now + result = early(def); } else { for (auto use : uses(def)) { auto cont = late(use); diff --git a/src/thorin/analyses/schedule.h b/src/thorin/analyses/schedule.h index b2cfa65aa..fd0ea8c96 100644 --- a/src/thorin/analyses/schedule.h +++ b/src/thorin/analyses/schedule.h @@ -11,7 +11,7 @@ using DomTree = DomTreeBase; class Scheduler { public: Scheduler() = default; - explicit Scheduler(const Scope&); + explicit Scheduler(const Scope&, ScopesForest&); /// @name getters //@{ @@ -19,18 +19,21 @@ class Scheduler { const F_CFG& cfg() const { return *cfg_; } const CFNode* cfg(Continuation* cont) const { return cfg()[cont]; } const DomTree& domtree() const { return *domtree_; } - const Uses& uses(const Def* def) const { return def2uses_.find(def)->second; } + const Uses& uses(const Def* def) const { assert(def2uses_.contains(def)); return def2uses_.find(def)->second; } //@} + void register_defs(const Scope&); + /// @name compute schedules //@{ - Continuation* early(const Def*); + Continuation* early(const Def*, DefSet* seen = nullptr); Continuation* late (const Def*); Continuation* smart(const Def*); //@} friend void swap(Scheduler& s1, Scheduler& s2) { using std::swap; + swap(s1.forest_, s2.forest_); swap(s1.scope_, s2.scope_); swap(s1.cfg_, s2.cfg_); swap(s1.domtree_, s2.domtree_); @@ -41,6 +44,7 @@ class Scheduler { } private: + ScopesForest* forest_ = nullptr; const Scope* scope_ = nullptr; const F_CFG* cfg_ = nullptr; const DomTree* domtree_ = nullptr; diff --git a/src/thorin/analyses/scope.cpp b/src/thorin/analyses/scope.cpp index 8fd518885..d9e7d6fc7 100644 --- a/src/thorin/analyses/scope.cpp +++ b/src/thorin/analyses/scope.cpp @@ -12,134 +12,354 @@ namespace thorin { -Scope::Scope(Continuation* entry) - : world_(entry->world()) - , entry_(entry) - , exit_(world().end_scope()) -{ +Scope::Scope(Continuation* entry) : world_(entry->world()), root(std::make_unique(world())), forest_(*root), entry_(entry) { run(); } +Scope::Scope(Continuation* entry, ScopesForest& forest) + : world_(entry->world()) + , forest_(forest) + , entry_(entry) +{} + Scope::~Scope() {} Scope& Scope::update() { defs_.clear(); - free_ = nullptr; + free_frontier_.clear(); + first_free_param_ = nullptr; free_params_ = nullptr; cfa_ = nullptr; run(); return *this; } -void Scope::run() { +DefSet Scope::potentially_contained() const { + DefSet potential_defs; std::queue queue; auto enqueue = [&] (const Def* def) { - if (defs_.insert(def).second) { + if (potential_defs.insert(def).second) { queue.push(def); - - if (auto continuation = def->isa_nom()) { - // when a continuation is part of this scope, we also enqueue its params, and we assert those to be unique - // TODO most likely redundant once params have the continuation in their ops - for (auto param : continuation->params()) { - auto p = defs_.insert(param); - assert_unused(p.second); - queue.push(param); - } - } } }; - enqueue(entry_); + for (auto param : entry()->params()) + enqueue(param); while (!queue.empty()) { auto def = pop(queue); - if (def != entry_) { - for (auto use : def->uses()) + if (def != entry()) { + for (auto use: def->uses()) enqueue(use); } } - enqueue(exit_); + return potential_defs; +} + +void Scope::run() { + DefSet potential_defs = potentially_contained(); + + unique_queue queue; + + if (entry()->has_body()) + queue.push(entry()->body()); + queue.push(entry()->filter()); + + defs_.insert(entry()); + for (auto p : entry()->params()) + defs_.insert(p); + + while (!queue.empty()) { + auto def = queue.pop(); + + if (potential_defs.contains(def)) { + defs_.insert(def); + for (auto op : def->ops()) + queue.push(op); + } else { + free_frontier_.insert(def); + } + } } -const DefSet& Scope::free() const { - if (!free_) { - free_ = std::make_unique(); +void Scope::verify() { + for (auto def : defs()) { + if (auto cont = def->isa_nom()) { + if (cont == entry()) + continue; + Scope& cont_scope = forest_.get_scope(cont); + assert(cont_scope.has_free_params()); + assert(!cont_scope.contains(entry())); + } + } +} - for (auto def : defs_) { - for (auto op : def->ops()) { - if (!contains(op)) - free_->emplace(op); +static auto first_or_null = [](ParamSet& set) -> const Param* { + for (auto p : set) { + return p; + } + return nullptr; +}; + +// searches for free variable in this scope, starting at the free frontier and transitively searching the scopes of the free continuations we encounter +// stop_after_first is used when we only care about whether this is a top-level scope (ie has no free params) or not, we can stop as soon as one param is found +template +std::tuple Scope::search_free_params() const { + if (free_params_) { + //world().WLOG("free variables analysis: reusing cached results for {}", entry()); + return std::make_tuple(*free_params_, true); + } + + if (stop_after_first && first_free_param_) { + ParamSet one_or_zero_params; + if (*first_free_param_ != nullptr) + one_or_zero_params.insert(first_free_param()); + return std::make_tuple(one_or_zero_params, true); + } + + ParamSet free_params; + /// as much as possible, we'd like to keep the results of those searches, but in the recursive case it's not always possible: + /// if there is a cycle, we stop when we encounter a continuation we're already searching the free variables for + /// this variable keeps track of whether we did that or not, if we didn't and this is a full search, we can safely save the results + bool thorough = true; + bool is_root = forest_.stack_.empty(); + //world().WLOG("free variables analysis: searching transitive ops of: {} (root={}, depth={})", entry(), root, forest_->stack_.size()); + forest_.stack_.push_back(entry()); + + unique_queue queue; + + for (auto def : free_frontier_) + queue.push(def); + + while (!queue.empty()) { + auto free_def = queue.pop(); + assert(!contains(free_def)); + + if (auto param = free_def->isa(); param && !param->continuation()->dead_) { + free_params.insert(param); + if (stop_after_first) + break; + } else if (auto cont = free_def->isa_nom()) { + // the free variables analysis can be recursive, but it's not necessary to inspect our own scope again ... + assert(cont != entry()); + + // if we hit the recursion wall, the results for this free variable search are only valid for the callee + if (std::find(forest_.stack_.begin(), forest_.stack_.end(), cont) != forest_.stack_.end()) { + //world().WLOG("free variables analysis: skipping {} to prevent infinite recursion", cont); + thorough = false; + continue; } + + Scope& scope = forest_.get_scope(cont); + assert(!scope.defs().empty() || !scope.entry()->has_body()); + + // When we have a free continuation in the body of our fn, their free variables are also free in us + auto [callee_free_params, callee_results_thorough] = scope.search_free_params(); + if (!is_root) + thorough &= callee_results_thorough; + + for (auto p: callee_free_params) { + // (those variables have to be free here! otherwise that continuation should be in this scope and not free) + if (contains(p)) { + world().WLOG("Potentially broken scoping: free variable {} showed up in the free variables of {} despite that continuation being part of its scope", p, entry()); + assert(false); + } + free_params.insert(p); + if (stop_after_first) + break; + } + } + else { + for (auto op : free_def->ops()) { + assert(op && "scope analysis doesn't work with unfinished nodes..."); + // the entry might be referenced by the outside world, but that's completely fine + if (op == entry()) + continue; + assert(!contains(op)); + queue.push(op); + } + } + } + + //world().WLOG("free variables analysis: done with : {} (hit_wall={})", entry(), hit_recursion_wall); + + assert(forest_.stack_.back() == entry()); + forest_.stack_.pop_back(); + + // save the results if we can + if (thorough) { + if (stop_after_first) { + first_free_param_ = std::make_optional(first_or_null(free_params)); + return std::make_tuple(free_params, true); + } else { + free_params_ = std::make_unique(std::move(free_params)); + return std::make_tuple(*free_params_, true); } } - return *free_; + for (auto fp : free_params) { + assert(fp->continuation() != entry()); + } + + return std::make_tuple(free_params, thorough); } const ParamSet& Scope::free_params() const { if (!free_params_) { - free_params_ = std::make_unique(); - unique_queue queue; - - auto enqueue = [&](const Def* def) { - if (auto param = def->isa(); param && !param->continuation()->dead_) - free_params_->emplace(param); - else if (def->isa()) - return; - else - queue.push(def); - }; - - for (auto def : free()) - enqueue(def); - while (!queue.empty()) { - for (auto op : queue.pop()->ops()) - enqueue(op); - } + auto [set, valid] = search_free_params(); + assert(valid); + free_params_ = std::make_unique(set); } return *free_params_; } +const Param* Scope::first_free_param() const { + if (!first_free_param_) { + // if we already computed the full free params list, let's reuse that ! + if (free_params_) { + first_free_param_ = std::make_optional(first_or_null(*free_params_)); + } else { + auto [set, valid] = search_free_params(); + assert(valid); + first_free_param_ = std::make_optional(first_or_null(set)); + } + } + + return *first_free_param_; +} + +bool Scope::has_free_params() const { + return first_free_param() != nullptr; +} + const CFA& Scope::cfa() const { return lazy_init(this, cfa_); } const F_CFG& Scope::f_cfg() const { return cfa().f_cfg(); } const B_CFG& Scope::b_cfg() const { return cfa().b_cfg(); } -template -void Scope::for_each(const World& world, std::function f) { - unique_queue continuation_queue; +Continuation* Scope::parent_scope() const { + if (!parent_scope_) { + ContinuationSet candidates_set; + std::queue candidates; - for (auto&& [_, def] : world.externals()) { - if (auto cont = def->template isa()) - if (cont->has_body()) continuation_queue.push(cont); - } + for (auto param : free_params()) { + if(candidates_set.insert(param->continuation()).second) + candidates.push(param->continuation()); + } - while (!continuation_queue.empty()) { - auto continuation = continuation_queue.pop(); - if (elide_empty && !continuation->has_body()) - continue; - Scope scope(continuation); - f(scope); + if (candidates.empty()) + parent_scope_ = std::make_optional(); + else { + while (true) { + auto candidate = pop(candidates); + // when there is only one candidate left, that's our parent + if (candidates.empty()) { + parent_scope_ = std::make_optional(candidate); + break; + } - unique_queue def_queue; - for (auto def : scope.free()) - def_queue.push(def); + auto other_candidate = pop(candidates); + assert(candidate != other_candidate); + if (forest_.get_scope(candidate).contains(other_candidate)) + candidates.push(other_candidate); + else { + assert(forest_.get_scope(other_candidate).contains(candidate) && "a scope cannot be nested in two unrelated parent scopes"); + candidates.push(candidate); + } + } + } + } - while (!def_queue.empty()) { - auto def = def_queue.pop(); - if (auto continuation = def->isa_nom()) - continuation_queue.push(continuation); + return *parent_scope_; +} + +ContinuationSet Scope::children_scopes() const { + ContinuationSet set; + for (auto def : defs()) { + if (auto cont = def->isa_nom()) { + auto& scope = forest_.get_scope(cont); + if (scope.parent_scope() == entry()) + set.insert(cont); else { - for (auto op : def->ops()) - def_queue.push(op); + //TODO: add check that the parent is eventually us! } } } + return set; +} + +template void ScopesForest::for_each ( std::function); +template void ScopesForest::for_each( std::function); + +Scope& ScopesForest::get_scope(Continuation* entry) { + if (scopes_.contains(entry)) { + auto existing = scopes_.find(entry); + assert((size_t)(existing->second.get()) != 0xbebebebe00000000); + return *existing->second; + } + auto scope = std::make_unique(entry, *this); + Scope* ptr = scope.get(); + ptr->run(); + scopes_[entry] = std::move(scope); + if (stack_.empty()) + ptr->verify(); + return *ptr; +} + +ContinuationSet ScopesForest::top_level_scopes() { + ContinuationSet set; + for (auto cont : world_.copy_continuations()) { + auto& scope = get_scope(cont); + assert(stack_.empty()); + if(!scope.has_free_params()) { + set.insert(cont); + } + assert(stack_.empty()); + } + return set; } -template void Scope::for_each (const World&, std::function); -template void Scope::for_each(const World&, std::function); +template +void ScopesForest::for_each(std::function f) { + for (auto cont : top_level_scopes()) { + if (elide_empty && !cont->has_body()) + continue; + auto& scope = get_scope(cont); + f(scope); + } +} + +DEBUG_UTIL void dump_scope_tree(Scope& s, ScopesForest* f = nullptr, int depth = 0) { + for (int i = 0; i < depth; i++) + std::cerr << " "; + std::unique_ptr sf; + if (!f) { + sf = std::make_unique(s.world()); + f = sf.get(); + } + + std::cerr << s.entry()->unique_name() << std::endl; + for (auto c : s.children_scopes()) { + auto& cs = f->get_scope(c); + dump_scope_tree(cs, f, depth + 1); + } +} + +DEBUG_UTIL void dump_scopes(World& w) { + ScopesForest forest(w); + std::vector q; + for (auto cont : w.copy_continuations()) { + auto& s = forest.get_scope(cont); + if (!s.parent_scope()) { + q.push_back(&s); + } + } + + for (auto root : q) { + dump_scope_tree(*root, &forest); + } +} } diff --git a/src/thorin/analyses/scope.h b/src/thorin/analyses/scope.h index fa301a457..c0aa9c7cf 100644 --- a/src/thorin/analyses/scope.h +++ b/src/thorin/analyses/scope.h @@ -16,6 +16,8 @@ typedef CFG B_CFG; class CFA; class CFNode; +class ScopesForest; + /** * A @p Scope represents a region of @p Continuation%s which are live from the view of an @p entry @p Continuation. * Transitively, all user's of the @p entry's parameters are pooled into this @p Scope. @@ -28,6 +30,7 @@ class Scope : public Streamable { Scope& operator=(Scope) = delete; explicit Scope(Continuation* entry); + explicit Scope(Continuation* entry, ScopesForest&); ~Scope(); /// Invoke if you have modified sth in this Scope. @@ -36,19 +39,23 @@ class Scope : public Streamable { //@{ misc getters World& world() const { return world_; } Continuation* entry() const { return entry_; } - Continuation* exit() const { return exit_; } + ScopesForest& forest() const { return forest_; } //@} //@{ get Def%s contained in this Scope const DefSet& defs() const { return defs_; } + const DefSet& free_frontier() const { return free_frontier_; } bool contains(const Def* def) const { return defs_.contains(def); } - /// All @p Def%s referenced but @em not contained in this @p Scope. - const DefSet& free() const; + //@} + /// All @p Param%s that appear free in this @p Scope. const ParamSet& free_params() const; /// Are there any free @p Param%s within this @p Scope. - bool has_free_params() const { return !free_params().empty(); } - //@} + bool has_free_params() const; + const Param* first_free_param() const; + + Continuation* parent_scope() const; + ContinuationSet children_scopes() const; //@{ simple CFA to construct a CFG const CFA& cfa() const; @@ -61,24 +68,50 @@ class Scope : public Streamable { Stream& stream(Stream&) const; ///< Streams thorin to file @p out. //@} + void verify(); +private: + void run(); + DefSet potentially_contained() const; + + template + std::tuple search_free_params() const; + + World& world_; + std::unique_ptr root; + ScopesForest& forest_; + Continuation* entry_ = nullptr; + DefSet defs_; + DefSet free_frontier_; + mutable std::optional first_free_param_; + mutable std::unique_ptr free_params_; + mutable std::unique_ptr cfa_; + mutable std::optional parent_scope_; + + friend ScopesForest; +}; + +class ScopesForest { +public: + ScopesForest(World& world) : world_(world) {} + + Scope& get_scope(Continuation* entry); + + ContinuationSet top_level_scopes(); + /** * Transitively visits all @em reachable Scope%s in @p world that do not have free variables. * We call these Scope%s @em top-level Scope%s. * Select with @p elide_empty whether you want to visit trivial Scope%s of Continuation%s without body. */ template - static void for_each(const World&, std::function); + void for_each(std::function); private: - void run(); - World& world_; - DefSet defs_; - Continuation* entry_ = nullptr; - Continuation* exit_ = nullptr; - mutable std::unique_ptr free_; - mutable std::unique_ptr free_params_; - mutable std::unique_ptr cfa_; + std::vector stack_; + ContinuationMap> scopes_; + + friend Scope; }; } diff --git a/src/thorin/analyses/verify.cpp b/src/thorin/analyses/verify.cpp index ba9a5f967..0eb3aee65 100644 --- a/src/thorin/analyses/verify.cpp +++ b/src/thorin/analyses/verify.cpp @@ -8,7 +8,7 @@ namespace thorin { // TODO this needs serious rewriting -static bool verify_calls(World& world) { +static bool verify_calls(World& world, ScopesForest& forest) { bool ok = true; for (auto def : world.defs()) { if (auto cont = def->isa()) @@ -17,16 +17,32 @@ static bool verify_calls(World& world) { return ok; } -static bool verify_top_level(World& world) { +static bool verify_top_level(World& world, ScopesForest& forest) { bool ok = true; - Scope::for_each(world, [&] (const Scope& scope) { - if (scope.has_free_params()) { - for (auto param : scope.free_params()) - world.ELOG("top-level continuation '{}' got free param '{}' belonging to continuation {}", scope.entry(), param, param->continuation()); - world.ELOG("here: {}", scope.entry()); - ok = false; + unique_queue defs; + for (auto& external : world.externals()) + defs.push(external.second); + while (!defs.empty()) { + auto def = defs.pop(); + if (auto cont = def->isa_nom()) { + world.VLOG("verifying external continuation '{}'", cont); + auto& scope = forest.get_scope(cont); + scope.verify(); + if (scope.has_free_params()) { + for (auto param : scope.free_params()) + world.ELOG("top-level continuation '{}' got free param '{}' belonging to continuation {}", scope.entry(), param, param->continuation()); + world.ELOG("here: {}", scope.entry()); + ok = false; + } + } else { + for (auto op : def->ops()) + defs.push(op); } - }); + } + for (auto cont : world.copy_continuations()) { + auto& scope = forest.get_scope(cont); + scope.verify(); + } return ok; } @@ -45,9 +61,10 @@ static bool verify_param(World& world) { } void verify(World& world) { + ScopesForest forest(world); bool ok = true; - ok &= verify_calls(world); - ok &= verify_top_level(world); + ok &= verify_calls(world, forest); + ok &= verify_top_level(world, forest); //TODO: This should not fail! //ok &= verify_param(world); if (!ok) diff --git a/src/thorin/be/c/c.cpp b/src/thorin/be/c/c.cpp index 61e47d3e0..ab6b503ad 100644 --- a/src/thorin/be/c/c.cpp +++ b/src/thorin/be/c/c.cpp @@ -75,6 +75,7 @@ class CCodeGen : public thorin::Emitter public: CCodeGen(Thorin& thorin, const Cont2Config& kernel_config, Stream& stream, Lang lang, bool debug, std::string& flags) : thorin_(thorin) + , forest_(world()) , kernel_config_(kernel_config) , lang_(lang) , fn_mem_(world().fn_type({world().mem_type()})) @@ -116,6 +117,7 @@ class CCodeGen : public thorin::Emitter std::string tuple_name(const TupleType*); Thorin& thorin_; + ScopesForest forest_; const Cont2Config& kernel_config_; Lang lang_; const FnType* fn_mem_; @@ -355,15 +357,15 @@ void CCodeGen::emit_module() { Continuation* hls_top = nullptr; interface_status = get_interface(interface, gmem_config); - Scope::for_each(world(), [&] (const Scope& scope) { + forest_.for_each([&] (const Scope& scope) { if (scope.entry()->name() == "hls_top") hls_top = scope.entry(); else if (scope.entry()->cc() != CC::Internal) - emit_scope(scope); + emit_scope(scope, forest_); }); if (hls_top) { hls_top_scope = true; - emit_scope(Scope(hls_top)); + emit_scope(Scope(hls_top), forest_); } if (lang_ == Lang::OpenCL) { diff --git a/src/thorin/be/codegen.cpp b/src/thorin/be/codegen.cpp index 56b4190d2..e1a155256 100644 --- a/src/thorin/be/codegen.cpp +++ b/src/thorin/be/codegen.cpp @@ -93,7 +93,8 @@ DeviceBackends::DeviceBackends(World& world, int opt, bool debug, std::string& f } // determine different parts of the world which need to be compiled differently - Scope::for_each(world, [&] (const Scope& scope) { + ScopesForest forest(world); + forest.for_each([&] (const Scope& scope) { auto continuation = scope.entry(); Continuation* imported = nullptr; diff --git a/src/thorin/be/emitter.h b/src/thorin/be/emitter.h index a13ad8d49..c138ad5e7 100644 --- a/src/thorin/be/emitter.h +++ b/src/thorin/be/emitter.h @@ -40,7 +40,8 @@ class Emitter { emit_unsafe(r); } - auto place = def->no_dep() ? entry_ : scheduler_.smart(def); + //auto place = def->no_dep() ? entry_ : scheduler_.smart(def); + auto place = !scheduler_.scope().contains(def) ? entry_ : scheduler_.smart(def); if (place) { auto& bb = cont2bb_[place]; @@ -73,22 +74,23 @@ class Emitter { return defs_[def] = val; } - void emit_scope(const Scope& scope) { + void emit_scope(const Scope& scope, ScopesForest& forest) { + scope_ = &scope; auto conts = schedule(scope); entry_ = scope.entry(); - assert(entry_->is_returning()); + //assert(entry_->is_returning()); auto fct = child().prepare(scope); for (auto cont : conts) { if (cont->intrinsic() != Intrinsic::EndScope) child().prepare(cont, fct); } - Scheduler new_scheduler(scope); + Scheduler new_scheduler(scope, forest); swap(scheduler_, new_scheduler); for (auto cont : conts) { if (cont->intrinsic() == Intrinsic::EndScope) continue; - assert(cont == entry_ || cont->is_basicblock()); + //assert(cont == entry_ || cont->is_basicblock()); child().emit_epilogue(cont); } @@ -96,6 +98,7 @@ class Emitter { if (cont->intrinsic() != Intrinsic::EndScope) child().finalize(cont); } child().finalize(scope); + scope_ = nullptr; } Scheduler scheduler_; @@ -103,6 +106,7 @@ class Emitter { DefMap types_; ContinuationMap cont2bb_; Continuation* entry_ = nullptr; + const Scope* scope_ = nullptr; }; } diff --git a/src/thorin/be/llvm/llvm.cpp b/src/thorin/be/llvm/llvm.cpp index 045763c3b..e1b36028a 100644 --- a/src/thorin/be/llvm/llvm.cpp +++ b/src/thorin/be/llvm/llvm.cpp @@ -309,11 +309,12 @@ CodeGen::emit_module() { } } - Scope::for_each(world(), [&] (const Scope& scope) { + ScopesForest forest(world()); + forest.for_each([&](const Scope& scope) { if(scope.entry()->cc() == CC::Internal) { return; } - emit_scope(scope); + emit_scope(scope, forest); }); if (debug()) dibuilder_.finalize(); diff --git a/src/thorin/be/shady/shady.cpp b/src/thorin/be/shady/shady.cpp index a2d83740f..297606171 100644 --- a/src/thorin/be/shady/shady.cpp +++ b/src/thorin/be/shady/shady.cpp @@ -18,11 +18,12 @@ void CodeGen::emit_stream(std::ostream& out) { arena = shady::new_ir_arena(config); module = shady::new_module(arena, world().name().c_str()); - Scope::for_each(world(), [&](const Scope& scope) { + ScopesForest forest(world()); + forest.for_each([&](const Scope& scope) { if(scope.entry()->cc() == CC::Internal) { return; } - emit_scope(scope); + emit_scope(scope, forest); }); char* bufptr; diff --git a/src/thorin/rec_stream.cpp b/src/thorin/rec_stream.cpp index 29ae570cc..037891722 100644 --- a/src/thorin/rec_stream.cpp +++ b/src/thorin/rec_stream.cpp @@ -55,10 +55,18 @@ void RecStreamer::run() { s.fmt("extern "); } + Scope scope(cont); + if (!scope.has_free_params()) + s.fmt("top_level "); + else { + s.fmt("// free variables: {, }\n", scope.free_params()); + s.fmt("// free frontier: {, }\n", scope.free_frontier()); + } + if (cont->has_body()) { std::vector param_names; for (auto param : cont->params()) param_names.push_back(param->unique_name()); - s.fmt("{}: {} = ({, }) => {{\t\n", cont->unique_name(), cont->type(), param_names); + s.fmt("{}: {} = ({, }) @({}) => {{\t\n", cont->unique_name(), cont->type(), param_names, cont->filter()); run(cont->filter()); if (defs.contains(cont->body())) { auto body = cont->body(); @@ -179,6 +187,7 @@ Stream& Scope::stream(Stream&) const { Stream& Type::stream(Stream& s) const { if (false) {} + else if (isa()) return s.fmt("*"); else if (isa()) return s.fmt("!!"); else if (isa< MemType>()) return s.fmt("mem"); else if (isa< FrameType>()) return s.fmt("frame"); @@ -221,8 +230,6 @@ Stream& Type::stream(Stream& s) const { if (t->is_vector()) s.fmt(">"); return s; - } else if (isa()) { - return s.fmt("★"); } THORIN_UNREACHABLE; } diff --git a/src/thorin/transform/cleanup_world.cpp b/src/thorin/transform/cleanup_world.cpp index 635b34a56..9a8eeaa99 100644 --- a/src/thorin/transform/cleanup_world.cpp +++ b/src/thorin/transform/cleanup_world.cpp @@ -36,7 +36,8 @@ class Cleaner { }; void Cleaner::eliminate_tail_rec() { - Scope::for_each(world(), [&](Scope& scope) { + ScopesForest forest(world()); + forest.for_each([&](Scope& scope) { auto entry = scope.entry(); bool only_tail_calls = true; diff --git a/src/thorin/transform/clone_bodies.cpp b/src/thorin/transform/clone_bodies.cpp deleted file mode 100644 index 6b48fc456..000000000 --- a/src/thorin/transform/clone_bodies.cpp +++ /dev/null @@ -1,46 +0,0 @@ -#include "thorin/world.h" -#include "thorin/analyses/scope.h" -#include "thorin/transform/mangle.h" - -namespace thorin { - -/* - * TODO rewrite - */ - -// TODO merge this with lift_builtins -void clone_bodies(World& /*world*/) { -#if 0 - std::vector todo; - - // TODO this looks broken: I guess we should do that in post-order as in lift_builtins - for (auto continuation : world.copy_continuations()) { - if (is_passed_to_accelerator(continuation)) - todo.push_back(continuation); - } - - for (auto continuation : todo) { - Scope scope(continuation); - bool first = true; - for (auto use : continuation->copy_uses()) { - if (first) { - first = false; // re-use the initial continuation as first clone - } else { - auto ncontinuation = clone(scope); - if (auto uapp = use->isa()) { - if (uapp->is_replaced()) continue; // dead app node - auto napp = uapp->with_different_op(use.index(), ncontinuation); - uapp->replace(napp); - } else if (auto primop = use->isa()) { - Array nops(primop->num_ops()); - std::copy(primop->ops().begin(), primop->ops().end(), nops.begin()); - nops[use.index()] = ncontinuation; - primop->replace(primop->rebuild(world, primop->type(), nops)); - } - } - } - } -#endif -} - -} diff --git a/src/thorin/transform/clone_bodies.h b/src/thorin/transform/clone_bodies.h deleted file mode 100644 index 063815c66..000000000 --- a/src/thorin/transform/clone_bodies.h +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef THORIN_TRANSFORM_CLONE_BODIES_H -#define THORIN_TRANSFORM_CLONE_BODIES_H - -namespace thorin { - -class World; - -void clone_bodies(World&); - -} - -#endif diff --git a/src/thorin/transform/codegen_prepare.cpp b/src/thorin/transform/codegen_prepare.cpp index 95fbbb972..6ae0401d9 100644 --- a/src/thorin/transform/codegen_prepare.cpp +++ b/src/thorin/transform/codegen_prepare.cpp @@ -5,7 +5,8 @@ namespace thorin { void codegen_prepare(World& world) { world.VLOG("start codegen_prepare"); - Scope::for_each(world, [&](Scope& scope) { + ScopesForest forest(world); + forest.for_each([&](Scope& scope) { world.DLOG("scope: {}", scope.entry()); bool dirty = false; auto ret_param = scope.entry()->ret_param(); diff --git a/src/thorin/transform/dead_load_opt.cpp b/src/thorin/transform/dead_load_opt.cpp index c88830b79..d3a83e3a8 100644 --- a/src/thorin/transform/dead_load_opt.cpp +++ b/src/thorin/transform/dead_load_opt.cpp @@ -37,7 +37,8 @@ static void dead_load_opt(const Scope& scope) { } void dead_load_opt(World& world) { - Scope::for_each(world, [&] (const Scope& scope) { dead_load_opt(scope); }); + ScopesForest forest(world); + forest.for_each([&] (const Scope& scope) { dead_load_opt(scope); }); } } diff --git a/src/thorin/transform/hls_channels.cpp b/src/thorin/transform/hls_channels.cpp index bae0f5291..f4c891133 100644 --- a/src/thorin/transform/hls_channels.cpp +++ b/src/thorin/transform/hls_channels.cpp @@ -160,7 +160,7 @@ DeviceParams hls_channels(Thorin& thorin, Importer& importer, Top2Kernel& top2ke Def2Def arg2param; - Scope::for_each(world, [&] (Scope& scope) { + ScopesForest(world).for_each([&] (Scope& scope) { auto old_kernel = scope.entry(); Def2Mode def2mode; extract_kernel_channels(schedule(scope), def2mode); diff --git a/src/thorin/transform/hls_kernel_launch.cpp b/src/thorin/transform/hls_kernel_launch.cpp index 413c24506..94c3f32ed 100644 --- a/src/thorin/transform/hls_kernel_launch.cpp +++ b/src/thorin/transform/hls_kernel_launch.cpp @@ -76,7 +76,7 @@ void hls_kernel_launch(World& world, DeviceParams& device_params) { const size_t base_opencl_param_num = 6; Array opencl_args(base_opencl_param_num + device_params.size()); - Scope::for_each(world, [&] (Scope& scope) { + ScopesForest(world).for_each([&] (Scope& scope) { Schedule scheduled = schedule(scope); for (auto& block : scheduled) { diff --git a/src/thorin/transform/hoist_enters.cpp b/src/thorin/transform/hoist_enters.cpp index a51cf8d64..3f8997915 100644 --- a/src/thorin/transform/hoist_enters.cpp +++ b/src/thorin/transform/hoist_enters.cpp @@ -24,41 +24,53 @@ static void find_enters(std::deque& enters, Continuation* continua find_enters(enters, mem_param); } -static void hoist_enters(const Scope& scope) { +static bool hoist_enters(const Scope& scope) { World& world = scope.world(); std::deque enters; for (auto n : scope.f_cfg().reverse_post_order()) find_enters(enters, n->continuation()); - if (enters.empty() || enters[0]->mem() != scope.entry()->mem_param()) { world.VLOG("cannot optimize {} - didn't find entry enter", scope.entry()); - return; + return false; } auto entry_enter = enters[0]; auto frame = entry_enter->out_frame(); enters.pop_front(); + bool todo = false; for (auto i = enters.rbegin(), e = enters.rend(); i != e; ++i) { auto old_enter = *i; for (auto use : old_enter->out_frame()->uses()) { auto slot = use->as(); + if (slot->uses().size() > 0) + todo = true; slot->replace_uses(world.slot(slot->alloced_type(), frame, slot->debug())); assert(slot->num_uses() == 0); } } - - for (auto i = enters.rbegin(), e = enters.rend(); i != e; ++i) - (*i)->out_mem()->replace_uses((*i)->mem()); - - if (frame->num_uses() == 0) - entry_enter->out_mem()->replace_uses(entry_enter->mem()); + return todo; } +// TODO: rewrite this and put it out of its misery void hoist_enters(Thorin& thorin) { - Scope::for_each(thorin.world(), [] (const Scope& scope) { hoist_enters(scope); }); + bool todo = false; + do { + todo = false; + ScopesForest forest(thorin.world()); + for (auto cont : thorin.world().copy_continuations()) { + if (!cont->has_body()) + continue; + auto& scope = forest.get_scope(cont); + if(!scope.has_free_params()) { + if (!todo) todo = hoist_enters(scope); + if (todo) + break; + } + } + } while (todo); thorin.cleanup(); } diff --git a/src/thorin/transform/inliner.cpp b/src/thorin/transform/inliner.cpp index d499051e0..ffaa8f309 100644 --- a/src/thorin/transform/inliner.cpp +++ b/src/thorin/transform/inliner.cpp @@ -69,7 +69,7 @@ void inliner(Thorin& thorin) { return nullptr; }; - Scope::for_each(world, [&] (Scope& scope) { + ScopesForest(world).for_each([&] (Scope& scope) { bool dirty = false; for (auto n : scope.f_cfg().post_order()) { auto continuation = n->continuation(); diff --git a/src/thorin/transform/lift_builtins.cpp b/src/thorin/transform/lift_builtins.cpp index 0ececb949..8f21d44ee 100644 --- a/src/thorin/transform/lift_builtins.cpp +++ b/src/thorin/transform/lift_builtins.cpp @@ -70,7 +70,8 @@ void lift_builtins(Thorin& thorin) { while (true) { World& world = thorin.world(); Continuation* cur = nullptr; - Scope::for_each(world, [&] (const Scope& scope) { + ScopesForest forest(world); + forest.for_each([&] (const Scope& scope) { if (cur) return; for (auto n : scope.f_cfg().post_order()) { if (n->continuation()->order() <= 1) diff --git a/src/thorin/transform/partial_evaluation.cpp b/src/thorin/transform/partial_evaluation.cpp index 08c8d07ae..0968e45b1 100644 --- a/src/thorin/transform/partial_evaluation.cpp +++ b/src/thorin/transform/partial_evaluation.cpp @@ -2,6 +2,7 @@ #include "thorin/world.h" #include "thorin/transform/mangle.h" #include "thorin/util/hash.h" +#include "partial_evaluation.h" namespace thorin { @@ -18,14 +19,12 @@ class PartialEvaluator { PartialEvaluator(World& world, bool lower2cff) : world_(world) , lower2cff_(lower2cff) - , boundary_(Def::gid_counter()) {} World& world() { return world_; } bool run(); void enqueue(Continuation* continuation) { - if (continuation->gid() < 2 * boundary_ && done_.emplace(continuation).second) - queue_.push(continuation); + queue_.push(continuation); } void eat_pe_info(Continuation*); @@ -34,16 +33,14 @@ class PartialEvaluator { bool lower2cff_; HashMap cache_; ContinuationSet done_; - std::queue queue_; - ContinuationMap top_level_; - size_t boundary_; + unique_queue queue_; }; class CondEval { public: - CondEval(Continuation* callee, Defs args, ContinuationMap& top_level) + CondEval(Continuation* callee, ScopesForest& forest, Defs args) : callee_(callee) - , top_level_(top_level) + , forest_(forest) { assert(callee->filter()->is_empty() || callee->filter()->size() == args.size()); assert(callee->num_params() == args.size()); @@ -75,11 +72,11 @@ class CondEval { auto order = callee_->param(i)->order(); if (lower2cff) if(order >= 2 || (order == 1 - && (!callee_->param(i)->type()->isa() - || (!callee_->is_returning() || (!is_top_level(callee_)))))) { - world().DLOG("bad param({}) {} of continuation {}", i, callee_->param(i), callee_); - return true; - } + && (!callee_->param(i)->type()->isa() + || (!callee_->is_returning() || (!is_top_level(callee_)))))) { + world().DLOG("bad param({}) {} of continuation {}", i, callee_->param(i), callee_); + return true; + } return ((!callee_->is_exported() || callee_->attributes().cc == CC::Internal) && callee_->can_be_inlined()) || is_one(instantiate(filter(i))); //return is_one(instantiate(filter(i))); @@ -90,39 +87,13 @@ class CondEval { } bool is_top_level(Continuation* continuation) { - auto p = top_level_.emplace(continuation, true); - if (!p.second) - return p.first->second; - - Scope scope(continuation); - unique_queue queue; - - for (auto def : scope.free()) - queue.push(def); - - while (!queue.empty()) { - auto def = queue.pop(); - - if (def->isa()) // if FV in this scope is a param, this cont can't be top-level - return top_level_[continuation] = false; - if (auto free_cn = def->isa_nom()) { - // if we have a non-top level continuation in scope as a free variable, - // then it must be bound by some outer continuation, and so we aren't top-level - if (!is_top_level(free_cn)) - return top_level_[continuation] = false; - } else { - for (auto op : def->ops()) - queue.push(op); - } - } - - return top_level_[continuation] = true; + return !forest_.get_scope(continuation).has_free_params(); } private: Continuation* callee_; Def2Def old2new_; - ContinuationMap& top_level_; + ScopesForest& forest_; }; void PartialEvaluator::eat_pe_info(Continuation* cur) { @@ -151,11 +122,10 @@ bool PartialEvaluator::run() { if (!cont) continue; if (!cont->has_body()) continue; enqueue(cont); - top_level_[cont] = true; } while (!queue_.empty()) { - auto continuation = pop(queue_); + auto continuation = queue_.pop(); bool force_fold = false; @@ -176,7 +146,9 @@ bool PartialEvaluator::run() { } if (callee->has_body()) { - CondEval cond_eval(callee, body->args(), top_level_); + // TODO cache the forest and only rebuild it when we need to + ScopesForest forest(world()); + CondEval cond_eval(callee, forest, body->args()); std::vector specialize(body->num_args()); diff --git a/src/thorin/transform/split_slots.cpp b/src/thorin/transform/split_slots.cpp index 87f643352..054587755 100644 --- a/src/thorin/transform/split_slots.cpp +++ b/src/thorin/transform/split_slots.cpp @@ -92,7 +92,7 @@ void split_slots(Thorin& thorin) { bool todo = true; while (todo) { todo = false; - Scope::for_each(thorin.world(), [&] (const Scope& scope) { todo |= split_slots(scope); }); + ScopesForest(thorin.world()).for_each([&] (const Scope& scope) { todo |= split_slots(scope); }); thorin.cleanup(); } } diff --git a/src/thorin/util/graphviz_dump.cpp b/src/thorin/util/graphviz_dump.cpp new file mode 100644 index 000000000..d85ea4bb4 --- /dev/null +++ b/src/thorin/util/graphviz_dump.cpp @@ -0,0 +1,315 @@ +#ifndef DOT_DUMP_H +#define DOT_DUMP_H + +#include "thorin/world.h" +#include "thorin/analyses/scope.h" + +namespace thorin { + +/// Outputs the raw thorin IR as a graph without performing any scope or scheduling analysis +struct DotPrinter { + DotPrinter(World& world, const char* filename = "world.dot") : world_(world), forest_(world) { + file = std::ofstream(filename); + begin(); + } + + ~DotPrinter() { + end(); + } +private: + int indent = 0; + std::string endl() { + std::string s = "\n"; + for (int i = 0; i < indent; i++) + s += " "; + return s; + } + std::string up() { + indent++; + return ""; + } + std::string down() { + indent--; + return ""; + } + + void begin() { + file << "digraph " << "world" << " {" << up(); + file << endl() << "bgcolor=transparent;"; + } + + void end() { + file << arrows.str(); + file << down() << endl() << "}" << endl(); + } + + std::string def_id(const Def* def) { + return std::string(tag2str(def->tag())) + "_" + std::to_string(def->gid()); + } + + std::string emit_def(const Def* def); + std::string dump_literal(const Literal*); + std::string dump_continuation(Continuation* cont); + + std::stringstream arrows; + + void arrow(const std::string& src, const std::string& dst, const std::string& extra) { + if (src.empty() || dst.empty()) + return; + arrows << endl() << src << " -> " << dst << " " << extra << ";"; + } +public: + std::string dump_def(const Def* def); + + void run() { + delay_printing_ops = false; + while (!todo.empty()) { + auto def = todo.pop(); + dump_def(def); + } + } + + void print_scope(Scope& scope) { + file << "subgraph cluster_" << u_++ << " {" << up() << endl(); + + auto cont = scope.entry(); + emit_def(cont); + + for (auto child : scope.children_scopes()) + print_scope(scope.forest().get_scope(child)); + + for (auto def : scope.defs()) { + if (!done.contains(def) && !def->isa()) { + emit_def(def); + } + } + + file << down() << endl() << "}" << endl(); + }; + + bool print_lower_order_args = true; + bool print_instanced_filters = false; + bool print_literals = true; + bool delay_printing_ops = true; + Scope* single_scope = nullptr; + +private: + thorin::World& world_; + ScopesForest forest_; + + int u_ = 0; + + unique_queue todo; + DefMap done; + std::ofstream file; +}; + +std::string DotPrinter::dump_def(const Def* def) { + if (done.contains(def)) + return done[def]; + + if (delay_printing_ops) { + todo.push(def); + return def_id(def); + } + + return emit_def(def); +} + +std::string DotPrinter::emit_def(const Def* def) { + assert(!done.contains(def)); + if (auto cont = def->isa_nom()) + return dump_continuation(cont); + else if (def->isa()) + return dump_literal(def->as()); + else { + // default (primops) + std::string color = ""; + std::string fillcolor = "darkseagreen1"; + std::string style = "filled"; + std::string shape = "oval"; + std::string label = std::string(def->op_name()) + " :: " + def->name(); + + std::unique_ptr>> filtered_ops; + + if (single_scope && single_scope->free_frontier().contains(def)) + color = "blue"; + + if (def->isa()) { + fillcolor = "grey"; + shape = "oval"; + label = def->unique_name(); + } else if (auto app = def->isa()) { + fillcolor = "darkgreen"; + + filtered_ops = std::make_unique>>(); + + if (auto callee_cont = app->callee()->isa_nom()) { + switch (callee_cont->intrinsic()) { + case Intrinsic::Branch: { + fillcolor = "lightblue"; + label = "branch"; + if (print_lower_order_args) { + (*filtered_ops).emplace_back("mem", app->arg(0)); + (*filtered_ops).emplace_back("condition", app->arg(1)); + } + (*filtered_ops).emplace_back("true", app->arg(2)); + (*filtered_ops).emplace_back("false", app->arg(3)); + + goto print_node; + } + case Intrinsic::Match: { + fillcolor = "lightblue"; + label = "match"; + + if (print_lower_order_args) { + (*filtered_ops).emplace_back("mem", app->arg(0)); + (*filtered_ops).emplace_back("inspectee", app->arg(1)); + } + + (*filtered_ops).emplace_back("default_case", app->arg(2)); + for (size_t i = 3; i < app->num_args(); i+=2) { + if (print_lower_order_args) + (*filtered_ops).emplace_back("case", app->arg(i)); + (*filtered_ops).emplace_back("case", app->arg(i + 1)); + } + + goto print_node; + } + default: break; + } + } + + (*filtered_ops).emplace_back("callee", app->callee()); + for (size_t i = 0; i < app->num_args(); i++) { + if (print_lower_order_args || app->arg(i)->type()->order() >= 1) + (*filtered_ops).emplace_back("arg"+std::to_string(i), app->arg(i)); + } + } else if (auto variant_ctor = def->isa()) { + label = "variant(" + std::to_string(variant_ctor->index()) + ")"; + } else if (auto variant_extract = def->isa()) { + label = "variant_extract(" + std::to_string(variant_extract->index()) + ")"; + } + + print_node: + + file << endl() << def_id(def) << " [" << up(); + + file << endl() << "label = \""; + file << label; + file << "\";"; + + file << endl() << "shape = " << shape << ";"; + file << endl() << "style = " << style << ";"; + file << endl() << "fillcolor = " << fillcolor << ";"; + if (color != "") + file << endl() << "color = " << color << ";"; + + file << down() << endl() << "]"; + done.emplace(def, def_id(def)); + + if (!filtered_ops) { + for (size_t i = 0; i < def->num_ops(); i++) { + const auto& op = def->op(i); + arrow(def_id(def), dump_def(op), "[arrowhead=vee,label=\"o" + std::to_string(i) + "\",fontsize=8,fontcolor=grey]"); + } + } else { + for (auto [edge_label, op] : *filtered_ops) { + arrow(def_id(def), dump_def(op), "[arrowhead=vee,label=\"" + edge_label + "\",fontsize=8,fontcolor=grey]"); + } + } + + return def_id(def); + } +} + +std::string DotPrinter::dump_literal(const Literal* def) { + if (!print_literals) + return ""; + assert(def->num_ops() == 0); + file << endl() << def_id(def) << " [" << up(); + + file << endl() << "label = \""; + file << def->to_string(); + file << "\";"; + + file << endl() << "style = dotted;"; + + file << down() << endl() << "]"; + + done.emplace(def, def_id(def)); + return def_id(def); +} + +std::string DotPrinter::dump_continuation(Continuation* cont) { + done.emplace(cont, def_id(cont)); + auto intrinsic = cont->intrinsic(); + file << endl() << def_id(cont) << " [" << up(); + + file << endl() << "label = \""; + if (cont->is_external()) + file << "[extern]\\n"; + auto name = cont->name(); + if (!cont->is_external()) + name = cont->unique_name(); + + file << name << "("; + for (size_t i = 0; i < cont->num_params(); i++) { + file << cont->param(i)->type()->to_string() << (i + 1 == cont->num_params() ? "" : ", "); + } + file << ")"; + + file << "\";"; + + file << endl() << "shape = rectangle;"; + if (intrinsic != Intrinsic::None) { + file << endl() << "color = lightblue;"; + file << endl() << "style = filled;"; + } + if (cont->is_external()) { + file << endl() << "color = pink;"; + file << endl() << "style = filled;"; + } + + file << down() << endl() << "]"; + + if (cont->has_body()) + arrow(def_id(cont), dump_def(cont->body()), "[arrowhead=normal]"); + + return def_id(cont); +} + +DEBUG_UTIL void dump_dot_world(World& world) { + DotPrinter printer(world); + for (auto& external: world.externals()) { + printer.dump_def(external.second); + } + printer.run(); +} + +DEBUG_UTIL void dump_dot_def(const Def* def) { + DotPrinter printer(def->world()); + printer.dump_def(def); + printer.run(); +} + +DEBUG_UTIL void dump_dot_scopes(World& world) { + DotPrinter printer(world); + ScopesForest forest(world); + for (auto& top_level : forest.top_level_scopes()) { + auto& scope = forest.get_scope(top_level); + printer.print_scope(scope); + } + printer.run(); +} + +DEBUG_UTIL void dump_dot_scope(Scope& scope) { + DotPrinter printer(scope.world()); + printer.single_scope = &scope; + printer.print_scope(scope); + printer.run(); +} + +} + +#endif //DOT_DUMP_H diff --git a/src/thorin/util/scoped_dump.cpp b/src/thorin/util/scoped_dump.cpp new file mode 100644 index 000000000..1821c32d3 --- /dev/null +++ b/src/thorin/util/scoped_dump.cpp @@ -0,0 +1,161 @@ +#include "scoped_dump.h" + +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_intrinsic()) + s.fmt("intrinsic "); + + s.fmt(Red); + s.fmt("{}", cont->unique_name()); + s.fmt(Reset); + s.fmt("("); + const FnType* t = cont->type(); + int ret_pi = -1; + for (size_t i = 0; i < cont->num_params(); i++) { + s.fmt(Yellow); + s.fmt("{}: ", cont->param(i)->unique_name()); + s.fmt(Blue); + s.fmt("{}", t->types()[i]); + s.fmt(Reset); + if (i + 1 < cont->num_params()) + s.fmt(", "); + } + s.fmt(")"); + if (!cont->has_body()) { + s.fmt(";"); + return; + } + + s.fmt(" = {{\t\n"); + + for (auto p : cont->params()) + done_.insert(p); + + Scope& sc = forest_.get_scope(cont); + scopes_to_defs_[cont] = std::make_unique>(); + auto children = sc.children_scopes(); + size_t i = 0; + for (auto child : children) { + stream_cont(s, child); + //if (i + 1 < children.size()) + s.fmt("\n\n"); + i++; + } + + prepare_def(cont, cont->body()); + + auto defs = *scopes_to_defs_[cont]; + stream_defs(s, defs); + + s.fmt("\b\n}}"); +} + +void ScopedWorld::prepare_def(Continuation* in, const thorin::Def* def) const { + if (done_.contains(def)) + return; + done_.insert(def); + if (def->isa_nom()) + return; + + while (in) { + Scope* scope = &forest_.get_scope(in); + if (scope->contains(def)) + break; + in = scope->parent_scope(); + } + + for (auto op : def->ops()) + prepare_def(in, op); + + if (!in) + top_lvl_.push_back(def); + else + scopes_to_defs_[in]->push_back(def); +} + +void ScopedWorld::stream_op(thorin::Stream& s, const thorin::Def* op) const { + //if (is_mem(op)) + // s.fmt(Gray); + if (op->isa()) + s.fmt(Cyan); + if (op->isa()) + s.fmt(Yellow); + if (op->isa()) + s.fmt(Red); + s.fmt("{}", op); + s.fmt(Reset); +} + +void ScopedWorld::stream_ops(thorin::Stream& s, Defs ops) const { + s.fmt("("); + size_t j = 0; + for (auto op : ops) { + stream_op(s, op); + if (j + 1 < ops.size()) + s.fmt(", "); + j++; + } + s.fmt(")"); +} + +void ScopedWorld::stream_def(thorin::Stream& s, const thorin::Def* def) const { + if (auto app = def->isa()) { + stream_op(s, app->callee()); + stream_ops(s, app->args()); + return; + } + + s.fmt(Green); + s.fmt("{}", def->op_name()); + s.fmt(Reset); + stream_ops(s, def->ops()); +} + +void ScopedWorld::stream_defs(thorin::Stream& s, std::vector& defs) const { + size_t i = 0; + for (auto def : defs) { + s.fmt("{}: ", def->unique_name()); + s.fmt(Blue); + s.fmt("{}", def->type()); + s.fmt(Reset); + s.fmt(" = "); + stream_def(s, def); + if (i + 1 < defs.size()) + s.fmt("\n"); + i++; + } +} + +Stream& ScopedWorld::stream(thorin::Stream& s) const { + auto tl = forest_.top_level_scopes(); + size_t i = 0; + for (auto root : tl) { + stream_cont(s, root); + //if (i + 1 < tl.size()) + s.fmt("\n\n"); + i++; + } + + stream_defs(s, top_lvl_); + + return s; +} + +void World::dump_scoped() const { + ScopedWorld s(*const_cast(this)); + s.dump(); +} + +void World::dump_scoped_to_disk() const { + ScopedWorld s(*const_cast(this), (ScopedWorld::Config) { false }); + auto name = this->name() + ".dump"; + std::ofstream file(name); + Stream st(file); + s.stream(st); +} + +} diff --git a/src/thorin/util/scoped_dump.h b/src/thorin/util/scoped_dump.h new file mode 100644 index 000000000..388c4f60d --- /dev/null +++ b/src/thorin/util/scoped_dump.h @@ -0,0 +1,52 @@ +#include "thorin/world.h" +#include "thorin/analyses/scope.h" +#include "thorin/util/stream.h" + +namespace thorin { + +#define COLORS(C) \ +C(Black, "\u001b[30m") \ +C(Red, "\u001b[31m") \ +C(Green, "\u001b[32m") \ +C(Yellow, "\u001b[33m") \ +C(Blue, "\u001b[34m") \ +C(Magenta, "\u001b[35m") \ +C(Cyan, "\u001b[36m") \ +C(White, "\u001b[37m") \ +C(Reset, "\u001b[0m") \ + +struct ScopedWorld : public Streamable { + struct Config { + bool use_color; + }; + + ScopedWorld(World& w, Config cfg = { true }) : world_(w), forest_(w), config_(cfg) { +#define T(n, c) n = cfg.use_color ? c : ""; + COLORS(T) +#undef T + } + + World& world_; + mutable ScopesForest forest_; + + mutable DefSet done_; + mutable ContinuationMap>> scopes_to_defs_; + mutable std::vector top_lvl_; + Config config_; + +#define T(n, c) const char* n; + COLORS(T) +#undef T + + Stream& stream(Stream&) const; +private: + + void stream_cont(thorin::Stream& s, Continuation* cont) const; + void prepare_def(Continuation* in, const Def* def) const; + void stream_op(thorin::Stream&, const Def* op) const; + void stream_ops(thorin::Stream& s, Defs defs) const; + void stream_def(thorin::Stream& s, const Def* def) const; + void stream_defs(thorin::Stream& s, std::vector& defs) const; +}; + +} diff --git a/src/thorin/world.cpp b/src/thorin/world.cpp index 53e0270da..661bc5e92 100644 --- a/src/thorin/world.cpp +++ b/src/thorin/world.cpp @@ -23,7 +23,6 @@ #include "thorin/analyses/scope.h" #include "thorin/analyses/verify.h" #include "thorin/transform/cleanup_world.h" -#include "thorin/transform/clone_bodies.h" #include "thorin/transform/closure_conversion.h" #include "thorin/transform/codegen_prepare.h" #include "thorin/transform/dead_load_opt.h" @@ -1310,7 +1309,6 @@ void Thorin::opt() { RUN_PASS(cleanup()) RUN_PASS(while (partial_evaluation(world(), true))); // lower2cff RUN_PASS(flatten_tuples(*this)) - RUN_PASS(clone_bodies(world())) RUN_PASS(split_slots(*this)) RUN_PASS(closure_conversion(world())) RUN_PASS(lift_builtins(*this)) diff --git a/src/thorin/world.h b/src/thorin/world.h index 56dda1c30..0e154afb6 100644 --- a/src/thorin/world.h +++ b/src/thorin/world.h @@ -93,6 +93,13 @@ class World : public Streamable { void make_internal(Def* cont) { assert(&cont->world() == this); data_.externals_.erase(cont->unique_name()); } bool is_external(const Def* cont) { return data_.externals_.contains(cont->unique_name()); } Def* lookup(const std::string& name) { return data_.externals_.lookup(name).value_or(nullptr); } + DEBUG_UTIL Continuation* find_cont(const char* name) { + for (auto cont : copy_continuations()) { + if (cont->unique_name() == name) + return cont; + } + return nullptr; + } //@} // types @@ -293,6 +300,8 @@ class World : public Streamable { /// @name logging //@{ + void dump_scoped() const; + void dump_scoped_to_disk() const; Stream& stream(Stream&) const; Stream& stream() { return *stream_; } /// Writes to a file named @c name(). From 7e090163392c2f9e5582e1527c03dde468cf7188 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Mon, 11 Sep 2023 10:01:21 +0200 Subject: [PATCH 182/342] improved diagnostics for PE --- src/thorin/continuation.cpp | 2 +- src/thorin/transform/partial_evaluation.cpp | 1 + src/thorin/world.h | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/thorin/continuation.cpp b/src/thorin/continuation.cpp index d82041aa8..b42263b9b 100644 --- a/src/thorin/continuation.cpp +++ b/src/thorin/continuation.cpp @@ -165,7 +165,7 @@ const Param* Continuation::ret_param() const { } void Continuation::destroy(const char* cause) { - world().VLOG("{} has been destroyed by {}", this, cause); + world().ddef(this, "{} has been destroyed by {}", this, cause); destroy_filter(); unset_op(0); set_op(0, world().bottom(world().bottom_type())); diff --git a/src/thorin/transform/partial_evaluation.cpp b/src/thorin/transform/partial_evaluation.cpp index 0968e45b1..bf5af1f30 100644 --- a/src/thorin/transform/partial_evaluation.cpp +++ b/src/thorin/transform/partial_evaluation.cpp @@ -166,6 +166,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); target = drop(callee, specialize); todo = true; } diff --git a/src/thorin/world.h b/src/thorin/world.h index 0e154afb6..ae070ce04 100644 --- a/src/thorin/world.h +++ b/src/thorin/world.h @@ -328,6 +328,7 @@ class World : public Streamable { // Ignore log void ignore() {} + template void ddef(const Def* def, const char* fmt, Args&&... args) { log(LogLevel::Debug, def->loc(), fmt, std::forward(args)...); } template void idef(const Def* def, const char* fmt, Args&&... args) { log(LogLevel::Info, def->loc(), fmt, std::forward(args)...); } template void wdef(const Def* def, const char* fmt, Args&&... args) { log(LogLevel::Warn, def->loc(), fmt, std::forward(args)...); } template void edef(const Def* def, const char* fmt, Args&&... args) { error(def->loc(), fmt, std::forward(args)...); } From 32ce4d7127245c9d0c489c9f1e3a119c84472ce8 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Mon, 11 Sep 2023 10:01:40 +0200 Subject: [PATCH 183/342] reintroduce boundary check in PE --- src/thorin/transform/partial_evaluation.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/thorin/transform/partial_evaluation.cpp b/src/thorin/transform/partial_evaluation.cpp index bf5af1f30..92ab41fa7 100644 --- a/src/thorin/transform/partial_evaluation.cpp +++ b/src/thorin/transform/partial_evaluation.cpp @@ -19,11 +19,14 @@ class PartialEvaluator { PartialEvaluator(World& world, bool lower2cff) : world_(world) , lower2cff_(lower2cff) + , boundary_(Def::gid_counter()) {} World& world() { return world_; } bool run(); void enqueue(Continuation* continuation) { + if (continuation->gid() < 2 * boundary_ && done_.emplace(continuation).second) + queue_.push(continuation); queue_.push(continuation); } void eat_pe_info(Continuation*); @@ -34,6 +37,7 @@ class PartialEvaluator { HashMap cache_; ContinuationSet done_; unique_queue queue_; + size_t boundary_; }; class CondEval { From 09fe3d52792bcd4ccf96018b15f58cbc93910e72 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Tue, 12 Sep 2023 16:27:49 +0200 Subject: [PATCH 184/342] LLVM: Make the codegen more value-oriented, generalise jumping code --- src/thorin/be/llvm/amdgpu.cpp | 2 +- src/thorin/be/llvm/amdgpu.h | 2 +- src/thorin/be/llvm/llvm.cpp | 274 ++++++++++++++++--------------- src/thorin/be/llvm/llvm.h | 33 ++-- src/thorin/be/llvm/nvvm.cpp | 2 +- src/thorin/be/llvm/nvvm.h | 2 +- src/thorin/be/llvm/parallel.cpp | 22 +-- src/thorin/be/llvm/runtime.cpp | 4 +- src/thorin/be/llvm/runtime.h | 2 +- src/thorin/be/llvm/vectorize.cpp | 4 +- 10 files changed, 176 insertions(+), 171 deletions(-) diff --git a/src/thorin/be/llvm/amdgpu.cpp b/src/thorin/be/llvm/amdgpu.cpp index d07f9f389..37468fe72 100644 --- a/src/thorin/be/llvm/amdgpu.cpp +++ b/src/thorin/be/llvm/amdgpu.cpp @@ -86,7 +86,7 @@ llvm::Value* AMDGPUCodeGen::emit_mathop(llvm::IRBuilder<>& irbuilder, const Math return call; } -Continuation* AMDGPUCodeGen::emit_reserve(llvm::IRBuilder<>& irbuilder, const Continuation* continuation) { +llvm::Value* AMDGPUCodeGen::emit_reserve(llvm::IRBuilder<>& irbuilder, const Continuation* continuation) { return emit_reserve_shared(irbuilder, continuation, true); } diff --git a/src/thorin/be/llvm/amdgpu.h b/src/thorin/be/llvm/amdgpu.h index fd5bd5779..999c60e81 100644 --- a/src/thorin/be/llvm/amdgpu.h +++ b/src/thorin/be/llvm/amdgpu.h @@ -22,7 +22,7 @@ class AMDGPUCodeGen : public CodeGen { llvm::Function* emit_fun_decl(Continuation*) override; llvm::Value* emit_global(const Global*) override; llvm::Value* emit_mathop(llvm::IRBuilder<>&, const MathOp*) override; - Continuation* emit_reserve(llvm::IRBuilder<>&, const Continuation*) override; + llvm::Value* emit_reserve(llvm::IRBuilder<>&, const Continuation*) override; std::string get_alloc_name() const override { return "malloc"; } const Cont2Config& kernel_config_; diff --git a/src/thorin/be/llvm/llvm.cpp b/src/thorin/be/llvm/llvm.cpp index e1b36028a..93f825fc2 100644 --- a/src/thorin/be/llvm/llvm.cpp +++ b/src/thorin/be/llvm/llvm.cpp @@ -449,36 +449,103 @@ void CodeGen::finalize(const Scope&) { defs_.erase(def); } -void CodeGen::emit_epilogue(Continuation* continuation) { - assert(continuation->has_body()); - auto body = continuation->body(); - - auto& [bb, ptr_irbuilder] = cont2bb_[continuation]; - auto& irbuilder = *ptr_irbuilder; - - if (body->callee() == entry_->ret_param()) { // return - std::vector values; - std::vector types; +std::vector CodeGen::split_values(llvm::IRBuilder<>& irbuilder, Types domain, llvm::Value* value) { + size_t n = 0; + for (auto t : domain) { + if (t == world().unit_type() || t->isa()) + continue; + n++; + } - for (auto arg : body->args()) { - if (auto val = emit_unsafe(arg)) { - values.emplace_back(val); - types.emplace_back(val->getType()); + switch (n) { + case 0: return {}; + case 1: return { value }; + default: { + std::vector values; + values.resize(n); + for (size_t i = 0; i < n; i++) { + values[i] = irbuilder.CreateExtractValue(value, i); } + return values; } + } +} - switch (values.size()) { +llvm::CallInst* CodeGen::emit_call(llvm::IRBuilder<>& irbuilder, const Def* callee, std::vector& args) { + if (callee == entry_->ret_param()) { // normal return + std::vector types; + for (auto val : args) + types.emplace_back(val->getType()); + switch (args.size()) { case 0: irbuilder.CreateRetVoid(); break; - case 1: irbuilder.CreateRet(values[0]); break; - default: + case 1: irbuilder.CreateRet(args[0]); break; + default: { llvm::Value* agg = llvm::UndefValue::get(llvm::StructType::get(context(), types)); - for (size_t i = 0, e = values.size(); i != e; ++i) - agg = irbuilder.CreateInsertValue(agg, values[i], { unsigned(i) }); + for (size_t i = 0, e = args.size(); i != e; ++i) + agg = irbuilder.CreateInsertValue(agg, args[i], { unsigned(i) }); irbuilder.CreateRet(agg); + } } - } else if (body->callee() == world().branch()) { + return nullptr; + } else if (callee->isa()) { + irbuilder.CreateUnreachable(); + return nullptr; + } else if (auto cont = callee->isa_nom(); cont && scope_->contains(cont) && cont != entry_) { + assert(cont->is_basicblock()); + size_t j = 0, i = 0; + for (auto t: cont->type()->types()) { + i++; + assert(t->order() == 0); + if (t->isa() || t == world().unit_type()) + continue; + emit_phi_arg(irbuilder, cont->param(i - 1), args[j++]); + } + irbuilder.CreateBr(cont2bb(cont)); + return nullptr; + } else if (auto closure_t = callee->type()->isa()) { + auto closure = emit(callee); + auto fnt = world().fn_type(concat(closure_t->types(), closure_t->as())); + args.push_back(closure); + auto func = irbuilder.CreateExtractValue(closure, 0); + auto call = irbuilder.CreateCall(llvm::cast(convert(fnt)), irbuilder.CreatePointerCast(func, convert(world().ptr_type(fnt))), args); + return call; + } else if (callee->type()->tag() == Node_FnType) { + auto call = irbuilder.CreateCall(llvm::cast(emit(callee)), args); + if (cont->is_exported()) + call->setCallingConv(kernel_calling_convention_); + else if (cont->cc() == CC::Device) + call->setCallingConv(device_calling_convention_); + else + call->setCallingConv(function_calling_convention_); + return call; + } + + THORIN_UNREACHABLE; +} + +static const Type* mangle_for_codegen(World& world, ArrayRef ret_types) { + // treat non-returning calls as if they return nothing, for now + std::vector types; + for (auto op: ret_types) { + assert(op->order() == 0); + if (op->isa() || is_type_unit(op)) continue; + types.push_back(op); + } + return world.tuple_type(types); +} + +void CodeGen::emit_epilogue(Continuation* continuation) { + assert(continuation->has_body()); + auto body = continuation->body(); + + auto& [bb, ptr_irbuilder] = cont2bb_[continuation]; + auto& irbuilder = *ptr_irbuilder; + + llvm::CallInst* call_instr = nullptr; + + if (body->callee() == world().branch()) { auto mem = body->arg(0); emit_unsafe(mem); @@ -499,18 +566,11 @@ void CodeGen::emit_epilogue(Continuation* continuation) { auto case_bb = cont2bb(arg->op(1)->as_nom()); match->addCase(case_const, case_bb); } - } else if (body->callee()->isa()) { - irbuilder.CreateUnreachable(); - } else if (auto callee = body->callee()->isa_nom(); callee && callee->is_basicblock()) { // ordinary jump - for (size_t i = 0, e = body->num_args(); i != e; ++i) { - if (auto val = emit_unsafe(body->arg(i))) emit_phi_arg(irbuilder, callee->param(i), val); - } - irbuilder.CreateBr(cont2bb(callee)); } else if (auto callee = body->callee()->isa_nom(); callee && callee->is_intrinsic()) { - auto ret_continuation = emit_intrinsic(irbuilder, continuation); - irbuilder.CreateBr(cont2bb(ret_continuation)); - } else { // function/closure call - // put all first-order args into an array + auto args = emit_intrinsic(irbuilder, continuation); + call_instr = emit_call(irbuilder, body->arg(callee->ret_param()->index()), args); + } else { + // plain continuation call: we can just emit all the arguments std::vector args; const Def* ret_arg = nullptr; for (auto arg : body->args()) { @@ -523,61 +583,26 @@ void CodeGen::emit_epilogue(Continuation* continuation) { } } - llvm::CallInst* call = nullptr; - if (auto callee = body->callee()->isa_nom()) { - call = irbuilder.CreateCall(llvm::cast(emit(callee)), args); - if (callee->is_exported()) - call->setCallingConv(kernel_calling_convention_); - else if (callee->cc() == CC::Device) - call->setCallingConv(device_calling_convention_); - else - call->setCallingConv(function_calling_convention_); - } else { - // must be a closure - auto closure = emit(body->callee()); - args.push_back(irbuilder.CreateExtractValue(closure, 1)); - auto func = irbuilder.CreateExtractValue(closure, 0); - auto call_type = convert_closure_type(body->callee()->type()); - call = irbuilder.CreateCall(call_type, func, args); - } - - // must be call + continuation --- call + return has been removed by codegen_prepare - auto succ = ret_arg->as_nom(); + call_instr = emit_call(irbuilder, body->callee(), args); + if (body->callee()->type()->as()->is_returning()) { + assert(call_instr && "returning calls always involve one of those"); + assert(ret_arg && "we need a return argument too!"); - size_t n = 0; - const Param* last_param = nullptr; - for (auto param : succ->params()) { - if (is_mem(param) || is_unit(param)) - continue; - last_param = param; - n++; + auto ret_args = split_values(irbuilder, ret_arg->type()->as()->types(), call_instr); + call_instr = emit_call(irbuilder, ret_arg, ret_args); } + } - if (n == 0) { - irbuilder.CreateBr(cont2bb(succ)); - } else if (n == 1) { - irbuilder.CreateBr(cont2bb(succ)); - emit_phi_arg(irbuilder, last_param, call); - } else { - Array extracts(n); - for (size_t i = 0, j = 0; i != succ->num_params(); ++i) { - auto param = succ->param(i); - if (is_mem(param) || is_unit(param)) - continue; - extracts[j] = irbuilder.CreateExtractValue(call, unsigned(j)); - j++; - } - - irbuilder.CreateBr(cont2bb(succ)); - - for (size_t i = 0, j = 0; i != succ->num_params(); ++i) { - auto param = succ->param(i); - if (is_mem(param) || is_unit(param)) - continue; - emit_phi_arg(irbuilder, param, extracts[j]); - j++; - } - } + if (call_instr) { + // we need to add a dummy return terminator if the last instruction is a call + if (entry_->type()->is_returning()) { + auto entry_return_t = mangle_for_codegen(world(), entry_->ret_param()->type()->as()->types()); + if (entry_return_t != world().unit_type()) { + irbuilder.CreateRet(llvm::UndefValue::get(convert(entry_return_t))); + } else + irbuilder.CreateRetVoid(); + } else + irbuilder.CreateRetVoid(); } // new insert point is just before the terminator for all other instructions we have to add later on @@ -586,12 +611,14 @@ void CodeGen::emit_epilogue(Continuation* continuation) { llvm::Value* CodeGen::emit_constant(const Def* def) { auto irbuilder = llvm::IRBuilder(context()); - return emit_builder(irbuilder, def); + auto val = emit_builder(irbuilder, def); + return val; } llvm::Value* CodeGen::emit_bb(BB& bb, const Def* def) { auto& irbuilder = *bb.second; - return emit_builder(irbuilder, def); + auto val = emit_builder(irbuilder, def); + return val; } llvm::Value* CodeGen::emit_builder(llvm::IRBuilder<>& irbuilder, const Def* def) { @@ -1221,7 +1248,7 @@ llvm::Value* CodeGen::emit_assembly(llvm::IRBuilder<>& irbuilder, const Assembly * emit intrinsic */ -Continuation* CodeGen::emit_intrinsic(llvm::IRBuilder<>& irbuilder, Continuation* continuation) { +std::vector CodeGen::emit_intrinsic(llvm::IRBuilder<>& irbuilder, Continuation* continuation) { assert(continuation->has_body()); auto body = continuation->body(); auto callee = body->callee()->as_nom(); @@ -1236,33 +1263,35 @@ Continuation* CodeGen::emit_intrinsic(llvm::IRBuilder<>& irbuilder, Continuation } switch (callee->intrinsic()) { - case Intrinsic::Atomic: return emit_atomic(irbuilder, continuation); - case Intrinsic::AtomicLoad: return emit_atomic_load(irbuilder, continuation); - case Intrinsic::AtomicStore: return emit_atomic_store(irbuilder, continuation); + case Intrinsic::Atomic: return { emit_atomic(irbuilder, continuation) }; + case Intrinsic::AtomicLoad: return { emit_atomic_load(irbuilder, continuation) }; + case Intrinsic::AtomicStore: emit_atomic_store(irbuilder, continuation); break; case Intrinsic::CmpXchg: return emit_cmpxchg(irbuilder, continuation, false); case Intrinsic::CmpXchgWeak: return emit_cmpxchg(irbuilder, continuation, true); - case Intrinsic::Fence: return emit_fence(irbuilder, continuation); - case Intrinsic::Reserve: return emit_reserve(irbuilder, continuation); - case Intrinsic::CUDA: return runtime_->emit_host_code(*this, irbuilder, Runtime::CUDA_PLATFORM, ".cu", continuation); - case Intrinsic::NVVM: return runtime_->emit_host_code(*this, irbuilder, Runtime::CUDA_PLATFORM, ".nvvm", continuation); - case Intrinsic::OpenCL: return runtime_->emit_host_code(*this, irbuilder, Runtime::OPENCL_PLATFORM, ".cl", continuation); - case Intrinsic::AMDGPU: return runtime_->emit_host_code(*this, irbuilder, Runtime::HSA_PLATFORM, ".amdgpu", continuation); - case Intrinsic::ShadyCompute: return runtime_->emit_host_code(*this, irbuilder, Runtime::SHADY_PLATFORM, ".shady", continuation); - case Intrinsic::HLS: return emit_hls(irbuilder, continuation); - case Intrinsic::Parallel: return emit_parallel(irbuilder, continuation); - case Intrinsic::Fibers: return emit_fibers(irbuilder, continuation); - case Intrinsic::Spawn: return emit_spawn(irbuilder, continuation); - case Intrinsic::Sync: return emit_sync(irbuilder, continuation); + case Intrinsic::Fence: emit_fence(irbuilder, continuation); break; + case Intrinsic::Reserve: return { emit_reserve(irbuilder, continuation) }; + case Intrinsic::CUDA: runtime_->emit_host_code(*this, irbuilder, Runtime::CUDA_PLATFORM, ".cu", continuation); break; + case Intrinsic::NVVM: runtime_->emit_host_code(*this, irbuilder, Runtime::CUDA_PLATFORM, ".nvvm", continuation); break; + case Intrinsic::OpenCL: runtime_->emit_host_code(*this, irbuilder, Runtime::OPENCL_PLATFORM, ".cl", continuation); break; + case Intrinsic::AMDGPU: runtime_->emit_host_code(*this, irbuilder, Runtime::HSA_PLATFORM, ".amdgpu", continuation); break; + case Intrinsic::ShadyCompute: runtime_->emit_host_code(*this, irbuilder, Runtime::SHADY_PLATFORM, ".shady", continuation); break; + case Intrinsic::HLS: emit_hls(irbuilder, continuation); break; + case Intrinsic::Parallel: emit_parallel(irbuilder, continuation); break; + case Intrinsic::Fibers: emit_fibers(irbuilder, continuation); break; + case Intrinsic::Spawn: return { emit_spawn(irbuilder, continuation) }; + case Intrinsic::Sync: emit_sync(irbuilder, continuation); break; #if THORIN_ENABLE_RV - case Intrinsic::Vectorize: return emit_vectorize_continuation(irbuilder, continuation); + case Intrinsic::Vectorize: emit_vectorize_continuation(irbuilder, continuation); break; #else - case Intrinsic::Vectorize: throw std::runtime_error("rebuild with RV support"); + case Intrinsic::Vectorize: throw std::runtime_error("rebuild with RV support"); #endif default: THORIN_UNREACHABLE; } + + return {}; } -Continuation* CodeGen::emit_atomic(llvm::IRBuilder<>& irbuilder, Continuation* continuation) { +llvm::Value* CodeGen::emit_atomic(llvm::IRBuilder<>& irbuilder, Continuation* continuation) { assert(continuation->has_body()); auto body = continuation->body(); assert(body->num_args() == 7 && "required arguments are missing"); @@ -1282,13 +1311,11 @@ Continuation* CodeGen::emit_atomic(llvm::IRBuilder<>& irbuilder, Continuation* c assert(int(llvm::AtomicOrdering::NotAtomic) <= int(order_tag) && int(order_tag) <= int(llvm::AtomicOrdering::SequentiallyConsistent) && "unsupported atomic ordering"); auto order = (llvm::AtomicOrdering)order_tag; auto scope = body->arg(5)->as()->from()->as()->init()->as(); - auto cont = body->arg(6)->as_nom(); auto call = irbuilder.CreateAtomicRMW(binop, ptr, val, llvm::MaybeAlign(), order, context().getOrInsertSyncScopeID(scope->as_string())); - emit_phi_arg(irbuilder, cont->param(1), call); - return cont; + return call; } -Continuation* CodeGen::emit_atomic_load(llvm::IRBuilder<>& irbuilder, Continuation* continuation) { +llvm::Value* CodeGen::emit_atomic_load(llvm::IRBuilder<>& irbuilder, Continuation* continuation) { assert(continuation->has_body()); auto body = continuation->body(); assert(body->num_args() == 5 && "required arguments are missing"); @@ -1303,11 +1330,10 @@ Continuation* CodeGen::emit_atomic_load(llvm::IRBuilder<>& irbuilder, Continuati auto align = module().getDataLayout().getABITypeAlign(load_type); load->setAlignment(align); load->setAtomic(order, context().getOrInsertSyncScopeID(scope->as_string())); - emit_phi_arg(irbuilder, cont->param(1), load); - return cont; + return load; } -Continuation* CodeGen::emit_atomic_store(llvm::IRBuilder<>& irbuilder, Continuation* continuation) { +void CodeGen::emit_atomic_store(llvm::IRBuilder<>& irbuilder, Continuation* continuation) { assert(continuation->has_body()); auto body = continuation->body(); assert(body->num_args() == 6 && "required arguments are missing"); @@ -1322,10 +1348,9 @@ Continuation* CodeGen::emit_atomic_store(llvm::IRBuilder<>& irbuilder, Continuat auto align = module().getDataLayout().getABITypeAlign(convert(body->arg(2)->type())); store->setAlignment(align); store->setAtomic(order, context().getOrInsertSyncScopeID(scope->as_string())); - return cont; } -Continuation* CodeGen::emit_cmpxchg(llvm::IRBuilder<>& irbuilder, Continuation* continuation, bool is_weak) { +std::vector CodeGen::emit_cmpxchg(llvm::IRBuilder<>& irbuilder, Continuation* continuation, bool is_weak) { assert(continuation->has_body()); auto body = continuation->body(); @@ -1345,12 +1370,10 @@ Continuation* CodeGen::emit_cmpxchg(llvm::IRBuilder<>& irbuilder, Continuation* auto cont = body->arg(7)->as_nom(); auto call = irbuilder.CreateAtomicCmpXchg(ptr, cmp, val, llvm::MaybeAlign(), success_order, failure_order, context().getOrInsertSyncScopeID(scope->as_string())); call->setWeak(is_weak); - emit_phi_arg(irbuilder, cont->param(1), irbuilder.CreateExtractValue(call, 0)); - emit_phi_arg(irbuilder, cont->param(2), irbuilder.CreateExtractValue(call, 1)); - return cont; + return { irbuilder.CreateExtractValue(call, 0), irbuilder.CreateExtractValue(call, 1) }; } -Continuation* CodeGen::emit_fence(llvm::IRBuilder<>& irbuilder, Continuation* continuation) { +void CodeGen::emit_fence(llvm::IRBuilder<>& irbuilder, Continuation* continuation) { assert(continuation->has_body()); auto body = continuation->body(); assert(body->num_args() == 4 && "required arguments are missing"); @@ -1360,15 +1383,14 @@ Continuation* CodeGen::emit_fence(llvm::IRBuilder<>& irbuilder, Continuation* co auto scope = body->arg(2)->as()->from()->as()->init()->as(); auto cont = body->arg(3)->as_nom(); irbuilder.CreateFence(order, context().getOrInsertSyncScopeID(scope->as_string())); - return cont; } -Continuation* CodeGen::emit_reserve(llvm::IRBuilder<>&, const Continuation* continuation) { +llvm::Value* CodeGen::emit_reserve(llvm::IRBuilder<>&, const Continuation* continuation) { world().edef(continuation, "reserve_shared: only allowed in device code"); // TODO debug THORIN_UNREACHABLE; } -Continuation* CodeGen::emit_reserve_shared(llvm::IRBuilder<>& irbuilder, const Continuation* continuation, bool init_undef) { +llvm::Value* CodeGen::emit_reserve_shared(llvm::IRBuilder<>& irbuilder, const Continuation* continuation, bool init_undef) { assert(continuation->has_body()); auto body = continuation->body(); assert(body->num_args() == 3 && "required arguments are missing"); @@ -1385,31 +1407,23 @@ Continuation* CodeGen::emit_reserve_shared(llvm::IRBuilder<>& irbuilder, const C std::replace(name.begin(), name.end(), '.', '_'); auto global = emit_global_variable(smem_type, name, 3, init_undef); auto call = irbuilder.CreatePointerCast(global, type); - emit_phi_arg(irbuilder, cont->param(1), call); - return cont; + return call; } /* * backend-specific stuff */ -Continuation* CodeGen::emit_hls(llvm::IRBuilder<>& irbuilder, Continuation* continuation) { +void CodeGen::emit_hls(llvm::IRBuilder<>& irbuilder, Continuation* continuation) { assert(continuation->has_body()); auto body = continuation->body(); std::vector args(body->num_args()-3); - Continuation* ret = nullptr; for (size_t i = 2, j = 0; i < body->num_args(); ++i) { - if (auto cont = body->arg(i)->isa_nom()) { - ret = cont; - continue; - } args[j++] = emit(body->arg(i)); } auto callee = body->arg(1)->as()->init()->as_nom(); world().make_external(callee); irbuilder.CreateCall(emit_fun_decl(callee), args); - assert(ret); - return ret; } /* diff --git a/src/thorin/be/llvm/llvm.h b/src/thorin/be/llvm/llvm.h index c146f37d1..7d5414331 100644 --- a/src/thorin/be/llvm/llvm.h +++ b/src/thorin/be/llvm/llvm.h @@ -83,8 +83,8 @@ class CodeGen : public thorin::CodeGen, public thorin::Emitter&, const LEA*); virtual llvm::Value* emit_assembly(llvm::IRBuilder<>&, const Assembly* assembly); - virtual Continuation* emit_reserve(llvm::IRBuilder<>&, const Continuation*); - Continuation* emit_reserve_shared(llvm::IRBuilder<>&, const Continuation*, bool=false); + virtual llvm::Value* emit_reserve(llvm::IRBuilder<>&, const Continuation*); + llvm::Value* emit_reserve_shared(llvm::IRBuilder<>&, const Continuation*, bool=false); virtual std::string get_alloc_name() const = 0; llvm::BasicBlock* cont2bb(Continuation* cont) { return cont2bb_[cont].first; } @@ -96,23 +96,26 @@ class CodeGen : public thorin::CodeGen, public thorin::Emitter&, llvm::Value*, llvm::Value*, llvm::Value*, llvm::Function*, std::function); llvm::Value* create_tmp_alloca(llvm::IRBuilder<>&, llvm::Type*, std::function); - llvm::Value* call_math_function(llvm::IRBuilder<>&, const MathOp*, const std::string&); + std::vector split_values(llvm::IRBuilder<>&, Types domain, llvm::Value* value); + /// Emits a 'call' using already emitted arguments. Returns the call instruction if appropriate + llvm::CallInst* emit_call(llvm::IRBuilder<>&, const Def* callee, std::vector& args); + private: Continuation* emit_peinfo(llvm::IRBuilder<>&, Continuation*); - Continuation* emit_intrinsic(llvm::IRBuilder<>&, Continuation*); - Continuation* emit_hls(llvm::IRBuilder<>&, Continuation*); - Continuation* emit_parallel(llvm::IRBuilder<>&, Continuation*); - Continuation* emit_fibers(llvm::IRBuilder<>&, Continuation*); - Continuation* emit_spawn(llvm::IRBuilder<>&, Continuation*); - Continuation* emit_sync(llvm::IRBuilder<>&, Continuation*); - Continuation* emit_vectorize_continuation(llvm::IRBuilder<>&, Continuation*); - Continuation* emit_atomic(llvm::IRBuilder<>&, Continuation*); - Continuation* emit_cmpxchg(llvm::IRBuilder<>&, Continuation*, bool); - Continuation* emit_fence(llvm::IRBuilder<>&, Continuation*); - Continuation* emit_atomic_load(llvm::IRBuilder<>&, Continuation*); - Continuation* emit_atomic_store(llvm::IRBuilder<>&, Continuation*); + std::vector emit_intrinsic(llvm::IRBuilder<>&, Continuation*); + void emit_hls(llvm::IRBuilder<>&, Continuation*); + void emit_parallel(llvm::IRBuilder<>&, Continuation*); + void emit_fibers(llvm::IRBuilder<>&, Continuation*); + llvm::Value* emit_spawn(llvm::IRBuilder<>&, Continuation*); + void emit_sync(llvm::IRBuilder<>&, Continuation*); + void emit_vectorize_continuation(llvm::IRBuilder<>&, Continuation*); + llvm::Value* emit_atomic(llvm::IRBuilder<>&, Continuation*); + std::vector emit_cmpxchg(llvm::IRBuilder<>&, Continuation*, bool); + void emit_fence(llvm::IRBuilder<>&, Continuation*); + llvm::Value* emit_atomic_load(llvm::IRBuilder<>&, Continuation*); + void emit_atomic_store(llvm::IRBuilder<>&, Continuation*); llvm::Value* emit_bitcast(llvm::IRBuilder<>&, const Def*, const Type*); void emit_vectorize(u32, llvm::Function*, llvm::CallInst*); void emit_phi_arg(llvm::IRBuilder<>&, const Param*, llvm::Value*); diff --git a/src/thorin/be/llvm/nvvm.cpp b/src/thorin/be/llvm/nvvm.cpp index ec41f6e58..20dd5e2d4 100644 --- a/src/thorin/be/llvm/nvvm.cpp +++ b/src/thorin/be/llvm/nvvm.cpp @@ -242,7 +242,7 @@ llvm::Value* NVVMCodeGen::emit_lea(llvm::IRBuilder<>& irbuilder, const LEA* lea) } } -Continuation* NVVMCodeGen::emit_reserve(llvm::IRBuilder<>& irbuilder, const Continuation* continuation) { +llvm::Value* NVVMCodeGen::emit_reserve(llvm::IRBuilder<>& irbuilder, const Continuation* continuation) { return emit_reserve_shared(irbuilder, continuation); } diff --git a/src/thorin/be/llvm/nvvm.h b/src/thorin/be/llvm/nvvm.h index 204ce6648..742f6127c 100644 --- a/src/thorin/be/llvm/nvvm.h +++ b/src/thorin/be/llvm/nvvm.h @@ -28,7 +28,7 @@ class NVVMCodeGen : public CodeGen { llvm::Value* emit_lea(llvm::IRBuilder<>&, const LEA*) override; llvm::Value* emit_mathop(llvm::IRBuilder<>&, const MathOp*) override; - Continuation* emit_reserve(llvm::IRBuilder<>&, const Continuation*) override; + llvm::Value* emit_reserve(llvm::IRBuilder<>&, const Continuation*) override; llvm::Value* emit_global(const Global*) override; diff --git a/src/thorin/be/llvm/parallel.cpp b/src/thorin/be/llvm/parallel.cpp index c9012525f..aae4a0208 100644 --- a/src/thorin/be/llvm/parallel.cpp +++ b/src/thorin/be/llvm/parallel.cpp @@ -12,7 +12,7 @@ enum { PAR_NUM_ARGS }; -Continuation* CodeGen::emit_parallel(llvm::IRBuilder<>& irbuilder, Continuation* continuation) { +void CodeGen::emit_parallel(llvm::IRBuilder<>& irbuilder, Continuation* continuation) { assert(continuation->has_body()); auto body = continuation->body(); // Emit memory dependencies up to this point @@ -36,7 +36,7 @@ Continuation* CodeGen::emit_parallel(llvm::IRBuilder<>& irbuilder, Continuation* } // fetch values and create a unified struct which contains all values (closure) - auto closure_type = convert(world().tuple_type(continuation->arg_fn_type()->types().skip_front(PAR_NUM_ARGS))); + auto closure_type = convert(world().tuple_type(continuation->body()->callee()->type()->as()->types().skip_front(PAR_NUM_ARGS))); llvm::Value* closure = llvm::UndefValue::get(closure_type); if (num_kernel_args != 1) { for (size_t i = 0; i < num_kernel_args; ++i) @@ -91,8 +91,6 @@ Continuation* CodeGen::emit_parallel(llvm::IRBuilder<>& irbuilder, Continuation* // restore old insert point irbuilder.SetInsertPoint(old_bb); - - return body->arg(PAR_ARG_RETURN)->as_nom(); } enum { @@ -105,7 +103,7 @@ enum { FIB_NUM_ARGS }; -Continuation* CodeGen::emit_fibers(llvm::IRBuilder<>& irbuilder, Continuation* continuation) { +void CodeGen::emit_fibers(llvm::IRBuilder<>& irbuilder, Continuation* continuation) { assert(continuation->has_body()); auto body = continuation->body(); // Emit memory dependencies up to this point @@ -183,8 +181,6 @@ Continuation* CodeGen::emit_fibers(llvm::IRBuilder<>& irbuilder, Continuation* c // restore old insert point irbuilder.SetInsertPoint(old_bb); - - return body->arg(FIB_ARG_RETURN)->as_nom(); } enum { @@ -194,7 +190,7 @@ enum { SPAWN_NUM_ARGS }; -Continuation* CodeGen::emit_spawn(llvm::IRBuilder<>& irbuilder, Continuation* continuation) { +llvm::Value* CodeGen::emit_spawn(llvm::IRBuilder<>& irbuilder, Continuation* continuation) { assert(continuation->has_body()); auto body = continuation->body(); assert(body->num_args() >= SPAWN_NUM_ARGS && "required arguments are missing"); @@ -213,7 +209,7 @@ Continuation* CodeGen::emit_spawn(llvm::IRBuilder<>& irbuilder, Continuation* co } // fetch values and create a unified struct which contains all values (closure) - auto closure_type = convert(world().tuple_type(continuation->arg_fn_type()->types().skip_front(SPAWN_NUM_ARGS))); + auto closure_type = convert(world().tuple_type(continuation->body()->callee()->type()->as()->types().skip_front(SPAWN_NUM_ARGS))); llvm::Value* closure = nullptr; if (closure_type->isStructTy()) { closure = llvm::UndefValue::get(closure_type); @@ -262,10 +258,7 @@ Continuation* CodeGen::emit_spawn(llvm::IRBuilder<>& irbuilder, Continuation* co // restore old insert point irbuilder.SetInsertPoint(old_bb); - // bind parameter of continuation to received handle - auto cont = body->arg(SPAWN_ARG_RETURN)->as_nom(); - emit_phi_arg(irbuilder, cont->param(1), call); - return cont; + return call; } enum { @@ -275,7 +268,7 @@ enum { SYNC_NUM_ARGS }; -Continuation* CodeGen::emit_sync(llvm::IRBuilder<>& irbuilder, Continuation* continuation) { +void CodeGen::emit_sync(llvm::IRBuilder<>& irbuilder, Continuation* continuation) { assert(continuation->has_body()); auto body = continuation->body(); assert(body->num_args() == SYNC_NUM_ARGS && "wrong number of arguments"); @@ -285,7 +278,6 @@ Continuation* CodeGen::emit_sync(llvm::IRBuilder<>& irbuilder, Continuation* con auto id = emit(body->arg(SYNC_ARG_ID)); runtime_->sync_thread(*this, irbuilder, id); - return body->arg(SYNC_ARG_RETURN)->as_nom(); } } diff --git a/src/thorin/be/llvm/runtime.cpp b/src/thorin/be/llvm/runtime.cpp index bc0267a73..375713091 100644 --- a/src/thorin/be/llvm/runtime.cpp +++ b/src/thorin/be/llvm/runtime.cpp @@ -60,7 +60,7 @@ static bool contains_ptrtype(const Type* type) { } } -Continuation* Runtime::emit_host_code(CodeGen& code_gen, llvm::IRBuilder<>& builder, Platform platform, const std::string& ext, Continuation* continuation) { +void Runtime::emit_host_code(CodeGen& code_gen, llvm::IRBuilder<>& builder, Platform platform, const std::string& ext, Continuation* continuation) { assert(continuation->has_body()); auto body = continuation->body(); // to-target is the desired kernel call @@ -184,8 +184,6 @@ Continuation* Runtime::emit_host_code(CodeGen& code_gen, llvm::IRBuilder<>& buil grid_size, block_size, args, sizes, aligns, allocs, types, builder.getInt32(num_kernel_args)); - - return body->arg(LaunchArgs::Return)->as_nom(); } llvm::Value* Runtime::launch_kernel( diff --git a/src/thorin/be/llvm/runtime.h b/src/thorin/be/llvm/runtime.h index d99de5c27..7c4516e8d 100644 --- a/src/thorin/be/llvm/runtime.h +++ b/src/thorin/be/llvm/runtime.h @@ -51,7 +51,7 @@ class Runtime { /// Emits a call to anydsl_sync_thread. llvm::Value* sync_thread(CodeGen&, llvm::IRBuilder<>&, llvm::Value* id); - Continuation* emit_host_code( + void emit_host_code( CodeGen& code_gen, llvm::IRBuilder<>& builder, Platform platform, const std::string& ext, Continuation* continuation); diff --git a/src/thorin/be/llvm/vectorize.cpp b/src/thorin/be/llvm/vectorize.cpp index 591f518c1..f2c74e619 100644 --- a/src/thorin/be/llvm/vectorize.cpp +++ b/src/thorin/be/llvm/vectorize.cpp @@ -48,7 +48,7 @@ struct VectorizeArgs { }; }; -Continuation* CodeGen::emit_vectorize_continuation(llvm::IRBuilder<>& irbuilder, Continuation* continuation) { +void CodeGen::emit_vectorize_continuation(llvm::IRBuilder<>& irbuilder, Continuation* continuation) { assert(continuation->has_body()); auto body = continuation->body(); auto target = body->callee()->as_nom(); @@ -93,8 +93,6 @@ Continuation* CodeGen::emit_vectorize_continuation(llvm::IRBuilder<>& irbuilder, world().edef(body->arg(VectorizeArgs::Length), "vector length must be known at compile-time"); u32 vector_length_constant = body->arg(VectorizeArgs::Length)->as()->qu32_value(); vec_todo_.emplace_back(vector_length_constant, emit_fun_decl(kernel), simd_kernel_call); - - return body->arg(VectorizeArgs::Return)->as_nom(); } void CodeGen::emit_vectorize(u32 vector_length, llvm::Function* kernel_func, llvm::CallInst* simd_kernel_call) { From 21422b59704bcd38d285cb45cc1c09349f7b9e32 Mon Sep 17 00:00:00 2001 From: Matthias Kurtenacker Date: Thu, 14 Sep 2023 16:28:29 +0200 Subject: [PATCH 185/342] Mark Continuations dropped as device code as being external. No special calling convention needed any more. --- src/thorin/be/codegen.cpp | 2 +- src/thorin/be/json/json.cpp | 2 -- src/thorin/continuation.h | 1 - 3 files changed, 1 insertion(+), 4 deletions(-) diff --git a/src/thorin/be/codegen.cpp b/src/thorin/be/codegen.cpp index e1a155256..8b992d8d3 100644 --- a/src/thorin/be/codegen.cpp +++ b/src/thorin/be/codegen.cpp @@ -48,7 +48,7 @@ static void get_kernel_configs( return false; }, true); - continuation->attributes().cc = CC::DeviceHostCode; + continuation->world().make_external(continuation); continuation->destroy("codegen"); } } diff --git a/src/thorin/be/json/json.cpp b/src/thorin/be/json/json.cpp index 2bd09fb21..ea0ae03c8 100644 --- a/src/thorin/be/json/json.cpp +++ b/src/thorin/be/json/json.cpp @@ -263,8 +263,6 @@ class DefTable { else forward_decl["external"] = cont->name(); } - if (cont->cc() == CC::DeviceHostCode) - forward_decl["device"] = cont->name(); decl_table.push_back(forward_decl); if(cont->has_body()) { diff --git a/src/thorin/continuation.h b/src/thorin/continuation.h index 010d9bd8f..3a1f03281 100644 --- a/src/thorin/continuation.h +++ b/src/thorin/continuation.h @@ -86,7 +86,6 @@ class App : public Def { enum class CC : uint8_t { C, ///< C calling convention. Device, ///< Device calling convention. These are special functions only available on a particular device. - DeviceHostCode, ///< Calling convention to denote continuations that are generated as device code. Internal, ///< External, but only for linking with artic or anyopt. }; From 8fe13b85428667f475191da586e29339d3bb4914 Mon Sep 17 00:00:00 2001 From: Matthias Kurtenacker Date: Fri, 15 Sep 2023 14:02:23 +0200 Subject: [PATCH 186/342] Bug fix: Include config.h in debug.h --- src/thorin/debug.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/thorin/debug.h b/src/thorin/debug.h index 05613fccb..ad23841d7 100644 --- a/src/thorin/debug.h +++ b/src/thorin/debug.h @@ -4,6 +4,7 @@ #include #include +#include "thorin/config.h" #include "thorin/util/stream.h" namespace thorin { From e8148592473c1fb39ccf718e0c0a708053e4424a Mon Sep 17 00:00:00 2001 From: Matthias Kurtenacker Date: Fri, 15 Sep 2023 14:56:11 +0200 Subject: [PATCH 187/342] Only exported continuations need to be generated on top level. --- src/thorin/be/c/c.cpp | 2 +- src/thorin/be/llvm/llvm.cpp | 2 ++ src/thorin/transform/codegen_prepare.cpp | 2 ++ 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/thorin/be/c/c.cpp b/src/thorin/be/c/c.cpp index ab6b503ad..973b84dd2 100644 --- a/src/thorin/be/c/c.cpp +++ b/src/thorin/be/c/c.cpp @@ -360,7 +360,7 @@ void CCodeGen::emit_module() { forest_.for_each([&] (const Scope& scope) { if (scope.entry()->name() == "hls_top") hls_top = scope.entry(); - else if (scope.entry()->cc() != CC::Internal) + else if (scope.entry()->cc() != CC::Internal && scope.entry()->is_exported()) emit_scope(scope, forest_); }); if (hls_top) { diff --git a/src/thorin/be/llvm/llvm.cpp b/src/thorin/be/llvm/llvm.cpp index e1b36028a..b89c66f2a 100644 --- a/src/thorin/be/llvm/llvm.cpp +++ b/src/thorin/be/llvm/llvm.cpp @@ -314,6 +314,8 @@ CodeGen::emit_module() { if(scope.entry()->cc() == CC::Internal) { return; } + if (!scope.entry()->is_exported()) + return; emit_scope(scope, forest); }); diff --git a/src/thorin/transform/codegen_prepare.cpp b/src/thorin/transform/codegen_prepare.cpp index 6ae0401d9..0f0279d56 100644 --- a/src/thorin/transform/codegen_prepare.cpp +++ b/src/thorin/transform/codegen_prepare.cpp @@ -28,6 +28,8 @@ void codegen_prepare(World& world) { if (dirty) scope.update(); + else + ret_cont->destroy("codegen_prepare"); //Destroy the ret_cont if it was never used. }); world.VLOG("end codegen_prepare"); } From e6fece82e70129915be7fdbcf1eb2b14714b7e0a Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Tue, 12 Sep 2023 13:59:19 +0200 Subject: [PATCH 188/342] Refactored rewriting and passes --- src/thorin/CMakeLists.txt | 3 +- src/thorin/be/codegen.cpp | 10 +- src/thorin/be/llvm/parallel.cpp | 6 +- src/thorin/continuation.cpp | 90 ++++------- src/thorin/continuation.h | 9 +- src/thorin/def.cpp | 7 +- src/thorin/def.h | 6 +- src/thorin/primop.cpp | 5 + src/thorin/primop.h | 4 +- src/thorin/transform/cleanup_world.cpp | 125 +++------------- src/thorin/transform/cleanup_world.h | 12 -- src/thorin/transform/closure_conversion.cpp | 2 +- src/thorin/transform/codegen_prepare.cpp | 68 +++++---- src/thorin/transform/codegen_prepare.h | 2 +- src/thorin/transform/hls_channels.cpp | 18 ++- src/thorin/transform/importer.cpp | 139 +++++++++++++---- src/thorin/transform/importer.h | 26 ++-- src/thorin/transform/lift_builtins.cpp | 2 +- src/thorin/transform/mangle.cpp | 158 ++++++++------------ src/thorin/transform/mangle.h | 28 ++-- src/thorin/transform/partial_evaluation.cpp | 31 ++-- src/thorin/transform/partial_evaluation.h | 14 ++ src/thorin/transform/rewrite.cpp | 66 ++++++++ src/thorin/transform/rewrite.h | 32 ++++ src/thorin/type.cpp | 31 ++-- src/thorin/type.h | 4 +- src/thorin/world.cpp | 13 +- src/thorin/world.h | 3 +- 28 files changed, 478 insertions(+), 436 deletions(-) delete mode 100644 src/thorin/transform/cleanup_world.h create mode 100644 src/thorin/transform/rewrite.cpp create mode 100644 src/thorin/transform/rewrite.h diff --git a/src/thorin/CMakeLists.txt b/src/thorin/CMakeLists.txt index e3247e5f7..58c57ce5a 100644 --- a/src/thorin/CMakeLists.txt +++ b/src/thorin/CMakeLists.txt @@ -41,7 +41,6 @@ set(THORIN_SOURCES tables/primtypetable.h tables/mathoptable.h transform/cleanup_world.cpp - transform/cleanup_world.h transform/closure_conversion.cpp transform/closure_conversion.h transform/codegen_prepare.h @@ -64,6 +63,8 @@ set(THORIN_SOURCES transform/resolve_loads.h transform/partial_evaluation.cpp transform/partial_evaluation.h + transform/rewrite.cpp + transform/rewrite.h transform/split_slots.cpp transform/split_slots.h transform/hls_channels.cpp diff --git a/src/thorin/be/codegen.cpp b/src/thorin/be/codegen.cpp index 8b992d8d3..e53fc2efb 100644 --- a/src/thorin/be/codegen.cpp +++ b/src/thorin/be/codegen.cpp @@ -93,8 +93,7 @@ DeviceBackends::DeviceBackends(World& world, int opt, bool debug, std::string& f } // determine different parts of the world which need to be compiled differently - ScopesForest forest(world); - forest.for_each([&] (const Scope& scope) { + ScopesForest(world).for_each([&] (const Scope& scope) { auto continuation = scope.entry(); Continuation* imported = nullptr; @@ -142,8 +141,9 @@ DeviceBackends::DeviceBackends(World& world, int opt, bool debug, std::string& f has_restrict &= p.second; } - auto it_config = app->arg(LaunchArgs::Config)->as(); - if (it_config->op(0)->isa() && + auto it_config = app->arg(LaunchArgs::Config)->isa(); + if (it_config && + it_config->op(0)->isa() && it_config->op(1)->isa() && it_config->op(2)->isa()) { return std::make_unique(std::tuple{ @@ -194,7 +194,7 @@ DeviceBackends::DeviceBackends(World& world, int opt, bool debug, std::string& f } return std::make_unique(param_sizes); }); - hls_annotate_top(importers[HLS].world(), top2kernel, kernel_config); + hls_annotate_top(accelerator_code[HLS].world(), top2kernel, kernel_config); } hls_kernel_launch(world, hls_host_params); diff --git a/src/thorin/be/llvm/parallel.cpp b/src/thorin/be/llvm/parallel.cpp index c9012525f..54e206510 100644 --- a/src/thorin/be/llvm/parallel.cpp +++ b/src/thorin/be/llvm/parallel.cpp @@ -36,7 +36,7 @@ Continuation* CodeGen::emit_parallel(llvm::IRBuilder<>& irbuilder, Continuation* } // fetch values and create a unified struct which contains all values (closure) - auto closure_type = convert(world().tuple_type(continuation->arg_fn_type()->types().skip_front(PAR_NUM_ARGS))); + auto closure_type = convert(world().tuple_type(continuation->body()->callee()->type()->as()->types().skip_front(PAR_NUM_ARGS))); llvm::Value* closure = llvm::UndefValue::get(closure_type); if (num_kernel_args != 1) { for (size_t i = 0; i < num_kernel_args; ++i) @@ -130,7 +130,7 @@ Continuation* CodeGen::emit_fibers(llvm::IRBuilder<>& irbuilder, Continuation* c } // fetch values and create a unified struct which contains all values (closure) - auto closure_type = convert(world().tuple_type(continuation->arg_fn_type()->types().skip_front(FIB_NUM_ARGS))); + auto closure_type = convert(world().tuple_type(continuation->body()->callee()->type()->as()->types().skip_front(FIB_NUM_ARGS))); llvm::Value* closure = llvm::UndefValue::get(closure_type); if (num_kernel_args != 1) { for (size_t i = 0; i < num_kernel_args; ++i) @@ -213,7 +213,7 @@ Continuation* CodeGen::emit_spawn(llvm::IRBuilder<>& irbuilder, Continuation* co } // fetch values and create a unified struct which contains all values (closure) - auto closure_type = convert(world().tuple_type(continuation->arg_fn_type()->types().skip_front(SPAWN_NUM_ARGS))); + auto closure_type = convert(world().tuple_type(continuation->body()->callee()->type()->as()->types().skip_front(SPAWN_NUM_ARGS))); llvm::Value* closure = nullptr; if (closure_type->isStructTy()) { closure = llvm::UndefValue::get(closure_type); diff --git a/src/thorin/continuation.cpp b/src/thorin/continuation.cpp index b42263b9b..28f7dad8e 100644 --- a/src/thorin/continuation.cpp +++ b/src/thorin/continuation.cpp @@ -2,6 +2,7 @@ #include +#include "thorin/transform/rewrite.h" #include "thorin/type.h" #include "thorin/world.h" #include "thorin/transform/mangle.h" @@ -20,7 +21,10 @@ Param::Param(World& world, const Type* type, const Continuation* continuation, s const Def* Param::rebuild(World&, const Type*, Defs defs) const { assert(defs.size() == 1); - auto cont = defs[0]->as(); + const Def* c = defs[0]; + if (auto r = c->isa()) + c = r->def()->as_nom(); + auto cont = c->as(); return cont->param(index()); } @@ -34,7 +38,7 @@ bool Param::equal(const Def* other) const { //------------------------------------------------------------------------------ -App::App(World& world, const Defs ops, Debug dbg) : Def(world, Node_App, ops[0]->world().bottom_type(), ops, dbg) { +App::App(World& world, const Defs ops, Debug dbg) : Def(world, Node_App, world.bottom_type(), ops, dbg) { #if THORIN_ENABLE_CHECKS verify(); if (auto cont = callee()->isa_nom()) @@ -69,6 +73,7 @@ Continuation::Continuation(World& w, const FnType* pi, const Attributes& attribu : Def(w, Node_Continuation, pi, 2, dbg) , attributes_(attributes) { + assert(pi->tag() == Node_FnType && "continuations may not be closures"); params_.reserve(pi->num_ops()); set_op(0, world().bottom(world().bottom_type())); set_op(1, world().filter({}, dbg)); @@ -80,61 +85,41 @@ Continuation::Continuation(World& w, const FnType* pi, const Attributes& attribu } } -// TODO: merge with regular stub() -Continuation* Continuation::mangle_stub() const { - Rewriter rewriter; - return mangle_stub(rewriter); -} - -Continuation* Continuation::mangle_stub(Rewriter& rewriter) const { - auto result = world().continuation(type(), attributes(), debug_history()); - for (size_t i = 0, e = num_params(); i != e; ++i) { - result->param(i)->set_name(debug_history().name); - rewriter.old2new[param(i)] = result->param(i); - } - - if (!filter()->is_empty()) { - Array new_conditions(num_params()); - for (size_t i = 0, e = num_params(); i != e; ++i) - new_conditions[i] = rewriter.instantiate(filter()->condition(i)); - - result->set_filter(world().filter(new_conditions, filter()->debug())); - } +Continuation* Continuation::stub(Rewriter& rewriter, const Type* nty) const { + assert(!dead_); + auto& nworld = rewriter.dst(); - return result; -} + auto npi = nty->isa(); + assert(npi && npi->tag() == Node_FnType); -Continuation* Continuation::stub(World& nworld, const Type* t) const { - assert(!dead_); - // TODO maybe we want to deal with intrinsics in a more streamlined way - if (this == world().branch()) - return nworld.branch(); - if (this == world().end_scope()) - return nworld.end_scope(); - - auto npi = t->isa(); - assert(npi); - Continuation* ncontinuation = nworld.continuation(npi, attributes(), debug_history()); + Continuation* ncontinuation = nworld.continuation(npi, attributes(), debug()); assert(&ncontinuation->world() == &nworld); assert(&npi->world() == &nworld); - for (size_t i = 0, e = num_params(); i != e; ++i) - ncontinuation->param(i)->set_name(param(i)->debug_history().name); - if (is_external()) - nworld.make_external(ncontinuation); + // TODO: investigate why this hangs + // ncontinuation->set_filter(rewriter.instantiate(filter())->as()); return ncontinuation; } -void Continuation::rebuild_from(const Def*, Defs nops) { - if (this == world().branch()) - return; - if (this == world().end_scope()) - return; +void Continuation::rebuild_from(Rewriter& rewriter, const Def* old) { + auto ocont = old->as(); + assert(ocont); + + assert(num_params() >= ocont->num_params()); + for (size_t i = 0, e = ocont->num_params(); i != e; ++i) + param(i)->set_name(ocont->param(i)->debug().name); + + set_filter(rewriter.instantiate(ocont->filter())->as()); + + if (ocont->is_external()) + world().make_external(this); - auto napp = nops[0]->isa(); - if (napp) + if (ocont->has_body()) { + auto napp = rewriter.instantiate(ocont->body())->isa(); + // i feel like this ought to be a warning, but maybe a later pass might legitimately do that + assert(napp && "is it legitimate to substitute away an App when rebuilding ?"); set_body(napp); - set_filter(nops[1]->as()); + } verify(); } @@ -172,17 +157,6 @@ void Continuation::destroy(const char* cause) { dead_ = true; } -const FnType* Continuation::arg_fn_type() const { - assert(has_body()); - Array args(body()->num_args()); - for (size_t i = 0, e = body()->num_args(); i != e; ++i) - args[i] = body()->arg(i)->type(); - - return body()->callee()->type()->isa() - ? world().closure_type(args)->as() - : world().fn_type(args); -} - const Param* Continuation::append_param(const Type* param_type, Debug dbg) { size_t size = type()->num_ops(); Array ops(size + 1); diff --git a/src/thorin/continuation.h b/src/thorin/continuation.h index 3a1f03281..ba72dbf91 100644 --- a/src/thorin/continuation.h +++ b/src/thorin/continuation.h @@ -142,10 +142,8 @@ class Continuation : public Def { public: const FnType* type() const { return Def::type()->as(); } - Continuation* mangle_stub() const; - Continuation* mangle_stub(Rewriter& rewriter) const; - Continuation* stub(World&, const Type*) const override; - void rebuild_from(const Def* old, Defs new_ops) override; + Continuation* stub(Rewriter&, const Type*) const override; + void rebuild_from(Rewriter&, const Def* old) override; const Param* append_param(const Type* type, Debug dbg = {}); Continuations preds() const; Continuations succs() const; @@ -156,9 +154,6 @@ class Continuation : public Def { const Param* ret_param() const; size_t num_params() const { return params().size(); } - // TODO only used in parallel.cpp to create a dummy value, should be refactored in something cleaner - const FnType* arg_fn_type() const; - Attributes& attributes() { return attributes_; } const Attributes& attributes() const { return attributes_; } Intrinsic intrinsic() const { return attributes().intrinsic; } diff --git a/src/thorin/def.cpp b/src/thorin/def.cpp index 8915d42ba..988811acf 100644 --- a/src/thorin/def.cpp +++ b/src/thorin/def.cpp @@ -3,6 +3,7 @@ #include #include +#include "thorin/transform/rewrite.h" #include "thorin/continuation.h" #include "thorin/primop.h" #include "thorin/type.h" @@ -138,10 +139,10 @@ bool is_minus_zero(const Def* def) { return false; } -void Def::rebuild_from(const Def*, Defs new_ops) { - assert(new_ops.size() == num_ops()); +void Def::rebuild_from(Rewriter& rewriter, const Def* old) { + assert(old->num_ops() == num_ops()); for (size_t i = 0; i < num_ops(); i++) - set_op(i, new_ops[i]); + set_op(i, rewriter.instantiate(old->op(i))); } void Def::replace_uses(const Def* with) const { diff --git a/src/thorin/def.h b/src/thorin/def.h index a58b2a216..83419e8dc 100644 --- a/src/thorin/def.h +++ b/src/thorin/def.h @@ -14,7 +14,7 @@ namespace thorin { class Continuation; class Def; -class Tracker; +class Rewriter; class Use; class World; class Type; @@ -221,8 +221,8 @@ class Def : public RuntimeCast, public Streamable { /// @name rebuild/stub //@{ virtual const Def* rebuild(World&, const Type*, Defs) const { THORIN_UNREACHABLE; } - virtual Def* stub(World&, const Type*) const { THORIN_UNREACHABLE; } - virtual void rebuild_from(const Def* old, Defs new_ops); + virtual Def* stub(Rewriter&, const Type*) const { THORIN_UNREACHABLE; } + virtual void rebuild_from(Rewriter&, const Def* old); //@} void replace_uses(const Def*) const; diff --git a/src/thorin/primop.cpp b/src/thorin/primop.cpp index 49208b173..a1222f4fe 100644 --- a/src/thorin/primop.cpp +++ b/src/thorin/primop.cpp @@ -1,5 +1,6 @@ #include "thorin/primop.h" #include "thorin/continuation.h" +#include "thorin/transform/rewrite.h" #include "thorin/config.h" #include "thorin/type.h" @@ -312,6 +313,10 @@ const PtrType* Closure::environment_ptr_type(World& world) { return world.ptr_type(world.type_pu8()); } +Continuation* Closure::fn() const { + return op(0)->as_nom(); +} + //------------------------------------------------------------------------------ } diff --git a/src/thorin/primop.h b/src/thorin/primop.h index 6347c85e6..5b2a7eb33 100644 --- a/src/thorin/primop.h +++ b/src/thorin/primop.h @@ -343,6 +343,8 @@ class Closure : public Aggregate { static const Type* environment_type(World&); static const PtrType* environment_ptr_type(World&); + Continuation* fn() const; + friend class World; }; @@ -534,11 +536,11 @@ class Global : public Def { bool is_external() const; void set_init(const Def* new_init) { unset_op(0); set_op(0, new_init); } + const Def* rebuild(World&, const Type*, Defs) const override; private: hash_t vhash() const override { return murmur3(gid()); } bool equal(const Def* other) const override { return this == other; } - const Def* rebuild(World&, const Type*, Defs) const override; bool is_mutable_; diff --git a/src/thorin/transform/cleanup_world.cpp b/src/thorin/transform/cleanup_world.cpp index 9a8eeaa99..65a860e48 100644 --- a/src/thorin/transform/cleanup_world.cpp +++ b/src/thorin/transform/cleanup_world.cpp @@ -2,8 +2,6 @@ #include "thorin/world.h" #include "thorin/analyses/cfg.h" #include "thorin/analyses/scope.h" -#include "thorin/analyses/domtree.h" -#include "thorin/analyses/scope.h" #include "thorin/analyses/verify.h" #include "thorin/transform/importer.h" #include "thorin/transform/mangle.h" @@ -14,14 +12,13 @@ namespace thorin { class Cleaner { public: - Cleaner(std::unique_ptr& world) - : world_(world) + Cleaner(Thorin& thorin) + : thorin_(thorin) {} - World& world() { return *world_; } + World& world() { return thorin_.world(); } void cleanup(); void eliminate_tail_rec(); - void eta_conversion(); void eliminate_params(); void rebuild(); void verify_closedness(); @@ -31,13 +28,12 @@ class Cleaner { private: void cleanup_fix_point(); void clean_pe_info(std::queue, Continuation*); - std::unique_ptr& world_; + Thorin& thorin_; bool todo_ = true; }; void Cleaner::eliminate_tail_rec() { - ScopesForest forest(world()); - forest.for_each([&](Scope& scope) { + ScopesForest(world()).for_each([&](Scope& scope) { auto entry = scope.entry(); bool only_tail_calls = true; @@ -100,86 +96,6 @@ void Cleaner::eliminate_tail_rec() { }); } -void Cleaner::eta_conversion() { - for (bool todo = true; todo;) { - todo = false; - for (auto def : world().copy_defs()) { - auto continuation = def->isa_nom(); - if (!continuation || !continuation->has_body()) continue; - - // eat calls to known continuations that are only used once - while (auto callee = continuation->body()->callee()->isa_nom()) { - auto body = continuation->body(); - if (callee == continuation) break; - - if (callee->has_body() && !world().is_external(callee) && callee->can_be_inlined()) { - auto callee_body = callee->body(); - for (size_t i = 0, e = body->num_args(); i != e; ++i) - callee->param(i)->replace_uses(body->arg(i)); - - // because App nodes are hash-consed (thus reusable), there is a risk to invalidate their other uses here, if there are indeed any - // can_be_inlined() should account for that by counting reused apps multiple times, but in case it fails we have this pair of asserts as insurance - assert(body->num_uses() == 1); - continuation->jump(callee_body->callee(), callee_body->args(), callee->debug()); // TODO debug - callee->destroy("cleanup: continuation only called once"); - assert(body->num_uses() == 0); - todo_ = todo = true; - } else - break; - } - - auto body = continuation->body(); - // try to subsume continuations which call a parameter - // (that is free within that continuation) with that parameter - if (auto param = body->callee()->isa()) { - if (param->continuation() == continuation || world().is_external(continuation)) - continue; - - if (body->args() == continuation->params_as_defs()) { - continuation->replace_uses(body->callee()); - continuation->destroy("cleanup: calls a parameter (no perm)"); - todo_ = todo = true; - continue; - } - - // build the permutation of the arguments - Array perm(body->num_args()); - bool is_permutation = true; - for (size_t i = 0, e = body->num_args(); i != e; ++i) { - auto param_it = std::find(continuation->params().begin(), - continuation->params().end(), - body->arg(i)); - - if (param_it == continuation->params().end()) { - is_permutation = false; - break; - } - - perm[i] = param_it - continuation->params().begin(); - } - - if (!is_permutation) continue; - - // for every use of the continuation at a call site, - // permute the arguments and call the parameter instead - for (auto use : continuation->copy_uses()) { - auto uapp = use->isa(); - if (uapp && use.index() == 0) { - for (auto ucontinuation : uapp->using_continuations()) { - Array new_args(perm.size()); - for (size_t i = 0, e = perm.size(); i != e; ++i) { - new_args[i] = uapp->arg(perm[i]); - } - ucontinuation->jump(param, new_args, ucontinuation->debug()); // TODO debug - todo_ = todo = true; - } - } - } - } - } - } -} - void Cleaner::eliminate_params() { // TODO for (auto ocontinuation : world().copy_continuations()) { @@ -222,10 +138,9 @@ void Cleaner::eliminate_params() { assert(use.index() == 0); for (auto ucontinuation : uapp->using_continuations()) { ucontinuation->jump(ncontinuation, uapp->args().cut(proxy_idx), ucontinuation->debug()); + todo_ = true; } } - - todo_ = true; } } next_continuation:; @@ -235,7 +150,6 @@ next_continuation:; void Cleaner::rebuild() { auto fresh_world = std::make_unique(world()); Importer importer(world(), *fresh_world); - importer.def_old2new_.rehash(world_->defs().capacity()); for (auto&& [_, def] : world().externals()) { if (auto cont = def->isa(); cont && cont->is_exported()) @@ -244,7 +158,7 @@ void Cleaner::rebuild() { importer.import(global); } - std::swap(world_, fresh_world); + std::swap(thorin_.world_container(), fresh_world); // verify(world()); @@ -281,7 +195,7 @@ void Cleaner::clean_pe_info(std::queue queue, Continuation* cur) auto next = body->arg(3); auto msg = body->arg(1)->as()->from()->as()->init()->as(); - world_->idef(body->callee(), "pe_info was not constant: {}: {}", msg->as_string(), body->arg(2)); + world().idef(body->callee(), "pe_info was not constant: {}: {}", msg->as_string(), body->arg(2)); cur->jump(next, {body->arg(0)}, cur->debug()); // TODO debug todo_ = true; @@ -290,7 +204,7 @@ void Cleaner::clean_pe_info(std::queue queue, Continuation* cur) } void Cleaner::clean_pe_infos() { - world_->VLOG("cleaning remaining pe_infos"); + world().VLOG("cleaning remaining pe_infos"); std::queue queue; ContinuationSet done; auto enqueue = [&](Continuation* continuation) { @@ -321,24 +235,23 @@ void Cleaner::clean_pe_infos() { void Cleaner::cleanup_fix_point() { int i = 0; for (; todo_; ++i) { - world_->VLOG("iteration: {}", i); + world().VLOG("iteration: {}", i); todo_ = false; - if (world_->is_pe_done()) - eliminate_tail_rec(); - eta_conversion(); + rebuild(); + eliminate_tail_rec(); eliminate_params(); rebuild(); // resolve replaced defs before going to resolve_loads todo_ |= resolve_loads(world()); rebuild(); - if (!world().is_pe_done()) - todo_ |= partial_evaluation(*world_); - else - clean_pe_infos(); + //if (!world().is_pe_done()) + todo_ |= partial_evaluation(world()); + //else + // clean_pe_infos(); } } void Cleaner::cleanup() { - world_->VLOG("start cleanup"); + world().VLOG("start cleanup"); cleanup_fix_point(); if (!world().is_pe_done()) { @@ -352,13 +265,13 @@ void Cleaner::cleanup() { cleanup_fix_point(); } - world_->VLOG("end cleanup"); + world().VLOG("end cleanup"); #if THORIN_ENABLE_CHECKS verify_closedness(); debug_verify(world()); #endif } -void cleanup_world(std::unique_ptr& world) { Cleaner(world).cleanup(); } +void Thorin::cleanup() { Cleaner(*this).cleanup(); } } diff --git a/src/thorin/transform/cleanup_world.h b/src/thorin/transform/cleanup_world.h deleted file mode 100644 index afcb68b75..000000000 --- a/src/thorin/transform/cleanup_world.h +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef THORIN_CLEANUP_WORLD_H -#define THORIN_CLEANUP_WORLD_H - -namespace thorin { - -class World; - -void cleanup_world(std::unique_ptr& world); - -} - -#endif diff --git a/src/thorin/transform/closure_conversion.cpp b/src/thorin/transform/closure_conversion.cpp index a91b55865..9140f4132 100644 --- a/src/thorin/transform/closure_conversion.cpp +++ b/src/thorin/transform/closure_conversion.cpp @@ -124,7 +124,7 @@ class ClosureConversion { return continuation && (!continuation->has_body() || continuation->is_intrinsic()); }); free_vars.shrink(filtered_out - free_vars.begin()); - auto lifted = lift(scope, free_vars); + auto lifted = lift(scope, scope.entry(), free_vars); // get the environment type const Type* env_type = nullptr; diff --git a/src/thorin/transform/codegen_prepare.cpp b/src/thorin/transform/codegen_prepare.cpp index 0f0279d56..e8218a571 100644 --- a/src/thorin/transform/codegen_prepare.cpp +++ b/src/thorin/transform/codegen_prepare.cpp @@ -1,37 +1,53 @@ #include "thorin/world.h" #include "thorin/analyses/scope.h" +#include "thorin/transform/rewrite.h" namespace thorin { -void codegen_prepare(World& world) { - world.VLOG("start codegen_prepare"); - ScopesForest forest(world); - forest.for_each([&](Scope& scope) { - world.DLOG("scope: {}", scope.entry()); - bool dirty = false; - auto ret_param = scope.entry()->ret_param(); - assert(ret_param && "scopes should have a return parameter"); - auto ret_cont = world.continuation(ret_param->type()->as(), ret_param->debug()); - ret_cont->jump(ret_param, ret_cont->params_as_defs(), ret_param->debug()); - - for (auto use : ret_param->copy_uses()) { - if (auto uapp = use->isa()) { - if (use.index() != 0) { - auto nops = uapp->copy_ops(); - nops[use.index()] = ret_cont; - auto napp = uapp->rebuild(world, uapp->type(), nops); - uapp->replace_uses(napp); - dirty = true; +struct CodegenPrepare : public Rewriter { + CodegenPrepare(World& src, World& dst) : Rewriter(src, dst) {} + + Continuation* make_wrapper(const Def* old_return_param) { + assert(old_return_param); + auto npi = instantiate(old_return_param->type())->as(); + npi = dst().fn_type(npi->types()); + auto wrapper = dst().continuation(npi, old_return_param->debug()); + return wrapper; + } + + const Def* rewrite(const Def* odef) override { + if (auto app = odef->isa()) { + auto new_ops = Array(app->num_args(), [&](size_t i) -> const Def* { + auto oarg = app->arg(i); + if (auto oparam = oarg->isa()) { + if (oparam == oparam->continuation()->ret_param()) { + auto wrapped = make_wrapper(oarg); + insert(oarg, wrapped); + auto imported_param = instantiate(oparam->continuation())->as_nom()->ret_param(); + wrapped->jump(imported_param, wrapped->params_as_defs(), imported_param->debug()); + return wrapped; + } } - } + return instantiate(app->arg(i)); + }); + return dst().app(instantiate(app->callee()), new_ops); } + return Rewriter::rewrite(odef); + } +}; + +/// this pass makes sure the return param is only called directly, by eta-expanding any uses where it appears in another position +void codegen_prepare(Thorin& thorin) { + thorin.world().VLOG("start codegen_prepare"); + auto& src = thorin.world(); + auto destination = std::make_unique(src); + CodegenPrepare pass(src, *destination.get()); + + for (auto& external : src.externals()) + pass.instantiate(external.second); - if (dirty) - scope.update(); - else - ret_cont->destroy("codegen_prepare"); //Destroy the ret_cont if it was never used. - }); - world.VLOG("end codegen_prepare"); + thorin.world_container().swap(destination); + thorin.world().VLOG("end codegen_prepare"); } } diff --git a/src/thorin/transform/codegen_prepare.h b/src/thorin/transform/codegen_prepare.h index fb4c91bd8..919878260 100644 --- a/src/thorin/transform/codegen_prepare.h +++ b/src/thorin/transform/codegen_prepare.h @@ -5,7 +5,7 @@ namespace thorin { class World; -void codegen_prepare(World&); +void codegen_prepare(Thorin&); } diff --git a/src/thorin/transform/hls_channels.cpp b/src/thorin/transform/hls_channels.cpp index f4c891133..a5e968a79 100644 --- a/src/thorin/transform/hls_channels.cpp +++ b/src/thorin/transform/hls_channels.cpp @@ -193,12 +193,14 @@ DeviceParams hls_channels(Thorin& thorin, Importer& importer, Top2Kernel& top2ke world.make_internal(old_kernel); - Rewriter rewriter; + // TODO this is now broken + // TODO this should likely use the mangler + Rewriter rewriter(world); // Map the parameters of the old kernel to the first N parameters of the new one // The channels used inside the kernel are mapped to the parameters N + 1, N + 2, ... - for (auto pair : index2def) { + /*for (auto pair : index2def) { auto param = new_kernel->param(pair.first); - rewriter.old2new[pair.second] = param; + rewriter.insert(pair.second, param); param2arg[param] = pair.second; // (channel params, globals) } for (auto def : scope.defs()) { @@ -206,9 +208,9 @@ DeviceParams hls_channels(Thorin& thorin, Importer& importer, Top2Kernel& top2ke // Copy the basic block by calling stub // Or reuse the newly created kernel copy if def is the old kernel auto new_cont = def == old_kernel ? new_kernel : cont->mangle_stub(); - rewriter.old2new[cont] = new_cont; + rewriter.insert(cont, new_cont); for (size_t i = 0; i < cont->num_params(); ++i) - rewriter.old2new[cont->param(i)] = new_cont->param(i); + rewriter.insert(cont->param(i), new_cont->param(i)); } } // Rewriting the basic blocks of the kernel using the map @@ -216,7 +218,7 @@ DeviceParams hls_channels(Thorin& thorin, Importer& importer, Top2Kernel& top2ke if (auto cont = def->isa_nom()) { // all basic blocks of the scope if (!cont->has_body()) continue; auto body = cont->body(); - auto new_cont = rewriter.old2new[cont]->isa_nom(); + auto new_cont = rewriter.instantiate(cont)->isa_nom(); auto new_callee = rewriter.instantiate(body->callee()); Array new_args(body->num_args()); for ( size_t i = 0; i < body->num_args(); ++i) @@ -225,7 +227,7 @@ DeviceParams hls_channels(Thorin& thorin, Importer& importer, Top2Kernel& top2ke } } if (!is_single_kernel(new_kernel)) - kernels_ch_modes.emplace_back(def2mode); + kernels_ch_modes.emplace_back(def2mode);*/ }); @@ -274,7 +276,7 @@ DeviceParams hls_channels(Thorin& thorin, Importer& importer, Top2Kernel& top2ke for (auto def : old_world.defs()) { if (auto ocontinuation = def->isa_nom()) { auto ncontinuation = elem->as()->continuation(); - if (ncontinuation == importer.def_old2new_[ocontinuation]) { + if (ncontinuation == importer.import(ocontinuation)) { elem = ocontinuation->param(elem->as()->index()); break; } diff --git a/src/thorin/transform/importer.cpp b/src/thorin/transform/importer.cpp index 4691e9795..9bc97bc9b 100644 --- a/src/thorin/transform/importer.cpp +++ b/src/thorin/transform/importer.cpp @@ -1,43 +1,128 @@ #include "thorin/transform/importer.h" +#include "thorin/transform/mangle.h" +#include "thorin/primop.h" namespace thorin { -const Def* Importer::import(const Def* odef) { - if (auto ndef = def_old2new_.lookup(odef)) { - assert(&(*ndef)->world() == &world()); - return *ndef; - } +const Def* Importer::rewrite(const Def* const odef) { + assert(&odef->world() == &src()); + if (auto memop = odef->isa()) { + // Optimise out dead loads when importing + if (memop->isa() || memop->isa()) { + if (memop->out(1)->num_uses() == 0) { + auto imported_mem = import(memop->mem()); + auto imported_ty = import(memop->out(1)->type())->as(); + todo_ = true; + return(dst().tuple({ imported_mem, dst().bottom(imported_ty) })); + } + } + } else if (auto app = odef->isa()) { + // eat calls to known continuations that are only used once + if (auto callee = app->callee()->isa_nom()) { + if (callee->has_body() && !src().is_external(callee) && callee->can_be_inlined()) { + todo_ = true; + src().VLOG("simplify: inlining continuation {} because it is called exactly once", callee); + for (size_t i = 0; i < callee->num_params(); i++) + insert(callee->param(i), import(app->arg(i))); - if (odef == odef->world().star()) { - def_old2new_[odef] = world().star(); - return world().star(); - } + return instantiate(callee->body()); + } + } + } else if (auto closure = odef->isa()) { + bool only_called = true; + for (auto use : closure->uses()) { + if (use.def()->isa() && use.index() == 0) + continue; + only_called = false; + break; + } + if (only_called) { + bool self_param_ok = true; + for (auto use: closure->fn()->params().back()->uses()) { + // the closure argument can be used, but only to extract the environment! + if (auto extract = use.def()->isa(); extract && is_primlit(extract->index(), 1)) + continue; + self_param_ok = false; + break; + } + if (self_param_ok) { + src().VLOG("simplify: eliminating closure {} as it is never passed as an argument, and is not recursive", closure); + Array args(closure->fn()->num_params()); + args.back() = closure; + todo_ = true; + return instantiate(drop(closure->fn(), args)); + } + } + } else if (auto cont = odef->isa_nom()) { + if (cont->has_body()) { + auto body = cont->body(); + // try to subsume continuations which call a def + // (that is free within that continuation) with that def + auto callee = body->callee(); + auto& scope = forest_->get_scope(cont); + if (!scope.contains(callee)) { + if (src().is_external(cont) || callee->type()->tag() != Node_FnType) + goto rebuild; - auto ntype = import(odef->type())->as(); + if (body->args() == cont->params_as_defs()) { + src().VLOG("simplify: continuation {} calls a free def: {}", cont->unique_name(), body->callee()); + // We completely replace the original continuation + // If we don't do so, then we miss some simplifications + return instantiate(body->callee()); + } else { + // build the permutation of the arguments + Array perm(body->num_args()); + bool is_permutation = true; + for (size_t i = 0, e = body->num_args(); i != e; ++i) { + auto param_it = std::find(cont->params().begin(), + cont->params().end(), + body->arg(i)); - Def* stub = nullptr; - if (odef->isa_nom()) { - stub = odef->stub(world(), ntype); - def_old2new_[odef] = stub; - } + if (param_it == cont->params().end()) { + is_permutation = false; + break; + } + + perm[i] = param_it - cont->params().begin(); + } + + if (!is_permutation) + goto rebuild; + } + + bool has_calls = false; + // for every use of the continuation at a call site, + // permute the arguments and call the parameter instead + for (auto use : cont->copy_uses()) { + auto uapp = use->isa(); + if (uapp && use.index() == 0) { + todo_ = true; + has_calls = true; + break; + } + } + + if (has_calls) { + auto rebuilt = cont->stub(*this, instantiate(cont->type())->as()); + src().VLOG("simplify: continuation {} calls a free def: {} (with permuted args), introducing a wrapper: {}", cont->unique_name(), body->callee(), rebuilt); + auto wrapped = dst().run(rebuilt); + insert(odef, wrapped); - size_t size = odef->num_ops(); - Array nops(size); - for (size_t i = 0; i != size; ++i) { - assert(odef->op(i) != odef); - nops[i] = import(odef->op(i)); - assert(&nops[i]->world() == &world()); + rebuilt->set_body(instantiate(body)->as()); + return wrapped; + } + } + } } + rebuild: + auto ndef = Rewriter::rewrite(odef); if (odef->isa_structural()) { - auto ndef = odef->rebuild(world(), ntype, nops); + // If some substitution took place + // TODO: this might be dead code at the moment todo_ |= odef->tag() != ndef->tag(); - return def_old2new_[odef] = ndef; - } else { - assert(odef->isa_nom() && stub); - stub->rebuild_from(odef, nops); - return stub; } + return ndef; } } diff --git a/src/thorin/transform/importer.h b/src/thorin/transform/importer.h index 10941b915..91110ade9 100644 --- a/src/thorin/transform/importer.h +++ b/src/thorin/transform/importer.h @@ -3,31 +3,29 @@ #include "thorin/world.h" #include "thorin/config.h" +#include "thorin/transform/rewrite.h" +#include "thorin/analyses/scope.h" namespace thorin { -class Importer { +class Importer : Rewriter { public: explicit Importer(World& src, World& dst) - : src(src) - , dst(dst) + : Rewriter(src, dst), forest_(std::make_unique(src)) { + assert(&src != &dst); if (src.is_pe_done()) - world().mark_pe_done(); -#if THORIN_ENABLE_CHECKS - if (src.track_history()) - world().enable_history(true); -#endif + dst.mark_pe_done(); } - World& world() { return dst; } - const Def* import(const Def*); + const Def* import(const Def* odef) { return instantiate(odef); } bool todo() const { return todo_; } -public: - Def2Def def_old2new_; - World& src; - World& dst; +protected: + const Def* rewrite(const Def* odef) override; + +private: + std::unique_ptr forest_; bool todo_ = false; }; diff --git a/src/thorin/transform/lift_builtins.cpp b/src/thorin/transform/lift_builtins.cpp index 8f21d44ee..6bd509da6 100644 --- a/src/thorin/transform/lift_builtins.cpp +++ b/src/thorin/transform/lift_builtins.cpp @@ -105,7 +105,7 @@ void lift_builtins(Thorin& thorin) { } } - auto lifted = lift(scope, defs); + auto lifted = lift(scope, scope.entry(), defs); for (auto use : cur->copy_uses()) { if (auto uapp = use->isa()) { if (auto callee = uapp->callee()->isa_nom()) { diff --git a/src/thorin/transform/mangle.cpp b/src/thorin/transform/mangle.cpp index b184d97c5..77bd201ab 100644 --- a/src/thorin/transform/mangle.cpp +++ b/src/thorin/transform/mangle.cpp @@ -7,31 +7,16 @@ namespace thorin { -const Def* Rewriter::instantiate(const Def* odef) { - if (auto ndef = old2new.lookup(odef)) return *ndef; - - if (odef->isa_structural() && !odef->isa()) { - Array nops(odef->num_ops()); - for (size_t i = 0; i != odef->num_ops(); ++i) - nops[i] = instantiate(odef->op(i)); - - auto nprimop = odef->rebuild(odef->world(), odef->type(), nops); - return old2new[odef] = nprimop; - } - - return old2new[odef] = odef; -} - /// Mangles a continuation's scope /// @p args has the size of the original continuation, a null entry means the parameter remains, non-null substitutes it in scope and removes it from the signature /// @p lift lists defs that should be replaced by a fresh param, to be appended at the end of the signature -Mangler::Mangler(const Scope& scope, Defs args, Defs lift) - : scope_(scope) +Mangler::Mangler(const Scope& scope, Continuation* entry, Defs args, Defs lift) + : Rewriter(scope.world()) + , scope_(scope) , args_(args) , lift_(lift) - , old_entry_(scope.entry()) + , old_entry_(entry) , defs_(scope.defs().capacity()) - , def2def_(scope.defs().capacity()) { assert(old_entry()->has_body()); assert(args.size() == old_entry()->num_params()); @@ -52,6 +37,10 @@ Mangler::Mangler(const Scope& scope, Defs args, Defs lift) for (auto use : pop(queue)->uses()) enqueue(use); } + + is_dropping_ = std::any_of(args.begin(), args.end(), [&](const auto& item) { + return item != nullptr; + }); } Continuation* Mangler::mangle() { @@ -62,122 +51,95 @@ Continuation* Mangler::mangle() { param_types.emplace_back(old_entry()->param(i)->type()); // TODO reduce } - auto fn_type = world().fn_type(param_types); - new_entry_ = world().continuation(fn_type, old_entry()->debug_history()); + auto fn_type = dst().fn_type(param_types); + new_entry_ = dst().continuation(fn_type, old_entry()->debug()); - // map value params - def2def_[old_entry()] = old_entry(); for (size_t i = 0, j = 0, e = old_entry()->num_params(); i != e; ++i) { auto old_param = old_entry()->param(i); if (auto def = args_[i]) - def2def_[old_param] = def; + insert(old_param, def); else { // we recreate params that aren't specialized auto new_param = new_entry()->param(j++); - def2def_[old_param] = new_param; + insert(old_param, new_param); new_param->set_name(old_param->name()); } } for (auto def : lift_) - def2def_[def] = new_entry()->append_param(def->type()); // TODO reduce + insert(def, new_entry()->append_param(def->type())); + + // if we are dropping parameters, we can't necessarily rewrite the entry, see also note about applications in Mangler::rewrite() + if (is_dropping_) + 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 (auto def : lift_) + args.push_back(new_entry()->param(recursion_wrapper->num_params() + i++)); + recursion_wrapper->jump(new_entry(), args); + } - // mangle filter + // cut/widen filter if (!old_entry()->filter()->is_empty()) { Array new_conditions(new_entry()->num_params()); size_t j = 0; for (size_t i = 0, e = old_entry()->num_params(); i != e; ++i) { if (args_[i] == nullptr) - new_conditions[j++] = mangle(old_entry()->filter()->condition(i)); + new_conditions[j++] = instantiate(old_entry()->filter()->condition(i)); } for (size_t e = new_entry()->num_params(); j != e; ++j) - new_conditions[j] = world().literal_bool(false, Debug{}); + new_conditions[j] = dst().literal_bool(false, Debug{}); - new_entry()->set_filter(world().filter(new_conditions, old_entry()->filter()->debug())); + new_entry()->set_filter(dst().filter(new_conditions, old_entry()->filter()->debug())); } - new_entry()->set_body(mangle_body(old_entry()->body())); - + new_entry()->set_body(instantiate(old_entry()->body())->as()); new_entry()->verify(); return new_entry(); } -Continuation* Mangler::mangle_head(Continuation* old_continuation) { - assert(!def2def_.contains(old_continuation)); - assert(old_continuation->has_body()); - Rewriter rewriter{def2def_}; - Continuation* new_continuation = old_continuation->mangle_stub(rewriter); - def2def_[old_continuation] = new_continuation; - - for (size_t i = 0, e = old_continuation->num_params(); i != e; ++i) - def2def_[old_continuation->param(i)] = new_continuation->param(i); - - return new_continuation; -} - -const App* Mangler::mangle_body(const App* old_body) { - Array nops(old_body->num_ops()); - for (size_t i = 0, e = nops.size(); i != e; ++i) - nops[i] = mangle(old_body->op(i)); - - Defs nargs(nops.skip_front()); // new args of body - auto ntarget = nops.front(); // new target of body - - // check whether we can optimize tail recursion - if (ntarget == old_entry()) { - std::vector cut; - bool substitute = true; - for (size_t i = 0, e = args_.size(); i != e && substitute; ++i) { - if (auto def = args_[i]) { - substitute &= def == nargs[i]; - cut.push_back(i); +const Def* Mangler::rewrite(const Def* old_def) { + if (!within(old_def)) + return old_def; // we leave free variables alone + if (auto param = old_def->isa()) + assert(within(param->continuation()) && "if the param is not free, the continuation should not be either!"); + auto ndef = Rewriter::rewrite(old_def); + if (auto app = ndef->isa()) { + // If you drop a parameter it is replaced by some other def, which will be identical for all recursive calls, because it's now specialised + // If there originally was a recursive call that specified the to-be-dropped parameter to something else, we need to call the unmangled original to preserve semantics + if (is_dropping_ && app->callee() == old_entry()) { + auto oargs = app->args(); + auto nargs = Array(oargs.size(), [&](size_t i) { return rewrite(oargs[i]); }); + std::vector cut; + bool substitute = true; + for (size_t i = 0, e = args_.size(); i != e && substitute; ++i) { + if (auto def = args_[i]) { + substitute &= def == nargs[i]; + cut.push_back(i); + } } - } - if (substitute) { - // Q: why not always change to the mangled continuation ? - // A: if you drop a parameter it is replaced by some def (likely a free param), which will be identical for all recursive calls, since they live in the same scope (that's how scopes work) - // so if there originally was a recursive call that specified the to-be-dropped parameter to something else, we need to call the unmangled original to preserve semantics - const auto& args = concat(nargs.cut(cut), new_entry()->params().get_back(lift_.size())); - return world().app(new_entry(), args, old_body->debug()); // TODO debug + if (substitute) { + const auto& args = concat(nargs.cut(cut), new_entry()->params().get_back(lift_.size())); + return dst().app(new_entry(), args, old_def->debug()); // TODO debug + } } } - - return world().app(ntarget, nargs, old_body->debug()); // TODO debug -} - -const Def* Mangler::mangle(const Def* old_def) { - if (auto new_def = def2def_.lookup(old_def)) - return *new_def; - else if (!within(old_def)) - return old_def; // we leave free variables alone - else if (auto old_continuation = old_def->isa_nom()) { - auto new_continuation = mangle_head(old_continuation); - if (old_continuation->has_body()) - new_continuation->set_body(mangle_body(old_continuation->body())); - return new_continuation; - } else if (auto param = old_def->isa()) { - assert(within(param->continuation())); - mangle(param->continuation()); - assert(def2def_.contains(param)); - return def2def_[param]; - } else { - Array nops(old_def->num_ops()); - for (size_t i = 0, e = old_def->num_ops(); i != e; ++i) - nops[i] = mangle(old_def->op(i)); - - auto type = old_def->type(); // TODO reduce - assert(!old_def->isa()); - return def2def_[old_def] = old_def->rebuild(world(), type, nops); - } + return ndef; } //------------------------------------------------------------------------------ -Continuation* mangle(const Scope& scope, Defs args, Defs lift) { - return Mangler(scope, args, lift).mangle(); +Continuation* mangle(const Scope& scope, Continuation* entry, Defs args, Defs lift) { + return Mangler(scope, entry, args, lift).mangle(); } Continuation* drop(const Def* callee, const Defs specialized_args) { diff --git a/src/thorin/transform/mangle.h b/src/thorin/transform/mangle.h index 162da6071..353c69fee 100644 --- a/src/thorin/transform/mangle.h +++ b/src/thorin/transform/mangle.h @@ -3,54 +3,48 @@ #include "thorin/type.h" #include "thorin/analyses/scope.h" +#include "thorin/transform/rewrite.h" namespace thorin { -struct Rewriter { - const Def* instantiate(const Def* odef); - Def2Def old2new; -}; - -class Mangler { +class Mangler : Rewriter { public: - Mangler(const Scope& scope, Defs args, Defs lift); + Mangler(const Scope& scope, Continuation* entry, Defs args, Defs lift); const Scope& scope() const { return scope_; } - World& world() const { return scope_.world(); } Continuation* mangle(); Continuation* old_entry() const { return old_entry_; } Continuation* new_entry() const { return new_entry_; } private: - const App* mangle_body(const App* obody); - Continuation* mangle_head(Continuation* ocontinuation); - const Def* mangle(const Def* odef); + const Def* rewrite(const Def* odef) override; bool within(const Def* def) { return scope().contains(def) || defs_.contains(def); } + bool is_dropping_; + const Scope& scope_; Defs args_; Defs lift_; Continuation* old_entry_; Continuation* new_entry_; DefSet defs_; - Def2Def def2def_; }; -Continuation* mangle(const Scope&, Defs args, Defs lift); +Continuation* mangle(const Scope&, Continuation* entry, Defs args, Defs lift); inline Continuation* drop(const Scope& scope, Defs args) { - return mangle(scope, args, Array()); + return mangle(scope, scope.entry(), args, Array()); } Continuation* drop(const Def* callee, const Defs specialized_args); -inline Continuation* lift(const Scope& scope, Defs defs) { - return mangle(scope, Array(scope.entry()->num_params()), defs); +inline Continuation* lift(const Scope& scope, Continuation* entry, Defs defs) { + return mangle(scope, entry, Array(entry->num_params()), defs); } inline Continuation* clone(const Scope& scope) { - return mangle(scope, Array(scope.entry()->num_params()), Defs()); + return mangle(scope, scope.entry(), Array(scope.entry()->num_params()), Defs()); } } diff --git a/src/thorin/transform/partial_evaluation.cpp b/src/thorin/transform/partial_evaluation.cpp index 92ab41fa7..7117fd15a 100644 --- a/src/thorin/transform/partial_evaluation.cpp +++ b/src/thorin/transform/partial_evaluation.cpp @@ -40,35 +40,28 @@ class PartialEvaluator { size_t boundary_; }; +const Def* BetaReducer::rewrite(const Def* odef) { + // leave nominal defs alone + if (odef->isa_nom()) + return odef; + return Rewriter::rewrite(odef); +} + class CondEval { public: CondEval(Continuation* callee, ScopesForest& forest, Defs args) - : callee_(callee) + : reducer_(callee->world()) + , callee_(callee) , forest_(forest) { assert(callee->filter()->is_empty() || callee->filter()->size() == args.size()); assert(callee->num_params() == args.size()); for (size_t i = 0, e = args.size(); i != e; ++i) - old2new_[callee->param(i)] = args[i]; + reducer_.provide_arg(callee->param(i), args[i]); } World& world() { return callee_->world(); } - const Def* instantiate(const Def* odef) { - if (auto ndef = old2new_.lookup(odef)) - return *ndef; - - if (odef->isa_structural() && !odef->isa()) { - Array nops(odef->num_ops()); - for (size_t i = 0; i != odef->num_ops(); ++i) - nops[i] = instantiate(odef->op(i)); - - auto nprimop = odef->rebuild(world(), odef->type(), nops); - return old2new_[odef] = nprimop; - } - - return old2new_[odef] = odef; - } bool eval(size_t i, bool lower2cff) { // the only higher order parameter that is allowed is a single 1st-order fn-parameter of a top-level continuation @@ -82,7 +75,7 @@ class CondEval { return true; } - return ((!callee_->is_exported() || callee_->attributes().cc == CC::Internal) && callee_->can_be_inlined()) || is_one(instantiate(filter(i))); + return ((!callee_->is_exported() || callee_->attributes().cc == CC::Internal) && callee_->can_be_inlined()) || is_one(reducer_.instantiate(filter(i))); //return is_one(instantiate(filter(i))); } @@ -95,8 +88,8 @@ class CondEval { } private: + BetaReducer reducer_; Continuation* callee_; - Def2Def old2new_; ScopesForest& forest_; }; diff --git a/src/thorin/transform/partial_evaluation.h b/src/thorin/transform/partial_evaluation.h index 4231e9fd6..fd5c2f908 100644 --- a/src/thorin/transform/partial_evaluation.h +++ b/src/thorin/transform/partial_evaluation.h @@ -1,10 +1,24 @@ #ifndef THORIN_TRANSFORM_PARTIAL_EVALUATION_H #define THORIN_TRANSFORM_PARTIAL_EVALUATION_H +#include "thorin/transform/rewrite.h" + namespace thorin { class World; +class BetaReducer : public Rewriter { +public: + BetaReducer(World& w) : Rewriter(w) {} + + void provide_arg(const Param* param, const Def* arg) { + insert(param, arg); + } + +protected: + const Def* rewrite(const Def* odef) override; +}; + bool partial_evaluation(World&, bool lower2cff = false); } diff --git a/src/thorin/transform/rewrite.cpp b/src/thorin/transform/rewrite.cpp new file mode 100644 index 000000000..ee4e4bec0 --- /dev/null +++ b/src/thorin/transform/rewrite.cpp @@ -0,0 +1,66 @@ +#include "rewrite.h" + +namespace thorin { + +Rewriter::Rewriter(World& src, World& dst) : src_(src), dst_(dst) { + old2new_.rehash(src.defs().capacity()); +} + +Rewriter::Rewriter(World& src, World& dst, Rewriter& parent) : Rewriter(src, dst) { + old2new_ = parent.old2new_; +} + +const Def* Rewriter::lookup(const thorin::Def* odef) { + if (auto ndef = old2new_.lookup(odef)) return *ndef; + + // TODO maybe we want to deal with intrinsics in a more streamlined way + if (odef == src().branch()) + return dst().branch(); + if (odef == src().end_scope()) + return dst().end_scope(); + return nullptr; +} + +const Def* Rewriter::instantiate(const Def* odef) { + auto found = lookup(odef); + if (found) return found; + + return old2new_[odef] = rewrite(odef); +} + +const Def* Rewriter::insert(const Def* odef, const Def* ndef) { + assert(&odef->world() == &src()); + assert(&ndef->world() == &dst()); + return old2new_[odef] = ndef; +} + +const Def* Rewriter::rewrite(const Def* odef) { + if (odef == odef->world().star()) + return insert(odef, dst().star()); + + auto ntype = instantiate(odef->type())->as(); + + Def* stub = nullptr; + if (odef->isa_nom()) { + stub = odef->stub(*this, ntype); + insert(odef, stub); + } + + if (odef->isa_structural()) { + size_t size = odef->num_ops(); + Array nops(size); + for (size_t i = 0; i != size; ++i) { + assert(odef->op(i) != odef); + nops[i] = instantiate(odef->op(i)); + assert(&nops[i]->world() == &dst()); + } + auto ndef = odef->rebuild(dst(), ntype, nops); + return ndef; + } else { + assert(odef->isa_nom() && stub); + stub->rebuild_from(*this, odef); + return stub; + } +} + +} \ No newline at end of file diff --git a/src/thorin/transform/rewrite.h b/src/thorin/transform/rewrite.h new file mode 100644 index 000000000..60fd1fa3d --- /dev/null +++ b/src/thorin/transform/rewrite.h @@ -0,0 +1,32 @@ +#ifndef THORIN_REWRITE_H +#define THORIN_REWRITE_H + +#include "thorin/world.h" + +namespace thorin { + +class Rewriter { +public: + explicit Rewriter(World& src, World& dst); + explicit Rewriter(World& world) : Rewriter(world, world) {} + + const Def* instantiate(const Def* odef); + const Def* insert(const Def* odef, const Def* ndef); + + World& src() { return src_; } + World& dst() { return dst_; } + +protected: + explicit Rewriter(World& src, World& dst, Rewriter& parent); + virtual const Def* lookup(const Def* odef); + virtual const Def* rewrite(const Def* odef); + +private: + Def2Def old2new_; + World& src_; + World& dst_; +}; + +} + +#endif \ No newline at end of file diff --git a/src/thorin/type.cpp b/src/thorin/type.cpp index b940e5f11..6285890ce 100644 --- a/src/thorin/type.cpp +++ b/src/thorin/type.cpp @@ -5,6 +5,7 @@ #include #include +#include "thorin/transform/rewrite.h" #include "thorin/continuation.h" #include "thorin/primop.h" #include "thorin/world.h" @@ -46,33 +47,33 @@ Array defs2types(ArrayRef defs) { * rebuild */ -const Type* NominalType::rebuild(World& , const Type* , Defs ) const { +const Type* NominalType::rebuild(World& w, const Type* t, Defs o) const { THORIN_UNREACHABLE; } -const Type* BottomType ::rebuild(World& w, const Type* , Defs ) const { return w.bottom_type(); } -const Type* ClosureType ::rebuild(World& w, const Type* , Defs o) const { return w.closure_type(defs2types(o)); } -const Type* DefiniteArrayType ::rebuild(World& w, const Type* , Defs o) const { return w.definite_array_type(o[0]->as(), dim()); } -const Type* FnType ::rebuild(World& w, const Type* , Defs o) const { return w.fn_type(defs2types(o)); } -const Type* FrameType ::rebuild(World& w, const Type* , Defs ) const { return w.frame_type(); } -const Type* IndefiniteArrayType::rebuild(World& w, const Type* , Defs o) const { return w.indefinite_array_type(o[0]->as()); } -const Type* MemType ::rebuild(World& w, const Type* , Defs ) const { return w.mem_type(); } -const Type* PrimType ::rebuild(World& w, const Type* , Defs ) const { return w.prim_type(primtype_tag(), length()); } -const Type* PtrType ::rebuild(World& w, const Type* , Defs o) const { return w.ptr_type(o[0]->as(), length(), device(), addr_space()); } -const Type* TupleType ::rebuild(World& w, const Type* , Defs o) const { return w.tuple_type(defs2types(o)); } +const Type* BottomType ::rebuild(World& w, const Type* t, Defs o) const { return w.bottom_type(); } +const Type* ClosureType ::rebuild(World& w, const Type* t, Defs o) const { return w.closure_type(defs2types(o)); } +const Type* DefiniteArrayType ::rebuild(World& w, const Type* t, Defs o) const { return w.definite_array_type(o[0]->as(), dim()); } +const Type* FnType ::rebuild(World& w, const Type* t, Defs o) const { return w.fn_type(defs2types(o)); } +const Type* FrameType ::rebuild(World& w, const Type* t, Defs o) const { return w.frame_type(); } +const Type* IndefiniteArrayType::rebuild(World& w, const Type* t, Defs o) const { return w.indefinite_array_type(o[0]->as()); } +const Type* MemType ::rebuild(World& w, const Type* t, Defs o) const { return w.mem_type(); } +const Type* PrimType ::rebuild(World& w, const Type* t, Defs o) const { return w.prim_type(primtype_tag(), length()); } +const Type* PtrType ::rebuild(World& w, const Type* t, Defs o) const { return w.ptr_type(o[0]->as(), length(), device(), addr_space()); } +const Type* TupleType ::rebuild(World& w, const Type* t, Defs o) const { return w.tuple_type(defs2types(o)); } /* * stub */ -StructType* StructType::stub(World& world, const Type*) const { - auto type = world.struct_type(name(), num_ops()); +StructType* StructType::stub(Rewriter& rewriter, const Type*) const { + auto type = rewriter.dst().struct_type(name(), num_ops()); std::copy(op_names_.begin(), op_names_.end(), type->op_names().begin()); return type; } -VariantType* VariantType::stub(World& world, const Type*) const { - auto type = world.variant_type(name(), num_ops()); +VariantType* VariantType::stub(Rewriter& rewriter, const Type*) const { + auto type = rewriter.dst().variant_type(name(), num_ops()); std::copy(op_names_.begin(), op_names_.end(), type->op_names().begin()); return type; } diff --git a/src/thorin/type.h b/src/thorin/type.h index 1018a95c3..95719d36c 100644 --- a/src/thorin/type.h +++ b/src/thorin/type.h @@ -114,7 +114,7 @@ class StructType : public NominalType, public TypeOpsMixin { {} public: - virtual StructType* stub(World&, const Type*) const override; + virtual StructType* stub(Rewriter&, const Type*) const override; friend class World; }; @@ -126,7 +126,7 @@ class VariantType : public NominalType, public TypeOpsMixin { {} public: - virtual VariantType* stub(World&, const Type*) const override; + virtual VariantType* stub(Rewriter&, const Type*) const override; bool has_payload() const; diff --git a/src/thorin/world.cpp b/src/thorin/world.cpp index 661bc5e92..8f242c70c 100644 --- a/src/thorin/world.cpp +++ b/src/thorin/world.cpp @@ -22,7 +22,6 @@ #include "thorin/type.h" #include "thorin/analyses/scope.h" #include "thorin/analyses/verify.h" -#include "thorin/transform/cleanup_world.h" #include "thorin/transform/closure_conversion.h" #include "thorin/transform/codegen_prepare.h" #include "thorin/transform/dead_load_opt.h" @@ -1296,14 +1295,14 @@ Thorin::Thorin(const std::string& name) : world_(std::make_unique(name)) {} -void Thorin::cleanup() { cleanup_world(world_); } - void Thorin::opt() { + bool debug_passes = getenv("THORIN_DEBUG_PASSES"); #define RUN_PASS(pass) \ { \ - world().VLOG("running pass {}", #pass); \ - pass; \ - debug_verify(world()); \ + world().VLOG("running pass {}", #pass); \ + pass; \ + debug_verify(world()); \ + if (debug_passes) world().dump_scoped(); \ } RUN_PASS(cleanup()) @@ -1316,7 +1315,7 @@ void Thorin::opt() { RUN_PASS(hoist_enters(*this)) RUN_PASS(dead_load_opt(world())) RUN_PASS(cleanup()) - RUN_PASS(codegen_prepare(world())) + RUN_PASS(codegen_prepare(*this)) } bool Thorin::ensure_stack_size(size_t new_size) { diff --git a/src/thorin/world.h b/src/thorin/world.h index ae070ce04..547dfdca6 100644 --- a/src/thorin/world.h +++ b/src/thorin/world.h @@ -272,6 +272,7 @@ class World : public Streamable { Continuation* branch() const { return data_.branch_; } Continuation* match(const Type* type, size_t num_patterns); Continuation* end_scope() const { return data_.end_scope_; } + const App* app(const Def* callee, const Defs args, Debug dbg = {}); const Filter* filter(const Defs, Debug dbg = {}); // getters @@ -340,7 +341,6 @@ class World : public Streamable { private: const Param* param(const Type* type, const Continuation*, size_t index, Debug dbg); - const App* app(const Def* callee, const Defs args, Debug dbg = {}); const Def* try_fold_aggregate(const Aggregate*); template const Def* transcendental(MathOpTag, const Def*, Debug, F&&); template const Def* transcendental(MathOpTag, const Def*, const Def*, Debug, F&&); @@ -407,6 +407,7 @@ class Thorin { explicit Thorin(const std::string& name); World& world() { return *world_; }; + std::unique_ptr& world_container() { return world_; } /// Performs dead code, unreachable code and unused type elimination. void cleanup(); From e92ef28d74fcd4a1f2df680dc8a5489211550190 Mon Sep 17 00:00:00 2001 From: Matthias Kurtenacker Date: Tue, 19 Sep 2023 12:00:33 +0200 Subject: [PATCH 189/342] BE: Check for is_returning, not is_exported. Resolves isssues where internal Continuations were not emitted at all. --- src/thorin/be/c/c.cpp | 2 +- src/thorin/be/llvm/llvm.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/thorin/be/c/c.cpp b/src/thorin/be/c/c.cpp index 973b84dd2..c0804238b 100644 --- a/src/thorin/be/c/c.cpp +++ b/src/thorin/be/c/c.cpp @@ -360,7 +360,7 @@ void CCodeGen::emit_module() { forest_.for_each([&] (const Scope& scope) { if (scope.entry()->name() == "hls_top") hls_top = scope.entry(); - else if (scope.entry()->cc() != CC::Internal && scope.entry()->is_exported()) + else if (scope.entry()->cc() != CC::Internal && scope.entry()->is_returning()) emit_scope(scope, forest_); }); if (hls_top) { diff --git a/src/thorin/be/llvm/llvm.cpp b/src/thorin/be/llvm/llvm.cpp index b89c66f2a..c846ba293 100644 --- a/src/thorin/be/llvm/llvm.cpp +++ b/src/thorin/be/llvm/llvm.cpp @@ -314,7 +314,7 @@ CodeGen::emit_module() { if(scope.entry()->cc() == CC::Internal) { return; } - if (!scope.entry()->is_exported()) + if (!scope.entry()->is_returning()) return; emit_scope(scope, forest); }); From 33b54a8d313fe145628cf74fd1fc7fb86c658a56 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Tue, 19 Sep 2023 12:05:17 +0200 Subject: [PATCH 190/342] scoped_dump: use stream1 for dumping literals --- 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 1821c32d3..6951af638 100644 --- a/src/thorin/util/scoped_dump.cpp +++ b/src/thorin/util/scoped_dump.cpp @@ -108,6 +108,10 @@ void ScopedWorld::stream_def(thorin::Stream& s, const thorin::Def* def) const { stream_ops(s, app->args()); return; } + if (auto prim_lit = def->isa()) { + def->stream1(s); + return; + } s.fmt(Green); s.fmt("{}", def->op_name()); From 6e3ce324bf6267e66d0a6afa90edad32e8be067b Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Wed, 4 Oct 2023 16:33:40 +0200 Subject: [PATCH 191/342] simplified/rewrote lifitng code in hls_channels --- src/thorin/transform/hls_channels.cpp | 66 ++++++--------------------- 1 file changed, 15 insertions(+), 51 deletions(-) diff --git a/src/thorin/transform/hls_channels.cpp b/src/thorin/transform/hls_channels.cpp index a5e968a79..cafabb63e 100644 --- a/src/thorin/transform/hls_channels.cpp +++ b/src/thorin/transform/hls_channels.cpp @@ -165,24 +165,16 @@ DeviceParams hls_channels(Thorin& thorin, Importer& importer, Top2Kernel& top2ke Def2Mode def2mode; extract_kernel_channels(schedule(scope), def2mode); - Array new_param_types(def2mode.size() + old_kernel->num_params()); - std::copy(old_kernel->type()->types().begin(), - old_kernel->type()->types().end(), - new_param_types.begin()); - size_t i = old_kernel->num_params(); - // This vector records pairs containing: - // - The position of the channel parameter for the new kernel - // - The old global definition for the channel - std::vector> index2def; - for (auto map : def2mode) { - index2def.emplace_back(i, map.first); - new_param_types[i++] = map.first->type(); - } + std::vector channels; + for (auto pair : def2mode) + channels.push_back(pair.first); + + // Map the parameters of the old kernel to the first N parameters of the new one + // The channels used inside the kernel are mapped to the parameters N + 1, N + 2, ... + auto new_kernel = lift(scope, old_kernel, channels); - // new kernels signature - // fn(mem, ret_cnt, ... , /channels/ ) - auto new_kernel = world.continuation(world.fn_type(new_param_types), old_kernel->debug()); world.make_external(new_kernel); + world.make_internal(old_kernel); kernel_new2old.emplace(new_kernel, old_kernel); @@ -191,43 +183,14 @@ DeviceParams hls_channels(Thorin& thorin, Importer& importer, Top2Kernel& top2ke else new_kernels.emplace_back(new_kernel); - world.make_internal(old_kernel); - - // TODO this is now broken - // TODO this should likely use the mangler - Rewriter rewriter(world); - // Map the parameters of the old kernel to the first N parameters of the new one - // The channels used inside the kernel are mapped to the parameters N + 1, N + 2, ... - /*for (auto pair : index2def) { - auto param = new_kernel->param(pair.first); - rewriter.insert(pair.second, param); - param2arg[param] = pair.second; // (channel params, globals) - } - for (auto def : scope.defs()) { - if (auto cont = def->isa_nom()) { - // Copy the basic block by calling stub - // Or reuse the newly created kernel copy if def is the old kernel - auto new_cont = def == old_kernel ? new_kernel : cont->mangle_stub(); - rewriter.insert(cont, new_cont); - for (size_t i = 0; i < cont->num_params(); ++i) - rewriter.insert(cont->param(i), new_cont->param(i)); - } - } - // Rewriting the basic blocks of the kernel using the map - for (auto def : scope.defs()) { - if (auto cont = def->isa_nom()) { // all basic blocks of the scope - if (!cont->has_body()) continue; - auto body = cont->body(); - auto new_cont = rewriter.instantiate(cont)->isa_nom(); - auto new_callee = rewriter.instantiate(body->callee()); - Array new_args(body->num_args()); - for ( size_t i = 0; i < body->num_args(); ++i) - new_args[i] = rewriter.instantiate(body->arg(i)); - new_cont->jump(new_callee, new_args, cont->debug()); - } + for (size_t i = old_kernel->num_params(); i < new_kernel->num_params(); i++) { + auto param = new_kernel->param(i); + assert(param); + param2arg[param] = channels[i - old_kernel->num_params()]; // (channel params -> former globals) } + if (!is_single_kernel(new_kernel)) - kernels_ch_modes.emplace_back(def2mode);*/ + kernels_ch_modes.emplace_back(def2mode); }); @@ -388,6 +351,7 @@ DeviceParams hls_channels(Thorin& thorin, Importer& importer, Top2Kernel& top2ke } else if (param == ret_param) { args[i] = ret; } else if (auto arg = param2arg[param]) { + assert(arg != nullptr); args[i] = arg->isa() && is_channel_type(arg->type()) ? global2slot[arg] : arg; } else { assert(false); From a0017801999bd72b7bbd5413d235c1b9595fb8b1 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Tue, 10 Oct 2023 14:16:58 +0200 Subject: [PATCH 192/342] fix: intrinsics can have multiple ret-alike params --- src/thorin/continuation.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/thorin/continuation.cpp b/src/thorin/continuation.cpp index 28f7dad8e..fec796937 100644 --- a/src/thorin/continuation.cpp +++ b/src/thorin/continuation.cpp @@ -142,7 +142,7 @@ const Param* Continuation::ret_param() const { const Param* result = nullptr; for (auto param : params()) { if (param->order() >= 1) { - assertf(result == nullptr, "only one ret_param allowed"); + assertf(is_intrinsic() || result == nullptr, "only one ret_param allowed"); result = param; } } From deee19101943ce940cc9d8a4b17b95a84b2c3a3e Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Thu, 12 Oct 2023 11:43:21 +0200 Subject: [PATCH 193/342] fixed regression with boundary in PE --- src/thorin/transform/partial_evaluation.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/thorin/transform/partial_evaluation.cpp b/src/thorin/transform/partial_evaluation.cpp index 7117fd15a..af2e4cec6 100644 --- a/src/thorin/transform/partial_evaluation.cpp +++ b/src/thorin/transform/partial_evaluation.cpp @@ -27,7 +27,6 @@ class PartialEvaluator { void enqueue(Continuation* continuation) { if (continuation->gid() < 2 * boundary_ && done_.emplace(continuation).second) queue_.push(continuation); - queue_.push(continuation); } void eat_pe_info(Continuation*); From 26eeecd642baefacfc0397ab59ee13c93d2d8e09 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Thu, 12 Oct 2023 11:51:15 +0200 Subject: [PATCH 194/342] mangle: don't instantiate dead branches if possible --- src/thorin/transform/mangle.cpp | 25 +++++++++++++++++++++++++ src/thorin/world.cpp | 1 + 2 files changed, 26 insertions(+) diff --git a/src/thorin/transform/mangle.cpp b/src/thorin/transform/mangle.cpp index 77bd201ab..96e4b8a4b 100644 --- a/src/thorin/transform/mangle.cpp +++ b/src/thorin/transform/mangle.cpp @@ -111,6 +111,31 @@ const Def* Mangler::rewrite(const Def* old_def) { return old_def; // we leave free variables alone if (auto param = old_def->isa()) assert(within(param->continuation()) && "if the param is not free, the continuation should not be either!"); + if (auto app = old_def->isa()) { + // HACK: only rebuild the branch we actually take + // this is a hack because the uses can be stale (dead stuff can have transitive uses) + if (auto br = app->callee()->isa_nom(); br && br->intrinsic() == Intrinsic::Branch) { + auto condition = instantiate(app->arg(1)); + if (auto lit = condition->isa()) { + auto mem = instantiate(app->arg(0)); + auto target = lit->value().get_bool() ? instantiate(app->arg(2)) : instantiate(app->arg(3)); + return dst().app(target, { mem }); + } + } + if (auto sw = app->callee()->isa_nom(); sw && sw->intrinsic() == Intrinsic::Match) { + auto index = instantiate(app->arg(1)); + if (auto lit = index->isa()) { + for (size_t i = 3; i < app->num_args(); i++) { + auto opattern = src().extract(app->arg(i), 0_s)->as(); + if (instantiate(opattern) == lit) { + auto mem = instantiate(app->arg(0)); + auto target = dst().extract(instantiate(app->arg(i)), 1); + return dst().app(target, { mem }, old_def->debug()); + } + } + } + } + } auto ndef = Rewriter::rewrite(old_def); if (auto app = ndef->isa()) { // If you drop a parameter it is replaced by some other def, which will be identical for all recursive calls, because it's now specialised diff --git a/src/thorin/world.cpp b/src/thorin/world.cpp index 8f242c70c..6d83d3570 100644 --- a/src/thorin/world.cpp +++ b/src/thorin/world.cpp @@ -1153,6 +1153,7 @@ const Filter* World::filter(const Defs defs, Debug dbg) { const App* World::app(const Def* callee, const Defs args, Debug dbg) { if (auto continuation = callee->isa()) { switch (continuation->intrinsic()) { + // See also mangle::instantiate when modifying this. case Intrinsic::Branch: { assert(args.size() == 4); auto mem = args[0], cond = args[1], t = args[2], f = args[3]; From 8ca2c0a1ac71f8c5247d5ff5cd838485f34795f3 Mon Sep 17 00:00:00 2001 From: Matthias Kurtenacker Date: Tue, 19 Sep 2023 13:53:35 +0200 Subject: [PATCH 195/342] CC::Thorin for internal continuations. --- src/thorin/be/c/c.cpp | 2 +- src/thorin/be/codegen.cpp | 1 + src/thorin/be/json/json.cpp | 2 +- src/thorin/be/llvm/llvm.cpp | 3 +-- src/thorin/continuation.h | 6 +++--- src/thorin/rec_stream.cpp | 2 +- src/thorin/transform/partial_evaluation.cpp | 2 +- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/thorin/be/c/c.cpp b/src/thorin/be/c/c.cpp index c0804238b..eee2db783 100644 --- a/src/thorin/be/c/c.cpp +++ b/src/thorin/be/c/c.cpp @@ -360,7 +360,7 @@ void CCodeGen::emit_module() { forest_.for_each([&] (const Scope& scope) { if (scope.entry()->name() == "hls_top") hls_top = scope.entry(); - else if (scope.entry()->cc() != CC::Internal && scope.entry()->is_returning()) + else if (scope.entry()->cc() != CC::Thorin && scope.entry()->is_returning()) emit_scope(scope, forest_); }); if (hls_top) { diff --git a/src/thorin/be/codegen.cpp b/src/thorin/be/codegen.cpp index e53fc2efb..3f8204ef0 100644 --- a/src/thorin/be/codegen.cpp +++ b/src/thorin/be/codegen.cpp @@ -121,6 +121,7 @@ DeviceBackends::DeviceBackends(World& world, int opt, bool debug, std::string& f for (size_t i = 0, e = continuation->num_params(); i != e; ++i) imported->param(i)->set_name(continuation->param(i)->name()); imported->world().make_external(imported); + imported->attributes().cc = CC::C; kernels.emplace_back(continuation); }); diff --git a/src/thorin/be/json/json.cpp b/src/thorin/be/json/json.cpp index ea0ae03c8..8188caa3f 100644 --- a/src/thorin/be/json/json.cpp +++ b/src/thorin/be/json/json.cpp @@ -258,7 +258,7 @@ class DefTable { forward_decl["fn_type"] = type; forward_decl["arg_names"] = arg_names; if (cont->is_external()) { - if (cont->cc() == CC::Internal) + if (cont->cc() == CC::Thorin) forward_decl["internal"] = cont->name(); else forward_decl["external"] = cont->name(); diff --git a/src/thorin/be/llvm/llvm.cpp b/src/thorin/be/llvm/llvm.cpp index 41af5aece..b4ca93de3 100644 --- a/src/thorin/be/llvm/llvm.cpp +++ b/src/thorin/be/llvm/llvm.cpp @@ -311,9 +311,8 @@ CodeGen::emit_module() { ScopesForest forest(world()); forest.for_each([&](const Scope& scope) { - if(scope.entry()->cc() == CC::Internal) { + if(scope.entry()->cc() == CC::Thorin) return; - } if (!scope.entry()->is_returning()) return; emit_scope(scope, forest); diff --git a/src/thorin/continuation.h b/src/thorin/continuation.h index ba72dbf91..e1aada870 100644 --- a/src/thorin/continuation.h +++ b/src/thorin/continuation.h @@ -84,9 +84,9 @@ class App : public Def { //------------------------------------------------------------------------------ enum class CC : uint8_t { + Thorin, ///< Standard calling convention for everything that solely lives inside thorin. C, ///< C calling convention. Device, ///< Device calling convention. These are special functions only available on a particular device. - Internal, ///< External, but only for linking with artic or anyopt. }; enum class Intrinsic : uint8_t { @@ -129,10 +129,10 @@ class Continuation : public Def { public: struct Attributes { Intrinsic intrinsic = Intrinsic::None; - CC cc = CC::C; + CC cc = CC::Thorin; Attributes(Intrinsic intrinsic) : intrinsic(intrinsic) {} - Attributes(CC cc = CC::C) : cc(cc) {} + Attributes(CC cc = CC::Thorin) : cc(cc) {} }; private: diff --git a/src/thorin/rec_stream.cpp b/src/thorin/rec_stream.cpp index 037891722..cb7c5ecac 100644 --- a/src/thorin/rec_stream.cpp +++ b/src/thorin/rec_stream.cpp @@ -49,7 +49,7 @@ void RecStreamer::run() { s.endl().endl(); if (cont->world().is_external(cont)) { - if (cont->attributes().cc == CC::Internal) + if (cont->attributes().cc == CC::Thorin) s.fmt("intern "); else s.fmt("extern "); diff --git a/src/thorin/transform/partial_evaluation.cpp b/src/thorin/transform/partial_evaluation.cpp index af2e4cec6..f0595640e 100644 --- a/src/thorin/transform/partial_evaluation.cpp +++ b/src/thorin/transform/partial_evaluation.cpp @@ -74,7 +74,7 @@ class CondEval { return true; } - return ((!callee_->is_exported() || callee_->attributes().cc == CC::Internal) && callee_->can_be_inlined()) || is_one(reducer_.instantiate(filter(i))); + return ((!callee_->is_exported() || callee_->attributes().cc == CC::Thorin) && callee_->can_be_inlined()) || is_one(reducer_.instantiate(filter(i))); //return is_one(instantiate(filter(i))); } From e369f91db7c6e56efebd1d8d5d2d8e40917710e5 Mon Sep 17 00:00:00 2001 From: Matthias Kurtenacker Date: Tue, 19 Sep 2023 15:40:25 +0200 Subject: [PATCH 196/342] BE: Generate code for exported continuations and everything reachable. --- src/thorin/be/llvm/llvm.cpp | 39 ++++++++++++++++++++++++++++++++----- 1 file changed, 34 insertions(+), 5 deletions(-) diff --git a/src/thorin/be/llvm/llvm.cpp b/src/thorin/be/llvm/llvm.cpp index b4ca93de3..da245fd6d 100644 --- a/src/thorin/be/llvm/llvm.cpp +++ b/src/thorin/be/llvm/llvm.cpp @@ -310,14 +310,43 @@ CodeGen::emit_module() { } ScopesForest forest(world()); + std::queue queue; + ContinuationSet emitted; + + auto enqueue = [&] (Continuation* cont) { + if (emitted.insert(cont).second) { + queue.push(cont); + } + }; + forest.for_each([&](const Scope& scope) { - if(scope.entry()->cc() == CC::Thorin) - return; - if (!scope.entry()->is_returning()) - return; - emit_scope(scope, forest); + if (scope.entry()->is_exported() && scope.entry()->cc() != CC::Thorin) + enqueue(scope.entry()); }); + while (!queue.empty()) { + Continuation* todo = pop(queue); + if (!todo->has_body()) + continue; + + Scope& scope = forest.get_scope(todo); + + emit_scope(scope, forest); + + for(auto free : scope.free_frontier()) { + if (const Continuation* cont_const = free->isa()) { + Continuation* cont = const_cast(cont_const); + enqueue(cont); + } + if (const Global* global_const = free->isa()) { + if (auto cont_const = global_const->init()->isa()) { + Continuation* cont = const_cast(cont_const); + enqueue(cont); + } + } + } + } + if (debug()) dibuilder_.finalize(); #if THORIN_ENABLE_RV From dd2b925c8afd328291adad927580ddab2275cb16 Mon Sep 17 00:00:00 2001 From: Matthias Kurtenacker Date: Tue, 1 Aug 2023 12:24:36 +0200 Subject: [PATCH 197/342] Fix warnings across the compiler. --- src/thorin/analyses/schedule.cpp | 4 ++-- src/thorin/analyses/schedule.h | 2 +- src/thorin/analyses/verify.cpp | 4 +++- src/thorin/be/llvm/llvm.cpp | 4 ---- src/thorin/transform/mangle.cpp | 2 +- src/thorin/type.cpp | 22 +++++++++++----------- src/thorin/util/scoped_dump.cpp | 3 +-- 7 files changed, 19 insertions(+), 22 deletions(-) diff --git a/src/thorin/analyses/schedule.cpp b/src/thorin/analyses/schedule.cpp index 999d5da85..2f7a6bd64 100644 --- a/src/thorin/analyses/schedule.cpp +++ b/src/thorin/analyses/schedule.cpp @@ -57,7 +57,7 @@ void Scheduler::register_defs(const Scope& s) { } } -Continuation* Scheduler::early(const Def* def, DefSet* seen) { +Continuation* Scheduler::early(const Def* def) { if (auto cont = early_.lookup(def)) return *cont; if (auto param = def->isa()) return early_[def] = param->continuation(); assert(false); @@ -71,7 +71,7 @@ Continuation* Scheduler::late(const Def* def) { result = continuation; } else if (auto param = def->isa()) { result = param->continuation(); - } else if (auto rec = def->isa_nom()) { + } else if (def->isa_nom()) { // don't try to late-schedule recursive nodes for now result = early(def); } else { diff --git a/src/thorin/analyses/schedule.h b/src/thorin/analyses/schedule.h index fd0ea8c96..223fec3c1 100644 --- a/src/thorin/analyses/schedule.h +++ b/src/thorin/analyses/schedule.h @@ -26,7 +26,7 @@ class Scheduler { /// @name compute schedules //@{ - Continuation* early(const Def*, DefSet* seen = nullptr); + Continuation* early(const Def*); Continuation* late (const Def*); Continuation* smart(const Def*); //@} diff --git a/src/thorin/analyses/verify.cpp b/src/thorin/analyses/verify.cpp index 0eb3aee65..614bca846 100644 --- a/src/thorin/analyses/verify.cpp +++ b/src/thorin/analyses/verify.cpp @@ -8,7 +8,7 @@ namespace thorin { // TODO this needs serious rewriting -static bool verify_calls(World& world, ScopesForest& forest) { +static bool verify_calls(World& world, ScopesForest&) { bool ok = true; for (auto def : world.defs()) { if (auto cont = def->isa()) @@ -46,6 +46,7 @@ static bool verify_top_level(World& world, ScopesForest& forest) { return ok; } +#if 0 static bool verify_param(World& world) { bool ok = true; for (auto def : world.defs()) { @@ -59,6 +60,7 @@ static bool verify_param(World& world) { } return ok; } +#endif void verify(World& world) { ScopesForest forest(world); diff --git a/src/thorin/be/llvm/llvm.cpp b/src/thorin/be/llvm/llvm.cpp index da245fd6d..a54cd2882 100644 --- a/src/thorin/be/llvm/llvm.cpp +++ b/src/thorin/be/llvm/llvm.cpp @@ -1355,7 +1355,6 @@ llvm::Value* CodeGen::emit_atomic_load(llvm::IRBuilder<>& irbuilder, Continuatio assert(int(llvm::AtomicOrdering::NotAtomic) <= int(tag) && int(tag) <= int(llvm::AtomicOrdering::SequentiallyConsistent) && "unsupported atomic ordering"); auto order = (llvm::AtomicOrdering)tag; auto scope = body->arg(3)->as()->from()->as()->init()->as(); - auto cont = body->arg(4)->as_nom(); auto load = irbuilder.CreateLoad(load_type, ptr); auto align = module().getDataLayout().getABITypeAlign(load_type); load->setAlignment(align); @@ -1373,7 +1372,6 @@ void CodeGen::emit_atomic_store(llvm::IRBuilder<>& irbuilder, Continuation* cont assert(int(llvm::AtomicOrdering::NotAtomic) <= int(tag) && int(tag) <= int(llvm::AtomicOrdering::SequentiallyConsistent) && "unsupported atomic ordering"); auto order = (llvm::AtomicOrdering)tag; auto scope = body->arg(4)->as()->from()->as()->init()->as(); - auto cont = body->arg(5)->as_nom(); auto store = irbuilder.CreateStore(val, ptr); auto align = module().getDataLayout().getABITypeAlign(convert(body->arg(2)->type())); store->setAlignment(align); @@ -1397,7 +1395,6 @@ std::vector CodeGen::emit_cmpxchg(llvm::IRBuilder<>& irbuilder, Co auto success_order = (llvm::AtomicOrdering)success_order_tag; auto failure_order = (llvm::AtomicOrdering)failure_order_tag; auto scope = body->arg(6)->as()->from()->as()->init()->as(); - auto cont = body->arg(7)->as_nom(); auto call = irbuilder.CreateAtomicCmpXchg(ptr, cmp, val, llvm::MaybeAlign(), success_order, failure_order, context().getOrInsertSyncScopeID(scope->as_string())); call->setWeak(is_weak); return { irbuilder.CreateExtractValue(call, 0), irbuilder.CreateExtractValue(call, 1) }; @@ -1411,7 +1408,6 @@ void CodeGen::emit_fence(llvm::IRBuilder<>& irbuilder, Continuation* continuatio assert(int(llvm::AtomicOrdering::NotAtomic) <= int(order_tag) && int(order_tag) <= int(llvm::AtomicOrdering::SequentiallyConsistent) && "unsupported atomic ordering"); auto order = (llvm::AtomicOrdering)order_tag; auto scope = body->arg(2)->as()->from()->as()->init()->as(); - auto cont = body->arg(3)->as_nom(); irbuilder.CreateFence(order, context().getOrInsertSyncScopeID(scope->as_string())); } diff --git a/src/thorin/transform/mangle.cpp b/src/thorin/transform/mangle.cpp index 96e4b8a4b..98b3a559c 100644 --- a/src/thorin/transform/mangle.cpp +++ b/src/thorin/transform/mangle.cpp @@ -80,7 +80,7 @@ Continuation* Mangler::mangle() { for (auto p : recursion_wrapper->params_as_defs()) args.push_back(p); size_t i = 0; - for (auto def : lift_) + for ([[maybe_unused]] auto def : lift_) args.push_back(new_entry()->param(recursion_wrapper->num_params() + i++)); recursion_wrapper->jump(new_entry(), args); } diff --git a/src/thorin/type.cpp b/src/thorin/type.cpp index 6285890ce..d5b84b0dd 100644 --- a/src/thorin/type.cpp +++ b/src/thorin/type.cpp @@ -47,20 +47,20 @@ Array defs2types(ArrayRef defs) { * rebuild */ -const Type* NominalType::rebuild(World& w, const Type* t, Defs o) const { +const Type* NominalType::rebuild(World& , const Type* , Defs ) const { THORIN_UNREACHABLE; } -const Type* BottomType ::rebuild(World& w, const Type* t, Defs o) const { return w.bottom_type(); } -const Type* ClosureType ::rebuild(World& w, const Type* t, Defs o) const { return w.closure_type(defs2types(o)); } -const Type* DefiniteArrayType ::rebuild(World& w, const Type* t, Defs o) const { return w.definite_array_type(o[0]->as(), dim()); } -const Type* FnType ::rebuild(World& w, const Type* t, Defs o) const { return w.fn_type(defs2types(o)); } -const Type* FrameType ::rebuild(World& w, const Type* t, Defs o) const { return w.frame_type(); } -const Type* IndefiniteArrayType::rebuild(World& w, const Type* t, Defs o) const { return w.indefinite_array_type(o[0]->as()); } -const Type* MemType ::rebuild(World& w, const Type* t, Defs o) const { return w.mem_type(); } -const Type* PrimType ::rebuild(World& w, const Type* t, Defs o) const { return w.prim_type(primtype_tag(), length()); } -const Type* PtrType ::rebuild(World& w, const Type* t, Defs o) const { return w.ptr_type(o[0]->as(), length(), device(), addr_space()); } -const Type* TupleType ::rebuild(World& w, const Type* t, Defs o) const { return w.tuple_type(defs2types(o)); } +const Type* BottomType ::rebuild(World& w, const Type* , Defs ) const { return w.bottom_type(); } +const Type* ClosureType ::rebuild(World& w, const Type* , Defs o) const { return w.closure_type(defs2types(o)); } +const Type* DefiniteArrayType ::rebuild(World& w, const Type* , Defs o) const { return w.definite_array_type(o[0]->as(), dim()); } +const Type* FnType ::rebuild(World& w, const Type* , Defs o) const { return w.fn_type(defs2types(o)); } +const Type* FrameType ::rebuild(World& w, const Type* , Defs ) const { return w.frame_type(); } +const Type* IndefiniteArrayType::rebuild(World& w, const Type* , Defs o) const { return w.indefinite_array_type(o[0]->as()); } +const Type* MemType ::rebuild(World& w, const Type* , Defs ) const { return w.mem_type(); } +const Type* PrimType ::rebuild(World& w, const Type* , Defs ) const { return w.prim_type(primtype_tag(), length()); } +const Type* PtrType ::rebuild(World& w, const Type* , Defs o) const { return w.ptr_type(o[0]->as(), length(), device(), addr_space()); } +const Type* TupleType ::rebuild(World& w, const Type* , Defs o) const { return w.tuple_type(defs2types(o)); } /* * stub diff --git a/src/thorin/util/scoped_dump.cpp b/src/thorin/util/scoped_dump.cpp index 6951af638..36b03c015 100644 --- a/src/thorin/util/scoped_dump.cpp +++ b/src/thorin/util/scoped_dump.cpp @@ -14,7 +14,6 @@ void ScopedWorld::stream_cont(thorin::Stream& s, Continuation* cont) const { s.fmt(Reset); s.fmt("("); const FnType* t = cont->type(); - int ret_pi = -1; for (size_t i = 0; i < cont->num_params(); i++) { s.fmt(Yellow); s.fmt("{}: ", cont->param(i)->unique_name()); @@ -108,7 +107,7 @@ void ScopedWorld::stream_def(thorin::Stream& s, const thorin::Def* def) const { stream_ops(s, app->args()); return; } - if (auto prim_lit = def->isa()) { + if (def->isa()) { def->stream1(s); return; } From d0af0711750c4c8f84baac11c2921e33f4af7e77 Mon Sep 17 00:00:00 2001 From: Matthias Kurtenacker Date: Mon, 16 Oct 2023 16:07:35 +0200 Subject: [PATCH 198/342] Add stdbool.h and stdint.h to generated header files. --- src/thorin/be/c/c.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/thorin/be/c/c.cpp b/src/thorin/be/c/c.cpp index eee2db783..f8a6c510d 100644 --- a/src/thorin/be/c/c.cpp +++ b/src/thorin/be/c/c.cpp @@ -1508,6 +1508,9 @@ void CCodeGen::emit_c_int() { stream_.fmt("extern \"C\" {{\n"); stream_.fmt("#endif\n\n"); + stream_.fmt("#include \n" // for the 'bool' type + "#include \n\n"); // for the fixed-width integer types + stream_.fmt("typedef int8_t i8;\n" "typedef uint8_t u8;\n" "typedef int16_t i16;\n" From a947aa84ef546d2b6b3ec0f970b53fbe14a4da45 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Fri, 15 Dec 2023 09:21:11 +0100 Subject: [PATCH 199/342] shady: updated API --- src/thorin/be/shady/shady.cpp | 50 ++++++++++++++++++++++++----------- 1 file changed, 34 insertions(+), 16 deletions(-) diff --git a/src/thorin/be/shady/shady.cpp b/src/thorin/be/shady/shady.cpp index 297606171..b3a6ddf87 100644 --- a/src/thorin/be/shady/shady.cpp +++ b/src/thorin/be/shady/shady.cpp @@ -20,7 +20,7 @@ void CodeGen::emit_stream(std::ostream& out) { ScopesForest forest(world()); forest.for_each([&](const Scope& scope) { - if(scope.entry()->cc() == CC::Internal) { + if(scope.entry()->cc() == CC::Thorin) { return; } emit_scope(scope, forest); @@ -74,9 +74,9 @@ const shady::Type* CodeGen::convert(const Type* type) { case PrimType_pu32: case PrimType_qu32: t = shady::int32_type(arena); break; case PrimType_ps64: case PrimType_qs64: case PrimType_pu64: case PrimType_qu64: t = shady::int64_type(arena); break; - case PrimType_pf16: case PrimType_qf16: assert(false && "TODO"); - case PrimType_pf32: case PrimType_qf32: t = shady::float_type(arena); break; - case PrimType_pf64: case PrimType_qf64: assert(false && "TODO"); + case PrimType_pf16: case PrimType_qf16: t = shady::fp16_type(arena); break; + case PrimType_pf32: case PrimType_qf32: t = shady::fp32_type(arena); break; + case PrimType_pf64: case PrimType_qf64: t = shady::fp64_type(arena); break; default: THORIN_UNREACHABLE; } } else if (auto ptr = type->isa()) { @@ -251,12 +251,12 @@ void CodeGen::prepare(Continuation* cont, shady::Node*) { } else assert(bb.head); - bb.builder = shady::begin_body(module); + bb.builder = shady::begin_body(arena); } static std::optional is_shady_prim_op(const Continuation* cont) { for (int i = 0; i < shady::PRIMOPS_COUNT; i++) { - if (cont->name() == shady::primop_names[i]) + if (cont->name() == shady::get_primop_name(static_cast(i))) return std::make_optional((shady::Op) i); } return std::nullopt; @@ -293,10 +293,9 @@ void CodeGen::emit_epilogue(Continuation* cont) { bb.terminator = shady::fn_ret(arena, payload); } else if (body->callee() == world().branch()) { shady::Branch payload = {}; - payload.args = shady::nodes(arena, 0, nullptr); payload.branch_condition = args[0]; - payload.true_target = args[1]; - payload.false_target = args[2]; + payload.true_jump = shady::jump_helper(arena, args[1], shady::empty(arena)); + payload.false_jump = shady::jump_helper(arena, args[2], shady::empty(arena)); bb.terminator = shady::branch(arena, payload); } else if (auto match = body->callee()->as_nom(); match && match->intrinsic() == Intrinsic::Match) { assert(false); @@ -332,12 +331,12 @@ void CodeGen::emit_epilogue(Continuation* cont) { } } - shady::BodyBuilder* builder = shady::begin_body(module); + shady::BodyBuilder* builder = shady::begin_body(arena); - shady::IndirectCall icall_payload; + shady::Call icall_payload; icall_payload.args = vec2nodes(args); icall_payload.callee = emit(body->callee()); - shady::Nodes results = shady::bind_instruction(builder, shady::indirect_call(arena, icall_payload)); + shady::Nodes results = shady::bind_instruction(builder, shady::call(arena, icall_payload)); assert(args[ret_param]->tag == shady::BasicBlock_TAG); shady::Jump jump_payload; @@ -391,6 +390,7 @@ const shady::Node* CodeGen::emit_bb(BB& bb, const Def* def) { if (auto prim_lit = def->isa()) { const auto& box = prim_lit->value(); + shady::FloatLiteral fl {}; switch (prim_lit->primtype_tag()) { case PrimType_bool: v = box.get_bool() ? shady::true_lit(arena) : shady::false_lit(arena); break; case PrimType_ps8: case PrimType_qs8: v = shady::int8_literal (arena, box.get_s8()); break; @@ -401,9 +401,27 @@ const shady::Node* CodeGen::emit_bb(BB& bb, const Def* def) { case PrimType_pu32: case PrimType_qu32: v = shady::uint32_literal(arena, box.get_u32()); break; case PrimType_ps64: case PrimType_qs64: v = shady::int64_literal(arena, box.get_s64()); break; case PrimType_pu64: case PrimType_qu64: v = shady::uint64_literal(arena, box.get_u64()); break; - case PrimType_pf16: case PrimType_qf16: assert(false && "TODO"); - case PrimType_pf32: case PrimType_qf32: v = shady::float_type(arena); break; - case PrimType_pf64: case PrimType_qf64: assert(false && "TODO"); + case PrimType_pf16: case PrimType_qf16: { + fl.width = shady::FloatTy16; + auto f = box.get_f16(); + memcpy(&fl.value, &f, sizeof(f)); + v = shady::float_literal(arena, fl); + break; + } + case PrimType_pf32: case PrimType_qf32: { + fl.width = shady::FloatTy32; + auto f = box.get_f32(); + memcpy(&fl.value, &f, sizeof(f)); + v = shady::float_literal(arena, fl); + break; + } + case PrimType_pf64: case PrimType_qf64:{ + fl.width = shady::FloatTy64; + auto f = box.get_f64(); + memcpy(&fl.value, &f, sizeof(f)); + v = shady::float_literal(arena, fl); + break; + } default: THORIN_UNREACHABLE; } } else if (auto arr = def->isa()) { @@ -416,7 +434,7 @@ const shady::Node* CodeGen::emit_bb(BB& bb, const Def* def) { payload.element_type = convert(arr->elem_type()); payload.size = shady::int32_literal(arena, contents.size()); const shady::Type* arr_type = shady::arr_type(arena, payload); - v = shady::composite(arena, arr_type, vec2nodes(contents)); + v = shady::composite_helper(arena, arr_type, vec2nodes(contents)); } else if (auto cmp = def->isa()) { switch (cmp->cmp_tag()) { case Cmp_eq: v = mk_primop(shady::Op::eq_op, { cmp->lhs(), cmp->rhs() }); break; From a3d2c2292cc88eb67814579d61597d079344f170 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Fri, 15 Dec 2023 09:24:41 +0100 Subject: [PATCH 200/342] shady: fixed conversion ops --- src/thorin/be/shady/shady.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/thorin/be/shady/shady.cpp b/src/thorin/be/shady/shady.cpp index b3a6ddf87..a5cc92cb3 100644 --- a/src/thorin/be/shady/shady.cpp +++ b/src/thorin/be/shady/shady.cpp @@ -468,7 +468,9 @@ const shady::Node* CodeGen::emit_bb(BB& bb, const Def* def) { defs_[def] = nullptr; return nullptr; } else if (auto bitcast = def->isa()) { - v = emit(bitcast->from()); + v = mk_primop(shady::Op::reinterpret_op, { bitcast->from() }, { bitcast->type() }); + } else if (auto conversion = def->isa()) { + v = mk_primop(shady::Op::convert_op, {conversion->from() }, {conversion->type() }); } else { def->dump(); THORIN_UNREACHABLE; From 9c15f686c298b2b42a1e8495ae3943af01a0b54e Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Fri, 15 Dec 2023 09:33:35 +0100 Subject: [PATCH 201/342] shady: implement a bunch of mathops --- src/thorin/be/shady/shady.cpp | 27 ++++++++++++++++++++++++--- src/thorin/be/shady/shady.h | 3 ++- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/src/thorin/be/shady/shady.cpp b/src/thorin/be/shady/shady.cpp index a5cc92cb3..3e4a91e7a 100644 --- a/src/thorin/be/shady/shady.cpp +++ b/src/thorin/be/shady/shady.cpp @@ -370,10 +370,15 @@ const shady::Node* CodeGen::emit_fun_decl(Continuation* cont) { return shady::fn_addr(arena, payload); } +void CodeGen::unimplemented(const Def* def) { + world().error(def->loc(), "We don't know how to emit {} !", def); + abort(); +} + const shady::Node* CodeGen::emit_bb(BB& bb, const Def* def) { const shady::Node* v = nullptr; - auto mk_primop = [&](shady::Op op, std::vector args, std::vector types = {}) -> const shady::Node* { + auto mk_primop = [&](shady::Op op, ArrayRef args, ArrayRef types = {}) -> const shady::Node* { shady::PrimOp payload = {}; payload.op = op; std::vector operands; @@ -457,6 +462,23 @@ const shady::Node* CodeGen::emit_bb(BB& bb, const Def* def) { case ArithOp_shl: v = mk_primop(shady::Op::lshift_op, { arith->lhs(), arith->rhs() }); break; case ArithOp_shr: v = mk_primop(shady::Op::rshift_logical_op, { arith->lhs(), arith->rhs() }); break; } + } else if (auto math = def->isa()) { + switch (math->mathop_tag()) { + case MathOp_fmin: v = mk_primop(shady::Op::min_op, math->ops()); break; + case MathOp_fmax: v = mk_primop(shady::Op::max_op, math->ops()); break; + case MathOp_cos: v = mk_primop(shady::Op::cos_op, math->ops()); break; + case MathOp_sin: v = mk_primop(shady::Op::sin_op, math->ops()); break; + case MathOp_fabs: v = mk_primop(shady::Op::abs_op, math->ops()); break; + case MathOp_floor: v = mk_primop(shady::Op::floor_op, math->ops()); break; + case MathOp_round: v = mk_primop(shady::Op::round_op, math->ops()); break; + case MathOp_pow: v = mk_primop(shady::Op::pow_op, math->ops()); break; + case MathOp_exp: v = mk_primop(shady::Op::exp_op, math->ops()); break; + case MathOp_sqrt: v = mk_primop(shady::Op::sqrt_op, math->ops()); break; + default: { + unimplemented(def); + THORIN_UNREACHABLE; + } + } } else if (auto store = def->isa()) { mk_primop(shady::Op::store_op, { store->ptr(), store->val() }); defs_[def] = nullptr; @@ -472,8 +494,7 @@ const shady::Node* CodeGen::emit_bb(BB& bb, const Def* def) { } else if (auto conversion = def->isa()) { v = mk_primop(shady::Op::convert_op, {conversion->from() }, {conversion->type() }); } else { - def->dump(); - THORIN_UNREACHABLE; + unimplemented(def); } assert(v && shady::is_value(v)); defs_[def] = v; diff --git a/src/thorin/be/shady/shady.h b/src/thorin/be/shady/shady.h index 75280caff..648cdf0c4 100644 --- a/src/thorin/be/shady/shady.h +++ b/src/thorin/be/shady/shady.h @@ -55,7 +55,8 @@ class CodeGen : public thorin::CodeGen, public thorin::Emitter top_level; + + void unimplemented(const Def* def); shady::Node* curr_fn; From ebed23c6093f35fd6fbbeeec2c3671e8ed817e0d Mon Sep 17 00:00:00 2001 From: Matthias Kurtenacker Date: Tue, 16 Jan 2024 15:15:32 +0100 Subject: [PATCH 202/342] Fixes to compile HLS code. * CodeGen: fix get_kernel_configs call if multiple backends use the same host continuation. * Keep kernel calling convention consistent in hls_channels. * Fix Closure Conversion for HLS. * Add find_origin to importer to simplify hls_channels and stop it from importing everything into the new world. --- src/thorin/be/codegen.cpp | 25 ++++++++++++--------- src/thorin/transform/closure_conversion.cpp | 2 ++ src/thorin/transform/hls_channels.cpp | 15 ++++++------- src/thorin/transform/importer.cpp | 8 +++++++ src/thorin/transform/importer.h | 1 + 5 files changed, 33 insertions(+), 18 deletions(-) diff --git a/src/thorin/be/codegen.cpp b/src/thorin/be/codegen.cpp index 3f8204ef0..c1e5f6b41 100644 --- a/src/thorin/be/codegen.cpp +++ b/src/thorin/be/codegen.cpp @@ -92,19 +92,19 @@ DeviceBackends::DeviceBackends(World& world, int opt, bool debug, std::string& f importers.emplace_back(world, accelerator_code.back().world()); } + static const auto backend_intrinsics = std::array { + std::pair { CUDA, Intrinsic::CUDA }, + std::pair { NVVM, Intrinsic::NVVM }, + std::pair { OpenCL, Intrinsic::OpenCL }, + std::pair { AMDGPU, Intrinsic::AMDGPU }, + std::pair { HLS, Intrinsic::HLS }, + std::pair { Shady, Intrinsic::ShadyCompute } + }; + // determine different parts of the world which need to be compiled differently ScopesForest(world).for_each([&] (const Scope& scope) { auto continuation = scope.entry(); Continuation* imported = nullptr; - - static const auto backend_intrinsics = std::array { - std::pair { CUDA, Intrinsic::CUDA }, - std::pair { NVVM, Intrinsic::NVVM }, - std::pair { OpenCL, Intrinsic::OpenCL }, - std::pair { AMDGPU, Intrinsic::AMDGPU }, - std::pair { HLS, Intrinsic::HLS }, - std::pair { Shady, Intrinsic::ShadyCompute } - }; for (auto [backend, intrinsic] : backend_intrinsics) { if (is_passed_to_intrinsic(continuation, intrinsic)) { imported = importers[backend].import(continuation)->as_nom(); @@ -126,10 +126,15 @@ DeviceBackends::DeviceBackends(World& world, int opt, bool debug, std::string& f kernels.emplace_back(continuation); }); - for (auto backend : std::array { CUDA, NVVM, OpenCL, AMDGPU, Shady }) { + for (auto [backend, intrinsic] : backend_intrinsics) { + if (backend == HLS) + continue; + if (!accelerator_code[backend].world().empty()) { get_kernel_configs(accelerator_code[backend], kernels, kernel_config, [&](Continuation *use, Continuation * /* imported */) { auto app = use->body(); + if (app->callee()->as()->intrinsic() != intrinsic) + return std::unique_ptr(nullptr); // determine whether or not this kernel uses restrict pointers bool has_restrict = true; DefSet allocs; diff --git a/src/thorin/transform/closure_conversion.cpp b/src/thorin/transform/closure_conversion.cpp index 9140f4132..2048d77a5 100644 --- a/src/thorin/transform/closure_conversion.cpp +++ b/src/thorin/transform/closure_conversion.cpp @@ -86,6 +86,8 @@ class ClosureConversion { new_args[i] = body->arg(i); else if (callee->intrinsic() == Intrinsic::Match && i > 2) new_args[i] = body->arg(i); + else if (callee->intrinsic() == Intrinsic::HLS && i > 1) + new_args[i] = body->arg(i); else new_args[i] = convert_def(body->arg(i)); } diff --git a/src/thorin/transform/hls_channels.cpp b/src/thorin/transform/hls_channels.cpp index cafabb63e..f515cd436 100644 --- a/src/thorin/transform/hls_channels.cpp +++ b/src/thorin/transform/hls_channels.cpp @@ -174,7 +174,9 @@ DeviceParams hls_channels(Thorin& thorin, Importer& importer, Top2Kernel& top2ke auto new_kernel = lift(scope, old_kernel, channels); world.make_external(new_kernel); + new_kernel->attributes().cc = CC::C; world.make_internal(old_kernel); + old_kernel->attributes().cc = CC::Thorin; kernel_new2old.emplace(new_kernel, old_kernel); @@ -236,14 +238,11 @@ DeviceParams hls_channels(Thorin& thorin, Importer& importer, Top2Kernel& top2ke // Maping hls world params (from old kernels) to old_world params. Required for host code (runtime) generation for (auto& elem : old_kernels_params) { - for (auto def : old_world.defs()) { - if (auto ocontinuation = def->isa_nom()) { - auto ncontinuation = elem->as()->continuation(); - if (ncontinuation == importer.import(ocontinuation)) { - elem = ocontinuation->param(elem->as()->index()); - break; - } - } + auto ncontinuation = elem->as()->continuation(); + auto odef = importer.find_origin(ncontinuation); + if (odef) { + auto ocontinuation = odef->as(); + elem = ocontinuation->param(elem->as()->index()); } } diff --git a/src/thorin/transform/importer.cpp b/src/thorin/transform/importer.cpp index 9bc97bc9b..2bebb9d07 100644 --- a/src/thorin/transform/importer.cpp +++ b/src/thorin/transform/importer.cpp @@ -125,4 +125,12 @@ const Def* Importer::rewrite(const Def* const odef) { return ndef; } +const Def* Importer::find_origin(const Def* ndef) { + for (auto def : src().defs()) { + if (ndef == lookup(def)) + return def; + } + return nullptr; +} + } diff --git a/src/thorin/transform/importer.h b/src/thorin/transform/importer.h index 91110ade9..0962a4da2 100644 --- a/src/thorin/transform/importer.h +++ b/src/thorin/transform/importer.h @@ -19,6 +19,7 @@ class Importer : Rewriter { } const Def* import(const Def* odef) { return instantiate(odef); } + const Def* find_origin(const Def* ndef); bool todo() const { return todo_; } protected: From f076776570758b2e0d2c68ab8609b5e8f2053d6e Mon Sep 17 00:00:00 2001 From: Matthias Kurtenacker Date: Thu, 1 Feb 2024 13:55:06 +0100 Subject: [PATCH 203/342] Fix GIDHash to not segfault computing a hash for "(Def*) nullptr". --- src/thorin/type.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/thorin/type.h b/src/thorin/type.h index 2ce899290..c2f9b74ae 100644 --- a/src/thorin/type.h +++ b/src/thorin/type.h @@ -433,7 +433,7 @@ struct GIDLt { template struct GIDHash { - static hash_t hash(T n) { return thorin::murmur3(n->gid()); } + static hash_t hash(T n) { return thorin::murmur3(n ? n->gid() : 0); } static bool eq(T a, T b) { return a == b; } static T sentinel() { return T(1); } }; From 76dabb1baaf938bd19c325c791d600b67785a74f Mon Sep 17 00:00:00 2001 From: Richard Membarth Date: Tue, 6 Feb 2024 22:21:12 +0100 Subject: [PATCH 204/342] Abstract class for AMDGPUCodeGen. --- src/thorin/CMakeLists.txt | 2 + src/thorin/be/llvm/amdgpu.cpp | 79 +++++++++++++++++++++++++++++++ src/thorin/be/llvm/amdgpu.h | 33 +++++++++++++ src/thorin/be/llvm/amdgpu_hsa.cpp | 69 +-------------------------- src/thorin/be/llvm/amdgpu_hsa.h | 15 +----- src/thorin/be/llvm/amdgpu_pal.cpp | 68 +------------------------- src/thorin/be/llvm/amdgpu_pal.h | 15 +----- 7 files changed, 120 insertions(+), 161 deletions(-) create mode 100644 src/thorin/be/llvm/amdgpu.cpp create mode 100644 src/thorin/be/llvm/amdgpu.h diff --git a/src/thorin/CMakeLists.txt b/src/thorin/CMakeLists.txt index 440d7cafc..5280c005a 100644 --- a/src/thorin/CMakeLists.txt +++ b/src/thorin/CMakeLists.txt @@ -95,6 +95,8 @@ if(LLVM_FOUND) be/llvm/cpu.h be/llvm/llvm.cpp be/llvm/llvm.h + be/llvm/amdgpu.cpp + be/llvm/amdgpu.h be/llvm/amdgpu_hsa.cpp be/llvm/amdgpu_hsa.h be/llvm/amdgpu_pal.cpp diff --git a/src/thorin/be/llvm/amdgpu.cpp b/src/thorin/be/llvm/amdgpu.cpp new file mode 100644 index 000000000..cda674b45 --- /dev/null +++ b/src/thorin/be/llvm/amdgpu.cpp @@ -0,0 +1,79 @@ +#include "thorin/be/llvm/amdgpu.h" + +#include // TODO don't use std::unordered_* + +#include "thorin/primop.h" +#include "thorin/world.h" + +namespace thorin::llvm { + +AMDGPUCodeGen::AMDGPUCodeGen(World& world, llvm::CallingConv::ID function_calling_convention, llvm::CallingConv::ID device_calling_convention, llvm::CallingConv::ID kernel_calling_convention, const Cont2Config& kernel_config, int opt, bool debug) + : CodeGen(world, function_calling_convention, device_calling_convention, kernel_calling_convention, opt, debug) + , kernel_config_(kernel_config) {} + +//------------------------------------------------------------------------------ +// Kernel code +//------------------------------------------------------------------------------ + +void AMDGPUCodeGen::emit_fun_decl_hook(Continuation* continuation, llvm::Function* f) { + auto config = kernel_config_.find(continuation); + if (config != kernel_config_.end()) { + auto block = config->second->as()->block_size(); + if (std::get<0>(block) > 0 && std::get<1>(block) > 0 && std::get<2>(block) > 0) { + Array annotation_values_wgsize(3); + auto int32_type = llvm::IntegerType::get(context(), 32); + annotation_values_wgsize[0] = llvm::ConstantAsMetadata::get(llvm::ConstantInt::get(int32_type, std::get<0>(block))); + annotation_values_wgsize[1] = llvm::ConstantAsMetadata::get(llvm::ConstantInt::get(int32_type, std::get<1>(block))); + annotation_values_wgsize[2] = llvm::ConstantAsMetadata::get(llvm::ConstantInt::get(int32_type, std::get<2>(block))); + f->setMetadata(llvm::StringRef("reqd_work_group_size"), llvm::MDNode::get(context(), llvm_ref(annotation_values_wgsize))); + } + } +} + +llvm::Value* AMDGPUCodeGen::emit_global(const Global* global) { + if (global->is_mutable()) + world().wdef(global, "AMDGPU: Global variable '{}' will not be synced with host", global); + return CodeGen::emit_global(global); +} + +llvm::Value* AMDGPUCodeGen::emit_mathop(llvm::IRBuilder<>& irbuilder, const MathOp* mathop) { + auto make_key = [] (MathOpTag tag, unsigned bitwidth) { return (static_cast(tag) << 16) | bitwidth; }; + static const std::unordered_map ocml_functions = { +#define MATH_FUNCTION(name) \ + { make_key(MathOp_##name, 32), "__ocml_" #name "_f32" }, \ + { make_key(MathOp_##name, 64), "__ocml_" #name "_f64" }, + MATH_FUNCTION(fabs) + MATH_FUNCTION(copysign) + MATH_FUNCTION(round) + MATH_FUNCTION(floor) + MATH_FUNCTION(ceil) + MATH_FUNCTION(fmin) + MATH_FUNCTION(fmax) + MATH_FUNCTION(cos) + MATH_FUNCTION(sin) + MATH_FUNCTION(tan) + MATH_FUNCTION(acos) + MATH_FUNCTION(asin) + MATH_FUNCTION(atan) + MATH_FUNCTION(atan2) + MATH_FUNCTION(sqrt) + MATH_FUNCTION(cbrt) + MATH_FUNCTION(pow) + MATH_FUNCTION(exp) + MATH_FUNCTION(exp2) + MATH_FUNCTION(log) + MATH_FUNCTION(log2) + MATH_FUNCTION(log10) +#undef MATH_FUNCTION + }; + auto key = make_key(mathop->mathop_tag(), num_bits(mathop->type()->primtype_tag())); + auto call = call_math_function(irbuilder, mathop, ocml_functions.at(key)); + llvm::cast(call)->setCallingConv(function_calling_convention_); + return call; +} + +Continuation* AMDGPUCodeGen::emit_reserve(llvm::IRBuilder<>& irbuilder, const Continuation* continuation) { + return emit_reserve_shared(irbuilder, continuation, true); +} + +} diff --git a/src/thorin/be/llvm/amdgpu.h b/src/thorin/be/llvm/amdgpu.h new file mode 100644 index 000000000..82e53187d --- /dev/null +++ b/src/thorin/be/llvm/amdgpu.h @@ -0,0 +1,33 @@ +#ifndef THORIN_BE_LLVM_AMDGPU_H +#define THORIN_BE_LLVM_AMDGPU_H + +#include "thorin/be/llvm/llvm.h" + +namespace thorin { + +namespace llvm { + +namespace llvm = ::llvm; + +class AMDGPUCodeGen : public CodeGen { +public: + AMDGPUCodeGen(World&, llvm::CallingConv::ID, llvm::CallingConv::ID, llvm::CallingConv::ID, const Cont2Config&, int opt, bool debug); + + const char* file_ext() const override { return ".amdgpu"; } + +protected: + void emit_fun_decl_hook(Continuation*, llvm::Function*) override; + virtual llvm::Function* emit_fun_decl(Continuation*) = 0; + llvm::Value* emit_global(const Global*) override; + 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"; } + + const Cont2Config& kernel_config_; +}; + +} + +} + +#endif diff --git a/src/thorin/be/llvm/amdgpu_hsa.cpp b/src/thorin/be/llvm/amdgpu_hsa.cpp index 8b88facd4..4b886159f 100644 --- a/src/thorin/be/llvm/amdgpu_hsa.cpp +++ b/src/thorin/be/llvm/amdgpu_hsa.cpp @@ -1,15 +1,9 @@ #include "thorin/be/llvm/amdgpu_hsa.h" -#include // TODO don't use std::unordered_* - -#include "thorin/primop.h" -#include "thorin/world.h" - namespace thorin::llvm { AMDGPUHSACodeGen::AMDGPUHSACodeGen(World& world, const Cont2Config& kernel_config, int opt, bool debug) - : CodeGen(world, llvm::CallingConv::C, llvm::CallingConv::C, llvm::CallingConv::AMDGPU_KERNEL, opt, debug) - , kernel_config_(kernel_config) + : AMDGPUCodeGen(world, llvm::CallingConv::C, llvm::CallingConv::C, llvm::CallingConv::AMDGPU_KERNEL, kernel_config, opt, debug) { module().setDataLayout("e-p:64:64-p1:64:64-p2:32:32-p3:32:32-p4:64:64-p5:32:32-p6:32:32-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024-v2048:2048-n32:64-S32-A5-G1-ni:7"); module().setTargetTriple("amdgcn-amd-amdhsa"); @@ -19,21 +13,6 @@ AMDGPUHSACodeGen::AMDGPUHSACodeGen(World& world, const Cont2Config& kernel_confi // Kernel code //------------------------------------------------------------------------------ -void AMDGPUHSACodeGen::emit_fun_decl_hook(Continuation* continuation, llvm::Function* f) { - auto config = kernel_config_.find(continuation); - if (config != kernel_config_.end()) { - auto block = config->second->as()->block_size(); - if (std::get<0>(block) > 0 && std::get<1>(block) > 0 && std::get<2>(block) > 0) { - Array annotation_values_wgsize(3); - auto int32_type = llvm::IntegerType::get(context(), 32); - annotation_values_wgsize[0] = llvm::ConstantAsMetadata::get(llvm::ConstantInt::get(int32_type, std::get<0>(block))); - annotation_values_wgsize[1] = llvm::ConstantAsMetadata::get(llvm::ConstantInt::get(int32_type, std::get<1>(block))); - annotation_values_wgsize[2] = llvm::ConstantAsMetadata::get(llvm::ConstantInt::get(int32_type, std::get<2>(block))); - f->setMetadata(llvm::StringRef("reqd_work_group_size"), llvm::MDNode::get(context(), llvm_ref(annotation_values_wgsize))); - } - } -} - llvm::Function* AMDGPUHSACodeGen::emit_fun_decl(Continuation* continuation) { if (continuation->name() == "llvm.amdgcn.implicitarg.ptr") if (auto f = defs_.lookup(entry_); f && llvm::isa(*f)) @@ -44,50 +23,4 @@ llvm::Function* AMDGPUHSACodeGen::emit_fun_decl(Continuation* continuation) { return CodeGen::emit_fun_decl(continuation); } -llvm::Value* AMDGPUHSACodeGen::emit_global(const Global* global) { - if (global->is_mutable()) - world().wdef(global, "AMDGPU: Global variable '{}' will not be synced with host", global); - return CodeGen::emit_global(global); -} - -llvm::Value* AMDGPUHSACodeGen::emit_mathop(llvm::IRBuilder<>& irbuilder, const MathOp* mathop) { - auto make_key = [] (MathOpTag tag, unsigned bitwidth) { return (static_cast(tag) << 16) | bitwidth; }; - static const std::unordered_map ocml_functions = { -#define MATH_FUNCTION(name) \ - { make_key(MathOp_##name, 32), "__ocml_" #name "_f32" }, \ - { make_key(MathOp_##name, 64), "__ocml_" #name "_f64" }, - MATH_FUNCTION(fabs) - MATH_FUNCTION(copysign) - MATH_FUNCTION(round) - MATH_FUNCTION(floor) - MATH_FUNCTION(ceil) - MATH_FUNCTION(fmin) - MATH_FUNCTION(fmax) - MATH_FUNCTION(cos) - MATH_FUNCTION(sin) - MATH_FUNCTION(tan) - MATH_FUNCTION(acos) - MATH_FUNCTION(asin) - MATH_FUNCTION(atan) - MATH_FUNCTION(atan2) - MATH_FUNCTION(sqrt) - MATH_FUNCTION(cbrt) - MATH_FUNCTION(pow) - MATH_FUNCTION(exp) - MATH_FUNCTION(exp2) - MATH_FUNCTION(log) - MATH_FUNCTION(log2) - MATH_FUNCTION(log10) -#undef MATH_FUNCTION - }; - auto key = make_key(mathop->mathop_tag(), num_bits(mathop->type()->primtype_tag())); - auto call = call_math_function(irbuilder, mathop, ocml_functions.at(key)); - llvm::cast(call)->setCallingConv(function_calling_convention_); - return call; -} - -Continuation* AMDGPUHSACodeGen::emit_reserve(llvm::IRBuilder<>& irbuilder, const Continuation* continuation) { - return emit_reserve_shared(irbuilder, continuation, true); -} - } diff --git a/src/thorin/be/llvm/amdgpu_hsa.h b/src/thorin/be/llvm/amdgpu_hsa.h index 05fb39044..32c4ace94 100644 --- a/src/thorin/be/llvm/amdgpu_hsa.h +++ b/src/thorin/be/llvm/amdgpu_hsa.h @@ -1,31 +1,20 @@ #ifndef THORIN_BE_LLVM_AMDGPU_HSA_H #define THORIN_BE_LLVM_AMDGPU_HSA_H -#include "thorin/be/llvm/llvm.h" +#include "thorin/be/llvm/amdgpu.h" namespace thorin { -class Load; - namespace llvm { namespace llvm = ::llvm; -class AMDGPUHSACodeGen : public CodeGen { +class AMDGPUHSACodeGen : public AMDGPUCodeGen { public: AMDGPUHSACodeGen(World& world, const Cont2Config&, int opt, bool debug); - const char* file_ext() const override { return ".amdgpu"; } - protected: - void emit_fun_decl_hook(Continuation*, llvm::Function*) override; llvm::Function* emit_fun_decl(Continuation*) override; - llvm::Value* emit_global(const Global*) override; - 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"; } - - const Cont2Config& kernel_config_; }; } diff --git a/src/thorin/be/llvm/amdgpu_pal.cpp b/src/thorin/be/llvm/amdgpu_pal.cpp index 9ea8562c2..8fac12d08 100644 --- a/src/thorin/be/llvm/amdgpu_pal.cpp +++ b/src/thorin/be/llvm/amdgpu_pal.cpp @@ -1,15 +1,9 @@ #include "thorin/be/llvm/amdgpu_pal.h" -#include // TODO don't use std::unordered_* - -#include "thorin/primop.h" -#include "thorin/world.h" - namespace thorin::llvm { AMDGPUPALCodeGen::AMDGPUPALCodeGen(World& world, const Cont2Config& kernel_config, int opt, bool debug) - : CodeGen(world, llvm::CallingConv::AMDGPU_Gfx, llvm::CallingConv::C, llvm::CallingConv::AMDGPU_CS, opt, debug) - , kernel_config_(kernel_config) + : AMDGPUCodeGen(world, llvm::CallingConv::AMDGPU_Gfx, llvm::CallingConv::C, llvm::CallingConv::AMDGPU_CS, kernel_config, opt, debug) { module().setDataLayout("e-p:64:64-p1:64:64-p2:32:32-p3:32:32-p4:64:64-p5:32:32-p6:32:32-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024-v2048:2048-n32:64-S32-A5-G1-ni:7"); module().setTargetTriple("amdgcn-amd-amdpal"); @@ -19,69 +13,9 @@ AMDGPUPALCodeGen::AMDGPUPALCodeGen(World& world, const Cont2Config& kernel_confi // Kernel code //------------------------------------------------------------------------------ -void AMDGPUPALCodeGen::emit_fun_decl_hook(Continuation* continuation, llvm::Function* f) { - auto config = kernel_config_.find(continuation); - if (config != kernel_config_.end()) { - auto block = config->second->as()->block_size(); - if (std::get<0>(block) > 0 && std::get<1>(block) > 0 && std::get<2>(block) > 0) { - Array annotation_values_wgsize(3); - auto int32_type = llvm::IntegerType::get(context(), 32); - annotation_values_wgsize[0] = llvm::ConstantAsMetadata::get(llvm::ConstantInt::get(int32_type, std::get<0>(block))); - annotation_values_wgsize[1] = llvm::ConstantAsMetadata::get(llvm::ConstantInt::get(int32_type, std::get<1>(block))); - annotation_values_wgsize[2] = llvm::ConstantAsMetadata::get(llvm::ConstantInt::get(int32_type, std::get<2>(block))); - f->setMetadata(llvm::StringRef("reqd_work_group_size"), llvm::MDNode::get(context(), llvm_ref(annotation_values_wgsize))); - } - } -} llvm::Function* AMDGPUPALCodeGen::emit_fun_decl(Continuation* continuation) { return CodeGen::emit_fun_decl(continuation); } -llvm::Value* AMDGPUPALCodeGen::emit_global(const Global* global) { - if (global->is_mutable()) - world().wdef(global, "AMDGPU: Global variable '{}' will not be synced with host", global); - return CodeGen::emit_global(global); -} - -llvm::Value* AMDGPUPALCodeGen::emit_mathop(llvm::IRBuilder<>& irbuilder, const MathOp* mathop) { - auto make_key = [] (MathOpTag tag, unsigned bitwidth) { return (static_cast(tag) << 16) | bitwidth; }; - static const std::unordered_map ocml_functions = { -#define MATH_FUNCTION(name) \ - { make_key(MathOp_##name, 32), "__ocml_" #name "_f32" }, \ - { make_key(MathOp_##name, 64), "__ocml_" #name "_f64" }, - MATH_FUNCTION(fabs) - MATH_FUNCTION(copysign) - MATH_FUNCTION(round) - MATH_FUNCTION(floor) - MATH_FUNCTION(ceil) - MATH_FUNCTION(fmin) - MATH_FUNCTION(fmax) - MATH_FUNCTION(cos) - MATH_FUNCTION(sin) - MATH_FUNCTION(tan) - MATH_FUNCTION(acos) - MATH_FUNCTION(asin) - MATH_FUNCTION(atan) - MATH_FUNCTION(atan2) - MATH_FUNCTION(sqrt) - MATH_FUNCTION(cbrt) - MATH_FUNCTION(pow) - MATH_FUNCTION(exp) - MATH_FUNCTION(exp2) - MATH_FUNCTION(log) - MATH_FUNCTION(log2) - MATH_FUNCTION(log10) -#undef MATH_FUNCTION - }; - auto key = make_key(mathop->mathop_tag(), num_bits(mathop->type()->primtype_tag())); - auto call = call_math_function(irbuilder, mathop, ocml_functions.at(key)); - llvm::cast(call)->setCallingConv(function_calling_convention_); - return call; -} - -Continuation* AMDGPUPALCodeGen::emit_reserve(llvm::IRBuilder<>& irbuilder, const Continuation* continuation) { - return emit_reserve_shared(irbuilder, continuation, true); -} - } diff --git a/src/thorin/be/llvm/amdgpu_pal.h b/src/thorin/be/llvm/amdgpu_pal.h index ab2e7fcdd..29a792136 100644 --- a/src/thorin/be/llvm/amdgpu_pal.h +++ b/src/thorin/be/llvm/amdgpu_pal.h @@ -1,31 +1,20 @@ #ifndef THORIN_BE_LLVM_AMDGPU_PAL_H #define THORIN_BE_LLVM_AMDGPU_PAL_H -#include "thorin/be/llvm/llvm.h" +#include "thorin/be/llvm/amdgpu.h" namespace thorin { -class Load; - namespace llvm { namespace llvm = ::llvm; -class AMDGPUPALCodeGen : public CodeGen { +class AMDGPUPALCodeGen : public AMDGPUCodeGen { public: AMDGPUPALCodeGen(World& world, const Cont2Config&, int opt, bool debug); - const char* file_ext() const override { return ".amdgpu"; } - protected: - void emit_fun_decl_hook(Continuation*, llvm::Function*) override; llvm::Function* emit_fun_decl(Continuation*) override; - llvm::Value* emit_global(const Global*) override; - 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"; } - - const Cont2Config& kernel_config_; }; } From 47f4b88ce28e83ce8419aa6a91ca3002ee7e000e Mon Sep 17 00:00:00 2001 From: Richard Membarth Date: Fri, 15 Mar 2024 12:00:21 +0100 Subject: [PATCH 205/342] NVVM: already optimize at compile time. --- src/thorin/be/codegen.cpp | 2 +- src/thorin/be/llvm/nvvm.cpp | 4 ++-- src/thorin/be/llvm/nvvm.h | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/thorin/be/codegen.cpp b/src/thorin/be/codegen.cpp index 12247b032..a690d258a 100644 --- a/src/thorin/be/codegen.cpp +++ b/src/thorin/be/codegen.cpp @@ -187,7 +187,7 @@ DeviceBackends::DeviceBackends(World& world, int opt, bool debug, std::string& f hls_kernel_launch(world, hls_host_params); #if THORIN_ENABLE_LLVM - if (!importers_[NVVM ].world().empty()) cgs[NVVM ] = std::make_unique(importers_[NVVM ].world(), kernel_config, debug); + if (!importers_[NVVM ].world().empty()) cgs[NVVM ] = std::make_unique(importers_[NVVM ].world(), kernel_config, opt, debug); if (!importers_[AMDGPU_HSA].world().empty()) cgs[AMDGPU_HSA] = std::make_unique(importers_[AMDGPU_HSA].world(), kernel_config, opt, debug); if (!importers_[AMDGPU_PAL].world().empty()) cgs[AMDGPU_PAL] = std::make_unique(importers_[AMDGPU_PAL].world(), kernel_config, opt, debug); #else diff --git a/src/thorin/be/llvm/nvvm.cpp b/src/thorin/be/llvm/nvvm.cpp index ab8d45ecc..ac6058084 100644 --- a/src/thorin/be/llvm/nvvm.cpp +++ b/src/thorin/be/llvm/nvvm.cpp @@ -19,8 +19,8 @@ namespace thorin::llvm { -NVVMCodeGen::NVVMCodeGen(World& world, const Cont2Config& kernel_config, bool debug) - : CodeGen(world, llvm::CallingConv::C, llvm::CallingConv::PTX_Device, llvm::CallingConv::PTX_Kernel, 0, debug) +NVVMCodeGen::NVVMCodeGen(World& world, const Cont2Config& kernel_config, int opt, bool debug) + : CodeGen(world, llvm::CallingConv::C, llvm::CallingConv::PTX_Device, llvm::CallingConv::PTX_Kernel, opt, debug) , kernel_config_(kernel_config) { auto triple = llvm::Triple(llvm::sys::getDefaultTargetTriple()); diff --git a/src/thorin/be/llvm/nvvm.h b/src/thorin/be/llvm/nvvm.h index ee6f5239f..1cb4c802a 100644 --- a/src/thorin/be/llvm/nvvm.h +++ b/src/thorin/be/llvm/nvvm.h @@ -13,7 +13,7 @@ namespace llvm = ::llvm; class NVVMCodeGen : public CodeGen { public: - NVVMCodeGen(World& world, const Cont2Config&, bool debug); // NVVM-specific optimizations are run in the runtime + NVVMCodeGen(World& world, const Cont2Config&, int opt, bool debug); const char* file_ext() const override { return ".nvvm"; } From 924feda0bb54c07acefbf4768a2000c0177538d0 Mon Sep 17 00:00:00 2001 From: Matthias Kurtenacker Date: Mon, 1 Apr 2024 21:01:50 +0200 Subject: [PATCH 206/342] Fix: Do not include execinfo.h if THORIN_ENABLE_CREATION_CONTEXT is not set. It is not required then, and the header is Linux only. --- src/thorin/world.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/thorin/world.cpp b/src/thorin/world.cpp index 6d83d3570..7fd1b9d0d 100644 --- a/src/thorin/world.cpp +++ b/src/thorin/world.cpp @@ -10,7 +10,10 @@ #endif #include + +#if THORIN_ENABLE_CREATION_CONTEXT #include +#endif #ifdef THORIN_ENABLE_RLIMITS #include From fb0f72189c16826f69ad947fbf197ebac3cc520c Mon Sep 17 00:00:00 2001 From: Matthias Kurtenacker Date: Tue, 9 Apr 2024 18:27:09 +0200 Subject: [PATCH 207/342] Fixes #155, thanks @PearCoding for the patch. --- src/thorin/util/scoped_dump.cpp | 2 +- src/thorin/world.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/thorin/util/scoped_dump.cpp b/src/thorin/util/scoped_dump.cpp index 36b03c015..3b76427d7 100644 --- a/src/thorin/util/scoped_dump.cpp +++ b/src/thorin/util/scoped_dump.cpp @@ -154,7 +154,7 @@ void World::dump_scoped() const { } void World::dump_scoped_to_disk() const { - ScopedWorld s(*const_cast(this), (ScopedWorld::Config) { false }); + ScopedWorld s(*const_cast(this), ScopedWorld::Config { false }); auto name = this->name() + ".dump"; std::ofstream file(name); Stream st(file); diff --git a/src/thorin/world.cpp b/src/thorin/world.cpp index 7fd1b9d0d..96b3322f5 100644 --- a/src/thorin/world.cpp +++ b/src/thorin/world.cpp @@ -15,7 +15,7 @@ #include #endif -#ifdef THORIN_ENABLE_RLIMITS +#if THORIN_ENABLE_RLIMITS #include #endif @@ -1323,7 +1323,7 @@ void Thorin::opt() { } bool Thorin::ensure_stack_size(size_t new_size) { -#ifdef THORIN_ENABLE_RLIMITS +#if THORIN_ENABLE_RLIMITS struct rlimit rl; int result = getrlimit(RLIMIT_STACK, &rl); if(result != 0) return false; From 3ff6142cf3d6fa450a5d33159c2af4a8ea477d29 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Thu, 23 May 2024 10:11:19 +0200 Subject: [PATCH 208/342] fix incorrect usage of TypeOpsMixin leading to UB cast --- src/thorin/type.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/thorin/type.h b/src/thorin/type.h index 95719d36c..39c799011 100644 --- a/src/thorin/type.h +++ b/src/thorin/type.h @@ -107,7 +107,7 @@ class NominalType : public Type { } }; -class StructType : public NominalType, public TypeOpsMixin { +class StructType : public NominalType, public TypeOpsMixin { private: StructType(World& world, Symbol name, size_t size, Debug dbg) : NominalType(world, Node_StructType, name, size, dbg) @@ -119,7 +119,7 @@ class StructType : public NominalType, public TypeOpsMixin { friend class World; }; -class VariantType : public NominalType, public TypeOpsMixin { +class VariantType : public NominalType, public TypeOpsMixin { private: VariantType(World& world, Symbol name, size_t size, Debug dbg) : NominalType(world, Node_VariantType, name, size, dbg) @@ -236,7 +236,7 @@ enum class AddrSpace : uint32_t { }; /// Pointer type. -class PtrType : public VectorType, public TypeOpsMixin { +class PtrType : public VectorType, public TypeOpsMixin { private: PtrType(World& world, const Type* pointee, size_t length, int32_t device, AddrSpace addr_space, Debug dbg) : VectorType(world, Node_PtrType, {pointee}, length, dbg) @@ -267,7 +267,7 @@ inline bool is_thin(const Type* type) { return type->isa() || type->isa() || is_type_unit(type); } -class FnType : public Type, public TypeOpsMixin { +class FnType : public Type, public TypeOpsMixin { protected: FnType(World& world, Defs ops, NodeTag tag, Debug dbg) : Type(world, tag, ops, dbg) @@ -306,7 +306,7 @@ class ClosureType : public FnType { //------------------------------------------------------------------------------ -class ArrayType : public Type, public TypeOpsMixin { +class ArrayType : public Type, public TypeOpsMixin { protected: ArrayType(World& world, NodeTag tag, const Type* elem_type, Debug dbg) : Type(world, tag, {elem_type}, dbg) From 4515acf6e355ac756fef42e05375e4ecc246df42 Mon Sep 17 00:00:00 2001 From: Richard Membarth Date: Tue, 28 May 2024 16:31:20 +0200 Subject: [PATCH 209/342] Fix compiler warning. --- src/thorin/continuation.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/thorin/continuation.h b/src/thorin/continuation.h index 3d39ae816..73e47e36a 100644 --- a/src/thorin/continuation.h +++ b/src/thorin/continuation.h @@ -12,8 +12,8 @@ namespace thorin { class Continuation; +class Rewriter; class Scope; -struct Rewriter; typedef std::vector Continuations; From 93258e5d86739299aaa8abffad296e889f29d319 Mon Sep 17 00:00:00 2001 From: Richard Membarth Date: Tue, 28 May 2024 16:33:36 +0200 Subject: [PATCH 210/342] Add option to dump thorin IR without color. --- src/thorin/util/scoped_dump.cpp | 4 ++-- src/thorin/world.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/thorin/util/scoped_dump.cpp b/src/thorin/util/scoped_dump.cpp index 3b76427d7..b035ac3ce 100644 --- a/src/thorin/util/scoped_dump.cpp +++ b/src/thorin/util/scoped_dump.cpp @@ -148,8 +148,8 @@ Stream& ScopedWorld::stream(thorin::Stream& s) const { return s; } -void World::dump_scoped() const { - ScopedWorld s(*const_cast(this)); +void World::dump_scoped(bool use_color) const { + ScopedWorld s(*const_cast(this), ScopedWorld::Config { use_color }); s.dump(); } diff --git a/src/thorin/world.h b/src/thorin/world.h index 547dfdca6..6e62023d3 100644 --- a/src/thorin/world.h +++ b/src/thorin/world.h @@ -301,7 +301,7 @@ class World : public Streamable { /// @name logging //@{ - void dump_scoped() const; + void dump_scoped(bool=true) const; void dump_scoped_to_disk() const; Stream& stream(Stream&) const; Stream& stream() { return *stream_; } From 7793f10424502c9e3afe84cef99c3e3c12728764 Mon Sep 17 00:00:00 2001 From: Matthias Kurtenacker Date: Wed, 29 May 2024 16:14:54 +0200 Subject: [PATCH 211/342] Lift builtins: Wrap continuations without bodies into globals. --- src/thorin/transform/lift_builtins.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/thorin/transform/lift_builtins.cpp b/src/thorin/transform/lift_builtins.cpp index 6bd509da6..ea4261578 100644 --- a/src/thorin/transform/lift_builtins.cpp +++ b/src/thorin/transform/lift_builtins.cpp @@ -71,7 +71,7 @@ void lift_builtins(Thorin& thorin) { World& world = thorin.world(); Continuation* cur = nullptr; ScopesForest forest(world); - forest.for_each([&] (const Scope& scope) { + forest.for_each([&] (const Scope& scope) { if (cur) return; for (auto n : scope.f_cfg().post_order()) { if (n->continuation()->order() <= 1) @@ -105,7 +105,13 @@ void lift_builtins(Thorin& thorin) { } } - auto lifted = lift(scope, scope.entry(), defs); + Continuation * lifted; + if (scope.entry()->has_body()) + lifted = lift(scope, scope.entry(), defs); + else { + assert(defs.size() == 0 && "Scopes without body cannot have free defs."); + lifted = scope.entry(); + } for (auto use : cur->copy_uses()) { if (auto uapp = use->isa()) { if (auto callee = uapp->callee()->isa_nom()) { From 5c2f8af80cc9106d6b5ddb009c56b8772917b347 Mon Sep 17 00:00:00 2001 From: Matthias Kurtenacker Date: Fri, 15 Dec 2023 17:49:06 +0100 Subject: [PATCH 212/342] Fixes to work with a module build of RV and a prebuild LLVM. --- src/thorin/CMakeLists.txt | 2 +- src/thorin/be/llvm/vectorize.cpp | 18 +----------------- 2 files changed, 2 insertions(+), 18 deletions(-) diff --git a/src/thorin/CMakeLists.txt b/src/thorin/CMakeLists.txt index 15817401d..ce15ca0c2 100644 --- a/src/thorin/CMakeLists.txt +++ b/src/thorin/CMakeLists.txt @@ -136,7 +136,7 @@ if(LLVM_FOUND) target_link_libraries(thorin PRIVATE ${RV_LIBRARIES}) list(APPEND Thorin_LLVM_COMPONENTS analysis passes transformutils) endif() - llvm_config(thorin ${AnyDSL_LLVM_LINK_SHARED} ${Thorin_LLVM_COMPONENTS}) + llvm_config(thorin ${Thorin_LLVM_COMPONENTS}) endif() if (THORIN_ENABLE_SHADY) diff --git a/src/thorin/be/llvm/vectorize.cpp b/src/thorin/be/llvm/vectorize.cpp index f2c74e619..2df5454f3 100644 --- a/src/thorin/be/llvm/vectorize.cpp +++ b/src/thorin/be/llvm/vectorize.cpp @@ -29,7 +29,6 @@ #include #include #include -#include #include #include "thorin/primop.h" @@ -164,20 +163,7 @@ void CodeGen::emit_vectorize(u32 vector_length, llvm::Function* kernel_func, llv rv::PlatformInfo platform_info(*module_.get(), &tti, &tli); if (vector_length == 1) { - llvm::ValueToValueMapTy argMap; - auto itCalleeArgs = simd_kernel_func->args().begin(); - auto itSourceArgs = kernel_func->args().begin(); - auto endSourceArgs = kernel_func->args().end(); - - for (; itSourceArgs != endSourceArgs; ++itCalleeArgs, ++itSourceArgs) { - argMap[&*itSourceArgs] = &*itCalleeArgs; - } - - llvm::SmallVector retVec; - llvm::CloneFunctionInto(simd_kernel_func, kernel_func, argMap, llvm::CloneFunctionChangeType::LocalChangesOnly, retVec); - - // lower mask intrinsics for scalar code (vector_length == 1) - rv::lowerIntrinsics(*simd_kernel_func); + rv::cloneFunctionAndLowerIntrinsics(*kernel_func, *simd_kernel_func); } else { rv::Config config = rv::Config::createForFunction(*kernel_func); config.enableIRPolish = config.useAVX2; @@ -215,8 +201,6 @@ void CodeGen::emit_vectorize(u32 vector_length, llvm::Function* kernel_func, llv bool vectorize_ok = vectorizer.vectorize(vec_info, FAM, nullptr); assert_unused(vectorize_ok); - - vectorizer.finalize(); } // inline kernel From d06da8f462a613f17b4d07dc8ec747b1c94da52e Mon Sep 17 00:00:00 2001 From: Matthias Kurtenacker Date: Tue, 11 Jun 2024 19:29:25 +0200 Subject: [PATCH 213/342] Revert removing ${AnyDSL_LLVM_LINK_SHARED} from llvm_config --- src/thorin/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/thorin/CMakeLists.txt b/src/thorin/CMakeLists.txt index ce15ca0c2..15817401d 100644 --- a/src/thorin/CMakeLists.txt +++ b/src/thorin/CMakeLists.txt @@ -136,7 +136,7 @@ if(LLVM_FOUND) target_link_libraries(thorin PRIVATE ${RV_LIBRARIES}) list(APPEND Thorin_LLVM_COMPONENTS analysis passes transformutils) endif() - llvm_config(thorin ${Thorin_LLVM_COMPONENTS}) + llvm_config(thorin ${AnyDSL_LLVM_LINK_SHARED} ${Thorin_LLVM_COMPONENTS}) endif() if (THORIN_ENABLE_SHADY) From e173b0bf37194316228622e2be7e09948a761c28 Mon Sep 17 00:00:00 2001 From: Matthias Kurtenacker Date: Thu, 13 Jun 2024 15:06:47 +0200 Subject: [PATCH 214/342] Add error message if thorin is build with shared libs and LLVM is not. --- CMakeLists.txt | 4 ++++ cmake/modules/FindRV.cmake | 34 ---------------------------------- 2 files changed, 4 insertions(+), 34 deletions(-) delete mode 100644 cmake/modules/FindRV.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index 7939482bb..c31f7152a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -42,6 +42,10 @@ if(LLVM_FOUND) message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}") if(LLVM_LINK_LLVM_DYLIB) set(AnyDSL_LLVM_LINK_SHARED "USE_SHARED") + else() + if (BUILD_SHARED_LIBS) + message(SEND_ERROR "Cannot build thorin as a shared library with the current build of LLVM. Build LLVM with LLVM_LINK_LLVM_DYLIB or change BUILD_SHARED_LIBS to off.") + endif() endif() # check for RV find_package(RV) diff --git a/cmake/modules/FindRV.cmake b/cmake/modules/FindRV.cmake deleted file mode 100644 index 8c852e6b7..000000000 --- a/cmake/modules/FindRV.cmake +++ /dev/null @@ -1,34 +0,0 @@ -# Find the RV library -# -# Once done this will define -# RV_INCLUDE_DIRS - where to find RV library include file -# RV_LIBRARIES - where to find RV library -# RV_FOUND - True if RV library is found - -find_path(RV_INCLUDE_DIR rv/rv.h - PATHS - ${LLVM_INCLUDE_DIRS} - ${LLVM_EXTERNAL_RV_SOURCE_DIR}/include - ${LLVM_BUILD_MAIN_SRC_DIR}/../rv/include - ${LLVM_BUILD_MAIN_SRC_DIR}/tools/rv/include) -if(TARGET RV) - set(RV_LIBRARY RV) -else() - find_library(RV_LIBRARY RV PATHS ${LLVM_LIBRARY_DIRS}) -endif() -if(TARGET gensleef) - set(RV_SLEEF_LIBRARY gensleef) -else() - find_library(RV_SLEEF_LIBRARY gensleef PATHS ${LLVM_LIBRARY_DIRS}) -endif() - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(RV DEFAULT_MSG RV_INCLUDE_DIR RV_LIBRARY) - -set(RV_INCLUDE_DIRS ${RV_INCLUDE_DIR}) -set(RV_LIBRARIES ${RV_LIBRARY}) -if(RV_SLEEF_LIBRARY) - list(APPEND RV_LIBRARIES ${RV_SLEEF_LIBRARY}) -endif() - -mark_as_advanced(RV_INCLUDE_DIR RV_LIBRARY RV_SLEEF_LIBRARY) From 2400342888fe9e04111ed6c74003c7901717e221 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Mon, 17 Jun 2024 21:35:07 +0200 Subject: [PATCH 215/342] llvm: tolerate empty call instr in case of Bottom calls --- src/thorin/be/llvm/llvm.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/thorin/be/llvm/llvm.cpp b/src/thorin/be/llvm/llvm.cpp index 8e7b5d8ff..ecea16edd 100644 --- a/src/thorin/be/llvm/llvm.cpp +++ b/src/thorin/be/llvm/llvm.cpp @@ -614,7 +614,7 @@ void CodeGen::emit_epilogue(Continuation* continuation) { } call_instr = emit_call(irbuilder, body->callee(), args); - if (body->callee()->type()->as()->is_returning()) { + if (body->callee()->type()->as()->is_returning() && !body->callee()->isa()) { assert(call_instr && "returning calls always involve one of those"); assert(ret_arg && "we need a return argument too!"); From 0b7694d94f46bec59f079d668d3c629d70883c1b Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Wed, 19 Jun 2024 10:11:33 +0200 Subject: [PATCH 216/342] hotfix: don't pre-size hashmap in Rewriter --- src/thorin/transform/rewrite.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/thorin/transform/rewrite.cpp b/src/thorin/transform/rewrite.cpp index ee4e4bec0..ac352568f 100644 --- a/src/thorin/transform/rewrite.cpp +++ b/src/thorin/transform/rewrite.cpp @@ -3,7 +3,11 @@ namespace thorin { Rewriter::Rewriter(World& src, World& dst) : src_(src), dst_(dst) { - old2new_.rehash(src.defs().capacity()); + // TODO: rehash is slow-ish, especially in Debug mode + // Many short-lived Rewriters are created that only rebuild a tiny portion of the + // world, such as in CondEval. For these, we end up paying a significant amount + // of time just running this, leading to bad performance. Be smarter or don't do this. + //old2new_.rehash(src.defs().capacity()); } Rewriter::Rewriter(World& src, World& dst, Rewriter& parent) : Rewriter(src, dst) { From 1d5aa13217ee9681047dac206e73bdf60ed64b35 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Wed, 19 Jun 2024 10:15:56 +0200 Subject: [PATCH 217/342] scope-analysis: gate verify() behind THORIN_ENABLE_CHECKS --- src/thorin/analyses/scope.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/thorin/analyses/scope.cpp b/src/thorin/analyses/scope.cpp index d9e7d6fc7..4688c8a4a 100644 --- a/src/thorin/analyses/scope.cpp +++ b/src/thorin/analyses/scope.cpp @@ -303,8 +303,10 @@ Scope& ScopesForest::get_scope(Continuation* entry) { Scope* ptr = scope.get(); ptr->run(); scopes_[entry] = std::move(scope); +#if THORIN_ENABLE_CHECKS if (stack_.empty()) ptr->verify(); +#endif return *ptr; } From 5b1b623eced1d3e7ba38f68a4047c5021b24e31e Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Thu, 20 Jun 2024 18:29:55 +0200 Subject: [PATCH 218/342] fix_this()) --- src/thorin/rec_stream.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/thorin/rec_stream.cpp b/src/thorin/rec_stream.cpp index cb7c5ecac..1dfde9ef3 100644 --- a/src/thorin/rec_stream.cpp +++ b/src/thorin/rec_stream.cpp @@ -154,10 +154,10 @@ Stream& Def::stream1(Stream& s) const { if (global->is_external()) return s.fmt("{}", unique_name()); else - return s.fmt("{}({, }))", op_name(), ops()); + return s.fmt("{}({, })", op_name(), ops()); } - return s.fmt("{}({, }))", op_name(), ops()); + return s.fmt("{}({, })", op_name(), ops()); } Stream& Def::stream_let(Stream& s) const { From 73f3c2168325c78a683ab3adb9e753269dccb61c Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Mon, 24 Jun 2024 10:24:03 +0200 Subject: [PATCH 219/342] dump: don't include cont. name in param names anymore --- src/thorin/rec_stream.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/thorin/rec_stream.cpp b/src/thorin/rec_stream.cpp index 1dfde9ef3..1ddf66f4a 100644 --- a/src/thorin/rec_stream.cpp +++ b/src/thorin/rec_stream.cpp @@ -117,7 +117,7 @@ Stream& Def::stream(Stream& s, size_t max) const { Stream& Def::stream1(Stream& s) const { if (auto param = isa()) { - return s.fmt("{}.{}", param->continuation(), param->unique_name()); + return s.fmt("{}", param->unique_name()); } else if (isa()) { #if THORIN_ENABLE_CREATION_CONTEXT if (debug().creation_context != "") From 607e79f87706c9da008e05d307245f78664adea1 Mon Sep 17 00:00:00 2001 From: Matthias Kurtenacker Date: Mon, 24 Jun 2024 17:09:57 +0200 Subject: [PATCH 220/342] First quickfix for some of the problems mentioned in issue #163. * C backend: do not expect body->arg(0) to always exist in emit_epilogue. * LLVM Backend: Do not expect ret to exist in CodeGen::convert(Type*). --- src/thorin/be/c/c.cpp | 4 +++- src/thorin/be/llvm/llvm.cpp | 5 ++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/thorin/be/c/c.cpp b/src/thorin/be/c/c.cpp index f8a6c510d..636eecfac 100644 --- a/src/thorin/be/c/c.cpp +++ b/src/thorin/be/c/c.cpp @@ -686,7 +686,9 @@ void CCodeGen::emit_epilogue(Continuation* cont) { auto&& bb = cont2bb_[cont]; assert(cont->has_body()); auto body = cont->body(); - emit_debug_info(bb.tail, body->arg(0)); + if (body->num_args() > 0) { + emit_debug_info(bb.tail, body->arg(0)); + } if ((lang_ == Lang::OpenCL || (lang_ == Lang::HLS && hls_top_scope)) && (cont->is_exported())) emit_fun_decl(cont); diff --git a/src/thorin/be/llvm/llvm.cpp b/src/thorin/be/llvm/llvm.cpp index ecea16edd..afe2a95af 100644 --- a/src/thorin/be/llvm/llvm.cpp +++ b/src/thorin/be/llvm/llvm.cpp @@ -160,7 +160,10 @@ llvm::Type* CodeGen::convert(const Type* type) { } else ops.push_back(convert(op)); } - assert(ret); + + if (!ret) { + ret = llvm::Type::getVoidTy(context()); + } if (type->tag() == Node_FnType) { auto llvm_type = llvm::FunctionType::get(ret, ops, false); From 396e97401dc7b55de7ac648c406bd481835aa6e0 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Thu, 27 Jun 2024 14:02:36 +0200 Subject: [PATCH 221/342] scoped_dump: fix broken output when app node is shared --- src/thorin/util/scoped_dump.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/thorin/util/scoped_dump.cpp b/src/thorin/util/scoped_dump.cpp index b035ac3ce..d370eeb3a 100644 --- a/src/thorin/util/scoped_dump.cpp +++ b/src/thorin/util/scoped_dump.cpp @@ -49,7 +49,7 @@ void ScopedWorld::stream_cont(thorin::Stream& s, Continuation* cont) const { auto defs = *scopes_to_defs_[cont]; stream_defs(s, defs); - + s.fmt("{}", cont->body()->unique_name()); s.fmt("\b\n}}"); } @@ -119,7 +119,6 @@ void ScopedWorld::stream_def(thorin::Stream& s, const thorin::Def* def) const { } void ScopedWorld::stream_defs(thorin::Stream& s, std::vector& defs) const { - size_t i = 0; for (auto def : defs) { s.fmt("{}: ", def->unique_name()); s.fmt(Blue); @@ -127,9 +126,7 @@ void ScopedWorld::stream_defs(thorin::Stream& s, std::vector& defs) s.fmt(Reset); s.fmt(" = "); stream_def(s, def); - if (i + 1 < defs.size()) - s.fmt("\n"); - i++; + s.fmt("\n"); } } From f62781b4755ee4ab7decf7be91482fcf930f5344 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Thu, 27 Jun 2024 14:02:59 +0200 Subject: [PATCH 222/342] scoped_dump: print CC as part of continuations if non-default --- src/thorin/util/scoped_dump.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/thorin/util/scoped_dump.cpp b/src/thorin/util/scoped_dump.cpp index d370eeb3a..7cb4102c1 100644 --- a/src/thorin/util/scoped_dump.cpp +++ b/src/thorin/util/scoped_dump.cpp @@ -9,6 +9,17 @@ void ScopedWorld::stream_cont(thorin::Stream& s, Continuation* cont) const { if (cont->is_intrinsic()) s.fmt("intrinsic "); + switch (cont->cc()) { + case CC::Thorin: break; + case CC::C: s.fmt("cc(C) "); break; + case CC::Device: s.fmt("cc(Device) "); break; + default: s.fmt("cc(?)"); break; + } + + s.fmt(Green); + s.fmt("cont "); + s.fmt(Reset); + s.fmt(Red); s.fmt("{}", cont->unique_name()); s.fmt(Reset); From cf797d7bb8744e7408c6a42f4e60be835cfe5ca2 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Thu, 27 Jun 2024 14:03:27 +0200 Subject: [PATCH 223/342] scoped_dump: ignore literals at the top-level --- src/thorin/util/scoped_dump.cpp | 12 ++++++++++++ src/thorin/util/scoped_dump.h | 1 + 2 files changed, 13 insertions(+) diff --git a/src/thorin/util/scoped_dump.cpp b/src/thorin/util/scoped_dump.cpp index 7cb4102c1..d471a4320 100644 --- a/src/thorin/util/scoped_dump.cpp +++ b/src/thorin/util/scoped_dump.cpp @@ -112,6 +112,12 @@ void ScopedWorld::stream_ops(thorin::Stream& s, Defs ops) const { s.fmt(")"); } +bool ScopedWorld::print_inline(const thorin::Def* def) const { + if (def->isa()) + return true; + return false; +} + void ScopedWorld::stream_def(thorin::Stream& s, const thorin::Def* def) const { if (auto app = def->isa()) { stream_op(s, app->callee()); @@ -122,6 +128,10 @@ void ScopedWorld::stream_def(thorin::Stream& s, const thorin::Def* def) const { def->stream1(s); return; } + if (def->isa_nom()) { + s.fmt("{}", def->unique_name()); + return; + } s.fmt(Green); s.fmt("{}", def->op_name()); @@ -131,6 +141,8 @@ void ScopedWorld::stream_def(thorin::Stream& s, const thorin::Def* def) const { void ScopedWorld::stream_defs(thorin::Stream& s, std::vector& defs) const { for (auto def : defs) { + if (print_inline(def)) + continue; s.fmt("{}: ", def->unique_name()); s.fmt(Blue); s.fmt("{}", def->type()); diff --git a/src/thorin/util/scoped_dump.h b/src/thorin/util/scoped_dump.h index 388c4f60d..09aff4df5 100644 --- a/src/thorin/util/scoped_dump.h +++ b/src/thorin/util/scoped_dump.h @@ -41,6 +41,7 @@ struct ScopedWorld : public Streamable { Stream& stream(Stream&) const; private: + bool print_inline(const Def* def) const; void stream_cont(thorin::Stream& s, Continuation* cont) const; void prepare_def(Continuation* in, const Def* def) const; void stream_op(thorin::Stream&, const Def* op) const; From 671515c1eb627d2afa346f39559149ca1b6b8a19 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Tue, 2 Jul 2024 16:30:08 +0200 Subject: [PATCH 224/342] remove 'device' from PtrType --- src/thorin/be/json/json.cpp | 3 --- src/thorin/primop.cpp | 8 ++++---- src/thorin/rec_stream.cpp | 1 - src/thorin/type.cpp | 10 +++++----- src/thorin/type.h | 6 +----- src/thorin/world.h | 2 +- 6 files changed, 11 insertions(+), 19 deletions(-) diff --git a/src/thorin/be/json/json.cpp b/src/thorin/be/json/json.cpp index 8188caa3f..cee970f22 100644 --- a/src/thorin/be/json/json.cpp +++ b/src/thorin/be/json/json.cpp @@ -128,14 +128,11 @@ class TypeTable { } } else if (auto ptrtype = type->isa()) { auto pointee_type = translate_type(ptrtype->pointee()); - auto device = ptrtype->device(); result["type"] = "ptr"; result["args"] = { pointee_type }; result["name"] = pointee_type + "_p_" + std::to_string(type_table.size()); result["length"] = ptrtype->length(); - if (device != -1) - result["device"] = device; switch (ptrtype->addr_space()) { case AddrSpace::Generic: //result["addrspace"] = "generic"; //Default diff --git a/src/thorin/primop.cpp b/src/thorin/primop.cpp index a1222f4fe..786a834d0 100644 --- a/src/thorin/primop.cpp +++ b/src/thorin/primop.cpp @@ -68,14 +68,14 @@ LEA::LEA(World& world, const Def* ptr, const Def* index, Debug dbg) { auto type = ptr_type(); if (auto tuple = ptr_pointee()->isa()) { - set_type(world.ptr_type(get(tuple->types(), index), type->length(), type->device(), type->addr_space())); + set_type(world.ptr_type(get(tuple->types(), index), type->length(), type->addr_space())); } else if (auto array = ptr_pointee()->isa()) { - set_type(world.ptr_type(array->elem_type(), type->length(), type->device(), type->addr_space())); + set_type(world.ptr_type(array->elem_type(), type->length(), type->addr_space())); } else if (auto struct_type = ptr_pointee()->isa()) { - set_type(world.ptr_type(get(struct_type->types(), index), type->length(), type->device(), type->addr_space())); + set_type(world.ptr_type(get(struct_type->types(), index), type->length(), type->addr_space())); } else if (auto prim_type = ptr_pointee()->isa()) { assert(prim_type->length() > 1); - set_type(world.ptr_type(world.prim_type(prim_type->primtype_tag()), type->length(), type->device(), type->addr_space())); + set_type(world.ptr_type(world.prim_type(prim_type->primtype_tag()), type->length(), type->addr_space())); } else { THORIN_UNREACHABLE; } diff --git a/src/thorin/rec_stream.cpp b/src/thorin/rec_stream.cpp index 1ddf66f4a..0eb0fe449 100644 --- a/src/thorin/rec_stream.cpp +++ b/src/thorin/rec_stream.cpp @@ -209,7 +209,6 @@ Stream& Type::stream(Stream& s) const { if (t->is_vector()) s.fmt("<{} x", t->length()); s.fmt("{}*", t->pointee()); if (t->is_vector()) s.fmt(">"); - if (t->device() != -1) s.fmt("[{}]", t->device()); switch (t->addr_space()) { case AddrSpace::Global: s.fmt("[Global]"); break; diff --git a/src/thorin/type.cpp b/src/thorin/type.cpp index d5b84b0dd..837d6ff2d 100644 --- a/src/thorin/type.cpp +++ b/src/thorin/type.cpp @@ -59,7 +59,7 @@ const Type* FrameType ::rebuild(World& w, const Type* , Defs ) const const Type* IndefiniteArrayType::rebuild(World& w, const Type* , Defs o) const { return w.indefinite_array_type(o[0]->as()); } const Type* MemType ::rebuild(World& w, const Type* , Defs ) const { return w.mem_type(); } const Type* PrimType ::rebuild(World& w, const Type* , Defs ) const { return w.prim_type(primtype_tag(), length()); } -const Type* PtrType ::rebuild(World& w, const Type* , Defs o) const { return w.ptr_type(o[0]->as(), length(), device(), addr_space()); } +const Type* PtrType ::rebuild(World& w, const Type* , Defs o) const { return w.ptr_type(o[0]->as(), length(), addr_space()); } const Type* TupleType ::rebuild(World& w, const Type* , Defs o) const { return w.tuple_type(defs2types(o)); } /* @@ -115,7 +115,7 @@ bool use_lea(const Type* type) { return type->isa() || type->isaas(); - return ptr->device() == device() && ptr->addr_space() == addr_space(); + return ptr->addr_space() == addr_space(); } TypeTable::TypeTable(World& world) @@ -163,8 +163,8 @@ const PrimType* World::prim_type(PrimTypeTag tag, size_t length) { return length == 1 ? types_.primtypes_[i] : make(*this, tag, length, Debug()); } -const PtrType* World::ptr_type(const Type* pointee, size_t length, int32_t device, AddrSpace addr_space) { - return make(*this, pointee, length, device, addr_space, Debug()); +const PtrType* World::ptr_type(const Type* pointee, size_t length, AddrSpace addr_space) { + return make(*this, pointee, length, addr_space, Debug()); } const FnType* World::fn_type(Types args) { return make(*this, types2defs(args), Node_FnType, Debug()); } diff --git a/src/thorin/type.h b/src/thorin/type.h index 39c799011..e73ad5b6f 100644 --- a/src/thorin/type.h +++ b/src/thorin/type.h @@ -238,17 +238,14 @@ enum class AddrSpace : uint32_t { /// Pointer type. class PtrType : public VectorType, public TypeOpsMixin { private: - PtrType(World& world, const Type* pointee, size_t length, int32_t device, AddrSpace addr_space, Debug dbg) + PtrType(World& world, const Type* pointee, size_t length, AddrSpace addr_space, Debug dbg) : VectorType(world, Node_PtrType, {pointee}, length, dbg) , addr_space_(addr_space) - , device_(device) {} public: const Type* pointee() const { return op(0)->as(); } AddrSpace addr_space() const { return addr_space_; } - int32_t device() const { return device_; } - bool is_host_device() const { return device_ == -1; } hash_t vhash() const override; bool equal(const Def* other) const override; @@ -257,7 +254,6 @@ class PtrType : public VectorType, public TypeOpsMixin { const Type* rebuild(World&, const Type*, Defs) const override; AddrSpace addr_space_; - int32_t device_; friend class World; }; diff --git a/src/thorin/world.h b/src/thorin/world.h index 6e62023d3..a35c95ad3 100644 --- a/src/thorin/world.h +++ b/src/thorin/world.h @@ -118,7 +118,7 @@ class World : public Streamable { const BottomType* bottom_type() { return make(*this, Debug()); } const MemType* mem_type() { return make(*this, Debug()); } const FrameType* frame_type() { return make(*this, Debug()); } - const PtrType* ptr_type(const Type* pointee, size_t length = 1, int32_t device = -1, AddrSpace addr_space = AddrSpace::Generic); + const PtrType* ptr_type(const Type* pointee, size_t length = 1, AddrSpace addr_space = AddrSpace::Generic); const FnType* fn_type() { return fn_type({}); } ///< Returns an empty @p FnType. const FnType* fn_type(Types args); const ClosureType* closure_type(Types args); From 955acc202a0578d7ec8df13783c5e8c19d4e25a8 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Thu, 27 Jun 2024 14:45:37 +0200 Subject: [PATCH 225/342] revive spir-v backend from the grave --- CMakeLists.txt | 6 + src/thorin/CMakeLists.txt | 7 + src/thorin/be/spirv/spirv.cpp | 975 ++++++++++++++++++++++++++ src/thorin/be/spirv/spirv.h | 186 +++++ src/thorin/be/spirv/spirv_builder.hpp | 669 ++++++++++++++++++ 5 files changed, 1843 insertions(+) create mode 100644 src/thorin/be/spirv/spirv.cpp create mode 100644 src/thorin/be/spirv/spirv.h create mode 100644 src/thorin/be/spirv/spirv_builder.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index c31f7152a..17d22bfe6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -72,6 +72,12 @@ else() set(THORIN_ENABLE_SHADY TRUE) endif() +find_package(SPIRV-Headers) +if (SPIRV-Headers_FOUND) + message(STATUS "Found SPIRV-Headers at ${SPIRV-Headers_DIR}") + set(THORIN_ENABLE_SPIRV TRUE) +endif() + message(STATUS "Using Debug flags: ${CMAKE_CXX_FLAGS_DEBUG}") message(STATUS "Using Release flags: ${CMAKE_CXX_FLAGS_RELEASE}") if(DEFINED CMAKE_BUILD_TYPE) diff --git a/src/thorin/CMakeLists.txt b/src/thorin/CMakeLists.txt index 15817401d..ec3bc9ef1 100644 --- a/src/thorin/CMakeLists.txt +++ b/src/thorin/CMakeLists.txt @@ -124,6 +124,13 @@ if(THORIN_ENABLE_JSON) ) endif() +if(THORIN_ENABLE_SPIRV) + list(APPEND THORIN_SOURCES + be/spirv/spirv.cpp + be/spirv/spirv.h + ) +endif() + add_library(thorin ${THORIN_SOURCES}) target_include_directories(thorin PUBLIC ${Half_INCLUDE_DIRS} ${Thorin_ROOT_DIR}/src ${CMAKE_BINARY_DIR}/include) diff --git a/src/thorin/be/spirv/spirv.cpp b/src/thorin/be/spirv/spirv.cpp new file mode 100644 index 000000000..3b2d0e146 --- /dev/null +++ b/src/thorin/be/spirv/spirv.cpp @@ -0,0 +1,975 @@ +#include "thorin/be/spirv/spirv.h" + +#include "thorin/analyses/scope.h" +#include "thorin/analyses/schedule.h" +#include "thorin/analyses/domtree.h" + +#include + +namespace thorin::spirv { + +/// Used as a dummy SSA value for emitting things like mem/unit +/// Should never make it in the binary files ! +constexpr SpvId spv_none { 0 }; + +// SPIR-V has 3 "kinds" of primitives, and the user may declare arbitrary bitwidths, the following helps in translation: +enum class PrimTypeKind { + Signed, Unsigned, Float +}; +inline PrimTypeKind classify_primtype(const PrimType* type) { + switch (type->tag()) { +#define THORIN_QS_TYPE(T, M) THORIN_PS_TYPE(T, M) +#define THORIN_PS_TYPE(T, M) \ +case PrimType_##T: \ + return PrimTypeKind::Signed; \ + break; +#include "thorin/tables/primtypetable.h" +#undef THORIN_QS_TYPE +#undef THORIN_PS_TYPE + +#define THORIN_QU_TYPE(T, M) THORIN_PU_TYPE(T, M) +#define THORIN_PU_TYPE(T, M) \ +case PrimType_##T: \ + return PrimTypeKind::Unsigned; \ + break; +#include "thorin/tables/primtypetable.h" +#undef THORIN_QU_TYPE +#undef THORIN_PU_TYPE + +#define THORIN_QF_TYPE(T, M) THORIN_PF_TYPE(T, M) +#define THORIN_PF_TYPE(T, M) \ +case PrimType_##T: \ + return PrimTypeKind::Float; \ + break; +#include "thorin/tables/primtypetable.h" +#undef THORIN_QF_TYPE +#undef THORIN_PF_TYPE + default: THORIN_UNREACHABLE; + } +} +inline const PrimType* get_primtype(World& world, PrimTypeKind kind, int bitwidth, int length) { +#define GET_PRIMTYPE_WITH_KIND(kind) \ +switch (bitwidth) { \ + case 8: return world.type_p##kind##8 (length); \ + case 16: return world.type_p##kind##16(length); \ + case 32: return world.type_p##kind##32(length); \ + case 64: return world.type_p##kind##64(length); \ +} + +#define GET_PRIMTYPE_WITH_KIND_F(kind) \ +switch (bitwidth) { \ + case 8: world.ELOG("8-bit floats do not exist"); \ + case 16: return world.type_p##kind##16(length); \ + case 32: return world.type_p##kind##32(length); \ + case 64: return world.type_p##kind##64(length); \ +} + + switch (kind) { + case PrimTypeKind::Signed: GET_PRIMTYPE_WITH_KIND(s); THORIN_UNREACHABLE; + case PrimTypeKind::Unsigned: GET_PRIMTYPE_WITH_KIND(u); THORIN_UNREACHABLE; + case PrimTypeKind::Float: GET_PRIMTYPE_WITH_KIND_F(f); THORIN_UNREACHABLE; + default: THORIN_UNREACHABLE; + } + +#undef GET_PRIMTYPE_WITH_KIND +#undef GET_PRIMTYPE_WITH_KIND_F +} + +BasicBlockBuilder::BasicBlockBuilder(FnBuilder& fn_builder) + : builder::SpvBasicBlockBuilder(fn_builder.file_builder), fn_builder(fn_builder), file_builder(fn_builder.file_builder) { + label = file_builder.generate_fresh_id(); +} + +FnBuilder::FnBuilder(CodeGen* cg, FileBuilder& file_builder) : builder::SpvFnBuilder(&file_builder), cg(cg), file_builder(file_builder) {} + +FileBuilder::FileBuilder(CodeGen* cg) : builder::SpvFileBuilder(), cg(cg) { + capability(spv::Capability::CapabilityShader); + capability(spv::Capability::CapabilityVariablePointers); + capability(spv::Capability::CapabilityPhysicalStorageBufferAddresses); + // capability(spv::Capability::CapabilityInt16); + capability(spv::Capability::CapabilityInt64); + + addressing_model = spv::AddressingModelPhysicalStorageBuffer64; + memory_model = spv::MemoryModel::MemoryModelGLSL450; +} + +SpvId FileBuilder::u32_t() { + if (u32_t_.id == 0) + u32_t_ = cg->convert(cg->world().type_pu32())->type_id; + return u32_t_; +} + +SpvId FileBuilder::u32_constant(uint32_t pattern) { + return constant(u32_t(), { pattern }); +} + +Builtins::Builtins(FileBuilder& builder) { + auto& world = builder.cg->world(); + auto spv_uvec3_t = builder.cg->convert(world.type_pu32(3)); + auto spv_uint_t = builder.cg->convert(world.type_pu32()); + auto spv_uvec3_pt = builder.declare_ptr_type(spv::StorageClassInput, spv_uvec3_t->type_id); + auto spv_uvec3_ptp = builder.declare_ptr_type(spv::StorageClassPrivate, spv_uvec3_t->type_id); + auto spv_uint_pt = builder.declare_ptr_type(spv::StorageClassInput, spv_uint_t->type_id); + + // Because we technically can have multiple entry points, we take the easy way out and make each entry point + // write to a private variable the actual workgroup size for that specific kernel. Dirty, but simple. + workgroup_size = builder.variable(spv_uvec3_ptp, spv::StorageClassPrivate); + builder.name(workgroup_size, "BuiltInWorkgroupSize"); + + num_workgroups = builder.variable(spv_uvec3_pt, spv::StorageClassInput); + builder.decorate(num_workgroups, spv::DecorationBuiltIn, { spv::BuiltInNumWorkgroups }); + builder.name(num_workgroups, "BuiltInNumWorkgroups"); + + workgroup_id = builder.variable(spv_uvec3_pt, spv::StorageClassInput); + builder.decorate(workgroup_id, spv::DecorationBuiltIn, { spv::BuiltInWorkgroupId }); + builder.name(workgroup_id, "BuiltInWorkgroupId"); + + local_id = builder.variable(spv_uvec3_pt, spv::StorageClassInput); + builder.decorate(local_id, spv::DecorationBuiltIn, { spv::BuiltInLocalInvocationId }); + builder.name(local_id, "BuiltInLocalInvocationId"); + + global_id = builder.variable(spv_uvec3_pt, spv::StorageClassInput); + builder.decorate(global_id, spv::DecorationBuiltIn, { spv::BuiltInGlobalInvocationId }); + builder.name(global_id, "BuiltInGlobalInvocationId"); + + local_invocation_index = builder.variable(spv_uint_pt, spv::StorageClassInput); + builder.decorate(local_invocation_index, spv::DecorationBuiltIn, { spv::BuiltInLocalInvocationIndex }); + builder.name(local_invocation_index, "BuiltInLocalInvocationIndex"); +} + +ImportedInstructions::ImportedInstructions(FileBuilder& builder) { + builder.extension("SPV_KHR_non_semantic_info"); + shader_printf = builder.extended_import("NonSemantic.DebugPrintf"); +} + +CodeGen::CodeGen(thorin::World& world, Cont2Config& kernel_config, bool debug) + : thorin::CodeGen(world, debug), kernel_config_(kernel_config) +{} + +void CodeGen::emit_stream(std::ostream& out) { + builder_ = std::make_unique(this); + + builder_->builtins = std::make_unique(*builder_); + builder_->imported_instrs = std::make_unique(*builder_); + + structure_loops(); + structure_flow(); + // cleanup_world(world()); + + Scope::for_each(world(), [&](const Scope& scope) { emit(scope); }); + + auto push_constant_arr_type = convert(world().definite_array_type(world().type_pu32(), 128))->type_id; + auto push_constant_struct_type = builder_->declare_struct_type({ push_constant_arr_type }); + auto push_constant_struct_ptr_type = builder_->declare_ptr_type(spv::StorageClassPushConstant, push_constant_struct_type); + builder_->name(push_constant_struct_type, "ThorinPushConstant"); + builder_->decorate(push_constant_struct_type, spv::DecorationBlock); + builder_->decorate_member(push_constant_struct_type, 0, spv::DecorationOffset, { 0 }); + builder_->decorate(push_constant_arr_type, spv::DecorationArrayStride, { 4 }); + auto push_constant_struct_ptr = builder_->variable(push_constant_struct_ptr_type, spv::StorageClassPushConstant); + builder_->name(push_constant_struct_ptr, "thorin_push_constant_data"); + + auto entry_pt_signature = builder_->declare_fn_type({}, builder_->void_type); + for (auto& cont : world().continuations()) { + if (cont->is_exported()) { + assert(defs_.contains(cont) && kernel_config_.contains(cont)); + auto config = kernel_config_.find(cont); + + SpvId callee = defs_[cont]; + + FnBuilder fn_builder(this, *builder_.get()); + fn_builder.fn_type = entry_pt_signature; + fn_builder.fn_ret_type = builder_->void_type; + + BasicBlockBuilder* bb = fn_builder.bbs.emplace_back(std::make_unique(fn_builder)).get(); + fn_builder.bbs_to_emit.push_back(bb); + + auto block = config->second->as()->block_size(); + std::vector local_size = { + (uint32_t) std::get<0>(block), + (uint32_t) std::get<1>(block), + (uint32_t) std::get<2>(block), + }; + + auto spv_uvec3_t = convert(world().type_pu32(3)); + SpvId wg_size_constant = builder_->constant_composite(spv_uvec3_t->type_id, { + builder_->u32_constant(local_size[0]), + builder_->u32_constant(local_size[1]), + builder_->u32_constant(local_size[2]), + }); + bb->store(wg_size_constant, builder_->builtins->workgroup_size); + + // iterate on cont type and extract the arguments + auto ptr_type = convert(world().ptr_type(world().definite_array_type(world().type_pu32(), 128), 1, 4, AddrSpace::Push))->type_id; + auto zero = bb->file_builder.u32_constant(0); + auto arr_ref = bb->access_chain(ptr_type, push_constant_struct_ptr, { zero }); + uint32_t offset = 0; + std::vector args; + for (size_t i = 0; i < cont->num_params(); i++) { + auto param = cont->param(i); + auto param_type = param->type(); + if (param_type == world().unit() || param_type == world().mem_type() || param_type->isa()) continue; + assert(param_type->order() == 0); + auto converted = convert(param_type); + assert(converted->datatype != nullptr); + SpvId arg = converted->datatype->emit_deserialization(*bb, spv::StorageClassPushConstant, arr_ref, bb->file_builder.u32_constant(offset)); + args.push_back(arg); + offset += converted->datatype->serialized_size(); + } + + bb->call(builder_->void_type, callee, args); + bb->return_void(); + + builder_->define_function(fn_builder); + builder_->name(fn_builder.function_id, "entry_point_" + cont->name()); + + std::vector interface = { + push_constant_struct_ptr, + builder_->builtins->workgroup_size, + builder_->builtins->num_workgroups, + builder_->builtins->workgroup_id, + builder_->builtins->local_id, + builder_->builtins->global_id, + builder_->builtins->local_invocation_index, + }; + builder_->declare_entry_point(spv::ExecutionModelGLCompute, fn_builder.function_id, "kernel_main", interface); + + builder_->execution_mode(fn_builder.function_id, spv::ExecutionModeLocalSize, local_size); + } + } + + builder_->finish(out); + builder_ = nullptr; +} + +void CodeGen::emit(const thorin::Scope& scope) { + entry_ = scope.entry(); + assert(entry_->is_returning()); + + FnBuilder fn(this, *builder_.get()); + fn.scope = &scope; + fn.fn_type = convert(entry_->type())->type_id; + fn.fn_ret_type = get_codom_type(entry_); + defs_.emplace(scope.entry(), fn.function_id); + + current_fn_ = &fn; + + auto conts = schedule(scope); + + fn.bbs_to_emit.reserve(conts.size()); + fn.bbs.reserve(conts.size()); + auto& bbs = fn.bbs; + + for (auto cont : conts) { + if (cont->intrinsic() == Intrinsic::EndScope) continue; + + BasicBlockBuilder* bb = bbs.emplace_back(std::make_unique(fn)).get(); + fn.bbs_to_emit.emplace_back(bb); + auto [i, b] = fn.bbs_map.emplace(cont, bb); + assert(b); + + if (debug()) + builder_->name(bb->label, cont->name().c_str()); + fn.labels.emplace(cont, bb->label); + + if (entry_ == cont) { + for (auto param : entry_->params()) { + if (is_mem(param) || is_unit(param)) { + // Nothing + } else if (param->order() == 0) { + auto param_t = convert(param->type()); + auto id = fn.parameter(param_t->type_id); + fn.params[param] = id; + if (param->type()->isa()) { + builder_->decorate(id, spv::DecorationAliased); + } + } + } + } else { + for (auto param : cont->params()) { + if (is_mem(param) || is_unit(param)) { + // Nothing + } else { + // OpPhi requires the full list of predecessors (values, labels) + // We don't have that yet! But we will need the Phi node identifier to build the basic blocks ... + // To solve this we generate an id for the phi node now, but defer emission of it to a later stage + auto type = convert(param->type())->type_id; + assert(type.id != 0); + bb->phis_map[param] = { type, builder_->generate_fresh_id(), {} }; + } + } + } + } + + for (auto cont : conts) { + if (cont->intrinsic() == Intrinsic::EndScope) continue; + assert(cont == entry_ || cont->is_basicblock()); + emit_epilogue(cont, fn.bbs_map[cont]); + } + + for(auto& bb : fn.bbs) { + for (auto& [param, phi] : bb->phis_map) { + bb->phis.emplace_back(&phi); + } + } + + builder_->define_function(fn); + builder_->name(fn.function_id, scope.entry()->name()); +} + +SpvId CodeGen::get_codom_type(const Continuation* fn) { + auto ret_cont_type = fn->ret_param()->type(); + std::vector types; + for (auto& op : ret_cont_type->ops()) { + if (op->isa() || is_type_unit(op)) + continue; + assert(op->order() == 0); + types.push_back(convert(op)->type_id); + } + if (types.empty()) + return builder_->void_type; + if (types.size() == 1) + return types[0]; + return builder_->declare_struct_type(types); +} + +void CodeGen::emit_epilogue(Continuation* continuation, BasicBlockBuilder* bb) { + // Handles the potential nuances of jumping to another continuation + auto jump_to_next_cont_with_args = [&](Continuation* succ, std::vector args) { + bb->branch(current_fn_->labels[succ]); + for (size_t i = 0, j = 0; i != succ->num_params(); ++i) { + auto param = succ->param(i); + if (is_mem(param) || is_unit(param)) + continue; + auto& phi = current_fn_->bbs_map[succ]->phis_map[param]; + phi.preds.emplace_back(args[j], current_fn_->labels[continuation]); + j++; + } + }; + + if (continuation->callee() == entry_->ret_param()) { + std::vector values; + + for (auto arg : continuation->args()) { + assert(arg->order() == 0); + auto val = emit(arg, bb); + if (is_mem(arg) || is_unit(arg)) + continue; + values.emplace_back(val); + } + + switch (values.size()) { + case 0: bb->return_void(); break; + case 1: bb->return_value(values[0]); break; + default: bb->return_value(bb->composite(current_fn_->fn_ret_type, values)); + } + } else if (auto callee = continuation->callee()->isa_continuation(); callee && callee->is_basicblock()) { // ordinary jump + int index = -1; + for (auto& arg : continuation->args()) { + index++; + auto val = emit(arg, bb); + if (is_mem(arg) || is_unit(arg)) continue; + bb->args[arg] = val; + auto* param = callee->param(index); + auto& phi = current_fn_->bbs_map[callee]->phis_map[param]; + phi.preds.emplace_back(bb->args[arg], current_fn_->labels[continuation]); + } + bb->branch(current_fn_->labels[callee]); + } else if (continuation->callee() == world().branch()) { + auto& domtree = current_fn_->scope->b_cfg().domtree(); + auto merge_cont = domtree.idom(current_fn_->scope->f_cfg().operator[](continuation))->continuation(); + SpvId merge_bb; + if (merge_cont == current_fn_->scope->exit()) { + BasicBlockBuilder* unreachable_merge_bb = current_fn_->bbs.emplace_back(std::make_unique(*current_fn_)).get(); + current_fn_->bbs_to_emit.emplace_back(unreachable_merge_bb); + builder_->name(unreachable_merge_bb->label, "merge_unreachable" + continuation->name()); + unreachable_merge_bb->unreachable(); + merge_bb = unreachable_merge_bb->label; + } else { + // TODO create a dedicated merge bb if this one is the merge blocks for more than 1 selection construct + merge_bb = current_fn_->labels[merge_cont]; + } + + auto cond = emit(continuation->arg(0), bb); + bb->args.emplace(continuation->arg(0), cond); + auto tbb = current_fn_->labels[continuation->arg(1)->as_continuation()]; + auto fbb = current_fn_->labels[continuation->arg(2)->as_continuation()]; + bb->selection_merge(merge_bb,spv::SelectionControlMaskNone); + bb->branch_conditional(cond, tbb, fbb); + } else if (continuation->callee()->isa() && continuation->callee()->as()->intrinsic() == Intrinsic::Match) { + /*auto val = emit(continuation->arg(0)); + auto otherwise_bb = cont2bb(continuation->arg(1)->as_continuation()); + auto match = irbuilder.CreateSwitch(val, otherwise_bb, continuation->num_args() - 2); + for (size_t i = 2; i < continuation->num_args(); i++) { + auto arg = continuation->arg(i)->as(); + auto case_const = llvm::cast(emit(arg->op(0))); + auto case_bb = cont2bb(arg->op(1)->as_continuation()); + match->addCase(case_const, case_bb); + }*/ + THORIN_UNREACHABLE; + } else if (continuation->callee()->isa()) { + bb->unreachable(); + } else if (continuation->intrinsic() == Intrinsic::SCFLoopHeader) { + auto merge_label = current_fn_->bbs_map[const_cast(continuation->attributes_.scf_metadata.loop_header.merge_target)]->label; + auto continue_label = current_fn_->bbs_map[const_cast(continuation->attributes_.scf_metadata.loop_header.continue_target)]->label; + bb->loop_merge(merge_label, continue_label, spv::LoopControlMaskNone, {}); + + BasicBlockBuilder* dispatch_bb = current_fn_->bbs.emplace_back(std::make_unique(*current_fn_)).get(); + + auto header_bb_location = std::find(current_fn_->bbs_to_emit.begin(), current_fn_->bbs_to_emit.end(), bb); + + current_fn_->bbs_to_emit.emplace(header_bb_location + 1, dispatch_bb); + builder_->name(dispatch_bb->label, "dispatch_" + continuation->name()); + bb->branch(dispatch_bb->label); + int targets = continuation->num_ops(); + assert(targets > 0); + + // TODO handle dispatching to multiple targets + assert(targets == 1); + auto dispatch_target = continuation->op(0)->as_continuation(); + // Extract the relevant variant & expand the tuple if necessary + auto arg = world().variant_extract(continuation->param(0), 0); + auto extracted = emit(arg, dispatch_bb); + + if (dispatch_target->param(0)->type()->equal(arg->type())) { + auto* param = dispatch_target->param(0); + auto& phi = current_fn_->bbs_map[dispatch_target]->phis_map[param]; + phi.preds.emplace_back(extracted, dispatch_bb->label); + } else { + assert(false && "TODO destructure argument"); + } + + dispatch_bb->branch(current_fn_->bbs_map[dispatch_target]->label); + + } else if (continuation->intrinsic() == Intrinsic::SCFLoopContinue) { + auto loop_header = continuation->op(0)->as_continuation(); + auto header_label = current_fn_->bbs_map[loop_header]->label; + + auto arg = continuation->param(0); + bb->args[arg] = emit(arg, bb); + auto* param = loop_header->param(0); + auto& phi = current_fn_->bbs_map[loop_header]->phis_map[param]; + phi.preds.emplace_back(bb->args[arg], current_fn_->labels[continuation]); + + bb->branch(header_label); + } else if (continuation->intrinsic() == Intrinsic::SCFLoopMerge) { + + int targets = continuation->num_ops(); + assert(targets > 0); + + // TODO handle dispatching to multiple targets + assert(targets == 1); + auto callee = continuation->op(0)->as_continuation(); + // TODO phis + bb->branch(current_fn_->bbs_map[callee]->label); + } else if (auto builtin = continuation->callee()->isa_continuation(); builtin->is_imported()) { + // Ensure we emit previous memory operations + assert(is_mem(continuation->arg(0))); + emit(continuation->arg(0), bb); + + auto productions = emit_builtin(continuation, builtin, bb); + auto succ = continuation->args().back()->as_continuation(); + jump_to_next_cont_with_args(succ, productions); + } else if (auto intrinsic = continuation->callee()->isa_continuation(); callee && callee->is_intrinsic()) { + THORIN_UNREACHABLE; + } else { // function/closure call + // put all first-order args into an array + std::vector call_args; + const Def* ret_arg = nullptr; + for (auto arg : continuation->args()) { + if (arg->order() == 0) { + auto arg_type = arg->type(); + auto arg_val = emit(arg, bb); + if (arg_type == world().unit() || arg_type == world().mem_type()) continue; + call_args.push_back(arg_val); + } else { + assert(!ret_arg); + ret_arg = arg; + } + } + + auto ret_type = get_codom_type(continuation); + + SpvId call_result; + if (auto called_continuation = continuation->callee()->isa_continuation()) { + call_result = bb->call(ret_type, emit(called_continuation, bb), call_args); + } else { + // must be a closure + THORIN_UNREACHABLE; + + // auto closure = emit(callee); + // args.push_back(irbuilder.CreateExtractValue(closure, 1)); + // call = irbuilder.CreateCall(irbuilder.CreateExtractValue(closure, 0), args); + } + + // must be call + continuation --- call + return has been removed by codegen_prepare + auto succ = ret_arg->as_continuation(); + + size_t n = 0; + const Param* last_param = nullptr; + for (auto param : succ->params()) { + if (is_mem(param) || is_unit(param)) + continue; + last_param = param; + n++; + } + + if (n == 0) { + bb->branch(current_fn_->labels[succ]); + } else if (n == 1) { + bb->branch(current_fn_->labels[succ]); + + auto& phi = current_fn_->bbs_map[succ]->phis_map[last_param]; + phi.preds.emplace_back(call_result, current_fn_->labels[continuation]); + } else { + Array extracts(n); + for (size_t i = 0, j = 0; i != succ->num_params(); ++i) { + auto param = succ->param(i); + if (is_mem(param) || is_unit(param)) + continue; + extracts[j] = bb->extract(convert(param->type())->type_id, call_result, { (uint32_t) j }); + j++; + } + + bb->branch(current_fn_->labels[succ]); + + for (size_t i = 0, j = 0; i != succ->num_params(); ++i) { + auto param = succ->param(i); + if (is_mem(param) || is_unit(param)) + continue; + + auto& phi = current_fn_->bbs_map[succ]->phis_map[param]; + phi.preds.emplace_back(extracts[j], current_fn_->labels[continuation]); + + j++; + } + } + } +} + +SpvId CodeGen::emit(const Def* def, BasicBlockBuilder* bb) { + if (auto bin = def->isa()) { + SpvId lhs = emit(bin->lhs(), bb); + SpvId rhs = emit(bin->rhs(), bb); + ConvertedType* result_types = convert(def->type()); + SpvId result_type = result_types->type_id; + + if (auto cmp = bin->isa()) { + auto type = cmp->lhs()->type(); + if (is_type_s(type)) { + switch (cmp->cmp_tag()) { + case Cmp_eq: return bb->binop(spv::Op::OpIEqual , result_type, lhs, rhs); + case Cmp_ne: return bb->binop(spv::Op::OpINotEqual , result_type, lhs, rhs); + case Cmp_gt: return bb->binop(spv::Op::OpSGreaterThan , result_type, lhs, rhs); + case Cmp_ge: return bb->binop(spv::Op::OpSGreaterThanEqual , result_type, lhs, rhs); + case Cmp_lt: return bb->binop(spv::Op::OpSLessThan , result_type, lhs, rhs); + case Cmp_le: return bb->binop(spv::Op::OpSLessThanEqual , result_type, lhs, rhs); + } + } else if (is_type_u(type)) { + switch (cmp->cmp_tag()) { + case Cmp_eq: return bb->binop(spv::Op::OpIEqual , result_type, lhs, rhs); + case Cmp_ne: return bb->binop(spv::Op::OpINotEqual , result_type, lhs, rhs); + case Cmp_gt: return bb->binop(spv::Op::OpUGreaterThan , result_type, lhs, rhs); + case Cmp_ge: return bb->binop(spv::Op::OpUGreaterThanEqual , result_type, lhs, rhs); + case Cmp_lt: return bb->binop(spv::Op::OpULessThan , result_type, lhs, rhs); + case Cmp_le: return bb->binop(spv::Op::OpULessThanEqual , result_type, lhs, rhs); + } + } else if (is_type_f(type)) { + switch (cmp->cmp_tag()) { + // TODO look into the NaN story + case Cmp_eq: return bb->binop(spv::Op::OpFOrdEqual , result_type, lhs, rhs); + case Cmp_ne: return bb->binop(spv::Op::OpFOrdNotEqual , result_type, lhs, rhs); + case Cmp_gt: return bb->binop(spv::Op::OpFOrdGreaterThan , result_type, lhs, rhs); + case Cmp_ge: return bb->binop(spv::Op::OpFOrdGreaterThanEqual , result_type, lhs, rhs); + case Cmp_lt: return bb->binop(spv::Op::OpFOrdLessThan , result_type, lhs, rhs); + case Cmp_le: return bb->binop(spv::Op::OpFOrdLessThanEqual , result_type, lhs, rhs); + } + } else if (type->isa()) { + assertf(false, "Physical pointers are unsupported"); + } else if(is_type_bool(type)) { + switch (cmp->cmp_tag()) { + // TODO look into the NaN story + case Cmp_eq: return bb->binop(spv::Op::OpLogicalEqual , result_type, lhs, rhs); + case Cmp_ne: return bb->binop(spv::Op::OpLogicalNotEqual , result_type, lhs, rhs); + default: THORIN_UNREACHABLE; + } + assertf(false, "TODO: should we emulate the other comparison ops ?"); + } + } + + if (auto arithop = bin->isa()) { + auto type = arithop->type(); + + if (is_type_f(type)) { + switch (arithop->arithop_tag()) { + case ArithOp_add: return bb->binop(spv::Op::OpFAdd, result_type, lhs, rhs); + case ArithOp_sub: return bb->binop(spv::Op::OpFSub, result_type, lhs, rhs); + case ArithOp_mul: return bb->binop(spv::Op::OpFMul, result_type, lhs, rhs); + case ArithOp_div: return bb->binop(spv::Op::OpFDiv, result_type, lhs, rhs); + case ArithOp_rem: return bb->binop(spv::Op::OpFRem, result_type, lhs, rhs); + case ArithOp_and: + case ArithOp_or: + case ArithOp_xor: + case ArithOp_shl: + case ArithOp_shr: THORIN_UNREACHABLE; + } + } + + if (is_type_s(type)) { + switch (arithop->arithop_tag()) { + case ArithOp_add: return bb->binop(spv::Op::OpIAdd , result_type, lhs, rhs); + case ArithOp_sub: return bb->binop(spv::Op::OpISub , result_type, lhs, rhs); + case ArithOp_mul: return bb->binop(spv::Op::OpIMul , result_type, lhs, rhs); + case ArithOp_div: return bb->binop(spv::Op::OpSDiv , result_type, lhs, rhs); + case ArithOp_rem: return bb->binop(spv::Op::OpSRem , result_type, lhs, rhs); + case ArithOp_and: return bb->binop(spv::Op::OpBitwiseAnd , result_type, lhs, rhs); + case ArithOp_or: return bb->binop(spv::Op::OpBitwiseOr , result_type, lhs, rhs); + case ArithOp_xor: return bb->binop(spv::Op::OpBitwiseXor , result_type, lhs, rhs); + case ArithOp_shl: return bb->binop(spv::Op::OpShiftLeftLogical , result_type, lhs, rhs); + case ArithOp_shr: return bb->binop(spv::Op::OpShiftRightArithmetic , result_type, lhs, rhs); + } + } else if (is_type_u(type)) { + switch (arithop->arithop_tag()) { + case ArithOp_add: return bb->binop(spv::Op::OpIAdd , result_type, lhs, rhs); + case ArithOp_sub: return bb->binop(spv::Op::OpISub , result_type, lhs, rhs); + case ArithOp_mul: return bb->binop(spv::Op::OpIMul , result_type, lhs, rhs); + case ArithOp_div: return bb->binop(spv::Op::OpUDiv , result_type, lhs, rhs); + case ArithOp_rem: return bb->binop(spv::Op::OpUMod , result_type, lhs, rhs); + case ArithOp_and: return bb->binop(spv::Op::OpBitwiseAnd , result_type, lhs, rhs); + case ArithOp_or: return bb->binop(spv::Op::OpBitwiseOr , result_type, lhs, rhs); + case ArithOp_xor: return bb->binop(spv::Op::OpBitwiseXor , result_type, lhs, rhs); + case ArithOp_shl: return bb->binop(spv::Op::OpShiftLeftLogical , result_type, lhs, rhs); + case ArithOp_shr: return bb->binop(spv::Op::OpShiftRightLogical , result_type, lhs, rhs); + } + } else if(is_type_bool(type)) { + switch (arithop->arithop_tag()) { + case ArithOp_and: return bb->binop(spv::Op::OpLogicalAnd , result_type, lhs, rhs); + case ArithOp_or: return bb->binop(spv::Op::OpLogicalOr , result_type, lhs, rhs); + // Note: there is no OpLogicalXor + case ArithOp_xor: return bb->binop(spv::Op::OpLogicalNotEqual , result_type, lhs, rhs); + default: THORIN_UNREACHABLE; + } + } + THORIN_UNREACHABLE; + } + } else if (auto primlit = def->isa()) { + Box box = primlit->value(); + auto type = convert(def->type())->type_id; + SpvId constant; + switch (primlit->primtype_tag()) { + case PrimType_bool: constant = bb->file_builder.bool_constant(type, box.get_bool()); break; + case PrimType_ps8: case PrimType_qs8: assertf(false, "not implemented yet"); + case PrimType_pu8: case PrimType_qu8: assertf(false, "not implemented yet"); + case PrimType_ps16: case PrimType_qs16: assertf(false, "not implemented yet"); + case PrimType_pu16: case PrimType_qu16: assertf(false, "not implemented yet"); + case PrimType_ps32: case PrimType_qs32: constant = bb->file_builder.constant(type, { static_cast(box.get_s32()) }); break; + case PrimType_pu32: case PrimType_qu32: constant = bb->file_builder.constant(type, { static_cast(box.get_u32()) }); break; + case PrimType_ps64: case PrimType_qs64: + case PrimType_pu64: case PrimType_qu64: { + uint64_t value = static_cast(box.get_u64()); + uint64_t upper = value >> 32U; + uint64_t lower = value & 0xFFFFFFFFU; + constant = bb->file_builder.constant(type, { (uint32_t) lower, (uint32_t) upper }); + break; + } + case PrimType_pf16: case PrimType_qf16: assertf(false, "not implemented yet"); + case PrimType_pf32: case PrimType_qf32: assertf(false, "not implemented yet"); + case PrimType_pf64: case PrimType_qf64: assertf(false, "not implemented yet"); + } + return constant; + } else if (auto param = def->isa()) { + if (is_mem(param)) return spv_none; + if (auto param_id = current_fn_->params.lookup(param)) { + assert((*param_id).id != 0); + return *param_id; + } else { + auto val = (*current_fn_->bbs_map[param->continuation()]).phis_map[param].value; + assert(val.id != 0); + return val; + } + } else if (auto variant = def->isa()) { + auto variant_type = def->type()->as(); + auto variant_datatype = (ProductDatatype*) convert(variant_type)->datatype.get(); + auto tag = builder_->u32_constant(variant->index()); + + if (variant_datatype->elements_types.size() > 1) { + auto alloc_type = convert(world().ptr_type(variant_datatype->elements_types[1]->src_type, 1, 4, AddrSpace::Function))->type_id; + auto payload_arr = current_fn_->variable(alloc_type, spv::StorageClassFunction); + auto converted_payload_type = convert(variant_type->op(variant->index())); + + converted_payload_type->datatype->emit_serialization(*bb, spv::StorageClassFunction, payload_arr, bb->file_builder.u32_constant(0), emit(variant->value(), bb)); + auto payload = bb->load(variant_datatype->elements_types[1]->type_id, payload_arr); + + std::vector with_tag = {tag, payload}; + return bb->composite(convert(variant->type())->type_id, with_tag); + } else { + // Zero-sized payload case + std::vector with_tag = { tag }; + return bb->composite(convert(variant->type())->type_id, with_tag); + } + } else if (auto vextract = def->isa()) { + auto variant_type = vextract->value()->type()->as(); + auto variant_datatype = (ProductDatatype*) convert(variant_type)->datatype.get(); + + auto target_type = convert(def->type()); + + assert(variant_datatype->elements_types.size() > 1 && "Can't extract zero-sized datatypes"); + auto alloc_type = convert(world().ptr_type(variant_datatype->elements_types[1]->src_type, 1, 4, AddrSpace::Function))->type_id; + auto payload_arr = current_fn_->variable(alloc_type, spv::StorageClassFunction); + auto payload = bb->extract(variant_datatype->elements_types[1]->type_id, emit(vextract->value(), bb), {1}); + bb->store(payload, payload_arr); + + return target_type->datatype->emit_deserialization(*bb, spv::StorageClassFunction, payload_arr, bb->file_builder.u32_constant(0)); + } else if (auto vindex = def->isa()) { + auto value = emit(vindex->op(0), bb); + return bb->extract(convert(world().type_pu32())->type_id, value, { 0 }); + } else if (auto tuple = def->isa()) { + std::vector elements; + elements.resize(tuple->num_ops()); + size_t x = 0; + for (auto& e : tuple->ops()) { + elements[x++] = emit(e, bb); + } + return bb->composite(convert(tuple->type())->type_id, elements); + } else if (auto structagg = def->isa()) { + std::vector elements; + elements.resize(structagg->num_ops()); + size_t x = 0; + for (auto& e : structagg->ops()) { + elements[x++] = emit(e, bb); + } + return bb->composite(convert(structagg->type())->type_id, elements); + } else if (auto access = def->isa()) { + // emit dependent operations first + emit(access->mem(), bb); + + std::vector operands; + auto ptr_type = access->ptr()->type()->as(); + if (ptr_type->addr_space() == AddrSpace::Global) { + operands.push_back(spv::MemoryAccessAlignedMask); + operands.push_back( 4 ); // TODO: SPIR-V docs say to consult client API for valid values. + } + if (auto load = def->isa()) { + return bb->load(convert(load->out_val_type())->type_id, emit(load->ptr(), bb), operands); + } else if (auto store = def->isa()) { + bb->store(emit(store->val(), bb), emit(store->ptr(), bb), operands); + return spv_none; + } else THORIN_UNREACHABLE; + } else if (auto lea = def->isa()) { + switch (lea->ptr_type()->addr_space()) { + case AddrSpace::Global: + case AddrSpace::Shared: + break; + default: + world().ELOG("LEA is only allowed in global & shared address spaces"); + break; + } + auto type = convert(lea->ptr_type()); + auto offset = emit(lea->index(), bb); + return bb->ptr_access_chain(type->type_id, emit(lea->ptr(), bb), offset, {}); + } else if (auto aggop = def->isa()) { + auto spv_agg = emit(aggop->agg(), bb); + auto agg_type = convert(aggop->agg()->type())->type_id; + + bool mem = false; + if (auto tt = aggop->agg()->type()->isa(); tt && tt->op(0)->isa()) mem = true; + + auto copy_to_alloca = [&] (SpvId target_type) { + world().wdef(def, "slow: alloca and loads/stores needed for aggregate '{}'", def); + auto agg_ptr_type = builder_->declare_ptr_type(spv::StorageClassFunction, agg_type); + + auto variable = bb->fn_builder.variable(agg_ptr_type, spv::StorageClassFunction); + bb->store(spv_agg, variable); + + auto cell_ptr_type = builder_->declare_ptr_type(spv::StorageClassFunction, target_type); + auto cell = bb->access_chain(cell_ptr_type, variable, { emit(aggop->index(), bb)} ); + return std::make_pair(variable, cell); + }; + + if (auto extract = aggop->isa()) { + if (is_mem(extract)) return spv_none; + + auto target_type = convert(extract->type())->type_id; + auto constant_index = aggop->index()->isa(); + + // We have a fast-path: if the index is constant, we can simply use OpCompositeExtract + if (aggop->agg()->type()->isa() && constant_index == nullptr) { + assert(aggop->agg()->type()->isa()); + assert(!is_mem(extract)); + return bb->load(target_type, copy_to_alloca(target_type).second); + } + + if (extract->agg()->type()->isa()) + return bb->vector_extract_dynamic(target_type, spv_agg, emit(extract->index(), bb)); + + // index *must* be constant for the remaining possible cases + assert(constant_index != nullptr); + uint32_t index = constant_index->value().get_u32(); + + unsigned offset = 0; + if (mem) { + if (aggop->agg()->type()->num_ops() == 2) return spv_agg; + offset = 1; + } + + return bb->extract(target_type, spv_agg, { index - offset }); + } else if (auto insert = def->isa()) { + auto value = emit(insert->value(), bb); + auto constant_index = aggop->index()->isa(); + + // TODO deal with mem - but I think for now this case shouldn't happen + + if (insert->agg()->type()->isa() && constant_index == nullptr) { + assert(aggop->agg()->type()->isa()); + auto [variable, cell] = copy_to_alloca(agg_type); + bb->store(value, cell); + return bb->load(agg_type, variable); + } + + if (insert->agg()->type()->isa()) + return bb->vector_insert_dynamic(agg_type, spv_agg, value, emit(insert->index(), bb)); + + // index *must* be constant for the remaining possible cases + assert(constant_index != nullptr); + uint32_t index = constant_index->value().get_u32(); + + return bb->insert(agg_type, value, spv_agg, { index }); + } else THORIN_UNREACHABLE; + } else if (auto conv = def->isa()) { + auto src_type = conv->from()->type(); + auto dst_type = conv->type(); + + auto conv_src_type = convert(src_type); + auto conv_dst_type = convert(dst_type); + + if (auto bitcast = def->isa()) { + if (conv_src_type->datatype->serialized_size() != conv_dst_type->datatype->serialized_size()) + world().ELOG("Source (%) and destination (%) datatypes sizes do not match (% vs % bytes)", src_type->to_string(), dst_type->to_string(), conv_src_type->datatype->serialized_size(), conv_dst_type->datatype->serialized_size()); + + return bb->convert(spv::OpBitcast, convert(bitcast->type())->type_id, emit(bitcast->from(), bb)); + } else if (auto cast = def->isa()) { + // NB: all ops used here are scalar/vector agnostic + auto src_prim = src_type->isa(); + auto dst_prim = dst_type->isa(); + if (!src_prim || !dst_prim || src_prim->length() != dst_prim->length()) + world().ELOG("Illegal cast: % to %, casts are only supported between primitives with identical vector length", src_type->to_string(), dst_type->to_string()); + + auto length = src_prim->length(); + + auto src_kind = classify_primtype(src_prim); + auto dst_kind = classify_primtype(dst_prim); + size_t src_bitwidth = conv_src_type->datatype->serialized_size(); + size_t dst_bitwidth = conv_src_type->datatype->serialized_size(); + + SpvId data = emit(cast->from(), bb); + + // If floating point is involved (src or dst), OpConvert*ToF and OpConvertFTo* can take care of the bit width transformation so no need for any chopping/expanding + if (src_kind == PrimTypeKind::Float || dst_kind == PrimTypeKind::Float) { + auto target_type = convert(get_primtype(world(), dst_kind, dst_bitwidth, length))->type_id; + switch (src_kind) { + case PrimTypeKind::Signed: data = bb->convert(spv::OpConvertSToF, target_type, data); break; + case PrimTypeKind::Unsigned: data = bb->convert(spv::OpConvertUToF, target_type, data); break; + case PrimTypeKind::Float: + switch (dst_kind) { + case PrimTypeKind::Signed: data = bb->convert(spv::OpConvertFToS, target_type, data); break; + case PrimTypeKind::Unsigned: data = bb->convert(spv::OpConvertFToU, target_type, data); break; + default: THORIN_UNREACHABLE; + } + break; + } + } else { + // we expand first and shrink last to minimize precision losses, with bitcast in the middle + bool needs_chopping = src_bitwidth > dst_bitwidth; + bool needs_expanding = src_bitwidth < dst_bitwidth; + + if (needs_expanding) { + auto target_type = convert(get_primtype(world(), src_kind, src_bitwidth, length))->type_id; + switch (src_kind) { + case PrimTypeKind::Signed: + data = bb->convert(spv::OpSConvert, target_type, data); + break; + case PrimTypeKind::Unsigned: + data = bb->convert(spv::OpUConvert, target_type, data); + break; + case PrimTypeKind::Float: + data = bb->convert(spv::OpFConvert, target_type, data); + break; + } + } + + auto expanded_bitwidth = needs_expanding ? dst_bitwidth : src_bitwidth; + auto bitcast_target_type = convert(get_primtype(world(), dst_kind, expanded_bitwidth, length))->type_id; + data = bb->convert(spv::OpBitcast, bitcast_target_type, data); + + if (needs_chopping) { + auto target_type = convert(get_primtype(world(), dst_kind, dst_bitwidth, length))->type_id; + switch (dst_kind) { + case PrimTypeKind::Signed: + data = bb->convert(spv::OpSConvert, target_type, data); + break; + case PrimTypeKind::Unsigned: + data = bb->convert(spv::OpUConvert, target_type, data); + break; + case PrimTypeKind::Float: + data = bb->convert(spv::OpFConvert, target_type, data); + break; + } + } + } + } else THORIN_UNREACHABLE; + } else if (def->isa()) { + return bb->undef(convert(def->type())->type_id); + } + assertf(false, "Incomplete emit(def) definition"); +} + +std::vector CodeGen::emit_builtin(const Continuation* source_cont, const Continuation* builtin, BasicBlockBuilder* bb) { + std::vector productions; + auto uvec3_t = convert(world().type_pu32(3)); + auto u32_t = convert(world().type_pu32()); + auto i32_t = convert(world().type_ps32()); + if (builtin->name() == "spirv.nonsemantic.printf") { + std::vector args; + auto string = source_cont->arg(1); + if (auto arr_type = string->type()->isa(); arr_type->elem_type() == world().type_pu8()) { + auto arr = string->as(); + std::vector the_string; + for (size_t i = 0; i < arr_type->dim(); i++) + the_string.push_back(arr->op(i)->as()->value().get_u8()); + the_string.push_back('\0'); + args.push_back(builder_->debug_string(the_string.data())); + } else world().ELOG("spirv.nonsemantic.printf takes a string literal"); + + for (size_t i = 2; i < source_cont->num_args() - 1; i++) { + args.push_back(emit(source_cont->arg(i), bb)); + } + + bb->ext_instruction(bb->file_builder.void_type, builder_->imported_instrs->shader_printf, 1, args); + } else if (builtin->name() == "get_work_dim") { + THORIN_UNREACHABLE; + } else if (builtin->name() == "get_global_id") { + auto vector = bb->load(uvec3_t->type_id, builder_->builtins->global_id); + auto extracted = bb->vector_extract_dynamic(u32_t->type_id, vector, emit(source_cont->arg(1), bb)); + productions.push_back(bb->convert(spv::OpBitcast, i32_t->type_id, extracted)); + } else if (builtin->name() == "get_local_size") { + auto vector = bb->load(uvec3_t->type_id, builder_->builtins->workgroup_size); + auto extracted = bb->vector_extract_dynamic(u32_t->type_id, vector, emit(source_cont->arg(1), bb)); + productions.push_back(bb->convert(spv::OpBitcast, i32_t->type_id, extracted)); + } else if (builtin->name() == "get_local_id") { + auto vector = bb->load(uvec3_t->type_id, builder_->builtins->local_id); + auto extracted = bb->vector_extract_dynamic(u32_t->type_id, vector, emit(source_cont->arg(1), bb)); + productions.push_back(bb->convert(spv::OpBitcast, i32_t->type_id, extracted)); + } else if (builtin->name() == "get_num_groups") { + auto vector = bb->load(uvec3_t->type_id, builder_->builtins->num_workgroups); + auto extracted = bb->vector_extract_dynamic(u32_t->type_id, vector, emit(source_cont->arg(1), bb)); + productions.push_back(bb->convert(spv::OpBitcast, i32_t->type_id, extracted)); + } else if (builtin->name() == "get_group_id") { + auto vector = bb->load(uvec3_t->type_id, builder_->builtins->workgroup_id); + auto extracted = bb->vector_extract_dynamic(u32_t->type_id, vector, emit(source_cont->arg(1), bb)); + productions.push_back(bb->convert(spv::OpBitcast, i32_t->type_id, extracted)); + } else { + world().ELOG("This spir-v builtin isn't recognised: %s", builtin->name()); + } + return productions; +} + +} diff --git a/src/thorin/be/spirv/spirv.h b/src/thorin/be/spirv/spirv.h new file mode 100644 index 000000000..75ed0bb73 --- /dev/null +++ b/src/thorin/be/spirv/spirv.h @@ -0,0 +1,186 @@ +#ifndef THORIN_SPIRV_H +#define THORIN_SPIRV_H + +#include "thorin/be/spirv/spirv_builder.hpp" +#include "thorin/be/codegen.h" + +namespace thorin::spirv { + +using SpvId = builder::SpvId; + +class CodeGen; +struct Datatype; +struct PtrDatatype; + +struct FileBuilder; +struct FnBuilder; + +struct ConvertedType { + ConvertedType(CodeGen* cg) : code_gen(cg) {} + ConvertedType(const ConvertedType&) = delete; + + spirv::CodeGen* code_gen; + const thorin::Type* src_type; + SpvId type_id { 0 }; + std::unique_ptr datatype; + + bool is_known_size() { return datatype != nullptr; } +}; + +struct BasicBlockBuilder : public builder::SpvBasicBlockBuilder { + explicit BasicBlockBuilder(FnBuilder& fn_builder); + BasicBlockBuilder(const BasicBlockBuilder&) = delete; + + FnBuilder& fn_builder; + FileBuilder& file_builder; + std::unordered_map phis_map; + DefMap args; +}; + +struct FnBuilder : public builder::SpvFnBuilder { + explicit FnBuilder(CodeGen* cg, FileBuilder& file_builder); + FnBuilder(const FnBuilder&) = delete; + + CodeGen* cg; + FileBuilder& file_builder; + + const Scope* scope = nullptr; + std::vector> bbs; + std::unordered_map bbs_map; + ContinuationMap labels; + DefMap params; +}; + +struct Builtins { + SpvId workgroup_size; + SpvId num_workgroups; + SpvId workgroup_id; + SpvId local_id; + SpvId global_id; + SpvId local_invocation_index; + + explicit Builtins(FileBuilder&); +}; + +struct ImportedInstructions { + SpvId shader_printf; + + explicit ImportedInstructions(FileBuilder&); +}; + +struct FileBuilder : public builder::SpvFileBuilder { + explicit FileBuilder(CodeGen* cg); + FileBuilder(const FileBuilder&) = delete; + + CodeGen* cg; + + std::unique_ptr builtins; + std::unique_ptr imported_instrs; + + SpvId u32_t(); + SpvId u32_constant(uint32_t); + +private: + SpvId u32_t_ { 0 }; + /*SpvId i32_t; + SpvId u32_t; + SpvId i64_t; + SpvId u64_t; + SpvId i32_constant(int32_t); + SpvId i64_constant(int64_t); + SpvId u64_constant(uint64_t);*/ +}; + +class CodeGen : public thorin::CodeGen { +public: + CodeGen(World&, Cont2Config&, bool debug); + + void emit_stream(std::ostream& stream) override; + const char* file_ext() const override { return ".spv"; } + + ConvertedType* convert(const Type*); +protected: + void structure_loops(); + void structure_flow(); + + void emit(const Scope& scope); + void emit_epilogue(Continuation*, BasicBlockBuilder* bb); + SpvId emit(const Def* def, BasicBlockBuilder* bb); + std::vector emit_builtin(const Continuation*, const Continuation*, BasicBlockBuilder*); + + SpvId get_codom_type(const Continuation* fn); + + std::unique_ptr builder_; + Continuation* entry_ = nullptr; + FnBuilder* current_fn_ = nullptr; + DefMap> types_; + DefMap defs_; + const Cont2Config& kernel_config_; + + friend PtrDatatype; +}; + +/// Thorin data types are mapped to SPIR-V in non-trivial ways, this interface is used by the emission code to abstract over +/// potentially different mappings, depending on the capabilities of the target platform. The serdes code deals with pointers +/// in arrays of unsigned 32 bit words, and is there to get around the limitation of not being able to bitcast pointers in the +/// logical addressing mode. +struct Datatype { +public: + ConvertedType* type; + Datatype(ConvertedType* type) : type(type) {} + + // Datatypes are serialized using a base element, for now it is hardcoded to use 32-bit scalar unsigned integers + static constexpr size_t base_element_bitwidth = 32; + static constexpr size_t base_element_bytes = base_element_bitwidth / 8; + + virtual size_t serialized_size() = 0; + virtual SpvId emit_deserialization(BasicBlockBuilder& bb, spv::StorageClass storage_class, SpvId array, SpvId base_offset) = 0; + virtual void emit_serialization(BasicBlockBuilder& bb, spv::StorageClass storage_class, SpvId array, SpvId base_offset, SpvId data) = 0; +}; + +/// For scalar datatypes +struct ScalarDatatype : public Datatype { + int type_tag; + size_t size_in_bytes; + size_t alignment; + ScalarDatatype(ConvertedType* type, int type_tag, size_t size_in_bytes, size_t alignment_in_bytes); + + size_t serialized_size() override { return (size_in_bytes + 3) / 4; }; + SpvId emit_deserialization(BasicBlockBuilder& bb, spv::StorageClass storage_class, SpvId array, SpvId base_offset) override; + void emit_serialization(BasicBlockBuilder& bb, spv::StorageClass storage_class, SpvId array, SpvId base_offset, SpvId data) override; +}; + +struct PtrDatatype : public Datatype { + static constexpr size_t bitwidth = 64; + PtrDatatype(ConvertedType* type) : Datatype(type) {} + + size_t serialized_size() override { return bitwidth / 32; }; + SpvId emit_deserialization(BasicBlockBuilder& bb, spv::StorageClass storage_class, SpvId array, SpvId base_offset) override; + void emit_serialization(BasicBlockBuilder& bb, spv::StorageClass storage_class, SpvId array, SpvId base_offset, SpvId data) override; +}; + +struct DefiniteArrayDatatype : public Datatype { + ConvertedType* element_type; + size_t length; + + DefiniteArrayDatatype(ConvertedType* type, ConvertedType* element_type, size_t length); + + size_t serialized_size() override { return element_type->datatype->serialized_size(); }; + SpvId emit_deserialization(BasicBlockBuilder& bb, spv::StorageClass storage_class, SpvId array, SpvId base_offset) override; + void emit_serialization(BasicBlockBuilder& bb, spv::StorageClass storage_class, SpvId array, SpvId base_offset, SpvId data) override; +}; + +struct ProductDatatype : public Datatype { + std::vector elements_types; + size_t total_size = 0; + + ProductDatatype(ConvertedType* type, const std::vector&& elements_types); + + size_t serialized_size() override { return total_size; }; + SpvId emit_deserialization(BasicBlockBuilder& bb, spv::StorageClass storage_class, SpvId array, SpvId base_offset) override; + void emit_serialization(BasicBlockBuilder& bb, spv::StorageClass storage_class, SpvId array, SpvId base_offset, SpvId data) override; +}; + +} + +#endif //THORIN_SPIRV_H diff --git a/src/thorin/be/spirv/spirv_builder.hpp b/src/thorin/be/spirv/spirv_builder.hpp new file mode 100644 index 000000000..238343e79 --- /dev/null +++ b/src/thorin/be/spirv/spirv_builder.hpp @@ -0,0 +1,669 @@ +#include + +#include +#include +#include +#include +#include +#include + +namespace thorin::spirv::builder { + +struct SpvId { uint32_t id; }; + +struct SpvSectionBuilder; +struct SpvBasicBlockBuilder; +struct SpvFnBuilder; +struct SpvFileBuilder; + +inline int div_roundup(int a, int b) { + if (a % b == 0) + return a / b; + else + return (a / b) + 1; +} + +struct SpvSectionBuilder { + std::vector data_; + +private: + void output_word(uint32_t word) { + data_.push_back(word); + } +public: + void op(spv::Op op, int ops_size) { + uint32_t lower = op & 0xFFFFu; + uint32_t upper = (ops_size << 16) & 0xFFFF0000u; + output_word(lower | upper); + } + + void ref_id(SpvId id) { + assert(id.id != 0); + output_word(id.id); + } + + void literal_name(std::string_view str) { + int i = 0; + uint32_t cword = 0; + for (char c : str) { + cword = cword | (c & 0xFF) << (i * 8); + i++; + if (i == 4) { + output_word(cword); + cword = 0; + i = 0; + } + } + output_word(cword); + } + + void literal_int(uint32_t i) { + output_word(i); + } +}; + +struct SpvBasicBlockBuilder : public SpvSectionBuilder { + explicit SpvBasicBlockBuilder(SpvFileBuilder& file_builder) + : file_builder(file_builder) + {} + + SpvFileBuilder& file_builder; + + struct Phi { + SpvId type; + SpvId value; + std::vector> preds; + }; + std::vector phis; + SpvId label; + + SpvId undef(SpvId type) { + op(spv::Op::OpUndef, 3); + ref_id(type); + auto id = generate_fresh_id(); + ref_id(id); + return id; + } + + SpvId composite(SpvId aggregate_t, std::vector& elements) { + op(spv::Op::OpCompositeConstruct, 3 + elements.size()); + ref_id(aggregate_t); + auto id = generate_fresh_id(); + ref_id(id); + for (auto e : elements) + ref_id(e); + return id; + } + + SpvId extract(SpvId target_type, SpvId composite, std::vector indices) { + op(spv::Op::OpCompositeExtract, 4 + indices.size()); + ref_id(target_type); + auto id = generate_fresh_id(); + ref_id(id); + ref_id(composite); + for (auto i : indices) + literal_int(i); + return id; + } + + SpvId insert(SpvId target_type, SpvId object, SpvId composite, std::vector indices) { + op(spv::Op::OpCompositeInsert, 5 + indices.size()); + ref_id(target_type); + auto id = generate_fresh_id(); + ref_id(id); + ref_id(object); + ref_id(composite); + for (auto i : indices) + literal_int(i); + return id; + } + + SpvId vector_extract_dynamic(SpvId target_type, SpvId vector, SpvId index) { + op(spv::Op::OpVectorExtractDynamic, 5); + ref_id(target_type); + auto id = generate_fresh_id(); + ref_id(id); + ref_id(vector); + ref_id(index); + return id; + } + + SpvId vector_insert_dynamic(SpvId target_type, SpvId vector, SpvId component, SpvId index) { + op(spv::Op::OpVectorInsertDynamic, 6); + ref_id(target_type); + auto id = generate_fresh_id(); + ref_id(id); + ref_id(vector); + ref_id(component); + ref_id(index); + return id; + } + + // Used for almost all conversion operations + SpvId convert(spv::Op op_, SpvId target_type, SpvId value) { + op(op_, 4); + auto id = generate_fresh_id(); + ref_id(target_type); + ref_id(id); + ref_id(value); + return id; + } + + SpvId access_chain(SpvId target_type, SpvId element, std::vector indexes) { + op(spv::Op::OpAccessChain, 4 + indexes.size()); + auto id = generate_fresh_id(); + ref_id(target_type); + ref_id(id); + ref_id(element); + for (auto index : indexes) + ref_id(index); + return id; + } + + SpvId ptr_access_chain(SpvId target_type, SpvId base, SpvId element, std::vector indexes) { + op(spv::Op::OpPtrAccessChain, 5 + indexes.size()); + auto id = generate_fresh_id(); + ref_id(target_type); + ref_id(id); + ref_id(base); + ref_id(element); + for (auto index : indexes) + ref_id(index); + return id; + } + + SpvId load(SpvId target_type, SpvId pointer, std::vector operands = {}) { + op(spv::Op::OpLoad, 4 + operands.size()); + auto id = generate_fresh_id(); + ref_id(target_type); + ref_id(id); + ref_id(pointer); + for (auto op : operands) + literal_int(op); + return id; + } + + void store(SpvId value, SpvId pointer, std::vector operands = {}) { + op(spv::Op::OpStore, 3 + operands.size()); + ref_id(pointer); + ref_id(value); + for (auto op : operands) + literal_int(op); + } + + SpvId binop(spv::Op op_, SpvId result_type, SpvId lhs, SpvId rhs) { + op(op_, 5); + auto id = generate_fresh_id(); + ref_id(result_type); + ref_id(id); + ref_id(lhs); + ref_id(rhs); + return id; + } + + void branch(SpvId target) { + op(spv::Op::OpBranch, 2); + ref_id(target); + } + + void branch_conditional(SpvId condition, SpvId true_target, SpvId false_target) { + op(spv::Op::OpBranchConditional, 4); + ref_id(condition); + ref_id(true_target); + ref_id(false_target); + } + + void selection_merge(SpvId merge_bb, spv::SelectionControlMask selection_control) { + op(spv::Op::OpSelectionMerge, 3); + ref_id(merge_bb); + literal_int(selection_control); + } + + void loop_merge(SpvId merge_bb, SpvId continue_bb, spv::LoopControlMask loop_control, std::vector loop_control_ops) { + op(spv::Op::OpLoopMerge, 4 + loop_control_ops.size()); + ref_id(merge_bb); + ref_id(continue_bb); + literal_int(loop_control); + + for (auto e : loop_control_ops) + literal_int(e); + } + + SpvId call(SpvId return_type, SpvId callee, std::vector arguments) { + op(spv::Op::OpFunctionCall, 4 + arguments.size()); + auto id = generate_fresh_id(); + ref_id(return_type); + ref_id(id); + ref_id(callee); + + for (auto a : arguments) + ref_id(a); + return id; + } + + SpvId ext_instruction(SpvId return_type, SpvId set, uint32_t instruction, std::vector arguments) { + op(spv::Op::OpExtInst, 5 + arguments.size()); + auto id = generate_fresh_id(); + ref_id(return_type); + ref_id(id); + ref_id(set); + literal_int(instruction); + for (auto a : arguments) + ref_id(a); + return id; + } + + void return_void() { + op(spv::Op::OpReturn, 1); + } + + void return_value(SpvId value) { + op(spv::Op::OpReturnValue, 2); + ref_id(value); + } + + void unreachable() { + op(spv::Op::OpUnreachable, 1); + } + +private: + SpvId generate_fresh_id(); +}; + +struct SpvFnBuilder { + explicit SpvFnBuilder(SpvFileBuilder* file_builder) + : file_builder(file_builder) + { + function_id = generate_fresh_id(); + } + + SpvFileBuilder* file_builder; + SpvId function_id; + + SpvId fn_type; + SpvId fn_ret_type; + std::vector bbs_to_emit; + + // Contains OpFunctionParams + SpvSectionBuilder header; + + SpvSectionBuilder variables; + + SpvId parameter(SpvId param_type) { + header.op(spv::Op::OpFunctionParameter, 3); + auto id = generate_fresh_id(); + header.ref_id(param_type); + header.ref_id(id); + return id; + } + + SpvId variable(SpvId type, spv::StorageClass storage_class) { + variables.op(spv::Op::OpVariable, 4); + variables.ref_id(type); + auto id = generate_fresh_id(); + variables.ref_id(id); + variables.literal_int(storage_class); + return id; + } + +private: + SpvId generate_fresh_id(); +}; + +struct SpvFileBuilder { + + enum UniqueDeclTag { + NONE, + FN_TYPE, + PTR_TYPE, + DEF_ARR_TYPE, + CONSTANT, + CONSTANT_COMPOSITE, + }; + + /// Prevents duplicate declarations + struct UniqueDeclKey { + UniqueDeclTag tag; + std::vector members; + + bool operator==(const UniqueDeclKey &b) const { + return tag == b.tag && members == b.members; + } + }; + + struct UniqueDeclKeyHasher { + size_t operator() (const UniqueDeclKey& key) const { + size_t acc = 0; + for (auto id : key.members) + acc ^= std::hash{}(id); + return std::hash{}(key.tag) ^ acc; + } + }; + + SpvFileBuilder() + : void_type(declare_void_type()) + {} + SpvFileBuilder(const SpvFileBuilder&) = delete; + + SpvId generate_fresh_id() { return { bound++ }; } + + void name(SpvId id, std::string_view str) { + assert(id.id < bound); + debug_names.op(spv::Op::OpName, 2 + div_roundup(str.size() + 1, 4)); + debug_names.ref_id(id); + debug_names.literal_name(str); + } + + SpvId declare_bool_type() { + types_constants.op(spv::Op::OpTypeBool, 2); + auto id = generate_fresh_id(); + types_constants.ref_id(id); + return id; + } + + SpvId declare_int_type(int width, bool signed_) { + types_constants.op(spv::Op::OpTypeInt, 4); + auto id = generate_fresh_id(); + types_constants.ref_id(id); + types_constants.literal_int(width); + types_constants.literal_int(signed_ ? 1 : 0); + return id; + } + + SpvId declare_float_type(int width) { + types_constants.op(spv::Op::OpTypeFloat, 3); + auto id = generate_fresh_id(); + types_constants.ref_id(id); + types_constants.literal_int(width); + return id; + } + + SpvId declare_ptr_type(spv::StorageClass storage_class, SpvId element_type) { + auto key = UniqueDeclKey { PTR_TYPE, { element_type.id, (uint32_t) storage_class } }; + if (auto iter = unique_decls.find(key); iter != unique_decls.end()) return iter->second; + types_constants.op(spv::Op::OpTypePointer, 4); + auto id = generate_fresh_id(); + types_constants.ref_id(id); + types_constants.literal_int(storage_class); + types_constants.ref_id(element_type); + unique_decls[key] = id; + return id; + } + + SpvId declare_array_type(SpvId element_type, SpvId dim) { + auto key = UniqueDeclKey { DEF_ARR_TYPE, { element_type.id, dim.id } }; + if (auto iter = unique_decls.find(key); iter != unique_decls.end()) return iter->second; + types_constants.op(spv::Op::OpTypeArray, 4); + auto id = generate_fresh_id(); + types_constants.ref_id(id); + types_constants.ref_id(element_type); + types_constants.ref_id(dim); + unique_decls[key] = id; + return id; + } + + SpvId declare_fn_type(std::vector dom, SpvId codom) { + auto key = UniqueDeclKey { FN_TYPE, {} }; + for (auto d : dom) key.members.push_back(d.id); + key.members.push_back(codom.id); + if (auto iter = unique_decls.find(key); iter != unique_decls.end()) return iter->second; + + types_constants.op(spv::Op::OpTypeFunction, 3 + dom.size()); + auto id = generate_fresh_id(); + types_constants.ref_id(id); + types_constants.ref_id(codom); + for (auto arg : dom) + types_constants.ref_id(arg); + unique_decls[key] = id; + return id; + } + + SpvId declare_struct_type(std::vector elements) { + types_constants.op(spv::Op::OpTypeStruct, 2 + elements.size()); + auto id = generate_fresh_id(); + types_constants.ref_id(id); + for (auto arg : elements) + types_constants.ref_id(arg); + return id; + } + + SpvId declare_vector_type(SpvId component_type, uint32_t dim) { + types_constants.op(spv::Op::OpTypeVector, 4); + auto id = generate_fresh_id(); + types_constants.ref_id(id); + types_constants.ref_id(component_type); + types_constants.literal_int(dim); + return id; + } + + void decorate(SpvId target, spv::Decoration decoration, std::vector extra = {}) { + annotations.op(spv::Op::OpDecorate, 3 + extra.size()); + annotations.ref_id(target); + annotations.literal_int(decoration); + for (auto e : extra) + annotations.literal_int(e); + } + + void decorate_member(SpvId target, uint32_t member, spv::Decoration decoration, std::vector extra = {}) { + annotations.op(spv::Op::OpMemberDecorate, 4 + extra.size()); + annotations.ref_id(target); + annotations.literal_int(member); + annotations.literal_int(decoration); + for (auto e : extra) + annotations.literal_int(e); + } + + SpvId debug_string(std::string string) { + debug_string_source.op(spv::Op::OpString, 2 + div_roundup(string.size() + 1, 4)); + auto id = generate_fresh_id(); + debug_string_source.ref_id(id); + debug_string_source.literal_name(string); + return id; + } + + SpvId bool_constant(SpvId type, bool value) { + types_constants.op(value ? spv::Op::OpConstantTrue : spv::Op::OpConstantFalse, 3); + auto id = generate_fresh_id(); + types_constants.ref_id(type); + types_constants.ref_id(id); + return id; + } + + SpvId constant(SpvId type, std::vector bit_pattern) { + auto key = UniqueDeclKey { CONSTANT, bit_pattern }; + key.members.push_back(type.id); + if (auto iter = unique_decls.find(key); iter != unique_decls.end()) return iter->second; + types_constants.op(spv::Op::OpConstant, 3 + bit_pattern.size()); + auto id = generate_fresh_id(); + types_constants.ref_id(type); + types_constants.ref_id(id); + for (auto arg : bit_pattern) + types_constants.literal_int(arg); + unique_decls[key] = id; + return id; + } + + SpvId constant_composite(SpvId type, std::vector ops) { + auto key = UniqueDeclKey { CONSTANT_COMPOSITE, {} }; + key.members.push_back(type.id); + for (auto op : ops) key.members.push_back(op.id); + if (auto iter = unique_decls.find(key); iter != unique_decls.end()) return iter->second; + types_constants.op(spv::Op::OpConstantComposite, 3 + ops.size()); + auto id = generate_fresh_id(); + types_constants.ref_id(type); + types_constants.ref_id(id); + for (auto op : ops) + types_constants.ref_id(op); + unique_decls[key] = id; + return id; + } + + SpvId variable(SpvId type, spv::StorageClass storage_class) { + types_constants.op(spv::Op::OpVariable, 4); + types_constants.ref_id(type); + auto id = generate_fresh_id(); + types_constants.ref_id(id); + types_constants.literal_int(storage_class); + return id; + } + + SpvId define_function(SpvFnBuilder& fn_builder) { + fn_defs.op(spv::Op::OpFunction, 5); + fn_defs.ref_id(fn_builder.fn_ret_type); + fn_defs.ref_id(fn_builder.function_id); + fn_defs.data_.push_back(spv::FunctionControlMaskNone); + fn_defs.ref_id(fn_builder.fn_type); + + // Includes stuff like OpFunctionParameters + for (auto w : fn_builder.header.data_) + fn_defs.data_.push_back(w); + + bool first = true; + for (auto& bb : fn_builder.bbs_to_emit) { + fn_defs.op(spv::Op::OpLabel, 2); + fn_defs.ref_id(bb->label); + + if (first) { + for (auto w : fn_builder.variables.data_) + fn_defs.data_.push_back(w); + first = false; + } + + for (auto& phi : bb->phis) { + fn_defs.op(spv::Op::OpPhi, 3 + 2 * phi->preds.size()); + fn_defs.ref_id(phi->type); + fn_defs.ref_id(phi->value); + assert(!phi->preds.empty()); + for (auto& [pred_value, pred_label] : phi->preds) { + fn_defs.ref_id(pred_value); + fn_defs.ref_id(pred_label); + } + } + + for (auto w : bb->data_) + fn_defs.data_.push_back(w); + } + + fn_defs.op(spv::Op::OpFunctionEnd, 1); + return fn_builder.function_id; + } + + void declare_entry_point(spv::ExecutionModel execution_model, SpvId entry_point, std::string name, std::vector interface) { + entry_points.op(spv::Op::OpEntryPoint, 3 + div_roundup(name.size() + 1, 4) + interface.size()); + entry_points.literal_int(execution_model); + entry_points.ref_id(entry_point); + entry_points.literal_name(name); + for (auto i : interface) + entry_points.ref_id(i); + } + + void execution_mode(SpvId entry_point, spv::ExecutionMode execution_mode, std::vector payloads) { + entry_points.op(spv::Op::OpExecutionMode, 3 + payloads.size()); + entry_points.ref_id(entry_point); + entry_points.literal_int(execution_mode); + for (auto d : payloads) + entry_points.literal_int(d); + } + + void capability(spv::Capability cap) { + capabilities.op(spv::Op::OpCapability, 2); + capabilities.data_.push_back(cap); + } + + void extension(std::string name) { + extensions.op(spv::Op::OpExtension, 1 + div_roundup(name.size() + 1, 4)); + extensions.literal_name(name); + } + + SpvId extended_import(std::string name) { + ext_inst_import.op(spv::Op::OpExtInstImport, 2 + div_roundup(name.size() + 1, 4)); + auto id = generate_fresh_id(); + ext_inst_import.ref_id(id); + ext_inst_import.literal_name(name); + return id; + } + + spv::AddressingModel addressing_model = spv::AddressingModel::AddressingModelLogical; + spv::MemoryModel memory_model = spv::MemoryModel::MemoryModelSimple; + +private: + std::ostream* output_ = nullptr; + uint32_t bound = 1; + + // Ordered as per https://www.khronos.org/registry/spir-v/specs/unified1/SPIRV.pdf#subsection.2.4 + SpvSectionBuilder capabilities; + SpvSectionBuilder extensions; + SpvSectionBuilder ext_inst_import; + SpvSectionBuilder entry_points; + SpvSectionBuilder execution_modes; + SpvSectionBuilder debug_string_source; + SpvSectionBuilder debug_names; + SpvSectionBuilder debug_module_processed; + SpvSectionBuilder annotations; + SpvSectionBuilder types_constants; + SpvSectionBuilder fn_decls; + SpvSectionBuilder fn_defs; + + // SPIR-V disallows duplicate non-aggregate type declarations, we protect against these with this + std::unordered_map unique_decls; + + SpvId declare_void_type() { + types_constants.op(spv::Op::OpTypeVoid, 2); + auto id = generate_fresh_id(); + types_constants.ref_id(id); + return id; + } + + void output_word_le(uint32_t word) { + output_->put((word >> 0) & 0xFFu); + output_->put((word >> 8) & 0xFFu); + output_->put((word >> 16) & 0xFFu); + output_->put((word >> 24) & 0xFFu); + } + + void output_section(SpvSectionBuilder& section) { + for (auto& word : section.data_) { + output_word_le(word); + } + } +public: + const SpvId void_type; + + void finish(std::ostream& output) { + output_ = &output; + SpvSectionBuilder memory_model_section; + memory_model_section.op(spv::Op::OpMemoryModel, 3); + memory_model_section.data_.push_back(addressing_model); + memory_model_section.data_.push_back(memory_model); + + output_word_le(spv::MagicNumber); + output_word_le(spv::Version); // TODO: target a specific spirv version + output_word_le(uint32_t(0)); // TODO get a magic number ? + output_word_le(bound); + output_word_le(uint32_t(0)); // instruction schema padding + + output_section(capabilities); + output_section(extensions); + output_section(ext_inst_import); + output_section(memory_model_section); + output_section(entry_points); + output_section(execution_modes); + output_section(debug_string_source); + output_section(debug_names); + output_section(debug_module_processed); + output_section(annotations); + output_section(types_constants); + output_section(fn_decls); + output_section(fn_defs); + } +}; + +inline SpvId SpvBasicBlockBuilder::generate_fresh_id() { + return file_builder.generate_fresh_id(); +} + +inline SpvId SpvFnBuilder::generate_fresh_id() { + return file_builder->generate_fresh_id(); +} + +} From cfeb4d5e8e235fea57bfceeba8163c7ce526965a Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Thu, 27 Jun 2024 15:01:26 +0200 Subject: [PATCH 226/342] spirv: API updates --- src/thorin/be/spirv/spirv.cpp | 90 ++++++++++++++++++----------------- src/thorin/be/spirv/spirv.h | 6 +-- 2 files changed, 49 insertions(+), 47 deletions(-) diff --git a/src/thorin/be/spirv/spirv.cpp b/src/thorin/be/spirv/spirv.cpp index 3b2d0e146..0f0ffb9c6 100644 --- a/src/thorin/be/spirv/spirv.cpp +++ b/src/thorin/be/spirv/spirv.cpp @@ -142,8 +142,8 @@ ImportedInstructions::ImportedInstructions(FileBuilder& builder) { shader_printf = builder.extended_import("NonSemantic.DebugPrintf"); } -CodeGen::CodeGen(thorin::World& world, Cont2Config& kernel_config, bool debug) - : thorin::CodeGen(world, debug), kernel_config_(kernel_config) +CodeGen::CodeGen(Thorin& thorin, Cont2Config& kernel_config, bool debug) + : thorin::CodeGen(thorin, debug), kernel_config_(kernel_config) {} void CodeGen::emit_stream(std::ostream& out) { @@ -156,7 +156,7 @@ void CodeGen::emit_stream(std::ostream& out) { structure_flow(); // cleanup_world(world()); - Scope::for_each(world(), [&](const Scope& scope) { emit(scope); }); + ScopesForest(world()).for_each([&](const Scope& scope) { emit(scope); }); auto push_constant_arr_type = convert(world().definite_array_type(world().type_pu32(), 128))->type_id; auto push_constant_struct_type = builder_->declare_struct_type({ push_constant_arr_type }); @@ -169,7 +169,7 @@ void CodeGen::emit_stream(std::ostream& out) { builder_->name(push_constant_struct_ptr, "thorin_push_constant_data"); auto entry_pt_signature = builder_->declare_fn_type({}, builder_->void_type); - for (auto& cont : world().continuations()) { + for (auto& cont : world().copy_continuations()) { if (cont->is_exported()) { assert(defs_.contains(cont) && kernel_config_.contains(cont)); auto config = kernel_config_.find(cont); @@ -207,7 +207,7 @@ void CodeGen::emit_stream(std::ostream& out) { for (size_t i = 0; i < cont->num_params(); i++) { auto param = cont->param(i); auto param_type = param->type(); - if (param_type == world().unit() || param_type == world().mem_type() || param_type->isa()) continue; + if (param_type == world().unit_type() || param_type == world().mem_type() || param_type->isa()) continue; assert(param_type->order() == 0); auto converted = convert(param_type); assert(converted->datatype != nullptr); @@ -320,7 +320,7 @@ SpvId CodeGen::get_codom_type(const Continuation* fn) { auto ret_cont_type = fn->ret_param()->type(); std::vector types; for (auto& op : ret_cont_type->ops()) { - if (op->isa() || is_type_unit(op)) + if (op->isa() || is_type_unit(op->type())) continue; assert(op->order() == 0); types.push_back(convert(op)->type_id); @@ -346,10 +346,12 @@ void CodeGen::emit_epilogue(Continuation* continuation, BasicBlockBuilder* bb) { } }; - if (continuation->callee() == entry_->ret_param()) { + auto& app = *continuation->body(); + + if (app.callee() == entry_->ret_param()) { std::vector values; - for (auto arg : continuation->args()) { + for (auto arg : app.args()) { assert(arg->order() == 0); auto val = emit(arg, bb); if (is_mem(arg) || is_unit(arg)) @@ -362,19 +364,19 @@ void CodeGen::emit_epilogue(Continuation* continuation, BasicBlockBuilder* bb) { case 1: bb->return_value(values[0]); break; default: bb->return_value(bb->composite(current_fn_->fn_ret_type, values)); } - } else if (auto callee = continuation->callee()->isa_continuation(); callee && callee->is_basicblock()) { // ordinary jump + } else if (auto dst_cont = app.callee()->isa_nom(); dst_cont && dst_cont->is_basicblock()) { // ordinary jump int index = -1; - for (auto& arg : continuation->args()) { + for (auto& arg : app.args()) { index++; auto val = emit(arg, bb); if (is_mem(arg) || is_unit(arg)) continue; bb->args[arg] = val; - auto* param = callee->param(index); - auto& phi = current_fn_->bbs_map[callee]->phis_map[param]; + auto* param = dst_cont->param(index); + auto& phi = current_fn_->bbs_map[dst_cont]->phis_map[param]; phi.preds.emplace_back(bb->args[arg], current_fn_->labels[continuation]); } - bb->branch(current_fn_->labels[callee]); - } else if (continuation->callee() == world().branch()) { + bb->branch(current_fn_->labels[dst_cont]); + } else if (app.callee() == world().branch()) { auto& domtree = current_fn_->scope->b_cfg().domtree(); auto merge_cont = domtree.idom(current_fn_->scope->f_cfg().operator[](continuation))->continuation(); SpvId merge_bb; @@ -389,24 +391,24 @@ void CodeGen::emit_epilogue(Continuation* continuation, BasicBlockBuilder* bb) { merge_bb = current_fn_->labels[merge_cont]; } - auto cond = emit(continuation->arg(0), bb); - bb->args.emplace(continuation->arg(0), cond); - auto tbb = current_fn_->labels[continuation->arg(1)->as_continuation()]; - auto fbb = current_fn_->labels[continuation->arg(2)->as_continuation()]; + auto cond = emit(app.arg(0), bb); + bb->args.emplace(app.arg(0), cond); + auto tbb = current_fn_->labels[app.arg(1)->isa_nom()]; + auto fbb = current_fn_->labels[app.arg(2)->isa_nom()]; bb->selection_merge(merge_bb,spv::SelectionControlMaskNone); bb->branch_conditional(cond, tbb, fbb); - } else if (continuation->callee()->isa() && continuation->callee()->as()->intrinsic() == Intrinsic::Match) { + } else if (app.callee()->isa() && app.callee()->as()->intrinsic() == Intrinsic::Match) { /*auto val = emit(continuation->arg(0)); - auto otherwise_bb = cont2bb(continuation->arg(1)->as_continuation()); + auto otherwise_bb = cont2bb(continuation->arg(1)->isa_nom()); auto match = irbuilder.CreateSwitch(val, otherwise_bb, continuation->num_args() - 2); for (size_t i = 2; i < continuation->num_args(); i++) { auto arg = continuation->arg(i)->as(); auto case_const = llvm::cast(emit(arg->op(0))); - auto case_bb = cont2bb(arg->op(1)->as_continuation()); + auto case_bb = cont2bb(arg->op(1)->isa_nom()); match->addCase(case_const, case_bb); }*/ THORIN_UNREACHABLE; - } else if (continuation->callee()->isa()) { + } else if (app.callee()->isa()) { bb->unreachable(); } else if (continuation->intrinsic() == Intrinsic::SCFLoopHeader) { auto merge_label = current_fn_->bbs_map[const_cast(continuation->attributes_.scf_metadata.loop_header.merge_target)]->label; @@ -425,7 +427,7 @@ void CodeGen::emit_epilogue(Continuation* continuation, BasicBlockBuilder* bb) { // TODO handle dispatching to multiple targets assert(targets == 1); - auto dispatch_target = continuation->op(0)->as_continuation(); + auto dispatch_target = continuation->op(0)->isa_nom(); // Extract the relevant variant & expand the tuple if necessary auto arg = world().variant_extract(continuation->param(0), 0); auto extracted = emit(arg, dispatch_bb); @@ -441,7 +443,7 @@ void CodeGen::emit_epilogue(Continuation* continuation, BasicBlockBuilder* bb) { dispatch_bb->branch(current_fn_->bbs_map[dispatch_target]->label); } else if (continuation->intrinsic() == Intrinsic::SCFLoopContinue) { - auto loop_header = continuation->op(0)->as_continuation(); + auto loop_header = continuation->op(0)->isa_nom(); auto header_label = current_fn_->bbs_map[loop_header]->label; auto arg = continuation->param(0); @@ -458,28 +460,28 @@ void CodeGen::emit_epilogue(Continuation* continuation, BasicBlockBuilder* bb) { // TODO handle dispatching to multiple targets assert(targets == 1); - auto callee = continuation->op(0)->as_continuation(); + auto callee = continuation->op(0)->isa_nom(); // TODO phis bb->branch(current_fn_->bbs_map[callee]->label); - } else if (auto builtin = continuation->callee()->isa_continuation(); builtin->is_imported()) { + } else if (auto builtin = app.callee()->isa_nom(); builtin->is_imported()) { // Ensure we emit previous memory operations - assert(is_mem(continuation->arg(0))); - emit(continuation->arg(0), bb); + assert(is_mem(app.arg(0))); + emit(app.arg(0), bb); - auto productions = emit_builtin(continuation, builtin, bb); - auto succ = continuation->args().back()->as_continuation(); + auto productions = emit_builtin(app, builtin, bb); + auto succ = app.args().back()->isa_nom(); jump_to_next_cont_with_args(succ, productions); - } else if (auto intrinsic = continuation->callee()->isa_continuation(); callee && callee->is_intrinsic()) { + } else if (auto intrinsic = app.callee()->isa_nom(); intrinsic && intrinsic->is_intrinsic()) { THORIN_UNREACHABLE; } else { // function/closure call // put all first-order args into an array std::vector call_args; const Def* ret_arg = nullptr; - for (auto arg : continuation->args()) { + for (auto arg : app.args()) { if (arg->order() == 0) { auto arg_type = arg->type(); auto arg_val = emit(arg, bb); - if (arg_type == world().unit() || arg_type == world().mem_type()) continue; + if (arg_type == world().unit_type() || arg_type == world().mem_type()) continue; call_args.push_back(arg_val); } else { assert(!ret_arg); @@ -490,7 +492,7 @@ void CodeGen::emit_epilogue(Continuation* continuation, BasicBlockBuilder* bb) { auto ret_type = get_codom_type(continuation); SpvId call_result; - if (auto called_continuation = continuation->callee()->isa_continuation()) { + if (auto called_continuation = app.callee()->isa_nom()) { call_result = bb->call(ret_type, emit(called_continuation, bb), call_args); } else { // must be a closure @@ -502,7 +504,7 @@ void CodeGen::emit_epilogue(Continuation* continuation, BasicBlockBuilder* bb) { } // must be call + continuation --- call + return has been removed by codegen_prepare - auto succ = ret_arg->as_continuation(); + auto succ = ret_arg->isa_nom(); size_t n = 0; const Param* last_param = nullptr; @@ -922,14 +924,14 @@ SpvId CodeGen::emit(const Def* def, BasicBlockBuilder* bb) { assertf(false, "Incomplete emit(def) definition"); } -std::vector CodeGen::emit_builtin(const Continuation* source_cont, const Continuation* builtin, BasicBlockBuilder* bb) { +std::vector CodeGen::emit_builtin(const App& app, const Continuation* builtin, BasicBlockBuilder* bb) { std::vector productions; auto uvec3_t = convert(world().type_pu32(3)); auto u32_t = convert(world().type_pu32()); auto i32_t = convert(world().type_ps32()); if (builtin->name() == "spirv.nonsemantic.printf") { std::vector args; - auto string = source_cont->arg(1); + auto string = app.arg(1); if (auto arr_type = string->type()->isa(); arr_type->elem_type() == world().type_pu8()) { auto arr = string->as(); std::vector the_string; @@ -939,8 +941,8 @@ std::vector CodeGen::emit_builtin(const Continuation* source_cont, const args.push_back(builder_->debug_string(the_string.data())); } else world().ELOG("spirv.nonsemantic.printf takes a string literal"); - for (size_t i = 2; i < source_cont->num_args() - 1; i++) { - args.push_back(emit(source_cont->arg(i), bb)); + for (size_t i = 2; i < app.num_args() - 1; i++) { + args.push_back(emit(app.arg(i), bb)); } bb->ext_instruction(bb->file_builder.void_type, builder_->imported_instrs->shader_printf, 1, args); @@ -948,23 +950,23 @@ std::vector CodeGen::emit_builtin(const Continuation* source_cont, const THORIN_UNREACHABLE; } else if (builtin->name() == "get_global_id") { auto vector = bb->load(uvec3_t->type_id, builder_->builtins->global_id); - auto extracted = bb->vector_extract_dynamic(u32_t->type_id, vector, emit(source_cont->arg(1), bb)); + auto extracted = bb->vector_extract_dynamic(u32_t->type_id, vector, emit(app.arg(1), bb)); productions.push_back(bb->convert(spv::OpBitcast, i32_t->type_id, extracted)); } else if (builtin->name() == "get_local_size") { auto vector = bb->load(uvec3_t->type_id, builder_->builtins->workgroup_size); - auto extracted = bb->vector_extract_dynamic(u32_t->type_id, vector, emit(source_cont->arg(1), bb)); + auto extracted = bb->vector_extract_dynamic(u32_t->type_id, vector, emit(app.arg(1), bb)); productions.push_back(bb->convert(spv::OpBitcast, i32_t->type_id, extracted)); } else if (builtin->name() == "get_local_id") { auto vector = bb->load(uvec3_t->type_id, builder_->builtins->local_id); - auto extracted = bb->vector_extract_dynamic(u32_t->type_id, vector, emit(source_cont->arg(1), bb)); + auto extracted = bb->vector_extract_dynamic(u32_t->type_id, vector, emit(app.arg(1), bb)); productions.push_back(bb->convert(spv::OpBitcast, i32_t->type_id, extracted)); } else if (builtin->name() == "get_num_groups") { auto vector = bb->load(uvec3_t->type_id, builder_->builtins->num_workgroups); - auto extracted = bb->vector_extract_dynamic(u32_t->type_id, vector, emit(source_cont->arg(1), bb)); + auto extracted = bb->vector_extract_dynamic(u32_t->type_id, vector, emit(app.arg(1), bb)); productions.push_back(bb->convert(spv::OpBitcast, i32_t->type_id, extracted)); } else if (builtin->name() == "get_group_id") { auto vector = bb->load(uvec3_t->type_id, builder_->builtins->workgroup_id); - auto extracted = bb->vector_extract_dynamic(u32_t->type_id, vector, emit(source_cont->arg(1), bb)); + auto extracted = bb->vector_extract_dynamic(u32_t->type_id, vector, emit(app.arg(1), bb)); productions.push_back(bb->convert(spv::OpBitcast, i32_t->type_id, extracted)); } else { world().ELOG("This spir-v builtin isn't recognised: %s", builtin->name()); diff --git a/src/thorin/be/spirv/spirv.h b/src/thorin/be/spirv/spirv.h index 75ed0bb73..d834f7fec 100644 --- a/src/thorin/be/spirv/spirv.h +++ b/src/thorin/be/spirv/spirv.h @@ -93,12 +93,12 @@ struct FileBuilder : public builder::SpvFileBuilder { class CodeGen : public thorin::CodeGen { public: - CodeGen(World&, Cont2Config&, bool debug); + CodeGen(Thorin& thorin, Cont2Config&, bool debug); void emit_stream(std::ostream& stream) override; const char* file_ext() const override { return ".spv"; } - ConvertedType* convert(const Type*); + ConvertedType* convert(const Def*); protected: void structure_loops(); void structure_flow(); @@ -106,7 +106,7 @@ class CodeGen : public thorin::CodeGen { void emit(const Scope& scope); void emit_epilogue(Continuation*, BasicBlockBuilder* bb); SpvId emit(const Def* def, BasicBlockBuilder* bb); - std::vector emit_builtin(const Continuation*, const Continuation*, BasicBlockBuilder*); + std::vector emit_builtin(const App&, const Continuation*, BasicBlockBuilder*); SpvId get_codom_type(const Continuation* fn); From ffae8bd81f0d261bd688336f37e2f36919e7d359 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Thu, 27 Jun 2024 15:03:15 +0200 Subject: [PATCH 227/342] spirv: remove all mentions of structured control flow --- src/thorin/be/spirv/spirv.cpp | 72 ----------------------------------- src/thorin/be/spirv/spirv.h | 3 -- 2 files changed, 75 deletions(-) diff --git a/src/thorin/be/spirv/spirv.cpp b/src/thorin/be/spirv/spirv.cpp index 0f0ffb9c6..d533aabb4 100644 --- a/src/thorin/be/spirv/spirv.cpp +++ b/src/thorin/be/spirv/spirv.cpp @@ -152,10 +152,6 @@ void CodeGen::emit_stream(std::ostream& out) { builder_->builtins = std::make_unique(*builder_); builder_->imported_instrs = std::make_unique(*builder_); - structure_loops(); - structure_flow(); - // cleanup_world(world()); - ScopesForest(world()).for_each([&](const Scope& scope) { emit(scope); }); auto push_constant_arr_type = convert(world().definite_array_type(world().type_pu32(), 128))->type_id; @@ -377,25 +373,10 @@ void CodeGen::emit_epilogue(Continuation* continuation, BasicBlockBuilder* bb) { } bb->branch(current_fn_->labels[dst_cont]); } else if (app.callee() == world().branch()) { - auto& domtree = current_fn_->scope->b_cfg().domtree(); - auto merge_cont = domtree.idom(current_fn_->scope->f_cfg().operator[](continuation))->continuation(); - SpvId merge_bb; - if (merge_cont == current_fn_->scope->exit()) { - BasicBlockBuilder* unreachable_merge_bb = current_fn_->bbs.emplace_back(std::make_unique(*current_fn_)).get(); - current_fn_->bbs_to_emit.emplace_back(unreachable_merge_bb); - builder_->name(unreachable_merge_bb->label, "merge_unreachable" + continuation->name()); - unreachable_merge_bb->unreachable(); - merge_bb = unreachable_merge_bb->label; - } else { - // TODO create a dedicated merge bb if this one is the merge blocks for more than 1 selection construct - merge_bb = current_fn_->labels[merge_cont]; - } - auto cond = emit(app.arg(0), bb); bb->args.emplace(app.arg(0), cond); auto tbb = current_fn_->labels[app.arg(1)->isa_nom()]; auto fbb = current_fn_->labels[app.arg(2)->isa_nom()]; - bb->selection_merge(merge_bb,spv::SelectionControlMaskNone); bb->branch_conditional(cond, tbb, fbb); } else if (app.callee()->isa() && app.callee()->as()->intrinsic() == Intrinsic::Match) { /*auto val = emit(continuation->arg(0)); @@ -410,59 +391,6 @@ void CodeGen::emit_epilogue(Continuation* continuation, BasicBlockBuilder* bb) { THORIN_UNREACHABLE; } else if (app.callee()->isa()) { bb->unreachable(); - } else if (continuation->intrinsic() == Intrinsic::SCFLoopHeader) { - auto merge_label = current_fn_->bbs_map[const_cast(continuation->attributes_.scf_metadata.loop_header.merge_target)]->label; - auto continue_label = current_fn_->bbs_map[const_cast(continuation->attributes_.scf_metadata.loop_header.continue_target)]->label; - bb->loop_merge(merge_label, continue_label, spv::LoopControlMaskNone, {}); - - BasicBlockBuilder* dispatch_bb = current_fn_->bbs.emplace_back(std::make_unique(*current_fn_)).get(); - - auto header_bb_location = std::find(current_fn_->bbs_to_emit.begin(), current_fn_->bbs_to_emit.end(), bb); - - current_fn_->bbs_to_emit.emplace(header_bb_location + 1, dispatch_bb); - builder_->name(dispatch_bb->label, "dispatch_" + continuation->name()); - bb->branch(dispatch_bb->label); - int targets = continuation->num_ops(); - assert(targets > 0); - - // TODO handle dispatching to multiple targets - assert(targets == 1); - auto dispatch_target = continuation->op(0)->isa_nom(); - // Extract the relevant variant & expand the tuple if necessary - auto arg = world().variant_extract(continuation->param(0), 0); - auto extracted = emit(arg, dispatch_bb); - - if (dispatch_target->param(0)->type()->equal(arg->type())) { - auto* param = dispatch_target->param(0); - auto& phi = current_fn_->bbs_map[dispatch_target]->phis_map[param]; - phi.preds.emplace_back(extracted, dispatch_bb->label); - } else { - assert(false && "TODO destructure argument"); - } - - dispatch_bb->branch(current_fn_->bbs_map[dispatch_target]->label); - - } else if (continuation->intrinsic() == Intrinsic::SCFLoopContinue) { - auto loop_header = continuation->op(0)->isa_nom(); - auto header_label = current_fn_->bbs_map[loop_header]->label; - - auto arg = continuation->param(0); - bb->args[arg] = emit(arg, bb); - auto* param = loop_header->param(0); - auto& phi = current_fn_->bbs_map[loop_header]->phis_map[param]; - phi.preds.emplace_back(bb->args[arg], current_fn_->labels[continuation]); - - bb->branch(header_label); - } else if (continuation->intrinsic() == Intrinsic::SCFLoopMerge) { - - int targets = continuation->num_ops(); - assert(targets > 0); - - // TODO handle dispatching to multiple targets - assert(targets == 1); - auto callee = continuation->op(0)->isa_nom(); - // TODO phis - bb->branch(current_fn_->bbs_map[callee]->label); } else if (auto builtin = app.callee()->isa_nom(); builtin->is_imported()) { // Ensure we emit previous memory operations assert(is_mem(app.arg(0))); diff --git a/src/thorin/be/spirv/spirv.h b/src/thorin/be/spirv/spirv.h index d834f7fec..ae6ea7a05 100644 --- a/src/thorin/be/spirv/spirv.h +++ b/src/thorin/be/spirv/spirv.h @@ -100,9 +100,6 @@ class CodeGen : public thorin::CodeGen { ConvertedType* convert(const Def*); protected: - void structure_loops(); - void structure_flow(); - void emit(const Scope& scope); void emit_epilogue(Continuation*, BasicBlockBuilder* bb); SpvId emit(const Def* def, BasicBlockBuilder* bb); From 6f0d6f6fbe6444f71340277a3f6d69bd0c066def Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Thu, 27 Jun 2024 15:04:39 +0200 Subject: [PATCH 228/342] spirv: update branch() to support mem arguments --- src/thorin/be/spirv/spirv.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/thorin/be/spirv/spirv.cpp b/src/thorin/be/spirv/spirv.cpp index d533aabb4..24939162c 100644 --- a/src/thorin/be/spirv/spirv.cpp +++ b/src/thorin/be/spirv/spirv.cpp @@ -373,10 +373,13 @@ void CodeGen::emit_epilogue(Continuation* continuation, BasicBlockBuilder* bb) { } bb->branch(current_fn_->labels[dst_cont]); } else if (app.callee() == world().branch()) { - auto cond = emit(app.arg(0), bb); - bb->args.emplace(app.arg(0), cond); - auto tbb = current_fn_->labels[app.arg(1)->isa_nom()]; - auto fbb = current_fn_->labels[app.arg(2)->isa_nom()]; + auto mem = app.arg(0); + emit_unsafe(mem); + + auto cond = emit(app.arg(1), bb); + bb->args.emplace(app.arg(2), cond); + auto tbb = current_fn_->labels[app.arg(2)->isa_nom()]; + auto fbb = current_fn_->labels[app.arg(3)->isa_nom()]; bb->branch_conditional(cond, tbb, fbb); } else if (app.callee()->isa() && app.callee()->as()->intrinsic() == Intrinsic::Match) { /*auto val = emit(continuation->arg(0)); From 9a4644aac49f256384091aa6bfa95d54000fe51a Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Thu, 27 Jun 2024 16:18:45 +0200 Subject: [PATCH 229/342] re-introduce select parts of spirv type codegen --- src/thorin/CMakeLists.txt | 1 + src/thorin/be/spirv/spirv.cpp | 144 +++++++++++++------------- src/thorin/be/spirv/spirv.h | 103 ++++-------------- src/thorin/be/spirv/spirv_builder.hpp | 43 ++++---- src/thorin/type.h | 2 + 5 files changed, 116 insertions(+), 177 deletions(-) diff --git a/src/thorin/CMakeLists.txt b/src/thorin/CMakeLists.txt index ec3bc9ef1..201d73665 100644 --- a/src/thorin/CMakeLists.txt +++ b/src/thorin/CMakeLists.txt @@ -127,6 +127,7 @@ endif() if(THORIN_ENABLE_SPIRV) list(APPEND THORIN_SOURCES be/spirv/spirv.cpp + be/spirv/spirv_types.cpp be/spirv/spirv.h ) endif() diff --git a/src/thorin/be/spirv/spirv.cpp b/src/thorin/be/spirv/spirv.cpp index 24939162c..651d44e71 100644 --- a/src/thorin/be/spirv/spirv.cpp +++ b/src/thorin/be/spirv/spirv.cpp @@ -94,8 +94,8 @@ FileBuilder::FileBuilder(CodeGen* cg) : builder::SpvFileBuilder(), cg(cg) { } SpvId FileBuilder::u32_t() { - if (u32_t_.id == 0) - u32_t_ = cg->convert(cg->world().type_pu32())->type_id; + if (u32_t_ == 0) + u32_t_ = cg->convert(cg->world().type_pu32()).id; return u32_t_; } @@ -107,9 +107,9 @@ Builtins::Builtins(FileBuilder& builder) { auto& world = builder.cg->world(); auto spv_uvec3_t = builder.cg->convert(world.type_pu32(3)); auto spv_uint_t = builder.cg->convert(world.type_pu32()); - auto spv_uvec3_pt = builder.declare_ptr_type(spv::StorageClassInput, spv_uvec3_t->type_id); - auto spv_uvec3_ptp = builder.declare_ptr_type(spv::StorageClassPrivate, spv_uvec3_t->type_id); - auto spv_uint_pt = builder.declare_ptr_type(spv::StorageClassInput, spv_uint_t->type_id); + auto spv_uvec3_pt = builder.declare_ptr_type(spv::StorageClassInput, spv_uvec3_t.id); + auto spv_uvec3_ptp = builder.declare_ptr_type(spv::StorageClassPrivate, spv_uvec3_t.id); + auto spv_uint_pt = builder.declare_ptr_type(spv::StorageClassInput, spv_uint_t.id); // Because we technically can have multiple entry points, we take the easy way out and make each entry point // write to a private variable the actual workgroup size for that specific kernel. Dirty, but simple. @@ -142,8 +142,8 @@ ImportedInstructions::ImportedInstructions(FileBuilder& builder) { shader_printf = builder.extended_import("NonSemantic.DebugPrintf"); } -CodeGen::CodeGen(Thorin& thorin, Cont2Config& kernel_config, bool debug) - : thorin::CodeGen(thorin, debug), kernel_config_(kernel_config) +CodeGen::CodeGen(Thorin& thorin, SpvTargetInfo target_info, Cont2Config& kernel_config, bool debug) + : thorin::CodeGen(thorin, debug), target_info_(target_info), kernel_config_(kernel_config) {} void CodeGen::emit_stream(std::ostream& out) { @@ -154,7 +154,7 @@ void CodeGen::emit_stream(std::ostream& out) { ScopesForest(world()).for_each([&](const Scope& scope) { emit(scope); }); - auto push_constant_arr_type = convert(world().definite_array_type(world().type_pu32(), 128))->type_id; + auto push_constant_arr_type = convert(world().definite_array_type(world().type_pu32(), 128)).id; auto push_constant_struct_type = builder_->declare_struct_type({ push_constant_arr_type }); auto push_constant_struct_ptr_type = builder_->declare_ptr_type(spv::StorageClassPushConstant, push_constant_struct_type); builder_->name(push_constant_struct_type, "ThorinPushConstant"); @@ -187,7 +187,7 @@ void CodeGen::emit_stream(std::ostream& out) { }; auto spv_uvec3_t = convert(world().type_pu32(3)); - SpvId wg_size_constant = builder_->constant_composite(spv_uvec3_t->type_id, { + SpvId wg_size_constant = builder_->constant_composite(spv_uvec3_t, { builder_->u32_constant(local_size[0]), builder_->u32_constant(local_size[1]), builder_->u32_constant(local_size[2]), @@ -195,7 +195,7 @@ void CodeGen::emit_stream(std::ostream& out) { bb->store(wg_size_constant, builder_->builtins->workgroup_size); // iterate on cont type and extract the arguments - auto ptr_type = convert(world().ptr_type(world().definite_array_type(world().type_pu32(), 128), 1, 4, AddrSpace::Push))->type_id; + auto ptr_type = convert(world().ptr_type(world().definite_array_type(world().type_pu32(), 128), 1, 4, AddrSpace::Push)); auto zero = bb->file_builder.u32_constant(0); auto arr_ref = bb->access_chain(ptr_type, push_constant_struct_ptr, { zero }); uint32_t offset = 0; @@ -243,7 +243,7 @@ void CodeGen::emit(const thorin::Scope& scope) { FnBuilder fn(this, *builder_.get()); fn.scope = &scope; - fn.fn_type = convert(entry_->type())->type_id; + fn.fn_type = convert(entry_->type()).id; fn.fn_ret_type = get_codom_type(entry_); defs_.emplace(scope.entry(), fn.function_id); @@ -273,7 +273,7 @@ void CodeGen::emit(const thorin::Scope& scope) { // Nothing } else if (param->order() == 0) { auto param_t = convert(param->type()); - auto id = fn.parameter(param_t->type_id); + auto id = fn.parameter(param_t.id); fn.params[param] = id; if (param->type()->isa()) { builder_->decorate(id, spv::DecorationAliased); @@ -288,8 +288,8 @@ void CodeGen::emit(const thorin::Scope& scope) { // OpPhi requires the full list of predecessors (values, labels) // We don't have that yet! But we will need the Phi node identifier to build the basic blocks ... // To solve this we generate an id for the phi node now, but defer emission of it to a later stage - auto type = convert(param->type())->type_id; - assert(type.id != 0); + auto type = convert(param->type()).id; + assert(type != 0); bb->phis_map[param] = { type, builder_->generate_fresh_id(), {} }; } } @@ -313,16 +313,16 @@ void CodeGen::emit(const thorin::Scope& scope) { } SpvId CodeGen::get_codom_type(const Continuation* fn) { - auto ret_cont_type = fn->ret_param()->type(); + auto ret_cont_type = fn->ret_param()->type()->as(); std::vector types; - for (auto& op : ret_cont_type->ops()) { + for (auto& op : ret_cont_type->types()) { if (op->isa() || is_type_unit(op->type())) continue; assert(op->order() == 0); - types.push_back(convert(op)->type_id); + types.push_back(convert(op).id); } if (types.empty()) - return builder_->void_type; + return convert(world().unit_type()).id; if (types.size() == 1) return types[0]; return builder_->declare_struct_type(types); @@ -459,7 +459,7 @@ void CodeGen::emit_epilogue(Continuation* continuation, BasicBlockBuilder* bb) { auto param = succ->param(i); if (is_mem(param) || is_unit(param)) continue; - extracts[j] = bb->extract(convert(param->type())->type_id, call_result, { (uint32_t) j }); + extracts[j] = bb->extract(convert(param->type()).id, call_result, { (uint32_t) j }); j++; } @@ -483,8 +483,7 @@ SpvId CodeGen::emit(const Def* def, BasicBlockBuilder* bb) { if (auto bin = def->isa()) { SpvId lhs = emit(bin->lhs(), bb); SpvId rhs = emit(bin->rhs(), bb); - ConvertedType* result_types = convert(def->type()); - SpvId result_type = result_types->type_id; + SpvId result_type = convert(def->type()).id; if (auto cmp = bin->isa()) { auto type = cmp->lhs()->type(); @@ -586,7 +585,7 @@ SpvId CodeGen::emit(const Def* def, BasicBlockBuilder* bb) { } } else if (auto primlit = def->isa()) { Box box = primlit->value(); - auto type = convert(def->type())->type_id; + auto type = convert(def->type()).id; SpvId constant; switch (primlit->primtype_tag()) { case PrimType_bool: constant = bb->file_builder.bool_constant(type, box.get_bool()); break; @@ -612,49 +611,51 @@ SpvId CodeGen::emit(const Def* def, BasicBlockBuilder* bb) { } else if (auto param = def->isa()) { if (is_mem(param)) return spv_none; if (auto param_id = current_fn_->params.lookup(param)) { - assert((*param_id).id != 0); + assert((*param_id) != 0); return *param_id; } else { auto val = (*current_fn_->bbs_map[param->continuation()]).phis_map[param].value; - assert(val.id != 0); + assert(val != 0); return val; } } else if (auto variant = def->isa()) { - auto variant_type = def->type()->as(); + assert(false && "TODO: rewrite"); + /*auto variant_type = def->type()->as(); auto variant_datatype = (ProductDatatype*) convert(variant_type)->datatype.get(); auto tag = builder_->u32_constant(variant->index()); if (variant_datatype->elements_types.size() > 1) { - auto alloc_type = convert(world().ptr_type(variant_datatype->elements_types[1]->src_type, 1, 4, AddrSpace::Function))->type_id; + auto alloc_type = convert(world().ptr_type(variant_datatype->elements_types[1]->src_type, 1, 4, AddrSpace::Function)); auto payload_arr = current_fn_->variable(alloc_type, spv::StorageClassFunction); auto converted_payload_type = convert(variant_type->op(variant->index())); converted_payload_type->datatype->emit_serialization(*bb, spv::StorageClassFunction, payload_arr, bb->file_builder.u32_constant(0), emit(variant->value(), bb)); - auto payload = bb->load(variant_datatype->elements_types[1]->type_id, payload_arr); + auto payload = bb->load(variant_datatype->elements_types[1], payload_arr); std::vector with_tag = {tag, payload}; - return bb->composite(convert(variant->type())->type_id, with_tag); + return bb->composite(convert(variant->type()), with_tag); } else { // Zero-sized payload case std::vector with_tag = { tag }; - return bb->composite(convert(variant->type())->type_id, with_tag); - } + return bb->composite(convert(variant->type()), with_tag); + }*/ } else if (auto vextract = def->isa()) { - auto variant_type = vextract->value()->type()->as(); + assert(false && "TODO: rewrite"); + /*auto variant_type = vextract->value()->type()->as(); auto variant_datatype = (ProductDatatype*) convert(variant_type)->datatype.get(); auto target_type = convert(def->type()); assert(variant_datatype->elements_types.size() > 1 && "Can't extract zero-sized datatypes"); - auto alloc_type = convert(world().ptr_type(variant_datatype->elements_types[1]->src_type, 1, 4, AddrSpace::Function))->type_id; + auto alloc_type = convert(world().ptr_type(variant_datatype->elements_types[1]->src_type, 1, 4, AddrSpace::Function)); auto payload_arr = current_fn_->variable(alloc_type, spv::StorageClassFunction); - auto payload = bb->extract(variant_datatype->elements_types[1]->type_id, emit(vextract->value(), bb), {1}); + auto payload = bb->extract(variant_datatype->elements_types[1], emit(vextract->value(), bb), {1}); bb->store(payload, payload_arr); - return target_type->datatype->emit_deserialization(*bb, spv::StorageClassFunction, payload_arr, bb->file_builder.u32_constant(0)); + return target_type->datatype->emit_deserialization(*bb, spv::StorageClassFunction, payload_arr, bb->file_builder.u32_constant(0));*/ } else if (auto vindex = def->isa()) { auto value = emit(vindex->op(0), bb); - return bb->extract(convert(world().type_pu32())->type_id, value, { 0 }); + return bb->extract(convert(world().type_pu32()).id, value, { 0 }); } else if (auto tuple = def->isa()) { std::vector elements; elements.resize(tuple->num_ops()); @@ -662,7 +663,7 @@ SpvId CodeGen::emit(const Def* def, BasicBlockBuilder* bb) { for (auto& e : tuple->ops()) { elements[x++] = emit(e, bb); } - return bb->composite(convert(tuple->type())->type_id, elements); + return bb->composite(convert(tuple->type()).id, elements); } else if (auto structagg = def->isa()) { std::vector elements; elements.resize(structagg->num_ops()); @@ -670,7 +671,7 @@ SpvId CodeGen::emit(const Def* def, BasicBlockBuilder* bb) { for (auto& e : structagg->ops()) { elements[x++] = emit(e, bb); } - return bb->composite(convert(structagg->type())->type_id, elements); + return bb->composite(convert(structagg->type()).id, elements); } else if (auto access = def->isa()) { // emit dependent operations first emit(access->mem(), bb); @@ -682,7 +683,7 @@ SpvId CodeGen::emit(const Def* def, BasicBlockBuilder* bb) { operands.push_back( 4 ); // TODO: SPIR-V docs say to consult client API for valid values. } if (auto load = def->isa()) { - return bb->load(convert(load->out_val_type())->type_id, emit(load->ptr(), bb), operands); + return bb->load(convert(load->out_val_type()).id, emit(load->ptr(), bb), operands); } else if (auto store = def->isa()) { bb->store(emit(store->val(), bb), emit(store->ptr(), bb), operands); return spv_none; @@ -696,12 +697,12 @@ SpvId CodeGen::emit(const Def* def, BasicBlockBuilder* bb) { world().ELOG("LEA is only allowed in global & shared address spaces"); break; } - auto type = convert(lea->ptr_type()); + auto type = convert(lea->ptr_type()).id; auto offset = emit(lea->index(), bb); - return bb->ptr_access_chain(type->type_id, emit(lea->ptr(), bb), offset, {}); + return bb->ptr_access_chain(type, emit(lea->ptr(), bb), offset, {}); } else if (auto aggop = def->isa()) { auto spv_agg = emit(aggop->agg(), bb); - auto agg_type = convert(aggop->agg()->type())->type_id; + auto agg_type = convert(aggop->agg()->type()).id; bool mem = false; if (auto tt = aggop->agg()->type()->isa(); tt && tt->op(0)->isa()) mem = true; @@ -721,7 +722,7 @@ SpvId CodeGen::emit(const Def* def, BasicBlockBuilder* bb) { if (auto extract = aggop->isa()) { if (is_mem(extract)) return spv_none; - auto target_type = convert(extract->type())->type_id; + auto target_type = convert(extract->type()).id; auto constant_index = aggop->index()->isa(); // We have a fast-path: if the index is constant, we can simply use OpCompositeExtract @@ -775,10 +776,11 @@ SpvId CodeGen::emit(const Def* def, BasicBlockBuilder* bb) { auto conv_dst_type = convert(dst_type); if (auto bitcast = def->isa()) { - if (conv_src_type->datatype->serialized_size() != conv_dst_type->datatype->serialized_size()) - world().ELOG("Source (%) and destination (%) datatypes sizes do not match (% vs % bytes)", src_type->to_string(), dst_type->to_string(), conv_src_type->datatype->serialized_size(), conv_dst_type->datatype->serialized_size()); + assert(conv_src_type.layout && conv_dst_type.layout); + if (conv_src_type.layout->size != conv_dst_type.layout->size) + world().ELOG("Source (%) and destination (%) datatypes sizes do not match (% vs % bytes)", src_type->to_string(), dst_type->to_string(), conv_src_type.layout->size, conv_dst_type.layout->size); - return bb->convert(spv::OpBitcast, convert(bitcast->type())->type_id, emit(bitcast->from(), bb)); + return bb->convert(spv::OpBitcast, convert(bitcast->type()).id, emit(bitcast->from(), bb)); } else if (auto cast = def->isa()) { // NB: all ops used here are scalar/vector agnostic auto src_prim = src_type->isa(); @@ -790,14 +792,14 @@ SpvId CodeGen::emit(const Def* def, BasicBlockBuilder* bb) { auto src_kind = classify_primtype(src_prim); auto dst_kind = classify_primtype(dst_prim); - size_t src_bitwidth = conv_src_type->datatype->serialized_size(); - size_t dst_bitwidth = conv_src_type->datatype->serialized_size(); + size_t src_bitwidth = conv_src_type.layout->size; + size_t dst_bitwidth = conv_src_type.layout->size; SpvId data = emit(cast->from(), bb); // If floating point is involved (src or dst), OpConvert*ToF and OpConvertFTo* can take care of the bit width transformation so no need for any chopping/expanding if (src_kind == PrimTypeKind::Float || dst_kind == PrimTypeKind::Float) { - auto target_type = convert(get_primtype(world(), dst_kind, dst_bitwidth, length))->type_id; + auto target_type = convert(get_primtype(world(), dst_kind, dst_bitwidth, length)).id; switch (src_kind) { case PrimTypeKind::Signed: data = bb->convert(spv::OpConvertSToF, target_type, data); break; case PrimTypeKind::Unsigned: data = bb->convert(spv::OpConvertUToF, target_type, data); break; @@ -815,7 +817,7 @@ SpvId CodeGen::emit(const Def* def, BasicBlockBuilder* bb) { bool needs_expanding = src_bitwidth < dst_bitwidth; if (needs_expanding) { - auto target_type = convert(get_primtype(world(), src_kind, src_bitwidth, length))->type_id; + auto target_type = convert(get_primtype(world(), src_kind, src_bitwidth, length)).id; switch (src_kind) { case PrimTypeKind::Signed: data = bb->convert(spv::OpSConvert, target_type, data); @@ -830,11 +832,11 @@ SpvId CodeGen::emit(const Def* def, BasicBlockBuilder* bb) { } auto expanded_bitwidth = needs_expanding ? dst_bitwidth : src_bitwidth; - auto bitcast_target_type = convert(get_primtype(world(), dst_kind, expanded_bitwidth, length))->type_id; + auto bitcast_target_type = convert(get_primtype(world(), dst_kind, expanded_bitwidth, length)).id; data = bb->convert(spv::OpBitcast, bitcast_target_type, data); if (needs_chopping) { - auto target_type = convert(get_primtype(world(), dst_kind, dst_bitwidth, length))->type_id; + auto target_type = convert(get_primtype(world(), dst_kind, dst_bitwidth, length)).id; switch (dst_kind) { case PrimTypeKind::Signed: data = bb->convert(spv::OpSConvert, target_type, data); @@ -850,16 +852,16 @@ SpvId CodeGen::emit(const Def* def, BasicBlockBuilder* bb) { } } else THORIN_UNREACHABLE; } else if (def->isa()) { - return bb->undef(convert(def->type())->type_id); + return bb->undef(convert(def->type()).id); } assertf(false, "Incomplete emit(def) definition"); } std::vector CodeGen::emit_builtin(const App& app, const Continuation* builtin, BasicBlockBuilder* bb) { std::vector productions; - auto uvec3_t = convert(world().type_pu32(3)); - auto u32_t = convert(world().type_pu32()); - auto i32_t = convert(world().type_ps32()); + auto uvec3_t = convert(world().type_pu32(3)).id; + auto u32_t = convert(world().type_pu32()).id; + auto i32_t = convert(world().type_ps32()).id; if (builtin->name() == "spirv.nonsemantic.printf") { std::vector args; auto string = app.arg(1); @@ -876,29 +878,29 @@ std::vector CodeGen::emit_builtin(const App& app, const Continuation* bui args.push_back(emit(app.arg(i), bb)); } - bb->ext_instruction(bb->file_builder.void_type, builder_->imported_instrs->shader_printf, 1, args); + bb->ext_instruction(convert(world().unit_type()).id, builder_->imported_instrs->shader_printf, 1, args); } else if (builtin->name() == "get_work_dim") { THORIN_UNREACHABLE; } else if (builtin->name() == "get_global_id") { - auto vector = bb->load(uvec3_t->type_id, builder_->builtins->global_id); - auto extracted = bb->vector_extract_dynamic(u32_t->type_id, vector, emit(app.arg(1), bb)); - productions.push_back(bb->convert(spv::OpBitcast, i32_t->type_id, extracted)); + auto vector = bb->load(uvec3_t, builder_->builtins->global_id); + auto extracted = bb->vector_extract_dynamic(u32_t, vector, emit(app.arg(1), bb)); + productions.push_back(bb->convert(spv::OpBitcast, i32_t, extracted)); } else if (builtin->name() == "get_local_size") { - auto vector = bb->load(uvec3_t->type_id, builder_->builtins->workgroup_size); - auto extracted = bb->vector_extract_dynamic(u32_t->type_id, vector, emit(app.arg(1), bb)); - productions.push_back(bb->convert(spv::OpBitcast, i32_t->type_id, extracted)); + auto vector = bb->load(uvec3_t, builder_->builtins->workgroup_size); + auto extracted = bb->vector_extract_dynamic(u32_t, vector, emit(app.arg(1), bb)); + productions.push_back(bb->convert(spv::OpBitcast, i32_t, extracted)); } else if (builtin->name() == "get_local_id") { - auto vector = bb->load(uvec3_t->type_id, builder_->builtins->local_id); - auto extracted = bb->vector_extract_dynamic(u32_t->type_id, vector, emit(app.arg(1), bb)); - productions.push_back(bb->convert(spv::OpBitcast, i32_t->type_id, extracted)); + auto vector = bb->load(uvec3_t, builder_->builtins->local_id); + auto extracted = bb->vector_extract_dynamic(u32_t, vector, emit(app.arg(1), bb)); + productions.push_back(bb->convert(spv::OpBitcast, i32_t, extracted)); } else if (builtin->name() == "get_num_groups") { - auto vector = bb->load(uvec3_t->type_id, builder_->builtins->num_workgroups); - auto extracted = bb->vector_extract_dynamic(u32_t->type_id, vector, emit(app.arg(1), bb)); - productions.push_back(bb->convert(spv::OpBitcast, i32_t->type_id, extracted)); + auto vector = bb->load(uvec3_t, builder_->builtins->num_workgroups); + auto extracted = bb->vector_extract_dynamic(u32_t, vector, emit(app.arg(1), bb)); + productions.push_back(bb->convert(spv::OpBitcast, i32_t, extracted)); } else if (builtin->name() == "get_group_id") { - auto vector = bb->load(uvec3_t->type_id, builder_->builtins->workgroup_id); - auto extracted = bb->vector_extract_dynamic(u32_t->type_id, vector, emit(app.arg(1), bb)); - productions.push_back(bb->convert(spv::OpBitcast, i32_t->type_id, extracted)); + auto vector = bb->load(uvec3_t, builder_->builtins->workgroup_id); + auto extracted = bb->vector_extract_dynamic(u32_t, vector, emit(app.arg(1), bb)); + productions.push_back(bb->convert(spv::OpBitcast, i32_t, extracted)); } else { world().ELOG("This spir-v builtin isn't recognised: %s", builtin->name()); } diff --git a/src/thorin/be/spirv/spirv.h b/src/thorin/be/spirv/spirv.h index ae6ea7a05..2239b677d 100644 --- a/src/thorin/be/spirv/spirv.h +++ b/src/thorin/be/spirv/spirv.h @@ -9,22 +9,28 @@ namespace thorin::spirv { using SpvId = builder::SpvId; class CodeGen; -struct Datatype; -struct PtrDatatype; struct FileBuilder; struct FnBuilder; -struct ConvertedType { - ConvertedType(CodeGen* cg) : code_gen(cg) {} - ConvertedType(const ConvertedType&) = delete; +struct SpvTargetInfo { + struct { + // Either '4' or '8' + size_t pointer_size; + } mem_layout; - spirv::CodeGen* code_gen; - const thorin::Type* src_type; - SpvId type_id { 0 }; - std::unique_ptr datatype; + enum Dialect{ + OpenCL, + Shady + }; +}; - bool is_known_size() { return datatype != nullptr; } +struct ConvertedType { + SpvId id; + struct Layout { + size_t size, alignment; + }; + std::optional layout; }; struct BasicBlockBuilder : public builder::SpvBasicBlockBuilder { @@ -82,23 +88,16 @@ struct FileBuilder : public builder::SpvFileBuilder { private: SpvId u32_t_ { 0 }; - /*SpvId i32_t; - SpvId u32_t; - SpvId i64_t; - SpvId u64_t; - SpvId i32_constant(int32_t); - SpvId i64_constant(int64_t); - SpvId u64_constant(uint64_t);*/ }; class CodeGen : public thorin::CodeGen { public: - CodeGen(Thorin& thorin, Cont2Config&, bool debug); + CodeGen(Thorin& thorin, SpvTargetInfo, Cont2Config&, bool debug); void emit_stream(std::ostream& stream) override; const char* file_ext() const override { return ".spv"; } - ConvertedType* convert(const Def*); + ConvertedType convert(const Type*); protected: void emit(const Scope& scope); void emit_epilogue(Continuation*, BasicBlockBuilder* bb); @@ -107,75 +106,13 @@ class CodeGen : public thorin::CodeGen { SpvId get_codom_type(const Continuation* fn); + SpvTargetInfo target_info_; std::unique_ptr builder_; Continuation* entry_ = nullptr; FnBuilder* current_fn_ = nullptr; - DefMap> types_; + DefMap types_; DefMap defs_; const Cont2Config& kernel_config_; - - friend PtrDatatype; -}; - -/// Thorin data types are mapped to SPIR-V in non-trivial ways, this interface is used by the emission code to abstract over -/// potentially different mappings, depending on the capabilities of the target platform. The serdes code deals with pointers -/// in arrays of unsigned 32 bit words, and is there to get around the limitation of not being able to bitcast pointers in the -/// logical addressing mode. -struct Datatype { -public: - ConvertedType* type; - Datatype(ConvertedType* type) : type(type) {} - - // Datatypes are serialized using a base element, for now it is hardcoded to use 32-bit scalar unsigned integers - static constexpr size_t base_element_bitwidth = 32; - static constexpr size_t base_element_bytes = base_element_bitwidth / 8; - - virtual size_t serialized_size() = 0; - virtual SpvId emit_deserialization(BasicBlockBuilder& bb, spv::StorageClass storage_class, SpvId array, SpvId base_offset) = 0; - virtual void emit_serialization(BasicBlockBuilder& bb, spv::StorageClass storage_class, SpvId array, SpvId base_offset, SpvId data) = 0; -}; - -/// For scalar datatypes -struct ScalarDatatype : public Datatype { - int type_tag; - size_t size_in_bytes; - size_t alignment; - ScalarDatatype(ConvertedType* type, int type_tag, size_t size_in_bytes, size_t alignment_in_bytes); - - size_t serialized_size() override { return (size_in_bytes + 3) / 4; }; - SpvId emit_deserialization(BasicBlockBuilder& bb, spv::StorageClass storage_class, SpvId array, SpvId base_offset) override; - void emit_serialization(BasicBlockBuilder& bb, spv::StorageClass storage_class, SpvId array, SpvId base_offset, SpvId data) override; -}; - -struct PtrDatatype : public Datatype { - static constexpr size_t bitwidth = 64; - PtrDatatype(ConvertedType* type) : Datatype(type) {} - - size_t serialized_size() override { return bitwidth / 32; }; - SpvId emit_deserialization(BasicBlockBuilder& bb, spv::StorageClass storage_class, SpvId array, SpvId base_offset) override; - void emit_serialization(BasicBlockBuilder& bb, spv::StorageClass storage_class, SpvId array, SpvId base_offset, SpvId data) override; -}; - -struct DefiniteArrayDatatype : public Datatype { - ConvertedType* element_type; - size_t length; - - DefiniteArrayDatatype(ConvertedType* type, ConvertedType* element_type, size_t length); - - size_t serialized_size() override { return element_type->datatype->serialized_size(); }; - SpvId emit_deserialization(BasicBlockBuilder& bb, spv::StorageClass storage_class, SpvId array, SpvId base_offset) override; - void emit_serialization(BasicBlockBuilder& bb, spv::StorageClass storage_class, SpvId array, SpvId base_offset, SpvId data) override; -}; - -struct ProductDatatype : public Datatype { - std::vector elements_types; - size_t total_size = 0; - - ProductDatatype(ConvertedType* type, const std::vector&& elements_types); - - size_t serialized_size() override { return total_size; }; - SpvId emit_deserialization(BasicBlockBuilder& bb, spv::StorageClass storage_class, SpvId array, SpvId base_offset) override; - void emit_serialization(BasicBlockBuilder& bb, spv::StorageClass storage_class, SpvId array, SpvId base_offset, SpvId data) override; }; } diff --git a/src/thorin/be/spirv/spirv_builder.hpp b/src/thorin/be/spirv/spirv_builder.hpp index 238343e79..6a600b7a6 100644 --- a/src/thorin/be/spirv/spirv_builder.hpp +++ b/src/thorin/be/spirv/spirv_builder.hpp @@ -9,7 +9,8 @@ namespace thorin::spirv::builder { -struct SpvId { uint32_t id; }; +//struct SpvId { uint32_t id; }; +using SpvId = uint32_t; struct SpvSectionBuilder; struct SpvBasicBlockBuilder; @@ -38,8 +39,8 @@ struct SpvSectionBuilder { } void ref_id(SpvId id) { - assert(id.id != 0); - output_word(id.id); + assert(id != 0); + output_word(id); } void literal_name(std::string_view str) { @@ -340,15 +341,13 @@ struct SpvFileBuilder { } }; - SpvFileBuilder() - : void_type(declare_void_type()) - {} + SpvFileBuilder() {} SpvFileBuilder(const SpvFileBuilder&) = delete; SpvId generate_fresh_id() { return { bound++ }; } void name(SpvId id, std::string_view str) { - assert(id.id < bound); + assert(id < bound); debug_names.op(spv::Op::OpName, 2 + div_roundup(str.size() + 1, 4)); debug_names.ref_id(id); debug_names.literal_name(str); @@ -379,7 +378,7 @@ struct SpvFileBuilder { } SpvId declare_ptr_type(spv::StorageClass storage_class, SpvId element_type) { - auto key = UniqueDeclKey { PTR_TYPE, { element_type.id, (uint32_t) storage_class } }; + auto key = UniqueDeclKey { PTR_TYPE, { element_type, (uint32_t) storage_class } }; if (auto iter = unique_decls.find(key); iter != unique_decls.end()) return iter->second; types_constants.op(spv::Op::OpTypePointer, 4); auto id = generate_fresh_id(); @@ -391,7 +390,7 @@ struct SpvFileBuilder { } SpvId declare_array_type(SpvId element_type, SpvId dim) { - auto key = UniqueDeclKey { DEF_ARR_TYPE, { element_type.id, dim.id } }; + auto key = UniqueDeclKey { DEF_ARR_TYPE, { element_type, dim } }; if (auto iter = unique_decls.find(key); iter != unique_decls.end()) return iter->second; types_constants.op(spv::Op::OpTypeArray, 4); auto id = generate_fresh_id(); @@ -404,8 +403,8 @@ struct SpvFileBuilder { SpvId declare_fn_type(std::vector dom, SpvId codom) { auto key = UniqueDeclKey { FN_TYPE, {} }; - for (auto d : dom) key.members.push_back(d.id); - key.members.push_back(codom.id); + for (auto d : dom) key.members.push_back(d); + key.members.push_back(codom); if (auto iter = unique_decls.find(key); iter != unique_decls.end()) return iter->second; types_constants.op(spv::Op::OpTypeFunction, 3 + dom.size()); @@ -471,7 +470,7 @@ struct SpvFileBuilder { SpvId constant(SpvId type, std::vector bit_pattern) { auto key = UniqueDeclKey { CONSTANT, bit_pattern }; - key.members.push_back(type.id); + key.members.push_back(type); if (auto iter = unique_decls.find(key); iter != unique_decls.end()) return iter->second; types_constants.op(spv::Op::OpConstant, 3 + bit_pattern.size()); auto id = generate_fresh_id(); @@ -485,8 +484,8 @@ struct SpvFileBuilder { SpvId constant_composite(SpvId type, std::vector ops) { auto key = UniqueDeclKey { CONSTANT_COMPOSITE, {} }; - key.members.push_back(type.id); - for (auto op : ops) key.members.push_back(op.id); + key.members.push_back(type); + for (auto op : ops) key.members.push_back(op); if (auto iter = unique_decls.find(key); iter != unique_decls.end()) return iter->second; types_constants.op(spv::Op::OpConstantComposite, 3 + ops.size()); auto id = generate_fresh_id(); @@ -507,6 +506,13 @@ struct SpvFileBuilder { return id; } + SpvId declare_void_type() { + types_constants.op(spv::Op::OpTypeVoid, 2); + auto id = generate_fresh_id(); + types_constants.ref_id(id); + return id; + } + SpvId define_function(SpvFnBuilder& fn_builder) { fn_defs.op(spv::Op::OpFunction, 5); fn_defs.ref_id(fn_builder.fn_ret_type); @@ -607,13 +613,6 @@ struct SpvFileBuilder { // SPIR-V disallows duplicate non-aggregate type declarations, we protect against these with this std::unordered_map unique_decls; - SpvId declare_void_type() { - types_constants.op(spv::Op::OpTypeVoid, 2); - auto id = generate_fresh_id(); - types_constants.ref_id(id); - return id; - } - void output_word_le(uint32_t word) { output_->put((word >> 0) & 0xFFu); output_->put((word >> 8) & 0xFFu); @@ -627,8 +626,6 @@ struct SpvFileBuilder { } } public: - const SpvId void_type; - void finish(std::ostream& output) { output_ = &output; SpvSectionBuilder memory_model_section; diff --git a/src/thorin/type.h b/src/thorin/type.h index e73ad5b6f..bf7cb26df 100644 --- a/src/thorin/type.h +++ b/src/thorin/type.h @@ -233,6 +233,8 @@ enum class AddrSpace : uint32_t { Shared = 3, Constant = 4, Private = 5, // Corresponds to the 'private' storage class in compute kernels/shaders, as in thread-private + Function = 6, // Corresponds to the 'function' storage class in SPIR-V + Push = 7, // Corresponds to the 'push constant' storage class in SPIR-V }; /// Pointer type. From 2fbfc56475615fbcdee0faa36f8514ff92993260 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Thu, 27 Jun 2024 16:25:58 +0200 Subject: [PATCH 230/342] spirv: just emit entry points naively --- src/thorin/be/spirv/spirv.cpp | 70 ++++------------------------------- 1 file changed, 8 insertions(+), 62 deletions(-) diff --git a/src/thorin/be/spirv/spirv.cpp b/src/thorin/be/spirv/spirv.cpp index 651d44e71..7d1f38727 100644 --- a/src/thorin/be/spirv/spirv.cpp +++ b/src/thorin/be/spirv/spirv.cpp @@ -154,17 +154,12 @@ void CodeGen::emit_stream(std::ostream& out) { ScopesForest(world()).for_each([&](const Scope& scope) { emit(scope); }); - auto push_constant_arr_type = convert(world().definite_array_type(world().type_pu32(), 128)).id; - auto push_constant_struct_type = builder_->declare_struct_type({ push_constant_arr_type }); - auto push_constant_struct_ptr_type = builder_->declare_ptr_type(spv::StorageClassPushConstant, push_constant_struct_type); - builder_->name(push_constant_struct_type, "ThorinPushConstant"); - builder_->decorate(push_constant_struct_type, spv::DecorationBlock); - builder_->decorate_member(push_constant_struct_type, 0, spv::DecorationOffset, { 0 }); - builder_->decorate(push_constant_arr_type, spv::DecorationArrayStride, { 4 }); - auto push_constant_struct_ptr = builder_->variable(push_constant_struct_ptr_type, spv::StorageClassPushConstant); - builder_->name(push_constant_struct_ptr, "thorin_push_constant_data"); - - auto entry_pt_signature = builder_->declare_fn_type({}, builder_->void_type); + std::vector interface; + for (auto def : world().defs()) { + if (auto global = def->isa()) + interface.push_back(emit(global)); + } + for (auto& cont : world().copy_continuations()) { if (cont->is_exported()) { assert(defs_.contains(cont) && kernel_config_.contains(cont)); @@ -172,13 +167,6 @@ void CodeGen::emit_stream(std::ostream& out) { SpvId callee = defs_[cont]; - FnBuilder fn_builder(this, *builder_.get()); - fn_builder.fn_type = entry_pt_signature; - fn_builder.fn_ret_type = builder_->void_type; - - BasicBlockBuilder* bb = fn_builder.bbs.emplace_back(std::make_unique(fn_builder)).get(); - fn_builder.bbs_to_emit.push_back(bb); - auto block = config->second->as()->block_size(); std::vector local_size = { (uint32_t) std::get<0>(block), @@ -186,50 +174,8 @@ void CodeGen::emit_stream(std::ostream& out) { (uint32_t) std::get<2>(block), }; - auto spv_uvec3_t = convert(world().type_pu32(3)); - SpvId wg_size_constant = builder_->constant_composite(spv_uvec3_t, { - builder_->u32_constant(local_size[0]), - builder_->u32_constant(local_size[1]), - builder_->u32_constant(local_size[2]), - }); - bb->store(wg_size_constant, builder_->builtins->workgroup_size); - - // iterate on cont type and extract the arguments - auto ptr_type = convert(world().ptr_type(world().definite_array_type(world().type_pu32(), 128), 1, 4, AddrSpace::Push)); - auto zero = bb->file_builder.u32_constant(0); - auto arr_ref = bb->access_chain(ptr_type, push_constant_struct_ptr, { zero }); - uint32_t offset = 0; - std::vector args; - for (size_t i = 0; i < cont->num_params(); i++) { - auto param = cont->param(i); - auto param_type = param->type(); - if (param_type == world().unit_type() || param_type == world().mem_type() || param_type->isa()) continue; - assert(param_type->order() == 0); - auto converted = convert(param_type); - assert(converted->datatype != nullptr); - SpvId arg = converted->datatype->emit_deserialization(*bb, spv::StorageClassPushConstant, arr_ref, bb->file_builder.u32_constant(offset)); - args.push_back(arg); - offset += converted->datatype->serialized_size(); - } - - bb->call(builder_->void_type, callee, args); - bb->return_void(); - - builder_->define_function(fn_builder); - builder_->name(fn_builder.function_id, "entry_point_" + cont->name()); - - std::vector interface = { - push_constant_struct_ptr, - builder_->builtins->workgroup_size, - builder_->builtins->num_workgroups, - builder_->builtins->workgroup_id, - builder_->builtins->local_id, - builder_->builtins->global_id, - builder_->builtins->local_invocation_index, - }; - builder_->declare_entry_point(spv::ExecutionModelGLCompute, fn_builder.function_id, "kernel_main", interface); - - builder_->execution_mode(fn_builder.function_id, spv::ExecutionModeLocalSize, local_size); + builder_->declare_entry_point(spv::ExecutionModelGLCompute, callee, cont->name().c_str(), interface); + builder_->execution_mode(callee, spv::ExecutionModeLocalSize, local_size); } } From 8d29a53f813e3196958eca55e389879759438c61 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Thu, 27 Jun 2024 17:46:08 +0200 Subject: [PATCH 231/342] spirv: base off Emitter --- src/thorin/be/spirv/spirv.cpp | 217 +++++++++++++++++----------------- src/thorin/be/spirv/spirv.h | 28 +++-- 2 files changed, 125 insertions(+), 120 deletions(-) diff --git a/src/thorin/be/spirv/spirv.cpp b/src/thorin/be/spirv/spirv.cpp index 7d1f38727..ee732ff4b 100644 --- a/src/thorin/be/spirv/spirv.cpp +++ b/src/thorin/be/spirv/spirv.cpp @@ -80,7 +80,7 @@ BasicBlockBuilder::BasicBlockBuilder(FnBuilder& fn_builder) label = file_builder.generate_fresh_id(); } -FnBuilder::FnBuilder(CodeGen* cg, FileBuilder& file_builder) : builder::SpvFnBuilder(&file_builder), cg(cg), file_builder(file_builder) {} +FnBuilder::FnBuilder(FileBuilder& file_builder) : builder::SpvFnBuilder(&file_builder), file_builder(file_builder) {} FileBuilder::FileBuilder(CodeGen* cg) : builder::SpvFileBuilder(), cg(cg) { capability(spv::Capability::CapabilityShader); @@ -152,7 +152,8 @@ void CodeGen::emit_stream(std::ostream& out) { builder_->builtins = std::make_unique(*builder_); builder_->imported_instrs = std::make_unique(*builder_); - ScopesForest(world()).for_each([&](const Scope& scope) { emit(scope); }); + ScopesForest forest(world()); + forest.for_each([&](const Scope& scope) { emit_scope(scope, forest); }); std::vector interface; for (auto def : world().defs()) { @@ -183,79 +184,73 @@ void CodeGen::emit_stream(std::ostream& out) { builder_ = nullptr; } -void CodeGen::emit(const thorin::Scope& scope) { - entry_ = scope.entry(); - assert(entry_->is_returning()); +SpvId CodeGen::emit_fun_decl(thorin::Continuation* continuation) { + return get_fn_builder(continuation).function_id; +} + +FnBuilder& CodeGen::get_fn_builder(thorin::Continuation* continuation) { + if (auto found = fn_builders_.find(continuation); found != fn_builders_.end()) { + return *found->second; + } - FnBuilder fn(this, *builder_.get()); - fn.scope = &scope; + auto& fn = *(fn_builders_[continuation] = std::make_unique(*builder_)); fn.fn_type = convert(entry_->type()).id; fn.fn_ret_type = get_codom_type(entry_); - defs_.emplace(scope.entry(), fn.function_id); - - current_fn_ = &fn; - - auto conts = schedule(scope); - - fn.bbs_to_emit.reserve(conts.size()); - fn.bbs.reserve(conts.size()); - auto& bbs = fn.bbs; - - for (auto cont : conts) { - if (cont->intrinsic() == Intrinsic::EndScope) continue; - - BasicBlockBuilder* bb = bbs.emplace_back(std::make_unique(fn)).get(); - fn.bbs_to_emit.emplace_back(bb); - auto [i, b] = fn.bbs_map.emplace(cont, bb); - assert(b); - - if (debug()) - builder_->name(bb->label, cont->name().c_str()); - fn.labels.emplace(cont, bb->label); - - if (entry_ == cont) { - for (auto param : entry_->params()) { - if (is_mem(param) || is_unit(param)) { - // Nothing - } else if (param->order() == 0) { - auto param_t = convert(param->type()); - auto id = fn.parameter(param_t.id); - fn.params[param] = id; - if (param->type()->isa()) { - builder_->decorate(id, spv::DecorationAliased); - } + return fn; +} + +FnBuilder& CodeGen::prepare(const thorin::Scope& scope) { + auto& fn = get_fn_builder(scope.entry()); + builder_->name(fn.function_id, scope.entry()->name()); + return fn; +} + +void CodeGen::prepare(thorin::Continuation* cont, FnBuilder& fn) { + auto& bb = *fn.bbs.emplace_back(std::make_unique(fn)); + cont2bb_[cont] = &bb; + fn.bbs_to_emit.emplace_back(&bb); + + builder_->name(bb.label, cont->name().c_str()); + + if (entry_ == cont) { + for (auto param : cont->params()) { + if (is_mem(param) || is_unit(param)) { + // Nothing + } else if (param->order() == 0) { + auto param_t = convert(param->type()); + auto id = fn.parameter(param_t.id); + fn.params[param] = id; + if (param->type()->isa()) { + builder_->decorate(id, spv::DecorationAliased); } } - } else { - for (auto param : cont->params()) { - if (is_mem(param) || is_unit(param)) { - // Nothing - } else { - // OpPhi requires the full list of predecessors (values, labels) - // We don't have that yet! But we will need the Phi node identifier to build the basic blocks ... - // To solve this we generate an id for the phi node now, but defer emission of it to a later stage - auto type = convert(param->type()).id; - assert(type != 0); - bb->phis_map[param] = { type, builder_->generate_fresh_id(), {} }; - } + } + } else { + for (auto param : cont->params()) { + if (is_mem(param) || is_unit(param)) { + // Nothing + } else { + // OpPhi requires the full list of predecessors (values, labels) + // We don't have that yet! But we will need the Phi node identifier to build the basic blocks ... + // To solve this we generate an id for the phi node now, but defer emission of it to a later stage + auto type = convert(param->type()).id; + assert(type != 0); + bb.phis_map[param] = { type, builder_->generate_fresh_id(), {} }; } } } - for (auto cont : conts) { - if (cont->intrinsic() == Intrinsic::EndScope) continue; - assert(cont == entry_ || cont->is_basicblock()); - emit_epilogue(cont, fn.bbs_map[cont]); - } +} - for(auto& bb : fn.bbs) { - for (auto& [param, phi] : bb->phis_map) { - bb->phis.emplace_back(&phi); - } +void CodeGen::finalize(thorin::Continuation* cont) { + auto& bb = *cont2bb_[cont]; + for (auto& [param, phi] : bb.phis_map) { + bb.phis.emplace_back(&phi); } +} - builder_->define_function(fn); - builder_->name(fn.function_id, scope.entry()->name()); +void CodeGen::finalize(const thorin::Scope&) { + builder_->define_function(*current_fn_); } SpvId CodeGen::get_codom_type(const Continuation* fn) { @@ -274,16 +269,18 @@ SpvId CodeGen::get_codom_type(const Continuation* fn) { return builder_->declare_struct_type(types); } -void CodeGen::emit_epilogue(Continuation* continuation, BasicBlockBuilder* bb) { +void CodeGen::emit_epilogue(Continuation* continuation) { + auto& bb = cont2bb_[continuation]; // Handles the potential nuances of jumping to another continuation auto jump_to_next_cont_with_args = [&](Continuation* succ, std::vector args) { - bb->branch(current_fn_->labels[succ]); + assert(succ->is_basicblock()); + bb->branch(emit(succ)); for (size_t i = 0, j = 0; i != succ->num_params(); ++i) { auto param = succ->param(i); if (is_mem(param) || is_unit(param)) continue; - auto& phi = current_fn_->bbs_map[succ]->phis_map[param]; - phi.preds.emplace_back(args[j], current_fn_->labels[continuation]); + auto& phi = cont2bb_[succ]->phis_map[param]; + phi.preds.emplace_back(args[j], emit(continuation)); j++; } }; @@ -295,7 +292,7 @@ void CodeGen::emit_epilogue(Continuation* continuation, BasicBlockBuilder* bb) { for (auto arg : app.args()) { assert(arg->order() == 0); - auto val = emit(arg, bb); + auto val = emit(arg); if (is_mem(arg) || is_unit(arg)) continue; values.emplace_back(val); @@ -310,22 +307,22 @@ void CodeGen::emit_epilogue(Continuation* continuation, BasicBlockBuilder* bb) { int index = -1; for (auto& arg : app.args()) { index++; - auto val = emit(arg, bb); + auto val = emit(arg); if (is_mem(arg) || is_unit(arg)) continue; bb->args[arg] = val; auto* param = dst_cont->param(index); - auto& phi = current_fn_->bbs_map[dst_cont]->phis_map[param]; - phi.preds.emplace_back(bb->args[arg], current_fn_->labels[continuation]); + auto& phi = cont2bb_[dst_cont]->phis_map[param]; + phi.preds.emplace_back(bb->args[arg], emit(continuation)); } - bb->branch(current_fn_->labels[dst_cont]); + bb->branch(emit(dst_cont)); } else if (app.callee() == world().branch()) { auto mem = app.arg(0); emit_unsafe(mem); - auto cond = emit(app.arg(1), bb); + auto cond = emit(app.arg(1)); bb->args.emplace(app.arg(2), cond); - auto tbb = current_fn_->labels[app.arg(2)->isa_nom()]; - auto fbb = current_fn_->labels[app.arg(3)->isa_nom()]; + auto tbb = emit(app.arg(2)); + auto fbb = emit(app.arg(3)); bb->branch_conditional(cond, tbb, fbb); } else if (app.callee()->isa() && app.callee()->as()->intrinsic() == Intrinsic::Match) { /*auto val = emit(continuation->arg(0)); @@ -343,7 +340,7 @@ void CodeGen::emit_epilogue(Continuation* continuation, BasicBlockBuilder* bb) { } else if (auto builtin = app.callee()->isa_nom(); builtin->is_imported()) { // Ensure we emit previous memory operations assert(is_mem(app.arg(0))); - emit(app.arg(0), bb); + emit(app.arg(0)); auto productions = emit_builtin(app, builtin, bb); auto succ = app.args().back()->isa_nom(); @@ -357,7 +354,7 @@ void CodeGen::emit_epilogue(Continuation* continuation, BasicBlockBuilder* bb) { for (auto arg : app.args()) { if (arg->order() == 0) { auto arg_type = arg->type(); - auto arg_val = emit(arg, bb); + auto arg_val = emit(arg); if (arg_type == world().unit_type() || arg_type == world().mem_type()) continue; call_args.push_back(arg_val); } else { @@ -370,7 +367,7 @@ void CodeGen::emit_epilogue(Continuation* continuation, BasicBlockBuilder* bb) { SpvId call_result; if (auto called_continuation = app.callee()->isa_nom()) { - call_result = bb->call(ret_type, emit(called_continuation, bb), call_args); + call_result = bb->call(ret_type, emit(called_continuation), call_args); } else { // must be a closure THORIN_UNREACHABLE; @@ -393,12 +390,12 @@ void CodeGen::emit_epilogue(Continuation* continuation, BasicBlockBuilder* bb) { } if (n == 0) { - bb->branch(current_fn_->labels[succ]); + bb->branch(emit(succ)); } else if (n == 1) { - bb->branch(current_fn_->labels[succ]); + bb->branch(emit(succ)); - auto& phi = current_fn_->bbs_map[succ]->phis_map[last_param]; - phi.preds.emplace_back(call_result, current_fn_->labels[continuation]); + auto& phi = cont2bb_[succ]->phis_map[last_param]; + phi.preds.emplace_back(call_result, emit(continuation)); } else { Array extracts(n); for (size_t i = 0, j = 0; i != succ->num_params(); ++i) { @@ -409,15 +406,15 @@ void CodeGen::emit_epilogue(Continuation* continuation, BasicBlockBuilder* bb) { j++; } - bb->branch(current_fn_->labels[succ]); + bb->branch(emit(succ)); for (size_t i = 0, j = 0; i != succ->num_params(); ++i) { auto param = succ->param(i); if (is_mem(param) || is_unit(param)) continue; - auto& phi = current_fn_->bbs_map[succ]->phis_map[param]; - phi.preds.emplace_back(extracts[j], current_fn_->labels[continuation]); + auto& phi = cont2bb_[succ]->phis_map[last_param]; + phi.preds.emplace_back(extracts[j], emit(continuation)); j++; } @@ -425,10 +422,10 @@ void CodeGen::emit_epilogue(Continuation* continuation, BasicBlockBuilder* bb) { } } -SpvId CodeGen::emit(const Def* def, BasicBlockBuilder* bb) { +SpvId CodeGen::emit_bb(const Def* def, BasicBlockBuilder* bb) { if (auto bin = def->isa()) { - SpvId lhs = emit(bin->lhs(), bb); - SpvId rhs = emit(bin->rhs(), bb); + SpvId lhs = emit(bin->lhs()); + SpvId rhs = emit(bin->rhs()); SpvId result_type = convert(def->type()).id; if (auto cmp = bin->isa()) { @@ -560,7 +557,7 @@ SpvId CodeGen::emit(const Def* def, BasicBlockBuilder* bb) { assert((*param_id) != 0); return *param_id; } else { - auto val = (*current_fn_->bbs_map[param->continuation()]).phis_map[param].value; + auto val = cont2bb_[param->continuation()]->phis_map[param].value; assert(val != 0); return val; } @@ -600,14 +597,14 @@ SpvId CodeGen::emit(const Def* def, BasicBlockBuilder* bb) { return target_type->datatype->emit_deserialization(*bb, spv::StorageClassFunction, payload_arr, bb->file_builder.u32_constant(0));*/ } else if (auto vindex = def->isa()) { - auto value = emit(vindex->op(0), bb); + auto value = emit(vindex->op(0)); return bb->extract(convert(world().type_pu32()).id, value, { 0 }); } else if (auto tuple = def->isa()) { std::vector elements; elements.resize(tuple->num_ops()); size_t x = 0; for (auto& e : tuple->ops()) { - elements[x++] = emit(e, bb); + elements[x++] = emit(e); } return bb->composite(convert(tuple->type()).id, elements); } else if (auto structagg = def->isa()) { @@ -615,12 +612,12 @@ SpvId CodeGen::emit(const Def* def, BasicBlockBuilder* bb) { elements.resize(structagg->num_ops()); size_t x = 0; for (auto& e : structagg->ops()) { - elements[x++] = emit(e, bb); + elements[x++] = emit(e); } return bb->composite(convert(structagg->type()).id, elements); } else if (auto access = def->isa()) { // emit dependent operations first - emit(access->mem(), bb); + emit(access->mem()); std::vector operands; auto ptr_type = access->ptr()->type()->as(); @@ -629,9 +626,9 @@ SpvId CodeGen::emit(const Def* def, BasicBlockBuilder* bb) { operands.push_back( 4 ); // TODO: SPIR-V docs say to consult client API for valid values. } if (auto load = def->isa()) { - return bb->load(convert(load->out_val_type()).id, emit(load->ptr(), bb), operands); + return bb->load(convert(load->out_val_type()).id, emit(load->ptr()), operands); } else if (auto store = def->isa()) { - bb->store(emit(store->val(), bb), emit(store->ptr(), bb), operands); + bb->store(emit(store->val()), emit(store->ptr()), operands); return spv_none; } else THORIN_UNREACHABLE; } else if (auto lea = def->isa()) { @@ -644,10 +641,10 @@ SpvId CodeGen::emit(const Def* def, BasicBlockBuilder* bb) { break; } auto type = convert(lea->ptr_type()).id; - auto offset = emit(lea->index(), bb); - return bb->ptr_access_chain(type, emit(lea->ptr(), bb), offset, {}); + auto offset = emit(lea->index()); + return bb->ptr_access_chain(type, emit(lea->ptr()), offset, {}); } else if (auto aggop = def->isa()) { - auto spv_agg = emit(aggop->agg(), bb); + auto spv_agg = emit(aggop->agg()); auto agg_type = convert(aggop->agg()->type()).id; bool mem = false; @@ -661,7 +658,7 @@ SpvId CodeGen::emit(const Def* def, BasicBlockBuilder* bb) { bb->store(spv_agg, variable); auto cell_ptr_type = builder_->declare_ptr_type(spv::StorageClassFunction, target_type); - auto cell = bb->access_chain(cell_ptr_type, variable, { emit(aggop->index(), bb)} ); + auto cell = bb->access_chain(cell_ptr_type, variable, { emit(aggop->index())} ); return std::make_pair(variable, cell); }; @@ -679,7 +676,7 @@ SpvId CodeGen::emit(const Def* def, BasicBlockBuilder* bb) { } if (extract->agg()->type()->isa()) - return bb->vector_extract_dynamic(target_type, spv_agg, emit(extract->index(), bb)); + return bb->vector_extract_dynamic(target_type, spv_agg, emit(extract->index())); // index *must* be constant for the remaining possible cases assert(constant_index != nullptr); @@ -693,7 +690,7 @@ SpvId CodeGen::emit(const Def* def, BasicBlockBuilder* bb) { return bb->extract(target_type, spv_agg, { index - offset }); } else if (auto insert = def->isa()) { - auto value = emit(insert->value(), bb); + auto value = emit(insert->value()); auto constant_index = aggop->index()->isa(); // TODO deal with mem - but I think for now this case shouldn't happen @@ -706,7 +703,7 @@ SpvId CodeGen::emit(const Def* def, BasicBlockBuilder* bb) { } if (insert->agg()->type()->isa()) - return bb->vector_insert_dynamic(agg_type, spv_agg, value, emit(insert->index(), bb)); + return bb->vector_insert_dynamic(agg_type, spv_agg, value, emit(insert->index())); // index *must* be constant for the remaining possible cases assert(constant_index != nullptr); @@ -726,7 +723,7 @@ SpvId CodeGen::emit(const Def* def, BasicBlockBuilder* bb) { if (conv_src_type.layout->size != conv_dst_type.layout->size) world().ELOG("Source (%) and destination (%) datatypes sizes do not match (% vs % bytes)", src_type->to_string(), dst_type->to_string(), conv_src_type.layout->size, conv_dst_type.layout->size); - return bb->convert(spv::OpBitcast, convert(bitcast->type()).id, emit(bitcast->from(), bb)); + return bb->convert(spv::OpBitcast, convert(bitcast->type()).id, emit(bitcast->from())); } else if (auto cast = def->isa()) { // NB: all ops used here are scalar/vector agnostic auto src_prim = src_type->isa(); @@ -741,7 +738,7 @@ SpvId CodeGen::emit(const Def* def, BasicBlockBuilder* bb) { size_t src_bitwidth = conv_src_type.layout->size; size_t dst_bitwidth = conv_src_type.layout->size; - SpvId data = emit(cast->from(), bb); + SpvId data = emit(cast->from()); // If floating point is involved (src or dst), OpConvert*ToF and OpConvertFTo* can take care of the bit width transformation so no need for any chopping/expanding if (src_kind == PrimTypeKind::Float || dst_kind == PrimTypeKind::Float) { @@ -821,7 +818,7 @@ std::vector CodeGen::emit_builtin(const App& app, const Continuation* bui } else world().ELOG("spirv.nonsemantic.printf takes a string literal"); for (size_t i = 2; i < app.num_args() - 1; i++) { - args.push_back(emit(app.arg(i), bb)); + args.push_back(emit(app.arg(i))); } bb->ext_instruction(convert(world().unit_type()).id, builder_->imported_instrs->shader_printf, 1, args); @@ -829,23 +826,23 @@ std::vector CodeGen::emit_builtin(const App& app, const Continuation* bui THORIN_UNREACHABLE; } else if (builtin->name() == "get_global_id") { auto vector = bb->load(uvec3_t, builder_->builtins->global_id); - auto extracted = bb->vector_extract_dynamic(u32_t, vector, emit(app.arg(1), bb)); + auto extracted = bb->vector_extract_dynamic(u32_t, vector, emit(app.arg(1))); productions.push_back(bb->convert(spv::OpBitcast, i32_t, extracted)); } else if (builtin->name() == "get_local_size") { auto vector = bb->load(uvec3_t, builder_->builtins->workgroup_size); - auto extracted = bb->vector_extract_dynamic(u32_t, vector, emit(app.arg(1), bb)); + auto extracted = bb->vector_extract_dynamic(u32_t, vector, emit(app.arg(1))); productions.push_back(bb->convert(spv::OpBitcast, i32_t, extracted)); } else if (builtin->name() == "get_local_id") { auto vector = bb->load(uvec3_t, builder_->builtins->local_id); - auto extracted = bb->vector_extract_dynamic(u32_t, vector, emit(app.arg(1), bb)); + auto extracted = bb->vector_extract_dynamic(u32_t, vector, emit(app.arg(1))); productions.push_back(bb->convert(spv::OpBitcast, i32_t, extracted)); } else if (builtin->name() == "get_num_groups") { auto vector = bb->load(uvec3_t, builder_->builtins->num_workgroups); - auto extracted = bb->vector_extract_dynamic(u32_t, vector, emit(app.arg(1), bb)); + auto extracted = bb->vector_extract_dynamic(u32_t, vector, emit(app.arg(1))); productions.push_back(bb->convert(spv::OpBitcast, i32_t, extracted)); } else if (builtin->name() == "get_group_id") { auto vector = bb->load(uvec3_t, builder_->builtins->workgroup_id); - auto extracted = bb->vector_extract_dynamic(u32_t, vector, emit(app.arg(1), bb)); + auto extracted = bb->vector_extract_dynamic(u32_t, vector, emit(app.arg(1))); productions.push_back(bb->convert(spv::OpBitcast, i32_t, extracted)); } else { world().ELOG("This spir-v builtin isn't recognised: %s", builtin->name()); diff --git a/src/thorin/be/spirv/spirv.h b/src/thorin/be/spirv/spirv.h index 2239b677d..e3e880c7e 100644 --- a/src/thorin/be/spirv/spirv.h +++ b/src/thorin/be/spirv/spirv.h @@ -3,6 +3,7 @@ #include "thorin/be/spirv/spirv_builder.hpp" #include "thorin/be/codegen.h" +#include "thorin/be/emitter.h" namespace thorin::spirv { @@ -44,16 +45,11 @@ struct BasicBlockBuilder : public builder::SpvBasicBlockBuilder { }; struct FnBuilder : public builder::SpvFnBuilder { - explicit FnBuilder(CodeGen* cg, FileBuilder& file_builder); + explicit FnBuilder(FileBuilder& file_builder); FnBuilder(const FnBuilder&) = delete; - CodeGen* cg; FileBuilder& file_builder; - - const Scope* scope = nullptr; std::vector> bbs; - std::unordered_map bbs_map; - ContinuationMap labels; DefMap params; }; @@ -90,18 +86,29 @@ struct FileBuilder : public builder::SpvFileBuilder { SpvId u32_t_ { 0 }; }; -class CodeGen : public thorin::CodeGen { +class CodeGen : public thorin::CodeGen, public thorin::Emitter { public: CodeGen(Thorin& thorin, SpvTargetInfo, Cont2Config&, bool debug); void emit_stream(std::ostream& stream) override; const char* file_ext() const override { return ".spv"; } + bool is_valid(SpvId id) { + return id > 0; + } + ConvertedType convert(const Type*); + + SpvId emit_fun_decl(Continuation*); + + FnBuilder& prepare(const Scope&); + void prepare(Continuation*, FnBuilder&); + void finalize(const Scope&); + void finalize(Continuation*); protected: - void emit(const Scope& scope); - void emit_epilogue(Continuation*, BasicBlockBuilder* bb); - SpvId emit(const Def* def, BasicBlockBuilder* bb); + FnBuilder& get_fn_builder(Continuation*); + void emit_epilogue(Continuation*); + SpvId emit_bb(const Def* def, BasicBlockBuilder* bb); std::vector emit_builtin(const App&, const Continuation*, BasicBlockBuilder*); SpvId get_codom_type(const Continuation* fn); @@ -110,6 +117,7 @@ class CodeGen : public thorin::CodeGen { std::unique_ptr builder_; Continuation* entry_ = nullptr; FnBuilder* current_fn_ = nullptr; + ContinuationMap> fn_builders_; DefMap types_; DefMap defs_; const Cont2Config& kernel_config_; From 01855fd2044ad1db0ed8780c16c1b624f9092bdc Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Thu, 27 Jun 2024 17:54:44 +0200 Subject: [PATCH 232/342] builds --- src/thorin/be/spirv/spirv.cpp | 90 ++++++++++++++++++----------------- src/thorin/be/spirv/spirv.h | 11 +++-- 2 files changed, 54 insertions(+), 47 deletions(-) diff --git a/src/thorin/be/spirv/spirv.cpp b/src/thorin/be/spirv/spirv.cpp index ee732ff4b..f16b7b340 100644 --- a/src/thorin/be/spirv/spirv.cpp +++ b/src/thorin/be/spirv/spirv.cpp @@ -199,16 +199,16 @@ FnBuilder& CodeGen::get_fn_builder(thorin::Continuation* continuation) { return fn; } -FnBuilder& CodeGen::prepare(const thorin::Scope& scope) { +FnBuilder* CodeGen::prepare(const thorin::Scope& scope) { auto& fn = get_fn_builder(scope.entry()); builder_->name(fn.function_id, scope.entry()->name()); - return fn; + return &fn; } -void CodeGen::prepare(thorin::Continuation* cont, FnBuilder& fn) { - auto& bb = *fn.bbs.emplace_back(std::make_unique(fn)); +void CodeGen::prepare(thorin::Continuation* cont, FnBuilder* fn) { + auto& bb = *fn->bbs.emplace_back(std::make_unique(*fn)); cont2bb_[cont] = &bb; - fn.bbs_to_emit.emplace_back(&bb); + fn->bbs_to_emit.emplace_back(&bb); builder_->name(bb.label, cont->name().c_str()); @@ -218,8 +218,8 @@ void CodeGen::prepare(thorin::Continuation* cont, FnBuilder& fn) { // Nothing } else if (param->order() == 0) { auto param_t = convert(param->type()); - auto id = fn.parameter(param_t.id); - fn.params[param] = id; + auto id = fn->parameter(param_t.id); + fn->params[param] = id; if (param->type()->isa()) { builder_->decorate(id, spv::DecorationAliased); } @@ -422,7 +422,46 @@ void CodeGen::emit_epilogue(Continuation* continuation) { } } -SpvId CodeGen::emit_bb(const Def* def, BasicBlockBuilder* bb) { +SpvId CodeGen::emit_constant(const thorin::Def* def) { + if (auto primlit = def->isa()) { + Box box = primlit->value(); + auto type = convert(def->type()).id; + SpvId constant; + switch (primlit->primtype_tag()) { + case PrimType_bool: constant = builder_->bool_constant(type, box.get_bool()); break; + case PrimType_ps8: case PrimType_qs8: assertf(false, "not implemented yet"); + case PrimType_pu8: case PrimType_qu8: assertf(false, "not implemented yet"); + case PrimType_ps16: case PrimType_qs16: assertf(false, "not implemented yet"); + case PrimType_pu16: case PrimType_qu16: assertf(false, "not implemented yet"); + case PrimType_ps32: case PrimType_qs32: constant = builder_->constant(type, { static_cast(box.get_s32()) }); break; + case PrimType_pu32: case PrimType_qu32: constant = builder_->constant(type, { static_cast(box.get_u32()) }); break; + case PrimType_ps64: case PrimType_qs64: + case PrimType_pu64: case PrimType_qu64: { + uint64_t value = static_cast(box.get_u64()); + uint64_t upper = value >> 32U; + uint64_t lower = value & 0xFFFFFFFFU; + constant = builder_->constant(type, { (uint32_t) lower, (uint32_t) upper }); + break; + } + case PrimType_pf16: case PrimType_qf16: assertf(false, "not implemented yet"); + case PrimType_pf32: case PrimType_qf32: assertf(false, "not implemented yet"); + case PrimType_pf64: case PrimType_qf64: assertf(false, "not implemented yet"); + } + return constant; + } else if (auto param = def->isa()) { + if (is_mem(param)) return spv_none; + if (auto param_id = current_fn_->params.lookup(param)) { + assert((*param_id) != 0); + return *param_id; + } else { + auto val = cont2bb_[param->continuation()]->phis_map[param].value; + assert(val != 0); + return val; + } + } else return emit_bb(nullptr, def); +} + +SpvId CodeGen::emit_bb(BasicBlockBuilder* bb, const Def* def) { if (auto bin = def->isa()) { SpvId lhs = emit(bin->lhs()); SpvId rhs = emit(bin->rhs()); @@ -526,41 +565,6 @@ SpvId CodeGen::emit_bb(const Def* def, BasicBlockBuilder* bb) { } THORIN_UNREACHABLE; } - } else if (auto primlit = def->isa()) { - Box box = primlit->value(); - auto type = convert(def->type()).id; - SpvId constant; - switch (primlit->primtype_tag()) { - case PrimType_bool: constant = bb->file_builder.bool_constant(type, box.get_bool()); break; - case PrimType_ps8: case PrimType_qs8: assertf(false, "not implemented yet"); - case PrimType_pu8: case PrimType_qu8: assertf(false, "not implemented yet"); - case PrimType_ps16: case PrimType_qs16: assertf(false, "not implemented yet"); - case PrimType_pu16: case PrimType_qu16: assertf(false, "not implemented yet"); - case PrimType_ps32: case PrimType_qs32: constant = bb->file_builder.constant(type, { static_cast(box.get_s32()) }); break; - case PrimType_pu32: case PrimType_qu32: constant = bb->file_builder.constant(type, { static_cast(box.get_u32()) }); break; - case PrimType_ps64: case PrimType_qs64: - case PrimType_pu64: case PrimType_qu64: { - uint64_t value = static_cast(box.get_u64()); - uint64_t upper = value >> 32U; - uint64_t lower = value & 0xFFFFFFFFU; - constant = bb->file_builder.constant(type, { (uint32_t) lower, (uint32_t) upper }); - break; - } - case PrimType_pf16: case PrimType_qf16: assertf(false, "not implemented yet"); - case PrimType_pf32: case PrimType_qf32: assertf(false, "not implemented yet"); - case PrimType_pf64: case PrimType_qf64: assertf(false, "not implemented yet"); - } - return constant; - } else if (auto param = def->isa()) { - if (is_mem(param)) return spv_none; - if (auto param_id = current_fn_->params.lookup(param)) { - assert((*param_id) != 0); - return *param_id; - } else { - auto val = cont2bb_[param->continuation()]->phis_map[param].value; - assert(val != 0); - return val; - } } else if (auto variant = def->isa()) { assert(false && "TODO: rewrite"); /*auto variant_type = def->type()->as(); diff --git a/src/thorin/be/spirv/spirv.h b/src/thorin/be/spirv/spirv.h index e3e880c7e..c474b92ae 100644 --- a/src/thorin/be/spirv/spirv.h +++ b/src/thorin/be/spirv/spirv.h @@ -2,6 +2,7 @@ #define THORIN_SPIRV_H #include "thorin/be/spirv/spirv_builder.hpp" +#include "thorin/analyses/schedule.h" #include "thorin/be/codegen.h" #include "thorin/be/emitter.h" @@ -101,14 +102,16 @@ class CodeGen : public thorin::CodeGen, public thorin::Emitter emit_builtin(const App&, const Continuation*, BasicBlockBuilder*); SpvId get_codom_type(const Continuation* fn); From 3d03c0d94694eae28b597f835573792f113dc50b Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Thu, 27 Jun 2024 18:07:37 +0200 Subject: [PATCH 233/342] cleanup --- src/thorin/be/spirv/spirv.h | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/thorin/be/spirv/spirv.h b/src/thorin/be/spirv/spirv.h index c474b92ae..61b4728b9 100644 --- a/src/thorin/be/spirv/spirv.h +++ b/src/thorin/be/spirv/spirv.h @@ -87,7 +87,7 @@ struct FileBuilder : public builder::SpvFileBuilder { SpvId u32_t_ { 0 }; }; -class CodeGen : public thorin::CodeGen, public thorin::Emitter { +class CodeGen : public thorin::CodeGen, public thorin::Emitter { public: CodeGen(Thorin& thorin, SpvTargetInfo, Cont2Config&, bool debug); @@ -118,11 +118,8 @@ class CodeGen : public thorin::CodeGen, public thorin::Emitter builder_; - Continuation* entry_ = nullptr; FnBuilder* current_fn_ = nullptr; ContinuationMap> fn_builders_; - DefMap types_; - DefMap defs_; const Cont2Config& kernel_config_; }; From 912ab06701bfd855fc9109d0fb27a3a02c860b1e Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Mon, 1 Jul 2024 14:15:07 +0200 Subject: [PATCH 234/342] spirv: don't leak spirv headers downstream --- src/thorin/be/spirv/spirv.cpp | 2 +- src/thorin/be/spirv/spirv.h | 38 ++-------------------- src/thorin/be/spirv/spirv_private.h | 49 +++++++++++++++++++++++++++++ 3 files changed, 52 insertions(+), 37 deletions(-) create mode 100644 src/thorin/be/spirv/spirv_private.h diff --git a/src/thorin/be/spirv/spirv.cpp b/src/thorin/be/spirv/spirv.cpp index f16b7b340..f2106cd96 100644 --- a/src/thorin/be/spirv/spirv.cpp +++ b/src/thorin/be/spirv/spirv.cpp @@ -1,4 +1,4 @@ -#include "thorin/be/spirv/spirv.h" +#include "spirv_private.h" #include "thorin/analyses/scope.h" #include "thorin/analyses/schedule.h" diff --git a/src/thorin/be/spirv/spirv.h b/src/thorin/be/spirv/spirv.h index 61b4728b9..955e4df15 100644 --- a/src/thorin/be/spirv/spirv.h +++ b/src/thorin/be/spirv/spirv.h @@ -1,14 +1,13 @@ #ifndef THORIN_SPIRV_H #define THORIN_SPIRV_H -#include "thorin/be/spirv/spirv_builder.hpp" #include "thorin/analyses/schedule.h" #include "thorin/be/codegen.h" #include "thorin/be/emitter.h" namespace thorin::spirv { -using SpvId = builder::SpvId; +using SpvId = uint32_t; class CodeGen; @@ -35,25 +34,6 @@ struct ConvertedType { std::optional layout; }; -struct BasicBlockBuilder : public builder::SpvBasicBlockBuilder { - explicit BasicBlockBuilder(FnBuilder& fn_builder); - BasicBlockBuilder(const BasicBlockBuilder&) = delete; - - FnBuilder& fn_builder; - FileBuilder& file_builder; - std::unordered_map phis_map; - DefMap args; -}; - -struct FnBuilder : public builder::SpvFnBuilder { - explicit FnBuilder(FileBuilder& file_builder); - FnBuilder(const FnBuilder&) = delete; - - FileBuilder& file_builder; - std::vector> bbs; - DefMap params; -}; - struct Builtins { SpvId workgroup_size; SpvId num_workgroups; @@ -71,21 +51,7 @@ struct ImportedInstructions { explicit ImportedInstructions(FileBuilder&); }; -struct FileBuilder : public builder::SpvFileBuilder { - explicit FileBuilder(CodeGen* cg); - FileBuilder(const FileBuilder&) = delete; - - CodeGen* cg; - - std::unique_ptr builtins; - std::unique_ptr imported_instrs; - - SpvId u32_t(); - SpvId u32_constant(uint32_t); - -private: - SpvId u32_t_ { 0 }; -}; +struct BasicBlockBuilder; class CodeGen : public thorin::CodeGen, public thorin::Emitter { public: diff --git a/src/thorin/be/spirv/spirv_private.h b/src/thorin/be/spirv/spirv_private.h new file mode 100644 index 000000000..228f73b2d --- /dev/null +++ b/src/thorin/be/spirv/spirv_private.h @@ -0,0 +1,49 @@ +#ifndef THORIN_SPIRV_PRIVATE_H +#define THORIN_SPIRV_PRIVATE_H + +#include "spirv.h" + +#include "thorin/be/spirv/spirv_builder.hpp" + +namespace thorin::spirv { + +struct BasicBlockBuilder : public builder::SpvBasicBlockBuilder { + explicit BasicBlockBuilder(FnBuilder& fn_builder); + + BasicBlockBuilder(const BasicBlockBuilder&) = delete; + + FnBuilder& fn_builder; + FileBuilder& file_builder; + std::unordered_map phis_map; + DefMap args; +}; + +struct FnBuilder : public builder::SpvFnBuilder { + explicit FnBuilder(FileBuilder& file_builder); + + FnBuilder(const FnBuilder&) = delete; + + FileBuilder& file_builder; + std::vector> bbs; + DefMap params; +}; + +struct FileBuilder : public builder::SpvFileBuilder { + explicit FileBuilder(CodeGen* cg); + FileBuilder(const FileBuilder&) = delete; + + CodeGen* cg; + + std::unique_ptr builtins; + std::unique_ptr imported_instrs; + + SpvId u32_t(); + SpvId u32_constant(uint32_t); + +private: + SpvId u32_t_ { 0 }; +}; + +} + +#endif // THORIN_SPIRV_PRIVATE_H From 6ebf90e7a03eb6a0412f02c4c6278e0be9c0eab7 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Mon, 1 Jul 2024 14:15:33 +0200 Subject: [PATCH 235/342] spirv: advertise in thorin-config.cmake.in --- cmake/thorin-config.cmake.in | 1 + 1 file changed, 1 insertion(+) diff --git a/cmake/thorin-config.cmake.in b/cmake/thorin-config.cmake.in index 3a53346dd..641f33e0c 100644 --- a/cmake/thorin-config.cmake.in +++ b/cmake/thorin-config.cmake.in @@ -32,6 +32,7 @@ set(Thorin_HAS_JSON_SUPPORT @THORIN_ENABLE_JSON@) set(Thorin_HAS_LLVM_SUPPORT @LLVM_FOUND@) set(Thorin_HAS_RV_SUPPORT @RV_FOUND@) set(Thorin_HAS_SHADY_SUPPORT @THORIN_ENABLE_SHADY@) +set(Thorin_HAS_SPIRV_SUPPORT @THORIN_ENABLE_SPIRV@) set(AnyDSL_LLVM_LINK_SHARED @AnyDSL_LLVM_LINK_SHARED@) if(Thorin_HAS_LLVM_SUPPORT) From e371c8d4cf9480227c2e15d27a69c5728b66658a Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Mon, 1 Jul 2024 14:43:04 +0200 Subject: [PATCH 236/342] spirv: make kernel_config optional --- src/thorin/be/spirv/spirv.cpp | 10 ++++++---- src/thorin/be/spirv/spirv.h | 4 ++-- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/thorin/be/spirv/spirv.cpp b/src/thorin/be/spirv/spirv.cpp index f2106cd96..add9e2314 100644 --- a/src/thorin/be/spirv/spirv.cpp +++ b/src/thorin/be/spirv/spirv.cpp @@ -142,7 +142,7 @@ ImportedInstructions::ImportedInstructions(FileBuilder& builder) { shader_printf = builder.extended_import("NonSemantic.DebugPrintf"); } -CodeGen::CodeGen(Thorin& thorin, SpvTargetInfo target_info, Cont2Config& kernel_config, bool debug) +CodeGen::CodeGen(Thorin& thorin, SpvTargetInfo target_info, bool debug, const Cont2Config* kernel_config) : thorin::CodeGen(thorin, debug), target_info_(target_info), kernel_config_(kernel_config) {} @@ -162,9 +162,11 @@ void CodeGen::emit_stream(std::ostream& out) { } for (auto& cont : world().copy_continuations()) { - if (cont->is_exported()) { - assert(defs_.contains(cont) && kernel_config_.contains(cont)); - auto config = kernel_config_.find(cont); + assert(defs_.contains(cont)); + if (cont->is_exported() && kernel_config_) { + auto config = kernel_config_->find(cont); + if (config == kernel_config_->end()) + continue; SpvId callee = defs_[cont]; diff --git a/src/thorin/be/spirv/spirv.h b/src/thorin/be/spirv/spirv.h index 955e4df15..714497a79 100644 --- a/src/thorin/be/spirv/spirv.h +++ b/src/thorin/be/spirv/spirv.h @@ -55,7 +55,7 @@ struct BasicBlockBuilder; class CodeGen : public thorin::CodeGen, public thorin::Emitter { public: - CodeGen(Thorin& thorin, SpvTargetInfo, Cont2Config&, bool debug); + CodeGen(Thorin& thorin, SpvTargetInfo, bool debug, const Cont2Config* = nullptr); void emit_stream(std::ostream& stream) override; const char* file_ext() const override { return ".spv"; } @@ -86,7 +86,7 @@ class CodeGen : public thorin::CodeGen, public thorin::Emitter builder_; FnBuilder* current_fn_ = nullptr; ContinuationMap> fn_builders_; - const Cont2Config& kernel_config_; + const Cont2Config* kernel_config_; }; } From c4969b3511ab23fb8f2b11ea622ef66007d55c6d Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Mon, 1 Jul 2024 14:48:53 +0200 Subject: [PATCH 237/342] add spirv_types to the index --- src/thorin/be/spirv/spirv_types.cpp | 225 ++++++++++++++++++++++++++++ 1 file changed, 225 insertions(+) create mode 100644 src/thorin/be/spirv/spirv_types.cpp diff --git a/src/thorin/be/spirv/spirv_types.cpp b/src/thorin/be/spirv/spirv_types.cpp new file mode 100644 index 000000000..51bb71e8c --- /dev/null +++ b/src/thorin/be/spirv/spirv_types.cpp @@ -0,0 +1,225 @@ +#include "spirv_private.h" +#include "thorin/util/stream.h" +#include "thorin/util/utility.h" + +namespace thorin::spirv { + +ConvertedType CodeGen::convert(const Type* type) { + // Spir-V requires each primitive type to be "unique", it doesn't allow for example two 32-bit signed integer types. + // Therefore we must enforce that precise/quick types map to the same thing. + switch (type->tag()) { +#define THORIN_Q_TYPE(T, M) \ + case PrimType_##T: \ + type = world().prim_type(PrimType_p##M, type->as()->length()); \ + break; +#include "thorin/tables/primtypetable.h" +#undef THORIN_Q_TYPE + default: break; + } + + if (auto iter = types_.find(type); iter != types_.end()) + return iter->second; + + ConvertedType converted = { 0, std::nullopt }; + + switch (type->tag()) { + // Boolean types are typically packed intelligently when declaring in local variables, however with vanilla Vulkan 1.0 they can only be represented via 32-bit integers + // Using extensions, we could use 16 or 8-bit ints instead + // We can also pack them inside structures using bit-twiddling tricks, if the need arises + // Note: this only affects storing booleans inside structures, for regular variables the actual spir-v bool type is used. + case Node_PrimType_bool: + converted.id = builder_->declare_bool_type(); + converted.layout = { 1, 1 }; + break; + case Node_PrimType_ps8: + converted.id = builder_->declare_int_type(8, true); + converted.layout = { 1, 1 }; + break; + case Node_PrimType_pu8: + converted.id = builder_->declare_int_type(8, false); + converted.layout = { 1, 1 }; + break; + case Node_PrimType_ps16: + converted.id = builder_->declare_int_type(16, true); + converted.layout = { 2, 2 }; + break; + case Node_PrimType_pu16: + converted.id = builder_->declare_int_type(16, false); + converted.layout = { 2, 2 }; + break; + case Node_PrimType_ps32: + converted.id = builder_->declare_int_type(32, true ); + converted.layout = { 4, 4 }; + break; + case Node_PrimType_pu32: + converted.id = builder_->declare_int_type(32, false); + converted.layout = { 4, 4 }; + break; + case Node_PrimType_ps64: + converted.id = builder_->declare_int_type(64, true); + converted.layout = { 8, 8 }; + break; + case Node_PrimType_pu64: + converted.id = builder_->declare_int_type(64, false); + converted.layout = { 8, 8 }; + break; + case Node_PrimType_pf16: + converted.id = builder_->declare_float_type(16); + converted.layout = { 2, 2 }; + break; + case Node_PrimType_pf32: + converted.id = builder_->declare_float_type(32); + converted.layout = { 4, 4 }; + break; + case Node_PrimType_pf64: + converted.id = builder_->declare_float_type(64); + converted.layout = { 8, 8 }; + break; + case Node_PtrType: { + auto ptr = type->as(); + spv::StorageClass storage_class; + switch (ptr->addr_space()) { + case AddrSpace::Function: storage_class = spv::StorageClassFunction; break; + case AddrSpace::Private: storage_class = spv::StorageClassPrivate; break; + case AddrSpace::Push: storage_class = spv::StorageClassPushConstant; break; + case AddrSpace::Global: storage_class = spv::StorageClassCrossWorkgroup; break; + case AddrSpace::Generic: storage_class = spv::StorageClassGeneric; break; + default: + assert(false && "This address space is not supported"); + break; + } + + const Type* pointee = ptr->pointee(); + while (auto arr = pointee->isa()) + pointee = arr->elem_type(); + converted.id = builder_->declare_ptr_type(storage_class, convert(pointee).id); + converted.layout = { target_info_.mem_layout.pointer_size, target_info_.mem_layout.pointer_size }; + break; + } + case Node_IndefiniteArrayType: { + world().ELOG("Using indefinite types directly is not permitted - they may only be pointed to"); + std::abort(); + } + case Node_DefiniteArrayType: { + auto array = type->as(); + auto element = convert(array->elem_type()); + converted.id = builder_->declare_array_type(element.id, builder_->u32_constant(array->dim())); + converted.layout = { element.layout->size * array->dim(), element.layout->alignment }; + break; + } + + case Node_ClosureType: + case Node_FnType: { + // extract "return" type, collect all other types + auto fn = type->as(); + SpvId ret = 0; + std::vector ops; + for (auto op : fn->types()) { + if (op->isa() || op == world().unit_type()) + continue; + auto fn_type = op->isa(); + if (fn_type && !op->isa()) { + assert(!ret && "only one 'return' supported"); + std::vector ret_types; + for (auto fn_op : fn_type->types()) { + if (fn_op->isa() || fn_op == world().unit_type()) + continue; + ret_types.push_back(convert(fn_op).id); + } + if (ret_types.empty()) ret = convert(world().tuple_type({})).id; + else if (ret_types.size() == 1) ret = ret_types.back(); + else assert(false && "Didn't we refactor this out yet by making functions single-argument ?"); + } else + ops.push_back(convert(op).id); + } + assert(ret); + + if (type->tag() == Node_FnType) { + converted.id = builder_->declare_fn_type(ops, ret); + } else { + assert(false && "TODO: handle closure mess"); + THORIN_UNREACHABLE; + } + break; + } + + case Node_Vector: { + auto vec = type->as(); + assert(vec->length() > 1); + auto component = vec->scalarize(); + auto conv_comp = convert(component); + converted.id = builder_->declare_vector_type(conv_comp.id, (uint32_t)vec->length()); + converted.layout = conv_comp.layout; + converted.layout->size *= vec->length(); + break; + } + + case Node_StructType: + case Node_TupleType: { + std::vector spv_types; + size_t total_serialized_size = 0; + converted.layout = { 0, 0 }; + for (auto member : type->ops()) { + auto member_type = member->as(); + if (member_type == world().unit_type() || member_type == world().mem_type()) continue; + auto converted_member_type = convert(member_type); + assert(converted_member_type.layout); + spv_types.push_back(converted_member_type.id); + converted.layout->alignment = std::max(converted.layout->alignment, converted_member_type.layout->alignment); + converted.layout->size = pad(converted.layout->size + converted_member_type.layout->size, converted.layout->alignment); + } + if (total_serialized_size == 0) { + outf("this one is void"); + converted.id = builder_->declare_void_type(); + converted.layout = std::nullopt; + break; + } + + converted.id = builder_->declare_struct_type(spv_types); + builder_->name(converted.id, type->to_string()); + break; + } + + case Node_VariantType: { + assert(type->num_ops() > 0 && "empty variants not supported"); + auto tag_type = world().type_pu32(); + SpvId converted_tag_type = convert(tag_type).id; + + size_t max_serialized_size = 0; + for (auto member : type->as()->types()) { + auto member_type = member->as(); + if (member_type == world().unit_type() || member_type == world().mem_type()) continue; + auto converted_member_type = convert(member_type); + assert(converted_member_type.layout); + if (converted_member_type.layout->size > max_serialized_size) + max_serialized_size = converted_member_type.layout->size; + } + + if (max_serialized_size > 0) { + auto payload_type = world().definite_array_type(world().type_pu8(), max_serialized_size); + auto struct_t = world().struct_type(type->name(), 2); + struct_t->set_op(0, tag_type); + struct_t->set_op(1, payload_type); + return convert(struct_t); + } else { + // We keep this useless level of struct so the rest of the code doesn't need a special path to extract the tag + auto struct_t = world().struct_type(type->name(), 1); + struct_t->set_op(0, tag_type); + return convert(struct_t); + } + THORIN_UNREACHABLE; + } + + case Node_MemType: { + assert(false && "MemType cannot be converted to SPIR-V"); + } + + default: + THORIN_UNREACHABLE; + } + + types_[type] = converted; + return converted; +} + +} From e77abf90438564ac9b1090cfb1e3fa9228836b9f Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Mon, 1 Jul 2024 14:57:11 +0200 Subject: [PATCH 238/342] spirv: adjust header to not rely on unavailable definitions --- src/thorin/be/spirv/spirv.cpp | 13 +++++++------ src/thorin/be/spirv/spirv.h | 4 +--- src/thorin/be/spirv/spirv_private.h | 3 +++ 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/thorin/be/spirv/spirv.cpp b/src/thorin/be/spirv/spirv.cpp index add9e2314..daa23f920 100644 --- a/src/thorin/be/spirv/spirv.cpp +++ b/src/thorin/be/spirv/spirv.cpp @@ -147,7 +147,8 @@ CodeGen::CodeGen(Thorin& thorin, SpvTargetInfo target_info, bool debug, const Co {} void CodeGen::emit_stream(std::ostream& out) { - builder_ = std::make_unique(this); + FileBuilder builder(this); + builder_ = &builder; builder_->builtins = std::make_unique(*builder_); builder_->imported_instrs = std::make_unique(*builder_); @@ -191,11 +192,11 @@ SpvId CodeGen::emit_fun_decl(thorin::Continuation* continuation) { } FnBuilder& CodeGen::get_fn_builder(thorin::Continuation* continuation) { - if (auto found = fn_builders_.find(continuation); found != fn_builders_.end()) { + if (auto found = builder_->fn_builders_.find(continuation); found != builder_->fn_builders_.end()) { return *found->second; } - auto& fn = *(fn_builders_[continuation] = std::make_unique(*builder_)); + auto& fn = *(builder_->fn_builders_[continuation] = std::make_unique(*builder_)); fn.fn_type = convert(entry_->type()).id; fn.fn_ret_type = get_codom_type(entry_); return fn; @@ -252,7 +253,7 @@ void CodeGen::finalize(thorin::Continuation* cont) { } void CodeGen::finalize(const thorin::Scope&) { - builder_->define_function(*current_fn_); + builder_->define_function(*builder_->current_fn_); } SpvId CodeGen::get_codom_type(const Continuation* fn) { @@ -303,7 +304,7 @@ void CodeGen::emit_epilogue(Continuation* continuation) { switch (values.size()) { case 0: bb->return_void(); break; case 1: bb->return_value(values[0]); break; - default: bb->return_value(bb->composite(current_fn_->fn_ret_type, values)); + default: bb->return_value(bb->composite(builder_->current_fn_->fn_ret_type, values)); } } else if (auto dst_cont = app.callee()->isa_nom(); dst_cont && dst_cont->is_basicblock()) { // ordinary jump int index = -1; @@ -452,7 +453,7 @@ SpvId CodeGen::emit_constant(const thorin::Def* def) { return constant; } else if (auto param = def->isa()) { if (is_mem(param)) return spv_none; - if (auto param_id = current_fn_->params.lookup(param)) { + if (auto param_id = builder_->current_fn_->params.lookup(param)) { assert((*param_id) != 0); return *param_id; } else { diff --git a/src/thorin/be/spirv/spirv.h b/src/thorin/be/spirv/spirv.h index 714497a79..05d27f628 100644 --- a/src/thorin/be/spirv/spirv.h +++ b/src/thorin/be/spirv/spirv.h @@ -83,9 +83,7 @@ class CodeGen : public thorin::CodeGen, public thorin::Emitter builder_; - FnBuilder* current_fn_ = nullptr; - ContinuationMap> fn_builders_; + FileBuilder* builder_; const Cont2Config* kernel_config_; }; diff --git a/src/thorin/be/spirv/spirv_private.h b/src/thorin/be/spirv/spirv_private.h index 228f73b2d..745b1ea0d 100644 --- a/src/thorin/be/spirv/spirv_private.h +++ b/src/thorin/be/spirv/spirv_private.h @@ -37,6 +37,9 @@ struct FileBuilder : public builder::SpvFileBuilder { std::unique_ptr builtins; std::unique_ptr imported_instrs; + FnBuilder* current_fn_ = nullptr; + ContinuationMap> fn_builders_; + SpvId u32_t(); SpvId u32_constant(uint32_t); From 559ef0c31946f048b355add77956db2dbfe9416f Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Mon, 1 Jul 2024 15:10:56 +0200 Subject: [PATCH 239/342] spirv: emit parameters/phis properly --- src/thorin/be/spirv/spirv.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/thorin/be/spirv/spirv.cpp b/src/thorin/be/spirv/spirv.cpp index daa23f920..d0ac89908 100644 --- a/src/thorin/be/spirv/spirv.cpp +++ b/src/thorin/be/spirv/spirv.cpp @@ -219,9 +219,11 @@ void CodeGen::prepare(thorin::Continuation* cont, FnBuilder* fn) { for (auto param : cont->params()) { if (is_mem(param) || is_unit(param)) { // Nothing + defs_[param] = 0; } else if (param->order() == 0) { auto param_t = convert(param->type()); auto id = fn->parameter(param_t.id); + defs_[param] = id; fn->params[param] = id; if (param->type()->isa()) { builder_->decorate(id, spv::DecorationAliased); @@ -232,13 +234,16 @@ void CodeGen::prepare(thorin::Continuation* cont, FnBuilder* fn) { for (auto param : cont->params()) { if (is_mem(param) || is_unit(param)) { // Nothing + defs_[param] = 0; } else { // OpPhi requires the full list of predecessors (values, labels) // We don't have that yet! But we will need the Phi node identifier to build the basic blocks ... // To solve this we generate an id for the phi node now, but defer emission of it to a later stage auto type = convert(param->type()).id; assert(type != 0); - bb.phis_map[param] = { type, builder_->generate_fresh_id(), {} }; + auto id = builder_->generate_fresh_id(); + defs_[param] = id; + bb.phis_map[param] = { type, id, {} }; } } } From aad02da1cc962ad4ce77aeb4c42361fed1790355 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Mon, 1 Jul 2024 15:13:51 +0200 Subject: [PATCH 240/342] spirv: deal with untangible args properly --- src/thorin/be/spirv/spirv.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/thorin/be/spirv/spirv.cpp b/src/thorin/be/spirv/spirv.cpp index d0ac89908..74ca3eadd 100644 --- a/src/thorin/be/spirv/spirv.cpp +++ b/src/thorin/be/spirv/spirv.cpp @@ -315,8 +315,11 @@ void CodeGen::emit_epilogue(Continuation* continuation) { int index = -1; for (auto& arg : app.args()) { index++; + if (is_mem(arg) || is_unit(arg)) { + emit_unsafe(arg); + continue; + } auto val = emit(arg); - if (is_mem(arg) || is_unit(arg)) continue; bb->args[arg] = val; auto* param = dst_cont->param(index); auto& phi = cont2bb_[dst_cont]->phis_map[param]; @@ -362,8 +365,11 @@ void CodeGen::emit_epilogue(Continuation* continuation) { for (auto arg : app.args()) { if (arg->order() == 0) { auto arg_type = arg->type(); + if (arg_type == world().unit_type() || arg_type == world().mem_type()) { + emit_unsafe(arg); + continue; + } auto arg_val = emit(arg); - if (arg_type == world().unit_type() || arg_type == world().mem_type()) continue; call_args.push_back(arg_val); } else { assert(!ret_arg); From abf4b6a84ed3e2006114340d97ca34b838a3bce0 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Mon, 1 Jul 2024 15:14:08 +0200 Subject: [PATCH 241/342] spirv: emit_bb redirects to emit_constant --- src/thorin/be/spirv/spirv.cpp | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/thorin/be/spirv/spirv.cpp b/src/thorin/be/spirv/spirv.cpp index 74ca3eadd..51f41c2b5 100644 --- a/src/thorin/be/spirv/spirv.cpp +++ b/src/thorin/be/spirv/spirv.cpp @@ -300,9 +300,11 @@ void CodeGen::emit_epilogue(Continuation* continuation) { for (auto arg : app.args()) { assert(arg->order() == 0); - auto val = emit(arg); - if (is_mem(arg) || is_unit(arg)) + if (is_mem(arg) || is_unit(arg)) { + emit_unsafe(arg); continue; + } + auto val = emit(arg); values.emplace_back(val); } @@ -472,7 +474,9 @@ SpvId CodeGen::emit_constant(const thorin::Def* def) { assert(val != 0); return val; } - } else return emit_bb(nullptr, def); + } + + assertf(false, "Incomplete emit(def) definition"); } SpvId CodeGen::emit_bb(BasicBlockBuilder* bb, const Def* def) { @@ -815,6 +819,10 @@ SpvId CodeGen::emit_bb(BasicBlockBuilder* bb, const Def* def) { } else if (def->isa()) { return bb->undef(convert(def->type()).id); } + + if (!def->has_dep(Dep::Param)) + return emit_constant(def); + assertf(false, "Incomplete emit(def) definition"); } From ae4207520fb0e19dfb457a887435a93fc46ed546 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Mon, 1 Jul 2024 15:18:35 +0200 Subject: [PATCH 242/342] spirv: deal with constants of all primtypes --- src/thorin/be/spirv/spirv.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/thorin/be/spirv/spirv.cpp b/src/thorin/be/spirv/spirv.cpp index 51f41c2b5..f06639b3f 100644 --- a/src/thorin/be/spirv/spirv.cpp +++ b/src/thorin/be/spirv/spirv.cpp @@ -445,11 +445,13 @@ SpvId CodeGen::emit_constant(const thorin::Def* def) { SpvId constant; switch (primlit->primtype_tag()) { case PrimType_bool: constant = builder_->bool_constant(type, box.get_bool()); break; - case PrimType_ps8: case PrimType_qs8: assertf(false, "not implemented yet"); - case PrimType_pu8: case PrimType_qu8: assertf(false, "not implemented yet"); - case PrimType_ps16: case PrimType_qs16: assertf(false, "not implemented yet"); - case PrimType_pu16: case PrimType_qu16: assertf(false, "not implemented yet"); - case PrimType_ps32: case PrimType_qs32: constant = builder_->constant(type, { static_cast(box.get_s32()) }); break; + case PrimType_ps8: case PrimType_qs8: + case PrimType_pu8: case PrimType_qu8: constant = builder_->constant(type, { static_cast(box.get_u8()) }); break; + case PrimType_ps16: case PrimType_qs16: + case PrimType_pu16: case PrimType_qu16: + case PrimType_pf16: case PrimType_qf16: constant = builder_->constant(type, { static_cast(box.get_u16()) }); break; + case PrimType_pf32: case PrimType_qf32: + case PrimType_ps32: case PrimType_qs32: case PrimType_pu32: case PrimType_qu32: constant = builder_->constant(type, { static_cast(box.get_u32()) }); break; case PrimType_ps64: case PrimType_qs64: case PrimType_pu64: case PrimType_qu64: { @@ -459,8 +461,6 @@ SpvId CodeGen::emit_constant(const thorin::Def* def) { constant = builder_->constant(type, { (uint32_t) lower, (uint32_t) upper }); break; } - case PrimType_pf16: case PrimType_qf16: assertf(false, "not implemented yet"); - case PrimType_pf32: case PrimType_qf32: assertf(false, "not implemented yet"); case PrimType_pf64: case PrimType_qf64: assertf(false, "not implemented yet"); } return constant; From 6daf1eac7483e5d348558f9e66c6b03e83838f88 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Mon, 1 Jul 2024 16:35:26 +0200 Subject: [PATCH 243/342] spirv_builder: improved extended instructions handling --- src/thorin/be/spirv/spirv.cpp | 3 +- src/thorin/be/spirv/spirv_builder.hpp | 48 +++++++++++++++++++-------- 2 files changed, 36 insertions(+), 15 deletions(-) diff --git a/src/thorin/be/spirv/spirv.cpp b/src/thorin/be/spirv/spirv.cpp index f06639b3f..3ec8b4139 100644 --- a/src/thorin/be/spirv/spirv.cpp +++ b/src/thorin/be/spirv/spirv.cpp @@ -847,7 +847,8 @@ std::vector CodeGen::emit_builtin(const App& app, const Continuation* bui args.push_back(emit(app.arg(i))); } - bb->ext_instruction(convert(world().unit_type()).id, builder_->imported_instrs->shader_printf, 1, args); + builder_->extension("SPV_KHR_non_semantic_info"); + bb->ext_instruction(convert(world().unit_type()).id, { "NonSemantic.DebugPrintf", 1}, args); } else if (builtin->name() == "get_work_dim") { THORIN_UNREACHABLE; } else if (builtin->name() == "get_global_id") { diff --git a/src/thorin/be/spirv/spirv_builder.hpp b/src/thorin/be/spirv/spirv_builder.hpp index 6a600b7a6..f110cc47f 100644 --- a/src/thorin/be/spirv/spirv_builder.hpp +++ b/src/thorin/be/spirv/spirv_builder.hpp @@ -17,6 +17,11 @@ struct SpvBasicBlockBuilder; struct SpvFnBuilder; struct SpvFileBuilder; +struct ExtendedInstruction { + const char* set_name; + uint32_t id; +}; + inline int div_roundup(int a, int b) { if (a % b == 0) return a / b; @@ -242,17 +247,7 @@ struct SpvBasicBlockBuilder : public SpvSectionBuilder { return id; } - SpvId ext_instruction(SpvId return_type, SpvId set, uint32_t instruction, std::vector arguments) { - op(spv::Op::OpExtInst, 5 + arguments.size()); - auto id = generate_fresh_id(); - ref_id(return_type); - ref_id(id); - ref_id(set); - literal_int(instruction); - for (auto a : arguments) - ref_id(a); - return id; - } + SpvId ext_instruction(SpvId return_type, ExtendedInstruction instr, std::vector arguments); void return_void() { op(spv::Op::OpReturn, 1); @@ -269,6 +264,19 @@ struct SpvBasicBlockBuilder : public SpvSectionBuilder { private: SpvId generate_fresh_id(); + +protected: + SpvId ext_instruction(SpvId return_type, SpvId set, uint32_t instruction, std::vector arguments) { + op(spv::Op::OpExtInst, 5 + arguments.size()); + auto id = generate_fresh_id(); + ref_id(return_type); + ref_id(id); + ref_id(set); + literal_int(instruction); + for (auto a : arguments) + ref_id(a); + return id; + } }; struct SpvFnBuilder { @@ -581,17 +589,22 @@ struct SpvFileBuilder { extensions.literal_name(name); } + spv::AddressingModel addressing_model = spv::AddressingModel::AddressingModelLogical; + spv::MemoryModel memory_model = spv::MemoryModel::MemoryModelSimple; + +protected: SpvId extended_import(std::string name) { + auto found = extended_instruction_sets.find(name); + if (found != extended_instruction_sets.end()) + return found->second; ext_inst_import.op(spv::Op::OpExtInstImport, 2 + div_roundup(name.size() + 1, 4)); auto id = generate_fresh_id(); ext_inst_import.ref_id(id); ext_inst_import.literal_name(name); + extended_instruction_sets[name] = id; return id; } - spv::AddressingModel addressing_model = spv::AddressingModel::AddressingModelLogical; - spv::MemoryModel memory_model = spv::MemoryModel::MemoryModelSimple; - private: std::ostream* output_ = nullptr; uint32_t bound = 1; @@ -612,6 +625,7 @@ struct SpvFileBuilder { // SPIR-V disallows duplicate non-aggregate type declarations, we protect against these with this std::unordered_map unique_decls; + std::unordered_map extended_instruction_sets; void output_word_le(uint32_t word) { output_->put((word >> 0) & 0xFFu); @@ -653,6 +667,8 @@ struct SpvFileBuilder { output_section(fn_decls); output_section(fn_defs); } + + friend SpvBasicBlockBuilder; }; inline SpvId SpvBasicBlockBuilder::generate_fresh_id() { @@ -663,4 +679,8 @@ inline SpvId SpvFnBuilder::generate_fresh_id() { return file_builder->generate_fresh_id(); } +SpvId SpvBasicBlockBuilder::ext_instruction(SpvId return_type, ExtendedInstruction instr, std::vector arguments) { + return ext_instruction(return_type, file_builder.extended_import(instr.set_name), instr.id, arguments); +} + } From 19c0f2f77f1f59b70af6e158c10876cfff38504f Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Mon, 1 Jul 2024 16:38:32 +0200 Subject: [PATCH 244/342] spirv: cache declared capabilities and extensions --- src/thorin/be/spirv/spirv_builder.hpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/thorin/be/spirv/spirv_builder.hpp b/src/thorin/be/spirv/spirv_builder.hpp index f110cc47f..c8886979e 100644 --- a/src/thorin/be/spirv/spirv_builder.hpp +++ b/src/thorin/be/spirv/spirv_builder.hpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include @@ -580,13 +581,21 @@ struct SpvFileBuilder { } void capability(spv::Capability cap) { + auto found = capabilities_set.find(cap); + if (found != capabilities_set.end()) + return; capabilities.op(spv::Op::OpCapability, 2); capabilities.data_.push_back(cap); + capabilities_set.insert(cap); } void extension(std::string name) { + auto found = extensions_set.find(name); + if (found != extensions_set.end()) + return; extensions.op(spv::Op::OpExtension, 1 + div_roundup(name.size() + 1, 4)); extensions.literal_name(name); + extensions_set.insert(name); } spv::AddressingModel addressing_model = spv::AddressingModel::AddressingModelLogical; @@ -626,6 +635,8 @@ struct SpvFileBuilder { // SPIR-V disallows duplicate non-aggregate type declarations, we protect against these with this std::unordered_map unique_decls; std::unordered_map extended_instruction_sets; + std::unordered_set capabilities_set; + std::unordered_set extensions_set; void output_word_le(uint32_t word) { output_->put((word >> 0) & 0xFFu); From 9ada962929fb9bc07ff65773cd7d1dc1e6c2d475 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Mon, 1 Jul 2024 17:05:39 +0200 Subject: [PATCH 245/342] spirv: fix builder multiple definition errors --- src/thorin/be/spirv/spirv_builder.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/thorin/be/spirv/spirv_builder.hpp b/src/thorin/be/spirv/spirv_builder.hpp index c8886979e..2e741f2fe 100644 --- a/src/thorin/be/spirv/spirv_builder.hpp +++ b/src/thorin/be/spirv/spirv_builder.hpp @@ -690,7 +690,7 @@ inline SpvId SpvFnBuilder::generate_fresh_id() { return file_builder->generate_fresh_id(); } -SpvId SpvBasicBlockBuilder::ext_instruction(SpvId return_type, ExtendedInstruction instr, std::vector arguments) { +inline SpvId SpvBasicBlockBuilder::ext_instruction(SpvId return_type, ExtendedInstruction instr, std::vector arguments) { return ext_instruction(return_type, file_builder.extended_import(instr.set_name), instr.id, arguments); } From a1509ab36c21f757d9ec3218517217b56493ae7e Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Mon, 1 Jul 2024 17:06:05 +0200 Subject: [PATCH 246/342] spirv: remove ImportedInstructions --- src/thorin/be/spirv/spirv.cpp | 6 ------ src/thorin/be/spirv/spirv.h | 6 ------ src/thorin/be/spirv/spirv_private.h | 1 - 3 files changed, 13 deletions(-) diff --git a/src/thorin/be/spirv/spirv.cpp b/src/thorin/be/spirv/spirv.cpp index 3ec8b4139..ed5531bee 100644 --- a/src/thorin/be/spirv/spirv.cpp +++ b/src/thorin/be/spirv/spirv.cpp @@ -137,11 +137,6 @@ Builtins::Builtins(FileBuilder& builder) { builder.name(local_invocation_index, "BuiltInLocalInvocationIndex"); } -ImportedInstructions::ImportedInstructions(FileBuilder& builder) { - builder.extension("SPV_KHR_non_semantic_info"); - shader_printf = builder.extended_import("NonSemantic.DebugPrintf"); -} - CodeGen::CodeGen(Thorin& thorin, SpvTargetInfo target_info, bool debug, const Cont2Config* kernel_config) : thorin::CodeGen(thorin, debug), target_info_(target_info), kernel_config_(kernel_config) {} @@ -151,7 +146,6 @@ void CodeGen::emit_stream(std::ostream& out) { builder_ = &builder; builder_->builtins = std::make_unique(*builder_); - builder_->imported_instrs = std::make_unique(*builder_); ScopesForest forest(world()); forest.for_each([&](const Scope& scope) { emit_scope(scope, forest); }); diff --git a/src/thorin/be/spirv/spirv.h b/src/thorin/be/spirv/spirv.h index 05d27f628..bf93c1c0c 100644 --- a/src/thorin/be/spirv/spirv.h +++ b/src/thorin/be/spirv/spirv.h @@ -45,12 +45,6 @@ struct Builtins { explicit Builtins(FileBuilder&); }; -struct ImportedInstructions { - SpvId shader_printf; - - explicit ImportedInstructions(FileBuilder&); -}; - struct BasicBlockBuilder; class CodeGen : public thorin::CodeGen, public thorin::Emitter { diff --git a/src/thorin/be/spirv/spirv_private.h b/src/thorin/be/spirv/spirv_private.h index 745b1ea0d..9af46295b 100644 --- a/src/thorin/be/spirv/spirv_private.h +++ b/src/thorin/be/spirv/spirv_private.h @@ -35,7 +35,6 @@ struct FileBuilder : public builder::SpvFileBuilder { CodeGen* cg; std::unique_ptr builtins; - std::unique_ptr imported_instrs; FnBuilder* current_fn_ = nullptr; ContinuationMap> fn_builders_; From 2566aecc3c1f105001a69c2cbf396f78d746dd08 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Mon, 1 Jul 2024 17:08:27 +0200 Subject: [PATCH 247/342] spirv: initial support for MathOps --- src/thorin/CMakeLists.txt | 1 + src/thorin/be/spirv/spirv.cpp | 3 ++ src/thorin/be/spirv/spirv.h | 2 + src/thorin/be/spirv/spirv_instructions.cpp | 55 ++++++++++++++++++++++ 4 files changed, 61 insertions(+) create mode 100644 src/thorin/be/spirv/spirv_instructions.cpp diff --git a/src/thorin/CMakeLists.txt b/src/thorin/CMakeLists.txt index 201d73665..01044d98f 100644 --- a/src/thorin/CMakeLists.txt +++ b/src/thorin/CMakeLists.txt @@ -128,6 +128,7 @@ if(THORIN_ENABLE_SPIRV) list(APPEND THORIN_SOURCES be/spirv/spirv.cpp be/spirv/spirv_types.cpp + be/spirv/spirv_instructions.cpp be/spirv/spirv.h ) endif() diff --git a/src/thorin/be/spirv/spirv.cpp b/src/thorin/be/spirv/spirv.cpp index ed5531bee..17c7d414f 100644 --- a/src/thorin/be/spirv/spirv.cpp +++ b/src/thorin/be/spirv/spirv.cpp @@ -474,6 +474,9 @@ SpvId CodeGen::emit_constant(const thorin::Def* def) { } SpvId CodeGen::emit_bb(BasicBlockBuilder* bb, const Def* def) { + if (auto mathop = def->isa()) + return emit_mathop(bb, *mathop); + if (auto bin = def->isa()) { SpvId lhs = emit(bin->lhs()); SpvId rhs = emit(bin->rhs()); diff --git a/src/thorin/be/spirv/spirv.h b/src/thorin/be/spirv/spirv.h index bf93c1c0c..de7026bd8 100644 --- a/src/thorin/be/spirv/spirv.h +++ b/src/thorin/be/spirv/spirv.h @@ -74,6 +74,8 @@ class CodeGen : public thorin::CodeGen, public thorin::Emitter emit_builtin(const App&, const Continuation*, BasicBlockBuilder*); + SpvId emit_mathop(BasicBlockBuilder* bb, const MathOp& op); + SpvId get_codom_type(const Continuation* fn); SpvTargetInfo target_info_; diff --git a/src/thorin/be/spirv/spirv_instructions.cpp b/src/thorin/be/spirv/spirv_instructions.cpp new file mode 100644 index 000000000..509179e7b --- /dev/null +++ b/src/thorin/be/spirv/spirv_instructions.cpp @@ -0,0 +1,55 @@ +#include "spirv_private.h" + +namespace thorin::spirv { + +struct SpirMathOps { +#define THORIN_MATHOP(mathop_name) builder::ExtendedInstruction mathop_name; +#include "thorin/tables/mathoptable.h" +#undef THORIN_MATHOP +}; + +#include + +SpirMathOps opencl_std = { + .fabs = { "OpenCL.std", OpenCLLIB::Fabs }, + .copysign = { "OpenCL.std", OpenCLLIB::Sign }, + .round = { "OpenCL.std", OpenCLLIB::Round }, + .floor = { "OpenCL.std", OpenCLLIB::Floor }, + .ceil = { "OpenCL.std", OpenCLLIB::Ceil }, + .fmin = { "OpenCL.std", OpenCLLIB::Fmin }, + .fmax = { "OpenCL.std", OpenCLLIB::Fmax }, + .cos = { "OpenCL.std", OpenCLLIB::Cos }, + .sin = { "OpenCL.std", OpenCLLIB::Sin }, + .tan = { "OpenCL.std", OpenCLLIB::Tan }, + .acos = { "OpenCL.std", OpenCLLIB::Acos }, + .asin = { "OpenCL.std", OpenCLLIB::Asin }, + .atan = { "OpenCL.std", OpenCLLIB::Atan }, + .atan2 = { "OpenCL.std", OpenCLLIB::Atan2 }, + .sqrt = { "OpenCL.std", OpenCLLIB::Sqrt }, + .cbrt = { "OpenCL.std", OpenCLLIB::Cbrt }, + .pow = { "OpenCL.std", OpenCLLIB::Pow }, + .exp = { "OpenCL.std", OpenCLLIB::Exp }, + .exp2 = { "OpenCL.std", OpenCLLIB::Exp2 }, + .log = { "OpenCL.std", OpenCLLIB::Log }, + .log2 = { "OpenCL.std", OpenCLLIB::Log2 }, + .log10 = { "OpenCL.std", OpenCLLIB::Log10 }, +}; + +SpvId CodeGen::emit_mathop(BasicBlockBuilder* bb, const thorin::MathOp& mathop) { + auto type = mathop.type(); + + SpirMathOps& impl = opencl_std; + std::vector ops; + for (auto& op : mathop.ops()) { + ops.push_back(emit(op)); + } + + if (is_type_f(type)) { + switch (mathop.mathop_tag()) { +#define THORIN_MATHOP(mathop_name) case MathOp_##mathop_name: return bb->ext_instruction(convert(type).id, impl.mathop_name, ops); +#include "thorin/tables/mathoptable.h" + } + } +} + +} \ No newline at end of file From df2db758ed5934478f8071beff183f7a0e164d05 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Mon, 1 Jul 2024 17:28:01 +0200 Subject: [PATCH 248/342] spirv: fix OpenCL copysign --- src/thorin/be/spirv/spirv_instructions.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/thorin/be/spirv/spirv_instructions.cpp b/src/thorin/be/spirv/spirv_instructions.cpp index 509179e7b..4a9b53104 100644 --- a/src/thorin/be/spirv/spirv_instructions.cpp +++ b/src/thorin/be/spirv/spirv_instructions.cpp @@ -12,7 +12,7 @@ struct SpirMathOps { SpirMathOps opencl_std = { .fabs = { "OpenCL.std", OpenCLLIB::Fabs }, - .copysign = { "OpenCL.std", OpenCLLIB::Sign }, + .copysign = { "OpenCL.std", OpenCLLIB::Copysign }, .round = { "OpenCL.std", OpenCLLIB::Round }, .floor = { "OpenCL.std", OpenCLLIB::Floor }, .ceil = { "OpenCL.std", OpenCLLIB::Ceil }, From 7028f0aab7622e91547536edf9ec6c3f60c239af Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Mon, 1 Jul 2024 17:28:18 +0200 Subject: [PATCH 249/342] spirv: fix some more issues --- src/thorin/be/spirv/spirv.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/thorin/be/spirv/spirv.cpp b/src/thorin/be/spirv/spirv.cpp index 17c7d414f..65afc7ace 100644 --- a/src/thorin/be/spirv/spirv.cpp +++ b/src/thorin/be/spirv/spirv.cpp @@ -157,12 +157,12 @@ void CodeGen::emit_stream(std::ostream& out) { } for (auto& cont : world().copy_continuations()) { - assert(defs_.contains(cont)); if (cont->is_exported() && kernel_config_) { auto config = kernel_config_->find(cont); if (config == kernel_config_->end()) continue; + assert(defs_.contains(cont)); SpvId callee = defs_[cont]; auto block = config->second->as()->block_size(); @@ -193,18 +193,21 @@ FnBuilder& CodeGen::get_fn_builder(thorin::Continuation* continuation) { auto& fn = *(builder_->fn_builders_[continuation] = std::make_unique(*builder_)); fn.fn_type = convert(entry_->type()).id; fn.fn_ret_type = get_codom_type(entry_); + defs_[continuation] = fn.function_id; return fn; } FnBuilder* CodeGen::prepare(const thorin::Scope& scope) { auto& fn = get_fn_builder(scope.entry()); builder_->name(fn.function_id, scope.entry()->name()); + builder_->current_fn_ = &fn; return &fn; } void CodeGen::prepare(thorin::Continuation* cont, FnBuilder* fn) { auto& bb = *fn->bbs.emplace_back(std::make_unique(*fn)); cont2bb_[cont] = &bb; + defs_[cont] = bb.label; fn->bbs_to_emit.emplace_back(&bb); builder_->name(bb.label, cont->name().c_str()); @@ -373,10 +376,9 @@ void CodeGen::emit_epilogue(Continuation* continuation) { } } - auto ret_type = get_codom_type(continuation); - SpvId call_result; if (auto called_continuation = app.callee()->isa_nom()) { + auto ret_type = get_codom_type(called_continuation); call_result = bb->call(ret_type, emit(called_continuation), call_args); } else { // must be a closure From 78ea186c708d8094a66892ac39a3240983211ac6 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Mon, 1 Jul 2024 17:28:54 +0200 Subject: [PATCH 250/342] spirv: cleanup --- src/thorin/be/spirv/spirv.cpp | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/thorin/be/spirv/spirv.cpp b/src/thorin/be/spirv/spirv.cpp index 65afc7ace..9c090429f 100644 --- a/src/thorin/be/spirv/spirv.cpp +++ b/src/thorin/be/spirv/spirv.cpp @@ -460,16 +460,6 @@ SpvId CodeGen::emit_constant(const thorin::Def* def) { case PrimType_pf64: case PrimType_qf64: assertf(false, "not implemented yet"); } return constant; - } else if (auto param = def->isa()) { - if (is_mem(param)) return spv_none; - if (auto param_id = builder_->current_fn_->params.lookup(param)) { - assert((*param_id) != 0); - return *param_id; - } else { - auto val = cont2bb_[param->continuation()]->phis_map[param].value; - assert(val != 0); - return val; - } } assertf(false, "Incomplete emit(def) definition"); From d72883099ca5534f372f8c20cc9383f1864f3181 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Mon, 1 Jul 2024 17:33:32 +0200 Subject: [PATCH 251/342] spirv: don't use labels as values. --- src/thorin/be/spirv/spirv.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/thorin/be/spirv/spirv.cpp b/src/thorin/be/spirv/spirv.cpp index 9c090429f..8374c570c 100644 --- a/src/thorin/be/spirv/spirv.cpp +++ b/src/thorin/be/spirv/spirv.cpp @@ -207,7 +207,6 @@ FnBuilder* CodeGen::prepare(const thorin::Scope& scope) { void CodeGen::prepare(thorin::Continuation* cont, FnBuilder* fn) { auto& bb = *fn->bbs.emplace_back(std::make_unique(*fn)); cont2bb_[cont] = &bb; - defs_[cont] = bb.label; fn->bbs_to_emit.emplace_back(&bb); builder_->name(bb.label, cont->name().c_str()); From 74ad6e49a93a0fa808ef037b471f402011b35230 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Mon, 1 Jul 2024 17:41:39 +0200 Subject: [PATCH 252/342] spirv: fix labels --- src/thorin/be/spirv/spirv.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/thorin/be/spirv/spirv.cpp b/src/thorin/be/spirv/spirv.cpp index 8374c570c..f88a396f7 100644 --- a/src/thorin/be/spirv/spirv.cpp +++ b/src/thorin/be/spirv/spirv.cpp @@ -227,6 +227,7 @@ void CodeGen::prepare(thorin::Continuation* cont, FnBuilder* fn) { } } } else { + defs_[cont] = bb.label; for (auto param : cont->params()) { if (is_mem(param) || is_unit(param)) { // Nothing From 0cb78ef8cd7918af3f377a7038b5fed0c7ef1ffe Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Mon, 1 Jul 2024 18:09:08 +0200 Subject: [PATCH 253/342] spirv: always use labels for OpPhi --- src/thorin/be/spirv/spirv.cpp | 13 +++++++++---- src/thorin/be/spirv/spirv.h | 1 + 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/thorin/be/spirv/spirv.cpp b/src/thorin/be/spirv/spirv.cpp index f88a396f7..542a7a2d5 100644 --- a/src/thorin/be/spirv/spirv.cpp +++ b/src/thorin/be/spirv/spirv.cpp @@ -274,6 +274,11 @@ SpvId CodeGen::get_codom_type(const Continuation* fn) { return builder_->declare_struct_type(types); } +SpvId CodeGen::emit_as_bb(thorin::Continuation* cont) { + emit(cont); + return cont2bb_[cont]->label; +} + void CodeGen::emit_epilogue(Continuation* continuation) { auto& bb = cont2bb_[continuation]; // Handles the potential nuances of jumping to another continuation @@ -285,7 +290,7 @@ void CodeGen::emit_epilogue(Continuation* continuation) { if (is_mem(param) || is_unit(param)) continue; auto& phi = cont2bb_[succ]->phis_map[param]; - phi.preds.emplace_back(args[j], emit(continuation)); + phi.preds.emplace_back(args[j], emit_as_bb(continuation)); j++; } }; @@ -322,7 +327,7 @@ void CodeGen::emit_epilogue(Continuation* continuation) { bb->args[arg] = val; auto* param = dst_cont->param(index); auto& phi = cont2bb_[dst_cont]->phis_map[param]; - phi.preds.emplace_back(bb->args[arg], emit(continuation)); + phi.preds.emplace_back(bb->args[arg], emit_as_bb(continuation)); } bb->branch(emit(dst_cont)); } else if (app.callee() == world().branch()) { @@ -407,7 +412,7 @@ void CodeGen::emit_epilogue(Continuation* continuation) { bb->branch(emit(succ)); auto& phi = cont2bb_[succ]->phis_map[last_param]; - phi.preds.emplace_back(call_result, emit(continuation)); + phi.preds.emplace_back(call_result, emit_as_bb(continuation)); } else { Array extracts(n); for (size_t i = 0, j = 0; i != succ->num_params(); ++i) { @@ -426,7 +431,7 @@ void CodeGen::emit_epilogue(Continuation* continuation) { continue; auto& phi = cont2bb_[succ]->phis_map[last_param]; - phi.preds.emplace_back(extracts[j], emit(continuation)); + phi.preds.emplace_back(extracts[j], emit_as_bb(continuation)); j++; } diff --git a/src/thorin/be/spirv/spirv.h b/src/thorin/be/spirv/spirv.h index de7026bd8..d3121dda2 100644 --- a/src/thorin/be/spirv/spirv.h +++ b/src/thorin/be/spirv/spirv.h @@ -74,6 +74,7 @@ class CodeGen : public thorin::CodeGen, public thorin::Emitter emit_builtin(const App&, const Continuation*, BasicBlockBuilder*); + SpvId emit_as_bb(Continuation*); SpvId emit_mathop(BasicBlockBuilder* bb, const MathOp& op); SpvId get_codom_type(const Continuation* fn); From 93da33228f77f518a4a84e1023c8fdbec9df9820 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Mon, 1 Jul 2024 18:09:32 +0200 Subject: [PATCH 254/342] spirv: deal with vectors properly --- src/thorin/be/spirv/spirv_types.cpp | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/src/thorin/be/spirv/spirv_types.cpp b/src/thorin/be/spirv/spirv_types.cpp index 51bb71e8c..e73fd83e4 100644 --- a/src/thorin/be/spirv/spirv_types.cpp +++ b/src/thorin/be/spirv/spirv_types.cpp @@ -20,9 +20,17 @@ ConvertedType CodeGen::convert(const Type* type) { if (auto iter = types_.find(type); iter != types_.end()) return iter->second; + // Vector types are stupid and dangerous! + ConvertedType converted = { 0, std::nullopt }; - switch (type->tag()) { + if (auto vec = type->isa(); vec && vec->length() > 1) { + auto component = vec->scalarize(); + auto conv_comp = convert(component); + converted.id = builder_->declare_vector_type(conv_comp.id, (uint32_t) vec->length()); + converted.layout = conv_comp.layout; + converted.layout->size *= vec->length(); + } else switch (type->tag()) { // Boolean types are typically packed intelligently when declaring in local variables, however with vanilla Vulkan 1.0 they can only be represented via 32-bit integers // Using extensions, we could use 16 or 8-bit ints instead // We can also pack them inside structures using bit-twiddling tricks, if the need arises @@ -143,17 +151,6 @@ ConvertedType CodeGen::convert(const Type* type) { break; } - case Node_Vector: { - auto vec = type->as(); - assert(vec->length() > 1); - auto component = vec->scalarize(); - auto conv_comp = convert(component); - converted.id = builder_->declare_vector_type(conv_comp.id, (uint32_t)vec->length()); - converted.layout = conv_comp.layout; - converted.layout->size *= vec->length(); - break; - } - case Node_StructType: case Node_TupleType: { std::vector spv_types; From b56a3da7646b21732f0dd698f150f6dd07e86e3c Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Mon, 1 Jul 2024 18:12:42 +0200 Subject: [PATCH 255/342] spirv: declare Linkage capability when without entry points --- src/thorin/be/spirv/spirv.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/thorin/be/spirv/spirv.cpp b/src/thorin/be/spirv/spirv.cpp index 542a7a2d5..50e816a15 100644 --- a/src/thorin/be/spirv/spirv.cpp +++ b/src/thorin/be/spirv/spirv.cpp @@ -156,6 +156,7 @@ void CodeGen::emit_stream(std::ostream& out) { interface.push_back(emit(global)); } + int entry_points_count = 0; for (auto& cont : world().copy_continuations()) { if (cont->is_exported() && kernel_config_) { auto config = kernel_config_->find(cont); @@ -174,9 +175,14 @@ void CodeGen::emit_stream(std::ostream& out) { builder_->declare_entry_point(spv::ExecutionModelGLCompute, callee, cont->name().c_str(), interface); builder_->execution_mode(callee, spv::ExecutionModeLocalSize, local_size); + entry_points_count++; } } + if (entry_points_count == 0) { + builder_->capability(spv::Capability::CapabilityLinkage); + } + builder_->finish(out); builder_ = nullptr; } From 99415933971e50c04391a0fc56ae7d72eaa802d3 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Mon, 1 Jul 2024 18:30:39 +0200 Subject: [PATCH 256/342] declare capabilities lazily, based on dialect --- src/thorin/be/spirv/spirv.cpp | 22 ++++++++++++++-------- src/thorin/be/spirv/spirv.h | 6 ++++-- src/thorin/be/spirv/spirv_types.cpp | 13 ++++++++++++- 3 files changed, 30 insertions(+), 11 deletions(-) diff --git a/src/thorin/be/spirv/spirv.cpp b/src/thorin/be/spirv/spirv.cpp index 50e816a15..86688633b 100644 --- a/src/thorin/be/spirv/spirv.cpp +++ b/src/thorin/be/spirv/spirv.cpp @@ -83,14 +83,6 @@ BasicBlockBuilder::BasicBlockBuilder(FnBuilder& fn_builder) FnBuilder::FnBuilder(FileBuilder& file_builder) : builder::SpvFnBuilder(&file_builder), file_builder(file_builder) {} FileBuilder::FileBuilder(CodeGen* cg) : builder::SpvFileBuilder(), cg(cg) { - capability(spv::Capability::CapabilityShader); - capability(spv::Capability::CapabilityVariablePointers); - capability(spv::Capability::CapabilityPhysicalStorageBufferAddresses); - // capability(spv::Capability::CapabilityInt16); - capability(spv::Capability::CapabilityInt64); - - addressing_model = spv::AddressingModelPhysicalStorageBuffer64; - memory_model = spv::MemoryModel::MemoryModelGLSL450; } SpvId FileBuilder::u32_t() { @@ -145,6 +137,20 @@ void CodeGen::emit_stream(std::ostream& out) { FileBuilder builder(this); builder_ = &builder; + switch (target_info_.dialect) { + case SpvTargetInfo::OpenCL: + builder_->capability(spv::Capability::CapabilityKernel); + builder_->capability(spv::Capability::CapabilityAddresses); + builder_->addressing_model = target_info_.mem_layout.pointer_size == 4 ? spv::AddressingModelPhysical32 : spv::AddressingModelPhysical64; + builder_->memory_model = spv::MemoryModel::MemoryModelOpenCL; + break; + case SpvTargetInfo::Vulkan: + builder_->capability(spv::Capability::CapabilityShader); + builder_->addressing_model = spv::AddressingModelPhysicalStorageBuffer64; + builder_->memory_model = spv::MemoryModel::MemoryModelGLSL450; + break; + } + builder_->builtins = std::make_unique(*builder_); ScopesForest forest(world()); diff --git a/src/thorin/be/spirv/spirv.h b/src/thorin/be/spirv/spirv.h index d3121dda2..e12abe270 100644 --- a/src/thorin/be/spirv/spirv.h +++ b/src/thorin/be/spirv/spirv.h @@ -17,13 +17,15 @@ struct FnBuilder; struct SpvTargetInfo { struct { // Either '4' or '8' - size_t pointer_size; + size_t pointer_size = 8; } mem_layout; enum Dialect{ OpenCL, - Shady + Vulkan }; + + Dialect dialect = OpenCL; }; struct ConvertedType { diff --git a/src/thorin/be/spirv/spirv_types.cpp b/src/thorin/be/spirv/spirv_types.cpp index e73fd83e4..c75adf9ee 100644 --- a/src/thorin/be/spirv/spirv_types.cpp +++ b/src/thorin/be/spirv/spirv_types.cpp @@ -48,10 +48,12 @@ ConvertedType CodeGen::convert(const Type* type) { converted.layout = { 1, 1 }; break; case Node_PrimType_ps16: + builder_->capability(spv::Capability::CapabilityInt16); converted.id = builder_->declare_int_type(16, true); converted.layout = { 2, 2 }; break; case Node_PrimType_pu16: + builder_->capability(spv::Capability::CapabilityInt16); converted.id = builder_->declare_int_type(16, false); converted.layout = { 2, 2 }; break; @@ -64,10 +66,12 @@ ConvertedType CodeGen::convert(const Type* type) { converted.layout = { 4, 4 }; break; case Node_PrimType_ps64: + builder_->capability(spv::Capability::CapabilityInt64); converted.id = builder_->declare_int_type(64, true); converted.layout = { 8, 8 }; break; case Node_PrimType_pu64: + builder_->capability(spv::Capability::CapabilityInt64); converted.id = builder_->declare_int_type(64, false); converted.layout = { 8, 8 }; break; @@ -90,8 +94,15 @@ ConvertedType CodeGen::convert(const Type* type) { case AddrSpace::Function: storage_class = spv::StorageClassFunction; break; case AddrSpace::Private: storage_class = spv::StorageClassPrivate; break; case AddrSpace::Push: storage_class = spv::StorageClassPushConstant; break; - case AddrSpace::Global: storage_class = spv::StorageClassCrossWorkgroup; break; case AddrSpace::Generic: storage_class = spv::StorageClassGeneric; break; + case AddrSpace::Global: { + if (target_info_.dialect == SpvTargetInfo::Vulkan) { + builder_->capability(spv::Capability::CapabilityPhysicalStorageBufferAddresses); + storage_class = spv::StorageClassPhysicalStorageBuffer; + } else + storage_class = spv::StorageClassCrossWorkgroup; + break; + } default: assert(false && "This address space is not supported"); break; From 6e6777988b1d8b71a6bdddc445655e810a13b192 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Mon, 1 Jul 2024 19:16:52 +0200 Subject: [PATCH 257/342] spirv: remove old Builtins code --- src/thorin/be/spirv/spirv.cpp | 69 ++--------------------------- src/thorin/be/spirv/spirv.h | 13 +----- src/thorin/be/spirv/spirv_private.h | 2 - 3 files changed, 4 insertions(+), 80 deletions(-) diff --git a/src/thorin/be/spirv/spirv.cpp b/src/thorin/be/spirv/spirv.cpp index 86688633b..236fb915b 100644 --- a/src/thorin/be/spirv/spirv.cpp +++ b/src/thorin/be/spirv/spirv.cpp @@ -95,40 +95,6 @@ SpvId FileBuilder::u32_constant(uint32_t pattern) { return constant(u32_t(), { pattern }); } -Builtins::Builtins(FileBuilder& builder) { - auto& world = builder.cg->world(); - auto spv_uvec3_t = builder.cg->convert(world.type_pu32(3)); - auto spv_uint_t = builder.cg->convert(world.type_pu32()); - auto spv_uvec3_pt = builder.declare_ptr_type(spv::StorageClassInput, spv_uvec3_t.id); - auto spv_uvec3_ptp = builder.declare_ptr_type(spv::StorageClassPrivate, spv_uvec3_t.id); - auto spv_uint_pt = builder.declare_ptr_type(spv::StorageClassInput, spv_uint_t.id); - - // Because we technically can have multiple entry points, we take the easy way out and make each entry point - // write to a private variable the actual workgroup size for that specific kernel. Dirty, but simple. - workgroup_size = builder.variable(spv_uvec3_ptp, spv::StorageClassPrivate); - builder.name(workgroup_size, "BuiltInWorkgroupSize"); - - num_workgroups = builder.variable(spv_uvec3_pt, spv::StorageClassInput); - builder.decorate(num_workgroups, spv::DecorationBuiltIn, { spv::BuiltInNumWorkgroups }); - builder.name(num_workgroups, "BuiltInNumWorkgroups"); - - workgroup_id = builder.variable(spv_uvec3_pt, spv::StorageClassInput); - builder.decorate(workgroup_id, spv::DecorationBuiltIn, { spv::BuiltInWorkgroupId }); - builder.name(workgroup_id, "BuiltInWorkgroupId"); - - local_id = builder.variable(spv_uvec3_pt, spv::StorageClassInput); - builder.decorate(local_id, spv::DecorationBuiltIn, { spv::BuiltInLocalInvocationId }); - builder.name(local_id, "BuiltInLocalInvocationId"); - - global_id = builder.variable(spv_uvec3_pt, spv::StorageClassInput); - builder.decorate(global_id, spv::DecorationBuiltIn, { spv::BuiltInGlobalInvocationId }); - builder.name(global_id, "BuiltInGlobalInvocationId"); - - local_invocation_index = builder.variable(spv_uint_pt, spv::StorageClassInput); - builder.decorate(local_invocation_index, spv::DecorationBuiltIn, { spv::BuiltInLocalInvocationIndex }); - builder.name(local_invocation_index, "BuiltInLocalInvocationIndex"); -} - CodeGen::CodeGen(Thorin& thorin, SpvTargetInfo target_info, bool debug, const Cont2Config* kernel_config) : thorin::CodeGen(thorin, debug), target_info_(target_info), kernel_config_(kernel_config) {} @@ -151,8 +117,6 @@ void CodeGen::emit_stream(std::ostream& out) { break; } - builder_->builtins = std::make_unique(*builder_); - ScopesForest forest(world()); forest.for_each([&](const Scope& scope) { emit_scope(scope, forest); }); @@ -364,16 +328,14 @@ void CodeGen::emit_epilogue(Continuation* continuation) { THORIN_UNREACHABLE; } else if (app.callee()->isa()) { bb->unreachable(); - } else if (auto builtin = app.callee()->isa_nom(); builtin->is_imported()) { + } else if (auto intrinsic = app.callee()->isa_nom(); intrinsic && intrinsic->is_intrinsic()) { // Ensure we emit previous memory operations assert(is_mem(app.arg(0))); emit(app.arg(0)); - auto productions = emit_builtin(app, builtin, bb); + auto productions = emit_intrinsic(app, intrinsic, bb); auto succ = app.args().back()->isa_nom(); jump_to_next_cont_with_args(succ, productions); - } else if (auto intrinsic = app.callee()->isa_nom(); intrinsic && intrinsic->is_intrinsic()) { - THORIN_UNREACHABLE; } else { // function/closure call // put all first-order args into an array std::vector call_args; @@ -832,11 +794,8 @@ SpvId CodeGen::emit_bb(BasicBlockBuilder* bb, const Def* def) { assertf(false, "Incomplete emit(def) definition"); } -std::vector CodeGen::emit_builtin(const App& app, const Continuation* builtin, BasicBlockBuilder* bb) { +std::vector CodeGen::emit_intrinsic(const App& app, const Continuation* builtin, BasicBlockBuilder* bb) { std::vector productions; - auto uvec3_t = convert(world().type_pu32(3)).id; - auto u32_t = convert(world().type_pu32()).id; - auto i32_t = convert(world().type_ps32()).id; if (builtin->name() == "spirv.nonsemantic.printf") { std::vector args; auto string = app.arg(1); @@ -855,28 +814,6 @@ std::vector CodeGen::emit_builtin(const App& app, const Continuation* bui builder_->extension("SPV_KHR_non_semantic_info"); bb->ext_instruction(convert(world().unit_type()).id, { "NonSemantic.DebugPrintf", 1}, args); - } else if (builtin->name() == "get_work_dim") { - THORIN_UNREACHABLE; - } else if (builtin->name() == "get_global_id") { - auto vector = bb->load(uvec3_t, builder_->builtins->global_id); - auto extracted = bb->vector_extract_dynamic(u32_t, vector, emit(app.arg(1))); - productions.push_back(bb->convert(spv::OpBitcast, i32_t, extracted)); - } else if (builtin->name() == "get_local_size") { - auto vector = bb->load(uvec3_t, builder_->builtins->workgroup_size); - auto extracted = bb->vector_extract_dynamic(u32_t, vector, emit(app.arg(1))); - productions.push_back(bb->convert(spv::OpBitcast, i32_t, extracted)); - } else if (builtin->name() == "get_local_id") { - auto vector = bb->load(uvec3_t, builder_->builtins->local_id); - auto extracted = bb->vector_extract_dynamic(u32_t, vector, emit(app.arg(1))); - productions.push_back(bb->convert(spv::OpBitcast, i32_t, extracted)); - } else if (builtin->name() == "get_num_groups") { - auto vector = bb->load(uvec3_t, builder_->builtins->num_workgroups); - auto extracted = bb->vector_extract_dynamic(u32_t, vector, emit(app.arg(1))); - productions.push_back(bb->convert(spv::OpBitcast, i32_t, extracted)); - } else if (builtin->name() == "get_group_id") { - auto vector = bb->load(uvec3_t, builder_->builtins->workgroup_id); - auto extracted = bb->vector_extract_dynamic(u32_t, vector, emit(app.arg(1))); - productions.push_back(bb->convert(spv::OpBitcast, i32_t, extracted)); } else { world().ELOG("This spir-v builtin isn't recognised: %s", builtin->name()); } diff --git a/src/thorin/be/spirv/spirv.h b/src/thorin/be/spirv/spirv.h index e12abe270..9154761a3 100644 --- a/src/thorin/be/spirv/spirv.h +++ b/src/thorin/be/spirv/spirv.h @@ -36,17 +36,6 @@ struct ConvertedType { std::optional layout; }; -struct Builtins { - SpvId workgroup_size; - SpvId num_workgroups; - SpvId workgroup_id; - SpvId local_id; - SpvId global_id; - SpvId local_invocation_index; - - explicit Builtins(FileBuilder&); -}; - struct BasicBlockBuilder; class CodeGen : public thorin::CodeGen, public thorin::Emitter { @@ -74,7 +63,7 @@ class CodeGen : public thorin::CodeGen, public thorin::Emitter emit_builtin(const App&, const Continuation*, BasicBlockBuilder*); + std::vector emit_intrinsic(const App& app, const Continuation* builtin, BasicBlockBuilder* bb); SpvId emit_as_bb(Continuation*); SpvId emit_mathop(BasicBlockBuilder* bb, const MathOp& op); diff --git a/src/thorin/be/spirv/spirv_private.h b/src/thorin/be/spirv/spirv_private.h index 9af46295b..62bf73368 100644 --- a/src/thorin/be/spirv/spirv_private.h +++ b/src/thorin/be/spirv/spirv_private.h @@ -34,8 +34,6 @@ struct FileBuilder : public builder::SpvFileBuilder { CodeGen* cg; - std::unique_ptr builtins; - FnBuilder* current_fn_ = nullptr; ContinuationMap> fn_builders_; From 26669681a83708ac624f17d7938b64177ecdda43 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Mon, 1 Jul 2024 19:17:05 +0200 Subject: [PATCH 258/342] spirv: use VectorComputeINTEL for Private --- src/thorin/be/spirv/spirv_types.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/thorin/be/spirv/spirv_types.cpp b/src/thorin/be/spirv/spirv_types.cpp index c75adf9ee..4e7e755ba 100644 --- a/src/thorin/be/spirv/spirv_types.cpp +++ b/src/thorin/be/spirv/spirv_types.cpp @@ -92,7 +92,12 @@ ConvertedType CodeGen::convert(const Type* type) { spv::StorageClass storage_class; switch (ptr->addr_space()) { case AddrSpace::Function: storage_class = spv::StorageClassFunction; break; - case AddrSpace::Private: storage_class = spv::StorageClassPrivate; break; + case AddrSpace::Private: { + storage_class = spv::StorageClassPrivate; + if (target_info_.dialect != SpvTargetInfo::Dialect::Vulkan) + builder_->capability(spv::CapabilityVectorComputeINTEL); + break; + } case AddrSpace::Push: storage_class = spv::StorageClassPushConstant; break; case AddrSpace::Generic: storage_class = spv::StorageClassGeneric; break; case AddrSpace::Global: { From a548bccb9d09aadff2906224741bd773defc5698 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Tue, 2 Jul 2024 09:17:15 +0200 Subject: [PATCH 259/342] spirv: renamed Target --- src/thorin/be/spirv/spirv.cpp | 8 +++++--- src/thorin/be/spirv/spirv.h | 10 ++++++---- src/thorin/be/spirv/spirv_types.cpp | 4 ++-- 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/thorin/be/spirv/spirv.cpp b/src/thorin/be/spirv/spirv.cpp index 236fb915b..4d3d00414 100644 --- a/src/thorin/be/spirv/spirv.cpp +++ b/src/thorin/be/spirv/spirv.cpp @@ -95,7 +95,9 @@ SpvId FileBuilder::u32_constant(uint32_t pattern) { return constant(u32_t(), { pattern }); } -CodeGen::CodeGen(Thorin& thorin, SpvTargetInfo target_info, bool debug, const Cont2Config* kernel_config) + + +CodeGen::CodeGen(Thorin& thorin, Target& target_info, bool debug, const Cont2Config* kernel_config) : thorin::CodeGen(thorin, debug), target_info_(target_info), kernel_config_(kernel_config) {} @@ -104,13 +106,13 @@ void CodeGen::emit_stream(std::ostream& out) { builder_ = &builder; switch (target_info_.dialect) { - case SpvTargetInfo::OpenCL: + case Target::OpenCL: builder_->capability(spv::Capability::CapabilityKernel); builder_->capability(spv::Capability::CapabilityAddresses); builder_->addressing_model = target_info_.mem_layout.pointer_size == 4 ? spv::AddressingModelPhysical32 : spv::AddressingModelPhysical64; builder_->memory_model = spv::MemoryModel::MemoryModelOpenCL; break; - case SpvTargetInfo::Vulkan: + case Target::Vulkan: builder_->capability(spv::Capability::CapabilityShader); builder_->addressing_model = spv::AddressingModelPhysicalStorageBuffer64; builder_->memory_model = spv::MemoryModel::MemoryModelGLSL450; diff --git a/src/thorin/be/spirv/spirv.h b/src/thorin/be/spirv/spirv.h index 9154761a3..94530e475 100644 --- a/src/thorin/be/spirv/spirv.h +++ b/src/thorin/be/spirv/spirv.h @@ -14,13 +14,13 @@ class CodeGen; struct FileBuilder; struct FnBuilder; -struct SpvTargetInfo { +struct Target { struct { // Either '4' or '8' size_t pointer_size = 8; } mem_layout; - enum Dialect{ + enum Dialect { OpenCL, Vulkan }; @@ -40,7 +40,7 @@ struct BasicBlockBuilder; class CodeGen : public thorin::CodeGen, public thorin::Emitter { public: - CodeGen(Thorin& thorin, SpvTargetInfo, bool debug, const Cont2Config* = nullptr); + CodeGen(Thorin& thorin, Target&, bool debug, const Cont2Config* = nullptr); void emit_stream(std::ostream& stream) override; const char* file_ext() const override { return ".spv"; } @@ -70,9 +70,11 @@ class CodeGen : public thorin::CodeGen, public thorin::Emittercapability(spv::CapabilityVectorComputeINTEL); break; } case AddrSpace::Push: storage_class = spv::StorageClassPushConstant; break; case AddrSpace::Generic: storage_class = spv::StorageClassGeneric; break; case AddrSpace::Global: { - if (target_info_.dialect == SpvTargetInfo::Vulkan) { + if (target_info_.dialect == Target::Dialect::Vulkan) { builder_->capability(spv::Capability::CapabilityPhysicalStorageBufferAddresses); storage_class = spv::StorageClassPhysicalStorageBuffer; } else From bb32c8226639ebdc9105bbe4f183b323833a9907 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Tue, 2 Jul 2024 10:17:25 +0200 Subject: [PATCH 260/342] spirv: opencl doesn't want signed integers --- src/thorin/be/spirv/spirv_types.cpp | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/thorin/be/spirv/spirv_types.cpp b/src/thorin/be/spirv/spirv_types.cpp index 312727a5c..797b20d04 100644 --- a/src/thorin/be/spirv/spirv_types.cpp +++ b/src/thorin/be/spirv/spirv_types.cpp @@ -17,6 +17,25 @@ ConvertedType CodeGen::convert(const Type* type) { default: break; } + // OpenCL has no signed integer types + if (target_info_.dialect == Target::OpenCL) { + switch (type->tag()) { + case Node_PrimType_ps8: + type = world().prim_type(PrimType_pu8, type->as()->length()); \ + break; + case Node_PrimType_ps16: + type = world().prim_type(PrimType_pu16, type->as()->length()); \ + break; + case Node_PrimType_ps32: + type = world().prim_type(PrimType_pu32, type->as()->length()); \ + break; + case Node_PrimType_ps64: + type = world().prim_type(PrimType_pu64, type->as()->length()); \ + break; + default: break; + } + } + if (auto iter = types_.find(type); iter != types_.end()) return iter->second; From 97fecff4fbf7206f8820fa4478bb0a4269d9ddc5 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Tue, 2 Jul 2024 13:27:25 +0200 Subject: [PATCH 261/342] spirv: fix some emission issues --- src/thorin/be/spirv/spirv.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/thorin/be/spirv/spirv.cpp b/src/thorin/be/spirv/spirv.cpp index 4d3d00414..ca4868451 100644 --- a/src/thorin/be/spirv/spirv.cpp +++ b/src/thorin/be/spirv/spirv.cpp @@ -330,10 +330,10 @@ void CodeGen::emit_epilogue(Continuation* continuation) { THORIN_UNREACHABLE; } else if (app.callee()->isa()) { bb->unreachable(); - } else if (auto intrinsic = app.callee()->isa_nom(); intrinsic && intrinsic->is_intrinsic()) { + } else if (auto intrinsic = app.callee()->isa_nom(); intrinsic && (intrinsic->is_intrinsic() || intrinsic->cc() == CC::Device)) { // Ensure we emit previous memory operations assert(is_mem(app.arg(0))); - emit(app.arg(0)); + emit_unsafe(app.arg(0)); auto productions = emit_intrinsic(app, intrinsic, bb); auto succ = app.args().back()->isa_nom(); @@ -609,7 +609,7 @@ SpvId CodeGen::emit_bb(BasicBlockBuilder* bb, const Def* def) { return bb->composite(convert(structagg->type()).id, elements); } else if (auto access = def->isa()) { // emit dependent operations first - emit(access->mem()); + emit_unsafe(access->mem()); std::vector operands; auto ptr_type = access->ptr()->type()->as(); From 14584d37866cd5fd84b0517d1d90b7d69ded577f Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Tue, 2 Jul 2024 13:27:52 +0200 Subject: [PATCH 262/342] spirv: add spirv.builtin intrinsic --- src/thorin/be/spirv/spirv.cpp | 22 ++++++++++-- src/thorin/be/spirv/spirv.h | 3 +- src/thorin/be/spirv/spirv_private.h | 1 + src/thorin/be/spirv/spirv_types.cpp | 55 ++++++++++++++++------------- 4 files changed, 52 insertions(+), 29 deletions(-) diff --git a/src/thorin/be/spirv/spirv.cpp b/src/thorin/be/spirv/spirv.cpp index ca4868451..e926308e3 100644 --- a/src/thorin/be/spirv/spirv.cpp +++ b/src/thorin/be/spirv/spirv.cpp @@ -796,9 +796,9 @@ SpvId CodeGen::emit_bb(BasicBlockBuilder* bb, const Def* def) { assertf(false, "Incomplete emit(def) definition"); } -std::vector CodeGen::emit_intrinsic(const App& app, const Continuation* builtin, BasicBlockBuilder* bb) { +std::vector CodeGen::emit_intrinsic(const App& app, const Continuation* intrinsic, BasicBlockBuilder* bb) { std::vector productions; - if (builtin->name() == "spirv.nonsemantic.printf") { + if (intrinsic->name() == "spirv.nonsemantic.printf") { std::vector args; auto string = app.arg(1); if (auto arr_type = string->type()->isa(); arr_type->elem_type() == world().type_pu8()) { @@ -816,8 +816,24 @@ std::vector CodeGen::emit_intrinsic(const App& app, const Continuation* b builder_->extension("SPV_KHR_non_semantic_info"); bb->ext_instruction(convert(world().unit_type()).id, { "NonSemantic.DebugPrintf", 1}, args); + } else if (intrinsic->name() == "spirv.builtin") { + if (auto spv_builtin_lit = app.arg(1)->isa()) { + auto spv_builtin = spv_builtin_lit->value().get_u32(); + auto found = builder_->builtins_.find(spv_builtin); + if (found != builder_->builtins_.end()) { + productions.push_back(found->second); + } else { + auto ret_type = (*intrinsic->params().back()).type()->as(); + auto desired_type = ret_type->types()[1]->as(); + auto id = builder_->variable(convert(desired_type).id, static_cast(convert(desired_type->addr_space()))); + builder_->decorate(id, spv::Decoration::DecorationBuiltIn, { spv_builtin }); + builder_->builtins_[spv_builtin] = id; + productions.push_back(id); + } + } else + world().ELOG("spirv.builtin requires an integer literal as the argument"); } else { - world().ELOG("This spir-v builtin isn't recognised: %s", builtin->name()); + world().ELOG("This spir-v builtin isn't recognised: %s", intrinsic->name()); } return productions; } diff --git a/src/thorin/be/spirv/spirv.h b/src/thorin/be/spirv/spirv.h index 94530e475..22886eab8 100644 --- a/src/thorin/be/spirv/spirv.h +++ b/src/thorin/be/spirv/spirv.h @@ -49,6 +49,7 @@ class CodeGen : public thorin::CodeGen, public thorin::Emitter 0; } + uint32_t convert(AddrSpace); ConvertedType convert(const Type*); SpvId emit_fun_decl(Continuation*); @@ -63,7 +64,7 @@ class CodeGen : public thorin::CodeGen, public thorin::Emitter emit_intrinsic(const App& app, const Continuation* builtin, BasicBlockBuilder* bb); + std::vector emit_intrinsic(const App& app, const Continuation* intrinsic, BasicBlockBuilder* bb); SpvId emit_as_bb(Continuation*); SpvId emit_mathop(BasicBlockBuilder* bb, const MathOp& op); diff --git a/src/thorin/be/spirv/spirv_private.h b/src/thorin/be/spirv/spirv_private.h index 62bf73368..ef1d72979 100644 --- a/src/thorin/be/spirv/spirv_private.h +++ b/src/thorin/be/spirv/spirv_private.h @@ -36,6 +36,7 @@ struct FileBuilder : public builder::SpvFileBuilder { FnBuilder* current_fn_ = nullptr; ContinuationMap> fn_builders_; + std::unordered_map builtins_; SpvId u32_t(); SpvId u32_constant(uint32_t); diff --git a/src/thorin/be/spirv/spirv_types.cpp b/src/thorin/be/spirv/spirv_types.cpp index 797b20d04..e29812081 100644 --- a/src/thorin/be/spirv/spirv_types.cpp +++ b/src/thorin/be/spirv/spirv_types.cpp @@ -4,6 +4,35 @@ namespace thorin::spirv { +uint32_t CodeGen::convert(AddrSpace as) { + spv::StorageClass storage_class; + switch (as) { + case AddrSpace::Function: storage_class = spv::StorageClassFunction; break; + case AddrSpace::Private: { + storage_class = spv::StorageClassPrivate; + if (target_info_.dialect != Target::Dialect::Vulkan) + builder_->capability(spv::CapabilityVectorComputeINTEL); + break; + } + case AddrSpace::Push: storage_class = spv::StorageClassPushConstant; break; + case AddrSpace::Generic: storage_class = spv::StorageClassGeneric; break; + case AddrSpace::Input: storage_class = spv::StorageClassInput; break; + case AddrSpace::Output: storage_class = spv::StorageClassOutput; break; + case AddrSpace::Global: { + if (target_info_.dialect == Target::Dialect::Vulkan) { + builder_->capability(spv::Capability::CapabilityPhysicalStorageBufferAddresses); + storage_class = spv::StorageClassPhysicalStorageBuffer; + } else + storage_class = spv::StorageClassCrossWorkgroup; + break; + } + default: + assert(false && "This address space is not supported"); + break; + } + return storage_class; +} + ConvertedType CodeGen::convert(const Type* type) { // Spir-V requires each primitive type to be "unique", it doesn't allow for example two 32-bit signed integer types. // Therefore we must enforce that precise/quick types map to the same thing. @@ -108,34 +137,10 @@ ConvertedType CodeGen::convert(const Type* type) { break; case Node_PtrType: { auto ptr = type->as(); - spv::StorageClass storage_class; - switch (ptr->addr_space()) { - case AddrSpace::Function: storage_class = spv::StorageClassFunction; break; - case AddrSpace::Private: { - storage_class = spv::StorageClassPrivate; - if (target_info_.dialect != Target::Dialect::Vulkan) - builder_->capability(spv::CapabilityVectorComputeINTEL); - break; - } - case AddrSpace::Push: storage_class = spv::StorageClassPushConstant; break; - case AddrSpace::Generic: storage_class = spv::StorageClassGeneric; break; - case AddrSpace::Global: { - if (target_info_.dialect == Target::Dialect::Vulkan) { - builder_->capability(spv::Capability::CapabilityPhysicalStorageBufferAddresses); - storage_class = spv::StorageClassPhysicalStorageBuffer; - } else - storage_class = spv::StorageClassCrossWorkgroup; - break; - } - default: - assert(false && "This address space is not supported"); - break; - } - const Type* pointee = ptr->pointee(); while (auto arr = pointee->isa()) pointee = arr->elem_type(); - converted.id = builder_->declare_ptr_type(storage_class, convert(pointee).id); + converted.id = builder_->declare_ptr_type(static_cast(convert(ptr->addr_space())), convert(pointee).id); converted.layout = { target_info_.mem_layout.pointer_size, target_info_.mem_layout.pointer_size }; break; } From e5f9031ff8933b51076424775244a6367e3600bc Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Tue, 2 Jul 2024 13:28:07 +0200 Subject: [PATCH 263/342] spirv: fix tuple type codegen --- src/thorin/be/spirv/spirv_types.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/thorin/be/spirv/spirv_types.cpp b/src/thorin/be/spirv/spirv_types.cpp index e29812081..27f2ca413 100644 --- a/src/thorin/be/spirv/spirv_types.cpp +++ b/src/thorin/be/spirv/spirv_types.cpp @@ -205,8 +205,7 @@ ConvertedType CodeGen::convert(const Type* type) { converted.layout->alignment = std::max(converted.layout->alignment, converted_member_type.layout->alignment); converted.layout->size = pad(converted.layout->size + converted_member_type.layout->size, converted.layout->alignment); } - if (total_serialized_size == 0) { - outf("this one is void"); + if (converted.layout->size == 0) { converted.id = builder_->declare_void_type(); converted.layout = std::nullopt; break; From 9f22df5f1c975260b4029b64105f1a3613393612 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Tue, 2 Jul 2024 13:28:20 +0200 Subject: [PATCH 264/342] added Input and Output address spaces --- src/thorin/rec_stream.cpp | 7 ++++++- src/thorin/type.h | 2 ++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/thorin/rec_stream.cpp b/src/thorin/rec_stream.cpp index 0eb0fe449..590693e31 100644 --- a/src/thorin/rec_stream.cpp +++ b/src/thorin/rec_stream.cpp @@ -211,11 +211,16 @@ Stream& Type::stream(Stream& s) const { if (t->is_vector()) s.fmt(">"); switch (t->addr_space()) { + case AddrSpace::Generic: break; case AddrSpace::Global: s.fmt("[Global]"); break; case AddrSpace::Texture: s.fmt("[Tex]"); break; case AddrSpace::Shared: s.fmt("[Shared]"); break; case AddrSpace::Constant: s.fmt("[Constant]"); break; - default: /* ignore unknown address space */ break; + case AddrSpace::Private: s.fmt("[Private]"); break; + case AddrSpace::Function: s.fmt("[Function]"); break; + case AddrSpace::Input: s.fmt("[Input]"); break; + case AddrSpace::Output: s.fmt("[Output]"); break; + default: s.fmt("[{}]", (int) t->addr_space()); break; } return s; } else if (auto t = isa()) { diff --git a/src/thorin/type.h b/src/thorin/type.h index bf7cb26df..8d69b4aa2 100644 --- a/src/thorin/type.h +++ b/src/thorin/type.h @@ -235,6 +235,8 @@ enum class AddrSpace : uint32_t { Private = 5, // Corresponds to the 'private' storage class in compute kernels/shaders, as in thread-private Function = 6, // Corresponds to the 'function' storage class in SPIR-V Push = 7, // Corresponds to the 'push constant' storage class in SPIR-V + Input = 8, + Output = 9, }; /// Pointer type. From cec8afef60d74f664cdefce14451efb2963ac28b Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Tue, 2 Jul 2024 13:28:51 +0200 Subject: [PATCH 265/342] whitespace --- src/thorin/be/spirv/spirv.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/thorin/be/spirv/spirv.cpp b/src/thorin/be/spirv/spirv.cpp index e926308e3..c6f8fc669 100644 --- a/src/thorin/be/spirv/spirv.cpp +++ b/src/thorin/be/spirv/spirv.cpp @@ -95,8 +95,6 @@ SpvId FileBuilder::u32_constant(uint32_t pattern) { return constant(u32_t(), { pattern }); } - - CodeGen::CodeGen(Thorin& thorin, Target& target_info, bool debug, const Cont2Config* kernel_config) : thorin::CodeGen(thorin, debug), target_info_(target_info), kernel_config_(kernel_config) {} From 4ffffed1dc9a4acb7fd021d0478ca397bf5f3c9a Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Tue, 2 Jul 2024 13:57:22 +0200 Subject: [PATCH 266/342] spirv: implement Match --- src/thorin/be/spirv/spirv.cpp | 21 +++++++++++---------- src/thorin/be/spirv/spirv_builder.hpp | 11 +++++++++++ 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/src/thorin/be/spirv/spirv.cpp b/src/thorin/be/spirv/spirv.cpp index c6f8fc669..b530ee5db 100644 --- a/src/thorin/be/spirv/spirv.cpp +++ b/src/thorin/be/spirv/spirv.cpp @@ -316,16 +316,17 @@ void CodeGen::emit_epilogue(Continuation* continuation) { auto fbb = emit(app.arg(3)); bb->branch_conditional(cond, tbb, fbb); } else if (app.callee()->isa() && app.callee()->as()->intrinsic() == Intrinsic::Match) { - /*auto val = emit(continuation->arg(0)); - auto otherwise_bb = cont2bb(continuation->arg(1)->isa_nom()); - auto match = irbuilder.CreateSwitch(val, otherwise_bb, continuation->num_args() - 2); - for (size_t i = 2; i < continuation->num_args(); i++) { - auto arg = continuation->arg(i)->as(); - auto case_const = llvm::cast(emit(arg->op(0))); - auto case_bb = cont2bb(arg->op(1)->isa_nom()); - match->addCase(case_const, case_bb); - }*/ - THORIN_UNREACHABLE; + emit_unsafe(app.arg(0)); + auto val = emit(app.arg(1)); + auto otherwise_bb = emit_as_bb(app.arg(2)->isa_nom()); + std::vector literals; + std::vector cases; + for (size_t i = 3; i < app.num_args(); i++) { + auto arg = app.arg(i)->as(); + literals.push_back(emit(arg->op(0))); + cases.push_back(emit_as_bb(arg->op(1)->as_nom())); + } + bb->branch_switch(val, otherwise_bb, literals, cases); } else if (app.callee()->isa()) { bb->unreachable(); } else if (auto intrinsic = app.callee()->isa_nom(); intrinsic && (intrinsic->is_intrinsic() || intrinsic->cc() == CC::Device)) { diff --git a/src/thorin/be/spirv/spirv_builder.hpp b/src/thorin/be/spirv/spirv_builder.hpp index 2e741f2fe..b94f11eb1 100644 --- a/src/thorin/be/spirv/spirv_builder.hpp +++ b/src/thorin/be/spirv/spirv_builder.hpp @@ -220,6 +220,17 @@ struct SpvBasicBlockBuilder : public SpvSectionBuilder { ref_id(false_target); } + void branch_switch(SpvId selector, SpvId default_case, std::vector literals, std::vector cases) { + assert(literals.size() == cases.size()); + op(spv::Op::OpSwitch, 3 + literals.size() * 2); + ref_id(selector); + ref_id(default_case); + for (size_t i = 0; i < literals.size(); i++) { + ref_id(literals[i]); + ref_id(cases[i]); + } + } + void selection_merge(SpvId merge_bb, spv::SelectionControlMask selection_control) { op(spv::Op::OpSelectionMerge, 3); ref_id(merge_bb); From 4f6563b77be5bdb0622aaf8f47e015fdf8fb8d57 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Tue, 2 Jul 2024 16:14:54 +0200 Subject: [PATCH 267/342] spirv: Implement Slot, Enter --- src/thorin/be/spirv/spirv.cpp | 22 +++++++++++++++++----- src/thorin/be/spirv/spirv_types.cpp | 8 ++++++-- 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/src/thorin/be/spirv/spirv.cpp b/src/thorin/be/spirv/spirv.cpp index b530ee5db..057436611 100644 --- a/src/thorin/be/spirv/spirv.cpp +++ b/src/thorin/be/spirv/spirv.cpp @@ -622,6 +622,14 @@ SpvId CodeGen::emit_bb(BasicBlockBuilder* bb, const Def* def) { bb->store(emit(store->val()), emit(store->ptr()), operands); return spv_none; } else THORIN_UNREACHABLE; + } else if (auto slot = def->isa()) { + emit_unsafe(slot->frame()); + auto type = slot->type(); + auto id = bb->fn_builder.variable(convert(world().ptr_type(type->pointee(), 1, AddrSpace::Function)).id, spv::StorageClass::StorageClassFunction); + id = bb->convert(spv::Op::OpBitcast, convert(type).id, id); + return id; + } else if (auto enter = def->isa()) { + return emit_unsafe(enter->mem()); } else if (auto lea = def->isa()) { switch (lea->ptr_type()->addr_space()) { case AddrSpace::Global: @@ -635,13 +643,12 @@ SpvId CodeGen::emit_bb(BasicBlockBuilder* bb, const Def* def) { auto offset = emit(lea->index()); return bb->ptr_access_chain(type, emit(lea->ptr()), offset, {}); } else if (auto aggop = def->isa()) { - auto spv_agg = emit(aggop->agg()); auto agg_type = convert(aggop->agg()->type()).id; bool mem = false; if (auto tt = aggop->agg()->type()->isa(); tt && tt->op(0)->isa()) mem = true; - auto copy_to_alloca = [&] (SpvId target_type) { + auto copy_to_alloca = [&] (SpvId spv_agg, SpvId target_type) { world().wdef(def, "slow: alloca and loads/stores needed for aggregate '{}'", def); auto agg_ptr_type = builder_->declare_ptr_type(spv::StorageClassFunction, agg_type); @@ -654,7 +661,11 @@ SpvId CodeGen::emit_bb(BasicBlockBuilder* bb, const Def* def) { }; if (auto extract = aggop->isa()) { - if (is_mem(extract)) return spv_none; + if (is_mem(extract) || extract->type()->isa()) { + emit_unsafe(extract->agg()); + return spv_none; + } + auto spv_agg = emit(aggop->agg()); auto target_type = convert(extract->type()).id; auto constant_index = aggop->index()->isa(); @@ -663,7 +674,7 @@ SpvId CodeGen::emit_bb(BasicBlockBuilder* bb, const Def* def) { if (aggop->agg()->type()->isa() && constant_index == nullptr) { assert(aggop->agg()->type()->isa()); assert(!is_mem(extract)); - return bb->load(target_type, copy_to_alloca(target_type).second); + return bb->load(target_type, copy_to_alloca(spv_agg, target_type).second); } if (extract->agg()->type()->isa()) @@ -681,6 +692,7 @@ SpvId CodeGen::emit_bb(BasicBlockBuilder* bb, const Def* def) { return bb->extract(target_type, spv_agg, { index - offset }); } else if (auto insert = def->isa()) { + auto spv_agg = emit(aggop->agg()); auto value = emit(insert->value()); auto constant_index = aggop->index()->isa(); @@ -688,7 +700,7 @@ SpvId CodeGen::emit_bb(BasicBlockBuilder* bb, const Def* def) { if (insert->agg()->type()->isa() && constant_index == nullptr) { assert(aggop->agg()->type()->isa()); - auto [variable, cell] = copy_to_alloca(agg_type); + auto [variable, cell] = copy_to_alloca(spv_agg, agg_type); bb->store(value, cell); return bb->load(agg_type, variable); } diff --git a/src/thorin/be/spirv/spirv_types.cpp b/src/thorin/be/spirv/spirv_types.cpp index 27f2ca413..90a718697 100644 --- a/src/thorin/be/spirv/spirv_types.cpp +++ b/src/thorin/be/spirv/spirv_types.cpp @@ -14,8 +14,12 @@ uint32_t CodeGen::convert(AddrSpace as) { builder_->capability(spv::CapabilityVectorComputeINTEL); break; } + case AddrSpace::Generic: { + storage_class = spv::StorageClassGeneric; + builder_->capability(spv::Capability::CapabilityGenericPointer); + break; + } case AddrSpace::Push: storage_class = spv::StorageClassPushConstant; break; - case AddrSpace::Generic: storage_class = spv::StorageClassGeneric; break; case AddrSpace::Input: storage_class = spv::StorageClassInput; break; case AddrSpace::Output: storage_class = spv::StorageClassOutput; break; case AddrSpace::Global: { @@ -198,7 +202,7 @@ ConvertedType CodeGen::convert(const Type* type) { converted.layout = { 0, 0 }; for (auto member : type->ops()) { auto member_type = member->as(); - if (member_type == world().unit_type() || member_type == world().mem_type()) continue; + if (member_type == world().unit_type() || member_type == world().mem_type() || member_type->isa()) continue; auto converted_member_type = convert(member_type); assert(converted_member_type.layout); spv_types.push_back(converted_member_type.id); From ec932facb60a88f503b572e982fbfd042acd1c9c Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Thu, 4 Jul 2024 14:25:09 +0200 Subject: [PATCH 268/342] rewrote DeviceBackends entirely --- src/thorin/be/codegen.cpp | 346 +++++++++++++++++++++++--------------- src/thorin/be/codegen.h | 42 ++++- src/thorin/world.cpp | 2 + src/thorin/world.h | 1 + 4 files changed, 253 insertions(+), 138 deletions(-) diff --git a/src/thorin/be/codegen.cpp b/src/thorin/be/codegen.cpp index 83b5a0bf0..a01279af5 100644 --- a/src/thorin/be/codegen.cpp +++ b/src/thorin/be/codegen.cpp @@ -1,58 +1,23 @@ #include "thorin/be/codegen.h" -#include "thorin/analyses/scope.h" -#include "thorin/transform/hls_channels.h" -#include "thorin/transform/hls_kernel_launch.h" + +#include "thorin/be/c/c.h" #if THORIN_ENABLE_LLVM -#include "thorin/be/llvm/cpu.h" #include "thorin/be/llvm/nvvm.h" #include "thorin/be/llvm/amdgpu_hsa.h" #include "thorin/be/llvm/amdgpu_pal.h" #endif + #if THORIN_ENABLE_SHADY #include "thorin/be/shady/shady.h" #undef empty #undef nodes #endif -#include "thorin/be/c/c.h" - -namespace thorin { -static void get_kernel_configs( - Thorin& thorin, - const std::vector& kernels, - Cont2Config& kernel_configs, - std::function (Continuation*, Continuation*)> use_callback) -{ - thorin.opt(); - - auto externals = thorin.world().externals(); - for (auto continuation : kernels) { - // recover the imported continuation (lost after the call to opt) - Continuation* imported = nullptr; - for (auto [_, def] : externals) { - auto exported = def->isa(); - if (!exported) continue; - if (!exported->has_body()) continue; - if (exported->name() == continuation->name()) - imported = exported; - } - if (!imported) continue; - - visit_uses(continuation, [&] (Continuation* use) { - assert(use->has_body()); - auto config = use_callback(use, imported); - if (config) { - auto p = kernel_configs.emplace(imported, std::move(config)); - assert_unused(p.second && "single kernel config entry expected"); - } - return false; - }, true); +#include "thorin/transform/hls_channels.h" +#include "thorin/transform/hls_kernel_launch.h" - continuation->world().make_external(continuation); - continuation->destroy("codegen"); - } -} +namespace thorin { static const App* get_alloc_call(const Def* def) { // look through casts @@ -84,95 +49,104 @@ static uint64_t get_alloc_size(const Def* def) { return size ? static_cast(size->value().get_qu64()) : 0_u64; } -DeviceBackends::DeviceBackends(World& world, int opt, bool debug, std::string& flags) - : cgs {} -{ - std::vector importers; - for (auto& name : backend_names) { - accelerator_code.emplace_back(name); - importers.emplace_back(world, accelerator_code.back().world()); - } - - static const auto backend_intrinsics = std::array { - std::pair { CUDA, Intrinsic::CUDA }, - std::pair { NVVM, Intrinsic::NVVM }, - std::pair { OpenCL, Intrinsic::OpenCL }, - std::pair { AMDGPU_HSA, Intrinsic::AMDGPUHSA }, - std::pair { AMDGPU_PAL, Intrinsic::AMDGPUPAL }, - std::pair { HLS, Intrinsic::HLS }, - std::pair { Shady, Intrinsic::ShadyCompute } - }; +static std::unique_ptr get_gpu_kernel_config(const App* app, Continuation* imported) { + // determine whether or not this kernel uses restrict pointers + bool has_restrict = true; + DefSet allocs; + for (size_t i = LaunchArgs::Num, e = app->num_args(); has_restrict && i != e; ++i) { + auto arg = app->arg(i); + if (!arg->type()->isa()) continue; + auto alloc = get_alloc_call(arg); + if (!alloc) has_restrict = false; + auto p = allocs.insert(alloc); + has_restrict &= p.second; + } - // determine different parts of the world which need to be compiled differently - ScopesForest(world).for_each([&] (const Scope& scope) { - auto continuation = scope.entry(); - Continuation* imported = nullptr; - for (auto [backend, intrinsic] : backend_intrinsics) { - if (is_passed_to_intrinsic(continuation, intrinsic)) { - imported = importers[backend].import(continuation)->as_nom(); - break; - } - } + auto it_config = app->arg(LaunchArgs::Config)->isa(); + if (it_config && + it_config->op(0)->isa() && + it_config->op(1)->isa() && + it_config->op(2)->isa()) { + return std::make_unique(std::tuple{ + it_config->op(0)->as()->qu32_value().data(), + it_config->op(1)->as()->qu32_value().data(), + it_config->op(2)->as()->qu32_value().data() + }, has_restrict); + } + return std::make_unique(std::tuple{-1, -1, -1}, has_restrict); +} - if (imported == nullptr) - return; +Backend::Backend(thorin::DeviceBackends& backends, World& src) : backends_(backends), device_code_(src), importer_(std::make_unique(src, device_code_.world())) {} - // Necessary so that the names match in the original and imported worlds - imported->set_name(continuation->unique_name()); - continuation->set_name(continuation->unique_name()); - for (size_t i = 0, e = continuation->num_params(); i != e; ++i) - imported->param(i)->set_name(continuation->param(i)->name()); - imported->world().make_external(imported); - imported->attributes().cc = CC::C; +struct CudaBackend : public Backend { + explicit CudaBackend(DeviceBackends& b, World& src) : Backend(b, src) { + b.register_intrinsic(Intrinsic::CUDA, *this, get_gpu_kernel_config); + } - kernels.emplace_back(continuation); - }); + std::unique_ptr create_cg(const Cont2Config& config) override { + std::string empty; + return std::make_unique(device_code_, config, c::Lang::CUDA, backends_.debug(), empty); + } +}; - for (auto [backend, intrinsic] : backend_intrinsics) { - if (backend == HLS) - continue; +struct OpenCLBackend : public Backend { + explicit OpenCLBackend(DeviceBackends& b, World& src) : Backend(b, src) { + b.register_intrinsic(Intrinsic::OpenCL, *this, get_gpu_kernel_config); + } - if (!accelerator_code[backend].world().empty()) { - get_kernel_configs(accelerator_code[backend], kernels, kernel_config, [&](Continuation *use, Continuation * /* imported */) { - auto app = use->body(); - if (app->callee()->as()->intrinsic() != intrinsic) - return std::unique_ptr(nullptr); - // determine whether or not this kernel uses restrict pointers - bool has_restrict = true; - DefSet allocs; - for (size_t i = LaunchArgs::Num, e = app->num_args(); has_restrict && i != e; ++i) { - auto arg = app->arg(i); - if (!arg->type()->isa()) continue; - auto alloc = get_alloc_call(arg); - if (!alloc) has_restrict = false; - auto p = allocs.insert(alloc); - has_restrict &= p.second; - } + std::unique_ptr create_cg(const Cont2Config& config) override { + std::string empty; + return std::make_unique(device_code_, config, c::Lang::OpenCL, backends_.debug(), empty); + } +}; - auto it_config = app->arg(LaunchArgs::Config)->isa(); - if (it_config && - it_config->op(0)->isa() && - it_config->op(1)->isa() && - it_config->op(2)->isa()) { - return std::make_unique(std::tuple{ - it_config->op(0)->as()->qu32_value().data(), - it_config->op(1)->as()->qu32_value().data(), - it_config->op(2)->as()->qu32_value().data() - }, has_restrict); - } - return std::make_unique(std::tuple{-1, -1, -1}, has_restrict); - }); - } +#if THORIN_ENABLE_LLVM +struct AMDHSABackend : public Backend { + explicit AMDHSABackend(DeviceBackends& b, World& src) : Backend(b, src) { + b.register_intrinsic(Intrinsic::AMDGPUHSA, *this, get_gpu_kernel_config); + } + + std::unique_ptr create_cg(const Cont2Config& config) override { + return std::make_unique(device_code_, config, backends_.opt(), backends_.debug()); + } +}; + +struct AMDPALBackend : public Backend { + explicit AMDPALBackend(DeviceBackends& b, World& src) : Backend(b, src) { + b.register_intrinsic(Intrinsic::AMDGPUPAL, *this, get_gpu_kernel_config); + } + + std::unique_ptr create_cg(const Cont2Config& config) override { + return std::make_unique(device_code_, config, backends_.opt(), backends_.debug()); + } +}; + +struct NVVMBackend : public Backend { + explicit NVVMBackend(DeviceBackends& b, World& src) : Backend(b, src) { + b.register_intrinsic(Intrinsic::NVVM, *this, get_gpu_kernel_config); + } + + std::unique_ptr create_cg(const Cont2Config& config) override { + return std::make_unique(device_code_, config, backends_.opt(), backends_.debug()); + } +}; +#endif + +#if THORIN_ENABLE_SHADY +struct ShadyBackend : public Backend { + explicit ShadyBackend(DeviceBackends2& b, World& src) : Backend(b, src) { + b.register_intrinsic(Intrinsic::ShadyCompute, get_gpu_kernel_config); } - // get the HLS kernel configurations - Top2Kernel top2kernel; - DeviceParams hls_host_params; - if (!accelerator_code[HLS].world().empty()) { - hls_host_params = hls_channels(accelerator_code[HLS], importers[HLS], top2kernel, world); + std::unique_ptr create_cg(const Cont2Config& config) override { + return std::make_unique(device_code_, config, backends_.debug()); + } +}; +#endif - get_kernel_configs(accelerator_code[HLS], kernels, kernel_config, [&] (Continuation* use, Continuation* imported) { - auto app = use->body(); +struct HLSBackend : public Backend { + explicit HLSBackend(DeviceBackends& b, World& src, std::string& hls_flags) : Backend(b, src), hls_flags_(hls_flags) { + b.register_intrinsic(Intrinsic::NVVM, *this, [&](const App* app, Continuation* imported) { HLSKernelConfig::Param2Size param_sizes; for (size_t i = hls_free_vars_offset, e = app->num_args(); i != e; ++i) { auto arg = app->arg(i); @@ -180,7 +154,7 @@ DeviceBackends::DeviceBackends(World& world, int opt, bool debug, std::string& f if (!ptr_type) continue; auto size = get_alloc_size(arg); if (size == 0) - world.edef(arg, "array size is not known at compile time"); + b.world().edef(arg, "array size is not known at compile time"); auto elem_type = ptr_type->pointee(); size_t multiplier = 1; if (!elem_type->isa()) { @@ -195,29 +169,137 @@ DeviceBackends::DeviceBackends(World& world, int opt, bool debug, std::string& f } auto prim_type = elem_type->isa(); if (!prim_type) - world.edef(arg, "only pointers to arrays of primitive types are supported"); + b.world().edef(arg, "only pointers to arrays of primitive types are supported"); auto num_elems = size / (multiplier * num_bits(prim_type->primtype_tag()) / 8); // imported has type: fn (mem, fn (mem), ...) param_sizes.emplace(imported->param(i - hls_free_vars_offset + 2), num_elems); } return std::make_unique(param_sizes); }); - hls_annotate_top(accelerator_code[HLS].world(), top2kernel, kernel_config); } - hls_kernel_launch(world, hls_host_params); + std::unique_ptr create_cg(const Cont2Config& config) override { + Top2Kernel top2kernel; + DeviceParams hls_host_params; + + hls_host_params = hls_channels(device_code_, *importer_, top2kernel, backends_.world()); + hls_annotate_top(device_code_.world(), top2kernel, const_cast(config)); + hls_kernel_launch(device_code_.world(), hls_host_params); + + return std::make_unique(device_code_, config, c::Lang::HLS, backends_.debug(), hls_flags_); + } + + std::string& hls_flags_; +}; + +DeviceBackends::DeviceBackends(thorin::World& world, int opt, bool debug, std::string& hls_flags) : world_(world), opt_(opt), debug_(debug) { + register_backend(std::make_unique(*this, world)); + register_backend(std::make_unique(*this, world)); #if THORIN_ENABLE_LLVM - if (!accelerator_code[NVVM ].world().empty()) cgs[NVVM ] = std::make_unique(accelerator_code[NVVM ], kernel_config, opt, debug); - if (!accelerator_code[AMDGPU_HSA].world().empty()) cgs[AMDGPU_HSA] = std::make_unique(accelerator_code[AMDGPU_HSA], kernel_config, opt, debug); - if (!accelerator_code[AMDGPU_PAL].world().empty()) cgs[AMDGPU_PAL] = std::make_unique(accelerator_code[AMDGPU_PAL], kernel_config, opt, debug); -#else - (void)opt; + register_backend(std::make_unique(*this, world)); + register_backend(std::make_unique(*this, world)); + register_backend(std::make_unique(*this, world)); #endif #if THORIN_ENABLE_SHADY - if (!accelerator_code[Shady].world().empty()) cgs[Shady] = std::make_unique(accelerator_code[Shady], kernel_config, debug); + register_backend(std::make_unique(*this, world)) #endif - for (auto [backend, lang] : std::array { std::pair { CUDA, c::Lang::CUDA }, std::pair { OpenCL, c::Lang::OpenCL }, std::pair { HLS, c::Lang::HLS } }) - if (!accelerator_code[backend].world().empty()) cgs[backend] = std::make_unique(accelerator_code[backend], kernel_config, lang, debug, flags); + register_backend(std::make_unique(*this, world, hls_flags)); + + search_for_device_code(); +} + +void DeviceBackends::register_backend(std::unique_ptr backend) { + backends_.push_back(std::move(backend)); +} + +World& DeviceBackends::world() { return world_; } +bool DeviceBackends::debug() { return debug_; } +int DeviceBackends::opt() { return opt_; } + +void DeviceBackends::register_intrinsic(thorin::Intrinsic intrinsic, Backend& backend, GetKernelConfigFn f) { + intrinsics_[intrinsic] = std::make_pair(&backend, f); +} + +void DeviceBackends::search_for_device_code() { + // determine different parts of the world which need to be compiled differently + ScopesForest(world_).for_each([&] (const Scope& scope) { + auto continuation = scope.entry(); + Continuation* imported = nullptr; + + Intrinsic intrinsic = Intrinsic::None; + visit_capturing_intrinsics(continuation, [&] (Continuation* continuation) { + if (continuation->is_accelerator()) { + intrinsic = continuation->intrinsic(); + return true; + } + return false; + }); + + if (intrinsic == Intrinsic::None) + return; + + auto handler = intrinsics_.find(intrinsic); + assert(handler != intrinsics_.end()); + auto [backend, get_config] = handler->second; + + imported = backend->importer_->import(continuation)->as_nom(); + if (imported == nullptr) + return; + + // Necessary so that the names match in the original and imported worlds + imported->set_name(continuation->unique_name()); + continuation->set_name(continuation->unique_name()); + for (size_t i = 0, e = continuation->num_params(); i != e; ++i) + imported->param(i)->set_name(continuation->param(i)->name()); + imported->world().make_external(imported); + imported->attributes().cc = CC::C; + + backend->kernels_.emplace_back(continuation); + }); + + for (auto& backend : backends_) { + if (backend->thorin().world().empty()) + continue; + + backend->thorin().opt(); + + Cont2Config kernel_configs; + + auto externals = world_.externals(); + for (auto continuation : backend->kernels_) { + // recover the imported continuation (lost after the call to opt) + Continuation* imported = nullptr; + for (auto [_, def] : externals) { + auto exported = def->isa(); + if (!exported) continue; + if (!exported->has_body()) continue; + if (exported->name() == continuation->name()) + imported = exported; + } + if (!imported) continue; + + visit_uses(continuation, [&] (Continuation* use) { + assert(use->has_body()); + + auto handler = intrinsics_.find(use->body()->callee()->as()->intrinsic()); + assert(handler != intrinsics_.end()); + auto [backend2, get_config] = handler->second; + assert(backend2 == &*backend); + + auto config = get_config(use->body(), imported); + if (config) { + auto p = kernel_configs.emplace(imported, std::move(config)); + assert_unused(p.second && "single kernel config entry expected"); + } + return false; + }, true); + + continuation->world().make_external(continuation); + continuation->destroy("codegen"); + } + + cgs.emplace_back(backend->create_cg(kernel_configs)); + } } CodeGen::CodeGen(Thorin& thorin, bool debug) diff --git a/src/thorin/be/codegen.h b/src/thorin/be/codegen.h index c46cee7c0..47a68ac45 100644 --- a/src/thorin/be/codegen.h +++ b/src/thorin/be/codegen.h @@ -39,17 +39,47 @@ struct LaunchArgs { }; }; +struct DeviceBackends; + +struct Backend { + Backend(DeviceBackends& backends, World& src); + + virtual std::unique_ptr create_cg(const Cont2Config& config) = 0; + + Thorin& thorin() { return device_code_; } + Importer& importer() { return *importer_; } + +protected: + DeviceBackends& backends_; + Thorin device_code_; + std::unique_ptr importer_; + + std::vector kernels_; + friend DeviceBackends; +}; + struct DeviceBackends { DeviceBackends(World& world, int opt, bool debug, std::string& hls_flags); - Cont2Config kernel_config; - std::vector kernels; + World& world(); + std::vector> cgs; + + int opt(); + bool debug(); + + void register_backend(std::unique_ptr); + using GetKernelConfigFn = std::function(const App*, Continuation*)>; + void register_intrinsic(Intrinsic, Backend&, GetKernelConfigFn); - enum { CUDA, NVVM, OpenCL, AMDGPU_HSA, AMDGPU_PAL, HLS, Shady, BackendCount }; - std::array, BackendCount> cgs; private: - std::array backend_names = { "CUDA", "NVVM", "OpenCL", "AMDGPU_HSA", "AMDGPU_PAL", "HLS", "Shady" }; - std::vector accelerator_code; + World& world_; + std::vector> backends_; + std::unordered_map> intrinsics_; + + int opt_; + bool debug_; + + void search_for_device_code(); }; } diff --git a/src/thorin/world.cpp b/src/thorin/world.cpp index 96b3322f5..5c3339685 100644 --- a/src/thorin/world.cpp +++ b/src/thorin/world.cpp @@ -1299,6 +1299,8 @@ Thorin::Thorin(const std::string& name) : world_(std::make_unique(name)) {} +Thorin::Thorin(thorin::World& src) : world_(std::make_unique(src)) {} + void Thorin::opt() { bool debug_passes = getenv("THORIN_DEBUG_PASSES"); #define RUN_PASS(pass) \ diff --git a/src/thorin/world.h b/src/thorin/world.h index a35c95ad3..4b77d8072 100644 --- a/src/thorin/world.h +++ b/src/thorin/world.h @@ -405,6 +405,7 @@ class Thorin { public: /// Initial world constructor explicit Thorin(const std::string& name); + explicit Thorin(World& src); World& world() { return *world_; }; std::unique_ptr& world_container() { return world_; } From 6dc95f7283c134c6d83e4145ab97c948435b6fd4 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Thu, 4 Jul 2024 14:29:49 +0200 Subject: [PATCH 269/342] move and rename KernelLaunchArgs --- src/thorin/be/codegen.cpp | 4 ++-- src/thorin/be/codegen.h | 12 ------------ src/thorin/be/kernel_config.h | 12 ++++++++++++ src/thorin/be/llvm/runtime.cpp | 14 +++++++------- 4 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/thorin/be/codegen.cpp b/src/thorin/be/codegen.cpp index a01279af5..fecc60720 100644 --- a/src/thorin/be/codegen.cpp +++ b/src/thorin/be/codegen.cpp @@ -53,7 +53,7 @@ static std::unique_ptr get_gpu_kernel_config(const App* app, Co // determine whether or not this kernel uses restrict pointers bool has_restrict = true; DefSet allocs; - for (size_t i = LaunchArgs::Num, e = app->num_args(); has_restrict && i != e; ++i) { + for (size_t i = KernelLaunchArgs::Num, e = app->num_args(); has_restrict && i != e; ++i) { auto arg = app->arg(i); if (!arg->type()->isa()) continue; auto alloc = get_alloc_call(arg); @@ -62,7 +62,7 @@ static std::unique_ptr get_gpu_kernel_config(const App* app, Co has_restrict &= p.second; } - auto it_config = app->arg(LaunchArgs::Config)->isa(); + auto it_config = app->arg(KernelLaunchArgs::Config)->isa(); if (it_config && it_config->op(0)->isa() && it_config->op(1)->isa() && diff --git a/src/thorin/be/codegen.h b/src/thorin/be/codegen.h index 47a68ac45..3bb5d5636 100644 --- a/src/thorin/be/codegen.h +++ b/src/thorin/be/codegen.h @@ -27,18 +27,6 @@ class CodeGen { bool debug_; }; -struct LaunchArgs { - enum { - Mem = 0, - Device, - Space, - Config, - Body, - Return, - Num - }; -}; - struct DeviceBackends; struct Backend { diff --git a/src/thorin/be/kernel_config.h b/src/thorin/be/kernel_config.h index c1d36aeb3..787b579a6 100644 --- a/src/thorin/be/kernel_config.h +++ b/src/thorin/be/kernel_config.h @@ -6,6 +6,18 @@ namespace thorin { +struct KernelLaunchArgs { + enum { + Mem = 0, + Device, + Space, + Config, + Body, + Return, + Num + }; +}; + class KernelConfig : public RuntimeCast { public: virtual ~KernelConfig() {} diff --git a/src/thorin/be/llvm/runtime.cpp b/src/thorin/be/llvm/runtime.cpp index 375713091..0d8ff9092 100644 --- a/src/thorin/be/llvm/runtime.cpp +++ b/src/thorin/be/llvm/runtime.cpp @@ -67,22 +67,22 @@ void Runtime::emit_host_code(CodeGen& code_gen, llvm::IRBuilder<>& builder, Plat // target(mem, device, (dim.x, dim.y, dim.z), (block.x, block.y, block.z), body, return, free_vars) auto target = body->callee()->as_nom(); assert_unused(target->is_intrinsic()); - assert(body->num_args() >= LaunchArgs::Num && "required arguments are missing"); + assert(body->num_args() >= KernelLaunchArgs::Num && "required arguments are missing"); // arguments - auto target_device_id = code_gen.emit(body->arg(LaunchArgs::Device)); + auto target_device_id = code_gen.emit(body->arg(KernelLaunchArgs::Device)); auto target_platform = builder.getInt32(platform); auto target_device = builder.CreateOr(target_platform, builder.CreateShl(target_device_id, builder.getInt32(4))); - auto it_space = body->arg(LaunchArgs::Space); - auto it_config = body->arg(LaunchArgs::Config); - auto kernel = body->arg(LaunchArgs::Body)->as()->init()->as(); + auto it_space = body->arg(KernelLaunchArgs::Space); + auto it_config = body->arg(KernelLaunchArgs::Config); + auto kernel = body->arg(KernelLaunchArgs::Body)->as()->init()->as(); auto& world = continuation->world(); //auto kernel_name = builder.CreateGlobalStringPtr(kernel->name() == "hls_top" ? kernel->name() : kernel->name()); auto kernel_name = builder.CreateGlobalStringPtr(kernel->name()); auto file_name = builder.CreateGlobalStringPtr(world.name() + ext); - const size_t num_kernel_args = body->num_args() - LaunchArgs::Num; + const size_t num_kernel_args = body->num_args() - KernelLaunchArgs::Num; // allocate argument pointers, sizes, and types llvm::Value* args = code_gen.emit_alloca(builder, llvm::ArrayType::get(builder.getInt8PtrTy(), num_kernel_args), "args"); @@ -93,7 +93,7 @@ void Runtime::emit_host_code(CodeGen& code_gen, llvm::IRBuilder<>& builder, Plat // fill array of arguments for (size_t i = 0; i < num_kernel_args; ++i) { - auto target_arg = body->arg(i + LaunchArgs::Num); + auto target_arg = body->arg(i + KernelLaunchArgs::Num); const auto target_val = code_gen.emit(target_arg); KernelArgType arg_type; From 735d8aa05974e8bd80d5e8d62bae58fc96dfa266 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Thu, 4 Jul 2024 15:08:19 +0200 Subject: [PATCH 270/342] move get_kernel_configs into method --- src/thorin/be/codegen.cpp | 81 +++++++++++++++++++++------------------ src/thorin/be/codegen.h | 2 + 2 files changed, 45 insertions(+), 38 deletions(-) diff --git a/src/thorin/be/codegen.cpp b/src/thorin/be/codegen.cpp index fecc60720..e9df0a49d 100644 --- a/src/thorin/be/codegen.cpp +++ b/src/thorin/be/codegen.cpp @@ -19,6 +19,47 @@ namespace thorin { +std::unique_ptr Backend::get_kernel_configs() { + device_code_.opt(); + + auto kernel_configs = std::make_unique(); + + auto& externals = backends_.world().externals(); + for (auto continuation : kernels_) { + // recover the imported continuation (lost after the call to opt) + Continuation* imported = nullptr; + for (auto [_, def] : externals) { + auto exported = def->isa(); + if (!exported) continue; + if (!exported->has_body()) continue; + if (exported->name() == continuation->name()) + imported = exported; + } + if (!imported) continue; + + visit_uses(continuation, [&] (Continuation* use) { + assert(use->has_body()); + + auto handler = backends_.intrinsics_.find(use->body()->callee()->as()->intrinsic()); + assert(handler != backends_.intrinsics_.end()); + auto [backend2, get_config] = handler->second; + assert(backend2 == this); + + auto config = get_config(use->body(), imported); + if (config) { + auto p = kernel_configs->emplace(imported, std::move(config)); + assert_unused(p.second && "single kernel config entry expected"); + } + return false; + }, true); + + continuation->world().make_external(continuation); + continuation->destroy("codegen"); + } + + return kernel_configs; +} + static const App* get_alloc_call(const Def* def) { // look through casts while (auto conv_op = def->isa()) @@ -261,44 +302,8 @@ void DeviceBackends::search_for_device_code() { if (backend->thorin().world().empty()) continue; - backend->thorin().opt(); - - Cont2Config kernel_configs; - - auto externals = world_.externals(); - for (auto continuation : backend->kernels_) { - // recover the imported continuation (lost after the call to opt) - Continuation* imported = nullptr; - for (auto [_, def] : externals) { - auto exported = def->isa(); - if (!exported) continue; - if (!exported->has_body()) continue; - if (exported->name() == continuation->name()) - imported = exported; - } - if (!imported) continue; - - visit_uses(continuation, [&] (Continuation* use) { - assert(use->has_body()); - - auto handler = intrinsics_.find(use->body()->callee()->as()->intrinsic()); - assert(handler != intrinsics_.end()); - auto [backend2, get_config] = handler->second; - assert(backend2 == &*backend); - - auto config = get_config(use->body(), imported); - if (config) { - auto p = kernel_configs.emplace(imported, std::move(config)); - assert_unused(p.second && "single kernel config entry expected"); - } - return false; - }, true); - - continuation->world().make_external(continuation); - continuation->destroy("codegen"); - } - - cgs.emplace_back(backend->create_cg(kernel_configs)); + auto kernel_configs = backend->get_kernel_configs(); + cgs.emplace_back(backend->create_cg(*kernel_configs)); } } diff --git a/src/thorin/be/codegen.h b/src/thorin/be/codegen.h index 3bb5d5636..c413e1121 100644 --- a/src/thorin/be/codegen.h +++ b/src/thorin/be/codegen.h @@ -32,6 +32,7 @@ struct DeviceBackends; struct Backend { Backend(DeviceBackends& backends, World& src); + std::unique_ptr get_kernel_configs(); virtual std::unique_ptr create_cg(const Cont2Config& config) = 0; Thorin& thorin() { return device_code_; } @@ -68,6 +69,7 @@ struct DeviceBackends { bool debug_; void search_for_device_code(); +friend Backend; }; } From 410c03a83ff02aae27ec853adc4fb3b80eaeac62 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Thu, 4 Jul 2024 15:47:13 +0200 Subject: [PATCH 271/342] backends: fix HLS using NVVM intrinsic --- src/thorin/be/codegen.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/thorin/be/codegen.cpp b/src/thorin/be/codegen.cpp index e9df0a49d..1c5d4f35e 100644 --- a/src/thorin/be/codegen.cpp +++ b/src/thorin/be/codegen.cpp @@ -187,7 +187,7 @@ struct ShadyBackend : public Backend { struct HLSBackend : public Backend { explicit HLSBackend(DeviceBackends& b, World& src, std::string& hls_flags) : Backend(b, src), hls_flags_(hls_flags) { - b.register_intrinsic(Intrinsic::NVVM, *this, [&](const App* app, Continuation* imported) { + b.register_intrinsic(Intrinsic::HLS, *this, [&](const App* app, Continuation* imported) { HLSKernelConfig::Param2Size param_sizes; for (size_t i = hls_free_vars_offset, e = app->num_args(); i != e; ++i) { auto arg = app->arg(i); From 5fea491c201f6d6b747e006d4de8913661068f7e Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Fri, 5 Jul 2024 11:21:58 +0200 Subject: [PATCH 272/342] spirv: fixed LEA using src pointer type instead of dst --- src/thorin/be/spirv/spirv.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/thorin/be/spirv/spirv.cpp b/src/thorin/be/spirv/spirv.cpp index 057436611..1e4e66532 100644 --- a/src/thorin/be/spirv/spirv.cpp +++ b/src/thorin/be/spirv/spirv.cpp @@ -631,7 +631,7 @@ SpvId CodeGen::emit_bb(BasicBlockBuilder* bb, const Def* def) { } else if (auto enter = def->isa()) { return emit_unsafe(enter->mem()); } else if (auto lea = def->isa()) { - switch (lea->ptr_type()->addr_space()) { + switch (lea->type()->addr_space()) { case AddrSpace::Global: case AddrSpace::Shared: break; @@ -639,7 +639,7 @@ SpvId CodeGen::emit_bb(BasicBlockBuilder* bb, const Def* def) { world().ELOG("LEA is only allowed in global & shared address spaces"); break; } - auto type = convert(lea->ptr_type()).id; + auto type = convert(lea->type()).id; auto offset = emit(lea->index()); return bb->ptr_access_chain(type, emit(lea->ptr()), offset, {}); } else if (auto aggop = def->isa()) { From 0f2bfce8aa2c0a0e467fbd3cae5301b0feb7641b Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Fri, 5 Jul 2024 12:23:51 +0200 Subject: [PATCH 273/342] fix issues with kernel config ownership --- src/thorin/be/codegen.cpp | 12 ++++-------- src/thorin/be/codegen.h | 5 ++++- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/src/thorin/be/codegen.cpp b/src/thorin/be/codegen.cpp index 1c5d4f35e..159bde465 100644 --- a/src/thorin/be/codegen.cpp +++ b/src/thorin/be/codegen.cpp @@ -19,11 +19,9 @@ namespace thorin { -std::unique_ptr Backend::get_kernel_configs() { +void Backend::prepare_kernel_configs() { device_code_.opt(); - auto kernel_configs = std::make_unique(); - auto& externals = backends_.world().externals(); for (auto continuation : kernels_) { // recover the imported continuation (lost after the call to opt) @@ -47,7 +45,7 @@ std::unique_ptr Backend::get_kernel_configs() { auto config = get_config(use->body(), imported); if (config) { - auto p = kernel_configs->emplace(imported, std::move(config)); + auto p = kernel_configs_.emplace(imported, std::move(config)); assert_unused(p.second && "single kernel config entry expected"); } return false; @@ -56,8 +54,6 @@ std::unique_ptr Backend::get_kernel_configs() { continuation->world().make_external(continuation); continuation->destroy("codegen"); } - - return kernel_configs; } static const App* get_alloc_call(const Def* def) { @@ -302,8 +298,8 @@ void DeviceBackends::search_for_device_code() { if (backend->thorin().world().empty()) continue; - auto kernel_configs = backend->get_kernel_configs(); - cgs.emplace_back(backend->create_cg(*kernel_configs)); + backend->prepare_kernel_configs(); + cgs.emplace_back(backend->create_cg(backend->kernel_configs())); } } diff --git a/src/thorin/be/codegen.h b/src/thorin/be/codegen.h index c413e1121..1e85b597d 100644 --- a/src/thorin/be/codegen.h +++ b/src/thorin/be/codegen.h @@ -32,7 +32,7 @@ struct DeviceBackends; struct Backend { Backend(DeviceBackends& backends, World& src); - std::unique_ptr get_kernel_configs(); + Cont2Config& kernel_configs() { return kernel_configs_; }; virtual std::unique_ptr create_cg(const Cont2Config& config) = 0; Thorin& thorin() { return device_code_; } @@ -44,6 +44,9 @@ struct Backend { std::unique_ptr importer_; std::vector kernels_; + Cont2Config kernel_configs_; + + void prepare_kernel_configs(); friend DeviceBackends; }; From d9f5726a20708c3cd148d82300788ba30c735457 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Fri, 5 Jul 2024 12:30:16 +0200 Subject: [PATCH 274/342] remove kernel_config from backend API --- src/thorin/be/codegen.cpp | 28 ++++++++++++++-------------- src/thorin/be/codegen.h | 3 +-- 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/src/thorin/be/codegen.cpp b/src/thorin/be/codegen.cpp index 159bde465..5953283d4 100644 --- a/src/thorin/be/codegen.cpp +++ b/src/thorin/be/codegen.cpp @@ -120,9 +120,9 @@ struct CudaBackend : public Backend { b.register_intrinsic(Intrinsic::CUDA, *this, get_gpu_kernel_config); } - std::unique_ptr create_cg(const Cont2Config& config) override { + std::unique_ptr create_cg() override { std::string empty; - return std::make_unique(device_code_, config, c::Lang::CUDA, backends_.debug(), empty); + return std::make_unique(device_code_, kernel_configs_, c::Lang::CUDA, backends_.debug(), empty); } }; @@ -131,9 +131,9 @@ struct OpenCLBackend : public Backend { b.register_intrinsic(Intrinsic::OpenCL, *this, get_gpu_kernel_config); } - std::unique_ptr create_cg(const Cont2Config& config) override { + std::unique_ptr create_cg() override { std::string empty; - return std::make_unique(device_code_, config, c::Lang::OpenCL, backends_.debug(), empty); + return std::make_unique(device_code_, kernel_configs_, c::Lang::OpenCL, backends_.debug(), empty); } }; @@ -143,8 +143,8 @@ struct AMDHSABackend : public Backend { b.register_intrinsic(Intrinsic::AMDGPUHSA, *this, get_gpu_kernel_config); } - std::unique_ptr create_cg(const Cont2Config& config) override { - return std::make_unique(device_code_, config, backends_.opt(), backends_.debug()); + std::unique_ptr create_cg() override { + return std::make_unique(device_code_, kernel_configs_, backends_.opt(), backends_.debug()); } }; @@ -153,8 +153,8 @@ struct AMDPALBackend : public Backend { b.register_intrinsic(Intrinsic::AMDGPUPAL, *this, get_gpu_kernel_config); } - std::unique_ptr create_cg(const Cont2Config& config) override { - return std::make_unique(device_code_, config, backends_.opt(), backends_.debug()); + std::unique_ptr create_cg() override { + return std::make_unique(device_code_, kernel_configs_, backends_.opt(), backends_.debug()); } }; @@ -163,8 +163,8 @@ struct NVVMBackend : public Backend { b.register_intrinsic(Intrinsic::NVVM, *this, get_gpu_kernel_config); } - std::unique_ptr create_cg(const Cont2Config& config) override { - return std::make_unique(device_code_, config, backends_.opt(), backends_.debug()); + std::unique_ptr create_cg() override { + return std::make_unique(device_code_, kernel_configs_, backends_.opt(), backends_.debug()); } }; #endif @@ -215,15 +215,15 @@ struct HLSBackend : public Backend { }); } - std::unique_ptr create_cg(const Cont2Config& config) override { + std::unique_ptr create_cg() override { Top2Kernel top2kernel; DeviceParams hls_host_params; hls_host_params = hls_channels(device_code_, *importer_, top2kernel, backends_.world()); - hls_annotate_top(device_code_.world(), top2kernel, const_cast(config)); + hls_annotate_top(device_code_.world(), top2kernel, kernel_configs_); hls_kernel_launch(device_code_.world(), hls_host_params); - return std::make_unique(device_code_, config, c::Lang::HLS, backends_.debug(), hls_flags_); + return std::make_unique(device_code_, kernel_configs_, c::Lang::HLS, backends_.debug(), hls_flags_); } std::string& hls_flags_; @@ -299,7 +299,7 @@ void DeviceBackends::search_for_device_code() { continue; backend->prepare_kernel_configs(); - cgs.emplace_back(backend->create_cg(backend->kernel_configs())); + cgs.emplace_back(backend->create_cg()); } } diff --git a/src/thorin/be/codegen.h b/src/thorin/be/codegen.h index 1e85b597d..215a54b59 100644 --- a/src/thorin/be/codegen.h +++ b/src/thorin/be/codegen.h @@ -32,8 +32,7 @@ struct DeviceBackends; struct Backend { Backend(DeviceBackends& backends, World& src); - Cont2Config& kernel_configs() { return kernel_configs_; }; - virtual std::unique_ptr create_cg(const Cont2Config& config) = 0; + virtual std::unique_ptr create_cg() = 0; Thorin& thorin() { return device_code_; } Importer& importer() { return *importer_; } From a256ca6d7cbb9f70bad77c99c64debc804df7785 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Mon, 22 Jul 2024 18:54:35 +0200 Subject: [PATCH 275/342] c: fix broken syntax when accessing tuple components --- src/thorin/be/c/c.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/thorin/be/c/c.cpp b/src/thorin/be/c/c.cpp index 636eecfac..947551dc9 100644 --- a/src/thorin/be/c/c.cpp +++ b/src/thorin/be/c/c.cpp @@ -943,7 +943,7 @@ void CCodeGen::emit_access(Stream& s, const Type* agg_type, const Def* index, co } else if (agg_type->isa()) { s.fmt("[{}]", emit(index)); } else if (agg_type->isa()) { - s.fmt("{}e{}", prefix, emit_constant(index)); + s.fmt("{}e{}", prefix, primlit_value(index)); } else if (agg_type->isa()) { s.fmt("{}{}", prefix, agg_type->as()->op_name(primlit_value(index))); } else if (agg_type->isa()) { From c4b5b8de68a6727942d74b9a09927e64c11c2352 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Mon, 22 Jul 2024 18:55:19 +0200 Subject: [PATCH 276/342] backends: fix broken imported cont recovery logic --- src/thorin/be/codegen.cpp | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/thorin/be/codegen.cpp b/src/thorin/be/codegen.cpp index 5953283d4..9125830e7 100644 --- a/src/thorin/be/codegen.cpp +++ b/src/thorin/be/codegen.cpp @@ -22,16 +22,15 @@ namespace thorin { void Backend::prepare_kernel_configs() { device_code_.opt(); - auto& externals = backends_.world().externals(); + auto conts = device_code_.world().copy_continuations(); for (auto continuation : kernels_) { // recover the imported continuation (lost after the call to opt) Continuation* imported = nullptr; - for (auto [_, def] : externals) { - auto exported = def->isa(); - if (!exported) continue; - if (!exported->has_body()) continue; - if (exported->name() == continuation->name()) - imported = exported; + for (auto original_cont : conts) { + if (!original_cont) continue; + if (!original_cont->has_body()) continue; + if (original_cont->name() == continuation->name()) + imported = original_cont; } if (!imported) continue; From 129895a425b17b305b52294b2cf12fb84dc2f7e4 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Mon, 22 Jul 2024 19:00:03 +0200 Subject: [PATCH 277/342] c: get rid of typedefs and move primtype emission logic into a fn --- src/thorin/be/c/c.cpp | 159 +++++++++++++++++++++++++----------------- 1 file changed, 95 insertions(+), 64 deletions(-) diff --git a/src/thorin/be/c/c.cpp b/src/thorin/be/c/c.cpp index 947551dc9..b945bace8 100644 --- a/src/thorin/be/c/c.cpp +++ b/src/thorin/be/c/c.cpp @@ -103,6 +103,7 @@ class CCodeGen : public thorin::Emitter void finalize(Continuation*); private: + void convert_primtype(StringStream&s, PrimTypeTag tag, int len); std::string convert(const Type*); std::string addr_space_prefix(AddrSpace); std::string constructor_prefix(const Type*); @@ -210,6 +211,97 @@ bool CCodeGen::get_interface(HlsInterface &interface, HlsInterface &gmem) { return false; } +inline const char* stddef_primtype_name(PrimTypeTag tag) { + switch (tag) { + case PrimType_ps8: case PrimType_qs8: return "int8_t"; + case PrimType_pu8: case PrimType_qu8: return "uint8_t"; + case PrimType_ps16: case PrimType_qs16: return "int16_t"; + case PrimType_pu16: case PrimType_qu16: return "uint16_t"; + case PrimType_ps32: case PrimType_qs32: return "int32_t"; + case PrimType_pu32: case PrimType_qu32: return "uint32_t"; + case PrimType_ps64: case PrimType_qs64: return "int64_t"; + case PrimType_pu64: case PrimType_qu64: return "uint64_t"; + case PrimType_pf16: case PrimType_qf16: return "half"; + default: THORIN_UNREACHABLE; + } +} + +inline const char* cuda_scalar_primtype(PrimTypeTag tag) { + switch (tag) { + case PrimType_ps8: case PrimType_qs8: return "char"; + case PrimType_pu8: case PrimType_qu8: return "unsigned char"; + case PrimType_ps16: case PrimType_qs16: return "short"; + case PrimType_pu16: case PrimType_qu16: return "unsigned short"; + case PrimType_ps32: case PrimType_qs32: return "int"; + case PrimType_pu32: case PrimType_qu32: return "unsigned int"; + case PrimType_ps64: case PrimType_qs64: return "long long"; + case PrimType_pu64: case PrimType_qu64: return "unsigned long long"; + case PrimType_pf16: case PrimType_qf16: return "f16"; // typedef'd with macro magic + default: THORIN_UNREACHABLE; + } +} + +/// OpenCL uses these for scalar and vectors. +/// CUDA actually uses the same prefixes for its vectors +/// See +/// https://registry.khronos.org/OpenCL/sdk/3.0/docs/man/html/vectorDataTypes.html +/// https://docs.nvidia.com/cuda/cuda-c-programming-guide/index.html#built-in-vector-types +inline const char* opencl_cuda_vectorbase(PrimTypeTag tag) { + switch (tag) { + case PrimType_ps8: case PrimType_qs8: return "char"; + case PrimType_pu8: case PrimType_qu8: return "uchar"; + case PrimType_ps16: case PrimType_qs16: return "short"; + case PrimType_pu16: case PrimType_qu16: return "ushort"; + case PrimType_ps32: case PrimType_qs32: return "int"; + case PrimType_pu32: case PrimType_qu32: return "uint"; + case PrimType_ps64: case PrimType_qs64: return "long"; + case PrimType_pu64: case PrimType_qu64: return "ulong"; + case PrimType_pf16: case PrimType_qf16: return "half"; // NB: cuda has no vectors of half. + default: THORIN_UNREACHABLE; + } +} + +void CCodeGen::convert_primtype(StringStream& s, PrimTypeTag tag, int len) { + assert(len > 0); + + // Enable special code paths for f16 and f64 + switch (tag) { + case PrimType_pf16: case PrimType_qf16: use_fp_16_ = true; break; + case PrimType_pf64: case PrimType_qf64: use_fp_64_ = true; break; + default: break; + } + + // 'bool', 'float' and 'double' are identical everywhere + switch (tag) { + case PrimType_bool: s << "bool"; break; + case PrimType_pf32: case PrimType_qf32: s << "float"; break; + case PrimType_pf64: case PrimType_qf64: s << "double"; break; + default: { + if (lang_ == Lang::CUDA && len == 1) + s << cuda_scalar_primtype(tag); + else if (lang_ == Lang::CUDA || lang_ == Lang::OpenCL) + s << opencl_cuda_vectorbase(tag); + else + s << stddef_primtype_name(tag); + break; + } + } + + // length suffixes + if (len == 1) + return; + switch (lang_) { + case Lang::CUDA: + case Lang::OpenCL: + s << len; + break; + case Lang::HLS: + case Lang::C99: + s.fmt(" __attribute__ ((ext_vector_size ({})))", len); + break; + } +} + /* * convert */ @@ -223,23 +315,7 @@ std::string CCodeGen::convert(const Type* type) { if (type == world().unit_type() || type->isa() || type->isa()) s << "void"; else if (auto primtype = type->isa()) { - switch (primtype->primtype_tag()) { - case PrimType_bool: s << "bool"; break; - case PrimType_ps8: case PrimType_qs8: s << "i8"; break; - case PrimType_pu8: case PrimType_qu8: s << "u8"; break; - case PrimType_ps16: case PrimType_qs16: s << "i16"; break; - case PrimType_pu16: case PrimType_qu16: s << "u16"; break; - case PrimType_ps32: case PrimType_qs32: s << "i32"; break; - case PrimType_pu32: case PrimType_qu32: s << "u32"; break; - case PrimType_ps64: case PrimType_qs64: s << "i64"; break; - case PrimType_pu64: case PrimType_qu64: s << "u64"; break; - case PrimType_pf16: case PrimType_qf16: s << "f16"; use_fp_16_ = true; break; - case PrimType_pf32: case PrimType_qf32: s << "f32"; break; - case PrimType_pf64: case PrimType_qf64: s << "f64"; use_fp_64_ = true; break; - default: THORIN_UNREACHABLE; - } - if (primtype->is_vector()) - s << primtype->length(); + convert_primtype(s, primtype->primtype_tag(), vector_length(primtype)); } else if (auto array = type->isa()) { return types_[type] = convert(array->elem_type()); // IndefiniteArrayType always occurs within a pointer } else if (type->isa()) { @@ -402,21 +478,6 @@ void CCodeGen::emit_module() { stream_ << "#pragma OPENCL EXTENSION cl_khr_fp16 : enable\n"; if (use_fp_64_) stream_ << "#pragma OPENCL EXTENSION cl_khr_fp64 : enable\n"; - - stream_.fmt( "\n" - "typedef char i8;\n" - "typedef uchar u8;\n" - "typedef short i16;\n" - "typedef ushort u16;\n" - "typedef int i32;\n" - "typedef uint u32;\n" - "typedef long i64;\n" - "typedef ulong u64;\n"); - if (use_fp_16_) - stream_.fmt("typedef half f16;\n"); - stream_.fmt( "typedef float f32;\n"); - if (use_fp_64_) - stream_.fmt("typedef double f64;\n"); } stream_.endl(); @@ -441,45 +502,15 @@ void CCodeGen::emit_module() { stream_.fmt("#include \n"); } - if (lang_ == Lang::C99 || lang_ == Lang::HLS) { - stream_.fmt( "\n" - "typedef int8_t i8;\n" - "typedef uint8_t u8;\n" - "typedef int16_t i16;\n" - "typedef uint16_t u16;\n" - "typedef int32_t i32;\n" - "typedef uint32_t u32;\n" - "typedef int64_t i64;\n" - "typedef uint64_t u64;\n" - "typedef float f32;\n" - "typedef double f64;\n" - "\n"); - - if (use_fp_16_ && lang_ == Lang::HLS) - stream_.fmt("typedef half f16;\n"); - } - if (lang_ == Lang::CUDA) { - if (use_fp_16_) + if (use_fp_16_) { stream_.fmt("#include \n\n"); - stream_.fmt( "typedef char i8;\n" - "typedef unsigned char u8;\n" - "typedef short i16;\n" - "typedef unsigned short u16;\n" - "typedef int i32;\n" - "typedef unsigned int u32;\n" - "typedef long long i64;\n" - "typedef unsigned long long u64;\n" - "\n"); - if (use_fp_16_) stream_.fmt("#if __CUDACC_VER_MAJOR__ <= 8\n" "typedef half f16;\n" "#else\n" "typedef __half_raw f16;\n" "#endif\n"); - stream_.fmt( "typedef float f32;\n" - "typedef double f64;\n" - "\n"); + } } if (lang_ == Lang::CUDA || lang_ == Lang::HLS) { From ff8060fe34b7a4abc144575a52aae2adb8afcfb6 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Thu, 25 Jul 2024 14:59:38 +0200 Subject: [PATCH 278/342] added OpenCL_SPIRV backend --- src/thorin/be/codegen.cpp | 20 ++++++++++++++++++++ src/thorin/be/llvm/llvm.cpp | 1 + src/thorin/config.h.in | 1 + src/thorin/continuation.cpp | 1 + src/thorin/continuation.h | 1 + 5 files changed, 24 insertions(+) diff --git a/src/thorin/be/codegen.cpp b/src/thorin/be/codegen.cpp index 9125830e7..da96a3b19 100644 --- a/src/thorin/be/codegen.cpp +++ b/src/thorin/be/codegen.cpp @@ -14,6 +14,10 @@ #undef nodes #endif +#if THORIN_ENABLE_SPIRV +#include "thorin/be/spirv/spirv.h" +#endif + #include "thorin/transform/hls_channels.h" #include "thorin/transform/hls_kernel_launch.h" @@ -136,6 +140,19 @@ struct OpenCLBackend : public Backend { } }; +#if THORIN_ENABLE_SPIRV +struct OpenCLSPIRVBackend : public Backend { + explicit OpenCLSPIRVBackend(DeviceBackends& b, World& src) : Backend(b, src) { + b.register_intrinsic(Intrinsic::OpenCL_SPIRV, *this, get_gpu_kernel_config); + } + + std::unique_ptr create_cg() override { + spirv::Target target; + return std::make_unique(device_code_, target, backends_.debug(), &kernel_configs_); + } +}; +#endif + #if THORIN_ENABLE_LLVM struct AMDHSABackend : public Backend { explicit AMDHSABackend(DeviceBackends& b, World& src) : Backend(b, src) { @@ -238,6 +255,9 @@ DeviceBackends::DeviceBackends(thorin::World& world, int opt, bool debug, std::s #endif #if THORIN_ENABLE_SHADY register_backend(std::make_unique(*this, world)) +#endif +#if THORIN_ENABLE_SPIRV + register_backend(std::make_unique(*this, world)); #endif register_backend(std::make_unique(*this, world, hls_flags)); diff --git a/src/thorin/be/llvm/llvm.cpp b/src/thorin/be/llvm/llvm.cpp index afe2a95af..89032b405 100644 --- a/src/thorin/be/llvm/llvm.cpp +++ b/src/thorin/be/llvm/llvm.cpp @@ -1306,6 +1306,7 @@ std::vector CodeGen::emit_intrinsic(llvm::IRBuilder<>& irbuilder, case Intrinsic::CUDA: runtime_->emit_host_code(*this, irbuilder, Runtime::CUDA_PLATFORM, ".cu", continuation); break; case Intrinsic::NVVM: runtime_->emit_host_code(*this, irbuilder, Runtime::CUDA_PLATFORM, ".nvvm", continuation); break; case Intrinsic::OpenCL: runtime_->emit_host_code(*this, irbuilder, Runtime::OPENCL_PLATFORM, ".cl", continuation); break; + case Intrinsic::OpenCL_SPIRV: runtime_->emit_host_code(*this, irbuilder, Runtime::OPENCL_PLATFORM, ".spv", continuation); break; case Intrinsic::AMDGPUHSA: runtime_->emit_host_code(*this, irbuilder, Runtime::HSA_PLATFORM, ".amdgpu", continuation); break; case Intrinsic::AMDGPUPAL: runtime_->emit_host_code(*this, irbuilder, Runtime::PAL_PLATFORM, ".amdgpu", continuation); break; case Intrinsic::ShadyCompute: runtime_->emit_host_code(*this, irbuilder, Runtime::SHADY_PLATFORM, ".shady", continuation); break; diff --git a/src/thorin/config.h.in b/src/thorin/config.h.in index e8f2dfc42..3a6b28644 100644 --- a/src/thorin/config.h.in +++ b/src/thorin/config.h.in @@ -8,6 +8,7 @@ #cmakedefine01 THORIN_ENABLE_JSON #cmakedefine01 THORIN_ENABLE_RV #cmakedefine01 THORIN_ENABLE_SHADY +#cmakedefine01 THORIN_ENABLE_SPIRV #cmakedefine01 THORIN_ENABLE_RLIMITS #endif diff --git a/src/thorin/continuation.cpp b/src/thorin/continuation.cpp index 9001206b4..1d97faa9a 100644 --- a/src/thorin/continuation.cpp +++ b/src/thorin/continuation.cpp @@ -248,6 +248,7 @@ void Continuation::set_intrinsic() { if (name() == "cuda") attributes().intrinsic = Intrinsic::CUDA; else if (name() == "nvvm") attributes().intrinsic = Intrinsic::NVVM; else if (name() == "opencl") attributes().intrinsic = Intrinsic::OpenCL; + else if (name() == "opencl_spirv") attributes().intrinsic = Intrinsic::OpenCL_SPIRV; else if (name() == "amdgpu_hsa") attributes().intrinsic = Intrinsic::AMDGPUHSA; else if (name() == "amdgpu_pal") attributes().intrinsic = Intrinsic::AMDGPUPAL; else if (name() == "shady_compute") attributes().intrinsic = Intrinsic::ShadyCompute; diff --git a/src/thorin/continuation.h b/src/thorin/continuation.h index 73e47e36a..d537134fe 100644 --- a/src/thorin/continuation.h +++ b/src/thorin/continuation.h @@ -95,6 +95,7 @@ enum class Intrinsic : uint8_t { CUDA = AcceleratorBegin, ///< Internal CUDA-Backend. NVVM, ///< Internal NNVM-Backend. OpenCL, ///< Internal OpenCL-Backend. + OpenCL_SPIRV, ///< Internal OpenCL-Backend. AMDGPUHSA, ///< Internal AMDGPU-HSA-Backend. AMDGPUPAL, ///< Internal AMDGPU-PAL-Backend. ShadyCompute, ///< Internal Shady Compute Backend. From cfa14c5eaa53d3fcef76c2037d20c6c1b3f70a79 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Thu, 25 Jul 2024 15:00:13 +0200 Subject: [PATCH 279/342] commented out weird LEA address space stuff --- src/thorin/be/spirv/spirv.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/thorin/be/spirv/spirv.cpp b/src/thorin/be/spirv/spirv.cpp index 1e4e66532..af485c169 100644 --- a/src/thorin/be/spirv/spirv.cpp +++ b/src/thorin/be/spirv/spirv.cpp @@ -631,14 +631,14 @@ SpvId CodeGen::emit_bb(BasicBlockBuilder* bb, const Def* def) { } else if (auto enter = def->isa()) { return emit_unsafe(enter->mem()); } else if (auto lea = def->isa()) { - switch (lea->type()->addr_space()) { - case AddrSpace::Global: - case AddrSpace::Shared: - break; - default: - world().ELOG("LEA is only allowed in global & shared address spaces"); - break; - } + //switch (lea->type()->addr_space()) { + // case AddrSpace::Global: + // case AddrSpace::Shared: + // break; + // default: + // world().ELOG("LEA is only allowed in global & shared address spaces"); + // break; + //} auto type = convert(lea->type()).id; auto offset = emit(lea->index()); return bb->ptr_access_chain(type, emit(lea->ptr()), offset, {}); From ca9b0d60f4f636cfdb4c1a5b2a7d9a6ecc1c8901 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Thu, 25 Jul 2024 15:00:29 +0200 Subject: [PATCH 280/342] c/opencl: handle ptr as casts --- src/thorin/be/c/c.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/thorin/be/c/c.cpp b/src/thorin/be/c/c.cpp index b945bace8..398e6dac4 100644 --- a/src/thorin/be/c/c.cpp +++ b/src/thorin/be/c/c.cpp @@ -1088,6 +1088,8 @@ std::string CCodeGen::emit_def(BB* bb, const Def* def) { s.fmt("(({})->e)", src); } else if (s_ptr && d_ptr && s_ptr->addr_space() == d_ptr->addr_space()) { s.fmt("(({}) {})", d_t, src); + } else if (s_ptr && d_ptr && s_ptr->addr_space() != d_ptr->addr_space() && lang_ == Lang::OpenCL) { + s.fmt("(({}) ((size_t) {}))", d_t, src); } else if (conv->isa()) { auto s_prim = s_type->isa(); auto d_prim = d_type->isa(); From 0cb3002cba991713df64cd5062621d7feffae226 Mon Sep 17 00:00:00 2001 From: Matthias Kurtenacker Date: Tue, 1 Oct 2024 12:37:21 +0200 Subject: [PATCH 281/342] Slight updates to switch to llvm 18. --- src/thorin/be/llvm/cpu.cpp | 2 +- src/thorin/be/llvm/llvm.cpp | 4 ++-- src/thorin/be/llvm/nvvm.cpp | 4 ++-- src/thorin/be/llvm/parallel.cpp | 6 +++--- src/thorin/be/llvm/runtime.cpp | 32 ++++++++++++++++---------------- 5 files changed, 24 insertions(+), 24 deletions(-) diff --git a/src/thorin/be/llvm/cpu.cpp b/src/thorin/be/llvm/cpu.cpp index 4a2d6dfbc..133d5ddfe 100644 --- a/src/thorin/be/llvm/cpu.cpp +++ b/src/thorin/be/llvm/cpu.cpp @@ -1,7 +1,7 @@ #include "thorin/be/llvm/cpu.h" #include -#include +#include #include #include #include diff --git a/src/thorin/be/llvm/llvm.cpp b/src/thorin/be/llvm/llvm.cpp index ecea16edd..31f4440c9 100644 --- a/src/thorin/be/llvm/llvm.cpp +++ b/src/thorin/be/llvm/llvm.cpp @@ -4,7 +4,7 @@ #include #include // TODO don't used std::unordered_* -#include +#include #include #include #include @@ -17,7 +17,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/src/thorin/be/llvm/nvvm.cpp b/src/thorin/be/llvm/nvvm.cpp index 88c488da8..7c39c160a 100644 --- a/src/thorin/be/llvm/nvvm.cpp +++ b/src/thorin/be/llvm/nvvm.cpp @@ -3,7 +3,7 @@ #include #include // TODO don't used std::unordered_* -#include +#include #include #include #include @@ -11,7 +11,7 @@ #include #include #include -#include +#include #include #include "thorin/primop.h" diff --git a/src/thorin/be/llvm/parallel.cpp b/src/thorin/be/llvm/parallel.cpp index 5b83bdcd2..96ab7142d 100644 --- a/src/thorin/be/llvm/parallel.cpp +++ b/src/thorin/be/llvm/parallel.cpp @@ -51,7 +51,7 @@ void CodeGen::emit_parallel(llvm::IRBuilder<>& irbuilder, Continuation* continua // create wrapper function and call the runtime // wrapper(void* closure, int lower, int upper) - llvm::Type* wrapper_arg_types[] = { irbuilder.getInt8PtrTy(0), irbuilder.getInt32Ty(), irbuilder.getInt32Ty() }; + llvm::Type* wrapper_arg_types[] = { irbuilder.getPtrTy(), irbuilder.getInt32Ty(), irbuilder.getInt32Ty() }; auto wrapper_ft = llvm::FunctionType::get(irbuilder.getVoidTy(), wrapper_arg_types, false); auto wrapper_name = kernel->unique_name() + "_parallel_for"; auto wrapper = (llvm::Function*)module_->getOrInsertFunction(wrapper_name, wrapper_ft).getCallee()->stripPointerCasts(); @@ -143,7 +143,7 @@ void CodeGen::emit_fibers(llvm::IRBuilder<>& irbuilder, Continuation* continuati // create wrapper function and call the runtime // wrapper(void* closure, int lower, int upper) - llvm::Type* wrapper_arg_types[] = { irbuilder.getInt8PtrTy(0), irbuilder.getInt32Ty(), irbuilder.getInt32Ty() }; + llvm::Type* wrapper_arg_types[] = { irbuilder.getPtrTy(), irbuilder.getInt32Ty(), irbuilder.getInt32Ty() }; auto wrapper_ft = llvm::FunctionType::get(irbuilder.getVoidTy(), wrapper_arg_types, false); auto wrapper_name = kernel->unique_name() + "_fibers"; auto wrapper = (llvm::Function*)module_->getOrInsertFunction(wrapper_name, wrapper_ft).getCallee()->stripPointerCasts(); @@ -225,7 +225,7 @@ llvm::Value* CodeGen::emit_spawn(llvm::IRBuilder<>& irbuilder, Continuation* con // create wrapper function and call the runtime // wrapper(void* closure) - llvm::Type* wrapper_arg_types[] = { irbuilder.getInt8PtrTy(0) }; + llvm::Type* wrapper_arg_types[] = { irbuilder.getPtrTy() }; auto wrapper_ft = llvm::FunctionType::get(irbuilder.getVoidTy(), wrapper_arg_types, false); auto wrapper_name = kernel->unique_name() + "_spawn_thread"; auto wrapper = (llvm::Function*)module_->getOrInsertFunction(wrapper_name, wrapper_ft).getCallee()->stripPointerCasts(); diff --git a/src/thorin/be/llvm/runtime.cpp b/src/thorin/be/llvm/runtime.cpp index 375713091..d4467edf8 100644 --- a/src/thorin/be/llvm/runtime.cpp +++ b/src/thorin/be/llvm/runtime.cpp @@ -85,11 +85,11 @@ void Runtime::emit_host_code(CodeGen& code_gen, llvm::IRBuilder<>& builder, Plat const size_t num_kernel_args = body->num_args() - LaunchArgs::Num; // allocate argument pointers, sizes, and types - llvm::Value* args = code_gen.emit_alloca(builder, llvm::ArrayType::get(builder.getInt8PtrTy(), num_kernel_args), "args"); - llvm::Value* sizes = code_gen.emit_alloca(builder, llvm::ArrayType::get(builder.getInt32Ty(), num_kernel_args), "sizes"); - llvm::Value* aligns = code_gen.emit_alloca(builder, llvm::ArrayType::get(builder.getInt32Ty(), num_kernel_args), "aligns"); - llvm::Value* allocs = code_gen.emit_alloca(builder, llvm::ArrayType::get(builder.getInt32Ty(), num_kernel_args), "allocs"); - llvm::Value* types = code_gen.emit_alloca(builder, llvm::ArrayType::get(builder.getInt8Ty(), num_kernel_args), "types"); + llvm::Value* args = code_gen.emit_alloca(builder, llvm::ArrayType::get(builder.getPtrTy(), num_kernel_args), "args"); + llvm::Value* sizes = code_gen.emit_alloca(builder, llvm::ArrayType::get(builder.getInt32Ty(), num_kernel_args), "sizes"); + llvm::Value* aligns = code_gen.emit_alloca(builder, llvm::ArrayType::get(builder.getInt32Ty(), num_kernel_args), "aligns"); + llvm::Value* allocs = code_gen.emit_alloca(builder, llvm::ArrayType::get(builder.getInt32Ty(), num_kernel_args), "allocs"); + llvm::Value* types = code_gen.emit_alloca(builder, llvm::ArrayType::get(builder.getInt8Ty(), num_kernel_args), "types"); // fill array of arguments for (size_t i = 0; i < num_kernel_args; ++i) { @@ -109,7 +109,7 @@ void Runtime::emit_host_code(CodeGen& code_gen, llvm::IRBuilder<>& builder, Plat if (!contains_ptrtype(target_arg->type())) world.wdef(target_arg, "argument '{}' of aggregate type '{}' contains pointer (not supported in OpenCL 1.2)", target_arg, target_arg->type()); - void_ptr = builder.CreatePointerCast(alloca, builder.getInt8PtrTy()); + void_ptr = builder.CreatePointerCast(alloca, builder.getPtrTy()); arg_type = KernelArgType::Struct; } else if (target_arg->type()->isa()) { auto ptr = target_arg->type()->as(); @@ -118,17 +118,17 @@ void Runtime::emit_host_code(CodeGen& code_gen, llvm::IRBuilder<>& builder, Plat if (!rtype->isa()) world.edef(target_arg, "currently only pointers to arrays supported as kernel argument; argument has different type: {}", ptr); - auto alloca = code_gen.emit_alloca(builder, builder.getInt8PtrTy(), target_arg->name()); - auto target_ptr = builder.CreatePointerCast(target_val, builder.getInt8PtrTy()); + auto alloca = code_gen.emit_alloca(builder, builder.getPtrTy(), target_arg->name()); + auto target_ptr = builder.CreatePointerCast(target_val, builder.getPtrTy()); builder.CreateStore(target_ptr, alloca); - void_ptr = builder.CreatePointerCast(alloca, builder.getInt8PtrTy()); + void_ptr = builder.CreatePointerCast(alloca, builder.getPtrTy()); arg_type = KernelArgType::Ptr; } else { // normal variable auto alloca = code_gen.emit_alloca(builder, target_val->getType(), target_arg->name()); builder.CreateStore(target_val, alloca); - void_ptr = builder.CreatePointerCast(alloca, builder.getInt8PtrTy()); + void_ptr = builder.CreatePointerCast(alloca, builder.getPtrTy()); arg_type = KernelArgType::Val; } @@ -203,8 +203,8 @@ llvm::Value* Runtime::parallel_for( { llvm::Value* parallel_args[] = { num_threads, lower, upper, - builder.CreatePointerCast(closure_ptr, builder.getInt8PtrTy()), - builder.CreatePointerCast(fun_ptr, builder.getInt8PtrTy()) + builder.CreatePointerCast(closure_ptr, builder.getPtrTy()), + builder.CreatePointerCast(fun_ptr, builder.getPtrTy()) }; return builder.CreateCall(get(code_gen, "anydsl_parallel_for"), parallel_args); } @@ -215,16 +215,16 @@ llvm::Value* Runtime::spawn_fibers( { llvm::Value* fibers_args[] = { num_threads, num_blocks, num_warps, - builder.CreatePointerCast(closure_ptr, builder.getInt8PtrTy()), - builder.CreatePointerCast(fun_ptr, builder.getInt8PtrTy()) + builder.CreatePointerCast(closure_ptr, builder.getPtrTy()), + builder.CreatePointerCast(fun_ptr, builder.getPtrTy()) }; return builder.CreateCall(get(code_gen, "anydsl_fibers_spawn"), fibers_args); } llvm::Value* Runtime::spawn_thread(CodeGen& code_gen, llvm::IRBuilder<>& builder, llvm::Value* closure_ptr, llvm::Value* fun_ptr) { llvm::Value* spawn_args[] = { - builder.CreatePointerCast(closure_ptr, builder.getInt8PtrTy()), - builder.CreatePointerCast(fun_ptr, builder.getInt8PtrTy()) + builder.CreatePointerCast(closure_ptr, builder.getPtrTy()), + builder.CreatePointerCast(fun_ptr, builder.getPtrTy()) }; return builder.CreateCall(get(code_gen, "anydsl_spawn_thread"), spawn_args); } From 11ab8dfe1188a49924b0195d52f0f8c6a49be2f3 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Wed, 16 Oct 2024 14:11:14 +0200 Subject: [PATCH 282/342] spirv: fix Cast def emission --- src/thorin/be/spirv/spirv.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/thorin/be/spirv/spirv.cpp b/src/thorin/be/spirv/spirv.cpp index af485c169..be1b22532 100644 --- a/src/thorin/be/spirv/spirv.cpp +++ b/src/thorin/be/spirv/spirv.cpp @@ -738,8 +738,8 @@ SpvId CodeGen::emit_bb(BasicBlockBuilder* bb, const Def* def) { auto src_kind = classify_primtype(src_prim); auto dst_kind = classify_primtype(dst_prim); - size_t src_bitwidth = conv_src_type.layout->size; - size_t dst_bitwidth = conv_src_type.layout->size; + size_t src_bitwidth = conv_src_type.layout->size * 8; + size_t dst_bitwidth = conv_src_type.layout->size * 8; SpvId data = emit(cast->from()); @@ -796,6 +796,8 @@ SpvId CodeGen::emit_bb(BasicBlockBuilder* bb, const Def* def) { } } } + + return data; } else THORIN_UNREACHABLE; } else if (def->isa()) { return bb->undef(convert(def->type()).id); From 80291f022c5e67749dab93bc346a4313d4776720 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Wed, 16 Oct 2024 14:11:30 +0200 Subject: [PATCH 283/342] added LevelZero runtime platform --- src/thorin/be/codegen.cpp | 11 ++++++++++ src/thorin/be/llvm/llvm.cpp | 39 ++++++++++++++++++------------------ src/thorin/be/llvm/runtime.h | 1 + src/thorin/continuation.cpp | 1 + src/thorin/continuation.h | 1 + 5 files changed, 34 insertions(+), 19 deletions(-) diff --git a/src/thorin/be/codegen.cpp b/src/thorin/be/codegen.cpp index da96a3b19..82213c87e 100644 --- a/src/thorin/be/codegen.cpp +++ b/src/thorin/be/codegen.cpp @@ -151,6 +151,17 @@ struct OpenCLSPIRVBackend : public Backend { return std::make_unique(device_code_, target, backends_.debug(), &kernel_configs_); } }; + +struct LevelZeroSPIRVBackend : public Backend { + explicit LevelZeroSPIRVBackend(DeviceBackends& b, World& src) : Backend(b, src) { + b.register_intrinsic(Intrinsic::LevelZero_SPIRV, *this, get_gpu_kernel_config); + } + + std::unique_ptr create_cg() override { + spirv::Target target; + return std::make_unique(device_code_, target, backends_.debug(), &kernel_configs_); + } +}; #endif #if THORIN_ENABLE_LLVM diff --git a/src/thorin/be/llvm/llvm.cpp b/src/thorin/be/llvm/llvm.cpp index 89032b405..7b66725d1 100644 --- a/src/thorin/be/llvm/llvm.cpp +++ b/src/thorin/be/llvm/llvm.cpp @@ -1296,25 +1296,26 @@ std::vector CodeGen::emit_intrinsic(llvm::IRBuilder<>& irbuilder, } switch (callee->intrinsic()) { - case Intrinsic::Atomic: return { emit_atomic(irbuilder, continuation) }; - case Intrinsic::AtomicLoad: return { emit_atomic_load(irbuilder, continuation) }; - case Intrinsic::AtomicStore: emit_atomic_store(irbuilder, continuation); break; - case Intrinsic::CmpXchg: return emit_cmpxchg(irbuilder, continuation, false); - case Intrinsic::CmpXchgWeak: return emit_cmpxchg(irbuilder, continuation, true); - case Intrinsic::Fence: emit_fence(irbuilder, continuation); break; - case Intrinsic::Reserve: return { emit_reserve(irbuilder, continuation) }; - case Intrinsic::CUDA: runtime_->emit_host_code(*this, irbuilder, Runtime::CUDA_PLATFORM, ".cu", continuation); break; - case Intrinsic::NVVM: runtime_->emit_host_code(*this, irbuilder, Runtime::CUDA_PLATFORM, ".nvvm", continuation); break; - case Intrinsic::OpenCL: runtime_->emit_host_code(*this, irbuilder, Runtime::OPENCL_PLATFORM, ".cl", continuation); break; - case Intrinsic::OpenCL_SPIRV: runtime_->emit_host_code(*this, irbuilder, Runtime::OPENCL_PLATFORM, ".spv", continuation); break; - case Intrinsic::AMDGPUHSA: runtime_->emit_host_code(*this, irbuilder, Runtime::HSA_PLATFORM, ".amdgpu", continuation); break; - case Intrinsic::AMDGPUPAL: runtime_->emit_host_code(*this, irbuilder, Runtime::PAL_PLATFORM, ".amdgpu", continuation); break; - case Intrinsic::ShadyCompute: runtime_->emit_host_code(*this, irbuilder, Runtime::SHADY_PLATFORM, ".shady", continuation); break; - case Intrinsic::HLS: emit_hls(irbuilder, continuation); break; - case Intrinsic::Parallel: emit_parallel(irbuilder, continuation); break; - case Intrinsic::Fibers: emit_fibers(irbuilder, continuation); break; - case Intrinsic::Spawn: return { emit_spawn(irbuilder, continuation) }; - case Intrinsic::Sync: emit_sync(irbuilder, continuation); break; + case Intrinsic::Atomic: return { emit_atomic(irbuilder, continuation) }; + case Intrinsic::AtomicLoad: return { emit_atomic_load(irbuilder, continuation) }; + case Intrinsic::AtomicStore: emit_atomic_store(irbuilder, continuation); break; + case Intrinsic::CmpXchg: return emit_cmpxchg(irbuilder, continuation, false); + case Intrinsic::CmpXchgWeak: return emit_cmpxchg(irbuilder, continuation, true); + case Intrinsic::Fence: emit_fence(irbuilder, continuation); break; + case Intrinsic::Reserve: return { emit_reserve(irbuilder, continuation) }; + case Intrinsic::CUDA: runtime_->emit_host_code(*this, irbuilder, Runtime::CUDA_PLATFORM, ".cu", continuation); break; + case Intrinsic::NVVM: runtime_->emit_host_code(*this, irbuilder, Runtime::CUDA_PLATFORM, ".nvvm", continuation); break; + case Intrinsic::OpenCL: runtime_->emit_host_code(*this, irbuilder, Runtime::OPENCL_PLATFORM, ".cl", continuation); break; + case Intrinsic::OpenCL_SPIRV: runtime_->emit_host_code(*this, irbuilder, Runtime::OPENCL_PLATFORM, ".spv", continuation); break; + case Intrinsic::LevelZero_SPIRV: runtime_->emit_host_code(*this, irbuilder, Runtime::LEVEL_ZERO_PLATFORM, ".spv", continuation); break; + case Intrinsic::AMDGPUHSA: runtime_->emit_host_code(*this, irbuilder, Runtime::HSA_PLATFORM, ".amdgpu", continuation); break; + case Intrinsic::AMDGPUPAL: runtime_->emit_host_code(*this, irbuilder, Runtime::PAL_PLATFORM, ".amdgpu", continuation); break; + case Intrinsic::ShadyCompute: runtime_->emit_host_code(*this, irbuilder, Runtime::SHADY_PLATFORM, ".shady", continuation); break; + case Intrinsic::HLS: emit_hls(irbuilder, continuation); break; + case Intrinsic::Parallel: emit_parallel(irbuilder, continuation); break; + case Intrinsic::Fibers: emit_fibers(irbuilder, continuation); break; + case Intrinsic::Spawn: return { emit_spawn(irbuilder, continuation) }; + case Intrinsic::Sync: emit_sync(irbuilder, continuation); break; #if THORIN_ENABLE_RV case Intrinsic::Vectorize: emit_vectorize_continuation(irbuilder, continuation); break; #else diff --git a/src/thorin/be/llvm/runtime.h b/src/thorin/be/llvm/runtime.h index 9038040a0..551d06797 100644 --- a/src/thorin/be/llvm/runtime.h +++ b/src/thorin/be/llvm/runtime.h @@ -24,6 +24,7 @@ class Runtime { OPENCL_PLATFORM, HSA_PLATFORM, PAL_PLATFORM, + LEVEL_ZERO_PLATFORM, SHADY_PLATFORM, }; diff --git a/src/thorin/continuation.cpp b/src/thorin/continuation.cpp index 1d97faa9a..d99dd0ae9 100644 --- a/src/thorin/continuation.cpp +++ b/src/thorin/continuation.cpp @@ -249,6 +249,7 @@ void Continuation::set_intrinsic() { else if (name() == "nvvm") attributes().intrinsic = Intrinsic::NVVM; else if (name() == "opencl") attributes().intrinsic = Intrinsic::OpenCL; else if (name() == "opencl_spirv") attributes().intrinsic = Intrinsic::OpenCL_SPIRV; + else if (name() == "levelzero") attributes().intrinsic = Intrinsic::LevelZero_SPIRV; else if (name() == "amdgpu_hsa") attributes().intrinsic = Intrinsic::AMDGPUHSA; else if (name() == "amdgpu_pal") attributes().intrinsic = Intrinsic::AMDGPUPAL; else if (name() == "shady_compute") attributes().intrinsic = Intrinsic::ShadyCompute; diff --git a/src/thorin/continuation.h b/src/thorin/continuation.h index d537134fe..fb747c5b7 100644 --- a/src/thorin/continuation.h +++ b/src/thorin/continuation.h @@ -96,6 +96,7 @@ enum class Intrinsic : uint8_t { NVVM, ///< Internal NNVM-Backend. OpenCL, ///< Internal OpenCL-Backend. OpenCL_SPIRV, ///< Internal OpenCL-Backend. + LevelZero_SPIRV, ///< Internal SPIRV for Level0-Backend. AMDGPUHSA, ///< Internal AMDGPU-HSA-Backend. AMDGPUPAL, ///< Internal AMDGPU-PAL-Backend. ShadyCompute, ///< Internal Shady Compute Backend. From a9757904024addf77f8b6db7ddfcd9fd8d4e3698 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Wed, 16 Oct 2024 14:33:37 +0200 Subject: [PATCH 284/342] move runtime enums outside of llvm backend --- src/thorin/CMakeLists.txt | 1 + src/thorin/be/codegen.cpp | 1 + src/thorin/be/kernel_config.h | 12 ------------ src/thorin/be/llvm/llvm.cpp | 16 ++++++++-------- src/thorin/be/llvm/runtime.h | 11 +---------- src/thorin/be/runtime.h | 29 +++++++++++++++++++++++++++++ 6 files changed, 40 insertions(+), 30 deletions(-) create mode 100644 src/thorin/be/runtime.h diff --git a/src/thorin/CMakeLists.txt b/src/thorin/CMakeLists.txt index 01044d98f..7910f0dfa 100644 --- a/src/thorin/CMakeLists.txt +++ b/src/thorin/CMakeLists.txt @@ -33,6 +33,7 @@ set(THORIN_SOURCES be/emitter.h be/c/c.cpp be/c/c.h + be/runtime.h be/kernel_config.h tables/allnodes.h tables/arithoptable.h diff --git a/src/thorin/be/codegen.cpp b/src/thorin/be/codegen.cpp index 82213c87e..4d9ec7c83 100644 --- a/src/thorin/be/codegen.cpp +++ b/src/thorin/be/codegen.cpp @@ -1,6 +1,7 @@ #include "thorin/be/codegen.h" #include "thorin/be/c/c.h" +#include "thorin/be/runtime.h" #if THORIN_ENABLE_LLVM #include "thorin/be/llvm/nvvm.h" diff --git a/src/thorin/be/kernel_config.h b/src/thorin/be/kernel_config.h index 787b579a6..c1d36aeb3 100644 --- a/src/thorin/be/kernel_config.h +++ b/src/thorin/be/kernel_config.h @@ -6,18 +6,6 @@ namespace thorin { -struct KernelLaunchArgs { - enum { - Mem = 0, - Device, - Space, - Config, - Body, - Return, - Num - }; -}; - class KernelConfig : public RuntimeCast { public: virtual ~KernelConfig() {} diff --git a/src/thorin/be/llvm/llvm.cpp b/src/thorin/be/llvm/llvm.cpp index 7b66725d1..362703d21 100644 --- a/src/thorin/be/llvm/llvm.cpp +++ b/src/thorin/be/llvm/llvm.cpp @@ -1303,14 +1303,14 @@ std::vector CodeGen::emit_intrinsic(llvm::IRBuilder<>& irbuilder, case Intrinsic::CmpXchgWeak: return emit_cmpxchg(irbuilder, continuation, true); case Intrinsic::Fence: emit_fence(irbuilder, continuation); break; case Intrinsic::Reserve: return { emit_reserve(irbuilder, continuation) }; - case Intrinsic::CUDA: runtime_->emit_host_code(*this, irbuilder, Runtime::CUDA_PLATFORM, ".cu", continuation); break; - case Intrinsic::NVVM: runtime_->emit_host_code(*this, irbuilder, Runtime::CUDA_PLATFORM, ".nvvm", continuation); break; - case Intrinsic::OpenCL: runtime_->emit_host_code(*this, irbuilder, Runtime::OPENCL_PLATFORM, ".cl", continuation); break; - case Intrinsic::OpenCL_SPIRV: runtime_->emit_host_code(*this, irbuilder, Runtime::OPENCL_PLATFORM, ".spv", continuation); break; - case Intrinsic::LevelZero_SPIRV: runtime_->emit_host_code(*this, irbuilder, Runtime::LEVEL_ZERO_PLATFORM, ".spv", continuation); break; - case Intrinsic::AMDGPUHSA: runtime_->emit_host_code(*this, irbuilder, Runtime::HSA_PLATFORM, ".amdgpu", continuation); break; - case Intrinsic::AMDGPUPAL: runtime_->emit_host_code(*this, irbuilder, Runtime::PAL_PLATFORM, ".amdgpu", continuation); break; - case Intrinsic::ShadyCompute: runtime_->emit_host_code(*this, irbuilder, Runtime::SHADY_PLATFORM, ".shady", continuation); break; + case Intrinsic::CUDA: runtime_->emit_host_code(*this, irbuilder, Platform::CUDA_PLATFORM, ".cu", continuation); break; + case Intrinsic::NVVM: runtime_->emit_host_code(*this, irbuilder, Platform::CUDA_PLATFORM, ".nvvm", continuation); break; + case Intrinsic::OpenCL: runtime_->emit_host_code(*this, irbuilder, Platform::OPENCL_PLATFORM, ".cl", continuation); break; + case Intrinsic::OpenCL_SPIRV: runtime_->emit_host_code(*this, irbuilder, Platform::OPENCL_PLATFORM, ".spv", continuation); break; + case Intrinsic::LevelZero_SPIRV: runtime_->emit_host_code(*this, irbuilder, Platform::LEVEL_ZERO_PLATFORM, ".spv", continuation); break; + case Intrinsic::AMDGPUHSA: runtime_->emit_host_code(*this, irbuilder, Platform::HSA_PLATFORM, ".amdgpu", continuation); break; + case Intrinsic::AMDGPUPAL: runtime_->emit_host_code(*this, irbuilder, Platform::PAL_PLATFORM, ".amdgpu", continuation); break; + case Intrinsic::ShadyCompute: runtime_->emit_host_code(*this, irbuilder, Platform::SHADY_PLATFORM, ".shady", continuation); break; case Intrinsic::HLS: emit_hls(irbuilder, continuation); break; case Intrinsic::Parallel: emit_parallel(irbuilder, continuation); break; case Intrinsic::Fibers: emit_fibers(irbuilder, continuation); break; diff --git a/src/thorin/be/llvm/runtime.h b/src/thorin/be/llvm/runtime.h index 551d06797..fca64aa6f 100644 --- a/src/thorin/be/llvm/runtime.h +++ b/src/thorin/be/llvm/runtime.h @@ -7,6 +7,7 @@ #include #include "thorin/world.h" +#include "thorin/be/runtime.h" namespace thorin::llvm { @@ -18,16 +19,6 @@ class Runtime { public: Runtime(llvm::LLVMContext&, llvm::Module&); - enum Platform { - CPU_PLATFORM, - CUDA_PLATFORM, - OPENCL_PLATFORM, - HSA_PLATFORM, - PAL_PLATFORM, - LEVEL_ZERO_PLATFORM, - SHADY_PLATFORM, - }; - /// Emits a call to anydsl_launch_kernel. llvm::Value* launch_kernel( CodeGen&, llvm::IRBuilder<>&, llvm::Value* device, diff --git a/src/thorin/be/runtime.h b/src/thorin/be/runtime.h new file mode 100644 index 000000000..68cdd9b97 --- /dev/null +++ b/src/thorin/be/runtime.h @@ -0,0 +1,29 @@ +#ifndef THORIN_RUNTIME_H +#define THORIN_RUNTIME_H + +/// Backend-agnostic information to interface with the runtime component +namespace thorin { + +enum Platform { + CPU_PLATFORM, + CUDA_PLATFORM, + OPENCL_PLATFORM, + HSA_PLATFORM, + PAL_PLATFORM, + LEVEL_ZERO_PLATFORM, + SHADY_PLATFORM, +}; + +enum KernelLaunchArgs { + Mem = 0, + Device, + Space, + Config, + Body, + Return, + Num +}; + +} + +#endif From 6b41a16524e3ec5a28663e9920d511e992cde7b2 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Wed, 16 Oct 2024 14:56:52 +0200 Subject: [PATCH 285/342] spirv: use Ptr variant of OpAccessChain only where appropriate --- src/thorin/be/spirv/spirv.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/thorin/be/spirv/spirv.cpp b/src/thorin/be/spirv/spirv.cpp index be1b22532..edae2cf59 100644 --- a/src/thorin/be/spirv/spirv.cpp +++ b/src/thorin/be/spirv/spirv.cpp @@ -641,7 +641,9 @@ SpvId CodeGen::emit_bb(BasicBlockBuilder* bb, const Def* def) { //} auto type = convert(lea->type()).id; auto offset = emit(lea->index()); - return bb->ptr_access_chain(type, emit(lea->ptr()), offset, {}); + if (lea->ptr_pointee()->isa()) + return bb->ptr_access_chain(type, emit(lea->ptr()), offset, { }); + return bb->access_chain(type, emit(lea->ptr()), { offset }); } else if (auto aggop = def->isa()) { auto agg_type = convert(aggop->agg()->type()).id; From 2c214ec1d448796f5a56e6cd4adf0db59e0faa7a Mon Sep 17 00:00:00 2001 From: Matthias Kurtenacker Date: Mon, 24 Jun 2024 19:40:06 +0200 Subject: [PATCH 286/342] Do not warn users if RV is not found. We have info messages for that. --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c31f7152a..8bfbeecd2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -48,7 +48,7 @@ if(LLVM_FOUND) endif() endif() # check for RV - find_package(RV) + find_package(RV QUIET CONFIG) if(RV_FOUND) message(STATUS "Building with RV from LLVM installation.") else() From e99f9b8bf8e388d5d4e7bcd190782bd8720206d6 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Fri, 18 Oct 2024 14:26:43 +0200 Subject: [PATCH 287/342] remove useless args field in spirv::BB --- src/thorin/be/spirv/spirv.cpp | 4 +--- src/thorin/be/spirv/spirv_private.h | 1 - 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/src/thorin/be/spirv/spirv.cpp b/src/thorin/be/spirv/spirv.cpp index edae2cf59..25f2e7b3d 100644 --- a/src/thorin/be/spirv/spirv.cpp +++ b/src/thorin/be/spirv/spirv.cpp @@ -300,10 +300,9 @@ void CodeGen::emit_epilogue(Continuation* continuation) { continue; } auto val = emit(arg); - bb->args[arg] = val; auto* param = dst_cont->param(index); auto& phi = cont2bb_[dst_cont]->phis_map[param]; - phi.preds.emplace_back(bb->args[arg], emit_as_bb(continuation)); + phi.preds.emplace_back(val, emit_as_bb(continuation)); } bb->branch(emit(dst_cont)); } else if (app.callee() == world().branch()) { @@ -311,7 +310,6 @@ void CodeGen::emit_epilogue(Continuation* continuation) { emit_unsafe(mem); auto cond = emit(app.arg(1)); - bb->args.emplace(app.arg(2), cond); auto tbb = emit(app.arg(2)); auto fbb = emit(app.arg(3)); bb->branch_conditional(cond, tbb, fbb); diff --git a/src/thorin/be/spirv/spirv_private.h b/src/thorin/be/spirv/spirv_private.h index ef1d72979..3d6701673 100644 --- a/src/thorin/be/spirv/spirv_private.h +++ b/src/thorin/be/spirv/spirv_private.h @@ -15,7 +15,6 @@ struct BasicBlockBuilder : public builder::SpvBasicBlockBuilder { FnBuilder& fn_builder; FileBuilder& file_builder; std::unordered_map phis_map; - DefMap args; }; struct FnBuilder : public builder::SpvFnBuilder { From 64454fc4cb19d30a2ced4e5ac5983ea256d28a95 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Fri, 18 Oct 2024 14:42:32 +0200 Subject: [PATCH 288/342] spirv: share logic between call & intrinsic paths --- src/thorin/be/spirv/spirv.cpp | 32 ++++++++++---------------------- 1 file changed, 10 insertions(+), 22 deletions(-) diff --git a/src/thorin/be/spirv/spirv.cpp b/src/thorin/be/spirv/spirv.cpp index 25f2e7b3d..098ed9204 100644 --- a/src/thorin/be/spirv/spirv.cpp +++ b/src/thorin/be/spirv/spirv.cpp @@ -262,6 +262,7 @@ void CodeGen::emit_epilogue(Continuation* continuation) { assert(succ->is_basicblock()); bb->branch(emit(succ)); for (size_t i = 0, j = 0; i != succ->num_params(); ++i) { + assert(j < args.size()); auto param = succ->param(i); if (is_mem(param) || is_unit(param)) continue; @@ -370,45 +371,32 @@ void CodeGen::emit_epilogue(Continuation* continuation) { // must be call + continuation --- call + return has been removed by codegen_prepare auto succ = ret_arg->isa_nom(); - size_t n = 0; + size_t real_params_count = 0; const Param* last_param = nullptr; for (auto param : succ->params()) { if (is_mem(param) || is_unit(param)) continue; last_param = param; - n++; + real_params_count++; } - if (n == 0) { - bb->branch(emit(succ)); - } else if (n == 1) { - bb->branch(emit(succ)); + std::vector args(real_params_count); - auto& phi = cont2bb_[succ]->phis_map[last_param]; - phi.preds.emplace_back(call_result, emit_as_bb(continuation)); - } else { - Array extracts(n); + if (real_params_count == 1) { + args[0] = call_result; + } else if (real_params_count > 1) { for (size_t i = 0, j = 0; i != succ->num_params(); ++i) { auto param = succ->param(i); if (is_mem(param) || is_unit(param)) continue; - extracts[j] = bb->extract(convert(param->type()).id, call_result, { (uint32_t) j }); + args[j] = bb->extract(convert(param->type()).id, call_result, { (uint32_t) j }); j++; } bb->branch(emit(succ)); - - for (size_t i = 0, j = 0; i != succ->num_params(); ++i) { - auto param = succ->param(i); - if (is_mem(param) || is_unit(param)) - continue; - - auto& phi = cont2bb_[succ]->phis_map[last_param]; - phi.preds.emplace_back(extracts[j], emit_as_bb(continuation)); - - j++; - } } + + jump_to_next_cont_with_args(succ, args); } } From 2eba44d2c197f3e4d2de7bb28d41c370fc2bbf88 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Fri, 18 Oct 2024 15:40:54 +0200 Subject: [PATCH 289/342] fix: Branch and Match don't have ret_param s --- src/thorin/continuation.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/thorin/continuation.cpp b/src/thorin/continuation.cpp index d99dd0ae9..61d53d2cf 100644 --- a/src/thorin/continuation.cpp +++ b/src/thorin/continuation.cpp @@ -139,6 +139,12 @@ const Param* Continuation::mem_param() const { } const Param* Continuation::ret_param() const { + switch (intrinsic()) { + case Intrinsic::Branch: + case Intrinsic::Match: + return nullptr; + default: break; + } const Param* result = nullptr; for (auto param : params()) { if (param->order() >= 1) { From 8191629094927f1fac18face91be82fe0d11378d Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Fri, 18 Oct 2024 15:41:31 +0200 Subject: [PATCH 290/342] added enum for ground truth of App operands --- src/thorin/continuation.h | 13 +++++++++---- src/thorin/primop.cpp | 2 +- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/thorin/continuation.h b/src/thorin/continuation.h index fb747c5b7..11c87ee25 100644 --- a/src/thorin/continuation.h +++ b/src/thorin/continuation.h @@ -60,10 +60,15 @@ class App : public Def { App(World&, const Defs ops, Debug dbg); public: - const Def* callee() const { return op(0); } - const Def* arg(size_t i) const { return op(1 + i); } - size_t num_args() const { return num_ops() - 1; } - const Defs args() const { return ops().skip_front(); } + enum Ops { + Callee = 0, + FirstArg = 1, + }; + + const Def* callee() const { return op(Ops::Callee); } + const Def* arg(size_t i) const { return op(Ops::FirstArg + i); } + size_t num_args() const { return num_ops() - Ops::FirstArg; } + const Defs args() const { return ops().skip_front(Ops::FirstArg); } const Def* rebuild(World&, const Type*, Defs) const override; Continuations using_continuations() const { diff --git a/src/thorin/primop.cpp b/src/thorin/primop.cpp index 786a834d0..8b6fa09d7 100644 --- a/src/thorin/primop.cpp +++ b/src/thorin/primop.cpp @@ -188,7 +188,7 @@ bool Slot::equal(const Def* other) const { return this == other; } * rebuild */ -const Def* App ::rebuild(World& w, const Type* , Defs o) const { return w.app(o[0], o.skip_front(), debug()); } +const Def* App ::rebuild(World& w, const Type* , Defs o) const { return w.app(o[App::Ops::Callee], o.skip_front(App::Ops::FirstArg), debug()); } const Def* ArithOp ::rebuild(World& w, const Type* , Defs o) const { return w.arithop(arithop_tag(), o[0], o[1], debug()); } const Def* Bitcast ::rebuild(World& w, const Type* t, Defs o) const { return w.bitcast(t, o[0], debug()); } const Def* Bottom ::rebuild(World& w, const Type* t, Defs ) const { return w.bottom(t, debug()); } From 12e48c53ae3bac2b4e7717f15e8bd17fc1bbe32e Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Fri, 18 Oct 2024 15:42:20 +0200 Subject: [PATCH 291/342] spirv: added a hack to avoid phi nodes with return blocks --- src/thorin/be/spirv/spirv.cpp | 48 ++++++++++++++++++++++++++--- src/thorin/be/spirv/spirv_private.h | 2 ++ 2 files changed, 45 insertions(+), 5 deletions(-) diff --git a/src/thorin/be/spirv/spirv.cpp b/src/thorin/be/spirv/spirv.cpp index 098ed9204..55e4e8234 100644 --- a/src/thorin/be/spirv/spirv.cpp +++ b/src/thorin/be/spirv/spirv.cpp @@ -180,6 +180,28 @@ FnBuilder* CodeGen::prepare(const thorin::Scope& scope) { return &fn; } +static bool is_return_block(thorin::Continuation* cont) { + if (!cont->is_basicblock()) + return false; + int uses_as_ret_param = 0; + for (auto use : cont->copy_uses()) { + if (use.def()->isa()) + continue; // the block can have params + else if (auto app = use.def()->isa()) { + if (auto callee = app->callee()->isa_nom()) { + auto arg_index = use.index() - App::FirstArg; + auto ret_param = callee->ret_param(); + if (ret_param && arg_index == ret_param->index()) { + uses_as_ret_param++; + continue; + } + } + } + return false; // any other use disqualifies the block + } + return uses_as_ret_param == 1; +} + void CodeGen::prepare(thorin::Continuation* cont, FnBuilder* fn) { auto& bb = *fn->bbs.emplace_back(std::make_unique(*fn)); cont2bb_[cont] = &bb; @@ -187,6 +209,10 @@ void CodeGen::prepare(thorin::Continuation* cont, FnBuilder* fn) { builder_->name(bb.label, cont->name().c_str()); + bb.semi_inline = is_return_block(cont); + if (bb.semi_inline) + world().ddef(cont, "Emitting {} as return block", cont); + if (entry_ == cont) { for (auto param : cont->params()) { if (is_mem(param) || is_unit(param)) { @@ -204,6 +230,8 @@ void CodeGen::prepare(thorin::Continuation* cont, FnBuilder* fn) { } } else { defs_[cont] = bb.label; + if (bb.semi_inline) + return; for (auto param : cont->params()) { if (is_mem(param) || is_unit(param)) { // Nothing @@ -256,20 +284,30 @@ SpvId CodeGen::emit_as_bb(thorin::Continuation* cont) { } void CodeGen::emit_epilogue(Continuation* continuation) { - auto& bb = cont2bb_[continuation]; + BasicBlockBuilder* bb = cont2bb_[continuation]; + // Handles the potential nuances of jumping to another continuation auto jump_to_next_cont_with_args = [&](Continuation* succ, std::vector args) { assert(succ->is_basicblock()); - bb->branch(emit(succ)); + BasicBlockBuilder* dstbb = cont2bb_[succ]; + for (size_t i = 0, j = 0; i != succ->num_params(); ++i) { assert(j < args.size()); auto param = succ->param(i); - if (is_mem(param) || is_unit(param)) + if (is_mem(param) || is_unit(param)) { + if (dstbb->semi_inline) + defs_[param] = 0; continue; - auto& phi = cont2bb_[succ]->phis_map[param]; - phi.preds.emplace_back(args[j], emit_as_bb(continuation)); + } + if (dstbb->semi_inline) { + defs_[param] = args[j]; + } else { + auto& phi = cont2bb_[succ]->phis_map[param]; + phi.preds.emplace_back(args[j], emit_as_bb(continuation)); + } j++; } + bb->branch(emit(succ)); }; auto& app = *continuation->body(); diff --git a/src/thorin/be/spirv/spirv_private.h b/src/thorin/be/spirv/spirv_private.h index 3d6701673..9f1546682 100644 --- a/src/thorin/be/spirv/spirv_private.h +++ b/src/thorin/be/spirv/spirv_private.h @@ -15,6 +15,8 @@ struct BasicBlockBuilder : public builder::SpvBasicBlockBuilder { FnBuilder& fn_builder; FileBuilder& file_builder; std::unordered_map phis_map; + + bool semi_inline; }; struct FnBuilder : public builder::SpvFnBuilder { From 63da5f84a21ab9531566a4778acd6431060a69c6 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Wed, 30 Oct 2024 17:22:15 +0100 Subject: [PATCH 292/342] fix levelzero intrinsic not being registered in codegen.cpp --- src/thorin/be/codegen.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/thorin/be/codegen.cpp b/src/thorin/be/codegen.cpp index 4d9ec7c83..6b50fa273 100644 --- a/src/thorin/be/codegen.cpp +++ b/src/thorin/be/codegen.cpp @@ -270,6 +270,7 @@ DeviceBackends::DeviceBackends(thorin::World& world, int opt, bool debug, std::s #endif #if THORIN_ENABLE_SPIRV register_backend(std::make_unique(*this, world)); + register_backend(std::make_unique(*this, world)); #endif register_backend(std::make_unique(*this, world, hls_flags)); From 5074b03f0aedc091e5fbb0d958cbc5c8070094f9 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Wed, 30 Oct 2024 17:22:34 +0100 Subject: [PATCH 293/342] spirv: fix dangling reference to target_info_ --- src/thorin/be/spirv/spirv.cpp | 1 + src/thorin/be/spirv/spirv.h | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/thorin/be/spirv/spirv.cpp b/src/thorin/be/spirv/spirv.cpp index 55e4e8234..78d299e0b 100644 --- a/src/thorin/be/spirv/spirv.cpp +++ b/src/thorin/be/spirv/spirv.cpp @@ -115,6 +115,7 @@ void CodeGen::emit_stream(std::ostream& out) { builder_->addressing_model = spv::AddressingModelPhysicalStorageBuffer64; builder_->memory_model = spv::MemoryModel::MemoryModelGLSL450; break; + default: assert(false && "unknown spirv dialect"); } ScopesForest forest(world()); diff --git a/src/thorin/be/spirv/spirv.h b/src/thorin/be/spirv/spirv.h index 22886eab8..44dfa9685 100644 --- a/src/thorin/be/spirv/spirv.h +++ b/src/thorin/be/spirv/spirv.h @@ -71,7 +71,7 @@ class CodeGen : public thorin::CodeGen, public thorin::Emitter Date: Wed, 30 Oct 2024 17:22:58 +0100 Subject: [PATCH 294/342] spirv: fix typo in error msg --- src/thorin/be/spirv/spirv.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/thorin/be/spirv/spirv.cpp b/src/thorin/be/spirv/spirv.cpp index 78d299e0b..14c0e42c4 100644 --- a/src/thorin/be/spirv/spirv.cpp +++ b/src/thorin/be/spirv/spirv.cpp @@ -873,7 +873,7 @@ std::vector CodeGen::emit_intrinsic(const App& app, const Continuation* i } else world().ELOG("spirv.builtin requires an integer literal as the argument"); } else { - world().ELOG("This spir-v builtin isn't recognised: %s", intrinsic->name()); + world().ELOG("This spir-v builtin isn't recognised: {}", intrinsic->name()); } return productions; } From 702cf3c34872af12e5453466cb1f65ea9bc8082a Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Wed, 30 Oct 2024 17:23:06 +0100 Subject: [PATCH 295/342] spirv: use GLCompute or Kernel appropriately --- src/thorin/be/spirv/spirv.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/thorin/be/spirv/spirv.cpp b/src/thorin/be/spirv/spirv.cpp index 14c0e42c4..3fa8a0111 100644 --- a/src/thorin/be/spirv/spirv.cpp +++ b/src/thorin/be/spirv/spirv.cpp @@ -144,7 +144,7 @@ void CodeGen::emit_stream(std::ostream& out) { (uint32_t) std::get<2>(block), }; - builder_->declare_entry_point(spv::ExecutionModelGLCompute, callee, cont->name().c_str(), interface); + builder_->declare_entry_point(target_info_.dialect == Target::Vulkan ? spv::ExecutionModelGLCompute : spv::ExecutionModelKernel, callee, cont->name().c_str(), interface); builder_->execution_mode(callee, spv::ExecutionModeLocalSize, local_size); entry_points_count++; } From 5e5fc8ab9ff7e992ec824ee8b10f920d41b76f14 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Wed, 30 Oct 2024 17:48:53 +0100 Subject: [PATCH 296/342] spv: fix typo in Cast --- src/thorin/be/spirv/spirv.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/thorin/be/spirv/spirv.cpp b/src/thorin/be/spirv/spirv.cpp index 3fa8a0111..144e8a450 100644 --- a/src/thorin/be/spirv/spirv.cpp +++ b/src/thorin/be/spirv/spirv.cpp @@ -766,7 +766,7 @@ SpvId CodeGen::emit_bb(BasicBlockBuilder* bb, const Def* def) { auto src_kind = classify_primtype(src_prim); auto dst_kind = classify_primtype(dst_prim); size_t src_bitwidth = conv_src_type.layout->size * 8; - size_t dst_bitwidth = conv_src_type.layout->size * 8; + size_t dst_bitwidth = conv_dst_type.layout->size * 8; SpvId data = emit(cast->from()); @@ -801,6 +801,7 @@ SpvId CodeGen::emit_bb(BasicBlockBuilder* bb, const Def* def) { case PrimTypeKind::Float: data = bb->convert(spv::OpFConvert, target_type, data); break; + default: assert(false); } } @@ -820,6 +821,7 @@ SpvId CodeGen::emit_bb(BasicBlockBuilder* bb, const Def* def) { case PrimTypeKind::Float: data = bb->convert(spv::OpFConvert, target_type, data); break; + default: assert(false); } } } From 8e8c309a4139cc62c55fee603d0e9d2edb251565 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Wed, 30 Oct 2024 17:58:48 +0100 Subject: [PATCH 297/342] fix interface of generated spv kernel to include builtins --- src/thorin/be/spirv/spirv.cpp | 6 +++--- src/thorin/be/spirv/spirv_private.h | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/thorin/be/spirv/spirv.cpp b/src/thorin/be/spirv/spirv.cpp index 144e8a450..ad7b79d6c 100644 --- a/src/thorin/be/spirv/spirv.cpp +++ b/src/thorin/be/spirv/spirv.cpp @@ -121,10 +121,9 @@ void CodeGen::emit_stream(std::ostream& out) { ScopesForest forest(world()); forest.for_each([&](const Scope& scope) { emit_scope(scope, forest); }); - std::vector interface; for (auto def : world().defs()) { if (auto global = def->isa()) - interface.push_back(emit(global)); + builder.interface.push_back(emit(global)); } int entry_points_count = 0; @@ -144,7 +143,7 @@ void CodeGen::emit_stream(std::ostream& out) { (uint32_t) std::get<2>(block), }; - builder_->declare_entry_point(target_info_.dialect == Target::Vulkan ? spv::ExecutionModelGLCompute : spv::ExecutionModelKernel, callee, cont->name().c_str(), interface); + builder_->declare_entry_point(target_info_.dialect == Target::Vulkan ? spv::ExecutionModelGLCompute : spv::ExecutionModelKernel, callee, cont->name().c_str(), builder.interface); builder_->execution_mode(callee, spv::ExecutionModeLocalSize, local_size); entry_points_count++; } @@ -868,6 +867,7 @@ std::vector CodeGen::emit_intrinsic(const App& app, const Continuation* i auto ret_type = (*intrinsic->params().back()).type()->as(); auto desired_type = ret_type->types()[1]->as(); auto id = builder_->variable(convert(desired_type).id, static_cast(convert(desired_type->addr_space()))); + builder_->interface.push_back(id); builder_->decorate(id, spv::Decoration::DecorationBuiltIn, { spv_builtin }); builder_->builtins_[spv_builtin] = id; productions.push_back(id); diff --git a/src/thorin/be/spirv/spirv_private.h b/src/thorin/be/spirv/spirv_private.h index 9f1546682..d36d4e81a 100644 --- a/src/thorin/be/spirv/spirv_private.h +++ b/src/thorin/be/spirv/spirv_private.h @@ -38,6 +38,7 @@ struct FileBuilder : public builder::SpvFileBuilder { FnBuilder* current_fn_ = nullptr; ContinuationMap> fn_builders_; std::unordered_map builtins_; + std::vector interface; SpvId u32_t(); SpvId u32_constant(uint32_t); From cbd75e59caed9ede0893b069f11a471a01e01576 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Wed, 30 Oct 2024 18:04:01 +0100 Subject: [PATCH 298/342] spv: emit spv 1.2 for OpenCL --- src/thorin/be/spirv/spirv.cpp | 1 + src/thorin/be/spirv/spirv_builder.hpp | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/thorin/be/spirv/spirv.cpp b/src/thorin/be/spirv/spirv.cpp index ad7b79d6c..d59a489e2 100644 --- a/src/thorin/be/spirv/spirv.cpp +++ b/src/thorin/be/spirv/spirv.cpp @@ -109,6 +109,7 @@ void CodeGen::emit_stream(std::ostream& out) { builder_->capability(spv::Capability::CapabilityAddresses); builder_->addressing_model = target_info_.mem_layout.pointer_size == 4 ? spv::AddressingModelPhysical32 : spv::AddressingModelPhysical64; builder_->memory_model = spv::MemoryModel::MemoryModelOpenCL; + builder_->version = 0x10200; break; case Target::Vulkan: builder_->capability(spv::Capability::CapabilityShader); diff --git a/src/thorin/be/spirv/spirv_builder.hpp b/src/thorin/be/spirv/spirv_builder.hpp index b94f11eb1..876dab6e0 100644 --- a/src/thorin/be/spirv/spirv_builder.hpp +++ b/src/thorin/be/spirv/spirv_builder.hpp @@ -609,6 +609,8 @@ struct SpvFileBuilder { extensions_set.insert(name); } + uint32_t version = spv::Version; + spv::AddressingModel addressing_model = spv::AddressingModel::AddressingModelLogical; spv::MemoryModel memory_model = spv::MemoryModel::MemoryModelSimple; @@ -670,7 +672,7 @@ struct SpvFileBuilder { memory_model_section.data_.push_back(memory_model); output_word_le(spv::MagicNumber); - output_word_le(spv::Version); // TODO: target a specific spirv version + output_word_le(version); // TODO: target a specific spirv version output_word_le(uint32_t(0)); // TODO get a magic number ? output_word_le(bound); output_word_le(uint32_t(0)); // instruction schema padding From 336e2628feaf3abb5f77641401474e39589b80d6 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Mon, 4 Nov 2024 15:33:20 +0100 Subject: [PATCH 299/342] codegen: add seperate notion of 'Offload' intrinsics --- src/thorin/be/codegen.cpp | 2 +- src/thorin/continuation.cpp | 3 +++ src/thorin/continuation.h | 5 ++++- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/thorin/be/codegen.cpp b/src/thorin/be/codegen.cpp index 6b50fa273..554a4c023 100644 --- a/src/thorin/be/codegen.cpp +++ b/src/thorin/be/codegen.cpp @@ -297,7 +297,7 @@ void DeviceBackends::search_for_device_code() { Intrinsic intrinsic = Intrinsic::None; visit_capturing_intrinsics(continuation, [&] (Continuation* continuation) { - if (continuation->is_accelerator()) { + if (continuation->is_offload_intrinsic()) { intrinsic = continuation->intrinsic(); return true; } diff --git a/src/thorin/continuation.cpp b/src/thorin/continuation.cpp index 61d53d2cf..1632708b1 100644 --- a/src/thorin/continuation.cpp +++ b/src/thorin/continuation.cpp @@ -250,6 +250,9 @@ const Filter* Continuation::all_true_filter() const { } bool Continuation::is_accelerator() const { return Intrinsic::AcceleratorBegin <= intrinsic() && intrinsic() < Intrinsic::AcceleratorEnd; } + +bool Continuation::is_offload_intrinsic() const { return Intrinsic::OffloadBegin <= intrinsic() && intrinsic() < Intrinsic::OffloadEnd; } + void Continuation::set_intrinsic() { if (name() == "cuda") attributes().intrinsic = Intrinsic::CUDA; else if (name() == "nvvm") attributes().intrinsic = Intrinsic::NVVM; diff --git a/src/thorin/continuation.h b/src/thorin/continuation.h index 11c87ee25..bdedec64f 100644 --- a/src/thorin/continuation.h +++ b/src/thorin/continuation.h @@ -97,7 +97,8 @@ enum class CC : uint8_t { enum class Intrinsic : uint8_t { None, AcceleratorBegin, - CUDA = AcceleratorBegin, ///< Internal CUDA-Backend. + OffloadBegin = AcceleratorBegin, + CUDA = OffloadBegin, ///< Internal CUDA-Backend. NVVM, ///< Internal NNVM-Backend. OpenCL, ///< Internal OpenCL-Backend. OpenCL_SPIRV, ///< Internal OpenCL-Backend. @@ -106,6 +107,7 @@ enum class Intrinsic : uint8_t { AMDGPUPAL, ///< Internal AMDGPU-PAL-Backend. ShadyCompute, ///< Internal Shady Compute Backend. HLS, ///< Internal HLS-Backend. + OffloadEnd = HLS, Parallel, ///< Internal Parallel-CPU-Backend. Fibers, ///< Internal Parallel-CPU-Backend using resumable fibers. Spawn, ///< Internal Parallel-CPU-Backend. @@ -186,6 +188,7 @@ class Continuation : public Def { bool is_channel() const { return name().find("channel") != std::string::npos; } bool is_pipe() const { return name().find("pipe") != std::string::npos; } bool is_accelerator() const; + bool is_offload_intrinsic() const; const App* body() const { return op(0)->as(); } bool has_body() const { return !op(0)->isa(); } From 30df11e0a1455467820ad6f44d878b1c6a997a90 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Mon, 4 Nov 2024 16:47:32 +0100 Subject: [PATCH 300/342] fix fp64 constant emission --- src/thorin/be/spirv/spirv.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/thorin/be/spirv/spirv.cpp b/src/thorin/be/spirv/spirv.cpp index d59a489e2..c0be0e581 100644 --- a/src/thorin/be/spirv/spirv.cpp +++ b/src/thorin/be/spirv/spirv.cpp @@ -439,6 +439,8 @@ void CodeGen::emit_epilogue(Continuation* continuation) { } } +static_assert(sizeof(double) == sizeof(uint64_t), "This code assumes 64-bit double"); + SpvId CodeGen::emit_constant(const thorin::Def* def) { if (auto primlit = def->isa()) { Box box = primlit->value(); @@ -454,6 +456,7 @@ SpvId CodeGen::emit_constant(const thorin::Def* def) { case PrimType_pf32: case PrimType_qf32: case PrimType_ps32: case PrimType_qs32: case PrimType_pu32: case PrimType_qu32: constant = builder_->constant(type, { static_cast(box.get_u32()) }); break; + case PrimType_pf64: case PrimType_qf64: case PrimType_ps64: case PrimType_qs64: case PrimType_pu64: case PrimType_qu64: { uint64_t value = static_cast(box.get_u64()); @@ -462,7 +465,6 @@ SpvId CodeGen::emit_constant(const thorin::Def* def) { constant = builder_->constant(type, { (uint32_t) lower, (uint32_t) upper }); break; } - case PrimType_pf64: case PrimType_qf64: assertf(false, "not implemented yet"); } return constant; } From 2b1b4fc092259bce54fb676a83f7db78f9164a2a Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Mon, 4 Nov 2024 19:27:09 +0100 Subject: [PATCH 301/342] spirv_builder.hpp: move FileBuilder first --- src/thorin/be/spirv/spirv_builder.hpp | 935 +++++++++++++------------- 1 file changed, 470 insertions(+), 465 deletions(-) diff --git a/src/thorin/be/spirv/spirv_builder.hpp b/src/thorin/be/spirv/spirv_builder.hpp index 876dab6e0..64efb134d 100644 --- a/src/thorin/be/spirv/spirv_builder.hpp +++ b/src/thorin/be/spirv/spirv_builder.hpp @@ -69,631 +69,636 @@ struct SpvSectionBuilder { } }; -struct SpvBasicBlockBuilder : public SpvSectionBuilder { - explicit SpvBasicBlockBuilder(SpvFileBuilder& file_builder) - : file_builder(file_builder) - {} - SpvFileBuilder& file_builder; - struct Phi { - SpvId type; - SpvId value; - std::vector> preds; +struct SpvFileBuilder { + + enum UniqueDeclTag { + NONE, + FN_TYPE, + PTR_TYPE, + DEF_ARR_TYPE, + CONSTANT, + CONSTANT_COMPOSITE, }; - std::vector phis; - SpvId label; - SpvId undef(SpvId type) { - op(spv::Op::OpUndef, 3); - ref_id(type); + /// Prevents duplicate declarations + struct UniqueDeclKey { + UniqueDeclTag tag; + std::vector members; + + bool operator==(const UniqueDeclKey &b) const { + return tag == b.tag && members == b.members; + } + }; + + struct UniqueDeclKeyHasher { + size_t operator() (const UniqueDeclKey& key) const { + size_t acc = 0; + for (auto id : key.members) + acc ^= std::hash{}(id); + return std::hash{}(key.tag) ^ acc; + } + }; + + SpvFileBuilder() {} + SpvFileBuilder(const SpvFileBuilder&) = delete; + + SpvId generate_fresh_id() { return { bound++ }; } + + void name(SpvId id, std::string_view str) { + assert(id < bound); + debug_names.op(spv::Op::OpName, 2 + div_roundup(str.size() + 1, 4)); + debug_names.ref_id(id); + debug_names.literal_name(str); + } + + SpvId declare_bool_type() { + types_constants.op(spv::Op::OpTypeBool, 2); auto id = generate_fresh_id(); - ref_id(id); + types_constants.ref_id(id); return id; } - SpvId composite(SpvId aggregate_t, std::vector& elements) { - op(spv::Op::OpCompositeConstruct, 3 + elements.size()); - ref_id(aggregate_t); + SpvId declare_int_type(int width, bool signed_) { + types_constants.op(spv::Op::OpTypeInt, 4); auto id = generate_fresh_id(); - ref_id(id); - for (auto e : elements) - ref_id(e); + types_constants.ref_id(id); + types_constants.literal_int(width); + types_constants.literal_int(signed_ ? 1 : 0); return id; } - SpvId extract(SpvId target_type, SpvId composite, std::vector indices) { - op(spv::Op::OpCompositeExtract, 4 + indices.size()); - ref_id(target_type); + SpvId declare_float_type(int width) { + types_constants.op(spv::Op::OpTypeFloat, 3); auto id = generate_fresh_id(); - ref_id(id); - ref_id(composite); - for (auto i : indices) - literal_int(i); + types_constants.ref_id(id); + types_constants.literal_int(width); return id; } - SpvId insert(SpvId target_type, SpvId object, SpvId composite, std::vector indices) { - op(spv::Op::OpCompositeInsert, 5 + indices.size()); - ref_id(target_type); + SpvId declare_ptr_type(spv::StorageClass storage_class, SpvId element_type) { + auto key = UniqueDeclKey { PTR_TYPE, { element_type, (uint32_t) storage_class } }; + if (auto iter = unique_decls.find(key); iter != unique_decls.end()) return iter->second; + types_constants.op(spv::Op::OpTypePointer, 4); auto id = generate_fresh_id(); - ref_id(id); - ref_id(object); - ref_id(composite); - for (auto i : indices) - literal_int(i); + types_constants.ref_id(id); + types_constants.literal_int(storage_class); + types_constants.ref_id(element_type); + unique_decls[key] = id; return id; } - SpvId vector_extract_dynamic(SpvId target_type, SpvId vector, SpvId index) { - op(spv::Op::OpVectorExtractDynamic, 5); - ref_id(target_type); + SpvId declare_array_type(SpvId element_type, SpvId dim) { + auto key = UniqueDeclKey { DEF_ARR_TYPE, { element_type, dim } }; + if (auto iter = unique_decls.find(key); iter != unique_decls.end()) return iter->second; + types_constants.op(spv::Op::OpTypeArray, 4); auto id = generate_fresh_id(); - ref_id(id); - ref_id(vector); - ref_id(index); + types_constants.ref_id(id); + types_constants.ref_id(element_type); + types_constants.ref_id(dim); + unique_decls[key] = id; return id; } - SpvId vector_insert_dynamic(SpvId target_type, SpvId vector, SpvId component, SpvId index) { - op(spv::Op::OpVectorInsertDynamic, 6); - ref_id(target_type); + SpvId declare_fn_type(std::vector dom, SpvId codom) { + auto key = UniqueDeclKey { FN_TYPE, {} }; + for (auto d : dom) key.members.push_back(d); + key.members.push_back(codom); + if (auto iter = unique_decls.find(key); iter != unique_decls.end()) return iter->second; + + types_constants.op(spv::Op::OpTypeFunction, 3 + dom.size()); auto id = generate_fresh_id(); - ref_id(id); - ref_id(vector); - ref_id(component); - ref_id(index); + types_constants.ref_id(id); + types_constants.ref_id(codom); + for (auto arg : dom) + types_constants.ref_id(arg); + unique_decls[key] = id; return id; } - // Used for almost all conversion operations - SpvId convert(spv::Op op_, SpvId target_type, SpvId value) { - op(op_, 4); + SpvId declare_struct_type(std::vector elements) { + types_constants.op(spv::Op::OpTypeStruct, 2 + elements.size()); auto id = generate_fresh_id(); - ref_id(target_type); - ref_id(id); - ref_id(value); + types_constants.ref_id(id); + for (auto arg : elements) + types_constants.ref_id(arg); return id; } - SpvId access_chain(SpvId target_type, SpvId element, std::vector indexes) { - op(spv::Op::OpAccessChain, 4 + indexes.size()); + SpvId declare_vector_type(SpvId component_type, uint32_t dim) { + types_constants.op(spv::Op::OpTypeVector, 4); auto id = generate_fresh_id(); - ref_id(target_type); - ref_id(id); - ref_id(element); - for (auto index : indexes) - ref_id(index); + types_constants.ref_id(id); + types_constants.ref_id(component_type); + types_constants.literal_int(dim); return id; } - SpvId ptr_access_chain(SpvId target_type, SpvId base, SpvId element, std::vector indexes) { - op(spv::Op::OpPtrAccessChain, 5 + indexes.size()); + void decorate(SpvId target, spv::Decoration decoration, std::vector extra = {}) { + annotations.op(spv::Op::OpDecorate, 3 + extra.size()); + annotations.ref_id(target); + annotations.literal_int(decoration); + for (auto e : extra) + annotations.literal_int(e); + } + + void decorate_member(SpvId target, uint32_t member, spv::Decoration decoration, std::vector extra = {}) { + annotations.op(spv::Op::OpMemberDecorate, 4 + extra.size()); + annotations.ref_id(target); + annotations.literal_int(member); + annotations.literal_int(decoration); + for (auto e : extra) + annotations.literal_int(e); + } + + SpvId debug_string(std::string string) { + debug_string_source.op(spv::Op::OpString, 2 + div_roundup(string.size() + 1, 4)); auto id = generate_fresh_id(); - ref_id(target_type); - ref_id(id); - ref_id(base); - ref_id(element); - for (auto index : indexes) - ref_id(index); + debug_string_source.ref_id(id); + debug_string_source.literal_name(string); return id; } - SpvId load(SpvId target_type, SpvId pointer, std::vector operands = {}) { - op(spv::Op::OpLoad, 4 + operands.size()); + SpvId bool_constant(SpvId type, bool value) { + types_constants.op(value ? spv::Op::OpConstantTrue : spv::Op::OpConstantFalse, 3); auto id = generate_fresh_id(); - ref_id(target_type); - ref_id(id); - ref_id(pointer); - for (auto op : operands) - literal_int(op); + types_constants.ref_id(type); + types_constants.ref_id(id); return id; } - void store(SpvId value, SpvId pointer, std::vector operands = {}) { - op(spv::Op::OpStore, 3 + operands.size()); - ref_id(pointer); - ref_id(value); - for (auto op : operands) - literal_int(op); + SpvId constant(SpvId type, std::vector bit_pattern) { + auto key = UniqueDeclKey { CONSTANT, bit_pattern }; + key.members.push_back(type); + if (auto iter = unique_decls.find(key); iter != unique_decls.end()) return iter->second; + types_constants.op(spv::Op::OpConstant, 3 + bit_pattern.size()); + auto id = generate_fresh_id(); + types_constants.ref_id(type); + types_constants.ref_id(id); + for (auto arg : bit_pattern) + types_constants.literal_int(arg); + unique_decls[key] = id; + return id; } - SpvId binop(spv::Op op_, SpvId result_type, SpvId lhs, SpvId rhs) { - op(op_, 5); + SpvId constant_composite(SpvId type, std::vector ops) { + auto key = UniqueDeclKey { CONSTANT_COMPOSITE, {} }; + key.members.push_back(type); + for (auto op : ops) key.members.push_back(op); + if (auto iter = unique_decls.find(key); iter != unique_decls.end()) return iter->second; + types_constants.op(spv::Op::OpConstantComposite, 3 + ops.size()); auto id = generate_fresh_id(); - ref_id(result_type); - ref_id(id); - ref_id(lhs); - ref_id(rhs); + types_constants.ref_id(type); + types_constants.ref_id(id); + for (auto op : ops) + types_constants.ref_id(op); + unique_decls[key] = id; return id; } - void branch(SpvId target) { - op(spv::Op::OpBranch, 2); - ref_id(target); + SpvId variable(SpvId type, spv::StorageClass storage_class) { + types_constants.op(spv::Op::OpVariable, 4); + types_constants.ref_id(type); + auto id = generate_fresh_id(); + types_constants.ref_id(id); + types_constants.literal_int(storage_class); + return id; } - void branch_conditional(SpvId condition, SpvId true_target, SpvId false_target) { - op(spv::Op::OpBranchConditional, 4); - ref_id(condition); - ref_id(true_target); - ref_id(false_target); + SpvId declare_void_type() { + types_constants.op(spv::Op::OpTypeVoid, 2); + auto id = generate_fresh_id(); + types_constants.ref_id(id); + return id; } - void branch_switch(SpvId selector, SpvId default_case, std::vector literals, std::vector cases) { - assert(literals.size() == cases.size()); - op(spv::Op::OpSwitch, 3 + literals.size() * 2); - ref_id(selector); - ref_id(default_case); - for (size_t i = 0; i < literals.size(); i++) { - ref_id(literals[i]); - ref_id(cases[i]); - } - } - - void selection_merge(SpvId merge_bb, spv::SelectionControlMask selection_control) { - op(spv::Op::OpSelectionMerge, 3); - ref_id(merge_bb); - literal_int(selection_control); - } + SpvId define_function(SpvFnBuilder& fn_builder); - void loop_merge(SpvId merge_bb, SpvId continue_bb, spv::LoopControlMask loop_control, std::vector loop_control_ops) { - op(spv::Op::OpLoopMerge, 4 + loop_control_ops.size()); - ref_id(merge_bb); - ref_id(continue_bb); - literal_int(loop_control); - - for (auto e : loop_control_ops) - literal_int(e); + void declare_entry_point(spv::ExecutionModel execution_model, SpvId entry_point, std::string name, std::vector interface) { + entry_points.op(spv::Op::OpEntryPoint, 3 + div_roundup(name.size() + 1, 4) + interface.size()); + entry_points.literal_int(execution_model); + entry_points.ref_id(entry_point); + entry_points.literal_name(name); + for (auto i : interface) + entry_points.ref_id(i); } - SpvId call(SpvId return_type, SpvId callee, std::vector arguments) { - op(spv::Op::OpFunctionCall, 4 + arguments.size()); - auto id = generate_fresh_id(); - ref_id(return_type); - ref_id(id); - ref_id(callee); - - for (auto a : arguments) - ref_id(a); - return id; + void execution_mode(SpvId entry_point, spv::ExecutionMode execution_mode, std::vector payloads) { + entry_points.op(spv::Op::OpExecutionMode, 3 + payloads.size()); + entry_points.ref_id(entry_point); + entry_points.literal_int(execution_mode); + for (auto d : payloads) + entry_points.literal_int(d); } - SpvId ext_instruction(SpvId return_type, ExtendedInstruction instr, std::vector arguments); - - void return_void() { - op(spv::Op::OpReturn, 1); + void capability(spv::Capability cap) { + auto found = capabilities_set.find(cap); + if (found != capabilities_set.end()) + return; + capabilities.op(spv::Op::OpCapability, 2); + capabilities.data_.push_back(cap); + capabilities_set.insert(cap); } - void return_value(SpvId value) { - op(spv::Op::OpReturnValue, 2); - ref_id(value); + void extension(std::string name) { + auto found = extensions_set.find(name); + if (found != extensions_set.end()) + return; + extensions.op(spv::Op::OpExtension, 1 + div_roundup(name.size() + 1, 4)); + extensions.literal_name(name); + extensions_set.insert(name); } - void unreachable() { - op(spv::Op::OpUnreachable, 1); - } + uint32_t version = spv::Version; -private: - SpvId generate_fresh_id(); + spv::AddressingModel addressing_model = spv::AddressingModel::AddressingModelLogical; + spv::MemoryModel memory_model = spv::MemoryModel::MemoryModelSimple; protected: - SpvId ext_instruction(SpvId return_type, SpvId set, uint32_t instruction, std::vector arguments) { - op(spv::Op::OpExtInst, 5 + arguments.size()); + SpvId extended_import(std::string name) { + auto found = extended_instruction_sets.find(name); + if (found != extended_instruction_sets.end()) + return found->second; + ext_inst_import.op(spv::Op::OpExtInstImport, 2 + div_roundup(name.size() + 1, 4)); auto id = generate_fresh_id(); - ref_id(return_type); - ref_id(id); - ref_id(set); - literal_int(instruction); - for (auto a : arguments) - ref_id(a); + ext_inst_import.ref_id(id); + ext_inst_import.literal_name(name); + extended_instruction_sets[name] = id; return id; } -}; - -struct SpvFnBuilder { - explicit SpvFnBuilder(SpvFileBuilder* file_builder) - : file_builder(file_builder) - { - function_id = generate_fresh_id(); - } - - SpvFileBuilder* file_builder; - SpvId function_id; - SpvId fn_type; - SpvId fn_ret_type; - std::vector bbs_to_emit; +private: + std::ostream* output_ = nullptr; + uint32_t bound = 1; - // Contains OpFunctionParams - SpvSectionBuilder header; + // Ordered as per https://www.khronos.org/registry/spir-v/specs/unified1/SPIRV.pdf#subsection.2.4 + SpvSectionBuilder capabilities; + SpvSectionBuilder extensions; + SpvSectionBuilder ext_inst_import; + SpvSectionBuilder entry_points; + SpvSectionBuilder execution_modes; + SpvSectionBuilder debug_string_source; + SpvSectionBuilder debug_names; + SpvSectionBuilder debug_module_processed; + SpvSectionBuilder annotations; + SpvSectionBuilder types_constants; + SpvSectionBuilder fn_decls; + SpvSectionBuilder fn_defs; - SpvSectionBuilder variables; + // SPIR-V disallows duplicate non-aggregate type declarations, we protect against these with this + std::unordered_map unique_decls; + std::unordered_map extended_instruction_sets; + std::unordered_set capabilities_set; + std::unordered_set extensions_set; - SpvId parameter(SpvId param_type) { - header.op(spv::Op::OpFunctionParameter, 3); - auto id = generate_fresh_id(); - header.ref_id(param_type); - header.ref_id(id); - return id; + void output_word_le(uint32_t word) { + output_->put((word >> 0) & 0xFFu); + output_->put((word >> 8) & 0xFFu); + output_->put((word >> 16) & 0xFFu); + output_->put((word >> 24) & 0xFFu); } - SpvId variable(SpvId type, spv::StorageClass storage_class) { - variables.op(spv::Op::OpVariable, 4); - variables.ref_id(type); - auto id = generate_fresh_id(); - variables.ref_id(id); - variables.literal_int(storage_class); - return id; + void output_section(SpvSectionBuilder& section) { + for (auto& word : section.data_) { + output_word_le(word); + } } +public: + void finish(std::ostream& output) { + output_ = &output; + SpvSectionBuilder memory_model_section; + memory_model_section.op(spv::Op::OpMemoryModel, 3); + memory_model_section.data_.push_back(addressing_model); + memory_model_section.data_.push_back(memory_model); -private: - SpvId generate_fresh_id(); -}; + output_word_le(spv::MagicNumber); + output_word_le(version); // TODO: target a specific spirv version + output_word_le(uint32_t(0)); // TODO get a magic number ? + output_word_le(bound); + output_word_le(uint32_t(0)); // instruction schema padding -struct SpvFileBuilder { + output_section(capabilities); + output_section(extensions); + output_section(ext_inst_import); + output_section(memory_model_section); + output_section(entry_points); + output_section(execution_modes); + output_section(debug_string_source); + output_section(debug_names); + output_section(debug_module_processed); + output_section(annotations); + output_section(types_constants); + output_section(fn_decls); + output_section(fn_defs); + } - enum UniqueDeclTag { - NONE, - FN_TYPE, - PTR_TYPE, - DEF_ARR_TYPE, - CONSTANT, - CONSTANT_COMPOSITE, - }; + friend SpvBasicBlockBuilder; +}; - /// Prevents duplicate declarations - struct UniqueDeclKey { - UniqueDeclTag tag; - std::vector members; +struct SpvBasicBlockBuilder : public SpvSectionBuilder { + explicit SpvBasicBlockBuilder(SpvFileBuilder& file_builder) + : file_builder(file_builder) + {} - bool operator==(const UniqueDeclKey &b) const { - return tag == b.tag && members == b.members; - } - }; + SpvFileBuilder& file_builder; - struct UniqueDeclKeyHasher { - size_t operator() (const UniqueDeclKey& key) const { - size_t acc = 0; - for (auto id : key.members) - acc ^= std::hash{}(id); - return std::hash{}(key.tag) ^ acc; - } + struct Phi { + SpvId type; + SpvId value; + std::vector> preds; }; + std::vector phis; + SpvId label; - SpvFileBuilder() {} - SpvFileBuilder(const SpvFileBuilder&) = delete; - - SpvId generate_fresh_id() { return { bound++ }; } - - void name(SpvId id, std::string_view str) { - assert(id < bound); - debug_names.op(spv::Op::OpName, 2 + div_roundup(str.size() + 1, 4)); - debug_names.ref_id(id); - debug_names.literal_name(str); - } - - SpvId declare_bool_type() { - types_constants.op(spv::Op::OpTypeBool, 2); - auto id = generate_fresh_id(); - types_constants.ref_id(id); - return id; - } - - SpvId declare_int_type(int width, bool signed_) { - types_constants.op(spv::Op::OpTypeInt, 4); - auto id = generate_fresh_id(); - types_constants.ref_id(id); - types_constants.literal_int(width); - types_constants.literal_int(signed_ ? 1 : 0); - return id; - } - - SpvId declare_float_type(int width) { - types_constants.op(spv::Op::OpTypeFloat, 3); - auto id = generate_fresh_id(); - types_constants.ref_id(id); - types_constants.literal_int(width); - return id; - } - - SpvId declare_ptr_type(spv::StorageClass storage_class, SpvId element_type) { - auto key = UniqueDeclKey { PTR_TYPE, { element_type, (uint32_t) storage_class } }; - if (auto iter = unique_decls.find(key); iter != unique_decls.end()) return iter->second; - types_constants.op(spv::Op::OpTypePointer, 4); + SpvId undef(SpvId type) { + op(spv::Op::OpUndef, 3); + ref_id(type); auto id = generate_fresh_id(); - types_constants.ref_id(id); - types_constants.literal_int(storage_class); - types_constants.ref_id(element_type); - unique_decls[key] = id; + ref_id(id); return id; } - SpvId declare_array_type(SpvId element_type, SpvId dim) { - auto key = UniqueDeclKey { DEF_ARR_TYPE, { element_type, dim } }; - if (auto iter = unique_decls.find(key); iter != unique_decls.end()) return iter->second; - types_constants.op(spv::Op::OpTypeArray, 4); + SpvId composite(SpvId aggregate_t, std::vector& elements) { + op(spv::Op::OpCompositeConstruct, 3 + elements.size()); + ref_id(aggregate_t); auto id = generate_fresh_id(); - types_constants.ref_id(id); - types_constants.ref_id(element_type); - types_constants.ref_id(dim); - unique_decls[key] = id; + ref_id(id); + for (auto e : elements) + ref_id(e); return id; } - SpvId declare_fn_type(std::vector dom, SpvId codom) { - auto key = UniqueDeclKey { FN_TYPE, {} }; - for (auto d : dom) key.members.push_back(d); - key.members.push_back(codom); - if (auto iter = unique_decls.find(key); iter != unique_decls.end()) return iter->second; - - types_constants.op(spv::Op::OpTypeFunction, 3 + dom.size()); + SpvId extract(SpvId target_type, SpvId composite, std::vector indices) { + op(spv::Op::OpCompositeExtract, 4 + indices.size()); + ref_id(target_type); auto id = generate_fresh_id(); - types_constants.ref_id(id); - types_constants.ref_id(codom); - for (auto arg : dom) - types_constants.ref_id(arg); - unique_decls[key] = id; + ref_id(id); + ref_id(composite); + for (auto i : indices) + literal_int(i); return id; } - SpvId declare_struct_type(std::vector elements) { - types_constants.op(spv::Op::OpTypeStruct, 2 + elements.size()); + SpvId insert(SpvId target_type, SpvId object, SpvId composite, std::vector indices) { + op(spv::Op::OpCompositeInsert, 5 + indices.size()); + ref_id(target_type); auto id = generate_fresh_id(); - types_constants.ref_id(id); - for (auto arg : elements) - types_constants.ref_id(arg); + ref_id(id); + ref_id(object); + ref_id(composite); + for (auto i : indices) + literal_int(i); return id; } - SpvId declare_vector_type(SpvId component_type, uint32_t dim) { - types_constants.op(spv::Op::OpTypeVector, 4); + SpvId vector_extract_dynamic(SpvId target_type, SpvId vector, SpvId index) { + op(spv::Op::OpVectorExtractDynamic, 5); + ref_id(target_type); auto id = generate_fresh_id(); - types_constants.ref_id(id); - types_constants.ref_id(component_type); - types_constants.literal_int(dim); + ref_id(id); + ref_id(vector); + ref_id(index); return id; } - void decorate(SpvId target, spv::Decoration decoration, std::vector extra = {}) { - annotations.op(spv::Op::OpDecorate, 3 + extra.size()); - annotations.ref_id(target); - annotations.literal_int(decoration); - for (auto e : extra) - annotations.literal_int(e); - } - - void decorate_member(SpvId target, uint32_t member, spv::Decoration decoration, std::vector extra = {}) { - annotations.op(spv::Op::OpMemberDecorate, 4 + extra.size()); - annotations.ref_id(target); - annotations.literal_int(member); - annotations.literal_int(decoration); - for (auto e : extra) - annotations.literal_int(e); - } - - SpvId debug_string(std::string string) { - debug_string_source.op(spv::Op::OpString, 2 + div_roundup(string.size() + 1, 4)); + SpvId vector_insert_dynamic(SpvId target_type, SpvId vector, SpvId component, SpvId index) { + op(spv::Op::OpVectorInsertDynamic, 6); + ref_id(target_type); auto id = generate_fresh_id(); - debug_string_source.ref_id(id); - debug_string_source.literal_name(string); + ref_id(id); + ref_id(vector); + ref_id(component); + ref_id(index); return id; } - SpvId bool_constant(SpvId type, bool value) { - types_constants.op(value ? spv::Op::OpConstantTrue : spv::Op::OpConstantFalse, 3); + // Used for almost all conversion operations + SpvId convert(spv::Op op_, SpvId target_type, SpvId value) { + op(op_, 4); auto id = generate_fresh_id(); - types_constants.ref_id(type); - types_constants.ref_id(id); + ref_id(target_type); + ref_id(id); + ref_id(value); return id; } - SpvId constant(SpvId type, std::vector bit_pattern) { - auto key = UniqueDeclKey { CONSTANT, bit_pattern }; - key.members.push_back(type); - if (auto iter = unique_decls.find(key); iter != unique_decls.end()) return iter->second; - types_constants.op(spv::Op::OpConstant, 3 + bit_pattern.size()); + SpvId access_chain(SpvId target_type, SpvId element, std::vector indexes) { + op(spv::Op::OpAccessChain, 4 + indexes.size()); auto id = generate_fresh_id(); - types_constants.ref_id(type); - types_constants.ref_id(id); - for (auto arg : bit_pattern) - types_constants.literal_int(arg); - unique_decls[key] = id; + ref_id(target_type); + ref_id(id); + ref_id(element); + for (auto index : indexes) + ref_id(index); return id; } - SpvId constant_composite(SpvId type, std::vector ops) { - auto key = UniqueDeclKey { CONSTANT_COMPOSITE, {} }; - key.members.push_back(type); - for (auto op : ops) key.members.push_back(op); - if (auto iter = unique_decls.find(key); iter != unique_decls.end()) return iter->second; - types_constants.op(spv::Op::OpConstantComposite, 3 + ops.size()); + SpvId ptr_access_chain(SpvId target_type, SpvId base, SpvId element, std::vector indexes) { + op(spv::Op::OpPtrAccessChain, 5 + indexes.size()); auto id = generate_fresh_id(); - types_constants.ref_id(type); - types_constants.ref_id(id); - for (auto op : ops) - types_constants.ref_id(op); - unique_decls[key] = id; + ref_id(target_type); + ref_id(id); + ref_id(base); + ref_id(element); + for (auto index : indexes) + ref_id(index); return id; } - SpvId variable(SpvId type, spv::StorageClass storage_class) { - types_constants.op(spv::Op::OpVariable, 4); - types_constants.ref_id(type); + SpvId load(SpvId target_type, SpvId pointer, std::vector operands = {}) { + op(spv::Op::OpLoad, 4 + operands.size()); auto id = generate_fresh_id(); - types_constants.ref_id(id); - types_constants.literal_int(storage_class); + ref_id(target_type); + ref_id(id); + ref_id(pointer); + for (auto op : operands) + literal_int(op); return id; } - SpvId declare_void_type() { - types_constants.op(spv::Op::OpTypeVoid, 2); + void store(SpvId value, SpvId pointer, std::vector operands = {}) { + op(spv::Op::OpStore, 3 + operands.size()); + ref_id(pointer); + ref_id(value); + for (auto op : operands) + literal_int(op); + } + + SpvId binop(spv::Op op_, SpvId result_type, SpvId lhs, SpvId rhs) { + op(op_, 5); auto id = generate_fresh_id(); - types_constants.ref_id(id); + ref_id(result_type); + ref_id(id); + ref_id(lhs); + ref_id(rhs); return id; } - SpvId define_function(SpvFnBuilder& fn_builder) { - fn_defs.op(spv::Op::OpFunction, 5); - fn_defs.ref_id(fn_builder.fn_ret_type); - fn_defs.ref_id(fn_builder.function_id); - fn_defs.data_.push_back(spv::FunctionControlMaskNone); - fn_defs.ref_id(fn_builder.fn_type); + void branch(SpvId target) { + op(spv::Op::OpBranch, 2); + ref_id(target); + } - // Includes stuff like OpFunctionParameters - for (auto w : fn_builder.header.data_) - fn_defs.data_.push_back(w); + void branch_conditional(SpvId condition, SpvId true_target, SpvId false_target) { + op(spv::Op::OpBranchConditional, 4); + ref_id(condition); + ref_id(true_target); + ref_id(false_target); + } + + void branch_switch(SpvId selector, SpvId default_case, std::vector literals, std::vector cases) { + assert(literals.size() == cases.size()); + op(spv::Op::OpSwitch, 3 + literals.size() * 2); + ref_id(selector); + ref_id(default_case); + for (size_t i = 0; i < literals.size(); i++) { + ref_id(literals[i]); + ref_id(cases[i]); + } + } - bool first = true; - for (auto& bb : fn_builder.bbs_to_emit) { - fn_defs.op(spv::Op::OpLabel, 2); - fn_defs.ref_id(bb->label); + void selection_merge(SpvId merge_bb, spv::SelectionControlMask selection_control) { + op(spv::Op::OpSelectionMerge, 3); + ref_id(merge_bb); + literal_int(selection_control); + } - if (first) { - for (auto w : fn_builder.variables.data_) - fn_defs.data_.push_back(w); - first = false; - } + void loop_merge(SpvId merge_bb, SpvId continue_bb, spv::LoopControlMask loop_control, std::vector loop_control_ops) { + op(spv::Op::OpLoopMerge, 4 + loop_control_ops.size()); + ref_id(merge_bb); + ref_id(continue_bb); + literal_int(loop_control); - for (auto& phi : bb->phis) { - fn_defs.op(spv::Op::OpPhi, 3 + 2 * phi->preds.size()); - fn_defs.ref_id(phi->type); - fn_defs.ref_id(phi->value); - assert(!phi->preds.empty()); - for (auto& [pred_value, pred_label] : phi->preds) { - fn_defs.ref_id(pred_value); - fn_defs.ref_id(pred_label); - } - } + for (auto e : loop_control_ops) + literal_int(e); + } - for (auto w : bb->data_) - fn_defs.data_.push_back(w); - } + SpvId call(SpvId return_type, SpvId callee, std::vector arguments) { + op(spv::Op::OpFunctionCall, 4 + arguments.size()); + auto id = generate_fresh_id(); + ref_id(return_type); + ref_id(id); + ref_id(callee); - fn_defs.op(spv::Op::OpFunctionEnd, 1); - return fn_builder.function_id; + for (auto a : arguments) + ref_id(a); + return id; } - void declare_entry_point(spv::ExecutionModel execution_model, SpvId entry_point, std::string name, std::vector interface) { - entry_points.op(spv::Op::OpEntryPoint, 3 + div_roundup(name.size() + 1, 4) + interface.size()); - entry_points.literal_int(execution_model); - entry_points.ref_id(entry_point); - entry_points.literal_name(name); - for (auto i : interface) - entry_points.ref_id(i); + SpvId ext_instruction(SpvId return_type, ExtendedInstruction instr, std::vector arguments); + + void return_void() { + op(spv::Op::OpReturn, 1); } - void execution_mode(SpvId entry_point, spv::ExecutionMode execution_mode, std::vector payloads) { - entry_points.op(spv::Op::OpExecutionMode, 3 + payloads.size()); - entry_points.ref_id(entry_point); - entry_points.literal_int(execution_mode); - for (auto d : payloads) - entry_points.literal_int(d); + void return_value(SpvId value) { + op(spv::Op::OpReturnValue, 2); + ref_id(value); } - void capability(spv::Capability cap) { - auto found = capabilities_set.find(cap); - if (found != capabilities_set.end()) - return; - capabilities.op(spv::Op::OpCapability, 2); - capabilities.data_.push_back(cap); - capabilities_set.insert(cap); + void unreachable() { + op(spv::Op::OpUnreachable, 1); } - void extension(std::string name) { - auto found = extensions_set.find(name); - if (found != extensions_set.end()) - return; - extensions.op(spv::Op::OpExtension, 1 + div_roundup(name.size() + 1, 4)); - extensions.literal_name(name); - extensions_set.insert(name); +private: + SpvId generate_fresh_id(); + +protected: + SpvId ext_instruction(SpvId return_type, SpvId set, uint32_t instruction, std::vector arguments) { + op(spv::Op::OpExtInst, 5 + arguments.size()); + auto id = generate_fresh_id(); + ref_id(return_type); + ref_id(id); + ref_id(set); + literal_int(instruction); + for (auto a : arguments) + ref_id(a); + return id; } +}; - uint32_t version = spv::Version; +struct SpvFnBuilder { + explicit SpvFnBuilder(SpvFileBuilder* file_builder) + : file_builder(file_builder) + { + function_id = generate_fresh_id(); + } - spv::AddressingModel addressing_model = spv::AddressingModel::AddressingModelLogical; - spv::MemoryModel memory_model = spv::MemoryModel::MemoryModelSimple; + SpvFileBuilder* file_builder; + SpvId function_id; -protected: - SpvId extended_import(std::string name) { - auto found = extended_instruction_sets.find(name); - if (found != extended_instruction_sets.end()) - return found->second; - ext_inst_import.op(spv::Op::OpExtInstImport, 2 + div_roundup(name.size() + 1, 4)); + SpvId fn_type; + SpvId fn_ret_type; + std::vector bbs_to_emit; + + // Contains OpFunctionParams + SpvSectionBuilder header; + + SpvSectionBuilder variables; + + SpvId parameter(SpvId param_type) { + header.op(spv::Op::OpFunctionParameter, 3); auto id = generate_fresh_id(); - ext_inst_import.ref_id(id); - ext_inst_import.literal_name(name); - extended_instruction_sets[name] = id; + header.ref_id(param_type); + header.ref_id(id); + return id; + } + + SpvId variable(SpvId type, spv::StorageClass storage_class) { + variables.op(spv::Op::OpVariable, 4); + variables.ref_id(type); + auto id = generate_fresh_id(); + variables.ref_id(id); + variables.literal_int(storage_class); return id; } private: - std::ostream* output_ = nullptr; - uint32_t bound = 1; + SpvId generate_fresh_id(); +}; - // Ordered as per https://www.khronos.org/registry/spir-v/specs/unified1/SPIRV.pdf#subsection.2.4 - SpvSectionBuilder capabilities; - SpvSectionBuilder extensions; - SpvSectionBuilder ext_inst_import; - SpvSectionBuilder entry_points; - SpvSectionBuilder execution_modes; - SpvSectionBuilder debug_string_source; - SpvSectionBuilder debug_names; - SpvSectionBuilder debug_module_processed; - SpvSectionBuilder annotations; - SpvSectionBuilder types_constants; - SpvSectionBuilder fn_decls; - SpvSectionBuilder fn_defs; +inline SpvId SpvFileBuilder::define_function(SpvFnBuilder &fn_builder) { + fn_defs.op(spv::Op::OpFunction, 5); + fn_defs.ref_id(fn_builder.fn_ret_type); + fn_defs.ref_id(fn_builder.function_id); + fn_defs.data_.push_back(spv::FunctionControlMaskNone); + fn_defs.ref_id(fn_builder.fn_type); - // SPIR-V disallows duplicate non-aggregate type declarations, we protect against these with this - std::unordered_map unique_decls; - std::unordered_map extended_instruction_sets; - std::unordered_set capabilities_set; - std::unordered_set extensions_set; + // Includes stuff like OpFunctionParameters + for (auto w : fn_builder.header.data_) + fn_defs.data_.push_back(w); - void output_word_le(uint32_t word) { - output_->put((word >> 0) & 0xFFu); - output_->put((word >> 8) & 0xFFu); - output_->put((word >> 16) & 0xFFu); - output_->put((word >> 24) & 0xFFu); - } + bool first = true; + for (auto& bb : fn_builder.bbs_to_emit) { + fn_defs.op(spv::Op::OpLabel, 2); + fn_defs.ref_id(bb->label); - void output_section(SpvSectionBuilder& section) { - for (auto& word : section.data_) { - output_word_le(word); + if (first) { + for (auto w : fn_builder.variables.data_) + fn_defs.data_.push_back(w); + first = false; } - } -public: - void finish(std::ostream& output) { - output_ = &output; - SpvSectionBuilder memory_model_section; - memory_model_section.op(spv::Op::OpMemoryModel, 3); - memory_model_section.data_.push_back(addressing_model); - memory_model_section.data_.push_back(memory_model); - output_word_le(spv::MagicNumber); - output_word_le(version); // TODO: target a specific spirv version - output_word_le(uint32_t(0)); // TODO get a magic number ? - output_word_le(bound); - output_word_le(uint32_t(0)); // instruction schema padding + for (auto& phi : bb->phis) { + fn_defs.op(spv::Op::OpPhi, 3 + 2 * phi->preds.size()); + fn_defs.ref_id(phi->type); + fn_defs.ref_id(phi->value); + assert(!phi->preds.empty()); + for (auto& [pred_value, pred_label] : phi->preds) { + fn_defs.ref_id(pred_value); + fn_defs.ref_id(pred_label); + } + } - output_section(capabilities); - output_section(extensions); - output_section(ext_inst_import); - output_section(memory_model_section); - output_section(entry_points); - output_section(execution_modes); - output_section(debug_string_source); - output_section(debug_names); - output_section(debug_module_processed); - output_section(annotations); - output_section(types_constants); - output_section(fn_decls); - output_section(fn_defs); + for (auto w : bb->data_) + fn_defs.data_.push_back(w); } - friend SpvBasicBlockBuilder; -}; + fn_defs.op(spv::Op::OpFunctionEnd, 1); + return fn_builder.function_id; +} + inline SpvId SpvBasicBlockBuilder::generate_fresh_id() { return file_builder.generate_fresh_id(); From bc35e0fc7318adfc16c5184dbb2e74864fac3e36 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Mon, 4 Nov 2024 19:28:59 +0100 Subject: [PATCH 302/342] spirv_builder.hpp: remove now-useless generate_fresh_id wrappers --- src/thorin/be/spirv/spirv_builder.hpp | 47 +++++++++------------------ 1 file changed, 16 insertions(+), 31 deletions(-) diff --git a/src/thorin/be/spirv/spirv_builder.hpp b/src/thorin/be/spirv/spirv_builder.hpp index 64efb134d..2f7cdf88c 100644 --- a/src/thorin/be/spirv/spirv_builder.hpp +++ b/src/thorin/be/spirv/spirv_builder.hpp @@ -414,7 +414,7 @@ struct SpvBasicBlockBuilder : public SpvSectionBuilder { SpvId undef(SpvId type) { op(spv::Op::OpUndef, 3); ref_id(type); - auto id = generate_fresh_id(); + auto id = file_builder.generate_fresh_id(); ref_id(id); return id; } @@ -422,7 +422,7 @@ struct SpvBasicBlockBuilder : public SpvSectionBuilder { SpvId composite(SpvId aggregate_t, std::vector& elements) { op(spv::Op::OpCompositeConstruct, 3 + elements.size()); ref_id(aggregate_t); - auto id = generate_fresh_id(); + auto id = file_builder.generate_fresh_id(); ref_id(id); for (auto e : elements) ref_id(e); @@ -432,7 +432,7 @@ struct SpvBasicBlockBuilder : public SpvSectionBuilder { SpvId extract(SpvId target_type, SpvId composite, std::vector indices) { op(spv::Op::OpCompositeExtract, 4 + indices.size()); ref_id(target_type); - auto id = generate_fresh_id(); + auto id = file_builder.generate_fresh_id(); ref_id(id); ref_id(composite); for (auto i : indices) @@ -443,7 +443,7 @@ struct SpvBasicBlockBuilder : public SpvSectionBuilder { SpvId insert(SpvId target_type, SpvId object, SpvId composite, std::vector indices) { op(spv::Op::OpCompositeInsert, 5 + indices.size()); ref_id(target_type); - auto id = generate_fresh_id(); + auto id = file_builder.generate_fresh_id(); ref_id(id); ref_id(object); ref_id(composite); @@ -455,7 +455,7 @@ struct SpvBasicBlockBuilder : public SpvSectionBuilder { SpvId vector_extract_dynamic(SpvId target_type, SpvId vector, SpvId index) { op(spv::Op::OpVectorExtractDynamic, 5); ref_id(target_type); - auto id = generate_fresh_id(); + auto id = file_builder.generate_fresh_id(); ref_id(id); ref_id(vector); ref_id(index); @@ -465,7 +465,7 @@ struct SpvBasicBlockBuilder : public SpvSectionBuilder { SpvId vector_insert_dynamic(SpvId target_type, SpvId vector, SpvId component, SpvId index) { op(spv::Op::OpVectorInsertDynamic, 6); ref_id(target_type); - auto id = generate_fresh_id(); + auto id = file_builder.generate_fresh_id(); ref_id(id); ref_id(vector); ref_id(component); @@ -476,7 +476,7 @@ struct SpvBasicBlockBuilder : public SpvSectionBuilder { // Used for almost all conversion operations SpvId convert(spv::Op op_, SpvId target_type, SpvId value) { op(op_, 4); - auto id = generate_fresh_id(); + auto id = file_builder.generate_fresh_id(); ref_id(target_type); ref_id(id); ref_id(value); @@ -485,7 +485,7 @@ struct SpvBasicBlockBuilder : public SpvSectionBuilder { SpvId access_chain(SpvId target_type, SpvId element, std::vector indexes) { op(spv::Op::OpAccessChain, 4 + indexes.size()); - auto id = generate_fresh_id(); + auto id = file_builder.generate_fresh_id(); ref_id(target_type); ref_id(id); ref_id(element); @@ -496,7 +496,7 @@ struct SpvBasicBlockBuilder : public SpvSectionBuilder { SpvId ptr_access_chain(SpvId target_type, SpvId base, SpvId element, std::vector indexes) { op(spv::Op::OpPtrAccessChain, 5 + indexes.size()); - auto id = generate_fresh_id(); + auto id = file_builder.generate_fresh_id(); ref_id(target_type); ref_id(id); ref_id(base); @@ -508,7 +508,7 @@ struct SpvBasicBlockBuilder : public SpvSectionBuilder { SpvId load(SpvId target_type, SpvId pointer, std::vector operands = {}) { op(spv::Op::OpLoad, 4 + operands.size()); - auto id = generate_fresh_id(); + auto id = file_builder.generate_fresh_id(); ref_id(target_type); ref_id(id); ref_id(pointer); @@ -527,7 +527,7 @@ struct SpvBasicBlockBuilder : public SpvSectionBuilder { SpvId binop(spv::Op op_, SpvId result_type, SpvId lhs, SpvId rhs) { op(op_, 5); - auto id = generate_fresh_id(); + auto id = file_builder.generate_fresh_id(); ref_id(result_type); ref_id(id); ref_id(lhs); @@ -576,7 +576,7 @@ struct SpvBasicBlockBuilder : public SpvSectionBuilder { SpvId call(SpvId return_type, SpvId callee, std::vector arguments) { op(spv::Op::OpFunctionCall, 4 + arguments.size()); - auto id = generate_fresh_id(); + auto id = file_builder.generate_fresh_id(); ref_id(return_type); ref_id(id); ref_id(callee); @@ -601,13 +601,10 @@ struct SpvBasicBlockBuilder : public SpvSectionBuilder { op(spv::Op::OpUnreachable, 1); } -private: - SpvId generate_fresh_id(); - protected: SpvId ext_instruction(SpvId return_type, SpvId set, uint32_t instruction, std::vector arguments) { op(spv::Op::OpExtInst, 5 + arguments.size()); - auto id = generate_fresh_id(); + auto id = file_builder.generate_fresh_id(); ref_id(return_type); ref_id(id); ref_id(set); @@ -622,7 +619,7 @@ struct SpvFnBuilder { explicit SpvFnBuilder(SpvFileBuilder* file_builder) : file_builder(file_builder) { - function_id = generate_fresh_id(); + function_id = file_builder->generate_fresh_id(); } SpvFileBuilder* file_builder; @@ -639,7 +636,7 @@ struct SpvFnBuilder { SpvId parameter(SpvId param_type) { header.op(spv::Op::OpFunctionParameter, 3); - auto id = generate_fresh_id(); + auto id = file_builder->generate_fresh_id(); header.ref_id(param_type); header.ref_id(id); return id; @@ -648,14 +645,11 @@ struct SpvFnBuilder { SpvId variable(SpvId type, spv::StorageClass storage_class) { variables.op(spv::Op::OpVariable, 4); variables.ref_id(type); - auto id = generate_fresh_id(); + auto id = file_builder->generate_fresh_id(); variables.ref_id(id); variables.literal_int(storage_class); return id; } - -private: - SpvId generate_fresh_id(); }; inline SpvId SpvFileBuilder::define_function(SpvFnBuilder &fn_builder) { @@ -699,15 +693,6 @@ inline SpvId SpvFileBuilder::define_function(SpvFnBuilder &fn_builder) { return fn_builder.function_id; } - -inline SpvId SpvBasicBlockBuilder::generate_fresh_id() { - return file_builder.generate_fresh_id(); -} - -inline SpvId SpvFnBuilder::generate_fresh_id() { - return file_builder->generate_fresh_id(); -} - inline SpvId SpvBasicBlockBuilder::ext_instruction(SpvId return_type, ExtendedInstruction instr, std::vector arguments) { return ext_instruction(return_type, file_builder.extended_import(instr.set_name), instr.id, arguments); } From b87c8631fb9b087dcedc25e116eb7c4a50237e7e Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Mon, 4 Nov 2024 19:30:05 +0100 Subject: [PATCH 303/342] spirv_builder.hpp: made file_builder field in FnBuilder a reference --- src/thorin/be/spirv/spirv.cpp | 2 +- src/thorin/be/spirv/spirv_builder.hpp | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/thorin/be/spirv/spirv.cpp b/src/thorin/be/spirv/spirv.cpp index c0be0e581..c879a4f37 100644 --- a/src/thorin/be/spirv/spirv.cpp +++ b/src/thorin/be/spirv/spirv.cpp @@ -80,7 +80,7 @@ BasicBlockBuilder::BasicBlockBuilder(FnBuilder& fn_builder) label = file_builder.generate_fresh_id(); } -FnBuilder::FnBuilder(FileBuilder& file_builder) : builder::SpvFnBuilder(&file_builder), file_builder(file_builder) {} +FnBuilder::FnBuilder(FileBuilder& file_builder) : builder::SpvFnBuilder(file_builder), file_builder(file_builder) {} FileBuilder::FileBuilder(CodeGen* cg) : builder::SpvFileBuilder(), cg(cg) { } diff --git a/src/thorin/be/spirv/spirv_builder.hpp b/src/thorin/be/spirv/spirv_builder.hpp index 2f7cdf88c..3c57da195 100644 --- a/src/thorin/be/spirv/spirv_builder.hpp +++ b/src/thorin/be/spirv/spirv_builder.hpp @@ -616,13 +616,13 @@ struct SpvBasicBlockBuilder : public SpvSectionBuilder { }; struct SpvFnBuilder { - explicit SpvFnBuilder(SpvFileBuilder* file_builder) + explicit SpvFnBuilder(SpvFileBuilder& file_builder) : file_builder(file_builder) { - function_id = file_builder->generate_fresh_id(); + function_id = file_builder.generate_fresh_id(); } - SpvFileBuilder* file_builder; + SpvFileBuilder& file_builder; SpvId function_id; SpvId fn_type; @@ -636,7 +636,7 @@ struct SpvFnBuilder { SpvId parameter(SpvId param_type) { header.op(spv::Op::OpFunctionParameter, 3); - auto id = file_builder->generate_fresh_id(); + auto id = file_builder.generate_fresh_id(); header.ref_id(param_type); header.ref_id(id); return id; @@ -645,7 +645,7 @@ struct SpvFnBuilder { SpvId variable(SpvId type, spv::StorageClass storage_class) { variables.op(spv::Op::OpVariable, 4); variables.ref_id(type); - auto id = file_builder->generate_fresh_id(); + auto id = file_builder.generate_fresh_id(); variables.ref_id(id); variables.literal_int(storage_class); return id; From e20add369de11cf8cde07f4b2664903a3efc79dc Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Mon, 4 Nov 2024 19:32:18 +0100 Subject: [PATCH 304/342] spirv_builder.hpp: rename op() into begin_op() --- src/thorin/be/spirv/spirv_builder.hpp | 107 +++++++++++++------------- 1 file changed, 53 insertions(+), 54 deletions(-) diff --git a/src/thorin/be/spirv/spirv_builder.hpp b/src/thorin/be/spirv/spirv_builder.hpp index 3c57da195..f1fc3e82e 100644 --- a/src/thorin/be/spirv/spirv_builder.hpp +++ b/src/thorin/be/spirv/spirv_builder.hpp @@ -38,9 +38,9 @@ struct SpvSectionBuilder { data_.push_back(word); } public: - void op(spv::Op op, int ops_size) { + void begin_op(spv::Op op, int size_in_words) { uint32_t lower = op & 0xFFFFu; - uint32_t upper = (ops_size << 16) & 0xFFFF0000u; + uint32_t upper = (size_in_words << 16) & 0xFFFF0000u; output_word(lower | upper); } @@ -72,7 +72,6 @@ struct SpvSectionBuilder { struct SpvFileBuilder { - enum UniqueDeclTag { NONE, FN_TYPE, @@ -108,20 +107,20 @@ struct SpvFileBuilder { void name(SpvId id, std::string_view str) { assert(id < bound); - debug_names.op(spv::Op::OpName, 2 + div_roundup(str.size() + 1, 4)); + debug_names.begin_op(spv::Op::OpName, 2 + div_roundup(str.size() + 1, 4)); debug_names.ref_id(id); debug_names.literal_name(str); } SpvId declare_bool_type() { - types_constants.op(spv::Op::OpTypeBool, 2); + types_constants.begin_op(spv::Op::OpTypeBool, 2); auto id = generate_fresh_id(); types_constants.ref_id(id); return id; } SpvId declare_int_type(int width, bool signed_) { - types_constants.op(spv::Op::OpTypeInt, 4); + types_constants.begin_op(spv::Op::OpTypeInt, 4); auto id = generate_fresh_id(); types_constants.ref_id(id); types_constants.literal_int(width); @@ -130,7 +129,7 @@ struct SpvFileBuilder { } SpvId declare_float_type(int width) { - types_constants.op(spv::Op::OpTypeFloat, 3); + types_constants.begin_op(spv::Op::OpTypeFloat, 3); auto id = generate_fresh_id(); types_constants.ref_id(id); types_constants.literal_int(width); @@ -140,7 +139,7 @@ struct SpvFileBuilder { SpvId declare_ptr_type(spv::StorageClass storage_class, SpvId element_type) { auto key = UniqueDeclKey { PTR_TYPE, { element_type, (uint32_t) storage_class } }; if (auto iter = unique_decls.find(key); iter != unique_decls.end()) return iter->second; - types_constants.op(spv::Op::OpTypePointer, 4); + types_constants.begin_op(spv::Op::OpTypePointer, 4); auto id = generate_fresh_id(); types_constants.ref_id(id); types_constants.literal_int(storage_class); @@ -152,7 +151,7 @@ struct SpvFileBuilder { SpvId declare_array_type(SpvId element_type, SpvId dim) { auto key = UniqueDeclKey { DEF_ARR_TYPE, { element_type, dim } }; if (auto iter = unique_decls.find(key); iter != unique_decls.end()) return iter->second; - types_constants.op(spv::Op::OpTypeArray, 4); + types_constants.begin_op(spv::Op::OpTypeArray, 4); auto id = generate_fresh_id(); types_constants.ref_id(id); types_constants.ref_id(element_type); @@ -167,7 +166,7 @@ struct SpvFileBuilder { key.members.push_back(codom); if (auto iter = unique_decls.find(key); iter != unique_decls.end()) return iter->second; - types_constants.op(spv::Op::OpTypeFunction, 3 + dom.size()); + types_constants.begin_op(spv::Op::OpTypeFunction, 3 + dom.size()); auto id = generate_fresh_id(); types_constants.ref_id(id); types_constants.ref_id(codom); @@ -178,7 +177,7 @@ struct SpvFileBuilder { } SpvId declare_struct_type(std::vector elements) { - types_constants.op(spv::Op::OpTypeStruct, 2 + elements.size()); + types_constants.begin_op(spv::Op::OpTypeStruct, 2 + elements.size()); auto id = generate_fresh_id(); types_constants.ref_id(id); for (auto arg : elements) @@ -187,7 +186,7 @@ struct SpvFileBuilder { } SpvId declare_vector_type(SpvId component_type, uint32_t dim) { - types_constants.op(spv::Op::OpTypeVector, 4); + types_constants.begin_op(spv::Op::OpTypeVector, 4); auto id = generate_fresh_id(); types_constants.ref_id(id); types_constants.ref_id(component_type); @@ -196,7 +195,7 @@ struct SpvFileBuilder { } void decorate(SpvId target, spv::Decoration decoration, std::vector extra = {}) { - annotations.op(spv::Op::OpDecorate, 3 + extra.size()); + annotations.begin_op(spv::Op::OpDecorate, 3 + extra.size()); annotations.ref_id(target); annotations.literal_int(decoration); for (auto e : extra) @@ -204,7 +203,7 @@ struct SpvFileBuilder { } void decorate_member(SpvId target, uint32_t member, spv::Decoration decoration, std::vector extra = {}) { - annotations.op(spv::Op::OpMemberDecorate, 4 + extra.size()); + annotations.begin_op(spv::Op::OpMemberDecorate, 4 + extra.size()); annotations.ref_id(target); annotations.literal_int(member); annotations.literal_int(decoration); @@ -213,7 +212,7 @@ struct SpvFileBuilder { } SpvId debug_string(std::string string) { - debug_string_source.op(spv::Op::OpString, 2 + div_roundup(string.size() + 1, 4)); + debug_string_source.begin_op(spv::Op::OpString, 2 + div_roundup(string.size() + 1, 4)); auto id = generate_fresh_id(); debug_string_source.ref_id(id); debug_string_source.literal_name(string); @@ -221,7 +220,7 @@ struct SpvFileBuilder { } SpvId bool_constant(SpvId type, bool value) { - types_constants.op(value ? spv::Op::OpConstantTrue : spv::Op::OpConstantFalse, 3); + types_constants.begin_op(value ? spv::Op::OpConstantTrue : spv::Op::OpConstantFalse, 3); auto id = generate_fresh_id(); types_constants.ref_id(type); types_constants.ref_id(id); @@ -232,7 +231,7 @@ struct SpvFileBuilder { auto key = UniqueDeclKey { CONSTANT, bit_pattern }; key.members.push_back(type); if (auto iter = unique_decls.find(key); iter != unique_decls.end()) return iter->second; - types_constants.op(spv::Op::OpConstant, 3 + bit_pattern.size()); + types_constants.begin_op(spv::Op::OpConstant, 3 + bit_pattern.size()); auto id = generate_fresh_id(); types_constants.ref_id(type); types_constants.ref_id(id); @@ -247,7 +246,7 @@ struct SpvFileBuilder { key.members.push_back(type); for (auto op : ops) key.members.push_back(op); if (auto iter = unique_decls.find(key); iter != unique_decls.end()) return iter->second; - types_constants.op(spv::Op::OpConstantComposite, 3 + ops.size()); + types_constants.begin_op(spv::Op::OpConstantComposite, 3 + ops.size()); auto id = generate_fresh_id(); types_constants.ref_id(type); types_constants.ref_id(id); @@ -258,7 +257,7 @@ struct SpvFileBuilder { } SpvId variable(SpvId type, spv::StorageClass storage_class) { - types_constants.op(spv::Op::OpVariable, 4); + types_constants.begin_op(spv::Op::OpVariable, 4); types_constants.ref_id(type); auto id = generate_fresh_id(); types_constants.ref_id(id); @@ -267,7 +266,7 @@ struct SpvFileBuilder { } SpvId declare_void_type() { - types_constants.op(spv::Op::OpTypeVoid, 2); + types_constants.begin_op(spv::Op::OpTypeVoid, 2); auto id = generate_fresh_id(); types_constants.ref_id(id); return id; @@ -276,7 +275,7 @@ struct SpvFileBuilder { SpvId define_function(SpvFnBuilder& fn_builder); void declare_entry_point(spv::ExecutionModel execution_model, SpvId entry_point, std::string name, std::vector interface) { - entry_points.op(spv::Op::OpEntryPoint, 3 + div_roundup(name.size() + 1, 4) + interface.size()); + entry_points.begin_op(spv::Op::OpEntryPoint, 3 + div_roundup(name.size() + 1, 4) + interface.size()); entry_points.literal_int(execution_model); entry_points.ref_id(entry_point); entry_points.literal_name(name); @@ -285,7 +284,7 @@ struct SpvFileBuilder { } void execution_mode(SpvId entry_point, spv::ExecutionMode execution_mode, std::vector payloads) { - entry_points.op(spv::Op::OpExecutionMode, 3 + payloads.size()); + entry_points.begin_op(spv::Op::OpExecutionMode, 3 + payloads.size()); entry_points.ref_id(entry_point); entry_points.literal_int(execution_mode); for (auto d : payloads) @@ -296,7 +295,7 @@ struct SpvFileBuilder { auto found = capabilities_set.find(cap); if (found != capabilities_set.end()) return; - capabilities.op(spv::Op::OpCapability, 2); + capabilities.begin_op(spv::Op::OpCapability, 2); capabilities.data_.push_back(cap); capabilities_set.insert(cap); } @@ -305,7 +304,7 @@ struct SpvFileBuilder { auto found = extensions_set.find(name); if (found != extensions_set.end()) return; - extensions.op(spv::Op::OpExtension, 1 + div_roundup(name.size() + 1, 4)); + extensions.begin_op(spv::Op::OpExtension, 1 + div_roundup(name.size() + 1, 4)); extensions.literal_name(name); extensions_set.insert(name); } @@ -320,7 +319,7 @@ struct SpvFileBuilder { auto found = extended_instruction_sets.find(name); if (found != extended_instruction_sets.end()) return found->second; - ext_inst_import.op(spv::Op::OpExtInstImport, 2 + div_roundup(name.size() + 1, 4)); + ext_inst_import.begin_op(spv::Op::OpExtInstImport, 2 + div_roundup(name.size() + 1, 4)); auto id = generate_fresh_id(); ext_inst_import.ref_id(id); ext_inst_import.literal_name(name); @@ -368,7 +367,7 @@ struct SpvFileBuilder { void finish(std::ostream& output) { output_ = &output; SpvSectionBuilder memory_model_section; - memory_model_section.op(spv::Op::OpMemoryModel, 3); + memory_model_section.begin_op(spv::Op::OpMemoryModel, 3); memory_model_section.data_.push_back(addressing_model); memory_model_section.data_.push_back(memory_model); @@ -412,7 +411,7 @@ struct SpvBasicBlockBuilder : public SpvSectionBuilder { SpvId label; SpvId undef(SpvId type) { - op(spv::Op::OpUndef, 3); + begin_op(spv::Op::OpUndef, 3); ref_id(type); auto id = file_builder.generate_fresh_id(); ref_id(id); @@ -420,7 +419,7 @@ struct SpvBasicBlockBuilder : public SpvSectionBuilder { } SpvId composite(SpvId aggregate_t, std::vector& elements) { - op(spv::Op::OpCompositeConstruct, 3 + elements.size()); + begin_op(spv::Op::OpCompositeConstruct, 3 + elements.size()); ref_id(aggregate_t); auto id = file_builder.generate_fresh_id(); ref_id(id); @@ -430,7 +429,7 @@ struct SpvBasicBlockBuilder : public SpvSectionBuilder { } SpvId extract(SpvId target_type, SpvId composite, std::vector indices) { - op(spv::Op::OpCompositeExtract, 4 + indices.size()); + begin_op(spv::Op::OpCompositeExtract, 4 + indices.size()); ref_id(target_type); auto id = file_builder.generate_fresh_id(); ref_id(id); @@ -441,7 +440,7 @@ struct SpvBasicBlockBuilder : public SpvSectionBuilder { } SpvId insert(SpvId target_type, SpvId object, SpvId composite, std::vector indices) { - op(spv::Op::OpCompositeInsert, 5 + indices.size()); + begin_op(spv::Op::OpCompositeInsert, 5 + indices.size()); ref_id(target_type); auto id = file_builder.generate_fresh_id(); ref_id(id); @@ -453,7 +452,7 @@ struct SpvBasicBlockBuilder : public SpvSectionBuilder { } SpvId vector_extract_dynamic(SpvId target_type, SpvId vector, SpvId index) { - op(spv::Op::OpVectorExtractDynamic, 5); + begin_op(spv::Op::OpVectorExtractDynamic, 5); ref_id(target_type); auto id = file_builder.generate_fresh_id(); ref_id(id); @@ -463,7 +462,7 @@ struct SpvBasicBlockBuilder : public SpvSectionBuilder { } SpvId vector_insert_dynamic(SpvId target_type, SpvId vector, SpvId component, SpvId index) { - op(spv::Op::OpVectorInsertDynamic, 6); + begin_op(spv::Op::OpVectorInsertDynamic, 6); ref_id(target_type); auto id = file_builder.generate_fresh_id(); ref_id(id); @@ -475,7 +474,7 @@ struct SpvBasicBlockBuilder : public SpvSectionBuilder { // Used for almost all conversion operations SpvId convert(spv::Op op_, SpvId target_type, SpvId value) { - op(op_, 4); + begin_op(op_, 4); auto id = file_builder.generate_fresh_id(); ref_id(target_type); ref_id(id); @@ -484,7 +483,7 @@ struct SpvBasicBlockBuilder : public SpvSectionBuilder { } SpvId access_chain(SpvId target_type, SpvId element, std::vector indexes) { - op(spv::Op::OpAccessChain, 4 + indexes.size()); + begin_op(spv::Op::OpAccessChain, 4 + indexes.size()); auto id = file_builder.generate_fresh_id(); ref_id(target_type); ref_id(id); @@ -495,7 +494,7 @@ struct SpvBasicBlockBuilder : public SpvSectionBuilder { } SpvId ptr_access_chain(SpvId target_type, SpvId base, SpvId element, std::vector indexes) { - op(spv::Op::OpPtrAccessChain, 5 + indexes.size()); + begin_op(spv::Op::OpPtrAccessChain, 5 + indexes.size()); auto id = file_builder.generate_fresh_id(); ref_id(target_type); ref_id(id); @@ -507,7 +506,7 @@ struct SpvBasicBlockBuilder : public SpvSectionBuilder { } SpvId load(SpvId target_type, SpvId pointer, std::vector operands = {}) { - op(spv::Op::OpLoad, 4 + operands.size()); + begin_op(spv::Op::OpLoad, 4 + operands.size()); auto id = file_builder.generate_fresh_id(); ref_id(target_type); ref_id(id); @@ -518,7 +517,7 @@ struct SpvBasicBlockBuilder : public SpvSectionBuilder { } void store(SpvId value, SpvId pointer, std::vector operands = {}) { - op(spv::Op::OpStore, 3 + operands.size()); + begin_op(spv::Op::OpStore, 3 + operands.size()); ref_id(pointer); ref_id(value); for (auto op : operands) @@ -526,7 +525,7 @@ struct SpvBasicBlockBuilder : public SpvSectionBuilder { } SpvId binop(spv::Op op_, SpvId result_type, SpvId lhs, SpvId rhs) { - op(op_, 5); + begin_op(op_, 5); auto id = file_builder.generate_fresh_id(); ref_id(result_type); ref_id(id); @@ -536,12 +535,12 @@ struct SpvBasicBlockBuilder : public SpvSectionBuilder { } void branch(SpvId target) { - op(spv::Op::OpBranch, 2); + begin_op(spv::Op::OpBranch, 2); ref_id(target); } void branch_conditional(SpvId condition, SpvId true_target, SpvId false_target) { - op(spv::Op::OpBranchConditional, 4); + begin_op(spv::Op::OpBranchConditional, 4); ref_id(condition); ref_id(true_target); ref_id(false_target); @@ -549,7 +548,7 @@ struct SpvBasicBlockBuilder : public SpvSectionBuilder { void branch_switch(SpvId selector, SpvId default_case, std::vector literals, std::vector cases) { assert(literals.size() == cases.size()); - op(spv::Op::OpSwitch, 3 + literals.size() * 2); + begin_op(spv::Op::OpSwitch, 3 + literals.size() * 2); ref_id(selector); ref_id(default_case); for (size_t i = 0; i < literals.size(); i++) { @@ -559,13 +558,13 @@ struct SpvBasicBlockBuilder : public SpvSectionBuilder { } void selection_merge(SpvId merge_bb, spv::SelectionControlMask selection_control) { - op(spv::Op::OpSelectionMerge, 3); + begin_op(spv::Op::OpSelectionMerge, 3); ref_id(merge_bb); literal_int(selection_control); } void loop_merge(SpvId merge_bb, SpvId continue_bb, spv::LoopControlMask loop_control, std::vector loop_control_ops) { - op(spv::Op::OpLoopMerge, 4 + loop_control_ops.size()); + begin_op(spv::Op::OpLoopMerge, 4 + loop_control_ops.size()); ref_id(merge_bb); ref_id(continue_bb); literal_int(loop_control); @@ -575,7 +574,7 @@ struct SpvBasicBlockBuilder : public SpvSectionBuilder { } SpvId call(SpvId return_type, SpvId callee, std::vector arguments) { - op(spv::Op::OpFunctionCall, 4 + arguments.size()); + begin_op(spv::Op::OpFunctionCall, 4 + arguments.size()); auto id = file_builder.generate_fresh_id(); ref_id(return_type); ref_id(id); @@ -589,21 +588,21 @@ struct SpvBasicBlockBuilder : public SpvSectionBuilder { SpvId ext_instruction(SpvId return_type, ExtendedInstruction instr, std::vector arguments); void return_void() { - op(spv::Op::OpReturn, 1); + begin_op(spv::Op::OpReturn, 1); } void return_value(SpvId value) { - op(spv::Op::OpReturnValue, 2); + begin_op(spv::Op::OpReturnValue, 2); ref_id(value); } void unreachable() { - op(spv::Op::OpUnreachable, 1); + begin_op(spv::Op::OpUnreachable, 1); } protected: SpvId ext_instruction(SpvId return_type, SpvId set, uint32_t instruction, std::vector arguments) { - op(spv::Op::OpExtInst, 5 + arguments.size()); + begin_op(spv::Op::OpExtInst, 5 + arguments.size()); auto id = file_builder.generate_fresh_id(); ref_id(return_type); ref_id(id); @@ -635,7 +634,7 @@ struct SpvFnBuilder { SpvSectionBuilder variables; SpvId parameter(SpvId param_type) { - header.op(spv::Op::OpFunctionParameter, 3); + header.begin_op(spv::Op::OpFunctionParameter, 3); auto id = file_builder.generate_fresh_id(); header.ref_id(param_type); header.ref_id(id); @@ -643,7 +642,7 @@ struct SpvFnBuilder { } SpvId variable(SpvId type, spv::StorageClass storage_class) { - variables.op(spv::Op::OpVariable, 4); + variables.begin_op(spv::Op::OpVariable, 4); variables.ref_id(type); auto id = file_builder.generate_fresh_id(); variables.ref_id(id); @@ -653,7 +652,7 @@ struct SpvFnBuilder { }; inline SpvId SpvFileBuilder::define_function(SpvFnBuilder &fn_builder) { - fn_defs.op(spv::Op::OpFunction, 5); + fn_defs.begin_op(spv::Op::OpFunction, 5); fn_defs.ref_id(fn_builder.fn_ret_type); fn_defs.ref_id(fn_builder.function_id); fn_defs.data_.push_back(spv::FunctionControlMaskNone); @@ -665,7 +664,7 @@ inline SpvId SpvFileBuilder::define_function(SpvFnBuilder &fn_builder) { bool first = true; for (auto& bb : fn_builder.bbs_to_emit) { - fn_defs.op(spv::Op::OpLabel, 2); + fn_defs.begin_op(spv::Op::OpLabel, 2); fn_defs.ref_id(bb->label); if (first) { @@ -675,7 +674,7 @@ inline SpvId SpvFileBuilder::define_function(SpvFnBuilder &fn_builder) { } for (auto& phi : bb->phis) { - fn_defs.op(spv::Op::OpPhi, 3 + 2 * phi->preds.size()); + fn_defs.begin_op(spv::Op::OpPhi, 3 + 2 * phi->preds.size()); fn_defs.ref_id(phi->type); fn_defs.ref_id(phi->value); assert(!phi->preds.empty()); @@ -689,7 +688,7 @@ inline SpvId SpvFileBuilder::define_function(SpvFnBuilder &fn_builder) { fn_defs.data_.push_back(w); } - fn_defs.op(spv::Op::OpFunctionEnd, 1); + fn_defs.begin_op(spv::Op::OpFunctionEnd, 1); return fn_builder.function_id; } From 5a60b5030ef13c9a88841ee613e042a33cc42688 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Mon, 4 Nov 2024 19:38:05 +0100 Subject: [PATCH 305/342] spirv: renamed a bunch of types to drop the 'Spv' prefix --- src/thorin/be/spirv/spirv.cpp | 62 +++---- src/thorin/be/spirv/spirv.h | 22 +-- src/thorin/be/spirv/spirv_builder.hpp | 182 ++++++++++----------- src/thorin/be/spirv/spirv_instructions.cpp | 4 +- src/thorin/be/spirv/spirv_private.h | 18 +- src/thorin/be/spirv/spirv_types.cpp | 10 +- 6 files changed, 148 insertions(+), 150 deletions(-) diff --git a/src/thorin/be/spirv/spirv.cpp b/src/thorin/be/spirv/spirv.cpp index c879a4f37..b8877a567 100644 --- a/src/thorin/be/spirv/spirv.cpp +++ b/src/thorin/be/spirv/spirv.cpp @@ -10,7 +10,7 @@ namespace thorin::spirv { /// Used as a dummy SSA value for emitting things like mem/unit /// Should never make it in the binary files ! -constexpr SpvId spv_none { 0 }; +constexpr Id spv_none { 0 }; // SPIR-V has 3 "kinds" of primitives, and the user may declare arbitrary bitwidths, the following helps in translation: enum class PrimTypeKind { @@ -76,22 +76,22 @@ switch (bitwidth) { \ } BasicBlockBuilder::BasicBlockBuilder(FnBuilder& fn_builder) - : builder::SpvBasicBlockBuilder(fn_builder.file_builder), fn_builder(fn_builder), file_builder(fn_builder.file_builder) { + : builder::BasicBlockBuilder(fn_builder.file_builder), fn_builder(fn_builder), file_builder(fn_builder.file_builder) { label = file_builder.generate_fresh_id(); } -FnBuilder::FnBuilder(FileBuilder& file_builder) : builder::SpvFnBuilder(file_builder), file_builder(file_builder) {} +FnBuilder::FnBuilder(FileBuilder& file_builder) : builder::FnBuilder(file_builder), file_builder(file_builder) {} -FileBuilder::FileBuilder(CodeGen* cg) : builder::SpvFileBuilder(), cg(cg) { +FileBuilder::FileBuilder(CodeGen* cg) : builder::FileBuilder(), cg(cg) { } -SpvId FileBuilder::u32_t() { +Id FileBuilder::u32_t() { if (u32_t_ == 0) u32_t_ = cg->convert(cg->world().type_pu32()).id; return u32_t_; } -SpvId FileBuilder::u32_constant(uint32_t pattern) { +Id FileBuilder::u32_constant(uint32_t pattern) { return constant(u32_t(), { pattern }); } @@ -135,7 +135,7 @@ void CodeGen::emit_stream(std::ostream& out) { continue; assert(defs_.contains(cont)); - SpvId callee = defs_[cont]; + Id callee = defs_[cont]; auto block = config->second->as()->block_size(); std::vector local_size = { @@ -158,7 +158,7 @@ void CodeGen::emit_stream(std::ostream& out) { builder_ = nullptr; } -SpvId CodeGen::emit_fun_decl(thorin::Continuation* continuation) { +Id CodeGen::emit_fun_decl(thorin::Continuation* continuation) { return get_fn_builder(continuation).function_id; } @@ -263,9 +263,9 @@ void CodeGen::finalize(const thorin::Scope&) { builder_->define_function(*builder_->current_fn_); } -SpvId CodeGen::get_codom_type(const Continuation* fn) { +Id CodeGen::get_codom_type(const Continuation* fn) { auto ret_cont_type = fn->ret_param()->type()->as(); - std::vector types; + std::vector types; for (auto& op : ret_cont_type->types()) { if (op->isa() || is_type_unit(op->type())) continue; @@ -279,7 +279,7 @@ SpvId CodeGen::get_codom_type(const Continuation* fn) { return builder_->declare_struct_type(types); } -SpvId CodeGen::emit_as_bb(thorin::Continuation* cont) { +Id CodeGen::emit_as_bb(thorin::Continuation* cont) { emit(cont); return cont2bb_[cont]->label; } @@ -288,7 +288,7 @@ void CodeGen::emit_epilogue(Continuation* continuation) { BasicBlockBuilder* bb = cont2bb_[continuation]; // Handles the potential nuances of jumping to another continuation - auto jump_to_next_cont_with_args = [&](Continuation* succ, std::vector args) { + auto jump_to_next_cont_with_args = [&](Continuation* succ, std::vector args) { assert(succ->is_basicblock()); BasicBlockBuilder* dstbb = cont2bb_[succ]; @@ -314,7 +314,7 @@ void CodeGen::emit_epilogue(Continuation* continuation) { auto& app = *continuation->body(); if (app.callee() == entry_->ret_param()) { - std::vector values; + std::vector values; for (auto arg : app.args()) { assert(arg->order() == 0); @@ -357,8 +357,8 @@ void CodeGen::emit_epilogue(Continuation* continuation) { emit_unsafe(app.arg(0)); auto val = emit(app.arg(1)); auto otherwise_bb = emit_as_bb(app.arg(2)->isa_nom()); - std::vector literals; - std::vector cases; + std::vector literals; + std::vector cases; for (size_t i = 3; i < app.num_args(); i++) { auto arg = app.arg(i)->as(); literals.push_back(emit(arg->op(0))); @@ -377,7 +377,7 @@ void CodeGen::emit_epilogue(Continuation* continuation) { jump_to_next_cont_with_args(succ, productions); } else { // function/closure call // put all first-order args into an array - std::vector call_args; + std::vector call_args; const Def* ret_arg = nullptr; for (auto arg : app.args()) { if (arg->order() == 0) { @@ -394,7 +394,7 @@ void CodeGen::emit_epilogue(Continuation* continuation) { } } - SpvId call_result; + Id call_result; if (auto called_continuation = app.callee()->isa_nom()) { auto ret_type = get_codom_type(called_continuation); call_result = bb->call(ret_type, emit(called_continuation), call_args); @@ -419,7 +419,7 @@ void CodeGen::emit_epilogue(Continuation* continuation) { real_params_count++; } - std::vector args(real_params_count); + std::vector args(real_params_count); if (real_params_count == 1) { args[0] = call_result; @@ -441,11 +441,11 @@ void CodeGen::emit_epilogue(Continuation* continuation) { static_assert(sizeof(double) == sizeof(uint64_t), "This code assumes 64-bit double"); -SpvId CodeGen::emit_constant(const thorin::Def* def) { +Id CodeGen::emit_constant(const thorin::Def* def) { if (auto primlit = def->isa()) { Box box = primlit->value(); auto type = convert(def->type()).id; - SpvId constant; + Id constant; switch (primlit->primtype_tag()) { case PrimType_bool: constant = builder_->bool_constant(type, box.get_bool()); break; case PrimType_ps8: case PrimType_qs8: @@ -472,14 +472,14 @@ SpvId CodeGen::emit_constant(const thorin::Def* def) { assertf(false, "Incomplete emit(def) definition"); } -SpvId CodeGen::emit_bb(BasicBlockBuilder* bb, const Def* def) { +Id CodeGen::emit_bb(BasicBlockBuilder* bb, const Def* def) { if (auto mathop = def->isa()) return emit_mathop(bb, *mathop); if (auto bin = def->isa()) { - SpvId lhs = emit(bin->lhs()); - SpvId rhs = emit(bin->rhs()); - SpvId result_type = convert(def->type()).id; + Id lhs = emit(bin->lhs()); + Id rhs = emit(bin->rhs()); + Id result_type = convert(def->type()).id; if (auto cmp = bin->isa()) { auto type = cmp->lhs()->type(); @@ -618,7 +618,7 @@ SpvId CodeGen::emit_bb(BasicBlockBuilder* bb, const Def* def) { auto value = emit(vindex->op(0)); return bb->extract(convert(world().type_pu32()).id, value, { 0 }); } else if (auto tuple = def->isa()) { - std::vector elements; + std::vector elements; elements.resize(tuple->num_ops()); size_t x = 0; for (auto& e : tuple->ops()) { @@ -626,7 +626,7 @@ SpvId CodeGen::emit_bb(BasicBlockBuilder* bb, const Def* def) { } return bb->composite(convert(tuple->type()).id, elements); } else if (auto structagg = def->isa()) { - std::vector elements; + std::vector elements; elements.resize(structagg->num_ops()); size_t x = 0; for (auto& e : structagg->ops()) { @@ -677,7 +677,7 @@ SpvId CodeGen::emit_bb(BasicBlockBuilder* bb, const Def* def) { bool mem = false; if (auto tt = aggop->agg()->type()->isa(); tt && tt->op(0)->isa()) mem = true; - auto copy_to_alloca = [&] (SpvId spv_agg, SpvId target_type) { + auto copy_to_alloca = [&] (Id spv_agg, Id target_type) { world().wdef(def, "slow: alloca and loads/stores needed for aggregate '{}'", def); auto agg_ptr_type = builder_->declare_ptr_type(spv::StorageClassFunction, agg_type); @@ -770,7 +770,7 @@ SpvId CodeGen::emit_bb(BasicBlockBuilder* bb, const Def* def) { size_t src_bitwidth = conv_src_type.layout->size * 8; size_t dst_bitwidth = conv_dst_type.layout->size * 8; - SpvId data = emit(cast->from()); + Id data = emit(cast->from()); // If floating point is involved (src or dst), OpConvert*ToF and OpConvertFTo* can take care of the bit width transformation so no need for any chopping/expanding if (src_kind == PrimTypeKind::Float || dst_kind == PrimTypeKind::Float) { @@ -840,10 +840,10 @@ SpvId CodeGen::emit_bb(BasicBlockBuilder* bb, const Def* def) { assertf(false, "Incomplete emit(def) definition"); } -std::vector CodeGen::emit_intrinsic(const App& app, const Continuation* intrinsic, BasicBlockBuilder* bb) { - std::vector productions; +std::vector CodeGen::emit_intrinsic(const App& app, const Continuation* intrinsic, BasicBlockBuilder* bb) { + std::vector productions; if (intrinsic->name() == "spirv.nonsemantic.printf") { - std::vector args; + std::vector args; auto string = app.arg(1); if (auto arr_type = string->type()->isa(); arr_type->elem_type() == world().type_pu8()) { auto arr = string->as(); diff --git a/src/thorin/be/spirv/spirv.h b/src/thorin/be/spirv/spirv.h index 44dfa9685..fe0c70c78 100644 --- a/src/thorin/be/spirv/spirv.h +++ b/src/thorin/be/spirv/spirv.h @@ -7,7 +7,7 @@ namespace thorin::spirv { -using SpvId = uint32_t; +using Id = uint32_t; class CodeGen; @@ -29,7 +29,7 @@ struct Target { }; struct ConvertedType { - SpvId id; + Id id; struct Layout { size_t size, alignment; }; @@ -38,21 +38,21 @@ struct ConvertedType { struct BasicBlockBuilder; -class CodeGen : public thorin::CodeGen, public thorin::Emitter { +class CodeGen : public thorin::CodeGen, public thorin::Emitter { public: CodeGen(Thorin& thorin, Target&, bool debug, const Cont2Config* = nullptr); void emit_stream(std::ostream& stream) override; const char* file_ext() const override { return ".spv"; } - bool is_valid(SpvId id) { + bool is_valid(Id id) { return id > 0; } uint32_t convert(AddrSpace); ConvertedType convert(const Type*); - SpvId emit_fun_decl(Continuation*); + Id emit_fun_decl(Continuation*); FnBuilder* prepare(const Scope&); void prepare(Continuation*, FnBuilder*); @@ -60,16 +60,16 @@ class CodeGen : public thorin::CodeGen, public thorin::Emitter emit_intrinsic(const App& app, const Continuation* intrinsic, BasicBlockBuilder* bb); + std::vector emit_intrinsic(const App& app, const Continuation* intrinsic, BasicBlockBuilder* bb); - SpvId emit_as_bb(Continuation*); - SpvId emit_mathop(BasicBlockBuilder* bb, const MathOp& op); + Id emit_as_bb(Continuation*); + Id emit_mathop(BasicBlockBuilder* bb, const MathOp& op); - SpvId get_codom_type(const Continuation* fn); + Id get_codom_type(const Continuation* fn); Target target_info_; FileBuilder* builder_; diff --git a/src/thorin/be/spirv/spirv_builder.hpp b/src/thorin/be/spirv/spirv_builder.hpp index f1fc3e82e..aca443356 100644 --- a/src/thorin/be/spirv/spirv_builder.hpp +++ b/src/thorin/be/spirv/spirv_builder.hpp @@ -11,12 +11,12 @@ namespace thorin::spirv::builder { //struct SpvId { uint32_t id; }; -using SpvId = uint32_t; +using Id = uint32_t; -struct SpvSectionBuilder; -struct SpvBasicBlockBuilder; -struct SpvFnBuilder; -struct SpvFileBuilder; +struct SectionBuilder; +struct BasicBlockBuilder; +struct FnBuilder; +struct FileBuilder; struct ExtendedInstruction { const char* set_name; @@ -30,7 +30,7 @@ inline int div_roundup(int a, int b) { return (a / b) + 1; } -struct SpvSectionBuilder { +struct SectionBuilder { std::vector data_; private: @@ -44,7 +44,7 @@ struct SpvSectionBuilder { output_word(lower | upper); } - void ref_id(SpvId id) { + void ref_id(Id id) { assert(id != 0); output_word(id); } @@ -69,9 +69,7 @@ struct SpvSectionBuilder { } }; - - -struct SpvFileBuilder { +struct FileBuilder { enum UniqueDeclTag { NONE, FN_TYPE, @@ -100,26 +98,26 @@ struct SpvFileBuilder { } }; - SpvFileBuilder() {} - SpvFileBuilder(const SpvFileBuilder&) = delete; + FileBuilder() {} + FileBuilder(const FileBuilder&) = delete; - SpvId generate_fresh_id() { return { bound++ }; } + Id generate_fresh_id() { return { bound++ }; } - void name(SpvId id, std::string_view str) { + void name(Id id, std::string_view str) { assert(id < bound); debug_names.begin_op(spv::Op::OpName, 2 + div_roundup(str.size() + 1, 4)); debug_names.ref_id(id); debug_names.literal_name(str); } - SpvId declare_bool_type() { + Id declare_bool_type() { types_constants.begin_op(spv::Op::OpTypeBool, 2); auto id = generate_fresh_id(); types_constants.ref_id(id); return id; } - SpvId declare_int_type(int width, bool signed_) { + Id declare_int_type(int width, bool signed_) { types_constants.begin_op(spv::Op::OpTypeInt, 4); auto id = generate_fresh_id(); types_constants.ref_id(id); @@ -128,7 +126,7 @@ struct SpvFileBuilder { return id; } - SpvId declare_float_type(int width) { + Id declare_float_type(int width) { types_constants.begin_op(spv::Op::OpTypeFloat, 3); auto id = generate_fresh_id(); types_constants.ref_id(id); @@ -136,7 +134,7 @@ struct SpvFileBuilder { return id; } - SpvId declare_ptr_type(spv::StorageClass storage_class, SpvId element_type) { + Id declare_ptr_type(spv::StorageClass storage_class, Id element_type) { auto key = UniqueDeclKey { PTR_TYPE, { element_type, (uint32_t) storage_class } }; if (auto iter = unique_decls.find(key); iter != unique_decls.end()) return iter->second; types_constants.begin_op(spv::Op::OpTypePointer, 4); @@ -148,7 +146,7 @@ struct SpvFileBuilder { return id; } - SpvId declare_array_type(SpvId element_type, SpvId dim) { + Id declare_array_type(Id element_type, Id dim) { auto key = UniqueDeclKey { DEF_ARR_TYPE, { element_type, dim } }; if (auto iter = unique_decls.find(key); iter != unique_decls.end()) return iter->second; types_constants.begin_op(spv::Op::OpTypeArray, 4); @@ -160,7 +158,7 @@ struct SpvFileBuilder { return id; } - SpvId declare_fn_type(std::vector dom, SpvId codom) { + Id declare_fn_type(std::vector dom, Id codom) { auto key = UniqueDeclKey { FN_TYPE, {} }; for (auto d : dom) key.members.push_back(d); key.members.push_back(codom); @@ -176,7 +174,7 @@ struct SpvFileBuilder { return id; } - SpvId declare_struct_type(std::vector elements) { + Id declare_struct_type(std::vector elements) { types_constants.begin_op(spv::Op::OpTypeStruct, 2 + elements.size()); auto id = generate_fresh_id(); types_constants.ref_id(id); @@ -185,7 +183,7 @@ struct SpvFileBuilder { return id; } - SpvId declare_vector_type(SpvId component_type, uint32_t dim) { + Id declare_vector_type(Id component_type, uint32_t dim) { types_constants.begin_op(spv::Op::OpTypeVector, 4); auto id = generate_fresh_id(); types_constants.ref_id(id); @@ -194,7 +192,7 @@ struct SpvFileBuilder { return id; } - void decorate(SpvId target, spv::Decoration decoration, std::vector extra = {}) { + void decorate(Id target, spv::Decoration decoration, std::vector extra = {}) { annotations.begin_op(spv::Op::OpDecorate, 3 + extra.size()); annotations.ref_id(target); annotations.literal_int(decoration); @@ -202,7 +200,7 @@ struct SpvFileBuilder { annotations.literal_int(e); } - void decorate_member(SpvId target, uint32_t member, spv::Decoration decoration, std::vector extra = {}) { + void decorate_member(Id target, uint32_t member, spv::Decoration decoration, std::vector extra = {}) { annotations.begin_op(spv::Op::OpMemberDecorate, 4 + extra.size()); annotations.ref_id(target); annotations.literal_int(member); @@ -211,7 +209,7 @@ struct SpvFileBuilder { annotations.literal_int(e); } - SpvId debug_string(std::string string) { + Id debug_string(std::string string) { debug_string_source.begin_op(spv::Op::OpString, 2 + div_roundup(string.size() + 1, 4)); auto id = generate_fresh_id(); debug_string_source.ref_id(id); @@ -219,7 +217,7 @@ struct SpvFileBuilder { return id; } - SpvId bool_constant(SpvId type, bool value) { + Id bool_constant(Id type, bool value) { types_constants.begin_op(value ? spv::Op::OpConstantTrue : spv::Op::OpConstantFalse, 3); auto id = generate_fresh_id(); types_constants.ref_id(type); @@ -227,7 +225,7 @@ struct SpvFileBuilder { return id; } - SpvId constant(SpvId type, std::vector bit_pattern) { + Id constant(Id type, std::vector bit_pattern) { auto key = UniqueDeclKey { CONSTANT, bit_pattern }; key.members.push_back(type); if (auto iter = unique_decls.find(key); iter != unique_decls.end()) return iter->second; @@ -241,7 +239,7 @@ struct SpvFileBuilder { return id; } - SpvId constant_composite(SpvId type, std::vector ops) { + Id constant_composite(Id type, std::vector ops) { auto key = UniqueDeclKey { CONSTANT_COMPOSITE, {} }; key.members.push_back(type); for (auto op : ops) key.members.push_back(op); @@ -256,7 +254,7 @@ struct SpvFileBuilder { return id; } - SpvId variable(SpvId type, spv::StorageClass storage_class) { + Id variable(Id type, spv::StorageClass storage_class) { types_constants.begin_op(spv::Op::OpVariable, 4); types_constants.ref_id(type); auto id = generate_fresh_id(); @@ -265,16 +263,16 @@ struct SpvFileBuilder { return id; } - SpvId declare_void_type() { + Id declare_void_type() { types_constants.begin_op(spv::Op::OpTypeVoid, 2); auto id = generate_fresh_id(); types_constants.ref_id(id); return id; } - SpvId define_function(SpvFnBuilder& fn_builder); + Id define_function(FnBuilder& fn_builder); - void declare_entry_point(spv::ExecutionModel execution_model, SpvId entry_point, std::string name, std::vector interface) { + void declare_entry_point(spv::ExecutionModel execution_model, Id entry_point, std::string name, std::vector interface) { entry_points.begin_op(spv::Op::OpEntryPoint, 3 + div_roundup(name.size() + 1, 4) + interface.size()); entry_points.literal_int(execution_model); entry_points.ref_id(entry_point); @@ -283,7 +281,7 @@ struct SpvFileBuilder { entry_points.ref_id(i); } - void execution_mode(SpvId entry_point, spv::ExecutionMode execution_mode, std::vector payloads) { + void execution_mode(Id entry_point, spv::ExecutionMode execution_mode, std::vector payloads) { entry_points.begin_op(spv::Op::OpExecutionMode, 3 + payloads.size()); entry_points.ref_id(entry_point); entry_points.literal_int(execution_mode); @@ -315,7 +313,7 @@ struct SpvFileBuilder { spv::MemoryModel memory_model = spv::MemoryModel::MemoryModelSimple; protected: - SpvId extended_import(std::string name) { + Id extended_import(std::string name) { auto found = extended_instruction_sets.find(name); if (found != extended_instruction_sets.end()) return found->second; @@ -332,22 +330,22 @@ struct SpvFileBuilder { uint32_t bound = 1; // Ordered as per https://www.khronos.org/registry/spir-v/specs/unified1/SPIRV.pdf#subsection.2.4 - SpvSectionBuilder capabilities; - SpvSectionBuilder extensions; - SpvSectionBuilder ext_inst_import; - SpvSectionBuilder entry_points; - SpvSectionBuilder execution_modes; - SpvSectionBuilder debug_string_source; - SpvSectionBuilder debug_names; - SpvSectionBuilder debug_module_processed; - SpvSectionBuilder annotations; - SpvSectionBuilder types_constants; - SpvSectionBuilder fn_decls; - SpvSectionBuilder fn_defs; + SectionBuilder capabilities; + SectionBuilder extensions; + SectionBuilder ext_inst_import; + SectionBuilder entry_points; + SectionBuilder execution_modes; + SectionBuilder debug_string_source; + SectionBuilder debug_names; + SectionBuilder debug_module_processed; + SectionBuilder annotations; + SectionBuilder types_constants; + SectionBuilder fn_decls; + SectionBuilder fn_defs; // SPIR-V disallows duplicate non-aggregate type declarations, we protect against these with this - std::unordered_map unique_decls; - std::unordered_map extended_instruction_sets; + std::unordered_map unique_decls; + std::unordered_map extended_instruction_sets; std::unordered_set capabilities_set; std::unordered_set extensions_set; @@ -358,7 +356,7 @@ struct SpvFileBuilder { output_->put((word >> 24) & 0xFFu); } - void output_section(SpvSectionBuilder& section) { + void output_section(SectionBuilder& section) { for (auto& word : section.data_) { output_word_le(word); } @@ -366,7 +364,7 @@ struct SpvFileBuilder { public: void finish(std::ostream& output) { output_ = &output; - SpvSectionBuilder memory_model_section; + SectionBuilder memory_model_section; memory_model_section.begin_op(spv::Op::OpMemoryModel, 3); memory_model_section.data_.push_back(addressing_model); memory_model_section.data_.push_back(memory_model); @@ -392,25 +390,25 @@ struct SpvFileBuilder { output_section(fn_defs); } - friend SpvBasicBlockBuilder; + friend BasicBlockBuilder; }; -struct SpvBasicBlockBuilder : public SpvSectionBuilder { - explicit SpvBasicBlockBuilder(SpvFileBuilder& file_builder) +struct BasicBlockBuilder : public SectionBuilder { + explicit BasicBlockBuilder(FileBuilder& file_builder) : file_builder(file_builder) {} - SpvFileBuilder& file_builder; + FileBuilder& file_builder; struct Phi { - SpvId type; - SpvId value; - std::vector> preds; + Id type; + Id value; + std::vector> preds; }; std::vector phis; - SpvId label; + Id label; - SpvId undef(SpvId type) { + Id undef(Id type) { begin_op(spv::Op::OpUndef, 3); ref_id(type); auto id = file_builder.generate_fresh_id(); @@ -418,7 +416,7 @@ struct SpvBasicBlockBuilder : public SpvSectionBuilder { return id; } - SpvId composite(SpvId aggregate_t, std::vector& elements) { + Id composite(Id aggregate_t, std::vector& elements) { begin_op(spv::Op::OpCompositeConstruct, 3 + elements.size()); ref_id(aggregate_t); auto id = file_builder.generate_fresh_id(); @@ -428,7 +426,7 @@ struct SpvBasicBlockBuilder : public SpvSectionBuilder { return id; } - SpvId extract(SpvId target_type, SpvId composite, std::vector indices) { + Id extract(Id target_type, Id composite, std::vector indices) { begin_op(spv::Op::OpCompositeExtract, 4 + indices.size()); ref_id(target_type); auto id = file_builder.generate_fresh_id(); @@ -439,7 +437,7 @@ struct SpvBasicBlockBuilder : public SpvSectionBuilder { return id; } - SpvId insert(SpvId target_type, SpvId object, SpvId composite, std::vector indices) { + Id insert(Id target_type, Id object, Id composite, std::vector indices) { begin_op(spv::Op::OpCompositeInsert, 5 + indices.size()); ref_id(target_type); auto id = file_builder.generate_fresh_id(); @@ -451,7 +449,7 @@ struct SpvBasicBlockBuilder : public SpvSectionBuilder { return id; } - SpvId vector_extract_dynamic(SpvId target_type, SpvId vector, SpvId index) { + Id vector_extract_dynamic(Id target_type, Id vector, Id index) { begin_op(spv::Op::OpVectorExtractDynamic, 5); ref_id(target_type); auto id = file_builder.generate_fresh_id(); @@ -461,7 +459,7 @@ struct SpvBasicBlockBuilder : public SpvSectionBuilder { return id; } - SpvId vector_insert_dynamic(SpvId target_type, SpvId vector, SpvId component, SpvId index) { + Id vector_insert_dynamic(Id target_type, Id vector, Id component, Id index) { begin_op(spv::Op::OpVectorInsertDynamic, 6); ref_id(target_type); auto id = file_builder.generate_fresh_id(); @@ -473,7 +471,7 @@ struct SpvBasicBlockBuilder : public SpvSectionBuilder { } // Used for almost all conversion operations - SpvId convert(spv::Op op_, SpvId target_type, SpvId value) { + Id convert(spv::Op op_, Id target_type, Id value) { begin_op(op_, 4); auto id = file_builder.generate_fresh_id(); ref_id(target_type); @@ -482,7 +480,7 @@ struct SpvBasicBlockBuilder : public SpvSectionBuilder { return id; } - SpvId access_chain(SpvId target_type, SpvId element, std::vector indexes) { + Id access_chain(Id target_type, Id element, std::vector indexes) { begin_op(spv::Op::OpAccessChain, 4 + indexes.size()); auto id = file_builder.generate_fresh_id(); ref_id(target_type); @@ -493,7 +491,7 @@ struct SpvBasicBlockBuilder : public SpvSectionBuilder { return id; } - SpvId ptr_access_chain(SpvId target_type, SpvId base, SpvId element, std::vector indexes) { + Id ptr_access_chain(Id target_type, Id base, Id element, std::vector indexes) { begin_op(spv::Op::OpPtrAccessChain, 5 + indexes.size()); auto id = file_builder.generate_fresh_id(); ref_id(target_type); @@ -505,7 +503,7 @@ struct SpvBasicBlockBuilder : public SpvSectionBuilder { return id; } - SpvId load(SpvId target_type, SpvId pointer, std::vector operands = {}) { + Id load(Id target_type, Id pointer, std::vector operands = {}) { begin_op(spv::Op::OpLoad, 4 + operands.size()); auto id = file_builder.generate_fresh_id(); ref_id(target_type); @@ -516,7 +514,7 @@ struct SpvBasicBlockBuilder : public SpvSectionBuilder { return id; } - void store(SpvId value, SpvId pointer, std::vector operands = {}) { + void store(Id value, Id pointer, std::vector operands = {}) { begin_op(spv::Op::OpStore, 3 + operands.size()); ref_id(pointer); ref_id(value); @@ -524,7 +522,7 @@ struct SpvBasicBlockBuilder : public SpvSectionBuilder { literal_int(op); } - SpvId binop(spv::Op op_, SpvId result_type, SpvId lhs, SpvId rhs) { + Id binop(spv::Op op_, Id result_type, Id lhs, Id rhs) { begin_op(op_, 5); auto id = file_builder.generate_fresh_id(); ref_id(result_type); @@ -534,19 +532,19 @@ struct SpvBasicBlockBuilder : public SpvSectionBuilder { return id; } - void branch(SpvId target) { + void branch(Id target) { begin_op(spv::Op::OpBranch, 2); ref_id(target); } - void branch_conditional(SpvId condition, SpvId true_target, SpvId false_target) { + void branch_conditional(Id condition, Id true_target, Id false_target) { begin_op(spv::Op::OpBranchConditional, 4); ref_id(condition); ref_id(true_target); ref_id(false_target); } - void branch_switch(SpvId selector, SpvId default_case, std::vector literals, std::vector cases) { + void branch_switch(Id selector, Id default_case, std::vector literals, std::vector cases) { assert(literals.size() == cases.size()); begin_op(spv::Op::OpSwitch, 3 + literals.size() * 2); ref_id(selector); @@ -557,13 +555,13 @@ struct SpvBasicBlockBuilder : public SpvSectionBuilder { } } - void selection_merge(SpvId merge_bb, spv::SelectionControlMask selection_control) { + void selection_merge(Id merge_bb, spv::SelectionControlMask selection_control) { begin_op(spv::Op::OpSelectionMerge, 3); ref_id(merge_bb); literal_int(selection_control); } - void loop_merge(SpvId merge_bb, SpvId continue_bb, spv::LoopControlMask loop_control, std::vector loop_control_ops) { + void loop_merge(Id merge_bb, Id continue_bb, spv::LoopControlMask loop_control, std::vector loop_control_ops) { begin_op(spv::Op::OpLoopMerge, 4 + loop_control_ops.size()); ref_id(merge_bb); ref_id(continue_bb); @@ -573,7 +571,7 @@ struct SpvBasicBlockBuilder : public SpvSectionBuilder { literal_int(e); } - SpvId call(SpvId return_type, SpvId callee, std::vector arguments) { + Id call(Id return_type, Id callee, std::vector arguments) { begin_op(spv::Op::OpFunctionCall, 4 + arguments.size()); auto id = file_builder.generate_fresh_id(); ref_id(return_type); @@ -585,13 +583,13 @@ struct SpvBasicBlockBuilder : public SpvSectionBuilder { return id; } - SpvId ext_instruction(SpvId return_type, ExtendedInstruction instr, std::vector arguments); + Id ext_instruction(Id return_type, ExtendedInstruction instr, std::vector arguments); void return_void() { begin_op(spv::Op::OpReturn, 1); } - void return_value(SpvId value) { + void return_value(Id value) { begin_op(spv::Op::OpReturnValue, 2); ref_id(value); } @@ -601,7 +599,7 @@ struct SpvBasicBlockBuilder : public SpvSectionBuilder { } protected: - SpvId ext_instruction(SpvId return_type, SpvId set, uint32_t instruction, std::vector arguments) { + Id ext_instruction(Id return_type, Id set, uint32_t instruction, std::vector arguments) { begin_op(spv::Op::OpExtInst, 5 + arguments.size()); auto id = file_builder.generate_fresh_id(); ref_id(return_type); @@ -614,26 +612,26 @@ struct SpvBasicBlockBuilder : public SpvSectionBuilder { } }; -struct SpvFnBuilder { - explicit SpvFnBuilder(SpvFileBuilder& file_builder) +struct FnBuilder { + explicit FnBuilder(FileBuilder& file_builder) : file_builder(file_builder) { function_id = file_builder.generate_fresh_id(); } - SpvFileBuilder& file_builder; - SpvId function_id; + FileBuilder& file_builder; + Id function_id; - SpvId fn_type; - SpvId fn_ret_type; - std::vector bbs_to_emit; + Id fn_type; + Id fn_ret_type; + std::vector bbs_to_emit; // Contains OpFunctionParams - SpvSectionBuilder header; + SectionBuilder header; - SpvSectionBuilder variables; + SectionBuilder variables; - SpvId parameter(SpvId param_type) { + Id parameter(Id param_type) { header.begin_op(spv::Op::OpFunctionParameter, 3); auto id = file_builder.generate_fresh_id(); header.ref_id(param_type); @@ -641,7 +639,7 @@ struct SpvFnBuilder { return id; } - SpvId variable(SpvId type, spv::StorageClass storage_class) { + Id variable(Id type, spv::StorageClass storage_class) { variables.begin_op(spv::Op::OpVariable, 4); variables.ref_id(type); auto id = file_builder.generate_fresh_id(); @@ -651,7 +649,7 @@ struct SpvFnBuilder { } }; -inline SpvId SpvFileBuilder::define_function(SpvFnBuilder &fn_builder) { +inline Id FileBuilder::define_function(FnBuilder &fn_builder) { fn_defs.begin_op(spv::Op::OpFunction, 5); fn_defs.ref_id(fn_builder.fn_ret_type); fn_defs.ref_id(fn_builder.function_id); @@ -692,7 +690,7 @@ inline SpvId SpvFileBuilder::define_function(SpvFnBuilder &fn_builder) { return fn_builder.function_id; } -inline SpvId SpvBasicBlockBuilder::ext_instruction(SpvId return_type, ExtendedInstruction instr, std::vector arguments) { +inline Id BasicBlockBuilder::ext_instruction(Id return_type, ExtendedInstruction instr, std::vector arguments) { return ext_instruction(return_type, file_builder.extended_import(instr.set_name), instr.id, arguments); } diff --git a/src/thorin/be/spirv/spirv_instructions.cpp b/src/thorin/be/spirv/spirv_instructions.cpp index 4a9b53104..484e94a73 100644 --- a/src/thorin/be/spirv/spirv_instructions.cpp +++ b/src/thorin/be/spirv/spirv_instructions.cpp @@ -35,11 +35,11 @@ SpirMathOps opencl_std = { .log10 = { "OpenCL.std", OpenCLLIB::Log10 }, }; -SpvId CodeGen::emit_mathop(BasicBlockBuilder* bb, const thorin::MathOp& mathop) { +Id CodeGen::emit_mathop(BasicBlockBuilder* bb, const thorin::MathOp& mathop) { auto type = mathop.type(); SpirMathOps& impl = opencl_std; - std::vector ops; + std::vector ops; for (auto& op : mathop.ops()) { ops.push_back(emit(op)); } diff --git a/src/thorin/be/spirv/spirv_private.h b/src/thorin/be/spirv/spirv_private.h index d36d4e81a..ae8f03d51 100644 --- a/src/thorin/be/spirv/spirv_private.h +++ b/src/thorin/be/spirv/spirv_private.h @@ -7,7 +7,7 @@ namespace thorin::spirv { -struct BasicBlockBuilder : public builder::SpvBasicBlockBuilder { +struct BasicBlockBuilder : public builder::BasicBlockBuilder { explicit BasicBlockBuilder(FnBuilder& fn_builder); BasicBlockBuilder(const BasicBlockBuilder&) = delete; @@ -19,17 +19,17 @@ struct BasicBlockBuilder : public builder::SpvBasicBlockBuilder { bool semi_inline; }; -struct FnBuilder : public builder::SpvFnBuilder { +struct FnBuilder : public builder::FnBuilder { explicit FnBuilder(FileBuilder& file_builder); FnBuilder(const FnBuilder&) = delete; FileBuilder& file_builder; std::vector> bbs; - DefMap params; + DefMap params; }; -struct FileBuilder : public builder::SpvFileBuilder { +struct FileBuilder : public builder::FileBuilder { explicit FileBuilder(CodeGen* cg); FileBuilder(const FileBuilder&) = delete; @@ -37,14 +37,14 @@ struct FileBuilder : public builder::SpvFileBuilder { FnBuilder* current_fn_ = nullptr; ContinuationMap> fn_builders_; - std::unordered_map builtins_; - std::vector interface; + std::unordered_map builtins_; + std::vector interface; - SpvId u32_t(); - SpvId u32_constant(uint32_t); + Id u32_t(); + Id u32_constant(uint32_t); private: - SpvId u32_t_ { 0 }; + Id u32_t_ { 0 }; }; } diff --git a/src/thorin/be/spirv/spirv_types.cpp b/src/thorin/be/spirv/spirv_types.cpp index 90a718697..f3d767a89 100644 --- a/src/thorin/be/spirv/spirv_types.cpp +++ b/src/thorin/be/spirv/spirv_types.cpp @@ -164,15 +164,15 @@ ConvertedType CodeGen::convert(const Type* type) { case Node_FnType: { // extract "return" type, collect all other types auto fn = type->as(); - SpvId ret = 0; - std::vector ops; + Id ret = 0; + std::vector ops; for (auto op : fn->types()) { if (op->isa() || op == world().unit_type()) continue; auto fn_type = op->isa(); if (fn_type && !op->isa()) { assert(!ret && "only one 'return' supported"); - std::vector ret_types; + std::vector ret_types; for (auto fn_op : fn_type->types()) { if (fn_op->isa() || fn_op == world().unit_type()) continue; @@ -197,7 +197,7 @@ ConvertedType CodeGen::convert(const Type* type) { case Node_StructType: case Node_TupleType: { - std::vector spv_types; + std::vector spv_types; size_t total_serialized_size = 0; converted.layout = { 0, 0 }; for (auto member : type->ops()) { @@ -223,7 +223,7 @@ ConvertedType CodeGen::convert(const Type* type) { case Node_VariantType: { assert(type->num_ops() > 0 && "empty variants not supported"); auto tag_type = world().type_pu32(); - SpvId converted_tag_type = convert(tag_type).id; + Id converted_tag_type = convert(tag_type).id; size_t max_serialized_size = 0; for (auto member : type->as()->types()) { From 90261bbaf645a6cc1365a2698df492466af55458 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Mon, 4 Nov 2024 19:47:24 +0100 Subject: [PATCH 306/342] spirv_builder.hpp: move terminators into their own section --- src/thorin/be/spirv/spirv.cpp | 18 ++-- src/thorin/be/spirv/spirv_builder.hpp | 113 ++++++++++++++------------ 2 files changed, 71 insertions(+), 60 deletions(-) diff --git a/src/thorin/be/spirv/spirv.cpp b/src/thorin/be/spirv/spirv.cpp index b8877a567..63a60211c 100644 --- a/src/thorin/be/spirv/spirv.cpp +++ b/src/thorin/be/spirv/spirv.cpp @@ -308,7 +308,7 @@ void CodeGen::emit_epilogue(Continuation* continuation) { } j++; } - bb->branch(emit(succ)); + bb->terminator.branch(emit(succ)); }; auto& app = *continuation->body(); @@ -327,9 +327,9 @@ void CodeGen::emit_epilogue(Continuation* continuation) { } switch (values.size()) { - case 0: bb->return_void(); break; - case 1: bb->return_value(values[0]); break; - default: bb->return_value(bb->composite(builder_->current_fn_->fn_ret_type, values)); + case 0: bb->terminator.return_void(); break; + case 1: bb->terminator.return_value(values[0]); break; + default: bb->terminator.return_value(bb->composite(builder_->current_fn_->fn_ret_type, values)); } } else if (auto dst_cont = app.callee()->isa_nom(); dst_cont && dst_cont->is_basicblock()) { // ordinary jump int index = -1; @@ -344,7 +344,7 @@ void CodeGen::emit_epilogue(Continuation* continuation) { auto& phi = cont2bb_[dst_cont]->phis_map[param]; phi.preds.emplace_back(val, emit_as_bb(continuation)); } - bb->branch(emit(dst_cont)); + bb->terminator.branch(emit(dst_cont)); } else if (app.callee() == world().branch()) { auto mem = app.arg(0); emit_unsafe(mem); @@ -352,7 +352,7 @@ void CodeGen::emit_epilogue(Continuation* continuation) { auto cond = emit(app.arg(1)); auto tbb = emit(app.arg(2)); auto fbb = emit(app.arg(3)); - bb->branch_conditional(cond, tbb, fbb); + bb->terminator.branch_conditional(cond, tbb, fbb); } else if (app.callee()->isa() && app.callee()->as()->intrinsic() == Intrinsic::Match) { emit_unsafe(app.arg(0)); auto val = emit(app.arg(1)); @@ -364,9 +364,9 @@ void CodeGen::emit_epilogue(Continuation* continuation) { literals.push_back(emit(arg->op(0))); cases.push_back(emit_as_bb(arg->op(1)->as_nom())); } - bb->branch_switch(val, otherwise_bb, literals, cases); + bb->terminator.branch_switch(val, otherwise_bb, literals, cases); } else if (app.callee()->isa()) { - bb->unreachable(); + bb->terminator.unreachable(); } else if (auto intrinsic = app.callee()->isa_nom(); intrinsic && (intrinsic->is_intrinsic() || intrinsic->cc() == CC::Device)) { // Ensure we emit previous memory operations assert(is_mem(app.arg(0))); @@ -432,7 +432,7 @@ void CodeGen::emit_epilogue(Continuation* continuation) { j++; } - bb->branch(emit(succ)); + bb->terminator.branch(emit(succ)); } jump_to_next_cont_with_args(succ, args); diff --git a/src/thorin/be/spirv/spirv_builder.hpp b/src/thorin/be/spirv/spirv_builder.hpp index aca443356..0ee6e8773 100644 --- a/src/thorin/be/spirv/spirv_builder.hpp +++ b/src/thorin/be/spirv/spirv_builder.hpp @@ -395,8 +395,7 @@ struct FileBuilder { struct BasicBlockBuilder : public SectionBuilder { explicit BasicBlockBuilder(FileBuilder& file_builder) - : file_builder(file_builder) - {} + : file_builder(file_builder), terminator(*this) {} FileBuilder& file_builder; @@ -532,45 +531,6 @@ struct BasicBlockBuilder : public SectionBuilder { return id; } - void branch(Id target) { - begin_op(spv::Op::OpBranch, 2); - ref_id(target); - } - - void branch_conditional(Id condition, Id true_target, Id false_target) { - begin_op(spv::Op::OpBranchConditional, 4); - ref_id(condition); - ref_id(true_target); - ref_id(false_target); - } - - void branch_switch(Id selector, Id default_case, std::vector literals, std::vector cases) { - assert(literals.size() == cases.size()); - begin_op(spv::Op::OpSwitch, 3 + literals.size() * 2); - ref_id(selector); - ref_id(default_case); - for (size_t i = 0; i < literals.size(); i++) { - ref_id(literals[i]); - ref_id(cases[i]); - } - } - - void selection_merge(Id merge_bb, spv::SelectionControlMask selection_control) { - begin_op(spv::Op::OpSelectionMerge, 3); - ref_id(merge_bb); - literal_int(selection_control); - } - - void loop_merge(Id merge_bb, Id continue_bb, spv::LoopControlMask loop_control, std::vector loop_control_ops) { - begin_op(spv::Op::OpLoopMerge, 4 + loop_control_ops.size()); - ref_id(merge_bb); - ref_id(continue_bb); - literal_int(loop_control); - - for (auto e : loop_control_ops) - literal_int(e); - } - Id call(Id return_type, Id callee, std::vector arguments) { begin_op(spv::Op::OpFunctionCall, 4 + arguments.size()); auto id = file_builder.generate_fresh_id(); @@ -585,18 +545,66 @@ struct BasicBlockBuilder : public SectionBuilder { Id ext_instruction(Id return_type, ExtendedInstruction instr, std::vector arguments); - void return_void() { - begin_op(spv::Op::OpReturn, 1); - } + struct TerminatorBuilder : public SectionBuilder { + TerminatorBuilder(BasicBlockBuilder& bb) : bb(bb) {} - void return_value(Id value) { - begin_op(spv::Op::OpReturnValue, 2); - ref_id(value); - } + void branch(Id target) { + begin_op(spv::Op::OpBranch, 2); + ref_id(target); + } - void unreachable() { - begin_op(spv::Op::OpUnreachable, 1); - } + void branch_conditional(Id condition, Id true_target, Id false_target) { + begin_op(spv::Op::OpBranchConditional, 4); + ref_id(condition); + ref_id(true_target); + ref_id(false_target); + } + + void branch_switch(Id selector, Id default_case, std::vector literals, std::vector cases) { + assert(literals.size() == cases.size()); + begin_op(spv::Op::OpSwitch, 3 + literals.size() * 2); + ref_id(selector); + ref_id(default_case); + for (size_t i = 0; i < literals.size(); i++) { + ref_id(literals[i]); + ref_id(cases[i]); + } + } + + void selection_merge(Id merge_bb, spv::SelectionControlMask selection_control) { + begin_op(spv::Op::OpSelectionMerge, 3); + ref_id(merge_bb); + literal_int(selection_control); + } + + void loop_merge(Id merge_bb, Id continue_bb, spv::LoopControlMask loop_control, std::vector loop_control_ops) { + begin_op(spv::Op::OpLoopMerge, 4 + loop_control_ops.size()); + ref_id(merge_bb); + ref_id(continue_bb); + literal_int(loop_control); + + for (auto e : loop_control_ops) + literal_int(e); + } + + void return_void() { + begin_op(spv::Op::OpReturn, 1); + } + + void return_value(Id value) { + begin_op(spv::Op::OpReturnValue, 2); + ref_id(value); + } + + void unreachable() { + begin_op(spv::Op::OpUnreachable, 1); + } + + private: + BasicBlockBuilder& bb; + }; + + TerminatorBuilder terminator; protected: Id ext_instruction(Id return_type, Id set, uint32_t instruction, std::vector arguments) { @@ -684,6 +692,9 @@ inline Id FileBuilder::define_function(FnBuilder &fn_builder) { for (auto w : bb->data_) fn_defs.data_.push_back(w); + + for (auto w : bb->terminator.data_) + fn_defs.data_.push_back(w); } fn_defs.begin_op(spv::Op::OpFunctionEnd, 1); From 7dd78b9731f9d5ee51206f6f29538d4768c8ec84 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Mon, 4 Nov 2024 19:55:15 +0100 Subject: [PATCH 307/342] spirv: fix int8 and fp16, 64 not declaring relevant capabilities --- src/thorin/be/spirv/spirv_types.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/thorin/be/spirv/spirv_types.cpp b/src/thorin/be/spirv/spirv_types.cpp index f3d767a89..823ad2faf 100644 --- a/src/thorin/be/spirv/spirv_types.cpp +++ b/src/thorin/be/spirv/spirv_types.cpp @@ -92,10 +92,12 @@ ConvertedType CodeGen::convert(const Type* type) { converted.layout = { 1, 1 }; break; case Node_PrimType_ps8: + builder_->capability(spv::Capability::CapabilityInt8); converted.id = builder_->declare_int_type(8, true); converted.layout = { 1, 1 }; break; case Node_PrimType_pu8: + builder_->capability(spv::Capability::CapabilityInt8); converted.id = builder_->declare_int_type(8, false); converted.layout = { 1, 1 }; break; @@ -128,6 +130,7 @@ ConvertedType CodeGen::convert(const Type* type) { converted.layout = { 8, 8 }; break; case Node_PrimType_pf16: + builder_->capability(spv::Capability::CapabilityFloat16); converted.id = builder_->declare_float_type(16); converted.layout = { 2, 2 }; break; @@ -136,6 +139,7 @@ ConvertedType CodeGen::convert(const Type* type) { converted.layout = { 4, 4 }; break; case Node_PrimType_pf64: + builder_->capability(spv::Capability::CapabilityFloat64); converted.id = builder_->declare_float_type(64); converted.layout = { 8, 8 }; break; From f26d0fdafe108cfad7ec4a2af0831e884abb28d3 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Mon, 4 Nov 2024 19:59:45 +0100 Subject: [PATCH 308/342] spirv_builder.hpp: ensure only one TypeVoid exists --- src/thorin/be/spirv/spirv_builder.hpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/thorin/be/spirv/spirv_builder.hpp b/src/thorin/be/spirv/spirv_builder.hpp index 0ee6e8773..ea0456f36 100644 --- a/src/thorin/be/spirv/spirv_builder.hpp +++ b/src/thorin/be/spirv/spirv_builder.hpp @@ -72,6 +72,7 @@ struct SectionBuilder { struct FileBuilder { enum UniqueDeclTag { NONE, + VOID_TYPE, FN_TYPE, PTR_TYPE, DEF_ARR_TYPE, @@ -264,9 +265,12 @@ struct FileBuilder { } Id declare_void_type() { + auto key = UniqueDeclKey { VOID_TYPE, { } }; + if (auto iter = unique_decls.find(key); iter != unique_decls.end()) return iter->second; types_constants.begin_op(spv::Op::OpTypeVoid, 2); auto id = generate_fresh_id(); types_constants.ref_id(id); + unique_decls[key] = id; return id; } From b5d6d85e0e1b2b389d885819b3e003c14db75aca Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Mon, 4 Nov 2024 20:14:03 +0100 Subject: [PATCH 309/342] spirv: fix LEA on array types --- src/thorin/be/spirv/spirv.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/thorin/be/spirv/spirv.cpp b/src/thorin/be/spirv/spirv.cpp index 63a60211c..97e2239a2 100644 --- a/src/thorin/be/spirv/spirv.cpp +++ b/src/thorin/be/spirv/spirv.cpp @@ -641,7 +641,7 @@ Id CodeGen::emit_bb(BasicBlockBuilder* bb, const Def* def) { auto ptr_type = access->ptr()->type()->as(); if (ptr_type->addr_space() == AddrSpace::Global) { operands.push_back(spv::MemoryAccessAlignedMask); - operands.push_back( 4 ); // TODO: SPIR-V docs say to consult client API for valid values. + operands.push_back(4); // TODO: SPIR-V docs say to consult client API for valid values. } if (auto load = def->isa()) { return bb->load(convert(load->out_val_type()).id, emit(load->ptr()), operands); @@ -668,8 +668,10 @@ Id CodeGen::emit_bb(BasicBlockBuilder* bb, const Def* def) { //} auto type = convert(lea->type()).id; auto offset = emit(lea->index()); - if (lea->ptr_pointee()->isa()) - return bb->ptr_access_chain(type, emit(lea->ptr()), offset, { }); + if (auto arr_type = lea->ptr_pointee()->isa()) { + auto base = bb->convert(spv::OpBitcast, type, emit(lea->ptr())); + return bb->ptr_access_chain(type, base, offset, { }); + } return bb->access_chain(type, emit(lea->ptr()), { offset }); } else if (auto aggop = def->isa()) { auto agg_type = convert(aggop->agg()->type()).id; From 19108af9817ca0d2b7069a585ea5f1474131edae Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Mon, 4 Nov 2024 20:14:25 +0100 Subject: [PATCH 310/342] spirv: use OpPtrCastToGeneric when allocating on the stack --- src/thorin/be/spirv/spirv.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/thorin/be/spirv/spirv.cpp b/src/thorin/be/spirv/spirv.cpp index 97e2239a2..15ef71773 100644 --- a/src/thorin/be/spirv/spirv.cpp +++ b/src/thorin/be/spirv/spirv.cpp @@ -653,7 +653,7 @@ Id CodeGen::emit_bb(BasicBlockBuilder* bb, const Def* def) { emit_unsafe(slot->frame()); auto type = slot->type(); auto id = bb->fn_builder.variable(convert(world().ptr_type(type->pointee(), 1, AddrSpace::Function)).id, spv::StorageClass::StorageClassFunction); - id = bb->convert(spv::Op::OpBitcast, convert(type).id, id); + id = bb->convert(spv::Op::OpPtrCastToGeneric, convert(type).id, id); return id; } else if (auto enter = def->isa()) { return emit_unsafe(enter->mem()); From f7819d15d84bf1b08301c18596d0054752b15a6d Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Mon, 4 Nov 2024 22:28:36 +0100 Subject: [PATCH 311/342] spirv: workarround for broken OpCompositeConstruct --- src/thorin/be/spirv/spirv.cpp | 44 +++++++++++++++++++++++------------ src/thorin/be/spirv/spirv.h | 6 +++++ 2 files changed, 35 insertions(+), 15 deletions(-) diff --git a/src/thorin/be/spirv/spirv.cpp b/src/thorin/be/spirv/spirv.cpp index 15ef71773..65aa43fb3 100644 --- a/src/thorin/be/spirv/spirv.cpp +++ b/src/thorin/be/spirv/spirv.cpp @@ -329,7 +329,7 @@ void CodeGen::emit_epilogue(Continuation* continuation) { switch (values.size()) { case 0: bb->terminator.return_void(); break; case 1: bb->terminator.return_value(values[0]); break; - default: bb->terminator.return_value(bb->composite(builder_->current_fn_->fn_ret_type, values)); + default: bb->terminator.return_value(emit_composite(bb, builder_->current_fn_->fn_ret_type, values)); } } else if (auto dst_cont = app.callee()->isa_nom(); dst_cont && dst_cont->is_basicblock()) { // ordinary jump int index = -1; @@ -471,6 +471,32 @@ Id CodeGen::emit_constant(const thorin::Def* def) { assertf(false, "Incomplete emit(def) definition"); } +Id CodeGen::emit_composite(BasicBlockBuilder* bb, Id t, Defs defs) { + std::vector ids; + for (auto& def : defs) { + ids.push_back(emit(def)); + } + return emit_composite(bb, t, ids); +} + +Id CodeGen::emit_composite(BasicBlockBuilder* bb, Id t, ArrayRef ids) { + if (target_info_.bugs.broken_op_construct_composite) { + Id c = bb->undef(t); + uint32_t x = 0; + for (auto& e : ids) { + c = bb->insert(t, e, c, { x++ }); + } + return c; + } else { + std::vector elements; + elements.resize(ids.size()); + size_t x = 0; + for (auto& e : ids) { + elements[x++] = e; + } + return bb->composite(t, elements); + } +} Id CodeGen::emit_bb(BasicBlockBuilder* bb, const Def* def) { if (auto mathop = def->isa()) @@ -618,21 +644,9 @@ Id CodeGen::emit_bb(BasicBlockBuilder* bb, const Def* def) { auto value = emit(vindex->op(0)); return bb->extract(convert(world().type_pu32()).id, value, { 0 }); } else if (auto tuple = def->isa()) { - std::vector elements; - elements.resize(tuple->num_ops()); - size_t x = 0; - for (auto& e : tuple->ops()) { - elements[x++] = emit(e); - } - return bb->composite(convert(tuple->type()).id, elements); + return emit_composite(bb, convert(tuple->type()).id, tuple->ops()); } else if (auto structagg = def->isa()) { - std::vector elements; - elements.resize(structagg->num_ops()); - size_t x = 0; - for (auto& e : structagg->ops()) { - elements[x++] = emit(e); - } - return bb->composite(convert(structagg->type()).id, elements); + return emit_composite(bb, convert(structagg->type()).id, structagg->ops()); } else if (auto access = def->isa()) { // emit dependent operations first emit_unsafe(access->mem()); diff --git a/src/thorin/be/spirv/spirv.h b/src/thorin/be/spirv/spirv.h index fe0c70c78..1eb733a13 100644 --- a/src/thorin/be/spirv/spirv.h +++ b/src/thorin/be/spirv/spirv.h @@ -20,6 +20,10 @@ struct Target { size_t pointer_size = 8; } mem_layout; + struct { + bool broken_op_construct_composite = true; + } bugs; + enum Dialect { OpenCL, Vulkan @@ -68,6 +72,8 @@ class CodeGen : public thorin::CodeGen, public thorin::Emitter); Id get_codom_type(const Continuation* fn); From d4786b05f82a70338edf9efb8903f652d3f8a1bd Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Mon, 4 Nov 2024 22:49:24 +0100 Subject: [PATCH 312/342] account for another bug in the SPIRV-LLVM translator --- src/thorin/be/spirv/spirv.cpp | 2 ++ src/thorin/be/spirv/spirv.h | 1 + 2 files changed, 3 insertions(+) diff --git a/src/thorin/be/spirv/spirv.cpp b/src/thorin/be/spirv/spirv.cpp index 65aa43fb3..9f872c522 100644 --- a/src/thorin/be/spirv/spirv.cpp +++ b/src/thorin/be/spirv/spirv.cpp @@ -686,6 +686,8 @@ Id CodeGen::emit_bb(BasicBlockBuilder* bb, const Def* def) { auto base = bb->convert(spv::OpBitcast, type, emit(lea->ptr())); return bb->ptr_access_chain(type, base, offset, { }); } + if (target_info_.bugs.static_ac_indices_must_be_i32) + offset = emit(world().cast(world().type_pu32(), lea->index())); return bb->access_chain(type, emit(lea->ptr()), { offset }); } else if (auto aggop = def->isa()) { auto agg_type = convert(aggop->agg()->type()).id; diff --git a/src/thorin/be/spirv/spirv.h b/src/thorin/be/spirv/spirv.h index 1eb733a13..ec5645879 100644 --- a/src/thorin/be/spirv/spirv.h +++ b/src/thorin/be/spirv/spirv.h @@ -22,6 +22,7 @@ struct Target { struct { bool broken_op_construct_composite = true; + bool static_ac_indices_must_be_i32 = true; } bugs; enum Dialect { From 17991eb96d2c5111828bfa9b84da825c59244c31 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Fri, 8 Nov 2024 15:41:47 +0100 Subject: [PATCH 313/342] move emit_intrinsic to spirv_instructions.cpp --- src/thorin/be/spirv/spirv.cpp | 43 -------------------- src/thorin/be/spirv/spirv_instructions.cpp | 46 +++++++++++++++++++++- 2 files changed, 45 insertions(+), 44 deletions(-) diff --git a/src/thorin/be/spirv/spirv.cpp b/src/thorin/be/spirv/spirv.cpp index 9f872c522..e0a2e5746 100644 --- a/src/thorin/be/spirv/spirv.cpp +++ b/src/thorin/be/spirv/spirv.cpp @@ -858,47 +858,4 @@ Id CodeGen::emit_bb(BasicBlockBuilder* bb, const Def* def) { assertf(false, "Incomplete emit(def) definition"); } -std::vector CodeGen::emit_intrinsic(const App& app, const Continuation* intrinsic, BasicBlockBuilder* bb) { - std::vector productions; - if (intrinsic->name() == "spirv.nonsemantic.printf") { - std::vector args; - auto string = app.arg(1); - if (auto arr_type = string->type()->isa(); arr_type->elem_type() == world().type_pu8()) { - auto arr = string->as(); - std::vector the_string; - for (size_t i = 0; i < arr_type->dim(); i++) - the_string.push_back(arr->op(i)->as()->value().get_u8()); - the_string.push_back('\0'); - args.push_back(builder_->debug_string(the_string.data())); - } else world().ELOG("spirv.nonsemantic.printf takes a string literal"); - - for (size_t i = 2; i < app.num_args() - 1; i++) { - args.push_back(emit(app.arg(i))); - } - - builder_->extension("SPV_KHR_non_semantic_info"); - bb->ext_instruction(convert(world().unit_type()).id, { "NonSemantic.DebugPrintf", 1}, args); - } else if (intrinsic->name() == "spirv.builtin") { - if (auto spv_builtin_lit = app.arg(1)->isa()) { - auto spv_builtin = spv_builtin_lit->value().get_u32(); - auto found = builder_->builtins_.find(spv_builtin); - if (found != builder_->builtins_.end()) { - productions.push_back(found->second); - } else { - auto ret_type = (*intrinsic->params().back()).type()->as(); - auto desired_type = ret_type->types()[1]->as(); - auto id = builder_->variable(convert(desired_type).id, static_cast(convert(desired_type->addr_space()))); - builder_->interface.push_back(id); - builder_->decorate(id, spv::Decoration::DecorationBuiltIn, { spv_builtin }); - builder_->builtins_[spv_builtin] = id; - productions.push_back(id); - } - } else - world().ELOG("spirv.builtin requires an integer literal as the argument"); - } else { - world().ELOG("This spir-v builtin isn't recognised: {}", intrinsic->name()); - } - return productions; -} - } diff --git a/src/thorin/be/spirv/spirv_instructions.cpp b/src/thorin/be/spirv/spirv_instructions.cpp index 484e94a73..3a4f3e0ca 100644 --- a/src/thorin/be/spirv/spirv_instructions.cpp +++ b/src/thorin/be/spirv/spirv_instructions.cpp @@ -52,4 +52,48 @@ Id CodeGen::emit_mathop(BasicBlockBuilder* bb, const thorin::MathOp& mathop) { } } -} \ No newline at end of file +std::vector CodeGen::emit_intrinsic(const App& app, const Continuation* intrinsic, BasicBlockBuilder* bb) { + std::vector productions; + + if (intrinsic->name() == "spirv.nonsemantic.printf") { + std::vector args; + auto string = app.arg(1); + if (auto arr_type = string->type()->isa(); arr_type->elem_type() == world().type_pu8()) { + auto arr = string->as(); + std::vector the_string; + for (size_t i = 0; i < arr_type->dim(); i++) + the_string.push_back(arr->op(i)->as()->value().get_u8()); + the_string.push_back('\0'); + args.push_back(builder_->debug_string(the_string.data())); + } else world().ELOG("spirv.nonsemantic.printf takes a string literal"); + + for (size_t i = 2; i < app.num_args() - 1; i++) { + args.push_back(emit(app.arg(i))); + } + + builder_->extension("SPV_KHR_non_semantic_info"); + bb->ext_instruction(convert(world().unit_type()).id, { "NonSemantic.DebugPrintf", 1}, args); + } else if (intrinsic->name() == "spirv.builtin") { + if (auto spv_builtin_lit = app.arg(1)->isa()) { + auto spv_builtin = spv_builtin_lit->value().get_u32(); + auto found = builder_->builtins_.find(spv_builtin); + if (found != builder_->builtins_.end()) { + productions.push_back(found->second); + } else { + auto ret_type = (*intrinsic->params().back()).type()->as(); + auto desired_type = ret_type->types()[1]->as(); + auto id = builder_->variable(convert(desired_type).id, static_cast(convert(desired_type->addr_space()))); + builder_->interface.push_back(id); + builder_->decorate(id, spv::Decoration::DecorationBuiltIn, { spv_builtin }); + builder_->builtins_[spv_builtin] = id; + productions.push_back(id); + } + } else + world().ELOG("spirv.builtin requires an integer literal as the argument"); + } else { + world().ELOG("thorin/spirv: Intrinsic '{}' isn't recognised", intrinsic->name()); + } + return productions; +} + +} From 720c161d38418323c7876b6678cc232edccce31c Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Fri, 8 Nov 2024 15:53:48 +0100 Subject: [PATCH 314/342] spirv: use the mathop path for intrinsics named the same --- src/thorin/be/spirv/spirv.cpp | 15 +++++++++++++++ src/thorin/be/spirv/spirv.h | 1 + src/thorin/be/spirv/spirv_instructions.cpp | 16 ++++++++++++++-- 3 files changed, 30 insertions(+), 2 deletions(-) diff --git a/src/thorin/be/spirv/spirv.cpp b/src/thorin/be/spirv/spirv.cpp index e0a2e5746..a640f0662 100644 --- a/src/thorin/be/spirv/spirv.cpp +++ b/src/thorin/be/spirv/spirv.cpp @@ -471,6 +471,21 @@ Id CodeGen::emit_constant(const thorin::Def* def) { assertf(false, "Incomplete emit(def) definition"); } + +std::vector CodeGen::emit_args(Defs defs) { + std::vector emitted; + for (auto arg : defs) { + auto arg_type = arg->type(); + if (arg_type == world().unit_type() || arg_type == world().mem_type()) { + emit_unsafe(arg); + continue; + } else { + emitted.push_back(emit(arg)); + } + } + return emitted; +} + Id CodeGen::emit_composite(BasicBlockBuilder* bb, Id t, Defs defs) { std::vector ids; for (auto& def : defs) { diff --git a/src/thorin/be/spirv/spirv.h b/src/thorin/be/spirv/spirv.h index ec5645879..0e8dc4355 100644 --- a/src/thorin/be/spirv/spirv.h +++ b/src/thorin/be/spirv/spirv.h @@ -70,6 +70,7 @@ class CodeGen : public thorin::CodeGen, public thorin::Emitter emit_intrinsic(const App& app, const Continuation* intrinsic, BasicBlockBuilder* bb); + std::vector emit_args(Defs); Id emit_as_bb(Continuation*); Id emit_mathop(BasicBlockBuilder* bb, const MathOp& op); diff --git a/src/thorin/be/spirv/spirv_instructions.cpp b/src/thorin/be/spirv/spirv_instructions.cpp index 3a4f3e0ca..a7eb19983 100644 --- a/src/thorin/be/spirv/spirv_instructions.cpp +++ b/src/thorin/be/spirv/spirv_instructions.cpp @@ -55,6 +55,19 @@ Id CodeGen::emit_mathop(BasicBlockBuilder* bb, const thorin::MathOp& mathop) { std::vector CodeGen::emit_intrinsic(const App& app, const Continuation* intrinsic, BasicBlockBuilder* bb) { std::vector productions; + auto get_produced_type = [&]() { + auto ret_type = (*intrinsic->params().back()).type()->as(); + return ret_type->types()[1]; + }; + + SpirMathOps& impl = opencl_std; + auto intrinsic_name = intrinsic->name(); +#define THORIN_MATHOP(mathop_name) \ + if ((#mathop_name) == intrinsic_name) { \ + return { bb->ext_instruction(convert(get_produced_type()).id, impl.mathop_name, emit_args(app.args().skip_back())) }; \ + } +#include "thorin/tables/mathoptable.h" + if (intrinsic->name() == "spirv.nonsemantic.printf") { std::vector args; auto string = app.arg(1); @@ -80,8 +93,7 @@ std::vector CodeGen::emit_intrinsic(const App& app, const Continuation* intr if (found != builder_->builtins_.end()) { productions.push_back(found->second); } else { - auto ret_type = (*intrinsic->params().back()).type()->as(); - auto desired_type = ret_type->types()[1]->as(); + auto desired_type = get_produced_type()->as(); auto id = builder_->variable(convert(desired_type).id, static_cast(convert(desired_type->addr_space()))); builder_->interface.push_back(id); builder_->decorate(id, spv::Decoration::DecorationBuiltIn, { spv_builtin }); From 4c703c2a9a1c81dd734981b2320402d316fe1eb2 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Fri, 8 Nov 2024 16:42:39 +0100 Subject: [PATCH 315/342] spirv: added support for reserve_shared --- src/thorin/be/spirv/spirv_instructions.cpp | 21 ++++++++++++++++----- src/thorin/be/spirv/spirv_types.cpp | 4 ++++ 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/src/thorin/be/spirv/spirv_instructions.cpp b/src/thorin/be/spirv/spirv_instructions.cpp index a7eb19983..0552cbcc1 100644 --- a/src/thorin/be/spirv/spirv_instructions.cpp +++ b/src/thorin/be/spirv/spirv_instructions.cpp @@ -53,8 +53,6 @@ Id CodeGen::emit_mathop(BasicBlockBuilder* bb, const thorin::MathOp& mathop) { } std::vector CodeGen::emit_intrinsic(const App& app, const Continuation* intrinsic, BasicBlockBuilder* bb) { - std::vector productions; - auto get_produced_type = [&]() { auto ret_type = (*intrinsic->params().back()).type()->as(); return ret_type->types()[1]; @@ -86,7 +84,9 @@ std::vector CodeGen::emit_intrinsic(const App& app, const Continuation* intr builder_->extension("SPV_KHR_non_semantic_info"); bb->ext_instruction(convert(world().unit_type()).id, { "NonSemantic.DebugPrintf", 1}, args); + return {}; } else if (intrinsic->name() == "spirv.builtin") { + std::vector productions; if (auto spv_builtin_lit = app.arg(1)->isa()) { auto spv_builtin = spv_builtin_lit->value().get_u32(); auto found = builder_->builtins_.find(spv_builtin); @@ -100,12 +100,23 @@ std::vector CodeGen::emit_intrinsic(const App& app, const Continuation* intr builder_->builtins_[spv_builtin] = id; productions.push_back(id); } + return productions; } else world().ELOG("spirv.builtin requires an integer literal as the argument"); - } else { - world().ELOG("thorin/spirv: Intrinsic '{}' isn't recognised", intrinsic->name()); + } else if (intrinsic->name() == "reserve_shared") { + auto size = app.arg(1)->isa(); + if (!size) + world().error(app.loc(), "reserve_shared called with non-constant size"); + else { + auto in_bytes = size->value().get_u64(); + auto type = world().definite_array_type(world().type_pu8(), in_bytes); + Id id = builder_->variable(convert(type).id, spv::StorageClass::StorageClassCrossWorkgroup); + id = bb->convert(spv::Op::OpBitcast, convert(get_produced_type()).id, id); + return { id }; + } } - return productions; + world().ELOG("thorin/spirv: Intrinsic '{}' isn't recognised", intrinsic->name()); + exit(-1); } } diff --git a/src/thorin/be/spirv/spirv_types.cpp b/src/thorin/be/spirv/spirv_types.cpp index 823ad2faf..8f72cb3a5 100644 --- a/src/thorin/be/spirv/spirv_types.cpp +++ b/src/thorin/be/spirv/spirv_types.cpp @@ -19,6 +19,10 @@ uint32_t CodeGen::convert(AddrSpace as) { builder_->capability(spv::Capability::CapabilityGenericPointer); break; } + case AddrSpace::Shared: { + storage_class = spv::StorageClassWorkgroup; + break; + } case AddrSpace::Push: storage_class = spv::StorageClassPushConstant; break; case AddrSpace::Input: storage_class = spv::StorageClassInput; break; case AddrSpace::Output: storage_class = spv::StorageClassOutput; break; From e6b9ad2b06becabcdff6be1c255a33f0977c1e67 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Fri, 8 Nov 2024 16:42:55 +0100 Subject: [PATCH 316/342] spirv: added support for 'min', 'max' intrinsics --- src/thorin/be/spirv/spirv_instructions.cpp | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/thorin/be/spirv/spirv_instructions.cpp b/src/thorin/be/spirv/spirv_instructions.cpp index 0552cbcc1..dd95e76b5 100644 --- a/src/thorin/be/spirv/spirv_instructions.cpp +++ b/src/thorin/be/spirv/spirv_instructions.cpp @@ -114,6 +114,22 @@ std::vector CodeGen::emit_intrinsic(const App& app, const Continuation* intr id = bb->convert(spv::Op::OpBitcast, convert(get_produced_type()).id, id); return { id }; } + } else if (intrinsic->name() == "min") { + auto type = get_produced_type(); + if (is_type_f(type)) + return { bb->ext_instruction(convert(get_produced_type()).id, { .set_name = "OpenCL.std", .id = OpenCLLIB::Fmin }, emit_args(app.args().skip_back())) }; + if (is_type_u(type)) + return { bb->ext_instruction(convert(get_produced_type()).id, { .set_name = "OpenCL.std", .id = OpenCLLIB::UMin }, emit_args(app.args().skip_back())) }; + if (is_type_i(type)) + return { bb->ext_instruction(convert(get_produced_type()).id, { .set_name = "OpenCL.std", .id = OpenCLLIB::SMin }, emit_args(app.args().skip_back())) }; + } else if (intrinsic->name() == "max") { + auto type = get_produced_type(); + if (is_type_f(type)) + return { bb->ext_instruction(convert(get_produced_type()).id, { .set_name = "OpenCL.std", .id = OpenCLLIB::Fmax }, emit_args(app.args().skip_back())) }; + if (is_type_u(type)) + return { bb->ext_instruction(convert(get_produced_type()).id, { .set_name = "OpenCL.std", .id = OpenCLLIB::UMax }, emit_args(app.args().skip_back())) }; + if (is_type_i(type)) + return { bb->ext_instruction(convert(get_produced_type()).id, { .set_name = "OpenCL.std", .id = OpenCLLIB::SMax }, emit_args(app.args().skip_back())) }; } world().ELOG("thorin/spirv: Intrinsic '{}' isn't recognised", intrinsic->name()); exit(-1); From 5dd14ae3d47762024f8a100203df47ac45700c8f Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Fri, 8 Nov 2024 21:51:49 +0100 Subject: [PATCH 317/342] spirv: implement more ops --- src/thorin/be/spirv/spirv.cpp | 21 ++++++++++++++++--- src/thorin/be/spirv/spirv_builder.hpp | 24 ++++++++++++---------- src/thorin/be/spirv/spirv_instructions.cpp | 9 ++++++++ 3 files changed, 40 insertions(+), 14 deletions(-) diff --git a/src/thorin/be/spirv/spirv.cpp b/src/thorin/be/spirv/spirv.cpp index a640f0662..cf64e2126 100644 --- a/src/thorin/be/spirv/spirv.cpp +++ b/src/thorin/be/spirv/spirv.cpp @@ -293,13 +293,13 @@ void CodeGen::emit_epilogue(Continuation* continuation) { BasicBlockBuilder* dstbb = cont2bb_[succ]; for (size_t i = 0, j = 0; i != succ->num_params(); ++i) { - assert(j < args.size()); auto param = succ->param(i); if (is_mem(param) || is_unit(param)) { if (dstbb->semi_inline) defs_[param] = 0; continue; } + assert(j < args.size()); if (dstbb->semi_inline) { defs_[param] = args[j]; } else { @@ -786,15 +786,28 @@ Id CodeGen::emit_bb(BasicBlockBuilder* bb, const Def* def) { if (auto bitcast = def->isa()) { assert(conv_src_type.layout && conv_dst_type.layout); if (conv_src_type.layout->size != conv_dst_type.layout->size) - world().ELOG("Source (%) and destination (%) datatypes sizes do not match (% vs % bytes)", src_type->to_string(), dst_type->to_string(), conv_src_type.layout->size, conv_dst_type.layout->size); + world().ELOG("Source ({}) and destination ({}) datatypes sizes do not match ({} vs {} bytes)", src_type, dst_type, conv_src_type.layout->size, conv_dst_type.layout->size); return bb->convert(spv::OpBitcast, convert(bitcast->type()).id, emit(bitcast->from())); } else if (auto cast = def->isa()) { + if (auto src_ptr_type = src_type->isa()) { + if (auto dst_ptr_type = dst_type->isa()) { + if (src_ptr_type->addr_space() == AddrSpace::Generic) { + return bb->op_with_result(spv::Op::OpGenericCastToPtr, convert(cast->type()).id, { emit(cast->from()) }); + } else if (dst_ptr_type->addr_space() == AddrSpace::Generic) { + return bb->op_with_result(spv::Op::OpPtrCastToGeneric, convert(cast->type()).id, { emit(cast->from()) }); + } else { + world().WLOG("Abnormal ptr-ptr cast: {} to {}, should be a bitcast", src_ptr_type, dst_ptr_type); + return bb->convert(spv::OpBitcast, convert(cast->type()).id, emit(cast->from())); + } + } + } + // NB: all ops used here are scalar/vector agnostic auto src_prim = src_type->isa(); auto dst_prim = dst_type->isa(); if (!src_prim || !dst_prim || src_prim->length() != dst_prim->length()) - world().ELOG("Illegal cast: % to %, casts are only supported between primitives with identical vector length", src_type->to_string(), dst_type->to_string()); + world().ELOG("Illegal cast: {} to {}, casts are only supported between primitives with identical vector length", src_type, dst_type); auto length = src_prim->length(); @@ -865,6 +878,8 @@ Id CodeGen::emit_bb(BasicBlockBuilder* bb, const Def* def) { } else THORIN_UNREACHABLE; } else if (def->isa()) { return bb->undef(convert(def->type()).id); + } else if (auto select = def->isa()) { return bb->op_with_result(spv::Op::OpSelect, convert(def->type()).id, emit_args(select->ops())); diff --git a/src/thorin/be/spirv/spirv.h b/src/thorin/be/spirv/spirv.h index 64452513f..cae51c7c6 100644 --- a/src/thorin/be/spirv/spirv.h +++ b/src/thorin/be/spirv/spirv.h @@ -90,6 +90,7 @@ class CodeGen : public thorin::CodeGen, public thorin::Emitter make_literal_string(std::string_view str) { } struct SectionBuilder { + explicit SectionBuilder(FileBuilder& file_builder) : file_builder_(file_builder) {} + std::vector data_; + FileBuilder& file_builder_; +protected: + inline Id fresh_id(); private: void output_word(uint32_t word) { data_.push_back(word); @@ -84,6 +89,22 @@ struct SectionBuilder { void literal_int(uint32_t i) { output_word(i); } + + void op(spv::Op op, std::vector operands) { + begin_op(op, operands.size() + 1); + for (auto e : operands) + literal_int(e); + } + + Id op_with_result(spv::Op op, Id type, std::vector operands) { + begin_op(op, operands.size() + 3); + ref_id(type); + auto id = fresh_id(); + ref_id(id); + for (auto e : operands) + literal_int(e); + return id; + } }; struct FileBuilder { @@ -116,7 +137,7 @@ struct FileBuilder { } }; - FileBuilder() {} + FileBuilder() : capabilities(*this), extensions(*this), ext_inst_import(*this), entry_points(*this), execution_modes(*this), debug_string_source(*this), debug_names(*this), debug_module_processed(*this), annotations(*this), types_constants(*this), fn_decls(*this), fn_defs(*this) {} FileBuilder(const FileBuilder&) = delete; Id generate_fresh_id() { return { bound++ }; } @@ -385,7 +406,7 @@ struct FileBuilder { public: void finish(std::ostream& output) { output_ = &output; - SectionBuilder memory_model_section; + SectionBuilder memory_model_section(*this); memory_model_section.begin_op(spv::Op::OpMemoryModel, 3); memory_model_section.data_.push_back(addressing_model); memory_model_section.data_.push_back(memory_model); @@ -414,11 +435,13 @@ struct FileBuilder { friend BasicBlockBuilder; }; +inline Id SectionBuilder::fresh_id() { + return file_builder_.generate_fresh_id(); +} + struct BasicBlockBuilder : public SectionBuilder { explicit BasicBlockBuilder(FileBuilder& file_builder) - : file_builder(file_builder), terminator(*this) {} - - FileBuilder& file_builder; + : SectionBuilder(file_builder), terminator(*this) {} struct Phi { Id type; @@ -428,22 +451,6 @@ struct BasicBlockBuilder : public SectionBuilder { std::vector phis; Id label; - void op(spv::Op op, std::vector operands) { - begin_op(op, operands.size() + 1); - for (auto e : operands) - literal_int(e); - } - - Id op_with_result(spv::Op op, Id type, std::vector operands) { - begin_op(op, operands.size() + 3); - ref_id(type); - auto id = file_builder.generate_fresh_id(); - ref_id(id); - for (auto e : operands) - literal_int(e); - return id; - } - Id undef(Id type) { return op_with_result(spv::Op::OpUndef, type, {}); } Id composite(Id aggregate_t, std::vector elements) { return op_with_result(spv::Op::OpCompositeConstruct, aggregate_t, elements); } @@ -451,7 +458,7 @@ struct BasicBlockBuilder : public SectionBuilder { Id extract(Id target_type, Id composite, std::vector indices) { begin_op(spv::Op::OpCompositeExtract, 4 + indices.size()); ref_id(target_type); - auto id = file_builder.generate_fresh_id(); + auto id = fresh_id(); ref_id(id); ref_id(composite); for (auto i : indices) @@ -462,7 +469,7 @@ struct BasicBlockBuilder : public SectionBuilder { Id insert(Id target_type, Id object, Id composite, std::vector indices) { begin_op(spv::Op::OpCompositeInsert, 5 + indices.size()); ref_id(target_type); - auto id = file_builder.generate_fresh_id(); + auto id = fresh_id(); ref_id(id); ref_id(object); ref_id(composite); @@ -474,7 +481,7 @@ struct BasicBlockBuilder : public SectionBuilder { Id vector_extract_dynamic(Id target_type, Id vector, Id index) { begin_op(spv::Op::OpVectorExtractDynamic, 5); ref_id(target_type); - auto id = file_builder.generate_fresh_id(); + auto id = fresh_id(); ref_id(id); ref_id(vector); ref_id(index); @@ -484,7 +491,7 @@ struct BasicBlockBuilder : public SectionBuilder { Id vector_insert_dynamic(Id target_type, Id vector, Id component, Id index) { begin_op(spv::Op::OpVectorInsertDynamic, 6); ref_id(target_type); - auto id = file_builder.generate_fresh_id(); + auto id = fresh_id(); ref_id(id); ref_id(vector); ref_id(component); @@ -495,7 +502,7 @@ struct BasicBlockBuilder : public SectionBuilder { // Used for almost all conversion operations Id convert(spv::Op op_, Id target_type, Id value) { begin_op(op_, 4); - auto id = file_builder.generate_fresh_id(); + auto id = fresh_id(); ref_id(target_type); ref_id(id); ref_id(value); @@ -504,7 +511,7 @@ struct BasicBlockBuilder : public SectionBuilder { Id access_chain(Id target_type, Id element, std::vector indexes) { begin_op(spv::Op::OpAccessChain, 4 + indexes.size()); - auto id = file_builder.generate_fresh_id(); + auto id = fresh_id(); ref_id(target_type); ref_id(id); ref_id(element); @@ -515,7 +522,7 @@ struct BasicBlockBuilder : public SectionBuilder { Id ptr_access_chain(Id target_type, Id base, Id element, std::vector indexes) { begin_op(spv::Op::OpPtrAccessChain, 5 + indexes.size()); - auto id = file_builder.generate_fresh_id(); + auto id = fresh_id(); ref_id(target_type); ref_id(id); ref_id(base); @@ -527,7 +534,7 @@ struct BasicBlockBuilder : public SectionBuilder { Id load(Id target_type, Id pointer, std::vector operands = {}) { begin_op(spv::Op::OpLoad, 4 + operands.size()); - auto id = file_builder.generate_fresh_id(); + auto id = fresh_id(); ref_id(target_type); ref_id(id); ref_id(pointer); @@ -546,7 +553,7 @@ struct BasicBlockBuilder : public SectionBuilder { Id binop(spv::Op op_, Id result_type, Id lhs, Id rhs) { begin_op(op_, 5); - auto id = file_builder.generate_fresh_id(); + auto id = fresh_id(); ref_id(result_type); ref_id(id); ref_id(lhs); @@ -556,7 +563,7 @@ struct BasicBlockBuilder : public SectionBuilder { Id call(Id return_type, Id callee, std::vector arguments) { begin_op(spv::Op::OpFunctionCall, 4 + arguments.size()); - auto id = file_builder.generate_fresh_id(); + auto id = fresh_id(); ref_id(return_type); ref_id(id); ref_id(callee); @@ -569,7 +576,7 @@ struct BasicBlockBuilder : public SectionBuilder { Id ext_instruction(Id return_type, ExtendedInstruction instr, std::vector arguments); struct TerminatorBuilder : public SectionBuilder { - TerminatorBuilder(BasicBlockBuilder& bb) : bb(bb) {} + TerminatorBuilder(BasicBlockBuilder& bb) : SectionBuilder(bb.file_builder_), bb(bb) {} void branch(Id target) { begin_op(spv::Op::OpBranch, 2); @@ -632,7 +639,7 @@ struct BasicBlockBuilder : public SectionBuilder { protected: Id ext_instruction(Id return_type, Id set, uint32_t instruction, std::vector arguments) { begin_op(spv::Op::OpExtInst, 5 + arguments.size()); - auto id = file_builder.generate_fresh_id(); + auto id = fresh_id(); ref_id(return_type); ref_id(id); ref_id(set); @@ -645,7 +652,7 @@ struct BasicBlockBuilder : public SectionBuilder { struct FnBuilder { explicit FnBuilder(FileBuilder& file_builder) - : file_builder(file_builder) + : file_builder(file_builder), header(file_builder), variables(file_builder) { function_id = file_builder.generate_fresh_id(); } @@ -726,7 +733,7 @@ inline Id FileBuilder::define_function(FnBuilder &fn_builder, bool definition) { } inline Id BasicBlockBuilder::ext_instruction(Id return_type, ExtendedInstruction instr, std::vector arguments) { - return ext_instruction(return_type, file_builder.extended_import(instr.set_name), instr.id, arguments); + return ext_instruction(return_type, file_builder_.extended_import(instr.set_name), instr.id, arguments); } } From f7bdd76fa06ffa0951de2f0d4dbd0f0a5fa4b438 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Thu, 21 Nov 2024 14:23:08 +0100 Subject: [PATCH 336/342] spirv: emit global atomic instead --- src/thorin/be/spirv/spirv_instructions.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/thorin/be/spirv/spirv_instructions.cpp b/src/thorin/be/spirv/spirv_instructions.cpp index 5c901827b..c66d5ab5a 100644 --- a/src/thorin/be/spirv/spirv_instructions.cpp +++ b/src/thorin/be/spirv/spirv_instructions.cpp @@ -152,7 +152,7 @@ std::vector CodeGen::emit_intrinsic(const App& app, const Continuation* intr op = spv::OpAtomicIAdd; else assert(false && "unknown primitive type for atomic_add"); - auto result = bb->op_with_result(op, convert(get_produced_type()).id, { ptr, literal(spv::Scope::ScopeInvocation), literal(spv::MemorySemanticsMask::MemorySemanticsAcquireReleaseMask), value }); + auto result = bb->op_with_result(op, convert(get_produced_type()).id, { ptr, literal(spv::Scope::ScopeDevice), literal(spv::MemorySemanticsMask::MemorySemanticsAcquireReleaseMask | spv::MemorySemanticsMask::MemorySemanticsCrossWorkgroupMemoryMask), value }); return { result }; } else if (intrinsic->name() == "rv_all") { auto args = emit_args(app.args().skip_back()); From ca68fb92dede62833fd064dac2496e7790726466 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Fri, 22 Nov 2024 15:24:11 +0100 Subject: [PATCH 337/342] spirv: fix regression --- src/thorin/be/spirv/spirv.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/thorin/be/spirv/spirv.cpp b/src/thorin/be/spirv/spirv.cpp index 89664230f..931546ea0 100644 --- a/src/thorin/be/spirv/spirv.cpp +++ b/src/thorin/be/spirv/spirv.cpp @@ -195,7 +195,7 @@ FnBuilder& CodeGen::get_fn_builder(thorin::Continuation* continuation) { auto& fn = *(builder_->fn_builders_[continuation] = std::make_unique(*builder_)); auto fn_type = entry_->type(); - if (kernel_config_->contains(continuation)) { + if (kernel_config_ && kernel_config_->contains(continuation)) { fn_type = patch_entry_point_signature(fn_type); } fn.fn_type = convert(fn_type).id; @@ -247,7 +247,7 @@ void CodeGen::prepare(thorin::Continuation* cont, FnBuilder* fn) { world().ddef(cont, "Emitting {} as return block", cont); if (entry_ == cont) { - bool entry_point = kernel_config_->contains(entry_); + bool entry_point = kernel_config_ && kernel_config_->contains(entry_); for (auto param : cont->params()) { if (!should_emit(param->type())) { // Nothing From 9af4dcceb3bf7397273b6bab9d4f067943c38309 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Fri, 22 Nov 2024 15:50:27 +0100 Subject: [PATCH 338/342] added default virtual destructor to Backend --- src/thorin/be/codegen.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/thorin/be/codegen.h b/src/thorin/be/codegen.h index 215a54b59..d4d804e94 100644 --- a/src/thorin/be/codegen.h +++ b/src/thorin/be/codegen.h @@ -31,6 +31,7 @@ struct DeviceBackends; struct Backend { Backend(DeviceBackends& backends, World& src); + virtual ~Backend() = default; virtual std::unique_ptr create_cg() = 0; From 9acff437a910a7e5fd394c343dc115083795b3e0 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Wed, 27 Nov 2024 14:08:21 +0100 Subject: [PATCH 339/342] spirv: implement atomic_min --- src/thorin/be/spirv/spirv_instructions.cpp | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/thorin/be/spirv/spirv_instructions.cpp b/src/thorin/be/spirv/spirv_instructions.cpp index c66d5ab5a..ddac8ed2e 100644 --- a/src/thorin/be/spirv/spirv_instructions.cpp +++ b/src/thorin/be/spirv/spirv_instructions.cpp @@ -154,6 +154,28 @@ std::vector CodeGen::emit_intrinsic(const App& app, const Continuation* intr assert(false && "unknown primitive type for atomic_add"); auto result = bb->op_with_result(op, convert(get_produced_type()).id, { ptr, literal(spv::Scope::ScopeDevice), literal(spv::MemorySemanticsMask::MemorySemanticsAcquireReleaseMask | spv::MemorySemanticsMask::MemorySemanticsCrossWorkgroupMemoryMask), value }); return { result }; + } else if (intrinsic->name() == "atomic_min") { + auto args = emit_args(app.args().skip_back()); + auto [ptr, value] = *(std::array*)args.data(); + auto produced = get_produced_type(); + spv::Op op; + if (is_type_f(produced)) { + op = spv::OpAtomicFMinEXT; + auto ct = convert(produced); + switch (ct.layout->size) { + case 2: builder_->capability(spv::Capability::CapabilityAtomicFloat16MinMaxEXT); break; + case 4: builder_->capability(spv::Capability::CapabilityAtomicFloat32MinMaxEXT); break; + case 8: builder_->capability(spv::Capability::CapabilityAtomicFloat64MinMaxEXT); break; + } + builder_->extension("SPV_EXT_shader_atomic_float_min_max"); + } else if (is_type_s(produced)) + op = spv::OpAtomicSMin; + else if (is_type_u(produced)) + op = spv::OpAtomicUMin; + else + assert(false && "unknown primitive type for atomic_add"); + auto result = bb->op_with_result(op, convert(get_produced_type()).id, { ptr, literal(spv::Scope::ScopeDevice), literal(spv::MemorySemanticsMask::MemorySemanticsAcquireReleaseMask | spv::MemorySemanticsMask::MemorySemanticsCrossWorkgroupMemoryMask), value }); + return { result }; } else if (intrinsic->name() == "rv_all") { auto args = emit_args(app.args().skip_back()); auto result = bb->op_with_result(spv::Op::OpGroupAll, convert(get_produced_type()).id, { literal(spv::Scope::ScopeInvocation), emit(app.arg(1)) }); From b19176c9495317da14d53e434efe3d08a0a4837b Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Wed, 27 Nov 2024 14:18:39 +0100 Subject: [PATCH 340/342] spirv: use appropriate scope/semantic mask depending on atomic ptr param --- src/thorin/be/spirv/spirv_instructions.cpp | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/thorin/be/spirv/spirv_instructions.cpp b/src/thorin/be/spirv/spirv_instructions.cpp index ddac8ed2e..fd1a5fce7 100644 --- a/src/thorin/be/spirv/spirv_instructions.cpp +++ b/src/thorin/be/spirv/spirv_instructions.cpp @@ -52,6 +52,18 @@ Id CodeGen::emit_mathop(BasicBlockBuilder* bb, const thorin::MathOp& mathop) { } } +std::tuple addrspace_atomics_params(World& world, AddrSpace as) { + switch (as) { + case AddrSpace::Global: + return std::make_tuple(spv::ScopeDevice, spv::MemorySemanticsMask::MemorySemanticsAcquireReleaseMask | spv::MemorySemanticsMask::MemorySemanticsCrossWorkgroupMemoryMask); + case AddrSpace::Shared: + return std::make_tuple(spv::ScopeWorkgroup, spv::MemorySemanticsMask::MemorySemanticsAcquireReleaseMask | spv::MemorySemanticsMask::MemorySemanticsWorkgroupMemoryMask); + default: + world.ELOG("Unsupported address space for atomics: {}", (int) as); + THORIN_UNREACHABLE; + } +} + std::vector CodeGen::emit_intrinsic(const App& app, const Continuation* intrinsic, BasicBlockBuilder* bb) { auto get_produced_type = [&]() { auto ret_type = (*intrinsic->params().back()).type()->as(); @@ -152,7 +164,8 @@ std::vector CodeGen::emit_intrinsic(const App& app, const Continuation* intr op = spv::OpAtomicIAdd; else assert(false && "unknown primitive type for atomic_add"); - auto result = bb->op_with_result(op, convert(get_produced_type()).id, { ptr, literal(spv::Scope::ScopeDevice), literal(spv::MemorySemanticsMask::MemorySemanticsAcquireReleaseMask | spv::MemorySemanticsMask::MemorySemanticsCrossWorkgroupMemoryMask), value }); + auto [scope, semantics] = addrspace_atomics_params(world(), app.arg(1)->type()->as()->addr_space()); + auto result = bb->op_with_result(op, convert(get_produced_type()).id, { ptr, literal(scope), literal(semantics), value }); return { result }; } else if (intrinsic->name() == "atomic_min") { auto args = emit_args(app.args().skip_back()); @@ -174,7 +187,8 @@ std::vector CodeGen::emit_intrinsic(const App& app, const Continuation* intr op = spv::OpAtomicUMin; else assert(false && "unknown primitive type for atomic_add"); - auto result = bb->op_with_result(op, convert(get_produced_type()).id, { ptr, literal(spv::Scope::ScopeDevice), literal(spv::MemorySemanticsMask::MemorySemanticsAcquireReleaseMask | spv::MemorySemanticsMask::MemorySemanticsCrossWorkgroupMemoryMask), value }); + auto [scope, semantics] = addrspace_atomics_params(world(), app.arg(1)->type()->as()->addr_space()); + auto result = bb->op_with_result(op, convert(get_produced_type()).id, { ptr, literal(scope), literal(semantics), value }); return { result }; } else if (intrinsic->name() == "rv_all") { auto args = emit_args(app.args().skip_back()); From 13db35de0dda2a47a32636b9bb3a6a68c53e8620 Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Wed, 27 Nov 2024 17:25:16 +0100 Subject: [PATCH 341/342] spirv: fix reserve_shared --- src/thorin/be/spirv/spirv_instructions.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/thorin/be/spirv/spirv_instructions.cpp b/src/thorin/be/spirv/spirv_instructions.cpp index fd1a5fce7..2bfbaccb6 100644 --- a/src/thorin/be/spirv/spirv_instructions.cpp +++ b/src/thorin/be/spirv/spirv_instructions.cpp @@ -116,14 +116,18 @@ std::vector CodeGen::emit_intrinsic(const App& app, const Continuation* intr } else world().ELOG("spirv.builtin requires an integer literal as the argument"); } else if (intrinsic->name() == "reserve_shared") { + auto produced_t = get_produced_type()->as(); + auto pointee = produced_t->pointee(); + if (auto indef = pointee->isa()) + pointee = indef->elem_type(); auto size = app.arg(1)->isa(); if (!size) world().error(app.loc(), "reserve_shared called with non-constant size"); else { - auto in_bytes = size->value().get_u64(); + auto in_bytes = size->value().get_u64() * convert(pointee).layout->size; auto type = world().definite_array_type(world().type_pu8(), in_bytes); Id id = builder_->variable(convert(world().ptr_type(type, 1, AddrSpace::Shared)).id, static_cast(convert(AddrSpace::Shared))); - id = bb->convert(spv::Op::OpBitcast, convert(get_produced_type()).id, id); + id = bb->convert(spv::Op::OpBitcast, convert(produced_t).id, id); return { id }; } } else if (intrinsic->name() == "min") { From 174499ee63e07d1d7c883c6331c0e85e23faaf8a Mon Sep 17 00:00:00 2001 From: Hugo Devillers Date: Thu, 28 Nov 2024 15:30:35 +0100 Subject: [PATCH 342/342] spirv: emit barrier() as a Control barrier instead --- src/thorin/be/spirv/spirv_instructions.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/thorin/be/spirv/spirv_instructions.cpp b/src/thorin/be/spirv/spirv_instructions.cpp index 2bfbaccb6..1e011542c 100644 --- a/src/thorin/be/spirv/spirv_instructions.cpp +++ b/src/thorin/be/spirv/spirv_instructions.cpp @@ -148,7 +148,7 @@ std::vector CodeGen::emit_intrinsic(const App& app, const Continuation* intr return { bb->ext_instruction(convert(get_produced_type()).id, { .set_name = "OpenCL.std", .id = OpenCLLIB::SMax }, emit_args(app.args().skip_back())) }; } else if (intrinsic->name() == "barrier") { emit_args(app.args().skip_back()); - bb->op(spv::Op::OpMemoryBarrier, { literal(spv::Scope::ScopeInvocation), literal(spv::MemorySemanticsMask::MemorySemanticsAcquireReleaseMask) }); + bb->op(spv::Op::OpControlBarrier, { literal(spv::Scope::ScopeWorkgroup), literal(spv::Scope::ScopeWorkgroup), literal(spv::MemorySemanticsMask::MemorySemanticsWorkgroupMemoryMask | spv::MemorySemanticsMask::MemorySemanticsSequentiallyConsistentMask) }); return { }; } else if (intrinsic->name() == "atomic_add") { auto args = emit_args(app.args().skip_back());