blob: 9f0e212841e7c0bdf00c47c69be6698bd5ee0ec0 [file]
// Copyright 2023 Google LLC
//
// 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
//
// https://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.
#include "riscv/riscv_b_instructions.h"
#include <algorithm>
#include <cstdint>
#include <type_traits>
#include "absl/base/casts.h"
#include "absl/numeric/bits.h"
#include "absl/types/span.h"
#include "mpact/sim/generic/instruction.h"
#include "riscv/riscv_instruction_helpers.h"
#include "riscv/riscv_register.h"
namespace mpact {
namespace sim {
namespace riscv {
namespace RV64 {
namespace {
using RegisterType = RV64Register;
using UIntReg =
typename std::make_unsigned<typename RegisterType::ValueType>::type;
using IntReg = typename std::make_signed<UIntReg>::type;
// Handles instructions that perform a shift and add operation.
void ShNaddHelper(const generic::Instruction* instruction, int shift) {
RiscVBinaryOp<RegisterType, IntReg, IntReg>(
instruction, [shift](IntReg a, IntReg b) {
return (absl::bit_cast<UIntReg>(a) << shift) + b;
});
}
// Handles instructions that perform an unsigned shift and add operation. Note
// that the least significant word (4 bytes) is masked before the operation is
// performed.
void ShNadduwHelper(const generic::Instruction* instruction, int shift) {
RiscVBinaryOp<RegisterType, UIntReg, UIntReg>(
instruction, [shift](UIntReg a, UIntReg b) {
return ((a & 0xffff'ffff) << shift) + b;
});
}
} // namespace
void RiscVAddUw(const generic::Instruction* instruction) {
RiscVBinaryOp<RegisterType, UIntReg, UIntReg>(
instruction, [](UIntReg a, UIntReg b) { return (a & 0xffff'ffff) + b; });
}
void RiscVSh1add(const generic::Instruction* instruction) {
ShNaddHelper(instruction, 1);
}
void RiscVSh2add(const generic::Instruction* instruction) {
ShNaddHelper(instruction, 2);
}
void RiscVSh3add(const generic::Instruction* instruction) {
ShNaddHelper(instruction, 3);
}
void RiscVSh1adduw(const generic::Instruction* instruction) {
ShNadduwHelper(instruction, 1);
}
void RiscVSh2adduw(const generic::Instruction* instruction) {
ShNadduwHelper(instruction, 2);
}
void RiscVSh3adduw(const generic::Instruction* instruction) {
ShNadduwHelper(instruction, 3);
}
void RiscVSlliuw(const generic::Instruction* instruction) {
RiscVBinaryOp<RegisterType, UIntReg, int32_t>(
instruction, [](UIntReg a, int32_t b) { return (a & 0xffff'ffff) << b; });
}
void RiscVAndn(const generic::Instruction* instruction) {
RiscVBinaryOp<RegisterType, UIntReg, UIntReg>(
instruction, [](UIntReg a, UIntReg b) { return a & ~b; });
}
void RiscVOrn(const generic::Instruction* instruction) {
RiscVBinaryOp<RegisterType, UIntReg, UIntReg>(
instruction, [](UIntReg a, UIntReg b) { return a | ~b; });
}
void RiscVXnor(const generic::Instruction* instruction) {
RiscVBinaryOp<RegisterType, UIntReg, UIntReg>(
instruction, [](UIntReg a, UIntReg b) { return a ^ ~b; });
}
void RiscVClz(const generic::Instruction* instruction) {
RiscVUnaryOp<RegisterType, UIntReg, UIntReg>(
instruction, [](UIntReg a) { return absl::countl_zero(a); });
}
void RiscVClzw(const generic::Instruction* instruction) {
RiscVUnaryOp<RegisterType, UIntReg, UIntReg>(instruction, [](UIntReg a) {
uint32_t value = static_cast<uint32_t>(a);
return absl::countl_zero(value);
});
}
void RiscVCtz(const generic::Instruction* instruction) {
RiscVUnaryOp<RegisterType, UIntReg, UIntReg>(
instruction, [](UIntReg a) { return absl::countr_zero(a); });
}
void RiscVCtzw(const generic::Instruction* instruction) {
RiscVUnaryOp<RegisterType, UIntReg, UIntReg>(instruction, [](UIntReg a) {
uint32_t value = static_cast<uint32_t>(a);
return absl::countr_zero(value);
});
}
void RiscVCpop(const generic::Instruction* instruction) {
RiscVUnaryOp<RegisterType, UIntReg, UIntReg>(
instruction, [](UIntReg a) { return absl::popcount(a); });
}
void RiscVCpopw(const generic::Instruction* instruction) {
RiscVUnaryOp<RegisterType, UIntReg, UIntReg>(instruction, [](UIntReg a) {
return absl::popcount(a & 0xffff'ffffULL);
});
}
void RiscVMax(const generic::Instruction* instruction) {
RiscVBinaryOp<RegisterType, IntReg, IntReg>(
instruction, [](IntReg a, IntReg b) { return std::max(a, b); });
}
void RiscVMaxu(const generic::Instruction* instruction) {
RiscVBinaryOp<RegisterType, UIntReg, UIntReg>(
instruction, [](UIntReg a, UIntReg b) { return std::max(a, b); });
}
void RiscVMin(const generic::Instruction* instruction) {
RiscVBinaryOp<RegisterType, IntReg, IntReg>(
instruction, [](IntReg a, IntReg b) { return std::min(a, b); });
}
void RiscVMinu(const generic::Instruction* instruction) {
RiscVBinaryOp<RegisterType, UIntReg, UIntReg>(
instruction, [](UIntReg a, UIntReg b) { return std::min(a, b); });
}
void RiscVSexth(const generic::Instruction* instruction) {
RiscVUnaryOp<RegisterType, IntReg, IntReg>(
instruction, [](IntReg a) -> IntReg {
if ((1 << 15) & a) {
return a | 0xffff'ffff'ffff'0000;
} else {
return a & 0xffff;
}
});
}
void RiscVSextb(const generic::Instruction* instruction) {
RiscVUnaryOp<RegisterType, IntReg, IntReg>(
instruction, [](IntReg a) -> IntReg {
if ((1 << 7) & a) {
return a | 0xffff'ffff'ffff'ff00;
} else {
return a & 0xff;
}
});
}
void RiscVZextw(const generic::Instruction* instruction) {
RiscVUnaryOp<RegisterType, UIntReg, UIntReg>(
instruction, [](UIntReg a) { return a & 0x0000'0000'ffff'ffff; });
}
void RiscVZexth(const generic::Instruction* instruction) {
RiscVUnaryOp<RegisterType, UIntReg, UIntReg>(
instruction, [](UIntReg a) { return a & 0x0000'0000'0000'ffff; });
}
void RiscVRol(const generic::Instruction* instruction) {
RiscVBinaryOp<RegisterType, UIntReg, UIntReg>(
instruction,
[](UIntReg a, UIntReg b) { return absl::rotl(a, b & 0x3f); });
}
void RiscVRolw(const generic::Instruction* instruction) {
RiscVBinaryOp<RegisterType, UIntReg, UIntReg>(
instruction, [](UIntReg a, UIntReg b) {
const uint32_t a32 = static_cast<uint32_t>(a);
const uint32_t value = absl::rotl(a32, b & 0x1f);
const uint64_t sign_extended_value =
(value & 0x8000'0000)
? static_cast<uint64_t>(value) | 0xffff'ffff'0000'0000
: static_cast<uint64_t>(value);
return sign_extended_value;
});
}
void RiscVRor(const generic::Instruction* instruction) {
RiscVBinaryOp<RegisterType, UIntReg, UIntReg>(
instruction,
[](UIntReg a, UIntReg b) { return absl::rotr(a, b & 0x3f); });
}
void RiscVRorw(const generic::Instruction* instruction) {
RiscVBinaryOp<RegisterType, UIntReg, UIntReg>(
instruction, [](UIntReg a, UIntReg b) {
const uint32_t a32 = static_cast<uint32_t>(a);
const uint32_t value = absl::rotr(a32, b & 0x1f);
const uint64_t sign_extended_value =
(value & 0x8000'0000)
? static_cast<uint64_t>(value) | 0xffff'ffff'0000'0000
: static_cast<uint64_t>(value);
return sign_extended_value;
});
}
void RiscVRori(const generic::Instruction* instruction) {
// Note: Handling of immediates and register values is identical.
RiscVRor(instruction);
}
void RiscVRoriw(const generic::Instruction* instruction) {
// Note: Handling of immediates and register values is identical.
RiscVRorw(instruction);
}
void RiscVOrcb(const generic::Instruction* instruction) {
RiscVUnaryOp<RegisterType, UIntReg, UIntReg>(instruction, [](UIntReg a) {
UIntReg result = 0;
for (int i = 0; i < sizeof(UIntReg); ++i) {
const UIntReg mask = 0xFFULL << (8 * i);
if (a & mask) {
result |= mask;
}
}
return result;
});
}
void RiscVRev8(const generic::Instruction* instruction) {
RiscVUnaryOp<RegisterType, UIntReg, UIntReg>(instruction, [](UIntReg a) {
absl::Span<const uint8_t> input_span(reinterpret_cast<const uint8_t*>(&a),
sizeof(UIntReg));
UIntReg output = 0;
absl::Span<uint8_t> output_span(reinterpret_cast<uint8_t*>(&output),
sizeof(UIntReg));
for (int i = 0; i < sizeof(UIntReg); ++i) {
const int output_index = (sizeof(UIntReg) - 1) - i;
output_span[output_index] = input_span[i];
}
return output;
});
}
} // namespace RV64
} // namespace riscv
} // namespace sim
} // namespace mpact