diff --git a/tools/perf/tests/bpf-script-example.c b/tools/perf/tests/bpf-script-example.c
index 268e5f8..265036e 100644
--- a/tools/perf/tests/bpf-script-example.c
+++ b/tools/perf/tests/bpf-script-example.c
@@ -46,3 +46,11 @@ int bpf_func__SyS_epoll_wait(void *ctx)
 }
 char _license[] SEC("license") = "GPL";
 int _version SEC("version") = LINUX_VERSION_CODE;
+
+#ifdef TEST_PERF_HOOK
+SEC("perfhook:test")
+void hook_test(void)
+{
+	return;
+}
+#endif
diff --git a/tools/perf/tests/clang.c b/tools/perf/tests/clang.c
index 2964c06..f274e62 100644
--- a/tools/perf/tests/clang.c
+++ b/tools/perf/tests/clang.c
@@ -16,6 +16,10 @@ static struct {
 		.func = test__clang_to_obj,
 		.desc = "Test builtin clang compile C source to ELF object",
 	},
+	{
+		.func = test__clang_jit,
+		.desc = "Test builtin clang compile mixed BPF and native code",
+	},
 #endif
 };
 
diff --git a/tools/perf/util/c++/clang-c.h b/tools/perf/util/c++/clang-c.h
index 0eadd79..5ebcb41 100644
--- a/tools/perf/util/c++/clang-c.h
+++ b/tools/perf/util/c++/clang-c.h
@@ -14,6 +14,7 @@ extern void perf_clang__cleanup(void);
 
 extern int test__clang_to_IR(void);
 extern int test__clang_to_obj(void);
+extern int test__clang_jit(void);
 
 extern int perf_clang__compile_bpf(const char *filename,
 				   void **p_obj_buf,
@@ -26,6 +27,7 @@ static inline void perf_clang__cleanup(void) { }
 
 static inline int test__clang_to_IR(void) { return -1; }
 static inline int test__clang_to_obj(void) { return -1;}
+static inline int test__clang_jit(void) { return -1;}
 
 static inline int
 perf_clang__compile_bpf(const char *filename __maybe_unused,
diff --git a/tools/perf/util/c++/clang-test.cpp b/tools/perf/util/c++/clang-test.cpp
index fb05e56..2b4aa8d 100644
--- a/tools/perf/util/c++/clang-test.cpp
+++ b/tools/perf/util/c++/clang-test.cpp
@@ -5,6 +5,7 @@
 
 #include <util-cxx.h>
 #include <tests/llvm.h>
+#include <perf-hooks.h>
 #include <string>
 
 class perf_clang_scope {
@@ -14,7 +15,7 @@ public:
 };
 
 static std::unique_ptr<perf::PerfModule>
-__test__clang_to_IR(void)
+__test__clang_to_IR(bool perfhook)
 {
 	unsigned int kernel_version;
 
@@ -23,14 +24,22 @@ __test__clang_to_IR(void)
 
 	std::string cflag_kver("-DLINUX_VERSION_CODE=" +
 				std::to_string(kernel_version));
+	std::string cflag_perfhook(perfhook ? "-DTEST_PERF_HOOK=1" : "");
 
 	std::unique_ptr<perf::PerfModule> M =
-		perf::getModuleFromSource({cflag_kver.c_str()},
+		perf::getModuleFromSource({cflag_kver.c_str(),
+					   cflag_perfhook.c_str()},
 					  "perf-test.c",
 					  test_llvm__bpf_base_prog);
 	return M;
 }
 
+static std::unique_ptr<perf::PerfModule>
+__test__clang_to_IR(void)
+{
+	return __test__clang_to_IR(false);
+}
+
 extern "C" {
 int test__clang_to_IR(void)
 {
@@ -59,4 +68,23 @@ int test__clang_to_obj(void)
 	return 0;
 }
 
+int test__clang_jit(void)
+{
+	perf_clang_scope _scope;
+
+	auto M = __test__clang_to_IR(true);
+	if (!M)
+		return -1;
+
+	if (M->doJIT())
+		return -1;
+
+	std::unique_ptr<perf::PerfModule::HookMap> hooks(M->copyJITResult());
+	for (auto i : *hooks)
+		perf_hooks__set_hook(i.first.c_str(), i.second, NULL);
+
+	perf_hooks__invoke_test();
+	return 0;
+}
+
 }
diff --git a/tools/perf/util/c++/clang.cpp b/tools/perf/util/c++/clang.cpp
index 98d05e2..03012b2 100644
--- a/tools/perf/util/c++/clang.cpp
+++ b/tools/perf/util/c++/clang.cpp
@@ -14,9 +14,14 @@
 #include "clang/Frontend/TextDiagnosticPrinter.h"
 #include "clang/Tooling/Tooling.h"
 #include "llvm/ADT/SmallString.h"
+#include "llvm/ExecutionEngine/Orc/CompileUtils.h"
+#include "llvm/ExecutionEngine/Orc/IRCompileLayer.h"
+#include "llvm/ExecutionEngine/Orc/LambdaResolver.h"
+#include "llvm/ExecutionEngine/Orc/ObjectLinkingLayer.h"
 #include "llvm/IR/LegacyPassManager.h"
 #include "llvm/IR/Module.h"
 #include "llvm/Option/Option.h"
+#include "llvm/Support/DynamicLibrary.h"
 #include "llvm/Support/FileSystem.h"
 #include "llvm/Support/ManagedStatic.h"
 #include "llvm/Support/TargetRegistry.h"
@@ -24,11 +29,13 @@
 #include "llvm/Target/TargetMachine.h"
 #include "llvm/Target/TargetOptions.h"
 #include <memory>
+#include <vector>
 
 #include "clang.h"
 #include "clang-c.h"
 #include "llvm-utils.h"
 #include "util-cxx.h"
+#include "perf-hooks.h"
 
 namespace perf {
 
@@ -187,6 +194,66 @@ PerfModule::toBPFObject(void)
 	return std::move(Buffer);
 }
 
+/*
+ * Use a global memory manager so allocated code and data won't be released
+ * when object destroy.
+ */
+static llvm::SectionMemoryManager JITMemoryManager;
+
+int PerfModule::doJIT(void)
+{
+	using namespace orc;
+
+	prepareJIT();
+
+	std::unique_ptr<TargetMachine> TM(EngineBuilder().selectTarget());
+	if (!TM) {
+		llvm::errs() << "Can't get target machine\n";
+		return -1;
+	}
+	const DataLayout DL(TM->createDataLayout());
+	Module->setDataLayout(DL);
+	Module->setTargetTriple(TM->getTargetTriple().normalize());
+
+	ObjectLinkingLayer<> ObjectLayer;
+	IRCompileLayer<decltype(ObjectLayer)> CompileLayer(ObjectLayer, SimpleCompiler(*TM));
+
+	auto Resolver = createLambdaResolver(
+			[](const std::string &Name) {
+				return RuntimeDyld::SymbolInfo(nullptr);
+			},
+			[](const std::string &Name) {
+				return RuntimeDyld::SymbolInfo(nullptr);
+			});
+
+	std::vector<llvm::Module *> Ms;
+	Ms.push_back(getModule());
+	CompileLayer.addModuleSet(std::move(Ms),
+			&JITMemoryManager,
+			std::move(Resolver));
+
+
+	for (Function *F : JITFunctions) {
+		JITSymbol sym = CompileLayer.findSymbol(F->getName().str(), true);
+
+		/*
+		 * Type of F->getSection() is moving from
+		 * const char * to StringRef.
+		 * Convert it to std::string so we don't need
+		 * consider this API change.
+		 */
+		std::string sec(F->getSection());
+		std::string hook(&sec.c_str()[sizeof("perfhook:") - 1]);
+		perf_hook_func_t func = (perf_hook_func_t)(intptr_t)sym.getAddress();
+
+		if (JITResult[hook])
+			llvm::errs() << "Warning: multiple functions on hook "
+				     << hook << ", only one is used\n";
+		JITResult[hook] = func;
+	}
+	return 0;
+}
+
 class ClangOptions {
 	llvm::SmallString<PATH_MAX> FileName;
 	llvm::SmallString<64> KVerDef;
@@ -292,6 +359,10 @@ void perf_clang__init(void)
 	LLVMInitializeBPFTarget();
 	LLVMInitializeBPFTargetMC();
 	LLVMInitializeBPFAsmPrinter();
+
+	llvm::InitializeNativeTarget();
+	llvm::InitializeNativeTargetAsmPrinter();
+	llvm::InitializeNativeTargetAsmParser();
 }
 
 void perf_clang__cleanup(void)
diff --git a/tools/perf/util/c++/clang.h b/tools/perf/util/c++/clang.h
index 1eb71a6..df2eb8f 100644
--- a/tools/perf/util/c++/clang.h
+++ b/tools/perf/util/c++/clang.h
@@ -7,18 +7,26 @@
 #include "llvm/Option/Option.h"
 #include <memory>
 #include <set>
+#include <map>
+
+#include "util/perf-hooks.h"
 
 namespace perf {
 
 using namespace llvm;
 
 class PerfModule {
+public:
+	typedef std::map<std::string, perf_hook_func_t> HookMap;
 private:
 	std::unique_ptr<llvm::Module> Module;
 
 	std::set<llvm::GlobalVariable *> Maps;
 	std::set<llvm::Function *> BPFFunctions;
 	std::set<llvm::Function *> JITFunctions;
+
+	HookMap JITResult;
+
 	void prepareBPF(void);
 	void prepareJIT(void);
 public:
@@ -26,10 +34,15 @@ class PerfModule {
 	{
 		return Module.get();
 	}
+	inline HookMap *copyJITResult(void)
+	{
+		return new HookMap(JITResult);
+	}
 
 	PerfModule(std::unique_ptr<llvm::Module>&& M);
 
 	std::unique_ptr<llvm::SmallVectorImpl<char>> toBPFObject(void);
+	int doJIT(void);
 };
 
 std::unique_ptr<PerfModule>
