#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#ifdef USE_NLLIBC
#include <nllibc.h>
#endif

#include "config.h"
#include "lib.h"
#include "objlist.h"
#include "type.h"
#include "symbol.h"
#include "value.h"
#include "asm.h"
#include "asm_syntax.h"
#include "asm_symbol.h"
#include "asm_format.h"
#include "asm_code.h"
#include "asm_queue.h"

static int label_next = 1;

int asm_symbol_tmp_label_reserve(int n)
{
  int r;
  r = label_next;
  label_next += n;
  return r;
}

int asm_symbol_tmp_label_release(int n)
{
  label_next -= n;
  return label_next;
}

int asm_symbol_tmp_label_current(void)
{
  return label_next - 1;
}

char *asm_symbol_tmp_label_name(int n)
{
  static char *label[] = {
    "0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
  };
  if (n > 9)
    ERREXIT(1);
  return label[asm_symbol_tmp_label_current() - n];
}

char *asm_symbol_tmp_label_namef(int n)
{
  static char *label[] = {
    "0f", "1f", "2f", "3f", "4f", "5f", "6f", "7f", "8f", "9f",
  };
  if (n > 9)
    ERREXIT(1);
  return label[asm_symbol_tmp_label_current() - n];
}

static int tmp_next = 0;

int asm_symbol_tmp_reserve(void)
{
  int r;
  r = tmp_next++;
  if (tmp_next > asm_info_tmp_reg_number() + asm_info_tmp_stack_number())
    ERREXIT(1);
  return r;
}

int asm_symbol_tmp_release(void)
{
  --tmp_next;
  return tmp_next;
}

int asm_symbol_tmp_current(void)
{
  return tmp_next - 1;
}

int asm_symbol_tmp_stack_load(FILE *out, int number)
{
  asm_queue_code_stack_load(out,
			    asm_info_word_size() * (asm_info_funccall_args_stack_number() + number),
			    NULL);
  return 0;
}

int asm_symbol_tmp_stack_store(FILE *out, int number)
{
  asm_queue_code_stack_store(out,
			     asm_info_word_size() * (asm_info_funccall_args_stack_number() + number),
			     NULL);
  return 0;
}

int asm_symbol_tmp_stack_load_r1(FILE *out, int number)
{
  asm_queue_code_stack_load_r1(out,
			       asm_info_word_size() * (asm_info_funccall_args_stack_number() + number),
			       NULL);
  return 0;
}

int asm_symbol_tmp_stack_store_r1(FILE *out, int number)
{
  asm_queue_code_stack_store_r1(out,
				asm_info_word_size() * (asm_info_funccall_args_stack_number() + number),
				NULL);
  return 0;
}

int asm_symbol_tmp_save(FILE *out, int number)
{
  number = asm_symbol_tmp_current() - number;
  if (number < asm_info_tmp_reg_number())
    asm_queue_code_tmp_reg_save(out, number);
  else
    asm_symbol_tmp_stack_store(out, number - asm_info_tmp_reg_number());
  return 0;
}

int asm_symbol_tmp_load(FILE *out, int number)
{
  number = asm_symbol_tmp_current() - number;
  if (number < asm_info_tmp_reg_number())
    asm_queue_code_tmp_reg_load(out, number);
  else
    asm_symbol_tmp_stack_load(out, number - asm_info_tmp_reg_number());
  return 0;
}

int asm_symbol_tmp_save_r1(FILE *out, int number)
{
  number = asm_symbol_tmp_current() - number;
  if (number < asm_info_tmp_reg_number())
    asm_queue_code_tmp_reg_save_r1(out, number);
  else
    asm_symbol_tmp_stack_store_r1(out, number - asm_info_tmp_reg_number());
  return 0;
}

int asm_symbol_tmp_load_r1(FILE *out, int number)
{
  number = asm_symbol_tmp_current() - number;
  if (number < asm_info_tmp_reg_number())
    asm_queue_code_tmp_reg_load_r1(out, number);
  else
    asm_symbol_tmp_stack_load_r1(out, number - asm_info_tmp_reg_number());
  return 0;
}

static int stack_variable_offset(void)
{
  return asm_info_word_size() * (asm_info_funccall_args_stack_number() + asm_info_tmp_stack_number());
}

static int stack_saveregs_offset(symbol_t function)
{
  return stack_variable_offset() +
    function->obj.function.stack->size +
    function->obj.function.stack->size_correct;
}

static int stack_saveregs_size(void)
{
  return asm_info_word_size() * asm_info_function_register_number();
}

static int stack_vararg_offset(symbol_t function)
{
  return stack_saveregs_offset(function) + stack_saveregs_size();
}

static int stack_vararg_size(void)
{
  return asm_info_word_size() * asm_info_funccall_args_reg_number();
}

static int stack_saveparam_offset(symbol_t function)
{
  return stack_vararg_offset(function) + stack_vararg_size();
}

static int stack_saveparam_size(void)
{
  return asm_info_word_size() * asm_info_function_saveparam_number();
}

static int stack_args_offset(symbol_t function)
{
  return stack_saveparam_offset(function) + stack_saveparam_size();
}

static int asm_symbol_function_args_get(FILE *out, symbol_t symbol, int number)
{
  int n;
  n = asm_info_funccall_args_reg_number();
  if (number < n) {
    asm_queue_code_stack_load(out, stack_vararg_offset(symbol) + number * asm_info_word_size(), NULL);
  } else {
    asm_queue_code_stack_load(out, stack_args_offset(symbol) + (number - n) * asm_info_word_size(), NULL);
  }
  return 0;
}

static int asm_symbol_function_args_set(FILE *out, int offset, int number)
{
  int n;
  n = asm_info_funccall_args_reg_number();
  if (number < n) {
    asm_queue_code_funccall_reg_store(out, number);
  } else {
    asm_queue_code_stack_store(out, offset + (number - n) * asm_info_word_size(), NULL);
  }
  return 0;
}

int asm_symbol_function_call(FILE *out, char *label, int args_number)
{
  int i;

  for (i = 0; i < args_number; i++) {
    asm_symbol_tmp_load(out, 0);
    asm_symbol_function_args_set(out, 0, (args_number - 1 - i));
    asm_symbol_tmp_release();
  }

  if (label) {
    if (asm_queue_code_function_call(out, label) < 0) {
      asm_symbol_tmp_reserve();
      asm_symbol_tmp_reserve();
      asm_symbol_tmp_save(out, 0);
      asm_queue_code_get_address(out, label);
      asm_symbol_tmp_save(out, 1);
      asm_symbol_tmp_load(out, 0);
      asm_symbol_tmp_release();
    } else {
      return 0;
    }
  }

  if (asm_queue_code_function_call_set(NULL) < 0) {
    asm_symbol_tmp_load(out, 0);
    asm_symbol_tmp_release();
  } else {
    asm_symbol_tmp_reserve();
    asm_symbol_tmp_save(out, 0);
    asm_symbol_tmp_load(out, 1);
    asm_queue_code_function_call_set(out);
    asm_symbol_tmp_load(out, 0);
    asm_symbol_tmp_release();
    asm_symbol_tmp_release();
  }

  if (asm_queue_code_function_call_pointer(out) < 0)
    ERREXIT(1);

  return 0;
}

int asm_symbol_sign_extension(FILE *out, model_t model)
{
  int r = 0;
  char *function = NULL;

  if (!model_is_address(model)) {
    switch (model->type) {
    case C_TYPE_CHAR:
      if (model->flags & MODEL_FLAG_UNSIGNED) {
	r = asm_queue_code_sign_extension_uchar(out);
	function = "__builtin_extension_uchar";
      } else {
	r = asm_queue_code_sign_extension_char(out);
	function = "__builtin_extension_char";
      }
      break;
    case C_TYPE_SHORT:
      if (model->flags & MODEL_FLAG_UNSIGNED) {
	r = asm_queue_code_sign_extension_ushort(out);
	function = "__builtin_extension_ushort";
      } else {
	r = asm_queue_code_sign_extension_short(out);
	function = "__builtin_extension_short";
      }
      break;
    case C_TYPE_INT:
      if (asm_info_word_size() == 4)
	break;
      if (model->flags & MODEL_FLAG_UNSIGNED) {
	r = asm_queue_code_sign_extension_uint(out);
	function = "__builtin_extension_uint";
      } else {
	r = asm_queue_code_sign_extension_int(out);
	function = "__builtin_extension_int";
      }
      break;
    default:
      break;
    }
  }

  if (!function)
    return 0;

  if (r < 0) {
    asm_symbol_tmp_reserve();
    asm_symbol_tmp_save_r1(out, 0);

    asm_symbol_tmp_reserve();
    asm_symbol_tmp_save(out, 0);
    asm_symbol_function_call(out, function, 1);

    asm_symbol_tmp_load_r1(out, 0);
    asm_symbol_tmp_release();
  }

  return 0;
}

int asm_symbol_branch_zero(FILE *out, char *label)
{
  return asm_queue_code_branch_zero(out, label);
}

int asm_symbol_branch_nzero(FILE *out, char *label)
{
  int r;

  r = asm_queue_code_branch_nzero(out, label);

  if (r < 0) {
    asm_symbol_tmp_label_reserve(1);
    r = asm_queue_code_branch_zero(out, asm_symbol_tmp_label_namef(0));
    if (r < 0)
      ERREXIT(1);
    asm_queue_code_branch(out, label);
    asm_queue_format_label(out, asm_symbol_tmp_label_name(0));
    asm_symbol_tmp_label_release(1);
  }

  if (r < 0)
    ERREXIT(1);

  return 0;
}

static int call_memcpy(FILE *out, int size)
{
  if (size == 0)
    return 0;

  asm_symbol_tmp_reserve();
  asm_symbol_tmp_save_r1(out, 0);

  asm_symbol_tmp_reserve();
  asm_symbol_tmp_save_r1(out, 0);
  asm_symbol_tmp_reserve();
  asm_symbol_tmp_save(out, 0);
  asm_symbol_tmp_reserve();
  if ((size % 4) == 0) {
    asm_queue_code_get_value(out, size / 4);
    asm_symbol_tmp_save(out, 0);
    asm_symbol_function_call(out, "__builtin_memcpy4", 3);
  } else {
    asm_queue_code_get_value(out, size);
    asm_symbol_tmp_save(out, 0);
    asm_symbol_function_call(out, "__builtin_memcpy", 3);
  }

  asm_symbol_tmp_load_r1(out, 0);
  asm_symbol_tmp_release();

  return 0;
}

static char *symbol_name(symbol_t symbol)
{
  return (symbol->name != NULL) ? symbol->name : "NONAME";
}

static int address_get_r1(FILE *out, symbol_t symbol, symbol_t function)
{
  /* optimize to get the address to R1 directly */
  switch (symbol->type) {
  case C_TYPE_LABEL:
    /* fall through */
  case C_TYPE_VARIABLE:
    switch (symbol->location.type) {
    case C_TYPE_STACK:
      if (asm_queue_code_get_address_stack_r1(out, stack_variable_offset() + symbol->location.offset) < 0)
	break;
      return 0;
    default:
      break;
    }
    break;
  default:
    break;
  }

  asm_symbol_tmp_reserve();
  asm_symbol_tmp_save(out, 0);
  asm_symbol_address_get(out, symbol, function);
  asm_queue_code_set_r1(out);
  asm_symbol_tmp_load(out, 0);
  asm_symbol_tmp_release();
  return 0;
}

int asm_symbol_address_get(FILE *out, symbol_t symbol, symbol_t function)
{
  asm_queue_format_section(out, ASM_FORMAT_TYPE_SECTION_TEXT);

  asm_queue_format_comment(out, "ADDRESS GET BEGIN", symbol_name(symbol), type_get_word(symbol->type), NULL);

  saveline(symbol->line);

  switch (symbol->type) {
  case C_TYPE_LABEL:
    /* fall through */
  case C_TYPE_VARIABLE:
    switch (symbol->location.type) {
    case C_TYPE_EXTERN:
    case C_TYPE_STATIC:
      asm_queue_code_get_address(out, symbol->name);
      break;
    case C_TYPE_STACK:
      asm_queue_code_get_address_stack(out, stack_variable_offset() + symbol->location.offset);
      break;
    case C_TYPE_STRUCT:
    case C_TYPE_UNION:
      ERREXIT(1);
    case C_TYPE_SYMBOL:
      asm_symbol_address_get(out, symbol->location.base.symbol, function);
      asm_queue_code_add_address(out, symbol->location.offset);
      break;
    case C_TYPE_POINTER:
      asm_symbol_value_get(out, symbol->location.base.symbol, function);
      asm_queue_code_add_address(out, symbol->location.offset);
      break;
    case C_TYPE_NONE:
      asm_queue_code_get_address(out, symbol->name);
      break;
    default:
      ERREXIT(1);
    }
    break;
  default:
    ERREXIT(1);
  }

  asm_queue_format_comment(out, "ADDRESS GET END", symbol_name(symbol), NULL, NULL);
  return 0;
}

static int bitfield_read(FILE *out, symbol_t symbol, symbol_t function)
{
  int n = 0;

  if (!(symbol->flags & SYMBOL_FLAG_BITFIELD))
    return 0;

  asm_symbol_tmp_reserve();
  asm_symbol_tmp_save_r1(out, 0);

  asm_symbol_tmp_reserve();
  asm_symbol_tmp_save(out, 0);
  n++;

  asm_symbol_tmp_reserve();
  asm_queue_code_get_value(out, 32);
  asm_symbol_tmp_save(out, 0);
  n++;

  asm_symbol_tmp_reserve();
  asm_queue_code_get_value(out, symbol->param.variable.bitfield.bits);
  asm_symbol_tmp_save(out, 0);
  n++;

  asm_symbol_tmp_reserve();
  asm_queue_code_get_value(out, symbol->param.variable.bitfield.shift);
  asm_symbol_tmp_save(out, 0);
  n++;

  if (symbol->model->flags & MODEL_FLAG_UNSIGNED)
    asm_symbol_function_call(out, "__builtin_bitfield_uread", n);
  else
    asm_symbol_function_call(out, "__builtin_bitfield_read", n);

  asm_symbol_tmp_load_r1(out, 0);
  asm_symbol_tmp_release();

  return 0;
}

static int value_get(FILE *out, symbol_t symbol, symbol_t function);

static int bitfield_write(FILE *out, symbol_t symbol, symbol_t function)
{
  int n = 0;

  if (!(symbol->flags & SYMBOL_FLAG_BITFIELD))
    return 0;

  asm_symbol_tmp_reserve();
  asm_symbol_tmp_save_r1(out, 0);

  asm_symbol_tmp_reserve();
  asm_symbol_tmp_save(out, 0);
  n++;

  asm_symbol_tmp_reserve();
  value_get(out, symbol, function);
  asm_symbol_tmp_save(out, 0);
  n++;

  asm_symbol_tmp_reserve();
  asm_queue_code_get_value(out, 32);
  asm_symbol_tmp_save(out, 0);
  n++;

  asm_symbol_tmp_reserve();
  asm_queue_code_get_value(out, symbol->param.variable.bitfield.bits);
  asm_symbol_tmp_save(out, 0);
  n++;

  asm_symbol_tmp_reserve();
  asm_queue_code_get_value(out, symbol->param.variable.bitfield.shift);
  asm_symbol_tmp_save(out, 0);
  n++;

  asm_symbol_function_call(out, "__builtin_bitfield_write", n);

  asm_symbol_tmp_load_r1(out, 0);
  asm_symbol_tmp_release();

  return 0;
}

static int asm_symbol_calc_op1(FILE *out, c_type_t type,
			       model_t model, model_t model_arg0)
{
  int r = -1;
  char *function = NULL;

  switch (type) {
  case C_TYPE_INV:   r = asm_queue_code_calc_inv(out, model, model_arg0);   break;
  case C_TYPE_MINUS: r = asm_queue_code_calc_minus(out, model, model_arg0); break;
  default:
    break;
  }

  if (r < 0)
    r = asm_queue_code_calc_op1(out, type, model, model_arg0);

  if (r < 0) {
    asm_symbol_tmp_reserve();
    asm_symbol_tmp_save_r1(out, 0);

    asm_symbol_tmp_reserve();
    asm_symbol_tmp_save(out, 0);
    switch (type) {
    case C_TYPE_INV:
      function = "__builtin_inv";
      break;
    case C_TYPE_MINUS:
      function = "__builtin_minus";
      break;
    default:
      break;
    }
    if (function == NULL)
      ERREXIT(1);
    asm_symbol_function_call(out, function, 1);

    asm_symbol_tmp_load_r1(out, 0);
    asm_symbol_tmp_release();
  }

  return 0;
}

static int asm_symbol_calc_op2(FILE *out, c_type_t type,
			       model_t model, model_t model_arg0, model_t model_arg1)
{
  int r = -1, bits, n = 0;
  char *function = NULL;

  switch (type) {
  case C_TYPE_ADD: asm_queue_code_calc_add(out, model, model_arg0, model_arg1); return 0;
  case C_TYPE_SUB: asm_queue_code_calc_sub(out, model, model_arg0, model_arg1); return 0;
  case C_TYPE_AND: asm_queue_code_calc_and(out, model, model_arg0, model_arg1); return 0;
  case C_TYPE_OR:  asm_queue_code_calc_or(out, model, model_arg0, model_arg1);  return 0;

  case C_TYPE_XOR: r = asm_queue_code_calc_xor(out, model, model_arg0, model_arg1); break;
  case C_TYPE_MUL: r = asm_queue_code_calc_mul(out, model, model_arg0, model_arg1); break;
  case C_TYPE_DIV: r = asm_queue_code_calc_div(out, model, model_arg0, model_arg1); break;
  case C_TYPE_MOD: r = asm_queue_code_calc_mod(out, model, model_arg0, model_arg1); break;
  case C_TYPE_LSHIFT:
    r = asm_queue_code_calc_llshift(out, model, model_arg0, model_arg1);
    break;
  case C_TYPE_RSHIFT:
    r = (model_arg0->flags & MODEL_FLAG_UNSIGNED)
      ? asm_queue_code_calc_rlshift(out, model, model_arg0, model_arg1)
      : asm_queue_code_calc_rashift(out, model, model_arg0, model_arg1);
    break;
  default:
    break;
  }

  if (r < 0)
    r = asm_queue_code_calc_op2(out, type, model, model_arg0, model_arg1);

  if (r < 0) {
    asm_symbol_tmp_reserve();
    asm_symbol_tmp_save_r1(out, 0);

    asm_symbol_tmp_reserve();
    asm_symbol_tmp_save(out, 0);
    n++;

    asm_symbol_tmp_reserve();
    asm_symbol_tmp_save_r1(out, 0);
    n++;

    bits = model_get_size(model_arg0) * 8;
    if (bits < 32) bits = 32;
    asm_symbol_tmp_reserve();
    asm_queue_code_get_value(out, bits);
    asm_symbol_tmp_save(out, 0);
    n++;

    switch (type) {
    case C_TYPE_XOR:
      if (bits == 32)
	function = "__builtin_xor_uint";
      else
	function = "__builtin_xor_ulong";
      break;
    case C_TYPE_MUL:
      if (bits == 32)
	function = (model_arg0->flags & MODEL_FLAG_UNSIGNED)
	  ? "__builtin_umulsi3" : "__builtin_mulsi3";
      else
	function = (model_arg0->flags & MODEL_FLAG_UNSIGNED)
	  ? "__builtin_umuldi3" : "__builtin_muldi3";
      break;
    case C_TYPE_DIV:
      if (bits == 32)
	function = (model_arg0->flags & MODEL_FLAG_UNSIGNED)
	  ? "__builtin_udivsi3" : "__builtin_divsi3";
      else
	function = (model_arg0->flags & MODEL_FLAG_UNSIGNED)
	  ? "__builtin_udivdi3" : "__builtin_divdi3";
      break;
    case C_TYPE_MOD:
      if (bits == 32)
	function = (model_arg0->flags & MODEL_FLAG_UNSIGNED)
	  ? "__builtin_umodsi3" : "__builtin_modsi3";
      else
	function = (model_arg0->flags & MODEL_FLAG_UNSIGNED)
	  ? "__builtin_umoddi3" : "__builtin_moddi3";
      break;
    case C_TYPE_LSHIFT:
      if (bits == 32)
	function = (model_arg0->flags & MODEL_FLAG_UNSIGNED)
	  ? "__builtin_lshlsi3" : "__builtin_ashlsi3";
      else
	function = (model_arg0->flags & MODEL_FLAG_UNSIGNED)
	  ? "__builtin_lshldi3" : "__builtin_ashldi3";
      break;
    case C_TYPE_RSHIFT:
      if (bits == 32)
	function = (model_arg0->flags & MODEL_FLAG_UNSIGNED)
	  ? "__builtin_lshrsi3" : "__builtin_ashrsi3";
      else
	function = (model_arg0->flags & MODEL_FLAG_UNSIGNED)
	  ? "__builtin_lshrdi3" : "__builtin_ashrdi3";
      break;
    default:
      break;
    }
    if (function == NULL)
      ERREXIT(1);
    asm_symbol_function_call(out, function, n);

    asm_symbol_tmp_load_r1(out, 0);
    asm_symbol_tmp_release();
  }

  return 0;
}

static int value_op1_incdec(FILE *out, symbol_t function, symbol_t *arg, c_type_t type, model_t model, int after)
{
  int n = 1;

  if (model_is_pointer(arg[0]->model))
    n = model_get_pointer_size(arg[0]->model);

  asm_symbol_value_set(out, arg[0], function);
  asm_symbol_value_get(out, arg[0], function);

  asm_symbol_tmp_reserve();
  asm_queue_code_get_value_r1(out, n);
  if (after) asm_symbol_tmp_save(out, 0);
  asm_symbol_calc_op2(out, type, model, arg[0]->model, arg[0]->model);
  if (!after) asm_symbol_tmp_save(out, 0);

  address_get_r1(out, arg[0], function);
  bitfield_write(out, arg[0], function);
  asm_queue_code_memory_store(out, 0, arg[0]->model);

  asm_symbol_tmp_load(out, 0);
  asm_symbol_tmp_release();

  return 0;
}

static int value_op2_readyreg(FILE *out, symbol_t function, symbol_t *arg)
{
  asm_symbol_value_set(out, arg[0], function);
  asm_symbol_value_set(out, arg[1], function);
  asm_symbol_value_get(out, arg[1], function);
  asm_symbol_tmp_reserve();
  asm_symbol_tmp_save(out, 0);
  asm_symbol_value_get(out, arg[0], function);
  asm_symbol_tmp_load_r1(out, 0);
  asm_symbol_tmp_release();
  return 0;
}

static int value_op2_setreg(FILE *out, symbol_t function, symbol_t *arg)
{
  model_t model;
  model = arg[0]->model;

  address_get_r1(out, arg[0], function);
  if (!model_is_address(model) &&
      ((model->type == C_TYPE_STRUCT) ||
       (model->type == C_TYPE_UNION))) {
    call_memcpy(out, model_get_size(model));
  } else {
    asm_symbol_tmp_reserve();
    asm_symbol_tmp_save(out, 0);
    bitfield_write(out, arg[0], function);
    asm_queue_code_memory_store(out, 0, model);
    asm_symbol_tmp_load(out, 0);
    asm_symbol_tmp_release();
  }
  return 0;
}

static int value_op2_calc(FILE *out, symbol_t function, symbol_t *arg, c_type_t type, model_t model)
{
  value_op2_readyreg(out, function, arg);
  asm_symbol_calc_op2(out, type, model, arg[0]->model, arg[1]->model);
  return 0;
}

static int asm_symbol_branch_cmp(FILE *out, char *label, c_type_t type, model_t model)
{
  int r = -1;
  int sign;

  sign = !(model->flags & MODEL_FLAG_UNSIGNED);

  switch (type) {
  case C_TYPE_LT:
    if (sign) r = asm_queue_code_branch_cmp_lt(out, label, model);
    else      r = asm_queue_code_branch_cmp_ult(out, label, model);
    break;
  case C_TYPE_GT:
    if (sign) r = asm_queue_code_branch_cmp_gt(out, label, model);
    else      r = asm_queue_code_branch_cmp_ugt(out, label, model);
    break;
  case C_TYPE_LTEQ:
    if (sign) r = asm_queue_code_branch_cmp_le(out, label, model);
    else      r = asm_queue_code_branch_cmp_ule(out, label, model);
    break;
  case C_TYPE_GTEQ:
    if (sign) r = asm_queue_code_branch_cmp_ge(out, label, model);
    else      r = asm_queue_code_branch_cmp_uge(out, label, model);
    break;
  case C_TYPE_EQEQ: r = asm_queue_code_branch_cmp_eq(out, label, model); break;
  case C_TYPE_NEQ:  r = asm_queue_code_branch_cmp_ne(out, label, model); break;
  default:
    break;
  }

  if (r < 0)
    asm_queue_code_branch_cmp(out, label, type, model);

  if (r < 0) {
    switch (type) {
    case C_TYPE_LTEQ:
      if (sign) r = asm_queue_code_branch_cmp_lt(out, label, model);
      else      r = asm_queue_code_branch_cmp_ult(out, label, model);
      if (r < 0)
	break;
      r = asm_queue_code_branch_cmp_eq(out, label, model);
      if (r < 0)
	break;
      break;

    case C_TYPE_GTEQ:
      asm_symbol_tmp_label_reserve(1);
      if (sign) r = asm_queue_code_branch_cmp_lt(out, asm_symbol_tmp_label_namef(0), model);
      else      r = asm_queue_code_branch_cmp_ult(out, asm_symbol_tmp_label_namef(0), model);
      if (r < 0)
	break;
      asm_queue_code_branch(out, label);
      asm_queue_format_label(out, asm_symbol_tmp_label_name(0));
      asm_symbol_tmp_label_release(1);
      break;

    case C_TYPE_GT:
      asm_symbol_tmp_label_reserve(1);
      if (sign) r = asm_queue_code_branch_cmp_lt(out, asm_symbol_tmp_label_namef(0), model);
      else      r = asm_queue_code_branch_cmp_ult(out, asm_symbol_tmp_label_namef(0), model);
      if (r < 0)
	break;
      r = asm_queue_code_branch_cmp_eq(out, asm_symbol_tmp_label_namef(0), model);
      if (r < 0)
	break;
      asm_queue_code_branch(out, label);
      asm_queue_format_label(out, asm_symbol_tmp_label_name(0));
      asm_symbol_tmp_label_release(1);
      break;

    case C_TYPE_NEQ:
      asm_symbol_tmp_label_reserve(1);
      r = asm_queue_code_branch_cmp_eq(out, asm_symbol_tmp_label_namef(0), model);
      if (r < 0)
	break;
      asm_queue_code_branch(out, label);
      asm_queue_format_label(out, asm_symbol_tmp_label_name(0));
      asm_symbol_tmp_label_release(1);
      break;

    default:
      break;
    }
  }

  if (r < 0)
    ERREXIT(1);

  return 0;
}

static int value_op2_cmp(FILE *out, symbol_t function, symbol_t *arg, c_type_t type, model_t model)
{
  value_op2_readyreg(out, function, arg);
  asm_symbol_tmp_label_reserve(2);
  asm_symbol_branch_cmp(out, asm_symbol_tmp_label_namef(1), type, model);
  asm_queue_code_get_value(out, 0);
  asm_queue_code_branch(out, asm_symbol_tmp_label_namef(0));
  asm_queue_format_label(out, asm_symbol_tmp_label_name(1));
  asm_queue_code_get_value(out, 1);
  asm_queue_format_label(out, asm_symbol_tmp_label_name(0));
  asm_symbol_tmp_label_release(2);
  return 0;
}

static int value_op2_eq(FILE *out, symbol_t function, symbol_t *arg, c_type_t type, model_t model)
{
  switch (type) {
  case C_TYPE_ADDEQ:    type = C_TYPE_ADD;    break;
  case C_TYPE_SUBEQ:    type = C_TYPE_SUB;    break;
  case C_TYPE_ANDEQ:    type = C_TYPE_AND;    break;
  case C_TYPE_OREQ:     type = C_TYPE_OR;     break;
  case C_TYPE_XOREQ:    type = C_TYPE_XOR;    break;
  case C_TYPE_MULEQ:    type = C_TYPE_MUL;    break;
  case C_TYPE_DIVEQ:    type = C_TYPE_DIV;    break;
  case C_TYPE_MODEQ:    type = C_TYPE_MOD;    break;
  case C_TYPE_LSHIFTEQ: type = C_TYPE_LSHIFT; break;
  case C_TYPE_RSHIFTEQ: type = C_TYPE_RSHIFT; break;
  default:
    ERREXIT(1);
  }
  value_op2_calc(out, function, arg, type, model);
  value_op2_setreg(out, function, arg);
  return 0;
}

static symbol_t builtin_function(FILE *out, symbol_t symbol, symbol_t function)
{
  objentry_t entry;
  symbol_t s;
  int i, n, offset;

  if (!strcmp(symbol->value->obj.function.symbol->name, "__builtin_va_arg")) {
    value_op1_incdec(out, function, symbol->value->obj.function.args.arg,
		     C_TYPE_ADD, symbol->value->obj.function.args.arg[0]->model, 1);
    asm_queue_code_set_r1(out);
    asm_queue_code_memory_load(out, 0, NULL);
    asm_symbol_sign_extension(out, symbol->value->obj.function.args.arg[1]->model);
    return NULL;
  }

  if (!strcmp(symbol->value->obj.function.symbol->name, "__builtin_va_start")) {
    n = asm_info_function_saveparam_number();
    for (i = 0; i < n; i++) {
      offset = i * asm_info_word_size();
      asm_queue_code_stack_load(out, stack_saveparam_offset(function) + offset, NULL);
      asm_symbol_tmp_reserve();
      asm_symbol_tmp_save(out, 0);
    }

    n = asm_info_funccall_args_reg_number();
    for (i = 0; i < n; i++) {
      offset = (n - i - 1) * asm_info_word_size();
      asm_queue_code_stack_load(out, stack_vararg_offset(function) + offset, NULL);
      asm_queue_code_stack_store(out, stack_vararg_offset(function) + stack_saveparam_size() + offset, NULL);
    }

    n = asm_info_function_saveparam_number();
    for (i = 0; i < n; i++) {
      offset = (n - i - 1) * asm_info_word_size();
      asm_symbol_tmp_load(out, 0);
      asm_symbol_tmp_release();
      asm_queue_code_stack_store(out, stack_vararg_offset(function) + offset, NULL);
    }

    if (symbol->value->obj.function.args.number < 2)
      ERREXIT(1);

    i = 0;
    n = -1;
    for (entry = objlist_get_head(function->obj.function.args);
	 !objlist_is_end(function->obj.function.args, entry);
	 entry = objentry_get_next(entry)) {
      s = objentry_get_obj(entry);
      if (s == symbol->value->obj.function.args.arg[1]) {
	n = i;
	break;
      }
      i++;
    }
    if (n < 0)
      ERREXIT(1);

    asm_symbol_address_get(out, symbol->value->obj.function.args.arg[0], function);
    asm_queue_code_set_r1(out);
    asm_queue_code_get_address_stack(out,
				     stack_vararg_offset(function)
				     + stack_saveparam_size() + (n + 1) * asm_info_word_size());
    asm_queue_code_memory_store(out, 0, NULL);
    return NULL;
  }

  if (!strcmp(symbol->value->obj.function.symbol->name, "__builtin_va_end")) {
    n = asm_info_function_saveparam_number();
    for (i = 0; i < n; i++) {
      offset = (n - 1 - i) * asm_info_word_size();
      asm_queue_code_stack_load(out, stack_vararg_offset(function) + offset, NULL);
      asm_symbol_tmp_reserve();
      asm_symbol_tmp_save(out, 0);
    }

    n = asm_info_funccall_args_reg_number();
    for (i = 0; i < n; i++) {
      offset = i * asm_info_word_size();
      asm_queue_code_stack_load(out, stack_vararg_offset(function) + stack_saveparam_size() + offset, NULL);
      asm_queue_code_stack_store(out, stack_vararg_offset(function) + offset, NULL);
    }

    n = asm_info_function_saveparam_number();
    for (i = 0; i < n; i++) {
      offset = i * asm_info_word_size();
      asm_symbol_tmp_load(out, 0);
      asm_symbol_tmp_release();
      asm_queue_code_stack_store(out, stack_saveparam_offset(function) + offset, NULL);
    }

    return NULL;
  }

  return symbol;
}

static int asm_symbol_value_op(FILE *out, symbol_t symbol, symbol_t function)
{
  symbol_t *arg;
  int i;

  asm_queue_format_comment(out, "VALUE OP BEGIN",
			   symbol_name(symbol), type_get_word(symbol->type),
			   type_get_word(symbol->value->type));

  switch (symbol->value->type) {
  case C_TYPE_NUMBER:
    asm_queue_code_get_value(out, symbol->value->obj.number);
    goto ret;
  default:
    break;
  }

  arg = symbol->value->obj.op.arg;

  switch (symbol->value->type) {
  case C_TYPE_CAST:
    asm_symbol_value_set(out, arg[0], function);
    asm_symbol_value_get(out, arg[0], function);
    break;
  case C_TYPE_ADDRESS:
    asm_symbol_value_set(out, arg[0], function);
    asm_symbol_address_get(out, arg[0], function);
    break;
  case C_TYPE_POINTER:
    asm_symbol_value_set(out, arg[0], function);
    asm_symbol_value_get(out, arg[0], function);
    if (symbol->type != C_TYPE_LABEL) {
      asm_queue_code_set_r1(out);
      asm_queue_code_memory_load(out, 0, symbol->model);
    }
    break;
  case C_TYPE_DOT:
    asm_symbol_value_set(out, arg[0], function);
    asm_symbol_address_get(out, arg[0], function);
    asm_queue_code_set_r1(out);
    asm_queue_code_memory_load(out, arg[1]->location.offset, symbol->model);
    break;
  case C_TYPE_NOT:
    asm_symbol_value_set(out, arg[0], function);
    asm_symbol_value_get(out, arg[0], function);
    asm_symbol_tmp_label_reserve(2);
    asm_symbol_branch_zero(out, asm_symbol_tmp_label_namef(1));
    asm_queue_code_get_value(out, 0);
    asm_queue_code_branch(out, asm_symbol_tmp_label_namef(0));
    asm_queue_format_label(out, asm_symbol_tmp_label_name(1));
    asm_queue_code_get_value(out, 1);
    asm_queue_format_label(out, asm_symbol_tmp_label_name(0));
    asm_symbol_tmp_label_release(2);
    break;
  case C_TYPE_INV:
    asm_symbol_value_set(out, arg[0], function);
    asm_symbol_value_get(out, arg[0], function);
    asm_symbol_calc_op1(out, symbol->value->type, symbol->model, arg[0]->model);
    break;
  case C_TYPE_PLUS:
    asm_symbol_value_set(out, arg[0], function);
    asm_symbol_value_get(out, arg[0], function);
    break;
  case C_TYPE_MINUS:
    asm_symbol_value_set(out, arg[0], function);
    asm_symbol_value_get(out, arg[0], function);
    asm_symbol_calc_op1(out, symbol->value->type, symbol->model, arg[0]->model);
    break;
  case C_TYPE_INC:  value_op1_incdec(out, function, arg, C_TYPE_ADD, symbol->model, 0); break; /* ++x */
  case C_TYPE_DEC:  value_op1_incdec(out, function, arg, C_TYPE_SUB, symbol->model, 0); break; /* --x */
  case C_TYPE_INCA: value_op1_incdec(out, function, arg, C_TYPE_ADD, symbol->model, 1); break; /* x++ */
  case C_TYPE_DECA: value_op1_incdec(out, function, arg, C_TYPE_SUB, symbol->model, 1); break; /* x-- */

  case C_TYPE_ADD:
  case C_TYPE_SUB:
  case C_TYPE_AND:
  case C_TYPE_OR:
  case C_TYPE_XOR:
  case C_TYPE_MUL:
  case C_TYPE_DIV:
  case C_TYPE_MOD:
  case C_TYPE_LSHIFT:
  case C_TYPE_RSHIFT:
    value_op2_calc(out, function, arg, symbol->value->type, symbol->model);
    break;

  case C_TYPE_LT:
  case C_TYPE_GT:
  case C_TYPE_LTEQ:
  case C_TYPE_GTEQ:
  case C_TYPE_EQEQ:
  case C_TYPE_NEQ:
    value_op2_cmp(out, function, arg, symbol->value->type, arg[0]->model);
    break;

  case C_TYPE_EQ:
    asm_symbol_value_set(out, arg[0], function);
    asm_symbol_value_set(out, arg[1], function);
    asm_symbol_value_get(out, arg[1], function);
    value_op2_setreg(out, function, arg);
    break;

  case C_TYPE_ADDEQ:
  case C_TYPE_SUBEQ:
  case C_TYPE_ANDEQ:
  case C_TYPE_OREQ:
  case C_TYPE_XOREQ:
  case C_TYPE_MULEQ:
  case C_TYPE_DIVEQ:
  case C_TYPE_MODEQ:
  case C_TYPE_LSHIFTEQ:
  case C_TYPE_RSHIFTEQ:
    value_op2_eq(out, function, arg, symbol->value->type, symbol->model);
    break;

  case C_TYPE_ANDAND:
    asm_symbol_value_set(out, arg[0], function);
    asm_symbol_value_get(out, arg[0], function);
    asm_symbol_tmp_label_reserve(2);
    asm_symbol_branch_zero(out, asm_symbol_tmp_label_namef(1));
    asm_symbol_value_set(out, arg[1], function);
    asm_symbol_value_get(out, arg[1], function);
    asm_symbol_branch_zero(out, asm_symbol_tmp_label_namef(1));
    asm_queue_code_get_value(out, 1);
    asm_queue_code_branch(out, asm_symbol_tmp_label_namef(0));
    asm_queue_format_label(out, asm_symbol_tmp_label_name(1));
    asm_queue_code_get_value(out, 0);
    asm_queue_format_label(out, asm_symbol_tmp_label_name(0));
    asm_symbol_tmp_label_release(2);
    break;

  case C_TYPE_OROR:
    asm_symbol_value_set(out, arg[0], function);
    asm_symbol_value_get(out, arg[0], function);
    asm_symbol_tmp_label_reserve(2);
    asm_symbol_branch_nzero(out, asm_symbol_tmp_label_namef(1));
    asm_symbol_value_set(out, arg[1], function);
    asm_symbol_value_get(out, arg[1], function);
    asm_symbol_branch_nzero(out, asm_symbol_tmp_label_namef(1));
    asm_queue_code_get_value(out, 0);
    asm_queue_code_branch(out, asm_symbol_tmp_label_namef(0));
    asm_queue_format_label(out, asm_symbol_tmp_label_name(1));
    asm_queue_code_get_value(out, 1);
    asm_queue_format_label(out, asm_symbol_tmp_label_name(0));
    asm_symbol_tmp_label_release(2);
    break;

  case C_TYPE_QUESTION:
    asm_symbol_value_set(out, arg[0], function);
    asm_symbol_value_get(out, arg[0], function);
    asm_symbol_tmp_label_reserve(2);
    asm_symbol_branch_zero(out, asm_symbol_tmp_label_namef(1));
    asm_symbol_value_set(out, arg[1], function);
    asm_symbol_value_get(out, arg[1], function);
    asm_queue_code_branch(out, asm_symbol_tmp_label_namef(0));
    asm_queue_format_label(out, asm_symbol_tmp_label_name(1));
    asm_symbol_value_set(out, arg[2], function);
    asm_symbol_value_get(out, arg[2], function);
    asm_queue_format_label(out, asm_symbol_tmp_label_name(0));
    asm_symbol_tmp_label_release(2);
    break;
  case C_TYPE_COMMA:
    asm_symbol_value_set(out, arg[0], function);
    asm_symbol_value_set(out, arg[1], function);
    asm_symbol_value_get(out, arg[0], function);
    asm_symbol_value_get(out, arg[1], function);
    break;

  case C_TYPE_ARG:
    asm_symbol_value_get(out, symbol, function);
    break;
  case C_TYPE_FUNCTION:
    if (builtin_function(out, symbol, function) == NULL)
      break;
    if (model_is_pointer(symbol->value->obj.function.symbol->model)) {
      asm_symbol_value_set(out, symbol->value->obj.function.symbol, function);
      asm_symbol_value_get(out, symbol->value->obj.function.symbol, function);
      asm_symbol_tmp_reserve();
      asm_symbol_tmp_save(out, 0);
    }
    for (i = 0; i < symbol->value->obj.function.args.number; i++) {
      asm_symbol_value_set(out, symbol->value->obj.function.args.arg[i], function);
    }
    for (i = 0; i < symbol->value->obj.function.args.number; i++) {
      asm_symbol_value_get(out, symbol->value->obj.function.args.arg[i], function);
      asm_symbol_tmp_reserve();
      asm_symbol_tmp_save(out, 0);
    }
    asm_symbol_function_call(out,
			     model_is_pointer(symbol->value->obj.function.symbol->model)
			     ? NULL
			     : symbol->value->obj.function.symbol->name,
			     symbol->value->obj.function.args.number);
    break;
  default:
    ERREXIT(1);
  }

ret:
  asm_symbol_sign_extension(out, symbol->model);
  asm_queue_format_comment(out, "VALUE OP END",
			   symbol_name(symbol), type_get_word(symbol->type),
			   type_get_word(symbol->value->type));
  return 0;
}

int asm_symbol_value_set(FILE *out, symbol_t symbol, symbol_t function)
{
  asm_queue_format_section(out, ASM_FORMAT_TYPE_SECTION_TEXT);

  asm_queue_format_comment(out, "VALUE SET BEGIN",
			   symbol_name(symbol), type_get_word(symbol->type), NULL);

  saveline(symbol->line);

  switch (symbol->type) {
  case C_TYPE_LABEL:
    if (symbol->flags & SYMBOL_FLAG_TEMPORARY) {
      asm_symbol_value_op(out, symbol, function);
    }
    break;

  case C_TYPE_VARIABLE:
    if (symbol->flags & SYMBOL_FLAG_TEMPORARY) {
      asm_symbol_value_op(out, symbol, function);
      if ((symbol->flags & SYMBOL_FLAG_LOCATED) &&
	  (symbol->location.type == C_TYPE_STACK)) {
	address_get_r1(out, symbol, function);
	if (!model_is_address(symbol->model) &&
	    ((symbol->model->type == C_TYPE_STRUCT) ||
	     (symbol->model->type == C_TYPE_UNION))) {
	  call_memcpy(out, model_get_size(symbol->model));
	} else {
	  asm_queue_code_memory_store(out, 0, symbol->model);
	}
      }
    }
    break;
  default:
    ERREXIT(1);
  }

  asm_queue_format_comment(out, "VALUE SET END", symbol_name(symbol), NULL, NULL);
  return 0;
}

static int value_get(FILE *out, symbol_t symbol, symbol_t function)
{
  asm_queue_format_section(out, ASM_FORMAT_TYPE_SECTION_TEXT);

  saveline(symbol->line);

  switch (symbol->type) {
  case C_TYPE_LABEL:
    asm_symbol_address_get(out, symbol, function);
    break;
  case C_TYPE_VARIABLE:
    if (symbol->flags & SYMBOL_FLAG_READONLY) {
      if (symbol->value == NULL)
	ERREXIT(1);
      switch (symbol->value->type) {
      case C_TYPE_NUMBER:
	asm_queue_code_get_value(out, symbol->value->obj.number);
	break;
      default:
	ERREXIT(1);
      }
      break;
    }
    if ((symbol->flags & SYMBOL_FLAG_LOCATED) ||
	(symbol->scope == C_TYPE_EXTERN)) {
      asm_symbol_address_get(out, symbol, function);
      if (!model_is_address(symbol->model) &&
	  ((symbol->model->type == C_TYPE_STRUCT) ||
	   (symbol->model->type == C_TYPE_UNION)))
	break;
      asm_queue_code_set_r1(out);
      asm_queue_code_memory_load(out, 0, symbol->model);
      break;
    }
    ERREXIT(1);
  default:
    ERREXIT(1);
  }

  return 0;
}

int asm_symbol_value_get(FILE *out, symbol_t symbol, symbol_t function)
{
  asm_queue_format_section(out, ASM_FORMAT_TYPE_SECTION_TEXT);

  asm_queue_format_comment(out, "VALUE GET BEGIN",
			   symbol_name(symbol), type_get_word(symbol->type), NULL);

  value_get(out, symbol, function);

  bitfield_read(out, symbol, function);

  asm_symbol_sign_extension(out, symbol->model);
  asm_queue_format_comment(out, "VALUE GET END", symbol_name(symbol), NULL, NULL);

  return 0;
}

static int asm_symbol_label_value(FILE *out, symbol_t symbol);
static int asm_symbol_variable_value(FILE *out, symbol_t symbol);

static int asm_symbol_array(FILE *out, objlist_t symbols)
{
  objentry_t entry;
  symbol_t symbol;
  int offset = 0, align;

  for (entry = objlist_get_head(symbols);
       !objlist_is_end(symbols, entry);
       entry = objentry_get_next(entry)) {
    symbol = objentry_get_obj(entry);

    align = model_get_align(symbol->model);
    offset = ((offset + align - 1) / align) * align;
    if (symbol->location.offset >= 0) {
      if (symbol->location.offset != offset)
	ERREXIT(1);
    }

#if 1
    asm_queue_format_align(out, model_get_align(symbol->model));
    switch (symbol->type) {
    case C_TYPE_LABEL:    asm_symbol_label_value(out, symbol);    break;
    case C_TYPE_VARIABLE: asm_symbol_variable_value(out, symbol); break;
    default:
      ERREXIT(1);
    }
#else
    asm_symbol(out, symbol);
#endif

    offset += model_get_size(symbol->model);
  }

  return 0;
}

static int asm_symbol_area(FILE *out, area_t area)
{
  asm_queue_format_align(out, area->align);
  asm_queue_format_space(out, area->size);
  return 0;
}

static int correct_size(symbol_t function)
{
  int align_size, correct_size, size;

  align_size = asm_info_stack_align_size();

  if (align_size == 0)
    return 0;
  if (align_size < 0)
    align_size = 16;

  correct_size = asm_info_stack_correct_size();
  if (correct_size < 0)
    correct_size = 0;

  function->obj.function.stack->size_correct = 0;

  size = (align_size - (stack_args_offset(function) % align_size)) % align_size;
  size += correct_size;

  return size;
}

static int asm_symbol_function(FILE *out, symbol_t symbol)
{
  syntax_t syntax;
  objentry_t entry;
  symbol_t s;
  int i, n;

  /* padding for alignment of stack */
  if (symbol->obj.function.stack->size_correct < 0)
    symbol->obj.function.stack->size_correct = correct_size(symbol);

  asm_queue_format_section(out, ASM_FORMAT_TYPE_SECTION_DATA);

  asm_queue_format_align(out, symbol->obj.function.stack->align);
  asm_queue_format_type(out, symbol->obj.function.initlabel->obj.label.name, asm_info_type_mark(), ASM_FORMAT_TYPE_TYPE_OBJECT);
  asm_queue_format_label(out, symbol->obj.function.initlabel->obj.label.name);
  asm_symbol_array(out, symbol->obj.function.stack->symbols);
  asm_queue_format_newline(out);

  asm_queue_format_section(out, ASM_FORMAT_TYPE_SECTION_TEXT);

  saveline(symbol->line);

  asm_queue_format_align(out, 4);
  if (symbol->scope != C_TYPE_STATIC)
    asm_queue_format_globl(out, symbol->name);
  asm_queue_format_type(out, symbol->name, asm_info_type_mark(), ASM_FORMAT_TYPE_TYPE_FUNCTION);
  asm_queue_format_label(out, symbol->name);

  if ((symbol->value != NULL) &&
      (symbol->value->type == C_TYPE_BLOCK)) {
    syntax = symbol->value->obj.block.syntax;
    if (syntax->type == C_TYPE_BLOCK) {
      asm_queue_code_function_start(out);
      asm_queue_code_stack_expand(out, stack_vararg_size());
      asm_queue_code_function_register_save(out);
      asm_queue_code_stack_expand(out, stack_saveregs_offset(symbol));

      n = asm_info_funccall_args_reg_number();
      for (i = 0; i < n; i++) {
	asm_queue_code_funccall_reg_load(out, i);
	asm_queue_code_stack_store(out, stack_vararg_offset(symbol) + i * asm_info_word_size(), NULL);
      }

      if (!(symbol->flags & SYMBOL_FLAG_FUNCNOINIT)) {
	if (asm_queue_code_get_address_stack_r1(out, stack_variable_offset()) < 0) {
	  asm_queue_code_get_address_stack(out, stack_variable_offset());
	  asm_queue_code_set_r1(out);
	}
	asm_queue_code_get_address(out, symbol->obj.function.initlabel->obj.label.name);
	call_memcpy(out, symbol->obj.function.stack->size_init);
      }

      n = 0;
      for (entry = objlist_get_head(symbol->obj.function.args);
	   !objlist_is_end(symbol->obj.function.args, entry);
	   entry = objentry_get_next(entry)) {
	s = objentry_get_obj(entry);
	asm_symbol_function_args_get(out, symbol, n++);
	address_get_r1(out, s, symbol);
	if ((s->model != NULL) && !model_is_address(s->model) &&
	    ((s->model->type == C_TYPE_STRUCT) ||
	     (s->model->type == C_TYPE_UNION))) {
	  call_memcpy(out, model_get_size(s->model));
	} else {
	  asm_queue_code_memory_store(out, 0, s->model);
	}
      }
      asm_syntax_list(out, syntax->obj.block.syntaxes, symbol);
      asm_queue_format_label(out, symbol->obj.function.retlabel->obj.label.name);

      asm_queue_code_stack_reduce(out, stack_saveregs_offset(symbol));
      asm_queue_code_function_register_load(out);
      asm_queue_code_stack_reduce(out, stack_vararg_size());
      asm_queue_code_function_end(out);
    }
  }

  return 0;
}

static int asm_symbol_label_header(FILE *out, symbol_t symbol)
{
  asm_queue_format_section(out, ASM_FORMAT_TYPE_SECTION_DATA);

  saveline(symbol->line);

  if (symbol->scope != C_TYPE_STATIC)
    asm_queue_format_globl(out, symbol->name);
  asm_queue_format_align(out, model_get_align(symbol->model));
  asm_queue_format_type(out, symbol->name, asm_info_type_mark(), ASM_FORMAT_TYPE_TYPE_OBJECT);
  asm_queue_format_size(out, symbol->name, model_get_size(symbol->model));

  asm_queue_format_label(out, symbol->name);

  return 0;
}

static int asm_symbol_label_value(FILE *out, symbol_t symbol)
{
  if (symbol->value == NULL) {
    asm_queue_format_comment(out, "SPACE", symbol_name(symbol), NULL, NULL);
    asm_queue_format_space(out, model_get_size(symbol->model));
  } else {
    switch (symbol->value->type) {
    case C_TYPE_STRING:
      asm_queue_format_string(out, symbol->value->obj.string.s, symbol->value->obj.string.size - 1);
      asm_queue_format_newline(out);
      break;
    case C_TYPE_ARRAY:
      asm_symbol_array(out, symbol->value->obj.array.values);
      asm_queue_format_newline(out);
      break;
    default:
      ERREXIT(1);
    }
  }

  return 0;
}

static int asm_symbol_label(FILE *out, symbol_t symbol)
{
  asm_symbol_label_header(out, symbol);
  asm_symbol_label_value(out, symbol);
  return 0;
}

static int asm_symbol_variable_header(FILE *out, symbol_t symbol)
{
  asm_queue_format_section(out, ASM_FORMAT_TYPE_SECTION_DATA);

  saveline(symbol->line);

  if (symbol->scope != C_TYPE_STATIC)
    asm_queue_format_globl(out, symbol->name);
  asm_queue_format_align(out, model_get_align(symbol->model));
  asm_queue_format_type(out, symbol->name, asm_info_type_mark(), ASM_FORMAT_TYPE_TYPE_OBJECT);
  asm_queue_format_size(out, symbol->name, model_get_size(symbol->model));

  asm_queue_format_label(out, symbol->name);

  return 0;
}

static int asm_symbol_variable_value(FILE *out, symbol_t symbol)
{
  int size = 0;
  c_type_t type;
  asm_format_type_t ftype;

  type = model_is_pointer(symbol->model) ? C_TYPE_LONG : symbol->model->type;

  asm_queue_format_comment(out, symbol_name(symbol), NULL, NULL, NULL);

  switch (type) {
  case C_TYPE_STRUCT:
    if ((symbol->value != NULL) && (symbol->value->type == C_TYPE_ARRAY)) {
      asm_queue_format_newline(out);
      asm_symbol_array(out, symbol->value->obj.array.values);
    } else {
      asm_symbol_area(out, symbol->model->custom._struct->members);
    }
    return 0;
  case C_TYPE_UNION:
    asm_symbol_area(out, symbol->model->custom._union->members);
    return 0;
  default:
    break;
  }

  switch (type) {
  case C_TYPE_VOID: /* (void *) */
    size = asm_info_word_size();
    goto setftype;

  case C_TYPE_CHAR:
  case C_TYPE_SHORT:
  case C_TYPE_INT:
  case C_TYPE_ENUM:
  case C_TYPE_LONG:
  case C_TYPE_LONGLONG:
  case C_TYPE_FLOAT:
  case C_TYPE_DOUBLE:
  case C_TYPE_LONGDOUBLE:
    size = model_get_size(symbol->model);
  setftype:
    switch (size) {
    case 1:  ftype = ASM_FORMAT_TYPE_VALUE_BYTE;  size -= 1; break;
    case 2:  ftype = ASM_FORMAT_TYPE_VALUE_SHORT; size -= 2; break;
    case 4:  ftype = ASM_FORMAT_TYPE_VALUE_INT;   size -= 4; break;
    case 8:
    default: ftype = ASM_FORMAT_TYPE_VALUE_QUAD;  size -= 8; break;
    }
    break;

  default:
    ERREXIT(1);
  }

  if ((symbol->value == NULL) || (symbol->flags & SYMBOL_FLAG_TEMPORARY)) {
    asm_queue_format_value(out, ftype, 0, NULL);
  } else {
    switch (symbol->value->type) {
    case C_TYPE_ARG:     asm_queue_format_value(out, ftype, 0, NULL); break;
    case C_TYPE_NUMBER:  asm_queue_format_value(out, ftype, symbol->value->obj.number, NULL); break;
    case C_TYPE_LABEL:   asm_queue_format_value(out, ftype, 0, symbol->value->obj.label.symbol->name); break;
    case C_TYPE_ADDRESS: asm_queue_format_value(out, ftype, 0, symbol->value->obj.label.symbol->name); break;
    default:
      ERREXIT(1);
    }
  }

  for (; size > 0; size -= 4)
    asm_queue_format_dummy(out);

  return 0;
}

static int asm_symbol_variable(FILE *out, symbol_t symbol)
{
  asm_symbol_variable_header(out, symbol);
  asm_symbol_variable_value(out, symbol);
  asm_queue_format_newline(out);
  return 0;
}

static int asm_symbol_builtin(symbol_t symbol)
{
  model_t model;
  int ext_char = 0, ext_uchar = 0, ext_short = 0, ext_ushort = 0, ext_int = 0, ext_uint = 0;
  int inv = 0, minus = 0, xor = 0, lshift = 0, rshift = 0, div = 0, mod = 0, mul = 0;

  model = symbol->model;

  if (asm_queue_code_sign_extension_char(NULL) < 0)
    ext_char = 1;
  if (asm_queue_code_sign_extension_uchar(NULL) < 0)
    ext_uchar = 1;
  if (asm_queue_code_sign_extension_short(NULL) < 0)
    ext_short = 1;
  if (asm_queue_code_sign_extension_ushort(NULL) < 0)
    ext_ushort = 1;
  if (asm_queue_code_sign_extension_int(NULL) < 0)
    ext_int = 1;
  if (asm_queue_code_sign_extension_uint(NULL) < 0)
    ext_uint = 1;

  if ((asm_queue_code_calc_inv(NULL, model, model) < 0) &&
      (asm_queue_code_calc_op1(NULL, C_TYPE_INV, model, model) < 0))
    inv = 1;

  if ((asm_queue_code_calc_minus(NULL, model, model) < 0) &&
      (asm_queue_code_calc_op1(NULL, C_TYPE_MINUS, model, model) < 0))
    minus = 1;

  if ((asm_queue_code_calc_xor(NULL, model, model, model) < 0) &&
      (asm_queue_code_calc_op2(NULL, C_TYPE_XOR, model, model, model) < 0))
    xor = 1;

  if ((asm_queue_code_calc_llshift(NULL, model, model, model) < 0) &&
      (asm_queue_code_calc_op2(NULL, C_TYPE_LSHIFT, model, model, model) < 0))
    lshift = 1;

  if (((asm_queue_code_calc_rlshift(NULL, model, model, model) < 0) ||
       (asm_queue_code_calc_rashift(NULL, model, model, model) < 0)) &&
      (asm_queue_code_calc_op2(NULL, C_TYPE_RSHIFT, model, model, model) < 0))
    rshift = 1;

  if ((asm_queue_code_calc_div(NULL, model, model, model) < 0) &&
      (asm_queue_code_calc_op2(NULL, C_TYPE_DIV, model, model, model) < 0))
    div = 1;

  if ((asm_queue_code_calc_mod(NULL, model, model, model) < 0) &&
      (asm_queue_code_calc_op2(NULL, C_TYPE_MOD, model, model, model) < 0))
    mod = 1;

  if ((asm_queue_code_calc_mul(NULL, model, model, model) < 0) &&
      (asm_queue_code_calc_op2(NULL, C_TYPE_MUL, model, model, model) < 0))
    mul = 1;

  if (!strcmp(symbol->name, "__builtin_memcpy16"))
    return 0;

  if (!strcmp(symbol->name, "__builtin_extension_char"))
    return ext_char;
  if (!strcmp(symbol->name, "__builtin_extension_uchar"))
    return ext_uchar;
  if (!strcmp(symbol->name, "__builtin_extension_short"))
    return ext_short;
  if (!strcmp(symbol->name, "__builtin_extension_ushort"))
    return ext_ushort;
  if (!strcmp(symbol->name, "__builtin_extension_int"))
    return ext_int;
  if (!strcmp(symbol->name, "__builtin_extension_uint"))
    return ext_uint;

  if (!strcmp(symbol->name, "__builtin_inv"))
    return inv;

  if (!strcmp(symbol->name, "__builtin_minus"))
    return minus;

  if (!strcmp(symbol->name, "__builtin_xor_uint") ||
      !strcmp(symbol->name, "__builtin_xor_ulong"))
    return xor;

  if (!strcmp(symbol->name, "__builtin_mask") ||
      !strcmp(symbol->name, "__builtin_mask_array") ||
      !strcmp(symbol->name, "__builtin_ulong_size"))
    return (lshift || rshift) ? 1 : 0;

  if (!strcmp(symbol->name, "__builtin_ashlsi3") ||
      !strcmp(symbol->name, "__builtin_lshlsi3") ||
      !strcmp(symbol->name, "__builtin_ashldi3") ||
      !strcmp(symbol->name, "__builtin_lshldi3"))
    return lshift;

  if (!strcmp(symbol->name, "__builtin_ashrsi3") ||
      !strcmp(symbol->name, "__builtin_lshrsi3") ||
      !strcmp(symbol->name, "__builtin_ashrdi3") ||
      !strcmp(symbol->name, "__builtin_lshrdi3")) {
    return rshift;
  }

  if (!strcmp(symbol->name, "__builtin_div_ulong"))
    return (div || mod) ? 1 : 0;

  if (!strcmp(symbol->name, "__builtin_divsi3") ||
      !strcmp(symbol->name, "__builtin_udivsi3") ||
      !strcmp(symbol->name, "__builtin_divdi3") ||
      !strcmp(symbol->name, "__builtin_udivdi3"))
    return div;

  if (!strcmp(symbol->name, "__builtin_modsi3") ||
      !strcmp(symbol->name, "__builtin_umodsi3") ||
      !strcmp(symbol->name, "__builtin_moddi3") ||
      !strcmp(symbol->name, "__builtin_umoddi3"))
    return mod;

  if (!strcmp(symbol->name, "__builtin_mulsi3") ||
      !strcmp(symbol->name, "__builtin_umulsi3") ||
      !strcmp(symbol->name, "__builtin_muldi3") ||
      !strcmp(symbol->name, "__builtin_umuldi3") ||
      !strcmp(symbol->name, "__builtin_mul_uint") ||
      !strcmp(symbol->name, "__builtin_mul_ulong"))
    return mul;

  if (!strcmp(symbol->name, "__builtin_bswap16"))
    return 0;

  if (!strcmp(symbol->name, "__builtin_bswap32"))
    return 0;

  if (!strcmp(symbol->name, "__builtin_bswap64"))
    return 0;

  return 1;
}

int asm_symbol(FILE *out, symbol_t symbol)
{
  saveline(symbol->line);

  if (!asm_symbol_builtin(symbol))
    return 0;

  switch (symbol->type) {
  case C_TYPE_LABEL:
    if (symbol->flags & SYMBOL_FLAG_FUNCTION) {
      if (symbol->flags & SYMBOL_FLAG_FUNCBLOCK)
	asm_symbol_function(out, symbol);
      break;
    }
    asm_symbol_label(out, symbol);
    break;
  case C_TYPE_VARIABLE:
    asm_symbol_variable(out, symbol);
    break;
  default:
    ERREXIT(1);
  }

  return 0;
}
