Skip to content
41 changes: 41 additions & 0 deletions integration_test/arcilator/JIT/runtime-environment.mlir
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// RUN: arcilator %s --run --jit-entry=main 2>&1 >/dev/null | FileCheck -strict-whitespace %s
// REQUIRES: arcilator-jit

// CHECK: Hello World!{{[[:space:]]}}

module {

llvm.func @_arc_env_get_print_stream(i32) -> !llvm.ptr
llvm.func @_arc_libc_fprintf(!llvm.ptr, !llvm.ptr, ...) -> i32
llvm.func @_arc_libc_fputc(i32, !llvm.ptr) -> i32
llvm.func @_arc_libc_fputs(!llvm.ptr, !llvm.ptr) -> i32

llvm.mlir.global internal constant @global_hello("He%c%co \00") {addr_space = 0 : i32}
llvm.mlir.global internal constant @global_world("World\00") {addr_space = 0 : i32}

arc.model @dut io !hw.modty<input clk : !seq.clock> {
^bb0(%arg0: !arc.storage<1>):
%cst0 = llvm.mlir.constant(0 : i32) : i32
%ascii_em = llvm.mlir.constant(33 : i32) : i32
%ascii_lf = llvm.mlir.constant(10 : i32) : i32
%ascii_l = llvm.mlir.constant(108 : i32) : i32
%in_clk = arc.root_input "clk", %arg0 {offset = 0 : i32} : (!arc.storage<1>) -> !arc.state<i1>
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems to be unused. Was there some issue with the storage lowering when removing this?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, tbh I didn't even try without at least one port as I was unsure whether this is a supported use-case.
Looks like it is working just fine. 👍

%stderr = llvm.call @_arc_env_get_print_stream(%cst0) : (i32) -> !llvm.ptr

%hello = llvm.mlir.addressof @global_hello : !llvm.ptr
%0 = llvm.call @_arc_libc_fprintf(%stderr, %hello, %ascii_l, %ascii_l) vararg(!llvm.func<i32 (ptr, ptr, ...)>) : (!llvm.ptr, !llvm.ptr, i32, i32) -> i32

%world = llvm.mlir.addressof @global_world : !llvm.ptr
%1 = llvm.call @_arc_libc_fputs(%world, %stderr) : (!llvm.ptr, !llvm.ptr) -> i32

%2 = llvm.call @_arc_libc_fputc(%ascii_em, %stderr) : (i32, !llvm.ptr) -> i32
%3 = llvm.call @_arc_libc_fputc(%ascii_lf, %stderr) : (i32, !llvm.ptr) -> i32
}

func.func @main() {
arc.sim.instantiate @dut as %arg0 {
arc.sim.step %arg0 : !arc.sim.instance<@dut>
}
return
}
}
9 changes: 8 additions & 1 deletion tools/arcilator/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
if(ARCILATOR_JIT_ENABLED)
add_compile_definitions(ARCILATOR_ENABLE_JIT)
add_subdirectory(jit-env)
set(ARCILATOR_JIT_LLVM_COMPONENTS native)
set(ARCILATOR_JIT_DEPS MLIRExecutionEngine)
set(ARCILATOR_JIT_DEPS MLIRExecutionEngine arc-jit-env)
endif()

set(LLVM_LINK_COMPONENTS Support ${ARCILATOR_JIT_LLVM_COMPONENTS})
Expand Down Expand Up @@ -45,3 +46,9 @@ configure_file(arcilator-runtime.h
${CIRCT_TOOLS_DIR}/arcilator-runtime.h)
add_custom_target(arcilator-runtime-header SOURCES
${CIRCT_TOOLS_DIR}/arcilator-runtime.h)

if(ARCILATOR_JIT_ENABLED)
target_include_directories(arcilator PRIVATE
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/jit-env>
)
endif()
51 changes: 51 additions & 0 deletions tools/arcilator/arcilator-runtime.h
Original file line number Diff line number Diff line change
@@ -1,12 +1,63 @@
// NOLINTBEGIN
#pragma once
#include <array>
#include <cstdarg>
#include <cstdint>
#include <cstdio>
#include <cstring>
#include <functional>
#include <ostream>
#include <vector>

// Sanity checks for binary compatibility
#ifdef __BYTE_ORDER__
#if (__BYTE_ORDER__ != __ORDER_LITTLE_ENDIAN__)
#error Unsupported endianess
#endif
#endif
static_assert(sizeof(int) == 4, "Unsupported ABI");
static_assert(sizeof(long long) == 8, "Unsupported ABI");

// --- Exports to the IR ---

#ifdef _WIN32
#define ARCEXPORT(rtype) extern "C" __declspec(dllexport) rtype __cdecl
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you make this consistent with MLIR_RUNNERUTILS_EXPORT macro?

Copy link
Copy Markdown
Contributor Author

@fzi-hielscher fzi-hielscher Aug 6, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MLIR_RUNNERUTILS_EXPORT does not include extern "C" yet it looks like it is prefixed with extern "C" every time it is used. That seems odd to me.
The __cdecl is probably not required. It don't have access to my Windows PC right now, I'll check later today. Basically, I've just used every magic incantation here that I could come up with to increase the likelihood of it linking. 😅

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've no clue about those windows specific things, but thanks for making this work with Windows as well! 🚀

#else
#define ARCEXPORT(rtype) extern "C" rtype
#endif

// libc Adapters
ARCEXPORT(int) _arc_libc_fprintf(FILE *stream, const char *format, ...) {
int result;
va_list args;
va_start(args, format);
result = vfprintf(stream, format, args);
va_end(args);
return result;
}

ARCEXPORT(int) _arc_libc_fputs(const char *str, FILE *stream) {
return fputs(str, stream);
}

ARCEXPORT(int) _arc_libc_fputc(int ch, FILE *stream) {
return fputc(ch, stream);
}

// Runtime Environment calls

#define ARC_ENV_DECL_GET_PRINT_STREAM(idarg) \
ARCEXPORT(FILE *) _arc_env_get_print_stream(uint32_t idarg)

#ifndef ARC_NO_DEFAULT_GET_PRINT_STREAM
ARC_ENV_DECL_GET_PRINT_STREAM(id) {
(void)id;
return stderr;
}
Comment on lines +57 to +60
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm wondering if it would make sense to let the id influence whether stderr or stdout is returned?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I've added this in anticipation of #7111. The problem is, AFAIK we don't have any lowering path that would support this, let alone an agreed on concept for file descriptors / output streams in the IR. I had a short exchange with @seldridge on this in the discord channel some weeks ago. For now we could at best add a command line flag to control this globally.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah I see. I was just thinking about adding an if-statement here to allow the LLVM call operation to choose between stderr and stdout when writing MLIR integration tests and examples by hand. But this is not important at all. We can also just leave it as is until we have a proper story for file descriptors.

#endif // ARC_NO_DEFAULT_GET_PRINT_STREAM

// ----------------

struct Signal {
const char *name;
unsigned offset;
Expand Down
11 changes: 11 additions & 0 deletions tools/arcilator/arcilator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,10 @@
#include "llvm/Support/TargetSelect.h"
#include "llvm/Support/ToolOutputFile.h"

#ifdef ARCILATOR_ENABLE_JIT
#include "arcilator-jit-env.h"
#endif

#include <optional>

using namespace mlir;
Expand Down Expand Up @@ -430,6 +434,8 @@ static LogicalResult processBuffer(
return failure();
}

arc_jit_runtime_env_init();

mlir::ExecutionEngineOptions engineOptions;
engineOptions.jitCodeGenOptLevel = llvm::CodeGenOptLevel::Aggressive;
engineOptions.transformer = mlir::makeOptimizingTransformer(
Expand All @@ -444,6 +450,8 @@ static LogicalResult processBuffer(
llvm::errs() << "failed to create execution engine: "
<< info.message() << "\n";
});

arc_jit_runtime_env_deinit();
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: you can use llvm::make_scope_exit to automatically call deinit.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice. Thanks!

return failure();
}

Expand All @@ -454,12 +462,15 @@ static LogicalResult processBuffer(
llvm::errs() << "failed to run simulation: " << info.message()
<< "\n";
});

arc_jit_runtime_env_deinit();
return failure();
}

void (*simulationFunc)(void **) = *expectedFunc;
(*simulationFunc)(nullptr);

arc_jit_runtime_env_deinit();
return success();
}
#endif // ARCILATOR_ENABLE_JIT
Expand Down
32 changes: 32 additions & 0 deletions tools/arcilator/jit-env/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
add_library(arc-jit-env SHARED arcilator-jit-env.cpp)

if(WIN32)
# Put the DLL into the binary directory so we don't have
# to worry about it not being found.
set_target_properties(arc-jit-env
PROPERTIES
LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib
RUNTIME_OUTPUT_DIRECTORY ${CIRCT_TOOLS_DIR}$<0:>
CXX_VISIBILITY_PRESET "default"
)
install(TARGETS arc-jit-env
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
COMPONENT arc-jit-env
)

else()

set_target_properties(arc-jit-env
PROPERTIES
LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib
CXX_VISIBILITY_PRESET "default"
)
install(TARGETS arc-jit-env
DESTINATION ${CMAKE_INSTALL_LIBDIR}
COMPONENT arc-jit-env
)

endif()

7 changes: 7 additions & 0 deletions tools/arcilator/jit-env/arcilator-jit-env.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#include "../arcilator-runtime.h"
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should probably add the standard header comments here.


#define ARCJITENV_EXPORTS
#include "arcilator-jit-env.h"

ARCJITENV_API int arc_jit_runtime_env_init(void) { return 0; }
ARCJITENV_API int arc_jit_runtime_env_deinit(void) { return 0; }
23 changes: 23 additions & 0 deletions tools/arcilator/jit-env/arcilator-jit-env.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// Arcilator JIT runtime environment API facing the arcilator.cpp
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should probably also add the standard header comments here.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, nice catch. I'm a little surprised the CI did not complain about it. The arcilator-runtime.h and arcilator-header-cpp.py should probably also at least get a license identifier, shouldn't they?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, seems like it slipped through there.

#pragma once

#ifdef _WIN32

#ifdef ARCJITENV_EXPORTS
#define ARCJITENV_API extern "C" __declspec(dllexport)
#else
#define ARCJITENV_API extern "C" __declspec(dllimport)
#endif // ARCJITENV_EXPORTS

#else

#define ARCJITENV_API extern "C"

#endif // _WIN32

// These don't do anything at the moment. It is still
// required to call them to make sure the library
// is linked and loaded before the JIT engine starts.

ARCJITENV_API int arc_jit_runtime_env_init(void);
ARCJITENV_API int arc_jit_runtime_env_deinit(void);