//===-- lib/Transforms/Scalar/LowerConstantExprs.cpp ------------*- C++ -*-===//
// 
//                     The LLVM Compiler Infrastructure
//
// This file was written by Vladimir Prus and is distributed under
// the University of Illinois Open Source License. See LICENSE.TXT for details.
// 
//===----------------------------------------------------------------------===//
//
// This file defines the LowerConstantExpression pass, which converts all
// constant expressions into instructions. This is primarily usefull for 
// code generators which don't yet want or don't have a need to handle
// constant expressions themself.
//
//===----------------------------------------------------------------------===//

#include "llvm/Pass.h"
#include "llvm/Function.h"
#include "llvm/Constants.h"
#include "llvm/Instructions.h"
#include "llvm/Support/InstIterator.h"
#include <vector>
#include <iostream>

using namespace llvm;
using namespace std;

namespace {

    class ConstantExpressionsLower : public FunctionPass {
    private: // FunctionPass overrides
        
        bool runOnFunction(Function& f);

    private: // internal methods

        /// For all operands of 'insn' which are constant expressions, generates
        /// an appropriate instruction and replaces the use of constant 
        /// expression with the use of the generated instruction.
        bool runOnInstruction(Instruction& insn);

        /// Given an constant expression 'c' which occures in 'instruction',
        /// at position 'pos', 
        /// generates instruction to compute 'c' and replaces the use of 'c'
        /// with the use of that instruction. This handles only top-level
        /// expression in 'c', any subexpressions are not handled.
        Instruction* convert(const ConstantExpr& c, Instruction* where);
    };

    RegisterOpt<ConstantExpressionsLower> X(
        "lowerconstantexprs", "Lower constant expressions");    
}

bool ConstantExpressionsLower::runOnFunction(Function& f)
{
    bool modified = false;
    for (inst_iterator i = inst_begin(f), e = inst_end(f); i != e; ++i)
    {
        modified |= runOnInstruction(*i);
    }
    return modified;
}

bool ConstantExpressionsLower::runOnInstruction(Instruction& instruction)
{
    bool modified = false;
    for (unsigned pos = 0; pos < instruction.getNumOperands(); ++pos)
    {
        if (ConstantExpr* ce 
            = dyn_cast<ConstantExpr>(instruction.getOperand(pos))) {

            // Decide where to insert the new instruction
            Instruction* where = &instruction;

            // For PHI nodes we can't insert new instruction before phi, 
            // since phi should always come at the beginning of the 
            // basic block.
            // So, we need to insert it in the predecessor, right before
            // the terminating instruction.
            if (PHINode* p = dyn_cast<PHINode>(&instruction)) {
                BasicBlock* predecessor = 0;
                for(unsigned i = 0; i < p->getNumIncomingValues(); ++i)
                    if (p->getIncomingValue(i) == ce) {
                        predecessor = p->getIncomingBlock(i);
                        break;
                    }
                assert(predecessor && "could not find predecessor");
                where = predecessor->getTerminator();
            }
            Instruction* n = convert(*ce, where);

            // Note: we can't call replaceAllUsesWith, since
            // that might replace uses in another functions,
            // where the instruction(s) we've generated are not
            // available. 
                    
            // Moreover, we can't replace all the users in the same 
            // function, because we can't be sure the definition 
            // made in this block will be available in other
            // places where the constant is used.                                       
            instruction.setOperand(pos, n);

            // The new instruction might have constant expressions in
            // it. Extract them too.
            runOnInstruction(*n);
            modified = true;
        }
    }            
    return modified;
}

Instruction* 
ConstantExpressionsLower::convert(const ConstantExpr& c, Instruction* where)
{
    Instruction* result = 0;

    if (c.getOpcode() >= Instruction::BinaryOpsBegin &&
        c.getOpcode() < Instruction::BinaryOpsEnd)
    {
        result = BinaryOperator::create(
            static_cast<Instruction::BinaryOps>(c.getOpcode()), 
            c.getOperand(0), c.getOperand(1), "", where);
    }
    else
    {
        switch(c.getOpcode()) {
        case Instruction::GetElementPtr: 
        {
            vector<Value*> idx;
            for (unsigned i = 1; i < c.getNumOperands(); ++i)
                idx.push_back(c.getOperand(i));
            result = new GetElementPtrInst(c.getOperand(0),
                                           idx, "", where);
            break;
        }

        case Instruction::Cast:
            result = new CastInst(c.getOperand(0), c.getType(), "", 
                                  where);
            break;


        case Instruction::Shl:
        case Instruction::Shr:
            result = new ShiftInst(
                static_cast<Instruction::OtherOps>(c.getOpcode()), 
                c.getOperand(0), c.getOperand(1), "", where);
            break;
                    
        case Instruction::Select:
            result = new SelectInst(c.getOperand(0), c.getOperand(1),
                                    c.getOperand(2), "", where);
            break;
                 
        default:
            std::cerr << "Offending expr: " << c << "\n";
            assert(0 && "Constant expression not yet handled!\n");
        }
    }
    return result;
}



namespace llvm {
    FunctionPass* createLowerConstantExpressionsPass()
    {
        return new ConstantExpressionsLower;
    }
}
