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