static void cg_scratch_var()

in sources/cg_c.c [707:809]


static void cg_scratch_var(ast_node *ast, sem_t sem_type, charbuf *var, charbuf *is_null, charbuf *value) {
  Contract(is_unitary(sem_type));
  Contract(!is_null_type(sem_type));

  sem_t core_type = core_type_of(sem_type);
  sem_type &= (SEM_TYPE_CORE | SEM_TYPE_NOTNULL);

  Contract(stack_level < CQL_MAX_STACK);

  // try to avoid creating a scratch variable if we can use the target of an assignment in flight.
  if (is_assignment_target_reusable(ast, sem_type)) {
    Invariant(ast && ast->parent && ast->parent->left);
    EXTRACT_ANY_NOTNULL(name_ast, ast->parent->left);
    EXTRACT_STRING(name, name_ast);
    if (is_out_parameter(name_ast->sem->sem_type)) {
      bprintf(var, "*%s", name);
    }
    else {
      bprintf(var, "%s", name);
    }
  }
  else {
    // Generate a scratch variable name of the correct type.  We don't generate
    // the declaration of any given scratch variable more than once.  We use the
    // current stack level to make the name.  This means that have to burn a stack level
    // if you want more than one scratch.  Stacklevel is normally increased by
    // the CG_PUSH_EVAL macro which does the recursion but it can also be manually
    // increased if temporaries are needed for some other reason.  Any level of
    // recursion is expected to fix all that.

    CSTR prefix;

    cg_type_masks *pmask;
    if (is_nullable(sem_type)) {
      pmask = &cg_current_masks->nullables;
      prefix = "_tmp_n";
    }
    else {
      pmask = &cg_current_masks->notnullables;
      prefix = "_tmp";
    }

    uint64_t *usedmask;

    switch (core_type) {
      case SEM_TYPE_INTEGER:
        bprintf(var, "%s_int_%d", prefix, stack_level);
        usedmask = pmask->ints;
        break;
      case SEM_TYPE_BLOB:
        bprintf(var, "%s_blob_%d", prefix, stack_level);
        usedmask = pmask->blobs;
        break;
      case SEM_TYPE_OBJECT:
        bprintf(var, "%s_object_%d", prefix, stack_level);
        usedmask = pmask->objects;
        break;
      case SEM_TYPE_TEXT:
        bprintf(var, "%s_text_%d", prefix, stack_level);
        usedmask = pmask->strings;
        break;
      case SEM_TYPE_LONG_INTEGER:
        bprintf(var, "%s_int64_%d", prefix, stack_level);
        usedmask = pmask->longs;
        break;
      case SEM_TYPE_REAL:
        bprintf(var, "%s_double_%d", prefix, stack_level);
        usedmask = pmask->reals;
        break;
      case SEM_TYPE_BOOL:
        bprintf(var, "%s_bool_%d", prefix, stack_level);
        usedmask = pmask->bools;
        break;
    }

    int32_t index = stack_level/64;
    uint64_t mask = ((uint64_t)1) << (stack_level % 64);

    // Emit scratch if needed.
    if (!(usedmask[index] & mask)) {
      cg_var_decl(cg_scratch_vars_output, sem_type, var->ptr, CG_VAR_DECL_LOCAL);
      usedmask[index] |= mask;
    }
  }

  // If the is_null and value expressions are desired, generate them here.
  if (is_null && value) {
    if (is_ref_type(sem_type)) {
      // note that because reference types begin initialized to null we have to check their
      // value even though they are "non-null" so the is_null expression can't be 0 for these ever.
      bprintf(is_null, "!%s", var->ptr);
      bprintf(value, "%s", var->ptr);
    }
    else if (is_not_nullable(sem_type)) {
      bprintf(is_null, "0");
      bprintf(value, "%s", var->ptr);
    }
    else {
      bprintf(is_null, "%s.is_null", var->ptr);
      bprintf(value, "%s.value", var->ptr);
    }
  }
}