/*
 * Copyright 2017, The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef TRANSFORMER_H
#define TRANSFORMER_H

#include <vector>

#include "instructions.h"
#include "pass.h"
#include "visitor.h"
#include "word_stream.h"

namespace android {
namespace spirit {

// Transformer is the base class for a transformation that transforms a Module.
// An instance of a derived class can be added to a PassQueue and applied to a
// Module.
class Transformer : public Pass, public DoNothingVisitor {
public:
  Transformer()
      : mStreamFunctions(WordStream::Create()),
        mStreamFinal(WordStream::Create()) {}

  virtual ~Transformer() {}

  Module *run(Module *m, int *error = nullptr) override;

  std::vector<uint32_t> runAndSerialize(Module *module,
                                        int *error = nullptr) override;

  // Returns the module being transformed
  Module *getModule() const { return mModule; }

  // Inserts a new instruction before the current instruction.
  // Call this from a transform() method in a derived class.
  void insert(Instruction *);

  void visit(FunctionDefinition *fdef) override {
    mShouldRecord = (mVisit == 0);
    DoNothingVisitor::visit(fdef);
  }

  // Transforms the current instruction into a new instruction as specified by
  // the return value. If returns nullptr, deletes the current instruction.
  // Override this in a derived class for desired behavior.
#define HANDLE_INSTRUCTION(OPCODE, INST_CLASS)                                 \
  virtual Instruction *transform(INST_CLASS *inst) {                           \
    return static_cast<Instruction *>(inst);                                   \
  }                                                                            \
  virtual void visit(INST_CLASS *inst) {                                       \
    if (!mShouldRecord) {                                                      \
      return;                                                                  \
    }                                                                          \
    if (Instruction *transformed = transform(inst)) {                          \
      transformed->Serialize(*mStream);                                        \
    }                                                                          \
  }
#include "instruction_dispatches_generated.h"
#undef HANDLE_INSTRUCTION

private:
  Module *mModule;
  int mVisit;
  bool mShouldRecord;
  std::unique_ptr<WordStream> mStreamFunctions;
  std::unique_ptr<WordStream> mStreamFinal;
  WordStream *mStream;
};

} // namespace spirit
} // namespace android

#endif // TRANSFORMER_H