1 /**
2  * Copyright (c) 2021-2022 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "gtest/gtest.h"
17 
18 #include "asmjit/x86.h"
19 
20 #include "mem/pool_manager.h"
21 
22 namespace panda::compiler {
23 using namespace asmjit;
24 
25 class AsmJitTest : public ::testing::Test {
26 public:
AsmJitTest()27     AsmJitTest()
28     {
29         panda::mem::MemConfig::Initialize(64_MB, 64_MB, 64_MB, 32_MB);
30         PoolManager::Initialize();
31         allocator_ = new ArenaAllocator(SpaceType::SPACE_TYPE_COMPILER);
32     }
33 
~AsmJitTest()34     ~AsmJitTest()
35     {
36         delete allocator_;
37         PoolManager::Finalize();
38         panda::mem::MemConfig::Finalize();
39     }
40 
GetAllocator()41     ArenaAllocator *GetAllocator()
42     {
43         return allocator_;
44     }
45 
46 private:
47     ArenaAllocator *allocator_ {nullptr};
48 };
49 
TEST_F(AsmJitTest, HelloWorld)50 TEST_F(AsmJitTest, HelloWorld)
51 {
52     // Runtime designed for JIT code execution.
53     JitRuntime rt;
54 
55     // Holds code and relocation information.
56     CodeHolder code(GetAllocator());
57     code.init(rt.environment());
58 
59     x86::Assembler a(&code);
60     a.mov(x86::rax, 1);
61     a.ret();
62 
63     // Signature of the generated function.
64     typedef int (*Func)(void);
65     Func fn {nullptr};
66 
67     // Add the generated code to the runtime.
68     Error err = rt.add(&fn, &code);
69     ASSERT_FALSE(err);
70 
71     int result {fn()};
72     ASSERT_EQ(1, result);
73 }
74 
TEST_F(AsmJitTest, Add)75 TEST_F(AsmJitTest, Add)
76 {
77     // Runtime designed for JIT code execution.
78     JitRuntime rt;
79 
80     // Holds code and relocation information.
81     CodeHolder code(GetAllocator());
82     code.init(rt.environment());
83 
84     // Generating code:
85     x86::Assembler a(&code);
86     x86::Gp lhs = a.zax();
87     x86::Gp rhs = a.zcx();
88 
89     FuncDetail func;
90     func.init(FuncSignatureT<size_t, size_t, size_t>(CallConv::kIdHost), code.environment());
91 
92     FuncFrame frame;
93     frame.init(func);
94     frame.addDirtyRegs(lhs, rhs);
95 
96     FuncArgsAssignment args(&func);
97     args.assignAll(lhs, rhs);
98     args.updateFuncFrame(frame);
99     frame.finalize();
100 
101     a.emitProlog(frame);
102     a.emitArgsAssignment(frame, args);
103     a.add(lhs, rhs);
104     a.emitEpilog(frame);
105 
106     // Signature of the generated function.
107     typedef size_t (*Func)(size_t, size_t);
108     Func fn {nullptr};
109 
110     // Add the generated code to the runtime.
111     Error err = rt.add(&fn, &code);
112     ASSERT_FALSE(err);
113 
114     size_t result {fn(size_t(2), size_t(3))};
115     ASSERT_EQ(size_t(5), result);
116 }
117 
TEST_F(AsmJitTest, Add2)118 TEST_F(AsmJitTest, Add2)
119 {
120     JitRuntime rt;
121 
122     CodeHolder code(GetAllocator());
123     code.init(rt.environment());
124 
125     // Generating code:
126     x86::Assembler a(&code);
127     a.add(x86::rdi, x86::rsi);
128     a.mov(x86::rax, x86::rdi);
129     a.ret();
130 
131     // Signature of the generated function.
132     typedef size_t (*Func)(size_t, size_t);
133     Func fn {nullptr};
134 
135     // Add the generated code to the runtime.
136     Error err = rt.add(&fn, &code);
137     ASSERT_FALSE(err);
138 
139     size_t result {fn(size_t(2), size_t(3))};
140     ASSERT_EQ(size_t(5), result);
141 }
142 
TEST_F(AsmJitTest, AddDouble)143 TEST_F(AsmJitTest, AddDouble)
144 {
145     JitRuntime rt;
146 
147     CodeHolder code(GetAllocator());
148     code.init(rt.environment());
149 
150     // Generating code:
151     x86::Assembler a(&code);
152     a.addsd(x86::xmm0, x86::xmm1);
153     a.ret();
154 
155     // Signature of the generated function.
156     typedef double (*Func)(double, double);
157     Func fn {nullptr};
158 
159     // Add the generated code to the runtime.
160     Error err = rt.add(&fn, &code);
161     ASSERT_FALSE(err);
162 
163     double result {fn(2.0, 3.0)};
164     ASSERT_EQ(5.0, result);
165 }
166 
TEST_F(AsmJitTest, AddExplicit)167 TEST_F(AsmJitTest, AddExplicit)
168 {
169     Environment env = hostEnvironment();
170     JitAllocator allocator;
171 
172     CodeHolder code(GetAllocator());
173     code.init(env);
174 
175     // Generating code:
176     x86::Assembler a(&code);
177     x86::Gp lhs = a.zax();
178     x86::Gp rhs = a.zcx();
179 
180     FuncDetail func;
181     func.init(FuncSignatureT<size_t, size_t, size_t>(CallConv::kIdHost), code.environment());
182 
183     FuncFrame frame;
184     frame.init(func);
185     frame.addDirtyRegs(lhs, rhs);
186 
187     FuncArgsAssignment args(&func);
188     args.assignAll(lhs, rhs);
189     args.updateFuncFrame(frame);
190     frame.finalize();
191 
192     a.emitProlog(frame);
193     a.emitArgsAssignment(frame, args);
194     a.add(lhs, rhs);
195     a.emitEpilog(frame);
196 
197     code.flatten();
198     code.resolveUnresolvedLinks();
199     size_t estimated_size = code.codeSize();
200 
201     // Allocate memory for the function and relocate it there.
202     void *ro_ptr;
203     void *rw_ptr;
204     Error err = allocator.alloc(&ro_ptr, &rw_ptr, estimated_size);
205     ASSERT_FALSE(err);
206 
207     // Relocate to the base-address of the allocated memory.
208     code.relocateToBase(reinterpret_cast<uintptr_t>(rw_ptr));
209     size_t code_size = code.codeSize();
210 
211     code.copyFlattenedData(rw_ptr, code_size, CodeHolder::kCopyPadSectionBuffer);
212 
213     // Execute the function and test whether it works.
214     typedef size_t (*Func)(size_t lhs, size_t rhs);
215     Func fn = (Func)ro_ptr;
216 
217     size_t result {fn(size_t(2), size_t(3))};
218     ASSERT_EQ(size_t(5), result);
219 
220     err = allocator.release(ro_ptr);
221     ASSERT_FALSE(err);
222 }
223 }  // namespace panda::compiler
224