1 /*
2 * Copyright (C) 2024 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #ifndef BERBERIS_ASSEMBLER_COMMON_RISCV_H_
18 #define BERBERIS_ASSEMBLER_COMMON_RISCV_H_
19
20 #include <cstddef> // std::size_t
21 #include <cstdint>
22 #include <type_traits> // std::enable_if, std::is_integral
23
24 #include "berberis/assembler/common.h"
25 #include "berberis/base/bit_util.h"
26 #include "berberis/base/checks.h"
27
28 namespace berberis {
29
30 namespace rv32e {
31
32 class Assembler;
33
34 } // namespace rv32e
35
36 namespace rv32i {
37
38 class Assembler;
39
40 } // namespace rv32i
41
42 namespace rv64i {
43
44 class Assembler;
45
46 } // namespace rv64i
47
48 // riscv::Assembler includes implementation of most Risc V assembler instructions.
49 //
50 // RV32 and RV64 assemblers are nearly identical, but difference lies in handling
51 // of some instructions: RV32 uses certain encodings differently to handle compressed
52 // instructions, while RV64 adds some extra instructions to handle 32bit quantities
53 // (*not* 64bit quantities as the name implies, instead there are width-native instructions
54 // and extra 32bit ones for RV64).
55 //
56 // To handle that difference efficiently riscv::Assembler is CRTP class: it's parameterized
57 // by its own descendant and pull certain functions from its implementation.
58
59 namespace riscv {
60
61 template <typename DerivedAssemblerType>
62 class Assembler;
63
64 enum class Condition {
65 kInvalidCondition = -1,
66
67 kEqual = 0,
68 kNotEqual = 1,
69 kLess = 4,
70 kGreaterEqual = 5,
71 kBelow = 6,
72 kAboveEqual = 7,
73 kAlways = 8,
74 kNever = 9,
75
76 // aka...
77 kCarry = kBelow,
78 kNotCarry = kAboveEqual,
79 kZero = kEqual,
80 kNotZero = kNotEqual
81 };
82
83 enum class Csr {
84 kFFlags = 0b00'00'0000'0001,
85 kFrm = 0b00'00'0000'0010,
86 kFCsr = 0b00'00'0000'0011,
87 kVstart = 0b00'00'0000'1000,
88 kVxsat = 0b00'00'0000'1001,
89 kVxrm = 0b00'00'0000'1010,
90 kVcsr = 0b00'00'0000'1111,
91 kCycle = 0b11'00'0000'0000,
92 kVl = 0b11'00'0010'0000,
93 kVtype = 0b11'00'0010'0001,
94 kVlenb = 0b11'00'0010'0010,
95 };
96
97 enum class Rounding { kRne = 0, kRtz = 1, kRdn = 2, kRup = 3, kRmm = 4, kDyn = 7 };
98
99 // Immediates are kept in a form ready to be used with emitter.
100 class BImmediate;
101 class CsrImmediate;
102 class IImmediate;
103 using Immediate = IImmediate;
104 class JImmediate;
105 // In RISC V manual shifts are described as using I-format with complex restrictions for which
106 // immediates are accepted and allowed (with parts of what manual classifies as “immediate” used
107 // to determine the actual instruction used and rules which differ between RV32 and RV64!).
108 //
109 // Instead of doing special handling for the instructions in python scripts we just reclassify
110 // these parts of immediate as “opcode” and reclassify these instructions as “Shift32-type” and
111 // “Shift64-type”.
112 //
113 // This also means that the same instructions for RV32 and RV64 would have different types, but
114 // since we don't have a goal to make RV32 a strict subset of RV64 that's acceptable.
115 //
116 // In addition we provide aliases in RV32 and RV64 assemblers to make sure users of assembler may
117 // still use ShiftImmediate and MakeShiftImmediate for native width without thinking about
118 // details of implementation.
119 class Shift32Immediate;
120 class Shift64Immediate;
121 class PImmediate;
122 class SImmediate;
123 class UImmediate;
124
125 // Don't use templates here to enable implicit conversions.
126 #define BERBERIS_DEFINE_MAKE_IMMEDIATE(Immediate, MakeImmediate) \
127 constexpr std::optional<Immediate> MakeImmediate(int8_t value); \
128 constexpr std::optional<Immediate> MakeImmediate(uint8_t value); \
129 constexpr std::optional<Immediate> MakeImmediate(int16_t value); \
130 constexpr std::optional<Immediate> MakeImmediate(uint16_t value); \
131 constexpr std::optional<Immediate> MakeImmediate(int32_t value); \
132 constexpr std::optional<Immediate> MakeImmediate(uint32_t value); \
133 constexpr std::optional<Immediate> MakeImmediate(int64_t value); \
134 constexpr std::optional<Immediate> MakeImmediate(uint64_t value)
135 BERBERIS_DEFINE_MAKE_IMMEDIATE(BImmediate, MakeBImmediate);
136 BERBERIS_DEFINE_MAKE_IMMEDIATE(CsrImmediate, MakeCsrImmediate);
137 BERBERIS_DEFINE_MAKE_IMMEDIATE(IImmediate, MakeImmediate);
138 BERBERIS_DEFINE_MAKE_IMMEDIATE(IImmediate, MakeIImmediate);
139 BERBERIS_DEFINE_MAKE_IMMEDIATE(JImmediate, MakeJImmediate);
140 BERBERIS_DEFINE_MAKE_IMMEDIATE(PImmediate, MakePImmediate);
141 BERBERIS_DEFINE_MAKE_IMMEDIATE(Shift32Immediate, MakeShift32Immediate);
142 BERBERIS_DEFINE_MAKE_IMMEDIATE(Shift64Immediate, MakeShift64Immediate);
143 BERBERIS_DEFINE_MAKE_IMMEDIATE(SImmediate, MakeSImmediate);
144 BERBERIS_DEFINE_MAKE_IMMEDIATE(UImmediate, MakeUImmediate);
145 #undef BERBERIS_DEFINE_MAKE_IMMEDIATE
146
147 // RawImmediate is used to bypass checks in constructor. It's not supposed to be used directly.
148 class RawImmediate {
149 private:
150 friend class BImmediate;
151 friend class CsrImmediate;
152 friend class IImmediate;
153 friend class JImmediate;
154 friend class Shift32Immediate;
155 friend class Shift64Immediate;
156 friend class PImmediate;
157 friend class SImmediate;
158 friend class UImmediate;
159 template <typename DerivedAssemblerType>
160 friend class Assembler;
161
RawImmediate(int32_t value)162 constexpr RawImmediate(int32_t value) : value_(value) {}
163 int32_t value_;
164 };
165
166 #define BERBERIS_DEFINE_IMMEDIATE_CONSTRUCTOR(Immediate, IntType) \
167 constexpr Immediate(IntType value) : Immediate(MakeRaw(value)) { \
168 CHECK(AccetableValue(value)); \
169 }
170 #define BERBERIS_DEFINE_IMMEDIATE(Immediate, MakeImmediate, kMaskValue, ...) \
171 class Immediate { \
172 public: \
173 static constexpr int32_t kMask = static_cast<int32_t>(kMaskValue); \
174 \
175 BERBERIS_DEFINE_IMMEDIATE_CONSTRUCTOR(Immediate, int8_t) \
176 BERBERIS_DEFINE_IMMEDIATE_CONSTRUCTOR(Immediate, uint8_t) \
177 BERBERIS_DEFINE_IMMEDIATE_CONSTRUCTOR(Immediate, int16_t) \
178 BERBERIS_DEFINE_IMMEDIATE_CONSTRUCTOR(Immediate, uint16_t) \
179 BERBERIS_DEFINE_IMMEDIATE_CONSTRUCTOR(Immediate, int32_t) \
180 BERBERIS_DEFINE_IMMEDIATE_CONSTRUCTOR(Immediate, uint32_t) \
181 BERBERIS_DEFINE_IMMEDIATE_CONSTRUCTOR(Immediate, int64_t) \
182 BERBERIS_DEFINE_IMMEDIATE_CONSTRUCTOR(Immediate, uint64_t) \
183 \
184 constexpr Immediate() : value_(0) {} \
185 \
186 constexpr int32_t EncodedValue() { \
187 return value_; \
188 } \
189 \
190 friend bool operator==(Immediate const&, Immediate const&) = default; \
191 \
192 template <typename DerivedAssemblerType> \
193 friend class Assembler; \
194 friend constexpr std::optional<Immediate> MakeImmediate(int8_t value); \
195 friend constexpr std::optional<Immediate> MakeImmediate(uint8_t value); \
196 friend constexpr std::optional<Immediate> MakeImmediate(int16_t value); \
197 friend constexpr std::optional<Immediate> MakeImmediate(uint16_t value); \
198 friend constexpr std::optional<Immediate> MakeImmediate(int32_t value); \
199 friend constexpr std::optional<Immediate> MakeImmediate(uint32_t value); \
200 friend constexpr std::optional<Immediate> MakeImmediate(int64_t value); \
201 friend constexpr std::optional<Immediate> MakeImmediate(uint64_t value); \
202 __VA_ARGS__ \
203 \
204 private: \
205 constexpr Immediate(RawImmediate raw) : value_(raw.value_) {} \
206 /* Return true if value would fit into immediate. */ \
207 template <typename IntType> \
208 static constexpr bool AccetableValue(IntType value); \
209 /* Make RawImmediate from immediate value. */ \
210 /* Note: value is not checked for correctness! Public interface is MakeImmediate factory. */ \
211 template <typename IntType> \
212 static constexpr RawImmediate MakeRaw(IntType value); \
213 \
214 int32_t value_; \
215 }
216 BERBERIS_DEFINE_IMMEDIATE(
217 BImmediate,
218 MakeBImmediate,
219 0xfe00'0f80,
220 explicit constexpr operator int16_t() const {
221 return ((value_ >> 7) & 0x001e) | ((value_ >> 20) & 0xf7e0) |
222 ((value_ << 4) & 0x0800);
223 }
224 explicit constexpr operator int32_t() const {
225 return ((value_ >> 7) & 0x0000'001e) | ((value_ >> 20) & 0xffff'f7e0) |
226 ((value_ << 4) & 0x0000'0800);
227 }
228 explicit constexpr operator int64_t() const {
229 return ((value_ >> 7) & 0x0000'0000'0000'001e) | ((value_ >> 20) & 0xffff'ffff'ffff'f7e0) |
230 ((value_ << 4) & 0x0000'0000'0000'0800);
231 });
232 BERBERIS_DEFINE_IMMEDIATE(
233 CsrImmediate,
234 MakeCsrImmediate,
235 0x000f'8000,
236 explicit constexpr operator int8_t() const { return value_ >> 15; }
237 explicit constexpr operator uint8_t() const { return value_ >> 15; }
238 explicit constexpr operator int16_t() const { return value_ >> 15; }
239 explicit constexpr operator uint16_t() const { return value_ >> 15; }
240 explicit constexpr operator int32_t() const { return value_ >> 15; }
241 explicit constexpr operator uint32_t() const { return value_ >> 15; }
242 explicit constexpr operator int64_t() const { return value_ >> 15;}
243 explicit constexpr operator uint64_t() const { return value_ >> 15; });
244 BERBERIS_DEFINE_IMMEDIATE(
245 IImmediate, MakeIImmediate, 0xfff0'0000, constexpr IImmediate(SImmediate s_imm);
246
247 explicit constexpr operator int16_t() const { return value_ >> 20; }
248 explicit constexpr operator int32_t() const { return value_ >> 20; }
249 explicit constexpr operator int64_t() const { return value_ >> 20; }
250
251 friend SImmediate;);
252 BERBERIS_DEFINE_IMMEDIATE(
253 JImmediate,
254 MakeJImmediate,
255 0xffff'f000,
256 explicit constexpr operator int32_t() const {
257 return ((value_ >> 20) & 0xfff0'07fe) | ((value_ >> 9) & 0x0000'0800) |
258 (value_ & 0x000f'f000);
259 }
260 explicit constexpr operator int64_t() const {
261 return ((value_ >> 20) & 0xffff'ffff'fff0'07fe) | ((value_ >> 9) & 0x0000'0000'0000'0800) |
262 (value_ & 0x0000'0000'000f'f000);
263 });
264 BERBERIS_DEFINE_IMMEDIATE(
265 PImmediate,
266 MakePImmediate,
267 0xfe00'0000,
268 explicit constexpr
269 operator int16_t() const { return value_ >> 20; }
270 explicit constexpr operator int32_t() const { return value_ >> 20; }
271 explicit constexpr operator int64_t() const { return value_ >> 20; });
272 BERBERIS_DEFINE_IMMEDIATE(
273 Shift32Immediate,
274 MakeShift32Immediate,
275 0x01f0'0000,
276 explicit constexpr operator int8_t() const { return value_ >> 20; }
277 explicit constexpr operator uint8_t() const { return value_ >> 20; }
278 explicit constexpr operator int16_t() const { return value_ >> 20; }
279 explicit constexpr operator uint16_t() const { return value_ >> 20; }
280 explicit constexpr operator int32_t() const { return value_ >> 20; }
281 explicit constexpr operator uint32_t() const { return value_ >> 20; }
282 explicit constexpr operator int64_t() const { return value_ >> 20;}
283 explicit constexpr operator uint64_t() const { return value_ >> 20; });
284 BERBERIS_DEFINE_IMMEDIATE(
285 Shift64Immediate,
286 MakeShift64Immediate,
287 0x03f0'0000,
288 explicit constexpr operator int8_t() const { return value_ >> 20; }
289 explicit constexpr operator uint8_t() const { return value_ >> 20; }
290 explicit constexpr operator int16_t() const { return value_ >> 20; }
291 explicit constexpr operator uint16_t() const { return value_ >> 20; }
292 explicit constexpr operator int32_t() const { return value_ >> 20; }
293 explicit constexpr operator uint32_t() const { return value_ >> 20; }
294 explicit constexpr operator int64_t() const { return value_ >> 20;}
295 explicit constexpr operator uint64_t() const { return value_ >> 20; });
296 BERBERIS_DEFINE_IMMEDIATE(
297 SImmediate, MakeSImmediate, 0xfe00'0f80, constexpr SImmediate(Immediate imm);
298
299 explicit constexpr operator int16_t() const {
300 return ((value_ >> 7) & 0x0000'001f) | (value_ >> 20);
301 }
302 explicit constexpr operator int32_t() const {
303 return ((value_ >> 7) & 0x0000'001f) | (value_ >> 20);
304 }
305 explicit constexpr operator int64_t() const {
306 return ((value_ >> 7) & 0x0000'001f) | (value_ >> 20);
307 }
308
309 friend class IImmediate;);
310 BERBERIS_DEFINE_IMMEDIATE(
311 UImmediate,
312 MakeUImmediate,
313 0xffff'f000,
314 explicit constexpr operator int32_t() const { return value_; }
315 explicit constexpr operator int64_t() const { return value_; });
316 #undef BERBERIS_DEFINE_IMMEDIATE
317 #undef BERBERIS_DEFINE_IMMEDIATE_CONSTRUCTOR
318
IImmediate(SImmediate s_imm)319 constexpr IImmediate::IImmediate(SImmediate s_imm)
320 : value_((s_imm.value_ & 0xfe00'0000) | ((s_imm.value_ & 0x0000'0f80) << 13)) {}
321
SImmediate(Immediate imm)322 constexpr SImmediate::SImmediate(Immediate imm)
323 : value_((imm.value_ & 0xfe00'0000) | ((imm.value_ & 0x01f0'0000) >> 13)) {}
324
325 #define BERBERIS_DEFINE_MAKE_IMMEDIATE(Immediate, MakeImmediate, IntType) \
326 constexpr std::optional<Immediate> MakeImmediate(IntType value) { \
327 if (!Immediate::AccetableValue(value)) { \
328 return std::nullopt; \
329 } \
330 return Immediate{Immediate::MakeRaw(value)}; \
331 }
332 #define BERBERIS_DEFINE_MAKE_IMMEDIATE_SET(Immediate, MakeImmediate) \
333 BERBERIS_DEFINE_MAKE_IMMEDIATE(Immediate, MakeImmediate, int8_t) \
334 BERBERIS_DEFINE_MAKE_IMMEDIATE(Immediate, MakeImmediate, uint8_t) \
335 BERBERIS_DEFINE_MAKE_IMMEDIATE(Immediate, MakeImmediate, int16_t) \
336 BERBERIS_DEFINE_MAKE_IMMEDIATE(Immediate, MakeImmediate, uint16_t) \
337 BERBERIS_DEFINE_MAKE_IMMEDIATE(Immediate, MakeImmediate, int32_t) \
338 BERBERIS_DEFINE_MAKE_IMMEDIATE(Immediate, MakeImmediate, uint32_t) \
339 BERBERIS_DEFINE_MAKE_IMMEDIATE(Immediate, MakeImmediate, int64_t) \
340 BERBERIS_DEFINE_MAKE_IMMEDIATE(Immediate, MakeImmediate, uint64_t)
BERBERIS_DEFINE_MAKE_IMMEDIATE_SET(BImmediate,MakeBImmediate)341 BERBERIS_DEFINE_MAKE_IMMEDIATE_SET(BImmediate, MakeBImmediate)
342 BERBERIS_DEFINE_MAKE_IMMEDIATE_SET(CsrImmediate, MakeCsrImmediate)
343 BERBERIS_DEFINE_MAKE_IMMEDIATE_SET(IImmediate, MakeIImmediate)
344 BERBERIS_DEFINE_MAKE_IMMEDIATE_SET(JImmediate, MakeJImmediate)
345 BERBERIS_DEFINE_MAKE_IMMEDIATE_SET(PImmediate, MakePImmediate)
346 BERBERIS_DEFINE_MAKE_IMMEDIATE_SET(Shift32Immediate, MakeShift32Immediate)
347 BERBERIS_DEFINE_MAKE_IMMEDIATE_SET(Shift64Immediate, MakeShift64Immediate)
348 BERBERIS_DEFINE_MAKE_IMMEDIATE_SET(SImmediate, MakeSImmediate)
349 BERBERIS_DEFINE_MAKE_IMMEDIATE_SET(UImmediate, MakeUImmediate)
350 #undef BERBERIS_DEFINE_MAKE_IMMEDIATE_SET
351 #undef BERBERIS_DEFINE_MAKE_IMMEDIATE
352
353 #define BERBERIS_DEFINE_MAKE_IMMEDIATE(IntType) \
354 constexpr std::optional<Immediate> MakeImmediate(IntType value) { \
355 return MakeIImmediate(value); \
356 }
357 BERBERIS_DEFINE_MAKE_IMMEDIATE(int8_t)
358 BERBERIS_DEFINE_MAKE_IMMEDIATE(uint8_t)
359 BERBERIS_DEFINE_MAKE_IMMEDIATE(int16_t)
360 BERBERIS_DEFINE_MAKE_IMMEDIATE(uint16_t)
361 BERBERIS_DEFINE_MAKE_IMMEDIATE(int32_t)
362 BERBERIS_DEFINE_MAKE_IMMEDIATE(uint32_t)
363 BERBERIS_DEFINE_MAKE_IMMEDIATE(int64_t)
364 BERBERIS_DEFINE_MAKE_IMMEDIATE(uint64_t)
365 #undef BERBERIS_DEFINE_MAKE_IMMEDIATE
366
367 // Return true if value would fit into B-immediate.
368 template <typename IntType>
369 constexpr bool BImmediate::AccetableValue(IntType value) {
370 static_assert(std::is_integral_v<IntType>);
371 static_assert(sizeof(IntType) <= sizeof(uint64_t));
372 // B-immediate accepts 12 bits, but encodes signed even values, that's why we only may accept
373 // low 12 bits of any unsigned value.
374 // Encode mask as the largest accepted value plus one and cut it to IntType size.
375 constexpr uint64_t kUnsigned64bitInputMask = 0xffff'ffff'ffff'f001;
376 if constexpr (!std::is_signed_v<IntType>) {
377 constexpr IntType kUnsignedInputMask = static_cast<IntType>(kUnsigned64bitInputMask);
378 return static_cast<IntType>(value & kUnsignedInputMask) == IntType{0};
379 } else {
380 // For signed values we also accept the same values as for unsigned case, but also accept
381 // value that have all bits in am kUnsignedInputMask set.
382 // B-immediate compresses these into one single sign bit, but lowest bit have to be zero.
383 constexpr IntType kSignedInputMask = static_cast<IntType>(kUnsigned64bitInputMask);
384 return static_cast<IntType>(value & kSignedInputMask) == IntType{0} ||
385 static_cast<IntType>(value & kSignedInputMask) == (kSignedInputMask & ~int64_t{1});
386 }
387 }
388
389 // Return true if value would fit into Csr-immediate.
390 template <typename IntType>
AccetableValue(IntType value)391 constexpr bool CsrImmediate::AccetableValue(IntType value) {
392 static_assert(std::is_integral_v<IntType>);
393 static_assert(sizeof(IntType) <= sizeof(uint64_t));
394 // Csr immediate is unsigned immediate with possible values between 0 and 31.
395 // If we make value unsigned negative numbers would become numbers >127 and would be rejected.
396 return std::make_unsigned_t<IntType>(value) < 32;
397 }
398
399 // Return true if value would fit into immediate.
400 template <typename IntType>
401 constexpr bool Immediate::AccetableValue(IntType value) {
402 static_assert(std::is_integral_v<IntType>);
403 static_assert(sizeof(IntType) <= sizeof(uint64_t));
404 // I-immediate accepts 12 bits, but encodes signed values, that's why we only may accept low
405 // 11 bits of any unsigned value.
406 // Encode mask as the largest accepted value plus one and cut it to IntType size.
407 constexpr uint64_t kUnsigned64bitInputMask = 0xffff'ffff'ffff'f800;
408 if constexpr (!std::is_signed_v<IntType>) {
409 constexpr IntType kUnsignedInputMask = static_cast<IntType>(kUnsigned64bitInputMask);
410 return static_cast<IntType>(value & kUnsignedInputMask) == IntType{0};
411 } else {
412 // For signed values we accept the same values as for unsigned case, but also accept
413 // values that have all bits in kUnsignedInputMask set.
414 // I-immediate compresses these into one single sign bit.
415 constexpr IntType kSignedInputMask = static_cast<IntType>(kUnsigned64bitInputMask);
416 return static_cast<IntType>(value & kSignedInputMask) == IntType{0} ||
417 static_cast<IntType>(value & kSignedInputMask) == kSignedInputMask;
418 }
419 }
420
421 // Return true if value would fit into J-immediate.
422 template <typename IntType>
423 constexpr bool JImmediate::AccetableValue(IntType value) {
424 static_assert(std::is_integral_v<IntType>);
425 static_assert(sizeof(IntType) <= sizeof(uint64_t));
426 // J-immediate accepts 20 bits, but encodes signed even values, that's why we only may accept
427 // bits from 1 to 19 of any unsigned value. Encode mask as the largest accepted value plus 1 and
428 // cut it to IntType size.
429 constexpr uint64_t kUnsigned64bitInputMask = 0xffff'ffff'fff0'0001;
430 if constexpr (!std::is_signed_v<IntType>) {
431 constexpr IntType kUnsignedInputMask = static_cast<IntType>(kUnsigned64bitInputMask);
432 return static_cast<IntType>(value & kUnsignedInputMask) == IntType{0};
433 } else {
434 // For signed values we accept the same values as for unsigned case, but also accept
435 // value that have all bits in kUnsignedInputMask set except zero bit (which is zero).
436 // J-immediate compresses these into one single sign bit, but lowest bit have to be zero.
437 constexpr IntType kSignedInputMask = static_cast<IntType>(kUnsigned64bitInputMask);
438 return static_cast<IntType>(value & kSignedInputMask) == IntType{0} ||
439 static_cast<IntType>(value & kSignedInputMask) == (kSignedInputMask & ~int64_t{1});
440 }
441 }
442
443 // Return true if value would fit into P-immediate.
444 template <typename IntType>
445 constexpr bool PImmediate::AccetableValue(IntType value) {
446 static_assert(std::is_integral_v<IntType>);
447 static_assert(sizeof(IntType) <= sizeof(uint64_t));
448 // P-immediate accepts 7 bits, but encodes only values divisible by 32, that's why we only may
449 // accept bits from 5 to 10 of any unsigned value. Encode mask as the largest accepted value
450 // plus 31 and cut it to IntType size.
451 constexpr uint64_t kUnsigned64bitInputMask = 0xffff'ffff'ffff'f81f;
452 if constexpr (!std::is_signed_v<IntType>) {
453 constexpr IntType kUnsignedInputMask = static_cast<IntType>(kUnsigned64bitInputMask);
454 return static_cast<IntType>(value & kUnsignedInputMask) == IntType{0};
455 } else {
456 // For signed values we accept the same values as for unsigned case, but also accept
457 // value that have all bits in kUnsignedInputMask set except the lowest 5 bits (which are
458 // zero). P-immediate compresses these into one single sign bit, but lowest bits have to be
459 // zero.
460 constexpr IntType kSignedInputMask = static_cast<IntType>(kUnsigned64bitInputMask);
461 return static_cast<IntType>(value & kSignedInputMask) == IntType{0} ||
462 static_cast<IntType>(value & kSignedInputMask) == (kSignedInputMask & ~int64_t{0x1f});
463 }
464 }
465
466 // Return true if value would fit into Shift32-immediate.
467 template <typename IntType>
468 constexpr bool Shift32Immediate::AccetableValue(IntType value) {
469 static_assert(std::is_integral_v<IntType>);
470 static_assert(sizeof(IntType) <= sizeof(uint64_t));
471 // Shift32 immediate is unsigned immediate with possible values between 0 and 31.
472 // If we make value unsigned negative numbers would become numbers >127 and would be rejected.
473 return std::make_unsigned_t<IntType>(value) < 32;
474 }
475
476 // Return true if value would fit into Shift64-immediate.
477 template <typename IntType>
478 constexpr bool Shift64Immediate::AccetableValue(IntType value) {
479 static_assert(std::is_integral_v<IntType>);
480 static_assert(sizeof(IntType) <= sizeof(uint64_t));
481 // Shift64 immediate is unsigned immediate with possible values between 0 and 63.
482 // If we make value unsigned negative numbers would become numbers >127 and would be rejected.
483 return std::make_unsigned_t<IntType>(value) < 64;
484 }
485
486 // Immediate (I-immediate in RISC V documentation) and S-Immediate are siblings: they encode
487 // the same values but in a different way.
488 // AccetableValue are the same for that reason, but MakeRaw are different.
489 template <typename IntType>
490 constexpr bool SImmediate::AccetableValue(IntType value) {
491 return Immediate::AccetableValue(value);
492 }
493
494 // Return true if value would fit into U-immediate.
495 template <typename IntType>
496 constexpr bool UImmediate::AccetableValue(IntType value) {
497 static_assert(std::is_integral_v<IntType>);
498 static_assert(sizeof(IntType) <= sizeof(uint64_t));
499 // U-immediate accepts 20 bits, but encodes only values divisible by 4096, that's why we only
500 // may accept bits from 12 to 30 of any unsigned value. Encode mask as the largest accepted
501 // value plus 4095 and cut it to IntType size.
502 constexpr uint64_t kUnsigned64bitInputMask = 0xffff'ffff'8000'0fff;
503 if constexpr (!std::is_signed_v<IntType>) {
504 constexpr IntType kUnsignedInputMask = static_cast<IntType>(kUnsigned64bitInputMask);
505 return static_cast<IntType>(value & kUnsignedInputMask) == IntType{0};
506 } else {
507 // For signed values we accept the same values as for unsigned case, but also accept
508 // value that have all bits in kUnsignedInputMask set except lower 12 bits (which are zero).
509 // U-immediate compresses these into one single sign bit, but lowest bits have to be zero.
510 constexpr IntType kSignedInputMask = static_cast<IntType>(kUnsigned64bitInputMask);
511 return static_cast<IntType>(value & kSignedInputMask) == IntType{0} ||
512 static_cast<IntType>(value & kSignedInputMask) == (kSignedInputMask & ~int64_t{0xfff});
513 }
514 }
515
516 // Make RawImmediate from immediate value.
517 // Note: value is not checked for correctness here! Public interface is MakeBImmediate factory.
518 template <typename IntType>
519 constexpr RawImmediate BImmediate::MakeRaw(IntType value) {
520 static_assert(std::is_integral_v<IntType>);
521 static_assert(sizeof(IntType) <= sizeof(uint64_t));
522 // Note: we have to convert type to int32_t before processing it! Otherwise we would produce
523 // incorrect value for negative inputs since one single input sign in the small immediate would
524 // turn into many bits in the insruction.
525 return (static_cast<int32_t>(value) & static_cast<int32_t>(0x8000'0000)) |
526 ((static_cast<int32_t>(value) & static_cast<int32_t>(0x0000'0800)) >> 4) |
527 ((static_cast<int32_t>(value) & static_cast<int32_t>(0x0000'001f)) << 7) |
528 ((static_cast<int32_t>(value) & static_cast<int32_t>(0x0000'07e0)) << 20);
529 }
530
531 // Make RawImmediate from immediate value.
532 // Note: value is not checked for correctness here! Public interface is MakeImmediate factory.
533 template <typename IntType>
534 constexpr RawImmediate CsrImmediate::MakeRaw(IntType value) {
535 static_assert(std::is_integral_v<IntType>);
536 static_assert(sizeof(IntType) <= sizeof(uint64_t));
537 // Note: this is correct if input value is between 0 and 31, but that would be checked in
538 // MakeCsrImmediate.
539 return static_cast<int32_t>(value) << 15;
540 }
541
542 // Make RawImmediate from immediate value.
543 // Note: value is not checked for correctness here! Public interface is MakeImmediate factory.
544 template <typename IntType>
545 constexpr RawImmediate Immediate::MakeRaw(IntType value) {
546 static_assert(std::is_integral_v<IntType>);
547 static_assert(sizeof(IntType) <= sizeof(uint64_t));
548 return static_cast<int32_t>(value) << 20;
549 }
550
551 // Make RawImmediate from immediate value.
552 // Note: value is not checked for correctness here! Public interface is MakeJImmediate factory.
553 template <typename IntType>
554 constexpr RawImmediate JImmediate::MakeRaw(IntType value) {
555 static_assert(std::is_integral_v<IntType>);
556 static_assert(sizeof(IntType) <= sizeof(uint64_t));
557 // Note: we have to convert type to int32_t before processing it! Otherwise we would produce
558 // incorrect value for negative inputs since one single input sign in the small immediate would
559 // turn into many bits in the insruction.
560 return (static_cast<int32_t>(value) & static_cast<int32_t>(0x800f'f000)) |
561 ((static_cast<int32_t>(value) & static_cast<int32_t>(0x0000'0800)) << 9) |
562 ((static_cast<int32_t>(value) & static_cast<int32_t>(0x0000'07fe)) << 20);
563 }
564
565 // Make RawImmediate from immediate value.
566 // Note: value is not checked for correctness here! Public interface is MakeImmediate factory.
567 template <typename IntType>
MakeRaw(IntType value)568 constexpr RawImmediate PImmediate::MakeRaw(IntType value) {
569 static_assert(std::is_integral_v<IntType>);
570 static_assert(sizeof(IntType) <= sizeof(uint64_t));
571 // Note: this is correct if input value is divisible by 32, but that would be checked in
572 // MakePImmediate.
573 return static_cast<int32_t>(value) << 20;
574 }
575
576 // Make RawImmediate from immediate value.
577 // Note: value is not checked for correctness here! Public interface is MakeImmediate factory.
578 template <typename IntType>
MakeRaw(IntType value)579 constexpr RawImmediate Shift32Immediate::MakeRaw(IntType value) {
580 static_assert(std::is_integral_v<IntType>);
581 static_assert(sizeof(IntType) <= sizeof(uint64_t));
582 // Note: this is correct if input value is between 0 and 31, but that would be checked in
583 // MakeShift32Immediate.
584 return static_cast<int32_t>(value) << 20;
585 }
586
587 // Make RawImmediate from immediate value.
588 // Note: value is not checked for correctness here! Public interface is MakeImmediate factory.
589 template <typename IntType>
MakeRaw(IntType value)590 constexpr RawImmediate Shift64Immediate::MakeRaw(IntType value) {
591 static_assert(std::is_integral_v<IntType>);
592 static_assert(sizeof(IntType) <= sizeof(uint64_t));
593 // Note: this is only correct if input value is between 0 and 63, but that would be checked in
594 // MakeShift64Immediate.
595 return static_cast<int32_t>(value) << 20;
596 }
597
598 // Make RawImmediate from immediate value.
599 // Note: value is not checked for correctness here! Public interface is MakeSImmediate factory.
600 template <typename IntType>
MakeRaw(IntType value)601 constexpr RawImmediate SImmediate::MakeRaw(IntType value) {
602 static_assert(std::is_integral_v<IntType>);
603 static_assert(sizeof(IntType) <= sizeof(uint64_t));
604 // Here, because we are only using platforms with 32bit ints conversion to 32bit signed int may
605 // happen both before masking and after but we are doing it before for consistency.
606 return ((static_cast<int32_t>(value) & static_cast<int32_t>(0xffff'ffe0)) << 20) |
607 ((static_cast<int32_t>(value) & static_cast<int32_t>(0x0000'001f)) << 7);
608 }
609
610 // Make RawImmediate from immediate value.
611 // Note: value is not checked for correctness here! Public interface is MakeImmediate factory.
612 template <typename IntType>
MakeRaw(IntType value)613 constexpr RawImmediate UImmediate::MakeRaw(IntType value) {
614 static_assert(std::is_integral_v<IntType>);
615 static_assert(sizeof(IntType) <= sizeof(uint64_t));
616 // Note: this is only correct if input value is between divisible by 4096 , but that would be
617 // checked in MakeUImmediate.
618 return static_cast<int32_t>(value);
619 }
620
621 template <typename DerivedAssemblerType>
622 class Assembler : public AssemblerBase {
623 public:
Assembler(MachineCode * code)624 explicit Assembler(MachineCode* code) : AssemblerBase(code) {}
625
626 using Condition = riscv::Condition;
627 using Csr = riscv::Csr;
628 using Rounding = riscv::Rounding;
629
630 class Register {
631 public:
632 constexpr bool operator==(const Register& reg) const { return num_ == reg.num_; }
633 constexpr bool operator!=(const Register& reg) const { return num_ != reg.num_; }
GetPhysicalIndex()634 constexpr uint8_t GetPhysicalIndex() { return num_; }
ValueForFmtSpec(Register value)635 friend constexpr uint8_t ValueForFmtSpec(Register value) { return value.num_; }
636 friend class Assembler<DerivedAssemblerType>;
637 friend class rv32e::Assembler;
638 friend class rv32i::Assembler;
639 friend class rv64i::Assembler;
640
641 private:
Register(uint8_t num)642 explicit constexpr Register(uint8_t num) : num_(num) {}
643 uint8_t num_;
644 };
645
646 // Note: register x0, technically, can be specified in assembler even if it doesn't exist
647 // as separate hardware register. It even have alias “zero” even in clang assembler.
648 static constexpr Register x0{0};
649 static constexpr Register x1{1};
650 static constexpr Register x2{2};
651 static constexpr Register x3{3};
652 static constexpr Register x4{4};
653 static constexpr Register x5{5};
654 static constexpr Register x6{6};
655 static constexpr Register x7{7};
656 static constexpr Register x8{8};
657 static constexpr Register x9{9};
658 static constexpr Register x10{10};
659 static constexpr Register x11{11};
660 static constexpr Register x12{12};
661 static constexpr Register x13{13};
662 static constexpr Register x14{14};
663 static constexpr Register x15{15};
664 static constexpr Register x16{16};
665 static constexpr Register x17{17};
666 static constexpr Register x18{18};
667 static constexpr Register x19{19};
668 static constexpr Register x20{20};
669 static constexpr Register x21{21};
670 static constexpr Register x22{22};
671 static constexpr Register x23{23};
672 static constexpr Register x24{24};
673 static constexpr Register x25{25};
674 static constexpr Register x26{26};
675 static constexpr Register x27{27};
676 static constexpr Register x28{28};
677 static constexpr Register x29{29};
678 static constexpr Register x30{30};
679 static constexpr Register x31{31};
680
681 // Aliases
682 static constexpr Register no_register{0x80};
683 static constexpr Register zero{0};
684
685 class FpRegister {
686 public:
687 constexpr bool operator==(const FpRegister& reg) const { return num_ == reg.num_; }
688 constexpr bool operator!=(const FpRegister& reg) const { return num_ != reg.num_; }
GetPhysicalIndex()689 constexpr uint8_t GetPhysicalIndex() { return num_; }
ValueForFmtSpec(FpRegister value)690 friend constexpr uint8_t ValueForFmtSpec(FpRegister value) { return value.num_; }
691 friend class Assembler<DerivedAssemblerType>;
692
693 private:
FpRegister(uint8_t num)694 explicit constexpr FpRegister(uint8_t num) : num_(num) {}
695 uint8_t num_;
696 };
697
698 static constexpr FpRegister f0{0};
699 static constexpr FpRegister f1{1};
700 static constexpr FpRegister f2{2};
701 static constexpr FpRegister f3{3};
702 static constexpr FpRegister f4{4};
703 static constexpr FpRegister f5{5};
704 static constexpr FpRegister f6{6};
705 static constexpr FpRegister f7{7};
706 static constexpr FpRegister f8{8};
707 static constexpr FpRegister f9{9};
708 static constexpr FpRegister f10{10};
709 static constexpr FpRegister f11{11};
710 static constexpr FpRegister f12{12};
711 static constexpr FpRegister f13{13};
712 static constexpr FpRegister f14{14};
713 static constexpr FpRegister f15{15};
714 static constexpr FpRegister f16{16};
715 static constexpr FpRegister f17{17};
716 static constexpr FpRegister f18{18};
717 static constexpr FpRegister f19{19};
718 static constexpr FpRegister f20{20};
719 static constexpr FpRegister f21{21};
720 static constexpr FpRegister f22{22};
721 static constexpr FpRegister f23{23};
722 static constexpr FpRegister f24{24};
723 static constexpr FpRegister f25{25};
724 static constexpr FpRegister f26{26};
725 static constexpr FpRegister f27{27};
726 static constexpr FpRegister f28{28};
727 static constexpr FpRegister f29{29};
728 static constexpr FpRegister f30{30};
729 static constexpr FpRegister f31{31};
730
731 // ABI
732 static constexpr FpRegister ft0{0};
733 static constexpr FpRegister ft1{1};
734 static constexpr FpRegister ft2{2};
735 static constexpr FpRegister ft3{3};
736 static constexpr FpRegister ft4{4};
737 static constexpr FpRegister ft5{5};
738 static constexpr FpRegister ft6{6};
739 static constexpr FpRegister ft7{7};
740 static constexpr FpRegister fs0{8};
741 static constexpr FpRegister fs1{9};
742 static constexpr FpRegister fa0{10};
743 static constexpr FpRegister fa1{11};
744 static constexpr FpRegister fa2{12};
745 static constexpr FpRegister fa3{13};
746 static constexpr FpRegister fa4{14};
747 static constexpr FpRegister fa5{15};
748 static constexpr FpRegister fa6{16};
749 static constexpr FpRegister fa7{17};
750 static constexpr FpRegister fs2{18};
751 static constexpr FpRegister fs3{19};
752 static constexpr FpRegister fs4{20};
753 static constexpr FpRegister fs5{21};
754 static constexpr FpRegister fs6{22};
755 static constexpr FpRegister fs7{23};
756 static constexpr FpRegister fs8{24};
757 static constexpr FpRegister fs9{25};
758 static constexpr FpRegister fs10{26};
759 static constexpr FpRegister fs11{27};
760 static constexpr FpRegister ft8{28};
761 static constexpr FpRegister ft9{29};
762 static constexpr FpRegister ft10{30};
763 static constexpr FpRegister ft11{31};
764
765 class VRegister {
766 public:
767 constexpr bool operator==(const VRegister& reg) const { return num_ == reg.num_; }
768 constexpr bool operator!=(const VRegister& reg) const { return num_ != reg.num_; }
GetPhysicalIndex()769 constexpr uint8_t GetPhysicalIndex() { return num_; }
ValueForFmtSpec(VRegister value)770 friend constexpr uint8_t ValueForFmtSpec(VRegister value) { return value.num_; }
771 friend class Assembler<DerivedAssemblerType>;
772
773 private:
VRegister(uint8_t num)774 explicit constexpr VRegister(uint8_t num) : num_(num) {}
775 uint8_t num_;
776 };
777
778 static constexpr VRegister no_v_register{0x80};
779 static constexpr VRegister v0{0};
780 static constexpr VRegister v1{1};
781 static constexpr VRegister v2{2};
782 static constexpr VRegister v3{3};
783 static constexpr VRegister v4{4};
784 static constexpr VRegister v5{5};
785 static constexpr VRegister v6{6};
786 static constexpr VRegister v7{7};
787 static constexpr VRegister v8{8};
788 static constexpr VRegister v9{9};
789 static constexpr VRegister v10{10};
790 static constexpr VRegister v11{11};
791 static constexpr VRegister v12{12};
792 static constexpr VRegister v13{13};
793 static constexpr VRegister v14{14};
794 static constexpr VRegister v15{15};
795 static constexpr VRegister v16{16};
796 static constexpr VRegister v17{17};
797 static constexpr VRegister v18{18};
798 static constexpr VRegister v19{19};
799 static constexpr VRegister v20{20};
800 static constexpr VRegister v21{21};
801 static constexpr VRegister v22{22};
802 static constexpr VRegister v23{23};
803 static constexpr VRegister v24{24};
804 static constexpr VRegister v25{25};
805 static constexpr VRegister v26{26};
806 static constexpr VRegister v27{27};
807 static constexpr VRegister v28{28};
808 static constexpr VRegister v29{29};
809 static constexpr VRegister v30{30};
810 static constexpr VRegister v31{31};
811
812 template <typename RegisterType, typename ImmediateType>
813 struct Operand {
814 RegisterType base{0};
815 ImmediateType disp = 0;
816 };
817
818 using BImmediate = riscv::BImmediate;
819 using CsrImmediate = riscv::CsrImmediate;
820 using IImmediate = riscv::IImmediate;
821 using Immediate = riscv::Immediate;
822 using JImmediate = riscv::JImmediate;
823 using Shift32Immediate = riscv::Shift32Immediate;
824 using Shift64Immediate = riscv::Shift64Immediate;
825 using PImmediate = riscv::PImmediate;
826 using SImmediate = riscv::SImmediate;
827 using UImmediate = riscv::UImmediate;
828
829 // Don't use templates here to enable implicit conversions.
830 #define BERBERIS_DEFINE_MAKE_IMMEDIATE(Immediate, MakeImmediate) \
831 static constexpr std::optional<Immediate> MakeImmediate(int8_t value) { \
832 return riscv::MakeImmediate(value); \
833 } \
834 static constexpr std::optional<Immediate> MakeImmediate(uint8_t value) { \
835 return riscv::MakeImmediate(value); \
836 } \
837 static constexpr std::optional<Immediate> MakeImmediate(int16_t value) { \
838 return riscv::MakeImmediate(value); \
839 } \
840 static constexpr std::optional<Immediate> MakeImmediate(uint16_t value) { \
841 return riscv::MakeImmediate(value); \
842 } \
843 static constexpr std::optional<Immediate> MakeImmediate(int32_t value) { \
844 return riscv::MakeImmediate(value); \
845 } \
846 static constexpr std::optional<Immediate> MakeImmediate(uint32_t value) { \
847 return riscv::MakeImmediate(value); \
848 } \
849 static constexpr std::optional<Immediate> MakeImmediate(int64_t value) { \
850 return riscv::MakeImmediate(value); \
851 } \
852 static constexpr std::optional<Immediate> MakeImmediate(uint64_t value) { \
853 return riscv::MakeImmediate(value); \
854 }
BERBERIS_DEFINE_MAKE_IMMEDIATE(BImmediate,MakeBImmediate)855 BERBERIS_DEFINE_MAKE_IMMEDIATE(BImmediate, MakeBImmediate)
856 BERBERIS_DEFINE_MAKE_IMMEDIATE(CsrImmediate, MakeCsrImmediate)
857 BERBERIS_DEFINE_MAKE_IMMEDIATE(IImmediate, MakeImmediate)
858 BERBERIS_DEFINE_MAKE_IMMEDIATE(IImmediate, MakeIImmediate)
859 BERBERIS_DEFINE_MAKE_IMMEDIATE(JImmediate, MakeJImmediate)
860 BERBERIS_DEFINE_MAKE_IMMEDIATE(PImmediate, MakePImmediate)
861 BERBERIS_DEFINE_MAKE_IMMEDIATE(Shift32Immediate, MakeShift32Immediate)
862 BERBERIS_DEFINE_MAKE_IMMEDIATE(Shift64Immediate, MakeShift64Immediate)
863 BERBERIS_DEFINE_MAKE_IMMEDIATE(SImmediate, MakeSImmediate)
864 BERBERIS_DEFINE_MAKE_IMMEDIATE(UImmediate, MakeUImmediate)
865 #undef BERBERIS_DEFINE_MAKE_IMMEDIATE
866
867 // Macro operations.
868 void Finalize() { ResolveJumps(); }
869
870 void ResolveJumps();
871
872 // Instructions.
873 #include "berberis/assembler/gen_assembler_common_riscv-inl.h" // NOLINT generated file!
874
875 protected:
876 // Information about operands.
877 template <typename OperandType, typename = void>
878 class OperandInfo;
879
880 // Wrapped operand with information of where in the encoded instruction should it be placed.
881 template <typename OperandMarker, typename RegisterType>
882 struct RegisterOperand {
EncodeImmediateRegisterOperand883 constexpr int32_t EncodeImmediate() {
884 return value.GetPhysicalIndex()
885 << OperandInfo<RegisterOperand<OperandMarker, RegisterType>>::kOffset;
886 }
887
888 RegisterType value;
889 };
890
891 struct ConditionOperand {
EncodeImmediateConditionOperand892 constexpr int32_t EncodeImmediate() {
893 return static_cast<int32_t>(value) << OperandInfo<ConditionOperand>::kOffset;
894 }
895
896 Condition value;
897 };
898
899 struct RoundingOperand {
EncodeImmediateRoundingOperand900 constexpr int32_t EncodeImmediate() {
901 return static_cast<int32_t>(value) << OperandInfo<RoundingOperand>::kOffset;
902 }
903
904 Rounding value;
905 };
906
907 // Operand class markers. Note, these classes shouldn't ever be instantiated, they are just
908 // used to carry information about operands.
909 class RdMarker;
910 class Rs1Marker;
911 class Rs2Marker;
912 class Rs3Marker;
913
914 template <typename RegisterType>
915 class OperandInfo<RegisterOperand<RdMarker, RegisterType>> {
916 public:
917 static constexpr bool IsImmediate = false;
918 static constexpr uint8_t kOffset = 7;
919 static constexpr uint32_t kMask = 0x0000'0f80;
920 };
921
922 template <>
923 class OperandInfo<ConditionOperand> {
924 public:
925 static constexpr bool IsImmediate = false;
926 static constexpr uint8_t kOffset = 12;
927 static constexpr uint32_t kMask = 0x0000'7000;
928 };
929
930 template <>
931 class OperandInfo<RoundingOperand> {
932 public:
933 static constexpr bool IsImmediate = false;
934 static constexpr uint8_t kOffset = 12;
935 static constexpr uint32_t kMask = 0x0000'7000;
936 };
937
938 template <typename RegisterType>
939 class OperandInfo<RegisterOperand<Rs1Marker, RegisterType>> {
940 public:
941 static constexpr bool IsImmediate = false;
942 static constexpr uint8_t kOffset = 15;
943 static constexpr uint32_t kMask = 0x000f'8000;
944 };
945
946 template <typename RegisterType>
947 class OperandInfo<RegisterOperand<Rs2Marker, RegisterType>> {
948 public:
949 static constexpr bool IsImmediate = false;
950 static constexpr uint8_t kOffset = 20;
951 static constexpr uint32_t kMask = 0x01f0'0000;
952 };
953
954 template <typename RegisterType>
955 class OperandInfo<RegisterOperand<Rs3Marker, RegisterType>> {
956 public:
957 static constexpr bool IsImmediate = false;
958 static constexpr uint8_t kOffset = 27;
959 static constexpr uint32_t kMask = 0xf800'0000;
960 };
961
962 template <typename Immediate>
963 class OperandInfo<Immediate, std::enable_if_t<sizeof(Immediate::kMask) != 0>> {
964 public:
965 static constexpr bool IsImmediate = true;
966 static constexpr uint8_t kOffset = 0;
967 static constexpr uint32_t kMask = Immediate::kMask;
968 };
969
970 template <typename RegisterType>
Rd(RegisterType value)971 RegisterOperand<RdMarker, RegisterType> Rd(RegisterType value) {
972 return {value};
973 }
974
Cond(Condition value)975 ConditionOperand Cond(Condition value) { return {value}; }
976
Rm(Rounding value)977 RoundingOperand Rm(Rounding value) { return {value}; }
978
979 template <typename RegisterType>
Rs1(RegisterType value)980 RegisterOperand<Rs1Marker, RegisterType> Rs1(RegisterType value) {
981 return {value};
982 }
983
984 template <typename RegisterType>
Rs2(RegisterType value)985 RegisterOperand<Rs2Marker, RegisterType> Rs2(RegisterType value) {
986 return {value};
987 }
988
989 template <typename RegisterType>
Rs3(RegisterType value)990 RegisterOperand<Rs3Marker, RegisterType> Rs3(RegisterType value) {
991 return {value};
992 }
993
994 template <uint32_t kOpcode, uint32_t kOpcodeMask, typename... ArgumentsTypes>
EmitInstruction(ArgumentsTypes...arguments)995 void EmitInstruction(ArgumentsTypes... arguments) {
996 // All uncompressed instructions in RISC-V have two lowest bit set and we don't handle
997 // compressed instructions here.
998 static_assert((kOpcode & 0b11) == 0b11);
999 // Instruction shouldn't have any bits set outside of its opcode mask.
1000 static_assert((kOpcode & ~kOpcodeMask) == 0);
1001 // Places for all operands in the opcode should not intersect with opcode.
1002 static_assert((((kOpcodeMask & OperandInfo<ArgumentsTypes>::kMask) == 0) && ...));
1003 Emit32((kOpcode | ... | [](auto argument) {
1004 if constexpr (OperandInfo<decltype(argument)>::IsImmediate) {
1005 return argument.EncodedValue();
1006 } else {
1007 return argument.EncodeImmediate();
1008 }
1009 }(arguments)));
1010 }
1011
1012 template <uint32_t kOpcode,
1013 typename ArgumentsType0,
1014 typename ArgumentsType1,
1015 typename ImmediateType>
EmitBTypeInstruction(ArgumentsType0 && argument0,ArgumentsType1 && argument1,ImmediateType && immediate)1016 void EmitBTypeInstruction(ArgumentsType0&& argument0,
1017 ArgumentsType1&& argument1,
1018 ImmediateType&& immediate) {
1019 return EmitInstruction<kOpcode, 0x0000'707f>(Rs1(argument0), Rs2(argument1), immediate);
1020 }
1021
1022 template <uint32_t kOpcode, typename ArgumentsType0, typename OperandType>
1023 void EmitITypeInstruction(ArgumentsType0&& argument0, OperandType&& operand) {
1024 return EmitInstruction<kOpcode, 0x0000'707f>(Rd(argument0), Rs1(operand.base), operand.disp);
1025 }
1026
1027 // Csr instructions are described as I-type instructions in RISC-V manual, but unlike most
1028 // I-type instructions they use IImmediate to encode Csr register number and it comes as second
1029 // argument, not third. In addition Csr value is defined as unsigned and not as signed which
1030 // means certain Csr values (e.g. kVlenb) wouldn't be accepted as IImmediate!
1031 template <uint32_t kOpcode, typename ArgumentsType0>
EmitITypeInstruction(ArgumentsType0 && argument0,Csr csr,Register argument1)1032 void EmitITypeInstruction(ArgumentsType0&& argument0, Csr csr, Register argument1) {
1033 return EmitInstruction<kOpcode, 0x0000'707f>(
1034 Rd(argument0),
1035 IImmediate{riscv::RawImmediate{static_cast<int32_t>(csr) << 20}},
1036 Rs1(argument1));
1037 }
1038
1039 template <uint32_t kOpcode, typename ArgumentsType0>
1040 void EmitITypeInstruction(ArgumentsType0&& argument0, Csr csr, CsrImmediate immediate) {
1041 return EmitInstruction<kOpcode, 0x0000'707f>(
1042 Rd(argument0), IImmediate{riscv::RawImmediate{static_cast<int32_t>(csr) << 20}}, immediate);
1043 }
1044
1045 template <uint32_t kOpcode,
1046 typename ArgumentsType0,
1047 typename ArgumentsType1,
1048 typename ImmediateType>
EmitITypeInstruction(ArgumentsType0 && argument0,ArgumentsType1 && argument1,ImmediateType && immediate)1049 void EmitITypeInstruction(ArgumentsType0&& argument0,
1050 ArgumentsType1&& argument1,
1051 ImmediateType&& immediate) {
1052 // Some I-type instructions use immediate as opcode extension. In that case different,
1053 // smaller, immediate with smaller mask is used. 0xfff0'707f &
1054 // ~std::decay_t<ImmediateType>::kMask turns these bits that are not used as immediate into
1055 // parts of opcode. For full I-immediate it produces 0x0000'707f, same as with I-type memory
1056 // operand.
1057 return EmitInstruction<kOpcode, 0xfff0'707f & ~std::decay_t<ImmediateType>::kMask>(
1058 Rd(argument0), Rs1(argument1), immediate);
1059 }
1060
1061 template <uint32_t kOpcode, typename ArgumentsType0, typename ImmediateType>
1062 void EmitJTypeInstruction(ArgumentsType0&& argument0, ImmediateType&& immediate) {
1063 return EmitInstruction<kOpcode, 0x0000'007f>(Rd(argument0), immediate);
1064 }
1065
1066 template <uint32_t kOpcode, typename OperandType>
EmitPTypeInstruction(OperandType && operand)1067 void EmitPTypeInstruction(OperandType&& operand) {
1068 return EmitInstruction<kOpcode, 0x01f0'7fff>(Rs1(operand.base), operand.disp);
1069 }
1070
1071 template <uint32_t kOpcode, typename ArgumentsType0, typename ArgumentsType1>
1072 void EmitRTypeInstruction(ArgumentsType0&& argument0, ArgumentsType1&& argument1) {
1073 return EmitInstruction<kOpcode, 0xfff0'707f>(Rd(argument0), Rs1(argument1));
1074 }
1075
1076 template <uint32_t kOpcode, typename ArgumentsType0, typename ArgumentsType1>
EmitRTypeInstruction(ArgumentsType0 && argument0,ArgumentsType1 && argument1,Rounding argument2)1077 void EmitRTypeInstruction(ArgumentsType0&& argument0,
1078 ArgumentsType1&& argument1,
1079 Rounding argument2) {
1080 return EmitInstruction<kOpcode, 0xfff0'007f>(Rd(argument0), Rs1(argument1), Rm(argument2));
1081 }
1082
1083 template <uint32_t kOpcode,
1084 typename ArgumentsType0,
1085 typename ArgumentsType1,
1086 typename ArgumentsType2>
1087 void EmitRTypeInstruction(ArgumentsType0&& argument0,
1088 ArgumentsType1&& argument1,
1089 ArgumentsType2&& argument2) {
1090 return EmitInstruction<kOpcode, 0xfe00'707f>(Rd(argument0), Rs1(argument1), Rs2(argument2));
1091 }
1092
1093 template <uint32_t kOpcode, typename ArgumentsType0, typename OperandType>
EmitSTypeInstruction(ArgumentsType0 && argument0,OperandType && operand)1094 void EmitSTypeInstruction(ArgumentsType0&& argument0, OperandType&& operand) {
1095 return EmitInstruction<kOpcode, 0x0000'707f>(Rs2(argument0), Rs1(operand.base), operand.disp);
1096 }
1097
1098 template <uint32_t kOpcode, typename ArgumentsType0, typename ImmediateType>
1099 void EmitUTypeInstruction(ArgumentsType0&& argument0, ImmediateType&& immediate) {
1100 return EmitInstruction<kOpcode, 0x0000'007f>(Rd(argument0), immediate);
1101 }
1102
1103 private:
1104 Assembler() = delete;
1105 Assembler(const Assembler&) = delete;
1106 Assembler(Assembler&&) = delete;
1107 void operator=(const Assembler&) = delete;
1108 void operator=(Assembler&&) = delete;
1109 };
1110
1111 template <typename DerivedAssemblerType>
Bcc(Condition cc,Register argument1,Register argument2,const Label & label)1112 inline void Assembler<DerivedAssemblerType>::Bcc(Condition cc,
1113 Register argument1,
1114 Register argument2,
1115 const Label& label) {
1116 if (cc == Condition::kAlways) {
1117 Jal(zero, label);
1118 return;
1119 } else if (cc == Condition::kNever) {
1120 return;
1121 }
1122 CHECK_EQ(0, static_cast<uint8_t>(cc) & 0xf8);
1123 jumps_.push_back(Jump{&label, pc(), false});
1124 EmitInstruction<0x0000'0063, 0x0000'007f>(Cond(cc), Rs1(argument1), Rs2(argument2));
1125 }
1126
1127 template <typename DerivedAssemblerType>
Bcc(Condition cc,Register argument1,Register argument2,BImmediate immediate)1128 inline void Assembler<DerivedAssemblerType>::Bcc(Condition cc,
1129 Register argument1,
1130 Register argument2,
1131 BImmediate immediate) {
1132 if (cc == Condition::kAlways) {
1133 int32_t encoded_immediate_value = immediate.EncodedValue();
1134 // Maybe better to provide an official interface to convert BImmediate into JImmediate?
1135 // Most CPUs have uncoditional jump with longer range than condtional one (8086, ARM, RISC-V)
1136 // or the same one (modern x86), thus such conversion is natural.
1137 JImmediate jimmediate =
1138 riscv::RawImmediate{((encoded_immediate_value >> 19) & 0x000f'f000) |
1139 ((encoded_immediate_value << 13) & 0x01f0'0000) |
1140 (encoded_immediate_value & static_cast<int32_t>(0xfe00'0000))};
1141 Jal(zero, jimmediate);
1142 return;
1143 } else if (cc == Condition::kNever) {
1144 return;
1145 }
1146 CHECK_EQ(0, static_cast<uint8_t>(cc) & 0xf8);
1147 EmitInstruction<0x0000'0063, 0x0000'007f>(Cond(cc), Rs1(argument1), Rs2(argument2), immediate);
1148 }
1149
1150 #define BERBERIS_DEFINE_LOAD_OR_STORE_INSTRUCTION(Name, TargetRegister, InstructionType, Opcode) \
1151 template <typename DerivedAssemblerType> \
1152 inline void Assembler<DerivedAssemblerType>::Name( \
1153 TargetRegister arg0, const Label& label, Register arg2) { \
1154 CHECK_NE(arg2, x0); \
1155 jumps_.push_back(Jump{&label, pc(), false}); \
1156 /* First issue auipc to load top 20 bits of difference between pc and target address */ \
1157 EmitUTypeInstruction<uint32_t{0x0000'0017}>(arg2, UImmediate{0}); \
1158 /* The low 12 bite of difference will be encoded in the memory accessing instruction */ \
1159 Emit##InstructionType##TypeInstruction<uint32_t{Opcode}>( \
1160 arg0, Operand<Register, InstructionType##Immediate>{.base = arg2}); \
1161 }
1162 BERBERIS_DEFINE_LOAD_OR_STORE_INSTRUCTION(Fld, FpRegister, I, 0x0000'3007)
1163 BERBERIS_DEFINE_LOAD_OR_STORE_INSTRUCTION(Flw, FpRegister, I, 0x0000'2007)
1164 BERBERIS_DEFINE_LOAD_OR_STORE_INSTRUCTION(Fsd, FpRegister, S, 0x0000'3027)
1165 BERBERIS_DEFINE_LOAD_OR_STORE_INSTRUCTION(Fsw, FpRegister, S, 0x0000'2027)
1166 BERBERIS_DEFINE_LOAD_OR_STORE_INSTRUCTION(Sb, Register, S, 0x0000'0023)
1167 BERBERIS_DEFINE_LOAD_OR_STORE_INSTRUCTION(Sh, Register, S, 0x0000'1023)
1168 BERBERIS_DEFINE_LOAD_OR_STORE_INSTRUCTION(Sw, Register, S, 0x0000'2023)
1169 #undef BERBERIS_DEFINE_LOAD_OR_STORE_INSTRUCTION
1170
1171 #define BERBERIS_DEFINE_LOAD_INSTRUCTION(Name, Opcode) \
1172 template <typename DerivedAssemblerType> \
1173 inline void Assembler<DerivedAssemblerType>::Name(Register arg0, const Label& label) { \
1174 CHECK_NE(arg0, x0); \
1175 jumps_.push_back(Jump{&label, pc(), false}); \
1176 /* First issue auipc to load top 20 bits of difference between pc and target address */ \
1177 EmitUTypeInstruction<uint32_t{0x0000'0017}>(arg0, UImmediate{0}); \
1178 /* The low 12 bite of difference will be encoded in the memory accessing instruction */ \
1179 EmitITypeInstruction<uint32_t{Opcode}>(arg0, Operand<Register, IImmediate>{.base = arg0}); \
1180 }
1181 BERBERIS_DEFINE_LOAD_INSTRUCTION(Lb, 0x0000'0003)
1182 BERBERIS_DEFINE_LOAD_INSTRUCTION(Lbu, 0x0000'4003)
1183 BERBERIS_DEFINE_LOAD_INSTRUCTION(Lh, 0x0000'1003)
1184 BERBERIS_DEFINE_LOAD_INSTRUCTION(Lhu, 0x0000'5003)
1185 BERBERIS_DEFINE_LOAD_INSTRUCTION(Lw, 0x0000'2003)
1186 #undef BERBERIS_DEFINE_LOAD_INSTRUCTION
1187
1188 template <typename DerivedAssemblerType>
La(Register arg0,const Label & label)1189 inline void Assembler<DerivedAssemblerType>::La(Register arg0, const Label& label) {
1190 CHECK_NE(arg0, x0);
1191 jumps_.push_back(Jump{&label, pc(), false});
1192 // First issue auipc to load top 20 bits of difference between pc and target address
1193 EmitUTypeInstruction<uint32_t{0x0000'0017}>(arg0, UImmediate{0});
1194 // The low 12 bite of difference will be added with addi instruction
1195 EmitITypeInstruction<uint32_t{0x0000'0013}>(arg0, arg0, IImmediate{0});
1196 }
1197
1198 #define BERBERIS_DEFINE_CONDITIONAL_INSTRUCTION(Name, Opcode) \
1199 template <typename DerivedAssemblerType> \
1200 inline void Assembler<DerivedAssemblerType>::Name( \
1201 Register arg0, Register arg1, const Label& label) { \
1202 jumps_.push_back(Jump{&label, pc(), false}); \
1203 EmitBTypeInstruction<uint32_t{Opcode}>(arg0, arg1, BImmediate{0}); \
1204 }
1205 BERBERIS_DEFINE_CONDITIONAL_INSTRUCTION(Beq, 0x0000'0063)
1206 BERBERIS_DEFINE_CONDITIONAL_INSTRUCTION(Bge, 0x0000'5063)
1207 BERBERIS_DEFINE_CONDITIONAL_INSTRUCTION(Bgeu, 0x0000'7063)
1208 BERBERIS_DEFINE_CONDITIONAL_INSTRUCTION(Blt, 0x0000'4063)
1209 BERBERIS_DEFINE_CONDITIONAL_INSTRUCTION(Bltu, 0x0000'6063)
1210 BERBERIS_DEFINE_CONDITIONAL_INSTRUCTION(Bne, 0x0000'1063)
1211 #undef BERBERIS_DEFINE_CONDITIONAL_INSTRUCTION
1212
1213 template <typename DerivedAssemblerType>
Jal(Register argument0,const Label & label)1214 inline void Assembler<DerivedAssemblerType>::Jal(Register argument0, const Label& label) {
1215 jumps_.push_back(Jump{&label, pc(), false});
1216 EmitInstruction<0x0000'006f, 0x0000'007f>(Rd(argument0));
1217 }
1218
1219 template <typename DerivedAssemblerType>
1220 inline void Assembler<DerivedAssemblerType>::ResolveJumps() {
1221 for (const auto& jump : jumps_) {
1222 const Label* label = jump.label;
1223 uint32_t pc = jump.pc;
1224 CHECK(label->IsBound());
1225 if (jump.is_recovery) {
1226 // Add pc -> label correspondence to recovery map.
1227 AddRelocation(0, RelocationType::RelocRecoveryPoint, pc, label->position());
1228 } else {
1229 int32_t offset = label->position() - pc;
1230 auto ProcessLabel =
1231 [this, pc, offset]<typename ImmediateType,
1232 std::optional<ImmediateType> (*MakeImmediate)(int32_t)>() {
1233 auto encoded_immediate = MakeImmediate(offset);
1234 if (!encoded_immediate.has_value()) {
1235 // UImmediate means we are dealing with auipc here, means we may accept any
1236 // ±2GB offset, but need to look at the next instruction to do that.
1237 if constexpr (std::is_same_v<ImmediateType, UImmediate>) {
1238 // Bottom immediate is decoded with a 12 → 32 bit sign-extended.
1239 // Compensate that by adding sign-bit of bottom to top.
1240 // Make calculation as unsigned types to ensure we wouldn't hit any UB here.
1241 int32_t top = (static_cast<uint32_t>(offset) +
1242 ((static_cast<uint32_t>(offset) & (1U << 11)) * 2)) &
1243 0xffff'f000U;
1244 struct {
1245 int32_t data : 12;
1246 } bottom = {offset};
1247 *AddrAs<int32_t>(pc) |= UImmediate{top}.EncodedValue();
1248 *AddrAs<int32_t>(pc + 4) |= ((*AddrAs<int32_t>(pc + 4) & 96) == 32)
1249 ? SImmediate{bottom.data}.EncodedValue()
1250 : IImmediate{bottom.data}.EncodedValue();
1251 return true;
1252 }
1253 return false;
1254 }
1255 *AddrAs<int32_t>(pc) |= encoded_immediate->EncodedValue();
1256 return true;
1257 };
1258 // Check the instruction type:
1259 // AUIPC uses UImmediate, Jal uses JImmediate, while Bcc uses BImmediate.
1260 bool RelocationInRange;
1261 if (*AddrAs<int32_t>(pc) & 16) {
1262 RelocationInRange = ProcessLabel.template operator()<UImmediate, MakeUImmediate>();
1263 } else if (*AddrAs<int32_t>(pc) & 4) {
1264 RelocationInRange = ProcessLabel.template operator()<JImmediate, MakeJImmediate>();
1265 } else {
1266 RelocationInRange = ProcessLabel.template operator()<BImmediate, MakeBImmediate>();
1267 }
1268 // Maybe need to propagate error to caller?
1269 CHECK(RelocationInRange);
1270 }
1271 }
1272 }
1273
1274 template <typename DerivedAssemblerType>
1275 inline void Assembler<DerivedAssemblerType>::Mv(Register dest, Register src) {
1276 Addi(dest, src, 0);
1277 }
1278
1279 template <typename DerivedAssemblerType>
1280 inline void Assembler<DerivedAssemblerType>::Li(Register dest, int32_t imm32) {
1281 // If the value fits into 12bit I-Immediate type, load using addi.
1282 if (-2048 <= imm32 && imm32 <= 2047) {
1283 Addi(dest, Assembler::zero, static_cast<IImmediate>(imm32));
1284 } else {
1285 // Otherwise we need to use 2 instructions: lui to load top 20 bits and addi for bottom 12 bits,
1286 // however since the I-Immediate is signed, we could not just split the number into 2 parts: for
1287 // example loading 4095 should result in loading 1 in upper 20 bits (lui 0x1) and then
1288 // subtracting 1 (addi dest, dest, -1).
1289 // Perform calculations on unsigned type to avoid undefined behavior.
1290 uint32_t uimm = static_cast<uint32_t>(imm32);
1291 // Since bottom 12bits are loaded via a 12-bit signed immediate, we need to add the sign bit to
1292 // the top part.
1293 int32_t top = (uimm + ((uimm & (1U << 11)) << 1)) & 0xffff'f000;
1294 // Sign extends the bottom 12 bits.
1295 struct {
1296 int32_t data : 12;
1297 } bottom = {imm32};
1298 Lui(dest, static_cast<UImmediate>(top));
1299 if (bottom.data) {
1300 Addi(dest, dest, static_cast<IImmediate>(bottom.data));
1301 }
1302 }
1303 }
1304
1305 template <typename DerivedAssemblerType>
Ret()1306 inline void Assembler<DerivedAssemblerType>::Ret() {
1307 Jalr(Assembler::x0, Assembler::x1, static_cast<IImmediate>(0));
1308 }
1309
1310 template <typename DerivedAssemblerType>
Call(const Label & label)1311 inline void Assembler<DerivedAssemblerType>::Call(const Label& label) {
1312 jumps_.push_back(Jump{&label, pc(), false});
1313 // First issue auipc to load top 20 bits of difference between pc and target address
1314 EmitUTypeInstruction<uint32_t{0x0000'0017}>(Assembler::x6, UImmediate{0});
1315 // The low 12 bite of difference will be added with jalr instruction
1316 EmitITypeInstruction<uint32_t{0x0000'0067}>(Assembler::x1, Assembler::x6, IImmediate{0});
1317 }
1318
1319 template <typename DerivedAssemblerType>
Tail(const Label & label)1320 inline void Assembler<DerivedAssemblerType>::Tail(const Label& label) {
1321 jumps_.push_back(Jump{&label, pc(), false});
1322 // First issue auipc to load top 20 bits of difference between pc and target address
1323 EmitUTypeInstruction<uint32_t{0x0000'0017}>(Assembler::x6, UImmediate{0});
1324 // The low 12 bite of difference will be added with jalr instruction
1325 EmitITypeInstruction<uint32_t{0x0000'0067}>(Assembler::x0, Assembler::x6, IImmediate{0});
1326 }
1327
1328 template <typename DerivedAssemblerType>
Bgt(Register arg0,Register arg1,const Label & label)1329 inline void Assembler<DerivedAssemblerType>::Bgt(Register arg0, Register arg1, const Label& label) {
1330 Blt(arg1, arg0, label);
1331 }
1332
1333 template <typename DerivedAssemblerType>
Bgtu(Register arg0,Register arg1,const Label & label)1334 inline void Assembler<DerivedAssemblerType>::Bgtu(Register arg0,
1335 Register arg1,
1336 const Label& label) {
1337 Bltu(arg1, arg0, label);
1338 }
1339
1340 template <typename DerivedAssemblerType>
Ble(Register arg0,Register arg1,const Label & label)1341 inline void Assembler<DerivedAssemblerType>::Ble(Register arg0, Register arg1, const Label& label) {
1342 Bge(arg1, arg0, label);
1343 }
1344
1345 template <typename DerivedAssemblerType>
Bleu(Register arg0,Register arg1,const Label & label)1346 inline void Assembler<DerivedAssemblerType>::Bleu(Register arg0,
1347 Register arg1,
1348 const Label& label) {
1349 Bgeu(arg1, arg0, label);
1350 }
1351
1352 template <typename DerivedAssemblerType>
Beqz(Register arg0,const Label & label)1353 inline void Assembler<DerivedAssemblerType>::Beqz(Register arg0, const Label& label) {
1354 Beq(arg0, zero, label);
1355 }
1356
1357 template <typename DerivedAssemblerType>
Bnez(Register arg0,const Label & label)1358 inline void Assembler<DerivedAssemblerType>::Bnez(Register arg0, const Label& label) {
1359 Bne(arg0, zero, label);
1360 }
1361
1362 template <typename DerivedAssemblerType>
Blez(Register arg0,const Label & label)1363 inline void Assembler<DerivedAssemblerType>::Blez(Register arg0, const Label& label) {
1364 Ble(arg0, zero, label);
1365 }
1366
1367 template <typename DerivedAssemblerType>
Bgez(Register arg0,const Label & label)1368 inline void Assembler<DerivedAssemblerType>::Bgez(Register arg0, const Label& label) {
1369 Bge(arg0, zero, label);
1370 }
1371
1372 template <typename DerivedAssemblerType>
Bltz(Register arg0,const Label & label)1373 inline void Assembler<DerivedAssemblerType>::Bltz(Register arg0, const Label& label) {
1374 Blt(arg0, zero, label);
1375 }
1376
1377 template <typename DerivedAssemblerType>
Bgtz(Register arg0,const Label & label)1378 inline void Assembler<DerivedAssemblerType>::Bgtz(Register arg0, const Label& label) {
1379 Bgt(arg0, zero, label);
1380 }
1381
1382 template <typename DerivedAssemblerType>
Seqz(Register arg0,Register arg1)1383 inline void Assembler<DerivedAssemblerType>::Seqz(Register arg0, Register arg1) {
1384 Sltiu(arg0, arg1, static_cast<IImmediate>(1));
1385 }
1386
1387 template <typename DerivedAssemblerType>
Snez(Register arg0,Register arg1)1388 inline void Assembler<DerivedAssemblerType>::Snez(Register arg0, Register arg1) {
1389 Sltu(arg0, zero, arg1);
1390 }
1391
1392 template <typename DerivedAssemblerType>
Sltz(Register arg0,Register arg1)1393 inline void Assembler<DerivedAssemblerType>::Sltz(Register arg0, Register arg1) {
1394 Slt(arg0, arg1, zero);
1395 }
1396
1397 template <typename DerivedAssemblerType>
Sgtz(Register arg0,Register arg1)1398 inline void Assembler<DerivedAssemblerType>::Sgtz(Register arg0, Register arg1) {
1399 Slt(arg0, zero, arg1);
1400 }
1401
1402 template <typename DerivedAssemblerType>
J(JImmediate arg0)1403 inline void Assembler<DerivedAssemblerType>::J(JImmediate arg0) {
1404 Jal(zero, arg0);
1405 }
1406
1407 template <typename DerivedAssemblerType>
Jal(JImmediate arg0)1408 inline void Assembler<DerivedAssemblerType>::Jal(JImmediate arg0) {
1409 Jal(x1, arg0);
1410 }
1411
1412 template <typename DerivedAssemblerType>
Jr(Register arg0)1413 inline void Assembler<DerivedAssemblerType>::Jr(Register arg0) {
1414 Jalr(zero, arg0, 0);
1415 }
1416
1417 template <typename DerivedAssemblerType>
Jalr(Register arg0)1418 inline void Assembler<DerivedAssemblerType>::Jalr(Register arg0) {
1419 Jalr(x1, arg0, 0);
1420 }
1421
1422 template <typename DerivedAssemblerType>
Not(Register arg0,Register arg1)1423 inline void Assembler<DerivedAssemblerType>::Not(Register arg0, Register arg1) {
1424 Xori(arg0, arg1, -1);
1425 }
1426
1427 template <typename DerivedAssemblerType>
Neg(Register arg0,Register arg1)1428 inline void Assembler<DerivedAssemblerType>::Neg(Register arg0, Register arg1) {
1429 Sub(arg0, zero, arg1);
1430 }
1431
1432 } // namespace riscv
1433
1434 } // namespace berberis
1435
1436 #endif // BERBERIS_ASSEMBLER_COMMON_RISCV_H_
1437