503 lines · cpp
1//===- MCJITTest.cpp - Unit tests for the MCJIT -----------------*- C++ -*-===//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-exception6//7//===----------------------------------------------------------------------===//8//9// This test suite verifies basic MCJIT functionality when invoked from the C10// API.11//12//===----------------------------------------------------------------------===//13 14#include "MCJITTestAPICommon.h"15#include "llvm-c/Analysis.h"16#include "llvm-c/Core.h"17#include "llvm-c/ExecutionEngine.h"18#include "llvm-c/Target.h"19#include "llvm-c/Transforms/PassBuilder.h"20#include "llvm/ExecutionEngine/SectionMemoryManager.h"21#include "llvm/Support/Debug.h"22#include "llvm/TargetParser/Host.h"23#include "gtest/gtest.h"24 25using namespace llvm;26 27static bool didCallAllocateCodeSection;28static bool didAllocateCompactUnwindSection;29static bool didCallYield;30 31static uint8_t *roundTripAllocateCodeSection(void *object, uintptr_t size,32 unsigned alignment,33 unsigned sectionID,34 const char *sectionName) {35 didCallAllocateCodeSection = true;36 return static_cast<SectionMemoryManager*>(object)->allocateCodeSection(37 size, alignment, sectionID, sectionName);38}39 40static uint8_t *roundTripAllocateDataSection(void *object, uintptr_t size,41 unsigned alignment,42 unsigned sectionID,43 const char *sectionName,44 LLVMBool isReadOnly) {45 if (!strcmp(sectionName, "__compact_unwind"))46 didAllocateCompactUnwindSection = true;47 return static_cast<SectionMemoryManager*>(object)->allocateDataSection(48 size, alignment, sectionID, sectionName, isReadOnly);49}50 51static LLVMBool roundTripFinalizeMemory(void *object, char **errMsg) {52 std::string errMsgString;53 bool result =54 static_cast<SectionMemoryManager*>(object)->finalizeMemory(&errMsgString);55 if (result) {56 *errMsg = LLVMCreateMessage(errMsgString.c_str());57 return 1;58 }59 return 0;60}61 62static void roundTripDestroy(void *object) {63 delete static_cast<SectionMemoryManager*>(object);64}65 66static void yield(LLVMContextRef, void *) {67 didCallYield = true;68}69 70namespace {71 72// memory manager to test reserve allocation space callback73class TestReserveAllocationSpaceMemoryManager: public SectionMemoryManager {74public:75 uintptr_t ReservedCodeSize;76 uintptr_t UsedCodeSize;77 uintptr_t ReservedDataSizeRO;78 uintptr_t UsedDataSizeRO;79 uintptr_t ReservedDataSizeRW;80 uintptr_t UsedDataSizeRW;81 82 TestReserveAllocationSpaceMemoryManager()83 : ReservedCodeSize(0), UsedCodeSize(0), ReservedDataSizeRO(0),84 UsedDataSizeRO(0), ReservedDataSizeRW(0), UsedDataSizeRW(0) {}85 86 bool needsToReserveAllocationSpace() override { return true; }87 88 void reserveAllocationSpace(uintptr_t CodeSize, Align CodeAlign,89 uintptr_t DataSizeRO, Align RODataAlign,90 uintptr_t DataSizeRW,91 Align RWDataAlign) override {92 ReservedCodeSize = CodeSize;93 ReservedDataSizeRO = DataSizeRO;94 ReservedDataSizeRW = DataSizeRW;95 }96 97 void useSpace(uintptr_t* UsedSize, uintptr_t Size, unsigned Alignment) {98 uintptr_t AlignedSize = (Size + Alignment - 1) / Alignment * Alignment;99 uintptr_t AlignedBegin = (*UsedSize + Alignment - 1) / Alignment * Alignment;100 *UsedSize = AlignedBegin + AlignedSize;101 }102 103 uint8_t *allocateDataSection(uintptr_t Size, unsigned Alignment,104 unsigned SectionID, StringRef SectionName,105 bool IsReadOnly) override {106 useSpace(IsReadOnly ? &UsedDataSizeRO : &UsedDataSizeRW, Size, Alignment);107 return SectionMemoryManager::allocateDataSection(Size, Alignment, SectionID,108 SectionName, IsReadOnly);109 }110 111 uint8_t *allocateCodeSection(uintptr_t Size, unsigned Alignment,112 unsigned SectionID,113 StringRef SectionName) override {114 useSpace(&UsedCodeSize, Size, Alignment);115 return SectionMemoryManager::allocateCodeSection(Size, Alignment, SectionID,116 SectionName);117 }118};119 120class MCJITCAPITest : public testing::Test, public MCJITTestAPICommon {121protected:122 MCJITCAPITest() {123 // The architectures below are known to be compatible with MCJIT as they124 // are copied from test/ExecutionEngine/MCJIT/lit.local.cfg and should be125 // kept in sync.126 SupportedArchs.push_back(Triple::aarch64);127 SupportedArchs.push_back(Triple::arm);128 SupportedArchs.push_back(Triple::mips);129 SupportedArchs.push_back(Triple::mips64);130 SupportedArchs.push_back(Triple::mips64el);131 SupportedArchs.push_back(Triple::x86);132 SupportedArchs.push_back(Triple::x86_64);133 134 // Some architectures have sub-architectures in which tests will fail, like135 // ARM. These two vectors will define if they do have sub-archs (to avoid136 // extra work for those who don't), and if so, if they are listed to work137 HasSubArchs.push_back(Triple::arm);138 SupportedSubArchs.push_back("armv6");139 SupportedSubArchs.push_back("armv7");140 141 // The operating systems below are known to be sufficiently incompatible142 // that they will fail the MCJIT C API tests.143 UnsupportedEnvironments.push_back(Triple::Cygnus);144 }145 146 void SetUp() override {147 didCallAllocateCodeSection = false;148 didAllocateCompactUnwindSection = false;149 didCallYield = false;150 Module = nullptr;151 Function = nullptr;152 Engine = nullptr;153 Error = nullptr;154 }155 156 void TearDown() override {157 if (Engine)158 LLVMDisposeExecutionEngine(Engine);159 else if (Module)160 LLVMDisposeModule(Module);161 }162 163 void buildSimpleFunction() {164 Module = LLVMModuleCreateWithName("simple_module");165 166 LLVMSetTarget(Module, HostTripleName.c_str());167 168 Function = LLVMAddFunction(Module, "simple_function",169 LLVMFunctionType(LLVMInt32Type(), nullptr,0, 0));170 LLVMSetFunctionCallConv(Function, LLVMCCallConv);171 172 LLVMBasicBlockRef entry = LLVMAppendBasicBlock(Function, "entry");173 LLVMBuilderRef builder = LLVMCreateBuilder();174 LLVMPositionBuilderAtEnd(builder, entry);175 LLVMBuildRet(builder, LLVMConstInt(LLVMInt32Type(), 42, 0));176 177 LLVMVerifyModule(Module, LLVMAbortProcessAction, &Error);178 LLVMDisposeMessage(Error);179 180 LLVMDisposeBuilder(builder);181 }182 183 void buildFunctionThatUsesStackmap() {184 Module = LLVMModuleCreateWithName("simple_module");185 186 LLVMSetTarget(Module, HostTripleName.c_str());187 188 LLVMTypeRef stackmapParamTypes[] = { LLVMInt64Type(), LLVMInt32Type() };189 LLVMTypeRef stackmapTy =190 LLVMFunctionType(LLVMVoidType(), stackmapParamTypes, 2, 1);191 LLVMValueRef stackmap = LLVMAddFunction(192 Module, "llvm.experimental.stackmap", stackmapTy);193 LLVMSetLinkage(stackmap, LLVMExternalLinkage);194 195 Function = LLVMAddFunction(Module, "simple_function",196 LLVMFunctionType(LLVMInt32Type(), nullptr, 0, 0));197 198 LLVMBasicBlockRef entry = LLVMAppendBasicBlock(Function, "entry");199 LLVMBuilderRef builder = LLVMCreateBuilder();200 LLVMPositionBuilderAtEnd(builder, entry);201 LLVMValueRef stackmapArgs[] = {202 LLVMConstInt(LLVMInt64Type(), 0, 0), LLVMConstInt(LLVMInt32Type(), 5, 0),203 LLVMConstInt(LLVMInt32Type(), 42, 0)204 };205 LLVMBuildCall2(builder, stackmapTy, stackmap, stackmapArgs, 3, "");206 LLVMBuildRet(builder, LLVMConstInt(LLVMInt32Type(), 42, 0));207 208 LLVMVerifyModule(Module, LLVMAbortProcessAction, &Error);209 LLVMDisposeMessage(Error);210 211 LLVMDisposeBuilder(builder);212 }213 214 void buildModuleWithCodeAndData() {215 Module = LLVMModuleCreateWithName("simple_module");216 217 LLVMSetTarget(Module, HostTripleName.c_str());218 219 // build a global int32 variable initialized to 42.220 LLVMValueRef GlobalVar = LLVMAddGlobal(Module, LLVMInt32Type(), "intVal");221 LLVMSetInitializer(GlobalVar, LLVMConstInt(LLVMInt32Type(), 42, 0));222 223 {224 Function = LLVMAddFunction(Module, "getGlobal",225 LLVMFunctionType(LLVMInt32Type(), nullptr, 0, 0));226 LLVMSetFunctionCallConv(Function, LLVMCCallConv);227 228 LLVMBasicBlockRef Entry = LLVMAppendBasicBlock(Function, "entry");229 LLVMBuilderRef Builder = LLVMCreateBuilder();230 LLVMPositionBuilderAtEnd(Builder, Entry);231 232 LLVMValueRef IntVal =233 LLVMBuildLoad2(Builder, LLVMInt32Type(), GlobalVar, "intVal");234 LLVMBuildRet(Builder, IntVal);235 236 LLVMVerifyModule(Module, LLVMAbortProcessAction, &Error);237 LLVMDisposeMessage(Error);238 239 LLVMDisposeBuilder(Builder);240 }241 242 {243 LLVMTypeRef ParamTypes[] = { LLVMInt32Type() };244 Function2 = LLVMAddFunction(245 Module, "setGlobal", LLVMFunctionType(LLVMVoidType(), ParamTypes, 1, 0));246 LLVMSetFunctionCallConv(Function2, LLVMCCallConv);247 248 LLVMBasicBlockRef Entry = LLVMAppendBasicBlock(Function2, "entry");249 LLVMBuilderRef Builder = LLVMCreateBuilder();250 LLVMPositionBuilderAtEnd(Builder, Entry);251 252 LLVMValueRef Arg = LLVMGetParam(Function2, 0);253 LLVMBuildStore(Builder, Arg, GlobalVar);254 LLVMBuildRetVoid(Builder);255 256 LLVMVerifyModule(Module, LLVMAbortProcessAction, &Error);257 LLVMDisposeMessage(Error);258 259 LLVMDisposeBuilder(Builder);260 }261 }262 263 void buildMCJITOptions() {264 LLVMInitializeMCJITCompilerOptions(&Options, sizeof(Options));265 Options.OptLevel = 2;266 267 // Just ensure that this field still exists.268 Options.NoFramePointerElim = false;269 }270 271 void useRoundTripSectionMemoryManager() {272 Options.MCJMM = LLVMCreateSimpleMCJITMemoryManager(273 new SectionMemoryManager(),274 roundTripAllocateCodeSection,275 roundTripAllocateDataSection,276 roundTripFinalizeMemory,277 roundTripDestroy);278 }279 280 void buildMCJITEngine() {281 ASSERT_EQ(282 0, LLVMCreateMCJITCompilerForModule(&Engine, Module, &Options,283 sizeof(Options), &Error));284 }285 286 void buildAndRunPasses() {287 LLVMPassBuilderOptionsRef Options = LLVMCreatePassBuilderOptions();288 if (LLVMErrorRef E =289 LLVMRunPasses(Module, "instcombine", nullptr, Options)) {290 char *Msg = LLVMGetErrorMessage(E);291 LLVMConsumeError(E);292 LLVMDisposePassBuilderOptions(Options);293 FAIL() << "Failed to run passes: " << Msg;294 }295 296 LLVMDisposePassBuilderOptions(Options);297 }298 299 void buildAndRunOptPasses() {300 LLVMPassBuilderOptionsRef Options = LLVMCreatePassBuilderOptions();301 if (LLVMErrorRef E =302 LLVMRunPasses(Module, "default<O2>", nullptr, Options)) {303 char *Msg = LLVMGetErrorMessage(E);304 LLVMConsumeError(E);305 LLVMDisposePassBuilderOptions(Options);306 FAIL() << "Failed to run passes: " << Msg;307 }308 309 LLVMDisposePassBuilderOptions(Options);310 }311 312 LLVMModuleRef Module;313 LLVMValueRef Function;314 LLVMValueRef Function2;315 LLVMMCJITCompilerOptions Options;316 LLVMExecutionEngineRef Engine;317 char *Error;318};319} // end anonymous namespace320 321TEST_F(MCJITCAPITest, simple_function) {322 SKIP_UNSUPPORTED_PLATFORM;323 324 buildSimpleFunction();325 buildMCJITOptions();326 buildMCJITEngine();327 buildAndRunPasses();328 329 auto *functionPointer = reinterpret_cast<int (*)()>(330 reinterpret_cast<uintptr_t>(LLVMGetPointerToGlobal(Engine, Function)));331 332 EXPECT_EQ(42, functionPointer());333}334 335TEST_F(MCJITCAPITest, gva) {336 SKIP_UNSUPPORTED_PLATFORM;337 338 Module = LLVMModuleCreateWithName("simple_module");339 LLVMSetTarget(Module, HostTripleName.c_str());340 LLVMValueRef GlobalVar = LLVMAddGlobal(Module, LLVMInt32Type(), "simple_value");341 LLVMSetInitializer(GlobalVar, LLVMConstInt(LLVMInt32Type(), 42, 0));342 343 buildMCJITOptions();344 buildMCJITEngine();345 buildAndRunPasses();346 347 uint64_t raw = LLVMGetGlobalValueAddress(Engine, "simple_value");348 int32_t *usable = (int32_t *) raw;349 350 EXPECT_EQ(42, *usable);351}352 353TEST_F(MCJITCAPITest, gfa) {354 SKIP_UNSUPPORTED_PLATFORM;355 356 buildSimpleFunction();357 buildMCJITOptions();358 buildMCJITEngine();359 buildAndRunPasses();360 361 uint64_t raw = LLVMGetFunctionAddress(Engine, "simple_function");362 int (*usable)() = (int (*)()) raw;363 364 EXPECT_EQ(42, usable());365}366 367TEST_F(MCJITCAPITest, custom_memory_manager) {368 SKIP_UNSUPPORTED_PLATFORM;369 370 buildSimpleFunction();371 buildMCJITOptions();372 useRoundTripSectionMemoryManager();373 buildMCJITEngine();374 buildAndRunPasses();375 376 auto *functionPointer = reinterpret_cast<int (*)()>(377 reinterpret_cast<uintptr_t>(LLVMGetPointerToGlobal(Engine, Function)));378 379 EXPECT_EQ(42, functionPointer());380 EXPECT_TRUE(didCallAllocateCodeSection);381}382 383TEST_F(MCJITCAPITest, stackmap_creates_compact_unwind_on_darwin) {384 SKIP_UNSUPPORTED_PLATFORM;385 386 // This test is also not supported on non-x86 platforms.387 if (Triple(HostTriple).getArch() != Triple::x86_64)388 GTEST_SKIP();389 390 buildFunctionThatUsesStackmap();391 buildMCJITOptions();392 useRoundTripSectionMemoryManager();393 buildMCJITEngine();394 buildAndRunOptPasses();395 396 auto *functionPointer = reinterpret_cast<int (*)()>(397 reinterpret_cast<uintptr_t>(LLVMGetPointerToGlobal(Engine, Function)));398 399 EXPECT_EQ(42, functionPointer());400 EXPECT_TRUE(didCallAllocateCodeSection);401 402 // Up to this point, the test is specific only to X86-64. But this next403 // expectation is only valid on Darwin because it assumes that unwind404 // data is made available only through compact_unwind. It would be405 // worthwhile to extend this to handle non-Darwin platforms, in which406 // case you'd want to look for an eh_frame or something.407 //408 // FIXME: Currently, MCJIT relies on a configure-time check to determine which409 // sections to emit. The JIT client should have runtime control over this.410 EXPECT_TRUE(411 Triple(HostTriple).getOS() != Triple::Darwin ||412 Triple(HostTriple).isMacOSXVersionLT(10, 7) ||413 didAllocateCompactUnwindSection);414}415 416#if defined(__APPLE__) && defined(__aarch64__)417// FIXME: Figure out why this fails on mac/arm, PR46647418#define MAYBE_reserve_allocation_space DISABLED_reserve_allocation_space419#else420#define MAYBE_reserve_allocation_space reserve_allocation_space421#endif422TEST_F(MCJITCAPITest, MAYBE_reserve_allocation_space) {423 SKIP_UNSUPPORTED_PLATFORM;424 425 TestReserveAllocationSpaceMemoryManager* MM = new TestReserveAllocationSpaceMemoryManager();426 427 buildModuleWithCodeAndData();428 buildMCJITOptions();429 Options.MCJMM = wrap(MM);430 buildMCJITEngine();431 buildAndRunPasses();432 433 auto GetGlobalFct = reinterpret_cast<int (*)()>(434 reinterpret_cast<uintptr_t>(LLVMGetPointerToGlobal(Engine, Function)));435 436 auto SetGlobalFct = reinterpret_cast<void (*)(int)>(437 reinterpret_cast<uintptr_t>(LLVMGetPointerToGlobal(Engine, Function2)));438 439 SetGlobalFct(789);440 EXPECT_EQ(789, GetGlobalFct());441 EXPECT_LE(MM->UsedCodeSize, MM->ReservedCodeSize);442 EXPECT_LE(MM->UsedDataSizeRO, MM->ReservedDataSizeRO);443 EXPECT_LE(MM->UsedDataSizeRW, MM->ReservedDataSizeRW);444 EXPECT_TRUE(MM->UsedCodeSize > 0);445 EXPECT_TRUE(MM->UsedDataSizeRW > 0);446}447 448TEST_F(MCJITCAPITest, yield) {449 SKIP_UNSUPPORTED_PLATFORM;450 451 buildSimpleFunction();452 buildMCJITOptions();453 buildMCJITEngine();454 LLVMContextRef C = LLVMGetGlobalContext();455 LLVMContextSetYieldCallback(C, yield, nullptr);456 buildAndRunPasses();457 458 auto *functionPointer = reinterpret_cast<int (*)()>(459 reinterpret_cast<uintptr_t>(LLVMGetPointerToGlobal(Engine, Function)));460 461 EXPECT_EQ(42, functionPointer());462 EXPECT_TRUE(didCallYield);463}464 465static int localTestFunc() {466 return 42;467}468 469TEST_F(MCJITCAPITest, addGlobalMapping) {470 SKIP_UNSUPPORTED_PLATFORM;471 472 Module = LLVMModuleCreateWithName("testModule");473 LLVMSetTarget(Module, HostTripleName.c_str());474 LLVMTypeRef FunctionType = LLVMFunctionType(LLVMInt32Type(), nullptr, 0, 0);475 LLVMValueRef MappedFn = LLVMAddFunction(Module, "mapped_fn", FunctionType);476 477 Function = LLVMAddFunction(Module, "test_fn", FunctionType);478 LLVMBasicBlockRef Entry = LLVMAppendBasicBlock(Function, "");479 LLVMBuilderRef Builder = LLVMCreateBuilder();480 LLVMPositionBuilderAtEnd(Builder, Entry);481 LLVMValueRef RetVal =482 LLVMBuildCall2(Builder, FunctionType, MappedFn, nullptr, 0, "");483 LLVMBuildRet(Builder, RetVal);484 LLVMDisposeBuilder(Builder);485 486 LLVMVerifyModule(Module, LLVMAbortProcessAction, &Error);487 LLVMDisposeMessage(Error);488 489 buildMCJITOptions();490 buildMCJITEngine();491 492 LLVMAddGlobalMapping(493 Engine, MappedFn,494 reinterpret_cast<void *>(reinterpret_cast<uintptr_t>(&localTestFunc)));495 496 buildAndRunPasses();497 498 uint64_t raw = LLVMGetFunctionAddress(Engine, "test_fn");499 int (*usable)() = (int (*)()) raw;500 501 EXPECT_EQ(42, usable());502}503