safe_ops: Implement safe shifting operations
Implement shifiting operations that perform consistently across architectures without running into undefined or implemented-defined behaviour.
This commit is contained in:
parent
b00fe23b91
commit
d21659152c
2 changed files with 110 additions and 0 deletions
|
@ -27,6 +27,7 @@ add_library(dynarmic
|
||||||
common/memory_pool.cpp
|
common/memory_pool.cpp
|
||||||
common/memory_pool.h
|
common/memory_pool.h
|
||||||
common/mp.h
|
common/mp.h
|
||||||
|
common/safe_ops.h
|
||||||
common/scope_exit.h
|
common/scope_exit.h
|
||||||
common/sm4.cpp
|
common/sm4.cpp
|
||||||
common/sm4.h
|
common/sm4.h
|
||||||
|
|
109
src/common/safe_ops.h
Normal file
109
src/common/safe_ops.h
Normal file
|
@ -0,0 +1,109 @@
|
||||||
|
/* This file is part of the dynarmic project.
|
||||||
|
* Copyright (c) 2018 MerryMage
|
||||||
|
* This software may be used and distributed according to the terms of the GNU
|
||||||
|
* General Public License version 2 or any later version.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
#include "common/bit_util.h"
|
||||||
|
#include "common/common_types.h"
|
||||||
|
#include "common/u128.h"
|
||||||
|
|
||||||
|
namespace Dynarmic::Safe {
|
||||||
|
|
||||||
|
template<typename T> T LogicalShiftLeft(T value, int shift_amount);
|
||||||
|
template<typename T> T LogicalShiftRight(T value, int shift_amount);
|
||||||
|
template<typename T> T ArithmeticShiftLeft(T value, int shift_amount);
|
||||||
|
template<typename T> T ArithmeticShiftRight(T value, int shift_amount);
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
T LogicalShiftLeft(T value, int shift_amount) {
|
||||||
|
static_assert(std::is_integral_v<T>);
|
||||||
|
|
||||||
|
if (shift_amount >= static_cast<int>(Common::BitSize<T>())) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shift_amount < 0) {
|
||||||
|
return LogicalShiftRight(value, -shift_amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto unsigned_value = static_cast<std::make_unsigned_t<T>>(value);
|
||||||
|
return static_cast<T>(unsigned_value << shift_amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<>
|
||||||
|
inline u128 LogicalShiftLeft(u128 value, int shift_amount) {
|
||||||
|
return value << shift_amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
T LogicalShiftRight(T value, int shift_amount) {
|
||||||
|
static_assert(std::is_integral_v<T>);
|
||||||
|
|
||||||
|
if (shift_amount >= static_cast<int>(Common::BitSize<T>())) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shift_amount < 0) {
|
||||||
|
return LogicalShiftLeft(value, -shift_amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto unsigned_value = static_cast<std::make_unsigned_t<T>>(value);
|
||||||
|
return static_cast<T>(unsigned_value >> shift_amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<>
|
||||||
|
inline u128 LogicalShiftRight(u128 value, int shift_amount) {
|
||||||
|
return value >> shift_amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
T LogicalShiftRightDouble(T top, T bottom, int shift_amount) {
|
||||||
|
return LogicalShiftLeft(top, int(Common::BitSize<T>()) - shift_amount) | LogicalShiftRight(bottom, shift_amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
T ArithmeticShiftLeft(T value, int shift_amount) {
|
||||||
|
static_assert(std::is_integral_v<T>);
|
||||||
|
|
||||||
|
if (shift_amount >= static_cast<int>(Common::BitSize<T>())) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shift_amount < 0) {
|
||||||
|
return ArithmeticShiftRight(value, -shift_amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto signed_value = static_cast<std::make_signed_t<T>>(value);
|
||||||
|
return static_cast<T>(signed_value << shift_amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
T ArithmeticShiftRight(T value, int shift_amount) {
|
||||||
|
static_assert(std::is_integral_v<T>);
|
||||||
|
|
||||||
|
if (shift_amount >= static_cast<int>(Common::BitSize<T>())) {
|
||||||
|
return Common::MostSignificantBit(value) ? ~static_cast<T>(0) : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shift_amount < 0) {
|
||||||
|
return ArithmeticShiftLeft(value, -shift_amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto signed_value = static_cast<std::make_signed_t<T>>(value);
|
||||||
|
return static_cast<T>(signed_value >> shift_amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
T ArithmeticShiftRightDouble(T top, T bottom, int shift_amount) {
|
||||||
|
return ArithmeticShiftLeft(top, int(Common::BitSize<T>()) - shift_amount) | LogicalShiftRight(bottom, shift_amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
T Negate(T value) {
|
||||||
|
return static_cast<T>(-static_cast<std::make_signed_t<T>>(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Dynarmic::Safe
|
Loading…
Reference in a new issue