Changeset 286592 in webkit
- Timestamp:
- Dec 7, 2021, 2:01:22 AM (5 years ago)
- Location:
- trunk/Source/JavaScriptCore
- Files:
-
- 3 edited
-
ChangeLog (modified) (1 diff)
-
assembler/MacroAssemblerRISCV64.h (modified) (3 diffs)
-
assembler/RISCV64Assembler.h (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
-
trunk/Source/JavaScriptCore/ChangeLog
r286580 r286592 1 2021-12-07 Zan Dobersek <zdobersek@igalia.com> 2 3 [RISCV64] Add more MacroAssemblerRISCV64 helper infrastructure 4 https://bugs.webkit.org/show_bug.cgi?id=233805 5 6 Reviewed by Yusuke Suzuki. 7 8 Introduce RISCV64Assembler::ImmediateLoader, a helper class that 9 generates the operations necessary for loading any immediate value into 10 some register. This can be as simple as using ADDI to load 12-bit values 11 or a combination of LUI, ADDI and possibly additional combinations of 12 LSHIFT and ADDI instructions. There's also a placeholder mode which 13 generates no-ops for unused operation slots, in order to enable 14 future patching and repatching for other immediate values. 15 16 MacroAssemblerRISCV64::Imm is introduced as a private helper struct that 17 groups together validity and construction operations for the different 18 immediate types implemented in the RISCV64Instructions namespace. 19 20 In MacroAssemblerRISCV64, resolveAddress() overloads are provided to 21 help generate most optimal address loading sequencing. RISC-V addressing 22 mode utilizes a base register and a 12-bit signed offset. When needed, 23 additional computation is done on the address object's parameters and 24 stored in the destination register through which the load can then be 25 performed. 26 27 Helper TempRegister and LazyTempRegister structs are added to the 28 MacroAssemblerRISCV64 class, along with the respective temps() and 29 lazyTemp() methods. temps() returns the TempRegister object, with the 30 template parameters defining which of the two scratch register types 31 should be allowed for use through this object. Release-time assert 32 on the m_allowScratchRegister value is done at the point of calling 33 temps(). lazyTemp() only handles one scratch register, and the assert 34 is done only when the register is actually used, and not just reserved 35 for use. This enables simpler implementations that better handle both 36 modes of scratch register usage (allowed or disallowed). 37 38 To get things rolling, the first set of MacroAssemblerRISCV64 methods 39 is implemented. Addition, subtraction and multiplication definitions 40 are provided, with the templated no-op declarations removed. 41 42 * assembler/MacroAssemblerRISCV64.h: 43 (JSC::MacroAssemblerRISCV64::TempRegister::data): 44 (JSC::MacroAssemblerRISCV64::TempRegister::memory): 45 (JSC::MacroAssemblerRISCV64::LazyTempRegister::LazyTempRegister): 46 (JSC::MacroAssemblerRISCV64::LazyTempRegister::operator RegisterID): 47 (JSC::MacroAssemblerRISCV64::temps): 48 (JSC::MacroAssemblerRISCV64::lazyTemp): 49 (JSC::MacroAssemblerRISCV64::add32): 50 (JSC::MacroAssemblerRISCV64::add64): 51 (JSC::MacroAssemblerRISCV64::sub32): 52 (JSC::MacroAssemblerRISCV64::sub64): 53 (JSC::MacroAssemblerRISCV64::mul32): 54 (JSC::MacroAssemblerRISCV64::mul64): 55 (JSC::MacroAssemblerRISCV64::Imm::isValid): 56 (JSC::MacroAssemblerRISCV64::Imm::I): 57 (JSC::MacroAssemblerRISCV64::Imm::S): 58 (JSC::MacroAssemblerRISCV64::Imm::B): 59 (JSC::MacroAssemblerRISCV64::Imm::U): 60 (JSC::MacroAssemblerRISCV64::Imm::J): 61 (JSC::MacroAssemblerRISCV64::resolveAddress): 62 * assembler/RISCV64Assembler.h: 63 (JSC::RISCV64Assembler::ImmediateLoader::ImmediateLoader): 64 (JSC::RISCV64Assembler::ImmediateLoader::moveInto): 65 1 66 2021-12-06 Keith Miller <keith_miller@apple.com> 2 67 -
trunk/Source/JavaScriptCore/assembler/MacroAssemblerRISCV64.h
r281757 r286592 55 55 } 56 56 57 enum TempRegisterType : int8_t { 58 Data, 59 Memory, 60 }; 61 62 template<TempRegisterType... RegisterTypes> 63 struct TempRegister { 64 RegisterID data() 65 { 66 static_assert(((RegisterTypes == Data) || ...)); 67 return dataTempRegister; 68 } 69 70 RegisterID memory() 71 { 72 static_assert(((RegisterTypes == Memory) || ...)); 73 return memoryTempRegister; 74 } 75 }; 76 77 template<TempRegisterType RegisterType> 78 struct LazyTempRegister { 79 LazyTempRegister(bool allowScratchRegister) 80 : m_allowScratchRegister(allowScratchRegister) 81 { 82 static_assert(RegisterType == Data || RegisterType == Memory); 83 } 84 85 operator RegisterID() 86 { 87 RELEASE_ASSERT(m_allowScratchRegister); 88 if constexpr (RegisterType == Data) 89 return dataTempRegister; 90 if constexpr (RegisterType == Memory) 91 return memoryTempRegister; 92 return InvalidGPRReg; 93 } 94 95 bool m_allowScratchRegister; 96 }; 97 98 template<TempRegisterType... RegisterTypes> 99 auto temps() -> TempRegister<RegisterTypes...> 100 { 101 RELEASE_ASSERT(m_allowScratchRegister); 102 return { }; 103 } 104 105 template<TempRegisterType RegisterType> 106 auto lazyTemp() -> LazyTempRegister<RegisterType> 107 { 108 return { m_allowScratchRegister }; 109 } 110 57 111 static bool supportsFloatingPoint() { return true; } 58 112 static bool supportsFloatingPointTruncate() { return true; } … … 106 160 static constexpr RegisterID linkRegister = RISCV64Registers::ra; 107 161 108 MACRO_ASSEMBLER_RISCV64_TEMPLATED_NOOP_METHOD(add32); 109 MACRO_ASSEMBLER_RISCV64_TEMPLATED_NOOP_METHOD(add64); 110 MACRO_ASSEMBLER_RISCV64_TEMPLATED_NOOP_METHOD(sub32); 111 MACRO_ASSEMBLER_RISCV64_TEMPLATED_NOOP_METHOD(sub64); 112 MACRO_ASSEMBLER_RISCV64_TEMPLATED_NOOP_METHOD(mul32); 113 MACRO_ASSEMBLER_RISCV64_TEMPLATED_NOOP_METHOD(mul64); 162 void add32(RegisterID src, RegisterID dest) 163 { 164 add32(src, dest, dest); 165 } 166 167 void add32(RegisterID op1, RegisterID op2, RegisterID dest) 168 { 169 m_assembler.addwInsn(dest, op1, op2); 170 m_assembler.maskRegister<32>(dest); 171 } 172 173 void add32(TrustedImm32 imm, RegisterID dest) 174 { 175 add32(imm, dest, dest); 176 } 177 178 void add32(TrustedImm32 imm, RegisterID op2, RegisterID dest) 179 { 180 if (Imm::isValid<Imm::IType>(imm.m_value)) { 181 m_assembler.addiwInsn(dest, op2, Imm::I(imm.m_value)); 182 m_assembler.maskRegister<32>(dest); 183 return; 184 } 185 186 auto temp = temps<Data>(); 187 move(imm, temp.data()); 188 m_assembler.addwInsn(dest, temp.data(), op2); 189 m_assembler.maskRegister<32>(dest); 190 } 191 192 void add32(TrustedImm32 imm, AbsoluteAddress address) 193 { 194 auto temp = temps<Data, Memory>(); 195 move(TrustedImmPtr(address.m_ptr), temp.memory()); 196 if (Imm::isValid<Imm::IType>(imm.m_value)) { 197 m_assembler.lwInsn(temp.data(), temp.memory(), Imm::I<0>()); 198 m_assembler.addiInsn(temp.data(), temp.data(), Imm::I(imm.m_value)); 199 m_assembler.swInsn(temp.memory(), temp.data(), Imm::S<0>()); 200 return; 201 } 202 203 m_assembler.lwInsn(temp.memory(), temp.memory(), Imm::I<0>()); 204 move(imm, temp.data()); 205 m_assembler.addInsn(temp.data(), temp.memory(), temp.data()); 206 207 move(TrustedImmPtr(address.m_ptr), temp.memory()); 208 m_assembler.swInsn(temp.memory(), temp.data(), Imm::S<0>()); 209 } 210 211 void add32(TrustedImm32 imm, Address address) 212 { 213 auto temp = temps<Data, Memory>(); 214 auto resolution = resolveAddress(address, temp.memory()); 215 if (Imm::isValid<Imm::IType>(imm.m_value)) { 216 m_assembler.lwInsn(temp.data(), resolution.base, Imm::I(resolution.offset)); 217 m_assembler.addiInsn(temp.data(), temp.data(), Imm::I(imm.m_value)); 218 m_assembler.swInsn(resolution.base, temp.data(), Imm::S(resolution.offset)); 219 return; 220 } 221 222 m_assembler.lwInsn(temp.memory(), resolution.base, Imm::I(resolution.offset)); 223 move(imm, temp.data()); 224 m_assembler.addInsn(temp.data(), temp.memory(), temp.data()); 225 226 resolution = resolveAddress(address, temp.memory()); 227 m_assembler.swInsn(resolution.base, temp.data(), Imm::S(resolution.offset)); 228 } 229 230 void add32(Address address, RegisterID dest) 231 { 232 auto temp = temps<Data, Memory>(); 233 auto resolution = resolveAddress(address, temp.memory()); 234 m_assembler.lwInsn(temp.data(), resolution.base, Imm::I(resolution.offset)); 235 m_assembler.addwInsn(dest, temp.data(), dest); 236 m_assembler.maskRegister<32>(dest); 237 } 238 239 void add64(RegisterID src, RegisterID dest) 240 { 241 add64(src, dest, dest); 242 } 243 244 void add64(RegisterID op1, RegisterID op2, RegisterID dest) 245 { 246 m_assembler.addInsn(dest, op1, op2); 247 } 248 249 void add64(TrustedImm32 imm, RegisterID dest) 250 { 251 add64(imm, dest, dest); 252 } 253 254 void add64(TrustedImm32 imm, RegisterID op2, RegisterID dest) 255 { 256 if (Imm::isValid<Imm::IType>(imm.m_value)) { 257 m_assembler.addiInsn(dest, op2, Imm::I(imm.m_value)); 258 return; 259 } 260 261 auto temp = temps<Data>(); 262 move(imm, temp.data()); 263 m_assembler.addInsn(dest, temp.data(), op2); 264 } 265 266 void add64(TrustedImm64 imm, RegisterID dest) 267 { 268 add64(imm, dest, dest); 269 } 270 271 void add64(TrustedImm64 imm, RegisterID op2, RegisterID dest) 272 { 273 if (Imm::isValid<Imm::IType>(imm.m_value)) { 274 m_assembler.addiInsn(dest, op2, Imm::I(imm.m_value)); 275 return; 276 } 277 278 auto temp = temps<Data>(); 279 move(imm, temp.data()); 280 m_assembler.addInsn(dest, temp.data(), op2); 281 } 282 283 void add64(TrustedImm32 imm, AbsoluteAddress address) 284 { 285 auto temp = temps<Data, Memory>(); 286 move(TrustedImmPtr(address.m_ptr), temp.memory()); 287 288 if (Imm::isValid<Imm::IType>(imm.m_value)) { 289 m_assembler.ldInsn(temp.data(), temp.memory(), Imm::I<0>()); 290 m_assembler.addiInsn(temp.data(), temp.data(), Imm::I(imm.m_value)); 291 m_assembler.sdInsn(temp.memory(), temp.data(), Imm::S<0>()); 292 return; 293 } 294 295 m_assembler.ldInsn(temp.memory(), temp.memory(), Imm::I<0>()); 296 move(imm, temp.data()); 297 m_assembler.addInsn(temp.data(), temp.data(), temp.memory()); 298 299 move(TrustedImmPtr(address.m_ptr), temp.memory()); 300 m_assembler.sdInsn(temp.memory(), temp.data(), Imm::S<0>()); 301 } 302 303 void add64(TrustedImm32 imm, Address address) 304 { 305 auto temp = temps<Data, Memory>(); 306 auto resolution = resolveAddress(address, temp.memory()); 307 m_assembler.ldInsn(temp.data(), resolution.base, Imm::I(resolution.offset)); 308 309 if (Imm::isValid<Imm::IType>(imm.m_value)) { 310 m_assembler.addiInsn(temp.data(), temp.data(), Imm::I(imm.m_value)); 311 m_assembler.sdInsn(resolution.base, temp.data(), Imm::S(resolution.offset)); 312 return; 313 } 314 315 move(imm, temp.memory()); 316 m_assembler.addInsn(temp.data(), temp.memory(), temp.data()); 317 318 resolution = resolveAddress(address, temp.memory()); 319 m_assembler.sdInsn(resolution.base, temp.data(), Imm::S(resolution.offset)); 320 } 321 322 void add64(AbsoluteAddress address, RegisterID dest) 323 { 324 auto temp = temps<Memory>(); 325 move(TrustedImmPtr(address.m_ptr), temp.memory()); 326 m_assembler.ldInsn(temp.memory(), temp.memory(), Imm::I<0>()); 327 m_assembler.addInsn(dest, temp.memory(), dest); 328 } 329 330 void add64(Address address, RegisterID dest) 331 { 332 auto temp = temps<Data, Memory>(); 333 auto resolution = resolveAddress(address, temp.memory()); 334 m_assembler.ldInsn(temp.data(), resolution.base, Imm::I(resolution.offset)); 335 m_assembler.addInsn(dest, temp.data(), dest); 336 } 337 338 void sub32(RegisterID src, RegisterID dest) 339 { 340 sub32(dest, src, dest); 341 } 342 343 void sub32(RegisterID op1, RegisterID op2, RegisterID dest) 344 { 345 m_assembler.subwInsn(dest, op1, op2); 346 m_assembler.maskRegister<32>(dest); 347 } 348 349 void sub32(TrustedImm32 imm, RegisterID dest) 350 { 351 sub32(dest, imm, dest); 352 } 353 354 void sub32(RegisterID op1, TrustedImm32 imm, RegisterID dest) 355 { 356 add32(TrustedImm32(-imm.m_value), op1, dest); 357 } 358 359 void sub32(TrustedImm32 imm, AbsoluteAddress address) 360 { 361 auto temp = temps<Data, Memory>(); 362 move(TrustedImmPtr(address.m_ptr), temp.memory()); 363 364 if (Imm::isValid<Imm::IType>(-imm.m_value)) { 365 m_assembler.lwInsn(temp.data(), temp.memory(), Imm::I<0>()); 366 m_assembler.addiwInsn(temp.data(), temp.data(), Imm::I(-imm.m_value)); 367 m_assembler.swInsn(temp.memory(), temp.data(), Imm::S<0>()); 368 return; 369 } 370 371 m_assembler.lwInsn(temp.memory(), temp.memory(), Imm::I<0>()); 372 move(imm, temp.data()); 373 m_assembler.subwInsn(temp.data(), temp.memory(), temp.data()); 374 375 move(TrustedImmPtr(address.m_ptr), temp.memory()); 376 m_assembler.swInsn(temp.memory(), temp.data(), Imm::S<0>()); 377 } 378 379 void sub32(TrustedImm32 imm, Address address) 380 { 381 auto temp = temps<Data, Memory>(); 382 auto resolution = resolveAddress(address, temp.memory()); 383 m_assembler.lwInsn(temp.data(), resolution.base, Imm::I(resolution.offset)); 384 385 if (Imm::isValid<Imm::IType>(-imm.m_value)) { 386 m_assembler.addiwInsn(temp.data(), temp.data(), Imm::I(-imm.m_value)); 387 m_assembler.swInsn(resolution.base, temp.data(), Imm::S(resolution.offset)); 388 return; 389 } 390 391 move(imm, temp.memory()); 392 m_assembler.subwInsn(temp.data(), temp.data(), temp.memory()); 393 394 resolution = resolveAddress(address, temp.memory()); 395 m_assembler.swInsn(resolution.base, temp.data(), Imm::S(resolution.offset)); 396 } 397 398 void sub32(Address address, RegisterID dest) 399 { 400 auto temp = temps<Data, Memory>(); 401 auto resolution = resolveAddress(address, temp.memory()); 402 m_assembler.lwInsn(temp.data(), resolution.base, Imm::I(resolution.offset)); 403 m_assembler.subwInsn(dest, dest, temp.data()); 404 m_assembler.maskRegister<32>(dest); 405 } 406 407 void sub64(RegisterID src, RegisterID dest) 408 { 409 sub64(dest, src, dest); 410 } 411 412 void sub64(RegisterID op1, RegisterID op2, RegisterID dest) 413 { 414 m_assembler.subInsn(dest, op1, op2); 415 } 416 417 void sub64(TrustedImm32 imm, RegisterID dest) 418 { 419 sub64(dest, imm, dest); 420 } 421 422 void sub64(RegisterID op1, TrustedImm32 imm, RegisterID dest) 423 { 424 add64(TrustedImm32(-imm.m_value), op1, dest); 425 } 426 427 void sub64(TrustedImm64 imm, RegisterID dest) 428 { 429 sub64(dest, imm, dest); 430 } 431 432 void sub64(RegisterID op1, TrustedImm64 imm, RegisterID dest) 433 { 434 add64(TrustedImm64(-imm.m_value), op1, dest); 435 } 436 437 void mul32(RegisterID src, RegisterID dest) 438 { 439 mul32(src, dest, dest); 440 } 441 442 void mul32(RegisterID lhs, RegisterID rhs, RegisterID dest) 443 { 444 m_assembler.mulwInsn(dest, lhs, rhs); 445 m_assembler.maskRegister<32>(dest); 446 } 447 448 void mul32(TrustedImm32 imm, RegisterID rhs, RegisterID dest) 449 { 450 auto temp = temps<Data>(); 451 move(imm, temp.data()); 452 m_assembler.mulwInsn(dest, temp.data(), rhs); 453 m_assembler.maskRegister<32>(dest); 454 } 455 456 void mul64(RegisterID src, RegisterID dest) 457 { 458 mul64(src, dest, dest); 459 } 460 461 void mul64(RegisterID lhs, RegisterID rhs, RegisterID dest) 462 { 463 m_assembler.mulInsn(dest, lhs, rhs); 464 } 465 114 466 MACRO_ASSEMBLER_RISCV64_TEMPLATED_NOOP_METHOD(and32); 115 467 MACRO_ASSEMBLER_RISCV64_TEMPLATED_NOOP_METHOD(and64); … … 389 741 template<PtrTag tag> 390 742 static void linkCall(void*, Call, FunctionPtr<tag>) { } 743 744 private: 745 struct Imm { 746 template<typename T> 747 using EnableIfInteger = std::enable_if_t<(std::is_same_v<T, int32_t> || std::is_same_v<T, int64_t>)>; 748 749 template<typename ImmediateType, typename T, typename = EnableIfInteger<T>> 750 static bool isValid(T value) { return ImmediateType::isValid(value); } 751 752 using IType = RISCV64Assembler::IImmediate; 753 template<int32_t value> 754 static IType I() { return IType::v<IType, value>(); } 755 template<typename T, typename = EnableIfInteger<T>> 756 static IType I(T value) { return IType::v<IType>(value); } 757 static IType I(uint32_t value) { return IType(value); } 758 759 using SType = RISCV64Assembler::SImmediate; 760 template<int32_t value> 761 static SType S() { return SType::v<SType, value>(); } 762 template<typename T, typename = EnableIfInteger<T>> 763 static SType S(T value) { return SType::v<SType>(value); } 764 765 using BType = RISCV64Assembler::BImmediate; 766 template<int32_t value> 767 static BType B() { return BType::v<BType, value>(); } 768 template<typename T, typename = EnableIfInteger<T>> 769 static BType B(T value) { return BType::v<BType>(value); } 770 static BType B(uint32_t value) { return BType(value); } 771 772 using UType = RISCV64Assembler::UImmediate; 773 static UType U(uint32_t value) { return UType(value); } 774 775 using JType = RISCV64Assembler::JImmediate; 776 template<int32_t value> 777 static JType J() { return JType::v<JType, value>(); } 778 }; 779 780 struct AddressResolution { 781 RegisterID base; 782 int32_t offset; 783 }; 784 785 template<typename RegisterType> 786 AddressResolution resolveAddress(BaseIndex address, RegisterType destination) 787 { 788 if (!!address.offset) { 789 if (RISCV64Assembler::ImmediateBase<12>::isValid(address.offset)) { 790 if (address.scale != TimesOne) { 791 m_assembler.slliInsn(destination, address.index, uint32_t(address.scale)); 792 m_assembler.addInsn(destination, address.base, destination); 793 } else 794 m_assembler.addInsn(destination, address.base, address.index); 795 return { destination, address.offset }; 796 } 797 798 if (address.scale != TimesOne) { 799 uint32_t scale = address.scale; 800 int32_t upperOffset = address.offset >> scale; 801 int32_t lowerOffset = address.offset & ((1 << scale) - 1); 802 803 if (!RISCV64Assembler::ImmediateBase<12>::isValid(upperOffset)) { 804 RISCV64Assembler::ImmediateLoader imml(upperOffset); 805 imml.moveInto(m_assembler, destination); 806 m_assembler.addInsn(destination, address.index, destination); 807 } else 808 m_assembler.addiInsn(destination, address.index, Imm::I(upperOffset)); 809 m_assembler.slliInsn(destination, destination, scale); 810 m_assembler.oriInsn(destination, destination, Imm::I(lowerOffset)); 811 } else { 812 RISCV64Assembler::ImmediateLoader imml(address.offset); 813 imml.moveInto(m_assembler, destination); 814 m_assembler.addInsn(destination, destination, address.index); 815 } 816 m_assembler.addInsn(destination, address.base, destination); 817 return { destination, 0 }; 818 } 819 820 if (address.scale != TimesOne) { 821 m_assembler.slliInsn(destination, address.index, address.scale); 822 m_assembler.addInsn(destination, address.base, destination); 823 } else 824 m_assembler.addInsn(destination, address.base, address.index); 825 return { destination, 0 }; 826 } 827 828 template<typename RegisterType> 829 AddressResolution resolveAddress(Address address, RegisterType destination) 830 { 831 if (RISCV64Assembler::ImmediateBase<12>::isValid(address.offset)) 832 return { address.base, address.offset }; 833 834 uint32_t value = *reinterpret_cast<uint32_t*>(&address.offset); 835 if (value & (1 << 11)) 836 value += (1 << 12); 837 838 m_assembler.luiInsn(destination, Imm::U(value)); 839 m_assembler.addiInsn(destination, destination, Imm::I(value & ((1 << 12) - 1))); 840 m_assembler.addInsn(destination, address.base, destination); 841 return { destination, 0 }; 842 } 843 844 template<typename RegisterType> 845 AddressResolution resolveAddress(ExtendedAddress address, RegisterType destination) 846 { 847 if (RISCV64Assembler::ImmediateBase<12>::isValid(address.offset)) 848 return { address.base, int32_t(address.offset) }; 849 850 RISCV64Assembler::ImmediateLoader imml(int64_t(address.offset)); 851 imml.moveInto(m_assembler, destination); 852 m_assembler.addInsn(destination, address.base, destination); 853 return { destination, 0 }; 854 } 391 855 }; 392 856 -
trunk/Source/JavaScriptCore/assembler/RISCV64Assembler.h
r286212 r286592 1988 1988 } 1989 1989 1990 struct ImmediateLoader { 1991 enum PlaceholderTag { Placeholder }; 1992 1993 ImmediateLoader(int32_t imm) 1994 : ImmediateLoader(int64_t(imm)) 1995 { } 1996 1997 ImmediateLoader(PlaceholderTag, int32_t imm) 1998 : ImmediateLoader(Placeholder, int64_t(imm)) 1999 { } 2000 2001 ImmediateLoader(int64_t imm) 2002 { 2003 // If the immediate value fits into the IImmediate mold, we can short-cut to just generating that through a single ADDI. 2004 if (IImmediate::isValid(imm)) { 2005 m_ops[m_opCount++] = { Op::Type::IImmediate, IImmediate::v<IImmediate>(imm).imm }; 2006 return; 2007 } 2008 2009 // The immediate is larger than 12 bits, so it has to be loaded through the initial LUI and then additional shift-and-addi pairs. 2010 // This sequence is generated in reverse. moveInto() or other users traverse the sequence accordingly. 2011 int64_t value = imm; 2012 2013 while (true) { 2014 uint32_t addiImm = value & ((1 << 12) - 1); 2015 // The addi will be sign-extending the 12-bit value and adding it to the register-contained value. If the addi-immediate 2016 // is negative, the remaining immediate has to be increased by 2^12 to offset the subsequent subtraction. 2017 if (addiImm & (1 << 11)) 2018 value += (1 << 12); 2019 m_ops[m_opCount++] = { Op::Type::ADDI, addiImm }; 2020 2021 // Shift out the bits incorporated into the just-added addi. 2022 value = value >> 12; 2023 2024 // If the remainder of the immediate can fit into a 20-bit immediate, we can generate the LUI instruction that will end up 2025 // loading the initial higher bits of the desired immediate. 2026 if (ImmediateBase<20>::isValid(value)) { 2027 m_ops[m_opCount++] = { Op::Type::LUI, uint32_t((value & ((1 << 20) - 1)) << 12) }; 2028 return; 2029 } 2030 2031 // Otherwise, generate the lshift operation that will make room for lower parts of the immediate value. 2032 m_ops[m_opCount++] = { Op::Type::LSHIFT12, 0 }; 2033 } 2034 } 2035 2036 ImmediateLoader(PlaceholderTag, int64_t imm) 2037 : ImmediateLoader(imm) 2038 { 2039 // The non-placeholder constructor already generated the necessary operations to load this immediate. 2040 // This constructor still fills out the remaining potential operations as nops. This enables future patching 2041 // of these instructions with other immediate-load sequences. 2042 2043 for (unsigned i = m_opCount; i < m_ops.size(); ++i) 2044 m_ops[i] = { Op::Type::NOP, 0 }; 2045 m_opCount = m_ops.size(); 2046 } 2047 2048 void moveInto(RISCV64Assembler& assembler, RegisterID dest) 2049 { 2050 // This is a helper method that generates the necessary instructions through the RISCV64Assembler infrastructure. 2051 // Operations are traversed in reverse in order to match the generation process. 2052 2053 for (unsigned i = 0; i < m_opCount; ++i) { 2054 auto& op = m_ops[m_opCount - (i + 1)]; 2055 switch (op.type) { 2056 case Op::Type::IImmediate: 2057 assembler.addiInsn(dest, RISCV64Registers::zero, IImmediate(op.value)); 2058 break; 2059 case Op::Type::LUI: 2060 assembler.luiInsn(dest, UImmediate(op.value)); 2061 break; 2062 case Op::Type::ADDI: 2063 assembler.addiInsn(dest, dest, IImmediate(op.value)); 2064 break; 2065 case Op::Type::LSHIFT12: 2066 assembler.slliInsn<12>(dest, dest); 2067 break; 2068 case Op::Type::NOP: 2069 assembler.addiInsn(RISCV64Registers::zero, RISCV64Registers::zero, IImmediate::v<IImmediate, 0>()); 2070 break; 2071 } 2072 } 2073 } 2074 2075 struct Op { 2076 enum class Type { 2077 IImmediate, 2078 LUI, 2079 ADDI, 2080 LSHIFT12, 2081 NOP, 2082 }; 2083 2084 Type type; 2085 uint32_t value; 2086 }; 2087 std::array<Op, 8> m_ops; 2088 unsigned m_opCount { 0 }; 2089 }; 2090 1990 2091 protected: 1991 2092 void insn(uint32_t instruction)
Note:
See TracChangeset
for help on using the changeset viewer.