Add a new JIT compiler for CPU code (#693)
* Start of the ARMeilleure project * Refactoring around the old IRAdapter, now renamed to PreAllocator * Optimize the LowestBitSet method * Add CLZ support and fix CLS implementation * Add missing Equals and GetHashCode overrides on some structs, misc small tweaks * Implement the ByteSwap IR instruction, and some refactoring on the assembler * Implement the DivideUI IR instruction and fix 64-bits IDIV * Correct constant operand type on CSINC * Move division instructions implementation to InstEmitDiv * Fix destination type for the ConditionalSelect IR instruction * Implement UMULH and SMULH, with new IR instructions * Fix some issues with shift instructions * Fix constant types for BFM instructions * Fix up new tests using the new V128 struct * Update tests * Move DIV tests to a separate file * Add support for calls, and some instructions that depends on them * Start adding support for SIMD & FP types, along with some of the related ARM instructions * Fix some typos and the divide instruction with FP operands * Fix wrong method call on Clz_V * Implement ARM FP & SIMD move instructions, Saddlv_V, and misc. fixes * Implement SIMD logical instructions and more misc. fixes * Fix PSRAD x86 instruction encoding, TRN, UABD and UABDL implementations * Implement float conversion instruction, merge in LDj3SNuD fixes, and some other misc. fixes * Implement SIMD shift instruction and fix Dup_V * Add SCVTF and UCVTF (vector, fixed-point) variants to the opcode table * Fix check with tolerance on tester * Implement FP & SIMD comparison instructions, and some fixes * Update FCVT (Scalar) encoding on the table to support the Half-float variants * Support passing V128 structs, some cleanup on the register allocator, merge LDj3SNuD fixes * Use old memory access methods, made a start on SIMD memory insts support, some fixes * Fix float constant passed to functions, save and restore non-volatile XMM registers, other fixes * Fix arguments count with struct return values, other fixes * More instructions * Misc. fixes and integrate LDj3SNuD fixes * Update tests * Add a faster linear scan allocator, unwinding support on windows, and other changes * Update Ryujinx.HLE * Update Ryujinx.Graphics * Fix V128 return pointer passing, RCX is clobbered * Update Ryujinx.Tests * Update ITimeZoneService * Stop using GetFunctionPointer as that can't be called from native code, misc. fixes and tweaks * Use generic GetFunctionPointerForDelegate method and other tweaks * Some refactoring on the code generator, assert on invalid operations and use a separate enum for intrinsics * Remove some unused code on the assembler * Fix REX.W prefix regression on float conversion instructions, add some sort of profiler * Add hardware capability detection * Fix regression on Sha1h and revert Fcm** changes * Add SSE2-only paths on vector extract and insert, some refactoring on the pre-allocator * Fix silly mistake introduced on last commit on CpuId * Generate inline stack probes when the stack allocation is too large * Initial support for the System-V ABI * Support multiple destination operands * Fix SSE2 VectorInsert8 path, and other fixes * Change placement of XMM callee save and restore code to match other compilers * Rename Dest to Destination and Inst to Instruction * Fix a regression related to calls and the V128 type * Add an extra space on comments to match code style * Some refactoring * Fix vector insert FP32 SSE2 path * Port over the ARM32 instructions * Avoid memory protection races on JIT Cache * Another fix on VectorInsert FP32 (thanks to LDj3SNuD * Float operands don't need to use the same register when VEX is supported * Add a new register allocator, higher quality code for hot code (tier up), and other tweaks * Some nits, small improvements on the pre allocator * CpuThreadState is gone * Allow changing CPU emulators with a config entry * Add runtime identifiers on the ARMeilleure project * Allow switching between CPUs through a config entry (pt. 2) * Change win10-x64 to win-x64 on projects * Update the Ryujinx project to use ARMeilleure * Ensure that the selected register is valid on the hybrid allocator * Allow exiting on returns to 0 (should fix test regression) * Remove register assignments for most used variables on the hybrid allocator * Do not use fixed registers as spill temp * Add missing namespace and remove unneeded using * Address PR feedback * Fix types, etc * Enable AssumeStrictAbiCompliance by default * Ensure that Spill and Fill don't load or store any more than necessary
This commit is contained in:
parent
1ba58e9942
commit
a731ab3a2a
310 changed files with 37389 additions and 2086 deletions
279
ARMeilleure/Instructions/CryptoHelper.cs
Normal file
279
ARMeilleure/Instructions/CryptoHelper.cs
Normal file
|
@ -0,0 +1,279 @@
|
|||
// https://www.intel.com/content/dam/doc/white-paper/advanced-encryption-standard-new-instructions-set-paper.pdf
|
||||
|
||||
using ARMeilleure.State;
|
||||
|
||||
namespace ARMeilleure.Instructions
|
||||
{
|
||||
static class CryptoHelper
|
||||
{
|
||||
#region "LookUp Tables"
|
||||
private static readonly byte[] _sBox = new byte[]
|
||||
{
|
||||
0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
|
||||
0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
|
||||
0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
|
||||
0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
|
||||
0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
|
||||
0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
|
||||
0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
|
||||
0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
|
||||
0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
|
||||
0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
|
||||
0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
|
||||
0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
|
||||
0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
|
||||
0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
|
||||
0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
|
||||
0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16
|
||||
};
|
||||
|
||||
private static readonly byte[] _invSBox = new byte[]
|
||||
{
|
||||
0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb,
|
||||
0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb,
|
||||
0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e,
|
||||
0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25,
|
||||
0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92,
|
||||
0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84,
|
||||
0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06,
|
||||
0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b,
|
||||
0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73,
|
||||
0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e,
|
||||
0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b,
|
||||
0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4,
|
||||
0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f,
|
||||
0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef,
|
||||
0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61,
|
||||
0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d
|
||||
};
|
||||
|
||||
private static readonly byte[] _gfMul02 = new byte[]
|
||||
{
|
||||
0x00, 0x02, 0x04, 0x06, 0x08, 0x0a, 0x0c, 0x0e, 0x10, 0x12, 0x14, 0x16, 0x18, 0x1a, 0x1c, 0x1e,
|
||||
0x20, 0x22, 0x24, 0x26, 0x28, 0x2a, 0x2c, 0x2e, 0x30, 0x32, 0x34, 0x36, 0x38, 0x3a, 0x3c, 0x3e,
|
||||
0x40, 0x42, 0x44, 0x46, 0x48, 0x4a, 0x4c, 0x4e, 0x50, 0x52, 0x54, 0x56, 0x58, 0x5a, 0x5c, 0x5e,
|
||||
0x60, 0x62, 0x64, 0x66, 0x68, 0x6a, 0x6c, 0x6e, 0x70, 0x72, 0x74, 0x76, 0x78, 0x7a, 0x7c, 0x7e,
|
||||
0x80, 0x82, 0x84, 0x86, 0x88, 0x8a, 0x8c, 0x8e, 0x90, 0x92, 0x94, 0x96, 0x98, 0x9a, 0x9c, 0x9e,
|
||||
0xa0, 0xa2, 0xa4, 0xa6, 0xa8, 0xaa, 0xac, 0xae, 0xb0, 0xb2, 0xb4, 0xb6, 0xb8, 0xba, 0xbc, 0xbe,
|
||||
0xc0, 0xc2, 0xc4, 0xc6, 0xc8, 0xca, 0xcc, 0xce, 0xd0, 0xd2, 0xd4, 0xd6, 0xd8, 0xda, 0xdc, 0xde,
|
||||
0xe0, 0xe2, 0xe4, 0xe6, 0xe8, 0xea, 0xec, 0xee, 0xf0, 0xf2, 0xf4, 0xf6, 0xf8, 0xfa, 0xfc, 0xfe,
|
||||
0x1b, 0x19, 0x1f, 0x1d, 0x13, 0x11, 0x17, 0x15, 0x0b, 0x09, 0x0f, 0x0d, 0x03, 0x01, 0x07, 0x05,
|
||||
0x3b, 0x39, 0x3f, 0x3d, 0x33, 0x31, 0x37, 0x35, 0x2b, 0x29, 0x2f, 0x2d, 0x23, 0x21, 0x27, 0x25,
|
||||
0x5b, 0x59, 0x5f, 0x5d, 0x53, 0x51, 0x57, 0x55, 0x4b, 0x49, 0x4f, 0x4d, 0x43, 0x41, 0x47, 0x45,
|
||||
0x7b, 0x79, 0x7f, 0x7d, 0x73, 0x71, 0x77, 0x75, 0x6b, 0x69, 0x6f, 0x6d, 0x63, 0x61, 0x67, 0x65,
|
||||
0x9b, 0x99, 0x9f, 0x9d, 0x93, 0x91, 0x97, 0x95, 0x8b, 0x89, 0x8f, 0x8d, 0x83, 0x81, 0x87, 0x85,
|
||||
0xbb, 0xb9, 0xbf, 0xbd, 0xb3, 0xb1, 0xb7, 0xb5, 0xab, 0xa9, 0xaf, 0xad, 0xa3, 0xa1, 0xa7, 0xa5,
|
||||
0xdb, 0xd9, 0xdf, 0xdd, 0xd3, 0xd1, 0xd7, 0xd5, 0xcb, 0xc9, 0xcf, 0xcd, 0xc3, 0xc1, 0xc7, 0xc5,
|
||||
0xfb, 0xf9, 0xff, 0xfd, 0xf3, 0xf1, 0xf7, 0xf5, 0xeb, 0xe9, 0xef, 0xed, 0xe3, 0xe1, 0xe7, 0xe5
|
||||
};
|
||||
|
||||
private static readonly byte[] _gfMul03 = new byte[]
|
||||
{
|
||||
0x00, 0x03, 0x06, 0x05, 0x0c, 0x0f, 0x0a, 0x09, 0x18, 0x1b, 0x1e, 0x1d, 0x14, 0x17, 0x12, 0x11,
|
||||
0x30, 0x33, 0x36, 0x35, 0x3c, 0x3f, 0x3a, 0x39, 0x28, 0x2b, 0x2e, 0x2d, 0x24, 0x27, 0x22, 0x21,
|
||||
0x60, 0x63, 0x66, 0x65, 0x6c, 0x6f, 0x6a, 0x69, 0x78, 0x7b, 0x7e, 0x7d, 0x74, 0x77, 0x72, 0x71,
|
||||
0x50, 0x53, 0x56, 0x55, 0x5c, 0x5f, 0x5a, 0x59, 0x48, 0x4b, 0x4e, 0x4d, 0x44, 0x47, 0x42, 0x41,
|
||||
0xc0, 0xc3, 0xc6, 0xc5, 0xcc, 0xcf, 0xca, 0xc9, 0xd8, 0xdb, 0xde, 0xdd, 0xd4, 0xd7, 0xd2, 0xd1,
|
||||
0xf0, 0xf3, 0xf6, 0xf5, 0xfc, 0xff, 0xfa, 0xf9, 0xe8, 0xeb, 0xee, 0xed, 0xe4, 0xe7, 0xe2, 0xe1,
|
||||
0xa0, 0xa3, 0xa6, 0xa5, 0xac, 0xaf, 0xaa, 0xa9, 0xb8, 0xbb, 0xbe, 0xbd, 0xb4, 0xb7, 0xb2, 0xb1,
|
||||
0x90, 0x93, 0x96, 0x95, 0x9c, 0x9f, 0x9a, 0x99, 0x88, 0x8b, 0x8e, 0x8d, 0x84, 0x87, 0x82, 0x81,
|
||||
0x9b, 0x98, 0x9d, 0x9e, 0x97, 0x94, 0x91, 0x92, 0x83, 0x80, 0x85, 0x86, 0x8f, 0x8c, 0x89, 0x8a,
|
||||
0xab, 0xa8, 0xad, 0xae, 0xa7, 0xa4, 0xa1, 0xa2, 0xb3, 0xb0, 0xb5, 0xb6, 0xbf, 0xbc, 0xb9, 0xba,
|
||||
0xfb, 0xf8, 0xfd, 0xfe, 0xf7, 0xf4, 0xf1, 0xf2, 0xe3, 0xe0, 0xe5, 0xe6, 0xef, 0xec, 0xe9, 0xea,
|
||||
0xcb, 0xc8, 0xcd, 0xce, 0xc7, 0xc4, 0xc1, 0xc2, 0xd3, 0xd0, 0xd5, 0xd6, 0xdf, 0xdc, 0xd9, 0xda,
|
||||
0x5b, 0x58, 0x5d, 0x5e, 0x57, 0x54, 0x51, 0x52, 0x43, 0x40, 0x45, 0x46, 0x4f, 0x4c, 0x49, 0x4a,
|
||||
0x6b, 0x68, 0x6d, 0x6e, 0x67, 0x64, 0x61, 0x62, 0x73, 0x70, 0x75, 0x76, 0x7f, 0x7c, 0x79, 0x7a,
|
||||
0x3b, 0x38, 0x3d, 0x3e, 0x37, 0x34, 0x31, 0x32, 0x23, 0x20, 0x25, 0x26, 0x2f, 0x2c, 0x29, 0x2a,
|
||||
0x0b, 0x08, 0x0d, 0x0e, 0x07, 0x04, 0x01, 0x02, 0x13, 0x10, 0x15, 0x16, 0x1f, 0x1c, 0x19, 0x1a
|
||||
};
|
||||
|
||||
private static readonly byte[] _gfMul09 = new byte[]
|
||||
{
|
||||
0x00, 0x09, 0x12, 0x1b, 0x24, 0x2d, 0x36, 0x3f, 0x48, 0x41, 0x5a, 0x53, 0x6c, 0x65, 0x7e, 0x77,
|
||||
0x90, 0x99, 0x82, 0x8b, 0xb4, 0xbd, 0xa6, 0xaf, 0xd8, 0xd1, 0xca, 0xc3, 0xfc, 0xf5, 0xee, 0xe7,
|
||||
0x3b, 0x32, 0x29, 0x20, 0x1f, 0x16, 0x0d, 0x04, 0x73, 0x7a, 0x61, 0x68, 0x57, 0x5e, 0x45, 0x4c,
|
||||
0xab, 0xa2, 0xb9, 0xb0, 0x8f, 0x86, 0x9d, 0x94, 0xe3, 0xea, 0xf1, 0xf8, 0xc7, 0xce, 0xd5, 0xdc,
|
||||
0x76, 0x7f, 0x64, 0x6d, 0x52, 0x5b, 0x40, 0x49, 0x3e, 0x37, 0x2c, 0x25, 0x1a, 0x13, 0x08, 0x01,
|
||||
0xe6, 0xef, 0xf4, 0xfd, 0xc2, 0xcb, 0xd0, 0xd9, 0xae, 0xa7, 0xbc, 0xb5, 0x8a, 0x83, 0x98, 0x91,
|
||||
0x4d, 0x44, 0x5f, 0x56, 0x69, 0x60, 0x7b, 0x72, 0x05, 0x0c, 0x17, 0x1e, 0x21, 0x28, 0x33, 0x3a,
|
||||
0xdd, 0xd4, 0xcf, 0xc6, 0xf9, 0xf0, 0xeb, 0xe2, 0x95, 0x9c, 0x87, 0x8e, 0xb1, 0xb8, 0xa3, 0xaa,
|
||||
0xec, 0xe5, 0xfe, 0xf7, 0xc8, 0xc1, 0xda, 0xd3, 0xa4, 0xad, 0xb6, 0xbf, 0x80, 0x89, 0x92, 0x9b,
|
||||
0x7c, 0x75, 0x6e, 0x67, 0x58, 0x51, 0x4a, 0x43, 0x34, 0x3d, 0x26, 0x2f, 0x10, 0x19, 0x02, 0x0b,
|
||||
0xd7, 0xde, 0xc5, 0xcc, 0xf3, 0xfa, 0xe1, 0xe8, 0x9f, 0x96, 0x8d, 0x84, 0xbb, 0xb2, 0xa9, 0xa0,
|
||||
0x47, 0x4e, 0x55, 0x5c, 0x63, 0x6a, 0x71, 0x78, 0x0f, 0x06, 0x1d, 0x14, 0x2b, 0x22, 0x39, 0x30,
|
||||
0x9a, 0x93, 0x88, 0x81, 0xbe, 0xb7, 0xac, 0xa5, 0xd2, 0xdb, 0xc0, 0xc9, 0xf6, 0xff, 0xe4, 0xed,
|
||||
0x0a, 0x03, 0x18, 0x11, 0x2e, 0x27, 0x3c, 0x35, 0x42, 0x4b, 0x50, 0x59, 0x66, 0x6f, 0x74, 0x7d,
|
||||
0xa1, 0xa8, 0xb3, 0xba, 0x85, 0x8c, 0x97, 0x9e, 0xe9, 0xe0, 0xfb, 0xf2, 0xcd, 0xc4, 0xdf, 0xd6,
|
||||
0x31, 0x38, 0x23, 0x2a, 0x15, 0x1c, 0x07, 0x0e, 0x79, 0x70, 0x6b, 0x62, 0x5d, 0x54, 0x4f, 0x46
|
||||
};
|
||||
|
||||
private static readonly byte[] _gfMul0B = new byte[]
|
||||
{
|
||||
0x00, 0x0b, 0x16, 0x1d, 0x2c, 0x27, 0x3a, 0x31, 0x58, 0x53, 0x4e, 0x45, 0x74, 0x7f, 0x62, 0x69,
|
||||
0xb0, 0xbb, 0xa6, 0xad, 0x9c, 0x97, 0x8a, 0x81, 0xe8, 0xe3, 0xfe, 0xf5, 0xc4, 0xcf, 0xd2, 0xd9,
|
||||
0x7b, 0x70, 0x6d, 0x66, 0x57, 0x5c, 0x41, 0x4a, 0x23, 0x28, 0x35, 0x3e, 0x0f, 0x04, 0x19, 0x12,
|
||||
0xcb, 0xc0, 0xdd, 0xd6, 0xe7, 0xec, 0xf1, 0xfa, 0x93, 0x98, 0x85, 0x8e, 0xbf, 0xb4, 0xa9, 0xa2,
|
||||
0xf6, 0xfd, 0xe0, 0xeb, 0xda, 0xd1, 0xcc, 0xc7, 0xae, 0xa5, 0xb8, 0xb3, 0x82, 0x89, 0x94, 0x9f,
|
||||
0x46, 0x4d, 0x50, 0x5b, 0x6a, 0x61, 0x7c, 0x77, 0x1e, 0x15, 0x08, 0x03, 0x32, 0x39, 0x24, 0x2f,
|
||||
0x8d, 0x86, 0x9b, 0x90, 0xa1, 0xaa, 0xb7, 0xbc, 0xd5, 0xde, 0xc3, 0xc8, 0xf9, 0xf2, 0xef, 0xe4,
|
||||
0x3d, 0x36, 0x2b, 0x20, 0x11, 0x1a, 0x07, 0x0c, 0x65, 0x6e, 0x73, 0x78, 0x49, 0x42, 0x5f, 0x54,
|
||||
0xf7, 0xfc, 0xe1, 0xea, 0xdb, 0xd0, 0xcd, 0xc6, 0xaf, 0xa4, 0xb9, 0xb2, 0x83, 0x88, 0x95, 0x9e,
|
||||
0x47, 0x4c, 0x51, 0x5a, 0x6b, 0x60, 0x7d, 0x76, 0x1f, 0x14, 0x09, 0x02, 0x33, 0x38, 0x25, 0x2e,
|
||||
0x8c, 0x87, 0x9a, 0x91, 0xa0, 0xab, 0xb6, 0xbd, 0xd4, 0xdf, 0xc2, 0xc9, 0xf8, 0xf3, 0xee, 0xe5,
|
||||
0x3c, 0x37, 0x2a, 0x21, 0x10, 0x1b, 0x06, 0x0d, 0x64, 0x6f, 0x72, 0x79, 0x48, 0x43, 0x5e, 0x55,
|
||||
0x01, 0x0a, 0x17, 0x1c, 0x2d, 0x26, 0x3b, 0x30, 0x59, 0x52, 0x4f, 0x44, 0x75, 0x7e, 0x63, 0x68,
|
||||
0xb1, 0xba, 0xa7, 0xac, 0x9d, 0x96, 0x8b, 0x80, 0xe9, 0xe2, 0xff, 0xf4, 0xc5, 0xce, 0xd3, 0xd8,
|
||||
0x7a, 0x71, 0x6c, 0x67, 0x56, 0x5d, 0x40, 0x4b, 0x22, 0x29, 0x34, 0x3f, 0x0e, 0x05, 0x18, 0x13,
|
||||
0xca, 0xc1, 0xdc, 0xd7, 0xe6, 0xed, 0xf0, 0xfb, 0x92, 0x99, 0x84, 0x8f, 0xbe, 0xb5, 0xa8, 0xa3
|
||||
};
|
||||
|
||||
private static readonly byte[] _gfMul0D = new byte[]
|
||||
{
|
||||
0x00, 0x0d, 0x1a, 0x17, 0x34, 0x39, 0x2e, 0x23, 0x68, 0x65, 0x72, 0x7f, 0x5c, 0x51, 0x46, 0x4b,
|
||||
0xd0, 0xdd, 0xca, 0xc7, 0xe4, 0xe9, 0xfe, 0xf3, 0xb8, 0xb5, 0xa2, 0xaf, 0x8c, 0x81, 0x96, 0x9b,
|
||||
0xbb, 0xb6, 0xa1, 0xac, 0x8f, 0x82, 0x95, 0x98, 0xd3, 0xde, 0xc9, 0xc4, 0xe7, 0xea, 0xfd, 0xf0,
|
||||
0x6b, 0x66, 0x71, 0x7c, 0x5f, 0x52, 0x45, 0x48, 0x03, 0x0e, 0x19, 0x14, 0x37, 0x3a, 0x2d, 0x20,
|
||||
0x6d, 0x60, 0x77, 0x7a, 0x59, 0x54, 0x43, 0x4e, 0x05, 0x08, 0x1f, 0x12, 0x31, 0x3c, 0x2b, 0x26,
|
||||
0xbd, 0xb0, 0xa7, 0xaa, 0x89, 0x84, 0x93, 0x9e, 0xd5, 0xd8, 0xcf, 0xc2, 0xe1, 0xec, 0xfb, 0xf6,
|
||||
0xd6, 0xdb, 0xcc, 0xc1, 0xe2, 0xef, 0xf8, 0xf5, 0xbe, 0xb3, 0xa4, 0xa9, 0x8a, 0x87, 0x90, 0x9d,
|
||||
0x06, 0x0b, 0x1c, 0x11, 0x32, 0x3f, 0x28, 0x25, 0x6e, 0x63, 0x74, 0x79, 0x5a, 0x57, 0x40, 0x4d,
|
||||
0xda, 0xd7, 0xc0, 0xcd, 0xee, 0xe3, 0xf4, 0xf9, 0xb2, 0xbf, 0xa8, 0xa5, 0x86, 0x8b, 0x9c, 0x91,
|
||||
0x0a, 0x07, 0x10, 0x1d, 0x3e, 0x33, 0x24, 0x29, 0x62, 0x6f, 0x78, 0x75, 0x56, 0x5b, 0x4c, 0x41,
|
||||
0x61, 0x6c, 0x7b, 0x76, 0x55, 0x58, 0x4f, 0x42, 0x09, 0x04, 0x13, 0x1e, 0x3d, 0x30, 0x27, 0x2a,
|
||||
0xb1, 0xbc, 0xab, 0xa6, 0x85, 0x88, 0x9f, 0x92, 0xd9, 0xd4, 0xc3, 0xce, 0xed, 0xe0, 0xf7, 0xfa,
|
||||
0xb7, 0xba, 0xad, 0xa0, 0x83, 0x8e, 0x99, 0x94, 0xdf, 0xd2, 0xc5, 0xc8, 0xeb, 0xe6, 0xf1, 0xfc,
|
||||
0x67, 0x6a, 0x7d, 0x70, 0x53, 0x5e, 0x49, 0x44, 0x0f, 0x02, 0x15, 0x18, 0x3b, 0x36, 0x21, 0x2c,
|
||||
0x0c, 0x01, 0x16, 0x1b, 0x38, 0x35, 0x22, 0x2f, 0x64, 0x69, 0x7e, 0x73, 0x50, 0x5d, 0x4a, 0x47,
|
||||
0xdc, 0xd1, 0xc6, 0xcb, 0xe8, 0xe5, 0xf2, 0xff, 0xb4, 0xb9, 0xae, 0xa3, 0x80, 0x8d, 0x9a, 0x97
|
||||
};
|
||||
|
||||
private static readonly byte[] _gfMul0E = new byte[]
|
||||
{
|
||||
0x00, 0x0e, 0x1c, 0x12, 0x38, 0x36, 0x24, 0x2a, 0x70, 0x7e, 0x6c, 0x62, 0x48, 0x46, 0x54, 0x5a,
|
||||
0xe0, 0xee, 0xfc, 0xf2, 0xd8, 0xd6, 0xc4, 0xca, 0x90, 0x9e, 0x8c, 0x82, 0xa8, 0xa6, 0xb4, 0xba,
|
||||
0xdb, 0xd5, 0xc7, 0xc9, 0xe3, 0xed, 0xff, 0xf1, 0xab, 0xa5, 0xb7, 0xb9, 0x93, 0x9d, 0x8f, 0x81,
|
||||
0x3b, 0x35, 0x27, 0x29, 0x03, 0x0d, 0x1f, 0x11, 0x4b, 0x45, 0x57, 0x59, 0x73, 0x7d, 0x6f, 0x61,
|
||||
0xad, 0xa3, 0xb1, 0xbf, 0x95, 0x9b, 0x89, 0x87, 0xdd, 0xd3, 0xc1, 0xcf, 0xe5, 0xeb, 0xf9, 0xf7,
|
||||
0x4d, 0x43, 0x51, 0x5f, 0x75, 0x7b, 0x69, 0x67, 0x3d, 0x33, 0x21, 0x2f, 0x05, 0x0b, 0x19, 0x17,
|
||||
0x76, 0x78, 0x6a, 0x64, 0x4e, 0x40, 0x52, 0x5c, 0x06, 0x08, 0x1a, 0x14, 0x3e, 0x30, 0x22, 0x2c,
|
||||
0x96, 0x98, 0x8a, 0x84, 0xae, 0xa0, 0xb2, 0xbc, 0xe6, 0xe8, 0xfa, 0xf4, 0xde, 0xd0, 0xc2, 0xcc,
|
||||
0x41, 0x4f, 0x5d, 0x53, 0x79, 0x77, 0x65, 0x6b, 0x31, 0x3f, 0x2d, 0x23, 0x09, 0x07, 0x15, 0x1b,
|
||||
0xa1, 0xaf, 0xbd, 0xb3, 0x99, 0x97, 0x85, 0x8b, 0xd1, 0xdf, 0xcd, 0xc3, 0xe9, 0xe7, 0xf5, 0xfb,
|
||||
0x9a, 0x94, 0x86, 0x88, 0xa2, 0xac, 0xbe, 0xb0, 0xea, 0xe4, 0xf6, 0xf8, 0xd2, 0xdc, 0xce, 0xc0,
|
||||
0x7a, 0x74, 0x66, 0x68, 0x42, 0x4c, 0x5e, 0x50, 0x0a, 0x04, 0x16, 0x18, 0x32, 0x3c, 0x2e, 0x20,
|
||||
0xec, 0xe2, 0xf0, 0xfe, 0xd4, 0xda, 0xc8, 0xc6, 0x9c, 0x92, 0x80, 0x8e, 0xa4, 0xaa, 0xb8, 0xb6,
|
||||
0x0c, 0x02, 0x10, 0x1e, 0x34, 0x3a, 0x28, 0x26, 0x7c, 0x72, 0x60, 0x6e, 0x44, 0x4a, 0x58, 0x56,
|
||||
0x37, 0x39, 0x2b, 0x25, 0x0f, 0x01, 0x13, 0x1d, 0x47, 0x49, 0x5b, 0x55, 0x7f, 0x71, 0x63, 0x6d,
|
||||
0xd7, 0xd9, 0xcb, 0xc5, 0xef, 0xe1, 0xf3, 0xfd, 0xa7, 0xa9, 0xbb, 0xb5, 0x9f, 0x91, 0x83, 0x8d
|
||||
};
|
||||
|
||||
private static readonly byte[] _srPerm = new byte[]
|
||||
{
|
||||
0, 13, 10, 7, 4, 1, 14, 11, 8, 5, 2, 15, 12, 9, 6, 3
|
||||
};
|
||||
|
||||
private static readonly byte[] _isrPerm = new byte[]
|
||||
{
|
||||
0, 5, 10, 15, 4, 9, 14, 3, 8, 13, 2, 7, 12, 1, 6, 11
|
||||
};
|
||||
#endregion
|
||||
|
||||
public static V128 AesInvMixColumns(V128 op)
|
||||
{
|
||||
byte[] inState = op.ToArray();
|
||||
byte[] outState = new byte[16];
|
||||
|
||||
for (int columns = 0; columns <= 3; columns++)
|
||||
{
|
||||
int idx = columns << 2;
|
||||
|
||||
byte row0 = inState[idx + 0]; // A, E, I, M: [row0, col0-col3]
|
||||
byte row1 = inState[idx + 1]; // B, F, J, N: [row1, col0-col3]
|
||||
byte row2 = inState[idx + 2]; // C, G, K, O: [row2, col0-col3]
|
||||
byte row3 = inState[idx + 3]; // D, H, L, P: [row3, col0-col3]
|
||||
|
||||
outState[idx + 0] = (byte)((uint)_gfMul0E[row0] ^ _gfMul0B[row1] ^ _gfMul0D[row2] ^ _gfMul09[row3]);
|
||||
outState[idx + 1] = (byte)((uint)_gfMul09[row0] ^ _gfMul0E[row1] ^ _gfMul0B[row2] ^ _gfMul0D[row3]);
|
||||
outState[idx + 2] = (byte)((uint)_gfMul0D[row0] ^ _gfMul09[row1] ^ _gfMul0E[row2] ^ _gfMul0B[row3]);
|
||||
outState[idx + 3] = (byte)((uint)_gfMul0B[row0] ^ _gfMul0D[row1] ^ _gfMul09[row2] ^ _gfMul0E[row3]);
|
||||
}
|
||||
|
||||
return new V128(outState);
|
||||
}
|
||||
|
||||
public static V128 AesInvShiftRows(V128 op)
|
||||
{
|
||||
byte[] inState = op.ToArray();
|
||||
byte[] outState = new byte[16];
|
||||
|
||||
for (int idx = 0; idx <= 15; idx++)
|
||||
{
|
||||
outState[_isrPerm[idx]] = inState[idx];
|
||||
}
|
||||
|
||||
return new V128(outState);
|
||||
}
|
||||
|
||||
public static V128 AesInvSubBytes(V128 op)
|
||||
{
|
||||
byte[] inState = op.ToArray();
|
||||
byte[] outState = new byte[16];
|
||||
|
||||
for (int idx = 0; idx <= 15; idx++)
|
||||
{
|
||||
outState[idx] = _invSBox[inState[idx]];
|
||||
}
|
||||
|
||||
return new V128(outState);
|
||||
}
|
||||
|
||||
public static V128 AesMixColumns(V128 op)
|
||||
{
|
||||
byte[] inState = op.ToArray();
|
||||
byte[] outState = new byte[16];
|
||||
|
||||
for (int columns = 0; columns <= 3; columns++)
|
||||
{
|
||||
int idx = columns << 2;
|
||||
|
||||
byte row0 = inState[idx + 0]; // A, E, I, M: [row0, col0-col3]
|
||||
byte row1 = inState[idx + 1]; // B, F, J, N: [row1, col0-col3]
|
||||
byte row2 = inState[idx + 2]; // C, G, K, O: [row2, col0-col3]
|
||||
byte row3 = inState[idx + 3]; // D, H, L, P: [row3, col0-col3]
|
||||
|
||||
outState[idx + 0] = (byte)((uint)_gfMul02[row0] ^ _gfMul03[row1] ^ row2 ^ row3);
|
||||
outState[idx + 1] = (byte)((uint)row0 ^ _gfMul02[row1] ^ _gfMul03[row2] ^ row3);
|
||||
outState[idx + 2] = (byte)((uint)row0 ^ row1 ^ _gfMul02[row2] ^ _gfMul03[row3]);
|
||||
outState[idx + 3] = (byte)((uint)_gfMul03[row0] ^ row1 ^ row2 ^ _gfMul02[row3]);
|
||||
}
|
||||
|
||||
return new V128(outState);
|
||||
}
|
||||
|
||||
public static V128 AesShiftRows(V128 op)
|
||||
{
|
||||
byte[] inState = op.ToArray();
|
||||
byte[] outState = new byte[16];
|
||||
|
||||
for (int idx = 0; idx <= 15; idx++)
|
||||
{
|
||||
outState[_srPerm[idx]] = inState[idx];
|
||||
}
|
||||
|
||||
return new V128(outState);
|
||||
}
|
||||
|
||||
public static V128 AesSubBytes(V128 op)
|
||||
{
|
||||
byte[] inState = op.ToArray();
|
||||
byte[] outState = new byte[16];
|
||||
|
||||
for (int idx = 0; idx <= 15; idx++)
|
||||
{
|
||||
outState[idx] = _sBox[inState[idx]];
|
||||
}
|
||||
|
||||
return new V128(outState);
|
||||
}
|
||||
}
|
||||
}
|
78
ARMeilleure/Instructions/DelegateTypes.cs
Normal file
78
ARMeilleure/Instructions/DelegateTypes.cs
Normal file
|
@ -0,0 +1,78 @@
|
|||
using ARMeilleure.State;
|
||||
using System;
|
||||
|
||||
namespace ARMeilleure.Instructions
|
||||
{
|
||||
delegate double _F64_F64(double a1);
|
||||
delegate double _F64_F64_F64(double a1, double a2);
|
||||
delegate double _F64_F64_F64_F64(double a1, double a2, double a3);
|
||||
delegate double _F64_F64_MidpointRounding(double a1, MidpointRounding a2);
|
||||
|
||||
delegate float _F32_F32(float a1);
|
||||
delegate float _F32_F32_F32(float a1, float a2);
|
||||
delegate float _F32_F32_F32_F32(float a1, float a2, float a3);
|
||||
delegate float _F32_F32_MidpointRounding(float a1, MidpointRounding a2);
|
||||
delegate float _F32_U16(ushort a1);
|
||||
|
||||
delegate int _S32_F32(float a1);
|
||||
delegate int _S32_F32_F32_Bool(float a1, float a2, bool a3);
|
||||
delegate int _S32_F64(double a1);
|
||||
delegate int _S32_F64_F64_Bool(double a1, double a2, bool a3);
|
||||
delegate int _S32_U64_U16(ulong a1, ushort a2);
|
||||
delegate int _S32_U64_U32(ulong a1, uint a2);
|
||||
delegate int _S32_U64_U64(ulong a1, ulong a2);
|
||||
delegate int _S32_U64_U8(ulong a1, byte a2);
|
||||
delegate int _S32_U64_V128(ulong a1, V128 a2);
|
||||
|
||||
delegate long _S64_F32(float a1);
|
||||
delegate long _S64_F64(double a1);
|
||||
delegate long _S64_S64(long a1);
|
||||
delegate long _S64_S64_S32(long a1, int a2);
|
||||
delegate long _S64_S64_S64(long a1, long a2);
|
||||
delegate long _S64_S64_S64_Bool_S32(long a1, long a2, bool a3, int a4);
|
||||
delegate long _S64_S64_S64_S32(long a1, long a2, int a3);
|
||||
delegate long _S64_U64_S32(ulong a1, int a2);
|
||||
delegate long _S64_U64_S64(ulong a1, long a2);
|
||||
|
||||
delegate ushort _U16_F32(float a1);
|
||||
delegate ushort _U16_U64(ulong a1);
|
||||
|
||||
delegate uint _U32_F32(float a1);
|
||||
delegate uint _U32_F64(double a1);
|
||||
delegate uint _U32_U32(uint a1);
|
||||
delegate uint _U32_U32_U16(uint a1, ushort a2);
|
||||
delegate uint _U32_U32_U32(uint a1, uint a2);
|
||||
delegate uint _U32_U32_U64(uint a1, ulong a2);
|
||||
delegate uint _U32_U32_U8(uint a1, byte a2);
|
||||
delegate uint _U32_U64(ulong a1);
|
||||
|
||||
delegate ulong _U64();
|
||||
delegate ulong _U64_F32(float a1);
|
||||
delegate ulong _U64_F64(double a1);
|
||||
delegate ulong _U64_S64_S32(long a1, int a2);
|
||||
delegate ulong _U64_S64_U64(long a1, ulong a2);
|
||||
delegate ulong _U64_U64(ulong a1);
|
||||
delegate ulong _U64_U64_S32(ulong a1, int a2);
|
||||
delegate ulong _U64_U64_S64_S32(ulong a1, long a2, int a3);
|
||||
delegate ulong _U64_U64_U64(ulong a1, ulong a2);
|
||||
delegate ulong _U64_U64_U64_Bool_S32(ulong a1, ulong a2, bool a3, int a4);
|
||||
|
||||
delegate byte _U8_U64(ulong a1);
|
||||
|
||||
delegate V128 _V128_U64(ulong a1);
|
||||
delegate V128 _V128_V128(V128 a1);
|
||||
delegate V128 _V128_V128_U32_V128(V128 a1, uint a2, V128 a3);
|
||||
delegate V128 _V128_V128_V128(V128 a1, V128 a2);
|
||||
delegate V128 _V128_V128_V128_V128(V128 a1, V128 a2, V128 a3);
|
||||
delegate V128 _V128_V128_V128_V128_V128(V128 a1, V128 a2, V128 a3, V128 a4);
|
||||
delegate V128 _V128_V128_V128_V128_V128_V128(V128 a1, V128 a2, V128 a3, V128 a4, V128 a5);
|
||||
|
||||
delegate void _Void();
|
||||
delegate void _Void_U64(ulong a1);
|
||||
delegate void _Void_U64_S32(ulong a1, int a2);
|
||||
delegate void _Void_U64_U16(ulong a1, ushort a2);
|
||||
delegate void _Void_U64_U32(ulong a1, uint a2);
|
||||
delegate void _Void_U64_U64(ulong a1, ulong a2);
|
||||
delegate void _Void_U64_U8(ulong a1, byte a2);
|
||||
delegate void _Void_U64_V128(ulong a1, V128 a2);
|
||||
}
|
369
ARMeilleure/Instructions/InstEmitAlu.cs
Normal file
369
ARMeilleure/Instructions/InstEmitAlu.cs
Normal file
|
@ -0,0 +1,369 @@
|
|||
using ARMeilleure.Decoders;
|
||||
using ARMeilleure.IntermediateRepresentation;
|
||||
using ARMeilleure.State;
|
||||
using ARMeilleure.Translation;
|
||||
|
||||
using static ARMeilleure.Instructions.InstEmitAluHelper;
|
||||
using static ARMeilleure.Instructions.InstEmitHelper;
|
||||
using static ARMeilleure.IntermediateRepresentation.OperandHelper;
|
||||
|
||||
namespace ARMeilleure.Instructions
|
||||
{
|
||||
static partial class InstEmit
|
||||
{
|
||||
public static void Adc(ArmEmitterContext context) => EmitAdc(context, setFlags: false);
|
||||
public static void Adcs(ArmEmitterContext context) => EmitAdc(context, setFlags: true);
|
||||
|
||||
private static void EmitAdc(ArmEmitterContext context, bool setFlags)
|
||||
{
|
||||
Operand n = GetAluN(context);
|
||||
Operand m = GetAluM(context);
|
||||
|
||||
Operand d = context.Add(n, m);
|
||||
|
||||
Operand carry = GetFlag(PState.CFlag);
|
||||
|
||||
if (context.CurrOp.RegisterSize == RegisterSize.Int64)
|
||||
{
|
||||
carry = context.ZeroExtend32(OperandType.I64, carry);
|
||||
}
|
||||
|
||||
d = context.Add(d, carry);
|
||||
|
||||
if (setFlags)
|
||||
{
|
||||
EmitNZFlagsCheck(context, d);
|
||||
|
||||
EmitAdcsCCheck(context, n, d);
|
||||
EmitAddsVCheck(context, n, m, d);
|
||||
}
|
||||
|
||||
SetAluDOrZR(context, d);
|
||||
}
|
||||
|
||||
public static void Add(ArmEmitterContext context)
|
||||
{
|
||||
SetAluD(context, context.Add(GetAluN(context), GetAluM(context)));
|
||||
}
|
||||
|
||||
public static void Adds(ArmEmitterContext context)
|
||||
{
|
||||
Operand n = GetAluN(context);
|
||||
Operand m = GetAluM(context);
|
||||
|
||||
context.MarkComparison(n, m);
|
||||
|
||||
Operand d = context.Add(n, m);
|
||||
|
||||
EmitNZFlagsCheck(context, d);
|
||||
|
||||
EmitAddsCCheck(context, n, d);
|
||||
EmitAddsVCheck(context, n, m, d);
|
||||
|
||||
SetAluDOrZR(context, d);
|
||||
}
|
||||
|
||||
public static void And(ArmEmitterContext context)
|
||||
{
|
||||
SetAluD(context, context.BitwiseAnd(GetAluN(context), GetAluM(context)));
|
||||
}
|
||||
|
||||
public static void Ands(ArmEmitterContext context)
|
||||
{
|
||||
Operand n = GetAluN(context);
|
||||
Operand m = GetAluM(context);
|
||||
|
||||
Operand d = context.BitwiseAnd(n, m);
|
||||
|
||||
EmitNZFlagsCheck(context, d);
|
||||
EmitCVFlagsClear(context);
|
||||
|
||||
SetAluDOrZR(context, d);
|
||||
}
|
||||
|
||||
public static void Asrv(ArmEmitterContext context)
|
||||
{
|
||||
SetAluDOrZR(context, context.ShiftRightSI(GetAluN(context), GetAluMShift(context)));
|
||||
}
|
||||
|
||||
public static void Bic(ArmEmitterContext context) => EmitBic(context, setFlags: false);
|
||||
public static void Bics(ArmEmitterContext context) => EmitBic(context, setFlags: true);
|
||||
|
||||
private static void EmitBic(ArmEmitterContext context, bool setFlags)
|
||||
{
|
||||
Operand n = GetAluN(context);
|
||||
Operand m = GetAluM(context);
|
||||
|
||||
Operand d = context.BitwiseAnd(n, context.BitwiseNot(m));
|
||||
|
||||
if (setFlags)
|
||||
{
|
||||
EmitNZFlagsCheck(context, d);
|
||||
EmitCVFlagsClear(context);
|
||||
}
|
||||
|
||||
SetAluD(context, d, setFlags);
|
||||
}
|
||||
|
||||
public static void Cls(ArmEmitterContext context)
|
||||
{
|
||||
OpCodeAlu op = (OpCodeAlu)context.CurrOp;
|
||||
|
||||
Operand n = GetIntOrZR(context, op.Rn);
|
||||
|
||||
Operand nHigh = context.ShiftRightUI(n, Const(1));
|
||||
|
||||
bool is32Bits = op.RegisterSize == RegisterSize.Int32;
|
||||
|
||||
Operand mask = is32Bits ? Const(int.MaxValue) : Const(long.MaxValue);
|
||||
|
||||
Operand nLow = context.BitwiseAnd(n, mask);
|
||||
|
||||
Operand res = context.CountLeadingZeros(context.BitwiseExclusiveOr(nHigh, nLow));
|
||||
|
||||
res = context.Subtract(res, Const(res.Type, 1));
|
||||
|
||||
SetAluDOrZR(context, res);
|
||||
}
|
||||
|
||||
public static void Clz(ArmEmitterContext context)
|
||||
{
|
||||
OpCodeAlu op = (OpCodeAlu)context.CurrOp;
|
||||
|
||||
Operand n = GetIntOrZR(context, op.Rn);
|
||||
|
||||
Operand d = context.CountLeadingZeros(n);
|
||||
|
||||
SetAluDOrZR(context, d);
|
||||
}
|
||||
|
||||
public static void Eon(ArmEmitterContext context)
|
||||
{
|
||||
Operand n = GetAluN(context);
|
||||
Operand m = GetAluM(context);
|
||||
|
||||
Operand d = context.BitwiseExclusiveOr(n, context.BitwiseNot(m));
|
||||
|
||||
SetAluD(context, d);
|
||||
}
|
||||
|
||||
public static void Eor(ArmEmitterContext context)
|
||||
{
|
||||
SetAluD(context, context.BitwiseExclusiveOr(GetAluN(context), GetAluM(context)));
|
||||
}
|
||||
|
||||
public static void Extr(ArmEmitterContext context)
|
||||
{
|
||||
OpCodeAluRs op = (OpCodeAluRs)context.CurrOp;
|
||||
|
||||
Operand res = GetIntOrZR(context, op.Rm);
|
||||
|
||||
if (op.Shift != 0)
|
||||
{
|
||||
if (op.Rn == op.Rm)
|
||||
{
|
||||
res = context.RotateRight(res, Const(op.Shift));
|
||||
}
|
||||
else
|
||||
{
|
||||
res = context.ShiftRightUI(res, Const(op.Shift));
|
||||
|
||||
Operand n = GetIntOrZR(context, op.Rn);
|
||||
|
||||
int invShift = op.GetBitsCount() - op.Shift;
|
||||
|
||||
res = context.BitwiseOr(res, context.ShiftLeft(n, Const(invShift)));
|
||||
}
|
||||
}
|
||||
|
||||
SetAluDOrZR(context, res);
|
||||
}
|
||||
|
||||
public static void Lslv(ArmEmitterContext context)
|
||||
{
|
||||
SetAluDOrZR(context, context.ShiftLeft(GetAluN(context), GetAluMShift(context)));
|
||||
}
|
||||
|
||||
public static void Lsrv(ArmEmitterContext context)
|
||||
{
|
||||
SetAluDOrZR(context, context.ShiftRightUI(GetAluN(context), GetAluMShift(context)));
|
||||
}
|
||||
|
||||
public static void Sbc(ArmEmitterContext context) => EmitSbc(context, setFlags: false);
|
||||
public static void Sbcs(ArmEmitterContext context) => EmitSbc(context, setFlags: true);
|
||||
|
||||
private static void EmitSbc(ArmEmitterContext context, bool setFlags)
|
||||
{
|
||||
Operand n = GetAluN(context);
|
||||
Operand m = GetAluM(context);
|
||||
|
||||
Operand d = context.Subtract(n, m);
|
||||
|
||||
Operand borrow = context.BitwiseExclusiveOr(GetFlag(PState.CFlag), Const(1));
|
||||
|
||||
if (context.CurrOp.RegisterSize == RegisterSize.Int64)
|
||||
{
|
||||
borrow = context.ZeroExtend32(OperandType.I64, borrow);
|
||||
}
|
||||
|
||||
d = context.Subtract(d, borrow);
|
||||
|
||||
if (setFlags)
|
||||
{
|
||||
EmitNZFlagsCheck(context, d);
|
||||
|
||||
EmitSbcsCCheck(context, n, m);
|
||||
EmitSubsVCheck(context, n, m, d);
|
||||
}
|
||||
|
||||
SetAluDOrZR(context, d);
|
||||
}
|
||||
|
||||
public static void Sub(ArmEmitterContext context)
|
||||
{
|
||||
SetAluD(context, context.Subtract(GetAluN(context), GetAluM(context)));
|
||||
}
|
||||
|
||||
public static void Subs(ArmEmitterContext context)
|
||||
{
|
||||
Operand n = GetAluN(context);
|
||||
Operand m = GetAluM(context);
|
||||
|
||||
context.MarkComparison(n, m);
|
||||
|
||||
Operand d = context.Subtract(n, m);
|
||||
|
||||
EmitNZFlagsCheck(context, d);
|
||||
|
||||
EmitSubsCCheck(context, n, m);
|
||||
EmitSubsVCheck(context, n, m, d);
|
||||
|
||||
SetAluDOrZR(context, d);
|
||||
}
|
||||
|
||||
public static void Orn(ArmEmitterContext context)
|
||||
{
|
||||
Operand n = GetAluN(context);
|
||||
Operand m = GetAluM(context);
|
||||
|
||||
Operand d = context.BitwiseOr(n, context.BitwiseNot(m));
|
||||
|
||||
SetAluD(context, d);
|
||||
}
|
||||
|
||||
public static void Orr(ArmEmitterContext context)
|
||||
{
|
||||
SetAluD(context, context.BitwiseOr(GetAluN(context), GetAluM(context)));
|
||||
}
|
||||
|
||||
public static void Rbit(ArmEmitterContext context)
|
||||
{
|
||||
OpCodeAlu op = (OpCodeAlu)context.CurrOp;
|
||||
|
||||
Operand n = GetIntOrZR(context, op.Rn);
|
||||
Operand d;
|
||||
|
||||
if (op.RegisterSize == RegisterSize.Int32)
|
||||
{
|
||||
d = context.Call(new _U32_U32(SoftFallback.ReverseBits32), n);
|
||||
}
|
||||
else
|
||||
{
|
||||
d = context.Call(new _U64_U64(SoftFallback.ReverseBits64), n);
|
||||
}
|
||||
|
||||
SetAluDOrZR(context, d);
|
||||
}
|
||||
|
||||
public static void Rev16(ArmEmitterContext context)
|
||||
{
|
||||
OpCodeAlu op = (OpCodeAlu)context.CurrOp;
|
||||
|
||||
Operand n = GetIntOrZR(context, op.Rn);
|
||||
Operand d;
|
||||
|
||||
if (op.RegisterSize == RegisterSize.Int32)
|
||||
{
|
||||
d = context.Call(new _U32_U32(SoftFallback.ReverseBytes16_32), n);
|
||||
}
|
||||
else
|
||||
{
|
||||
d = context.Call(new _U64_U64(SoftFallback.ReverseBytes16_64), n);
|
||||
}
|
||||
|
||||
SetAluDOrZR(context, d);
|
||||
}
|
||||
|
||||
public static void Rev32(ArmEmitterContext context)
|
||||
{
|
||||
OpCodeAlu op = (OpCodeAlu)context.CurrOp;
|
||||
|
||||
Operand n = GetIntOrZR(context, op.Rn);
|
||||
|
||||
if (op.RegisterSize == RegisterSize.Int32)
|
||||
{
|
||||
SetAluDOrZR(context, context.ByteSwap(n));
|
||||
}
|
||||
else
|
||||
{
|
||||
Operand d = context.Call(new _U64_U64(SoftFallback.ReverseBytes32_64), n);
|
||||
|
||||
SetAluDOrZR(context, d);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Rev64(ArmEmitterContext context)
|
||||
{
|
||||
OpCodeAlu op = (OpCodeAlu)context.CurrOp;
|
||||
|
||||
SetAluDOrZR(context, context.ByteSwap(GetIntOrZR(context, op.Rn)));
|
||||
}
|
||||
|
||||
public static void Rorv(ArmEmitterContext context)
|
||||
{
|
||||
SetAluDOrZR(context, context.RotateRight(GetAluN(context), GetAluMShift(context)));
|
||||
}
|
||||
|
||||
private static Operand GetAluMShift(ArmEmitterContext context)
|
||||
{
|
||||
IOpCodeAluRs op = (IOpCodeAluRs)context.CurrOp;
|
||||
|
||||
Operand m = GetIntOrZR(context, op.Rm);
|
||||
|
||||
if (op.RegisterSize == RegisterSize.Int64)
|
||||
{
|
||||
m = context.ConvertI64ToI32(m);
|
||||
}
|
||||
|
||||
return context.BitwiseAnd(m, Const(context.CurrOp.GetBitsCount() - 1));
|
||||
}
|
||||
|
||||
private static void EmitCVFlagsClear(ArmEmitterContext context)
|
||||
{
|
||||
SetFlag(context, PState.CFlag, Const(0));
|
||||
SetFlag(context, PState.VFlag, Const(0));
|
||||
}
|
||||
|
||||
public static void SetAluD(ArmEmitterContext context, Operand d)
|
||||
{
|
||||
SetAluD(context, d, x31IsZR: false);
|
||||
}
|
||||
|
||||
public static void SetAluDOrZR(ArmEmitterContext context, Operand d)
|
||||
{
|
||||
SetAluD(context, d, x31IsZR: true);
|
||||
}
|
||||
|
||||
public static void SetAluD(ArmEmitterContext context, Operand d, bool x31IsZR)
|
||||
{
|
||||
IOpCodeAlu op = (IOpCodeAlu)context.CurrOp;
|
||||
|
||||
if ((x31IsZR || op is IOpCodeAluRs) && op.Rd == RegisterConsts.ZeroIndex)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
SetIntOrSP(context, op.Rd, d);
|
||||
}
|
||||
}
|
||||
}
|
129
ARMeilleure/Instructions/InstEmitAlu32.cs
Normal file
129
ARMeilleure/Instructions/InstEmitAlu32.cs
Normal file
|
@ -0,0 +1,129 @@
|
|||
using ARMeilleure.Decoders;
|
||||
using ARMeilleure.IntermediateRepresentation;
|
||||
using ARMeilleure.State;
|
||||
using ARMeilleure.Translation;
|
||||
|
||||
using static ARMeilleure.Instructions.InstEmitHelper;
|
||||
using static ARMeilleure.Instructions.InstEmitAluHelper;
|
||||
using static ARMeilleure.IntermediateRepresentation.OperandHelper;
|
||||
|
||||
namespace ARMeilleure.Instructions
|
||||
{
|
||||
static partial class InstEmit32
|
||||
{
|
||||
public static void Add(ArmEmitterContext context)
|
||||
{
|
||||
IOpCode32Alu op = (IOpCode32Alu)context.CurrOp;
|
||||
|
||||
Operand n = GetAluN(context);
|
||||
Operand m = GetAluM(context, setCarry: false);
|
||||
|
||||
Operand res = context.Add(n, m);
|
||||
|
||||
if (op.SetFlags)
|
||||
{
|
||||
EmitNZFlagsCheck(context, res);
|
||||
|
||||
EmitAddsCCheck(context, n, res);
|
||||
EmitAddsVCheck(context, n, m, res);
|
||||
}
|
||||
|
||||
EmitAluStore(context, res);
|
||||
}
|
||||
|
||||
public static void Cmp(ArmEmitterContext context)
|
||||
{
|
||||
IOpCode32Alu op = (IOpCode32Alu)context.CurrOp;
|
||||
|
||||
Operand n = GetAluN(context);
|
||||
Operand m = GetAluM(context, setCarry: false);
|
||||
|
||||
Operand res = context.Subtract(n, m);
|
||||
|
||||
EmitNZFlagsCheck(context, res);
|
||||
|
||||
EmitSubsCCheck(context, n, res);
|
||||
EmitSubsVCheck(context, n, m, res);
|
||||
}
|
||||
|
||||
public static void Mov(ArmEmitterContext context)
|
||||
{
|
||||
IOpCode32Alu op = (IOpCode32Alu)context.CurrOp;
|
||||
|
||||
Operand m = GetAluM(context);
|
||||
|
||||
if (op.SetFlags)
|
||||
{
|
||||
EmitNZFlagsCheck(context, m);
|
||||
}
|
||||
|
||||
EmitAluStore(context, m);
|
||||
}
|
||||
|
||||
public static void Sub(ArmEmitterContext context)
|
||||
{
|
||||
IOpCode32Alu op = (IOpCode32Alu)context.CurrOp;
|
||||
|
||||
Operand n = GetAluN(context);
|
||||
Operand m = GetAluM(context, setCarry: false);
|
||||
|
||||
Operand res = context.Subtract(n, m);
|
||||
|
||||
if (op.SetFlags)
|
||||
{
|
||||
EmitNZFlagsCheck(context, res);
|
||||
|
||||
EmitSubsCCheck(context, n, res);
|
||||
EmitSubsVCheck(context, n, m, res);
|
||||
}
|
||||
|
||||
EmitAluStore(context, res);
|
||||
}
|
||||
|
||||
private static void EmitAluStore(ArmEmitterContext context, Operand value)
|
||||
{
|
||||
IOpCode32Alu op = (IOpCode32Alu)context.CurrOp;
|
||||
|
||||
if (op.Rd == RegisterAlias.Aarch32Pc)
|
||||
{
|
||||
if (op.SetFlags)
|
||||
{
|
||||
// TODO: Load SPSR etc.
|
||||
Operand isThumb = GetFlag(PState.TFlag);
|
||||
|
||||
Operand lblThumb = Label();
|
||||
|
||||
context.BranchIfTrue(lblThumb, isThumb);
|
||||
|
||||
context.Return(context.ZeroExtend32(OperandType.I64, context.BitwiseAnd(value, Const(~3))));
|
||||
|
||||
context.MarkLabel(lblThumb);
|
||||
|
||||
context.Return(context.ZeroExtend32(OperandType.I64, context.BitwiseAnd(value, Const(~1))));
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitAluWritePc(context, value);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
SetIntA32(context, op.Rd, value);
|
||||
}
|
||||
}
|
||||
|
||||
private static void EmitAluWritePc(ArmEmitterContext context, Operand value)
|
||||
{
|
||||
context.StoreToContext();
|
||||
|
||||
if (IsThumb(context.CurrOp))
|
||||
{
|
||||
context.Return(context.ZeroExtend32(OperandType.I64, context.BitwiseAnd(value, Const(~1))));
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitBxWritePc(context, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
351
ARMeilleure/Instructions/InstEmitAluHelper.cs
Normal file
351
ARMeilleure/Instructions/InstEmitAluHelper.cs
Normal file
|
@ -0,0 +1,351 @@
|
|||
using ARMeilleure.Decoders;
|
||||
using ARMeilleure.IntermediateRepresentation;
|
||||
using ARMeilleure.State;
|
||||
using ARMeilleure.Translation;
|
||||
using System;
|
||||
|
||||
using static ARMeilleure.Instructions.InstEmitHelper;
|
||||
using static ARMeilleure.IntermediateRepresentation.OperandHelper;
|
||||
|
||||
namespace ARMeilleure.Instructions
|
||||
{
|
||||
static class InstEmitAluHelper
|
||||
{
|
||||
public static void EmitNZFlagsCheck(ArmEmitterContext context, Operand d)
|
||||
{
|
||||
SetFlag(context, PState.NFlag, context.ICompareLess (d, Const(d.Type, 0)));
|
||||
SetFlag(context, PState.ZFlag, context.ICompareEqual(d, Const(d.Type, 0)));
|
||||
}
|
||||
|
||||
public static void EmitAdcsCCheck(ArmEmitterContext context, Operand n, Operand d)
|
||||
{
|
||||
// C = (Rd == Rn && CIn) || Rd < Rn
|
||||
Operand cIn = GetFlag(PState.CFlag);
|
||||
|
||||
Operand cOut = context.BitwiseAnd(context.ICompareEqual(d, n), cIn);
|
||||
|
||||
cOut = context.BitwiseOr(cOut, context.ICompareLessUI(d, n));
|
||||
|
||||
SetFlag(context, PState.CFlag, cOut);
|
||||
}
|
||||
|
||||
public static void EmitAddsCCheck(ArmEmitterContext context, Operand n, Operand d)
|
||||
{
|
||||
// C = Rd < Rn
|
||||
SetFlag(context, PState.CFlag, context.ICompareLessUI(d, n));
|
||||
}
|
||||
|
||||
public static void EmitAddsVCheck(ArmEmitterContext context, Operand n, Operand m, Operand d)
|
||||
{
|
||||
// V = (Rd ^ Rn) & ~(Rn ^ Rm) < 0
|
||||
Operand vOut = context.BitwiseExclusiveOr(d, n);
|
||||
|
||||
vOut = context.BitwiseAnd(vOut, context.BitwiseNot(context.BitwiseExclusiveOr(n, m)));
|
||||
|
||||
vOut = context.ICompareLess(vOut, Const(vOut.Type, 0));
|
||||
|
||||
SetFlag(context, PState.VFlag, vOut);
|
||||
}
|
||||
|
||||
public static void EmitSbcsCCheck(ArmEmitterContext context, Operand n, Operand m)
|
||||
{
|
||||
// C = (Rn == Rm && CIn) || Rn > Rm
|
||||
Operand cIn = GetFlag(PState.CFlag);
|
||||
|
||||
Operand cOut = context.BitwiseAnd(context.ICompareEqual(n, m), cIn);
|
||||
|
||||
cOut = context.BitwiseOr(cOut, context.ICompareGreaterUI(n, m));
|
||||
|
||||
SetFlag(context, PState.CFlag, cOut);
|
||||
}
|
||||
|
||||
public static void EmitSubsCCheck(ArmEmitterContext context, Operand n, Operand m)
|
||||
{
|
||||
// C = Rn >= Rm
|
||||
SetFlag(context, PState.CFlag, context.ICompareGreaterOrEqualUI(n, m));
|
||||
}
|
||||
|
||||
public static void EmitSubsVCheck(ArmEmitterContext context, Operand n, Operand m, Operand d)
|
||||
{
|
||||
// V = (Rd ^ Rn) & (Rn ^ Rm) < 0
|
||||
Operand vOut = context.BitwiseExclusiveOr(d, n);
|
||||
|
||||
vOut = context.BitwiseAnd(vOut, context.BitwiseExclusiveOr(n, m));
|
||||
|
||||
vOut = context.ICompareLess(vOut, Const(vOut.Type, 0));
|
||||
|
||||
SetFlag(context, PState.VFlag, vOut);
|
||||
}
|
||||
|
||||
|
||||
public static Operand GetAluN(ArmEmitterContext context)
|
||||
{
|
||||
if (context.CurrOp is IOpCodeAlu op)
|
||||
{
|
||||
if (op.DataOp == DataOp.Logical || op is IOpCodeAluRs)
|
||||
{
|
||||
return GetIntOrZR(context, op.Rn);
|
||||
}
|
||||
else
|
||||
{
|
||||
return GetIntOrSP(context, op.Rn);
|
||||
}
|
||||
}
|
||||
else if (context.CurrOp is IOpCode32Alu op32)
|
||||
{
|
||||
return GetIntA32(context, op32.Rn);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw InvalidOpCodeType(context.CurrOp);
|
||||
}
|
||||
}
|
||||
|
||||
public static Operand GetAluM(ArmEmitterContext context, bool setCarry = true)
|
||||
{
|
||||
switch (context.CurrOp)
|
||||
{
|
||||
// ARM32.
|
||||
case OpCode32AluImm op:
|
||||
{
|
||||
if (op.SetFlags && op.IsRotated)
|
||||
{
|
||||
SetFlag(context, PState.CFlag, Const((uint)op.Immediate >> 31));
|
||||
}
|
||||
|
||||
return Const(op.Immediate);
|
||||
}
|
||||
|
||||
case OpCode32AluRsImm op: return GetMShiftedByImmediate(context, op, setCarry);
|
||||
|
||||
case OpCodeT16AluImm8 op: return Const(op.Immediate);
|
||||
|
||||
// ARM64.
|
||||
case IOpCodeAluImm op:
|
||||
{
|
||||
if (op.GetOperandType() == OperandType.I32)
|
||||
{
|
||||
return Const((int)op.Immediate);
|
||||
}
|
||||
else
|
||||
{
|
||||
return Const(op.Immediate);
|
||||
}
|
||||
}
|
||||
|
||||
case IOpCodeAluRs op:
|
||||
{
|
||||
Operand value = GetIntOrZR(context, op.Rm);
|
||||
|
||||
switch (op.ShiftType)
|
||||
{
|
||||
case ShiftType.Lsl: value = context.ShiftLeft (value, Const(op.Shift)); break;
|
||||
case ShiftType.Lsr: value = context.ShiftRightUI(value, Const(op.Shift)); break;
|
||||
case ShiftType.Asr: value = context.ShiftRightSI(value, Const(op.Shift)); break;
|
||||
case ShiftType.Ror: value = context.RotateRight (value, Const(op.Shift)); break;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
case IOpCodeAluRx op:
|
||||
{
|
||||
Operand value = GetExtendedM(context, op.Rm, op.IntType);
|
||||
|
||||
value = context.ShiftLeft(value, Const(op.Shift));
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
default: throw InvalidOpCodeType(context.CurrOp);
|
||||
}
|
||||
}
|
||||
|
||||
private static Exception InvalidOpCodeType(OpCode opCode)
|
||||
{
|
||||
return new InvalidOperationException($"Invalid OpCode type \"{opCode?.GetType().Name ?? "null"}\".");
|
||||
}
|
||||
|
||||
// ARM32 helpers.
|
||||
private static Operand GetMShiftedByImmediate(ArmEmitterContext context, OpCode32AluRsImm op, bool setCarry)
|
||||
{
|
||||
Operand m = GetIntA32(context, op.Rm);
|
||||
|
||||
int shift = op.Imm;
|
||||
|
||||
if (shift == 0)
|
||||
{
|
||||
switch (op.ShiftType)
|
||||
{
|
||||
case ShiftType.Lsr: shift = 32; break;
|
||||
case ShiftType.Asr: shift = 32; break;
|
||||
case ShiftType.Ror: shift = 1; break;
|
||||
}
|
||||
}
|
||||
|
||||
if (shift != 0)
|
||||
{
|
||||
setCarry &= op.SetFlags;
|
||||
|
||||
switch (op.ShiftType)
|
||||
{
|
||||
case ShiftType.Lsl: m = GetLslC(context, m, setCarry, shift); break;
|
||||
case ShiftType.Lsr: m = GetLsrC(context, m, setCarry, shift); break;
|
||||
case ShiftType.Asr: m = GetAsrC(context, m, setCarry, shift); break;
|
||||
case ShiftType.Ror:
|
||||
if (op.Imm != 0)
|
||||
{
|
||||
m = GetRorC(context, m, setCarry, shift);
|
||||
}
|
||||
else
|
||||
{
|
||||
m = GetRrxC(context, m, setCarry);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
private static Operand GetLslC(ArmEmitterContext context, Operand m, bool setCarry, int shift)
|
||||
{
|
||||
if ((uint)shift > 32)
|
||||
{
|
||||
return GetShiftByMoreThan32(context, setCarry);
|
||||
}
|
||||
else if (shift == 32)
|
||||
{
|
||||
if (setCarry)
|
||||
{
|
||||
SetCarryMLsb(context, m);
|
||||
}
|
||||
|
||||
return Const(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (setCarry)
|
||||
{
|
||||
Operand cOut = context.ShiftRightUI(m, Const(32 - shift));
|
||||
|
||||
cOut = context.BitwiseAnd(cOut, Const(1));
|
||||
|
||||
SetFlag(context, PState.CFlag, cOut);
|
||||
}
|
||||
|
||||
return context.ShiftLeft(m, Const(shift));
|
||||
}
|
||||
}
|
||||
|
||||
private static Operand GetLsrC(ArmEmitterContext context, Operand m, bool setCarry, int shift)
|
||||
{
|
||||
if ((uint)shift > 32)
|
||||
{
|
||||
return GetShiftByMoreThan32(context, setCarry);
|
||||
}
|
||||
else if (shift == 32)
|
||||
{
|
||||
if (setCarry)
|
||||
{
|
||||
SetCarryMMsb(context, m);
|
||||
}
|
||||
|
||||
return Const(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (setCarry)
|
||||
{
|
||||
SetCarryMShrOut(context, m, shift);
|
||||
}
|
||||
|
||||
return context.ShiftRightUI(m, Const(shift));
|
||||
}
|
||||
}
|
||||
|
||||
private static Operand GetShiftByMoreThan32(ArmEmitterContext context, bool setCarry)
|
||||
{
|
||||
if (setCarry)
|
||||
{
|
||||
SetFlag(context, PState.CFlag, Const(0));;
|
||||
}
|
||||
|
||||
return Const(0);
|
||||
}
|
||||
|
||||
private static Operand GetAsrC(ArmEmitterContext context, Operand m, bool setCarry, int shift)
|
||||
{
|
||||
if ((uint)shift >= 32)
|
||||
{
|
||||
m = context.ShiftRightSI(m, Const(31));
|
||||
|
||||
if (setCarry)
|
||||
{
|
||||
SetCarryMLsb(context, m);
|
||||
}
|
||||
|
||||
return m;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (setCarry)
|
||||
{
|
||||
SetCarryMShrOut(context, m, shift);
|
||||
}
|
||||
|
||||
return context.ShiftRightSI(m, Const(shift));
|
||||
}
|
||||
}
|
||||
|
||||
private static Operand GetRorC(ArmEmitterContext context, Operand m, bool setCarry, int shift)
|
||||
{
|
||||
shift &= 0x1f;
|
||||
|
||||
m = context.RotateRight(m, Const(shift));
|
||||
|
||||
if (setCarry)
|
||||
{
|
||||
SetCarryMMsb(context, m);
|
||||
}
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
private static Operand GetRrxC(ArmEmitterContext context, Operand m, bool setCarry)
|
||||
{
|
||||
// Rotate right by 1 with carry.
|
||||
Operand cIn = context.Copy(GetFlag(PState.CFlag));
|
||||
|
||||
if (setCarry)
|
||||
{
|
||||
SetCarryMLsb(context, m);
|
||||
}
|
||||
|
||||
m = context.ShiftRightUI(m, Const(1));
|
||||
|
||||
m = context.BitwiseOr(m, context.ShiftLeft(cIn, Const(31)));
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
private static void SetCarryMLsb(ArmEmitterContext context, Operand m)
|
||||
{
|
||||
SetFlag(context, PState.CFlag, context.BitwiseAnd(m, Const(1)));
|
||||
}
|
||||
|
||||
private static void SetCarryMMsb(ArmEmitterContext context, Operand m)
|
||||
{
|
||||
SetFlag(context, PState.CFlag, context.ShiftRightUI(m, Const(31)));
|
||||
}
|
||||
|
||||
private static void SetCarryMShrOut(ArmEmitterContext context, Operand m, int shift)
|
||||
{
|
||||
Operand cOut = context.ShiftRightUI(m, Const(shift - 1));
|
||||
|
||||
cOut = context.BitwiseAnd(cOut, Const(1));
|
||||
|
||||
SetFlag(context, PState.CFlag, cOut);
|
||||
}
|
||||
}
|
||||
}
|
196
ARMeilleure/Instructions/InstEmitBfm.cs
Normal file
196
ARMeilleure/Instructions/InstEmitBfm.cs
Normal file
|
@ -0,0 +1,196 @@
|
|||
using ARMeilleure.Decoders;
|
||||
using ARMeilleure.IntermediateRepresentation;
|
||||
using ARMeilleure.Translation;
|
||||
|
||||
using static ARMeilleure.Instructions.InstEmitHelper;
|
||||
using static ARMeilleure.IntermediateRepresentation.OperandHelper;
|
||||
|
||||
namespace ARMeilleure.Instructions
|
||||
{
|
||||
static partial class InstEmit
|
||||
{
|
||||
public static void Bfm(ArmEmitterContext context)
|
||||
{
|
||||
OpCodeBfm op = (OpCodeBfm)context.CurrOp;
|
||||
|
||||
Operand d = GetIntOrZR(context, op.Rd);
|
||||
Operand n = GetIntOrZR(context, op.Rn);
|
||||
|
||||
Operand res;
|
||||
|
||||
if (op.Pos < op.Shift)
|
||||
{
|
||||
// BFI.
|
||||
int shift = op.GetBitsCount() - op.Shift;
|
||||
|
||||
int width = op.Pos + 1;
|
||||
|
||||
long mask = (long)(ulong.MaxValue >> (64 - width));
|
||||
|
||||
res = context.ShiftLeft(context.BitwiseAnd(n, Const(n.Type, mask)), Const(shift));
|
||||
|
||||
res = context.BitwiseOr(res, context.BitwiseAnd(d, Const(d.Type, ~(mask << shift))));
|
||||
}
|
||||
else
|
||||
{
|
||||
// BFXIL.
|
||||
int shift = op.Shift;
|
||||
|
||||
int width = op.Pos - shift + 1;
|
||||
|
||||
long mask = (long)(ulong.MaxValue >> (64 - width));
|
||||
|
||||
res = context.BitwiseAnd(context.ShiftRightUI(n, Const(shift)), Const(n.Type, mask));
|
||||
|
||||
res = context.BitwiseOr(res, context.BitwiseAnd(d, Const(d.Type, ~mask)));
|
||||
}
|
||||
|
||||
SetIntOrZR(context, op.Rd, res);
|
||||
}
|
||||
|
||||
public static void Sbfm(ArmEmitterContext context)
|
||||
{
|
||||
OpCodeBfm op = (OpCodeBfm)context.CurrOp;
|
||||
|
||||
int bitsCount = op.GetBitsCount();
|
||||
|
||||
if (op.Pos + 1 == bitsCount)
|
||||
{
|
||||
EmitSbfmShift(context);
|
||||
}
|
||||
else if (op.Pos < op.Shift)
|
||||
{
|
||||
EmitSbfiz(context);
|
||||
}
|
||||
else if (op.Pos == 7 && op.Shift == 0)
|
||||
{
|
||||
Operand n = GetIntOrZR(context, op.Rn);
|
||||
|
||||
SetIntOrZR(context, op.Rd, context.SignExtend8(n.Type, n));
|
||||
}
|
||||
else if (op.Pos == 15 && op.Shift == 0)
|
||||
{
|
||||
Operand n = GetIntOrZR(context, op.Rn);
|
||||
|
||||
SetIntOrZR(context, op.Rd, context.SignExtend16(n.Type, n));
|
||||
}
|
||||
else if (op.Pos == 31 && op.Shift == 0)
|
||||
{
|
||||
Operand n = GetIntOrZR(context, op.Rn);
|
||||
|
||||
SetIntOrZR(context, op.Rd, context.SignExtend32(n.Type, n));
|
||||
}
|
||||
else
|
||||
{
|
||||
Operand res = GetIntOrZR(context, op.Rn);
|
||||
|
||||
res = context.ShiftLeft (res, Const(bitsCount - 1 - op.Pos));
|
||||
res = context.ShiftRightSI(res, Const(bitsCount - 1));
|
||||
res = context.BitwiseAnd (res, Const(res.Type, ~op.TMask));
|
||||
|
||||
Operand n2 = GetBfmN(context);
|
||||
|
||||
SetIntOrZR(context, op.Rd, context.BitwiseOr(res, n2));
|
||||
}
|
||||
}
|
||||
|
||||
public static void Ubfm(ArmEmitterContext context)
|
||||
{
|
||||
OpCodeBfm op = (OpCodeBfm)context.CurrOp;
|
||||
|
||||
if (op.Pos + 1 == op.GetBitsCount())
|
||||
{
|
||||
EmitUbfmShift(context);
|
||||
}
|
||||
else if (op.Pos < op.Shift)
|
||||
{
|
||||
EmitUbfiz(context);
|
||||
}
|
||||
else if (op.Pos + 1 == op.Shift)
|
||||
{
|
||||
EmitBfmLsl(context);
|
||||
}
|
||||
else if (op.Pos == 7 && op.Shift == 0)
|
||||
{
|
||||
Operand n = GetIntOrZR(context, op.Rn);
|
||||
|
||||
SetIntOrZR(context, op.Rd, context.BitwiseAnd(n, Const(n.Type, 0xff)));
|
||||
}
|
||||
else if (op.Pos == 15 && op.Shift == 0)
|
||||
{
|
||||
Operand n = GetIntOrZR(context, op.Rn);
|
||||
|
||||
SetIntOrZR(context, op.Rd, context.BitwiseAnd(n, Const(n.Type, 0xffff)));
|
||||
}
|
||||
else
|
||||
{
|
||||
SetIntOrZR(context, op.Rd, GetBfmN(context));
|
||||
}
|
||||
}
|
||||
|
||||
private static void EmitSbfiz(ArmEmitterContext context) => EmitBfiz(context, signed: true);
|
||||
private static void EmitUbfiz(ArmEmitterContext context) => EmitBfiz(context, signed: false);
|
||||
|
||||
private static void EmitBfiz(ArmEmitterContext context, bool signed)
|
||||
{
|
||||
OpCodeBfm op = (OpCodeBfm)context.CurrOp;
|
||||
|
||||
int width = op.Pos + 1;
|
||||
|
||||
Operand res = GetIntOrZR(context, op.Rn);
|
||||
|
||||
res = context.ShiftLeft(res, Const(op.GetBitsCount() - width));
|
||||
|
||||
res = signed
|
||||
? context.ShiftRightSI(res, Const(op.Shift - width))
|
||||
: context.ShiftRightUI(res, Const(op.Shift - width));
|
||||
|
||||
SetIntOrZR(context, op.Rd, res);
|
||||
}
|
||||
|
||||
private static void EmitSbfmShift(ArmEmitterContext context)
|
||||
{
|
||||
EmitBfmShift(context, signed: true);
|
||||
}
|
||||
|
||||
private static void EmitUbfmShift(ArmEmitterContext context)
|
||||
{
|
||||
EmitBfmShift(context, signed: false);
|
||||
}
|
||||
|
||||
private static void EmitBfmShift(ArmEmitterContext context, bool signed)
|
||||
{
|
||||
OpCodeBfm op = (OpCodeBfm)context.CurrOp;
|
||||
|
||||
Operand res = GetIntOrZR(context, op.Rn);
|
||||
|
||||
res = signed
|
||||
? context.ShiftRightSI(res, Const(op.Shift))
|
||||
: context.ShiftRightUI(res, Const(op.Shift));
|
||||
|
||||
SetIntOrZR(context, op.Rd, res);
|
||||
}
|
||||
|
||||
private static void EmitBfmLsl(ArmEmitterContext context)
|
||||
{
|
||||
OpCodeBfm op = (OpCodeBfm)context.CurrOp;
|
||||
|
||||
Operand res = GetIntOrZR(context, op.Rn);
|
||||
|
||||
int shift = op.GetBitsCount() - op.Shift;
|
||||
|
||||
SetIntOrZR(context, op.Rd, context.ShiftLeft(res, Const(shift)));
|
||||
}
|
||||
|
||||
private static Operand GetBfmN(ArmEmitterContext context)
|
||||
{
|
||||
OpCodeBfm op = (OpCodeBfm)context.CurrOp;
|
||||
|
||||
Operand res = GetIntOrZR(context, op.Rn);
|
||||
|
||||
long mask = op.WMask & op.TMask;
|
||||
|
||||
return context.BitwiseAnd(context.RotateRight(res, Const(op.Shift)), Const(res.Type, mask));
|
||||
}
|
||||
}
|
||||
}
|
61
ARMeilleure/Instructions/InstEmitCcmp.cs
Normal file
61
ARMeilleure/Instructions/InstEmitCcmp.cs
Normal file
|
@ -0,0 +1,61 @@
|
|||
using ARMeilleure.Decoders;
|
||||
using ARMeilleure.IntermediateRepresentation;
|
||||
using ARMeilleure.State;
|
||||
using ARMeilleure.Translation;
|
||||
|
||||
using static ARMeilleure.Instructions.InstEmitAluHelper;
|
||||
using static ARMeilleure.Instructions.InstEmitFlowHelper;
|
||||
using static ARMeilleure.Instructions.InstEmitHelper;
|
||||
using static ARMeilleure.IntermediateRepresentation.OperandHelper;
|
||||
|
||||
namespace ARMeilleure.Instructions
|
||||
{
|
||||
static partial class InstEmit
|
||||
{
|
||||
public static void Ccmn(ArmEmitterContext context) => EmitCcmp(context, isNegated: true);
|
||||
public static void Ccmp(ArmEmitterContext context) => EmitCcmp(context, isNegated: false);
|
||||
|
||||
private static void EmitCcmp(ArmEmitterContext context, bool isNegated)
|
||||
{
|
||||
OpCodeCcmp op = (OpCodeCcmp)context.CurrOp;
|
||||
|
||||
Operand lblTrue = Label();
|
||||
Operand lblEnd = Label();
|
||||
|
||||
EmitCondBranch(context, lblTrue, op.Cond);
|
||||
|
||||
SetFlag(context, PState.VFlag, Const((op.Nzcv >> 0) & 1));
|
||||
SetFlag(context, PState.CFlag, Const((op.Nzcv >> 1) & 1));
|
||||
SetFlag(context, PState.ZFlag, Const((op.Nzcv >> 2) & 1));
|
||||
SetFlag(context, PState.NFlag, Const((op.Nzcv >> 3) & 1));
|
||||
|
||||
context.Branch(lblEnd);
|
||||
|
||||
context.MarkLabel(lblTrue);
|
||||
|
||||
Operand n = GetAluN(context);
|
||||
Operand m = GetAluM(context);
|
||||
|
||||
if (isNegated)
|
||||
{
|
||||
Operand d = context.Add(n, m);
|
||||
|
||||
EmitNZFlagsCheck(context, d);
|
||||
|
||||
EmitAddsCCheck(context, n, d);
|
||||
EmitAddsVCheck(context, n, m, d);
|
||||
}
|
||||
else
|
||||
{
|
||||
Operand d = context.Subtract(n, m);
|
||||
|
||||
EmitNZFlagsCheck(context, d);
|
||||
|
||||
EmitSubsCCheck(context, n, m);
|
||||
EmitSubsVCheck(context, n, m, d);
|
||||
}
|
||||
|
||||
context.MarkLabel(lblEnd);
|
||||
}
|
||||
}
|
||||
}
|
53
ARMeilleure/Instructions/InstEmitCsel.cs
Normal file
53
ARMeilleure/Instructions/InstEmitCsel.cs
Normal file
|
@ -0,0 +1,53 @@
|
|||
using ARMeilleure.Decoders;
|
||||
using ARMeilleure.IntermediateRepresentation;
|
||||
using ARMeilleure.Translation;
|
||||
|
||||
using static ARMeilleure.Instructions.InstEmitFlowHelper;
|
||||
using static ARMeilleure.Instructions.InstEmitHelper;
|
||||
using static ARMeilleure.IntermediateRepresentation.OperandHelper;
|
||||
|
||||
namespace ARMeilleure.Instructions
|
||||
{
|
||||
static partial class InstEmit
|
||||
{
|
||||
private enum CselOperation
|
||||
{
|
||||
None,
|
||||
Increment,
|
||||
Invert,
|
||||
Negate
|
||||
}
|
||||
|
||||
public static void Csel(ArmEmitterContext context) => EmitCsel(context, CselOperation.None);
|
||||
public static void Csinc(ArmEmitterContext context) => EmitCsel(context, CselOperation.Increment);
|
||||
public static void Csinv(ArmEmitterContext context) => EmitCsel(context, CselOperation.Invert);
|
||||
public static void Csneg(ArmEmitterContext context) => EmitCsel(context, CselOperation.Negate);
|
||||
|
||||
private static void EmitCsel(ArmEmitterContext context, CselOperation cselOp)
|
||||
{
|
||||
OpCodeCsel op = (OpCodeCsel)context.CurrOp;
|
||||
|
||||
Operand n = GetIntOrZR(context, op.Rn);
|
||||
Operand m = GetIntOrZR(context, op.Rm);
|
||||
|
||||
if (cselOp == CselOperation.Increment)
|
||||
{
|
||||
m = context.Add(m, Const(m.Type, 1));
|
||||
}
|
||||
else if (cselOp == CselOperation.Invert)
|
||||
{
|
||||
m = context.BitwiseNot(m);
|
||||
}
|
||||
else if (cselOp == CselOperation.Negate)
|
||||
{
|
||||
m = context.Negate(m);
|
||||
}
|
||||
|
||||
Operand condTrue = GetCondTrue(context, op.Cond);
|
||||
|
||||
Operand d = context.ConditionalSelect(condTrue, n, m);
|
||||
|
||||
SetIntOrZR(context, op.Rd, d);
|
||||
}
|
||||
}
|
||||
}
|
67
ARMeilleure/Instructions/InstEmitDiv.cs
Normal file
67
ARMeilleure/Instructions/InstEmitDiv.cs
Normal file
|
@ -0,0 +1,67 @@
|
|||
using ARMeilleure.Decoders;
|
||||
using ARMeilleure.IntermediateRepresentation;
|
||||
using ARMeilleure.Translation;
|
||||
|
||||
using static ARMeilleure.Instructions.InstEmitHelper;
|
||||
using static ARMeilleure.IntermediateRepresentation.OperandHelper;
|
||||
|
||||
namespace ARMeilleure.Instructions
|
||||
{
|
||||
static partial class InstEmit
|
||||
{
|
||||
public static void Sdiv(ArmEmitterContext context) => EmitDiv(context, unsigned: false);
|
||||
public static void Udiv(ArmEmitterContext context) => EmitDiv(context, unsigned: true);
|
||||
|
||||
private static void EmitDiv(ArmEmitterContext context, bool unsigned)
|
||||
{
|
||||
OpCodeAluBinary op = (OpCodeAluBinary)context.CurrOp;
|
||||
|
||||
// If Rm == 0, Rd = 0 (division by zero).
|
||||
Operand n = GetIntOrZR(context, op.Rn);
|
||||
Operand m = GetIntOrZR(context, op.Rm);
|
||||
|
||||
Operand divisorIsZero = context.ICompareEqual(m, Const(m.Type, 0));
|
||||
|
||||
Operand lblBadDiv = Label();
|
||||
Operand lblEnd = Label();
|
||||
|
||||
context.BranchIfTrue(lblBadDiv, divisorIsZero);
|
||||
|
||||
if (!unsigned)
|
||||
{
|
||||
// If Rn == INT_MIN && Rm == -1, Rd = INT_MIN (overflow).
|
||||
bool is32Bits = op.RegisterSize == RegisterSize.Int32;
|
||||
|
||||
Operand intMin = is32Bits ? Const(int.MinValue) : Const(long.MinValue);
|
||||
Operand minus1 = is32Bits ? Const(-1) : Const(-1L);
|
||||
|
||||
Operand nIsIntMin = context.ICompareEqual(n, intMin);
|
||||
Operand mIsMinus1 = context.ICompareEqual(m, minus1);
|
||||
|
||||
Operand lblGoodDiv = Label();
|
||||
|
||||
context.BranchIfFalse(lblGoodDiv, context.BitwiseAnd(nIsIntMin, mIsMinus1));
|
||||
|
||||
SetAluDOrZR(context, intMin);
|
||||
|
||||
context.Branch(lblEnd);
|
||||
|
||||
context.MarkLabel(lblGoodDiv);
|
||||
}
|
||||
|
||||
Operand d = unsigned
|
||||
? context.DivideUI(n, m)
|
||||
: context.Divide (n, m);
|
||||
|
||||
SetAluDOrZR(context, d);
|
||||
|
||||
context.Branch(lblEnd);
|
||||
|
||||
context.MarkLabel(lblBadDiv);
|
||||
|
||||
SetAluDOrZR(context, Const(op.GetOperandType(), 0));
|
||||
|
||||
context.MarkLabel(lblEnd);
|
||||
}
|
||||
}
|
||||
}
|
55
ARMeilleure/Instructions/InstEmitException.cs
Normal file
55
ARMeilleure/Instructions/InstEmitException.cs
Normal file
|
@ -0,0 +1,55 @@
|
|||
using ARMeilleure.Decoders;
|
||||
using ARMeilleure.Translation;
|
||||
using System;
|
||||
|
||||
using static ARMeilleure.IntermediateRepresentation.OperandHelper;
|
||||
|
||||
namespace ARMeilleure.Instructions
|
||||
{
|
||||
static partial class InstEmit
|
||||
{
|
||||
public static void Brk(ArmEmitterContext context)
|
||||
{
|
||||
EmitExceptionCall(context, NativeInterface.Break);
|
||||
}
|
||||
|
||||
public static void Svc(ArmEmitterContext context)
|
||||
{
|
||||
EmitExceptionCall(context, NativeInterface.SupervisorCall);
|
||||
}
|
||||
|
||||
private static void EmitExceptionCall(ArmEmitterContext context, _Void_U64_S32 func)
|
||||
{
|
||||
OpCodeException op = (OpCodeException)context.CurrOp;
|
||||
|
||||
context.StoreToContext();
|
||||
|
||||
context.Call(func, Const(op.Address), Const(op.Id));
|
||||
|
||||
context.LoadFromContext();
|
||||
|
||||
if (context.CurrBlock.Next == null)
|
||||
{
|
||||
context.Return(Const(op.Address + 4));
|
||||
}
|
||||
}
|
||||
|
||||
public static void Und(ArmEmitterContext context)
|
||||
{
|
||||
OpCode op = context.CurrOp;
|
||||
|
||||
Delegate dlg = new _Void_U64_S32(NativeInterface.Undefined);
|
||||
|
||||
context.StoreToContext();
|
||||
|
||||
context.Call(dlg, Const(op.Address), Const(op.RawOpCode));
|
||||
|
||||
context.LoadFromContext();
|
||||
|
||||
if (context.CurrBlock.Next == null)
|
||||
{
|
||||
context.Return(Const(op.Address + 4));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
159
ARMeilleure/Instructions/InstEmitFlow.cs
Normal file
159
ARMeilleure/Instructions/InstEmitFlow.cs
Normal file
|
@ -0,0 +1,159 @@
|
|||
using ARMeilleure.Decoders;
|
||||
using ARMeilleure.IntermediateRepresentation;
|
||||
using ARMeilleure.State;
|
||||
using ARMeilleure.Translation;
|
||||
|
||||
using static ARMeilleure.Instructions.InstEmitFlowHelper;
|
||||
using static ARMeilleure.Instructions.InstEmitHelper;
|
||||
using static ARMeilleure.IntermediateRepresentation.OperandHelper;
|
||||
|
||||
namespace ARMeilleure.Instructions
|
||||
{
|
||||
static partial class InstEmit
|
||||
{
|
||||
public static void B(ArmEmitterContext context)
|
||||
{
|
||||
OpCodeBImmAl op = (OpCodeBImmAl)context.CurrOp;
|
||||
|
||||
if (context.CurrBlock.Branch != null)
|
||||
{
|
||||
context.Branch(context.GetLabel((ulong)op.Immediate));
|
||||
}
|
||||
else
|
||||
{
|
||||
context.Return(Const(op.Immediate));
|
||||
}
|
||||
}
|
||||
|
||||
public static void B_Cond(ArmEmitterContext context)
|
||||
{
|
||||
OpCodeBImmCond op = (OpCodeBImmCond)context.CurrOp;
|
||||
|
||||
EmitBranch(context, op.Cond);
|
||||
}
|
||||
|
||||
public static void Bl(ArmEmitterContext context)
|
||||
{
|
||||
OpCodeBImmAl op = (OpCodeBImmAl)context.CurrOp;
|
||||
|
||||
context.Copy(GetIntOrZR(context, RegisterAlias.Lr), Const(op.Address + 4));
|
||||
|
||||
EmitCall(context, (ulong)op.Immediate);
|
||||
}
|
||||
|
||||
public static void Blr(ArmEmitterContext context)
|
||||
{
|
||||
OpCodeBReg op = (OpCodeBReg)context.CurrOp;
|
||||
|
||||
Operand n = context.Copy(GetIntOrZR(context, op.Rn));
|
||||
|
||||
context.Copy(GetIntOrZR(context, RegisterAlias.Lr), Const(op.Address + 4));
|
||||
|
||||
EmitVirtualCall(context, n);
|
||||
}
|
||||
|
||||
public static void Br(ArmEmitterContext context)
|
||||
{
|
||||
OpCodeBReg op = (OpCodeBReg)context.CurrOp;
|
||||
|
||||
EmitVirtualJump(context, GetIntOrZR(context, op.Rn));
|
||||
}
|
||||
|
||||
public static void Cbnz(ArmEmitterContext context) => EmitCb(context, onNotZero: true);
|
||||
public static void Cbz(ArmEmitterContext context) => EmitCb(context, onNotZero: false);
|
||||
|
||||
private static void EmitCb(ArmEmitterContext context, bool onNotZero)
|
||||
{
|
||||
OpCodeBImmCmp op = (OpCodeBImmCmp)context.CurrOp;
|
||||
|
||||
EmitBranch(context, GetIntOrZR(context, op.Rt), onNotZero);
|
||||
}
|
||||
|
||||
public static void Ret(ArmEmitterContext context)
|
||||
{
|
||||
context.Return(context.BitwiseOr(GetIntOrZR(context, RegisterAlias.Lr), Const(CallFlag)));
|
||||
}
|
||||
|
||||
public static void Tbnz(ArmEmitterContext context) => EmitTb(context, onNotZero: true);
|
||||
public static void Tbz(ArmEmitterContext context) => EmitTb(context, onNotZero: false);
|
||||
|
||||
private static void EmitTb(ArmEmitterContext context, bool onNotZero)
|
||||
{
|
||||
OpCodeBImmTest op = (OpCodeBImmTest)context.CurrOp;
|
||||
|
||||
Operand value = context.BitwiseAnd(GetIntOrZR(context, op.Rt), Const(1L << op.Bit));
|
||||
|
||||
EmitBranch(context, value, onNotZero);
|
||||
}
|
||||
|
||||
private static void EmitBranch(ArmEmitterContext context, Condition cond)
|
||||
{
|
||||
OpCodeBImm op = (OpCodeBImm)context.CurrOp;
|
||||
|
||||
if (context.CurrBlock.Branch != null)
|
||||
{
|
||||
EmitCondBranch(context, context.GetLabel((ulong)op.Immediate), cond);
|
||||
|
||||
if (context.CurrBlock.Next == null)
|
||||
{
|
||||
context.Return(Const(op.Address + 4));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Operand lblTaken = Label();
|
||||
|
||||
EmitCondBranch(context, lblTaken, cond);
|
||||
|
||||
context.Return(Const(op.Address + 4));
|
||||
|
||||
context.MarkLabel(lblTaken);
|
||||
|
||||
context.Return(Const(op.Immediate));
|
||||
}
|
||||
}
|
||||
|
||||
private static void EmitBranch(ArmEmitterContext context, Operand value, bool onNotZero)
|
||||
{
|
||||
OpCodeBImm op = (OpCodeBImm)context.CurrOp;
|
||||
|
||||
if (context.CurrBlock.Branch != null)
|
||||
{
|
||||
Operand lblTarget = context.GetLabel((ulong)op.Immediate);
|
||||
|
||||
if (onNotZero)
|
||||
{
|
||||
context.BranchIfTrue(lblTarget, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
context.BranchIfFalse(lblTarget, value);
|
||||
}
|
||||
|
||||
if (context.CurrBlock.Next == null)
|
||||
{
|
||||
context.Return(Const(op.Address + 4));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Operand lblTaken = Label();
|
||||
|
||||
if (onNotZero)
|
||||
{
|
||||
context.BranchIfTrue(lblTaken, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
context.BranchIfFalse(lblTaken, value);
|
||||
}
|
||||
|
||||
context.Return(Const(op.Address + 4));
|
||||
|
||||
context.MarkLabel(lblTaken);
|
||||
|
||||
context.Return(Const(op.Immediate));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
71
ARMeilleure/Instructions/InstEmitFlow32.cs
Normal file
71
ARMeilleure/Instructions/InstEmitFlow32.cs
Normal file
|
@ -0,0 +1,71 @@
|
|||
using ARMeilleure.Decoders;
|
||||
using ARMeilleure.State;
|
||||
using ARMeilleure.Translation;
|
||||
|
||||
using static ARMeilleure.Instructions.InstEmitHelper;
|
||||
using static ARMeilleure.IntermediateRepresentation.OperandHelper;
|
||||
|
||||
namespace ARMeilleure.Instructions
|
||||
{
|
||||
static partial class InstEmit32
|
||||
{
|
||||
public static void B(ArmEmitterContext context)
|
||||
{
|
||||
IOpCode32BImm op = (IOpCode32BImm)context.CurrOp;
|
||||
|
||||
if (context.CurrBlock.Branch != null)
|
||||
{
|
||||
context.Branch(context.GetLabel((ulong)op.Immediate));
|
||||
}
|
||||
else
|
||||
{
|
||||
context.StoreToContext();
|
||||
|
||||
context.Return(Const(op.Immediate));
|
||||
}
|
||||
}
|
||||
|
||||
public static void Bl(ArmEmitterContext context)
|
||||
{
|
||||
Blx(context, x: false);
|
||||
}
|
||||
|
||||
public static void Blx(ArmEmitterContext context)
|
||||
{
|
||||
Blx(context, x: true);
|
||||
}
|
||||
|
||||
public static void Bx(ArmEmitterContext context)
|
||||
{
|
||||
IOpCode32BReg op = (IOpCode32BReg)context.CurrOp;
|
||||
|
||||
context.StoreToContext();
|
||||
|
||||
EmitBxWritePc(context, GetIntA32(context, op.Rm));
|
||||
}
|
||||
|
||||
private static void Blx(ArmEmitterContext context, bool x)
|
||||
{
|
||||
IOpCode32BImm op = (IOpCode32BImm)context.CurrOp;
|
||||
|
||||
uint pc = op.GetPc();
|
||||
|
||||
bool isThumb = IsThumb(context.CurrOp);
|
||||
|
||||
uint currentPc = isThumb
|
||||
? op.GetPc() | 1
|
||||
: op.GetPc() - 4;
|
||||
|
||||
SetIntOrSP(context, GetBankedRegisterAlias(context.Mode, RegisterAlias.Aarch32Lr), Const(currentPc));
|
||||
|
||||
// If x is true, then this is a branch with link and exchange.
|
||||
// In this case we need to swap the mode between Arm <-> Thumb.
|
||||
if (x)
|
||||
{
|
||||
SetFlag(context, PState.TFlag, Const(isThumb ? 0 : 1));
|
||||
}
|
||||
|
||||
InstEmitFlowHelper.EmitCall(context, (ulong)op.Immediate);
|
||||
}
|
||||
}
|
||||
}
|
192
ARMeilleure/Instructions/InstEmitFlowHelper.cs
Normal file
192
ARMeilleure/Instructions/InstEmitFlowHelper.cs
Normal file
|
@ -0,0 +1,192 @@
|
|||
using ARMeilleure.Decoders;
|
||||
using ARMeilleure.IntermediateRepresentation;
|
||||
using ARMeilleure.State;
|
||||
using ARMeilleure.Translation;
|
||||
|
||||
using static ARMeilleure.Instructions.InstEmitHelper;
|
||||
using static ARMeilleure.IntermediateRepresentation.OperandHelper;
|
||||
|
||||
namespace ARMeilleure.Instructions
|
||||
{
|
||||
static class InstEmitFlowHelper
|
||||
{
|
||||
public const ulong CallFlag = 1;
|
||||
|
||||
public static void EmitCondBranch(ArmEmitterContext context, Operand target, Condition cond)
|
||||
{
|
||||
if (cond != Condition.Al)
|
||||
{
|
||||
context.BranchIfTrue(target, GetCondTrue(context, cond));
|
||||
}
|
||||
else
|
||||
{
|
||||
context.Branch(target);
|
||||
}
|
||||
}
|
||||
|
||||
public static Operand GetCondTrue(ArmEmitterContext context, Condition condition)
|
||||
{
|
||||
Operand cmpResult = context.TryGetComparisonResult(condition);
|
||||
|
||||
if (cmpResult != null)
|
||||
{
|
||||
return cmpResult;
|
||||
}
|
||||
|
||||
Operand value = Const(1);
|
||||
|
||||
Operand Inverse(Operand val)
|
||||
{
|
||||
return context.BitwiseExclusiveOr(val, Const(1));
|
||||
}
|
||||
|
||||
switch (condition)
|
||||
{
|
||||
case Condition.Eq:
|
||||
value = GetFlag(PState.ZFlag);
|
||||
break;
|
||||
|
||||
case Condition.Ne:
|
||||
value = Inverse(GetFlag(PState.ZFlag));
|
||||
break;
|
||||
|
||||
case Condition.GeUn:
|
||||
value = GetFlag(PState.CFlag);
|
||||
break;
|
||||
|
||||
case Condition.LtUn:
|
||||
value = Inverse(GetFlag(PState.CFlag));
|
||||
break;
|
||||
|
||||
case Condition.Mi:
|
||||
value = GetFlag(PState.NFlag);
|
||||
break;
|
||||
|
||||
case Condition.Pl:
|
||||
value = Inverse(GetFlag(PState.NFlag));
|
||||
break;
|
||||
|
||||
case Condition.Vs:
|
||||
value = GetFlag(PState.VFlag);
|
||||
break;
|
||||
|
||||
case Condition.Vc:
|
||||
value = Inverse(GetFlag(PState.VFlag));
|
||||
break;
|
||||
|
||||
case Condition.GtUn:
|
||||
{
|
||||
Operand c = GetFlag(PState.CFlag);
|
||||
Operand z = GetFlag(PState.ZFlag);
|
||||
|
||||
value = context.BitwiseAnd(c, Inverse(z));
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case Condition.LeUn:
|
||||
{
|
||||
Operand c = GetFlag(PState.CFlag);
|
||||
Operand z = GetFlag(PState.ZFlag);
|
||||
|
||||
value = context.BitwiseOr(Inverse(c), z);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case Condition.Ge:
|
||||
{
|
||||
Operand n = GetFlag(PState.NFlag);
|
||||
Operand v = GetFlag(PState.VFlag);
|
||||
|
||||
value = context.ICompareEqual(n, v);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case Condition.Lt:
|
||||
{
|
||||
Operand n = GetFlag(PState.NFlag);
|
||||
Operand v = GetFlag(PState.VFlag);
|
||||
|
||||
value = context.ICompareNotEqual(n, v);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case Condition.Gt:
|
||||
{
|
||||
Operand n = GetFlag(PState.NFlag);
|
||||
Operand z = GetFlag(PState.ZFlag);
|
||||
Operand v = GetFlag(PState.VFlag);
|
||||
|
||||
value = context.BitwiseAnd(Inverse(z), context.ICompareEqual(n, v));
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case Condition.Le:
|
||||
{
|
||||
Operand n = GetFlag(PState.NFlag);
|
||||
Operand z = GetFlag(PState.ZFlag);
|
||||
Operand v = GetFlag(PState.VFlag);
|
||||
|
||||
value = context.BitwiseOr(z, context.ICompareNotEqual(n, v));
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
public static void EmitCall(ArmEmitterContext context, ulong immediate)
|
||||
{
|
||||
context.Return(Const(immediate | CallFlag));
|
||||
}
|
||||
|
||||
public static void EmitVirtualCall(ArmEmitterContext context, Operand target)
|
||||
{
|
||||
EmitVirtualCallOrJump(context, target, isJump: false);
|
||||
}
|
||||
|
||||
public static void EmitVirtualJump(ArmEmitterContext context, Operand target)
|
||||
{
|
||||
EmitVirtualCallOrJump(context, target, isJump: true);
|
||||
}
|
||||
|
||||
private static void EmitVirtualCallOrJump(ArmEmitterContext context, Operand target, bool isJump)
|
||||
{
|
||||
context.Return(context.BitwiseOr(target, Const(target.Type, (long)CallFlag)));
|
||||
}
|
||||
|
||||
private static void EmitContinueOrReturnCheck(ArmEmitterContext context, Operand retVal)
|
||||
{
|
||||
// Note: The return value of the called method will be placed
|
||||
// at the Stack, the return value is always a Int64 with the
|
||||
// return address of the function. We check if the address is
|
||||
// correct, if it isn't we keep returning until we reach the dispatcher.
|
||||
ulong nextAddr = GetNextOpAddress(context.CurrOp);
|
||||
|
||||
if (context.CurrBlock.Next != null)
|
||||
{
|
||||
Operand lblContinue = Label();
|
||||
|
||||
context.BranchIfTrue(lblContinue, context.ICompareEqual(retVal, Const(nextAddr)));
|
||||
|
||||
context.Return(Const(nextAddr));
|
||||
|
||||
context.MarkLabel(lblContinue);
|
||||
}
|
||||
else
|
||||
{
|
||||
context.Return(Const(nextAddr));
|
||||
}
|
||||
}
|
||||
|
||||
private static ulong GetNextOpAddress(OpCode op)
|
||||
{
|
||||
return op.Address + (ulong)op.OpCodeSizeInBytes;
|
||||
}
|
||||
}
|
||||
}
|
64
ARMeilleure/Instructions/InstEmitHash.cs
Normal file
64
ARMeilleure/Instructions/InstEmitHash.cs
Normal file
|
@ -0,0 +1,64 @@
|
|||
using ARMeilleure.Decoders;
|
||||
using ARMeilleure.IntermediateRepresentation;
|
||||
using ARMeilleure.Translation;
|
||||
using System;
|
||||
|
||||
using static ARMeilleure.Instructions.InstEmitHelper;
|
||||
|
||||
namespace ARMeilleure.Instructions
|
||||
{
|
||||
static partial class InstEmit
|
||||
{
|
||||
public static void Crc32b(ArmEmitterContext context)
|
||||
{
|
||||
EmitCrc32Call(context, new _U32_U32_U8(SoftFallback.Crc32b));
|
||||
}
|
||||
|
||||
public static void Crc32h(ArmEmitterContext context)
|
||||
{
|
||||
EmitCrc32Call(context, new _U32_U32_U16(SoftFallback.Crc32h));
|
||||
}
|
||||
|
||||
public static void Crc32w(ArmEmitterContext context)
|
||||
{
|
||||
EmitCrc32Call(context, new _U32_U32_U32(SoftFallback.Crc32w));
|
||||
}
|
||||
|
||||
public static void Crc32x(ArmEmitterContext context)
|
||||
{
|
||||
EmitCrc32Call(context, new _U32_U32_U64(SoftFallback.Crc32x));
|
||||
}
|
||||
|
||||
public static void Crc32cb(ArmEmitterContext context)
|
||||
{
|
||||
EmitCrc32Call(context, new _U32_U32_U8(SoftFallback.Crc32cb));
|
||||
}
|
||||
|
||||
public static void Crc32ch(ArmEmitterContext context)
|
||||
{
|
||||
EmitCrc32Call(context, new _U32_U32_U16(SoftFallback.Crc32ch));
|
||||
}
|
||||
|
||||
public static void Crc32cw(ArmEmitterContext context)
|
||||
{
|
||||
EmitCrc32Call(context, new _U32_U32_U32(SoftFallback.Crc32cw));
|
||||
}
|
||||
|
||||
public static void Crc32cx(ArmEmitterContext context)
|
||||
{
|
||||
EmitCrc32Call(context, new _U32_U32_U64(SoftFallback.Crc32cx));
|
||||
}
|
||||
|
||||
private static void EmitCrc32Call(ArmEmitterContext context, Delegate dlg)
|
||||
{
|
||||
OpCodeAluBinary op = (OpCodeAluBinary)context.CurrOp;
|
||||
|
||||
Operand n = GetIntOrZR(context, op.Rn);
|
||||
Operand m = GetIntOrZR(context, op.Rm);
|
||||
|
||||
Operand d = context.Call(dlg, n, m);
|
||||
|
||||
SetIntOrZR(context, op.Rd, d);
|
||||
}
|
||||
}
|
||||
}
|
218
ARMeilleure/Instructions/InstEmitHelper.cs
Normal file
218
ARMeilleure/Instructions/InstEmitHelper.cs
Normal file
|
@ -0,0 +1,218 @@
|
|||
using ARMeilleure.Decoders;
|
||||
using ARMeilleure.IntermediateRepresentation;
|
||||
using ARMeilleure.State;
|
||||
using ARMeilleure.Translation;
|
||||
using System;
|
||||
|
||||
using static ARMeilleure.IntermediateRepresentation.OperandHelper;
|
||||
|
||||
namespace ARMeilleure.Instructions
|
||||
{
|
||||
static class InstEmitHelper
|
||||
{
|
||||
public static bool IsThumb(OpCode op)
|
||||
{
|
||||
return op is OpCodeT16;
|
||||
}
|
||||
|
||||
public static Operand GetExtendedM(ArmEmitterContext context, int rm, IntType type)
|
||||
{
|
||||
Operand value = GetIntOrZR(context, rm);
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case IntType.UInt8: value = context.ZeroExtend8 (value.Type, value); break;
|
||||
case IntType.UInt16: value = context.ZeroExtend16(value.Type, value); break;
|
||||
case IntType.UInt32: value = context.ZeroExtend32(value.Type, value); break;
|
||||
|
||||
case IntType.Int8: value = context.SignExtend8 (value.Type, value); break;
|
||||
case IntType.Int16: value = context.SignExtend16(value.Type, value); break;
|
||||
case IntType.Int32: value = context.SignExtend32(value.Type, value); break;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
public static Operand GetIntA32(ArmEmitterContext context, int regIndex)
|
||||
{
|
||||
if (regIndex == RegisterAlias.Aarch32Pc)
|
||||
{
|
||||
OpCode32 op = (OpCode32)context.CurrOp;
|
||||
|
||||
return Const((int)op.GetPc());
|
||||
}
|
||||
else
|
||||
{
|
||||
return GetIntOrSP(context, GetRegisterAlias(context.Mode, regIndex));
|
||||
}
|
||||
}
|
||||
|
||||
public static void SetIntA32(ArmEmitterContext context, int regIndex, Operand value)
|
||||
{
|
||||
if (regIndex == RegisterAlias.Aarch32Pc)
|
||||
{
|
||||
context.StoreToContext();
|
||||
|
||||
EmitBxWritePc(context, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetIntOrSP(context, GetRegisterAlias(context.Mode, regIndex), value);
|
||||
}
|
||||
}
|
||||
|
||||
public static int GetRegisterAlias(Aarch32Mode mode, int regIndex)
|
||||
{
|
||||
// Only registers >= 8 are banked,
|
||||
// with registers in the range [8, 12] being
|
||||
// banked for the FIQ mode, and registers
|
||||
// 13 and 14 being banked for all modes.
|
||||
if ((uint)regIndex < 8)
|
||||
{
|
||||
return regIndex;
|
||||
}
|
||||
|
||||
return GetBankedRegisterAlias(mode, regIndex);
|
||||
}
|
||||
|
||||
public static int GetBankedRegisterAlias(Aarch32Mode mode, int regIndex)
|
||||
{
|
||||
switch (regIndex)
|
||||
{
|
||||
case 8: return mode == Aarch32Mode.Fiq
|
||||
? RegisterAlias.R8Fiq
|
||||
: RegisterAlias.R8Usr;
|
||||
|
||||
case 9: return mode == Aarch32Mode.Fiq
|
||||
? RegisterAlias.R9Fiq
|
||||
: RegisterAlias.R9Usr;
|
||||
|
||||
case 10: return mode == Aarch32Mode.Fiq
|
||||
? RegisterAlias.R10Fiq
|
||||
: RegisterAlias.R10Usr;
|
||||
|
||||
case 11: return mode == Aarch32Mode.Fiq
|
||||
? RegisterAlias.R11Fiq
|
||||
: RegisterAlias.R11Usr;
|
||||
|
||||
case 12: return mode == Aarch32Mode.Fiq
|
||||
? RegisterAlias.R12Fiq
|
||||
: RegisterAlias.R12Usr;
|
||||
|
||||
case 13:
|
||||
switch (mode)
|
||||
{
|
||||
case Aarch32Mode.User:
|
||||
case Aarch32Mode.System: return RegisterAlias.SpUsr;
|
||||
case Aarch32Mode.Fiq: return RegisterAlias.SpFiq;
|
||||
case Aarch32Mode.Irq: return RegisterAlias.SpIrq;
|
||||
case Aarch32Mode.Supervisor: return RegisterAlias.SpSvc;
|
||||
case Aarch32Mode.Abort: return RegisterAlias.SpAbt;
|
||||
case Aarch32Mode.Hypervisor: return RegisterAlias.SpHyp;
|
||||
case Aarch32Mode.Undefined: return RegisterAlias.SpUnd;
|
||||
|
||||
default: throw new ArgumentException(nameof(mode));
|
||||
}
|
||||
|
||||
case 14:
|
||||
switch (mode)
|
||||
{
|
||||
case Aarch32Mode.User:
|
||||
case Aarch32Mode.Hypervisor:
|
||||
case Aarch32Mode.System: return RegisterAlias.LrUsr;
|
||||
case Aarch32Mode.Fiq: return RegisterAlias.LrFiq;
|
||||
case Aarch32Mode.Irq: return RegisterAlias.LrIrq;
|
||||
case Aarch32Mode.Supervisor: return RegisterAlias.LrSvc;
|
||||
case Aarch32Mode.Abort: return RegisterAlias.LrAbt;
|
||||
case Aarch32Mode.Undefined: return RegisterAlias.LrUnd;
|
||||
|
||||
default: throw new ArgumentException(nameof(mode));
|
||||
}
|
||||
|
||||
default: throw new ArgumentOutOfRangeException(nameof(regIndex));
|
||||
}
|
||||
}
|
||||
|
||||
public static void EmitBxWritePc(ArmEmitterContext context, Operand pc)
|
||||
{
|
||||
Operand mode = context.BitwiseAnd(pc, Const(1));
|
||||
|
||||
SetFlag(context, PState.TFlag, mode);
|
||||
|
||||
Operand lblArmMode = Label();
|
||||
|
||||
context.BranchIfTrue(lblArmMode, mode);
|
||||
|
||||
context.Return(context.ZeroExtend32(OperandType.I64, context.BitwiseAnd(pc, Const(~1))));
|
||||
|
||||
context.MarkLabel(lblArmMode);
|
||||
|
||||
context.Return(context.ZeroExtend32(OperandType.I64, context.BitwiseAnd(pc, Const(~3))));
|
||||
}
|
||||
|
||||
public static Operand GetIntOrZR(ArmEmitterContext context, int regIndex)
|
||||
{
|
||||
if (regIndex == RegisterConsts.ZeroIndex)
|
||||
{
|
||||
OperandType type = context.CurrOp.GetOperandType();
|
||||
|
||||
return type == OperandType.I32 ? Const(0) : Const(0L);
|
||||
}
|
||||
else
|
||||
{
|
||||
return GetIntOrSP(context, regIndex);
|
||||
}
|
||||
}
|
||||
|
||||
public static void SetIntOrZR(ArmEmitterContext context, int regIndex, Operand value)
|
||||
{
|
||||
if (regIndex == RegisterConsts.ZeroIndex)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
SetIntOrSP(context, regIndex, value);
|
||||
}
|
||||
|
||||
public static Operand GetIntOrSP(ArmEmitterContext context, int regIndex)
|
||||
{
|
||||
Operand value = Register(regIndex, RegisterType.Integer, OperandType.I64);
|
||||
|
||||
if (context.CurrOp.RegisterSize == RegisterSize.Int32)
|
||||
{
|
||||
value = context.ConvertI64ToI32(value);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
public static void SetIntOrSP(ArmEmitterContext context, int regIndex, Operand value)
|
||||
{
|
||||
Operand reg = Register(regIndex, RegisterType.Integer, OperandType.I64);
|
||||
|
||||
if (value.Type == OperandType.I32)
|
||||
{
|
||||
value = context.ZeroExtend32(OperandType.I64, value);
|
||||
}
|
||||
|
||||
context.Copy(reg, value);
|
||||
}
|
||||
|
||||
public static Operand GetVec(int regIndex)
|
||||
{
|
||||
return Register(regIndex, RegisterType.Vector, OperandType.V128);
|
||||
}
|
||||
|
||||
public static Operand GetFlag(PState stateFlag)
|
||||
{
|
||||
return Register((int)stateFlag, RegisterType.Flag, OperandType.I32);
|
||||
}
|
||||
|
||||
public static void SetFlag(ArmEmitterContext context, PState stateFlag, Operand value)
|
||||
{
|
||||
context.Copy(GetFlag(stateFlag), value);
|
||||
|
||||
context.MarkFlagSet(stateFlag);
|
||||
}
|
||||
}
|
||||
}
|
177
ARMeilleure/Instructions/InstEmitMemory.cs
Normal file
177
ARMeilleure/Instructions/InstEmitMemory.cs
Normal file
|
@ -0,0 +1,177 @@
|
|||
using ARMeilleure.Decoders;
|
||||
using ARMeilleure.IntermediateRepresentation;
|
||||
using ARMeilleure.Translation;
|
||||
|
||||
using static ARMeilleure.Instructions.InstEmitHelper;
|
||||
using static ARMeilleure.Instructions.InstEmitMemoryHelper;
|
||||
using static ARMeilleure.IntermediateRepresentation.OperandHelper;
|
||||
|
||||
namespace ARMeilleure.Instructions
|
||||
{
|
||||
static partial class InstEmit
|
||||
{
|
||||
public static void Adr(ArmEmitterContext context)
|
||||
{
|
||||
OpCodeAdr op = (OpCodeAdr)context.CurrOp;
|
||||
|
||||
SetIntOrZR(context, op.Rd, Const(op.Address + (ulong)op.Immediate));
|
||||
}
|
||||
|
||||
public static void Adrp(ArmEmitterContext context)
|
||||
{
|
||||
OpCodeAdr op = (OpCodeAdr)context.CurrOp;
|
||||
|
||||
ulong address = (op.Address & ~0xfffUL) + ((ulong)op.Immediate << 12);
|
||||
|
||||
SetIntOrZR(context, op.Rd, Const(address));
|
||||
}
|
||||
|
||||
public static void Ldr(ArmEmitterContext context) => EmitLdr(context, signed: false);
|
||||
public static void Ldrs(ArmEmitterContext context) => EmitLdr(context, signed: true);
|
||||
|
||||
private static void EmitLdr(ArmEmitterContext context, bool signed)
|
||||
{
|
||||
OpCodeMem op = (OpCodeMem)context.CurrOp;
|
||||
|
||||
Operand address = GetAddress(context);
|
||||
|
||||
if (signed && op.Extend64)
|
||||
{
|
||||
EmitLoadSx64(context, address, op.Rt, op.Size);
|
||||
}
|
||||
else if (signed)
|
||||
{
|
||||
EmitLoadSx32(context, address, op.Rt, op.Size);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitLoadZx(context, address, op.Rt, op.Size);
|
||||
}
|
||||
|
||||
EmitWBackIfNeeded(context, address);
|
||||
}
|
||||
|
||||
public static void Ldr_Literal(ArmEmitterContext context)
|
||||
{
|
||||
IOpCodeLit op = (IOpCodeLit)context.CurrOp;
|
||||
|
||||
if (op.Prefetch)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (op.Signed)
|
||||
{
|
||||
EmitLoadSx64(context, Const(op.Immediate), op.Rt, op.Size);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitLoadZx(context, Const(op.Immediate), op.Rt, op.Size);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Ldp(ArmEmitterContext context)
|
||||
{
|
||||
OpCodeMemPair op = (OpCodeMemPair)context.CurrOp;
|
||||
|
||||
void EmitLoad(int rt, Operand ldAddr)
|
||||
{
|
||||
if (op.Extend64)
|
||||
{
|
||||
EmitLoadSx64(context, ldAddr, rt, op.Size);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitLoadZx(context, ldAddr, rt, op.Size);
|
||||
}
|
||||
}
|
||||
|
||||
Operand address = GetAddress(context);
|
||||
|
||||
Operand address2 = context.Add(address, Const(1L << op.Size));
|
||||
|
||||
EmitLoad(op.Rt, address);
|
||||
EmitLoad(op.Rt2, address2);
|
||||
|
||||
EmitWBackIfNeeded(context, address);
|
||||
}
|
||||
|
||||
public static void Str(ArmEmitterContext context)
|
||||
{
|
||||
OpCodeMem op = (OpCodeMem)context.CurrOp;
|
||||
|
||||
Operand address = GetAddress(context);
|
||||
|
||||
InstEmitMemoryHelper.EmitStore(context, address, op.Rt, op.Size);
|
||||
|
||||
EmitWBackIfNeeded(context, address);
|
||||
}
|
||||
|
||||
public static void Stp(ArmEmitterContext context)
|
||||
{
|
||||
OpCodeMemPair op = (OpCodeMemPair)context.CurrOp;
|
||||
|
||||
Operand address = GetAddress(context);
|
||||
|
||||
Operand address2 = context.Add(address, Const(1L << op.Size));
|
||||
|
||||
InstEmitMemoryHelper.EmitStore(context, address, op.Rt, op.Size);
|
||||
InstEmitMemoryHelper.EmitStore(context, address2, op.Rt2, op.Size);
|
||||
|
||||
EmitWBackIfNeeded(context, address);
|
||||
}
|
||||
|
||||
private static Operand GetAddress(ArmEmitterContext context)
|
||||
{
|
||||
Operand address = null;
|
||||
|
||||
switch (context.CurrOp)
|
||||
{
|
||||
case OpCodeMemImm op:
|
||||
{
|
||||
address = context.Copy(GetIntOrSP(context, op.Rn));
|
||||
|
||||
// Pre-indexing.
|
||||
if (!op.PostIdx)
|
||||
{
|
||||
address = context.Add(address, Const(op.Immediate));
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case OpCodeMemReg op:
|
||||
{
|
||||
Operand n = GetIntOrSP(context, op.Rn);
|
||||
|
||||
Operand m = GetExtendedM(context, op.Rm, op.IntType);
|
||||
|
||||
if (op.Shift)
|
||||
{
|
||||
m = context.ShiftLeft(m, Const(op.Size));
|
||||
}
|
||||
|
||||
address = context.Add(n, m);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return address;
|
||||
}
|
||||
|
||||
private static void EmitWBackIfNeeded(ArmEmitterContext context, Operand address)
|
||||
{
|
||||
// Check whenever the current OpCode has post-indexed write back, if so write it.
|
||||
if (context.CurrOp is OpCodeMemImm op && op.WBack)
|
||||
{
|
||||
if (op.PostIdx)
|
||||
{
|
||||
address = context.Add(address, Const(op.Immediate));
|
||||
}
|
||||
|
||||
SetIntOrSP(context, op.Rn, address);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
256
ARMeilleure/Instructions/InstEmitMemory32.cs
Normal file
256
ARMeilleure/Instructions/InstEmitMemory32.cs
Normal file
|
@ -0,0 +1,256 @@
|
|||
using ARMeilleure.Decoders;
|
||||
using ARMeilleure.IntermediateRepresentation;
|
||||
using ARMeilleure.State;
|
||||
using ARMeilleure.Translation;
|
||||
using System;
|
||||
|
||||
using static ARMeilleure.Instructions.InstEmitHelper;
|
||||
using static ARMeilleure.Instructions.InstEmitMemoryHelper;
|
||||
using static ARMeilleure.IntermediateRepresentation.OperandHelper;
|
||||
|
||||
namespace ARMeilleure.Instructions
|
||||
{
|
||||
static partial class InstEmit32
|
||||
{
|
||||
private const int ByteSizeLog2 = 0;
|
||||
private const int HWordSizeLog2 = 1;
|
||||
private const int WordSizeLog2 = 2;
|
||||
private const int DWordSizeLog2 = 3;
|
||||
|
||||
[Flags]
|
||||
enum AccessType
|
||||
{
|
||||
Store = 0,
|
||||
Signed = 1,
|
||||
Load = 2,
|
||||
|
||||
LoadZx = Load,
|
||||
LoadSx = Load | Signed,
|
||||
}
|
||||
|
||||
public static void Ldm(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32MemMult op = (OpCode32MemMult)context.CurrOp;
|
||||
|
||||
Operand n = GetIntA32(context, op.Rn);
|
||||
|
||||
Operand baseAddress = context.Add(n, Const(op.Offset));
|
||||
|
||||
bool writesToPc = (op.RegisterMask & (1 << RegisterAlias.Aarch32Pc)) != 0;
|
||||
|
||||
bool writeBack = op.PostOffset != 0 && (op.Rn != RegisterAlias.Aarch32Pc || !writesToPc);
|
||||
|
||||
if (writeBack)
|
||||
{
|
||||
SetIntA32(context, op.Rn, context.Add(n, Const(op.PostOffset)));
|
||||
}
|
||||
|
||||
int mask = op.RegisterMask;
|
||||
int offset = 0;
|
||||
|
||||
for (int register = 0; mask != 0; mask >>= 1, register++)
|
||||
{
|
||||
if ((mask & 1) != 0)
|
||||
{
|
||||
Operand address = context.Add(baseAddress, Const(offset));
|
||||
|
||||
EmitLoadZx(context, address, register, WordSizeLog2);
|
||||
|
||||
offset += 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void Ldr(ArmEmitterContext context)
|
||||
{
|
||||
EmitLoadOrStore(context, WordSizeLog2, AccessType.LoadZx);
|
||||
}
|
||||
|
||||
public static void Ldrb(ArmEmitterContext context)
|
||||
{
|
||||
EmitLoadOrStore(context, ByteSizeLog2, AccessType.LoadZx);
|
||||
}
|
||||
|
||||
public static void Ldrd(ArmEmitterContext context)
|
||||
{
|
||||
EmitLoadOrStore(context, DWordSizeLog2, AccessType.LoadZx);
|
||||
}
|
||||
|
||||
public static void Ldrh(ArmEmitterContext context)
|
||||
{
|
||||
EmitLoadOrStore(context, HWordSizeLog2, AccessType.LoadZx);
|
||||
}
|
||||
|
||||
public static void Ldrsb(ArmEmitterContext context)
|
||||
{
|
||||
EmitLoadOrStore(context, ByteSizeLog2, AccessType.LoadSx);
|
||||
}
|
||||
|
||||
public static void Ldrsh(ArmEmitterContext context)
|
||||
{
|
||||
EmitLoadOrStore(context, HWordSizeLog2, AccessType.LoadSx);
|
||||
}
|
||||
|
||||
public static void Stm(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32MemMult op = (OpCode32MemMult)context.CurrOp;
|
||||
|
||||
Operand n = GetIntA32(context, op.Rn);
|
||||
|
||||
Operand baseAddress = context.Add(n, Const(op.Offset));
|
||||
|
||||
int mask = op.RegisterMask;
|
||||
int offset = 0;
|
||||
|
||||
for (int register = 0; mask != 0; mask >>= 1, register++)
|
||||
{
|
||||
if ((mask & 1) != 0)
|
||||
{
|
||||
Operand address = context.Add(baseAddress, Const(offset));
|
||||
|
||||
EmitStore(context, address, register, WordSizeLog2);
|
||||
|
||||
// Note: If Rn is also specified on the register list,
|
||||
// and Rn is the first register on this list, then the
|
||||
// value that is written to memory is the unmodified value,
|
||||
// before the write back. If it is on the list, but it's
|
||||
// not the first one, then the value written to memory
|
||||
// varies between CPUs.
|
||||
if (offset == 0 && op.PostOffset != 0)
|
||||
{
|
||||
// Emit write back after the first write.
|
||||
SetIntA32(context, op.Rn, context.Add(n, Const(op.PostOffset)));
|
||||
}
|
||||
|
||||
offset += 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void Str(ArmEmitterContext context)
|
||||
{
|
||||
EmitLoadOrStore(context, WordSizeLog2, AccessType.Store);
|
||||
}
|
||||
|
||||
public static void Strb(ArmEmitterContext context)
|
||||
{
|
||||
EmitLoadOrStore(context, ByteSizeLog2, AccessType.Store);
|
||||
}
|
||||
|
||||
public static void Strd(ArmEmitterContext context)
|
||||
{
|
||||
EmitLoadOrStore(context, DWordSizeLog2, AccessType.Store);
|
||||
}
|
||||
|
||||
public static void Strh(ArmEmitterContext context)
|
||||
{
|
||||
EmitLoadOrStore(context, HWordSizeLog2, AccessType.Store);
|
||||
}
|
||||
|
||||
private static void EmitLoadOrStore(ArmEmitterContext context, int size, AccessType accType)
|
||||
{
|
||||
OpCode32Mem op = (OpCode32Mem)context.CurrOp;
|
||||
|
||||
Operand n = context.Copy(GetIntA32(context, op.Rn));
|
||||
|
||||
Operand temp = null;
|
||||
|
||||
if (op.Index || op.WBack)
|
||||
{
|
||||
temp = op.Add
|
||||
? context.Add (n, Const(op.Immediate))
|
||||
: context.Subtract(n, Const(op.Immediate));
|
||||
}
|
||||
|
||||
if (op.WBack)
|
||||
{
|
||||
SetIntA32(context, op.Rn, temp);
|
||||
}
|
||||
|
||||
Operand address;
|
||||
|
||||
if (op.Index)
|
||||
{
|
||||
address = temp;
|
||||
}
|
||||
else
|
||||
{
|
||||
address = n;
|
||||
}
|
||||
|
||||
if ((accType & AccessType.Load) != 0)
|
||||
{
|
||||
void Load(int rt, int offs, int loadSize)
|
||||
{
|
||||
Operand addr = context.Add(address, Const(offs));
|
||||
|
||||
if ((accType & AccessType.Signed) != 0)
|
||||
{
|
||||
EmitLoadSx32(context, addr, rt, loadSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitLoadZx(context, addr, rt, loadSize);
|
||||
}
|
||||
}
|
||||
|
||||
if (size == DWordSizeLog2)
|
||||
{
|
||||
Operand lblBigEndian = Label();
|
||||
Operand lblEnd = Label();
|
||||
|
||||
context.BranchIfTrue(lblBigEndian, GetFlag(PState.EFlag));
|
||||
|
||||
Load(op.Rt, 0, WordSizeLog2);
|
||||
Load(op.Rt | 1, 4, WordSizeLog2);
|
||||
|
||||
context.Branch(lblEnd);
|
||||
|
||||
context.MarkLabel(lblBigEndian);
|
||||
|
||||
Load(op.Rt | 1, 0, WordSizeLog2);
|
||||
Load(op.Rt, 4, WordSizeLog2);
|
||||
|
||||
context.MarkLabel(lblEnd);
|
||||
}
|
||||
else
|
||||
{
|
||||
Load(op.Rt, 0, size);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
void Store(int rt, int offs, int storeSize)
|
||||
{
|
||||
Operand addr = context.Add(address, Const(offs));
|
||||
|
||||
EmitStore(context, addr, rt, storeSize);
|
||||
}
|
||||
|
||||
if (size == DWordSizeLog2)
|
||||
{
|
||||
Operand lblBigEndian = Label();
|
||||
Operand lblEnd = Label();
|
||||
|
||||
context.BranchIfTrue(lblBigEndian, GetFlag(PState.EFlag));
|
||||
|
||||
Store(op.Rt, 0, WordSizeLog2);
|
||||
Store(op.Rt | 1, 4, WordSizeLog2);
|
||||
|
||||
context.Branch(lblEnd);
|
||||
|
||||
context.MarkLabel(lblBigEndian);
|
||||
|
||||
Store(op.Rt | 1, 0, WordSizeLog2);
|
||||
Store(op.Rt, 4, WordSizeLog2);
|
||||
|
||||
context.MarkLabel(lblEnd);
|
||||
}
|
||||
else
|
||||
{
|
||||
Store(op.Rt, 0, size);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
261
ARMeilleure/Instructions/InstEmitMemoryEx.cs
Normal file
261
ARMeilleure/Instructions/InstEmitMemoryEx.cs
Normal file
|
@ -0,0 +1,261 @@
|
|||
using ARMeilleure.Decoders;
|
||||
using ARMeilleure.IntermediateRepresentation;
|
||||
using ARMeilleure.Translation;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
|
||||
using static ARMeilleure.Instructions.InstEmitHelper;
|
||||
using static ARMeilleure.IntermediateRepresentation.OperandHelper;
|
||||
|
||||
namespace ARMeilleure.Instructions
|
||||
{
|
||||
static partial class InstEmit
|
||||
{
|
||||
[Flags]
|
||||
private enum AccessType
|
||||
{
|
||||
None = 0,
|
||||
Ordered = 1,
|
||||
Exclusive = 2,
|
||||
OrderedEx = Ordered | Exclusive
|
||||
}
|
||||
|
||||
public static void Clrex(ArmEmitterContext context)
|
||||
{
|
||||
context.Call(new _Void(NativeInterface.ClearExclusive));
|
||||
}
|
||||
|
||||
public static void Dmb(ArmEmitterContext context) => EmitBarrier(context);
|
||||
public static void Dsb(ArmEmitterContext context) => EmitBarrier(context);
|
||||
|
||||
public static void Ldar(ArmEmitterContext context) => EmitLdr(context, AccessType.Ordered);
|
||||
public static void Ldaxr(ArmEmitterContext context) => EmitLdr(context, AccessType.OrderedEx);
|
||||
public static void Ldxr(ArmEmitterContext context) => EmitLdr(context, AccessType.Exclusive);
|
||||
public static void Ldxp(ArmEmitterContext context) => EmitLdp(context, AccessType.Exclusive);
|
||||
public static void Ldaxp(ArmEmitterContext context) => EmitLdp(context, AccessType.OrderedEx);
|
||||
|
||||
private static void EmitLdr(ArmEmitterContext context, AccessType accType)
|
||||
{
|
||||
EmitLoadEx(context, accType, pair: false);
|
||||
}
|
||||
|
||||
private static void EmitLdp(ArmEmitterContext context, AccessType accType)
|
||||
{
|
||||
EmitLoadEx(context, accType, pair: true);
|
||||
}
|
||||
|
||||
private static void EmitLoadEx(ArmEmitterContext context, AccessType accType, bool pair)
|
||||
{
|
||||
OpCodeMemEx op = (OpCodeMemEx)context.CurrOp;
|
||||
|
||||
bool ordered = (accType & AccessType.Ordered) != 0;
|
||||
bool exclusive = (accType & AccessType.Exclusive) != 0;
|
||||
|
||||
if (ordered)
|
||||
{
|
||||
EmitBarrier(context);
|
||||
}
|
||||
|
||||
Operand address = context.Copy(GetIntOrSP(context, op.Rn));
|
||||
|
||||
if (pair)
|
||||
{
|
||||
// Exclusive loads should be atomic. For pairwise loads, we need to
|
||||
// read all the data at once. For a 32-bits pairwise load, we do a
|
||||
// simple 64-bits load, for a 128-bits load, we need to call a special
|
||||
// method to read 128-bits atomically.
|
||||
if (op.Size == 2)
|
||||
{
|
||||
Operand value = EmitLoad(context, address, exclusive, 3);
|
||||
|
||||
Operand valueLow = context.ConvertI64ToI32(value);
|
||||
|
||||
valueLow = context.ZeroExtend32(OperandType.I64, valueLow);
|
||||
|
||||
Operand valueHigh = context.ShiftRightUI(value, Const(32));
|
||||
|
||||
SetIntOrZR(context, op.Rt, valueLow);
|
||||
SetIntOrZR(context, op.Rt2, valueHigh);
|
||||
}
|
||||
else if (op.Size == 3)
|
||||
{
|
||||
Operand value = EmitLoad(context, address, exclusive, 4);
|
||||
|
||||
Operand valueLow = context.VectorExtract(OperandType.I64, value, 0);
|
||||
Operand valueHigh = context.VectorExtract(OperandType.I64, value, 1);
|
||||
|
||||
SetIntOrZR(context, op.Rt, valueLow);
|
||||
SetIntOrZR(context, op.Rt2, valueHigh);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException($"Invalid load size of {1 << op.Size} bytes.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 8, 16, 32 or 64-bits (non-pairwise) load.
|
||||
Operand value = EmitLoad(context, address, exclusive, op.Size);
|
||||
|
||||
SetIntOrZR(context, op.Rt, value);
|
||||
}
|
||||
}
|
||||
|
||||
private static Operand EmitLoad(
|
||||
ArmEmitterContext context,
|
||||
Operand address,
|
||||
bool exclusive,
|
||||
int size)
|
||||
{
|
||||
Delegate fallbackMethodDlg = null;
|
||||
|
||||
if (exclusive)
|
||||
{
|
||||
switch (size)
|
||||
{
|
||||
case 0: fallbackMethodDlg = new _U8_U64 (NativeInterface.ReadByteExclusive); break;
|
||||
case 1: fallbackMethodDlg = new _U16_U64 (NativeInterface.ReadUInt16Exclusive); break;
|
||||
case 2: fallbackMethodDlg = new _U32_U64 (NativeInterface.ReadUInt32Exclusive); break;
|
||||
case 3: fallbackMethodDlg = new _U64_U64 (NativeInterface.ReadUInt64Exclusive); break;
|
||||
case 4: fallbackMethodDlg = new _V128_U64(NativeInterface.ReadVector128Exclusive); break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (size)
|
||||
{
|
||||
case 0: fallbackMethodDlg = new _U8_U64 (NativeInterface.ReadByte); break;
|
||||
case 1: fallbackMethodDlg = new _U16_U64 (NativeInterface.ReadUInt16); break;
|
||||
case 2: fallbackMethodDlg = new _U32_U64 (NativeInterface.ReadUInt32); break;
|
||||
case 3: fallbackMethodDlg = new _U64_U64 (NativeInterface.ReadUInt64); break;
|
||||
case 4: fallbackMethodDlg = new _V128_U64(NativeInterface.ReadVector128); break;
|
||||
}
|
||||
}
|
||||
|
||||
return context.Call(fallbackMethodDlg, address);
|
||||
}
|
||||
|
||||
public static void Pfrm(ArmEmitterContext context)
|
||||
{
|
||||
// Memory Prefetch, execute as no-op.
|
||||
}
|
||||
|
||||
public static void Stlr(ArmEmitterContext context) => EmitStr(context, AccessType.Ordered);
|
||||
public static void Stlxr(ArmEmitterContext context) => EmitStr(context, AccessType.OrderedEx);
|
||||
public static void Stxr(ArmEmitterContext context) => EmitStr(context, AccessType.Exclusive);
|
||||
public static void Stxp(ArmEmitterContext context) => EmitStp(context, AccessType.Exclusive);
|
||||
public static void Stlxp(ArmEmitterContext context) => EmitStp(context, AccessType.OrderedEx);
|
||||
|
||||
private static void EmitStr(ArmEmitterContext context, AccessType accType)
|
||||
{
|
||||
EmitStoreEx(context, accType, pair: false);
|
||||
}
|
||||
|
||||
private static void EmitStp(ArmEmitterContext context, AccessType accType)
|
||||
{
|
||||
EmitStoreEx(context, accType, pair: true);
|
||||
}
|
||||
|
||||
private static void EmitStoreEx(ArmEmitterContext context, AccessType accType, bool pair)
|
||||
{
|
||||
OpCodeMemEx op = (OpCodeMemEx)context.CurrOp;
|
||||
|
||||
bool ordered = (accType & AccessType.Ordered) != 0;
|
||||
bool exclusive = (accType & AccessType.Exclusive) != 0;
|
||||
|
||||
if (ordered)
|
||||
{
|
||||
EmitBarrier(context);
|
||||
}
|
||||
|
||||
Operand address = context.Copy(GetIntOrSP(context, op.Rn));
|
||||
|
||||
Operand t = GetIntOrZR(context, op.Rt);
|
||||
|
||||
Operand s = null;
|
||||
|
||||
if (pair)
|
||||
{
|
||||
Debug.Assert(op.Size == 2 || op.Size == 3, "Invalid size for pairwise store.");
|
||||
|
||||
Operand t2 = GetIntOrZR(context, op.Rt2);
|
||||
|
||||
Operand value;
|
||||
|
||||
if (op.Size == 2)
|
||||
{
|
||||
value = context.BitwiseOr(t, context.ShiftLeft(t2, Const(32)));
|
||||
}
|
||||
else /* if (op.Size == 3) */
|
||||
{
|
||||
value = context.VectorInsert(context.VectorZero(), t, 0);
|
||||
value = context.VectorInsert(value, t2, 1);
|
||||
}
|
||||
|
||||
s = EmitStore(context, address, value, exclusive, op.Size + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
s = EmitStore(context, address, t, exclusive, op.Size);
|
||||
}
|
||||
|
||||
if (s != null)
|
||||
{
|
||||
// This is only needed for exclusive stores. The function returns 0
|
||||
// when the store is successful, and 1 otherwise.
|
||||
SetIntOrZR(context, op.Rs, s);
|
||||
}
|
||||
}
|
||||
|
||||
private static Operand EmitStore(
|
||||
ArmEmitterContext context,
|
||||
Operand address,
|
||||
Operand value,
|
||||
bool exclusive,
|
||||
int size)
|
||||
{
|
||||
if (size < 3)
|
||||
{
|
||||
value = context.ConvertI64ToI32(value);
|
||||
}
|
||||
|
||||
Delegate fallbackMethodDlg = null;
|
||||
|
||||
if (exclusive)
|
||||
{
|
||||
switch (size)
|
||||
{
|
||||
case 0: fallbackMethodDlg = new _S32_U64_U8 (NativeInterface.WriteByteExclusive); break;
|
||||
case 1: fallbackMethodDlg = new _S32_U64_U16 (NativeInterface.WriteUInt16Exclusive); break;
|
||||
case 2: fallbackMethodDlg = new _S32_U64_U32 (NativeInterface.WriteUInt32Exclusive); break;
|
||||
case 3: fallbackMethodDlg = new _S32_U64_U64 (NativeInterface.WriteUInt64Exclusive); break;
|
||||
case 4: fallbackMethodDlg = new _S32_U64_V128(NativeInterface.WriteVector128Exclusive); break;
|
||||
}
|
||||
|
||||
return context.Call(fallbackMethodDlg, address, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (size)
|
||||
{
|
||||
case 0: fallbackMethodDlg = new _Void_U64_U8 (NativeInterface.WriteByte); break;
|
||||
case 1: fallbackMethodDlg = new _Void_U64_U16 (NativeInterface.WriteUInt16); break;
|
||||
case 2: fallbackMethodDlg = new _Void_U64_U32 (NativeInterface.WriteUInt32); break;
|
||||
case 3: fallbackMethodDlg = new _Void_U64_U64 (NativeInterface.WriteUInt64); break;
|
||||
case 4: fallbackMethodDlg = new _Void_U64_V128(NativeInterface.WriteVector128); break;
|
||||
}
|
||||
|
||||
context.Call(fallbackMethodDlg, address, value);
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static void EmitBarrier(ArmEmitterContext context)
|
||||
{
|
||||
// Note: This barrier is most likely not necessary, and probably
|
||||
// doesn't make any difference since we need to do a ton of stuff
|
||||
// (software MMU emulation) to read or write anything anyway.
|
||||
}
|
||||
}
|
||||
}
|
512
ARMeilleure/Instructions/InstEmitMemoryHelper.cs
Normal file
512
ARMeilleure/Instructions/InstEmitMemoryHelper.cs
Normal file
|
@ -0,0 +1,512 @@
|
|||
using ARMeilleure.Decoders;
|
||||
using ARMeilleure.IntermediateRepresentation;
|
||||
using ARMeilleure.Memory;
|
||||
using ARMeilleure.Translation;
|
||||
using System;
|
||||
|
||||
using static ARMeilleure.Instructions.InstEmitHelper;
|
||||
using static ARMeilleure.IntermediateRepresentation.OperandHelper;
|
||||
|
||||
namespace ARMeilleure.Instructions
|
||||
{
|
||||
static class InstEmitMemoryHelper
|
||||
{
|
||||
private enum Extension
|
||||
{
|
||||
Zx,
|
||||
Sx32,
|
||||
Sx64
|
||||
}
|
||||
|
||||
public static void EmitLoadZx(ArmEmitterContext context, Operand address, int rt, int size)
|
||||
{
|
||||
EmitLoad(context, address, Extension.Zx, rt, size);
|
||||
}
|
||||
|
||||
public static void EmitLoadSx32(ArmEmitterContext context, Operand address, int rt, int size)
|
||||
{
|
||||
EmitLoad(context, address, Extension.Sx32, rt, size);
|
||||
}
|
||||
|
||||
public static void EmitLoadSx64(ArmEmitterContext context, Operand address, int rt, int size)
|
||||
{
|
||||
EmitLoad(context, address, Extension.Sx64, rt, size);
|
||||
}
|
||||
|
||||
private static void EmitLoad(ArmEmitterContext context, Operand address, Extension ext, int rt, int size)
|
||||
{
|
||||
bool isSimd = IsSimd(context);
|
||||
|
||||
if ((uint)size > (isSimd ? 4 : 3))
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(size));
|
||||
}
|
||||
|
||||
if (isSimd)
|
||||
{
|
||||
EmitReadVector(context, address, context.VectorZero(), rt, 0, size);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitReadInt(context, address, rt, size);
|
||||
}
|
||||
|
||||
if (!isSimd)
|
||||
{
|
||||
Operand value = GetIntOrZR(context, rt);
|
||||
|
||||
if (ext == Extension.Sx32 || ext == Extension.Sx64)
|
||||
{
|
||||
OperandType destType = ext == Extension.Sx64 ? OperandType.I64 : OperandType.I32;
|
||||
|
||||
switch (size)
|
||||
{
|
||||
case 0: value = context.SignExtend8 (destType, value); break;
|
||||
case 1: value = context.SignExtend16(destType, value); break;
|
||||
case 2: value = context.SignExtend32(destType, value); break;
|
||||
}
|
||||
}
|
||||
|
||||
SetIntOrZR(context, rt, value);
|
||||
}
|
||||
}
|
||||
|
||||
public static void EmitLoadSimd(
|
||||
ArmEmitterContext context,
|
||||
Operand address,
|
||||
Operand vector,
|
||||
int rt,
|
||||
int elem,
|
||||
int size)
|
||||
{
|
||||
EmitReadVector(context, address, vector, rt, elem, size);
|
||||
}
|
||||
|
||||
public static void EmitStore(ArmEmitterContext context, Operand address, int rt, int size)
|
||||
{
|
||||
bool isSimd = IsSimd(context);
|
||||
|
||||
if ((uint)size > (isSimd ? 4 : 3))
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(size));
|
||||
}
|
||||
|
||||
if (isSimd)
|
||||
{
|
||||
EmitWriteVector(context, address, rt, 0, size);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitWriteInt(context, address, rt, size);
|
||||
}
|
||||
}
|
||||
|
||||
public static void EmitStoreSimd(
|
||||
ArmEmitterContext context,
|
||||
Operand address,
|
||||
int rt,
|
||||
int elem,
|
||||
int size)
|
||||
{
|
||||
EmitWriteVector(context, address, rt, elem, size);
|
||||
}
|
||||
|
||||
private static bool IsSimd(ArmEmitterContext context)
|
||||
{
|
||||
return context.CurrOp is IOpCodeSimd &&
|
||||
!(context.CurrOp is OpCodeSimdMemMs ||
|
||||
context.CurrOp is OpCodeSimdMemSs);
|
||||
}
|
||||
|
||||
private static void EmitReadInt(ArmEmitterContext context, Operand address, int rt, int size)
|
||||
{
|
||||
Operand isUnalignedAddr = EmitAddressCheck(context, address, size);
|
||||
|
||||
Operand lblFastPath = Label();
|
||||
Operand lblSlowPath = Label();
|
||||
Operand lblEnd = Label();
|
||||
|
||||
context.BranchIfFalse(lblFastPath, isUnalignedAddr);
|
||||
|
||||
context.MarkLabel(lblSlowPath);
|
||||
|
||||
EmitReadIntFallback(context, address, rt, size);
|
||||
|
||||
context.Branch(lblEnd);
|
||||
|
||||
context.MarkLabel(lblFastPath);
|
||||
|
||||
Operand physAddr = EmitPtPointerLoad(context, address, lblSlowPath);
|
||||
|
||||
Operand value = null;
|
||||
|
||||
switch (size)
|
||||
{
|
||||
case 0:
|
||||
value = context.Load8(physAddr);
|
||||
break;
|
||||
|
||||
case 1:
|
||||
value = context.Load16(physAddr);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
value = context.Load(OperandType.I32, physAddr);
|
||||
break;
|
||||
|
||||
case 3:
|
||||
value = context.Load(OperandType.I64, physAddr);
|
||||
break;
|
||||
}
|
||||
|
||||
SetInt(context, rt, value);
|
||||
|
||||
context.MarkLabel(lblEnd);
|
||||
}
|
||||
|
||||
private static void EmitReadVector(
|
||||
ArmEmitterContext context,
|
||||
Operand address,
|
||||
Operand vector,
|
||||
int rt,
|
||||
int elem,
|
||||
int size)
|
||||
{
|
||||
Operand isUnalignedAddr = EmitAddressCheck(context, address, size);
|
||||
|
||||
Operand lblFastPath = Label();
|
||||
Operand lblSlowPath = Label();
|
||||
Operand lblEnd = Label();
|
||||
|
||||
context.BranchIfFalse(lblFastPath, isUnalignedAddr);
|
||||
|
||||
context.MarkLabel(lblSlowPath);
|
||||
|
||||
EmitReadVectorFallback(context, address, vector, rt, elem, size);
|
||||
|
||||
context.Branch(lblEnd);
|
||||
|
||||
context.MarkLabel(lblFastPath);
|
||||
|
||||
Operand physAddr = EmitPtPointerLoad(context, address, lblSlowPath);
|
||||
|
||||
Operand value = null;
|
||||
|
||||
switch (size)
|
||||
{
|
||||
case 0:
|
||||
value = context.VectorInsert8(vector, context.Load8(physAddr), elem);
|
||||
break;
|
||||
|
||||
case 1:
|
||||
value = context.VectorInsert16(vector, context.Load16(physAddr), elem);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
value = context.VectorInsert(vector, context.Load(OperandType.I32, physAddr), elem);
|
||||
break;
|
||||
|
||||
case 3:
|
||||
value = context.VectorInsert(vector, context.Load(OperandType.I64, physAddr), elem);
|
||||
break;
|
||||
|
||||
case 4:
|
||||
value = context.Load(OperandType.V128, physAddr);
|
||||
break;
|
||||
}
|
||||
|
||||
context.Copy(GetVec(rt), value);
|
||||
|
||||
context.MarkLabel(lblEnd);
|
||||
}
|
||||
|
||||
private static Operand VectorCreate(ArmEmitterContext context, Operand value)
|
||||
{
|
||||
return context.VectorInsert(context.VectorZero(), value, 0);
|
||||
}
|
||||
|
||||
private static void EmitWriteInt(ArmEmitterContext context, Operand address, int rt, int size)
|
||||
{
|
||||
Operand isUnalignedAddr = EmitAddressCheck(context, address, size);
|
||||
|
||||
Operand lblFastPath = Label();
|
||||
Operand lblSlowPath = Label();
|
||||
Operand lblEnd = Label();
|
||||
|
||||
context.BranchIfFalse(lblFastPath, isUnalignedAddr);
|
||||
|
||||
context.MarkLabel(lblSlowPath);
|
||||
|
||||
EmitWriteIntFallback(context, address, rt, size);
|
||||
|
||||
context.Branch(lblEnd);
|
||||
|
||||
context.MarkLabel(lblFastPath);
|
||||
|
||||
Operand physAddr = EmitPtPointerLoad(context, address, lblSlowPath);
|
||||
|
||||
Operand value = GetInt(context, rt);
|
||||
|
||||
if (size < 3 && value.Type == OperandType.I64)
|
||||
{
|
||||
value = context.ConvertI64ToI32(value);
|
||||
}
|
||||
|
||||
switch (size)
|
||||
{
|
||||
case 0: context.Store8 (physAddr, value); break;
|
||||
case 1: context.Store16(physAddr, value); break;
|
||||
case 2: context.Store (physAddr, value); break;
|
||||
case 3: context.Store (physAddr, value); break;
|
||||
}
|
||||
|
||||
context.MarkLabel(lblEnd);
|
||||
}
|
||||
|
||||
private static void EmitWriteVector(
|
||||
ArmEmitterContext context,
|
||||
Operand address,
|
||||
int rt,
|
||||
int elem,
|
||||
int size)
|
||||
{
|
||||
Operand isUnalignedAddr = EmitAddressCheck(context, address, size);
|
||||
|
||||
Operand lblFastPath = Label();
|
||||
Operand lblSlowPath = Label();
|
||||
Operand lblEnd = Label();
|
||||
|
||||
context.BranchIfFalse(lblFastPath, isUnalignedAddr);
|
||||
|
||||
context.MarkLabel(lblSlowPath);
|
||||
|
||||
EmitWriteVectorFallback(context, address, rt, elem, size);
|
||||
|
||||
context.Branch(lblEnd);
|
||||
|
||||
context.MarkLabel(lblFastPath);
|
||||
|
||||
Operand physAddr = EmitPtPointerLoad(context, address, lblSlowPath);
|
||||
|
||||
Operand value = GetVec(rt);
|
||||
|
||||
switch (size)
|
||||
{
|
||||
case 0:
|
||||
context.Store8(physAddr, context.VectorExtract8(value, elem));
|
||||
break;
|
||||
|
||||
case 1:
|
||||
context.Store16(physAddr, context.VectorExtract16(value, elem));
|
||||
break;
|
||||
|
||||
case 2:
|
||||
context.Store(physAddr, context.VectorExtract(OperandType.FP32, value, elem));
|
||||
break;
|
||||
|
||||
case 3:
|
||||
context.Store(physAddr, context.VectorExtract(OperandType.FP64, value, elem));
|
||||
break;
|
||||
|
||||
case 4:
|
||||
context.Store(physAddr, value);
|
||||
break;
|
||||
}
|
||||
|
||||
context.MarkLabel(lblEnd);
|
||||
}
|
||||
|
||||
private static Operand EmitAddressCheck(ArmEmitterContext context, Operand address, int size)
|
||||
{
|
||||
long addressCheckMask = ~(context.Memory.AddressSpaceSize - 1);
|
||||
|
||||
addressCheckMask |= (1u << size) - 1;
|
||||
|
||||
return context.BitwiseAnd(address, Const(address.Type, addressCheckMask));
|
||||
}
|
||||
|
||||
private static Operand EmitPtPointerLoad(ArmEmitterContext context, Operand address, Operand lblFallbackPath)
|
||||
{
|
||||
Operand pte = Const(context.Memory.PageTable.ToInt64());
|
||||
|
||||
int bit = MemoryManager.PageBits;
|
||||
|
||||
do
|
||||
{
|
||||
Operand addrPart = context.ShiftRightUI(address, Const(bit));
|
||||
|
||||
bit += context.Memory.PtLevelBits;
|
||||
|
||||
if (bit < context.Memory.AddressSpaceBits)
|
||||
{
|
||||
addrPart = context.BitwiseAnd(addrPart, Const(addrPart.Type, context.Memory.PtLevelMask));
|
||||
}
|
||||
|
||||
Operand pteOffset = context.ShiftLeft(addrPart, Const(3));
|
||||
|
||||
if (pteOffset.Type == OperandType.I32)
|
||||
{
|
||||
pteOffset = context.ZeroExtend32(OperandType.I64, pteOffset);
|
||||
}
|
||||
|
||||
Operand pteAddress = context.Add(pte, pteOffset);
|
||||
|
||||
pte = context.Load(OperandType.I64, pteAddress);
|
||||
}
|
||||
while (bit < context.Memory.AddressSpaceBits);
|
||||
|
||||
if (!context.Memory.HasWriteWatchSupport)
|
||||
{
|
||||
Operand hasFlagSet = context.BitwiseAnd(pte, Const((long)MemoryManager.PteFlagsMask));
|
||||
|
||||
context.BranchIfTrue(lblFallbackPath, hasFlagSet);
|
||||
}
|
||||
|
||||
Operand pageOffset = context.BitwiseAnd(address, Const(address.Type, MemoryManager.PageMask));
|
||||
|
||||
if (pageOffset.Type == OperandType.I32)
|
||||
{
|
||||
pageOffset = context.ZeroExtend32(OperandType.I64, pageOffset);
|
||||
}
|
||||
|
||||
Operand physAddr = context.Add(pte, pageOffset);
|
||||
|
||||
return physAddr;
|
||||
}
|
||||
|
||||
private static void EmitReadIntFallback(ArmEmitterContext context, Operand address, int rt, int size)
|
||||
{
|
||||
Delegate fallbackMethodDlg = null;
|
||||
|
||||
switch (size)
|
||||
{
|
||||
case 0: fallbackMethodDlg = new _U8_U64 (NativeInterface.ReadByte); break;
|
||||
case 1: fallbackMethodDlg = new _U16_U64(NativeInterface.ReadUInt16); break;
|
||||
case 2: fallbackMethodDlg = new _U32_U64(NativeInterface.ReadUInt32); break;
|
||||
case 3: fallbackMethodDlg = new _U64_U64(NativeInterface.ReadUInt64); break;
|
||||
}
|
||||
|
||||
SetInt(context, rt, context.Call(fallbackMethodDlg, address));
|
||||
}
|
||||
|
||||
private static void EmitReadVectorFallback(
|
||||
ArmEmitterContext context,
|
||||
Operand address,
|
||||
Operand vector,
|
||||
int rt,
|
||||
int elem,
|
||||
int size)
|
||||
{
|
||||
Delegate fallbackMethodDlg = null;
|
||||
|
||||
switch (size)
|
||||
{
|
||||
case 0: fallbackMethodDlg = new _U8_U64 (NativeInterface.ReadByte); break;
|
||||
case 1: fallbackMethodDlg = new _U16_U64 (NativeInterface.ReadUInt16); break;
|
||||
case 2: fallbackMethodDlg = new _U32_U64 (NativeInterface.ReadUInt32); break;
|
||||
case 3: fallbackMethodDlg = new _U64_U64 (NativeInterface.ReadUInt64); break;
|
||||
case 4: fallbackMethodDlg = new _V128_U64(NativeInterface.ReadVector128); break;
|
||||
}
|
||||
|
||||
Operand value = context.Call(fallbackMethodDlg, address);
|
||||
|
||||
switch (size)
|
||||
{
|
||||
case 0: value = context.VectorInsert8 (vector, value, elem); break;
|
||||
case 1: value = context.VectorInsert16(vector, value, elem); break;
|
||||
case 2: value = context.VectorInsert (vector, value, elem); break;
|
||||
case 3: value = context.VectorInsert (vector, value, elem); break;
|
||||
}
|
||||
|
||||
context.Copy(GetVec(rt), value);
|
||||
}
|
||||
|
||||
private static void EmitWriteIntFallback(ArmEmitterContext context, Operand address, int rt, int size)
|
||||
{
|
||||
Delegate fallbackMethodDlg = null;
|
||||
|
||||
switch (size)
|
||||
{
|
||||
case 0: fallbackMethodDlg = new _Void_U64_U8 (NativeInterface.WriteByte); break;
|
||||
case 1: fallbackMethodDlg = new _Void_U64_U16(NativeInterface.WriteUInt16); break;
|
||||
case 2: fallbackMethodDlg = new _Void_U64_U32(NativeInterface.WriteUInt32); break;
|
||||
case 3: fallbackMethodDlg = new _Void_U64_U64(NativeInterface.WriteUInt64); break;
|
||||
}
|
||||
|
||||
Operand value = GetInt(context, rt);
|
||||
|
||||
if (size < 3 && value.Type == OperandType.I64)
|
||||
{
|
||||
value = context.ConvertI64ToI32(value);
|
||||
}
|
||||
|
||||
context.Call(fallbackMethodDlg, address, value);
|
||||
}
|
||||
|
||||
private static void EmitWriteVectorFallback(
|
||||
ArmEmitterContext context,
|
||||
Operand address,
|
||||
int rt,
|
||||
int elem,
|
||||
int size)
|
||||
{
|
||||
Delegate fallbackMethodDlg = null;
|
||||
|
||||
switch (size)
|
||||
{
|
||||
case 0: fallbackMethodDlg = new _Void_U64_U8 (NativeInterface.WriteByte); break;
|
||||
case 1: fallbackMethodDlg = new _Void_U64_U16 (NativeInterface.WriteUInt16); break;
|
||||
case 2: fallbackMethodDlg = new _Void_U64_U32 (NativeInterface.WriteUInt32); break;
|
||||
case 3: fallbackMethodDlg = new _Void_U64_U64 (NativeInterface.WriteUInt64); break;
|
||||
case 4: fallbackMethodDlg = new _Void_U64_V128(NativeInterface.WriteVector128); break;
|
||||
}
|
||||
|
||||
Operand value = null;
|
||||
|
||||
if (size < 4)
|
||||
{
|
||||
switch (size)
|
||||
{
|
||||
case 0:
|
||||
value = context.VectorExtract8(GetVec(rt), elem);
|
||||
break;
|
||||
|
||||
case 1:
|
||||
value = context.VectorExtract16(GetVec(rt), elem);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
value = context.VectorExtract(OperandType.I32, GetVec(rt), elem);
|
||||
break;
|
||||
|
||||
case 3:
|
||||
value = context.VectorExtract(OperandType.I64, GetVec(rt), elem);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
value = GetVec(rt);
|
||||
}
|
||||
|
||||
context.Call(fallbackMethodDlg, address, value);
|
||||
}
|
||||
|
||||
private static Operand GetInt(ArmEmitterContext context, int rt)
|
||||
{
|
||||
return context.CurrOp is OpCode32 ? GetIntA32(context, rt) : GetIntOrZR(context, rt);
|
||||
}
|
||||
|
||||
private static void SetInt(ArmEmitterContext context, int rt, Operand value)
|
||||
{
|
||||
if (context.CurrOp is OpCode32)
|
||||
{
|
||||
SetIntA32(context, rt, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetIntOrZR(context, rt, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
41
ARMeilleure/Instructions/InstEmitMove.cs
Normal file
41
ARMeilleure/Instructions/InstEmitMove.cs
Normal file
|
@ -0,0 +1,41 @@
|
|||
using ARMeilleure.Decoders;
|
||||
using ARMeilleure.IntermediateRepresentation;
|
||||
using ARMeilleure.Translation;
|
||||
|
||||
using static ARMeilleure.Instructions.InstEmitHelper;
|
||||
using static ARMeilleure.IntermediateRepresentation.OperandHelper;
|
||||
|
||||
namespace ARMeilleure.Instructions
|
||||
{
|
||||
static partial class InstEmit
|
||||
{
|
||||
public static void Movk(ArmEmitterContext context)
|
||||
{
|
||||
OpCodeMov op = (OpCodeMov)context.CurrOp;
|
||||
|
||||
OperandType type = op.GetOperandType();
|
||||
|
||||
Operand res = GetIntOrZR(context, op.Rd);
|
||||
|
||||
res = context.BitwiseAnd(res, Const(type, ~(0xffffL << op.Bit)));
|
||||
|
||||
res = context.BitwiseOr(res, Const(type, op.Immediate));
|
||||
|
||||
SetIntOrZR(context, op.Rd, res);
|
||||
}
|
||||
|
||||
public static void Movn(ArmEmitterContext context)
|
||||
{
|
||||
OpCodeMov op = (OpCodeMov)context.CurrOp;
|
||||
|
||||
SetIntOrZR(context, op.Rd, Const(op.GetOperandType(), ~op.Immediate));
|
||||
}
|
||||
|
||||
public static void Movz(ArmEmitterContext context)
|
||||
{
|
||||
OpCodeMov op = (OpCodeMov)context.CurrOp;
|
||||
|
||||
SetIntOrZR(context, op.Rd, Const(op.GetOperandType(), op.Immediate));
|
||||
}
|
||||
}
|
||||
}
|
100
ARMeilleure/Instructions/InstEmitMul.cs
Normal file
100
ARMeilleure/Instructions/InstEmitMul.cs
Normal file
|
@ -0,0 +1,100 @@
|
|||
using ARMeilleure.Decoders;
|
||||
using ARMeilleure.IntermediateRepresentation;
|
||||
using ARMeilleure.Translation;
|
||||
using System;
|
||||
|
||||
using static ARMeilleure.Instructions.InstEmitHelper;
|
||||
|
||||
namespace ARMeilleure.Instructions
|
||||
{
|
||||
static partial class InstEmit
|
||||
{
|
||||
public static void Madd(ArmEmitterContext context) => EmitMul(context, isAdd: true);
|
||||
public static void Msub(ArmEmitterContext context) => EmitMul(context, isAdd: false);
|
||||
|
||||
private static void EmitMul(ArmEmitterContext context, bool isAdd)
|
||||
{
|
||||
OpCodeMul op = (OpCodeMul)context.CurrOp;
|
||||
|
||||
Operand a = GetIntOrZR(context, op.Ra);
|
||||
Operand n = GetIntOrZR(context, op.Rn);
|
||||
Operand m = GetIntOrZR(context, op.Rm);
|
||||
|
||||
Operand res = context.Multiply(n, m);
|
||||
|
||||
res = isAdd ? context.Add(a, res) : context.Subtract(a, res);
|
||||
|
||||
SetIntOrZR(context, op.Rd, res);
|
||||
}
|
||||
|
||||
public static void Smaddl(ArmEmitterContext context) => EmitMull(context, MullFlags.SignedAdd);
|
||||
public static void Smsubl(ArmEmitterContext context) => EmitMull(context, MullFlags.SignedSubtract);
|
||||
public static void Umaddl(ArmEmitterContext context) => EmitMull(context, MullFlags.Add);
|
||||
public static void Umsubl(ArmEmitterContext context) => EmitMull(context, MullFlags.Subtract);
|
||||
|
||||
[Flags]
|
||||
private enum MullFlags
|
||||
{
|
||||
Subtract = 0,
|
||||
Add = 1 << 0,
|
||||
Signed = 1 << 1,
|
||||
|
||||
SignedAdd = Signed | Add,
|
||||
SignedSubtract = Signed | Subtract
|
||||
}
|
||||
|
||||
private static void EmitMull(ArmEmitterContext context, MullFlags flags)
|
||||
{
|
||||
OpCodeMul op = (OpCodeMul)context.CurrOp;
|
||||
|
||||
Operand GetExtendedRegister32(int index)
|
||||
{
|
||||
Operand value = GetIntOrZR(context, index);
|
||||
|
||||
if ((flags & MullFlags.Signed) != 0)
|
||||
{
|
||||
return context.SignExtend32(value.Type, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
return context.ZeroExtend32(value.Type, value);
|
||||
}
|
||||
}
|
||||
|
||||
Operand a = GetIntOrZR(context, op.Ra);
|
||||
|
||||
Operand n = GetExtendedRegister32(op.Rn);
|
||||
Operand m = GetExtendedRegister32(op.Rm);
|
||||
|
||||
Operand res = context.Multiply(n, m);
|
||||
|
||||
res = (flags & MullFlags.Add) != 0 ? context.Add(a, res) : context.Subtract(a, res);
|
||||
|
||||
SetIntOrZR(context, op.Rd, res);
|
||||
}
|
||||
|
||||
public static void Smulh(ArmEmitterContext context)
|
||||
{
|
||||
OpCodeMul op = (OpCodeMul)context.CurrOp;
|
||||
|
||||
Operand n = GetIntOrZR(context, op.Rn);
|
||||
Operand m = GetIntOrZR(context, op.Rm);
|
||||
|
||||
Operand d = context.Multiply64HighSI(n, m);
|
||||
|
||||
SetIntOrZR(context, op.Rd, d);
|
||||
}
|
||||
|
||||
public static void Umulh(ArmEmitterContext context)
|
||||
{
|
||||
OpCodeMul op = (OpCodeMul)context.CurrOp;
|
||||
|
||||
Operand n = GetIntOrZR(context, op.Rn);
|
||||
Operand m = GetIntOrZR(context, op.Rm);
|
||||
|
||||
Operand d = context.Multiply64HighUI(n, m);
|
||||
|
||||
SetIntOrZR(context, op.Rd, d);
|
||||
}
|
||||
}
|
||||
}
|
3159
ARMeilleure/Instructions/InstEmitSimdArithmetic.cs
Normal file
3159
ARMeilleure/Instructions/InstEmitSimdArithmetic.cs
Normal file
File diff suppressed because it is too large
Load diff
712
ARMeilleure/Instructions/InstEmitSimdCmp.cs
Normal file
712
ARMeilleure/Instructions/InstEmitSimdCmp.cs
Normal file
|
@ -0,0 +1,712 @@
|
|||
using ARMeilleure.Decoders;
|
||||
using ARMeilleure.IntermediateRepresentation;
|
||||
using ARMeilleure.State;
|
||||
using ARMeilleure.Translation;
|
||||
using System;
|
||||
|
||||
using static ARMeilleure.Instructions.InstEmitHelper;
|
||||
using static ARMeilleure.Instructions.InstEmitSimdHelper;
|
||||
using static ARMeilleure.IntermediateRepresentation.OperandHelper;
|
||||
|
||||
namespace ARMeilleure.Instructions
|
||||
{
|
||||
using Func2I = Func<Operand, Operand, Operand>;
|
||||
|
||||
static partial class InstEmit
|
||||
{
|
||||
public static void Cmeq_S(ArmEmitterContext context)
|
||||
{
|
||||
EmitCmpOp(context, (op1, op2) => context.ICompareEqual(op1, op2), scalar: true);
|
||||
}
|
||||
|
||||
public static void Cmeq_V(ArmEmitterContext context)
|
||||
{
|
||||
if (Optimizations.UseSse41)
|
||||
{
|
||||
OpCodeSimd op = (OpCodeSimd)context.CurrOp;
|
||||
|
||||
Operand n = GetVec(op.Rn);
|
||||
Operand m;
|
||||
|
||||
if (op is OpCodeSimdReg binOp)
|
||||
{
|
||||
m = GetVec(binOp.Rm);
|
||||
}
|
||||
else
|
||||
{
|
||||
m = context.VectorZero();
|
||||
}
|
||||
|
||||
Intrinsic cmpInst = X86PcmpeqInstruction[op.Size];
|
||||
|
||||
Operand res = context.AddIntrinsic(cmpInst, n, m);
|
||||
|
||||
if (op.RegisterSize == RegisterSize.Simd64)
|
||||
{
|
||||
res = context.VectorZeroUpper64(res);
|
||||
}
|
||||
|
||||
context.Copy(GetVec(op.Rd), res);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitCmpOp(context, (op1, op2) => context.ICompareEqual(op1, op2), scalar: false);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Cmge_S(ArmEmitterContext context)
|
||||
{
|
||||
EmitCmpOp(context, (op1, op2) => context.ICompareGreaterOrEqual(op1, op2), scalar: true);
|
||||
}
|
||||
|
||||
public static void Cmge_V(ArmEmitterContext context)
|
||||
{
|
||||
if (Optimizations.UseSse42)
|
||||
{
|
||||
OpCodeSimd op = (OpCodeSimd)context.CurrOp;
|
||||
|
||||
Operand n = GetVec(op.Rn);
|
||||
Operand m;
|
||||
|
||||
if (op is OpCodeSimdReg binOp)
|
||||
{
|
||||
m = GetVec(binOp.Rm);
|
||||
}
|
||||
else
|
||||
{
|
||||
m = context.VectorZero();
|
||||
}
|
||||
|
||||
Intrinsic cmpInst = X86PcmpgtInstruction[op.Size];
|
||||
|
||||
Operand res = context.AddIntrinsic(cmpInst, m, n);
|
||||
|
||||
Operand mask = X86GetAllElements(context, -1L);
|
||||
|
||||
res = context.AddIntrinsic(Intrinsic.X86Pandn, res, mask);
|
||||
|
||||
if (op.RegisterSize == RegisterSize.Simd64)
|
||||
{
|
||||
res = context.VectorZeroUpper64(res);
|
||||
}
|
||||
|
||||
context.Copy(GetVec(op.Rd), res);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitCmpOp(context, (op1, op2) => context.ICompareGreaterOrEqual(op1, op2), scalar: false);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Cmgt_S(ArmEmitterContext context)
|
||||
{
|
||||
EmitCmpOp(context, (op1, op2) => context.ICompareGreater(op1, op2), scalar: true);
|
||||
}
|
||||
|
||||
public static void Cmgt_V(ArmEmitterContext context)
|
||||
{
|
||||
if (Optimizations.UseSse42)
|
||||
{
|
||||
OpCodeSimd op = (OpCodeSimd)context.CurrOp;
|
||||
|
||||
Operand n = GetVec(op.Rn);
|
||||
Operand m;
|
||||
|
||||
if (op is OpCodeSimdReg binOp)
|
||||
{
|
||||
m = GetVec(binOp.Rm);
|
||||
}
|
||||
else
|
||||
{
|
||||
m = context.VectorZero();
|
||||
}
|
||||
|
||||
Intrinsic cmpInst = X86PcmpgtInstruction[op.Size];
|
||||
|
||||
Operand res = context.AddIntrinsic(cmpInst, n, m);
|
||||
|
||||
if (op.RegisterSize == RegisterSize.Simd64)
|
||||
{
|
||||
res = context.VectorZeroUpper64(res);
|
||||
}
|
||||
|
||||
context.Copy(GetVec(op.Rd), res);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitCmpOp(context, (op1, op2) => context.ICompareGreater(op1, op2), scalar: false);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Cmhi_S(ArmEmitterContext context)
|
||||
{
|
||||
EmitCmpOp(context, (op1, op2) => context.ICompareGreaterUI(op1, op2), scalar: true);
|
||||
}
|
||||
|
||||
public static void Cmhi_V(ArmEmitterContext context)
|
||||
{
|
||||
OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp;
|
||||
|
||||
if (Optimizations.UseSse41 && op.Size < 3)
|
||||
{
|
||||
Operand n = GetVec(op.Rn);
|
||||
Operand m = GetVec(op.Rm);
|
||||
|
||||
Intrinsic maxInst = X86PmaxuInstruction[op.Size];
|
||||
|
||||
Operand res = context.AddIntrinsic(maxInst, m, n);
|
||||
|
||||
Intrinsic cmpInst = X86PcmpeqInstruction[op.Size];
|
||||
|
||||
res = context.AddIntrinsic(cmpInst, res, m);
|
||||
|
||||
Operand mask = X86GetAllElements(context, -1L);
|
||||
|
||||
res = context.AddIntrinsic(Intrinsic.X86Pandn, res, mask);
|
||||
|
||||
if (op.RegisterSize == RegisterSize.Simd64)
|
||||
{
|
||||
res = context.VectorZeroUpper64(res);
|
||||
}
|
||||
|
||||
context.Copy(GetVec(op.Rd), res);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitCmpOp(context, (op1, op2) => context.ICompareGreaterUI(op1, op2), scalar: false);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Cmhs_S(ArmEmitterContext context)
|
||||
{
|
||||
EmitCmpOp(context, (op1, op2) => context.ICompareGreaterOrEqualUI(op1, op2), scalar: true);
|
||||
}
|
||||
|
||||
public static void Cmhs_V(ArmEmitterContext context)
|
||||
{
|
||||
OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp;
|
||||
|
||||
if (Optimizations.UseSse41 && op.Size < 3)
|
||||
{
|
||||
Operand n = GetVec(op.Rn);
|
||||
Operand m = GetVec(op.Rm);
|
||||
|
||||
Intrinsic maxInst = X86PmaxuInstruction[op.Size];
|
||||
|
||||
Operand res = context.AddIntrinsic(maxInst, n, m);
|
||||
|
||||
Intrinsic cmpInst = X86PcmpeqInstruction[op.Size];
|
||||
|
||||
res = context.AddIntrinsic(cmpInst, res, n);
|
||||
|
||||
if (op.RegisterSize == RegisterSize.Simd64)
|
||||
{
|
||||
res = context.VectorZeroUpper64(res);
|
||||
}
|
||||
|
||||
context.Copy(GetVec(op.Rd), res);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitCmpOp(context, (op1, op2) => context.ICompareGreaterOrEqualUI(op1, op2), scalar: false);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Cmle_S(ArmEmitterContext context)
|
||||
{
|
||||
EmitCmpOp(context, (op1, op2) => context.ICompareLessOrEqual(op1, op2), scalar: true);
|
||||
}
|
||||
|
||||
public static void Cmle_V(ArmEmitterContext context)
|
||||
{
|
||||
if (Optimizations.UseSse42)
|
||||
{
|
||||
OpCodeSimd op = (OpCodeSimd)context.CurrOp;
|
||||
|
||||
Operand n = GetVec(op.Rn);
|
||||
|
||||
Intrinsic cmpInst = X86PcmpgtInstruction[op.Size];
|
||||
|
||||
Operand res = context.AddIntrinsic(cmpInst, n, context.VectorZero());
|
||||
|
||||
Operand mask = X86GetAllElements(context, -1L);
|
||||
|
||||
res = context.AddIntrinsic(Intrinsic.X86Pandn, res, mask);
|
||||
|
||||
if (op.RegisterSize == RegisterSize.Simd64)
|
||||
{
|
||||
res = context.VectorZeroUpper64(res);
|
||||
}
|
||||
|
||||
context.Copy(GetVec(op.Rd), res);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitCmpOp(context, (op1, op2) => context.ICompareLessOrEqual(op1, op2), scalar: false);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Cmlt_S(ArmEmitterContext context)
|
||||
{
|
||||
EmitCmpOp(context, (op1, op2) => context.ICompareLess(op1, op2), scalar: true);
|
||||
}
|
||||
|
||||
public static void Cmlt_V(ArmEmitterContext context)
|
||||
{
|
||||
if (Optimizations.UseSse42)
|
||||
{
|
||||
OpCodeSimd op = (OpCodeSimd)context.CurrOp;
|
||||
|
||||
Operand n = GetVec(op.Rn);
|
||||
|
||||
Intrinsic cmpInst = X86PcmpgtInstruction[op.Size];
|
||||
|
||||
Operand res = context.AddIntrinsic(cmpInst, context.VectorZero(), n);
|
||||
|
||||
if (op.RegisterSize == RegisterSize.Simd64)
|
||||
{
|
||||
res = context.VectorZeroUpper64(res);
|
||||
}
|
||||
|
||||
context.Copy(GetVec(op.Rd), res);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitCmpOp(context, (op1, op2) => context.ICompareLess(op1, op2), scalar: false);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Cmtst_S(ArmEmitterContext context)
|
||||
{
|
||||
EmitCmtstOp(context, scalar: true);
|
||||
}
|
||||
|
||||
public static void Cmtst_V(ArmEmitterContext context)
|
||||
{
|
||||
EmitCmtstOp(context, scalar: false);
|
||||
}
|
||||
|
||||
public static void Fccmp_S(ArmEmitterContext context)
|
||||
{
|
||||
EmitFccmpOrFccmpe(context, signalNaNs: false);
|
||||
}
|
||||
|
||||
public static void Fccmpe_S(ArmEmitterContext context)
|
||||
{
|
||||
EmitFccmpOrFccmpe(context, signalNaNs: true);
|
||||
}
|
||||
|
||||
public static void Fcmeq_S(ArmEmitterContext context)
|
||||
{
|
||||
if (Optimizations.FastFP && Optimizations.UseSse2)
|
||||
{
|
||||
EmitCmpSseOrSse2OpF(context, CmpCondition.Equal, scalar: true);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitCmpOpF(context, SoftFloat32.FPCompareEQ, SoftFloat64.FPCompareEQ, scalar: true);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Fcmeq_V(ArmEmitterContext context)
|
||||
{
|
||||
if (Optimizations.FastFP && Optimizations.UseSse2)
|
||||
{
|
||||
EmitCmpSseOrSse2OpF(context, CmpCondition.Equal, scalar: false);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitCmpOpF(context, SoftFloat32.FPCompareEQ, SoftFloat64.FPCompareEQ, scalar: false);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Fcmge_S(ArmEmitterContext context)
|
||||
{
|
||||
if (Optimizations.FastFP && Optimizations.UseSse2)
|
||||
{
|
||||
EmitCmpSseOrSse2OpF(context, CmpCondition.GreaterThanOrEqual, scalar: true);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitCmpOpF(context, SoftFloat32.FPCompareGE, SoftFloat64.FPCompareGE, scalar: true);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Fcmge_V(ArmEmitterContext context)
|
||||
{
|
||||
if (Optimizations.FastFP && Optimizations.UseSse2)
|
||||
{
|
||||
EmitCmpSseOrSse2OpF(context, CmpCondition.GreaterThanOrEqual, scalar: false);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitCmpOpF(context, SoftFloat32.FPCompareGE, SoftFloat64.FPCompareGE, scalar: false);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Fcmgt_S(ArmEmitterContext context)
|
||||
{
|
||||
if (Optimizations.FastFP && Optimizations.UseSse2)
|
||||
{
|
||||
EmitCmpSseOrSse2OpF(context, CmpCondition.GreaterThan, scalar: true);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitCmpOpF(context, SoftFloat32.FPCompareGT, SoftFloat64.FPCompareGT, scalar: true);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Fcmgt_V(ArmEmitterContext context)
|
||||
{
|
||||
if (Optimizations.FastFP && Optimizations.UseSse2)
|
||||
{
|
||||
EmitCmpSseOrSse2OpF(context, CmpCondition.GreaterThan, scalar: false);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitCmpOpF(context, SoftFloat32.FPCompareGT, SoftFloat64.FPCompareGT, scalar: false);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Fcmle_S(ArmEmitterContext context)
|
||||
{
|
||||
if (Optimizations.FastFP && Optimizations.UseSse2)
|
||||
{
|
||||
EmitCmpSseOrSse2OpF(context, CmpCondition.GreaterThanOrEqual, scalar: true, isLeOrLt: true);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitCmpOpF(context, SoftFloat32.FPCompareLE, SoftFloat64.FPCompareLE, scalar: true);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Fcmle_V(ArmEmitterContext context)
|
||||
{
|
||||
if (Optimizations.FastFP && Optimizations.UseSse2)
|
||||
{
|
||||
EmitCmpSseOrSse2OpF(context, CmpCondition.GreaterThanOrEqual, scalar: false, isLeOrLt: true);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitCmpOpF(context, SoftFloat32.FPCompareLE, SoftFloat64.FPCompareLE, scalar: false);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Fcmlt_S(ArmEmitterContext context)
|
||||
{
|
||||
if (Optimizations.FastFP && Optimizations.UseSse2)
|
||||
{
|
||||
EmitCmpSseOrSse2OpF(context, CmpCondition.GreaterThan, scalar: true, isLeOrLt: true);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitCmpOpF(context, SoftFloat32.FPCompareLT, SoftFloat64.FPCompareLT, scalar: true);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Fcmlt_V(ArmEmitterContext context)
|
||||
{
|
||||
if (Optimizations.FastFP && Optimizations.UseSse2)
|
||||
{
|
||||
EmitCmpSseOrSse2OpF(context, CmpCondition.GreaterThan, scalar: false, isLeOrLt: true);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitCmpOpF(context, SoftFloat32.FPCompareLT, SoftFloat64.FPCompareLT, scalar: false);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Fcmp_S(ArmEmitterContext context)
|
||||
{
|
||||
EmitFcmpOrFcmpe(context, signalNaNs: false);
|
||||
}
|
||||
|
||||
public static void Fcmpe_S(ArmEmitterContext context)
|
||||
{
|
||||
EmitFcmpOrFcmpe(context, signalNaNs: true);
|
||||
}
|
||||
|
||||
public static void EmitFccmpOrFccmpe(ArmEmitterContext context, bool signalNaNs)
|
||||
{
|
||||
OpCodeSimdFcond op = (OpCodeSimdFcond)context.CurrOp;
|
||||
|
||||
Operand lblTrue = Label();
|
||||
Operand lblEnd = Label();
|
||||
|
||||
context.BranchIfTrue(lblTrue, InstEmitFlowHelper.GetCondTrue(context, op.Cond));
|
||||
|
||||
EmitSetNzcv(context, Const(op.Nzcv));
|
||||
|
||||
context.Branch(lblEnd);
|
||||
|
||||
context.MarkLabel(lblTrue);
|
||||
|
||||
EmitFcmpOrFcmpe(context, signalNaNs);
|
||||
|
||||
context.MarkLabel(lblEnd);
|
||||
}
|
||||
|
||||
private static void EmitFcmpOrFcmpe(ArmEmitterContext context, bool signalNaNs)
|
||||
{
|
||||
OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp;
|
||||
|
||||
const int cmpOrdered = 7;
|
||||
|
||||
bool cmpWithZero = !(op is OpCodeSimdFcond) ? op.Bit3 : false;
|
||||
|
||||
if (Optimizations.FastFP && Optimizations.UseSse2)
|
||||
{
|
||||
Operand n = GetVec(op.Rn);
|
||||
Operand m = cmpWithZero ? context.VectorZero() : GetVec(op.Rm);
|
||||
|
||||
Operand lblNaN = Label();
|
||||
Operand lblEnd = Label();
|
||||
|
||||
if (op.Size == 0)
|
||||
{
|
||||
Operand ordMask = context.AddIntrinsic(Intrinsic.X86Cmpss, n, m, Const(cmpOrdered));
|
||||
|
||||
Operand isOrdered = context.VectorExtract16(ordMask, 0);
|
||||
|
||||
context.BranchIfFalse(lblNaN, isOrdered);
|
||||
|
||||
Operand cf = context.AddIntrinsicInt(Intrinsic.X86Comissge, n, m);
|
||||
Operand zf = context.AddIntrinsicInt(Intrinsic.X86Comisseq, n, m);
|
||||
Operand nf = context.AddIntrinsicInt(Intrinsic.X86Comisslt, n, m);
|
||||
|
||||
SetFlag(context, PState.VFlag, Const(0));
|
||||
SetFlag(context, PState.CFlag, cf);
|
||||
SetFlag(context, PState.ZFlag, zf);
|
||||
SetFlag(context, PState.NFlag, nf);
|
||||
}
|
||||
else /* if (op.Size == 1) */
|
||||
{
|
||||
Operand ordMask = context.AddIntrinsic(Intrinsic.X86Cmpsd, n, m, Const(cmpOrdered));
|
||||
|
||||
Operand isOrdered = context.VectorExtract16(ordMask, 0);
|
||||
|
||||
context.BranchIfFalse(lblNaN, isOrdered);
|
||||
|
||||
Operand cf = context.AddIntrinsicInt(Intrinsic.X86Comisdge, n, m);
|
||||
Operand zf = context.AddIntrinsicInt(Intrinsic.X86Comisdeq, n, m);
|
||||
Operand nf = context.AddIntrinsicInt(Intrinsic.X86Comisdlt, n, m);
|
||||
|
||||
SetFlag(context, PState.VFlag, Const(0));
|
||||
SetFlag(context, PState.CFlag, cf);
|
||||
SetFlag(context, PState.ZFlag, zf);
|
||||
SetFlag(context, PState.NFlag, nf);
|
||||
}
|
||||
|
||||
context.Branch(lblEnd);
|
||||
|
||||
context.MarkLabel(lblNaN);
|
||||
|
||||
SetFlag(context, PState.VFlag, Const(1));
|
||||
SetFlag(context, PState.CFlag, Const(1));
|
||||
SetFlag(context, PState.ZFlag, Const(0));
|
||||
SetFlag(context, PState.NFlag, Const(0));
|
||||
|
||||
context.MarkLabel(lblEnd);
|
||||
}
|
||||
else
|
||||
{
|
||||
OperandType type = op.Size != 0 ? OperandType.FP64 : OperandType.FP32;
|
||||
|
||||
Operand ne = context.VectorExtract(type, GetVec(op.Rn), 0);
|
||||
Operand me;
|
||||
|
||||
if (cmpWithZero)
|
||||
{
|
||||
me = op.Size == 0 ? ConstF(0f) : ConstF(0d);
|
||||
}
|
||||
else
|
||||
{
|
||||
me = context.VectorExtract(type, GetVec(op.Rm), 0);
|
||||
}
|
||||
|
||||
Delegate dlg = op.Size != 0
|
||||
? (Delegate)new _S32_F64_F64_Bool(SoftFloat64.FPCompare)
|
||||
: (Delegate)new _S32_F32_F32_Bool(SoftFloat32.FPCompare);
|
||||
|
||||
Operand nzcv = context.Call(dlg, ne, me, Const(signalNaNs));
|
||||
|
||||
EmitSetNzcv(context, nzcv);
|
||||
}
|
||||
}
|
||||
|
||||
private static void EmitSetNzcv(ArmEmitterContext context, Operand nzcv)
|
||||
{
|
||||
Operand Extract(Operand value, int bit)
|
||||
{
|
||||
if (bit != 0)
|
||||
{
|
||||
value = context.ShiftRightUI(value, Const(bit));
|
||||
}
|
||||
|
||||
value = context.BitwiseAnd(value, Const(1));
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
SetFlag(context, PState.VFlag, Extract(nzcv, 0));
|
||||
SetFlag(context, PState.CFlag, Extract(nzcv, 1));
|
||||
SetFlag(context, PState.ZFlag, Extract(nzcv, 2));
|
||||
SetFlag(context, PState.NFlag, Extract(nzcv, 3));
|
||||
}
|
||||
|
||||
private static void EmitCmpOp(ArmEmitterContext context, Func2I emitCmp, bool scalar)
|
||||
{
|
||||
OpCodeSimd op = (OpCodeSimd)context.CurrOp;
|
||||
|
||||
Operand res = context.VectorZero();
|
||||
|
||||
int elems = !scalar ? op.GetBytesCount() >> op.Size : 1;
|
||||
|
||||
ulong szMask = ulong.MaxValue >> (64 - (8 << op.Size));
|
||||
|
||||
for (int index = 0; index < elems; index++)
|
||||
{
|
||||
Operand ne = EmitVectorExtractSx(context, op.Rn, index, op.Size);
|
||||
Operand me;
|
||||
|
||||
if (op is OpCodeSimdReg binOp)
|
||||
{
|
||||
me = EmitVectorExtractSx(context, binOp.Rm, index, op.Size);
|
||||
}
|
||||
else
|
||||
{
|
||||
me = Const(0L);
|
||||
}
|
||||
|
||||
Operand isTrue = emitCmp(ne, me);
|
||||
|
||||
Operand mask = context.ConditionalSelect(isTrue, Const(szMask), Const(0L));
|
||||
|
||||
res = EmitVectorInsert(context, res, mask, index, op.Size);
|
||||
}
|
||||
|
||||
context.Copy(GetVec(op.Rd), res);
|
||||
}
|
||||
|
||||
private static void EmitCmtstOp(ArmEmitterContext context, bool scalar)
|
||||
{
|
||||
OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp;
|
||||
|
||||
Operand res = context.VectorZero();
|
||||
|
||||
int elems = !scalar ? op.GetBytesCount() >> op.Size : 1;
|
||||
|
||||
ulong szMask = ulong.MaxValue >> (64 - (8 << op.Size));
|
||||
|
||||
for (int index = 0; index < elems; index++)
|
||||
{
|
||||
Operand ne = EmitVectorExtractZx(context, op.Rn, index, op.Size);
|
||||
Operand me = EmitVectorExtractZx(context, op.Rm, index, op.Size);
|
||||
|
||||
Operand test = context.BitwiseAnd(ne, me);
|
||||
|
||||
Operand isTrue = context.ICompareNotEqual(test, Const(0L));
|
||||
|
||||
Operand mask = context.ConditionalSelect(isTrue, Const(szMask), Const(0L));
|
||||
|
||||
res = EmitVectorInsert(context, res, mask, index, op.Size);
|
||||
}
|
||||
|
||||
context.Copy(GetVec(op.Rd), res);
|
||||
}
|
||||
|
||||
private static void EmitCmpOpF(
|
||||
ArmEmitterContext context,
|
||||
_F32_F32_F32 f32,
|
||||
_F64_F64_F64 f64,
|
||||
bool scalar)
|
||||
{
|
||||
OpCodeSimd op = (OpCodeSimd)context.CurrOp;
|
||||
|
||||
Operand res = context.VectorZero();
|
||||
|
||||
int sizeF = op.Size & 1;
|
||||
|
||||
OperandType type = sizeF != 0 ? OperandType.FP64 : OperandType.FP32;
|
||||
|
||||
int elems = !scalar ? op.GetBytesCount() >> sizeF + 2 : 1;
|
||||
|
||||
for (int index = 0; index < elems; index++)
|
||||
{
|
||||
Operand ne = context.VectorExtract(type, GetVec(op.Rn), index);
|
||||
Operand me;
|
||||
|
||||
if (op is OpCodeSimdReg binOp)
|
||||
{
|
||||
me = context.VectorExtract(type, GetVec(binOp.Rm), index);
|
||||
}
|
||||
else
|
||||
{
|
||||
me = sizeF == 0 ? ConstF(0f) : ConstF(0d);
|
||||
}
|
||||
|
||||
Operand e = EmitSoftFloatCall(context, f32, f64, ne, me);
|
||||
|
||||
res = context.VectorInsert(res, e, index);
|
||||
}
|
||||
|
||||
context.Copy(GetVec(op.Rd), res);
|
||||
}
|
||||
|
||||
private enum CmpCondition
|
||||
{
|
||||
Equal = 0,
|
||||
GreaterThanOrEqual = 5,
|
||||
GreaterThan = 6
|
||||
}
|
||||
|
||||
private static void EmitCmpSseOrSse2OpF(
|
||||
ArmEmitterContext context,
|
||||
CmpCondition cond,
|
||||
bool scalar,
|
||||
bool isLeOrLt = false)
|
||||
{
|
||||
OpCodeSimd op = (OpCodeSimd)context.CurrOp;
|
||||
|
||||
Operand n = GetVec(op.Rn);
|
||||
Operand m = op is OpCodeSimdReg binOp ? GetVec(binOp.Rm) : context.VectorZero();
|
||||
|
||||
int sizeF = op.Size & 1;
|
||||
|
||||
if (sizeF == 0)
|
||||
{
|
||||
Intrinsic inst = scalar ? Intrinsic.X86Cmpss : Intrinsic.X86Cmpps;
|
||||
|
||||
Operand res = isLeOrLt
|
||||
? context.AddIntrinsic(inst, m, n, Const((int)cond))
|
||||
: context.AddIntrinsic(inst, n, m, Const((int)cond));
|
||||
|
||||
if (scalar)
|
||||
{
|
||||
res = context.VectorZeroUpper96(res);
|
||||
}
|
||||
else if (op.RegisterSize == RegisterSize.Simd64)
|
||||
{
|
||||
res = context.VectorZeroUpper64(res);
|
||||
}
|
||||
|
||||
context.Copy(GetVec(op.Rd), res);
|
||||
}
|
||||
else /* if (sizeF == 1) */
|
||||
{
|
||||
Intrinsic inst = scalar ? Intrinsic.X86Cmpsd : Intrinsic.X86Cmppd;
|
||||
|
||||
Operand res = isLeOrLt
|
||||
? context.AddIntrinsic(inst, m, n, Const((int)cond))
|
||||
: context.AddIntrinsic(inst, n, m, Const((int)cond));
|
||||
|
||||
if (scalar)
|
||||
{
|
||||
res = context.VectorZeroUpper64(res);
|
||||
}
|
||||
|
||||
context.Copy(GetVec(op.Rd), res);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
49
ARMeilleure/Instructions/InstEmitSimdCrypto.cs
Normal file
49
ARMeilleure/Instructions/InstEmitSimdCrypto.cs
Normal file
|
@ -0,0 +1,49 @@
|
|||
using ARMeilleure.Decoders;
|
||||
using ARMeilleure.IntermediateRepresentation;
|
||||
using ARMeilleure.Translation;
|
||||
|
||||
using static ARMeilleure.Instructions.InstEmitHelper;
|
||||
|
||||
namespace ARMeilleure.Instructions
|
||||
{
|
||||
static partial class InstEmit
|
||||
{
|
||||
public static void Aesd_V(ArmEmitterContext context)
|
||||
{
|
||||
OpCodeSimd op = (OpCodeSimd)context.CurrOp;
|
||||
|
||||
Operand d = GetVec(op.Rd);
|
||||
Operand n = GetVec(op.Rn);
|
||||
|
||||
context.Copy(d, context.Call(new _V128_V128_V128(SoftFallback.Decrypt), d, n));
|
||||
}
|
||||
|
||||
public static void Aese_V(ArmEmitterContext context)
|
||||
{
|
||||
OpCodeSimd op = (OpCodeSimd)context.CurrOp;
|
||||
|
||||
Operand d = GetVec(op.Rd);
|
||||
Operand n = GetVec(op.Rn);
|
||||
|
||||
context.Copy(d, context.Call(new _V128_V128_V128(SoftFallback.Encrypt), d, n));
|
||||
}
|
||||
|
||||
public static void Aesimc_V(ArmEmitterContext context)
|
||||
{
|
||||
OpCodeSimd op = (OpCodeSimd)context.CurrOp;
|
||||
|
||||
Operand n = GetVec(op.Rn);
|
||||
|
||||
context.Copy(GetVec(op.Rd), context.Call(new _V128_V128(SoftFallback.InverseMixColumns), n));
|
||||
}
|
||||
|
||||
public static void Aesmc_V(ArmEmitterContext context)
|
||||
{
|
||||
OpCodeSimd op = (OpCodeSimd)context.CurrOp;
|
||||
|
||||
Operand n = GetVec(op.Rn);
|
||||
|
||||
context.Copy(GetVec(op.Rd), context.Call(new _V128_V128(SoftFallback.MixColumns), n));
|
||||
}
|
||||
}
|
||||
}
|
1166
ARMeilleure/Instructions/InstEmitSimdCvt.cs
Normal file
1166
ARMeilleure/Instructions/InstEmitSimdCvt.cs
Normal file
File diff suppressed because it is too large
Load diff
147
ARMeilleure/Instructions/InstEmitSimdHash.cs
Normal file
147
ARMeilleure/Instructions/InstEmitSimdHash.cs
Normal file
|
@ -0,0 +1,147 @@
|
|||
using ARMeilleure.Decoders;
|
||||
using ARMeilleure.IntermediateRepresentation;
|
||||
using ARMeilleure.Translation;
|
||||
|
||||
using static ARMeilleure.Instructions.InstEmitHelper;
|
||||
|
||||
namespace ARMeilleure.Instructions
|
||||
{
|
||||
static partial class InstEmit
|
||||
{
|
||||
#region "Sha1"
|
||||
public static void Sha1c_V(ArmEmitterContext context)
|
||||
{
|
||||
OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp;
|
||||
|
||||
Operand d = GetVec(op.Rd);
|
||||
|
||||
Operand ne = context.VectorExtract(OperandType.I32, GetVec(op.Rn), 0);
|
||||
|
||||
Operand m = GetVec(op.Rm);
|
||||
|
||||
Operand res = context.Call(new _V128_V128_U32_V128(SoftFallback.HashChoose), d, ne, m);
|
||||
|
||||
context.Copy(GetVec(op.Rd), res);
|
||||
}
|
||||
|
||||
public static void Sha1h_V(ArmEmitterContext context)
|
||||
{
|
||||
OpCodeSimd op = (OpCodeSimd)context.CurrOp;
|
||||
|
||||
Operand ne = context.VectorExtract(OperandType.I32, GetVec(op.Rn), 0);
|
||||
|
||||
Operand res = context.Call(new _U32_U32(SoftFallback.FixedRotate), ne);
|
||||
|
||||
context.Copy(GetVec(op.Rd), context.VectorCreateScalar(res));
|
||||
}
|
||||
|
||||
public static void Sha1m_V(ArmEmitterContext context)
|
||||
{
|
||||
OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp;
|
||||
|
||||
Operand d = GetVec(op.Rd);
|
||||
|
||||
Operand ne = context.VectorExtract(OperandType.I32, GetVec(op.Rn), 0);
|
||||
|
||||
Operand m = GetVec(op.Rm);
|
||||
|
||||
Operand res = context.Call(new _V128_V128_U32_V128(SoftFallback.HashMajority), d, ne, m);
|
||||
|
||||
context.Copy(GetVec(op.Rd), res);
|
||||
}
|
||||
|
||||
public static void Sha1p_V(ArmEmitterContext context)
|
||||
{
|
||||
OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp;
|
||||
|
||||
Operand d = GetVec(op.Rd);
|
||||
|
||||
Operand ne = context.VectorExtract(OperandType.I32, GetVec(op.Rn), 0);
|
||||
|
||||
Operand m = GetVec(op.Rm);
|
||||
|
||||
Operand res = context.Call(new _V128_V128_U32_V128(SoftFallback.HashParity), d, ne, m);
|
||||
|
||||
context.Copy(GetVec(op.Rd), res);
|
||||
}
|
||||
|
||||
public static void Sha1su0_V(ArmEmitterContext context)
|
||||
{
|
||||
OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp;
|
||||
|
||||
Operand d = GetVec(op.Rd);
|
||||
Operand n = GetVec(op.Rn);
|
||||
Operand m = GetVec(op.Rm);
|
||||
|
||||
Operand res = context.Call(new _V128_V128_V128_V128(SoftFallback.Sha1SchedulePart1), d, n, m);
|
||||
|
||||
context.Copy(GetVec(op.Rd), res);
|
||||
}
|
||||
|
||||
public static void Sha1su1_V(ArmEmitterContext context)
|
||||
{
|
||||
OpCodeSimd op = (OpCodeSimd)context.CurrOp;
|
||||
|
||||
Operand d = GetVec(op.Rd);
|
||||
Operand n = GetVec(op.Rn);
|
||||
|
||||
Operand res = context.Call(new _V128_V128_V128(SoftFallback.Sha1SchedulePart2), d, n);
|
||||
|
||||
context.Copy(GetVec(op.Rd), res);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region "Sha256"
|
||||
public static void Sha256h_V(ArmEmitterContext context)
|
||||
{
|
||||
OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp;
|
||||
|
||||
Operand d = GetVec(op.Rd);
|
||||
Operand n = GetVec(op.Rn);
|
||||
Operand m = GetVec(op.Rm);
|
||||
|
||||
Operand res = context.Call(new _V128_V128_V128_V128(SoftFallback.HashLower), d, n, m);
|
||||
|
||||
context.Copy(GetVec(op.Rd), res);
|
||||
}
|
||||
|
||||
public static void Sha256h2_V(ArmEmitterContext context)
|
||||
{
|
||||
OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp;
|
||||
|
||||
Operand d = GetVec(op.Rd);
|
||||
Operand n = GetVec(op.Rn);
|
||||
Operand m = GetVec(op.Rm);
|
||||
|
||||
Operand res = context.Call(new _V128_V128_V128_V128(SoftFallback.HashUpper), d, n, m);
|
||||
|
||||
context.Copy(GetVec(op.Rd), res);
|
||||
}
|
||||
|
||||
public static void Sha256su0_V(ArmEmitterContext context)
|
||||
{
|
||||
OpCodeSimd op = (OpCodeSimd)context.CurrOp;
|
||||
|
||||
Operand d = GetVec(op.Rd);
|
||||
Operand n = GetVec(op.Rn);
|
||||
|
||||
Operand res = context.Call(new _V128_V128_V128(SoftFallback.Sha256SchedulePart1), d, n);
|
||||
|
||||
context.Copy(GetVec(op.Rd), res);
|
||||
}
|
||||
|
||||
public static void Sha256su1_V(ArmEmitterContext context)
|
||||
{
|
||||
OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp;
|
||||
|
||||
Operand d = GetVec(op.Rd);
|
||||
Operand n = GetVec(op.Rn);
|
||||
Operand m = GetVec(op.Rm);
|
||||
|
||||
Operand res = context.Call(new _V128_V128_V128_V128(SoftFallback.Sha256SchedulePart2), d, n, m);
|
||||
|
||||
context.Copy(GetVec(op.Rd), res);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
1477
ARMeilleure/Instructions/InstEmitSimdHelper.cs
Normal file
1477
ARMeilleure/Instructions/InstEmitSimdHelper.cs
Normal file
File diff suppressed because it is too large
Load diff
456
ARMeilleure/Instructions/InstEmitSimdLogical.cs
Normal file
456
ARMeilleure/Instructions/InstEmitSimdLogical.cs
Normal file
|
@ -0,0 +1,456 @@
|
|||
using ARMeilleure.Decoders;
|
||||
using ARMeilleure.IntermediateRepresentation;
|
||||
using ARMeilleure.Translation;
|
||||
|
||||
using static ARMeilleure.Instructions.InstEmitHelper;
|
||||
using static ARMeilleure.Instructions.InstEmitSimdHelper;
|
||||
using static ARMeilleure.IntermediateRepresentation.OperandHelper;
|
||||
|
||||
namespace ARMeilleure.Instructions
|
||||
{
|
||||
static partial class InstEmit
|
||||
{
|
||||
public static void And_V(ArmEmitterContext context)
|
||||
{
|
||||
if (Optimizations.UseSse2)
|
||||
{
|
||||
OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp;
|
||||
|
||||
Operand n = GetVec(op.Rn);
|
||||
Operand m = GetVec(op.Rm);
|
||||
|
||||
Operand res = context.AddIntrinsic(Intrinsic.X86Pand, n, m);
|
||||
|
||||
if (op.RegisterSize == RegisterSize.Simd64)
|
||||
{
|
||||
res = context.VectorZeroUpper64(res);
|
||||
}
|
||||
|
||||
context.Copy(GetVec(op.Rd), res);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitVectorBinaryOpZx(context, (op1, op2) => context.BitwiseAnd(op1, op2));
|
||||
}
|
||||
}
|
||||
|
||||
public static void Bic_V(ArmEmitterContext context)
|
||||
{
|
||||
if (Optimizations.UseSse2)
|
||||
{
|
||||
OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp;
|
||||
|
||||
Operand n = GetVec(op.Rn);
|
||||
Operand m = GetVec(op.Rm);
|
||||
|
||||
Operand res = context.AddIntrinsic(Intrinsic.X86Pandn, m, n);
|
||||
|
||||
if (op.RegisterSize == RegisterSize.Simd64)
|
||||
{
|
||||
res = context.VectorZeroUpper64(res);
|
||||
}
|
||||
|
||||
context.Copy(GetVec(op.Rd), res);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitVectorBinaryOpZx(context, (op1, op2) =>
|
||||
{
|
||||
return context.BitwiseAnd(op1, context.BitwiseNot(op2));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public static void Bic_Vi(ArmEmitterContext context)
|
||||
{
|
||||
EmitVectorImmBinaryOp(context, (op1, op2) =>
|
||||
{
|
||||
return context.BitwiseAnd(op1, context.BitwiseNot(op2));
|
||||
});
|
||||
}
|
||||
|
||||
public static void Bif_V(ArmEmitterContext context)
|
||||
{
|
||||
EmitBifBit(context, notRm: true);
|
||||
}
|
||||
|
||||
public static void Bit_V(ArmEmitterContext context)
|
||||
{
|
||||
EmitBifBit(context, notRm: false);
|
||||
}
|
||||
|
||||
private static void EmitBifBit(ArmEmitterContext context, bool notRm)
|
||||
{
|
||||
OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp;
|
||||
|
||||
if (Optimizations.UseSse2)
|
||||
{
|
||||
Operand d = GetVec(op.Rd);
|
||||
Operand n = GetVec(op.Rn);
|
||||
Operand m = GetVec(op.Rm);
|
||||
|
||||
Operand res = context.AddIntrinsic(Intrinsic.X86Pxor, n, d);
|
||||
|
||||
if (notRm)
|
||||
{
|
||||
res = context.AddIntrinsic(Intrinsic.X86Pandn, m, res);
|
||||
}
|
||||
else
|
||||
{
|
||||
res = context.AddIntrinsic(Intrinsic.X86Pand, m, res);
|
||||
}
|
||||
|
||||
res = context.AddIntrinsic(Intrinsic.X86Pxor, d, res);
|
||||
|
||||
if (op.RegisterSize == RegisterSize.Simd64)
|
||||
{
|
||||
res = context.VectorZeroUpper64(res);
|
||||
}
|
||||
|
||||
context.Copy(GetVec(op.Rd), res);
|
||||
}
|
||||
else
|
||||
{
|
||||
Operand res = context.VectorZero();
|
||||
|
||||
int elems = op.RegisterSize == RegisterSize.Simd128 ? 2 : 1;
|
||||
|
||||
for (int index = 0; index < elems; index++)
|
||||
{
|
||||
Operand d = EmitVectorExtractZx(context, op.Rd, index, 3);
|
||||
Operand n = EmitVectorExtractZx(context, op.Rn, index, 3);
|
||||
Operand m = EmitVectorExtractZx(context, op.Rm, index, 3);
|
||||
|
||||
if (notRm)
|
||||
{
|
||||
m = context.BitwiseNot(m);
|
||||
}
|
||||
|
||||
Operand e = context.BitwiseExclusiveOr(d, n);
|
||||
|
||||
e = context.BitwiseAnd(e, m);
|
||||
e = context.BitwiseExclusiveOr(e, d);
|
||||
|
||||
res = EmitVectorInsert(context, res, e, index, 3);
|
||||
}
|
||||
|
||||
context.Copy(GetVec(op.Rd), res);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Bsl_V(ArmEmitterContext context)
|
||||
{
|
||||
if (Optimizations.UseSse2)
|
||||
{
|
||||
OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp;
|
||||
|
||||
Operand d = GetVec(op.Rd);
|
||||
Operand n = GetVec(op.Rn);
|
||||
Operand m = GetVec(op.Rm);
|
||||
|
||||
Operand res = context.AddIntrinsic(Intrinsic.X86Pxor, n, m);
|
||||
|
||||
res = context.AddIntrinsic(Intrinsic.X86Pand, res, d);
|
||||
res = context.AddIntrinsic(Intrinsic.X86Pxor, res, m);
|
||||
|
||||
if (op.RegisterSize == RegisterSize.Simd64)
|
||||
{
|
||||
res = context.VectorZeroUpper64(res);
|
||||
}
|
||||
|
||||
context.Copy(GetVec(op.Rd), res);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitVectorTernaryOpZx(context, (op1, op2, op3) =>
|
||||
{
|
||||
return context.BitwiseExclusiveOr(
|
||||
context.BitwiseAnd(op1,
|
||||
context.BitwiseExclusiveOr(op2, op3)), op3);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public static void Eor_V(ArmEmitterContext context)
|
||||
{
|
||||
if (Optimizations.UseSse2)
|
||||
{
|
||||
OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp;
|
||||
|
||||
Operand n = GetVec(op.Rn);
|
||||
Operand m = GetVec(op.Rm);
|
||||
|
||||
Operand res = context.AddIntrinsic(Intrinsic.X86Pxor, n, m);
|
||||
|
||||
if (op.RegisterSize == RegisterSize.Simd64)
|
||||
{
|
||||
res = context.VectorZeroUpper64(res);
|
||||
}
|
||||
|
||||
context.Copy(GetVec(op.Rd), res);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitVectorBinaryOpZx(context, (op1, op2) => context.BitwiseExclusiveOr(op1, op2));
|
||||
}
|
||||
}
|
||||
|
||||
public static void Not_V(ArmEmitterContext context)
|
||||
{
|
||||
if (Optimizations.UseSse2)
|
||||
{
|
||||
OpCodeSimd op = (OpCodeSimd)context.CurrOp;
|
||||
|
||||
Operand n = GetVec(op.Rn);
|
||||
|
||||
Operand mask = X86GetAllElements(context, -1L);
|
||||
|
||||
Operand res = context.AddIntrinsic(Intrinsic.X86Pandn, n, mask);
|
||||
|
||||
if (op.RegisterSize == RegisterSize.Simd64)
|
||||
{
|
||||
res = context.VectorZeroUpper64(res);
|
||||
}
|
||||
|
||||
context.Copy(GetVec(op.Rd), res);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitVectorUnaryOpZx(context, (op1) => context.BitwiseNot(op1));
|
||||
}
|
||||
}
|
||||
|
||||
public static void Orn_V(ArmEmitterContext context)
|
||||
{
|
||||
if (Optimizations.UseSse2)
|
||||
{
|
||||
OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp;
|
||||
|
||||
Operand n = GetVec(op.Rn);
|
||||
Operand m = GetVec(op.Rm);
|
||||
|
||||
Operand mask = X86GetAllElements(context, -1L);
|
||||
|
||||
Operand res = context.AddIntrinsic(Intrinsic.X86Pandn, m, mask);
|
||||
|
||||
res = context.AddIntrinsic(Intrinsic.X86Por, res, n);
|
||||
|
||||
if (op.RegisterSize == RegisterSize.Simd64)
|
||||
{
|
||||
res = context.VectorZeroUpper64(res);
|
||||
}
|
||||
|
||||
context.Copy(GetVec(op.Rd), res);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitVectorBinaryOpZx(context, (op1, op2) =>
|
||||
{
|
||||
return context.BitwiseOr(op1, context.BitwiseNot(op2));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public static void Orr_V(ArmEmitterContext context)
|
||||
{
|
||||
if (Optimizations.UseSse2)
|
||||
{
|
||||
OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp;
|
||||
|
||||
Operand n = GetVec(op.Rn);
|
||||
Operand m = GetVec(op.Rm);
|
||||
|
||||
Operand res = context.AddIntrinsic(Intrinsic.X86Por, n, m);
|
||||
|
||||
if (op.RegisterSize == RegisterSize.Simd64)
|
||||
{
|
||||
res = context.VectorZeroUpper64(res);
|
||||
}
|
||||
|
||||
context.Copy(GetVec(op.Rd), res);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitVectorBinaryOpZx(context, (op1, op2) => context.BitwiseOr(op1, op2));
|
||||
}
|
||||
}
|
||||
|
||||
public static void Orr_Vi(ArmEmitterContext context)
|
||||
{
|
||||
EmitVectorImmBinaryOp(context, (op1, op2) => context.BitwiseOr(op1, op2));
|
||||
}
|
||||
|
||||
public static void Rbit_V(ArmEmitterContext context)
|
||||
{
|
||||
OpCodeSimd op = (OpCodeSimd)context.CurrOp;
|
||||
|
||||
Operand res = context.VectorZero();
|
||||
|
||||
int elems = op.RegisterSize == RegisterSize.Simd128 ? 16 : 8;
|
||||
|
||||
for (int index = 0; index < elems; index++)
|
||||
{
|
||||
Operand ne = EmitVectorExtractZx(context, op.Rn, index, 0);
|
||||
|
||||
ne = context.ConvertI64ToI32(ne);
|
||||
|
||||
Operand de = context.Call(new _U32_U32(SoftFallback.ReverseBits8), ne);
|
||||
|
||||
de = context.ZeroExtend32(OperandType.I64, de);
|
||||
|
||||
res = EmitVectorInsert(context, res, de, index, 0);
|
||||
}
|
||||
|
||||
context.Copy(GetVec(op.Rd), res);
|
||||
}
|
||||
|
||||
public static void Rev16_V(ArmEmitterContext context)
|
||||
{
|
||||
if (Optimizations.UseSsse3)
|
||||
{
|
||||
OpCodeSimd op = (OpCodeSimd)context.CurrOp;
|
||||
|
||||
Operand n = GetVec(op.Rn);
|
||||
|
||||
const long maskE0 = 06L << 56 | 07L << 48 | 04L << 40 | 05L << 32 | 02L << 24 | 03L << 16 | 00L << 8 | 01L << 0;
|
||||
const long maskE1 = 14L << 56 | 15L << 48 | 12L << 40 | 13L << 32 | 10L << 24 | 11L << 16 | 08L << 8 | 09L << 0;
|
||||
|
||||
Operand mask = X86GetScalar(context, maskE0);
|
||||
|
||||
mask = EmitVectorInsert(context, mask, Const(maskE1), 1, 3);
|
||||
|
||||
Operand res = context.AddIntrinsic(Intrinsic.X86Pshufb, n, mask);
|
||||
|
||||
if (op.RegisterSize == RegisterSize.Simd64)
|
||||
{
|
||||
res = context.VectorZeroUpper64(res);
|
||||
}
|
||||
|
||||
context.Copy(GetVec(op.Rd), res);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitRev_V(context, containerSize: 1);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Rev32_V(ArmEmitterContext context)
|
||||
{
|
||||
if (Optimizations.UseSsse3)
|
||||
{
|
||||
OpCodeSimd op = (OpCodeSimd)context.CurrOp;
|
||||
|
||||
Operand n = GetVec(op.Rn);
|
||||
|
||||
Operand mask;
|
||||
|
||||
if (op.Size == 0)
|
||||
{
|
||||
const long maskE0 = 04L << 56 | 05L << 48 | 06L << 40 | 07L << 32 | 00L << 24 | 01L << 16 | 02L << 8 | 03L << 0;
|
||||
const long maskE1 = 12L << 56 | 13L << 48 | 14L << 40 | 15L << 32 | 08L << 24 | 09L << 16 | 10L << 8 | 11L << 0;
|
||||
|
||||
mask = X86GetScalar(context, maskE0);
|
||||
|
||||
mask = EmitVectorInsert(context, mask, Const(maskE1), 1, 3);
|
||||
}
|
||||
else /* if (op.Size == 1) */
|
||||
{
|
||||
const long maskE0 = 05L << 56 | 04L << 48 | 07L << 40 | 06L << 32 | 01L << 24 | 00L << 16 | 03L << 8 | 02L << 0;
|
||||
const long maskE1 = 13L << 56 | 12L << 48 | 15L << 40 | 14L << 32 | 09L << 24 | 08L << 16 | 11L << 8 | 10L << 0;
|
||||
|
||||
mask = X86GetScalar(context, maskE0);
|
||||
|
||||
mask = EmitVectorInsert(context, mask, Const(maskE1), 1, 3);
|
||||
}
|
||||
|
||||
Operand res = context.AddIntrinsic(Intrinsic.X86Pshufb, n, mask);
|
||||
|
||||
if (op.RegisterSize == RegisterSize.Simd64)
|
||||
{
|
||||
res = context.VectorZeroUpper64(res);
|
||||
}
|
||||
|
||||
context.Copy(GetVec(op.Rd), res);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitRev_V(context, containerSize: 2);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Rev64_V(ArmEmitterContext context)
|
||||
{
|
||||
if (Optimizations.UseSsse3)
|
||||
{
|
||||
OpCodeSimd op = (OpCodeSimd)context.CurrOp;
|
||||
|
||||
Operand n = GetVec(op.Rn);
|
||||
|
||||
Operand mask;
|
||||
|
||||
if (op.Size == 0)
|
||||
{
|
||||
const long maskE0 = 00L << 56 | 01L << 48 | 02L << 40 | 03L << 32 | 04L << 24 | 05L << 16 | 06L << 8 | 07L << 0;
|
||||
const long maskE1 = 08L << 56 | 09L << 48 | 10L << 40 | 11L << 32 | 12L << 24 | 13L << 16 | 14L << 8 | 15L << 0;
|
||||
|
||||
mask = X86GetScalar(context, maskE0);
|
||||
|
||||
mask = EmitVectorInsert(context, mask, Const(maskE1), 1, 3);
|
||||
}
|
||||
else if (op.Size == 1)
|
||||
{
|
||||
const long maskE0 = 01L << 56 | 00L << 48 | 03L << 40 | 02L << 32 | 05L << 24 | 04L << 16 | 07L << 8 | 06L << 0;
|
||||
const long maskE1 = 09L << 56 | 08L << 48 | 11L << 40 | 10L << 32 | 13L << 24 | 12L << 16 | 15L << 8 | 14L << 0;
|
||||
|
||||
mask = X86GetScalar(context, maskE0);
|
||||
|
||||
mask = EmitVectorInsert(context, mask, Const(maskE1), 1, 3);
|
||||
}
|
||||
else /* if (op.Size == 2) */
|
||||
{
|
||||
const long maskE0 = 03L << 56 | 02L << 48 | 01L << 40 | 00L << 32 | 07L << 24 | 06L << 16 | 05L << 8 | 04L << 0;
|
||||
const long maskE1 = 11L << 56 | 10L << 48 | 09L << 40 | 08L << 32 | 15L << 24 | 14L << 16 | 13L << 8 | 12L << 0;
|
||||
|
||||
mask = X86GetScalar(context, maskE0);
|
||||
|
||||
mask = EmitVectorInsert(context, mask, Const(maskE1), 1, 3);
|
||||
}
|
||||
|
||||
Operand res = context.AddIntrinsic(Intrinsic.X86Pshufb, n, mask);
|
||||
|
||||
if (op.RegisterSize == RegisterSize.Simd64)
|
||||
{
|
||||
res = context.VectorZeroUpper64(res);
|
||||
}
|
||||
|
||||
context.Copy(GetVec(op.Rd), res);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitRev_V(context, containerSize: 3);
|
||||
}
|
||||
}
|
||||
|
||||
private static void EmitRev_V(ArmEmitterContext context, int containerSize)
|
||||
{
|
||||
OpCodeSimd op = (OpCodeSimd)context.CurrOp;
|
||||
|
||||
Operand res = context.VectorZero();
|
||||
|
||||
int elems = op.GetBytesCount() >> op.Size;
|
||||
|
||||
int containerMask = (1 << (containerSize - op.Size)) - 1;
|
||||
|
||||
for (int index = 0; index < elems; index++)
|
||||
{
|
||||
int revIndex = index ^ containerMask;
|
||||
|
||||
Operand ne = EmitVectorExtractZx(context, op.Rn, revIndex, op.Size);
|
||||
|
||||
res = EmitVectorInsert(context, res, ne, index, op.Size);
|
||||
}
|
||||
|
||||
context.Copy(GetVec(op.Rd), res);
|
||||
}
|
||||
}
|
||||
}
|
160
ARMeilleure/Instructions/InstEmitSimdMemory.cs
Normal file
160
ARMeilleure/Instructions/InstEmitSimdMemory.cs
Normal file
|
@ -0,0 +1,160 @@
|
|||
using ARMeilleure.Decoders;
|
||||
using ARMeilleure.IntermediateRepresentation;
|
||||
using ARMeilleure.State;
|
||||
using ARMeilleure.Translation;
|
||||
using System.Diagnostics;
|
||||
|
||||
using static ARMeilleure.Instructions.InstEmitHelper;
|
||||
using static ARMeilleure.Instructions.InstEmitMemoryHelper;
|
||||
using static ARMeilleure.IntermediateRepresentation.OperandHelper;
|
||||
|
||||
namespace ARMeilleure.Instructions
|
||||
{
|
||||
static partial class InstEmit
|
||||
{
|
||||
public static void Ld__Vms(ArmEmitterContext context)
|
||||
{
|
||||
EmitSimdMemMs(context, isLoad: true);
|
||||
}
|
||||
|
||||
public static void Ld__Vss(ArmEmitterContext context)
|
||||
{
|
||||
EmitSimdMemSs(context, isLoad: true);
|
||||
}
|
||||
|
||||
public static void St__Vms(ArmEmitterContext context)
|
||||
{
|
||||
EmitSimdMemMs(context, isLoad: false);
|
||||
}
|
||||
|
||||
public static void St__Vss(ArmEmitterContext context)
|
||||
{
|
||||
EmitSimdMemSs(context, isLoad: false);
|
||||
}
|
||||
|
||||
private static void EmitSimdMemMs(ArmEmitterContext context, bool isLoad)
|
||||
{
|
||||
OpCodeSimdMemMs op = (OpCodeSimdMemMs)context.CurrOp;
|
||||
|
||||
Operand n = GetIntOrSP(context, op.Rn);
|
||||
|
||||
long offset = 0;
|
||||
|
||||
for (int rep = 0; rep < op.Reps; rep++)
|
||||
for (int elem = 0; elem < op.Elems; elem++)
|
||||
for (int sElem = 0; sElem < op.SElems; sElem++)
|
||||
{
|
||||
int rtt = (op.Rt + rep + sElem) & 0x1f;
|
||||
|
||||
Operand tt = GetVec(rtt);
|
||||
|
||||
Operand address = context.Add(n, Const(offset));
|
||||
|
||||
if (isLoad)
|
||||
{
|
||||
EmitLoadSimd(context, address, tt, rtt, elem, op.Size);
|
||||
|
||||
if (op.RegisterSize == RegisterSize.Simd64 && elem == op.Elems - 1)
|
||||
{
|
||||
context.Copy(tt, context.VectorZeroUpper64(tt));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitStoreSimd(context, address, rtt, elem, op.Size);
|
||||
}
|
||||
|
||||
offset += 1 << op.Size;
|
||||
}
|
||||
|
||||
if (op.WBack)
|
||||
{
|
||||
EmitSimdMemWBack(context, offset);
|
||||
}
|
||||
}
|
||||
|
||||
private static void EmitSimdMemSs(ArmEmitterContext context, bool isLoad)
|
||||
{
|
||||
OpCodeSimdMemSs op = (OpCodeSimdMemSs)context.CurrOp;
|
||||
|
||||
Operand n = GetIntOrSP(context, op.Rn);
|
||||
|
||||
long offset = 0;
|
||||
|
||||
if (op.Replicate)
|
||||
{
|
||||
// Only loads uses the replicate mode.
|
||||
Debug.Assert(isLoad, "Replicate mode is not valid for stores.");
|
||||
|
||||
int elems = op.GetBytesCount() >> op.Size;
|
||||
|
||||
for (int sElem = 0; sElem < op.SElems; sElem++)
|
||||
{
|
||||
int rt = (op.Rt + sElem) & 0x1f;
|
||||
|
||||
Operand t = GetVec(rt);
|
||||
|
||||
Operand address = context.Add(n, Const(offset));
|
||||
|
||||
for (int index = 0; index < elems; index++)
|
||||
{
|
||||
EmitLoadSimd(context, address, t, rt, index, op.Size);
|
||||
}
|
||||
|
||||
if (op.RegisterSize == RegisterSize.Simd64)
|
||||
{
|
||||
context.Copy(t, context.VectorZeroUpper64(t));
|
||||
}
|
||||
|
||||
offset += 1 << op.Size;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int sElem = 0; sElem < op.SElems; sElem++)
|
||||
{
|
||||
int rt = (op.Rt + sElem) & 0x1f;
|
||||
|
||||
Operand t = GetVec(rt);
|
||||
|
||||
Operand address = context.Add(n, Const(offset));
|
||||
|
||||
if (isLoad)
|
||||
{
|
||||
EmitLoadSimd(context, address, t, rt, op.Index, op.Size);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitStoreSimd(context, address, rt, op.Index, op.Size);
|
||||
}
|
||||
|
||||
offset += 1 << op.Size;
|
||||
}
|
||||
}
|
||||
|
||||
if (op.WBack)
|
||||
{
|
||||
EmitSimdMemWBack(context, offset);
|
||||
}
|
||||
}
|
||||
|
||||
private static void EmitSimdMemWBack(ArmEmitterContext context, long offset)
|
||||
{
|
||||
OpCodeMemReg op = (OpCodeMemReg)context.CurrOp;
|
||||
|
||||
Operand n = GetIntOrSP(context, op.Rn);
|
||||
Operand m;
|
||||
|
||||
if (op.Rm != RegisterAlias.Zr)
|
||||
{
|
||||
m = GetIntOrZR(context, op.Rm);
|
||||
}
|
||||
else
|
||||
{
|
||||
m = Const(offset);
|
||||
}
|
||||
|
||||
context.Copy(n, context.Add(n, m));
|
||||
}
|
||||
}
|
||||
}
|
794
ARMeilleure/Instructions/InstEmitSimdMove.cs
Normal file
794
ARMeilleure/Instructions/InstEmitSimdMove.cs
Normal file
|
@ -0,0 +1,794 @@
|
|||
using ARMeilleure.Decoders;
|
||||
using ARMeilleure.IntermediateRepresentation;
|
||||
using ARMeilleure.Translation;
|
||||
using System;
|
||||
|
||||
using static ARMeilleure.Instructions.InstEmitHelper;
|
||||
using static ARMeilleure.Instructions.InstEmitSimdHelper;
|
||||
using static ARMeilleure.IntermediateRepresentation.OperandHelper;
|
||||
|
||||
namespace ARMeilleure.Instructions
|
||||
{
|
||||
static partial class InstEmit
|
||||
{
|
||||
#region "Masks"
|
||||
private static readonly long[] _masksE0_TrnUzpXtn = new long[]
|
||||
{
|
||||
14L << 56 | 12L << 48 | 10L << 40 | 08L << 32 | 06L << 24 | 04L << 16 | 02L << 8 | 00L << 0,
|
||||
13L << 56 | 12L << 48 | 09L << 40 | 08L << 32 | 05L << 24 | 04L << 16 | 01L << 8 | 00L << 0,
|
||||
11L << 56 | 10L << 48 | 09L << 40 | 08L << 32 | 03L << 24 | 02L << 16 | 01L << 8 | 00L << 0
|
||||
};
|
||||
|
||||
private static readonly long[] _masksE1_TrnUzp = new long[]
|
||||
{
|
||||
15L << 56 | 13L << 48 | 11L << 40 | 09L << 32 | 07L << 24 | 05L << 16 | 03L << 8 | 01L << 0,
|
||||
15L << 56 | 14L << 48 | 11L << 40 | 10L << 32 | 07L << 24 | 06L << 16 | 03L << 8 | 02L << 0,
|
||||
15L << 56 | 14L << 48 | 13L << 40 | 12L << 32 | 07L << 24 | 06L << 16 | 05L << 8 | 04L << 0
|
||||
};
|
||||
|
||||
private static readonly long[] _masksE0_Uzp = new long[]
|
||||
{
|
||||
13L << 56 | 09L << 48 | 05L << 40 | 01L << 32 | 12L << 24 | 08L << 16 | 04L << 8 | 00L << 0,
|
||||
11L << 56 | 10L << 48 | 03L << 40 | 02L << 32 | 09L << 24 | 08L << 16 | 01L << 8 | 00L << 0
|
||||
};
|
||||
|
||||
private static readonly long[] _masksE1_Uzp = new long[]
|
||||
{
|
||||
15L << 56 | 11L << 48 | 07L << 40 | 03L << 32 | 14L << 24 | 10L << 16 | 06L << 8 | 02L << 0,
|
||||
15L << 56 | 14L << 48 | 07L << 40 | 06L << 32 | 13L << 24 | 12L << 16 | 05L << 8 | 04L << 0
|
||||
};
|
||||
#endregion
|
||||
|
||||
public static void Dup_Gp(ArmEmitterContext context)
|
||||
{
|
||||
OpCodeSimdIns op = (OpCodeSimdIns)context.CurrOp;
|
||||
|
||||
Operand n = GetIntOrZR(context, op.Rn);
|
||||
|
||||
if (Optimizations.UseSse2)
|
||||
{
|
||||
switch (op.Size)
|
||||
{
|
||||
case 0: n = context.ZeroExtend8 (n.Type, n); n = context.Multiply(n, Const(n.Type, 0x01010101)); break;
|
||||
case 1: n = context.ZeroExtend16(n.Type, n); n = context.Multiply(n, Const(n.Type, 0x00010001)); break;
|
||||
case 2: n = context.ZeroExtend32(n.Type, n); break;
|
||||
}
|
||||
|
||||
Operand res = context.VectorInsert(context.VectorZero(), n, 0);
|
||||
|
||||
if (op.Size < 3)
|
||||
{
|
||||
if (op.RegisterSize == RegisterSize.Simd64)
|
||||
{
|
||||
res = context.AddIntrinsic(Intrinsic.X86Shufps, res, res, Const(0xf0));
|
||||
}
|
||||
else
|
||||
{
|
||||
res = context.AddIntrinsic(Intrinsic.X86Shufps, res, res, Const(0));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
res = context.AddIntrinsic(Intrinsic.X86Movlhps, res, res);
|
||||
}
|
||||
|
||||
context.Copy(GetVec(op.Rd), res);
|
||||
}
|
||||
else
|
||||
{
|
||||
Operand res = context.VectorZero();
|
||||
|
||||
int elems = op.GetBytesCount() >> op.Size;
|
||||
|
||||
for (int index = 0; index < elems; index++)
|
||||
{
|
||||
res = EmitVectorInsert(context, res, n, index, op.Size);
|
||||
}
|
||||
|
||||
context.Copy(GetVec(op.Rd), res);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Dup_S(ArmEmitterContext context)
|
||||
{
|
||||
OpCodeSimdIns op = (OpCodeSimdIns)context.CurrOp;
|
||||
|
||||
Operand ne = EmitVectorExtractZx(context, op.Rn, op.DstIndex, op.Size);
|
||||
|
||||
context.Copy(GetVec(op.Rd), EmitVectorInsert(context, context.VectorZero(), ne, 0, op.Size));
|
||||
}
|
||||
|
||||
public static void Dup_V(ArmEmitterContext context)
|
||||
{
|
||||
OpCodeSimdIns op = (OpCodeSimdIns)context.CurrOp;
|
||||
|
||||
if (Optimizations.UseSse2)
|
||||
{
|
||||
Operand res = GetVec(op.Rn);
|
||||
|
||||
if (op.Size == 0)
|
||||
{
|
||||
if (op.DstIndex != 0)
|
||||
{
|
||||
res = context.AddIntrinsic(Intrinsic.X86Psrldq, res, Const(op.DstIndex));
|
||||
}
|
||||
|
||||
res = context.AddIntrinsic(Intrinsic.X86Punpcklbw, res, res);
|
||||
res = context.AddIntrinsic(Intrinsic.X86Punpcklwd, res, res);
|
||||
res = context.AddIntrinsic(Intrinsic.X86Shufps, res, res, Const(0));
|
||||
}
|
||||
else if (op.Size == 1)
|
||||
{
|
||||
if (op.DstIndex != 0)
|
||||
{
|
||||
res = context.AddIntrinsic(Intrinsic.X86Psrldq, res, Const(op.DstIndex * 2));
|
||||
}
|
||||
|
||||
res = context.AddIntrinsic(Intrinsic.X86Punpcklwd, res, res);
|
||||
res = context.AddIntrinsic(Intrinsic.X86Shufps, res, res, Const(0));
|
||||
}
|
||||
else if (op.Size == 2)
|
||||
{
|
||||
int mask = op.DstIndex * 0b01010101;
|
||||
|
||||
res = context.AddIntrinsic(Intrinsic.X86Shufps, res, res, Const(mask));
|
||||
}
|
||||
else if (op.DstIndex == 0 && op.RegisterSize != RegisterSize.Simd64)
|
||||
{
|
||||
res = context.AddIntrinsic(Intrinsic.X86Movlhps, res, res);
|
||||
}
|
||||
else if (op.DstIndex == 1)
|
||||
{
|
||||
res = context.AddIntrinsic(Intrinsic.X86Movhlps, res, res);
|
||||
}
|
||||
|
||||
if (op.RegisterSize == RegisterSize.Simd64)
|
||||
{
|
||||
res = context.VectorZeroUpper64(res);
|
||||
}
|
||||
|
||||
context.Copy(GetVec(op.Rd), res);
|
||||
}
|
||||
else
|
||||
{
|
||||
Operand ne = EmitVectorExtractZx(context, op.Rn, op.DstIndex, op.Size);
|
||||
|
||||
Operand res = context.VectorZero();
|
||||
|
||||
int elems = op.GetBytesCount() >> op.Size;
|
||||
|
||||
for (int index = 0; index < elems; index++)
|
||||
{
|
||||
res = EmitVectorInsert(context, res, ne, index, op.Size);
|
||||
}
|
||||
|
||||
context.Copy(GetVec(op.Rd), res);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Ext_V(ArmEmitterContext context)
|
||||
{
|
||||
OpCodeSimdExt op = (OpCodeSimdExt)context.CurrOp;
|
||||
|
||||
if (Optimizations.UseSse2)
|
||||
{
|
||||
Operand nShifted = GetVec(op.Rn);
|
||||
|
||||
if (op.RegisterSize == RegisterSize.Simd64)
|
||||
{
|
||||
nShifted = context.AddIntrinsic(Intrinsic.X86Movlhps, nShifted, context.VectorZero());
|
||||
}
|
||||
|
||||
nShifted = context.AddIntrinsic(Intrinsic.X86Psrldq, nShifted, Const(op.Imm4));
|
||||
|
||||
Operand mShifted = GetVec(op.Rm);
|
||||
|
||||
mShifted = context.AddIntrinsic(Intrinsic.X86Pslldq, mShifted, Const(op.GetBytesCount() - op.Imm4));
|
||||
|
||||
if (op.RegisterSize == RegisterSize.Simd64)
|
||||
{
|
||||
mShifted = context.AddIntrinsic(Intrinsic.X86Movlhps, mShifted, context.VectorZero());
|
||||
}
|
||||
|
||||
Operand res = context.AddIntrinsic(Intrinsic.X86Por, nShifted, mShifted);
|
||||
|
||||
context.Copy(GetVec(op.Rd), res);
|
||||
}
|
||||
else
|
||||
{
|
||||
Operand res = context.VectorZero();
|
||||
|
||||
int bytes = op.GetBytesCount();
|
||||
|
||||
int position = op.Imm4 & (bytes - 1);
|
||||
|
||||
for (int index = 0; index < bytes; index++)
|
||||
{
|
||||
int reg = op.Imm4 + index < bytes ? op.Rn : op.Rm;
|
||||
|
||||
Operand e = EmitVectorExtractZx(context, reg, position, 0);
|
||||
|
||||
position = (position + 1) & (bytes - 1);
|
||||
|
||||
res = EmitVectorInsert(context, res, e, index, 0);
|
||||
}
|
||||
|
||||
context.Copy(GetVec(op.Rd), res);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Fcsel_S(ArmEmitterContext context)
|
||||
{
|
||||
OpCodeSimdFcond op = (OpCodeSimdFcond)context.CurrOp;
|
||||
|
||||
Operand lblTrue = Label();
|
||||
Operand lblEnd = Label();
|
||||
|
||||
Operand isTrue = InstEmitFlowHelper.GetCondTrue(context, op.Cond);
|
||||
|
||||
context.BranchIfTrue(lblTrue, isTrue);
|
||||
|
||||
OperandType type = op.Size == 0 ? OperandType.FP32 : OperandType.FP64;
|
||||
|
||||
Operand me = context.VectorExtract(type, GetVec(op.Rm), 0);
|
||||
|
||||
context.Copy(GetVec(op.Rd), context.VectorInsert(context.VectorZero(), me, 0));
|
||||
|
||||
context.Branch(lblEnd);
|
||||
|
||||
context.MarkLabel(lblTrue);
|
||||
|
||||
Operand ne = context.VectorExtract(type, GetVec(op.Rn), 0);
|
||||
|
||||
context.Copy(GetVec(op.Rd), context.VectorInsert(context.VectorZero(), ne, 0));
|
||||
|
||||
context.MarkLabel(lblEnd);
|
||||
}
|
||||
|
||||
public static void Fmov_Ftoi(ArmEmitterContext context)
|
||||
{
|
||||
OpCodeSimd op = (OpCodeSimd)context.CurrOp;
|
||||
|
||||
Operand ne = EmitVectorExtractZx(context, op.Rn, 0, op.Size + 2);
|
||||
|
||||
SetIntOrZR(context, op.Rd, ne);
|
||||
}
|
||||
|
||||
public static void Fmov_Ftoi1(ArmEmitterContext context)
|
||||
{
|
||||
OpCodeSimd op = (OpCodeSimd)context.CurrOp;
|
||||
|
||||
Operand ne = EmitVectorExtractZx(context, op.Rn, 1, 3);
|
||||
|
||||
SetIntOrZR(context, op.Rd, ne);
|
||||
}
|
||||
|
||||
public static void Fmov_Itof(ArmEmitterContext context)
|
||||
{
|
||||
OpCodeSimd op = (OpCodeSimd)context.CurrOp;
|
||||
|
||||
Operand n = GetIntOrZR(context, op.Rn);
|
||||
|
||||
context.Copy(GetVec(op.Rd), EmitVectorInsert(context, context.VectorZero(), n, 0, op.Size + 2));
|
||||
}
|
||||
|
||||
public static void Fmov_Itof1(ArmEmitterContext context)
|
||||
{
|
||||
OpCodeSimd op = (OpCodeSimd)context.CurrOp;
|
||||
|
||||
Operand n = GetIntOrZR(context, op.Rn);
|
||||
|
||||
context.Copy(GetVec(op.Rd), EmitVectorInsert(context, GetVec(op.Rd), n, 1, 3));
|
||||
}
|
||||
|
||||
public static void Fmov_S(ArmEmitterContext context)
|
||||
{
|
||||
OpCodeSimd op = (OpCodeSimd)context.CurrOp;
|
||||
|
||||
OperandType type = op.Size == 0 ? OperandType.FP32 : OperandType.FP64;
|
||||
|
||||
Operand ne = context.VectorExtract(type, GetVec(op.Rn), 0);
|
||||
|
||||
context.Copy(GetVec(op.Rd), context.VectorInsert(context.VectorZero(), ne, 0));
|
||||
}
|
||||
|
||||
public static void Fmov_Si(ArmEmitterContext context)
|
||||
{
|
||||
OpCodeSimdFmov op = (OpCodeSimdFmov)context.CurrOp;
|
||||
|
||||
if (op.Size == 0)
|
||||
{
|
||||
context.Copy(GetVec(op.Rd), X86GetScalar(context, (int)op.Immediate));
|
||||
}
|
||||
else
|
||||
{
|
||||
context.Copy(GetVec(op.Rd), X86GetScalar(context, op.Immediate));
|
||||
}
|
||||
}
|
||||
|
||||
public static void Fmov_Vi(ArmEmitterContext context)
|
||||
{
|
||||
OpCodeSimdImm op = (OpCodeSimdImm)context.CurrOp;
|
||||
|
||||
Operand e = Const(op.Immediate);
|
||||
|
||||
Operand res = context.VectorZero();
|
||||
|
||||
int elems = op.RegisterSize == RegisterSize.Simd128 ? 4 : 2;
|
||||
|
||||
for (int index = 0; index < (elems >> op.Size); index++)
|
||||
{
|
||||
res = EmitVectorInsert(context, res, e, index, op.Size + 2);
|
||||
}
|
||||
|
||||
context.Copy(GetVec(op.Rd), res);
|
||||
}
|
||||
|
||||
public static void Ins_Gp(ArmEmitterContext context)
|
||||
{
|
||||
OpCodeSimdIns op = (OpCodeSimdIns)context.CurrOp;
|
||||
|
||||
Operand d = GetVec(op.Rd);
|
||||
Operand n = GetIntOrZR(context, op.Rn);
|
||||
|
||||
context.Copy(d, EmitVectorInsert(context, d, n, op.DstIndex, op.Size));
|
||||
}
|
||||
|
||||
public static void Ins_V(ArmEmitterContext context)
|
||||
{
|
||||
OpCodeSimdIns op = (OpCodeSimdIns)context.CurrOp;
|
||||
|
||||
Operand d = GetVec(op.Rd);
|
||||
Operand ne = EmitVectorExtractZx(context, op.Rn, op.SrcIndex, op.Size);
|
||||
|
||||
context.Copy(d, EmitVectorInsert(context, d, ne, op.DstIndex, op.Size));
|
||||
}
|
||||
|
||||
public static void Movi_V(ArmEmitterContext context)
|
||||
{
|
||||
if (Optimizations.UseSse2)
|
||||
{
|
||||
EmitMoviMvni(context, not: false);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitVectorImmUnaryOp(context, (op1) => op1);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Mvni_V(ArmEmitterContext context)
|
||||
{
|
||||
if (Optimizations.UseSse2)
|
||||
{
|
||||
EmitMoviMvni(context, not: true);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitVectorImmUnaryOp(context, (op1) => context.BitwiseNot(op1));
|
||||
}
|
||||
}
|
||||
|
||||
public static void Smov_S(ArmEmitterContext context)
|
||||
{
|
||||
OpCodeSimdIns op = (OpCodeSimdIns)context.CurrOp;
|
||||
|
||||
Operand ne = EmitVectorExtractSx(context, op.Rn, op.DstIndex, op.Size);
|
||||
|
||||
if (op.RegisterSize == RegisterSize.Simd64)
|
||||
{
|
||||
ne = context.ZeroExtend32(OperandType.I64, ne);
|
||||
}
|
||||
|
||||
SetIntOrZR(context, op.Rd, ne);
|
||||
}
|
||||
|
||||
public static void Tbl_V(ArmEmitterContext context)
|
||||
{
|
||||
OpCodeSimdTbl op = (OpCodeSimdTbl)context.CurrOp;
|
||||
|
||||
if (Optimizations.UseSsse3)
|
||||
{
|
||||
Operand n = GetVec(op.Rn);
|
||||
Operand m = GetVec(op.Rm);
|
||||
|
||||
Operand mask = X86GetAllElements(context, 0x0F0F0F0F0F0F0F0FL);
|
||||
|
||||
Operand mMask = context.AddIntrinsic(Intrinsic.X86Pcmpgtb, m, mask);
|
||||
|
||||
mMask = context.AddIntrinsic(Intrinsic.X86Por, mMask, m);
|
||||
|
||||
Operand res = context.AddIntrinsic(Intrinsic.X86Pshufb, n, mMask);
|
||||
|
||||
for (int index = 1; index < op.Size; index++)
|
||||
{
|
||||
Operand ni = GetVec((op.Rn + index) & 0x1f);
|
||||
|
||||
Operand indexMask = X86GetAllElements(context, 0x1010101010101010L * index);
|
||||
|
||||
Operand mMinusMask = context.AddIntrinsic(Intrinsic.X86Psubb, m, indexMask);
|
||||
|
||||
Operand mMask2 = context.AddIntrinsic(Intrinsic.X86Pcmpgtb, mMinusMask, mask);
|
||||
|
||||
mMask2 = context.AddIntrinsic(Intrinsic.X86Por, mMask2, mMinusMask);
|
||||
|
||||
Operand res2 = context.AddIntrinsic(Intrinsic.X86Pshufb, ni, mMask2);
|
||||
|
||||
res = context.AddIntrinsic(Intrinsic.X86Por, res, res2);
|
||||
}
|
||||
|
||||
if (op.RegisterSize == RegisterSize.Simd64)
|
||||
{
|
||||
res = context.VectorZeroUpper64(res);
|
||||
}
|
||||
|
||||
context.Copy(GetVec(op.Rd), res);
|
||||
}
|
||||
else
|
||||
{
|
||||
Operand[] args = new Operand[1 + op.Size];
|
||||
|
||||
args[0] = GetVec(op.Rm);
|
||||
|
||||
for (int index = 0; index < op.Size; index++)
|
||||
{
|
||||
args[1 + index] = GetVec((op.Rn + index) & 0x1f);
|
||||
}
|
||||
|
||||
Delegate dlg = null;
|
||||
|
||||
switch (op.Size)
|
||||
{
|
||||
case 1: dlg = op.RegisterSize == RegisterSize.Simd64
|
||||
? (Delegate)new _V128_V128_V128(SoftFallback.Tbl1_V64)
|
||||
: (Delegate)new _V128_V128_V128(SoftFallback.Tbl1_V128); break;
|
||||
|
||||
case 2: dlg = op.RegisterSize == RegisterSize.Simd64
|
||||
? (Delegate)new _V128_V128_V128_V128(SoftFallback.Tbl2_V64)
|
||||
: (Delegate)new _V128_V128_V128_V128(SoftFallback.Tbl2_V128); break;
|
||||
|
||||
case 3: dlg = op.RegisterSize == RegisterSize.Simd64
|
||||
? (Delegate)new _V128_V128_V128_V128_V128(SoftFallback.Tbl3_V64)
|
||||
: (Delegate)new _V128_V128_V128_V128_V128(SoftFallback.Tbl3_V128); break;
|
||||
|
||||
case 4: dlg = op.RegisterSize == RegisterSize.Simd64
|
||||
? (Delegate)new _V128_V128_V128_V128_V128_V128(SoftFallback.Tbl4_V64)
|
||||
: (Delegate)new _V128_V128_V128_V128_V128_V128(SoftFallback.Tbl4_V128); break;
|
||||
}
|
||||
|
||||
context.Copy(GetVec(op.Rd), context.Call(dlg, args));
|
||||
}
|
||||
}
|
||||
|
||||
public static void Trn1_V(ArmEmitterContext context)
|
||||
{
|
||||
EmitVectorTranspose(context, part: 0);
|
||||
}
|
||||
|
||||
public static void Trn2_V(ArmEmitterContext context)
|
||||
{
|
||||
EmitVectorTranspose(context, part: 1);
|
||||
}
|
||||
|
||||
public static void Umov_S(ArmEmitterContext context)
|
||||
{
|
||||
OpCodeSimdIns op = (OpCodeSimdIns)context.CurrOp;
|
||||
|
||||
Operand ne = EmitVectorExtractZx(context, op.Rn, op.DstIndex, op.Size);
|
||||
|
||||
SetIntOrZR(context, op.Rd, ne);
|
||||
}
|
||||
|
||||
public static void Uzp1_V(ArmEmitterContext context)
|
||||
{
|
||||
EmitVectorUnzip(context, part: 0);
|
||||
}
|
||||
|
||||
public static void Uzp2_V(ArmEmitterContext context)
|
||||
{
|
||||
EmitVectorUnzip(context, part: 1);
|
||||
}
|
||||
|
||||
public static void Xtn_V(ArmEmitterContext context)
|
||||
{
|
||||
OpCodeSimd op = (OpCodeSimd)context.CurrOp;
|
||||
|
||||
if (Optimizations.UseSsse3)
|
||||
{
|
||||
Operand d = GetVec(op.Rd);
|
||||
|
||||
Operand res = context.AddIntrinsic(Intrinsic.X86Movlhps, d, context.VectorZero());
|
||||
|
||||
Operand n = GetVec(op.Rn);
|
||||
|
||||
Operand mask = X86GetAllElements(context, _masksE0_TrnUzpXtn[op.Size]);
|
||||
|
||||
Operand res2 = context.AddIntrinsic(Intrinsic.X86Pshufb, n, mask);
|
||||
|
||||
Intrinsic movInst = op.RegisterSize == RegisterSize.Simd128
|
||||
? Intrinsic.X86Movlhps
|
||||
: Intrinsic.X86Movhlps;
|
||||
|
||||
res = context.AddIntrinsic(movInst, res, res2);
|
||||
|
||||
context.Copy(GetVec(op.Rd), res);
|
||||
}
|
||||
else
|
||||
{
|
||||
int elems = 8 >> op.Size;
|
||||
|
||||
int part = op.RegisterSize == RegisterSize.Simd128 ? elems : 0;
|
||||
|
||||
Operand res = part == 0 ? context.VectorZero() : context.Copy(GetVec(op.Rd));
|
||||
|
||||
for (int index = 0; index < elems; index++)
|
||||
{
|
||||
Operand ne = EmitVectorExtractZx(context, op.Rn, index, op.Size + 1);
|
||||
|
||||
res = EmitVectorInsert(context, res, ne, part + index, op.Size);
|
||||
}
|
||||
|
||||
context.Copy(GetVec(op.Rd), res);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Zip1_V(ArmEmitterContext context)
|
||||
{
|
||||
EmitVectorZip(context, part: 0);
|
||||
}
|
||||
|
||||
public static void Zip2_V(ArmEmitterContext context)
|
||||
{
|
||||
EmitVectorZip(context, part: 1);
|
||||
}
|
||||
|
||||
private static void EmitMoviMvni(ArmEmitterContext context, bool not)
|
||||
{
|
||||
OpCodeSimdImm op = (OpCodeSimdImm)context.CurrOp;
|
||||
|
||||
long imm = op.Immediate;
|
||||
|
||||
switch (op.Size)
|
||||
{
|
||||
case 0: imm *= 0x01010101; break;
|
||||
case 1: imm *= 0x00010001; break;
|
||||
}
|
||||
|
||||
if (not)
|
||||
{
|
||||
imm = ~imm;
|
||||
}
|
||||
|
||||
Operand mask;
|
||||
|
||||
if (op.Size < 3)
|
||||
{
|
||||
mask = X86GetAllElements(context, (int)imm);
|
||||
}
|
||||
else
|
||||
{
|
||||
mask = X86GetAllElements(context, imm);
|
||||
}
|
||||
|
||||
if (op.RegisterSize == RegisterSize.Simd64)
|
||||
{
|
||||
mask = context.VectorZeroUpper64(mask);
|
||||
}
|
||||
|
||||
context.Copy(GetVec(op.Rd), mask);
|
||||
}
|
||||
|
||||
private static void EmitVectorTranspose(ArmEmitterContext context, int part)
|
||||
{
|
||||
OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp;
|
||||
|
||||
if (Optimizations.UseSsse3)
|
||||
{
|
||||
Operand mask = null;
|
||||
|
||||
if (op.Size < 3)
|
||||
{
|
||||
long maskE0 = _masksE0_TrnUzpXtn[op.Size];
|
||||
long maskE1 = _masksE1_TrnUzp [op.Size];
|
||||
|
||||
mask = X86GetScalar(context, maskE0);
|
||||
|
||||
mask = EmitVectorInsert(context, mask, Const(maskE1), 1, 3);
|
||||
}
|
||||
|
||||
Operand n = GetVec(op.Rn);
|
||||
|
||||
if (op.Size < 3)
|
||||
{
|
||||
n = context.AddIntrinsic(Intrinsic.X86Pshufb, n, mask);
|
||||
}
|
||||
|
||||
Operand m = GetVec(op.Rm);
|
||||
|
||||
if (op.Size < 3)
|
||||
{
|
||||
m = context.AddIntrinsic(Intrinsic.X86Pshufb, m, mask);
|
||||
}
|
||||
|
||||
Intrinsic punpckInst = part == 0
|
||||
? X86PunpcklInstruction[op.Size]
|
||||
: X86PunpckhInstruction[op.Size];
|
||||
|
||||
Operand res = context.AddIntrinsic(punpckInst, n, m);
|
||||
|
||||
if (op.RegisterSize == RegisterSize.Simd64)
|
||||
{
|
||||
res = context.VectorZeroUpper64(res);
|
||||
}
|
||||
|
||||
context.Copy(GetVec(op.Rd), res);
|
||||
}
|
||||
else
|
||||
{
|
||||
Operand res = context.VectorZero();
|
||||
|
||||
int pairs = op.GetPairsCount() >> op.Size;
|
||||
|
||||
for (int index = 0; index < pairs; index++)
|
||||
{
|
||||
int pairIndex = index << 1;
|
||||
|
||||
Operand ne = EmitVectorExtractZx(context, op.Rn, pairIndex + part, op.Size);
|
||||
Operand me = EmitVectorExtractZx(context, op.Rm, pairIndex + part, op.Size);
|
||||
|
||||
res = EmitVectorInsert(context, res, ne, pairIndex, op.Size);
|
||||
res = EmitVectorInsert(context, res, me, pairIndex + 1, op.Size);
|
||||
}
|
||||
|
||||
context.Copy(GetVec(op.Rd), res);
|
||||
}
|
||||
}
|
||||
|
||||
private static void EmitVectorUnzip(ArmEmitterContext context, int part)
|
||||
{
|
||||
OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp;
|
||||
|
||||
if (Optimizations.UseSsse3)
|
||||
{
|
||||
if (op.RegisterSize == RegisterSize.Simd128)
|
||||
{
|
||||
Operand mask = null;
|
||||
|
||||
if (op.Size < 3)
|
||||
{
|
||||
long maskE0 = _masksE0_TrnUzpXtn[op.Size];
|
||||
long maskE1 = _masksE1_TrnUzp [op.Size];
|
||||
|
||||
mask = X86GetScalar(context, maskE0);
|
||||
|
||||
mask = EmitVectorInsert(context, mask, Const(maskE1), 1, 3);
|
||||
}
|
||||
|
||||
Operand n = GetVec(op.Rn);
|
||||
|
||||
if (op.Size < 3)
|
||||
{
|
||||
n = context.AddIntrinsic(Intrinsic.X86Pshufb, n, mask);
|
||||
}
|
||||
|
||||
Operand m = GetVec(op.Rm);
|
||||
|
||||
if (op.Size < 3)
|
||||
{
|
||||
m = context.AddIntrinsic(Intrinsic.X86Pshufb, m, mask);
|
||||
}
|
||||
|
||||
Intrinsic punpckInst = part == 0
|
||||
? Intrinsic.X86Punpcklqdq
|
||||
: Intrinsic.X86Punpckhqdq;
|
||||
|
||||
Operand res = context.AddIntrinsic(punpckInst, n, m);
|
||||
|
||||
context.Copy(GetVec(op.Rd), res);
|
||||
}
|
||||
else
|
||||
{
|
||||
Operand n = GetVec(op.Rn);
|
||||
Operand m = GetVec(op.Rm);
|
||||
|
||||
Intrinsic punpcklInst = X86PunpcklInstruction[op.Size];
|
||||
|
||||
Operand res = context.AddIntrinsic(punpcklInst, n, m);
|
||||
|
||||
if (op.Size < 2)
|
||||
{
|
||||
long maskE0 = _masksE0_Uzp[op.Size];
|
||||
long maskE1 = _masksE1_Uzp[op.Size];
|
||||
|
||||
Operand mask = X86GetScalar(context, maskE0);
|
||||
|
||||
mask = EmitVectorInsert(context, mask, Const(maskE1), 1, 3);
|
||||
|
||||
res = context.AddIntrinsic(Intrinsic.X86Pshufb, res, mask);
|
||||
}
|
||||
|
||||
Intrinsic punpckInst = part == 0
|
||||
? Intrinsic.X86Punpcklqdq
|
||||
: Intrinsic.X86Punpckhqdq;
|
||||
|
||||
res = context.AddIntrinsic(punpckInst, res, context.VectorZero());
|
||||
|
||||
context.Copy(GetVec(op.Rd), res);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Operand res = context.VectorZero();
|
||||
|
||||
int pairs = op.GetPairsCount() >> op.Size;
|
||||
|
||||
for (int index = 0; index < pairs; index++)
|
||||
{
|
||||
int idx = index << 1;
|
||||
|
||||
Operand ne = EmitVectorExtractZx(context, op.Rn, idx + part, op.Size);
|
||||
Operand me = EmitVectorExtractZx(context, op.Rm, idx + part, op.Size);
|
||||
|
||||
res = EmitVectorInsert(context, res, ne, index, op.Size);
|
||||
res = EmitVectorInsert(context, res, me, pairs + index, op.Size);
|
||||
}
|
||||
|
||||
context.Copy(GetVec(op.Rd), res);
|
||||
}
|
||||
}
|
||||
|
||||
private static void EmitVectorZip(ArmEmitterContext context, int part)
|
||||
{
|
||||
OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp;
|
||||
|
||||
if (Optimizations.UseSse2)
|
||||
{
|
||||
Operand n = GetVec(op.Rn);
|
||||
Operand m = GetVec(op.Rm);
|
||||
|
||||
if (op.RegisterSize == RegisterSize.Simd128)
|
||||
{
|
||||
Intrinsic punpckInst = part == 0
|
||||
? X86PunpcklInstruction[op.Size]
|
||||
: X86PunpckhInstruction[op.Size];
|
||||
|
||||
Operand res = context.AddIntrinsic(punpckInst, n, m);
|
||||
|
||||
context.Copy(GetVec(op.Rd), res);
|
||||
}
|
||||
else
|
||||
{
|
||||
Operand res = context.AddIntrinsic(X86PunpcklInstruction[op.Size], n, m);
|
||||
|
||||
Intrinsic punpckInst = part == 0
|
||||
? Intrinsic.X86Punpcklqdq
|
||||
: Intrinsic.X86Punpckhqdq;
|
||||
|
||||
res = context.AddIntrinsic(punpckInst, res, context.VectorZero());
|
||||
|
||||
context.Copy(GetVec(op.Rd), res);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Operand res = context.VectorZero();
|
||||
|
||||
int pairs = op.GetPairsCount() >> op.Size;
|
||||
|
||||
int baseIndex = part != 0 ? pairs : 0;
|
||||
|
||||
for (int index = 0; index < pairs; index++)
|
||||
{
|
||||
int pairIndex = index << 1;
|
||||
|
||||
Operand ne = EmitVectorExtractZx(context, op.Rn, baseIndex + index, op.Size);
|
||||
Operand me = EmitVectorExtractZx(context, op.Rm, baseIndex + index, op.Size);
|
||||
|
||||
res = EmitVectorInsert(context, res, ne, pairIndex, op.Size);
|
||||
res = EmitVectorInsert(context, res, me, pairIndex + 1, op.Size);
|
||||
}
|
||||
|
||||
context.Copy(GetVec(op.Rd), res);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
1057
ARMeilleure/Instructions/InstEmitSimdShift.cs
Normal file
1057
ARMeilleure/Instructions/InstEmitSimdShift.cs
Normal file
File diff suppressed because it is too large
Load diff
114
ARMeilleure/Instructions/InstEmitSystem.cs
Normal file
114
ARMeilleure/Instructions/InstEmitSystem.cs
Normal file
|
@ -0,0 +1,114 @@
|
|||
using ARMeilleure.Decoders;
|
||||
using ARMeilleure.IntermediateRepresentation;
|
||||
using ARMeilleure.Translation;
|
||||
using System;
|
||||
|
||||
using static ARMeilleure.Instructions.InstEmitHelper;
|
||||
using static ARMeilleure.IntermediateRepresentation.OperandHelper;
|
||||
|
||||
namespace ARMeilleure.Instructions
|
||||
{
|
||||
static partial class InstEmit
|
||||
{
|
||||
private const int DczSizeLog2 = 4;
|
||||
|
||||
public static void Hint(ArmEmitterContext context)
|
||||
{
|
||||
// Execute as no-op.
|
||||
}
|
||||
|
||||
public static void Isb(ArmEmitterContext context)
|
||||
{
|
||||
// Execute as no-op.
|
||||
}
|
||||
|
||||
public static void Mrs(ArmEmitterContext context)
|
||||
{
|
||||
OpCodeSystem op = (OpCodeSystem)context.CurrOp;
|
||||
|
||||
Delegate dlg;
|
||||
|
||||
switch (GetPackedId(op))
|
||||
{
|
||||
case 0b11_011_0000_0000_001: dlg = new _U64(NativeInterface.GetCtrEl0); break;
|
||||
case 0b11_011_0000_0000_111: dlg = new _U64(NativeInterface.GetDczidEl0); break;
|
||||
case 0b11_011_0100_0100_000: dlg = new _U64(NativeInterface.GetFpcr); break;
|
||||
case 0b11_011_0100_0100_001: dlg = new _U64(NativeInterface.GetFpsr); break;
|
||||
case 0b11_011_1101_0000_010: dlg = new _U64(NativeInterface.GetTpidrEl0); break;
|
||||
case 0b11_011_1101_0000_011: dlg = new _U64(NativeInterface.GetTpidr); break;
|
||||
case 0b11_011_1110_0000_000: dlg = new _U64(NativeInterface.GetCntfrqEl0); break;
|
||||
case 0b11_011_1110_0000_001: dlg = new _U64(NativeInterface.GetCntpctEl0); break;
|
||||
|
||||
default: throw new NotImplementedException($"Unknown MRS 0x{op.RawOpCode:X8} at 0x{op.Address:X16}.");
|
||||
}
|
||||
|
||||
SetIntOrZR(context, op.Rt, context.Call(dlg));
|
||||
}
|
||||
|
||||
public static void Msr(ArmEmitterContext context)
|
||||
{
|
||||
OpCodeSystem op = (OpCodeSystem)context.CurrOp;
|
||||
|
||||
Delegate dlg;
|
||||
|
||||
switch (GetPackedId(op))
|
||||
{
|
||||
case 0b11_011_0100_0100_000: dlg = new _Void_U64(NativeInterface.SetFpcr); break;
|
||||
case 0b11_011_0100_0100_001: dlg = new _Void_U64(NativeInterface.SetFpsr); break;
|
||||
case 0b11_011_1101_0000_010: dlg = new _Void_U64(NativeInterface.SetTpidrEl0); break;
|
||||
|
||||
default: throw new NotImplementedException($"Unknown MSR 0x{op.RawOpCode:X8} at 0x{op.Address:X16}.");
|
||||
}
|
||||
|
||||
context.Call(dlg, GetIntOrZR(context, op.Rt));
|
||||
}
|
||||
|
||||
public static void Nop(ArmEmitterContext context)
|
||||
{
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
public static void Sys(ArmEmitterContext context)
|
||||
{
|
||||
// This instruction is used to do some operations on the CPU like cache invalidation,
|
||||
// address translation and the like.
|
||||
// We treat it as no-op here since we don't have any cache being emulated anyway.
|
||||
OpCodeSystem op = (OpCodeSystem)context.CurrOp;
|
||||
|
||||
switch (GetPackedId(op))
|
||||
{
|
||||
case 0b11_011_0111_0100_001:
|
||||
{
|
||||
// DC ZVA
|
||||
Operand t = GetIntOrZR(context, op.Rt);
|
||||
|
||||
for (long offset = 0; offset < (4 << DczSizeLog2); offset += 8)
|
||||
{
|
||||
Operand address = context.Add(t, Const(offset));
|
||||
|
||||
context.Call(new _Void_U64_U64(NativeInterface.WriteUInt64), address, Const(0L));
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// No-op
|
||||
case 0b11_011_0111_1110_001: //DC CIVAC
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private static int GetPackedId(OpCodeSystem op)
|
||||
{
|
||||
int id;
|
||||
|
||||
id = op.Op2 << 0;
|
||||
id |= op.CRm << 3;
|
||||
id |= op.CRn << 7;
|
||||
id |= op.Op1 << 11;
|
||||
id |= op.Op0 << 14;
|
||||
|
||||
return id;
|
||||
}
|
||||
}
|
||||
}
|
459
ARMeilleure/Instructions/InstName.cs
Normal file
459
ARMeilleure/Instructions/InstName.cs
Normal file
|
@ -0,0 +1,459 @@
|
|||
namespace ARMeilleure.Instructions
|
||||
{
|
||||
enum InstName
|
||||
{
|
||||
// Base (AArch64)
|
||||
Adc,
|
||||
Adcs,
|
||||
Add,
|
||||
Adds,
|
||||
Adr,
|
||||
Adrp,
|
||||
And,
|
||||
Ands,
|
||||
Asrv,
|
||||
B,
|
||||
B_Cond,
|
||||
Bfm,
|
||||
Bic,
|
||||
Bics,
|
||||
Bl,
|
||||
Blr,
|
||||
Br,
|
||||
Brk,
|
||||
Cbnz,
|
||||
Cbz,
|
||||
Ccmn,
|
||||
Ccmp,
|
||||
Clrex,
|
||||
Cls,
|
||||
Clz,
|
||||
Crc32b,
|
||||
Crc32h,
|
||||
Crc32w,
|
||||
Crc32x,
|
||||
Crc32cb,
|
||||
Crc32ch,
|
||||
Crc32cw,
|
||||
Crc32cx,
|
||||
Csel,
|
||||
Csinc,
|
||||
Csinv,
|
||||
Csneg,
|
||||
Dmb,
|
||||
Dsb,
|
||||
Eon,
|
||||
Eor,
|
||||
Extr,
|
||||
Hint,
|
||||
Isb,
|
||||
Ldar,
|
||||
Ldaxp,
|
||||
Ldaxr,
|
||||
Ldp,
|
||||
Ldr,
|
||||
Ldr_Literal,
|
||||
Ldrs,
|
||||
Ldxr,
|
||||
Ldxp,
|
||||
Lslv,
|
||||
Lsrv,
|
||||
Madd,
|
||||
Movk,
|
||||
Movn,
|
||||
Movz,
|
||||
Mrs,
|
||||
Msr,
|
||||
Msub,
|
||||
Nop,
|
||||
Orn,
|
||||
Orr,
|
||||
Pfrm,
|
||||
Rbit,
|
||||
Ret,
|
||||
Rev16,
|
||||
Rev32,
|
||||
Rev64,
|
||||
Rorv,
|
||||
Sbc,
|
||||
Sbcs,
|
||||
Sbfm,
|
||||
Sdiv,
|
||||
Smaddl,
|
||||
Smsubl,
|
||||
Smulh,
|
||||
Stlr,
|
||||
Stlxp,
|
||||
Stlxr,
|
||||
Stp,
|
||||
Str,
|
||||
Stxp,
|
||||
Stxr,
|
||||
Sub,
|
||||
Subs,
|
||||
Svc,
|
||||
Sys,
|
||||
Tbnz,
|
||||
Tbz,
|
||||
Ubfm,
|
||||
Udiv,
|
||||
Umaddl,
|
||||
Umsubl,
|
||||
Umulh,
|
||||
Und,
|
||||
|
||||
// FP & SIMD (AArch64)
|
||||
Abs_S,
|
||||
Abs_V,
|
||||
Add_S,
|
||||
Add_V,
|
||||
Addhn_V,
|
||||
Addp_S,
|
||||
Addp_V,
|
||||
Addv_V,
|
||||
Aesd_V,
|
||||
Aese_V,
|
||||
Aesimc_V,
|
||||
Aesmc_V,
|
||||
And_V,
|
||||
Bic_V,
|
||||
Bic_Vi,
|
||||
Bif_V,
|
||||
Bit_V,
|
||||
Bsl_V,
|
||||
Cls_V,
|
||||
Clz_V,
|
||||
Cmeq_S,
|
||||
Cmeq_V,
|
||||
Cmge_S,
|
||||
Cmge_V,
|
||||
Cmgt_S,
|
||||
Cmgt_V,
|
||||
Cmhi_S,
|
||||
Cmhi_V,
|
||||
Cmhs_S,
|
||||
Cmhs_V,
|
||||
Cmle_S,
|
||||
Cmle_V,
|
||||
Cmlt_S,
|
||||
Cmlt_V,
|
||||
Cmtst_S,
|
||||
Cmtst_V,
|
||||
Cnt_V,
|
||||
Dup_Gp,
|
||||
Dup_S,
|
||||
Dup_V,
|
||||
Eor_V,
|
||||
Ext_V,
|
||||
Fabd_S,
|
||||
Fabd_V,
|
||||
Fabs_S,
|
||||
Fabs_V,
|
||||
Fadd_S,
|
||||
Fadd_V,
|
||||
Faddp_S,
|
||||
Faddp_V,
|
||||
Fccmp_S,
|
||||
Fccmpe_S,
|
||||
Fcmeq_S,
|
||||
Fcmeq_V,
|
||||
Fcmge_S,
|
||||
Fcmge_V,
|
||||
Fcmgt_S,
|
||||
Fcmgt_V,
|
||||
Fcmle_S,
|
||||
Fcmle_V,
|
||||
Fcmlt_S,
|
||||
Fcmlt_V,
|
||||
Fcmp_S,
|
||||
Fcmpe_S,
|
||||
Fcsel_S,
|
||||
Fcvt_S,
|
||||
Fcvtas_Gp,
|
||||
Fcvtau_Gp,
|
||||
Fcvtl_V,
|
||||
Fcvtms_Gp,
|
||||
Fcvtmu_Gp,
|
||||
Fcvtn_V,
|
||||
Fcvtns_S,
|
||||
Fcvtns_V,
|
||||
Fcvtnu_S,
|
||||
Fcvtnu_V,
|
||||
Fcvtps_Gp,
|
||||
Fcvtpu_Gp,
|
||||
Fcvtzs_Gp,
|
||||
Fcvtzs_Gp_Fixed,
|
||||
Fcvtzs_S,
|
||||
Fcvtzs_V,
|
||||
Fcvtzs_V_Fixed,
|
||||
Fcvtzu_Gp,
|
||||
Fcvtzu_Gp_Fixed,
|
||||
Fcvtzu_S,
|
||||
Fcvtzu_V,
|
||||
Fcvtzu_V_Fixed,
|
||||
Fdiv_S,
|
||||
Fdiv_V,
|
||||
Fmadd_S,
|
||||
Fmax_S,
|
||||
Fmax_V,
|
||||
Fmaxnm_S,
|
||||
Fmaxnm_V,
|
||||
Fmaxp_V,
|
||||
Fmin_S,
|
||||
Fmin_V,
|
||||
Fminnm_S,
|
||||
Fminnm_V,
|
||||
Fminp_V,
|
||||
Fmla_Se,
|
||||
Fmla_V,
|
||||
Fmla_Ve,
|
||||
Fmls_Se,
|
||||
Fmls_V,
|
||||
Fmls_Ve,
|
||||
Fmov_S,
|
||||
Fmov_Si,
|
||||
Fmov_Vi,
|
||||
Fmov_Ftoi,
|
||||
Fmov_Itof,
|
||||
Fmov_Ftoi1,
|
||||
Fmov_Itof1,
|
||||
Fmsub_S,
|
||||
Fmul_S,
|
||||
Fmul_Se,
|
||||
Fmul_V,
|
||||
Fmul_Ve,
|
||||
Fmulx_S,
|
||||
Fmulx_Se,
|
||||
Fmulx_V,
|
||||
Fmulx_Ve,
|
||||
Fneg_S,
|
||||
Fneg_V,
|
||||
Fnmadd_S,
|
||||
Fnmsub_S,
|
||||
Fnmul_S,
|
||||
Frecpe_S,
|
||||
Frecpe_V,
|
||||
Frecps_S,
|
||||
Frecps_V,
|
||||
Frecpx_S,
|
||||
Frinta_S,
|
||||
Frinta_V,
|
||||
Frinti_S,
|
||||
Frinti_V,
|
||||
Frintm_S,
|
||||
Frintm_V,
|
||||
Frintn_S,
|
||||
Frintn_V,
|
||||
Frintp_S,
|
||||
Frintp_V,
|
||||
Frintx_S,
|
||||
Frintx_V,
|
||||
Frintz_S,
|
||||
Frintz_V,
|
||||
Frsqrte_S,
|
||||
Frsqrte_V,
|
||||
Frsqrts_S,
|
||||
Frsqrts_V,
|
||||
Fsqrt_S,
|
||||
Fsqrt_V,
|
||||
Fsub_S,
|
||||
Fsub_V,
|
||||
Ins_Gp,
|
||||
Ins_V,
|
||||
Ld__Vms,
|
||||
Ld__Vss,
|
||||
Mla_V,
|
||||
Mla_Ve,
|
||||
Mls_V,
|
||||
Mls_Ve,
|
||||
Movi_V,
|
||||
Mul_V,
|
||||
Mul_Ve,
|
||||
Mvni_V,
|
||||
Neg_S,
|
||||
Neg_V,
|
||||
Not_V,
|
||||
Orn_V,
|
||||
Orr_V,
|
||||
Orr_Vi,
|
||||
Raddhn_V,
|
||||
Rbit_V,
|
||||
Rev16_V,
|
||||
Rev32_V,
|
||||
Rev64_V,
|
||||
Rshrn_V,
|
||||
Rsubhn_V,
|
||||
Saba_V,
|
||||
Sabal_V,
|
||||
Sabd_V,
|
||||
Sabdl_V,
|
||||
Sadalp_V,
|
||||
Saddl_V,
|
||||
Saddlp_V,
|
||||
Saddlv_V,
|
||||
Saddw_V,
|
||||
Scvtf_Gp,
|
||||
Scvtf_Gp_Fixed,
|
||||
Scvtf_S,
|
||||
Scvtf_V,
|
||||
Scvtf_V_Fixed,
|
||||
Sha1c_V,
|
||||
Sha1h_V,
|
||||
Sha1m_V,
|
||||
Sha1p_V,
|
||||
Sha1su0_V,
|
||||
Sha1su1_V,
|
||||
Sha256h_V,
|
||||
Sha256h2_V,
|
||||
Sha256su0_V,
|
||||
Sha256su1_V,
|
||||
Shadd_V,
|
||||
Shl_S,
|
||||
Shl_V,
|
||||
Shll_V,
|
||||
Shrn_V,
|
||||
Shsub_V,
|
||||
Sli_V,
|
||||
Smax_V,
|
||||
Smaxp_V,
|
||||
Smaxv_V,
|
||||
Smin_V,
|
||||
Sminp_V,
|
||||
Sminv_V,
|
||||
Smlal_V,
|
||||
Smlal_Ve,
|
||||
Smlsl_V,
|
||||
Smlsl_Ve,
|
||||
Smov_S,
|
||||
Smull_V,
|
||||
Smull_Ve,
|
||||
Sqabs_S,
|
||||
Sqabs_V,
|
||||
Sqadd_S,
|
||||
Sqadd_V,
|
||||
Sqdmulh_S,
|
||||
Sqdmulh_V,
|
||||
Sqneg_S,
|
||||
Sqneg_V,
|
||||
Sqrdmulh_S,
|
||||
Sqrdmulh_V,
|
||||
Sqrshl_V,
|
||||
Sqrshrn_S,
|
||||
Sqrshrn_V,
|
||||
Sqrshrun_S,
|
||||
Sqrshrun_V,
|
||||
Sqshl_V,
|
||||
Sqshrn_S,
|
||||
Sqshrn_V,
|
||||
Sqshrun_S,
|
||||
Sqshrun_V,
|
||||
Sqsub_S,
|
||||
Sqsub_V,
|
||||
Sqxtn_S,
|
||||
Sqxtn_V,
|
||||
Sqxtun_S,
|
||||
Sqxtun_V,
|
||||
Srhadd_V,
|
||||
Srshl_V,
|
||||
Srshr_S,
|
||||
Srshr_V,
|
||||
Srsra_S,
|
||||
Srsra_V,
|
||||
Sshl_V,
|
||||
Sshll_V,
|
||||
Sshr_S,
|
||||
Sshr_V,
|
||||
Ssra_S,
|
||||
Ssra_V,
|
||||
Ssubl_V,
|
||||
Ssubw_V,
|
||||
St__Vms,
|
||||
St__Vss,
|
||||
Sub_S,
|
||||
Sub_V,
|
||||
Subhn_V,
|
||||
Suqadd_S,
|
||||
Suqadd_V,
|
||||
Tbl_V,
|
||||
Trn1_V,
|
||||
Trn2_V,
|
||||
Uaba_V,
|
||||
Uabal_V,
|
||||
Uabd_V,
|
||||
Uabdl_V,
|
||||
Uadalp_V,
|
||||
Uaddl_V,
|
||||
Uaddlp_V,
|
||||
Uaddlv_V,
|
||||
Uaddw_V,
|
||||
Ucvtf_Gp,
|
||||
Ucvtf_Gp_Fixed,
|
||||
Ucvtf_S,
|
||||
Ucvtf_V,
|
||||
Ucvtf_V_Fixed,
|
||||
Uhadd_V,
|
||||
Uhsub_V,
|
||||
Umax_V,
|
||||
Umaxp_V,
|
||||
Umaxv_V,
|
||||
Umin_V,
|
||||
Uminp_V,
|
||||
Uminv_V,
|
||||
Umlal_V,
|
||||
Umlal_Ve,
|
||||
Umlsl_V,
|
||||
Umlsl_Ve,
|
||||
Umov_S,
|
||||
Umull_V,
|
||||
Umull_Ve,
|
||||
Uqadd_S,
|
||||
Uqadd_V,
|
||||
Uqrshl_V,
|
||||
Uqrshrn_S,
|
||||
Uqrshrn_V,
|
||||
Uqshl_V,
|
||||
Uqshrn_S,
|
||||
Uqshrn_V,
|
||||
Uqsub_S,
|
||||
Uqsub_V,
|
||||
Uqxtn_S,
|
||||
Uqxtn_V,
|
||||
Urhadd_V,
|
||||
Urshl_V,
|
||||
Urshr_S,
|
||||
Urshr_V,
|
||||
Ursra_S,
|
||||
Ursra_V,
|
||||
Ushl_V,
|
||||
Ushll_V,
|
||||
Ushr_S,
|
||||
Ushr_V,
|
||||
Usqadd_S,
|
||||
Usqadd_V,
|
||||
Usra_S,
|
||||
Usra_V,
|
||||
Usubl_V,
|
||||
Usubw_V,
|
||||
Uzp1_V,
|
||||
Uzp2_V,
|
||||
Xtn_V,
|
||||
Zip1_V,
|
||||
Zip2_V,
|
||||
|
||||
// Base (AArch32)
|
||||
Blx,
|
||||
Bx,
|
||||
Cmp,
|
||||
Ldm,
|
||||
Ldrb,
|
||||
Ldrd,
|
||||
Ldrh,
|
||||
Ldrsb,
|
||||
Ldrsh,
|
||||
Mov,
|
||||
Stm,
|
||||
Strb,
|
||||
Strd,
|
||||
Strh
|
||||
}
|
||||
}
|
367
ARMeilleure/Instructions/NativeInterface.cs
Normal file
367
ARMeilleure/Instructions/NativeInterface.cs
Normal file
|
@ -0,0 +1,367 @@
|
|||
using ARMeilleure.Memory;
|
||||
using ARMeilleure.State;
|
||||
using System;
|
||||
|
||||
namespace ARMeilleure.Instructions
|
||||
{
|
||||
static class NativeInterface
|
||||
{
|
||||
private const int ErgSizeLog2 = 4;
|
||||
|
||||
private class ThreadContext
|
||||
{
|
||||
public ExecutionContext Context { get; }
|
||||
public MemoryManager Memory { get; }
|
||||
|
||||
public ulong ExclusiveAddress { get; set; }
|
||||
public ulong ExclusiveValueLow { get; set; }
|
||||
public ulong ExclusiveValueHigh { get; set; }
|
||||
|
||||
public ThreadContext(ExecutionContext context, MemoryManager memory)
|
||||
{
|
||||
Context = context;
|
||||
Memory = memory;
|
||||
|
||||
ExclusiveAddress = ulong.MaxValue;
|
||||
}
|
||||
}
|
||||
|
||||
[ThreadStatic]
|
||||
private static ThreadContext _context;
|
||||
|
||||
public static void RegisterThread(ExecutionContext context, MemoryManager memory)
|
||||
{
|
||||
_context = new ThreadContext(context, memory);
|
||||
}
|
||||
|
||||
public static void UnregisterThread()
|
||||
{
|
||||
_context = null;
|
||||
}
|
||||
|
||||
public static void Break(ulong address, int imm)
|
||||
{
|
||||
Statistics.PauseTimer();
|
||||
|
||||
GetContext().OnBreak(address, imm);
|
||||
|
||||
Statistics.ResumeTimer();
|
||||
}
|
||||
|
||||
public static void SupervisorCall(ulong address, int imm)
|
||||
{
|
||||
Statistics.PauseTimer();
|
||||
|
||||
GetContext().OnSupervisorCall(address, imm);
|
||||
|
||||
Statistics.ResumeTimer();
|
||||
}
|
||||
|
||||
public static void Undefined(ulong address, int opCode)
|
||||
{
|
||||
Statistics.PauseTimer();
|
||||
|
||||
GetContext().OnUndefined(address, opCode);
|
||||
|
||||
Statistics.ResumeTimer();
|
||||
}
|
||||
|
||||
#region "System registers"
|
||||
public static ulong GetCtrEl0()
|
||||
{
|
||||
return (ulong)GetContext().CtrEl0;
|
||||
}
|
||||
|
||||
public static ulong GetDczidEl0()
|
||||
{
|
||||
return (ulong)GetContext().DczidEl0;
|
||||
}
|
||||
|
||||
public static ulong GetFpcr()
|
||||
{
|
||||
return (ulong)GetContext().Fpcr;
|
||||
}
|
||||
|
||||
public static ulong GetFpsr()
|
||||
{
|
||||
return (ulong)GetContext().Fpsr;
|
||||
}
|
||||
|
||||
public static ulong GetTpidrEl0()
|
||||
{
|
||||
return (ulong)GetContext().TpidrEl0;
|
||||
}
|
||||
|
||||
public static ulong GetTpidr()
|
||||
{
|
||||
return (ulong)GetContext().Tpidr;
|
||||
}
|
||||
|
||||
public static ulong GetCntfrqEl0()
|
||||
{
|
||||
return GetContext().CntfrqEl0;
|
||||
}
|
||||
|
||||
public static ulong GetCntpctEl0()
|
||||
{
|
||||
return GetContext().CntpctEl0;
|
||||
}
|
||||
|
||||
public static void SetFpcr(ulong value)
|
||||
{
|
||||
GetContext().Fpcr = (FPCR)value;
|
||||
}
|
||||
|
||||
public static void SetFpsr(ulong value)
|
||||
{
|
||||
GetContext().Fpsr = (FPSR)value;
|
||||
}
|
||||
|
||||
public static void SetTpidrEl0(ulong value)
|
||||
{
|
||||
GetContext().TpidrEl0 = (long)value;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region "Read"
|
||||
public static byte ReadByte(ulong address)
|
||||
{
|
||||
return GetMemoryManager().ReadByte((long)address);
|
||||
}
|
||||
|
||||
public static ushort ReadUInt16(ulong address)
|
||||
{
|
||||
return GetMemoryManager().ReadUInt16((long)address);
|
||||
}
|
||||
|
||||
public static uint ReadUInt32(ulong address)
|
||||
{
|
||||
return GetMemoryManager().ReadUInt32((long)address);
|
||||
}
|
||||
|
||||
public static ulong ReadUInt64(ulong address)
|
||||
{
|
||||
return GetMemoryManager().ReadUInt64((long)address);
|
||||
}
|
||||
|
||||
public static V128 ReadVector128(ulong address)
|
||||
{
|
||||
return GetMemoryManager().ReadVector128((long)address);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region "Read exclusive"
|
||||
public static byte ReadByteExclusive(ulong address)
|
||||
{
|
||||
byte value = _context.Memory.ReadByte((long)address);
|
||||
|
||||
_context.ExclusiveAddress = GetMaskedExclusiveAddress(address);
|
||||
_context.ExclusiveValueLow = value;
|
||||
_context.ExclusiveValueHigh = 0;
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
public static ushort ReadUInt16Exclusive(ulong address)
|
||||
{
|
||||
ushort value = _context.Memory.ReadUInt16((long)address);
|
||||
|
||||
_context.ExclusiveAddress = GetMaskedExclusiveAddress(address);
|
||||
_context.ExclusiveValueLow = value;
|
||||
_context.ExclusiveValueHigh = 0;
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
public static uint ReadUInt32Exclusive(ulong address)
|
||||
{
|
||||
uint value = _context.Memory.ReadUInt32((long)address);
|
||||
|
||||
_context.ExclusiveAddress = GetMaskedExclusiveAddress(address);
|
||||
_context.ExclusiveValueLow = value;
|
||||
_context.ExclusiveValueHigh = 0;
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
public static ulong ReadUInt64Exclusive(ulong address)
|
||||
{
|
||||
ulong value = _context.Memory.ReadUInt64((long)address);
|
||||
|
||||
_context.ExclusiveAddress = GetMaskedExclusiveAddress(address);
|
||||
_context.ExclusiveValueLow = value;
|
||||
_context.ExclusiveValueHigh = 0;
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
public static V128 ReadVector128Exclusive(ulong address)
|
||||
{
|
||||
V128 value = _context.Memory.AtomicLoadInt128((long)address);
|
||||
|
||||
_context.ExclusiveAddress = GetMaskedExclusiveAddress(address);
|
||||
_context.ExclusiveValueLow = value.GetUInt64(0);
|
||||
_context.ExclusiveValueHigh = value.GetUInt64(1);
|
||||
|
||||
return value;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region "Write"
|
||||
public static void WriteByte(ulong address, byte value)
|
||||
{
|
||||
GetMemoryManager().WriteByte((long)address, value);
|
||||
}
|
||||
|
||||
public static void WriteUInt16(ulong address, ushort value)
|
||||
{
|
||||
GetMemoryManager().WriteUInt16((long)address, value);
|
||||
}
|
||||
|
||||
public static void WriteUInt32(ulong address, uint value)
|
||||
{
|
||||
GetMemoryManager().WriteUInt32((long)address, value);
|
||||
}
|
||||
|
||||
public static void WriteUInt64(ulong address, ulong value)
|
||||
{
|
||||
GetMemoryManager().WriteUInt64((long)address, value);
|
||||
}
|
||||
|
||||
public static void WriteVector128(ulong address, V128 value)
|
||||
{
|
||||
GetMemoryManager().WriteVector128((long)address, value);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region "Write exclusive"
|
||||
public static int WriteByteExclusive(ulong address, byte value)
|
||||
{
|
||||
bool success = _context.ExclusiveAddress == GetMaskedExclusiveAddress(address);
|
||||
|
||||
if (success)
|
||||
{
|
||||
success = _context.Memory.AtomicCompareExchangeByte(
|
||||
(long)address,
|
||||
(byte)_context.ExclusiveValueLow,
|
||||
(byte)value);
|
||||
|
||||
if (success)
|
||||
{
|
||||
ClearExclusive();
|
||||
}
|
||||
}
|
||||
|
||||
return success ? 0 : 1;
|
||||
}
|
||||
|
||||
public static int WriteUInt16Exclusive(ulong address, ushort value)
|
||||
{
|
||||
bool success = _context.ExclusiveAddress == GetMaskedExclusiveAddress(address);
|
||||
|
||||
if (success)
|
||||
{
|
||||
success = _context.Memory.AtomicCompareExchangeInt16(
|
||||
(long)address,
|
||||
(short)_context.ExclusiveValueLow,
|
||||
(short)value);
|
||||
|
||||
if (success)
|
||||
{
|
||||
ClearExclusive();
|
||||
}
|
||||
}
|
||||
|
||||
return success ? 0 : 1;
|
||||
}
|
||||
|
||||
public static int WriteUInt32Exclusive(ulong address, uint value)
|
||||
{
|
||||
bool success = _context.ExclusiveAddress == GetMaskedExclusiveAddress(address);
|
||||
|
||||
if (success)
|
||||
{
|
||||
success = _context.Memory.AtomicCompareExchangeInt32(
|
||||
(long)address,
|
||||
(int)_context.ExclusiveValueLow,
|
||||
(int)value);
|
||||
|
||||
if (success)
|
||||
{
|
||||
ClearExclusive();
|
||||
}
|
||||
}
|
||||
|
||||
return success ? 0 : 1;
|
||||
}
|
||||
|
||||
public static int WriteUInt64Exclusive(ulong address, ulong value)
|
||||
{
|
||||
bool success = _context.ExclusiveAddress == GetMaskedExclusiveAddress(address);
|
||||
|
||||
if (success)
|
||||
{
|
||||
success = _context.Memory.AtomicCompareExchangeInt64(
|
||||
(long)address,
|
||||
(long)_context.ExclusiveValueLow,
|
||||
(long)value);
|
||||
|
||||
if (success)
|
||||
{
|
||||
ClearExclusive();
|
||||
}
|
||||
}
|
||||
|
||||
return success ? 0 : 1;
|
||||
}
|
||||
|
||||
public static int WriteVector128Exclusive(ulong address, V128 value)
|
||||
{
|
||||
bool success = _context.ExclusiveAddress == GetMaskedExclusiveAddress(address);
|
||||
|
||||
if (success)
|
||||
{
|
||||
V128 expected = new V128(_context.ExclusiveValueLow, _context.ExclusiveValueHigh);
|
||||
|
||||
success = _context.Memory.AtomicCompareExchangeInt128((long)address, expected, value);
|
||||
|
||||
if (success)
|
||||
{
|
||||
ClearExclusive();
|
||||
}
|
||||
}
|
||||
|
||||
return success ? 0 : 1;
|
||||
}
|
||||
#endregion
|
||||
|
||||
private static ulong GetMaskedExclusiveAddress(ulong address)
|
||||
{
|
||||
return address & ~((4UL << ErgSizeLog2) - 1);
|
||||
}
|
||||
|
||||
public static void ClearExclusive()
|
||||
{
|
||||
_context.ExclusiveAddress = ulong.MaxValue;
|
||||
}
|
||||
|
||||
public static void CheckSynchronization()
|
||||
{
|
||||
Statistics.PauseTimer();
|
||||
|
||||
GetContext().CheckInterrupt();
|
||||
|
||||
Statistics.ResumeTimer();
|
||||
}
|
||||
|
||||
public static ExecutionContext GetContext()
|
||||
{
|
||||
return _context.Context;
|
||||
}
|
||||
|
||||
public static MemoryManager GetMemoryManager()
|
||||
{
|
||||
return _context.Memory;
|
||||
}
|
||||
}
|
||||
}
|
1307
ARMeilleure/Instructions/SoftFallback.cs
Normal file
1307
ARMeilleure/Instructions/SoftFallback.cs
Normal file
File diff suppressed because it is too large
Load diff
2757
ARMeilleure/Instructions/SoftFloat.cs
Normal file
2757
ARMeilleure/Instructions/SoftFloat.cs
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue