aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorJohannes Stoelp <johannes.stoelp@gmail.com>2022-06-18 19:41:38 +0200
committerJohannes Stoelp <johannes.stoelp@gmail.com>2022-06-18 19:41:38 +0200
commit394366cf1c32aae428e588b262a9dc365d897a70 (patch)
treec73a1e6b673ab84ec0519361999efdd5e8393b90
parentdf6dfea1ac8b9040afeb7acf3607a355a898fa6b (diff)
downloadblog-394366cf1c32aae428e588b262a9dc365d897a70.tar.gz
blog-394366cf1c32aae428e588b262a9dc365d897a70.zip
clang: add post about in memory c -> llvm ir
-rw-r--r--content/2022-06-18-libclang-c-to-llvm-ir.md32
-rw-r--r--content/2022-06-18-libclang-c-to-llvm-ir/.clang-format104
-rw-r--r--content/2022-06-18-libclang-c-to-llvm-ir/Makefile27
-rw-r--r--content/2022-06-18-libclang-c-to-llvm-ir/gen-ir.cc97
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;
+}