blob: 5385a8b035b9362aafff4d479685aaa270c33204 [file] [log] [blame]
Ian Rogersb033c752011-07-20 12:22:35 -07001// Copyright 2011 Google Inc. All Rights Reserved.
2// Author: irogers@google.com (Ian Rogers)
3#include "src/jni_compiler.h"
4#include <sys/mman.h>
5#include "src/assembler.h"
6#include "src/calling_convention.h"
Ian Rogersdf20fe02011-07-20 20:34:16 -07007#include "src/jni_internal.h"
Ian Rogersb033c752011-07-20 12:22:35 -07008#include "src/macros.h"
9#include "src/managed_register.h"
10#include "src/logging.h"
11#include "src/thread.h"
12
13namespace art {
14
15// Generate the JNI bridge for the given method, general contract:
16// - Arguments are in the managed runtime format, either on stack or in
17// registers, a reference to the method object is supplied as part of this
18// convention.
19//
20void JniCompiler::Compile(Assembler* jni_asm, Method* native_method) {
21 CHECK(native_method->IsNative());
22 JniCallingConvention jni_conv(native_method);
23 ManagedRuntimeCallingConvention mr_conv(native_method);
24 const bool is_static = native_method->IsStatic();
25
26 // 1. Build the frame
27 const size_t frame_size(jni_conv.FrameSize());
28 jni_asm->BuildFrame(frame_size, mr_conv.MethodRegister());
29
30 // 2. Save callee save registers that aren't callee save in the native code
31 // TODO: implement computing the difference of the callee saves
32 // and saving
33
34 // 3. Set up the StackHandleBlock
35 mr_conv.ResetIterator(FrameOffset(frame_size));
36 jni_conv.ResetIterator(FrameOffset(0));
37 jni_asm->StoreImmediateToFrame(jni_conv.ShbNumRefsOffset(),
38 jni_conv.HandleCount(),
39 mr_conv.InterproceduralScratchRegister());
40 jni_asm->CopyRawPtrFromThread(jni_conv.ShbLinkOffset(),
41 Thread::TopShbOffset(),
42 mr_conv.InterproceduralScratchRegister());
43 jni_asm->StoreStackOffsetToThread(Thread::TopShbOffset(),
44 jni_conv.ShbOffset(),
45 mr_conv.InterproceduralScratchRegister());
46
47 // 4. Place incoming reference arguments into handle block
48 jni_conv.Next(); // Skip JNIEnv*
49 // 4.5. Create Class argument for static methods out of passed method
50 if (is_static) {
51 FrameOffset handle_offset = jni_conv.CurrentParamHandleOffset();
52 // Check handle offset is within frame
53 CHECK_LT(handle_offset.Uint32Value(), frame_size);
54 jni_asm->LoadRef(jni_conv.InterproceduralScratchRegister(),
55 mr_conv.MethodRegister(), Method::ClassOffset());
56 jni_asm->ValidateRef(jni_conv.InterproceduralScratchRegister(), false);
57 jni_asm->StoreRef(handle_offset, jni_conv.InterproceduralScratchRegister());
58 jni_conv.Next(); // handlerized so move to next argument
59 }
60 while (mr_conv.HasNext()) {
61 CHECK(jni_conv.HasNext());
62 bool ref_param = jni_conv.IsCurrentParamAReference();
63 CHECK(!ref_param || mr_conv.IsCurrentParamAReference());
64 // References need handlerization and the handle address passing
65 if (ref_param) {
66 // Compute handle offset, note null is handlerized but its boxed value
67 // must be NULL
68 FrameOffset handle_offset = jni_conv.CurrentParamHandleOffset();
69 // Check handle offset is within frame
70 CHECK_LT(handle_offset.Uint32Value(), frame_size);
71 bool input_in_reg = mr_conv.IsCurrentParamInRegister();
72 CHECK(input_in_reg || mr_conv.IsCurrentParamOnStack());
73 if (input_in_reg) {
74 LOG(FATAL) << "UNTESTED";
75 ManagedRegister in_reg = mr_conv.CurrentParamRegister();
76 jni_asm->ValidateRef(in_reg, mr_conv.IsCurrentParamPossiblyNull());
77 jni_asm->StoreRef(handle_offset, in_reg);
78 } else {
79 FrameOffset in_off = mr_conv.CurrentParamStackOffset();
80 jni_asm->ValidateRef(in_off, mr_conv.IsCurrentParamPossiblyNull());
81 jni_asm->CopyRef(handle_offset, in_off,
82 mr_conv.InterproceduralScratchRegister());
83 }
84 }
85 mr_conv.Next();
86 jni_conv.Next();
87 }
88
Ian Rogersdf20fe02011-07-20 20:34:16 -070089 // 5. Transition from being in managed to native code
Ian Rogersb033c752011-07-20 12:22:35 -070090 // TODO: write out anchor, ensure the transition to native follow a store
91 // fence.
92 jni_asm->StoreImmediateToThread(Thread::StateOffset(), Thread::kNative,
93 mr_conv.InterproceduralScratchRegister());
94
Ian Rogersdf20fe02011-07-20 20:34:16 -070095 // 6. Move frame down to allow space for out going args. Do for as short a
Ian Rogersb033c752011-07-20 12:22:35 -070096 // time as possible to aid profiling..
97 const size_t out_arg_size = jni_conv.OutArgSize();
98 jni_asm->IncreaseFrameSize(out_arg_size);
99
Ian Rogersdf20fe02011-07-20 20:34:16 -0700100 // 7. Acquire lock for synchronized methods.
101 if (native_method->IsSynchronized()) {
102 mr_conv.ResetIterator(FrameOffset(frame_size+out_arg_size));
103 jni_conv.ResetIterator(FrameOffset(out_arg_size));
104 jni_conv.Next(); // Skip JNIEnv*
105 // Get stack handle for 1st argument
106 if (is_static) {
107 FrameOffset handle_offset = jni_conv.CurrentParamHandleOffset();
108 if (jni_conv.IsCurrentParamOnStack()) {
109 FrameOffset out_off = jni_conv.CurrentParamStackOffset();
110 jni_asm->CreateStackHandle(out_off, handle_offset,
111 mr_conv.InterproceduralScratchRegister(),
112 false);
113 } else {
114 ManagedRegister out_reg = jni_conv.CurrentParamRegister();
115 jni_asm->CreateStackHandle(out_reg, handle_offset,
116 ManagedRegister::NoRegister(), false);
117 }
118 } else {
119 CopyParameter(jni_asm, &mr_conv, &jni_conv, frame_size, out_arg_size);
120 }
121 // Generate JNIEnv* in place and leave a copy in jni_env_register
122 ManagedRegister jni_env_register =
123 jni_conv.InterproceduralScratchRegister();
124 if (jni_conv.IsCurrentParamInRegister()) {
125 jni_env_register = jni_conv.CurrentParamRegister();
126 }
127 jni_asm->LoadRawPtrFromThread(jni_env_register, Thread::JniEnvOffset());
128 if (!jni_conv.IsCurrentParamInRegister()) {
129 FrameOffset out_off = jni_conv.CurrentParamStackOffset();
130 jni_asm->StoreRawPtr(out_off, jni_env_register);
131 }
132 // Call JNIEnv*->MonitorEnter(JNIEnv*, object)
133 jni_asm->Call(jni_env_register, JniEnvironment::MonitorEnterOffset(),
134 jni_conv.InterproceduralScratchRegister());
135 }
136
Ian Rogersb033c752011-07-20 12:22:35 -0700137 // 8. Iterate over arguments placing values from managed calling convention in
138 // to the convention required for a native call (shuffling). For references
139 // place an index/pointer to the reference after checking whether it is
140 // NULL (which must be encoded as NULL).
141 // NB. we do this prior to materializing the JNIEnv* and static's jclass to
142 // give as many free registers for the shuffle as possible
143 mr_conv.ResetIterator(FrameOffset(frame_size+out_arg_size));
144 jni_conv.ResetIterator(FrameOffset(out_arg_size));
145 jni_conv.Next(); // Skip JNIEnv*
146 if (is_static) {
147 FrameOffset handle_offset = jni_conv.CurrentParamHandleOffset();
148 if (jni_conv.IsCurrentParamOnStack()) {
149 FrameOffset out_off = jni_conv.CurrentParamStackOffset();
150 jni_asm->CreateStackHandle(out_off, handle_offset,
151 mr_conv.InterproceduralScratchRegister(),
152 false);
153 } else {
154 ManagedRegister out_reg = jni_conv.CurrentParamRegister();
155 jni_asm->CreateStackHandle(out_reg, handle_offset,
156 ManagedRegister::NoRegister(), false);
157 }
158 jni_conv.Next();
159 }
160 while (mr_conv.HasNext()) {
161 CHECK(jni_conv.HasNext());
Ian Rogersdf20fe02011-07-20 20:34:16 -0700162 CopyParameter(jni_asm, &mr_conv, &jni_conv, frame_size, out_arg_size);
Ian Rogersb033c752011-07-20 12:22:35 -0700163 mr_conv.Next();
164 jni_conv.Next();
165 }
166 // 9. Create 1st argument, the JNI environment ptr
167 jni_conv.ResetIterator(FrameOffset(out_arg_size));
168 if (jni_conv.IsCurrentParamInRegister()) {
169 jni_asm->LoadRawPtrFromThread(jni_conv.CurrentParamRegister(),
170 Thread::JniEnvOffset());
171 } else {
172 jni_asm->CopyRawPtrFromThread(jni_conv.CurrentParamStackOffset(),
173 Thread::JniEnvOffset(),
174 jni_conv.InterproceduralScratchRegister());
175 }
176
177 // 10. Plant call to native code associated with method
178 jni_asm->Call(mr_conv.MethodRegister(), Method::NativeMethodOffset(),
179 mr_conv.InterproceduralScratchRegister());
180
Ian Rogersdf20fe02011-07-20 20:34:16 -0700181 // 11. Release lock for synchronized methods.
182 if (native_method->IsSynchronized()) {
183 mr_conv.ResetIterator(FrameOffset(frame_size+out_arg_size));
184 jni_conv.ResetIterator(FrameOffset(out_arg_size));
185 jni_conv.Next(); // Skip JNIEnv*
186 // Save return value
187 FrameOffset return_save_location = jni_conv.ReturnValueSaveLocation();
188 CHECK_LT(return_save_location.Uint32Value(), frame_size+out_arg_size);
189 jni_asm->Store(return_save_location, jni_conv.ReturnRegister(),
190 jni_conv.SizeOfReturnValue());
191 // Get stack handle for 1st argument
192 if (is_static) {
193 FrameOffset handle_offset = jni_conv.CurrentParamHandleOffset();
194 if (jni_conv.IsCurrentParamOnStack()) {
195 FrameOffset out_off = jni_conv.CurrentParamStackOffset();
196 jni_asm->CreateStackHandle(out_off, handle_offset,
197 mr_conv.InterproceduralScratchRegister(),
198 false);
199 } else {
200 ManagedRegister out_reg = jni_conv.CurrentParamRegister();
201 jni_asm->CreateStackHandle(out_reg, handle_offset,
202 ManagedRegister::NoRegister(), false);
203 }
204 } else {
205 CopyParameter(jni_asm, &mr_conv, &jni_conv, frame_size, out_arg_size);
206 }
207 // Generate JNIEnv* in place and leave a copy in jni_env_register
208 ManagedRegister jni_env_register =
209 jni_conv.InterproceduralScratchRegister();
210 if (jni_conv.IsCurrentParamInRegister()) {
211 jni_env_register = jni_conv.CurrentParamRegister();
212 }
213 jni_asm->LoadRawPtrFromThread(jni_env_register, Thread::JniEnvOffset());
214 if (!jni_conv.IsCurrentParamInRegister()) {
215 FrameOffset out_off = jni_conv.CurrentParamStackOffset();
216 jni_asm->StoreRawPtr(out_off, jni_env_register);
217 }
218 // Call JNIEnv*->MonitorExit(JNIEnv*, object)
219 jni_asm->Call(jni_env_register, JniEnvironment::MonitorExitOffset(),
220 jni_conv.InterproceduralScratchRegister());
221 // Reload return value
222 jni_asm->Load(jni_conv.ReturnRegister(), return_save_location,
223 jni_conv.SizeOfReturnValue());
224 }
225
Ian Rogersb033c752011-07-20 12:22:35 -0700226 // 11. Release outgoing argument area
227 jni_asm->DecreaseFrameSize(out_arg_size);
Ian Rogersdf20fe02011-07-20 20:34:16 -0700228 mr_conv.ResetIterator(FrameOffset(frame_size));
229 jni_conv.ResetIterator(FrameOffset(0));
Ian Rogersb033c752011-07-20 12:22:35 -0700230
231 // 12. Transition from being in native to managed code, possibly entering a
232 // safepoint
233 jni_asm->StoreImmediateToThread(Thread::StateOffset(), Thread::kRunnable,
234 mr_conv.InterproceduralScratchRegister());
235 // TODO: check for safepoint transition
236
Ian Rogersb033c752011-07-20 12:22:35 -0700237 // 15. Place result in correct register possibly dehandlerizing
238 if (jni_conv.IsReturnAReference()) {
239 jni_asm->LoadReferenceFromStackHandle(mr_conv.ReturnRegister(),
Ian Rogersdf20fe02011-07-20 20:34:16 -0700240 jni_conv.ReturnRegister());
Ian Rogersb033c752011-07-20 12:22:35 -0700241 } else {
242 jni_asm->Move(mr_conv.ReturnRegister(), jni_conv.ReturnRegister());
243 }
244
245 // 16. Remove stack handle block from thread
246 jni_asm->CopyRawPtrToThread(Thread::TopShbOffset(), jni_conv.ShbLinkOffset(),
247 jni_conv.InterproceduralScratchRegister());
248
249 // 17. Remove activation
250 jni_asm->RemoveFrame(frame_size);
251
252 // 18. Finalize code generation
253 size_t cs = jni_asm->CodeSize();
254 MemoryRegion code(AllocateCode(cs), cs);
255 jni_asm->FinalizeInstructions(code);
256 native_method->SetCode(code.pointer());
257}
258
Ian Rogersdf20fe02011-07-20 20:34:16 -0700259// Copy a single parameter from the managed to the JNI calling convention
260void JniCompiler::CopyParameter(Assembler* jni_asm,
261 ManagedRuntimeCallingConvention* mr_conv,
262 JniCallingConvention* jni_conv,
263 size_t frame_size, size_t out_arg_size) {
264 bool input_in_reg = mr_conv->IsCurrentParamInRegister();
265 bool output_in_reg = jni_conv->IsCurrentParamInRegister();
266 FrameOffset handle_offset(0);
267 bool null_allowed = false;
268 bool ref_param = jni_conv->IsCurrentParamAReference();
269 CHECK(!ref_param || mr_conv->IsCurrentParamAReference());
270 CHECK(input_in_reg || mr_conv->IsCurrentParamOnStack());
271 CHECK(output_in_reg || jni_conv->IsCurrentParamOnStack());
272 // References need handlerization and the handle address passing
273 if (ref_param) {
274 null_allowed = mr_conv->IsCurrentParamPossiblyNull();
275 // Compute handle offset. Note null is placed in the SHB but the jobject
276 // passed to the native code must be null (not a pointer into the SHB
277 // as with regular references).
278 handle_offset = jni_conv->CurrentParamHandleOffset();
279 // Check handle offset is within frame.
280 CHECK_LT(handle_offset.Uint32Value(), (frame_size+out_arg_size));
281 }
282 if (input_in_reg && output_in_reg) {
283 LOG(FATAL) << "UNTESTED";
284 ManagedRegister in_reg = mr_conv->CurrentParamRegister();
285 ManagedRegister out_reg = jni_conv->CurrentParamRegister();
286 if (ref_param) {
287 jni_asm->CreateStackHandle(out_reg, handle_offset, in_reg,
288 null_allowed);
289 } else {
290 jni_asm->Move(out_reg, in_reg);
291 }
292 } else if (!input_in_reg && !output_in_reg) {
293 FrameOffset out_off = jni_conv->CurrentParamStackOffset();
294 if (ref_param) {
295 jni_asm->CreateStackHandle(out_off, handle_offset,
296 mr_conv->InterproceduralScratchRegister(),
297 null_allowed);
298 } else {
299 FrameOffset in_off = mr_conv->CurrentParamStackOffset();
300 size_t param_size = mr_conv->CurrentParamSize();
301 CHECK_EQ(param_size, jni_conv->CurrentParamSize());
302 jni_asm->Copy(out_off, in_off, mr_conv->InterproceduralScratchRegister(),
303 param_size);
304 }
305 } else if (!input_in_reg && output_in_reg) {
306 LOG(FATAL) << "UNTESTED";
307 FrameOffset in_off = mr_conv->CurrentParamStackOffset();
308 ManagedRegister out_reg = jni_conv->CurrentParamRegister();
309 // Check that incoming stack arguments are above the current stack frame.
310 CHECK_GT(in_off.Uint32Value(), frame_size);
311 if (ref_param) {
312 jni_asm->CreateStackHandle(out_reg, handle_offset,
313 ManagedRegister::NoRegister(), null_allowed);
314 } else {
315 unsigned int param_size = mr_conv->CurrentParamSize();
316 CHECK_EQ(param_size, jni_conv->CurrentParamSize());
317 jni_asm->Load(out_reg, in_off, param_size);
318 }
319 } else {
320 LOG(FATAL) << "UNTESTED";
321 CHECK(input_in_reg && !output_in_reg);
322 ManagedRegister in_reg = mr_conv->CurrentParamRegister();
323 FrameOffset out_off = jni_conv->CurrentParamStackOffset();
324 // Check outgoing argument is within frame
325 CHECK_LT(out_off.Uint32Value(), frame_size);
326 if (ref_param) {
327 // TODO: recycle value in in_reg rather than reload from handle
328 jni_asm->CreateStackHandle(out_off, handle_offset,
329 mr_conv->InterproceduralScratchRegister(),
330 null_allowed);
331 } else {
332 size_t param_size = mr_conv->CurrentParamSize();
333 CHECK_EQ(param_size, jni_conv->CurrentParamSize());
334 jni_asm->Store(out_off, in_reg, param_size);
335 }
336 }
337}
338
Ian Rogersb033c752011-07-20 12:22:35 -0700339void* JniCompiler::AllocateCode(size_t size) {
340 CHECK_LT(((jni_code_top_ - jni_code_) + size), jni_code_size_);
341 void *result = jni_code_top_;
342 jni_code_top_ += size;
343 return result;
344}
345
346JniCompiler::JniCompiler() {
347 // TODO: this shouldn't be managed by the JniCompiler, we should have a
348 // code cache.
349 jni_code_size_ = 4096;
350 jni_code_ = static_cast<byte*>(mmap(NULL, jni_code_size_,
351 PROT_READ | PROT_WRITE | PROT_EXEC,
352 MAP_ANONYMOUS | MAP_PRIVATE, -1, 0));
353 CHECK_NE(MAP_FAILED, jni_code_);
354 jni_code_top_ = jni_code_;
355}
356
357JniCompiler::~JniCompiler() {
358 // TODO: this shouldn't be managed by the JniCompiler, we should have a
359 // code cache.
360 CHECK_EQ(0, munmap(jni_code_, jni_code_size_));
361}
362
363} // namespace art