src/lima/gp_ir/regalloc.c
/* Register allocation within the scheduler
 *
 * The scheduler can spill intermediate results to registers in case it cannot
 * schedule a node, in which case it needs to allocate a register for the
 * intermediate result. Here, we can assume three things:
 * 1. The register is always of size 1 (i.e. is scalar)
 * 2. There is only one definition
 * 3. All uses are in the same basic block as the definition
 * We allocate registers one-by-one within the scheduler, looking for free
 * physical registers, choosing one, filling in the liveness info, and adding
 * any "false dependencies" introduced by the allocation.
 *
 * Unfortunately, when in the middle of scheduling, we must be careful not to
 * introduce any dependencies that cannot be fulfilled by the scheduler.
 * For example, we can't insert a write to register 0.x if there is another
 * write to 0.x beforehand that the scheduler hasn't scheduled yet or another
 * write to 0.x afterwards that the scheduler has already scheduled. Rather than
 * doing a costly analysis pass, we simply make sure that each register we
 * allocate is never used in that block. This also makes register allocation a
 * lot faster, as we only have to pre-compute the set of available registers at
 * the beginning of the schedule and then choose an available register. If we
 * cannot find a register that way, then we can fall back to the more expensive
 * way which can add dependencies (or spill the register to a temporary) and
 * then re-schedule the block.
 */
Register allocation within the scheduler
The scheduler can spill intermediate results to registers in case it cannot
schedule a node, in which case it needs to allocate a register for the
intermediate result. Here, we can assume three things:
1. The register is always of size 1
2. There is only one definition
3. All uses are in the same basic block as the definition
We allocate registers one-by-one within the scheduler, looking for free
physical registers, choosing one, filling in the liveness info, and adding
any "false dependencies" introduced by the allocation.

Unfortunately, when in the middle of scheduling, we must be careful not to
introduce any dependencies that cannot be fulfilled by the scheduler.
For example, we can't insert a write to register 0.x if there is another
write to 0.x beforehand that the scheduler hasn't scheduled yet or another
write to 0.x afterwards that the scheduler has already scheduled. Rather than
doing a costly analysis pass, we simply make sure that each register we
allocate is never used in that block. This also makes register allocation a
lot faster, as we only have to pre-compute the set of available registers at
the beginning of the schedule and then choose an available register. If we
cannot find a register that way, then we can fall back to the more expensive
way which can add dependencies (or spill the register to a temporary) and
then re-schedule the block.
Register allocation within the scheduler The scheduler can spill intermediate results to registers in case it cannot schedule a node in which case it needs to allocate a register for the intermediate result . Here we can assume three things : 1 . The register is always of size 1 2 . There is only one definition 3 . All uses are in the same basic block as the definition We allocate registers one - by - one within the scheduler looking for free physical registers choosing one filling in the liveness info and adding any " false dependencies " introduced by the allocation . Unfortunately when in the middle of scheduling we must be careful not to introduce any dependencies that cannot be fulfilled by the scheduler . For example we can ' t insert a write to register 0 . x if there is another write to 0 . x beforehand that the scheduler hasn ' t scheduled yet or another write to 0 . x afterwards that the scheduler has already scheduled . Rather than doing a costly analysis pass we simply make sure that each register we allocate is never used in that block . This also makes register allocation a lot faster as we only have to pre - compute the set of available registers at the beginning of the schedule and then choose an available register . If we cannot find a register that way then we can fall back to the more expensive way which can add dependencies ( or spill the register to a temporary ) and then re - schedule the block .
bitset_t lima_gp_ir_regalloc_get_free_regs(lima_gp_ir_block_t* block)
{
	bitset_t ret = bitset_create_full(16*4);
	lima_gp_ir_root_node_t* node;
	gp_ir_block_for_each_node(block, node)
	{
		bitset_subtract(&ret, node->live_phys_after);
	}
	bitset_subtract(&ret, block->live_phys_before);
	return ret;
}
bitset_t lima_gp_ir_regalloc_get_free_regs ( lima_gp_ir_block_t * block ) { bitset_t ret = bitset_create_full ( 16 * 4 ) ; lima_gp_ir_root_node_t * node ; gp_ir_block_for_each_node ( block , node ) { bitset_subtract ( & ret , node -> live_phys_after ) ; } bitset_subtract ( & ret , block -> live_phys_before ) ; return ret ; }
Register allocation within the scheduler
 The scheduler can spill intermediate results to registers in case it cannot
 schedule a node, in which case it needs to allocate a register for the
 intermediate result.
Register allocation within the scheduler The scheduler can spill intermediate results to registers in case it cannot schedule a node in which case it needs to allocate a register for the intermediate result .