diff options
Diffstat (limited to 'externals/dynarmic/src/dynarmic/backend/arm64/emit_arm64_saturation.cpp')
-rw-r--r-- | externals/dynarmic/src/dynarmic/backend/arm64/emit_arm64_saturation.cpp | 273 |
1 files changed, 273 insertions, 0 deletions
diff --git a/externals/dynarmic/src/dynarmic/backend/arm64/emit_arm64_saturation.cpp b/externals/dynarmic/src/dynarmic/backend/arm64/emit_arm64_saturation.cpp new file mode 100644 index 0000000000..95b8b51ee9 --- /dev/null +++ b/externals/dynarmic/src/dynarmic/backend/arm64/emit_arm64_saturation.cpp @@ -0,0 +1,273 @@ +/* This file is part of the dynarmic project. + * Copyright (c) 2022 MerryMage + * SPDX-License-Identifier: 0BSD + */ + +#include <oaknut/oaknut.hpp> + +#include "dynarmic/backend/arm64/a32_jitstate.h" +#include "dynarmic/backend/arm64/abi.h" +#include "dynarmic/backend/arm64/emit_arm64.h" +#include "dynarmic/backend/arm64/emit_context.h" +#include "dynarmic/backend/arm64/reg_alloc.h" +#include "dynarmic/ir/basic_block.h" +#include "dynarmic/ir/microinstruction.h" +#include "dynarmic/ir/opcodes.h" + +namespace Dynarmic::Backend::Arm64 { + +using namespace oaknut::util; + +template<> +void EmitIR<IR::Opcode::SignedSaturatedAddWithFlag32>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) { + const auto overflow_inst = inst->GetAssociatedPseudoOperation(IR::Opcode::GetOverflowFromOp); + ASSERT(overflow_inst); + + auto args = ctx.reg_alloc.GetArgumentInfo(inst); + auto Wresult = ctx.reg_alloc.WriteW(inst); + auto Wa = ctx.reg_alloc.ReadW(args[0]); + auto Wb = ctx.reg_alloc.ReadW(args[1]); + auto Woverflow = ctx.reg_alloc.WriteW(overflow_inst); + RegAlloc::Realize(Wresult, Wa, Wb, Woverflow); + ctx.reg_alloc.SpillFlags(); + + code.ADDS(Wresult, *Wa, Wb); + code.ASR(Wscratch0, Wresult, 31); + code.EOR(Wscratch0, Wscratch0, 0x8000'0000); + code.CSEL(Wresult, Wresult, Wscratch0, VC); + code.CSET(Woverflow, VS); +} + +template<> +void EmitIR<IR::Opcode::SignedSaturatedSubWithFlag32>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) { + const auto overflow_inst = inst->GetAssociatedPseudoOperation(IR::Opcode::GetOverflowFromOp); + ASSERT(overflow_inst); + + auto args = ctx.reg_alloc.GetArgumentInfo(inst); + auto Wresult = ctx.reg_alloc.WriteW(inst); + auto Wa = ctx.reg_alloc.ReadW(args[0]); + auto Wb = ctx.reg_alloc.ReadW(args[1]); + auto Woverflow = ctx.reg_alloc.WriteW(overflow_inst); + RegAlloc::Realize(Wresult, Wa, Wb, Woverflow); + ctx.reg_alloc.SpillFlags(); + + code.SUBS(Wresult, *Wa, Wb); + code.ASR(Wscratch0, Wresult, 31); + code.EOR(Wscratch0, Wscratch0, 0x8000'0000); + code.CSEL(Wresult, Wresult, Wscratch0, VC); + code.CSET(Woverflow, VS); +} + +template<> +void EmitIR<IR::Opcode::SignedSaturation>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) { + const auto overflow_inst = inst->GetAssociatedPseudoOperation(IR::Opcode::GetOverflowFromOp); + + auto args = ctx.reg_alloc.GetArgumentInfo(inst); + const size_t N = args[1].GetImmediateU8(); + ASSERT(N >= 1 && N <= 32); + + if (N == 32) { + ctx.reg_alloc.DefineAsExisting(inst, args[0]); + if (overflow_inst) { + auto Woverflow = ctx.reg_alloc.WriteW(overflow_inst); + RegAlloc::Realize(Woverflow); + code.MOV(*Woverflow, WZR); + } + return; + } + + const u32 positive_saturated_value = (1u << (N - 1)) - 1; + const u32 negative_saturated_value = ~u32{0} << (N - 1); + + auto Woperand = ctx.reg_alloc.ReadW(args[0]); + auto Wresult = ctx.reg_alloc.WriteW(inst); + RegAlloc::Realize(Woperand, Wresult); + ctx.reg_alloc.SpillFlags(); + + code.MOV(Wscratch0, negative_saturated_value); + code.MOV(Wscratch1, positive_saturated_value); + code.CMP(*Woperand, Wscratch0); + code.CSEL(Wresult, Woperand, Wscratch0, GT); + code.CMP(*Woperand, Wscratch1); + code.CSEL(Wresult, Wresult, Wscratch1, LT); + + if (overflow_inst) { + auto Woverflow = ctx.reg_alloc.WriteW(overflow_inst); + RegAlloc::Realize(Woverflow); + code.CMP(*Wresult, Woperand); + code.CSET(Woverflow, NE); + } +} + +template<> +void EmitIR<IR::Opcode::UnsignedSaturation>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) { + const auto overflow_inst = inst->GetAssociatedPseudoOperation(IR::Opcode::GetOverflowFromOp); + + auto args = ctx.reg_alloc.GetArgumentInfo(inst); + auto Wresult = ctx.reg_alloc.WriteW(inst); + auto Woperand = ctx.reg_alloc.ReadW(args[0]); + RegAlloc::Realize(Wresult, Woperand); + ctx.reg_alloc.SpillFlags(); + + const size_t N = args[1].GetImmediateU8(); + ASSERT(N <= 31); + const u32 saturated_value = (1u << N) - 1; + + code.MOV(Wscratch0, saturated_value); + code.CMP(*Woperand, 0); + code.CSEL(Wresult, Woperand, WZR, GT); + code.CMP(*Woperand, Wscratch0); + code.CSEL(Wresult, Wresult, Wscratch0, LT); + + if (overflow_inst) { + auto Woverflow = ctx.reg_alloc.WriteW(overflow_inst); + RegAlloc::Realize(Woverflow); + code.CSET(Woverflow, HI); + } +} + +template<> +void EmitIR<IR::Opcode::SignedSaturatedAdd8>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) { + (void)code; + (void)ctx; + (void)inst; + ASSERT_FALSE("Unimplemented"); +} + +template<> +void EmitIR<IR::Opcode::SignedSaturatedAdd16>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) { + (void)code; + (void)ctx; + (void)inst; + ASSERT_FALSE("Unimplemented"); +} + +template<> +void EmitIR<IR::Opcode::SignedSaturatedAdd32>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) { + (void)code; + (void)ctx; + (void)inst; + ASSERT_FALSE("Unimplemented"); +} + +template<> +void EmitIR<IR::Opcode::SignedSaturatedAdd64>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) { + (void)code; + (void)ctx; + (void)inst; + ASSERT_FALSE("Unimplemented"); +} + +template<> +void EmitIR<IR::Opcode::SignedSaturatedDoublingMultiplyReturnHigh16>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) { + (void)code; + (void)ctx; + (void)inst; + ASSERT_FALSE("Unimplemented"); +} + +template<> +void EmitIR<IR::Opcode::SignedSaturatedDoublingMultiplyReturnHigh32>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) { + (void)code; + (void)ctx; + (void)inst; + ASSERT_FALSE("Unimplemented"); +} + +template<> +void EmitIR<IR::Opcode::SignedSaturatedSub8>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) { + (void)code; + (void)ctx; + (void)inst; + ASSERT_FALSE("Unimplemented"); +} + +template<> +void EmitIR<IR::Opcode::SignedSaturatedSub16>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) { + (void)code; + (void)ctx; + (void)inst; + ASSERT_FALSE("Unimplemented"); +} + +template<> +void EmitIR<IR::Opcode::SignedSaturatedSub32>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) { + (void)code; + (void)ctx; + (void)inst; + ASSERT_FALSE("Unimplemented"); +} + +template<> +void EmitIR<IR::Opcode::SignedSaturatedSub64>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) { + (void)code; + (void)ctx; + (void)inst; + ASSERT_FALSE("Unimplemented"); +} + +template<> +void EmitIR<IR::Opcode::UnsignedSaturatedAdd8>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) { + (void)code; + (void)ctx; + (void)inst; + ASSERT_FALSE("Unimplemented"); +} + +template<> +void EmitIR<IR::Opcode::UnsignedSaturatedAdd16>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) { + (void)code; + (void)ctx; + (void)inst; + ASSERT_FALSE("Unimplemented"); +} + +template<> +void EmitIR<IR::Opcode::UnsignedSaturatedAdd32>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) { + (void)code; + (void)ctx; + (void)inst; + ASSERT_FALSE("Unimplemented"); +} + +template<> +void EmitIR<IR::Opcode::UnsignedSaturatedAdd64>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) { + (void)code; + (void)ctx; + (void)inst; + ASSERT_FALSE("Unimplemented"); +} + +template<> +void EmitIR<IR::Opcode::UnsignedSaturatedSub8>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) { + (void)code; + (void)ctx; + (void)inst; + ASSERT_FALSE("Unimplemented"); +} + +template<> +void EmitIR<IR::Opcode::UnsignedSaturatedSub16>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) { + (void)code; + (void)ctx; + (void)inst; + ASSERT_FALSE("Unimplemented"); +} + +template<> +void EmitIR<IR::Opcode::UnsignedSaturatedSub32>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) { + (void)code; + (void)ctx; + (void)inst; + ASSERT_FALSE("Unimplemented"); +} + +template<> +void EmitIR<IR::Opcode::UnsignedSaturatedSub64>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) { + (void)code; + (void)ctx; + (void)inst; + ASSERT_FALSE("Unimplemented"); +} + +} // namespace Dynarmic::Backend::Arm64 |