+/* Local-value allocator
+ * After finishing creating the liferange of all values used in a function
+ * we can allocate their global-positions.
+ * This is the counterpart to register-allocation in register machines.
+ */
+typedef struct {
+ MEM_VECTOR_MAKE(ir_value*, locals);
+ MEM_VECTOR_MAKE(size_t, sizes);
+ MEM_VECTOR_MAKE(size_t, positions);
+} function_allocator;
+MEM_VEC_FUNCTIONS(function_allocator, ir_value*, locals)
+MEM_VEC_FUNCTIONS(function_allocator, size_t, sizes)
+MEM_VEC_FUNCTIONS(function_allocator, size_t, positions)
+
+static bool function_allocator_alloc(function_allocator *alloc, const ir_value *var)
+{
+ ir_value *slot;
+ size_t vsize = type_sizeof[var->vtype];
+
+ slot = ir_value_var("reg", store_global, var->vtype);
+ if (!slot)
+ return false;
+
+ if (!ir_value_life_merge_into(slot, var))
+ goto localerror;
+
+ if (!function_allocator_locals_add(alloc, slot))
+ goto localerror;
+
+ if (!function_allocator_sizes_add(alloc, vsize))
+ goto localerror;
+
+ return true;
+
+localerror:
+ ir_value_delete(slot);
+ return false;
+}
+
+bool ir_function_allocate_locals(ir_function *self)
+{
+ size_t i, a;
+ bool retval = true;
+ size_t pos;
+
+ ir_value *slot;
+ const ir_value *v;
+
+ function_allocator alloc;
+
+ MEM_VECTOR_INIT(&alloc, locals);
+ MEM_VECTOR_INIT(&alloc, sizes);
+ MEM_VECTOR_INIT(&alloc, positions);
+
+ for (i = 0; i < self->locals_count; ++i)
+ {
+ if (!function_allocator_alloc(&alloc, self->locals[i]))
+ goto error;
+ }
+
+ /* Allocate a slot for any value that still exists */
+ for (i = 0; i < self->values_count; ++i)
+ {
+ v = self->values[i];
+
+ if (!v->life_count)
+ continue;
+
+ for (a = 0; a < alloc.locals_count; ++a)
+ {
+ slot = alloc.locals[a];
+
+ if (ir_values_overlap(v, slot))
+ continue;
+
+ if (!ir_value_life_merge_into(slot, v))
+ goto error;
+
+ /* adjust size for this slot */
+ if (alloc.sizes[a] < type_sizeof[v->vtype])
+ alloc.sizes[a] = type_sizeof[v->vtype];
+
+ self->values[i]->code.local = a;
+ break;
+ }
+ if (a >= alloc.locals_count) {
+ self->values[i]->code.local = alloc.locals_count;
+ if (!function_allocator_alloc(&alloc, v))
+ goto error;
+ }
+ }
+
+ /* Adjust slot positions based on sizes */
+ if (!function_allocator_positions_add(&alloc, 0))
+ goto error;
+
+ for (i = 1; i < alloc.sizes_count; ++i)
+ {
+ pos = alloc.positions[i-1] + alloc.sizes[i-1];
+ if (!function_allocator_positions_add(&alloc, pos))
+ goto error;
+ }
+
+ self->allocated_locals = pos + alloc.sizes[alloc.sizes_count-1];
+
+ /* Take over the actual slot positions */
+ for (i = 0; i < self->values_count; ++i)
+ self->values[i]->code.local = alloc.positions[self->values[i]->code.local];
+
+ goto cleanup;
+
+error:
+ retval = false;
+cleanup:
+ for (i = 0; i < alloc.locals_count; ++i)
+ ir_value_delete(alloc.locals[i]);
+ MEM_VECTOR_CLEAR(&alloc, locals);
+ MEM_VECTOR_CLEAR(&alloc, sizes);
+ MEM_VECTOR_CLEAR(&alloc, positions);
+ return retval;
+}
+