From b14fce113fa45da85c8c3528552ae446ced340ca Mon Sep 17 00:00:00 2001 From: Johannes Stoelp Date: Sat, 5 Nov 2022 23:24:05 +0100 Subject: llvm: small ORCv2 jit example --- content/2022-07-07-llvm-orc-jit/.clang-format | 104 ++++++++++++++++++++++++ content/2022-07-07-llvm-orc-jit/Makefile | 27 +++++++ content/2022-07-07-llvm-orc-jit/ccompiler.h | 109 ++++++++++++++++++++++++++ content/2022-07-07-llvm-orc-jit/jit.h | 100 +++++++++++++++++++++++ content/2022-07-07-llvm-orc-jit/main.cc | 55 +++++++++++++ 5 files changed, 395 insertions(+) create mode 100644 content/2022-07-07-llvm-orc-jit/.clang-format create mode 100644 content/2022-07-07-llvm-orc-jit/Makefile create mode 100644 content/2022-07-07-llvm-orc-jit/ccompiler.h create mode 100644 content/2022-07-07-llvm-orc-jit/jit.h create mode 100644 content/2022-07-07-llvm-orc-jit/main.cc (limited to 'content/2022-07-07-llvm-orc-jit') diff --git a/content/2022-07-07-llvm-orc-jit/.clang-format b/content/2022-07-07-llvm-orc-jit/.clang-format new file mode 100644 index 0000000..9a0c292 --- /dev/null +++ b/content/2022-07-07-llvm-orc-jit/.clang-format @@ -0,0 +1,104 @@ +# dotfiles -- clang-format +# author: johannst +# doc : https://clang.llvm.org/docs/ClangFormatStyleOptions.html + +Language: Cpp +Standard: Auto + +AccessModifierOffset: -2 +AlignAfterOpenBracket: Align +AlignConsecutiveMacros: true +AlignConsecutiveAssignments: false +AlignConsecutiveDeclarations: false +AlignEscapedNewlines: Left +AlignOperands: true +AlignTrailingComments: true +AllowAllArgumentsOnNextLine: true +AllowAllConstructorInitializersOnNextLine: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortBlocksOnASingleLine: false +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: Inline +AllowShortIfStatementsOnASingleLine: Never +AllowShortLambdasOnASingleLine: All +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: true +AlwaysBreakTemplateDeclarations: Yes +BinPackArguments: true +BinPackParameters: true +BreakBeforeBraces: Custom +BraceWrapping: + AfterCaseLabel: false + AfterClass: false + AfterControlStatement: false + AfterEnum: false + AfterFunction: false + AfterNamespace: false + AfterStruct: false + AfterUnion: false + AfterExternBlock: false + BeforeCatch: false + BeforeElse: false + IndentBraces: false + SplitEmptyFunction: true + SplitEmptyRecord: true + SplitEmptyNamespace: true +BreakBeforeBinaryOperators: None +BreakBeforeTernaryOperators: true +BreakConstructorInitializers: AfterColon +BreakConstructorInitializersBeforeComma: false +BreakInheritanceList: BeforeColon +BreakBeforeInheritanceComma: false +BreakStringLiterals: true +ColumnLimit: 80 +CompactNamespaces: true +ConstructorInitializerAllOnOneLineOrOnePerLine: false +ConstructorInitializerIndentWidth: 2 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: true +DerivePointerAlignment: false +DisableFormat: false +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: true +IncludeBlocks: Preserve +# Could use `IncludeCategories` to define rules for includes +IndentCaseLabels: true +IndentPPDirectives: AfterHash +IndentWidth: 4 +IndentWrappedFunctionNames: false +KeepEmptyLinesAtTheStartOfBlocks: false +MacroBlockBegin: '' +MacroBlockEnd: '' +MaxEmptyLinesToKeep: 2 +NamespaceIndentation: All +PenaltyBreakAssignment: 2 +PenaltyBreakBeforeFirstCallParameter: 1 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakString: 1000 +PenaltyBreakTemplateDeclaration: 10 +PenaltyExcessCharacter: 1000000 +PenaltyReturnTypeOnItsOwnLine: 200 +PointerAlignment: Left +ReflowComments: true +SortIncludes: true +SortUsingDeclarations: true +SpaceAfterCStyleCast: false +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: false +SpaceBeforeAssignmentOperators: true +SpaceBeforeCpp11BracedList: false +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeParens: ControlStatements +SpaceBeforeRangeBasedForLoopColon: true +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 2 +SpacesInAngles: false +SpacesInCStyleCastParentheses: false +SpacesInContainerLiterals: true +SpacesInParentheses: false +SpacesInSquareBrackets: false +TabWidth: 4 +UseTab: Never diff --git a/content/2022-07-07-llvm-orc-jit/Makefile b/content/2022-07-07-llvm-orc-jit/Makefile new file mode 100644 index 0000000..d292bf1 --- /dev/null +++ b/content/2022-07-07-llvm-orc-jit/Makefile @@ -0,0 +1,27 @@ +LIBS = $(shell llvm-config --libs) +LIBS += -lclang-cpp + +CXXFLAGS = -Wall -Wextra -Werror +CXXFLAGS += -O0 +CXXFLAGS += -g +CXXFLAGS += -std=c++17 + +SAN ?= 1 +ifeq ($(SAN),1) +FLAGS = -fsanitize=address -fsanitize=leak -fsanitize=undefined +endif + +run: main + ./$^ + +main: main.o + $(CXX) -o $@ $^ $(LIBS) $(FLAGS) + +%.o: %.cc + $(CXX) -o $@ -c $^ $(CXXFLAGS) $(FLAGS) + +fmt: + clang-format -i *.cc *.h + +clean: + $(RM) main *.o diff --git a/content/2022-07-07-llvm-orc-jit/ccompiler.h b/content/2022-07-07-llvm-orc-jit/ccompiler.h new file mode 100644 index 0000000..57a2b62 --- /dev/null +++ b/content/2022-07-07-llvm-orc-jit/ccompiler.h @@ -0,0 +1,109 @@ +#ifndef CCOMPILER_H +#define CCOMPILER_H + +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace cc { + +using clang::CompilerInstance; +using clang::CompilerInvocation; +using clang::DiagnosticConsumer; +using clang::DiagnosticOptions; +using clang::DiagnosticsEngine; +using clang::EmitLLVMOnlyAction; +using clang::TextDiagnosticPrinter; + +using llvm::cantFail; +using llvm::Expected; +using llvm::IntrusiveRefCntPtr; +using llvm::LLVMContext; +using llvm::MemoryBuffer; +using llvm::Module; +using llvm::StringError; + +class CCompiler { +public: + CCompiler() { + // Setup custom diagnostic options. + auto DO = IntrusiveRefCntPtr(new DiagnosticOptions()); + DO->ShowColors = 1; + + // Setup stderr custom diagnostic consumer. + DC = std::make_unique(llvm::errs(), DO.get()); + + // Create custom diagnostics engine. + // The engine will NOT take ownership of the DiagnosticConsumer object. + DE = std::make_unique( + nullptr /* DiagnosticIDs */, std::move(DO), DC.get(), + false /* own DiagnosticConsumer */); + } + + struct CompileResult { + std::unique_ptr C; + std::unique_ptr M; + }; + + Expected compile(const char *code) const { + using std::errc; + const auto err = [](errc ec) { return std::make_error_code(ec); }; + + const char code_fname[] = "jit.c"; + + // Create compiler instance. + CompilerInstance CC; + + // Setup compiler invocation. + bool ok = CompilerInvocation::CreateFromArgs(CC.getInvocation(), + {code_fname}, *DE); + // We control the arguments, so we assert. + assert(ok); + + // Setup custom diagnostic printer. + CC.createDiagnostics(DC.get(), false /* own DiagnosticConsumer */); + + // Configure remapping from pseudo file name to in-memory code buffer + // code_fname -> code_buffer. + // + // PreprocessorOptions take ownership of MemoryBuffer. + CC.getPreprocessorOpts().addRemappedFile( + code_fname, MemoryBuffer::getMemBuffer(code).release()); + + // Configure codegen options. + auto &CG = CC.getCodeGenOpts(); + CG.OptimizationLevel = 3; + CG.setInlining(clang::CodeGenOptions::NormalInlining); + + // Generate LLVM IR. + EmitLLVMOnlyAction A; + if (!CC.ExecuteAction(A)) { + return llvm::make_error( + "Failed to generate LLVM IR from C code!", + err(errc::invalid_argument)); + } + + // Take generated LLVM IR module and the LLVMContext. + auto M = A.takeModule(); + auto C = std::unique_ptr(A.takeLLVMContext()); + + // TODO: Can this become nullptr when the action succeeds? + assert(M); + + return CompileResult{std::move(C), std::move(M)}; + } + +private: + std::unique_ptr DC; + std::unique_ptr DE; +}; + +} // namespace cc + +#endif diff --git a/content/2022-07-07-llvm-orc-jit/jit.h b/content/2022-07-07-llvm-orc-jit/jit.h new file mode 100644 index 0000000..10d6d4a --- /dev/null +++ b/content/2022-07-07-llvm-orc-jit/jit.h @@ -0,0 +1,100 @@ +#ifndef JIT_H +#define JIT_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace jit { + +using llvm::DataLayout; +using llvm::Expected; +using llvm::JITEvaluatedSymbol; +using llvm::SectionMemoryManager; +using llvm::StringRef; + +using llvm::orc::ConcurrentIRCompiler; +// using llvm::orc::DynamicLibrarySearchGenerator; +using llvm::orc::ExecutionSession; +using llvm::orc::IRCompileLayer; +using llvm::orc::JITDylib; +using llvm::orc::JITTargetMachineBuilder; +using llvm::orc::MangleAndInterner; +using llvm::orc::ResourceTrackerSP; +using llvm::orc::RTDyldObjectLinkingLayer; +using llvm::orc::SelfExecutorProcessControl; +using llvm::orc::ThreadSafeModule; + +// Simple JIT engine based on the KaleidoscopeJIT. +// https://www.llvm.org/docs/tutorial/BuildingAJIT1.html +class Jit { +private: + std::unique_ptr ES; + + DataLayout DL; + MangleAndInterner Mangle; + + RTDyldObjectLinkingLayer ObjectLayer; + IRCompileLayer CompileLayer; + + JITDylib &JD; + +public: + Jit(std::unique_ptr ES, JITTargetMachineBuilder JTMB, + DataLayout DL) + : ES(std::move(ES)), DL(std::move(DL)), Mangle(*this->ES, this->DL), + ObjectLayer(*this->ES, + []() { return std::make_unique(); }), + CompileLayer(*this->ES, ObjectLayer, + std::make_unique(std::move(JTMB))), + JD(this->ES->createBareJITDylib("main")) { + // https://www.llvm.org/docs/ORCv2.html#how-to-add-process-and-library-symbols-to-jitdylibs + // JD.addGenerator( + // cantFail(DynamicLibrarySearchGenerator::GetForCurrentProcess( + // DL.getGlobalPrefix()))); + (void)JD.define(llvm::orc::absoluteSymbols( + {{Mangle("libc_puts"), llvm::JITEvaluatedSymbol::fromPointer(&puts)}})); + } + + ~Jit() { + if (auto Err = ES->endSession()) + ES->reportError(std::move(Err)); + } + + static std::unique_ptr Create() { + auto EPC = cantFail(SelfExecutorProcessControl::Create()); + auto ES = std::make_unique(std::move(EPC)); + + JITTargetMachineBuilder JTMB( + ES->getExecutorProcessControl().getTargetTriple()); + + auto DL = cantFail(JTMB.getDefaultDataLayoutForTarget()); + + return std::make_unique(std::move(ES), std::move(JTMB), std::move(DL)); + } + + // Error addModule(ThreadSafeModule TSM) { + Expected addModule(ThreadSafeModule TSM) { + auto RT = JD.createResourceTracker(); + if (auto E = CompileLayer.add(RT, std::move(TSM))) { + return E; + } + return RT; + } + + Expected lookup(StringRef Name) { + return ES->lookup({&JD}, Mangle(Name.str())); + } +}; + +} // namespace jit + +#endif diff --git a/content/2022-07-07-llvm-orc-jit/main.cc b/content/2022-07-07-llvm-orc-jit/main.cc new file mode 100644 index 0000000..1631990 --- /dev/null +++ b/content/2022-07-07-llvm-orc-jit/main.cc @@ -0,0 +1,55 @@ +#include "ccompiler.h" +#include "jit.h" + +int main() { + const char code[] = "extern void libc_puts(const char*);" + "struct S { int a; int b; };" + "static void init_a(struct S* s) { s->a = 1111; }" + "static void init_b(struct S* s) { s->b = 2222; }" + "void init(struct S* s) {" + "init_a(s); init_b(s);" + "libc_puts(\"libc_puts()\"); }"; + + auto R = cc::CCompiler().compile(code); + // Abort if compilation failed. + auto [C, M] = cantFail(std::move(R)); + // M->print(llvm::errs(), nullptr); + + // -- JIT compiler the IR module. + + llvm::InitializeNativeTarget(); + llvm::InitializeNativeTargetAsmPrinter(); + + auto JIT = jit::Jit::Create(); + auto TSM = llvm::orc::ThreadSafeModule(std::move(M), std::move(C)); + + auto RT = JIT->addModule(std::move(TSM)); + if (auto E = RT.takeError()) { + llvm::errs() << llvm::toString(std::move(E)) << '\n'; + return 1; + } + + if (auto ADDR = JIT->lookup("init")) { + std::printf("JIT ADDR 0x%lx\n", (*ADDR).getAddress()); + + struct S { + int a, b; + } state = {0, 0}; + auto JIT_FN = (void (*)(struct S *))(*ADDR).getAddress(); + + std::printf("S { a=%d b=%d }\n", state.a, state.b); + JIT_FN(&state); + std::printf("S { a=%d b=%d }\n", state.a, state.b); + } + + // Remove jitted code tracked by this RT. + (void)(*RT)->remove(); + + if (auto E = JIT->lookup("init").takeError()) { + llvm::errs() << "Expected error, we dropped removed code tracked by RT and " + "hence 'init' should be " + "removed from the JIT!\n"; + } + + return 0; +} -- cgit v1.2.3