blob: a6f5fafdcf705839169add74fb0acca9e89b0069 [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.
Ian Rogers45a76cb2011-07-21 22:00:15 -070092 jni_asm->StoreStackPointerToThread(Thread::TopOfManagedStackOffset());
Ian Rogersb033c752011-07-20 12:22:35 -070093 jni_asm->StoreImmediateToThread(Thread::StateOffset(), Thread::kNative,
94 mr_conv.InterproceduralScratchRegister());
95
Ian Rogersdf20fe02011-07-20 20:34:16 -070096 // 6. Move frame down to allow space for out going args. Do for as short a
Ian Rogersb033c752011-07-20 12:22:35 -070097 // time as possible to aid profiling..
98 const size_t out_arg_size = jni_conv.OutArgSize();
99 jni_asm->IncreaseFrameSize(out_arg_size);
100
Ian Rogersdf20fe02011-07-20 20:34:16 -0700101 // 7. Acquire lock for synchronized methods.
102 if (native_method->IsSynchronized()) {
103 mr_conv.ResetIterator(FrameOffset(frame_size+out_arg_size));
104 jni_conv.ResetIterator(FrameOffset(out_arg_size));
105 jni_conv.Next(); // Skip JNIEnv*
106 // Get stack handle for 1st argument
107 if (is_static) {
108 FrameOffset handle_offset = jni_conv.CurrentParamHandleOffset();
109 if (jni_conv.IsCurrentParamOnStack()) {
110 FrameOffset out_off = jni_conv.CurrentParamStackOffset();
111 jni_asm->CreateStackHandle(out_off, handle_offset,
112 mr_conv.InterproceduralScratchRegister(),
113 false);
114 } else {
115 ManagedRegister out_reg = jni_conv.CurrentParamRegister();
116 jni_asm->CreateStackHandle(out_reg, handle_offset,
117 ManagedRegister::NoRegister(), false);
118 }
119 } else {
120 CopyParameter(jni_asm, &mr_conv, &jni_conv, frame_size, out_arg_size);
121 }
122 // Generate JNIEnv* in place and leave a copy in jni_env_register
123 ManagedRegister jni_env_register =
124 jni_conv.InterproceduralScratchRegister();
125 if (jni_conv.IsCurrentParamInRegister()) {
126 jni_env_register = jni_conv.CurrentParamRegister();
127 }
128 jni_asm->LoadRawPtrFromThread(jni_env_register, Thread::JniEnvOffset());
129 if (!jni_conv.IsCurrentParamInRegister()) {
130 FrameOffset out_off = jni_conv.CurrentParamStackOffset();
131 jni_asm->StoreRawPtr(out_off, jni_env_register);
132 }
133 // Call JNIEnv*->MonitorEnter(JNIEnv*, object)
134 jni_asm->Call(jni_env_register, JniEnvironment::MonitorEnterOffset(),
135 jni_conv.InterproceduralScratchRegister());
136 }
137
Ian Rogersb033c752011-07-20 12:22:35 -0700138 // 8. Iterate over arguments placing values from managed calling convention in
139 // to the convention required for a native call (shuffling). For references
140 // place an index/pointer to the reference after checking whether it is
141 // NULL (which must be encoded as NULL).
142 // NB. we do this prior to materializing the JNIEnv* and static's jclass to
143 // give as many free registers for the shuffle as possible
144 mr_conv.ResetIterator(FrameOffset(frame_size+out_arg_size));
145 jni_conv.ResetIterator(FrameOffset(out_arg_size));
146 jni_conv.Next(); // Skip JNIEnv*
147 if (is_static) {
148 FrameOffset handle_offset = jni_conv.CurrentParamHandleOffset();
149 if (jni_conv.IsCurrentParamOnStack()) {
150 FrameOffset out_off = jni_conv.CurrentParamStackOffset();
151 jni_asm->CreateStackHandle(out_off, handle_offset,
152 mr_conv.InterproceduralScratchRegister(),
153 false);
154 } else {
155 ManagedRegister out_reg = jni_conv.CurrentParamRegister();
156 jni_asm->CreateStackHandle(out_reg, handle_offset,
157 ManagedRegister::NoRegister(), false);
158 }
159 jni_conv.Next();
160 }
161 while (mr_conv.HasNext()) {
162 CHECK(jni_conv.HasNext());
Ian Rogersdf20fe02011-07-20 20:34:16 -0700163 CopyParameter(jni_asm, &mr_conv, &jni_conv, frame_size, out_arg_size);
Ian Rogersb033c752011-07-20 12:22:35 -0700164 mr_conv.Next();
165 jni_conv.Next();
166 }
167 // 9. Create 1st argument, the JNI environment ptr
168 jni_conv.ResetIterator(FrameOffset(out_arg_size));
169 if (jni_conv.IsCurrentParamInRegister()) {
170 jni_asm->LoadRawPtrFromThread(jni_conv.CurrentParamRegister(),
171 Thread::JniEnvOffset());
172 } else {
173 jni_asm->CopyRawPtrFromThread(jni_conv.CurrentParamStackOffset(),
174 Thread::JniEnvOffset(),
175 jni_conv.InterproceduralScratchRegister());
176 }
177
178 // 10. Plant call to native code associated with method
179 jni_asm->Call(mr_conv.MethodRegister(), Method::NativeMethodOffset(),
180 mr_conv.InterproceduralScratchRegister());
181
Ian Rogersdf20fe02011-07-20 20:34:16 -0700182 // 11. Release lock for synchronized methods.
183 if (native_method->IsSynchronized()) {
184 mr_conv.ResetIterator(FrameOffset(frame_size+out_arg_size));
185 jni_conv.ResetIterator(FrameOffset(out_arg_size));
186 jni_conv.Next(); // Skip JNIEnv*
187 // Save return value
188 FrameOffset return_save_location = jni_conv.ReturnValueSaveLocation();
189 CHECK_LT(return_save_location.Uint32Value(), frame_size+out_arg_size);
190 jni_asm->Store(return_save_location, jni_conv.ReturnRegister(),
191 jni_conv.SizeOfReturnValue());
192 // Get stack handle for 1st argument
193 if (is_static) {
194 FrameOffset handle_offset = jni_conv.CurrentParamHandleOffset();
195 if (jni_conv.IsCurrentParamOnStack()) {
196 FrameOffset out_off = jni_conv.CurrentParamStackOffset();
197 jni_asm->CreateStackHandle(out_off, handle_offset,
198 mr_conv.InterproceduralScratchRegister(),
199 false);
200 } else {
201 ManagedRegister out_reg = jni_conv.CurrentParamRegister();
202 jni_asm->CreateStackHandle(out_reg, handle_offset,
203 ManagedRegister::NoRegister(), false);
204 }
205 } else {
206 CopyParameter(jni_asm, &mr_conv, &jni_conv, frame_size, out_arg_size);
207 }
208 // Generate JNIEnv* in place and leave a copy in jni_env_register
209 ManagedRegister jni_env_register =
210 jni_conv.InterproceduralScratchRegister();
211 if (jni_conv.IsCurrentParamInRegister()) {
212 jni_env_register = jni_conv.CurrentParamRegister();
213 }
214 jni_asm->LoadRawPtrFromThread(jni_env_register, Thread::JniEnvOffset());
215 if (!jni_conv.IsCurrentParamInRegister()) {
216 FrameOffset out_off = jni_conv.CurrentParamStackOffset();
217 jni_asm->StoreRawPtr(out_off, jni_env_register);
218 }
219 // Call JNIEnv*->MonitorExit(JNIEnv*, object)
220 jni_asm->Call(jni_env_register, JniEnvironment::MonitorExitOffset(),
221 jni_conv.InterproceduralScratchRegister());
222 // Reload return value
223 jni_asm->Load(jni_conv.ReturnRegister(), return_save_location,
224 jni_conv.SizeOfReturnValue());
225 }
226
Ian Rogersb033c752011-07-20 12:22:35 -0700227 // 11. Release outgoing argument area
228 jni_asm->DecreaseFrameSize(out_arg_size);
Ian Rogersdf20fe02011-07-20 20:34:16 -0700229 mr_conv.ResetIterator(FrameOffset(frame_size));
230 jni_conv.ResetIterator(FrameOffset(0));
Ian Rogersb033c752011-07-20 12:22:35 -0700231
232 // 12. Transition from being in native to managed code, possibly entering a
233 // safepoint
Ian Rogers45a76cb2011-07-21 22:00:15 -0700234 CHECK(!jni_conv.InterproceduralScratchRegister()
235 .Equals(jni_conv.ReturnRegister())); // don't clobber result
236 // Location to preserve result on slow path, ensuring its within the frame
237 FrameOffset return_save_location = jni_conv.ReturnValueSaveLocation();
238 CHECK_LT(return_save_location.Uint32Value(), frame_size);
239 jni_asm->SuspendPoll(jni_conv.InterproceduralScratchRegister(),
240 jni_conv.ReturnRegister(), return_save_location,
241 jni_conv.SizeOfReturnValue());
242 jni_asm->ExceptionPoll(jni_conv.InterproceduralScratchRegister());
Ian Rogersb033c752011-07-20 12:22:35 -0700243 jni_asm->StoreImmediateToThread(Thread::StateOffset(), Thread::kRunnable,
Ian Rogers45a76cb2011-07-21 22:00:15 -0700244 jni_conv.InterproceduralScratchRegister());
245
Ian Rogersb033c752011-07-20 12:22:35 -0700246
Ian Rogersb033c752011-07-20 12:22:35 -0700247 // 15. Place result in correct register possibly dehandlerizing
248 if (jni_conv.IsReturnAReference()) {
249 jni_asm->LoadReferenceFromStackHandle(mr_conv.ReturnRegister(),
Ian Rogersdf20fe02011-07-20 20:34:16 -0700250 jni_conv.ReturnRegister());
Ian Rogersb033c752011-07-20 12:22:35 -0700251 } else {
252 jni_asm->Move(mr_conv.ReturnRegister(), jni_conv.ReturnRegister());
253 }
254
255 // 16. Remove stack handle block from thread
256 jni_asm->CopyRawPtrToThread(Thread::TopShbOffset(), jni_conv.ShbLinkOffset(),
257 jni_conv.InterproceduralScratchRegister());
258
259 // 17. Remove activation
260 jni_asm->RemoveFrame(frame_size);
261
262 // 18. Finalize code generation
Ian Rogers45a76cb2011-07-21 22:00:15 -0700263 jni_asm->EmitSlowPaths();
Ian Rogersb033c752011-07-20 12:22:35 -0700264 size_t cs = jni_asm->CodeSize();
265 MemoryRegion code(AllocateCode(cs), cs);
266 jni_asm->FinalizeInstructions(code);
267 native_method->SetCode(code.pointer());
268}
269
Ian Rogersdf20fe02011-07-20 20:34:16 -0700270// Copy a single parameter from the managed to the JNI calling convention
271void JniCompiler::CopyParameter(Assembler* jni_asm,
272 ManagedRuntimeCallingConvention* mr_conv,
273 JniCallingConvention* jni_conv,
274 size_t frame_size, size_t out_arg_size) {
275 bool input_in_reg = mr_conv->IsCurrentParamInRegister();
276 bool output_in_reg = jni_conv->IsCurrentParamInRegister();
277 FrameOffset handle_offset(0);
278 bool null_allowed = false;
279 bool ref_param = jni_conv->IsCurrentParamAReference();
280 CHECK(!ref_param || mr_conv->IsCurrentParamAReference());
281 CHECK(input_in_reg || mr_conv->IsCurrentParamOnStack());
282 CHECK(output_in_reg || jni_conv->IsCurrentParamOnStack());
283 // References need handlerization and the handle address passing
284 if (ref_param) {
285 null_allowed = mr_conv->IsCurrentParamPossiblyNull();
286 // Compute handle offset. Note null is placed in the SHB but the jobject
287 // passed to the native code must be null (not a pointer into the SHB
288 // as with regular references).
289 handle_offset = jni_conv->CurrentParamHandleOffset();
290 // Check handle offset is within frame.
291 CHECK_LT(handle_offset.Uint32Value(), (frame_size+out_arg_size));
292 }
293 if (input_in_reg && output_in_reg) {
294 LOG(FATAL) << "UNTESTED";
295 ManagedRegister in_reg = mr_conv->CurrentParamRegister();
296 ManagedRegister out_reg = jni_conv->CurrentParamRegister();
297 if (ref_param) {
298 jni_asm->CreateStackHandle(out_reg, handle_offset, in_reg,
299 null_allowed);
300 } else {
301 jni_asm->Move(out_reg, in_reg);
302 }
303 } else if (!input_in_reg && !output_in_reg) {
304 FrameOffset out_off = jni_conv->CurrentParamStackOffset();
305 if (ref_param) {
306 jni_asm->CreateStackHandle(out_off, handle_offset,
307 mr_conv->InterproceduralScratchRegister(),
308 null_allowed);
309 } else {
310 FrameOffset in_off = mr_conv->CurrentParamStackOffset();
311 size_t param_size = mr_conv->CurrentParamSize();
312 CHECK_EQ(param_size, jni_conv->CurrentParamSize());
313 jni_asm->Copy(out_off, in_off, mr_conv->InterproceduralScratchRegister(),
314 param_size);
315 }
316 } else if (!input_in_reg && output_in_reg) {
317 LOG(FATAL) << "UNTESTED";
318 FrameOffset in_off = mr_conv->CurrentParamStackOffset();
319 ManagedRegister out_reg = jni_conv->CurrentParamRegister();
320 // Check that incoming stack arguments are above the current stack frame.
321 CHECK_GT(in_off.Uint32Value(), frame_size);
322 if (ref_param) {
323 jni_asm->CreateStackHandle(out_reg, handle_offset,
324 ManagedRegister::NoRegister(), null_allowed);
325 } else {
326 unsigned int param_size = mr_conv->CurrentParamSize();
327 CHECK_EQ(param_size, jni_conv->CurrentParamSize());
328 jni_asm->Load(out_reg, in_off, param_size);
329 }
330 } else {
331 LOG(FATAL) << "UNTESTED";
332 CHECK(input_in_reg && !output_in_reg);
333 ManagedRegister in_reg = mr_conv->CurrentParamRegister();
334 FrameOffset out_off = jni_conv->CurrentParamStackOffset();
335 // Check outgoing argument is within frame
336 CHECK_LT(out_off.Uint32Value(), frame_size);
337 if (ref_param) {
338 // TODO: recycle value in in_reg rather than reload from handle
339 jni_asm->CreateStackHandle(out_off, handle_offset,
340 mr_conv->InterproceduralScratchRegister(),
341 null_allowed);
342 } else {
343 size_t param_size = mr_conv->CurrentParamSize();
344 CHECK_EQ(param_size, jni_conv->CurrentParamSize());
345 jni_asm->Store(out_off, in_reg, param_size);
346 }
347 }
348}
349
Ian Rogersb033c752011-07-20 12:22:35 -0700350void* JniCompiler::AllocateCode(size_t size) {
351 CHECK_LT(((jni_code_top_ - jni_code_) + size), jni_code_size_);
352 void *result = jni_code_top_;
353 jni_code_top_ += size;
354 return result;
355}
356
357JniCompiler::JniCompiler() {
358 // TODO: this shouldn't be managed by the JniCompiler, we should have a
359 // code cache.
360 jni_code_size_ = 4096;
361 jni_code_ = static_cast<byte*>(mmap(NULL, jni_code_size_,
362 PROT_READ | PROT_WRITE | PROT_EXEC,
363 MAP_ANONYMOUS | MAP_PRIVATE, -1, 0));
364 CHECK_NE(MAP_FAILED, jni_code_);
365 jni_code_top_ = jni_code_;
366}
367
368JniCompiler::~JniCompiler() {
369 // TODO: this shouldn't be managed by the JniCompiler, we should have a
370 // code cache.
371 CHECK_EQ(0, munmap(jni_code_, jni_code_size_));
372}
373
374} // namespace art