Skip to content

File instr_op.h

File List > projects > simtix > src > simtix > sm > instr_op.h

Go to the documentation of this file

#pragma once

#include <cstdint>
#include <vector>

#include "sm/instr.h"
#include "sm/thread.h"
#include "sm/warp.h"

namespace simtix {

class InstrOp : public Instr {
 public:
  using Op = void (InstrOp::*)();

  // Implementation of Instr's virtual methods
  void Decode() override;

  void Issue() override;

  void OperandCollect() override;

  void Execute() override;

  void Commit() override;

  void Reset() override;

  void Assign(const Instr *other) override;

  bool CanIssue() const override;

  bool CanExecute() const override {
    return rs1_data_ready_ && rs2_data_ready_;
  }

  bool CanCommit() const override { return executed_; }

  bool CanRetire() const override { return committed_; }

 protected:
  InstrOp(Warp *warp, uint32_t iword, uint64_t wpc);

  void Reinitialize(Warp *warp, uint32_t iword, uint64_t wpc) override;

  int64_t imm_ = 0;

  Op op_ = nullptr;

  bool rs1_data_ready_ = false;
  bool rs2_data_ready_ = false;
  bool executed_ = false;
  bool committed_ = false;

  // Register data
  std::vector<int64_t> rs1_data_;
  std::vector<int64_t> rs2_data_;
  std::vector<int64_t> rd_data_;

 private:
  // Operations defined in this Opcode
  void add_() {
    for (Thread *t : active_threads_) {
      uint32_t tid = t->tid();
      rd_data_[tid] = (rs1_data_[tid] + rs2_data_[tid]);
    }
  }

  void sub_() {
    for (Thread *t : active_threads_) {
      uint32_t tid = t->tid();
      rd_data_[tid] = (rs1_data_[tid] - rs2_data_[tid]);
    }
  }

  void slt_() {
    for (Thread *t : active_threads_) {
      uint32_t tid = t->tid();
      rd_data_[tid] = (rs1_data_[tid] < rs2_data_[tid]) ? 1 : 0;
    }
  }

  void sltu_() {
    for (Thread *t : active_threads_) {
      uint32_t tid = t->tid();
      rd_data_[tid] =
          (uint64_t(rs1_data_[tid]) < uint64_t(rs2_data_[tid])) ? 1 : 0;
    }
  }

  void xor_() {
    for (Thread *t : active_threads_) {
      uint32_t tid = t->tid();
      rd_data_[tid] = rs1_data_[tid] ^ rs2_data_[tid];
    }
  }

  void or_() {
    for (Thread *t : active_threads_) {
      uint32_t tid = t->tid();
      rd_data_[tid] = rs1_data_[tid] | rs2_data_[tid];
    }
  }

  void and_() {
    for (Thread *t : active_threads_) {
      uint32_t tid = t->tid();
      rd_data_[tid] = rs1_data_[tid] & rs2_data_[tid];
    }
  }

  void sll_() {
    for (Thread *t : active_threads_) {
      uint32_t tid = t->tid();
      rd_data_[tid] = rs1_data_[tid] << (rs2_data_[tid] & 0x3f);
    }
  }

  void srl_() {
    for (Thread *t : active_threads_) {
      uint32_t tid = t->tid();
      rd_data_[tid] = uint64_t(rs1_data_[tid]) >> (rs2_data_[tid] & 0x3f);
    }
  }

  void sra_() {
    for (Thread *t : active_threads_) {
      uint32_t tid = t->tid();
      rd_data_[tid] = rs1_data_[tid] >> (rs2_data_[tid] & 0x3f);
    }
  }

  void czero_eqz_() {
    for (Thread *t : active_threads_) {
      uint32_t tid = t->tid();
      rd_data_[tid] = (rs2_data_[tid] == 0) ? 0 : rs1_data_[tid];
    }
  }

  void czero_nez_() {
    for (Thread *t : active_threads_) {
      uint32_t tid = t->tid();
      rd_data_[tid] = (rs2_data_[tid] != 0) ? 0 : rs1_data_[tid];
    }
  }

  void mul_() {
    for (Thread *t : active_threads_) {
      uint32_t tid = t->tid();
      rd_data_[tid] = rs1_data_[tid] * rs2_data_[tid];
    }
  }

  void mulh_() {
    for (Thread *t : active_threads_) {
      uint32_t tid = t->tid();
      __int128_t mul_res =
          (__int128_t)rs1_data_[tid] * (__int128_t)rs2_data_[tid];
      rd_data_[tid] = (int64_t)(mul_res >> 64);
    }
  }

  void mulhsu_() {
    for (Thread *t : active_threads_) {
      uint32_t tid = t->tid();
      uint64_t rs2_unsigned = (uint64_t)rs2_data_[tid];
      __int128_t mul_res =
          (__int128_t)rs1_data_[tid] * (__uint128_t)rs2_unsigned;
      rd_data_[tid] = (int64_t)(mul_res >> 64);
    }
  }

  void mulhu_() {
    for (Thread *t : active_threads_) {
      uint32_t tid = t->tid();
      uint64_t rs1_unsigned = (uint64_t)rs1_data_[tid];
      uint64_t rs2_unsigned = (uint64_t)rs2_data_[tid];
      __uint128_t mul_res =
          (__uint128_t)rs1_unsigned * (__uint128_t)rs2_unsigned;
      rd_data_[tid] = (int64_t)(mul_res >> 64);
    }
  }

  void div_() {
    for (Thread *t : active_threads_) {
      uint32_t tid = t->tid();
      if (rs2_data_[tid] == 0) {
        rd_data_[tid] = -1;
      } else if ((rs1_data_[tid] == INT64_MIN) && (rs2_data_[tid] == -1)) {
        rd_data_[tid] = rs1_data_[tid];
      } else {
        rd_data_[tid] = rs1_data_[tid] / rs2_data_[tid];
      }
    }
  }

  void divu_() {
    for (Thread *t : active_threads_) {
      uint32_t tid = t->tid();
      if (rs2_data_[tid] == 0) {
        rd_data_[tid] = -1;
      } else {
        rd_data_[tid] =
            (int64_t)((uint64_t)rs1_data_[tid] / (uint64_t)rs2_data_[tid]);
      }
    }
  }

  void rem_() {
    for (Thread *t : active_threads_) {
      uint32_t tid = t->tid();
      if (rs2_data_[tid] == 0) {
        rd_data_[tid] = rs1_data_[tid];
      } else if ((rs1_data_[tid] == INT64_MIN) && (rs2_data_[tid] == -1)) {
        rd_data_[tid] = 0;
      } else {
        rd_data_[tid] = rs1_data_[tid] % rs2_data_[tid];
      }
    }
  }

  void remu_() {
    for (Thread *t : active_threads_) {
      uint32_t tid = t->tid();
      if (rs2_data_[tid] == 0) {
        rd_data_[tid] = rs1_data_[tid];
      } else {
        rd_data_[tid] =
            (int64_t)((uint64_t)rs1_data_[tid] % (uint64_t)rs2_data_[tid]);
      }
    }
  }

  void nop_() {}

  // Make InstrPool friend so that they can call our protected constructor
  template <class InstrType>
  friend class InstrPool;
};

}  // namespace simtix