Skip to content

Commit 54397f9

Browse files
[llvm-c] Expose debug support for LLJIT in Orc C-API bindings (#73257)
Allow C-API users to debug their JITed code via the GDB JIT Interface. This is currently supported on ELF and MachO based platforms. On other systems `LLVMOrcLLJITEnableDebugSupport()` returns an error. This patch adds a new C-API header `LLJITUtils.h`, which can host further advanced JIT features in the future. Using the header requires linking against LLVMOrcDebugging.
1 parent 07ed325 commit 54397f9

File tree

7 files changed

+163
-2
lines changed

7 files changed

+163
-2
lines changed

llvm/include/llvm-c/LLJIT.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/*===----------- llvm-c/LLJIT.h - OrcV2 LLJIT C bindings --------*- C++ -*-===*\
1+
/*===----------- llvm-c/LLJIT.h - OrcV2 LLJIT C bindings ----------*- C -*-===*\
22
|* *|
33
|* Part of the LLVM Project, under the Apache License v2.0 with LLVM *|
44
|* Exceptions. *|

llvm/include/llvm-c/LLJITUtils.h

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/*===------- llvm-c/LLJITUtils.h - Advanced LLJIT features --------*- C -*-===*\
2+
|* *|
3+
|* Part of the LLVM Project, under the Apache License v2.0 with LLVM *|
4+
|* Exceptions. *|
5+
|* See https://llvm.org/LICENSE.txt for license information. *|
6+
|* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception *|
7+
|* *|
8+
|*===----------------------------------------------------------------------===*|
9+
|* *|
10+
|* This header declares the C interface for extra utilities to be used with *|
11+
|* the LLJIT class from the llvm-c/LLJIT.h header. It requires to following *|
12+
|* link libraries in addition to libLLVMOrcJIT.a: *|
13+
|* - libLLVMOrcDebugging.a *|
14+
|* *|
15+
|* Many exotic languages can interoperate with C code but have a harder time *|
16+
|* with C++ due to name mangling. So in addition to C, this interface enables *|
17+
|* tools written in such languages. *|
18+
|* *|
19+
|* Note: This interface is experimental. It is *NOT* stable, and may be *|
20+
|* changed without warning. Only C API usage documentation is *|
21+
|* provided. See the C++ documentation for all higher level ORC API *|
22+
|* details. *|
23+
|* *|
24+
\*===----------------------------------------------------------------------===*/
25+
26+
#ifndef LLVM_C_LLJITUTILS_H
27+
#define LLVM_C_LLJITUTILS_H
28+
29+
#include "llvm-c/LLJIT.h"
30+
31+
LLVM_C_EXTERN_C_BEGIN
32+
33+
/**
34+
* @defgroup LLVMCExecutionEngineLLJITUtils LLJIT Utilities
35+
* @ingroup LLVMCExecutionEngineLLJIT
36+
*
37+
* @{
38+
*/
39+
40+
/**
41+
* Install the plugin that submits debug objects to the executor. Executors must
42+
* expose the llvm_orc_registerJITLoaderGDBWrapper symbol.
43+
*/
44+
LLVMErrorRef LLVMOrcLLJITEnableDebugSupport(LLVMOrcLLJITRef J);
45+
46+
/**
47+
* @}
48+
*/
49+
50+
LLVM_C_EXTERN_C_END
51+
52+
#endif /* LLVM_C_LLJITUTILS_H */

llvm/lib/ExecutionEngine/Orc/Debugging/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ add_llvm_component_library(LLVMOrcDebugging
66
DebugInfoSupport.cpp
77
DebuggerSupport.cpp
88
DebuggerSupportPlugin.cpp
9+
LLJITUtilsCBindings.cpp
910
PerfSupportPlugin.cpp
1011

1112
ADDITIONAL_HEADER_DIRS

llvm/lib/ExecutionEngine/Orc/Debugging/DebuggerSupport.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ Error enableDebuggerSupport(LLJIT &J) {
3939
if (!Registrar)
4040
return Registrar.takeError();
4141
ObjLinkingLayer->addPlugin(std::make_unique<DebugObjectManagerPlugin>(
42-
ES, std::move(*Registrar), true, true));
42+
ES, std::move(*Registrar), false, true));
4343
return Error::success();
4444
}
4545
case Triple::MachO: {
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
//===--------- LLJITUtilsCBindings.cpp - Advanced LLJIT features ----------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "llvm-c/LLJIT.h"
10+
#include "llvm-c/LLJITUtils.h"
11+
12+
#include "llvm/ExecutionEngine/Orc/Debugging/DebuggerSupport.h"
13+
#include "llvm/ExecutionEngine/Orc/LLJIT.h"
14+
15+
using namespace llvm;
16+
using namespace llvm::orc;
17+
18+
DEFINE_SIMPLE_CONVERSION_FUNCTIONS(LLJIT, LLVMOrcLLJITRef)
19+
20+
LLVMErrorRef LLVMOrcLLJITEnableDebugSupport(LLVMOrcLLJITRef J) {
21+
return wrap(llvm::orc::enableDebuggerSupport(*unwrap(J)));
22+
}

llvm/unittests/ExecutionEngine/Orc/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ set(LLVM_LINK_COMPONENTS
55
IRReader
66
JITLink
77
Object
8+
OrcDebugging
89
OrcJIT
910
OrcShared
1011
OrcTargetProcess

llvm/unittests/ExecutionEngine/Orc/OrcCAPITest.cpp

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,12 @@
99
#include "llvm-c/Core.h"
1010
#include "llvm-c/Error.h"
1111
#include "llvm-c/LLJIT.h"
12+
#include "llvm-c/LLJITUtils.h"
1213
#include "llvm-c/Orc.h"
1314
#include "gtest/gtest.h"
1415

1516
#include "llvm/ExecutionEngine/Orc/CompileUtils.h"
17+
#include "llvm/ExecutionEngine/Orc/TargetProcess/JITLoaderGDB.h"
1618
#include "llvm/IR/LLVMContext.h"
1719
#include "llvm/IR/Module.h"
1820
#include "llvm/IRReader/IRReader.h"
@@ -211,6 +213,20 @@ constexpr StringRef SumExample =
211213
}
212214
)";
213215

216+
constexpr StringRef SumDebugExample =
217+
R"(
218+
define i32 @sum(i32 %x, i32 %y) {
219+
entry:
220+
%r = add nsw i32 %x, %y
221+
ret i32 %r
222+
}
223+
!llvm.module.flags = !{!0}
224+
!llvm.dbg.cu = !{!1}
225+
!0 = !{i32 2, !"Debug Info Version", i32 3}
226+
!1 = distinct !DICompileUnit(language: DW_LANG_C99, file: !2, emissionKind: FullDebug)
227+
!2 = !DIFile(filename: "sum.c", directory: "/tmp")
228+
)";
229+
214230
} // end anonymous namespace.
215231

216232
// Consumes the given error ref and returns the string error message.
@@ -494,6 +510,75 @@ TEST_F(OrcCAPITestBase, AddObjectBuffer) {
494510
ASSERT_TRUE(!!SumAddr);
495511
}
496512

513+
// This must be kept in sync with gdb/gdb/jit.h .
514+
extern "C" {
515+
516+
typedef enum {
517+
JIT_NOACTION = 0,
518+
JIT_REGISTER_FN,
519+
JIT_UNREGISTER_FN
520+
} jit_actions_t;
521+
522+
struct jit_code_entry {
523+
struct jit_code_entry *next_entry;
524+
struct jit_code_entry *prev_entry;
525+
const char *symfile_addr;
526+
uint64_t symfile_size;
527+
};
528+
529+
struct jit_descriptor {
530+
uint32_t version;
531+
// This should be jit_actions_t, but we want to be specific about the
532+
// bit-width.
533+
uint32_t action_flag;
534+
struct jit_code_entry *relevant_entry;
535+
struct jit_code_entry *first_entry;
536+
};
537+
538+
// We put information about the JITed function in this global, which the
539+
// debugger reads. Make sure to specify the version statically, because the
540+
// debugger checks the version before we can set it during runtime.
541+
extern struct jit_descriptor __jit_debug_descriptor;
542+
543+
static void *findLastDebugDescriptorEntryPtr() {
544+
struct jit_code_entry *Last = __jit_debug_descriptor.first_entry;
545+
while (Last && Last->next_entry)
546+
Last = Last->next_entry;
547+
return Last;
548+
}
549+
}
550+
551+
#if defined(_AIX) or not(defined(__ELF__) or defined(__MACH__))
552+
TEST_F(OrcCAPITestBase, DISABLED_EnableDebugSupport) {
553+
#else
554+
static LLVM_ATTRIBUTE_USED void linkComponents() {
555+
errs() << "Linking in runtime functions\n"
556+
<< (void *)&llvm_orc_registerJITLoaderGDBWrapper << '\n'
557+
<< (void *)&llvm_orc_registerJITLoaderGDBAllocAction << '\n';
558+
}
559+
TEST_F(OrcCAPITestBase, EnableDebugSupport) {
560+
#endif
561+
if (LLVMErrorRef E = LLVMOrcLLJITEnableDebugSupport(Jit))
562+
FAIL() << "Error testing LLJIT debug support (triple = " << TargetTriple
563+
<< "): " << toString(E);
564+
565+
void *Before = findLastDebugDescriptorEntryPtr();
566+
LLVMMemoryBufferRef ObjBuffer = createTestObject(SumDebugExample, "sum.ll");
567+
LLVMOrcObjectLayerRef ObjLayer = LLVMOrcLLJITGetObjLinkingLayer(Jit);
568+
if (LLVMErrorRef E =
569+
LLVMOrcObjectLayerAddObjectFile(ObjLayer, MainDylib, ObjBuffer))
570+
FAIL() << "Failed to add object file to ObjLinkingLayer (triple = "
571+
<< TargetTriple << "): " << toString(E);
572+
573+
LLVMOrcJITTargetAddress SumAddr;
574+
if (LLVMErrorRef E = LLVMOrcLLJITLookup(Jit, &SumAddr, "sum"))
575+
FAIL() << "Symbol \"sum\" was not added into JIT (triple = " << TargetTriple
576+
<< "): " << toString(E);
577+
578+
void *After = findLastDebugDescriptorEntryPtr();
579+
ASSERT_NE(Before, After);
580+
}
581+
497582
#if defined(_AIX)
498583
TEST_F(OrcCAPITestBase, DISABLED_ExecutionTest) {
499584
#else

0 commit comments

Comments
 (0)