mirror of
https://github.com/ryujinx-mirror/ryujinx.git
synced 2025-11-04 15:08:59 -06:00
Move solution and projects to src
This commit is contained in:
280
src/ARMeilleure/Instructions/CryptoHelper.cs
Normal file
280
src/ARMeilleure/Instructions/CryptoHelper.cs
Normal file
@@ -0,0 +1,280 @@
|
||||
// https://www.intel.com/content/dam/doc/white-paper/advanced-encryption-standard-new-instructions-set-paper.pdf
|
||||
|
||||
using ARMeilleure.State;
|
||||
using System;
|
||||
|
||||
namespace ARMeilleure.Instructions
|
||||
{
|
||||
static class CryptoHelper
|
||||
{
|
||||
#region "LookUp Tables"
|
||||
private static ReadOnlySpan<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 ReadOnlySpan<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 ReadOnlySpan<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 ReadOnlySpan<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 ReadOnlySpan<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 ReadOnlySpan<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 ReadOnlySpan<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 ReadOnlySpan<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 ReadOnlySpan<byte> _srPerm => new byte[]
|
||||
{
|
||||
0, 13, 10, 7, 4, 1, 14, 11, 8, 5, 2, 15, 12, 9, 6, 3
|
||||
};
|
||||
|
||||
private static ReadOnlySpan<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);
|
||||
}
|
||||
}
|
||||
}
|
||||
400
src/ARMeilleure/Instructions/InstEmitAlu.cs
Normal file
400
src/ARMeilleure/Instructions/InstEmitAlu.cs
Normal file
@@ -0,0 +1,400 @@
|
||||
using ARMeilleure.Decoders;
|
||||
using ARMeilleure.IntermediateRepresentation;
|
||||
using ARMeilleure.State;
|
||||
using ARMeilleure.Translation;
|
||||
using System.Diagnostics;
|
||||
|
||||
using static ARMeilleure.Instructions.InstEmitAluHelper;
|
||||
using static ARMeilleure.Instructions.InstEmitHelper;
|
||||
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
|
||||
|
||||
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 = EmitReverseBits32Op(context, n);
|
||||
}
|
||||
else
|
||||
{
|
||||
d = EmitReverseBits64Op(context, n);
|
||||
}
|
||||
|
||||
SetAluDOrZR(context, d);
|
||||
}
|
||||
|
||||
private static Operand EmitReverseBits64Op(ArmEmitterContext context, Operand op)
|
||||
{
|
||||
Debug.Assert(op.Type == OperandType.I64);
|
||||
|
||||
Operand val = context.BitwiseOr(context.ShiftRightUI(context.BitwiseAnd(op, Const(0xaaaaaaaaaaaaaaaaul)), Const(1)),
|
||||
context.ShiftLeft (context.BitwiseAnd(op, Const(0x5555555555555555ul)), Const(1)));
|
||||
|
||||
val = context.BitwiseOr(context.ShiftRightUI(context.BitwiseAnd(val, Const(0xccccccccccccccccul)), Const(2)),
|
||||
context.ShiftLeft (context.BitwiseAnd(val, Const(0x3333333333333333ul)), Const(2)));
|
||||
val = context.BitwiseOr(context.ShiftRightUI(context.BitwiseAnd(val, Const(0xf0f0f0f0f0f0f0f0ul)), Const(4)),
|
||||
context.ShiftLeft (context.BitwiseAnd(val, Const(0x0f0f0f0f0f0f0f0ful)), Const(4)));
|
||||
val = context.BitwiseOr(context.ShiftRightUI(context.BitwiseAnd(val, Const(0xff00ff00ff00ff00ul)), Const(8)),
|
||||
context.ShiftLeft (context.BitwiseAnd(val, Const(0x00ff00ff00ff00fful)), Const(8)));
|
||||
val = context.BitwiseOr(context.ShiftRightUI(context.BitwiseAnd(val, Const(0xffff0000ffff0000ul)), Const(16)),
|
||||
context.ShiftLeft (context.BitwiseAnd(val, Const(0x0000ffff0000fffful)), Const(16)));
|
||||
|
||||
return context.BitwiseOr(context.ShiftRightUI(val, Const(32)), context.ShiftLeft(val, Const(32)));
|
||||
}
|
||||
|
||||
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 = EmitReverseBytes16_32Op(context, n);
|
||||
}
|
||||
else
|
||||
{
|
||||
d = EmitReverseBytes16_64Op(context, n);
|
||||
}
|
||||
|
||||
SetAluDOrZR(context, d);
|
||||
}
|
||||
|
||||
public static void Rev32(ArmEmitterContext context)
|
||||
{
|
||||
OpCodeAlu op = (OpCodeAlu)context.CurrOp;
|
||||
|
||||
Operand n = GetIntOrZR(context, op.Rn);
|
||||
Operand d;
|
||||
|
||||
if (op.RegisterSize == RegisterSize.Int32)
|
||||
{
|
||||
d = context.ByteSwap(n);
|
||||
}
|
||||
else
|
||||
{
|
||||
d = EmitReverseBytes32_64Op(context, n);
|
||||
}
|
||||
|
||||
SetAluDOrZR(context, d);
|
||||
}
|
||||
|
||||
private static Operand EmitReverseBytes32_64Op(ArmEmitterContext context, Operand op)
|
||||
{
|
||||
Debug.Assert(op.Type == OperandType.I64);
|
||||
|
||||
Operand val = EmitReverseBytes16_64Op(context, op);
|
||||
|
||||
return context.BitwiseOr(context.ShiftRightUI(context.BitwiseAnd(val, Const(0xffff0000ffff0000ul)), Const(16)),
|
||||
context.ShiftLeft (context.BitwiseAnd(val, Const(0x0000ffff0000fffful)), Const(16)));
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
931
src/ARMeilleure/Instructions/InstEmitAlu32.cs
Normal file
931
src/ARMeilleure/Instructions/InstEmitAlu32.cs
Normal file
@@ -0,0 +1,931 @@
|
||||
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.Operand.Factory;
|
||||
|
||||
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 (ShouldSetFlags(context))
|
||||
{
|
||||
EmitNZFlagsCheck(context, res);
|
||||
|
||||
EmitAddsCCheck(context, n, res);
|
||||
EmitAddsVCheck(context, n, m, res);
|
||||
}
|
||||
|
||||
EmitAluStore(context, res);
|
||||
}
|
||||
|
||||
public static void Adc(ArmEmitterContext context)
|
||||
{
|
||||
IOpCode32Alu op = (IOpCode32Alu)context.CurrOp;
|
||||
|
||||
Operand n = GetAluN(context);
|
||||
Operand m = GetAluM(context, setCarry: false);
|
||||
|
||||
Operand res = context.Add(n, m);
|
||||
|
||||
Operand carry = GetFlag(PState.CFlag);
|
||||
|
||||
res = context.Add(res, carry);
|
||||
|
||||
if (ShouldSetFlags(context))
|
||||
{
|
||||
EmitNZFlagsCheck(context, res);
|
||||
|
||||
EmitAdcsCCheck(context, n, res);
|
||||
EmitAddsVCheck(context, n, m, res);
|
||||
}
|
||||
|
||||
EmitAluStore(context, res);
|
||||
}
|
||||
|
||||
public static void And(ArmEmitterContext context)
|
||||
{
|
||||
IOpCode32Alu op = (IOpCode32Alu)context.CurrOp;
|
||||
|
||||
Operand n = GetAluN(context);
|
||||
Operand m = GetAluM(context);
|
||||
|
||||
Operand res = context.BitwiseAnd(n, m);
|
||||
|
||||
if (ShouldSetFlags(context))
|
||||
{
|
||||
EmitNZFlagsCheck(context, res);
|
||||
}
|
||||
|
||||
EmitAluStore(context, res);
|
||||
}
|
||||
|
||||
public static void Bfc(ArmEmitterContext context)
|
||||
{
|
||||
IOpCode32AluBf op = (IOpCode32AluBf)context.CurrOp;
|
||||
|
||||
Operand d = GetIntA32(context, op.Rd);
|
||||
Operand res = context.BitwiseAnd(d, Const(~op.DestMask));
|
||||
|
||||
SetIntA32(context, op.Rd, res);
|
||||
}
|
||||
|
||||
public static void Bfi(ArmEmitterContext context)
|
||||
{
|
||||
IOpCode32AluBf op = (IOpCode32AluBf)context.CurrOp;
|
||||
|
||||
Operand n = GetIntA32(context, op.Rn);
|
||||
Operand d = GetIntA32(context, op.Rd);
|
||||
Operand part = context.BitwiseAnd(n, Const(op.SourceMask));
|
||||
|
||||
if (op.Lsb != 0)
|
||||
{
|
||||
part = context.ShiftLeft(part, Const(op.Lsb));
|
||||
}
|
||||
|
||||
Operand res = context.BitwiseAnd(d, Const(~op.DestMask));
|
||||
res = context.BitwiseOr(res, context.BitwiseAnd(part, Const(op.DestMask)));
|
||||
|
||||
SetIntA32(context, op.Rd, res);
|
||||
}
|
||||
|
||||
public static void Bic(ArmEmitterContext context)
|
||||
{
|
||||
IOpCode32Alu op = (IOpCode32Alu)context.CurrOp;
|
||||
|
||||
Operand n = GetAluN(context);
|
||||
Operand m = GetAluM(context);
|
||||
|
||||
Operand res = context.BitwiseAnd(n, context.BitwiseNot(m));
|
||||
|
||||
if (ShouldSetFlags(context))
|
||||
{
|
||||
EmitNZFlagsCheck(context, res);
|
||||
}
|
||||
|
||||
EmitAluStore(context, res);
|
||||
}
|
||||
|
||||
public static void Clz(ArmEmitterContext context)
|
||||
{
|
||||
Operand m = GetAluM(context, setCarry: false);
|
||||
|
||||
Operand res = context.CountLeadingZeros(m);
|
||||
EmitAluStore(context, res);
|
||||
}
|
||||
|
||||
public static void Cmp(ArmEmitterContext context)
|
||||
{
|
||||
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 Cmn(ArmEmitterContext context)
|
||||
{
|
||||
Operand n = GetAluN(context);
|
||||
Operand m = GetAluM(context, setCarry: false);
|
||||
|
||||
Operand res = context.Add(n, m);
|
||||
|
||||
EmitNZFlagsCheck(context, res);
|
||||
|
||||
EmitAddsCCheck(context, n, res);
|
||||
EmitAddsVCheck(context, n, m, res);
|
||||
}
|
||||
|
||||
public static void Eor(ArmEmitterContext context)
|
||||
{
|
||||
IOpCode32Alu op = (IOpCode32Alu)context.CurrOp;
|
||||
|
||||
Operand n = GetAluN(context);
|
||||
Operand m = GetAluM(context);
|
||||
|
||||
Operand res = context.BitwiseExclusiveOr(n, m);
|
||||
|
||||
if (ShouldSetFlags(context))
|
||||
{
|
||||
EmitNZFlagsCheck(context, res);
|
||||
}
|
||||
|
||||
EmitAluStore(context, res);
|
||||
}
|
||||
|
||||
public static void Mov(ArmEmitterContext context)
|
||||
{
|
||||
IOpCode32Alu op = (IOpCode32Alu)context.CurrOp;
|
||||
|
||||
Operand m = GetAluM(context);
|
||||
|
||||
if (ShouldSetFlags(context))
|
||||
{
|
||||
EmitNZFlagsCheck(context, m);
|
||||
}
|
||||
|
||||
EmitAluStore(context, m);
|
||||
}
|
||||
|
||||
public static void Movt(ArmEmitterContext context)
|
||||
{
|
||||
IOpCode32AluImm16 op = (IOpCode32AluImm16)context.CurrOp;
|
||||
|
||||
Operand d = GetIntA32(context, op.Rd);
|
||||
Operand imm = Const(op.Immediate << 16); // Immeditate value as top halfword.
|
||||
Operand res = context.BitwiseAnd(d, Const(0x0000ffff));
|
||||
res = context.BitwiseOr(res, imm);
|
||||
|
||||
EmitAluStore(context, res);
|
||||
}
|
||||
|
||||
public static void Mul(ArmEmitterContext context)
|
||||
{
|
||||
IOpCode32Alu op = (IOpCode32Alu)context.CurrOp;
|
||||
|
||||
Operand n = GetAluN(context);
|
||||
Operand m = GetAluM(context);
|
||||
|
||||
Operand res = context.Multiply(n, m);
|
||||
|
||||
if (ShouldSetFlags(context))
|
||||
{
|
||||
EmitNZFlagsCheck(context, res);
|
||||
}
|
||||
|
||||
EmitAluStore(context, res);
|
||||
}
|
||||
|
||||
public static void Mvn(ArmEmitterContext context)
|
||||
{
|
||||
IOpCode32Alu op = (IOpCode32Alu)context.CurrOp;
|
||||
Operand m = GetAluM(context);
|
||||
|
||||
Operand res = context.BitwiseNot(m);
|
||||
|
||||
if (ShouldSetFlags(context))
|
||||
{
|
||||
EmitNZFlagsCheck(context, res);
|
||||
}
|
||||
|
||||
EmitAluStore(context, res);
|
||||
}
|
||||
|
||||
public static void Orr(ArmEmitterContext context)
|
||||
{
|
||||
IOpCode32Alu op = (IOpCode32Alu)context.CurrOp;
|
||||
|
||||
Operand n = GetAluN(context);
|
||||
Operand m = GetAluM(context);
|
||||
|
||||
Operand res = context.BitwiseOr(n, m);
|
||||
|
||||
if (ShouldSetFlags(context))
|
||||
{
|
||||
EmitNZFlagsCheck(context, res);
|
||||
}
|
||||
|
||||
EmitAluStore(context, res);
|
||||
}
|
||||
|
||||
public static void Orn(ArmEmitterContext context)
|
||||
{
|
||||
IOpCode32Alu op = (IOpCode32Alu)context.CurrOp;
|
||||
|
||||
Operand n = GetAluN(context);
|
||||
Operand m = GetAluM(context);
|
||||
|
||||
Operand res = context.BitwiseOr(n, context.BitwiseNot(m));
|
||||
|
||||
if (ShouldSetFlags(context))
|
||||
{
|
||||
EmitNZFlagsCheck(context, res);
|
||||
}
|
||||
|
||||
EmitAluStore(context, res);
|
||||
}
|
||||
|
||||
public static void Pkh(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32AluRsImm op = (OpCode32AluRsImm)context.CurrOp;
|
||||
|
||||
Operand n = GetAluN(context);
|
||||
Operand m = GetAluM(context);
|
||||
|
||||
Operand res;
|
||||
|
||||
bool tbform = op.ShiftType == ShiftType.Asr;
|
||||
if (tbform)
|
||||
{
|
||||
res = context.BitwiseOr(context.BitwiseAnd(n, Const(0xFFFF0000)), context.BitwiseAnd(m, Const(0xFFFF)));
|
||||
}
|
||||
else
|
||||
{
|
||||
res = context.BitwiseOr(context.BitwiseAnd(m, Const(0xFFFF0000)), context.BitwiseAnd(n, Const(0xFFFF)));
|
||||
}
|
||||
|
||||
EmitAluStore(context, res);
|
||||
}
|
||||
|
||||
public static void Rbit(ArmEmitterContext context)
|
||||
{
|
||||
Operand m = GetAluM(context);
|
||||
|
||||
Operand res = EmitReverseBits32Op(context, m);
|
||||
|
||||
EmitAluStore(context, res);
|
||||
}
|
||||
|
||||
public static void Rev(ArmEmitterContext context)
|
||||
{
|
||||
Operand m = GetAluM(context);
|
||||
|
||||
Operand res = context.ByteSwap(m);
|
||||
|
||||
EmitAluStore(context, res);
|
||||
}
|
||||
|
||||
public static void Rev16(ArmEmitterContext context)
|
||||
{
|
||||
Operand m = GetAluM(context);
|
||||
|
||||
Operand res = EmitReverseBytes16_32Op(context, m);
|
||||
|
||||
EmitAluStore(context, res);
|
||||
}
|
||||
|
||||
public static void Revsh(ArmEmitterContext context)
|
||||
{
|
||||
Operand m = GetAluM(context);
|
||||
|
||||
Operand res = EmitReverseBytes16_32Op(context, m);
|
||||
|
||||
EmitAluStore(context, context.SignExtend16(OperandType.I32, res));
|
||||
}
|
||||
|
||||
public static void Rsc(ArmEmitterContext context)
|
||||
{
|
||||
IOpCode32Alu op = (IOpCode32Alu)context.CurrOp;
|
||||
|
||||
Operand n = GetAluN(context);
|
||||
Operand m = GetAluM(context, setCarry: false);
|
||||
|
||||
Operand res = context.Subtract(m, n);
|
||||
|
||||
Operand borrow = context.BitwiseExclusiveOr(GetFlag(PState.CFlag), Const(1));
|
||||
|
||||
res = context.Subtract(res, borrow);
|
||||
|
||||
if (ShouldSetFlags(context))
|
||||
{
|
||||
EmitNZFlagsCheck(context, res);
|
||||
|
||||
EmitSbcsCCheck(context, m, n);
|
||||
EmitSubsVCheck(context, m, n, res);
|
||||
}
|
||||
|
||||
EmitAluStore(context, res);
|
||||
}
|
||||
|
||||
public static void Rsb(ArmEmitterContext context)
|
||||
{
|
||||
IOpCode32Alu op = (IOpCode32Alu)context.CurrOp;
|
||||
|
||||
Operand n = GetAluN(context);
|
||||
Operand m = GetAluM(context, setCarry: false);
|
||||
|
||||
Operand res = context.Subtract(m, n);
|
||||
|
||||
if (ShouldSetFlags(context))
|
||||
{
|
||||
EmitNZFlagsCheck(context, res);
|
||||
|
||||
EmitSubsCCheck(context, m, res);
|
||||
EmitSubsVCheck(context, m, n, res);
|
||||
}
|
||||
|
||||
EmitAluStore(context, res);
|
||||
}
|
||||
|
||||
public static void Sadd8(ArmEmitterContext context)
|
||||
{
|
||||
EmitAddSub8(context, add: true, unsigned: false);
|
||||
}
|
||||
|
||||
public static void Sbc(ArmEmitterContext context)
|
||||
{
|
||||
IOpCode32Alu op = (IOpCode32Alu)context.CurrOp;
|
||||
|
||||
Operand n = GetAluN(context);
|
||||
Operand m = GetAluM(context, setCarry: false);
|
||||
|
||||
Operand res = context.Subtract(n, m);
|
||||
|
||||
Operand borrow = context.BitwiseExclusiveOr(GetFlag(PState.CFlag), Const(1));
|
||||
|
||||
res = context.Subtract(res, borrow);
|
||||
|
||||
if (ShouldSetFlags(context))
|
||||
{
|
||||
EmitNZFlagsCheck(context, res);
|
||||
|
||||
EmitSbcsCCheck(context, n, m);
|
||||
EmitSubsVCheck(context, n, m, res);
|
||||
}
|
||||
|
||||
EmitAluStore(context, res);
|
||||
}
|
||||
|
||||
public static void Sbfx(ArmEmitterContext context)
|
||||
{
|
||||
IOpCode32AluBf op = (IOpCode32AluBf)context.CurrOp;
|
||||
|
||||
var msb = op.Lsb + op.Msb; // For this instruction, the msb is actually a width.
|
||||
|
||||
Operand n = GetIntA32(context, op.Rn);
|
||||
Operand res = context.ShiftRightSI(context.ShiftLeft(n, Const(31 - msb)), Const(31 - op.Msb));
|
||||
|
||||
SetIntA32(context, op.Rd, res);
|
||||
}
|
||||
|
||||
public static void Sdiv(ArmEmitterContext context)
|
||||
{
|
||||
EmitDiv(context, unsigned: false);
|
||||
}
|
||||
|
||||
public static void Sel(ArmEmitterContext context)
|
||||
{
|
||||
IOpCode32AluReg op = (IOpCode32AluReg)context.CurrOp;
|
||||
|
||||
Operand n = GetIntA32(context, op.Rn);
|
||||
Operand m = GetIntA32(context, op.Rm);
|
||||
|
||||
Operand ge0 = context.ZeroExtend8(OperandType.I32, context.Negate(GetFlag(PState.GE0Flag)));
|
||||
Operand ge1 = context.ZeroExtend8(OperandType.I32, context.Negate(GetFlag(PState.GE1Flag)));
|
||||
Operand ge2 = context.ZeroExtend8(OperandType.I32, context.Negate(GetFlag(PState.GE2Flag)));
|
||||
Operand ge3 = context.Negate(GetFlag(PState.GE3Flag));
|
||||
|
||||
Operand mask = context.BitwiseOr(ge0, context.ShiftLeft(ge1, Const(8)));
|
||||
mask = context.BitwiseOr(mask, context.ShiftLeft(ge2, Const(16)));
|
||||
mask = context.BitwiseOr(mask, context.ShiftLeft(ge3, Const(24)));
|
||||
|
||||
Operand res = context.BitwiseOr(context.BitwiseAnd(n, mask), context.BitwiseAnd(m, context.BitwiseNot(mask)));
|
||||
|
||||
SetIntA32(context, op.Rd, res);
|
||||
}
|
||||
|
||||
public static void Shadd8(ArmEmitterContext context)
|
||||
{
|
||||
EmitHadd8(context, unsigned: false);
|
||||
}
|
||||
|
||||
public static void Shsub8(ArmEmitterContext context)
|
||||
{
|
||||
EmitHsub8(context, unsigned: false);
|
||||
}
|
||||
|
||||
public static void Ssat(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32Sat op = (OpCode32Sat)context.CurrOp;
|
||||
|
||||
EmitSat(context, -(1 << op.SatImm), (1 << op.SatImm) - 1);
|
||||
}
|
||||
|
||||
public static void Ssat16(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32Sat16 op = (OpCode32Sat16)context.CurrOp;
|
||||
|
||||
EmitSat16(context, -(1 << op.SatImm), (1 << op.SatImm) - 1);
|
||||
}
|
||||
|
||||
public static void Ssub8(ArmEmitterContext context)
|
||||
{
|
||||
EmitAddSub8(context, add: false, unsigned: false);
|
||||
}
|
||||
|
||||
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 (ShouldSetFlags(context))
|
||||
{
|
||||
EmitNZFlagsCheck(context, res);
|
||||
|
||||
EmitSubsCCheck(context, n, res);
|
||||
EmitSubsVCheck(context, n, m, res);
|
||||
}
|
||||
|
||||
EmitAluStore(context, res);
|
||||
}
|
||||
|
||||
public static void Sxtb(ArmEmitterContext context)
|
||||
{
|
||||
EmitSignExtend(context, true, 8);
|
||||
}
|
||||
|
||||
public static void Sxtb16(ArmEmitterContext context)
|
||||
{
|
||||
EmitExtend16(context, true);
|
||||
}
|
||||
|
||||
public static void Sxth(ArmEmitterContext context)
|
||||
{
|
||||
EmitSignExtend(context, true, 16);
|
||||
}
|
||||
|
||||
public static void Teq(ArmEmitterContext context)
|
||||
{
|
||||
Operand n = GetAluN(context);
|
||||
Operand m = GetAluM(context);
|
||||
|
||||
Operand res = context.BitwiseExclusiveOr(n, m);
|
||||
|
||||
EmitNZFlagsCheck(context, res);
|
||||
}
|
||||
|
||||
public static void Tst(ArmEmitterContext context)
|
||||
{
|
||||
Operand n = GetAluN(context);
|
||||
Operand m = GetAluM(context);
|
||||
|
||||
Operand res = context.BitwiseAnd(n, m);
|
||||
EmitNZFlagsCheck(context, res);
|
||||
}
|
||||
|
||||
public static void Uadd8(ArmEmitterContext context)
|
||||
{
|
||||
EmitAddSub8(context, add: true, unsigned: true);
|
||||
}
|
||||
|
||||
public static void Ubfx(ArmEmitterContext context)
|
||||
{
|
||||
IOpCode32AluBf op = (IOpCode32AluBf)context.CurrOp;
|
||||
|
||||
var msb = op.Lsb + op.Msb; // For this instruction, the msb is actually a width.
|
||||
|
||||
Operand n = GetIntA32(context, op.Rn);
|
||||
Operand res = context.ShiftRightUI(context.ShiftLeft(n, Const(31 - msb)), Const(31 - op.Msb));
|
||||
|
||||
SetIntA32(context, op.Rd, res);
|
||||
}
|
||||
|
||||
public static void Udiv(ArmEmitterContext context)
|
||||
{
|
||||
EmitDiv(context, unsigned: true);
|
||||
}
|
||||
|
||||
public static void Uhadd8(ArmEmitterContext context)
|
||||
{
|
||||
EmitHadd8(context, unsigned: true);
|
||||
}
|
||||
|
||||
public static void Uhsub8(ArmEmitterContext context)
|
||||
{
|
||||
EmitHsub8(context, unsigned: true);
|
||||
}
|
||||
|
||||
public static void Usat(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32Sat op = (OpCode32Sat)context.CurrOp;
|
||||
|
||||
EmitSat(context, 0, op.SatImm == 32 ? (int)(~0) : (1 << op.SatImm) - 1);
|
||||
}
|
||||
|
||||
public static void Usat16(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32Sat16 op = (OpCode32Sat16)context.CurrOp;
|
||||
|
||||
EmitSat16(context, 0, (1 << op.SatImm) - 1);
|
||||
}
|
||||
|
||||
public static void Usub8(ArmEmitterContext context)
|
||||
{
|
||||
EmitAddSub8(context, add: false, unsigned: true);
|
||||
}
|
||||
|
||||
public static void Uxtb(ArmEmitterContext context)
|
||||
{
|
||||
EmitSignExtend(context, false, 8);
|
||||
}
|
||||
|
||||
public static void Uxtb16(ArmEmitterContext context)
|
||||
{
|
||||
EmitExtend16(context, false);
|
||||
}
|
||||
|
||||
public static void Uxth(ArmEmitterContext context)
|
||||
{
|
||||
EmitSignExtend(context, false, 16);
|
||||
}
|
||||
|
||||
private static void EmitSignExtend(ArmEmitterContext context, bool signed, int bits)
|
||||
{
|
||||
IOpCode32AluUx op = (IOpCode32AluUx)context.CurrOp;
|
||||
|
||||
Operand m = GetAluM(context);
|
||||
Operand res;
|
||||
|
||||
if (op.RotateBits == 0)
|
||||
{
|
||||
res = m;
|
||||
}
|
||||
else
|
||||
{
|
||||
Operand rotate = Const(op.RotateBits);
|
||||
res = context.RotateRight(m, rotate);
|
||||
}
|
||||
|
||||
switch (bits)
|
||||
{
|
||||
case 8:
|
||||
res = (signed) ? context.SignExtend8(OperandType.I32, res) : context.ZeroExtend8(OperandType.I32, res);
|
||||
break;
|
||||
case 16:
|
||||
res = (signed) ? context.SignExtend16(OperandType.I32, res) : context.ZeroExtend16(OperandType.I32, res);
|
||||
break;
|
||||
}
|
||||
|
||||
if (op.Add)
|
||||
{
|
||||
res = context.Add(res, GetAluN(context));
|
||||
}
|
||||
|
||||
EmitAluStore(context, res);
|
||||
}
|
||||
|
||||
private static void EmitExtend16(ArmEmitterContext context, bool signed)
|
||||
{
|
||||
IOpCode32AluUx op = (IOpCode32AluUx)context.CurrOp;
|
||||
|
||||
Operand m = GetAluM(context);
|
||||
Operand res;
|
||||
|
||||
if (op.RotateBits == 0)
|
||||
{
|
||||
res = m;
|
||||
}
|
||||
else
|
||||
{
|
||||
Operand rotate = Const(op.RotateBits);
|
||||
res = context.RotateRight(m, rotate);
|
||||
}
|
||||
|
||||
Operand low16, high16;
|
||||
if (signed)
|
||||
{
|
||||
low16 = context.SignExtend8(OperandType.I32, res);
|
||||
high16 = context.SignExtend8(OperandType.I32, context.ShiftRightUI(res, Const(16)));
|
||||
}
|
||||
else
|
||||
{
|
||||
low16 = context.ZeroExtend8(OperandType.I32, res);
|
||||
high16 = context.ZeroExtend8(OperandType.I32, context.ShiftRightUI(res, Const(16)));
|
||||
}
|
||||
|
||||
if (op.Add)
|
||||
{
|
||||
Operand n = GetAluN(context);
|
||||
Operand lowAdd, highAdd;
|
||||
if (signed)
|
||||
{
|
||||
lowAdd = context.SignExtend16(OperandType.I32, n);
|
||||
highAdd = context.SignExtend16(OperandType.I32, context.ShiftRightUI(n, Const(16)));
|
||||
}
|
||||
else
|
||||
{
|
||||
lowAdd = context.ZeroExtend16(OperandType.I32, n);
|
||||
highAdd = context.ZeroExtend16(OperandType.I32, context.ShiftRightUI(n, Const(16)));
|
||||
}
|
||||
|
||||
low16 = context.Add(low16, lowAdd);
|
||||
high16 = context.Add(high16, highAdd);
|
||||
}
|
||||
|
||||
res = context.BitwiseOr(
|
||||
context.ZeroExtend16(OperandType.I32, low16),
|
||||
context.ShiftLeft(context.ZeroExtend16(OperandType.I32, high16), Const(16)));
|
||||
|
||||
EmitAluStore(context, res);
|
||||
}
|
||||
|
||||
private static void EmitDiv(ArmEmitterContext context, bool unsigned)
|
||||
{
|
||||
Operand n = GetAluN(context);
|
||||
Operand m = GetAluM(context);
|
||||
Operand zero = Const(m.Type, 0);
|
||||
|
||||
Operand divisorIsZero = context.ICompareEqual(m, zero);
|
||||
|
||||
Operand lblBadDiv = Label();
|
||||
Operand lblEnd = Label();
|
||||
|
||||
context.BranchIfTrue(lblBadDiv, divisorIsZero);
|
||||
|
||||
if (!unsigned)
|
||||
{
|
||||
// ARM64 behaviour: If Rn == INT_MIN && Rm == -1, Rd = INT_MIN (overflow).
|
||||
// TODO: tests to ensure A32 works the same
|
||||
|
||||
Operand intMin = Const(int.MinValue);
|
||||
Operand minus1 = Const(-1);
|
||||
|
||||
Operand nIsIntMin = context.ICompareEqual(n, intMin);
|
||||
Operand mIsMinus1 = context.ICompareEqual(m, minus1);
|
||||
|
||||
Operand lblGoodDiv = Label();
|
||||
|
||||
context.BranchIfFalse(lblGoodDiv, context.BitwiseAnd(nIsIntMin, mIsMinus1));
|
||||
|
||||
EmitAluStore(context, intMin);
|
||||
|
||||
context.Branch(lblEnd);
|
||||
|
||||
context.MarkLabel(lblGoodDiv);
|
||||
}
|
||||
|
||||
Operand res = unsigned
|
||||
? context.DivideUI(n, m)
|
||||
: context.Divide(n, m);
|
||||
|
||||
EmitAluStore(context, res);
|
||||
|
||||
context.Branch(lblEnd);
|
||||
|
||||
context.MarkLabel(lblBadDiv);
|
||||
|
||||
EmitAluStore(context, zero);
|
||||
|
||||
context.MarkLabel(lblEnd);
|
||||
}
|
||||
|
||||
private static void EmitAddSub8(ArmEmitterContext context, bool add, bool unsigned)
|
||||
{
|
||||
IOpCode32AluReg op = (IOpCode32AluReg)context.CurrOp;
|
||||
|
||||
Operand n = GetIntA32(context, op.Rn);
|
||||
Operand m = GetIntA32(context, op.Rm);
|
||||
|
||||
Operand res = Const(0);
|
||||
|
||||
for (int byteSel = 0; byteSel < 4; byteSel++)
|
||||
{
|
||||
Operand shift = Const(byteSel * 8);
|
||||
|
||||
Operand nByte = context.ShiftRightUI(n, shift);
|
||||
Operand mByte = context.ShiftRightUI(m, shift);
|
||||
|
||||
nByte = unsigned ? context.ZeroExtend8(OperandType.I32, nByte) : context.SignExtend8(OperandType.I32, nByte);
|
||||
mByte = unsigned ? context.ZeroExtend8(OperandType.I32, mByte) : context.SignExtend8(OperandType.I32, mByte);
|
||||
|
||||
Operand resByte = add ? context.Add(nByte, mByte) : context.Subtract(nByte, mByte);
|
||||
|
||||
res = context.BitwiseOr(res, context.ShiftLeft(context.ZeroExtend8(OperandType.I32, resByte), shift));
|
||||
|
||||
SetFlag(context, PState.GE0Flag + byteSel, unsigned && add
|
||||
? context.ShiftRightUI(resByte, Const(8))
|
||||
: context.ShiftRightUI(context.BitwiseNot(resByte), Const(31)));
|
||||
}
|
||||
|
||||
SetIntA32(context, op.Rd, res);
|
||||
}
|
||||
|
||||
private static void EmitHadd8(ArmEmitterContext context, bool unsigned)
|
||||
{
|
||||
IOpCode32AluReg op = (IOpCode32AluReg)context.CurrOp;
|
||||
|
||||
Operand m = GetIntA32(context, op.Rm);
|
||||
Operand n = GetIntA32(context, op.Rn);
|
||||
|
||||
Operand xor, res, carry;
|
||||
|
||||
// This relies on the equality x+y == ((x&y) << 1) + (x^y).
|
||||
// Note that x^y always contains the LSB of the result.
|
||||
// Since we want to calculate (x+y)/2, we can instead calculate (x&y) + ((x^y)>>1).
|
||||
// We mask by 0x7F to remove the LSB so that it doesn't leak into the field below.
|
||||
|
||||
res = context.BitwiseAnd(m, n);
|
||||
carry = context.BitwiseExclusiveOr(m, n);
|
||||
xor = context.ShiftRightUI(carry, Const(1));
|
||||
xor = context.BitwiseAnd(xor, Const(0x7F7F7F7Fu));
|
||||
res = context.Add(res, xor);
|
||||
|
||||
if (!unsigned)
|
||||
{
|
||||
// Propagates the sign bit from (x^y)>>1 upwards by one.
|
||||
carry = context.BitwiseAnd(carry, Const(0x80808080u));
|
||||
res = context.BitwiseExclusiveOr(res, carry);
|
||||
}
|
||||
|
||||
SetIntA32(context, op.Rd, res);
|
||||
}
|
||||
|
||||
private static void EmitHsub8(ArmEmitterContext context, bool unsigned)
|
||||
{
|
||||
IOpCode32AluReg op = (IOpCode32AluReg)context.CurrOp;
|
||||
|
||||
Operand m = GetIntA32(context, op.Rm);
|
||||
Operand n = GetIntA32(context, op.Rn);
|
||||
Operand left, right, carry, res;
|
||||
|
||||
// This relies on the equality x-y == (x^y) - (((x^y)&y) << 1).
|
||||
// Note that x^y always contains the LSB of the result.
|
||||
// Since we want to calculate (x+y)/2, we can instead calculate ((x^y)>>1) - ((x^y)&y).
|
||||
|
||||
carry = context.BitwiseExclusiveOr(m, n);
|
||||
left = context.ShiftRightUI(carry, Const(1));
|
||||
right = context.BitwiseAnd(carry, m);
|
||||
|
||||
// We must now perform a partitioned subtraction.
|
||||
// We can do this because minuend contains 7 bit fields.
|
||||
// We use the extra bit in minuend as a bit to borrow from; we set this bit.
|
||||
// We invert this bit at the end as this tells us if that bit was borrowed from.
|
||||
|
||||
res = context.BitwiseOr(left, Const(0x80808080));
|
||||
res = context.Subtract(res, right);
|
||||
res = context.BitwiseExclusiveOr(res, Const(0x80808080));
|
||||
|
||||
if (!unsigned)
|
||||
{
|
||||
// We then sign extend the result into this bit.
|
||||
carry = context.BitwiseAnd(carry, Const(0x80808080));
|
||||
res = context.BitwiseExclusiveOr(res, carry);
|
||||
}
|
||||
|
||||
SetIntA32(context, op.Rd, res);
|
||||
}
|
||||
|
||||
private static void EmitSat(ArmEmitterContext context, int intMin, int intMax)
|
||||
{
|
||||
OpCode32Sat op = (OpCode32Sat)context.CurrOp;
|
||||
|
||||
Operand n = GetIntA32(context, op.Rn);
|
||||
|
||||
int shift = DecodeImmShift(op.ShiftType, op.Imm5);
|
||||
|
||||
switch (op.ShiftType)
|
||||
{
|
||||
case ShiftType.Lsl:
|
||||
if (shift == 32)
|
||||
{
|
||||
n = Const(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
n = context.ShiftLeft(n, Const(shift));
|
||||
}
|
||||
break;
|
||||
case ShiftType.Asr:
|
||||
if (shift == 32)
|
||||
{
|
||||
n = context.ShiftRightSI(n, Const(31));
|
||||
}
|
||||
else
|
||||
{
|
||||
n = context.ShiftRightSI(n, Const(shift));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
Operand lblCheckLtIntMin = Label();
|
||||
Operand lblNoSat = Label();
|
||||
Operand lblEnd = Label();
|
||||
|
||||
context.BranchIfFalse(lblCheckLtIntMin, context.ICompareGreater(n, Const(intMax)));
|
||||
|
||||
SetFlag(context, PState.QFlag, Const(1));
|
||||
SetIntA32(context, op.Rd, Const(intMax));
|
||||
context.Branch(lblEnd);
|
||||
|
||||
context.MarkLabel(lblCheckLtIntMin);
|
||||
context.BranchIfFalse(lblNoSat, context.ICompareLess(n, Const(intMin)));
|
||||
|
||||
SetFlag(context, PState.QFlag, Const(1));
|
||||
SetIntA32(context, op.Rd, Const(intMin));
|
||||
context.Branch(lblEnd);
|
||||
|
||||
context.MarkLabel(lblNoSat);
|
||||
|
||||
SetIntA32(context, op.Rd, n);
|
||||
|
||||
context.MarkLabel(lblEnd);
|
||||
}
|
||||
|
||||
private static void EmitSat16(ArmEmitterContext context, int intMin, int intMax)
|
||||
{
|
||||
OpCode32Sat16 op = (OpCode32Sat16)context.CurrOp;
|
||||
|
||||
void SetD(int part, Operand value)
|
||||
{
|
||||
if (part == 0)
|
||||
{
|
||||
SetIntA32(context, op.Rd, context.ZeroExtend16(OperandType.I32, value));
|
||||
}
|
||||
else
|
||||
{
|
||||
SetIntA32(context, op.Rd, context.BitwiseOr(GetIntA32(context, op.Rd), context.ShiftLeft(value, Const(16))));
|
||||
}
|
||||
}
|
||||
|
||||
Operand n = GetIntA32(context, op.Rn);
|
||||
|
||||
Operand nLow = context.SignExtend16(OperandType.I32, n);
|
||||
Operand nHigh = context.ShiftRightSI(n, Const(16));
|
||||
|
||||
for (int part = 0; part < 2; part++)
|
||||
{
|
||||
Operand nPart = part == 0 ? nLow : nHigh;
|
||||
|
||||
Operand lblCheckLtIntMin = Label();
|
||||
Operand lblNoSat = Label();
|
||||
Operand lblEnd = Label();
|
||||
|
||||
context.BranchIfFalse(lblCheckLtIntMin, context.ICompareGreater(nPart, Const(intMax)));
|
||||
|
||||
SetFlag(context, PState.QFlag, Const(1));
|
||||
SetD(part, Const(intMax));
|
||||
context.Branch(lblEnd);
|
||||
|
||||
context.MarkLabel(lblCheckLtIntMin);
|
||||
context.BranchIfFalse(lblNoSat, context.ICompareLess(nPart, Const(intMin)));
|
||||
|
||||
SetFlag(context, PState.QFlag, Const(1));
|
||||
SetD(part, Const(intMin));
|
||||
context.Branch(lblEnd);
|
||||
|
||||
context.MarkLabel(lblNoSat);
|
||||
|
||||
SetD(part, nPart);
|
||||
|
||||
context.MarkLabel(lblEnd);
|
||||
}
|
||||
}
|
||||
|
||||
private static void EmitAluStore(ArmEmitterContext context, Operand value)
|
||||
{
|
||||
IOpCode32Alu op = (IOpCode32Alu)context.CurrOp;
|
||||
|
||||
EmitGenericAluStoreA32(context, op.Rd, ShouldSetFlags(context), value);
|
||||
}
|
||||
}
|
||||
}
|
||||
613
src/ARMeilleure/Instructions/InstEmitAluHelper.cs
Normal file
613
src/ARMeilleure/Instructions/InstEmitAluHelper.cs
Normal file
@@ -0,0 +1,613 @@
|
||||
using ARMeilleure.Decoders;
|
||||
using ARMeilleure.IntermediateRepresentation;
|
||||
using ARMeilleure.State;
|
||||
using ARMeilleure.Translation;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
|
||||
using static ARMeilleure.Instructions.InstEmitHelper;
|
||||
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
|
||||
|
||||
namespace ARMeilleure.Instructions
|
||||
{
|
||||
static class InstEmitAluHelper
|
||||
{
|
||||
public static bool ShouldSetFlags(ArmEmitterContext context)
|
||||
{
|
||||
IOpCode32HasSetFlags op = (IOpCode32HasSetFlags)context.CurrOp;
|
||||
|
||||
if (op.SetFlags == null)
|
||||
{
|
||||
return !context.IsInIfThenBlock;
|
||||
}
|
||||
|
||||
return op.SetFlags.Value;
|
||||
}
|
||||
|
||||
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 EmitReverseBits32Op(ArmEmitterContext context, Operand op)
|
||||
{
|
||||
Debug.Assert(op.Type == OperandType.I32);
|
||||
|
||||
Operand val = context.BitwiseOr(context.ShiftRightUI(context.BitwiseAnd(op, Const(0xaaaaaaaau)), Const(1)),
|
||||
context.ShiftLeft(context.BitwiseAnd(op, Const(0x55555555u)), Const(1)));
|
||||
|
||||
val = context.BitwiseOr(context.ShiftRightUI(context.BitwiseAnd(val, Const(0xccccccccu)), Const(2)),
|
||||
context.ShiftLeft(context.BitwiseAnd(val, Const(0x33333333u)), Const(2)));
|
||||
val = context.BitwiseOr(context.ShiftRightUI(context.BitwiseAnd(val, Const(0xf0f0f0f0u)), Const(4)),
|
||||
context.ShiftLeft(context.BitwiseAnd(val, Const(0x0f0f0f0fu)), Const(4)));
|
||||
val = context.BitwiseOr(context.ShiftRightUI(context.BitwiseAnd(val, Const(0xff00ff00u)), Const(8)),
|
||||
context.ShiftLeft(context.BitwiseAnd(val, Const(0x00ff00ffu)), Const(8)));
|
||||
|
||||
return context.BitwiseOr(context.ShiftRightUI(val, Const(16)), context.ShiftLeft(val, Const(16)));
|
||||
}
|
||||
|
||||
public static Operand EmitReverseBytes16_64Op(ArmEmitterContext context, Operand op)
|
||||
{
|
||||
Debug.Assert(op.Type == OperandType.I64);
|
||||
|
||||
return context.BitwiseOr(context.ShiftRightUI(context.BitwiseAnd(op, Const(0xff00ff00ff00ff00ul)), Const(8)),
|
||||
context.ShiftLeft(context.BitwiseAnd(op, Const(0x00ff00ff00ff00fful)), Const(8)));
|
||||
}
|
||||
|
||||
public static Operand EmitReverseBytes16_32Op(ArmEmitterContext context, Operand op)
|
||||
{
|
||||
Debug.Assert(op.Type == OperandType.I32);
|
||||
|
||||
Operand val = EmitReverseBytes16_64Op(context, context.ZeroExtend32(OperandType.I64, op));
|
||||
|
||||
return context.ConvertI64ToI32(val);
|
||||
}
|
||||
|
||||
private static void EmitAluWritePc(ArmEmitterContext context, Operand value)
|
||||
{
|
||||
Debug.Assert(value.Type == OperandType.I32);
|
||||
|
||||
if (((OpCode32)context.CurrOp).IsThumb)
|
||||
{
|
||||
bool isReturn = IsA32Return(context);
|
||||
if (!isReturn)
|
||||
{
|
||||
context.StoreToContext();
|
||||
}
|
||||
|
||||
InstEmitFlowHelper.EmitVirtualJump(context, value, isReturn);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitBxWritePc(context, value);
|
||||
}
|
||||
}
|
||||
|
||||
public static void EmitGenericAluStoreA32(ArmEmitterContext context, int rd, bool setFlags, Operand value)
|
||||
{
|
||||
Debug.Assert(value.Type == OperandType.I32);
|
||||
|
||||
if (rd == RegisterAlias.Aarch32Pc && setFlags)
|
||||
{
|
||||
if (setFlags)
|
||||
{
|
||||
// TODO: Load SPSR etc.
|
||||
|
||||
EmitBxWritePc(context, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitAluWritePc(context, value);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
SetIntA32(context, rd, value);
|
||||
}
|
||||
}
|
||||
|
||||
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 IOpCode32AluImm op:
|
||||
{
|
||||
if (ShouldSetFlags(context) && op.IsRotated && setCarry)
|
||||
{
|
||||
SetFlag(context, PState.CFlag, Const((uint)op.Immediate >> 31));
|
||||
}
|
||||
|
||||
return Const(op.Immediate);
|
||||
}
|
||||
|
||||
case IOpCode32AluImm16 op: return Const(op.Immediate);
|
||||
|
||||
case IOpCode32AluRsImm op: return GetMShiftedByImmediate(context, op, setCarry);
|
||||
case IOpCode32AluRsReg op: return GetMShiftedByReg(context, op, setCarry);
|
||||
|
||||
case IOpCode32AluReg op: return GetIntA32(context, op.Rm);
|
||||
|
||||
// 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.
|
||||
public static Operand GetMShiftedByImmediate(ArmEmitterContext context, IOpCode32AluRsImm op, bool setCarry)
|
||||
{
|
||||
Operand m = GetIntA32(context, op.Rm);
|
||||
|
||||
int shift = op.Immediate;
|
||||
|
||||
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 &= ShouldSetFlags(context);
|
||||
|
||||
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.Immediate != 0)
|
||||
{
|
||||
m = GetRorC(context, m, setCarry, shift);
|
||||
}
|
||||
else
|
||||
{
|
||||
m = GetRrxC(context, m, setCarry);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
public static int DecodeImmShift(ShiftType shiftType, int shift)
|
||||
{
|
||||
if (shift == 0)
|
||||
{
|
||||
switch (shiftType)
|
||||
{
|
||||
case ShiftType.Lsr: shift = 32; break;
|
||||
case ShiftType.Asr: shift = 32; break;
|
||||
case ShiftType.Ror: shift = 1; break;
|
||||
}
|
||||
}
|
||||
|
||||
return shift;
|
||||
}
|
||||
|
||||
public static Operand GetMShiftedByReg(ArmEmitterContext context, IOpCode32AluRsReg op, bool setCarry)
|
||||
{
|
||||
Operand m = GetIntA32(context, op.Rm);
|
||||
Operand s = context.ZeroExtend8(OperandType.I32, GetIntA32(context, op.Rs));
|
||||
Operand shiftIsZero = context.ICompareEqual(s, Const(0));
|
||||
|
||||
Operand zeroResult = m;
|
||||
Operand shiftResult = m;
|
||||
|
||||
setCarry &= ShouldSetFlags(context);
|
||||
|
||||
switch (op.ShiftType)
|
||||
{
|
||||
case ShiftType.Lsl: shiftResult = EmitLslC(context, m, setCarry, s, shiftIsZero); break;
|
||||
case ShiftType.Lsr: shiftResult = EmitLsrC(context, m, setCarry, s, shiftIsZero); break;
|
||||
case ShiftType.Asr: shiftResult = EmitAsrC(context, m, setCarry, s, shiftIsZero); break;
|
||||
case ShiftType.Ror: shiftResult = EmitRorC(context, m, setCarry, s, shiftIsZero); break;
|
||||
}
|
||||
|
||||
return context.ConditionalSelect(shiftIsZero, zeroResult, shiftResult);
|
||||
}
|
||||
|
||||
public static void EmitIfHelper(ArmEmitterContext context, Operand boolValue, Action action, bool expected = true)
|
||||
{
|
||||
Debug.Assert(boolValue.Type == OperandType.I32);
|
||||
|
||||
Operand endLabel = Label();
|
||||
|
||||
if (expected)
|
||||
{
|
||||
context.BranchIfFalse(endLabel, boolValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
context.BranchIfTrue(endLabel, boolValue);
|
||||
}
|
||||
|
||||
action();
|
||||
|
||||
context.MarkLabel(endLabel);
|
||||
}
|
||||
|
||||
public static Operand EmitLslC(ArmEmitterContext context, Operand m, bool setCarry, Operand shift, Operand shiftIsZero)
|
||||
{
|
||||
Debug.Assert(m.Type == OperandType.I32 && shift.Type == OperandType.I32 && shiftIsZero.Type == OperandType.I32);
|
||||
|
||||
Operand shiftLarge = context.ICompareGreaterOrEqual(shift, Const(32));
|
||||
Operand result = context.ShiftLeft(m, shift);
|
||||
if (setCarry)
|
||||
{
|
||||
EmitIfHelper(context, shiftIsZero, () =>
|
||||
{
|
||||
Operand cOut = context.ShiftRightUI(m, context.Subtract(Const(32), shift));
|
||||
|
||||
cOut = context.BitwiseAnd(cOut, Const(1));
|
||||
cOut = context.ConditionalSelect(context.ICompareGreater(shift, Const(32)), Const(0), cOut);
|
||||
|
||||
SetFlag(context, PState.CFlag, cOut);
|
||||
}, false);
|
||||
}
|
||||
|
||||
return context.ConditionalSelect(shiftLarge, Const(0), result);
|
||||
}
|
||||
|
||||
public static Operand GetLslC(ArmEmitterContext context, Operand m, bool setCarry, int shift)
|
||||
{
|
||||
Debug.Assert(m.Type == OperandType.I32);
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
public static Operand EmitLsrC(ArmEmitterContext context, Operand m, bool setCarry, Operand shift, Operand shiftIsZero)
|
||||
{
|
||||
Debug.Assert(m.Type == OperandType.I32 && shift.Type == OperandType.I32 && shiftIsZero.Type == OperandType.I32);
|
||||
|
||||
Operand shiftLarge = context.ICompareGreaterOrEqual(shift, Const(32));
|
||||
Operand result = context.ShiftRightUI(m, shift);
|
||||
if (setCarry)
|
||||
{
|
||||
EmitIfHelper(context, shiftIsZero, () =>
|
||||
{
|
||||
Operand cOut = context.ShiftRightUI(m, context.Subtract(shift, Const(1)));
|
||||
|
||||
cOut = context.BitwiseAnd(cOut, Const(1));
|
||||
cOut = context.ConditionalSelect(context.ICompareGreater(shift, Const(32)), Const(0), cOut);
|
||||
|
||||
SetFlag(context, PState.CFlag, cOut);
|
||||
}, false);
|
||||
}
|
||||
|
||||
return context.ConditionalSelect(shiftLarge, Const(0), result);
|
||||
}
|
||||
|
||||
public static Operand GetLsrC(ArmEmitterContext context, Operand m, bool setCarry, int shift)
|
||||
{
|
||||
Debug.Assert(m.Type == OperandType.I32);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
public static Operand EmitAsrC(ArmEmitterContext context, Operand m, bool setCarry, Operand shift, Operand shiftIsZero)
|
||||
{
|
||||
Debug.Assert(m.Type == OperandType.I32 && shift.Type == OperandType.I32 && shiftIsZero.Type == OperandType.I32);
|
||||
|
||||
Operand l32Result;
|
||||
Operand ge32Result;
|
||||
|
||||
Operand less32 = context.ICompareLess(shift, Const(32));
|
||||
|
||||
ge32Result = context.ShiftRightSI(m, Const(31));
|
||||
|
||||
if (setCarry)
|
||||
{
|
||||
EmitIfHelper(context, context.BitwiseOr(less32, shiftIsZero), () =>
|
||||
{
|
||||
SetCarryMLsb(context, ge32Result);
|
||||
}, false);
|
||||
}
|
||||
|
||||
l32Result = context.ShiftRightSI(m, shift);
|
||||
if (setCarry)
|
||||
{
|
||||
EmitIfHelper(context, context.BitwiseAnd(less32, context.BitwiseNot(shiftIsZero)), () =>
|
||||
{
|
||||
Operand cOut = context.ShiftRightUI(m, context.Subtract(shift, Const(1)));
|
||||
|
||||
cOut = context.BitwiseAnd(cOut, Const(1));
|
||||
|
||||
SetFlag(context, PState.CFlag, cOut);
|
||||
});
|
||||
}
|
||||
|
||||
return context.ConditionalSelect(less32, l32Result, ge32Result);
|
||||
}
|
||||
|
||||
public static Operand GetAsrC(ArmEmitterContext context, Operand m, bool setCarry, int shift)
|
||||
{
|
||||
Debug.Assert(m.Type == OperandType.I32);
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
public static Operand EmitRorC(ArmEmitterContext context, Operand m, bool setCarry, Operand shift, Operand shiftIsZero)
|
||||
{
|
||||
Debug.Assert(m.Type == OperandType.I32 && shift.Type == OperandType.I32 && shiftIsZero.Type == OperandType.I32);
|
||||
|
||||
shift = context.BitwiseAnd(shift, Const(0x1f));
|
||||
m = context.RotateRight(m, shift);
|
||||
|
||||
if (setCarry)
|
||||
{
|
||||
EmitIfHelper(context, shiftIsZero, () =>
|
||||
{
|
||||
SetCarryMMsb(context, m);
|
||||
}, false);
|
||||
}
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
public static Operand GetRorC(ArmEmitterContext context, Operand m, bool setCarry, int shift)
|
||||
{
|
||||
Debug.Assert(m.Type == OperandType.I32);
|
||||
|
||||
shift &= 0x1f;
|
||||
|
||||
m = context.RotateRight(m, Const(shift));
|
||||
|
||||
if (setCarry)
|
||||
{
|
||||
SetCarryMMsb(context, m);
|
||||
}
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
public static Operand GetRrxC(ArmEmitterContext context, Operand m, bool setCarry)
|
||||
{
|
||||
Debug.Assert(m.Type == OperandType.I32);
|
||||
|
||||
// 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)
|
||||
{
|
||||
Debug.Assert(m.Type == OperandType.I32);
|
||||
|
||||
SetFlag(context, PState.CFlag, context.BitwiseAnd(m, Const(1)));
|
||||
}
|
||||
|
||||
private static void SetCarryMMsb(ArmEmitterContext context, Operand m)
|
||||
{
|
||||
Debug.Assert(m.Type == OperandType.I32);
|
||||
|
||||
SetFlag(context, PState.CFlag, context.ShiftRightUI(m, Const(31)));
|
||||
}
|
||||
|
||||
private static void SetCarryMShrOut(ArmEmitterContext context, Operand m, int shift)
|
||||
{
|
||||
Debug.Assert(m.Type == OperandType.I32);
|
||||
|
||||
Operand cOut = context.ShiftRightUI(m, Const(shift - 1));
|
||||
|
||||
cOut = context.BitwiseAnd(cOut, Const(1));
|
||||
|
||||
SetFlag(context, PState.CFlag, cOut);
|
||||
}
|
||||
}
|
||||
}
|
||||
196
src/ARMeilleure/Instructions/InstEmitBfm.cs
Normal file
196
src/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.Operand.Factory;
|
||||
|
||||
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
src/ARMeilleure/Instructions/InstEmitCcmp.cs
Normal file
61
src/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.Operand.Factory;
|
||||
|
||||
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
src/ARMeilleure/Instructions/InstEmitCsel.cs
Normal file
53
src/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.Operand.Factory;
|
||||
|
||||
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
src/ARMeilleure/Instructions/InstEmitDiv.cs
Normal file
67
src/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.Operand.Factory;
|
||||
|
||||
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
src/ARMeilleure/Instructions/InstEmitException.cs
Normal file
55
src/ARMeilleure/Instructions/InstEmitException.cs
Normal file
@@ -0,0 +1,55 @@
|
||||
using ARMeilleure.Decoders;
|
||||
using ARMeilleure.Translation;
|
||||
|
||||
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
|
||||
|
||||
namespace ARMeilleure.Instructions
|
||||
{
|
||||
static partial class InstEmit
|
||||
{
|
||||
public static void Brk(ArmEmitterContext context)
|
||||
{
|
||||
OpCodeException op = (OpCodeException)context.CurrOp;
|
||||
|
||||
string name = nameof(NativeInterface.Break);
|
||||
|
||||
context.StoreToContext();
|
||||
|
||||
context.Call(typeof(NativeInterface).GetMethod(name), Const(op.Address), Const(op.Id));
|
||||
|
||||
context.LoadFromContext();
|
||||
|
||||
context.Return(Const(op.Address));
|
||||
}
|
||||
|
||||
public static void Svc(ArmEmitterContext context)
|
||||
{
|
||||
OpCodeException op = (OpCodeException)context.CurrOp;
|
||||
|
||||
string name = nameof(NativeInterface.SupervisorCall);
|
||||
|
||||
context.StoreToContext();
|
||||
|
||||
context.Call(typeof(NativeInterface).GetMethod(name), Const(op.Address), Const(op.Id));
|
||||
|
||||
context.LoadFromContext();
|
||||
|
||||
Translator.EmitSynchronization(context);
|
||||
}
|
||||
|
||||
public static void Und(ArmEmitterContext context)
|
||||
{
|
||||
OpCode op = context.CurrOp;
|
||||
|
||||
string name = nameof(NativeInterface.Undefined);
|
||||
|
||||
context.StoreToContext();
|
||||
|
||||
context.Call(typeof(NativeInterface).GetMethod(name), Const(op.Address), Const(op.RawOpCode));
|
||||
|
||||
context.LoadFromContext();
|
||||
|
||||
context.Return(Const(op.Address));
|
||||
}
|
||||
}
|
||||
}
|
||||
39
src/ARMeilleure/Instructions/InstEmitException32.cs
Normal file
39
src/ARMeilleure/Instructions/InstEmitException32.cs
Normal file
@@ -0,0 +1,39 @@
|
||||
using ARMeilleure.Decoders;
|
||||
using ARMeilleure.Translation;
|
||||
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
|
||||
|
||||
namespace ARMeilleure.Instructions
|
||||
{
|
||||
static partial class InstEmit32
|
||||
{
|
||||
public static void Svc(ArmEmitterContext context)
|
||||
{
|
||||
IOpCode32Exception op = (IOpCode32Exception)context.CurrOp;
|
||||
|
||||
string name = nameof(NativeInterface.SupervisorCall);
|
||||
|
||||
context.StoreToContext();
|
||||
|
||||
context.Call(typeof(NativeInterface).GetMethod(name), Const(((IOpCode)op).Address), Const(op.Id));
|
||||
|
||||
context.LoadFromContext();
|
||||
|
||||
Translator.EmitSynchronization(context);
|
||||
}
|
||||
|
||||
public static void Trap(ArmEmitterContext context)
|
||||
{
|
||||
IOpCode32Exception op = (IOpCode32Exception)context.CurrOp;
|
||||
|
||||
string name = nameof(NativeInterface.Break);
|
||||
|
||||
context.StoreToContext();
|
||||
|
||||
context.Call(typeof(NativeInterface).GetMethod(name), Const(((IOpCode)op).Address), Const(op.Id));
|
||||
|
||||
context.LoadFromContext();
|
||||
|
||||
context.Return(Const(context.CurrOp.Address));
|
||||
}
|
||||
}
|
||||
}
|
||||
107
src/ARMeilleure/Instructions/InstEmitFlow.cs
Normal file
107
src/ARMeilleure/Instructions/InstEmitFlow.cs
Normal file
@@ -0,0 +1,107 @@
|
||||
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.Operand.Factory;
|
||||
|
||||
namespace ARMeilleure.Instructions
|
||||
{
|
||||
static partial class InstEmit
|
||||
{
|
||||
public static void B(ArmEmitterContext context)
|
||||
{
|
||||
OpCodeBImmAl op = (OpCodeBImmAl)context.CurrOp;
|
||||
|
||||
context.Branch(context.GetLabel((ulong)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), op.Rn == RegisterAlias.Lr);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
OpCodeBReg op = (OpCodeBReg)context.CurrOp;
|
||||
|
||||
context.Return(GetIntOrZR(context, op.Rn));
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
EmitCondBranch(context, context.GetLabel((ulong)op.Immediate), cond);
|
||||
}
|
||||
|
||||
private static void EmitBranch(ArmEmitterContext context, Operand value, bool onNotZero)
|
||||
{
|
||||
OpCodeBImm op = (OpCodeBImm)context.CurrOp;
|
||||
|
||||
Operand lblTarget = context.GetLabel((ulong)op.Immediate);
|
||||
|
||||
if (onNotZero)
|
||||
{
|
||||
context.BranchIfTrue(lblTarget, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
context.BranchIfFalse(lblTarget, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
136
src/ARMeilleure/Instructions/InstEmitFlow32.cs
Normal file
136
src/ARMeilleure/Instructions/InstEmitFlow32.cs
Normal file
@@ -0,0 +1,136 @@
|
||||
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.Operand.Factory;
|
||||
|
||||
namespace ARMeilleure.Instructions
|
||||
{
|
||||
static partial class InstEmit32
|
||||
{
|
||||
public static void B(ArmEmitterContext context)
|
||||
{
|
||||
IOpCode32BImm op = (IOpCode32BImm)context.CurrOp;
|
||||
|
||||
context.Branch(context.GetLabel((ulong)op.Immediate));
|
||||
}
|
||||
|
||||
public static void Bl(ArmEmitterContext context)
|
||||
{
|
||||
Blx(context, x: false);
|
||||
}
|
||||
|
||||
public static void Blx(ArmEmitterContext context)
|
||||
{
|
||||
Blx(context, x: true);
|
||||
}
|
||||
|
||||
private static void Blx(ArmEmitterContext context, bool x)
|
||||
{
|
||||
IOpCode32BImm op = (IOpCode32BImm)context.CurrOp;
|
||||
|
||||
uint pc = op.GetPc();
|
||||
|
||||
bool isThumb = ((OpCode32)context.CurrOp).IsThumb;
|
||||
|
||||
uint currentPc = isThumb
|
||||
? pc | 1
|
||||
: pc - 4;
|
||||
|
||||
SetIntA32(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));
|
||||
}
|
||||
|
||||
EmitCall(context, (ulong)op.Immediate);
|
||||
}
|
||||
|
||||
public static void Blxr(ArmEmitterContext context)
|
||||
{
|
||||
IOpCode32BReg op = (IOpCode32BReg)context.CurrOp;
|
||||
|
||||
uint pc = op.GetPc();
|
||||
|
||||
Operand addr = context.Copy(GetIntA32(context, op.Rm));
|
||||
Operand bitOne = context.BitwiseAnd(addr, Const(1));
|
||||
|
||||
bool isThumb = ((OpCode32)context.CurrOp).IsThumb;
|
||||
|
||||
uint currentPc = isThumb
|
||||
? (pc - 2) | 1
|
||||
: pc - 4;
|
||||
|
||||
SetIntA32(context, GetBankedRegisterAlias(context.Mode, RegisterAlias.Aarch32Lr), Const(currentPc));
|
||||
|
||||
SetFlag(context, PState.TFlag, bitOne);
|
||||
|
||||
EmitBxWritePc(context, addr);
|
||||
}
|
||||
|
||||
public static void Bx(ArmEmitterContext context)
|
||||
{
|
||||
IOpCode32BReg op = (IOpCode32BReg)context.CurrOp;
|
||||
|
||||
EmitBxWritePc(context, GetIntA32(context, op.Rm), op.Rm);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
OpCodeT16BImmCmp op = (OpCodeT16BImmCmp)context.CurrOp;
|
||||
|
||||
Operand value = GetIntA32(context, op.Rn);
|
||||
Operand lblTarget = context.GetLabel((ulong)op.Immediate);
|
||||
|
||||
if (onNotZero)
|
||||
{
|
||||
context.BranchIfTrue(lblTarget, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
context.BranchIfFalse(lblTarget, value);
|
||||
}
|
||||
}
|
||||
|
||||
public static void It(ArmEmitterContext context)
|
||||
{
|
||||
OpCodeT16IfThen op = (OpCodeT16IfThen)context.CurrOp;
|
||||
|
||||
context.SetIfThenBlockState(op.IfThenBlockConds);
|
||||
}
|
||||
|
||||
public static void Tbb(ArmEmitterContext context) => EmitTb(context, halfword: false);
|
||||
public static void Tbh(ArmEmitterContext context) => EmitTb(context, halfword: true);
|
||||
|
||||
private static void EmitTb(ArmEmitterContext context, bool halfword)
|
||||
{
|
||||
OpCodeT32Tb op = (OpCodeT32Tb)context.CurrOp;
|
||||
|
||||
Operand halfwords;
|
||||
|
||||
if (halfword)
|
||||
{
|
||||
Operand address = context.Add(GetIntA32(context, op.Rn), context.ShiftLeft(GetIntA32(context, op.Rm), Const(1)));
|
||||
halfwords = InstEmitMemoryHelper.EmitReadInt(context, address, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
Operand address = context.Add(GetIntA32(context, op.Rn), GetIntA32(context, op.Rm));
|
||||
halfwords = InstEmitMemoryHelper.EmitReadIntAligned(context, address, 0);
|
||||
}
|
||||
|
||||
Operand targetAddress = context.Add(Const((int)op.GetPc()), context.ShiftLeft(halfwords, Const(1)));
|
||||
|
||||
EmitVirtualJump(context, targetAddress, isReturn: false);
|
||||
}
|
||||
}
|
||||
}
|
||||
240
src/ARMeilleure/Instructions/InstEmitFlowHelper.cs
Normal file
240
src/ARMeilleure/Instructions/InstEmitFlowHelper.cs
Normal file
@@ -0,0 +1,240 @@
|
||||
using ARMeilleure.CodeGen.Linking;
|
||||
using ARMeilleure.Decoders;
|
||||
using ARMeilleure.IntermediateRepresentation;
|
||||
using ARMeilleure.State;
|
||||
using ARMeilleure.Translation;
|
||||
using ARMeilleure.Translation.PTC;
|
||||
|
||||
using static ARMeilleure.Instructions.InstEmitHelper;
|
||||
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
|
||||
|
||||
namespace ARMeilleure.Instructions
|
||||
{
|
||||
static class InstEmitFlowHelper
|
||||
{
|
||||
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 != default)
|
||||
{
|
||||
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)
|
||||
{
|
||||
bool isRecursive = immediate == context.EntryAddress;
|
||||
|
||||
if (isRecursive)
|
||||
{
|
||||
context.Branch(context.GetLabel(immediate));
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitTableBranch(context, Const(immediate), isJump: false);
|
||||
}
|
||||
}
|
||||
|
||||
public static void EmitVirtualCall(ArmEmitterContext context, Operand target)
|
||||
{
|
||||
EmitTableBranch(context, target, isJump: false);
|
||||
}
|
||||
|
||||
public static void EmitVirtualJump(ArmEmitterContext context, Operand target, bool isReturn)
|
||||
{
|
||||
if (isReturn)
|
||||
{
|
||||
if (target.Type == OperandType.I32)
|
||||
{
|
||||
target = context.ZeroExtend32(OperandType.I64, target);
|
||||
}
|
||||
|
||||
context.Return(target);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitTableBranch(context, target, isJump: true);
|
||||
}
|
||||
}
|
||||
|
||||
private static void EmitTableBranch(ArmEmitterContext context, Operand guestAddress, bool isJump)
|
||||
{
|
||||
context.StoreToContext();
|
||||
|
||||
if (guestAddress.Type == OperandType.I32)
|
||||
{
|
||||
guestAddress = context.ZeroExtend32(OperandType.I64, guestAddress);
|
||||
}
|
||||
|
||||
// Store the target guest address into the native context. The stubs uses this address to dispatch into the
|
||||
// next translation.
|
||||
Operand nativeContext = context.LoadArgument(OperandType.I64, 0);
|
||||
Operand dispAddressAddr = context.Add(nativeContext, Const((ulong)NativeContext.GetDispatchAddressOffset()));
|
||||
context.Store(dispAddressAddr, guestAddress);
|
||||
|
||||
Operand hostAddress;
|
||||
|
||||
// If address is mapped onto the function table, we can skip the table walk. Otherwise we fallback
|
||||
// onto the dispatch stub.
|
||||
if (guestAddress.Kind == OperandKind.Constant && context.FunctionTable.IsValid(guestAddress.Value))
|
||||
{
|
||||
Operand hostAddressAddr = !context.HasPtc ?
|
||||
Const(ref context.FunctionTable.GetValue(guestAddress.Value)) :
|
||||
Const(ref context.FunctionTable.GetValue(guestAddress.Value), new Symbol(SymbolType.FunctionTable, guestAddress.Value));
|
||||
|
||||
hostAddress = context.Load(OperandType.I64, hostAddressAddr);
|
||||
}
|
||||
else
|
||||
{
|
||||
hostAddress = !context.HasPtc ?
|
||||
Const((long)context.Stubs.DispatchStub) :
|
||||
Const((long)context.Stubs.DispatchStub, Ptc.DispatchStubSymbol);
|
||||
}
|
||||
|
||||
if (isJump)
|
||||
{
|
||||
context.Tailcall(hostAddress, nativeContext);
|
||||
}
|
||||
else
|
||||
{
|
||||
OpCode op = context.CurrOp;
|
||||
|
||||
Operand returnAddress = context.Call(hostAddress, OperandType.I64, nativeContext);
|
||||
|
||||
context.LoadFromContext();
|
||||
|
||||
// Note: The return value of a translated function is always an Int64 with the address execution has
|
||||
// returned to. We expect this address to be immediately after the current instruction, if it isn't we
|
||||
// keep returning until we reach the dispatcher.
|
||||
Operand nextAddr = Const((long)op.Address + op.OpCodeSizeInBytes);
|
||||
|
||||
// Try to continue within this block.
|
||||
// If the return address isn't to our next instruction, we need to return so the JIT can figure out
|
||||
// what to do.
|
||||
Operand lblContinue = context.GetLabel(nextAddr.Value);
|
||||
context.BranchIf(lblContinue, returnAddress, nextAddr, Comparison.Equal, BasicBlockFrequency.Cold);
|
||||
|
||||
context.Return(returnAddress);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
69
src/ARMeilleure/Instructions/InstEmitHash.cs
Normal file
69
src/ARMeilleure/Instructions/InstEmitHash.cs
Normal file
@@ -0,0 +1,69 @@
|
||||
using ARMeilleure.Decoders;
|
||||
using ARMeilleure.IntermediateRepresentation;
|
||||
using ARMeilleure.Translation;
|
||||
|
||||
using static ARMeilleure.Instructions.InstEmitHashHelper;
|
||||
using static ARMeilleure.Instructions.InstEmitHelper;
|
||||
|
||||
namespace ARMeilleure.Instructions
|
||||
{
|
||||
static partial class InstEmit
|
||||
{
|
||||
private const int ByteSizeLog2 = 0;
|
||||
private const int HWordSizeLog2 = 1;
|
||||
private const int WordSizeLog2 = 2;
|
||||
private const int DWordSizeLog2 = 3;
|
||||
|
||||
public static void Crc32b(ArmEmitterContext context)
|
||||
{
|
||||
EmitCrc32Call(context, ByteSizeLog2, false);
|
||||
}
|
||||
|
||||
public static void Crc32h(ArmEmitterContext context)
|
||||
{
|
||||
EmitCrc32Call(context, HWordSizeLog2, false);
|
||||
}
|
||||
|
||||
public static void Crc32w(ArmEmitterContext context)
|
||||
{
|
||||
EmitCrc32Call(context, WordSizeLog2, false);
|
||||
}
|
||||
|
||||
public static void Crc32x(ArmEmitterContext context)
|
||||
{
|
||||
EmitCrc32Call(context, DWordSizeLog2, false);
|
||||
}
|
||||
|
||||
public static void Crc32cb(ArmEmitterContext context)
|
||||
{
|
||||
EmitCrc32Call(context, ByteSizeLog2, true);
|
||||
}
|
||||
|
||||
public static void Crc32ch(ArmEmitterContext context)
|
||||
{
|
||||
EmitCrc32Call(context, HWordSizeLog2, true);
|
||||
}
|
||||
|
||||
public static void Crc32cw(ArmEmitterContext context)
|
||||
{
|
||||
EmitCrc32Call(context, WordSizeLog2, true);
|
||||
}
|
||||
|
||||
public static void Crc32cx(ArmEmitterContext context)
|
||||
{
|
||||
EmitCrc32Call(context, DWordSizeLog2, true);
|
||||
}
|
||||
|
||||
private static void EmitCrc32Call(ArmEmitterContext context, int size, bool c)
|
||||
{
|
||||
OpCodeAluBinary op = (OpCodeAluBinary)context.CurrOp;
|
||||
|
||||
Operand n = GetIntOrZR(context, op.Rn);
|
||||
Operand m = GetIntOrZR(context, op.Rm);
|
||||
|
||||
Operand d = EmitCrc32(context, n, m, size, c);
|
||||
|
||||
SetIntOrZR(context, op.Rd, d);
|
||||
}
|
||||
}
|
||||
}
|
||||
53
src/ARMeilleure/Instructions/InstEmitHash32.cs
Normal file
53
src/ARMeilleure/Instructions/InstEmitHash32.cs
Normal file
@@ -0,0 +1,53 @@
|
||||
using ARMeilleure.Decoders;
|
||||
using ARMeilleure.IntermediateRepresentation;
|
||||
using ARMeilleure.Translation;
|
||||
using static ARMeilleure.Instructions.InstEmitHashHelper;
|
||||
using static ARMeilleure.Instructions.InstEmitHelper;
|
||||
|
||||
namespace ARMeilleure.Instructions
|
||||
{
|
||||
static partial class InstEmit32
|
||||
{
|
||||
public static void Crc32b(ArmEmitterContext context)
|
||||
{
|
||||
EmitCrc32Call(context, ByteSizeLog2, false);
|
||||
}
|
||||
|
||||
public static void Crc32h(ArmEmitterContext context)
|
||||
{
|
||||
EmitCrc32Call(context, HWordSizeLog2, false);
|
||||
}
|
||||
|
||||
public static void Crc32w(ArmEmitterContext context)
|
||||
{
|
||||
EmitCrc32Call(context, WordSizeLog2, false);
|
||||
}
|
||||
|
||||
public static void Crc32cb(ArmEmitterContext context)
|
||||
{
|
||||
EmitCrc32Call(context, ByteSizeLog2, true);
|
||||
}
|
||||
|
||||
public static void Crc32ch(ArmEmitterContext context)
|
||||
{
|
||||
EmitCrc32Call(context, HWordSizeLog2, true);
|
||||
}
|
||||
|
||||
public static void Crc32cw(ArmEmitterContext context)
|
||||
{
|
||||
EmitCrc32Call(context, WordSizeLog2, true);
|
||||
}
|
||||
|
||||
private static void EmitCrc32Call(ArmEmitterContext context, int size, bool c)
|
||||
{
|
||||
IOpCode32AluReg op = (IOpCode32AluReg)context.CurrOp;
|
||||
|
||||
Operand n = GetIntA32(context, op.Rn);
|
||||
Operand m = GetIntA32(context, op.Rm);
|
||||
|
||||
Operand d = EmitCrc32(context, n, m, size, c);
|
||||
|
||||
EmitAluStore(context, d);
|
||||
}
|
||||
}
|
||||
}
|
||||
118
src/ARMeilleure/Instructions/InstEmitHashHelper.cs
Normal file
118
src/ARMeilleure/Instructions/InstEmitHashHelper.cs
Normal file
@@ -0,0 +1,118 @@
|
||||
// https://www.intel.com/content/dam/www/public/us/en/documents/white-papers/fast-crc-computation-generic-polynomials-pclmulqdq-paper.pdf
|
||||
|
||||
using ARMeilleure.IntermediateRepresentation;
|
||||
using ARMeilleure.Translation;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using static ARMeilleure.Instructions.InstEmitSimdHelper;
|
||||
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
|
||||
|
||||
namespace ARMeilleure.Instructions
|
||||
{
|
||||
static class InstEmitHashHelper
|
||||
{
|
||||
public const uint Crc32RevPoly = 0xedb88320;
|
||||
public const uint Crc32cRevPoly = 0x82f63b78;
|
||||
|
||||
public static Operand EmitCrc32(ArmEmitterContext context, Operand crc, Operand value, int size, bool castagnoli)
|
||||
{
|
||||
Debug.Assert(crc.Type.IsInteger() && value.Type.IsInteger());
|
||||
Debug.Assert(size >= 0 && size < 4);
|
||||
Debug.Assert((size < 3) || (value.Type == OperandType.I64));
|
||||
|
||||
if (castagnoli && Optimizations.UseSse42)
|
||||
{
|
||||
// The CRC32 instruction does not have an immediate variant, so ensure both inputs are in registers.
|
||||
value = (value.Kind == OperandKind.Constant) ? context.Copy(value) : value;
|
||||
crc = (crc.Kind == OperandKind.Constant) ? context.Copy(crc) : crc;
|
||||
|
||||
Intrinsic op = size switch
|
||||
{
|
||||
0 => Intrinsic.X86Crc32_8,
|
||||
1 => Intrinsic.X86Crc32_16,
|
||||
_ => Intrinsic.X86Crc32,
|
||||
};
|
||||
|
||||
return (size == 3) ? context.ConvertI64ToI32(context.AddIntrinsicLong(op, crc, value)) : context.AddIntrinsicInt(op, crc, value);
|
||||
}
|
||||
else if (Optimizations.UsePclmulqdq)
|
||||
{
|
||||
return size switch
|
||||
{
|
||||
3 => EmitCrc32Optimized64(context, crc, value, castagnoli),
|
||||
_ => EmitCrc32Optimized(context, crc, value, castagnoli, size),
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
string name = (size, castagnoli) switch
|
||||
{
|
||||
(0, false) => nameof(SoftFallback.Crc32b),
|
||||
(1, false) => nameof(SoftFallback.Crc32h),
|
||||
(2, false) => nameof(SoftFallback.Crc32w),
|
||||
(3, false) => nameof(SoftFallback.Crc32x),
|
||||
(0, true) => nameof(SoftFallback.Crc32cb),
|
||||
(1, true) => nameof(SoftFallback.Crc32ch),
|
||||
(2, true) => nameof(SoftFallback.Crc32cw),
|
||||
(3, true) => nameof(SoftFallback.Crc32cx),
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(size))
|
||||
};
|
||||
|
||||
return context.Call(typeof(SoftFallback).GetMethod(name), crc, value);
|
||||
}
|
||||
}
|
||||
|
||||
private static Operand EmitCrc32Optimized(ArmEmitterContext context, Operand crc, Operand data, bool castagnoli, int size)
|
||||
{
|
||||
long mu = castagnoli ? 0x0DEA713F1 : 0x1F7011641; // mu' = floor(x^64/P(x))'
|
||||
long polynomial = castagnoli ? 0x105EC76F0 : 0x1DB710641; // P'(x) << 1
|
||||
|
||||
crc = context.VectorInsert(context.VectorZero(), crc, 0);
|
||||
|
||||
switch (size)
|
||||
{
|
||||
case 0: data = context.VectorInsert8(context.VectorZero(), data, 0); break;
|
||||
case 1: data = context.VectorInsert16(context.VectorZero(), data, 0); break;
|
||||
case 2: data = context.VectorInsert(context.VectorZero(), data, 0); break;
|
||||
}
|
||||
|
||||
int bitsize = 8 << size;
|
||||
|
||||
Operand tmp = context.AddIntrinsic(Intrinsic.X86Pxor, crc, data);
|
||||
tmp = context.AddIntrinsic(Intrinsic.X86Psllq, tmp, Const(64 - bitsize));
|
||||
tmp = context.AddIntrinsic(Intrinsic.X86Pclmulqdq, tmp, X86GetScalar(context, mu), Const(0));
|
||||
tmp = context.AddIntrinsic(Intrinsic.X86Pclmulqdq, tmp, X86GetScalar(context, polynomial), Const(0));
|
||||
|
||||
if (bitsize < 32)
|
||||
{
|
||||
crc = context.AddIntrinsic(Intrinsic.X86Pslldq, crc, Const((64 - bitsize) / 8));
|
||||
tmp = context.AddIntrinsic(Intrinsic.X86Pxor, tmp, crc);
|
||||
}
|
||||
|
||||
return context.VectorExtract(OperandType.I32, tmp, 2);
|
||||
}
|
||||
|
||||
private static Operand EmitCrc32Optimized64(ArmEmitterContext context, Operand crc, Operand data, bool castagnoli)
|
||||
{
|
||||
long mu = castagnoli ? 0x0DEA713F1 : 0x1F7011641; // mu' = floor(x^64/P(x))'
|
||||
long polynomial = castagnoli ? 0x105EC76F0 : 0x1DB710641; // P'(x) << 1
|
||||
|
||||
crc = context.VectorInsert(context.VectorZero(), crc, 0);
|
||||
data = context.VectorInsert(context.VectorZero(), data, 0);
|
||||
|
||||
Operand tmp = context.AddIntrinsic(Intrinsic.X86Pxor, crc, data);
|
||||
Operand res = context.AddIntrinsic(Intrinsic.X86Pslldq, tmp, Const(4));
|
||||
|
||||
tmp = context.AddIntrinsic(Intrinsic.X86Pclmulqdq, res, X86GetScalar(context, mu), Const(0));
|
||||
tmp = context.AddIntrinsic(Intrinsic.X86Pclmulqdq, tmp, X86GetScalar(context, polynomial), Const(0));
|
||||
|
||||
tmp = context.AddIntrinsic(Intrinsic.X86Pxor, tmp, res);
|
||||
tmp = context.AddIntrinsic(Intrinsic.X86Psllq, tmp, Const(32));
|
||||
|
||||
tmp = context.AddIntrinsic(Intrinsic.X86Pclmulqdq, tmp, X86GetScalar(context, mu), Const(1));
|
||||
tmp = context.AddIntrinsic(Intrinsic.X86Pclmulqdq, tmp, X86GetScalar(context, polynomial), Const(0));
|
||||
|
||||
return context.VectorExtract(OperandType.I32, tmp, 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
264
src/ARMeilleure/Instructions/InstEmitHelper.cs
Normal file
264
src/ARMeilleure/Instructions/InstEmitHelper.cs
Normal file
@@ -0,0 +1,264 @@
|
||||
using ARMeilleure.Decoders;
|
||||
using ARMeilleure.IntermediateRepresentation;
|
||||
using ARMeilleure.State;
|
||||
using ARMeilleure.Translation;
|
||||
using System;
|
||||
|
||||
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
|
||||
|
||||
namespace ARMeilleure.Instructions
|
||||
{
|
||||
static class InstEmitHelper
|
||||
{
|
||||
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 Register(GetRegisterAlias(context.Mode, regIndex), RegisterType.Integer, OperandType.I32);
|
||||
}
|
||||
}
|
||||
|
||||
public static Operand GetIntA32AlignedPC(ArmEmitterContext context, int regIndex)
|
||||
{
|
||||
if (regIndex == RegisterAlias.Aarch32Pc)
|
||||
{
|
||||
OpCode32 op = (OpCode32)context.CurrOp;
|
||||
|
||||
return Const((int)(op.GetPc() & 0xfffffffc));
|
||||
}
|
||||
else
|
||||
{
|
||||
return Register(GetRegisterAlias(context.Mode, regIndex), RegisterType.Integer, OperandType.I32);
|
||||
}
|
||||
}
|
||||
|
||||
public static Operand GetVecA32(int regIndex)
|
||||
{
|
||||
return Register(regIndex, RegisterType.Vector, OperandType.V128);
|
||||
}
|
||||
|
||||
public static void SetIntA32(ArmEmitterContext context, int regIndex, Operand value)
|
||||
{
|
||||
if (regIndex == RegisterAlias.Aarch32Pc)
|
||||
{
|
||||
if (!IsA32Return(context))
|
||||
{
|
||||
context.StoreToContext();
|
||||
}
|
||||
|
||||
EmitBxWritePc(context, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (value.Type == OperandType.I64)
|
||||
{
|
||||
value = context.ConvertI64ToI32(value);
|
||||
}
|
||||
Operand reg = Register(GetRegisterAlias(context.Mode, regIndex), RegisterType.Integer, OperandType.I32);
|
||||
|
||||
context.Copy(reg, 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 bool IsA32Return(ArmEmitterContext context)
|
||||
{
|
||||
switch (context.CurrOp)
|
||||
{
|
||||
case IOpCode32MemMult op:
|
||||
return true; // Setting PC using LDM is nearly always a return.
|
||||
case OpCode32AluRsImm op:
|
||||
return op.Rm == RegisterAlias.Aarch32Lr;
|
||||
case OpCode32AluRsReg op:
|
||||
return op.Rm == RegisterAlias.Aarch32Lr;
|
||||
case OpCode32AluReg op:
|
||||
return op.Rm == RegisterAlias.Aarch32Lr;
|
||||
case OpCode32Mem op:
|
||||
return op.Rn == RegisterAlias.Aarch32Sp && op.WBack && !op.Index; // Setting PC to an address stored on the stack is nearly always a return.
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void EmitBxWritePc(ArmEmitterContext context, Operand pc, int sourceRegister = 0)
|
||||
{
|
||||
bool isReturn = sourceRegister == RegisterAlias.Aarch32Lr || IsA32Return(context);
|
||||
Operand mode = context.BitwiseAnd(pc, Const(1));
|
||||
|
||||
SetFlag(context, PState.TFlag, mode);
|
||||
|
||||
Operand addr = context.ConditionalSelect(mode, context.BitwiseAnd(pc, Const(~1)), context.BitwiseAnd(pc, Const(~3)));
|
||||
|
||||
InstEmitFlowHelper.EmitVirtualJump(context, addr, isReturn);
|
||||
}
|
||||
|
||||
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 Operand GetFpFlag(FPState stateFlag)
|
||||
{
|
||||
return Register((int)stateFlag, RegisterType.FpFlag, OperandType.I32);
|
||||
}
|
||||
|
||||
public static void SetFlag(ArmEmitterContext context, PState stateFlag, Operand value)
|
||||
{
|
||||
context.Copy(GetFlag(stateFlag), value);
|
||||
|
||||
context.MarkFlagSet(stateFlag);
|
||||
}
|
||||
|
||||
public static void SetFpFlag(ArmEmitterContext context, FPState stateFlag, Operand value)
|
||||
{
|
||||
context.Copy(GetFpFlag(stateFlag), value);
|
||||
}
|
||||
}
|
||||
}
|
||||
184
src/ARMeilleure/Instructions/InstEmitMemory.cs
Normal file
184
src/ARMeilleure/Instructions/InstEmitMemory.cs
Normal file
@@ -0,0 +1,184 @@
|
||||
using ARMeilleure.Decoders;
|
||||
using ARMeilleure.IntermediateRepresentation;
|
||||
using ARMeilleure.Translation;
|
||||
|
||||
using static ARMeilleure.Instructions.InstEmitHelper;
|
||||
using static ARMeilleure.Instructions.InstEmitMemoryHelper;
|
||||
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
|
||||
|
||||
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 = GetAddress(context, 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);
|
||||
|
||||
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 = GetAddress(context, 1L << op.Size);
|
||||
|
||||
EmitStore(context, address, op.Rt, op.Size);
|
||||
EmitStore(context, address2, op.Rt2, op.Size);
|
||||
|
||||
EmitWBackIfNeeded(context, address);
|
||||
}
|
||||
|
||||
private static Operand GetAddress(ArmEmitterContext context, long addend = 0)
|
||||
{
|
||||
Operand address = default;
|
||||
|
||||
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 + addend));
|
||||
}
|
||||
else if (addend != 0)
|
||||
{
|
||||
address = context.Add(address, Const(addend));
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
if (addend != 0)
|
||||
{
|
||||
address = context.Add(address, Const(addend));
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
265
src/ARMeilleure/Instructions/InstEmitMemory32.cs
Normal file
265
src/ARMeilleure/Instructions/InstEmitMemory32.cs
Normal file
@@ -0,0 +1,265 @@
|
||||
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.Operand.Factory;
|
||||
|
||||
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,
|
||||
Ordered = 4,
|
||||
Exclusive = 8,
|
||||
|
||||
LoadZx = Load,
|
||||
LoadSx = Load | Signed,
|
||||
}
|
||||
|
||||
public static void Ldm(ArmEmitterContext context)
|
||||
{
|
||||
IOpCode32MemMult op = (IOpCode32MemMult)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)
|
||||
{
|
||||
IOpCode32MemMult op = (IOpCode32MemMult)context.CurrOp;
|
||||
|
||||
Operand n = context.Copy(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)
|
||||
{
|
||||
IOpCode32Mem op = (IOpCode32Mem)context.CurrOp;
|
||||
|
||||
Operand n = context.Copy(GetIntA32AlignedPC(context, op.Rn));
|
||||
Operand m = GetMemM(context, setCarry: false);
|
||||
|
||||
Operand temp = default;
|
||||
|
||||
if (op.Index || op.WBack)
|
||||
{
|
||||
temp = op.Add
|
||||
? context.Add (n, m)
|
||||
: context.Subtract(n, m);
|
||||
}
|
||||
|
||||
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.Rt2, 4, WordSizeLog2);
|
||||
|
||||
context.Branch(lblEnd);
|
||||
|
||||
context.MarkLabel(lblBigEndian);
|
||||
|
||||
Load(op.Rt2, 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.Rt2, 4, WordSizeLog2);
|
||||
|
||||
context.Branch(lblEnd);
|
||||
|
||||
context.MarkLabel(lblBigEndian);
|
||||
|
||||
Store(op.Rt2, 0, WordSizeLog2);
|
||||
Store(op.Rt, 4, WordSizeLog2);
|
||||
|
||||
context.MarkLabel(lblEnd);
|
||||
}
|
||||
else
|
||||
{
|
||||
Store(op.Rt, 0, size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void Adr(ArmEmitterContext context)
|
||||
{
|
||||
IOpCode32Adr op = (IOpCode32Adr)context.CurrOp;
|
||||
SetIntA32(context, op.Rd, Const(op.Immediate));
|
||||
}
|
||||
}
|
||||
}
|
||||
178
src/ARMeilleure/Instructions/InstEmitMemoryEx.cs
Normal file
178
src/ARMeilleure/Instructions/InstEmitMemoryEx.cs
Normal file
@@ -0,0 +1,178 @@
|
||||
using ARMeilleure.Decoders;
|
||||
using ARMeilleure.IntermediateRepresentation;
|
||||
using ARMeilleure.Translation;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
|
||||
using static ARMeilleure.Instructions.InstEmitHelper;
|
||||
using static ARMeilleure.Instructions.InstEmitMemoryExHelper;
|
||||
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
|
||||
|
||||
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)
|
||||
{
|
||||
EmitClearExclusive(context);
|
||||
}
|
||||
|
||||
public static void Csdb(ArmEmitterContext context)
|
||||
{
|
||||
// Execute as no-op.
|
||||
}
|
||||
|
||||
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 = EmitLoadExclusive(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 = EmitLoadExclusive(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 = EmitLoadExclusive(context, address, exclusive, op.Size);
|
||||
|
||||
SetIntOrZR(context, op.Rt, value);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Prfm(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;
|
||||
|
||||
Operand address = context.Copy(GetIntOrSP(context, op.Rn));
|
||||
|
||||
Operand t = GetIntOrZR(context, op.Rt);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
EmitStoreExclusive(context, address, value, exclusive, op.Size + 1, op.Rs, a32: false);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitStoreExclusive(context, address, t, exclusive, op.Size, op.Rs, a32: false);
|
||||
}
|
||||
|
||||
if (ordered)
|
||||
{
|
||||
EmitBarrier(context);
|
||||
}
|
||||
}
|
||||
|
||||
private static void EmitBarrier(ArmEmitterContext context)
|
||||
{
|
||||
context.MemoryBarrier();
|
||||
}
|
||||
}
|
||||
}
|
||||
237
src/ARMeilleure/Instructions/InstEmitMemoryEx32.cs
Normal file
237
src/ARMeilleure/Instructions/InstEmitMemoryEx32.cs
Normal file
@@ -0,0 +1,237 @@
|
||||
using ARMeilleure.Decoders;
|
||||
using ARMeilleure.IntermediateRepresentation;
|
||||
using ARMeilleure.State;
|
||||
using ARMeilleure.Translation;
|
||||
|
||||
using static ARMeilleure.Instructions.InstEmitHelper;
|
||||
using static ARMeilleure.Instructions.InstEmitMemoryExHelper;
|
||||
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
|
||||
|
||||
namespace ARMeilleure.Instructions
|
||||
{
|
||||
static partial class InstEmit32
|
||||
{
|
||||
public static void Clrex(ArmEmitterContext context)
|
||||
{
|
||||
EmitClearExclusive(context);
|
||||
}
|
||||
|
||||
public static void Csdb(ArmEmitterContext context)
|
||||
{
|
||||
// Execute as no-op.
|
||||
}
|
||||
|
||||
public static void Dmb(ArmEmitterContext context) => EmitBarrier(context);
|
||||
|
||||
public static void Dsb(ArmEmitterContext context) => EmitBarrier(context);
|
||||
|
||||
public static void Ldrex(ArmEmitterContext context)
|
||||
{
|
||||
EmitExLoadOrStore(context, WordSizeLog2, AccessType.LoadZx | AccessType.Exclusive);
|
||||
}
|
||||
|
||||
public static void Ldrexb(ArmEmitterContext context)
|
||||
{
|
||||
EmitExLoadOrStore(context, ByteSizeLog2, AccessType.LoadZx | AccessType.Exclusive);
|
||||
}
|
||||
|
||||
public static void Ldrexd(ArmEmitterContext context)
|
||||
{
|
||||
EmitExLoadOrStore(context, DWordSizeLog2, AccessType.LoadZx | AccessType.Exclusive);
|
||||
}
|
||||
|
||||
public static void Ldrexh(ArmEmitterContext context)
|
||||
{
|
||||
EmitExLoadOrStore(context, HWordSizeLog2, AccessType.LoadZx | AccessType.Exclusive);
|
||||
}
|
||||
|
||||
public static void Lda(ArmEmitterContext context)
|
||||
{
|
||||
EmitExLoadOrStore(context, WordSizeLog2, AccessType.LoadZx | AccessType.Ordered);
|
||||
}
|
||||
|
||||
public static void Ldab(ArmEmitterContext context)
|
||||
{
|
||||
EmitExLoadOrStore(context, ByteSizeLog2, AccessType.LoadZx | AccessType.Ordered);
|
||||
}
|
||||
|
||||
public static void Ldaex(ArmEmitterContext context)
|
||||
{
|
||||
EmitExLoadOrStore(context, WordSizeLog2, AccessType.LoadZx | AccessType.Exclusive | AccessType.Ordered);
|
||||
}
|
||||
|
||||
public static void Ldaexb(ArmEmitterContext context)
|
||||
{
|
||||
EmitExLoadOrStore(context, ByteSizeLog2, AccessType.LoadZx | AccessType.Exclusive | AccessType.Ordered);
|
||||
}
|
||||
|
||||
public static void Ldaexd(ArmEmitterContext context)
|
||||
{
|
||||
EmitExLoadOrStore(context, DWordSizeLog2, AccessType.LoadZx | AccessType.Exclusive | AccessType.Ordered);
|
||||
}
|
||||
|
||||
public static void Ldaexh(ArmEmitterContext context)
|
||||
{
|
||||
EmitExLoadOrStore(context, HWordSizeLog2, AccessType.LoadZx | AccessType.Exclusive | AccessType.Ordered);
|
||||
}
|
||||
|
||||
public static void Ldah(ArmEmitterContext context)
|
||||
{
|
||||
EmitExLoadOrStore(context, HWordSizeLog2, AccessType.LoadZx | AccessType.Ordered);
|
||||
}
|
||||
|
||||
// Stores.
|
||||
|
||||
public static void Strex(ArmEmitterContext context)
|
||||
{
|
||||
EmitExLoadOrStore(context, WordSizeLog2, AccessType.Store | AccessType.Exclusive);
|
||||
}
|
||||
|
||||
public static void Strexb(ArmEmitterContext context)
|
||||
{
|
||||
EmitExLoadOrStore(context, ByteSizeLog2, AccessType.Store | AccessType.Exclusive);
|
||||
}
|
||||
|
||||
public static void Strexd(ArmEmitterContext context)
|
||||
{
|
||||
EmitExLoadOrStore(context, DWordSizeLog2, AccessType.Store | AccessType.Exclusive);
|
||||
}
|
||||
|
||||
public static void Strexh(ArmEmitterContext context)
|
||||
{
|
||||
EmitExLoadOrStore(context, HWordSizeLog2, AccessType.Store | AccessType.Exclusive);
|
||||
}
|
||||
|
||||
public static void Stl(ArmEmitterContext context)
|
||||
{
|
||||
EmitExLoadOrStore(context, WordSizeLog2, AccessType.Store | AccessType.Ordered);
|
||||
}
|
||||
|
||||
public static void Stlb(ArmEmitterContext context)
|
||||
{
|
||||
EmitExLoadOrStore(context, ByteSizeLog2, AccessType.Store | AccessType.Ordered);
|
||||
}
|
||||
|
||||
public static void Stlex(ArmEmitterContext context)
|
||||
{
|
||||
EmitExLoadOrStore(context, WordSizeLog2, AccessType.Store | AccessType.Exclusive | AccessType.Ordered);
|
||||
}
|
||||
|
||||
public static void Stlexb(ArmEmitterContext context)
|
||||
{
|
||||
EmitExLoadOrStore(context, ByteSizeLog2, AccessType.Store | AccessType.Exclusive | AccessType.Ordered);
|
||||
}
|
||||
|
||||
public static void Stlexd(ArmEmitterContext context)
|
||||
{
|
||||
EmitExLoadOrStore(context, DWordSizeLog2, AccessType.Store | AccessType.Exclusive | AccessType.Ordered);
|
||||
}
|
||||
|
||||
public static void Stlexh(ArmEmitterContext context)
|
||||
{
|
||||
EmitExLoadOrStore(context, HWordSizeLog2, AccessType.Store | AccessType.Exclusive | AccessType.Ordered);
|
||||
}
|
||||
|
||||
public static void Stlh(ArmEmitterContext context)
|
||||
{
|
||||
EmitExLoadOrStore(context, HWordSizeLog2, AccessType.Store | AccessType.Ordered);
|
||||
}
|
||||
|
||||
private static void EmitExLoadOrStore(ArmEmitterContext context, int size, AccessType accType)
|
||||
{
|
||||
IOpCode32MemEx op = (IOpCode32MemEx)context.CurrOp;
|
||||
|
||||
Operand address = context.Copy(GetIntA32(context, op.Rn));
|
||||
|
||||
var exclusive = (accType & AccessType.Exclusive) != 0;
|
||||
var ordered = (accType & AccessType.Ordered) != 0;
|
||||
|
||||
if ((accType & AccessType.Load) != 0)
|
||||
{
|
||||
if (ordered)
|
||||
{
|
||||
EmitBarrier(context);
|
||||
}
|
||||
|
||||
if (size == DWordSizeLog2)
|
||||
{
|
||||
// Keep loads atomic - make the call to get the whole region and then decompose it into parts
|
||||
// for the registers.
|
||||
|
||||
Operand value = EmitLoadExclusive(context, address, exclusive, size);
|
||||
|
||||
Operand valueLow = context.ConvertI64ToI32(value);
|
||||
|
||||
valueLow = context.ZeroExtend32(OperandType.I64, valueLow);
|
||||
|
||||
Operand valueHigh = context.ShiftRightUI(value, Const(32));
|
||||
|
||||
Operand lblBigEndian = Label();
|
||||
Operand lblEnd = Label();
|
||||
|
||||
context.BranchIfTrue(lblBigEndian, GetFlag(PState.EFlag));
|
||||
|
||||
SetIntA32(context, op.Rt, valueLow);
|
||||
SetIntA32(context, op.Rt2, valueHigh);
|
||||
|
||||
context.Branch(lblEnd);
|
||||
|
||||
context.MarkLabel(lblBigEndian);
|
||||
|
||||
SetIntA32(context, op.Rt2, valueLow);
|
||||
SetIntA32(context, op.Rt, valueHigh);
|
||||
|
||||
context.MarkLabel(lblEnd);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetIntA32(context, op.Rt, EmitLoadExclusive(context, address, exclusive, size));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (size == DWordSizeLog2)
|
||||
{
|
||||
// Split the result into 2 words (based on endianness)
|
||||
|
||||
Operand lo = context.ZeroExtend32(OperandType.I64, GetIntA32(context, op.Rt));
|
||||
Operand hi = context.ZeroExtend32(OperandType.I64, GetIntA32(context, op.Rt2));
|
||||
|
||||
Operand lblBigEndian = Label();
|
||||
Operand lblEnd = Label();
|
||||
|
||||
context.BranchIfTrue(lblBigEndian, GetFlag(PState.EFlag));
|
||||
|
||||
Operand leResult = context.BitwiseOr(lo, context.ShiftLeft(hi, Const(32)));
|
||||
EmitStoreExclusive(context, address, leResult, exclusive, size, op.Rd, a32: true);
|
||||
|
||||
context.Branch(lblEnd);
|
||||
|
||||
context.MarkLabel(lblBigEndian);
|
||||
|
||||
Operand beResult = context.BitwiseOr(hi, context.ShiftLeft(lo, Const(32)));
|
||||
EmitStoreExclusive(context, address, beResult, exclusive, size, op.Rd, a32: true);
|
||||
|
||||
context.MarkLabel(lblEnd);
|
||||
}
|
||||
else
|
||||
{
|
||||
Operand value = context.ZeroExtend32(OperandType.I64, GetIntA32(context, op.Rt));
|
||||
EmitStoreExclusive(context, address, value, exclusive, size, op.Rd, a32: true);
|
||||
}
|
||||
|
||||
if (ordered)
|
||||
{
|
||||
EmitBarrier(context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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.
|
||||
}
|
||||
}
|
||||
}
|
||||
174
src/ARMeilleure/Instructions/InstEmitMemoryExHelper.cs
Normal file
174
src/ARMeilleure/Instructions/InstEmitMemoryExHelper.cs
Normal file
@@ -0,0 +1,174 @@
|
||||
using ARMeilleure.IntermediateRepresentation;
|
||||
using ARMeilleure.State;
|
||||
using ARMeilleure.Translation;
|
||||
|
||||
using static ARMeilleure.Instructions.InstEmitHelper;
|
||||
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
|
||||
|
||||
namespace ARMeilleure.Instructions
|
||||
{
|
||||
static class InstEmitMemoryExHelper
|
||||
{
|
||||
private const int ErgSizeLog2 = 4;
|
||||
|
||||
public static Operand EmitLoadExclusive(ArmEmitterContext context, Operand address, bool exclusive, int size)
|
||||
{
|
||||
if (exclusive)
|
||||
{
|
||||
Operand value;
|
||||
|
||||
if (size == 4)
|
||||
{
|
||||
// Only 128-bit CAS is guaranteed to have a atomic load.
|
||||
Operand physAddr = InstEmitMemoryHelper.EmitPtPointerLoad(context, address, default, write: false, 4);
|
||||
|
||||
Operand zero = context.VectorZero();
|
||||
|
||||
value = context.CompareAndSwap(physAddr, zero, zero);
|
||||
}
|
||||
else
|
||||
{
|
||||
value = InstEmitMemoryHelper.EmitReadIntAligned(context, address, size);
|
||||
}
|
||||
|
||||
Operand arg0 = context.LoadArgument(OperandType.I64, 0);
|
||||
|
||||
Operand exAddrPtr = context.Add(arg0, Const((long)NativeContext.GetExclusiveAddressOffset()));
|
||||
Operand exValuePtr = context.Add(arg0, Const((long)NativeContext.GetExclusiveValueOffset()));
|
||||
|
||||
context.Store(exAddrPtr, context.BitwiseAnd(address, Const(address.Type, GetExclusiveAddressMask())));
|
||||
|
||||
// Make sure the unused higher bits of the value are cleared.
|
||||
if (size < 3)
|
||||
{
|
||||
context.Store(exValuePtr, Const(0UL));
|
||||
}
|
||||
if (size < 4)
|
||||
{
|
||||
context.Store(context.Add(exValuePtr, Const(exValuePtr.Type, 8L)), Const(0UL));
|
||||
}
|
||||
|
||||
// Store the new exclusive value.
|
||||
context.Store(exValuePtr, value);
|
||||
|
||||
return value;
|
||||
}
|
||||
else
|
||||
{
|
||||
return InstEmitMemoryHelper.EmitReadIntAligned(context, address, size);
|
||||
}
|
||||
}
|
||||
|
||||
public static void EmitStoreExclusive(
|
||||
ArmEmitterContext context,
|
||||
Operand address,
|
||||
Operand value,
|
||||
bool exclusive,
|
||||
int size,
|
||||
int rs,
|
||||
bool a32)
|
||||
{
|
||||
if (size < 3)
|
||||
{
|
||||
value = context.ConvertI64ToI32(value);
|
||||
}
|
||||
|
||||
if (exclusive)
|
||||
{
|
||||
// We overwrite one of the register (Rs),
|
||||
// keep a copy of the values to ensure we are working with the correct values.
|
||||
address = context.Copy(address);
|
||||
value = context.Copy(value);
|
||||
|
||||
void SetRs(Operand value)
|
||||
{
|
||||
if (a32)
|
||||
{
|
||||
SetIntA32(context, rs, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetIntOrZR(context, rs, value);
|
||||
}
|
||||
}
|
||||
|
||||
Operand arg0 = context.LoadArgument(OperandType.I64, 0);
|
||||
|
||||
Operand exAddrPtr = context.Add(arg0, Const((long)NativeContext.GetExclusiveAddressOffset()));
|
||||
Operand exAddr = context.Load(address.Type, exAddrPtr);
|
||||
|
||||
// STEP 1: Check if we have exclusive access to this memory region. If not, fail and skip store.
|
||||
Operand maskedAddress = context.BitwiseAnd(address, Const(address.Type, GetExclusiveAddressMask()));
|
||||
|
||||
Operand exFailed = context.ICompareNotEqual(exAddr, maskedAddress);
|
||||
|
||||
Operand lblExit = Label();
|
||||
|
||||
SetRs(Const(1));
|
||||
|
||||
context.BranchIfTrue(lblExit, exFailed);
|
||||
|
||||
// STEP 2: We have exclusive access and the address is valid, attempt the store using CAS.
|
||||
Operand physAddr = InstEmitMemoryHelper.EmitPtPointerLoad(context, address, default, write: true, size);
|
||||
|
||||
Operand exValuePtr = context.Add(arg0, Const((long)NativeContext.GetExclusiveValueOffset()));
|
||||
Operand exValue = size switch
|
||||
{
|
||||
0 => context.Load8(exValuePtr),
|
||||
1 => context.Load16(exValuePtr),
|
||||
2 => context.Load(OperandType.I32, exValuePtr),
|
||||
3 => context.Load(OperandType.I64, exValuePtr),
|
||||
_ => context.Load(OperandType.V128, exValuePtr)
|
||||
};
|
||||
|
||||
Operand currValue = size switch
|
||||
{
|
||||
0 => context.CompareAndSwap8(physAddr, exValue, value),
|
||||
1 => context.CompareAndSwap16(physAddr, exValue, value),
|
||||
_ => context.CompareAndSwap(physAddr, exValue, value)
|
||||
};
|
||||
|
||||
// STEP 3: Check if we succeeded by comparing expected and in-memory values.
|
||||
Operand storeFailed;
|
||||
|
||||
if (size == 4)
|
||||
{
|
||||
Operand currValueLow = context.VectorExtract(OperandType.I64, currValue, 0);
|
||||
Operand currValueHigh = context.VectorExtract(OperandType.I64, currValue, 1);
|
||||
|
||||
Operand exValueLow = context.VectorExtract(OperandType.I64, exValue, 0);
|
||||
Operand exValueHigh = context.VectorExtract(OperandType.I64, exValue, 1);
|
||||
|
||||
storeFailed = context.BitwiseOr(
|
||||
context.ICompareNotEqual(currValueLow, exValueLow),
|
||||
context.ICompareNotEqual(currValueHigh, exValueHigh));
|
||||
}
|
||||
else
|
||||
{
|
||||
storeFailed = context.ICompareNotEqual(currValue, exValue);
|
||||
}
|
||||
|
||||
SetRs(storeFailed);
|
||||
|
||||
context.MarkLabel(lblExit);
|
||||
}
|
||||
else
|
||||
{
|
||||
InstEmitMemoryHelper.EmitWriteIntAligned(context, address, value, size);
|
||||
}
|
||||
}
|
||||
|
||||
public static void EmitClearExclusive(ArmEmitterContext context)
|
||||
{
|
||||
Operand arg0 = context.LoadArgument(OperandType.I64, 0);
|
||||
|
||||
Operand exAddrPtr = context.Add(arg0, Const((long)NativeContext.GetExclusiveAddressOffset()));
|
||||
|
||||
// We store ULONG max to force any exclusive address checks to fail,
|
||||
// since this value is not aligned to the ERG mask.
|
||||
context.Store(exAddrPtr, Const(ulong.MaxValue));
|
||||
}
|
||||
|
||||
private static long GetExclusiveAddressMask() => ~((4L << ErgSizeLog2) - 1);
|
||||
}
|
||||
}
|
||||
648
src/ARMeilleure/Instructions/InstEmitMemoryHelper.cs
Normal file
648
src/ARMeilleure/Instructions/InstEmitMemoryHelper.cs
Normal file
@@ -0,0 +1,648 @@
|
||||
using ARMeilleure.Decoders;
|
||||
using ARMeilleure.IntermediateRepresentation;
|
||||
using ARMeilleure.Memory;
|
||||
using ARMeilleure.Translation;
|
||||
using ARMeilleure.Translation.PTC;
|
||||
using System;
|
||||
using System.Reflection;
|
||||
|
||||
using static ARMeilleure.Instructions.InstEmitHelper;
|
||||
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
|
||||
|
||||
namespace ARMeilleure.Instructions
|
||||
{
|
||||
static class InstEmitMemoryHelper
|
||||
{
|
||||
private const int PageBits = 12;
|
||||
private const int PageMask = (1 << PageBits) - 1;
|
||||
|
||||
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 && !(context.CurrOp is OpCode32 && rt == State.RegisterAlias.Aarch32Pc))
|
||||
{
|
||||
Operand value = GetInt(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;
|
||||
}
|
||||
}
|
||||
|
||||
SetInt(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);
|
||||
}
|
||||
|
||||
public static Operand EmitReadInt(ArmEmitterContext context, Operand address, int size)
|
||||
{
|
||||
Operand temp = context.AllocateLocal(size == 3 ? OperandType.I64 : OperandType.I32);
|
||||
|
||||
Operand lblSlowPath = Label();
|
||||
Operand lblEnd = Label();
|
||||
|
||||
Operand physAddr = EmitPtPointerLoad(context, address, lblSlowPath, write: false, size);
|
||||
|
||||
Operand value = default;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
context.Copy(temp, value);
|
||||
|
||||
if (!context.Memory.Type.IsHostMapped())
|
||||
{
|
||||
context.Branch(lblEnd);
|
||||
|
||||
context.MarkLabel(lblSlowPath, BasicBlockFrequency.Cold);
|
||||
|
||||
context.Copy(temp, EmitReadIntFallback(context, address, size));
|
||||
|
||||
context.MarkLabel(lblEnd);
|
||||
}
|
||||
|
||||
return temp;
|
||||
}
|
||||
|
||||
private static void EmitReadInt(ArmEmitterContext context, Operand address, int rt, int size)
|
||||
{
|
||||
Operand lblSlowPath = Label();
|
||||
Operand lblEnd = Label();
|
||||
|
||||
Operand physAddr = EmitPtPointerLoad(context, address, lblSlowPath, write: false, size);
|
||||
|
||||
Operand value = default;
|
||||
|
||||
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);
|
||||
|
||||
if (!context.Memory.Type.IsHostMapped())
|
||||
{
|
||||
context.Branch(lblEnd);
|
||||
|
||||
context.MarkLabel(lblSlowPath, BasicBlockFrequency.Cold);
|
||||
|
||||
EmitReadIntFallback(context, address, rt, size);
|
||||
|
||||
context.MarkLabel(lblEnd);
|
||||
}
|
||||
}
|
||||
|
||||
public static Operand EmitReadIntAligned(ArmEmitterContext context, Operand address, int size)
|
||||
{
|
||||
if ((uint)size > 4)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(size));
|
||||
}
|
||||
|
||||
Operand physAddr = EmitPtPointerLoad(context, address, default, write: false, size);
|
||||
|
||||
return size switch
|
||||
{
|
||||
0 => context.Load8(physAddr),
|
||||
1 => context.Load16(physAddr),
|
||||
2 => context.Load(OperandType.I32, physAddr),
|
||||
3 => context.Load(OperandType.I64, physAddr),
|
||||
_ => context.Load(OperandType.V128, physAddr)
|
||||
};
|
||||
}
|
||||
|
||||
private static void EmitReadVector(
|
||||
ArmEmitterContext context,
|
||||
Operand address,
|
||||
Operand vector,
|
||||
int rt,
|
||||
int elem,
|
||||
int size)
|
||||
{
|
||||
Operand lblSlowPath = Label();
|
||||
Operand lblEnd = Label();
|
||||
|
||||
Operand physAddr = EmitPtPointerLoad(context, address, lblSlowPath, write: false, size);
|
||||
|
||||
Operand value = default;
|
||||
|
||||
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);
|
||||
|
||||
if (!context.Memory.Type.IsHostMapped())
|
||||
{
|
||||
context.Branch(lblEnd);
|
||||
|
||||
context.MarkLabel(lblSlowPath, BasicBlockFrequency.Cold);
|
||||
|
||||
EmitReadVectorFallback(context, address, vector, rt, elem, size);
|
||||
|
||||
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 lblSlowPath = Label();
|
||||
Operand lblEnd = Label();
|
||||
|
||||
Operand physAddr = EmitPtPointerLoad(context, address, lblSlowPath, write: true, size);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
if (!context.Memory.Type.IsHostMapped())
|
||||
{
|
||||
context.Branch(lblEnd);
|
||||
|
||||
context.MarkLabel(lblSlowPath, BasicBlockFrequency.Cold);
|
||||
|
||||
EmitWriteIntFallback(context, address, rt, size);
|
||||
|
||||
context.MarkLabel(lblEnd);
|
||||
}
|
||||
}
|
||||
|
||||
public static void EmitWriteIntAligned(ArmEmitterContext context, Operand address, Operand value, int size)
|
||||
{
|
||||
if ((uint)size > 4)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(size));
|
||||
}
|
||||
|
||||
Operand physAddr = EmitPtPointerLoad(context, address, default, write: true, size);
|
||||
|
||||
if (size < 3 && value.Type == OperandType.I64)
|
||||
{
|
||||
value = context.ConvertI64ToI32(value);
|
||||
}
|
||||
|
||||
if (size == 0)
|
||||
{
|
||||
context.Store8(physAddr, value);
|
||||
}
|
||||
else if (size == 1)
|
||||
{
|
||||
context.Store16(physAddr, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
context.Store(physAddr, value);
|
||||
}
|
||||
}
|
||||
|
||||
private static void EmitWriteVector(
|
||||
ArmEmitterContext context,
|
||||
Operand address,
|
||||
int rt,
|
||||
int elem,
|
||||
int size)
|
||||
{
|
||||
Operand lblSlowPath = Label();
|
||||
Operand lblEnd = Label();
|
||||
|
||||
Operand physAddr = EmitPtPointerLoad(context, address, lblSlowPath, write: true, size);
|
||||
|
||||
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.I32, value, elem)); break;
|
||||
case 3: context.Store (physAddr, context.VectorExtract(OperandType.I64, value, elem)); break;
|
||||
case 4: context.Store (physAddr, value); break;
|
||||
}
|
||||
|
||||
if (!context.Memory.Type.IsHostMapped())
|
||||
{
|
||||
context.Branch(lblEnd);
|
||||
|
||||
context.MarkLabel(lblSlowPath, BasicBlockFrequency.Cold);
|
||||
|
||||
EmitWriteVectorFallback(context, address, rt, elem, size);
|
||||
|
||||
context.MarkLabel(lblEnd);
|
||||
}
|
||||
}
|
||||
|
||||
public static Operand EmitPtPointerLoad(ArmEmitterContext context, Operand address, Operand lblSlowPath, bool write, int size)
|
||||
{
|
||||
if (context.Memory.Type.IsHostMapped())
|
||||
{
|
||||
return EmitHostMappedPointer(context, address);
|
||||
}
|
||||
|
||||
int ptLevelBits = context.Memory.AddressSpaceBits - PageBits;
|
||||
int ptLevelSize = 1 << ptLevelBits;
|
||||
int ptLevelMask = ptLevelSize - 1;
|
||||
|
||||
Operand addrRotated = size != 0 ? context.RotateRight(address, Const(size)) : address;
|
||||
Operand addrShifted = context.ShiftRightUI(addrRotated, Const(PageBits - size));
|
||||
|
||||
Operand pte = !context.HasPtc
|
||||
? Const(context.Memory.PageTablePointer.ToInt64())
|
||||
: Const(context.Memory.PageTablePointer.ToInt64(), Ptc.PageTableSymbol);
|
||||
|
||||
Operand pteOffset = context.BitwiseAnd(addrShifted, Const(addrShifted.Type, ptLevelMask));
|
||||
|
||||
if (pteOffset.Type == OperandType.I32)
|
||||
{
|
||||
pteOffset = context.ZeroExtend32(OperandType.I64, pteOffset);
|
||||
}
|
||||
|
||||
pte = context.Load(OperandType.I64, context.Add(pte, context.ShiftLeft(pteOffset, Const(3))));
|
||||
|
||||
if (addrShifted.Type == OperandType.I32)
|
||||
{
|
||||
addrShifted = context.ZeroExtend32(OperandType.I64, addrShifted);
|
||||
}
|
||||
|
||||
// If the VA is out of range, or not aligned to the access size, force PTE to 0 by masking it.
|
||||
pte = context.BitwiseAnd(pte, context.ShiftRightSI(context.Add(addrShifted, Const(-(long)ptLevelSize)), Const(63)));
|
||||
|
||||
if (lblSlowPath != default)
|
||||
{
|
||||
if (write)
|
||||
{
|
||||
context.BranchIf(lblSlowPath, pte, Const(0L), Comparison.LessOrEqual);
|
||||
pte = context.BitwiseAnd(pte, Const(0xffffffffffffUL)); // Ignore any software protection bits. (they are still used by C# memory access)
|
||||
}
|
||||
else
|
||||
{
|
||||
pte = context.ShiftLeft(pte, Const(1));
|
||||
context.BranchIf(lblSlowPath, pte, Const(0L), Comparison.LessOrEqual);
|
||||
pte = context.ShiftRightUI(pte, Const(1));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// When no label is provided to jump to a slow path if the address is invalid,
|
||||
// we do the validation ourselves, and throw if needed.
|
||||
|
||||
Operand lblNotWatched = Label();
|
||||
|
||||
// Is the page currently being tracked for read/write? If so we need to call SignalMemoryTracking.
|
||||
context.BranchIf(lblNotWatched, pte, Const(0L), Comparison.GreaterOrEqual, BasicBlockFrequency.Cold);
|
||||
|
||||
// Signal memory tracking. Size here doesn't matter as address is assumed to be size aligned here.
|
||||
context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SignalMemoryTracking)), address, Const(1UL), Const(write ? 1 : 0));
|
||||
context.MarkLabel(lblNotWatched);
|
||||
|
||||
pte = context.BitwiseAnd(pte, Const(0xffffffffffffUL)); // Ignore any software protection bits. (they are still used by C# memory access)
|
||||
|
||||
Operand lblNonNull = Label();
|
||||
|
||||
// Skip exception if the PTE address is non-null (not zero).
|
||||
context.BranchIfTrue(lblNonNull, pte, BasicBlockFrequency.Cold);
|
||||
|
||||
// The call is not expected to return (it should throw).
|
||||
context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ThrowInvalidMemoryAccess)), address);
|
||||
context.MarkLabel(lblNonNull);
|
||||
}
|
||||
|
||||
Operand pageOffset = context.BitwiseAnd(address, Const(address.Type, PageMask));
|
||||
|
||||
if (pageOffset.Type == OperandType.I32)
|
||||
{
|
||||
pageOffset = context.ZeroExtend32(OperandType.I64, pageOffset);
|
||||
}
|
||||
|
||||
return context.Add(pte, pageOffset);
|
||||
}
|
||||
|
||||
public static Operand EmitHostMappedPointer(ArmEmitterContext context, Operand address)
|
||||
{
|
||||
if (address.Type == OperandType.I32)
|
||||
{
|
||||
address = context.ZeroExtend32(OperandType.I64, address);
|
||||
}
|
||||
|
||||
if (context.Memory.Type == MemoryManagerType.HostMapped)
|
||||
{
|
||||
Operand mask = Const(ulong.MaxValue >> (64 - context.Memory.AddressSpaceBits));
|
||||
address = context.BitwiseAnd(address, mask);
|
||||
}
|
||||
|
||||
Operand baseAddr = !context.HasPtc
|
||||
? Const(context.Memory.PageTablePointer.ToInt64())
|
||||
: Const(context.Memory.PageTablePointer.ToInt64(), Ptc.PageTableSymbol);
|
||||
|
||||
return context.Add(baseAddr, address);
|
||||
}
|
||||
|
||||
private static void EmitReadIntFallback(ArmEmitterContext context, Operand address, int rt, int size)
|
||||
{
|
||||
SetInt(context, rt, EmitReadIntFallback(context, address, size));
|
||||
}
|
||||
|
||||
private static Operand EmitReadIntFallback(ArmEmitterContext context, Operand address, int size)
|
||||
{
|
||||
MethodInfo info = null;
|
||||
|
||||
switch (size)
|
||||
{
|
||||
case 0: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadByte)); break;
|
||||
case 1: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt16)); break;
|
||||
case 2: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt32)); break;
|
||||
case 3: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt64)); break;
|
||||
}
|
||||
|
||||
return context.Call(info, address);
|
||||
}
|
||||
|
||||
private static void EmitReadVectorFallback(
|
||||
ArmEmitterContext context,
|
||||
Operand address,
|
||||
Operand vector,
|
||||
int rt,
|
||||
int elem,
|
||||
int size)
|
||||
{
|
||||
MethodInfo info = null;
|
||||
|
||||
switch (size)
|
||||
{
|
||||
case 0: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadByte)); break;
|
||||
case 1: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt16)); break;
|
||||
case 2: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt32)); break;
|
||||
case 3: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt64)); break;
|
||||
case 4: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadVector128)); break;
|
||||
}
|
||||
|
||||
Operand value = context.Call(info, 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)
|
||||
{
|
||||
MethodInfo info = null;
|
||||
|
||||
switch (size)
|
||||
{
|
||||
case 0: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteByte)); break;
|
||||
case 1: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteUInt16)); break;
|
||||
case 2: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteUInt32)); break;
|
||||
case 3: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteUInt64)); break;
|
||||
}
|
||||
|
||||
Operand value = GetInt(context, rt);
|
||||
|
||||
if (size < 3 && value.Type == OperandType.I64)
|
||||
{
|
||||
value = context.ConvertI64ToI32(value);
|
||||
}
|
||||
|
||||
context.Call(info, address, value);
|
||||
}
|
||||
|
||||
private static void EmitWriteVectorFallback(
|
||||
ArmEmitterContext context,
|
||||
Operand address,
|
||||
int rt,
|
||||
int elem,
|
||||
int size)
|
||||
{
|
||||
MethodInfo info = null;
|
||||
|
||||
switch (size)
|
||||
{
|
||||
case 0: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteByte)); break;
|
||||
case 1: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteUInt16)); break;
|
||||
case 2: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteUInt32)); break;
|
||||
case 3: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteUInt64)); break;
|
||||
case 4: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteVector128)); break;
|
||||
}
|
||||
|
||||
Operand value = default;
|
||||
|
||||
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(info, 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);
|
||||
}
|
||||
}
|
||||
|
||||
// ARM32 helpers.
|
||||
public static Operand GetMemM(ArmEmitterContext context, bool setCarry = true)
|
||||
{
|
||||
switch (context.CurrOp)
|
||||
{
|
||||
case IOpCode32MemRsImm op: return GetMShiftedByImmediate(context, op, setCarry);
|
||||
|
||||
case IOpCode32MemReg op: return GetIntA32(context, op.Rm);
|
||||
|
||||
case IOpCode32Mem op: return Const(op.Immediate);
|
||||
|
||||
case OpCode32SimdMemImm op: return Const(op.Immediate);
|
||||
|
||||
default: throw InvalidOpCodeType(context.CurrOp);
|
||||
}
|
||||
}
|
||||
|
||||
private static Exception InvalidOpCodeType(OpCode opCode)
|
||||
{
|
||||
return new InvalidOperationException($"Invalid OpCode type \"{opCode?.GetType().Name ?? "null"}\".");
|
||||
}
|
||||
|
||||
public static Operand GetMShiftedByImmediate(ArmEmitterContext context, IOpCode32MemRsImm op, bool setCarry)
|
||||
{
|
||||
Operand m = GetIntA32(context, op.Rm);
|
||||
|
||||
int shift = op.Immediate;
|
||||
|
||||
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 &= false;
|
||||
|
||||
switch (op.ShiftType)
|
||||
{
|
||||
case ShiftType.Lsl: m = InstEmitAluHelper.GetLslC(context, m, setCarry, shift); break;
|
||||
case ShiftType.Lsr: m = InstEmitAluHelper.GetLsrC(context, m, setCarry, shift); break;
|
||||
case ShiftType.Asr: m = InstEmitAluHelper.GetAsrC(context, m, setCarry, shift); break;
|
||||
case ShiftType.Ror:
|
||||
if (op.Immediate != 0)
|
||||
{
|
||||
m = InstEmitAluHelper.GetRorC(context, m, setCarry, shift);
|
||||
}
|
||||
else
|
||||
{
|
||||
m = InstEmitAluHelper.GetRrxC(context, m, setCarry);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return m;
|
||||
}
|
||||
}
|
||||
}
|
||||
41
src/ARMeilleure/Instructions/InstEmitMove.cs
Normal file
41
src/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.Operand.Factory;
|
||||
|
||||
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
src/ARMeilleure/Instructions/InstEmitMul.cs
Normal file
100
src/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);
|
||||
}
|
||||
}
|
||||
}
|
||||
379
src/ARMeilleure/Instructions/InstEmitMul32.cs
Normal file
379
src/ARMeilleure/Instructions/InstEmitMul32.cs
Normal file
@@ -0,0 +1,379 @@
|
||||
using ARMeilleure.Decoders;
|
||||
using ARMeilleure.IntermediateRepresentation;
|
||||
using ARMeilleure.State;
|
||||
using ARMeilleure.Translation;
|
||||
using System;
|
||||
|
||||
using static ARMeilleure.Instructions.InstEmitAluHelper;
|
||||
using static ARMeilleure.Instructions.InstEmitHelper;
|
||||
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
|
||||
|
||||
namespace ARMeilleure.Instructions
|
||||
{
|
||||
static partial class InstEmit32
|
||||
{
|
||||
[Flags]
|
||||
private enum MullFlags
|
||||
{
|
||||
Subtract = 1,
|
||||
Add = 1 << 1,
|
||||
Signed = 1 << 2,
|
||||
|
||||
SignedAdd = Signed | Add,
|
||||
SignedSubtract = Signed | Subtract
|
||||
}
|
||||
|
||||
public static void Mla(ArmEmitterContext context)
|
||||
{
|
||||
IOpCode32AluMla op = (IOpCode32AluMla)context.CurrOp;
|
||||
|
||||
Operand n = GetAluN(context);
|
||||
Operand m = GetAluM(context);
|
||||
Operand a = GetIntA32(context, op.Ra);
|
||||
|
||||
Operand res = context.Add(a, context.Multiply(n, m));
|
||||
|
||||
if (ShouldSetFlags(context))
|
||||
{
|
||||
EmitNZFlagsCheck(context, res);
|
||||
}
|
||||
|
||||
EmitAluStore(context, res);
|
||||
}
|
||||
|
||||
public static void Mls(ArmEmitterContext context)
|
||||
{
|
||||
IOpCode32AluMla op = (IOpCode32AluMla)context.CurrOp;
|
||||
|
||||
Operand n = GetAluN(context);
|
||||
Operand m = GetAluM(context);
|
||||
Operand a = GetIntA32(context, op.Ra);
|
||||
|
||||
Operand res = context.Subtract(a, context.Multiply(n, m));
|
||||
|
||||
EmitAluStore(context, res);
|
||||
}
|
||||
|
||||
public static void Smmla(ArmEmitterContext context)
|
||||
{
|
||||
EmitSmmul(context, MullFlags.SignedAdd);
|
||||
}
|
||||
|
||||
public static void Smmls(ArmEmitterContext context)
|
||||
{
|
||||
EmitSmmul(context, MullFlags.SignedSubtract);
|
||||
}
|
||||
|
||||
public static void Smmul(ArmEmitterContext context)
|
||||
{
|
||||
EmitSmmul(context, MullFlags.Signed);
|
||||
}
|
||||
|
||||
private static void EmitSmmul(ArmEmitterContext context, MullFlags flags)
|
||||
{
|
||||
IOpCode32AluMla op = (IOpCode32AluMla)context.CurrOp;
|
||||
|
||||
Operand n = context.SignExtend32(OperandType.I64, GetIntA32(context, op.Rn));
|
||||
Operand m = context.SignExtend32(OperandType.I64, GetIntA32(context, op.Rm));
|
||||
|
||||
Operand res = context.Multiply(n, m);
|
||||
|
||||
if (flags.HasFlag(MullFlags.Add) && op.Ra != 0xf)
|
||||
{
|
||||
res = context.Add(context.ShiftLeft(context.ZeroExtend32(OperandType.I64, GetIntA32(context, op.Ra)), Const(32)), res);
|
||||
}
|
||||
else if (flags.HasFlag(MullFlags.Subtract))
|
||||
{
|
||||
res = context.Subtract(context.ShiftLeft(context.ZeroExtend32(OperandType.I64, GetIntA32(context, op.Ra)), Const(32)), res);
|
||||
}
|
||||
|
||||
if (op.R)
|
||||
{
|
||||
res = context.Add(res, Const(0x80000000L));
|
||||
}
|
||||
|
||||
Operand hi = context.ConvertI64ToI32(context.ShiftRightSI(res, Const(32)));
|
||||
|
||||
EmitGenericAluStoreA32(context, op.Rd, false, hi);
|
||||
}
|
||||
|
||||
public static void Smla__(ArmEmitterContext context)
|
||||
{
|
||||
IOpCode32AluMla op = (IOpCode32AluMla)context.CurrOp;
|
||||
|
||||
Operand n = GetIntA32(context, op.Rn);
|
||||
Operand m = GetIntA32(context, op.Rm);
|
||||
Operand a = GetIntA32(context, op.Ra);
|
||||
|
||||
if (op.NHigh)
|
||||
{
|
||||
n = context.SignExtend16(OperandType.I64, context.ShiftRightUI(n, Const(16)));
|
||||
}
|
||||
else
|
||||
{
|
||||
n = context.SignExtend16(OperandType.I64, n);
|
||||
}
|
||||
|
||||
if (op.MHigh)
|
||||
{
|
||||
m = context.SignExtend16(OperandType.I64, context.ShiftRightUI(m, Const(16)));
|
||||
}
|
||||
else
|
||||
{
|
||||
m = context.SignExtend16(OperandType.I64, m);
|
||||
}
|
||||
|
||||
Operand res = context.Multiply(n, m);
|
||||
|
||||
Operand toAdd = context.SignExtend32(OperandType.I64, a);
|
||||
res = context.Add(res, toAdd);
|
||||
Operand q = context.ICompareNotEqual(res, context.SignExtend32(OperandType.I64, res));
|
||||
res = context.ConvertI64ToI32(res);
|
||||
|
||||
UpdateQFlag(context, q);
|
||||
|
||||
EmitGenericAluStoreA32(context, op.Rd, false, res);
|
||||
}
|
||||
|
||||
public static void Smlal(ArmEmitterContext context)
|
||||
{
|
||||
EmitMlal(context, true);
|
||||
}
|
||||
|
||||
public static void Smlal__(ArmEmitterContext context)
|
||||
{
|
||||
IOpCode32AluUmull op = (IOpCode32AluUmull)context.CurrOp;
|
||||
|
||||
Operand n = GetIntA32(context, op.Rn);
|
||||
Operand m = GetIntA32(context, op.Rm);
|
||||
|
||||
if (op.NHigh)
|
||||
{
|
||||
n = context.SignExtend16(OperandType.I64, context.ShiftRightUI(n, Const(16)));
|
||||
}
|
||||
else
|
||||
{
|
||||
n = context.SignExtend16(OperandType.I64, n);
|
||||
}
|
||||
|
||||
if (op.MHigh)
|
||||
{
|
||||
m = context.SignExtend16(OperandType.I64, context.ShiftRightUI(m, Const(16)));
|
||||
}
|
||||
else
|
||||
{
|
||||
m = context.SignExtend16(OperandType.I64, m);
|
||||
}
|
||||
|
||||
Operand res = context.Multiply(n, m);
|
||||
|
||||
Operand toAdd = context.ShiftLeft(context.ZeroExtend32(OperandType.I64, GetIntA32(context, op.RdHi)), Const(32));
|
||||
toAdd = context.BitwiseOr(toAdd, context.ZeroExtend32(OperandType.I64, GetIntA32(context, op.RdLo)));
|
||||
res = context.Add(res, toAdd);
|
||||
|
||||
Operand hi = context.ConvertI64ToI32(context.ShiftRightUI(res, Const(32)));
|
||||
Operand lo = context.ConvertI64ToI32(res);
|
||||
|
||||
EmitGenericAluStoreA32(context, op.RdHi, false, hi);
|
||||
EmitGenericAluStoreA32(context, op.RdLo, false, lo);
|
||||
}
|
||||
|
||||
public static void Smlaw_(ArmEmitterContext context)
|
||||
{
|
||||
IOpCode32AluMla op = (IOpCode32AluMla)context.CurrOp;
|
||||
|
||||
Operand n = GetIntA32(context, op.Rn);
|
||||
Operand m = GetIntA32(context, op.Rm);
|
||||
Operand a = GetIntA32(context, op.Ra);
|
||||
|
||||
if (op.MHigh)
|
||||
{
|
||||
m = context.SignExtend16(OperandType.I64, context.ShiftRightUI(m, Const(16)));
|
||||
}
|
||||
else
|
||||
{
|
||||
m = context.SignExtend16(OperandType.I64, m);
|
||||
}
|
||||
|
||||
Operand res = context.Multiply(context.SignExtend32(OperandType.I64, n), m);
|
||||
|
||||
Operand toAdd = context.ShiftLeft(context.SignExtend32(OperandType.I64, a), Const(16));
|
||||
res = context.Add(res, toAdd);
|
||||
res = context.ShiftRightSI(res, Const(16));
|
||||
Operand q = context.ICompareNotEqual(res, context.SignExtend32(OperandType.I64, res));
|
||||
res = context.ConvertI64ToI32(res);
|
||||
|
||||
UpdateQFlag(context, q);
|
||||
|
||||
EmitGenericAluStoreA32(context, op.Rd, false, res);
|
||||
}
|
||||
|
||||
public static void Smul__(ArmEmitterContext context)
|
||||
{
|
||||
IOpCode32AluMla op = (IOpCode32AluMla)context.CurrOp;
|
||||
|
||||
Operand n = GetIntA32(context, op.Rn);
|
||||
Operand m = GetIntA32(context, op.Rm);
|
||||
|
||||
if (op.NHigh)
|
||||
{
|
||||
n = context.ShiftRightSI(n, Const(16));
|
||||
}
|
||||
else
|
||||
{
|
||||
n = context.SignExtend16(OperandType.I32, n);
|
||||
}
|
||||
|
||||
if (op.MHigh)
|
||||
{
|
||||
m = context.ShiftRightSI(m, Const(16));
|
||||
}
|
||||
else
|
||||
{
|
||||
m = context.SignExtend16(OperandType.I32, m);
|
||||
}
|
||||
|
||||
Operand res = context.Multiply(n, m);
|
||||
|
||||
EmitGenericAluStoreA32(context, op.Rd, false, res);
|
||||
}
|
||||
|
||||
public static void Smull(ArmEmitterContext context)
|
||||
{
|
||||
IOpCode32AluUmull op = (IOpCode32AluUmull)context.CurrOp;
|
||||
|
||||
Operand n = context.SignExtend32(OperandType.I64, GetIntA32(context, op.Rn));
|
||||
Operand m = context.SignExtend32(OperandType.I64, GetIntA32(context, op.Rm));
|
||||
|
||||
Operand res = context.Multiply(n, m);
|
||||
|
||||
Operand hi = context.ConvertI64ToI32(context.ShiftRightUI(res, Const(32)));
|
||||
Operand lo = context.ConvertI64ToI32(res);
|
||||
|
||||
if (ShouldSetFlags(context))
|
||||
{
|
||||
EmitNZFlagsCheck(context, res);
|
||||
}
|
||||
|
||||
EmitGenericAluStoreA32(context, op.RdHi, ShouldSetFlags(context), hi);
|
||||
EmitGenericAluStoreA32(context, op.RdLo, ShouldSetFlags(context), lo);
|
||||
}
|
||||
|
||||
public static void Smulw_(ArmEmitterContext context)
|
||||
{
|
||||
IOpCode32AluMla op = (IOpCode32AluMla)context.CurrOp;
|
||||
|
||||
Operand n = GetIntA32(context, op.Rn);
|
||||
Operand m = GetIntA32(context, op.Rm);
|
||||
|
||||
if (op.MHigh)
|
||||
{
|
||||
m = context.SignExtend16(OperandType.I64, context.ShiftRightUI(m, Const(16)));
|
||||
}
|
||||
else
|
||||
{
|
||||
m = context.SignExtend16(OperandType.I64, m);
|
||||
}
|
||||
|
||||
Operand res = context.Multiply(context.SignExtend32(OperandType.I64, n), m);
|
||||
|
||||
res = context.ShiftRightUI(res, Const(16));
|
||||
res = context.ConvertI64ToI32(res);
|
||||
|
||||
EmitGenericAluStoreA32(context, op.Rd, false, res);
|
||||
}
|
||||
|
||||
public static void Umaal(ArmEmitterContext context)
|
||||
{
|
||||
IOpCode32AluUmull op = (IOpCode32AluUmull)context.CurrOp;
|
||||
|
||||
Operand n = context.ZeroExtend32(OperandType.I64, GetIntA32(context, op.Rn));
|
||||
Operand m = context.ZeroExtend32(OperandType.I64, GetIntA32(context, op.Rm));
|
||||
Operand dHi = context.ZeroExtend32(OperandType.I64, GetIntA32(context, op.RdHi));
|
||||
Operand dLo = context.ZeroExtend32(OperandType.I64, GetIntA32(context, op.RdLo));
|
||||
|
||||
Operand res = context.Multiply(n, m);
|
||||
res = context.Add(res, dHi);
|
||||
res = context.Add(res, dLo);
|
||||
|
||||
Operand hi = context.ConvertI64ToI32(context.ShiftRightUI(res, Const(32)));
|
||||
Operand lo = context.ConvertI64ToI32(res);
|
||||
|
||||
EmitGenericAluStoreA32(context, op.RdHi, false, hi);
|
||||
EmitGenericAluStoreA32(context, op.RdLo, false, lo);
|
||||
}
|
||||
|
||||
public static void Umlal(ArmEmitterContext context)
|
||||
{
|
||||
EmitMlal(context, false);
|
||||
}
|
||||
|
||||
public static void Umull(ArmEmitterContext context)
|
||||
{
|
||||
IOpCode32AluUmull op = (IOpCode32AluUmull)context.CurrOp;
|
||||
|
||||
Operand n = context.ZeroExtend32(OperandType.I64, GetIntA32(context, op.Rn));
|
||||
Operand m = context.ZeroExtend32(OperandType.I64, GetIntA32(context, op.Rm));
|
||||
|
||||
Operand res = context.Multiply(n, m);
|
||||
|
||||
Operand hi = context.ConvertI64ToI32(context.ShiftRightUI(res, Const(32)));
|
||||
Operand lo = context.ConvertI64ToI32(res);
|
||||
|
||||
if (ShouldSetFlags(context))
|
||||
{
|
||||
EmitNZFlagsCheck(context, res);
|
||||
}
|
||||
|
||||
EmitGenericAluStoreA32(context, op.RdHi, ShouldSetFlags(context), hi);
|
||||
EmitGenericAluStoreA32(context, op.RdLo, ShouldSetFlags(context), lo);
|
||||
}
|
||||
|
||||
private static void EmitMlal(ArmEmitterContext context, bool signed)
|
||||
{
|
||||
IOpCode32AluUmull op = (IOpCode32AluUmull)context.CurrOp;
|
||||
|
||||
Operand n = GetIntA32(context, op.Rn);
|
||||
Operand m = GetIntA32(context, op.Rm);
|
||||
|
||||
if (signed)
|
||||
{
|
||||
n = context.SignExtend32(OperandType.I64, n);
|
||||
m = context.SignExtend32(OperandType.I64, m);
|
||||
}
|
||||
else
|
||||
{
|
||||
n = context.ZeroExtend32(OperandType.I64, n);
|
||||
m = context.ZeroExtend32(OperandType.I64, m);
|
||||
}
|
||||
|
||||
Operand res = context.Multiply(n, m);
|
||||
|
||||
Operand toAdd = context.ShiftLeft(context.ZeroExtend32(OperandType.I64, GetIntA32(context, op.RdHi)), Const(32));
|
||||
toAdd = context.BitwiseOr(toAdd, context.ZeroExtend32(OperandType.I64, GetIntA32(context, op.RdLo)));
|
||||
res = context.Add(res, toAdd);
|
||||
|
||||
Operand hi = context.ConvertI64ToI32(context.ShiftRightUI(res, Const(32)));
|
||||
Operand lo = context.ConvertI64ToI32(res);
|
||||
|
||||
if (ShouldSetFlags(context))
|
||||
{
|
||||
EmitNZFlagsCheck(context, res);
|
||||
}
|
||||
|
||||
EmitGenericAluStoreA32(context, op.RdHi, ShouldSetFlags(context), hi);
|
||||
EmitGenericAluStoreA32(context, op.RdLo, ShouldSetFlags(context), lo);
|
||||
}
|
||||
|
||||
private static void UpdateQFlag(ArmEmitterContext context, Operand q)
|
||||
{
|
||||
Operand lblSkipSetQ = Label();
|
||||
|
||||
context.BranchIfFalse(lblSkipSetQ, q);
|
||||
|
||||
SetFlag(context, PState.QFlag, Const(1));
|
||||
|
||||
context.MarkLabel(lblSkipSetQ);
|
||||
}
|
||||
}
|
||||
}
|
||||
5224
src/ARMeilleure/Instructions/InstEmitSimdArithmetic.cs
Normal file
5224
src/ARMeilleure/Instructions/InstEmitSimdArithmetic.cs
Normal file
File diff suppressed because it is too large
Load Diff
1703
src/ARMeilleure/Instructions/InstEmitSimdArithmetic32.cs
Normal file
1703
src/ARMeilleure/Instructions/InstEmitSimdArithmetic32.cs
Normal file
File diff suppressed because it is too large
Load Diff
799
src/ARMeilleure/Instructions/InstEmitSimdCmp.cs
Normal file
799
src/ARMeilleure/Instructions/InstEmitSimdCmp.cs
Normal file
@@ -0,0 +1,799 @@
|
||||
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.Operand.Factory;
|
||||
|
||||
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 Facge_S(ArmEmitterContext context)
|
||||
{
|
||||
if (Optimizations.FastFP && Optimizations.UseAvx)
|
||||
{
|
||||
EmitSse2OrAvxCmpOpF(context, CmpCondition.GreaterThanOrEqual, scalar: true, absolute: true);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitCmpOpF(context, nameof(SoftFloat32.FPCompareGE), scalar: true, absolute: true);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Facge_V(ArmEmitterContext context)
|
||||
{
|
||||
if (Optimizations.FastFP && Optimizations.UseAvx)
|
||||
{
|
||||
EmitSse2OrAvxCmpOpF(context, CmpCondition.GreaterThanOrEqual, scalar: false, absolute: true);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitCmpOpF(context, nameof(SoftFloat32.FPCompareGE), scalar: false, absolute: true);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Facgt_S(ArmEmitterContext context)
|
||||
{
|
||||
if (Optimizations.FastFP && Optimizations.UseAvx)
|
||||
{
|
||||
EmitSse2OrAvxCmpOpF(context, CmpCondition.GreaterThan, scalar: true, absolute: true);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitCmpOpF(context, nameof(SoftFloat32.FPCompareGT), scalar: true, absolute: true);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Facgt_V(ArmEmitterContext context)
|
||||
{
|
||||
if (Optimizations.FastFP && Optimizations.UseAvx)
|
||||
{
|
||||
EmitSse2OrAvxCmpOpF(context, CmpCondition.GreaterThan, scalar: false, absolute: true);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitCmpOpF(context, nameof(SoftFloat32.FPCompareGT), scalar: false, absolute: true);
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
EmitSse2OrAvxCmpOpF(context, CmpCondition.Equal, scalar: true);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitCmpOpF(context, nameof(SoftFloat32.FPCompareEQ), scalar: true);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Fcmeq_V(ArmEmitterContext context)
|
||||
{
|
||||
if (Optimizations.FastFP && Optimizations.UseSse2)
|
||||
{
|
||||
EmitSse2OrAvxCmpOpF(context, CmpCondition.Equal, scalar: false);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitCmpOpF(context, nameof(SoftFloat32.FPCompareEQ), scalar: false);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Fcmge_S(ArmEmitterContext context)
|
||||
{
|
||||
if (Optimizations.FastFP && Optimizations.UseAvx)
|
||||
{
|
||||
EmitSse2OrAvxCmpOpF(context, CmpCondition.GreaterThanOrEqual, scalar: true);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitCmpOpF(context, nameof(SoftFloat32.FPCompareGE), scalar: true);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Fcmge_V(ArmEmitterContext context)
|
||||
{
|
||||
if (Optimizations.FastFP && Optimizations.UseAvx)
|
||||
{
|
||||
EmitSse2OrAvxCmpOpF(context, CmpCondition.GreaterThanOrEqual, scalar: false);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitCmpOpF(context, nameof(SoftFloat32.FPCompareGE), scalar: false);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Fcmgt_S(ArmEmitterContext context)
|
||||
{
|
||||
if (Optimizations.FastFP && Optimizations.UseAvx)
|
||||
{
|
||||
EmitSse2OrAvxCmpOpF(context, CmpCondition.GreaterThan, scalar: true);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitCmpOpF(context, nameof(SoftFloat32.FPCompareGT), scalar: true);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Fcmgt_V(ArmEmitterContext context)
|
||||
{
|
||||
if (Optimizations.FastFP && Optimizations.UseAvx)
|
||||
{
|
||||
EmitSse2OrAvxCmpOpF(context, CmpCondition.GreaterThan, scalar: false);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitCmpOpF(context, nameof(SoftFloat32.FPCompareGT), scalar: false);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Fcmle_S(ArmEmitterContext context)
|
||||
{
|
||||
if (Optimizations.FastFP && Optimizations.UseSse2)
|
||||
{
|
||||
EmitSse2OrAvxCmpOpF(context, CmpCondition.LessThanOrEqual, scalar: true);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitCmpOpF(context, nameof(SoftFloat32.FPCompareLE), scalar: true);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Fcmle_V(ArmEmitterContext context)
|
||||
{
|
||||
if (Optimizations.FastFP && Optimizations.UseSse2)
|
||||
{
|
||||
EmitSse2OrAvxCmpOpF(context, CmpCondition.LessThanOrEqual, scalar: false);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitCmpOpF(context, nameof(SoftFloat32.FPCompareLE), scalar: false);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Fcmlt_S(ArmEmitterContext context)
|
||||
{
|
||||
if (Optimizations.FastFP && Optimizations.UseSse2)
|
||||
{
|
||||
EmitSse2OrAvxCmpOpF(context, CmpCondition.LessThan, scalar: true);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitCmpOpF(context, nameof(SoftFloat32.FPCompareLT), scalar: true);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Fcmlt_V(ArmEmitterContext context)
|
||||
{
|
||||
if (Optimizations.FastFP && Optimizations.UseSse2)
|
||||
{
|
||||
EmitSse2OrAvxCmpOpF(context, CmpCondition.LessThan, scalar: false);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitCmpOpF(context, nameof(SoftFloat32.FPCompareLT), scalar: false);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Fcmp_S(ArmEmitterContext context)
|
||||
{
|
||||
if (Optimizations.UseAdvSimd)
|
||||
{
|
||||
InstEmitSimdHelperArm64.EmitFcmpOrFcmpe(context, signalNaNs: false);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitFcmpOrFcmpe(context, signalNaNs: false);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Fcmpe_S(ArmEmitterContext context)
|
||||
{
|
||||
if (Optimizations.UseAdvSimd)
|
||||
{
|
||||
InstEmitSimdHelperArm64.EmitFcmpOrFcmpe(context, signalNaNs: true);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitFcmpOrFcmpe(context, signalNaNs: true);
|
||||
}
|
||||
}
|
||||
|
||||
private 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, op.Nzcv);
|
||||
|
||||
context.Branch(lblEnd);
|
||||
|
||||
context.MarkLabel(lblTrue);
|
||||
|
||||
EmitFcmpOrFcmpe(context, signalNaNs);
|
||||
|
||||
context.MarkLabel(lblEnd);
|
||||
}
|
||||
|
||||
private static void EmitSetNzcv(ArmEmitterContext context, int nzcv)
|
||||
{
|
||||
Operand Extract(int value, int bit)
|
||||
{
|
||||
if (bit != 0)
|
||||
{
|
||||
value >>= bit;
|
||||
}
|
||||
|
||||
value &= 1;
|
||||
|
||||
return Const(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 EmitFcmpOrFcmpe(ArmEmitterContext context, bool signalNaNs)
|
||||
{
|
||||
OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp;
|
||||
|
||||
bool cmpWithZero = !(op is OpCodeSimdFcond) ? op.Bit3 : false;
|
||||
|
||||
if (Optimizations.FastFP && (signalNaNs ? Optimizations.UseAvx : Optimizations.UseSse2))
|
||||
{
|
||||
Operand n = GetVec(op.Rn);
|
||||
Operand m = cmpWithZero ? context.VectorZero() : GetVec(op.Rm);
|
||||
|
||||
CmpCondition cmpOrdered = signalNaNs ? CmpCondition.OrderedS : CmpCondition.OrderedQ;
|
||||
|
||||
Operand lblNaN = Label();
|
||||
Operand lblEnd = Label();
|
||||
|
||||
if (op.Size == 0)
|
||||
{
|
||||
Operand ordMask = context.AddIntrinsic(Intrinsic.X86Cmpss, n, m, Const((int)cmpOrdered));
|
||||
|
||||
Operand isOrdered = context.AddIntrinsicInt(Intrinsic.X86Cvtsi2si, ordMask);
|
||||
|
||||
context.BranchIfFalse(lblNaN, isOrdered);
|
||||
|
||||
Operand nCopy = context.Copy(n);
|
||||
Operand mCopy = cmpWithZero ? context.VectorZero() : context.Copy(m);
|
||||
|
||||
Operand cf = context.AddIntrinsicInt(Intrinsic.X86Comissge, nCopy, mCopy);
|
||||
Operand zf = context.AddIntrinsicInt(Intrinsic.X86Comisseq, nCopy, mCopy);
|
||||
Operand nf = context.AddIntrinsicInt(Intrinsic.X86Comisslt, nCopy, mCopy);
|
||||
|
||||
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((int)cmpOrdered));
|
||||
|
||||
Operand isOrdered = context.AddIntrinsicLong(Intrinsic.X86Cvtsi2si, ordMask);
|
||||
|
||||
context.BranchIfFalse(lblNaN, isOrdered);
|
||||
|
||||
Operand nCopy = context.Copy(n);
|
||||
Operand mCopy = cmpWithZero ? context.VectorZero() : context.Copy(m);
|
||||
|
||||
Operand cf = context.AddIntrinsicInt(Intrinsic.X86Comisdge, nCopy, mCopy);
|
||||
Operand zf = context.AddIntrinsicInt(Intrinsic.X86Comisdeq, nCopy, mCopy);
|
||||
Operand nf = context.AddIntrinsicInt(Intrinsic.X86Comisdlt, nCopy, mCopy);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
Operand nzcv = EmitSoftFloatCall(context, nameof(SoftFloat32.FPCompare), 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, string name, bool scalar, bool absolute = false)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
if (absolute)
|
||||
{
|
||||
ne = EmitUnaryMathCall(context, nameof(Math.Abs), ne);
|
||||
me = EmitUnaryMathCall(context, nameof(Math.Abs), me);
|
||||
}
|
||||
|
||||
Operand e = EmitSoftFloatCall(context, name, ne, me);
|
||||
|
||||
res = context.VectorInsert(res, e, index);
|
||||
}
|
||||
|
||||
context.Copy(GetVec(op.Rd), res);
|
||||
}
|
||||
|
||||
private static void EmitSse2OrAvxCmpOpF(ArmEmitterContext context, CmpCondition cond, bool scalar, bool absolute = 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)
|
||||
{
|
||||
if (absolute)
|
||||
{
|
||||
Operand mask = scalar ? X86GetScalar(context, int.MaxValue) : X86GetAllElements(context, int.MaxValue);
|
||||
|
||||
n = context.AddIntrinsic(Intrinsic.X86Andps, n, mask);
|
||||
m = context.AddIntrinsic(Intrinsic.X86Andps, m, mask);
|
||||
}
|
||||
|
||||
Intrinsic inst = scalar ? Intrinsic.X86Cmpss : Intrinsic.X86Cmpps;
|
||||
|
||||
Operand res = 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) */
|
||||
{
|
||||
if (absolute)
|
||||
{
|
||||
Operand mask = scalar ? X86GetScalar(context, long.MaxValue) : X86GetAllElements(context, long.MaxValue);
|
||||
|
||||
n = context.AddIntrinsic(Intrinsic.X86Andpd, n, mask);
|
||||
m = context.AddIntrinsic(Intrinsic.X86Andpd, m, mask);
|
||||
}
|
||||
|
||||
Intrinsic inst = scalar ? Intrinsic.X86Cmpsd : Intrinsic.X86Cmppd;
|
||||
|
||||
Operand res = context.AddIntrinsic(inst, n, m, Const((int)cond));
|
||||
|
||||
if (scalar)
|
||||
{
|
||||
res = context.VectorZeroUpper64(res);
|
||||
}
|
||||
|
||||
context.Copy(GetVec(op.Rd), res);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
437
src/ARMeilleure/Instructions/InstEmitSimdCmp32.cs
Normal file
437
src/ARMeilleure/Instructions/InstEmitSimdCmp32.cs
Normal file
@@ -0,0 +1,437 @@
|
||||
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.Instructions.InstEmitSimdHelper32;
|
||||
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
|
||||
|
||||
namespace ARMeilleure.Instructions
|
||||
{
|
||||
using Func2I = Func<Operand, Operand, Operand>;
|
||||
|
||||
static partial class InstEmit32
|
||||
{
|
||||
public static void Vceq_V(ArmEmitterContext context)
|
||||
{
|
||||
if (Optimizations.FastFP && Optimizations.UseAdvSimd)
|
||||
{
|
||||
InstEmitSimdHelper32Arm64.EmitCmpOpF32(context, CmpCondition.Equal, false);
|
||||
}
|
||||
else if (Optimizations.FastFP && Optimizations.UseSse2)
|
||||
{
|
||||
EmitSse2OrAvxCmpOpF32(context, CmpCondition.Equal, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitCmpOpF32(context, nameof(SoftFloat32.FPCompareEQFpscr), false);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Vceq_I(ArmEmitterContext context)
|
||||
{
|
||||
EmitCmpOpI32(context, context.ICompareEqual, context.ICompareEqual, false, false);
|
||||
}
|
||||
|
||||
public static void Vceq_Z(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32Simd op = (OpCode32Simd)context.CurrOp;
|
||||
|
||||
if (op.F)
|
||||
{
|
||||
if (Optimizations.FastFP && Optimizations.UseAdvSimd)
|
||||
{
|
||||
InstEmitSimdHelper32Arm64.EmitCmpOpF32(context, CmpCondition.Equal, true);
|
||||
}
|
||||
else if (Optimizations.FastFP && Optimizations.UseSse2)
|
||||
{
|
||||
EmitSse2OrAvxCmpOpF32(context, CmpCondition.Equal, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitCmpOpF32(context, nameof(SoftFloat32.FPCompareEQFpscr), true);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitCmpOpI32(context, context.ICompareEqual, context.ICompareEqual, true, false);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Vcge_V(ArmEmitterContext context)
|
||||
{
|
||||
if (Optimizations.FastFP && Optimizations.UseAdvSimd)
|
||||
{
|
||||
InstEmitSimdHelper32Arm64.EmitCmpOpF32(context, CmpCondition.GreaterThanOrEqual, false);
|
||||
}
|
||||
else if (Optimizations.FastFP && Optimizations.UseAvx)
|
||||
{
|
||||
EmitSse2OrAvxCmpOpF32(context, CmpCondition.GreaterThanOrEqual, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitCmpOpF32(context, nameof(SoftFloat32.FPCompareGEFpscr), false);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Vcge_I(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32SimdReg op = (OpCode32SimdReg)context.CurrOp;
|
||||
|
||||
EmitCmpOpI32(context, context.ICompareGreaterOrEqual, context.ICompareGreaterOrEqualUI, false, !op.U);
|
||||
}
|
||||
|
||||
public static void Vcge_Z(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32Simd op = (OpCode32Simd)context.CurrOp;
|
||||
|
||||
if (op.F)
|
||||
{
|
||||
if (Optimizations.FastFP && Optimizations.UseAdvSimd)
|
||||
{
|
||||
InstEmitSimdHelper32Arm64.EmitCmpOpF32(context, CmpCondition.GreaterThanOrEqual, true);
|
||||
}
|
||||
else if (Optimizations.FastFP && Optimizations.UseAvx)
|
||||
{
|
||||
EmitSse2OrAvxCmpOpF32(context, CmpCondition.GreaterThanOrEqual, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitCmpOpF32(context, nameof(SoftFloat32.FPCompareGEFpscr), true);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitCmpOpI32(context, context.ICompareGreaterOrEqual, context.ICompareGreaterOrEqualUI, true, true);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Vcgt_V(ArmEmitterContext context)
|
||||
{
|
||||
if (Optimizations.FastFP && Optimizations.UseAdvSimd)
|
||||
{
|
||||
InstEmitSimdHelper32Arm64.EmitCmpOpF32(context, CmpCondition.GreaterThan, false);
|
||||
}
|
||||
else if (Optimizations.FastFP && Optimizations.UseAvx)
|
||||
{
|
||||
EmitSse2OrAvxCmpOpF32(context, CmpCondition.GreaterThan, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitCmpOpF32(context, nameof(SoftFloat32.FPCompareGTFpscr), false);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Vcgt_I(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32SimdReg op = (OpCode32SimdReg)context.CurrOp;
|
||||
|
||||
EmitCmpOpI32(context, context.ICompareGreater, context.ICompareGreaterUI, false, !op.U);
|
||||
}
|
||||
|
||||
public static void Vcgt_Z(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32Simd op = (OpCode32Simd)context.CurrOp;
|
||||
|
||||
if (op.F)
|
||||
{
|
||||
if (Optimizations.FastFP && Optimizations.UseAdvSimd)
|
||||
{
|
||||
InstEmitSimdHelper32Arm64.EmitCmpOpF32(context, CmpCondition.GreaterThan, true);
|
||||
}
|
||||
else if (Optimizations.FastFP && Optimizations.UseAvx)
|
||||
{
|
||||
EmitSse2OrAvxCmpOpF32(context, CmpCondition.GreaterThan, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitCmpOpF32(context, nameof(SoftFloat32.FPCompareGTFpscr), true);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitCmpOpI32(context, context.ICompareGreater, context.ICompareGreaterUI, true, true);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Vcle_Z(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32Simd op = (OpCode32Simd)context.CurrOp;
|
||||
|
||||
if (op.F)
|
||||
{
|
||||
if (Optimizations.FastFP && Optimizations.UseAdvSimd)
|
||||
{
|
||||
InstEmitSimdHelper32Arm64.EmitCmpOpF32(context, CmpCondition.LessThanOrEqual, true);
|
||||
}
|
||||
else if (Optimizations.FastFP && Optimizations.UseSse2)
|
||||
{
|
||||
EmitSse2OrAvxCmpOpF32(context, CmpCondition.LessThanOrEqual, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitCmpOpF32(context, nameof(SoftFloat32.FPCompareLEFpscr), true);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitCmpOpI32(context, context.ICompareLessOrEqual, context.ICompareLessOrEqualUI, true, true);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Vclt_Z(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32Simd op = (OpCode32Simd)context.CurrOp;
|
||||
|
||||
if (op.F)
|
||||
{
|
||||
if (Optimizations.FastFP && Optimizations.UseAdvSimd)
|
||||
{
|
||||
InstEmitSimdHelper32Arm64.EmitCmpOpF32(context, CmpCondition.LessThan, true);
|
||||
}
|
||||
else if (Optimizations.FastFP && Optimizations.UseSse2)
|
||||
{
|
||||
EmitSse2OrAvxCmpOpF32(context, CmpCondition.LessThan, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitCmpOpF32(context, nameof(SoftFloat32.FPCompareLTFpscr), true);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitCmpOpI32(context, context.ICompareLess, context.ICompareLessUI, true, true);
|
||||
}
|
||||
}
|
||||
|
||||
private static void EmitCmpOpF32(ArmEmitterContext context, string name, bool zero)
|
||||
{
|
||||
if (zero)
|
||||
{
|
||||
EmitVectorUnaryOpF32(context, (m) =>
|
||||
{
|
||||
Operand zeroOp = m.Type == OperandType.FP64 ? ConstF(0.0d) : ConstF(0.0f);
|
||||
|
||||
return EmitSoftFloatCallDefaultFpscr(context, name, m, zeroOp);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitVectorBinaryOpF32(context, (n, m) =>
|
||||
{
|
||||
return EmitSoftFloatCallDefaultFpscr(context, name, n, m);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private static Operand ZerosOrOnes(ArmEmitterContext context, Operand fromBool, OperandType baseType)
|
||||
{
|
||||
var ones = (baseType == OperandType.I64) ? Const(-1L) : Const(-1);
|
||||
|
||||
return context.ConditionalSelect(fromBool, ones, Const(baseType, 0L));
|
||||
}
|
||||
|
||||
private static void EmitCmpOpI32(
|
||||
ArmEmitterContext context,
|
||||
Func2I signedOp,
|
||||
Func2I unsignedOp,
|
||||
bool zero,
|
||||
bool signed)
|
||||
{
|
||||
if (zero)
|
||||
{
|
||||
if (signed)
|
||||
{
|
||||
EmitVectorUnaryOpSx32(context, (m) =>
|
||||
{
|
||||
OperandType type = m.Type;
|
||||
Operand zeroV = (type == OperandType.I64) ? Const(0L) : Const(0);
|
||||
|
||||
return ZerosOrOnes(context, signedOp(m, zeroV), type);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitVectorUnaryOpZx32(context, (m) =>
|
||||
{
|
||||
OperandType type = m.Type;
|
||||
Operand zeroV = (type == OperandType.I64) ? Const(0L) : Const(0);
|
||||
|
||||
return ZerosOrOnes(context, unsignedOp(m, zeroV), type);
|
||||
});
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (signed)
|
||||
{
|
||||
EmitVectorBinaryOpSx32(context, (n, m) => ZerosOrOnes(context, signedOp(n, m), n.Type));
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitVectorBinaryOpZx32(context, (n, m) => ZerosOrOnes(context, unsignedOp(n, m), n.Type));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void Vcmp(ArmEmitterContext context)
|
||||
{
|
||||
if (Optimizations.UseAdvSimd)
|
||||
{
|
||||
InstEmitSimdHelper32Arm64.EmitVcmpOrVcmpe(context, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitVcmpOrVcmpe(context, false);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Vcmpe(ArmEmitterContext context)
|
||||
{
|
||||
if (Optimizations.UseAdvSimd)
|
||||
{
|
||||
InstEmitSimdHelper32Arm64.EmitVcmpOrVcmpe(context, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitVcmpOrVcmpe(context, true);
|
||||
}
|
||||
}
|
||||
|
||||
private static void EmitVcmpOrVcmpe(ArmEmitterContext context, bool signalNaNs)
|
||||
{
|
||||
OpCode32SimdS op = (OpCode32SimdS)context.CurrOp;
|
||||
|
||||
bool cmpWithZero = (op.Opc & 2) != 0;
|
||||
int sizeF = op.Size & 1;
|
||||
|
||||
if (Optimizations.FastFP && (signalNaNs ? Optimizations.UseAvx : Optimizations.UseSse2))
|
||||
{
|
||||
CmpCondition cmpOrdered = signalNaNs ? CmpCondition.OrderedS : CmpCondition.OrderedQ;
|
||||
|
||||
bool doubleSize = sizeF != 0;
|
||||
int shift = doubleSize ? 1 : 2;
|
||||
Operand m = GetVecA32(op.Vm >> shift);
|
||||
Operand n = GetVecA32(op.Vd >> shift);
|
||||
|
||||
n = EmitSwapScalar(context, n, op.Vd, doubleSize);
|
||||
m = cmpWithZero ? context.VectorZero() : EmitSwapScalar(context, m, op.Vm, doubleSize);
|
||||
|
||||
Operand lblNaN = Label();
|
||||
Operand lblEnd = Label();
|
||||
|
||||
if (!doubleSize)
|
||||
{
|
||||
Operand ordMask = context.AddIntrinsic(Intrinsic.X86Cmpss, n, m, Const((int)cmpOrdered));
|
||||
|
||||
Operand isOrdered = context.AddIntrinsicInt(Intrinsic.X86Cvtsi2si, ordMask);
|
||||
|
||||
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);
|
||||
|
||||
SetFpFlag(context, FPState.VFlag, Const(0));
|
||||
SetFpFlag(context, FPState.CFlag, cf);
|
||||
SetFpFlag(context, FPState.ZFlag, zf);
|
||||
SetFpFlag(context, FPState.NFlag, nf);
|
||||
}
|
||||
else
|
||||
{
|
||||
Operand ordMask = context.AddIntrinsic(Intrinsic.X86Cmpsd, n, m, Const((int)cmpOrdered));
|
||||
|
||||
Operand isOrdered = context.AddIntrinsicLong(Intrinsic.X86Cvtsi2si, ordMask);
|
||||
|
||||
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);
|
||||
|
||||
SetFpFlag(context, FPState.VFlag, Const(0));
|
||||
SetFpFlag(context, FPState.CFlag, cf);
|
||||
SetFpFlag(context, FPState.ZFlag, zf);
|
||||
SetFpFlag(context, FPState.NFlag, nf);
|
||||
}
|
||||
|
||||
context.Branch(lblEnd);
|
||||
|
||||
context.MarkLabel(lblNaN);
|
||||
|
||||
SetFpFlag(context, FPState.VFlag, Const(1));
|
||||
SetFpFlag(context, FPState.CFlag, Const(1));
|
||||
SetFpFlag(context, FPState.ZFlag, Const(0));
|
||||
SetFpFlag(context, FPState.NFlag, Const(0));
|
||||
|
||||
context.MarkLabel(lblEnd);
|
||||
}
|
||||
else
|
||||
{
|
||||
OperandType type = sizeF != 0 ? OperandType.FP64 : OperandType.FP32;
|
||||
|
||||
Operand ne = ExtractScalar(context, type, op.Vd);
|
||||
Operand me;
|
||||
|
||||
if (cmpWithZero)
|
||||
{
|
||||
me = sizeF == 0 ? ConstF(0f) : ConstF(0d);
|
||||
}
|
||||
else
|
||||
{
|
||||
me = ExtractScalar(context, type, op.Vm);
|
||||
}
|
||||
|
||||
Operand nzcv = EmitSoftFloatCall(context, nameof(SoftFloat32.FPCompare), ne, me, Const(signalNaNs));
|
||||
|
||||
EmitSetFpscrNzcv(context, nzcv);
|
||||
}
|
||||
}
|
||||
|
||||
private static void EmitSetFpscrNzcv(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;
|
||||
}
|
||||
|
||||
SetFpFlag(context, FPState.VFlag, Extract(nzcv, 0));
|
||||
SetFpFlag(context, FPState.CFlag, Extract(nzcv, 1));
|
||||
SetFpFlag(context, FPState.ZFlag, Extract(nzcv, 2));
|
||||
SetFpFlag(context, FPState.NFlag, Extract(nzcv, 3));
|
||||
}
|
||||
|
||||
private static void EmitSse2OrAvxCmpOpF32(ArmEmitterContext context, CmpCondition cond, bool zero)
|
||||
{
|
||||
OpCode32Simd op = (OpCode32Simd)context.CurrOp;
|
||||
|
||||
int sizeF = op.Size & 1;
|
||||
Intrinsic inst = (sizeF == 0) ? Intrinsic.X86Cmpps : Intrinsic.X86Cmppd;
|
||||
|
||||
if (zero)
|
||||
{
|
||||
EmitVectorUnaryOpSimd32(context, (m) =>
|
||||
{
|
||||
return context.AddIntrinsic(inst, m, context.VectorZero(), Const((int)cond));
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitVectorBinaryOpSimd32(context, (n, m) =>
|
||||
{
|
||||
return context.AddIntrinsic(inst, n, m, Const((int)cond));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
99
src/ARMeilleure/Instructions/InstEmitSimdCrypto.cs
Normal file
99
src/ARMeilleure/Instructions/InstEmitSimdCrypto.cs
Normal file
@@ -0,0 +1,99 @@
|
||||
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);
|
||||
|
||||
Operand res;
|
||||
|
||||
if (Optimizations.UseAesni)
|
||||
{
|
||||
res = context.AddIntrinsic(Intrinsic.X86Aesdeclast, context.AddIntrinsic(Intrinsic.X86Xorpd, d, n), context.VectorZero());
|
||||
}
|
||||
else
|
||||
{
|
||||
res = context.Call(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Decrypt)), d, n);
|
||||
}
|
||||
|
||||
context.Copy(d, res);
|
||||
}
|
||||
|
||||
public static void Aese_V(ArmEmitterContext context)
|
||||
{
|
||||
OpCodeSimd op = (OpCodeSimd)context.CurrOp;
|
||||
|
||||
Operand d = GetVec(op.Rd);
|
||||
Operand n = GetVec(op.Rn);
|
||||
|
||||
Operand res;
|
||||
|
||||
if (Optimizations.UseAesni)
|
||||
{
|
||||
res = context.AddIntrinsic(Intrinsic.X86Aesenclast, context.AddIntrinsic(Intrinsic.X86Xorpd, d, n), context.VectorZero());
|
||||
}
|
||||
else
|
||||
{
|
||||
res = context.Call(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Encrypt)), d, n);
|
||||
}
|
||||
|
||||
context.Copy(d, res);
|
||||
}
|
||||
|
||||
public static void Aesimc_V(ArmEmitterContext context)
|
||||
{
|
||||
OpCodeSimd op = (OpCodeSimd)context.CurrOp;
|
||||
|
||||
Operand n = GetVec(op.Rn);
|
||||
|
||||
Operand res;
|
||||
|
||||
if (Optimizations.UseAesni)
|
||||
{
|
||||
res = context.AddIntrinsic(Intrinsic.X86Aesimc, n);
|
||||
}
|
||||
else
|
||||
{
|
||||
res = context.Call(typeof(SoftFallback).GetMethod(nameof(SoftFallback.InverseMixColumns)), n);
|
||||
}
|
||||
|
||||
context.Copy(GetVec(op.Rd), res);
|
||||
}
|
||||
|
||||
public static void Aesmc_V(ArmEmitterContext context)
|
||||
{
|
||||
OpCodeSimd op = (OpCodeSimd)context.CurrOp;
|
||||
|
||||
Operand n = GetVec(op.Rn);
|
||||
|
||||
Operand res;
|
||||
|
||||
if (Optimizations.UseAesni)
|
||||
{
|
||||
Operand roundKey = context.VectorZero();
|
||||
|
||||
// Inverse Shift Rows, Inverse Sub Bytes, xor 0 so nothing happens
|
||||
res = context.AddIntrinsic(Intrinsic.X86Aesdeclast, n, roundKey);
|
||||
|
||||
// Shift Rows, Sub Bytes, Mix Columns (!), xor 0 so nothing happens
|
||||
res = context.AddIntrinsic(Intrinsic.X86Aesenc, res, roundKey);
|
||||
}
|
||||
else
|
||||
{
|
||||
res = context.Call(typeof(SoftFallback).GetMethod(nameof(SoftFallback.MixColumns)), n);
|
||||
}
|
||||
|
||||
context.Copy(GetVec(op.Rd), res);
|
||||
}
|
||||
}
|
||||
}
|
||||
99
src/ARMeilleure/Instructions/InstEmitSimdCrypto32.cs
Normal file
99
src/ARMeilleure/Instructions/InstEmitSimdCrypto32.cs
Normal file
@@ -0,0 +1,99 @@
|
||||
using ARMeilleure.Decoders;
|
||||
using ARMeilleure.IntermediateRepresentation;
|
||||
using ARMeilleure.Translation;
|
||||
|
||||
using static ARMeilleure.Instructions.InstEmitHelper;
|
||||
|
||||
namespace ARMeilleure.Instructions
|
||||
{
|
||||
partial class InstEmit32
|
||||
{
|
||||
public static void Aesd_V(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32Simd op = (OpCode32Simd)context.CurrOp;
|
||||
|
||||
Operand d = GetVecA32(op.Qd);
|
||||
Operand n = GetVecA32(op.Qm);
|
||||
|
||||
Operand res;
|
||||
|
||||
if (Optimizations.UseAesni)
|
||||
{
|
||||
res = context.AddIntrinsic(Intrinsic.X86Aesdeclast, context.AddIntrinsic(Intrinsic.X86Xorpd, d, n), context.VectorZero());
|
||||
}
|
||||
else
|
||||
{
|
||||
res = context.Call(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Decrypt)), d, n);
|
||||
}
|
||||
|
||||
context.Copy(d, res);
|
||||
}
|
||||
|
||||
public static void Aese_V(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32Simd op = (OpCode32Simd)context.CurrOp;
|
||||
|
||||
Operand d = GetVecA32(op.Qd);
|
||||
Operand n = GetVecA32(op.Qm);
|
||||
|
||||
Operand res;
|
||||
|
||||
if (Optimizations.UseAesni)
|
||||
{
|
||||
res = context.AddIntrinsic(Intrinsic.X86Aesenclast, context.AddIntrinsic(Intrinsic.X86Xorpd, d, n), context.VectorZero());
|
||||
}
|
||||
else
|
||||
{
|
||||
res = context.Call(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Encrypt)), d, n);
|
||||
}
|
||||
|
||||
context.Copy(d, res);
|
||||
}
|
||||
|
||||
public static void Aesimc_V(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32Simd op = (OpCode32Simd)context.CurrOp;
|
||||
|
||||
Operand n = GetVecA32(op.Qm);
|
||||
|
||||
Operand res;
|
||||
|
||||
if (Optimizations.UseAesni)
|
||||
{
|
||||
res = context.AddIntrinsic(Intrinsic.X86Aesimc, n);
|
||||
}
|
||||
else
|
||||
{
|
||||
res = context.Call(typeof(SoftFallback).GetMethod(nameof(SoftFallback.InverseMixColumns)), n);
|
||||
}
|
||||
|
||||
context.Copy(GetVecA32(op.Qd), res);
|
||||
}
|
||||
|
||||
public static void Aesmc_V(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32Simd op = (OpCode32Simd)context.CurrOp;
|
||||
|
||||
Operand n = GetVecA32(op.Qm);
|
||||
|
||||
Operand res;
|
||||
|
||||
if (Optimizations.UseAesni)
|
||||
{
|
||||
Operand roundKey = context.VectorZero();
|
||||
|
||||
// Inverse Shift Rows, Inverse Sub Bytes, xor 0 so nothing happens.
|
||||
res = context.AddIntrinsic(Intrinsic.X86Aesdeclast, n, roundKey);
|
||||
|
||||
// Shift Rows, Sub Bytes, Mix Columns (!), xor 0 so nothing happens.
|
||||
res = context.AddIntrinsic(Intrinsic.X86Aesenc, res, roundKey);
|
||||
}
|
||||
else
|
||||
{
|
||||
res = context.Call(typeof(SoftFallback).GetMethod(nameof(SoftFallback.MixColumns)), n);
|
||||
}
|
||||
|
||||
context.Copy(GetVecA32(op.Qd), res);
|
||||
}
|
||||
}
|
||||
}
|
||||
1891
src/ARMeilleure/Instructions/InstEmitSimdCvt.cs
Normal file
1891
src/ARMeilleure/Instructions/InstEmitSimdCvt.cs
Normal file
File diff suppressed because it is too large
Load Diff
800
src/ARMeilleure/Instructions/InstEmitSimdCvt32.cs
Normal file
800
src/ARMeilleure/Instructions/InstEmitSimdCvt32.cs
Normal file
@@ -0,0 +1,800 @@
|
||||
using ARMeilleure.Decoders;
|
||||
using ARMeilleure.IntermediateRepresentation;
|
||||
using ARMeilleure.State;
|
||||
using ARMeilleure.Translation;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Reflection;
|
||||
|
||||
using static ARMeilleure.Instructions.InstEmitHelper;
|
||||
using static ARMeilleure.Instructions.InstEmitSimdHelper;
|
||||
using static ARMeilleure.Instructions.InstEmitSimdHelper32;
|
||||
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
|
||||
|
||||
namespace ARMeilleure.Instructions
|
||||
{
|
||||
static partial class InstEmit32
|
||||
{
|
||||
private static int FlipVdBits(int vd, bool lowBit)
|
||||
{
|
||||
if (lowBit)
|
||||
{
|
||||
// Move the low bit to the top.
|
||||
return ((vd & 0x1) << 4) | (vd >> 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Move the high bit to the bottom.
|
||||
return ((vd & 0xf) << 1) | (vd >> 4);
|
||||
}
|
||||
}
|
||||
|
||||
private static Operand EmitSaturateFloatToInt(ArmEmitterContext context, Operand op1, bool unsigned)
|
||||
{
|
||||
MethodInfo info;
|
||||
|
||||
if (op1.Type == OperandType.FP64)
|
||||
{
|
||||
info = unsigned
|
||||
? typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF64ToU32))
|
||||
: typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF64ToS32));
|
||||
}
|
||||
else
|
||||
{
|
||||
info = unsigned
|
||||
? typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF32ToU32))
|
||||
: typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF32ToS32));
|
||||
}
|
||||
|
||||
return context.Call(info, op1);
|
||||
}
|
||||
|
||||
public static void Vcvt_V(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32Simd op = (OpCode32Simd)context.CurrOp;
|
||||
|
||||
bool unsigned = (op.Opc & 1) != 0;
|
||||
bool toInteger = (op.Opc & 2) != 0;
|
||||
OperandType floatSize = (op.Size == 2) ? OperandType.FP32 : OperandType.FP64;
|
||||
|
||||
if (toInteger)
|
||||
{
|
||||
if (Optimizations.UseAdvSimd)
|
||||
{
|
||||
InstEmitSimdHelper32Arm64.EmitVectorUnaryOpF32(context, unsigned ? Intrinsic.Arm64FcvtzuV : Intrinsic.Arm64FcvtzsV);
|
||||
}
|
||||
else if (Optimizations.UseSse41)
|
||||
{
|
||||
EmitSse41ConvertVector32(context, FPRoundingMode.TowardsZero, !unsigned);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitVectorUnaryOpF32(context, (op1) =>
|
||||
{
|
||||
return EmitSaturateFloatToInt(context, op1, unsigned);
|
||||
});
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Optimizations.UseSse2)
|
||||
{
|
||||
EmitVectorUnaryOpSimd32(context, (n) =>
|
||||
{
|
||||
if (unsigned)
|
||||
{
|
||||
Operand mask = X86GetAllElements(context, 0x47800000);
|
||||
|
||||
Operand res = context.AddIntrinsic(Intrinsic.X86Psrld, n, Const(16));
|
||||
res = context.AddIntrinsic(Intrinsic.X86Cvtdq2ps, res);
|
||||
res = context.AddIntrinsic(Intrinsic.X86Mulps, res, mask);
|
||||
|
||||
Operand res2 = context.AddIntrinsic(Intrinsic.X86Pslld, n, Const(16));
|
||||
res2 = context.AddIntrinsic(Intrinsic.X86Psrld, res2, Const(16));
|
||||
res2 = context.AddIntrinsic(Intrinsic.X86Cvtdq2ps, res2);
|
||||
|
||||
return context.AddIntrinsic(Intrinsic.X86Addps, res, res2);
|
||||
}
|
||||
else
|
||||
{
|
||||
return context.AddIntrinsic(Intrinsic.X86Cvtdq2ps, n);
|
||||
}
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
if (unsigned)
|
||||
{
|
||||
EmitVectorUnaryOpZx32(context, (op1) => EmitFPConvert(context, op1, floatSize, false));
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitVectorUnaryOpSx32(context, (op1) => EmitFPConvert(context, op1, floatSize, true));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void Vcvt_FD(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32SimdS op = (OpCode32SimdS)context.CurrOp;
|
||||
|
||||
int vm = op.Vm;
|
||||
int vd;
|
||||
if (op.Size == 3)
|
||||
{
|
||||
vd = FlipVdBits(op.Vd, false);
|
||||
// Double to single.
|
||||
Operand fp = ExtractScalar(context, OperandType.FP64, vm);
|
||||
|
||||
Operand res = context.ConvertToFP(OperandType.FP32, fp);
|
||||
|
||||
InsertScalar(context, vd, res);
|
||||
}
|
||||
else
|
||||
{
|
||||
vd = FlipVdBits(op.Vd, true);
|
||||
// Single to double.
|
||||
Operand fp = ExtractScalar(context, OperandType.FP32, vm);
|
||||
|
||||
Operand res = context.ConvertToFP(OperandType.FP64, fp);
|
||||
|
||||
InsertScalar(context, vd, res);
|
||||
}
|
||||
}
|
||||
|
||||
// VCVT (floating-point to integer, floating-point) | VCVT (integer to floating-point, floating-point).
|
||||
public static void Vcvt_FI(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32SimdCvtFI op = (OpCode32SimdCvtFI)context.CurrOp;
|
||||
|
||||
bool toInteger = (op.Opc2 & 0b100) != 0;
|
||||
|
||||
OperandType floatSize = op.RegisterSize == RegisterSize.Int64 ? OperandType.FP64 : OperandType.FP32;
|
||||
|
||||
if (toInteger)
|
||||
{
|
||||
bool unsigned = (op.Opc2 & 1) == 0;
|
||||
bool roundWithFpscr = op.Opc != 1;
|
||||
|
||||
if (!roundWithFpscr && Optimizations.UseAdvSimd)
|
||||
{
|
||||
bool doubleSize = floatSize == OperandType.FP64;
|
||||
|
||||
if (doubleSize)
|
||||
{
|
||||
Operand m = GetVecA32(op.Vm >> 1);
|
||||
|
||||
Operand toConvert = InstEmitSimdHelper32Arm64.EmitExtractScalar(context, m, op.Vm, doubleSize);
|
||||
|
||||
Intrinsic inst = (unsigned ? Intrinsic.Arm64FcvtzuGp : Intrinsic.Arm64FcvtzsGp) | Intrinsic.Arm64VDouble;
|
||||
|
||||
Operand asInteger = context.AddIntrinsicInt(inst, toConvert);
|
||||
|
||||
InsertScalar(context, op.Vd, asInteger);
|
||||
}
|
||||
else
|
||||
{
|
||||
InstEmitSimdHelper32Arm64.EmitScalarUnaryOpF32(context, unsigned ? Intrinsic.Arm64FcvtzuS : Intrinsic.Arm64FcvtzsS);
|
||||
}
|
||||
}
|
||||
else if (!roundWithFpscr && Optimizations.UseSse41)
|
||||
{
|
||||
EmitSse41ConvertInt32(context, FPRoundingMode.TowardsZero, !unsigned);
|
||||
}
|
||||
else
|
||||
{
|
||||
Operand toConvert = ExtractScalar(context, floatSize, op.Vm);
|
||||
|
||||
// TODO: Fast Path.
|
||||
if (roundWithFpscr)
|
||||
{
|
||||
toConvert = EmitRoundByRMode(context, toConvert);
|
||||
}
|
||||
|
||||
// Round towards zero.
|
||||
Operand asInteger = EmitSaturateFloatToInt(context, toConvert, unsigned);
|
||||
|
||||
InsertScalar(context, op.Vd, asInteger);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
bool unsigned = op.Opc == 0;
|
||||
|
||||
Operand toConvert = ExtractScalar(context, OperandType.I32, op.Vm);
|
||||
|
||||
Operand asFloat = EmitFPConvert(context, toConvert, floatSize, !unsigned);
|
||||
|
||||
InsertScalar(context, op.Vd, asFloat);
|
||||
}
|
||||
}
|
||||
|
||||
private static Operand EmitRoundMathCall(ArmEmitterContext context, MidpointRounding roundMode, Operand n)
|
||||
{
|
||||
IOpCode32Simd op = (IOpCode32Simd)context.CurrOp;
|
||||
|
||||
string name = nameof(Math.Round);
|
||||
|
||||
MethodInfo info = (op.Size & 1) == 0
|
||||
? typeof(MathF).GetMethod(name, new Type[] { typeof(float), typeof(MidpointRounding) })
|
||||
: typeof(Math). GetMethod(name, new Type[] { typeof(double), typeof(MidpointRounding) });
|
||||
|
||||
return context.Call(info, n, Const((int)roundMode));
|
||||
}
|
||||
|
||||
private static FPRoundingMode RMToRoundMode(int rm)
|
||||
{
|
||||
FPRoundingMode roundMode;
|
||||
switch (rm)
|
||||
{
|
||||
case 0b00:
|
||||
roundMode = FPRoundingMode.ToNearestAway;
|
||||
break;
|
||||
case 0b01:
|
||||
roundMode = FPRoundingMode.ToNearest;
|
||||
break;
|
||||
case 0b10:
|
||||
roundMode = FPRoundingMode.TowardsPlusInfinity;
|
||||
break;
|
||||
case 0b11:
|
||||
roundMode = FPRoundingMode.TowardsMinusInfinity;
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(rm));
|
||||
}
|
||||
return roundMode;
|
||||
}
|
||||
|
||||
// VCVTA/M/N/P (floating-point).
|
||||
public static void Vcvt_RM(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32SimdCvtFI op = (OpCode32SimdCvtFI)context.CurrOp; // toInteger == true (opCode<18> == 1 => Opc2<2> == 1).
|
||||
|
||||
OperandType floatSize = op.RegisterSize == RegisterSize.Int64 ? OperandType.FP64 : OperandType.FP32;
|
||||
|
||||
bool unsigned = op.Opc == 0;
|
||||
int rm = op.Opc2 & 3;
|
||||
|
||||
Intrinsic inst;
|
||||
|
||||
if (Optimizations.UseAdvSimd)
|
||||
{
|
||||
if (unsigned)
|
||||
{
|
||||
inst = rm switch {
|
||||
0b00 => Intrinsic.Arm64FcvtauS,
|
||||
0b01 => Intrinsic.Arm64FcvtnuS,
|
||||
0b10 => Intrinsic.Arm64FcvtpuS,
|
||||
0b11 => Intrinsic.Arm64FcvtmuS,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(rm))
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
inst = rm switch {
|
||||
0b00 => Intrinsic.Arm64FcvtasS,
|
||||
0b01 => Intrinsic.Arm64FcvtnsS,
|
||||
0b10 => Intrinsic.Arm64FcvtpsS,
|
||||
0b11 => Intrinsic.Arm64FcvtmsS,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(rm))
|
||||
};
|
||||
}
|
||||
|
||||
InstEmitSimdHelper32Arm64.EmitScalarUnaryOpF32(context, inst);
|
||||
}
|
||||
else if (Optimizations.UseSse41)
|
||||
{
|
||||
EmitSse41ConvertInt32(context, RMToRoundMode(rm), !unsigned);
|
||||
}
|
||||
else
|
||||
{
|
||||
Operand toConvert = ExtractScalar(context, floatSize, op.Vm);
|
||||
|
||||
switch (rm)
|
||||
{
|
||||
case 0b00: // Away
|
||||
toConvert = EmitRoundMathCall(context, MidpointRounding.AwayFromZero, toConvert);
|
||||
break;
|
||||
case 0b01: // Nearest
|
||||
toConvert = EmitRoundMathCall(context, MidpointRounding.ToEven, toConvert);
|
||||
break;
|
||||
case 0b10: // Towards positive infinity
|
||||
toConvert = EmitUnaryMathCall(context, nameof(Math.Ceiling), toConvert);
|
||||
break;
|
||||
case 0b11: // Towards negative infinity
|
||||
toConvert = EmitUnaryMathCall(context, nameof(Math.Floor), toConvert);
|
||||
break;
|
||||
}
|
||||
|
||||
Operand asInteger = EmitSaturateFloatToInt(context, toConvert, unsigned);
|
||||
|
||||
InsertScalar(context, op.Vd, asInteger);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Vcvt_TB(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32SimdCvtTB op = (OpCode32SimdCvtTB)context.CurrOp;
|
||||
|
||||
if (Optimizations.UseF16c)
|
||||
{
|
||||
Debug.Assert(!Optimizations.ForceLegacySse);
|
||||
|
||||
if (op.Op)
|
||||
{
|
||||
Operand res = ExtractScalar(context, op.Size == 1 ? OperandType.FP64 : OperandType.FP32, op.Vm);
|
||||
if (op.Size == 1)
|
||||
{
|
||||
res = context.AddIntrinsic(Intrinsic.X86Cvtsd2ss, context.VectorZero(), res);
|
||||
}
|
||||
res = context.AddIntrinsic(Intrinsic.X86Vcvtps2ph, res, Const(X86GetRoundControl(FPRoundingMode.ToNearest)));
|
||||
res = context.VectorExtract16(res, 0);
|
||||
InsertScalar16(context, op.Vd, op.T, res);
|
||||
}
|
||||
else
|
||||
{
|
||||
Operand res = context.VectorCreateScalar(ExtractScalar16(context, op.Vm, op.T));
|
||||
res = context.AddIntrinsic(Intrinsic.X86Vcvtph2ps, res);
|
||||
if (op.Size == 1)
|
||||
{
|
||||
res = context.AddIntrinsic(Intrinsic.X86Cvtss2sd, context.VectorZero(), res);
|
||||
}
|
||||
res = context.VectorExtract(op.Size == 1 ? OperandType.I64 : OperandType.I32, res, 0);
|
||||
InsertScalar(context, op.Vd, res);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (op.Op)
|
||||
{
|
||||
// Convert to half.
|
||||
|
||||
Operand src = ExtractScalar(context, op.Size == 1 ? OperandType.FP64 : OperandType.FP32, op.Vm);
|
||||
|
||||
MethodInfo method = op.Size == 1
|
||||
? typeof(SoftFloat64_16).GetMethod(nameof(SoftFloat64_16.FPConvert))
|
||||
: typeof(SoftFloat32_16).GetMethod(nameof(SoftFloat32_16.FPConvert));
|
||||
|
||||
context.ExitArmFpMode();
|
||||
context.StoreToContext();
|
||||
Operand res = context.Call(method, src);
|
||||
context.LoadFromContext();
|
||||
context.EnterArmFpMode();
|
||||
|
||||
InsertScalar16(context, op.Vd, op.T, res);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Convert from half.
|
||||
|
||||
Operand src = ExtractScalar16(context, op.Vm, op.T);
|
||||
|
||||
MethodInfo method = op.Size == 1
|
||||
? typeof(SoftFloat16_64).GetMethod(nameof(SoftFloat16_64.FPConvert))
|
||||
: typeof(SoftFloat16_32).GetMethod(nameof(SoftFloat16_32.FPConvert));
|
||||
|
||||
context.ExitArmFpMode();
|
||||
context.StoreToContext();
|
||||
Operand res = context.Call(method, src);
|
||||
context.LoadFromContext();
|
||||
context.EnterArmFpMode();
|
||||
|
||||
InsertScalar(context, op.Vd, res);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// VRINTA/M/N/P (floating-point).
|
||||
public static void Vrint_RM(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32SimdS op = (OpCode32SimdS)context.CurrOp;
|
||||
|
||||
OperandType floatSize = op.RegisterSize == RegisterSize.Int64 ? OperandType.FP64 : OperandType.FP32;
|
||||
|
||||
int rm = op.Opc2 & 3;
|
||||
|
||||
if (Optimizations.UseAdvSimd)
|
||||
{
|
||||
Intrinsic inst = rm switch {
|
||||
0b00 => Intrinsic.Arm64FrintaS,
|
||||
0b01 => Intrinsic.Arm64FrintnS,
|
||||
0b10 => Intrinsic.Arm64FrintpS,
|
||||
0b11 => Intrinsic.Arm64FrintmS,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(rm))
|
||||
};
|
||||
|
||||
InstEmitSimdHelper32Arm64.EmitScalarUnaryOpF32(context, inst);
|
||||
}
|
||||
else if (Optimizations.UseSse41)
|
||||
{
|
||||
EmitScalarUnaryOpSimd32(context, (m) =>
|
||||
{
|
||||
FPRoundingMode roundMode = RMToRoundMode(rm);
|
||||
|
||||
if (roundMode != FPRoundingMode.ToNearestAway)
|
||||
{
|
||||
Intrinsic inst = (op.Size & 1) == 0 ? Intrinsic.X86Roundss : Intrinsic.X86Roundsd;
|
||||
return context.AddIntrinsic(inst, m, Const(X86GetRoundControl(roundMode)));
|
||||
}
|
||||
else
|
||||
{
|
||||
return EmitSse41RoundToNearestWithTiesToAwayOpF(context, m, scalar: true);
|
||||
}
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
Operand toConvert = ExtractScalar(context, floatSize, op.Vm);
|
||||
|
||||
switch (rm)
|
||||
{
|
||||
case 0b00: // Away
|
||||
toConvert = EmitRoundMathCall(context, MidpointRounding.AwayFromZero, toConvert);
|
||||
break;
|
||||
case 0b01: // Nearest
|
||||
toConvert = EmitRoundMathCall(context, MidpointRounding.ToEven, toConvert);
|
||||
break;
|
||||
case 0b10: // Towards positive infinity
|
||||
toConvert = EmitUnaryMathCall(context, nameof(Math.Ceiling), toConvert);
|
||||
break;
|
||||
case 0b11: // Towards negative infinity
|
||||
toConvert = EmitUnaryMathCall(context, nameof(Math.Floor), toConvert);
|
||||
break;
|
||||
}
|
||||
|
||||
InsertScalar(context, op.Vd, toConvert);
|
||||
}
|
||||
}
|
||||
|
||||
// VRINTA (vector).
|
||||
public static void Vrinta_V(ArmEmitterContext context)
|
||||
{
|
||||
if (Optimizations.UseAdvSimd)
|
||||
{
|
||||
InstEmitSimdHelper32Arm64.EmitVectorUnaryOpF32(context, Intrinsic.Arm64FrintaS);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitVectorUnaryOpF32(context, (m) => EmitRoundMathCall(context, MidpointRounding.AwayFromZero, m));
|
||||
}
|
||||
}
|
||||
|
||||
// VRINTM (vector).
|
||||
public static void Vrintm_V(ArmEmitterContext context)
|
||||
{
|
||||
if (Optimizations.UseAdvSimd)
|
||||
{
|
||||
InstEmitSimdHelper32Arm64.EmitVectorUnaryOpF32(context, Intrinsic.Arm64FrintmS);
|
||||
}
|
||||
else if (Optimizations.UseSse2)
|
||||
{
|
||||
EmitVectorUnaryOpSimd32(context, (m) =>
|
||||
{
|
||||
return context.AddIntrinsic(Intrinsic.X86Roundps, m, Const(X86GetRoundControl(FPRoundingMode.TowardsMinusInfinity)));
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitVectorUnaryOpF32(context, (m) => EmitUnaryMathCall(context, nameof(Math.Floor), m));
|
||||
}
|
||||
}
|
||||
|
||||
// VRINTN (vector).
|
||||
public static void Vrintn_V(ArmEmitterContext context)
|
||||
{
|
||||
if (Optimizations.UseAdvSimd)
|
||||
{
|
||||
InstEmitSimdHelper32Arm64.EmitVectorUnaryOpF32(context, Intrinsic.Arm64FrintnS);
|
||||
}
|
||||
else if (Optimizations.UseSse2)
|
||||
{
|
||||
EmitVectorUnaryOpSimd32(context, (m) =>
|
||||
{
|
||||
return context.AddIntrinsic(Intrinsic.X86Roundps, m, Const(X86GetRoundControl(FPRoundingMode.ToNearest)));
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitVectorUnaryOpF32(context, (m) => EmitRoundMathCall(context, MidpointRounding.ToEven, m));
|
||||
}
|
||||
}
|
||||
|
||||
// VRINTP (vector).
|
||||
public static void Vrintp_V(ArmEmitterContext context)
|
||||
{
|
||||
if (Optimizations.UseAdvSimd)
|
||||
{
|
||||
InstEmitSimdHelper32Arm64.EmitVectorUnaryOpF32(context, Intrinsic.Arm64FrintpS);
|
||||
}
|
||||
else if (Optimizations.UseSse2)
|
||||
{
|
||||
EmitVectorUnaryOpSimd32(context, (m) =>
|
||||
{
|
||||
return context.AddIntrinsic(Intrinsic.X86Roundps, m, Const(X86GetRoundControl(FPRoundingMode.TowardsPlusInfinity)));
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitVectorUnaryOpF32(context, (m) => EmitUnaryMathCall(context, nameof(Math.Ceiling), m));
|
||||
}
|
||||
}
|
||||
|
||||
// VRINTZ (floating-point).
|
||||
public static void Vrint_Z(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32SimdS op = (OpCode32SimdS)context.CurrOp;
|
||||
|
||||
if (Optimizations.UseAdvSimd)
|
||||
{
|
||||
InstEmitSimdHelper32Arm64.EmitScalarUnaryOpF32(context, Intrinsic.Arm64FrintzS);
|
||||
}
|
||||
else if (Optimizations.UseSse2)
|
||||
{
|
||||
EmitScalarUnaryOpSimd32(context, (m) =>
|
||||
{
|
||||
Intrinsic inst = (op.Size & 1) == 0 ? Intrinsic.X86Roundss : Intrinsic.X86Roundsd;
|
||||
return context.AddIntrinsic(inst, m, Const(X86GetRoundControl(FPRoundingMode.TowardsZero)));
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitScalarUnaryOpF32(context, (op1) => EmitUnaryMathCall(context, nameof(Math.Truncate), op1));
|
||||
}
|
||||
}
|
||||
|
||||
// VRINTX (floating-point).
|
||||
public static void Vrintx_S(ArmEmitterContext context)
|
||||
{
|
||||
if (Optimizations.UseAdvSimd)
|
||||
{
|
||||
InstEmitSimdHelper32Arm64.EmitScalarUnaryOpF32(context, Intrinsic.Arm64FrintxS);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitScalarUnaryOpF32(context, (op1) =>
|
||||
{
|
||||
return EmitRoundByRMode(context, op1);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private static Operand EmitFPConvert(ArmEmitterContext context, Operand value, OperandType type, bool signed)
|
||||
{
|
||||
Debug.Assert(value.Type == OperandType.I32 || value.Type == OperandType.I64);
|
||||
|
||||
if (signed)
|
||||
{
|
||||
return context.ConvertToFP(type, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
return context.ConvertToFPUI(type, value);
|
||||
}
|
||||
}
|
||||
|
||||
private static void EmitSse41ConvertInt32(ArmEmitterContext context, FPRoundingMode roundMode, bool signed)
|
||||
{
|
||||
// A port of the similar round function in InstEmitSimdCvt.
|
||||
OpCode32SimdCvtFI op = (OpCode32SimdCvtFI)context.CurrOp;
|
||||
|
||||
bool doubleSize = (op.Size & 1) != 0;
|
||||
int shift = doubleSize ? 1 : 2;
|
||||
Operand n = GetVecA32(op.Vm >> shift);
|
||||
n = EmitSwapScalar(context, n, op.Vm, doubleSize);
|
||||
|
||||
if (!doubleSize)
|
||||
{
|
||||
Operand nRes = context.AddIntrinsic(Intrinsic.X86Cmpss, n, n, Const((int)CmpCondition.OrderedQ));
|
||||
nRes = context.AddIntrinsic(Intrinsic.X86Pand, nRes, n);
|
||||
|
||||
if (roundMode != FPRoundingMode.ToNearestAway)
|
||||
{
|
||||
nRes = context.AddIntrinsic(Intrinsic.X86Roundss, nRes, Const(X86GetRoundControl(roundMode)));
|
||||
}
|
||||
else
|
||||
{
|
||||
nRes = EmitSse41RoundToNearestWithTiesToAwayOpF(context, nRes, scalar: true);
|
||||
}
|
||||
|
||||
Operand zero = context.VectorZero();
|
||||
|
||||
Operand nCmp;
|
||||
Operand nIntOrLong2 = default;
|
||||
|
||||
if (!signed)
|
||||
{
|
||||
nCmp = context.AddIntrinsic(Intrinsic.X86Cmpss, nRes, zero, Const((int)CmpCondition.NotLessThanOrEqual));
|
||||
nRes = context.AddIntrinsic(Intrinsic.X86Pand, nRes, nCmp);
|
||||
}
|
||||
|
||||
int fpMaxVal = 0x4F000000; // 2.14748365E9f (2147483648)
|
||||
|
||||
Operand fpMaxValMask = X86GetScalar(context, fpMaxVal);
|
||||
|
||||
Operand nIntOrLong = context.AddIntrinsicInt(Intrinsic.X86Cvtss2si, nRes);
|
||||
|
||||
if (!signed)
|
||||
{
|
||||
nRes = context.AddIntrinsic(Intrinsic.X86Subss, nRes, fpMaxValMask);
|
||||
|
||||
nCmp = context.AddIntrinsic(Intrinsic.X86Cmpss, nRes, zero, Const((int)CmpCondition.NotLessThanOrEqual));
|
||||
nRes = context.AddIntrinsic(Intrinsic.X86Pand, nRes, nCmp);
|
||||
|
||||
nIntOrLong2 = context.AddIntrinsicInt(Intrinsic.X86Cvtss2si, nRes);
|
||||
}
|
||||
|
||||
nRes = context.AddIntrinsic(Intrinsic.X86Cmpss, nRes, fpMaxValMask, Const((int)CmpCondition.NotLessThan));
|
||||
|
||||
Operand nInt = context.AddIntrinsicInt(Intrinsic.X86Cvtsi2si, nRes);
|
||||
|
||||
Operand dRes;
|
||||
if (signed)
|
||||
{
|
||||
dRes = context.BitwiseExclusiveOr(nIntOrLong, nInt);
|
||||
}
|
||||
else
|
||||
{
|
||||
dRes = context.BitwiseExclusiveOr(nIntOrLong2, nInt);
|
||||
dRes = context.Add(dRes, nIntOrLong);
|
||||
}
|
||||
|
||||
InsertScalar(context, op.Vd, dRes);
|
||||
}
|
||||
else
|
||||
{
|
||||
Operand nRes = context.AddIntrinsic(Intrinsic.X86Cmpsd, n, n, Const((int)CmpCondition.OrderedQ));
|
||||
nRes = context.AddIntrinsic(Intrinsic.X86Pand, nRes, n);
|
||||
|
||||
if (roundMode != FPRoundingMode.ToNearestAway)
|
||||
{
|
||||
nRes = context.AddIntrinsic(Intrinsic.X86Roundsd, nRes, Const(X86GetRoundControl(roundMode)));
|
||||
}
|
||||
else
|
||||
{
|
||||
nRes = EmitSse41RoundToNearestWithTiesToAwayOpF(context, nRes, scalar: true);
|
||||
}
|
||||
|
||||
Operand zero = context.VectorZero();
|
||||
|
||||
Operand nCmp;
|
||||
Operand nIntOrLong2 = default;
|
||||
|
||||
if (!signed)
|
||||
{
|
||||
nCmp = context.AddIntrinsic(Intrinsic.X86Cmpsd, nRes, zero, Const((int)CmpCondition.NotLessThanOrEqual));
|
||||
nRes = context.AddIntrinsic(Intrinsic.X86Pand, nRes, nCmp);
|
||||
}
|
||||
|
||||
long fpMaxVal = 0x41E0000000000000L; // 2147483648.0000000d (2147483648)
|
||||
|
||||
Operand fpMaxValMask = X86GetScalar(context, fpMaxVal);
|
||||
|
||||
Operand nIntOrLong = context.AddIntrinsicInt(Intrinsic.X86Cvtsd2si, nRes);
|
||||
|
||||
if (!signed)
|
||||
{
|
||||
nRes = context.AddIntrinsic(Intrinsic.X86Subsd, nRes, fpMaxValMask);
|
||||
|
||||
nCmp = context.AddIntrinsic(Intrinsic.X86Cmpsd, nRes, zero, Const((int)CmpCondition.NotLessThanOrEqual));
|
||||
nRes = context.AddIntrinsic(Intrinsic.X86Pand, nRes, nCmp);
|
||||
|
||||
nIntOrLong2 = context.AddIntrinsicInt(Intrinsic.X86Cvtsd2si, nRes);
|
||||
}
|
||||
|
||||
nRes = context.AddIntrinsic(Intrinsic.X86Cmpsd, nRes, fpMaxValMask, Const((int)CmpCondition.NotLessThan));
|
||||
|
||||
Operand nLong = context.AddIntrinsicLong(Intrinsic.X86Cvtsi2si, nRes);
|
||||
nLong = context.ConvertI64ToI32(nLong);
|
||||
|
||||
Operand dRes;
|
||||
if (signed)
|
||||
{
|
||||
dRes = context.BitwiseExclusiveOr(nIntOrLong, nLong);
|
||||
}
|
||||
else
|
||||
{
|
||||
dRes = context.BitwiseExclusiveOr(nIntOrLong2, nLong);
|
||||
dRes = context.Add(dRes, nIntOrLong);
|
||||
}
|
||||
|
||||
InsertScalar(context, op.Vd, dRes);
|
||||
}
|
||||
}
|
||||
|
||||
private static void EmitSse41ConvertVector32(ArmEmitterContext context, FPRoundingMode roundMode, bool signed)
|
||||
{
|
||||
OpCode32Simd op = (OpCode32Simd)context.CurrOp;
|
||||
|
||||
EmitVectorUnaryOpSimd32(context, (n) =>
|
||||
{
|
||||
int sizeF = op.Size & 1;
|
||||
|
||||
if (sizeF == 0)
|
||||
{
|
||||
Operand nRes = context.AddIntrinsic(Intrinsic.X86Cmpps, n, n, Const((int)CmpCondition.OrderedQ));
|
||||
nRes = context.AddIntrinsic(Intrinsic.X86Pand, nRes, n);
|
||||
|
||||
nRes = context.AddIntrinsic(Intrinsic.X86Roundps, nRes, Const(X86GetRoundControl(roundMode)));
|
||||
|
||||
Operand zero = context.VectorZero();
|
||||
Operand nCmp;
|
||||
if (!signed)
|
||||
{
|
||||
nCmp = context.AddIntrinsic(Intrinsic.X86Cmpps, nRes, zero, Const((int)CmpCondition.NotLessThanOrEqual));
|
||||
nRes = context.AddIntrinsic(Intrinsic.X86Pand, nRes, nCmp);
|
||||
}
|
||||
|
||||
Operand fpMaxValMask = X86GetAllElements(context, 0x4F000000); // 2.14748365E9f (2147483648)
|
||||
|
||||
Operand nInt = context.AddIntrinsic(Intrinsic.X86Cvtps2dq, nRes);
|
||||
Operand nInt2 = default;
|
||||
|
||||
if (!signed)
|
||||
{
|
||||
nRes = context.AddIntrinsic(Intrinsic.X86Subps, nRes, fpMaxValMask);
|
||||
|
||||
nCmp = context.AddIntrinsic(Intrinsic.X86Cmpps, nRes, zero, Const((int)CmpCondition.NotLessThanOrEqual));
|
||||
nRes = context.AddIntrinsic(Intrinsic.X86Pand, nRes, nCmp);
|
||||
|
||||
nInt2 = context.AddIntrinsic(Intrinsic.X86Cvtps2dq, nRes);
|
||||
}
|
||||
|
||||
nRes = context.AddIntrinsic(Intrinsic.X86Cmpps, nRes, fpMaxValMask, Const((int)CmpCondition.NotLessThan));
|
||||
|
||||
if (signed)
|
||||
{
|
||||
return context.AddIntrinsic(Intrinsic.X86Pxor, nInt, nRes);
|
||||
}
|
||||
else
|
||||
{
|
||||
Operand dRes = context.AddIntrinsic(Intrinsic.X86Pxor, nInt2, nRes);
|
||||
return context.AddIntrinsic(Intrinsic.X86Paddd, dRes, nInt);
|
||||
}
|
||||
}
|
||||
else /* if (sizeF == 1) */
|
||||
{
|
||||
Operand nRes = context.AddIntrinsic(Intrinsic.X86Cmppd, n, n, Const((int)CmpCondition.OrderedQ));
|
||||
nRes = context.AddIntrinsic(Intrinsic.X86Pand, nRes, n);
|
||||
|
||||
nRes = context.AddIntrinsic(Intrinsic.X86Roundpd, nRes, Const(X86GetRoundControl(roundMode)));
|
||||
|
||||
Operand zero = context.VectorZero();
|
||||
Operand nCmp;
|
||||
if (!signed)
|
||||
{
|
||||
nCmp = context.AddIntrinsic(Intrinsic.X86Cmppd, nRes, zero, Const((int)CmpCondition.NotLessThanOrEqual));
|
||||
nRes = context.AddIntrinsic(Intrinsic.X86Pand, nRes, nCmp);
|
||||
}
|
||||
|
||||
Operand fpMaxValMask = X86GetAllElements(context, 0x43E0000000000000L); // 9.2233720368547760E18d (9223372036854775808)
|
||||
|
||||
Operand nLong = InstEmit.EmitSse2CvtDoubleToInt64OpF(context, nRes, false);
|
||||
Operand nLong2 = default;
|
||||
|
||||
if (!signed)
|
||||
{
|
||||
nRes = context.AddIntrinsic(Intrinsic.X86Subpd, nRes, fpMaxValMask);
|
||||
|
||||
nCmp = context.AddIntrinsic(Intrinsic.X86Cmppd, nRes, zero, Const((int)CmpCondition.NotLessThanOrEqual));
|
||||
nRes = context.AddIntrinsic(Intrinsic.X86Pand, nRes, nCmp);
|
||||
|
||||
nLong2 = InstEmit.EmitSse2CvtDoubleToInt64OpF(context, nRes, false);
|
||||
}
|
||||
|
||||
nRes = context.AddIntrinsic(Intrinsic.X86Cmppd, nRes, fpMaxValMask, Const((int)CmpCondition.NotLessThan));
|
||||
|
||||
if (signed)
|
||||
{
|
||||
return context.AddIntrinsic(Intrinsic.X86Pxor, nLong, nRes);
|
||||
}
|
||||
else
|
||||
{
|
||||
Operand dRes = context.AddIntrinsic(Intrinsic.X86Pxor, nLong2, nRes);
|
||||
return context.AddIntrinsic(Intrinsic.X86Paddq, dRes, nLong);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
147
src/ARMeilleure/Instructions/InstEmitSimdHash.cs
Normal file
147
src/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(typeof(SoftFallback).GetMethod(nameof(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(typeof(SoftFallback).GetMethod(nameof(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(typeof(SoftFallback).GetMethod(nameof(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(typeof(SoftFallback).GetMethod(nameof(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(typeof(SoftFallback).GetMethod(nameof(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(typeof(SoftFallback).GetMethod(nameof(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 = InstEmitSimdHashHelper.EmitSha256h(context, d, n, m, part2: false);
|
||||
|
||||
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 = InstEmitSimdHashHelper.EmitSha256h(context, n, d, m, part2: true);
|
||||
|
||||
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 = InstEmitSimdHashHelper.EmitSha256su0(context, 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 = InstEmitSimdHashHelper.EmitSha256su1(context, d, n, m);
|
||||
|
||||
context.Copy(GetVec(op.Rd), res);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
64
src/ARMeilleure/Instructions/InstEmitSimdHash32.cs
Normal file
64
src/ARMeilleure/Instructions/InstEmitSimdHash32.cs
Normal file
@@ -0,0 +1,64 @@
|
||||
using ARMeilleure.Decoders;
|
||||
using ARMeilleure.IntermediateRepresentation;
|
||||
using ARMeilleure.Translation;
|
||||
|
||||
using static ARMeilleure.Instructions.InstEmitHelper;
|
||||
|
||||
namespace ARMeilleure.Instructions
|
||||
{
|
||||
static partial class InstEmit32
|
||||
{
|
||||
#region "Sha256"
|
||||
public static void Sha256h_V(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32SimdReg op = (OpCode32SimdReg)context.CurrOp;
|
||||
|
||||
Operand d = GetVecA32(op.Qd);
|
||||
Operand n = GetVecA32(op.Qn);
|
||||
Operand m = GetVecA32(op.Qm);
|
||||
|
||||
Operand res = InstEmitSimdHashHelper.EmitSha256h(context, d, n, m, part2: false);
|
||||
|
||||
context.Copy(GetVecA32(op.Qd), res);
|
||||
}
|
||||
|
||||
public static void Sha256h2_V(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32SimdReg op = (OpCode32SimdReg)context.CurrOp;
|
||||
|
||||
Operand d = GetVecA32(op.Qd);
|
||||
Operand n = GetVecA32(op.Qn);
|
||||
Operand m = GetVecA32(op.Qm);
|
||||
|
||||
Operand res = InstEmitSimdHashHelper.EmitSha256h(context, n, d, m, part2: true);
|
||||
|
||||
context.Copy(GetVecA32(op.Qd), res);
|
||||
}
|
||||
|
||||
public static void Sha256su0_V(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32Simd op = (OpCode32Simd)context.CurrOp;
|
||||
|
||||
Operand d = GetVecA32(op.Qd);
|
||||
Operand m = GetVecA32(op.Qm);
|
||||
|
||||
Operand res = InstEmitSimdHashHelper.EmitSha256su0(context, d, m);
|
||||
|
||||
context.Copy(GetVecA32(op.Qd), res);
|
||||
}
|
||||
|
||||
public static void Sha256su1_V(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32SimdReg op = (OpCode32SimdReg)context.CurrOp;
|
||||
|
||||
Operand d = GetVecA32(op.Qd);
|
||||
Operand n = GetVecA32(op.Qn);
|
||||
Operand m = GetVecA32(op.Qm);
|
||||
|
||||
Operand res = InstEmitSimdHashHelper.EmitSha256su1(context, d, n, m);
|
||||
|
||||
context.Copy(GetVecA32(op.Qd), res);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
56
src/ARMeilleure/Instructions/InstEmitSimdHashHelper.cs
Normal file
56
src/ARMeilleure/Instructions/InstEmitSimdHashHelper.cs
Normal file
@@ -0,0 +1,56 @@
|
||||
using ARMeilleure.IntermediateRepresentation;
|
||||
using ARMeilleure.Translation;
|
||||
using System;
|
||||
|
||||
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
|
||||
|
||||
namespace ARMeilleure.Instructions
|
||||
{
|
||||
static class InstEmitSimdHashHelper
|
||||
{
|
||||
public static Operand EmitSha256h(ArmEmitterContext context, Operand x, Operand y, Operand w, bool part2)
|
||||
{
|
||||
if (Optimizations.UseSha)
|
||||
{
|
||||
Operand src1 = context.AddIntrinsic(Intrinsic.X86Shufps, y, x, Const(0xbb));
|
||||
Operand src2 = context.AddIntrinsic(Intrinsic.X86Shufps, y, x, Const(0x11));
|
||||
Operand w2 = context.AddIntrinsic(Intrinsic.X86Punpckhqdq, w, w);
|
||||
|
||||
Operand round2 = context.AddIntrinsic(Intrinsic.X86Sha256Rnds2, src1, src2, w);
|
||||
Operand round4 = context.AddIntrinsic(Intrinsic.X86Sha256Rnds2, src2, round2, w2);
|
||||
|
||||
Operand res = context.AddIntrinsic(Intrinsic.X86Shufps, round4, round2, Const(part2 ? 0x11 : 0xbb));
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
String method = part2 ? nameof(SoftFallback.HashUpper) : nameof(SoftFallback.HashLower);
|
||||
return context.Call(typeof(SoftFallback).GetMethod(method), x, y, w);
|
||||
}
|
||||
|
||||
public static Operand EmitSha256su0(ArmEmitterContext context, Operand x, Operand y)
|
||||
{
|
||||
if (Optimizations.UseSha)
|
||||
{
|
||||
return context.AddIntrinsic(Intrinsic.X86Sha256Msg1, x, y);
|
||||
}
|
||||
|
||||
return context.Call(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Sha256SchedulePart1)), x, y);
|
||||
}
|
||||
|
||||
public static Operand EmitSha256su1(ArmEmitterContext context, Operand x, Operand y, Operand z)
|
||||
{
|
||||
if (Optimizations.UseSha && Optimizations.UseSsse3)
|
||||
{
|
||||
Operand extr = context.AddIntrinsic(Intrinsic.X86Palignr, z, y, Const(4));
|
||||
Operand tmp = context.AddIntrinsic(Intrinsic.X86Paddd, extr, x);
|
||||
|
||||
Operand res = context.AddIntrinsic(Intrinsic.X86Sha256Msg2, tmp, z);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
return context.Call(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Sha256SchedulePart2)), x, y, z);
|
||||
}
|
||||
}
|
||||
}
|
||||
2088
src/ARMeilleure/Instructions/InstEmitSimdHelper.cs
Normal file
2088
src/ARMeilleure/Instructions/InstEmitSimdHelper.cs
Normal file
File diff suppressed because it is too large
Load Diff
1286
src/ARMeilleure/Instructions/InstEmitSimdHelper32.cs
Normal file
1286
src/ARMeilleure/Instructions/InstEmitSimdHelper32.cs
Normal file
File diff suppressed because it is too large
Load Diff
366
src/ARMeilleure/Instructions/InstEmitSimdHelper32Arm64.cs
Normal file
366
src/ARMeilleure/Instructions/InstEmitSimdHelper32Arm64.cs
Normal file
@@ -0,0 +1,366 @@
|
||||
|
||||
using ARMeilleure.Decoders;
|
||||
using ARMeilleure.IntermediateRepresentation;
|
||||
using ARMeilleure.State;
|
||||
using ARMeilleure.Translation;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
|
||||
using static ARMeilleure.Instructions.InstEmitHelper;
|
||||
using static ARMeilleure.Instructions.InstEmitSimdHelper;
|
||||
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
|
||||
|
||||
namespace ARMeilleure.Instructions
|
||||
{
|
||||
using Func1I = Func<Operand, Operand>;
|
||||
using Func2I = Func<Operand, Operand, Operand>;
|
||||
using Func3I = Func<Operand, Operand, Operand, Operand>;
|
||||
|
||||
static class InstEmitSimdHelper32Arm64
|
||||
{
|
||||
// Intrinsic Helpers
|
||||
|
||||
public static Operand EmitMoveDoubleWordToSide(ArmEmitterContext context, Operand input, int originalV, int targetV)
|
||||
{
|
||||
Debug.Assert(input.Type == OperandType.V128);
|
||||
|
||||
int originalSide = originalV & 1;
|
||||
int targetSide = targetV & 1;
|
||||
|
||||
if (originalSide == targetSide)
|
||||
{
|
||||
return input;
|
||||
}
|
||||
|
||||
Intrinsic vType = Intrinsic.Arm64VDWord | Intrinsic.Arm64V128;
|
||||
|
||||
if (targetSide == 1)
|
||||
{
|
||||
return context.AddIntrinsic(Intrinsic.Arm64DupVe | vType, input, Const(OperandType.I32, 0)); // Low to high.
|
||||
}
|
||||
else
|
||||
{
|
||||
return context.AddIntrinsic(Intrinsic.Arm64DupVe | vType, input, Const(OperandType.I32, 1)); // High to low.
|
||||
}
|
||||
}
|
||||
|
||||
public static Operand EmitDoubleWordInsert(ArmEmitterContext context, Operand target, Operand value, int targetV)
|
||||
{
|
||||
Debug.Assert(target.Type == OperandType.V128 && value.Type == OperandType.V128);
|
||||
|
||||
int targetSide = targetV & 1;
|
||||
Operand idx = Const(targetSide);
|
||||
|
||||
return context.AddIntrinsic(Intrinsic.Arm64InsVe | Intrinsic.Arm64VDWord, target, idx, value, idx);
|
||||
}
|
||||
|
||||
public static Operand EmitScalarInsert(ArmEmitterContext context, Operand target, Operand value, int reg, bool doubleWidth)
|
||||
{
|
||||
Debug.Assert(target.Type == OperandType.V128 && value.Type == OperandType.V128);
|
||||
|
||||
// Insert from index 0 in value to index in target.
|
||||
int index = reg & (doubleWidth ? 1 : 3);
|
||||
|
||||
if (doubleWidth)
|
||||
{
|
||||
return context.AddIntrinsic(Intrinsic.Arm64InsVe | Intrinsic.Arm64VDWord, target, Const(index), value, Const(0));
|
||||
}
|
||||
else
|
||||
{
|
||||
return context.AddIntrinsic(Intrinsic.Arm64InsVe | Intrinsic.Arm64VWord, target, Const(index), value, Const(0));
|
||||
}
|
||||
}
|
||||
|
||||
public static Operand EmitExtractScalar(ArmEmitterContext context, Operand target, int reg, bool doubleWidth)
|
||||
{
|
||||
int index = reg & (doubleWidth ? 1 : 3);
|
||||
if (index == 0) return target; // Element is already at index 0, so just return the vector directly.
|
||||
|
||||
if (doubleWidth)
|
||||
{
|
||||
return context.AddIntrinsic(Intrinsic.Arm64DupSe | Intrinsic.Arm64VDWord, target, Const(1)); // Extract high (index 1).
|
||||
}
|
||||
else
|
||||
{
|
||||
return context.AddIntrinsic(Intrinsic.Arm64DupSe | Intrinsic.Arm64VWord, target, Const(index)); // Extract element at index.
|
||||
}
|
||||
}
|
||||
|
||||
// Vector Operand Templates
|
||||
|
||||
public static void EmitVectorUnaryOpSimd32(ArmEmitterContext context, Func1I vectorFunc)
|
||||
{
|
||||
OpCode32Simd op = (OpCode32Simd)context.CurrOp;
|
||||
|
||||
Operand m = GetVecA32(op.Qm);
|
||||
Operand d = GetVecA32(op.Qd);
|
||||
|
||||
if (!op.Q) // Register swap: move relevant doubleword to destination side.
|
||||
{
|
||||
m = EmitMoveDoubleWordToSide(context, m, op.Vm, op.Vd);
|
||||
}
|
||||
|
||||
Operand res = vectorFunc(m);
|
||||
|
||||
if (!op.Q) // Register insert.
|
||||
{
|
||||
res = EmitDoubleWordInsert(context, d, res, op.Vd);
|
||||
}
|
||||
|
||||
context.Copy(d, res);
|
||||
}
|
||||
|
||||
public static void EmitVectorUnaryOpF32(ArmEmitterContext context, Intrinsic inst)
|
||||
{
|
||||
OpCode32Simd op = (OpCode32Simd)context.CurrOp;
|
||||
|
||||
inst |= ((op.Size & 1) != 0 ? Intrinsic.Arm64VDouble : Intrinsic.Arm64VFloat) | Intrinsic.Arm64V128;
|
||||
EmitVectorUnaryOpSimd32(context, (m) => context.AddIntrinsic(inst, m));
|
||||
}
|
||||
|
||||
public static void EmitVectorBinaryOpSimd32(ArmEmitterContext context, Func2I vectorFunc, int side = -1)
|
||||
{
|
||||
OpCode32SimdReg op = (OpCode32SimdReg)context.CurrOp;
|
||||
|
||||
Operand n = GetVecA32(op.Qn);
|
||||
Operand m = GetVecA32(op.Qm);
|
||||
Operand d = GetVecA32(op.Qd);
|
||||
|
||||
if (side == -1)
|
||||
{
|
||||
side = op.Vd;
|
||||
}
|
||||
|
||||
if (!op.Q) // Register swap: move relevant doubleword to destination side.
|
||||
{
|
||||
n = EmitMoveDoubleWordToSide(context, n, op.Vn, side);
|
||||
m = EmitMoveDoubleWordToSide(context, m, op.Vm, side);
|
||||
}
|
||||
|
||||
Operand res = vectorFunc(n, m);
|
||||
|
||||
if (!op.Q) // Register insert.
|
||||
{
|
||||
if (side != op.Vd)
|
||||
{
|
||||
res = EmitMoveDoubleWordToSide(context, res, side, op.Vd);
|
||||
}
|
||||
res = EmitDoubleWordInsert(context, d, res, op.Vd);
|
||||
}
|
||||
|
||||
context.Copy(d, res);
|
||||
}
|
||||
|
||||
public static void EmitVectorBinaryOpF32(ArmEmitterContext context, Intrinsic inst)
|
||||
{
|
||||
OpCode32SimdReg op = (OpCode32SimdReg)context.CurrOp;
|
||||
|
||||
inst |= ((op.Size & 1) != 0 ? Intrinsic.Arm64VDouble : Intrinsic.Arm64VFloat) | Intrinsic.Arm64V128;
|
||||
EmitVectorBinaryOpSimd32(context, (n, m) => context.AddIntrinsic(inst, n, m));
|
||||
}
|
||||
|
||||
public static void EmitVectorTernaryOpSimd32(ArmEmitterContext context, Func3I vectorFunc)
|
||||
{
|
||||
OpCode32SimdReg op = (OpCode32SimdReg)context.CurrOp;
|
||||
|
||||
Operand n = GetVecA32(op.Qn);
|
||||
Operand m = GetVecA32(op.Qm);
|
||||
Operand d = GetVecA32(op.Qd);
|
||||
Operand initialD = d;
|
||||
|
||||
if (!op.Q) // Register swap: move relevant doubleword to destination side.
|
||||
{
|
||||
n = EmitMoveDoubleWordToSide(context, n, op.Vn, op.Vd);
|
||||
m = EmitMoveDoubleWordToSide(context, m, op.Vm, op.Vd);
|
||||
}
|
||||
|
||||
Operand res = vectorFunc(d, n, m);
|
||||
|
||||
if (!op.Q) // Register insert.
|
||||
{
|
||||
res = EmitDoubleWordInsert(context, initialD, res, op.Vd);
|
||||
}
|
||||
|
||||
context.Copy(initialD, res);
|
||||
}
|
||||
|
||||
public static void EmitVectorTernaryOpF32(ArmEmitterContext context, Intrinsic inst)
|
||||
{
|
||||
OpCode32SimdReg op = (OpCode32SimdReg)context.CurrOp;
|
||||
|
||||
inst |= ((op.Size & 1) != 0 ? Intrinsic.Arm64VDouble : Intrinsic.Arm64VFloat) | Intrinsic.Arm64V128;
|
||||
EmitVectorTernaryOpSimd32(context, (d, n, m) => context.AddIntrinsic(inst, d, n, m));
|
||||
}
|
||||
|
||||
public static void EmitScalarUnaryOpSimd32(ArmEmitterContext context, Func1I scalarFunc)
|
||||
{
|
||||
OpCode32SimdS op = (OpCode32SimdS)context.CurrOp;
|
||||
|
||||
bool doubleSize = (op.Size & 1) != 0;
|
||||
int shift = doubleSize ? 1 : 2;
|
||||
Operand m = GetVecA32(op.Vm >> shift);
|
||||
Operand d = GetVecA32(op.Vd >> shift);
|
||||
|
||||
m = EmitExtractScalar(context, m, op.Vm, doubleSize);
|
||||
|
||||
Operand res = scalarFunc(m);
|
||||
|
||||
// Insert scalar into vector.
|
||||
res = EmitScalarInsert(context, d, res, op.Vd, doubleSize);
|
||||
|
||||
context.Copy(d, res);
|
||||
}
|
||||
|
||||
public static void EmitScalarUnaryOpF32(ArmEmitterContext context, Intrinsic inst)
|
||||
{
|
||||
OpCode32SimdS op = (OpCode32SimdS)context.CurrOp;
|
||||
|
||||
inst |= ((op.Size & 1) != 0 ? Intrinsic.Arm64VDouble : Intrinsic.Arm64VFloat) | Intrinsic.Arm64V128;
|
||||
EmitScalarUnaryOpSimd32(context, (m) => (inst == 0) ? m : context.AddIntrinsic(inst, m));
|
||||
}
|
||||
|
||||
public static void EmitScalarBinaryOpSimd32(ArmEmitterContext context, Func2I scalarFunc)
|
||||
{
|
||||
OpCode32SimdRegS op = (OpCode32SimdRegS)context.CurrOp;
|
||||
|
||||
bool doubleSize = (op.Size & 1) != 0;
|
||||
int shift = doubleSize ? 1 : 2;
|
||||
Operand n = GetVecA32(op.Vn >> shift);
|
||||
Operand m = GetVecA32(op.Vm >> shift);
|
||||
Operand d = GetVecA32(op.Vd >> shift);
|
||||
|
||||
n = EmitExtractScalar(context, n, op.Vn, doubleSize);
|
||||
m = EmitExtractScalar(context, m, op.Vm, doubleSize);
|
||||
|
||||
Operand res = scalarFunc(n, m);
|
||||
|
||||
// Insert scalar into vector.
|
||||
res = EmitScalarInsert(context, d, res, op.Vd, doubleSize);
|
||||
|
||||
context.Copy(d, res);
|
||||
}
|
||||
|
||||
public static void EmitScalarBinaryOpF32(ArmEmitterContext context, Intrinsic inst)
|
||||
{
|
||||
OpCode32SimdRegS op = (OpCode32SimdRegS)context.CurrOp;
|
||||
|
||||
inst |= ((op.Size & 1) != 0 ? Intrinsic.Arm64VDouble : Intrinsic.Arm64VFloat) | Intrinsic.Arm64V128;
|
||||
EmitScalarBinaryOpSimd32(context, (n, m) => context.AddIntrinsic(inst, n, m));
|
||||
}
|
||||
|
||||
public static void EmitScalarTernaryOpSimd32(ArmEmitterContext context, Func3I scalarFunc)
|
||||
{
|
||||
OpCode32SimdRegS op = (OpCode32SimdRegS)context.CurrOp;
|
||||
|
||||
bool doubleSize = (op.Size & 1) != 0;
|
||||
int shift = doubleSize ? 1 : 2;
|
||||
Operand n = GetVecA32(op.Vn >> shift);
|
||||
Operand m = GetVecA32(op.Vm >> shift);
|
||||
Operand d = GetVecA32(op.Vd >> shift);
|
||||
Operand initialD = d;
|
||||
|
||||
n = EmitExtractScalar(context, n, op.Vn, doubleSize);
|
||||
m = EmitExtractScalar(context, m, op.Vm, doubleSize);
|
||||
d = EmitExtractScalar(context, d, op.Vd, doubleSize);
|
||||
|
||||
Operand res = scalarFunc(d, n, m);
|
||||
|
||||
// Insert scalar into vector.
|
||||
res = EmitScalarInsert(context, initialD, res, op.Vd, doubleSize);
|
||||
|
||||
context.Copy(initialD, res);
|
||||
}
|
||||
|
||||
public static void EmitScalarTernaryOpF32(ArmEmitterContext context, Intrinsic inst)
|
||||
{
|
||||
OpCode32SimdRegS op = (OpCode32SimdRegS)context.CurrOp;
|
||||
|
||||
inst |= ((op.Size & 1) != 0 ? Intrinsic.Arm64VDouble : Intrinsic.Arm64VFloat) | Intrinsic.Arm64V128;
|
||||
EmitScalarTernaryOpSimd32(context, (d, n, m) => context.AddIntrinsic(inst, d, n, m));
|
||||
}
|
||||
|
||||
// Pairwise
|
||||
|
||||
public static void EmitVectorPairwiseOpF32(ArmEmitterContext context, Intrinsic inst32)
|
||||
{
|
||||
OpCode32SimdReg op = (OpCode32SimdReg)context.CurrOp;
|
||||
|
||||
inst32 |= Intrinsic.Arm64V64 | Intrinsic.Arm64VFloat;
|
||||
EmitVectorBinaryOpSimd32(context, (n, m) => context.AddIntrinsic(inst32, n, m), 0);
|
||||
}
|
||||
|
||||
public static void EmitVcmpOrVcmpe(ArmEmitterContext context, bool signalNaNs)
|
||||
{
|
||||
OpCode32SimdS op = (OpCode32SimdS)context.CurrOp;
|
||||
|
||||
bool cmpWithZero = (op.Opc & 2) != 0;
|
||||
|
||||
Intrinsic inst = signalNaNs ? Intrinsic.Arm64FcmpeS : Intrinsic.Arm64FcmpS;
|
||||
inst |= ((op.Size & 1) != 0 ? Intrinsic.Arm64VDouble : Intrinsic.Arm64VFloat) | Intrinsic.Arm64V128;
|
||||
|
||||
bool doubleSize = (op.Size & 1) != 0;
|
||||
int shift = doubleSize ? 1 : 2;
|
||||
Operand n = GetVecA32(op.Vd >> shift);
|
||||
Operand m = GetVecA32(op.Vm >> shift);
|
||||
|
||||
n = EmitExtractScalar(context, n, op.Vd, doubleSize);
|
||||
m = cmpWithZero ? Const(0) : EmitExtractScalar(context, m, op.Vm, doubleSize);
|
||||
|
||||
Operand nzcv = context.AddIntrinsicInt(inst, n, m);
|
||||
|
||||
Operand one = Const(1);
|
||||
|
||||
SetFpFlag(context, FPState.VFlag, context.BitwiseAnd(context.ShiftRightUI(nzcv, Const(28)), one));
|
||||
SetFpFlag(context, FPState.CFlag, context.BitwiseAnd(context.ShiftRightUI(nzcv, Const(29)), one));
|
||||
SetFpFlag(context, FPState.ZFlag, context.BitwiseAnd(context.ShiftRightUI(nzcv, Const(30)), one));
|
||||
SetFpFlag(context, FPState.NFlag, context.BitwiseAnd(context.ShiftRightUI(nzcv, Const(31)), one));
|
||||
}
|
||||
|
||||
public static void EmitCmpOpF32(ArmEmitterContext context, CmpCondition cond, bool zero)
|
||||
{
|
||||
OpCode32Simd op = (OpCode32Simd)context.CurrOp;
|
||||
|
||||
int sizeF = op.Size & 1;
|
||||
|
||||
Intrinsic inst;
|
||||
if (zero)
|
||||
{
|
||||
inst = cond switch
|
||||
{
|
||||
CmpCondition.Equal => Intrinsic.Arm64FcmeqVz,
|
||||
CmpCondition.GreaterThan => Intrinsic.Arm64FcmgtVz,
|
||||
CmpCondition.GreaterThanOrEqual => Intrinsic.Arm64FcmgeVz,
|
||||
CmpCondition.LessThan => Intrinsic.Arm64FcmltVz,
|
||||
CmpCondition.LessThanOrEqual => Intrinsic.Arm64FcmleVz,
|
||||
_ => throw new InvalidOperationException()
|
||||
};
|
||||
}
|
||||
else {
|
||||
inst = cond switch
|
||||
{
|
||||
CmpCondition.Equal => Intrinsic.Arm64FcmeqV,
|
||||
CmpCondition.GreaterThan => Intrinsic.Arm64FcmgtV,
|
||||
CmpCondition.GreaterThanOrEqual => Intrinsic.Arm64FcmgeV,
|
||||
_ => throw new InvalidOperationException()
|
||||
};
|
||||
}
|
||||
|
||||
inst |= (sizeF != 0 ? Intrinsic.Arm64VDouble : Intrinsic.Arm64VFloat) | Intrinsic.Arm64V128;
|
||||
|
||||
if (zero)
|
||||
{
|
||||
EmitVectorUnaryOpSimd32(context, (m) =>
|
||||
{
|
||||
return context.AddIntrinsic(inst, m);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitVectorBinaryOpSimd32(context, (n, m) =>
|
||||
{
|
||||
return context.AddIntrinsic(inst, n, m);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
720
src/ARMeilleure/Instructions/InstEmitSimdHelperArm64.cs
Normal file
720
src/ARMeilleure/Instructions/InstEmitSimdHelperArm64.cs
Normal file
@@ -0,0 +1,720 @@
|
||||
using ARMeilleure.Decoders;
|
||||
using ARMeilleure.IntermediateRepresentation;
|
||||
using ARMeilleure.State;
|
||||
using ARMeilleure.Translation;
|
||||
|
||||
using static ARMeilleure.Instructions.InstEmitHelper;
|
||||
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
|
||||
|
||||
namespace ARMeilleure.Instructions
|
||||
{
|
||||
static class InstEmitSimdHelperArm64
|
||||
{
|
||||
public static void EmitScalarUnaryOpF(ArmEmitterContext context, Intrinsic inst)
|
||||
{
|
||||
OpCodeSimd op = (OpCodeSimd)context.CurrOp;
|
||||
|
||||
Operand n = GetVec(op.Rn);
|
||||
|
||||
if ((op.Size & 1) != 0)
|
||||
{
|
||||
inst |= Intrinsic.Arm64VDouble;
|
||||
}
|
||||
|
||||
context.Copy(GetVec(op.Rd), context.AddIntrinsic(inst, n));
|
||||
}
|
||||
|
||||
public static void EmitScalarUnaryOpFFromGp(ArmEmitterContext context, Intrinsic inst)
|
||||
{
|
||||
OpCodeSimdCvt op = (OpCodeSimdCvt)context.CurrOp;
|
||||
|
||||
Operand n = GetIntOrZR(context, op.Rn);
|
||||
|
||||
if ((op.Size & 1) != 0)
|
||||
{
|
||||
inst |= Intrinsic.Arm64VDouble;
|
||||
}
|
||||
|
||||
context.Copy(GetVec(op.Rd), context.AddIntrinsic(inst, n));
|
||||
}
|
||||
|
||||
public static void EmitScalarUnaryOpFToGp(ArmEmitterContext context, Intrinsic inst)
|
||||
{
|
||||
OpCodeSimdCvt op = (OpCodeSimdCvt)context.CurrOp;
|
||||
|
||||
Operand n = GetVec(op.Rn);
|
||||
|
||||
if ((op.Size & 1) != 0)
|
||||
{
|
||||
inst |= Intrinsic.Arm64VDouble;
|
||||
}
|
||||
|
||||
SetIntOrZR(context, op.Rd, op.RegisterSize == RegisterSize.Int32
|
||||
? context.AddIntrinsicInt (inst, n)
|
||||
: context.AddIntrinsicLong(inst, n));
|
||||
}
|
||||
|
||||
public static void EmitScalarBinaryOpF(ArmEmitterContext context, Intrinsic inst)
|
||||
{
|
||||
OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp;
|
||||
|
||||
Operand n = GetVec(op.Rn);
|
||||
Operand m = GetVec(op.Rm);
|
||||
|
||||
if ((op.Size & 1) != 0)
|
||||
{
|
||||
inst |= Intrinsic.Arm64VDouble;
|
||||
}
|
||||
|
||||
context.Copy(GetVec(op.Rd), context.AddIntrinsic(inst, n, m));
|
||||
}
|
||||
|
||||
public static void EmitScalarBinaryOpFByElem(ArmEmitterContext context, Intrinsic inst)
|
||||
{
|
||||
OpCodeSimdRegElemF op = (OpCodeSimdRegElemF)context.CurrOp;
|
||||
|
||||
Operand n = GetVec(op.Rn);
|
||||
Operand m = GetVec(op.Rm);
|
||||
|
||||
if ((op.Size & 1) != 0)
|
||||
{
|
||||
inst |= Intrinsic.Arm64VDouble;
|
||||
}
|
||||
|
||||
context.Copy(GetVec(op.Rd), context.AddIntrinsic(inst, n, m, Const(op.Index)));
|
||||
}
|
||||
|
||||
public static void EmitScalarTernaryOpF(ArmEmitterContext context, Intrinsic inst)
|
||||
{
|
||||
OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp;
|
||||
|
||||
Operand n = GetVec(op.Rn);
|
||||
Operand m = GetVec(op.Rm);
|
||||
Operand a = GetVec(op.Ra);
|
||||
|
||||
if ((op.Size & 1) != 0)
|
||||
{
|
||||
inst |= Intrinsic.Arm64VDouble;
|
||||
}
|
||||
|
||||
context.Copy(GetVec(op.Rd), context.AddIntrinsic(inst, a, n, m));
|
||||
}
|
||||
|
||||
public static void EmitScalarTernaryOpFRdByElem(ArmEmitterContext context, Intrinsic inst)
|
||||
{
|
||||
OpCodeSimdRegElemF op = (OpCodeSimdRegElemF)context.CurrOp;
|
||||
|
||||
Operand d = GetVec(op.Rd);
|
||||
Operand n = GetVec(op.Rn);
|
||||
Operand m = GetVec(op.Rm);
|
||||
|
||||
if ((op.Size & 1) != 0)
|
||||
{
|
||||
inst |= Intrinsic.Arm64VDouble;
|
||||
}
|
||||
|
||||
context.Copy(d, context.AddIntrinsic(inst, d, n, m, Const(op.Index)));
|
||||
}
|
||||
|
||||
public static void EmitScalarUnaryOp(ArmEmitterContext context, Intrinsic inst)
|
||||
{
|
||||
OpCodeSimd op = (OpCodeSimd)context.CurrOp;
|
||||
|
||||
Operand n = GetVec(op.Rn);
|
||||
|
||||
inst |= (Intrinsic)(op.Size << (int)Intrinsic.Arm64VSizeShift);
|
||||
|
||||
context.Copy(GetVec(op.Rd), context.AddIntrinsic(inst, n));
|
||||
}
|
||||
|
||||
public static void EmitScalarBinaryOp(ArmEmitterContext context, Intrinsic inst)
|
||||
{
|
||||
OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp;
|
||||
|
||||
Operand n = GetVec(op.Rn);
|
||||
Operand m = GetVec(op.Rm);
|
||||
|
||||
inst |= (Intrinsic)(op.Size << (int)Intrinsic.Arm64VSizeShift);
|
||||
|
||||
context.Copy(GetVec(op.Rd), context.AddIntrinsic(inst, n, m));
|
||||
}
|
||||
|
||||
public static void EmitScalarBinaryOpRd(ArmEmitterContext context, Intrinsic inst)
|
||||
{
|
||||
OpCodeSimd op = (OpCodeSimd)context.CurrOp;
|
||||
|
||||
Operand d = GetVec(op.Rd);
|
||||
Operand n = GetVec(op.Rn);
|
||||
|
||||
inst |= (Intrinsic)(op.Size << (int)Intrinsic.Arm64VSizeShift);
|
||||
|
||||
context.Copy(GetVec(op.Rd), context.AddIntrinsic(inst, d, n));
|
||||
}
|
||||
|
||||
public static void EmitScalarTernaryOpRd(ArmEmitterContext context, Intrinsic inst)
|
||||
{
|
||||
OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp;
|
||||
|
||||
Operand d = GetVec(op.Rd);
|
||||
Operand n = GetVec(op.Rn);
|
||||
Operand m = GetVec(op.Rm);
|
||||
|
||||
inst |= (Intrinsic)(op.Size << (int)Intrinsic.Arm64VSizeShift);
|
||||
|
||||
context.Copy(d, context.AddIntrinsic(inst, d, n, m));
|
||||
}
|
||||
|
||||
public static void EmitScalarShiftBinaryOp(ArmEmitterContext context, Intrinsic inst, int shift)
|
||||
{
|
||||
OpCodeSimd op = (OpCodeSimd)context.CurrOp;
|
||||
|
||||
Operand n = GetVec(op.Rn);
|
||||
|
||||
inst |= (Intrinsic)(op.Size << (int)Intrinsic.Arm64VSizeShift);
|
||||
|
||||
context.Copy(GetVec(op.Rd), context.AddIntrinsic(inst, n, Const(shift)));
|
||||
}
|
||||
|
||||
public static void EmitScalarShiftTernaryOpRd(ArmEmitterContext context, Intrinsic inst, int shift)
|
||||
{
|
||||
OpCodeSimd op = (OpCodeSimd)context.CurrOp;
|
||||
|
||||
Operand d = GetVec(op.Rd);
|
||||
Operand n = GetVec(op.Rn);
|
||||
|
||||
inst |= (Intrinsic)(op.Size << (int)Intrinsic.Arm64VSizeShift);
|
||||
|
||||
context.Copy(GetVec(op.Rd), context.AddIntrinsic(inst, d, n, Const(shift)));
|
||||
}
|
||||
|
||||
public static void EmitScalarSaturatingShiftTernaryOpRd(ArmEmitterContext context, Intrinsic inst, int shift)
|
||||
{
|
||||
OpCodeSimd op = (OpCodeSimd)context.CurrOp;
|
||||
|
||||
Operand d = GetVec(op.Rd);
|
||||
Operand n = GetVec(op.Rn);
|
||||
|
||||
inst |= (Intrinsic)(op.Size << (int)Intrinsic.Arm64VSizeShift);
|
||||
|
||||
context.Copy(GetVec(op.Rd), context.AddIntrinsic(inst, d, n, Const(shift)));
|
||||
|
||||
context.SetPendingQcFlagSync();
|
||||
}
|
||||
|
||||
public static void EmitScalarSaturatingUnaryOp(ArmEmitterContext context, Intrinsic inst)
|
||||
{
|
||||
OpCodeSimd op = (OpCodeSimd)context.CurrOp;
|
||||
|
||||
Operand n = GetVec(op.Rn);
|
||||
|
||||
inst |= (Intrinsic)(op.Size << (int)Intrinsic.Arm64VSizeShift);
|
||||
|
||||
Operand result = context.AddIntrinsic(inst, n);
|
||||
|
||||
context.Copy(GetVec(op.Rd), result);
|
||||
|
||||
context.SetPendingQcFlagSync();
|
||||
}
|
||||
|
||||
public static void EmitScalarSaturatingBinaryOp(ArmEmitterContext context, Intrinsic inst)
|
||||
{
|
||||
OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp;
|
||||
|
||||
Operand n = GetVec(op.Rn);
|
||||
Operand m = GetVec(op.Rm);
|
||||
|
||||
inst |= (Intrinsic)(op.Size << (int)Intrinsic.Arm64VSizeShift);
|
||||
|
||||
Operand result = context.AddIntrinsic(inst, n, m);
|
||||
|
||||
context.Copy(GetVec(op.Rd), result);
|
||||
|
||||
context.SetPendingQcFlagSync();
|
||||
}
|
||||
|
||||
public static void EmitScalarSaturatingBinaryOpRd(ArmEmitterContext context, Intrinsic inst)
|
||||
{
|
||||
OpCodeSimd op = (OpCodeSimd)context.CurrOp;
|
||||
|
||||
Operand d = GetVec(op.Rd);
|
||||
Operand n = GetVec(op.Rn);
|
||||
|
||||
inst |= (Intrinsic)(op.Size << (int)Intrinsic.Arm64VSizeShift);
|
||||
|
||||
Operand result = context.AddIntrinsic(inst, d, n);
|
||||
|
||||
context.Copy(GetVec(op.Rd), result);
|
||||
|
||||
context.SetPendingQcFlagSync();
|
||||
}
|
||||
|
||||
public static void EmitScalarConvertBinaryOpF(ArmEmitterContext context, Intrinsic inst, int fBits)
|
||||
{
|
||||
OpCodeSimd op = (OpCodeSimd)context.CurrOp;
|
||||
|
||||
Operand n = GetVec(op.Rn);
|
||||
|
||||
if ((op.Size & 1) != 0)
|
||||
{
|
||||
inst |= Intrinsic.Arm64VDouble;
|
||||
}
|
||||
|
||||
context.Copy(GetVec(op.Rd), context.AddIntrinsic(inst, n, Const(fBits)));
|
||||
}
|
||||
|
||||
public static void EmitScalarConvertBinaryOpFFromGp(ArmEmitterContext context, Intrinsic inst, int fBits)
|
||||
{
|
||||
OpCodeSimd op = (OpCodeSimd)context.CurrOp;
|
||||
|
||||
Operand n = GetIntOrZR(context, op.Rn);
|
||||
|
||||
if ((op.Size & 1) != 0)
|
||||
{
|
||||
inst |= Intrinsic.Arm64VDouble;
|
||||
}
|
||||
|
||||
context.Copy(GetVec(op.Rd), context.AddIntrinsic(inst, n, Const(fBits)));
|
||||
}
|
||||
|
||||
public static void EmitScalarConvertBinaryOpFToGp(ArmEmitterContext context, Intrinsic inst, int fBits)
|
||||
{
|
||||
OpCodeSimd op = (OpCodeSimd)context.CurrOp;
|
||||
|
||||
Operand n = GetVec(op.Rn);
|
||||
|
||||
if ((op.Size & 1) != 0)
|
||||
{
|
||||
inst |= Intrinsic.Arm64VDouble;
|
||||
}
|
||||
|
||||
SetIntOrZR(context, op.Rd, op.RegisterSize == RegisterSize.Int32
|
||||
? context.AddIntrinsicInt (inst, n, Const(fBits))
|
||||
: context.AddIntrinsicLong(inst, n, Const(fBits)));
|
||||
}
|
||||
|
||||
public static void EmitVectorUnaryOpF(ArmEmitterContext context, Intrinsic inst)
|
||||
{
|
||||
OpCodeSimd op = (OpCodeSimd)context.CurrOp;
|
||||
|
||||
Operand n = GetVec(op.Rn);
|
||||
|
||||
if ((op.Size & 1) != 0)
|
||||
{
|
||||
inst |= Intrinsic.Arm64VDouble;
|
||||
}
|
||||
|
||||
if (op.RegisterSize == RegisterSize.Simd128)
|
||||
{
|
||||
inst |= Intrinsic.Arm64V128;
|
||||
}
|
||||
|
||||
context.Copy(GetVec(op.Rd), context.AddIntrinsic(inst, n));
|
||||
}
|
||||
|
||||
public static void EmitVectorBinaryOpF(ArmEmitterContext context, Intrinsic inst)
|
||||
{
|
||||
OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp;
|
||||
|
||||
Operand n = GetVec(op.Rn);
|
||||
Operand m = GetVec(op.Rm);
|
||||
|
||||
if ((op.Size & 1) != 0)
|
||||
{
|
||||
inst |= Intrinsic.Arm64VDouble;
|
||||
}
|
||||
|
||||
if (op.RegisterSize == RegisterSize.Simd128)
|
||||
{
|
||||
inst |= Intrinsic.Arm64V128;
|
||||
}
|
||||
|
||||
context.Copy(GetVec(op.Rd), context.AddIntrinsic(inst, n, m));
|
||||
}
|
||||
|
||||
public static void EmitVectorBinaryOpFRd(ArmEmitterContext context, Intrinsic inst)
|
||||
{
|
||||
OpCodeSimd op = (OpCodeSimd)context.CurrOp;
|
||||
|
||||
Operand d = GetVec(op.Rd);
|
||||
Operand n = GetVec(op.Rn);
|
||||
|
||||
if ((op.Size & 1) != 0)
|
||||
{
|
||||
inst |= Intrinsic.Arm64VDouble;
|
||||
}
|
||||
|
||||
if (op.RegisterSize == RegisterSize.Simd128)
|
||||
{
|
||||
inst |= Intrinsic.Arm64V128;
|
||||
}
|
||||
|
||||
context.Copy(GetVec(op.Rd), context.AddIntrinsic(inst, d, n));
|
||||
}
|
||||
|
||||
public static void EmitVectorBinaryOpFByElem(ArmEmitterContext context, Intrinsic inst)
|
||||
{
|
||||
OpCodeSimdRegElemF op = (OpCodeSimdRegElemF)context.CurrOp;
|
||||
|
||||
Operand n = GetVec(op.Rn);
|
||||
Operand m = GetVec(op.Rm);
|
||||
|
||||
if ((op.Size & 1) != 0)
|
||||
{
|
||||
inst |= Intrinsic.Arm64VDouble;
|
||||
}
|
||||
|
||||
if (op.RegisterSize == RegisterSize.Simd128)
|
||||
{
|
||||
inst |= Intrinsic.Arm64V128;
|
||||
}
|
||||
|
||||
context.Copy(GetVec(op.Rd), context.AddIntrinsic(inst, n, m, Const(op.Index)));
|
||||
}
|
||||
|
||||
public static void EmitVectorTernaryOpFRd(ArmEmitterContext context, Intrinsic inst)
|
||||
{
|
||||
OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp;
|
||||
|
||||
Operand d = GetVec(op.Rd);
|
||||
Operand n = GetVec(op.Rn);
|
||||
Operand m = GetVec(op.Rm);
|
||||
|
||||
if ((op.Size & 1) != 0)
|
||||
{
|
||||
inst |= Intrinsic.Arm64VDouble;
|
||||
}
|
||||
|
||||
if (op.RegisterSize == RegisterSize.Simd128)
|
||||
{
|
||||
inst |= Intrinsic.Arm64V128;
|
||||
}
|
||||
|
||||
context.Copy(d, context.AddIntrinsic(inst, d, n, m));
|
||||
}
|
||||
|
||||
public static void EmitVectorTernaryOpFRdByElem(ArmEmitterContext context, Intrinsic inst)
|
||||
{
|
||||
OpCodeSimdRegElemF op = (OpCodeSimdRegElemF)context.CurrOp;
|
||||
|
||||
Operand d = GetVec(op.Rd);
|
||||
Operand n = GetVec(op.Rn);
|
||||
Operand m = GetVec(op.Rm);
|
||||
|
||||
if ((op.Size & 1) != 0)
|
||||
{
|
||||
inst |= Intrinsic.Arm64VDouble;
|
||||
}
|
||||
|
||||
if (op.RegisterSize == RegisterSize.Simd128)
|
||||
{
|
||||
inst |= Intrinsic.Arm64V128;
|
||||
}
|
||||
|
||||
context.Copy(d, context.AddIntrinsic(inst, d, n, m, Const(op.Index)));
|
||||
}
|
||||
|
||||
public static void EmitVectorUnaryOp(ArmEmitterContext context, Intrinsic inst)
|
||||
{
|
||||
OpCodeSimd op = (OpCodeSimd)context.CurrOp;
|
||||
|
||||
Operand n = GetVec(op.Rn);
|
||||
|
||||
inst |= (Intrinsic)(op.Size << (int)Intrinsic.Arm64VSizeShift);
|
||||
|
||||
if (op.RegisterSize == RegisterSize.Simd128)
|
||||
{
|
||||
inst |= Intrinsic.Arm64V128;
|
||||
}
|
||||
|
||||
context.Copy(GetVec(op.Rd), context.AddIntrinsic(inst, n));
|
||||
}
|
||||
|
||||
public static void EmitVectorBinaryOp(ArmEmitterContext context, Intrinsic inst)
|
||||
{
|
||||
OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp;
|
||||
|
||||
Operand n = GetVec(op.Rn);
|
||||
Operand m = GetVec(op.Rm);
|
||||
|
||||
inst |= (Intrinsic)(op.Size << (int)Intrinsic.Arm64VSizeShift);
|
||||
|
||||
if (op.RegisterSize == RegisterSize.Simd128)
|
||||
{
|
||||
inst |= Intrinsic.Arm64V128;
|
||||
}
|
||||
|
||||
context.Copy(GetVec(op.Rd), context.AddIntrinsic(inst, n, m));
|
||||
}
|
||||
|
||||
public static void EmitVectorBinaryOpRd(ArmEmitterContext context, Intrinsic inst)
|
||||
{
|
||||
OpCodeSimd op = (OpCodeSimd)context.CurrOp;
|
||||
|
||||
Operand d = GetVec(op.Rd);
|
||||
Operand n = GetVec(op.Rn);
|
||||
|
||||
inst |= (Intrinsic)(op.Size << (int)Intrinsic.Arm64VSizeShift);
|
||||
|
||||
if (op.RegisterSize == RegisterSize.Simd128)
|
||||
{
|
||||
inst |= Intrinsic.Arm64V128;
|
||||
}
|
||||
|
||||
context.Copy(GetVec(op.Rd), context.AddIntrinsic(inst, d, n));
|
||||
}
|
||||
|
||||
public static void EmitVectorBinaryOpByElem(ArmEmitterContext context, Intrinsic inst)
|
||||
{
|
||||
OpCodeSimdRegElem op = (OpCodeSimdRegElem)context.CurrOp;
|
||||
|
||||
Operand n = GetVec(op.Rn);
|
||||
Operand m = GetVec(op.Rm);
|
||||
|
||||
inst |= (Intrinsic)(op.Size << (int)Intrinsic.Arm64VSizeShift);
|
||||
|
||||
if (op.RegisterSize == RegisterSize.Simd128)
|
||||
{
|
||||
inst |= Intrinsic.Arm64V128;
|
||||
}
|
||||
|
||||
context.Copy(GetVec(op.Rd), context.AddIntrinsic(inst, n, m, Const(op.Index)));
|
||||
}
|
||||
|
||||
public static void EmitVectorTernaryOpRd(ArmEmitterContext context, Intrinsic inst)
|
||||
{
|
||||
OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp;
|
||||
|
||||
Operand d = GetVec(op.Rd);
|
||||
Operand n = GetVec(op.Rn);
|
||||
Operand m = GetVec(op.Rm);
|
||||
|
||||
inst |= (Intrinsic)(op.Size << (int)Intrinsic.Arm64VSizeShift);
|
||||
|
||||
if (op.RegisterSize == RegisterSize.Simd128)
|
||||
{
|
||||
inst |= Intrinsic.Arm64V128;
|
||||
}
|
||||
|
||||
context.Copy(d, context.AddIntrinsic(inst, d, n, m));
|
||||
}
|
||||
|
||||
public static void EmitVectorTernaryOpRdByElem(ArmEmitterContext context, Intrinsic inst)
|
||||
{
|
||||
OpCodeSimdRegElem op = (OpCodeSimdRegElem)context.CurrOp;
|
||||
|
||||
Operand d = GetVec(op.Rd);
|
||||
Operand n = GetVec(op.Rn);
|
||||
Operand m = GetVec(op.Rm);
|
||||
|
||||
inst |= (Intrinsic)(op.Size << (int)Intrinsic.Arm64VSizeShift);
|
||||
|
||||
if (op.RegisterSize == RegisterSize.Simd128)
|
||||
{
|
||||
inst |= Intrinsic.Arm64V128;
|
||||
}
|
||||
|
||||
context.Copy(d, context.AddIntrinsic(inst, d, n, m, Const(op.Index)));
|
||||
}
|
||||
|
||||
public static void EmitVectorShiftBinaryOp(ArmEmitterContext context, Intrinsic inst, int shift)
|
||||
{
|
||||
OpCodeSimd op = (OpCodeSimd)context.CurrOp;
|
||||
|
||||
Operand n = GetVec(op.Rn);
|
||||
|
||||
inst |= (Intrinsic)(op.Size << (int)Intrinsic.Arm64VSizeShift);
|
||||
|
||||
if (op.RegisterSize == RegisterSize.Simd128)
|
||||
{
|
||||
inst |= Intrinsic.Arm64V128;
|
||||
}
|
||||
|
||||
context.Copy(GetVec(op.Rd), context.AddIntrinsic(inst, n, Const(shift)));
|
||||
}
|
||||
|
||||
public static void EmitVectorShiftTernaryOpRd(ArmEmitterContext context, Intrinsic inst, int shift)
|
||||
{
|
||||
OpCodeSimd op = (OpCodeSimd)context.CurrOp;
|
||||
|
||||
Operand d = GetVec(op.Rd);
|
||||
Operand n = GetVec(op.Rn);
|
||||
|
||||
inst |= (Intrinsic)(op.Size << (int)Intrinsic.Arm64VSizeShift);
|
||||
|
||||
if (op.RegisterSize == RegisterSize.Simd128)
|
||||
{
|
||||
inst |= Intrinsic.Arm64V128;
|
||||
}
|
||||
|
||||
context.Copy(GetVec(op.Rd), context.AddIntrinsic(inst, d, n, Const(shift)));
|
||||
}
|
||||
|
||||
public static void EmitVectorSaturatingShiftTernaryOpRd(ArmEmitterContext context, Intrinsic inst, int shift)
|
||||
{
|
||||
OpCodeSimd op = (OpCodeSimd)context.CurrOp;
|
||||
|
||||
Operand d = GetVec(op.Rd);
|
||||
Operand n = GetVec(op.Rn);
|
||||
|
||||
inst |= (Intrinsic)(op.Size << (int)Intrinsic.Arm64VSizeShift);
|
||||
|
||||
if (op.RegisterSize == RegisterSize.Simd128)
|
||||
{
|
||||
inst |= Intrinsic.Arm64V128;
|
||||
}
|
||||
|
||||
context.Copy(GetVec(op.Rd), context.AddIntrinsic(inst, d, n, Const(shift)));
|
||||
|
||||
context.SetPendingQcFlagSync();
|
||||
}
|
||||
|
||||
public static void EmitVectorSaturatingUnaryOp(ArmEmitterContext context, Intrinsic inst)
|
||||
{
|
||||
OpCodeSimd op = (OpCodeSimd)context.CurrOp;
|
||||
|
||||
Operand n = GetVec(op.Rn);
|
||||
|
||||
inst |= (Intrinsic)(op.Size << (int)Intrinsic.Arm64VSizeShift);
|
||||
|
||||
if (op.RegisterSize == RegisterSize.Simd128)
|
||||
{
|
||||
inst |= Intrinsic.Arm64V128;
|
||||
}
|
||||
|
||||
Operand result = context.AddIntrinsic(inst, n);
|
||||
|
||||
context.Copy(GetVec(op.Rd), result);
|
||||
|
||||
context.SetPendingQcFlagSync();
|
||||
}
|
||||
|
||||
public static void EmitVectorSaturatingBinaryOp(ArmEmitterContext context, Intrinsic inst)
|
||||
{
|
||||
OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp;
|
||||
|
||||
Operand n = GetVec(op.Rn);
|
||||
Operand m = GetVec(op.Rm);
|
||||
|
||||
inst |= (Intrinsic)(op.Size << (int)Intrinsic.Arm64VSizeShift);
|
||||
|
||||
if (op.RegisterSize == RegisterSize.Simd128)
|
||||
{
|
||||
inst |= Intrinsic.Arm64V128;
|
||||
}
|
||||
|
||||
Operand result = context.AddIntrinsic(inst, n, m);
|
||||
|
||||
context.Copy(GetVec(op.Rd), result);
|
||||
|
||||
context.SetPendingQcFlagSync();
|
||||
}
|
||||
|
||||
public static void EmitVectorSaturatingBinaryOpRd(ArmEmitterContext context, Intrinsic inst)
|
||||
{
|
||||
OpCodeSimd op = (OpCodeSimd)context.CurrOp;
|
||||
|
||||
Operand d = GetVec(op.Rd);
|
||||
Operand n = GetVec(op.Rn);
|
||||
|
||||
inst |= (Intrinsic)(op.Size << (int)Intrinsic.Arm64VSizeShift);
|
||||
|
||||
if (op.RegisterSize == RegisterSize.Simd128)
|
||||
{
|
||||
inst |= Intrinsic.Arm64V128;
|
||||
}
|
||||
|
||||
Operand result = context.AddIntrinsic(inst, d, n);
|
||||
|
||||
context.Copy(GetVec(op.Rd), result);
|
||||
|
||||
context.SetPendingQcFlagSync();
|
||||
}
|
||||
|
||||
public static void EmitVectorSaturatingBinaryOpByElem(ArmEmitterContext context, Intrinsic inst)
|
||||
{
|
||||
OpCodeSimdRegElem op = (OpCodeSimdRegElem)context.CurrOp;
|
||||
|
||||
Operand n = GetVec(op.Rn);
|
||||
Operand m = GetVec(op.Rm);
|
||||
|
||||
inst |= (Intrinsic)(op.Size << (int)Intrinsic.Arm64VSizeShift);
|
||||
|
||||
if (op.RegisterSize == RegisterSize.Simd128)
|
||||
{
|
||||
inst |= Intrinsic.Arm64V128;
|
||||
}
|
||||
|
||||
Operand result = context.AddIntrinsic(inst, n, m, Const(op.Index));
|
||||
|
||||
context.Copy(GetVec(op.Rd), result);
|
||||
|
||||
context.SetPendingQcFlagSync();
|
||||
}
|
||||
|
||||
public static void EmitVectorConvertBinaryOpF(ArmEmitterContext context, Intrinsic inst, int fBits)
|
||||
{
|
||||
OpCodeSimd op = (OpCodeSimd)context.CurrOp;
|
||||
|
||||
Operand n = GetVec(op.Rn);
|
||||
|
||||
if ((op.Size & 1) != 0)
|
||||
{
|
||||
inst |= Intrinsic.Arm64VDouble;
|
||||
}
|
||||
|
||||
if (op.RegisterSize == RegisterSize.Simd128)
|
||||
{
|
||||
inst |= Intrinsic.Arm64V128;
|
||||
}
|
||||
|
||||
context.Copy(GetVec(op.Rd), context.AddIntrinsic(inst, n, Const(fBits)));
|
||||
}
|
||||
|
||||
public static void EmitVectorLookupTable(ArmEmitterContext context, Intrinsic inst)
|
||||
{
|
||||
OpCodeSimdTbl op = (OpCodeSimdTbl)context.CurrOp;
|
||||
|
||||
Operand[] operands = new Operand[op.Size + 1];
|
||||
|
||||
operands[op.Size] = GetVec(op.Rm);
|
||||
|
||||
for (int index = 0; index < op.Size; index++)
|
||||
{
|
||||
operands[index] = GetVec((op.Rn + index) & 0x1F);
|
||||
}
|
||||
|
||||
if (op.RegisterSize == RegisterSize.Simd128)
|
||||
{
|
||||
inst |= Intrinsic.Arm64V128;
|
||||
}
|
||||
|
||||
context.Copy(GetVec(op.Rd), context.AddIntrinsic(inst, operands));
|
||||
}
|
||||
|
||||
public static void EmitFcmpOrFcmpe(ArmEmitterContext context, bool signalNaNs)
|
||||
{
|
||||
OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp;
|
||||
|
||||
bool cmpWithZero = !(op is OpCodeSimdFcond) ? op.Bit3 : false;
|
||||
|
||||
Intrinsic inst = signalNaNs ? Intrinsic.Arm64FcmpeS : Intrinsic.Arm64FcmpS;
|
||||
|
||||
if ((op.Size & 1) != 0)
|
||||
{
|
||||
inst |= Intrinsic.Arm64VDouble;
|
||||
}
|
||||
|
||||
Operand n = GetVec(op.Rn);
|
||||
Operand m = cmpWithZero ? Const(0) : GetVec(op.Rm);
|
||||
|
||||
Operand nzcv = context.AddIntrinsicInt(inst, n, m);
|
||||
|
||||
Operand one = Const(1);
|
||||
|
||||
SetFlag(context, PState.VFlag, context.BitwiseAnd(context.ShiftRightUI(nzcv, Const(28)), one));
|
||||
SetFlag(context, PState.CFlag, context.BitwiseAnd(context.ShiftRightUI(nzcv, Const(29)), one));
|
||||
SetFlag(context, PState.ZFlag, context.BitwiseAnd(context.ShiftRightUI(nzcv, Const(30)), one));
|
||||
SetFlag(context, PState.NFlag, context.BitwiseAnd(context.ShiftRightUI(nzcv, Const(31)), one));
|
||||
}
|
||||
}
|
||||
}
|
||||
612
src/ARMeilleure/Instructions/InstEmitSimdLogical.cs
Normal file
612
src/ARMeilleure/Instructions/InstEmitSimdLogical.cs
Normal file
@@ -0,0 +1,612 @@
|
||||
using ARMeilleure.Decoders;
|
||||
using ARMeilleure.IntermediateRepresentation;
|
||||
using ARMeilleure.Translation;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
|
||||
using static ARMeilleure.Instructions.InstEmitHelper;
|
||||
using static ARMeilleure.Instructions.InstEmitSimdHelper;
|
||||
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
|
||||
|
||||
namespace ARMeilleure.Instructions
|
||||
{
|
||||
static partial class InstEmit
|
||||
{
|
||||
public static void And_V(ArmEmitterContext context)
|
||||
{
|
||||
if (Optimizations.UseAdvSimd)
|
||||
{
|
||||
InstEmitSimdHelperArm64.EmitVectorBinaryOp(context, Intrinsic.Arm64AndV);
|
||||
}
|
||||
else 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.UseAdvSimd)
|
||||
{
|
||||
InstEmitSimdHelperArm64.EmitVectorBinaryOp(context, Intrinsic.Arm64BicV);
|
||||
}
|
||||
else 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)
|
||||
{
|
||||
if (Optimizations.UseSse2)
|
||||
{
|
||||
OpCodeSimdImm op = (OpCodeSimdImm)context.CurrOp;
|
||||
|
||||
int eSize = 8 << op.Size;
|
||||
|
||||
Operand d = GetVec(op.Rd);
|
||||
Operand imm = eSize switch {
|
||||
16 => X86GetAllElements(context, (short)~op.Immediate),
|
||||
32 => X86GetAllElements(context, (int)~op.Immediate),
|
||||
_ => throw new InvalidOperationException($"Invalid element size {eSize}.")
|
||||
};
|
||||
|
||||
Operand res = context.AddIntrinsic(Intrinsic.X86Pand, d, imm);
|
||||
|
||||
if (op.RegisterSize == RegisterSize.Simd64)
|
||||
{
|
||||
res = context.VectorZeroUpper64(res);
|
||||
}
|
||||
|
||||
context.Copy(GetVec(op.Rd), res);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitVectorImmBinaryOp(context, (op1, op2) =>
|
||||
{
|
||||
return context.BitwiseAnd(op1, context.BitwiseNot(op2));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public static void Bif_V(ArmEmitterContext context)
|
||||
{
|
||||
if (Optimizations.UseAdvSimd)
|
||||
{
|
||||
InstEmitSimdHelperArm64.EmitVectorTernaryOpRd(context, Intrinsic.Arm64BifV);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitBifBit(context, notRm: true);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Bit_V(ArmEmitterContext context)
|
||||
{
|
||||
if (Optimizations.UseAdvSimd)
|
||||
{
|
||||
InstEmitSimdHelperArm64.EmitVectorTernaryOpRd(context, Intrinsic.Arm64BitV);
|
||||
}
|
||||
else
|
||||
{
|
||||
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(d, 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.UseAdvSimd)
|
||||
{
|
||||
InstEmitSimdHelperArm64.EmitVectorTernaryOpRd(context, Intrinsic.Arm64BslV);
|
||||
}
|
||||
else 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(d, 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.UseAdvSimd)
|
||||
{
|
||||
InstEmitSimdHelperArm64.EmitVectorBinaryOp(context, Intrinsic.Arm64EorV);
|
||||
}
|
||||
else 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.UseAvx512Ortho)
|
||||
{
|
||||
OpCodeSimd op = (OpCodeSimd)context.CurrOp;
|
||||
|
||||
Operand n = GetVec(op.Rn);
|
||||
|
||||
Operand res = context.AddIntrinsic(Intrinsic.X86Vpternlogd, n, n, Const(~0b10101010));
|
||||
|
||||
if (op.RegisterSize == RegisterSize.Simd64)
|
||||
{
|
||||
res = context.VectorZeroUpper64(res);
|
||||
}
|
||||
|
||||
context.Copy(GetVec(op.Rd), res);
|
||||
}
|
||||
else 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.UseAdvSimd)
|
||||
{
|
||||
InstEmitSimdHelperArm64.EmitVectorBinaryOp(context, Intrinsic.Arm64OrnV);
|
||||
}
|
||||
else if (Optimizations.UseAvx512Ortho)
|
||||
{
|
||||
OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp;
|
||||
|
||||
Operand n = GetVec(op.Rn);
|
||||
Operand m = GetVec(op.Rm);
|
||||
|
||||
Operand res = context.AddIntrinsic(Intrinsic.X86Vpternlogd, n, m, Const(0b11001100 | ~0b10101010));
|
||||
|
||||
if (op.RegisterSize == RegisterSize.Simd64)
|
||||
{
|
||||
res = context.VectorZeroUpper64(res);
|
||||
}
|
||||
|
||||
context.Copy(GetVec(op.Rd), res);
|
||||
}
|
||||
else 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.UseAdvSimd)
|
||||
{
|
||||
InstEmitSimdHelperArm64.EmitVectorBinaryOp(context, Intrinsic.Arm64OrrV);
|
||||
}
|
||||
else 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)
|
||||
{
|
||||
if (Optimizations.UseSse2)
|
||||
{
|
||||
OpCodeSimdImm op = (OpCodeSimdImm)context.CurrOp;
|
||||
|
||||
int eSize = 8 << op.Size;
|
||||
|
||||
Operand d = GetVec(op.Rd);
|
||||
Operand imm = eSize switch {
|
||||
16 => X86GetAllElements(context, (short)op.Immediate),
|
||||
32 => X86GetAllElements(context, (int)op.Immediate),
|
||||
_ => throw new InvalidOperationException($"Invalid element size {eSize}.")
|
||||
};
|
||||
|
||||
Operand res = context.AddIntrinsic(Intrinsic.X86Por, d, imm);
|
||||
|
||||
if (op.RegisterSize == RegisterSize.Simd64)
|
||||
{
|
||||
res = context.VectorZeroUpper64(res);
|
||||
}
|
||||
|
||||
context.Copy(GetVec(op.Rd), res);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitVectorImmBinaryOp(context, (op1, op2) => context.BitwiseOr(op1, op2));
|
||||
}
|
||||
}
|
||||
|
||||
public static void Rbit_V(ArmEmitterContext context)
|
||||
{
|
||||
OpCodeSimd op = (OpCodeSimd)context.CurrOp;
|
||||
|
||||
if (Optimizations.UseGfni)
|
||||
{
|
||||
const long bitMatrix =
|
||||
(0b10000000L << 56) |
|
||||
(0b01000000L << 48) |
|
||||
(0b00100000L << 40) |
|
||||
(0b00010000L << 32) |
|
||||
(0b00001000L << 24) |
|
||||
(0b00000100L << 16) |
|
||||
(0b00000010L << 8) |
|
||||
(0b00000001L << 0);
|
||||
|
||||
Operand vBitMatrix = X86GetAllElements(context, bitMatrix);
|
||||
|
||||
Operand res = context.AddIntrinsic(Intrinsic.X86Gf2p8affineqb, GetVec(op.Rn), vBitMatrix, Const(0));
|
||||
|
||||
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 ? 16 : 8;
|
||||
|
||||
for (int index = 0; index < elems; index++)
|
||||
{
|
||||
Operand ne = EmitVectorExtractZx(context, op.Rn, index, 0);
|
||||
|
||||
Operand de = EmitReverseBits8Op(context, ne);
|
||||
|
||||
res = EmitVectorInsert(context, res, de, index, 0);
|
||||
}
|
||||
|
||||
context.Copy(GetVec(op.Rd), res);
|
||||
}
|
||||
}
|
||||
|
||||
private static Operand EmitReverseBits8Op(ArmEmitterContext context, Operand op)
|
||||
{
|
||||
Debug.Assert(op.Type == OperandType.I64);
|
||||
|
||||
Operand val = context.BitwiseOr(context.ShiftRightUI(context.BitwiseAnd(op, Const(0xaaul)), Const(1)),
|
||||
context.ShiftLeft (context.BitwiseAnd(op, Const(0x55ul)), Const(1)));
|
||||
|
||||
val = context.BitwiseOr(context.ShiftRightUI(context.BitwiseAnd(val, Const(0xccul)), Const(2)),
|
||||
context.ShiftLeft (context.BitwiseAnd(val, Const(0x33ul)), Const(2)));
|
||||
|
||||
return context.BitwiseOr(context.ShiftRightUI(val, Const(4)),
|
||||
context.ShiftLeft (context.BitwiseAnd(val, Const(0x0ful)), Const(4)));
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
266
src/ARMeilleure/Instructions/InstEmitSimdLogical32.cs
Normal file
266
src/ARMeilleure/Instructions/InstEmitSimdLogical32.cs
Normal file
@@ -0,0 +1,266 @@
|
||||
using ARMeilleure.Decoders;
|
||||
using ARMeilleure.IntermediateRepresentation;
|
||||
using ARMeilleure.Translation;
|
||||
|
||||
using static ARMeilleure.Instructions.InstEmitHelper;
|
||||
using static ARMeilleure.Instructions.InstEmitSimdHelper;
|
||||
using static ARMeilleure.Instructions.InstEmitSimdHelper32;
|
||||
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
|
||||
|
||||
namespace ARMeilleure.Instructions
|
||||
{
|
||||
static partial class InstEmit32
|
||||
{
|
||||
public static void Vand_I(ArmEmitterContext context)
|
||||
{
|
||||
if (Optimizations.UseAdvSimd)
|
||||
{
|
||||
InstEmitSimdHelper32Arm64.EmitVectorBinaryOpSimd32(context, (n, m) => context.AddIntrinsic(Intrinsic.Arm64AndV | Intrinsic.Arm64V128, n, m));
|
||||
}
|
||||
else if (Optimizations.UseSse2)
|
||||
{
|
||||
EmitVectorBinaryOpSimd32(context, (n, m) => context.AddIntrinsic(Intrinsic.X86Pand, n, m));
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitVectorBinaryOpZx32(context, (op1, op2) => context.BitwiseAnd(op1, op2));
|
||||
}
|
||||
}
|
||||
|
||||
public static void Vbic_I(ArmEmitterContext context)
|
||||
{
|
||||
if (Optimizations.UseAdvSimd)
|
||||
{
|
||||
InstEmitSimdHelper32Arm64.EmitVectorBinaryOpSimd32(context, (n, m) => context.AddIntrinsic(Intrinsic.Arm64BicV | Intrinsic.Arm64V128, n, m));
|
||||
}
|
||||
else if (Optimizations.UseSse2)
|
||||
{
|
||||
EmitVectorBinaryOpSimd32(context, (n, m) => context.AddIntrinsic(Intrinsic.X86Pandn, m, n));
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitVectorBinaryOpZx32(context, (op1, op2) => context.BitwiseAnd(op1, context.BitwiseNot(op2)));
|
||||
}
|
||||
}
|
||||
|
||||
public static void Vbic_II(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32SimdImm op = (OpCode32SimdImm)context.CurrOp;
|
||||
|
||||
long immediate = op.Immediate;
|
||||
|
||||
// Replicate fields to fill the 64-bits, if size is < 64-bits.
|
||||
switch (op.Size)
|
||||
{
|
||||
case 0: immediate *= 0x0101010101010101L; break;
|
||||
case 1: immediate *= 0x0001000100010001L; break;
|
||||
case 2: immediate *= 0x0000000100000001L; break;
|
||||
}
|
||||
|
||||
Operand imm = Const(immediate);
|
||||
Operand res = GetVecA32(op.Qd);
|
||||
|
||||
if (op.Q)
|
||||
{
|
||||
for (int elem = 0; elem < 2; elem++)
|
||||
{
|
||||
Operand de = EmitVectorExtractZx(context, op.Qd, elem, 3);
|
||||
|
||||
res = EmitVectorInsert(context, res, context.BitwiseAnd(de, context.BitwiseNot(imm)), elem, 3);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Operand de = EmitVectorExtractZx(context, op.Qd, op.Vd & 1, 3);
|
||||
|
||||
res = EmitVectorInsert(context, res, context.BitwiseAnd(de, context.BitwiseNot(imm)), op.Vd & 1, 3);
|
||||
}
|
||||
|
||||
context.Copy(GetVecA32(op.Qd), res);
|
||||
}
|
||||
|
||||
public static void Vbif(ArmEmitterContext context)
|
||||
{
|
||||
if (Optimizations.UseAdvSimd)
|
||||
{
|
||||
InstEmitSimdHelper32Arm64.EmitVectorTernaryOpSimd32(context, (d, n, m) => context.AddIntrinsic(Intrinsic.Arm64BifV | Intrinsic.Arm64V128, d, n, m));
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitBifBit(context, true);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Vbit(ArmEmitterContext context)
|
||||
{
|
||||
if (Optimizations.UseAdvSimd)
|
||||
{
|
||||
InstEmitSimdHelper32Arm64.EmitVectorTernaryOpSimd32(context, (d, n, m) => context.AddIntrinsic(Intrinsic.Arm64BitV | Intrinsic.Arm64V128, d, n, m));
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitBifBit(context, false);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Vbsl(ArmEmitterContext context)
|
||||
{
|
||||
if (Optimizations.UseAdvSimd)
|
||||
{
|
||||
InstEmitSimdHelper32Arm64.EmitVectorTernaryOpSimd32(context, (d, n, m) => context.AddIntrinsic(Intrinsic.Arm64BslV | Intrinsic.Arm64V128, d, n, m));
|
||||
}
|
||||
else if (Optimizations.UseSse2)
|
||||
{
|
||||
EmitVectorTernaryOpSimd32(context, (d, n, m) =>
|
||||
{
|
||||
Operand res = context.AddIntrinsic(Intrinsic.X86Pxor, n, m);
|
||||
res = context.AddIntrinsic(Intrinsic.X86Pand, res, d);
|
||||
return context.AddIntrinsic(Intrinsic.X86Pxor, res, m);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitVectorTernaryOpZx32(context, (op1, op2, op3) =>
|
||||
{
|
||||
return context.BitwiseExclusiveOr(
|
||||
context.BitwiseAnd(op1,
|
||||
context.BitwiseExclusiveOr(op2, op3)), op3);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public static void Veor_I(ArmEmitterContext context)
|
||||
{
|
||||
if (Optimizations.UseAdvSimd)
|
||||
{
|
||||
InstEmitSimdHelper32Arm64.EmitVectorBinaryOpSimd32(context, (n, m) => context.AddIntrinsic(Intrinsic.Arm64EorV | Intrinsic.Arm64V128, n, m));
|
||||
}
|
||||
else if (Optimizations.UseSse2)
|
||||
{
|
||||
EmitVectorBinaryOpSimd32(context, (n, m) => context.AddIntrinsic(Intrinsic.X86Pxor, n, m));
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitVectorBinaryOpZx32(context, (op1, op2) => context.BitwiseExclusiveOr(op1, op2));
|
||||
}
|
||||
}
|
||||
|
||||
public static void Vorn_I(ArmEmitterContext context)
|
||||
{
|
||||
if (Optimizations.UseAdvSimd)
|
||||
{
|
||||
InstEmitSimdHelper32Arm64.EmitVectorBinaryOpSimd32(context, (n, m) => context.AddIntrinsic(Intrinsic.Arm64OrnV | Intrinsic.Arm64V128, n, m));
|
||||
}
|
||||
else if (Optimizations.UseAvx512Ortho)
|
||||
{
|
||||
EmitVectorBinaryOpSimd32(context, (n, m) =>
|
||||
{
|
||||
return context.AddIntrinsic(Intrinsic.X86Vpternlogd, n, m, Const(0b11001100 | ~0b10101010));
|
||||
});
|
||||
}
|
||||
else if (Optimizations.UseSse2)
|
||||
{
|
||||
Operand mask = context.VectorOne();
|
||||
|
||||
EmitVectorBinaryOpSimd32(context, (n, m) =>
|
||||
{
|
||||
m = context.AddIntrinsic(Intrinsic.X86Pandn, m, mask);
|
||||
return context.AddIntrinsic(Intrinsic.X86Por, n, m);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitVectorBinaryOpZx32(context, (op1, op2) => context.BitwiseOr(op1, context.BitwiseNot(op2)));
|
||||
}
|
||||
}
|
||||
|
||||
public static void Vorr_I(ArmEmitterContext context)
|
||||
{
|
||||
if (Optimizations.UseAdvSimd)
|
||||
{
|
||||
InstEmitSimdHelper32Arm64.EmitVectorBinaryOpSimd32(context, (n, m) => context.AddIntrinsic(Intrinsic.Arm64OrrV | Intrinsic.Arm64V128, n, m));
|
||||
}
|
||||
else if (Optimizations.UseSse2)
|
||||
{
|
||||
EmitVectorBinaryOpSimd32(context, (n, m) => context.AddIntrinsic(Intrinsic.X86Por, n, m));
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitVectorBinaryOpZx32(context, (op1, op2) => context.BitwiseOr(op1, op2));
|
||||
}
|
||||
}
|
||||
|
||||
public static void Vorr_II(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32SimdImm op = (OpCode32SimdImm)context.CurrOp;
|
||||
|
||||
long immediate = op.Immediate;
|
||||
|
||||
// Replicate fields to fill the 64-bits, if size is < 64-bits.
|
||||
switch (op.Size)
|
||||
{
|
||||
case 0: immediate *= 0x0101010101010101L; break;
|
||||
case 1: immediate *= 0x0001000100010001L; break;
|
||||
case 2: immediate *= 0x0000000100000001L; break;
|
||||
}
|
||||
|
||||
Operand imm = Const(immediate);
|
||||
Operand res = GetVecA32(op.Qd);
|
||||
|
||||
if (op.Q)
|
||||
{
|
||||
for (int elem = 0; elem < 2; elem++)
|
||||
{
|
||||
Operand de = EmitVectorExtractZx(context, op.Qd, elem, 3);
|
||||
|
||||
res = EmitVectorInsert(context, res, context.BitwiseOr(de, imm), elem, 3);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Operand de = EmitVectorExtractZx(context, op.Qd, op.Vd & 1, 3);
|
||||
|
||||
res = EmitVectorInsert(context, res, context.BitwiseOr(de, imm), op.Vd & 1, 3);
|
||||
}
|
||||
|
||||
context.Copy(GetVecA32(op.Qd), res);
|
||||
}
|
||||
|
||||
public static void Vtst(ArmEmitterContext context)
|
||||
{
|
||||
EmitVectorBinaryOpZx32(context, (op1, op2) =>
|
||||
{
|
||||
Operand isZero = context.ICompareEqual(context.BitwiseAnd(op1, op2), Const(0));
|
||||
return context.ConditionalSelect(isZero, Const(0), Const(-1));
|
||||
});
|
||||
}
|
||||
|
||||
private static void EmitBifBit(ArmEmitterContext context, bool notRm)
|
||||
{
|
||||
OpCode32SimdReg op = (OpCode32SimdReg)context.CurrOp;
|
||||
|
||||
if (Optimizations.UseSse2)
|
||||
{
|
||||
EmitVectorTernaryOpSimd32(context, (d, n, m) =>
|
||||
{
|
||||
Operand res = context.AddIntrinsic(Intrinsic.X86Pxor, n, d);
|
||||
res = context.AddIntrinsic((notRm) ? Intrinsic.X86Pandn : Intrinsic.X86Pand, m, res);
|
||||
return context.AddIntrinsic(Intrinsic.X86Pxor, d, res);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitVectorTernaryOpZx32(context, (d, n, m) =>
|
||||
{
|
||||
if (notRm)
|
||||
{
|
||||
m = context.BitwiseNot(m);
|
||||
}
|
||||
return context.BitwiseExclusiveOr(
|
||||
context.BitwiseAnd(m,
|
||||
context.BitwiseExclusiveOr(d, n)), d);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
160
src/ARMeilleure/Instructions/InstEmitSimdMemory.cs
Normal file
160
src/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.Operand.Factory;
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
352
src/ARMeilleure/Instructions/InstEmitSimdMemory32.cs
Normal file
352
src/ARMeilleure/Instructions/InstEmitSimdMemory32.cs
Normal file
@@ -0,0 +1,352 @@
|
||||
using ARMeilleure.Decoders;
|
||||
using ARMeilleure.IntermediateRepresentation;
|
||||
using ARMeilleure.State;
|
||||
using ARMeilleure.Translation;
|
||||
|
||||
using static ARMeilleure.Instructions.InstEmitHelper;
|
||||
using static ARMeilleure.Instructions.InstEmitMemoryHelper;
|
||||
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
|
||||
|
||||
namespace ARMeilleure.Instructions
|
||||
{
|
||||
static partial class InstEmit32
|
||||
{
|
||||
public static void Vld1(ArmEmitterContext context)
|
||||
{
|
||||
EmitVStoreOrLoadN(context, 1, true);
|
||||
}
|
||||
|
||||
public static void Vld2(ArmEmitterContext context)
|
||||
{
|
||||
EmitVStoreOrLoadN(context, 2, true);
|
||||
}
|
||||
|
||||
public static void Vld3(ArmEmitterContext context)
|
||||
{
|
||||
EmitVStoreOrLoadN(context, 3, true);
|
||||
}
|
||||
|
||||
public static void Vld4(ArmEmitterContext context)
|
||||
{
|
||||
EmitVStoreOrLoadN(context, 4, true);
|
||||
}
|
||||
|
||||
public static void Vst1(ArmEmitterContext context)
|
||||
{
|
||||
EmitVStoreOrLoadN(context, 1, false);
|
||||
}
|
||||
|
||||
public static void Vst2(ArmEmitterContext context)
|
||||
{
|
||||
EmitVStoreOrLoadN(context, 2, false);
|
||||
}
|
||||
|
||||
public static void Vst3(ArmEmitterContext context)
|
||||
{
|
||||
EmitVStoreOrLoadN(context, 3, false);
|
||||
}
|
||||
|
||||
public static void Vst4(ArmEmitterContext context)
|
||||
{
|
||||
EmitVStoreOrLoadN(context, 4, false);
|
||||
}
|
||||
|
||||
public static void EmitVStoreOrLoadN(ArmEmitterContext context, int count, bool load)
|
||||
{
|
||||
if (context.CurrOp is OpCode32SimdMemSingle)
|
||||
{
|
||||
OpCode32SimdMemSingle op = (OpCode32SimdMemSingle)context.CurrOp;
|
||||
|
||||
int eBytes = 1 << op.Size;
|
||||
|
||||
Operand n = context.Copy(GetIntA32(context, op.Rn));
|
||||
|
||||
// TODO: Check alignment.
|
||||
int offset = 0;
|
||||
int d = op.Vd;
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
// Accesses an element from a double simd register.
|
||||
Operand address = context.Add(n, Const(offset));
|
||||
if (eBytes == 8)
|
||||
{
|
||||
if (load)
|
||||
{
|
||||
EmitDVectorLoad(context, address, d);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitDVectorStore(context, address, d);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int index = ((d & 1) << (3 - op.Size)) + op.Index;
|
||||
if (load)
|
||||
{
|
||||
if (op.Replicate)
|
||||
{
|
||||
var regs = (count > 1) ? 1 : op.Increment;
|
||||
for (int reg = 0; reg < regs; reg++)
|
||||
{
|
||||
int dreg = reg + d;
|
||||
int rIndex = ((dreg & 1) << (3 - op.Size));
|
||||
int limit = rIndex + (1 << (3 - op.Size));
|
||||
|
||||
while (rIndex < limit)
|
||||
{
|
||||
EmitLoadSimd(context, address, GetVecA32(dreg >> 1), dreg >> 1, rIndex++, op.Size);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitLoadSimd(context, address, GetVecA32(d >> 1), d >> 1, index, op.Size);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitStoreSimd(context, address, d >> 1, index, op.Size);
|
||||
}
|
||||
}
|
||||
offset += eBytes;
|
||||
d += op.Increment;
|
||||
}
|
||||
|
||||
if (op.WBack)
|
||||
{
|
||||
if (op.RegisterIndex)
|
||||
{
|
||||
Operand m = GetIntA32(context, op.Rm);
|
||||
SetIntA32(context, op.Rn, context.Add(n, m));
|
||||
}
|
||||
else
|
||||
{
|
||||
SetIntA32(context, op.Rn, context.Add(n, Const(count * eBytes)));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
OpCode32SimdMemPair op = (OpCode32SimdMemPair)context.CurrOp;
|
||||
|
||||
int increment = count > 1 ? op.Increment : 1;
|
||||
int eBytes = 1 << op.Size;
|
||||
|
||||
Operand n = context.Copy(GetIntA32(context, op.Rn));
|
||||
int offset = 0;
|
||||
int d = op.Vd;
|
||||
|
||||
for (int reg = 0; reg < op.Regs; reg++)
|
||||
{
|
||||
for (int elem = 0; elem < op.Elems; elem++)
|
||||
{
|
||||
int elemD = d + reg;
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
// Accesses an element from a double simd register,
|
||||
// add ebytes for each element.
|
||||
Operand address = context.Add(n, Const(offset));
|
||||
int index = ((elemD & 1) << (3 - op.Size)) + elem;
|
||||
if (eBytes == 8)
|
||||
{
|
||||
if (load)
|
||||
{
|
||||
EmitDVectorLoad(context, address, elemD);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitDVectorStore(context, address, elemD);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (load)
|
||||
{
|
||||
EmitLoadSimd(context, address, GetVecA32(elemD >> 1), elemD >> 1, index, op.Size);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitStoreSimd(context, address, elemD >> 1, index, op.Size);
|
||||
}
|
||||
}
|
||||
|
||||
offset += eBytes;
|
||||
elemD += increment;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (op.WBack)
|
||||
{
|
||||
if (op.RegisterIndex)
|
||||
{
|
||||
Operand m = GetIntA32(context, op.Rm);
|
||||
SetIntA32(context, op.Rn, context.Add(n, m));
|
||||
}
|
||||
else
|
||||
{
|
||||
SetIntA32(context, op.Rn, context.Add(n, Const(count * 8 * op.Regs)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void Vldm(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32SimdMemMult op = (OpCode32SimdMemMult)context.CurrOp;
|
||||
|
||||
Operand n = context.Copy(GetIntA32(context, op.Rn));
|
||||
|
||||
Operand baseAddress = context.Add(n, Const(op.Offset));
|
||||
|
||||
bool writeBack = op.PostOffset != 0;
|
||||
|
||||
if (writeBack)
|
||||
{
|
||||
SetIntA32(context, op.Rn, context.Add(n, Const(op.PostOffset)));
|
||||
}
|
||||
|
||||
int range = op.RegisterRange;
|
||||
|
||||
int sReg = (op.DoubleWidth) ? (op.Vd << 1) : op.Vd;
|
||||
int offset = 0;
|
||||
int byteSize = 4;
|
||||
|
||||
for (int num = 0; num < range; num++, sReg++)
|
||||
{
|
||||
Operand address = context.Add(baseAddress, Const(offset));
|
||||
Operand vec = GetVecA32(sReg >> 2);
|
||||
|
||||
EmitLoadSimd(context, address, vec, sReg >> 2, sReg & 3, WordSizeLog2);
|
||||
offset += byteSize;
|
||||
}
|
||||
}
|
||||
|
||||
public static void Vstm(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32SimdMemMult op = (OpCode32SimdMemMult)context.CurrOp;
|
||||
|
||||
Operand n = context.Copy(GetIntA32(context, op.Rn));
|
||||
|
||||
Operand baseAddress = context.Add(n, Const(op.Offset));
|
||||
|
||||
bool writeBack = op.PostOffset != 0;
|
||||
|
||||
if (writeBack)
|
||||
{
|
||||
SetIntA32(context, op.Rn, context.Add(n, Const(op.PostOffset)));
|
||||
}
|
||||
|
||||
int offset = 0;
|
||||
|
||||
int range = op.RegisterRange;
|
||||
int sReg = (op.DoubleWidth) ? (op.Vd << 1) : op.Vd;
|
||||
int byteSize = 4;
|
||||
|
||||
for (int num = 0; num < range; num++, sReg++)
|
||||
{
|
||||
Operand address = context.Add(baseAddress, Const(offset));
|
||||
|
||||
EmitStoreSimd(context, address, sReg >> 2, sReg & 3, WordSizeLog2);
|
||||
|
||||
offset += byteSize;
|
||||
}
|
||||
}
|
||||
|
||||
public static void Vldr(ArmEmitterContext context)
|
||||
{
|
||||
EmitVLoadOrStore(context, AccessType.Load);
|
||||
}
|
||||
|
||||
public static void Vstr(ArmEmitterContext context)
|
||||
{
|
||||
EmitVLoadOrStore(context, AccessType.Store);
|
||||
}
|
||||
|
||||
private static void EmitDVectorStore(ArmEmitterContext context, Operand address, int vecD)
|
||||
{
|
||||
int vecQ = vecD >> 1;
|
||||
int vecSElem = (vecD & 1) << 1;
|
||||
Operand lblBigEndian = Label();
|
||||
Operand lblEnd = Label();
|
||||
|
||||
context.BranchIfTrue(lblBigEndian, GetFlag(PState.EFlag));
|
||||
|
||||
EmitStoreSimd(context, address, vecQ, vecSElem, WordSizeLog2);
|
||||
EmitStoreSimd(context, context.Add(address, Const(4)), vecQ, vecSElem | 1, WordSizeLog2);
|
||||
|
||||
context.Branch(lblEnd);
|
||||
|
||||
context.MarkLabel(lblBigEndian);
|
||||
|
||||
EmitStoreSimd(context, address, vecQ, vecSElem | 1, WordSizeLog2);
|
||||
EmitStoreSimd(context, context.Add(address, Const(4)), vecQ, vecSElem, WordSizeLog2);
|
||||
|
||||
context.MarkLabel(lblEnd);
|
||||
}
|
||||
|
||||
private static void EmitDVectorLoad(ArmEmitterContext context, Operand address, int vecD)
|
||||
{
|
||||
int vecQ = vecD >> 1;
|
||||
int vecSElem = (vecD & 1) << 1;
|
||||
Operand vec = GetVecA32(vecQ);
|
||||
|
||||
Operand lblBigEndian = Label();
|
||||
Operand lblEnd = Label();
|
||||
|
||||
context.BranchIfTrue(lblBigEndian, GetFlag(PState.EFlag));
|
||||
|
||||
EmitLoadSimd(context, address, vec, vecQ, vecSElem, WordSizeLog2);
|
||||
EmitLoadSimd(context, context.Add(address, Const(4)), vec, vecQ, vecSElem | 1, WordSizeLog2);
|
||||
|
||||
context.Branch(lblEnd);
|
||||
|
||||
context.MarkLabel(lblBigEndian);
|
||||
|
||||
EmitLoadSimd(context, address, vec, vecQ, vecSElem | 1, WordSizeLog2);
|
||||
EmitLoadSimd(context, context.Add(address, Const(4)), vec, vecQ, vecSElem, WordSizeLog2);
|
||||
|
||||
context.MarkLabel(lblEnd);
|
||||
}
|
||||
|
||||
private static void EmitVLoadOrStore(ArmEmitterContext context, AccessType accType)
|
||||
{
|
||||
OpCode32SimdMemImm op = (OpCode32SimdMemImm)context.CurrOp;
|
||||
|
||||
Operand n = context.Copy(GetIntA32(context, op.Rn));
|
||||
Operand m = GetMemM(context, setCarry: false);
|
||||
|
||||
Operand address = op.Add
|
||||
? context.Add(n, m)
|
||||
: context.Subtract(n, m);
|
||||
|
||||
int size = op.Size;
|
||||
|
||||
if ((accType & AccessType.Load) != 0)
|
||||
{
|
||||
if (size == DWordSizeLog2)
|
||||
{
|
||||
EmitDVectorLoad(context, address, op.Vd);
|
||||
}
|
||||
else
|
||||
{
|
||||
Operand vec = GetVecA32(op.Vd >> 2);
|
||||
EmitLoadSimd(context, address, vec, op.Vd >> 2, (op.Vd & 3) << (2 - size), size);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (size == DWordSizeLog2)
|
||||
{
|
||||
EmitDVectorStore(context, address, op.Vd);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitStoreSimd(context, address, op.Vd >> 2, (op.Vd & 3) << (2 - size), size);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
850
src/ARMeilleure/Instructions/InstEmitSimdMove.cs
Normal file
850
src/ARMeilleure/Instructions/InstEmitSimdMove.cs
Normal file
@@ -0,0 +1,850 @@
|
||||
using ARMeilleure.Decoders;
|
||||
using ARMeilleure.IntermediateRepresentation;
|
||||
using ARMeilleure.Translation;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
|
||||
using static ARMeilleure.Instructions.InstEmitHelper;
|
||||
using static ARMeilleure.Instructions.InstEmitSimdHelper;
|
||||
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
|
||||
|
||||
namespace ARMeilleure.Instructions
|
||||
{
|
||||
static partial class InstEmit
|
||||
{
|
||||
#region "Masks"
|
||||
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.VectorZeroUpper64(nShifted);
|
||||
}
|
||||
|
||||
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.VectorZeroUpper64(mShifted);
|
||||
}
|
||||
|
||||
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 d = GetVec(op.Rd);
|
||||
Operand n = GetIntOrZR(context, op.Rn);
|
||||
|
||||
context.Copy(d, EmitVectorInsert(context, d, 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 (Optimizations.UseSse2)
|
||||
{
|
||||
if (op.Size == 0)
|
||||
{
|
||||
context.Copy(GetVec(op.Rd), X86GetScalar(context, (int)op.Immediate));
|
||||
}
|
||||
else
|
||||
{
|
||||
context.Copy(GetVec(op.Rd), X86GetScalar(context, op.Immediate));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Operand e = Const(op.Immediate);
|
||||
|
||||
Operand res = context.VectorZero();
|
||||
|
||||
res = EmitVectorInsert(context, res, e, 0, op.Size + 2);
|
||||
|
||||
context.Copy(GetVec(op.Rd), res);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Fmov_Vi(ArmEmitterContext context)
|
||||
{
|
||||
OpCodeSimdImm op = (OpCodeSimdImm)context.CurrOp;
|
||||
|
||||
if (Optimizations.UseSse2)
|
||||
{
|
||||
if (op.RegisterSize == RegisterSize.Simd128)
|
||||
{
|
||||
context.Copy(GetVec(op.Rd), X86GetAllElements(context, op.Immediate));
|
||||
}
|
||||
else
|
||||
{
|
||||
context.Copy(GetVec(op.Rd), X86GetScalar(context, op.Immediate));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Operand e = Const(op.Immediate);
|
||||
|
||||
Operand res = context.VectorZero();
|
||||
|
||||
int elems = op.RegisterSize == RegisterSize.Simd128 ? 2 : 1;
|
||||
|
||||
for (int index = 0; index < elems; index++)
|
||||
{
|
||||
res = EmitVectorInsert(context, res, e, index, 3);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
EmitSse2VectorMoviMvniOp(context, not: false);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitVectorImmUnaryOp(context, (op1) => op1);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Mvni_V(ArmEmitterContext context)
|
||||
{
|
||||
if (Optimizations.UseSse2)
|
||||
{
|
||||
EmitSse2VectorMoviMvniOp(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)
|
||||
{
|
||||
EmitTableVectorLookup(context, isTbl: true);
|
||||
}
|
||||
|
||||
public static void Tbx_V(ArmEmitterContext context)
|
||||
{
|
||||
EmitTableVectorLookup(context, isTbl: false);
|
||||
}
|
||||
|
||||
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.VectorZeroUpper64(d);
|
||||
|
||||
Operand mask = X86GetAllElements(context, EvenMasks[op.Size]);
|
||||
|
||||
Operand res2 = context.AddIntrinsic(Intrinsic.X86Pshufb, GetVec(op.Rn), mask);
|
||||
|
||||
Intrinsic movInst = op.RegisterSize == RegisterSize.Simd128
|
||||
? Intrinsic.X86Movlhps
|
||||
: Intrinsic.X86Movhlps;
|
||||
|
||||
res = context.AddIntrinsic(movInst, res, res2);
|
||||
|
||||
context.Copy(d, res);
|
||||
}
|
||||
else
|
||||
{
|
||||
int elems = 8 >> op.Size;
|
||||
|
||||
int part = op.RegisterSize == RegisterSize.Simd128 ? elems : 0;
|
||||
|
||||
Operand d = GetVec(op.Rd);
|
||||
|
||||
Operand res = part == 0 ? context.VectorZero() : context.Copy(d);
|
||||
|
||||
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(d, 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 EmitSse2VectorMoviMvniOp(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 EmitTableVectorLookup(ArmEmitterContext context, bool isTbl)
|
||||
{
|
||||
OpCodeSimdTbl op = (OpCodeSimdTbl)context.CurrOp;
|
||||
|
||||
if (Optimizations.UseSsse3)
|
||||
{
|
||||
Operand d = GetVec(op.Rd);
|
||||
Operand m = GetVec(op.Rm);
|
||||
|
||||
Operand res;
|
||||
|
||||
Operand mask = X86GetAllElements(context, 0x0F0F0F0F0F0F0F0FL);
|
||||
|
||||
// Fast path for single register table.
|
||||
{
|
||||
Operand n = GetVec(op.Rn);
|
||||
|
||||
Operand mMask = context.AddIntrinsic(Intrinsic.X86Pcmpgtb, m, mask);
|
||||
mMask = context.AddIntrinsic(Intrinsic.X86Por, mMask, m);
|
||||
|
||||
res = context.AddIntrinsic(Intrinsic.X86Pshufb, n, mMask);
|
||||
}
|
||||
|
||||
for (int index = 1; index < op.Size; index++)
|
||||
{
|
||||
Operand ni = GetVec((op.Rn + index) & 0x1F);
|
||||
|
||||
Operand idxMask = X86GetAllElements(context, 0x1010101010101010L * index);
|
||||
|
||||
Operand mSubMask = context.AddIntrinsic(Intrinsic.X86Psubb, m, idxMask);
|
||||
|
||||
Operand mMask = context.AddIntrinsic(Intrinsic.X86Pcmpgtb, mSubMask, mask);
|
||||
mMask = context.AddIntrinsic(Intrinsic.X86Por, mMask, mSubMask);
|
||||
|
||||
Operand res2 = context.AddIntrinsic(Intrinsic.X86Pshufb, ni, mMask);
|
||||
|
||||
res = context.AddIntrinsic(Intrinsic.X86Por, res, res2);
|
||||
}
|
||||
|
||||
if (!isTbl)
|
||||
{
|
||||
Operand idxMask = X86GetAllElements(context, (0x1010101010101010L * op.Size) - 0x0101010101010101L);
|
||||
Operand zeroMask = context.VectorZero();
|
||||
|
||||
Operand mPosMask = context.AddIntrinsic(Intrinsic.X86Pcmpgtb, m, idxMask);
|
||||
Operand mNegMask = context.AddIntrinsic(Intrinsic.X86Pcmpgtb, zeroMask, m);
|
||||
|
||||
Operand mMask = context.AddIntrinsic(Intrinsic.X86Por, mPosMask, mNegMask);
|
||||
|
||||
Operand dMask = context.AddIntrinsic(Intrinsic.X86Pand, d, mMask);
|
||||
|
||||
res = context.AddIntrinsic(Intrinsic.X86Por, res, dMask);
|
||||
}
|
||||
|
||||
if (op.RegisterSize == RegisterSize.Simd64)
|
||||
{
|
||||
res = context.VectorZeroUpper64(res);
|
||||
}
|
||||
|
||||
context.Copy(d, res);
|
||||
}
|
||||
else
|
||||
{
|
||||
Operand d = GetVec(op.Rd);
|
||||
|
||||
List<Operand> args = new List<Operand>();
|
||||
|
||||
if (!isTbl)
|
||||
{
|
||||
args.Add(d);
|
||||
}
|
||||
|
||||
args.Add(GetVec(op.Rm));
|
||||
|
||||
args.Add(Const(op.RegisterSize == RegisterSize.Simd64 ? 8 : 16));
|
||||
|
||||
for (int index = 0; index < op.Size; index++)
|
||||
{
|
||||
args.Add(GetVec((op.Rn + index) & 0x1F));
|
||||
}
|
||||
|
||||
MethodInfo info = null;
|
||||
|
||||
if (isTbl)
|
||||
{
|
||||
switch (op.Size)
|
||||
{
|
||||
case 1: info = typeof(SoftFallback).GetMethod(nameof(SoftFallback.Tbl1)); break;
|
||||
case 2: info = typeof(SoftFallback).GetMethod(nameof(SoftFallback.Tbl2)); break;
|
||||
case 3: info = typeof(SoftFallback).GetMethod(nameof(SoftFallback.Tbl3)); break;
|
||||
case 4: info = typeof(SoftFallback).GetMethod(nameof(SoftFallback.Tbl4)); break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (op.Size)
|
||||
{
|
||||
case 1: info = typeof(SoftFallback).GetMethod(nameof(SoftFallback.Tbx1)); break;
|
||||
case 2: info = typeof(SoftFallback).GetMethod(nameof(SoftFallback.Tbx2)); break;
|
||||
case 3: info = typeof(SoftFallback).GetMethod(nameof(SoftFallback.Tbx3)); break;
|
||||
case 4: info = typeof(SoftFallback).GetMethod(nameof(SoftFallback.Tbx4)); break;
|
||||
}
|
||||
}
|
||||
|
||||
context.Copy(d, context.Call(info, args.ToArray()));
|
||||
}
|
||||
}
|
||||
|
||||
private static void EmitVectorTranspose(ArmEmitterContext context, int part)
|
||||
{
|
||||
OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp;
|
||||
|
||||
if (Optimizations.UseSsse3)
|
||||
{
|
||||
Operand mask = default;
|
||||
|
||||
if (op.Size < 3)
|
||||
{
|
||||
long maskE0 = EvenMasks[op.Size];
|
||||
long maskE1 = OddMasks [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 = default;
|
||||
|
||||
if (op.Size < 3)
|
||||
{
|
||||
long maskE0 = EvenMasks[op.Size];
|
||||
long maskE1 = OddMasks [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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
656
src/ARMeilleure/Instructions/InstEmitSimdMove32.cs
Normal file
656
src/ARMeilleure/Instructions/InstEmitSimdMove32.cs
Normal file
@@ -0,0 +1,656 @@
|
||||
using ARMeilleure.Decoders;
|
||||
using ARMeilleure.IntermediateRepresentation;
|
||||
using ARMeilleure.Translation;
|
||||
using System;
|
||||
|
||||
using static ARMeilleure.Instructions.InstEmitHelper;
|
||||
using static ARMeilleure.Instructions.InstEmitSimdHelper;
|
||||
using static ARMeilleure.Instructions.InstEmitSimdHelper32;
|
||||
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
|
||||
|
||||
namespace ARMeilleure.Instructions
|
||||
{
|
||||
static partial class InstEmit32
|
||||
{
|
||||
#region "Masks"
|
||||
// Same as InstEmitSimdMove, as the instructions do the same thing.
|
||||
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 Vmov_I(ArmEmitterContext context)
|
||||
{
|
||||
EmitVectorImmUnaryOp32(context, (op1) => op1);
|
||||
}
|
||||
|
||||
public static void Vmvn_I(ArmEmitterContext context)
|
||||
{
|
||||
if (Optimizations.UseAvx512Ortho)
|
||||
{
|
||||
EmitVectorUnaryOpSimd32(context, (op1) =>
|
||||
{
|
||||
return context.AddIntrinsic(Intrinsic.X86Vpternlogd, op1, op1, Const(0b01010101));
|
||||
});
|
||||
}
|
||||
else if (Optimizations.UseSse2)
|
||||
{
|
||||
EmitVectorUnaryOpSimd32(context, (op1) =>
|
||||
{
|
||||
Operand mask = X86GetAllElements(context, -1L);
|
||||
return context.AddIntrinsic(Intrinsic.X86Pandn, op1, mask);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitVectorUnaryOpZx32(context, (op1) => context.BitwiseNot(op1));
|
||||
}
|
||||
}
|
||||
|
||||
public static void Vmvn_II(ArmEmitterContext context)
|
||||
{
|
||||
EmitVectorImmUnaryOp32(context, (op1) => context.BitwiseNot(op1));
|
||||
}
|
||||
|
||||
public static void Vmov_GS(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32SimdMovGp op = (OpCode32SimdMovGp)context.CurrOp;
|
||||
|
||||
Operand vec = GetVecA32(op.Vn >> 2);
|
||||
if (op.Op == 1)
|
||||
{
|
||||
// To general purpose.
|
||||
Operand value = context.VectorExtract(OperandType.I32, vec, op.Vn & 0x3);
|
||||
SetIntA32(context, op.Rt, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
// From general purpose.
|
||||
Operand value = GetIntA32(context, op.Rt);
|
||||
context.Copy(vec, context.VectorInsert(vec, value, op.Vn & 0x3));
|
||||
}
|
||||
}
|
||||
|
||||
public static void Vmov_G1(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32SimdMovGpElem op = (OpCode32SimdMovGpElem)context.CurrOp;
|
||||
|
||||
int index = op.Index + ((op.Vd & 1) << (3 - op.Size));
|
||||
if (op.Op == 1)
|
||||
{
|
||||
// To general purpose.
|
||||
Operand value = EmitVectorExtract32(context, op.Vd >> 1, index, op.Size, !op.U);
|
||||
SetIntA32(context, op.Rt, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
// From general purpose.
|
||||
Operand vec = GetVecA32(op.Vd >> 1);
|
||||
Operand value = GetIntA32(context, op.Rt);
|
||||
context.Copy(vec, EmitVectorInsert(context, vec, value, index, op.Size));
|
||||
}
|
||||
}
|
||||
|
||||
public static void Vmov_G2(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32SimdMovGpDouble op = (OpCode32SimdMovGpDouble)context.CurrOp;
|
||||
|
||||
Operand vec = GetVecA32(op.Vm >> 2);
|
||||
int vm1 = op.Vm + 1;
|
||||
bool sameOwnerVec = (op.Vm >> 2) == (vm1 >> 2);
|
||||
Operand vec2 = sameOwnerVec ? vec : GetVecA32(vm1 >> 2);
|
||||
if (op.Op == 1)
|
||||
{
|
||||
// To general purpose.
|
||||
Operand lowValue = context.VectorExtract(OperandType.I32, vec, op.Vm & 3);
|
||||
SetIntA32(context, op.Rt, lowValue);
|
||||
|
||||
Operand highValue = context.VectorExtract(OperandType.I32, vec2, vm1 & 3);
|
||||
SetIntA32(context, op.Rt2, highValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
// From general purpose.
|
||||
Operand lowValue = GetIntA32(context, op.Rt);
|
||||
Operand resultVec = context.VectorInsert(vec, lowValue, op.Vm & 3);
|
||||
|
||||
Operand highValue = GetIntA32(context, op.Rt2);
|
||||
|
||||
if (sameOwnerVec)
|
||||
{
|
||||
context.Copy(vec, context.VectorInsert(resultVec, highValue, vm1 & 3));
|
||||
}
|
||||
else
|
||||
{
|
||||
context.Copy(vec, resultVec);
|
||||
context.Copy(vec2, context.VectorInsert(vec2, highValue, vm1 & 3));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void Vmov_GD(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32SimdMovGpDouble op = (OpCode32SimdMovGpDouble)context.CurrOp;
|
||||
|
||||
Operand vec = GetVecA32(op.Vm >> 1);
|
||||
if (op.Op == 1)
|
||||
{
|
||||
// To general purpose.
|
||||
Operand value = context.VectorExtract(OperandType.I64, vec, op.Vm & 1);
|
||||
SetIntA32(context, op.Rt, context.ConvertI64ToI32(value));
|
||||
SetIntA32(context, op.Rt2, context.ConvertI64ToI32(context.ShiftRightUI(value, Const(32))));
|
||||
}
|
||||
else
|
||||
{
|
||||
// From general purpose.
|
||||
Operand lowValue = GetIntA32(context, op.Rt);
|
||||
Operand highValue = GetIntA32(context, op.Rt2);
|
||||
|
||||
Operand value = context.BitwiseOr(
|
||||
context.ZeroExtend32(OperandType.I64, lowValue),
|
||||
context.ShiftLeft(context.ZeroExtend32(OperandType.I64, highValue), Const(32)));
|
||||
|
||||
context.Copy(vec, context.VectorInsert(vec, value, op.Vm & 1));
|
||||
}
|
||||
}
|
||||
|
||||
public static void Vmovl(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32SimdLong op = (OpCode32SimdLong)context.CurrOp;
|
||||
|
||||
Operand res = context.VectorZero();
|
||||
|
||||
int elems = op.GetBytesCount() >> op.Size;
|
||||
|
||||
for (int index = 0; index < elems; index++)
|
||||
{
|
||||
Operand me = EmitVectorExtract32(context, op.Qm, op.Im + index, op.Size, !op.U);
|
||||
|
||||
if (op.Size == 2)
|
||||
{
|
||||
if (op.U)
|
||||
{
|
||||
me = context.ZeroExtend32(OperandType.I64, me);
|
||||
}
|
||||
else
|
||||
{
|
||||
me = context.SignExtend32(OperandType.I64, me);
|
||||
}
|
||||
}
|
||||
|
||||
res = EmitVectorInsert(context, res, me, index, op.Size + 1);
|
||||
}
|
||||
|
||||
context.Copy(GetVecA32(op.Qd), res);
|
||||
}
|
||||
|
||||
public static void Vtbl(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32SimdTbl op = (OpCode32SimdTbl)context.CurrOp;
|
||||
|
||||
bool extension = op.Opc == 1;
|
||||
int length = op.Length + 1;
|
||||
|
||||
if (Optimizations.UseSsse3)
|
||||
{
|
||||
Operand d = GetVecA32(op.Qd);
|
||||
Operand m = EmitMoveDoubleWordToSide(context, GetVecA32(op.Qm), op.Vm, 0);
|
||||
|
||||
Operand res;
|
||||
Operand mask = X86GetAllElements(context, 0x0707070707070707L);
|
||||
|
||||
// Fast path for single register table.
|
||||
{
|
||||
Operand n = EmitMoveDoubleWordToSide(context, GetVecA32(op.Qn), op.Vn, 0);
|
||||
|
||||
Operand mMask = context.AddIntrinsic(Intrinsic.X86Pcmpgtb, m, mask);
|
||||
mMask = context.AddIntrinsic(Intrinsic.X86Por, mMask, m);
|
||||
|
||||
res = context.AddIntrinsic(Intrinsic.X86Pshufb, n, mMask);
|
||||
}
|
||||
|
||||
for (int index = 1; index < length; index++)
|
||||
{
|
||||
int newVn = (op.Vn + index) & 0x1F;
|
||||
(int qn, int ind) = GetQuadwordAndSubindex(newVn, op.RegisterSize);
|
||||
Operand ni = EmitMoveDoubleWordToSide(context, GetVecA32(qn), newVn, 0);
|
||||
|
||||
Operand idxMask = X86GetAllElements(context, 0x0808080808080808L * index);
|
||||
|
||||
Operand mSubMask = context.AddIntrinsic(Intrinsic.X86Psubb, m, idxMask);
|
||||
|
||||
Operand mMask = context.AddIntrinsic(Intrinsic.X86Pcmpgtb, mSubMask, mask);
|
||||
mMask = context.AddIntrinsic(Intrinsic.X86Por, mMask, mSubMask);
|
||||
|
||||
Operand res2 = context.AddIntrinsic(Intrinsic.X86Pshufb, ni, mMask);
|
||||
|
||||
res = context.AddIntrinsic(Intrinsic.X86Por, res, res2);
|
||||
}
|
||||
|
||||
if (extension)
|
||||
{
|
||||
Operand idxMask = X86GetAllElements(context, (0x0808080808080808L * length) - 0x0101010101010101L);
|
||||
Operand zeroMask = context.VectorZero();
|
||||
|
||||
Operand mPosMask = context.AddIntrinsic(Intrinsic.X86Pcmpgtb, m, idxMask);
|
||||
Operand mNegMask = context.AddIntrinsic(Intrinsic.X86Pcmpgtb, zeroMask, m);
|
||||
|
||||
Operand mMask = context.AddIntrinsic(Intrinsic.X86Por, mPosMask, mNegMask);
|
||||
|
||||
Operand dMask = context.AddIntrinsic(Intrinsic.X86Pand, EmitMoveDoubleWordToSide(context, d, op.Vd, 0), mMask);
|
||||
|
||||
res = context.AddIntrinsic(Intrinsic.X86Por, res, dMask);
|
||||
}
|
||||
|
||||
res = EmitMoveDoubleWordToSide(context, res, 0, op.Vd);
|
||||
|
||||
context.Copy(d, EmitDoubleWordInsert(context, d, res, op.Vd));
|
||||
}
|
||||
else
|
||||
{
|
||||
int elems = op.GetBytesCount() >> op.Size;
|
||||
|
||||
(int Qx, int Ix)[] tableTuples = new (int, int)[length];
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
tableTuples[i] = GetQuadwordAndSubindex(op.Vn + i, op.RegisterSize);
|
||||
}
|
||||
|
||||
int byteLength = length * 8;
|
||||
|
||||
Operand res = GetVecA32(op.Qd);
|
||||
Operand m = GetVecA32(op.Qm);
|
||||
|
||||
for (int index = 0; index < elems; index++)
|
||||
{
|
||||
Operand selectedIndex = context.ZeroExtend8(OperandType.I32, context.VectorExtract8(m, index + op.Im));
|
||||
|
||||
Operand inRange = context.ICompareLess(selectedIndex, Const(byteLength));
|
||||
Operand elemRes = default; // Note: This is I64 for ease of calculation.
|
||||
|
||||
// TODO: Branching rather than conditional select.
|
||||
|
||||
// Get indexed byte.
|
||||
// To simplify (ha) the il, we get bytes from every vector and use a nested conditional select to choose the right result.
|
||||
// This does have to extract `length` times for every element but certainly not as bad as it could be.
|
||||
|
||||
// Which vector number is the index on.
|
||||
Operand vecIndex = context.ShiftRightUI(selectedIndex, Const(3));
|
||||
// What should we shift by to extract it.
|
||||
Operand subVecIndexShift = context.ShiftLeft(context.BitwiseAnd(selectedIndex, Const(7)), Const(3));
|
||||
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
(int qx, int ix) = tableTuples[i];
|
||||
// Get the whole vector, we'll get a byte out of it.
|
||||
Operand lookupResult;
|
||||
if (qx == op.Qd)
|
||||
{
|
||||
// Result contains the current state of the vector.
|
||||
lookupResult = context.VectorExtract(OperandType.I64, res, ix);
|
||||
}
|
||||
else
|
||||
{
|
||||
lookupResult = EmitVectorExtract32(context, qx, ix, 3, false); // I64
|
||||
}
|
||||
|
||||
lookupResult = context.ShiftRightUI(lookupResult, subVecIndexShift); // Get the relevant byte from this vector.
|
||||
|
||||
if (i == 0)
|
||||
{
|
||||
elemRes = lookupResult; // First result is always default.
|
||||
}
|
||||
else
|
||||
{
|
||||
Operand isThisElem = context.ICompareEqual(vecIndex, Const(i));
|
||||
elemRes = context.ConditionalSelect(isThisElem, lookupResult, elemRes);
|
||||
}
|
||||
}
|
||||
|
||||
Operand fallback = (extension) ? context.ZeroExtend32(OperandType.I64, EmitVectorExtract32(context, op.Qd, index + op.Id, 0, false)) : Const(0L);
|
||||
|
||||
res = EmitVectorInsert(context, res, context.ConditionalSelect(inRange, elemRes, fallback), index + op.Id, 0);
|
||||
}
|
||||
|
||||
context.Copy(GetVecA32(op.Qd), res);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Vtrn(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32SimdCmpZ op = (OpCode32SimdCmpZ)context.CurrOp;
|
||||
|
||||
if (Optimizations.UseSsse3)
|
||||
{
|
||||
EmitVectorShuffleOpSimd32(context, (m, d) =>
|
||||
{
|
||||
Operand mask = default;
|
||||
|
||||
if (op.Size < 3)
|
||||
{
|
||||
long maskE0 = EvenMasks[op.Size];
|
||||
long maskE1 = OddMasks[op.Size];
|
||||
|
||||
mask = X86GetScalar(context, maskE0);
|
||||
|
||||
mask = EmitVectorInsert(context, mask, Const(maskE1), 1, 3);
|
||||
}
|
||||
|
||||
if (op.Size < 3)
|
||||
{
|
||||
d = context.AddIntrinsic(Intrinsic.X86Pshufb, d, mask);
|
||||
m = context.AddIntrinsic(Intrinsic.X86Pshufb, m, mask);
|
||||
}
|
||||
|
||||
Operand resD = context.AddIntrinsic(X86PunpcklInstruction[op.Size], d, m);
|
||||
Operand resM = context.AddIntrinsic(X86PunpckhInstruction[op.Size], d, m);
|
||||
|
||||
return (resM, resD);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
int elems = op.GetBytesCount() >> op.Size;
|
||||
int pairs = elems >> 1;
|
||||
|
||||
bool overlap = op.Qm == op.Qd;
|
||||
|
||||
Operand resD = GetVecA32(op.Qd);
|
||||
Operand resM = GetVecA32(op.Qm);
|
||||
|
||||
for (int index = 0; index < pairs; index++)
|
||||
{
|
||||
int pairIndex = index << 1;
|
||||
Operand d2 = EmitVectorExtract32(context, op.Qd, pairIndex + 1 + op.Id, op.Size, false);
|
||||
Operand m1 = EmitVectorExtract32(context, op.Qm, pairIndex + op.Im, op.Size, false);
|
||||
|
||||
resD = EmitVectorInsert(context, resD, m1, pairIndex + 1 + op.Id, op.Size);
|
||||
|
||||
if (overlap)
|
||||
{
|
||||
resM = resD;
|
||||
}
|
||||
|
||||
resM = EmitVectorInsert(context, resM, d2, pairIndex + op.Im, op.Size);
|
||||
|
||||
if (overlap)
|
||||
{
|
||||
resD = resM;
|
||||
}
|
||||
}
|
||||
|
||||
context.Copy(GetVecA32(op.Qd), resD);
|
||||
if (!overlap)
|
||||
{
|
||||
context.Copy(GetVecA32(op.Qm), resM);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void Vzip(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32SimdCmpZ op = (OpCode32SimdCmpZ)context.CurrOp;
|
||||
|
||||
if (Optimizations.UseAdvSimd)
|
||||
{
|
||||
EmitVectorZipUzpOpSimd32(context, Intrinsic.Arm64Zip1V, Intrinsic.Arm64Zip2V);
|
||||
}
|
||||
else if (Optimizations.UseSse2)
|
||||
{
|
||||
EmitVectorShuffleOpSimd32(context, (m, d) =>
|
||||
{
|
||||
if (op.RegisterSize == RegisterSize.Simd128)
|
||||
{
|
||||
Operand resD = context.AddIntrinsic(X86PunpcklInstruction[op.Size], d, m);
|
||||
Operand resM = context.AddIntrinsic(X86PunpckhInstruction[op.Size], d, m);
|
||||
|
||||
return (resM, resD);
|
||||
}
|
||||
else
|
||||
{
|
||||
Operand res = context.AddIntrinsic(X86PunpcklInstruction[op.Size], d, m);
|
||||
|
||||
Operand resD = context.AddIntrinsic(Intrinsic.X86Punpcklqdq, res, context.VectorZero());
|
||||
Operand resM = context.AddIntrinsic(Intrinsic.X86Punpckhqdq, res, context.VectorZero());
|
||||
return (resM, resD);
|
||||
}
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
int elems = op.GetBytesCount() >> op.Size;
|
||||
int pairs = elems >> 1;
|
||||
|
||||
bool overlap = op.Qm == op.Qd;
|
||||
|
||||
Operand resD = GetVecA32(op.Qd);
|
||||
Operand resM = GetVecA32(op.Qm);
|
||||
|
||||
for (int index = 0; index < pairs; index++)
|
||||
{
|
||||
int pairIndex = index << 1;
|
||||
Operand dRowD = EmitVectorExtract32(context, op.Qd, index + op.Id, op.Size, false);
|
||||
Operand mRowD = EmitVectorExtract32(context, op.Qm, index + op.Im, op.Size, false);
|
||||
|
||||
Operand dRowM = EmitVectorExtract32(context, op.Qd, index + op.Id + pairs, op.Size, false);
|
||||
Operand mRowM = EmitVectorExtract32(context, op.Qm, index + op.Im + pairs, op.Size, false);
|
||||
|
||||
resD = EmitVectorInsert(context, resD, dRowD, pairIndex + op.Id, op.Size);
|
||||
resD = EmitVectorInsert(context, resD, mRowD, pairIndex + 1 + op.Id, op.Size);
|
||||
|
||||
if (overlap)
|
||||
{
|
||||
resM = resD;
|
||||
}
|
||||
|
||||
resM = EmitVectorInsert(context, resM, dRowM, pairIndex + op.Im, op.Size);
|
||||
resM = EmitVectorInsert(context, resM, mRowM, pairIndex + 1 + op.Im, op.Size);
|
||||
|
||||
if (overlap)
|
||||
{
|
||||
resD = resM;
|
||||
}
|
||||
}
|
||||
|
||||
context.Copy(GetVecA32(op.Qd), resD);
|
||||
if (!overlap)
|
||||
{
|
||||
context.Copy(GetVecA32(op.Qm), resM);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void Vuzp(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32SimdCmpZ op = (OpCode32SimdCmpZ)context.CurrOp;
|
||||
|
||||
if (Optimizations.UseAdvSimd)
|
||||
{
|
||||
EmitVectorZipUzpOpSimd32(context, Intrinsic.Arm64Uzp1V, Intrinsic.Arm64Uzp2V);
|
||||
}
|
||||
else if (Optimizations.UseSsse3)
|
||||
{
|
||||
EmitVectorShuffleOpSimd32(context, (m, d) =>
|
||||
{
|
||||
if (op.RegisterSize == RegisterSize.Simd128)
|
||||
{
|
||||
Operand mask = default;
|
||||
|
||||
if (op.Size < 3)
|
||||
{
|
||||
long maskE0 = EvenMasks[op.Size];
|
||||
long maskE1 = OddMasks[op.Size];
|
||||
|
||||
mask = X86GetScalar(context, maskE0);
|
||||
mask = EmitVectorInsert(context, mask, Const(maskE1), 1, 3);
|
||||
|
||||
d = context.AddIntrinsic(Intrinsic.X86Pshufb, d, mask);
|
||||
m = context.AddIntrinsic(Intrinsic.X86Pshufb, m, mask);
|
||||
}
|
||||
|
||||
Operand resD = context.AddIntrinsic(Intrinsic.X86Punpcklqdq, d, m);
|
||||
Operand resM = context.AddIntrinsic(Intrinsic.X86Punpckhqdq, d, m);
|
||||
|
||||
return (resM, resD);
|
||||
}
|
||||
else
|
||||
{
|
||||
Intrinsic punpcklInst = X86PunpcklInstruction[op.Size];
|
||||
|
||||
Operand res = context.AddIntrinsic(punpcklInst, d, 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);
|
||||
}
|
||||
|
||||
Operand resD = context.AddIntrinsic(Intrinsic.X86Punpcklqdq, res, context.VectorZero());
|
||||
Operand resM = context.AddIntrinsic(Intrinsic.X86Punpckhqdq, res, context.VectorZero());
|
||||
|
||||
return (resM, resD);
|
||||
}
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
int elems = op.GetBytesCount() >> op.Size;
|
||||
int pairs = elems >> 1;
|
||||
|
||||
bool overlap = op.Qm == op.Qd;
|
||||
|
||||
Operand resD = GetVecA32(op.Qd);
|
||||
Operand resM = GetVecA32(op.Qm);
|
||||
|
||||
for (int index = 0; index < elems; index++)
|
||||
{
|
||||
Operand dIns, mIns;
|
||||
if (index >= pairs)
|
||||
{
|
||||
int pairIndex = index - pairs;
|
||||
dIns = EmitVectorExtract32(context, op.Qm, (pairIndex << 1) + op.Im, op.Size, false);
|
||||
mIns = EmitVectorExtract32(context, op.Qm, ((pairIndex << 1) | 1) + op.Im, op.Size, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
dIns = EmitVectorExtract32(context, op.Qd, (index << 1) + op.Id, op.Size, false);
|
||||
mIns = EmitVectorExtract32(context, op.Qd, ((index << 1) | 1) + op.Id, op.Size, false);
|
||||
}
|
||||
|
||||
resD = EmitVectorInsert(context, resD, dIns, index + op.Id, op.Size);
|
||||
|
||||
if (overlap)
|
||||
{
|
||||
resM = resD;
|
||||
}
|
||||
|
||||
resM = EmitVectorInsert(context, resM, mIns, index + op.Im, op.Size);
|
||||
|
||||
if (overlap)
|
||||
{
|
||||
resD = resM;
|
||||
}
|
||||
}
|
||||
|
||||
context.Copy(GetVecA32(op.Qd), resD);
|
||||
if (!overlap)
|
||||
{
|
||||
context.Copy(GetVecA32(op.Qm), resM);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void EmitVectorZipUzpOpSimd32(ArmEmitterContext context, Intrinsic inst1, Intrinsic inst2)
|
||||
{
|
||||
OpCode32SimdCmpZ op = (OpCode32SimdCmpZ)context.CurrOp;
|
||||
|
||||
bool overlap = op.Qm == op.Qd;
|
||||
|
||||
Operand d = GetVecA32(op.Qd);
|
||||
Operand m = GetVecA32(op.Qm);
|
||||
|
||||
Operand dPart = d;
|
||||
Operand mPart = m;
|
||||
|
||||
if (!op.Q) // Register swap: move relevant doubleword to destination side.
|
||||
{
|
||||
dPart = InstEmitSimdHelper32Arm64.EmitMoveDoubleWordToSide(context, d, op.Vd, 0);
|
||||
mPart = InstEmitSimdHelper32Arm64.EmitMoveDoubleWordToSide(context, m, op.Vm, 0);
|
||||
}
|
||||
|
||||
Intrinsic vSize = op.Q ? Intrinsic.Arm64V128 : Intrinsic.Arm64V64;
|
||||
|
||||
vSize |= (Intrinsic)(op.Size << (int)Intrinsic.Arm64VSizeShift);
|
||||
|
||||
Operand resD = context.AddIntrinsic(inst1 | vSize, dPart, mPart);
|
||||
Operand resM = context.AddIntrinsic(inst2 | vSize, dPart, mPart);
|
||||
|
||||
if (!op.Q) // Register insert.
|
||||
{
|
||||
resD = context.AddIntrinsic(Intrinsic.Arm64InsVe | Intrinsic.Arm64VDWord, d, Const(op.Vd & 1), resD, Const(0));
|
||||
|
||||
if (overlap)
|
||||
{
|
||||
resD = context.AddIntrinsic(Intrinsic.Arm64InsVe | Intrinsic.Arm64VDWord, resD, Const(op.Vm & 1), resM, Const(0));
|
||||
}
|
||||
else
|
||||
{
|
||||
resM = context.AddIntrinsic(Intrinsic.Arm64InsVe | Intrinsic.Arm64VDWord, m, Const(op.Vm & 1), resM, Const(0));
|
||||
}
|
||||
}
|
||||
|
||||
context.Copy(d, resD);
|
||||
if (!overlap)
|
||||
{
|
||||
context.Copy(m, resM);
|
||||
}
|
||||
}
|
||||
|
||||
private static void EmitVectorShuffleOpSimd32(ArmEmitterContext context, Func<Operand, Operand, (Operand, Operand)> shuffleFunc)
|
||||
{
|
||||
OpCode32Simd op = (OpCode32Simd)context.CurrOp;
|
||||
|
||||
Operand m = GetVecA32(op.Qm);
|
||||
Operand d = GetVecA32(op.Qd);
|
||||
Operand initialM = m;
|
||||
Operand initialD = d;
|
||||
|
||||
if (!op.Q) // Register swap: move relevant doubleword to side 0, for consistency.
|
||||
{
|
||||
m = EmitMoveDoubleWordToSide(context, m, op.Vm, 0);
|
||||
d = EmitMoveDoubleWordToSide(context, d, op.Vd, 0);
|
||||
}
|
||||
|
||||
(Operand resM, Operand resD) = shuffleFunc(m, d);
|
||||
|
||||
bool overlap = op.Qm == op.Qd;
|
||||
|
||||
if (!op.Q) // Register insert.
|
||||
{
|
||||
resM = EmitDoubleWordInsert(context, initialM, EmitMoveDoubleWordToSide(context, resM, 0, op.Vm), op.Vm);
|
||||
resD = EmitDoubleWordInsert(context, overlap ? resM : initialD, EmitMoveDoubleWordToSide(context, resD, 0, op.Vd), op.Vd);
|
||||
}
|
||||
|
||||
if (!overlap)
|
||||
{
|
||||
context.Copy(initialM, resM);
|
||||
}
|
||||
|
||||
context.Copy(initialD, resD);
|
||||
}
|
||||
}
|
||||
}
|
||||
1827
src/ARMeilleure/Instructions/InstEmitSimdShift.cs
Normal file
1827
src/ARMeilleure/Instructions/InstEmitSimdShift.cs
Normal file
File diff suppressed because it is too large
Load Diff
389
src/ARMeilleure/Instructions/InstEmitSimdShift32.cs
Normal file
389
src/ARMeilleure/Instructions/InstEmitSimdShift32.cs
Normal file
@@ -0,0 +1,389 @@
|
||||
using ARMeilleure.Decoders;
|
||||
using ARMeilleure.IntermediateRepresentation;
|
||||
using ARMeilleure.State;
|
||||
using ARMeilleure.Translation;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Reflection;
|
||||
|
||||
using static ARMeilleure.Instructions.InstEmitHelper;
|
||||
using static ARMeilleure.Instructions.InstEmitSimdHelper;
|
||||
using static ARMeilleure.Instructions.InstEmitSimdHelper32;
|
||||
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
|
||||
|
||||
namespace ARMeilleure.Instructions
|
||||
{
|
||||
static partial class InstEmit32
|
||||
{
|
||||
public static void Vqrshrn(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32SimdShImm op = (OpCode32SimdShImm)context.CurrOp;
|
||||
|
||||
EmitRoundShrImmSaturatingNarrowOp(context, op.U ? ShrImmSaturatingNarrowFlags.VectorZxZx : ShrImmSaturatingNarrowFlags.VectorSxSx);
|
||||
}
|
||||
|
||||
public static void Vqrshrun(ArmEmitterContext context)
|
||||
{
|
||||
EmitRoundShrImmSaturatingNarrowOp(context, ShrImmSaturatingNarrowFlags.VectorSxZx);
|
||||
}
|
||||
|
||||
public static void Vqshrn(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32SimdShImm op = (OpCode32SimdShImm)context.CurrOp;
|
||||
|
||||
EmitShrImmSaturatingNarrowOp(context, op.U ? ShrImmSaturatingNarrowFlags.VectorZxZx : ShrImmSaturatingNarrowFlags.VectorSxSx);
|
||||
}
|
||||
|
||||
public static void Vqshrun(ArmEmitterContext context)
|
||||
{
|
||||
EmitShrImmSaturatingNarrowOp(context, ShrImmSaturatingNarrowFlags.VectorSxZx);
|
||||
}
|
||||
|
||||
public static void Vrshr(ArmEmitterContext context)
|
||||
{
|
||||
EmitRoundShrImmOp(context, accumulate: false);
|
||||
}
|
||||
|
||||
public static void Vrshrn(ArmEmitterContext context)
|
||||
{
|
||||
EmitRoundShrImmNarrowOp(context, signed: false);
|
||||
}
|
||||
|
||||
public static void Vrsra(ArmEmitterContext context)
|
||||
{
|
||||
EmitRoundShrImmOp(context, accumulate: true);
|
||||
}
|
||||
|
||||
public static void Vshl(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32SimdShImm op = (OpCode32SimdShImm)context.CurrOp;
|
||||
|
||||
EmitVectorUnaryOpZx32(context, (op1) => context.ShiftLeft(op1, Const(op.Shift)));
|
||||
}
|
||||
|
||||
public static void Vshl_I(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32SimdReg op = (OpCode32SimdReg)context.CurrOp;
|
||||
|
||||
if (op.U)
|
||||
{
|
||||
EmitVectorBinaryOpZx32(context, (op1, op2) => EmitShlRegOp(context, op2, op1, op.Size, true));
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitVectorBinaryOpSx32(context, (op1, op2) => EmitShlRegOp(context, op2, op1, op.Size, false));
|
||||
}
|
||||
}
|
||||
|
||||
public static void Vshll(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32SimdShImmLong op = (OpCode32SimdShImmLong)context.CurrOp;
|
||||
|
||||
Operand res = context.VectorZero();
|
||||
|
||||
int elems = op.GetBytesCount() >> op.Size;
|
||||
|
||||
for (int index = 0; index < elems; index++)
|
||||
{
|
||||
Operand me = EmitVectorExtract32(context, op.Qm, op.Im + index, op.Size, !op.U);
|
||||
|
||||
if (op.Size == 2)
|
||||
{
|
||||
if (op.U)
|
||||
{
|
||||
me = context.ZeroExtend32(OperandType.I64, me);
|
||||
}
|
||||
else
|
||||
{
|
||||
me = context.SignExtend32(OperandType.I64, me);
|
||||
}
|
||||
}
|
||||
|
||||
me = context.ShiftLeft(me, Const(op.Shift));
|
||||
|
||||
res = EmitVectorInsert(context, res, me, index, op.Size + 1);
|
||||
}
|
||||
|
||||
context.Copy(GetVecA32(op.Qd), res);
|
||||
}
|
||||
|
||||
public static void Vshr(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32SimdShImm op = (OpCode32SimdShImm)context.CurrOp;
|
||||
int shift = GetImmShr(op);
|
||||
int maxShift = (8 << op.Size) - 1;
|
||||
|
||||
if (op.U)
|
||||
{
|
||||
EmitVectorUnaryOpZx32(context, (op1) => (shift > maxShift) ? Const(op1.Type, 0) : context.ShiftRightUI(op1, Const(shift)));
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitVectorUnaryOpSx32(context, (op1) => context.ShiftRightSI(op1, Const(Math.Min(maxShift, shift))));
|
||||
}
|
||||
}
|
||||
|
||||
public static void Vshrn(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32SimdShImm op = (OpCode32SimdShImm)context.CurrOp;
|
||||
int shift = GetImmShr(op);
|
||||
|
||||
EmitVectorUnaryNarrowOp32(context, (op1) => context.ShiftRightUI(op1, Const(shift)));
|
||||
}
|
||||
|
||||
public static void Vsra(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32SimdShImm op = (OpCode32SimdShImm)context.CurrOp;
|
||||
int shift = GetImmShr(op);
|
||||
int maxShift = (8 << op.Size) - 1;
|
||||
|
||||
if (op.U)
|
||||
{
|
||||
EmitVectorImmBinaryQdQmOpZx32(context, (op1, op2) =>
|
||||
{
|
||||
Operand shiftRes = shift > maxShift ? Const(op2.Type, 0) : context.ShiftRightUI(op2, Const(shift));
|
||||
|
||||
return context.Add(op1, shiftRes);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitVectorImmBinaryQdQmOpSx32(context, (op1, op2) => context.Add(op1, context.ShiftRightSI(op2, Const(Math.Min(maxShift, shift)))));
|
||||
}
|
||||
}
|
||||
|
||||
public static void EmitRoundShrImmOp(ArmEmitterContext context, bool accumulate)
|
||||
{
|
||||
OpCode32SimdShImm op = (OpCode32SimdShImm)context.CurrOp;
|
||||
int shift = GetImmShr(op);
|
||||
long roundConst = 1L << (shift - 1);
|
||||
|
||||
if (op.U)
|
||||
{
|
||||
if (op.Size < 2)
|
||||
{
|
||||
EmitVectorUnaryOpZx32(context, (op1) =>
|
||||
{
|
||||
op1 = context.Add(op1, Const(op1.Type, roundConst));
|
||||
|
||||
return context.ShiftRightUI(op1, Const(shift));
|
||||
}, accumulate);
|
||||
}
|
||||
else if (op.Size == 2)
|
||||
{
|
||||
EmitVectorUnaryOpZx32(context, (op1) =>
|
||||
{
|
||||
op1 = context.ZeroExtend32(OperandType.I64, op1);
|
||||
op1 = context.Add(op1, Const(op1.Type, roundConst));
|
||||
|
||||
return context.ConvertI64ToI32(context.ShiftRightUI(op1, Const(shift)));
|
||||
}, accumulate);
|
||||
}
|
||||
else /* if (op.Size == 3) */
|
||||
{
|
||||
EmitVectorUnaryOpZx32(context, (op1) => EmitShrImm64(context, op1, signed: false, roundConst, shift), accumulate);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (op.Size < 2)
|
||||
{
|
||||
EmitVectorUnaryOpSx32(context, (op1) =>
|
||||
{
|
||||
op1 = context.Add(op1, Const(op1.Type, roundConst));
|
||||
|
||||
return context.ShiftRightSI(op1, Const(shift));
|
||||
}, accumulate);
|
||||
}
|
||||
else if (op.Size == 2)
|
||||
{
|
||||
EmitVectorUnaryOpSx32(context, (op1) =>
|
||||
{
|
||||
op1 = context.SignExtend32(OperandType.I64, op1);
|
||||
op1 = context.Add(op1, Const(op1.Type, roundConst));
|
||||
|
||||
return context.ConvertI64ToI32(context.ShiftRightSI(op1, Const(shift)));
|
||||
}, accumulate);
|
||||
}
|
||||
else /* if (op.Size == 3) */
|
||||
{
|
||||
EmitVectorUnaryOpZx32(context, (op1) => EmitShrImm64(context, op1, signed: true, roundConst, shift), accumulate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void EmitRoundShrImmNarrowOp(ArmEmitterContext context, bool signed)
|
||||
{
|
||||
OpCode32SimdShImm op = (OpCode32SimdShImm)context.CurrOp;
|
||||
|
||||
int shift = GetImmShr(op);
|
||||
long roundConst = 1L << (shift - 1);
|
||||
|
||||
EmitVectorUnaryNarrowOp32(context, (op1) =>
|
||||
{
|
||||
if (op.Size <= 1)
|
||||
{
|
||||
op1 = context.Add(op1, Const(op1.Type, roundConst));
|
||||
op1 = signed ? context.ShiftRightSI(op1, Const(shift)) : context.ShiftRightUI(op1, Const(shift));
|
||||
}
|
||||
else /* if (op.Size == 2 && round) */
|
||||
{
|
||||
op1 = EmitShrImm64(context, op1, signed, roundConst, shift); // shift <= 32
|
||||
}
|
||||
|
||||
return op1;
|
||||
}, signed);
|
||||
}
|
||||
|
||||
private static Operand EmitShlRegOp(ArmEmitterContext context, Operand op, Operand shiftLsB, int size, bool unsigned)
|
||||
{
|
||||
if (shiftLsB.Type == OperandType.I64)
|
||||
{
|
||||
shiftLsB = context.ConvertI64ToI32(shiftLsB);
|
||||
}
|
||||
|
||||
shiftLsB = context.SignExtend8(OperandType.I32, shiftLsB);
|
||||
Debug.Assert((uint)size < 4u);
|
||||
|
||||
Operand negShiftLsB = context.Negate(shiftLsB);
|
||||
|
||||
Operand isPositive = context.ICompareGreaterOrEqual(shiftLsB, Const(0));
|
||||
|
||||
Operand shl = context.ShiftLeft(op, shiftLsB);
|
||||
Operand shr = unsigned ? context.ShiftRightUI(op, negShiftLsB) : context.ShiftRightSI(op, negShiftLsB);
|
||||
|
||||
Operand res = context.ConditionalSelect(isPositive, shl, shr);
|
||||
|
||||
if (unsigned)
|
||||
{
|
||||
Operand isOutOfRange = context.BitwiseOr(
|
||||
context.ICompareGreaterOrEqual(shiftLsB, Const(8 << size)),
|
||||
context.ICompareGreaterOrEqual(negShiftLsB, Const(8 << size)));
|
||||
|
||||
return context.ConditionalSelect(isOutOfRange, Const(op.Type, 0), res);
|
||||
}
|
||||
else
|
||||
{
|
||||
Operand isOutOfRange0 = context.ICompareGreaterOrEqual(shiftLsB, Const(8 << size));
|
||||
Operand isOutOfRangeN = context.ICompareGreaterOrEqual(negShiftLsB, Const(8 << size));
|
||||
|
||||
// Also zero if shift is too negative, but value was positive.
|
||||
isOutOfRange0 = context.BitwiseOr(isOutOfRange0, context.BitwiseAnd(isOutOfRangeN, context.ICompareGreaterOrEqual(op, Const(op.Type, 0))));
|
||||
|
||||
Operand min = (op.Type == OperandType.I64) ? Const(-1L) : Const(-1);
|
||||
|
||||
return context.ConditionalSelect(isOutOfRange0, Const(op.Type, 0), context.ConditionalSelect(isOutOfRangeN, min, res));
|
||||
}
|
||||
}
|
||||
|
||||
[Flags]
|
||||
private enum ShrImmSaturatingNarrowFlags
|
||||
{
|
||||
Scalar = 1 << 0,
|
||||
SignedSrc = 1 << 1,
|
||||
SignedDst = 1 << 2,
|
||||
|
||||
Round = 1 << 3,
|
||||
|
||||
ScalarSxSx = Scalar | SignedSrc | SignedDst,
|
||||
ScalarSxZx = Scalar | SignedSrc,
|
||||
ScalarZxZx = Scalar,
|
||||
|
||||
VectorSxSx = SignedSrc | SignedDst,
|
||||
VectorSxZx = SignedSrc,
|
||||
VectorZxZx = 0
|
||||
}
|
||||
|
||||
private static void EmitRoundShrImmSaturatingNarrowOp(ArmEmitterContext context, ShrImmSaturatingNarrowFlags flags)
|
||||
{
|
||||
EmitShrImmSaturatingNarrowOp(context, ShrImmSaturatingNarrowFlags.Round | flags);
|
||||
}
|
||||
|
||||
private static void EmitShrImmSaturatingNarrowOp(ArmEmitterContext context, ShrImmSaturatingNarrowFlags flags)
|
||||
{
|
||||
OpCode32SimdShImm op = (OpCode32SimdShImm)context.CurrOp;
|
||||
|
||||
bool scalar = (flags & ShrImmSaturatingNarrowFlags.Scalar) != 0;
|
||||
bool signedSrc = (flags & ShrImmSaturatingNarrowFlags.SignedSrc) != 0;
|
||||
bool signedDst = (flags & ShrImmSaturatingNarrowFlags.SignedDst) != 0;
|
||||
bool round = (flags & ShrImmSaturatingNarrowFlags.Round) != 0;
|
||||
|
||||
if (scalar)
|
||||
{
|
||||
// TODO: Support scalar operation.
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
int shift = GetImmShr(op);
|
||||
long roundConst = 1L << (shift - 1);
|
||||
|
||||
EmitVectorUnaryNarrowOp32(context, (op1) =>
|
||||
{
|
||||
if (op.Size <= 1 || !round)
|
||||
{
|
||||
if (round)
|
||||
{
|
||||
op1 = context.Add(op1, Const(op1.Type, roundConst));
|
||||
}
|
||||
|
||||
op1 = signedSrc ? context.ShiftRightSI(op1, Const(shift)) : context.ShiftRightUI(op1, Const(shift));
|
||||
}
|
||||
else /* if (op.Size == 2 && round) */
|
||||
{
|
||||
op1 = EmitShrImm64(context, op1, signedSrc, roundConst, shift); // shift <= 32
|
||||
}
|
||||
|
||||
return EmitSatQ(context, op1, 8 << op.Size, signedSrc, signedDst);
|
||||
}, signedSrc);
|
||||
}
|
||||
|
||||
private static int GetImmShr(OpCode32SimdShImm op)
|
||||
{
|
||||
return (8 << op.Size) - op.Shift; // Shr amount is flipped.
|
||||
}
|
||||
|
||||
// dst64 = (Int(src64, signed) + roundConst) >> shift;
|
||||
private static Operand EmitShrImm64(
|
||||
ArmEmitterContext context,
|
||||
Operand value,
|
||||
bool signed,
|
||||
long roundConst,
|
||||
int shift)
|
||||
{
|
||||
MethodInfo info = signed
|
||||
? typeof(SoftFallback).GetMethod(nameof(SoftFallback.SignedShrImm64))
|
||||
: typeof(SoftFallback).GetMethod(nameof(SoftFallback.UnsignedShrImm64));
|
||||
|
||||
return context.Call(info, value, Const(roundConst), Const(shift));
|
||||
}
|
||||
|
||||
private static Operand EmitSatQ(ArmEmitterContext context, Operand value, int eSize, bool signedSrc, bool signedDst)
|
||||
{
|
||||
Debug.Assert(eSize <= 32);
|
||||
|
||||
long intMin = signedDst ? -(1L << (eSize - 1)) : 0;
|
||||
long intMax = signedDst ? (1L << (eSize - 1)) - 1 : (1L << eSize) - 1;
|
||||
|
||||
Operand gt = signedSrc
|
||||
? context.ICompareGreater(value, Const(value.Type, intMax))
|
||||
: context.ICompareGreaterUI(value, Const(value.Type, intMax));
|
||||
|
||||
Operand lt = signedSrc
|
||||
? context.ICompareLess(value, Const(value.Type, intMin))
|
||||
: context.ICompareLessUI(value, Const(value.Type, intMin));
|
||||
|
||||
value = context.ConditionalSelect(gt, Const(value.Type, intMax), value);
|
||||
value = context.ConditionalSelect(lt, Const(value.Type, intMin), value);
|
||||
|
||||
Operand lblNoSat = Label();
|
||||
|
||||
context.BranchIfFalse(lblNoSat, context.BitwiseOr(gt, lt));
|
||||
|
||||
SetFpFlag(context, FPState.QcFlag, Const(1));
|
||||
|
||||
context.MarkLabel(lblNoSat);
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
248
src/ARMeilleure/Instructions/InstEmitSystem.cs
Normal file
248
src/ARMeilleure/Instructions/InstEmitSystem.cs
Normal file
@@ -0,0 +1,248 @@
|
||||
using ARMeilleure.Decoders;
|
||||
using ARMeilleure.IntermediateRepresentation;
|
||||
using ARMeilleure.State;
|
||||
using ARMeilleure.Translation;
|
||||
using System;
|
||||
using System.Reflection;
|
||||
|
||||
using static ARMeilleure.Instructions.InstEmitHelper;
|
||||
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
|
||||
|
||||
namespace ARMeilleure.Instructions
|
||||
{
|
||||
static partial class InstEmit
|
||||
{
|
||||
private const int DczSizeLog2 = 4; // Log2 size in words
|
||||
public const int DczSizeInBytes = 4 << DczSizeLog2;
|
||||
|
||||
public static void Isb(ArmEmitterContext context)
|
||||
{
|
||||
// Execute as no-op.
|
||||
}
|
||||
|
||||
public static void Mrs(ArmEmitterContext context)
|
||||
{
|
||||
OpCodeSystem op = (OpCodeSystem)context.CurrOp;
|
||||
|
||||
MethodInfo info;
|
||||
|
||||
switch (GetPackedId(op))
|
||||
{
|
||||
case 0b11_011_0000_0000_001: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCtrEl0)); break;
|
||||
case 0b11_011_0000_0000_111: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetDczidEl0)); break;
|
||||
case 0b11_011_0100_0010_000: EmitGetNzcv(context); return;
|
||||
case 0b11_011_0100_0100_000: EmitGetFpcr(context); return;
|
||||
case 0b11_011_0100_0100_001: EmitGetFpsr(context); return;
|
||||
case 0b11_011_1101_0000_010: EmitGetTpidrEl0(context); return;
|
||||
case 0b11_011_1101_0000_011: EmitGetTpidrroEl0(context); return;
|
||||
case 0b11_011_1110_0000_000: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCntfrqEl0)); break;
|
||||
case 0b11_011_1110_0000_001: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCntpctEl0)); break;
|
||||
case 0b11_011_1110_0000_010: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCntvctEl0)); break;
|
||||
|
||||
default: throw new NotImplementedException($"Unknown MRS 0x{op.RawOpCode:X8} at 0x{op.Address:X16}.");
|
||||
}
|
||||
|
||||
SetIntOrZR(context, op.Rt, context.Call(info));
|
||||
}
|
||||
|
||||
public static void Msr(ArmEmitterContext context)
|
||||
{
|
||||
OpCodeSystem op = (OpCodeSystem)context.CurrOp;
|
||||
|
||||
switch (GetPackedId(op))
|
||||
{
|
||||
case 0b11_011_0100_0010_000: EmitSetNzcv(context); return;
|
||||
case 0b11_011_0100_0100_000: EmitSetFpcr(context); return;
|
||||
case 0b11_011_0100_0100_001: EmitSetFpsr(context); return;
|
||||
case 0b11_011_1101_0000_010: EmitSetTpidrEl0(context); return;
|
||||
|
||||
default: throw new NotImplementedException($"Unknown MSR 0x{op.RawOpCode:X8} at 0x{op.Address:X16}.");
|
||||
}
|
||||
}
|
||||
|
||||
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 < DczSizeInBytes; offset += 8)
|
||||
{
|
||||
Operand address = context.Add(t, Const(offset));
|
||||
|
||||
InstEmitMemoryHelper.EmitStore(context, address, RegisterConsts.ZeroIndex, 3);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// No-op
|
||||
case 0b11_011_0111_1110_001: // DC CIVAC
|
||||
break;
|
||||
|
||||
case 0b11_011_0111_0101_001: // IC IVAU
|
||||
Operand target = Register(op.Rt, RegisterType.Integer, OperandType.I64);
|
||||
context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.InvalidateCacheLine)), target);
|
||||
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;
|
||||
}
|
||||
|
||||
private static void EmitGetNzcv(ArmEmitterContext context)
|
||||
{
|
||||
OpCodeSystem op = (OpCodeSystem)context.CurrOp;
|
||||
|
||||
Operand nzcv = context.ShiftLeft(GetFlag(PState.VFlag), Const((int)PState.VFlag));
|
||||
nzcv = context.BitwiseOr(nzcv, context.ShiftLeft(GetFlag(PState.CFlag), Const((int)PState.CFlag)));
|
||||
nzcv = context.BitwiseOr(nzcv, context.ShiftLeft(GetFlag(PState.ZFlag), Const((int)PState.ZFlag)));
|
||||
nzcv = context.BitwiseOr(nzcv, context.ShiftLeft(GetFlag(PState.NFlag), Const((int)PState.NFlag)));
|
||||
|
||||
SetIntOrZR(context, op.Rt, nzcv);
|
||||
}
|
||||
|
||||
private static void EmitGetFpcr(ArmEmitterContext context)
|
||||
{
|
||||
OpCodeSystem op = (OpCodeSystem)context.CurrOp;
|
||||
|
||||
Operand fpcr = Const(0);
|
||||
|
||||
for (int flag = 0; flag < RegisterConsts.FpFlagsCount; flag++)
|
||||
{
|
||||
if (FPCR.Mask.HasFlag((FPCR)(1u << flag)))
|
||||
{
|
||||
fpcr = context.BitwiseOr(fpcr, context.ShiftLeft(GetFpFlag((FPState)flag), Const(flag)));
|
||||
}
|
||||
}
|
||||
|
||||
SetIntOrZR(context, op.Rt, fpcr);
|
||||
}
|
||||
|
||||
private static void EmitGetFpsr(ArmEmitterContext context)
|
||||
{
|
||||
OpCodeSystem op = (OpCodeSystem)context.CurrOp;
|
||||
|
||||
context.SyncQcFlag();
|
||||
|
||||
Operand fpsr = Const(0);
|
||||
|
||||
for (int flag = 0; flag < RegisterConsts.FpFlagsCount; flag++)
|
||||
{
|
||||
if (FPSR.Mask.HasFlag((FPSR)(1u << flag)))
|
||||
{
|
||||
fpsr = context.BitwiseOr(fpsr, context.ShiftLeft(GetFpFlag((FPState)flag), Const(flag)));
|
||||
}
|
||||
}
|
||||
|
||||
SetIntOrZR(context, op.Rt, fpsr);
|
||||
}
|
||||
|
||||
private static void EmitGetTpidrEl0(ArmEmitterContext context)
|
||||
{
|
||||
OpCodeSystem op = (OpCodeSystem)context.CurrOp;
|
||||
|
||||
Operand nativeContext = context.LoadArgument(OperandType.I64, 0);
|
||||
|
||||
Operand result = context.Load(OperandType.I64, context.Add(nativeContext, Const((ulong)NativeContext.GetTpidrEl0Offset())));
|
||||
|
||||
SetIntOrZR(context, op.Rt, result);
|
||||
}
|
||||
|
||||
private static void EmitGetTpidrroEl0(ArmEmitterContext context)
|
||||
{
|
||||
OpCodeSystem op = (OpCodeSystem)context.CurrOp;
|
||||
|
||||
Operand nativeContext = context.LoadArgument(OperandType.I64, 0);
|
||||
|
||||
Operand result = context.Load(OperandType.I64, context.Add(nativeContext, Const((ulong)NativeContext.GetTpidrroEl0Offset())));
|
||||
|
||||
SetIntOrZR(context, op.Rt, result);
|
||||
}
|
||||
|
||||
private static void EmitSetNzcv(ArmEmitterContext context)
|
||||
{
|
||||
OpCodeSystem op = (OpCodeSystem)context.CurrOp;
|
||||
|
||||
Operand nzcv = GetIntOrZR(context, op.Rt);
|
||||
nzcv = context.ConvertI64ToI32(nzcv);
|
||||
|
||||
SetFlag(context, PState.VFlag, context.BitwiseAnd(context.ShiftRightUI(nzcv, Const((int)PState.VFlag)), Const(1)));
|
||||
SetFlag(context, PState.CFlag, context.BitwiseAnd(context.ShiftRightUI(nzcv, Const((int)PState.CFlag)), Const(1)));
|
||||
SetFlag(context, PState.ZFlag, context.BitwiseAnd(context.ShiftRightUI(nzcv, Const((int)PState.ZFlag)), Const(1)));
|
||||
SetFlag(context, PState.NFlag, context.BitwiseAnd(context.ShiftRightUI(nzcv, Const((int)PState.NFlag)), Const(1)));
|
||||
}
|
||||
|
||||
private static void EmitSetFpcr(ArmEmitterContext context)
|
||||
{
|
||||
OpCodeSystem op = (OpCodeSystem)context.CurrOp;
|
||||
|
||||
Operand fpcr = GetIntOrZR(context, op.Rt);
|
||||
fpcr = context.ConvertI64ToI32(fpcr);
|
||||
|
||||
for (int flag = 0; flag < RegisterConsts.FpFlagsCount; flag++)
|
||||
{
|
||||
if (FPCR.Mask.HasFlag((FPCR)(1u << flag)))
|
||||
{
|
||||
SetFpFlag(context, (FPState)flag, context.BitwiseAnd(context.ShiftRightUI(fpcr, Const(flag)), Const(1)));
|
||||
}
|
||||
}
|
||||
|
||||
context.UpdateArmFpMode();
|
||||
}
|
||||
|
||||
private static void EmitSetFpsr(ArmEmitterContext context)
|
||||
{
|
||||
OpCodeSystem op = (OpCodeSystem)context.CurrOp;
|
||||
|
||||
context.ClearQcFlagIfModified();
|
||||
|
||||
Operand fpsr = GetIntOrZR(context, op.Rt);
|
||||
fpsr = context.ConvertI64ToI32(fpsr);
|
||||
|
||||
for (int flag = 0; flag < RegisterConsts.FpFlagsCount; flag++)
|
||||
{
|
||||
if (FPSR.Mask.HasFlag((FPSR)(1u << flag)))
|
||||
{
|
||||
SetFpFlag(context, (FPState)flag, context.BitwiseAnd(context.ShiftRightUI(fpsr, Const(flag)), Const(1)));
|
||||
}
|
||||
}
|
||||
|
||||
context.UpdateArmFpMode();
|
||||
}
|
||||
|
||||
private static void EmitSetTpidrEl0(ArmEmitterContext context)
|
||||
{
|
||||
OpCodeSystem op = (OpCodeSystem)context.CurrOp;
|
||||
|
||||
Operand value = GetIntOrZR(context, op.Rt);
|
||||
|
||||
Operand nativeContext = context.LoadArgument(OperandType.I64, 0);
|
||||
|
||||
context.Store(context.Add(nativeContext, Const((ulong)NativeContext.GetTpidrEl0Offset())), value);
|
||||
}
|
||||
}
|
||||
}
|
||||
351
src/ARMeilleure/Instructions/InstEmitSystem32.cs
Normal file
351
src/ARMeilleure/Instructions/InstEmitSystem32.cs
Normal file
@@ -0,0 +1,351 @@
|
||||
using ARMeilleure.Decoders;
|
||||
using ARMeilleure.IntermediateRepresentation;
|
||||
using ARMeilleure.State;
|
||||
using ARMeilleure.Translation;
|
||||
using System;
|
||||
using System.Reflection;
|
||||
|
||||
using static ARMeilleure.Instructions.InstEmitHelper;
|
||||
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
|
||||
|
||||
namespace ARMeilleure.Instructions
|
||||
{
|
||||
static partial class InstEmit32
|
||||
{
|
||||
public static void Mcr(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32System op = (OpCode32System)context.CurrOp;
|
||||
|
||||
if (op.Coproc != 15 || op.Opc1 != 0)
|
||||
{
|
||||
InstEmit.Und(context);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
switch (op.CRn)
|
||||
{
|
||||
case 13: // Process and Thread Info.
|
||||
if (op.CRm != 0)
|
||||
{
|
||||
throw new NotImplementedException($"Unknown MRC CRm 0x{op.CRm:X} at 0x{op.Address:X} (0x{op.RawOpCode:X}).");
|
||||
}
|
||||
|
||||
switch (op.Opc2)
|
||||
{
|
||||
case 2:
|
||||
EmitSetTpidrEl0(context); return;
|
||||
|
||||
default:
|
||||
throw new NotImplementedException($"Unknown MRC Opc2 0x{op.Opc2:X} at 0x{op.Address:X} (0x{op.RawOpCode:X}).");
|
||||
}
|
||||
|
||||
case 7:
|
||||
switch (op.CRm) // Cache and Memory barrier.
|
||||
{
|
||||
case 10:
|
||||
switch (op.Opc2)
|
||||
{
|
||||
case 5: // Data Memory Barrier Register.
|
||||
return; // No-op.
|
||||
|
||||
default:
|
||||
throw new NotImplementedException($"Unknown MRC Opc2 0x{op.Opc2:X16} at 0x{op.Address:X16} (0x{op.RawOpCode:X}).");
|
||||
}
|
||||
|
||||
default:
|
||||
throw new NotImplementedException($"Unknown MRC CRm 0x{op.CRm:X16} at 0x{op.Address:X16} (0x{op.RawOpCode:X}).");
|
||||
}
|
||||
|
||||
default:
|
||||
throw new NotImplementedException($"Unknown MRC 0x{op.RawOpCode:X8} at 0x{op.Address:X16}.");
|
||||
}
|
||||
}
|
||||
|
||||
public static void Mrc(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32System op = (OpCode32System)context.CurrOp;
|
||||
|
||||
if (op.Coproc != 15 || op.Opc1 != 0)
|
||||
{
|
||||
InstEmit.Und(context);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Operand result;
|
||||
|
||||
switch (op.CRn)
|
||||
{
|
||||
case 13: // Process and Thread Info.
|
||||
if (op.CRm != 0)
|
||||
{
|
||||
throw new NotImplementedException($"Unknown MRC CRm 0x{op.CRm:X} at 0x{op.Address:X} (0x{op.RawOpCode:X}).");
|
||||
}
|
||||
|
||||
switch (op.Opc2)
|
||||
{
|
||||
case 2:
|
||||
result = EmitGetTpidrEl0(context); break;
|
||||
|
||||
case 3:
|
||||
result = EmitGetTpidrroEl0(context); break;
|
||||
|
||||
default:
|
||||
throw new NotImplementedException($"Unknown MRC Opc2 0x{op.Opc2:X} at 0x{op.Address:X} (0x{op.RawOpCode:X}).");
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new NotImplementedException($"Unknown MRC 0x{op.RawOpCode:X} at 0x{op.Address:X}.");
|
||||
}
|
||||
|
||||
if (op.Rt == RegisterAlias.Aarch32Pc)
|
||||
{
|
||||
// Special behavior: copy NZCV flags into APSR.
|
||||
EmitSetNzcv(context, result);
|
||||
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
SetIntA32(context, op.Rt, result);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Mrrc(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32System op = (OpCode32System)context.CurrOp;
|
||||
|
||||
if (op.Coproc != 15)
|
||||
{
|
||||
InstEmit.Und(context);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
int opc = op.MrrcOp;
|
||||
|
||||
MethodInfo info;
|
||||
|
||||
switch (op.CRm)
|
||||
{
|
||||
case 14: // Timer.
|
||||
switch (opc)
|
||||
{
|
||||
case 0:
|
||||
info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCntpctEl0)); break;
|
||||
|
||||
default:
|
||||
throw new NotImplementedException($"Unknown MRRC Opc1 0x{opc:X} at 0x{op.Address:X} (0x{op.RawOpCode:X}).");
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new NotImplementedException($"Unknown MRRC 0x{op.RawOpCode:X} at 0x{op.Address:X}.");
|
||||
}
|
||||
|
||||
Operand result = context.Call(info);
|
||||
|
||||
SetIntA32(context, op.Rt, context.ConvertI64ToI32(result));
|
||||
SetIntA32(context, op.CRn, context.ConvertI64ToI32(context.ShiftRightUI(result, Const(32))));
|
||||
}
|
||||
|
||||
public static void Mrs(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32Mrs op = (OpCode32Mrs)context.CurrOp;
|
||||
|
||||
if (op.R)
|
||||
{
|
||||
throw new NotImplementedException("SPSR");
|
||||
}
|
||||
else
|
||||
{
|
||||
Operand spsr = context.ShiftLeft(GetFlag(PState.VFlag), Const((int)PState.VFlag));
|
||||
spsr = context.BitwiseOr(spsr, context.ShiftLeft(GetFlag(PState.CFlag), Const((int)PState.CFlag)));
|
||||
spsr = context.BitwiseOr(spsr, context.ShiftLeft(GetFlag(PState.ZFlag), Const((int)PState.ZFlag)));
|
||||
spsr = context.BitwiseOr(spsr, context.ShiftLeft(GetFlag(PState.NFlag), Const((int)PState.NFlag)));
|
||||
spsr = context.BitwiseOr(spsr, context.ShiftLeft(GetFlag(PState.QFlag), Const((int)PState.QFlag)));
|
||||
|
||||
// TODO: Remaining flags.
|
||||
|
||||
SetIntA32(context, op.Rd, spsr);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Msr(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32MsrReg op = (OpCode32MsrReg)context.CurrOp;
|
||||
|
||||
if (op.R)
|
||||
{
|
||||
throw new NotImplementedException("SPSR");
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((op.Mask & 8) != 0)
|
||||
{
|
||||
Operand value = GetIntA32(context, op.Rn);
|
||||
|
||||
EmitSetNzcv(context, value);
|
||||
|
||||
Operand q = context.BitwiseAnd(context.ShiftRightUI(value, Const((int)PState.QFlag)), Const(1));
|
||||
|
||||
SetFlag(context, PState.QFlag, q);
|
||||
}
|
||||
|
||||
if ((op.Mask & 4) != 0)
|
||||
{
|
||||
throw new NotImplementedException("APSR_g");
|
||||
}
|
||||
|
||||
if ((op.Mask & 2) != 0)
|
||||
{
|
||||
throw new NotImplementedException("CPSR_x");
|
||||
}
|
||||
|
||||
if ((op.Mask & 1) != 0)
|
||||
{
|
||||
throw new NotImplementedException("CPSR_c");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void Nop(ArmEmitterContext context) { }
|
||||
|
||||
public static void Vmrs(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32SimdSpecial op = (OpCode32SimdSpecial)context.CurrOp;
|
||||
|
||||
if (op.Rt == RegisterAlias.Aarch32Pc && op.Sreg == 0b0001)
|
||||
{
|
||||
// Special behavior: copy NZCV flags into APSR.
|
||||
SetFlag(context, PState.VFlag, GetFpFlag(FPState.VFlag));
|
||||
SetFlag(context, PState.CFlag, GetFpFlag(FPState.CFlag));
|
||||
SetFlag(context, PState.ZFlag, GetFpFlag(FPState.ZFlag));
|
||||
SetFlag(context, PState.NFlag, GetFpFlag(FPState.NFlag));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
switch (op.Sreg)
|
||||
{
|
||||
case 0b0000: // FPSID
|
||||
throw new NotImplementedException("Supervisor Only");
|
||||
case 0b0001: // FPSCR
|
||||
EmitGetFpscr(context); return;
|
||||
case 0b0101: // MVFR2
|
||||
throw new NotImplementedException("MVFR2");
|
||||
case 0b0110: // MVFR1
|
||||
throw new NotImplementedException("MVFR1");
|
||||
case 0b0111: // MVFR0
|
||||
throw new NotImplementedException("MVFR0");
|
||||
case 0b1000: // FPEXC
|
||||
throw new NotImplementedException("Supervisor Only");
|
||||
default:
|
||||
throw new NotImplementedException($"Unknown VMRS 0x{op.RawOpCode:X} at 0x{op.Address:X}.");
|
||||
}
|
||||
}
|
||||
|
||||
public static void Vmsr(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32SimdSpecial op = (OpCode32SimdSpecial)context.CurrOp;
|
||||
|
||||
switch (op.Sreg)
|
||||
{
|
||||
case 0b0000: // FPSID
|
||||
throw new NotImplementedException("Supervisor Only");
|
||||
case 0b0001: // FPSCR
|
||||
EmitSetFpscr(context); return;
|
||||
case 0b0101: // MVFR2
|
||||
throw new NotImplementedException("MVFR2");
|
||||
case 0b0110: // MVFR1
|
||||
throw new NotImplementedException("MVFR1");
|
||||
case 0b0111: // MVFR0
|
||||
throw new NotImplementedException("MVFR0");
|
||||
case 0b1000: // FPEXC
|
||||
throw new NotImplementedException("Supervisor Only");
|
||||
default:
|
||||
throw new NotImplementedException($"Unknown VMSR 0x{op.RawOpCode:X} at 0x{op.Address:X}.");
|
||||
}
|
||||
}
|
||||
|
||||
private static void EmitSetNzcv(ArmEmitterContext context, Operand t)
|
||||
{
|
||||
Operand v = context.BitwiseAnd(context.ShiftRightUI(t, Const((int)PState.VFlag)), Const(1));
|
||||
Operand c = context.BitwiseAnd(context.ShiftRightUI(t, Const((int)PState.CFlag)), Const(1));
|
||||
Operand z = context.BitwiseAnd(context.ShiftRightUI(t, Const((int)PState.ZFlag)), Const(1));
|
||||
Operand n = context.BitwiseAnd(context.ShiftRightUI(t, Const((int)PState.NFlag)), Const(1));
|
||||
|
||||
SetFlag(context, PState.VFlag, v);
|
||||
SetFlag(context, PState.CFlag, c);
|
||||
SetFlag(context, PState.ZFlag, z);
|
||||
SetFlag(context, PState.NFlag, n);
|
||||
}
|
||||
|
||||
private static void EmitGetFpscr(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32SimdSpecial op = (OpCode32SimdSpecial)context.CurrOp;
|
||||
|
||||
Operand fpscr = Const(0);
|
||||
|
||||
for (int flag = 0; flag < RegisterConsts.FpFlagsCount; flag++)
|
||||
{
|
||||
if (FPSCR.Mask.HasFlag((FPSCR)(1u << flag)))
|
||||
{
|
||||
fpscr = context.BitwiseOr(fpscr, context.ShiftLeft(GetFpFlag((FPState)flag), Const(flag)));
|
||||
}
|
||||
}
|
||||
|
||||
SetIntA32(context, op.Rt, fpscr);
|
||||
}
|
||||
|
||||
private static void EmitSetFpscr(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32SimdSpecial op = (OpCode32SimdSpecial)context.CurrOp;
|
||||
|
||||
Operand fpscr = GetIntA32(context, op.Rt);
|
||||
|
||||
for (int flag = 0; flag < RegisterConsts.FpFlagsCount; flag++)
|
||||
{
|
||||
if (FPSCR.Mask.HasFlag((FPSCR)(1u << flag)))
|
||||
{
|
||||
SetFpFlag(context, (FPState)flag, context.BitwiseAnd(context.ShiftRightUI(fpscr, Const(flag)), Const(1)));
|
||||
}
|
||||
}
|
||||
|
||||
context.UpdateArmFpMode();
|
||||
}
|
||||
|
||||
private static Operand EmitGetTpidrEl0(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32System op = (OpCode32System)context.CurrOp;
|
||||
|
||||
Operand nativeContext = context.LoadArgument(OperandType.I64, 0);
|
||||
|
||||
return context.Load(OperandType.I64, context.Add(nativeContext, Const((ulong)NativeContext.GetTpidrEl0Offset())));
|
||||
}
|
||||
|
||||
private static Operand EmitGetTpidrroEl0(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32System op = (OpCode32System)context.CurrOp;
|
||||
|
||||
Operand nativeContext = context.LoadArgument(OperandType.I64, 0);
|
||||
|
||||
return context.Load(OperandType.I64, context.Add(nativeContext, Const((ulong)NativeContext.GetTpidrroEl0Offset())));
|
||||
}
|
||||
|
||||
private static void EmitSetTpidrEl0(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32System op = (OpCode32System)context.CurrOp;
|
||||
|
||||
Operand value = GetIntA32(context, op.Rt);
|
||||
|
||||
Operand nativeContext = context.LoadArgument(OperandType.I64, 0);
|
||||
|
||||
context.Store(context.Add(nativeContext, Const((ulong)NativeContext.GetTpidrEl0Offset())), context.ZeroExtend32(OperandType.I64, value));
|
||||
}
|
||||
}
|
||||
}
|
||||
685
src/ARMeilleure/Instructions/InstName.cs
Normal file
685
src/ARMeilleure/Instructions/InstName.cs
Normal file
@@ -0,0 +1,685 @@
|
||||
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,
|
||||
Csdb,
|
||||
Csel,
|
||||
Csinc,
|
||||
Csinv,
|
||||
Csneg,
|
||||
Dmb,
|
||||
Dsb,
|
||||
Eon,
|
||||
Eor,
|
||||
Esb,
|
||||
Extr,
|
||||
Hint,
|
||||
Isb,
|
||||
It,
|
||||
Ldar,
|
||||
Ldaxp,
|
||||
Ldaxr,
|
||||
Ldp,
|
||||
Ldr,
|
||||
Ldr_Literal,
|
||||
Ldrs,
|
||||
Ldxr,
|
||||
Ldxp,
|
||||
Lslv,
|
||||
Lsrv,
|
||||
Madd,
|
||||
Movk,
|
||||
Movn,
|
||||
Movz,
|
||||
Mrs,
|
||||
Msr,
|
||||
Msub,
|
||||
Nop,
|
||||
Orn,
|
||||
Orr,
|
||||
Prfm,
|
||||
Rbit,
|
||||
Ret,
|
||||
Rev16,
|
||||
Rev32,
|
||||
Rev64,
|
||||
Rorv,
|
||||
Sbc,
|
||||
Sbcs,
|
||||
Sbfm,
|
||||
Sdiv,
|
||||
Sel,
|
||||
Sev,
|
||||
Sevl,
|
||||
Shsub8,
|
||||
Smaddl,
|
||||
Smsubl,
|
||||
Smulh,
|
||||
Smull,
|
||||
Smulw_,
|
||||
Ssat,
|
||||
Ssat16,
|
||||
Stlr,
|
||||
Stlxp,
|
||||
Stlxr,
|
||||
Stp,
|
||||
Str,
|
||||
Stxp,
|
||||
Stxr,
|
||||
Sub,
|
||||
Subs,
|
||||
Svc,
|
||||
Sxtb,
|
||||
Sxth,
|
||||
Sys,
|
||||
Tbnz,
|
||||
Tbz,
|
||||
Tsb,
|
||||
Ubfm,
|
||||
Udiv,
|
||||
Umaddl,
|
||||
Umsubl,
|
||||
Umulh,
|
||||
Und,
|
||||
Wfe,
|
||||
Wfi,
|
||||
Yield,
|
||||
|
||||
// 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,
|
||||
Facge_S,
|
||||
Facge_V,
|
||||
Facgt_S,
|
||||
Facgt_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,
|
||||
Fcvtas_S,
|
||||
Fcvtas_V,
|
||||
Fcvtau_Gp,
|
||||
Fcvtau_S,
|
||||
Fcvtau_V,
|
||||
Fcvtl_V,
|
||||
Fcvtms_Gp,
|
||||
Fcvtms_V,
|
||||
Fcvtmu_Gp,
|
||||
Fcvtn_V,
|
||||
Fcvtns_Gp,
|
||||
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,
|
||||
Fmaxnmp_S,
|
||||
Fmaxnmp_V,
|
||||
Fmaxnmv_V,
|
||||
Fmaxp_V,
|
||||
Fmaxv_V,
|
||||
Fmin_S,
|
||||
Fmin_V,
|
||||
Fminnm_S,
|
||||
Fminnm_V,
|
||||
Fminnmp_S,
|
||||
Fminnmp_V,
|
||||
Fminnmv_V,
|
||||
Fminp_V,
|
||||
Fminv_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,
|
||||
Pmull_V,
|
||||
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_S_Fixed,
|
||||
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_S,
|
||||
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,
|
||||
Sqdmulh_Ve,
|
||||
Sqneg_S,
|
||||
Sqneg_V,
|
||||
Sqrdmulh_S,
|
||||
Sqrdmulh_V,
|
||||
Sqrdmulh_Ve,
|
||||
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,
|
||||
Sri_S,
|
||||
Sri_V,
|
||||
Srshl_V,
|
||||
Srshr_S,
|
||||
Srshr_V,
|
||||
Srsra_S,
|
||||
Srsra_V,
|
||||
Sshl_S,
|
||||
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,
|
||||
Tbx_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_S_Fixed,
|
||||
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_S,
|
||||
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)
|
||||
Bfc,
|
||||
Bfi,
|
||||
Blx,
|
||||
Bx,
|
||||
Cmp,
|
||||
Cmn,
|
||||
Movt,
|
||||
Mul,
|
||||
Lda,
|
||||
Ldab,
|
||||
Ldaex,
|
||||
Ldaexb,
|
||||
Ldaexd,
|
||||
Ldaexh,
|
||||
Ldah,
|
||||
Ldm,
|
||||
Ldrb,
|
||||
Ldrd,
|
||||
Ldrex,
|
||||
Ldrexb,
|
||||
Ldrexd,
|
||||
Ldrexh,
|
||||
Ldrh,
|
||||
Ldrsb,
|
||||
Ldrsh,
|
||||
Mcr,
|
||||
Mla,
|
||||
Mls,
|
||||
Mov,
|
||||
Mrc,
|
||||
Mrrc,
|
||||
Mvn,
|
||||
Pkh,
|
||||
Pld,
|
||||
Pop,
|
||||
Push,
|
||||
Rev,
|
||||
Revsh,
|
||||
Rsb,
|
||||
Rsc,
|
||||
Sadd8,
|
||||
Sbfx,
|
||||
Shadd8,
|
||||
Smla__,
|
||||
Smlal,
|
||||
Smlal__,
|
||||
Smlaw_,
|
||||
Smmla,
|
||||
Smmls,
|
||||
Smul__,
|
||||
Smmul,
|
||||
Ssub8,
|
||||
Stl,
|
||||
Stlb,
|
||||
Stlex,
|
||||
Stlexb,
|
||||
Stlexd,
|
||||
Stlexh,
|
||||
Stlh,
|
||||
Stm,
|
||||
Strb,
|
||||
Strd,
|
||||
Strex,
|
||||
Strexb,
|
||||
Strexd,
|
||||
Strexh,
|
||||
Strh,
|
||||
Sxtb16,
|
||||
Tbb,
|
||||
Tbh,
|
||||
Teq,
|
||||
Trap,
|
||||
Tst,
|
||||
Uadd8,
|
||||
Ubfx,
|
||||
Uhadd8,
|
||||
Uhsub8,
|
||||
Umaal,
|
||||
Umlal,
|
||||
Umull,
|
||||
Usat,
|
||||
Usat16,
|
||||
Usub8,
|
||||
Uxtb,
|
||||
Uxtb16,
|
||||
Uxth,
|
||||
|
||||
// FP & SIMD (AArch32)
|
||||
Vabd,
|
||||
Vabdl,
|
||||
Vabs,
|
||||
Vadd,
|
||||
Vaddl,
|
||||
Vaddw,
|
||||
Vand,
|
||||
Vbic,
|
||||
Vbif,
|
||||
Vbit,
|
||||
Vbsl,
|
||||
Vceq,
|
||||
Vcge,
|
||||
Vcgt,
|
||||
Vcle,
|
||||
Vclt,
|
||||
Vcmp,
|
||||
Vcmpe,
|
||||
Vcnt,
|
||||
Vcvt,
|
||||
Vdiv,
|
||||
Vdup,
|
||||
Veor,
|
||||
Vext,
|
||||
Vfma,
|
||||
Vfms,
|
||||
Vfnma,
|
||||
Vfnms,
|
||||
Vhadd,
|
||||
Vld1,
|
||||
Vld2,
|
||||
Vld3,
|
||||
Vld4,
|
||||
Vldm,
|
||||
Vldr,
|
||||
Vmax,
|
||||
Vmaxnm,
|
||||
Vmin,
|
||||
Vminnm,
|
||||
Vmla,
|
||||
Vmlal,
|
||||
Vmls,
|
||||
Vmlsl,
|
||||
Vmov,
|
||||
Vmovl,
|
||||
Vmovn,
|
||||
Vmrs,
|
||||
Vmsr,
|
||||
Vmul,
|
||||
Vmull,
|
||||
Vmvn,
|
||||
Vneg,
|
||||
Vnmul,
|
||||
Vnmla,
|
||||
Vnmls,
|
||||
Vorn,
|
||||
Vorr,
|
||||
Vpadd,
|
||||
Vpaddl,
|
||||
Vpmax,
|
||||
Vpmin,
|
||||
Vqadd,
|
||||
Vqdmulh,
|
||||
Vqmovn,
|
||||
Vqmovun,
|
||||
Vqrshrn,
|
||||
Vqrshrun,
|
||||
Vqshrn,
|
||||
Vqshrun,
|
||||
Vqsub,
|
||||
Vrev,
|
||||
Vrhadd,
|
||||
Vrint,
|
||||
Vrinta,
|
||||
Vrintm,
|
||||
Vrintn,
|
||||
Vrintp,
|
||||
Vrintx,
|
||||
Vrshr,
|
||||
Vrshrn,
|
||||
Vsel,
|
||||
Vshl,
|
||||
Vshll,
|
||||
Vshr,
|
||||
Vshrn,
|
||||
Vst1,
|
||||
Vst2,
|
||||
Vst3,
|
||||
Vst4,
|
||||
Vstm,
|
||||
Vstr,
|
||||
Vsqrt,
|
||||
Vrecpe,
|
||||
Vrecps,
|
||||
Vrsqrte,
|
||||
Vrsqrts,
|
||||
Vrsra,
|
||||
Vsra,
|
||||
Vsub,
|
||||
Vsubl,
|
||||
Vsubw,
|
||||
Vtbl,
|
||||
Vtrn,
|
||||
Vtst,
|
||||
Vuzp,
|
||||
Vzip,
|
||||
}
|
||||
}
|
||||
195
src/ARMeilleure/Instructions/NativeInterface.cs
Normal file
195
src/ARMeilleure/Instructions/NativeInterface.cs
Normal file
@@ -0,0 +1,195 @@
|
||||
using ARMeilleure.Memory;
|
||||
using ARMeilleure.State;
|
||||
using ARMeilleure.Translation;
|
||||
using System;
|
||||
|
||||
namespace ARMeilleure.Instructions
|
||||
{
|
||||
static class NativeInterface
|
||||
{
|
||||
private class ThreadContext
|
||||
{
|
||||
public ExecutionContext Context { get; }
|
||||
public IMemoryManager Memory { get; }
|
||||
public Translator Translator { get; }
|
||||
|
||||
public ThreadContext(ExecutionContext context, IMemoryManager memory, Translator translator)
|
||||
{
|
||||
Context = context;
|
||||
Memory = memory;
|
||||
Translator = translator;
|
||||
}
|
||||
}
|
||||
|
||||
[ThreadStatic]
|
||||
private static ThreadContext Context;
|
||||
|
||||
public static void RegisterThread(ExecutionContext context, IMemoryManager memory, Translator translator)
|
||||
{
|
||||
Context = new ThreadContext(context, memory, translator);
|
||||
}
|
||||
|
||||
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 GetCntfrqEl0()
|
||||
{
|
||||
return GetContext().CntfrqEl0;
|
||||
}
|
||||
|
||||
public static ulong GetCntpctEl0()
|
||||
{
|
||||
return GetContext().CntpctEl0;
|
||||
}
|
||||
|
||||
public static ulong GetCntvctEl0()
|
||||
{
|
||||
return GetContext().CntvctEl0;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region "Read"
|
||||
public static byte ReadByte(ulong address)
|
||||
{
|
||||
return GetMemoryManager().ReadTracked<byte>(address);
|
||||
}
|
||||
|
||||
public static ushort ReadUInt16(ulong address)
|
||||
{
|
||||
return GetMemoryManager().ReadTracked<ushort>(address);
|
||||
}
|
||||
|
||||
public static uint ReadUInt32(ulong address)
|
||||
{
|
||||
return GetMemoryManager().ReadTracked<uint>(address);
|
||||
}
|
||||
|
||||
public static ulong ReadUInt64(ulong address)
|
||||
{
|
||||
return GetMemoryManager().ReadTracked<ulong>(address);
|
||||
}
|
||||
|
||||
public static V128 ReadVector128(ulong address)
|
||||
{
|
||||
return GetMemoryManager().ReadTracked<V128>(address);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region "Write"
|
||||
public static void WriteByte(ulong address, byte value)
|
||||
{
|
||||
GetMemoryManager().Write(address, value);
|
||||
}
|
||||
|
||||
public static void WriteUInt16(ulong address, ushort value)
|
||||
{
|
||||
GetMemoryManager().Write(address, value);
|
||||
}
|
||||
|
||||
public static void WriteUInt32(ulong address, uint value)
|
||||
{
|
||||
GetMemoryManager().Write(address, value);
|
||||
}
|
||||
|
||||
public static void WriteUInt64(ulong address, ulong value)
|
||||
{
|
||||
GetMemoryManager().Write(address, value);
|
||||
}
|
||||
|
||||
public static void WriteVector128(ulong address, V128 value)
|
||||
{
|
||||
GetMemoryManager().Write(address, value);
|
||||
}
|
||||
#endregion
|
||||
|
||||
public static void EnqueueForRejit(ulong address)
|
||||
{
|
||||
Context.Translator.EnqueueForRejit(address, GetContext().ExecutionMode);
|
||||
}
|
||||
|
||||
public static void SignalMemoryTracking(ulong address, ulong size, bool write)
|
||||
{
|
||||
GetMemoryManager().SignalMemoryTracking(address, size, write);
|
||||
}
|
||||
|
||||
public static void ThrowInvalidMemoryAccess(ulong address)
|
||||
{
|
||||
throw new InvalidAccessException(address);
|
||||
}
|
||||
|
||||
public static ulong GetFunctionAddress(ulong address)
|
||||
{
|
||||
TranslatedFunction function = Context.Translator.GetOrTranslate(address, GetContext().ExecutionMode);
|
||||
|
||||
return (ulong)function.FuncPointer.ToInt64();
|
||||
}
|
||||
|
||||
public static void InvalidateCacheLine(ulong address)
|
||||
{
|
||||
Context.Translator.InvalidateJitCacheRegion(address, InstEmit.DczSizeInBytes);
|
||||
}
|
||||
|
||||
public static bool CheckSynchronization()
|
||||
{
|
||||
Statistics.PauseTimer();
|
||||
|
||||
ExecutionContext context = GetContext();
|
||||
|
||||
context.CheckInterrupt();
|
||||
|
||||
Statistics.ResumeTimer();
|
||||
|
||||
return context.Running;
|
||||
}
|
||||
|
||||
public static ExecutionContext GetContext()
|
||||
{
|
||||
return Context.Context;
|
||||
}
|
||||
|
||||
public static IMemoryManager GetMemoryManager()
|
||||
{
|
||||
return Context.Memory;
|
||||
}
|
||||
}
|
||||
}
|
||||
624
src/ARMeilleure/Instructions/SoftFallback.cs
Normal file
624
src/ARMeilleure/Instructions/SoftFallback.cs
Normal file
@@ -0,0 +1,624 @@
|
||||
using ARMeilleure.State;
|
||||
using System;
|
||||
|
||||
namespace ARMeilleure.Instructions
|
||||
{
|
||||
static class SoftFallback
|
||||
{
|
||||
#region "ShrImm64"
|
||||
public static long SignedShrImm64(long value, long roundConst, int shift)
|
||||
{
|
||||
if (roundConst == 0L)
|
||||
{
|
||||
if (shift <= 63)
|
||||
{
|
||||
return value >> shift;
|
||||
}
|
||||
else /* if (shift == 64) */
|
||||
{
|
||||
if (value < 0L)
|
||||
{
|
||||
return -1L;
|
||||
}
|
||||
else /* if (value >= 0L) */
|
||||
{
|
||||
return 0L;
|
||||
}
|
||||
}
|
||||
}
|
||||
else /* if (roundConst == 1L << (shift - 1)) */
|
||||
{
|
||||
if (shift <= 63)
|
||||
{
|
||||
long add = value + roundConst;
|
||||
|
||||
if ((~value & (value ^ add)) < 0L)
|
||||
{
|
||||
return (long)((ulong)add >> shift);
|
||||
}
|
||||
else
|
||||
{
|
||||
return add >> shift;
|
||||
}
|
||||
}
|
||||
else /* if (shift == 64) */
|
||||
{
|
||||
return 0L;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static ulong UnsignedShrImm64(ulong value, long roundConst, int shift)
|
||||
{
|
||||
if (roundConst == 0L)
|
||||
{
|
||||
if (shift <= 63)
|
||||
{
|
||||
return value >> shift;
|
||||
}
|
||||
else /* if (shift == 64) */
|
||||
{
|
||||
return 0UL;
|
||||
}
|
||||
}
|
||||
else /* if (roundConst == 1L << (shift - 1)) */
|
||||
{
|
||||
ulong add = value + (ulong)roundConst;
|
||||
|
||||
if ((add < value) && (add < (ulong)roundConst))
|
||||
{
|
||||
if (shift <= 63)
|
||||
{
|
||||
return (add >> shift) | (0x8000000000000000UL >> (shift - 1));
|
||||
}
|
||||
else /* if (shift == 64) */
|
||||
{
|
||||
return 1UL;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (shift <= 63)
|
||||
{
|
||||
return add >> shift;
|
||||
}
|
||||
else /* if (shift == 64) */
|
||||
{
|
||||
return 0UL;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region "Saturation"
|
||||
public static int SatF32ToS32(float value)
|
||||
{
|
||||
if (float.IsNaN(value)) return 0;
|
||||
|
||||
return value >= int.MaxValue ? int.MaxValue :
|
||||
value <= int.MinValue ? int.MinValue : (int)value;
|
||||
}
|
||||
|
||||
public static long SatF32ToS64(float value)
|
||||
{
|
||||
if (float.IsNaN(value)) return 0;
|
||||
|
||||
return value >= long.MaxValue ? long.MaxValue :
|
||||
value <= long.MinValue ? long.MinValue : (long)value;
|
||||
}
|
||||
|
||||
public static uint SatF32ToU32(float value)
|
||||
{
|
||||
if (float.IsNaN(value)) return 0;
|
||||
|
||||
return value >= uint.MaxValue ? uint.MaxValue :
|
||||
value <= uint.MinValue ? uint.MinValue : (uint)value;
|
||||
}
|
||||
|
||||
public static ulong SatF32ToU64(float value)
|
||||
{
|
||||
if (float.IsNaN(value)) return 0;
|
||||
|
||||
return value >= ulong.MaxValue ? ulong.MaxValue :
|
||||
value <= ulong.MinValue ? ulong.MinValue : (ulong)value;
|
||||
}
|
||||
|
||||
public static int SatF64ToS32(double value)
|
||||
{
|
||||
if (double.IsNaN(value)) return 0;
|
||||
|
||||
return value >= int.MaxValue ? int.MaxValue :
|
||||
value <= int.MinValue ? int.MinValue : (int)value;
|
||||
}
|
||||
|
||||
public static long SatF64ToS64(double value)
|
||||
{
|
||||
if (double.IsNaN(value)) return 0;
|
||||
|
||||
return value >= long.MaxValue ? long.MaxValue :
|
||||
value <= long.MinValue ? long.MinValue : (long)value;
|
||||
}
|
||||
|
||||
public static uint SatF64ToU32(double value)
|
||||
{
|
||||
if (double.IsNaN(value)) return 0;
|
||||
|
||||
return value >= uint.MaxValue ? uint.MaxValue :
|
||||
value <= uint.MinValue ? uint.MinValue : (uint)value;
|
||||
}
|
||||
|
||||
public static ulong SatF64ToU64(double value)
|
||||
{
|
||||
if (double.IsNaN(value)) return 0;
|
||||
|
||||
return value >= ulong.MaxValue ? ulong.MaxValue :
|
||||
value <= ulong.MinValue ? ulong.MinValue : (ulong)value;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region "Count"
|
||||
public static ulong CountLeadingSigns(ulong value, int size) // size is 8, 16, 32 or 64 (SIMD&FP or Base Inst.).
|
||||
{
|
||||
value ^= value >> 1;
|
||||
|
||||
int highBit = size - 2;
|
||||
|
||||
for (int bit = highBit; bit >= 0; bit--)
|
||||
{
|
||||
if (((int)(value >> bit) & 0b1) != 0)
|
||||
{
|
||||
return (ulong)(highBit - bit);
|
||||
}
|
||||
}
|
||||
|
||||
return (ulong)(size - 1);
|
||||
}
|
||||
|
||||
private static ReadOnlySpan<byte> ClzNibbleTbl => new byte[] { 4, 3, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0 };
|
||||
|
||||
public static ulong CountLeadingZeros(ulong value, int size) // size is 8, 16, 32 or 64 (SIMD&FP or Base Inst.).
|
||||
{
|
||||
if (value == 0ul)
|
||||
{
|
||||
return (ulong)size;
|
||||
}
|
||||
|
||||
int nibbleIdx = size;
|
||||
int preCount, count = 0;
|
||||
|
||||
do
|
||||
{
|
||||
nibbleIdx -= 4;
|
||||
preCount = ClzNibbleTbl[(int)(value >> nibbleIdx) & 0b1111];
|
||||
count += preCount;
|
||||
}
|
||||
while (preCount == 4);
|
||||
|
||||
return (ulong)count;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region "Table"
|
||||
public static V128 Tbl1(V128 vector, int bytes, V128 tb0)
|
||||
{
|
||||
return TblOrTbx(default, vector, bytes, tb0);
|
||||
}
|
||||
|
||||
public static V128 Tbl2(V128 vector, int bytes, V128 tb0, V128 tb1)
|
||||
{
|
||||
return TblOrTbx(default, vector, bytes, tb0, tb1);
|
||||
}
|
||||
|
||||
public static V128 Tbl3(V128 vector, int bytes, V128 tb0, V128 tb1, V128 tb2)
|
||||
{
|
||||
return TblOrTbx(default, vector, bytes, tb0, tb1, tb2);
|
||||
}
|
||||
|
||||
public static V128 Tbl4(V128 vector, int bytes, V128 tb0, V128 tb1, V128 tb2, V128 tb3)
|
||||
{
|
||||
return TblOrTbx(default, vector, bytes, tb0, tb1, tb2, tb3);
|
||||
}
|
||||
|
||||
public static V128 Tbx1(V128 dest, V128 vector, int bytes, V128 tb0)
|
||||
{
|
||||
return TblOrTbx(dest, vector, bytes, tb0);
|
||||
}
|
||||
|
||||
public static V128 Tbx2(V128 dest, V128 vector, int bytes, V128 tb0, V128 tb1)
|
||||
{
|
||||
return TblOrTbx(dest, vector, bytes, tb0, tb1);
|
||||
}
|
||||
|
||||
public static V128 Tbx3(V128 dest, V128 vector, int bytes, V128 tb0, V128 tb1, V128 tb2)
|
||||
{
|
||||
return TblOrTbx(dest, vector, bytes, tb0, tb1, tb2);
|
||||
}
|
||||
|
||||
public static V128 Tbx4(V128 dest, V128 vector, int bytes, V128 tb0, V128 tb1, V128 tb2, V128 tb3)
|
||||
{
|
||||
return TblOrTbx(dest, vector, bytes, tb0, tb1, tb2, tb3);
|
||||
}
|
||||
|
||||
private static V128 TblOrTbx(V128 dest, V128 vector, int bytes, params V128[] tb)
|
||||
{
|
||||
byte[] res = new byte[16];
|
||||
|
||||
if (dest != default)
|
||||
{
|
||||
Buffer.BlockCopy(dest.ToArray(), 0, res, 0, bytes);
|
||||
}
|
||||
|
||||
byte[] table = new byte[tb.Length * 16];
|
||||
|
||||
for (byte index = 0; index < tb.Length; index++)
|
||||
{
|
||||
Buffer.BlockCopy(tb[index].ToArray(), 0, table, index * 16, 16);
|
||||
}
|
||||
|
||||
byte[] v = vector.ToArray();
|
||||
|
||||
for (byte index = 0; index < bytes; index++)
|
||||
{
|
||||
byte tblIndex = v[index];
|
||||
|
||||
if (tblIndex < table.Length)
|
||||
{
|
||||
res[index] = table[tblIndex];
|
||||
}
|
||||
}
|
||||
|
||||
return new V128(res);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region "Crc32"
|
||||
private const uint Crc32RevPoly = 0xedb88320;
|
||||
private const uint Crc32cRevPoly = 0x82f63b78;
|
||||
|
||||
public static uint Crc32b(uint crc, byte value) => Crc32 (crc, Crc32RevPoly, value);
|
||||
public static uint Crc32h(uint crc, ushort value) => Crc32h(crc, Crc32RevPoly, value);
|
||||
public static uint Crc32w(uint crc, uint value) => Crc32w(crc, Crc32RevPoly, value);
|
||||
public static uint Crc32x(uint crc, ulong value) => Crc32x(crc, Crc32RevPoly, value);
|
||||
|
||||
public static uint Crc32cb(uint crc, byte value) => Crc32 (crc, Crc32cRevPoly, value);
|
||||
public static uint Crc32ch(uint crc, ushort value) => Crc32h(crc, Crc32cRevPoly, value);
|
||||
public static uint Crc32cw(uint crc, uint value) => Crc32w(crc, Crc32cRevPoly, value);
|
||||
public static uint Crc32cx(uint crc, ulong value) => Crc32x(crc, Crc32cRevPoly, value);
|
||||
|
||||
private static uint Crc32h(uint crc, uint poly, ushort val)
|
||||
{
|
||||
crc = Crc32(crc, poly, (byte)(val >> 0));
|
||||
crc = Crc32(crc, poly, (byte)(val >> 8));
|
||||
|
||||
return crc;
|
||||
}
|
||||
|
||||
private static uint Crc32w(uint crc, uint poly, uint val)
|
||||
{
|
||||
crc = Crc32(crc, poly, (byte)(val >> 0));
|
||||
crc = Crc32(crc, poly, (byte)(val >> 8));
|
||||
crc = Crc32(crc, poly, (byte)(val >> 16));
|
||||
crc = Crc32(crc, poly, (byte)(val >> 24));
|
||||
|
||||
return crc;
|
||||
}
|
||||
|
||||
private static uint Crc32x(uint crc, uint poly, ulong val)
|
||||
{
|
||||
crc = Crc32(crc, poly, (byte)(val >> 0));
|
||||
crc = Crc32(crc, poly, (byte)(val >> 8));
|
||||
crc = Crc32(crc, poly, (byte)(val >> 16));
|
||||
crc = Crc32(crc, poly, (byte)(val >> 24));
|
||||
crc = Crc32(crc, poly, (byte)(val >> 32));
|
||||
crc = Crc32(crc, poly, (byte)(val >> 40));
|
||||
crc = Crc32(crc, poly, (byte)(val >> 48));
|
||||
crc = Crc32(crc, poly, (byte)(val >> 56));
|
||||
|
||||
return crc;
|
||||
}
|
||||
|
||||
private static uint Crc32(uint crc, uint poly, byte val)
|
||||
{
|
||||
crc ^= val;
|
||||
|
||||
for (int bit = 7; bit >= 0; bit--)
|
||||
{
|
||||
uint mask = (uint)(-(int)(crc & 1));
|
||||
|
||||
crc = (crc >> 1) ^ (poly & mask);
|
||||
}
|
||||
|
||||
return crc;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region "Aes"
|
||||
public static V128 Decrypt(V128 value, V128 roundKey)
|
||||
{
|
||||
return CryptoHelper.AesInvSubBytes(CryptoHelper.AesInvShiftRows(value ^ roundKey));
|
||||
}
|
||||
|
||||
public static V128 Encrypt(V128 value, V128 roundKey)
|
||||
{
|
||||
return CryptoHelper.AesSubBytes(CryptoHelper.AesShiftRows(value ^ roundKey));
|
||||
}
|
||||
|
||||
public static V128 InverseMixColumns(V128 value)
|
||||
{
|
||||
return CryptoHelper.AesInvMixColumns(value);
|
||||
}
|
||||
|
||||
public static V128 MixColumns(V128 value)
|
||||
{
|
||||
return CryptoHelper.AesMixColumns(value);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region "Sha1"
|
||||
public static V128 HashChoose(V128 hash_abcd, uint hash_e, V128 wk)
|
||||
{
|
||||
for (int e = 0; e <= 3; e++)
|
||||
{
|
||||
uint t = ShaChoose(hash_abcd.Extract<uint>(1),
|
||||
hash_abcd.Extract<uint>(2),
|
||||
hash_abcd.Extract<uint>(3));
|
||||
|
||||
hash_e += Rol(hash_abcd.Extract<uint>(0), 5) + t + wk.Extract<uint>(e);
|
||||
|
||||
t = Rol(hash_abcd.Extract<uint>(1), 30);
|
||||
|
||||
hash_abcd.Insert(1, t);
|
||||
|
||||
Rol32_160(ref hash_e, ref hash_abcd);
|
||||
}
|
||||
|
||||
return hash_abcd;
|
||||
}
|
||||
|
||||
public static uint FixedRotate(uint hash_e)
|
||||
{
|
||||
return hash_e.Rol(30);
|
||||
}
|
||||
|
||||
public static V128 HashMajority(V128 hash_abcd, uint hash_e, V128 wk)
|
||||
{
|
||||
for (int e = 0; e <= 3; e++)
|
||||
{
|
||||
uint t = ShaMajority(hash_abcd.Extract<uint>(1),
|
||||
hash_abcd.Extract<uint>(2),
|
||||
hash_abcd.Extract<uint>(3));
|
||||
|
||||
hash_e += Rol(hash_abcd.Extract<uint>(0), 5) + t + wk.Extract<uint>(e);
|
||||
|
||||
t = Rol(hash_abcd.Extract<uint>(1), 30);
|
||||
|
||||
hash_abcd.Insert(1, t);
|
||||
|
||||
Rol32_160(ref hash_e, ref hash_abcd);
|
||||
}
|
||||
|
||||
return hash_abcd;
|
||||
}
|
||||
|
||||
public static V128 HashParity(V128 hash_abcd, uint hash_e, V128 wk)
|
||||
{
|
||||
for (int e = 0; e <= 3; e++)
|
||||
{
|
||||
uint t = ShaParity(hash_abcd.Extract<uint>(1),
|
||||
hash_abcd.Extract<uint>(2),
|
||||
hash_abcd.Extract<uint>(3));
|
||||
|
||||
hash_e += Rol(hash_abcd.Extract<uint>(0), 5) + t + wk.Extract<uint>(e);
|
||||
|
||||
t = Rol(hash_abcd.Extract<uint>(1), 30);
|
||||
|
||||
hash_abcd.Insert(1, t);
|
||||
|
||||
Rol32_160(ref hash_e, ref hash_abcd);
|
||||
}
|
||||
|
||||
return hash_abcd;
|
||||
}
|
||||
|
||||
public static V128 Sha1SchedulePart1(V128 w0_3, V128 w4_7, V128 w8_11)
|
||||
{
|
||||
ulong t2 = w4_7.Extract<ulong>(0);
|
||||
ulong t1 = w0_3.Extract<ulong>(1);
|
||||
|
||||
V128 result = new V128(t1, t2);
|
||||
|
||||
return result ^ (w0_3 ^ w8_11);
|
||||
}
|
||||
|
||||
public static V128 Sha1SchedulePart2(V128 tw0_3, V128 w12_15)
|
||||
{
|
||||
V128 t = tw0_3 ^ (w12_15 >> 32);
|
||||
|
||||
uint tE0 = t.Extract<uint>(0);
|
||||
uint tE1 = t.Extract<uint>(1);
|
||||
uint tE2 = t.Extract<uint>(2);
|
||||
uint tE3 = t.Extract<uint>(3);
|
||||
|
||||
return new V128(tE0.Rol(1), tE1.Rol(1), tE2.Rol(1), tE3.Rol(1) ^ tE0.Rol(2));
|
||||
}
|
||||
|
||||
private static void Rol32_160(ref uint y, ref V128 x)
|
||||
{
|
||||
uint xE3 = x.Extract<uint>(3);
|
||||
|
||||
x <<= 32;
|
||||
x.Insert(0, y);
|
||||
|
||||
y = xE3;
|
||||
}
|
||||
|
||||
private static uint ShaChoose(uint x, uint y, uint z)
|
||||
{
|
||||
return ((y ^ z) & x) ^ z;
|
||||
}
|
||||
|
||||
private static uint ShaMajority(uint x, uint y, uint z)
|
||||
{
|
||||
return (x & y) | ((x | y) & z);
|
||||
}
|
||||
|
||||
private static uint ShaParity(uint x, uint y, uint z)
|
||||
{
|
||||
return x ^ y ^ z;
|
||||
}
|
||||
|
||||
private static uint Rol(this uint value, int count)
|
||||
{
|
||||
return (value << count) | (value >> (32 - count));
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region "Sha256"
|
||||
public static V128 HashLower(V128 hash_abcd, V128 hash_efgh, V128 wk)
|
||||
{
|
||||
return Sha256Hash(hash_abcd, hash_efgh, wk, part1: true);
|
||||
}
|
||||
|
||||
public static V128 HashUpper(V128 hash_abcd, V128 hash_efgh, V128 wk)
|
||||
{
|
||||
return Sha256Hash(hash_abcd, hash_efgh, wk, part1: false);
|
||||
}
|
||||
|
||||
public static V128 Sha256SchedulePart1(V128 w0_3, V128 w4_7)
|
||||
{
|
||||
V128 result = new V128();
|
||||
|
||||
for (int e = 0; e <= 3; e++)
|
||||
{
|
||||
uint elt = (e <= 2 ? w0_3 : w4_7).Extract<uint>(e <= 2 ? e + 1 : 0);
|
||||
|
||||
elt = elt.Ror(7) ^ elt.Ror(18) ^ elt.Lsr(3);
|
||||
|
||||
elt += w0_3.Extract<uint>(e);
|
||||
|
||||
result.Insert(e, elt);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static V128 Sha256SchedulePart2(V128 w0_3, V128 w8_11, V128 w12_15)
|
||||
{
|
||||
V128 result = new V128();
|
||||
|
||||
ulong t1 = w12_15.Extract<ulong>(1);
|
||||
|
||||
for (int e = 0; e <= 1; e++)
|
||||
{
|
||||
uint elt = t1.ULongPart(e);
|
||||
|
||||
elt = elt.Ror(17) ^ elt.Ror(19) ^ elt.Lsr(10);
|
||||
|
||||
elt += w0_3.Extract<uint>(e) + w8_11.Extract<uint>(e + 1);
|
||||
|
||||
result.Insert(e, elt);
|
||||
}
|
||||
|
||||
t1 = result.Extract<ulong>(0);
|
||||
|
||||
for (int e = 2; e <= 3; e++)
|
||||
{
|
||||
uint elt = t1.ULongPart(e - 2);
|
||||
|
||||
elt = elt.Ror(17) ^ elt.Ror(19) ^ elt.Lsr(10);
|
||||
|
||||
elt += w0_3.Extract<uint>(e) + (e == 2 ? w8_11 : w12_15).Extract<uint>(e == 2 ? 3 : 0);
|
||||
|
||||
result.Insert(e, elt);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static V128 Sha256Hash(V128 x, V128 y, V128 w, bool part1)
|
||||
{
|
||||
for (int e = 0; e <= 3; e++)
|
||||
{
|
||||
uint chs = ShaChoose(y.Extract<uint>(0),
|
||||
y.Extract<uint>(1),
|
||||
y.Extract<uint>(2));
|
||||
|
||||
uint maj = ShaMajority(x.Extract<uint>(0),
|
||||
x.Extract<uint>(1),
|
||||
x.Extract<uint>(2));
|
||||
|
||||
uint t1 = y.Extract<uint>(3) + ShaHashSigma1(y.Extract<uint>(0)) + chs + w.Extract<uint>(e);
|
||||
|
||||
uint t2 = t1 + x.Extract<uint>(3);
|
||||
|
||||
x.Insert(3, t2);
|
||||
|
||||
t2 = t1 + ShaHashSigma0(x.Extract<uint>(0)) + maj;
|
||||
|
||||
y.Insert(3, t2);
|
||||
|
||||
Rol32_256(ref y, ref x);
|
||||
}
|
||||
|
||||
return part1 ? x : y;
|
||||
}
|
||||
|
||||
private static void Rol32_256(ref V128 y, ref V128 x)
|
||||
{
|
||||
uint yE3 = y.Extract<uint>(3);
|
||||
uint xE3 = x.Extract<uint>(3);
|
||||
|
||||
y <<= 32;
|
||||
x <<= 32;
|
||||
|
||||
y.Insert(0, xE3);
|
||||
x.Insert(0, yE3);
|
||||
}
|
||||
|
||||
private static uint ShaHashSigma0(uint x)
|
||||
{
|
||||
return x.Ror(2) ^ x.Ror(13) ^ x.Ror(22);
|
||||
}
|
||||
|
||||
private static uint ShaHashSigma1(uint x)
|
||||
{
|
||||
return x.Ror(6) ^ x.Ror(11) ^ x.Ror(25);
|
||||
}
|
||||
|
||||
private static uint Ror(this uint value, int count)
|
||||
{
|
||||
return (value >> count) | (value << (32 - count));
|
||||
}
|
||||
|
||||
private static uint Lsr(this uint value, int count)
|
||||
{
|
||||
return value >> count;
|
||||
}
|
||||
|
||||
private static uint ULongPart(this ulong value, int part)
|
||||
{
|
||||
return part == 0
|
||||
? (uint)(value & 0xFFFFFFFFUL)
|
||||
: (uint)(value >> 32);
|
||||
}
|
||||
#endregion
|
||||
|
||||
public static V128 PolynomialMult64_128(ulong op1, ulong op2)
|
||||
{
|
||||
V128 result = V128.Zero;
|
||||
|
||||
V128 op2_128 = new V128(op2, 0);
|
||||
|
||||
for (int i = 0; i < 64; i++)
|
||||
{
|
||||
if (((op1 >> i) & 1) == 1)
|
||||
{
|
||||
result ^= op2_128 << i;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
3480
src/ARMeilleure/Instructions/SoftFloat.cs
Normal file
3480
src/ARMeilleure/Instructions/SoftFloat.cs
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user