diff options
-rw-r--r-- | content/2022-06-18-libclang-c-to-llvm-ir.md | 32 | ||||
-rw-r--r-- | content/2022-06-18-libclang-c-to-llvm-ir/.clang-format | 104 | ||||
-rw-r--r-- | content/2022-06-18-libclang-c-to-llvm-ir/Makefile | 27 | ||||
-rw-r--r-- | content/2022-06-18-libclang-c-to-llvm-ir/gen-ir.cc | 97 |
4 files changed, 260 insertions, 0 deletions
diff --git a/content/2022-06-18-libclang-c-to-llvm-ir.md b/content/2022-06-18-libclang-c-to-llvm-ir.md new file mode 100644 index 0000000..7d3ee63 --- /dev/null +++ b/content/2022-06-18-libclang-c-to-llvm-ir.md @@ -0,0 +1,32 @@ ++++ +title = "C to LLVM IR in memory using libclang" + +[taxonomies] +tags = ["llvm", "clang", "c++"] ++++ + +For some experiments with the LLVM just in time (JIT) APIs, I was looking for a +way to compile in memory from `C -> LLVM IR` and without invoking Clang as a +child process. + +I created a minimal example for my purpose based on the [Clang +source][src-clang] code and the example given in the blog post [Compiling C++ +Code In Memory With Clang][blog-clang-in-memory]. + +The code listing below shows the example with detailed comments inlined, hence +I am not further describing any details here. + +> The example was build & tested with LLVM & Clang 13. + +```cpp +{{ include(path="content/2022-06-18-libclang-c-to-llvm-ir/gen-ir.cc") }} +``` + +The following Makefile can be used to compile and run the example. + +```make +{{ include(path="content/2022-06-18-libclang-c-to-llvm-ir/Makefile") }} +``` + +[src-clang]: https://github.com/llvm/llvm-project/tree/main/clang +[blog-clang-in-memory]: https://blog.audio-tk.com/2018/09/18/compiling-c-code-in-memory-with-clang/ diff --git a/content/2022-06-18-libclang-c-to-llvm-ir/.clang-format b/content/2022-06-18-libclang-c-to-llvm-ir/.clang-format new file mode 100644 index 0000000..9a0c292 --- /dev/null +++ b/content/2022-06-18-libclang-c-to-llvm-ir/.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-06-18-libclang-c-to-llvm-ir/Makefile b/content/2022-06-18-libclang-c-to-llvm-ir/Makefile new file mode 100644 index 0000000..ec36db3 --- /dev/null +++ b/content/2022-06-18-libclang-c-to-llvm-ir/Makefile @@ -0,0 +1,27 @@ +LIBS = $(shell llvm-config --libs) +LIBS += -lclang-cpp + +CXXFLAGS = -Wall +CXXFLAGS += -Wextra +CXXFLAGS += -Werror +CXXFLAGS += -O3 + +SAN ?= 1 +ifeq ($(SAN),1) +FLAGS = -fsanitize=address -fsanitize=leak -fsanitize=undefined +endif + +run: gen-ir + ./$^ + +gen-ir: gen-ir.o + $(CXX) -o $@ $^ $(LIBS) $(FLAGS) + +%.o: %.cc + $(CXX) -o $@ -c $^ $(CXXFLAGS) $(FLAGS) + +fmt: + clang-format -i *.cc + +clean: + $(RM) gen-ir *.o diff --git a/content/2022-06-18-libclang-c-to-llvm-ir/gen-ir.cc b/content/2022-06-18-libclang-c-to-llvm-ir/gen-ir.cc new file mode 100644 index 0000000..a2055e4 --- /dev/null +++ b/content/2022-06-18-libclang-c-to-llvm-ir/gen-ir.cc @@ -0,0 +1,97 @@ +#include <clang/Basic/DiagnosticOptions.h> +#include <clang/CodeGen/CodeGenAction.h> +#include <clang/Frontend/CompilerInstance.h> +#include <clang/Frontend/TextDiagnosticPrinter.h> +#include <clang/Lex/PreprocessorOptions.h> + +#include <llvm/IR/Module.h> +#include <llvm/Support/Host.h> +#include <llvm/Support/TargetSelect.h> + +using clang::CompilerInstance; +using clang::CompilerInvocation; +using clang::DiagnosticConsumer; +using clang::DiagnosticOptions; +using clang::DiagnosticsEngine; +using clang::EmitLLVMOnlyAction; +using clang::TextDiagnosticPrinter; + +using llvm::ArrayRef; +using llvm::IntrusiveRefCntPtr; +using llvm::MemoryBuffer; + +int main() { + const char* code_fname = "jit.c"; + const char* code_input = + "struct S { int a; int b; };\n" + "void init(struct S* s) { s->a = 42; s->b = 1337; }\n"; + + // Setup custom diagnostic options. + IntrusiveRefCntPtr<DiagnosticOptions> diag_opts(new DiagnosticOptions()); + diag_opts->ShowColors = 1; + + // Setup custom diagnostic consumer. + // + // We configure the consumer with our custom diagnostic options and set it + // up that diagnostic messages are printed to stderr. + std::unique_ptr<DiagnosticConsumer> diag_print = + std::make_unique<TextDiagnosticPrinter>(llvm::errs(), diag_opts.get()); + + // Create custom diagnostics engine. + // + // The engine will NOT take ownership of the DiagnosticConsumer object. + auto diag_eng = std::make_unique<DiagnosticsEngine>( + nullptr /* DiagnosticIDs */, diag_opts, diag_print.get(), + false /* own DiagnosticConsumer */); + + + // Create compiler instance. + CompilerInstance cc; + + // Setup compiler invocation. + // + // We are only passing a single argument, which is the pseudo file name for + // our code `code_fname`. We will be remapping this pseudo file name to an + // in-memory buffer via the preprocessor options below. + // + // The CompilerInvocation is a helper class which holds the data describing + // a compiler invocation (eg include paths, code generation options, + // warning flags, ..). + if (!CompilerInvocation::CreateFromArgs(cc.getInvocation(), + ArrayRef<const char*>({code_fname}), + *diag_eng)) { + std::puts("Failed to create CompilerInvocation!"); + return 1; + } + + // Setup a TextDiagnosticPrinter printer with our diagnostic options to + // handle diagnostic messaged. + // + // The compiler will NOT take ownership of the DiagnosticConsumer object. + cc.createDiagnostics(diag_print.get(), false /* own DiagnosticConsumer */); + + // Create in-memory readonly buffer with pointing to our C code. + std::unique_ptr<MemoryBuffer> code_buffer = + MemoryBuffer::getMemBuffer(code_input); + // Configure remapping from pseudo file name to in-memory code buffer + // code_fname -> code_buffer. + // + // Ownership of the MemoryBuffer object is moved, except we would set + // `RetainRemappedFileBuffers = 1` in the PreprocessorOptions. + cc.getPreprocessorOpts().addRemappedFile(code_fname, code_buffer.release()); + + // Create action to generate LLVM IR. + EmitLLVMOnlyAction action; + // Run action against our compiler instance. + if (!cc.ExecuteAction(action)) { + std::puts("Failed to run EmitLLVMOnlyAction!"); + return 1; + } + + // Take generated LLVM IR module and print to stdout. + if (auto mod = action.takeModule()) { + mod->print(llvm::outs(), nullptr); + } + + return 0; +} |