1 /* This file is part of the dynarmic project.
2  * Copyright (c) 2020 MerryMage
3  * SPDX-License-Identifier: 0BSD
4  */
5 
6 #include "frontend/A32/translate/impl/translate_arm.h"
7 
8 #include <optional>
9 #include <tuple>
10 #include "common/bit_util.h"
11 
12 namespace Dynarmic::A32 {
13 
14 namespace {
15 
DecodeType(Imm<4> type,size_t size,size_t align)16 std::optional<std::tuple<size_t, size_t, size_t>> DecodeType(Imm<4> type, size_t size, size_t align) {
17     switch (type.ZeroExtend()) {
18     case 0b0111: // VST1 A1 / VLD1 A1
19         if (Common::Bit<1>(align)) {
20             return std::nullopt;
21         }
22         return std::tuple<size_t, size_t, size_t>{1, 1, 0};
23     case 0b1010: // VST1 A2 / VLD1 A2
24         if (align == 0b11) {
25             return std::nullopt;
26         }
27         return std::tuple<size_t, size_t, size_t>{1, 2, 0};
28     case 0b0110: // VST1 A3 / VLD1 A3
29         if (Common::Bit<1>(align)) {
30             return std::nullopt;
31         }
32         return std::tuple<size_t, size_t, size_t>{1, 3, 0};
33     case 0b0010: // VST1 A4 / VLD1 A4
34         return std::tuple<size_t, size_t, size_t>{1, 4, 0};
35     case 0b1000: // VST2 A1 / VLD2 A1
36         if (size == 0b11 || align == 0b11) {
37             return std::nullopt;
38         }
39         return std::tuple<size_t, size_t, size_t>{2, 1, 1};
40     case 0b1001: // VST2 A1 / VLD2 A1
41         if (size == 0b11 || align == 0b11) {
42             return std::nullopt;
43         }
44         return std::tuple<size_t, size_t, size_t>{2, 1, 2};
45     case 0b0011: // VST2 A2 / VLD2 A2
46         if (size == 0b11) {
47             return std::nullopt;
48         }
49         return std::tuple<size_t, size_t, size_t>{2, 2, 2};
50     case 0b0100: // VST3 / VLD3
51         if (size == 0b11 || Common::Bit<1>(align)) {
52             return std::nullopt;
53         }
54         return std::tuple<size_t, size_t, size_t>{3, 1, 1};
55     case 0b0101: // VST3 / VLD3
56         if (size == 0b11 || Common::Bit<1>(align)) {
57             return std::nullopt;
58         }
59         return std::tuple<size_t, size_t, size_t>{3, 1, 2};
60     case 0b0000: // VST4 / VLD4
61         if (size == 0b11) {
62             return std::nullopt;
63         }
64         return std::tuple<size_t, size_t, size_t>{4, 1, 1};
65     case 0b0001: // VST4 / VLD4
66         if (size == 0b11) {
67             return std::nullopt;
68         }
69         return std::tuple<size_t, size_t, size_t>{4, 1, 2};
70     }
71     ASSERT_FALSE("Decode error");
72 }
73 } // anoynmous namespace
74 
v8_VST_multiple(bool D,Reg n,size_t Vd,Imm<4> type,size_t size,size_t align,Reg m)75 bool ArmTranslatorVisitor::v8_VST_multiple(bool D, Reg n, size_t Vd, Imm<4> type, size_t size, size_t align, Reg m) {
76     const auto decoded_type = DecodeType(type, size, align);
77     if (!decoded_type) {
78         return UndefinedInstruction();
79     }
80     const auto [nelem, regs, inc] = *decoded_type;
81 
82     const ExtReg d = ToExtRegD(Vd, D);
83     const size_t d_last = RegNumber(d) + inc * (nelem - 1);
84     if (n == Reg::R15 || d_last + regs > 32) {
85         return UnpredictableInstruction();
86     }
87 
88     [[maybe_unused]] const size_t alignment = align == 0 ? 1 : 4 << align;
89     const size_t ebytes = static_cast<size_t>(1) << size;
90     const size_t elements = 8 / ebytes;
91 
92     const bool wback = m != Reg::R15;
93     const bool register_index = m != Reg::R15 && m != Reg::R13;
94 
95     IR::U32 address = ir.GetRegister(n);
96     for (size_t r = 0; r < regs; r++) {
97         for (size_t e = 0; e < elements; e++) {
98             for (size_t i = 0; i < nelem; i++) {
99                 const ExtReg ext_reg = d + i * inc + r;
100                 const IR::U64 shifted_element = ir.LogicalShiftRight(ir.GetExtendedRegister(ext_reg), ir.Imm8(static_cast<u8>(e * ebytes * 8)));
101                 const IR::UAny element = ir.LeastSignificant(8 * ebytes, shifted_element);
102                 ir.WriteMemory(8 * ebytes, address, element);
103 
104                 address = ir.Add(address, ir.Imm32(static_cast<u32>(ebytes)));
105             }
106         }
107     }
108 
109     if (wback) {
110         if (register_index) {
111             ir.SetRegister(n, ir.Add(ir.GetRegister(n), ir.GetRegister(m)));
112         } else {
113             ir.SetRegister(n, ir.Add(ir.GetRegister(n), ir.Imm32(static_cast<u32>(8 * nelem * regs))));
114         }
115     }
116 
117     return true;
118 }
119 
v8_VLD_multiple(bool D,Reg n,size_t Vd,Imm<4> type,size_t size,size_t align,Reg m)120 bool ArmTranslatorVisitor::v8_VLD_multiple(bool D, Reg n, size_t Vd, Imm<4> type, size_t size, size_t align, Reg m) {
121     const auto decoded_type = DecodeType(type, size, align);
122     if (!decoded_type) {
123         return UndefinedInstruction();
124     }
125     const auto [nelem, regs, inc] = *decoded_type;
126 
127     const ExtReg d = ToExtRegD(Vd, D);
128     const size_t d_last = RegNumber(d) + inc * (nelem - 1);
129     if (n == Reg::R15 || d_last + regs > 32) {
130         return UnpredictableInstruction();
131     }
132 
133     [[maybe_unused]] const size_t alignment = align == 0 ? 1 : 4 << align;
134     const size_t ebytes = static_cast<size_t>(1) << size;
135     const size_t elements = 8 / ebytes;
136 
137     const bool wback = m != Reg::R15;
138     const bool register_index = m != Reg::R15 && m != Reg::R13;
139 
140     for (size_t r = 0; r < regs; r++) {
141         for (size_t i = 0; i < nelem; i++) {
142             const ExtReg ext_reg = d + i * inc + r;
143             ir.SetExtendedRegister(ext_reg, ir.Imm64(0));
144         }
145     }
146 
147     IR::U32 address = ir.GetRegister(n);
148     for (size_t r = 0; r < regs; r++) {
149         for (size_t e = 0; e < elements; e++) {
150             for (size_t i = 0; i < nelem; i++) {
151                 const ExtReg ext_reg = d + i * inc + r;
152                 const IR::U64 element = ir.ZeroExtendToLong(ir.ReadMemory(ebytes * 8, address));
153                 const IR::U64 shifted_element = ir.LogicalShiftLeft(element, ir.Imm8(static_cast<u8>(e * ebytes * 8)));
154                 ir.SetExtendedRegister(ext_reg, ir.Or(ir.GetExtendedRegister(ext_reg), shifted_element));
155 
156                 address = ir.Add(address, ir.Imm32(static_cast<u32>(ebytes)));
157             }
158         }
159     }
160 
161     if (wback) {
162         if (register_index) {
163             ir.SetRegister(n, ir.Add(ir.GetRegister(n), ir.GetRegister(m)));
164         } else {
165             ir.SetRegister(n, ir.Add(ir.GetRegister(n), ir.Imm32(static_cast<u32>(8 * nelem * regs))));
166         }
167     }
168 
169     return true;
170 }
171 
172 } // namespace Dynarmic::A32
173