aboutsummaryrefslogtreecommitdiffstats
path: root/unittests/ExecutionEngine/MCJIT/MCJITTest.cpp
blob: 94b9a6927b19b450241448ffc0f9ca2ec918304d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
//===- MCJITTest.cpp - Unit tests for the MCJIT ---------------------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This test suite verifies basic MCJIT functionality such as making function
// calls, using global variables, and compiling multpile modules.
//
//===----------------------------------------------------------------------===//

#include "llvm/ExecutionEngine/MCJIT.h"
#include "MCJITTestBase.h"
#include "gtest/gtest.h"

using namespace llvm;

namespace {

class MCJITTest : public testing::Test, public MCJITTestBase {
protected:
  void SetUp() override { M.reset(createEmptyModule("<main>")); }
};

// FIXME: Ensure creating an execution engine does not crash when constructed
//        with a null module.
/*
TEST_F(MCJITTest, null_module) {
  createJIT(0);
}
*/

// FIXME: In order to JIT an empty module, there needs to be
// an interface to ExecutionEngine that forces compilation but
// does not require retrieval of a pointer to a function/global.
/*
TEST_F(MCJITTest, empty_module) {
  createJIT(M.take());
  //EXPECT_NE(0, TheJIT->getObjectImage())
  //  << "Unable to generate executable loaded object image";
}
*/

TEST_F(MCJITTest, global_variable) {
  SKIP_UNSUPPORTED_PLATFORM;

  int initialValue = 5;
  GlobalValue *Global = insertGlobalInt32(M.get(), "test_global", initialValue);
  createJIT(std::move(M));
  void *globalPtr =  TheJIT->getPointerToGlobal(Global);
  EXPECT_TRUE(nullptr != globalPtr)
    << "Unable to get pointer to global value from JIT";

  EXPECT_EQ(initialValue, *(int32_t*)globalPtr)
    << "Unexpected initial value of global";
}

TEST_F(MCJITTest, add_function) {
  SKIP_UNSUPPORTED_PLATFORM;

  Function *F = insertAddFunction(M.get());
  createJIT(std::move(M));
  uint64_t addPtr = TheJIT->getFunctionAddress(F->getName().str());
  EXPECT_TRUE(0 != addPtr)
    << "Unable to get pointer to function from JIT";

  ASSERT_TRUE(addPtr != 0) << "Unable to get pointer to function .";
  int (*AddPtr)(int, int) = (int(*)(int, int))addPtr ;
  EXPECT_EQ(0,   AddPtr(0, 0));
  EXPECT_EQ(1,   AddPtr(1, 0));
  EXPECT_EQ(3,   AddPtr(1, 2));
  EXPECT_EQ(-5,  AddPtr(-2, -3));
  EXPECT_EQ(30,  AddPtr(10, 20));
  EXPECT_EQ(-30, AddPtr(-10, -20));
  EXPECT_EQ(-40, AddPtr(-10, -30));
}

TEST_F(MCJITTest, run_main) {
  SKIP_UNSUPPORTED_PLATFORM;

  int rc = 6;
  Function *Main = insertMainFunction(M.get(), 6);
  createJIT(std::move(M));
  uint64_t ptr = TheJIT->getFunctionAddress(Main->getName().str());
  EXPECT_TRUE(0 != ptr)
    << "Unable to get pointer to main() from JIT";

  int (*FuncPtr)(void) = (int(*)(void))ptr;
  int returnCode = FuncPtr();
  EXPECT_EQ(returnCode, rc);
}

TEST_F(MCJITTest, return_global) {
  SKIP_UNSUPPORTED_PLATFORM;

  int32_t initialNum = 7;
  GlobalVariable *GV = insertGlobalInt32(M.get(), "myglob", initialNum);

  Function *ReturnGlobal = startFunction<int32_t(void)>(M.get(),
                                                        "ReturnGlobal");
  Value *ReadGlobal = Builder.CreateLoad(GV);
  endFunctionWithRet(ReturnGlobal, ReadGlobal);

  createJIT(std::move(M));
  uint64_t rgvPtr = TheJIT->getFunctionAddress(ReturnGlobal->getName().str());
  EXPECT_TRUE(0 != rgvPtr);

  int32_t(*FuncPtr)(void) = (int32_t(*)(void))rgvPtr;
  EXPECT_EQ(initialNum, FuncPtr())
    << "Invalid value for global returned from JITted function";
}

// FIXME: This case fails due to a bug with getPointerToGlobal().
// The bug is due to MCJIT not having an implementation of getPointerToGlobal()
// which results in falling back on the ExecutionEngine implementation that
// allocates a new memory block for the global instead of using the same
// global variable that is emitted by MCJIT. Hence, the pointer (gvPtr below)
// has the correct initial value, but updates to the real global (accessed by
// JITted code) are not propagated. Instead, getPointerToGlobal() should return
// a pointer into the loaded ObjectImage to reference the emitted global.
/*
TEST_F(MCJITTest, increment_global) {
  SKIP_UNSUPPORTED_PLATFORM;

  int32_t initialNum = 5;
  Function *IncrementGlobal = startFunction<int32_t(void)>(M.get(), "IncrementGlobal");
  GlobalVariable *GV = insertGlobalInt32(M.get(), "my_global", initialNum);
  Value *DerefGV = Builder.CreateLoad(GV);
  Value *AddResult = Builder.CreateAdd(DerefGV,
                                       ConstantInt::get(Context, APInt(32, 1)));
  Builder.CreateStore(AddResult, GV);
  endFunctionWithRet(IncrementGlobal, AddResult);

  createJIT(M.take());
  void *gvPtr = TheJIT->getPointerToGlobal(GV);
  EXPECT_EQ(initialNum, *(int32_t*)gvPtr);

  void *vPtr = TheJIT->getFunctionAddress(IncrementGlobal->getName().str());
  EXPECT_TRUE(0 != vPtr)
    << "Unable to get pointer to main() from JIT";

  int32_t(*FuncPtr)(void) = (int32_t(*)(void))(intptr_t)vPtr;

  for(int i = 1; i < 3; ++i) {
    int32_t result = FuncPtr();
    EXPECT_EQ(initialNum + i, result);            // OK
    EXPECT_EQ(initialNum + i, *(int32_t*)gvPtr);  // FAILS
  }
}
*/

// PR16013: XFAIL this test on ARM, which currently can't handle multiple relocations.
#if !defined(__arm__)

TEST_F(MCJITTest, multiple_functions) {
  SKIP_UNSUPPORTED_PLATFORM;

  unsigned int numLevels = 23;
  int32_t innerRetVal= 5;

  Function *Inner = startFunction<int32_t(void)>(M.get(), "Inner");
  endFunctionWithRet(Inner, ConstantInt::get(Context, APInt(32, innerRetVal)));

  Function *Outer;
  for (unsigned int i = 0; i < numLevels; ++i) {
    std::stringstream funcName;
    funcName << "level_" << i;
    Outer = startFunction<int32_t(void)>(M.get(), funcName.str());
    Value *innerResult = Builder.CreateCall(Inner);
    endFunctionWithRet(Outer, innerResult);

    Inner = Outer;
  }

  createJIT(std::move(M));
  uint64_t ptr = TheJIT->getFunctionAddress(Outer->getName().str());
  EXPECT_TRUE(0 != ptr)
    << "Unable to get pointer to outer function from JIT";

  int32_t(*FuncPtr)(void) = (int32_t(*)(void))ptr;
  EXPECT_EQ(innerRetVal, FuncPtr())
    << "Incorrect result returned from function";
}

#endif /*!defined(__arm__)*/

TEST_F(MCJITTest, multiple_decl_lookups) {
  SKIP_UNSUPPORTED_PLATFORM;

  Function *Foo = insertExternalReferenceToFunction<void(void)>(M.get(), "_exit");
  createJIT(std::move(M));
  void *A = TheJIT->getPointerToFunction(Foo);
  void *B = TheJIT->getPointerToFunction(Foo);

  EXPECT_TRUE(A != 0) << "Failed lookup - test not correctly configured.";
  EXPECT_EQ(A, B) << "Repeat calls to getPointerToFunction fail.";
}

}