Beispiel #1
0
MonoPIFunc
mono_arch_create_trampoline (MonoMethodSignature *sig, gboolean string_ctor)
{
	guint8 *p, *code_buffer;
	size_data sz;

	DEBUG (printf ("\nPInvoke [start emiting]\n"));
	calculate_sizes (sig, &sz, string_ctor);

	p = code_buffer = alloc_code_memory (sz.code_size);
	p = emit_prolog (p, sig, &sz);
	p = emit_save_parameters (p, sig, &sz);
	p = emit_call_and_store_retval (p, sig, &sz, string_ctor);
	p = emit_epilog (p, sig, &sz);

#ifdef NEED_MPROTECT
	if (mprotect (code_buffer, 1024, PROT_READ | PROT_WRITE | PROT_EXEC)) {
		g_error ("Cannot mprotect trampoline\n");
	}
#endif

	DEBUG (printf ("emited code size: %d\n", p - code_buffer));

	DEBUG (printf ("PInvoke [end emiting]\n"));

	return (MonoPIFunc) code_buffer;
}
Beispiel #2
0
MonoPIFunc
mono_arch_create_trampoline (MonoMethodSignature *sig, gboolean string_ctor)
{
	guint32 *p, *code_buffer;
	guint stack_size, code_size, i;
	gboolean use_memcpy = FALSE;
	static GHashTable *cache = NULL;
	MonoPIFunc res;

	if (!cache)
		cache = g_hash_table_new ((GHashFunc)mono_signature_hash,
				 (GCompareFunc)mono_metadata_signature_equal);
	
	if ((res = (MonoPIFunc)g_hash_table_lookup(cache, sig)))
		return res;

	calculate_sizes (sig, &stack_size, &code_size, 
			 string_ctor, &use_memcpy);

	p = code_buffer = alloc_code_memory (code_size);
	p = emit_prolog (p, sig, stack_size);
	p = emit_save_parameters (p, sig, stack_size, use_memcpy);
	p = emit_call_and_store_retval (p, sig, stack_size, string_ctor);
	/* we don't return structs here so pass in NULL as signature */
	p = emit_epilog (p, NULL, stack_size);

	g_assert(p <= code_buffer + (code_size / 4));
	
	DEBUG(sparc_disassemble_code (code_buffer, p, sig_to_name(sig, NULL)));

	/* So here's the deal...
	 * UltraSPARC will flush a whole cache line at a time
	 * BUT, older SPARCs won't.
	 * So, be compatable and flush dwords at a time...
	 */

	for (i = 0; i < ((p - code_buffer)/2); i++)
		flushi((code_buffer + (i*8)));

	g_hash_table_insert(cache, sig, code_buffer);

	return (MonoPIFunc)code_buffer;
}
Beispiel #3
0
void *
mono_arch_create_method_pointer (MonoMethod *method)
{
	MonoMethodSignature *sig;
	MonoJitInfo *ji;
	guint stack_size, code_size, stackval_arg_pos, local_pos;
	guint i, local_start, reg_param = 0, stack_param, cpos, vt_cur;
	guint32 align = 0;
	guint32 *p, *code_buffer;
	gint *vtbuf;
	gint32 simpletype;

	code_size = 1024; /* these should be calculated... */
	stack_size = 1024;
	stack_param = 0;

	sig = method->signature;

	p = code_buffer = g_malloc (code_size);

	DEBUG(fprintf(stderr, "Delegate [start emiting] %s\n", method->name));
	DEBUG(fprintf(stderr, "%s\n", sig_to_name(sig, FALSE)));

	p = emit_prolog (p, sig, stack_size);

	/* fill MonoInvocation */
	sparc_st_imm_ptr (p, sparc_g0, sparc_sp,
		  (MINV_POS + G_STRUCT_OFFSET (MonoInvocation, ex)));
	sparc_st_imm_ptr (p, sparc_g0, sparc_sp,
		  (MINV_POS + G_STRUCT_OFFSET (MonoInvocation, ex_handler)));
	sparc_st_imm_ptr (p, sparc_g0, sparc_sp,
		  (MINV_POS + G_STRUCT_OFFSET (MonoInvocation, parent)));

	sparc_set_ptr (p, (void *)method, sparc_l0);
	sparc_st_imm_ptr (p, sparc_l0, sparc_sp,
		  (MINV_POS + G_STRUCT_OFFSET (MonoInvocation, method)));

	stackval_arg_pos = MINV_POS + sizeof (MonoInvocation);
	local_start = local_pos = stackval_arg_pos + (sig->param_count + 1) * sizeof (stackval);

	if (sig->hasthis) {
		sparc_st_imm_ptr (p, sparc_i0, sparc_sp,
			  (MINV_POS + G_STRUCT_OFFSET (MonoInvocation, obj)));
		reg_param = 1;
	} 

	if (sig->param_count) {
		gint save_count = MIN (OUT_REGS, sig->param_count + sig->hasthis);
		for (i = reg_param; i < save_count; i++) {
			sparc_st_imm_ptr (p, sparc_i0 + i, sparc_sp, local_pos);
			local_pos += SLOT_SIZE;
		}
	}

	/* prepare space for valuetypes */
        vt_cur = local_pos;
        vtbuf = alloca (sizeof(int)*sig->param_count);
        cpos = 0;
        for (i = 0; i < sig->param_count; i++) {
                MonoType *type = sig->params [i];
                vtbuf [i] = -1;
                if (!sig->params[i]->byref && type->type == MONO_TYPE_VALUETYPE) {
                        MonoClass *klass = type->data.klass;
                        gint size;
			
                        if (klass->enumtype)
                                continue;
                        size = mono_class_native_size (klass, &align);
                        cpos += align - 1;
                        cpos &= ~(align - 1);
                        vtbuf [i] = cpos;
                        cpos += size;
                }
        }
        cpos += SLOT_SIZE - 1;
        cpos &= ~(SLOT_SIZE - 1);
	
	local_pos += cpos;
	
	/* set MonoInvocation::stack_args */
	sparc_add_imm (p, 0, sparc_sp, stackval_arg_pos, sparc_l0);
	sparc_st_imm_ptr (p, sparc_l0, sparc_sp,
		  (MINV_POS + G_STRUCT_OFFSET (MonoInvocation, stack_args)));

	/* add stackval arguments */
	for (i=0; i < sig->param_count; i++) {
		int stack_offset;
		int type;
		if (reg_param < OUT_REGS) {
			stack_offset = local_start + i * SLOT_SIZE;
			reg_param++;
		} else {
			stack_offset = stack_size + 8 + stack_param;
			stack_param++;
		}

		if (!sig->params[i]->byref) {
			type = sig->params[i]->type;
		enum_arg:
			switch (type) {
			case MONO_TYPE_I8:
			case MONO_TYPE_U8:
			case MONO_TYPE_I:
			case MONO_TYPE_U:
			case MONO_TYPE_STRING:
			case MONO_TYPE_OBJECT:
			case MONO_TYPE_CLASS:
			case MONO_TYPE_SZARRAY:
			case MONO_TYPE_PTR:
			case MONO_TYPE_R8:
				break;
			case MONO_TYPE_I4:
			case MONO_TYPE_U4:
				stack_offset += SLOT_SIZE - 4;
				break;
			case MONO_TYPE_CHAR:
			case MONO_TYPE_I2:
			case MONO_TYPE_U2:
				stack_offset += SLOT_SIZE - 2;
				break;
			case MONO_TYPE_I1:
			case MONO_TYPE_U1:
			case MONO_TYPE_BOOLEAN:
				stack_offset += SLOT_SIZE - 1;
				break;
	                case MONO_TYPE_VALUETYPE:
	                        if (sig->params[i]->data.klass->enumtype) {
	                                type = sig->params[i]->data.klass->enum_basetype->type;
	                                goto enum_arg;
	                        }
				g_assert(vtbuf[i] >= 0);
	                        break;
			default:
				g_error ("can not cope with delegate arg type %d", type);
			}
		}
	
		sparc_add_imm (p, 0, sparc_sp, stack_offset, sparc_o2);

		if (vtbuf[i] >= 0) {
			sparc_add_imm (p, 0, sparc_sp, vt_cur, sparc_o1);
			sparc_st_imm_ptr (p, sparc_o1, sparc_sp, stackval_arg_pos);
			sparc_add_imm (p, 0, sparc_sp, stackval_arg_pos, 
				       sparc_o1);
			sparc_ld_imm_ptr (p, sparc_o2, 0, sparc_o2);
			vt_cur += vtbuf[i];
		} else {
			sparc_add_imm (p, 0, sparc_sp, stackval_arg_pos, 
				       sparc_o1);
		}

		sparc_set_ptr (p, (void *)sig->params[i], sparc_o0);
		sparc_set (p, (guint32)sig->pinvoke, sparc_o3);

		/* YOU make the CALL! */
		sparc_set_ptr (p, (void *)stackval_from_data, sparc_l0);
		sparc_jmpl_imm (p, sparc_l0, 0, sparc_callsite);
		sparc_nop (p);
                stackval_arg_pos += sizeof(stackval);
	}

	/* return value storage */
	/* Align to dword */
	stackval_arg_pos = (stackval_arg_pos + (8 - 1)) & (~(8 -1));
	if (sig->param_count) {
		sparc_add_imm (p, 0, sparc_sp, stackval_arg_pos, sparc_l0);
	}
	if (!sig->ret->byref && sig->ret->type == MONO_TYPE_VALUETYPE && !sig->ret->data.klass->enumtype) {
#if !SPARCV9
		/* pass on callers buffer */
		sparc_ld_imm_ptr (p, sparc_fp, 64, sparc_l1);
		sparc_st_imm_ptr (p, sparc_l1, sparc_l0, 0);
#else
		sparc_add_imm (p, 0, sparc_l0, sizeof(stackval), sparc_l1);
		sparc_st_imm_ptr (p, sparc_l1, sparc_l0, 0);
#endif
	}

	sparc_st_imm_ptr (p, sparc_l0, sparc_sp,
		  (MINV_POS + G_STRUCT_OFFSET (MonoInvocation, retval)));

	/* call ves_exec_method */
	sparc_add_imm (p, 0, sparc_sp, MINV_POS, sparc_o0);
	sparc_set_ptr (p, (void *)ves_exec_method, sparc_l0);
	sparc_jmpl_imm (p, sparc_l0, 0, sparc_callsite);
	sparc_nop (p);

	/* move retval from stackval to proper place (r3/r4/...) */
        if (sig->ret->byref) {
		sparc_ld_imm_ptr (p, sparc_sp, stackval_arg_pos, sparc_i0 );
        } else {
        enum_retvalue:
                switch (sig->ret->type) {
                case MONO_TYPE_VOID:
                        break;
                case MONO_TYPE_BOOLEAN:
                case MONO_TYPE_I1:
                case MONO_TYPE_U1:
                case MONO_TYPE_I2:
                case MONO_TYPE_U2:
                case MONO_TYPE_I4:
                case MONO_TYPE_U4:
                        sparc_ld_imm (p, sparc_sp, stackval_arg_pos, sparc_i0);
                        break;
                case MONO_TYPE_I:
                case MONO_TYPE_U:
                case MONO_TYPE_OBJECT:
		case MONO_TYPE_STRING:
                case MONO_TYPE_CLASS:
                        sparc_ld_imm_ptr (p, sparc_sp, stackval_arg_pos, sparc_i0);
                        break;
                case MONO_TYPE_I8:
                case MONO_TYPE_U8:
#if SPARCV9
                        sparc_ldx_imm (p, sparc_sp, stackval_arg_pos, sparc_i0);
#else
                        sparc_ld_imm (p, sparc_sp, stackval_arg_pos, sparc_i0);
                        sparc_ld_imm (p, sparc_sp, stackval_arg_pos + 4, sparc_i1);
#endif
                        break;
                case MONO_TYPE_R4:
                        sparc_lddf_imm (p, sparc_sp, stackval_arg_pos, sparc_f0);
			sparc_fdtos(p, sparc_f0, sparc_f0);
                        break;
                case MONO_TYPE_R8:
                        sparc_lddf_imm (p, sparc_sp, stackval_arg_pos, sparc_f0);
                        break;
                case MONO_TYPE_VALUETYPE: {
			gint size;
			gint reg = sparc_i0;
                        if (sig->ret->data.klass->enumtype) {
                                simpletype = sig->ret->data.klass->enum_basetype->type;
                                goto enum_retvalue;
                        }
#if SPARCV9
                        size = mono_class_native_size (sig->ret->data.klass, NULL);
                        sparc_ldx_imm (p, sparc_sp, stackval_arg_pos, sparc_l0);
			if (size <= 16) {
				gint off = 0;
				if (size >= 8) {
		                        sparc_ldx_imm (p, sparc_l0, 0, reg);
					size -= 8;
					off += 8;
					reg++;
				}
				if (size > 0)
		                        sparc_ldx_imm (p, sparc_l0, off, reg);
			} else
	                        NOT_IMPL("value type as ret val from delegate");
#endif
                        break;
		}
                default: 
			g_error ("Type 0x%x not handled yet in thunk creation",
				 sig->ret->type);
                        break;
                }
        }

	p = emit_epilog (p, sig, stack_size);

	for (i = 0; i < ((p - code_buffer)/2); i++)
		flushi((code_buffer + (i*8)));
	
	ji = g_new0 (MonoJitInfo, 1);
        ji->method = method;
        ji->code_size = p - code_buffer;
	ji->code_start = code_buffer;
	
	mono_jit_info_table_add (mono_get_root_domain (), ji);

	DEBUG(sparc_disassemble_code (code_buffer, p, method->name));

	DEBUG(fprintf(stderr, "Delegate [end emiting] %s\n", method->name));

	return ji->code_start;
}
Beispiel #4
0
// Allocates memory (parameter(), literal(), emit_epilog, emit_with_literal)
void quotation_jit::iterate_quotation() {
  bool stack_frame = stack_frame_p();

  set_position(0);

  if (stack_frame) {
    emit(parent->special_objects[JIT_SAFEPOINT]);
    emit(parent->special_objects[JIT_PROLOG]);
  }

  cell length = array_capacity(elements.untagged());
  bool tail_call = false;

  for (cell i = 0; i < length; i++) {
    set_position(i);
    data_root<object> obj(nth(i), parent);

    switch (obj.type()) {
      case WORD_TYPE:
        // Sub-primitives
        if (to_boolean(obj.as<word>()->subprimitive)) {
          tail_call = emit_subprimitive(obj.value(),     // word
                                        i == length - 1, // tail_call_p
                                        stack_frame);    // stack_frame_p
        }                                                // Everything else
        else if (i == length - 1) {
          emit_epilog(stack_frame);
          tail_call = true;
          word_jump(obj.value());
        } else
          word_call(obj.value());
        break;
      case WRAPPER_TYPE:
        push(obj.as<wrapper>()->object);
        break;
      case BYTE_ARRAY_TYPE:
        // Primitive calls
        if (primitive_call_p(i, length)) {
// On x86-64 and PowerPC, the VM pointer is stored in a register;
// on other platforms, the RT_VM relocation is used and it needs
// an offset parameter
#ifdef FACTOR_X86
          parameter(tag_fixnum(0));
#endif
          parameter(obj.value());
          parameter(false_object);
#ifdef FACTOR_PPC_TOC
          parameter(obj.value());
          parameter(false_object);
#endif
          emit(parent->special_objects[JIT_PRIMITIVE]);

          i++;
        } else
          push(obj.value());
        break;
      case QUOTATION_TYPE:
        // 'if' preceded by two literal quotations (this is why if and ? are
        // mutually recursive in the library, but both still work)
        if (fast_if_p(i, length)) {
          emit_epilog(stack_frame);
          tail_call = true;
          emit_quotation(nth(i));
          emit_quotation(nth(i + 1));
          emit(parent->special_objects[JIT_IF]);
          i += 2;
        } // dip
        else if (fast_dip_p(i, length)) {
          emit_quotation(obj.value());
          emit(parent->special_objects[JIT_DIP]);
          i++;
        } // 2dip
        else if (fast_2dip_p(i, length)) {
          emit_quotation(obj.value());
          emit(parent->special_objects[JIT_2DIP]);
          i++;
        } // 3dip
        else if (fast_3dip_p(i, length)) {
          emit_quotation(obj.value());
          emit(parent->special_objects[JIT_3DIP]);
          i++;
        } else
          push(obj.value());
        break;
      case ARRAY_TYPE:
        // Method dispatch
        if (mega_lookup_p(i, length)) {
          tail_call = true;
          emit_mega_cache_lookup(nth(i), untag_fixnum(nth(i + 1)), nth(i + 2));
          i += 3;
        } // Non-optimizing compiler ignores declarations
        else if (declare_p(i, length))
          i++;
        else
          push(obj.value());
        break;
      default:
        push(obj.value());
        break;
    }
  }

  if (!tail_call) {
    set_position(length);
    emit_epilog(stack_frame);
    emit(parent->special_objects[JIT_RETURN]);
  }
}
Beispiel #5
0
MonoPIFunc
mono_arch_create_trampoline (MonoMethodSignature *sig, gboolean string_ctor)
{
	unsigned int *p;
	unsigned int *buffer;
	MonoType* param;

	int i, pos;
	int alpharegs;
	int hasthis;
	int STACK_SIZE;
	int BUFFER_SIZE;
	int simple_type;
	int regbase;
	
	// Set up basic stuff.  like has this.	
	hasthis = !!sig->hasthis;
	alpharegs = AXP_GENERAL_REGS - hasthis;
	regbase  = hasthis?alpha_a1:alpha_a0 ;
	
	// Make a ballpark estimate for now.
	calculate_size( sig, &BUFFER_SIZE, &STACK_SIZE );
	
	// convert to the correct number of bytes.
	BUFFER_SIZE = BUFFER_SIZE * 4;

	
	// allocate.	
	buffer = p = (unsigned int *)malloc(BUFFER_SIZE);
	memset( buffer, 0, BUFFER_SIZE );
	pos = 8 * (sig->param_count - alpharegs - 1);
	
	// Ok, start creating this thing.
	p = emit_prolog( p, STACK_SIZE, hasthis );

	// copy everything into the correct register/stack space
	for (i = sig->param_count; --i >= 0; ) 
	{
		param = sig->params [i];
		
		if( param->byref )
		{
			if( i >= alpharegs )
			{
				// load into temp register, then store on the stack 
				alpha_ldq( p, alpha_t1, alpha_t0, ARG_LOC( i ));
				alpha_stq( p, alpha_t1, alpha_sp, pos );
				pos -= 8;
			}
			else
			{
				// load into register
				alpha_ldq( p, (regbase + i), alpha_t0, ARG_LOC( i ) );
			}
		}
		else
		{
			simple_type = param->type;
			if( simple_type == MONO_TYPE_VALUETYPE )
			{
                        	if (param->data.klass->enumtype)
                                	simple_type = param->data.klass->enum_basetype->type;
                        }
			
			switch (simple_type) 
			{
			case MONO_TYPE_VOID:
				break;
			case MONO_TYPE_BOOLEAN:
			case MONO_TYPE_CHAR:
			case MONO_TYPE_I1:
			case MONO_TYPE_U1:
			case MONO_TYPE_I2:
			case MONO_TYPE_U2:
			case MONO_TYPE_I4:
			case MONO_TYPE_U4:
				// 4 bytes - need to sign-extend (stackvals are not extended)
				if( i >= alpharegs )
				{
					// load into temp register, then store on the stack
					alpha_ldl( p, alpha_t1, alpha_t0, ARG_LOC( i ) );
					alpha_stq( p, alpha_t1, alpha_sp, pos );
					pos -= 8;
				}
				else
				{
					// load into register
					alpha_ldl( p, (regbase + i), alpha_t0, (ARG_LOC(i)) );
				}
				break;
			case MONO_TYPE_I:
			case MONO_TYPE_U:
			case MONO_TYPE_PTR:
			case MONO_TYPE_CLASS:
			case MONO_TYPE_OBJECT:
			case MONO_TYPE_SZARRAY:
			case MONO_TYPE_STRING:
			case MONO_TYPE_I8:
				// 8 bytes
				if( i >= alpharegs )
				{
					// load into temp register, then store on the stack
					alpha_ldq( p, alpha_t1, alpha_t0, ARG_LOC( i ) );
					alpha_stq( p, alpha_t1, alpha_sp, pos );
					pos -= 8;
				}
				else
				{
					// load into register
					alpha_ldq( p, (regbase + i), alpha_t0, ARG_LOC(i) );
				}
				break;
			case MONO_TYPE_R4:
			case MONO_TYPE_R8:
				/*
				// floating point... Maybe this does the correct thing.
				if( i > alpharegs )
				{
					alpha_ldq( p, alpha_t1, alpha_t0, ARG_LOC( i ) );
					alpha_cpys( p, alpha_ft1, alpha_ft1, alpha_ft2 );
					alpha_stt( p, alpha_ft2, alpha_sp, pos );
					pos -= 8;
				}
				else
				{
					alpha_ldq( p, alpha_t1, alpha_t0, ARG_LOC(i) );
					alpha_cpys( p, alpha_ft1, alpha_ft1, alpha_fa0 + i + hasthis );
				}
				break;
				*/
			case MONO_TYPE_VALUETYPE:
				g_error ("Not implemented: ValueType as parameter to delegate." );
				break;
			default:
				g_error( "Not implemented: 0x%x.", simple_type );
				break;	
			}
		}
	}
	
	// Now call the function and store the return parameter.
	p = emit_call( p, STACK_SIZE );
	p = emit_store_return_default( p, STACK_SIZE );
	p = emit_epilog( p, STACK_SIZE );

	if( p > buffer + BUFFER_SIZE )
		g_error( "Buffer overflow: got 0x%lx, expected <=0x%x.", (long)(p-buffer), BUFFER_SIZE );

	/* flush instruction cache to see trampoline code */
	asm volatile("imb":::"memory");
	
	return (MonoPIFunc)buffer;
}
Beispiel #6
0
/* Allocates memory (parameter(), literal(), emit_epilog, emit_with_literal)*/
void quotation_jit::iterate_quotation() {
  bool no_non_safepoint_words = no_non_safepoint_words_p();

  set_position(0);

  if (no_non_safepoint_words) {
    emit(parent->special_objects[JIT_SAFEPOINT]);
    emit(parent->special_objects[JIT_PROLOG]);
  }

  cell length = array_capacity(elements.untagged());
  bool tail_call = false;

  for (cell i = 0; i < length; i++) {
    set_position(i);

    data_root<object> obj(array_nth(elements.untagged(), i), parent);

    switch (obj.type()) {
      case WORD_TYPE:
        /* Sub-primitives */
        if (to_boolean(obj.as<word>()->subprimitive)) {
          tail_call = emit_subprimitive(obj.value(),     /* word */
                                        i == length - 1, /* tail_call_p */
                                        no_non_safepoint_words);    /* stack_frame_p */
        }                                                /* Everything else */
        else if (i == length - 1) {
          emit_epilog(no_non_safepoint_words);
          tail_call = true;
          word_jump(obj.value());
        } else
          word_call(obj.value());
        break;
      case WRAPPER_TYPE:
        push(obj.as<wrapper>()->object);
        break;
      case BYTE_ARRAY_TYPE:
        /* Primitive calls */
        if (primitive_call_p(i, length)) {
/* On x86-64 and PowerPC, the VM pointer is stored in
   a register; on other platforms, the RT_VM relocation
   is used and it needs an offset parameter */
#ifdef FACTOR_X86
          parameter(tag_fixnum(0));
#endif
          parameter(obj.value());
          parameter(false_object);
#ifdef FACTOR_PPC_TOC
          parameter(obj.value());
          parameter(false_object);
#endif
          emit(parent->special_objects[JIT_PRIMITIVE]);

          i++;
        } else
          push(obj.value());
        break;
      case QUOTATION_TYPE:
        /* 'if' preceded by two literal quotations (this is why if and ? are
           mutually recursive in the library, but both still work) */
        if (fast_if_p(i, length)) {
          emit_epilog(no_non_safepoint_words);
          tail_call = true;

          emit_quotation(array_nth(elements.untagged(), i));
          emit_quotation(array_nth(elements.untagged(), i + 1));
          emit(parent->special_objects[JIT_IF]);

          i += 2;
        } /* dip */
        else if (fast_dip_p(i, length)) {
          emit_quotation(obj.value());
          emit(parent->special_objects[JIT_DIP]);
          i++;
        } /* 2dip */
        else if (fast_2dip_p(i, length)) {
          emit_quotation(obj.value());
          emit(parent->special_objects[JIT_2DIP]);
          i++;
        } /* 3dip */
        else if (fast_3dip_p(i, length)) {
          emit_quotation(obj.value());
          emit(parent->special_objects[JIT_3DIP]);
          i++;
        } else
          push(obj.value());
        break;
      case ARRAY_TYPE:
        /* Method dispatch */
        if (mega_lookup_p(i, length)) {
          fixnum index = untag_fixnum(array_nth(elements.untagged(), i + 1));
          /* Load the object from the datastack, then remove our stack frame. */
          emit_with_literal(parent->special_objects[PIC_LOAD],
                            tag_fixnum(-index * sizeof(cell)));
          emit_epilog(no_non_safepoint_words);
          tail_call = true;

          emit_mega_cache_lookup(array_nth(elements.untagged(), i), index,
                                 array_nth(elements.untagged(), i + 2));
          i += 3;
        } /* Non-optimizing compiler ignores declarations */
        else if (declare_p(i, length))
          i++;
        else
          push(obj.value());
        break;
      default:
        push(obj.value());
        break;
    }
  }

  if (!tail_call) {
    set_position(length);
    emit_epilog(no_non_safepoint_words);
    emit(parent->special_objects[JIT_RETURN]);
  }
}