Exemple #1
0
static void pointer_alloc(compile_t* c, reach_type_t* t,
  reach_type_t* t_elem)
{
  FIND_METHOD("_alloc");

  LLVMTypeRef params[2];
  params[0] = t->use_type;
  params[1] = c->intptr;
  start_function(c, m, t->use_type, params, 2);

  // Set up a constant integer for the allocation size.
  size_t size = (size_t)LLVMABISizeOfType(c->target_data, t_elem->use_type);
  LLVMValueRef l_size = LLVMConstInt(c->intptr, size, false);

  LLVMValueRef len = LLVMGetParam(m->func, 1);
  LLVMValueRef args[2];
  args[0] = codegen_ctx(c);
  args[1] = LLVMBuildMul(c->builder, len, l_size, "");

  LLVMValueRef result = gencall_runtime(c, "pony_alloc", args, 2, "");
  result = LLVMBuildBitCast(c->builder, result, t->use_type, "");

  LLVMBuildRet(c->builder, result);
  codegen_finishfun(c);
}
Exemple #2
0
static void donotoptimise_apply(compile_t* c, reach_type_t* t,
  reach_method_t* m)
{
  const char* strtab_name = m->name;
  m->intrinsic = true;

  ast_t* typearg = ast_child(m->typeargs);
  reach_type_t* t_elem = reach_type(c->reach, typearg);
  LLVMTypeRef params[2];
  params[0] = t->use_type;
  params[1] = t_elem->use_type;

  start_function(c, m, m->result->use_type, params, 2);

  LLVMValueRef obj = LLVMGetParam(m->func, 1);
  LLVMTypeRef void_fn = LLVMFunctionType(c->void_type, &t_elem->use_type, 1,
    false);
  LLVMValueRef asmstr = LLVMConstInlineAsm(void_fn, "", "imr,~{memory}", true,
    false);
  LLVMBuildCall(c->builder, asmstr, &obj, 1, "");

  LLVMBuildRet(c->builder, m->result->instance);
  codegen_finishfun(c);

  BOX_FUNCTION();
}
Exemple #3
0
static void pointer_offset(compile_t* c, reach_type_t* t, reach_type_t* t_elem)
{
  FIND_METHOD("_offset");

  LLVMTypeRef params[3];
  params[0] = t->use_type;
  params[1] = c->intptr;
  start_function(c, m, t->use_type, params, 2);

  // Set up a constant integer for the allocation size.
  size_t size = (size_t)LLVMABISizeOfType(c->target_data, t_elem->use_type);
  LLVMValueRef l_size = LLVMConstInt(c->intptr, size, false);

  LLVMValueRef ptr = LLVMGetParam(m->func, 0);
  LLVMValueRef n = LLVMGetParam(m->func, 1);

  // Return ptr + (n * sizeof(len)).
  LLVMValueRef src = LLVMBuildPtrToInt(c->builder, ptr, c->intptr, "");
  LLVMValueRef offset = LLVMBuildMul(c->builder, n, l_size, "");
  LLVMValueRef result = LLVMBuildAdd(c->builder, src, offset, "");
  result = LLVMBuildIntToPtr(c->builder, result, t->use_type, "");

  LLVMBuildRet(c->builder, result);
  codegen_finishfun(c);

  BOX_FUNCTION();
}
Exemple #4
0
static tree
start_cdtor (int method_type)
{
  tree fnname = get_file_function_name (method_type);
  tree void_list_node_1 = build_tree_list (NULL_TREE, void_type_node);
  tree body;

  start_function (void_list_node_1,
		  build_nt (CALL_EXPR, fnname,
			    tree_cons (NULL_TREE, NULL_TREE, void_list_node_1),
			    NULL_TREE),
		  NULL_TREE);
  store_parm_decls ();

  current_function_cannot_inline
    = "static constructors and destructors cannot be inlined";

  body = c_begin_compound_stmt ();

  pushlevel (0);
  clear_last_expr ();
  add_scope_stmt (/*begin_p=*/1, /*partial_p=*/0);

  return body;
}
Exemple #5
0
static void maybe_none(compile_t* c, reach_type_t* t)
{
  FIND_METHOD("none");
  start_function(c, m, t->use_type, &t->use_type, 1);

  LLVMBuildRet(c->builder, LLVMConstNull(t->use_type));
  codegen_finishfun(c);
}
Exemple #6
0
static void pointer_unsafe(compile_t* c, reach_type_t* t)
{
  FIND_METHOD("_unsafe");
  start_function(c, m, t->use_type, &t->use_type, 1);

  LLVMBuildRet(c->builder, LLVMGetParam(m->func, 0));
  codegen_finishfun(c);
}
Exemple #7
0
static void number_conversion(compile_t* c, num_conv_t* from, num_conv_t* to,
  bool native128)
{
  if(!native128 &&
    ((from->is_float && (to->size > 64)) ||
    (to->is_float && (from->size > 64)))
    )
  {
    return;
  }

  reach_type_t* t = reach_type_name(c->reach, from->type_name);

  if(t == NULL)
    return;

  FIND_METHOD(to->fun_name);
  start_function(c, m, to->type, &from->type, 1);

  LLVMValueRef arg = LLVMGetParam(m->func, 0);
  LLVMValueRef result;

  if(from->is_float)
  {
    if(to->is_float)
    {
      if(from->size < to->size)
        result = LLVMBuildFPExt(c->builder, arg, to->type, "");
      else if(from->size > to->size)
        result = LLVMBuildFPTrunc(c->builder, arg, to->type, "");
      else
        result = arg;
    } else if(to->is_signed) {
      result = LLVMBuildFPToSI(c->builder, arg, to->type, "");
    } else {
      result = LLVMBuildFPToUI(c->builder, arg, to->type, "");
    }
  } else if(to->is_float) {
    if(from->is_signed)
      result = LLVMBuildSIToFP(c->builder, arg, to->type, "");
    else
      result = LLVMBuildUIToFP(c->builder, arg, to->type, "");
  } else if(from->size > to->size) {
      result = LLVMBuildTrunc(c->builder, arg, to->type, "");
  } else if(from->size < to->size) {
    if(from->is_signed)
      result = LLVMBuildSExt(c->builder, arg, to->type, "");
    else
      result = LLVMBuildZExt(c->builder, arg, to->type, "");
  } else {
    result = arg;
  }

  LLVMBuildRet(c->builder, result);
  codegen_finishfun(c);

  BOX_FUNCTION();
}
Exemple #8
0
static void f64__nan(compile_t* c, reach_type_t* t)
{
  FIND_METHOD("_nan");
  start_function(c, m, c->f64, &c->f64, 1);

  LLVMValueRef result = LLVMConstNaN(c->f64);
  LLVMBuildRet(c->builder, result);
  codegen_finishfun(c);
}
Exemple #9
0
static void pointer_create(compile_t* c, reach_type_t* t)
{
  FIND_METHOD("create");
  start_function(c, m, t->use_type, &t->use_type, 1);

  LLVMValueRef result = LLVMConstNull(t->use_type);

  LLVMBuildRet(c->builder, result);
  codegen_finishfun(c);
}
Exemple #10
0
static void platform_debug(compile_t* c, reach_type_t* t)
{
  FIND_METHOD("debug");
  start_function(c, m, c->ibool, &t->use_type, 1);

  LLVMValueRef result = LLVMConstInt(c->ibool, !c->opt->release, false);
  LLVMBuildRet(c->builder, result);
  codegen_finishfun(c);

  BOX_FUNCTION();
}
Exemple #11
0
void start_built_in_module(const char* name, 
                           const char* author, 
                           const char* description,
                           UInt32 version,
                           UInt32 compat,
                           void(*start_function)(void))
{
    start_function();
    // Notify the module system that this module really exists, specificaly, let other module link with it
    module_loaded(name, author, description, version, compat);
}
Exemple #12
0
static void pointer_usize(compile_t* c, reach_type_t* t)
{
  FIND_METHOD("usize");
  start_function(c, m, c->intptr, &t->use_type, 1);

  LLVMValueRef ptr = LLVMGetParam(m->func, 0);
  LLVMValueRef result = LLVMBuildPtrToInt(c->builder, ptr, c->intptr, "");

  LLVMBuildRet(c->builder, result);
  codegen_finishfun(c);
}
Exemple #13
0
static void f64_bits(compile_t* c, reach_type_t* t)
{
  FIND_METHOD("bits");
  start_function(c, m, c->i64, &c->f64, 1);

  LLVMValueRef result = LLVMBuildBitCast(c->builder, LLVMGetParam(m->func, 0),
    c->i64, "");
  LLVMBuildRet(c->builder, result);
  codegen_finishfun(c);

  BOX_FUNCTION();
}
Exemple #14
0
static void platform_x86(compile_t* c, reach_type_t* t)
{
  FIND_METHOD("x86");
  start_function(c, m, c->ibool, &t->use_type, 1);

  LLVMValueRef result =
    LLVMConstInt(c->ibool, target_is_x86(c->opt->triple), false);
  LLVMBuildRet(c->builder, result);
  codegen_finishfun(c);

  BOX_FUNCTION();
}
Exemple #15
0
static void maybe_create(compile_t* c, reach_type_t* t, reach_type_t* t_elem)
{
  FIND_METHOD("create");

  LLVMTypeRef params[2];
  params[0] = t->use_type;
  params[1] = t_elem->use_type;
  start_function(c, m, t->use_type, params, 2);

  LLVMValueRef param = LLVMGetParam(m->func, 1);
  LLVMValueRef result = LLVMBuildBitCast(c->builder, param, t->use_type, "");
  LLVMBuildRet(c->builder, result);
  codegen_finishfun(c);
}
Exemple #16
0
static void f64_from_bits(compile_t* c, reach_type_t* t)
{
  FIND_METHOD("from_bits");

  LLVMTypeRef params[2];
  params[0] = c->f64;
  params[1] = c->i64;
  start_function(c, m, c->f64, params, 2);

  LLVMValueRef result = LLVMBuildBitCast(c->builder, LLVMGetParam(m->func, 1),
    c->f64, "");
  LLVMBuildRet(c->builder, result);
  codegen_finishfun(c);
}
Exemple #17
0
static void maybe_is_none(compile_t* c, reach_type_t* t)
{
  // Returns true if the receiver is null.
  FIND_METHOD("is_none");
  start_function(c, m, c->ibool, &t->use_type, 1);

  LLVMValueRef receiver = LLVMGetParam(m->func, 0);
  LLVMValueRef test = LLVMBuildIsNull(c->builder, receiver, "");
  LLVMValueRef value = LLVMBuildZExt(c->builder, test, c->ibool, "");

  LLVMBuildRet(c->builder, value);
  codegen_finishfun(c);

  BOX_FUNCTION();
}
Exemple #18
0
static void donotoptimise_observe(compile_t* c, reach_type_t* t)
{
  FIND_METHOD("observe");

  start_function(c, m, m->result->use_type, &t->use_type, 1);

  LLVMTypeRef void_fn = LLVMFunctionType(c->void_type, NULL, 0, false);
  LLVMValueRef asmstr = LLVMConstInlineAsm(void_fn, "", "~{memory}", true,
    false);
  LLVMBuildCall(c->builder, asmstr, NULL, 0, "");

  LLVMBuildRet(c->builder, m->result->instance);
  codegen_finishfun(c);

  BOX_FUNCTION();
}
Exemple #19
0
static void pointer_delete(compile_t* c, reach_type_t* t, reach_type_t* t_elem)
{
  FIND_METHOD("_delete");

  LLVMTypeRef params[3];
  params[0] = t->use_type;
  params[1] = c->intptr;
  params[2] = c->intptr;
  start_function(c, m, t_elem->use_type, params, 3);

  // Set up a constant integer for the allocation size.
  size_t size = (size_t)LLVMABISizeOfType(c->target_data, t_elem->use_type);
  LLVMValueRef l_size = LLVMConstInt(c->intptr, size, false);

  LLVMValueRef ptr = LLVMGetParam(m->func, 0);
  LLVMValueRef n = LLVMGetParam(m->func, 1);
  LLVMValueRef len = LLVMGetParam(m->func, 2);

  LLVMValueRef elem_ptr = LLVMBuildBitCast(c->builder, ptr,
    LLVMPointerType(t_elem->use_type, 0), "");
  LLVMValueRef result = LLVMBuildLoad(c->builder, elem_ptr, "");

  LLVMValueRef dst = LLVMBuildPtrToInt(c->builder, elem_ptr, c->intptr, "");
  LLVMValueRef offset = LLVMBuildMul(c->builder, n, l_size, "");
  LLVMValueRef src = LLVMBuildAdd(c->builder, dst, offset, "");
  LLVMValueRef elen = LLVMBuildMul(c->builder, len, l_size, "");

  LLVMValueRef args[5];
  args[0] = LLVMBuildIntToPtr(c->builder, dst, c->void_ptr, "");
  args[1] = LLVMBuildIntToPtr(c->builder, src, c->void_ptr, "");
  args[2] = elen;
  args[3] = LLVMConstInt(c->i32, 1, false);
  args[4] = LLVMConstInt(c->i1, 0, false);

  // llvm.memmove.*(ptr, ptr + (n * sizeof(elem)), len * sizeof(elem))
  if(target_is_ilp32(c->opt->triple))
  {
    gencall_runtime(c, "llvm.memmove.p0i8.p0i8.i32", args, 5, "");
  } else {
    gencall_runtime(c, "llvm.memmove.p0i8.p0i8.i64", args, 5, "");
  }

  // Return ptr[0].
  LLVMBuildRet(c->builder, result);
  codegen_finishfun(c);
}
Exemple #20
0
static void pointer_copy_to(compile_t* c, reach_type_t* t,
  reach_type_t* t_elem)
{
  FIND_METHOD("_copy_to");

  LLVMTypeRef params[3];
  params[0] = t->use_type;
  params[1] = t->use_type;
  params[2] = c->intptr;
  start_function(c, m, t->use_type, params, 3);

  // Set up a constant integer for the allocation size.
  size_t size = (size_t)LLVMABISizeOfType(c->target_data, t_elem->use_type);
  LLVMValueRef l_size = LLVMConstInt(c->intptr, size, false);

  LLVMValueRef ptr = LLVMGetParam(m->func, 0);
  LLVMValueRef ptr2 = LLVMGetParam(m->func, 1);
  LLVMValueRef n = LLVMGetParam(m->func, 2);
  LLVMValueRef elen = LLVMBuildMul(c->builder, n, l_size, "");

  LLVMValueRef args[5];
  args[0] = LLVMBuildBitCast(c->builder, ptr2, c->void_ptr, "");
  args[1] = LLVMBuildBitCast(c->builder, ptr, c->void_ptr, "");
  args[2] = elen;
  args[3] = LLVMConstInt(c->i32, 1, false);
  args[4] = LLVMConstInt(c->i1, 0, false);

  // llvm.memcpy.*(ptr2, ptr, n * sizeof(elem), 1, 0)
  if(target_is_ilp32(c->opt->triple))
  {
    gencall_runtime(c, "llvm.memcpy.p0i8.p0i8.i32", args, 5, "");
  } else {
    gencall_runtime(c, "llvm.memcpy.p0i8.p0i8.i64", args, 5, "");
  }

  LLVMBuildRet(c->builder, ptr);
  codegen_finishfun(c);

  BOX_FUNCTION();
}
Exemple #21
0
static void pointer_update(compile_t* c, reach_type_t* t, reach_type_t* t_elem)
{
  FIND_METHOD("_update");

  LLVMTypeRef params[3];
  params[0] = t->use_type;
  params[1] = c->intptr;
  params[2] = t_elem->use_type;
  start_function(c, m, t_elem->use_type, params, 3);

  LLVMValueRef ptr = LLVMGetParam(m->func, 0);
  LLVMValueRef index = LLVMGetParam(m->func, 1);

  LLVMValueRef elem_ptr = LLVMBuildBitCast(c->builder, ptr,
    LLVMPointerType(t_elem->use_type, 0), "");
  LLVMValueRef loc = LLVMBuildGEP(c->builder, elem_ptr, &index, 1, "");
  LLVMValueRef result = LLVMBuildLoad(c->builder, loc, "");
  LLVMBuildStore(c->builder, LLVMGetParam(m->func, 2), loc);

  LLVMBuildRet(c->builder, result);
  codegen_finishfun(c);
}
Exemple #22
0
static void maybe_apply(compile_t* c, reach_type_t* t, reach_type_t* t_elem)
{
  // Returns the receiver if it isn't null.
  FIND_METHOD("apply");
  start_function(c, m, t_elem->use_type, &t->use_type, 1);

  LLVMValueRef result = LLVMGetParam(m->func, 0);
  LLVMValueRef test = LLVMBuildIsNull(c->builder, result, "");

  LLVMBasicBlockRef is_false = codegen_block(c, "");
  LLVMBasicBlockRef is_true = codegen_block(c, "");
  LLVMBuildCondBr(c->builder, test, is_true, is_false);

  LLVMPositionBuilderAtEnd(c->builder, is_false);
  result = LLVMBuildBitCast(c->builder, result, t_elem->use_type, "");
  LLVMBuildRet(c->builder, result);

  LLVMPositionBuilderAtEnd(c->builder, is_true);
  gencall_throw(c);

  codegen_finishfun(c);

  BOX_FUNCTION();
}
Exemple #23
0
int start_single(void){
	sched_prepare();
	start_function(NULL);
}
Exemple #24
0
void
synthesize_method (tree fndecl)
{
  bool nested = (current_function_decl != NULL_TREE);
  tree context = decl_function_context (fndecl);
  bool need_body = true;
  tree stmt;

  if (at_eof)
    import_export_decl (fndecl);

  /* If we've been asked to synthesize a clone, just synthesize the
     cloned function instead.  Doing so will automatically fill in the
     body for the clone.  */
  if (DECL_CLONED_FUNCTION_P (fndecl))
    {
      synthesize_method (DECL_CLONED_FUNCTION (fndecl));
      return;
    }

  /* We may be in the middle of deferred access check.  Disable
     it now.  */
  push_deferring_access_checks (dk_no_deferred);

  if (! context)
    push_to_top_level ();
  else if (nested)
    push_function_context_to (context);

  /* Put the function definition at the position where it is needed,
     rather than within the body of the class.  That way, an error
     during the generation of the implicit body points at the place
     where the attempt to generate the function occurs, giving the
     user a hint as to why we are attempting to generate the
     function.  */
  DECL_SOURCE_LOCATION (fndecl) = input_location;

  interface_unknown = 1;
  start_function (NULL_TREE, fndecl, NULL_TREE, SF_DEFAULT | SF_PRE_PARSED);
  clear_last_expr ();
  stmt = begin_function_body ();

  if (DECL_OVERLOADED_OPERATOR_P (fndecl) == NOP_EXPR)
    {
      do_build_assign_ref (fndecl);
      need_body = false;
    }
  else if (DECL_CONSTRUCTOR_P (fndecl))
    {
      tree arg_chain = FUNCTION_FIRST_USER_PARMTYPE (fndecl);
      if (arg_chain != void_list_node)
	do_build_copy_constructor (fndecl);
      else if (TYPE_NEEDS_CONSTRUCTING (current_class_type))
	finish_mem_initializers (NULL_TREE);
    }

  /* If we haven't yet generated the body of the function, just
     generate an empty compound statement.  */
  if (need_body)
    {
      tree compound_stmt;
      compound_stmt = begin_compound_stmt (/*has_no_scope=*/false);
      finish_compound_stmt (compound_stmt);
    }

  finish_function_body (stmt);
  expand_or_defer_fn (finish_function (0));

  extract_interface_info ();
  if (! context)
    pop_from_top_level ();
  else if (nested)
    pop_function_context_from (context);

  pop_deferring_access_checks ();
}
Exemple #25
0
void
use_thunk (tree thunk_fndecl, bool emit_p)
{
  tree function, alias;
  tree virtual_offset;
  HOST_WIDE_INT fixed_offset, virtual_value;
  bool this_adjusting = DECL_THIS_THUNK_P (thunk_fndecl);

  /* We should have called finish_thunk to give it a name.  */
  my_friendly_assert (DECL_NAME (thunk_fndecl), 20021127);

  /* We should never be using an alias, always refer to the
     aliased thunk.  */
  my_friendly_assert (!THUNK_ALIAS (thunk_fndecl), 20031023);

  if (TREE_ASM_WRITTEN (thunk_fndecl))
    return;
  
  function = THUNK_TARGET (thunk_fndecl);
  if (DECL_RESULT (thunk_fndecl))
    /* We already turned this thunk into an ordinary function.
       There's no need to process this thunk again.  */
    return;

  if (DECL_THUNK_P (function))
    /* The target is itself a thunk, process it now.  */
    use_thunk (function, emit_p);
  
  /* Thunks are always addressable; they only appear in vtables.  */
  TREE_ADDRESSABLE (thunk_fndecl) = 1;

  /* Figure out what function is being thunked to.  It's referenced in
     this translation unit.  */
  TREE_ADDRESSABLE (function) = 1;
  mark_used (function);
  if (!emit_p)
    return;

#ifdef ASM_OUTPUT_DEF
  alias = make_alias_for_thunk (function);
#else
  alias = function;
#endif

  fixed_offset = THUNK_FIXED_OFFSET (thunk_fndecl);
  virtual_offset = THUNK_VIRTUAL_OFFSET (thunk_fndecl);

  if (virtual_offset)
    {
      if (!this_adjusting)
	virtual_offset = BINFO_VPTR_FIELD (virtual_offset);
      virtual_value = tree_low_cst (virtual_offset, /*pos=*/0);
      my_friendly_assert (virtual_value, 20021026);
    }
  else
    virtual_value = 0;
  
  /* And, if we need to emit the thunk, it's used.  */
  mark_used (thunk_fndecl);
  /* This thunk is actually defined.  */
  DECL_EXTERNAL (thunk_fndecl) = 0;
  /* The linkage of the function may have changed.  FIXME in linkage
     rewrite.  */
  TREE_PUBLIC (thunk_fndecl) = TREE_PUBLIC (function);
  DECL_VISIBILITY (thunk_fndecl) = DECL_VISIBILITY (function);

  if (flag_syntax_only)
    {
      TREE_ASM_WRITTEN (thunk_fndecl) = 1;
      return;
    }

  push_to_top_level ();

#if defined (ASM_OUTPUT_DEF) \
  && !defined (TARGET_IS_PE_COFF)
  if (targetm.have_named_sections)
    {
      resolve_unique_section (function, 0, flag_function_sections);

      if (DECL_SECTION_NAME (function) != NULL && DECL_ONE_ONLY (function))
	{
	  resolve_unique_section (thunk_fndecl, 0, flag_function_sections);

	  /* Output the thunk into the same section as function.  */
	  DECL_SECTION_NAME (thunk_fndecl) = DECL_SECTION_NAME (function);
	}
    }
#endif

  /* The back-end expects DECL_INITIAL to contain a BLOCK, so we
     create one.  */
  DECL_INITIAL (thunk_fndecl) = make_node (BLOCK);
  BLOCK_VARS (DECL_INITIAL (thunk_fndecl)) = DECL_ARGUMENTS (thunk_fndecl);
  
  if (this_adjusting
      && targetm.asm_out.can_output_mi_thunk (thunk_fndecl, fixed_offset,
					      virtual_value, alias))
    {
      const char *fnname;
      current_function_decl = thunk_fndecl;
      DECL_RESULT (thunk_fndecl)
	= build_decl (RESULT_DECL, 0, integer_type_node);
      fnname = XSTR (XEXP (DECL_RTL (thunk_fndecl), 0), 0);
      init_function_start (thunk_fndecl);
      current_function_is_thunk = 1;
      assemble_start_function (thunk_fndecl, fnname);

      targetm.asm_out.output_mi_thunk (asm_out_file, thunk_fndecl,
				       fixed_offset, virtual_value, alias);

      assemble_end_function (thunk_fndecl, fnname);
      current_function_decl = 0;
      cfun = 0;
      /* Because init_function_start increments this, we must
	 decrement it.  */
      immediate_size_expand--;
      TREE_ASM_WRITTEN (thunk_fndecl) = 1;
    }
  else
    {
      /* If this is a covariant thunk, or we don't have the necessary
	 code for efficient thunks, generate a thunk function that
	 just makes a call to the real function.  Unfortunately, this
	 doesn't work for varargs.  */

      tree a, t;

      if (varargs_function_p (function))
	error ("generic thunk code fails for method `%#D' which uses `...'",
	       function);

      /* Set up cloned argument trees for the thunk.  */
      t = NULL_TREE;
      for (a = DECL_ARGUMENTS (function); a; a = TREE_CHAIN (a))
	{
	  tree x = copy_node (a);
	  TREE_CHAIN (x) = t;
	  DECL_CONTEXT (x) = thunk_fndecl;
	  SET_DECL_RTL (x, NULL_RTX);
	  t = x;
	}
      a = nreverse (t);
      DECL_ARGUMENTS (thunk_fndecl) = a;
      DECL_RESULT (thunk_fndecl) = NULL_TREE;

      start_function (NULL_TREE, thunk_fndecl, NULL_TREE, SF_PRE_PARSED);
      /* We don't bother with a body block for thunks.  */

      /* There's no need to check accessibility inside the thunk body.  */
      push_deferring_access_checks (dk_no_check);

      t = a;
      if (this_adjusting)
	t = thunk_adjust (t, /*this_adjusting=*/1,
			  fixed_offset, virtual_offset);
      
      /* Build up the call to the real function.  */
      t = tree_cons (NULL_TREE, t, NULL_TREE);
      for (a = TREE_CHAIN (a); a; a = TREE_CHAIN (a))
	t = tree_cons (NULL_TREE, a, t);
      t = nreverse (t);
      t = build_call (alias, t);
      CALL_FROM_THUNK_P (t) = 1;
      t = force_target_expr (TREE_TYPE (t), t);
      if (!this_adjusting)
	t = thunk_adjust (t, /*this_adjusting=*/0,
			  fixed_offset, virtual_offset);
      
      if (VOID_TYPE_P (TREE_TYPE (t)))
	finish_expr_stmt (t);
      else
	finish_return_stmt (t);

      /* Since we want to emit the thunk, we explicitly mark its name as
	 referenced.  */
      mark_referenced (DECL_ASSEMBLER_NAME (thunk_fndecl));

      /* But we don't want debugging information about it.  */
      DECL_IGNORED_P (thunk_fndecl) = 1;

      /* Re-enable access control.  */
      pop_deferring_access_checks ();

      expand_body (finish_function (0));
    }

  pop_from_top_level ();
}