diff --git a/CMakeLists.txt b/CMakeLists.txt index a1cad41882..44437c9932 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -448,24 +448,6 @@ else() endif() endif() -#------------------------------------------------------------------------------- -# llhd-sim Configuration -#------------------------------------------------------------------------------- - -if(NOT WIN32) - option(CIRCT_LLHD_SIM_ENABLED "Enables LLHD sim." ON) -else() - option(CIRCT_LLHD_SIM_ENABLED "Enables LLHD sim." OFF) -endif() - -if(CIRCT_LLHD_SIM_ENABLED) - message(STATUS "llhd-sim is enabled.") -else() - message(STATUS "llhd-sim is disabled.") -endif() - -llvm_canonicalize_cmake_booleans(CIRCT_LLHD_SIM_ENABLED) - #------------------------------------------------------------------------------- # Z3Lib Configuration (for circt-lec and SMT dialect integration tests) #------------------------------------------------------------------------------- diff --git a/CODEOWNERS b/CODEOWNERS index 3f5d4f2fc5..bc7a5abab5 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -32,7 +32,6 @@ tools/arcilator @fabianschuiki @maerhart # LLHD **/LLHD* @fabianschuiki @maerhart -tools/llhd-sim @fabianschuiki @maerhart # Pipeline **/Dialect/Pipeline @mortbopet diff --git a/docs/dialects.dot b/docs/dialects.dot index a1f46bcf9c..45205de940 100644 --- a/docs/dialects.dot +++ b/docs/dialects.dot @@ -50,7 +50,7 @@ digraph G { ESI [URL="https://circt.llvm.org/docs/Dialects/ESI/"] FSM [URL="https://circt.llvm.org/docs/Dialects/FSM/"] HWArith [URL="https://circt.llvm.org/docs/Dialects/HWArith/"] - MooreMIR [URL="https://circt.llvm.org/docs/Dialects/Moore/", label="Moore MIR"] + Moore [URL="https://circt.llvm.org/docs/Dialects/Moore/"] // Intermediate node to target when lowering to both SV and Core dialects lower_to_sv_and_core [shape=point label="" fillcolor=black] @@ -79,7 +79,6 @@ digraph G { subgraph backend_internal_tools{ node [fillcolor="#fdc086"] Arcilator - llhd_sim [label="llhd-sim"] ExportSystemC ExportVerilog [URL="https://circt.llvm.org/docs/VerilogGeneration/"] } @@ -88,7 +87,7 @@ digraph G { // External tools subgraph external_tools { node [shape=octagon fillcolor="#ffff99"] - Moore + Slang Calyx_native [label="Calyx native"] } @@ -126,13 +125,14 @@ digraph G { // Things that lower into a subset of the RTL-like dialects. Cluster these // together to avoid a massive clutter. - {Pipeline MSFT HWArith MooreMIR} -> lower_to_core [arrowhead=none] + {Pipeline MSFT HWArith Moore} -> lower_to_core [arrowhead=none] + Moore -> LLHD {ESI FIRRTL FSM} -> lower_to_sv_and_core [arrowhead=none] lower_to_sv_and_core -> SV lower_to_sv_and_core -> Comb [lhead=cluster_RTL] lower_to_core -> Comb [lhead=cluster_RTL] Seq -> SV - {HW MooreMIR} -> LLHD + LLHD -> Arcilator Interop -> Arc [ltail=cluster_RTL] Comb -> SystemC [ltail=cluster_RTL] ExportVerilog -> SVFile @@ -140,6 +140,7 @@ digraph G { // Tool flows Arc -> Arcilator Arcilator -> SimBinary + Arcilator -> VCDTrace Scheduling -> LoopSchedule [dir=both] Scheduling -> Pipeline [dir=both] Chisel -> FIRFile @@ -149,13 +150,10 @@ digraph G { ExportSystemC -> SystemCFile SV -> ExportVerilog Seq -> ExportVerilog [ltail=cluster_RTL] - HW -> llhd_sim [ltail=cluster_RTL] - SVVHDL -> Moore [weight=999] - Moore -> MooreMIR + SVVHDL -> Slang [weight=999] + Slang -> Moore Calyx -> Calyx_native Calyx_native -> lower_to_sv_and_core [arrowhead=none] - LLHD -> llhd_sim - llhd_sim -> VCDTrace ESI -> ServiceDesc MSFT -> TCL PyFile -> PyCDE @@ -170,7 +168,7 @@ digraph G { // Leave one rank free above the RTL cluster to improve routing of incoming // edges. - {FIRRTL FSM ESI MSFT HWArith MooreMIR} -> space_above_RTL [weight=999, style=invis] + {FIRRTL FSM ESI MSFT HWArith Moore} -> space_above_RTL [weight=999, style=invis] {space_above_RTL} -> {Seq HW Comb} [lhead=cluster_RTL, weight=999, style=invis] // Fix the following sink nodes below the CIRCT cluster diff --git a/docs/includes/img/dialects.png b/docs/includes/img/dialects.png index f907d7c080..ca8db2e744 100644 Binary files a/docs/includes/img/dialects.png and b/docs/includes/img/dialects.png differ diff --git a/docs/includes/img/dialects.svg b/docs/includes/img/dialects.svg index 8d0d3c7457..0cf44cd7ec 100644 --- a/docs/includes/img/dialects.svg +++ b/docs/includes/img/dialects.svg @@ -1,171 +1,171 @@ - - - + + G - + cluster_mlir_frontends - -Upstream frontends (selection) + +Upstream frontends (selection) cluster_mlir - -Upstream MLIR + +Upstream MLIR cluster_std_arith_dialect - + cluster_circt - -CIRCT + +CIRCT cluster_RTL - -Core dialects + +Core dialects cluster_input_langs - -Input languages + +Input languages PyTorch - -PyTorch + +PyTorch CF - -CF + +CF PyTorch->CF - - + + Polygeist - -Polygeist + +Polygeist Affine - -Affine + +Affine Polygeist->Affine - - + + SCF - -SCF + +SCF Calyx - -Calyx + +Calyx SCF->Calyx - - + + LoopSchedule - -LoopSchedule + +LoopSchedule Affine->LoopSchedule - - + + Arith - -Arith + +Arith Handshake - -Handshake + +Handshake Arith->Handshake - - + + Arith->Calyx - - + + FIRRTLParser - -FIRRTLParser + +FIRRTLParser FIRRTL - -FIRRTL + +FIRRTL - + FIRRTLParser->FIRRTL - - + + PyCDE - -PyCDE + +PyCDE @@ -173,68 +173,68 @@ MSFT - -MSFT + +MSFT - + PyCDE->MSFT - - + + ESI - -ESI + +ESI - + PyCDE->ESI - - + + FSM - -FSM + +FSM - + PyCDE->FSM - - + + HWArith - -HWArith + +HWArith - + PyCDE->HWArith - - + + Scheduling - -Scheduling + +Scheduling @@ -242,225 +242,225 @@ Pipeline - -Pipeline + +Pipeline - + Scheduling->Pipeline - - - + + + - + Scheduling->LoopSchedule - - - + + + SSP - -SSP + +SSP - + Scheduling->SSP - - - + + + - + Scheduling->MSFT - - - + + + Handshake->FIRRTL - - + + Calyx->FSM - - + + - + Calyx_native - -Calyx native + +Calyx native Calyx->Calyx_native - - + + lower_to_sv_and_core - + - + FIRRTL->lower_to_sv_and_core - + lower_to_core - + Pipeline->lower_to_core - + LoopSchedule->Calyx - - + + MSFT->lower_to_core - + - + TCL - - - -Placements (tcl) + + + +Placements (tcl) - + MSFT->TCL - - + + - + ESI->lower_to_sv_and_core - + - + ServiceDesc - - - -ESI system description -(JSON) + + + +ESI system description +(JSON) - + ESI->ServiceDesc - - + + - + FSM->lower_to_sv_and_core - + HWArith->lower_to_core - + - + -MooreMIR - - -Moore MIR +Moore + + +Moore - + -MooreMIR->lower_to_core - +Moore->lower_to_core + - + LLHD - -LLHD + +LLHD - - -MooreMIR->LLHD - - + + +Moore->LLHD + + Comb - -Comb + +Comb - + lower_to_sv_and_core->Comb - - + + SV - -SV + +SV - + lower_to_sv_and_core->SV - - + + - + lower_to_core->Comb - - + + Seq - -Seq + +Seq @@ -469,73 +469,55 @@ HW - -HW + +HW - + Seq->SV - - + + - + ExportVerilog - - -ExportVerilog + + +ExportVerilog - -Seq->ExportVerilog - - - - - -HW->LLHD - - - - - -llhd_sim - -llhd-sim - - -HW->llhd_sim - - +Seq->ExportVerilog + + SystemC - -SystemC + +SystemC Comb->SystemC - - + + Interop - -Interop + +Interop @@ -543,196 +525,196 @@ Arc - -Arc + +Arc Interop->Arc - - - - - -LLHD->llhd_sim - - + + Arcilator - -Arcilator + +Arcilator + + + +LLHD->Arcilator + + Arc->Arcilator - - + + - + SV->ExportVerilog - - + + - + ExportSystemC - -ExportSystemC + +ExportSystemC - + SystemC->ExportSystemC - - + + + + + +VCDTrace + + + +Trace (vcd) + + + +Arcilator->VCDTrace + + - + SimBinary - - - -Simulation Binary (obj) + + + +Simulation Binary (obj) Arcilator->SimBinary - - - - - -VCDTrace - - - -Trace (vcd) - - - -llhd_sim->VCDTrace - - + + - + SystemCFile - - - -SystemC (c++) + + + +SystemC (c++) - + ExportSystemC->SystemCFile - - + + - + SVFile - - - -SystemVerilog + + + +SystemVerilog ExportVerilog->SVFile - - + + - - -Moore - -Moore + + +Slang + +Slang - + -Moore->MooreMIR - - +Slang->Moore + + Calyx_native->lower_to_sv_and_core - + - + FIRFile - - - -.fir + + + +.fir - + FIRFile->FIRRTLParser - - + + - + Chisel - -Chisel + +Chisel - + Chisel->FIRFile - - + + - + SVVHDL - -SV/VHDL + +SV/VHDL - + -SVVHDL->Moore - - +SVVHDL->Slang + + - + PyFile - -Python + +Python - + PyFile->PyCDE - - + + - + SoftwareAPI - - - -Software API -(e.g. py/c++/c#) + + + +Software API +(e.g. py/c++/c#) - + ServiceDesc->SoftwareAPI - - + + diff --git a/include/circt/Conversion/LLHDToLLVM.h b/include/circt/Conversion/LLHDToLLVM.h deleted file mode 100644 index c83d86126b..0000000000 --- a/include/circt/Conversion/LLHDToLLVM.h +++ /dev/null @@ -1,43 +0,0 @@ -//===- LLHDToLLVM.h - LLHD to LLVM pass entry point -------------*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// This header file defines prototypes that expose the LLHDToLLVM pass -// constructors. -// -//===----------------------------------------------------------------------===// - -#ifndef CIRCT_CONVERSION_LLHDTOLLVM_LLHDTOLLVM_H -#define CIRCT_CONVERSION_LLHDTOLLVM_LLHDTOLLVM_H - -#include "circt/Support/LLVM.h" -#include - -namespace mlir { -class LLVMTypeConverter; -} // namespace mlir - -namespace circt { - -#define GEN_PASS_DECL_CONVERTLLHDTOLLVM -#include "circt/Conversion/Passes.h.inc" - -/// Get the LLHD to LLVM type conversions -void populateLLHDToLLVMTypeConversions(mlir::LLVMTypeConverter &converter); - -/// Get the LLHD to LLVM conversion patterns. -void populateLLHDToLLVMConversionPatterns(mlir::LLVMTypeConverter &converter, - RewritePatternSet &patterns, - size_t &sigCounter, - size_t ®Counter); - -/// Create an LLHD to LLVM conversion pass. -std::unique_ptr> createConvertLLHDToLLVMPass(); - -} // namespace circt - -#endif // CIRCT_CONVERSION_LLHDTOLLVM_LLHDTOLLVM_H diff --git a/include/circt/Conversion/Passes.h b/include/circt/Conversion/Passes.h index f540554e27..6e002b0686 100644 --- a/include/circt/Conversion/Passes.h +++ b/include/circt/Conversion/Passes.h @@ -36,7 +36,6 @@ #include "circt/Conversion/HWToSystemC.h" #include "circt/Conversion/HandshakeToDC.h" #include "circt/Conversion/HandshakeToHW.h" -#include "circt/Conversion/LLHDToLLVM.h" #include "circt/Conversion/LTLToCore.h" #include "circt/Conversion/LoopScheduleToCalyx.h" #include "circt/Conversion/MooreToCore.h" diff --git a/include/circt/Conversion/Passes.td b/include/circt/Conversion/Passes.td index 96807afb88..ba69a52a64 100644 --- a/include/circt/Conversion/Passes.td +++ b/include/circt/Conversion/Passes.td @@ -519,22 +519,6 @@ def ConvertMooreToCore : Pass<"convert-moore-to-core", "mlir::ModuleOp"> { "llhd::LLHDDialect"]; } -//===----------------------------------------------------------------------===// -// LLHDToLLVM -//===----------------------------------------------------------------------===// - -def ConvertLLHDToLLVM : Pass<"convert-llhd-to-llvm", "mlir::ModuleOp"> { - let summary = "Convert LLHD to LLVM"; - let description = [{ - This pass translates LLHD to LLVM. - }]; - let constructor = "circt::createConvertLLHDToLLVMPass()"; - let dependentDialects = [ - "mlir::arith::ArithDialect", - "mlir::LLVM::LLVMDialect" - ]; -} - //===----------------------------------------------------------------------===// // HWToLLVM //===----------------------------------------------------------------------===// diff --git a/include/circt/Dialect/LLHD/Simulator/Engine.h b/include/circt/Dialect/LLHD/Simulator/Engine.h deleted file mode 100644 index 17e33322af..0000000000 --- a/include/circt/Dialect/LLHD/Simulator/Engine.h +++ /dev/null @@ -1,82 +0,0 @@ -//===- Engine.h - LLHD simulaton engine -------------------------*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// This file defines the main Engine class of the LLHD simulator. -// -//===----------------------------------------------------------------------===// - -#ifndef CIRCT_DIALECT_LLHD_SIMULATOR_ENGINE_H -#define CIRCT_DIALECT_LLHD_SIMULATOR_ENGINE_H - -#include "State.h" -#include "Trace.h" - -#include "circt/Dialect/LLHD/IR/LLHDOps.h" -#include "mlir/IR/BuiltinOps.h" - -namespace mlir { -class ExecutionEngine; -} // namespace mlir - -namespace llvm { -class Error; -class Module; -} // namespace llvm - -namespace circt { -namespace llhd { -namespace sim { - -class Engine { -public: - /// Initialize an LLHD simulation engine. This initializes the state, as well - /// as the mlir::ExecutionEngine with the given module. - Engine( - llvm::raw_ostream &out, ModuleOp module, - llvm::function_ref mlirTransformer, - llvm::function_ref llvmTransformer, - std::string root, TraceMode tm, ArrayRef sharedLibPaths); - - /// Default destructor - ~Engine(); - - /// Run simulation up to n steps or maxTime picoseconds of simulation time. - /// n=0 and T=0 make the simulation run indefinitely. - int simulate(int n, uint64_t maxTime); - - /// Build the instance layout of the design. - void buildLayout(ModuleOp module); - - /// Get the MLIR module. - const ModuleOp getModule() const { return module; } - - /// Get the simulation state. - const State *getState() const { return state.get(); } - - /// Dump the instance layout stored in the State. - void dumpStateLayout(); - - /// Dump the instances each signal triggers. - void dumpStateSignalTriggers(); - -private: - void walkEntity(EntityOp entity, Instance &child); - - llvm::raw_ostream &out; - std::string root; - std::unique_ptr state; - std::unique_ptr engine; - ModuleOp module; - TraceMode traceMode; -}; - -} // namespace sim -} // namespace llhd -} // namespace circt - -#endif // CIRCT_DIALECT_LLHD_SIMULATOR_ENGINE_H diff --git a/include/circt/Dialect/LLHD/Simulator/State.h b/include/circt/Dialect/LLHD/Simulator/State.h deleted file mode 100644 index a50d8741df..0000000000 --- a/include/circt/Dialect/LLHD/Simulator/State.h +++ /dev/null @@ -1,357 +0,0 @@ -//===- State.h - Simulation state definition --------------------*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// Defines structures used to keep track of the simulation state in the LLHD -// simulator. -// -//===----------------------------------------------------------------------===// - -#ifndef CIRCT_DIALECT_LLHD_SIMULATOR_STATE_H -#define CIRCT_DIALECT_LLHD_SIMULATOR_STATE_H - -#include "llvm/ADT/APInt.h" -#include "llvm/ADT/SmallVector.h" -#include "llvm/ADT/StringMap.h" - -#include -#include -#include - -namespace circt { -namespace llhd { -namespace sim { - -/// The simulator's internal representation of time. -class Time { -public: - /// Empty (zero) time constructor. All the time values are defaulted to 0. - Time() = default; - - /// Construct with given time values. - Time(uint64_t time, uint64_t delta, uint64_t eps) - : time(time), delta(delta), eps(eps) {} - - /// Compare the time values in order of time, delta, eps. - bool operator<(const Time &rhs) const { - return time < rhs.time || (time == rhs.time && delta < rhs.delta) || - (time == rhs.time && delta == rhs.delta && eps < rhs.eps); - } - - /// Return true if all the time values are equal. - bool operator==(const Time &rhs) const { - return time == rhs.time && delta == rhs.delta && eps == rhs.eps; - } - - /// Add two time values. - Time operator+(const Time &rhs) const { - return Time(time + rhs.time, delta + rhs.delta, eps + rhs.eps); - } - - /// Get the stored time in a printable format. - std::string toString() const; - - uint64_t getTime() const { return time; } - -private: - /// Simulation real time. - uint64_t time; - uint64_t delta; - uint64_t eps; -}; - -/// Detail structure that can be easily accessed by the lowered code. -struct SignalDetail { - uint8_t *value; - uint64_t offset; - uint64_t instIndex; - uint64_t globalIndex; -}; - -/// The simulator's internal representation of a signal. -class Signal { -public: - /// Construct an "empty" signal. - Signal(std::string name, std::string owner) - : name(name), owner(owner), size(0), value(nullptr) {} - - /// Construct a signal with the given name, owner and initial value. - Signal(std::string name, std::string owner, uint8_t *value, uint64_t size) - : name(name), owner(owner), size(size), value(value) {} - - /// Default move constructor. - Signal(Signal &&) = default; - - /// Free 'value' since it is allocated using 'malloc' in the LLVM code - /// generated by LLHDToLLVM. - ~Signal(); - - /// Returns true if the signals match in name, owner, size and value. - bool operator==(const Signal &rhs) const { - if (owner != rhs.owner || name != rhs.name || size != rhs.size) - return false; - return std::memcmp(value, rhs.value, size); - } - - /// Returns true if the owner name is lexically smaller than rhs's owner, or - /// the name is lexically smaller than rhs's name, in case they share the same - /// owner. - bool operator<(const Signal &rhs) const { - return (owner < rhs.owner || (owner == rhs.owner && name < rhs.name)); - } - - bool isOwner(const std::string &rhs) const { return owner == rhs; }; - - std::string getOwner() const { return owner; } - - bool isValidSigName() const { - return std::regex_match(name, std::regex("(sig)?[0-9]*")); - } - - std::string getName() const { return name; } - - uint64_t getSize() const { return size; } - - uint8_t *getValue() const { return value; } - - const std::vector &getTriggeredInstanceIndices() const { - return instanceIndices; - } - - void pushInstanceIndex(unsigned i) { instanceIndices.push_back(i); } - - bool hasElement() const { return elements.size() > 0; } - - size_t getElementSize() const { return elements.size(); } - - void pushElement(std::pair val) { - elements.push_back(val); - } - - /// Store JIT allocated signal pointer and size. - void store(uint8_t *v, uint64_t s) { - value = v; - size = s; - } - - /// Update signal value when it is changed, the width of incoming signal - /// value and the stored signal value are identical. - /// As majority signals are smaller than 64 bits, this implementation - /// is much faster as it avoided memcpy in most cases. - /// @param v Pointer to signal value - /// @return true when signal is updated, false when not - bool updateWhenChanged(const uint64_t *v) { - switch (size) { - case 1: { - const uint8_t *newVal = reinterpret_cast(v); - if (*value == *newVal) - return false; - *value = *newVal; - break; - } - case 2: { - const uint16_t *newVal = reinterpret_cast(v); - if (*(uint16_t *)value == *newVal) - return false; - *(uint16_t *)value = *newVal; - break; - } - case 4: { - const uint32_t *newVal = reinterpret_cast(v); - if (*(uint32_t *)value == *newVal) - return false; - *(uint32_t *)value = *newVal; - break; - } - case 8: { - if (*(uint64_t *)value == *v) - return false; - *(uint64_t *)value = *v; - break; - } - default: { - if (std::memcmp(value, v, size) == 0) - return false; - std::memcpy(value, v, size); - break; - } - } - - return true; - } - - /// Return the value of the signal in hexadecimal string format. - std::string toHexString() const; - - /// Return the value of the i-th element of the signal in hexadecimal string - /// format. - std::string toHexString(unsigned) const; - -private: - std::string name; - std::string owner; - // The list of instances this signal triggers. - std::vector instanceIndices; - uint64_t size; - uint8_t *value; - std::vector> elements; -}; - -/// The simulator's internal representation of one queue slot. -struct Slot { - /// Create a new empty slot. - Slot(Time time) : time(time) {} - - /// Returns true if the slot's time is smaller than the compared slot's time. - bool operator<(const Slot &rhs) const; - - /// Returns true if the slot's time is greater than the compared slot's time. - bool operator>(const Slot &rhs) const; - - /// Insert a change. - void insertChange(int index, int bitOffset, uint8_t *bytes, unsigned width); - - /// Insert a scheduled process wakeup. - void insertChange(unsigned inst); - - // A map from signal indexes to change buffers. Makes it easy to sort the - // changes such that we can process one signal at a time. - llvm::SmallVector, 32> changes; - // Buffers for the signal changes. - llvm::SmallVector, 32> buffers; - // The number of used change buffers in the slot. - size_t changesSize = 0; - - // Processes with scheduled wakeup. - llvm::SmallVector scheduled; - Time time; - bool unused = false; -}; - -/// This is equivalent to and std::priorityQueue ordered using the greater -/// operator, which adds an insertion method to add changes to a slot. -class UpdateQueue : public llvm::SmallVector { - unsigned topSlot = 0; - llvm::SmallVector unused; - -public: - /// Check wheter a slot for the given time already exists. If that's the case, - /// add the new change to it, else create a new slot and push it to the queue. - void insertOrUpdate(Time time, int index, int bitOffset, uint8_t *bytes, - unsigned width); - - /// Check wheter a slot for the given time already exists. If that's the case, - /// add the scheduled wakeup to it, else create a new slot and push it to the - /// queue. - void insertOrUpdate(Time time, unsigned inst); - - /// Return a reference to a slot with the given timestamp. If such a slot - /// already exists, a reference to it will be returned. Otherwise a reference - /// to a fresh slot is returned. - Slot &getOrCreateSlot(Time time); - - /// Get a reference to the current top of the queue (the earliest event - /// available). - const Slot &top(); - - /// Pop the current top of the queue. This marks the current top slot as - /// unused and resets its internal structures such that they can be reused. - void pop(); - - unsigned events = 0; -}; - -/// State structure for process persistence across suspension. -struct ProcState { - unsigned inst; - int resume; - bool *senses; - uint8_t *resumeState; -}; - -/// The simulator internal representation of an instance. -struct Instance { - Instance() = default; - - Instance(std::string name) - : name(name), procState(nullptr), entityState(nullptr) {} - - // The instance name. - std::string name; - // The instance's hierarchical path. - std::string path; - // The instance's base unit. - std::string unit; - bool isEntity; - size_t nArgs = 0; - // The arguments and signals of this instance. - llvm::SmallVector sensitivityList; - ProcState *procState; - uint8_t *entityState; - Time expectedWakeup; - // A pointer to the base unit jitted function. - void (*unitFPtr)(void **); - - /// Free procState and entityState since they are allocated using 'malloc' in - /// the LLVM code generated in LLHDToLLVM. - ~Instance(); -}; - -/// The simulator's state. It contains the current simulation time, signal -/// values and the event queue. -struct State { - /// Construct a new empty (at 0 time) state. - State() = default; - - /// State destructor, ensures all malloc'd regions stored in the state are - /// correctly free'd. - ~State(); - - /// Pop the head of the queue and update the simulation time. - Slot popQueue(); - - /// Push a new scheduled wakeup event in the event queue. - void pushQueue(Time time, unsigned inst); - - /// Find an instance in the instances list by name and return an - /// iterator for it. - llvm::SmallVectorTemplateCommon::iterator - getInstanceIterator(std::string instName); - - /// Add a new signal to the state. Returns the index of the new signal. - int addSignal(std::string name, std::string owner); - - int addSignalData(int index, std::string owner, uint8_t *value, - uint64_t size); - - void addSignalElement(unsigned, unsigned, unsigned); - - /// Add a pointer to the process persistence state to a process instance. - void addProcPtr(std::string name, ProcState *procStatePtr); - - /// Dump a signal to the out stream. One entry is added for every instance - /// the signal appears in. - void dumpSignal(llvm::raw_ostream &out, int index); - - /// Dump the instance layout. Used for testing purposes. - void dumpLayout(); - - /// Dump the instances each signal triggers. Used for testing purposes. - void dumpSignalTriggers(); - - Time time; - std::string root; - llvm::SmallVector instances; - llvm::SmallVector signals; - UpdateQueue queue; -}; - -} // namespace sim -} // namespace llhd -} // namespace circt - -#endif // CIRCT_DIALECT_LLHD_SIMULATOR_STATE_H diff --git a/include/circt/Dialect/LLHD/Simulator/Trace.h b/include/circt/Dialect/LLHD/Simulator/Trace.h deleted file mode 100644 index bd3d83be7e..0000000000 --- a/include/circt/Dialect/LLHD/Simulator/Trace.h +++ /dev/null @@ -1,79 +0,0 @@ -//===- Trace.h - Simulation trace definition --------------------*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// This file defines the Trace class, used to handle the signal trace generation -// for the llhd-sim tool. -// -//===----------------------------------------------------------------------===// - -#ifndef CIRCT_DIALECT_LLHD_SIMULATOR_TRACE_H -#define CIRCT_DIALECT_LLHD_SIMULATOR_TRACE_H - -#include "State.h" - -#include -#include - -namespace llvm { -class raw_ostream; -} // namespace llvm - -namespace circt { -namespace llhd { -namespace sim { - -enum class TraceMode { Full, Reduced, Merged, MergedReduce, NamedOnly, None }; - -class Trace { - llvm::raw_ostream &out; - std::unique_ptr const &state; - TraceMode mode; - Time currentTime; - // Each entry defines if the respective signal is active for tracing. - std::vector isTraced; - // Buffer of changes ready to be flushed. - std::vector> changes; - // Buffer of changes for the merged formats. - std::map, std::string> mergedChanges; - // Buffer of last dumped change for each signal. - std::map, std::string> lastValue; - - /// Push one change to the changes vector. - void pushChange(unsigned inst, unsigned sigIndex, int elem); - /// Push one change for each element of a signal if it is of a structured - /// type, or the full signal otherwise. - void pushAllChanges(unsigned inst, unsigned sigIndex); - - /// Add a merged change to the change buffer. - void addChangeMerged(unsigned); - - /// Sorts the changes buffer lexicographically wrt. the hierarchical paths. - void sortChanges(); - - /// Flush the changes buffer to the output stream with full format. - void flushFull(); - // Flush the changes buffer to the output stream with merged format. - void flushMerged(); - -public: - Trace(std::unique_ptr const &state, llvm::raw_ostream &out, - TraceMode mode); - - /// Add a value change to the trace changes buffer. - void addChange(unsigned); - - /// Flush the changes buffer to the output stream. The flush can be forced for - /// merged changes, flushing even if the next real-time step has not been - /// reached. - void flush(bool force = false); -}; -} // namespace sim -} // namespace llhd -} // namespace circt - -#endif // CIRCT_DIALECT_LLHD_SIMULATOR_TRACE_H diff --git a/lib/CAPI/Conversion/CMakeLists.txt b/lib/CAPI/Conversion/CMakeLists.txt index 2c4e950177..50a409e176 100644 --- a/lib/CAPI/Conversion/CMakeLists.txt +++ b/lib/CAPI/Conversion/CMakeLists.txt @@ -25,7 +25,6 @@ add_circt_public_c_api_library(CIRCTCAPIConversion CIRCTHWToSMT CIRCTHWToSV CIRCTHWToSystemC - CIRCTLLHDToLLVM CIRCTLoopScheduleToCalyx CIRCTLTLToCore CIRCTMooreToCore diff --git a/lib/Conversion/CMakeLists.txt b/lib/Conversion/CMakeLists.txt index 2d487427d8..562779ee3d 100644 --- a/lib/Conversion/CMakeLists.txt +++ b/lib/Conversion/CMakeLists.txt @@ -20,7 +20,6 @@ add_subdirectory(HWToLLVM) add_subdirectory(HWToSMT) add_subdirectory(HWToSV) add_subdirectory(HWToSystemC) -add_subdirectory(LLHDToLLVM) add_subdirectory(LoopScheduleToCalyx) add_subdirectory(MooreToCore) add_subdirectory(PipelineToHW) diff --git a/lib/Conversion/LLHDToLLVM/CMakeLists.txt b/lib/Conversion/LLHDToLLVM/CMakeLists.txt deleted file mode 100644 index 7640e5238d..0000000000 --- a/lib/Conversion/LLHDToLLVM/CMakeLists.txt +++ /dev/null @@ -1,26 +0,0 @@ -add_circt_conversion_library(CIRCTLLHDToLLVM - LLHDToLLVM.cpp - - DEPENDS - CIRCTConversionPassIncGen - MLIRArithDialect - - LINK_COMPONENTS - Core - - LINK_LIBS PUBLIC - CIRCTLLHD - CIRCTComb - CIRCTCombToArith - CIRCTCombToLLVM - CIRCTHWToLLVM - CIRCTHW - CIRCTSupport - MLIRArithToLLVM - MLIRControlFlowToLLVM - MLIRFuncToLLVM - MLIRLLVMCommonConversion - MLIRVectorDialect - MLIRTransforms - MLIRReconcileUnrealizedCasts - ) diff --git a/lib/Conversion/LLHDToLLVM/LLHDToLLVM.cpp b/lib/Conversion/LLHDToLLVM/LLHDToLLVM.cpp deleted file mode 100644 index 91dfcda459..0000000000 --- a/lib/Conversion/LLHDToLLVM/LLHDToLLVM.cpp +++ /dev/null @@ -1,1918 +0,0 @@ -//===- LLHDToLLVM.cpp - LLHD to LLVM Conversion Pass ----------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// This is the main LLHD to LLVM Conversion Pass Implementation. -// -//===----------------------------------------------------------------------===// - -#include "circt/Conversion/LLHDToLLVM.h" -#include "circt/Conversion/CombToArith.h" -#include "circt/Conversion/CombToLLVM.h" -#include "circt/Conversion/HWToLLVM.h" -#include "circt/Dialect/LLHD/IR/LLHDDialect.h" -#include "circt/Dialect/LLHD/IR/LLHDOps.h" -#include "circt/Support/LLVM.h" -#include "circt/Support/Namespace.h" -#include "mlir/Conversion/ArithToLLVM/ArithToLLVM.h" -#include "mlir/Conversion/ControlFlowToLLVM/ControlFlowToLLVM.h" -#include "mlir/Conversion/FuncToLLVM/ConvertFuncToLLVM.h" -#include "mlir/Conversion/FuncToLLVM/ConvertFuncToLLVMPass.h" -#include "mlir/Conversion/LLVMCommon/ConversionTarget.h" -#include "mlir/Conversion/LLVMCommon/Pattern.h" -#include "mlir/Conversion/ReconcileUnrealizedCasts/ReconcileUnrealizedCasts.h" -#include "mlir/Dialect/Arith/IR/Arith.h" -#include "mlir/Dialect/LLVMIR/LLVMDialect.h" -#include "mlir/IR/IRMapping.h" -#include "mlir/Pass/Pass.h" -#include "mlir/Transforms/DialectConversion.h" - -namespace circt { -#define GEN_PASS_DEF_CONVERTLLHDTOLLVM -#include "circt/Conversion/Passes.h.inc" -} // namespace circt - -using namespace mlir; -using namespace circt; -using namespace circt::llhd; - -//===----------------------------------------------------------------------===// -// Helpers -//===----------------------------------------------------------------------===// - -/// Get an existing global string. -static Value getGlobalString(Location loc, OpBuilder &builder, - const TypeConverter *typeConverter, - LLVM::GlobalOp &str) { - auto voidPtrTy = LLVM::LLVMPointerType::get(builder.getContext()); - auto i32Ty = IntegerType::get(builder.getContext(), 32); - - auto addr = builder.create(loc, voidPtrTy, str.getName()); - auto idx = builder.create(loc, i32Ty, - builder.getI32IntegerAttr(0)); - std::array idxs({idx, idx}); - return builder.create(loc, voidPtrTy, str.getType(), addr, idxs); -} - -/// Looks up a symbol and inserts a new functino at the beginning of the -/// module's region in case the function does not exists. If -/// insertBodyAndTerminator is set, also adds the entry block and return -/// terminator. -static LLVM::LLVMFuncOp -getOrInsertFunction(ModuleOp &module, ConversionPatternRewriter &rewriter, - Location loc, std::string name, Type signature, - bool insertBodyAndTerminator = false) { - auto func = module.lookupSymbol(name); - if (!func) { - OpBuilder moduleBuilder(module.getBodyRegion()); - func = moduleBuilder.create(loc, name, signature); - if (insertBodyAndTerminator) { - func.addEntryBlock(moduleBuilder); - OpBuilder b(func.getBody()); - b.create(loc, ValueRange()); - } - } - return func; -} - -/// Return the LLVM type used to represent a signal. It corresponds to a struct -/// with the format: {valuePtr, bitOffset, instanceIndex, globalIndex}. -static Type getLLVMSigType(LLVM::LLVMDialect *dialect) { - auto voidPtrTy = LLVM::LLVMPointerType::get(dialect->getContext()); - auto i64Ty = IntegerType::get(dialect->getContext(), 64); - return LLVM::LLVMStructType::getLiteral(dialect->getContext(), - {voidPtrTy, i64Ty, i64Ty, i64Ty}); -} - -/// Extract the details from the given signal struct. The details are returned -/// in the original struct order. -static std::vector getSignalDetail(ConversionPatternRewriter &rewriter, - LLVM::LLVMDialect *dialect, - Location loc, Value signal, - bool extractIndices = false) { - - auto voidPtrTy = LLVM::LLVMPointerType::get(dialect->getContext()); - auto i64Ty = IntegerType::get(dialect->getContext(), 64); - auto sigTy = getLLVMSigType(dialect); - - std::vector result; - - // Extract the value and offset elements. - auto sigPtrPtr = rewriter.create(loc, voidPtrTy, sigTy, signal, - ArrayRef({0, 0})); - result.push_back(rewriter.create(loc, voidPtrTy, sigPtrPtr)); - - auto offsetPtr = rewriter.create(loc, voidPtrTy, sigTy, signal, - ArrayRef({0, 1})); - result.push_back(rewriter.create(loc, i64Ty, offsetPtr)); - - // Extract the instance and global indices. - if (extractIndices) { - auto instIndexPtr = rewriter.create( - loc, voidPtrTy, sigTy, signal, ArrayRef({0, 2})); - result.push_back(rewriter.create(loc, i64Ty, instIndexPtr)); - - auto globalIndexPtr = rewriter.create( - loc, voidPtrTy, sigTy, signal, ArrayRef({0, 3})); - result.push_back(rewriter.create(loc, i64Ty, globalIndexPtr)); - } - - return result; -} - -/// Create a subsignal struct. -static Value createSubSig(LLVM::LLVMDialect *dialect, - ConversionPatternRewriter &rewriter, Location loc, - std::vector originDetail, Value newPtr, - Value newOffset) { - auto i32Ty = IntegerType::get(dialect->getContext(), 32); - auto sigTy = getLLVMSigType(dialect); - - // Create signal struct. - auto sigUndef = rewriter.create(loc, sigTy); - auto storeSubPtr = - rewriter.create(loc, sigUndef, newPtr, 0); - auto storeSubOffset = - rewriter.create(loc, storeSubPtr, newOffset, 1); - auto storeSubInstIndex = rewriter.create( - loc, storeSubOffset, originDetail[2], 2); - auto storeSubGlobalIndex = rewriter.create( - loc, storeSubInstIndex, originDetail[3], 3); - - // Allocate and store the subsignal. - auto oneC = rewriter.create(loc, i32Ty, - rewriter.getI32IntegerAttr(1)); - auto allocaSubSig = rewriter.create( - loc, LLVM::LLVMPointerType::get(dialect->getContext()), sigTy, oneC, 4); - rewriter.create(loc, storeSubGlobalIndex, allocaSubSig); - - return allocaSubSig; -} - -/// Returns true if the given value is passed as an argument to the destination -/// block of the given WaitOp. -static bool isWaitDestArg(WaitOp op, Value val) { - for (auto arg : op.getDestOps()) { - if (arg == val) - return true; - } - return false; -} - -// Returns true if the given operation is used as a destination argument in a -// WaitOp. -static bool isWaitDestArg(Operation *op) { - for (auto user : op->getUsers()) { - if (auto wait = dyn_cast(user)) - return isWaitDestArg(wait, op->getResult(0)); - } - return false; -} - -/// Gather the types of values that are used outside of the block they're -/// defined in. An LLVMType structure containing those types, in order of -/// appearance, is returned. -static Type getProcPersistenceTy(LLVM::LLVMDialect *dialect, - const TypeConverter *converter, ProcOp &proc) { - SmallVector types = SmallVector(); - proc.walk([&](Operation *op) -> void { - if (op->isUsedOutsideOfBlock(op->getBlock()) || isWaitDestArg(op)) { - auto ty = op->getResult(0).getType(); - auto convertedTy = converter->convertType(ty); - types.push_back(convertedTy); - } - }); - - // Also persist block arguments escaping their defining block. - for (auto &block : proc.getBlocks()) { - // Skip entry block (contains the function signature in its args). - if (block.isEntryBlock()) - continue; - - for (auto arg : block.getArguments()) { - if (arg.isUsedOutsideOfBlock(&block)) { - types.push_back(converter->convertType(arg.getType())); - } - } - } - - return LLVM::LLVMStructType::getLiteral(dialect->getContext(), types); -} - -/// Insert a comparison block that either jumps to the trueDest block, if the -/// resume index mathces the current index, or to falseDest otherwise. If no -/// falseDest is provided, the next block is taken insead. -static void insertComparisonBlock(ConversionPatternRewriter &rewriter, - LLVM::LLVMDialect *dialect, Location loc, - Region *body, Value resumeIdx, int currIdx, - Block *trueDest, ValueRange trueDestArgs, - Block *falseDest = nullptr) { - auto i32Ty = IntegerType::get(dialect->getContext(), 32); - auto secondBlock = ++body->begin(); - auto newBlock = rewriter.createBlock(body, secondBlock); - auto cmpIdx = rewriter.create( - loc, i32Ty, rewriter.getI32IntegerAttr(currIdx)); - auto cmpRes = rewriter.create(loc, LLVM::ICmpPredicate::eq, - resumeIdx, cmpIdx); - - // Default to jumping to the next block for the false case, if no explicit - // block is provided. - if (!falseDest) - falseDest = &*secondBlock; - - rewriter.create(loc, cmpRes, trueDest, trueDestArgs, - falseDest, ValueRange()); - - // Redirect the entry block terminator to the new comparison block. - auto entryTer = body->front().getTerminator(); - entryTer->setSuccessor(newBlock, 0); -} - -/// Insert a GEP operation to the pointer of the i-th value in the process -/// persistence table. -static Value gepPersistenceState(LLVM::LLVMDialect *dialect, Location loc, - ConversionPatternRewriter &rewriter, - Type stateTy, int index, Value state) { - return rewriter.create( - loc, LLVM::LLVMPointerType::get(dialect->getContext()), stateTy, state, - ArrayRef({0, 3, index})); -} - -/// Persist a `Value` by storing it into the process persistence table, and -/// substituting the uses that escape the block the operation is defined in with -/// a load from the persistence table. -static void persistValue(LLVM::LLVMDialect *dialect, Location loc, - const TypeConverter *converter, - ConversionPatternRewriter &rewriter, Type stateTy, - int &i, Value state, Value persist) { - auto elemTy = cast( - cast(stateTy).getBody()[3]) - .getBody()[i]; - - if (auto arg = dyn_cast(persist)) { - rewriter.setInsertionPointToStart(arg.getParentBlock()); - } else { - rewriter.setInsertionPointAfter(persist.getDefiningOp()); - } - - Value convPersist = converter->materializeTargetConversion( - rewriter, loc, converter->convertType(persist.getType()), {persist}); - - auto gep0 = gepPersistenceState(dialect, loc, rewriter, stateTy, i, state); - - Value toStore; - if (auto ptr = dyn_cast(persist.getType())) { - // Unwrap the pointer and store it's value. - auto elemTy = converter->convertType(ptr.getUnderlyingType()); - toStore = rewriter.create(loc, elemTy, convPersist); - } else if (isa(persist.getType())) { - // Unwrap and store the signal struct. - toStore = rewriter.create(loc, getLLVMSigType(dialect), - convPersist); - } else { - // Store the value directly. - toStore = convPersist; - } - - rewriter.create(loc, toStore, gep0); - - // Load the value from the persistence table and substitute the original - // use with it, whenever it is in a different block. - for (auto &use : llvm::make_early_inc_range(persist.getUses())) { - auto user = use.getOwner(); - if (isa(persist.getType()) && user != toStore.getDefiningOp() && - user != convPersist.getDefiningOp() && - persist.getParentBlock() == user->getBlock()) { - // Redirect uses of the pointer in the same block to the pointer in the - // persistence state. This ensures that stores and loads all operate on - // the same value. - use.set(gep0); - } else if (persist.getParentBlock() != user->getBlock() || - (isa(user) && - isWaitDestArg(cast(user), persist))) { - // The destination args of a wait op have to be loaded in the entry block - // of the function, before jumping to the resume destination, so they can - // be passed as block arguments by the comparison block. - if (isa(user) && isWaitDestArg(cast(user), persist)) - rewriter.setInsertionPoint( - user->getParentRegion()->front().getTerminator()); - else - rewriter.setInsertionPointToStart(user->getBlock()); - - auto gep1 = - gepPersistenceState(dialect, loc, rewriter, stateTy, i, state); - // Use the pointer in the state struct directly for pointer and signal - // types. - if (isa(persist.getType())) { - use.set(gep1); - } else { - auto load1 = rewriter.create(loc, elemTy, gep1); - // Load the value otherwise. - use.set(load1); - } - } - } - i++; -} - -/// Insert the blocks and operations needed to persist values across suspension, -/// as well as ones needed to resume execution at the right spot. -static void insertPersistence(const TypeConverter *converter, - ConversionPatternRewriter &rewriter, - LLVM::LLVMDialect *dialect, Location loc, - ProcOp &proc, Type &stateTy, - LLVM::LLVMFuncOp &converted, - Operation *splitEntryBefore) { - auto i32Ty = IntegerType::get(dialect->getContext(), 32); - - auto &firstBB = converted.getBody().front(); - - // Split entry block such that all the operations contained in it in the - // original process appear after the comparison blocks. - auto splitFirst = - rewriter.splitBlock(&firstBB, splitEntryBefore->getIterator()); - - // Insert dummy branch terminator at the new end of the function's entry - // block. - rewriter.setInsertionPointToEnd(&firstBB); - rewriter.create(loc, ValueRange(), splitFirst); - - // Load the resume index from the process state argument. - rewriter.setInsertionPoint(firstBB.getTerminator()); - auto gep = rewriter.create( - loc, LLVM::LLVMPointerType::get(dialect->getContext()), i32Ty, - converted.getArgument(1), ArrayRef({1})); - - auto larg = rewriter.create(loc, i32Ty, gep); - - auto body = &converted.getBody(); - - // Insert an abort block as the last block. - auto abortBlock = rewriter.createBlock(body, body->end()); - rewriter.create(loc, ValueRange()); - - // Redirect the entry block to a first comparison block. If on a first - // execution, jump to the new (splitted) entry block, else the process is in - // an illegal state and jump to the abort block. - insertComparisonBlock(rewriter, dialect, loc, body, larg, 0, splitFirst, - ValueRange(), abortBlock); - - // Keep track of the index in the presistence table of the operation we - // are currently processing. - int i = 0; - // Keep track of the current resume index for comparison blocks. - int waitInd = 0; - - // Insert operations required to persist values across process suspension. - converted.walk([&](Operation *op) -> void { - if ((op->isUsedOutsideOfBlock(op->getBlock()) || isWaitDestArg(op)) && - op->getResult(0) != larg.getResult()) { - persistValue(dialect, loc, converter, rewriter, stateTy, i, - converted.getArgument(1), op->getResult(0)); - } - - // Insert a comparison block for wait operations. - if (auto wait = dyn_cast(op)) { - insertComparisonBlock(rewriter, dialect, loc, body, larg, ++waitInd, - wait.getDest(), wait.getDestOps()); - - // Insert the resume index update at the wait operation location. - rewriter.setInsertionPoint(op); - auto procState = op->getParentOfType().getArgument(1); - auto resumeIdxC = rewriter.create( - loc, i32Ty, rewriter.getI32IntegerAttr(waitInd)); - auto resumeIdxPtr = rewriter.create( - loc, LLVM::LLVMPointerType::get(dialect->getContext()), i32Ty, - procState, ArrayRef({1})); - rewriter.create(op->getLoc(), resumeIdxC, resumeIdxPtr); - } - }); - - // Also persist argument blocks escaping their defining block. - for (auto &block : converted.getBlocks()) { - // Skip entry block as it contains the function signature. - if (block.isEntryBlock()) - continue; - - for (auto arg : block.getArguments()) { - if (arg.isUsedOutsideOfBlock(&block)) { - persistValue(dialect, loc, converter, rewriter, stateTy, i, - converted.getArgument(1), arg); - } - } - } -} - -/// Return a struct type of arrays containing one entry for each RegOp condition -/// that require more than one state of the trigger to infer it (i.e. `both`, -/// `rise` and `fall`). -static LLVM::LLVMStructType getRegStateTy(LLVM::LLVMDialect *dialect, - Operation *entity) { - SmallVector types; - entity->walk([&](RegOp op) { - size_t count = 0; - for (size_t i = 0; i < op.getModes().size(); ++i) { - auto mode = op.getRegModeAt(i); - if (mode == RegMode::fall || mode == RegMode::rise || - mode == RegMode::both) - ++count; - } - if (count > 0) - types.push_back(LLVM::LLVMArrayType::get( - IntegerType::get(dialect->getContext(), 1), count)); - }); - return LLVM::LLVMStructType::getLiteral(dialect->getContext(), types); -} - -/// Create a zext operation by one bit on the given value. This is useful when -/// passing unsigned indexes to a GEP instruction, which treats indexes as -/// signed values, to avoid unexpected "sign overflows". -static Value zextByOne(Location loc, ConversionPatternRewriter &rewriter, - Value value) { - auto valueTy = value.getType(); - auto zextTy = IntegerType::get(valueTy.getContext(), - valueTy.getIntOrFloatBitWidth() + 1); - return rewriter.create(loc, zextTy, value); -} - -/// Adjust the bithwidth of value to be the same as targetTy's bitwidth. -static Value adjustBitWidth(Location loc, ConversionPatternRewriter &rewriter, - Type targetTy, Value value) { - auto valueWidth = value.getType().getIntOrFloatBitWidth(); - auto targetWidth = targetTy.getIntOrFloatBitWidth(); - - if (valueWidth < targetWidth) - return rewriter.create(loc, targetTy, value); - - if (valueWidth > targetWidth) - return rewriter.create(loc, targetTy, value); - - return value; -} - -static unsigned getIndexOfOperandResult(Operation *op, Value result) { - for (unsigned j = 0, e = op->getNumResults(); j < e; ++j) { - if (result == result.getDefiningOp()->getResult(j)) - return j; - } - llvm_unreachable( - "no way to recurse to an operation that does not return any value"); -} - -/// Recursively clone the init origin of a sig operation into the init function, -/// up to the initial constant value(s). This is required to clone the -/// initialization of array and struct signals, where the init operand cannot -/// originate from a constant operation. -static Value recursiveCloneInit(OpBuilder &initBuilder, IRMapping &mapping, - Value init) { - SmallVector clonedOperands; - Operation *initOp = init.getDefiningOp(); - - // If we end up at a value that we get via BlockArgument or as a result of a - // llhd.prb op, return a nullptr to signal that something went wrong, because - // these cases are not supported. - if (!initOp || isa(initOp)) - return nullptr; - - for (size_t i = 0, e = initOp->getNumOperands(); i < e; ++i) { - Value operand = initOp->getOperand(i); - - // If we have some value that is used multiple times (e.g., broadcasted to - // an array) then don't emit the ops to create this value several times, - // but instead remember the cloned value and use it again. - if (auto memorizedOperand = mapping.lookupOrNull(operand)) { - clonedOperands.push_back(memorizedOperand); - continue; - } - - // Recursively follow operands. - Value clonedOperand = recursiveCloneInit(initBuilder, mapping, operand); - if (!clonedOperand) - return nullptr; - - mapping.map(operand, clonedOperand); - clonedOperands.push_back(clonedOperand); - } - - Operation *clone = initOp->clone(); - clone->setOperands(clonedOperands); - - // If we have cloned an operation that returns several values, we have to - // find the result value of the cloned operation we want to return. - unsigned index = getIndexOfOperandResult(initOp, init); - return initBuilder.insert(clone)->getResult(index); -} - -/// Check if the given type is either of LLHD's ArrayType, StructType, or LLVM -/// array or struct type. -static bool isArrayOrStruct(Type type) { - return isa(type); -} - -/// Shift an integer signal pointer to obtain a view of the underlying value as -/// if it was shifted. -static std::pair -shiftIntegerSigPointer(Location loc, LLVM::LLVMDialect *dialect, - ConversionPatternRewriter &rewriter, Value pointer, - Value index) { - auto voidPtrTy = LLVM::LLVMPointerType::get(dialect->getContext()); - auto i64Ty = IntegerType::get(dialect->getContext(), 64); - - auto ptrToInt = rewriter.create(loc, i64Ty, pointer); - auto const8 = rewriter.create( - loc, index.getType(), rewriter.getI64IntegerAttr(8)); - auto ptrOffset = rewriter.create(loc, index, const8); - auto shiftedPtr = rewriter.create(loc, ptrToInt, ptrOffset); - auto newPtr = rewriter.create(loc, voidPtrTy, shiftedPtr); - - // Compute the new offset into the first byte. - auto bitOffset = rewriter.create(loc, index, const8); - - return std::make_pair(newPtr, bitOffset); -} - -/// Shift the pointer of a structured-type (array or struct) signal, to change -/// its view as if the desired slice/element was extracted. -static Value shiftStructuredSigPointer(Location loc, - ConversionPatternRewriter &rewriter, - Type elemTy, Value pointer, - LLVM::GEPArg index) { - // TODO: Remove unused args - auto voidPtrTy = LLVM::LLVMPointerType::get(rewriter.getContext()); - return rewriter.create(loc, voidPtrTy, elemTy, pointer, - ArrayRef({0, index})); -} - -/// Shift the pointer of an array-typed signal, to change its view as if the -/// desired slice/element was extracted. -static Value shiftArraySigPointer(Location loc, - ConversionPatternRewriter &rewriter, - Type arrTy, Value pointer, - LLVM::GEPArg index) { - if (auto indexValue = dyn_cast(index)) - index = zextByOne(loc, rewriter, indexValue); - return shiftStructuredSigPointer(loc, rewriter, arrTy, pointer, index); -} - -//===----------------------------------------------------------------------===// -// Type conversions -//===----------------------------------------------------------------------===// - -static Type convertSigType(SigType type, LLVMTypeConverter &converter) { - auto &context = converter.getContext(); - // auto i64Ty = IntegerType::get(&context, 64); - auto voidPtrTy = LLVM::LLVMPointerType::get(&context); - // LLVM::LLVMStructType::getLiteral(&context, {voidPtrTy, i64Ty, i64Ty, - // i64Ty}) - return voidPtrTy; -} - -static Type convertTimeType(TimeType type, LLVMTypeConverter &converter) { - auto i64Ty = IntegerType::get(&converter.getContext(), 64); - return LLVM::LLVMArrayType::get(i64Ty, 3); -} - -static Type convertPtrType(PtrType type, LLVMTypeConverter &converter) { - // converter.convertType(type.getUnderlyingType()) - return LLVM::LLVMPointerType::get(type.getContext()); -} - -//===----------------------------------------------------------------------===// -// Unit conversions -//===----------------------------------------------------------------------===// - -namespace { -/// Convert an `llhd.entity` entity to LLVM dialect. The result is an -/// `llvm.func` which takes a pointer to the global simulation state, a pointer -/// to the entity's local state, and a pointer to the instance's signal table as -/// arguments. -struct EntityOpConversion : public ConvertToLLVMPattern { - explicit EntityOpConversion(MLIRContext *ctx, - LLVMTypeConverter &typeConverter, - size_t &sigCounter, size_t ®Counter) - : ConvertToLLVMPattern(llhd::EntityOp::getOperationName(), ctx, - typeConverter), - sigCounter(sigCounter), regCounter(regCounter) {} - - LogicalResult - matchAndRewrite(Operation *op, ArrayRef operands, - ConversionPatternRewriter &rewriter) const override { - // Get adapted operands. - EntityOpAdaptor transformed(operands); - // Get entity operation. - auto entityOp = cast(op); - - // Collect used llvm types. - auto voidTy = getVoidType(); - auto voidPtrTy = getVoidPtrType(); - auto sigTy = getLLVMSigType(&getDialect()); - - regCounter = 0; - - // Use an intermediate signature conversion to add the arguments for the - // state and signal table pointer arguments. - LLVMTypeConverter::SignatureConversion intermediate( - entityOp.getNumArguments()); - // Add state and signal table arguments. - intermediate.addInputs( - std::array({voidPtrTy, voidPtrTy, voidPtrTy})); - for (size_t i = 0, e = entityOp.getNumArguments(); i < e; ++i) - intermediate.addInputs(i, voidTy); - rewriter.applySignatureConversion(entityOp.getBodyBlock(), intermediate, - typeConverter); - - OpBuilder bodyBuilder = - OpBuilder::atBlockBegin(&entityOp.getBlocks().front()); - LLVMTypeConverter::SignatureConversion final( - intermediate.getConvertedTypes().size()); - final.addInputs(0, voidPtrTy); - final.addInputs(1, voidPtrTy); - final.addInputs(2, voidPtrTy); - - // The first n elements of the signal table represent the entity arguments, - // while the remaining elements represent the entity's owned signals. - sigCounter = entityOp.getNumArguments(); - for (size_t i = 0; i < sigCounter; ++i) { - // Create gep operations from the signal table for each original argument. - auto gep = bodyBuilder.create(op->getLoc(), voidPtrTy, sigTy, - entityOp.getArgument(2), - LLVM::GEPArg(i)); - // Remap i-th original argument to the gep'd signal pointer. - final.remapInput(i + 3, gep.getResult()); - } - - rewriter.applySignatureConversion(entityOp.getBodyBlock(), final, - typeConverter); - - // Get the converted entity signature. - auto funcTy = - LLVM::LLVMFunctionType::get(voidTy, {voidPtrTy, voidPtrTy, voidPtrTy}); - - // Create the a new llvm function to house the lowered entity. - auto llvmFunc = rewriter.create( - op->getLoc(), entityOp.getName(), funcTy); - - // Add a return to the entity for later inclusion into the LLVM function. - rewriter.setInsertionPointToEnd(&entityOp.getBlocks().front()); - rewriter.create(op->getLoc(), ValueRange{}); - - // Inline the entity region in the new llvm function. - rewriter.inlineRegionBefore(entityOp.getBody(), llvmFunc.getBody(), - llvmFunc.end()); - - // Erase the original operation. - rewriter.eraseOp(op); - - return success(); - } - -private: - size_t &sigCounter; - size_t ®Counter; -}; -} // namespace - -namespace { -/// Convert an `llhd.proc` operation to LLVM dialect. This inserts the required -/// logic to resume execution after an `llhd.wait` operation, as well as state -/// keeping for values that need to persist across suspension. -struct ProcOpConversion : public ConvertToLLVMPattern { - explicit ProcOpConversion(MLIRContext *ctx, LLVMTypeConverter &typeConverter) - : ConvertToLLVMPattern(ProcOp::getOperationName(), ctx, typeConverter) {} - - LogicalResult - matchAndRewrite(Operation *op, ArrayRef operands, - ConversionPatternRewriter &rewriter) const override { - auto procOp = cast(op); - - // Get adapted operands. - ProcOpAdaptor transformed(operands); - - // Collect used llvm types. - auto voidTy = getVoidType(); - auto voidPtrTy = getVoidPtrType(); - auto i32Ty = IntegerType::get(rewriter.getContext(), 32); - auto stateTy = LLVM::LLVMStructType::getLiteral( - rewriter.getContext(), - {/*currentInstance=*/i32Ty, /*resumeIndex=*/i32Ty, - /*senseFlags=*/voidPtrTy /*senseTableTy*/, - /*persistence=*/ - getProcPersistenceTy(&getDialect(), typeConverter, procOp)}); - auto sigTy = getLLVMSigType(&getDialect()); - - // Keep track of the original first operation of the process, to know where - // to split the first block to insert comparison blocks. - auto &firstOp = op->getRegion(0).front().front(); - - // Have an intermediate signature conversion to add the arguments for the - // state, process-specific state and signal table. - LLVMTypeConverter::SignatureConversion intermediate( - procOp.getNumArguments()); - // Add state, process state table and signal table arguments. - std::array procArgTys({voidPtrTy, voidPtrTy, voidPtrTy}); - intermediate.addInputs(procArgTys); - for (size_t i = 0, e = procOp.getNumArguments(); i < e; ++i) - intermediate.addInputs(i, voidTy); - rewriter.applySignatureConversion(&procOp.getBlocks().front(), intermediate, - typeConverter); - - // Get the final signature conversion. - OpBuilder bodyBuilder = - OpBuilder::atBlockBegin(&procOp.getBlocks().front()); - LLVMTypeConverter::SignatureConversion final( - intermediate.getConvertedTypes().size()); - final.addInputs(0, voidPtrTy); - final.addInputs(1, voidPtrTy); - final.addInputs(2, voidPtrTy); - - for (size_t i = 0, e = procOp.getNumArguments(); i < e; ++i) { - // Create gep operations from the signal table for each original argument. - auto gep = bodyBuilder.create(op->getLoc(), voidPtrTy, sigTy, - procOp.getArgument(2), - LLVM::GEPArg(i)); - - // Remap the i-th original argument to the gep'd value. - final.remapInput(i + 3, gep.getResult()); - } - - // Get the converted process signature. - auto funcTy = - LLVM::LLVMFunctionType::get(voidTy, {voidPtrTy, voidPtrTy, voidPtrTy}); - // Create a new llvm function to house the lowered process. - auto llvmFunc = rewriter.create(op->getLoc(), - procOp.getName(), funcTy); - llvmFunc->setAttr("llhd.argument_count", - rewriter.getI32IntegerAttr(procOp.getNumArguments())); - - // Inline the process region in the new llvm function. - rewriter.inlineRegionBefore(procOp.getBody(), llvmFunc.getBody(), - llvmFunc.end()); - - insertPersistence(typeConverter, rewriter, &getDialect(), op->getLoc(), - procOp, stateTy, llvmFunc, &firstOp); - - // Convert the block argument types after inserting the persistence, as this - // would otherwise interfere with the persistence generation. - if (failed(rewriter.convertRegionTypes(&llvmFunc.getBody(), *typeConverter, - &final))) { - return failure(); - } - - rewriter.eraseOp(op); - - return success(); - } -}; -} // namespace - -namespace { -/// Convert an `llhd.halt` operation to LLVM dialect. This zeroes out all the -/// senses and returns, effectively making the process unable to be invoked -/// again. -struct HaltOpConversion : public ConvertToLLVMPattern { - explicit HaltOpConversion(MLIRContext *ctx, LLVMTypeConverter &typeConverter) - : ConvertToLLVMPattern(HaltOp::getOperationName(), ctx, typeConverter) {} - - LogicalResult - matchAndRewrite(Operation *op, ArrayRef operands, - ConversionPatternRewriter &rewriter) const override { - auto i1Ty = IntegerType::get(rewriter.getContext(), 1); - auto voidPtrTy = LLVM::LLVMPointerType::get(rewriter.getContext()); - auto llvmFunc = op->getParentOfType(); - auto procState = llvmFunc.getArgument(1); - - // Get senses ptr from the process state argument. - auto sensePtrGep = - rewriter.create(op->getLoc(), voidPtrTy, voidPtrTy, - procState, ArrayRef({2})); - auto sensePtr = - rewriter.create(op->getLoc(), voidPtrTy, sensePtrGep); - - // Zero out all the senses flags. - unsigned numSenseEntries = - llvmFunc->getAttrOfType("llhd.argument_count") - .getValue() - .getZExtValue(); - auto zeroB = rewriter.create(op->getLoc(), i1Ty, - rewriter.getBoolAttr(false)); - for (unsigned i = 0; i < numSenseEntries; ++i) { - auto senseElemPtr = rewriter.create( - op->getLoc(), voidPtrTy, i1Ty, sensePtr, ArrayRef({i})); - rewriter.create(op->getLoc(), zeroB, senseElemPtr); - } - - rewriter.replaceOpWithNewOp(op, ValueRange()); - return success(); - } -}; -} // namespace - -namespace { -/// Convert an `llhd.wait` operation to LLVM dialect. This sets the current -/// resume point, sets the observed senses (if present) and schedules the timed -/// wake up (if present). -struct WaitOpConversion : public ConvertToLLVMPattern { - explicit WaitOpConversion(MLIRContext *ctx, LLVMTypeConverter &typeConverter) - : ConvertToLLVMPattern(WaitOp::getOperationName(), ctx, typeConverter) {} - - LogicalResult - matchAndRewrite(Operation *op, ArrayRef operands, - ConversionPatternRewriter &rewriter) const override { - auto waitOp = cast(op); - WaitOpAdaptor transformed(operands, op->getAttrDictionary()); - auto llvmFunc = op->getParentOfType(); - - auto voidTy = getVoidType(); - auto voidPtrTy = getVoidPtrType(); - auto i1Ty = IntegerType::get(rewriter.getContext(), 1); - auto i64Ty = IntegerType::get(rewriter.getContext(), 64); - - // Get the llhdSuspend runtime function. - auto llhdSuspendTy = LLVM::LLVMFunctionType::get( - voidTy, {voidPtrTy, voidPtrTy, i64Ty, i64Ty, i64Ty}); - auto module = op->getParentOfType(); - auto llhdSuspendFunc = getOrInsertFunction(module, rewriter, op->getLoc(), - "llhdSuspend", llhdSuspendTy); - - auto statePtr = llvmFunc.getArgument(0); - auto procState = llvmFunc.getArgument(1); - - // Get senses ptr. - auto sensePtrGep = - rewriter.create(op->getLoc(), voidPtrTy, voidPtrTy, - procState, ArrayRef({2})); - auto sensePtr = - rewriter.create(op->getLoc(), voidPtrTy, sensePtrGep); - - // Reset sense table, if not all signals are observed. - unsigned numSenseEntries = - llvmFunc->getAttrOfType("llhd.argument_count") - .getValue() - .getZExtValue(); - if (waitOp.getObs().size() < numSenseEntries) { - auto zeroB = rewriter.create( - op->getLoc(), i1Ty, rewriter.getBoolAttr(false)); - for (size_t i = 0; i < numSenseEntries; ++i) { - auto senseElemPtr = - rewriter.create(op->getLoc(), voidPtrTy, i1Ty, - sensePtr, ArrayRef({i})); - rewriter.create(op->getLoc(), zeroB, senseElemPtr); - } - } - - // Set sense flags for observed signals. - for (auto observed : transformed.getObs()) { - auto instIndexPtr = - rewriter.create(op->getLoc(), voidPtrTy, i64Ty, observed, - ArrayRef({2})); - auto instIndex = - rewriter.create(op->getLoc(), i64Ty, instIndexPtr) - .getResult(); - auto oneB = rewriter.create(op->getLoc(), i1Ty, - rewriter.getBoolAttr(true)); - auto senseElementPtr = - rewriter.create(op->getLoc(), voidPtrTy, i1Ty, sensePtr, - ArrayRef({instIndex})); - rewriter.create(op->getLoc(), oneB, senseElementPtr); - } - - // Update and store the new resume index in the process state. - // Spawn scheduled event, if present. - if (waitOp.getTime()) { - auto realTime = rewriter.create( - op->getLoc(), transformed.getTime(), 0); - auto delta = rewriter.create( - op->getLoc(), transformed.getTime(), 1); - auto eps = rewriter.create( - op->getLoc(), transformed.getTime(), 2); - - std::array args({statePtr, procState, realTime, delta, eps}); - rewriter.create(op->getLoc(), std::nullopt, - SymbolRefAttr::get(llhdSuspendFunc), args); - } - - rewriter.replaceOpWithNewOp(op, ValueRange()); - return success(); - } -}; -} // namespace - -namespace { -/// Lower an llhd.inst operation to LLVM dialect. This generates malloc calls -/// and allocSignal calls (to store the pointer into the state) for each signal -/// in the instantiated entity. -struct InstOpConversion : public ConvertToLLVMPattern { - explicit InstOpConversion(MLIRContext *ctx, LLVMTypeConverter &typeConverter) - : ConvertToLLVMPattern(InstOp::getOperationName(), ctx, typeConverter) {} - - LogicalResult - matchAndRewrite(Operation *op, ArrayRef operands, - ConversionPatternRewriter &rewriter) const override { - // Get the inst operation. - auto instOp = cast(op); - // Get the parent module. - auto module = op->getParentOfType(); - auto entity = op->getParentOfType(); - - auto voidTy = getVoidType(); - auto voidPtrTy = getVoidPtrType(); - auto i1Ty = IntegerType::get(rewriter.getContext(), 1); - auto i32Ty = IntegerType::get(rewriter.getContext(), 32); - auto i64Ty = IntegerType::get(rewriter.getContext(), 64); - - // Init function signature: (i8* %state) -> void. - auto initFuncTy = LLVM::LLVMFunctionType::get(voidTy, {voidPtrTy}); - auto initFunc = - getOrInsertFunction(module, rewriter, op->getLoc(), "llhd_init", - initFuncTy, /*insertBodyAndTerminator=*/true); - - // Get or insert the malloc function definition. - // Malloc function signature: (i64 %size) -> i8* %pointer. - auto mallocSigFuncTy = LLVM::LLVMFunctionType::get(voidPtrTy, {i64Ty}); - auto mallFunc = getOrInsertFunction(module, rewriter, op->getLoc(), - "malloc", mallocSigFuncTy); - - // Get or insert the allocSignal library call definition. - // allocSignal function signature: (i8* %state, i8* %sig_name, i8* - // %sig_owner, i32 %value) -> i32 %sig_index. - auto allocSigFuncTy = LLVM::LLVMFunctionType::get( - i32Ty, {voidPtrTy, i32Ty, voidPtrTy, voidPtrTy, i64Ty}); - auto sigFunc = getOrInsertFunction(module, rewriter, op->getLoc(), - "allocSignal", allocSigFuncTy); - - // Add information about the elements of an array signal to the state. - // Signature: (i8* state, i32 signalIndex, i32 size, i32 numElements) -> - // void - auto addSigArrElemFuncTy = - LLVM::LLVMFunctionType::get(voidTy, {voidPtrTy, i32Ty, i32Ty, i32Ty}); - auto addSigElemFunc = - getOrInsertFunction(module, rewriter, op->getLoc(), - "addSigArrayElements", addSigArrElemFuncTy); - - // Add information about one element of a struct signal to the state. - // Signature: (i8* state, i32 signalIndex, i32 offset, i32 size) -> void - auto addSigStructElemFuncTy = - LLVM::LLVMFunctionType::get(voidTy, {voidPtrTy, i32Ty, i32Ty, i32Ty}); - auto addSigStructFunc = - getOrInsertFunction(module, rewriter, op->getLoc(), - "addSigStructElement", addSigStructElemFuncTy); - - // Get or insert allocProc library call definition. - auto allocProcFuncTy = - LLVM::LLVMFunctionType::get(voidTy, {voidPtrTy, voidPtrTy, voidPtrTy}); - auto allocProcFunc = getOrInsertFunction(module, rewriter, op->getLoc(), - "allocProc", allocProcFuncTy); - - // Get or insert allocEntity library call definition. - auto allocEntityFuncTy = - LLVM::LLVMFunctionType::get(voidTy, {voidPtrTy, voidPtrTy, voidPtrTy}); - auto allocEntityFunc = getOrInsertFunction( - module, rewriter, op->getLoc(), "allocEntity", allocEntityFuncTy); - - Value initStatePtr = initFunc.getArgument(0); - - // Get a builder for the init function. - OpBuilder initBuilder = - OpBuilder::atBlockTerminator(&initFunc.getBody().getBlocks().front()); - - // Use the instance name to retrieve the instance from the state. - auto ownerName = entity.getName().str() + "." + instOp.getName().str(); - - // Get or create owner name string - Value owner; - auto parentSym = - module.lookupSymbol("instance." + ownerName); - if (!parentSym) { - owner = LLVM::createGlobalString( - op->getLoc(), initBuilder, "instance." + ownerName, ownerName + '\0', - LLVM::Linkage::Internal); - parentSym = module.lookupSymbol("instance." + ownerName); - } else { - owner = - getGlobalString(op->getLoc(), initBuilder, typeConverter, parentSym); - } - - // Handle entity instantiation. - if (auto child = module.lookupSymbol(instOp.getCallee())) { - auto regStateTy = getRegStateTy(&getDialect(), child.getOperation()); - - // Get reg state size. - auto regNull = initBuilder.create(op->getLoc(), voidPtrTy); - auto regGep = - initBuilder.create(op->getLoc(), voidPtrTy, regStateTy, - regNull, ArrayRef({1})); - auto regSize = - initBuilder.create(op->getLoc(), i64Ty, regGep); - - // Malloc reg state. - auto regMall = initBuilder - .create(op->getLoc(), voidPtrTy, - SymbolRefAttr::get(mallFunc), - ArrayRef({regSize})) - .getResult(); - auto zeroB = initBuilder.create( - op->getLoc(), i1Ty, rewriter.getBoolAttr(false)); - - // Zero-initialize reg state entries. - for (size_t i = 0, - e = cast(regStateTy).getBody().size(); - i < e; ++i) { - size_t f = cast( - cast(regStateTy).getBody()[i]) - .getNumElements(); - for (size_t j = 0; j < f; ++j) { - auto regGep = initBuilder.create( - op->getLoc(), voidPtrTy, regStateTy, regMall, - ArrayRef({0, i, j})); - initBuilder.create(op->getLoc(), zeroB, regGep); - } - } - - // Add reg state pointer to global state. - initBuilder.create( - op->getLoc(), std::nullopt, SymbolRefAttr::get(allocEntityFunc), - ArrayRef({initStatePtr, owner, regMall})); - - // Index of the signal in the entity's signal table. - int initCounter = 0; - // Walk over the entity and generate mallocs for each one of its signals. - WalkResult sigWalkResult = child.walk([&](SigOp op) -> WalkResult { - // if (auto sigOp = dyn_cast(op)) { - auto underlyingTy = typeConverter->convertType(op.getInit().getType()); - // Get index constant of the signal in the entity's signal table. - auto indexConst = initBuilder.create( - op.getLoc(), i32Ty, rewriter.getI32IntegerAttr(initCounter)); - initCounter++; - - // Clone and insert the operation that defines the signal's init - // operand (assmued to be a constant/array op) - IRMapping mapping; - Value initDef = recursiveCloneInit(initBuilder, mapping, op.getInit()); - - if (!initDef) - return WalkResult::interrupt(); - - Value initDefCast = typeConverter->materializeTargetConversion( - initBuilder, initDef.getLoc(), - typeConverter->convertType(initDef.getType()), initDef); - - // Compute the required space to malloc. - auto twoC = initBuilder.create( - op.getLoc(), i64Ty, rewriter.getI32IntegerAttr(2)); - auto nullPtr = initBuilder.create(op.getLoc(), voidPtrTy); - auto sizeGep = initBuilder.create( - op.getLoc(), voidPtrTy, underlyingTy, nullPtr, - ArrayRef({1})); - auto size = - initBuilder.create(op.getLoc(), i64Ty, sizeGep); - // Malloc double the required space to make sure signal - // shifts do not segfault. - auto mallocSize = - initBuilder.create(op.getLoc(), i64Ty, size, twoC); - std::array margs({mallocSize}); - auto mall = - initBuilder - .create(op.getLoc(), voidPtrTy, - SymbolRefAttr::get(mallFunc), margs) - .getResult(); - - // Store the initial value. - initBuilder.create(op.getLoc(), initDefCast, mall); - - // Get the amount of bytes required to represent an integer underlying - // type. Use the whole size of the type if not an integer. - Value passSize; - if (auto intTy = dyn_cast(underlyingTy)) { - auto byteWidth = llvm::divideCeil(intTy.getWidth(), 8); - passSize = initBuilder.create( - op.getLoc(), i64Ty, rewriter.getI64IntegerAttr(byteWidth)); - } else { - passSize = size; - } - - std::array args( - {initStatePtr, indexConst, owner, mall, passSize}); - auto sigIndex = - initBuilder - .create(op.getLoc(), i32Ty, - SymbolRefAttr::get(sigFunc), args) - .getResult(); - - // Add structured underlying type information. - if (auto arrayTy = dyn_cast(underlyingTy)) { - auto numElements = initBuilder.create( - op.getLoc(), i32Ty, - rewriter.getI32IntegerAttr(arrayTy.getNumElements())); - - // Get element size. - auto null = initBuilder.create(op.getLoc(), voidPtrTy); - auto gepFirst = initBuilder.create( - op.getLoc(), voidPtrTy, arrayTy, null, - ArrayRef({0, 1})); - auto toInt = initBuilder.create(op.getLoc(), i32Ty, - gepFirst); - - // Add information to the state. - initBuilder.create( - op.getLoc(), std::nullopt, SymbolRefAttr::get(addSigElemFunc), - ArrayRef({initStatePtr, sigIndex, toInt, numElements})); - } else if (auto structTy = - dyn_cast(underlyingTy)) { - auto null = initBuilder.create(op.getLoc(), voidPtrTy); - for (size_t i = 0, e = structTy.getBody().size(); i < e; ++i) { - // Get pointer offset. - auto gepElem = initBuilder.create( - op.getLoc(), voidPtrTy, structTy, null, - ArrayRef({0, i})); - auto elemToInt = initBuilder.create( - op.getLoc(), i32Ty, gepElem); - - // Get element size. - auto elemNull = - initBuilder.create(op.getLoc(), voidPtrTy); - auto gepElemSize = initBuilder.create( - op.getLoc(), voidPtrTy, structTy.getBody()[i], elemNull, - ArrayRef({1})); - auto elemSizeToInt = initBuilder.create( - op.getLoc(), i32Ty, gepElemSize); - - // Add information to the state. - initBuilder.create( - op.getLoc(), std::nullopt, SymbolRefAttr::get(addSigStructFunc), - ArrayRef( - {initStatePtr, sigIndex, elemToInt, elemSizeToInt})); - } - } - return WalkResult::advance(); - }); - - if (sigWalkResult.wasInterrupted()) - return failure(); - - } else if (auto proc = module.lookupSymbol(instOp.getCallee())) { - // Handle process instantiation. - auto sensesTy = LLVM::LLVMArrayType::get(i1Ty, proc.getNumArguments()); - auto procStateTy = LLVM::LLVMStructType::getLiteral( - rewriter.getContext(), - {i32Ty, i32Ty, voidPtrTy /*ptr(sensesTy)*/, - getProcPersistenceTy(&getDialect(), typeConverter, proc)}); - - auto zeroC = initBuilder.create( - op->getLoc(), i32Ty, rewriter.getI32IntegerAttr(0)); - - // Malloc space for the process state. - auto procStateNullPtr = - initBuilder.create(op->getLoc(), voidPtrTy); - auto procStateGep = initBuilder.create( - op->getLoc(), voidPtrTy, procStateTy, procStateNullPtr, - ArrayRef({1})); - auto procStateSize = initBuilder.create( - op->getLoc(), i64Ty, procStateGep); - std::array procStateMArgs({procStateSize}); - auto procStateMall = initBuilder - .create( - op->getLoc(), voidPtrTy, - SymbolRefAttr::get(mallFunc), procStateMArgs) - .getResult(); - - // Store the initial resume index. - auto resumeGep = initBuilder.create( - op->getLoc(), voidPtrTy, procStateTy, procStateMall, - ArrayRef({0, 1})); - initBuilder.create(op->getLoc(), zeroC, resumeGep); - - // Malloc space for the senses table. - auto sensesNullPtr = - initBuilder.create(op->getLoc(), voidPtrTy); - auto sensesGep = initBuilder.create( - op->getLoc(), voidPtrTy, sensesTy, sensesNullPtr, - ArrayRef({1})); - auto sensesSize = - initBuilder.create(op->getLoc(), i64Ty, sensesGep); - std::array senseMArgs({sensesSize}); - auto sensesMall = - initBuilder - .create(op->getLoc(), voidPtrTy, - SymbolRefAttr::get(mallFunc), senseMArgs) - .getResult(); - - // Set all initial senses to 1. - auto oneB = initBuilder.create( - op->getLoc(), i1Ty, rewriter.getBoolAttr(true)); - for (size_t i = 0, e = sensesTy.getNumElements(); i < e; ++i) { - auto senseGep = initBuilder.create( - op->getLoc(), voidPtrTy, i1Ty, sensesMall, - ArrayRef({i})); - initBuilder.create(op->getLoc(), oneB, senseGep); - } - - // Store the senses pointer in the process state. - auto procStateSensesPtr = initBuilder.create( - op->getLoc(), voidPtrTy, procStateTy, procStateMall, - ArrayRef({0, 2})); - initBuilder.create(op->getLoc(), sensesMall, - procStateSensesPtr); - - std::array allocProcArgs({initStatePtr, owner, procStateMall}); - initBuilder.create(op->getLoc(), std::nullopt, - SymbolRefAttr::get(allocProcFunc), - allocProcArgs); - } - - rewriter.eraseOp(op); - return success(); - } -}; -} // namespace - -//===----------------------------------------------------------------------===// -// Signal conversions -//===----------------------------------------------------------------------===// - -namespace { -/// Convert an `llhd.sig` operation to LLVM dialect. The i-th signal of an -/// entity get's lowered to a load of the i-th element of the signal table, -/// passed as an argument. -struct SigOpConversion : public ConvertToLLVMPattern { - explicit SigOpConversion(MLIRContext *ctx, LLVMTypeConverter &typeConverter, - size_t &sigCounter) - : ConvertToLLVMPattern(llhd::SigOp::getOperationName(), ctx, - typeConverter), - sigCounter(sigCounter) {} - - LogicalResult - matchAndRewrite(Operation *op, ArrayRef operands, - ConversionPatternRewriter &rewriter) const override { - // Get the adapted opreands. - SigOpAdaptor transformed(operands); - - // Collect the used llvm types. - auto voidPtrTy = LLVM::LLVMPointerType::get(rewriter.getContext()); - auto sigTy = getLLVMSigType(&getDialect()); - - // Get the signal table pointer from the arguments. - Value sigTablePtr = op->getParentOfType().getArgument(2); - - // Get the index in the signal table and increase counter. - // Insert a gep to the signal index in the signal table argument. - rewriter.replaceOpWithNewOp(op, voidPtrTy, sigTy, sigTablePtr, - LLVM::GEPArg(sigCounter)); - ++sigCounter; - - return success(); - } - -private: - size_t &sigCounter; -}; -} // namespace - -namespace { -/// Convert an `llhd.prb` operation to LLVM dialect. The result is a library -/// call to the -/// `@probe_signal` function. The signal details are then extracted and used to -/// load the final probe value. -struct PrbOpConversion : public ConvertToLLVMPattern { - explicit PrbOpConversion(MLIRContext *ctx, LLVMTypeConverter &typeConverter) - : ConvertToLLVMPattern(llhd::PrbOp::getOperationName(), ctx, - typeConverter) {} - - LogicalResult - matchAndRewrite(Operation *op, ArrayRef operands, - ConversionPatternRewriter &rewriter) const override { - // Get the adapted operands. - PrbOpAdaptor transformed(operands); - // Get the prb operation. - auto prbOp = cast(op); - - // Collect the used llvm types. - auto resTy = prbOp.getType(); - auto finalTy = typeConverter->convertType(resTy); - - // Get the signal details from the signal struct. - auto sigDetail = getSignalDetail(rewriter, &getDialect(), op->getLoc(), - transformed.getSignal()); - - if (isa(resTy)) { - // Get the amount of bytes to load. An extra byte is always loaded to - // cover the case where a subsignal spans halfway in the last byte. - int resWidth = resTy.getIntOrFloatBitWidth(); - int loadWidth = (llvm::divideCeil(resWidth, 8) + 1) * 8; - auto loadTy = IntegerType::get(rewriter.getContext(), loadWidth); - - auto loadSig = - rewriter.create(op->getLoc(), loadTy, sigDetail[0]); - - // Shift the loaded value by the offset and truncate to the final width. - auto trOff = adjustBitWidth(op->getLoc(), rewriter, loadTy, sigDetail[1]); - auto shifted = - rewriter.create(op->getLoc(), loadTy, loadSig, trOff); - rewriter.replaceOpWithNewOp(op, finalTy, shifted); - - return success(); - } - - if (isa(resTy)) { - rewriter.replaceOpWithNewOp(op, finalTy, sigDetail[0]); - - return success(); - } - - return failure(); - } -}; -} // namespace - -namespace { -/// Convert an `llhd.drv` operation to LLVM dialect. The result is a library -/// call to the -/// `@driveSignal` function, which declaration is inserted at the beginning of -/// the module if missing. The required arguments are either generated or -/// fetched. -struct DrvOpConversion : public ConvertToLLVMPattern { - explicit DrvOpConversion(MLIRContext *ctx, LLVMTypeConverter &typeConverter) - : ConvertToLLVMPattern(llhd::DrvOp::getOperationName(), ctx, - typeConverter) {} - - LogicalResult - matchAndRewrite(Operation *op, ArrayRef operands, - ConversionPatternRewriter &rewriter) const override { - // Get the adapted operands. - DrvOpAdaptor transformed(operands); - // Get the drive operation. - auto drvOp = cast(op); - // Get the parent module. - auto module = op->getParentOfType(); - - // Collect used llvm types. - auto voidTy = getVoidType(); - auto voidPtrTy = getVoidPtrType(); - auto i1Ty = IntegerType::get(rewriter.getContext(), 1); - auto i32Ty = IntegerType::get(rewriter.getContext(), 32); - auto i64Ty = IntegerType::get(rewriter.getContext(), 64); - - // Get or insert the drive library call. - auto drvFuncTy = LLVM::LLVMFunctionType::get( - voidTy, {voidPtrTy, voidPtrTy, voidPtrTy, i64Ty, i64Ty, i64Ty, i64Ty}); - auto drvFunc = getOrInsertFunction(module, rewriter, op->getLoc(), - "driveSignal", drvFuncTy); - - // Get the state pointer from the function arguments. - Value statePtr = op->getParentOfType().getArgument(0); - - // Get signal width. - Value sigWidth; - auto underlyingTy = drvOp.getValue().getType(); - if (isArrayOrStruct(underlyingTy)) { - auto underlyingTyConv = typeConverter->convertType(underlyingTy); - auto eightC = rewriter.create( - op->getLoc(), i64Ty, rewriter.getI64IntegerAttr(8)); - auto nullPtr = rewriter.create(op->getLoc(), voidPtrTy); - auto gepOne = rewriter.create(op->getLoc(), voidPtrTy, - underlyingTyConv, nullPtr, - ArrayRef({1})); - auto toInt = - rewriter.create(op->getLoc(), i64Ty, gepOne); - sigWidth = rewriter.create(op->getLoc(), toInt, eightC); - } else { - sigWidth = rewriter.create( - op->getLoc(), i64Ty, - rewriter.getI64IntegerAttr(underlyingTy.getIntOrFloatBitWidth())); - } - - // Insert enable comparison. Skip if the enable operand is 0. - if (auto gate = drvOp.getEnable()) { - auto block = op->getBlock(); - auto continueBlock = - rewriter.splitBlock(rewriter.getInsertionBlock(), op->getIterator()); - auto drvBlock = rewriter.createBlock(continueBlock); - rewriter.setInsertionPointToEnd(drvBlock); - rewriter.create(op->getLoc(), ValueRange(), continueBlock); - - rewriter.setInsertionPointToEnd(block); - auto oneC = rewriter.create( - op->getLoc(), i1Ty, rewriter.getI16IntegerAttr(1)); - auto cmp = rewriter.create( - op->getLoc(), LLVM::ICmpPredicate::eq, transformed.getEnable(), oneC); - rewriter.create(op->getLoc(), cmp, drvBlock, - continueBlock); - - rewriter.setInsertionPointToStart(drvBlock); - } - - Type valTy = typeConverter->convertType(transformed.getValue().getType()); - Value castVal = typeConverter->materializeTargetConversion( - rewriter, transformed.getValue().getLoc(), valTy, - transformed.getValue()); - - auto oneConst = rewriter.create( - op->getLoc(), i32Ty, rewriter.getI32IntegerAttr(1)); - - // This assumes that alloca does always allocate full bytes (round up to a - // multiple of 8 bits). - auto alloca = rewriter.create(op->getLoc(), voidPtrTy, - valTy, oneConst, 4); - rewriter.create(op->getLoc(), castVal, alloca); - - // Get the time values. - auto realTime = rewriter.create( - op->getLoc(), transformed.getTime(), 0); - auto delta = rewriter.create( - op->getLoc(), transformed.getTime(), 1); - auto eps = rewriter.create(op->getLoc(), - transformed.getTime(), 2); - - // Define the driveSignal library call arguments. - std::array args({statePtr, transformed.getSignal(), alloca, - sigWidth, realTime, delta, eps}); - // Create the library call. - rewriter.create(op->getLoc(), std::nullopt, - SymbolRefAttr::get(drvFunc), args); - - rewriter.eraseOp(op); - return success(); - } -}; -} // namespace - -namespace { -/// Convert an `llhd.reg` operation to LLVM dialect. This generates a series of -/// comparisons (blocks) that end up driving the signal with the arguments of -/// the first matching trigger from the trigger list. -struct RegOpConversion : public ConvertToLLVMPattern { - explicit RegOpConversion(MLIRContext *ctx, LLVMTypeConverter &typeConverter, - size_t ®Counter) - : ConvertToLLVMPattern(RegOp::getOperationName(), ctx, typeConverter), - regCounter(regCounter) {} - - LogicalResult - matchAndRewrite(Operation *op, ArrayRef operands, - ConversionPatternRewriter &rewriter) const override { - auto regOp = cast(op); - RegOpAdaptor transformed(operands, op->getAttrDictionary()); - - auto voidPtrTy = LLVM::LLVMPointerType::get(rewriter.getContext()); - auto i1Ty = IntegerType::get(rewriter.getContext(), 1); - auto func = op->getParentOfType(); - - // Retrieve and update previous trigger values for rising/falling edge - // detection. - size_t triggerIndex = 0; - SmallVector prevTriggers; - for (int i = 0, e = regOp.getValues().size(); i < e; ++i) { - auto mode = regOp.getRegModeAt(i); - if (mode == RegMode::both || mode == RegMode::fall || - mode == RegMode::rise) { - auto gep = rewriter.create( - op->getLoc(), voidPtrTy, i1Ty, func.getArgument(1), - ArrayRef({0, regCounter, triggerIndex++})); - prevTriggers.push_back( - rewriter.create(op->getLoc(), i1Ty, gep)); - rewriter.create(op->getLoc(), - transformed.getTriggers()[i], gep); - } - } - - // Create blocks for drive and continue. - auto block = op->getBlock(); - auto continueBlock = block->splitBlock(op); - - auto drvBlock = rewriter.createBlock(continueBlock); - auto valArg = drvBlock->addArgument(transformed.getValues()[0].getType(), - transformed.getValues()[0].getLoc()); - auto delayArg = drvBlock->addArgument(transformed.getDelays()[0].getType(), - transformed.getDelays()[0].getLoc()); - auto gateArg = drvBlock->addArgument(i1Ty, rewriter.getUnknownLoc()); - - // Create a drive with the block arguments. - rewriter.setInsertionPointToStart(drvBlock); - rewriter.create(op->getLoc(), regOp.getSignal(), valArg, delayArg, - gateArg); - rewriter.create(op->getLoc(), ValueRange(), continueBlock); - - int j = prevTriggers.size() - 1; - // Create a comparison block for each of the reg tuples. - for (int i = regOp.getValues().size() - 1, e = i; i >= 0; --i) { - auto cmpBlock = rewriter.createBlock(block->getNextNode()); - rewriter.setInsertionPointToStart(cmpBlock); - - Value gate; - if (regOp.hasGate(i)) { - gate = regOp.getGateAt(i); - } else { - gate = rewriter.create(op->getLoc(), i1Ty, - rewriter.getBoolAttr(true)); - } - - auto drvArgs = std::array( - {transformed.getValues()[i], transformed.getDelays()[i], gate}); - - RegMode mode = regOp.getRegModeAt(i); - - // Create comparison constants for all modes other than both. - Value rhs; - if (mode == RegMode::low || mode == RegMode::fall) { - rhs = rewriter.create(op->getLoc(), i1Ty, - rewriter.getBoolAttr(false)); - } else if (mode == RegMode::high || mode == RegMode::rise) { - rhs = rewriter.create(op->getLoc(), i1Ty, - rewriter.getBoolAttr(true)); - } - - // Create comparison for non-both modes. - Value comp; - if (rhs) - comp = - rewriter.create(op->getLoc(), LLVM::ICmpPredicate::eq, - transformed.getTriggers()[i], rhs); - - // Create comparison for modes needing more than one state of the trigger. - Value brCond; - if (mode == RegMode::rise || mode == RegMode::fall || - mode == RegMode::both) { - - auto cmpPrev = rewriter.create( - op->getLoc(), LLVM::ICmpPredicate::ne, transformed.getTriggers()[i], - prevTriggers[j--]); - if (mode == RegMode::both) - brCond = cmpPrev; - else - brCond = - rewriter.create(op->getLoc(), i1Ty, comp, cmpPrev); - } else { - brCond = comp; - } - - Block *nextBlock; - nextBlock = cmpBlock->getNextNode(); - // Don't go to next block for last comparison's false branch (skip the - // drive block). - if (i == e) - nextBlock = continueBlock; - - rewriter.create(op->getLoc(), brCond, drvBlock, drvArgs, - nextBlock, ValueRange()); - } - - rewriter.setInsertionPointToEnd(block); - rewriter.create(op->getLoc(), ArrayRef(), - block->getNextNode()); - - rewriter.eraseOp(op); - - ++regCounter; - - return success(); - } - -private: - size_t ®Counter; -}; // namespace -} // namespace - -//===----------------------------------------------------------------------===// -// Value creation conversions -//===----------------------------------------------------------------------===// - -namespace { -/// Lower an LLHD constant operation to an equivalent LLVM dialect constant -/// operation. -struct ConstantTimeOpConversion : public ConvertToLLVMPattern { - explicit ConstantTimeOpConversion(MLIRContext *ctx, - LLVMTypeConverter &typeConverter) - : ConvertToLLVMPattern(llhd::ConstantTimeOp::getOperationName(), ctx, - typeConverter) {} - - LogicalResult - matchAndRewrite(Operation *op, ArrayRef operand, - ConversionPatternRewriter &rewriter) const override { - // Get the ConstOp. - auto constOp = cast(op); - // Get the constant's attribute. - TimeAttr timeAttr = constOp.getValueAttr(); - // Handle the time const special case: create a new array containing the - // three time values. - auto timeTy = typeConverter->convertType(constOp.getResult().getType()); - - // Convert real-time element to ps. - llvm::StringMap map = { - {"s", 12}, {"ms", 9}, {"us", 6}, {"ns", 3}, {"ps", 0}}; - uint64_t adjusted = - std::pow(10, map[timeAttr.getTimeUnit()]) * timeAttr.getTime(); - - // Get sub-steps. - uint64_t delta = timeAttr.getDelta(); - uint64_t eps = timeAttr.getEpsilon(); - - // Create time constant. - auto denseAttr = - DenseElementsAttr::get(RankedTensorType::get(3, rewriter.getI64Type()), - {adjusted, delta, eps}); - rewriter.replaceOpWithNewOp(op, timeTy, denseAttr); - return success(); - } -}; -} // namespace - -//===----------------------------------------------------------------------===// -// Extraction operation conversions -//===----------------------------------------------------------------------===// - -namespace { -/// Convert a DynExtractSliceOp to LLVM dialect. -struct SigArraySliceOpConversion - : public ConvertOpToLLVMPattern { - using ConvertOpToLLVMPattern::ConvertOpToLLVMPattern; - - LogicalResult - matchAndRewrite(llhd::SigArraySliceOp op, OpAdaptor adaptor, - ConversionPatternRewriter &rewriter) const override { - - Type llvmArrTy = typeConverter->convertType(op.getInputArrayType()); - Type inputTy = typeConverter->convertType(op.getInput().getType()); - Type lowIndexTy = typeConverter->convertType(op.getLowIndex().getType()); - Value castInput = typeConverter->materializeTargetConversion( - rewriter, op->getLoc(), inputTy, op.getInput()); - Value castLowIndex = typeConverter->materializeTargetConversion( - rewriter, op->getLoc(), lowIndexTy, op.getLowIndex()); - - auto sigDetail = getSignalDetail(rewriter, &getDialect(), op->getLoc(), - castInput, /*extractIndices=*/true); - - auto adjustedPtr = shiftArraySigPointer(op->getLoc(), rewriter, llvmArrTy, - sigDetail[0], castLowIndex); - rewriter.replaceOp(op, createSubSig(&getDialect(), rewriter, op->getLoc(), - sigDetail, adjustedPtr, sigDetail[1])); - return success(); - } -}; -} // namespace - -namespace { -/// Convert a DynExtractSliceOp to LLVM dialect. -struct SigExtractOpConversion - : public ConvertOpToLLVMPattern { - using ConvertOpToLLVMPattern::ConvertOpToLLVMPattern; - - LogicalResult - matchAndRewrite(llhd::SigExtractOp op, OpAdaptor adaptor, - ConversionPatternRewriter &rewriter) const override { - - Type inputTy = typeConverter->convertType(op.getInput().getType()); - Type lowBitTy = typeConverter->convertType(op.getLowBit().getType()); - Value castInput = typeConverter->materializeTargetConversion( - rewriter, op->getLoc(), inputTy, op.getInput()); - Value castLowBit = typeConverter->materializeTargetConversion( - rewriter, op->getLoc(), lowBitTy, op.getLowBit()); - - auto sigDetail = getSignalDetail(rewriter, &getDialect(), op->getLoc(), - castInput, /*extractIndices=*/true); - - auto zextStart = adjustBitWidth(op->getLoc(), rewriter, - rewriter.getI64Type(), castLowBit); - // Adjust the slice starting point by the signal's offset. - auto adjustedStart = - rewriter.create(op->getLoc(), sigDetail[1], zextStart); - - auto adjusted = shiftIntegerSigPointer( - op->getLoc(), &getDialect(), rewriter, sigDetail[0], adjustedStart); - // Create a new subsignal with the new pointer and offset. - rewriter.replaceOp(op, createSubSig(&getDialect(), rewriter, op->getLoc(), - sigDetail, adjusted.first, - adjusted.second)); - return success(); - } -}; -} // namespace - -namespace { -/// Convert -struct SigStructExtractOpConversion - : public ConvertOpToLLVMPattern { - using ConvertOpToLLVMPattern< - llhd::SigStructExtractOp>::ConvertOpToLLVMPattern; - - LogicalResult - matchAndRewrite(llhd::SigStructExtractOp op, OpAdaptor adaptor, - ConversionPatternRewriter &rewriter) const override { - - Type llvmStructTy = typeConverter->convertType(op.getStructType()); - Type inputTy = typeConverter->convertType(op.getInput().getType()); - Value castInput = typeConverter->materializeTargetConversion( - rewriter, op->getLoc(), inputTy, op.getInput()); - - std::vector sigDetail = - getSignalDetail(rewriter, &getDialect(), op->getLoc(), castInput, - /*extractIndices=*/true); - - uint32_t index = HWToLLVMEndianessConverter::llvmIndexOfStructField( - op.getStructType(), op.getField()); - - Value adjusted = shiftStructuredSigPointer( - op->getLoc(), rewriter, llvmStructTy, sigDetail[0], index); - - rewriter.replaceOp(op, createSubSig(&getDialect(), rewriter, op->getLoc(), - sigDetail, adjusted, sigDetail[1])); - - return success(); - } -}; -} // namespace - -namespace { -/// Convert a DynExtractElementOp to LLVM dialect. -struct SigArrayGetOpConversion - : public ConvertOpToLLVMPattern { - using ConvertOpToLLVMPattern::ConvertOpToLLVMPattern; - - LogicalResult - matchAndRewrite(llhd::SigArrayGetOp op, OpAdaptor adaptor, - ConversionPatternRewriter &rewriter) const override { - - auto llvmArrTy = typeConverter->convertType(op.getArrayType()); - Type inputTy = typeConverter->convertType(op.getInput().getType()); - Type indexTy = typeConverter->convertType(op.getIndex().getType()); - Value castInput = typeConverter->materializeTargetConversion( - rewriter, op->getLoc(), inputTy, op.getInput()); - Value castIndex = typeConverter->materializeTargetConversion( - rewriter, op->getLoc(), indexTy, op.getIndex()); - - auto sigDetail = getSignalDetail(rewriter, &getDialect(), op->getLoc(), - castInput, /*extractIndices=*/true); - - auto adjustedPtr = shiftArraySigPointer(op->getLoc(), rewriter, llvmArrTy, - sigDetail[0], castIndex); - rewriter.replaceOp(op, createSubSig(&getDialect(), rewriter, op->getLoc(), - sigDetail, adjustedPtr, sigDetail[1])); - - return success(); - } -}; -} // namespace - -//===----------------------------------------------------------------------===// -// Memory operations -//===----------------------------------------------------------------------===// - -namespace { -/// Lower a `llhd.var` operation to the LLVM dialect. This results in an alloca, -/// followed by storing the initial value. -struct VarOpConversion : ConvertToLLVMPattern { - explicit VarOpConversion(MLIRContext *ctx, LLVMTypeConverter &typeConverter) - : ConvertToLLVMPattern(VarOp::getOperationName(), ctx, typeConverter) {} - - LogicalResult - matchAndRewrite(Operation *op, ArrayRef operands, - ConversionPatternRewriter &rewriter) const override { - VarOpAdaptor transformed(operands); - - auto i32Ty = IntegerType::get(rewriter.getContext(), 32); - Type initTy = typeConverter->convertType(transformed.getInit().getType()); - - auto oneC = rewriter.create( - op->getLoc(), i32Ty, rewriter.getI32IntegerAttr(1)); - auto alloca = rewriter.create( - op->getLoc(), LLVM::LLVMPointerType::get(rewriter.getContext()), initTy, - oneC, 4); - rewriter.create(op->getLoc(), transformed.getInit(), alloca); - rewriter.replaceOp(op, alloca.getResult()); - return success(); - } -}; -} // namespace - -namespace { -/// Convert an `llhd.store` operation to LLVM. This lowers the store -/// one-to-one as an LLVM store, but with the operands flipped. -struct StoreOpConversion : ConvertToLLVMPattern { - explicit StoreOpConversion(MLIRContext *ctx, LLVMTypeConverter &typeConverter) - : ConvertToLLVMPattern(llhd::StoreOp::getOperationName(), ctx, - typeConverter) {} - - LogicalResult - matchAndRewrite(Operation *op, ArrayRef operands, - ConversionPatternRewriter &rewriter) const override { - llhd::StoreOpAdaptor transformed(operands); - - rewriter.replaceOpWithNewOp(op, transformed.getValue(), - transformed.getPointer()); - - return success(); - } -}; -} // namespace - -using LoadOpConversion = - OneToOneConvertToLLVMPattern; - -//===----------------------------------------------------------------------===// -// Pass initialization -//===----------------------------------------------------------------------===// - -namespace { -struct LLHDToLLVMLoweringPass - : public circt::impl::ConvertLLHDToLLVMBase { - void runOnOperation() override; -}; -} // namespace - -void circt::populateLLHDToLLVMConversionPatterns(LLVMTypeConverter &converter, - RewritePatternSet &patterns, - size_t &sigCounter, - size_t ®Counter) { - MLIRContext *ctx = converter.getDialect()->getContext(); - - // Value creation conversion patterns. - patterns.add(ctx, converter); - - // Extract conversion patterns. - patterns.add( - converter); - - // Unit conversion patterns. - patterns.add(ctx, - converter); - patterns.add(ctx, converter, sigCounter, regCounter); - - // Signal conversion patterns. - patterns.add(ctx, converter); - patterns.add(ctx, converter, sigCounter); - patterns.add(ctx, converter, regCounter); - - // Memory conversion patterns. - patterns.add(ctx, converter); - patterns.add(converter); -} - -void circt::populateLLHDToLLVMTypeConversions(LLVMTypeConverter &converter) { - converter.addConversion( - [&](SigType sig) { return convertSigType(sig, converter); }); - converter.addConversion( - [&](TimeType time) { return convertTimeType(time, converter); }); - converter.addConversion( - [&](PtrType ptr) { return convertPtrType(ptr, converter); }); -} - -void LLHDToLLVMLoweringPass::runOnOperation() { - Namespace globals; - SymbolCache cache; - cache.addDefinitions(getOperation()); - globals.add(cache); - - // Keep a counter to infer a signal's index in his entity's signal table. - size_t sigCounter = 0; - - // Keep a counter to infer a reg's index in his entity. - size_t regCounter = 0; - - RewritePatternSet patterns(&getContext()); - auto converter = mlir::LLVMTypeConverter(&getContext()); - populateLLHDToLLVMTypeConversions(converter); - - // Also populate with HW type conversions - populateHWToLLVMTypeConversions(converter); - - // Apply a partial conversion first, lowering only the instances, to generate - // the init function. - patterns.add(&getContext(), converter); - - LLVMConversionTarget target(getContext()); - target.addIllegalOp(); - cf::populateControlFlowToLLVMConversionPatterns(converter, patterns); - arith::populateArithToLLVMConversionPatterns(converter, patterns); - - // Apply the partial conversion. - if (failed( - applyPartialConversion(getOperation(), target, std::move(patterns)))) - return signalPassFailure(); - patterns.clear(); - - // Setup the full conversion. - populateFuncToLLVMConversionPatterns(converter, patterns); - populateLLHDToLLVMConversionPatterns(converter, patterns, sigCounter, - regCounter); - - // Populate with HW and Comb conversion patterns - DenseMap, LLVM::GlobalOp> constAggregateGlobalsMap; - populateHWToLLVMConversionPatterns(converter, patterns, globals, - constAggregateGlobalsMap); - populateCombToLLVMConversionPatterns(converter, patterns); - populateCombToArithConversionPatterns(converter, patterns); - arith::populateArithToLLVMConversionPatterns(converter, patterns); - - target.addLegalOp(); - - if (failed(applyFullConversion(getOperation(), target, std::move(patterns)))) - return signalPassFailure(); -} - -/// Create an LLHD to LLVM conversion pass. -std::unique_ptr> circt::createConvertLLHDToLLVMPass() { - return std::make_unique(); -} diff --git a/lib/Dialect/LLHD/CMakeLists.txt b/lib/Dialect/LLHD/CMakeLists.txt index e2b2ca840d..9f57627c32 100644 --- a/lib/Dialect/LLHD/CMakeLists.txt +++ b/lib/Dialect/LLHD/CMakeLists.txt @@ -1,5 +1,2 @@ add_subdirectory(IR) -if(CIRCT_LLHD_SIM_ENABLED) - add_subdirectory(Simulator) -endif() add_subdirectory(Transforms) diff --git a/lib/Dialect/LLHD/Simulator/CMakeLists.txt b/lib/Dialect/LLHD/Simulator/CMakeLists.txt deleted file mode 100644 index e5cef915fd..0000000000 --- a/lib/Dialect/LLHD/Simulator/CMakeLists.txt +++ /dev/null @@ -1,38 +0,0 @@ -set(LLVM_OPTIONAL_SOURCES - State.cpp - Engine.cpp - signals-runtime-wrappers.cpp - Trace.cpp -) - -add_circt_library(CIRCTLLHDSimState - State.cpp -) - -add_circt_library(CIRCTLLHDSimTrace - Trace.cpp - - LINK_LIBS PUBLIC - CIRCTLLHDSimState -) - -add_circt_library(circt-llhd-signals-runtime-wrappers SHARED - signals-runtime-wrappers.cpp - - LINK_LIBS PUBLIC - CIRCTLLHDSimState -) -set_target_properties(circt-llhd-signals-runtime-wrappers - PROPERTIES CXX_VISIBILITY_PRESET "default") - -add_circt_library(CIRCTLLHDSimEngine - Engine.cpp - - LINK_LIBS PUBLIC - CIRCTLLHD - CIRCTLLHDToLLVM - CIRCTLLHDSimState - CIRCTLLHDSimTrace - circt-llhd-signals-runtime-wrappers - MLIRExecutionEngine - ) diff --git a/lib/Dialect/LLHD/Simulator/Engine.cpp b/lib/Dialect/LLHD/Simulator/Engine.cpp deleted file mode 100644 index be5f246469..0000000000 --- a/lib/Dialect/LLHD/Simulator/Engine.cpp +++ /dev/null @@ -1,315 +0,0 @@ -//===- Engine.cpp - Simulator Engine class implementation -------*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// This file implements the Engine class. -// -//===----------------------------------------------------------------------===// - -#include "circt/Dialect/LLHD/Simulator/Engine.h" -#include "circt/Conversion/LLHDToLLVM.h" - -#include "mlir/ExecutionEngine/ExecutionEngine.h" -#include "mlir/IR/Builders.h" - -#include "llvm/Support/TargetSelect.h" - -using namespace circt::llhd::sim; - -Engine::Engine( - llvm::raw_ostream &out, ModuleOp module, - llvm::function_ref mlirTransformer, - llvm::function_ref llvmTransformer, - std::string root, TraceMode tm, ArrayRef sharedLibPaths) - : out(out), root(root), traceMode(tm) { - state = std::make_unique(); - state->root = root + '.' + root; - - buildLayout(module); - - auto rootEntity = module.lookupSymbol(root); - - // Insert explicit instantiation of the design root. - OpBuilder insertInst = - OpBuilder::atBlockEnd(&rootEntity.getBody().getBlocks().front()); - insertInst.create(rootEntity.getBlocks().front().back().getLoc(), - std::nullopt, root, root, ArrayRef(), - ArrayRef()); - - if (failed(mlirTransformer(module))) { - llvm::errs() << "failed to apply the MLIR passes\n"; - exit(EXIT_FAILURE); - } - - this->module = module; - - llvm::InitializeNativeTarget(); - llvm::InitializeNativeTargetAsmPrinter(); - - mlir::ExecutionEngineOptions options; - options.transformer = llvmTransformer; - options.sharedLibPaths = sharedLibPaths; - auto maybeEngine = mlir::ExecutionEngine::create(this->module, options); - assert(maybeEngine && "failed to create JIT"); - engine = std::move(*maybeEngine); -} - -Engine::~Engine() = default; - -void Engine::dumpStateLayout() { state->dumpLayout(); } - -void Engine::dumpStateSignalTriggers() { state->dumpSignalTriggers(); } - -int Engine::simulate(int n, uint64_t maxTime) { - assert(engine && "engine not found"); - assert(state && "state not found"); - - auto tm = static_cast(traceMode); - Trace trace(state, out, tm); - - SmallVector arg({&state}); - // Initialize tbe simulation state. - auto invocationResult = engine->invokePacked("llhd_init", arg); - if (invocationResult) { - llvm::errs() << "Failed invocation of llhd_init: " << invocationResult; - return -1; - } - - if (traceMode != TraceMode::None) { - // Add changes for all the signals' initial values. - for (size_t i = 0, e = state->signals.size(); i < e; ++i) { - trace.addChange(i); - } - } - - // Add a dummy event to get the simulation started. - state->queue.push_back(Slot(Time())); - ++state->queue.events; - - // Keep track of the instances that need to wakeup. - llvm::SmallVector wakeupQueue; - - // Add all instances to the wakeup queue for the first run and add the jitted - // function pointers to all of the instances to make them readily available. - for (size_t i = 0, e = state->instances.size(); i < e; ++i) { - wakeupQueue.push_back(i); - auto &inst = state->instances[i]; - auto expectedFPtr = engine->lookupPacked(inst.unit); - if (!expectedFPtr) { - llvm::errs() << "Could not lookup " << inst.unit << "!\n"; - return -1; - } - inst.unitFPtr = *expectedFPtr; - } - - int cycle = 0; - while (state->queue.events > 0) { - const auto &pop = state->queue.top(); - - // Interrupt the simulation if a stop condition is met. - if ((n > 0 && cycle >= n) || - (maxTime > 0 && pop.time.getTime() > maxTime)) { - break; - } - - // Update the simulation time. - state->time = pop.time; - - if (traceMode != TraceMode::None) - trace.flush(); - - // Process signal changes. - size_t i = 0, e = pop.changesSize; - while (i < e) { - const auto sigIndex = pop.changes[i].first; - auto &curr = state->signals[sigIndex]; - APInt buff(curr.getSize() * 8, 0); - llvm::LoadIntFromMemory(buff, curr.getValue(), curr.getSize()); - - // Apply the changes to the buffer until we reach the next signal. - while (i < e && pop.changes[i].first == sigIndex) { - const auto &change = pop.buffers[pop.changes[i].second]; - const auto offset = change.first; - const auto &drive = change.second; - if (drive.getBitWidth() < buff.getBitWidth()) - buff.insertBits(drive, offset); - else - buff = drive; - - ++i; - } - - if (!curr.updateWhenChanged(buff.getRawData())) - continue; - - // Add sensitive instances. - for (auto inst : curr.getTriggeredInstanceIndices()) { - // Skip if the process is not currently sensible to the signal. - if (!state->instances[inst].isEntity) { - const auto &sensList = state->instances[inst].sensitivityList; - auto it = std::find_if(sensList.begin(), sensList.end(), - [sigIndex](const SignalDetail &sig) { - return sig.globalIndex == sigIndex; - }); - if (sensList.end() != it && - state->instances[inst].procState->senses[it - sensList.begin()] == - 0) - continue; - - // Invalidate scheduled wakeup - state->instances[inst].expectedWakeup = Time(); - } - wakeupQueue.push_back(inst); - } - - // Dump the updated signal. - if (traceMode != TraceMode::None) - trace.addChange(sigIndex); - } - - // Add scheduled process resumes to the wakeup queue. - for (auto inst : pop.scheduled) { - if (state->time == state->instances[inst].expectedWakeup) - wakeupQueue.push_back(inst); - } - - state->queue.pop(); - - std::sort(wakeupQueue.begin(), wakeupQueue.end()); - wakeupQueue.erase(std::unique(wakeupQueue.begin(), wakeupQueue.end()), - wakeupQueue.end()); - - // Run the instances present in the wakeup queue. - for (auto i : wakeupQueue) { - auto &inst = state->instances[i]; - auto signalTable = inst.sensitivityList.data(); - - // Gather the instance arguments for unit invocation. - SmallVector args; - if (inst.isEntity) - args.assign({&state, &inst.entityState, &signalTable}); - else { - args.assign({&state, &inst.procState, &signalTable}); - } - // Run the unit. - (*inst.unitFPtr)(args.data()); - } - - // Clear wakeup queue. - wakeupQueue.clear(); - ++cycle; - } - - if (traceMode != TraceMode::None) { - // Flush any remainign changes - trace.flush(/*force=*/true); - } - - llvm::errs() << "Finished at " << state->time.toString() << " (" << cycle - << " cycles)\n"; - return 0; -} - -void Engine::buildLayout(ModuleOp module) { - // Start from the root entity. - auto rootEntity = module.lookupSymbol(root); - assert(rootEntity && "root entity not found!"); - - // Build root instance, the parent and instance names are the same for the - // root. - Instance rootInst(state->root); - rootInst.unit = root; - rootInst.path = root; - - // Recursively walk the units starting at root. - walkEntity(rootEntity, rootInst); - - // The root is always an instance. - rootInst.isEntity = true; - // Store the root instance. - state->instances.push_back(std::move(rootInst)); - - // Add triggers to signals. - for (size_t i = 0, e = state->instances.size(); i < e; ++i) { - auto &inst = state->instances[i]; - for (auto trigger : inst.sensitivityList) { - state->signals[trigger.globalIndex].pushInstanceIndex(i); - } - } -} - -void Engine::walkEntity(EntityOp entity, Instance &child) { - entity.walk([&](Operation *op) { - assert(op); - - // Add a signal to the signal table. - if (auto sig = dyn_cast(op)) { - uint64_t index = state->addSignal(sig.getName().str(), child.name); - child.sensitivityList.push_back( - SignalDetail({nullptr, 0, child.sensitivityList.size(), index})); - } - - // Build (recursive) instance layout. - if (auto inst = dyn_cast(op)) { - // Skip self-recursion. - if (inst.getCallee() == child.name) - return; - if (auto e = - op->getParentOfType().lookupSymbol(inst.getCallee())) { - Instance newChild(child.unit + '.' + inst.getName().str()); - newChild.unit = inst.getCallee().str(); - newChild.nArgs = inst.getNumOperands(); - newChild.path = child.path + "/" + inst.getName().str(); - - // Add instance arguments to sensitivity list. The first nArgs signals - // in the sensitivity list represent the unit's arguments, while the - // following ones represent the unit-defined signals. - llvm::SmallVector args; - args.insert(args.end(), inst.getInputs().begin(), - inst.getInputs().end()); - args.insert(args.end(), inst.getOutputs().begin(), - inst.getOutputs().end()); - - for (size_t i = 0, e = args.size(); i < e; ++i) { - // The signal comes from an instance's argument. - if (auto blockArg = dyn_cast(args[i])) { - auto detail = child.sensitivityList[blockArg.getArgNumber()]; - detail.instIndex = i; - newChild.sensitivityList.push_back(detail); - } else if (auto sigOp = dyn_cast(args[i].getDefiningOp())) { - // The signal comes from one of the instance's owned signals. - auto it = std::find_if( - child.sensitivityList.begin(), child.sensitivityList.end(), - [&](SignalDetail &detail) { - return state->signals[detail.globalIndex].getName() == - sigOp.getName() && - state->signals[detail.globalIndex].getOwner() == - child.name; - }); - if (it != child.sensitivityList.end()) { - auto detail = *it; - detail.instIndex = i; - newChild.sensitivityList.push_back(detail); - } - } - } - - // Recursively walk a new entity, otherwise it is a process and cannot - // define new signals or instances. - if (auto ent = dyn_cast(e)) { - newChild.isEntity = true; - walkEntity(ent, newChild); - } else { - newChild.isEntity = false; - } - - // Store the created instance. - state->instances.push_back(std::move(newChild)); - } - } - }); -} diff --git a/lib/Dialect/LLHD/Simulator/State.cpp b/lib/Dialect/LLHD/Simulator/State.cpp deleted file mode 100644 index db5be41970..0000000000 --- a/lib/Dialect/LLHD/Simulator/State.cpp +++ /dev/null @@ -1,314 +0,0 @@ -//===- State.cpp - LLHD simulator state -------------------------*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// This file implements the constructs used to keep track of the simulation -// state in the LLHD simulator. -// -//===----------------------------------------------------------------------===// - -#include "circt/Dialect/LLHD/Simulator/State.h" - -#include "llvm/Support/Format.h" -#include "llvm/Support/raw_ostream.h" - -#include - -using namespace llvm; -using namespace circt::llhd::sim; - -//===----------------------------------------------------------------------===// -// Time -//===----------------------------------------------------------------------===// - -std::string Time::toString() const { - return std::to_string(time) + "ps " + std::to_string(delta) + "d " + - std::to_string(eps) + "e"; -} - -//===----------------------------------------------------------------------===// -// Signal -//===----------------------------------------------------------------------===// - -std::string Signal::toHexString() const { - std::string ret; - raw_string_ostream ss(ret); - ss << "0x"; - for (int i = size - 1; i >= 0; --i) { - ss << format_hex_no_prefix(static_cast(value[i]), 2); - } - return ret; -} - -std::string Signal::toHexString(unsigned elemIndex) const { - assert(elements.size() > 0 && "the signal type has to be tuple or array!"); - auto elemSize = elements[elemIndex].second; - auto *ptr = value + elements[elemIndex].first; - std::string ret; - raw_string_ostream ss(ret); - ss << "0x"; - for (int i = elemSize - 1; i >= 0; --i) { - ss << format_hex_no_prefix(static_cast(ptr[i]), 2); - } - return ret; -} - -Signal::~Signal() { - std::free(value); - value = nullptr; -} - -//===----------------------------------------------------------------------===// -// Slot -//===----------------------------------------------------------------------===// - -bool Slot::operator<(const Slot &rhs) const { return time < rhs.time; } - -bool Slot::operator>(const Slot &rhs) const { return rhs.time < time; } - -void Slot::insertChange(int index, int bitOffset, uint8_t *bytes, - unsigned width) { - // Get the amount of 64 bit words required to store the value in an APInt. - auto size = llvm::divideCeil(width, 8); - - APInt buffer(width, 0); - llvm::LoadIntFromMemory(buffer, bytes, size); - auto offsetBufferPair = std::make_pair(bitOffset, buffer); - - if (changesSize >= buffers.size()) { - // Create a new change buffer if we don't have any unused one available for - // reuse. - buffers.push_back(offsetBufferPair); - } else { - // Reuse the first available buffer. - buffers[changesSize] = offsetBufferPair; - } - - // Map the signal index to the change buffer so we can retrieve - // it after sorting. - changes.push_back(std::make_pair(index, changesSize)); - ++changesSize; -} - -void Slot::insertChange(unsigned inst) { scheduled.push_back(inst); } - -//===----------------------------------------------------------------------===// -// UpdateQueue -//===----------------------------------------------------------------------===// -void UpdateQueue::insertOrUpdate(Time time, int index, int bitOffset, - uint8_t *bytes, unsigned width) { - auto &slot = getOrCreateSlot(time); - slot.insertChange(index, bitOffset, bytes, width); -} - -void UpdateQueue::insertOrUpdate(Time time, unsigned inst) { - auto &slot = getOrCreateSlot(time); - slot.insertChange(inst); -} - -Slot &UpdateQueue::getOrCreateSlot(Time time) { - auto &top = begin()[topSlot]; - - // Directly add to top slot. - if (!top.unused && time == top.time) { - return top; - } - - // We need to search through the queue for an existing slot only if we're - // spawning an event later than the top slot. Adding to an existing slot - // scheduled earlier than the top slot should never happens, as then it should - // be the top. - if (events > 0 && top.time < time) { - for (size_t i = 0, e = size(); i < e; ++i) { - if (time == begin()[i].time) { - return begin()[i]; - } - } - } - - // Spawn new event using an existing slot. - if (!unused.empty()) { - auto firstUnused = unused.pop_back_val(); - auto &newSlot = begin()[firstUnused]; - newSlot.unused = false; - newSlot.time = time; - - // Update the top of the queue either if it is currently unused or the new - // timestamp is earlier than it. - if (top.unused || time < top.time) - topSlot = firstUnused; - - ++events; - return newSlot; - } - - // We do not have pre-allocated slots available, generate a new one. - push_back(Slot(time)); - - // Update the top of the queue either if it is currently unused or the new - // timestamp is earlier than it. - if (top.unused || time < top.time) - topSlot = size() - 1; - - ++events; - return back(); -} - -const Slot &UpdateQueue::top() { - assert(topSlot < size() && "top is pointing out of bounds!"); - - // Sort the changes of the top slot such that all changes to the same signal - // are in succession. - auto &top = begin()[topSlot]; - llvm::sort(top.changes.begin(), top.changes.begin() + top.changesSize); - return top; -} - -void UpdateQueue::pop() { - // Reset internal structures and decrease the event counter. - auto &curr = begin()[topSlot]; - curr.unused = true; - curr.changesSize = 0; - curr.scheduled.clear(); - curr.changes.clear(); - curr.time = Time(); - --events; - - // Add to unused slots list for easy retrieval. - unused.push_back(topSlot); - - // Update the current top of the queue. - topSlot = std::distance( - begin(), - std::min_element(begin(), end(), [](const auto &a, const auto &b) { - // a is "smaller" than b if either a's timestamp is earlier than b's, or - // b is unused (i.e. b has no actual meaning). - return !a.unused && (a < b || b.unused); - })); -} - -//===----------------------------------------------------------------------===// -// Instance -//===----------------------------------------------------------------------===// - -Instance::~Instance() { - std::free(procState); - procState = nullptr; - std::free(entityState); - entityState = nullptr; -} - -//===----------------------------------------------------------------------===// -// State -//===----------------------------------------------------------------------===// - -State::~State() { - for (auto &inst : instances) { - if (inst.procState) { - std::free(inst.procState->senses); - } - } -} - -Slot State::popQueue() { - assert(!queue.empty() && "the event queue is empty"); - Slot pop = queue.top(); - queue.pop(); - return pop; -} - -void State::pushQueue(Time t, unsigned inst) { - Time newTime = time + t; - queue.insertOrUpdate(newTime, inst); - instances[inst].expectedWakeup = newTime; -} - -llvm::SmallVectorTemplateCommon::iterator -State::getInstanceIterator(std::string instName) { - auto it = - std::find_if(instances.begin(), instances.end(), - [&](const auto &inst) { return instName == inst.name; }); - - assert(it != instances.end() && "instance does not exist!"); - - return it; -} - -int State::addSignal(std::string name, std::string owner) { - signals.push_back(Signal(name, owner)); - return signals.size() - 1; -} - -void State::addProcPtr(std::string name, ProcState *procStatePtr) { - auto it = getInstanceIterator(name); - - // Store instance index in process state. - procStatePtr->inst = it - instances.begin(); - (*it).procState = procStatePtr; -} - -int State::addSignalData(int index, std::string owner, uint8_t *value, - uint64_t size) { - auto it = getInstanceIterator(owner); - - uint64_t globalIdx = (*it).sensitivityList[index + (*it).nArgs].globalIndex; - auto &sig = signals[globalIdx]; - - // Add pointer and size to global signal table entry. - sig.store(value, size); - - // Add the value pointer to the signal detail struct for each instance this - // signal appears in. - for (auto inst : signals[globalIdx].getTriggeredInstanceIndices()) { - for (auto &detail : instances[inst].sensitivityList) { - if (detail.globalIndex == globalIdx) { - detail.value = sig.getValue(); - } - } - } - return globalIdx; -} - -void State::addSignalElement(unsigned index, unsigned offset, unsigned size) { - signals[index].pushElement(std::make_pair(offset, size)); -} - -void State::dumpSignal(llvm::raw_ostream &out, int index) { - auto &sig = signals[index]; - for (auto inst : sig.getTriggeredInstanceIndices()) { - out << time.toString() << " " << instances[inst].path << "/" - << sig.getName() << " " << sig.toHexString() << "\n"; - } -} - -void State::dumpLayout() { - llvm::errs() << "::------------------- Layout -------------------::\n"; - for (const auto &inst : instances) { - llvm::errs() << inst.name << ":\n"; - llvm::errs() << "---path: " << inst.path << "\n"; - llvm::errs() << "---isEntity: " << inst.isEntity << "\n"; - llvm::errs() << "---sensitivity list: "; - for (auto in : inst.sensitivityList) { - llvm::errs() << in.globalIndex << " "; - } - llvm::errs() << "\n"; - } - llvm::errs() << "::----------------------------------------------::\n"; -} - -void State::dumpSignalTriggers() { - llvm::errs() << "::------------- Signal information -------------::\n"; - for (size_t i = 0, e = signals.size(); i < e; ++i) { - llvm::errs() << signals[i].getOwner() << "/" << signals[i].getName() - << " triggers: "; - for (auto trig : signals[i].getTriggeredInstanceIndices()) { - llvm::errs() << trig << " "; - } - llvm::errs() << "\n"; - } - llvm::errs() << "::----------------------------------------------::\n"; -} diff --git a/lib/Dialect/LLHD/Simulator/Trace.cpp b/lib/Dialect/LLHD/Simulator/Trace.cpp deleted file mode 100644 index 07cc48c578..0000000000 --- a/lib/Dialect/LLHD/Simulator/Trace.cpp +++ /dev/null @@ -1,172 +0,0 @@ -//===- Trace.cpp - Simulation trace implementation ------------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// This file implements the Trace class, used to handle the signal trace -// generation for the llhd-sim tool. -// -//===----------------------------------------------------------------------===// - -#include "circt/Dialect/LLHD/Simulator/Trace.h" - -#include "llvm/Support/raw_ostream.h" - -using namespace circt::llhd::sim; - -Trace::Trace(std::unique_ptr const &state, llvm::raw_ostream &out, - TraceMode mode) - : out(out), state(state), mode(mode) { - auto root = state->root; - for (auto &sig : state->signals) { - bool done = (mode != TraceMode::Full && mode != TraceMode::Merged && - !sig.isOwner(root)) || - (mode == TraceMode::NamedOnly && sig.isValidSigName()); - isTraced.push_back(!done); - } -} - -//===----------------------------------------------------------------------===// -// Changes gathering methods -//===----------------------------------------------------------------------===// - -void Trace::pushChange(unsigned inst, unsigned sigIndex, int elem = -1) { - auto &sig = state->signals[sigIndex]; - std::string valueDump; - std::string path; - llvm::raw_string_ostream ss(path); - - ss << state->instances[inst].path << '/' << sig.getName(); - - if (elem >= 0) { - // Add element index to the hierarchical path. - ss << '[' << elem << ']'; - // Get element value dump. - valueDump = sig.toHexString(elem); - } else { - // Get signal value dump. - valueDump = sig.toHexString(); - } - - // Check wheter we have an actual change from last value. - auto lastValKey = std::make_pair(path, elem); - if (valueDump != lastValue[lastValKey]) { - changes.push_back(std::make_pair(path, valueDump)); - lastValue[lastValKey] = valueDump; - } -} - -void Trace::pushAllChanges(unsigned inst, unsigned sigIndex) { - auto &sig = state->signals[sigIndex]; - if (sig.hasElement()) { - // Push changes for all signal elements. - for (size_t i = 0, e = sig.getElementSize(); i < e; ++i) { - pushChange(inst, sigIndex, i); - } - } else { - // Push one change for the whole signal. - pushChange(inst, sigIndex); - } -} - -void Trace::addChange(unsigned sigIndex) { - currentTime = state->time; - if (isTraced[sigIndex]) { - if (mode == TraceMode::Full) { - auto &sig = state->signals[sigIndex]; - // Add a change for each connected instance. - for (auto inst : sig.getTriggeredInstanceIndices()) { - pushAllChanges(inst, sigIndex); - } - } else if (mode == TraceMode::Reduced) { - // The root is always the last instance in the instances list. - pushAllChanges(state->instances.size() - 1, sigIndex); - } else if (mode == TraceMode::Merged || mode == TraceMode::MergedReduce || - mode == TraceMode::NamedOnly) { - addChangeMerged(sigIndex); - } - } -} - -void Trace::addChangeMerged(unsigned sigIndex) { - auto &sig = state->signals[sigIndex]; - if (sig.hasElement()) { - // Add a change for all sub-elements - for (size_t i = 0, e = sig.getElementSize(); i < e; ++i) { - auto valueDump = sig.toHexString(i); - mergedChanges[std::make_pair(sigIndex, i)] = valueDump; - } - } else { - // Add one change for the whole signal. - auto valueDump = sig.toHexString(); - mergedChanges[std::make_pair(sigIndex, -1)] = valueDump; - } -} - -//===----------------------------------------------------------------------===// -// Flush methods -//===----------------------------------------------------------------------===// - -void Trace::sortChanges() { - std::sort(changes.begin(), changes.end(), - [](std::pair &lhs, - std::pair &rhs) -> bool { - return lhs.first < rhs.first; - }); -} - -void Trace::flush(bool force) { - if (mode == TraceMode::Full || mode == TraceMode::Reduced) - flushFull(); - else if (mode == TraceMode::Merged || mode == TraceMode::MergedReduce || - mode == TraceMode::NamedOnly) - if (state->time.getTime() > currentTime.getTime() || force) - flushMerged(); -} - -void Trace::flushFull() { - if (changes.size() > 0) { - sortChanges(); - - auto timeDump = currentTime.toString(); - for (auto change : changes) { - out << timeDump << " " << change.first << " " << change.second << "\n"; - } - changes.clear(); - } -} - -void Trace::flushMerged() { - // Move the merged changes to the changes vector for dumping. - for (auto elem : mergedChanges) { - auto sigIndex = elem.first.first; - auto sigElem = elem.first.second; - auto &sig = state->signals[sigIndex]; - auto change = elem.second; - - if (mode == TraceMode::Merged) { - // Add the changes for all connected instances. - for (auto inst : sig.getTriggeredInstanceIndices()) { - pushChange(inst, sigIndex, sigElem); - } - } else { - // The root is always the last instance in the instances list. - pushChange(state->instances.size() - 1, sigIndex, sigElem); - } - } - - if (changes.size() > 0) { - sortChanges(); - - // Flush the changes to output stream. - out << currentTime.getTime() << "ps\n"; - for (auto change : changes) { - out << " " << change.first << " " << change.second << "\n"; - } - mergedChanges.clear(); - changes.clear(); - } -} diff --git a/lib/Dialect/LLHD/Simulator/signals-runtime-wrappers.cpp b/lib/Dialect/LLHD/Simulator/signals-runtime-wrappers.cpp deleted file mode 100644 index e26c1b0320..0000000000 --- a/lib/Dialect/LLHD/Simulator/signals-runtime-wrappers.cpp +++ /dev/null @@ -1,78 +0,0 @@ -//===- signals-runtime-wrappers.cpp - Runtime library implementation ------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// This file implements the runtime library used in LLHD simulation. -// -//===----------------------------------------------------------------------===// - -#include "signals-runtime-wrappers.h" - -#include "llvm/ADT/ArrayRef.h" -#include "llvm/Support/raw_ostream.h" - -using namespace llvm; -using namespace circt::llhd::sim; - -//===----------------------------------------------------------------------===// -// Runtime interface -//===----------------------------------------------------------------------===// - -int allocSignal(State *state, int index, char *owner, uint8_t *value, - int64_t size) { - assert(state && "alloc_signal: state not found"); - std::string sOwner(owner); - - return state->addSignalData(index, sOwner, value, size); -} - -void addSigArrayElements(State *state, unsigned index, unsigned size, - unsigned numElements) { - for (size_t i = 0; i < numElements; ++i) - state->addSignalElement(index, size * i, size); -} - -void addSigStructElement(State *state, unsigned index, unsigned offset, - unsigned size) { - state->addSignalElement(index, offset, size); -} - -void allocProc(State *state, char *owner, ProcState *procState) { - assert(state && "alloc_proc: state not found"); - std::string sOwner(owner); - state->addProcPtr(sOwner, procState); -} - -void allocEntity(State *state, char *owner, uint8_t *entityState) { - assert(state && "alloc_entity: state not found"); - auto it = state->getInstanceIterator(owner); - (*it).entityState = entityState; -} - -void driveSignal(State *state, SignalDetail *detail, uint8_t *value, - uint64_t width, int time, int delta, int eps) { - assert(state && "drive_signal: state not found"); - - auto globalIndex = detail->globalIndex; - auto offset = detail->offset; - - int bitOffset = - (detail->value - state->signals[globalIndex].getValue()) * 8 + offset; - - // Spawn a new event. - state->queue.insertOrUpdate(state->time + Time(time, delta, eps), globalIndex, - bitOffset, value, width); -} - -void llhdSuspend(State *state, ProcState *procState, int time, int delta, - int eps) { - // Add a new scheduled wake up if a time is specified. - if (time || delta || eps) { - Time sTime(time, delta, eps); - state->pushQueue(sTime, procState->inst); - } -} diff --git a/lib/Dialect/LLHD/Simulator/signals-runtime-wrappers.h b/lib/Dialect/LLHD/Simulator/signals-runtime-wrappers.h deleted file mode 100644 index 13f4cbeea4..0000000000 --- a/lib/Dialect/LLHD/Simulator/signals-runtime-wrappers.h +++ /dev/null @@ -1,58 +0,0 @@ -//===- signals-runtime-wrappers.h - Simulation runtime library --*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// Defines the runtime library used in LLHD simulation. -// -//===----------------------------------------------------------------------===// - -#ifndef CIRCT_DIALECT_LLHD_SIMULATOR_SIGNALS_RUNTIME_WRAPPERS_H -#define CIRCT_DIALECT_LLHD_SIMULATOR_SIGNALS_RUNTIME_WRAPPERS_H - -#include "circt/Dialect/LLHD/Simulator/State.h" - -extern "C" { - -//===----------------------------------------------------------------------===// -// Runtime interfaces -//===----------------------------------------------------------------------===// - -/// Allocate a new signal. The index of the new signal in the state's list of -/// signals is returned. -int allocSignal(circt::llhd::sim::State *state, int index, char *owner, - uint8_t *value, int64_t size); - -/// Add offset and size information for the elements of an array signal. -void addSigArrayElements(circt::llhd::sim::State *state, unsigned index, - unsigned size, unsigned numElements); - -/// Add offset and size information for one element of a struct signal. Elements -/// are assumed to be added (by calling this function) in sequential order, from -/// first to last. -void addSigStructElement(circt::llhd::sim::State *state, unsigned index, - unsigned offset, unsigned size); - -/// Add allocated constructs to a process instance. -void allocProc(circt::llhd::sim::State *state, char *owner, - circt::llhd::sim::ProcState *procState); - -/// Add allocated entity state to the given instance. -void allocEntity(circt::llhd::sim::State *state, char *owner, - uint8_t *entityState); - -/// Drive a value onto a signal. -void driveSignal(circt::llhd::sim::State *state, - circt::llhd::sim::SignalDetail *index, uint8_t *value, - uint64_t width, int time, int delta, int eps); - -/// Suspend a process. -void llhdSuspend(circt::llhd::sim::State *state, - circt::llhd::sim::ProcState *procState, int time, int delta, - int eps); -} - -#endif // CIRCT_DIALECT_LLHD_SIMULATOR_SIGNALS_RUNTIME_WRAPPERS_H diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 5ba60ae931..11738c2363 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -38,11 +38,6 @@ if (CIRCT_GTEST_AVAILABLE) list(APPEND CIRCT_TEST_DEPENDS CIRCTUnitTests) endif() -if(CIRCT_LLHD_SIM_ENABLED) - list(APPEND CIRCT_TEST_DEPENDS llhd-sim) - list(APPEND CIRCT_TEST_DEPENDS circt-llhd-signals-runtime-wrappers) -endif() - if(CIRCT_SLANG_FRONTEND_ENABLED) list(APPEND CIRCT_TEST_DEPENDS circt-verilog) endif() diff --git a/test/Conversion/LLHDToLLVM/convert_entity.mlir b/test/Conversion/LLHDToLLVM/convert_entity.mlir deleted file mode 100644 index 343683c95d..0000000000 --- a/test/Conversion/LLHDToLLVM/convert_entity.mlir +++ /dev/null @@ -1,29 +0,0 @@ -// RUN: circt-opt %s --convert-llhd-to-llvm --reconcile-unrealized-casts --split-input-file | FileCheck %s - -// CHECK-LABEL: llvm.func @convert_empty( -// CHECK-SAME: %arg0: !llvm.ptr, %arg1: !llvm.ptr, %arg2: !llvm.ptr) { -// CHECK: llvm.return -// CHECK: } -llhd.entity @convert_empty() -> () {} - -// CHECK-LABEL: llvm.func @convert_one_input( -// CHECK-SAME: %arg0: !llvm.ptr, %arg1: !llvm.ptr, %arg2: !llvm.ptr) { -// CHECK: [[IN0:%.+]] = llvm.getelementptr %arg2[0] : (!llvm.ptr) -> !llvm.ptr, !llvm.struct<(ptr, i64, i64, i64)> -// CHECK: llvm.return -// CHECK: } -llhd.entity @convert_one_input(%in0 : !llhd.sig) -> () {} - -// CHECK-LABEL: llvm.func @convert_one_output( -// CHECK-SAME: %arg0: !llvm.ptr, %arg1: !llvm.ptr, %arg2: !llvm.ptr) { -// CHECK: [[OUT0:%.*]] = llvm.getelementptr %arg2[0] : (!llvm.ptr) -> !llvm.ptr, !llvm.struct<(ptr, i64, i64, i64)> -// CHECK: llvm.return -// CHECK: } -llhd.entity @convert_one_output () -> (%out0 : !llhd.sig) {} - -// CHECK-LABEL: llvm.func @convert_input_and_output( -// CHECK-SAME: %arg0: !llvm.ptr, %arg1: !llvm.ptr, %arg2: !llvm.ptr) { -// CHECK: [[IN0:%.*]] = llvm.getelementptr %arg2[0] : (!llvm.ptr) -> !llvm.ptr, !llvm.struct<(ptr, i64, i64, i64)> -// CHECK: [[OUT0:%.*]] = llvm.getelementptr %arg2[1] : (!llvm.ptr) -> !llvm.ptr, !llvm.struct<(ptr, i64, i64, i64)> -// CHECK: llvm.return -// CHECK: } -llhd.entity @convert_input_and_output (%in0 : !llhd.sig) -> (%out0 : !llhd.sig) {} diff --git a/test/Conversion/LLHDToLLVM/convert_extract.mlir b/test/Conversion/LLHDToLLVM/convert_extract.mlir deleted file mode 100644 index 09816d6cc3..0000000000 --- a/test/Conversion/LLHDToLLVM/convert_extract.mlir +++ /dev/null @@ -1,126 +0,0 @@ -// RUN: circt-opt %s --convert-llhd-to-llvm --reconcile-unrealized-casts | FileCheck %s - -// CHECK-LABEL: llvm.func @convertSigExtract( -// CHECK-SAME: %arg0: i5, %arg1: !llvm.ptr) -func.func @convertSigExtract(%arg0: i5, %arg1: !llhd.sig) { - // CHECK: [[TMP:%.+]] = llvm.getelementptr %arg1[0, 0] : (!llvm.ptr) -> !llvm.ptr, !llvm.struct<(ptr, i64, i64, i64)> - // CHECK: [[VALUE_PTR:%.+]] = llvm.load [[TMP]] : !llvm.ptr -> !llvm.ptr - // CHECK: [[TMP:%.+]] = llvm.getelementptr %arg1[0, 1] : (!llvm.ptr) -> !llvm.ptr, !llvm.struct<(ptr, i64, i64, i64)> - // CHECK: [[OFFSET:%.+]] = llvm.load [[TMP]] : !llvm.ptr -> i64 - // CHECK: [[TMP:%.+]] = llvm.getelementptr %arg1[0, 2] : (!llvm.ptr) -> !llvm.ptr, !llvm.struct<(ptr, i64, i64, i64)> - // CHECK: [[INST_IDX:%.+]] = llvm.load [[TMP]] : !llvm.ptr -> i64 - // CHECK: [[TMP:%.+]] = llvm.getelementptr %arg1[0, 3] : (!llvm.ptr) -> !llvm.ptr, !llvm.struct<(ptr, i64, i64, i64)> - // CHECK: [[GLOBAL_IDX:%.+]] = llvm.load [[TMP]] : !llvm.ptr -> i64 - - // Adjust offset - // CHECK: [[TMP:%.+]] = llvm.zext %arg0 : i5 to i64 - // CHECK: [[NEW_OFFSET:%.+]] = llvm.add [[OFFSET]], [[TMP]] - - // Adjust value pointer to closest byte - // CHECK: [[TMP1:%.+]] = llvm.ptrtoint [[VALUE_PTR]] - // CHECK: [[C8_I64:%.+]] = llvm.mlir.constant(8 : - // CHECK: [[BYTE_OFFSET:%.+]] = llvm.udiv [[NEW_OFFSET]], [[C8_I64]] - // CHECK: [[TMP2:%.+]] = llvm.add [[TMP1]], [[BYTE_OFFSET]] - // CHECK: [[VALUE_PTR:%.+]] = llvm.inttoptr [[TMP2]] - - // Adjust offset to closest byte - // CHECK: [[OFFSET:%.+]] = llvm.urem [[NEW_OFFSET]], [[C8_I64]] - - // Create new signal struct on the stack. - // CHECK: [[TMP1:%.+]] = llvm.mlir.undef : !llvm.struct<(ptr, i64, i64, i64)> - // CHECK: [[TMP2:%.+]] = llvm.insertvalue [[VALUE_PTR]], [[TMP1]][0] - // CHECK: [[TMP3:%.+]] = llvm.insertvalue [[OFFSET]], [[TMP2]][1] - // CHECK: [[TMP4:%.+]] = llvm.insertvalue [[INST_IDX]], [[TMP3]][2] - // CHECK: [[TMP5:%.+]] = llvm.insertvalue [[GLOBAL_IDX]], [[TMP4]][3] - // CHECK: [[BUF:%.+]] = llvm.alloca {{%.+}} x !llvm.struct<(ptr, i64, i64, i64)> - // CHECK: llvm.store [[TMP5]], [[BUF]] - - llhd.sig.extract %arg1 from %arg0 : (!llhd.sig) -> !llhd.sig - return -} - -// CHECK-LABEL: llvm.func @convertSigArrayGet( -// CHECK-SAME: %arg0: i2, %arg1: !llvm.ptr) -func.func @convertSigArrayGet(%arg0 : i2, %arg1 : !llhd.sig>) { - // CHECK: [[TMP:%.+]] = llvm.getelementptr %arg1[0, 0] : (!llvm.ptr) -> !llvm.ptr, !llvm.struct<(ptr, i64, i64, i64)> - // CHECK: [[VALUE_PTR:%.+]] = llvm.load [[TMP]] : !llvm.ptr -> !llvm.ptr - // CHECK: [[TMP:%.+]] = llvm.getelementptr %arg1[0, 1] : (!llvm.ptr) -> !llvm.ptr, !llvm.struct<(ptr, i64, i64, i64)> - // CHECK: [[OFFSET:%.+]] = llvm.load [[TMP]] : !llvm.ptr -> i64 - // CHECK: [[TMP:%.+]] = llvm.getelementptr %arg1[0, 2] : (!llvm.ptr) -> !llvm.ptr, !llvm.struct<(ptr, i64, i64, i64)> - // CHECK: [[INST_IDX:%.+]] = llvm.load [[TMP]] : !llvm.ptr -> i64 - // CHECK: [[TMP:%.+]] = llvm.getelementptr %arg1[0, 3] : (!llvm.ptr) -> !llvm.ptr, !llvm.struct<(ptr, i64, i64, i64)> - // CHECK: [[GLOBAL_IDX:%.+]] = llvm.load [[TMP]] : !llvm.ptr -> i64 - - // Adjust value pointer - // CHECK: [[TMP:%.+]] = llvm.zext %arg0 : i2 to i3 - // CHECK: [[NEW_VALUE_PTR:%.+]] = llvm.getelementptr [[VALUE_PTR]][0, [[TMP]]] : (!llvm.ptr, i3) -> !llvm.ptr, !llvm.array<4 x i4> - - // Create new signal struct on the stack. - // CHECK: [[TMP1:%.+]] = llvm.mlir.undef : !llvm.struct<(ptr, i64, i64, i64)> - // CHECK: [[TMP2:%.+]] = llvm.insertvalue [[NEW_VALUE_PTR]], [[TMP1]][0] - // CHECK: [[TMP3:%.+]] = llvm.insertvalue [[OFFSET]], [[TMP2]][1] - // CHECK: [[TMP4:%.+]] = llvm.insertvalue [[INST_IDX]], [[TMP3]][2] - // CHECK: [[TMP5:%.+]] = llvm.insertvalue [[GLOBAL_IDX]], [[TMP4]][3] - // CHECK: [[BUF:%.+]] = llvm.alloca {{%.+}} x !llvm.struct<(ptr, i64, i64, i64)> - // CHECK: llvm.store [[TMP5]], [[BUF]] - - llhd.sig.array_get %arg1[%arg0] : !llhd.sig> - return -} - -// CHECK-LABEL: llvm.func @convertSigArraySlice( -// CHECK-SAME: %arg0: i2, %arg1: !llvm.ptr) -func.func @convertSigArraySlice(%arg0: i2, %arg1: !llhd.sig>) { - // CHECK: [[TMP:%.+]] = llvm.getelementptr %arg1[0, 0] : (!llvm.ptr) -> !llvm.ptr, !llvm.struct<(ptr, i64, i64, i64)> - // CHECK: [[VALUE_PTR:%.+]] = llvm.load [[TMP]] : !llvm.ptr -> !llvm.ptr - // CHECK: [[TMP:%.+]] = llvm.getelementptr %arg1[0, 1] : (!llvm.ptr) -> !llvm.ptr, !llvm.struct<(ptr, i64, i64, i64)> - // CHECK: [[OFFSET:%.+]] = llvm.load [[TMP]] : !llvm.ptr -> i64 - // CHECK: [[TMP:%.+]] = llvm.getelementptr %arg1[0, 2] : (!llvm.ptr) -> !llvm.ptr, !llvm.struct<(ptr, i64, i64, i64)> - // CHECK: [[INST_IDX:%.+]] = llvm.load [[TMP]] : !llvm.ptr -> i64 - // CHECK: [[TMP:%.+]] = llvm.getelementptr %arg1[0, 3] : (!llvm.ptr) -> !llvm.ptr, !llvm.struct<(ptr, i64, i64, i64)> - // CHECK: [[GLOBAL_IDX:%.+]] = llvm.load [[TMP]] : !llvm.ptr -> i64 - - // Adjust value pointer - // CHECK: [[TMP:%.+]] = llvm.zext %arg0 : i2 to i3 - // CHECK: [[NEW_VALUE_PTR:%.+]] = llvm.getelementptr [[VALUE_PTR]][0, [[TMP]]] : (!llvm.ptr, i3) -> !llvm.ptr, !llvm.array<4 x i4> - - // Create new signal struct on the stack. - // CHECK: [[TMP1:%.+]] = llvm.mlir.undef : !llvm.struct<(ptr, i64, i64, i64)> - // CHECK: [[TMP2:%.+]] = llvm.insertvalue [[NEW_VALUE_PTR]], [[TMP1]][0] - // CHECK: [[TMP3:%.+]] = llvm.insertvalue [[OFFSET]], [[TMP2]][1] - // CHECK: [[TMP4:%.+]] = llvm.insertvalue [[INST_IDX]], [[TMP3]][2] - // CHECK: [[TMP5:%.+]] = llvm.insertvalue [[GLOBAL_IDX]], [[TMP4]][3] - // CHECK: [[BUF:%.+]] = llvm.alloca {{%.+}} x !llvm.struct<(ptr, i64, i64, i64)> - // CHECK: llvm.store [[TMP5]], [[BUF]] - - llhd.sig.array_slice %arg1 at %arg0 : (!llhd.sig>) -> !llhd.sig> - return -} - -// CHECK-LABEL: llvm.func @convertSigStructExtract( -// CHECK-SAME: %arg0: !llvm.ptr) -func.func @convertSigStructExtract(%arg0: !llhd.sig>) { - // CHECK: [[TMP:%.+]] = llvm.getelementptr %arg0[0, 0] : (!llvm.ptr) -> !llvm.ptr, !llvm.struct<(ptr, i64, i64, i64)> - // CHECK: [[VALUE_PTR:%.+]] = llvm.load [[TMP]] : !llvm.ptr -> !llvm.ptr - // CHECK: [[TMP:%.+]] = llvm.getelementptr %arg0[0, 1] : (!llvm.ptr) -> !llvm.ptr, !llvm.struct<(ptr, i64, i64, i64)> - // CHECK: [[OFFSET:%.+]] = llvm.load [[TMP]] : !llvm.ptr -> i64 - // CHECK: [[TMP:%.+]] = llvm.getelementptr %arg0[0, 2] : (!llvm.ptr) -> !llvm.ptr, !llvm.struct<(ptr, i64, i64, i64)> - // CHECK: [[INST_IDX:%.+]] = llvm.load [[TMP]] : !llvm.ptr -> i64 - // CHECK: [[TMP:%.+]] = llvm.getelementptr %arg0[0, 3] : (!llvm.ptr) -> !llvm.ptr, !llvm.struct<(ptr, i64, i64, i64)> - // CHECK: [[GLOBAL_IDX:%.+]] = llvm.load [[TMP]] : !llvm.ptr -> i64 - - // Adjust value pointer - // CHECK: [[NEW_VALUE_PTR:%.+]] = llvm.getelementptr [[VALUE_PTR]][0, 1] : (!llvm.ptr) -> !llvm.ptr, !llvm.struct<(i3, i2, i1)> - - // Create new signal struct on the stack. - // CHECK: [[TMP1:%.+]] = llvm.mlir.undef : !llvm.struct<(ptr, i64, i64, i64)> - // CHECK: [[TMP2:%.+]] = llvm.insertvalue [[NEW_VALUE_PTR]], [[TMP1]][0] - // CHECK: [[TMP3:%.+]] = llvm.insertvalue [[OFFSET]], [[TMP2]][1] - // CHECK: [[TMP4:%.+]] = llvm.insertvalue [[INST_IDX]], [[TMP3]][2] - // CHECK: [[TMP5:%.+]] = llvm.insertvalue [[GLOBAL_IDX]], [[TMP4]][3] - // CHECK: [[BUF:%.+]] = llvm.alloca {{%.+}} x !llvm.struct<(ptr, i64, i64, i64)> - // CHECK: llvm.store [[TMP5]], [[BUF]] - - llhd.sig.struct_extract %arg0["bar"] : !llhd.sig> - return -} diff --git a/test/Conversion/LLHDToLLVM/convert_memory.mlir b/test/Conversion/LLHDToLLVM/convert_memory.mlir deleted file mode 100644 index fd323964fe..0000000000 --- a/test/Conversion/LLHDToLLVM/convert_memory.mlir +++ /dev/null @@ -1,36 +0,0 @@ -// RUN: circt-opt %s --convert-llhd-to-llvm --reconcile-unrealized-casts | FileCheck %s - -// CHECK-LABEL: llvm.func @lower_var( -// CHECK-SAME: %arg0: i1, %arg1: i32) { -func.func @lower_var(%arg0: i1, %arg1: i32) { - // CHECK: [[VAR0:%.+]] = llvm.alloca {{%.+}} x i1 - // CHECK: llvm.store %arg0, [[VAR0]] - %0 = llhd.var %arg0 : i1 - // CHECK: [[VAR1:%.+]] = llvm.alloca {{%.+}} x i32 - // CHECK: llvm.store %arg1, [[VAR1]] - %1 = llhd.var %arg1 : i32 - // CHECK: llvm.return - return -} - -// CHECK-LABEL: llvm.func @lower_load( -// CHECK-SAME: %arg0: !llvm.ptr, %arg1: !llvm.ptr) { -func.func @lower_load(%arg0: !llhd.ptr, %arg1: !llhd.ptr) { - // CHECK: llvm.load %arg0 : !llvm.ptr -> i1 - %0 = llhd.load %arg0 : !llhd.ptr - // CHECK: llvm.load %arg1 : !llvm.ptr -> i32 - %1 = llhd.load %arg1 : !llhd.ptr - // CHECK: llvm.return - return -} - -// CHECK-LABEL: llvm.func @lower_store( -// CHECK-SAME: %arg0: i1, %arg1: !llvm.ptr, %arg2: i32, %arg3: !llvm.ptr) { -func.func @lower_store(%arg0: i1, %arg1: !llhd.ptr, %arg2: i32, %arg3: !llhd.ptr) { - // CHECK: llvm.store %arg0, %arg1 : i1, !llvm.ptr - llhd.store %arg1, %arg0 : !llhd.ptr - // CHECK: llvm.store %arg2, %arg3 : i32, !llvm.ptr - llhd.store %arg3, %arg2 : !llhd.ptr - // CHECK: llvm.return - return -} diff --git a/test/Conversion/LLHDToLLVM/convert_process_persistence.mlir b/test/Conversion/LLHDToLLVM/convert_process_persistence.mlir deleted file mode 100644 index c2fedd1d4b..0000000000 --- a/test/Conversion/LLHDToLLVM/convert_process_persistence.mlir +++ /dev/null @@ -1,40 +0,0 @@ -// RUN: circt-opt %s --convert-llhd-to-llvm --reconcile-unrealized-casts | FileCheck %s - -// CHECK: @dummyA -// CHECK: @dummyB -func.func private @dummyA(%arg0: i42) -func.func private @dummyB(%arg0: !llhd.ptr) - -// CHECK-LABEL: llvm.func @PersistValuesAcrossPotentialResumePoints( -// CHECK-SAME: %arg0: !llvm.ptr, %arg1: !llvm.ptr, %arg2: !llvm.ptr) -llhd.proc @PersistValuesAcrossPotentialResumePoints () -> () { - // Values used across basic blocks get persisted directly - // CHECK: [[TMP1:%.+]] = llvm.mlir.constant(1337 : - // CHECK: [[PERSIST_PTR1:%.+]] = llvm.getelementptr %arg1[0, 3, 0] : (!llvm.ptr) -> !llvm.ptr, !llvm.struct<(i32, i32, ptr, struct<(i42, ptr)>)> - // CHECK: llvm.store [[TMP1]], [[PERSIST_PTR1]] : i42, !llvm.ptr - %0 = hw.constant 1337 : i42 - - // Variables used across basic blocks get persisted by loading their value - // CHECK: [[TMP2:%.+]] = llvm.alloca {{%.+}} x i42 - // CHECK: llvm.store [[TMP1]], [[TMP2]] : i42, !llvm.ptr - // CHECK: [[PERSIST_PTR2:%.+]] = llvm.getelementptr %arg1[0, 3, 1] : (!llvm.ptr) -> !llvm.ptr, !llvm.struct<(i32, i32, ptr, struct<(i42, ptr)>)> - // CHECK: [[TMP3:%.+]] = llvm.load [[TMP2]] : !llvm.ptr -> i42 - // CHECK: llvm.store [[TMP3]], [[PERSIST_PTR2]] : i42, !llvm.ptr - %1 = llhd.var %0 : i42 - - // CHECK: llvm.br [[BB:\^.+]] - cf.br ^resume - - // CHECK: [[BB]]: -^resume: - // CHECK: [[PERSIST_PTR2:%.+]] = llvm.getelementptr %arg1[0, 3, 1] : (!llvm.ptr) -> !llvm.ptr, !llvm.struct<(i32, i32, ptr, struct<(i42, ptr)>)> - // CHECK: [[PERSIST_PTR1:%.+]] = llvm.getelementptr %arg1[0, 3, 0] : (!llvm.ptr) -> !llvm.ptr, !llvm.struct<(i32, i32, ptr, struct<(i42, ptr)>)> - // CHECK: [[TMP1:%.+]] = llvm.load [[PERSIST_PTR1]] : !llvm.ptr -> i42 - // CHECK: llvm.call @dummyA([[TMP1]]) - // CHECK: llvm.call @dummyB([[PERSIST_PTR2]]) - func.call @dummyA(%0) : (i42) -> () - func.call @dummyB(%1) : (!llhd.ptr) -> () - - // CHECK: llvm.br [[BB]] - cf.br ^resume -} diff --git a/test/Conversion/LLHDToLLVM/convert_signals.mlir b/test/Conversion/LLHDToLLVM/convert_signals.mlir deleted file mode 100644 index 50dff9f5e0..0000000000 --- a/test/Conversion/LLHDToLLVM/convert_signals.mlir +++ /dev/null @@ -1,102 +0,0 @@ -// RUN: circt-opt %s --convert-llhd-to-llvm --reconcile-unrealized-casts | FileCheck %s - -// CHECK-LABEL: llvm.func @driveSignal(!llvm.ptr, !llvm.ptr, !llvm.ptr, i64, i64, i64, i64) - -// CHECK-LABEL: llvm.func @convert_sig( -// CHECK-SAME: %arg0: !llvm.ptr, %arg1: !llvm.ptr, %arg2: !llvm.ptr) { -llhd.entity @convert_sig() -> () { - // Unused in entity definition. Only used at instantiation site. - %0 = hw.constant 0 : i1 - %1 = hw.array_create %0, %0, %0, %0 : i1 - - // CHECK: llvm.getelementptr %arg2[0] : (!llvm.ptr) -> !llvm.ptr, !llvm.struct<(ptr, i64, i64, i64)> - // CHECK: llvm.getelementptr %arg2[1] : (!llvm.ptr) -> !llvm.ptr, !llvm.struct<(ptr, i64, i64, i64)> - llhd.sig "sig0" %0 : i1 - llhd.sig "sig1" %1 : !hw.array<4xi1> -} - -// CHECK-LABEL: llvm.func @convert_prb( -// CHECK-SAME: %arg0: !llvm.ptr, %arg1: !llvm.ptr, %arg2: !llvm.ptr) { -llhd.entity @convert_prb(%a: !llhd.sig, %b: !llhd.sig>) -> () { - // CHECK: [[SIGPTR_A:%.+]] = llvm.getelementptr %arg2[0] : (!llvm.ptr) -> !llvm.ptr, !llvm.struct<(ptr, i64, i64, i64)> - // CHECK: [[SIGPTR_B:%.+]] = llvm.getelementptr %arg2[1] : (!llvm.ptr) -> !llvm.ptr, !llvm.struct<(ptr, i64, i64, i64)> - - // CHECK: [[TMP:%.+]] = llvm.getelementptr [[SIGPTR_A]][0, 0] : (!llvm.ptr) -> !llvm.ptr, !llvm.struct<(ptr, i64, i64, i64)> - // CHECK: [[VALUEPTR_A:%.+]] = llvm.load [[TMP]] : !llvm.ptr -> !llvm.ptr - // CHECK: [[TMP:%.+]] = llvm.getelementptr [[SIGPTR_A]][0, 1] : (!llvm.ptr) -> !llvm.ptr, !llvm.struct<(ptr, i64, i64, i64)> - // CHECK: [[OFFSET_A:%.+]] = llvm.load [[TMP]] : !llvm.ptr -> i64 - // CHECK: [[VALUE_A:%.+]] = llvm.load [[VALUEPTR_A]] : !llvm.ptr -> i16 - // CHECK: [[TMP1:%.+]] = llvm.trunc [[OFFSET_A]] : i64 to i16 - // CHECK: [[TMP2:%.+]] = llvm.lshr [[VALUE_A]], [[TMP1]] : i16 - // CHECK: [[VALUE_A:%.+]] = llvm.trunc [[TMP2]] : i16 to i1 - %0 = llhd.prb %a : !llhd.sig - - // CHECK: [[TMP:%.+]] = llvm.getelementptr [[SIGPTR_B]][0, 0] : (!llvm.ptr) -> !llvm.ptr, !llvm.struct<(ptr, i64, i64, i64)> - // CHECK: [[VALUEPTR_B:%.+]] = llvm.load [[TMP]] : !llvm.ptr -> !llvm.ptr - // CHECK: [[VALUE_B:%.+]] = llvm.load [[VALUEPTR_B]] : !llvm.ptr -> !llvm.array<3 x i5> - %1 = llhd.prb %b : !llhd.sig> -} - -// CHECK-LABEL: llvm.func @convert_drv( -// CHECK-SAME: %arg0: !llvm.ptr, %arg1: !llvm.ptr, %arg2: !llvm.ptr) { -llhd.entity @convert_drv(%a: !llhd.sig, %b: !llhd.sig>) -> () { - // CHECK: [[SIGPTR_A:%.+]] = llvm.getelementptr %arg2[0] : (!llvm.ptr) -> !llvm.ptr, !llvm.struct<(ptr, i64, i64, i64)> - // CHECK: [[SIGPTR_B:%.+]] = llvm.getelementptr %arg2[1] : (!llvm.ptr) -> !llvm.ptr, !llvm.struct<(ptr, i64, i64, i64)> - - // CHECK: [[C0_I1:%.+]] = llvm.mlir.constant(false) : i1 - // CHECK: [[C0_I5:%.+]] = llvm.mlir.constant(0 : i5) : i5 - %c0_i1 = hw.constant 0 : i1 - %c0_i5 = hw.constant 0 : i5 - - // CHECK: [[ARRPTR:%.+]] = llvm.mlir.addressof {{@.+}} : !llvm.ptr - // CHECK: [[ARR:%.+]] = llvm.load [[ARRPTR]] : !llvm.ptr -> !llvm.array<3 x i5> - %0 = hw.array_create %c0_i5, %c0_i5, %c0_i5 : i5 - - // CHECK: [[DT:%.+]] = llvm.mlir.constant(dense<[1000, 0, 0]> - %1 = llhd.constant_time #llhd.time<1ns, 0d, 0e> - - // CHECK: [[C1_I64:%.+]] = llvm.mlir.constant(1 : i64) : i64 - // CHECK: [[C1_I32:%.+]] = llvm.mlir.constant(1 : i32) : i32 - // CHECK: [[BUF:%.+]] = llvm.alloca [[C1_I32]] x i1 - // CHECK: llvm.store [[C0_I1]], [[BUF]] : i1, !llvm.ptr - // CHECK: [[DTS:%.+]] = llvm.extractvalue [[DT]][0] : !llvm.array<3 x i64> - // CHECK: [[DTD:%.+]] = llvm.extractvalue [[DT]][1] : !llvm.array<3 x i64> - // CHECK: [[DTE:%.+]] = llvm.extractvalue [[DT]][2] : !llvm.array<3 x i64> - // CHECK: llvm.call @driveSignal(%arg0, [[SIGPTR_A]], [[BUF]], [[C1_I64]], [[DTS]], [[DTD]], [[DTE]]) - llhd.drv %a, %c0_i1 after %1 : !llhd.sig - - // CHECK: [[C8_I64:%.+]] = llvm.mlir.constant(8 : i64) : i64 - // CHECK: [[TMP1:%.+]] = llvm.mlir.zero : !llvm.ptr - // CHECK: [[TMP2:%.+]] = llvm.getelementptr [[TMP1]][1] : (!llvm.ptr) -> !llvm.ptr, !llvm.array<3 x i5> - // CHECK: [[ARRBYTES:%.+]] = llvm.ptrtoint [[TMP2]] : !llvm.ptr to i64 - // CHECK: [[ARRBITS:%.+]] = llvm.mul [[ARRBYTES]], [[C8_I64]] - - // CHECK: [[C1_I32:%.+]] = llvm.mlir.constant(1 : i32) : i32 - // CHECK: [[BUF:%.+]] = llvm.alloca [[C1_I32]] x !llvm.array<3 x i5> - // CHECK: llvm.store [[ARR]], [[BUF]] : !llvm.array<3 x i5>, !llvm.ptr - // CHECK: [[DTS:%.+]] = llvm.extractvalue [[DT]][0] : !llvm.array<3 x i64> - // CHECK: [[DTD:%.+]] = llvm.extractvalue [[DT]][1] : !llvm.array<3 x i64> - // CHECK: [[DTE:%.+]] = llvm.extractvalue [[DT]][2] : !llvm.array<3 x i64> - // CHECK: llvm.call @driveSignal(%arg0, [[SIGPTR_B]], [[BUF]], [[ARRBITS]], [[DTS]], [[DTD]], [[DTE]]) - llhd.drv %b, %0 after %1 : !llhd.sig> -} - -// CHECK-LABEL: llvm.func @convert_drv_enable( -// CHECK-SAME: %arg0: !llvm.ptr, %arg1: !llvm.ptr, %arg2: !llvm.ptr) { -llhd.entity @convert_drv_enable(%a: !llhd.sig) -> () { - // Last piece of read logic. - // CHECK: [[VALUE_A:%.+]] = llvm.trunc {{%.+}} : i16 to i1 - %0 = llhd.prb %a : !llhd.sig - %1 = llhd.constant_time #llhd.time<1ns, 0d, 0e> - - // CHECK: [[C1_I1:%.+]] = llvm.mlir.constant(1 {{.*}}) : i1 - // CHECK: [[ENABLE:%.+]] = llvm.icmp "eq" {{%.+}}, [[C1_I1]] : i1 - // CHECK: llvm.cond_br [[ENABLE]], ^bb1, ^bb2 - // CHECK: ^bb1: - // CHECK: llvm.call @driveSignal - // CHECK: llvm.br ^bb2 - // CHECK: ^bb2: - llhd.drv %a, %0 after %1 if %0 : !llhd.sig -} - -// TODO: Fix `llhd.reg` code generation and add test. diff --git a/test/Conversion/LLHDToLLVM/convert_simple.mlir b/test/Conversion/LLHDToLLVM/convert_simple.mlir deleted file mode 100644 index 56595fcd55..0000000000 --- a/test/Conversion/LLHDToLLVM/convert_simple.mlir +++ /dev/null @@ -1,49 +0,0 @@ -// RUN: circt-opt %s --convert-llhd-to-llvm --reconcile-unrealized-casts | FileCheck %s - -// CHECK-LABEL: llvm.func @driveSignal(!llvm.ptr, !llvm.ptr, !llvm.ptr, i64, i64, i64, i64) - -// CHECK-LABEL: llvm.func @Foo( -// CHECK-SAME: %arg0: !llvm.ptr, %arg1: !llvm.ptr, %arg2: !llvm.ptr) { -llhd.entity @Foo () -> () { - // Unused in entity definition. Only used at instantiation site. - // CHECK: [[C0:%.+]] = llvm.mlir.constant(false) : i1 - %0 = hw.constant 0 : i1 - - // CHECK: [[SIG_PTR:%.+]] = llvm.getelementptr %arg2[0] : (!llvm.ptr) -> !llvm.ptr, !llvm.struct<(ptr, i64, i64, i64)> - %toggle = llhd.sig "toggle" %0 : i1 - - // CHECK: [[TMP:%.+]] = llvm.getelementptr [[SIG_PTR]][0, 0] : (!llvm.ptr) -> !llvm.ptr, !llvm.struct<(ptr, i64, i64, i64)> - // CHECK: [[SIG_VALUE_PTR:%.+]] = llvm.load [[TMP]] : !llvm.ptr -> !llvm.ptr - // CHECK: [[TMP:%.+]] = llvm.getelementptr [[SIG_PTR]][0, 1] : (!llvm.ptr) -> !llvm.ptr, !llvm.struct<(ptr, i64, i64, i64)> - // CHECK: [[SIG_OFFSET:%.+]] = llvm.load [[TMP]] : !llvm.ptr -> i64 - // CHECK: [[SIG_VALUE:%.+]] = llvm.load [[SIG_VALUE_PTR]] : !llvm.ptr -> i16 - // CHECK: [[TMP:%.+]] = llvm.trunc [[SIG_OFFSET]] : i64 to i16 - // CHECK: [[SIG_VALUE_SHIFTED:%.+]] = llvm.lshr [[SIG_VALUE]], [[TMP]] : i16 - // CHECK: [[SIG_VALUE:%.+]] = llvm.trunc [[SIG_VALUE_SHIFTED]] : i16 to i1 - %1 = llhd.prb %toggle : !llhd.sig - - // CHECK: [[DRV_VALUE:%.+]] = llvm.xor - %allset = hw.constant 1 : i1 - %2 = comb.xor %1, %allset : i1 - - // CHECK: [[DT:%.+]] = llvm.mlir.constant(dense<[1000, 0, 0]> - %dt = llhd.constant_time #llhd.time<1ns, 0d, 0e> - - // CHECK: [[C1_I64:%.+]] = llvm.mlir.constant(1 : i64) : i64 - // CHECK: [[C1_I32:%.+]] = llvm.mlir.constant(1 : i32) : i32 - // CHECK: [[BUF:%.+]] = llvm.alloca [[C1_I32]] x i1 - // CHECK: llvm.store [[DRV_VALUE]], [[BUF]] : i1, !llvm.ptr - // CHECK: [[DT_S:%.+]] = llvm.extractvalue [[DT]][0] : !llvm.array<3 x i64> - // CHECK: [[DT_D:%.+]] = llvm.extractvalue [[DT]][1] : !llvm.array<3 x i64> - // CHECK: [[DT_E:%.+]] = llvm.extractvalue [[DT]][2] : !llvm.array<3 x i64> - // CHECK: llvm.call @driveSignal(%arg0, [[SIG_PTR]], [[BUF]], [[C1_I64]], [[DT_S]], [[DT_D]], [[DT_E]]) - llhd.drv %toggle, %2 after %dt : !llhd.sig -} - -// CHECK-LABEL: @convertConstantTime -llvm.func @convertConstantTime() { - // CHECK-NEXT: {{%.+}} = llvm.mlir.constant(dense<[0, 1, 2]> : tensor<3xi64>) : !llvm.array<3 x i64> - %2 = llhd.constant_time #llhd.time<0ns, 1d, 2e> - // CHECK-NEXT: llvm.return - llvm.return -} diff --git a/test/Conversion/LLHDToLLVM/convert_wait_halt.mlir b/test/Conversion/LLHDToLLVM/convert_wait_halt.mlir deleted file mode 100644 index bc4e714310..0000000000 --- a/test/Conversion/LLHDToLLVM/convert_wait_halt.mlir +++ /dev/null @@ -1,72 +0,0 @@ -// RUN: circt-opt %s --convert-llhd-to-llvm --reconcile-unrealized-casts | FileCheck %s - -// CHECK-LABEL: llvm.func @llhdSuspend(!llvm.ptr, !llvm.ptr, i64, i64, i64) - -// CHECK-LABEL: llvm.func @convert_wait( -// CHECK-SAME: %arg0: !llvm.ptr, %arg1: !llvm.ptr, %arg2: !llvm.ptr) -llhd.proc @convert_wait(%a: !llhd.sig, %b: !llhd.sig) -> () { - // CHECK: [[SIGPTR_A:%.+]] = llvm.getelementptr %arg2[0] : (!llvm.ptr) -> !llvm.ptr, !llvm.struct<(ptr, i64, i64, i64)> - // CHECK: [[SIGPTR_B:%.+]] = llvm.getelementptr %arg2[1] : (!llvm.ptr) -> !llvm.ptr, !llvm.struct<(ptr, i64, i64, i64)> - - // CHECK: [[RESUME_PTR:%.+]] = llvm.getelementptr %arg1[1] : (!llvm.ptr) -> !llvm.ptr, i32 - // CHECK: [[RESUME:%.+]] = llvm.load [[RESUME_PTR]] : !llvm.ptr -> i32 - // CHECK: llvm.br [[BB:\^.+]] - // CHECK: [[BB]]: - - // Resume 1 (after wait) - // CHECK: [[C1_I32:%.+]] = llvm.mlir.constant(1 : - // CHECK: [[EQ:%.+]] = llvm.icmp "eq" [[RESUME]], [[C1_I32]] - // CHECK: llvm.cond_br [[EQ]], [[BB_END:\^.+]], [[BB:\^.+]] - // CHECK: [[BB]]: - - // Resume 0 (entry point) - // CHECK: [[C0_I32:%.+]] = llvm.mlir.constant(0 : - // CHECK: [[EQ:%.+]] = llvm.icmp "eq" [[RESUME]], [[C0_I32]] - // CHECK: llvm.cond_br [[EQ]], [[BB_ENTRY:\^.+]], {{\^.+}} - - // CHECK: [[BB_ENTRY]]: - %0 = llhd.constant_time #llhd.time<1ns, 0d, 0e> - - // Update resume index to 1 (after wait) - // CHECK: [[C1_I32:%.+]] = llvm.mlir.constant(1 : - // CHECK: [[RESUME_PTR:%.+]] = llvm.getelementptr %arg1[1] - // CHECK: llvm.store [[C1_I32]], [[RESUME_PTR]] - - // Clear sensitivity flags for all signals. - // CHECK: [[TMP:%.+]] = llvm.getelementptr %arg1[2] - // CHECK: [[SENSE_PTR:%.+]] = llvm.load [[TMP]] - // CHECK: [[FALSE:%.+]] = llvm.mlir.constant(false) - // CHECK: [[SENSE_A:%.+]] = llvm.getelementptr [[SENSE_PTR]][0] - // CHECK: llvm.store [[FALSE]], [[SENSE_A]] : i1, !llvm.ptr - // CHECK: [[SENSE_B:%.+]] = llvm.getelementptr [[SENSE_PTR]][1] - // CHECK: llvm.store [[FALSE]], [[SENSE_B]] : i1, !llvm.ptr - - // Set sensitivity flag for signal "b" (index 1). - // CHECK: [[SIGIDX_PTR:%.+]] = llvm.getelementptr [[SIGPTR_B]][2] - // CHECK: [[SIGIDX:%.+]] = llvm.load [[SIGIDX_PTR]] : !llvm.ptr -> i64 - // CHECK: [[TRUE:%.+]] = llvm.mlir.constant(true) - // CHECK: [[SENSE:%.+]] = llvm.getelementptr [[SENSE_PTR]][[[SIGIDX]]] - // CHECK: llvm.store [[TRUE]], [[SENSE]] : i1, !llvm.ptr - - // CHECK: llvm.call @llhdSuspend(%arg0, %arg1, {{%.+}}, {{%.+}}, {{%.+}}) - llhd.wait for %0, (%b : !llhd.sig), ^end - - // CHECK: [[BB_END]]: - // CHECK: llvm.br [[BB_END]] -^end: - cf.br ^end -} - -// CHECK-LABEL: llvm.func @convert_halt( -// CHECK-SAME: %arg0: !llvm.ptr, %arg1: !llvm.ptr, %arg2: !llvm.ptr) -llhd.proc @convert_halt() -> (%a: !llhd.sig, %b: !llhd.sig) { - // Clear sensitivity flags for all signals. - // CHECK: [[TMP:%.+]] = llvm.getelementptr %arg1[2] - // CHECK: [[SENSE_PTR:%.+]] = llvm.load [[TMP]] - // CHECK: [[FALSE:%.+]] = llvm.mlir.constant(false) - // CHECK: [[SENSE_A:%.+]] = llvm.getelementptr [[SENSE_PTR]][0] - // CHECK: llvm.store [[FALSE]], [[SENSE_A]] : i1, !llvm.ptr - // CHECK: [[SENSE_B:%.+]] = llvm.getelementptr [[SENSE_PTR]][1] - // CHECK: llvm.store [[FALSE]], [[SENSE_B]] : i1, !llvm.ptr - llhd.halt -} diff --git a/test/Conversion/LLHDToLLVM/signal-init-error.mlir b/test/Conversion/LLHDToLLVM/signal-init-error.mlir deleted file mode 100644 index e43dc66591..0000000000 --- a/test/Conversion/LLHDToLLVM/signal-init-error.mlir +++ /dev/null @@ -1,16 +0,0 @@ -// RUN: circt-opt %s --convert-llhd-to-llvm --verify-diagnostics --split-input-file - -llhd.entity @root() -> () { - // expected-error @+1 {{failed to legalize operation 'llhd.inst'}} - llhd.inst "inst" @initUsesProbedValue () -> () : () -> () -} - -llhd.entity @initUsesProbedValue () -> () { - %0 = hw.constant 0 : i1 - %1 = llhd.sig "sig" %0 : i1 - %2 = llhd.prb %1 : !llhd.sig - %3 = hw.array_create %2, %2 : i1 - %4 = llhd.sig "sig1" %3 : !hw.array<2xi1> -} - -// TODO: add testcase where the init value of llhd.sig comes from a block argument diff --git a/test/Conversion/LLHDToLLVM/signal-init.mlir b/test/Conversion/LLHDToLLVM/signal-init.mlir deleted file mode 100644 index 1fc1499a58..0000000000 --- a/test/Conversion/LLHDToLLVM/signal-init.mlir +++ /dev/null @@ -1,77 +0,0 @@ -// RUN: circt-opt %s --convert-llhd-to-llvm --reconcile-unrealized-casts | FileCheck %s - -// CHECK-LABEL: llvm.func @llhd_init -// CHECK-SAME: %arg0: !llvm.ptr) { -llhd.entity @Root() -> () { - // CHECK: [[SIZE:%.+]] = llvm.ptrtoint - // CHECK: [[MEM:%.+]] = llvm.call @malloc([[SIZE]]) - // CHECK: llvm.call @allocEntity(%arg0, [[OWNER:%.+]], [[MEM]]) - - // sig0 - // CHECK: [[VALUE:%.+]] = llvm.mlir.constant(1337 : i42) : i42 - // CHECK: llvm.store [[VALUE]], [[BUF:%.+]] : i42, !llvm.ptr - // CHECK: [[SIZE:%.+]] = llvm.mlir.constant(6 : - // CHECK: llvm.call @allocSignal(%arg0, {{%.+}}, [[OWNER]], [[BUF]], [[SIZE]]) - - // sig1 - // CHECK: [[TMP:%.+]] = llvm.getelementptr {{%.+}}[1] : (!llvm.ptr) -> !llvm.ptr, !llvm.array<2 x i1> - // CHECK: [[SIZE:%.+]] = llvm.ptrtoint [[TMP]] : !llvm.ptr to i64 - // CHECK: llvm.store {{%.+}}, [[BUF:%.+]] : !llvm.array<2 x i1>, !llvm.ptr - // CHECK: [[SIGID1:%.+]] = llvm.call @allocSignal(%arg0, {{%.+}}, [[OWNER]], [[BUF]], [[SIZE]]) - // sig1 layout details - // CHECK: [[ELEMENT_COUNT:%.+]] = llvm.mlir.constant(2 : - // CHECK: [[TMP:%.+]] = llvm.getelementptr {{%.+}}[0, 1] : (!llvm.ptr) -> !llvm.ptr, !llvm.array<2 x i1> - // CHECK: [[ELEMENT_SIZE:%.+]] = llvm.ptrtoint [[TMP]] - // CHECK: llvm.call @addSigArrayElements(%arg0, [[SIGID1]], [[ELEMENT_SIZE]], [[ELEMENT_COUNT]]) - - // sig2 - // CHECK: [[TMP:%.+]] = llvm.getelementptr {{%.+}}[1] : (!llvm.ptr) -> !llvm.ptr, !llvm.struct<(i5, i1)> - // CHECK: [[SIZE:%.+]] = llvm.ptrtoint [[TMP]] : !llvm.ptr to i64 - // CHECK: llvm.store {{%.+}}, [[BUF:%.+]] : !llvm.struct<(i5, i1)>, !llvm.ptr - // CHECK: [[SIGID2:%.+]] = llvm.call @allocSignal(%arg0, {{%.+}}, [[OWNER]], [[BUF]], [[SIZE]]) - // sig2 layout details f2 - // CHECK: [[TMP:%.+]] = llvm.getelementptr {{%.+}}[0, 0] : (!llvm.ptr) -> !llvm.ptr, !llvm.struct<(i5, i1)> - // CHECK: [[ELEMENT_OFFSET:%.+]] = llvm.ptrtoint [[TMP]] - // CHECK: [[TMP:%.+]] = llvm.getelementptr {{%.+}}[1] : (!llvm.ptr) -> !llvm.ptr, i5 - // CHECK: [[ELEMENT_SIZE:%.+]] = llvm.ptrtoint [[TMP]] - // CHECK: llvm.call @addSigStructElement(%arg0, [[SIGID2]], [[ELEMENT_OFFSET]], [[ELEMENT_SIZE]]) - // sig2 layout details f1 - // CHECK: [[TMP:%.+]] = llvm.getelementptr {{%.+}}[0, 1] : (!llvm.ptr) -> !llvm.ptr, !llvm.struct<(i5, i1)> - // CHECK: [[ELEMENT_OFFSET:%.+]] = llvm.ptrtoint [[TMP]] - // CHECK: [[TMP:%.+]] = llvm.getelementptr {{%.+}}[1] : (!llvm.ptr) -> !llvm.ptr, i1 - // CHECK: [[ELEMENT_SIZE:%.+]] = llvm.ptrtoint [[TMP]] - // CHECK: llvm.call @addSigStructElement(%arg0, [[SIGID2]], [[ELEMENT_OFFSET]], [[ELEMENT_SIZE]]) - - llhd.inst "inst0" @Signals () -> () : () -> () - - // CHECK: llvm.call @allocEntity(%arg0, [[OWNER:%.+]], {{%.+}}) - - // sig3 - // CHECK-DAG: [[FALSE:%.+]] = llvm.mlir.constant(false) - // CHECK-DAG: [[TMP1:%.+]] = llvm.mlir.undef - // CHECK-DAG: [[TMP2:%.+]] = llvm.insertvalue [[FALSE]], [[TMP1]][0] - // CHECK-DAG: [[TMP3:%.+]] = llvm.insertvalue [[FALSE]], [[TMP2]][1] - // CHECK: llvm.store [[TMP3]], [[BUF:%.+]] : !llvm.array<2 x i1>, !llvm.ptr - // CHECK: [[SIGID3:%.+]] = llvm.call @allocSignal(%arg0, {{%.+}}, [[OWNER]], [[BUF]], {{%.+}}) - - llhd.inst "inst1" @PartiallyLowered () -> () : () -> () - // llhd.inst "inst2" @MultipleResults () -> () : () -> () -} - -llhd.entity @Signals () -> () { - %0 = hw.constant 1337 : i42 - %1 = llhd.sig "sig0" %0 : i42 - %2 = hw.aggregate_constant [0 : i1, 1 : i1] : !hw.array<2xi1> - %3 = llhd.sig "sig1" %2 : !hw.array<2xi1> - %4 = hw.aggregate_constant [0 : i1, 1 : i5] : !hw.struct - %5 = llhd.sig "sig2" %4 : !hw.struct -} - -llhd.entity @PartiallyLowered () -> () { - %0 = llvm.mlir.constant(false) : i1 - %1 = llvm.mlir.undef : !llvm.array<2 x i1> - %2 = llvm.insertvalue %0, %1[0] : !llvm.array<2 x i1> - %3 = llvm.insertvalue %0, %2[1] : !llvm.array<2 x i1> - %4 = builtin.unrealized_conversion_cast %3 : !llvm.array<2 x i1> to !hw.array<2xi1> - %5 = llhd.sig "sig3" %4 : !hw.array<2xi1> -} diff --git a/test/lit.cfg.py b/test/lit.cfg.py index 6807b90e2f..7db81a899a 100644 --- a/test/lit.cfg.py +++ b/test/lit.cfg.py @@ -75,11 +75,6 @@ if config.zlib == "1": if config.scheduling_or_tools != "": config.available_features.add('or-tools') -# Add llhd-sim if it is built. -if config.llhd_sim_enabled: - config.available_features.add('llhd-sim') - tools.append('llhd-sim') - # Add circt-verilog if the Slang frontend is enabled. if config.slang_frontend_enabled: config.available_features.add('slang') diff --git a/test/lit.site.cfg.py.in b/test/lit.site.cfg.py.in index 1c1e7215f2..3eb9be6b7a 100644 --- a/test/lit.site.cfg.py.in +++ b/test/lit.site.cfg.py.in @@ -38,7 +38,6 @@ config.circt_shlib_dir = "@LLVM_LIBRARY_OUTPUT_INTDIR@" config.verilator_path = "@VERILATOR_PATH@" config.zlib = "@HAVE_ZLIB@" config.scheduling_or_tools = "@SCHEDULING_OR_TOOLS@" -config.llhd_sim_enabled = @CIRCT_LLHD_SIM_ENABLED@ config.slang_frontend_enabled = @CIRCT_SLANG_FRONTEND_ENABLED@ # Support substitution of the tools_dir with user parameters. This is diff --git a/test/llhd-sim/commandline.mlir b/test/llhd-sim/commandline.mlir deleted file mode 100644 index c952231d74..0000000000 --- a/test/llhd-sim/commandline.mlir +++ /dev/null @@ -1,7 +0,0 @@ -// REQUIRES: llhd-sim -// RUN: llhd-sim --help | FileCheck %s --implicit-check-not='{{[Oo]}}ptions:' - -// CHECK: OVERVIEW: LLHD simulator -// CHECK: OPTIONS -// CHECK: Generic Options -// CHECK: llhd-sim Options diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index c74fb9f95a..7614afa486 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -12,7 +12,6 @@ add_subdirectory(firtool) add_subdirectory(handshake-runner) add_subdirectory(hlstool) add_subdirectory(ibistool) -add_subdirectory(llhd-sim) add_subdirectory(om-linker) add_subdirectory(py-split-input-file) diff --git a/tools/circt-opt/CMakeLists.txt b/tools/circt-opt/CMakeLists.txt index d8d575554b..be49d8901a 100644 --- a/tools/circt-opt/CMakeLists.txt +++ b/tools/circt-opt/CMakeLists.txt @@ -46,7 +46,6 @@ target_link_libraries(circt-opt CIRCTHandshakeTransforms CIRCTLECTransforms CIRCTLLHD - CIRCTLLHDToLLVM CIRCTHWToLLVM CIRCTCombToArith CIRCTCombToLLVM diff --git a/tools/llhd-sim/CMakeLists.txt b/tools/llhd-sim/CMakeLists.txt deleted file mode 100644 index 059abee197..0000000000 --- a/tools/llhd-sim/CMakeLists.txt +++ /dev/null @@ -1,16 +0,0 @@ -set(LIBS - CIRCTLLHD - CIRCTComb - CIRCTHW - CIRCTLLHDToLLVM - CIRCTLLHDSimEngine - ) - -# llhd-sim fails to link on Windows with MSVC. -IF(CIRCT_LLHD_SIM_ENABLED) - add_circt_tool(llhd-sim - llhd-sim.cpp) - - llvm_update_compile_flags(llhd-sim) - target_link_libraries(llhd-sim PRIVATE ${LIBS}) -endif() diff --git a/tools/llhd-sim/llhd-sim.cpp b/tools/llhd-sim/llhd-sim.cpp deleted file mode 100644 index 7cc27fca19..0000000000 --- a/tools/llhd-sim/llhd-sim.cpp +++ /dev/null @@ -1,234 +0,0 @@ -//===- llhd-sim.cpp - LLHD simulator tool -----------------------*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// This file implements a command line tool to run LLHD simulation. -// -//===----------------------------------------------------------------------===// - -#include "circt/Conversion/LLHDToLLVM.h" -#include "circt/Dialect/Comb/CombDialect.h" -#include "circt/Dialect/HW/HWDialect.h" -#include "circt/Dialect/LLHD/IR/LLHDDialect.h" -#include "circt/Dialect/LLHD/Simulator/Engine.h" -#include "circt/Dialect/LLHD/Simulator/Trace.h" -#include "circt/Support/Version.h" - -#include "mlir/Conversion/ReconcileUnrealizedCasts/ReconcileUnrealizedCasts.h" -#include "mlir/Dialect/ControlFlow/IR/ControlFlow.h" -#include "mlir/Dialect/Func/IR/FuncOps.h" -#include "mlir/Dialect/LLVMIR/LLVMDialect.h" -#include "mlir/ExecutionEngine/OptUtils.h" -#include "mlir/Parser/Parser.h" -#include "mlir/Pass/PassManager.h" -#include "mlir/Support/FileUtilities.h" -#include "mlir/Target/LLVMIR/Dialect/Builtin/BuiltinToLLVMIRTranslation.h" -#include "mlir/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.h" -#include "mlir/Target/LLVMIR/Export.h" -#include "mlir/Transforms/Passes.h" - -#include "llvm/Support/Error.h" -#include "llvm/Support/InitLLVM.h" -#include "llvm/Support/SourceMgr.h" -#include "llvm/Support/ToolOutputFile.h" - -using namespace llvm; -using namespace mlir; -using namespace mlir::func; -using namespace circt; -using namespace circt::llhd::sim; - -static cl::OptionCategory mainCategory("llhd-sim Options"); - -static cl::opt inputFilename(cl::Positional, - cl::desc(""), - cl::init("-"), cl::cat(mainCategory)); - -static cl::opt outputFilename("o", cl::desc("Output filename"), - cl::value_desc("filename"), - cl::init("-"), - cl::cat(mainCategory)); - -static cl::opt nSteps("n", cl::desc("Set the maximum number of steps"), - cl::value_desc("max-steps"), cl::cat(mainCategory)); - -static cl::opt maxTime( - "T", - cl::desc("Stop the simulation after the given amount of simulation time in " - "picoseconds, including all sub-steps for that real-time step"), - cl::value_desc("max-time"), cl::cat(mainCategory)); - -static cl::opt - dumpLLVMDialect("dump-llvm-dialect", - cl::desc("Dump the LLVM IR dialect module"), - cl::cat(mainCategory)); - -static cl::opt dumpLLVMIR("dump-llvm-ir", - cl::desc("Dump the LLVM IR module"), - cl::cat(mainCategory)); - -static cl::opt dumpMLIR("dump-mlir", - cl::desc("Dump the original MLIR module"), - cl::cat(mainCategory)); - -static cl::opt dumpLayout("dump-layout", - cl::desc("Dump the gathered instance layout"), - cl::cat(mainCategory)); - -static cl::opt root( - "root", - cl::desc("Specify the name of the entity to use as root of the design"), - cl::value_desc("root_name"), cl::init("root"), cl::cat(mainCategory)); -static cl::alias rootA("r", cl::desc("Alias for -root"), cl::aliasopt(root), - cl::cat(mainCategory)); - -enum OptLevel { O0, O1, O2, O3 }; - -static cl::opt - optimizationLevel(cl::desc("Choose optimization level:"), cl::init(O2), - cl::values(clEnumVal(O0, "Run passes and codegen at O0"), - clEnumVal(O1, "Run passes and codegen at O1"), - clEnumVal(O2, "Run passes and codegen at O2"), - clEnumVal(O3, "Run passes and codegen at O3")), - cl::cat(mainCategory)); - -static cl::opt traceMode( - "trace-format", cl::desc("Choose the dump format:"), - cl::init(TraceMode::Full), - cl::values( - clEnumValN(TraceMode::Full, "full", - "Dump signal changes for every time step and sub-step, " - "for all instances"), - clEnumValN(TraceMode::Reduced, "reduced", - "Dump signal changes for every time-step and " - "sub-step, only for the top-level instance"), - clEnumValN(TraceMode::Merged, "merged", - "Only dump changes for real-time steps, for all instances"), - clEnumValN(TraceMode::MergedReduce, "merged-reduce", - "Only dump changes for real-time steps, only for the " - "top-level instance"), - clEnumValN( - TraceMode::NamedOnly, "named-only", - "Only dump changes for real-time steps, only for top-level " - "instance and signals not having the default name '(sig)?[0-9]*'"), - clEnumValN(TraceMode::None, "none", "Don't dump a signal trace")), - cl::cat(mainCategory)); - -static cl::list - sharedLibs("shared-libs", - cl::desc("Libraries to link dynamically. Specify absolute path " - "to llhd-signals-runtime-wrappers for GCC or Windows. " - "Optional otherwise."), - cl::ZeroOrMore, cl::MiscFlags::CommaSeparated, - cl::cat(mainCategory)); - -static int dumpLLVM(ModuleOp module, MLIRContext &context) { - if (dumpLLVMDialect) { - module.dump(); - llvm::errs() << "\n"; - return 0; - } - - // Translate the module, that contains the LLVM dialect, to LLVM IR. - llvm::LLVMContext llvmContext; - auto llvmModule = mlir::translateModuleToLLVMIR(module, llvmContext); - if (!llvmModule) { - llvm::errs() << "Failed to emit LLVM IR\n"; - return -1; - } - - auto llvmTransformer = - makeOptimizingTransformer(optimizationLevel, 0, nullptr); - - if (auto err = llvmTransformer(llvmModule.get())) { - llvm::errs() << "Failed to optimize LLVM IR " << err << "\n"; - return -1; - } - - llvm::errs() << *llvmModule << "\n"; - return 0; -} - -static LogicalResult applyMLIRPasses(ModuleOp module) { - PassManager pm(module.getContext()); - - pm.addPass(createConvertLLHDToLLVMPass()); - pm.addPass(::mlir::createReconcileUnrealizedCastsPass()); - - return pm.run(module); -} - -int main(int argc, char **argv) { - InitLLVM y(argc, argv); - - // Set the bug report message to indicate users should file issues on - // llvm/circt and not llvm/llvm-project. - setBugReportMsg(circtBugReportMsg); - - // Hide default LLVM options, other than for this tool. - cl::HideUnrelatedOptions(mainCategory); - - cl::ParseCommandLineOptions(argc, argv, "LLHD simulator\n"); - - // Set up the input and output files. - std::string errorMessage; - auto file = openInputFile(inputFilename, &errorMessage); - if (!file) { - llvm::errs() << errorMessage << "\n"; - return 1; - } - - auto output = openOutputFile(outputFilename, &errorMessage); - if (!output) { - llvm::errs() << errorMessage << "\n"; - exit(1); - } - - // Parse the input file. - SourceMgr mgr; - mgr.AddNewSourceBuffer(std::move(file), SMLoc()); - - MLIRContext context; - // Load the dialects - context - .loadDialect(); - mlir::registerLLVMDialectTranslation(context); - mlir::registerBuiltinDialectTranslation(context); - - mlir::OwningOpRef module( - parseSourceFile(mgr, &context)); - - if (dumpMLIR) { - module->dump(); - llvm::errs() << "\n"; - return 0; - } - - SmallVector sharedLibPaths(sharedLibs.begin(), - sharedLibs.end()); - - llhd::sim::Engine engine( - output->os(), *module, &applyMLIRPasses, - makeOptimizingTransformer(optimizationLevel, 0, nullptr), root, traceMode, - sharedLibPaths); - - if (dumpLLVMDialect || dumpLLVMIR) { - return dumpLLVM(engine.getModule(), context); - } - - if (dumpLayout) { - engine.dumpStateLayout(); - engine.dumpStateSignalTriggers(); - return 0; - } - - engine.simulate(nSteps, maxTime); - - output->keep(); - return 0; -}