blob: b6c3e12d833296b9bb3b8150742584b7307258f3 [file] [log] [blame]
/*
* Copyright (C) 2023 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 BERBERIS_DECODER_RISCV64_SEMANTICS_PLAYER_H_
#define BERBERIS_DECODER_RISCV64_SEMANTICS_PLAYER_H_
#include "berberis/base/overloaded.h"
#include "berberis/decoder/riscv64/decoder.h"
namespace berberis {
// This class expresses the semantics of instructions by calling a sequence of SemanticsListener
// callbacks.
template <class SemanticsListener>
class SemanticsPlayer {
public:
using Decoder = Decoder<SemanticsPlayer>;
using Register = typename SemanticsListener::Register;
using Float32 = typename SemanticsListener::Float32;
using Float64 = typename SemanticsListener::Float64;
using FpRegister = typename SemanticsListener::FpRegister;
explicit SemanticsPlayer(SemanticsListener* listener) : listener_(listener) {}
// Decoder's InsnConsumer implementation.
void Amo(const typename Decoder::AmoArgs& args) {
Register arg1 = GetRegOrZero(args.src1);
Register arg2 = GetRegOrZero(args.src2);
Register result = listener_->Amo(args.opcode, arg1, arg2, args.aq, args.rl);
SetRegOrIgnore(args.dst, result);
};
void Auipc(const typename Decoder::UpperImmArgs& args) {
Register result = listener_->Auipc(args.imm);
SetRegOrIgnore(args.dst, result);
}
void CompareAndBranch(const typename Decoder::BranchArgs& args) {
Register arg1 = GetRegOrZero(args.src1);
Register arg2 = GetRegOrZero(args.src2);
listener_->CompareAndBranch(args.opcode, arg1, arg2, args.offset);
};
void Csr(const typename Decoder::CsrArgs& args) {
Register result;
Register arg = GetRegOrZero(args.src);
result = listener_->Csr(args.opcode, arg, args.csr);
SetRegOrIgnore(args.dst, result);
}
void Csr(const typename Decoder::CsrImmArgs& args) {
Register result;
result = listener_->Csr(args.opcode, args.imm, args.csr);
SetRegOrIgnore(args.dst, result);
}
void Fcvt(const typename Decoder::FcvtFloatToFloatArgs& args) {
FpRegister arg = GetFRegAndUnboxNan(args.src, args.src_type);
Register frm = listener_->GetFrm();
FpRegister result;
if (args.dst_type == Decoder::FloatOperandType::kFloat &&
args.src_type == Decoder::FloatOperandType::kDouble) {
result = listener_->template FCvtFloatToFloat<Float32, Float64>(args.rm, frm, arg);
} else if (args.dst_type == Decoder::FloatOperandType::kDouble &&
args.src_type == Decoder::FloatOperandType::kFloat) {
result = listener_->template FCvtFloatToFloat<Float64, Float32>(args.rm, frm, arg);
} else {
Unimplemented();
return;
}
NanBoxAndSetFpReg(args.dst, result, args.dst_type);
}
void Fcvt(const typename Decoder::FcvtFloatToIntegerArgs& args) {
FpRegister arg = GetFRegAndUnboxNan(args.src, args.src_type);
Register frm = listener_->GetFrm();
Register result;
switch (args.src_type) {
case Decoder::FloatOperandType::kFloat:
switch (args.dst_type) {
case Decoder::FcvtOperandType::k32bitSigned:
result = listener_->template FCvtFloatToInteger<int32_t, Float32>(args.rm, frm, arg);
break;
case Decoder::FcvtOperandType::k32bitUnsigned:
result = listener_->template FCvtFloatToInteger<uint32_t, Float32>(args.rm, frm, arg);
break;
case Decoder::FcvtOperandType::k64bitSigned:
result = listener_->template FCvtFloatToInteger<int64_t, Float32>(args.rm, frm, arg);
break;
case Decoder::FcvtOperandType::k64bitUnsigned:
result = listener_->template FCvtFloatToInteger<uint64_t, Float32>(args.rm, frm, arg);
break;
default:
Unimplemented();
return;
}
break;
case Decoder::FloatOperandType::kDouble:
switch (args.dst_type) {
case Decoder::FcvtOperandType::k32bitSigned:
result = listener_->template FCvtFloatToInteger<int32_t, Float64>(args.rm, frm, arg);
break;
case Decoder::FcvtOperandType::k32bitUnsigned:
result = listener_->template FCvtFloatToInteger<uint32_t, Float64>(args.rm, frm, arg);
break;
case Decoder::FcvtOperandType::k64bitSigned:
result = listener_->template FCvtFloatToInteger<int64_t, Float64>(args.rm, frm, arg);
break;
case Decoder::FcvtOperandType::k64bitUnsigned:
result = listener_->template FCvtFloatToInteger<uint64_t, Float64>(args.rm, frm, arg);
break;
default:
Unimplemented();
return;
}
break;
default:
Unimplemented();
return;
}
SetRegOrIgnore(args.dst, result);
}
void Fcvt(const typename Decoder::FcvtIntegerToFloatArgs& args) {
Register arg = GetRegOrZero(args.src);
Register frm = listener_->GetFrm();
FpRegister result;
switch (args.dst_type) {
case Decoder::FloatOperandType::kFloat:
switch (args.src_type) {
case Decoder::FcvtOperandType::k32bitSigned:
result = listener_->template FCvtIntegerToFloat<Float32, int32_t>(args.rm, frm, arg);
break;
case Decoder::FcvtOperandType::k32bitUnsigned:
result = listener_->template FCvtIntegerToFloat<Float32, uint32_t>(args.rm, frm, arg);
break;
case Decoder::FcvtOperandType::k64bitSigned:
result = listener_->template FCvtIntegerToFloat<Float32, int64_t>(args.rm, frm, arg);
break;
case Decoder::FcvtOperandType::k64bitUnsigned:
result = listener_->template FCvtIntegerToFloat<Float32, uint64_t>(args.rm, frm, arg);
break;
default:
Unimplemented();
return;
}
break;
case Decoder::FloatOperandType::kDouble:
switch (args.src_type) {
case Decoder::FcvtOperandType::k32bitSigned:
result = listener_->template FCvtIntegerToFloat<Float64, int32_t>(args.rm, frm, arg);
break;
case Decoder::FcvtOperandType::k32bitUnsigned:
result = listener_->template FCvtIntegerToFloat<Float64, uint32_t>(args.rm, frm, arg);
break;
case Decoder::FcvtOperandType::k64bitSigned:
result = listener_->template FCvtIntegerToFloat<Float64, int64_t>(args.rm, frm, arg);
break;
case Decoder::FcvtOperandType::k64bitUnsigned:
result = listener_->template FCvtIntegerToFloat<Float64, uint64_t>(args.rm, frm, arg);
break;
default:
Unimplemented();
return;
}
break;
default:
Unimplemented();
return;
}
NanBoxAndSetFpReg(args.dst, result, args.dst_type);
}
void Fma(const typename Decoder::FmaArgs& args) {
FpRegister arg1 = GetFRegAndUnboxNan(args.src1, args.operand_type);
FpRegister arg2 = GetFRegAndUnboxNan(args.src2, args.operand_type);
FpRegister arg3 = GetFRegAndUnboxNan(args.src3, args.operand_type);
FpRegister result;
Register frm = listener_->GetFrm();
switch (args.operand_type) {
case Decoder::FloatOperandType::kFloat:
result = Fma<Float32>(args.opcode, args.rm, frm, arg1, arg2, arg3);
break;
case Decoder::FloatOperandType::kDouble:
result = Fma<Float64>(args.opcode, args.rm, frm, arg1, arg2, arg3);
break;
default:
Unimplemented();
return;
}
result = CanonicalizeNan(result, args.operand_type);
NanBoxAndSetFpReg(args.dst, result, args.operand_type);
}
template <typename FloatType>
FpRegister Fma(typename Decoder::FmaOpcode opcode,
int8_t rm,
Register frm,
FpRegister arg1,
FpRegister arg2,
FpRegister arg3) {
switch (opcode) {
case Decoder::FmaOpcode::kFmadd:
return listener_->template FMAdd<FloatType>(rm, frm, arg1, arg2, arg3);
case Decoder::FmaOpcode::kFmsub:
return listener_->template FMSub<FloatType>(rm, frm, arg1, arg2, arg3);
// Note (from RISC-V manual): The FNMSUB and FNMADD instructions are counterintuitively named,
// owing to the naming of the corresponding instructions in MIPS-IV. The MIPS instructions
// were defined to negate the sum, rather than negating the product as the RISC-V instructions
// do, so the naming scheme was more rational at the time. The two definitions differ with
// respect to signed-zero results. The RISC-V definition matches the behavior of the x86 and
// ARM fused multiply-add instructions, but unfortunately the RISC-V FNMSUB and FNMADD
// instruction names are swapped compared to x86 and ARM.
//
// Since even official documentation calls the names “counterintuitive” it's better to use x86
// ones for intrinsics.
case Decoder::FmaOpcode::kFnmsub:
return listener_->template FNMAdd<FloatType>(rm, frm, arg1, arg2, arg3);
case Decoder::FmaOpcode::kFnmadd:
return listener_->template FNMSub<FloatType>(rm, frm, arg1, arg2, arg3);
default:
Unimplemented();
return {};
}
}
void Fence(const typename Decoder::FenceArgs& args) {
listener_->Fence(args.opcode,
// args.src is currently unused - read below.
Register{},
args.sw,
args.sr,
args.so,
args.si,
args.pw,
args.pr,
args.po,
args.pi);
// The unused fields in the FENCE instructions — args.src and args.dst — are reserved for
// finer-grain fences in future extensions. For forward compatibility, base implementations
// shall ignore these fields, and standard software shall zero these fields. Likewise, many
// args.opcode and predecessor/successor set settings are also reserved for future use. Base
// implementations shall treat all such reserved configurations as normal fences with
// args.opcode=0000, and standard software shall use only non-reserved configurations.
}
void FenceI(const typename Decoder::FenceIArgs& args) {
Register arg = GetRegOrZero(args.src);
listener_->FenceI(arg, args.imm);
// The unused fields in the FENCE.I instruction, imm[11:0], rs1, and rd, are reserved for
// finer-grain fences in future extensions. For forward compatibility, base implementations
// shall ignore these fields, and standard software shall zero these fields.
}
void JumpAndLink(const typename Decoder::JumpAndLinkArgs& args) {
Register result = listener_->GetImm(listener_->GetInsnAddr() + args.insn_len);
SetRegOrIgnore(args.dst, result);
listener_->Branch(args.offset);
};
void JumpAndLinkRegister(const typename Decoder::JumpAndLinkRegisterArgs& args) {
Register result = listener_->GetImm(listener_->GetInsnAddr() + args.insn_len);
SetRegOrIgnore(args.dst, result);
Register base = GetRegOrZero(args.base);
listener_->BranchRegister(base, args.offset);
};
void Load(const typename Decoder::LoadArgs& args) {
Register arg = GetRegOrZero(args.src);
Register result = listener_->Load(args.operand_type, arg, args.offset);
SetRegOrIgnore(args.dst, result);
};
void Load(const typename Decoder::LoadFpArgs& args) {
Register arg = GetRegOrZero(args.src);
FpRegister result;
switch (args.operand_type) {
case Decoder::FloatOperandType::kFloat:
result = listener_->template LoadFp<Float32>(arg, args.offset);
break;
case Decoder::FloatOperandType::kDouble:
result = listener_->template LoadFp<Float64>(arg, args.offset);
break;
default:
Unimplemented();
return;
}
NanBoxAndSetFpReg(args.dst, result, args.operand_type);
};
void Lui(const typename Decoder::UpperImmArgs& args) {
Register result = listener_->Lui(args.imm);
SetRegOrIgnore(args.dst, result);
}
void Nop() { listener_->Nop(); }
template <typename OpArgs>
void Op(OpArgs&& args) {
Register arg1 = GetRegOrZero(args.src1);
Register arg2 = GetRegOrZero(args.src2);
Register result = Overloaded{[&](const typename Decoder::OpArgs& args) {
return listener_->Op(args.opcode, arg1, arg2);
},
[&](const typename Decoder::Op32Args& args) {
return listener_->Op32(args.opcode, arg1, arg2);
}}(args);
SetRegOrIgnore(args.dst, result);
};
void OpFp(const typename Decoder::OpFpArgs& args) {
FpRegister arg1 = GetFRegAndUnboxNan(args.src1, args.operand_type);
FpRegister arg2 = GetFRegAndUnboxNan(args.src2, args.operand_type);
FpRegister result;
Register frm = listener_->GetFrm();
switch (args.operand_type) {
case Decoder::FloatOperandType::kFloat:
result = OpFp<Float32>(args.opcode, args.rm, frm, arg1, arg2);
break;
case Decoder::FloatOperandType::kDouble:
result = OpFp<Float64>(args.opcode, args.rm, frm, arg1, arg2);
break;
default:
Unimplemented();
return;
}
result = CanonicalizeNan(result, args.operand_type);
NanBoxAndSetFpReg(args.dst, result, args.operand_type);
}
template <typename FloatType>
FpRegister OpFp(typename Decoder::OpFpOpcode opcode,
int8_t rm,
Register frm,
FpRegister arg1,
FpRegister arg2) {
switch (opcode) {
case Decoder::OpFpOpcode::kFAdd:
return listener_->template FAdd<FloatType>(rm, frm, arg1, arg2);
case Decoder::OpFpOpcode::kFSub:
return listener_->template FSub<FloatType>(rm, frm, arg1, arg2);
case Decoder::OpFpOpcode::kFMul:
return listener_->template FMul<FloatType>(rm, frm, arg1, arg2);
case Decoder::OpFpOpcode::kFDiv:
return listener_->template FDiv<FloatType>(rm, frm, arg1, arg2);
default:
Unimplemented();
return {};
}
}
void OpFpGpRegisterTargetNoRounding(
const typename Decoder::OpFpGpRegisterTargetNoRoundingArgs& args) {
FpRegister arg1 = GetFRegAndUnboxNan(args.src1, args.operand_type);
FpRegister arg2 = GetFRegAndUnboxNan(args.src2, args.operand_type);
Register result =
listener_->OpFpGpRegisterTargetNoRounding(args.opcode, args.operand_type, arg1, arg2);
SetRegOrIgnore(args.dst, result);
}
void OpFpGpRegisterTargetSingleInputNoRounding(
const typename Decoder::OpFpGpRegisterTargetSingleInputNoRoundingArgs& args) {
FpRegister arg = GetFRegAndUnboxNan(args.src, args.operand_type);
Register result;
switch (args.operand_type) {
case Decoder::FloatOperandType::kFloat:
result = OpFpGpRegisterTargetSingleInputNoRounding<Float32>(args.opcode, arg);
break;
case Decoder::FloatOperandType::kDouble:
result = OpFpGpRegisterTargetSingleInputNoRounding<Float64>(args.opcode, arg);
break;
default:
Unimplemented();
return;
}
SetRegOrIgnore(args.dst, result);
}
template <typename FloatType>
Register OpFpGpRegisterTargetSingleInputNoRounding(
typename Decoder::OpFpGpRegisterTargetSingleInputNoRoundingOpcode opcode,
FpRegister arg) {
switch (opcode) {
case Decoder::OpFpGpRegisterTargetSingleInputNoRoundingOpcode::kFclass:
return listener_->template FClass<FloatType>(arg);
default:
Unimplemented();
return {};
}
}
void OpFpNoRounding(const typename Decoder::OpFpNoRoundingArgs& args) {
FpRegister arg1 = GetFRegAndUnboxNan(args.src1, args.operand_type);
FpRegister arg2 = GetFRegAndUnboxNan(args.src2, args.operand_type);
FpRegister result;
switch (args.operand_type) {
case Decoder::FloatOperandType::kFloat:
result = OpFpNoRounding<Float32>(args.opcode, arg1, arg2);
break;
case Decoder::FloatOperandType::kDouble:
result = OpFpNoRounding<Float64>(args.opcode, arg1, arg2);
break;
default:
Unimplemented();
return;
}
result = CanonicalizeNan(result, args.operand_type);
NanBoxAndSetFpReg(args.dst, result, args.operand_type);
}
template <typename FloatType>
FpRegister OpFpNoRounding(const typename Decoder::OpFpNoRoundingOpcode opcode,
FpRegister arg1,
FpRegister arg2) {
switch (opcode) {
case Decoder::OpFpNoRoundingOpcode::kFSgnj:
return listener_->template FSgnj<FloatType>(arg1, arg2);
case Decoder::OpFpNoRoundingOpcode::kFSgnjn:
return listener_->template FSgnjn<FloatType>(arg1, arg2);
case Decoder::OpFpNoRoundingOpcode::kFSgnjx:
return listener_->template FSgnjx<FloatType>(arg1, arg2);
case Decoder::OpFpNoRoundingOpcode::kFMin:
return listener_->template FMin<FloatType>(arg1, arg2);
case Decoder::OpFpNoRoundingOpcode::kFMax:
return listener_->template FMax<FloatType>(arg1, arg2);
default:
Unimplemented();
return {};
}
}
void FmvFloatToInteger(const typename Decoder::FmvFloatToIntegerArgs& args) {
FpRegister arg = GetFpReg(args.src);
Register result = listener_->Fmv(args.operand_type, arg);
SetRegOrIgnore(args.dst, result);
}
void FmvIntegerToFloat(const typename Decoder::FmvIntegerToFloatArgs& args) {
Register arg = GetRegOrZero(args.src);
FpRegister result = listener_->Fmv(arg);
NanBoxAndSetFpReg(args.dst, result, args.operand_type);
}
void OpFpSingleInput(const typename Decoder::OpFpSingleInputArgs& args) {
FpRegister arg = GetFRegAndUnboxNan(args.src, args.operand_type);
FpRegister result;
Register frm = listener_->GetFrm();
switch (args.operand_type) {
case Decoder::FloatOperandType::kFloat:
result = OpFpSingleInput<Float32>(args.opcode, args.rm, frm, arg);
break;
case Decoder::FloatOperandType::kDouble:
result = OpFpSingleInput<Float64>(args.opcode, args.rm, frm, arg);
break;
default:
Unimplemented();
return;
}
result = CanonicalizeNan(result, args.operand_type);
NanBoxAndSetFpReg(args.dst, result, args.operand_type);
}
template <typename FloatType>
FpRegister OpFpSingleInput(typename Decoder::OpFpSingleInputOpcode opcode,
int8_t rm,
Register frm,
FpRegister arg) {
switch (opcode) {
case Decoder::OpFpSingleInputOpcode::kFSqrt:
return listener_->template FSqrt<FloatType>(rm, frm, arg);
default:
Unimplemented();
return {};
}
}
template <typename OpImmArgs>
void OpImm(OpImmArgs&& args) {
Register arg = GetRegOrZero(args.src);
Register result = Overloaded{[&](const typename Decoder::OpImmArgs& args) {
return listener_->OpImm(args.opcode, arg, args.imm);
},
[&](const typename Decoder::OpImm32Args& args) {
return listener_->OpImm32(args.opcode, arg, args.imm);
},
[&](const typename Decoder::ShiftImmArgs& args) {
return listener_->ShiftImm(args.opcode, arg, args.imm);
},
[&](const typename Decoder::ShiftImm32Args& args) {
return listener_->ShiftImm32(args.opcode, arg, args.imm);
}}(args);
SetRegOrIgnore(args.dst, result);
};
void Store(const typename Decoder::StoreArgs& args) {
Register arg = GetRegOrZero(args.src);
Register data = GetRegOrZero(args.data);
listener_->Store(args.operand_type, arg, args.offset, data);
};
void Store(const typename Decoder::StoreFpArgs& args) {
Register arg = GetRegOrZero(args.src);
FpRegister data = GetFpReg(args.data);
switch (args.operand_type) {
case Decoder::FloatOperandType::kFloat:
listener_->template StoreFp<Float32>(arg, args.offset, data);
break;
case Decoder::FloatOperandType::kDouble:
listener_->template StoreFp<Float64>(arg, args.offset, data);
break;
default:
Unimplemented();
return;
}
};
// We may have executed a signal handler just after the syscall. If that handler changed x10, then
// overwriting x10 here would be incorrect. On the other hand asynchronous signals are unlikely to
// change CPU state, so we don't support this at the moment for simplicity."
void System(const typename Decoder::SystemArgs& args) {
if (args.opcode != Decoder::SystemOpcode::kEcall) {
return Unimplemented();
}
Register syscall_nr = GetRegOrZero(17);
Register arg0 = GetRegOrZero(10);
Register arg1 = GetRegOrZero(11);
Register arg2 = GetRegOrZero(12);
Register arg3 = GetRegOrZero(13);
Register arg4 = GetRegOrZero(14);
Register arg5 = GetRegOrZero(15);
Register result = listener_->Ecall(syscall_nr, arg0, arg1, arg2, arg3, arg4, arg5);
SetRegOrIgnore(10, result);
}
void Unimplemented() { listener_->Unimplemented(); };
private:
Register GetRegOrZero(uint8_t reg) {
return reg == 0 ? listener_->GetImm(0) : listener_->GetReg(reg);
}
void SetRegOrIgnore(uint8_t reg, Register value) {
if (reg != 0) {
listener_->SetReg(reg, value);
}
}
// Floating point instructions in RISC-V are encoded in a way where you may find out size of
// operand (single-precision, double-precision, half-precision or quad-precesion; the latter
// two optional) from the instruction encoding without determining the full form of instruction.
//
// Sources and targets are also specified via dedicated bits in opcodes.
//
// This allows us to split instruction handling in four steps:
// 1. Load operands from register and convert it into a form suitable for host.
// 2. Execute operations specified by opcode.
// 3. Normalize NaNs if host and guest architctures handled them differently.
// 4. Encode results as required by RISC-V (if host doesn't do that).
//
// Note that in case of execution of RISC-V on RISC-V all steps except #2 are not doing anything.
// Step #1:
// • GetFpReg — for instructions like fsw or fmv.x.w use GetFpReg which doesn't change value.
// • GetFRegAndBoxNan — for most instructions (improperly boxed narrow float is turned into NaN).
FpRegister GetFpReg(uint8_t reg) { return listener_->GetFpReg(reg); }
FpRegister GetFRegAndUnboxNan(uint8_t reg, typename Decoder::FloatOperandType operand_type) {
return listener_->GetFRegAndUnboxNan(reg, operand_type);
}
// Step #3.
FpRegister CanonicalizeNan(FpRegister value, typename Decoder::FloatOperandType operand_type) {
return listener_->CanonicalizeNan(value, operand_type);
}
// Step #4. Note the assymetry: step #1 may skip the NaN unboxing (would use GetFpReg if so),
// but step #4 boxes uncoditionally (if actual instruction doesn't do that on host).
void NanBoxAndSetFpReg(uint8_t reg,
FpRegister value,
typename Decoder::FloatOperandType operand_type) {
listener_->NanBoxAndSetFpReg(reg, value, operand_type);
}
SemanticsListener* listener_;
};
} // namespace berberis
#endif // BERBERIS_DECODER_RISCV64_SEMANTICS_PLAYER_H_