Compare commits
435 Commits
Author | SHA1 | Date | |
---|---|---|---|
b6ac45d36d | |||
7afae8c699 | |||
7835968214 | |||
0aceb534cb | |||
a0af6e4d07 | |||
f61b7818c3 | |||
a2a97e1b11 | |||
8b2625b0be | |||
651e24fed9 | |||
41b104d0fb | |||
bc44b85b0b | |||
01c2b8097c | |||
4bd2ca3f0d | |||
e63157cc33 | |||
7f2fb049f5 | |||
4744bde0e5 | |||
4a835bb2b9 | |||
ddc9ae2a83 | |||
d6d3cdd573 | |||
53bd4c9f60 | |||
eca8808649 | |||
f6c3f1cdfd | |||
8026e1c804 | |||
d9f9bbfaa6 | |||
fe9e19d8cc | |||
fb55f57da7 | |||
44862dce3e | |||
e601419bd4 | |||
d6bc0de785 | |||
9f26fd3600 | |||
88df636c87 | |||
7ccff037e8 | |||
a745913329 | |||
e6700b314f | |||
e2cfe6fe44 | |||
210f475484 | |||
ddb6493896 | |||
f631933e60 | |||
5ff6ea6d82 | |||
c2d9c6955d | |||
fbe0c211c1 | |||
db0f3c0b74 | |||
34447d7359 | |||
5f771f5661 | |||
93cd327873 | |||
12cbacffca | |||
437c78e198 | |||
f09bba82b9 | |||
93d78f9ac4 | |||
cd7b52f995 | |||
7f96dbc024 | |||
3e5c211394 | |||
153b8bfc7c | |||
c6a699414a | |||
2563f88de0 | |||
b0b7843d5c | |||
6ed613a6e6 | |||
64079c034c | |||
17354d59d1 | |||
0c445184c1 | |||
511b558ddc | |||
9b8625d999 | |||
b12ea343d0 | |||
abaa35ad3a | |||
effd546331 | |||
492a046335 | |||
550fd4a733 | |||
33f544fd92 | |||
b423197619 | |||
8edfb2bc7b | |||
ddefb4fff4 | |||
2efd74b9cb | |||
8c61ddd49d | |||
7b2225c6b0 | |||
fe15c77d30 | |||
5e9678c8fa | |||
773e239db7 | |||
42750a74f8 | |||
3ab0a71c7b | |||
6e784e0aca | |||
5a0aa074b6 | |||
93aa40f1fb | |||
bedee64af5 | |||
86931cc3f1 | |||
2be8b6ea45 | |||
f95b7c5877 | |||
eb528ae0f0 | |||
487261592e | |||
9e04e6cba1 | |||
4cf2419e6c | |||
440abac9f8 | |||
732714349e | |||
016262514d | |||
326749498b | |||
fec8291c17 | |||
c5d9e67cb2 | |||
e5261228d7 | |||
e61c09bc85 | |||
ac2444f908 | |||
9c6071a645 | |||
fa32ef9275 | |||
7805d27e67 | |||
6c515e1822 | |||
8a363b5df2 | |||
2b5abac809 | |||
c19c8bbade | |||
1c7a90ef35 | |||
3b46bb73f7 | |||
2457cfc911 | |||
515fc32b21 | |||
0684b00b3c | |||
02b5c7ea89 | |||
801b71a128 | |||
12c5f6ee89 | |||
79a1314ee4 | |||
e9848339dd | |||
6e28a4dd13 | |||
7c989f88bd | |||
16fa983704 | |||
40daca5684 | |||
981e0c082d | |||
cebfa54467 | |||
fc20d9b925 | |||
0a75b73fa4 | |||
46b7c905f5 | |||
40f2bd37e3 | |||
9288ffd26d | |||
2cdc82cb91 | |||
6aa8d71588 | |||
9becbd7d72 | |||
e055217292 | |||
fbaf62c230 | |||
b186ec9fc5 | |||
0191e2396a | |||
e96299eef5 | |||
ff53dcf560 | |||
2de78a2d55 | |||
b29ded1d60 | |||
9860bfb2cd | |||
f6ada8d169 | |||
42d31f646d | |||
07fc3ded68 | |||
fd01259d2b | |||
7ffe7f8442 | |||
2b2ce68f07 | |||
bc53d00463 | |||
bddb2a1483 | |||
e3bacfa774 | |||
7c2f07d124 | |||
ede5b3c324 | |||
df5be5812f | |||
bc392e55df | |||
fffc3ed193 | |||
7d160e98fd | |||
bf96bc84a8 | |||
91e4caaa69 | |||
efbd29463d | |||
7608cb37ab | |||
d604e98227 | |||
58907e2c29 | |||
649d372f7d | |||
f9a538bb0f | |||
f92921a6d1 | |||
32d21ddf17 | |||
82f90704a0 | |||
f978d3726a | |||
6f28c4abad | |||
105c9712c1 | |||
4d804ed45e | |||
4a27d29412 | |||
5bd2c58ad6 | |||
cf4c78b9c8 | |||
52aa4b6c22 | |||
5a02433080 | |||
915a0f7173 | |||
0cc266ff19 | |||
9a1b74799d | |||
638f3761f3 | |||
193ca3c9a2 | |||
eb0bb36bbf | |||
0e95a8271a | |||
76b474e97b | |||
27ee86f33b | |||
f7ec310231 | |||
e94d24f508 | |||
2bf4555591 | |||
86de288142 | |||
f35aa8e9d6 | |||
0e8e735a6d | |||
0003a7c118 | |||
2cdcfe46d8 | |||
fe30c03cac | |||
5813b2e354 | |||
af1906ea04 | |||
68848000f7 | |||
d98da47a0f | |||
306f7e93a0 | |||
8954ff3af2 | |||
d2f3adbf69 | |||
d511c845b7 | |||
21c9ac6240 | |||
81c9052847 | |||
9367e3c35d | |||
52cf141874 | |||
8a352df3c6 | |||
c545c59851 | |||
96ea4e8c8e | |||
b8f48bcf64 | |||
6966211e07 | |||
57524a4c8a | |||
f4539c49d8 | |||
12c62fdbc2 | |||
e3c6be5e29 | |||
4741a05df9 | |||
c6676007bf | |||
92b0b7d753 | |||
232237bf28 | |||
c27e453fd3 | |||
0e037d0213 | |||
0dca1fbe12 | |||
35d91a0e58 | |||
a73a5d7e85 | |||
832a5e8852 | |||
96d1f0da2d | |||
597388ecda | |||
1cf6d7b7bb | |||
7bc9d0cdad | |||
dc0dbc50ab | |||
994f4dc77d | |||
c9e297b74c | |||
dd514a115c | |||
7e0b4bd538 | |||
378080eb87 | |||
e8f5e97fa4 | |||
f3873620a3 | |||
986ac9ff83 | |||
42b9c1e8fe | |||
3b375525fb | |||
e6658c133c | |||
5b42a4d2c4 | |||
8f0c89ffd6 | |||
2c9715acf6 | |||
274af65f69 | |||
4ca78eded5 | |||
6cb6b15612 | |||
2725e40838 | |||
c2e4c8f98e | |||
b53e7ffd46 | |||
ac66643346 | |||
21e88f17f6 | |||
5626f2ca1c | |||
402f05b8ef | |||
fb27042e01 | |||
69a9de33d3 | |||
bba51c2eeb | |||
fc26189fe1 | |||
a40c90e7dd | |||
f864a49014 | |||
ecbf303266 | |||
b3bf05356b | |||
cb4b58052f | |||
f8cdd5f484 | |||
22202be394 | |||
17ba217940 | |||
aae4595bdb | |||
880fd3cfcb | |||
f679f25e08 | |||
c2709b3bdd | |||
2b6e81deea | |||
7271f1b18e | |||
5fda543f84 | |||
95c06de4c1 | |||
49c63ea077 | |||
531da8a1c0 | |||
5cbdfbc7a4 | |||
e0544dd9c7 | |||
aa784c3e5e | |||
9205077590 | |||
0ed40c7175 | |||
40d47b7aa2 | |||
ec0bb74968 | |||
42f7f98666 | |||
95bad6995c | |||
3d42995822 | |||
9095941fd1 | |||
ba71141bdc | |||
0a0675a7f6 | |||
a7c6e6a8cf | |||
0bc8151c7e | |||
40c17673f5 | |||
a8950d6ac4 | |||
162798b026 | |||
1b28ecd63e | |||
895d9b53bc | |||
0e06aace45 | |||
adf4ebcd60 | |||
470a8031a4 | |||
5440d4ad5c | |||
dde208b480 | |||
4c3d2d5d75 | |||
fab11ba3f1 | |||
332891b5ff | |||
7df4fcada7 | |||
d6698680be | |||
e5c9838b0b | |||
f8ec878796 | |||
9ff21f9ab6 | |||
aa021085cf | |||
1f5d881860 | |||
1f664100bd | |||
1f5e1ffa80 | |||
264438ff19 | |||
3b8ac1641a | |||
4250732353 | |||
4d1579acbf | |||
6279f5e430 | |||
b7d2bff6aa | |||
7c327fecb3 | |||
cc1a933a2f | |||
dd574146fb | |||
2c94ac455e | |||
e18d258fa0 | |||
36f10df775 | |||
680e548022 | |||
21c4176157 | |||
3b4ff2d6d9 | |||
12504f280c | |||
250fc51374 | |||
206e0882c2 | |||
609abc8b9b | |||
cee7121058 | |||
cd124bda58 | |||
9f12e50a54 | |||
097562bc6c | |||
db4242c5dc | |||
4dd77316f7 | |||
3f98369a17 | |||
c26aeefe03 | |||
666e05f5cb | |||
8d9d508dc7 | |||
e27f5522e2 | |||
add2a9d151 | |||
9e50dd99d7 | |||
0dec91bb42 | |||
d9b63353b0 | |||
eabd0ec93f | |||
138d5dc64a | |||
3e68a87d63 | |||
69b6ef7a4a | |||
40e87c634e | |||
79d1c190db | |||
2bc88467eb | |||
baf8752e74 | |||
d5e4378aea | |||
6dbcdfea47 | |||
c5258cf082 | |||
5c89e22bb9 | |||
11ecff2ff0 | |||
4c3f09644a | |||
e187a8870a | |||
a64fee29dc | |||
9ef94c8292 | |||
915d6d044c | |||
a4780ab33b | |||
a947a45d81 | |||
9db73f74cf | |||
a1efd87c45 | |||
49be977588 | |||
c95be55091 | |||
63dedbda86 | |||
c532118d94 | |||
52d6f2e656 | |||
c9bc4eaf58 | |||
3249f8ff41 | |||
1b41b285ac | |||
f5a6f45b27 | |||
210557951b | |||
4c2d9ff3ff | |||
8198b99935 | |||
460f96967d | |||
7ca779a26d | |||
b5032b3c91 | |||
f0a3dff136 | |||
f659dcb9d8 | |||
a34fb0e939 | |||
21ce8a9b80 | |||
9ecbee8032 | |||
80519af67d | |||
26e30faff3 | |||
0992310b76 | |||
009c1101d2 | |||
ba95ee54ab | |||
4ce4299ca2 | |||
17620d18db | |||
9f1cf6458c | |||
67b4e63cff | |||
c05c688ee8 | |||
b2623dc27d | |||
5131b71437 | |||
7870423671 | |||
b72916fbc1 | |||
da073fce61 | |||
1fc90e57d2 | |||
eafcc314a9 | |||
6e9bd4de13 | |||
05a41b31bc | |||
eed17f963e | |||
c09c0c002d | |||
d56d335c0b | |||
23c844b2aa | |||
81691b9e37 | |||
2dc422bc14 | |||
a80fa5e33f | |||
954e995321 | |||
dad9ab6bb6 | |||
f0562b9c75 | |||
b8556530f2 | |||
4f3af839be | |||
155736c986 | |||
dba908dc78 | |||
ecee34a50c | |||
9b5a0c3889 | |||
80b4972139 | |||
5d85468302 | |||
9b1cc2cec6 | |||
e691622f0a | |||
f663a5cd38 | |||
f7c2e867f4 | |||
cedd200745 | |||
58207685c0 | |||
095ad923ad | |||
f07ae7d53f | |||
c308f09722 | |||
f1eef29409 | |||
1f8d66db7c |
@ -1,8 +1,7 @@
|
||||
# Remove the line below if you want to inherit .editorconfig settings from higher directories
|
||||
root = true
|
||||
|
||||
# C# files
|
||||
[*.cs]
|
||||
[*]
|
||||
|
||||
#### Core EditorConfig Options ####
|
||||
|
||||
@ -12,8 +11,18 @@ indent_style = space
|
||||
tab_width = 4
|
||||
|
||||
# New line preferences
|
||||
end_of_line = crlf
|
||||
insert_final_newline = false
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
|
||||
# JSON files
|
||||
[*.json]
|
||||
|
||||
# Indentation and spacing
|
||||
indent_size = 2
|
||||
tab_width = 2
|
||||
|
||||
# C# files
|
||||
[*.cs]
|
||||
|
||||
#### .NET Coding Conventions ####
|
||||
|
||||
@ -59,10 +68,14 @@ dotnet_style_prefer_simplified_interpolation = true:suggestion
|
||||
dotnet_style_readonly_field = true:suggestion
|
||||
|
||||
# Parameter preferences
|
||||
dotnet_code_quality_unused_parameters = all:suggestion
|
||||
dotnet_code_quality_unused_parameters = all:silent
|
||||
|
||||
#### C# Coding Conventions ####
|
||||
|
||||
# Namespace preferences
|
||||
csharp_style_namespace_declarations = block_scoped:warning
|
||||
resharper_csharp_namespace_body = block_scoped
|
||||
|
||||
# var preferences
|
||||
csharp_style_var_elsewhere = false:silent
|
||||
csharp_style_var_for_built_in_types = false:silent
|
||||
@ -81,7 +94,7 @@ csharp_style_expression_bodied_properties = true:silent
|
||||
# Pattern matching preferences
|
||||
csharp_style_pattern_matching_over_as_with_null_check = true:suggestion
|
||||
csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion
|
||||
csharp_style_prefer_switch_expression = true:suggestion
|
||||
csharp_style_prefer_switch_expression = false:silent
|
||||
|
||||
# Null-checking preferences
|
||||
csharp_style_conditional_delegate_call = true:suggestion
|
||||
@ -90,6 +103,7 @@ csharp_style_conditional_delegate_call = true:suggestion
|
||||
csharp_prefer_static_local_function = true:suggestion
|
||||
csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:silent
|
||||
csharp_style_prefer_readonly_struct = true
|
||||
csharp_style_prefer_method_group_conversion = true
|
||||
|
||||
# Code-block preferences
|
||||
csharp_prefer_braces = true:silent
|
||||
@ -105,6 +119,7 @@ csharp_style_prefer_range_operator = true:suggestion
|
||||
csharp_style_throw_expression = true:suggestion
|
||||
csharp_style_unused_value_assignment_preference = discard_variable:suggestion
|
||||
csharp_style_unused_value_expression_statement_preference = discard_variable:silent
|
||||
csharp_style_implicit_object_creation_when_type_is_apparent = true
|
||||
|
||||
# 'using' directive preferences
|
||||
csharp_using_directive_placement = outside_namespace:silent
|
||||
@ -136,7 +151,6 @@ csharp_space_after_dot = false
|
||||
csharp_space_after_keywords_in_control_flow_statements = true
|
||||
csharp_space_after_semicolon_in_for_statement = true
|
||||
csharp_space_around_binary_operators = before_and_after
|
||||
csharp_space_around_declaration_statements = false
|
||||
csharp_space_before_colon_in_inheritance_clause = true
|
||||
csharp_space_before_comma = false
|
||||
csharp_space_before_dot = false
|
||||
@ -154,23 +168,31 @@ csharp_space_between_square_brackets = false
|
||||
|
||||
# Wrapping preferences
|
||||
csharp_preserve_single_line_blocks = true
|
||||
csharp_preserve_single_line_statements = true
|
||||
csharp_preserve_single_line_statements = false
|
||||
|
||||
#### Naming styles ####
|
||||
|
||||
# Naming rules
|
||||
|
||||
dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion
|
||||
dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface
|
||||
dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i
|
||||
dotnet_naming_rule.interfaces_should_be_prefixed_with_I.severity = suggestion
|
||||
dotnet_naming_rule.interfaces_should_be_prefixed_with_I.symbols = interface
|
||||
dotnet_naming_rule.interfaces_should_be_prefixed_with_I.style = IPascalCase
|
||||
|
||||
dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion
|
||||
dotnet_naming_rule.types_should_be_pascal_case.symbols = types
|
||||
dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case
|
||||
dotnet_naming_rule.types_should_be_pascal_case.style = PascalCase
|
||||
|
||||
dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion
|
||||
dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members
|
||||
dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case
|
||||
dotnet_naming_rule.non_field_members_should_be_pascal_case.style = PascalCase
|
||||
|
||||
dotnet_naming_rule.private_static_readonly_fields_should_be_camel_case_and_prefixed_with__.symbols = private_static_readonly_fields
|
||||
dotnet_naming_rule.private_static_readonly_fields_should_be_camel_case_and_prefixed_with__.severity = suggestion
|
||||
dotnet_naming_rule.private_static_readonly_fields_should_be_camel_case_and_prefixed_with__.style = _camelCase
|
||||
|
||||
dotnet_naming_rule.local_constants_should_be_pascal_case.symbols = local_constants
|
||||
dotnet_naming_rule.local_constants_should_be_pascal_case.severity = suggestion
|
||||
dotnet_naming_rule.local_constants_should_be_pascal_case.style = PascalCase
|
||||
|
||||
# Symbol specifications
|
||||
|
||||
@ -186,14 +208,39 @@ dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, meth
|
||||
dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
|
||||
dotnet_naming_symbols.non_field_members.required_modifiers =
|
||||
|
||||
dotnet_naming_symbols.private_static_readonly_fields.applicable_kinds = field
|
||||
dotnet_naming_symbols.private_static_readonly_fields.applicable_accessibilities = private
|
||||
dotnet_naming_symbols.private_static_readonly_fields.required_modifiers = static, readonly
|
||||
|
||||
dotnet_naming_symbols.local_constants.applicable_kinds = local
|
||||
dotnet_naming_symbols.local_constants.applicable_accessibilities = local
|
||||
dotnet_naming_symbols.local_constants.required_modifiers = const
|
||||
|
||||
# Naming styles
|
||||
|
||||
dotnet_naming_style.pascal_case.required_prefix =
|
||||
dotnet_naming_style.pascal_case.required_suffix =
|
||||
dotnet_naming_style.pascal_case.word_separator =
|
||||
dotnet_naming_style.pascal_case.capitalization = pascal_case
|
||||
dotnet_naming_style._camelCase.required_prefix = _
|
||||
dotnet_naming_style._camelCase.required_suffix =
|
||||
dotnet_naming_style._camelCase.word_separator =
|
||||
dotnet_naming_style._camelCase.capitalization = camel_case
|
||||
|
||||
dotnet_naming_style.begins_with_i.required_prefix = I
|
||||
dotnet_naming_style.begins_with_i.required_suffix =
|
||||
dotnet_naming_style.begins_with_i.word_separator =
|
||||
dotnet_naming_style.begins_with_i.capitalization = pascal_case
|
||||
dotnet_naming_style.PascalCase.required_prefix =
|
||||
dotnet_naming_style.PascalCase.required_suffix =
|
||||
dotnet_naming_style.PascalCase.word_separator =
|
||||
dotnet_naming_style.PascalCase.capitalization = pascal_case
|
||||
|
||||
dotnet_naming_style.IPascalCase.required_prefix = I
|
||||
dotnet_naming_style.IPascalCase.required_suffix =
|
||||
dotnet_naming_style.IPascalCase.word_separator =
|
||||
dotnet_naming_style.IPascalCase.capitalization = pascal_case
|
||||
|
||||
[src/Ryujinx.HLE/HOS/Services/**.cs]
|
||||
# Disable "mark members as static" rule for services
|
||||
dotnet_diagnostic.CA1822.severity = none
|
||||
|
||||
[src/Ryujinx.Ava/UI/ViewModels/**.cs]
|
||||
# Disable "mark members as static" rule for ViewModels
|
||||
dotnet_diagnostic.CA1822.severity = none
|
||||
|
||||
[src/Ryujinx.Tests/Cpu/*.cs]
|
||||
# Disable naming rules for CPU tests
|
||||
dotnet_diagnostic.IDE1006.severity = none
|
||||
|
19
.github/ISSUE_TEMPLATE/missing_shader_instruction.yml
vendored
Normal file
19
.github/ISSUE_TEMPLATE/missing_shader_instruction.yml
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
name: Missing Shader Instruction
|
||||
description: Shader Instruction is missing in Ryujinx.
|
||||
title: "[GPU]"
|
||||
labels: [gpu, not-implemented]
|
||||
body:
|
||||
- type: textarea
|
||||
id: instruction
|
||||
attributes:
|
||||
label: Shader instruction
|
||||
description: What shader instruction is missing?
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: required
|
||||
attributes:
|
||||
label: Required by
|
||||
description: Add links to the [compatibility list page(s)](https://github.com/Ryujinx/Ryujinx-Games-List/issues) of the game(s) that require this instruction.
|
||||
validations:
|
||||
required: true
|
18
.github/csc.json
vendored
Normal file
18
.github/csc.json
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
{
|
||||
"problemMatcher": [
|
||||
{
|
||||
"owner": "csc",
|
||||
"pattern": [
|
||||
{
|
||||
"regexp": "^((?:\\\\|/)(?:[^\\\\/:]+(?:\\\\|/))+[^\\\\/]+)\\((\\d+),(\\d+)\\):\\s+([a-zA-Z]+)\\s+([^:]+):\\s+([^[]+)\\s+\\[",
|
||||
"file": 1,
|
||||
"line": 2,
|
||||
"column": 3,
|
||||
"severity": 4,
|
||||
"code": 5,
|
||||
"message": 6
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
33
.github/labeler.yml
vendored
Normal file
33
.github/labeler.yml
vendored
Normal file
@ -0,0 +1,33 @@
|
||||
audio: 'src/Ryujinx.Audio*/**'
|
||||
|
||||
cpu:
|
||||
- 'src/ARMeilleure/**'
|
||||
- 'src/Ryujinx.Cpu/**'
|
||||
- 'src/Ryujinx.Memory/**'
|
||||
|
||||
gpu:
|
||||
- 'src/Ryujinx.Graphics.*/**'
|
||||
- 'src/Spv.Generator/**'
|
||||
- 'src/Ryujinx.ShaderTools/**'
|
||||
|
||||
'graphics-backend:opengl': 'src/Ryujinx.Graphics.OpenGL/**'
|
||||
'graphics-backend:vulkan':
|
||||
- 'src/Ryujinx.Graphics.Vulkan/**'
|
||||
- 'src/Spv.Generator/**'
|
||||
|
||||
gui:
|
||||
- 'src/Ryujinx/**'
|
||||
- 'src/Ryujinx.Ui.Common/**'
|
||||
- 'src/Ryujinx.Ui.LocaleGenerator/**'
|
||||
- 'src/Ryujinx.Ava/**'
|
||||
|
||||
horizon:
|
||||
- 'src/Ryujinx.HLE/**'
|
||||
- 'src/Ryujinx.Horizon*/**'
|
||||
|
||||
kernel: 'src/Ryujinx.HLE/HOS/Kernel/**'
|
||||
|
||||
infra:
|
||||
- '.github/**'
|
||||
- 'distribution/**'
|
||||
- 'Directory.Packages.props'
|
32
.github/reviewers.yml
vendored
Normal file
32
.github/reviewers.yml
vendored
Normal file
@ -0,0 +1,32 @@
|
||||
audio:
|
||||
- marysaka
|
||||
|
||||
cpu:
|
||||
- gdkchan
|
||||
- riperiperi
|
||||
- marysaka
|
||||
- LDj3SNuD
|
||||
|
||||
gpu:
|
||||
- gdkchan
|
||||
- riperiperi
|
||||
- marysaka
|
||||
|
||||
gui:
|
||||
- Ack77
|
||||
- emmauss
|
||||
- TSRBerry
|
||||
- marysaka
|
||||
|
||||
horizon:
|
||||
- gdkchan
|
||||
- Ack77
|
||||
- marysaka
|
||||
- TSRBerry
|
||||
|
||||
infra:
|
||||
- marysaka
|
||||
- TSRBerry
|
||||
|
||||
default:
|
||||
- '@developers'
|
135
.github/workflows/build.yml
vendored
135
.github/workflows/build.yml
vendored
@ -1,27 +1,18 @@
|
||||
name: Build job
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs: {}
|
||||
#push:
|
||||
# branches: [ master ]
|
||||
# paths-ignore:
|
||||
# - '.github/*'
|
||||
# - '.github/ISSUE_TEMPLATE/**'
|
||||
# - '*.yml'
|
||||
# - 'README.md'
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
paths-ignore:
|
||||
- '.github/*'
|
||||
- '.github/ISSUE_TEMPLATE/**'
|
||||
- '*.yml'
|
||||
- 'README.md'
|
||||
workflow_call:
|
||||
|
||||
env:
|
||||
POWERSHELL_TELEMETRY_OPTOUT: 1
|
||||
DOTNET_CLI_TELEMETRY_OPTOUT: 1
|
||||
RYUJINX_BASE_VERSION: "1.1.0"
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: ${{ matrix.os }} (${{ matrix.configuration }})
|
||||
name: ${{ matrix.OS_NAME }} (${{ matrix.configuration }})
|
||||
runs-on: ${{ matrix.os }}
|
||||
timeout-minutes: 45
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macOS-latest, windows-latest]
|
||||
@ -33,7 +24,7 @@ jobs:
|
||||
RELEASE_ZIP_OS_NAME: linux_x64
|
||||
|
||||
- os: macOS-latest
|
||||
OS_NAME: MacOS x64
|
||||
OS_NAME: macOS x64
|
||||
DOTNET_RUNTIME_IDENTIFIER: osx-x64
|
||||
RELEASE_ZIP_OS_NAME: osx_x64
|
||||
|
||||
@ -43,47 +34,125 @@ jobs:
|
||||
RELEASE_ZIP_OS_NAME: win_x64
|
||||
|
||||
fail-fast: false
|
||||
env:
|
||||
POWERSHELL_TELEMETRY_OPTOUT: 1
|
||||
DOTNET_CLI_TELEMETRY_OPTOUT: 1
|
||||
RYUJINX_BASE_VERSION: "1.1.0"
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: actions/setup-dotnet@v3
|
||||
with:
|
||||
dotnet-version: 7.0.x
|
||||
global-json-file: global.json
|
||||
|
||||
- name: Overwrite csc problem matcher
|
||||
run: echo "::add-matcher::.github/csc.json"
|
||||
|
||||
- name: Get git short hash
|
||||
id: git_short_hash
|
||||
run: echo "result=$(git rev-parse --short "${{ github.sha }}")" >> $GITHUB_OUTPUT
|
||||
shell: bash
|
||||
|
||||
- name: Build
|
||||
run: dotnet build -c "${{ matrix.configuration }}" -p:Version="${{ env.RYUJINX_BASE_VERSION }}" -p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" -p:ExtraDefineConstants=DISABLE_UPDATER
|
||||
|
||||
- name: Test
|
||||
run: dotnet test --no-build -c "${{ matrix.configuration }}"
|
||||
uses: TSRBerry/unstable-commands@v1
|
||||
with:
|
||||
commands: dotnet test --no-build -c "${{ matrix.configuration }}"
|
||||
timeout-minutes: 10
|
||||
retry-codes: 139
|
||||
|
||||
- name: Publish Ryujinx
|
||||
run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.DOTNET_RUNTIME_IDENTIFIER }}" -o ./publish -p:Version="${{ env.RYUJINX_BASE_VERSION }}" -p:DebugType=embedded -p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" -p:ExtraDefineConstants=DISABLE_UPDATER Ryujinx --self-contained true
|
||||
if: github.event_name == 'pull_request'
|
||||
run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.DOTNET_RUNTIME_IDENTIFIER }}" -o ./publish -p:Version="${{ env.RYUJINX_BASE_VERSION }}" -p:DebugType=embedded -p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" -p:ExtraDefineConstants=DISABLE_UPDATER src/Ryujinx --self-contained true
|
||||
if: github.event_name == 'pull_request' && matrix.os != 'macOS-latest'
|
||||
|
||||
- name: Publish Ryujinx.Headless.SDL2
|
||||
run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.DOTNET_RUNTIME_IDENTIFIER }}" -o ./publish_sdl2_headless -p:Version="${{ env.RYUJINX_BASE_VERSION }}" -p:DebugType=embedded -p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" -p:ExtraDefineConstants=DISABLE_UPDATER Ryujinx.Headless.SDL2 --self-contained true
|
||||
if: github.event_name == 'pull_request'
|
||||
run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.DOTNET_RUNTIME_IDENTIFIER }}" -o ./publish_sdl2_headless -p:Version="${{ env.RYUJINX_BASE_VERSION }}" -p:DebugType=embedded -p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" -p:ExtraDefineConstants=DISABLE_UPDATER src/Ryujinx.Headless.SDL2 --self-contained true
|
||||
if: github.event_name == 'pull_request' && matrix.os != 'macOS-latest'
|
||||
|
||||
- name: Publish Ryujinx.Ava
|
||||
run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.DOTNET_RUNTIME_IDENTIFIER }}" -o ./publish_ava -p:Version="${{ env.RYUJINX_BASE_VERSION }}" -p:DebugType=embedded -p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" -p:ExtraDefineConstants=DISABLE_UPDATER Ryujinx.Ava --self-contained true
|
||||
if: github.event_name == 'pull_request'
|
||||
run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.DOTNET_RUNTIME_IDENTIFIER }}" -o ./publish_ava -p:Version="${{ env.RYUJINX_BASE_VERSION }}" -p:DebugType=embedded -p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" -p:ExtraDefineConstants=DISABLE_UPDATER src/Ryujinx.Ava --self-contained true
|
||||
if: github.event_name == 'pull_request' && matrix.os != 'macOS-latest'
|
||||
|
||||
- name: Set executable bit
|
||||
run: |
|
||||
chmod +x ./publish/Ryujinx ./publish/Ryujinx.sh
|
||||
chmod +x ./publish_sdl2_headless/Ryujinx.Headless.SDL2 ./publish_sdl2_headless/Ryujinx.sh
|
||||
chmod +x ./publish_ava/Ryujinx.Ava ./publish_ava/Ryujinx.sh
|
||||
if: github.event_name == 'pull_request' && matrix.os == 'ubuntu-latest'
|
||||
|
||||
- name: Upload Ryujinx artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: ryujinx-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-${{ matrix.RELEASE_ZIP_OS_NAME }}
|
||||
path: publish
|
||||
if: github.event_name == 'pull_request'
|
||||
if: github.event_name == 'pull_request' && matrix.os != 'macOS-latest'
|
||||
|
||||
- name: Upload Ryujinx.Headless.SDL2 artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: sdl2-ryujinx-headless-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-${{ matrix.RELEASE_ZIP_OS_NAME }}
|
||||
path: publish_sdl2_headless
|
||||
if: github.event_name == 'pull_request'
|
||||
if: github.event_name == 'pull_request' && matrix.os != 'macOS-latest'
|
||||
|
||||
- name: Upload Ryujinx.Ava artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: ava-ryujinx-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-${{ matrix.RELEASE_ZIP_OS_NAME }}
|
||||
path: publish_ava
|
||||
if: github.event_name == 'pull_request' && matrix.os != 'macOS-latest'
|
||||
|
||||
build_macos:
|
||||
name: macOS Universal (${{ matrix.configuration }})
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 45
|
||||
strategy:
|
||||
matrix:
|
||||
configuration: [ Debug, Release ]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: actions/setup-dotnet@v3
|
||||
with:
|
||||
global-json-file: global.json
|
||||
|
||||
- name: Setup LLVM 14
|
||||
run: |
|
||||
wget https://apt.llvm.org/llvm.sh
|
||||
chmod +x llvm.sh
|
||||
sudo ./llvm.sh 14
|
||||
|
||||
- name: Install rcodesign
|
||||
run: |
|
||||
mkdir -p $HOME/.bin
|
||||
gh release download -R indygreg/apple-platform-rs -O apple-codesign.tar.gz -p 'apple-codesign-*-x86_64-unknown-linux-musl.tar.gz'
|
||||
tar -xzvf apple-codesign.tar.gz --wildcards '*/rcodesign' --strip-components=1
|
||||
rm apple-codesign.tar.gz
|
||||
mv rcodesign $HOME/.bin/
|
||||
echo "$HOME/.bin" >> $GITHUB_PATH
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Get git short hash
|
||||
id: git_short_hash
|
||||
run: echo "result=$(git rev-parse --short "${{ github.sha }}")" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Publish macOS Ryujinx.Ava
|
||||
run: |
|
||||
./distribution/macos/create_macos_build_ava.sh . publish_tmp_ava publish_ava ./distribution/macos/entitlements.xml "${{ env.RYUJINX_BASE_VERSION }}" "${{ steps.git_short_hash.outputs.result }}" "${{ matrix.configuration }}" "-p:ExtraDefineConstants=DISABLE_UPDATER"
|
||||
|
||||
- name: Publish macOS Ryujinx.Headless.SDL2
|
||||
run: |
|
||||
./distribution/macos/create_macos_build_headless.sh . publish_tmp_headless publish_headless ./distribution/macos/entitlements.xml "${{ env.RYUJINX_BASE_VERSION }}" "${{ steps.git_short_hash.outputs.result }}" "${{ matrix.configuration }}" "-p:ExtraDefineConstants=DISABLE_UPDATER"
|
||||
|
||||
- name: Upload Ryujinx.Ava artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: ava-ryujinx-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-macos_universal
|
||||
path: "publish_ava/*.tar.gz"
|
||||
if: github.event_name == 'pull_request'
|
||||
|
||||
- name: Upload Ryujinx.Headless.SDL2 artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: sdl2-ryujinx-headless-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-macos_universal
|
||||
path: "publish_headless/*.tar.gz"
|
||||
if: github.event_name == 'pull_request'
|
74
.github/workflows/checks.yml
vendored
Normal file
74
.github/workflows/checks.yml
vendored
Normal file
@ -0,0 +1,74 @@
|
||||
name: Perform checks
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
paths:
|
||||
- '**'
|
||||
- '!.github/**'
|
||||
- '!*.yml'
|
||||
- '!*.config'
|
||||
- '!README.md'
|
||||
- '.github/workflows/*.yml'
|
||||
|
||||
permissions:
|
||||
pull-requests: write
|
||||
checks: write
|
||||
|
||||
concurrency:
|
||||
group: pr-checks-${{ github.event.number }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
format:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- uses: actions/setup-dotnet@v3
|
||||
with:
|
||||
global-json-file: global.json
|
||||
|
||||
- name: Overwrite csc problem matcher
|
||||
run: echo "::add-matcher::.github/csc.json"
|
||||
|
||||
- run: dotnet restore
|
||||
|
||||
- name: Print dotnet format version
|
||||
run: dotnet format --version
|
||||
|
||||
- name: Run dotnet format whitespace
|
||||
run: |
|
||||
dotnet format whitespace --verify-no-changes --report ./whitespace-report.json -v d
|
||||
|
||||
# For some unknown reason this step sometimes fails with exit code 139 (segfault?),
|
||||
# so in that case we'll try again (3 tries max).
|
||||
- name: Run dotnet format style
|
||||
uses: TSRBerry/unstable-commands@v1
|
||||
with:
|
||||
commands: dotnet format style --severity info --verify-no-changes --report ./style-report.json -v d
|
||||
timeout-minutes: 5
|
||||
retry-codes: 139
|
||||
|
||||
# For some unknown reason this step sometimes fails with exit code 139 (segfault?),
|
||||
# so in that case we'll try again (3 tries max).
|
||||
- name: Run dotnet format analyzers
|
||||
uses: TSRBerry/unstable-commands@v1
|
||||
with:
|
||||
commands: dotnet format analyzers --severity info --verify-no-changes --report ./analyzers-report.json -v d
|
||||
timeout-minutes: 5
|
||||
retry-codes: 139
|
||||
|
||||
- name: Upload report
|
||||
if: failure()
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: dotnet-format
|
||||
path: ./*-report.json
|
||||
|
||||
pr_build:
|
||||
uses: ./.github/workflows/build.yml
|
||||
needs: format
|
||||
secrets: inherit
|
172
.github/workflows/flatpak.yml
vendored
Normal file
172
.github/workflows/flatpak.yml
vendored
Normal file
@ -0,0 +1,172 @@
|
||||
name: Flatpak release job
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
ryujinx_version:
|
||||
required: true
|
||||
type: string
|
||||
|
||||
|
||||
concurrency: flatpak-release
|
||||
|
||||
jobs:
|
||||
release:
|
||||
timeout-minutes: ${{ fromJSON(vars.JOB_TIMEOUT) }}
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
env:
|
||||
NUGET_PACKAGES: ${{ github.workspace }}/.nuget/packages
|
||||
GIT_COMMITTER_NAME: "RyujinxBot"
|
||||
GIT_COMMITTER_EMAIL: "61127645+RyujinxBot@users.noreply.github.com"
|
||||
RYUJINX_PROJECT_FILE: "src/Ryujinx/Ryujinx.csproj"
|
||||
NUGET_SOURCES_DESTDIR: "nuget-sources"
|
||||
RYUJINX_VERSION: "${{ inputs.ryujinx_version }}"
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
path: Ryujinx
|
||||
|
||||
- uses: actions/setup-dotnet@v3
|
||||
with:
|
||||
global-json-file: Ryujinx/global.json
|
||||
|
||||
- name: Get version info
|
||||
id: version_info
|
||||
working-directory: Ryujinx
|
||||
run: |
|
||||
echo "git_hash=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
repository: flathub/org.ryujinx.Ryujinx
|
||||
token: ${{ secrets.RYUJINX_BOT_PAT }}
|
||||
submodules: recursive
|
||||
path: flathub
|
||||
|
||||
- name: Install dependencies
|
||||
run: python -m pip install PyYAML lxml
|
||||
|
||||
- name: Restore Nuget packages
|
||||
run: dotnet restore Ryujinx/${{ env.RYUJINX_PROJECT_FILE }}
|
||||
|
||||
- name: Generate nuget_sources.json
|
||||
shell: python
|
||||
run: |
|
||||
from pathlib import Path
|
||||
import base64
|
||||
import binascii
|
||||
import json
|
||||
import os
|
||||
|
||||
sources = []
|
||||
|
||||
for path in Path(os.environ['NUGET_PACKAGES']).glob('**/*.nupkg.sha512'):
|
||||
name = path.parent.parent.name
|
||||
version = path.parent.name
|
||||
filename = '{}.{}.nupkg'.format(name, version)
|
||||
url = 'https://api.nuget.org/v3-flatcontainer/{}/{}/{}'.format(name, version, filename)
|
||||
|
||||
with path.open() as fp:
|
||||
sha512 = binascii.hexlify(base64.b64decode(fp.read())).decode('ascii')
|
||||
|
||||
sources.append({
|
||||
'type': 'file',
|
||||
'url': url,
|
||||
'sha512': sha512,
|
||||
'dest': os.environ['NUGET_SOURCES_DESTDIR'],
|
||||
'dest-filename': filename,
|
||||
})
|
||||
|
||||
with open('flathub/nuget_sources.json', 'w') as fp:
|
||||
json.dump(sources, fp, indent=4)
|
||||
|
||||
- name: Update flatpak metadata
|
||||
id: metadata
|
||||
env:
|
||||
RYUJINX_GIT_HASH: ${{ steps.version_info.outputs.git_hash }}
|
||||
shell: python
|
||||
run: |
|
||||
import hashlib
|
||||
import hmac
|
||||
import json
|
||||
import os
|
||||
import yaml
|
||||
from datetime import datetime
|
||||
from lxml import etree
|
||||
|
||||
|
||||
# Ensure we don't destroy multiline strings
|
||||
def str_presenter(dumper, data):
|
||||
if len(data.splitlines()) > 1:
|
||||
return dumper.represent_scalar("tag:yaml.org,2002:str", data, style="|")
|
||||
return dumper.represent_scalar("tag:yaml.org,2002:str", data)
|
||||
|
||||
|
||||
yaml.representer.SafeRepresenter.add_representer(str, str_presenter)
|
||||
|
||||
yaml_file = "flathub/org.ryujinx.Ryujinx.yml"
|
||||
xml_file = "flathub/org.ryujinx.Ryujinx.appdata.xml"
|
||||
|
||||
with open(yaml_file, "r") as f:
|
||||
data = yaml.safe_load(f)
|
||||
|
||||
for source in data["modules"][0]["sources"]:
|
||||
if type(source) is str:
|
||||
continue
|
||||
if (
|
||||
source["type"] == "git"
|
||||
and source["url"] == "https://github.com/Ryujinx/Ryujinx.git"
|
||||
):
|
||||
source["commit"] = os.environ['RYUJINX_GIT_HASH']
|
||||
|
||||
is_same_version = data["modules"][0]["build-options"]["env"]["RYUJINX_VERSION"] == os.environ['RYUJINX_VERSION']
|
||||
|
||||
with open(os.environ['GITHUB_OUTPUT'], "a") as gh_out:
|
||||
if is_same_version:
|
||||
gh_out.write(f"commit_message=Retry update to {os.environ['RYUJINX_VERSION']}")
|
||||
else:
|
||||
gh_out.write(f"commit_message=Update to {os.environ['RYUJINX_VERSION']}")
|
||||
|
||||
if not is_same_version:
|
||||
data["modules"][0]["build-options"]["env"]["RYUJINX_VERSION"] = os.environ['RYUJINX_VERSION']
|
||||
|
||||
with open(yaml_file, "w") as f:
|
||||
yaml.safe_dump(data, f, sort_keys=False)
|
||||
|
||||
parser = etree.XMLParser(remove_blank_text=True)
|
||||
tree = etree.parse(xml_file, parser)
|
||||
|
||||
root = tree.getroot()
|
||||
|
||||
releases = root.find("releases")
|
||||
|
||||
element = etree.Element("release")
|
||||
element.set("version", os.environ['RYUJINX_VERSION'])
|
||||
element.set("date", datetime.now().date().isoformat())
|
||||
releases.insert(0, element)
|
||||
|
||||
# Ensure 4 spaces
|
||||
etree.indent(root, space=" ")
|
||||
|
||||
with open(xml_file, "wb") as f:
|
||||
f.write(
|
||||
etree.tostring(
|
||||
tree,
|
||||
pretty_print=True,
|
||||
encoding="UTF-8",
|
||||
doctype='<?xml version="1.0" encoding="UTF-8"?>',
|
||||
)
|
||||
)
|
||||
|
||||
- name: Push flatpak update
|
||||
working-directory: flathub
|
||||
env:
|
||||
COMMIT_MESSAGE: ${{ steps.metadata.outputs.commit_message }}
|
||||
run: |
|
||||
git config user.name "${{ env.GIT_COMMITTER_NAME }}"
|
||||
git config user.email "${{ env.GIT_COMMITTER_EMAIL }}"
|
||||
git add .
|
||||
git commit -m "$COMMIT_MESSAGE"
|
||||
git push origin master
|
7
.github/workflows/nightly_pr_comment.yml
vendored
7
.github/workflows/nightly_pr_comment.yml
vendored
@ -1,12 +1,15 @@
|
||||
name: Comment PR artifacts links
|
||||
|
||||
on:
|
||||
workflow_run:
|
||||
workflows: ['Build job']
|
||||
workflows: ['Perform checks']
|
||||
types: [completed]
|
||||
|
||||
jobs:
|
||||
pr_comment:
|
||||
if: github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.conclusion == 'success'
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: ${{ fromJSON(vars.JOB_TIMEOUT) }}
|
||||
steps:
|
||||
- uses: actions/github-script@v6
|
||||
with:
|
||||
@ -65,4 +68,4 @@ jobs:
|
||||
} else {
|
||||
core.info(`Creating a comment`);
|
||||
await github.rest.issues.createComment({repo, owner, issue_number, body});
|
||||
}
|
||||
}
|
47
.github/workflows/pr_triage.yml
vendored
Normal file
47
.github/workflows/pr_triage.yml
vendored
Normal file
@ -0,0 +1,47 @@
|
||||
name: "Pull Request Triage"
|
||||
on:
|
||||
pull_request_target:
|
||||
types: [opened, ready_for_review]
|
||||
|
||||
jobs:
|
||||
triage:
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
# Grab sources to get latest labeler.yml
|
||||
- name: Fetch sources
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
# Ensure we pin the source origin as pull_request_target run under forks.
|
||||
fetch-depth: 0
|
||||
repository: Ryujinx/Ryujinx
|
||||
ref: master
|
||||
|
||||
- name: Checkout Ryujinx-Mako
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: Ryujinx/Ryujinx-Mako
|
||||
ref: master
|
||||
path: '.ryujinx-mako'
|
||||
|
||||
- name: Setup Ryujinx-Mako
|
||||
uses: ./.ryujinx-mako/.github/actions/setup-mako
|
||||
|
||||
- name: Update labels based on changes
|
||||
uses: actions/labeler@v4
|
||||
with:
|
||||
sync-labels: true
|
||||
dot: true
|
||||
|
||||
- name: Assign reviewers
|
||||
run: |
|
||||
poetry -n -C .ryujinx-mako run ryujinx-mako update-reviewers ${{ github.repository }} ${{ github.event.pull_request.number }} .github/reviewers.yml
|
||||
shell: bash
|
||||
env:
|
||||
MAKO_APP_ID: ${{ secrets.MAKO_APP_ID }}
|
||||
MAKO_PRIVATE_KEY: ${{ secrets.MAKO_PRIVATE_KEY }}
|
||||
MAKO_INSTALLATION_ID: ${{ secrets.MAKO_INSTALLATION_ID }}
|
210
.github/workflows/release.yml
vendored
210
.github/workflows/release.yml
vendored
@ -6,96 +6,128 @@ on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
paths-ignore:
|
||||
- '.github/*'
|
||||
- '.github/ISSUE_TEMPLATE/**'
|
||||
- '.github/**'
|
||||
- '*.yml'
|
||||
- '*.json'
|
||||
- '*.config'
|
||||
- 'README.md'
|
||||
|
||||
concurrency: release
|
||||
|
||||
env:
|
||||
POWERSHELL_TELEMETRY_OPTOUT: 1
|
||||
DOTNET_CLI_TELEMETRY_OPTOUT: 1
|
||||
RYUJINX_BASE_VERSION: "1.1"
|
||||
RYUJINX_TARGET_RELEASE_CHANNEL_NAME: "master"
|
||||
RYUJINX_TARGET_RELEASE_CHANNEL_OWNER: "Ryujinx"
|
||||
RYUJINX_TARGET_RELEASE_CHANNEL_REPO: "release-channel-master"
|
||||
|
||||
jobs:
|
||||
release:
|
||||
runs-on: windows-latest
|
||||
|
||||
env:
|
||||
POWERSHELL_TELEMETRY_OPTOUT: 1
|
||||
DOTNET_CLI_TELEMETRY_OPTOUT: 1
|
||||
RYUJINX_BASE_VERSION: "1.1"
|
||||
RYUJINX_TARGET_RELEASE_CHANNEL_NAME: "master"
|
||||
RYUJINX_TARGET_RELEASE_CHANNEL_OWNER: "Ryujinx"
|
||||
RYUJINX_TARGET_RELEASE_CHANNEL_REPO: "release-channel-master"
|
||||
|
||||
tag:
|
||||
name: Create tag
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Get version info
|
||||
id: version_info
|
||||
run: |
|
||||
echo "build_version=${{ env.RYUJINX_BASE_VERSION }}.${{ github.run_number }}" >> $GITHUB_OUTPUT
|
||||
shell: bash
|
||||
|
||||
- name: Create tag
|
||||
uses: actions/github-script@v6
|
||||
with:
|
||||
script: |
|
||||
github.rest.git.createRef({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
ref: 'refs/tags/${{ steps.version_info.outputs.build_version }}',
|
||||
sha: context.sha
|
||||
})
|
||||
|
||||
release:
|
||||
name: Release ${{ matrix.OS_NAME }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
timeout-minutes: ${{ fromJSON(vars.JOB_TIMEOUT) }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ ubuntu-latest, windows-latest ]
|
||||
include:
|
||||
- os: ubuntu-latest
|
||||
OS_NAME: Linux x64
|
||||
DOTNET_RUNTIME_IDENTIFIER: linux-x64
|
||||
RELEASE_ZIP_OS_NAME: linux_x64
|
||||
|
||||
- os: windows-latest
|
||||
OS_NAME: Windows x64
|
||||
DOTNET_RUNTIME_IDENTIFIER: win10-x64
|
||||
RELEASE_ZIP_OS_NAME: win_x64
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: actions/setup-dotnet@v3
|
||||
with:
|
||||
dotnet-version: 7.0.x
|
||||
global-json-file: global.json
|
||||
|
||||
- name: Overwrite csc problem matcher
|
||||
run: echo "::add-matcher::.github/csc.json"
|
||||
|
||||
- name: Get version info
|
||||
id: version_info
|
||||
run: |
|
||||
echo "build_version=${{ env.RYUJINX_BASE_VERSION }}.${{ github.run_number }}" >> $GITHUB_OUTPUT
|
||||
echo "git_short_hash=$(git rev-parse --short "${{ github.sha }}")" >> $GITHUB_OUTPUT
|
||||
shell: bash
|
||||
|
||||
- name: Configure for release
|
||||
run: |
|
||||
sed -r --in-place 's/\%\%RYUJINX_BUILD_VERSION\%\%/${{ steps.version_info.outputs.build_version }}/g;' Ryujinx.Common/ReleaseInformation.cs
|
||||
sed -r --in-place 's/\%\%RYUJINX_BUILD_GIT_HASH\%\%/${{ steps.version_info.outputs.git_short_hash }}/g;' Ryujinx.Common/ReleaseInformation.cs
|
||||
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_NAME\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_NAME }}/g;' Ryujinx.Common/ReleaseInformation.cs
|
||||
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_OWNER\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/g;' Ryujinx.Common/ReleaseInformation.cs
|
||||
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_REPO\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/g;' Ryujinx.Common/ReleaseInformation.cs
|
||||
sed -r --in-place 's/\%\%RYUJINX_BUILD_VERSION\%\%/${{ steps.version_info.outputs.build_version }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
||||
sed -r --in-place 's/\%\%RYUJINX_BUILD_GIT_HASH\%\%/${{ steps.version_info.outputs.git_short_hash }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
||||
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_NAME\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_NAME }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
||||
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_OWNER\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
||||
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_REPO\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
||||
shell: bash
|
||||
|
||||
- name: Create output dir
|
||||
run: "mkdir release_output"
|
||||
- name: Publish Windows
|
||||
|
||||
- name: Publish
|
||||
run: |
|
||||
dotnet publish -c Release -r win10-x64 -o ./publish_windows/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded Ryujinx --self-contained true
|
||||
dotnet publish -c Release -r win10-x64 -o ./publish_windows_sdl2_headless/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded Ryujinx.Headless.SDL2 --self-contained true
|
||||
dotnet publish -c Release -r win10-x64 -o ./publish_windows_ava/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded Ryujinx.Ava --self-contained true
|
||||
dotnet publish -c Release -r "${{ matrix.DOTNET_RUNTIME_IDENTIFIER }}" -o ./publish_gtk/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded src/Ryujinx --self-contained true
|
||||
dotnet publish -c Release -r "${{ matrix.DOTNET_RUNTIME_IDENTIFIER }}" -o ./publish_sdl2_headless/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded src/Ryujinx.Headless.SDL2 --self-contained true
|
||||
dotnet publish -c Release -r "${{ matrix.DOTNET_RUNTIME_IDENTIFIER }}" -o ./publish_ava/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded src/Ryujinx.Ava --self-contained true
|
||||
|
||||
- name: Packing Windows builds
|
||||
if: matrix.os == 'windows-latest'
|
||||
run: |
|
||||
pushd publish_windows
|
||||
pushd publish_gtk
|
||||
7z a ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-win_x64.zip publish
|
||||
popd
|
||||
|
||||
pushd publish_windows_sdl2_headless
|
||||
pushd publish_sdl2_headless
|
||||
7z a ../release_output/sdl2-ryujinx-headless-${{ steps.version_info.outputs.build_version }}-win_x64.zip publish
|
||||
popd
|
||||
|
||||
pushd publish_windows_ava
|
||||
pushd publish_ava
|
||||
7z a ../release_output/test-ava-ryujinx-${{ steps.version_info.outputs.build_version }}-win_x64.zip publish
|
||||
popd
|
||||
shell: bash
|
||||
|
||||
- name: Publish Linux
|
||||
run: |
|
||||
dotnet publish -c Release -r linux-x64 -o ./publish_linux/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded Ryujinx --self-contained true
|
||||
dotnet publish -c Release -r linux-x64 -o ./publish_linux_sdl2_headless/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded Ryujinx.Headless.SDL2 --self-contained true
|
||||
dotnet publish -c Release -r linux-x64 -o ./publish_linux_ava/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded Ryujinx.Ava --self-contained true
|
||||
|
||||
- name: Packing Linux builds
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
run: |
|
||||
pushd publish_linux
|
||||
tar --exclude "publish/Ryujinx" --exclude "publish/Ryujinx.sh" -cvf ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar publish
|
||||
python3 ../distribution/misc/add_tar_exec.py ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar "publish/Ryujinx" "publish/Ryujinx"
|
||||
python3 ../distribution/misc/add_tar_exec.py ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar "publish/Ryujinx.sh" "publish/Ryujinx.sh"
|
||||
gzip -9 < ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar > ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar.gz
|
||||
rm ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar
|
||||
pushd publish_gtk
|
||||
chmod +x publish/Ryujinx.sh publish/Ryujinx
|
||||
tar -czvf ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar.gz publish
|
||||
popd
|
||||
|
||||
pushd publish_linux_sdl2_headless
|
||||
tar --exclude "publish/Ryujinx.Headless.SDL2" --exclude "publish/Ryujinx.sh" -cvf ../release_output/sdl2-ryujinx-headless-${{ steps.version_info.outputs.build_version }}-linux_x64.tar publish
|
||||
python3 ../distribution/misc/add_tar_exec.py ../release_output/sdl2-ryujinx-headless-${{ steps.version_info.outputs.build_version }}-linux_x64.tar "publish/Ryujinx.Headless.SDL2" "publish/Ryujinx.Headless.SDL2"
|
||||
python3 ../distribution/misc/add_tar_exec.py ../release_output/sdl2-ryujinx-headless-${{ steps.version_info.outputs.build_version }}-linux_x64.tar "publish/Ryujinx.sh" "publish/Ryujinx.sh"
|
||||
gzip -9 < ../release_output/sdl2-ryujinx-headless-${{ steps.version_info.outputs.build_version }}-linux_x64.tar > ../release_output/sdl2-ryujinx-headless-${{ steps.version_info.outputs.build_version }}-linux_x64.tar.gz
|
||||
rm ../release_output/sdl2-ryujinx-headless-${{ steps.version_info.outputs.build_version }}-linux_x64.tar
|
||||
pushd publish_sdl2_headless
|
||||
chmod +x publish/Ryujinx.sh publish/Ryujinx.Headless.SDL2
|
||||
tar -czvf ../release_output/sdl2-ryujinx-headless-${{ steps.version_info.outputs.build_version }}-linux_x64.tar.gz publish
|
||||
popd
|
||||
|
||||
pushd publish_linux_ava
|
||||
tar --exclude "publish/Ryujinx.Ava" --exclude "publish/Ryujinx.sh" -cvf ../release_output/test-ava-ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar publish
|
||||
python3 ../distribution/misc/add_tar_exec.py ../release_output/test-ava-ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar "publish/Ryujinx.Ava" "publish/Ryujinx.Ava"
|
||||
python3 ../distribution/misc/add_tar_exec.py ../release_output/test-ava-ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar "publish/Ryujinx.sh" "publish/Ryujinx.sh"
|
||||
gzip -9 < ../release_output/test-ava-ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar > ../release_output/test-ava-ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar.gz
|
||||
rm ../release_output/test-ava-ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar
|
||||
pushd publish_ava
|
||||
chmod +x publish/Ryujinx.sh publish/Ryujinx.Ava
|
||||
tar -czvf ../release_output/test-ava-ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar.gz publish
|
||||
popd
|
||||
shell: bash
|
||||
|
||||
@ -105,10 +137,82 @@ jobs:
|
||||
name: ${{ steps.version_info.outputs.build_version }}
|
||||
artifacts: "release_output/*.tar.gz,release_output/*.zip"
|
||||
tag: ${{ steps.version_info.outputs.build_version }}
|
||||
body: "For more informations about this release please check out the official [Changelog](https://github.com/Ryujinx/Ryujinx/wiki/Changelog)."
|
||||
body: "For more information about this release please check out the official [Changelog](https://github.com/Ryujinx/Ryujinx/wiki/Changelog)."
|
||||
omitBodyDuringUpdate: true
|
||||
allowUpdates: true
|
||||
removeArtifacts: true
|
||||
replacesArtifacts: true
|
||||
owner: ${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}
|
||||
repo: ${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}
|
||||
token: ${{ secrets.RELEASE_TOKEN }}
|
||||
|
||||
macos_release:
|
||||
name: Release MacOS universal
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: ${{ fromJSON(vars.JOB_TIMEOUT) }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: actions/setup-dotnet@v3
|
||||
with:
|
||||
global-json-file: global.json
|
||||
|
||||
- name: Setup LLVM 14
|
||||
run: |
|
||||
wget https://apt.llvm.org/llvm.sh
|
||||
chmod +x llvm.sh
|
||||
sudo ./llvm.sh 14
|
||||
|
||||
- name: Install rcodesign
|
||||
run: |
|
||||
mkdir -p $HOME/.bin
|
||||
gh release download -R indygreg/apple-platform-rs -O apple-codesign.tar.gz -p 'apple-codesign-*-x86_64-unknown-linux-musl.tar.gz'
|
||||
tar -xzvf apple-codesign.tar.gz --wildcards '*/rcodesign' --strip-components=1
|
||||
rm apple-codesign.tar.gz
|
||||
mv rcodesign $HOME/.bin/
|
||||
echo "$HOME/.bin" >> $GITHUB_PATH
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Get version info
|
||||
id: version_info
|
||||
run: |
|
||||
echo "build_version=${{ env.RYUJINX_BASE_VERSION }}.${{ github.run_number }}" >> $GITHUB_OUTPUT
|
||||
echo "git_short_hash=$(git rev-parse --short "${{ github.sha }}")" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Configure for release
|
||||
run: |
|
||||
sed -r --in-place 's/\%\%RYUJINX_BUILD_VERSION\%\%/${{ steps.version_info.outputs.build_version }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
||||
sed -r --in-place 's/\%\%RYUJINX_BUILD_GIT_HASH\%\%/${{ steps.version_info.outputs.git_short_hash }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
||||
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_NAME\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_NAME }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
||||
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_OWNER\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
||||
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_REPO\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
||||
shell: bash
|
||||
|
||||
- name: Publish macOS Ryujinx.Ava
|
||||
run: |
|
||||
./distribution/macos/create_macos_build_ava.sh . publish_tmp_ava publish_ava ./distribution/macos/entitlements.xml "${{ steps.version_info.outputs.build_version }}" "${{ steps.version_info.outputs.git_short_hash }}" Release
|
||||
|
||||
- name: Publish macOS Ryujinx.Headless.SDL2
|
||||
run: |
|
||||
./distribution/macos/create_macos_build_headless.sh . publish_tmp_headless publish_headless ./distribution/macos/entitlements.xml "${{ steps.version_info.outputs.build_version }}" "${{ steps.version_info.outputs.git_short_hash }}" Release
|
||||
|
||||
- name: Pushing new release
|
||||
uses: ncipollo/release-action@v1
|
||||
with:
|
||||
name: ${{ steps.version_info.outputs.build_version }}
|
||||
artifacts: "publish_ava/*.tar.gz, publish_headless/*.tar.gz"
|
||||
tag: ${{ steps.version_info.outputs.build_version }}
|
||||
body: "For more information about this release please check out the official [Changelog](https://github.com/Ryujinx/Ryujinx/wiki/Changelog)."
|
||||
omitBodyDuringUpdate: true
|
||||
allowUpdates: true
|
||||
replacesArtifacts: true
|
||||
owner: ${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}
|
||||
repo: ${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}
|
||||
token: ${{ secrets.RELEASE_TOKEN }}
|
||||
|
||||
flatpak_release:
|
||||
uses: ./.github/workflows/flatpak.yml
|
||||
needs: release
|
||||
with:
|
||||
ryujinx_version: "1.1.${{ github.run_number }}"
|
||||
secrets: inherit
|
File diff suppressed because it is too large
Load Diff
@ -1,96 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace ARMeilleure.CodeGen.Arm64
|
||||
{
|
||||
static class CallingConvention
|
||||
{
|
||||
private const int RegistersMask = unchecked((int)0xffffffff);
|
||||
|
||||
// Some of those register have specific roles and can't be used as general purpose registers.
|
||||
// X18 - Reserved for platform specific usage.
|
||||
// X29 - Frame pointer.
|
||||
// X30 - Return address.
|
||||
// X31 - Not an actual register, in some cases maps to SP, and in others to ZR.
|
||||
private const int ReservedRegsMask = (1 << CodeGenCommon.ReservedRegister) | (1 << 18) | (1 << 29) | (1 << 30) | (1 << 31);
|
||||
|
||||
public static int GetIntAvailableRegisters()
|
||||
{
|
||||
return RegistersMask & ~ReservedRegsMask;
|
||||
}
|
||||
|
||||
public static int GetVecAvailableRegisters()
|
||||
{
|
||||
return RegistersMask;
|
||||
}
|
||||
|
||||
public static int GetIntCallerSavedRegisters()
|
||||
{
|
||||
return (GetIntCalleeSavedRegisters() ^ RegistersMask) & ~ReservedRegsMask;
|
||||
}
|
||||
|
||||
public static int GetFpCallerSavedRegisters()
|
||||
{
|
||||
return GetFpCalleeSavedRegisters() ^ RegistersMask;
|
||||
}
|
||||
|
||||
public static int GetVecCallerSavedRegisters()
|
||||
{
|
||||
return GetVecCalleeSavedRegisters() ^ RegistersMask;
|
||||
}
|
||||
|
||||
public static int GetIntCalleeSavedRegisters()
|
||||
{
|
||||
return 0x1ff80000; // X19 to X28
|
||||
}
|
||||
|
||||
public static int GetFpCalleeSavedRegisters()
|
||||
{
|
||||
return 0xff00; // D8 to D15
|
||||
}
|
||||
|
||||
public static int GetVecCalleeSavedRegisters()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static int GetArgumentsOnRegsCount()
|
||||
{
|
||||
return 8;
|
||||
}
|
||||
|
||||
public static int GetIntArgumentRegister(int index)
|
||||
{
|
||||
if ((uint)index < (uint)GetArgumentsOnRegsCount())
|
||||
{
|
||||
return index;
|
||||
}
|
||||
|
||||
throw new ArgumentOutOfRangeException(nameof(index));
|
||||
}
|
||||
|
||||
public static int GetVecArgumentRegister(int index)
|
||||
{
|
||||
if ((uint)index < (uint)GetArgumentsOnRegsCount())
|
||||
{
|
||||
return index;
|
||||
}
|
||||
|
||||
throw new ArgumentOutOfRangeException(nameof(index));
|
||||
}
|
||||
|
||||
public static int GetIntReturnRegister()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static int GetIntReturnRegisterHigh()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
public static int GetVecReturnRegister()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,286 +0,0 @@
|
||||
using ARMeilleure.CodeGen.Linking;
|
||||
using ARMeilleure.CodeGen.RegisterAllocators;
|
||||
using ARMeilleure.IntermediateRepresentation;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
namespace ARMeilleure.CodeGen.Arm64
|
||||
{
|
||||
class CodeGenContext
|
||||
{
|
||||
private const int BccInstLength = 4;
|
||||
private const int CbnzInstLength = 4;
|
||||
private const int LdrLitInstLength = 4;
|
||||
|
||||
private Stream _stream;
|
||||
|
||||
public int StreamOffset => (int)_stream.Length;
|
||||
|
||||
public AllocationResult AllocResult { get; }
|
||||
|
||||
public Assembler Assembler { get; }
|
||||
|
||||
public BasicBlock CurrBlock { get; private set; }
|
||||
|
||||
public bool HasCall { get; }
|
||||
|
||||
public int CallArgsRegionSize { get; }
|
||||
public int FpLrSaveRegionSize { get; }
|
||||
|
||||
private readonly Dictionary<BasicBlock, long> _visitedBlocks;
|
||||
private readonly Dictionary<BasicBlock, List<(ArmCondition Condition, long BranchPos)>> _pendingBranches;
|
||||
|
||||
private struct ConstantPoolEntry
|
||||
{
|
||||
public readonly int Offset;
|
||||
public readonly Symbol Symbol;
|
||||
public readonly List<(Operand, int)> LdrOffsets;
|
||||
|
||||
public ConstantPoolEntry(int offset, Symbol symbol)
|
||||
{
|
||||
Offset = offset;
|
||||
Symbol = symbol;
|
||||
LdrOffsets = new List<(Operand, int)>();
|
||||
}
|
||||
}
|
||||
|
||||
private readonly Dictionary<ulong, ConstantPoolEntry> _constantPool;
|
||||
|
||||
private bool _constantPoolWritten;
|
||||
private long _constantPoolOffset;
|
||||
|
||||
private ArmCondition _jNearCondition;
|
||||
private Operand _jNearValue;
|
||||
|
||||
private long _jNearPosition;
|
||||
|
||||
private readonly bool _relocatable;
|
||||
|
||||
public CodeGenContext(AllocationResult allocResult, int maxCallArgs, int blocksCount, bool relocatable)
|
||||
{
|
||||
_stream = new MemoryStream();
|
||||
|
||||
AllocResult = allocResult;
|
||||
|
||||
Assembler = new Assembler(_stream);
|
||||
|
||||
bool hasCall = maxCallArgs >= 0;
|
||||
|
||||
HasCall = hasCall;
|
||||
|
||||
if (maxCallArgs < 0)
|
||||
{
|
||||
maxCallArgs = 0;
|
||||
}
|
||||
|
||||
CallArgsRegionSize = maxCallArgs * 16;
|
||||
FpLrSaveRegionSize = hasCall ? 16 : 0;
|
||||
|
||||
_visitedBlocks = new Dictionary<BasicBlock, long>();
|
||||
_pendingBranches = new Dictionary<BasicBlock, List<(ArmCondition, long)>>();
|
||||
_constantPool = new Dictionary<ulong, ConstantPoolEntry>();
|
||||
|
||||
_relocatable = relocatable;
|
||||
}
|
||||
|
||||
public void EnterBlock(BasicBlock block)
|
||||
{
|
||||
CurrBlock = block;
|
||||
|
||||
long target = _stream.Position;
|
||||
|
||||
if (_pendingBranches.TryGetValue(block, out var list))
|
||||
{
|
||||
foreach (var tuple in list)
|
||||
{
|
||||
_stream.Seek(tuple.BranchPos, SeekOrigin.Begin);
|
||||
WriteBranch(tuple.Condition, target);
|
||||
}
|
||||
|
||||
_stream.Seek(target, SeekOrigin.Begin);
|
||||
_pendingBranches.Remove(block);
|
||||
}
|
||||
|
||||
_visitedBlocks.Add(block, target);
|
||||
}
|
||||
|
||||
public void JumpTo(BasicBlock target)
|
||||
{
|
||||
JumpTo(ArmCondition.Al, target);
|
||||
}
|
||||
|
||||
public void JumpTo(ArmCondition condition, BasicBlock target)
|
||||
{
|
||||
if (_visitedBlocks.TryGetValue(target, out long offset))
|
||||
{
|
||||
WriteBranch(condition, offset);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!_pendingBranches.TryGetValue(target, out var list))
|
||||
{
|
||||
list = new List<(ArmCondition, long)>();
|
||||
_pendingBranches.Add(target, list);
|
||||
}
|
||||
|
||||
list.Add((condition, _stream.Position));
|
||||
|
||||
_stream.Seek(BccInstLength, SeekOrigin.Current);
|
||||
}
|
||||
}
|
||||
|
||||
private void WriteBranch(ArmCondition condition, long to)
|
||||
{
|
||||
int imm = checked((int)(to - _stream.Position));
|
||||
|
||||
if (condition != ArmCondition.Al)
|
||||
{
|
||||
Assembler.B(condition, imm);
|
||||
}
|
||||
else
|
||||
{
|
||||
Assembler.B(imm);
|
||||
}
|
||||
}
|
||||
|
||||
public void JumpToNear(ArmCondition condition)
|
||||
{
|
||||
_jNearCondition = condition;
|
||||
_jNearPosition = _stream.Position;
|
||||
|
||||
_stream.Seek(BccInstLength, SeekOrigin.Current);
|
||||
}
|
||||
|
||||
public void JumpToNearIfNotZero(Operand value)
|
||||
{
|
||||
_jNearValue = value;
|
||||
_jNearPosition = _stream.Position;
|
||||
|
||||
_stream.Seek(CbnzInstLength, SeekOrigin.Current);
|
||||
}
|
||||
|
||||
public void JumpHere()
|
||||
{
|
||||
long currentPosition = _stream.Position;
|
||||
long offset = currentPosition - _jNearPosition;
|
||||
|
||||
_stream.Seek(_jNearPosition, SeekOrigin.Begin);
|
||||
|
||||
if (_jNearValue != default)
|
||||
{
|
||||
Assembler.Cbnz(_jNearValue, checked((int)offset));
|
||||
_jNearValue = default;
|
||||
}
|
||||
else
|
||||
{
|
||||
Assembler.B(_jNearCondition, checked((int)offset));
|
||||
}
|
||||
|
||||
_stream.Seek(currentPosition, SeekOrigin.Begin);
|
||||
}
|
||||
|
||||
public void ReserveRelocatableConstant(Operand rt, Symbol symbol, ulong value)
|
||||
{
|
||||
if (!_constantPool.TryGetValue(value, out ConstantPoolEntry cpe))
|
||||
{
|
||||
cpe = new ConstantPoolEntry(_constantPool.Count * sizeof(ulong), symbol);
|
||||
_constantPool.Add(value, cpe);
|
||||
}
|
||||
|
||||
cpe.LdrOffsets.Add((rt, (int)_stream.Position));
|
||||
_stream.Seek(LdrLitInstLength, SeekOrigin.Current);
|
||||
}
|
||||
|
||||
private long WriteConstantPool()
|
||||
{
|
||||
if (_constantPoolWritten)
|
||||
{
|
||||
return _constantPoolOffset;
|
||||
}
|
||||
|
||||
long constantPoolBaseOffset = _stream.Position;
|
||||
|
||||
foreach (ulong value in _constantPool.Keys)
|
||||
{
|
||||
WriteUInt64(value);
|
||||
}
|
||||
|
||||
foreach (ConstantPoolEntry cpe in _constantPool.Values)
|
||||
{
|
||||
foreach ((Operand rt, int ldrOffset) in cpe.LdrOffsets)
|
||||
{
|
||||
_stream.Seek(ldrOffset, SeekOrigin.Begin);
|
||||
|
||||
int absoluteOffset = checked((int)(constantPoolBaseOffset + cpe.Offset));
|
||||
int pcRelativeOffset = absoluteOffset - ldrOffset;
|
||||
|
||||
Assembler.LdrLit(rt, pcRelativeOffset);
|
||||
}
|
||||
}
|
||||
|
||||
_stream.Seek(constantPoolBaseOffset + _constantPool.Count * sizeof(ulong), SeekOrigin.Begin);
|
||||
|
||||
_constantPoolOffset = constantPoolBaseOffset;
|
||||
_constantPoolWritten = true;
|
||||
|
||||
return constantPoolBaseOffset;
|
||||
}
|
||||
|
||||
public (byte[], RelocInfo) GetCode()
|
||||
{
|
||||
long constantPoolBaseOffset = WriteConstantPool();
|
||||
|
||||
byte[] code = new byte[_stream.Length];
|
||||
|
||||
long originalPosition = _stream.Position;
|
||||
|
||||
_stream.Seek(0, SeekOrigin.Begin);
|
||||
_stream.Read(code, 0, code.Length);
|
||||
_stream.Seek(originalPosition, SeekOrigin.Begin);
|
||||
|
||||
RelocInfo relocInfo;
|
||||
|
||||
if (_relocatable)
|
||||
{
|
||||
RelocEntry[] relocs = new RelocEntry[_constantPool.Count];
|
||||
|
||||
int index = 0;
|
||||
|
||||
foreach (ConstantPoolEntry cpe in _constantPool.Values)
|
||||
{
|
||||
if (cpe.Symbol.Type != SymbolType.None)
|
||||
{
|
||||
int absoluteOffset = checked((int)(constantPoolBaseOffset + cpe.Offset));
|
||||
relocs[index++] = new RelocEntry(absoluteOffset, cpe.Symbol);
|
||||
}
|
||||
}
|
||||
|
||||
if (index != relocs.Length)
|
||||
{
|
||||
Array.Resize(ref relocs, index);
|
||||
}
|
||||
|
||||
relocInfo = new RelocInfo(relocs);
|
||||
}
|
||||
else
|
||||
{
|
||||
relocInfo = new RelocInfo(new RelocEntry[0]);
|
||||
}
|
||||
|
||||
return (code, relocInfo);
|
||||
}
|
||||
|
||||
private void WriteUInt64(ulong value)
|
||||
{
|
||||
_stream.WriteByte((byte)(value >> 0));
|
||||
_stream.WriteByte((byte)(value >> 8));
|
||||
_stream.WriteByte((byte)(value >> 16));
|
||||
_stream.WriteByte((byte)(value >> 24));
|
||||
_stream.WriteByte((byte)(value >> 32));
|
||||
_stream.WriteByte((byte)(value >> 40));
|
||||
_stream.WriteByte((byte)(value >> 48));
|
||||
_stream.WriteByte((byte)(value >> 56));
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,185 +0,0 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.Intrinsics.Arm;
|
||||
using System.Runtime.Versioning;
|
||||
|
||||
namespace ARMeilleure.CodeGen.Arm64
|
||||
{
|
||||
static partial class HardwareCapabilities
|
||||
{
|
||||
static HardwareCapabilities()
|
||||
{
|
||||
if (!ArmBase.Arm64.IsSupported)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (OperatingSystem.IsLinux())
|
||||
{
|
||||
LinuxFeatureInfoHwCap = (LinuxFeatureFlagsHwCap)getauxval(AT_HWCAP);
|
||||
LinuxFeatureInfoHwCap2 = (LinuxFeatureFlagsHwCap2)getauxval(AT_HWCAP2);
|
||||
}
|
||||
|
||||
if (OperatingSystem.IsMacOS())
|
||||
{
|
||||
for (int i = 0; i < _sysctlNames.Length; i++)
|
||||
{
|
||||
if (CheckSysctlName(_sysctlNames[i]))
|
||||
{
|
||||
MacOsFeatureInfo |= (MacOsFeatureFlags)(1 << i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#region Linux
|
||||
|
||||
private const ulong AT_HWCAP = 16;
|
||||
private const ulong AT_HWCAP2 = 26;
|
||||
|
||||
[LibraryImport("libc", SetLastError = true)]
|
||||
private static partial ulong getauxval(ulong type);
|
||||
|
||||
[Flags]
|
||||
public enum LinuxFeatureFlagsHwCap : ulong
|
||||
{
|
||||
Fp = 1 << 0,
|
||||
Asimd = 1 << 1,
|
||||
Evtstrm = 1 << 2,
|
||||
Aes = 1 << 3,
|
||||
Pmull = 1 << 4,
|
||||
Sha1 = 1 << 5,
|
||||
Sha2 = 1 << 6,
|
||||
Crc32 = 1 << 7,
|
||||
Atomics = 1 << 8,
|
||||
FpHp = 1 << 9,
|
||||
AsimdHp = 1 << 10,
|
||||
CpuId = 1 << 11,
|
||||
AsimdRdm = 1 << 12,
|
||||
Jscvt = 1 << 13,
|
||||
Fcma = 1 << 14,
|
||||
Lrcpc = 1 << 15,
|
||||
DcpOp = 1 << 16,
|
||||
Sha3 = 1 << 17,
|
||||
Sm3 = 1 << 18,
|
||||
Sm4 = 1 << 19,
|
||||
AsimdDp = 1 << 20,
|
||||
Sha512 = 1 << 21,
|
||||
Sve = 1 << 22,
|
||||
AsimdFhm = 1 << 23,
|
||||
Dit = 1 << 24,
|
||||
Uscat = 1 << 25,
|
||||
Ilrcpc = 1 << 26,
|
||||
FlagM = 1 << 27,
|
||||
Ssbs = 1 << 28,
|
||||
Sb = 1 << 29,
|
||||
Paca = 1 << 30,
|
||||
Pacg = 1UL << 31
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum LinuxFeatureFlagsHwCap2 : ulong
|
||||
{
|
||||
Dcpodp = 1 << 0,
|
||||
Sve2 = 1 << 1,
|
||||
SveAes = 1 << 2,
|
||||
SvePmull = 1 << 3,
|
||||
SveBitperm = 1 << 4,
|
||||
SveSha3 = 1 << 5,
|
||||
SveSm4 = 1 << 6,
|
||||
FlagM2 = 1 << 7,
|
||||
Frint = 1 << 8,
|
||||
SveI8mm = 1 << 9,
|
||||
SveF32mm = 1 << 10,
|
||||
SveF64mm = 1 << 11,
|
||||
SveBf16 = 1 << 12,
|
||||
I8mm = 1 << 13,
|
||||
Bf16 = 1 << 14,
|
||||
Dgh = 1 << 15,
|
||||
Rng = 1 << 16,
|
||||
Bti = 1 << 17,
|
||||
Mte = 1 << 18,
|
||||
Ecv = 1 << 19,
|
||||
Afp = 1 << 20,
|
||||
Rpres = 1 << 21,
|
||||
Mte3 = 1 << 22,
|
||||
Sme = 1 << 23,
|
||||
Sme_i16i64 = 1 << 24,
|
||||
Sme_f64f64 = 1 << 25,
|
||||
Sme_i8i32 = 1 << 26,
|
||||
Sme_f16f32 = 1 << 27,
|
||||
Sme_b16f32 = 1 << 28,
|
||||
Sme_f32f32 = 1 << 29,
|
||||
Sme_fa64 = 1 << 30,
|
||||
Wfxt = 1UL << 31,
|
||||
Ebf16 = 1UL << 32,
|
||||
Sve_Ebf16 = 1UL << 33,
|
||||
Cssc = 1UL << 34,
|
||||
Rprfm = 1UL << 35,
|
||||
Sve2p1 = 1UL << 36
|
||||
}
|
||||
|
||||
public static LinuxFeatureFlagsHwCap LinuxFeatureInfoHwCap { get; } = 0;
|
||||
public static LinuxFeatureFlagsHwCap2 LinuxFeatureInfoHwCap2 { get; } = 0;
|
||||
|
||||
#endregion
|
||||
|
||||
#region macOS
|
||||
|
||||
[LibraryImport("libSystem.dylib", SetLastError = true)]
|
||||
private static unsafe partial int sysctlbyname([MarshalAs(UnmanagedType.LPStr)] string name, out int oldValue, ref ulong oldSize, IntPtr newValue, ulong newValueSize);
|
||||
|
||||
[SupportedOSPlatform("macos")]
|
||||
private static bool CheckSysctlName(string name)
|
||||
{
|
||||
ulong size = sizeof(int);
|
||||
if (sysctlbyname(name, out int val, ref size, IntPtr.Zero, 0) == 0 && size == sizeof(int))
|
||||
{
|
||||
return val != 0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static string[] _sysctlNames = new string[]
|
||||
{
|
||||
"hw.optional.floatingpoint",
|
||||
"hw.optional.AdvSIMD",
|
||||
"hw.optional.arm.FEAT_FP16",
|
||||
"hw.optional.arm.FEAT_AES",
|
||||
"hw.optional.arm.FEAT_PMULL",
|
||||
"hw.optional.arm.FEAT_LSE",
|
||||
"hw.optional.armv8_crc32",
|
||||
"hw.optional.arm.FEAT_SHA1",
|
||||
"hw.optional.arm.FEAT_SHA256"
|
||||
};
|
||||
|
||||
[Flags]
|
||||
public enum MacOsFeatureFlags
|
||||
{
|
||||
Fp = 1 << 0,
|
||||
AdvSimd = 1 << 1,
|
||||
Fp16 = 1 << 2,
|
||||
Aes = 1 << 3,
|
||||
Pmull = 1 << 4,
|
||||
Lse = 1 << 5,
|
||||
Crc32 = 1 << 6,
|
||||
Sha1 = 1 << 7,
|
||||
Sha256 = 1 << 8
|
||||
}
|
||||
|
||||
public static MacOsFeatureFlags MacOsFeatureInfo { get; } = 0;
|
||||
|
||||
#endregion
|
||||
|
||||
public static bool SupportsAdvSimd => LinuxFeatureInfoHwCap.HasFlag(LinuxFeatureFlagsHwCap.Asimd) || MacOsFeatureInfo.HasFlag(MacOsFeatureFlags.AdvSimd);
|
||||
public static bool SupportsAes => LinuxFeatureInfoHwCap.HasFlag(LinuxFeatureFlagsHwCap.Aes) || MacOsFeatureInfo.HasFlag(MacOsFeatureFlags.Aes);
|
||||
public static bool SupportsPmull => LinuxFeatureInfoHwCap.HasFlag(LinuxFeatureFlagsHwCap.Pmull) || MacOsFeatureInfo.HasFlag(MacOsFeatureFlags.Pmull);
|
||||
public static bool SupportsLse => LinuxFeatureInfoHwCap.HasFlag(LinuxFeatureFlagsHwCap.Atomics) || MacOsFeatureInfo.HasFlag(MacOsFeatureFlags.Lse);
|
||||
public static bool SupportsCrc32 => LinuxFeatureInfoHwCap.HasFlag(LinuxFeatureFlagsHwCap.Crc32) || MacOsFeatureInfo.HasFlag(MacOsFeatureFlags.Crc32);
|
||||
public static bool SupportsSha1 => LinuxFeatureInfoHwCap.HasFlag(LinuxFeatureFlagsHwCap.Sha1) || MacOsFeatureInfo.HasFlag(MacOsFeatureFlags.Sha1);
|
||||
public static bool SupportsSha256 => LinuxFeatureInfoHwCap.HasFlag(LinuxFeatureFlagsHwCap.Sha2) || MacOsFeatureInfo.HasFlag(MacOsFeatureFlags.Sha256);
|
||||
}
|
||||
}
|
@ -1,14 +0,0 @@
|
||||
namespace ARMeilleure.CodeGen.Arm64
|
||||
{
|
||||
struct IntrinsicInfo
|
||||
{
|
||||
public uint Inst { get; }
|
||||
public IntrinsicType Type { get; }
|
||||
|
||||
public IntrinsicInfo(uint inst, IntrinsicType type)
|
||||
{
|
||||
Inst = inst;
|
||||
Type = type;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,461 +0,0 @@
|
||||
using ARMeilleure.Common;
|
||||
using ARMeilleure.IntermediateRepresentation;
|
||||
|
||||
namespace ARMeilleure.CodeGen.Arm64
|
||||
{
|
||||
static class IntrinsicTable
|
||||
{
|
||||
private static IntrinsicInfo[] _intrinTable;
|
||||
|
||||
static IntrinsicTable()
|
||||
{
|
||||
_intrinTable = new IntrinsicInfo[EnumUtils.GetCount(typeof(Intrinsic))];
|
||||
|
||||
Add(Intrinsic.Arm64AbsS, new IntrinsicInfo(0x5e20b800u, IntrinsicType.ScalarUnary));
|
||||
Add(Intrinsic.Arm64AbsV, new IntrinsicInfo(0x0e20b800u, IntrinsicType.VectorUnary));
|
||||
Add(Intrinsic.Arm64AddhnV, new IntrinsicInfo(0x0e204000u, IntrinsicType.VectorTernaryRd));
|
||||
Add(Intrinsic.Arm64AddpS, new IntrinsicInfo(0x5e31b800u, IntrinsicType.ScalarUnary));
|
||||
Add(Intrinsic.Arm64AddpV, new IntrinsicInfo(0x0e20bc00u, IntrinsicType.VectorBinary));
|
||||
Add(Intrinsic.Arm64AddvV, new IntrinsicInfo(0x0e31b800u, IntrinsicType.VectorUnary));
|
||||
Add(Intrinsic.Arm64AddS, new IntrinsicInfo(0x5e208400u, IntrinsicType.ScalarBinary));
|
||||
Add(Intrinsic.Arm64AddV, new IntrinsicInfo(0x0e208400u, IntrinsicType.VectorBinary));
|
||||
Add(Intrinsic.Arm64AesdV, new IntrinsicInfo(0x4e285800u, IntrinsicType.Vector128Unary));
|
||||
Add(Intrinsic.Arm64AeseV, new IntrinsicInfo(0x4e284800u, IntrinsicType.Vector128Unary));
|
||||
Add(Intrinsic.Arm64AesimcV, new IntrinsicInfo(0x4e287800u, IntrinsicType.Vector128Unary));
|
||||
Add(Intrinsic.Arm64AesmcV, new IntrinsicInfo(0x4e286800u, IntrinsicType.Vector128Unary));
|
||||
Add(Intrinsic.Arm64AndV, new IntrinsicInfo(0x0e201c00u, IntrinsicType.VectorBinaryBitwise));
|
||||
Add(Intrinsic.Arm64BicVi, new IntrinsicInfo(0x2f001400u, IntrinsicType.VectorBinaryBitwiseImm));
|
||||
Add(Intrinsic.Arm64BicV, new IntrinsicInfo(0x0e601c00u, IntrinsicType.VectorBinaryBitwise));
|
||||
Add(Intrinsic.Arm64BifV, new IntrinsicInfo(0x2ee01c00u, IntrinsicType.VectorTernaryRdBitwise));
|
||||
Add(Intrinsic.Arm64BitV, new IntrinsicInfo(0x2ea01c00u, IntrinsicType.VectorTernaryRdBitwise));
|
||||
Add(Intrinsic.Arm64BslV, new IntrinsicInfo(0x2e601c00u, IntrinsicType.VectorTernaryRdBitwise));
|
||||
Add(Intrinsic.Arm64ClsV, new IntrinsicInfo(0x0e204800u, IntrinsicType.VectorUnary));
|
||||
Add(Intrinsic.Arm64ClzV, new IntrinsicInfo(0x2e204800u, IntrinsicType.VectorUnary));
|
||||
Add(Intrinsic.Arm64CmeqS, new IntrinsicInfo(0x7e208c00u, IntrinsicType.ScalarBinary));
|
||||
Add(Intrinsic.Arm64CmeqV, new IntrinsicInfo(0x2e208c00u, IntrinsicType.VectorBinary));
|
||||
Add(Intrinsic.Arm64CmeqSz, new IntrinsicInfo(0x5e209800u, IntrinsicType.ScalarUnary));
|
||||
Add(Intrinsic.Arm64CmeqVz, new IntrinsicInfo(0x0e209800u, IntrinsicType.VectorUnary));
|
||||
Add(Intrinsic.Arm64CmgeS, new IntrinsicInfo(0x5e203c00u, IntrinsicType.ScalarBinary));
|
||||
Add(Intrinsic.Arm64CmgeV, new IntrinsicInfo(0x0e203c00u, IntrinsicType.VectorBinary));
|
||||
Add(Intrinsic.Arm64CmgeSz, new IntrinsicInfo(0x7e208800u, IntrinsicType.ScalarUnary));
|
||||
Add(Intrinsic.Arm64CmgeVz, new IntrinsicInfo(0x2e208800u, IntrinsicType.VectorUnary));
|
||||
Add(Intrinsic.Arm64CmgtS, new IntrinsicInfo(0x5e203400u, IntrinsicType.ScalarBinary));
|
||||
Add(Intrinsic.Arm64CmgtV, new IntrinsicInfo(0x0e203400u, IntrinsicType.VectorBinary));
|
||||
Add(Intrinsic.Arm64CmgtSz, new IntrinsicInfo(0x5e208800u, IntrinsicType.ScalarUnary));
|
||||
Add(Intrinsic.Arm64CmgtVz, new IntrinsicInfo(0x0e208800u, IntrinsicType.VectorUnary));
|
||||
Add(Intrinsic.Arm64CmhiS, new IntrinsicInfo(0x7e203400u, IntrinsicType.ScalarBinary));
|
||||
Add(Intrinsic.Arm64CmhiV, new IntrinsicInfo(0x2e203400u, IntrinsicType.VectorBinary));
|
||||
Add(Intrinsic.Arm64CmhsS, new IntrinsicInfo(0x7e203c00u, IntrinsicType.ScalarBinary));
|
||||
Add(Intrinsic.Arm64CmhsV, new IntrinsicInfo(0x2e203c00u, IntrinsicType.VectorBinary));
|
||||
Add(Intrinsic.Arm64CmleSz, new IntrinsicInfo(0x7e209800u, IntrinsicType.ScalarUnary));
|
||||
Add(Intrinsic.Arm64CmleVz, new IntrinsicInfo(0x2e209800u, IntrinsicType.VectorUnary));
|
||||
Add(Intrinsic.Arm64CmltSz, new IntrinsicInfo(0x5e20a800u, IntrinsicType.ScalarUnary));
|
||||
Add(Intrinsic.Arm64CmltVz, new IntrinsicInfo(0x0e20a800u, IntrinsicType.VectorUnary));
|
||||
Add(Intrinsic.Arm64CmtstS, new IntrinsicInfo(0x5e208c00u, IntrinsicType.ScalarBinary));
|
||||
Add(Intrinsic.Arm64CmtstV, new IntrinsicInfo(0x0e208c00u, IntrinsicType.VectorBinary));
|
||||
Add(Intrinsic.Arm64CntV, new IntrinsicInfo(0x0e205800u, IntrinsicType.VectorUnary));
|
||||
Add(Intrinsic.Arm64DupSe, new IntrinsicInfo(0x5e000400u, IntrinsicType.ScalarUnaryByElem));
|
||||
Add(Intrinsic.Arm64DupVe, new IntrinsicInfo(0x0e000400u, IntrinsicType.VectorUnaryByElem));
|
||||
Add(Intrinsic.Arm64DupGp, new IntrinsicInfo(0x0e000c00u, IntrinsicType.VectorUnaryByElem));
|
||||
Add(Intrinsic.Arm64EorV, new IntrinsicInfo(0x2e201c00u, IntrinsicType.VectorBinaryBitwise));
|
||||
Add(Intrinsic.Arm64ExtV, new IntrinsicInfo(0x2e000000u, IntrinsicType.VectorExt));
|
||||
Add(Intrinsic.Arm64FabdS, new IntrinsicInfo(0x7ea0d400u, IntrinsicType.ScalarBinary));
|
||||
Add(Intrinsic.Arm64FabdV, new IntrinsicInfo(0x2ea0d400u, IntrinsicType.VectorBinary));
|
||||
Add(Intrinsic.Arm64FabsV, new IntrinsicInfo(0x0ea0f800u, IntrinsicType.VectorUnary));
|
||||
Add(Intrinsic.Arm64FabsS, new IntrinsicInfo(0x1e20c000u, IntrinsicType.ScalarUnary));
|
||||
Add(Intrinsic.Arm64FacgeS, new IntrinsicInfo(0x7e20ec00u, IntrinsicType.ScalarBinary));
|
||||
Add(Intrinsic.Arm64FacgeV, new IntrinsicInfo(0x2e20ec00u, IntrinsicType.VectorBinary));
|
||||
Add(Intrinsic.Arm64FacgtS, new IntrinsicInfo(0x7ea0ec00u, IntrinsicType.ScalarBinary));
|
||||
Add(Intrinsic.Arm64FacgtV, new IntrinsicInfo(0x2ea0ec00u, IntrinsicType.VectorBinary));
|
||||
Add(Intrinsic.Arm64FaddpS, new IntrinsicInfo(0x7e30d800u, IntrinsicType.ScalarUnary));
|
||||
Add(Intrinsic.Arm64FaddpV, new IntrinsicInfo(0x2e20d400u, IntrinsicType.VectorBinary));
|
||||
Add(Intrinsic.Arm64FaddV, new IntrinsicInfo(0x0e20d400u, IntrinsicType.VectorBinary));
|
||||
Add(Intrinsic.Arm64FaddS, new IntrinsicInfo(0x1e202800u, IntrinsicType.ScalarBinary));
|
||||
Add(Intrinsic.Arm64FccmpeS, new IntrinsicInfo(0x1e200410u, IntrinsicType.ScalarFPCompareCond));
|
||||
Add(Intrinsic.Arm64FccmpS, new IntrinsicInfo(0x1e200400u, IntrinsicType.ScalarFPCompareCond));
|
||||
Add(Intrinsic.Arm64FcmeqS, new IntrinsicInfo(0x5e20e400u, IntrinsicType.ScalarBinary));
|
||||
Add(Intrinsic.Arm64FcmeqV, new IntrinsicInfo(0x0e20e400u, IntrinsicType.VectorBinary));
|
||||
Add(Intrinsic.Arm64FcmeqSz, new IntrinsicInfo(0x5ea0d800u, IntrinsicType.ScalarUnary));
|
||||
Add(Intrinsic.Arm64FcmeqVz, new IntrinsicInfo(0x0ea0d800u, IntrinsicType.VectorUnary));
|
||||
Add(Intrinsic.Arm64FcmgeS, new IntrinsicInfo(0x7e20e400u, IntrinsicType.ScalarBinary));
|
||||
Add(Intrinsic.Arm64FcmgeV, new IntrinsicInfo(0x2e20e400u, IntrinsicType.VectorBinary));
|
||||
Add(Intrinsic.Arm64FcmgeSz, new IntrinsicInfo(0x7ea0c800u, IntrinsicType.ScalarUnary));
|
||||
Add(Intrinsic.Arm64FcmgeVz, new IntrinsicInfo(0x2ea0c800u, IntrinsicType.VectorUnary));
|
||||
Add(Intrinsic.Arm64FcmgtS, new IntrinsicInfo(0x7ea0e400u, IntrinsicType.ScalarBinary));
|
||||
Add(Intrinsic.Arm64FcmgtV, new IntrinsicInfo(0x2ea0e400u, IntrinsicType.VectorBinary));
|
||||
Add(Intrinsic.Arm64FcmgtSz, new IntrinsicInfo(0x5ea0c800u, IntrinsicType.ScalarUnary));
|
||||
Add(Intrinsic.Arm64FcmgtVz, new IntrinsicInfo(0x0ea0c800u, IntrinsicType.VectorUnary));
|
||||
Add(Intrinsic.Arm64FcmleSz, new IntrinsicInfo(0x7ea0d800u, IntrinsicType.ScalarUnary));
|
||||
Add(Intrinsic.Arm64FcmleVz, new IntrinsicInfo(0x2ea0d800u, IntrinsicType.VectorUnary));
|
||||
Add(Intrinsic.Arm64FcmltSz, new IntrinsicInfo(0x5ea0e800u, IntrinsicType.ScalarUnary));
|
||||
Add(Intrinsic.Arm64FcmltVz, new IntrinsicInfo(0x0ea0e800u, IntrinsicType.VectorUnary));
|
||||
Add(Intrinsic.Arm64FcmpeS, new IntrinsicInfo(0x1e202010u, IntrinsicType.ScalarFPCompare));
|
||||
Add(Intrinsic.Arm64FcmpS, new IntrinsicInfo(0x1e202000u, IntrinsicType.ScalarFPCompare));
|
||||
Add(Intrinsic.Arm64FcselS, new IntrinsicInfo(0x1e200c00u, IntrinsicType.ScalarFcsel));
|
||||
Add(Intrinsic.Arm64FcvtasS, new IntrinsicInfo(0x5e21c800u, IntrinsicType.ScalarUnary));
|
||||
Add(Intrinsic.Arm64FcvtasV, new IntrinsicInfo(0x0e21c800u, IntrinsicType.VectorUnary));
|
||||
Add(Intrinsic.Arm64FcvtasGp, new IntrinsicInfo(0x1e240000u, IntrinsicType.ScalarFPConvGpr));
|
||||
Add(Intrinsic.Arm64FcvtauS, new IntrinsicInfo(0x7e21c800u, IntrinsicType.ScalarUnary));
|
||||
Add(Intrinsic.Arm64FcvtauV, new IntrinsicInfo(0x2e21c800u, IntrinsicType.VectorUnary));
|
||||
Add(Intrinsic.Arm64FcvtauGp, new IntrinsicInfo(0x1e250000u, IntrinsicType.ScalarFPConvGpr));
|
||||
Add(Intrinsic.Arm64FcvtlV, new IntrinsicInfo(0x0e217800u, IntrinsicType.VectorUnary));
|
||||
Add(Intrinsic.Arm64FcvtmsS, new IntrinsicInfo(0x5e21b800u, IntrinsicType.ScalarUnary));
|
||||
Add(Intrinsic.Arm64FcvtmsV, new IntrinsicInfo(0x0e21b800u, IntrinsicType.VectorUnary));
|
||||
Add(Intrinsic.Arm64FcvtmsGp, new IntrinsicInfo(0x1e300000u, IntrinsicType.ScalarFPConvGpr));
|
||||
Add(Intrinsic.Arm64FcvtmuS, new IntrinsicInfo(0x7e21b800u, IntrinsicType.ScalarUnary));
|
||||
Add(Intrinsic.Arm64FcvtmuV, new IntrinsicInfo(0x2e21b800u, IntrinsicType.VectorUnary));
|
||||
Add(Intrinsic.Arm64FcvtmuGp, new IntrinsicInfo(0x1e310000u, IntrinsicType.ScalarFPConvGpr));
|
||||
Add(Intrinsic.Arm64FcvtnsS, new IntrinsicInfo(0x5e21a800u, IntrinsicType.ScalarUnary));
|
||||
Add(Intrinsic.Arm64FcvtnsV, new IntrinsicInfo(0x0e21a800u, IntrinsicType.VectorUnary));
|
||||
Add(Intrinsic.Arm64FcvtnsGp, new IntrinsicInfo(0x1e200000u, IntrinsicType.ScalarFPConvGpr));
|
||||
Add(Intrinsic.Arm64FcvtnuS, new IntrinsicInfo(0x7e21a800u, IntrinsicType.ScalarUnary));
|
||||
Add(Intrinsic.Arm64FcvtnuV, new IntrinsicInfo(0x2e21a800u, IntrinsicType.VectorUnary));
|
||||
Add(Intrinsic.Arm64FcvtnuGp, new IntrinsicInfo(0x1e210000u, IntrinsicType.ScalarFPConvGpr));
|
||||
Add(Intrinsic.Arm64FcvtnV, new IntrinsicInfo(0x0e216800u, IntrinsicType.VectorBinaryRd));
|
||||
Add(Intrinsic.Arm64FcvtpsS, new IntrinsicInfo(0x5ea1a800u, IntrinsicType.ScalarUnary));
|
||||
Add(Intrinsic.Arm64FcvtpsV, new IntrinsicInfo(0x0ea1a800u, IntrinsicType.VectorUnary));
|
||||
Add(Intrinsic.Arm64FcvtpsGp, new IntrinsicInfo(0x1e280000u, IntrinsicType.ScalarFPConvGpr));
|
||||
Add(Intrinsic.Arm64FcvtpuS, new IntrinsicInfo(0x7ea1a800u, IntrinsicType.ScalarUnary));
|
||||
Add(Intrinsic.Arm64FcvtpuV, new IntrinsicInfo(0x2ea1a800u, IntrinsicType.VectorUnary));
|
||||
Add(Intrinsic.Arm64FcvtpuGp, new IntrinsicInfo(0x1e290000u, IntrinsicType.ScalarFPConvGpr));
|
||||
Add(Intrinsic.Arm64FcvtxnS, new IntrinsicInfo(0x7e216800u, IntrinsicType.ScalarUnary));
|
||||
Add(Intrinsic.Arm64FcvtxnV, new IntrinsicInfo(0x2e216800u, IntrinsicType.VectorUnary));
|
||||
Add(Intrinsic.Arm64FcvtzsSFixed, new IntrinsicInfo(0x5f00fc00u, IntrinsicType.ScalarFPConvFixed));
|
||||
Add(Intrinsic.Arm64FcvtzsVFixed, new IntrinsicInfo(0x0f00fc00u, IntrinsicType.VectorFPConvFixed));
|
||||
Add(Intrinsic.Arm64FcvtzsS, new IntrinsicInfo(0x5ea1b800u, IntrinsicType.ScalarUnary));
|
||||
Add(Intrinsic.Arm64FcvtzsV, new IntrinsicInfo(0x0ea1b800u, IntrinsicType.VectorUnary));
|
||||
Add(Intrinsic.Arm64FcvtzsGpFixed, new IntrinsicInfo(0x1e180000u, IntrinsicType.ScalarFPConvFixedGpr));
|
||||
Add(Intrinsic.Arm64FcvtzsGp, new IntrinsicInfo(0x1e380000u, IntrinsicType.ScalarFPConvGpr));
|
||||
Add(Intrinsic.Arm64FcvtzuSFixed, new IntrinsicInfo(0x7f00fc00u, IntrinsicType.ScalarFPConvFixed));
|
||||
Add(Intrinsic.Arm64FcvtzuVFixed, new IntrinsicInfo(0x2f00fc00u, IntrinsicType.VectorFPConvFixed));
|
||||
Add(Intrinsic.Arm64FcvtzuS, new IntrinsicInfo(0x7ea1b800u, IntrinsicType.ScalarUnary));
|
||||
Add(Intrinsic.Arm64FcvtzuV, new IntrinsicInfo(0x2ea1b800u, IntrinsicType.VectorUnary));
|
||||
Add(Intrinsic.Arm64FcvtzuGpFixed, new IntrinsicInfo(0x1e190000u, IntrinsicType.ScalarFPConvFixedGpr));
|
||||
Add(Intrinsic.Arm64FcvtzuGp, new IntrinsicInfo(0x1e390000u, IntrinsicType.ScalarFPConvGpr));
|
||||
Add(Intrinsic.Arm64FcvtS, new IntrinsicInfo(0x1e224000u, IntrinsicType.ScalarFPConv));
|
||||
Add(Intrinsic.Arm64FdivV, new IntrinsicInfo(0x2e20fc00u, IntrinsicType.VectorBinary));
|
||||
Add(Intrinsic.Arm64FdivS, new IntrinsicInfo(0x1e201800u, IntrinsicType.ScalarBinary));
|
||||
Add(Intrinsic.Arm64FmaddS, new IntrinsicInfo(0x1f000000u, IntrinsicType.ScalarTernary));
|
||||
Add(Intrinsic.Arm64FmaxnmpS, new IntrinsicInfo(0x7e30c800u, IntrinsicType.ScalarUnary));
|
||||
Add(Intrinsic.Arm64FmaxnmpV, new IntrinsicInfo(0x2e20c400u, IntrinsicType.VectorBinary));
|
||||
Add(Intrinsic.Arm64FmaxnmvV, new IntrinsicInfo(0x2e30c800u, IntrinsicType.VectorUnary));
|
||||
Add(Intrinsic.Arm64FmaxnmV, new IntrinsicInfo(0x0e20c400u, IntrinsicType.VectorBinary));
|
||||
Add(Intrinsic.Arm64FmaxnmS, new IntrinsicInfo(0x1e206800u, IntrinsicType.ScalarBinary));
|
||||
Add(Intrinsic.Arm64FmaxpS, new IntrinsicInfo(0x7e30f800u, IntrinsicType.ScalarUnary));
|
||||
Add(Intrinsic.Arm64FmaxpV, new IntrinsicInfo(0x2e20f400u, IntrinsicType.VectorBinary));
|
||||
Add(Intrinsic.Arm64FmaxvV, new IntrinsicInfo(0x2e30f800u, IntrinsicType.VectorUnary));
|
||||
Add(Intrinsic.Arm64FmaxV, new IntrinsicInfo(0x0e20f400u, IntrinsicType.VectorBinary));
|
||||
Add(Intrinsic.Arm64FmaxS, new IntrinsicInfo(0x1e204800u, IntrinsicType.ScalarBinary));
|
||||
Add(Intrinsic.Arm64FminnmpS, new IntrinsicInfo(0x7eb0c800u, IntrinsicType.ScalarUnary));
|
||||
Add(Intrinsic.Arm64FminnmpV, new IntrinsicInfo(0x2ea0c400u, IntrinsicType.VectorBinary));
|
||||
Add(Intrinsic.Arm64FminnmvV, new IntrinsicInfo(0x2eb0c800u, IntrinsicType.VectorUnary));
|
||||
Add(Intrinsic.Arm64FminnmV, new IntrinsicInfo(0x0ea0c400u, IntrinsicType.VectorBinary));
|
||||
Add(Intrinsic.Arm64FminnmS, new IntrinsicInfo(0x1e207800u, IntrinsicType.ScalarBinary));
|
||||
Add(Intrinsic.Arm64FminpS, new IntrinsicInfo(0x7eb0f800u, IntrinsicType.ScalarUnary));
|
||||
Add(Intrinsic.Arm64FminpV, new IntrinsicInfo(0x2ea0f400u, IntrinsicType.VectorBinary));
|
||||
Add(Intrinsic.Arm64FminvV, new IntrinsicInfo(0x2eb0f800u, IntrinsicType.VectorUnary));
|
||||
Add(Intrinsic.Arm64FminV, new IntrinsicInfo(0x0ea0f400u, IntrinsicType.VectorBinary));
|
||||
Add(Intrinsic.Arm64FminS, new IntrinsicInfo(0x1e205800u, IntrinsicType.ScalarBinary));
|
||||
Add(Intrinsic.Arm64FmlaSe, new IntrinsicInfo(0x5f801000u, IntrinsicType.ScalarTernaryFPRdByElem));
|
||||
Add(Intrinsic.Arm64FmlaVe, new IntrinsicInfo(0x0f801000u, IntrinsicType.VectorTernaryFPRdByElem));
|
||||
Add(Intrinsic.Arm64FmlaV, new IntrinsicInfo(0x0e20cc00u, IntrinsicType.VectorTernaryRd));
|
||||
Add(Intrinsic.Arm64FmlsSe, new IntrinsicInfo(0x5f805000u, IntrinsicType.ScalarTernaryFPRdByElem));
|
||||
Add(Intrinsic.Arm64FmlsVe, new IntrinsicInfo(0x0f805000u, IntrinsicType.VectorTernaryFPRdByElem));
|
||||
Add(Intrinsic.Arm64FmlsV, new IntrinsicInfo(0x0ea0cc00u, IntrinsicType.VectorTernaryRd));
|
||||
Add(Intrinsic.Arm64FmovVi, new IntrinsicInfo(0x0f00f400u, IntrinsicType.VectorFmovi));
|
||||
Add(Intrinsic.Arm64FmovS, new IntrinsicInfo(0x1e204000u, IntrinsicType.ScalarUnary));
|
||||
Add(Intrinsic.Arm64FmovGp, new IntrinsicInfo(0x1e260000u, IntrinsicType.ScalarFPConvGpr));
|
||||
Add(Intrinsic.Arm64FmovSi, new IntrinsicInfo(0x1e201000u, IntrinsicType.ScalarFmovi));
|
||||
Add(Intrinsic.Arm64FmsubS, new IntrinsicInfo(0x1f008000u, IntrinsicType.ScalarTernary));
|
||||
Add(Intrinsic.Arm64FmulxSe, new IntrinsicInfo(0x7f809000u, IntrinsicType.ScalarBinaryFPByElem));
|
||||
Add(Intrinsic.Arm64FmulxVe, new IntrinsicInfo(0x2f809000u, IntrinsicType.VectorBinaryFPByElem));
|
||||
Add(Intrinsic.Arm64FmulxS, new IntrinsicInfo(0x5e20dc00u, IntrinsicType.ScalarBinary));
|
||||
Add(Intrinsic.Arm64FmulxV, new IntrinsicInfo(0x0e20dc00u, IntrinsicType.VectorBinary));
|
||||
Add(Intrinsic.Arm64FmulSe, new IntrinsicInfo(0x5f809000u, IntrinsicType.ScalarBinaryFPByElem));
|
||||
Add(Intrinsic.Arm64FmulVe, new IntrinsicInfo(0x0f809000u, IntrinsicType.VectorBinaryFPByElem));
|
||||
Add(Intrinsic.Arm64FmulV, new IntrinsicInfo(0x2e20dc00u, IntrinsicType.VectorBinary));
|
||||
Add(Intrinsic.Arm64FmulS, new IntrinsicInfo(0x1e200800u, IntrinsicType.ScalarBinary));
|
||||
Add(Intrinsic.Arm64FnegV, new IntrinsicInfo(0x2ea0f800u, IntrinsicType.VectorUnary));
|
||||
Add(Intrinsic.Arm64FnegS, new IntrinsicInfo(0x1e214000u, IntrinsicType.ScalarUnary));
|
||||
Add(Intrinsic.Arm64FnmaddS, new IntrinsicInfo(0x1f200000u, IntrinsicType.ScalarTernary));
|
||||
Add(Intrinsic.Arm64FnmsubS, new IntrinsicInfo(0x1f208000u, IntrinsicType.ScalarTernary));
|
||||
Add(Intrinsic.Arm64FnmulS, new IntrinsicInfo(0x1e208800u, IntrinsicType.ScalarBinary));
|
||||
Add(Intrinsic.Arm64FrecpeS, new IntrinsicInfo(0x5ea1d800u, IntrinsicType.ScalarUnary));
|
||||
Add(Intrinsic.Arm64FrecpeV, new IntrinsicInfo(0x0ea1d800u, IntrinsicType.VectorUnary));
|
||||
Add(Intrinsic.Arm64FrecpsS, new IntrinsicInfo(0x5e20fc00u, IntrinsicType.ScalarBinary));
|
||||
Add(Intrinsic.Arm64FrecpsV, new IntrinsicInfo(0x0e20fc00u, IntrinsicType.VectorBinary));
|
||||
Add(Intrinsic.Arm64FrecpxS, new IntrinsicInfo(0x5ea1f800u, IntrinsicType.ScalarUnary));
|
||||
Add(Intrinsic.Arm64FrintaV, new IntrinsicInfo(0x2e218800u, IntrinsicType.VectorUnary));
|
||||
Add(Intrinsic.Arm64FrintaS, new IntrinsicInfo(0x1e264000u, IntrinsicType.ScalarUnary));
|
||||
Add(Intrinsic.Arm64FrintiV, new IntrinsicInfo(0x2ea19800u, IntrinsicType.VectorUnary));
|
||||
Add(Intrinsic.Arm64FrintiS, new IntrinsicInfo(0x1e27c000u, IntrinsicType.ScalarUnary));
|
||||
Add(Intrinsic.Arm64FrintmV, new IntrinsicInfo(0x0e219800u, IntrinsicType.VectorUnary));
|
||||
Add(Intrinsic.Arm64FrintmS, new IntrinsicInfo(0x1e254000u, IntrinsicType.ScalarUnary));
|
||||
Add(Intrinsic.Arm64FrintnV, new IntrinsicInfo(0x0e218800u, IntrinsicType.VectorUnary));
|
||||
Add(Intrinsic.Arm64FrintnS, new IntrinsicInfo(0x1e244000u, IntrinsicType.ScalarUnary));
|
||||
Add(Intrinsic.Arm64FrintpV, new IntrinsicInfo(0x0ea18800u, IntrinsicType.VectorUnary));
|
||||
Add(Intrinsic.Arm64FrintpS, new IntrinsicInfo(0x1e24c000u, IntrinsicType.ScalarUnary));
|
||||
Add(Intrinsic.Arm64FrintxV, new IntrinsicInfo(0x2e219800u, IntrinsicType.VectorUnary));
|
||||
Add(Intrinsic.Arm64FrintxS, new IntrinsicInfo(0x1e274000u, IntrinsicType.ScalarUnary));
|
||||
Add(Intrinsic.Arm64FrintzV, new IntrinsicInfo(0x0ea19800u, IntrinsicType.VectorUnary));
|
||||
Add(Intrinsic.Arm64FrintzS, new IntrinsicInfo(0x1e25c000u, IntrinsicType.ScalarUnary));
|
||||
Add(Intrinsic.Arm64FrsqrteS, new IntrinsicInfo(0x7ea1d800u, IntrinsicType.ScalarUnary));
|
||||
Add(Intrinsic.Arm64FrsqrteV, new IntrinsicInfo(0x2ea1d800u, IntrinsicType.VectorUnary));
|
||||
Add(Intrinsic.Arm64FrsqrtsS, new IntrinsicInfo(0x5ea0fc00u, IntrinsicType.ScalarBinary));
|
||||
Add(Intrinsic.Arm64FrsqrtsV, new IntrinsicInfo(0x0ea0fc00u, IntrinsicType.VectorBinary));
|
||||
Add(Intrinsic.Arm64FsqrtV, new IntrinsicInfo(0x2ea1f800u, IntrinsicType.VectorUnary));
|
||||
Add(Intrinsic.Arm64FsqrtS, new IntrinsicInfo(0x1e21c000u, IntrinsicType.ScalarUnary));
|
||||
Add(Intrinsic.Arm64FsubV, new IntrinsicInfo(0x0ea0d400u, IntrinsicType.VectorBinary));
|
||||
Add(Intrinsic.Arm64FsubS, new IntrinsicInfo(0x1e203800u, IntrinsicType.ScalarBinary));
|
||||
Add(Intrinsic.Arm64InsVe, new IntrinsicInfo(0x6e000400u, IntrinsicType.VectorInsertByElem));
|
||||
Add(Intrinsic.Arm64InsGp, new IntrinsicInfo(0x4e001c00u, IntrinsicType.ScalarUnaryByElem));
|
||||
Add(Intrinsic.Arm64Ld1rV, new IntrinsicInfo(0x0d40c000u, IntrinsicType.VectorLdSt));
|
||||
Add(Intrinsic.Arm64Ld1Vms, new IntrinsicInfo(0x0c402000u, IntrinsicType.VectorLdSt));
|
||||
Add(Intrinsic.Arm64Ld1Vss, new IntrinsicInfo(0x0d400000u, IntrinsicType.VectorLdStSs));
|
||||
Add(Intrinsic.Arm64Ld2rV, new IntrinsicInfo(0x0d60c000u, IntrinsicType.VectorLdSt));
|
||||
Add(Intrinsic.Arm64Ld2Vms, new IntrinsicInfo(0x0c408000u, IntrinsicType.VectorLdSt));
|
||||
Add(Intrinsic.Arm64Ld2Vss, new IntrinsicInfo(0x0d600000u, IntrinsicType.VectorLdStSs));
|
||||
Add(Intrinsic.Arm64Ld3rV, new IntrinsicInfo(0x0d40e000u, IntrinsicType.VectorLdSt));
|
||||
Add(Intrinsic.Arm64Ld3Vms, new IntrinsicInfo(0x0c404000u, IntrinsicType.VectorLdSt));
|
||||
Add(Intrinsic.Arm64Ld3Vss, new IntrinsicInfo(0x0d402000u, IntrinsicType.VectorLdStSs));
|
||||
Add(Intrinsic.Arm64Ld4rV, new IntrinsicInfo(0x0d60e000u, IntrinsicType.VectorLdSt));
|
||||
Add(Intrinsic.Arm64Ld4Vms, new IntrinsicInfo(0x0c400000u, IntrinsicType.VectorLdSt));
|
||||
Add(Intrinsic.Arm64Ld4Vss, new IntrinsicInfo(0x0d602000u, IntrinsicType.VectorLdStSs));
|
||||
Add(Intrinsic.Arm64MlaVe, new IntrinsicInfo(0x2f000000u, IntrinsicType.VectorTernaryRdByElem));
|
||||
Add(Intrinsic.Arm64MlaV, new IntrinsicInfo(0x0e209400u, IntrinsicType.VectorTernaryRd));
|
||||
Add(Intrinsic.Arm64MlsVe, new IntrinsicInfo(0x2f004000u, IntrinsicType.VectorTernaryRdByElem));
|
||||
Add(Intrinsic.Arm64MlsV, new IntrinsicInfo(0x2e209400u, IntrinsicType.VectorTernaryRd));
|
||||
Add(Intrinsic.Arm64MoviV, new IntrinsicInfo(0x0f000400u, IntrinsicType.VectorMovi));
|
||||
Add(Intrinsic.Arm64MrsFpsr, new IntrinsicInfo(0xd53b4420u, IntrinsicType.GetRegister));
|
||||
Add(Intrinsic.Arm64MsrFpsr, new IntrinsicInfo(0xd51b4420u, IntrinsicType.SetRegister));
|
||||
Add(Intrinsic.Arm64MulVe, new IntrinsicInfo(0x0f008000u, IntrinsicType.VectorBinaryByElem));
|
||||
Add(Intrinsic.Arm64MulV, new IntrinsicInfo(0x0e209c00u, IntrinsicType.VectorBinary));
|
||||
Add(Intrinsic.Arm64MvniV, new IntrinsicInfo(0x2f000400u, IntrinsicType.VectorMvni));
|
||||
Add(Intrinsic.Arm64NegS, new IntrinsicInfo(0x7e20b800u, IntrinsicType.ScalarUnary));
|
||||
Add(Intrinsic.Arm64NegV, new IntrinsicInfo(0x2e20b800u, IntrinsicType.VectorUnary));
|
||||
Add(Intrinsic.Arm64NotV, new IntrinsicInfo(0x2e205800u, IntrinsicType.VectorUnaryBitwise));
|
||||
Add(Intrinsic.Arm64OrnV, new IntrinsicInfo(0x0ee01c00u, IntrinsicType.VectorBinaryBitwise));
|
||||
Add(Intrinsic.Arm64OrrVi, new IntrinsicInfo(0x0f001400u, IntrinsicType.VectorBinaryBitwiseImm));
|
||||
Add(Intrinsic.Arm64OrrV, new IntrinsicInfo(0x0ea01c00u, IntrinsicType.VectorBinaryBitwise));
|
||||
Add(Intrinsic.Arm64PmullV, new IntrinsicInfo(0x0e20e000u, IntrinsicType.VectorBinary));
|
||||
Add(Intrinsic.Arm64PmulV, new IntrinsicInfo(0x2e209c00u, IntrinsicType.VectorBinary));
|
||||
Add(Intrinsic.Arm64RaddhnV, new IntrinsicInfo(0x2e204000u, IntrinsicType.VectorTernaryRd));
|
||||
Add(Intrinsic.Arm64RbitV, new IntrinsicInfo(0x2e605800u, IntrinsicType.VectorUnaryBitwise));
|
||||
Add(Intrinsic.Arm64Rev16V, new IntrinsicInfo(0x0e201800u, IntrinsicType.VectorUnary));
|
||||
Add(Intrinsic.Arm64Rev32V, new IntrinsicInfo(0x2e200800u, IntrinsicType.VectorUnary));
|
||||
Add(Intrinsic.Arm64Rev64V, new IntrinsicInfo(0x0e200800u, IntrinsicType.VectorUnary));
|
||||
Add(Intrinsic.Arm64RshrnV, new IntrinsicInfo(0x0f008c00u, IntrinsicType.VectorTernaryShrRd));
|
||||
Add(Intrinsic.Arm64RsubhnV, new IntrinsicInfo(0x2e206000u, IntrinsicType.VectorTernaryRd));
|
||||
Add(Intrinsic.Arm64SabalV, new IntrinsicInfo(0x0e205000u, IntrinsicType.VectorTernaryRd));
|
||||
Add(Intrinsic.Arm64SabaV, new IntrinsicInfo(0x0e207c00u, IntrinsicType.VectorTernaryRd));
|
||||
Add(Intrinsic.Arm64SabdlV, new IntrinsicInfo(0x0e207000u, IntrinsicType.VectorBinary));
|
||||
Add(Intrinsic.Arm64SabdV, new IntrinsicInfo(0x0e207400u, IntrinsicType.VectorBinary));
|
||||
Add(Intrinsic.Arm64SadalpV, new IntrinsicInfo(0x0e206800u, IntrinsicType.VectorBinaryRd));
|
||||
Add(Intrinsic.Arm64SaddlpV, new IntrinsicInfo(0x0e202800u, IntrinsicType.VectorUnary));
|
||||
Add(Intrinsic.Arm64SaddlvV, new IntrinsicInfo(0x0e303800u, IntrinsicType.VectorUnary));
|
||||
Add(Intrinsic.Arm64SaddlV, new IntrinsicInfo(0x0e200000u, IntrinsicType.VectorBinary));
|
||||
Add(Intrinsic.Arm64SaddwV, new IntrinsicInfo(0x0e201000u, IntrinsicType.VectorBinary));
|
||||
Add(Intrinsic.Arm64ScvtfSFixed, new IntrinsicInfo(0x5f00e400u, IntrinsicType.ScalarFPConvFixed));
|
||||
Add(Intrinsic.Arm64ScvtfVFixed, new IntrinsicInfo(0x0f00e400u, IntrinsicType.VectorFPConvFixed));
|
||||
Add(Intrinsic.Arm64ScvtfS, new IntrinsicInfo(0x5e21d800u, IntrinsicType.ScalarUnary));
|
||||
Add(Intrinsic.Arm64ScvtfV, new IntrinsicInfo(0x0e21d800u, IntrinsicType.VectorUnary));
|
||||
Add(Intrinsic.Arm64ScvtfGpFixed, new IntrinsicInfo(0x1e020000u, IntrinsicType.ScalarFPConvFixedGpr));
|
||||
Add(Intrinsic.Arm64ScvtfGp, new IntrinsicInfo(0x1e220000u, IntrinsicType.ScalarFPConvGpr));
|
||||
Add(Intrinsic.Arm64Sha1cV, new IntrinsicInfo(0x5e000000u, IntrinsicType.Vector128Binary));
|
||||
Add(Intrinsic.Arm64Sha1hV, new IntrinsicInfo(0x5e280800u, IntrinsicType.Vector128Unary));
|
||||
Add(Intrinsic.Arm64Sha1mV, new IntrinsicInfo(0x5e002000u, IntrinsicType.Vector128Binary));
|
||||
Add(Intrinsic.Arm64Sha1pV, new IntrinsicInfo(0x5e001000u, IntrinsicType.Vector128Binary));
|
||||
Add(Intrinsic.Arm64Sha1su0V, new IntrinsicInfo(0x5e003000u, IntrinsicType.Vector128Binary));
|
||||
Add(Intrinsic.Arm64Sha1su1V, new IntrinsicInfo(0x5e281800u, IntrinsicType.Vector128Unary));
|
||||
Add(Intrinsic.Arm64Sha256h2V, new IntrinsicInfo(0x5e005000u, IntrinsicType.Vector128Binary));
|
||||
Add(Intrinsic.Arm64Sha256hV, new IntrinsicInfo(0x5e004000u, IntrinsicType.Vector128Binary));
|
||||
Add(Intrinsic.Arm64Sha256su0V, new IntrinsicInfo(0x5e282800u, IntrinsicType.Vector128Unary));
|
||||
Add(Intrinsic.Arm64Sha256su1V, new IntrinsicInfo(0x5e006000u, IntrinsicType.Vector128Binary));
|
||||
Add(Intrinsic.Arm64ShaddV, new IntrinsicInfo(0x0e200400u, IntrinsicType.VectorBinary));
|
||||
Add(Intrinsic.Arm64ShllV, new IntrinsicInfo(0x2e213800u, IntrinsicType.VectorUnary));
|
||||
Add(Intrinsic.Arm64ShlS, new IntrinsicInfo(0x5f005400u, IntrinsicType.ScalarBinaryShl));
|
||||
Add(Intrinsic.Arm64ShlV, new IntrinsicInfo(0x0f005400u, IntrinsicType.VectorBinaryShl));
|
||||
Add(Intrinsic.Arm64ShrnV, new IntrinsicInfo(0x0f008400u, IntrinsicType.VectorTernaryShrRd));
|
||||
Add(Intrinsic.Arm64ShsubV, new IntrinsicInfo(0x0e202400u, IntrinsicType.VectorBinary));
|
||||
Add(Intrinsic.Arm64SliS, new IntrinsicInfo(0x7f005400u, IntrinsicType.ScalarTernaryShlRd));
|
||||
Add(Intrinsic.Arm64SliV, new IntrinsicInfo(0x2f005400u, IntrinsicType.VectorTernaryShlRd));
|
||||
Add(Intrinsic.Arm64SmaxpV, new IntrinsicInfo(0x0e20a400u, IntrinsicType.VectorBinary));
|
||||
Add(Intrinsic.Arm64SmaxvV, new IntrinsicInfo(0x0e30a800u, IntrinsicType.VectorUnary));
|
||||
Add(Intrinsic.Arm64SmaxV, new IntrinsicInfo(0x0e206400u, IntrinsicType.VectorBinary));
|
||||
Add(Intrinsic.Arm64SminpV, new IntrinsicInfo(0x0e20ac00u, IntrinsicType.VectorBinary));
|
||||
Add(Intrinsic.Arm64SminvV, new IntrinsicInfo(0x0e31a800u, IntrinsicType.VectorUnary));
|
||||
Add(Intrinsic.Arm64SminV, new IntrinsicInfo(0x0e206c00u, IntrinsicType.VectorBinary));
|
||||
Add(Intrinsic.Arm64SmlalVe, new IntrinsicInfo(0x0f002000u, IntrinsicType.VectorTernaryRdByElem));
|
||||
Add(Intrinsic.Arm64SmlalV, new IntrinsicInfo(0x0e208000u, IntrinsicType.VectorTernaryRd));
|
||||
Add(Intrinsic.Arm64SmlslVe, new IntrinsicInfo(0x0f006000u, IntrinsicType.VectorTernaryRdByElem));
|
||||
Add(Intrinsic.Arm64SmlslV, new IntrinsicInfo(0x0e20a000u, IntrinsicType.VectorTernaryRd));
|
||||
Add(Intrinsic.Arm64SmovV, new IntrinsicInfo(0x0e002c00u, IntrinsicType.VectorUnaryByElem));
|
||||
Add(Intrinsic.Arm64SmullVe, new IntrinsicInfo(0x0f00a000u, IntrinsicType.VectorBinaryByElem));
|
||||
Add(Intrinsic.Arm64SmullV, new IntrinsicInfo(0x0e20c000u, IntrinsicType.VectorBinary));
|
||||
Add(Intrinsic.Arm64SqabsS, new IntrinsicInfo(0x5e207800u, IntrinsicType.ScalarUnary));
|
||||
Add(Intrinsic.Arm64SqabsV, new IntrinsicInfo(0x0e207800u, IntrinsicType.VectorUnary));
|
||||
Add(Intrinsic.Arm64SqaddS, new IntrinsicInfo(0x5e200c00u, IntrinsicType.ScalarBinary));
|
||||
Add(Intrinsic.Arm64SqaddV, new IntrinsicInfo(0x0e200c00u, IntrinsicType.VectorBinary));
|
||||
Add(Intrinsic.Arm64SqdmlalSe, new IntrinsicInfo(0x5f003000u, IntrinsicType.ScalarBinaryByElem));
|
||||
Add(Intrinsic.Arm64SqdmlalVe, new IntrinsicInfo(0x0f003000u, IntrinsicType.VectorBinaryByElem));
|
||||
Add(Intrinsic.Arm64SqdmlalS, new IntrinsicInfo(0x5e209000u, IntrinsicType.ScalarBinary));
|
||||
Add(Intrinsic.Arm64SqdmlalV, new IntrinsicInfo(0x0e209000u, IntrinsicType.VectorBinary));
|
||||
Add(Intrinsic.Arm64SqdmlslSe, new IntrinsicInfo(0x5f007000u, IntrinsicType.ScalarBinaryByElem));
|
||||
Add(Intrinsic.Arm64SqdmlslVe, new IntrinsicInfo(0x0f007000u, IntrinsicType.VectorBinaryByElem));
|
||||
Add(Intrinsic.Arm64SqdmlslS, new IntrinsicInfo(0x5e20b000u, IntrinsicType.ScalarBinary));
|
||||
Add(Intrinsic.Arm64SqdmlslV, new IntrinsicInfo(0x0e20b000u, IntrinsicType.VectorBinary));
|
||||
Add(Intrinsic.Arm64SqdmulhSe, new IntrinsicInfo(0x5f00c000u, IntrinsicType.ScalarBinaryByElem));
|
||||
Add(Intrinsic.Arm64SqdmulhVe, new IntrinsicInfo(0x0f00c000u, IntrinsicType.VectorBinaryByElem));
|
||||
Add(Intrinsic.Arm64SqdmulhS, new IntrinsicInfo(0x5e20b400u, IntrinsicType.ScalarBinary));
|
||||
Add(Intrinsic.Arm64SqdmulhV, new IntrinsicInfo(0x0e20b400u, IntrinsicType.VectorBinary));
|
||||
Add(Intrinsic.Arm64SqdmullSe, new IntrinsicInfo(0x5f00b000u, IntrinsicType.ScalarBinaryByElem));
|
||||
Add(Intrinsic.Arm64SqdmullVe, new IntrinsicInfo(0x0f00b000u, IntrinsicType.VectorBinaryByElem));
|
||||
Add(Intrinsic.Arm64SqdmullS, new IntrinsicInfo(0x5e20d000u, IntrinsicType.ScalarBinary));
|
||||
Add(Intrinsic.Arm64SqdmullV, new IntrinsicInfo(0x0e20d000u, IntrinsicType.VectorBinary));
|
||||
Add(Intrinsic.Arm64SqnegS, new IntrinsicInfo(0x7e207800u, IntrinsicType.ScalarUnary));
|
||||
Add(Intrinsic.Arm64SqnegV, new IntrinsicInfo(0x2e207800u, IntrinsicType.VectorUnary));
|
||||
Add(Intrinsic.Arm64SqrdmulhSe, new IntrinsicInfo(0x5f00d000u, IntrinsicType.ScalarBinaryByElem));
|
||||
Add(Intrinsic.Arm64SqrdmulhVe, new IntrinsicInfo(0x0f00d000u, IntrinsicType.VectorBinaryByElem));
|
||||
Add(Intrinsic.Arm64SqrdmulhS, new IntrinsicInfo(0x7e20b400u, IntrinsicType.ScalarBinary));
|
||||
Add(Intrinsic.Arm64SqrdmulhV, new IntrinsicInfo(0x2e20b400u, IntrinsicType.VectorBinary));
|
||||
Add(Intrinsic.Arm64SqrshlS, new IntrinsicInfo(0x5e205c00u, IntrinsicType.ScalarBinary));
|
||||
Add(Intrinsic.Arm64SqrshlV, new IntrinsicInfo(0x0e205c00u, IntrinsicType.VectorBinary));
|
||||
Add(Intrinsic.Arm64SqrshrnS, new IntrinsicInfo(0x5f009c00u, IntrinsicType.ScalarTernaryShrRd));
|
||||
Add(Intrinsic.Arm64SqrshrnV, new IntrinsicInfo(0x0f009c00u, IntrinsicType.VectorTernaryShrRd));
|
||||
Add(Intrinsic.Arm64SqrshrunS, new IntrinsicInfo(0x7f008c00u, IntrinsicType.ScalarTernaryShrRd));
|
||||
Add(Intrinsic.Arm64SqrshrunV, new IntrinsicInfo(0x2f008c00u, IntrinsicType.VectorTernaryShrRd));
|
||||
Add(Intrinsic.Arm64SqshluS, new IntrinsicInfo(0x7f006400u, IntrinsicType.ScalarBinaryShl));
|
||||
Add(Intrinsic.Arm64SqshluV, new IntrinsicInfo(0x2f006400u, IntrinsicType.VectorBinaryShl));
|
||||
Add(Intrinsic.Arm64SqshlSi, new IntrinsicInfo(0x5f007400u, IntrinsicType.ScalarBinaryShl));
|
||||
Add(Intrinsic.Arm64SqshlVi, new IntrinsicInfo(0x0f007400u, IntrinsicType.VectorBinaryShl));
|
||||
Add(Intrinsic.Arm64SqshlS, new IntrinsicInfo(0x5e204c00u, IntrinsicType.ScalarBinary));
|
||||
Add(Intrinsic.Arm64SqshlV, new IntrinsicInfo(0x0e204c00u, IntrinsicType.VectorBinary));
|
||||
Add(Intrinsic.Arm64SqshrnS, new IntrinsicInfo(0x5f009400u, IntrinsicType.ScalarTernaryShrRd));
|
||||
Add(Intrinsic.Arm64SqshrnV, new IntrinsicInfo(0x0f009400u, IntrinsicType.VectorTernaryShrRd));
|
||||
Add(Intrinsic.Arm64SqshrunS, new IntrinsicInfo(0x7f008400u, IntrinsicType.ScalarTernaryShrRd));
|
||||
Add(Intrinsic.Arm64SqshrunV, new IntrinsicInfo(0x2f008400u, IntrinsicType.VectorTernaryShrRd));
|
||||
Add(Intrinsic.Arm64SqsubS, new IntrinsicInfo(0x5e202c00u, IntrinsicType.ScalarBinary));
|
||||
Add(Intrinsic.Arm64SqsubV, new IntrinsicInfo(0x0e202c00u, IntrinsicType.VectorBinary));
|
||||
Add(Intrinsic.Arm64SqxtnS, new IntrinsicInfo(0x5e214800u, IntrinsicType.ScalarBinaryRd));
|
||||
Add(Intrinsic.Arm64SqxtnV, new IntrinsicInfo(0x0e214800u, IntrinsicType.VectorBinaryRd));
|
||||
Add(Intrinsic.Arm64SqxtunS, new IntrinsicInfo(0x7e212800u, IntrinsicType.ScalarBinaryRd));
|
||||
Add(Intrinsic.Arm64SqxtunV, new IntrinsicInfo(0x2e212800u, IntrinsicType.VectorBinaryRd));
|
||||
Add(Intrinsic.Arm64SrhaddV, new IntrinsicInfo(0x0e201400u, IntrinsicType.VectorBinary));
|
||||
Add(Intrinsic.Arm64SriS, new IntrinsicInfo(0x7f004400u, IntrinsicType.ScalarTernaryShrRd));
|
||||
Add(Intrinsic.Arm64SriV, new IntrinsicInfo(0x2f004400u, IntrinsicType.VectorTernaryShrRd));
|
||||
Add(Intrinsic.Arm64SrshlS, new IntrinsicInfo(0x5e205400u, IntrinsicType.ScalarBinary));
|
||||
Add(Intrinsic.Arm64SrshlV, new IntrinsicInfo(0x0e205400u, IntrinsicType.VectorBinary));
|
||||
Add(Intrinsic.Arm64SrshrS, new IntrinsicInfo(0x5f002400u, IntrinsicType.ScalarBinaryShr));
|
||||
Add(Intrinsic.Arm64SrshrV, new IntrinsicInfo(0x0f002400u, IntrinsicType.VectorBinaryShr));
|
||||
Add(Intrinsic.Arm64SrsraS, new IntrinsicInfo(0x5f003400u, IntrinsicType.ScalarTernaryShrRd));
|
||||
Add(Intrinsic.Arm64SrsraV, new IntrinsicInfo(0x0f003400u, IntrinsicType.VectorTernaryShrRd));
|
||||
Add(Intrinsic.Arm64SshllV, new IntrinsicInfo(0x0f00a400u, IntrinsicType.VectorBinaryShl));
|
||||
Add(Intrinsic.Arm64SshlS, new IntrinsicInfo(0x5e204400u, IntrinsicType.ScalarBinary));
|
||||
Add(Intrinsic.Arm64SshlV, new IntrinsicInfo(0x0e204400u, IntrinsicType.VectorBinary));
|
||||
Add(Intrinsic.Arm64SshrS, new IntrinsicInfo(0x5f000400u, IntrinsicType.ScalarBinaryShr));
|
||||
Add(Intrinsic.Arm64SshrV, new IntrinsicInfo(0x0f000400u, IntrinsicType.VectorBinaryShr));
|
||||
Add(Intrinsic.Arm64SsraS, new IntrinsicInfo(0x5f001400u, IntrinsicType.ScalarTernaryShrRd));
|
||||
Add(Intrinsic.Arm64SsraV, new IntrinsicInfo(0x0f001400u, IntrinsicType.VectorTernaryShrRd));
|
||||
Add(Intrinsic.Arm64SsublV, new IntrinsicInfo(0x0e202000u, IntrinsicType.VectorBinary));
|
||||
Add(Intrinsic.Arm64SsubwV, new IntrinsicInfo(0x0e203000u, IntrinsicType.VectorBinary));
|
||||
Add(Intrinsic.Arm64St1Vms, new IntrinsicInfo(0x0c002000u, IntrinsicType.VectorLdSt));
|
||||
Add(Intrinsic.Arm64St1Vss, new IntrinsicInfo(0x0d000000u, IntrinsicType.VectorLdStSs));
|
||||
Add(Intrinsic.Arm64St2Vms, new IntrinsicInfo(0x0c008000u, IntrinsicType.VectorLdSt));
|
||||
Add(Intrinsic.Arm64St2Vss, new IntrinsicInfo(0x0d200000u, IntrinsicType.VectorLdStSs));
|
||||
Add(Intrinsic.Arm64St3Vms, new IntrinsicInfo(0x0c004000u, IntrinsicType.VectorLdSt));
|
||||
Add(Intrinsic.Arm64St3Vss, new IntrinsicInfo(0x0d002000u, IntrinsicType.VectorLdStSs));
|
||||
Add(Intrinsic.Arm64St4Vms, new IntrinsicInfo(0x0c000000u, IntrinsicType.VectorLdSt));
|
||||
Add(Intrinsic.Arm64St4Vss, new IntrinsicInfo(0x0d202000u, IntrinsicType.VectorLdStSs));
|
||||
Add(Intrinsic.Arm64SubhnV, new IntrinsicInfo(0x0e206000u, IntrinsicType.VectorTernaryRd));
|
||||
Add(Intrinsic.Arm64SubS, new IntrinsicInfo(0x7e208400u, IntrinsicType.ScalarBinary));
|
||||
Add(Intrinsic.Arm64SubV, new IntrinsicInfo(0x2e208400u, IntrinsicType.VectorBinary));
|
||||
Add(Intrinsic.Arm64SuqaddS, new IntrinsicInfo(0x5e203800u, IntrinsicType.ScalarBinaryRd));
|
||||
Add(Intrinsic.Arm64SuqaddV, new IntrinsicInfo(0x0e203800u, IntrinsicType.VectorBinaryRd));
|
||||
Add(Intrinsic.Arm64TblV, new IntrinsicInfo(0x0e000000u, IntrinsicType.VectorLookupTable));
|
||||
Add(Intrinsic.Arm64TbxV, new IntrinsicInfo(0x0e001000u, IntrinsicType.VectorLookupTable));
|
||||
Add(Intrinsic.Arm64Trn1V, new IntrinsicInfo(0x0e002800u, IntrinsicType.VectorBinary));
|
||||
Add(Intrinsic.Arm64Trn2V, new IntrinsicInfo(0x0e006800u, IntrinsicType.VectorBinary));
|
||||
Add(Intrinsic.Arm64UabalV, new IntrinsicInfo(0x2e205000u, IntrinsicType.VectorTernaryRd));
|
||||
Add(Intrinsic.Arm64UabaV, new IntrinsicInfo(0x2e207c00u, IntrinsicType.VectorTernaryRd));
|
||||
Add(Intrinsic.Arm64UabdlV, new IntrinsicInfo(0x2e207000u, IntrinsicType.VectorBinary));
|
||||
Add(Intrinsic.Arm64UabdV, new IntrinsicInfo(0x2e207400u, IntrinsicType.VectorBinary));
|
||||
Add(Intrinsic.Arm64UadalpV, new IntrinsicInfo(0x2e206800u, IntrinsicType.VectorBinaryRd));
|
||||
Add(Intrinsic.Arm64UaddlpV, new IntrinsicInfo(0x2e202800u, IntrinsicType.VectorUnary));
|
||||
Add(Intrinsic.Arm64UaddlvV, new IntrinsicInfo(0x2e303800u, IntrinsicType.VectorUnary));
|
||||
Add(Intrinsic.Arm64UaddlV, new IntrinsicInfo(0x2e200000u, IntrinsicType.VectorBinary));
|
||||
Add(Intrinsic.Arm64UaddwV, new IntrinsicInfo(0x2e201000u, IntrinsicType.VectorBinary));
|
||||
Add(Intrinsic.Arm64UcvtfSFixed, new IntrinsicInfo(0x7f00e400u, IntrinsicType.ScalarFPConvFixed));
|
||||
Add(Intrinsic.Arm64UcvtfVFixed, new IntrinsicInfo(0x2f00e400u, IntrinsicType.VectorFPConvFixed));
|
||||
Add(Intrinsic.Arm64UcvtfS, new IntrinsicInfo(0x7e21d800u, IntrinsicType.ScalarUnary));
|
||||
Add(Intrinsic.Arm64UcvtfV, new IntrinsicInfo(0x2e21d800u, IntrinsicType.VectorUnary));
|
||||
Add(Intrinsic.Arm64UcvtfGpFixed, new IntrinsicInfo(0x1e030000u, IntrinsicType.ScalarFPConvFixedGpr));
|
||||
Add(Intrinsic.Arm64UcvtfGp, new IntrinsicInfo(0x1e230000u, IntrinsicType.ScalarFPConvGpr));
|
||||
Add(Intrinsic.Arm64UhaddV, new IntrinsicInfo(0x2e200400u, IntrinsicType.VectorBinary));
|
||||
Add(Intrinsic.Arm64UhsubV, new IntrinsicInfo(0x2e202400u, IntrinsicType.VectorBinary));
|
||||
Add(Intrinsic.Arm64UmaxpV, new IntrinsicInfo(0x2e20a400u, IntrinsicType.VectorBinary));
|
||||
Add(Intrinsic.Arm64UmaxvV, new IntrinsicInfo(0x2e30a800u, IntrinsicType.VectorUnary));
|
||||
Add(Intrinsic.Arm64UmaxV, new IntrinsicInfo(0x2e206400u, IntrinsicType.VectorBinary));
|
||||
Add(Intrinsic.Arm64UminpV, new IntrinsicInfo(0x2e20ac00u, IntrinsicType.VectorBinary));
|
||||
Add(Intrinsic.Arm64UminvV, new IntrinsicInfo(0x2e31a800u, IntrinsicType.VectorUnary));
|
||||
Add(Intrinsic.Arm64UminV, new IntrinsicInfo(0x2e206c00u, IntrinsicType.VectorBinary));
|
||||
Add(Intrinsic.Arm64UmlalVe, new IntrinsicInfo(0x2f002000u, IntrinsicType.VectorTernaryRdByElem));
|
||||
Add(Intrinsic.Arm64UmlalV, new IntrinsicInfo(0x2e208000u, IntrinsicType.VectorTernaryRd));
|
||||
Add(Intrinsic.Arm64UmlslVe, new IntrinsicInfo(0x2f006000u, IntrinsicType.VectorTernaryRdByElem));
|
||||
Add(Intrinsic.Arm64UmlslV, new IntrinsicInfo(0x2e20a000u, IntrinsicType.VectorTernaryRd));
|
||||
Add(Intrinsic.Arm64UmovV, new IntrinsicInfo(0x0e003c00u, IntrinsicType.VectorUnaryByElem));
|
||||
Add(Intrinsic.Arm64UmullVe, new IntrinsicInfo(0x2f00a000u, IntrinsicType.VectorBinaryByElem));
|
||||
Add(Intrinsic.Arm64UmullV, new IntrinsicInfo(0x2e20c000u, IntrinsicType.VectorBinary));
|
||||
Add(Intrinsic.Arm64UqaddS, new IntrinsicInfo(0x7e200c00u, IntrinsicType.ScalarBinary));
|
||||
Add(Intrinsic.Arm64UqaddV, new IntrinsicInfo(0x2e200c00u, IntrinsicType.VectorBinary));
|
||||
Add(Intrinsic.Arm64UqrshlS, new IntrinsicInfo(0x7e205c00u, IntrinsicType.ScalarBinary));
|
||||
Add(Intrinsic.Arm64UqrshlV, new IntrinsicInfo(0x2e205c00u, IntrinsicType.VectorBinary));
|
||||
Add(Intrinsic.Arm64UqrshrnS, new IntrinsicInfo(0x7f009c00u, IntrinsicType.ScalarTernaryShrRd));
|
||||
Add(Intrinsic.Arm64UqrshrnV, new IntrinsicInfo(0x2f009c00u, IntrinsicType.VectorTernaryShrRd));
|
||||
Add(Intrinsic.Arm64UqshlSi, new IntrinsicInfo(0x7f007400u, IntrinsicType.ScalarBinaryShl));
|
||||
Add(Intrinsic.Arm64UqshlVi, new IntrinsicInfo(0x2f007400u, IntrinsicType.VectorBinaryShl));
|
||||
Add(Intrinsic.Arm64UqshlS, new IntrinsicInfo(0x7e204c00u, IntrinsicType.ScalarBinary));
|
||||
Add(Intrinsic.Arm64UqshlV, new IntrinsicInfo(0x2e204c00u, IntrinsicType.VectorBinary));
|
||||
Add(Intrinsic.Arm64UqshrnS, new IntrinsicInfo(0x7f009400u, IntrinsicType.ScalarTernaryShrRd));
|
||||
Add(Intrinsic.Arm64UqshrnV, new IntrinsicInfo(0x2f009400u, IntrinsicType.VectorTernaryShrRd));
|
||||
Add(Intrinsic.Arm64UqsubS, new IntrinsicInfo(0x7e202c00u, IntrinsicType.ScalarBinary));
|
||||
Add(Intrinsic.Arm64UqsubV, new IntrinsicInfo(0x2e202c00u, IntrinsicType.VectorBinary));
|
||||
Add(Intrinsic.Arm64UqxtnS, new IntrinsicInfo(0x7e214800u, IntrinsicType.ScalarBinaryRd));
|
||||
Add(Intrinsic.Arm64UqxtnV, new IntrinsicInfo(0x2e214800u, IntrinsicType.VectorBinaryRd));
|
||||
Add(Intrinsic.Arm64UrecpeV, new IntrinsicInfo(0x0ea1c800u, IntrinsicType.VectorUnary));
|
||||
Add(Intrinsic.Arm64UrhaddV, new IntrinsicInfo(0x2e201400u, IntrinsicType.VectorBinary));
|
||||
Add(Intrinsic.Arm64UrshlS, new IntrinsicInfo(0x7e205400u, IntrinsicType.ScalarBinary));
|
||||
Add(Intrinsic.Arm64UrshlV, new IntrinsicInfo(0x2e205400u, IntrinsicType.VectorBinary));
|
||||
Add(Intrinsic.Arm64UrshrS, new IntrinsicInfo(0x7f002400u, IntrinsicType.ScalarBinaryShr));
|
||||
Add(Intrinsic.Arm64UrshrV, new IntrinsicInfo(0x2f002400u, IntrinsicType.VectorBinaryShr));
|
||||
Add(Intrinsic.Arm64UrsqrteV, new IntrinsicInfo(0x2ea1c800u, IntrinsicType.VectorUnary));
|
||||
Add(Intrinsic.Arm64UrsraS, new IntrinsicInfo(0x7f003400u, IntrinsicType.ScalarTernaryShrRd));
|
||||
Add(Intrinsic.Arm64UrsraV, new IntrinsicInfo(0x2f003400u, IntrinsicType.VectorTernaryShrRd));
|
||||
Add(Intrinsic.Arm64UshllV, new IntrinsicInfo(0x2f00a400u, IntrinsicType.VectorBinaryShl));
|
||||
Add(Intrinsic.Arm64UshlS, new IntrinsicInfo(0x7e204400u, IntrinsicType.ScalarBinary));
|
||||
Add(Intrinsic.Arm64UshlV, new IntrinsicInfo(0x2e204400u, IntrinsicType.VectorBinary));
|
||||
Add(Intrinsic.Arm64UshrS, new IntrinsicInfo(0x7f000400u, IntrinsicType.ScalarBinaryShr));
|
||||
Add(Intrinsic.Arm64UshrV, new IntrinsicInfo(0x2f000400u, IntrinsicType.VectorBinaryShr));
|
||||
Add(Intrinsic.Arm64UsqaddS, new IntrinsicInfo(0x7e203800u, IntrinsicType.ScalarBinaryRd));
|
||||
Add(Intrinsic.Arm64UsqaddV, new IntrinsicInfo(0x2e203800u, IntrinsicType.VectorBinaryRd));
|
||||
Add(Intrinsic.Arm64UsraS, new IntrinsicInfo(0x7f001400u, IntrinsicType.ScalarTernaryShrRd));
|
||||
Add(Intrinsic.Arm64UsraV, new IntrinsicInfo(0x2f001400u, IntrinsicType.VectorTernaryShrRd));
|
||||
Add(Intrinsic.Arm64UsublV, new IntrinsicInfo(0x2e202000u, IntrinsicType.VectorBinary));
|
||||
Add(Intrinsic.Arm64UsubwV, new IntrinsicInfo(0x2e203000u, IntrinsicType.VectorBinary));
|
||||
Add(Intrinsic.Arm64Uzp1V, new IntrinsicInfo(0x0e001800u, IntrinsicType.VectorBinary));
|
||||
Add(Intrinsic.Arm64Uzp2V, new IntrinsicInfo(0x0e005800u, IntrinsicType.VectorBinary));
|
||||
Add(Intrinsic.Arm64XtnV, new IntrinsicInfo(0x0e212800u, IntrinsicType.VectorUnary));
|
||||
Add(Intrinsic.Arm64Zip1V, new IntrinsicInfo(0x0e003800u, IntrinsicType.VectorBinary));
|
||||
Add(Intrinsic.Arm64Zip2V, new IntrinsicInfo(0x0e007800u, IntrinsicType.VectorBinary));
|
||||
}
|
||||
|
||||
private static void Add(Intrinsic intrin, IntrinsicInfo info)
|
||||
{
|
||||
_intrinTable[(int)intrin] = info;
|
||||
}
|
||||
|
||||
public static IntrinsicInfo GetInfo(Intrinsic intrin)
|
||||
{
|
||||
return _intrinTable[(int)intrin];
|
||||
}
|
||||
}
|
||||
}
|
@ -1,59 +0,0 @@
|
||||
namespace ARMeilleure.CodeGen.Arm64
|
||||
{
|
||||
enum IntrinsicType
|
||||
{
|
||||
ScalarUnary,
|
||||
ScalarUnaryByElem,
|
||||
ScalarBinary,
|
||||
ScalarBinaryByElem,
|
||||
ScalarBinaryFPByElem,
|
||||
ScalarBinaryRd,
|
||||
ScalarBinaryShl,
|
||||
ScalarBinaryShr,
|
||||
ScalarFcsel,
|
||||
ScalarFmovi,
|
||||
ScalarFPCompare,
|
||||
ScalarFPCompareCond,
|
||||
ScalarFPConv,
|
||||
ScalarFPConvFixed,
|
||||
ScalarFPConvFixedGpr,
|
||||
ScalarFPConvGpr,
|
||||
ScalarTernary,
|
||||
ScalarTernaryFPRdByElem,
|
||||
ScalarTernaryShlRd,
|
||||
ScalarTernaryShrRd,
|
||||
|
||||
VectorUnary,
|
||||
VectorUnaryBitwise,
|
||||
VectorUnaryByElem,
|
||||
VectorBinary,
|
||||
VectorBinaryBitwise,
|
||||
VectorBinaryBitwiseImm,
|
||||
VectorBinaryByElem,
|
||||
VectorBinaryFPByElem,
|
||||
VectorBinaryRd,
|
||||
VectorBinaryShl,
|
||||
VectorBinaryShr,
|
||||
VectorExt,
|
||||
VectorFmovi,
|
||||
VectorFPConvFixed,
|
||||
VectorInsertByElem,
|
||||
VectorLdSt,
|
||||
VectorLdStSs,
|
||||
VectorLookupTable,
|
||||
VectorMovi,
|
||||
VectorMvni,
|
||||
VectorTernaryFPRdByElem,
|
||||
VectorTernaryRd,
|
||||
VectorTernaryRdBitwise,
|
||||
VectorTernaryRdByElem,
|
||||
VectorTernaryShlRd,
|
||||
VectorTernaryShrRd,
|
||||
|
||||
Vector128Unary,
|
||||
Vector128Binary,
|
||||
|
||||
GetRegister,
|
||||
SetRegister
|
||||
}
|
||||
}
|
@ -1,940 +0,0 @@
|
||||
using ARMeilleure.CodeGen.RegisterAllocators;
|
||||
using ARMeilleure.IntermediateRepresentation;
|
||||
using ARMeilleure.Translation;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
|
||||
using static ARMeilleure.IntermediateRepresentation.Operation.Factory;
|
||||
|
||||
namespace ARMeilleure.CodeGen.Arm64
|
||||
{
|
||||
class PreAllocator
|
||||
{
|
||||
private class ConstantDict
|
||||
{
|
||||
private readonly Dictionary<(ulong, OperandType), Operand> _constants;
|
||||
|
||||
public ConstantDict()
|
||||
{
|
||||
_constants = new Dictionary<(ulong, OperandType), Operand>();
|
||||
}
|
||||
|
||||
public void Add(ulong value, OperandType type, Operand local)
|
||||
{
|
||||
_constants.Add((value, type), local);
|
||||
}
|
||||
|
||||
public bool TryGetValue(ulong value, OperandType type, out Operand local)
|
||||
{
|
||||
return _constants.TryGetValue((value, type), out local);
|
||||
}
|
||||
}
|
||||
|
||||
public static void RunPass(CompilerContext cctx, StackAllocator stackAlloc, out int maxCallArgs)
|
||||
{
|
||||
maxCallArgs = -1;
|
||||
|
||||
Span<Operation> buffer = default;
|
||||
|
||||
Operand[] preservedArgs = new Operand[CallingConvention.GetArgumentsOnRegsCount()];
|
||||
|
||||
for (BasicBlock block = cctx.Cfg.Blocks.First; block != null; block = block.ListNext)
|
||||
{
|
||||
ConstantDict constants = new ConstantDict();
|
||||
|
||||
Operation nextNode;
|
||||
|
||||
for (Operation node = block.Operations.First; node != default; node = nextNode)
|
||||
{
|
||||
nextNode = node.ListNext;
|
||||
|
||||
if (node.Instruction == Instruction.Phi)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
HandleConstantRegCopy(constants, block.Operations, node);
|
||||
HandleDestructiveRegCopy(block.Operations, node);
|
||||
|
||||
switch (node.Instruction)
|
||||
{
|
||||
case Instruction.Call:
|
||||
// Get the maximum number of arguments used on a call.
|
||||
// On windows, when a struct is returned from the call,
|
||||
// we also need to pass the pointer where the struct
|
||||
// should be written on the first argument.
|
||||
int argsCount = node.SourcesCount - 1;
|
||||
|
||||
if (node.Destination != default && node.Destination.Type == OperandType.V128)
|
||||
{
|
||||
argsCount++;
|
||||
}
|
||||
|
||||
if (maxCallArgs < argsCount)
|
||||
{
|
||||
maxCallArgs = argsCount;
|
||||
}
|
||||
|
||||
// Copy values to registers expected by the function
|
||||
// being called, as mandated by the ABI.
|
||||
HandleCall(constants, block.Operations, node);
|
||||
break;
|
||||
case Instruction.CompareAndSwap:
|
||||
case Instruction.CompareAndSwap16:
|
||||
case Instruction.CompareAndSwap8:
|
||||
nextNode = HandleCompareAndSwap(block.Operations, node);
|
||||
break;
|
||||
case Instruction.LoadArgument:
|
||||
nextNode = HandleLoadArgument(cctx, ref buffer, block.Operations, preservedArgs, node);
|
||||
break;
|
||||
case Instruction.Return:
|
||||
HandleReturn(block.Operations, node);
|
||||
break;
|
||||
case Instruction.Tailcall:
|
||||
HandleTailcall(constants, block.Operations, stackAlloc, node, node);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void HandleConstantRegCopy(ConstantDict constants, IntrusiveList<Operation> nodes, Operation node)
|
||||
{
|
||||
if (node.SourcesCount == 0 || IsIntrinsicWithConst(node))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Instruction inst = node.Instruction;
|
||||
|
||||
Operand src1 = node.GetSource(0);
|
||||
Operand src2;
|
||||
|
||||
if (src1.Kind == OperandKind.Constant)
|
||||
{
|
||||
if (!src1.Type.IsInteger())
|
||||
{
|
||||
// Handle non-integer types (FP32, FP64 and V128).
|
||||
// For instructions without an immediate operand, we do the following:
|
||||
// - Insert a copy with the constant value (as integer) to a GPR.
|
||||
// - Insert a copy from the GPR to a XMM register.
|
||||
// - Replace the constant use with the XMM register.
|
||||
src1 = AddFloatConstantCopy(constants, nodes, node, src1);
|
||||
|
||||
node.SetSource(0, src1);
|
||||
}
|
||||
else if (!HasConstSrc1(node, src1.Value))
|
||||
{
|
||||
// Handle integer types.
|
||||
// Most ALU instructions accepts a 32-bits immediate on the second operand.
|
||||
// We need to ensure the following:
|
||||
// - If the constant is on operand 1, we need to move it.
|
||||
// -- But first, we try to swap operand 1 and 2 if the instruction is commutative.
|
||||
// -- Doing so may allow us to encode the constant as operand 2 and avoid a copy.
|
||||
// - If the constant is on operand 2, we check if the instruction supports it,
|
||||
// if not, we also add a copy. 64-bits constants are usually not supported.
|
||||
if (IsCommutative(node))
|
||||
{
|
||||
src2 = node.GetSource(1);
|
||||
|
||||
Operand temp = src1;
|
||||
|
||||
src1 = src2;
|
||||
src2 = temp;
|
||||
|
||||
node.SetSource(0, src1);
|
||||
node.SetSource(1, src2);
|
||||
}
|
||||
|
||||
if (src1.Kind == OperandKind.Constant)
|
||||
{
|
||||
src1 = AddIntConstantCopy(constants, nodes, node, src1);
|
||||
|
||||
node.SetSource(0, src1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (node.SourcesCount < 2)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
src2 = node.GetSource(1);
|
||||
|
||||
if (src2.Kind == OperandKind.Constant)
|
||||
{
|
||||
if (!src2.Type.IsInteger())
|
||||
{
|
||||
src2 = AddFloatConstantCopy(constants, nodes, node, src2);
|
||||
|
||||
node.SetSource(1, src2);
|
||||
}
|
||||
else if (!HasConstSrc2(inst, src2))
|
||||
{
|
||||
src2 = AddIntConstantCopy(constants, nodes, node, src2);
|
||||
|
||||
node.SetSource(1, src2);
|
||||
}
|
||||
}
|
||||
|
||||
if (node.SourcesCount < 3 ||
|
||||
node.Instruction == Instruction.BranchIf ||
|
||||
node.Instruction == Instruction.Compare ||
|
||||
node.Instruction == Instruction.VectorInsert ||
|
||||
node.Instruction == Instruction.VectorInsert16 ||
|
||||
node.Instruction == Instruction.VectorInsert8)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (int srcIndex = 2; srcIndex < node.SourcesCount; srcIndex++)
|
||||
{
|
||||
Operand src = node.GetSource(srcIndex);
|
||||
|
||||
if (src.Kind == OperandKind.Constant)
|
||||
{
|
||||
if (!src.Type.IsInteger())
|
||||
{
|
||||
src = AddFloatConstantCopy(constants, nodes, node, src);
|
||||
|
||||
node.SetSource(srcIndex, src);
|
||||
}
|
||||
else
|
||||
{
|
||||
src = AddIntConstantCopy(constants, nodes, node, src);
|
||||
|
||||
node.SetSource(srcIndex, src);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void HandleDestructiveRegCopy(IntrusiveList<Operation> nodes, Operation node)
|
||||
{
|
||||
if (node.Destination == default || node.SourcesCount == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Operand dest = node.Destination;
|
||||
Operand src1 = node.GetSource(0);
|
||||
|
||||
if (IsSameOperandDestSrc1(node) && src1.Kind == OperandKind.LocalVariable)
|
||||
{
|
||||
bool useNewLocal = false;
|
||||
|
||||
for (int srcIndex = 1; srcIndex < node.SourcesCount; srcIndex++)
|
||||
{
|
||||
if (node.GetSource(srcIndex) == dest)
|
||||
{
|
||||
useNewLocal = true;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (useNewLocal)
|
||||
{
|
||||
// Dest is being used as some source already, we need to use a new
|
||||
// local to store the temporary value, otherwise the value on dest
|
||||
// local would be overwritten.
|
||||
Operand temp = Local(dest.Type);
|
||||
|
||||
nodes.AddBefore(node, Operation(Instruction.Copy, temp, src1));
|
||||
|
||||
node.SetSource(0, temp);
|
||||
|
||||
nodes.AddAfter(node, Operation(Instruction.Copy, dest, temp));
|
||||
|
||||
node.Destination = temp;
|
||||
}
|
||||
else
|
||||
{
|
||||
nodes.AddBefore(node, Operation(Instruction.Copy, dest, src1));
|
||||
|
||||
node.SetSource(0, dest);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void HandleCall(ConstantDict constants, IntrusiveList<Operation> nodes, Operation node)
|
||||
{
|
||||
Operation operation = node;
|
||||
|
||||
Operand dest = operation.Destination;
|
||||
|
||||
List<Operand> sources = new List<Operand>
|
||||
{
|
||||
operation.GetSource(0)
|
||||
};
|
||||
|
||||
int argsCount = operation.SourcesCount - 1;
|
||||
|
||||
int intMax = CallingConvention.GetArgumentsOnRegsCount();
|
||||
int vecMax = CallingConvention.GetArgumentsOnRegsCount();
|
||||
|
||||
int intCount = 0;
|
||||
int vecCount = 0;
|
||||
|
||||
int stackOffset = 0;
|
||||
|
||||
for (int index = 0; index < argsCount; index++)
|
||||
{
|
||||
Operand source = operation.GetSource(index + 1);
|
||||
|
||||
bool passOnReg;
|
||||
|
||||
if (source.Type.IsInteger())
|
||||
{
|
||||
passOnReg = intCount < intMax;
|
||||
}
|
||||
else if (source.Type == OperandType.V128)
|
||||
{
|
||||
passOnReg = intCount + 1 < intMax;
|
||||
}
|
||||
else
|
||||
{
|
||||
passOnReg = vecCount < vecMax;
|
||||
}
|
||||
|
||||
if (source.Type == OperandType.V128 && passOnReg)
|
||||
{
|
||||
// V128 is a struct, we pass each half on a GPR if possible.
|
||||
Operand argReg = Gpr(CallingConvention.GetIntArgumentRegister(intCount++), OperandType.I64);
|
||||
Operand argReg2 = Gpr(CallingConvention.GetIntArgumentRegister(intCount++), OperandType.I64);
|
||||
|
||||
nodes.AddBefore(node, Operation(Instruction.VectorExtract, argReg, source, Const(0)));
|
||||
nodes.AddBefore(node, Operation(Instruction.VectorExtract, argReg2, source, Const(1)));
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (passOnReg)
|
||||
{
|
||||
Operand argReg = source.Type.IsInteger()
|
||||
? Gpr(CallingConvention.GetIntArgumentRegister(intCount++), source.Type)
|
||||
: Xmm(CallingConvention.GetVecArgumentRegister(vecCount++), source.Type);
|
||||
|
||||
Operation copyOp = Operation(Instruction.Copy, argReg, source);
|
||||
|
||||
HandleConstantRegCopy(constants, nodes, nodes.AddBefore(node, copyOp));
|
||||
|
||||
sources.Add(argReg);
|
||||
}
|
||||
else
|
||||
{
|
||||
Operand offset = Const(stackOffset);
|
||||
|
||||
Operation spillOp = Operation(Instruction.SpillArg, default, offset, source);
|
||||
|
||||
HandleConstantRegCopy(constants, nodes, nodes.AddBefore(node, spillOp));
|
||||
|
||||
stackOffset += source.Type.GetSizeInBytes();
|
||||
}
|
||||
}
|
||||
|
||||
if (dest != default)
|
||||
{
|
||||
if (dest.Type == OperandType.V128)
|
||||
{
|
||||
Operand retLReg = Gpr(CallingConvention.GetIntReturnRegister(), OperandType.I64);
|
||||
Operand retHReg = Gpr(CallingConvention.GetIntReturnRegisterHigh(), OperandType.I64);
|
||||
|
||||
node = nodes.AddAfter(node, Operation(Instruction.VectorCreateScalar, dest, retLReg));
|
||||
nodes.AddAfter(node, Operation(Instruction.VectorInsert, dest, dest, retHReg, Const(1)));
|
||||
|
||||
operation.Destination = default;
|
||||
}
|
||||
else
|
||||
{
|
||||
Operand retReg = dest.Type.IsInteger()
|
||||
? Gpr(CallingConvention.GetIntReturnRegister(), dest.Type)
|
||||
: Xmm(CallingConvention.GetVecReturnRegister(), dest.Type);
|
||||
|
||||
Operation copyOp = Operation(Instruction.Copy, dest, retReg);
|
||||
|
||||
nodes.AddAfter(node, copyOp);
|
||||
|
||||
operation.Destination = retReg;
|
||||
}
|
||||
}
|
||||
|
||||
operation.SetSources(sources.ToArray());
|
||||
}
|
||||
|
||||
private static void HandleTailcall(
|
||||
ConstantDict constants,
|
||||
IntrusiveList<Operation> nodes,
|
||||
StackAllocator stackAlloc,
|
||||
Operation node,
|
||||
Operation operation)
|
||||
{
|
||||
List<Operand> sources = new List<Operand>
|
||||
{
|
||||
operation.GetSource(0)
|
||||
};
|
||||
|
||||
int argsCount = operation.SourcesCount - 1;
|
||||
|
||||
int intMax = CallingConvention.GetArgumentsOnRegsCount();
|
||||
int vecMax = CallingConvention.GetArgumentsOnRegsCount();
|
||||
|
||||
int intCount = 0;
|
||||
int vecCount = 0;
|
||||
|
||||
// Handle arguments passed on registers.
|
||||
for (int index = 0; index < argsCount; index++)
|
||||
{
|
||||
Operand source = operation.GetSource(1 + index);
|
||||
|
||||
bool passOnReg;
|
||||
|
||||
if (source.Type.IsInteger())
|
||||
{
|
||||
passOnReg = intCount + 1 < intMax;
|
||||
}
|
||||
else
|
||||
{
|
||||
passOnReg = vecCount < vecMax;
|
||||
}
|
||||
|
||||
if (source.Type == OperandType.V128 && passOnReg)
|
||||
{
|
||||
// V128 is a struct, we pass each half on a GPR if possible.
|
||||
Operand argReg = Gpr(CallingConvention.GetIntArgumentRegister(intCount++), OperandType.I64);
|
||||
Operand argReg2 = Gpr(CallingConvention.GetIntArgumentRegister(intCount++), OperandType.I64);
|
||||
|
||||
nodes.AddBefore(node, Operation(Instruction.VectorExtract, argReg, source, Const(0)));
|
||||
nodes.AddBefore(node, Operation(Instruction.VectorExtract, argReg2, source, Const(1)));
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (passOnReg)
|
||||
{
|
||||
Operand argReg = source.Type.IsInteger()
|
||||
? Gpr(CallingConvention.GetIntArgumentRegister(intCount++), source.Type)
|
||||
: Xmm(CallingConvention.GetVecArgumentRegister(vecCount++), source.Type);
|
||||
|
||||
Operation copyOp = Operation(Instruction.Copy, argReg, source);
|
||||
|
||||
HandleConstantRegCopy(constants, nodes, nodes.AddBefore(node, copyOp));
|
||||
|
||||
sources.Add(argReg);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new NotImplementedException("Spilling is not currently supported for tail calls. (too many arguments)");
|
||||
}
|
||||
}
|
||||
|
||||
// The target address must be on the return registers, since we
|
||||
// don't return anything and it is guaranteed to not be a
|
||||
// callee saved register (which would be trashed on the epilogue).
|
||||
Operand tcAddress = Gpr(CodeGenCommon.TcAddressRegister, OperandType.I64);
|
||||
|
||||
Operation addrCopyOp = Operation(Instruction.Copy, tcAddress, operation.GetSource(0));
|
||||
|
||||
nodes.AddBefore(node, addrCopyOp);
|
||||
|
||||
sources[0] = tcAddress;
|
||||
|
||||
operation.SetSources(sources.ToArray());
|
||||
}
|
||||
|
||||
private static Operation HandleCompareAndSwap(IntrusiveList<Operation> nodes, Operation node)
|
||||
{
|
||||
Operand expected = node.GetSource(1);
|
||||
|
||||
if (expected.Type == OperandType.V128)
|
||||
{
|
||||
Operand dest = node.Destination;
|
||||
Operand expectedLow = Local(OperandType.I64);
|
||||
Operand expectedHigh = Local(OperandType.I64);
|
||||
Operand desiredLow = Local(OperandType.I64);
|
||||
Operand desiredHigh = Local(OperandType.I64);
|
||||
Operand actualLow = Local(OperandType.I64);
|
||||
Operand actualHigh = Local(OperandType.I64);
|
||||
|
||||
Operand address = node.GetSource(0);
|
||||
Operand desired = node.GetSource(2);
|
||||
|
||||
void SplitOperand(Operand source, Operand low, Operand high)
|
||||
{
|
||||
nodes.AddBefore(node, Operation(Instruction.VectorExtract, low, source, Const(0)));
|
||||
nodes.AddBefore(node, Operation(Instruction.VectorExtract, high, source, Const(1)));
|
||||
}
|
||||
|
||||
SplitOperand(expected, expectedLow, expectedHigh);
|
||||
SplitOperand(desired, desiredLow, desiredHigh);
|
||||
|
||||
Operation operation = node;
|
||||
|
||||
// Update the sources and destinations with split 64-bit halfs of the whole 128-bit values.
|
||||
// We also need a additional registers that will be used to store temporary information.
|
||||
operation.SetDestinations(new[] { actualLow, actualHigh, Local(OperandType.I64), Local(OperandType.I64) });
|
||||
operation.SetSources(new[] { address, expectedLow, expectedHigh, desiredLow, desiredHigh });
|
||||
|
||||
// Add some dummy uses of the input operands, as the CAS operation will be a loop,
|
||||
// so they can't be used as destination operand.
|
||||
for (int i = 0; i < operation.SourcesCount; i++)
|
||||
{
|
||||
Operand src = operation.GetSource(i);
|
||||
node = nodes.AddAfter(node, Operation(Instruction.Copy, src, src));
|
||||
}
|
||||
|
||||
// Assemble the vector with the 64-bit values at the given memory location.
|
||||
node = nodes.AddAfter(node, Operation(Instruction.VectorCreateScalar, dest, actualLow));
|
||||
node = nodes.AddAfter(node, Operation(Instruction.VectorInsert, dest, dest, actualHigh, Const(1)));
|
||||
}
|
||||
else
|
||||
{
|
||||
// We need a additional register where the store result will be written to.
|
||||
node.SetDestinations(new[] { node.Destination, Local(OperandType.I32) });
|
||||
|
||||
// Add some dummy uses of the input operands, as the CAS operation will be a loop,
|
||||
// so they can't be used as destination operand.
|
||||
Operation operation = node;
|
||||
|
||||
for (int i = 0; i < operation.SourcesCount; i++)
|
||||
{
|
||||
Operand src = operation.GetSource(i);
|
||||
node = nodes.AddAfter(node, Operation(Instruction.Copy, src, src));
|
||||
}
|
||||
}
|
||||
|
||||
return node.ListNext;
|
||||
}
|
||||
|
||||
private static void HandleReturn(IntrusiveList<Operation> nodes, Operation node)
|
||||
{
|
||||
if (node.SourcesCount == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Operand source = node.GetSource(0);
|
||||
|
||||
if (source.Type == OperandType.V128)
|
||||
{
|
||||
Operand retLReg = Gpr(CallingConvention.GetIntReturnRegister(), OperandType.I64);
|
||||
Operand retHReg = Gpr(CallingConvention.GetIntReturnRegisterHigh(), OperandType.I64);
|
||||
|
||||
nodes.AddBefore(node, Operation(Instruction.VectorExtract, retLReg, source, Const(0)));
|
||||
nodes.AddBefore(node, Operation(Instruction.VectorExtract, retHReg, source, Const(1)));
|
||||
}
|
||||
else
|
||||
{
|
||||
Operand retReg = source.Type.IsInteger()
|
||||
? Gpr(CallingConvention.GetIntReturnRegister(), source.Type)
|
||||
: Xmm(CallingConvention.GetVecReturnRegister(), source.Type);
|
||||
|
||||
Operation retCopyOp = Operation(Instruction.Copy, retReg, source);
|
||||
|
||||
nodes.AddBefore(node, retCopyOp);
|
||||
}
|
||||
}
|
||||
|
||||
private static Operation HandleLoadArgument(
|
||||
CompilerContext cctx,
|
||||
ref Span<Operation> buffer,
|
||||
IntrusiveList<Operation> nodes,
|
||||
Operand[] preservedArgs,
|
||||
Operation node)
|
||||
{
|
||||
Operand source = node.GetSource(0);
|
||||
|
||||
Debug.Assert(source.Kind == OperandKind.Constant, "Non-constant LoadArgument source kind.");
|
||||
|
||||
int index = source.AsInt32();
|
||||
|
||||
int intCount = 0;
|
||||
int vecCount = 0;
|
||||
|
||||
for (int cIndex = 0; cIndex < index; cIndex++)
|
||||
{
|
||||
OperandType argType = cctx.FuncArgTypes[cIndex];
|
||||
|
||||
if (argType.IsInteger())
|
||||
{
|
||||
intCount++;
|
||||
}
|
||||
else if (argType == OperandType.V128)
|
||||
{
|
||||
intCount += 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
vecCount++;
|
||||
}
|
||||
}
|
||||
|
||||
bool passOnReg;
|
||||
|
||||
if (source.Type.IsInteger())
|
||||
{
|
||||
passOnReg = intCount < CallingConvention.GetArgumentsOnRegsCount();
|
||||
}
|
||||
else if (source.Type == OperandType.V128)
|
||||
{
|
||||
passOnReg = intCount + 1 < CallingConvention.GetArgumentsOnRegsCount();
|
||||
}
|
||||
else
|
||||
{
|
||||
passOnReg = vecCount < CallingConvention.GetArgumentsOnRegsCount();
|
||||
}
|
||||
|
||||
if (passOnReg)
|
||||
{
|
||||
Operand dest = node.Destination;
|
||||
|
||||
if (preservedArgs[index] == default)
|
||||
{
|
||||
if (dest.Type == OperandType.V128)
|
||||
{
|
||||
// V128 is a struct, we pass each half on a GPR if possible.
|
||||
Operand pArg = Local(OperandType.V128);
|
||||
|
||||
Operand argLReg = Gpr(CallingConvention.GetIntArgumentRegister(intCount), OperandType.I64);
|
||||
Operand argHReg = Gpr(CallingConvention.GetIntArgumentRegister(intCount + 1), OperandType.I64);
|
||||
|
||||
Operation copyL = Operation(Instruction.VectorCreateScalar, pArg, argLReg);
|
||||
Operation copyH = Operation(Instruction.VectorInsert, pArg, pArg, argHReg, Const(1));
|
||||
|
||||
cctx.Cfg.Entry.Operations.AddFirst(copyH);
|
||||
cctx.Cfg.Entry.Operations.AddFirst(copyL);
|
||||
|
||||
preservedArgs[index] = pArg;
|
||||
}
|
||||
else
|
||||
{
|
||||
Operand pArg = Local(dest.Type);
|
||||
|
||||
Operand argReg = dest.Type.IsInteger()
|
||||
? Gpr(CallingConvention.GetIntArgumentRegister(intCount), dest.Type)
|
||||
: Xmm(CallingConvention.GetVecArgumentRegister(vecCount), dest.Type);
|
||||
|
||||
Operation copyOp = Operation(Instruction.Copy, pArg, argReg);
|
||||
|
||||
cctx.Cfg.Entry.Operations.AddFirst(copyOp);
|
||||
|
||||
preservedArgs[index] = pArg;
|
||||
}
|
||||
}
|
||||
|
||||
Operation nextNode;
|
||||
|
||||
if (dest.AssignmentsCount == 1)
|
||||
{
|
||||
// Let's propagate the argument if we can to avoid copies.
|
||||
Propagate(ref buffer, dest, preservedArgs[index]);
|
||||
nextNode = node.ListNext;
|
||||
}
|
||||
else
|
||||
{
|
||||
Operation argCopyOp = Operation(Instruction.Copy, dest, preservedArgs[index]);
|
||||
nextNode = nodes.AddBefore(node, argCopyOp);
|
||||
}
|
||||
|
||||
Delete(nodes, node);
|
||||
return nextNode;
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: Pass on stack.
|
||||
return node;
|
||||
}
|
||||
}
|
||||
|
||||
private static void Propagate(ref Span<Operation> buffer, Operand dest, Operand value)
|
||||
{
|
||||
ReadOnlySpan<Operation> uses = dest.GetUses(ref buffer);
|
||||
|
||||
foreach (Operation use in uses)
|
||||
{
|
||||
for (int srcIndex = 0; srcIndex < use.SourcesCount; srcIndex++)
|
||||
{
|
||||
Operand useSrc = use.GetSource(srcIndex);
|
||||
|
||||
if (useSrc == dest)
|
||||
{
|
||||
use.SetSource(srcIndex, value);
|
||||
}
|
||||
else if (useSrc.Kind == OperandKind.Memory)
|
||||
{
|
||||
MemoryOperand memoryOp = useSrc.GetMemory();
|
||||
|
||||
Operand baseAddr = memoryOp.BaseAddress;
|
||||
Operand index = memoryOp.Index;
|
||||
bool changed = false;
|
||||
|
||||
if (baseAddr == dest)
|
||||
{
|
||||
baseAddr = value;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (index == dest)
|
||||
{
|
||||
index = value;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (changed)
|
||||
{
|
||||
use.SetSource(srcIndex, MemoryOp(
|
||||
useSrc.Type,
|
||||
baseAddr,
|
||||
index,
|
||||
memoryOp.Scale,
|
||||
memoryOp.Displacement));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static Operand AddFloatConstantCopy(
|
||||
ConstantDict constants,
|
||||
IntrusiveList<Operation> nodes,
|
||||
Operation node,
|
||||
Operand source)
|
||||
{
|
||||
Operand temp = Local(source.Type);
|
||||
|
||||
Operand intConst = AddIntConstantCopy(constants, nodes, node, GetIntConst(source));
|
||||
|
||||
Operation copyOp = Operation(Instruction.VectorCreateScalar, temp, intConst);
|
||||
|
||||
nodes.AddBefore(node, copyOp);
|
||||
|
||||
return temp;
|
||||
}
|
||||
|
||||
private static Operand AddIntConstantCopy(
|
||||
ConstantDict constants,
|
||||
IntrusiveList<Operation> nodes,
|
||||
Operation node,
|
||||
Operand source)
|
||||
{
|
||||
if (constants.TryGetValue(source.Value, source.Type, out Operand temp))
|
||||
{
|
||||
return temp;
|
||||
}
|
||||
|
||||
temp = Local(source.Type);
|
||||
|
||||
Operation copyOp = Operation(Instruction.Copy, temp, source);
|
||||
|
||||
nodes.AddBefore(node, copyOp);
|
||||
|
||||
constants.Add(source.Value, source.Type, temp);
|
||||
|
||||
return temp;
|
||||
}
|
||||
|
||||
private static Operand GetIntConst(Operand value)
|
||||
{
|
||||
if (value.Type == OperandType.FP32)
|
||||
{
|
||||
return Const(value.AsInt32());
|
||||
}
|
||||
else if (value.Type == OperandType.FP64)
|
||||
{
|
||||
return Const(value.AsInt64());
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
private static void Delete(IntrusiveList<Operation> nodes, Operation node)
|
||||
{
|
||||
node.Destination = default;
|
||||
|
||||
for (int index = 0; index < node.SourcesCount; index++)
|
||||
{
|
||||
node.SetSource(index, default);
|
||||
}
|
||||
|
||||
nodes.Remove(node);
|
||||
}
|
||||
|
||||
private static Operand Gpr(int register, OperandType type)
|
||||
{
|
||||
return Register(register, RegisterType.Integer, type);
|
||||
}
|
||||
|
||||
private static Operand Xmm(int register, OperandType type)
|
||||
{
|
||||
return Register(register, RegisterType.Vector, type);
|
||||
}
|
||||
|
||||
private static bool IsSameOperandDestSrc1(Operation operation)
|
||||
{
|
||||
switch (operation.Instruction)
|
||||
{
|
||||
case Instruction.Extended:
|
||||
return IsSameOperandDestSrc1(operation.Intrinsic);
|
||||
case Instruction.VectorInsert:
|
||||
case Instruction.VectorInsert16:
|
||||
case Instruction.VectorInsert8:
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool IsSameOperandDestSrc1(Intrinsic intrinsic)
|
||||
{
|
||||
IntrinsicInfo info = IntrinsicTable.GetInfo(intrinsic & ~(Intrinsic.Arm64VTypeMask | Intrinsic.Arm64VSizeMask));
|
||||
|
||||
return info.Type == IntrinsicType.ScalarBinaryRd ||
|
||||
info.Type == IntrinsicType.ScalarTernaryFPRdByElem ||
|
||||
info.Type == IntrinsicType.ScalarTernaryShlRd ||
|
||||
info.Type == IntrinsicType.ScalarTernaryShrRd ||
|
||||
info.Type == IntrinsicType.VectorBinaryRd ||
|
||||
info.Type == IntrinsicType.VectorInsertByElem ||
|
||||
info.Type == IntrinsicType.VectorTernaryRd ||
|
||||
info.Type == IntrinsicType.VectorTernaryRdBitwise ||
|
||||
info.Type == IntrinsicType.VectorTernaryFPRdByElem ||
|
||||
info.Type == IntrinsicType.VectorTernaryRdByElem ||
|
||||
info.Type == IntrinsicType.VectorTernaryShlRd ||
|
||||
info.Type == IntrinsicType.VectorTernaryShrRd;
|
||||
}
|
||||
|
||||
private static bool HasConstSrc1(Operation node, ulong value)
|
||||
{
|
||||
switch (node.Instruction)
|
||||
{
|
||||
case Instruction.Add:
|
||||
case Instruction.BranchIf:
|
||||
case Instruction.Compare:
|
||||
case Instruction.Subtract:
|
||||
// The immediate encoding of those instructions does not allow Rn to be
|
||||
// XZR (it will be SP instead), so we can't allow a Rn constant in this case.
|
||||
return value == 0 && NotConstOrConst0(node.GetSource(1));
|
||||
case Instruction.BitwiseAnd:
|
||||
case Instruction.BitwiseExclusiveOr:
|
||||
case Instruction.BitwiseNot:
|
||||
case Instruction.BitwiseOr:
|
||||
case Instruction.ByteSwap:
|
||||
case Instruction.CountLeadingZeros:
|
||||
case Instruction.Multiply:
|
||||
case Instruction.Negate:
|
||||
case Instruction.RotateRight:
|
||||
case Instruction.ShiftLeft:
|
||||
case Instruction.ShiftRightSI:
|
||||
case Instruction.ShiftRightUI:
|
||||
return value == 0;
|
||||
case Instruction.Copy:
|
||||
case Instruction.LoadArgument:
|
||||
case Instruction.Spill:
|
||||
case Instruction.SpillArg:
|
||||
return true;
|
||||
case Instruction.Extended:
|
||||
return value == 0;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool NotConstOrConst0(Operand operand)
|
||||
{
|
||||
return operand.Kind != OperandKind.Constant || operand.Value == 0;
|
||||
}
|
||||
|
||||
private static bool HasConstSrc2(Instruction inst, Operand operand)
|
||||
{
|
||||
ulong value = operand.Value;
|
||||
|
||||
switch (inst)
|
||||
{
|
||||
case Instruction.Add:
|
||||
case Instruction.BranchIf:
|
||||
case Instruction.Compare:
|
||||
case Instruction.Subtract:
|
||||
return ConstFitsOnUImm12Sh(value);
|
||||
case Instruction.BitwiseAnd:
|
||||
case Instruction.BitwiseExclusiveOr:
|
||||
case Instruction.BitwiseOr:
|
||||
return value == 0 || CodeGenCommon.TryEncodeBitMask(operand, out _, out _, out _);
|
||||
case Instruction.Multiply:
|
||||
case Instruction.Store:
|
||||
case Instruction.Store16:
|
||||
case Instruction.Store8:
|
||||
return value == 0;
|
||||
case Instruction.RotateRight:
|
||||
case Instruction.ShiftLeft:
|
||||
case Instruction.ShiftRightSI:
|
||||
case Instruction.ShiftRightUI:
|
||||
case Instruction.VectorExtract:
|
||||
case Instruction.VectorExtract16:
|
||||
case Instruction.VectorExtract8:
|
||||
return true;
|
||||
case Instruction.Extended:
|
||||
// TODO: Check if actual intrinsic is supposed to have consts here?
|
||||
// Right now we only hit this case for fixed-point int <-> FP conversion instructions.
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool IsCommutative(Operation operation)
|
||||
{
|
||||
switch (operation.Instruction)
|
||||
{
|
||||
case Instruction.Add:
|
||||
case Instruction.BitwiseAnd:
|
||||
case Instruction.BitwiseExclusiveOr:
|
||||
case Instruction.BitwiseOr:
|
||||
case Instruction.Multiply:
|
||||
return true;
|
||||
|
||||
case Instruction.BranchIf:
|
||||
case Instruction.Compare:
|
||||
{
|
||||
Operand comp = operation.GetSource(2);
|
||||
|
||||
Debug.Assert(comp.Kind == OperandKind.Constant);
|
||||
|
||||
var compType = (Comparison)comp.AsInt32();
|
||||
|
||||
return compType == Comparison.Equal || compType == Comparison.NotEqual;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool ConstFitsOnUImm12Sh(ulong value)
|
||||
{
|
||||
return (value & ~0xfffUL) == 0 || (value & ~0xfff000UL) == 0;
|
||||
}
|
||||
|
||||
private static bool IsIntrinsicWithConst(Operation operation)
|
||||
{
|
||||
bool isIntrinsic = IsIntrinsic(operation.Instruction);
|
||||
|
||||
if (isIntrinsic)
|
||||
{
|
||||
Intrinsic intrinsic = operation.Intrinsic;
|
||||
IntrinsicInfo info = IntrinsicTable.GetInfo(intrinsic & ~(Intrinsic.Arm64VTypeMask | Intrinsic.Arm64VSizeMask));
|
||||
|
||||
// Those have integer inputs that don't support consts.
|
||||
return info.Type != IntrinsicType.ScalarFPConvGpr &&
|
||||
info.Type != IntrinsicType.ScalarFPConvFixedGpr &&
|
||||
info.Type != IntrinsicType.SetRegister;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool IsIntrinsic(Instruction inst)
|
||||
{
|
||||
return inst == Instruction.Extended;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,346 +0,0 @@
|
||||
using ARMeilleure.IntermediateRepresentation;
|
||||
using System;
|
||||
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
|
||||
|
||||
namespace ARMeilleure.CodeGen.Optimizations
|
||||
{
|
||||
static class ConstantFolding
|
||||
{
|
||||
public static void RunPass(Operation operation)
|
||||
{
|
||||
if (operation.Destination == default || operation.SourcesCount == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!AreAllSourcesConstant(operation))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
OperandType type = operation.Destination.Type;
|
||||
|
||||
switch (operation.Instruction)
|
||||
{
|
||||
case Instruction.Add:
|
||||
if (operation.GetSource(0).Relocatable ||
|
||||
operation.GetSource(1).Relocatable)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (type == OperandType.I32)
|
||||
{
|
||||
EvaluateBinaryI32(operation, (x, y) => x + y);
|
||||
}
|
||||
else if (type == OperandType.I64)
|
||||
{
|
||||
EvaluateBinaryI64(operation, (x, y) => x + y);
|
||||
}
|
||||
break;
|
||||
|
||||
case Instruction.BitwiseAnd:
|
||||
if (type == OperandType.I32)
|
||||
{
|
||||
EvaluateBinaryI32(operation, (x, y) => x & y);
|
||||
}
|
||||
else if (type == OperandType.I64)
|
||||
{
|
||||
EvaluateBinaryI64(operation, (x, y) => x & y);
|
||||
}
|
||||
break;
|
||||
|
||||
case Instruction.BitwiseExclusiveOr:
|
||||
if (type == OperandType.I32)
|
||||
{
|
||||
EvaluateBinaryI32(operation, (x, y) => x ^ y);
|
||||
}
|
||||
else if (type == OperandType.I64)
|
||||
{
|
||||
EvaluateBinaryI64(operation, (x, y) => x ^ y);
|
||||
}
|
||||
break;
|
||||
|
||||
case Instruction.BitwiseNot:
|
||||
if (type == OperandType.I32)
|
||||
{
|
||||
EvaluateUnaryI32(operation, (x) => ~x);
|
||||
}
|
||||
else if (type == OperandType.I64)
|
||||
{
|
||||
EvaluateUnaryI64(operation, (x) => ~x);
|
||||
}
|
||||
break;
|
||||
|
||||
case Instruction.BitwiseOr:
|
||||
if (type == OperandType.I32)
|
||||
{
|
||||
EvaluateBinaryI32(operation, (x, y) => x | y);
|
||||
}
|
||||
else if (type == OperandType.I64)
|
||||
{
|
||||
EvaluateBinaryI64(operation, (x, y) => x | y);
|
||||
}
|
||||
break;
|
||||
|
||||
case Instruction.ConvertI64ToI32:
|
||||
if (type == OperandType.I32)
|
||||
{
|
||||
EvaluateUnaryI32(operation, (x) => x);
|
||||
}
|
||||
break;
|
||||
|
||||
case Instruction.Compare:
|
||||
if (type == OperandType.I32 &&
|
||||
operation.GetSource(0).Type == type &&
|
||||
operation.GetSource(1).Type == type)
|
||||
{
|
||||
switch ((Comparison)operation.GetSource(2).Value)
|
||||
{
|
||||
case Comparison.Equal:
|
||||
EvaluateBinaryI32(operation, (x, y) => x == y ? 1 : 0);
|
||||
break;
|
||||
case Comparison.NotEqual:
|
||||
EvaluateBinaryI32(operation, (x, y) => x != y ? 1 : 0);
|
||||
break;
|
||||
case Comparison.Greater:
|
||||
EvaluateBinaryI32(operation, (x, y) => x > y ? 1 : 0);
|
||||
break;
|
||||
case Comparison.LessOrEqual:
|
||||
EvaluateBinaryI32(operation, (x, y) => x <= y ? 1 : 0);
|
||||
break;
|
||||
case Comparison.GreaterUI:
|
||||
EvaluateBinaryI32(operation, (x, y) => (uint)x > (uint)y ? 1 : 0);
|
||||
break;
|
||||
case Comparison.LessOrEqualUI:
|
||||
EvaluateBinaryI32(operation, (x, y) => (uint)x <= (uint)y ? 1 : 0);
|
||||
break;
|
||||
case Comparison.GreaterOrEqual:
|
||||
EvaluateBinaryI32(operation, (x, y) => x >= y ? 1 : 0);
|
||||
break;
|
||||
case Comparison.Less:
|
||||
EvaluateBinaryI32(operation, (x, y) => x < y ? 1 : 0);
|
||||
break;
|
||||
case Comparison.GreaterOrEqualUI:
|
||||
EvaluateBinaryI32(operation, (x, y) => (uint)x >= (uint)y ? 1 : 0);
|
||||
break;
|
||||
case Comparison.LessUI:
|
||||
EvaluateBinaryI32(operation, (x, y) => (uint)x < (uint)y ? 1 : 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case Instruction.Copy:
|
||||
if (type == OperandType.I32)
|
||||
{
|
||||
EvaluateUnaryI32(operation, (x) => x);
|
||||
}
|
||||
else if (type == OperandType.I64)
|
||||
{
|
||||
EvaluateUnaryI64(operation, (x) => x);
|
||||
}
|
||||
break;
|
||||
|
||||
case Instruction.Divide:
|
||||
if (type == OperandType.I32)
|
||||
{
|
||||
EvaluateBinaryI32(operation, (x, y) => y != 0 ? x / y : 0);
|
||||
}
|
||||
else if (type == OperandType.I64)
|
||||
{
|
||||
EvaluateBinaryI64(operation, (x, y) => y != 0 ? x / y : 0);
|
||||
}
|
||||
break;
|
||||
|
||||
case Instruction.DivideUI:
|
||||
if (type == OperandType.I32)
|
||||
{
|
||||
EvaluateBinaryI32(operation, (x, y) => y != 0 ? (int)((uint)x / (uint)y) : 0);
|
||||
}
|
||||
else if (type == OperandType.I64)
|
||||
{
|
||||
EvaluateBinaryI64(operation, (x, y) => y != 0 ? (long)((ulong)x / (ulong)y) : 0);
|
||||
}
|
||||
break;
|
||||
|
||||
case Instruction.Multiply:
|
||||
if (type == OperandType.I32)
|
||||
{
|
||||
EvaluateBinaryI32(operation, (x, y) => x * y);
|
||||
}
|
||||
else if (type == OperandType.I64)
|
||||
{
|
||||
EvaluateBinaryI64(operation, (x, y) => x * y);
|
||||
}
|
||||
break;
|
||||
|
||||
case Instruction.Negate:
|
||||
if (type == OperandType.I32)
|
||||
{
|
||||
EvaluateUnaryI32(operation, (x) => -x);
|
||||
}
|
||||
else if (type == OperandType.I64)
|
||||
{
|
||||
EvaluateUnaryI64(operation, (x) => -x);
|
||||
}
|
||||
break;
|
||||
|
||||
case Instruction.ShiftLeft:
|
||||
if (type == OperandType.I32)
|
||||
{
|
||||
EvaluateBinaryI32(operation, (x, y) => x << y);
|
||||
}
|
||||
else if (type == OperandType.I64)
|
||||
{
|
||||
EvaluateBinaryI64(operation, (x, y) => x << (int)y);
|
||||
}
|
||||
break;
|
||||
|
||||
case Instruction.ShiftRightSI:
|
||||
if (type == OperandType.I32)
|
||||
{
|
||||
EvaluateBinaryI32(operation, (x, y) => x >> y);
|
||||
}
|
||||
else if (type == OperandType.I64)
|
||||
{
|
||||
EvaluateBinaryI64(operation, (x, y) => x >> (int)y);
|
||||
}
|
||||
break;
|
||||
|
||||
case Instruction.ShiftRightUI:
|
||||
if (type == OperandType.I32)
|
||||
{
|
||||
EvaluateBinaryI32(operation, (x, y) => (int)((uint)x >> y));
|
||||
}
|
||||
else if (type == OperandType.I64)
|
||||
{
|
||||
EvaluateBinaryI64(operation, (x, y) => (long)((ulong)x >> (int)y));
|
||||
}
|
||||
break;
|
||||
|
||||
case Instruction.SignExtend16:
|
||||
if (type == OperandType.I32)
|
||||
{
|
||||
EvaluateUnaryI32(operation, (x) => (short)x);
|
||||
}
|
||||
else if (type == OperandType.I64)
|
||||
{
|
||||
EvaluateUnaryI64(operation, (x) => (short)x);
|
||||
}
|
||||
break;
|
||||
|
||||
case Instruction.SignExtend32:
|
||||
if (type == OperandType.I32)
|
||||
{
|
||||
EvaluateUnaryI32(operation, (x) => x);
|
||||
}
|
||||
else if (type == OperandType.I64)
|
||||
{
|
||||
EvaluateUnaryI64(operation, (x) => (int)x);
|
||||
}
|
||||
break;
|
||||
|
||||
case Instruction.SignExtend8:
|
||||
if (type == OperandType.I32)
|
||||
{
|
||||
EvaluateUnaryI32(operation, (x) => (sbyte)x);
|
||||
}
|
||||
else if (type == OperandType.I64)
|
||||
{
|
||||
EvaluateUnaryI64(operation, (x) => (sbyte)x);
|
||||
}
|
||||
break;
|
||||
|
||||
case Instruction.ZeroExtend16:
|
||||
if (type == OperandType.I32)
|
||||
{
|
||||
EvaluateUnaryI32(operation, (x) => (ushort)x);
|
||||
}
|
||||
else if (type == OperandType.I64)
|
||||
{
|
||||
EvaluateUnaryI64(operation, (x) => (ushort)x);
|
||||
}
|
||||
break;
|
||||
|
||||
case Instruction.ZeroExtend32:
|
||||
if (type == OperandType.I32)
|
||||
{
|
||||
EvaluateUnaryI32(operation, (x) => x);
|
||||
}
|
||||
else if (type == OperandType.I64)
|
||||
{
|
||||
EvaluateUnaryI64(operation, (x) => (uint)x);
|
||||
}
|
||||
break;
|
||||
|
||||
case Instruction.ZeroExtend8:
|
||||
if (type == OperandType.I32)
|
||||
{
|
||||
EvaluateUnaryI32(operation, (x) => (byte)x);
|
||||
}
|
||||
else if (type == OperandType.I64)
|
||||
{
|
||||
EvaluateUnaryI64(operation, (x) => (byte)x);
|
||||
}
|
||||
break;
|
||||
|
||||
case Instruction.Subtract:
|
||||
if (type == OperandType.I32)
|
||||
{
|
||||
EvaluateBinaryI32(operation, (x, y) => x - y);
|
||||
}
|
||||
else if (type == OperandType.I64)
|
||||
{
|
||||
EvaluateBinaryI64(operation, (x, y) => x - y);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool AreAllSourcesConstant(Operation operation)
|
||||
{
|
||||
for (int index = 0; index < operation.SourcesCount; index++)
|
||||
{
|
||||
Operand srcOp = operation.GetSource(index);
|
||||
|
||||
if (srcOp.Kind != OperandKind.Constant)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void EvaluateUnaryI32(Operation operation, Func<int, int> op)
|
||||
{
|
||||
int x = operation.GetSource(0).AsInt32();
|
||||
|
||||
operation.TurnIntoCopy(Const(op(x)));
|
||||
}
|
||||
|
||||
private static void EvaluateUnaryI64(Operation operation, Func<long, long> op)
|
||||
{
|
||||
long x = operation.GetSource(0).AsInt64();
|
||||
|
||||
operation.TurnIntoCopy(Const(op(x)));
|
||||
}
|
||||
|
||||
private static void EvaluateBinaryI32(Operation operation, Func<int, int, int> op)
|
||||
{
|
||||
int x = operation.GetSource(0).AsInt32();
|
||||
int y = operation.GetSource(1).AsInt32();
|
||||
|
||||
operation.TurnIntoCopy(Const(op(x, y)));
|
||||
}
|
||||
|
||||
private static void EvaluateBinaryI64(Operation operation, Func<long, long, long> op)
|
||||
{
|
||||
long x = operation.GetSource(0).AsInt64();
|
||||
long y = operation.GetSource(1).AsInt64();
|
||||
|
||||
operation.TurnIntoCopy(Const(op(x, y)));
|
||||
}
|
||||
}
|
||||
}
|
@ -1,252 +0,0 @@
|
||||
using ARMeilleure.IntermediateRepresentation;
|
||||
using ARMeilleure.Translation;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
|
||||
|
||||
namespace ARMeilleure.CodeGen.Optimizations
|
||||
{
|
||||
static class Optimizer
|
||||
{
|
||||
public static void RunPass(ControlFlowGraph cfg)
|
||||
{
|
||||
// Scratch buffer used to store uses.
|
||||
Span<Operation> buffer = default;
|
||||
|
||||
bool modified;
|
||||
|
||||
do
|
||||
{
|
||||
modified = false;
|
||||
|
||||
for (BasicBlock block = cfg.Blocks.Last; block != null; block = block.ListPrevious)
|
||||
{
|
||||
Operation node;
|
||||
Operation prevNode;
|
||||
|
||||
for (node = block.Operations.Last; node != default; node = prevNode)
|
||||
{
|
||||
prevNode = node.ListPrevious;
|
||||
|
||||
if (IsUnused(node))
|
||||
{
|
||||
RemoveNode(block, node);
|
||||
|
||||
modified = true;
|
||||
|
||||
continue;
|
||||
}
|
||||
else if (node.Instruction == Instruction.Phi)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
ConstantFolding.RunPass(node);
|
||||
Simplification.RunPass(node);
|
||||
|
||||
if (DestIsSingleLocalVar(node))
|
||||
{
|
||||
if (IsPropagableCompare(node))
|
||||
{
|
||||
modified |= PropagateCompare(ref buffer, node);
|
||||
|
||||
if (modified && IsUnused(node))
|
||||
{
|
||||
RemoveNode(block, node);
|
||||
}
|
||||
}
|
||||
else if (IsPropagableCopy(node))
|
||||
{
|
||||
PropagateCopy(ref buffer, node);
|
||||
|
||||
RemoveNode(block, node);
|
||||
|
||||
modified = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
while (modified);
|
||||
}
|
||||
|
||||
public static void RemoveUnusedNodes(ControlFlowGraph cfg)
|
||||
{
|
||||
bool modified;
|
||||
|
||||
do
|
||||
{
|
||||
modified = false;
|
||||
|
||||
for (BasicBlock block = cfg.Blocks.Last; block != null; block = block.ListPrevious)
|
||||
{
|
||||
Operation node;
|
||||
Operation prevNode;
|
||||
|
||||
for (node = block.Operations.Last; node != default; node = prevNode)
|
||||
{
|
||||
prevNode = node.ListPrevious;
|
||||
|
||||
if (IsUnused(node))
|
||||
{
|
||||
RemoveNode(block, node);
|
||||
|
||||
modified = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
while (modified);
|
||||
}
|
||||
|
||||
private static bool PropagateCompare(ref Span<Operation> buffer, Operation compOp)
|
||||
{
|
||||
// Try to propagate Compare operations into their BranchIf uses, when these BranchIf uses are in the form
|
||||
// of:
|
||||
//
|
||||
// - BranchIf %x, 0x0, Equal ;; i.e BranchIfFalse %x
|
||||
// - BranchIf %x, 0x0, NotEqual ;; i.e BranchIfTrue %x
|
||||
//
|
||||
// The commutative property of Equal and NotEqual is taken into consideration as well.
|
||||
//
|
||||
// For example:
|
||||
//
|
||||
// %x = Compare %a, %b, comp
|
||||
// BranchIf %x, 0x0, NotEqual
|
||||
//
|
||||
// =>
|
||||
//
|
||||
// BranchIf %a, %b, comp
|
||||
|
||||
static bool IsZeroBranch(Operation operation, out Comparison compType)
|
||||
{
|
||||
compType = Comparison.Equal;
|
||||
|
||||
if (operation.Instruction != Instruction.BranchIf)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Operand src1 = operation.GetSource(0);
|
||||
Operand src2 = operation.GetSource(1);
|
||||
Operand comp = operation.GetSource(2);
|
||||
|
||||
compType = (Comparison)comp.AsInt32();
|
||||
|
||||
return (src1.Kind == OperandKind.Constant && src1.Value == 0) ||
|
||||
(src2.Kind == OperandKind.Constant && src2.Value == 0);
|
||||
}
|
||||
|
||||
bool modified = false;
|
||||
|
||||
Operand dest = compOp.Destination;
|
||||
Operand src1 = compOp.GetSource(0);
|
||||
Operand src2 = compOp.GetSource(1);
|
||||
Operand comp = compOp.GetSource(2);
|
||||
|
||||
Comparison compType = (Comparison)comp.AsInt32();
|
||||
|
||||
Span<Operation> uses = dest.GetUses(ref buffer);
|
||||
|
||||
foreach (Operation use in uses)
|
||||
{
|
||||
// If operation is a BranchIf and has a constant value 0 in its RHS or LHS source operands.
|
||||
if (IsZeroBranch(use, out Comparison otherCompType))
|
||||
{
|
||||
Comparison propCompType;
|
||||
|
||||
if (otherCompType == Comparison.NotEqual)
|
||||
{
|
||||
propCompType = compType;
|
||||
}
|
||||
else if (otherCompType == Comparison.Equal)
|
||||
{
|
||||
propCompType = compType.Invert();
|
||||
}
|
||||
else
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
use.SetSource(0, src1);
|
||||
use.SetSource(1, src2);
|
||||
use.SetSource(2, Const((int)propCompType));
|
||||
|
||||
modified = true;
|
||||
}
|
||||
}
|
||||
|
||||
return modified;
|
||||
}
|
||||
|
||||
private static void PropagateCopy(ref Span<Operation> buffer, Operation copyOp)
|
||||
{
|
||||
// Propagate copy source operand to all uses of the destination operand.
|
||||
Operand dest = copyOp.Destination;
|
||||
Operand source = copyOp.GetSource(0);
|
||||
|
||||
Span<Operation> uses = dest.GetUses(ref buffer);
|
||||
|
||||
foreach (Operation use in uses)
|
||||
{
|
||||
for (int index = 0; index < use.SourcesCount; index++)
|
||||
{
|
||||
if (use.GetSource(index) == dest)
|
||||
{
|
||||
use.SetSource(index, source);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void RemoveNode(BasicBlock block, Operation node)
|
||||
{
|
||||
// Remove a node from the nodes list, and also remove itself
|
||||
// from all the use lists on the operands that this node uses.
|
||||
block.Operations.Remove(node);
|
||||
|
||||
for (int index = 0; index < node.SourcesCount; index++)
|
||||
{
|
||||
node.SetSource(index, default);
|
||||
}
|
||||
|
||||
Debug.Assert(node.Destination == default || node.Destination.UsesCount == 0);
|
||||
|
||||
node.Destination = default;
|
||||
}
|
||||
|
||||
private static bool IsUnused(Operation node)
|
||||
{
|
||||
return DestIsSingleLocalVar(node) && node.Destination.UsesCount == 0 && !HasSideEffects(node);
|
||||
}
|
||||
|
||||
private static bool DestIsSingleLocalVar(Operation node)
|
||||
{
|
||||
return node.DestinationsCount == 1 && node.Destination.Kind == OperandKind.LocalVariable;
|
||||
}
|
||||
|
||||
private static bool HasSideEffects(Operation node)
|
||||
{
|
||||
return node.Instruction == Instruction.Call
|
||||
|| node.Instruction == Instruction.Tailcall
|
||||
|| node.Instruction == Instruction.CompareAndSwap
|
||||
|| node.Instruction == Instruction.CompareAndSwap16
|
||||
|| node.Instruction == Instruction.CompareAndSwap8;
|
||||
}
|
||||
|
||||
private static bool IsPropagableCompare(Operation operation)
|
||||
{
|
||||
return operation.Instruction == Instruction.Compare;
|
||||
}
|
||||
|
||||
private static bool IsPropagableCopy(Operation operation)
|
||||
{
|
||||
if (operation.Instruction != Instruction.Copy)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return operation.Destination.Type == operation.GetSource(0).Type;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,183 +0,0 @@
|
||||
using ARMeilleure.IntermediateRepresentation;
|
||||
using System;
|
||||
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
|
||||
|
||||
namespace ARMeilleure.CodeGen.Optimizations
|
||||
{
|
||||
static class Simplification
|
||||
{
|
||||
public static void RunPass(Operation operation)
|
||||
{
|
||||
switch (operation.Instruction)
|
||||
{
|
||||
case Instruction.Add:
|
||||
if (operation.GetSource(0).Relocatable ||
|
||||
operation.GetSource(1).Relocatable)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
TryEliminateBinaryOpComutative(operation, 0);
|
||||
break;
|
||||
|
||||
case Instruction.BitwiseAnd:
|
||||
TryEliminateBitwiseAnd(operation);
|
||||
break;
|
||||
|
||||
case Instruction.BitwiseOr:
|
||||
TryEliminateBitwiseOr(operation);
|
||||
break;
|
||||
|
||||
case Instruction.BitwiseExclusiveOr:
|
||||
TryEliminateBitwiseExclusiveOr(operation);
|
||||
break;
|
||||
|
||||
case Instruction.ConditionalSelect:
|
||||
TryEliminateConditionalSelect(operation);
|
||||
break;
|
||||
|
||||
case Instruction.Divide:
|
||||
TryEliminateBinaryOpY(operation, 1);
|
||||
break;
|
||||
|
||||
case Instruction.Multiply:
|
||||
TryEliminateBinaryOpComutative(operation, 1);
|
||||
break;
|
||||
|
||||
case Instruction.ShiftLeft:
|
||||
case Instruction.ShiftRightSI:
|
||||
case Instruction.ShiftRightUI:
|
||||
case Instruction.Subtract:
|
||||
TryEliminateBinaryOpY(operation, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private static void TryEliminateBitwiseAnd(Operation operation)
|
||||
{
|
||||
// Try to recognize and optimize those 3 patterns (in order):
|
||||
// x & 0xFFFFFFFF == x, 0xFFFFFFFF & y == y,
|
||||
// x & 0x00000000 == 0x00000000, 0x00000000 & y == 0x00000000
|
||||
Operand x = operation.GetSource(0);
|
||||
Operand y = operation.GetSource(1);
|
||||
|
||||
if (IsConstEqual(x, AllOnes(x.Type)))
|
||||
{
|
||||
operation.TurnIntoCopy(y);
|
||||
}
|
||||
else if (IsConstEqual(y, AllOnes(y.Type)))
|
||||
{
|
||||
operation.TurnIntoCopy(x);
|
||||
}
|
||||
else if (IsConstEqual(x, 0) || IsConstEqual(y, 0))
|
||||
{
|
||||
operation.TurnIntoCopy(Const(x.Type, 0));
|
||||
}
|
||||
}
|
||||
|
||||
private static void TryEliminateBitwiseOr(Operation operation)
|
||||
{
|
||||
// Try to recognize and optimize those 3 patterns (in order):
|
||||
// x | 0x00000000 == x, 0x00000000 | y == y,
|
||||
// x | 0xFFFFFFFF == 0xFFFFFFFF, 0xFFFFFFFF | y == 0xFFFFFFFF
|
||||
Operand x = operation.GetSource(0);
|
||||
Operand y = operation.GetSource(1);
|
||||
|
||||
if (IsConstEqual(x, 0))
|
||||
{
|
||||
operation.TurnIntoCopy(y);
|
||||
}
|
||||
else if (IsConstEqual(y, 0))
|
||||
{
|
||||
operation.TurnIntoCopy(x);
|
||||
}
|
||||
else if (IsConstEqual(x, AllOnes(x.Type)) || IsConstEqual(y, AllOnes(y.Type)))
|
||||
{
|
||||
operation.TurnIntoCopy(Const(AllOnes(x.Type)));
|
||||
}
|
||||
}
|
||||
|
||||
private static void TryEliminateBitwiseExclusiveOr(Operation operation)
|
||||
{
|
||||
// Try to recognize and optimize those 2 patterns (in order):
|
||||
// x ^ y == 0x00000000 when x == y
|
||||
// 0x00000000 ^ y == y, x ^ 0x00000000 == x
|
||||
Operand x = operation.GetSource(0);
|
||||
Operand y = operation.GetSource(1);
|
||||
|
||||
if (x == y && x.Type.IsInteger())
|
||||
{
|
||||
operation.TurnIntoCopy(Const(x.Type, 0));
|
||||
}
|
||||
else
|
||||
{
|
||||
TryEliminateBinaryOpComutative(operation, 0);
|
||||
}
|
||||
}
|
||||
|
||||
private static void TryEliminateBinaryOpY(Operation operation, ulong comparand)
|
||||
{
|
||||
Operand x = operation.GetSource(0);
|
||||
Operand y = operation.GetSource(1);
|
||||
|
||||
if (IsConstEqual(y, comparand))
|
||||
{
|
||||
operation.TurnIntoCopy(x);
|
||||
}
|
||||
}
|
||||
|
||||
private static void TryEliminateBinaryOpComutative(Operation operation, ulong comparand)
|
||||
{
|
||||
Operand x = operation.GetSource(0);
|
||||
Operand y = operation.GetSource(1);
|
||||
|
||||
if (IsConstEqual(x, comparand))
|
||||
{
|
||||
operation.TurnIntoCopy(y);
|
||||
}
|
||||
else if (IsConstEqual(y, comparand))
|
||||
{
|
||||
operation.TurnIntoCopy(x);
|
||||
}
|
||||
}
|
||||
|
||||
private static void TryEliminateConditionalSelect(Operation operation)
|
||||
{
|
||||
Operand cond = operation.GetSource(0);
|
||||
|
||||
if (cond.Kind != OperandKind.Constant)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// The condition is constant, we can turn it into a copy, and select
|
||||
// the source based on the condition value.
|
||||
int srcIndex = cond.Value != 0 ? 1 : 2;
|
||||
|
||||
Operand source = operation.GetSource(srcIndex);
|
||||
|
||||
operation.TurnIntoCopy(source);
|
||||
}
|
||||
|
||||
private static bool IsConstEqual(Operand operand, ulong comparand)
|
||||
{
|
||||
if (operand.Kind != OperandKind.Constant || !operand.Type.IsInteger())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return operand.Value == comparand;
|
||||
}
|
||||
|
||||
private static ulong AllOnes(OperandType type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case OperandType.I32: return ~0U;
|
||||
case OperandType.I64: return ~0UL;
|
||||
}
|
||||
|
||||
throw new ArgumentException("Invalid operand type \"" + type + "\".");
|
||||
}
|
||||
}
|
||||
}
|
@ -1,84 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace ARMeilleure.CodeGen.RegisterAllocators
|
||||
{
|
||||
unsafe struct UseList
|
||||
{
|
||||
private int* _items;
|
||||
private int _capacity;
|
||||
private int _count;
|
||||
|
||||
public int Count => _count;
|
||||
public int FirstUse => _count > 0 ? _items[_count - 1] : LiveInterval.NotFound;
|
||||
public Span<int> Span => new(_items, _count);
|
||||
|
||||
public void Add(int position)
|
||||
{
|
||||
if (_count + 1 > _capacity)
|
||||
{
|
||||
var oldSpan = Span;
|
||||
|
||||
_capacity = Math.Max(4, _capacity * 2);
|
||||
_items = Allocators.Default.Allocate<int>((uint)_capacity);
|
||||
|
||||
var newSpan = Span;
|
||||
|
||||
oldSpan.CopyTo(newSpan);
|
||||
}
|
||||
|
||||
// Use positions are usually inserted in descending order, so inserting in descending order is faster,
|
||||
// since the number of half exchanges is reduced.
|
||||
int i = _count - 1;
|
||||
|
||||
while (i >= 0 && _items[i] < position)
|
||||
{
|
||||
_items[i + 1] = _items[i--];
|
||||
}
|
||||
|
||||
_items[i + 1] = position;
|
||||
_count++;
|
||||
}
|
||||
|
||||
public int NextUse(int position)
|
||||
{
|
||||
int index = NextUseIndex(position);
|
||||
|
||||
return index != LiveInterval.NotFound ? _items[index] : LiveInterval.NotFound;
|
||||
}
|
||||
|
||||
public int NextUseIndex(int position)
|
||||
{
|
||||
int i = _count - 1;
|
||||
|
||||
if (i == -1 || position > _items[0])
|
||||
{
|
||||
return LiveInterval.NotFound;
|
||||
}
|
||||
|
||||
while (i >= 0 && _items[i] < position)
|
||||
{
|
||||
i--;
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
public UseList Split(int position)
|
||||
{
|
||||
int index = NextUseIndex(position);
|
||||
|
||||
// Since the list is in descending order, the new split list takes the front of the list and the current
|
||||
// list takes the back of the list.
|
||||
UseList result = new();
|
||||
result._count = index + 1;
|
||||
result._capacity = result._count;
|
||||
result._items = _items;
|
||||
|
||||
_count = _count - result._count;
|
||||
_capacity = _count;
|
||||
_items = _items + result._count;
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
namespace ARMeilleure.CodeGen.Unwinding
|
||||
{
|
||||
enum UnwindPseudoOp
|
||||
{
|
||||
PushReg = 0,
|
||||
SetFrame = 1,
|
||||
AllocStack = 2,
|
||||
SaveReg = 3,
|
||||
SaveXmm128 = 4
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,158 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace ARMeilleure.CodeGen.X86
|
||||
{
|
||||
static class CallingConvention
|
||||
{
|
||||
private const int RegistersMask = 0xffff;
|
||||
|
||||
public static int GetIntAvailableRegisters()
|
||||
{
|
||||
return RegistersMask & ~(1 << (int)X86Register.Rsp);
|
||||
}
|
||||
|
||||
public static int GetVecAvailableRegisters()
|
||||
{
|
||||
return RegistersMask;
|
||||
}
|
||||
|
||||
public static int GetIntCallerSavedRegisters()
|
||||
{
|
||||
if (GetCurrentCallConv() == CallConvName.Windows)
|
||||
{
|
||||
return (1 << (int)X86Register.Rax) |
|
||||
(1 << (int)X86Register.Rcx) |
|
||||
(1 << (int)X86Register.Rdx) |
|
||||
(1 << (int)X86Register.R8) |
|
||||
(1 << (int)X86Register.R9) |
|
||||
(1 << (int)X86Register.R10) |
|
||||
(1 << (int)X86Register.R11);
|
||||
}
|
||||
else /* if (GetCurrentCallConv() == CallConvName.SystemV) */
|
||||
{
|
||||
return (1 << (int)X86Register.Rax) |
|
||||
(1 << (int)X86Register.Rcx) |
|
||||
(1 << (int)X86Register.Rdx) |
|
||||
(1 << (int)X86Register.Rsi) |
|
||||
(1 << (int)X86Register.Rdi) |
|
||||
(1 << (int)X86Register.R8) |
|
||||
(1 << (int)X86Register.R9) |
|
||||
(1 << (int)X86Register.R10) |
|
||||
(1 << (int)X86Register.R11);
|
||||
}
|
||||
}
|
||||
|
||||
public static int GetVecCallerSavedRegisters()
|
||||
{
|
||||
if (GetCurrentCallConv() == CallConvName.Windows)
|
||||
{
|
||||
return (1 << (int)X86Register.Xmm0) |
|
||||
(1 << (int)X86Register.Xmm1) |
|
||||
(1 << (int)X86Register.Xmm2) |
|
||||
(1 << (int)X86Register.Xmm3) |
|
||||
(1 << (int)X86Register.Xmm4) |
|
||||
(1 << (int)X86Register.Xmm5);
|
||||
}
|
||||
else /* if (GetCurrentCallConv() == CallConvName.SystemV) */
|
||||
{
|
||||
return RegistersMask;
|
||||
}
|
||||
}
|
||||
|
||||
public static int GetIntCalleeSavedRegisters()
|
||||
{
|
||||
return GetIntCallerSavedRegisters() ^ RegistersMask;
|
||||
}
|
||||
|
||||
public static int GetVecCalleeSavedRegisters()
|
||||
{
|
||||
return GetVecCallerSavedRegisters() ^ RegistersMask;
|
||||
}
|
||||
|
||||
public static int GetArgumentsOnRegsCount()
|
||||
{
|
||||
return 4;
|
||||
}
|
||||
|
||||
public static int GetIntArgumentsOnRegsCount()
|
||||
{
|
||||
return 6;
|
||||
}
|
||||
|
||||
public static int GetVecArgumentsOnRegsCount()
|
||||
{
|
||||
return 8;
|
||||
}
|
||||
|
||||
public static X86Register GetIntArgumentRegister(int index)
|
||||
{
|
||||
if (GetCurrentCallConv() == CallConvName.Windows)
|
||||
{
|
||||
switch (index)
|
||||
{
|
||||
case 0: return X86Register.Rcx;
|
||||
case 1: return X86Register.Rdx;
|
||||
case 2: return X86Register.R8;
|
||||
case 3: return X86Register.R9;
|
||||
}
|
||||
}
|
||||
else /* if (GetCurrentCallConv() == CallConvName.SystemV) */
|
||||
{
|
||||
switch (index)
|
||||
{
|
||||
case 0: return X86Register.Rdi;
|
||||
case 1: return X86Register.Rsi;
|
||||
case 2: return X86Register.Rdx;
|
||||
case 3: return X86Register.Rcx;
|
||||
case 4: return X86Register.R8;
|
||||
case 5: return X86Register.R9;
|
||||
}
|
||||
}
|
||||
|
||||
throw new ArgumentOutOfRangeException(nameof(index));
|
||||
}
|
||||
|
||||
public static X86Register GetVecArgumentRegister(int index)
|
||||
{
|
||||
int count;
|
||||
|
||||
if (GetCurrentCallConv() == CallConvName.Windows)
|
||||
{
|
||||
count = 4;
|
||||
}
|
||||
else /* if (GetCurrentCallConv() == CallConvName.SystemV) */
|
||||
{
|
||||
count = 8;
|
||||
}
|
||||
|
||||
if ((uint)index < count)
|
||||
{
|
||||
return X86Register.Xmm0 + index;
|
||||
}
|
||||
|
||||
throw new ArgumentOutOfRangeException(nameof(index));
|
||||
}
|
||||
|
||||
public static X86Register GetIntReturnRegister()
|
||||
{
|
||||
return X86Register.Rax;
|
||||
}
|
||||
|
||||
public static X86Register GetIntReturnRegisterHigh()
|
||||
{
|
||||
return X86Register.Rdx;
|
||||
}
|
||||
|
||||
public static X86Register GetVecReturnRegister()
|
||||
{
|
||||
return X86Register.Xmm0;
|
||||
}
|
||||
|
||||
public static CallConvName GetCurrentCallConv()
|
||||
{
|
||||
return OperatingSystem.IsWindows()
|
||||
? CallConvName.Windows
|
||||
: CallConvName.SystemV;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,104 +0,0 @@
|
||||
using ARMeilleure.CodeGen.RegisterAllocators;
|
||||
using ARMeilleure.IntermediateRepresentation;
|
||||
using System.IO;
|
||||
using System.Numerics;
|
||||
|
||||
namespace ARMeilleure.CodeGen.X86
|
||||
{
|
||||
class CodeGenContext
|
||||
{
|
||||
private readonly Stream _stream;
|
||||
private readonly Operand[] _blockLabels;
|
||||
|
||||
public int StreamOffset => (int)_stream.Length;
|
||||
|
||||
public AllocationResult AllocResult { get; }
|
||||
|
||||
public Assembler Assembler { get; }
|
||||
public BasicBlock CurrBlock { get; private set; }
|
||||
|
||||
public int CallArgsRegionSize { get; }
|
||||
public int XmmSaveRegionSize { get; }
|
||||
|
||||
public CodeGenContext(AllocationResult allocResult, int maxCallArgs, int blocksCount, bool relocatable)
|
||||
{
|
||||
_stream = new MemoryStream();
|
||||
_blockLabels = new Operand[blocksCount];
|
||||
|
||||
AllocResult = allocResult;
|
||||
Assembler = new Assembler(_stream, relocatable);
|
||||
|
||||
CallArgsRegionSize = GetCallArgsRegionSize(allocResult, maxCallArgs, out int xmmSaveRegionSize);
|
||||
XmmSaveRegionSize = xmmSaveRegionSize;
|
||||
}
|
||||
|
||||
private static int GetCallArgsRegionSize(AllocationResult allocResult, int maxCallArgs, out int xmmSaveRegionSize)
|
||||
{
|
||||
// We need to add 8 bytes to the total size, as the call to this function already pushed 8 bytes (the
|
||||
// return address).
|
||||
int intMask = CallingConvention.GetIntCalleeSavedRegisters() & allocResult.IntUsedRegisters;
|
||||
int vecMask = CallingConvention.GetVecCalleeSavedRegisters() & allocResult.VecUsedRegisters;
|
||||
|
||||
xmmSaveRegionSize = BitOperations.PopCount((uint)vecMask) * 16;
|
||||
|
||||
int calleeSaveRegionSize = BitOperations.PopCount((uint)intMask) * 8 + xmmSaveRegionSize + 8;
|
||||
|
||||
int argsCount = maxCallArgs;
|
||||
|
||||
if (argsCount < 0)
|
||||
{
|
||||
// When the function has no calls, argsCount is -1. In this case, we don't need to allocate the shadow
|
||||
// space.
|
||||
argsCount = 0;
|
||||
}
|
||||
else if (argsCount < 4)
|
||||
{
|
||||
// The ABI mandates that the space for at least 4 arguments is reserved on the stack (this is called
|
||||
// shadow space).
|
||||
argsCount = 4;
|
||||
}
|
||||
|
||||
// TODO: Align XMM save region to 16 bytes because unwinding on Windows requires it.
|
||||
int frameSize = calleeSaveRegionSize + allocResult.SpillRegionSize;
|
||||
|
||||
// TODO: Instead of always multiplying by 16 (the largest possible size of a variable, since a V128 has 16
|
||||
// bytes), we should calculate the exact size consumed by the arguments passed to the called functions on
|
||||
// the stack.
|
||||
int callArgsAndFrameSize = frameSize + argsCount * 16;
|
||||
|
||||
// Ensure that the Stack Pointer will be aligned to 16 bytes.
|
||||
callArgsAndFrameSize = (callArgsAndFrameSize + 0xf) & ~0xf;
|
||||
|
||||
return callArgsAndFrameSize - frameSize;
|
||||
}
|
||||
|
||||
public void EnterBlock(BasicBlock block)
|
||||
{
|
||||
Assembler.MarkLabel(GetLabel(block));
|
||||
|
||||
CurrBlock = block;
|
||||
}
|
||||
|
||||
public void JumpTo(BasicBlock target)
|
||||
{
|
||||
Assembler.Jmp(GetLabel(target));
|
||||
}
|
||||
|
||||
public void JumpTo(X86Condition condition, BasicBlock target)
|
||||
{
|
||||
Assembler.Jcc(condition, GetLabel(target));
|
||||
}
|
||||
|
||||
private Operand GetLabel(BasicBlock block)
|
||||
{
|
||||
ref Operand label = ref _blockLabels[block.Index];
|
||||
|
||||
if (label == default)
|
||||
{
|
||||
label = Operand.Factory.Label();
|
||||
}
|
||||
|
||||
return label;
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,89 +0,0 @@
|
||||
using System;
|
||||
using System.Runtime.Intrinsics.X86;
|
||||
|
||||
namespace ARMeilleure.CodeGen.X86
|
||||
{
|
||||
static class HardwareCapabilities
|
||||
{
|
||||
static HardwareCapabilities()
|
||||
{
|
||||
if (!X86Base.IsSupported)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
(int maxNum, _, _, _) = X86Base.CpuId(0x00000000, 0x00000000);
|
||||
|
||||
(_, _, int ecx1, int edx1) = X86Base.CpuId(0x00000001, 0x00000000);
|
||||
FeatureInfo1Edx = (FeatureFlags1Edx)edx1;
|
||||
FeatureInfo1Ecx = (FeatureFlags1Ecx)ecx1;
|
||||
|
||||
if (maxNum >= 7)
|
||||
{
|
||||
(_, int ebx7, int ecx7, _) = X86Base.CpuId(0x00000007, 0x00000000);
|
||||
FeatureInfo7Ebx = (FeatureFlags7Ebx)ebx7;
|
||||
FeatureInfo7Ecx = (FeatureFlags7Ecx)ecx7;
|
||||
}
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum FeatureFlags1Edx
|
||||
{
|
||||
Sse = 1 << 25,
|
||||
Sse2 = 1 << 26
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum FeatureFlags1Ecx
|
||||
{
|
||||
Sse3 = 1 << 0,
|
||||
Pclmulqdq = 1 << 1,
|
||||
Ssse3 = 1 << 9,
|
||||
Fma = 1 << 12,
|
||||
Sse41 = 1 << 19,
|
||||
Sse42 = 1 << 20,
|
||||
Popcnt = 1 << 23,
|
||||
Aes = 1 << 25,
|
||||
Avx = 1 << 28,
|
||||
F16c = 1 << 29
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum FeatureFlags7Ebx
|
||||
{
|
||||
Avx2 = 1 << 5,
|
||||
Sha = 1 << 29
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum FeatureFlags7Ecx
|
||||
{
|
||||
Gfni = 1 << 8,
|
||||
}
|
||||
|
||||
public static FeatureFlags1Edx FeatureInfo1Edx { get; }
|
||||
public static FeatureFlags1Ecx FeatureInfo1Ecx { get; }
|
||||
public static FeatureFlags7Ebx FeatureInfo7Ebx { get; } = 0;
|
||||
public static FeatureFlags7Ecx FeatureInfo7Ecx { get; } = 0;
|
||||
|
||||
public static bool SupportsSse => FeatureInfo1Edx.HasFlag(FeatureFlags1Edx.Sse);
|
||||
public static bool SupportsSse2 => FeatureInfo1Edx.HasFlag(FeatureFlags1Edx.Sse2);
|
||||
public static bool SupportsSse3 => FeatureInfo1Ecx.HasFlag(FeatureFlags1Ecx.Sse3);
|
||||
public static bool SupportsPclmulqdq => FeatureInfo1Ecx.HasFlag(FeatureFlags1Ecx.Pclmulqdq);
|
||||
public static bool SupportsSsse3 => FeatureInfo1Ecx.HasFlag(FeatureFlags1Ecx.Ssse3);
|
||||
public static bool SupportsFma => FeatureInfo1Ecx.HasFlag(FeatureFlags1Ecx.Fma);
|
||||
public static bool SupportsSse41 => FeatureInfo1Ecx.HasFlag(FeatureFlags1Ecx.Sse41);
|
||||
public static bool SupportsSse42 => FeatureInfo1Ecx.HasFlag(FeatureFlags1Ecx.Sse42);
|
||||
public static bool SupportsPopcnt => FeatureInfo1Ecx.HasFlag(FeatureFlags1Ecx.Popcnt);
|
||||
public static bool SupportsAesni => FeatureInfo1Ecx.HasFlag(FeatureFlags1Ecx.Aes);
|
||||
public static bool SupportsAvx => FeatureInfo1Ecx.HasFlag(FeatureFlags1Ecx.Avx);
|
||||
public static bool SupportsAvx2 => FeatureInfo7Ebx.HasFlag(FeatureFlags7Ebx.Avx2) && SupportsAvx;
|
||||
public static bool SupportsF16c => FeatureInfo1Ecx.HasFlag(FeatureFlags1Ecx.F16c);
|
||||
public static bool SupportsSha => FeatureInfo7Ebx.HasFlag(FeatureFlags7Ebx.Sha);
|
||||
public static bool SupportsGfni => FeatureInfo7Ecx.HasFlag(FeatureFlags7Ecx.Gfni);
|
||||
|
||||
public static bool ForceLegacySse { get; set; }
|
||||
|
||||
public static bool SupportsVexEncoding => SupportsAvx && !ForceLegacySse;
|
||||
}
|
||||
}
|
@ -1,14 +0,0 @@
|
||||
namespace ARMeilleure.CodeGen.X86
|
||||
{
|
||||
readonly struct IntrinsicInfo
|
||||
{
|
||||
public X86Instruction Inst { get; }
|
||||
public IntrinsicType Type { get; }
|
||||
|
||||
public IntrinsicInfo(X86Instruction inst, IntrinsicType type)
|
||||
{
|
||||
Inst = inst;
|
||||
Type = type;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,197 +0,0 @@
|
||||
using ARMeilleure.Common;
|
||||
using ARMeilleure.IntermediateRepresentation;
|
||||
|
||||
namespace ARMeilleure.CodeGen.X86
|
||||
{
|
||||
static class IntrinsicTable
|
||||
{
|
||||
private static IntrinsicInfo[] _intrinTable;
|
||||
|
||||
static IntrinsicTable()
|
||||
{
|
||||
_intrinTable = new IntrinsicInfo[EnumUtils.GetCount(typeof(Intrinsic))];
|
||||
|
||||
Add(Intrinsic.X86Addpd, new IntrinsicInfo(X86Instruction.Addpd, IntrinsicType.Binary));
|
||||
Add(Intrinsic.X86Addps, new IntrinsicInfo(X86Instruction.Addps, IntrinsicType.Binary));
|
||||
Add(Intrinsic.X86Addsd, new IntrinsicInfo(X86Instruction.Addsd, IntrinsicType.Binary));
|
||||
Add(Intrinsic.X86Addss, new IntrinsicInfo(X86Instruction.Addss, IntrinsicType.Binary));
|
||||
Add(Intrinsic.X86Aesdec, new IntrinsicInfo(X86Instruction.Aesdec, IntrinsicType.Binary));
|
||||
Add(Intrinsic.X86Aesdeclast, new IntrinsicInfo(X86Instruction.Aesdeclast, IntrinsicType.Binary));
|
||||
Add(Intrinsic.X86Aesenc, new IntrinsicInfo(X86Instruction.Aesenc, IntrinsicType.Binary));
|
||||
Add(Intrinsic.X86Aesenclast, new IntrinsicInfo(X86Instruction.Aesenclast, IntrinsicType.Binary));
|
||||
Add(Intrinsic.X86Aesimc, new IntrinsicInfo(X86Instruction.Aesimc, IntrinsicType.Unary));
|
||||
Add(Intrinsic.X86Andnpd, new IntrinsicInfo(X86Instruction.Andnpd, IntrinsicType.Binary));
|
||||
Add(Intrinsic.X86Andnps, new IntrinsicInfo(X86Instruction.Andnps, IntrinsicType.Binary));
|
||||
Add(Intrinsic.X86Andpd, new IntrinsicInfo(X86Instruction.Andpd, IntrinsicType.Binary));
|
||||
Add(Intrinsic.X86Andps, new IntrinsicInfo(X86Instruction.Andps, IntrinsicType.Binary));
|
||||
Add(Intrinsic.X86Blendvpd, new IntrinsicInfo(X86Instruction.Blendvpd, IntrinsicType.Ternary));
|
||||
Add(Intrinsic.X86Blendvps, new IntrinsicInfo(X86Instruction.Blendvps, IntrinsicType.Ternary));
|
||||
Add(Intrinsic.X86Cmppd, new IntrinsicInfo(X86Instruction.Cmppd, IntrinsicType.TernaryImm));
|
||||
Add(Intrinsic.X86Cmpps, new IntrinsicInfo(X86Instruction.Cmpps, IntrinsicType.TernaryImm));
|
||||
Add(Intrinsic.X86Cmpsd, new IntrinsicInfo(X86Instruction.Cmpsd, IntrinsicType.TernaryImm));
|
||||
Add(Intrinsic.X86Cmpss, new IntrinsicInfo(X86Instruction.Cmpss, IntrinsicType.TernaryImm));
|
||||
Add(Intrinsic.X86Comisdeq, new IntrinsicInfo(X86Instruction.Comisd, IntrinsicType.Comis_));
|
||||
Add(Intrinsic.X86Comisdge, new IntrinsicInfo(X86Instruction.Comisd, IntrinsicType.Comis_));
|
||||
Add(Intrinsic.X86Comisdlt, new IntrinsicInfo(X86Instruction.Comisd, IntrinsicType.Comis_));
|
||||
Add(Intrinsic.X86Comisseq, new IntrinsicInfo(X86Instruction.Comiss, IntrinsicType.Comis_));
|
||||
Add(Intrinsic.X86Comissge, new IntrinsicInfo(X86Instruction.Comiss, IntrinsicType.Comis_));
|
||||
Add(Intrinsic.X86Comisslt, new IntrinsicInfo(X86Instruction.Comiss, IntrinsicType.Comis_));
|
||||
Add(Intrinsic.X86Crc32, new IntrinsicInfo(X86Instruction.Crc32, IntrinsicType.Crc32));
|
||||
Add(Intrinsic.X86Crc32_16, new IntrinsicInfo(X86Instruction.Crc32_16, IntrinsicType.Crc32));
|
||||
Add(Intrinsic.X86Crc32_8, new IntrinsicInfo(X86Instruction.Crc32_8, IntrinsicType.Crc32));
|
||||
Add(Intrinsic.X86Cvtdq2pd, new IntrinsicInfo(X86Instruction.Cvtdq2pd, IntrinsicType.Unary));
|
||||
Add(Intrinsic.X86Cvtdq2ps, new IntrinsicInfo(X86Instruction.Cvtdq2ps, IntrinsicType.Unary));
|
||||
Add(Intrinsic.X86Cvtpd2dq, new IntrinsicInfo(X86Instruction.Cvtpd2dq, IntrinsicType.Unary));
|
||||
Add(Intrinsic.X86Cvtpd2ps, new IntrinsicInfo(X86Instruction.Cvtpd2ps, IntrinsicType.Unary));
|
||||
Add(Intrinsic.X86Cvtps2dq, new IntrinsicInfo(X86Instruction.Cvtps2dq, IntrinsicType.Unary));
|
||||
Add(Intrinsic.X86Cvtps2pd, new IntrinsicInfo(X86Instruction.Cvtps2pd, IntrinsicType.Unary));
|
||||
Add(Intrinsic.X86Cvtsd2si, new IntrinsicInfo(X86Instruction.Cvtsd2si, IntrinsicType.UnaryToGpr));
|
||||
Add(Intrinsic.X86Cvtsd2ss, new IntrinsicInfo(X86Instruction.Cvtsd2ss, IntrinsicType.Binary));
|
||||
Add(Intrinsic.X86Cvtsi2sd, new IntrinsicInfo(X86Instruction.Cvtsi2sd, IntrinsicType.BinaryGpr));
|
||||
Add(Intrinsic.X86Cvtsi2si, new IntrinsicInfo(X86Instruction.Movd, IntrinsicType.UnaryToGpr));
|
||||
Add(Intrinsic.X86Cvtsi2ss, new IntrinsicInfo(X86Instruction.Cvtsi2ss, IntrinsicType.BinaryGpr));
|
||||
Add(Intrinsic.X86Cvtss2sd, new IntrinsicInfo(X86Instruction.Cvtss2sd, IntrinsicType.Binary));
|
||||
Add(Intrinsic.X86Cvtss2si, new IntrinsicInfo(X86Instruction.Cvtss2si, IntrinsicType.UnaryToGpr));
|
||||
Add(Intrinsic.X86Divpd, new IntrinsicInfo(X86Instruction.Divpd, IntrinsicType.Binary));
|
||||
Add(Intrinsic.X86Divps, new IntrinsicInfo(X86Instruction.Divps, IntrinsicType.Binary));
|
||||
Add(Intrinsic.X86Divsd, new IntrinsicInfo(X86Instruction.Divsd, IntrinsicType.Binary));
|
||||
Add(Intrinsic.X86Divss, new IntrinsicInfo(X86Instruction.Divss, IntrinsicType.Binary));
|
||||
Add(Intrinsic.X86Gf2p8affineqb, new IntrinsicInfo(X86Instruction.Gf2p8affineqb, IntrinsicType.TernaryImm));
|
||||
Add(Intrinsic.X86Haddpd, new IntrinsicInfo(X86Instruction.Haddpd, IntrinsicType.Binary));
|
||||
Add(Intrinsic.X86Haddps, new IntrinsicInfo(X86Instruction.Haddps, IntrinsicType.Binary));
|
||||
Add(Intrinsic.X86Insertps, new IntrinsicInfo(X86Instruction.Insertps, IntrinsicType.TernaryImm));
|
||||
Add(Intrinsic.X86Maxpd, new IntrinsicInfo(X86Instruction.Maxpd, IntrinsicType.Binary));
|
||||
Add(Intrinsic.X86Maxps, new IntrinsicInfo(X86Instruction.Maxps, IntrinsicType.Binary));
|
||||
Add(Intrinsic.X86Maxsd, new IntrinsicInfo(X86Instruction.Maxsd, IntrinsicType.Binary));
|
||||
Add(Intrinsic.X86Maxss, new IntrinsicInfo(X86Instruction.Maxss, IntrinsicType.Binary));
|
||||
Add(Intrinsic.X86Minpd, new IntrinsicInfo(X86Instruction.Minpd, IntrinsicType.Binary));
|
||||
Add(Intrinsic.X86Minps, new IntrinsicInfo(X86Instruction.Minps, IntrinsicType.Binary));
|
||||
Add(Intrinsic.X86Minsd, new IntrinsicInfo(X86Instruction.Minsd, IntrinsicType.Binary));
|
||||
Add(Intrinsic.X86Minss, new IntrinsicInfo(X86Instruction.Minss, IntrinsicType.Binary));
|
||||
Add(Intrinsic.X86Movhlps, new IntrinsicInfo(X86Instruction.Movhlps, IntrinsicType.Binary));
|
||||
Add(Intrinsic.X86Movlhps, new IntrinsicInfo(X86Instruction.Movlhps, IntrinsicType.Binary));
|
||||
Add(Intrinsic.X86Movss, new IntrinsicInfo(X86Instruction.Movss, IntrinsicType.Binary));
|
||||
Add(Intrinsic.X86Mulpd, new IntrinsicInfo(X86Instruction.Mulpd, IntrinsicType.Binary));
|
||||
Add(Intrinsic.X86Mulps, new IntrinsicInfo(X86Instruction.Mulps, IntrinsicType.Binary));
|
||||
Add(Intrinsic.X86Mulsd, new IntrinsicInfo(X86Instruction.Mulsd, IntrinsicType.Binary));
|
||||
Add(Intrinsic.X86Mulss, new IntrinsicInfo(X86Instruction.Mulss, IntrinsicType.Binary));
|
||||
Add(Intrinsic.X86Mxcsrmb, new IntrinsicInfo(X86Instruction.None, IntrinsicType.Mxcsr)); // Mask bits.
|
||||
Add(Intrinsic.X86Mxcsrub, new IntrinsicInfo(X86Instruction.None, IntrinsicType.Mxcsr)); // Unmask bits.
|
||||
Add(Intrinsic.X86Paddb, new IntrinsicInfo(X86Instruction.Paddb, IntrinsicType.Binary));
|
||||
Add(Intrinsic.X86Paddd, new IntrinsicInfo(X86Instruction.Paddd, IntrinsicType.Binary));
|
||||
Add(Intrinsic.X86Paddq, new IntrinsicInfo(X86Instruction.Paddq, IntrinsicType.Binary));
|
||||
Add(Intrinsic.X86Paddw, new IntrinsicInfo(X86Instruction.Paddw, IntrinsicType.Binary));
|
||||
Add(Intrinsic.X86Palignr, new IntrinsicInfo(X86Instruction.Palignr, IntrinsicType.TernaryImm));
|
||||
Add(Intrinsic.X86Pand, new IntrinsicInfo(X86Instruction.Pand, IntrinsicType.Binary));
|
||||
Add(Intrinsic.X86Pandn, new IntrinsicInfo(X86Instruction.Pandn, IntrinsicType.Binary));
|
||||
Add(Intrinsic.X86Pavgb, new IntrinsicInfo(X86Instruction.Pavgb, IntrinsicType.Binary));
|
||||
Add(Intrinsic.X86Pavgw, new IntrinsicInfo(X86Instruction.Pavgw, IntrinsicType.Binary));
|
||||
Add(Intrinsic.X86Pblendvb, new IntrinsicInfo(X86Instruction.Pblendvb, IntrinsicType.Ternary));
|
||||
Add(Intrinsic.X86Pclmulqdq, new IntrinsicInfo(X86Instruction.Pclmulqdq, IntrinsicType.TernaryImm));
|
||||
Add(Intrinsic.X86Pcmpeqb, new IntrinsicInfo(X86Instruction.Pcmpeqb, IntrinsicType.Binary));
|
||||
Add(Intrinsic.X86Pcmpeqd, new IntrinsicInfo(X86Instruction.Pcmpeqd, IntrinsicType.Binary));
|
||||
Add(Intrinsic.X86Pcmpeqq, new IntrinsicInfo(X86Instruction.Pcmpeqq, IntrinsicType.Binary));
|
||||
Add(Intrinsic.X86Pcmpeqw, new IntrinsicInfo(X86Instruction.Pcmpeqw, IntrinsicType.Binary));
|
||||
Add(Intrinsic.X86Pcmpgtb, new IntrinsicInfo(X86Instruction.Pcmpgtb, IntrinsicType.Binary));
|
||||
Add(Intrinsic.X86Pcmpgtd, new IntrinsicInfo(X86Instruction.Pcmpgtd, IntrinsicType.Binary));
|
||||
Add(Intrinsic.X86Pcmpgtq, new IntrinsicInfo(X86Instruction.Pcmpgtq, IntrinsicType.Binary));
|
||||
Add(Intrinsic.X86Pcmpgtw, new IntrinsicInfo(X86Instruction.Pcmpgtw, IntrinsicType.Binary));
|
||||
Add(Intrinsic.X86Pmaxsb, new IntrinsicInfo(X86Instruction.Pmaxsb, IntrinsicType.Binary));
|
||||
Add(Intrinsic.X86Pmaxsd, new IntrinsicInfo(X86Instruction.Pmaxsd, IntrinsicType.Binary));
|
||||
Add(Intrinsic.X86Pmaxsw, new IntrinsicInfo(X86Instruction.Pmaxsw, IntrinsicType.Binary));
|
||||
Add(Intrinsic.X86Pmaxub, new IntrinsicInfo(X86Instruction.Pmaxub, IntrinsicType.Binary));
|
||||
Add(Intrinsic.X86Pmaxud, new IntrinsicInfo(X86Instruction.Pmaxud, IntrinsicType.Binary));
|
||||
Add(Intrinsic.X86Pmaxuw, new IntrinsicInfo(X86Instruction.Pmaxuw, IntrinsicType.Binary));
|
||||
Add(Intrinsic.X86Pminsb, new IntrinsicInfo(X86Instruction.Pminsb, IntrinsicType.Binary));
|
||||
Add(Intrinsic.X86Pminsd, new IntrinsicInfo(X86Instruction.Pminsd, IntrinsicType.Binary));
|
||||
Add(Intrinsic.X86Pminsw, new IntrinsicInfo(X86Instruction.Pminsw, IntrinsicType.Binary));
|
||||
Add(Intrinsic.X86Pminub, new IntrinsicInfo(X86Instruction.Pminub, IntrinsicType.Binary));
|
||||
Add(Intrinsic.X86Pminud, new IntrinsicInfo(X86Instruction.Pminud, IntrinsicType.Binary));
|
||||
Add(Intrinsic.X86Pminuw, new IntrinsicInfo(X86Instruction.Pminuw, IntrinsicType.Binary));
|
||||
Add(Intrinsic.X86Pmovsxbw, new IntrinsicInfo(X86Instruction.Pmovsxbw, IntrinsicType.Unary));
|
||||
Add(Intrinsic.X86Pmovsxdq, new IntrinsicInfo(X86Instruction.Pmovsxdq, IntrinsicType.Unary));
|
||||
Add(Intrinsic.X86Pmovsxwd, new IntrinsicInfo(X86Instruction.Pmovsxwd, IntrinsicType.Unary));
|
||||
Add(Intrinsic.X86Pmovzxbw, new IntrinsicInfo(X86Instruction.Pmovzxbw, IntrinsicType.Unary));
|
||||
Add(Intrinsic.X86Pmovzxdq, new IntrinsicInfo(X86Instruction.Pmovzxdq, IntrinsicType.Unary));
|
||||
Add(Intrinsic.X86Pmovzxwd, new IntrinsicInfo(X86Instruction.Pmovzxwd, IntrinsicType.Unary));
|
||||
Add(Intrinsic.X86Pmulld, new IntrinsicInfo(X86Instruction.Pmulld, IntrinsicType.Binary));
|
||||
Add(Intrinsic.X86Pmullw, new IntrinsicInfo(X86Instruction.Pmullw, IntrinsicType.Binary));
|
||||
Add(Intrinsic.X86Popcnt, new IntrinsicInfo(X86Instruction.Popcnt, IntrinsicType.PopCount));
|
||||
Add(Intrinsic.X86Por, new IntrinsicInfo(X86Instruction.Por, IntrinsicType.Binary));
|
||||
Add(Intrinsic.X86Pshufb, new IntrinsicInfo(X86Instruction.Pshufb, IntrinsicType.Binary));
|
||||
Add(Intrinsic.X86Pshufd, new IntrinsicInfo(X86Instruction.Pshufd, IntrinsicType.BinaryImm));
|
||||
Add(Intrinsic.X86Pslld, new IntrinsicInfo(X86Instruction.Pslld, IntrinsicType.Binary));
|
||||
Add(Intrinsic.X86Pslldq, new IntrinsicInfo(X86Instruction.Pslldq, IntrinsicType.Binary));
|
||||
Add(Intrinsic.X86Psllq, new IntrinsicInfo(X86Instruction.Psllq, IntrinsicType.Binary));
|
||||
Add(Intrinsic.X86Psllw, new IntrinsicInfo(X86Instruction.Psllw, IntrinsicType.Binary));
|
||||
Add(Intrinsic.X86Psrad, new IntrinsicInfo(X86Instruction.Psrad, IntrinsicType.Binary));
|
||||
Add(Intrinsic.X86Psraw, new IntrinsicInfo(X86Instruction.Psraw, IntrinsicType.Binary));
|
||||
Add(Intrinsic.X86Psrld, new IntrinsicInfo(X86Instruction.Psrld, IntrinsicType.Binary));
|
||||
Add(Intrinsic.X86Psrlq, new IntrinsicInfo(X86Instruction.Psrlq, IntrinsicType.Binary));
|
||||
Add(Intrinsic.X86Psrldq, new IntrinsicInfo(X86Instruction.Psrldq, IntrinsicType.Binary));
|
||||
Add(Intrinsic.X86Psrlw, new IntrinsicInfo(X86Instruction.Psrlw, IntrinsicType.Binary));
|
||||
Add(Intrinsic.X86Psubb, new IntrinsicInfo(X86Instruction.Psubb, IntrinsicType.Binary));
|
||||
Add(Intrinsic.X86Psubd, new IntrinsicInfo(X86Instruction.Psubd, IntrinsicType.Binary));
|
||||
Add(Intrinsic.X86Psubq, new IntrinsicInfo(X86Instruction.Psubq, IntrinsicType.Binary));
|
||||
Add(Intrinsic.X86Psubw, new IntrinsicInfo(X86Instruction.Psubw, IntrinsicType.Binary));
|
||||
Add(Intrinsic.X86Punpckhbw, new IntrinsicInfo(X86Instruction.Punpckhbw, IntrinsicType.Binary));
|
||||
Add(Intrinsic.X86Punpckhdq, new IntrinsicInfo(X86Instruction.Punpckhdq, IntrinsicType.Binary));
|
||||
Add(Intrinsic.X86Punpckhqdq, new IntrinsicInfo(X86Instruction.Punpckhqdq, IntrinsicType.Binary));
|
||||
Add(Intrinsic.X86Punpckhwd, new IntrinsicInfo(X86Instruction.Punpckhwd, IntrinsicType.Binary));
|
||||
Add(Intrinsic.X86Punpcklbw, new IntrinsicInfo(X86Instruction.Punpcklbw, IntrinsicType.Binary));
|
||||
Add(Intrinsic.X86Punpckldq, new IntrinsicInfo(X86Instruction.Punpckldq, IntrinsicType.Binary));
|
||||
Add(Intrinsic.X86Punpcklqdq, new IntrinsicInfo(X86Instruction.Punpcklqdq, IntrinsicType.Binary));
|
||||
Add(Intrinsic.X86Punpcklwd, new IntrinsicInfo(X86Instruction.Punpcklwd, IntrinsicType.Binary));
|
||||
Add(Intrinsic.X86Pxor, new IntrinsicInfo(X86Instruction.Pxor, IntrinsicType.Binary));
|
||||
Add(Intrinsic.X86Rcpps, new IntrinsicInfo(X86Instruction.Rcpps, IntrinsicType.Unary));
|
||||
Add(Intrinsic.X86Rcpss, new IntrinsicInfo(X86Instruction.Rcpss, IntrinsicType.Unary));
|
||||
Add(Intrinsic.X86Roundpd, new IntrinsicInfo(X86Instruction.Roundpd, IntrinsicType.BinaryImm));
|
||||
Add(Intrinsic.X86Roundps, new IntrinsicInfo(X86Instruction.Roundps, IntrinsicType.BinaryImm));
|
||||
Add(Intrinsic.X86Roundsd, new IntrinsicInfo(X86Instruction.Roundsd, IntrinsicType.BinaryImm));
|
||||
Add(Intrinsic.X86Roundss, new IntrinsicInfo(X86Instruction.Roundss, IntrinsicType.BinaryImm));
|
||||
Add(Intrinsic.X86Rsqrtps, new IntrinsicInfo(X86Instruction.Rsqrtps, IntrinsicType.Unary));
|
||||
Add(Intrinsic.X86Rsqrtss, new IntrinsicInfo(X86Instruction.Rsqrtss, IntrinsicType.Unary));
|
||||
Add(Intrinsic.X86Sha256Msg1, new IntrinsicInfo(X86Instruction.Sha256Msg1, IntrinsicType.Binary));
|
||||
Add(Intrinsic.X86Sha256Msg2, new IntrinsicInfo(X86Instruction.Sha256Msg2, IntrinsicType.Binary));
|
||||
Add(Intrinsic.X86Sha256Rnds2, new IntrinsicInfo(X86Instruction.Sha256Rnds2, IntrinsicType.Ternary));
|
||||
Add(Intrinsic.X86Shufpd, new IntrinsicInfo(X86Instruction.Shufpd, IntrinsicType.TernaryImm));
|
||||
Add(Intrinsic.X86Shufps, new IntrinsicInfo(X86Instruction.Shufps, IntrinsicType.TernaryImm));
|
||||
Add(Intrinsic.X86Sqrtpd, new IntrinsicInfo(X86Instruction.Sqrtpd, IntrinsicType.Unary));
|
||||
Add(Intrinsic.X86Sqrtps, new IntrinsicInfo(X86Instruction.Sqrtps, IntrinsicType.Unary));
|
||||
Add(Intrinsic.X86Sqrtsd, new IntrinsicInfo(X86Instruction.Sqrtsd, IntrinsicType.Unary));
|
||||
Add(Intrinsic.X86Sqrtss, new IntrinsicInfo(X86Instruction.Sqrtss, IntrinsicType.Unary));
|
||||
Add(Intrinsic.X86Subpd, new IntrinsicInfo(X86Instruction.Subpd, IntrinsicType.Binary));
|
||||
Add(Intrinsic.X86Subps, new IntrinsicInfo(X86Instruction.Subps, IntrinsicType.Binary));
|
||||
Add(Intrinsic.X86Subsd, new IntrinsicInfo(X86Instruction.Subsd, IntrinsicType.Binary));
|
||||
Add(Intrinsic.X86Subss, new IntrinsicInfo(X86Instruction.Subss, IntrinsicType.Binary));
|
||||
Add(Intrinsic.X86Unpckhpd, new IntrinsicInfo(X86Instruction.Unpckhpd, IntrinsicType.Binary));
|
||||
Add(Intrinsic.X86Unpckhps, new IntrinsicInfo(X86Instruction.Unpckhps, IntrinsicType.Binary));
|
||||
Add(Intrinsic.X86Unpcklpd, new IntrinsicInfo(X86Instruction.Unpcklpd, IntrinsicType.Binary));
|
||||
Add(Intrinsic.X86Unpcklps, new IntrinsicInfo(X86Instruction.Unpcklps, IntrinsicType.Binary));
|
||||
Add(Intrinsic.X86Vcvtph2ps, new IntrinsicInfo(X86Instruction.Vcvtph2ps, IntrinsicType.Unary));
|
||||
Add(Intrinsic.X86Vcvtps2ph, new IntrinsicInfo(X86Instruction.Vcvtps2ph, IntrinsicType.BinaryImm));
|
||||
Add(Intrinsic.X86Vfmadd231ps, new IntrinsicInfo(X86Instruction.Vfmadd231ps, IntrinsicType.Fma));
|
||||
Add(Intrinsic.X86Vfmadd231sd, new IntrinsicInfo(X86Instruction.Vfmadd231sd, IntrinsicType.Fma));
|
||||
Add(Intrinsic.X86Vfmadd231ss, new IntrinsicInfo(X86Instruction.Vfmadd231ss, IntrinsicType.Fma));
|
||||
Add(Intrinsic.X86Vfmsub231sd, new IntrinsicInfo(X86Instruction.Vfmsub231sd, IntrinsicType.Fma));
|
||||
Add(Intrinsic.X86Vfmsub231ss, new IntrinsicInfo(X86Instruction.Vfmsub231ss, IntrinsicType.Fma));
|
||||
Add(Intrinsic.X86Vfnmadd231ps, new IntrinsicInfo(X86Instruction.Vfnmadd231ps, IntrinsicType.Fma));
|
||||
Add(Intrinsic.X86Vfnmadd231sd, new IntrinsicInfo(X86Instruction.Vfnmadd231sd, IntrinsicType.Fma));
|
||||
Add(Intrinsic.X86Vfnmadd231ss, new IntrinsicInfo(X86Instruction.Vfnmadd231ss, IntrinsicType.Fma));
|
||||
Add(Intrinsic.X86Vfnmsub231sd, new IntrinsicInfo(X86Instruction.Vfnmsub231sd, IntrinsicType.Fma));
|
||||
Add(Intrinsic.X86Vfnmsub231ss, new IntrinsicInfo(X86Instruction.Vfnmsub231ss, IntrinsicType.Fma));
|
||||
Add(Intrinsic.X86Xorpd, new IntrinsicInfo(X86Instruction.Xorpd, IntrinsicType.Binary));
|
||||
Add(Intrinsic.X86Xorps, new IntrinsicInfo(X86Instruction.Xorps, IntrinsicType.Binary));
|
||||
}
|
||||
|
||||
private static void Add(Intrinsic intrin, IntrinsicInfo info)
|
||||
{
|
||||
_intrinTable[(int)intrin] = info;
|
||||
}
|
||||
|
||||
public static IntrinsicInfo GetInfo(Intrinsic intrin)
|
||||
{
|
||||
return _intrinTable[(int)intrin];
|
||||
}
|
||||
}
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
namespace ARMeilleure.CodeGen.X86
|
||||
{
|
||||
enum IntrinsicType
|
||||
{
|
||||
Comis_,
|
||||
Mxcsr,
|
||||
PopCount,
|
||||
Unary,
|
||||
UnaryToGpr,
|
||||
Binary,
|
||||
BinaryGpr,
|
||||
BinaryImm,
|
||||
Crc32,
|
||||
Ternary,
|
||||
TernaryImm,
|
||||
Fma
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,47 +0,0 @@
|
||||
using ARMeilleure.IntermediateRepresentation;
|
||||
using System;
|
||||
|
||||
namespace ARMeilleure.CodeGen.X86
|
||||
{
|
||||
enum X86Condition
|
||||
{
|
||||
Overflow = 0x0,
|
||||
NotOverflow = 0x1,
|
||||
Below = 0x2,
|
||||
AboveOrEqual = 0x3,
|
||||
Equal = 0x4,
|
||||
NotEqual = 0x5,
|
||||
BelowOrEqual = 0x6,
|
||||
Above = 0x7,
|
||||
Sign = 0x8,
|
||||
NotSign = 0x9,
|
||||
ParityEven = 0xa,
|
||||
ParityOdd = 0xb,
|
||||
Less = 0xc,
|
||||
GreaterOrEqual = 0xd,
|
||||
LessOrEqual = 0xe,
|
||||
Greater = 0xf
|
||||
}
|
||||
|
||||
static class ComparisonX86Extensions
|
||||
{
|
||||
public static X86Condition ToX86Condition(this Comparison comp)
|
||||
{
|
||||
return comp switch
|
||||
{
|
||||
Comparison.Equal => X86Condition.Equal,
|
||||
Comparison.NotEqual => X86Condition.NotEqual,
|
||||
Comparison.Greater => X86Condition.Greater,
|
||||
Comparison.LessOrEqual => X86Condition.LessOrEqual,
|
||||
Comparison.GreaterUI => X86Condition.Above,
|
||||
Comparison.LessOrEqualUI => X86Condition.BelowOrEqual,
|
||||
Comparison.GreaterOrEqual => X86Condition.GreaterOrEqual,
|
||||
Comparison.Less => X86Condition.Less,
|
||||
Comparison.GreaterOrEqualUI => X86Condition.AboveOrEqual,
|
||||
Comparison.LessUI => X86Condition.Below,
|
||||
|
||||
_ => throw new ArgumentException(null, nameof(comp))
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
@ -1,41 +0,0 @@
|
||||
namespace ARMeilleure.CodeGen.X86
|
||||
{
|
||||
enum X86Register
|
||||
{
|
||||
Invalid = -1,
|
||||
|
||||
Rax = 0,
|
||||
Rcx = 1,
|
||||
Rdx = 2,
|
||||
Rbx = 3,
|
||||
Rsp = 4,
|
||||
Rbp = 5,
|
||||
Rsi = 6,
|
||||
Rdi = 7,
|
||||
R8 = 8,
|
||||
R9 = 9,
|
||||
R10 = 10,
|
||||
R11 = 11,
|
||||
R12 = 12,
|
||||
R13 = 13,
|
||||
R14 = 14,
|
||||
R15 = 15,
|
||||
|
||||
Xmm0 = 0,
|
||||
Xmm1 = 1,
|
||||
Xmm2 = 2,
|
||||
Xmm3 = 3,
|
||||
Xmm4 = 4,
|
||||
Xmm5 = 5,
|
||||
Xmm6 = 6,
|
||||
Xmm7 = 7,
|
||||
Xmm8 = 8,
|
||||
Xmm9 = 9,
|
||||
Xmm10 = 10,
|
||||
Xmm11 = 11,
|
||||
Xmm12 = 12,
|
||||
Xmm13 = 13,
|
||||
Xmm14 = 14,
|
||||
Xmm15 = 15
|
||||
}
|
||||
}
|
@ -1,222 +0,0 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace ARMeilleure.Common
|
||||
{
|
||||
unsafe class BitMap : IEnumerable<int>, IDisposable
|
||||
{
|
||||
private const int IntSize = 64;
|
||||
private const int IntMask = IntSize - 1;
|
||||
|
||||
private int _count;
|
||||
private long* _masks;
|
||||
private readonly Allocator _allocator;
|
||||
|
||||
public BitMap(Allocator allocator)
|
||||
{
|
||||
_allocator = allocator;
|
||||
}
|
||||
|
||||
public BitMap(Allocator allocator, int capacity) : this(allocator)
|
||||
{
|
||||
EnsureCapacity(capacity);
|
||||
}
|
||||
|
||||
public bool Set(int bit)
|
||||
{
|
||||
EnsureCapacity(bit + 1);
|
||||
|
||||
int wordIndex = bit / IntSize;
|
||||
int wordBit = bit & IntMask;
|
||||
|
||||
long wordMask = 1L << wordBit;
|
||||
|
||||
if ((_masks[wordIndex] & wordMask) != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
_masks[wordIndex] |= wordMask;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Clear(int bit)
|
||||
{
|
||||
EnsureCapacity(bit + 1);
|
||||
|
||||
int wordIndex = bit / IntSize;
|
||||
int wordBit = bit & IntMask;
|
||||
|
||||
long wordMask = 1L << wordBit;
|
||||
|
||||
_masks[wordIndex] &= ~wordMask;
|
||||
}
|
||||
|
||||
public bool IsSet(int bit)
|
||||
{
|
||||
EnsureCapacity(bit + 1);
|
||||
|
||||
int wordIndex = bit / IntSize;
|
||||
int wordBit = bit & IntMask;
|
||||
|
||||
return (_masks[wordIndex] & (1L << wordBit)) != 0;
|
||||
}
|
||||
|
||||
public int FindFirstUnset()
|
||||
{
|
||||
for (int index = 0; index < _count; index++)
|
||||
{
|
||||
long mask = _masks[index];
|
||||
|
||||
if (mask != -1L)
|
||||
{
|
||||
return BitOperations.TrailingZeroCount(~mask) + index * IntSize;
|
||||
}
|
||||
}
|
||||
|
||||
return _count * IntSize;
|
||||
}
|
||||
|
||||
public bool Set(BitMap map)
|
||||
{
|
||||
EnsureCapacity(map._count * IntSize);
|
||||
|
||||
bool modified = false;
|
||||
|
||||
for (int index = 0; index < _count; index++)
|
||||
{
|
||||
long newValue = _masks[index] | map._masks[index];
|
||||
|
||||
if (_masks[index] != newValue)
|
||||
{
|
||||
_masks[index] = newValue;
|
||||
|
||||
modified = true;
|
||||
}
|
||||
}
|
||||
|
||||
return modified;
|
||||
}
|
||||
|
||||
public bool Clear(BitMap map)
|
||||
{
|
||||
EnsureCapacity(map._count * IntSize);
|
||||
|
||||
bool modified = false;
|
||||
|
||||
for (int index = 0; index < _count; index++)
|
||||
{
|
||||
long newValue = _masks[index] & ~map._masks[index];
|
||||
|
||||
if (_masks[index] != newValue)
|
||||
{
|
||||
_masks[index] = newValue;
|
||||
|
||||
modified = true;
|
||||
}
|
||||
}
|
||||
|
||||
return modified;
|
||||
}
|
||||
|
||||
private void EnsureCapacity(int size)
|
||||
{
|
||||
int count = (size + IntMask) / IntSize;
|
||||
|
||||
if (count > _count)
|
||||
{
|
||||
var oldMask = _masks;
|
||||
var oldSpan = new Span<long>(_masks, _count);
|
||||
|
||||
_masks = _allocator.Allocate<long>((uint)count);
|
||||
_count = count;
|
||||
|
||||
var newSpan = new Span<long>(_masks, _count);
|
||||
|
||||
oldSpan.CopyTo(newSpan);
|
||||
newSpan.Slice(oldSpan.Length).Clear();
|
||||
|
||||
_allocator.Free(oldMask);
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_masks != null)
|
||||
{
|
||||
_allocator.Free(_masks);
|
||||
|
||||
_masks = null;
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
|
||||
IEnumerator<int> IEnumerable<int>.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
|
||||
public Enumerator GetEnumerator()
|
||||
{
|
||||
return new Enumerator(this);
|
||||
}
|
||||
|
||||
public struct Enumerator : IEnumerator<int>
|
||||
{
|
||||
private long _index;
|
||||
private long _mask;
|
||||
private int _bit;
|
||||
private readonly BitMap _map;
|
||||
|
||||
public int Current => (int)_index * IntSize + _bit;
|
||||
object IEnumerator.Current => Current;
|
||||
|
||||
public Enumerator(BitMap map)
|
||||
{
|
||||
_index = -1;
|
||||
_mask = 0;
|
||||
_bit = 0;
|
||||
_map = map;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool MoveNext()
|
||||
{
|
||||
if (_mask != 0)
|
||||
{
|
||||
_mask &= ~(1L << _bit);
|
||||
}
|
||||
|
||||
// Manually hoist these loads, because RyuJIT does not.
|
||||
long count = (uint)_map._count;
|
||||
long* masks = _map._masks;
|
||||
|
||||
while (_mask == 0)
|
||||
{
|
||||
if (++_index >= count)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
_mask = masks[_index];
|
||||
}
|
||||
|
||||
_bit = BitOperations.TrailingZeroCount(_mask);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Reset() { }
|
||||
|
||||
public void Dispose() { }
|
||||
}
|
||||
}
|
||||
}
|
@ -1,101 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class Block
|
||||
{
|
||||
public ulong Address { get; set; }
|
||||
public ulong EndAddress { get; set; }
|
||||
|
||||
public Block Next { get; set; }
|
||||
public Block Branch { get; set; }
|
||||
|
||||
public bool Exit { get; set; }
|
||||
|
||||
public List<OpCode> OpCodes { get; }
|
||||
|
||||
public Block()
|
||||
{
|
||||
OpCodes = new List<OpCode>();
|
||||
}
|
||||
|
||||
public Block(ulong address) : this()
|
||||
{
|
||||
Address = address;
|
||||
}
|
||||
|
||||
public void Split(Block rightBlock)
|
||||
{
|
||||
int splitIndex = BinarySearch(OpCodes, rightBlock.Address);
|
||||
|
||||
if (OpCodes[splitIndex].Address < rightBlock.Address)
|
||||
{
|
||||
splitIndex++;
|
||||
}
|
||||
|
||||
int splitCount = OpCodes.Count - splitIndex;
|
||||
|
||||
if (splitCount <= 0)
|
||||
{
|
||||
throw new ArgumentException("Can't split at right block address.");
|
||||
}
|
||||
|
||||
rightBlock.EndAddress = EndAddress;
|
||||
|
||||
rightBlock.Next = Next;
|
||||
rightBlock.Branch = Branch;
|
||||
|
||||
rightBlock.OpCodes.AddRange(OpCodes.GetRange(splitIndex, splitCount));
|
||||
|
||||
EndAddress = rightBlock.Address;
|
||||
|
||||
Next = rightBlock;
|
||||
Branch = null;
|
||||
|
||||
OpCodes.RemoveRange(splitIndex, splitCount);
|
||||
}
|
||||
|
||||
private static int BinarySearch(List<OpCode> opCodes, ulong address)
|
||||
{
|
||||
int left = 0;
|
||||
int middle = 0;
|
||||
int right = opCodes.Count - 1;
|
||||
|
||||
while (left <= right)
|
||||
{
|
||||
int size = right - left;
|
||||
|
||||
middle = left + (size >> 1);
|
||||
|
||||
OpCode opCode = opCodes[middle];
|
||||
|
||||
if (address == (ulong)opCode.Address)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (address < (ulong)opCode.Address)
|
||||
{
|
||||
right = middle - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
left = middle + 1;
|
||||
}
|
||||
}
|
||||
|
||||
return middle;
|
||||
}
|
||||
|
||||
public OpCode GetLastOp()
|
||||
{
|
||||
if (OpCodes.Count > 0)
|
||||
{
|
||||
return OpCodes[OpCodes.Count - 1];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,32 +0,0 @@
|
||||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
enum Condition
|
||||
{
|
||||
Eq = 0,
|
||||
Ne = 1,
|
||||
GeUn = 2,
|
||||
LtUn = 3,
|
||||
Mi = 4,
|
||||
Pl = 5,
|
||||
Vs = 6,
|
||||
Vc = 7,
|
||||
GtUn = 8,
|
||||
LeUn = 9,
|
||||
Ge = 10,
|
||||
Lt = 11,
|
||||
Gt = 12,
|
||||
Le = 13,
|
||||
Al = 14,
|
||||
Nv = 15
|
||||
}
|
||||
|
||||
static class ConditionExtensions
|
||||
{
|
||||
public static Condition Invert(this Condition cond)
|
||||
{
|
||||
// Bit 0 of all conditions is basically a negation bit, so
|
||||
// inverting this bit has the effect of inverting the condition.
|
||||
return (Condition)((int)cond ^ 1);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
enum DataOp
|
||||
{
|
||||
Adr = 0,
|
||||
Arithmetic = 1,
|
||||
Logical = 2,
|
||||
BitField = 3
|
||||
}
|
||||
}
|
@ -1,391 +0,0 @@
|
||||
using ARMeilleure.Decoders.Optimizations;
|
||||
using ARMeilleure.Instructions;
|
||||
using ARMeilleure.Memory;
|
||||
using ARMeilleure.State;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
static class Decoder
|
||||
{
|
||||
// We define a limit on the number of instructions that a function may have,
|
||||
// this prevents functions being potentially too large, which would
|
||||
// take too long to compile and use too much memory.
|
||||
private const int MaxInstsPerFunction = 2500;
|
||||
|
||||
// For lower code quality translation, we set a lower limit since we're blocking execution.
|
||||
private const int MaxInstsPerFunctionLowCq = 500;
|
||||
|
||||
public static Block[] Decode(IMemoryManager memory, ulong address, ExecutionMode mode, bool highCq, DecoderMode dMode)
|
||||
{
|
||||
List<Block> blocks = new List<Block>();
|
||||
|
||||
Queue<Block> workQueue = new Queue<Block>();
|
||||
|
||||
Dictionary<ulong, Block> visited = new Dictionary<ulong, Block>();
|
||||
|
||||
Debug.Assert(MaxInstsPerFunctionLowCq <= MaxInstsPerFunction);
|
||||
|
||||
int opsCount = 0;
|
||||
|
||||
int instructionLimit = highCq ? MaxInstsPerFunction : MaxInstsPerFunctionLowCq;
|
||||
|
||||
Block GetBlock(ulong blkAddress)
|
||||
{
|
||||
if (!visited.TryGetValue(blkAddress, out Block block))
|
||||
{
|
||||
block = new Block(blkAddress);
|
||||
|
||||
if ((dMode != DecoderMode.MultipleBlocks && visited.Count >= 1) || opsCount > instructionLimit || !memory.IsMapped(blkAddress))
|
||||
{
|
||||
block.Exit = true;
|
||||
block.EndAddress = blkAddress;
|
||||
}
|
||||
|
||||
workQueue.Enqueue(block);
|
||||
|
||||
visited.Add(blkAddress, block);
|
||||
}
|
||||
|
||||
return block;
|
||||
}
|
||||
|
||||
GetBlock(address);
|
||||
|
||||
while (workQueue.TryDequeue(out Block currBlock))
|
||||
{
|
||||
// Check if the current block is inside another block.
|
||||
if (BinarySearch(blocks, currBlock.Address, out int nBlkIndex))
|
||||
{
|
||||
Block nBlock = blocks[nBlkIndex];
|
||||
|
||||
if (nBlock.Address == currBlock.Address)
|
||||
{
|
||||
throw new InvalidOperationException("Found duplicate block address on the list.");
|
||||
}
|
||||
|
||||
currBlock.Exit = false;
|
||||
|
||||
nBlock.Split(currBlock);
|
||||
|
||||
blocks.Insert(nBlkIndex + 1, currBlock);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!currBlock.Exit)
|
||||
{
|
||||
// If we have a block after the current one, set the limit address.
|
||||
ulong limitAddress = ulong.MaxValue;
|
||||
|
||||
if (nBlkIndex != blocks.Count)
|
||||
{
|
||||
Block nBlock = blocks[nBlkIndex];
|
||||
|
||||
int nextIndex = nBlkIndex + 1;
|
||||
|
||||
if (nBlock.Address < currBlock.Address && nextIndex < blocks.Count)
|
||||
{
|
||||
limitAddress = blocks[nextIndex].Address;
|
||||
}
|
||||
else if (nBlock.Address > currBlock.Address)
|
||||
{
|
||||
limitAddress = blocks[nBlkIndex].Address;
|
||||
}
|
||||
}
|
||||
|
||||
if (dMode == DecoderMode.SingleInstruction)
|
||||
{
|
||||
// Only read at most one instruction
|
||||
limitAddress = currBlock.Address + 1;
|
||||
}
|
||||
|
||||
FillBlock(memory, mode, currBlock, limitAddress);
|
||||
|
||||
opsCount += currBlock.OpCodes.Count;
|
||||
|
||||
if (currBlock.OpCodes.Count != 0)
|
||||
{
|
||||
// Set child blocks. "Branch" is the block the branch instruction
|
||||
// points to (when taken), "Next" is the block at the next address,
|
||||
// executed when the branch is not taken. For Unconditional Branches
|
||||
// (except BL/BLR that are sub calls) or end of executable, Next is null.
|
||||
OpCode lastOp = currBlock.GetLastOp();
|
||||
|
||||
bool isCall = IsCall(lastOp);
|
||||
|
||||
if (lastOp is IOpCodeBImm op && !isCall)
|
||||
{
|
||||
currBlock.Branch = GetBlock((ulong)op.Immediate);
|
||||
}
|
||||
|
||||
if (isCall || !(IsUnconditionalBranch(lastOp) || IsTrap(lastOp)))
|
||||
{
|
||||
currBlock.Next = GetBlock(currBlock.EndAddress);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Insert the new block on the list (sorted by address).
|
||||
if (blocks.Count != 0)
|
||||
{
|
||||
Block nBlock = blocks[nBlkIndex];
|
||||
|
||||
blocks.Insert(nBlkIndex + (nBlock.Address < currBlock.Address ? 1 : 0), currBlock);
|
||||
}
|
||||
else
|
||||
{
|
||||
blocks.Add(currBlock);
|
||||
}
|
||||
}
|
||||
|
||||
if (blocks.Count == 1 && blocks[0].OpCodes.Count == 0)
|
||||
{
|
||||
Debug.Assert(blocks[0].Exit);
|
||||
Debug.Assert(blocks[0].Address == blocks[0].EndAddress);
|
||||
|
||||
throw new InvalidOperationException($"Decoded a single empty exit block. Entry point = 0x{address:X}.");
|
||||
}
|
||||
|
||||
if (dMode == DecoderMode.MultipleBlocks)
|
||||
{
|
||||
return TailCallRemover.RunPass(address, blocks);
|
||||
}
|
||||
else
|
||||
{
|
||||
return blocks.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
public static bool BinarySearch(List<Block> blocks, ulong address, out int index)
|
||||
{
|
||||
index = 0;
|
||||
|
||||
int left = 0;
|
||||
int right = blocks.Count - 1;
|
||||
|
||||
while (left <= right)
|
||||
{
|
||||
int size = right - left;
|
||||
|
||||
int middle = left + (size >> 1);
|
||||
|
||||
Block block = blocks[middle];
|
||||
|
||||
index = middle;
|
||||
|
||||
if (address >= block.Address && address < block.EndAddress)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (address < block.Address)
|
||||
{
|
||||
right = middle - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
left = middle + 1;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static void FillBlock(
|
||||
IMemoryManager memory,
|
||||
ExecutionMode mode,
|
||||
Block block,
|
||||
ulong limitAddress)
|
||||
{
|
||||
ulong address = block.Address;
|
||||
int itBlockSize = 0;
|
||||
|
||||
OpCode opCode;
|
||||
|
||||
do
|
||||
{
|
||||
if (address >= limitAddress && itBlockSize == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
opCode = DecodeOpCode(memory, address, mode);
|
||||
|
||||
block.OpCodes.Add(opCode);
|
||||
|
||||
address += (ulong)opCode.OpCodeSizeInBytes;
|
||||
|
||||
if (opCode is OpCodeT16IfThen it)
|
||||
{
|
||||
itBlockSize = it.IfThenBlockSize;
|
||||
}
|
||||
else if (itBlockSize > 0)
|
||||
{
|
||||
itBlockSize--;
|
||||
}
|
||||
}
|
||||
while (!(IsBranch(opCode) || IsException(opCode)));
|
||||
|
||||
block.EndAddress = address;
|
||||
}
|
||||
|
||||
private static bool IsBranch(OpCode opCode)
|
||||
{
|
||||
return opCode is OpCodeBImm ||
|
||||
opCode is OpCodeBReg || IsAarch32Branch(opCode);
|
||||
}
|
||||
|
||||
private static bool IsUnconditionalBranch(OpCode opCode)
|
||||
{
|
||||
return opCode is OpCodeBImmAl ||
|
||||
opCode is OpCodeBReg || IsAarch32UnconditionalBranch(opCode);
|
||||
}
|
||||
|
||||
private static bool IsAarch32UnconditionalBranch(OpCode opCode)
|
||||
{
|
||||
if (!(opCode is OpCode32 op))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Compare and branch instructions are always conditional.
|
||||
if (opCode.Instruction.Name == InstName.Cbz ||
|
||||
opCode.Instruction.Name == InstName.Cbnz)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Note: On ARM32, most instructions have conditional execution,
|
||||
// so there's no "Always" (unconditional) branch like on ARM64.
|
||||
// We need to check if the condition is "Always" instead.
|
||||
return IsAarch32Branch(op) && op.Cond >= Condition.Al;
|
||||
}
|
||||
|
||||
private static bool IsAarch32Branch(OpCode opCode)
|
||||
{
|
||||
// Note: On ARM32, most ALU operations can write to R15 (PC),
|
||||
// so we must consider such operations as a branch in potential aswell.
|
||||
if (opCode is IOpCode32Alu opAlu && opAlu.Rd == RegisterAlias.Aarch32Pc)
|
||||
{
|
||||
if (opCode is OpCodeT32)
|
||||
{
|
||||
return opCode.Instruction.Name != InstName.Tst && opCode.Instruction.Name != InstName.Teq &&
|
||||
opCode.Instruction.Name != InstName.Cmp && opCode.Instruction.Name != InstName.Cmn;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Same thing for memory operations. We have the cases where PC is a target
|
||||
// register (Rt == 15 or (mask & (1 << 15)) != 0), and cases where there is
|
||||
// a write back to PC (wback == true && Rn == 15), however the later may
|
||||
// be "undefined" depending on the CPU, so compilers should not produce that.
|
||||
if (opCode is IOpCode32Mem || opCode is IOpCode32MemMult)
|
||||
{
|
||||
int rt, rn;
|
||||
|
||||
bool wBack, isLoad;
|
||||
|
||||
if (opCode is IOpCode32Mem opMem)
|
||||
{
|
||||
rt = opMem.Rt;
|
||||
rn = opMem.Rn;
|
||||
wBack = opMem.WBack;
|
||||
isLoad = opMem.IsLoad;
|
||||
|
||||
// For the dual load, we also need to take into account the
|
||||
// case were Rt2 == 15 (PC).
|
||||
if (rt == 14 && opMem.Instruction.Name == InstName.Ldrd)
|
||||
{
|
||||
rt = RegisterAlias.Aarch32Pc;
|
||||
}
|
||||
}
|
||||
else if (opCode is IOpCode32MemMult opMemMult)
|
||||
{
|
||||
const int pcMask = 1 << RegisterAlias.Aarch32Pc;
|
||||
|
||||
rt = (opMemMult.RegisterMask & pcMask) != 0 ? RegisterAlias.Aarch32Pc : 0;
|
||||
rn = opMemMult.Rn;
|
||||
wBack = opMemMult.PostOffset != 0;
|
||||
isLoad = opMemMult.IsLoad;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new NotImplementedException($"The type \"{opCode.GetType().Name}\" is not implemented on the decoder.");
|
||||
}
|
||||
|
||||
if ((rt == RegisterAlias.Aarch32Pc && isLoad) ||
|
||||
(rn == RegisterAlias.Aarch32Pc && wBack))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Explicit branch instructions.
|
||||
return opCode is IOpCode32BImm ||
|
||||
opCode is IOpCode32BReg;
|
||||
}
|
||||
|
||||
private static bool IsCall(OpCode opCode)
|
||||
{
|
||||
return opCode.Instruction.Name == InstName.Bl ||
|
||||
opCode.Instruction.Name == InstName.Blr ||
|
||||
opCode.Instruction.Name == InstName.Blx;
|
||||
}
|
||||
|
||||
private static bool IsException(OpCode opCode)
|
||||
{
|
||||
return IsTrap(opCode) || opCode.Instruction.Name == InstName.Svc;
|
||||
}
|
||||
|
||||
private static bool IsTrap(OpCode opCode)
|
||||
{
|
||||
return opCode.Instruction.Name == InstName.Brk ||
|
||||
opCode.Instruction.Name == InstName.Trap ||
|
||||
opCode.Instruction.Name == InstName.Und;
|
||||
}
|
||||
|
||||
public static OpCode DecodeOpCode(IMemoryManager memory, ulong address, ExecutionMode mode)
|
||||
{
|
||||
int opCode = memory.Read<int>(address);
|
||||
|
||||
InstDescriptor inst;
|
||||
|
||||
OpCodeTable.MakeOp makeOp;
|
||||
|
||||
if (mode == ExecutionMode.Aarch64)
|
||||
{
|
||||
(inst, makeOp) = OpCodeTable.GetInstA64(opCode);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (mode == ExecutionMode.Aarch32Arm)
|
||||
{
|
||||
(inst, makeOp) = OpCodeTable.GetInstA32(opCode);
|
||||
}
|
||||
else /* if (mode == ExecutionMode.Aarch32Thumb) */
|
||||
{
|
||||
(inst, makeOp) = OpCodeTable.GetInstT32(opCode);
|
||||
}
|
||||
}
|
||||
|
||||
if (makeOp != null)
|
||||
{
|
||||
return makeOp(inst, address, opCode);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (mode == ExecutionMode.Aarch32Thumb)
|
||||
{
|
||||
return new OpCodeT16(inst, address, opCode);
|
||||
}
|
||||
else
|
||||
{
|
||||
return new OpCode(inst, address, opCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,167 +0,0 @@
|
||||
using ARMeilleure.Common;
|
||||
|
||||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
static class DecoderHelper
|
||||
{
|
||||
static DecoderHelper()
|
||||
{
|
||||
Imm8ToFP32Table = BuildImm8ToFP32Table();
|
||||
Imm8ToFP64Table = BuildImm8ToFP64Table();
|
||||
}
|
||||
|
||||
public static readonly uint[] Imm8ToFP32Table;
|
||||
public static readonly ulong[] Imm8ToFP64Table;
|
||||
|
||||
private static uint[] BuildImm8ToFP32Table()
|
||||
{
|
||||
uint[] tbl = new uint[256];
|
||||
|
||||
for (int idx = 0; idx < 256; idx++)
|
||||
{
|
||||
tbl[idx] = ExpandImm8ToFP32((uint)idx);
|
||||
}
|
||||
|
||||
return tbl;
|
||||
}
|
||||
|
||||
private static ulong[] BuildImm8ToFP64Table()
|
||||
{
|
||||
ulong[] tbl = new ulong[256];
|
||||
|
||||
for (int idx = 0; idx < 256; idx++)
|
||||
{
|
||||
tbl[idx] = ExpandImm8ToFP64((ulong)idx);
|
||||
}
|
||||
|
||||
return tbl;
|
||||
}
|
||||
|
||||
// abcdefgh -> aBbbbbbc defgh000 00000000 00000000 (B = ~b)
|
||||
private static uint ExpandImm8ToFP32(uint imm)
|
||||
{
|
||||
uint MoveBit(uint bits, int from, int to)
|
||||
{
|
||||
return ((bits >> from) & 1U) << to;
|
||||
}
|
||||
|
||||
return MoveBit(imm, 7, 31) | MoveBit(~imm, 6, 30) |
|
||||
MoveBit(imm, 6, 29) | MoveBit( imm, 6, 28) |
|
||||
MoveBit(imm, 6, 27) | MoveBit( imm, 6, 26) |
|
||||
MoveBit(imm, 6, 25) | MoveBit( imm, 5, 24) |
|
||||
MoveBit(imm, 4, 23) | MoveBit( imm, 3, 22) |
|
||||
MoveBit(imm, 2, 21) | MoveBit( imm, 1, 20) |
|
||||
MoveBit(imm, 0, 19);
|
||||
}
|
||||
|
||||
// abcdefgh -> aBbbbbbb bbcdefgh 00000000 00000000 00000000 00000000 00000000 00000000 (B = ~b)
|
||||
private static ulong ExpandImm8ToFP64(ulong imm)
|
||||
{
|
||||
ulong MoveBit(ulong bits, int from, int to)
|
||||
{
|
||||
return ((bits >> from) & 1UL) << to;
|
||||
}
|
||||
|
||||
return MoveBit(imm, 7, 63) | MoveBit(~imm, 6, 62) |
|
||||
MoveBit(imm, 6, 61) | MoveBit( imm, 6, 60) |
|
||||
MoveBit(imm, 6, 59) | MoveBit( imm, 6, 58) |
|
||||
MoveBit(imm, 6, 57) | MoveBit( imm, 6, 56) |
|
||||
MoveBit(imm, 6, 55) | MoveBit( imm, 6, 54) |
|
||||
MoveBit(imm, 5, 53) | MoveBit( imm, 4, 52) |
|
||||
MoveBit(imm, 3, 51) | MoveBit( imm, 2, 50) |
|
||||
MoveBit(imm, 1, 49) | MoveBit( imm, 0, 48);
|
||||
}
|
||||
|
||||
public struct BitMask
|
||||
{
|
||||
public long WMask;
|
||||
public long TMask;
|
||||
public int Pos;
|
||||
public int Shift;
|
||||
public bool IsUndefined;
|
||||
|
||||
public static BitMask Invalid => new BitMask { IsUndefined = true };
|
||||
}
|
||||
|
||||
public static BitMask DecodeBitMask(int opCode, bool immediate)
|
||||
{
|
||||
int immS = (opCode >> 10) & 0x3f;
|
||||
int immR = (opCode >> 16) & 0x3f;
|
||||
|
||||
int n = (opCode >> 22) & 1;
|
||||
int sf = (opCode >> 31) & 1;
|
||||
|
||||
int length = BitUtils.HighestBitSet((~immS & 0x3f) | (n << 6));
|
||||
|
||||
if (length < 1 || (sf == 0 && n != 0))
|
||||
{
|
||||
return BitMask.Invalid;
|
||||
}
|
||||
|
||||
int size = 1 << length;
|
||||
|
||||
int levels = size - 1;
|
||||
|
||||
int s = immS & levels;
|
||||
int r = immR & levels;
|
||||
|
||||
if (immediate && s == levels)
|
||||
{
|
||||
return BitMask.Invalid;
|
||||
}
|
||||
|
||||
long wMask = BitUtils.FillWithOnes(s + 1);
|
||||
long tMask = BitUtils.FillWithOnes(((s - r) & levels) + 1);
|
||||
|
||||
if (r > 0)
|
||||
{
|
||||
wMask = BitUtils.RotateRight(wMask, r, size);
|
||||
wMask &= BitUtils.FillWithOnes(size);
|
||||
}
|
||||
|
||||
return new BitMask()
|
||||
{
|
||||
WMask = BitUtils.Replicate(wMask, size),
|
||||
TMask = BitUtils.Replicate(tMask, size),
|
||||
|
||||
Pos = immS,
|
||||
Shift = immR
|
||||
};
|
||||
}
|
||||
|
||||
public static long DecodeImm24_2(int opCode)
|
||||
{
|
||||
return ((long)opCode << 40) >> 38;
|
||||
}
|
||||
|
||||
public static long DecodeImm26_2(int opCode)
|
||||
{
|
||||
return ((long)opCode << 38) >> 36;
|
||||
}
|
||||
|
||||
public static long DecodeImmS19_2(int opCode)
|
||||
{
|
||||
return (((long)opCode << 40) >> 43) & ~3;
|
||||
}
|
||||
|
||||
public static long DecodeImmS14_2(int opCode)
|
||||
{
|
||||
return (((long)opCode << 45) >> 48) & ~3;
|
||||
}
|
||||
|
||||
public static bool VectorArgumentsInvalid(bool q, params int[] args)
|
||||
{
|
||||
if (q)
|
||||
{
|
||||
for (int i = 0; i < args.Length; i++)
|
||||
{
|
||||
if ((args[i] & 1) == 1)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,6 +0,0 @@
|
||||
namespace ARMeilleure.Decoders;
|
||||
|
||||
interface IOpCode32Exception
|
||||
{
|
||||
int Id { get; }
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
interface IOpCodeLit : IOpCode
|
||||
{
|
||||
int Rt { get; }
|
||||
long Immediate { get; }
|
||||
int Size { get; }
|
||||
bool Signed { get; }
|
||||
bool Prefetch { get; }
|
||||
}
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
using ARMeilleure.Instructions;
|
||||
|
||||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
readonly struct InstDescriptor
|
||||
{
|
||||
public static InstDescriptor Undefined => new InstDescriptor(InstName.Und, InstEmit.Und);
|
||||
|
||||
public InstName Name { get; }
|
||||
public InstEmitter Emitter { get; }
|
||||
|
||||
public InstDescriptor(InstName name, InstEmitter emitter)
|
||||
{
|
||||
Name = name;
|
||||
Emitter = emitter;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,6 +0,0 @@
|
||||
using ARMeilleure.Translation;
|
||||
|
||||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
delegate void InstEmitter(ArmEmitterContext context);
|
||||
}
|
@ -1,14 +0,0 @@
|
||||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
enum IntType
|
||||
{
|
||||
UInt8 = 0,
|
||||
UInt16 = 1,
|
||||
UInt32 = 2,
|
||||
UInt64 = 3,
|
||||
Int8 = 4,
|
||||
Int16 = 5,
|
||||
Int32 = 6,
|
||||
Int64 = 7
|
||||
}
|
||||
}
|
@ -1,49 +0,0 @@
|
||||
using ARMeilleure.IntermediateRepresentation;
|
||||
using System;
|
||||
|
||||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class OpCode : IOpCode
|
||||
{
|
||||
public ulong Address { get; }
|
||||
public int RawOpCode { get; }
|
||||
|
||||
public int OpCodeSizeInBytes { get; protected set; } = 4;
|
||||
|
||||
public InstDescriptor Instruction { get; protected set; }
|
||||
|
||||
public RegisterSize RegisterSize { get; protected set; }
|
||||
|
||||
public static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCode(inst, address, opCode);
|
||||
|
||||
public OpCode(InstDescriptor inst, ulong address, int opCode)
|
||||
{
|
||||
Instruction = inst;
|
||||
Address = address;
|
||||
RawOpCode = opCode;
|
||||
|
||||
RegisterSize = RegisterSize.Int64;
|
||||
}
|
||||
|
||||
public int GetPairsCount() => GetBitsCount() / 16;
|
||||
public int GetBytesCount() => GetBitsCount() / 8;
|
||||
|
||||
public int GetBitsCount()
|
||||
{
|
||||
switch (RegisterSize)
|
||||
{
|
||||
case RegisterSize.Int32: return 32;
|
||||
case RegisterSize.Int64: return 64;
|
||||
case RegisterSize.Simd64: return 64;
|
||||
case RegisterSize.Simd128: return 128;
|
||||
}
|
||||
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
public OperandType GetOperandType()
|
||||
{
|
||||
return RegisterSize == RegisterSize.Int32 ? OperandType.I32 : OperandType.I64;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,39 +0,0 @@
|
||||
using ARMeilleure.Instructions;
|
||||
|
||||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class OpCode32Mem : OpCode32, IOpCode32Mem
|
||||
{
|
||||
public int Rt { get; protected set; }
|
||||
public int Rn { get; }
|
||||
|
||||
public int Immediate { get; protected set; }
|
||||
|
||||
public bool Index { get; }
|
||||
public bool Add { get; }
|
||||
public bool WBack { get; }
|
||||
public bool Unprivileged { get; }
|
||||
|
||||
public bool IsLoad { get; }
|
||||
|
||||
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCode32Mem(inst, address, opCode);
|
||||
|
||||
public OpCode32Mem(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
{
|
||||
Rt = (opCode >> 12) & 0xf;
|
||||
Rn = (opCode >> 16) & 0xf;
|
||||
|
||||
bool isLoad = (opCode & (1 << 20)) != 0;
|
||||
bool w = (opCode & (1 << 21)) != 0;
|
||||
bool u = (opCode & (1 << 23)) != 0;
|
||||
bool p = (opCode & (1 << 24)) != 0;
|
||||
|
||||
Index = p;
|
||||
Add = u;
|
||||
WBack = !p || w;
|
||||
Unprivileged = !p && w;
|
||||
|
||||
IsLoad = isLoad || inst.Name == InstName.Ldrd;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,19 +0,0 @@
|
||||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class OpCodeAdr : OpCode
|
||||
{
|
||||
public int Rd { get; }
|
||||
|
||||
public long Immediate { get; }
|
||||
|
||||
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCodeAdr(inst, address, opCode);
|
||||
|
||||
public OpCodeAdr(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
{
|
||||
Rd = opCode & 0x1f;
|
||||
|
||||
Immediate = DecoderHelper.DecodeImmS19_2(opCode);
|
||||
Immediate |= ((long)opCode >> 29) & 3;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,19 +0,0 @@
|
||||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class OpCodeMem : OpCode
|
||||
{
|
||||
public int Rt { get; protected set; }
|
||||
public int Rn { get; protected set; }
|
||||
public int Size { get; protected set; }
|
||||
public bool Extend64 { get; protected set; }
|
||||
|
||||
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCodeMem(inst, address, opCode);
|
||||
|
||||
public OpCodeMem(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
{
|
||||
Rt = (opCode >> 0) & 0x1f;
|
||||
Rn = (opCode >> 5) & 0x1f;
|
||||
Size = (opCode >> 30) & 0x3;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class OpCodeMemLit : OpCode, IOpCodeLit
|
||||
{
|
||||
public int Rt { get; }
|
||||
public long Immediate { get; }
|
||||
public int Size { get; }
|
||||
public bool Signed { get; }
|
||||
public bool Prefetch { get; }
|
||||
|
||||
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCodeMemLit(inst, address, opCode);
|
||||
|
||||
public OpCodeMemLit(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
{
|
||||
Rt = opCode & 0x1f;
|
||||
|
||||
Immediate = (long)address + DecoderHelper.DecodeImmS19_2(opCode);
|
||||
|
||||
switch ((opCode >> 30) & 3)
|
||||
{
|
||||
case 0: Size = 2; Signed = false; Prefetch = false; break;
|
||||
case 1: Size = 3; Signed = false; Prefetch = false; break;
|
||||
case 2: Size = 2; Signed = true; Prefetch = false; break;
|
||||
case 3: Size = 0; Signed = false; Prefetch = true; break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class OpCodeMemPair : OpCodeMemImm
|
||||
{
|
||||
public int Rt2 { get; }
|
||||
|
||||
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCodeMemPair(inst, address, opCode);
|
||||
|
||||
public OpCodeMemPair(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
{
|
||||
Rt2 = (opCode >> 10) & 0x1f;
|
||||
WBack = ((opCode >> 23) & 0x1) != 0;
|
||||
PostIdx = ((opCode >> 23) & 0x3) == 1;
|
||||
Extend64 = ((opCode >> 30) & 0x3) == 1;
|
||||
Size = ((opCode >> 31) & 0x1) | 2;
|
||||
|
||||
DecodeImm(opCode);
|
||||
}
|
||||
|
||||
protected void DecodeImm(int opCode)
|
||||
{
|
||||
Immediate = ((long)(opCode >> 15) << 57) >> (57 - Size);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,20 +0,0 @@
|
||||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class OpCodeMemReg : OpCodeMem
|
||||
{
|
||||
public bool Shift { get; }
|
||||
public int Rm { get; }
|
||||
|
||||
public IntType IntType { get; }
|
||||
|
||||
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCodeMemReg(inst, address, opCode);
|
||||
|
||||
public OpCodeMemReg(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
{
|
||||
Shift = ((opCode >> 12) & 0x1) != 0;
|
||||
IntType = (IntType)((opCode >> 13) & 0x7);
|
||||
Rm = (opCode >> 16) & 0x1f;
|
||||
Extend64 = ((opCode >> 22) & 0x3) == 2;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,24 +0,0 @@
|
||||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class OpCodeSimd : OpCode, IOpCodeSimd
|
||||
{
|
||||
public int Rd { get; }
|
||||
public int Rn { get; }
|
||||
public int Opc { get; }
|
||||
public int Size { get; protected set; }
|
||||
|
||||
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCodeSimd(inst, address, opCode);
|
||||
|
||||
public OpCodeSimd(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
{
|
||||
Rd = (opCode >> 0) & 0x1f;
|
||||
Rn = (opCode >> 5) & 0x1f;
|
||||
Opc = (opCode >> 15) & 0x3;
|
||||
Size = (opCode >> 22) & 0x3;
|
||||
|
||||
RegisterSize = ((opCode >> 30) & 1) != 0
|
||||
? RegisterSize.Simd128
|
||||
: RegisterSize.Simd64;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,36 +0,0 @@
|
||||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class OpCodeSimdIns : OpCodeSimd
|
||||
{
|
||||
public int SrcIndex { get; }
|
||||
public int DstIndex { get; }
|
||||
|
||||
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCodeSimdIns(inst, address, opCode);
|
||||
|
||||
public OpCodeSimdIns(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
{
|
||||
int imm4 = (opCode >> 11) & 0xf;
|
||||
int imm5 = (opCode >> 16) & 0x1f;
|
||||
|
||||
if (imm5 == 0b10000)
|
||||
{
|
||||
Instruction = InstDescriptor.Undefined;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Size = imm5 & -imm5;
|
||||
|
||||
switch (Size)
|
||||
{
|
||||
case 1: Size = 0; break;
|
||||
case 2: Size = 1; break;
|
||||
case 4: Size = 2; break;
|
||||
case 8: Size = 3; break;
|
||||
}
|
||||
|
||||
SrcIndex = imm4 >> Size;
|
||||
DstIndex = imm5 >> (Size + 1);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,48 +0,0 @@
|
||||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class OpCodeSimdMemMs : OpCodeMemReg, IOpCodeSimd
|
||||
{
|
||||
public int Reps { get; }
|
||||
public int SElems { get; }
|
||||
public int Elems { get; }
|
||||
public bool WBack { get; }
|
||||
|
||||
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCodeSimdMemMs(inst, address, opCode);
|
||||
|
||||
public OpCodeSimdMemMs(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
{
|
||||
switch ((opCode >> 12) & 0xf)
|
||||
{
|
||||
case 0b0000: Reps = 1; SElems = 4; break;
|
||||
case 0b0010: Reps = 4; SElems = 1; break;
|
||||
case 0b0100: Reps = 1; SElems = 3; break;
|
||||
case 0b0110: Reps = 3; SElems = 1; break;
|
||||
case 0b0111: Reps = 1; SElems = 1; break;
|
||||
case 0b1000: Reps = 1; SElems = 2; break;
|
||||
case 0b1010: Reps = 2; SElems = 1; break;
|
||||
|
||||
default: Instruction = InstDescriptor.Undefined; return;
|
||||
}
|
||||
|
||||
Size = (opCode >> 10) & 3;
|
||||
WBack = ((opCode >> 23) & 1) != 0;
|
||||
|
||||
bool q = ((opCode >> 30) & 1) != 0;
|
||||
|
||||
if (!q && Size == 3 && SElems != 1)
|
||||
{
|
||||
Instruction = InstDescriptor.Undefined;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Extend64 = false;
|
||||
|
||||
RegisterSize = q
|
||||
? RegisterSize.Simd128
|
||||
: RegisterSize.Simd64;
|
||||
|
||||
Elems = (GetBitsCount() >> 3) >> Size;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,97 +0,0 @@
|
||||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class OpCodeSimdMemSs : OpCodeMemReg, IOpCodeSimd
|
||||
{
|
||||
public int SElems { get; }
|
||||
public int Index { get; }
|
||||
public bool Replicate { get; }
|
||||
public bool WBack { get; }
|
||||
|
||||
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCodeSimdMemSs(inst, address, opCode);
|
||||
|
||||
public OpCodeSimdMemSs(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
{
|
||||
int size = (opCode >> 10) & 3;
|
||||
int s = (opCode >> 12) & 1;
|
||||
int sElems = (opCode >> 12) & 2;
|
||||
int scale = (opCode >> 14) & 3;
|
||||
int l = (opCode >> 22) & 1;
|
||||
int q = (opCode >> 30) & 1;
|
||||
|
||||
sElems |= (opCode >> 21) & 1;
|
||||
|
||||
sElems++;
|
||||
|
||||
int index = (q << 3) | (s << 2) | size;
|
||||
|
||||
switch (scale)
|
||||
{
|
||||
case 1:
|
||||
{
|
||||
if ((size & 1) != 0)
|
||||
{
|
||||
Instruction = InstDescriptor.Undefined;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
index >>= 1;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 2:
|
||||
{
|
||||
if ((size & 2) != 0 ||
|
||||
((size & 1) != 0 && s != 0))
|
||||
{
|
||||
Instruction = InstDescriptor.Undefined;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ((size & 1) != 0)
|
||||
{
|
||||
index >>= 3;
|
||||
|
||||
scale = 3;
|
||||
}
|
||||
else
|
||||
{
|
||||
index >>= 2;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 3:
|
||||
{
|
||||
if (l == 0 || s != 0)
|
||||
{
|
||||
Instruction = InstDescriptor.Undefined;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
scale = size;
|
||||
|
||||
Replicate = true;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Index = index;
|
||||
SElems = sElems;
|
||||
Size = scale;
|
||||
|
||||
Extend64 = false;
|
||||
|
||||
WBack = ((opCode >> 23) & 1) != 0;
|
||||
|
||||
RegisterSize = q != 0
|
||||
? RegisterSize.Simd128
|
||||
: RegisterSize.Simd64;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class OpCodeSimdReg : OpCodeSimd
|
||||
{
|
||||
public bool Bit3 { get; }
|
||||
public int Ra { get; }
|
||||
public int Rm { get; protected set; }
|
||||
|
||||
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCodeSimdReg(inst, address, opCode);
|
||||
|
||||
public OpCodeSimdReg(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
{
|
||||
Bit3 = ((opCode >> 3) & 0x1) != 0;
|
||||
Ra = (opCode >> 10) & 0x1f;
|
||||
Rm = (opCode >> 16) & 0x1f;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,24 +0,0 @@
|
||||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class OpCodeSystem : OpCode
|
||||
{
|
||||
public int Rt { get; }
|
||||
public int Op2 { get; }
|
||||
public int CRm { get; }
|
||||
public int CRn { get; }
|
||||
public int Op1 { get; }
|
||||
public int Op0 { get; }
|
||||
|
||||
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCodeSystem(inst, address, opCode);
|
||||
|
||||
public OpCodeSystem(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
{
|
||||
Rt = (opCode >> 0) & 0x1f;
|
||||
Op2 = (opCode >> 5) & 0x7;
|
||||
CRm = (opCode >> 8) & 0xf;
|
||||
CRn = (opCode >> 12) & 0xf;
|
||||
Op1 = (opCode >> 16) & 0x7;
|
||||
Op0 = ((opCode >> 19) & 0x1) | 2;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,58 +0,0 @@
|
||||
using ARMeilleure.Instructions;
|
||||
using System;
|
||||
|
||||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class OpCodeT16MemImm5 : OpCodeT16, IOpCode32Mem
|
||||
{
|
||||
public int Rt { get; }
|
||||
public int Rn { get; }
|
||||
|
||||
public bool WBack => false;
|
||||
public bool IsLoad { get; }
|
||||
public bool Index => true;
|
||||
public bool Add => true;
|
||||
|
||||
public int Immediate { get; }
|
||||
|
||||
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCodeT16MemImm5(inst, address, opCode);
|
||||
|
||||
public OpCodeT16MemImm5(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
{
|
||||
Rt = (opCode >> 0) & 7;
|
||||
Rn = (opCode >> 3) & 7;
|
||||
|
||||
switch (inst.Name)
|
||||
{
|
||||
case InstName.Ldr:
|
||||
case InstName.Ldrb:
|
||||
case InstName.Ldrh:
|
||||
IsLoad = true;
|
||||
break;
|
||||
case InstName.Str:
|
||||
case InstName.Strb:
|
||||
case InstName.Strh:
|
||||
IsLoad = false;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (inst.Name)
|
||||
{
|
||||
case InstName.Str:
|
||||
case InstName.Ldr:
|
||||
Immediate = ((opCode >> 6) & 0x1f) << 2;
|
||||
break;
|
||||
case InstName.Strb:
|
||||
case InstName.Ldrb:
|
||||
Immediate = ((opCode >> 6) & 0x1f);
|
||||
break;
|
||||
case InstName.Strh:
|
||||
case InstName.Ldrh:
|
||||
Immediate = ((opCode >> 6) & 0x1f) << 1;
|
||||
break;
|
||||
default:
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,56 +0,0 @@
|
||||
using ARMeilleure.Translation;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace ARMeilleure.Diagnostics
|
||||
{
|
||||
static class Logger
|
||||
{
|
||||
private static long _startTime;
|
||||
|
||||
private static long[] _accumulatedTime;
|
||||
|
||||
static Logger()
|
||||
{
|
||||
_accumulatedTime = new long[(int)PassName.Count];
|
||||
}
|
||||
|
||||
[Conditional("M_DEBUG")]
|
||||
public static void StartPass(PassName name)
|
||||
{
|
||||
WriteOutput(name + " pass started...");
|
||||
|
||||
_startTime = Stopwatch.GetTimestamp();
|
||||
}
|
||||
|
||||
[Conditional("M_DEBUG")]
|
||||
public static void EndPass(PassName name, ControlFlowGraph cfg)
|
||||
{
|
||||
EndPass(name);
|
||||
|
||||
WriteOutput("IR after " + name + " pass:");
|
||||
|
||||
WriteOutput(IRDumper.GetDump(cfg));
|
||||
}
|
||||
|
||||
[Conditional("M_DEBUG")]
|
||||
public static void EndPass(PassName name)
|
||||
{
|
||||
long elapsedTime = Stopwatch.GetTimestamp() - _startTime;
|
||||
|
||||
_accumulatedTime[(int)name] += elapsedTime;
|
||||
|
||||
WriteOutput($"{name} pass ended after {GetMilliseconds(_accumulatedTime[(int)name])} ms...");
|
||||
}
|
||||
|
||||
private static long GetMilliseconds(long ticks)
|
||||
{
|
||||
return (long)(((double)ticks / Stopwatch.Frequency) * 1000);
|
||||
}
|
||||
|
||||
private static void WriteOutput(string text)
|
||||
{
|
||||
Console.WriteLine(text);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,613 +0,0 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,264 +0,0 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,184 +0,0 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,648 +0,0 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,41 +0,0 @@
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
@ -1,215 +0,0 @@
|
||||
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: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetTpidrEl0)); break;
|
||||
case 0b11_011_1101_0000_011: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetTpidrroEl0)); break;
|
||||
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;
|
||||
|
||||
MethodInfo info;
|
||||
|
||||
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: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetTpidrEl0)); break;
|
||||
|
||||
default: throw new NotImplementedException($"Unknown MSR 0x{op.RawOpCode:X8} at 0x{op.Address:X16}.");
|
||||
}
|
||||
|
||||
context.Call(info, GetIntOrZR(context, op.Rt));
|
||||
}
|
||||
|
||||
public static void Nop(ArmEmitterContext context)
|
||||
{
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
public static void Sys(ArmEmitterContext context)
|
||||
{
|
||||
// This instruction is used to do some operations on the CPU like cache invalidation,
|
||||
// address translation and the like.
|
||||
// We treat it as no-op here since we don't have any cache being emulated anyway.
|
||||
OpCodeSystem op = (OpCodeSystem)context.CurrOp;
|
||||
|
||||
switch (GetPackedId(op))
|
||||
{
|
||||
case 0b11_011_0111_0100_001:
|
||||
{
|
||||
// DC ZVA
|
||||
Operand t = GetIntOrZR(context, op.Rt);
|
||||
|
||||
for (long offset = 0; offset < 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 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)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,685 +0,0 @@
|
||||
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,
|
||||
}
|
||||
}
|
@ -1,159 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace ARMeilleure.IntermediateRepresentation
|
||||
{
|
||||
class BasicBlock : IEquatable<BasicBlock>, IIntrusiveListNode<BasicBlock>
|
||||
{
|
||||
private const uint MaxSuccessors = 2;
|
||||
|
||||
private int _succCount;
|
||||
private BasicBlock _succ0;
|
||||
private BasicBlock _succ1;
|
||||
private HashSet<BasicBlock> _domFrontiers;
|
||||
|
||||
public int Index { get; set; }
|
||||
public BasicBlockFrequency Frequency { get; set; }
|
||||
public BasicBlock ListPrevious { get; set; }
|
||||
public BasicBlock ListNext { get; set; }
|
||||
public IntrusiveList<Operation> Operations { get; }
|
||||
public List<BasicBlock> Predecessors { get; }
|
||||
public BasicBlock ImmediateDominator { get; set; }
|
||||
|
||||
public int SuccessorsCount => _succCount;
|
||||
|
||||
public HashSet<BasicBlock> DominanceFrontiers
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_domFrontiers == null)
|
||||
{
|
||||
_domFrontiers = new HashSet<BasicBlock>();
|
||||
}
|
||||
|
||||
return _domFrontiers;
|
||||
}
|
||||
}
|
||||
|
||||
public BasicBlock() : this(index: -1) { }
|
||||
|
||||
public BasicBlock(int index)
|
||||
{
|
||||
Operations = new IntrusiveList<Operation>();
|
||||
Predecessors = new List<BasicBlock>();
|
||||
|
||||
Index = index;
|
||||
}
|
||||
|
||||
public void AddSuccessor(BasicBlock block)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(block);
|
||||
|
||||
if ((uint)_succCount + 1 > MaxSuccessors)
|
||||
{
|
||||
ThrowSuccessorOverflow();
|
||||
}
|
||||
|
||||
block.Predecessors.Add(this);
|
||||
|
||||
GetSuccessorUnsafe(_succCount++) = block;
|
||||
}
|
||||
|
||||
public void RemoveSuccessor(int index)
|
||||
{
|
||||
if ((uint)index >= (uint)_succCount)
|
||||
{
|
||||
ThrowOutOfRange(nameof(index));
|
||||
}
|
||||
|
||||
ref BasicBlock oldBlock = ref GetSuccessorUnsafe(index);
|
||||
|
||||
oldBlock.Predecessors.Remove(this);
|
||||
oldBlock = null;
|
||||
|
||||
if (index == 0)
|
||||
{
|
||||
_succ0 = _succ1;
|
||||
}
|
||||
|
||||
_succCount--;
|
||||
}
|
||||
|
||||
public BasicBlock GetSuccessor(int index)
|
||||
{
|
||||
if ((uint)index >= (uint)_succCount)
|
||||
{
|
||||
ThrowOutOfRange(nameof(index));
|
||||
}
|
||||
|
||||
return GetSuccessorUnsafe(index);
|
||||
}
|
||||
|
||||
private ref BasicBlock GetSuccessorUnsafe(int index)
|
||||
{
|
||||
return ref Unsafe.Add(ref _succ0, index);
|
||||
}
|
||||
|
||||
public void SetSuccessor(int index, BasicBlock block)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(block);
|
||||
|
||||
if ((uint)index >= (uint)_succCount)
|
||||
{
|
||||
ThrowOutOfRange(nameof(index));
|
||||
}
|
||||
|
||||
ref BasicBlock oldBlock = ref GetSuccessorUnsafe(index);
|
||||
|
||||
oldBlock.Predecessors.Remove(this);
|
||||
block.Predecessors.Add(this);
|
||||
|
||||
oldBlock = block;
|
||||
}
|
||||
|
||||
public void Append(Operation node)
|
||||
{
|
||||
Operation last = Operations.Last;
|
||||
|
||||
// Append node before terminal or to end if no terminal.
|
||||
if (last == default)
|
||||
{
|
||||
Operations.AddLast(node);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
switch (last.Instruction)
|
||||
{
|
||||
case Instruction.Return:
|
||||
case Instruction.Tailcall:
|
||||
case Instruction.BranchIf:
|
||||
Operations.AddBefore(last, node);
|
||||
break;
|
||||
|
||||
default:
|
||||
Operations.AddLast(node);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private static void ThrowOutOfRange(string name) => throw new ArgumentOutOfRangeException(name);
|
||||
private static void ThrowSuccessorOverflow() => throw new OverflowException($"BasicBlock can only have {MaxSuccessors} successors.");
|
||||
|
||||
public bool Equals(BasicBlock other)
|
||||
{
|
||||
return other == this;
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return Equals(obj as BasicBlock);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return base.GetHashCode();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,24 +0,0 @@
|
||||
namespace ARMeilleure.IntermediateRepresentation
|
||||
{
|
||||
enum Comparison
|
||||
{
|
||||
Equal = 0,
|
||||
NotEqual = 1,
|
||||
Greater = 2,
|
||||
LessOrEqual = 3,
|
||||
GreaterUI = 4,
|
||||
LessOrEqualUI = 5,
|
||||
GreaterOrEqual = 6,
|
||||
Less = 7,
|
||||
GreaterOrEqualUI = 8,
|
||||
LessUI = 9
|
||||
}
|
||||
|
||||
static class ComparisonExtensions
|
||||
{
|
||||
public static Comparison Invert(this Comparison comp)
|
||||
{
|
||||
return (Comparison)((int)comp ^ 1);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,72 +0,0 @@
|
||||
namespace ARMeilleure.IntermediateRepresentation
|
||||
{
|
||||
enum Instruction : ushort
|
||||
{
|
||||
Add,
|
||||
BitwiseAnd,
|
||||
BitwiseExclusiveOr,
|
||||
BitwiseNot,
|
||||
BitwiseOr,
|
||||
BranchIf,
|
||||
ByteSwap,
|
||||
Call,
|
||||
Compare,
|
||||
CompareAndSwap,
|
||||
CompareAndSwap16,
|
||||
CompareAndSwap8,
|
||||
ConditionalSelect,
|
||||
ConvertI64ToI32,
|
||||
ConvertToFP,
|
||||
ConvertToFPUI,
|
||||
Copy,
|
||||
CountLeadingZeros,
|
||||
Divide,
|
||||
DivideUI,
|
||||
Load,
|
||||
Load16,
|
||||
Load8,
|
||||
LoadArgument,
|
||||
MemoryBarrier,
|
||||
Multiply,
|
||||
Multiply64HighSI,
|
||||
Multiply64HighUI,
|
||||
Negate,
|
||||
Return,
|
||||
RotateRight,
|
||||
ShiftLeft,
|
||||
ShiftRightSI,
|
||||
ShiftRightUI,
|
||||
SignExtend16,
|
||||
SignExtend32,
|
||||
SignExtend8,
|
||||
StackAlloc,
|
||||
Store,
|
||||
Store16,
|
||||
Store8,
|
||||
Subtract,
|
||||
Tailcall,
|
||||
VectorCreateScalar,
|
||||
VectorExtract,
|
||||
VectorExtract16,
|
||||
VectorExtract8,
|
||||
VectorInsert,
|
||||
VectorInsert16,
|
||||
VectorInsert8,
|
||||
VectorOne,
|
||||
VectorZero,
|
||||
VectorZeroUpper64,
|
||||
VectorZeroUpper96,
|
||||
ZeroExtend16,
|
||||
ZeroExtend32,
|
||||
ZeroExtend8,
|
||||
|
||||
Clobber,
|
||||
Extended,
|
||||
Fill,
|
||||
LoadFromContext,
|
||||
Phi,
|
||||
Spill,
|
||||
SpillArg,
|
||||
StoreToContext
|
||||
}
|
||||
}
|
@ -1,594 +0,0 @@
|
||||
using ARMeilleure.CodeGen.Linking;
|
||||
using ARMeilleure.Common;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace ARMeilleure.IntermediateRepresentation
|
||||
{
|
||||
unsafe struct Operand : IEquatable<Operand>
|
||||
{
|
||||
internal struct Data
|
||||
{
|
||||
public byte Kind;
|
||||
public byte Type;
|
||||
public byte SymbolType;
|
||||
public byte Padding; // Unused space.
|
||||
public ushort AssignmentsCount;
|
||||
public ushort AssignmentsCapacity;
|
||||
public uint UsesCount;
|
||||
public uint UsesCapacity;
|
||||
public Operation* Assignments;
|
||||
public Operation* Uses;
|
||||
public ulong Value;
|
||||
public ulong SymbolValue;
|
||||
}
|
||||
|
||||
private Data* _data;
|
||||
|
||||
public OperandKind Kind
|
||||
{
|
||||
get => (OperandKind)_data->Kind;
|
||||
private set => _data->Kind = (byte)value;
|
||||
}
|
||||
|
||||
public OperandType Type
|
||||
{
|
||||
get => (OperandType)_data->Type;
|
||||
private set => _data->Type = (byte)value;
|
||||
}
|
||||
|
||||
public ulong Value
|
||||
{
|
||||
get => _data->Value;
|
||||
private set => _data->Value = value;
|
||||
}
|
||||
|
||||
public Symbol Symbol
|
||||
{
|
||||
get
|
||||
{
|
||||
Debug.Assert(Kind != OperandKind.Memory);
|
||||
|
||||
return new Symbol((SymbolType)_data->SymbolType, _data->SymbolValue);
|
||||
}
|
||||
private set
|
||||
{
|
||||
Debug.Assert(Kind != OperandKind.Memory);
|
||||
|
||||
if (value.Type == SymbolType.None)
|
||||
{
|
||||
_data->SymbolType = (byte)SymbolType.None;
|
||||
}
|
||||
else
|
||||
{
|
||||
_data->SymbolType = (byte)value.Type;
|
||||
_data->SymbolValue = value.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public ReadOnlySpan<Operation> Assignments
|
||||
{
|
||||
get
|
||||
{
|
||||
Debug.Assert(Kind != OperandKind.Memory);
|
||||
|
||||
return new ReadOnlySpan<Operation>(_data->Assignments, _data->AssignmentsCount);
|
||||
}
|
||||
}
|
||||
|
||||
public ReadOnlySpan<Operation> Uses
|
||||
{
|
||||
get
|
||||
{
|
||||
Debug.Assert(Kind != OperandKind.Memory);
|
||||
|
||||
return new ReadOnlySpan<Operation>(_data->Uses, (int)_data->UsesCount);
|
||||
}
|
||||
}
|
||||
|
||||
public int UsesCount => (int)_data->UsesCount;
|
||||
public int AssignmentsCount => _data->AssignmentsCount;
|
||||
|
||||
public bool Relocatable => Symbol.Type != SymbolType.None;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public Register GetRegister()
|
||||
{
|
||||
Debug.Assert(Kind == OperandKind.Register);
|
||||
|
||||
return new Register((int)Value & 0xffffff, (RegisterType)(Value >> 24));
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public MemoryOperand GetMemory()
|
||||
{
|
||||
Debug.Assert(Kind == OperandKind.Memory);
|
||||
|
||||
return new MemoryOperand(this);
|
||||
}
|
||||
|
||||
public int GetLocalNumber()
|
||||
{
|
||||
Debug.Assert(Kind == OperandKind.LocalVariable);
|
||||
|
||||
return (int)Value;
|
||||
}
|
||||
|
||||
public byte AsByte()
|
||||
{
|
||||
return (byte)Value;
|
||||
}
|
||||
|
||||
public short AsInt16()
|
||||
{
|
||||
return (short)Value;
|
||||
}
|
||||
|
||||
public int AsInt32()
|
||||
{
|
||||
return (int)Value;
|
||||
}
|
||||
|
||||
public long AsInt64()
|
||||
{
|
||||
return (long)Value;
|
||||
}
|
||||
|
||||
public float AsFloat()
|
||||
{
|
||||
return BitConverter.Int32BitsToSingle((int)Value);
|
||||
}
|
||||
|
||||
public double AsDouble()
|
||||
{
|
||||
return BitConverter.Int64BitsToDouble((long)Value);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal ref ulong GetValueUnsafe()
|
||||
{
|
||||
return ref _data->Value;
|
||||
}
|
||||
|
||||
internal void NumberLocal(int number)
|
||||
{
|
||||
if (Kind != OperandKind.LocalVariable)
|
||||
{
|
||||
throw new InvalidOperationException("The operand is not a local variable.");
|
||||
}
|
||||
|
||||
Value = (ulong)number;
|
||||
}
|
||||
|
||||
public void AddAssignment(Operation operation)
|
||||
{
|
||||
if (Kind == OperandKind.LocalVariable)
|
||||
{
|
||||
Add(operation, ref _data->Assignments, ref _data->AssignmentsCount, ref _data->AssignmentsCapacity);
|
||||
}
|
||||
else if (Kind == OperandKind.Memory)
|
||||
{
|
||||
MemoryOperand memOp = GetMemory();
|
||||
Operand addr = memOp.BaseAddress;
|
||||
Operand index = memOp.Index;
|
||||
|
||||
if (addr != default)
|
||||
{
|
||||
Add(operation, ref addr._data->Assignments, ref addr._data->AssignmentsCount, ref addr._data->AssignmentsCapacity);
|
||||
}
|
||||
|
||||
if (index != default)
|
||||
{
|
||||
Add(operation, ref index._data->Assignments, ref index._data->AssignmentsCount, ref index._data->AssignmentsCapacity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveAssignment(Operation operation)
|
||||
{
|
||||
if (Kind == OperandKind.LocalVariable)
|
||||
{
|
||||
Remove(operation, ref _data->Assignments, ref _data->AssignmentsCount);
|
||||
}
|
||||
else if (Kind == OperandKind.Memory)
|
||||
{
|
||||
MemoryOperand memOp = GetMemory();
|
||||
Operand addr = memOp.BaseAddress;
|
||||
Operand index = memOp.Index;
|
||||
|
||||
if (addr != default)
|
||||
{
|
||||
Remove(operation, ref addr._data->Assignments, ref addr._data->AssignmentsCount);
|
||||
}
|
||||
|
||||
if (index != default)
|
||||
{
|
||||
Remove(operation, ref index._data->Assignments, ref index._data->AssignmentsCount);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void AddUse(Operation operation)
|
||||
{
|
||||
if (Kind == OperandKind.LocalVariable)
|
||||
{
|
||||
Add(operation, ref _data->Uses, ref _data->UsesCount, ref _data->UsesCapacity);
|
||||
}
|
||||
else if (Kind == OperandKind.Memory)
|
||||
{
|
||||
MemoryOperand memOp = GetMemory();
|
||||
Operand addr = memOp.BaseAddress;
|
||||
Operand index = memOp.Index;
|
||||
|
||||
if (addr != default)
|
||||
{
|
||||
Add(operation, ref addr._data->Uses, ref addr._data->UsesCount, ref addr._data->UsesCapacity);
|
||||
}
|
||||
|
||||
if (index != default)
|
||||
{
|
||||
Add(operation, ref index._data->Uses, ref index._data->UsesCount, ref index._data->UsesCapacity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveUse(Operation operation)
|
||||
{
|
||||
if (Kind == OperandKind.LocalVariable)
|
||||
{
|
||||
Remove(operation, ref _data->Uses, ref _data->UsesCount);
|
||||
}
|
||||
else if (Kind == OperandKind.Memory)
|
||||
{
|
||||
MemoryOperand memOp = GetMemory();
|
||||
Operand addr = memOp.BaseAddress;
|
||||
Operand index = memOp.Index;
|
||||
|
||||
if (addr != default)
|
||||
{
|
||||
Remove(operation, ref addr._data->Uses, ref addr._data->UsesCount);
|
||||
}
|
||||
|
||||
if (index != default)
|
||||
{
|
||||
Remove(operation, ref index._data->Uses, ref index._data->UsesCount);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Span<Operation> GetUses(ref Span<Operation> buffer)
|
||||
{
|
||||
ReadOnlySpan<Operation> uses = Uses;
|
||||
|
||||
if (buffer.Length < uses.Length)
|
||||
{
|
||||
buffer = Allocators.Default.AllocateSpan<Operation>((uint)uses.Length);
|
||||
}
|
||||
|
||||
uses.CopyTo(buffer);
|
||||
|
||||
return buffer.Slice(0, uses.Length);
|
||||
}
|
||||
|
||||
private static void New<T>(ref T* data, ref ushort count, ref ushort capacity, ushort initialCapacity) where T : unmanaged
|
||||
{
|
||||
count = 0;
|
||||
capacity = initialCapacity;
|
||||
data = Allocators.References.Allocate<T>(initialCapacity);
|
||||
}
|
||||
|
||||
private static void New<T>(ref T* data, ref uint count, ref uint capacity, uint initialCapacity) where T : unmanaged
|
||||
{
|
||||
count = 0;
|
||||
capacity = initialCapacity;
|
||||
data = Allocators.References.Allocate<T>(initialCapacity);
|
||||
}
|
||||
|
||||
private static void Add<T>(T item, ref T* data, ref ushort count, ref ushort capacity) where T : unmanaged
|
||||
{
|
||||
if (count < capacity)
|
||||
{
|
||||
data[(uint)count++] = item;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Could not add item in the fast path, fallback onto the slow path.
|
||||
ExpandAdd(item, ref data, ref count, ref capacity);
|
||||
|
||||
static void ExpandAdd(T item, ref T* data, ref ushort count, ref ushort capacity)
|
||||
{
|
||||
ushort newCount = checked((ushort)(count + 1));
|
||||
ushort newCapacity = (ushort)Math.Min(capacity * 2, ushort.MaxValue);
|
||||
|
||||
var oldSpan = new Span<T>(data, count);
|
||||
|
||||
capacity = newCapacity;
|
||||
data = Allocators.References.Allocate<T>(capacity);
|
||||
|
||||
oldSpan.CopyTo(new Span<T>(data, count));
|
||||
|
||||
data[count] = item;
|
||||
count = newCount;
|
||||
}
|
||||
}
|
||||
|
||||
private static void Add<T>(T item, ref T* data, ref uint count, ref uint capacity) where T : unmanaged
|
||||
{
|
||||
if (count < capacity)
|
||||
{
|
||||
data[count++] = item;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Could not add item in the fast path, fallback onto the slow path.
|
||||
ExpandAdd(item, ref data, ref count, ref capacity);
|
||||
|
||||
static void ExpandAdd(T item, ref T* data, ref uint count, ref uint capacity)
|
||||
{
|
||||
uint newCount = checked(count + 1);
|
||||
uint newCapacity = (uint)Math.Min(capacity * 2, int.MaxValue);
|
||||
|
||||
if (newCapacity <= capacity)
|
||||
{
|
||||
throw new OverflowException();
|
||||
}
|
||||
|
||||
var oldSpan = new Span<T>(data, (int)count);
|
||||
|
||||
capacity = newCapacity;
|
||||
data = Allocators.References.Allocate<T>(capacity);
|
||||
|
||||
oldSpan.CopyTo(new Span<T>(data, (int)count));
|
||||
|
||||
data[count] = item;
|
||||
count = newCount;
|
||||
}
|
||||
}
|
||||
|
||||
private static void Remove<T>(in T item, ref T* data, ref ushort count) where T : unmanaged
|
||||
{
|
||||
var span = new Span<T>(data, count);
|
||||
|
||||
for (int i = 0; i < span.Length; i++)
|
||||
{
|
||||
if (EqualityComparer<T>.Default.Equals(span[i], item))
|
||||
{
|
||||
if (i + 1 < count)
|
||||
{
|
||||
span.Slice(i + 1).CopyTo(span.Slice(i));
|
||||
}
|
||||
|
||||
count--;
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void Remove<T>(in T item, ref T* data, ref uint count) where T : unmanaged
|
||||
{
|
||||
var span = new Span<T>(data, (int)count);
|
||||
|
||||
for (int i = 0; i < span.Length; i++)
|
||||
{
|
||||
if (EqualityComparer<T>.Default.Equals(span[i], item))
|
||||
{
|
||||
if (i + 1 < count)
|
||||
{
|
||||
span.Slice(i + 1).CopyTo(span.Slice(i));
|
||||
}
|
||||
|
||||
count--;
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return ((ulong)_data).GetHashCode();
|
||||
}
|
||||
|
||||
public bool Equals(Operand operand)
|
||||
{
|
||||
return operand._data == _data;
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return obj is Operand operand && Equals(operand);
|
||||
}
|
||||
|
||||
public static bool operator ==(Operand a, Operand b)
|
||||
{
|
||||
return a.Equals(b);
|
||||
}
|
||||
|
||||
public static bool operator !=(Operand a, Operand b)
|
||||
{
|
||||
return !a.Equals(b);
|
||||
}
|
||||
|
||||
public static class Factory
|
||||
{
|
||||
private const int InternTableSize = 256;
|
||||
private const int InternTableProbeLength = 8;
|
||||
|
||||
[ThreadStatic]
|
||||
private static Data* _internTable;
|
||||
|
||||
private static Data* InternTable
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_internTable == null)
|
||||
{
|
||||
_internTable = (Data*)NativeAllocator.Instance.Allocate((uint)sizeof(Data) * InternTableSize);
|
||||
|
||||
// Make sure the table is zeroed.
|
||||
new Span<Data>(_internTable, InternTableSize).Clear();
|
||||
}
|
||||
|
||||
return _internTable;
|
||||
}
|
||||
}
|
||||
|
||||
private static Operand Make(OperandKind kind, OperandType type, ulong value, Symbol symbol = default)
|
||||
{
|
||||
Debug.Assert(kind != OperandKind.None);
|
||||
|
||||
Data* data = null;
|
||||
|
||||
// If constant or register, then try to look up in the intern table before allocating.
|
||||
if (kind == OperandKind.Constant || kind == OperandKind.Register)
|
||||
{
|
||||
uint hash = (uint)HashCode.Combine(kind, type, value);
|
||||
|
||||
// Look in the next InternTableProbeLength slots for a match.
|
||||
for (uint i = 0; i < InternTableProbeLength; i++)
|
||||
{
|
||||
Operand interned = new();
|
||||
interned._data = &InternTable[(hash + i) % InternTableSize];
|
||||
|
||||
// If slot matches the allocation request then return that slot.
|
||||
if (interned.Kind == kind && interned.Type == type && interned.Value == value && interned.Symbol == symbol)
|
||||
{
|
||||
return interned;
|
||||
}
|
||||
// Otherwise if the slot is not occupied, we store in that slot.
|
||||
else if (interned.Kind == OperandKind.None)
|
||||
{
|
||||
data = interned._data;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If we could not get a slot from the intern table, we allocate somewhere else and store there.
|
||||
if (data == null)
|
||||
{
|
||||
data = Allocators.Operands.Allocate<Data>();
|
||||
}
|
||||
|
||||
*data = default;
|
||||
|
||||
Operand result = new();
|
||||
result._data = data;
|
||||
result.Value = value;
|
||||
result.Kind = kind;
|
||||
result.Type = type;
|
||||
|
||||
if (kind != OperandKind.Memory)
|
||||
{
|
||||
result.Symbol = symbol;
|
||||
}
|
||||
|
||||
// If local variable, then the use and def list is initialized with default sizes.
|
||||
if (kind == OperandKind.LocalVariable)
|
||||
{
|
||||
New(ref result._data->Assignments, ref result._data->AssignmentsCount, ref result._data->AssignmentsCapacity, 1);
|
||||
New(ref result._data->Uses, ref result._data->UsesCount, ref result._data->UsesCapacity, 4);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static Operand Const(OperandType type, long value)
|
||||
{
|
||||
Debug.Assert(type is OperandType.I32 or OperandType.I64);
|
||||
|
||||
return type == OperandType.I32 ? Const((int)value) : Const(value);
|
||||
}
|
||||
|
||||
public static Operand Const(bool value)
|
||||
{
|
||||
return Const(value ? 1 : 0);
|
||||
}
|
||||
|
||||
public static Operand Const(int value)
|
||||
{
|
||||
return Const((uint)value);
|
||||
}
|
||||
|
||||
public static Operand Const(uint value)
|
||||
{
|
||||
return Make(OperandKind.Constant, OperandType.I32, value);
|
||||
}
|
||||
|
||||
public static Operand Const(long value)
|
||||
{
|
||||
return Const(value, symbol: default);
|
||||
}
|
||||
|
||||
public static Operand Const<T>(ref T reference, Symbol symbol = default)
|
||||
{
|
||||
return Const((long)Unsafe.AsPointer(ref reference), symbol);
|
||||
}
|
||||
|
||||
public static Operand Const(long value, Symbol symbol)
|
||||
{
|
||||
return Make(OperandKind.Constant, OperandType.I64, (ulong)value, symbol);
|
||||
}
|
||||
|
||||
public static Operand Const(ulong value)
|
||||
{
|
||||
return Make(OperandKind.Constant, OperandType.I64, value);
|
||||
}
|
||||
|
||||
public static Operand ConstF(float value)
|
||||
{
|
||||
return Make(OperandKind.Constant, OperandType.FP32, (ulong)BitConverter.SingleToInt32Bits(value));
|
||||
}
|
||||
|
||||
public static Operand ConstF(double value)
|
||||
{
|
||||
return Make(OperandKind.Constant, OperandType.FP64, (ulong)BitConverter.DoubleToInt64Bits(value));
|
||||
}
|
||||
|
||||
public static Operand Label()
|
||||
{
|
||||
return Make(OperandKind.Label, OperandType.None, 0);
|
||||
}
|
||||
|
||||
public static Operand Local(OperandType type)
|
||||
{
|
||||
return Make(OperandKind.LocalVariable, type, 0);
|
||||
}
|
||||
|
||||
public static Operand Register(int index, RegisterType regType, OperandType type)
|
||||
{
|
||||
return Make(OperandKind.Register, type, (ulong)((int)regType << 24 | index));
|
||||
}
|
||||
|
||||
public static Operand Undef()
|
||||
{
|
||||
return Make(OperandKind.Undefined, OperandType.None, 0);
|
||||
}
|
||||
|
||||
public static Operand MemoryOp(
|
||||
OperandType type,
|
||||
Operand baseAddress,
|
||||
Operand index = default,
|
||||
Multiplier scale = Multiplier.x1,
|
||||
int displacement = 0)
|
||||
{
|
||||
Operand result = Make(OperandKind.Memory, type, 0);
|
||||
|
||||
MemoryOperand memory = result.GetMemory();
|
||||
memory.BaseAddress = baseAddress;
|
||||
memory.Index = index;
|
||||
memory.Scale = scale;
|
||||
memory.Displacement = displacement;
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,65 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace ARMeilleure.IntermediateRepresentation
|
||||
{
|
||||
enum OperandType
|
||||
{
|
||||
None,
|
||||
I32,
|
||||
I64,
|
||||
FP32,
|
||||
FP64,
|
||||
V128
|
||||
}
|
||||
|
||||
static class OperandTypeExtensions
|
||||
{
|
||||
public static bool IsInteger(this OperandType type)
|
||||
{
|
||||
return type == OperandType.I32 ||
|
||||
type == OperandType.I64;
|
||||
}
|
||||
|
||||
public static RegisterType ToRegisterType(this OperandType type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case OperandType.FP32: return RegisterType.Vector;
|
||||
case OperandType.FP64: return RegisterType.Vector;
|
||||
case OperandType.I32: return RegisterType.Integer;
|
||||
case OperandType.I64: return RegisterType.Integer;
|
||||
case OperandType.V128: return RegisterType.Vector;
|
||||
}
|
||||
|
||||
throw new InvalidOperationException($"Invalid operand type \"{type}\".");
|
||||
}
|
||||
|
||||
public static int GetSizeInBytes(this OperandType type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case OperandType.FP32: return 4;
|
||||
case OperandType.FP64: return 8;
|
||||
case OperandType.I32: return 4;
|
||||
case OperandType.I64: return 8;
|
||||
case OperandType.V128: return 16;
|
||||
}
|
||||
|
||||
throw new InvalidOperationException($"Invalid operand type \"{type}\".");
|
||||
}
|
||||
|
||||
public static int GetSizeInBytesLog2(this OperandType type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case OperandType.FP32: return 2;
|
||||
case OperandType.FP64: return 3;
|
||||
case OperandType.I32: return 2;
|
||||
case OperandType.I64: return 3;
|
||||
case OperandType.V128: return 4;
|
||||
}
|
||||
|
||||
throw new InvalidOperationException($"Invalid operand type \"{type}\".");
|
||||
}
|
||||
}
|
||||
}
|
@ -1,376 +0,0 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace ARMeilleure.IntermediateRepresentation
|
||||
{
|
||||
unsafe struct Operation : IEquatable<Operation>, IIntrusiveListNode<Operation>
|
||||
{
|
||||
internal struct Data
|
||||
{
|
||||
public ushort Instruction;
|
||||
public ushort Intrinsic;
|
||||
public ushort SourcesCount;
|
||||
public ushort DestinationsCount;
|
||||
public Operation ListPrevious;
|
||||
public Operation ListNext;
|
||||
public Operand* Destinations;
|
||||
public Operand* Sources;
|
||||
}
|
||||
|
||||
private Data* _data;
|
||||
|
||||
public Instruction Instruction
|
||||
{
|
||||
get => (Instruction)_data->Instruction;
|
||||
private set => _data->Instruction = (ushort)value;
|
||||
}
|
||||
|
||||
public Intrinsic Intrinsic
|
||||
{
|
||||
get => (Intrinsic)_data->Intrinsic;
|
||||
private set => _data->Intrinsic = (ushort)value;
|
||||
}
|
||||
|
||||
public Operation ListPrevious
|
||||
{
|
||||
get => _data->ListPrevious;
|
||||
set => _data->ListPrevious = value;
|
||||
}
|
||||
|
||||
public Operation ListNext
|
||||
{
|
||||
get => _data->ListNext;
|
||||
set => _data->ListNext = value;
|
||||
}
|
||||
|
||||
public Operand Destination
|
||||
{
|
||||
get => _data->DestinationsCount != 0 ? GetDestination(0) : default;
|
||||
set => SetDestination(value);
|
||||
}
|
||||
|
||||
public int DestinationsCount => _data->DestinationsCount;
|
||||
public int SourcesCount => _data->SourcesCount;
|
||||
|
||||
internal Span<Operand> DestinationsUnsafe => new(_data->Destinations, _data->DestinationsCount);
|
||||
internal Span<Operand> SourcesUnsafe => new(_data->Sources, _data->SourcesCount);
|
||||
|
||||
public PhiOperation AsPhi()
|
||||
{
|
||||
Debug.Assert(Instruction == Instruction.Phi);
|
||||
|
||||
return new PhiOperation(this);
|
||||
}
|
||||
|
||||
public Operand GetDestination(int index)
|
||||
{
|
||||
return DestinationsUnsafe[index];
|
||||
}
|
||||
|
||||
public Operand GetSource(int index)
|
||||
{
|
||||
return SourcesUnsafe[index];
|
||||
}
|
||||
|
||||
public void SetDestination(int index, Operand dest)
|
||||
{
|
||||
ref Operand curDest = ref DestinationsUnsafe[index];
|
||||
|
||||
RemoveAssignment(curDest);
|
||||
AddAssignment(dest);
|
||||
|
||||
curDest = dest;
|
||||
}
|
||||
|
||||
public void SetSource(int index, Operand src)
|
||||
{
|
||||
ref Operand curSrc = ref SourcesUnsafe[index];
|
||||
|
||||
RemoveUse(curSrc);
|
||||
AddUse(src);
|
||||
|
||||
curSrc = src;
|
||||
}
|
||||
|
||||
private void RemoveOldDestinations()
|
||||
{
|
||||
for (int i = 0; i < _data->DestinationsCount; i++)
|
||||
{
|
||||
RemoveAssignment(_data->Destinations[i]);
|
||||
}
|
||||
}
|
||||
|
||||
public void SetDestination(Operand dest)
|
||||
{
|
||||
RemoveOldDestinations();
|
||||
|
||||
if (dest == default)
|
||||
{
|
||||
_data->DestinationsCount = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
EnsureCapacity(ref _data->Destinations, ref _data->DestinationsCount, 1);
|
||||
|
||||
_data->Destinations[0] = dest;
|
||||
|
||||
AddAssignment(dest);
|
||||
}
|
||||
}
|
||||
|
||||
public void SetDestinations(Operand[] dests)
|
||||
{
|
||||
RemoveOldDestinations();
|
||||
|
||||
EnsureCapacity(ref _data->Destinations, ref _data->DestinationsCount, dests.Length);
|
||||
|
||||
for (int index = 0; index < dests.Length; index++)
|
||||
{
|
||||
Operand newOp = dests[index];
|
||||
|
||||
_data->Destinations[index] = newOp;
|
||||
|
||||
AddAssignment(newOp);
|
||||
}
|
||||
}
|
||||
|
||||
private void RemoveOldSources()
|
||||
{
|
||||
for (int index = 0; index < _data->SourcesCount; index++)
|
||||
{
|
||||
RemoveUse(_data->Sources[index]);
|
||||
}
|
||||
}
|
||||
|
||||
public void SetSource(Operand src)
|
||||
{
|
||||
RemoveOldSources();
|
||||
|
||||
if (src == default)
|
||||
{
|
||||
_data->SourcesCount = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
EnsureCapacity(ref _data->Sources, ref _data->SourcesCount, 1);
|
||||
|
||||
_data->Sources[0] = src;
|
||||
|
||||
AddUse(src);
|
||||
}
|
||||
}
|
||||
|
||||
public void SetSources(Operand[] srcs)
|
||||
{
|
||||
RemoveOldSources();
|
||||
|
||||
EnsureCapacity(ref _data->Sources, ref _data->SourcesCount, srcs.Length);
|
||||
|
||||
for (int index = 0; index < srcs.Length; index++)
|
||||
{
|
||||
Operand newOp = srcs[index];
|
||||
|
||||
_data->Sources[index] = newOp;
|
||||
|
||||
AddUse(newOp);
|
||||
}
|
||||
}
|
||||
|
||||
public void TurnIntoCopy(Operand source)
|
||||
{
|
||||
Instruction = Instruction.Copy;
|
||||
|
||||
SetSource(source);
|
||||
}
|
||||
|
||||
private void AddAssignment(Operand op)
|
||||
{
|
||||
if (op != default)
|
||||
{
|
||||
op.AddAssignment(this);
|
||||
}
|
||||
}
|
||||
|
||||
private void RemoveAssignment(Operand op)
|
||||
{
|
||||
if (op != default)
|
||||
{
|
||||
op.RemoveAssignment(this);
|
||||
}
|
||||
}
|
||||
|
||||
private void AddUse(Operand op)
|
||||
{
|
||||
if (op != default)
|
||||
{
|
||||
op.AddUse(this);
|
||||
}
|
||||
}
|
||||
|
||||
private void RemoveUse(Operand op)
|
||||
{
|
||||
if (op != default)
|
||||
{
|
||||
op.RemoveUse(this);
|
||||
}
|
||||
}
|
||||
|
||||
public bool Equals(Operation operation)
|
||||
{
|
||||
return operation._data == _data;
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return obj is Operation operation && Equals(operation);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return HashCode.Combine((IntPtr)_data);
|
||||
}
|
||||
|
||||
public static bool operator ==(Operation a, Operation b)
|
||||
{
|
||||
return a.Equals(b);
|
||||
}
|
||||
|
||||
public static bool operator !=(Operation a, Operation b)
|
||||
{
|
||||
return !a.Equals(b);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static void EnsureCapacity(ref Operand* list, ref ushort capacity, int newCapacity)
|
||||
{
|
||||
if (newCapacity > ushort.MaxValue)
|
||||
{
|
||||
ThrowOverflow(newCapacity);
|
||||
}
|
||||
// We only need to allocate a new buffer if we're increasing the size.
|
||||
else if (newCapacity > capacity)
|
||||
{
|
||||
list = Allocators.References.Allocate<Operand>((uint)newCapacity);
|
||||
}
|
||||
|
||||
capacity = (ushort)newCapacity;
|
||||
}
|
||||
|
||||
private static void ThrowOverflow(int count) =>
|
||||
throw new OverflowException($"Exceeded maximum size for Source or Destinations. Required {count}.");
|
||||
|
||||
public static class Factory
|
||||
{
|
||||
private static Operation Make(Instruction inst, int destCount, int srcCount)
|
||||
{
|
||||
Data* data = Allocators.Operations.Allocate<Data>();
|
||||
*data = default;
|
||||
|
||||
Operation result = new();
|
||||
result._data = data;
|
||||
result.Instruction = inst;
|
||||
|
||||
EnsureCapacity(ref result._data->Destinations, ref result._data->DestinationsCount, destCount);
|
||||
EnsureCapacity(ref result._data->Sources, ref result._data->SourcesCount, srcCount);
|
||||
|
||||
result.DestinationsUnsafe.Clear();
|
||||
result.SourcesUnsafe.Clear();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static Operation Operation(Instruction inst, Operand dest)
|
||||
{
|
||||
Operation result = Make(inst, 0, 0);
|
||||
result.SetDestination(dest);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static Operation Operation(Instruction inst, Operand dest, Operand src0)
|
||||
{
|
||||
Operation result = Make(inst, 0, 1);
|
||||
result.SetDestination(dest);
|
||||
result.SetSource(0, src0);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static Operation Operation(Instruction inst, Operand dest, Operand src0, Operand src1)
|
||||
{
|
||||
Operation result = Make(inst, 0, 2);
|
||||
result.SetDestination(dest);
|
||||
result.SetSource(0, src0);
|
||||
result.SetSource(1, src1);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static Operation Operation(Instruction inst, Operand dest, Operand src0, Operand src1, Operand src2)
|
||||
{
|
||||
Operation result = Make(inst, 0, 3);
|
||||
result.SetDestination(dest);
|
||||
result.SetSource(0, src0);
|
||||
result.SetSource(1, src1);
|
||||
result.SetSource(2, src2);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static Operation Operation(Instruction inst, Operand dest, int srcCount)
|
||||
{
|
||||
Operation result = Make(inst, 0, srcCount);
|
||||
result.SetDestination(dest);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static Operation Operation(Instruction inst, Operand dest, Operand[] srcs)
|
||||
{
|
||||
Operation result = Make(inst, 0, srcs.Length);
|
||||
|
||||
result.SetDestination(dest);
|
||||
|
||||
for (int index = 0; index < srcs.Length; index++)
|
||||
{
|
||||
result.SetSource(index, srcs[index]);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static Operation Operation(Intrinsic intrin, Operand dest, params Operand[] srcs)
|
||||
{
|
||||
Operation result = Make(Instruction.Extended, 0, srcs.Length);
|
||||
|
||||
result.Intrinsic = intrin;
|
||||
result.SetDestination(dest);
|
||||
|
||||
for (int index = 0; index < srcs.Length; index++)
|
||||
{
|
||||
result.SetSource(index, srcs[index]);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static Operation Operation(Instruction inst, Operand[] dests, Operand[] srcs)
|
||||
{
|
||||
Operation result = Make(inst, dests.Length, srcs.Length);
|
||||
|
||||
for (int index = 0; index < dests.Length; index++)
|
||||
{
|
||||
result.SetDestination(index, dests[index]);
|
||||
}
|
||||
|
||||
for (int index = 0; index < srcs.Length; index++)
|
||||
{
|
||||
result.SetSource(index, srcs[index]);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static Operation PhiOperation(Operand dest, int srcCount)
|
||||
{
|
||||
return Operation(Instruction.Phi, dest, srcCount * 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,43 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace ARMeilleure.IntermediateRepresentation
|
||||
{
|
||||
readonly struct Register : IEquatable<Register>
|
||||
{
|
||||
public int Index { get; }
|
||||
|
||||
public RegisterType Type { get; }
|
||||
|
||||
public Register(int index, RegisterType type)
|
||||
{
|
||||
Index = index;
|
||||
Type = type;
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return (ushort)Index | ((int)Type << 16);
|
||||
}
|
||||
|
||||
public static bool operator ==(Register x, Register y)
|
||||
{
|
||||
return x.Equals(y);
|
||||
}
|
||||
|
||||
public static bool operator !=(Register x, Register y)
|
||||
{
|
||||
return !x.Equals(y);
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return obj is Register reg && Equals(reg);
|
||||
}
|
||||
|
||||
public bool Equals(Register other)
|
||||
{
|
||||
return other.Index == Index &&
|
||||
other.Type == Type;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
namespace ARMeilleure.IntermediateRepresentation
|
||||
{
|
||||
enum RegisterType
|
||||
{
|
||||
Integer,
|
||||
Vector,
|
||||
Flag,
|
||||
FpFlag
|
||||
}
|
||||
}
|
@ -1,57 +0,0 @@
|
||||
using System.Runtime.Intrinsics.Arm;
|
||||
|
||||
namespace ARMeilleure
|
||||
{
|
||||
using Arm64HardwareCapabilities = ARMeilleure.CodeGen.Arm64.HardwareCapabilities;
|
||||
using X86HardwareCapabilities = ARMeilleure.CodeGen.X86.HardwareCapabilities;
|
||||
|
||||
public static class Optimizations
|
||||
{
|
||||
public static bool FastFP { get; set; } = true;
|
||||
|
||||
public static bool AllowLcqInFunctionTable { get; set; } = true;
|
||||
public static bool UseUnmanagedDispatchLoop { get; set; } = true;
|
||||
|
||||
public static bool UseAdvSimdIfAvailable { get; set; } = true;
|
||||
public static bool UseArm64PmullIfAvailable { get; set; } = true;
|
||||
|
||||
public static bool UseSseIfAvailable { get; set; } = true;
|
||||
public static bool UseSse2IfAvailable { get; set; } = true;
|
||||
public static bool UseSse3IfAvailable { get; set; } = true;
|
||||
public static bool UseSsse3IfAvailable { get; set; } = true;
|
||||
public static bool UseSse41IfAvailable { get; set; } = true;
|
||||
public static bool UseSse42IfAvailable { get; set; } = true;
|
||||
public static bool UsePopCntIfAvailable { get; set; } = true;
|
||||
public static bool UseAvxIfAvailable { get; set; } = true;
|
||||
public static bool UseF16cIfAvailable { get; set; } = true;
|
||||
public static bool UseFmaIfAvailable { get; set; } = true;
|
||||
public static bool UseAesniIfAvailable { get; set; } = true;
|
||||
public static bool UsePclmulqdqIfAvailable { get; set; } = true;
|
||||
public static bool UseShaIfAvailable { get; set; } = true;
|
||||
public static bool UseGfniIfAvailable { get; set; } = true;
|
||||
|
||||
public static bool ForceLegacySse
|
||||
{
|
||||
get => X86HardwareCapabilities.ForceLegacySse;
|
||||
set => X86HardwareCapabilities.ForceLegacySse = value;
|
||||
}
|
||||
|
||||
internal static bool UseAdvSimd => UseAdvSimdIfAvailable && Arm64HardwareCapabilities.SupportsAdvSimd;
|
||||
internal static bool UseArm64Pmull => UseArm64PmullIfAvailable && Arm64HardwareCapabilities.SupportsPmull;
|
||||
|
||||
internal static bool UseSse => UseSseIfAvailable && X86HardwareCapabilities.SupportsSse;
|
||||
internal static bool UseSse2 => UseSse2IfAvailable && X86HardwareCapabilities.SupportsSse2;
|
||||
internal static bool UseSse3 => UseSse3IfAvailable && X86HardwareCapabilities.SupportsSse3;
|
||||
internal static bool UseSsse3 => UseSsse3IfAvailable && X86HardwareCapabilities.SupportsSsse3;
|
||||
internal static bool UseSse41 => UseSse41IfAvailable && X86HardwareCapabilities.SupportsSse41;
|
||||
internal static bool UseSse42 => UseSse42IfAvailable && X86HardwareCapabilities.SupportsSse42;
|
||||
internal static bool UsePopCnt => UsePopCntIfAvailable && X86HardwareCapabilities.SupportsPopcnt;
|
||||
internal static bool UseAvx => UseAvxIfAvailable && X86HardwareCapabilities.SupportsAvx && !ForceLegacySse;
|
||||
internal static bool UseF16c => UseF16cIfAvailable && X86HardwareCapabilities.SupportsF16c;
|
||||
internal static bool UseFma => UseFmaIfAvailable && X86HardwareCapabilities.SupportsFma;
|
||||
internal static bool UseAesni => UseAesniIfAvailable && X86HardwareCapabilities.SupportsAesni;
|
||||
internal static bool UsePclmulqdq => UsePclmulqdqIfAvailable && X86HardwareCapabilities.SupportsPclmulqdq;
|
||||
internal static bool UseSha => UseShaIfAvailable && X86HardwareCapabilities.SupportsSha;
|
||||
internal static bool UseGfni => UseGfniIfAvailable && X86HardwareCapabilities.SupportsGfni;
|
||||
}
|
||||
}
|
@ -1,15 +0,0 @@
|
||||
namespace ARMeilleure.State
|
||||
{
|
||||
enum Aarch32Mode
|
||||
{
|
||||
User = 0b10000,
|
||||
Fiq = 0b10001,
|
||||
Irq = 0b10010,
|
||||
Supervisor = 0b10011,
|
||||
Monitor = 0b10110,
|
||||
Abort = 0b10111,
|
||||
Hypervisor = 0b11010,
|
||||
Undefined = 0b11011,
|
||||
System = 0b11111
|
||||
}
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
namespace ARMeilleure.State
|
||||
{
|
||||
enum ExecutionMode : int
|
||||
{
|
||||
Aarch32Arm = 0,
|
||||
Aarch32Thumb = 1,
|
||||
Aarch64 = 2
|
||||
}
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
namespace ARMeilleure.State
|
||||
{
|
||||
enum FPException
|
||||
{
|
||||
InvalidOp = 0,
|
||||
DivideByZero = 1,
|
||||
Overflow = 2,
|
||||
Underflow = 3,
|
||||
Inexact = 4,
|
||||
InputDenorm = 7
|
||||
}
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
namespace ARMeilleure.State
|
||||
{
|
||||
public enum FPRoundingMode
|
||||
{
|
||||
ToNearest = 0, // With ties to even.
|
||||
TowardsPlusInfinity = 1,
|
||||
TowardsMinusInfinity = 2,
|
||||
TowardsZero = 3,
|
||||
ToNearestAway = 4 // With ties to away.
|
||||
}
|
||||
}
|
@ -1,15 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace ARMeilleure.State
|
||||
{
|
||||
[Flags]
|
||||
public enum FPSCR : uint
|
||||
{
|
||||
V = 1u << 28,
|
||||
C = 1u << 29,
|
||||
Z = 1u << 30,
|
||||
N = 1u << 31,
|
||||
|
||||
Mask = N | Z | C | V | FPSR.Mask | FPCR.Mask // 0xFFC09F9Fu
|
||||
}
|
||||
}
|
@ -1,42 +0,0 @@
|
||||
namespace ARMeilleure.State
|
||||
{
|
||||
static class RegisterAlias
|
||||
{
|
||||
public const int R8Usr = 8;
|
||||
public const int R9Usr = 9;
|
||||
public const int R10Usr = 10;
|
||||
public const int R11Usr = 11;
|
||||
public const int R12Usr = 12;
|
||||
public const int SpUsr = 13;
|
||||
public const int LrUsr = 14;
|
||||
|
||||
public const int SpHyp = 15;
|
||||
|
||||
public const int LrIrq = 16;
|
||||
public const int SpIrq = 17;
|
||||
|
||||
public const int LrSvc = 18;
|
||||
public const int SpSvc = 19;
|
||||
|
||||
public const int LrAbt = 20;
|
||||
public const int SpAbt = 21;
|
||||
|
||||
public const int LrUnd = 22;
|
||||
public const int SpUnd = 23;
|
||||
|
||||
public const int R8Fiq = 24;
|
||||
public const int R9Fiq = 25;
|
||||
public const int R10Fiq = 26;
|
||||
public const int R11Fiq = 27;
|
||||
public const int R12Fiq = 28;
|
||||
public const int SpFiq = 29;
|
||||
public const int LrFiq = 30;
|
||||
|
||||
public const int Aarch32Sp = 13;
|
||||
public const int Aarch32Lr = 14;
|
||||
public const int Aarch32Pc = 15;
|
||||
|
||||
public const int Lr = 30;
|
||||
public const int Zr = 31;
|
||||
}
|
||||
}
|
@ -1,15 +0,0 @@
|
||||
namespace ARMeilleure.State
|
||||
{
|
||||
static class RegisterConsts
|
||||
{
|
||||
public const int IntRegsCount = 32;
|
||||
public const int VecRegsCount = 32;
|
||||
public const int FlagsCount = 32;
|
||||
public const int FpFlagsCount = 32;
|
||||
public const int IntAndVecRegsCount = IntRegsCount + VecRegsCount;
|
||||
public const int FpFlagsOffset = IntRegsCount + VecRegsCount + FlagsCount;
|
||||
public const int TotalCount = IntRegsCount + VecRegsCount + FlagsCount + FpFlagsCount;
|
||||
|
||||
public const int ZeroIndex = 31;
|
||||
}
|
||||
}
|
@ -1,198 +0,0 @@
|
||||
using ARMeilleure.CodeGen;
|
||||
using ARMeilleure.CodeGen.Unwinding;
|
||||
using ARMeilleure.Memory;
|
||||
using ARMeilleure.Native;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace ARMeilleure.Translation.Cache
|
||||
{
|
||||
static class JitCache
|
||||
{
|
||||
private const int PageSize = 4 * 1024;
|
||||
private const int PageMask = PageSize - 1;
|
||||
|
||||
private const int CodeAlignment = 4; // Bytes.
|
||||
private const int CacheSize = 2047 * 1024 * 1024;
|
||||
|
||||
private static ReservedRegion _jitRegion;
|
||||
private static JitCacheInvalidation _jitCacheInvalidator;
|
||||
|
||||
private static CacheMemoryAllocator _cacheAllocator;
|
||||
|
||||
private static readonly List<CacheEntry> _cacheEntries = new List<CacheEntry>();
|
||||
|
||||
private static readonly object _lock = new object();
|
||||
private static bool _initialized;
|
||||
|
||||
public static void Initialize(IJitMemoryAllocator allocator)
|
||||
{
|
||||
if (_initialized) return;
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
if (_initialized) return;
|
||||
|
||||
_jitRegion = new ReservedRegion(allocator, CacheSize);
|
||||
_jitCacheInvalidator = new JitCacheInvalidation(allocator);
|
||||
|
||||
_cacheAllocator = new CacheMemoryAllocator(CacheSize);
|
||||
|
||||
if (OperatingSystem.IsWindows())
|
||||
{
|
||||
JitUnwindWindows.InstallFunctionTableHandler(_jitRegion.Pointer, CacheSize, _jitRegion.Pointer + Allocate(PageSize));
|
||||
}
|
||||
|
||||
_initialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
public static IntPtr Map(CompiledFunction func)
|
||||
{
|
||||
byte[] code = func.Code;
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
Debug.Assert(_initialized);
|
||||
|
||||
int funcOffset = Allocate(code.Length);
|
||||
|
||||
IntPtr funcPtr = _jitRegion.Pointer + funcOffset;
|
||||
|
||||
if (OperatingSystem.IsMacOS() && RuntimeInformation.ProcessArchitecture == Architecture.Arm64)
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
fixed (byte *codePtr = code)
|
||||
{
|
||||
JitSupportDarwin.Copy(funcPtr, (IntPtr)codePtr, (ulong)code.Length);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ReprotectAsWritable(funcOffset, code.Length);
|
||||
Marshal.Copy(code, 0, funcPtr, code.Length);
|
||||
ReprotectAsExecutable(funcOffset, code.Length);
|
||||
|
||||
_jitCacheInvalidator.Invalidate(funcPtr, (ulong)code.Length);
|
||||
}
|
||||
|
||||
Add(funcOffset, code.Length, func.UnwindInfo);
|
||||
|
||||
return funcPtr;
|
||||
}
|
||||
}
|
||||
|
||||
public static void Unmap(IntPtr pointer)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
Debug.Assert(_initialized);
|
||||
|
||||
int funcOffset = (int)(pointer.ToInt64() - _jitRegion.Pointer.ToInt64());
|
||||
|
||||
bool result = TryFind(funcOffset, out CacheEntry entry);
|
||||
Debug.Assert(result);
|
||||
|
||||
_cacheAllocator.Free(funcOffset, AlignCodeSize(entry.Size));
|
||||
|
||||
Remove(funcOffset);
|
||||
}
|
||||
}
|
||||
|
||||
private static void ReprotectAsWritable(int offset, int size)
|
||||
{
|
||||
int endOffs = offset + size;
|
||||
|
||||
int regionStart = offset & ~PageMask;
|
||||
int regionEnd = (endOffs + PageMask) & ~PageMask;
|
||||
|
||||
_jitRegion.Block.MapAsRwx((ulong)regionStart, (ulong)(regionEnd - regionStart));
|
||||
}
|
||||
|
||||
private static void ReprotectAsExecutable(int offset, int size)
|
||||
{
|
||||
int endOffs = offset + size;
|
||||
|
||||
int regionStart = offset & ~PageMask;
|
||||
int regionEnd = (endOffs + PageMask) & ~PageMask;
|
||||
|
||||
_jitRegion.Block.MapAsRx((ulong)regionStart, (ulong)(regionEnd - regionStart));
|
||||
}
|
||||
|
||||
private static int Allocate(int codeSize)
|
||||
{
|
||||
codeSize = AlignCodeSize(codeSize);
|
||||
|
||||
int allocOffset = _cacheAllocator.Allocate(codeSize);
|
||||
|
||||
if (allocOffset < 0)
|
||||
{
|
||||
throw new OutOfMemoryException("JIT Cache exhausted.");
|
||||
}
|
||||
|
||||
_jitRegion.ExpandIfNeeded((ulong)allocOffset + (ulong)codeSize);
|
||||
|
||||
return allocOffset;
|
||||
}
|
||||
|
||||
private static int AlignCodeSize(int codeSize)
|
||||
{
|
||||
return checked(codeSize + (CodeAlignment - 1)) & ~(CodeAlignment - 1);
|
||||
}
|
||||
|
||||
private static void Add(int offset, int size, UnwindInfo unwindInfo)
|
||||
{
|
||||
CacheEntry entry = new CacheEntry(offset, size, unwindInfo);
|
||||
|
||||
int index = _cacheEntries.BinarySearch(entry);
|
||||
|
||||
if (index < 0)
|
||||
{
|
||||
index = ~index;
|
||||
}
|
||||
|
||||
_cacheEntries.Insert(index, entry);
|
||||
}
|
||||
|
||||
private static void Remove(int offset)
|
||||
{
|
||||
int index = _cacheEntries.BinarySearch(new CacheEntry(offset, 0, default));
|
||||
|
||||
if (index < 0)
|
||||
{
|
||||
index = ~index - 1;
|
||||
}
|
||||
|
||||
if (index >= 0)
|
||||
{
|
||||
_cacheEntries.RemoveAt(index);
|
||||
}
|
||||
}
|
||||
|
||||
public static bool TryFind(int offset, out CacheEntry entry)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
int index = _cacheEntries.BinarySearch(new CacheEntry(offset, 0, default));
|
||||
|
||||
if (index < 0)
|
||||
{
|
||||
index = ~index - 1;
|
||||
}
|
||||
|
||||
if (index >= 0)
|
||||
{
|
||||
entry = _cacheEntries[index];
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
entry = default;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,189 +0,0 @@
|
||||
// https://github.com/MicrosoftDocs/cpp-docs/blob/master/docs/build/exception-handling-x64.md
|
||||
|
||||
using ARMeilleure.CodeGen.Unwinding;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace ARMeilleure.Translation.Cache
|
||||
{
|
||||
static partial class JitUnwindWindows
|
||||
{
|
||||
private const int MaxUnwindCodesArraySize = 32; // Must be an even value.
|
||||
|
||||
private struct RuntimeFunction
|
||||
{
|
||||
public uint BeginAddress;
|
||||
public uint EndAddress;
|
||||
public uint UnwindData;
|
||||
}
|
||||
|
||||
private struct UnwindInfo
|
||||
{
|
||||
public byte VersionAndFlags;
|
||||
public byte SizeOfProlog;
|
||||
public byte CountOfUnwindCodes;
|
||||
public byte FrameRegister;
|
||||
public unsafe fixed ushort UnwindCodes[MaxUnwindCodesArraySize];
|
||||
}
|
||||
|
||||
private enum UnwindOp
|
||||
{
|
||||
PushNonvol = 0,
|
||||
AllocLarge = 1,
|
||||
AllocSmall = 2,
|
||||
SetFpreg = 3,
|
||||
SaveNonvol = 4,
|
||||
SaveNonvolFar = 5,
|
||||
SaveXmm128 = 8,
|
||||
SaveXmm128Far = 9,
|
||||
PushMachframe = 10
|
||||
}
|
||||
|
||||
private unsafe delegate RuntimeFunction* GetRuntimeFunctionCallback(ulong controlPc, IntPtr context);
|
||||
|
||||
[LibraryImport("kernel32.dll")]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
private static unsafe partial bool RtlInstallFunctionTableCallback(
|
||||
ulong tableIdentifier,
|
||||
ulong baseAddress,
|
||||
uint length,
|
||||
GetRuntimeFunctionCallback callback,
|
||||
IntPtr context,
|
||||
[MarshalAs(UnmanagedType.LPWStr)] string outOfProcessCallbackDll);
|
||||
|
||||
private static GetRuntimeFunctionCallback _getRuntimeFunctionCallback;
|
||||
|
||||
private static int _sizeOfRuntimeFunction;
|
||||
|
||||
private unsafe static RuntimeFunction* _runtimeFunction;
|
||||
|
||||
private unsafe static UnwindInfo* _unwindInfo;
|
||||
|
||||
public static void InstallFunctionTableHandler(IntPtr codeCachePointer, uint codeCacheLength, IntPtr workBufferPtr)
|
||||
{
|
||||
ulong codeCachePtr = (ulong)codeCachePointer.ToInt64();
|
||||
|
||||
_sizeOfRuntimeFunction = Marshal.SizeOf<RuntimeFunction>();
|
||||
|
||||
bool result;
|
||||
|
||||
unsafe
|
||||
{
|
||||
_runtimeFunction = (RuntimeFunction*)workBufferPtr;
|
||||
|
||||
_unwindInfo = (UnwindInfo*)(workBufferPtr + _sizeOfRuntimeFunction);
|
||||
|
||||
_getRuntimeFunctionCallback = new GetRuntimeFunctionCallback(FunctionTableHandler);
|
||||
|
||||
result = RtlInstallFunctionTableCallback(
|
||||
codeCachePtr | 3,
|
||||
codeCachePtr,
|
||||
codeCacheLength,
|
||||
_getRuntimeFunctionCallback,
|
||||
codeCachePointer,
|
||||
null);
|
||||
}
|
||||
|
||||
if (!result)
|
||||
{
|
||||
throw new InvalidOperationException("Failure installing function table callback.");
|
||||
}
|
||||
}
|
||||
|
||||
private static unsafe RuntimeFunction* FunctionTableHandler(ulong controlPc, IntPtr context)
|
||||
{
|
||||
int offset = (int)((long)controlPc - context.ToInt64());
|
||||
|
||||
if (!JitCache.TryFind(offset, out CacheEntry funcEntry))
|
||||
{
|
||||
return null; // Not found.
|
||||
}
|
||||
|
||||
var unwindInfo = funcEntry.UnwindInfo;
|
||||
|
||||
int codeIndex = 0;
|
||||
|
||||
for (int index = unwindInfo.PushEntries.Length - 1; index >= 0; index--)
|
||||
{
|
||||
var entry = unwindInfo.PushEntries[index];
|
||||
|
||||
switch (entry.PseudoOp)
|
||||
{
|
||||
case UnwindPseudoOp.SaveXmm128:
|
||||
{
|
||||
int stackOffset = entry.StackOffsetOrAllocSize;
|
||||
|
||||
Debug.Assert(stackOffset % 16 == 0);
|
||||
|
||||
if (stackOffset <= 0xFFFF0)
|
||||
{
|
||||
_unwindInfo->UnwindCodes[codeIndex++] = PackUnwindOp(UnwindOp.SaveXmm128, entry.PrologOffset, entry.RegIndex);
|
||||
_unwindInfo->UnwindCodes[codeIndex++] = (ushort)(stackOffset / 16);
|
||||
}
|
||||
else
|
||||
{
|
||||
_unwindInfo->UnwindCodes[codeIndex++] = PackUnwindOp(UnwindOp.SaveXmm128Far, entry.PrologOffset, entry.RegIndex);
|
||||
_unwindInfo->UnwindCodes[codeIndex++] = (ushort)(stackOffset >> 0);
|
||||
_unwindInfo->UnwindCodes[codeIndex++] = (ushort)(stackOffset >> 16);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case UnwindPseudoOp.AllocStack:
|
||||
{
|
||||
int allocSize = entry.StackOffsetOrAllocSize;
|
||||
|
||||
Debug.Assert(allocSize % 8 == 0);
|
||||
|
||||
if (allocSize <= 128)
|
||||
{
|
||||
_unwindInfo->UnwindCodes[codeIndex++] = PackUnwindOp(UnwindOp.AllocSmall, entry.PrologOffset, (allocSize / 8) - 1);
|
||||
}
|
||||
else if (allocSize <= 0x7FFF8)
|
||||
{
|
||||
_unwindInfo->UnwindCodes[codeIndex++] = PackUnwindOp(UnwindOp.AllocLarge, entry.PrologOffset, 0);
|
||||
_unwindInfo->UnwindCodes[codeIndex++] = (ushort)(allocSize / 8);
|
||||
}
|
||||
else
|
||||
{
|
||||
_unwindInfo->UnwindCodes[codeIndex++] = PackUnwindOp(UnwindOp.AllocLarge, entry.PrologOffset, 1);
|
||||
_unwindInfo->UnwindCodes[codeIndex++] = (ushort)(allocSize >> 0);
|
||||
_unwindInfo->UnwindCodes[codeIndex++] = (ushort)(allocSize >> 16);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case UnwindPseudoOp.PushReg:
|
||||
{
|
||||
_unwindInfo->UnwindCodes[codeIndex++] = PackUnwindOp(UnwindOp.PushNonvol, entry.PrologOffset, entry.RegIndex);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default: throw new NotImplementedException($"({nameof(entry.PseudoOp)} = {entry.PseudoOp})");
|
||||
}
|
||||
}
|
||||
|
||||
Debug.Assert(codeIndex <= MaxUnwindCodesArraySize);
|
||||
|
||||
_unwindInfo->VersionAndFlags = 1; // Flags: The function has no handler.
|
||||
_unwindInfo->SizeOfProlog = (byte)unwindInfo.PrologSize;
|
||||
_unwindInfo->CountOfUnwindCodes = (byte)codeIndex;
|
||||
_unwindInfo->FrameRegister = 0;
|
||||
|
||||
_runtimeFunction->BeginAddress = (uint)funcEntry.Offset;
|
||||
_runtimeFunction->EndAddress = (uint)(funcEntry.Offset + funcEntry.Size);
|
||||
_runtimeFunction->UnwindData = (uint)_sizeOfRuntimeFunction;
|
||||
|
||||
return _runtimeFunction;
|
||||
}
|
||||
|
||||
private static ushort PackUnwindOp(UnwindOp op, int prologOffset, int opInfo)
|
||||
{
|
||||
return (ushort)(prologOffset | ((int)op << 8) | (opInfo << 12));
|
||||
}
|
||||
}
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
using ARMeilleure.IntermediateRepresentation;
|
||||
|
||||
namespace ARMeilleure.Translation
|
||||
{
|
||||
readonly struct CompilerContext
|
||||
{
|
||||
public ControlFlowGraph Cfg { get; }
|
||||
|
||||
public OperandType[] FuncArgTypes { get; }
|
||||
public OperandType FuncReturnType { get; }
|
||||
|
||||
public CompilerOptions Options { get; }
|
||||
|
||||
public CompilerContext(
|
||||
ControlFlowGraph cfg,
|
||||
OperandType[] funcArgTypes,
|
||||
OperandType funcReturnType,
|
||||
CompilerOptions options)
|
||||
{
|
||||
Cfg = cfg;
|
||||
FuncArgTypes = funcArgTypes;
|
||||
FuncReturnType = funcReturnType;
|
||||
Options = options;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace ARMeilleure.Translation
|
||||
{
|
||||
[Flags]
|
||||
enum CompilerOptions
|
||||
{
|
||||
None = 0,
|
||||
SsaForm = 1 << 0,
|
||||
Optimize = 1 << 1,
|
||||
Lsra = 1 << 2,
|
||||
Relocatable = 1 << 3,
|
||||
|
||||
MediumCq = SsaForm | Optimize,
|
||||
HighCq = SsaForm | Optimize | Lsra
|
||||
}
|
||||
}
|
@ -1,155 +0,0 @@
|
||||
using ARMeilleure.IntermediateRepresentation;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace ARMeilleure.Translation
|
||||
{
|
||||
class ControlFlowGraph
|
||||
{
|
||||
private BasicBlock[] _postOrderBlocks;
|
||||
private int[] _postOrderMap;
|
||||
|
||||
public int LocalsCount { get; private set; }
|
||||
public BasicBlock Entry { get; }
|
||||
public IntrusiveList<BasicBlock> Blocks { get; }
|
||||
public BasicBlock[] PostOrderBlocks => _postOrderBlocks;
|
||||
public int[] PostOrderMap => _postOrderMap;
|
||||
|
||||
public ControlFlowGraph(BasicBlock entry, IntrusiveList<BasicBlock> blocks, int localsCount)
|
||||
{
|
||||
Entry = entry;
|
||||
Blocks = blocks;
|
||||
LocalsCount = localsCount;
|
||||
|
||||
Update();
|
||||
}
|
||||
|
||||
public Operand AllocateLocal(OperandType type)
|
||||
{
|
||||
Operand result = Operand.Factory.Local(type);
|
||||
|
||||
result.NumberLocal(++LocalsCount);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
RemoveUnreachableBlocks(Blocks);
|
||||
|
||||
var visited = new HashSet<BasicBlock>();
|
||||
var blockStack = new Stack<BasicBlock>();
|
||||
|
||||
Array.Resize(ref _postOrderBlocks, Blocks.Count);
|
||||
Array.Resize(ref _postOrderMap, Blocks.Count);
|
||||
|
||||
visited.Add(Entry);
|
||||
blockStack.Push(Entry);
|
||||
|
||||
int index = 0;
|
||||
|
||||
while (blockStack.TryPop(out BasicBlock block))
|
||||
{
|
||||
bool visitedNew = false;
|
||||
|
||||
for (int i = 0; i < block.SuccessorsCount; i++)
|
||||
{
|
||||
BasicBlock succ = block.GetSuccessor(i);
|
||||
|
||||
if (visited.Add(succ))
|
||||
{
|
||||
blockStack.Push(block);
|
||||
blockStack.Push(succ);
|
||||
|
||||
visitedNew = true;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!visitedNew)
|
||||
{
|
||||
PostOrderMap[block.Index] = index;
|
||||
|
||||
PostOrderBlocks[index++] = block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void RemoveUnreachableBlocks(IntrusiveList<BasicBlock> blocks)
|
||||
{
|
||||
var visited = new HashSet<BasicBlock>();
|
||||
var workQueue = new Queue<BasicBlock>();
|
||||
|
||||
visited.Add(Entry);
|
||||
workQueue.Enqueue(Entry);
|
||||
|
||||
while (workQueue.TryDequeue(out BasicBlock block))
|
||||
{
|
||||
Debug.Assert(block.Index != -1, "Invalid block index.");
|
||||
|
||||
for (int i = 0; i < block.SuccessorsCount; i++)
|
||||
{
|
||||
BasicBlock succ = block.GetSuccessor(i);
|
||||
|
||||
if (visited.Add(succ))
|
||||
{
|
||||
workQueue.Enqueue(succ);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (visited.Count < blocks.Count)
|
||||
{
|
||||
// Remove unreachable blocks and renumber.
|
||||
int index = 0;
|
||||
|
||||
for (BasicBlock block = blocks.First; block != null;)
|
||||
{
|
||||
BasicBlock nextBlock = block.ListNext;
|
||||
|
||||
if (!visited.Contains(block))
|
||||
{
|
||||
while (block.SuccessorsCount > 0)
|
||||
{
|
||||
block.RemoveSuccessor(index: block.SuccessorsCount - 1);
|
||||
}
|
||||
|
||||
blocks.Remove(block);
|
||||
}
|
||||
else
|
||||
{
|
||||
block.Index = index++;
|
||||
}
|
||||
|
||||
block = nextBlock;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public BasicBlock SplitEdge(BasicBlock predecessor, BasicBlock successor)
|
||||
{
|
||||
BasicBlock splitBlock = new BasicBlock(Blocks.Count);
|
||||
|
||||
for (int i = 0; i < predecessor.SuccessorsCount; i++)
|
||||
{
|
||||
if (predecessor.GetSuccessor(i) == successor)
|
||||
{
|
||||
predecessor.SetSuccessor(i, splitBlock);
|
||||
}
|
||||
}
|
||||
|
||||
if (splitBlock.Predecessors.Count == 0)
|
||||
{
|
||||
throw new ArgumentException("Predecessor and successor are not connected.");
|
||||
}
|
||||
|
||||
splitBlock.AddSuccessor(successor);
|
||||
|
||||
Blocks.AddBefore(successor, splitBlock);
|
||||
|
||||
return splitBlock;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,104 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
|
||||
namespace ARMeilleure.Translation
|
||||
{
|
||||
static class DelegateHelper
|
||||
{
|
||||
private const string DelegateTypesAssemblyName = "JitDelegateTypes";
|
||||
|
||||
private static readonly ModuleBuilder _modBuilder;
|
||||
|
||||
private static readonly Dictionary<string, Type> _delegateTypesCache;
|
||||
|
||||
static DelegateHelper()
|
||||
{
|
||||
AssemblyBuilder asmBuilder = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName(DelegateTypesAssemblyName), AssemblyBuilderAccess.Run);
|
||||
|
||||
_modBuilder = asmBuilder.DefineDynamicModule(DelegateTypesAssemblyName);
|
||||
|
||||
_delegateTypesCache = new Dictionary<string, Type>();
|
||||
}
|
||||
|
||||
public static Delegate GetDelegate(MethodInfo info)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(info);
|
||||
|
||||
Type[] parameters = info.GetParameters().Select(pI => pI.ParameterType).ToArray();
|
||||
Type returnType = info.ReturnType;
|
||||
|
||||
Type delegateType = GetDelegateType(parameters, returnType);
|
||||
|
||||
return Delegate.CreateDelegate(delegateType, info);
|
||||
}
|
||||
|
||||
private static Type GetDelegateType(Type[] parameters, Type returnType)
|
||||
{
|
||||
string key = GetFunctionSignatureKey(parameters, returnType);
|
||||
|
||||
if (!_delegateTypesCache.TryGetValue(key, out Type delegateType))
|
||||
{
|
||||
delegateType = MakeDelegateType(parameters, returnType, key);
|
||||
|
||||
_delegateTypesCache.TryAdd(key, delegateType);
|
||||
}
|
||||
|
||||
return delegateType;
|
||||
}
|
||||
|
||||
private static string GetFunctionSignatureKey(Type[] parameters, Type returnType)
|
||||
{
|
||||
string sig = GetTypeName(returnType);
|
||||
|
||||
foreach (Type type in parameters)
|
||||
{
|
||||
sig += '_' + GetTypeName(type);
|
||||
}
|
||||
|
||||
return sig;
|
||||
}
|
||||
|
||||
private static string GetTypeName(Type type)
|
||||
{
|
||||
return type.FullName.Replace(".", string.Empty);
|
||||
}
|
||||
|
||||
private const MethodAttributes CtorAttributes =
|
||||
MethodAttributes.RTSpecialName |
|
||||
MethodAttributes.HideBySig |
|
||||
MethodAttributes.Public;
|
||||
|
||||
private const TypeAttributes DelegateTypeAttributes =
|
||||
TypeAttributes.Class |
|
||||
TypeAttributes.Public |
|
||||
TypeAttributes.Sealed |
|
||||
TypeAttributes.AnsiClass |
|
||||
TypeAttributes.AutoClass;
|
||||
|
||||
private const MethodImplAttributes ImplAttributes =
|
||||
MethodImplAttributes.Runtime |
|
||||
MethodImplAttributes.Managed;
|
||||
|
||||
private const MethodAttributes InvokeAttributes =
|
||||
MethodAttributes.Public |
|
||||
MethodAttributes.HideBySig |
|
||||
MethodAttributes.NewSlot |
|
||||
MethodAttributes.Virtual;
|
||||
|
||||
private static readonly Type[] _delegateCtorSignature = { typeof(object), typeof(IntPtr) };
|
||||
|
||||
private static Type MakeDelegateType(Type[] parameters, Type returnType, string name)
|
||||
{
|
||||
TypeBuilder builder = _modBuilder.DefineType(name, DelegateTypeAttributes, typeof(MulticastDelegate));
|
||||
|
||||
builder.DefineConstructor(CtorAttributes, CallingConventions.Standard, _delegateCtorSignature).SetImplementationFlags(ImplAttributes);
|
||||
|
||||
builder.DefineMethod("Invoke", InvokeAttributes, returnType, parameters).SetImplementationFlags(ImplAttributes);
|
||||
|
||||
return builder.CreateTypeInfo();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,267 +0,0 @@
|
||||
using ARMeilleure.Instructions;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
|
||||
namespace ARMeilleure.Translation
|
||||
{
|
||||
static class Delegates
|
||||
{
|
||||
public static bool TryGetDelegateFuncPtrByIndex(int index, out IntPtr funcPtr)
|
||||
{
|
||||
if (index >= 0 && index < _delegates.Count)
|
||||
{
|
||||
funcPtr = _delegates.Values[index].FuncPtr; // O(1).
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
funcPtr = default;
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static IntPtr GetDelegateFuncPtrByIndex(int index)
|
||||
{
|
||||
if (index < 0 || index >= _delegates.Count)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException($"({nameof(index)} = {index})");
|
||||
}
|
||||
|
||||
return _delegates.Values[index].FuncPtr; // O(1).
|
||||
}
|
||||
|
||||
public static IntPtr GetDelegateFuncPtr(MethodInfo info)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(info);
|
||||
|
||||
string key = GetKey(info);
|
||||
|
||||
if (!_delegates.TryGetValue(key, out DelegateInfo dlgInfo)) // O(log(n)).
|
||||
{
|
||||
throw new KeyNotFoundException($"({nameof(key)} = {key})");
|
||||
}
|
||||
|
||||
return dlgInfo.FuncPtr;
|
||||
}
|
||||
|
||||
public static int GetDelegateIndex(MethodInfo info)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(info);
|
||||
|
||||
string key = GetKey(info);
|
||||
|
||||
int index = _delegates.IndexOfKey(key); // O(log(n)).
|
||||
|
||||
if (index == -1)
|
||||
{
|
||||
throw new KeyNotFoundException($"({nameof(key)} = {key})");
|
||||
}
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
private static void SetDelegateInfo(MethodInfo info)
|
||||
{
|
||||
string key = GetKey(info);
|
||||
|
||||
Delegate dlg = DelegateHelper.GetDelegate(info);
|
||||
|
||||
_delegates.Add(key, new DelegateInfo(dlg)); // ArgumentException (key).
|
||||
}
|
||||
|
||||
private static string GetKey(MethodInfo info)
|
||||
{
|
||||
return $"{info.DeclaringType.Name}.{info.Name}";
|
||||
}
|
||||
|
||||
private static readonly SortedList<string, DelegateInfo> _delegates;
|
||||
|
||||
static Delegates()
|
||||
{
|
||||
_delegates = new SortedList<string, DelegateInfo>();
|
||||
|
||||
SetDelegateInfo(typeof(Math).GetMethod(nameof(Math.Abs), new Type[] { typeof(double) }));
|
||||
SetDelegateInfo(typeof(Math).GetMethod(nameof(Math.Ceiling), new Type[] { typeof(double) }));
|
||||
SetDelegateInfo(typeof(Math).GetMethod(nameof(Math.Floor), new Type[] { typeof(double) }));
|
||||
SetDelegateInfo(typeof(Math).GetMethod(nameof(Math.Round), new Type[] { typeof(double), typeof(MidpointRounding) }));
|
||||
SetDelegateInfo(typeof(Math).GetMethod(nameof(Math.Truncate), new Type[] { typeof(double) }));
|
||||
|
||||
SetDelegateInfo(typeof(MathF).GetMethod(nameof(MathF.Abs), new Type[] { typeof(float) }));
|
||||
SetDelegateInfo(typeof(MathF).GetMethod(nameof(MathF.Ceiling), new Type[] { typeof(float) }));
|
||||
SetDelegateInfo(typeof(MathF).GetMethod(nameof(MathF.Floor), new Type[] { typeof(float) }));
|
||||
SetDelegateInfo(typeof(MathF).GetMethod(nameof(MathF.Round), new Type[] { typeof(float), typeof(MidpointRounding) }));
|
||||
SetDelegateInfo(typeof(MathF).GetMethod(nameof(MathF.Truncate), new Type[] { typeof(float) }));
|
||||
|
||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.Break)));
|
||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.CheckSynchronization)));
|
||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.EnqueueForRejit)));
|
||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCntfrqEl0)));
|
||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCntpctEl0)));
|
||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCntvctEl0)));
|
||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCtrEl0)));
|
||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetDczidEl0)));
|
||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetFunctionAddress)));
|
||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.InvalidateCacheLine)));
|
||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetTpidrroEl0)));
|
||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetTpidr32))); // A32 only.
|
||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetTpidrEl0)));
|
||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetTpidrEl032))); // A32 only.
|
||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadByte)));
|
||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt16)));
|
||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt32)));
|
||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt64)));
|
||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadVector128)));
|
||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetTpidrEl0)));
|
||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetTpidrEl032))); // A32 only.
|
||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SignalMemoryTracking)));
|
||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SupervisorCall)));
|
||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ThrowInvalidMemoryAccess)));
|
||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.Undefined)));
|
||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteByte)));
|
||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteUInt16)));
|
||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteUInt32)));
|
||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteUInt64)));
|
||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteVector128)));
|
||||
|
||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.CountLeadingSigns)));
|
||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.CountLeadingZeros)));
|
||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Crc32b)));
|
||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Crc32cb)));
|
||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Crc32ch)));
|
||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Crc32cw)));
|
||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Crc32cx)));
|
||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Crc32h)));
|
||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Crc32w)));
|
||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Crc32x)));
|
||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Decrypt)));
|
||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Encrypt)));
|
||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.FixedRotate)));
|
||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.HashChoose)));
|
||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.HashLower)));
|
||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.HashMajority)));
|
||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.HashParity)));
|
||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.HashUpper)));
|
||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.InverseMixColumns)));
|
||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.MixColumns)));
|
||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.PolynomialMult64_128)));
|
||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF32ToS32)));
|
||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF32ToS64)));
|
||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF32ToU32)));
|
||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF32ToU64)));
|
||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF64ToS32)));
|
||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF64ToS64)));
|
||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF64ToU32)));
|
||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF64ToU64)));
|
||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Sha1SchedulePart1)));
|
||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Sha1SchedulePart2)));
|
||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Sha256SchedulePart1)));
|
||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Sha256SchedulePart2)));
|
||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.SignedShrImm64)));
|
||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Tbl1)));
|
||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Tbl2)));
|
||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Tbl3)));
|
||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Tbl4)));
|
||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Tbx1)));
|
||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Tbx2)));
|
||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Tbx3)));
|
||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Tbx4)));
|
||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.UnsignedShrImm64)));
|
||||
|
||||
SetDelegateInfo(typeof(SoftFloat16_32).GetMethod(nameof(SoftFloat16_32.FPConvert)));
|
||||
SetDelegateInfo(typeof(SoftFloat16_64).GetMethod(nameof(SoftFloat16_64.FPConvert)));
|
||||
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPAdd)));
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPAddFpscr))); // A32 only.
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompare)));
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareEQ)));
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareEQFpscr))); // A32 only.
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareGE)));
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareGEFpscr))); // A32 only.
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareGT)));
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareGTFpscr))); // A32 only.
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareLE)));
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareLEFpscr))); // A32 only.
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareLT)));
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareLTFpscr))); // A32 only.
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPDiv)));
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMax)));
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMaxFpscr))); // A32 only.
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMaxNum)));
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMaxNumFpscr))); // A32 only.
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMin)));
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMinFpscr))); // A32 only.
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMinNum)));
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMinNumFpscr))); // A32 only.
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMul)));
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMulFpscr))); // A32 only.
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMulAdd)));
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMulAddFpscr))); // A32 only.
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMulSub)));
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMulSubFpscr))); // A32 only.
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMulX)));
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPNegMulAdd)));
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPNegMulSub)));
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPRecipEstimate)));
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPRecipEstimateFpscr))); // A32 only.
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPRecipStep))); // A32 only.
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPRecipStepFused)));
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPRecpX)));
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPRSqrtEstimate)));
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPRSqrtEstimateFpscr))); // A32 only.
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPRSqrtStep))); // A32 only.
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPRSqrtStepFused)));
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPSqrt)));
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPSub)));
|
||||
|
||||
SetDelegateInfo(typeof(SoftFloat32_16).GetMethod(nameof(SoftFloat32_16.FPConvert)));
|
||||
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPAdd)));
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPAddFpscr))); // A32 only.
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompare)));
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareEQ)));
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareEQFpscr))); // A32 only.
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareGE)));
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareGEFpscr))); // A32 only.
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareGT)));
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareGTFpscr))); // A32 only.
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareLE)));
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareLEFpscr))); // A32 only.
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareLT)));
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareLTFpscr))); // A32 only.
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPDiv)));
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMax)));
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMaxFpscr))); // A32 only.
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMaxNum)));
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMaxNumFpscr))); // A32 only.
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMin)));
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMinFpscr))); // A32 only.
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMinNum)));
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMinNumFpscr))); // A32 only.
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMul)));
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMulFpscr))); // A32 only.
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMulAdd)));
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMulAddFpscr))); // A32 only.
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMulSub)));
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMulSubFpscr))); // A32 only.
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMulX)));
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPNegMulAdd)));
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPNegMulSub)));
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPRecipEstimate)));
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPRecipEstimateFpscr))); // A32 only.
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPRecipStep))); // A32 only.
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPRecipStepFused)));
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPRecpX)));
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPRSqrtEstimate)));
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPRSqrtEstimateFpscr))); // A32 only.
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPRSqrtStep))); // A32 only.
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPRSqrtStepFused)));
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPSqrt)));
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPSub)));
|
||||
|
||||
SetDelegateInfo(typeof(SoftFloat64_16).GetMethod(nameof(SoftFloat64_16.FPConvert)));
|
||||
}
|
||||
}
|
||||
}
|
@ -1,6 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace ARMeilleure.Translation
|
||||
{
|
||||
delegate void DispatcherFunction(IntPtr nativeContext, ulong startAddress);
|
||||
}
|
@ -1,95 +0,0 @@
|
||||
using ARMeilleure.IntermediateRepresentation;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace ARMeilleure.Translation
|
||||
{
|
||||
static class Dominance
|
||||
{
|
||||
// Those methods are an implementation of the algorithms on "A Simple, Fast Dominance Algorithm".
|
||||
// https://www.cs.rice.edu/~keith/EMBED/dom.pdf
|
||||
public static void FindDominators(ControlFlowGraph cfg)
|
||||
{
|
||||
BasicBlock Intersect(BasicBlock block1, BasicBlock block2)
|
||||
{
|
||||
while (block1 != block2)
|
||||
{
|
||||
while (cfg.PostOrderMap[block1.Index] < cfg.PostOrderMap[block2.Index])
|
||||
{
|
||||
block1 = block1.ImmediateDominator;
|
||||
}
|
||||
|
||||
while (cfg.PostOrderMap[block2.Index] < cfg.PostOrderMap[block1.Index])
|
||||
{
|
||||
block2 = block2.ImmediateDominator;
|
||||
}
|
||||
}
|
||||
|
||||
return block1;
|
||||
}
|
||||
|
||||
cfg.Entry.ImmediateDominator = cfg.Entry;
|
||||
|
||||
Debug.Assert(cfg.Entry == cfg.PostOrderBlocks[cfg.PostOrderBlocks.Length - 1]);
|
||||
|
||||
bool modified;
|
||||
|
||||
do
|
||||
{
|
||||
modified = false;
|
||||
|
||||
for (int blkIndex = cfg.PostOrderBlocks.Length - 2; blkIndex >= 0; blkIndex--)
|
||||
{
|
||||
BasicBlock block = cfg.PostOrderBlocks[blkIndex];
|
||||
|
||||
BasicBlock newIDom = null;
|
||||
|
||||
foreach (BasicBlock predecessor in block.Predecessors)
|
||||
{
|
||||
if (predecessor.ImmediateDominator != null)
|
||||
{
|
||||
if (newIDom != null)
|
||||
{
|
||||
newIDom = Intersect(predecessor, newIDom);
|
||||
}
|
||||
else
|
||||
{
|
||||
newIDom = predecessor;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (block.ImmediateDominator != newIDom)
|
||||
{
|
||||
block.ImmediateDominator = newIDom;
|
||||
|
||||
modified = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
while (modified);
|
||||
}
|
||||
|
||||
public static void FindDominanceFrontiers(ControlFlowGraph cfg)
|
||||
{
|
||||
for (BasicBlock block = cfg.Blocks.First; block != null; block = block.ListNext)
|
||||
{
|
||||
if (block.Predecessors.Count < 2)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int pBlkIndex = 0; pBlkIndex < block.Predecessors.Count; pBlkIndex++)
|
||||
{
|
||||
BasicBlock current = block.Predecessors[pBlkIndex];
|
||||
|
||||
while (current != block.ImmediateDominator)
|
||||
{
|
||||
current.DominanceFrontiers.Add(block);
|
||||
|
||||
current = current.ImmediateDominator;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,680 +0,0 @@
|
||||
using ARMeilleure.Diagnostics;
|
||||
using ARMeilleure.IntermediateRepresentation;
|
||||
using ARMeilleure.State;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
|
||||
|
||||
namespace ARMeilleure.Translation
|
||||
{
|
||||
class EmitterContext
|
||||
{
|
||||
private int _localsCount;
|
||||
|
||||
private readonly Dictionary<Operand, BasicBlock> _irLabels;
|
||||
private readonly IntrusiveList<BasicBlock> _irBlocks;
|
||||
|
||||
private BasicBlock _irBlock;
|
||||
private BasicBlock _ifBlock;
|
||||
|
||||
private bool _needsNewBlock;
|
||||
private BasicBlockFrequency _nextBlockFreq;
|
||||
|
||||
public EmitterContext()
|
||||
{
|
||||
_localsCount = 0;
|
||||
|
||||
_irLabels = new Dictionary<Operand, BasicBlock>();
|
||||
_irBlocks = new IntrusiveList<BasicBlock>();
|
||||
|
||||
_needsNewBlock = true;
|
||||
_nextBlockFreq = BasicBlockFrequency.Default;
|
||||
}
|
||||
|
||||
public Operand AllocateLocal(OperandType type)
|
||||
{
|
||||
Operand local = Local(type);
|
||||
|
||||
local.NumberLocal(++_localsCount);
|
||||
|
||||
return local;
|
||||
}
|
||||
|
||||
public Operand Add(Operand op1, Operand op2)
|
||||
{
|
||||
return Add(Instruction.Add, Local(op1.Type), op1, op2);
|
||||
}
|
||||
|
||||
public Operand BitwiseAnd(Operand op1, Operand op2)
|
||||
{
|
||||
return Add(Instruction.BitwiseAnd, Local(op1.Type), op1, op2);
|
||||
}
|
||||
|
||||
public Operand BitwiseExclusiveOr(Operand op1, Operand op2)
|
||||
{
|
||||
return Add(Instruction.BitwiseExclusiveOr, Local(op1.Type), op1, op2);
|
||||
}
|
||||
|
||||
public Operand BitwiseNot(Operand op1)
|
||||
{
|
||||
return Add(Instruction.BitwiseNot, Local(op1.Type), op1);
|
||||
}
|
||||
|
||||
public Operand BitwiseOr(Operand op1, Operand op2)
|
||||
{
|
||||
return Add(Instruction.BitwiseOr, Local(op1.Type), op1, op2);
|
||||
}
|
||||
|
||||
public void Branch(Operand label)
|
||||
{
|
||||
NewNextBlockIfNeeded();
|
||||
|
||||
BranchToLabel(label, uncond: true, BasicBlockFrequency.Default);
|
||||
}
|
||||
|
||||
public void BranchIf(Operand label, Operand op1, Operand op2, Comparison comp, BasicBlockFrequency falseFreq = default)
|
||||
{
|
||||
Add(Instruction.BranchIf, default, op1, op2, Const((int)comp));
|
||||
|
||||
BranchToLabel(label, uncond: false, falseFreq);
|
||||
}
|
||||
|
||||
public void BranchIfFalse(Operand label, Operand op1, BasicBlockFrequency falseFreq = default)
|
||||
{
|
||||
BranchIf(label, op1, Const(op1.Type, 0), Comparison.Equal, falseFreq);
|
||||
}
|
||||
|
||||
public void BranchIfTrue(Operand label, Operand op1, BasicBlockFrequency falseFreq = default)
|
||||
{
|
||||
BranchIf(label, op1, Const(op1.Type, 0), Comparison.NotEqual, falseFreq);
|
||||
}
|
||||
|
||||
public Operand ByteSwap(Operand op1)
|
||||
{
|
||||
return Add(Instruction.ByteSwap, Local(op1.Type), op1);
|
||||
}
|
||||
|
||||
public virtual Operand Call(MethodInfo info, params Operand[] callArgs)
|
||||
{
|
||||
IntPtr funcPtr = Delegates.GetDelegateFuncPtr(info);
|
||||
|
||||
OperandType returnType = GetOperandType(info.ReturnType);
|
||||
|
||||
Symbols.Add((ulong)funcPtr.ToInt64(), info.Name);
|
||||
|
||||
return Call(Const(funcPtr.ToInt64()), returnType, callArgs);
|
||||
}
|
||||
|
||||
protected static OperandType GetOperandType(Type type)
|
||||
{
|
||||
if (type == typeof(bool) || type == typeof(byte) ||
|
||||
type == typeof(char) || type == typeof(short) ||
|
||||
type == typeof(int) || type == typeof(sbyte) ||
|
||||
type == typeof(ushort) || type == typeof(uint))
|
||||
{
|
||||
return OperandType.I32;
|
||||
}
|
||||
else if (type == typeof(long) || type == typeof(ulong))
|
||||
{
|
||||
return OperandType.I64;
|
||||
}
|
||||
else if (type == typeof(double))
|
||||
{
|
||||
return OperandType.FP64;
|
||||
}
|
||||
else if (type == typeof(float))
|
||||
{
|
||||
return OperandType.FP32;
|
||||
}
|
||||
else if (type == typeof(V128))
|
||||
{
|
||||
return OperandType.V128;
|
||||
}
|
||||
else if (type == typeof(void))
|
||||
{
|
||||
return OperandType.None;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentException($"Invalid type \"{type.Name}\".");
|
||||
}
|
||||
}
|
||||
|
||||
public Operand Call(Operand address, OperandType returnType, params Operand[] callArgs)
|
||||
{
|
||||
Operand[] args = new Operand[callArgs.Length + 1];
|
||||
|
||||
args[0] = address;
|
||||
|
||||
Array.Copy(callArgs, 0, args, 1, callArgs.Length);
|
||||
|
||||
if (returnType != OperandType.None)
|
||||
{
|
||||
return Add(Instruction.Call, Local(returnType), args);
|
||||
}
|
||||
else
|
||||
{
|
||||
return Add(Instruction.Call, default, args);
|
||||
}
|
||||
}
|
||||
|
||||
public void Tailcall(Operand address, params Operand[] callArgs)
|
||||
{
|
||||
Operand[] args = new Operand[callArgs.Length + 1];
|
||||
|
||||
args[0] = address;
|
||||
|
||||
Array.Copy(callArgs, 0, args, 1, callArgs.Length);
|
||||
|
||||
Add(Instruction.Tailcall, default, args);
|
||||
|
||||
_needsNewBlock = true;
|
||||
}
|
||||
|
||||
public Operand CompareAndSwap(Operand address, Operand expected, Operand desired)
|
||||
{
|
||||
return Add(Instruction.CompareAndSwap, Local(desired.Type), address, expected, desired);
|
||||
}
|
||||
|
||||
public Operand CompareAndSwap16(Operand address, Operand expected, Operand desired)
|
||||
{
|
||||
return Add(Instruction.CompareAndSwap16, Local(OperandType.I32), address, expected, desired);
|
||||
}
|
||||
|
||||
public Operand CompareAndSwap8(Operand address, Operand expected, Operand desired)
|
||||
{
|
||||
return Add(Instruction.CompareAndSwap8, Local(OperandType.I32), address, expected, desired);
|
||||
}
|
||||
|
||||
public Operand ConditionalSelect(Operand op1, Operand op2, Operand op3)
|
||||
{
|
||||
return Add(Instruction.ConditionalSelect, Local(op2.Type), op1, op2, op3);
|
||||
}
|
||||
|
||||
public Operand ConvertI64ToI32(Operand op1)
|
||||
{
|
||||
if (op1.Type != OperandType.I64)
|
||||
{
|
||||
throw new ArgumentException($"Invalid operand type \"{op1.Type}\".");
|
||||
}
|
||||
|
||||
return Add(Instruction.ConvertI64ToI32, Local(OperandType.I32), op1);
|
||||
}
|
||||
|
||||
public Operand ConvertToFP(OperandType type, Operand op1)
|
||||
{
|
||||
return Add(Instruction.ConvertToFP, Local(type), op1);
|
||||
}
|
||||
|
||||
public Operand ConvertToFPUI(OperandType type, Operand op1)
|
||||
{
|
||||
return Add(Instruction.ConvertToFPUI, Local(type), op1);
|
||||
}
|
||||
|
||||
public Operand Copy(Operand op1)
|
||||
{
|
||||
return Add(Instruction.Copy, Local(op1.Type), op1);
|
||||
}
|
||||
|
||||
public Operand Copy(Operand dest, Operand op1)
|
||||
{
|
||||
if (dest.Kind != OperandKind.Register &&
|
||||
(dest.Kind != OperandKind.LocalVariable || dest.GetLocalNumber() == 0))
|
||||
{
|
||||
throw new ArgumentException($"Destination operand must be a Register or a numbered LocalVariable.");
|
||||
}
|
||||
|
||||
return Add(Instruction.Copy, dest, op1);
|
||||
}
|
||||
|
||||
public Operand CountLeadingZeros(Operand op1)
|
||||
{
|
||||
return Add(Instruction.CountLeadingZeros, Local(op1.Type), op1);
|
||||
}
|
||||
|
||||
public Operand Divide(Operand op1, Operand op2)
|
||||
{
|
||||
return Add(Instruction.Divide, Local(op1.Type), op1, op2);
|
||||
}
|
||||
|
||||
public Operand DivideUI(Operand op1, Operand op2)
|
||||
{
|
||||
return Add(Instruction.DivideUI, Local(op1.Type), op1, op2);
|
||||
}
|
||||
|
||||
public Operand ICompare(Operand op1, Operand op2, Comparison comp)
|
||||
{
|
||||
return Add(Instruction.Compare, Local(OperandType.I32), op1, op2, Const((int)comp));
|
||||
}
|
||||
|
||||
public Operand ICompareEqual(Operand op1, Operand op2)
|
||||
{
|
||||
return ICompare(op1, op2, Comparison.Equal);
|
||||
}
|
||||
|
||||
public Operand ICompareGreater(Operand op1, Operand op2)
|
||||
{
|
||||
return ICompare(op1, op2, Comparison.Greater);
|
||||
}
|
||||
|
||||
public Operand ICompareGreaterOrEqual(Operand op1, Operand op2)
|
||||
{
|
||||
return ICompare(op1, op2, Comparison.GreaterOrEqual);
|
||||
}
|
||||
|
||||
public Operand ICompareGreaterOrEqualUI(Operand op1, Operand op2)
|
||||
{
|
||||
return ICompare(op1, op2, Comparison.GreaterOrEqualUI);
|
||||
}
|
||||
|
||||
public Operand ICompareGreaterUI(Operand op1, Operand op2)
|
||||
{
|
||||
return ICompare(op1, op2, Comparison.GreaterUI);
|
||||
}
|
||||
|
||||
public Operand ICompareLess(Operand op1, Operand op2)
|
||||
{
|
||||
return ICompare(op1, op2, Comparison.Less);
|
||||
}
|
||||
|
||||
public Operand ICompareLessOrEqual(Operand op1, Operand op2)
|
||||
{
|
||||
return ICompare(op1, op2, Comparison.LessOrEqual);
|
||||
}
|
||||
|
||||
public Operand ICompareLessOrEqualUI(Operand op1, Operand op2)
|
||||
{
|
||||
return ICompare(op1, op2, Comparison.LessOrEqualUI);
|
||||
}
|
||||
|
||||
public Operand ICompareLessUI(Operand op1, Operand op2)
|
||||
{
|
||||
return ICompare(op1, op2, Comparison.LessUI);
|
||||
}
|
||||
|
||||
public Operand ICompareNotEqual(Operand op1, Operand op2)
|
||||
{
|
||||
return ICompare(op1, op2, Comparison.NotEqual);
|
||||
}
|
||||
|
||||
public Operand Load(OperandType type, Operand address)
|
||||
{
|
||||
return Add(Instruction.Load, Local(type), address);
|
||||
}
|
||||
|
||||
public Operand Load16(Operand address)
|
||||
{
|
||||
return Add(Instruction.Load16, Local(OperandType.I32), address);
|
||||
}
|
||||
|
||||
public Operand Load8(Operand address)
|
||||
{
|
||||
return Add(Instruction.Load8, Local(OperandType.I32), address);
|
||||
}
|
||||
|
||||
public Operand LoadArgument(OperandType type, int index)
|
||||
{
|
||||
return Add(Instruction.LoadArgument, Local(type), Const(index));
|
||||
}
|
||||
|
||||
public void LoadFromContext()
|
||||
{
|
||||
_needsNewBlock = true;
|
||||
|
||||
Add(Instruction.LoadFromContext);
|
||||
}
|
||||
|
||||
public void MemoryBarrier()
|
||||
{
|
||||
Add(Instruction.MemoryBarrier);
|
||||
}
|
||||
|
||||
public Operand Multiply(Operand op1, Operand op2)
|
||||
{
|
||||
return Add(Instruction.Multiply, Local(op1.Type), op1, op2);
|
||||
}
|
||||
|
||||
public Operand Multiply64HighSI(Operand op1, Operand op2)
|
||||
{
|
||||
return Add(Instruction.Multiply64HighSI, Local(OperandType.I64), op1, op2);
|
||||
}
|
||||
|
||||
public Operand Multiply64HighUI(Operand op1, Operand op2)
|
||||
{
|
||||
return Add(Instruction.Multiply64HighUI, Local(OperandType.I64), op1, op2);
|
||||
}
|
||||
|
||||
public Operand Negate(Operand op1)
|
||||
{
|
||||
return Add(Instruction.Negate, Local(op1.Type), op1);
|
||||
}
|
||||
|
||||
public void Return()
|
||||
{
|
||||
Add(Instruction.Return);
|
||||
|
||||
_needsNewBlock = true;
|
||||
}
|
||||
|
||||
public void Return(Operand op1)
|
||||
{
|
||||
Add(Instruction.Return, default, op1);
|
||||
|
||||
_needsNewBlock = true;
|
||||
}
|
||||
|
||||
public Operand RotateRight(Operand op1, Operand op2)
|
||||
{
|
||||
return Add(Instruction.RotateRight, Local(op1.Type), op1, op2);
|
||||
}
|
||||
|
||||
public Operand ShiftLeft(Operand op1, Operand op2)
|
||||
{
|
||||
return Add(Instruction.ShiftLeft, Local(op1.Type), op1, op2);
|
||||
}
|
||||
|
||||
public Operand ShiftRightSI(Operand op1, Operand op2)
|
||||
{
|
||||
return Add(Instruction.ShiftRightSI, Local(op1.Type), op1, op2);
|
||||
}
|
||||
|
||||
public Operand ShiftRightUI(Operand op1, Operand op2)
|
||||
{
|
||||
return Add(Instruction.ShiftRightUI, Local(op1.Type), op1, op2);
|
||||
}
|
||||
|
||||
public Operand SignExtend16(OperandType type, Operand op1)
|
||||
{
|
||||
return Add(Instruction.SignExtend16, Local(type), op1);
|
||||
}
|
||||
|
||||
public Operand SignExtend32(OperandType type, Operand op1)
|
||||
{
|
||||
return Add(Instruction.SignExtend32, Local(type), op1);
|
||||
}
|
||||
|
||||
public Operand SignExtend8(OperandType type, Operand op1)
|
||||
{
|
||||
return Add(Instruction.SignExtend8, Local(type), op1);
|
||||
}
|
||||
|
||||
public void Store(Operand address, Operand value)
|
||||
{
|
||||
Add(Instruction.Store, default, address, value);
|
||||
}
|
||||
|
||||
public void Store16(Operand address, Operand value)
|
||||
{
|
||||
Add(Instruction.Store16, default, address, value);
|
||||
}
|
||||
|
||||
public void Store8(Operand address, Operand value)
|
||||
{
|
||||
Add(Instruction.Store8, default, address, value);
|
||||
}
|
||||
|
||||
public void StoreToContext()
|
||||
{
|
||||
Add(Instruction.StoreToContext);
|
||||
|
||||
_needsNewBlock = true;
|
||||
}
|
||||
|
||||
public Operand Subtract(Operand op1, Operand op2)
|
||||
{
|
||||
return Add(Instruction.Subtract, Local(op1.Type), op1, op2);
|
||||
}
|
||||
|
||||
public Operand VectorCreateScalar(Operand value)
|
||||
{
|
||||
return Add(Instruction.VectorCreateScalar, Local(OperandType.V128), value);
|
||||
}
|
||||
|
||||
public Operand VectorExtract(OperandType type, Operand vector, int index)
|
||||
{
|
||||
return Add(Instruction.VectorExtract, Local(type), vector, Const(index));
|
||||
}
|
||||
|
||||
public Operand VectorExtract16(Operand vector, int index)
|
||||
{
|
||||
return Add(Instruction.VectorExtract16, Local(OperandType.I32), vector, Const(index));
|
||||
}
|
||||
|
||||
public Operand VectorExtract8(Operand vector, int index)
|
||||
{
|
||||
return Add(Instruction.VectorExtract8, Local(OperandType.I32), vector, Const(index));
|
||||
}
|
||||
|
||||
public Operand VectorInsert(Operand vector, Operand value, int index)
|
||||
{
|
||||
return Add(Instruction.VectorInsert, Local(OperandType.V128), vector, value, Const(index));
|
||||
}
|
||||
|
||||
public Operand VectorInsert16(Operand vector, Operand value, int index)
|
||||
{
|
||||
return Add(Instruction.VectorInsert16, Local(OperandType.V128), vector, value, Const(index));
|
||||
}
|
||||
|
||||
public Operand VectorInsert8(Operand vector, Operand value, int index)
|
||||
{
|
||||
return Add(Instruction.VectorInsert8, Local(OperandType.V128), vector, value, Const(index));
|
||||
}
|
||||
|
||||
public Operand VectorOne()
|
||||
{
|
||||
return Add(Instruction.VectorOne, Local(OperandType.V128));
|
||||
}
|
||||
|
||||
public Operand VectorZero()
|
||||
{
|
||||
return Add(Instruction.VectorZero, Local(OperandType.V128));
|
||||
}
|
||||
|
||||
public Operand VectorZeroUpper64(Operand vector)
|
||||
{
|
||||
return Add(Instruction.VectorZeroUpper64, Local(OperandType.V128), vector);
|
||||
}
|
||||
|
||||
public Operand VectorZeroUpper96(Operand vector)
|
||||
{
|
||||
return Add(Instruction.VectorZeroUpper96, Local(OperandType.V128), vector);
|
||||
}
|
||||
|
||||
public Operand ZeroExtend16(OperandType type, Operand op1)
|
||||
{
|
||||
return Add(Instruction.ZeroExtend16, Local(type), op1);
|
||||
}
|
||||
|
||||
public Operand ZeroExtend32(OperandType type, Operand op1)
|
||||
{
|
||||
return Add(Instruction.ZeroExtend32, Local(type), op1);
|
||||
}
|
||||
|
||||
public Operand ZeroExtend8(OperandType type, Operand op1)
|
||||
{
|
||||
return Add(Instruction.ZeroExtend8, Local(type), op1);
|
||||
}
|
||||
|
||||
private void NewNextBlockIfNeeded()
|
||||
{
|
||||
if (_needsNewBlock)
|
||||
{
|
||||
NewNextBlock();
|
||||
}
|
||||
}
|
||||
|
||||
private Operand Add(Instruction inst, Operand dest = default)
|
||||
{
|
||||
NewNextBlockIfNeeded();
|
||||
|
||||
Operation operation = Operation.Factory.Operation(inst, dest);
|
||||
|
||||
_irBlock.Operations.AddLast(operation);
|
||||
|
||||
return dest;
|
||||
}
|
||||
|
||||
private Operand Add(Instruction inst, Operand dest, Operand[] sources)
|
||||
{
|
||||
NewNextBlockIfNeeded();
|
||||
|
||||
Operation operation = Operation.Factory.Operation(inst, dest, sources);
|
||||
|
||||
_irBlock.Operations.AddLast(operation);
|
||||
|
||||
return dest;
|
||||
}
|
||||
|
||||
private Operand Add(Instruction inst, Operand dest, Operand source0)
|
||||
{
|
||||
NewNextBlockIfNeeded();
|
||||
|
||||
Operation operation = Operation.Factory.Operation(inst, dest, source0);
|
||||
|
||||
_irBlock.Operations.AddLast(operation);
|
||||
|
||||
return dest;
|
||||
}
|
||||
|
||||
private Operand Add(Instruction inst, Operand dest, Operand source0, Operand source1)
|
||||
{
|
||||
NewNextBlockIfNeeded();
|
||||
|
||||
Operation operation = Operation.Factory.Operation(inst, dest, source0, source1);
|
||||
|
||||
_irBlock.Operations.AddLast(operation);
|
||||
|
||||
return dest;
|
||||
}
|
||||
|
||||
private Operand Add(Instruction inst, Operand dest, Operand source0, Operand source1, Operand source2)
|
||||
{
|
||||
NewNextBlockIfNeeded();
|
||||
|
||||
Operation operation = Operation.Factory.Operation(inst, dest, source0, source1, source2);
|
||||
|
||||
_irBlock.Operations.AddLast(operation);
|
||||
|
||||
return dest;
|
||||
}
|
||||
|
||||
public Operand AddIntrinsic(Intrinsic intrin, params Operand[] args)
|
||||
{
|
||||
return Add(intrin, Local(OperandType.V128), args);
|
||||
}
|
||||
|
||||
public Operand AddIntrinsicInt(Intrinsic intrin, params Operand[] args)
|
||||
{
|
||||
return Add(intrin, Local(OperandType.I32), args);
|
||||
}
|
||||
|
||||
public Operand AddIntrinsicLong(Intrinsic intrin, params Operand[] args)
|
||||
{
|
||||
return Add(intrin, Local(OperandType.I64), args);
|
||||
}
|
||||
|
||||
public void AddIntrinsicNoRet(Intrinsic intrin, params Operand[] args)
|
||||
{
|
||||
Add(intrin, default, args);
|
||||
}
|
||||
|
||||
private Operand Add(Intrinsic intrin, Operand dest, params Operand[] sources)
|
||||
{
|
||||
NewNextBlockIfNeeded();
|
||||
|
||||
Operation operation = Operation.Factory.Operation(intrin, dest, sources);
|
||||
|
||||
_irBlock.Operations.AddLast(operation);
|
||||
|
||||
return dest;
|
||||
}
|
||||
|
||||
private void BranchToLabel(Operand label, bool uncond, BasicBlockFrequency nextFreq)
|
||||
{
|
||||
if (!_irLabels.TryGetValue(label, out BasicBlock branchBlock))
|
||||
{
|
||||
branchBlock = new BasicBlock();
|
||||
|
||||
_irLabels.Add(label, branchBlock);
|
||||
}
|
||||
|
||||
if (uncond)
|
||||
{
|
||||
_irBlock.AddSuccessor(branchBlock);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Defer registration of successor to _irBlock so that the order of successors is correct.
|
||||
_ifBlock = branchBlock;
|
||||
}
|
||||
|
||||
_needsNewBlock = true;
|
||||
_nextBlockFreq = nextFreq;
|
||||
}
|
||||
|
||||
public void MarkLabel(Operand label, BasicBlockFrequency nextFreq = default)
|
||||
{
|
||||
_nextBlockFreq = nextFreq;
|
||||
|
||||
if (_irLabels.TryGetValue(label, out BasicBlock nextBlock))
|
||||
{
|
||||
nextBlock.Index = _irBlocks.Count;
|
||||
|
||||
_irBlocks.AddLast(nextBlock);
|
||||
|
||||
NextBlock(nextBlock);
|
||||
}
|
||||
else
|
||||
{
|
||||
NewNextBlock();
|
||||
|
||||
_irLabels.Add(label, _irBlock);
|
||||
}
|
||||
}
|
||||
|
||||
private void NewNextBlock()
|
||||
{
|
||||
BasicBlock block = new BasicBlock(_irBlocks.Count);
|
||||
|
||||
_irBlocks.AddLast(block);
|
||||
|
||||
NextBlock(block);
|
||||
}
|
||||
|
||||
private void NextBlock(BasicBlock nextBlock)
|
||||
{
|
||||
if (_irBlock?.SuccessorsCount == 0 && !EndsWithUnconditional(_irBlock))
|
||||
{
|
||||
_irBlock.AddSuccessor(nextBlock);
|
||||
|
||||
if (_ifBlock != null)
|
||||
{
|
||||
_irBlock.AddSuccessor(_ifBlock);
|
||||
|
||||
_ifBlock = null;
|
||||
}
|
||||
}
|
||||
|
||||
_irBlock = nextBlock;
|
||||
_irBlock.Frequency = _nextBlockFreq;
|
||||
|
||||
_needsNewBlock = false;
|
||||
_nextBlockFreq = BasicBlockFrequency.Default;
|
||||
}
|
||||
|
||||
private static bool EndsWithUnconditional(BasicBlock block)
|
||||
{
|
||||
Operation last = block.Operations.Last;
|
||||
|
||||
return last != default &&
|
||||
(last.Instruction == Instruction.Return ||
|
||||
last.Instruction == Instruction.Tailcall);
|
||||
}
|
||||
|
||||
public ControlFlowGraph GetControlFlowGraph()
|
||||
{
|
||||
return new ControlFlowGraph(_irBlocks.First, _irBlocks, _localsCount);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,745 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace ARMeilleure.Translation
|
||||
{
|
||||
/// <summary>
|
||||
/// An Augmented Interval Tree based off of the "TreeDictionary"'s Red-Black Tree. Allows fast overlap checking of ranges.
|
||||
/// </summary>
|
||||
/// <typeparam name="K">Key</typeparam>
|
||||
/// <typeparam name="V">Value</typeparam>
|
||||
class IntervalTree<K, V> where K : IComparable<K>
|
||||
{
|
||||
private const int ArrayGrowthSize = 32;
|
||||
|
||||
private const bool Black = true;
|
||||
private const bool Red = false;
|
||||
private IntervalTreeNode<K, V> _root = null;
|
||||
private int _count = 0;
|
||||
|
||||
public int Count => _count;
|
||||
|
||||
#region Public Methods
|
||||
|
||||
/// <summary>
|
||||
/// Gets the values of the interval whose key is <paramref name="key"/>.
|
||||
/// </summary>
|
||||
/// <param name="key">Key of the node value to get</param>
|
||||
/// <param name="value">Value with the given <paramref name="key"/></param>
|
||||
/// <returns>True if the key is on the dictionary, false otherwise</returns>
|
||||
public bool TryGet(K key, out V value)
|
||||
{
|
||||
IntervalTreeNode<K, V> node = GetNode(key);
|
||||
|
||||
if (node == null)
|
||||
{
|
||||
value = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
value = node.Value;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the start addresses of the intervals whose start and end keys overlap the given range.
|
||||
/// </summary>
|
||||
/// <param name="start">Start of the range</param>
|
||||
/// <param name="end">End of the range</param>
|
||||
/// <param name="overlaps">Overlaps array to place results in</param>
|
||||
/// <param name="overlapCount">Index to start writing results into the array. Defaults to 0</param>
|
||||
/// <returns>Number of intervals found</returns>
|
||||
public int Get(K start, K end, ref K[] overlaps, int overlapCount = 0)
|
||||
{
|
||||
GetKeys(_root, start, end, ref overlaps, ref overlapCount);
|
||||
|
||||
return overlapCount;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new interval into the tree whose start is <paramref name="start"/>, end is <paramref name="end"/> and value is <paramref name="value"/>.
|
||||
/// </summary>
|
||||
/// <param name="start">Start of the range to add</param>
|
||||
/// <param name="end">End of the range to insert</param>
|
||||
/// <param name="value">Value to add</param>
|
||||
/// <param name="updateFactoryCallback">Optional factory used to create a new value if <paramref name="start"/> is already on the tree</param>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="value"/> is null</exception>
|
||||
/// <returns>True if the value was added, false if the start key was already in the dictionary</returns>
|
||||
public bool AddOrUpdate(K start, K end, V value, Func<K, V, V> updateFactoryCallback)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(value);
|
||||
|
||||
return BSTInsert(start, end, value, updateFactoryCallback, out IntervalTreeNode<K, V> node);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an existing or adds a new interval into the tree whose start is <paramref name="start"/>, end is <paramref name="end"/> and value is <paramref name="value"/>.
|
||||
/// </summary>
|
||||
/// <param name="start">Start of the range to add</param>
|
||||
/// <param name="end">End of the range to insert</param>
|
||||
/// <param name="value">Value to add</param>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="value"/> is null</exception>
|
||||
/// <returns><paramref name="value"/> if <paramref name="start"/> is not yet on the tree, or the existing value otherwise</returns>
|
||||
public V GetOrAdd(K start, K end, V value)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(value);
|
||||
|
||||
BSTInsert(start, end, value, null, out IntervalTreeNode<K, V> node);
|
||||
return node.Value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a value from the tree, searching for it with <paramref name="key"/>.
|
||||
/// </summary>
|
||||
/// <param name="key">Key of the node to remove</param>
|
||||
/// <returns>Number of deleted values</returns>
|
||||
public int Remove(K key)
|
||||
{
|
||||
int removed = Delete(key);
|
||||
|
||||
_count -= removed;
|
||||
|
||||
return removed;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds all the nodes in the dictionary into <paramref name="list"/>.
|
||||
/// </summary>
|
||||
/// <returns>A list of all values sorted by Key Order</returns>
|
||||
public List<V> AsList()
|
||||
{
|
||||
List<V> list = new List<V>();
|
||||
|
||||
AddToList(_root, list);
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Methods (BST)
|
||||
|
||||
/// <summary>
|
||||
/// Adds all values that are children of or contained within <paramref name="node"/> into <paramref name="list"/>, in Key Order.
|
||||
/// </summary>
|
||||
/// <param name="node">The node to search for values within</param>
|
||||
/// <param name="list">The list to add values to</param>
|
||||
private void AddToList(IntervalTreeNode<K, V> node, List<V> list)
|
||||
{
|
||||
if (node == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
AddToList(node.Left, list);
|
||||
|
||||
list.Add(node.Value);
|
||||
|
||||
AddToList(node.Right, list);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieve the node reference whose key is <paramref name="key"/>, or null if no such node exists.
|
||||
/// </summary>
|
||||
/// <param name="key">Key of the node to get</param>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="key"/> is null</exception>
|
||||
/// <returns>Node reference in the tree</returns>
|
||||
private IntervalTreeNode<K, V> GetNode(K key)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(key);
|
||||
|
||||
IntervalTreeNode<K, V> node = _root;
|
||||
while (node != null)
|
||||
{
|
||||
int cmp = key.CompareTo(node.Start);
|
||||
if (cmp < 0)
|
||||
{
|
||||
node = node.Left;
|
||||
}
|
||||
else if (cmp > 0)
|
||||
{
|
||||
node = node.Right;
|
||||
}
|
||||
else
|
||||
{
|
||||
return node;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieve all keys that overlap the given start and end keys.
|
||||
/// </summary>
|
||||
/// <param name="start">Start of the range</param>
|
||||
/// <param name="end">End of the range</param>
|
||||
/// <param name="overlaps">Overlaps array to place results in</param>
|
||||
/// <param name="overlapCount">Overlaps count to update</param>
|
||||
private void GetKeys(IntervalTreeNode<K, V> node, K start, K end, ref K[] overlaps, ref int overlapCount)
|
||||
{
|
||||
if (node == null || start.CompareTo(node.Max) >= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
GetKeys(node.Left, start, end, ref overlaps, ref overlapCount);
|
||||
|
||||
bool endsOnRight = end.CompareTo(node.Start) > 0;
|
||||
if (endsOnRight)
|
||||
{
|
||||
if (start.CompareTo(node.End) < 0)
|
||||
{
|
||||
if (overlaps.Length >= overlapCount)
|
||||
{
|
||||
Array.Resize(ref overlaps, overlapCount + ArrayGrowthSize);
|
||||
}
|
||||
|
||||
overlaps[overlapCount++] = node.Start;
|
||||
}
|
||||
|
||||
GetKeys(node.Right, start, end, ref overlaps, ref overlapCount);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Propagate an increase in max value starting at the given node, heading up the tree.
|
||||
/// This should only be called if the max increases - not for rebalancing or removals.
|
||||
/// </summary>
|
||||
/// <param name="node">The node to start propagating from</param>
|
||||
private void PropagateIncrease(IntervalTreeNode<K, V> node)
|
||||
{
|
||||
K max = node.Max;
|
||||
IntervalTreeNode<K, V> ptr = node;
|
||||
|
||||
while ((ptr = ptr.Parent) != null)
|
||||
{
|
||||
if (max.CompareTo(ptr.Max) > 0)
|
||||
{
|
||||
ptr.Max = max;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Propagate recalculating max value starting at the given node, heading up the tree.
|
||||
/// This fully recalculates the max value from all children when there is potential for it to decrease.
|
||||
/// </summary>
|
||||
/// <param name="node">The node to start propagating from</param>
|
||||
private void PropagateFull(IntervalTreeNode<K, V> node)
|
||||
{
|
||||
IntervalTreeNode<K, V> ptr = node;
|
||||
|
||||
do
|
||||
{
|
||||
K max = ptr.End;
|
||||
|
||||
if (ptr.Left != null && ptr.Left.Max.CompareTo(max) > 0)
|
||||
{
|
||||
max = ptr.Left.Max;
|
||||
}
|
||||
|
||||
if (ptr.Right != null && ptr.Right.Max.CompareTo(max) > 0)
|
||||
{
|
||||
max = ptr.Right.Max;
|
||||
}
|
||||
|
||||
ptr.Max = max;
|
||||
} while ((ptr = ptr.Parent) != null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Insertion Mechanism for the interval tree. Similar to a BST insert, with the start of the range as the key.
|
||||
/// Iterates the tree starting from the root and inserts a new node where all children in the left subtree are less than <paramref name="start"/>, and all children in the right subtree are greater than <paramref name="start"/>.
|
||||
/// Each node can contain multiple values, and has an end address which is the maximum of all those values.
|
||||
/// Post insertion, the "max" value of the node and all parents are updated.
|
||||
/// </summary>
|
||||
/// <param name="start">Start of the range to insert</param>
|
||||
/// <param name="end">End of the range to insert</param>
|
||||
/// <param name="value">Value to insert</param>
|
||||
/// <param name="updateFactoryCallback">Optional factory used to create a new value if <paramref name="start"/> is already on the tree</param>
|
||||
/// <param name="outNode">Node that was inserted or modified</param>
|
||||
/// <returns>True if <paramref name="start"/> was not yet on the tree, false otherwise</returns>
|
||||
private bool BSTInsert(K start, K end, V value, Func<K, V, V> updateFactoryCallback, out IntervalTreeNode<K, V> outNode)
|
||||
{
|
||||
IntervalTreeNode<K, V> parent = null;
|
||||
IntervalTreeNode<K, V> node = _root;
|
||||
|
||||
while (node != null)
|
||||
{
|
||||
parent = node;
|
||||
int cmp = start.CompareTo(node.Start);
|
||||
if (cmp < 0)
|
||||
{
|
||||
node = node.Left;
|
||||
}
|
||||
else if (cmp > 0)
|
||||
{
|
||||
node = node.Right;
|
||||
}
|
||||
else
|
||||
{
|
||||
outNode = node;
|
||||
|
||||
if (updateFactoryCallback != null)
|
||||
{
|
||||
// Replace
|
||||
node.Value = updateFactoryCallback(start, node.Value);
|
||||
|
||||
int endCmp = end.CompareTo(node.End);
|
||||
|
||||
if (endCmp > 0)
|
||||
{
|
||||
node.End = end;
|
||||
if (end.CompareTo(node.Max) > 0)
|
||||
{
|
||||
node.Max = end;
|
||||
PropagateIncrease(node);
|
||||
RestoreBalanceAfterInsertion(node);
|
||||
}
|
||||
}
|
||||
else if (endCmp < 0)
|
||||
{
|
||||
node.End = end;
|
||||
PropagateFull(node);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
IntervalTreeNode<K, V> newNode = new IntervalTreeNode<K, V>(start, end, value, parent);
|
||||
if (newNode.Parent == null)
|
||||
{
|
||||
_root = newNode;
|
||||
}
|
||||
else if (start.CompareTo(parent.Start) < 0)
|
||||
{
|
||||
parent.Left = newNode;
|
||||
}
|
||||
else
|
||||
{
|
||||
parent.Right = newNode;
|
||||
}
|
||||
|
||||
PropagateIncrease(newNode);
|
||||
_count++;
|
||||
RestoreBalanceAfterInsertion(newNode);
|
||||
outNode = newNode;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the value from the dictionary after searching for it with <paramref name="key"/>.
|
||||
/// </summary>
|
||||
/// <param name="key">Key to search for</param>
|
||||
/// <returns>Number of deleted values</returns>
|
||||
private int Delete(K key)
|
||||
{
|
||||
IntervalTreeNode<K, V> nodeToDelete = GetNode(key);
|
||||
|
||||
if (nodeToDelete == null)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
IntervalTreeNode<K, V> replacementNode;
|
||||
|
||||
if (LeftOf(nodeToDelete) == null || RightOf(nodeToDelete) == null)
|
||||
{
|
||||
replacementNode = nodeToDelete;
|
||||
}
|
||||
else
|
||||
{
|
||||
replacementNode = PredecessorOf(nodeToDelete);
|
||||
}
|
||||
|
||||
IntervalTreeNode<K, V> tmp = LeftOf(replacementNode) ?? RightOf(replacementNode);
|
||||
|
||||
if (tmp != null)
|
||||
{
|
||||
tmp.Parent = ParentOf(replacementNode);
|
||||
}
|
||||
|
||||
if (ParentOf(replacementNode) == null)
|
||||
{
|
||||
_root = tmp;
|
||||
}
|
||||
else if (replacementNode == LeftOf(ParentOf(replacementNode)))
|
||||
{
|
||||
ParentOf(replacementNode).Left = tmp;
|
||||
}
|
||||
else
|
||||
{
|
||||
ParentOf(replacementNode).Right = tmp;
|
||||
}
|
||||
|
||||
if (replacementNode != nodeToDelete)
|
||||
{
|
||||
nodeToDelete.Start = replacementNode.Start;
|
||||
nodeToDelete.Value = replacementNode.Value;
|
||||
nodeToDelete.End = replacementNode.End;
|
||||
nodeToDelete.Max = replacementNode.Max;
|
||||
}
|
||||
|
||||
PropagateFull(replacementNode);
|
||||
|
||||
if (tmp != null && ColorOf(replacementNode) == Black)
|
||||
{
|
||||
RestoreBalanceAfterRemoval(tmp);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the node with the largest key where <paramref name="node"/> is considered the root node.
|
||||
/// </summary>
|
||||
/// <param name="node">Root Node</param>
|
||||
/// <returns>Node with the maximum key in the tree of <paramref name="node"/></returns>
|
||||
private static IntervalTreeNode<K, V> Maximum(IntervalTreeNode<K, V> node)
|
||||
{
|
||||
IntervalTreeNode<K, V> tmp = node;
|
||||
while (tmp.Right != null)
|
||||
{
|
||||
tmp = tmp.Right;
|
||||
}
|
||||
|
||||
return tmp;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds the node whose key is immediately less than <paramref name="node"/>.
|
||||
/// </summary>
|
||||
/// <param name="node">Node to find the predecessor of</param>
|
||||
/// <returns>Predecessor of <paramref name="node"/></returns>
|
||||
private static IntervalTreeNode<K, V> PredecessorOf(IntervalTreeNode<K, V> node)
|
||||
{
|
||||
if (node.Left != null)
|
||||
{
|
||||
return Maximum(node.Left);
|
||||
}
|
||||
IntervalTreeNode<K, V> parent = node.Parent;
|
||||
while (parent != null && node == parent.Left)
|
||||
{
|
||||
node = parent;
|
||||
parent = parent.Parent;
|
||||
}
|
||||
return parent;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Methods (RBL)
|
||||
|
||||
private void RestoreBalanceAfterRemoval(IntervalTreeNode<K, V> balanceNode)
|
||||
{
|
||||
IntervalTreeNode<K, V> ptr = balanceNode;
|
||||
|
||||
while (ptr != _root && ColorOf(ptr) == Black)
|
||||
{
|
||||
if (ptr == LeftOf(ParentOf(ptr)))
|
||||
{
|
||||
IntervalTreeNode<K, V> sibling = RightOf(ParentOf(ptr));
|
||||
|
||||
if (ColorOf(sibling) == Red)
|
||||
{
|
||||
SetColor(sibling, Black);
|
||||
SetColor(ParentOf(ptr), Red);
|
||||
RotateLeft(ParentOf(ptr));
|
||||
sibling = RightOf(ParentOf(ptr));
|
||||
}
|
||||
if (ColorOf(LeftOf(sibling)) == Black && ColorOf(RightOf(sibling)) == Black)
|
||||
{
|
||||
SetColor(sibling, Red);
|
||||
ptr = ParentOf(ptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ColorOf(RightOf(sibling)) == Black)
|
||||
{
|
||||
SetColor(LeftOf(sibling), Black);
|
||||
SetColor(sibling, Red);
|
||||
RotateRight(sibling);
|
||||
sibling = RightOf(ParentOf(ptr));
|
||||
}
|
||||
SetColor(sibling, ColorOf(ParentOf(ptr)));
|
||||
SetColor(ParentOf(ptr), Black);
|
||||
SetColor(RightOf(sibling), Black);
|
||||
RotateLeft(ParentOf(ptr));
|
||||
ptr = _root;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
IntervalTreeNode<K, V> sibling = LeftOf(ParentOf(ptr));
|
||||
|
||||
if (ColorOf(sibling) == Red)
|
||||
{
|
||||
SetColor(sibling, Black);
|
||||
SetColor(ParentOf(ptr), Red);
|
||||
RotateRight(ParentOf(ptr));
|
||||
sibling = LeftOf(ParentOf(ptr));
|
||||
}
|
||||
if (ColorOf(RightOf(sibling)) == Black && ColorOf(LeftOf(sibling)) == Black)
|
||||
{
|
||||
SetColor(sibling, Red);
|
||||
ptr = ParentOf(ptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ColorOf(LeftOf(sibling)) == Black)
|
||||
{
|
||||
SetColor(RightOf(sibling), Black);
|
||||
SetColor(sibling, Red);
|
||||
RotateLeft(sibling);
|
||||
sibling = LeftOf(ParentOf(ptr));
|
||||
}
|
||||
SetColor(sibling, ColorOf(ParentOf(ptr)));
|
||||
SetColor(ParentOf(ptr), Black);
|
||||
SetColor(LeftOf(sibling), Black);
|
||||
RotateRight(ParentOf(ptr));
|
||||
ptr = _root;
|
||||
}
|
||||
}
|
||||
}
|
||||
SetColor(ptr, Black);
|
||||
}
|
||||
|
||||
private void RestoreBalanceAfterInsertion(IntervalTreeNode<K, V> balanceNode)
|
||||
{
|
||||
SetColor(balanceNode, Red);
|
||||
while (balanceNode != null && balanceNode != _root && ColorOf(ParentOf(balanceNode)) == Red)
|
||||
{
|
||||
if (ParentOf(balanceNode) == LeftOf(ParentOf(ParentOf(balanceNode))))
|
||||
{
|
||||
IntervalTreeNode<K, V> sibling = RightOf(ParentOf(ParentOf(balanceNode)));
|
||||
|
||||
if (ColorOf(sibling) == Red)
|
||||
{
|
||||
SetColor(ParentOf(balanceNode), Black);
|
||||
SetColor(sibling, Black);
|
||||
SetColor(ParentOf(ParentOf(balanceNode)), Red);
|
||||
balanceNode = ParentOf(ParentOf(balanceNode));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (balanceNode == RightOf(ParentOf(balanceNode)))
|
||||
{
|
||||
balanceNode = ParentOf(balanceNode);
|
||||
RotateLeft(balanceNode);
|
||||
}
|
||||
SetColor(ParentOf(balanceNode), Black);
|
||||
SetColor(ParentOf(ParentOf(balanceNode)), Red);
|
||||
RotateRight(ParentOf(ParentOf(balanceNode)));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
IntervalTreeNode<K, V> sibling = LeftOf(ParentOf(ParentOf(balanceNode)));
|
||||
|
||||
if (ColorOf(sibling) == Red)
|
||||
{
|
||||
SetColor(ParentOf(balanceNode), Black);
|
||||
SetColor(sibling, Black);
|
||||
SetColor(ParentOf(ParentOf(balanceNode)), Red);
|
||||
balanceNode = ParentOf(ParentOf(balanceNode));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (balanceNode == LeftOf(ParentOf(balanceNode)))
|
||||
{
|
||||
balanceNode = ParentOf(balanceNode);
|
||||
RotateRight(balanceNode);
|
||||
}
|
||||
SetColor(ParentOf(balanceNode), Black);
|
||||
SetColor(ParentOf(ParentOf(balanceNode)), Red);
|
||||
RotateLeft(ParentOf(ParentOf(balanceNode)));
|
||||
}
|
||||
}
|
||||
}
|
||||
SetColor(_root, Black);
|
||||
}
|
||||
|
||||
private void RotateLeft(IntervalTreeNode<K, V> node)
|
||||
{
|
||||
if (node != null)
|
||||
{
|
||||
IntervalTreeNode<K, V> right = RightOf(node);
|
||||
node.Right = LeftOf(right);
|
||||
if (node.Right != null)
|
||||
{
|
||||
node.Right.Parent = node;
|
||||
}
|
||||
IntervalTreeNode<K, V> nodeParent = ParentOf(node);
|
||||
right.Parent = nodeParent;
|
||||
if (nodeParent == null)
|
||||
{
|
||||
_root = right;
|
||||
}
|
||||
else if (node == LeftOf(nodeParent))
|
||||
{
|
||||
nodeParent.Left = right;
|
||||
}
|
||||
else
|
||||
{
|
||||
nodeParent.Right = right;
|
||||
}
|
||||
right.Left = node;
|
||||
node.Parent = right;
|
||||
|
||||
PropagateFull(node);
|
||||
}
|
||||
}
|
||||
|
||||
private void RotateRight(IntervalTreeNode<K, V> node)
|
||||
{
|
||||
if (node != null)
|
||||
{
|
||||
IntervalTreeNode<K, V> left = LeftOf(node);
|
||||
node.Left = RightOf(left);
|
||||
if (node.Left != null)
|
||||
{
|
||||
node.Left.Parent = node;
|
||||
}
|
||||
IntervalTreeNode<K, V> nodeParent = ParentOf(node);
|
||||
left.Parent = nodeParent;
|
||||
if (nodeParent == null)
|
||||
{
|
||||
_root = left;
|
||||
}
|
||||
else if (node == RightOf(nodeParent))
|
||||
{
|
||||
nodeParent.Right = left;
|
||||
}
|
||||
else
|
||||
{
|
||||
nodeParent.Left = left;
|
||||
}
|
||||
left.Right = node;
|
||||
node.Parent = left;
|
||||
|
||||
PropagateFull(node);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Safety-Methods
|
||||
|
||||
// These methods save memory by allowing us to forego sentinel nil nodes, as well as serve as protection against NullReferenceExceptions.
|
||||
|
||||
/// <summary>
|
||||
/// Returns the color of <paramref name="node"/>, or Black if it is null.
|
||||
/// </summary>
|
||||
/// <param name="node">Node</param>
|
||||
/// <returns>The boolean color of <paramref name="node"/>, or black if null</returns>
|
||||
private static bool ColorOf(IntervalTreeNode<K, V> node)
|
||||
{
|
||||
return node == null || node.Color;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the color of <paramref name="node"/> node to <paramref name="color"/>.
|
||||
/// <br></br>
|
||||
/// This method does nothing if <paramref name="node"/> is null.
|
||||
/// </summary>
|
||||
/// <param name="node">Node to set the color of</param>
|
||||
/// <param name="color">Color (Boolean)</param>
|
||||
private static void SetColor(IntervalTreeNode<K, V> node, bool color)
|
||||
{
|
||||
if (node != null)
|
||||
{
|
||||
node.Color = color;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This method returns the left node of <paramref name="node"/>, or null if <paramref name="node"/> is null.
|
||||
/// </summary>
|
||||
/// <param name="node">Node to retrieve the left child from</param>
|
||||
/// <returns>Left child of <paramref name="node"/></returns>
|
||||
private static IntervalTreeNode<K, V> LeftOf(IntervalTreeNode<K, V> node)
|
||||
{
|
||||
return node?.Left;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This method returns the right node of <paramref name="node"/>, or null if <paramref name="node"/> is null.
|
||||
/// </summary>
|
||||
/// <param name="node">Node to retrieve the right child from</param>
|
||||
/// <returns>Right child of <paramref name="node"/></returns>
|
||||
private static IntervalTreeNode<K, V> RightOf(IntervalTreeNode<K, V> node)
|
||||
{
|
||||
return node?.Right;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the parent node of <paramref name="node"/>, or null if <paramref name="node"/> is null.
|
||||
/// </summary>
|
||||
/// <param name="node">Node to retrieve the parent from</param>
|
||||
/// <returns>Parent of <paramref name="node"/></returns>
|
||||
private static IntervalTreeNode<K, V> ParentOf(IntervalTreeNode<K, V> node)
|
||||
{
|
||||
return node?.Parent;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public bool ContainsKey(K key)
|
||||
{
|
||||
return GetNode(key) != null;
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
_root = null;
|
||||
_count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a node in the IntervalTree which contains start and end keys of type K, and a value of generic type V.
|
||||
/// </summary>
|
||||
/// <typeparam name="K">Key type of the node</typeparam>
|
||||
/// <typeparam name="V">Value type of the node</typeparam>
|
||||
class IntervalTreeNode<K, V>
|
||||
{
|
||||
public bool Color = true;
|
||||
public IntervalTreeNode<K, V> Left = null;
|
||||
public IntervalTreeNode<K, V> Right = null;
|
||||
public IntervalTreeNode<K, V> Parent = null;
|
||||
|
||||
/// <summary>
|
||||
/// The start of the range.
|
||||
/// </summary>
|
||||
public K Start;
|
||||
|
||||
/// <summary>
|
||||
/// The end of the range.
|
||||
/// </summary>
|
||||
public K End;
|
||||
|
||||
/// <summary>
|
||||
/// The maximum end value of this node and all its children.
|
||||
/// </summary>
|
||||
public K Max;
|
||||
|
||||
/// <summary>
|
||||
/// Value stored on this node.
|
||||
/// </summary>
|
||||
public V Value;
|
||||
|
||||
public IntervalTreeNode(K start, K end, V value, IntervalTreeNode<K, V> parent)
|
||||
{
|
||||
Start = start;
|
||||
End = end;
|
||||
Max = end;
|
||||
Value = value;
|
||||
Parent = parent;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,420 +0,0 @@
|
||||
using ARMeilleure.State;
|
||||
using Ryujinx.Common;
|
||||
using Ryujinx.Common.Logging;
|
||||
using System;
|
||||
using System.Buffers.Binary;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
|
||||
using static ARMeilleure.Translation.PTC.PtcFormatter;
|
||||
|
||||
namespace ARMeilleure.Translation.PTC
|
||||
{
|
||||
class PtcProfiler
|
||||
{
|
||||
private const string OuterHeaderMagicString = "Pohd\0\0\0\0";
|
||||
|
||||
private const uint InternalVersion = 1866; //! Not to be incremented manually for each change to the ARMeilleure project.
|
||||
|
||||
private const int SaveInterval = 30; // Seconds.
|
||||
|
||||
private const CompressionLevel SaveCompressionLevel = CompressionLevel.Fastest;
|
||||
|
||||
private readonly Ptc _ptc;
|
||||
|
||||
private readonly System.Timers.Timer _timer;
|
||||
|
||||
private readonly ulong _outerHeaderMagic;
|
||||
|
||||
private readonly ManualResetEvent _waitEvent;
|
||||
|
||||
private readonly object _lock;
|
||||
|
||||
private bool _disposed;
|
||||
|
||||
private Hash128 _lastHash;
|
||||
|
||||
public Dictionary<ulong, FuncProfile> ProfiledFuncs { get; private set; }
|
||||
|
||||
public bool Enabled { get; private set; }
|
||||
|
||||
public ulong StaticCodeStart { get; set; }
|
||||
public ulong StaticCodeSize { get; set; }
|
||||
|
||||
public PtcProfiler(Ptc ptc)
|
||||
{
|
||||
_ptc = ptc;
|
||||
|
||||
_timer = new System.Timers.Timer((double)SaveInterval * 1000d);
|
||||
_timer.Elapsed += PreSave;
|
||||
|
||||
_outerHeaderMagic = BinaryPrimitives.ReadUInt64LittleEndian(EncodingCache.UTF8NoBOM.GetBytes(OuterHeaderMagicString).AsSpan());
|
||||
|
||||
_waitEvent = new ManualResetEvent(true);
|
||||
|
||||
_lock = new object();
|
||||
|
||||
_disposed = false;
|
||||
|
||||
ProfiledFuncs = new Dictionary<ulong, FuncProfile>();
|
||||
|
||||
Enabled = false;
|
||||
}
|
||||
|
||||
public void AddEntry(ulong address, ExecutionMode mode, bool highCq)
|
||||
{
|
||||
if (IsAddressInStaticCodeRange(address))
|
||||
{
|
||||
Debug.Assert(!highCq);
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
ProfiledFuncs.TryAdd(address, new FuncProfile(mode, highCq: false));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateEntry(ulong address, ExecutionMode mode, bool highCq)
|
||||
{
|
||||
if (IsAddressInStaticCodeRange(address))
|
||||
{
|
||||
Debug.Assert(highCq);
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
Debug.Assert(ProfiledFuncs.ContainsKey(address));
|
||||
|
||||
ProfiledFuncs[address] = new FuncProfile(mode, highCq: true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsAddressInStaticCodeRange(ulong address)
|
||||
{
|
||||
return address >= StaticCodeStart && address < StaticCodeStart + StaticCodeSize;
|
||||
}
|
||||
|
||||
public ConcurrentQueue<(ulong address, FuncProfile funcProfile)> GetProfiledFuncsToTranslate(TranslatorCache<TranslatedFunction> funcs)
|
||||
{
|
||||
var profiledFuncsToTranslate = new ConcurrentQueue<(ulong address, FuncProfile funcProfile)>();
|
||||
|
||||
foreach (var profiledFunc in ProfiledFuncs)
|
||||
{
|
||||
if (!funcs.ContainsKey(profiledFunc.Key))
|
||||
{
|
||||
profiledFuncsToTranslate.Enqueue((profiledFunc.Key, profiledFunc.Value));
|
||||
}
|
||||
}
|
||||
|
||||
return profiledFuncsToTranslate;
|
||||
}
|
||||
|
||||
public void ClearEntries()
|
||||
{
|
||||
ProfiledFuncs.Clear();
|
||||
ProfiledFuncs.TrimExcess();
|
||||
}
|
||||
|
||||
public void PreLoad()
|
||||
{
|
||||
_lastHash = default;
|
||||
|
||||
string fileNameActual = $"{_ptc.CachePathActual}.info";
|
||||
string fileNameBackup = $"{_ptc.CachePathBackup}.info";
|
||||
|
||||
FileInfo fileInfoActual = new FileInfo(fileNameActual);
|
||||
FileInfo fileInfoBackup = new FileInfo(fileNameBackup);
|
||||
|
||||
if (fileInfoActual.Exists && fileInfoActual.Length != 0L)
|
||||
{
|
||||
if (!Load(fileNameActual, false))
|
||||
{
|
||||
if (fileInfoBackup.Exists && fileInfoBackup.Length != 0L)
|
||||
{
|
||||
Load(fileNameBackup, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (fileInfoBackup.Exists && fileInfoBackup.Length != 0L)
|
||||
{
|
||||
Load(fileNameBackup, true);
|
||||
}
|
||||
}
|
||||
|
||||
private bool Load(string fileName, bool isBackup)
|
||||
{
|
||||
using (FileStream compressedStream = new(fileName, FileMode.Open))
|
||||
using (DeflateStream deflateStream = new(compressedStream, CompressionMode.Decompress, true))
|
||||
{
|
||||
OuterHeader outerHeader = DeserializeStructure<OuterHeader>(compressedStream);
|
||||
|
||||
if (!outerHeader.IsHeaderValid())
|
||||
{
|
||||
InvalidateCompressedStream(compressedStream);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (outerHeader.Magic != _outerHeaderMagic)
|
||||
{
|
||||
InvalidateCompressedStream(compressedStream);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (outerHeader.InfoFileVersion != InternalVersion)
|
||||
{
|
||||
InvalidateCompressedStream(compressedStream);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (outerHeader.Endianness != Ptc.GetEndianness())
|
||||
{
|
||||
InvalidateCompressedStream(compressedStream);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
using (MemoryStream stream = new MemoryStream())
|
||||
{
|
||||
Debug.Assert(stream.Seek(0L, SeekOrigin.Begin) == 0L && stream.Length == 0L);
|
||||
|
||||
try
|
||||
{
|
||||
deflateStream.CopyTo(stream);
|
||||
}
|
||||
catch
|
||||
{
|
||||
InvalidateCompressedStream(compressedStream);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Debug.Assert(stream.Position == stream.Length);
|
||||
|
||||
stream.Seek(0L, SeekOrigin.Begin);
|
||||
|
||||
Hash128 expectedHash = DeserializeStructure<Hash128>(stream);
|
||||
|
||||
Hash128 actualHash = XXHash128.ComputeHash(GetReadOnlySpan(stream));
|
||||
|
||||
if (actualHash != expectedHash)
|
||||
{
|
||||
InvalidateCompressedStream(compressedStream);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
ProfiledFuncs = Deserialize(stream);
|
||||
|
||||
Debug.Assert(stream.Position == stream.Length);
|
||||
|
||||
_lastHash = actualHash;
|
||||
}
|
||||
}
|
||||
|
||||
long fileSize = new FileInfo(fileName).Length;
|
||||
|
||||
Logger.Info?.Print(LogClass.Ptc, $"{(isBackup ? "Loaded Backup Profiling Info" : "Loaded Profiling Info")} (size: {fileSize} bytes, profiled functions: {ProfiledFuncs.Count}).");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static Dictionary<ulong, FuncProfile> Deserialize(Stream stream)
|
||||
{
|
||||
return DeserializeDictionary<ulong, FuncProfile>(stream, (stream) => DeserializeStructure<FuncProfile>(stream));
|
||||
}
|
||||
|
||||
private ReadOnlySpan<byte> GetReadOnlySpan(MemoryStream memoryStream)
|
||||
{
|
||||
return new(memoryStream.GetBuffer(), (int)memoryStream.Position, (int)memoryStream.Length - (int)memoryStream.Position);
|
||||
}
|
||||
|
||||
private void InvalidateCompressedStream(FileStream compressedStream)
|
||||
{
|
||||
compressedStream.SetLength(0L);
|
||||
}
|
||||
|
||||
private void PreSave(object source, System.Timers.ElapsedEventArgs e)
|
||||
{
|
||||
_waitEvent.Reset();
|
||||
|
||||
string fileNameActual = $"{_ptc.CachePathActual}.info";
|
||||
string fileNameBackup = $"{_ptc.CachePathBackup}.info";
|
||||
|
||||
FileInfo fileInfoActual = new FileInfo(fileNameActual);
|
||||
|
||||
if (fileInfoActual.Exists && fileInfoActual.Length != 0L)
|
||||
{
|
||||
File.Copy(fileNameActual, fileNameBackup, true);
|
||||
}
|
||||
|
||||
Save(fileNameActual);
|
||||
|
||||
_waitEvent.Set();
|
||||
}
|
||||
|
||||
private void Save(string fileName)
|
||||
{
|
||||
int profiledFuncsCount;
|
||||
|
||||
OuterHeader outerHeader = new OuterHeader();
|
||||
|
||||
outerHeader.Magic = _outerHeaderMagic;
|
||||
|
||||
outerHeader.InfoFileVersion = InternalVersion;
|
||||
outerHeader.Endianness = Ptc.GetEndianness();
|
||||
|
||||
outerHeader.SetHeaderHash();
|
||||
|
||||
using (MemoryStream stream = new MemoryStream())
|
||||
{
|
||||
Debug.Assert(stream.Seek(0L, SeekOrigin.Begin) == 0L && stream.Length == 0L);
|
||||
|
||||
stream.Seek((long)Unsafe.SizeOf<Hash128>(), SeekOrigin.Begin);
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
Serialize(stream, ProfiledFuncs);
|
||||
|
||||
profiledFuncsCount = ProfiledFuncs.Count;
|
||||
}
|
||||
|
||||
Debug.Assert(stream.Position == stream.Length);
|
||||
|
||||
stream.Seek((long)Unsafe.SizeOf<Hash128>(), SeekOrigin.Begin);
|
||||
Hash128 hash = XXHash128.ComputeHash(GetReadOnlySpan(stream));
|
||||
|
||||
stream.Seek(0L, SeekOrigin.Begin);
|
||||
SerializeStructure(stream, hash);
|
||||
|
||||
if (hash == _lastHash)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
using (FileStream compressedStream = new(fileName, FileMode.OpenOrCreate))
|
||||
using (DeflateStream deflateStream = new(compressedStream, SaveCompressionLevel, true))
|
||||
{
|
||||
try
|
||||
{
|
||||
SerializeStructure(compressedStream, outerHeader);
|
||||
|
||||
stream.WriteTo(deflateStream);
|
||||
|
||||
_lastHash = hash;
|
||||
}
|
||||
catch
|
||||
{
|
||||
compressedStream.Position = 0L;
|
||||
|
||||
_lastHash = default;
|
||||
}
|
||||
|
||||
if (compressedStream.Position < compressedStream.Length)
|
||||
{
|
||||
compressedStream.SetLength(compressedStream.Position);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
long fileSize = new FileInfo(fileName).Length;
|
||||
|
||||
if (fileSize != 0L)
|
||||
{
|
||||
Logger.Info?.Print(LogClass.Ptc, $"Saved Profiling Info (size: {fileSize} bytes, profiled functions: {profiledFuncsCount}).");
|
||||
}
|
||||
}
|
||||
|
||||
private void Serialize(Stream stream, Dictionary<ulong, FuncProfile> profiledFuncs)
|
||||
{
|
||||
SerializeDictionary(stream, profiledFuncs, (stream, structure) => SerializeStructure(stream, structure));
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1/*, Size = 29*/)]
|
||||
private struct OuterHeader
|
||||
{
|
||||
public ulong Magic;
|
||||
|
||||
public uint InfoFileVersion;
|
||||
|
||||
public bool Endianness;
|
||||
|
||||
public Hash128 HeaderHash;
|
||||
|
||||
public void SetHeaderHash()
|
||||
{
|
||||
Span<OuterHeader> spanHeader = MemoryMarshal.CreateSpan(ref this, 1);
|
||||
|
||||
HeaderHash = XXHash128.ComputeHash(MemoryMarshal.AsBytes(spanHeader).Slice(0, Unsafe.SizeOf<OuterHeader>() - Unsafe.SizeOf<Hash128>()));
|
||||
}
|
||||
|
||||
public bool IsHeaderValid()
|
||||
{
|
||||
Span<OuterHeader> spanHeader = MemoryMarshal.CreateSpan(ref this, 1);
|
||||
|
||||
return XXHash128.ComputeHash(MemoryMarshal.AsBytes(spanHeader).Slice(0, Unsafe.SizeOf<OuterHeader>() - Unsafe.SizeOf<Hash128>())) == HeaderHash;
|
||||
}
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1/*, Size = 5*/)]
|
||||
public struct FuncProfile
|
||||
{
|
||||
public ExecutionMode Mode;
|
||||
public bool HighCq;
|
||||
|
||||
public FuncProfile(ExecutionMode mode, bool highCq)
|
||||
{
|
||||
Mode = mode;
|
||||
HighCq = highCq;
|
||||
}
|
||||
}
|
||||
|
||||
public void Start()
|
||||
{
|
||||
if (_ptc.State == PtcState.Enabled ||
|
||||
_ptc.State == PtcState.Continuing)
|
||||
{
|
||||
Enabled = true;
|
||||
|
||||
_timer.Enabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
Enabled = false;
|
||||
|
||||
if (!_disposed)
|
||||
{
|
||||
_timer.Enabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
public void Wait()
|
||||
{
|
||||
_waitEvent.WaitOne();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (!_disposed)
|
||||
{
|
||||
_disposed = true;
|
||||
|
||||
_timer.Elapsed -= PreSave;
|
||||
_timer.Dispose();
|
||||
|
||||
Wait();
|
||||
_waitEvent.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,394 +0,0 @@
|
||||
using ARMeilleure.IntermediateRepresentation;
|
||||
using ARMeilleure.State;
|
||||
using System;
|
||||
using System.Numerics;
|
||||
using System.Runtime.Intrinsics;
|
||||
using System.Runtime.Intrinsics.X86;
|
||||
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
|
||||
using static ARMeilleure.IntermediateRepresentation.Operation.Factory;
|
||||
|
||||
namespace ARMeilleure.Translation
|
||||
{
|
||||
static class RegisterUsage
|
||||
{
|
||||
private const int RegsCount = 32;
|
||||
private const int RegsMask = RegsCount - 1;
|
||||
|
||||
private readonly struct RegisterMask : IEquatable<RegisterMask>
|
||||
{
|
||||
public long IntMask => Mask.GetElement(0);
|
||||
public long VecMask => Mask.GetElement(1);
|
||||
|
||||
public Vector128<long> Mask { get; }
|
||||
|
||||
public RegisterMask(Vector128<long> mask)
|
||||
{
|
||||
Mask = mask;
|
||||
}
|
||||
|
||||
public RegisterMask(long intMask, long vecMask)
|
||||
{
|
||||
Mask = Vector128.Create(intMask, vecMask);
|
||||
}
|
||||
|
||||
public static RegisterMask operator &(RegisterMask x, RegisterMask y)
|
||||
{
|
||||
if (Sse2.IsSupported)
|
||||
{
|
||||
return new RegisterMask(Sse2.And(x.Mask, y.Mask));
|
||||
}
|
||||
|
||||
return new RegisterMask(x.IntMask & y.IntMask, x.VecMask & y.VecMask);
|
||||
}
|
||||
|
||||
public static RegisterMask operator |(RegisterMask x, RegisterMask y)
|
||||
{
|
||||
if (Sse2.IsSupported)
|
||||
{
|
||||
return new RegisterMask(Sse2.Or(x.Mask, y.Mask));
|
||||
}
|
||||
|
||||
return new RegisterMask(x.IntMask | y.IntMask, x.VecMask | y.VecMask);
|
||||
}
|
||||
|
||||
public static RegisterMask operator ~(RegisterMask x)
|
||||
{
|
||||
if (Sse2.IsSupported)
|
||||
{
|
||||
return new RegisterMask(Sse2.AndNot(x.Mask, Vector128<long>.AllBitsSet));
|
||||
}
|
||||
|
||||
return new RegisterMask(~x.IntMask, ~x.VecMask);
|
||||
}
|
||||
|
||||
public static bool operator ==(RegisterMask x, RegisterMask y)
|
||||
{
|
||||
return x.Equals(y);
|
||||
}
|
||||
|
||||
public static bool operator !=(RegisterMask x, RegisterMask y)
|
||||
{
|
||||
return !x.Equals(y);
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return obj is RegisterMask regMask && Equals(regMask);
|
||||
}
|
||||
|
||||
public bool Equals(RegisterMask other)
|
||||
{
|
||||
return Mask.Equals(other.Mask);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return Mask.GetHashCode();
|
||||
}
|
||||
}
|
||||
|
||||
public static void RunPass(ControlFlowGraph cfg, ExecutionMode mode)
|
||||
{
|
||||
// Compute local register inputs and outputs used inside blocks.
|
||||
RegisterMask[] localInputs = new RegisterMask[cfg.Blocks.Count];
|
||||
RegisterMask[] localOutputs = new RegisterMask[cfg.Blocks.Count];
|
||||
|
||||
for (BasicBlock block = cfg.Blocks.First; block != null; block = block.ListNext)
|
||||
{
|
||||
for (Operation node = block.Operations.First; node != default; node = node.ListNext)
|
||||
{
|
||||
for (int index = 0; index < node.SourcesCount; index++)
|
||||
{
|
||||
Operand source = node.GetSource(index);
|
||||
|
||||
if (source.Kind == OperandKind.Register)
|
||||
{
|
||||
Register register = source.GetRegister();
|
||||
|
||||
localInputs[block.Index] |= GetMask(register) & ~localOutputs[block.Index];
|
||||
}
|
||||
}
|
||||
|
||||
if (node.Destination != default && node.Destination.Kind == OperandKind.Register)
|
||||
{
|
||||
localOutputs[block.Index] |= GetMask(node.Destination.GetRegister());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Compute global register inputs and outputs used across blocks.
|
||||
RegisterMask[] globalCmnOutputs = new RegisterMask[cfg.Blocks.Count];
|
||||
|
||||
RegisterMask[] globalInputs = new RegisterMask[cfg.Blocks.Count];
|
||||
RegisterMask[] globalOutputs = new RegisterMask[cfg.Blocks.Count];
|
||||
|
||||
bool modified;
|
||||
bool firstPass = true;
|
||||
|
||||
do
|
||||
{
|
||||
modified = false;
|
||||
|
||||
// Compute register outputs.
|
||||
for (int index = cfg.PostOrderBlocks.Length - 1; index >= 0; index--)
|
||||
{
|
||||
BasicBlock block = cfg.PostOrderBlocks[index];
|
||||
|
||||
if (block.Predecessors.Count != 0 && !HasContextLoad(block))
|
||||
{
|
||||
BasicBlock predecessor = block.Predecessors[0];
|
||||
|
||||
RegisterMask cmnOutputs = localOutputs[predecessor.Index] | globalCmnOutputs[predecessor.Index];
|
||||
RegisterMask outputs = globalOutputs[predecessor.Index];
|
||||
|
||||
for (int pIndex = 1; pIndex < block.Predecessors.Count; pIndex++)
|
||||
{
|
||||
predecessor = block.Predecessors[pIndex];
|
||||
|
||||
cmnOutputs &= localOutputs[predecessor.Index] | globalCmnOutputs[predecessor.Index];
|
||||
outputs |= globalOutputs[predecessor.Index];
|
||||
}
|
||||
|
||||
globalInputs[block.Index] |= outputs & ~cmnOutputs;
|
||||
|
||||
if (!firstPass)
|
||||
{
|
||||
cmnOutputs &= globalCmnOutputs[block.Index];
|
||||
}
|
||||
|
||||
modified |= Exchange(globalCmnOutputs, block.Index, cmnOutputs);
|
||||
outputs |= localOutputs[block.Index];
|
||||
modified |= Exchange(globalOutputs, block.Index, globalOutputs[block.Index] | outputs);
|
||||
}
|
||||
else
|
||||
{
|
||||
modified |= Exchange(globalOutputs, block.Index, localOutputs[block.Index]);
|
||||
}
|
||||
}
|
||||
|
||||
// Compute register inputs.
|
||||
for (int index = 0; index < cfg.PostOrderBlocks.Length; index++)
|
||||
{
|
||||
BasicBlock block = cfg.PostOrderBlocks[index];
|
||||
|
||||
RegisterMask inputs = localInputs[block.Index];
|
||||
|
||||
for (int i = 0; i < block.SuccessorsCount; i++)
|
||||
{
|
||||
inputs |= globalInputs[block.GetSuccessor(i).Index];
|
||||
}
|
||||
|
||||
inputs &= ~globalCmnOutputs[block.Index];
|
||||
|
||||
modified |= Exchange(globalInputs, block.Index, globalInputs[block.Index] | inputs);
|
||||
}
|
||||
|
||||
firstPass = false;
|
||||
}
|
||||
while (modified);
|
||||
|
||||
// Insert load and store context instructions where needed.
|
||||
for (BasicBlock block = cfg.Blocks.First; block != null; block = block.ListNext)
|
||||
{
|
||||
bool hasContextLoad = HasContextLoad(block);
|
||||
|
||||
if (hasContextLoad)
|
||||
{
|
||||
block.Operations.Remove(block.Operations.First);
|
||||
}
|
||||
|
||||
Operand arg = default;
|
||||
|
||||
// The only block without any predecessor should be the entry block.
|
||||
// It always needs a context load as it is the first block to run.
|
||||
if (block.Predecessors.Count == 0 || hasContextLoad)
|
||||
{
|
||||
long vecMask = globalInputs[block.Index].VecMask;
|
||||
long intMask = globalInputs[block.Index].IntMask;
|
||||
|
||||
if (vecMask != 0 || intMask != 0)
|
||||
{
|
||||
arg = Local(OperandType.I64);
|
||||
|
||||
Operation loadArg = block.Operations.AddFirst(Operation(Instruction.LoadArgument, arg, Const(0)));
|
||||
|
||||
LoadLocals(block, vecMask, RegisterType.Vector, mode, loadArg, arg);
|
||||
LoadLocals(block, intMask, RegisterType.Integer, mode, loadArg, arg);
|
||||
}
|
||||
}
|
||||
|
||||
bool hasContextStore = HasContextStore(block);
|
||||
|
||||
if (hasContextStore)
|
||||
{
|
||||
block.Operations.Remove(block.Operations.Last);
|
||||
}
|
||||
|
||||
if (EndsWithReturn(block) || hasContextStore)
|
||||
{
|
||||
long vecMask = globalOutputs[block.Index].VecMask;
|
||||
long intMask = globalOutputs[block.Index].IntMask;
|
||||
|
||||
if (vecMask != 0 || intMask != 0)
|
||||
{
|
||||
if (arg == default)
|
||||
{
|
||||
arg = Local(OperandType.I64);
|
||||
|
||||
block.Append(Operation(Instruction.LoadArgument, arg, Const(0)));
|
||||
}
|
||||
|
||||
StoreLocals(block, intMask, RegisterType.Integer, mode, arg);
|
||||
StoreLocals(block, vecMask, RegisterType.Vector, mode, arg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static bool HasContextLoad(BasicBlock block)
|
||||
{
|
||||
return StartsWith(block, Instruction.LoadFromContext) && block.Operations.First.SourcesCount == 0;
|
||||
}
|
||||
|
||||
private static bool HasContextStore(BasicBlock block)
|
||||
{
|
||||
return EndsWith(block, Instruction.StoreToContext) && block.Operations.Last.SourcesCount == 0;
|
||||
}
|
||||
|
||||
private static bool StartsWith(BasicBlock block, Instruction inst)
|
||||
{
|
||||
if (block.Operations.Count > 0)
|
||||
{
|
||||
Operation first = block.Operations.First;
|
||||
|
||||
return first != default && first.Instruction == inst;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool EndsWith(BasicBlock block, Instruction inst)
|
||||
{
|
||||
if (block.Operations.Count > 0)
|
||||
{
|
||||
Operation last = block.Operations.Last;
|
||||
|
||||
return last != default && last.Instruction == inst;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static RegisterMask GetMask(Register register)
|
||||
{
|
||||
long intMask = 0;
|
||||
long vecMask = 0;
|
||||
|
||||
switch (register.Type)
|
||||
{
|
||||
case RegisterType.Flag: intMask = (1L << RegsCount) << register.Index; break;
|
||||
case RegisterType.Integer: intMask = 1L << register.Index; break;
|
||||
case RegisterType.FpFlag: vecMask = (1L << RegsCount) << register.Index; break;
|
||||
case RegisterType.Vector: vecMask = 1L << register.Index; break;
|
||||
}
|
||||
|
||||
return new RegisterMask(intMask, vecMask);
|
||||
}
|
||||
|
||||
private static bool Exchange(RegisterMask[] masks, int blkIndex, RegisterMask value)
|
||||
{
|
||||
ref RegisterMask curValue = ref masks[blkIndex];
|
||||
|
||||
bool changed = curValue != value;
|
||||
|
||||
curValue = value;
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
private static void LoadLocals(
|
||||
BasicBlock block,
|
||||
long inputs,
|
||||
RegisterType baseType,
|
||||
ExecutionMode mode,
|
||||
Operation loadArg,
|
||||
Operand arg)
|
||||
{
|
||||
while (inputs != 0)
|
||||
{
|
||||
int bit = 63 - BitOperations.LeadingZeroCount((ulong)inputs);
|
||||
|
||||
Operand dest = GetRegFromBit(bit, baseType, mode);
|
||||
Operand offset = Const((long)NativeContext.GetRegisterOffset(dest.GetRegister()));
|
||||
Operand addr = Local(OperandType.I64);
|
||||
|
||||
block.Operations.AddAfter(loadArg, Operation(Instruction.Load, dest, addr));
|
||||
block.Operations.AddAfter(loadArg, Operation(Instruction.Add, addr, arg, offset));
|
||||
|
||||
inputs &= ~(1L << bit);
|
||||
}
|
||||
}
|
||||
|
||||
private static void StoreLocals(
|
||||
BasicBlock block,
|
||||
long outputs,
|
||||
RegisterType baseType,
|
||||
ExecutionMode mode,
|
||||
Operand arg)
|
||||
{
|
||||
while (outputs != 0)
|
||||
{
|
||||
int bit = BitOperations.TrailingZeroCount(outputs);
|
||||
|
||||
Operand source = GetRegFromBit(bit, baseType, mode);
|
||||
Operand offset = Const((long)NativeContext.GetRegisterOffset(source.GetRegister()));
|
||||
Operand addr = Local(OperandType.I64);
|
||||
|
||||
block.Append(Operation(Instruction.Add, addr, arg, offset));
|
||||
block.Append(Operation(Instruction.Store, default, addr, source));
|
||||
|
||||
outputs &= ~(1L << bit);
|
||||
}
|
||||
}
|
||||
|
||||
private static Operand GetRegFromBit(int bit, RegisterType baseType, ExecutionMode mode)
|
||||
{
|
||||
if (bit < RegsCount)
|
||||
{
|
||||
return Register(bit, baseType, GetOperandType(baseType, mode));
|
||||
}
|
||||
else if (baseType == RegisterType.Integer)
|
||||
{
|
||||
return Register(bit & RegsMask, RegisterType.Flag, OperandType.I32);
|
||||
}
|
||||
else if (baseType == RegisterType.Vector)
|
||||
{
|
||||
return Register(bit & RegsMask, RegisterType.FpFlag, OperandType.I32);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(bit));
|
||||
}
|
||||
}
|
||||
|
||||
private static OperandType GetOperandType(RegisterType type, ExecutionMode mode)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case RegisterType.Flag: return OperandType.I32;
|
||||
case RegisterType.FpFlag: return OperandType.I32;
|
||||
case RegisterType.Integer: return (mode == ExecutionMode.Aarch64) ? OperandType.I64 : OperandType.I32;
|
||||
case RegisterType.Vector: return OperandType.V128;
|
||||
}
|
||||
|
||||
throw new ArgumentException($"Invalid register type \"{type}\".");
|
||||
}
|
||||
|
||||
private static bool EndsWithReturn(BasicBlock block)
|
||||
{
|
||||
Operation last = block.Operations.Last;
|
||||
|
||||
return last != default && last.Instruction == Instruction.Return;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,576 +0,0 @@
|
||||
using ARMeilleure.CodeGen;
|
||||
using ARMeilleure.Common;
|
||||
using ARMeilleure.Decoders;
|
||||
using ARMeilleure.Diagnostics;
|
||||
using ARMeilleure.Instructions;
|
||||
using ARMeilleure.IntermediateRepresentation;
|
||||
using ARMeilleure.Memory;
|
||||
using ARMeilleure.Signal;
|
||||
using ARMeilleure.State;
|
||||
using ARMeilleure.Translation.Cache;
|
||||
using ARMeilleure.Translation.PTC;
|
||||
using Ryujinx.Common;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
|
||||
|
||||
namespace ARMeilleure.Translation
|
||||
{
|
||||
public class Translator
|
||||
{
|
||||
private static readonly AddressTable<ulong>.Level[] Levels64Bit =
|
||||
new AddressTable<ulong>.Level[]
|
||||
{
|
||||
new(31, 17),
|
||||
new(23, 8),
|
||||
new(15, 8),
|
||||
new( 7, 8),
|
||||
new( 2, 5)
|
||||
};
|
||||
|
||||
private static readonly AddressTable<ulong>.Level[] Levels32Bit =
|
||||
new AddressTable<ulong>.Level[]
|
||||
{
|
||||
new(31, 17),
|
||||
new(23, 8),
|
||||
new(15, 8),
|
||||
new( 7, 8),
|
||||
new( 1, 6)
|
||||
};
|
||||
|
||||
private readonly IJitMemoryAllocator _allocator;
|
||||
private readonly ConcurrentQueue<KeyValuePair<ulong, TranslatedFunction>> _oldFuncs;
|
||||
|
||||
private readonly Ptc _ptc;
|
||||
|
||||
internal TranslatorCache<TranslatedFunction> Functions { get; }
|
||||
internal AddressTable<ulong> FunctionTable { get; }
|
||||
internal EntryTable<uint> CountTable { get; }
|
||||
internal TranslatorStubs Stubs { get; }
|
||||
internal TranslatorQueue Queue { get; }
|
||||
internal IMemoryManager Memory { get; }
|
||||
|
||||
private volatile int _threadCount;
|
||||
|
||||
// FIXME: Remove this once the init logic of the emulator will be redone.
|
||||
public static readonly ManualResetEvent IsReadyForTranslation = new(false);
|
||||
|
||||
public Translator(IJitMemoryAllocator allocator, IMemoryManager memory, bool for64Bits)
|
||||
{
|
||||
_allocator = allocator;
|
||||
Memory = memory;
|
||||
|
||||
_oldFuncs = new ConcurrentQueue<KeyValuePair<ulong, TranslatedFunction>>();
|
||||
|
||||
_ptc = new Ptc();
|
||||
|
||||
Queue = new TranslatorQueue();
|
||||
|
||||
JitCache.Initialize(allocator);
|
||||
|
||||
CountTable = new EntryTable<uint>();
|
||||
Functions = new TranslatorCache<TranslatedFunction>();
|
||||
FunctionTable = new AddressTable<ulong>(for64Bits ? Levels64Bit : Levels32Bit);
|
||||
Stubs = new TranslatorStubs(this);
|
||||
|
||||
FunctionTable.Fill = (ulong)Stubs.SlowDispatchStub;
|
||||
|
||||
if (memory.Type.IsHostMapped())
|
||||
{
|
||||
NativeSignalHandler.InitializeSignalHandler(allocator.GetPageSize());
|
||||
}
|
||||
}
|
||||
|
||||
public IPtcLoadState LoadDiskCache(string titleIdText, string displayVersion, bool enabled)
|
||||
{
|
||||
_ptc.Initialize(titleIdText, displayVersion, enabled, Memory.Type);
|
||||
return _ptc;
|
||||
}
|
||||
|
||||
public void PrepareCodeRange(ulong address, ulong size)
|
||||
{
|
||||
if (_ptc.Profiler.StaticCodeSize == 0)
|
||||
{
|
||||
_ptc.Profiler.StaticCodeStart = address;
|
||||
_ptc.Profiler.StaticCodeSize = size;
|
||||
}
|
||||
}
|
||||
|
||||
public void Execute(State.ExecutionContext context, ulong address)
|
||||
{
|
||||
if (Interlocked.Increment(ref _threadCount) == 1)
|
||||
{
|
||||
IsReadyForTranslation.WaitOne();
|
||||
|
||||
if (_ptc.State == PtcState.Enabled)
|
||||
{
|
||||
Debug.Assert(Functions.Count == 0);
|
||||
_ptc.LoadTranslations(this);
|
||||
_ptc.MakeAndSaveTranslations(this);
|
||||
}
|
||||
|
||||
_ptc.Profiler.Start();
|
||||
|
||||
_ptc.Disable();
|
||||
|
||||
// Simple heuristic, should be user configurable in future. (1 for 4 core/ht or less, 2 for 6 core + ht
|
||||
// etc). All threads are normal priority except from the last, which just fills as much of the last core
|
||||
// as the os lets it with a low priority. If we only have one rejit thread, it should be normal priority
|
||||
// as highCq code is performance critical.
|
||||
//
|
||||
// TODO: Use physical cores rather than logical. This only really makes sense for processors with
|
||||
// hyperthreading. Requires OS specific code.
|
||||
int unboundedThreadCount = Math.Max(1, (Environment.ProcessorCount - 6) / 3);
|
||||
int threadCount = Math.Min(4, unboundedThreadCount);
|
||||
|
||||
for (int i = 0; i < threadCount; i++)
|
||||
{
|
||||
bool last = i != 0 && i == unboundedThreadCount - 1;
|
||||
|
||||
Thread backgroundTranslatorThread = new Thread(BackgroundTranslate)
|
||||
{
|
||||
Name = "CPU.BackgroundTranslatorThread." + i,
|
||||
Priority = last ? ThreadPriority.Lowest : ThreadPriority.Normal
|
||||
};
|
||||
|
||||
backgroundTranslatorThread.Start();
|
||||
}
|
||||
}
|
||||
|
||||
Statistics.InitializeTimer();
|
||||
|
||||
NativeInterface.RegisterThread(context, Memory, this);
|
||||
|
||||
if (Optimizations.UseUnmanagedDispatchLoop)
|
||||
{
|
||||
Stubs.DispatchLoop(context.NativeContextPtr, address);
|
||||
}
|
||||
else
|
||||
{
|
||||
do
|
||||
{
|
||||
address = ExecuteSingle(context, address);
|
||||
}
|
||||
while (context.Running && address != 0);
|
||||
}
|
||||
|
||||
NativeInterface.UnregisterThread();
|
||||
|
||||
if (Interlocked.Decrement(ref _threadCount) == 0)
|
||||
{
|
||||
ClearJitCache();
|
||||
|
||||
Queue.Dispose();
|
||||
Stubs.Dispose();
|
||||
FunctionTable.Dispose();
|
||||
CountTable.Dispose();
|
||||
|
||||
_ptc.Close();
|
||||
_ptc.Profiler.Stop();
|
||||
|
||||
_ptc.Dispose();
|
||||
_ptc.Profiler.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
private ulong ExecuteSingle(State.ExecutionContext context, ulong address)
|
||||
{
|
||||
TranslatedFunction func = GetOrTranslate(address, context.ExecutionMode);
|
||||
|
||||
Statistics.StartTimer();
|
||||
|
||||
ulong nextAddr = func.Execute(context);
|
||||
|
||||
Statistics.StopTimer(address);
|
||||
|
||||
return nextAddr;
|
||||
}
|
||||
|
||||
public ulong Step(State.ExecutionContext context, ulong address)
|
||||
{
|
||||
TranslatedFunction func = Translate(address, context.ExecutionMode, highCq: false, singleStep: true);
|
||||
|
||||
address = func.Execute(context);
|
||||
|
||||
EnqueueForDeletion(address, func);
|
||||
|
||||
return address;
|
||||
}
|
||||
|
||||
internal TranslatedFunction GetOrTranslate(ulong address, ExecutionMode mode)
|
||||
{
|
||||
if (!Functions.TryGetValue(address, out TranslatedFunction func))
|
||||
{
|
||||
func = Translate(address, mode, highCq: false);
|
||||
|
||||
TranslatedFunction oldFunc = Functions.GetOrAdd(address, func.GuestSize, func);
|
||||
|
||||
if (oldFunc != func)
|
||||
{
|
||||
JitCache.Unmap(func.FuncPointer);
|
||||
func = oldFunc;
|
||||
}
|
||||
|
||||
if (_ptc.Profiler.Enabled)
|
||||
{
|
||||
_ptc.Profiler.AddEntry(address, mode, highCq: false);
|
||||
}
|
||||
|
||||
RegisterFunction(address, func);
|
||||
}
|
||||
|
||||
return func;
|
||||
}
|
||||
|
||||
internal void RegisterFunction(ulong guestAddress, TranslatedFunction func)
|
||||
{
|
||||
if (FunctionTable.IsValid(guestAddress) && (Optimizations.AllowLcqInFunctionTable || func.HighCq))
|
||||
{
|
||||
Volatile.Write(ref FunctionTable.GetValue(guestAddress), (ulong)func.FuncPointer);
|
||||
}
|
||||
}
|
||||
|
||||
internal TranslatedFunction Translate(ulong address, ExecutionMode mode, bool highCq, bool singleStep = false)
|
||||
{
|
||||
var context = new ArmEmitterContext(
|
||||
Memory,
|
||||
CountTable,
|
||||
FunctionTable,
|
||||
Stubs,
|
||||
address,
|
||||
highCq,
|
||||
_ptc.State != PtcState.Disabled,
|
||||
mode: Aarch32Mode.User);
|
||||
|
||||
Logger.StartPass(PassName.Decoding);
|
||||
|
||||
Block[] blocks = Decoder.Decode(Memory, address, mode, highCq, singleStep ? DecoderMode.SingleInstruction : DecoderMode.MultipleBlocks);
|
||||
|
||||
Logger.EndPass(PassName.Decoding);
|
||||
|
||||
Logger.StartPass(PassName.Translation);
|
||||
|
||||
EmitSynchronization(context);
|
||||
|
||||
if (blocks[0].Address != address)
|
||||
{
|
||||
context.Branch(context.GetLabel(address));
|
||||
}
|
||||
|
||||
ControlFlowGraph cfg = EmitAndGetCFG(context, blocks, out Range funcRange, out Counter<uint> counter);
|
||||
|
||||
ulong funcSize = funcRange.End - funcRange.Start;
|
||||
|
||||
Logger.EndPass(PassName.Translation, cfg);
|
||||
|
||||
Logger.StartPass(PassName.RegisterUsage);
|
||||
|
||||
RegisterUsage.RunPass(cfg, mode);
|
||||
|
||||
Logger.EndPass(PassName.RegisterUsage);
|
||||
|
||||
var retType = OperandType.I64;
|
||||
var argTypes = new OperandType[] { OperandType.I64 };
|
||||
|
||||
var options = highCq ? CompilerOptions.HighCq : CompilerOptions.None;
|
||||
|
||||
if (context.HasPtc && !singleStep)
|
||||
{
|
||||
options |= CompilerOptions.Relocatable;
|
||||
}
|
||||
|
||||
CompiledFunction compiledFunc = Compiler.Compile(cfg, argTypes, retType, options, RuntimeInformation.ProcessArchitecture);
|
||||
|
||||
if (context.HasPtc && !singleStep)
|
||||
{
|
||||
Hash128 hash = Ptc.ComputeHash(Memory, address, funcSize);
|
||||
|
||||
_ptc.WriteCompiledFunction(address, funcSize, hash, highCq, compiledFunc);
|
||||
}
|
||||
|
||||
GuestFunction func = compiledFunc.MapWithPointer<GuestFunction>(out IntPtr funcPointer);
|
||||
|
||||
Allocators.ResetAll();
|
||||
|
||||
return new TranslatedFunction(func, funcPointer, counter, funcSize, highCq);
|
||||
}
|
||||
|
||||
private void BackgroundTranslate()
|
||||
{
|
||||
while (_threadCount != 0 && Queue.TryDequeue(out RejitRequest request))
|
||||
{
|
||||
TranslatedFunction func = Translate(request.Address, request.Mode, highCq: true);
|
||||
|
||||
Functions.AddOrUpdate(request.Address, func.GuestSize, func, (key, oldFunc) =>
|
||||
{
|
||||
EnqueueForDeletion(key, oldFunc);
|
||||
return func;
|
||||
});
|
||||
|
||||
if (_ptc.Profiler.Enabled)
|
||||
{
|
||||
_ptc.Profiler.UpdateEntry(request.Address, request.Mode, highCq: true);
|
||||
}
|
||||
|
||||
RegisterFunction(request.Address, func);
|
||||
}
|
||||
}
|
||||
|
||||
private readonly struct Range
|
||||
{
|
||||
public ulong Start { get; }
|
||||
public ulong End { get; }
|
||||
|
||||
public Range(ulong start, ulong end)
|
||||
{
|
||||
Start = start;
|
||||
End = end;
|
||||
}
|
||||
}
|
||||
|
||||
private static ControlFlowGraph EmitAndGetCFG(
|
||||
ArmEmitterContext context,
|
||||
Block[] blocks,
|
||||
out Range range,
|
||||
out Counter<uint> counter)
|
||||
{
|
||||
counter = null;
|
||||
|
||||
ulong rangeStart = ulong.MaxValue;
|
||||
ulong rangeEnd = 0;
|
||||
|
||||
for (int blkIndex = 0; blkIndex < blocks.Length; blkIndex++)
|
||||
{
|
||||
Block block = blocks[blkIndex];
|
||||
|
||||
if (!block.Exit)
|
||||
{
|
||||
if (rangeStart > block.Address)
|
||||
{
|
||||
rangeStart = block.Address;
|
||||
}
|
||||
|
||||
if (rangeEnd < block.EndAddress)
|
||||
{
|
||||
rangeEnd = block.EndAddress;
|
||||
}
|
||||
}
|
||||
|
||||
if (block.Address == context.EntryAddress)
|
||||
{
|
||||
if (!context.HighCq)
|
||||
{
|
||||
EmitRejitCheck(context, out counter);
|
||||
}
|
||||
|
||||
context.ClearQcFlag();
|
||||
}
|
||||
|
||||
context.CurrBlock = block;
|
||||
|
||||
context.MarkLabel(context.GetLabel(block.Address));
|
||||
|
||||
if (block.Exit)
|
||||
{
|
||||
// Left option here as it may be useful if we need to return to managed rather than tail call in
|
||||
// future. (eg. for debug)
|
||||
bool useReturns = false;
|
||||
|
||||
InstEmitFlowHelper.EmitVirtualJump(context, Const(block.Address), isReturn: useReturns);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int opcIndex = 0; opcIndex < block.OpCodes.Count; opcIndex++)
|
||||
{
|
||||
OpCode opCode = block.OpCodes[opcIndex];
|
||||
|
||||
context.CurrOp = opCode;
|
||||
|
||||
bool isLastOp = opcIndex == block.OpCodes.Count - 1;
|
||||
|
||||
if (isLastOp)
|
||||
{
|
||||
context.SyncQcFlag();
|
||||
|
||||
if (block.Branch != null && !block.Branch.Exit && block.Branch.Address <= block.Address)
|
||||
{
|
||||
EmitSynchronization(context);
|
||||
}
|
||||
}
|
||||
|
||||
Operand lblPredicateSkip = default;
|
||||
|
||||
if (context.IsInIfThenBlock && context.CurrentIfThenBlockCond != Condition.Al)
|
||||
{
|
||||
lblPredicateSkip = Label();
|
||||
|
||||
InstEmitFlowHelper.EmitCondBranch(context, lblPredicateSkip, context.CurrentIfThenBlockCond.Invert());
|
||||
}
|
||||
|
||||
if (opCode is OpCode32 op && op.Cond < Condition.Al)
|
||||
{
|
||||
lblPredicateSkip = Label();
|
||||
|
||||
InstEmitFlowHelper.EmitCondBranch(context, lblPredicateSkip, op.Cond.Invert());
|
||||
}
|
||||
|
||||
if (opCode.Instruction.Emitter != null)
|
||||
{
|
||||
opCode.Instruction.Emitter(context);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException($"Invalid instruction \"{opCode.Instruction.Name}\".");
|
||||
}
|
||||
|
||||
if (lblPredicateSkip != default)
|
||||
{
|
||||
context.MarkLabel(lblPredicateSkip);
|
||||
}
|
||||
|
||||
if (context.IsInIfThenBlock && opCode.Instruction.Name != InstName.It)
|
||||
{
|
||||
context.AdvanceIfThenBlockState();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
range = new Range(rangeStart, rangeEnd);
|
||||
|
||||
return context.GetControlFlowGraph();
|
||||
}
|
||||
|
||||
internal static void EmitRejitCheck(ArmEmitterContext context, out Counter<uint> counter)
|
||||
{
|
||||
const int MinsCallForRejit = 100;
|
||||
|
||||
counter = new Counter<uint>(context.CountTable);
|
||||
|
||||
Operand lblEnd = Label();
|
||||
|
||||
Operand address = !context.HasPtc ?
|
||||
Const(ref counter.Value) :
|
||||
Const(ref counter.Value, Ptc.CountTableSymbol);
|
||||
|
||||
Operand curCount = context.Load(OperandType.I32, address);
|
||||
Operand count = context.Add(curCount, Const(1));
|
||||
context.Store(address, count);
|
||||
context.BranchIf(lblEnd, curCount, Const(MinsCallForRejit), Comparison.NotEqual, BasicBlockFrequency.Cold);
|
||||
|
||||
context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.EnqueueForRejit)), Const(context.EntryAddress));
|
||||
|
||||
context.MarkLabel(lblEnd);
|
||||
}
|
||||
|
||||
internal static void EmitSynchronization(EmitterContext context)
|
||||
{
|
||||
long countOffs = NativeContext.GetCounterOffset();
|
||||
|
||||
Operand lblNonZero = Label();
|
||||
Operand lblExit = Label();
|
||||
|
||||
Operand countAddr = context.Add(context.LoadArgument(OperandType.I64, 0), Const(countOffs));
|
||||
Operand count = context.Load(OperandType.I32, countAddr);
|
||||
context.BranchIfTrue(lblNonZero, count, BasicBlockFrequency.Cold);
|
||||
|
||||
Operand running = context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.CheckSynchronization)));
|
||||
context.BranchIfTrue(lblExit, running, BasicBlockFrequency.Cold);
|
||||
|
||||
context.Return(Const(0L));
|
||||
|
||||
context.MarkLabel(lblNonZero);
|
||||
count = context.Subtract(count, Const(1));
|
||||
context.Store(countAddr, count);
|
||||
|
||||
context.MarkLabel(lblExit);
|
||||
}
|
||||
|
||||
public void InvalidateJitCacheRegion(ulong address, ulong size)
|
||||
{
|
||||
ulong[] overlapAddresses = Array.Empty<ulong>();
|
||||
|
||||
int overlapsCount = Functions.GetOverlaps(address, size, ref overlapAddresses);
|
||||
|
||||
if (overlapsCount != 0)
|
||||
{
|
||||
// If rejit is running, stop it as it may be trying to rejit a function on the invalidated region.
|
||||
ClearRejitQueue(allowRequeue: true);
|
||||
}
|
||||
|
||||
for (int index = 0; index < overlapsCount; index++)
|
||||
{
|
||||
ulong overlapAddress = overlapAddresses[index];
|
||||
|
||||
if (Functions.TryGetValue(overlapAddress, out TranslatedFunction overlap))
|
||||
{
|
||||
Functions.Remove(overlapAddress);
|
||||
Volatile.Write(ref FunctionTable.GetValue(overlapAddress), FunctionTable.Fill);
|
||||
EnqueueForDeletion(overlapAddress, overlap);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Remove overlapping functions from the JitCache aswell.
|
||||
// This should be done safely, with a mechanism to ensure the function is not being executed.
|
||||
}
|
||||
|
||||
internal void EnqueueForRejit(ulong guestAddress, ExecutionMode mode)
|
||||
{
|
||||
Queue.Enqueue(guestAddress, mode);
|
||||
}
|
||||
|
||||
private void EnqueueForDeletion(ulong guestAddress, TranslatedFunction func)
|
||||
{
|
||||
_oldFuncs.Enqueue(new(guestAddress, func));
|
||||
}
|
||||
|
||||
private void ClearJitCache()
|
||||
{
|
||||
// Ensure no attempt will be made to compile new functions due to rejit.
|
||||
ClearRejitQueue(allowRequeue: false);
|
||||
|
||||
List<TranslatedFunction> functions = Functions.AsList();
|
||||
|
||||
foreach (var func in functions)
|
||||
{
|
||||
JitCache.Unmap(func.FuncPointer);
|
||||
|
||||
func.CallCounter?.Dispose();
|
||||
}
|
||||
|
||||
Functions.Clear();
|
||||
|
||||
while (_oldFuncs.TryDequeue(out var kv))
|
||||
{
|
||||
JitCache.Unmap(kv.Value.FuncPointer);
|
||||
|
||||
kv.Value.CallCounter?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
private void ClearRejitQueue(bool allowRequeue)
|
||||
{
|
||||
if (!allowRequeue)
|
||||
{
|
||||
Queue.Clear();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
lock (Queue.Sync)
|
||||
{
|
||||
while (Queue.Count > 0 && Queue.TryDequeue(out RejitRequest request))
|
||||
{
|
||||
if (Functions.TryGetValue(request.Address, out var func) && func.CallCounter != null)
|
||||
{
|
||||
Volatile.Write(ref func.CallCounter.Value, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
147
CONTRIBUTING.md
Normal file
147
CONTRIBUTING.md
Normal file
@ -0,0 +1,147 @@
|
||||
# Contribution to Ryujinx
|
||||
|
||||
You can contribute to Ryujinx with PRs, testing of PRs and issues. Contributing code and other implementations is greatly appreciated alongside simply filing issues for problems you encounter.
|
||||
Please read the entire document before continuing as it can potentially save everyone involved a significant amount of time.
|
||||
|
||||
# Quick Links
|
||||
|
||||
* [Code Style Documentation](docs/coding-guidelines/coding-style.md)
|
||||
* [Pull Request Guidelines](docs/workflow/pr-guide.md)
|
||||
|
||||
## Reporting Issues
|
||||
|
||||
We always welcome bug reports, feature proposals and overall feedback. Here are a few tips on how you can make reporting your issue as effective as possible.
|
||||
|
||||
### Identify Where to Report
|
||||
|
||||
The Ryujinx codebase is distributed across multiple repositories in the [Ryujinx organization](https://github.com/Ryujinx). Depending on the feedback you might want to file the issue on a different repo. Here are a few common repos:
|
||||
|
||||
* [Ryujinx/Ryujinx](https://github.com/Ryujinx/Ryujinx) Ryujinx core project files.
|
||||
* [Ryujinx/Ryujinx-Games-List](https://github.com/Ryujinx/Ryujinx-Games-List) Ryujinx game compatibility list.
|
||||
* [Ryujinx/Ryujinx-Website](https://github.com/Ryujinx/Ryujinx-Website) Ryujinx website source code.
|
||||
* [Ryujinx/Ryujinx-Ldn-Website](https://github.com/Ryujinx/Ryujinx-Ldn-Website) Ryujinx LDN website source code.
|
||||
|
||||
### Finding Existing Issues
|
||||
|
||||
Before filing a new issue, please search our [open issues](https://github.com/Ryujinx/Ryujinx/issues) to check if it already exists.
|
||||
|
||||
If you do find an existing issue, please include your own feedback in the discussion. Do consider upvoting (👍 reaction) the original post, as this helps us prioritize popular issues in our backlog.
|
||||
|
||||
### Writing a Good Feature Request
|
||||
|
||||
Please review any feature requests already opened to both check it has not already been suggested, and to familiarize yourself with the format. When ready to submit a proposal, please use the [Feature Request issue template](https://github.com/Ryujinx/Ryujinx/issues/new?assignees=&labels=&projects=&template=feature_request.yml&title=%5BFeature+Request%5D).
|
||||
|
||||
### Writing a Good Bug Report
|
||||
|
||||
Good bug reports make it easier for maintainers to verify and root cause the underlying problem. The better a bug report, the faster the problem will be resolved.
|
||||
Ideally, a bug report should contain the following information:
|
||||
|
||||
* A high-level description of the problem.
|
||||
* A _minimal reproduction_, i.e. the smallest time commitment/configuration required to reproduce the wrong behavior. This can be in the form of a small homebrew application, or by providing a save file and reproduction steps for a specific game.
|
||||
* A description of the _expected behavior_, contrasted with the _actual behavior_ observed.
|
||||
* Information on the environment: OS/distro, CPU, GPU (including driver), RAM etc.
|
||||
* A Ryujinx log file of the run instance where the issue occurred. Log files can be found in `[Executable Folder]/Logs` and are named chronologically.
|
||||
* Additional information, e.g. is it a regression from previous versions? Are there any known workarounds?
|
||||
|
||||
When ready to submit a bug report, please use the [Bug Report issue template](https://github.com/Ryujinx/Ryujinx/issues/new?assignees=&labels=bug&projects=&template=bug_report.yml&title=%5BBug%5D).
|
||||
|
||||
## Contributing Changes
|
||||
|
||||
Project maintainers will merge changes that both improve the project and meet our standards for code quality.
|
||||
|
||||
The [Pull Request Guide](docs/workflow/pr-guide.md) and [License](https://github.com/Ryujinx/Ryujinx/blob/master/LICENSE.txt) docs define additional guidance.
|
||||
|
||||
### DOs and DON'Ts
|
||||
|
||||
Please do:
|
||||
|
||||
* **DO** follow our [coding style](docs/coding-guidelines/coding-style.md) (C# code-specific).
|
||||
* **DO** give priority to the current style of the project or file you're changing even if it diverges from the general guidelines.
|
||||
* **DO** keep the discussions focused. When a new or related topic comes up
|
||||
it's often better to create new issue than to side track the discussion.
|
||||
* **DO** clearly state on an issue that you are going to take on implementing it.
|
||||
* **DO** blog and tweet (or whatever) about your contributions, frequently!
|
||||
|
||||
Please do not:
|
||||
|
||||
* **DON'T** make PRs for style changes.
|
||||
* **DON'T** surprise us with big pull requests. Instead, file an issue and talk with us on Discord to start
|
||||
a discussion so we can agree on a direction before you invest a large amount
|
||||
of time.
|
||||
* **DON'T** commit code that you didn't write. If you find code that you think is a good fit to add to Ryujinx, file an issue or talk to us on Discord to start a discussion before proceeding.
|
||||
* **DON'T** submit PRs that alter licensing related files or headers. If you believe there's a problem with them, file an issue and we'll be happy to discuss it.
|
||||
|
||||
### Suggested Workflow
|
||||
|
||||
We use and recommend the following workflow:
|
||||
|
||||
1. Create or find an issue for your work.
|
||||
- You can skip this step for trivial changes.
|
||||
- Get agreement from the team and the community that your proposed change is a good one if it is of significant size or changes core functionality.
|
||||
- Clearly state that you are going to take on implementing it, if that's the case. You can request that the issue be assigned to you. Note: The issue filer and the implementer don't have to be the same person.
|
||||
2. Create a personal fork of the repository on GitHub (if you don't already have one).
|
||||
3. In your fork, create a branch off of main (`git checkout -b mybranch`).
|
||||
- Branches are useful since they isolate your changes from incoming changes from upstream. They also enable you to create multiple PRs from the same fork.
|
||||
4. Make and commit your changes to your branch.
|
||||
- [Build Instructions](https://github.com/Ryujinx/Ryujinx#building) explains how to build and test.
|
||||
- Commit messages should be clear statements of action and intent.
|
||||
6. Build the repository with your changes.
|
||||
- Make sure that the builds are clean.
|
||||
- Make sure that `dotnet format` has been run and any corrections tested and committed.
|
||||
7. Create a pull request (PR) against the Ryujinx/Ryujinx repository's **main** branch.
|
||||
- State in the description what issue or improvement your change is addressing.
|
||||
- Check if all the Continuous Integration checks are passing. Refer to [Actions](https://github.com/Ryujinx/Ryujinx/actions) to check for outstanding errors.
|
||||
8. Wait for feedback or approval of your changes from the [core development team](https://github.com/orgs/Ryujinx/teams/developers)
|
||||
- Details about the pull request [review procedure](docs/workflow/ci/pr-guide.md).
|
||||
9. When the team members have signed off, and all checks are green, your PR will be merged.
|
||||
- The next official build will automatically include your change.
|
||||
- You can delete the branch you used for making the change.
|
||||
|
||||
### Good First Issues
|
||||
|
||||
The team marks the most straightforward issues as [good first issues](https://github.com/Ryujinx/Ryujinx/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22). This set of issues is the place to start if you are interested in contributing but new to the codebase.
|
||||
|
||||
### Commit Messages
|
||||
|
||||
Please format commit messages as follows (based on [A Note About Git Commit Messages](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html)):
|
||||
|
||||
```
|
||||
Summarize change in 50 characters or less
|
||||
|
||||
Provide more detail after the first line. Leave one blank line below the
|
||||
summary and wrap all lines at 72 characters or less.
|
||||
|
||||
If the change fixes an issue, leave another blank line after the final
|
||||
paragraph and indicate which issue is fixed in the specific format
|
||||
below.
|
||||
|
||||
Fix #42
|
||||
```
|
||||
|
||||
Also do your best to factor commits appropriately, not too large with unrelated things in the same commit, and not too small with the same small change applied N times in N different commits.
|
||||
|
||||
### PR - CI Process
|
||||
|
||||
The [Ryujinx continuous integration](https://github.com/Ryujinx/Ryujinx/actions) (CI) system will automatically perform the required builds and run tests (including the ones you are expected to run) for PRs. Builds and test runs must be clean or have bugs properly filed against flaky/unexpected failures that are unrelated to your change.
|
||||
|
||||
If the CI build fails for any reason, the PR actions tab should be consulted for further information on the failure. There are a few usual suspects for such a failure:
|
||||
* `dotnet format` has not been run on the PR and has outstanding stylistic issues.
|
||||
* There is an error within the PR that fails a test or errors the compiler.
|
||||
* Random failure of the workflow can occasionally result in a CI failure. In this scenario a maintainer will manually restart the job.
|
||||
|
||||
### PR Feedback
|
||||
|
||||
Ryujinx team and community members will provide feedback on your change. Community feedback is highly valued. You may see the absence of team feedback if the community has already provided good review feedback.
|
||||
|
||||
Two Ryujinx team members must review and approve every PR prior to merge. They will often reply with "LGTM, see nit". That means that the PR will be merged once the feedback is resolved. "LGTM" == "looks good to me".
|
||||
|
||||
There are lots of thoughts and [approaches](https://github.com/antlr/antlr4-cpp/blob/master/CONTRIBUTING.md#emoji) for how to efficiently discuss changes. It is best to be clear and explicit with your feedback. Please be patient with people who might not understand the finer details about your approach to feedback.
|
||||
|
||||
#### Copying Changes from Other Projects
|
||||
|
||||
Ryujinx uses some implementations and frameworks from other projects. The following rules must be followed for PRs that include changes from another project:
|
||||
|
||||
- The license of the file is [permissive](https://en.wikipedia.org/wiki/Permissive_free_software_licence).
|
||||
- The license of the file is left in-tact.
|
||||
- The contribution is correctly attributed in the [3rd party notices](https://github.com/Ryujinx/Ryujinx/blob/master/distribution/legal/THIRDPARTY.md) file in the repository, as needed.
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user