brintos

brintos / llvm-project-archived public Read only

0
0
Text · 16.6 KiB · bc43515 Raw
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