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);
}
}
}