// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void IndirectCallPromotion::MergeReturnedValues(BlockList& callBlocks, 
                                                Block* continuationBlock,
                                                CallInstr* instr) {
    // If the call returns 'void' or the result is not used
    // there are no values to merge.
    if(instr->IsVoid() || (instr->HasDestinationOp() == false)) {
        return;
    }

    auto unit = callBlocks[0]->ParentFunction()->ParentUnit();
    auto& references = unit->References();

    // Create the 'phi' that merges the returned values.
    auto phiResultOp = Temporary::GetTemporary(instr->ResultOp()->GetType());
    auto phiInstr = PhiInstr::GetPhi(phiResultOp, callBlocks.Count());
    continuationBlock->InsertInstructionFirst(phiInstr);

    // Now add the incoming operands.
    for(int i = 0; i < callBlocks.Count(); i++) {
        auto beforeGoto = callBlocks[i]->LastInstruction()->PreviousInstruction();
        auto callInstr = beforeGoto->As<CallInstr>();
        
        DebugValidator::IsNotNull(callInstr);
        DebugValidator::IsNotNull(callInstr->ResultOp());

        auto blockRef = references.GetBlockRef(callBlocks[i]);
        phiInstr->AddOperand(callInstr->ResultOp(), blockRef);
    }

    // The original returned value is replaced by the 'phi' result.
    instr->ResultOp()->ReplaceWith(phiResultOp);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void IndirectCallPromotion::ConnectGeneratedBlocks(BlockList& testBlocks, 
                                                   BlockList& callBlocks) {
    // Connect the blocks based on the following pattern:
    // TEST_BLOCK0:
    //     t0 = ucmp targetOp, FUNCT_REF0
    //     if t0, CALL_BLOCK0, TEST_BLOCK1
    // CALL_BLOCK0:
    //     call FUNCT_REF0
    //     goto CONTINUATION_BLOCK
    // TEST_BLOCK1:
    //     t1 = ucmp targetOp, FUNCT_REF1
    //     if t1, CALL_BLOCK1, TEST_BLOCK2
    // ...
    // CALL_BLOCK_N:   // only if unpromoted targets
    //     call targetOp
    //     goto CONTINUATION_BLOCK
    // CONTINUATION_BLOCK:
    auto unit = callBlocks[0]->ParentFunction()->ParentUnit();
    auto& references = unit->References();

    for(int i = 0; i < testBlocks.Count(); i++) {
        auto testBlock = testBlocks[i];
        auto ucmpResultOp = testBlocks[i]->LastInstruction()->GetDestinationOp();
        auto trueBlockRef = references.GetBlockRef(callBlocks[i]);
        Block* falseBlock;
        
        if((i + 1) < testBlocks.Count()) {
            // There is a next target test.
            falseBlock = testBlocks[i + 1];
        }
        else {
            // Unpromoted targets exist, always do the call.
            falseBlock = callBlocks[i + 1];
        }

        auto falseBlockRef = references.GetBlockRef(falseBlock);
        auto ifInstr = IfInstr::GetIf(ucmpResultOp, trueBlockRef, falseBlockRef);
        testBlock->InsertInstruction(ifInstr);
    }
}