maint: update deps

This commit is contained in:
Rim 2024-06-17 19:04:36 -04:00
parent 8a594869b8
commit e54ad89202
691 changed files with 34781 additions and 23029 deletions

View File

@ -0,0 +1,42 @@
name: Bug Report
description: File a bug report
body:
- type: markdown
attributes:
value: |
Before you hit the submit button:
* Please see our [Contribution Guidelines](https://github.com/asmjit/asmjit/blob/master/CONTRIBUTING.md).
* Make sure that you use a recent AsmJit (master branch) before filing a bug report.
* Make sure that you use logging and error handling features to collect as much information as possible, if applicable.
- type: textarea
id: issue-description
attributes:
label: Issue Description
description: Please share a clear and concise description of the issue and optionally provide reproducibility information and output from AsmJit's logger.
placeholder: Description
validations:
required: true
- type: dropdown
id: operating-system
attributes:
label: Operating System
multiple: true
options:
- Not specified / possibly all
- Windows
- Linux
- Mac
- Android
- Other
- type: dropdown
id: target-architecture
attributes:
label: Architecture
multiple: true
options:
- Not specified
- X86 / X86_64
- AArch32
- AArch64
- RISC-V
- Other

View File

@ -0,0 +1,18 @@
name: Feature Request
description: Request a new feature or enhancement
labels: [enhancement]
body:
- type: markdown
attributes:
value: |
Before you hit the submit button:
* Please see our [Contribution Guidelines](https://github.com/asmjit/asmjit/blob/master/CONTRIBUTING.md).
* Make sure that you use a recent AsmJit (master branch) before filing a feature request.
- type: textarea
id: issue-description
attributes:
label: Issue Description
description: Please share a clear and concise description of a new feature or enhancement.
placeholder: Description
validations:
required: true

View File

@ -0,0 +1,18 @@
name: Help & Questions
description: Ask a question or get help
labels: [question]
body:
- type: markdown
attributes:
value: |
Before you hit the submit button:
* Please see our [Contribution Guidelines](https://github.com/asmjit/asmjit/blob/master/CONTRIBUTING.md).
* [Gitter Chat](https://app.gitter.im/#/room/#asmjit:gitter.im) is usually faster to get answers.
* If you need a help, please include as much information as possible to make it clear what the intend is.
- type: textarea
id: issue-description
attributes:
label: Details
description: The description of the problem or question.
validations:
required: true

View File

@ -0,0 +1,11 @@
name: Other Issues
description: Something that doesn't fit the other categories
body:
- type: textarea
id: issue-description
attributes:
label: Issue Description
description: Please share a clear and concise description of the issue.
placeholder: Description
validations:
required: true

View File

@ -1,7 +1,8 @@
{
"diagnostics": {
"asan": { "definitions": ["ASMJIT_SANITIZE=address"] },
"ubsan": { "definitions": ["ASMJIT_SANITIZE=undefined"] }
"ubsan": { "definitions": ["ASMJIT_SANITIZE=undefined"] },
"msan": { "definitions": ["ASMJIT_SANITIZE=memory"] }
},
"valgrind_arguments": [
@ -11,37 +12,14 @@
],
"tests": [
{
"cmd": ["asmjit_test_unit", "--quick"],
"optional": true
},
{
"cmd": ["asmjit_test_assembler"],
"optional": true
},
{
"cmd": ["asmjit_test_assembler", "--validate"],
"optional": true
},
{
"cmd": ["asmjit_test_emitters"],
"optional": true
},
{
"cmd": ["asmjit_test_compiler"],
"optional": true
},
{
"cmd": ["asmjit_test_instinfo"],
"optional": true
},
{
"cmd": ["asmjit_test_x86_sections"],
"optional": true
},
{
"cmd": ["asmjit_test_perf", "--quick"],
"optional": true
}
{ "optional": true, "cmd": ["asmjit_test_unit", "--quick"] },
{ "optional": true, "cmd": ["asmjit_test_assembler"] },
{ "optional": true, "cmd": ["asmjit_test_assembler", "--validate"] },
{ "optional": true, "cmd": ["asmjit_test_emitters"] },
{ "optional": true, "cmd": ["asmjit_test_execute"] },
{ "optional": true, "cmd": ["asmjit_test_compiler"] },
{ "optional": true, "cmd": ["asmjit_test_instinfo"] },
{ "optional": true, "cmd": ["asmjit_test_x86_sections"] },
{ "optional": true, "cmd": ["asmjit_test_perf", "--quick"] }
]
}

View File

@ -14,12 +14,12 @@ jobs:
steps:
- name: "Checkout"
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: "Setup node.js"
uses: actions/setup-node@v3
uses: actions/setup-node@v4
with:
node-version: "16"
node-version: "*"
- name: "Check Enumerations"
run: |
@ -31,127 +31,163 @@ jobs:
fail-fast: false
matrix:
include:
- { title: "linux-lib" , os: "ubuntu-latest" , cc: "clang" , arch: "x64", build_type: "Debug" , problem_matcher: "cpp" }
- { title: "macos-lib" , os: "macos-latest" , cc: "clang" , arch: "x64", build_type: "Debug" , problem_matcher: "cpp" }
- { title: "windows-lib" , os: "windows-latest", cc: "vs2022" , arch: "x64", build_type: "Debug" , problem_matcher: "cpp" }
- { title: "diag-analyze" , host: "ubuntu-latest" , arch: "x64" , cc: "clang-18", conf: "Debug" , diagnostics: "analyze-build" }
- { title: "diag-asan" , host: "ubuntu-latest" , arch: "x64" , cc: "clang-18", conf: "Release", diagnostics: "asan", defs: "ASMJIT_TEST=1" }
- { title: "diag-msan" , host: "ubuntu-latest" , arch: "x64" , cc: "clang-18", conf: "Release", diagnostics: "msan", defs: "ASMJIT_TEST=1" }
- { title: "diag-ubsan" , host: "ubuntu-latest" , arch: "x64" , cc: "clang-18", conf: "Release", diagnostics: "ubsan", defs: "ASMJIT_TEST=1" }
- { title: "diag-hardened" , host: "ubuntu-latest" , arch: "x64" , cc: "clang-18", conf: "Release", diagnostics: "hardened", defs: "ASMJIT_TEST=1" }
- { title: "diag-valgrind" , host: "ubuntu-latest" , arch: "x64" , cc: "clang-18", conf: "Release", diagnostics: "valgrind", defs: "ASMJIT_TEST=1" }
- { title: "diag-asan" , os: "ubuntu-latest" , cc: "clang" , arch: "x64", build_type: "Release", diagnostics: "address" , defs: "ASMJIT_TEST=1" }
- { title: "diag-ubsan" , os: "ubuntu-latest" , cc: "clang" , arch: "x64", build_type: "Release", diagnostics: "undefined", defs: "ASMJIT_TEST=1" }
- { title: "diag-valgrind" , os: "ubuntu-latest" , cc: "clang" , arch: "x64", build_type: "Release", diagnostics: "valgrind" , defs: "ASMJIT_TEST=1" }
- { title: "diag-scan-build" , os: "ubuntu-latest" , cc: "clang" , arch: "x64", build_type: "Debug" , diagnostics: "scan-build" }
- { title: "no-deprecated" , host: "ubuntu-latest" , arch: "x64" , cc: "clang-18", conf: "Release", defs: "ASMJIT_TEST=1,ASMJIT_NO_DEPRECATED=1" }
- { title: "no-intrinsics" , host: "ubuntu-latest" , arch: "x64" , cc: "clang-18", conf: "Release", defs: "ASMJIT_TEST=1,ASMJIT_NO_INTRINSICS=1" }
- { title: "no-logging" , host: "ubuntu-latest" , arch: "x64" , cc: "clang-18", conf: "Release", defs: "ASMJIT_TEST=1,ASMJIT_NO_LOGGING=1" }
- { title: "no-logging-text" , host: "ubuntu-latest" , arch: "x64" , cc: "clang-18", conf: "Release", defs: "ASMJIT_TEST=1,ASMJIT_NO_LOGGING=1,ASMJIT_NO_TEXT=1" }
- { title: "no-builder" , host: "ubuntu-latest" , arch: "x64" , cc: "clang-18", conf: "Release", defs: "ASMJIT_TEST=1,ASMJIT_NO_BUILDER=1" }
- { title: "no-compiler" , host: "ubuntu-latest" , arch: "x64" , cc: "clang-18", conf: "Release", defs: "ASMJIT_TEST=1,ASMJIT_NO_COMPILER=1" }
- { title: "no-introspection", host: "ubuntu-latest" , arch: "x64" , cc: "clang-18", conf: "Release", defs: "ASMJIT_TEST=1,ASMJIT_NO_COMPILER=1,ASMJIT_NO_INTROSPECTION=1" }
- { title: "no-jit" , host: "ubuntu-latest" , arch: "x64" , cc: "clang-18", conf: "Release", defs: "ASMJIT_TEST=1,ASMJIT_NO_JIT=1" }
- { title: "no-validation" , host: "ubuntu-latest" , arch: "x64" , cc: "clang-18", conf: "Release", defs: "ASMJIT_TEST=1,ASMJIT_NO_VALIDATION=1" }
- { title: "no-x86" , host: "ubuntu-latest" , arch: "x64" , cc: "clang-18", conf: "Release", defs: "ASMJIT_TEST=1,ASMJIT_NO_X86=1" }
- { title: "no-aarch64" , host: "ubuntu-latest" , arch: "x64" , cc: "clang-18", conf: "Release", defs: "ASMJIT_TEST=1,ASMJIT_NO_AARCH64=1" }
- { title: "no-deprecated" , os: "ubuntu-latest" , cc: "clang" , arch: "x64", build_type: "Release", defs: "ASMJIT_TEST=1,ASMJIT_NO_DEPRECATED=1" }
- { title: "no-intrinsics" , os: "ubuntu-latest" , cc: "clang" , arch: "x64", build_type: "Release", defs: "ASMJIT_TEST=1,ASMJIT_NO_INTRINSICS=1" }
- { title: "no-logging" , os: "ubuntu-latest" , cc: "clang" , arch: "x64", build_type: "Release", defs: "ASMJIT_TEST=1,ASMJIT_NO_LOGGING=1" }
- { title: "no-logging-text" , os: "ubuntu-latest" , cc: "clang" , arch: "x64", build_type: "Release", defs: "ASMJIT_TEST=1,ASMJIT_NO_LOGGING=1,ASMJIT_NO_TEXT=1" }
- { title: "no-builder" , os: "ubuntu-latest" , cc: "clang" , arch: "x64", build_type: "Release", defs: "ASMJIT_TEST=1,ASMJIT_NO_BUILDER=1" }
- { title: "no-compiler" , os: "ubuntu-latest" , cc: "clang" , arch: "x64", build_type: "Release", defs: "ASMJIT_TEST=1,ASMJIT_NO_COMPILER=1" }
- { title: "no-jit" , os: "ubuntu-latest" , cc: "clang" , arch: "x64", build_type: "Release", defs: "ASMJIT_TEST=1,ASMJIT_NO_JIT=1" }
- { title: "no-introspection", os: "ubuntu-latest" , cc: "clang" , arch: "x64", build_type: "Release", defs: "ASMJIT_TEST=1,ASMJIT_NO_INTROSPECTION=1" }
- { title: "no-validation" , os: "ubuntu-latest" , cc: "clang" , arch: "x64", build_type: "Release", defs: "ASMJIT_TEST=1,ASMJIT_NO_VALIDATION=1" }
- { title: "no-x86" , os: "ubuntu-latest" , cc: "clang" , arch: "x64", build_type: "Release", defs: "ASMJIT_TEST=1,ASMJIT_NO_X86=1" }
- { title: "no-aarch64" , os: "ubuntu-latest" , cc: "clang" , arch: "x64", build_type: "Release", defs: "ASMJIT_TEST=1,ASMJIT_NO_AARCH64=1" }
- { title: "linux" , host: "ubuntu-20.04" , arch: "x86" , cc: "gcc-7" , conf: "Debug" , defs: "ASMJIT_TEST=1" }
- { title: "linux" , host: "ubuntu-20.04" , arch: "x86" , cc: "gcc-7" , conf: "Release", defs: "ASMJIT_TEST=1" }
- { title: "linux" , host: "ubuntu-20.04" , arch: "x64" , cc: "gcc-7" , conf: "Debug" , defs: "ASMJIT_TEST=1" }
- { title: "linux" , host: "ubuntu-20.04" , arch: "x64" , cc: "gcc-7" , conf: "Release", defs: "ASMJIT_TEST=1" }
- { title: "linux" , host: "ubuntu-20.04" , arch: "x86" , cc: "gcc-8" , conf: "Debug" , defs: "ASMJIT_TEST=1" }
- { title: "linux" , host: "ubuntu-20.04" , arch: "x86" , cc: "gcc-8" , conf: "Release", defs: "ASMJIT_TEST=1" }
- { title: "linux" , host: "ubuntu-20.04" , arch: "x64" , cc: "gcc-8" , conf: "Debug" , defs: "ASMJIT_TEST=1" }
- { title: "linux" , host: "ubuntu-20.04" , arch: "x64" , cc: "gcc-8" , conf: "Release", defs: "ASMJIT_TEST=1" }
- { title: "linux" , host: "ubuntu-22.04" , arch: "x86" , cc: "gcc-9" , conf: "Debug" , defs: "ASMJIT_TEST=1" }
- { title: "linux" , host: "ubuntu-22.04" , arch: "x86" , cc: "gcc-9" , conf: "Release", defs: "ASMJIT_TEST=1" }
- { title: "linux" , host: "ubuntu-22.04" , arch: "x64" , cc: "gcc-9" , conf: "Debug" , defs: "ASMJIT_TEST=1" }
- { title: "linux" , host: "ubuntu-22.04" , arch: "x64" , cc: "gcc-9" , conf: "Release", defs: "ASMJIT_TEST=1" }
- { title: "linux" , host: "ubuntu-22.04" , arch: "x86" , cc: "gcc-10" , conf: "Debug" , defs: "ASMJIT_TEST=1" }
- { title: "linux" , host: "ubuntu-22.04" , arch: "x86" , cc: "gcc-10" , conf: "Release", defs: "ASMJIT_TEST=1" }
- { title: "linux" , host: "ubuntu-22.04" , arch: "x64" , cc: "gcc-10" , conf: "Debug" , defs: "ASMJIT_TEST=1" }
- { title: "linux" , host: "ubuntu-22.04" , arch: "x64" , cc: "gcc-10" , conf: "Release", defs: "ASMJIT_TEST=1" }
- { title: "linux" , host: "ubuntu-22.04" , arch: "x86" , cc: "gcc-11" , conf: "Debug" , defs: "ASMJIT_TEST=1" }
- { title: "linux" , host: "ubuntu-22.04" , arch: "x86" , cc: "gcc-11" , conf: "Release", defs: "ASMJIT_TEST=1" }
- { title: "linux" , host: "ubuntu-22.04" , arch: "x64" , cc: "gcc-11" , conf: "Debug" , defs: "ASMJIT_TEST=1" }
- { title: "linux" , host: "ubuntu-22.04" , arch: "x64" , cc: "gcc-11" , conf: "Release", defs: "ASMJIT_TEST=1" }
- { title: "linux" , host: "ubuntu-22.04" , arch: "x86" , cc: "gcc-12" , conf: "Debug" , defs: "ASMJIT_TEST=1" }
- { title: "linux" , host: "ubuntu-22.04" , arch: "x86" , cc: "gcc-12" , conf: "Release", defs: "ASMJIT_TEST=1" }
- { title: "linux" , host: "ubuntu-22.04" , arch: "x64" , cc: "gcc-12" , conf: "Debug" , defs: "ASMJIT_TEST=1" }
- { title: "linux" , host: "ubuntu-22.04" , arch: "x64" , cc: "gcc-12" , conf: "Release", defs: "ASMJIT_TEST=1" }
- { title: "linux" , host: "ubuntu-22.04" , arch: "x86" , cc: "gcc-13" , conf: "Debug" , defs: "ASMJIT_TEST=1" }
- { title: "linux" , host: "ubuntu-22.04" , arch: "x86" , cc: "gcc-13" , conf: "Release", defs: "ASMJIT_TEST=1" }
- { title: "linux" , host: "ubuntu-22.04" , arch: "x64" , cc: "gcc-13" , conf: "Debug" , defs: "ASMJIT_TEST=1" }
- { title: "linux" , host: "ubuntu-22.04" , arch: "x64" , cc: "gcc-13" , conf: "Release", defs: "ASMJIT_TEST=1" }
- { title: "linux" , host: "ubuntu-20.04" , arch: "x86" , cc: "clang-10", conf: "Debug" , defs: "ASMJIT_TEST=1" }
- { title: "linux" , host: "ubuntu-20.04" , arch: "x86" , cc: "clang-10", conf: "Release", defs: "ASMJIT_TEST=1" }
- { title: "linux" , host: "ubuntu-20.04" , arch: "x64" , cc: "clang-10", conf: "Debug" , defs: "ASMJIT_TEST=1" }
- { title: "linux" , host: "ubuntu-20.04" , arch: "x64" , cc: "clang-10", conf: "Release", defs: "ASMJIT_TEST=1" }
- { title: "linux" , host: "ubuntu-22.04" , arch: "x86" , cc: "clang-11", conf: "Debug" , defs: "ASMJIT_TEST=1" }
- { title: "linux" , host: "ubuntu-22.04" , arch: "x86" , cc: "clang-11", conf: "Release", defs: "ASMJIT_TEST=1" }
- { title: "linux" , host: "ubuntu-22.04" , arch: "x64" , cc: "clang-11", conf: "Debug" , defs: "ASMJIT_TEST=1" }
- { title: "linux" , host: "ubuntu-22.04" , arch: "x64" , cc: "clang-11", conf: "Release", defs: "ASMJIT_TEST=1" }
- { title: "linux" , host: "ubuntu-22.04" , arch: "x86" , cc: "clang-12", conf: "Debug" , defs: "ASMJIT_TEST=1" }
- { title: "linux" , host: "ubuntu-22.04" , arch: "x86" , cc: "clang-12", conf: "Release", defs: "ASMJIT_TEST=1" }
- { title: "linux" , host: "ubuntu-22.04" , arch: "x64" , cc: "clang-12", conf: "Debug" , defs: "ASMJIT_TEST=1" }
- { title: "linux" , host: "ubuntu-22.04" , arch: "x64" , cc: "clang-12", conf: "Release", defs: "ASMJIT_TEST=1" }
- { title: "linux" , host: "ubuntu-22.04" , arch: "x86" , cc: "clang-13", conf: "Debug" , defs: "ASMJIT_TEST=1" }
- { title: "linux" , host: "ubuntu-22.04" , arch: "x86" , cc: "clang-13", conf: "Release", defs: "ASMJIT_TEST=1" }
- { title: "linux" , host: "ubuntu-22.04" , arch: "x64" , cc: "clang-13", conf: "Debug" , defs: "ASMJIT_TEST=1" }
- { title: "linux" , host: "ubuntu-22.04" , arch: "x64" , cc: "clang-13", conf: "Release", defs: "ASMJIT_TEST=1" }
- { title: "linux" , host: "ubuntu-22.04" , arch: "x86" , cc: "clang-14", conf: "Debug" , defs: "ASMJIT_TEST=1" }
- { title: "linux" , host: "ubuntu-22.04" , arch: "x86" , cc: "clang-14", conf: "Release", defs: "ASMJIT_TEST=1" }
- { title: "linux" , host: "ubuntu-22.04" , arch: "x64" , cc: "clang-14", conf: "Debug" , defs: "ASMJIT_TEST=1" }
- { title: "linux" , host: "ubuntu-22.04" , arch: "x64" , cc: "clang-14", conf: "Release", defs: "ASMJIT_TEST=1" }
- { title: "linux" , host: "ubuntu-22.04" , arch: "x86" , cc: "clang-15", conf: "Debug" , defs: "ASMJIT_TEST=1" }
- { title: "linux" , host: "ubuntu-22.04" , arch: "x86" , cc: "clang-15", conf: "Release", defs: "ASMJIT_TEST=1" }
- { title: "linux" , host: "ubuntu-22.04" , arch: "x64" , cc: "clang-15", conf: "Debug" , defs: "ASMJIT_TEST=1" }
- { title: "linux" , host: "ubuntu-22.04" , arch: "x64" , cc: "clang-15", conf: "Release", defs: "ASMJIT_TEST=1" }
- { title: "linux" , host: "ubuntu-22.04" , arch: "x86" , cc: "clang-16", conf: "Debug" , defs: "ASMJIT_TEST=1" }
- { title: "linux" , host: "ubuntu-22.04" , arch: "x86" , cc: "clang-16", conf: "Release", defs: "ASMJIT_TEST=1" }
- { title: "linux" , host: "ubuntu-22.04" , arch: "x64" , cc: "clang-16", conf: "Debug" , defs: "ASMJIT_TEST=1" }
- { title: "linux" , host: "ubuntu-22.04" , arch: "x64" , cc: "clang-16", conf: "Release", defs: "ASMJIT_TEST=1" }
- { title: "linux" , host: "ubuntu-22.04" , arch: "x86" , cc: "clang-17", conf: "Debug" , defs: "ASMJIT_TEST=1" }
- { title: "linux" , host: "ubuntu-22.04" , arch: "x86" , cc: "clang-17", conf: "Release", defs: "ASMJIT_TEST=1" }
- { title: "linux" , host: "ubuntu-22.04" , arch: "x64" , cc: "clang-17", conf: "Debug" , defs: "ASMJIT_TEST=1" }
- { title: "linux" , host: "ubuntu-22.04" , arch: "x64" , cc: "clang-17", conf: "Release", defs: "ASMJIT_TEST=1" }
- { title: "linux" , host: "ubuntu-22.04" , arch: "x86" , cc: "clang-18", conf: "Debug" , defs: "ASMJIT_TEST=1" }
- { title: "linux" , host: "ubuntu-22.04" , arch: "x86" , cc: "clang-18", conf: "Release", defs: "ASMJIT_TEST=1" }
- { title: "linux" , host: "ubuntu-22.04" , arch: "x64" , cc: "clang-18", conf: "Debug" , defs: "ASMJIT_TEST=1" }
- { title: "linux" , host: "ubuntu-22.04" , arch: "x64" , cc: "clang-18", conf: "Release", defs: "ASMJIT_TEST=1" }
- { title: "macos" , host: "macos-12" , arch: "x64" , cc: "gcc-11" , conf: "Debug" , defs: "ASMJIT_TEST=1" }
- { title: "macos" , host: "macos-12" , arch: "x64" , cc: "gcc-11" , conf: "Release", defs: "ASMJIT_TEST=1" }
- { title: "macos" , host: "macos-12" , arch: "x64" , cc: "clang" , conf: "Debug" , defs: "ASMJIT_TEST=1" }
- { title: "macos" , host: "macos-12" , arch: "x64" , cc: "clang" , conf: "Release", defs: "ASMJIT_TEST=1" }
- { title: "macos" , host: "macos-14" , arch: "arm64" , cc: "clang" , conf: "Debug" , defs: "ASMJIT_TEST=1" }
- { title: "macos" , host: "macos-14" , arch: "arm64" , cc: "clang" , conf: "Release", defs: "ASMJIT_TEST=1" }
- { title: "windows" , host: "windows-2019" , arch: "x86" , cc: "vs2019" , conf: "Debug" , defs: "ASMJIT_TEST=1" }
- { title: "windows" , host: "windows-2019" , arch: "x86" , cc: "vs2019" , conf: "Release", defs: "ASMJIT_TEST=1" }
- { title: "windows" , host: "windows-2019" , arch: "x64" , cc: "vs2019" , conf: "Debug" , defs: "ASMJIT_TEST=1" }
- { title: "windows" , host: "windows-2019" , arch: "x64" , cc: "vs2019" , conf: "Release", defs: "ASMJIT_TEST=1" }
- { title: "windows" , host: "windows-2022" , arch: "x86" , cc: "vs2022" , conf: "Debug" , defs: "ASMJIT_TEST=1" }
- { title: "windows" , host: "windows-2022" , arch: "x86" , cc: "vs2022" , conf: "Release", defs: "ASMJIT_TEST=1" }
- { title: "windows" , host: "windows-2022" , arch: "x64" , cc: "vs2022" , conf: "Debug" , defs: "ASMJIT_TEST=1" }
- { title: "windows" , host: "windows-2022" , arch: "x64" , cc: "vs2022" , conf: "Release", defs: "ASMJIT_TEST=1" }
- { title: "linux" , os: "ubuntu-20.04" , cc: "gcc-7" , arch: "x86", build_type: "Debug" , defs: "ASMJIT_TEST=1" }
- { title: "linux" , os: "ubuntu-20.04" , cc: "gcc-7" , arch: "x86", build_type: "Release", defs: "ASMJIT_TEST=1" }
- { title: "linux" , os: "ubuntu-20.04" , cc: "gcc-7" , arch: "x64", build_type: "Debug" , defs: "ASMJIT_TEST=1" }
- { title: "linux" , os: "ubuntu-20.04" , cc: "gcc-7" , arch: "x64", build_type: "Release", defs: "ASMJIT_TEST=1" }
- { title: "linux" , os: "ubuntu-20.04" , cc: "gcc-8" , arch: "x86", build_type: "Debug" , defs: "ASMJIT_TEST=1" }
- { title: "linux" , os: "ubuntu-20.04" , cc: "gcc-8" , arch: "x86", build_type: "Release", defs: "ASMJIT_TEST=1" }
- { title: "linux" , os: "ubuntu-20.04" , cc: "gcc-8" , arch: "x64", build_type: "Debug" , defs: "ASMJIT_TEST=1" }
- { title: "linux" , os: "ubuntu-20.04" , cc: "gcc-8" , arch: "x64", build_type: "Release", defs: "ASMJIT_TEST=1" }
- { title: "linux" , os: "ubuntu-22.04" , cc: "gcc-9" , arch: "x86", build_type: "Debug" , defs: "ASMJIT_TEST=1" }
- { title: "linux" , os: "ubuntu-22.04" , cc: "gcc-9" , arch: "x86", build_type: "Release", defs: "ASMJIT_TEST=1" }
- { title: "linux" , os: "ubuntu-22.04" , cc: "gcc-9" , arch: "x64", build_type: "Debug" , defs: "ASMJIT_TEST=1" }
- { title: "linux" , os: "ubuntu-22.04" , cc: "gcc-9" , arch: "x64", build_type: "Release", defs: "ASMJIT_TEST=1" }
- { title: "linux" , os: "ubuntu-22.04" , cc: "gcc-10" , arch: "x86", build_type: "Debug" , defs: "ASMJIT_TEST=1" }
- { title: "linux" , os: "ubuntu-22.04" , cc: "gcc-10" , arch: "x86", build_type: "Release", defs: "ASMJIT_TEST=1" }
- { title: "linux" , os: "ubuntu-22.04" , cc: "gcc-10" , arch: "x64", build_type: "Debug" , defs: "ASMJIT_TEST=1" }
- { title: "linux" , os: "ubuntu-22.04" , cc: "gcc-10" , arch: "x64", build_type: "Release", defs: "ASMJIT_TEST=1" }
- { title: "linux" , os: "ubuntu-22.04" , cc: "gcc-11" , arch: "x86", build_type: "Debug" , defs: "ASMJIT_TEST=1" }
- { title: "linux" , os: "ubuntu-22.04" , cc: "gcc-11" , arch: "x86", build_type: "Release", defs: "ASMJIT_TEST=1" }
- { title: "linux" , os: "ubuntu-22.04" , cc: "gcc-11" , arch: "x64", build_type: "Debug" , defs: "ASMJIT_TEST=1" }
- { title: "linux" , os: "ubuntu-22.04" , cc: "gcc-11" , arch: "x64", build_type: "Release", defs: "ASMJIT_TEST=1" }
- { title: "linux" , os: "ubuntu-22.04" , cc: "gcc-12" , arch: "x86", build_type: "Debug" , defs: "ASMJIT_TEST=1" }
- { title: "linux" , os: "ubuntu-22.04" , cc: "gcc-12" , arch: "x86", build_type: "Release", defs: "ASMJIT_TEST=1" }
- { title: "linux" , os: "ubuntu-22.04" , cc: "gcc-12" , arch: "x64", build_type: "Debug" , defs: "ASMJIT_TEST=1" }
- { title: "linux" , os: "ubuntu-22.04" , cc: "gcc-12" , arch: "x64", build_type: "Release", defs: "ASMJIT_TEST=1" }
- { title: "linux" , os: "ubuntu-20.04" , cc: "clang-10", arch: "x86", build_type: "Debug" , defs: "ASMJIT_TEST=1" }
- { title: "linux" , os: "ubuntu-20.04" , cc: "clang-10", arch: "x86", build_type: "Release", defs: "ASMJIT_TEST=1" }
- { title: "linux" , os: "ubuntu-20.04" , cc: "clang-10", arch: "x64", build_type: "Debug" , defs: "ASMJIT_TEST=1" }
- { title: "linux" , os: "ubuntu-20.04" , cc: "clang-10", arch: "x64", build_type: "Release", defs: "ASMJIT_TEST=1" }
- { title: "linux" , os: "ubuntu-22.04" , cc: "clang-11", arch: "x86", build_type: "Debug" , defs: "ASMJIT_TEST=1" }
- { title: "linux" , os: "ubuntu-22.04" , cc: "clang-11", arch: "x86", build_type: "Release", defs: "ASMJIT_TEST=1" }
- { title: "linux" , os: "ubuntu-22.04" , cc: "clang-11", arch: "x64", build_type: "Debug" , defs: "ASMJIT_TEST=1" }
- { title: "linux" , os: "ubuntu-22.04" , cc: "clang-11", arch: "x64", build_type: "Release", defs: "ASMJIT_TEST=1" }
- { title: "linux" , os: "ubuntu-22.04" , cc: "clang-12", arch: "x86", build_type: "Debug" , defs: "ASMJIT_TEST=1" }
- { title: "linux" , os: "ubuntu-22.04" , cc: "clang-12", arch: "x86", build_type: "Release", defs: "ASMJIT_TEST=1" }
- { title: "linux" , os: "ubuntu-22.04" , cc: "clang-12", arch: "x64", build_type: "Debug" , defs: "ASMJIT_TEST=1" }
- { title: "linux" , os: "ubuntu-22.04" , cc: "clang-12", arch: "x64", build_type: "Release", defs: "ASMJIT_TEST=1" }
- { title: "linux" , os: "ubuntu-22.04" , cc: "clang-13", arch: "x86", build_type: "Debug" , defs: "ASMJIT_TEST=1" }
- { title: "linux" , os: "ubuntu-22.04" , cc: "clang-13", arch: "x86", build_type: "Release", defs: "ASMJIT_TEST=1" }
- { title: "linux" , os: "ubuntu-22.04" , cc: "clang-13", arch: "x64", build_type: "Debug" , defs: "ASMJIT_TEST=1" }
- { title: "linux" , os: "ubuntu-22.04" , cc: "clang-13", arch: "x64", build_type: "Release", defs: "ASMJIT_TEST=1" }
- { title: "linux" , os: "ubuntu-22.04" , cc: "clang-14", arch: "x86", build_type: "Debug" , defs: "ASMJIT_TEST=1" }
- { title: "linux" , os: "ubuntu-22.04" , cc: "clang-14", arch: "x86", build_type: "Release", defs: "ASMJIT_TEST=1" }
- { title: "linux" , os: "ubuntu-22.04" , cc: "clang-14", arch: "x64", build_type: "Debug" , defs: "ASMJIT_TEST=1" }
- { title: "linux" , os: "ubuntu-22.04" , cc: "clang-14", arch: "x64", build_type: "Release", defs: "ASMJIT_TEST=1" }
- { title: "macos" , os: "macos-12" , cc: "gcc-11" , arch: "x64", build_type: "Debug" , defs: "ASMJIT_TEST=1" }
- { title: "macos" , os: "macos-12" , cc: "gcc-11" , arch: "x64", build_type: "Release", defs: "ASMJIT_TEST=1" }
- { title: "macos" , os: "macos-12" , cc: "clang" , arch: "x64", build_type: "Debug" , defs: "ASMJIT_TEST=1" }
- { title: "macos" , os: "macos-12" , cc: "clang" , arch: "x64", build_type: "Release", defs: "ASMJIT_TEST=1" }
- { title: "windows" , os: "windows-2019" , cc: "vs2019" , arch: "x86", build_type: "Debug" , defs: "ASMJIT_TEST=1" }
- { title: "windows" , os: "windows-2019" , cc: "vs2019" , arch: "x86", build_type: "Release", defs: "ASMJIT_TEST=1" }
- { title: "windows" , os: "windows-2019" , cc: "vs2019" , arch: "x64", build_type: "Debug" , defs: "ASMJIT_TEST=1" }
- { title: "windows" , os: "windows-2019" , cc: "vs2019" , arch: "x64", build_type: "Release", defs: "ASMJIT_TEST=1" }
- { title: "windows" , os: "windows-2022" , cc: "vs2022" , arch: "x86", build_type: "Debug" , defs: "ASMJIT_TEST=1" }
- { title: "windows" , os: "windows-2022" , cc: "vs2022" , arch: "x86", build_type: "Release", defs: "ASMJIT_TEST=1" }
- { title: "windows" , os: "windows-2022" , cc: "vs2022" , arch: "x64", build_type: "Debug" , defs: "ASMJIT_TEST=1" }
- { title: "windows" , os: "windows-2022" , cc: "vs2022" , arch: "x64", build_type: "Release", defs: "ASMJIT_TEST=1" }
- { title: "freebsd" , host: "macos-12" , arch: "x86-64" , cc: "clang" , conf: "Release", vm: "freebsd", vm_ver: "13.2", defs: "ASMJIT_TEST=1" }
- { title: "netbsd" , host: "macos-12" , arch: "x86-64" , cc: "clang" , conf: "Release", vm: "netbsd" , vm_ver: "9.3" , defs: "ASMJIT_TEST=1" }
- { title: "openbsd" , host: "macos-12" , arch: "x86-64" , cc: "clang" , conf: "Release", vm: "openbsd", vm_ver: "7.4" , defs: "ASMJIT_TEST=1" }
- { title: "openbsd" , host: "ubuntu-latest" , arch: "arm64" , cc: "clang" , conf: "Release", vm: "openbsd", vm_ver: "7.4" , defs: "ASMJIT_TEST=1" }
- { host: "macos-12" , os: "freebsd", osver: "13.2", cc: "clang", arch: "x86-64", build_type: "Release", defs: "ASMJIT_TEST=ON" }
- { host: "macos-12" , os: "netbsd" , osver: "9.3" , cc: "clang", arch: "x86-64", build_type: "Release", defs: "ASMJIT_TEST=ON" }
- { host: "macos-12" , os: "openbsd", osver: "7.3" , cc: "clang", arch: "x86-64", build_type: "Release", defs: "ASMJIT_TEST=ON" }
- { host: "ubuntu-latest" , os: "openbsd", osver: "7.3" , cc: "clang", arch: "arm64" , build_type: "Release", defs: "ASMJIT_TEST=ON" }
# arm/v7 VM image doesn't work on CI environment at the moment
# - { title: "debian" , host: "ubuntu-latest" , arch: "arm/v7" , cc: "clang" , conf: "Release", vm: "debian:unstable", defs: "ASMJIT_TEST=1" }
name: "${{matrix.title || format('{0}-{1}', matrix.os, matrix.osver)}} (${{matrix.cc}}, ${{matrix.arch}}, ${{matrix.build_type}})"
runs-on: "${{matrix.host || matrix.os}}"
- { title: "debian" , host: "ubuntu-latest" , arch: "arm64" , cc: "clang" , conf: "Release", vm: "debian:unstable", defs: "ASMJIT_TEST=1" }
- { title: "debian" , host: "ubuntu-latest" , arch: "riscv64", cc: "clang" , conf: "Release", vm: "debian:unstable", defs: "ASMJIT_TEST=1" }
- { title: "debian" , host: "ubuntu-latest" , arch: "ppc64le", cc: "clang" , conf: "Release", vm: "debian:unstable", defs: "ASMJIT_TEST=1" }
name: "${{matrix.title}}/${{matrix.arch}}, ${{matrix.cc}} ${{matrix.conf}}"
runs-on: "${{matrix.host}}"
steps:
- name: "Checkout"
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
path: "source"
- name: "Checkout Build Actions"
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
repository: build-actions/build-actions
path: "build-actions"
- name: "Python"
uses: actions/setup-python@v4
uses: actions/setup-python@v5
with:
python-version: "3.x"
- name: "Build & Test"
if: ${{!matrix.host}}
- name: QEMU
if: ${{matrix.vm && !matrix.vm_ver}}
uses: docker/setup-qemu-action@v3
with:
platforms: linux/${{matrix.arch}}
- name: "Build & Test - Native"
if: ${{!matrix.vm}}
run: python build-actions/action.py
--step=all
--compiler=${{matrix.cc}}
--architecture=${{matrix.arch}}
--source-dir=source
--config=source/.github/workflows/build-config.json
--build-type=${{matrix.build_type}}
--compiler=${{matrix.cc}}
--diagnostics=${{matrix.diagnostics}}
--architecture=${{matrix.arch}}
--problem-matcher=auto
--build-type=${{matrix.conf}}
--build-defs=${{matrix.defs}}
- name: "Build & Test in VM"
if: ${{matrix.host}}
- name: "Build & Test - Cross Platform Actions"
if: ${{matrix.vm && matrix.vm_ver}}
uses: cross-platform-actions/action@master
with:
operating_system: ${{matrix.os}}
operating_system: ${{matrix.vm}}
architecture: ${{matrix.arch}}
version: ${{matrix.osver}}
version: ${{matrix.vm_ver}}
sync_files: "runner-to-vm"
shutdown_vm: false
shell: bash
run: |
set -e
@ -164,10 +200,30 @@ jobs:
sh ./build-actions/prepare-environment.sh
python3 build-actions/action.py \
--step=all \
--compiler=${{matrix.cc}} \
--architecture=${{matrix.arch}} \
--source-dir=source \
--config=source/.github/workflows/build-config.json \
--build-type=${{matrix.build_type}} \
--compiler=${{matrix.cc}} \
--diagnostics=${{matrix.diagnostics}} \
--architecture=${{matrix.arch}} \
--problem-matcher=auto \
--build-type=${{matrix.conf}} \
--build-defs=${{matrix.defs}}
- name: "Build & Test - Docker + QEMU"
if: ${{matrix.vm && !matrix.vm_ver}}
run: |
docker run \
--rm \
-v $(pwd):/${{github.workspace}} \
-w ${{github.workspace}}/build-actions \
--platform linux/${{matrix.arch}} \
${{matrix.vm}} \
bash action.sh \
--source-dir=../source \
--config=../source/.github/workflows/build-config.json \
--compiler=${{matrix.cc}} \
--diagnostics=${{matrix.diagnostics}} \
--architecture=${{matrix.arch}} \
--problem-matcher=auto \
--build-type=${{matrix.conf}} \
--build-defs=${{matrix.defs}}

View File

@ -1,12 +1,12 @@
cmake_minimum_required(VERSION 3.5 FATAL_ERROR)
cmake_minimum_required(VERSION 3.8 FATAL_ERROR)
cmake_policy(PUSH)
if(POLICY CMP0063)
if (POLICY CMP0063)
cmake_policy(SET CMP0063 NEW) # Honor visibility properties.
endif()
if(POLICY CMP0092)
if (POLICY CMP0092)
cmake_policy(SET CMP0092 NEW) # Don't add -W3 warning level by default.
endif()
@ -33,15 +33,13 @@ if (DEFINED ASMJIT_BUILD_STATIC)
set(ASMJIT_STATIC "${ASMJIT_BUILD_STATIC}")
endif()
# AsmJit - Configuration
# ======================
# AsmJit - Configuration - Build
# ==============================
# AsmJit testing.
if (NOT DEFINED ASMJIT_TEST)
set(ASMJIT_TEST FALSE)
endif()
# AsmJit build options
if (NOT DEFINED ASMJIT_EMBED)
set(ASMJIT_EMBED FALSE)
endif()
@ -54,15 +52,22 @@ if (NOT DEFINED ASMJIT_SANITIZE)
set(ASMJIT_SANITIZE FALSE)
endif()
if (NOT DEFINED ASMJIT_NO_NATVIS)
set(ASMJIT_NO_NATVIS FALSE)
endif()
if (NOT DEFINED ASMJIT_NO_CUSTOM_FLAGS)
set(ASMJIT_NO_CUSTOM_FLAGS FALSE)
endif()
# AsmJit backends selection.
if (NOT DEFINED ASMJIT_NO_NATVIS)
set(ASMJIT_NO_NATVIS FALSE)
endif()
# EMBED implies STATIC.
if (ASMJIT_EMBED AND NOT ASMJIT_STATIC)
set(ASMJIT_STATIC TRUE)
endif()
# AsmJit - Configuration - Backend
# ================================
if (NOT DEFINED ASMJIT_NO_X86)
set(ASMJIT_NO_X86 FALSE)
endif()
@ -75,7 +80,9 @@ if (NOT DEFINED ASMJIT_NO_FOREIGN)
set(ASMJIT_NO_FOREIGN FALSE)
endif()
# AsmJit features selection.
# AsmJit - Configuration - Features
# =================================
if (NOT DEFINED ASMJIT_NO_DEPRECATED)
set(ASMJIT_NO_DEPRECATED FALSE)
endif()
@ -89,19 +96,19 @@ if (NOT DEFINED ASMJIT_NO_JIT)
endif()
if (NOT DEFINED ASMJIT_NO_TEXT)
set(ASMJIT_NO_TEXT ${ASMJIT_NO_TEXT})
set(ASMJIT_NO_TEXT FALSE)
endif()
if (NOT DEFINED ASMJIT_NO_LOGGING)
set(ASMJIT_NO_LOGGING FALSE)
set(ASMJIT_NO_LOGGING ${ASMJIT_NO_TEXT})
endif()
if (NOT DEFINED ASMJIT_NO_VALIDATION)
set(ASMJIT_NO_VALIDATION ${ASMJIT_NO_VALIDATION})
set(ASMJIT_NO_VALIDATION FALSE)
endif()
if (NOT DEFINED ASMJIT_NO_INTROSPECTION)
set(ASMJIT_NO_INTROSPECTION ${ASMJIT_NO_INTROSPECTION})
set(ASMJIT_NO_INTROSPECTION FALSE)
endif()
if (NOT DEFINED ASMJIT_NO_BUILDER)
@ -109,16 +116,17 @@ if (NOT DEFINED ASMJIT_NO_BUILDER)
endif()
if (NOT DEFINED ASMJIT_NO_COMPILER)
set(ASMJIT_NO_BUILDER ${ASMJIT_NO_BUILDER})
if (ASMJIT_NO_BUILDER OR ASMJIT_NO_INTROSPECTION)
set(ASMJIT_NO_COMPILER TRUE)
else()
set(ASMJIT_NO_COMPILER FALSE)
endif()
endif()
# EMBED implies STATIC.
if (ASMJIT_EMBED AND NOT ASMJIT_STATIC)
set(ASMJIT_STATIC TRUE)
endif()
# AsmJit - Configuration - CMake Introspection
# ============================================
set(ASMJIT_DIR "${CMAKE_CURRENT_LIST_DIR}" CACHE PATH "Location of 'asmjit'")
set(ASMJIT_TEST "${ASMJIT_TEST}" CACHE BOOL "Build 'asmjit' test applications")
set(ASMJIT_EMBED "${ASMJIT_EMBED}" CACHE BOOL "Embed 'asmjit' library (no targets)")
set(ASMJIT_STATIC "${ASMJIT_STATIC}" CACHE BOOL "Build 'asmjit' library as static")
@ -212,11 +220,7 @@ function(asmjit_add_target target target_type)
set_property(TARGET ${target} APPEND_STRING PROPERTY LINK_FLAGS " ${link_flag}")
endforeach()
if (${CMAKE_VERSION} VERSION_LESS "3.8.0")
set_property(TARGET ${target} PROPERTY CXX_STANDARD 11)
else()
target_compile_features(${target} PUBLIC cxx_std_11)
endif()
set_property(TARGET ${target} PROPERTY CXX_EXTENSIONS NO)
set_property(TARGET ${target} PROPERTY CXX_VISIBILITY_PRESET hidden)
target_compile_options(${target} PRIVATE ${X_CFLAGS} ${ASMJIT_SANITIZE_CFLAGS} $<$<CONFIG:Debug>:${X_CFLAGS_DBG}> $<$<NOT:$<CONFIG:Debug>>:${X_CFLAGS_REL}>)
@ -229,17 +233,6 @@ endfunction()
# AsmJit - Compiler Support
# =========================
set(ASMJIT_INCLUDE_DIRS "${ASMJIT_DIR}/src") # Include directory is the same as source dir.
set(ASMJIT_DEPS "") # AsmJit dependencies (libraries) for the linker.
set(ASMJIT_LIBS "") # Dependencies of libs/apps that want to use AsmJit.
set(ASMJIT_CFLAGS "") # Public compiler flags.
set(ASMJIT_PRIVATE_CFLAGS "") # Private compiler flags independent of build type.
set(ASMJIT_PRIVATE_CFLAGS_DBG "") # Private compiler flags used by debug builds.
set(ASMJIT_PRIVATE_CFLAGS_REL "") # Private compiler flags used by release builds.
set(ASMJIT_PRIVATE_LFLAGS "") # Private linker flags.
set(ASMJIT_SANITIZE_CFLAGS "") # Compiler flags required by currently enabled sanitizers.
set(ASMJIT_SANITIZE_LFLAGS "") # Linker flags required by currently enabled sanitizers.
# We will have to keep this most likely forever as some users may still be using it.
set(ASMJIT_INCLUDE_DIR "${ASMJIT_INCLUDE_DIRS}")
@ -282,7 +275,7 @@ endif()
# Support for sanitizers.
if (ASMJIT_SANITIZE)
ASMJIT_detect_sanitizers(ASMJIT_SANITIZE_CFLAGS ${ASMJIT_SANITIZE})
asmjit_detect_sanitizers(ASMJIT_SANITIZE_CFLAGS ${ASMJIT_SANITIZE})
if (ASMJIT_SANITIZE_CFLAGS)
message("-- Enabling sanitizers: '${ASMJIT_SANITIZE_CFLAGS}'")
@ -363,21 +356,21 @@ else()
set(ASMJIT_TARGET_TYPE "SHARED")
endif()
foreach(build_option ASMJIT_STATIC
foreach(build_option # AsmJit build options.
ASMJIT_STATIC
ASMJIT_NO_DEPRECATED
# AsmJit backends selection.
ASMJIT_NO_X86
ASMJIT_NO_AARCH64
ASMJIT_NO_FOREIGN
# AsmJit features selection.
ASMJIT_NO_DEPRECATED
ASMJIT_NO_SHM_OPEN
ASMJIT_NO_JIT
ASMJIT_NO_TEXT
ASMJIT_NO_LOGGING
ASMJIT_NO_BUILDER
ASMJIT_NO_COMPILER
ASMJIT_NO_INTROSPECTION
ASMJIT_NO_VALIDATION
ASMJIT_NO_INTROSPECTION)
ASMJIT_NO_BUILDER
ASMJIT_NO_COMPILER)
if (${build_option})
List(APPEND ASMJIT_CFLAGS "-D${build_option}")
List(APPEND ASMJIT_PRIVATE_CFLAGS "-D${build_option}")
@ -388,7 +381,7 @@ endforeach()
# =======================
if (WIN32)
if(CMAKE_LINKER MATCHES "link\\.exe" OR CMAKE_LINKER MATCHES "lld-link\\.exe")
if (CMAKE_LINKER MATCHES "link\\.exe" OR CMAKE_LINKER MATCHES "lld-link\\.exe")
set(ASMJIT_LINKER_SUPPORTS_NATVIS TRUE)
endif()
endif()
@ -443,6 +436,8 @@ set(ASMJIT_SRC_LIST
asmjit/core/globals.h
asmjit/core/inst.cpp
asmjit/core/inst.h
asmjit/core/instdb.cpp
asmjit/core/instdb_p.h
asmjit/core/jitallocator.cpp
asmjit/core/jitallocator.h
asmjit/core/jitruntime.cpp
@ -562,9 +557,7 @@ foreach(src_file ${ASMJIT_SRC_LIST})
endif()
endforeach()
if (NOT ${CMAKE_VERSION} VERSION_LESS "3.8.0")
source_group(TREE "${ASMJIT_DIR}" FILES ${ASMJIT_SRC})
endif()
source_group(TREE "${ASMJIT_DIR}" FILES ${ASMJIT_SRC})
# AsmJit - Summary
# ================
@ -597,7 +590,7 @@ if (NOT ASMJIT_EMBED)
$<BUILD_INTERFACE:${ASMJIT_INCLUDE_DIRS}>
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>)
# Add blend2d::blend2d alias.
# Add asmjit::asmjit alias.
add_library(asmjit::asmjit ALIAS asmjit)
# TODO: [CMAKE] Deprecated alias - we use projectname::libraryname convention now.
add_library(AsmJit::AsmJit ALIAS asmjit)
@ -662,6 +655,7 @@ if (NOT ASMJIT_EMBED)
CFLAGS_REL ${ASMJIT_PRIVATE_CFLAGS_REL})
foreach(_target asmjit_test_emitters
asmjit_test_execute
asmjit_test_x86_sections)
asmjit_add_target(${_target} TEST
SOURCES test/${_target}.cpp
@ -682,11 +676,29 @@ if (NOT ASMJIT_EMBED)
if (NOT (ASMJIT_NO_BUILDER OR ASMJIT_NO_COMPILER))
# Vectorcall tests and XMM tests require at least SSE2 in 32-bit mode (in 64-bit mode it's implicit).
set(sse2_flags "")
# Some compilers don't like passing -msse2 for 64-bit targets, and some compilers targeting non-x86
# would pass "-msse2" compile flag check, but with a warning not detected by CMake. Thus, verify that
# our target is really 32-bit X86 and only use -msse2 or -arch:SSE2 flags when necessary.
set(ASMJIT_SSE2_CFLAGS "")
check_cxx_source_compiles("
#if defined(_M_IX86) || defined(__X86__) || defined(__i386__)
int target_is_32_bit_x86() { return 1; }
#else
// Compile error...
#endif
int main() {
return target_is_32_bit_x86();
}
" ASMJIT_TARGET_IS_32_BIT_X86)
if (ASMJIT_TARGET_IS_32_BIT_X86)
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC" OR "x${CMAKE_CXX_COMPILER_FRONTEND_VARIANT}" STREQUAL "xMSVC")
asmjit_detect_cflags(sse2_flags "-arch:SSE2")
asmjit_detect_cflags(ASMJIT_SSE2_CFLAGS "-arch:SSE2")
else()
asmjit_detect_cflags(sse2_flags "-msse2")
asmjit_detect_cflags(ASMJIT_SSE2_CFLAGS "-msse2")
endif()
endif()
asmjit_add_target(asmjit_test_compiler TEST
SOURCES test/asmjit_test_compiler.cpp
@ -694,7 +706,7 @@ if (NOT ASMJIT_EMBED)
test/asmjit_test_compiler_a64.cpp
test/asmjit_test_compiler_x86.cpp
LIBRARIES asmjit::asmjit
CFLAGS ${ASMJIT_PRIVATE_CFLAGS} ${sse2_flags}
CFLAGS ${ASMJIT_PRIVATE_CFLAGS} ${ASMJIT_SSE2_CFLAGS}
CFLAGS_DBG ${ASMJIT_PRIVATE_CFLAGS_DBG}
CFLAGS_REL ${ASMJIT_PRIVATE_CFLAGS_REL})
endif()

102
deps/asmjit/CONTRIBUTING.md vendored Normal file
View File

@ -0,0 +1,102 @@
## How to Contribute to AsmJit
### Did you find a bug or something isn't working as expected?
* Please use [Issues](https://github.com/asmjit/asmjit/issues) page to report bugs or create a [pull request](https://github.com/asmjit/asmjit/pulls) if you have already fixed it.
* Make sure that when a bug is reported it provides as much information as possible to make it easy to either reproduce it locally or to at least guess where the problem could be. AsmJit is a low-level tool, which makes it very easy to emit code that would crash or not work as intended when executed. Always use AsmJit's [Logging](https://asmjit.com/doc/group__asmjit__logging.html) and [Error Handling](https://asmjit.com/doc/group__asmjit__error__handling.html) features first to analyze whether there is not a simple to catch bug in your own code.
* Don't be afraid to ask for help if you don't know how to solve a particular problem or in case it's unclear how to do it. The community would help if the problem is well described and has a solution. In general we always try to at least improve the documentation in case it doesn't provide enough information and users must ask for help.
### Asking questions
* We prefer GitHub issues to be used for reporting bugs or feature requests, but it's still okay to ask questions there as well. However, please consider joining our [Gitter Chat](https://app.gitter.im/#/room/#asmjit:gitter.im) to ask questions; it has an active community that can quickly respond.
### Suggesting feature requests
* It's very likely that when using AsmJit you have found something that AsmJit doesn't provide, which would be handy to have as a built-in. The [Issues](https://github.com/asmjit/asmjit/issues) page can be used to submit feature requests, but please keep in mind that AsmJit is a relatively small project and not all requested features will be accepted, especially if they are non-trivial, time consuming to implement, or the scope of the feature doesn't match AsmJit goals.
* If you have already implemented the feature you are suggesting, please open a [pull request](https://github.com/asmjit/asmjit/pulls).
* Ports (requesting new AsmJit backends) can be reported as feature requests, but only by people that are willing to work on them as creating new ports takes a lot of time.
### Suggesting a documentation enhancement
* [AsmJit's documentation](https://asmjit.com/doc/index.html) is auto-generated from source code, so if you would like to improve it just open a [pull request](https://github.com/asmjit/asmjit/pulls) with your changes. The documentation uses [Doxygen](https://www.doxygen.nl/) as a front-end, so you can use `\ref` keyword to create links and other Doxygen keywords to enhance the documentation.
### Suggesting a website content enhancement
* [AsmJit's website](https://asmjit.com) is also generated, but not from public sources at the moment. If you did find an issue on the website you can either use contact information on the [support page](https://asmjit.com/support.html) or to discuss the change on our [Gitter Chat](https://app.gitter.im/#/room/#asmjit:gitter.im). Alternatively, opening a regular issue is also okay.
## Coding Style & Consistency
* If you decide to open a pull request, make sure that the code you submit uses the same convention as the rest of the code. We prefer keeping the code consistent.
* [.editorconfig](./.editorconfig) should help with basic settings.
* Initially, AsmJit coding style was based on Google C++ Style Guide, but it has diverged from it.
* Include guards use `<PATH_TO_SRC>_H_INCLUDED` format.
* `asmjit` namespace must be open by `ASMJIT_BEGIN_NAMESPACE` and closed by `ASMJIT_END_NAMESPACE`
* `asmjit::xxx` (backend specific) nested namespace must be open by `ASMJIT_BEGIN_SUB_NAMESPACE(xxx)` and closed by `ASMJIT_END_SUB_NAMESPACE`.
* Opening bracket is on the same line, like `struct Something {`, `if (condition) {`, etc...
* The code uses a soft limit of 120 characters per line (including documentation), but it's not enforced and it's okay to use more when it makes sense (for example defining tables, etc...).
* Since AsmJit doesn't use Exceptions nor RTTI the code cannot use containers provided by the C++ standard library. In general, we try to only use a bare minimum from the C++ standard library to make it viable to use AsmJit even in C code bases where JIT complier is implemented in C++ ([Erlang](https://www.erlang.org/) can be seen as a great example).
## Testing
* AsmJit uses a minimalist unit testing framework to write unit tests to avoid third-party dependencies.
* At the moment tests are in the same file as the implementation and are only compiled when `ASMJIT_TEST` macro is defined.
* Use `-DASMJIT_TEST=1` when invoking [CMake](https://cmake.org/) to compile AsmJit tests.
* Unit tests are compiled to a single `asmjit_test_unit[.exe]` executable.
* Other tests have their own executables based on what is tested.
* Always add assembler tests when adding new instructions, see [asmjit_test_assembler_x64.cpp](./test/asmjit_test_assembler_x64.cpp) and [asmjit_test_assembler_a64.cpp](./test/asmjit_test_assembler_a64.cpp) for more details.
## Pull Request Messages
* If a change fixes a bug the message should should start with `[bug]`.
* If a change fixes or enhances documentation it should start with `[doc]`.
* If a change fixes or enhances our CI it should start with `[ci]`.
* If a change breaks ABI it must start with `[abi]`.
* Otherwise there is no suggested prefix.
## ABI Changes
* ABI changes happen, but they are usually accumulated and committed within a short time window to not break it often. In general we prefer to break ABI once a year, or once 6 months if there is something that has a high priority. There are no hard rules though.
* AsmJit uses an `inline namespace`, which should make it impossible to link to AsmJit library that is ABI incompatible. When ABI break happens both AsmJit version and ABI namespace are changed, see [asmjit/core/api-config.h](./src/asmjit/core/api-config.h) for more details.
* What is an ABI break?
* Modifying a public struct/class in a way that its functionality is altered and/or its size is changed
* Adding/removing virtual functions to/from classes, respectively
* Changing a signature of a public function or a class member function (for example adding a parameter).
* Changing the value of an enum or global constant (for example instructions are now sorted by name, so adding a new instruction breaks ABI)
* Possibly more, but these were the most common...
* What is not ABI break?
* Extending the functionality by using reserved members of a struct/class
* Adding new API including new structs and classes
* Changing anything that is internal and that doesn't leak to public headers

View File

@ -1,4 +1,4 @@
Copyright (c) 2008-2020 The AsmJit Authors
Copyright (c) 2008-2024 The AsmJit Authors
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages

45
deps/asmjit/README.md vendored
View File

@ -5,7 +5,7 @@ AsmJit is a lightweight library for machine code generation written in C++ langu
* [Official Home Page (asmjit.com)](https://asmjit.com)
* [Official Repository (asmjit/asmjit)](https://github.com/asmjit/asmjit)
* [Public Chat Channel](https://gitter.im/asmjit/asmjit)
* [Public Chat Channel](https://app.gitter.im/#/room/#asmjit:gitter.im)
* [Zlib License](./LICENSE.md)
See [asmjit.com](https://asmjit.com) page for more details, examples, and documentation.
@ -16,39 +16,42 @@ Documentation
* [Documentation Index](https://asmjit.com/doc/index.html)
* [Build Instructions](https://asmjit.com/doc/group__asmjit__build.html)
Contributing
------------
* See [CONTRIBUTING](./CONTRIBUTING.md) page for more details
Breaking Changes
----------------
Breaking the API is sometimes inevitable, what to do?
* See [Breaking Changes Guide](https://asmjit.com/doc/group__asmjit__breaking__changes.html), which is now part of AsmJit documentation.
* See [Breaking Changes Guide](https://asmjit.com/doc/group__asmjit__breaking__changes.html), which is now part of AsmJit documentation
* See asmjit tests, they always compile and provide implementation of many use-cases:
* [asmjit_test_emitters.cpp](./test/asmjit_test_emitters.cpp) - Tests that demonstrate the purpose of emitters.
* [asmjit_test_assembler_x86.cpp](./test/asmjit_test_assembler_x86.cpp) - Tests targeting AsmJit's Assembler (x86/x64).
* [asmjit_test_compiler_x86.cpp](./test/asmjit_test_compiler_x86.cpp) - Tests targeting AsmJit's Compiler (x86/x64).
* [asmjit_test_instinfo.cpp](./test/asmjit_test_instinfo.cpp) - Tests that query instruction information.
* [asmjit_test_emitters.cpp](./test/asmjit_test_emitters.cpp) - Tests that demonstrate the purpose of emitters
* [asmjit_test_assembler_x86.cpp](./test/asmjit_test_assembler_x86.cpp) - Tests targeting AsmJit's Assembler (x86/x64)
* [asmjit_test_compiler_x86.cpp](./test/asmjit_test_compiler_x86.cpp) - Tests targeting AsmJit's Compiler (x86/x64)
* [asmjit_test_instinfo.cpp](./test/asmjit_test_instinfo.cpp) - Tests that query instruction information
* [asmjit_test_x86_sections.cpp](./test/asmjit_test_x86_sections.cpp) - Multiple sections test.
* Visit our [Official Chat](https://gitter.im/asmjit/asmjit) if you need a quick help.
* Visit our [Gitter Chat](https://app.gitter.im/#/room/#asmjit:gitter.im) if you need a quick help
Project Organization
--------------------
* **`/`** - Project root.
* **src** - Source code.
* **asmjit** - Source code and headers (always point include path in here).
* **core** - Core API, backend independent except relocations.
* **arm** - ARM specific API, used only by ARM and AArch64 backends.
* **x86** - X86 specific API, used only by X86 and X64 backends.
* **test** - Unit and integration tests (don't embed in your project).
* **tools** - Tools used for configuring, documenting, and generating files.
* **`/`** - Project root
* **src** - Source code
* **asmjit** - Source code and headers (always point include path in here)
* **core** - Core API, backend independent except relocations
* **arm** - ARM specific API, used only by ARM and AArch64 backends
* **x86** - X86 specific API, used only by X86 and X64 backends
* **test** - Unit and integration tests (don't embed in your project)
* **tools** - Tools used for configuring, documenting, and generating files
TODO
----
Ports
-----
* [ ] Ports:
* [ ] 32-bit ARM/Thumb port.
* [ ] 64-bit ARM (AArch64) port.
* [ ] RISC-V port.
* [ ] 32-bit ARM/Thumb port (work in progress)
* [ ] RISC-V port (not in progress, help welcome)
Support
-------

26
deps/asmjit/db/LICENSE.md vendored Normal file
View File

@ -0,0 +1,26 @@
AsmJit database is dual licensed under Zlib and Unlicense (public domain)
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to <http://unlicense.org>

View File

@ -1,15 +1,21 @@
AsmJit Instruction Database
---------------------------
This is a database of instructions that is used by AsmJit to generate its internal database and also assembler implementations. This project started initially as AsmDB, but was merged to AsmJit later to make the maintenance easier. The database was created in a way so that each instruction definition would only need a single line in JSON data. The data is then processed by architecture specific data readers that make the data canonical and ready for processing.
This is a database of instructions that is used by AsmJit to generate its internal database and also assembler implementations. This project started initially as AsmDB, but was merged to AsmJit later to make the maintenance easier. The database was created in a way so that each instruction definition would only need a single line in JSON data file. The data is then processed by architecture specific data readers that make the data canonical and ready for processing.
AsmJit database provides the following ISAs:
* `isa_x86.json` - provides X86 instruction data (both 32-bit and 64-bit)
* `isa_arm.json` - provides AArch32 instruction data (both ARM32 and THUMB)
* `isa_a64.json` - provides AArch64 instruction data
* `isa_aarch32.json` - provides AArch32 instruction data (A32/T16/T32 encoding)
* `isa_aarch64.json` - provides AArch64 instruction data (A64 encoding)
* `isa_aarch64_sme.json` - provides AArch64 SME instruction data (work-in-progress)
To Be Documented
----------------
This project will be refactored and documented in the future.
License
-------
AsmJit database is dual licensed under Zlib (AsmJit license) or public domain. The database can be used for any purpose, not just by AsmJit.

View File

@ -1,8 +1,7 @@
// This file is part of AsmJit project <https://asmjit.com>
//
// See asmjit.h or LICENSE.md for license and copyright information
// SPDX-License-Identifier: Zlib
// SPDX-License-Identifier: (Zlib or Unlicense)
(function($scope, $as) {
"use strict";
@ -29,14 +28,14 @@ const arm = $scope[$as] = dict();
// Database
// ========
arm.dbName = "isa_arm.json";
arm.dbName = "isa_aarch32.json";
// asmdb.arm.Utils
// ===============
// asmdb.aarch32.Utils
// ===================
// Can be used to assign the number of bits each part of the opcode occupies.
// NOTE: THUMB instructions that use halfword must always specify the width
// of all registers as many instructictions accept only LO (r0..r7) registers.
// of all registers as many instructions accept only LO (r0..r7) registers.
const FieldInfo = {
"P" : { "bits": 1 },
"U" : { "bits": 1 },
@ -56,6 +55,7 @@ const FieldInfo = {
"cmode" : { "bits": 4 },
"Cn" : { "bits": 4 },
"Cm" : { "bits": 4 },
"Rd" : { "bits": 4, "read": false, "write": true },
"Rd2" : { "bits": 4, "read": false, "write": true },
"RdLo" : { "bits": 4, "read": false, "write": true },
@ -70,17 +70,22 @@ const FieldInfo = {
"Rs" : { "bits": 4, "read": true , "write": false },
"Rs2" : { "bits": 4, "read": true , "write": false },
"RsList": { "bits": 4, "read": true , "write": false , "list": true },
"Sd" : { "bits": 4, "read": false, "write": true },
"Sd2" : { "bits": 4, "read": false, "write": true },
"SdList": { "bits": 4, "read": false, "write": true , "list": true },
"Sx" : { "bits": 4, "read": true , "write": true },
"Sn" : { "bits": 4, "read": true , "write": false },
"Sm" : { "bits": 4, "read": true , "write": false },
"Ss" : { "bits": 4, "read": true , "write": false },
"Ss2" : { "bits": 4, "read": true , "write": false },
"SsList": { "bits": 4, "read": true , "write": false , "list": true },
"Dd" : { "bits": 4, "read": false, "write": true },
"Dd2" : { "bits": 4, "read": false, "write": true },
"Dd3" : { "bits": 4, "read": false, "write": true },
"Dd4" : { "bits": 4, "read": false, "write": true },
"DdList": { "bits": 4, "read": false, "write": true , "list": true },
"Dx" : { "bits": 4, "read": true , "write": true },
"Dx2" : { "bits": 4, "read": true , "write": true },
"Dn" : { "bits": 4, "read": true , "write": false },
@ -92,18 +97,18 @@ const FieldInfo = {
"Ds2" : { "bits": 4, "read": true , "write": false },
"Ds3" : { "bits": 4, "read": true , "write": false },
"Ds4" : { "bits": 4, "read": true , "write": false },
"DsList": { "bits": 4, "read": true , "write": false , "list": true },
"Vd" : { "bits": 4, "read": false, "write": true },
"Vd2" : { "bits": 4, "read": false, "write": true },
"Vd3" : { "bits": 4, "read": false, "write": true },
"Vd4" : { "bits": 4, "read": false, "write": true },
"VdList": { "bits": 4, "read": false, "write": true , "list": true },
"Vx" : { "bits": 4, "read": true , "write": true },
"Vx2" : { "bits": 4, "read": true , "write": true },
"Vn" : { "bits": 4, "read": true , "write": false },
"Vm" : { "bits": 4, "read": true , "write": false },
"Vs" : { "bits": 4, "read": true , "write": false },
"Vs2" : { "bits": 4, "read": true , "write": false },
"VsList": { "bits": 4, "read": true , "write": false , "list": true }
};
arm.FieldInfo = FieldInfo;
@ -192,10 +197,16 @@ function decomposeOperand(s) {
const elementSuffix = "[#i]";
let element = null;
let consecutive = 0;
let userRegList = false;
if (s.endsWith("^")) {
userRegList = true;
s = s.substring(0, s.length - 1);
}
if (s.endsWith(elementSuffix)) {
element = "#i";
s = s.substr(0, s.length - elementSuffix.length);
s = s.substring(0, s.length - elementSuffix.length);
}
if (s.endsWith("++")) {
@ -219,7 +230,8 @@ function decomposeOperand(s) {
data : s,
element : element,
restrict: restrict,
consecutive: consecutive
consecutive: consecutive,
userRegList: true
};
}
@ -238,8 +250,8 @@ function splitOpcodeFields(s) {
return out.map((field) => { return field.trim(); });
}
// asmdb.arm.Operand
// =================
// asmdb.aarch32.Operand
// =====================
// ARM operand.
class Operand extends base.Operand {
@ -267,17 +279,44 @@ class Operand extends base.Operand {
else
return 0;
}
isRelative() {
if (this.type === "imm")
return this.name === "relA" || this.name === "relS" || this.name === "relZ";
else
return false;
}
}
arm.Operand = Operand;
// asmdb.arm.Instruction
// =====================
// asmdb.aarch32.Instruction
// =========================
function patternFromOperand(key) {
return key;
// return key.replace(/\b(?:[RVDS](?:d|s|n|m|x|x2))\b/, "R");
}
// Rewrite a memory operand expression (either base or index) to a simplified one, which is okay
// to be generated as C++ expression. In general, we want to simplify != to a more favorable code.
function simplifyMemoryExpression(e) {
if (e.type === "binary" && e.op === "!=" && e.right.type === "var") {
// Rewrite A != PC to A < PC
if (e.right.name === "PC") { e.op = "<"; }
// Rewrite A != HI to A < 8
if (e.right.name === "HI") { e.op = "<"; e.right = exp.Imm(8); }
// Rewrite A != XX to A < SP || A == LR
if (e.right.name === "XX") {
return exp.Or(exp.Lt(e.left, exp.Var("SP")),
exp.Eq(e.left.clone(), exp.Var("LR")));
}
}
return e;
}
// ARM instruction.
class Instruction extends base.Instruction {
constructor(db, data) {
@ -482,13 +521,14 @@ class Instruction extends base.Instruction {
const m = part.match(/^([A-Za-z]\w*)/);
if (m.length < part.length) {
op.base.exp = exp.parse(part);
op.base.exp = simplifyMemoryExpression(exp.parse(part));
op.base.field = m[1];
}
}
else if (part.startsWith("#")) {
let p = part.substring(1);
let u = "1";
let alwaysNegative = false;
let offExp = null;
let offMul = 1;
@ -498,6 +538,11 @@ class Instruction extends base.Instruction {
p = p.substring(3);
}
if (p.startsWith("-")) {
alwaysNegative = false;
p = p.substring(1);
}
const expMatch = p.match(/^([A-Za-z]\w*)==/);
if (expMatch) {
offExp = exp.parse(p);
@ -515,6 +560,7 @@ class Instruction extends base.Instruction {
op.offset.u = u;
op.offset.exp = offExp;
op.offset.mul = offMul;
op.offset.negative = alwaysNegative;
}
else {
let p = part;
@ -531,7 +577,7 @@ class Instruction extends base.Instruction {
const m = p.match(/^([A-Za-z]\w*)/);
if (m.length < p.length) {
op.index.exp = exp.parse(p);
op.index.exp = simplifyMemoryExpression(exp.parse(p));
op.index.field = m[1];
}
}
@ -858,8 +904,8 @@ class Instruction extends base.Instruction {
}
arm.Instruction = Instruction;
// asmdb.arm.ISA
// =============
// asmdb.aarch32.ISA
// =================
function mergeGroupData(data, group) {
for (let k in group) {
@ -921,7 +967,7 @@ class ISA extends base.ISA {
hasOwn.call(obj, "t16") ? "t16" : "";
if (!encoding)
FAIL(`Instrution ${names.join("/")} doesn't encoding, it must provide either a32, t32, or t16 field`);
FAIL(`Instruction ${names.join("/")} doesn't encoding, it must provide either a32, t32, or t16 field`);
for (let j = 0; j < names.length; j++) {
const inst = new Instruction(this, names[j], operands, encoding.toUpperCase(), obj[encoding], obj);
@ -938,4 +984,4 @@ class ISA extends base.ISA {
arm.ISA = ISA;
}).apply(this, typeof module === "object" && module && module.exports
? [module, "exports"] : [this.asmdb || (this.asmdb = {}), "arm"]);
? [module, "exports"] : [this.asmdb || (this.asmdb = {}), "aarch32"]);

View File

@ -1,8 +1,7 @@
// This file is part of AsmJit project <https://asmjit.com>
//
// See asmjit.h or LICENSE.md for license and copyright information
// SPDX-License-Identifier: Zlib
// SPDX-License-Identifier: (Zlib or Unlicense)
(function($scope, $as) {
"use strict";
@ -36,7 +35,7 @@ arm.dbName = "isa_aarch64.json";
// Can be used to assign the number of bits each part of the opcode occupies.
// NOTE: THUMB instructions that use halfword must always specify the width
// of all registers as many instructictions accept only LO (r0..r7) registers.
// of all registers as many instructions accept only LO (r0..r7) registers.
const FieldInfo = {
"P" : { "bits": 1 },
"U" : { "bits": 1 },

View File

@ -1,7 +1,7 @@
// This file is part of AsmJit project <https://asmjit.com>
//
// See asmjit.h or LICENSE.md for license and copyright information
// SPDX-License-Identifier: Zlib
// SPDX-License-Identifier: (Zlib or Unlicense)
(function($scope, $as) {
"use strict";
@ -205,7 +205,7 @@ class Operand {
toString() { return this.data; }
isReg() { return !!this.reg; }
isReg() { return !!this.reg && this.type !== "reg-list"; }
isMem() { return !!this.mem; }
isImm() { return !!this.imm; }
isRel() { return !!this.rel; }
@ -259,6 +259,20 @@ class Instruction {
return out;
}
get operandCount() {
return this.operands.length;
}
get minimumOperandCount() {
const count = this.operands.length;
for (let i = 0; i < count; i++) {
if (this.operands[i].optional) {
return i;
}
}
return count
}
_assignAttribute(key, value) {
switch (key) {
case "ext":

155
deps/asmjit/db/exp.js vendored
View File

@ -1,7 +1,7 @@
// This file is part of AsmJit project <https://asmjit.com>
//
// See asmjit.h or LICENSE.md for license and copyright information
// SPDX-License-Identifier: Zlib
// SPDX-License-Identifier: (Zlib or Unlicense)
(function($scope, $as) {
"use strict";
@ -87,6 +87,7 @@ class ExpNode {
info() { return null; }
clone() { throw new Error("ExpNode.clone() must be overridden"); }
evaluate(ctx) { throw new Error("ExpNode.evaluate() must be overridden"); }
toString(ctx) { throw new Error("ExpNode.toString() must be overridden"); }
}
@ -97,6 +98,7 @@ class ImmNode extends ExpNode {
}
clone() { return new ImmNode(this.imm); }
evaluate(ctx) { return this.imm; }
toString(ctx) { return ctx ? ctx.stringifyImmediate(this.imm) : String(this.imm); }
}
@ -106,7 +108,8 @@ class VarNode extends ExpNode {
this.name = name || "";
}
clone() { return new VarNode(this.var); }
clone() { return new VarNode(this.name); }
evaluate(ctx) { return ctx.variable(this.name); }
toString(ctx) { return ctx ? ctx.stringifyVariable(this.name) : String(this.name); }
}
@ -117,7 +120,14 @@ class CallNode extends ExpNode {
this.args = args || [];
}
clone() { return new CallNode(this.name, this.args.map(function(arg) { return arg.clone(); })); }
clone() {
return new CallNode(this.name, this.args.map(function(arg) { return arg.clone(); }));
}
evaluate(ctx) {
const evaluatedArgs = this.args.map(function(arg) { return arg.evaluate(ctx); });
return ctx.function(this.name, evaluatedArgs);
}
toString(ctx) {
if (this.name === "$bit") {
@ -143,8 +153,23 @@ class UnaryNode extends ExpNode {
this.child = child || null;
}
info() { return kUnaryOperators[this.op]; }
clone() { return new UnaryNode(this.op, this.left ? this.left.clone() : null); }
info() {
return kUnaryOperators[this.op];
}
clone() {
return new UnaryNode(this.op, this.left ? this.left.clone() : null);
}
evaluate(ctx) {
const val = this.child.evaluate(ctx);
switch (this.op) {
case "-": return (-val);
case "~": return (~val);
case "!": return (val ? 0 : 1);
default : return ctx.unary(this.op, val);
}
}
toString(ctx) {
return this.info().emit.replace(/@1/g, () => {
@ -166,8 +191,40 @@ class BinaryNode extends ExpNode {
this.right = right || null;
}
info() { return kBinaryOperators[this.op]; }
clone() { return new BinaryNode(this.op, this.left ? this.left.clone() : null, this.right ? this.right.clone() : null); }
info() {
return kBinaryOperators[this.op];
}
clone() {
return new BinaryNode(this.op, this.left ? this.left.clone() : null, this.right ? this.right.clone() : null);
}
evaluate(ctx) {
const left = this.left.evaluate(ctx);
const right = this.right.evaluate(ctx);
switch (this.op) {
case "-" : return left - right;
case "+" : return left + right;
case "*" : return left * right;
case "/" : return (left / right)|0;
case "%" : return (left % right)|0;
case "&" : return left & right;
case "|" : return left | right;
case "^" : return left ^ right;
case "<<": return left << right;
case ">>": return left >> right;
case "==": return left == right ? 1 : 0;
case "!=": return left != right ? 1 : 0;
case "<" : return left < right ? 1 : 0;
case "<=": return left <= right ? 1 : 0;
case ">" : return left > right ? 1 : 0;
case ">=": return left >= right ? 1 : 0;
case "&&": return left && right ? 1 : 0;
case "||": return left || right ? 1 : 0;
default : return ctx.binary(this.op, left, right);
}
}
toString(ctx) {
return this.info().emit.replace(/@[1-2]/g, (p) => {
@ -184,8 +241,6 @@ function Call(name, args) { return new CallNode(name, args); }
function Unary(op, child) { return new UnaryNode(op, child); }
function Binary(op, left, right) { return new BinaryNode(op, left, right); }
/*
// TODO: Unused, remove?
function Negate(child) { return Unary("-", child); }
function BitNot(child) { return Unary("~", child); }
@ -207,7 +262,8 @@ function Gt(left, right) { return Binary(">", left, right); }
function Ge(left, right) { return Binary(">=", left, right); }
function And(left, right) { return Binary("&&", left, right); }
function Or(left, right) { return Binary("||", left, right); }
*/
// Expression Tokenizer
// --------------------
@ -256,7 +312,44 @@ function newToken(type, position, data, value) {
const NoToken = newToken(kTokenNone, -1, "<end>", null);
// Must be reset before it can be used, use `RegExp.lastIndex`.
const reValue = /(?:(?:\d*\.\d+|\d+)(?:[E|e][+|-]?\d+)?)/g;
const reNumValue = /(?:(?:\d*\.\d+|\d+)(?:[E|e][+|-]?\d+)?)/g;
function parseHex(source, from) {
let i = from;
let number = 0;
while (i < source.length) {
let c = source.charCodeAt(i);
let n = 0;
if (c >= '0'.charCodeAt(0) && c <= '9'.charCodeAt(0)) {
n = c - '0'.charCodeAt(0);
}
else if (c >= 'a'.charCodeAt(0) && c <= 'f'.charCodeAt(0)) {
n = c - 'a'.charCodeAt(0) + 10;
}
else if (c >= 'A'.charCodeAt(0) && c <= 'F'.charCodeAt(0)) {
n = c - 'A'.charCodeAt(0) + 10;
}
else if (c >= 'g'.charCodeAt(0) && c <= 'z'.charCodeAt(0) || c >= 'g'.charCodeAt(0) && c <= 'Z'.charCodeAt(0)) {
throwExpressionError(`Invalid hex number 0x${source.substring(from, i + 1)}`);
}
else {
break;
}
number = (number << 4) | n;
i++;
}
if (i === from)
throwExpressionError(`Invalid number starting with 0x`);
return {
number: number,
end: i
};
}
function tokenize(source) {
const len = source.length;
@ -268,23 +361,34 @@ function tokenize(source) {
let c, cat; // Current character code and category.
while (i < len) {
cat = Category(c = source.charCodeAt(i));
c = source.charCodeAt(i);
cat = Category(c);
if (cat === kCharSpace) {
i++;
}
else if (cat === kCharDigit) {
const n = tokens.length - 1;
// Hex number.
if (c === '0'.charCodeAt(0) && i + 1 < len && source.charCodeAt(i + 1) === 'x'.charCodeAt(0)) {
const status = parseHex(source, i + 2);
tokens.push(newToken(kTokenValue, i, source.substring(i, status.end), status.number));
i = status.end;
}
else {
if (n >= 0 && tokens[n].data === "." && source[i - 1] === ".") {
tokens.length = n;
i--;
}
reValue.lastIndex = i;
data = reValue.exec(source)[0];
reNumValue.lastIndex = i;
data = reNumValue.exec(source)[0];
tokens.push(newToken(kTokenValue, i, data, parseFloat(data)));
i += data.length;
}
}
else if (cat === kCharAlpha) {
start = i;
while (++i < len && ((cat = Category(source.charCodeAt(i))) === kCharAlpha || cat === kCharDigit))
@ -623,6 +727,29 @@ $scope[$as] = {
Call: Call,
Unary: Unary,
Binary: Binary,
Negate: Negate,
BitNot: BitNot,
Add: Add,
Sub: Sub,
Mul: Mul,
Div: Div,
Mod: Mod,
Shl: Shl,
Shr: Shr,
BitAnd: BitAnd,
BitOr: BitOr,
BitXor: BitXor,
Eq: Eq,
Ne: Ne,
Lt: Lt,
Le: Le,
Gt: Gt,
Ge: Ge,
And: And,
Or: Or,
Visitor: Visitor,
ExpressionError: ExpressionError,

View File

@ -1,11 +1,11 @@
// This file is part of AsmJit project <https://asmjit.com>
//
// See asmjit.h or LICENSE.md for license and copyright information
// SPDX-License-Identifier: Zlib
// SPDX-License-Identifier: (Zlib or Unlicense)
"use strict";
exports.base = require("./base.js");
exports.arm = require("./arm.js");
exports.aarch32 = require("./aarch32.js");
exports.aarch64 = require("./aarch64.js");
exports.x86 = require("./x86.js");

File diff suppressed because it is too large Load Diff

View File

@ -139,70 +139,70 @@
{"inst": "ldaxr Xd, [Xn|SP]" , "op": "11001000|010|11111|1|11111|Rn|Rd"},
{"inst": "ldaxrb Wd, [Xn|SP]" , "op": "00001000|010|11111|1|11111|Rn|Rd"},
{"inst": "ldaxrh Xd, [Xn|SP]" , "op": "01001000|010|11111|1|11111|Rn|Rd"},
{"inst": "ldnp Wd, Wd2, [Xn|SP, #soff*4]" , "op": "00101000|01|soff:7|Rd2|Rn|Rd"},
{"inst": "ldnp Xd, Xd2, [Xn|SP, #soff*8]" , "op": "10101000|01|soff:7|Rd2|Rn|Rd"},
{"inst": "ldp Wd, Wd2, [Xn|SP, #soff*4]{@}{!}" , "op": "0010100|!post|W|1|soff:7|Rd2|Rn|Rd"},
{"inst": "ldp Xd, Xd2, [Xn|SP, #soff*8]{@}{!}" , "op": "1010100|!post|W|1|soff:7|Rd2|Rn|Rd"},
{"inst": "ldpsw Xd, Xd2, [Xn|SP, #soff*4]{@}{!}" , "op": "0110100|!post|W|1|soff:7|Rd2|Rn|Rd"},
{"inst": "ldr Wd, [Xn|SP, #zoff*4]" , "op": "10111001|01|zoff:12|Rn|Rd"},
{"inst": "ldr Xd, [Xn|SP, #zoff*8]" , "op": "11111001|01|zoff:12|Rn|Rd"},
{"inst": "ldr Wd, [Xn|SP, #soff*4]@" , "op": "10111000|010|soff:9|01|Rn|Rd"},
{"inst": "ldr Xd, [Xn|SP, #soff*8]@" , "op": "11111000|010|soff:9|01|Rn|Rd"},
{"inst": "ldr Wd, [Xn|SP, #soff*4]!" , "op": "10111000|010|soff:9|11|Rn|Rd"},
{"inst": "ldr Xd, [Xn|SP, #soff*8]!" , "op": "11111000|010|soff:9|11|Rn|Rd"},
{"inst": "ldnp Wd, Wd2, [Xn|SP, #offS*4]" , "op": "00101000|01|offS:7|Rd2|Rn|Rd"},
{"inst": "ldnp Xd, Xd2, [Xn|SP, #offS*8]" , "op": "10101000|01|offS:7|Rd2|Rn|Rd"},
{"inst": "ldp Wd, Wd2, [Xn|SP, #offS*4]{@}{!}" , "op": "0010100|!post|W|1|offS:7|Rd2|Rn|Rd"},
{"inst": "ldp Xd, Xd2, [Xn|SP, #offS*8]{@}{!}" , "op": "1010100|!post|W|1|offS:7|Rd2|Rn|Rd"},
{"inst": "ldpsw Xd, Xd2, [Xn|SP, #offS*4]{@}{!}" , "op": "0110100|!post|W|1|offS:7|Rd2|Rn|Rd"},
{"inst": "ldr Wd, [Xn|SP, #offZ*4]" , "op": "10111001|01|offZ:12|Rn|Rd"},
{"inst": "ldr Xd, [Xn|SP, #offZ*8]" , "op": "11111001|01|offZ:12|Rn|Rd"},
{"inst": "ldr Wd, [Xn|SP, #offS*4]@" , "op": "10111000|010|offS:9|01|Rn|Rd"},
{"inst": "ldr Xd, [Xn|SP, #offS*8]@" , "op": "11111000|010|offS:9|01|Rn|Rd"},
{"inst": "ldr Wd, [Xn|SP, #offS*4]!" , "op": "10111000|010|offS:9|11|Rn|Rd"},
{"inst": "ldr Xd, [Xn|SP, #offS*8]!" , "op": "11111000|010|offS:9|11|Rn|Rd"},
{"inst": "ldr Wd, [Xn|SP, Rm, {uxtw|lsl|sxtw|sxtx #n}]" , "op": "10111000|011|Rm|option:3|s:1|10|Rn|Rd" , "imm": "ImmLDR(iop, n)"},
{"inst": "ldr Xd, [Xn|SP, Rm, {uxtw|lsl|sxtw|sxtx #n}]" , "op": "11111000|011|Rm|option:3|s:1|10|Rn|Rd" , "imm": "ImmLDR(iop, n)"},
{"inst": "ldr Wd, [PC, #soff*4]" , "op": "00011000|soff:19|Rd"},
{"inst": "ldr Xd, [PC, #soff*4]" , "op": "01011000|soff:19|Rd"},
{"inst": "ldrb Wd, [Xn|SP, #zoff]" , "op": "00111001|01|zoff:12|Rn|Rd"},
{"inst": "ldrb Wd, [Xn|SP, #soff]@" , "op": "00111000|010|soff:9|01|Rn|Rd"},
{"inst": "ldrb Wd, [Xn|SP, #soff]!" , "op": "00111000|010|soff:9|11|Rn|Rd"},
{"inst": "ldr Wd, [PC, #offS*4]" , "op": "00011000|offS:19|Rd"},
{"inst": "ldr Xd, [PC, #offS*4]" , "op": "01011000|offS:19|Rd"},
{"inst": "ldrb Wd, [Xn|SP, #offZ]" , "op": "00111001|01|offZ:12|Rn|Rd"},
{"inst": "ldrb Wd, [Xn|SP, #offS]@" , "op": "00111000|010|offS:9|01|Rn|Rd"},
{"inst": "ldrb Wd, [Xn|SP, #offS]!" , "op": "00111000|010|offS:9|11|Rn|Rd"},
{"inst": "ldrb Wd, [Xn|SP, Rm, {uxtw|lsl|sxtw|sxtx #n}]" , "op": "00111000|011|Rm|option:3|s:1|10|Rn|Rd" , "imm": "ImmLDRB(iop, n)"},
{"inst": "ldrh Wd, [Xn|SP, #zoff*2]" , "op": "01111001|01|zoff:12|Rn|Rd"},
{"inst": "ldrh Wd, [Xn|SP, #soff*2]@" , "op": "01111000|010|soff:9|01|Rn|Rd"},
{"inst": "ldrh Wd, [Xn|SP, #soff*2]!" , "op": "01111000|010|soff:9|11|Rn|Rd"},
{"inst": "ldrh Wd, [Xn|SP, #offZ*2]" , "op": "01111001|01|offZ:12|Rn|Rd"},
{"inst": "ldrh Wd, [Xn|SP, #offS*2]@" , "op": "01111000|010|offS:9|01|Rn|Rd"},
{"inst": "ldrh Wd, [Xn|SP, #offS*2]!" , "op": "01111000|010|offS:9|11|Rn|Rd"},
{"inst": "ldrh Wd, [Xn|SP, Rm, {uxtw|lsl|sxtw|sxtx #n}]" , "op": "01111000|011|Rm|option:3|s:1|10|Rn|Rd" , "imm": "ImmLDRH(iop, n)"},
{"inst": "ldrsb Wd, [Xn|SP, #zoff]" , "op": "00111001|11|zoff:12|Rn|Rd"},
{"inst": "ldrsb Xd, [Xn|SP, #zoff]" , "op": "00111001|10|zoff:12|Rn|Rd"},
{"inst": "ldrsb Wd, [Xn|SP, #soff]@" , "op": "00111000|110|soff:9|01|Rn|Rd"},
{"inst": "ldrsb Xd, [Xn|SP, #soff]@" , "op": "00111000|100|soff:9|01|Rn|Rd"},
{"inst": "ldrsb Wd, [Xn|SP, #soff]!" , "op": "00111000|110|soff:9|11|Rn|Rd"},
{"inst": "ldrsb Xd, [Xn|SP, #soff]!" , "op": "00111000|100|soff:9|11|Rn|Rd"},
{"inst": "ldrsb Wd, [Xn|SP, #offZ]" , "op": "00111001|11|offZ:12|Rn|Rd"},
{"inst": "ldrsb Xd, [Xn|SP, #offZ]" , "op": "00111001|10|offZ:12|Rn|Rd"},
{"inst": "ldrsb Wd, [Xn|SP, #offS]@" , "op": "00111000|110|offS:9|01|Rn|Rd"},
{"inst": "ldrsb Xd, [Xn|SP, #offS]@" , "op": "00111000|100|offS:9|01|Rn|Rd"},
{"inst": "ldrsb Wd, [Xn|SP, #offS]!" , "op": "00111000|110|offS:9|11|Rn|Rd"},
{"inst": "ldrsb Xd, [Xn|SP, #offS]!" , "op": "00111000|100|offS:9|11|Rn|Rd"},
{"inst": "ldrsb Wd, [Xn|SP, Rm, {uxtw|lsl|sxtw|sxtx #n}]" , "op": "00111000|111|Rm|option:3|s:1|10|Rn|Rd" , "imm": "ImmLDRB(iop, n)"},
{"inst": "ldrsb Xd, [Xn|SP, Rm, {uxtw|lsl|sxtw|sxtx #n}]" , "op": "00111000|101|Rm|option:3|s:1|10|Rn|Rd" , "imm": "ImmLDRB(iop, n)"},
{"inst": "ldrsh Wd, [Xn|SP, #zoff*2]" , "op": "01111001|11|zoff:12|Rn|Rd"},
{"inst": "ldrsh Xd, [Xn|SP, #zoff*2]" , "op": "01111001|10|zoff:12|Rn|Rd"},
{"inst": "ldrsh Wd, [Xn|SP, #soff*2]@" , "op": "01111000|110|soff:9|01|Rn|Rd"},
{"inst": "ldrsh Xd, [Xn|SP, #soff*2]@" , "op": "01111000|100|soff:9|01|Rn|Rd"},
{"inst": "ldrsh Wd, [Xn|SP, #soff*2]!" , "op": "01111000|110|soff:9|11|Rn|Rd"},
{"inst": "ldrsh Xd, [Xn|SP, #soff*2]!" , "op": "01111000|100|soff:9|11|Rn|Rd"},
{"inst": "ldrsh Wd, [Xn|SP, #offZ*2]" , "op": "01111001|11|offZ:12|Rn|Rd"},
{"inst": "ldrsh Xd, [Xn|SP, #offZ*2]" , "op": "01111001|10|offZ:12|Rn|Rd"},
{"inst": "ldrsh Wd, [Xn|SP, #offS*2]@" , "op": "01111000|110|offS:9|01|Rn|Rd"},
{"inst": "ldrsh Xd, [Xn|SP, #offS*2]@" , "op": "01111000|100|offS:9|01|Rn|Rd"},
{"inst": "ldrsh Wd, [Xn|SP, #offS*2]!" , "op": "01111000|110|offS:9|11|Rn|Rd"},
{"inst": "ldrsh Xd, [Xn|SP, #offS*2]!" , "op": "01111000|100|offS:9|11|Rn|Rd"},
{"inst": "ldrsh Wd, [Xn|SP, Rm, {uxtw|lsl|sxtw|sxtx #n}]" , "op": "01111000|111|Rm|option:3|s:1|10|Rn|Rd" , "imm": "ImmLDRH(iop, n)"},
{"inst": "ldrsh Xd, [Xn|SP, Rm, {uxtw|lsl|sxtw|sxtx #n}]" , "op": "01111000|101|Rm|option:3|s:1|10|Rn|Rd" , "imm": "ImmLDRH(iop, n)"},
{"inst": "ldrsw Xd, [Xn|SP, #zoff*4]" , "op": "10111001|10|zoff:12|Rn|Rd"},
{"inst": "ldrsw Xd, [Xn|SP, #soff*4]@" , "op": "10111000|100|soff:9|01|Rn|Rd"},
{"inst": "ldrsw Xd, [Xn|SP, #soff*4]!" , "op": "10111000|100|soff:9|11|Rn|Rd"},
{"inst": "ldrsw Xd, [PC, #soff*4]" , "op": "10011000|soff:19|Rd"},
{"inst": "ldrsw Xd, [Xn|SP, #offZ*4]" , "op": "10111001|10|offZ:12|Rn|Rd"},
{"inst": "ldrsw Xd, [Xn|SP, #offS*4]@" , "op": "10111000|100|offS:9|01|Rn|Rd"},
{"inst": "ldrsw Xd, [Xn|SP, #offS*4]!" , "op": "10111000|100|offS:9|11|Rn|Rd"},
{"inst": "ldrsw Xd, [PC, #offS*4]" , "op": "10011000|offS:19|Rd"},
{"inst": "ldrsw Xd, [Xn|SP, Rm, {uxtw|lsl|sxtw|sxtx #n}]" , "op": "10111000|101|Rm|option:3|s:1|10|Rn|Rd" , "imm": "ImmLDRW(iop, n)"},
{"inst": "ldtr Wd, [Xn|SP, #soff]" , "op": "10111000|010|soff:9|10|Rn|Rd"},
{"inst": "ldtr Xd, [Xn|SP, #soff]" , "op": "11111000|010|soff:9|10|Rn|Rd"},
{"inst": "ldtrb Wd, [Xn|SP, #soff]" , "op": "00111000|010|soff:9|10|Rn|Rd"},
{"inst": "ldtrh Wd, [Xn|SP, #soff]" , "op": "01111000|010|soff:9|10|Rn|Rd"},
{"inst": "ldtrsb Wd, [Xn|SP, #soff]" , "op": "00111000|100|soff:9|10|Rn|Rd"},
{"inst": "ldtrsb Xd, [Xn|SP, #soff]" , "op": "00111000|110|soff:9|10|Rn|Rd"},
{"inst": "ldtrsh Wd, [Xn|SP, #soff]" , "op": "01111000|100|soff:9|10|Rn|Rd"},
{"inst": "ldtrsh Xd, [Xn|SP, #soff]" , "op": "01111000|110|soff:9|10|Rn|Rd"},
{"inst": "ldtrsw Xd, [Xn|SP, #soff]" , "op": "10111000|100|soff:9|10|Rn|Rd"},
{"inst": "ldur Wd, [Xn|SP, #soff]" , "op": "10111000|010|soff:9|00|Rn|Rd"},
{"inst": "ldur Xd, [Xn|SP, #soff]" , "op": "11111000|010|soff:9|00|Rn|Rd"},
{"inst": "ldurb Wd, [Xn|SP, #soff]" , "op": "00111000|010|soff:9|00|Rn|Rd"},
{"inst": "ldurh Wd, [Xn|SP, #soff]" , "op": "01111000|010|soff:9|00|Rn|Rd"},
{"inst": "ldursb Wd, [Xn|SP, #soff]" , "op": "00111000|100|soff:9|00|Rn|Rd"},
{"inst": "ldursb Xd, [Xn|SP, #soff]" , "op": "00111000|110|soff:9|00|Rn|Rd"},
{"inst": "ldursh Wd, [Xn|SP, #soff]" , "op": "01111000|100|soff:9|00|Rn|Rd"},
{"inst": "ldursh Xd, [Xn|SP, #soff]" , "op": "01111000|110|soff:9|00|Rn|Rd"},
{"inst": "ldursw Xd, [Xn|SP, #soff]" , "op": "10111000|100|soff:9|00|Rn|Rd"},
{"inst": "ldxp Wd, Wd2, [Xn|SP, #soff*4]" , "op": "10001000|011|11111|0|Rd2|Rn|Rd"},
{"inst": "ldxp Xd, Xd2, [Xn|SP, #soff*8]" , "op": "11001000|011|11111|0|Rd2|Rn|Rd"},
{"inst": "ldtr Wd, [Xn|SP, #offS]" , "op": "10111000|010|offS:9|10|Rn|Rd"},
{"inst": "ldtr Xd, [Xn|SP, #offS]" , "op": "11111000|010|offS:9|10|Rn|Rd"},
{"inst": "ldtrb Wd, [Xn|SP, #offS]" , "op": "00111000|010|offS:9|10|Rn|Rd"},
{"inst": "ldtrh Wd, [Xn|SP, #offS]" , "op": "01111000|010|offS:9|10|Rn|Rd"},
{"inst": "ldtrsb Wd, [Xn|SP, #offS]" , "op": "00111000|100|offS:9|10|Rn|Rd"},
{"inst": "ldtrsb Xd, [Xn|SP, #offS]" , "op": "00111000|110|offS:9|10|Rn|Rd"},
{"inst": "ldtrsh Wd, [Xn|SP, #offS]" , "op": "01111000|100|offS:9|10|Rn|Rd"},
{"inst": "ldtrsh Xd, [Xn|SP, #offS]" , "op": "01111000|110|offS:9|10|Rn|Rd"},
{"inst": "ldtrsw Xd, [Xn|SP, #offS]" , "op": "10111000|100|offS:9|10|Rn|Rd"},
{"inst": "ldur Wd, [Xn|SP, #offS]" , "op": "10111000|010|offS:9|00|Rn|Rd"},
{"inst": "ldur Xd, [Xn|SP, #offS]" , "op": "11111000|010|offS:9|00|Rn|Rd"},
{"inst": "ldurb Wd, [Xn|SP, #offS]" , "op": "00111000|010|offS:9|00|Rn|Rd"},
{"inst": "ldurh Wd, [Xn|SP, #offS]" , "op": "01111000|010|offS:9|00|Rn|Rd"},
{"inst": "ldursb Wd, [Xn|SP, #offS]" , "op": "00111000|100|offS:9|00|Rn|Rd"},
{"inst": "ldursb Xd, [Xn|SP, #offS]" , "op": "00111000|110|offS:9|00|Rn|Rd"},
{"inst": "ldursh Wd, [Xn|SP, #offS]" , "op": "01111000|100|offS:9|00|Rn|Rd"},
{"inst": "ldursh Xd, [Xn|SP, #offS]" , "op": "01111000|110|offS:9|00|Rn|Rd"},
{"inst": "ldursw Xd, [Xn|SP, #offS]" , "op": "10111000|100|offS:9|00|Rn|Rd"},
{"inst": "ldxp Wd, Wd2, [Xn|SP, #offS*4]" , "op": "10001000|011|11111|0|Rd2|Rn|Rd"},
{"inst": "ldxp Xd, Xd2, [Xn|SP, #offS*8]" , "op": "11001000|011|11111|0|Rd2|Rn|Rd"},
{"inst": "ldxr Wd, [Xn|SP]" , "op": "10001000|010|11111|0|11111|Rn|Rd"},
{"inst": "ldxr Xd, [Xn|SP]" , "op": "11001000|010|11111|0|11111|Rn|Rd"},
{"inst": "ldxrb Wd, [Xn|SP]" , "op": "00001000|010|11111|0|11111|Rn|Rd"},
@ -260,9 +260,9 @@
{"inst": "orr Wd|WSP, Wn, #log_imm" , "op": "00110010|0|imm:13|Rn|Rd" , "imm": "ImmLogical(log_imm, 0)"},
{"inst": "orr Xd|SP, Xn, #log_imm" , "op": "10110010|0|imm:13|Rn|Rd" , "imm": "ImmLogical(log_imm, 1)"},
{"inst": "prfm #prf_op, [Xn|SP, Rm, {uxtw|lsl|sxtw|sxtx #n*8}]" , "op": "11111000|101|Rm|option:3|n:1|10|Rn|prf_op:5" , "imm": "ImmPRF(prf_op)"},
{"inst": "prfm #prf_op, [Xn|SP, #zoff]" , "op": "11111001|10|zoff:12|Rn|prf_op:5" , "imm": "ImmPRF(prf_op)"},
{"inst": "prfm #prf_op, [PC, #soff*4]" , "op": "11011000|soff:19|prf_op:5" , "imm": "ImmPRF(prf_op)"},
{"inst": "prfum #prf_op, [Xn|SP, #soff]" , "op": "11111000|100|soff:9|00|Rn|prf_op:5" , "imm": "ImmPRF(prf_op)"},
{"inst": "prfm #prf_op, [Xn|SP, #offZ]" , "op": "11111001|10|offZ:12|Rn|prf_op:5" , "imm": "ImmPRF(prf_op)"},
{"inst": "prfm #prf_op, [PC, #offS*4]" , "op": "11011000|offS:19|prf_op:5" , "imm": "ImmPRF(prf_op)"},
{"inst": "prfum #prf_op, [Xn|SP, #offS]" , "op": "11111000|100|offS:9|00|Rn|prf_op:5" , "imm": "ImmPRF(prf_op)"},
{"inst": "pssbb" , "op": "11010101|000|00011|0011|0100|100|11111"},
{"inst": "rbit Wd, Wn" , "op": "01011010|110|00000|0|00000|Rn|Rd"},
{"inst": "rbit Xd, Xn" , "op": "11011010|110|00000|0|00000|Rn|Rd"},
@ -291,7 +291,7 @@
{"inst": "sev" , "op": "11010101|000|00011|0010|0000|100|11111"},
{"inst": "sevl" , "op": "11010101|000|00011|0010|0000|101|11111"},
{"inst": "smaddl Xd, Wn, Wm, Xa" , "op": "10011011|001|Rm|0|Ra|Rn|Rd"},
{"inst": "smc #zimm" , "op": "11010100|000|zimm:16|00011"},
{"inst": "smc #immZ" , "op": "11010100|000|immZ:16|00011"},
{"inst": "smnegl Xd, Wn, Wm" , "op": "10011011|001|Rm|1|11111|Rn|Rd"},
{"inst": "smsubl Xd, Wn, Wm, Xa" , "op": "10011011|001|Rm|1|Ra|Rn|Rd"},
{"inst": "smulh Xd, Xn, Xm" , "op": "10011011|010|Rm|0|11111|Rn|Rd"},
@ -307,34 +307,34 @@
{"inst": "stlxr Wd, Xs, [Xn|SP]" , "op": "11001000|000|Rd|1|11111|Rn|Rs"},
{"inst": "stlxrb Wd, Ws, [Xn|SP]" , "op": "00001000|000|Rd|1|11111|Rn|Rs"},
{"inst": "stlxrh Wd, Xs, [Xn|SP]" , "op": "01001000|000|Rd|1|11111|Rn|Rs"},
{"inst": "stnp Ws, Ws2, [Xn|SP, #simm*4]" , "op": "00101000|00|simm:7|Rs2|Rn|Rs"},
{"inst": "stnp Xs, Xs2, [Xn|SP, #simm*8]" , "op": "10101000|00|simm:7|Rs2|Rn|Rs"},
{"inst": "stp Ws, Ws2, [Xn|SP, #simm*4]{@}{!}" , "op": "0010100|!post|W|0|simm:7|Rs2|Rn|Rs"},
{"inst": "stp Xs, Xs2, [Xn|SP, #simm*8]{@}{!}" , "op": "1010100|!post|W|0|simm:7|Rs2|Rn|Rs"},
{"inst": "str Ws, [Xn|SP, #zoff*4]" , "op": "10111001|00|zoff:12|Rn|Rs"},
{"inst": "str Xs, [Xn|SP, #zoff*8]" , "op": "11111001|00|zoff:12|Rn|Rs"},
{"inst": "str Ws, [Xn|SP, #soff*4]@" , "op": "10111000|000|soff:9|01|Rn|Rs"},
{"inst": "str Xs, [Xn|SP, #soff*8]@" , "op": "11111000|000|soff:9|01|Rn|Rs"},
{"inst": "str Ws, [Xn|SP, #soff*4]!" , "op": "10111000|000|soff:9|11|Rn|Rs"},
{"inst": "str Xs, [Xn|SP, #soff*8]!" , "op": "11111000|000|soff:9|11|Rn|Rs"},
{"inst": "stnp Ws, Ws2, [Xn|SP, #offS*4]" , "op": "00101000|00|offS:7|Rs2|Rn|Rs"},
{"inst": "stnp Xs, Xs2, [Xn|SP, #offS*8]" , "op": "10101000|00|offS:7|Rs2|Rn|Rs"},
{"inst": "stp Ws, Ws2, [Xn|SP, #offS*4]{@}{!}" , "op": "0010100|!post|W|0|offS:7|Rs2|Rn|Rs"},
{"inst": "stp Xs, Xs2, [Xn|SP, #offS*8]{@}{!}" , "op": "1010100|!post|W|0|offS:7|Rs2|Rn|Rs"},
{"inst": "str Ws, [Xn|SP, #offZ*4]" , "op": "10111001|00|offZ:12|Rn|Rs"},
{"inst": "str Xs, [Xn|SP, #offZ*8]" , "op": "11111001|00|offZ:12|Rn|Rs"},
{"inst": "str Ws, [Xn|SP, #offS*4]@" , "op": "10111000|000|offS:9|01|Rn|Rs"},
{"inst": "str Xs, [Xn|SP, #offS*8]@" , "op": "11111000|000|offS:9|01|Rn|Rs"},
{"inst": "str Ws, [Xn|SP, #offS*4]!" , "op": "10111000|000|offS:9|11|Rn|Rs"},
{"inst": "str Xs, [Xn|SP, #offS*8]!" , "op": "11111000|000|offS:9|11|Rn|Rs"},
{"inst": "str Ws, [Xn|SP, Rm, {uxtw|lsl|sxtw|sxtx #n}]" , "op": "10111000|001|Rm|option:3|s:1|10|Rn|Rs" , "imm": "ImmLDR_STR(iop, n)"},
{"inst": "str Xs, [Xn|SP, Rm, {uxtw|lsl|sxtw|sxtx #n}]" , "op": "11111000|001|Rm|option:3|s:1|10|Rn|Rs" , "imm": "ImmLDR_STR(iop, n)"},
{"inst": "strb Ws, [Xn|SP, #zoff]" , "op": "00111001|00|zoff:12|Rn|Rs"},
{"inst": "strb Ws, [Xn|SP, #soff]@" , "op": "00111000|000|soff:9|01|Rn|Rs"},
{"inst": "strb Ws, [Xn|SP, #soff]!" , "op": "00111000|000|soff:9|11|Rn|Rs"},
{"inst": "strb Ws, [Xn|SP, #offZ]" , "op": "00111001|00|offZ:12|Rn|Rs"},
{"inst": "strb Ws, [Xn|SP, #offS]@" , "op": "00111000|000|offS:9|01|Rn|Rs"},
{"inst": "strb Ws, [Xn|SP, #offS]!" , "op": "00111000|000|offS:9|11|Rn|Rs"},
{"inst": "strb Ws, [Xn|SP, Rm, {uxtw|lsl|sxtw|sxtx #n}]" , "op": "00111000|001|Rm|option:3|s:1|10|Rn|Rs" , "imm": "ImmLDRB_STRB(iop, n)"},
{"inst": "strh Ws, [Xn|SP, #zoff*2]" , "op": "01111001|00|zoff:12|Rn|Rs"},
{"inst": "strh Ws, [Xn|SP, #soff*2]@" , "op": "01111000|000|soff:9|01|Rn|Rs"},
{"inst": "strh Ws, [Xn|SP, #soff*2]!" , "op": "01111000|000|soff:9|11|Rn|Rs"},
{"inst": "strh Ws, [Xn|SP, #offZ*2]" , "op": "01111001|00|offZ:12|Rn|Rs"},
{"inst": "strh Ws, [Xn|SP, #offS*2]@" , "op": "01111000|000|offS:9|01|Rn|Rs"},
{"inst": "strh Ws, [Xn|SP, #offS*2]!" , "op": "01111000|000|offS:9|11|Rn|Rs"},
{"inst": "strh Ws, [Xn|SP, Rm, {uxtw|lsl|sxtw|sxtx #n}]" , "op": "01111000|001|Rm|option:3|s:1|10|Rn|Rs" , "imm": "ImmLDRH_STRH(iop, n)"},
{"inst": "sttr Ws, [Xn|SP, #soff]" , "op": "10111000|000|soff:9|10|Rn|Rs"},
{"inst": "sttr Xs, [Xn|SP, #soff]" , "op": "11111000|000|soff:9|10|Rn|Rs"},
{"inst": "sttrb Ws, [Xn|SP, #soff]" , "op": "00111000|000|soff:9|10|Rn|Rs"},
{"inst": "sttrh Ws, [Xn|SP, #soff]" , "op": "01111000|000|soff:9|10|Rn|Rs"},
{"inst": "stur Ws, [Xn|SP, #soff]" , "op": "10111000|000|soff:9|00|Rn|Rs"},
{"inst": "stur Xs, [Xn|SP, #soff]" , "op": "11111000|000|soff:9|00|Rn|Rs"},
{"inst": "sturb Ws, [Xn|SP, #soff]" , "op": "00111000|000|soff:9|00|Rn|Rs"},
{"inst": "sturh Ws, [Xn|SP, #soff]" , "op": "01111000|000|soff:9|00|Rn|Rs"},
{"inst": "sttr Ws, [Xn|SP, #offS]" , "op": "10111000|000|offS:9|10|Rn|Rs"},
{"inst": "sttr Xs, [Xn|SP, #offS]" , "op": "11111000|000|offS:9|10|Rn|Rs"},
{"inst": "sttrb Ws, [Xn|SP, #offS]" , "op": "00111000|000|offS:9|10|Rn|Rs"},
{"inst": "sttrh Ws, [Xn|SP, #offS]" , "op": "01111000|000|offS:9|10|Rn|Rs"},
{"inst": "stur Ws, [Xn|SP, #offS]" , "op": "10111000|000|offS:9|00|Rn|Rs"},
{"inst": "stur Xs, [Xn|SP, #offS]" , "op": "11111000|000|offS:9|00|Rn|Rs"},
{"inst": "sturb Ws, [Xn|SP, #offS]" , "op": "00111000|000|offS:9|00|Rn|Rs"},
{"inst": "sturh Ws, [Xn|SP, #offS]" , "op": "01111000|000|offS:9|00|Rn|Rs"},
{"inst": "stxp Wd, Ws, Ws2, [Xn|SP]" , "op": "10001000|001|Rd|0|Rs2|Rn|Rs"},
{"inst": "stxp Wd, Xs, Xs2, [Xn|SP]" , "op": "11001000|001|Rd|0|Rs2|Rn|Rs"},
{"inst": "stxr Wd, Ws, [Xn|SP]" , "op": "10001000|000|Rd|0|11111|Rn|Rs"},
@ -353,7 +353,7 @@
{"inst": "subs Xd, Xn|SP, Rm, {extend #n}" , "op": "11101011|00|1|Rm|option:3|n:3|Rn|Rd" , "io": "N=W Z=W C=W V=W"},
{"inst": "subs Wd|WSP, Wn|WSP, #immZ, {lsl #n=0|12}" , "op": "01110001|0|n:1|immZ:12|Rn|Rd" , "io": "N=W Z=W C=W V=W"},
{"inst": "subs Xd|SP, Xn|SP, #immZ, {lsl #n=0|12}" , "op": "11110001|0|n:1|immZ:12|Rn|Rd" , "io": "N=W Z=W C=W V=W"},
{"inst": "svc #zimm" , "op": "11010100|000|zimm:16|00001"},
{"inst": "svc #immZ" , "op": "11010100|000|immZ:16|00001"},
{"inst": "sxtb Wd, Wn" , "op": "00010011|000|00000|0|00111|Rn|Rd"},
{"inst": "sxtb Xd, Wn" , "op": "10010011|010|00000|0|00111|Rn|Rd"},
{"inst": "sxth Wd, Wn" , "op": "00010011|000|00000|0|01111|Rn|Rd"},
@ -431,20 +431,20 @@
{"inst": "ctz Xd, Xn" , "op": "11011010|110|00000|0|00110|Rn|Rd"},
{"inst": "smax Wd, Wn, Wm" , "op": "00011010|110|Rm|0|11000|Rn|Rd"},
{"inst": "smax Xd, Xn, Xm" , "op": "10011010|110|Rm|0|11000|Rn|Rd"},
{"inst": "smax Wd, Wn, #simm" , "op": "00010001|110|000|simm:8|Rn|Rd"},
{"inst": "smax Xd, Xn, #simm" , "op": "10010001|110|000|simm:8|Rn|Rd"},
{"inst": "smax Wd, Wn, #immS" , "op": "00010001|110|000|immS:8|Rn|Rd"},
{"inst": "smax Xd, Xn, #immS" , "op": "10010001|110|000|immS:8|Rn|Rd"},
{"inst": "smin Wd, Wn, Wm" , "op": "00011010|110|Rm|0|11010|Rn|Rd"},
{"inst": "smin Xd, Xn, Xm" , "op": "10011010|110|Rm|0|11010|Rn|Rd"},
{"inst": "smin Wd, Wn, #simm" , "op": "00010001|110|010|simm:8|Rn|Rd"},
{"inst": "smin Xd, Xn, #simm" , "op": "10010001|110|010|simm:8|Rn|Rd"},
{"inst": "smin Wd, Wn, #immS" , "op": "00010001|110|010|immS:8|Rn|Rd"},
{"inst": "smin Xd, Xn, #immS" , "op": "10010001|110|010|immS:8|Rn|Rd"},
{"inst": "umax Wd, Wn, Wm" , "op": "00011010|110|Rm|0|11001|Rn|Rd"},
{"inst": "umax Xd, Xn, Xm" , "op": "10011010|110|Rm|0|11001|Rn|Rd"},
{"inst": "umax Wd, Wn, #simm" , "op": "00010001|110|001|simm:8|Rn|Rd"},
{"inst": "umax Xd, Xn, #simm" , "op": "10010001|110|001|simm:8|Rn|Rd"},
{"inst": "umax Wd, Wn, #immZ" , "op": "00010001|110|001|immZ:8|Rn|Rd"},
{"inst": "umax Xd, Xn, #immZ" , "op": "10010001|110|001|immZ:8|Rn|Rd"},
{"inst": "umin Wd, Wn, Wm" , "op": "00011010|110|Rm|0|11011|Rn|Rd"},
{"inst": "umin Xd, Xn, Xm" , "op": "10011010|110|Rm|0|11011|Rn|Rd"},
{"inst": "umin Wd, Wn, #simm" , "op": "00010001|110|011|simm:8|Rn|Rd"},
{"inst": "umin Xd, Xn, #simm" , "op": "10010001|110|011|simm:8|Rn|Rd"}
{"inst": "umin Wd, Wn, #immZ" , "op": "00010001|110|011|immZ:8|Rn|Rd"},
{"inst": "umin Xd, Xn, #immZ" , "op": "10010001|110|011|immZ:8|Rn|Rd"}
]},
{"category": "GP GP_EXT", "ext": "D128", "data": [
@ -510,21 +510,21 @@
]},
{"category": "GP GP_EXT", "ext": "LRCPC2", "data": [
{"inst": "ldapur Wd, [Xn|SP, #soff]" , "op": "10011001|010|soff:9|00|Rn|Rd"},
{"inst": "ldapur Xd, [Xn|SP, #soff]" , "op": "11011001|010|soff:9|00|Rn|Rd"},
{"inst": "ldapurb Wd, [Xn|SP, #soff]" , "op": "00011001|010|soff:9|00|Rn|Rd"},
{"inst": "ldapurh Wd, [Xn|SP, #soff]" , "op": "01011001|010|soff:9|00|Rn|Rd"},
{"inst": "ldapur Wd, [Xn|SP, #offS]" , "op": "10011001|010|offS:9|00|Rn|Rd"},
{"inst": "ldapur Xd, [Xn|SP, #offS]" , "op": "11011001|010|offS:9|00|Rn|Rd"},
{"inst": "ldapurb Wd, [Xn|SP, #offS]" , "op": "00011001|010|offS:9|00|Rn|Rd"},
{"inst": "ldapurh Wd, [Xn|SP, #offS]" , "op": "01011001|010|offS:9|00|Rn|Rd"},
{"inst": "ldapursb Wd, [Xn|SP, #soff]" , "op": "00011001|110|soff:9|00|Rn|Rd"},
{"inst": "ldapursb Xd, [Xn|SP, #soff]" , "op": "00011001|100|soff:9|00|Rn|Rd"},
{"inst": "ldapursh Wd, [Xn|SP, #soff]" , "op": "01011001|110|soff:9|00|Rn|Rd"},
{"inst": "ldapursh Xd, [Xn|SP, #soff]" , "op": "01011001|100|soff:9|00|Rn|Rd"},
{"inst": "ldapursw Xd, [Xn|SP, #soff]" , "op": "10011001|100|soff:9|00|Rn|Rd"},
{"inst": "ldapursb Wd, [Xn|SP, #offS]" , "op": "00011001|110|offS:9|00|Rn|Rd"},
{"inst": "ldapursb Xd, [Xn|SP, #offS]" , "op": "00011001|100|offS:9|00|Rn|Rd"},
{"inst": "ldapursh Wd, [Xn|SP, #offS]" , "op": "01011001|110|offS:9|00|Rn|Rd"},
{"inst": "ldapursh Xd, [Xn|SP, #offS]" , "op": "01011001|100|offS:9|00|Rn|Rd"},
{"inst": "ldapursw Xd, [Xn|SP, #offS]" , "op": "10011001|100|offS:9|00|Rn|Rd"},
{"inst": "stlur Ws, [Xn|SP, #soff]" , "op": "10011001|000|soff:9|00|Rn|Rs"},
{"inst": "stlur Xs, [Xn|SP, #soff]" , "op": "11011001|000|soff:9|00|Rn|Rs"},
{"inst": "stlurb Ws, [Xn|SP, #soff]" , "op": "00011001|000|soff:9|00|Rn|Rs"},
{"inst": "stlurh Ws, [Xn|SP, #soff]" , "op": "01011001|000|soff:9|00|Rn|Rs"}
{"inst": "stlur Ws, [Xn|SP, #offS]" , "op": "10011001|000|offS:9|00|Rn|Rs"},
{"inst": "stlur Xs, [Xn|SP, #offS]" , "op": "11011001|000|offS:9|00|Rn|Rs"},
{"inst": "stlurb Ws, [Xn|SP, #offS]" , "op": "00011001|000|offS:9|00|Rn|Rs"},
{"inst": "stlurh Ws, [Xn|SP, #offS]" , "op": "01011001|000|offS:9|00|Rn|Rs"}
]},
{"category": "GP GP_EXT", "ext": "LRCPC3", "data": [
@ -981,12 +981,12 @@
{"inst": "cmpp Xn|SP, Xm|SP" , "op": "10111010|110|Rm|000000|Rn|11111"},
{"inst": "gmi Xd, Xn|SP, Xm" , "op": "10011010|110|Rm|000101|Rn|Rd"},
{"inst": "irg Xd, Xn|SP, Xm" , "op": "10011010|110|Rm|000100|Rn|Rd"},
{"inst": "ldg Xd, [Xn|SP, #soff*16]" , "op": "11011001|011|soff:9|00|Rn|Rd"},
{"inst": "st2g Xs|SP, [Xn|SP, #soff*16]{@}{!}" , "op": "11011001|101|soff:9|!post|W|Rn|Rs"},
{"inst": "stg Xs|SP, [Xn|SP, #soff*16]{@}{!}" , "op": "11011001|001|soff:9|!post|W|Rn|Rs"},
{"inst": "stgp Xs, Xs2, [Xn|SP, #soff*16]{@}{!}" , "op": "0110100|!post|W|0|soff:7|Rs2|Rn|Rs"},
{"inst": "stz2g Xs|SP, [Xn|SP, #soff*16]{@}{!}" , "op": "11011001|111|soff:9|!post|W|Rn|Rs"},
{"inst": "stzg Xs|SP, [Xn|SP, #soff*16]{@}{!}" , "op": "11011001|011|soff:9|!post|W|Rn|Rs"},
{"inst": "ldg Xd, [Xn|SP, #offS*16]" , "op": "11011001|011|offS:9|00|Rn|Rd"},
{"inst": "st2g Xs|SP, [Xn|SP, #offS*16]{@}{!}" , "op": "11011001|101|offS:9|!post|W|Rn|Rs"},
{"inst": "stg Xs|SP, [Xn|SP, #offS*16]{@}{!}" , "op": "11011001|001|offS:9|!post|W|Rn|Rs"},
{"inst": "stgp Xs, Xs2, [Xn|SP, #offS*16]{@}{!}" , "op": "0110100|!post|W|0|offS:7|Rs2|Rn|Rs"},
{"inst": "stz2g Xs|SP, [Xn|SP, #offS*16]{@}{!}" , "op": "11011001|111|offS:9|!post|W|Rn|Rs"},
{"inst": "stzg Xs|SP, [Xn|SP, #offS*16]{@}{!}" , "op": "11011001|011|offS:9|!post|W|Rn|Rs"},
{"inst": "subg Xd|SP, Xn|SP, #imm1, #imm2" , "op": "11010001|10|imm1:6|00|imm2:4|Rn|Rd"},
{"inst": "subp Xd, Xn|SP, Xm|SP" , "op": "10011010|110|Rm|0|00000|Rn|Rd"},
{"inst": "subps Xd, Xn|SP, Xm|SP" , "op": "10011010|110|Rm|0|00000|Rn|Rd" , "io": "N=W Z=W C=W V=W"}
@ -1023,8 +1023,8 @@
{"inst": "brabz Xn" , "op": "11010110|000|11111|0000|11|Rn|11111" , "control": "call"},
{"inst": "eretaa" , "op": "11010110|100|11111|0000|10|11111|11111" , "control": "return"},
{"inst": "eretab" , "op": "11010110|100|11111|0000|11|11111|11111" , "control": "return"},
{"inst": "ldraa Xd, [Xn|SP, #soff]{!}" , "op": "11111000|0|soff:1|1|soff:9|W1|Rn|Rd"},
{"inst": "ldrab Xd, [Xn|SP, #soff]{!}" , "op": "11111000|1|soff:1|1|soff:9|W1|Rn|Rd"},
{"inst": "ldraa Xd, [Xn|SP, #offS]{!}" , "op": "11111000|0|offS:1|1|offS:9|W1|Rn|Rd"},
{"inst": "ldrab Xd, [Xn|SP, #offS]{!}" , "op": "11111000|1|offS:1|1|offS:9|W1|Rn|Rd"},
{"inst": "pacda Xd, Xn|SP" , "op": "11011010|110|00001|0|00010|Rn|Rd"},
{"inst": "pacdb Xd, Xn|SP" , "op": "11011010|110|00001|0|00011|Rn|Rd"},
{"inst": "pacdza Xd" , "op": "11011010|110|00001|0|01010|11111|Rd"},
@ -1768,40 +1768,40 @@
{"inst": "ld4r 4x{Vd.t}, [Xn|SP, Xm]@" , "op": "01001101|111|Rm |1110|sz|Rn|Vd" , "t": "16B 8H 4S 2D"},
{"inst": "ld4r 4x{Vd.t}, [Xn|SP, #off==4<<sz]@" , "op": "00001101|111|11111|1110|sz|Rn|Vd" , "t": "8B 4H 2S 1D"},
{"inst": "ld4r 4x{Vd.t}, [Xn|SP, #off==4<<sz]@" , "op": "01001101|111|11111|1110|sz|Rn|Vd" , "t": "16B 8H 4S 2D"},
{"inst": "ldnp Sd, Sd2, [Xn|SP, #soff*4]" , "op": "00101100|01|soff:7|Vd2|Vn|Vd"},
{"inst": "ldnp Dd, Dd2, [Xn|SP, #soff*8]" , "op": "01101100|01|soff:7|Vd2|Vn|Vd"},
{"inst": "ldnp Qd, Qd2, [Xn|SP, #soff*16]" , "op": "10101100|01|soff:7|Vd2|Vn|Vd"},
{"inst": "ldp Sd, Sd2, [Xn|SP, #soff*4]{@}{!}" , "op": "0010110|!post|W|1|soff:7|Vd2|Vn|Vd"},
{"inst": "ldp Dd, Dd2, [Xn|SP, #soff*8]{@}{!}" , "op": "0110110|!post|W|1|soff:7|Vd2|Vn|Vd"},
{"inst": "ldp Qd, Qd2, [Xn|SP, #soff*16]{@}{!}" , "op": "1010110|!post|W|1|soff:7|Vd2|Vn|Vd"},
{"inst": "ldnp Sd, Sd2, [Xn|SP, #offS*4]" , "op": "00101100|01|offS:7|Vd2|Vn|Vd"},
{"inst": "ldnp Dd, Dd2, [Xn|SP, #offS*8]" , "op": "01101100|01|offS:7|Vd2|Vn|Vd"},
{"inst": "ldnp Qd, Qd2, [Xn|SP, #offS*16]" , "op": "10101100|01|offS:7|Vd2|Vn|Vd"},
{"inst": "ldp Sd, Sd2, [Xn|SP, #offS*4]{@}{!}" , "op": "0010110|!post|W|1|offS:7|Vd2|Vn|Vd"},
{"inst": "ldp Dd, Dd2, [Xn|SP, #offS*8]{@}{!}" , "op": "0110110|!post|W|1|offS:7|Vd2|Vn|Vd"},
{"inst": "ldp Qd, Qd2, [Xn|SP, #offS*16]{@}{!}" , "op": "1010110|!post|W|1|offS:7|Vd2|Vn|Vd"},
{"inst": "ldr Bd, [Xn|SP, Rm, {uxtw|lsl|sxtw|sxtx #n}]" , "op": "00111100|011|Rm|option:3|n:1|10|Rn|Vd"},
{"inst": "ldr Hd, [Xn|SP, Rm, {uxtw|lsl|sxtw|sxtx #n}]" , "op": "01111100|011|Rm|option:3|n:1|10|Rn|Vd"},
{"inst": "ldr Sd, [Xn|SP, Rm, {uxtw|lsl|sxtw|sxtx #n*2}]" , "op": "10111100|011|Rm|option:3|n:1|10|Rn|Vd"},
{"inst": "ldr Dd, [Xn|SP, Rm, {uxtw|lsl|sxtw|sxtx #n*3}]" , "op": "11111100|011|Rm|option:3|n:1|10|Rn|Vd"},
{"inst": "ldr Qd, [Xn|SP, Rm, {uxtw|lsl|sxtw|sxtx #n*4}]" , "op": "00111100|111|Rm|option:3|n:1|10|Rn|Vd"},
{"inst": "ldr Sd, [PC, #soff*4]" , "op": "00011100|soff:19|Vd"},
{"inst": "ldr Dd, [PC, #soff*4]" , "op": "01011100|soff:19|Vd"},
{"inst": "ldr Qd, [PC, #soff*4]" , "op": "10011100|soff:19|Vd"},
{"inst": "ldr Bd, [Xn|SP, #zoff]" , "op": "00111101|01|zoff:12|Rn|Vd"},
{"inst": "ldr Hd, [Xn|SP, #zoff*2]" , "op": "01111101|01|zoff:12|Rn|Vd"},
{"inst": "ldr Sd, [Xn|SP, #zoff*4]" , "op": "10111101|01|zoff:12|Rn|Vd"},
{"inst": "ldr Dd, [Xn|SP, #zoff*8]" , "op": "11111101|01|zoff:12|Rn|Vd"},
{"inst": "ldr Qd, [Xn|SP, #zoff*16]" , "op": "00111101|11|zoff:12|Rn|Vd"},
{"inst": "ldr Bd, [Xn|SP, #soff]!" , "op": "00111100|010|soff:9|11|Rn|Vd"},
{"inst": "ldr Hd, [Xn|SP, #soff*2]!" , "op": "01111100|010|soff:9|11|Rn|Vd"},
{"inst": "ldr Sd, [Xn|SP, #soff*4]!" , "op": "10111100|010|soff:9|11|Rn|Vd"},
{"inst": "ldr Dd, [Xn|SP, #soff*8]!" , "op": "11111100|010|soff:9|11|Rn|Vd"},
{"inst": "ldr Qd, [Xn|SP, #soff*16]!" , "op": "00111100|110|soff:9|11|Rn|Vd"},
{"inst": "ldr Bd, [Xn|SP, #soff]@" , "op": "00111100|010|soff:9|01|Rn|Vd"},
{"inst": "ldr Hd, [Xn|SP, #soff*2]@" , "op": "01111100|010|soff:9|01|Rn|Vd"},
{"inst": "ldr Sd, [Xn|SP, #soff*4]@" , "op": "10111100|010|soff:9|01|Rn|Vd"},
{"inst": "ldr Dd, [Xn|SP, #soff*8]@" , "op": "11111100|010|soff:9|01|Rn|Vd"},
{"inst": "ldr Qd, [Xn|SP, #soff*16]@" , "op": "00111100|110|soff:9|01|Rn|Vd"},
{"inst": "ldur Bd, [Xn|SP, #soff]" , "op": "00111100|010|soff:9|00|Rn|Vd"},
{"inst": "ldur Hd, [Xn|SP, #soff]" , "op": "01111100|010|soff:9|00|Rn|Vd"},
{"inst": "ldur Sd, [Xn|SP, #soff]" , "op": "10111100|010|soff:9|00|Rn|Vd"},
{"inst": "ldur Dd, [Xn|SP, #soff]" , "op": "11111100|010|soff:9|00|Rn|Vd"},
{"inst": "ldur Qd, [Xn|SP, #soff]" , "op": "00111100|110|soff:9|00|Rn|Vd"},
{"inst": "ldr Sd, [PC, #offS*4]" , "op": "00011100|offS:19|Vd"},
{"inst": "ldr Dd, [PC, #offS*4]" , "op": "01011100|offS:19|Vd"},
{"inst": "ldr Qd, [PC, #offS*4]" , "op": "10011100|offS:19|Vd"},
{"inst": "ldr Bd, [Xn|SP, #offZ]" , "op": "00111101|01|offZ:12|Rn|Vd"},
{"inst": "ldr Hd, [Xn|SP, #offZ*2]" , "op": "01111101|01|offZ:12|Rn|Vd"},
{"inst": "ldr Sd, [Xn|SP, #offZ*4]" , "op": "10111101|01|offZ:12|Rn|Vd"},
{"inst": "ldr Dd, [Xn|SP, #offZ*8]" , "op": "11111101|01|offZ:12|Rn|Vd"},
{"inst": "ldr Qd, [Xn|SP, #offZ*16]" , "op": "00111101|11|offZ:12|Rn|Vd"},
{"inst": "ldr Bd, [Xn|SP, #offS]!" , "op": "00111100|010|offS:9|11|Rn|Vd"},
{"inst": "ldr Hd, [Xn|SP, #offS*2]!" , "op": "01111100|010|offS:9|11|Rn|Vd"},
{"inst": "ldr Sd, [Xn|SP, #offS*4]!" , "op": "10111100|010|offS:9|11|Rn|Vd"},
{"inst": "ldr Dd, [Xn|SP, #offS*8]!" , "op": "11111100|010|offS:9|11|Rn|Vd"},
{"inst": "ldr Qd, [Xn|SP, #offS*16]!" , "op": "00111100|110|offS:9|11|Rn|Vd"},
{"inst": "ldr Bd, [Xn|SP, #offS]@" , "op": "00111100|010|offS:9|01|Rn|Vd"},
{"inst": "ldr Hd, [Xn|SP, #offS*2]@" , "op": "01111100|010|offS:9|01|Rn|Vd"},
{"inst": "ldr Sd, [Xn|SP, #offS*4]@" , "op": "10111100|010|offS:9|01|Rn|Vd"},
{"inst": "ldr Dd, [Xn|SP, #offS*8]@" , "op": "11111100|010|offS:9|01|Rn|Vd"},
{"inst": "ldr Qd, [Xn|SP, #offS*16]@" , "op": "00111100|110|offS:9|01|Rn|Vd"},
{"inst": "ldur Bd, [Xn|SP, #offS]" , "op": "00111100|010|offS:9|00|Rn|Vd"},
{"inst": "ldur Hd, [Xn|SP, #offS]" , "op": "01111100|010|offS:9|00|Rn|Vd"},
{"inst": "ldur Sd, [Xn|SP, #offS]" , "op": "10111100|010|offS:9|00|Rn|Vd"},
{"inst": "ldur Dd, [Xn|SP, #offS]" , "op": "11111100|010|offS:9|00|Rn|Vd"},
{"inst": "ldur Qd, [Xn|SP, #offS]" , "op": "00111100|110|offS:9|00|Rn|Vd"},
{"inst": "mla Vx.t, Vn.t, Vm.t" , "op": "00001110|sz|1|Vm|10010|1|Vn|Vx" , "t": "8B 4H 2S"},
{"inst": "mla Vx.t, Vn.t, Vm.t" , "op": "01001110|sz|1|Vm|10010|1|Vn|Vx" , "t": "16B 8H 4S"},
{"inst": "mla Vx.4H, Vn.4H, Vm.H[#idx]" , "op": "00101111|01|idx[1:0]|Vm:4|0000|idx[2]|0|Vn|Vx"},
@ -1934,18 +1934,18 @@
{"inst": "sminv Hd, Vn.4H" , "op": "00001110|01|11000|11010|10|Vn|Vd"},
{"inst": "sminv Hd, Vn.8H" , "op": "01001110|01|11000|11010|10|Vn|Vd"},
{"inst": "sminv Sd, Vn.4S" , "op": "01001110|10|11000|11010|10|Vn|Vd"},
{"inst": "smlal Vd.ta, Vn.tb, Vm.tb" , "op": "00001110|sz|1|Vm|10000|0|Vn|Vd" , "t": "8H.8B 4S.4H 2D.2S"},
{"inst": "smlal2 Vd.ta, Vn.tb, Vm.tb" , "op": "01001110|sz|1|Vm|10000|0|Vn|Vd" , "t": "8H.16B 4S.8H 2D.4S"},
{"inst": "smlal Vd.4S, Vn.4H, Vm.H[#dx]" , "op": "00001111|01|idx[1:0]|Vm:4|0010|idx[2]|0|Vn|Vd"},
{"inst": "smlal2 Vd.4S, Vn.8H, Vm.H[#dx]" , "op": "01001111|01|idx[1:0]|Vm:4|0010|idx[2]|0|Vn|Vd"},
{"inst": "smlal Vd.2D, Vn.2S, Vm.S[#dx]" , "op": "00001111|10|idx[0] |Vm |0010|idx[1]|0|Vn|Vd"},
{"inst": "smlal2 Vd.2D, Vn.4S, Vm.S[#dx]" , "op": "01001111|10|idx[0] |Vm |0010|idx[1]|0|Vn|Vd"},
{"inst": "smlsl Vd.ta, Vn.tb, Vm.tb" , "op": "00001110|sz|1|Vm|10100|0|Vn|Vd" , "t": "8H.8B 4S.4H 2D.2S"},
{"inst": "smlsl2 Vd.ta, Vn.tb, Vm.tb" , "op": "01001110|sz|1|Vm|10100|0|Vn|Vd" , "t": "8H.16B 4S.8H 2D.4S"},
{"inst": "smlsl Vd.4S, Vn.4H, Vm.H[#dx]" , "op": "00001111|01|idx[1:0]|Vm:4|0110|idx[2]|0|Vn|Vd"},
{"inst": "smlsl2 Vd.4S, Vn.8H, Vm.H[#dx]" , "op": "01001111|01|idx[1:0]|Vm:4|0110|idx[2]|0|Vn|Vd"},
{"inst": "smlsl Vd.2D, Vn.2S, Vm.S[#dx]" , "op": "00001111|10|idx[0] |Vm |0110|idx[1]|0|Vn|Vd"},
{"inst": "smlsl2 Vd.2D, Vn.4S, Vm.S[#dx]" , "op": "01001111|10|idx[0] |Vm |0110|idx[1]|0|Vn|Vd"},
{"inst": "smlal Vx.ta, Vn.tb, Vm.tb" , "op": "00001110|sz|1|Vm|10000|0|Vn|Vx" , "t": "8H.8B 4S.4H 2D.2S"},
{"inst": "smlal2 Vx.ta, Vn.tb, Vm.tb" , "op": "01001110|sz|1|Vm|10000|0|Vn|Vx" , "t": "8H.16B 4S.8H 2D.4S"},
{"inst": "smlal Vx.4S, Vn.4H, Vm.H[#dx]" , "op": "00001111|01|idx[1:0]|Vm:4|0010|idx[2]|0|Vn|Vx"},
{"inst": "smlal2 Vx.4S, Vn.8H, Vm.H[#dx]" , "op": "01001111|01|idx[1:0]|Vm:4|0010|idx[2]|0|Vn|Vx"},
{"inst": "smlal Vx.2D, Vn.2S, Vm.S[#dx]" , "op": "00001111|10|idx[0] |Vm |0010|idx[1]|0|Vn|Vx"},
{"inst": "smlal2 Vx.2D, Vn.4S, Vm.S[#dx]" , "op": "01001111|10|idx[0] |Vm |0010|idx[1]|0|Vn|Vx"},
{"inst": "smlsl Vx.ta, Vn.tb, Vm.tb" , "op": "00001110|sz|1|Vm|10100|0|Vn|Vx" , "t": "8H.8B 4S.4H 2D.2S"},
{"inst": "smlsl2 Vx.ta, Vn.tb, Vm.tb" , "op": "01001110|sz|1|Vm|10100|0|Vn|Vx" , "t": "8H.16B 4S.8H 2D.4S"},
{"inst": "smlsl Vx.4S, Vn.4H, Vm.H[#dx]" , "op": "00001111|01|idx[1:0]|Vm:4|0110|idx[2]|0|Vn|Vx"},
{"inst": "smlsl2 Vx.4S, Vn.8H, Vm.H[#dx]" , "op": "01001111|01|idx[1:0]|Vm:4|0110|idx[2]|0|Vn|Vx"},
{"inst": "smlsl Vx.2D, Vn.2S, Vm.S[#dx]" , "op": "00001111|10|idx[0] |Vm |0110|idx[1]|0|Vn|Vx"},
{"inst": "smlsl2 Vx.2D, Vn.4S, Vm.S[#dx]" , "op": "01001111|10|idx[0] |Vm |0110|idx[1]|0|Vn|Vx"},
{"inst": "smov Wd, Vn.B[#idx]" , "op": "00001110|00|0|idx:4| 1|00101|1|Vn|Rd"},
{"inst": "smov Wd, Vn.H[#idx]" , "op": "00001110|00|0|idx:3| 10|00101|1|Vn|Rd"},
{"inst": "smov Xd, Vn.B[#idx]" , "op": "01001110|00|0|idx:4| 1|00101|1|Vn|Rd"},
@ -2216,37 +2216,37 @@
{"inst": "st4 4x{Vs.t}+, [Xn|SP, Xm]@" , "op": "01001100|100|Rm |0000|sz|Rn|Vs" , "t": "16B 8H 4S 2D"},
{"inst": "st4 4x{Vs.t}+, [Xn|SP, #off==32]@" , "op": "00001100|100|11111|0000|sz|Rn|Vs" , "t": "8B 4H 2S"},
{"inst": "st4 4x{Vs.t}+, [Xn|SP, #off==64]@" , "op": "01001100|100|11111|0000|sz|Rn|Vs" , "t": "16B 8H 4S 2D"},
{"inst": "stnp Sd, Sd2, [Xn|SP, #soff*4]" , "op": "00101100|00|soff:7|Vs2|Vn|Vs"},
{"inst": "stnp Dd, Dd2, [Xn|SP, #soff*8]" , "op": "01101100|00|soff:7|Vs2|Vn|Vs"},
{"inst": "stnp Qd, Qd2, [Xn|SP, #soff*16]" , "op": "10101100|00|soff:7|Vs2|Vn|Vs"},
{"inst": "stp Sd, Sd2, [Xn|SP, #soff*4]{@}{!}" , "op": "0010110|!post|W|0|soff:7|Vs2|Vn|Vs"},
{"inst": "stp Dd, Dd2, [Xn|SP, #soff*8]{@}{!}" , "op": "0110110|!post|W|0|soff:7|Vs2|Vn|Vs"},
{"inst": "stp Qd, Qd2, [Xn|SP, #soff*16]{@}{!}" , "op": "1010110|!post|W|0|soff:7|Vs2|Vn|Vs"},
{"inst": "stnp Sd, Sd2, [Xn|SP, #offS*4]" , "op": "00101100|00|offS:7|Vs2|Vn|Vs"},
{"inst": "stnp Dd, Dd2, [Xn|SP, #offS*8]" , "op": "01101100|00|offS:7|Vs2|Vn|Vs"},
{"inst": "stnp Qd, Qd2, [Xn|SP, #offS*16]" , "op": "10101100|00|offS:7|Vs2|Vn|Vs"},
{"inst": "stp Sd, Sd2, [Xn|SP, #offS*4]{@}{!}" , "op": "0010110|!post|W|0|offS:7|Vs2|Vn|Vs"},
{"inst": "stp Dd, Dd2, [Xn|SP, #offS*8]{@}{!}" , "op": "0110110|!post|W|0|offS:7|Vs2|Vn|Vs"},
{"inst": "stp Qd, Qd2, [Xn|SP, #offS*16]{@}{!}" , "op": "1010110|!post|W|0|offS:7|Vs2|Vn|Vs"},
{"inst": "str Bd, [Xn|SP, Rm, {uxtw|lsl|sxtw|sxtx #n}]" , "op": "00111100|001|Rm|option:3|n:1|10|Rn|Vs"},
{"inst": "str Hd, [Xn|SP, Rm, {uxtw|lsl|sxtw|sxtx #n}]" , "op": "01111100|001|Rm|option:3|n:1|10|Rn|Vs"},
{"inst": "str Sd, [Xn|SP, Rm, {uxtw|lsl|sxtw|sxtx #n*2}]" , "op": "10111100|001|Rm|option:3|n:1|10|Rn|Vs"},
{"inst": "str Dd, [Xn|SP, Rm, {uxtw|lsl|sxtw|sxtx #n*3}]" , "op": "11111100|001|Rm|option:3|n:1|10|Rn|Vs"},
{"inst": "str Qd, [Xn|SP, Rm, {uxtw|lsl|sxtw|sxtx #n*4}]" , "op": "00111100|101|Rm|option:3|n:1|10|Rn|Vs"},
{"inst": "str Bd, [Xn|SP, #zoff]" , "op": "00111101|00|zoff:12|Rn|Vs"},
{"inst": "str Hd, [Xn|SP, #zoff*2]" , "op": "01111101|00|zoff:12|Rn|Vs"},
{"inst": "str Sd, [Xn|SP, #zoff*4]" , "op": "10111101|00|zoff:12|Rn|Vs"},
{"inst": "str Dd, [Xn|SP, #zoff*8]" , "op": "11111101|00|zoff:12|Rn|Vs"},
{"inst": "str Qd, [Xn|SP, #zoff*16]" , "op": "00111101|10|zoff:12|Rn|Vs"},
{"inst": "str Bd, [Xn|SP, #soff]!" , "op": "00111100|000|soff:9|11|Rn|Vs"},
{"inst": "str Hd, [Xn|SP, #soff*2]!" , "op": "01111100|000|soff:9|11|Rn|Vs"},
{"inst": "str Sd, [Xn|SP, #soff*4]!" , "op": "10111100|000|soff:9|11|Rn|Vs"},
{"inst": "str Dd, [Xn|SP, #soff*8]!" , "op": "11111100|000|soff:9|11|Rn|Vs"},
{"inst": "str Qd, [Xn|SP, #soff*16]!" , "op": "00111100|100|soff:9|11|Rn|Vs"},
{"inst": "str Bd, [Xn|SP, #soff]@" , "op": "00111100|000|soff:9|01|Rn|Vs"},
{"inst": "str Hd, [Xn|SP, #soff*2]@" , "op": "01111100|000|soff:9|01|Rn|Vs"},
{"inst": "str Sd, [Xn|SP, #soff*4]@" , "op": "10111100|000|soff:9|01|Rn|Vs"},
{"inst": "str Dd, [Xn|SP, #soff*8]@" , "op": "11111100|000|soff:9|01|Rn|Vs"},
{"inst": "str Qd, [Xn|SP, #soff*16]@" , "op": "00111100|100|soff:9|01|Rn|Vs"},
{"inst": "stur Bd, [Xn|SP, #soff]" , "op": "00111100|000|soff:9|00|Rn|Vs"},
{"inst": "stur Hd, [Xn|SP, #soff]" , "op": "01111100|000|soff:9|00|Rn|Vs"},
{"inst": "stur Sd, [Xn|SP, #soff]" , "op": "10111100|000|soff:9|00|Rn|Vs"},
{"inst": "stur Dd, [Xn|SP, #soff]" , "op": "11111100|000|soff:9|00|Rn|Vs"},
{"inst": "stur Qd, [Xn|SP, #soff]" , "op": "00111100|100|soff:9|00|Rn|Vs"},
{"inst": "str Bd, [Xn|SP, #offZ]" , "op": "00111101|00|offZ:12|Rn|Vs"},
{"inst": "str Hd, [Xn|SP, #offZ*2]" , "op": "01111101|00|offZ:12|Rn|Vs"},
{"inst": "str Sd, [Xn|SP, #offZ*4]" , "op": "10111101|00|offZ:12|Rn|Vs"},
{"inst": "str Dd, [Xn|SP, #offZ*8]" , "op": "11111101|00|offZ:12|Rn|Vs"},
{"inst": "str Qd, [Xn|SP, #offZ*16]" , "op": "00111101|10|offZ:12|Rn|Vs"},
{"inst": "str Bd, [Xn|SP, #offS]!" , "op": "00111100|000|offS:9|11|Rn|Vs"},
{"inst": "str Hd, [Xn|SP, #offS*2]!" , "op": "01111100|000|offS:9|11|Rn|Vs"},
{"inst": "str Sd, [Xn|SP, #offS*4]!" , "op": "10111100|000|offS:9|11|Rn|Vs"},
{"inst": "str Dd, [Xn|SP, #offS*8]!" , "op": "11111100|000|offS:9|11|Rn|Vs"},
{"inst": "str Qd, [Xn|SP, #offS*16]!" , "op": "00111100|100|offS:9|11|Rn|Vs"},
{"inst": "str Bd, [Xn|SP, #offS]@" , "op": "00111100|000|offS:9|01|Rn|Vs"},
{"inst": "str Hd, [Xn|SP, #offS*2]@" , "op": "01111100|000|offS:9|01|Rn|Vs"},
{"inst": "str Sd, [Xn|SP, #offS*4]@" , "op": "10111100|000|offS:9|01|Rn|Vs"},
{"inst": "str Dd, [Xn|SP, #offS*8]@" , "op": "11111100|000|offS:9|01|Rn|Vs"},
{"inst": "str Qd, [Xn|SP, #offS*16]@" , "op": "00111100|100|offS:9|01|Rn|Vs"},
{"inst": "stur Bd, [Xn|SP, #offS]" , "op": "00111100|000|offS:9|00|Rn|Vs"},
{"inst": "stur Hd, [Xn|SP, #offS]" , "op": "01111100|000|offS:9|00|Rn|Vs"},
{"inst": "stur Sd, [Xn|SP, #offS]" , "op": "10111100|000|offS:9|00|Rn|Vs"},
{"inst": "stur Dd, [Xn|SP, #offS]" , "op": "11111100|000|offS:9|00|Rn|Vs"},
{"inst": "stur Qd, [Xn|SP, #offS]" , "op": "00111100|100|offS:9|00|Rn|Vs"},
{"inst": "sub Dd, Dn, Dm" , "op": "01111110|11|1|Vm|10000|1|Vn|Vd"},
{"inst": "sub Vd.t, Vn.t, Vm.t" , "op": "00101110|sz|1|Vm|10000|1|Vn|Vd" , "t": "8B 4H 2S"},
{"inst": "sub Vd.t, Vn.t, Vm.t" , "op": "01101110|sz|1|Vm|10000|1|Vn|Vd" , "t": "16B 8H 4S 2D"},
@ -2810,17 +2810,17 @@
{"category": "ASIMD", "ext": "ASIMD LRCPC3", "data": [
{"inst": "ldap1 Vd.D[#idx], [Xn|SP]" , "op": "0|idx:1|001101|010|00001|10000|1|Rn|Vd"},
{"inst": "ldapur Bd, [Xn|SP, #soff]" , "op": "00011101|010|soff:9|10|Rn|Vd"},
{"inst": "ldapur Hd, [Xn|SP, #soff]" , "op": "01011101|010|soff:9|10|Rn|Vd"},
{"inst": "ldapur Sd, [Xn|SP, #soff]" , "op": "10011101|010|soff:9|10|Rn|Vd"},
{"inst": "ldapur Dd, [Xn|SP, #soff]" , "op": "11011101|010|soff:9|10|Rn|Vd"},
{"inst": "ldapur Qd, [Xn|SP, #soff]" , "op": "00011101|110|soff:9|10|Rn|Vd"},
{"inst": "ldapur Bd, [Xn|SP, #offS]" , "op": "00011101|010|offS:9|10|Rn|Vd"},
{"inst": "ldapur Hd, [Xn|SP, #offS]" , "op": "01011101|010|offS:9|10|Rn|Vd"},
{"inst": "ldapur Sd, [Xn|SP, #offS]" , "op": "10011101|010|offS:9|10|Rn|Vd"},
{"inst": "ldapur Dd, [Xn|SP, #offS]" , "op": "11011101|010|offS:9|10|Rn|Vd"},
{"inst": "ldapur Qd, [Xn|SP, #offS]" , "op": "00011101|110|offS:9|10|Rn|Vd"},
{"inst": "stl1 Vs.D[#idx], [Xn|SP]" , "op": "0|idx:1|001101|000|00001|10000|1|Rn|Vs"},
{"inst": "stlur Bs, [Xn|SP, #soff]" , "op": "00011101|000|soff:9|10|Rn|Vs"},
{"inst": "stlur Hs, [Xn|SP, #soff]" , "op": "01011101|000|soff:9|10|Rn|Vs"},
{"inst": "stlur Ss, [Xn|SP, #soff]" , "op": "10011101|000|soff:9|10|Rn|Vs"},
{"inst": "stlur Ds, [Xn|SP, #soff]" , "op": "11011101|000|soff:9|10|Rn|Vs"},
{"inst": "stlur Qs, [Xn|SP, #soff]" , "op": "00011101|100|soff:9|10|Rn|Vs"}
{"inst": "stlur Bs, [Xn|SP, #offS]" , "op": "00011101|000|offS:9|10|Rn|Vs"},
{"inst": "stlur Hs, [Xn|SP, #offS]" , "op": "01011101|000|offS:9|10|Rn|Vs"},
{"inst": "stlur Ss, [Xn|SP, #offS]" , "op": "10011101|000|offS:9|10|Rn|Vs"},
{"inst": "stlur Ds, [Xn|SP, #offS]" , "op": "11011101|000|offS:9|10|Rn|Vs"},
{"inst": "stlur Qs, [Xn|SP, #offS]" , "op": "00011101|100|offS:9|10|Rn|Vs"}
]},
{"category": "ASIMD", "ext": "RDM", "data": [

View File

@ -624,200 +624,405 @@
{"inst": "[rep] outs R:dx, R:m32(ds:zsi)" , "op": "6F"}
]},
{"category": "GP GP_EXT", "data": [
{"inst": "aadd X:m32, r32" , "op": "MR: 0F 38 FC /r" , "ext": "RAO_INT"},
{"inst": "aadd X:m64, r64" , "op": "MR: REX.W 0F 38 FC /r" , "ext": "RAO_INT"},
{"inst": "aand X:m32, r32" , "op": "MR: 66 0F 38 FC /r" , "ext": "RAO_INT"},
{"inst": "aand X:m64, r64" , "op": "MR: REX.W 66 0F 38 FC /r" , "ext": "RAO_INT"},
{"inst": "adcx X:~r32, ~r32/m32" , "op": "RM: 66 0F 38 F6 /r" , "ext": "ADX" , "io": "CF=X"},
{"inst": "adcx X:~r64, ~r64/m64" , "op": "RM: REX.W 66 0F 38 F6 /r" , "ext": "ADX" , "io": "CF=X"},
{"inst": "adox X:~r32, ~r32/m32" , "op": "RM: F3 0F 38 F6 /r" , "ext": "ADX" , "io": "OF=X"},
{"inst": "adox X:~r64, ~r64/m64" , "op": "RM: REX.W F3 0F 38 F6 /r" , "ext": "ADX" , "io": "OF=X"},
{"inst": "andn W:r32, r32, r32/m32" , "op": "RVM: VEX.LZ.0F38.W0 F2 /r" , "ext": "BMI" , "io": "OF=0 SF=W ZF=W AF=U PF=U CF=0"},
{"inst": "andn W:r64, r64, r64/m64" , "op": "RVM: VEX.LZ.0F38.W1 F2 /r" , "ext": "BMI" , "io": "OF=0 SF=W ZF=W AF=U PF=U CF=0"},
{"inst": "aor X:m32, r32" , "op": "MR: F2 0F 38 FC /r" , "ext": "RAO_INT"},
{"inst": "aor X:m64, r64" , "op": "MR: REX.W F2 0F 38 FC /r" , "ext": "RAO_INT"},
{"inst": "axor X:m32, r32" , "op": "MR: F3 0F 38 FC /r" , "ext": "RAO_INT"},
{"inst": "axor X:m64, r64" , "op": "MR: REX.W F3 0F 38 FC /r" , "ext": "RAO_INT"},
{"inst": "bextr W:r32, r32/m32, r32" , "op": "RMV: VEX.LZ.0F38.W0 F7 /r" , "ext": "BMI" , "io": "OF=0 SF=U ZF=W AF=U PF=U CF=0"},
{"inst": "bextr W:r64, r64/m64, r64" , "op": "RMV: VEX.LZ.0F38.W1 F7 /r" , "ext": "BMI" , "io": "OF=0 SF=U ZF=W AF=U PF=U CF=0"},
{"inst": "blsi W:r32, r32/m32" , "op": "VM: VEX.LZ.0F38.W0 F3 /3" , "ext": "BMI" , "io": "OF=0 SF=W ZF=W AF=U PF=U CF=W"},
{"inst": "blsi W:r64, r64/m64" , "op": "VM: VEX.LZ.0F38.W1 F3 /3" , "ext": "BMI" , "io": "OF=0 SF=W ZF=W AF=U PF=U CF=W"},
{"inst": "blsmsk W:r32, r32/m32" , "op": "VM: VEX.LZ.0F38.W0 F3 /2" , "ext": "BMI" , "io": "OF=0 SF=W ZF=0 AF=U PF=U CF=W"},
{"inst": "blsmsk W:r64, r64/m64" , "op": "VM: VEX.LZ.0F38.W1 F3 /2" , "ext": "BMI" , "io": "OF=0 SF=W ZF=0 AF=U PF=U CF=W"},
{"inst": "blsr W:r32, r32/m32" , "op": "VM: VEX.LZ.0F38.W0 F3 /1" , "ext": "BMI" , "io": "OF=0 SF=W ZF=W AF=U PF=U CF=W"},
{"inst": "blsr W:r64, r64/m64" , "op": "VM: VEX.LZ.0F38.W1 F3 /1" , "ext": "BMI" , "io": "OF=0 SF=W ZF=W AF=U PF=U CF=W"},
{"inst": "bzhi W:r32, r32/m32, r32" , "op": "RMV: VEX.LZ.0F38.W0 F5 /r" , "ext": "BMI2" , "io": "OF=0 SF=W ZF=W AF=U PF=U CF=W"},
{"inst": "bzhi W:r64, r64/m64, r64" , "op": "RMV: VEX.LZ.0F38.W1 F5 /r" , "ext": "BMI2" , "io": "OF=0 SF=W ZF=W AF=U PF=U CF=W"},
{"inst": "cldemote R:mem" , "op": "0F 1C /0" , "ext": "CLDEMOTE"},
{"inst": "clflush R:mem" , "op": "0F AE /7" , "ext": "CLFLUSH"},
{"inst": "clflushopt R:mem" , "op": "66 0F AE /7" , "ext": "CLFLUSHOPT"},
{"inst": "clwb R:mem" , "op": "66 0F AE /6" , "ext": "CLWB"},
{"inst": "clzero R:<m512(ds:zax)>" , "op": "0F 01 FC" , "ext": "CLZERO"},
{"inst": "cmovo x:r16, r16/m16" , "op": "RM: 66 0F 40 /r" , "ext": "CMOV" , "io": "OF=R"},
{"inst": "cmovo X:r32, r32/m32" , "op": "RM: 0F 40 /r" , "ext": "CMOV" , "io": "OF=R"},
{"inst": "cmovo X:r64, r64/m64" , "op": "RM: REX.W 0F 40 /r" , "ext": "CMOV" , "io": "OF=R"},
{"inst": "cmovno x:r16, r16/m16" , "op": "RM: 66 0F 41 /r" , "ext": "CMOV" , "io": "OF=R"},
{"inst": "cmovno X:r32, r32/m32" , "op": "RM: 0F 41 /r" , "ext": "CMOV" , "io": "OF=R"},
{"inst": "cmovno X:r64, r64/m64" , "op": "RM: REX.W 0F 41 /r" , "ext": "CMOV" , "io": "OF=R"},
{"inst": "cmovb|cmovnae|cmovc x:r16, r16/m16" , "op": "RM: 66 0F 42 /r" , "ext": "CMOV" , "io": "CF=R"},
{"inst": "cmovb|cmovnae|cmovc X:r32, r32/m32" , "op": "RM: 0F 42 /r" , "ext": "CMOV" , "io": "CF=R"},
{"inst": "cmovb|cmovnae|cmovc X:r64, r64/m64" , "op": "RM: REX.W 0F 42 /r" , "ext": "CMOV" , "io": "CF=R"},
{"inst": "cmovae|cmovnb|cmovnc x:r16, r16/m16" , "op": "RM: 66 0F 43 /r" , "ext": "CMOV" , "io": "CF=R"},
{"inst": "cmovae|cmovnb|cmovnc X:r32, r32/m32" , "op": "RM: 0F 43 /r" , "ext": "CMOV" , "io": "CF=R"},
{"inst": "cmovae|cmovnb|cmovnc X:r64, r64/m64" , "op": "RM: REX.W 0F 43 /r" , "ext": "CMOV" , "io": "CF=R"},
{"inst": "cmove|cmovz x:r16, r16/m16" , "op": "RM: 66 0F 44 /r" , "ext": "CMOV" , "io": "ZF=R"},
{"inst": "cmove|cmovz X:r32, r32/m32" , "op": "RM: 0F 44 /r" , "ext": "CMOV" , "io": "ZF=R"},
{"inst": "cmove|cmovz X:r64, r64/m64" , "op": "RM: REX.W 0F 44 /r" , "ext": "CMOV" , "io": "ZF=R"},
{"inst": "cmovne|cmovnz x:r16, r16/m16" , "op": "RM: 66 0F 45 /r" , "ext": "CMOV" , "io": "ZF=R"},
{"inst": "cmovne|cmovnz X:r32, r32/m32" , "op": "RM: 0F 45 /r" , "ext": "CMOV" , "io": "ZF=R"},
{"inst": "cmovne|cmovnz X:r64, r64/m64" , "op": "RM: REX.W 0F 45 /r" , "ext": "CMOV" , "io": "ZF=R"},
{"inst": "cmovbe|cmovna x:r16, r16/m16" , "op": "RM: 66 0F 46 /r" , "ext": "CMOV" , "io": "CF=R ZF=R"},
{"inst": "cmovbe|cmovna X:r32, r32/m32" , "op": "RM: 0F 46 /r" , "ext": "CMOV" , "io": "CF=R ZF=R"},
{"inst": "cmovbe|cmovna X:r64, r64/m64" , "op": "RM: REX.W 0F 46 /r" , "ext": "CMOV" , "io": "CF=R ZF=R"},
{"inst": "cmova|cmovnbe x:r16, r16/m16" , "op": "RM: 66 0F 47 /r" , "ext": "CMOV" , "io": "CF=R ZF=R"},
{"inst": "cmova|cmovnbe X:r32, r32/m32" , "op": "RM: 0F 47 /r" , "ext": "CMOV" , "io": "CF=R ZF=R"},
{"inst": "cmova|cmovnbe X:r64, r64/m64" , "op": "RM: REX.W 0F 47 /r" , "ext": "CMOV" , "io": "CF=R ZF=R"},
{"inst": "cmovs x:r16, r16/m16" , "op": "RM: 66 0F 48 /r" , "ext": "CMOV" , "io": "SF=R"},
{"inst": "cmovs X:r32, r32/m32" , "op": "RM: 0F 48 /r" , "ext": "CMOV" , "io": "SF=R"},
{"inst": "cmovs X:r64, r64/m64" , "op": "RM: REX.W 0F 48 /r" , "ext": "CMOV" , "io": "SF=R"},
{"inst": "cmovns x:r16, r16/m16" , "op": "RM: 66 0F 49 /r" , "ext": "CMOV" , "io": "SF=R"},
{"inst": "cmovns X:r32, r32/m32" , "op": "RM: 0F 49 /r" , "ext": "CMOV" , "io": "SF=R"},
{"inst": "cmovns X:r64, r64/m64" , "op": "RM: REX.W 0F 49 /r" , "ext": "CMOV" , "io": "SF=R"},
{"inst": "cmovp|cmovpe x:r16, r16/m16" , "op": "RM: 66 0F 4A /r" , "ext": "CMOV" , "io": "PF=R"},
{"inst": "cmovp|cmovpe X:r32, r32/m32" , "op": "RM: 0F 4A /r" , "ext": "CMOV" , "io": "PF=R"},
{"inst": "cmovp|cmovpe X:r64, r64/m64" , "op": "RM: REX.W 0F 4A /r" , "ext": "CMOV" , "io": "PF=R"},
{"inst": "cmovnp|cmovpo x:r16, r16/m16" , "op": "RM: 66 0F 4B /r" , "ext": "CMOV" , "io": "PF=R"},
{"inst": "cmovnp|cmovpo X:r32, r32/m32" , "op": "RM: 0F 4B /r" , "ext": "CMOV" , "io": "PF=R"},
{"inst": "cmovnp|cmovpo X:r64, r64/m64" , "op": "RM: REX.W 0F 4B /r" , "ext": "CMOV" , "io": "PF=R"},
{"inst": "cmovl|cmovnge x:r16, r16/m16" , "op": "RM: 66 0F 4C /r" , "ext": "CMOV" , "io": "SF=R OF=R"},
{"inst": "cmovl|cmovnge X:r32, r32/m32" , "op": "RM: 0F 4C /r" , "ext": "CMOV" , "io": "SF=R OF=R"},
{"inst": "cmovl|cmovnge X:r64, r64/m64" , "op": "RM: REX.W 0F 4C /r" , "ext": "CMOV" , "io": "SF=R OF=R"},
{"inst": "cmovge|cmovnl x:r16, r16/m16" , "op": "RM: 66 0F 4D /r" , "ext": "CMOV" , "io": "SF=R OF=R"},
{"inst": "cmovge|cmovnl X:r32, r32/m32" , "op": "RM: 0F 4D /r" , "ext": "CMOV" , "io": "SF=R OF=R"},
{"inst": "cmovge|cmovnl X:r64, r64/m64" , "op": "RM: REX.W 0F 4D /r" , "ext": "CMOV" , "io": "SF=R OF=R"},
{"inst": "cmovle|cmovng x:r16, r16/m16" , "op": "RM: 66 0F 4E /r" , "ext": "CMOV" , "io": "ZF=R SF=R OF=R"},
{"inst": "cmovle|cmovng X:r32, r32/m32" , "op": "RM: 0F 4E /r" , "ext": "CMOV" , "io": "ZF=R SF=R OF=R"},
{"inst": "cmovle|cmovng X:r64, r64/m64" , "op": "RM: REX.W 0F 4E /r" , "ext": "CMOV" , "io": "ZF=R SF=R OF=R"},
{"inst": "cmovg|cmovnle x:r16, r16/m16" , "op": "RM: 66 0F 4F /r" , "ext": "CMOV" , "io": "ZF=R SF=R OF=R"},
{"inst": "cmovg|cmovnle X:r32, r32/m32" , "op": "RM: 0F 4F /r" , "ext": "CMOV" , "io": "ZF=R SF=R OF=R"},
{"inst": "cmovg|cmovnle X:r64, r64/m64" , "op": "RM: REX.W 0F 4F /r" , "ext": "CMOV" , "io": "ZF=R SF=R OF=R"},
{"inst": "cmpbexadd X:m32, X:r32, R:r32" , "op": "MVR: VEX.128.66.0F38.W0 E6 /r" , "ext": "CMPCCXADD" , "io": "CF=W OF=W SF=W ZF=W AF=W PF=W"},
{"inst": "cmpbexadd X:m64, X:r64, R:r64" , "op": "MVR: VEX.128.66.0F38.W1 E6 /r" , "ext": "CMPCCXADD" , "io": "CF=W OF=W SF=W ZF=W AF=W PF=W"},
{"inst": "cmpbxadd X:m32, X:r32, R:r32" , "op": "MVR: VEX.128.66.0F38.W0 E2 /r" , "ext": "CMPCCXADD" , "io": "CF=W OF=W SF=W ZF=W AF=W PF=W"},
{"inst": "cmpbxadd X:m64, X:r64, R:r64" , "op": "MVR: VEX.128.66.0F38.W1 E2 /r" , "ext": "CMPCCXADD" , "io": "CF=W OF=W SF=W ZF=W AF=W PF=W"},
{"inst": "cmplexadd X:m32, X:r32, R:r32" , "op": "MVR: VEX.128.66.0F38.W0 EE /r" , "ext": "CMPCCXADD" , "io": "CF=W OF=W SF=W ZF=W AF=W PF=W"},
{"inst": "cmplexadd X:m64, X:r64, R:r64" , "op": "MVR: VEX.128.66.0F38.W1 EE /r" , "ext": "CMPCCXADD" , "io": "CF=W OF=W SF=W ZF=W AF=W PF=W"},
{"inst": "cmplxadd X:m32, X:r32, R:r32" , "op": "MVR: VEX.128.66.0F38.W0 EC /r" , "ext": "CMPCCXADD" , "io": "CF=W OF=W SF=W ZF=W AF=W PF=W"},
{"inst": "cmplxadd X:m64, X:r64, R:r64" , "op": "MVR: VEX.128.66.0F38.W1 EC /r" , "ext": "CMPCCXADD" , "io": "CF=W OF=W SF=W ZF=W AF=W PF=W"},
{"inst": "cmpnbexadd X:m32, X:r32, R:r32" , "op": "MVR: VEX.128.66.0F38.W0 E7 /r" , "ext": "CMPCCXADD" , "io": "CF=W OF=W SF=W ZF=W AF=W PF=W"},
{"inst": "cmpnbexadd X:m64, X:r64, R:r64" , "op": "MVR: VEX.128.66.0F38.W1 E7 /r" , "ext": "CMPCCXADD" , "io": "CF=W OF=W SF=W ZF=W AF=W PF=W"},
{"inst": "cmpnbxadd X:m32, X:r32, R:r32" , "op": "MVR: VEX.128.66.0F38.W0 E3 /r" , "ext": "CMPCCXADD" , "io": "CF=W OF=W SF=W ZF=W AF=W PF=W"},
{"inst": "cmpnbxadd X:m64, X:r64, R:r64" , "op": "MVR: VEX.128.66.0F38.W1 E3 /r" , "ext": "CMPCCXADD" , "io": "CF=W OF=W SF=W ZF=W AF=W PF=W"},
{"inst": "cmpnlexadd X:m32, X:r32, R:r32" , "op": "MVR: VEX.128.66.0F38.W0 EF /r" , "ext": "CMPCCXADD" , "io": "CF=W OF=W SF=W ZF=W AF=W PF=W"},
{"inst": "cmpnlexadd X:m64, X:r64, R:r64" , "op": "MVR: VEX.128.66.0F38.W1 EF /r" , "ext": "CMPCCXADD" , "io": "CF=W OF=W SF=W ZF=W AF=W PF=W"},
{"inst": "cmpnlxadd X:m32, X:r32, R:r32" , "op": "MVR: VEX.128.66.0F38.W0 ED /r" , "ext": "CMPCCXADD" , "io": "CF=W OF=W SF=W ZF=W AF=W PF=W"},
{"inst": "cmpnlxadd X:m64, X:r64, R:r64" , "op": "MVR: VEX.128.66.0F38.W1 ED /r" , "ext": "CMPCCXADD" , "io": "CF=W OF=W SF=W ZF=W AF=W PF=W"},
{"inst": "cmpnoxadd X:m32, X:r32, R:r32" , "op": "MVR: VEX.128.66.0F38.W0 E1 /r" , "ext": "CMPCCXADD" , "io": "CF=W OF=W SF=W ZF=W AF=W PF=W"},
{"inst": "cmpnoxadd X:m64, X:r64, R:r64" , "op": "MVR: VEX.128.66.0F38.W1 E1 /r" , "ext": "CMPCCXADD" , "io": "CF=W OF=W SF=W ZF=W AF=W PF=W"},
{"inst": "cmpnpxadd X:m32, X:r32, R:r32" , "op": "MVR: VEX.128.66.0F38.W0 EB /r" , "ext": "CMPCCXADD" , "io": "CF=W OF=W SF=W ZF=W AF=W PF=W"},
{"inst": "cmpnpxadd X:m64, X:r64, R:r64" , "op": "MVR: VEX.128.66.0F38.W1 EB /r" , "ext": "CMPCCXADD" , "io": "CF=W OF=W SF=W ZF=W AF=W PF=W"},
{"inst": "cmpnsxadd X:m32, X:r32, R:r32" , "op": "MVR: VEX.128.66.0F38.W0 E9 /r" , "ext": "CMPCCXADD" , "io": "CF=W OF=W SF=W ZF=W AF=W PF=W"},
{"inst": "cmpnsxadd X:m64, X:r64, R:r64" , "op": "MVR: VEX.128.66.0F38.W1 E9 /r" , "ext": "CMPCCXADD" , "io": "CF=W OF=W SF=W ZF=W AF=W PF=W"},
{"inst": "cmpnzxadd X:m32, X:r32, R:r32" , "op": "MVR: VEX.128.66.0F38.W0 E5 /r" , "ext": "CMPCCXADD" , "io": "CF=W OF=W SF=W ZF=W AF=W PF=W"},
{"inst": "cmpnzxadd X:m64, X:r64, R:r64" , "op": "MVR: VEX.128.66.0F38.W1 E5 /r" , "ext": "CMPCCXADD" , "io": "CF=W OF=W SF=W ZF=W AF=W PF=W"},
{"inst": "cmpoxadd X:m32, X:r32, R:r32" , "op": "MVR: VEX.128.66.0F38.W0 E0 /r" , "ext": "CMPCCXADD" , "io": "CF=W OF=W SF=W ZF=W AF=W PF=W"},
{"inst": "cmpoxadd X:m64, X:r64, R:r64" , "op": "MVR: VEX.128.66.0F38.W1 E0 /r" , "ext": "CMPCCXADD" , "io": "CF=W OF=W SF=W ZF=W AF=W PF=W"},
{"inst": "cmppxadd X:m32, X:r32, R:r32" , "op": "MVR: VEX.128.66.0F38.W0 EA /r" , "ext": "CMPCCXADD" , "io": "CF=W OF=W SF=W ZF=W AF=W PF=W"},
{"inst": "cmppxadd X:m64, X:r64, R:r64" , "op": "MVR: VEX.128.66.0F38.W1 EA /r" , "ext": "CMPCCXADD" , "io": "CF=W OF=W SF=W ZF=W AF=W PF=W"},
{"inst": "cmpsxadd X:m32, X:r32, R:r32" , "op": "MVR: VEX.128.66.0F38.W0 E8 /r" , "ext": "CMPCCXADD" , "io": "CF=W OF=W SF=W ZF=W AF=W PF=W"},
{"inst": "cmpsxadd X:m64, X:r64, R:r64" , "op": "MVR: VEX.128.66.0F38.W1 E8 /r" , "ext": "CMPCCXADD" , "io": "CF=W OF=W SF=W ZF=W AF=W PF=W"},
{"inst": "[lock|xacqrel] cmpxchg x:r8/m8, r8, <al>" , "op": "MR: 0F B0 /r" , "ext": "I486" , "io": "OF=W SF=W ZF=W AF=W PF=W CF=W"},
{"inst": "[lock|xacqrel] cmpxchg x:r16/m16, r16, <ax>" , "op": "MR: 66 0F B1 /r" , "ext": "I486" , "io": "OF=W SF=W ZF=W AF=W PF=W CF=W"},
{"inst": "[lock|xacqrel] cmpxchg X:r32/m32, r32, <eax>" , "op": "MR: 0F B1 /r" , "ext": "I486" , "io": "OF=W SF=W ZF=W AF=W PF=W CF=W"},
{"inst": "[lock|xacqrel] cmpxchg X:r64/m64, r64, <rax>" , "op": "MR: REX.W 0F B1 /r" , "ext": "I486" , "io": "OF=W SF=W ZF=W AF=W PF=W CF=W"},
{"inst": "[lock|xacqrel] cmpxchg8b X:m64,X:<edx>,X:<eax>,<ecx>,<ebx>" , "op": "0F C7 /1" , "ext": "CMPXCHG8B" , "io": "ZF=W"},
{"inst": "[lock|xacqrel] cmpxchg16b X:m128,X:<rdx>,X:<rax>,<rcx>,<rbx>","op": "REX.W 0F C7 /1" , "ext": "CMPXCHG16B" , "io": "ZF=W"},
{"inst": "cmpzxadd X:m32, X:r32, R:r32" , "op": "VEX.128.66.0F38.W0 E4 /r" , "ext": "CMPCCXADD" , "io": "CF=W OF=W SF=W ZF=W AF=W PF=W"},
{"inst": "cmpzxadd X:m64, X:r64, R:r64" , "op": "VEX.128.66.0F38.W1 E4 /r" , "ext": "CMPCCXADD" , "io": "CF=W OF=W SF=W ZF=W AF=W PF=W"},
{"inst": "cpuid X:<eax>, W:<ebx>, X:<ecx>, W:<edx>" , "op": "0F A2" , "ext": "I486" , "volatile": true},
{"inst": "lahf w:<ah>" , "op": "9F" , "ext": "LAHFSAHF" , "io": "SF=R ZF=R AF=R PF=R CF=R"},
{"inst": "lfence" , "op": "0F AE E8" , "ext": "SSE2"},
{"inst": "lzcnt w:r16, r16/m16" , "op": "RM: 66 F3 0F BD /r" , "ext": "LZCNT" , "io": "OF=U SF=U ZF=W AF=U PF=U CF=W"},
{"inst": "lzcnt W:r32, r32/m32" , "op": "RM: F3 0F BD /r" , "ext": "LZCNT" , "io": "OF=U SF=U ZF=W AF=U PF=U CF=W"},
{"inst": "lzcnt W:r64, r64/m64" , "op": "RM: REX.W F3 0F BD /r" , "ext": "LZCNT" , "io": "OF=U SF=U ZF=W AF=U PF=U CF=W"},
{"inst": "mcommit" , "op": "F3 0F 01 FA" , "ext": "MCOMMIT" , "io": "OF=0 SF=0 ZF=0 AF=0 PF=0 CF=W"},
{"inst": "mfence" , "op": "0F AE F0" , "ext": "SSE2"},
{"inst": "movbe w:r16, m16" , "op": "RM: 66 0F 38 F0 /r" , "ext": "MOVBE"},
{"inst": "movbe W:r32, m32" , "op": "RM: 0F 38 F0 /r" , "ext": "MOVBE"},
{"inst": "movbe W:r64, m64" , "op": "RM: REX.W 0F 38 F0 /r" , "ext": "MOVBE"},
{"inst": "movbe W:m16, r16" , "op": "MR: 66 0F 38 F1 /r" , "ext": "MOVBE"},
{"inst": "movbe W:m32, r32" , "op": "MR: 0F 38 F1 /r" , "ext": "MOVBE"},
{"inst": "movbe W:m64, r64" , "op": "MR: REX.W 0F 38 F1 /r" , "ext": "MOVBE"},
{"inst": "movdiri W:m32, r32" , "op": "MR: 0F 38 F9 /r" , "ext": "MOVDIRI"},
{"inst": "movdiri W:m64, r64" , "op": "MR: REX.W 0F 38 F9 /r" , "ext": "MOVDIRI"},
{"inst": "movdir64b W:m512(es:r32), m512" , "op": "RM: 66 0F 38 F8 /r" , "ext": "MOVDIR64B"},
{"inst": "movdir64b W:m512(es:r64), m512" , "op": "RM: 66 0F 38 F8 /r" , "ext": "MOVDIR64B"},
{"inst": "movnti W:m32, r32" , "op": "MR: 0F C3 /r" , "ext": "SSE2"},
{"inst": "movnti W:m64, r64" , "op": "MR: REX.W 0F C3 /r" , "ext": "SSE2"},
{"inst": "mulx W:r32, W:r32, ~r32/m32, ~<edx>" , "op": "RVM: VEX.LZ.F2.0F38.W0 F6 /r" , "ext": "BMI2"},
{"inst": "mulx W:r64, W:r64, ~r64/m64, ~<rdx>" , "op": "RVM: VEX.LZ.F2.0F38.W1 F6 /r" , "ext": "BMI2"},
{"inst": "pdep W:r32, r32, r32/m32" , "op": "RVM: VEX.LZ.F2.0F38.W0 F5 /r" , "ext": "BMI2"},
{"inst": "pdep W:r64, r64, r64/m64" , "op": "RVM: VEX.LZ.F2.0F38.W1 F5 /r" , "ext": "BMI2"},
{"inst": "pext W:r32, r32, r32/m32" , "op": "RVM: VEX.LZ.F3.0F38.W0 F5 /r" , "ext": "BMI2"},
{"inst": "pext W:r64, r64, r64/m64" , "op": "RVM: VEX.LZ.F3.0F38.W1 F5 /r" , "ext": "BMI2"},
{"inst": "popcnt w:r16, r16/m16" , "op": "RM: 66 F3 0F B8 /r" , "ext": "POPCNT" , "io": "OF=0 SF=0 ZF=W AF=0 PF=0 CF=0"},
{"inst": "popcnt W:r32, r32/m32" , "op": "RM: F3 0F B8 /r" , "ext": "POPCNT" , "io": "OF=0 SF=0 ZF=W AF=0 PF=0 CF=0"},
{"inst": "popcnt W:r64, r64/m64" , "op": "RM: REX.W F3 0F B8 /r" , "ext": "POPCNT" , "io": "OF=0 SF=0 ZF=W AF=0 PF=0 CF=0"},
{"inst": "prefetch R:mem" , "op": "0F 0D /0" , "ext": "3DNOW"},
{"inst": "prefetchit0 R:mem" , "op": "0F 18 /7" , "ext": "PREFETCHI" , "arch": "X64"},
{"inst": "prefetchit1 R:mem" , "op": "0F 18 /6" , "ext": "PREFETCHI" , "arch": "X64"},
{"inst": "prefetchnta R:mem" , "op": "0F 18 /0" , "ext": "SSE"},
{"inst": "prefetcht0 R:mem" , "op": "0F 18 /1" , "ext": "SSE"},
{"inst": "prefetcht1 R:mem" , "op": "0F 18 /2" , "ext": "SSE"},
{"inst": "prefetcht2 R:mem" , "op": "0F 18 /3" , "ext": "SSE"},
{"inst": "prefetchw R:mem" , "op": "0F 0D /1" , "ext": "PREFETCHW" , "io": "OF=U SF=U ZF=U AF=U PF=U CF=U"},
{"inst": "prefetchwt1 R:mem" , "op": "0F 0D /2" , "ext": "PREFETCHWT1", "io": "OF=U SF=U ZF=U AF=U PF=U CF=U"},
{"inst": "ptwrite R:r32/m32" , "op": "F3 0F AE /4" , "ext": "PTWRITE"},
{"inst": "ptwrite R:r64/m64" , "op": "REX.W F3 0F AE /4" , "ext": "PTWRITE"},
{"inst": "rdpid W:r32" , "op": "R: F3 0F C7 /7" , "ext": "RDPID" , "arch": "X86"},
{"inst": "rdpid W:r64" , "op": "R: F3 0F C7 /7" , "ext": "RDPID" , "arch": "X64"},
{"inst": "rdpkru W:<edx>, W:<eax>, R:<ecx>" , "op": "0F 01 EE" , "ext": "OSPKE"},
{"inst": "rdpru W:<edx>, W:<eax>, R:<ecx>" , "op": "0F 01 FD" , "ext": "RDPRU"},
{"inst": "rdrand w:r16" , "op": "66 0F C7 /6" , "ext": "RDRAND" , "io": "OF=0 SF=0 ZF=0 AF=0 PF=0 CF=W"},
{"inst": "rdrand W:r32" , "op": "0F C7 /6" , "ext": "RDRAND" , "io": "OF=0 SF=0 ZF=0 AF=0 PF=0 CF=W"},
{"inst": "rdrand W:r64" , "op": "REX.W 0F C7 /6" , "ext": "RDRAND" , "io": "OF=0 SF=0 ZF=0 AF=0 PF=0 CF=W"},
{"inst": "rdseed w:r16" , "op": "66 0F C7 /7" , "ext": "RDSEED" , "io": "OF=0 SF=0 ZF=0 AF=0 PF=0 CF=W"},
{"inst": "rdseed W:r32" , "op": "0F C7 /7" , "ext": "RDSEED" , "io": "OF=0 SF=0 ZF=0 AF=0 PF=0 CF=W"},
{"inst": "rdseed W:r64" , "op": "REX.W 0F C7 /7" , "ext": "RDSEED" , "io": "OF=0 SF=0 ZF=0 AF=0 PF=0 CF=W"},
{"inst": "rdtsc W:<edx>, W:<eax>" , "op": "0F 31" , "ext": "RDTSC"},
{"inst": "rdtscp W:<edx>, W:<eax>, W:<ecx>" , "op": "0F 01 F9" , "ext": "RDTSCP"},
{"inst": "rorx W:r32, r32/m32, ib/ub" , "op": "RM: VEX.LZ.F2.0F3A.W0 F0 /r ib" , "ext": "BMI2"},
{"inst": "rorx W:r64, r64/m64, ib/ub" , "op": "RM: VEX.LZ.F2.0F3A.W1 F0 /r ib" , "ext": "BMI2"},
{"inst": "sahf R:<ah>" , "op": "9E" , "ext": "LAHFSAHF" , "io": "SF=W ZF=W AF=W PF=W CF=W"},
{"inst": "sarx W:r32, r32/m32, r32" , "op": "RMV: VEX.LZ.F3.0F38.W0 F7 /r" , "ext": "BMI2"},
{"inst": "sarx W:r64, r64/m64, r64" , "op": "RMV: VEX.LZ.F3.0F38.W1 F7 /r" , "ext": "BMI2"},
{"inst": "serialize" , "op": "0F 01 E8" , "ext": "SERIALIZE"},
{"inst": "sfence" , "op": "0F AE F8" , "ext": "SSE"},
{"inst": "shlx W:r32, r32/m32, r32" , "op": "RMV: VEX.LZ.66.0F38.W0 F7 /r" , "ext": "BMI2"},
{"inst": "shlx W:r64, r64/m64, r64" , "op": "RMV: VEX.LZ.66.0F38.W1 F7 /r" , "ext": "BMI2"},
{"inst": "shrx W:r32, r32/m32, r32" , "op": "RMV: VEX.LZ.F2.0F38.W0 F7 /r" , "ext": "BMI2"},
{"inst": "shrx W:r64, r64/m64, r64" , "op": "RMV: VEX.LZ.F2.0F38.W1 F7 /r" , "ext": "BMI2"},
{"inst": "tzcnt w:r16, r16/m16" , "op": "RM: 66 F3 0F BC /r" , "ext": "BMI" , "io": "OF=U SF=U ZF=W AF=U PF=U CF=W"},
{"inst": "tzcnt W:r32, r32/m32" , "op": "RM: F3 0F BC /r" , "ext": "BMI" , "io": "OF=U SF=U ZF=W AF=U PF=U CF=W"},
{"inst": "tzcnt W:r64, r64/m64" , "op": "RM: REX.W F3 0F BC /r" , "ext": "BMI" , "io": "OF=U SF=U ZF=W AF=U PF=U CF=W"},
{"inst": "[lock|xacqrel] xadd x:r8/m8, x:r8" , "op": "MR: 0F C0 /r" , "ext": "I486" , "io": "OF=W SF=W ZF=W AF=W PF=W CF=W"},
{"inst": "[lock|xacqrel] xadd x:r16/m16, x:r16" , "op": "MR: 66 0F C1 /r" , "ext": "I486" , "io": "OF=W SF=W ZF=W AF=W PF=W CF=W"},
{"inst": "[lock|xacqrel] xadd X:r32/m32, X:r32" , "op": "MR: 0F C1 /r" , "ext": "I486" , "io": "OF=W SF=W ZF=W AF=W PF=W CF=W"},
{"inst": "[lock|xacqrel] xadd X:r64/m64, X:r64" , "op": "MR: REX.W 0F C1 /r" , "ext": "I486" , "io": "OF=W SF=W ZF=W AF=W PF=W CF=W"}
{"category": "GP GP_EXT", "ext": "I486", "data": [
{"inst": "[lock|xacqrel] cmpxchg x:r8/m8, r8, <al>" , "op": "MR: 0F B0 /r" , "io": "OF=W SF=W ZF=W AF=W PF=W CF=W"},
{"inst": "[lock|xacqrel] cmpxchg x:r16/m16, r16, <ax>" , "op": "MR: 66 0F B1 /r" , "io": "OF=W SF=W ZF=W AF=W PF=W CF=W"},
{"inst": "[lock|xacqrel] cmpxchg X:r32/m32, r32, <eax>" , "op": "MR: 0F B1 /r" , "io": "OF=W SF=W ZF=W AF=W PF=W CF=W"},
{"inst": "[lock|xacqrel] cmpxchg X:r64/m64, r64, <rax>" , "op": "MR: REX.W 0F B1 /r" , "io": "OF=W SF=W ZF=W AF=W PF=W CF=W"},
{"inst": "cpuid X:<eax>, W:<ebx>, X:<ecx>, W:<edx>" , "op": "0F A2" , "volatile": true},
{"inst": "[lock|xacqrel] xadd x:r8/m8, x:r8" , "op": "MR: 0F C0 /r" , "io": "OF=W SF=W ZF=W AF=W PF=W CF=W"},
{"inst": "[lock|xacqrel] xadd x:r16/m16, x:r16" , "op": "MR: 66 0F C1 /r" , "io": "OF=W SF=W ZF=W AF=W PF=W CF=W"},
{"inst": "[lock|xacqrel] xadd X:r32/m32, X:r32" , "op": "MR: 0F C1 /r" , "io": "OF=W SF=W ZF=W AF=W PF=W CF=W"},
{"inst": "[lock|xacqrel] xadd X:r64/m64, X:r64" , "op": "MR: REX.W 0F C1 /r" , "io": "OF=W SF=W ZF=W AF=W PF=W CF=W"}
]},
{"category": "GP GP_EXT CRYPTO_HASH", "data": [
{"inst": "crc32 X:r32, r8/m8" , "op": "RM: F2 0F 38 F0 /r" , "ext": "SSE4_2"},
{"inst": "crc32 X:r32, r16/m16" , "op": "RM: 66 F2 0F 38 F1 /r" , "ext": "SSE4_2"},
{"inst": "crc32 X:r32, r32/m32" , "op": "RM: F2 0F 38 F1 /r" , "ext": "SSE4_2"},
{"inst": "crc32 X:r64, r8/m8" , "op": "RM: REX.W F2 0F 38 F0 /r" , "ext": "SSE4_2"},
{"inst": "crc32 X:r64, r64/m64" , "op": "RM: REX.W F2 0F 38 F1 /r" , "ext": "SSE4_2"}
{"category": "GP GP_EXT", "ext": "3DNOW", "volatile": true, "data": [
{"inst": "prefetch R:mem" , "op": "0F 0D /0"}
]},
{"category": "GP GP_EXT", "ext": "ADX", "data": [
{"inst": "adcx X:~r32, ~r32/m32" , "op": "RM: 66 0F 38 F6 /r" , "io": "CF=X"},
{"inst": "adcx X:~r64, ~r64/m64" , "op": "RM: REX.W 66 0F 38 F6 /r" , "io": "CF=X"},
{"inst": "adox X:~r32, ~r32/m32" , "op": "RM: F3 0F 38 F6 /r" , "io": "OF=X"},
{"inst": "adox X:~r64, ~r64/m64" , "op": "RM: REX.W F3 0F 38 F6 /r" , "io": "OF=X"}
]},
{"category": "GP GP_EXT", "ext": "BMI", "data": [
{"inst": "andn W:r32, r32, r32/m32" , "op": "RVM: VEX.LZ.0F38.W0 F2 /r" , "io": "OF=0 SF=W ZF=W AF=U PF=U CF=0"},
{"inst": "andn W:r64, r64, r64/m64" , "op": "RVM: VEX.LZ.0F38.W1 F2 /r" , "io": "OF=0 SF=W ZF=W AF=U PF=U CF=0"},
{"inst": "bextr W:r32, r32/m32, r32" , "op": "RMV: VEX.LZ.0F38.W0 F7 /r" , "io": "OF=0 SF=U ZF=W AF=U PF=U CF=0"},
{"inst": "bextr W:r64, r64/m64, r64" , "op": "RMV: VEX.LZ.0F38.W1 F7 /r" , "io": "OF=0 SF=U ZF=W AF=U PF=U CF=0"},
{"inst": "blsi W:r32, r32/m32" , "op": "VM: VEX.LZ.0F38.W0 F3 /3" , "io": "OF=0 SF=W ZF=W AF=U PF=U CF=W"},
{"inst": "blsi W:r64, r64/m64" , "op": "VM: VEX.LZ.0F38.W1 F3 /3" , "io": "OF=0 SF=W ZF=W AF=U PF=U CF=W"},
{"inst": "blsmsk W:r32, r32/m32" , "op": "VM: VEX.LZ.0F38.W0 F3 /2" , "io": "OF=0 SF=W ZF=0 AF=U PF=U CF=W"},
{"inst": "blsmsk W:r64, r64/m64" , "op": "VM: VEX.LZ.0F38.W1 F3 /2" , "io": "OF=0 SF=W ZF=0 AF=U PF=U CF=W"},
{"inst": "blsr W:r32, r32/m32" , "op": "VM: VEX.LZ.0F38.W0 F3 /1" , "io": "OF=0 SF=W ZF=W AF=U PF=U CF=W"},
{"inst": "blsr W:r64, r64/m64" , "op": "VM: VEX.LZ.0F38.W1 F3 /1" , "io": "OF=0 SF=W ZF=W AF=U PF=U CF=W"},
{"inst": "tzcnt w:r16, r16/m16" , "op": "RM: 66 F3 0F BC /r" , "io": "OF=U SF=U ZF=W AF=U PF=U CF=W"},
{"inst": "tzcnt W:r32, r32/m32" , "op": "RM: F3 0F BC /r" , "io": "OF=U SF=U ZF=W AF=U PF=U CF=W"},
{"inst": "tzcnt W:r64, r64/m64" , "op": "RM: REX.W F3 0F BC /r" , "io": "OF=U SF=U ZF=W AF=U PF=U CF=W"}
]},
{"category": "GP GP_EXT", "ext": "BMI2", "data": [
{"inst": "bzhi W:r32, r32/m32, r32" , "op": "RMV: VEX.LZ.0F38.W0 F5 /r" , "io": "OF=0 SF=W ZF=W AF=U PF=U CF=W"},
{"inst": "bzhi W:r64, r64/m64, r64" , "op": "RMV: VEX.LZ.0F38.W1 F5 /r" , "io": "OF=0 SF=W ZF=W AF=U PF=U CF=W"},
{"inst": "mulx W:r32, W:r32, ~r32/m32, ~<edx>" , "op": "RVM: VEX.LZ.F2.0F38.W0 F6 /r"},
{"inst": "mulx W:r64, W:r64, ~r64/m64, ~<rdx>" , "op": "RVM: VEX.LZ.F2.0F38.W1 F6 /r"},
{"inst": "pdep W:r32, r32, r32/m32" , "op": "RVM: VEX.LZ.F2.0F38.W0 F5 /r"},
{"inst": "pdep W:r64, r64, r64/m64" , "op": "RVM: VEX.LZ.F2.0F38.W1 F5 /r"},
{"inst": "pext W:r32, r32, r32/m32" , "op": "RVM: VEX.LZ.F3.0F38.W0 F5 /r"},
{"inst": "pext W:r64, r64, r64/m64" , "op": "RVM: VEX.LZ.F3.0F38.W1 F5 /r"},
{"inst": "rorx W:r32, r32/m32, ib/ub" , "op": "RM: VEX.LZ.F2.0F3A.W0 F0 /r ib"},
{"inst": "rorx W:r64, r64/m64, ib/ub" , "op": "RM: VEX.LZ.F2.0F3A.W1 F0 /r ib"},
{"inst": "sarx W:r32, r32/m32, r32" , "op": "RMV: VEX.LZ.F3.0F38.W0 F7 /r"},
{"inst": "sarx W:r64, r64/m64, r64" , "op": "RMV: VEX.LZ.F3.0F38.W1 F7 /r"},
{"inst": "shlx W:r32, r32/m32, r32" , "op": "RMV: VEX.LZ.66.0F38.W0 F7 /r"},
{"inst": "shlx W:r64, r64/m64, r64" , "op": "RMV: VEX.LZ.66.0F38.W1 F7 /r"},
{"inst": "shrx W:r32, r32/m32, r32" , "op": "RMV: VEX.LZ.F2.0F38.W0 F7 /r"},
{"inst": "shrx W:r64, r64/m64, r64" , "op": "RMV: VEX.LZ.F2.0F38.W1 F7 /r"}
]},
{"category": "GP GP_EXT", "ext": "CET_SS", "volatile": true, "data": [
{"inst": "incsspd r32" , "op": "F3 0F AE /5"},
{"inst": "incsspq r64" , "op": "REX.W F3 0F AE /5"},
{"inst": "rdsspd W:r32" , "op": "F3 0F 1E /1"},
{"inst": "rdsspq W:r64" , "op": "REX.W F3 0F 1E /1"},
{"inst": "rstorssp R:m64" , "op": "F3 0F 01 /5" , "io": "OF=0 SF=0 ZF=0 AF=0 PF=0 CF=W"},
{"inst": "saveprevssp" , "op": "F3 0F 01 EA" , "io": "OF=0 SF=0 ZF=0 AF=0 PF=0 CF=W"}
]},
{"category": "GP GP_EXT", "ext": "CLDEMOTE", "data": [
{"inst": "cldemote R:mem" , "op": "0F 1C /0"}
]},
{"category": "GP GP_EXT", "ext": "CLFLUSH", "data": [
{"inst": "clflush R:mem" , "op": "0F AE /7"}
]},
{"category": "GP GP_EXT", "ext": "CLFLUSHOPT", "data": [
{"inst": "clflushopt R:mem" , "op": "66 0F AE /7"}
]},
{"category": "GP GP_EXT", "ext": "CLWB", "data": [
{"inst": "clwb R:mem" , "op": "66 0F AE /6"}
]},
{"category": "GP GP_EXT", "ext": "CLZERO", "data": [
{"inst": "clzero R:<m512(ds:zax)>" , "op": "0F 01 FC"}
]},
{"category": "GP GP_EXT", "ext": "CMOV", "data": [
{"inst": "cmovo x:r16, r16/m16" , "op": "RM: 66 0F 40 /r" , "io": "OF=R"},
{"inst": "cmovo X:r32, r32/m32" , "op": "RM: 0F 40 /r" , "io": "OF=R"},
{"inst": "cmovo X:r64, r64/m64" , "op": "RM: REX.W 0F 40 /r" , "io": "OF=R"},
{"inst": "cmovno x:r16, r16/m16" , "op": "RM: 66 0F 41 /r" , "io": "OF=R"},
{"inst": "cmovno X:r32, r32/m32" , "op": "RM: 0F 41 /r" , "io": "OF=R"},
{"inst": "cmovno X:r64, r64/m64" , "op": "RM: REX.W 0F 41 /r" , "io": "OF=R"},
{"inst": "cmovb|cmovnae|cmovc x:r16, r16/m16" , "op": "RM: 66 0F 42 /r" , "io": "CF=R"},
{"inst": "cmovb|cmovnae|cmovc X:r32, r32/m32" , "op": "RM: 0F 42 /r" , "io": "CF=R"},
{"inst": "cmovb|cmovnae|cmovc X:r64, r64/m64" , "op": "RM: REX.W 0F 42 /r" , "io": "CF=R"},
{"inst": "cmovae|cmovnb|cmovnc x:r16, r16/m16" , "op": "RM: 66 0F 43 /r" , "io": "CF=R"},
{"inst": "cmovae|cmovnb|cmovnc X:r32, r32/m32" , "op": "RM: 0F 43 /r" , "io": "CF=R"},
{"inst": "cmovae|cmovnb|cmovnc X:r64, r64/m64" , "op": "RM: REX.W 0F 43 /r" , "io": "CF=R"},
{"inst": "cmove|cmovz x:r16, r16/m16" , "op": "RM: 66 0F 44 /r" , "io": "ZF=R"},
{"inst": "cmove|cmovz X:r32, r32/m32" , "op": "RM: 0F 44 /r" , "io": "ZF=R"},
{"inst": "cmove|cmovz X:r64, r64/m64" , "op": "RM: REX.W 0F 44 /r" , "io": "ZF=R"},
{"inst": "cmovne|cmovnz x:r16, r16/m16" , "op": "RM: 66 0F 45 /r" , "io": "ZF=R"},
{"inst": "cmovne|cmovnz X:r32, r32/m32" , "op": "RM: 0F 45 /r" , "io": "ZF=R"},
{"inst": "cmovne|cmovnz X:r64, r64/m64" , "op": "RM: REX.W 0F 45 /r" , "io": "ZF=R"},
{"inst": "cmovbe|cmovna x:r16, r16/m16" , "op": "RM: 66 0F 46 /r" , "io": "CF=R ZF=R"},
{"inst": "cmovbe|cmovna X:r32, r32/m32" , "op": "RM: 0F 46 /r" , "io": "CF=R ZF=R"},
{"inst": "cmovbe|cmovna X:r64, r64/m64" , "op": "RM: REX.W 0F 46 /r" , "io": "CF=R ZF=R"},
{"inst": "cmova|cmovnbe x:r16, r16/m16" , "op": "RM: 66 0F 47 /r" , "io": "CF=R ZF=R"},
{"inst": "cmova|cmovnbe X:r32, r32/m32" , "op": "RM: 0F 47 /r" , "io": "CF=R ZF=R"},
{"inst": "cmova|cmovnbe X:r64, r64/m64" , "op": "RM: REX.W 0F 47 /r" , "io": "CF=R ZF=R"},
{"inst": "cmovs x:r16, r16/m16" , "op": "RM: 66 0F 48 /r" , "io": "SF=R"},
{"inst": "cmovs X:r32, r32/m32" , "op": "RM: 0F 48 /r" , "io": "SF=R"},
{"inst": "cmovs X:r64, r64/m64" , "op": "RM: REX.W 0F 48 /r" , "io": "SF=R"},
{"inst": "cmovns x:r16, r16/m16" , "op": "RM: 66 0F 49 /r" , "io": "SF=R"},
{"inst": "cmovns X:r32, r32/m32" , "op": "RM: 0F 49 /r" , "io": "SF=R"},
{"inst": "cmovns X:r64, r64/m64" , "op": "RM: REX.W 0F 49 /r" , "io": "SF=R"},
{"inst": "cmovp|cmovpe x:r16, r16/m16" , "op": "RM: 66 0F 4A /r" , "io": "PF=R"},
{"inst": "cmovp|cmovpe X:r32, r32/m32" , "op": "RM: 0F 4A /r" , "io": "PF=R"},
{"inst": "cmovp|cmovpe X:r64, r64/m64" , "op": "RM: REX.W 0F 4A /r" , "io": "PF=R"},
{"inst": "cmovnp|cmovpo x:r16, r16/m16" , "op": "RM: 66 0F 4B /r" , "io": "PF=R"},
{"inst": "cmovnp|cmovpo X:r32, r32/m32" , "op": "RM: 0F 4B /r" , "io": "PF=R"},
{"inst": "cmovnp|cmovpo X:r64, r64/m64" , "op": "RM: REX.W 0F 4B /r" , "io": "PF=R"},
{"inst": "cmovl|cmovnge x:r16, r16/m16" , "op": "RM: 66 0F 4C /r" , "io": "SF=R OF=R"},
{"inst": "cmovl|cmovnge X:r32, r32/m32" , "op": "RM: 0F 4C /r" , "io": "SF=R OF=R"},
{"inst": "cmovl|cmovnge X:r64, r64/m64" , "op": "RM: REX.W 0F 4C /r" , "io": "SF=R OF=R"},
{"inst": "cmovge|cmovnl x:r16, r16/m16" , "op": "RM: 66 0F 4D /r" , "io": "SF=R OF=R"},
{"inst": "cmovge|cmovnl X:r32, r32/m32" , "op": "RM: 0F 4D /r" , "io": "SF=R OF=R"},
{"inst": "cmovge|cmovnl X:r64, r64/m64" , "op": "RM: REX.W 0F 4D /r" , "io": "SF=R OF=R"},
{"inst": "cmovle|cmovng x:r16, r16/m16" , "op": "RM: 66 0F 4E /r" , "io": "ZF=R SF=R OF=R"},
{"inst": "cmovle|cmovng X:r32, r32/m32" , "op": "RM: 0F 4E /r" , "io": "ZF=R SF=R OF=R"},
{"inst": "cmovle|cmovng X:r64, r64/m64" , "op": "RM: REX.W 0F 4E /r" , "io": "ZF=R SF=R OF=R"},
{"inst": "cmovg|cmovnle x:r16, r16/m16" , "op": "RM: 66 0F 4F /r" , "io": "ZF=R SF=R OF=R"},
{"inst": "cmovg|cmovnle X:r32, r32/m32" , "op": "RM: 0F 4F /r" , "io": "ZF=R SF=R OF=R"},
{"inst": "cmovg|cmovnle X:r64, r64/m64" , "op": "RM: REX.W 0F 4F /r" , "io": "ZF=R SF=R OF=R"}
]},
{"category": "GP GP_EXT", "ext": "CMPCCXADD", "data": [
{"inst": "cmpbexadd X:m32, X:r32, R:r32" , "op": "MVR: VEX.128.66.0F38.W0 E6 /r" , "io": "CF=W OF=W SF=W ZF=W AF=W PF=W"},
{"inst": "cmpbexadd X:m64, X:r64, R:r64" , "op": "MVR: VEX.128.66.0F38.W1 E6 /r" , "io": "CF=W OF=W SF=W ZF=W AF=W PF=W"},
{"inst": "cmpbxadd X:m32, X:r32, R:r32" , "op": "MVR: VEX.128.66.0F38.W0 E2 /r" , "io": "CF=W OF=W SF=W ZF=W AF=W PF=W"},
{"inst": "cmpbxadd X:m64, X:r64, R:r64" , "op": "MVR: VEX.128.66.0F38.W1 E2 /r" , "io": "CF=W OF=W SF=W ZF=W AF=W PF=W"},
{"inst": "cmplexadd X:m32, X:r32, R:r32" , "op": "MVR: VEX.128.66.0F38.W0 EE /r" , "io": "CF=W OF=W SF=W ZF=W AF=W PF=W"},
{"inst": "cmplexadd X:m64, X:r64, R:r64" , "op": "MVR: VEX.128.66.0F38.W1 EE /r" , "io": "CF=W OF=W SF=W ZF=W AF=W PF=W"},
{"inst": "cmplxadd X:m32, X:r32, R:r32" , "op": "MVR: VEX.128.66.0F38.W0 EC /r" , "io": "CF=W OF=W SF=W ZF=W AF=W PF=W"},
{"inst": "cmplxadd X:m64, X:r64, R:r64" , "op": "MVR: VEX.128.66.0F38.W1 EC /r" , "io": "CF=W OF=W SF=W ZF=W AF=W PF=W"},
{"inst": "cmpnbexadd X:m32, X:r32, R:r32" , "op": "MVR: VEX.128.66.0F38.W0 E7 /r" , "io": "CF=W OF=W SF=W ZF=W AF=W PF=W"},
{"inst": "cmpnbexadd X:m64, X:r64, R:r64" , "op": "MVR: VEX.128.66.0F38.W1 E7 /r" , "io": "CF=W OF=W SF=W ZF=W AF=W PF=W"},
{"inst": "cmpnbxadd X:m32, X:r32, R:r32" , "op": "MVR: VEX.128.66.0F38.W0 E3 /r" , "io": "CF=W OF=W SF=W ZF=W AF=W PF=W"},
{"inst": "cmpnbxadd X:m64, X:r64, R:r64" , "op": "MVR: VEX.128.66.0F38.W1 E3 /r" , "io": "CF=W OF=W SF=W ZF=W AF=W PF=W"},
{"inst": "cmpnlexadd X:m32, X:r32, R:r32" , "op": "MVR: VEX.128.66.0F38.W0 EF /r" , "io": "CF=W OF=W SF=W ZF=W AF=W PF=W"},
{"inst": "cmpnlexadd X:m64, X:r64, R:r64" , "op": "MVR: VEX.128.66.0F38.W1 EF /r" , "io": "CF=W OF=W SF=W ZF=W AF=W PF=W"},
{"inst": "cmpnlxadd X:m32, X:r32, R:r32" , "op": "MVR: VEX.128.66.0F38.W0 ED /r" , "io": "CF=W OF=W SF=W ZF=W AF=W PF=W"},
{"inst": "cmpnlxadd X:m64, X:r64, R:r64" , "op": "MVR: VEX.128.66.0F38.W1 ED /r" , "io": "CF=W OF=W SF=W ZF=W AF=W PF=W"},
{"inst": "cmpnoxadd X:m32, X:r32, R:r32" , "op": "MVR: VEX.128.66.0F38.W0 E1 /r" , "io": "CF=W OF=W SF=W ZF=W AF=W PF=W"},
{"inst": "cmpnoxadd X:m64, X:r64, R:r64" , "op": "MVR: VEX.128.66.0F38.W1 E1 /r" , "io": "CF=W OF=W SF=W ZF=W AF=W PF=W"},
{"inst": "cmpnpxadd X:m32, X:r32, R:r32" , "op": "MVR: VEX.128.66.0F38.W0 EB /r" , "io": "CF=W OF=W SF=W ZF=W AF=W PF=W"},
{"inst": "cmpnpxadd X:m64, X:r64, R:r64" , "op": "MVR: VEX.128.66.0F38.W1 EB /r" , "io": "CF=W OF=W SF=W ZF=W AF=W PF=W"},
{"inst": "cmpnsxadd X:m32, X:r32, R:r32" , "op": "MVR: VEX.128.66.0F38.W0 E9 /r" , "io": "CF=W OF=W SF=W ZF=W AF=W PF=W"},
{"inst": "cmpnsxadd X:m64, X:r64, R:r64" , "op": "MVR: VEX.128.66.0F38.W1 E9 /r" , "io": "CF=W OF=W SF=W ZF=W AF=W PF=W"},
{"inst": "cmpnzxadd X:m32, X:r32, R:r32" , "op": "MVR: VEX.128.66.0F38.W0 E5 /r" , "io": "CF=W OF=W SF=W ZF=W AF=W PF=W"},
{"inst": "cmpnzxadd X:m64, X:r64, R:r64" , "op": "MVR: VEX.128.66.0F38.W1 E5 /r" , "io": "CF=W OF=W SF=W ZF=W AF=W PF=W"},
{"inst": "cmpoxadd X:m32, X:r32, R:r32" , "op": "MVR: VEX.128.66.0F38.W0 E0 /r" , "io": "CF=W OF=W SF=W ZF=W AF=W PF=W"},
{"inst": "cmpoxadd X:m64, X:r64, R:r64" , "op": "MVR: VEX.128.66.0F38.W1 E0 /r" , "io": "CF=W OF=W SF=W ZF=W AF=W PF=W"},
{"inst": "cmppxadd X:m32, X:r32, R:r32" , "op": "MVR: VEX.128.66.0F38.W0 EA /r" , "io": "CF=W OF=W SF=W ZF=W AF=W PF=W"},
{"inst": "cmppxadd X:m64, X:r64, R:r64" , "op": "MVR: VEX.128.66.0F38.W1 EA /r" , "io": "CF=W OF=W SF=W ZF=W AF=W PF=W"},
{"inst": "cmpsxadd X:m32, X:r32, R:r32" , "op": "MVR: VEX.128.66.0F38.W0 E8 /r" , "io": "CF=W OF=W SF=W ZF=W AF=W PF=W"},
{"inst": "cmpsxadd X:m64, X:r64, R:r64" , "op": "MVR: VEX.128.66.0F38.W1 E8 /r" , "io": "CF=W OF=W SF=W ZF=W AF=W PF=W"},
{"inst": "cmpzxadd X:m32, X:r32, R:r32" , "op": "VEX.128.66.0F38.W0 E4 /r" , "io": "CF=W OF=W SF=W ZF=W AF=W PF=W"},
{"inst": "cmpzxadd X:m64, X:r64, R:r64" , "op": "VEX.128.66.0F38.W1 E4 /r" , "io": "CF=W OF=W SF=W ZF=W AF=W PF=W"}
]},
{"category": "GP GP_EXT", "ext": "CMPXCHG8B", "data": [
{"inst": "[lock|xacqrel] cmpxchg8b X:m64,X:<edx>,X:<eax>,<ecx>,<ebx>" , "op": "0F C7 /1" , "io": "ZF=W"}
]},
{"category": "GP GP_EXT", "ext": "CMPXCHG16B", "data": [
{"inst": "[lock|xacqrel] cmpxchg16b X:m128,X:<rdx>,X:<rax>,<rcx>,<rbx>","op": "REX.W 0F C7 /1" , "io": "ZF=W"}
]},
{"category": "GP GP_EXT", "ext": "FSGSBASE", "arch": "X64", "volatile": true, "data": [
{"inst": "rdfsbase W:r32" , "op": "F3 0F AE /0"},
{"inst": "rdfsbase W:r64" , "op": "REX.W F3 0F AE /0"},
{"inst": "rdgsbase W:r32" , "op": "F3 0F AE /1"},
{"inst": "rdgsbase W:r64" , "op": "REX.W F3 0F AE /1"},
{"inst": "wrfsbase R:r32" , "op": "F3 0F AE /2"},
{"inst": "wrfsbase R:r64" , "op": "REX.W F3 0F AE /2"},
{"inst": "wrgsbase R:r32" , "op": "F3 0F AE /3"},
{"inst": "wrgsbase R:r64" , "op": "REX.W F3 0F AE /3"}
]},
{"category": "GP GP_EXT", "ext": "FXSR", "volatile": true, "data": [
{"inst": "fxrstor R:mem" , "op": "0F AE /1" , "io": "C0=W C1=W C2=W C3=W"},
{"inst": "fxrstor64 R:mem" , "op": "REX.W 0F AE /1" , "io": "C0=W C1=W C2=W C3=W"},
{"inst": "fxsave W:mem" , "op": "0F AE /0" , "io": "C0=R C1=R C2=R C3=R"},
{"inst": "fxsave64 W:mem" , "op": "REX.W 0F AE /0" , "io": "C0=R C1=R C2=R C3=R"}
]},
{"category": "GP GP_EXT", "ext": "LAHFSAHF", "data": [
{"inst": "lahf w:<ah>" , "op": "9F" , "io": "SF=R ZF=R AF=R PF=R CF=R"},
{"inst": "sahf R:<ah>" , "op": "9E" , "io": "SF=W ZF=W AF=W PF=W CF=W"}
]},
{"category": "GP GP_EXT", "ext": "LWP", "volatile": true, "data": [
{"inst": "llwpcb R:r32" , "op": "XOP.L0.P0.M09.W0 12 /0"},
{"inst": "llwpcb R:r64" , "op": "XOP.L0.P0.M09.W1 12 /0"},
{"inst": "lwpins R:r32, R:r32/m32, id/ud" , "op": "VM: XOP.L0.P0.M0A.W0 12 /0 id"},
{"inst": "lwpins R:r64, R:r32/m32, id/ud" , "op": "VM: XOP.L0.P0.M0A.W1 12 /0 id"},
{"inst": "lwpval R:r32, R:r32/m32, id/ud" , "op": "VM: XOP.L0.P0.M0A.W0 12 /1 id"},
{"inst": "lwpval R:r64, R:r32/m32, id/ud" , "op": "VM: XOP.L0.P0.M0A.W1 12 /1 id"},
{"inst": "slwpcb W:r32" , "op": "XOP.L0.P0.M09.W0 12 /1"},
{"inst": "slwpcb W:r64" , "op": "XOP.L0.P0.M09.W1 12 /1"}
]},
{"category": "GP GP_EXT", "ext": "LZCNT", "data": [
{"inst": "lzcnt w:r16, r16/m16" , "op": "RM: 66 F3 0F BD /r" , "io": "OF=U SF=U ZF=W AF=U PF=U CF=W"},
{"inst": "lzcnt W:r32, r32/m32" , "op": "RM: F3 0F BD /r" , "io": "OF=U SF=U ZF=W AF=U PF=U CF=W"},
{"inst": "lzcnt W:r64, r64/m64" , "op": "RM: REX.W F3 0F BD /r" , "io": "OF=U SF=U ZF=W AF=U PF=U CF=W"}
]},
{"category": "GP GP_EXT", "ext": "MONITORX", "volatile": true, "data": [
{"inst": "monitorx R:<mem(ds:zax)>, R:<ecx>, R:<edx>" , "op": "0F 01 FA"},
{"inst": "mwaitx R:<eax>, R:<ecx>, R:<ebx>" , "op": "0F 01 FB"}
]},
{"category": "GP GP_EXT", "ext": "MCOMMIT", "data": [
{"inst": "mcommit" , "op": "F3 0F 01 FA" , "io": "OF=0 SF=0 ZF=0 AF=0 PF=0 CF=W"}
]},
{"category": "GP GP_EXT", "ext": "MOVBE", "data": [
{"inst": "movbe w:r16, m16" , "op": "RM: 66 0F 38 F0 /r"},
{"inst": "movbe W:r32, m32" , "op": "RM: 0F 38 F0 /r"},
{"inst": "movbe W:r64, m64" , "op": "RM: REX.W 0F 38 F0 /r"},
{"inst": "movbe W:m16, r16" , "op": "MR: 66 0F 38 F1 /r"},
{"inst": "movbe W:m32, r32" , "op": "MR: 0F 38 F1 /r"},
{"inst": "movbe W:m64, r64" , "op": "MR: REX.W 0F 38 F1 /r"}
]},
{"category": "GP GP_EXT", "ext": "MOVDIRI", "data": [
{"inst": "movdiri W:m32, r32" , "op": "MR: 0F 38 F9 /r"},
{"inst": "movdiri W:m64, r64" , "op": "MR: REX.W 0F 38 F9 /r"}
]},
{"category": "GP GP_EXT", "ext": "MOVDIR64B", "data": [
{"inst": "movdir64b W:m512(es:r32), m512" , "op": "RM: 66 0F 38 F8 /r"},
{"inst": "movdir64b W:m512(es:r64), m512" , "op": "RM: 66 0F 38 F8 /r"}
]},
{"category": "GP GP_EXT", "ext": "PCONFIG", "volatile": true, "data": [
{"inst": "pconfig" , "op": "0F 01 C5"}
]},
{"category": "GP GP_EXT", "ext": "POPCNT", "data": [
{"inst": "popcnt w:r16, r16/m16" , "op": "RM: 66 F3 0F B8 /r" , "io": "OF=0 SF=0 ZF=W AF=0 PF=0 CF=0"},
{"inst": "popcnt W:r32, r32/m32" , "op": "RM: F3 0F B8 /r" , "io": "OF=0 SF=0 ZF=W AF=0 PF=0 CF=0"},
{"inst": "popcnt W:r64, r64/m64" , "op": "RM: REX.W F3 0F B8 /r" , "io": "OF=0 SF=0 ZF=W AF=0 PF=0 CF=0"}
]},
{"category": "GP GP_EXT", "ext": "OSPKE", "data": [
{"inst": "rdpkru W:<edx>, W:<eax>, R:<ecx>" , "op": "0F 01 EE"}
]},
{"category": "GP GP_EXT", "ext": "PREFETCHI", "arch": "X64", "volatile": true, "data": [
{"inst": "prefetchit0 R:mem" , "op": "0F 18 /7"},
{"inst": "prefetchit1 R:mem" , "op": "0F 18 /6"}
]},
{"category": "GP GP_EXT", "ext": "PREFETCHW", "volatile": true, "data": [
{"inst": "prefetchw R:mem" , "op": "0F 0D /1" , "io": "OF=U SF=U ZF=U AF=U PF=U CF=U"}
]},
{"category": "GP GP_EXT", "ext": "PREFETCHWT1", "volatile": true, "data": [
{"inst": "prefetchwt1 R:mem" , "op": "0F 0D /2" , "io": "OF=U SF=U ZF=U AF=U PF=U CF=U"}
]},
{"category": "GP GP_EXT", "ext": "PTWRITE", "volatile": true, "data": [
{"inst": "ptwrite R:r32/m32" , "op": "F3 0F AE /4"},
{"inst": "ptwrite R:r64/m64" , "op": "REX.W F3 0F AE /4"}
]},
{"category": "GP GP_EXT", "ext": "RAO_INT", "volatile": true, "data": [
{"inst": "aadd X:m32, r32" , "op": "MR: 0F 38 FC /r"},
{"inst": "aadd X:m64, r64" , "op": "MR: REX.W 0F 38 FC /r"},
{"inst": "aand X:m32, r32" , "op": "MR: 66 0F 38 FC /r"},
{"inst": "aand X:m64, r64" , "op": "MR: REX.W 66 0F 38 FC /r"},
{"inst": "aor X:m32, r32" , "op": "MR: F2 0F 38 FC /r"},
{"inst": "aor X:m64, r64" , "op": "MR: REX.W F2 0F 38 FC /r"},
{"inst": "axor X:m32, r32" , "op": "MR: F3 0F 38 FC /r"},
{"inst": "axor X:m64, r64" , "op": "MR: REX.W F3 0F 38 FC /r"}
]},
{"category": "GP GP_EXT", "ext": "RDPID", "data": [
{"inst": "rdpid W:r32" , "op": "R: F3 0F C7 /7" , "arch": "X86"},
{"inst": "rdpid W:r64" , "op": "R: F3 0F C7 /7" , "arch": "X64"}
]},
{"category": "GP GP_EXT", "ext": "RDPRU", "data": [
{"inst": "rdpru W:<edx>, W:<eax>, R:<ecx>" , "op": "0F 01 FD"}
]},
{"category": "GP GP_EXT", "ext": "RDRAND", "data": [
{"inst": "rdrand w:r16" , "op": "66 0F C7 /6" , "io": "OF=0 SF=0 ZF=0 AF=0 PF=0 CF=W"},
{"inst": "rdrand W:r32" , "op": "0F C7 /6" , "io": "OF=0 SF=0 ZF=0 AF=0 PF=0 CF=W"},
{"inst": "rdrand W:r64" , "op": "REX.W 0F C7 /6" , "io": "OF=0 SF=0 ZF=0 AF=0 PF=0 CF=W"}
]},
{"category": "GP GP_EXT", "ext": "RDSEED", "data": [
{"inst": "rdseed w:r16" , "op": "66 0F C7 /7" , "io": "OF=0 SF=0 ZF=0 AF=0 PF=0 CF=W"},
{"inst": "rdseed W:r32" , "op": "0F C7 /7" , "io": "OF=0 SF=0 ZF=0 AF=0 PF=0 CF=W"},
{"inst": "rdseed W:r64" , "op": "REX.W 0F C7 /7" , "io": "OF=0 SF=0 ZF=0 AF=0 PF=0 CF=W"}
]},
{"category": "GP GP_EXT", "ext": "RDTSC", "data": [
{"inst": "rdtsc W:<edx>, W:<eax>" , "op": "0F 31"}
]},
{"category": "GP GP_EXT", "ext": "RDTSCP", "data": [
{"inst": "rdtscp W:<edx>, W:<eax>, W:<ecx>" , "op": "0F 01 F9"}
]},
{"category": "GP GP_EXT", "ext": "RTM", "volatile": true, "data": [
{"inst": "xabort ib/ub" , "op": "C6 /7 ib"},
{"inst": "xbegin rel16" , "op": "66 C7 /7 cw"},
{"inst": "xbegin rel32" , "op": "C7 /7 cd"},
{"inst": "xend" , "op": "0F 01 D5"}
]},
{"category": "GP GP_EXT", "ext": "SERIALIZE", "volatile": true, "data": [
{"inst": "serialize" , "op": "0F 01 E8"}
]},
{"category": "GP GP_EXT", "ext": "SSE", "volatile": true, "data": [
{"inst": "prefetchnta R:mem" , "op": "0F 18 /0"},
{"inst": "prefetcht0 R:mem" , "op": "0F 18 /1"},
{"inst": "prefetcht1 R:mem" , "op": "0F 18 /2"},
{"inst": "prefetcht2 R:mem" , "op": "0F 18 /3"},
{"inst": "sfence" , "op": "0F AE F8"}
]},
{"category": "GP GP_EXT", "ext": "SSE2", "volatile": true, "data": [
{"inst": "lfence" , "op": "0F AE E8"},
{"inst": "mfence" , "op": "0F AE F0"},
{"inst": "movnti W:m32, r32" , "op": "MR: 0F C3 /r"},
{"inst": "movnti W:m64, r64" , "op": "MR: REX.W 0F C3 /r"}
]},
{"category": "GP GP_EXT CRYPTO_HASH", "ext": "SSE4_2", "data": [
{"inst": "crc32 X:r32, r8/m8" , "op": "RM: F2 0F 38 F0 /r"},
{"inst": "crc32 X:r32, r16/m16" , "op": "RM: 66 F2 0F 38 F1 /r"},
{"inst": "crc32 X:r32, r32/m32" , "op": "RM: F2 0F 38 F1 /r"},
{"inst": "crc32 X:r64, r8/m8" , "op": "RM: REX.W F2 0F 38 F0 /r"},
{"inst": "crc32 X:r64, r64/m64" , "op": "RM: REX.W F2 0F 38 F1 /r"}
]},
{"category": "GP GP_EXT", "ext": "TSE", "arch": "X64", "volatile": true, "data": [
{"inst": "pbndkb W:<rax>, R:<mem(ds:rbx)>, W:<mem(ds:rcx)>" , "op": "0F 01 C7" , "io": "OF=0 SF=0 ZF=W AF=0 PF=0 CF=0"}
]},
{"category": "GP GP_EXT", "ext": "TSX", "volatile": true, "data": [
{"inst": "xtest" , "op": "0F 01 D6" , "io": "OF=0 SF=0 ZF=W AF=0 PF=0 CF=0"}
]},
{"category": "GP GP_EXT", "ext": "TSXLDTRK", "volatile": true, "data": [
{"inst": "xresldtrk" , "op": "F2 0F 01 E9"},
{"inst": "xsusldtrk" , "op": "F2 0F 01 E8"}
]},
{"category": "GP GP_EXT", "ext": "UINTR", "arch": "X64", "volatile": true, "data": [
{"inst": "uiret" , "op": "F3 0F 01 EC"},
{"inst": "clui" , "op": "F3 0F 01 EE"},
{"inst": "stui" , "op": "F3 0F 01 EF"},
{"inst": "testui" , "op": "F3 0F 01 ED" , "io": "OF=0 SF=0 ZF=0 AF=0 PF=0 CF=W"},
{"inst": "senduipi R:r64" , "op": "R: F3 0F C7 /6"}
]},
{"category": "GP GP_EXT", "ext": "WAITPKG", "volatile": true, "data": [
{"inst": "tpause R:r32, <edx>, <eax>" , "op": "66 0F AE /6" , "io": "OF=0 SF=0 ZF=0 AF=0 PF=0 CF=W"},
{"inst": "umonitor R:mem(ds:r32)" , "op": "F3 0F AE /6" , "arch": "X86"},
{"inst": "umonitor R:mem(ds:r64)" , "op": "F3 0F AE /6" , "arch": "X64"},
{"inst": "umwait R:r32, <edx>, <eax>" , "op": "F2 0F AE /6" , "io": "OF=0 SF=0 ZF=0 AF=0 PF=0 CF=W"}
]},
{"category": "GP GP_EXT", "ext": "XSAVE", "volatile": true, "data": [
{"inst": "xgetbv W:<edx>, W:<eax>, R:<ecx>" , "op": "0F 01 D0" , "io": "XCR=R"},
{"inst": "xrstor R:mem, <edx>, <eax>" , "op": "0F AE /5" , "io": "XCR=R"},
{"inst": "xrstor64 R:mem, <edx>, <eax>" , "op": "REX.W 0F AE /5" , "io": "XCR=R"},
{"inst": "xsave W:mem, <edx>, <eax>" , "op": "0F AE /4" , "io": "XCR=R"},
{"inst": "xsave64 W:mem, <edx>, <eax>" , "op": "REX.W 0F AE /4" , "io": "XCR=R"}
]},
{"category": "GP GP_EXT", "ext": "XSAVEC", "volatile": true, "data": [
{"inst": "xsavec W:mem, <edx>, <eax>" , "op": "0F C7 /4" , "io": "XCR=R"},
{"inst": "xsavec64 W:mem, <edx>, <eax>" , "op": "REX.W 0F C7 /4" , "io": "XCR=R"}
]},
{"category": "GP GP_EXT", "ext": "XSAVEOPT", "volatile": true, "data": [
{"inst": "xsaveopt W:mem, <edx>, <eax>" , "op": "0F AE /6" , "io": "XCR=R"},
{"inst": "xsaveopt64 W:mem, <edx>, <eax>" , "op": "REX.W 0F AE /6" , "io": "XCR=R"}
]},
{"category": "GP", "volatile": true, "data": [
@ -863,21 +1068,35 @@
{"inst": "xlatb" , "op": "D7"}
]},
{"category": "GP", "deprecated": true, "data": [
{"inst": "aaa x:<ax>" , "op": "37" , "arch": "X86", "io": "OF=U SF=U ZF=U AF=W PF=U CF=W"},
{"inst": "aas x:<ax>" , "op": "3F" , "arch": "X86", "io": "OF=U SF=U ZF=U AF=W PF=U CF=W"},
{"inst": "aad x:<ax>, ib/ub" , "op": "D5 ib" , "arch": "X86", "io": "OF=U SF=W ZF=W AF=U PF=W CF=U"},
{"inst": "aam x:<ax>, ib/ub" , "op": "D4 ib" , "arch": "X86", "io": "OF=U SF=W ZF=W AF=U PF=W CF=U"},
{"inst": "arpl x:r16/m16, R:r16" , "op": "MR: 63 /r" , "arch": "X86", "io": "ZF=W"},
{"inst": "bound R:r16, R:m32" , "op": "RM: 66 62 /r" , "arch": "X86"},
{"inst": "bound R:r32, R:m64" , "op": "RM: 62 /r" , "arch": "X86"},
{"inst": "daa x:<ax>" , "op": "27" , "arch": "X86", "io": "OF=U SF=W ZF=W AF=W PF=W CF=W"},
{"inst": "das x:<ax>" , "op": "2F" , "arch": "X86", "io": "OF=U SF=W ZF=W AF=W PF=W CF=W"},
{"inst": "into" , "op": "CE" , "arch": "X86", "io": "OF=R"},
{"inst": "popa" , "op": "66 61" , "arch": "X86"},
{"inst": "popad" , "op": "61" , "arch": "X86"},
{"inst": "pusha" , "op": "66 60" , "arch": "X86"},
{"inst": "pushad" , "op": "60" , "arch": "X86"}
{"category": "GP", "deprecated": true, "arch": "X86", "data": [
{"inst": "aaa x:<ax>" , "op": "37" , "io": "OF=U SF=U ZF=U AF=W PF=U CF=W"},
{"inst": "aas x:<ax>" , "op": "3F" , "io": "OF=U SF=U ZF=U AF=W PF=U CF=W"},
{"inst": "aad x:<ax>, ib/ub" , "op": "D5 ib" , "io": "OF=U SF=W ZF=W AF=U PF=W CF=U"},
{"inst": "aam x:<ax>, ib/ub" , "op": "D4 ib" , "io": "OF=U SF=W ZF=W AF=U PF=W CF=U"},
{"inst": "arpl x:r16/m16, R:r16" , "op": "MR: 63 /r" , "io": "ZF=W"},
{"inst": "bound R:r16, R:m32" , "op": "RM: 66 62 /r"},
{"inst": "bound R:r32, R:m64" , "op": "RM: 62 /r"},
{"inst": "daa x:<ax>" , "op": "27" , "io": "OF=U SF=W ZF=W AF=W PF=W CF=W"},
{"inst": "das x:<ax>" , "op": "2F" , "io": "OF=U SF=W ZF=W AF=W PF=W CF=W"},
{"inst": "into" , "op": "CE" , "io": "OF=R"},
{"inst": "popa" , "op": "66 61"},
{"inst": "popad" , "op": "61"},
{"inst": "pusha" , "op": "66 60"},
{"inst": "pushad" , "op": "60"}
]},
{"category": "GP GP_EXT", "ext": "MPX", "deprecated": true, "data": [
{"inst": "bndcl R:bnd, r32/m32" , "op": "RM: F3 0F 1A /r" , "arch": "X86"},
{"inst": "bndcl R:bnd, r64/m64" , "op": "RM: F3 0F 1A /r" , "arch": "X64"},
{"inst": "bndcn R:bnd, r32/m32" , "op": "RM: F2 0F 1B /r" , "arch": "X86"},
{"inst": "bndcn R:bnd, r64/m64" , "op": "RM: F2 0F 1B /r" , "arch": "X64"},
{"inst": "bndcu R:bnd, r32/m32" , "op": "RM: F2 0F 1A /r" , "arch": "X86"},
{"inst": "bndcu R:bnd, r64/m64" , "op": "RM: F2 0F 1A /r" , "arch": "X64"},
{"inst": "bndldx W:bnd, mib" , "op": "RM: 0F 1A /r"},
{"inst": "bndmk W:bnd, mem" , "op": "RM: F3 0F 1B /r"},
{"inst": "bndmov W:bnd, bnd/mem" , "op": "RM: 66 0F 1A /r"},
{"inst": "bndmov W:bnd/mem, bnd" , "op": "MR: 66 0F 1B /r"},
{"inst": "bndstx W:mib, bnd" , "op": "MR: 0F 1B /r"}
]},
{"category": "GP GP_EXT", "ext": "TBM", "deprecated": true, "data": [
@ -901,141 +1120,84 @@
{"inst": "t1mskc W:r64, r64/m64" , "op": "VM: XOP.LZ.M09.W1 01 /7"}
]},
{"category": "GP GP_EXT", "ext": "MPX", "deprecated": true, "data": [
{"inst": "bndcl R:bnd, r32/m32" , "op": "RM: F3 0F 1A /r" , "arch": "X86"},
{"inst": "bndcl R:bnd, r64/m64" , "op": "RM: F3 0F 1A /r" , "arch": "X64"},
{"inst": "bndcn R:bnd, r32/m32" , "op": "RM: F2 0F 1B /r" , "arch": "X86"},
{"inst": "bndcn R:bnd, r64/m64" , "op": "RM: F2 0F 1B /r" , "arch": "X64"},
{"inst": "bndcu R:bnd, r32/m32" , "op": "RM: F2 0F 1A /r" , "arch": "X86"},
{"inst": "bndcu R:bnd, r64/m64" , "op": "RM: F2 0F 1A /r" , "arch": "X64"},
{"inst": "bndldx W:bnd, mib" , "op": "RM: 0F 1A /r"},
{"inst": "bndmk W:bnd, mem" , "op": "RM: F3 0F 1B /r"},
{"inst": "bndmov W:bnd, bnd/mem" , "op": "RM: 66 0F 1A /r"},
{"inst": "bndmov W:bnd/mem, bnd" , "op": "MR: 66 0F 1B /r"},
{"inst": "bndstx W:mib, bnd" , "op": "MR: 0F 1B /r"}
]},
{"category": "GP GP_EXT", "volatile": true, "data": [
{"inst": "fxrstor R:mem" , "op": "0F AE /1" , "ext": "FXSR" , "io": "C0=W C1=W C2=W C3=W"},
{"inst": "fxrstor64 R:mem" , "op": "REX.W 0F AE /1" , "ext": "FXSR" , "io": "C0=W C1=W C2=W C3=W"},
{"inst": "fxsave W:mem" , "op": "0F AE /0" , "ext": "FXSR" , "io": "C0=R C1=R C2=R C3=R"},
{"inst": "fxsave64 W:mem" , "op": "REX.W 0F AE /0" , "ext": "FXSR" , "io": "C0=R C1=R C2=R C3=R"},
{"inst": "xgetbv W:<edx>, W:<eax>, R:<ecx>" , "op": "0F 01 D0" , "ext": "XSAVE" , "io": "XCR=R"},
{"inst": "xrstor R:mem, <edx>, <eax>" , "op": "0F AE /5" , "ext": "XSAVE" , "io": "XCR=R"},
{"inst": "xrstor64 R:mem, <edx>, <eax>" , "op": "REX.W 0F AE /5" , "ext": "XSAVE" , "io": "XCR=R"},
{"inst": "xsave W:mem, <edx>, <eax>" , "op": "0F AE /4" , "ext": "XSAVE" , "io": "XCR=R"},
{"inst": "xsave64 W:mem, <edx>, <eax>" , "op": "REX.W 0F AE /4" , "ext": "XSAVE" , "io": "XCR=R"},
{"inst": "xsavec W:mem, <edx>, <eax>" , "op": "0F C7 /4" , "ext": "XSAVEC" , "io": "XCR=R"},
{"inst": "xsavec64 W:mem, <edx>, <eax>" , "op": "REX.W 0F C7 /4" , "ext": "XSAVEC" , "io": "XCR=R"},
{"inst": "xsaveopt W:mem, <edx>, <eax>" , "op": "0F AE /6" , "ext": "XSAVEOPT", "io": "XCR=R"},
{"inst": "xsaveopt64 W:mem, <edx>, <eax>" , "op": "REX.W 0F AE /6" , "ext": "XSAVEOPT", "io": "XCR=R"}
]},
{"category": "GP GP_EXT", "volatile": true, "data": [
{"inst": "incsspd r32" , "op": "F3 0F AE /5" , "ext": "CET_SS"},
{"inst": "incsspq r64" , "op": "REX.W F3 0F AE /5" , "ext": "CET_SS"},
{"inst": "monitorx R:<mem(ds:zax)>, R:<ecx>, R:<edx>" , "op": "0F 01 FA" , "ext": "MONITORX"},
{"inst": "mwaitx R:<eax>, R:<ecx>, R:<ebx>" , "op": "0F 01 FB" , "ext": "MONITORX"},
{"inst": "rdsspd W:r32" , "op": "F3 0F 1E /1" , "ext": "CET_SS"},
{"inst": "rdsspq W:r64" , "op": "REX.W F3 0F 1E /1" , "ext": "CET_SS"},
{"inst": "rstorssp R:m64" , "op": "F3 0F 01 /5" , "ext": "CET_SS" , "io": "OF=0 SF=0 ZF=0 AF=0 PF=0 CF=W"},
{"inst": "saveprevssp" , "op": "F3 0F 01 EA" , "ext": "CET_SS" , "io": "OF=0 SF=0 ZF=0 AF=0 PF=0 CF=W"},
{"inst": "tpause R:r32, <edx>, <eax>" , "op": "66 0F AE /6" , "ext": "WAITPKG" , "io": "OF=0 SF=0 ZF=0 AF=0 PF=0 CF=W"},
{"inst": "umonitor R:mem(ds:r32)" , "op": "F3 0F AE /6" , "ext": "WAITPKG" , "arch": "X86"},
{"inst": "umonitor R:mem(ds:r64)" , "op": "F3 0F AE /6" , "ext": "WAITPKG" , "arch": "X64"},
{"inst": "umwait R:r32, <edx>, <eax>" , "op": "F2 0F AE /6" , "ext": "WAITPKG" , "io": "OF=0 SF=0 ZF=0 AF=0 PF=0 CF=W"}
]},
{"category": "GP GP_EXT", "ext": "LWP", "volatile": true, "data": [
{"inst": "llwpcb R:r32" , "op": "XOP.L0.P0.M09.W0 12 /0"},
{"inst": "llwpcb R:r64" , "op": "XOP.L0.P0.M09.W1 12 /0"},
{"inst": "lwpins R:r32, R:r32/m32, id/ud" , "op": "VM: XOP.L0.P0.M0A.W0 12 /0 id"},
{"inst": "lwpins R:r64, R:r32/m32, id/ud" , "op": "VM: XOP.L0.P0.M0A.W1 12 /0 id"},
{"inst": "lwpval R:r32, R:r32/m32, id/ud" , "op": "VM: XOP.L0.P0.M0A.W0 12 /1 id"},
{"inst": "lwpval R:r64, R:r32/m32, id/ud" , "op": "VM: XOP.L0.P0.M0A.W1 12 /1 id"},
{"inst": "slwpcb W:r32" , "op": "XOP.L0.P0.M09.W0 12 /1"},
{"inst": "slwpcb W:r64" , "op": "XOP.L0.P0.M09.W1 12 /1"}
]},
{"category": "GP GP_EXT", "ext": "FSGSBASE", "arch": "X64", "volatile": true, "data": [
{"inst": "rdfsbase W:r32" , "op": "F3 0F AE /0"},
{"inst": "rdfsbase W:r64" , "op": "REX.W F3 0F AE /0"},
{"inst": "rdgsbase W:r32" , "op": "F3 0F AE /1"},
{"inst": "rdgsbase W:r64" , "op": "REX.W F3 0F AE /1"},
{"inst": "wrfsbase R:r32" , "op": "F3 0F AE /2"},
{"inst": "wrfsbase R:r64" , "op": "REX.W F3 0F AE /2"},
{"inst": "wrgsbase R:r32" , "op": "F3 0F AE /3"},
{"inst": "wrgsbase R:r64" , "op": "REX.W F3 0F AE /3"}
]},
{"category": "GP GP_EXT", "ext": "UINTR", "arch": "X64", "volatile": true, "data": [
{"inst": "uiret" , "op": "F3 0F 01 EC"},
{"inst": "clui" , "op": "F3 0F 01 EE"},
{"inst": "stui" , "op": "F3 0F 01 EF"},
{"inst": "testui" , "op": "F3 0F 01 ED" , "io": "OF=0 SF=0 ZF=0 AF=0 PF=0 CF=W"},
{"inst": "senduipi R:r64" , "op": "R: F3 0F C7 /6"}
]},
{"category": "GP GP_EXT", "ext": "PCONFIG", "volatile": true, "data": [
{"inst": "pconfig" , "op": "0F 01 C5"}
]},
{"category": "GP GP_EXT", "ext": "TSE", "arch": "X64", "volatile": true, "data": [
{"inst": "pbndkb" , "op": "0F 01 C7"}
]},
{"category": "GP GP_EXT", "volatile": true, "data": [
{"inst": "xabort ib/ub" , "op": "C6 /7 ib" , "ext": "RTM"},
{"inst": "xbegin rel16" , "op": "66 C7 /7 cw" , "ext": "RTM"},
{"inst": "xbegin rel32" , "op": "C7 /7 cd" , "ext": "RTM"},
{"inst": "xend" , "op": "0F 01 D5" , "ext": "RTM"},
{"inst": "xresldtrk" , "op": "F2 0F 01 E9" , "ext": "TSXLDTRK"},
{"inst": "xsusldtrk" , "op": "F2 0F 01 E8" , "ext": "TSXLDTRK"},
{"inst": "xtest" , "op": "0F 01 D6" , "ext": "TSX" , "io": "OF=0 SF=0 ZF=W AF=0 PF=0 CF=0"}
]},
{"category": "GP", "volatile": true, "privilege": "L0", "data": [
{"inst": "clrssbsy R:m64" , "op": "F3 0F AE /6" , "ext": "CET_SS" , "io": "OF=0 SF=0 ZF=0 AF=0 PF=0 CF=W"},
{"inst": "clts" , "op": "0F 06"},
{"inst": "endbr32" , "op": "F3 0F 1E FB" , "ext": "CET_IBT"},
{"inst": "endbr64" , "op": "F3 0F 1E FA" , "ext": "CET_IBT"},
{"inst": "getsec <eax>" , "op": "0F 37" , "ext": "SMX"},
{"inst": "hlt" , "op": "F4"},
{"inst": "hreset ib/ub, W:<eax>" , "op": "F3 0F 3A F0 /0 ib" , "ext": "HRESET"},
{"inst": "invd" , "op": "0F 08" , "ext": "I486" },
{"inst": "invlpg R:mem" , "op": "0F 01 /7" , "ext": "I486"},
{"inst": "invpcid R:r32, R:m128" , "op": "RM: 66 0F 38 82 /r" , "ext": "I486" , "arch": "X86"},
{"inst": "invpcid R:r64, R:m128" , "op": "RM: 66 0F 38 82 /r" , "ext": "I486" , "arch": "X64"},
{"inst": "lgdt R:mem" , "op": "0F 01 /2"},
{"inst": "lidt R:mem" , "op": "0F 01 /3"},
{"inst": "lldt R:r16/m16" , "op": "0F 00 /2"},
{"inst": "lmsw R:r16/m16" , "op": "0F 01 /6"},
{"inst": "ltr R:r16/m16" , "op": "0F 00 /3"},
{"inst": "monitor R:<mem(ds:zax)>, R:<ecx>, R:<edx>" , "op": "0F 01 C8" , "ext": "MONITOR"},
{"inst": "mwait R:<eax>, R:<ecx>" , "op": "0F 01 C9" , "ext": "MONITOR"},
{"inst": "rdpmc W:<edx>, W:<eax>, R:<ecx>" , "op": "0F 33"},
{"inst": "rdmsr W:<edx>, W:<eax>, R:<ecx>" , "op": "0F 32" , "ext": "MSR" , "io": "MSR=R"},
{"inst": "rdmsrlist R:<mem(ds:rsi)>, W:<mem(ds:rdi)>, X:<rcx>" , "op": "F2 0F 01 C6" , "ext": "MSRLIST" , "arch": "X64", "io": "MSR=R"},
{"inst": "setssbsy" , "op": "F3 0F 01 E8" , "ext": "CET_SS"},
{"inst": "swapgs" , "op": "0F 01 F8" , "arch": "X64"},
{"inst": "sysexit" , "op": "0F 35"},
{"inst": "sysexitq" , "op": "REX.W 0F 35"},
{"inst": "sysret" , "op": "0F 07" , "arch": "X64"},
{"inst": "sysretq" , "op": "REX.W 0F 07" , "arch": "X64"},
{"inst": "wbinvd" , "op": "0F 09" , "ext": "I486"},
{"inst": "wbnoinvd" , "op": "F3 0F 09" , "ext": "WBNOINVD"},
{"inst": "wrmsrns R:<edx>, R:<eax>, R:<ecx>" , "op": "0F 01 C6" , "ext": "WRMSRNS" , "io": "MSR=W"},
{"inst": "wrmsr R:<edx>, R:<eax>, R:<ecx>" , "op": "0F 30" , "ext": "MSR" , "io": "MSR=W"},
{"inst": "wrmsrlist R:<mem(ds:rsi)>, R:<mem(ds:rdi)>, X:<rcx>" , "op": "F3 0F 01 C6" , "ext": "MSRLIST" , "arch": "X64", "io": "MSR=W"},
{"inst": "wrssd W:r32/m32, r32" , "op": "MR: 0F 38 F6 /r" , "ext": "CET_SS"},
{"inst": "wrssq W:r64/m64, r64" , "op": "MR: REX.W 0F 38 F6 /r" , "ext": "CET_SS"},
{"inst": "xrstors R:mem, <edx>, <eax>" , "op": "0F C7 /3" , "ext": "XSAVES" , "io": "XCR=R"},
{"inst": "xrstors64 R:mem, <edx>, <eax>" , "op": "REX.W 0F C7 /3" , "ext": "XSAVES" , "io": "XCR=R"},
{"inst": "wrussd W:r32/m32, r32" , "op": "MR: 66 0F 38 F5 /r" , "ext": "CET_SS"},
{"inst": "wrussq W:r64/m64, r64" , "op": "MR: REX.W 66 0F 38 F5 /r" , "ext": "CET_SS"},
{"inst": "xsaves W:mem, <edx>, <eax>" , "op": "0F C7 /5" , "ext": "XSAVES" , "io": "XCR=R"},
{"inst": "xsaves64 W:mem, <edx>, <eax>" , "op": "REX.W 0F C7 /5" , "ext": "XSAVES" , "io": "XCR=R"},
{"inst": "xsetbv R:<edx>, R:<eax>, R:<ecx>" , "op": "0F 01 D1" , "ext": "XSAVE" , "io": "XCR=W"}
{"inst": "sysretq" , "op": "REX.W 0F 07" , "arch": "X64"}
]},
{"category": "GP", "ext": "I486", "volatile": true, "privilege": "L0", "data": [
{"inst": "invd" , "op": "0F 08"},
{"inst": "invlpg R:mem" , "op": "0F 01 /7"},
{"inst": "invpcid R:r32, R:m128" , "op": "RM: 66 0F 38 82 /r" , "arch": "X86"},
{"inst": "invpcid R:r64, R:m128" , "op": "RM: 66 0F 38 82 /r" , "arch": "X64"},
{"inst": "wbinvd" , "op": "0F 09"}
]},
{"category": "GP GP_EXT", "ext": "CET_IBT", "volatile": true, "privilege": "L0", "data": [
{"inst": "endbr32" , "op": "F3 0F 1E FB"},
{"inst": "endbr64" , "op": "F3 0F 1E FA"}
]},
{"category": "GP GP_EXT", "ext": "CET_SS", "volatile": true, "privilege": "L0", "data": [
{"inst": "clrssbsy R:m64" , "op": "F3 0F AE /6" , "io": "OF=0 SF=0 ZF=0 AF=0 PF=0 CF=W"},
{"inst": "setssbsy" , "op": "F3 0F 01 E8"},
{"inst": "wrssd W:r32/m32, r32" , "op": "MR: 0F 38 F6 /r"},
{"inst": "wrssq W:r64/m64, r64" , "op": "MR: REX.W 0F 38 F6 /r"},
{"inst": "wrussd W:r32/m32, r32" , "op": "MR: 66 0F 38 F5 /r"},
{"inst": "wrussq W:r64/m64, r64" , "op": "MR: REX.W 66 0F 38 F5 /r"}
]},
{"category": "GP GP_EXT", "ext": "HRESET", "volatile": true, "privilege": "L0", "data": [
{"inst": "hreset ib/ub, W:<eax>" , "op": "F3 0F 3A F0 /0 ib"}
]},
{"category": "GP GP_EXT", "ext": "MONITOR", "volatile": true, "privilege": "L0", "data": [
{"inst": "monitor R:<mem(ds:zax)>, R:<ecx>, R:<edx>" , "op": "0F 01 C8"},
{"inst": "mwait R:<eax>, R:<ecx>" , "op": "0F 01 C9"}
]},
{"category": "GP GP_EXT", "ext": "MSR", "volatile": true, "privilege": "L0", "data": [
{"inst": "rdmsr W:<edx>, W:<eax>, R:<ecx>" , "op": "0F 32" , "io": "MSR=R"},
{"inst": "wrmsr R:<edx>, R:<eax>, R:<ecx>" , "op": "0F 30" , "io": "MSR=W"}
]},
{"category": "GP GP_EXT", "ext": "MSRLIST", "arch": "X64", "volatile": true, "privilege": "L0", "data": [
{"inst": "rdmsrlist R:<mem(ds:rsi)>, W:<mem(ds:rdi)>, X:<rcx>" , "op": "F2 0F 01 C6" , "io": "MSR=R"},
{"inst": "wrmsrlist R:<mem(ds:rsi)>, R:<mem(ds:rdi)>, X:<rcx>" , "op": "F3 0F 01 C6" , "io": "MSR=W"}
]},
{"category": "GP GP_EXT", "ext": "SMX", "volatile": true, "privilege": "L0", "data": [
{"inst": "getsec <eax>" , "op": "0F 37"}
]},
{"category": "GP GP_EXT", "ext": "WBNOINVD", "volatile": true, "privilege": "L0", "data": [
{"inst": "wbnoinvd" , "op": "F3 0F 09"}
]},
{"category": "GP GP_EXT", "ext": "WRMSRNS", "volatile": true, "privilege": "L0", "data": [
{"inst": "wrmsrns R:<edx>, R:<eax>, R:<ecx>" , "op": "0F 01 C6" , "io": "MSR=W"}
]},
{"category": "GP GP_EXT", "ext": "XSAVE", "volatile": true, "privilege": "L0", "data": [
{"inst": "xsetbv R:<edx>, R:<eax>, R:<ecx>" , "op": "0F 01 D1" , "io": "XCR=W"}
]},
{"category": "GP GP_EXT", "ext": "XSAVES", "volatile": true, "privilege": "L0", "data": [
{"inst": "xrstors R:mem, <edx>, <eax>" , "op": "0F C7 /3" , "io": "XCR=R"},
{"inst": "xrstors64 R:mem, <edx>, <eax>" , "op": "REX.W 0F C7 /3" , "io": "XCR=R"},
{"inst": "xsaves W:mem, <edx>, <eax>" , "op": "0F C7 /5" , "io": "XCR=R"},
{"inst": "xsaves64 W:mem, <edx>, <eax>" , "op": "REX.W 0F C7 /5" , "io": "XCR=R"}
]},
{"category": "VIRTUALIZATION", "volatile": true, "data": [
@ -1108,14 +1270,6 @@
{"inst": "fbstp W:m80bcd" , "op": "DF /6" , "io": "C0=U C1=W C2=U C3=U", "fpuStack": "pop"},
{"inst": "fchs" , "op": "D9 E0" , "io": "C0=U C1=0 C2=U C3=U"},
{"inst": "fclex" , "op": "9B DB E2" , "io": "C0=U C1=U C2=U C3=U"},
{"inst": "fcmovb st(i)" , "op": "DA C0+i" , "io": "C0=U C1=W C2=U C3=U CF=R" , "ext": "CMOV"},
{"inst": "fcmovbe st(i)" , "op": "DA D0+i" , "io": "C0=U C1=W C2=U C3=U CF=R ZF=R", "ext": "CMOV"},
{"inst": "fcmove st(i)" , "op": "DA C8+i" , "io": "C0=U C1=W C2=U C3=U ZF=R" , "ext": "CMOV"},
{"inst": "fcmovnb st(i)" , "op": "DB C0+i" , "io": "C0=U C1=W C2=U C3=U CF=R" , "ext": "CMOV"},
{"inst": "fcmovnbe st(i)" , "op": "DB D0+i" , "io": "C0=U C1=W C2=U C3=U CF=R ZF=R", "ext": "CMOV"},
{"inst": "fcmovne st(i)" , "op": "DB C8+i" , "io": "C0=U C1=W C2=U C3=U ZF=R" , "ext": "CMOV"},
{"inst": "fcmovnu st(i)" , "op": "DB D8+i" , "io": "C0=U C1=W C2=U C3=U PF=R" , "ext": "CMOV"},
{"inst": "fcmovu st(i)" , "op": "DA D8+i" , "io": "C0=U C1=W C2=U C3=U PF=R" , "ext": "CMOV"},
{"inst": "fcom" , "op": "D8 D1" , "io": "C0=W C1=0 C2=W C3=W"},
{"inst": "fcom R:m32fp" , "op": "D8 /2" , "io": "C0=W C1=0 C2=W C3=W"},
{"inst": "fcom R:m64fp" , "op": "DC /2" , "io": "C0=W C1=0 C2=W C3=W"},
@ -1164,9 +1318,6 @@
{"inst": "fistp W:m16int" , "op": "DF /3" , "io": "C0=U C1=W C2=U C3=U", "fpuStack": "pop"},
{"inst": "fistp W:m32int" , "op": "DB /3" , "io": "C0=U C1=W C2=U C3=U", "fpuStack": "pop"},
{"inst": "fistp W:m64int" , "op": "DF /7" , "io": "C0=U C1=W C2=U C3=U", "fpuStack": "pop"},
{"inst": "fisttp W:m16int" , "op": "DF /1" , "io": "C0=U C1=0 C2=U C3=U", "fpuStack": "pop", "ext": "SSE3"},
{"inst": "fisttp W:m32int" , "op": "DB /1" , "io": "C0=U C1=0 C2=U C3=U", "fpuStack": "pop", "ext": "SSE3"},
{"inst": "fisttp W:m64int" , "op": "DD /1" , "io": "C0=U C1=0 C2=U C3=U", "fpuStack": "pop", "ext": "SSE3"},
{"inst": "fisub R:m16int" , "op": "DE /4" , "io": "C0=U C1=W C2=U C3=U"},
{"inst": "fisub R:m32int" , "op": "DA /4" , "io": "C0=U C1=W C2=U C3=U"},
{"inst": "fisubr R:m16int" , "op": "DE /5" , "io": "C0=U C1=W C2=U C3=U"},
@ -1249,6 +1400,23 @@
{"inst": "fyl2xp1" , "op": "D9 F9" , "io": "C0=U C1=W C2=U C3=U", "fpuStack": "pop"}
]},
{"category": "FPU", "ext": "FPU CMOV", "data": [
{"inst": "fcmovb st(i)" , "op": "DA C0+i" , "io": "C0=U C1=W C2=U C3=U CF=R" },
{"inst": "fcmovbe st(i)" , "op": "DA D0+i" , "io": "C0=U C1=W C2=U C3=U CF=R ZF=R"},
{"inst": "fcmove st(i)" , "op": "DA C8+i" , "io": "C0=U C1=W C2=U C3=U ZF=R" },
{"inst": "fcmovnb st(i)" , "op": "DB C0+i" , "io": "C0=U C1=W C2=U C3=U CF=R" },
{"inst": "fcmovnbe st(i)" , "op": "DB D0+i" , "io": "C0=U C1=W C2=U C3=U CF=R ZF=R"},
{"inst": "fcmovne st(i)" , "op": "DB C8+i" , "io": "C0=U C1=W C2=U C3=U ZF=R" },
{"inst": "fcmovnu st(i)" , "op": "DB D8+i" , "io": "C0=U C1=W C2=U C3=U PF=R" },
{"inst": "fcmovu st(i)" , "op": "DA D8+i" , "io": "C0=U C1=W C2=U C3=U PF=R" }
]},
{"category": "FPU", "ext": "FPU SSE3", "data": [
{"inst": "fisttp W:m16int" , "op": "DF /1" , "io": "C0=U C1=0 C2=U C3=U", "fpuStack": "pop"},
{"inst": "fisttp W:m32int" , "op": "DB /1" , "io": "C0=U C1=0 C2=U C3=U", "fpuStack": "pop"},
{"inst": "fisttp W:m64int" , "op": "DD /1" , "io": "C0=U C1=0 C2=U C3=U", "fpuStack": "pop"}
]},
{"category": "MMX STATE", "deprecated": true, "volatile": true, "data": [
{"inst": "emms" , "op": "0F 77" , "ext": "MMX"},
{"inst": "femms" , "op": "0F 0E" , "ext": "3DNOW"}
@ -1649,7 +1817,7 @@
{"inst": "movntdqa W:xmm, m128" , "op": "RM: 66 0F 38 2A /r"},
{"inst": "mpsadbw X:xmm, xmm/m128, ib/ub" , "op": "RM: 66 0F 3A 42 /r ib"},
{"inst": "packusdw X:xmm, xmm/m128" , "op": "RM: 66 0F 38 2B /r"},
{"inst": "pblendvb X:xmm, xmm/m128, <xmm0>" , "op": "RM: 66 0F E0 /r"},
{"inst": "pblendvb X:xmm, xmm/m128, <xmm0>" , "op": "RM: 66 0F 38 10 /r"},
{"inst": "pblendw X:xmm, xmm/m128, ib/ub" , "op": "RM: 66 0F 3A 0E /r ib"},
{"inst": "pcmpeqq X:~xmm, ~xmm/m128" , "op": "RM: 66 0F 38 29 /r"},
{"inst": "pextrb W:r32[7:0]/m8, xmm, ib/ub" , "op": "MR: 66 0F 3A 14 /r ib"},
@ -1707,10 +1875,6 @@
{"inst": "insertq X:xmm, xmm, ib/ub, ib/ub" , "op": "RM: F2 0F 78 /r ib ib"}
]},
{"category": "SSE SIMD", "ext": "PCLMULQDQ", "data": [
{"inst": "pclmulqdq X:xmm, xmm/m128, ib/ub" , "op": "RM: 66 0F 3A 44 /r ib"}
]},
{"category": "SSE SIMD CRYPTO_HASH", "ext": "AESNI", "data": [
{"inst": "aesdec X:xmm, xmm/m128" , "op": "RM: 66 0F 38 DE /r"},
{"inst": "aesdeclast X:xmm, xmm/m128" , "op": "RM: 66 0F 38 DF /r"},
@ -1720,6 +1884,16 @@
{"inst": "aeskeygenassist W:xmm, xmm/m128, ib/ub" , "op": "RM: 66 0F 3A DF /r ib"}
]},
{"category": "SSE SIMD", "ext": "GFNI", "data": [
{"inst": "gf2p8affineinvqb X:xmm, xmm/m128, ib/ub" , "op": "RM: 66 0F 3A CF /r ib"},
{"inst": "gf2p8affineqb X:xmm, xmm/m128, ib/ub" , "op": "RM: 66 0F 3A CE /r ib"},
{"inst": "gf2p8mulb X:xmm, xmm/m128" , "op": "RM: 66 0F 38 CF /r"}
]},
{"category": "SSE SIMD", "ext": "PCLMULQDQ", "data": [
{"inst": "pclmulqdq X:xmm, xmm/m128, ib/ub" , "op": "RM: 66 0F 3A 44 /r ib"}
]},
{"category": "SSE SIMD CRYPTO_HASH", "ext": "SHA", "data": [
{"inst": "sha1msg1 xmm, xmm/m128" , "op": "RM: 0F 38 C9 /r"},
{"inst": "sha1msg2 xmm, xmm/m128" , "op": "RM: 0F 38 CA /r"},
@ -1730,12 +1904,6 @@
{"inst": "sha256rnds2 xmm, xmm/m128, <xmm0>" , "op": "RM: 0F 38 CB /r"}
]},
{"category": "SSE SIMD", "ext": "GFNI", "data": [
{"inst": "gf2p8affineinvqb X:xmm, xmm/m128, ib/ub" , "op": "RM: 66 0F 3A CF /r ib"},
{"inst": "gf2p8affineqb X:xmm, xmm/m128, ib/ub" , "op": "RM: 66 0F 3A CE /r ib"},
{"inst": "gf2p8mulb X:xmm, xmm/m128" , "op": "RM: 66 0F 38 CF /r"}
]},
{"category": "AVX STATE", "ext": "AVX", "data": [
{"inst": "vldmxcsr R:m32" , "op": "VEX.LZ.0F.WIG AE /2", "io": "MXCSR=W"},
{"inst": "vstmxcsr W:m32" , "op": "VEX.LZ.0F.WIG AE /3", "io": "MXCSR=R"},
@ -2626,7 +2794,7 @@
{"category": "AVX SIMD", "ext": "AVX SM3", "data": [
{"inst": "vsm3msg1 X:xmm, xmm, xmm/m128" , "op": "RVM: VEX.128.NP.0F38.W0 DA /r"},
{"inst": "vsm3msg2 X:xmm, xmm, xmm/m128" , "op": "RVM: VEX.128.66.0F38.W0 DA /r"},
{"inst": "vsm3rnds2 X:xmm, xmm, xmm/m128, ib/ub" , "op": "RVM: VEX.128.66.0F3A.W0 DE /r /ib"}
{"inst": "vsm3rnds2 X:xmm, xmm, xmm/m128, ib/ub" , "op": "RVM: VEX.128.66.0F3A.W0 DE /r ib"}
]},
{"category": "AVX SIMD", "ext": "AVX SM4", "data": [

View File

@ -1,7 +1,7 @@
// This file is part of AsmJit project <https://asmjit.com>
//
// See asmjit.h or LICENSE.md for license and copyright information
// SPDX-License-Identifier: Zlib
// SPDX-License-Identifier: (Zlib or Unlicense)
(function($scope, $as) {
"use strict";

View File

@ -26,16 +26,16 @@
//!
//! ### Register Operands
//!
//! - \ref arm::Reg - Base class for any AArch32/AArch64 register.
//! - \ref arm::Gp - General purpose register:
//! - \ref arm::GpW - 32-bit register.
//! - \ref arm::GpX - 64-bit register.
//! - \ref arm::Vec - Vector (SIMD) register:
//! - \ref arm::VecB - 8-bit SIMD register.
//! - \ref arm::VecH - 16-bit SIMD register.
//! - \ref arm::VecS - 32-bit SIMD register.
//! - \ref arm::VecD - 64-bit SIMD register.
//! - \ref arm::VecV - 128-bit SIMD register.
//! - \ref arm::Reg - Base class of all AArch32/AArch64 registers.
//! - \ref a64::Gp - General purpose register (AArch64):
//! - \ref a64::GpW - 32-bit general purpose register (AArch64).
//! - \ref a64::GpX - 64-bit general purpose register (AArch64).
//! - \ref a64::Vec - Vector (SIMD) register:
//! - \ref a64::VecB - 8-bit SIMD register.
//! - \ref a64::VecH - 16-bit SIMD register.
//! - \ref a64::VecS - 32-bit SIMD register.
//! - \ref a64::VecD - 64-bit SIMD register.
//! - \ref a64::VecV - 128-bit SIMD register.
//!
//! ### Memory Operands
//!

View File

@ -18,14 +18,16 @@
//!
//! ### Emitters
//!
//! - AArch64
//! - AArch32
//! - \ref a32::Assembler - AArch32 assembler (must read, provides examples).
//! - \ref a64::Assembler - AArch64 assembler (must read, provides examples).
//! - \ref a32::Builder - AArch32 builder.
//! - \ref a64::Builder - AArch64 builder.
//! - \ref a32::Compiler - AArch32 compiler.
//! - \ref a64::Compiler - AArch64 compiler.
//! - \ref a32::Emitter - AArch32 emitter (abstract).
//!
//! - AArch64
//! - \ref a64::Assembler - AArch64 assembler (must read, provides examples).
//! - \ref a64::Builder - AArch64 builder.
//! - \ref a64::Compiler - AArch64 compiler.
//! - \ref a64::Emitter - AArch64 emitter (abstract).
//!
//! ### Supported Instructions
@ -46,16 +48,22 @@
//!
//! ### Register Operands
//!
//! - \ref arm::Reg - Base class for any AArch32/AArch64 register.
//! - \ref arm::Gp - General purpose register:
//! - \ref arm::GpW - 32-bit register.
//! - \ref arm::GpX - 64-bit register (AArch64 only).
//! - \ref arm::Vec - Vector (SIMD) register:
//! - \ref arm::VecB - 8-bit SIMD register (AArch64 only).
//! - \ref arm::VecH - 16-bit SIMD register (AArch64 only).
//! - \ref arm::VecS - 32-bit SIMD register.
//! - \ref arm::VecD - 64-bit SIMD register.
//! - \ref arm::VecV - 128-bit SIMD register.
//! - \ref arm::Reg - Base class of all AArch32/AArch64 registers.
//! - \ref a32::Gp - 32-bit general purpose register used by AArch32:
//! - \ref a64::Gp - 32-bit or 64-bit general purpose register used by AArch64:
//! - \ref a64::GpW - 32-bit register (AArch64).
//! - \ref a64::GpX - 64-bit register (AArch64).
//! - \ref arm::BaseVec - Base vector (SIMD) register.
//! - \ref a32::Vec - Vector (SIMD) register (AArch32):
//! - \ref a32::VecS - 32-bit SIMD register (AArch32).
//! - \ref a32::VecD - 64-bit SIMD register (AArch32).
//! - \ref a32::VecV - 128-bit SIMD register (AArch32).
//! - \ref a64::Vec - Vector (SIMD) register (AArch64):
//! - \ref a64::VecB - 8-bit SIMD register (AArch64).
//! - \ref a64::VecH - 16-bit SIMD register (AArch64).
//! - \ref a64::VecS - 32-bit SIMD register (AArch64).
//! - \ref a64::VecD - 64-bit SIMD register (AArch64).
//! - \ref a64::VecV - 128-bit SIMD register (AArch64).
//!
//! ### Memory Operands
//!

View File

@ -21,12 +21,16 @@
ASMJIT_BEGIN_SUB_NAMESPACE(a64)
// a64::Assembler - Utils
// ======================
static ASMJIT_FORCE_INLINE constexpr uint32_t diff(RegType a, RegType b) noexcept { return uint32_t(a) - uint32_t(b); }
static ASMJIT_FORCE_INLINE constexpr uint32_t diff(VecElementType elementType, VecElementType baseType) noexcept { return uint32_t(elementType) - uint32_t(baseType); }
// a64::Assembler - Cond
// =====================
static inline uint32_t condCodeToOpcodeCond(uint32_t cond) noexcept {
return (uint32_t(cond) - 2u) & 0xFu;
}
static inline uint32_t condCodeToOpcodeCond(uint32_t cond) noexcept { return (uint32_t(cond) - 2u) & 0xFu; }
// a64::Assembler - Bits
// =====================
@ -49,10 +53,6 @@ static constexpr uint32_t kWX = InstDB::kWX;
static const uint8_t armShiftOpToLdStOptMap[] = { ASMJIT_LOOKUP_TABLE_16(VALUE, 0) };
#undef VALUE
static inline constexpr uint32_t diff(RegType a, RegType b) noexcept {
return uint32_t(a) - uint32_t(b);
}
// asmjit::a64::Assembler - SizeOp
// ===============================
@ -118,25 +118,25 @@ struct SizeOpTable {
};
#define VALUE_BIN(x) { \
x == (((uint32_t(RegType::kARM_VecD) - uint32_t(RegType::kARM_VecB)) << 3) | (Vec::kElementTypeNone)) ? SizeOp::k00 : \
x == (((uint32_t(RegType::kARM_VecV) - uint32_t(RegType::kARM_VecB)) << 3) | (Vec::kElementTypeNone)) ? SizeOp::k00Q : \
x == (((uint32_t(RegType::kARM_VecD) - uint32_t(RegType::kARM_VecB)) << 3) | (Vec::kElementTypeB )) ? SizeOp::k00 : \
x == (((uint32_t(RegType::kARM_VecV) - uint32_t(RegType::kARM_VecB)) << 3) | (Vec::kElementTypeB )) ? SizeOp::k00Q : SizeOp::kInvalid \
x == (((uint32_t(RegType::kARM_VecD) - uint32_t(RegType::kARM_VecB)) << 3) | uint32_t(VecElementType::kNone)) ? SizeOp::k00 : \
x == (((uint32_t(RegType::kARM_VecV) - uint32_t(RegType::kARM_VecB)) << 3) | uint32_t(VecElementType::kNone)) ? SizeOp::k00Q : \
x == (((uint32_t(RegType::kARM_VecD) - uint32_t(RegType::kARM_VecB)) << 3) | uint32_t(VecElementType::kB )) ? SizeOp::k00 : \
x == (((uint32_t(RegType::kARM_VecV) - uint32_t(RegType::kARM_VecB)) << 3) | uint32_t(VecElementType::kB )) ? SizeOp::k00Q : SizeOp::kInvalid \
}
#define VALUE_ANY(x) { \
x == (((uint32_t(RegType::kARM_VecB) - uint32_t(RegType::kARM_VecB)) << 3) | (Vec::kElementTypeNone)) ? SizeOp::k00S : \
x == (((uint32_t(RegType::kARM_VecH) - uint32_t(RegType::kARM_VecB)) << 3) | (Vec::kElementTypeNone)) ? SizeOp::k01S : \
x == (((uint32_t(RegType::kARM_VecS) - uint32_t(RegType::kARM_VecB)) << 3) | (Vec::kElementTypeNone)) ? SizeOp::k10S : \
x == (((uint32_t(RegType::kARM_VecD) - uint32_t(RegType::kARM_VecB)) << 3) | (Vec::kElementTypeNone)) ? SizeOp::k11S : \
x == (((uint32_t(RegType::kARM_VecD) - uint32_t(RegType::kARM_VecB)) << 3) | (Vec::kElementTypeB )) ? SizeOp::k00 : \
x == (((uint32_t(RegType::kARM_VecV) - uint32_t(RegType::kARM_VecB)) << 3) | (Vec::kElementTypeB )) ? SizeOp::k00Q : \
x == (((uint32_t(RegType::kARM_VecD) - uint32_t(RegType::kARM_VecB)) << 3) | (Vec::kElementTypeH )) ? SizeOp::k01 : \
x == (((uint32_t(RegType::kARM_VecV) - uint32_t(RegType::kARM_VecB)) << 3) | (Vec::kElementTypeH )) ? SizeOp::k01Q : \
x == (((uint32_t(RegType::kARM_VecD) - uint32_t(RegType::kARM_VecB)) << 3) | (Vec::kElementTypeS )) ? SizeOp::k10 : \
x == (((uint32_t(RegType::kARM_VecV) - uint32_t(RegType::kARM_VecB)) << 3) | (Vec::kElementTypeS )) ? SizeOp::k10Q : \
x == (((uint32_t(RegType::kARM_VecD) - uint32_t(RegType::kARM_VecB)) << 3) | (Vec::kElementTypeD )) ? SizeOp::k11S : \
x == (((uint32_t(RegType::kARM_VecV) - uint32_t(RegType::kARM_VecB)) << 3) | (Vec::kElementTypeD )) ? SizeOp::k11Q : SizeOp::kInvalid \
x == (((uint32_t(RegType::kARM_VecB) - uint32_t(RegType::kARM_VecB)) << 3) | uint32_t(VecElementType::kNone)) ? SizeOp::k00S : \
x == (((uint32_t(RegType::kARM_VecH) - uint32_t(RegType::kARM_VecB)) << 3) | uint32_t(VecElementType::kNone)) ? SizeOp::k01S : \
x == (((uint32_t(RegType::kARM_VecS) - uint32_t(RegType::kARM_VecB)) << 3) | uint32_t(VecElementType::kNone)) ? SizeOp::k10S : \
x == (((uint32_t(RegType::kARM_VecD) - uint32_t(RegType::kARM_VecB)) << 3) | uint32_t(VecElementType::kNone)) ? SizeOp::k11S : \
x == (((uint32_t(RegType::kARM_VecD) - uint32_t(RegType::kARM_VecB)) << 3) | uint32_t(VecElementType::kB )) ? SizeOp::k00 : \
x == (((uint32_t(RegType::kARM_VecV) - uint32_t(RegType::kARM_VecB)) << 3) | uint32_t(VecElementType::kB )) ? SizeOp::k00Q : \
x == (((uint32_t(RegType::kARM_VecD) - uint32_t(RegType::kARM_VecB)) << 3) | uint32_t(VecElementType::kH )) ? SizeOp::k01 : \
x == (((uint32_t(RegType::kARM_VecV) - uint32_t(RegType::kARM_VecB)) << 3) | uint32_t(VecElementType::kH )) ? SizeOp::k01Q : \
x == (((uint32_t(RegType::kARM_VecD) - uint32_t(RegType::kARM_VecB)) << 3) | uint32_t(VecElementType::kS )) ? SizeOp::k10 : \
x == (((uint32_t(RegType::kARM_VecV) - uint32_t(RegType::kARM_VecB)) << 3) | uint32_t(VecElementType::kS )) ? SizeOp::k10Q : \
x == (((uint32_t(RegType::kARM_VecD) - uint32_t(RegType::kARM_VecB)) << 3) | uint32_t(VecElementType::kD )) ? SizeOp::k11S : \
x == (((uint32_t(RegType::kARM_VecV) - uint32_t(RegType::kARM_VecB)) << 3) | uint32_t(VecElementType::kD )) ? SizeOp::k11Q : SizeOp::kInvalid \
}
static const SizeOpTable sizeOpTable[SizeOpTable::kCount] = {
@ -254,16 +254,16 @@ static const Operand_& significantSimdOp(const Operand_& o0, const Operand_& o1,
return !(instFlags & InstDB::kInstFlagLong) ? o0 : o1;
}
static inline SizeOp armElementTypeToSizeOp(uint32_t vecOpType, RegType regType, uint32_t elementType) noexcept {
static inline SizeOp armElementTypeToSizeOp(uint32_t vecOpType, RegType regType, VecElementType elementType) noexcept {
// Instruction data or Assembler is wrong if this triggers an assertion failure.
ASMJIT_ASSERT(vecOpType < InstDB::kVO_Count);
// ElementType uses 3 bits in the operand signature, it should never overflow.
ASMJIT_ASSERT(elementType <= 0x7u);
ASMJIT_ASSERT(uint32_t(elementType) <= 0x7u);
const SizeOpMap& map = sizeOpMap[vecOpType];
const SizeOpTable& table = sizeOpTable[map.tableId];
size_t index = (Support::min<uint32_t>(diff(regType, RegType::kARM_VecB), diff(RegType::kARM_VecV, RegType::kARM_VecB) + 1) << 3) | elementType;
size_t index = (Support::min<uint32_t>(diff(regType, RegType::kARM_VecB), diff(RegType::kARM_VecV, RegType::kARM_VecB) + 1) << 3) | uint32_t(elementType);
SizeOp op = table.array[index];
SizeOp modifiedOp { uint8_t(op.value & map.sizeOpMask) };
@ -467,7 +467,7 @@ static inline bool matchSignature(const Operand_& o0, const Operand_& o1, const
}
static inline bool matchSignature(const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3, uint32_t instFlags) noexcept {
return matchSignature(o0, o1, instFlags) && o1.signature() == o2.signature() && o2.signature() == o3.signature();;
return matchSignature(o0, o1, instFlags) && o1.signature() == o2.signature() && o2.signature() == o3.signature();
}
// Memory must be either:
@ -537,7 +537,7 @@ static inline bool pickFpOpcode(const Vec& reg, uint32_t sOp, uint32_t sHf, uint
else {
// Vector operation [HSD].
uint32_t q = diff(reg.type(), RegType::kARM_VecD);
uint32_t sz = reg.elementType() - Vec::kElementTypeH;
uint32_t sz = diff(reg.elementType(), VecElementType::kH);
if (q > 1u || sz > 2u || !Support::bitTest(szBits[vHf].sizeMask, sz))
return false;
@ -716,8 +716,6 @@ static inline bool checkValidRegs(const Operand_& o0, const Operand_& o1, const
Assembler::Assembler(CodeHolder* code) noexcept : BaseAssembler() {
_archMask = uint64_t(1) << uint32_t(Arch::kAArch64);
assignEmitterFuncs(this);
if (code)
code->attach(this);
}
@ -803,7 +801,7 @@ Error Assembler::_emit(InstId instId, const Operand_& o0, const Operand_& o1, co
Operand_ opArray[Globals::kMaxOpCount];
EmitterUtils::opArrayFromEmitArgs(opArray, o0, o1, o2, opExt);
err = _funcs.validate(arch(), BaseInst(instId, options, _extraReg), opArray, Globals::kMaxOpCount, ValidationFlags::kNone);
err = _funcs.validate(BaseInst(instId, options, _extraReg), opArray, Globals::kMaxOpCount, ValidationFlags::kNone);
if (ASMJIT_UNLIKELY(err))
goto Failed;
}
@ -2241,6 +2239,86 @@ Error Assembler::_emit(InstId instId, const Operand_& o0, const Operand_& o1, co
break;
}
// ------------------------------------------------------------------------
// [Base - Prefetch]
// ------------------------------------------------------------------------
case InstDB::kEncodingBasePrfm: {
const InstDB::EncodingData::BasePrfm& opData = InstDB::EncodingData::basePrfm[encodingIndex];
if (isign4 == ENC_OPS2(Imm, Mem)) {
const Mem& m = o1.as<Mem>();
rmRel = &m;
uint32_t immShift = 3u;
if (o0.as<Imm>().valueAs<uint64_t>() > 0x1Fu)
goto InvalidImmediate;
if (!armCheckMemBaseIndexRel(m))
goto InvalidAddress;
int64_t offset = m.offset();
uint32_t prfop = o0.as<Imm>().valueAs<uint32_t>();
if (m.hasBaseReg()) {
// [Base {Offset | Index}]
if (m.hasIndex()) {
uint32_t opt = armShiftOpToLdStOptMap[size_t(m.shiftOp())];
if (opt == 0xFF)
goto InvalidAddress;
uint32_t shift = m.shift();
uint32_t s = shift != 0;
if (s && shift != immShift)
goto InvalidAddressScale;
opcode.reset(uint32_t(opData.registerOp) << 21);
opcode.addImm(opt, 13);
opcode.addImm(s, 12);
opcode |= B(11);
opcode.addImm(prfop, 0);
goto EmitOp_MemBaseIndex_Rn5_Rm16;
}
if (!Support::isInt32(offset))
goto InvalidDisplacement;
int32_t offset32 = int32_t(offset);
if (m.isPreOrPost())
goto InvalidAddress;
uint32_t imm12 = uint32_t(offset32) >> immShift;
if (Support::isUInt12(imm12) && (imm12 << immShift) == uint32_t(offset32)) {
opcode.reset(uint32_t(opData.sOffsetOp) << 22);
opcode.addImm(imm12, 10);
opcode.addImm(prfop, 0);
goto EmitOp_MemBase_Rn5;
}
if (Support::isInt9(offset32)) {
opcode.reset(uint32_t(opData.uOffsetOp) << 21);
opcode.addImm(uint32_t(offset32) & 0x1FFu, 12);
opcode.addImm(prfop, 0);
goto EmitOp_MemBase_Rn5;
}
goto InvalidAddress;
}
else {
opcode.reset(uint32_t(opData.literalOp) << 24);
opcode.addImm(prfop, 0);
offsetFormat.resetToImmValue(OffsetType::kSignedOffset, 4, 5, 19, 2);
goto EmitOp_Rel;
}
}
break;
}
// ------------------------------------------------------------------------
// [Base - Load / Store]
// ------------------------------------------------------------------------
@ -2272,7 +2350,7 @@ Error Assembler::_emit(InstId instId, const Operand_& o0, const Operand_& o1, co
if (m.hasBaseReg()) {
// [Base {Offset | Index}]
if (m.hasIndex()) {
uint32_t opt = armShiftOpToLdStOptMap[m.predicate()];
uint32_t opt = armShiftOpToLdStOptMap[size_t(m.shiftOp())];
if (opt == 0xFF)
goto InvalidAddress;
@ -2690,7 +2768,7 @@ Case_BaseLdurStur:
// hD, vS.{4|8}h (16-bit)
// sD, vS.4s (32-bit)
uint32_t sz = diff(o0.as<Reg>().type(), RegType::kARM_VecH);
uint32_t elementSz = o1.as<Vec>().elementType() - Vec::kElementTypeH;
uint32_t elementSz = diff(o1.as<Vec>().elementType(), VecElementType::kH);
// Size greater than 1 means 64-bit elements, not supported.
if ((sz | elementSz) > 1 || sz != elementSz)
@ -2812,7 +2890,7 @@ Case_BaseLdurStur:
if (q > 1)
goto InvalidInstruction;
uint32_t sz = o0.as<Vec>().elementType() - Vec::kElementTypeB;
uint32_t sz = diff(o0.as<Vec>().elementType(), VecElementType::kB);
if (sz == 0 || sz > 3)
goto InvalidInstruction;
@ -2904,7 +2982,7 @@ Case_BaseLdurStur:
if (q > 1)
goto InvalidInstruction;
uint32_t sz = o0.as<Vec>().elementType() - Vec::kElementTypeB;
uint32_t sz = diff(o0.as<Vec>().elementType(), VecElementType::kB);
if (sz == 0 || sz > 3)
goto InvalidInstruction;
@ -3086,11 +3164,11 @@ Case_BaseLdurStur:
if (uint32_t(opcode.hasQ()) != q)
goto InvalidInstruction;
if (rL.isVecS4() && rN.elementType() == Vec::kElementTypeH && !opData.isCvtxn()) {
if (rL.isVecS4() && rN.elementType() == VecElementType::kH && !opData.isCvtxn()) {
goto EmitOp_Rd0_Rn5;
}
if (rL.isVecD2() && rN.elementType() == Vec::kElementTypeS) {
if (rL.isVecD2() && rN.elementType() == VecElementType::kS) {
opcode |= B(22);
goto EmitOp_Rd0_Rn5;
}
@ -3201,8 +3279,8 @@ Case_BaseLdurStur:
}
if (uint32_t(o0.as<Reg>().type()) != uint32_t(o1.as<Reg>().type()) + qIsOptional ||
o0.as<Vec>().elementType() != opData.tA ||
o1.as<Vec>().elementType() != opData.tB)
uint32_t(o0.as<Vec>().elementType()) != opData.tA ||
uint32_t(o1.as<Vec>().elementType()) != opData.tB)
goto InvalidInstruction;
if (!o2.as<Vec>().hasElementIndex()) {
@ -3214,7 +3292,7 @@ Case_BaseLdurStur:
goto EmitOp_Rd0_Rn5_Rm16;
}
else {
if (o2.as<Vec>().elementType() != opData.tElement)
if (uint32_t(o2.as<Vec>().elementType()) != opData.tElement)
goto InvalidInstruction;
if (o2.as<Reg>().id() > 15)
@ -3364,7 +3442,7 @@ Case_BaseLdurStur:
}
else {
uint32_t q = diff(o0.as<Vec>().type(), RegType::kARM_VecD);
uint32_t sz = o0.as<Vec>().elementType() - Vec::kElementTypeH;
uint32_t sz = diff(o0.as<Vec>().elementType(), VecElementType::kH);
if (q > 1 || sz > 2)
goto InvalidInstruction;
@ -3418,7 +3496,7 @@ Case_BaseLdurStur:
if (q > 1)
goto InvalidInstruction;
uint32_t sz = o0.as<Vec>().elementType() - Vec::kElementTypeH;
uint32_t sz = diff(o0.as<Vec>().elementType(), VecElementType::kH);
if (sz > 2)
goto InvalidInstruction;
@ -3442,7 +3520,7 @@ Case_BaseLdurStur:
if (isign4 == ENC_OPS2(Reg, Reg)) {
// The first destination operand is scalar, which matches element-type of source vectors.
uint32_t L = (instFlags & InstDB::kInstFlagLong) != 0;
if (diff(o0.as<Vec>().type(), RegType::kARM_VecB) != o1.as<Vec>().elementType() - Vec::kElementTypeB + L)
if (diff(o0.as<Vec>().type(), RegType::kARM_VecB) != diff(o1.as<Vec>().elementType(), VecElementType::kB) + L)
goto InvalidInstruction;
SizeOp sizeOp = armElementTypeToSizeOp(opData.vecOpType, o1.as<Reg>().type(), o1.as<Vec>().elementType());
@ -3542,7 +3620,7 @@ Case_BaseLdurStur:
if (!sizeOp.isValid())
goto InvalidInstruction;
if (!checkSignature(o0, o1) || !o0.as<Reg>().isVecV() || o0.as<Vec>().elementType() != o2.as<Vec>().elementType() + 1)
if (!checkSignature(o0, o1) || !o0.as<Reg>().isVecV() || uint32_t(o0.as<Vec>().elementType()) != uint32_t(o2.as<Vec>().elementType()) + 1u)
goto InvalidInstruction;
opcode.reset(opData.opcode());
@ -3827,9 +3905,9 @@ Case_BaseLdurStur:
if (o0.as<Reg>().type() != o1.as<Reg>().type() || o1.as<Reg>().type() != o2.as<Reg>().type())
goto InvalidInstruction;
if (o0.as<Vec>().elementType() != opData.tA ||
o1.as<Vec>().elementType() != opData.tB ||
o2.as<Vec>().elementType() != opData.tB)
if (uint32_t(o0.as<Vec>().elementType()) != opData.tA ||
uint32_t(o1.as<Vec>().elementType()) != opData.tB ||
uint32_t(o2.as<Vec>().elementType()) != opData.tB)
goto InvalidInstruction;
opcode.reset(uint32_t(opData.vectorOp) << 10);
@ -3843,9 +3921,9 @@ Case_BaseLdurStur:
if (o0.as<Reg>().type() != o1.as<Reg>().type() || !o2.as<Reg>().isVecV())
goto InvalidInstruction;
if (o0.as<Vec>().elementType() != opData.tA ||
o1.as<Vec>().elementType() != opData.tB ||
o2.as<Vec>().elementType() != opData.tElement)
if (uint32_t(o0.as<Vec>().elementType()) != opData.tA ||
uint32_t(o1.as<Vec>().elementType()) != opData.tB ||
uint32_t(o2.as<Vec>().elementType()) != opData.tElement)
goto InvalidInstruction;
uint32_t elementIndex = o2.as<Vec>().elementIndex();
@ -3871,13 +3949,13 @@ Case_BaseLdurStur:
case InstDB::kEncodingSimdDup: SimdDup: {
if (isign4 == ENC_OPS2(Reg, Reg)) {
// Truth table of valid encodings of `Q:1|ElementType:3`
uint32_t kValidEncodings = B(Vec::kElementTypeB + 0) |
B(Vec::kElementTypeH + 0) |
B(Vec::kElementTypeS + 0) |
B(Vec::kElementTypeB + 8) |
B(Vec::kElementTypeH + 8) |
B(Vec::kElementTypeS + 8) |
B(Vec::kElementTypeD + 8) ;
uint32_t kValidEncodings = B(uint32_t(VecElementType::kB) + 0) |
B(uint32_t(VecElementType::kH) + 0) |
B(uint32_t(VecElementType::kS) + 0) |
B(uint32_t(VecElementType::kB) + 8) |
B(uint32_t(VecElementType::kH) + 8) |
B(uint32_t(VecElementType::kS) + 8) |
B(uint32_t(VecElementType::kD) + 8) ;
uint32_t q = diff(o0.as<Reg>().type(), RegType::kARM_VecD);
@ -3886,7 +3964,7 @@ Case_BaseLdurStur:
//
// NOTE: This is only scalar for `dup d, x` case, otherwise the value
// would be duplicated across all vector elements (1, 2, 4, 8, or 16).
uint32_t elementType = o0.as<Vec>().elementType();
uint32_t elementType = uint32_t(o0.as<Vec>().elementType());
if (q > 1 || !Support::bitTest(kValidEncodings, (q << 3) | elementType))
goto InvalidInstruction;
@ -3907,7 +3985,7 @@ Case_BaseLdurStur:
// DUP - Vec (scalar) <- Vec[N].
uint32_t lsbIndex = diff(o0.as<Reg>().type(), RegType::kARM_VecB);
if (lsbIndex != o1.as<Vec>().elementType() - Vec::kElementTypeB || lsbIndex > 3)
if (lsbIndex != diff(o1.as<Vec>().elementType(), VecElementType::kB) || lsbIndex > 3)
goto InvalidInstruction;
uint32_t imm5 = ((dstIndex << 1) | 1u) << lsbIndex;
@ -3920,7 +3998,7 @@ Case_BaseLdurStur:
}
else {
// DUP - Vec (all) <- Vec[N].
uint32_t elementType = o0.as<Vec>().elementType();
uint32_t elementType = uint32_t(o0.as<Vec>().elementType());
if (q > 1 || !Support::bitTest(kValidEncodings, (q << 3) | elementType))
goto InvalidInstruction;
@ -3945,7 +4023,7 @@ Case_BaseLdurStur:
if (!o0.as<Vec>().hasElementIndex())
goto InvalidInstruction;
uint32_t elementType = o0.as<Vec>().elementType();
uint32_t elementType = uint32_t(o0.as<Vec>().elementType());
uint32_t dstIndex = o0.as<Vec>().elementIndex();
uint32_t lsbIndex = elementType - 1u;
@ -4114,7 +4192,6 @@ Case_BaseLdurStur:
if (inverted) {
imm8 = ~imm8 & 0xFFu;
inverted = 0;
}
cmode = B(3) | B(2) | B(1);
@ -4147,7 +4224,6 @@ Case_BaseLdurStur:
case 3:
if (inverted) {
imm8 = ~imm8 & 0xFFu;
inverted = 0;
}
op = 1;
@ -4435,7 +4511,7 @@ Case_BaseLdurStur:
if (m.hasBaseReg()) {
// [Base {Offset | Index}]
if (m.hasIndex()) {
uint32_t opt = armShiftOpToLdStOptMap[m.predicate()];
uint32_t opt = armShiftOpToLdStOptMap[size_t(m.shiftOp())];
if (opt == 0xFFu)
goto InvalidAddress;
@ -4659,7 +4735,7 @@ Case_SimdLdurStur:
uint32_t q = 0;
uint32_t rm = 0;
uint32_t rn = m.baseId();
uint32_t sz = v.elementType() - Vec::kElementTypeB;
uint32_t sz = diff(v.elementType(), VecElementType::kB);
uint32_t opcSsize = sz;
uint32_t offsetPossibility = 0;
@ -5091,6 +5167,10 @@ Error Assembler::align(AlignMode alignMode, uint32_t alignment) {
Error Assembler::onAttach(CodeHolder* code) noexcept {
ASMJIT_PROPAGATE(Base::onAttach(code));
_instructionAlignment = uint8_t(4);
assignEmitterFuncs(this);
return kErrorOk;
}

View File

@ -23,7 +23,7 @@ class ASMJIT_VIRTAPI Assembler
public:
typedef BaseAssembler Base;
//! \name Construction / Destruction
//! \name Construction & Destruction
//! \{
ASMJIT_API Assembler(CodeHolder* code = nullptr) noexcept;
@ -31,17 +31,6 @@ public:
//! \}
//! \name Accessors
//! \{
//! Gets whether the current ARM mode is THUMB (alternative to 32-bit ARM encoding).
ASMJIT_INLINE_NODEBUG bool isInThumbMode() const noexcept { return _environment.isArchThumb(); }
//! Gets the current code alignment of the current mode (ARM vs THUMB).
ASMJIT_INLINE_NODEBUG uint32_t codeAlignment() const noexcept { return isInThumbMode() ? 2 : 4; }
//! \}
//! \name Emit
//! \{

View File

@ -17,8 +17,6 @@ ASMJIT_BEGIN_SUB_NAMESPACE(a64)
Builder::Builder(CodeHolder* code) noexcept : BaseBuilder() {
_archMask = uint64_t(1) << uint32_t(Arch::kAArch64);
assignEmitterFuncs(this);
if (code)
code->attach(this);
}
@ -28,13 +26,19 @@ Builder::~Builder() noexcept {}
// =====================
Error Builder::onAttach(CodeHolder* code) noexcept {
return Base::onAttach(code);
ASMJIT_PROPAGATE(Base::onAttach(code));
_instructionAlignment = uint8_t(4);
assignEmitterFuncs(this);
return kErrorOk;
}
Error Builder::onDetach(CodeHolder* code) noexcept {
return Base::onDetach(code);
}
// a64::Builder - Finalize
// =======================

View File

@ -18,8 +18,6 @@ ASMJIT_BEGIN_SUB_NAMESPACE(a64)
Compiler::Compiler(CodeHolder* code) noexcept : BaseCompiler() {
_archMask = uint64_t(1) << uint32_t(Arch::kAArch64);
assignEmitterFuncs(this);
if (code)
code->attach(this);
}
@ -37,6 +35,9 @@ Error Compiler::onAttach(CodeHolder* code) noexcept {
return err;
}
_instructionAlignment = uint8_t(4);
assignEmitterFuncs(this);
return kErrorOk;
}

View File

@ -45,20 +45,27 @@ public:
return reg;
}
template<typename RegT, typename Type>
ASMJIT_INLINE_NODEBUG RegT _newRegInternal(const Type& type, const char* s) {
#ifndef ASMJIT_NO_LOGGING
RegT reg(Globals::NoInit);
_newReg(&reg, type, s);
return reg;
#else
DebugUtils::unused(s);
return _newRegInternal<RegT>(type);
#endif
}
template<typename RegT, typename Type, typename... Args>
ASMJIT_INLINE_NODEBUG RegT _newRegInternal(const Type& type, const char* s, Args&&... args) {
#ifndef ASMJIT_NO_LOGGING
RegT reg(Globals::NoInit);
if (sizeof...(Args) == 0)
_newReg(&reg, type, s);
else
_newRegFmt(&reg, type, s, std::forward<Args>(args)...);
return reg;
#else
DebugUtils::unused(s, std::forward<Args>(args)...);
RegT reg(Globals::NoInit);
_newReg(&reg, type, nullptr);
return reg;
return _newRegInternal<RegT>(type);
#endif
}
//! \endcond

View File

@ -169,7 +169,7 @@ Error EmitHelper::emitArgMove(
if (TypeUtils::isInt(dstTypeId)) {
if (TypeUtils::isInt(srcTypeId)) {
uint32_t x = dstSize == 8;
uint32_t x = uint32_t(dstSize == 8);
dst.setSignature(OperandSignature{x ? uint32_t(GpX::kSignature) : uint32_t(GpW::kSignature)});
_emitter->setInlineComment(comment);
@ -186,7 +186,7 @@ Error EmitHelper::emitArgMove(
case TypeId::kInt16: instId = Inst::kIdLdrsh; break;
case TypeId::kUInt16: instId = Inst::kIdLdrh; break;
case TypeId::kInt32: instId = x ? Inst::kIdLdrsw : Inst::kIdLdr; break;
case TypeId::kUInt32: instId = Inst::kIdLdr; x = 0; break;
case TypeId::kUInt32: instId = Inst::kIdLdr; break;
case TypeId::kInt64: instId = Inst::kIdLdr; break;
case TypeId::kUInt64: instId = Inst::kIdLdr; break;
default:
@ -312,6 +312,12 @@ ASMJIT_FAVOR_SIZE Error EmitHelper::emitProlog(const FuncFrame& frame) {
{ Inst::kIdStr_v, Inst::kIdStp_v }
}};
// Emit: 'bti' (indirect branch protection).
if (frame.hasIndirectBranchProtection()) {
// TODO: The instruction is not available at the moment (would be ABI break).
// ASMJIT_PROPAGATE(emitter->bti());
}
uint32_t adjustInitialOffset = pei.sizeTotal;
for (RegGroup group : Support::EnumValues<RegGroup, RegGroup::kGp, RegGroup::kVec>{}) {
@ -339,7 +345,7 @@ ASMJIT_FAVOR_SIZE Error EmitHelper::emitProlog(const FuncFrame& frame) {
else
ASMJIT_PROPAGATE(emitter->emit(insts.pairInstId, regs[0], regs[1], mem));
mem.resetToFixedOffset();
mem.resetOffsetMode();
if (i == 0 && frame.hasPreservedFP()) {
ASMJIT_PROPAGATE(emitter->mov(x29, sp));
@ -421,7 +427,7 @@ ASMJIT_FAVOR_SIZE Error EmitHelper::emitEpilog(const FuncFrame& frame) {
else
ASMJIT_PROPAGATE(emitter->emit(insts.pairInstId, regs[0], regs[1], mem));
mem.resetToFixedOffset();
mem.resetOffsetMode();
}
}

View File

@ -71,7 +71,7 @@ ASMJIT_BEGIN_SUB_NAMESPACE(a64)
//! ARM emitter.
//!
//! NOTE: This class cannot be instantiated, you can only cast to it and use it as emitter that emits to either
//! \ref Assembler, \ref Builder, or \ref Compiler (use withcaution with \ref Compiler as it expects virtual
//! \ref Assembler, \ref Builder, or \ref Compiler (use with caution with \ref Compiler as it expects virtual
//! registers to be used).
template<typename This>
struct EmitterExplicitT {
@ -79,11 +79,22 @@ struct EmitterExplicitT {
// These two are unfortunately reported by the sanitizer. We know what we do, however, the sanitizer doesn't.
// I have tried to use reinterpret_cast instead, but that would generate bad code when compiled by MSC.
ASMJIT_ATTRIBUTE_NO_SANITIZE_UNDEF inline This* _emitter() noexcept { return static_cast<This*>(this); }
ASMJIT_ATTRIBUTE_NO_SANITIZE_UNDEF inline const This* _emitter() const noexcept { return static_cast<const This*>(this); }
ASMJIT_ATTRIBUTE_NO_SANITIZE_UNDEF ASMJIT_INLINE_NODEBUG This* _emitter() noexcept { return static_cast<This*>(this); }
ASMJIT_ATTRIBUTE_NO_SANITIZE_UNDEF ASMJIT_INLINE_NODEBUG const This* _emitter() const noexcept { return static_cast<const This*>(this); }
//! \endcond
//! \name Native Registers
//! \{
//! Returns either 32-bit or 64-bit GP register of the given `id` depending on the emitter's architecture.
inline Gp gpz(uint32_t id) const noexcept { return Gp(_emitter()->_gpSignature, id); }
//! Clones the given `reg` to either 32-bit or 64-bit GP register depending on the emitter's architecture.
inline Gp gpz(const Gp& reg) const noexcept { return Gp(_emitter()->_gpSignature, reg.id()); }
//! \}
//! \name General Purpose Instructions
//! \{
@ -514,6 +525,8 @@ struct EmitterExplicitT {
ASMJIT_INST_2x(ldxrb, Ldxrb, Gp, Mem)
ASMJIT_INST_2x(ldxrh, Ldxrh, Gp, Mem)
ASMJIT_INST_2x(prfm, Prfm, Imm, Mem)
ASMJIT_INST_2x(stadd, Stadd, Gp, Mem)
ASMJIT_INST_2x(staddb, Staddb, Gp, Mem)
ASMJIT_INST_2x(staddh, Staddh, Gp, Mem)

View File

@ -29,12 +29,10 @@ ASMJIT_FAVOR_SIZE Error FormatterInternal::formatInstruction(
Arch arch,
const BaseInst& inst, const Operand_* operands, size_t opCount) noexcept {
DebugUtils::unused(arch);
// Format instruction options and instruction mnemonic.
InstId instId = inst.realId();
if (instId < Inst::_kIdCount)
ASMJIT_PROPAGATE(InstInternal::instIdToString(arch, instId, sb));
if (instId != Inst::kIdNone && instId < Inst::_kIdCount)
ASMJIT_PROPAGATE(InstInternal::instIdToString(instId, sb));
else
ASMJIT_PROPAGATE(sb.appendFormat("[InstId=#%u]", unsigned(instId)));

View File

@ -41,12 +41,13 @@ static RegType regTypeFromFpOrVecTypeId(TypeId typeId) noexcept {
ASMJIT_FAVOR_SIZE Error initCallConv(CallConv& cc, CallConvId ccId, const Environment& environment) noexcept {
cc.setArch(environment.arch());
cc.setStrategy(environment.isDarwin() ? CallConvStrategy::kAArch64Apple : CallConvStrategy::kDefault);
cc.setSaveRestoreRegSize(RegGroup::kGp, 8);
cc.setSaveRestoreRegSize(RegGroup::kVec, 8);
cc.setSaveRestoreAlignment(RegGroup::kGp, 16);
cc.setSaveRestoreAlignment(RegGroup::kVec, 16);
cc.setSaveRestoreAlignment(RegGroup::kExtraVirt2, 1);
cc.setSaveRestoreAlignment(RegGroup::kMask, 1);
cc.setSaveRestoreAlignment(RegGroup::kExtraVirt3, 1);
cc.setPassedOrder(RegGroup::kGp, 0, 1, 2, 3, 4, 5, 6, 7);
cc.setPassedOrder(RegGroup::kVec, 0, 1, 2, 3, 4, 5, 6, 7);
@ -68,7 +69,7 @@ ASMJIT_FAVOR_SIZE Error initCallConv(CallConv& cc, CallConvId ccId, const Enviro
return kErrorOk;
}
ASMJIT_FAVOR_SIZE Error initFuncDetail(FuncDetail& func, const FuncSignature& signature, uint32_t registerSize) noexcept {
ASMJIT_FAVOR_SIZE Error initFuncDetail(FuncDetail& func, const FuncSignature& signature) noexcept {
DebugUtils::unused(signature);
const CallConv& cc = func.callConv();
@ -77,6 +78,13 @@ ASMJIT_FAVOR_SIZE Error initFuncDetail(FuncDetail& func, const FuncSignature& si
uint32_t i;
uint32_t argCount = func.argCount();
// Minimum stack size of a single argument passed via stack. The standard AArch64 calling convention
// specifies 8 bytes, so each function argument would occupy at least 8 bytes even if it needs less.
// However, Apple has decided to not follow this rule and function argument can occupy less, for
// example two consecutive 32-bit arguments would occupy 8 bytes total, instead of 16 as specified
// by ARM.
uint32_t minStackArgSize = cc.strategy() == CallConvStrategy::kAArch64Apple ? 4u : 8u;
if (func.hasRet()) {
for (uint32_t valueIndex = 0; valueIndex < Globals::kMaxValuePack; valueIndex++) {
TypeId typeId = func._rets[valueIndex].typeId();
@ -119,7 +127,8 @@ ASMJIT_FAVOR_SIZE Error initFuncDetail(FuncDetail& func, const FuncSignature& si
}
switch (cc.strategy()) {
case CallConvStrategy::kDefault: {
case CallConvStrategy::kDefault:
case CallConvStrategy::kAArch64Apple: {
uint32_t gpzPos = 0;
uint32_t vecPos = 0;
@ -140,7 +149,9 @@ ASMJIT_FAVOR_SIZE Error initFuncDetail(FuncDetail& func, const FuncSignature& si
gpzPos++;
}
else {
uint32_t size = Support::max<uint32_t>(TypeUtils::sizeOf(typeId), registerSize);
uint32_t size = Support::max<uint32_t>(TypeUtils::sizeOf(typeId), minStackArgSize);
if (size >= 8)
stackOffset = Support::alignUp(stackOffset, 8);
arg.assignStackOffset(int32_t(stackOffset));
stackOffset += size;
}
@ -164,7 +175,9 @@ ASMJIT_FAVOR_SIZE Error initFuncDetail(FuncDetail& func, const FuncSignature& si
vecPos++;
}
else {
uint32_t size = TypeUtils::sizeOf(typeId);
uint32_t size = Support::max<uint32_t>(TypeUtils::sizeOf(typeId), minStackArgSize);
if (size >= 8)
stackOffset = Support::alignUp(stackOffset, 8);
arg.assignStackOffset(int32_t(stackOffset));
stackOffset += size;
}
@ -178,7 +191,7 @@ ASMJIT_FAVOR_SIZE Error initFuncDetail(FuncDetail& func, const FuncSignature& si
return DebugUtils::errored(kErrorInvalidState);
}
func._argStackSize = stackOffset;
func._argStackSize = Support::alignUp(stackOffset, 8u);
return kErrorOk;
}

View File

@ -21,7 +21,7 @@ namespace FuncInternal {
Error initCallConv(CallConv& cc, CallConvId ccId, const Environment& environment) noexcept;
//! Initialize `FuncDetail` (AArch64 specific).
Error initFuncDetail(FuncDetail& func, const FuncSignature& signature, uint32_t registerSize) noexcept;
Error initFuncDetail(FuncDetail& func, const FuncSignature& signature) noexcept;
} // {FuncInternal}

View File

@ -15,9 +15,6 @@
ASMJIT_BEGIN_SUB_NAMESPACE(a64)
// a64 uses everything from arm namespace and adds into it.
using namespace arm;
//! \addtogroup asmjit_a64
//! \{
@ -293,6 +290,7 @@ struct Inst {
kIdPacdza, //!< Instruction 'pacdza'.
kIdPacdzb, //!< Instruction 'pacdzb'.
kIdPacga, //!< Instruction 'pacga'.
kIdPrfm, //!< Instruction 'prfm'.
kIdPssbb, //!< Instruction 'pssbb'.
kIdRbit, //!< Instruction 'rbit'.
kIdRet, //!< Instruction 'ret'.

View File

@ -15,65 +15,22 @@
ASMJIT_BEGIN_SUB_NAMESPACE(a64)
namespace InstInternal {
// a64::InstInternal - Text
// ========================
#ifndef ASMJIT_NO_TEXT
Error InstInternal::instIdToString(Arch arch, InstId instId, String& output) noexcept {
Error instIdToString(InstId instId, String& output) noexcept {
uint32_t realId = instId & uint32_t(InstIdParts::kRealId);
DebugUtils::unused(arch);
if (ASMJIT_UNLIKELY(!Inst::isDefinedId(realId)))
return DebugUtils::errored(kErrorInvalidInstruction);
char nameData[32];
size_t nameSize = Support::decodeInstName(nameData, InstDB::_instNameIndexTable[realId], InstDB::_instNameStringTable);
return output.append(nameData, nameSize);
return InstNameUtils::decode(output, InstDB::_instNameIndexTable[realId], InstDB::_instNameStringTable);
}
InstId InstInternal::stringToInstId(Arch arch, const char* s, size_t len) noexcept {
DebugUtils::unused(arch);
if (ASMJIT_UNLIKELY(!s))
return Inst::kIdNone;
if (len == SIZE_MAX)
len = strlen(s);
if (ASMJIT_UNLIKELY(len == 0 || len > InstDB::kMaxNameSize))
return Inst::kIdNone;
uint32_t prefix = uint32_t(s[0]) - 'a';
if (ASMJIT_UNLIKELY(prefix > 'z' - 'a'))
return Inst::kIdNone;
size_t base = InstDB::instNameIndex[prefix].start;
size_t end = InstDB::instNameIndex[prefix].end;
if (ASMJIT_UNLIKELY(!base))
return Inst::kIdNone;
char nameData[32];
for (size_t lim = end - base; lim != 0; lim >>= 1) {
size_t instId = base + (lim >> 1);
size_t nameSize = Support::decodeInstName(nameData, InstDB::_instNameIndexTable[instId], InstDB::_instNameStringTable);
int result = Support::compareStringViews(s, len, nameData, nameSize);
if (result < 0)
continue;
if (result > 0) {
base = instId + 1;
lim--;
continue;
}
return InstId(instId);
}
return Inst::kIdNone;
InstId stringToInstId(const char* s, size_t len) noexcept {
return InstNameUtils::find(s, len, InstDB::instNameIndex, InstDB::_instNameIndexTable, InstDB::_instNameStringTable);
}
#endif // !ASMJIT_NO_TEXT
@ -81,9 +38,9 @@ InstId InstInternal::stringToInstId(Arch arch, const char* s, size_t len) noexce
// ============================
#ifndef ASMJIT_NO_VALIDATION
ASMJIT_FAVOR_SIZE Error InstInternal::validate(Arch arch, const BaseInst& inst, const Operand_* operands, size_t opCount, ValidationFlags validationFlags) noexcept {
ASMJIT_FAVOR_SIZE Error validate(const BaseInst& inst, const Operand_* operands, size_t opCount, ValidationFlags validationFlags) noexcept {
// TODO:
DebugUtils::unused(arch, inst, operands, opCount, validationFlags);
DebugUtils::unused(inst, operands, opCount, validationFlags);
return kErrorOk;
}
#endif // !ASMJIT_NO_VALIDATION
@ -127,13 +84,7 @@ static const InstRWInfoData instRWInfoData[] = {
static const uint8_t elementTypeSize[8] = { 0, 1, 2, 4, 8, 4, 4, 0 };
Error InstInternal::queryRWInfo(Arch arch, const BaseInst& inst, const Operand_* operands, size_t opCount, InstRWInfo* out) noexcept {
// Unused in Release configuration as the assert is not compiled in.
DebugUtils::unused(arch);
// Only called when `arch` matches X86 family.
ASMJIT_ASSERT(Environment::isFamilyARM(arch));
Error queryRWInfo(const BaseInst& inst, const Operand_* operands, size_t opCount, InstRWInfo* out) noexcept {
// Get the instruction data.
uint32_t realId = inst.id() & uint32_t(InstIdParts::kRealId);
@ -223,10 +174,10 @@ Error InstInternal::queryRWInfo(Arch arch, const BaseInst& inst, const Operand_*
if (srcOp.isReg()) {
if (srcOp.as<Vec>().hasElementIndex()) {
// Only part of the vector is accessed if element index [] is used.
uint32_t elementType = srcOp.as<Vec>().elementType();
VecElementType elementType = srcOp.as<Vec>().elementType();
uint32_t elementIndex = srcOp.as<Vec>().elementIndex();
uint32_t elementSize = elementTypeSize[elementType];
uint32_t elementSize = elementTypeSize[size_t(elementType)];
uint64_t accessMask = uint64_t(Support::lsbMask<uint32_t>(elementSize)) << (elementIndex * elementSize);
op._readByteMask &= accessMask;
@ -243,8 +194,7 @@ Error InstInternal::queryRWInfo(Arch arch, const BaseInst& inst, const Operand_*
}
if (memOp.hasIndex()) {
op.addOpFlags(OpRWFlags::kMemIndexRead);
op.addOpFlags(memOp.isPreOrPost() ? OpRWFlags::kMemIndexWrite : OpRWFlags::kNone);
op.addOpFlags(memOp.isPreOrPost() ? OpRWFlags::kMemIndexRW : OpRWFlags::kMemIndexRead);
}
}
}
@ -258,13 +208,15 @@ Error InstInternal::queryRWInfo(Arch arch, const BaseInst& inst, const Operand_*
// =================================
#ifndef ASMJIT_NO_INTROSPECTION
Error InstInternal::queryFeatures(Arch arch, const BaseInst& inst, const Operand_* operands, size_t opCount, CpuFeatures* out) noexcept {
Error queryFeatures(const BaseInst& inst, const Operand_* operands, size_t opCount, CpuFeatures* out) noexcept {
// TODO: [ARM] QueryFeatures not implemented yet.
DebugUtils::unused(arch, inst, operands, opCount, out);
DebugUtils::unused(inst, operands, opCount, out);
return kErrorOk;
}
#endif // !ASMJIT_NO_INTROSPECTION
} // {InstInternal}
// a64::InstInternal - Unit
// ========================

View File

@ -18,17 +18,17 @@ ASMJIT_BEGIN_SUB_NAMESPACE(a64)
namespace InstInternal {
#ifndef ASMJIT_NO_TEXT
Error ASMJIT_CDECL instIdToString(Arch arch, InstId instId, String& output) noexcept;
InstId ASMJIT_CDECL stringToInstId(Arch arch, const char* s, size_t len) noexcept;
Error ASMJIT_CDECL instIdToString(InstId instId, String& output) noexcept;
InstId ASMJIT_CDECL stringToInstId(const char* s, size_t len) noexcept;
#endif // !ASMJIT_NO_TEXT
#ifndef ASMJIT_NO_VALIDATION
Error ASMJIT_CDECL validate(Arch arch, const BaseInst& inst, const Operand_* operands, size_t opCount, ValidationFlags validationFlags) noexcept;
Error ASMJIT_CDECL validate(const BaseInst& inst, const Operand_* operands, size_t opCount, ValidationFlags validationFlags) noexcept;
#endif // !ASMJIT_NO_VALIDATION
#ifndef ASMJIT_NO_INTROSPECTION
Error ASMJIT_CDECL queryRWInfo(Arch arch, const BaseInst& inst, const Operand_* operands, size_t opCount, InstRWInfo* out) noexcept;
Error ASMJIT_CDECL queryFeatures(Arch arch, const BaseInst& inst, const Operand_* operands, size_t opCount, CpuFeatures* out) noexcept;
Error ASMJIT_CDECL queryRWInfo(const BaseInst& inst, const Operand_* operands, size_t opCount, InstRWInfo* out) noexcept;
Error ASMJIT_CDECL queryFeatures(const BaseInst& inst, const Operand_* operands, size_t opCount, CpuFeatures* out) noexcept;
#endif // !ASMJIT_NO_INTROSPECTION
} // {InstInternal}

File diff suppressed because it is too large Load Diff

View File

@ -29,7 +29,7 @@ enum InstFlags : uint32_t {
//! SIMD element access of half-words can only be used with v0..15.
kInstFlagVH0_15 = 0x00000010u,
//! Instruction may consecutive registers if the number of operands is greater than 2.
//! Instruction uses consecutive registers if the number of operands is greater than 2.
kInstFlagConsecutive = 0x00000080u
};

View File

@ -7,6 +7,7 @@
#define ASMJIT_ARM_A64INSTDB_H_P_INCLUDED
#include "../core/codeholder.h"
#include "../core/instdb_p.h"
#include "../arm/a64instdb.h"
#include "../arm/a64operand.h"
@ -58,14 +59,14 @@ enum RWInfoType : uint32_t {
// a64::InstDB - ElementType
// =========================
enum ElementType : uint8_t {
kET_None = Vec::kElementTypeNone,
kET_B = Vec::kElementTypeB,
kET_H = Vec::kElementTypeH,
kET_S = Vec::kElementTypeS,
kET_D = Vec::kElementTypeD,
kET_2H = Vec::kElementTypeH2,
kET_4B = Vec::kElementTypeB4
enum InstElementType : uint8_t {
kET_None = uint8_t(VecElementType::kNone),
kET_B = uint8_t(VecElementType::kB),
kET_H = uint8_t(VecElementType::kH),
kET_S = uint8_t(VecElementType::kS),
kET_D = uint8_t(VecElementType::kD),
kET_2H = uint8_t(VecElementType::kH2),
kET_4B = uint8_t(VecElementType::kB4)
};
// a64::InstDB - GpType
@ -192,6 +193,7 @@ enum EncodingId : uint32_t {
kEncodingBaseMvnNeg,
kEncodingBaseOp,
kEncodingBaseOpImm,
kEncodingBasePrfm,
kEncodingBaseR,
kEncodingBaseRM_NoImm,
kEncodingBaseRM_SImm10,
@ -412,6 +414,13 @@ struct BaseRM_SImm10 {
uint32_t immShift : 4;
};
struct BasePrfm {
uint32_t registerOp : 11;
uint32_t sOffsetOp : 10;
uint32_t uOffsetOp : 11;
uint32_t literalOp;
};
struct BaseLdSt {
uint32_t uOffsetOp : 10;
uint32_t prePostOp : 11;
@ -787,6 +796,7 @@ extern const BaseMovKNZ baseMovKNZ[3];
extern const BaseMvnNeg baseMvnNeg[3];
extern const BaseOp baseOp[23];
extern const BaseOpImm baseOpImm[14];
extern const BasePrfm basePrfm[1];
extern const BaseR baseR[10];
extern const BaseRM_NoImm baseRM_NoImm[21];
extern const BaseRM_SImm10 baseRM_SImm10[2];
@ -843,27 +853,13 @@ extern const SimdTblTbx simdTblTbx[2];
} // {EncodingData}
// a64::InstDB - InstNameIndex
// ===========================
// ${NameLimits:Begin}
// ------------------- Automatically generated, do not edit -------------------
enum : uint32_t { kMaxNameSize = 9 };
// ----------------------------------------------------------------------------
// ${NameLimits:End}
struct InstNameIndex {
uint16_t start;
uint16_t end;
};
// a64::InstDB - Tables
// ====================
#ifndef ASMJIT_NO_TEXT
extern const uint32_t _instNameIndexTable[];
extern const InstNameIndex instNameIndex;
extern const char _instNameStringTable[];
extern const InstNameIndex instNameIndex[26];
extern const uint32_t _instNameIndexTable[];
#endif // !ASMJIT_NO_TEXT
} // {InstDB}

View File

@ -56,7 +56,7 @@ UNIT(a64_operand) {
EXPECT_EQ(vd_1.group(), RegGroup::kVec);
EXPECT_EQ(vd_1.id(), 15u);
EXPECT_TRUE(vd_1.isVecD2());
EXPECT_EQ(vd_1.elementType(), Vec::kElementTypeD);
EXPECT_EQ(vd_1.elementType(), VecElementType::kD);
EXPECT_TRUE(vd_1.hasElementIndex());
EXPECT_EQ(vd_1.elementIndex(), 1u);
@ -65,7 +65,7 @@ UNIT(a64_operand) {
EXPECT_EQ(vs_3.group(), RegGroup::kVec);
EXPECT_EQ(vs_3.id(), 15u);
EXPECT_TRUE(vs_3.isVecS4());
EXPECT_EQ(vs_3.elementType(), Vec::kElementTypeS);
EXPECT_EQ(vs_3.elementType(), VecElementType::kS);
EXPECT_TRUE(vs_3.hasElementIndex());
EXPECT_EQ(vs_3.elementIndex(), 3u);
@ -74,7 +74,7 @@ UNIT(a64_operand) {
EXPECT_EQ(vb_4.group(), RegGroup::kVec);
EXPECT_EQ(vb_4.id(), 15u);
EXPECT_TRUE(vb_4.isVecB4x4());
EXPECT_EQ(vb_4.elementType(), Vec::kElementTypeB4);
EXPECT_EQ(vb_4.elementType(), VecElementType::kB4);
EXPECT_TRUE(vb_4.hasElementIndex());
EXPECT_EQ(vb_4.elementIndex(), 3u);
}

View File

@ -13,24 +13,276 @@ ASMJIT_BEGIN_SUB_NAMESPACE(a64)
//! \addtogroup asmjit_a64
//! \{
using arm::Reg;
using arm::Mem;
using arm::Gp;
using arm::GpW;
using arm::GpX;
class GpW;
class GpX;
using arm::Vec;
using arm::VecB;
using arm::VecH;
using arm::VecS;
using arm::VecD;
using arm::VecV;
class VecB;
class VecH;
class VecS;
class VecD;
class VecV;
//! General purpose register (AArch64).
class Gp : public Reg {
public:
ASMJIT_DEFINE_ABSTRACT_REG(Gp, Reg)
//! Special register id.
enum Id : uint32_t {
//! Register that depends on OS, could be used as TLS offset.
kIdOs = 18,
//! Frame pointer register id.
kIdFp = 29,
//! Link register id.
kIdLr = 30,
//! Stack register id.
kIdSp = 31,
//! Zero register id.
//!
//! Although zero register has the same id as stack register it has a special treatment, because we need to be
//! able to distinguish between these two at API level. Some instructions were designed to be used with SP and
//! some other with ZR - so we need a way to distinguish these two to make sure we emit the right thing.
//!
//! The number 63 is not random, when you perform `id & 31` you would always get 31 for both SP and ZR inputs,
//! which is the identifier used by AArch64 ISA to encode either SP or ZR depending on the instruction.
kIdZr = 63
};
//! Test whether this register is ZR register.
ASMJIT_INLINE_NODEBUG constexpr bool isZR() const noexcept { return id() == kIdZr; }
//! Test whether this register is SP register.
ASMJIT_INLINE_NODEBUG constexpr bool isSP() const noexcept { return id() == kIdSp; }
//! Cast this register to a 32-bit W register (returns a new operand).
ASMJIT_INLINE_NODEBUG GpW w() const noexcept;
//! \overload
ASMJIT_INLINE_NODEBUG GpW r32() const noexcept;
//! Cast this register to a 64-bit X register (returns a new operand).
ASMJIT_INLINE_NODEBUG GpX x() const noexcept;
//! \overload
ASMJIT_INLINE_NODEBUG GpX r64() const noexcept;
};
//! 32-bit general purpose W register (AArch64).
class GpW : public Gp { ASMJIT_DEFINE_FINAL_REG(GpW, Gp, RegTraits<RegType::kARM_GpW>); };
//! 64-bit general purpose X register (AArch64).
class GpX : public Gp { ASMJIT_DEFINE_FINAL_REG(GpX, Gp, RegTraits<RegType::kARM_GpX>); };
#ifndef _DOXYGEN
ASMJIT_INLINE_NODEBUG GpW Gp::w() const noexcept { return GpW(id()); }
ASMJIT_INLINE_NODEBUG GpX Gp::x() const noexcept { return GpX(id()); }
ASMJIT_INLINE_NODEBUG GpW Gp::r32() const noexcept { return GpW(id()); }
ASMJIT_INLINE_NODEBUG GpX Gp::r64() const noexcept { return GpX(id()); }
#endif
//! Vector element type (AArch64).
enum class VecElementType : uint32_t {
//! No element type specified.
kNone = 0,
//! Byte elements (B8 or B16).
kB,
//! Halfword elements (H4 or H8).
kH,
//! Singleword elements (S2 or S4).
kS,
//! Doubleword elements (D2).
kD,
//! Byte elements grouped by 4 bytes (B4).
//!
//! \note This element-type is only used by few instructions.
kB4,
//! Halfword elements grouped by 2 halfwords (H2).
//!
//! \note This element-type is only used by few instructions.
kH2,
//! Maximum value of \ref VecElementType
kMaxValue = kH2
};
//! Vector register (AArch64).
class Vec : public BaseVec {
public:
ASMJIT_DEFINE_ABSTRACT_REG(Vec, BaseVec)
//! \cond
//! Shortcuts.
enum SignatureReg : uint32_t {
kSignatureElementB = uint32_t(VecElementType::kB) << kSignatureRegElementTypeShift,
kSignatureElementH = uint32_t(VecElementType::kH) << kSignatureRegElementTypeShift,
kSignatureElementS = uint32_t(VecElementType::kS) << kSignatureRegElementTypeShift,
kSignatureElementD = uint32_t(VecElementType::kD) << kSignatureRegElementTypeShift,
kSignatureElementB4 = uint32_t(VecElementType::kB4) << kSignatureRegElementTypeShift,
kSignatureElementH2 = uint32_t(VecElementType::kH2) << kSignatureRegElementTypeShift
};
//! \endcond
//! Returns whether the register has element type or element index (or both).
ASMJIT_INLINE_NODEBUG constexpr bool hasElementTypeOrIndex() const noexcept { return _signature.hasField<kSignatureRegElementTypeMask | kSignatureRegElementFlagMask>(); }
//! Returns whether the vector register has associated a vector element type.
ASMJIT_INLINE_NODEBUG constexpr bool hasElementType() const noexcept { return _signature.hasField<kSignatureRegElementTypeMask>(); }
//! Returns vector element type of the register.
ASMJIT_INLINE_NODEBUG constexpr VecElementType elementType() const noexcept { return VecElementType(_signature.getField<kSignatureRegElementTypeMask>()); }
//! Sets vector element type of the register to `elementType`.
ASMJIT_INLINE_NODEBUG void setElementType(VecElementType elementType) noexcept { _signature.setField<kSignatureRegElementTypeMask>(uint32_t(elementType)); }
//! Resets vector element type to none.
ASMJIT_INLINE_NODEBUG void resetElementType() noexcept { _signature.setField<kSignatureRegElementTypeMask>(0); }
ASMJIT_INLINE_NODEBUG constexpr bool isVecB8() const noexcept { return _signature.subset(kBaseSignatureMask | kSignatureRegElementTypeMask) == (RegTraits<RegType::kARM_VecD>::kSignature | kSignatureElementB); }
ASMJIT_INLINE_NODEBUG constexpr bool isVecH4() const noexcept { return _signature.subset(kBaseSignatureMask | kSignatureRegElementTypeMask) == (RegTraits<RegType::kARM_VecD>::kSignature | kSignatureElementH); }
ASMJIT_INLINE_NODEBUG constexpr bool isVecS2() const noexcept { return _signature.subset(kBaseSignatureMask | kSignatureRegElementTypeMask) == (RegTraits<RegType::kARM_VecD>::kSignature | kSignatureElementS); }
ASMJIT_INLINE_NODEBUG constexpr bool isVecD1() const noexcept { return _signature.subset(kBaseSignatureMask | kSignatureRegElementTypeMask) == (RegTraits<RegType::kARM_VecD>::kSignature); }
ASMJIT_INLINE_NODEBUG constexpr bool isVecB16() const noexcept { return _signature.subset(kBaseSignatureMask | kSignatureRegElementTypeMask) == (RegTraits<RegType::kARM_VecV>::kSignature | kSignatureElementB); }
ASMJIT_INLINE_NODEBUG constexpr bool isVecH8() const noexcept { return _signature.subset(kBaseSignatureMask | kSignatureRegElementTypeMask) == (RegTraits<RegType::kARM_VecV>::kSignature | kSignatureElementH); }
ASMJIT_INLINE_NODEBUG constexpr bool isVecS4() const noexcept { return _signature.subset(kBaseSignatureMask | kSignatureRegElementTypeMask) == (RegTraits<RegType::kARM_VecV>::kSignature | kSignatureElementS); }
ASMJIT_INLINE_NODEBUG constexpr bool isVecD2() const noexcept { return _signature.subset(kBaseSignatureMask | kSignatureRegElementTypeMask) == (RegTraits<RegType::kARM_VecV>::kSignature | kSignatureElementD); }
ASMJIT_INLINE_NODEBUG constexpr bool isVecB4x4() const noexcept { return _signature.subset(kBaseSignatureMask | kSignatureRegElementTypeMask) == (RegTraits<RegType::kARM_VecV>::kSignature | kSignatureElementB4); }
ASMJIT_INLINE_NODEBUG constexpr bool isVecH2x4() const noexcept { return _signature.subset(kBaseSignatureMask | kSignatureRegElementTypeMask) == (RegTraits<RegType::kARM_VecV>::kSignature | kSignatureElementH2); }
//! Creates a cloned register with element access.
ASMJIT_INLINE_NODEBUG Vec at(uint32_t elementIndex) const noexcept {
return Vec((signature() & ~kSignatureRegElementIndexMask) | (elementIndex << kSignatureRegElementIndexShift) | kSignatureRegElementFlagMask, id());
}
//! Cast this register to an 8-bit B register (AArch64 only).
ASMJIT_INLINE_NODEBUG VecB b() const noexcept;
//! Cast this register to a 16-bit H register (AArch64 only).
ASMJIT_INLINE_NODEBUG VecH h() const noexcept;
//! Cast this register to a 32-bit S register.
ASMJIT_INLINE_NODEBUG VecS s() const noexcept;
//! Cast this register to a 64-bit D register.
ASMJIT_INLINE_NODEBUG VecD d() const noexcept;
//! Cast this register to a 128-bit Q register.
ASMJIT_INLINE_NODEBUG VecV q() const noexcept;
//! Cast this register to a 128-bit V register.
ASMJIT_INLINE_NODEBUG VecV v() const noexcept;
//! Casts this register to b (clone).
ASMJIT_INLINE_NODEBUG Vec v8() const noexcept;
//! Casts this register to h (clone).
ASMJIT_INLINE_NODEBUG Vec v16() const noexcept;
//! Casts this register to s (clone).
ASMJIT_INLINE_NODEBUG Vec v32() const noexcept;
//! Casts this register to d (clone).
ASMJIT_INLINE_NODEBUG Vec v64() const noexcept;
//! Casts this register to q (clone).
ASMJIT_INLINE_NODEBUG Vec v128() const noexcept;
//! Cast this register to a 128-bit V.B[elementIndex] register.
ASMJIT_INLINE_NODEBUG VecV b(uint32_t elementIndex) const noexcept;
//! Cast this register to a 128-bit V.H[elementIndex] register.
ASMJIT_INLINE_NODEBUG VecV h(uint32_t elementIndex) const noexcept;
//! Cast this register to a 128-bit V.S[elementIndex] register.
ASMJIT_INLINE_NODEBUG VecV s(uint32_t elementIndex) const noexcept;
//! Cast this register to a 128-bit V.D[elementIndex] register.
ASMJIT_INLINE_NODEBUG VecV d(uint32_t elementIndex) const noexcept;
//! Cast this register to a 128-bit V.H2[elementIndex] register.
ASMJIT_INLINE_NODEBUG VecV h2(uint32_t elementIndex) const noexcept;
//! Cast this register to a 128-bit V.B4[elementIndex] register.
ASMJIT_INLINE_NODEBUG VecV b4(uint32_t elementIndex) const noexcept;
//! Cast this register to V.8B.
ASMJIT_INLINE_NODEBUG VecD b8() const noexcept;
//! Cast this register to V.16B.
ASMJIT_INLINE_NODEBUG VecV b16() const noexcept;
//! Cast this register to V.2H.
ASMJIT_INLINE_NODEBUG VecS h2() const noexcept;
//! Cast this register to V.4H.
ASMJIT_INLINE_NODEBUG VecD h4() const noexcept;
//! Cast this register to V.8H.
ASMJIT_INLINE_NODEBUG VecV h8() const noexcept;
//! Cast this register to V.2S.
ASMJIT_INLINE_NODEBUG VecD s2() const noexcept;
//! Cast this register to V.4S.
ASMJIT_INLINE_NODEBUG VecV s4() const noexcept;
//! Cast this register to V.2D.
ASMJIT_INLINE_NODEBUG VecV d2() const noexcept;
static ASMJIT_INLINE_NODEBUG constexpr OperandSignature _makeElementAccessSignature(VecElementType elementType, uint32_t elementIndex) noexcept {
return OperandSignature{
uint32_t(RegTraits<RegType::kARM_VecV>::kSignature) |
uint32_t(kSignatureRegElementFlagMask) |
(uint32_t(elementType) << kSignatureRegElementTypeShift) |
(uint32_t(elementIndex << kSignatureRegElementIndexShift))};
}
};
//! 8-bit view (S) of VFP/SIMD register.
class VecB : public Vec {
public:
ASMJIT_DEFINE_FINAL_REG(VecB, Vec, RegTraits<RegType::kARM_VecB>)
};
//! 16-bit view (S) of VFP/SIMD register.
class VecH : public Vec {
public:
ASMJIT_DEFINE_FINAL_REG(VecH, Vec, RegTraits<RegType::kARM_VecH>)
};
//! 32-bit view (S) of VFP/SIMD register.
class VecS : public Vec {
public:
ASMJIT_DEFINE_FINAL_REG(VecS, Vec, RegTraits<RegType::kARM_VecS>)
};
//! 64-bit view (D) of VFP/SIMD register.
class VecD : public Vec {
public:
ASMJIT_DEFINE_FINAL_REG(VecD, Vec, RegTraits<RegType::kARM_VecD>)
};
//! 128-bit vector register (Q or V).
class VecV : public Vec {
public:
ASMJIT_DEFINE_FINAL_REG(VecV, Vec, RegTraits<RegType::kARM_VecV>)
};
ASMJIT_INLINE_NODEBUG VecB Vec::b() const noexcept { return VecB(id()); }
ASMJIT_INLINE_NODEBUG VecH Vec::h() const noexcept { return VecH(id()); }
ASMJIT_INLINE_NODEBUG VecS Vec::s() const noexcept { return VecS(id()); }
ASMJIT_INLINE_NODEBUG VecD Vec::d() const noexcept { return VecD(id()); }
ASMJIT_INLINE_NODEBUG VecV Vec::q() const noexcept { return VecV(id()); }
ASMJIT_INLINE_NODEBUG VecV Vec::v() const noexcept { return VecV(id()); }
ASMJIT_INLINE_NODEBUG Vec Vec::v8() const noexcept { return VecB(id()); }
ASMJIT_INLINE_NODEBUG Vec Vec::v16() const noexcept { return VecH(id()); }
ASMJIT_INLINE_NODEBUG Vec Vec::v32() const noexcept { return VecS(id()); }
ASMJIT_INLINE_NODEBUG Vec Vec::v64() const noexcept { return VecD(id()); }
ASMJIT_INLINE_NODEBUG Vec Vec::v128() const noexcept { return VecV(id()); }
ASMJIT_INLINE_NODEBUG VecV Vec::b(uint32_t elementIndex) const noexcept { return VecV(_makeElementAccessSignature(VecElementType::kB, elementIndex), id()); }
ASMJIT_INLINE_NODEBUG VecV Vec::h(uint32_t elementIndex) const noexcept { return VecV(_makeElementAccessSignature(VecElementType::kH, elementIndex), id()); }
ASMJIT_INLINE_NODEBUG VecV Vec::s(uint32_t elementIndex) const noexcept { return VecV(_makeElementAccessSignature(VecElementType::kS, elementIndex), id()); }
ASMJIT_INLINE_NODEBUG VecV Vec::d(uint32_t elementIndex) const noexcept { return VecV(_makeElementAccessSignature(VecElementType::kD, elementIndex), id()); }
ASMJIT_INLINE_NODEBUG VecV Vec::h2(uint32_t elementIndex) const noexcept { return VecV(_makeElementAccessSignature(VecElementType::kH2, elementIndex), id()); }
ASMJIT_INLINE_NODEBUG VecV Vec::b4(uint32_t elementIndex) const noexcept { return VecV(_makeElementAccessSignature(VecElementType::kB4, elementIndex), id()); }
ASMJIT_INLINE_NODEBUG VecD Vec::b8() const noexcept { return VecD(OperandSignature{VecD::kSignature | kSignatureElementB}, id()); }
ASMJIT_INLINE_NODEBUG VecS Vec::h2() const noexcept { return VecS(OperandSignature{VecS::kSignature | kSignatureElementH}, id()); }
ASMJIT_INLINE_NODEBUG VecD Vec::h4() const noexcept { return VecD(OperandSignature{VecD::kSignature | kSignatureElementH}, id()); }
ASMJIT_INLINE_NODEBUG VecD Vec::s2() const noexcept { return VecD(OperandSignature{VecD::kSignature | kSignatureElementS}, id()); }
ASMJIT_INLINE_NODEBUG VecV Vec::b16() const noexcept { return VecV(OperandSignature{VecV::kSignature | kSignatureElementB}, id()); }
ASMJIT_INLINE_NODEBUG VecV Vec::h8() const noexcept { return VecV(OperandSignature{VecV::kSignature | kSignatureElementH}, id()); }
ASMJIT_INLINE_NODEBUG VecV Vec::s4() const noexcept { return VecV(OperandSignature{VecV::kSignature | kSignatureElementS}, id()); }
ASMJIT_INLINE_NODEBUG VecV Vec::d2() const noexcept { return VecV(OperandSignature{VecV::kSignature | kSignatureElementD}, id()); }
#ifndef _DOXYGEN
namespace regs {
#endif
using namespace ::asmjit::arm::regs;
//! Creates a 32-bit W register operand.
static ASMJIT_INLINE_NODEBUG constexpr GpW w(uint32_t id) noexcept { return GpW(id); }
//! Creates a 64-bit X register operand.
static ASMJIT_INLINE_NODEBUG constexpr GpX x(uint32_t id) noexcept { return GpX(id); }
//! Creates a 32-bit S register operand.
static ASMJIT_INLINE_NODEBUG constexpr VecS s(uint32_t id) noexcept { return VecS(id); }
//! Creates a 64-bit D register operand.
static ASMJIT_INLINE_NODEBUG constexpr VecD d(uint32_t id) noexcept { return VecD(id); }
//! Creates a 1282-bit V register operand.
static ASMJIT_INLINE_NODEBUG constexpr VecV v(uint32_t id) noexcept { return VecV(id); }
static constexpr GpW w0 = GpW(0);
static constexpr GpW w1 = GpW(1);
@ -305,8 +557,94 @@ static constexpr VecV v31 = VecV(31);
using namespace regs;
#endif
//! \name Shift Operation Construction
//! \{
//! Constructs a `UXTB #value` extend and shift (unsigned byte extend) (AArch64).
static ASMJIT_INLINE_NODEBUG constexpr Shift uxtb(uint32_t value) noexcept { return Shift(ShiftOp::kUXTB, value); }
//! Constructs a `UXTH #value` extend and shift (unsigned hword extend) (AArch64).
static ASMJIT_INLINE_NODEBUG constexpr Shift uxth(uint32_t value) noexcept { return Shift(ShiftOp::kUXTH, value); }
//! Constructs a `UXTW #value` extend and shift (unsigned word extend) (AArch64).
static ASMJIT_INLINE_NODEBUG constexpr Shift uxtw(uint32_t value) noexcept { return Shift(ShiftOp::kUXTW, value); }
//! Constructs a `UXTX #value` extend and shift (unsigned dword extend) (AArch64).
static ASMJIT_INLINE_NODEBUG constexpr Shift uxtx(uint32_t value) noexcept { return Shift(ShiftOp::kUXTX, value); }
//! Constructs a `SXTB #value` extend and shift (signed byte extend) (AArch64).
static ASMJIT_INLINE_NODEBUG constexpr Shift sxtb(uint32_t value) noexcept { return Shift(ShiftOp::kSXTB, value); }
//! Constructs a `SXTH #value` extend and shift (signed hword extend) (AArch64).
static ASMJIT_INLINE_NODEBUG constexpr Shift sxth(uint32_t value) noexcept { return Shift(ShiftOp::kSXTH, value); }
//! Constructs a `SXTW #value` extend and shift (signed word extend) (AArch64).
static ASMJIT_INLINE_NODEBUG constexpr Shift sxtw(uint32_t value) noexcept { return Shift(ShiftOp::kSXTW, value); }
//! Constructs a `SXTX #value` extend and shift (signed dword extend) (AArch64).
static ASMJIT_INLINE_NODEBUG constexpr Shift sxtx(uint32_t value) noexcept { return Shift(ShiftOp::kSXTX, value); }
//! \}
//! \name Memory Operand Construction
//! \{
//! Creates `[base, offset]` memory operand (offset mode) (AArch64).
static ASMJIT_INLINE_NODEBUG constexpr Mem ptr(const Gp& base, int32_t offset = 0) noexcept {
return Mem(base, offset);
}
//! Creates `[base, offset]!` memory operand (pre-index mode) (AArch64).
static ASMJIT_INLINE_NODEBUG constexpr Mem ptr_pre(const Gp& base, int32_t offset = 0) noexcept {
return Mem(base, offset, OperandSignature::fromValue<Mem::kSignatureMemOffsetModeMask>(OffsetMode::kPreIndex));
}
//! Creates `[base], offset` memory operand (post-index mode) (AArch64).
static ASMJIT_INLINE_NODEBUG constexpr Mem ptr_post(const Gp& base, int32_t offset = 0) noexcept {
return Mem(base, offset, OperandSignature::fromValue<Mem::kSignatureMemOffsetModeMask>(OffsetMode::kPostIndex));
}
//! Creates `[base, index]` memory operand (AArch64).
static ASMJIT_INLINE_NODEBUG constexpr Mem ptr(const Gp& base, const Gp& index) noexcept {
return Mem(base, index);
}
//! Creates `[base, index]!` memory operand (pre-index mode) (AArch64).
static ASMJIT_INLINE_NODEBUG constexpr Mem ptr_pre(const Gp& base, const Gp& index) noexcept {
return Mem(base, index, OperandSignature::fromValue<Mem::kSignatureMemOffsetModeMask>(OffsetMode::kPreIndex));
}
//! Creates `[base], index` memory operand (post-index mode) (AArch64).
static ASMJIT_INLINE_NODEBUG constexpr Mem ptr_post(const Gp& base, const Gp& index) noexcept {
return Mem(base, index, OperandSignature::fromValue<Mem::kSignatureMemOffsetModeMask>(OffsetMode::kPostIndex));
}
//! Creates `[base, index, SHIFT_OP #shift]` memory operand (AArch64).
static ASMJIT_INLINE_NODEBUG constexpr Mem ptr(const Gp& base, const Gp& index, const Shift& shift) noexcept {
return Mem(base, index, shift);
}
//! Creates `[base, offset]` memory operand (AArch64).
static ASMJIT_INLINE_NODEBUG constexpr Mem ptr(const Label& base, int32_t offset = 0) noexcept {
return Mem(base, offset);
}
// TODO: [ARM] PC + offset address.
#if 0
//! Creates `[PC + offset]` (relative) memory operand.
static ASMJIT_INLINE_NODEBUG constexpr Mem ptr(const PC& pc, int32_t offset = 0) noexcept {
return Mem(pc, offset);
}
#endif
//! \}
//! \}
ASMJIT_END_SUB_NAMESPACE
//! \cond INTERNAL
ASMJIT_BEGIN_NAMESPACE
ASMJIT_DEFINE_TYPE_ID(a64::GpW, TypeId::kInt32);
ASMJIT_DEFINE_TYPE_ID(a64::GpX, TypeId::kInt64);
ASMJIT_DEFINE_TYPE_ID(a64::VecS, TypeId::kFloat32x1);
ASMJIT_DEFINE_TYPE_ID(a64::VecD, TypeId::kFloat64x1);
ASMJIT_DEFINE_TYPE_ID(a64::VecV, TypeId::kInt32x4);
ASMJIT_END_NAMESPACE
//! \endcond
#endif // ASMJIT_ARM_A64OPERAND_H_INCLUDED

View File

@ -131,7 +131,7 @@ Error RACFGBuilder::onInst(InstNode* inst, InstControlFlow& controlType, RAInstB
InstId instId = inst->id();
uint32_t opCount = inst->opCount();
const Operand* opArray = inst->operands();
ASMJIT_PROPAGATE(InstInternal::queryRWInfo(_arch, inst->baseInst(), opArray, opCount, &rwInfo));
ASMJIT_PROPAGATE(InstInternal::queryRWInfo(inst->baseInst(), opArray, opCount, &rwInfo));
const InstDB::InstInfo& instInfo = InstDB::infoById(instId);
uint32_t singleRegOps = 0;
@ -230,7 +230,7 @@ Error RACFGBuilder::onInst(InstNode* inst, InstControlFlow& controlType, RAInstB
if (reg.as<Vec>().hasElementIndex()) {
// Only the first 0..15 registers can be used if the register uses
// element accessor that accesses half-words (h[0..7] elements).
if (instInfo.hasFlag(InstDB::kInstFlagVH0_15) && reg.as<Vec>().elementType() == Vec::kElementTypeH) {
if (instInfo.hasFlag(InstDB::kInstFlagVH0_15) && reg.as<Vec>().elementType() == VecElementType::kH) {
if (Support::test(flags, RATiedFlags::kUse))
useId &= 0x0000FFFFu;
else
@ -595,14 +595,14 @@ void ARMRAPass::onInit() noexcept {
_archTraits = &ArchTraits::byArch(arch);
_physRegCount.set(RegGroup::kGp, 32);
_physRegCount.set(RegGroup::kVec, 32);
_physRegCount.set(RegGroup::kExtraVirt2, 0);
_physRegCount.set(RegGroup::kMask, 0);
_physRegCount.set(RegGroup::kExtraVirt3, 0);
_buildPhysIndex();
_availableRegCount = _physRegCount;
_availableRegs[RegGroup::kGp] = Support::lsbMask<uint32_t>(_physRegCount.get(RegGroup::kGp));
_availableRegs[RegGroup::kVec] = Support::lsbMask<uint32_t>(_physRegCount.get(RegGroup::kVec));
_availableRegs[RegGroup::kExtraVirt3] = Support::lsbMask<uint32_t>(_physRegCount.get(RegGroup::kExtraVirt2));
_availableRegs[RegGroup::kMask] = Support::lsbMask<uint32_t>(_physRegCount.get(RegGroup::kMask));
_availableRegs[RegGroup::kExtraVirt3] = Support::lsbMask<uint32_t>(_physRegCount.get(RegGroup::kExtraVirt3));
_scratchRegIndexes[0] = uint8_t(27);
@ -612,7 +612,9 @@ void ARMRAPass::onInit() noexcept {
// make unavailable all registers that are special and cannot be used in general.
bool hasFP = _func->frame().hasPreservedFP();
if (hasFP)
// Apple ABI requires that the frame-pointer register is not changed by leaf functions and properly updated
// by non-leaf functions. So, let's make this register unavailable as it's just not safe to update it.
if (hasFP || cc()->environment().isDarwin())
makeUnavailable(RegGroup::kGp, Gp::kIdFp);
makeUnavailable(RegGroup::kGp, Gp::kIdSp);

View File

@ -9,7 +9,7 @@
#include "../core/misc_p.h"
#include "../core/support.h"
#include "../arm/armformatter_p.h"
#include "../arm/armoperand.h"
#include "../arm/a64operand.h"
#include "../arm/a64instapi_p.h"
#include "../arm/a64instdb_p.h"
@ -31,26 +31,45 @@ Error FormatterInternal::formatFeature(String& sb, uint32_t featureId) noexcept
"ARMv8a\0"
"THUMB\0"
"THUMBv2\0"
"ABLE\0"
"ADERR\0"
"AES\0"
"AFP\0"
"AIE\0"
"AMU1\0"
"AMU1_1\0"
"ANERR\0"
"ASIMD\0"
"BF16\0"
"BRBE\0"
"BTI\0"
"BWE\0"
"CCIDX\0"
"CHK\0"
"CLRBHB\0"
"CMOW\0"
"CONSTPACFIELD\0"
"CPA\0"
"CPA2\0"
"CPUID\0"
"CRC32\0"
"CSSC\0"
"CSV2\0"
"CSV2_3\0"
"CSV3\0"
"D128\0"
"DGH\0"
"DIT\0"
"DOTPROD\0"
"DPB\0"
"DPB2\0"
"EBEP\0"
"EBF16\0"
"ECBHB\0"
"ECV\0"
"EDHSR\0"
"EDSP\0"
"FAMINMAX\0"
"FCMA\0"
"FGT\0"
"FGT2\0"
@ -61,13 +80,25 @@ Error FormatterInternal::formatFeature(String& sb, uint32_t featureId) noexcept
"FP\0"
"FP16\0"
"FP16CONV\0"
"FP8\0"
"FP8DOT2\0"
"FP8DOT4\0"
"FP8FMA\0"
"FPMR\0"
"FRINTTS\0"
"GCS\0"
"HACDBS\0"
"HAFDBS\0"
"HAFT\0"
"HDBSS\0"
"HBC\0"
"HCX\0"
"HPDS\0"
"HPDS2\0"
"I8MM\0"
"IDIVA\0"
"IDIVT\0"
"ITE\0"
"JSCVT\0"
"LOR\0"
"LRCPC\0"
@ -79,12 +110,24 @@ Error FormatterInternal::formatFeature(String& sb, uint32_t featureId) noexcept
"LSE\0"
"LSE128\0"
"LSE2\0"
"LUT\0"
"LVA\0"
"LVA3\0"
"MEC\0"
"MOPS\0"
"MPAM\0"
"MTE\0"
"MTE2\0"
"MTE3\0"
"MTE4\0"
"MTE_ASYM_FAULT\0"
"MTE_ASYNC\0"
"MTE_CANONICAL_TAGS\0"
"MTE_NO_ADDRESS_TAGS\0"
"MTE_PERM_S1\0"
"MTE_STORE_ONLY\0"
"MTE_TAGGED_FAR\0"
"MTPMU\0"
"NMI\0"
"NV\0"
"NV2\0"
@ -92,19 +135,28 @@ Error FormatterInternal::formatFeature(String& sb, uint32_t featureId) noexcept
"PAN2\0"
"PAN3\0"
"PAUTH\0"
"PFAR\0"
"PMU\0"
"PMULL\0"
"PRFMSLC\0"
"RAS\0"
"RAS1_1\0"
"RAS2\0"
"RASSA2\0"
"RDM\0"
"RME\0"
"RNG\0"
"RNG_TRAP\0"
"RPRES\0"
"RPRFM\0"
"S1PIE\0"
"S1POE\0"
"S2PIE\0"
"S2POE\0"
"SB\0"
"SCTLR2\0"
"SEBEP\0"
"SEL2\0"
"SHA1\0"
"SHA256\0"
"SHA3\0"
@ -121,14 +173,32 @@ Error FormatterInternal::formatFeature(String& sb, uint32_t featureId) noexcept
"SME_F16F32\0"
"SME_F32F32\0"
"SME_F64F64\0"
"SME_F8F16\0"
"SME_F8F32\0"
"SME_FA64\0"
"SME_I16I32\0"
"SME_I16I64\0"
"SME_I8I32\0"
"SME_LUTv2\0"
"SPE\0"
"SPE1_1\0"
"SPE1_2\0"
"SPE1_3\0"
"SPE1_4\0"
"SPE_ALTCLK\0"
"SPE_CRR\0"
"SPE_EFT\0"
"SPE_FDS\0"
"SPE_FPF\0"
"SPE_SME\0"
"SPECRES\0"
"SPECRES2\0"
"SPMU\0"
"SSBS\0"
"SSBS2\0"
"SSVE_FP8DOT2\0"
"SSVE_FP8DOT4\0"
"SSVE_FP8FMA\0"
"SVE\0"
"SVE2\0"
"SVE2_1\0"
@ -146,25 +216,35 @@ Error FormatterInternal::formatFeature(String& sb, uint32_t featureId) noexcept
"SYSINSTR128\0"
"SYSREG128\0"
"THE\0"
"TLBIOS\0"
"TLBIRANGE\0"
"TLBIW\0"
"TME\0"
"TRF\0"
"UAO\0"
"VFP_D32\0"
"VHE\0"
"VMID16\0"
"WFXT\0"
"XNX\0"
"XS\0"
"<Unknown>\0";
static const uint16_t sFeatureIndex[] = {
0, 5, 11, 17, 24, 30, 38, 42, 46, 52, 57, 61, 67, 71, 78, 84, 90, 95, 100,
104, 108, 116, 120, 125, 131, 135, 140, 145, 149, 154, 158, 164, 171, 176,
179, 184, 193, 201, 205, 209, 213, 218, 224, 230, 236, 240, 246, 253, 260,
265, 278, 285, 289, 296, 301, 306, 311, 315, 320, 325, 330, 334, 337, 341,
345, 350, 355, 361, 365, 371, 379, 383, 390, 395, 399, 403, 407, 416, 422,
428, 431, 436, 443, 448, 455, 459, 463, 467, 472, 479, 490, 501, 513, 524,
535, 546, 557, 566, 577, 588, 598, 606, 615, 620, 626, 630, 635, 642, 650,
661, 670, 682, 692, 702, 712, 721, 734, 743, 751, 763, 773, 777, 781, 785,
789, 797, 801, 806, 809
0, 5, 11, 17, 24, 30, 38, 43, 49, 53, 57, 61, 66, 73, 79, 85, 90, 95, 99,
103, 109, 113, 120, 125, 139, 143, 148, 154, 160, 165, 170, 177, 182, 187,
191, 195, 203, 207, 212, 217, 223, 229, 233, 239, 244, 253, 258, 262, 267,
271, 277, 284, 289, 292, 297, 306, 310, 318, 326, 333, 338, 346, 350, 357,
364, 369, 375, 379, 383, 388, 394, 399, 405, 411, 415, 421, 425, 431, 438,
445, 450, 463, 470, 474, 481, 486, 490, 494, 499, 503, 508, 513, 517, 522,
527, 532, 547, 557, 576, 596, 608, 623, 638, 644, 648, 651, 655, 659, 664,
669, 675, 680, 684, 690, 698, 702, 709, 714, 721, 725, 729, 733, 742, 748,
754, 760, 766, 772, 778, 781, 788, 794, 799, 804, 811, 816, 823, 827, 831,
835, 840, 847, 858, 869, 881, 892, 903, 914, 925, 935, 945, 954, 965, 976,
986, 996, 1000, 1007, 1014, 1021, 1028, 1039, 1047, 1055, 1063, 1071, 1079,
1087, 1096, 1101, 1106, 1112, 1125, 1138, 1150, 1154, 1159, 1166, 1174, 1185,
1194, 1206, 1216, 1226, 1236, 1245, 1258, 1267, 1275, 1287, 1297, 1301, 1308,
1318, 1324, 1328, 1332, 1336, 1344, 1348, 1355, 1360, 1364, 1367
};
// @EnumStringEnd@
@ -178,14 +258,14 @@ ASMJIT_FAVOR_SIZE Error FormatterInternal::formatCondCode(String& sb, CondCode c
static const char condCodeData[] =
"al\0" "na\0"
"eq\0" "ne\0"
"cs\0" "cc\0" "mi\0" "pl\0" "vs\0" "vc\0"
"hs\0" "lo\0" "mi\0" "pl\0" "vs\0" "vc\0"
"hi\0" "ls\0" "ge\0" "lt\0" "gt\0" "le\0"
"<Unknown>";
return sb.append(condCodeData + Support::min<uint32_t>(uint32_t(cc), 16u) * 3);
}
ASMJIT_FAVOR_SIZE Error FormatterInternal::formatShiftOp(String& sb, ShiftOp shiftOp) noexcept {
const char* str = "<Unknown>";
const char* str = nullptr;
switch (shiftOp) {
case ShiftOp::kLSL: str = "lsl"; break;
case ShiftOp::kLSR: str = "lsr"; break;
@ -201,6 +281,7 @@ ASMJIT_FAVOR_SIZE Error FormatterInternal::formatShiftOp(String& sb, ShiftOp shi
case ShiftOp::kSXTH: str = "sxth"; break;
case ShiftOp::kSXTW: str = "sxtw"; break;
case ShiftOp::kSXTX: str = "sxtx"; break;
default: str = "<Unknown>"; break;
}
return sb.append(str);
}
@ -208,6 +289,25 @@ ASMJIT_FAVOR_SIZE Error FormatterInternal::formatShiftOp(String& sb, ShiftOp shi
// arm::FormatterInternal - Format Register
// ========================================
struct FormatElementData {
char letter;
uint8_t elementCount;
uint8_t onlyIndex;
uint8_t reserved;
};
static constexpr FormatElementData formatElementDataTable[9] = {
{ '?' , 0 , 0, 0 }, // None
{ 'b' , 16, 0, 0 }, // bX or b[index]
{ 'h' , 8 , 0, 0 }, // hX or h[index]
{ 's' , 4 , 0, 0 }, // sX or s[index]
{ 'd' , 2 , 0, 0 }, // dX or d[index]
{ 'b' , 4 , 1, 0 }, // ?? or b4[index]
{ 'h' , 2 , 1, 0 }, // ?? or h2[index]
{ '?' , 0 , 0, 0 }, // invalid (possibly stored in Operand)
{ '?' , 0 , 0, 0 } // invalid (never stored in Operand, bug...)
};
ASMJIT_FAVOR_SIZE Error FormatterInternal::formatRegister(
String& sb,
FormatFlags flags,
@ -264,31 +364,22 @@ ASMJIT_FAVOR_SIZE Error FormatterInternal::formatRegister(
if (Environment::is64Bit(arch)) {
letter = 'w';
if (rId == Gp::kIdZr)
if (rId == a64::Gp::kIdZr)
return sb.append("wzr", 3);
if (rId == Gp::kIdSp)
if (rId == a64::Gp::kIdSp)
return sb.append("wsp", 3);
}
else {
letter = 'r';
if (rId == 13)
return sb.append("sp", 2);
if (rId == 14)
return sb.append("lr", 2);
if (rId == 15)
return sb.append("pc", 2);
}
break;
case RegType::kARM_GpX:
if (Environment::is64Bit(arch)) {
if (rId == Gp::kIdZr)
if (rId == a64::Gp::kIdZr)
return sb.append("xzr", 3);
if (rId == Gp::kIdSp)
if (rId == a64::Gp::kIdSp)
return sb.append("sp", 2);
letter = 'x';
@ -299,7 +390,7 @@ ASMJIT_FAVOR_SIZE Error FormatterInternal::formatRegister(
ASMJIT_FALLTHROUGH;
default:
ASMJIT_PROPAGATE(sb.appendFormat("<Reg-%u>?$u", uint32_t(regType), rId));
ASMJIT_PROPAGATE(sb.appendFormat("<Reg-%u>?%u", uint32_t(regType), rId));
break;
}
@ -307,54 +398,69 @@ ASMJIT_FAVOR_SIZE Error FormatterInternal::formatRegister(
ASMJIT_PROPAGATE(sb.appendFormat("%c%u", letter, rId));
}
constexpr uint32_t kElementTypeCount = uint32_t(a64::VecElementType::kMaxValue) + 1;
if (elementType) {
char elementLetter = '\0';
uint32_t elementCount = 0;
elementType = Support::min(elementType, kElementTypeCount);
switch (elementType) {
case Vec::kElementTypeB:
elementLetter = 'b';
elementCount = 16;
break;
FormatElementData elementData = formatElementDataTable[elementType];
uint32_t elementCount = elementData.elementCount;
case Vec::kElementTypeH:
elementLetter = 'h';
elementCount = 8;
break;
case Vec::kElementTypeS:
elementLetter = 's';
elementCount = 4;
break;
case Vec::kElementTypeD:
elementLetter = 'd';
elementCount = 2;
break;
default:
return sb.append(".<Unknown>");
}
if (elementLetter) {
if (elementIndex == 0xFFFFFFFFu) {
if (regType == RegType::kARM_VecD)
if (regType == RegType::kARM_VecD) {
elementCount /= 2u;
ASMJIT_PROPAGATE(sb.appendFormat(".%u%c", elementCount, elementLetter));
}
else {
ASMJIT_PROPAGATE(sb.appendFormat(".%c[%u]", elementLetter, elementIndex));
ASMJIT_PROPAGATE(sb.append('.'));
if (elementCount) {
ASMJIT_PROPAGATE(sb.appendUInt(elementCount));
}
ASMJIT_PROPAGATE(sb.append(elementData.letter));
}
}
else if (elementIndex != 0xFFFFFFFFu) {
// This should only be used by AArch32 - AArch64 requires an additional elementType in index[].
if (elementIndex != 0xFFFFFFFFu) {
ASMJIT_PROPAGATE(sb.appendFormat("[%u]", elementIndex));
}
return kErrorOk;
}
ASMJIT_FAVOR_SIZE Error FormatterInternal::formatRegisterList(
String& sb,
FormatFlags flags,
const BaseEmitter* emitter,
Arch arch,
RegType regType,
uint32_t rMask) noexcept {
bool first = true;
ASMJIT_PROPAGATE(sb.append('{'));
while (rMask != 0u) {
uint32_t start = Support::ctz(rMask);
uint32_t count = 0u;
uint32_t mask = 1u << start;
do {
rMask &= ~mask;
mask <<= 1u;
count++;
} while (rMask & mask);
if (!first)
ASMJIT_PROPAGATE(sb.append(", "));
ASMJIT_PROPAGATE(formatRegister(sb, flags, emitter, arch, regType, start, 0, 0xFFFFFFFFu));
if (count >= 2u) {
ASMJIT_PROPAGATE(sb.append('-'));
ASMJIT_PROPAGATE(formatRegister(sb, flags, emitter, arch, regType, start + count - 1, 0, 0xFFFFFFFFu));
}
first = false;
}
ASMJIT_PROPAGATE(sb.append('}'));
return kErrorOk;
}
// a64::FormatterInternal - Format Operand
// =======================================
@ -368,10 +474,10 @@ ASMJIT_FAVOR_SIZE Error FormatterInternal::formatOperand(
if (op.isReg()) {
const BaseReg& reg = op.as<BaseReg>();
uint32_t elementType = op.as<Vec>().elementType();
uint32_t elementIndex = op.as<Vec>().elementIndex();
uint32_t elementType = op._signature.getField<BaseVec::kSignatureRegElementTypeMask>();
uint32_t elementIndex = op.as<BaseVec>().elementIndex();
if (!op.as<Vec>().hasElementIndex())
if (!op.as<BaseVec>().hasElementIndex())
elementIndex = 0xFFFFFFFFu;
return formatRegister(sb, flags, emitter, arch, reg.type(), reg.id(), elementType, elementIndex);
@ -433,7 +539,7 @@ ASMJIT_FAVOR_SIZE Error FormatterInternal::formatOperand(
if (m.hasShift()) {
ASMJIT_PROPAGATE(sb.append(' '));
if (!m.isPreOrPost())
ASMJIT_PROPAGATE(formatShiftOp(sb, (ShiftOp)m.predicate()));
ASMJIT_PROPAGATE(formatShiftOp(sb, m.shiftOp()));
ASMJIT_PROPAGATE(sb.appendFormat(" %u", m.shift()));
}
@ -449,6 +555,12 @@ ASMJIT_FAVOR_SIZE Error FormatterInternal::formatOperand(
if (op.isImm()) {
const Imm& i = op.as<Imm>();
int64_t val = i.value();
uint32_t predicate = i.predicate();
if (predicate) {
ASMJIT_PROPAGATE(formatShiftOp(sb, ShiftOp(predicate)));
ASMJIT_PROPAGATE(sb.append(' '));
}
if (Support::test(flags, FormatFlags::kHexImms) && uint64_t(val) > 9) {
ASMJIT_PROPAGATE(sb.append("0x"));
@ -463,6 +575,11 @@ ASMJIT_FAVOR_SIZE Error FormatterInternal::formatOperand(
return Formatter::formatLabel(sb, flags, emitter, op.id());
}
if (op.isRegList()) {
const BaseRegList& regList = op.as<BaseRegList>();
return formatRegisterList(sb, flags, emitter, arch, regList.type(), regList.list());
}
return sb.append("<None>");
}

View File

@ -43,6 +43,14 @@ Error ASMJIT_CDECL formatRegister(
uint32_t elementType = 0,
uint32_t elementIndex = 0xFFFFFFFF) noexcept;
Error ASMJIT_CDECL formatRegisterList(
String& sb,
FormatFlags flags,
const BaseEmitter* emitter,
Arch arch,
RegType regType,
uint32_t rMask) noexcept;
Error ASMJIT_CDECL formatOperand(
String& sb,
FormatFlags flags,

View File

@ -14,8 +14,4 @@
//!
//! API shared between AArch32 & AArch64 backends.
ASMJIT_BEGIN_SUB_NAMESPACE(arm)
ASMJIT_END_SUB_NAMESPACE
#endif // ASMJIT_ARM_ARMGLOBALS_H_INCLUDED

View File

@ -19,18 +19,7 @@ ASMJIT_BEGIN_SUB_NAMESPACE(arm)
class Reg;
class Mem;
class Gp;
class GpW;
class GpX;
class Vec;
class VecB;
class VecH;
class VecS;
class VecD;
class VecV;
//! Register traits (ARM/AArch64).
//! Register traits (AArch32/AArch64).
//!
//! Register traits contains information about a particular register type. It's used by asmjit to setup register
//! information on-the-fly and to populate tables that contain register information (this way it's possible to
@ -39,27 +28,31 @@ template<RegType kRegType>
struct RegTraits : public BaseRegTraits {};
//! \cond
// <--------------------+-----+-------------------------+------------------------+---+---+------------------+
// | Reg | Reg-Type | Reg-Group |Sz |Cnt| TypeId |
// <--------------------+-----+-------------------------+------------------------+---+---+------------------+
ASMJIT_DEFINE_REG_TRAITS(GpW , RegType::kARM_GpW , RegGroup::kGp , 4 , 32, TypeId::kInt32 );
ASMJIT_DEFINE_REG_TRAITS(GpX , RegType::kARM_GpX , RegGroup::kGp , 8 , 32, TypeId::kInt64 );
ASMJIT_DEFINE_REG_TRAITS(VecB , RegType::kARM_VecB , RegGroup::kVec , 1 , 32, TypeId::kVoid );
ASMJIT_DEFINE_REG_TRAITS(VecH , RegType::kARM_VecH , RegGroup::kVec , 2 , 32, TypeId::kVoid );
ASMJIT_DEFINE_REG_TRAITS(VecS , RegType::kARM_VecS , RegGroup::kVec , 4 , 32, TypeId::kInt32x1 );
ASMJIT_DEFINE_REG_TRAITS(VecD , RegType::kARM_VecD , RegGroup::kVec , 8 , 32, TypeId::kInt32x2 );
ASMJIT_DEFINE_REG_TRAITS(VecV , RegType::kARM_VecV , RegGroup::kVec , 16, 32, TypeId::kInt32x4 );
// <--------------------+------------------------+------------------------+---+------------------+
// | Reg-Type | Reg-Group |Sz | TypeId |
// <--------------------+------------------------+------------------------+---+------------------+
ASMJIT_DEFINE_REG_TRAITS(RegType::kARM_GpW , RegGroup::kGp , 4 , TypeId::kInt32 ); // AArch32 & AArch64
ASMJIT_DEFINE_REG_TRAITS(RegType::kARM_GpX , RegGroup::kGp , 8 , TypeId::kInt64 ); // AArch64
ASMJIT_DEFINE_REG_TRAITS(RegType::kARM_VecB , RegGroup::kVec , 1 , TypeId::kVoid ); // AArch64
ASMJIT_DEFINE_REG_TRAITS(RegType::kARM_VecH , RegGroup::kVec , 2 , TypeId::kVoid ); // AArch64
ASMJIT_DEFINE_REG_TRAITS(RegType::kARM_VecS , RegGroup::kVec , 4 , TypeId::kInt32x1 ); // AArch32 & AArch64
ASMJIT_DEFINE_REG_TRAITS(RegType::kARM_VecD , RegGroup::kVec , 8 , TypeId::kInt32x2 ); // AArch32 & AArch64
ASMJIT_DEFINE_REG_TRAITS(RegType::kARM_VecQ , RegGroup::kVec , 16, TypeId::kInt32x4 ); // AArch32 & AArch64
ASMJIT_DEFINE_REG_TRAITS(RegType::kARM_PC , RegGroup::kPC , 8 , TypeId::kInt64 ); // AArch64
//! \endcond
//! Register (ARM).
//! Register operand that can represent AArch32 and AArch64 registers.
class Reg : public BaseReg {
public:
ASMJIT_DEFINE_ABSTRACT_REG(Reg, BaseReg)
//! Gets whether the register is a `R|W` register (32-bit).
//! Gets whether the register is either `R` or `W` register (32-bit).
ASMJIT_INLINE_NODEBUG constexpr bool isGpR() const noexcept { return baseSignature() == RegTraits<RegType::kARM_GpW>::kSignature; }
//! Gets whether the register is either `R` or `W` register (32-bit).
ASMJIT_INLINE_NODEBUG constexpr bool isGpW() const noexcept { return baseSignature() == RegTraits<RegType::kARM_GpW>::kSignature; }
//! Gets whether the register is an `X` register (64-bit).
ASMJIT_INLINE_NODEBUG constexpr bool isGpX() const noexcept { return baseSignature() == RegTraits<RegType::kARM_GpX>::kSignature; }
//! Gets whether the register is a VEC-B register (8-bit).
ASMJIT_INLINE_NODEBUG constexpr bool isVecB() const noexcept { return baseSignature() == RegTraits<RegType::kARM_VecB>::kSignature; }
//! Gets whether the register is a VEC-H register (16-bit).
@ -70,13 +63,22 @@ public:
ASMJIT_INLINE_NODEBUG constexpr bool isVecD() const noexcept { return baseSignature() == RegTraits<RegType::kARM_VecD>::kSignature; }
//! Gets whether the register is a VEC-Q register (128-bit).
ASMJIT_INLINE_NODEBUG constexpr bool isVecQ() const noexcept { return baseSignature() == RegTraits<RegType::kARM_VecV>::kSignature; }
//! Gets whether the register is either VEC-D (64-bit) or VEC-Q (128-bit).
ASMJIT_INLINE_NODEBUG constexpr bool isVecDOrQ() const noexcept { return uint32_t(type()) - uint32_t(RegType::kARM_VecD) <= 1u; }
//! Gets whether the register is a VEC-V register (128-bit).
ASMJIT_INLINE_NODEBUG constexpr bool isVecV() const noexcept { return baseSignature() == RegTraits<RegType::kARM_VecV>::kSignature; }
//! Gets whether the register is an 8-bit vector register or view, alias if \ref isVecB().
ASMJIT_INLINE_NODEBUG constexpr bool isVec8() const noexcept { return baseSignature() == RegTraits<RegType::kARM_VecB>::kSignature; }
//! Gets whether the register is a 16-bit vector register or view, alias if \ref isVecH().
ASMJIT_INLINE_NODEBUG constexpr bool isVec16() const noexcept { return baseSignature() == RegTraits<RegType::kARM_VecH>::kSignature; }
//! Gets whether the register is a 32-bit vector register or view, alias if \ref isVecS().
ASMJIT_INLINE_NODEBUG constexpr bool isVec32() const noexcept { return baseSignature() == RegTraits<RegType::kARM_VecS>::kSignature; }
//! Gets whether the register is a 64-bit vector register or view, alias if \ref isVecD().
ASMJIT_INLINE_NODEBUG constexpr bool isVec64() const noexcept { return baseSignature() == RegTraits<RegType::kARM_VecD>::kSignature; }
//! Gets whether the register is a 128-bit vector register or view, alias if \ref isVecQ().
ASMJIT_INLINE_NODEBUG constexpr bool isVec128() const noexcept { return baseSignature() == RegTraits<RegType::kARM_VecV>::kSignature; }
template<RegType kRegType>
ASMJIT_INLINE_NODEBUG void setRegT(uint32_t id) noexcept {
setSignature(RegTraits<kRegType>::kSignature);
@ -99,7 +101,7 @@ public:
static ASMJIT_INLINE_NODEBUG TypeId typeIdOfT() noexcept { return RegTraits<kRegType>::kTypeId; }
template<RegType kRegType>
static ASMJIT_INLINE_NODEBUG OperandSignature signatureOfT() noexcept { return RegTraits<kRegType>::kSignature; }
static ASMJIT_INLINE_NODEBUG OperandSignature signatureOfT() noexcept { return OperandSignature{RegTraits<kRegType>::kSignature}; }
static ASMJIT_INLINE_NODEBUG bool isGpW(const Operand_& op) noexcept { return op.as<Reg>().isGpW(); }
static ASMJIT_INLINE_NODEBUG bool isGpX(const Operand_& op) noexcept { return op.as<Reg>().isGpX(); }
@ -120,47 +122,12 @@ public:
static ASMJIT_INLINE_NODEBUG bool isVecV(const Operand_& op, uint32_t id) noexcept { return bool(unsigned(isVecV(op)) & unsigned(op.id() == id)); }
};
//! General purpose register (ARM).
class Gp : public Reg {
//! Vector register base - a common base for both AArch32 & AArch64 vector register.
class BaseVec : public Reg {
public:
ASMJIT_DEFINE_ABSTRACT_REG(Gp, Reg)
ASMJIT_DEFINE_ABSTRACT_REG(BaseVec, Reg)
//! Special register id.
enum Id : uint32_t {
//! Register that depends on OS, could be used as TLS offset.
kIdOs = 18,
//! Frame pointer.
kIdFp = 29,
//! Link register.
kIdLr = 30,
//! Stack register id.
kIdSp = 31,
//! Zero register id.
//!
//! Although zero register has the same id as stack register it has a special treatment, because we need to be
//! able to distinguish between these two at API level. Some intructions were designed to be used with SP and
//! some other with ZR - so we need a way to distinguish these two to make sure we emit the right thing.
//!
//! The number 63 is not random, when you perform `id & 31` you would always get 31 for both SP and ZR inputs,
//! which is the identifier used by AArch64 ISA to encode either SP or ZR depending on the instruction.
kIdZr = 63
};
ASMJIT_INLINE_NODEBUG constexpr bool isZR() const noexcept { return id() == kIdZr; }
ASMJIT_INLINE_NODEBUG constexpr bool isSP() const noexcept { return id() == kIdSp; }
//! Cast this register to a 32-bit R|W.
ASMJIT_INLINE_NODEBUG GpW w() const noexcept;
//! Cast this register to a 64-bit X.
ASMJIT_INLINE_NODEBUG GpX x() const noexcept;
};
//! Vector register (ARM).
class Vec : public Reg {
public:
ASMJIT_DEFINE_ABSTRACT_REG(Vec, Reg)
//! Additional signature bits used by arm::Vec.
//! Additional signature bits used by a vector register.
enum AdditionalBits : uint32_t {
// Register element type (3 bits).
// |........|........|.XXX....|........|
@ -178,57 +145,8 @@ public:
kSignatureRegElementIndexMask = 0x0F << kSignatureRegElementIndexShift
};
//! Element type (AArch64 only).
enum ElementType : uint32_t {
//! No element type specified.
kElementTypeNone = 0,
//! Byte elements (B8 or B16).
kElementTypeB,
//! Halfword elements (H4 or H8).
kElementTypeH,
//! Singleword elements (S2 or S4).
kElementTypeS,
//! Doubleword elements (D2).
kElementTypeD,
//! Byte elements grouped by 4 bytes (B4).
//!
//! \note This element-type is only used by few instructions.
kElementTypeB4,
//! Halfword elements grouped by 2 halfwords (H2).
//!
//! \note This element-type is only used by few instructions.
kElementTypeH2,
//! Count of element types.
kElementTypeCount
};
//! \cond
//! Shortcuts.
enum SignatureReg : uint32_t {
kSignatureElementB = kElementTypeB << kSignatureRegElementTypeShift,
kSignatureElementH = kElementTypeH << kSignatureRegElementTypeShift,
kSignatureElementS = kElementTypeS << kSignatureRegElementTypeShift,
kSignatureElementD = kElementTypeD << kSignatureRegElementTypeShift,
kSignatureElementB4 = kElementTypeB4 << kSignatureRegElementTypeShift,
kSignatureElementH2 = kElementTypeH2 << kSignatureRegElementTypeShift
};
//! \endcond
//! Returns whether the register has associated an element type.
ASMJIT_INLINE_NODEBUG constexpr bool hasElementType() const noexcept { return _signature.hasField<kSignatureRegElementTypeMask>(); }
//! Returns whether the register has element index (it's an element index access).
ASMJIT_INLINE_NODEBUG constexpr bool hasElementIndex() const noexcept { return _signature.hasField<kSignatureRegElementFlagMask>(); }
//! Returns whether the reggister has element type or element index (or both).
ASMJIT_INLINE_NODEBUG constexpr bool hasElementTypeOrIndex() const noexcept { return _signature.hasField<kSignatureRegElementTypeMask | kSignatureRegElementFlagMask>(); }
//! Returns element type of the register.
ASMJIT_INLINE_NODEBUG constexpr uint32_t elementType() const noexcept { return _signature.getField<kSignatureRegElementTypeMask>(); }
//! Sets element type of the register to `elementType`.
ASMJIT_INLINE_NODEBUG void setElementType(uint32_t elementType) noexcept { _signature.setField<kSignatureRegElementTypeMask>(elementType); }
//! Resets element type to none.
ASMJIT_INLINE_NODEBUG void resetElementType() noexcept { _signature.setField<kSignatureRegElementTypeMask>(0); }
//! Returns element index of the register.
ASMJIT_INLINE_NODEBUG constexpr uint32_t elementIndex() const noexcept { return _signature.getField<kSignatureRegElementIndexMask>(); }
//! Sets element index of the register to `elementType`.
@ -240,140 +158,8 @@ public:
ASMJIT_INLINE_NODEBUG void resetElementIndex() noexcept {
_signature &= ~(kSignatureRegElementFlagMask | kSignatureRegElementIndexMask);
}
ASMJIT_INLINE_NODEBUG constexpr bool isVecB8() const noexcept { return _signature.subset(kBaseSignatureMask | kSignatureRegElementTypeMask) == (RegTraits<RegType::kARM_VecD>::kSignature | kSignatureElementB); }
ASMJIT_INLINE_NODEBUG constexpr bool isVecH4() const noexcept { return _signature.subset(kBaseSignatureMask | kSignatureRegElementTypeMask) == (RegTraits<RegType::kARM_VecD>::kSignature | kSignatureElementH); }
ASMJIT_INLINE_NODEBUG constexpr bool isVecS2() const noexcept { return _signature.subset(kBaseSignatureMask | kSignatureRegElementTypeMask) == (RegTraits<RegType::kARM_VecD>::kSignature | kSignatureElementS); }
ASMJIT_INLINE_NODEBUG constexpr bool isVecD1() const noexcept { return _signature.subset(kBaseSignatureMask | kSignatureRegElementTypeMask) == (RegTraits<RegType::kARM_VecD>::kSignature); }
ASMJIT_INLINE_NODEBUG constexpr bool isVecB16() const noexcept { return _signature.subset(kBaseSignatureMask | kSignatureRegElementTypeMask) == (RegTraits<RegType::kARM_VecV>::kSignature | kSignatureElementB); }
ASMJIT_INLINE_NODEBUG constexpr bool isVecH8() const noexcept { return _signature.subset(kBaseSignatureMask | kSignatureRegElementTypeMask) == (RegTraits<RegType::kARM_VecV>::kSignature | kSignatureElementH); }
ASMJIT_INLINE_NODEBUG constexpr bool isVecS4() const noexcept { return _signature.subset(kBaseSignatureMask | kSignatureRegElementTypeMask) == (RegTraits<RegType::kARM_VecV>::kSignature | kSignatureElementS); }
ASMJIT_INLINE_NODEBUG constexpr bool isVecD2() const noexcept { return _signature.subset(kBaseSignatureMask | kSignatureRegElementTypeMask) == (RegTraits<RegType::kARM_VecV>::kSignature | kSignatureElementD); }
ASMJIT_INLINE_NODEBUG constexpr bool isVecB4x4() const noexcept { return _signature.subset(kBaseSignatureMask | kSignatureRegElementTypeMask) == (RegTraits<RegType::kARM_VecV>::kSignature | kSignatureElementB4); }
ASMJIT_INLINE_NODEBUG constexpr bool isVecH2x4() const noexcept { return _signature.subset(kBaseSignatureMask | kSignatureRegElementTypeMask) == (RegTraits<RegType::kARM_VecV>::kSignature | kSignatureElementH2); }
//! Creates a cloned register with element access.
ASMJIT_INLINE_NODEBUG Vec at(uint32_t elementIndex) const noexcept {
return Vec((signature() & ~kSignatureRegElementIndexMask) | (elementIndex << kSignatureRegElementIndexShift) | kSignatureRegElementFlagMask, id());
}
//! Cast this register to an 8-bit B register (AArch64 only).
ASMJIT_INLINE_NODEBUG VecB b() const noexcept;
//! Cast this register to a 16-bit H register (AArch64 only).
ASMJIT_INLINE_NODEBUG VecH h() const noexcept;
//! Cast this register to a 32-bit S register.
ASMJIT_INLINE_NODEBUG VecS s() const noexcept;
//! Cast this register to a 64-bit D register.
ASMJIT_INLINE_NODEBUG VecD d() const noexcept;
//! Cast this register to a 128-bit Q register.
ASMJIT_INLINE_NODEBUG VecV q() const noexcept;
//! Cast this register to a 128-bit V register.
ASMJIT_INLINE_NODEBUG VecV v() const noexcept;
//! Cast this register to a 128-bit V.B[elementIndex] register.
ASMJIT_INLINE_NODEBUG VecV b(uint32_t elementIndex) const noexcept;
//! Cast this register to a 128-bit V.H[elementIndex] register.
ASMJIT_INLINE_NODEBUG VecV h(uint32_t elementIndex) const noexcept;
//! Cast this register to a 128-bit V.S[elementIndex] register.
ASMJIT_INLINE_NODEBUG VecV s(uint32_t elementIndex) const noexcept;
//! Cast this register to a 128-bit V.D[elementIndex] register.
ASMJIT_INLINE_NODEBUG VecV d(uint32_t elementIndex) const noexcept;
//! Cast this register to a 128-bit V.H2[elementIndex] register.
ASMJIT_INLINE_NODEBUG VecV h2(uint32_t elementIndex) const noexcept;
//! Cast this register to a 128-bit V.B4[elementIndex] register.
ASMJIT_INLINE_NODEBUG VecV b4(uint32_t elementIndex) const noexcept;
//! Cast this register to V.8B.
ASMJIT_INLINE_NODEBUG VecD b8() const noexcept;
//! Cast this register to V.16B.
ASMJIT_INLINE_NODEBUG VecV b16() const noexcept;
//! Cast this register to V.2H.
ASMJIT_INLINE_NODEBUG VecS h2() const noexcept;
//! Cast this register to V.4H.
ASMJIT_INLINE_NODEBUG VecD h4() const noexcept;
//! Cast this register to V.8H.
ASMJIT_INLINE_NODEBUG VecV h8() const noexcept;
//! Cast this register to V.2S.
ASMJIT_INLINE_NODEBUG VecD s2() const noexcept;
//! Cast this register to V.4S.
ASMJIT_INLINE_NODEBUG VecV s4() const noexcept;
//! Cast this register to V.2D.
ASMJIT_INLINE_NODEBUG VecV d2() const noexcept;
static ASMJIT_INLINE_NODEBUG constexpr OperandSignature _makeElementAccessSignature(uint32_t elementType, uint32_t elementIndex) noexcept {
return OperandSignature{
uint32_t(RegTraits<RegType::kARM_VecV>::kSignature) |
uint32_t(kSignatureRegElementFlagMask) |
uint32_t(elementType << kSignatureRegElementTypeShift) |
uint32_t(elementIndex << kSignatureRegElementIndexShift)};
}
};
//! 32-bit GPW (AArch64) and/or GPR (ARM/AArch32) register.
class GpW : public Gp { ASMJIT_DEFINE_FINAL_REG(GpW, Gp, RegTraits<RegType::kARM_GpW>) };
//! 64-bit GPX (AArch64) register.
class GpX : public Gp { ASMJIT_DEFINE_FINAL_REG(GpX, Gp, RegTraits<RegType::kARM_GpX>) };
//! 8-bit view (S) of VFP/SIMD register.
class VecB : public Vec { ASMJIT_DEFINE_FINAL_REG(VecB, Vec, RegTraits<RegType::kARM_VecB>) };
//! 16-bit view (S) of VFP/SIMD register.
class VecH : public Vec { ASMJIT_DEFINE_FINAL_REG(VecH, Vec, RegTraits<RegType::kARM_VecH>) };
//! 32-bit view (S) of VFP/SIMD register.
class VecS : public Vec { ASMJIT_DEFINE_FINAL_REG(VecS, Vec, RegTraits<RegType::kARM_VecS>) };
//! 64-bit view (D) of VFP/SIMD register.
class VecD : public Vec { ASMJIT_DEFINE_FINAL_REG(VecD, Vec, RegTraits<RegType::kARM_VecD>) };
//! 128-bit vector register (Q or V).
class VecV : public Vec { ASMJIT_DEFINE_FINAL_REG(VecV, Vec, RegTraits<RegType::kARM_VecV>) };
ASMJIT_INLINE_NODEBUG GpW Gp::w() const noexcept { return GpW(id()); }
ASMJIT_INLINE_NODEBUG GpX Gp::x() const noexcept { return GpX(id()); }
ASMJIT_INLINE_NODEBUG VecB Vec::b() const noexcept { return VecB(id()); }
ASMJIT_INLINE_NODEBUG VecH Vec::h() const noexcept { return VecH(id()); }
ASMJIT_INLINE_NODEBUG VecS Vec::s() const noexcept { return VecS(id()); }
ASMJIT_INLINE_NODEBUG VecD Vec::d() const noexcept { return VecD(id()); }
ASMJIT_INLINE_NODEBUG VecV Vec::q() const noexcept { return VecV(id()); }
ASMJIT_INLINE_NODEBUG VecV Vec::v() const noexcept { return VecV(id()); }
ASMJIT_INLINE_NODEBUG VecV Vec::b(uint32_t elementIndex) const noexcept { return VecV(_makeElementAccessSignature(kElementTypeB, elementIndex), id()); }
ASMJIT_INLINE_NODEBUG VecV Vec::h(uint32_t elementIndex) const noexcept { return VecV(_makeElementAccessSignature(kElementTypeH, elementIndex), id()); }
ASMJIT_INLINE_NODEBUG VecV Vec::s(uint32_t elementIndex) const noexcept { return VecV(_makeElementAccessSignature(kElementTypeS, elementIndex), id()); }
ASMJIT_INLINE_NODEBUG VecV Vec::d(uint32_t elementIndex) const noexcept { return VecV(_makeElementAccessSignature(kElementTypeD, elementIndex), id()); }
ASMJIT_INLINE_NODEBUG VecV Vec::h2(uint32_t elementIndex) const noexcept { return VecV(_makeElementAccessSignature(kElementTypeH2, elementIndex), id()); }
ASMJIT_INLINE_NODEBUG VecV Vec::b4(uint32_t elementIndex) const noexcept { return VecV(_makeElementAccessSignature(kElementTypeB4, elementIndex), id()); }
ASMJIT_INLINE_NODEBUG VecD Vec::b8() const noexcept { return VecD(OperandSignature{VecD::kSignature | kSignatureElementB}, id()); }
ASMJIT_INLINE_NODEBUG VecS Vec::h2() const noexcept { return VecS(OperandSignature{VecS::kSignature | kSignatureElementH}, id()); }
ASMJIT_INLINE_NODEBUG VecD Vec::h4() const noexcept { return VecD(OperandSignature{VecD::kSignature | kSignatureElementH}, id()); }
ASMJIT_INLINE_NODEBUG VecD Vec::s2() const noexcept { return VecD(OperandSignature{VecD::kSignature | kSignatureElementS}, id()); }
ASMJIT_INLINE_NODEBUG VecV Vec::b16() const noexcept { return VecV(OperandSignature{VecV::kSignature | kSignatureElementB}, id()); }
ASMJIT_INLINE_NODEBUG VecV Vec::h8() const noexcept { return VecV(OperandSignature{VecV::kSignature | kSignatureElementH}, id()); }
ASMJIT_INLINE_NODEBUG VecV Vec::s4() const noexcept { return VecV(OperandSignature{VecV::kSignature | kSignatureElementS}, id()); }
ASMJIT_INLINE_NODEBUG VecV Vec::d2() const noexcept { return VecV(OperandSignature{VecV::kSignature | kSignatureElementD}, id()); }
#ifndef _DOXYGEN
namespace regs {
#endif
//! Creates a 32-bit W register operand (ARM/AArch64).
static ASMJIT_INLINE_NODEBUG constexpr GpW w(uint32_t id) noexcept { return GpW(id); }
//! Creates a 64-bit X register operand (AArch64).
static ASMJIT_INLINE_NODEBUG constexpr GpX x(uint32_t id) noexcept { return GpX(id); }
//! Creates a 32-bit S register operand (ARM/AArch64).
static ASMJIT_INLINE_NODEBUG constexpr VecS s(uint32_t id) noexcept { return VecS(id); }
//! Creates a 64-bit D register operand (ARM/AArch64).
static ASMJIT_INLINE_NODEBUG constexpr VecD d(uint32_t id) noexcept { return VecD(id); }
//! Creates a 1282-bit V register operand (ARM/AArch64).
static ASMJIT_INLINE_NODEBUG constexpr VecV v(uint32_t id) noexcept { return VecV(id); }
#ifndef _DOXYGEN
} // {regs}
// Make `arm::regs` accessible through `arm` namespace as well.
using namespace regs;
#endif
//! Memory operand (ARM).
class Mem : public BaseMem {
public:
@ -385,23 +171,18 @@ public:
kSignatureMemShiftValueShift = 14,
kSignatureMemShiftValueMask = 0x1Fu << kSignatureMemShiftValueShift,
// Shift operation type (4 bits).
// Index shift operation (4 bits).
// |........|XXXX....|........|........|
kSignatureMemPredicateShift = 20,
kSignatureMemPredicateMask = 0x0Fu << kSignatureMemPredicateShift
kSignatureMemShiftOpShift = 20,
kSignatureMemShiftOpMask = 0x0Fu << kSignatureMemShiftOpShift,
// Offset mode type (2 bits).
// |......XX|........|........|........|
kSignatureMemOffsetModeShift = 24,
kSignatureMemOffsetModeMask = 0x03u << kSignatureMemOffsetModeShift
};
//! \endcond
//! Memory offset mode.
//!
//! Additional constants that can be used with the `predicate`.
enum OffsetMode : uint32_t {
//! Pre-index "[BASE, #Offset {, <shift>}]!" with write-back.
kOffsetPreIndex = 0xE,
//! Post-index "[BASE], #Offset {, <shift>}" with write-back.
kOffsetPostIndex = 0xF
};
//! \name Construction & Destruction
//! \{
@ -438,11 +219,11 @@ public:
: BaseMem(Signature::fromOpType(OperandType::kMem) |
Signature::fromMemBaseType(base.type()) |
Signature::fromMemIndexType(index.type()) |
Signature::fromValue<kSignatureMemPredicateMask>(uint32_t(shift.op())) |
Signature::fromValue<kSignatureMemShiftOpMask>(uint32_t(shift.op())) |
Signature::fromValue<kSignatureMemShiftValueMask>(shift.value()) |
signature, base.id(), index.id(), 0) {}
ASMJIT_INLINE_NODEBUG constexpr Mem(uint64_t base, Signature signature = Signature{0}) noexcept
ASMJIT_INLINE_NODEBUG constexpr explicit Mem(uint64_t base, Signature signature = Signature{0}) noexcept
: BaseMem(Signature::fromOpType(OperandType::kMem) |
signature, uint32_t(base >> 32), 0, int32_t(uint32_t(base & 0xFFFFFFFFu))) {}
@ -471,14 +252,14 @@ public:
//! Clones the memory operand and makes it pre-index.
ASMJIT_INLINE_NODEBUG Mem pre() const noexcept {
Mem result(*this);
result.setPredicate(kOffsetPreIndex);
result.setOffsetMode(OffsetMode::kPreIndex);
return result;
}
//! Clones the memory operand, applies a given offset `off` and makes it pre-index.
ASMJIT_INLINE_NODEBUG Mem pre(int64_t off) const noexcept {
Mem result(*this);
result.setPredicate(kOffsetPreIndex);
result.setOffsetMode(OffsetMode::kPreIndex);
result.addOffset(off);
return result;
}
@ -486,14 +267,14 @@ public:
//! Clones the memory operand and makes it post-index.
ASMJIT_INLINE_NODEBUG Mem post() const noexcept {
Mem result(*this);
result.setPredicate(kOffsetPostIndex);
result.setOffsetMode(OffsetMode::kPostIndex);
return result;
}
//! Clones the memory operand, applies a given offset `off` and makes it post-index.
ASMJIT_INLINE_NODEBUG Mem post(int64_t off) const noexcept {
Mem result(*this);
result.setPredicate(kOffsetPostIndex);
result.setOffsetMode(OffsetMode::kPostIndex);
result.addOffset(off);
return result;
}
@ -520,88 +301,85 @@ public:
setShift(shift);
}
ASMJIT_INLINE_NODEBUG void setIndex(const BaseReg& index, Shift shift) noexcept {
setIndex(index);
setShift(shift);
}
//! \}
//! \name ARM Specific Features
//! \{
//! Gets offset mode.
ASMJIT_INLINE_NODEBUG constexpr OffsetMode offsetMode() const noexcept { return OffsetMode(_signature.getField<kSignatureMemOffsetModeMask>()); }
//! Sets offset mode to `mode`.
ASMJIT_INLINE_NODEBUG void setOffsetMode(OffsetMode mode) noexcept { _signature.setField<kSignatureMemOffsetModeMask>(uint32_t(mode)); }
//! Resets offset mode to default (fixed offset, without write-back).
ASMJIT_INLINE_NODEBUG void resetOffsetMode() noexcept { _signature.setField<kSignatureMemOffsetModeMask>(uint32_t(OffsetMode::kFixed)); }
//! Tests whether the current memory offset mode is fixed (see \ref OffsetMode::kFixed).
ASMJIT_INLINE_NODEBUG constexpr bool isFixedOffset() const noexcept { return offsetMode() == OffsetMode::kFixed; }
//! Tests whether the current memory offset mode is either pre-index or post-index (write-back is used).
ASMJIT_INLINE_NODEBUG constexpr bool isPreOrPost() const noexcept { return offsetMode() != OffsetMode::kFixed; }
//! Tests whether the current memory offset mode is pre-index (write-back is used).
ASMJIT_INLINE_NODEBUG constexpr bool isPreIndex() const noexcept { return offsetMode() == OffsetMode::kPreIndex; }
//! Tests whether the current memory offset mode is post-index (write-back is used).
ASMJIT_INLINE_NODEBUG constexpr bool isPostIndex() const noexcept { return offsetMode() == OffsetMode::kPostIndex; }
//! Sets offset mode of this memory operand to pre-index (write-back is used).
ASMJIT_INLINE_NODEBUG void makePreIndex() noexcept { setOffsetMode(OffsetMode::kPreIndex); }
//! Sets offset mode of this memory operand to post-index (write-back is used).
ASMJIT_INLINE_NODEBUG void makePostIndex() noexcept { setOffsetMode(OffsetMode::kPostIndex); }
//! Gets shift operation that is used by index register.
ASMJIT_INLINE_NODEBUG constexpr ShiftOp shiftOp() const noexcept { return ShiftOp(_signature.getField<kSignatureMemShiftOpMask>()); }
//! Sets shift operation that is used by index register.
ASMJIT_INLINE_NODEBUG void setShiftOp(ShiftOp sop) noexcept { _signature.setField<kSignatureMemShiftOpMask>(uint32_t(sop)); }
//! Resets shift operation that is used by index register to LSL (default value).
ASMJIT_INLINE_NODEBUG void resetShiftOp() noexcept { _signature.setField<kSignatureMemShiftOpMask>(uint32_t(ShiftOp::kLSL)); }
//! Gets whether the memory operand has shift (aka scale) constant.
ASMJIT_INLINE_NODEBUG constexpr bool hasShift() const noexcept { return _signature.hasField<kSignatureMemShiftValueMask>(); }
//! Gets the memory operand's shift (aka scale) constant.
ASMJIT_INLINE_NODEBUG constexpr uint32_t shift() const noexcept { return _signature.getField<kSignatureMemShiftValueMask>(); }
//! Sets the memory operand's shift (aka scale) constant.
ASMJIT_INLINE_NODEBUG void setShift(uint32_t shift) noexcept { _signature.setField<kSignatureMemShiftValueMask>(shift); }
//! Sets the memory operand's shift and shift operation.
ASMJIT_INLINE_NODEBUG void setShift(Shift shift) noexcept {
_signature.setField<kSignatureMemShiftOpMask>(uint32_t(shift.op()));
_signature.setField<kSignatureMemShiftValueMask>(shift.value());
}
//! Resets the memory operand's shift (aka scale) constant to zero.
ASMJIT_INLINE_NODEBUG void resetShift() noexcept { _signature.setField<kSignatureMemShiftValueMask>(0); }
//! Gets memory predicate (shift mode or offset mode), see \ref ShiftOp and \ref OffsetMode.
ASMJIT_INLINE_NODEBUG constexpr uint32_t predicate() const noexcept { return _signature.getField<kSignatureMemPredicateMask>(); }
//! Sets memory predicate to `predicate`, see `Mem::ShiftOp`.
ASMJIT_INLINE_NODEBUG void setPredicate(uint32_t predicate) noexcept { _signature.setField<kSignatureMemPredicateMask>(predicate); }
//! Resets shift mode to LSL (default).
ASMJIT_INLINE_NODEBUG void resetPredicate() noexcept { _signature.setField<kSignatureMemPredicateMask>(0); }
ASMJIT_INLINE_NODEBUG constexpr bool isFixedOffset() const noexcept { return predicate() < kOffsetPreIndex; }
ASMJIT_INLINE_NODEBUG constexpr bool isPreOrPost() const noexcept { return predicate() >= kOffsetPreIndex; }
ASMJIT_INLINE_NODEBUG constexpr bool isPreIndex() const noexcept { return predicate() == kOffsetPreIndex; }
ASMJIT_INLINE_NODEBUG constexpr bool isPostIndex() const noexcept { return predicate() == kOffsetPostIndex; }
ASMJIT_INLINE_NODEBUG void resetToFixedOffset() noexcept { resetPredicate(); }
ASMJIT_INLINE_NODEBUG void makePreIndex() noexcept { setPredicate(kOffsetPreIndex); }
ASMJIT_INLINE_NODEBUG void makePostIndex() noexcept { setPredicate(kOffsetPostIndex); }
//! \}
};
//! Creates `[base, offset]` memory operand (offset mode).
static ASMJIT_INLINE_NODEBUG constexpr Mem ptr(const Gp& base, int32_t offset = 0) noexcept {
return Mem(base, offset);
}
//! \name Shift Operation Construction
//! \{
//! Creates `[base, offset]!` memory operand (pre-index mode).
static ASMJIT_INLINE_NODEBUG constexpr Mem ptr_pre(const Gp& base, int32_t offset = 0) noexcept {
return Mem(base, offset, OperandSignature::fromValue<Mem::kSignatureMemPredicateMask>(Mem::kOffsetPreIndex));
}
//! Constructs a `LSL #value` shift (logical shift left).
static ASMJIT_INLINE_NODEBUG constexpr Shift lsl(uint32_t value) noexcept { return Shift(ShiftOp::kLSL, value); }
//! Constructs a `LSR #value` shift (logical shift right).
static ASMJIT_INLINE_NODEBUG constexpr Shift lsr(uint32_t value) noexcept { return Shift(ShiftOp::kLSR, value); }
//! Constructs a `ASR #value` shift (arithmetic shift right).
static ASMJIT_INLINE_NODEBUG constexpr Shift asr(uint32_t value) noexcept { return Shift(ShiftOp::kASR, value); }
//! Constructs a `ROR #value` shift (rotate right).
static ASMJIT_INLINE_NODEBUG constexpr Shift ror(uint32_t value) noexcept { return Shift(ShiftOp::kROR, value); }
//! Constructs a `RRX` shift (rotate with carry by 1).
static ASMJIT_INLINE_NODEBUG constexpr Shift rrx() noexcept { return Shift(ShiftOp::kRRX, 0); }
//! Constructs a `MSL #value` shift (logical shift left filling ones).
static ASMJIT_INLINE_NODEBUG constexpr Shift msl(uint32_t value) noexcept { return Shift(ShiftOp::kMSL, value); }
//! Creates `[base], offset` memory operand (post-index mode).
static ASMJIT_INLINE_NODEBUG constexpr Mem ptr_post(const Gp& base, int32_t offset = 0) noexcept {
return Mem(base, offset, OperandSignature::fromValue<Mem::kSignatureMemPredicateMask>(Mem::kOffsetPostIndex));
}
//! \}
//! Creates `[base, index]` memory operand.
static ASMJIT_INLINE_NODEBUG constexpr Mem ptr(const Gp& base, const Gp& index) noexcept {
return Mem(base, index);
}
//! \name Memory Operand Construction
//! \{
//! Creates `[base, index]!` memory operand (pre-index mode).
static ASMJIT_INLINE_NODEBUG constexpr Mem ptr_pre(const Gp& base, const Gp& index) noexcept {
return Mem(base, index, OperandSignature::fromValue<Mem::kSignatureMemPredicateMask>(Mem::kOffsetPreIndex));
}
//! Creates `[base], index` memory operand (post-index mode).
static ASMJIT_INLINE_NODEBUG constexpr Mem ptr_post(const Gp& base, const Gp& index) noexcept {
return Mem(base, index, OperandSignature::fromValue<Mem::kSignatureMemPredicateMask>(Mem::kOffsetPostIndex));
}
//! Creates `[base, index, SHIFT_OP #shift]` memory operand.
static ASMJIT_INLINE_NODEBUG constexpr Mem ptr(const Gp& base, const Gp& index, const Shift& shift) noexcept {
return Mem(base, index, shift);
}
//! Creates `[base, offset]` memory operand.
static ASMJIT_INLINE_NODEBUG constexpr Mem ptr(const Label& base, int32_t offset = 0) noexcept {
return Mem(base, offset);
}
// TODO: [ARM] PC + offset address.
#if 0
//! Creates `[PC + offset]` (relative) memory operand.
static ASMJIT_INLINE_NODEBUG constexpr Mem ptr(const PC& pc, int32_t offset = 0) noexcept {
return Mem(pc, offset);
}
#endif
//! Creates `[base]` absolute memory operand.
//! Creates `[base]` absolute memory operand (AArch32 or AArch64).
//!
//! \note The concept of absolute memory operands doesn't exist on ARM, the ISA only provides PC relative addressing.
//! Absolute memory operands can only be used if it's known that the PC relative offset is encodable and that it
@ -611,16 +389,8 @@ static ASMJIT_INLINE_NODEBUG constexpr Mem ptr(uint64_t base) noexcept { return
//! \}
//! \}
ASMJIT_END_SUB_NAMESPACE
//! \cond INTERNAL
ASMJIT_BEGIN_NAMESPACE
ASMJIT_DEFINE_TYPE_ID(arm::GpW, TypeId::kInt32);
ASMJIT_DEFINE_TYPE_ID(arm::GpX, TypeId::kInt64);
ASMJIT_DEFINE_TYPE_ID(arm::VecS, TypeId::kFloat32x1);
ASMJIT_DEFINE_TYPE_ID(arm::VecD, TypeId::kFloat64x1);
ASMJIT_DEFINE_TYPE_ID(arm::VecV, TypeId::kInt32x4);
ASMJIT_END_NAMESPACE
//! \endcond
#endif // ASMJIT_ARM_ARMOPERAND_H_INCLUDED

View File

@ -6,6 +6,7 @@
#ifndef ASMJIT_ARM_ARMUTILS_H_INCLUDED
#define ASMJIT_ARM_ARMUTILS_H_INCLUDED
#include "../core/support.h"
#include "../arm/armglobals.h"
ASMJIT_BEGIN_SUB_NAMESPACE(arm)
@ -16,6 +17,38 @@ ASMJIT_BEGIN_SUB_NAMESPACE(arm)
//! Public utilities and helpers for targeting AArch32 and AArch64 architectures.
namespace Utils {
//! Encodes a 12-bit immediate part of opcode that ise used by a standard 32-bit ARM encoding.
ASMJIT_MAYBE_UNUSED
static inline bool encodeAArch32Imm(uint64_t imm, uint32_t* encodedImmOut) noexcept {
if (imm & 0xFFFFFFFF00000000u)
return false;
uint32_t v = uint32_t(imm);
uint32_t r = 0;
if (v <= 0xFFu) {
*encodedImmOut = v;
return true;
}
// Rotate if there are bits on both ends (LSB and MSB)
// (otherwise we would not be able to calculate the rotation with ctz).
if (v & 0xFF0000FFu) {
v = Support::ror(v, 16);
r = 16u;
}
uint32_t n = Support::ctz(v) & ~0x1u;
r = (r - n) & 0x1Eu;
v = Support::ror(v, n);
if (v > 0xFFu)
return false;
*encodedImmOut = v | (r << 7);
return true;
}
//! Decomposed fields of a logical immediate value.
struct LogicalImm {
uint32_t n;
@ -94,6 +127,13 @@ static ASMJIT_INLINE_NODEBUG bool isLogicalImm(uint64_t imm, uint32_t width) noe
return encodeLogicalImm(imm, width, &dummy);
}
//! Returns true if the given `imm` value is encodable as an immediate with `add` and `sub` instructions on AArch64.
//! These two instructions can encode 12-bit immediate value optionally shifted left by 12 bits.
ASMJIT_MAYBE_UNUSED
static ASMJIT_INLINE_NODEBUG bool isAddSubImm(uint64_t imm) noexcept {
return imm <= 0xFFFu || (imm & ~uint64_t(0xFFFu << 12)) == 0;
}
//! Returns true if the given `imm` value is a byte mask. Byte mask has each byte part of the value set to either
//! 0x00 or 0xFF. Some ARM instructions accept immediates that form a byte-mask and this function can be used to
//! verify that the immediate is encodable before using the value.

View File

@ -3,7 +3,7 @@
// SPDX-License-Identifier: Zlib
// Official GitHub Repository: https://github.com/asmjit/asmjit
//
// Copyright (c) 2008-2021 The AsmJit Authors
// Copyright (c) 2008-2024 The AsmJit Authors
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages

View File

@ -179,38 +179,47 @@ namespace asmjit {
//! AsmJit currently supports only X86/X64 backend, but the plan is to add more backends in the future. By default
//! AsmJit builds only the host backend, which is auto-detected at compile-time, but this can be overridden.
//!
//! - \ref ASMJIT_NO_X86 - Disable X86/X64 backends.
//! - \ref ASMJIT_NO_FOREIGN - Disables the support for foreign architectures.
//! - \ref ASMJIT_NO_X86 - Disables both X86 and X86_64 backends.
//! - \ref ASMJIT_NO_AARCH64 - Disables AArch64 backend.
//! - \ref ASMJIT_NO_FOREIGN - Disables the support for foreign architecture backends, only keeps a native backend.
//!
//! ### AsmJit Compilation Options
//!
//! - \ref ASMJIT_NO_DEPRECATED - Disables deprecated API at compile time so it won't be available and the
//! compilation will fail if there is attempt to use such API. This includes deprecated classes, namespaces,
//! enumerations, and functions.
//!
//! - \ref ASMJIT_NO_SHM_OPEN - Disables functionality that uses `shm_open()`.
//!
//! - \ref ASMJIT_NO_ABI_NAMESPACE - Disables inline ABI namespace within `asmjit` namespace. This is only provided
//! for users that control all the dependencies (even transitive ones) and that make sure that no two AsmJit
//! versions are used at the same time. This option can be debugging a little simpler as there would not be ABI
//! tag after `asmjit::` namespace. Otherwise asmjit would look like `asmjit::_abi_1_13::`, for example.
//!
//! ### Features Selection
//!
//! AsmJit builds by defaults all supported features, which includes all emitters, logging, instruction validation and
//! introspection, and JIT memory allocation. Features can be disabled at compile time by using `ASMJIT_NO_...`
//! definitions.
//!
//! - \ref ASMJIT_NO_DEPRECATED - Disables deprecated API at compile time so it won't be available and the
//! compilation will fail if there is attempt to use such API. This includes deprecated classes, namespaces,
//! enumerations, and functions.
//!
//! - \ref ASMJIT_NO_BUILDER - Disables \ref asmjit_builder functionality completely. This implies \ref
//! ASMJIT_NO_COMPILER as \ref asmjit_compiler cannot be used without \ref asmjit_builder.
//!
//! - \ref ASMJIT_NO_COMPILER - Disables \ref asmjit_compiler functionality completely.
//!
//! - \ref ASMJIT_NO_JIT - Disables JIT memory management and \ref JitRuntime.
//!
//! - \ref ASMJIT_NO_LOGGING - Disables \ref Logger and \ref Formatter.
//!
//! - \ref ASMJIT_NO_TEXT - Disables everything that contains string representation of AsmJit constants, should
//! be used together with \ref ASMJIT_NO_LOGGING as logging doesn't make sense without the ability to query
//! instruction names, register names, etc...
//!
//! - \ref ASMJIT_NO_LOGGING - Disables \ref Logger and \ref Formatter.
//!
//! - \ref ASMJIT_NO_VALIDATION - Disables validation API.
//!
//! - \ref ASMJIT_NO_INTROSPECTION - Disables instruction introspection API, must be used together with \ref
//! ASMJIT_NO_COMPILER as \ref asmjit_compiler requires introspection for its liveness analysis and register
//! allocation.
//!
//! - \ref ASMJIT_NO_BUILDER - Disables \ref asmjit_builder functionality completely. This implies \ref
//! ASMJIT_NO_COMPILER as \ref asmjit_compiler cannot be used without \ref asmjit_builder.
//!
//! - \ref ASMJIT_NO_COMPILER - Disables \ref asmjit_compiler functionality completely.
//!
//! \note It's not recommended to disable features if you plan to build AsmJit as a shared library that will be
//! used by multiple projects that you don't control how AsmJit was built (for example AsmJit in a Linux distribution).
//! The possibility to disable certain features exists mainly for customized AsmJit builds.
@ -231,7 +240,7 @@ namespace asmjit {
//!
//! Useful tips before you start:
//!
//! - Visit our [Public Gitter Channel](https://gitter.im/asmjit/asmjit) if you need a quick help.
//! - Visit our [Public Gitter Chat](https://app.gitter.im/#/room/#asmjit:gitter.im) if you need a quick help.
//!
//! - Build AsmJit with `ASMJIT_NO_DEPRECATED` macro defined to make sure that you are not using deprecated
//! functionality at all. Deprecated functions are decorated with `ASMJIT_DEPRECATED()` macro, but sometimes
@ -239,6 +248,47 @@ namespace asmjit {
//! because some compilers would warn about that. If your project compiles fine with `ASMJIT_NO_DEPRECATED`
//! it's not using anything, which was deprecated.
//!
//! ### Changes committed at 2024-01-01
//!
//! Core changes:
//!
//! - Renamed equality functions `eq()` to `equals()` - Only related to `String`, `ZoneVector`, and `CpuFeatures`.
//! Old function names were deprecated.
//!
//! - Removed `CallConvId::kNone` in favor of `CallConvId::kCDecl`, which is now the default calling convention.
//!
//! - Deprecated `CallConvId::kHost` in favor of `CallConvId::kCDecl` - host calling convention is now not part
//! of CallConvId, it can be calculated from CallConvId and Environment instead.
//!
//! ### Changes committed at 2023-12-27
//!
//! Core changes:
//!
//! - Renamed `a64::Vec::ElementType` to `a64::VecElementType` and made it a typed enum. This enum was used mostly
//! internally, but there is a public API using it, so it's a breaking change.
//!
//! - Refactored `FuncSignature`, `FuncSignatureT`, and `FuncSignatureBuilder`. There is only `FuncSignature` now,
//! which acts as a function signature holder and builder. Replace `FuncSignatureBuilder` with `FuncSignature`
//! and use `FuncSignature::build<args>` instead of `FuncSignatureT<args>`. The old API has been deprecated.
//!
//! - The maximum number of function arguments was raised from 16 to 32.
//!
//! ### Changes committed at 2023-12-26
//!
//! Core changes:
//!
//! - Reworked InstNode and InstExNode to be friendlier to static analysis and to not cause undefined behavior.
//! InstNode has no operands visually embedded within the struct so there is no _opArray (which was internal).
//! This means that sizeof(InstNode) changed, but since it's allocated by AsmJit this should be fine. Moreover,
//! there is no longer InstExNode as that was more a hack, instead there is now InstNodeWithOperands, which is
//! a template and specifies the number of operands embedded (InstNode accesses these). All nodes that inherited
//! InstExNode now just inherit InstNodeWithOperands<InstNode::kBaseOpCapacity>, which would provide the same
//! number of nodes as InstNode.
//!
//! - Moved GP and Vec registers from asmjit::arm namespace to asmjit::a64 namespace. At this time there was
//! no prior deprecation as having arm::Vec would collide with a64::Vec as arm namespace is used within a64
//! namespace. Just change `arm::Gp` to `a64::Gp` and `arm::Vec` to `a64::Vec`.
//!
//! ### Changes committed at 2023-09-10
//!
//! Core changes:
@ -419,7 +469,7 @@ namespace asmjit {
//! // Calling a function (Compiler) changed - use invoke() instead of call().
//! void functionInvocation(x86::Compiler& cc) {
//! InvokeNode* invokeNode;
//! cc.invoke(&invokeNode, targetOperand, FuncSignatureT<...>(...));
//! cc.invoke(&invokeNode, targetOperand, FuncSignature::build<...>(...));
//! }
//! ```
@ -916,12 +966,15 @@ namespace asmjit {
//! with assembler requires the knowledge of the following:
//!
//! - \ref BaseAssembler and architecture-specific assemblers:
//! - \ref x86::Assembler - Assembler specific to X86 architecture
//! - \ref x86::Assembler - Assembler implementation targeting X86 and X86_64 architectures.
//! - \ref a64::Assembler - Assembler implementation targeting AArch64 architecture.
//! - \ref Operand and its variations:
//! - \ref BaseReg - Base class for a register operand, inherited by:
//! - \ref x86::Reg - Register operand specific to X86 architecture.
//! - \ref x86::Reg - Register operand specific to X86 and X86_64 architectures.
//! - \ref arm::Reg - Register operand specific to AArch64 architecture.
//! - \ref BaseMem - Base class for a memory operand, inherited by:
//! - \ref x86::Mem - Memory operand specific to X86 architecture.
//! - \ref arm::Mem - Memory operand specific to AArch64 architecture.
//! - \ref Imm - Immediate (value) operand.
//! - \ref Label - Label operand.
//!
@ -1029,7 +1082,7 @@ namespace asmjit {
//!
//! // Type-unsafe, but possible.
//! a.emit(x86::Inst::kIdMov, dst, m);
//! // Also possible, `emit()` is typeless and can be used with raw Operand.
//! // Also possible, `emit()` is type-less and can be used with raw Operand.
//! a.emit(x86::Inst::kIdMov, dst, op);
//! }
//! ```
@ -1100,10 +1153,10 @@ namespace asmjit {
//!
//! void testX86Mem() {
//! // The same as: dword ptr [rax + rbx].
//! x86::Mem a = x86::dword_ptr(rax, rbx);
//! x86::Mem a = x86::dword_ptr(x86::rax, x86::rbx);
//!
//! // The same as: qword ptr [rdx + rsi << 0 + 1].
//! x86::Mem b = x86::qword_ptr(rdx, rsi, 0, 1);
//! x86::Mem b = x86::qword_ptr(x86::rdx, x86::rsi, 0, 1);
//! }
//! ```
//!
@ -1116,18 +1169,18 @@ namespace asmjit {
//!
//! void testX86Mem() {
//! // The same as: dword ptr [rax + 12].
//! x86::Mem mem = x86::dword_ptr(rax, 12);
//! x86::Mem mem = x86::dword_ptr(x86::rax, 12);
//!
//! mem.hasBase(); // true.
//! mem.hasIndex(); // false.
//! mem.size(); // 4.
//! mem.offset(); // 12.
//!
//! mem.setSize(0); // Sets the size to 0 (makes it sizeless).
//! mem.setSize(0); // Sets the size to 0 (makes it size-less).
//! mem.addOffset(-1); // Adds -1 to the offset and makes it 11.
//! mem.setOffset(0); // Sets the offset to 0.
//! mem.setBase(rcx); // Changes BASE to RCX.
//! mem.setIndex(rax); // Changes INDEX to RAX.
//! mem.setBase(x86::rcx); // Changes BASE to RCX.
//! mem.setIndex(x86::rax); // Changes INDEX to RAX.
//! mem.hasIndex(); // true.
//! }
//! // ...
@ -1219,7 +1272,8 @@ namespace asmjit {
//!
//! ### Builder Examples
//!
//! - \ref x86::Builder provides many X86/X64 examples.
//! - \ref x86::Builder - Builder implementation targeting X86 and X86_64 architectures.
//! - \ref a64::Builder - Builder implementation targeting AArch64 architecture.
//! \defgroup asmjit_compiler Compiler
@ -1256,7 +1310,8 @@ namespace asmjit {
//!
//! ### Compiler Examples
//!
//! - \ref x86::Compiler provides many X86/X64 examples.
//! - \ref x86::Compiler - Compiler implementation targeting X86 and X86_64 architectures.
//! - \ref a64::Compiler - Compiler implementation targeting AArch64 architecture.
//!
//! ### Compiler Tips
//!
@ -1428,7 +1483,7 @@ namespace asmjit {
//! The first example illustrates how to format operands:
//!
//! ```
//! #include <asmjit/core.h>
//! #include <asmjit/x86.h>
//! #include <stdio.h>
//!
//! using namespace asmjit;
@ -1453,17 +1508,17 @@ namespace asmjit {
//! // compatible with what AsmJit normally does.
//! Arch arch = Arch::kX64;
//!
//! log(arch, rax); // Prints 'rax'.
//! log(arch, ptr(rax, rbx, 2)); // Prints '[rax + rbx * 4]`.
//! log(arch, dword_ptr(rax, rbx, 2)); // Prints 'dword [rax + rbx * 4]`.
//! log(arch, imm(42)); // Prints '42'.
//! logOperand(arch, rax); // Prints 'rax'.
//! logOperand(arch, ptr(rax, rbx, 2)); // Prints '[rax + rbx * 4]`.
//! logOperand(arch, dword_ptr(rax, rbx, 2)); // Prints 'dword [rax + rbx * 4]`.
//! logOperand(arch, imm(42)); // Prints '42'.
//! }
//! ```
//!
//! Next example illustrates how to format whole instructions:
//!
//! ```
//! #include <asmjit/core.h>
//! #include <asmjit/x86.h>
//! #include <stdio.h>
//! #include <utility>
//!
@ -1478,7 +1533,7 @@ namespace asmjit {
//! FormatFlags formatFlags = FormatFlags::kNone;
//!
//! // The formatter expects operands in an array.
//! Operand_ operands { std::forward<Args>(args)... };
//! Operand_ operands[] { std::forward<Args>(args)... };
//!
//! StringTmp<128> sb;
//! Formatter::formatInstruction(
@ -1500,13 +1555,13 @@ namespace asmjit {
//! // Prints 'vaddpd zmm0, zmm1, [rax] {1to8}'.
//! logInstruction(arch,
//! BaseInst(Inst::kIdVaddpd),
//! zmm0, zmm1, ptr(rax)._1toN());
//! zmm0, zmm1, ptr(rax)._1to8());
//!
//! // BaseInst abstracts instruction id, instruction options, and extraReg.
//! // Prints 'lock add [rax], rcx'.
//! logInstruction(arch,
//! BaseInst(Inst::kIdAdd, InstOptions::kX86_Lock),
//! x86::ptr(rax), rcx);
//! ptr(rax), rcx);
//!
//! // Similarly an extra register (like AVX-512 selector) can be used.
//! // Prints 'vaddpd zmm0 {k2} {z}, zmm1, [rax]'.
@ -1624,7 +1679,7 @@ namespace asmjit {
//!
//! Each instruction can be then queried for the following information:
//!
//! - \ref InstRWInfo - Read/write information of instruction and its oprands (includes \ref OpRWInfo).
//! - \ref InstRWInfo - Read/write information of instruction and its operands (includes \ref OpRWInfo).
//!
//! - \ref CpuFeatures - CPU features required to execute the instruction.
//!

View File

@ -16,7 +16,7 @@
#define ASMJIT_LIBRARY_MAKE_VERSION(major, minor, patch) ((major << 16) | (minor << 8) | (patch))
//! AsmJit library version, see \ref ASMJIT_LIBRARY_MAKE_VERSION for a version format reference.
#define ASMJIT_LIBRARY_VERSION ASMJIT_LIBRARY_MAKE_VERSION(1, 11, 0)
#define ASMJIT_LIBRARY_VERSION ASMJIT_LIBRARY_MAKE_VERSION(1, 13, 0)
//! \def ASMJIT_ABI_NAMESPACE
//!
@ -27,7 +27,7 @@
//! AsmJit default, which makes it possible to use multiple AsmJit libraries within a single project, totally
//! controlled by users. This is useful especially in cases in which some of such library comes from third party.
#if !defined(ASMJIT_ABI_NAMESPACE)
#define ASMJIT_ABI_NAMESPACE _abi_1_11
#define ASMJIT_ABI_NAMESPACE _abi_1_13
#endif // !ASMJIT_ABI_NAMESPACE
//! \}
@ -42,7 +42,7 @@
#include <stdlib.h>
#include <string.h>
#include <iterator>
#include <initializer_list>
#include <limits>
#include <type_traits>
#include <utility>
@ -112,7 +112,7 @@ namespace asmjit {
#define ASMJIT_NO_INTROSPECTION
// Avoid doxygen preprocessor using feature-selection definitions.
#undef ASMJIT_BUILD_EMBNED
#undef ASMJIT_BUILD_EMBED
#undef ASMJIT_BUILD_STATIC
#undef ASMJIT_BUILD_DEBUG
#undef ASMJIT_BUILD_RELEASE
@ -163,6 +163,41 @@ namespace asmjit {
// Target Architecture Detection
// =============================
//! \addtogroup asmjit_core
//! \{
//! \def ASMJIT_ARCH_X86
//!
//! Defined to either 0, 32, or 64 depending on whether the target CPU is X86 (32) or X86_64 (64).
//! \def ASMJIT_ARCH_ARM
//!
//! Defined to either 0, 32, or 64 depending on whether the target CPU is ARM (32) or AArch64 (64).
//! \def ASMJIT_ARCH_MIPS
//!
//! Defined to either 0, 32, or 64 depending on whether the target CPU is MIPS (32) or MISP64 (64).
//! \def ASMJIT_ARCH_RISCV
//!
//! Defined to either 0, 32, or 64 depending on whether the target CPU is RV32 (32) or RV64 (64).
//! \def ASMJIT_ARCH_BITS
//!
//! Defined to either 32 or 64 depending on the target.
//! \def ASMJIT_ARCH_LE
//!
//! Defined to 1 if the target architecture is little endian.
//! \def ASMJIT_ARCH_BE
//!
//! Defined to 1 if the target architecture is big endian.
//! \}
//! \cond NONE
#if defined(_M_X64) || defined(__x86_64__)
#define ASMJIT_ARCH_X86 64
#elif defined(_M_IX86) || defined(__X86__) || defined(__i386__)
@ -187,10 +222,17 @@ namespace asmjit {
#define ASMJIT_ARCH_MIPS 0
#endif
#define ASMJIT_ARCH_BITS (ASMJIT_ARCH_X86 | ASMJIT_ARCH_ARM | ASMJIT_ARCH_MIPS)
// NOTE `__riscv` is the correct macro in this case as specified by "RISC-V Toolchain Conventions".
#if (defined(__riscv) || defined(__riscv__)) && defined(__riscv_xlen)
#define ASMJIT_ARCH_RISCV __riscv_xlen
#else
#define ASMJIT_ARCH_RISCV 0
#endif
#define ASMJIT_ARCH_BITS (ASMJIT_ARCH_X86 | ASMJIT_ARCH_ARM | ASMJIT_ARCH_MIPS | ASMJIT_ARCH_RISCV)
#if ASMJIT_ARCH_BITS == 0
#undef ASMJIT_ARCH_BITS
#if defined (__LP64__) || defined(_LP64)
#if defined(__LP64__) || defined(_LP64)
#define ASMJIT_ARCH_BITS 64
#else
#define ASMJIT_ARCH_BITS 32
@ -212,62 +254,88 @@ namespace asmjit {
#define ASMJIT_NO_X86
#endif
#if !ASMJIT_ARCH_ARM && !defined(ASMJIT_NO_AARCH64)
#if ASMJIT_ARCH_ARM != 64 && !defined(ASMJIT_NO_AARCH64)
#define ASMJIT_NO_AARCH64
#endif
#endif
//! \endcond
// C++ Compiler and Features Detection
// ===================================
#define ASMJIT_CXX_GNU 0
#define ASMJIT_CXX_MAKE_VER(MAJOR, MINOR) ((MAJOR) * 1000 + (MINOR))
// Intel Compiler [pretends to be GNU or MSC, so it must be checked first]:
// - https://software.intel.com/en-us/articles/c0x-features-supported-by-intel-c-compiler
// - https://software.intel.com/en-us/articles/c14-features-supported-by-intel-c-compiler
// - https://software.intel.com/en-us/articles/c17-features-supported-by-intel-c-compiler
#if defined(__INTEL_COMPILER)
// MSC Compiler:
// - https://msdn.microsoft.com/en-us/library/hh567368.aspx
//
// Version List:
// - 16.00.0 == VS2010
// - 17.00.0 == VS2012
// - 18.00.0 == VS2013
// - 19.00.0 == VS2015
// - 19.10.0 == VS2017
#elif defined(_MSC_VER) && defined(_MSC_FULL_VER)
// Clang Compiler [Pretends to be GNU, so it must be checked before]:
// - https://clang.llvm.org/cxx_status.html
#elif defined(__clang_major__) && defined(__clang_minor__) && defined(__clang_patchlevel__)
// GNU Compiler:
// - https://gcc.gnu.org/projects/cxx-status.html
#elif defined(__GNUC__) && defined(__GNUC_MINOR__) && defined(__GNUC_PATCHLEVEL__)
#undef ASMJIT_CXX_GNU
#define ASMJIT_CXX_GNU ASMJIT_CXX_MAKE_VER(__GNUC__, __GNUC_MINOR__)
#endif
// Compiler features detection macros.
#if defined(__clang__) && defined(__has_attribute)
#if defined(__GNUC__) && defined(__has_attribute)
#define ASMJIT_CXX_HAS_ATTRIBUTE(NAME, CHECK) (__has_attribute(NAME))
#else
#define ASMJIT_CXX_HAS_ATTRIBUTE(NAME, CHECK) (!(!(CHECK)))
#endif
#endif // !ASMJIT_CXX_HAS_ATTRIBUTE
// API Decorators & C++ Extensions
// ===============================
//! \addtogroup asmjit_core
//! \{
//! \def ASMJIT_API
//!
//! A decorator that is used to decorate API that AsmJit exports when built as a shared library.
//! \def ASMJIT_VIRTAPI
//!
//! This is basically a workaround. When using MSVC and marking class as DLL export everything gets exported, which
//! is unwanted in most projects. MSVC automatically exports typeinfo and vtable if at least one symbol of the class
//! is exported. However, GCC has some strange behavior that even if one or more symbol is exported it doesn't export
//! typeinfo unless the class itself is decorated with "visibility(default)" (i.e. ASMJIT_API).
//! \def ASMJIT_FORCE_INLINE
//!
//! Decorator to force inlining of functions, uses either `__attribute__((__always_inline__))` or __forceinline,
//! depending on C++ compiler.
//! \def ASMJIT_INLINE_NODEBUG
//!
//! Like \ref ASMJIT_FORCE_INLINE, but uses additionally `__nodebug__` or `__artificial__` attribute to make the
//! debugging of some AsmJit functions easier, especially getters and one-line abstractions where usually you don't
//! want to step in.
//! \def ASMJIT_NOINLINE
//!
//! Decorator to avoid inlining of functions, uses either `__attribute__((__noinline__))` or `__declspec(noinline)`
//! depending on C++ compiler.
//! \def ASMJIT_NORETURN
//!
//! Decorator that marks functions that should never return. Typically used to implement assertion handlers that
//! terminate, so the function never returns.
//! \def ASMJIT_CDECL
//!
//! CDECL function attribute - either `__attribute__((__cdecl__))` or `__cdecl`.
//! \def ASMJIT_STDCALL
//!
//! STDCALL function attribute - either `__attribute__((__stdcall__))` or `__stdcall`.
//!
//! \note This expands to nothing on non-x86 targets as STDCALL is X86 specific.
//! \def ASMJIT_FASTCALL
//!
//! FASTCALL function attribute - either `__attribute__((__fastcall__))` or `__fastcall`.
//!
//! \note Expands to nothing on non-x86 targets as FASTCALL is X86 specific.
//! \def ASMJIT_REGPARM(N)
//!
//! Expands to `__attribute__((__regparm__(N)))` when compiled by GCC or clang, nothing otherwise.
//! \def ASMJIT_VECTORCALL
//!
//! VECTORCALL function attribute - either `__attribute__((__vectorcall__))` or `__vectorcall`.
//!
//! \note Expands to nothing on non-x86 targets as VECTORCALL is X86 specific.
//! \}
// API (Export / Import).
#if !defined(ASMJIT_STATIC)
#if defined(_WIN32) && (defined(_MSC_VER) || defined(__MINGW32__))
@ -295,13 +363,7 @@ namespace asmjit {
#define ASMJIT_VARAPI extern ASMJIT_API
#endif
//! \def ASMJIT_VIRTAPI
//!
//! This is basically a workaround. When using MSVC and marking class as DLL export everything gets exported, which
//! is unwanted in most projects. MSVC automatically exports typeinfo and vtable if at least one symbol of the class
//! is exported. However, GCC has some strange behavior that even if one or more symbol is exported it doesn't export
//! typeinfo unless the class itself is decorated with "visibility(default)" (i.e. ASMJIT_API).
#if !defined(_WIN32) && defined(__GNUC__)
#if defined(__GNUC__) && !defined(_WIN32)
#define ASMJIT_VIRTAPI ASMJIT_API
#else
#define ASMJIT_VIRTAPI
@ -458,17 +520,10 @@ namespace asmjit {
//! Marks function, class, struct, enum, or anything else as deprecated.
#if defined(__GNUC__)
#define ASMJIT_DEPRECATED(MESSAGE) __attribute__((__deprecated__(MESSAGE)))
#if defined(__clang__)
#define ASMJIT_DEPRECATED_STRUCT(MESSAGE) __attribute__((__deprecated__(MESSAGE)))
#else
#define ASMJIT_DEPRECATED_STRUCT(MESSAGE) /* not usable if a deprecated function uses it */
#endif
#elif defined(_MSC_VER)
#define ASMJIT_DEPRECATED(MESSAGE) __declspec(deprecated(MESSAGE))
#define ASMJIT_DEPRECATED_STRUCT(MESSAGE) /* not usable if a deprecated function uses it */
#else
#define ASMJIT_DEPRECATED(MESSAGE)
#define ASMJIT_DEPRECATED_STRUCT(MESSAGE)
#endif
// Utilities.
@ -477,66 +532,60 @@ namespace asmjit {
#if ASMJIT_CXX_HAS_ATTRIBUTE(no_sanitize, 0)
#define ASMJIT_ATTRIBUTE_NO_SANITIZE_UNDEF __attribute__((__no_sanitize__("undefined")))
#elif ASMJIT_CXX_GNU >= ASMJIT_CXX_MAKE_VER(4, 9)
#elif defined(__GNUC__) && __GNUC__ >= 5
#define ASMJIT_ATTRIBUTE_NO_SANITIZE_UNDEF __attribute__((__no_sanitize_undefined__))
#else
#define ASMJIT_ATTRIBUTE_NO_SANITIZE_UNDEF
#endif
// Begin-Namespace & End-Namespace Macros
// Diagnostic Macros
// ======================================
#if defined _DOXYGEN
#define ASMJIT_BEGIN_NAMESPACE namespace asmjit {
#define ASMJIT_END_NAMESPACE }
#elif defined(__clang__)
#define ASMJIT_BEGIN_NAMESPACE \
namespace asmjit { inline namespace ASMJIT_ABI_NAMESPACE { \
_Pragma("clang diagnostic push") \
_Pragma("clang diagnostic ignored \"-Wconstant-logical-operand\"") \
_Pragma("clang diagnostic ignored \"-Wunnamed-type-template-args\"")
#define ASMJIT_END_NAMESPACE \
_Pragma("clang diagnostic pop") \
}}
#elif defined(__GNUC__) && __GNUC__ == 4
#define ASMJIT_BEGIN_NAMESPACE \
namespace asmjit { inline namespace ASMJIT_ABI_NAMESPACE { \
#if !defined(__clang__) && !defined(__INTEL_COMPILER) && !defined(_DOXYGEN)
#if defined(__GNUC__) && __GNUC__ == 4
// There is a bug in GCC 4.X that has been fixed in GCC 5+, so just silence the warning.
#define ASMJIT_BEGIN_DIAGNOSTIC_SCOPE \
_Pragma("GCC diagnostic push") \
_Pragma("GCC diagnostic ignored \"-Wmissing-field-initializers\"")
#define ASMJIT_END_NAMESPACE \
_Pragma("GCC diagnostic pop") \
}}
#elif defined(__GNUC__) && __GNUC__ >= 8
#define ASMJIT_BEGIN_NAMESPACE \
namespace asmjit { inline namespace ASMJIT_ABI_NAMESPACE { \
_Pragma("GCC diagnostic push") \
_Pragma("GCC diagnostic ignored \"-Wclass-memaccess\"")
#define ASMJIT_END_NAMESPACE \
_Pragma("GCC diagnostic pop") \
}}
#elif defined(_MSC_VER) && !defined(__INTEL_COMPILER)
#define ASMJIT_BEGIN_NAMESPACE \
namespace asmjit { inline namespace ASMJIT_ABI_NAMESPACE { \
#define ASMJIT_END_DIAGNOSTIC_SCOPE \
_Pragma("GCC diagnostic pop")
#elif defined(_MSC_VER)
#define ASMJIT_BEGIN_DIAGNOSTIC_SCOPE \
__pragma(warning(push)) \
__pragma(warning(disable: 4127)) /* conditional expression is const */ \
__pragma(warning(disable: 4201)) /* nameless struct/union */
#define ASMJIT_END_DIAGNOSTIC_SCOPE \
__pragma(warning(pop))
#endif
#endif
#if !defined(ASMJIT_BEGIN_DIAGNOSTIC_SCOPE) && !defined(ASMJIT_END_DIAGNOSTIC_SCOPE)
#define ASMJIT_BEGIN_DIAGNOSTIC_SCOPE
#define ASMJIT_END_DIAGNOSTIC_SCOPE
#endif
// Begin-Namespace & End-Namespace Macros
// ======================================
#if !defined(ASMJIT_NO_ABI_NAMESPACE) && !defined(_DOXYGEN)
#define ASMJIT_BEGIN_NAMESPACE \
ASMJIT_BEGIN_DIAGNOSTIC_SCOPE \
namespace asmjit { \
inline namespace ASMJIT_ABI_NAMESPACE {
#define ASMJIT_END_NAMESPACE \
}} \
ASMJIT_END_DIAGNOSTIC_SCOPE
#else
#define ASMJIT_BEGIN_NAMESPACE \
ASMJIT_BEGIN_DIAGNOSTIC_SCOPE \
namespace asmjit {
#define ASMJIT_END_NAMESPACE \
__pragma(warning(pop)) \
}}
#endif
#if !defined(ASMJIT_BEGIN_NAMESPACE) && !defined(ASMJIT_END_NAMESPACE)
#define ASMJIT_BEGIN_NAMESPACE namespace asmjit { inline namespace ASMJIT_ABI_NAMESPACE {
#define ASMJIT_END_NAMESPACE }}
#endif
#define ASMJIT_BEGIN_SUB_NAMESPACE(NAMESPACE) \
ASMJIT_BEGIN_NAMESPACE \
namespace NAMESPACE {
#define ASMJIT_END_SUB_NAMESPACE \
} \
ASMJIT_END_NAMESPACE
ASMJIT_END_DIAGNOSTIC_SCOPE
#endif
#define ASMJIT_BEGIN_SUB_NAMESPACE(NAMESPACE) ASMJIT_BEGIN_NAMESPACE namespace NAMESPACE {
#define ASMJIT_END_SUB_NAMESPACE } ASMJIT_END_NAMESPACE
// C++ Utilities
// =============
@ -612,10 +661,4 @@ namespace asmjit {
}
#endif
// Cleanup Api-Config Specific Macros
// ==================================
#undef ASMJIT_CXX_GNU
#undef ASMJIT_CXX_MAKE_VER
#endif // ASMJIT_CORE_API_CONFIG_H_INCLUDED

View File

@ -29,8 +29,8 @@ enum class CondCode : uint8_t {
kNE = 0x03u, //!< Z==0 (any_sign !=)
kCS = 0x04u, //!< C==1 (unsigned >=)
kHS = 0x04u, //!< C==1 (unsigned >=)
kCC = 0x05u, //!< C==0 (unsigned < )
kLO = 0x05u, //!< C==0 (unsigned < )
kCC = 0x05u, //!< C==0 (unsigned < )
kMI = 0x06u, //!< N==1 (is negative)
kPL = 0x07u, //!< N==0 (is positive or zero)
kVS = 0x08u, //!< V==1 (is overflow)
@ -42,21 +42,24 @@ enum class CondCode : uint8_t {
kGT = 0x0Eu, //!< Z==0 & N==V (signed > )
kLE = 0x0Fu, //!< Z==1 | N!=V (signed <=)
kSign = kMI, //!< Sign.
kNotSign = kPL, //!< Not sign.
kOverflow = kVS, //!< Signed overflow.
kNotOverflow = kVC, //!< Not signed overflow.
kZero = kEQ, //!< Zero flag (alias to equal).
kNotZero = kNE, //!< Not zero (alias to Not Equal).
kEqual = kEQ, //!< Equal `a == b`.
kNotEqual = kNE, //!< Not Equal `a != b`.
kZero = kEQ, //!< Zero (alias to equal).
kNotZero = kNE, //!< Not Zero (alias to Not Equal).
kCarry = kCS, //!< Carry flag.
kNotCarry = kCC, //!< Not carry.
kSign = kMI, //!< Sign flag.
kNotSign = kPL, //!< Not sign.
kNegative = kMI, //!< Negative.
kPositive = kPL, //!< Positive or zero.
kOverflow = kVS, //!< Signed overflow.
kNotOverflow = kVC, //!< Not signed overflow.
kSignedLT = kLT, //!< Signed `a < b`.
kSignedLE = kLE, //!< Signed `a <= b`.
kSignedGT = kGT, //!< Signed `a > b`.
@ -67,49 +70,51 @@ enum class CondCode : uint8_t {
kUnsignedGT = kHI, //!< Unsigned `a > b`.
kUnsignedGE = kHS, //!< Unsigned `a >= b`.
kBTZero = kZero, //!< Tested bit is zero.
kBTNotZero = kNotZero, //!< Tested bit is not zero.
kAlways = kAL, //!< No condition code (always).
kMaxValue = 0x0Fu //!< Maximum value of `CondCode`.
};
//! \cond
static constexpr CondCode _reverseCondTable[] = {
CondCode::kAL, // AL <- AL
CondCode::kNA, // NA <- NA
CondCode::kEQ, // EQ <- EQ
CondCode::kNE, // NE <- NE
CondCode::kLS, // LS <- CS
CondCode::kHI, // HI <- LO
CondCode::kMI, // MI <- MI
CondCode::kPL, // PL <- PL
CondCode::kVS, // VS <- VS
CondCode::kVC, // VC <- VC
CondCode::kLO, // LO <- HI
CondCode::kCS, // CS <- LS
CondCode::kLE, // LE <- GE
CondCode::kGT, // GT <- LT
CondCode::kLT, // LT <- GT
CondCode::kGE // GE <- LE
};
//! \endcond
//! Reverses a condition code (reverses the corresponding operands of a comparison).
static ASMJIT_INLINE_NODEBUG constexpr CondCode reverseCond(CondCode cond) noexcept { return _reverseCondTable[uint8_t(cond)]; }
//! Negates a condition code.
static ASMJIT_INLINE_NODEBUG constexpr CondCode negateCond(CondCode cond) noexcept { return CondCode(uint8_t(cond) ^ uint8_t(1)); }
//! Data type that can be encoded with the instruction (AArch32 only).
enum class DataType : uint32_t {
//! No data type specified (default for all general purpose instructions).
kNone = 0,
//! 8-bit signed integer, specified as `.s8` in assembly.
kS8 = 1,
//! 16-bit signed integer, specified as `.s16` in assembly.
kS16 = 2,
//! 32-bit signed integer, specified as `.s32` in assembly.
kS32 = 3,
//! 64-bit signed integer, specified as `.s64` in assembly.
kS64 = 4,
//! 8-bit unsigned integer, specified as `.u8` in assembly.
kU8 = 5,
//! 16-bit unsigned integer, specified as `.u16` in assembly.
kU16 = 6,
//! 32-bit unsigned integer, specified as `.u32` in assembly.
kU32 = 7,
//! 64-bit unsigned integer, specified as `.u64` in assembly.
kU64 = 8,
//! 16-bit floating point (half precision), specified as `.f16` in assembly.
kF16 = 10,
//! 32-bit floating point (single precision), specified as `.f32` in assembly.
kF32 = 11,
//! 64-bit floating point (double precision), specified as `.f64` in assembly.
kF64 = 12,
//! 8-bit polynomial.
kP8 = 13,
//! 16-bit BF16 floating point.
kBF16 = 14,
//! 64-bit polynomial.
kP64 = 15,
//! Maximum value of `DataType`.
kMaxValue = 15
//! Memory offset mode.
//!
//! Describes either fixed, pre-index, or post-index offset modes.
enum class OffsetMode : uint32_t {
//! Fixed offset mode (either no index at all or a regular index without a write-back).
kFixed = 0u,
//! Pre-index "[BASE, #Offset {, <shift>}]!" with write-back.
kPreIndex = 1u,
//! Post-index "[BASE], #Offset {, <shift>}" with write-back.
kPostIndex = 2u
};
//! Shift operation predicate (ARM) describes either SHIFT or EXTEND operation.
@ -187,45 +192,70 @@ public:
//! Sets shift operation to `op`.
ASMJIT_INLINE_NODEBUG void setOp(ShiftOp op) noexcept { _op = op; }
//! Returns the shift smount.
//! Returns the shift amount.
ASMJIT_INLINE_NODEBUG constexpr uint32_t value() const noexcept { return _value; }
//! Sets shift amount to `value`.
ASMJIT_INLINE_NODEBUG void setValue(uint32_t value) noexcept { _value = value; }
};
//! Constructs a `LSL #value` shift (logical shift left).
static ASMJIT_INLINE_NODEBUG constexpr Shift lsl(uint32_t value) noexcept { return Shift(ShiftOp::kLSL, value); }
//! Constructs a `LSR #value` shift (logical shift right).
static ASMJIT_INLINE_NODEBUG constexpr Shift lsr(uint32_t value) noexcept { return Shift(ShiftOp::kLSR, value); }
//! Constructs a `ASR #value` shift (arithmetic shift right).
static ASMJIT_INLINE_NODEBUG constexpr Shift asr(uint32_t value) noexcept { return Shift(ShiftOp::kASR, value); }
//! Constructs a `ROR #value` shift (rotate right).
static ASMJIT_INLINE_NODEBUG constexpr Shift ror(uint32_t value) noexcept { return Shift(ShiftOp::kROR, value); }
//! Constructs a `RRX` shift (rotate with carry by 1).
static ASMJIT_INLINE_NODEBUG constexpr Shift rrx() noexcept { return Shift(ShiftOp::kRRX, 0); }
//! Constructs a `MSL #value` shift (logical shift left filling ones).
static ASMJIT_INLINE_NODEBUG constexpr Shift msl(uint32_t value) noexcept { return Shift(ShiftOp::kMSL, value); }
//! Constructs a `UXTB #value` extend and shift (unsigned byte extend).
static ASMJIT_INLINE_NODEBUG constexpr Shift uxtb(uint32_t value) noexcept { return Shift(ShiftOp::kUXTB, value); }
//! Constructs a `UXTH #value` extend and shift (unsigned hword extend).
static ASMJIT_INLINE_NODEBUG constexpr Shift uxth(uint32_t value) noexcept { return Shift(ShiftOp::kUXTH, value); }
//! Constructs a `UXTW #value` extend and shift (unsigned word extend).
static ASMJIT_INLINE_NODEBUG constexpr Shift uxtw(uint32_t value) noexcept { return Shift(ShiftOp::kUXTW, value); }
//! Constructs a `UXTX #value` extend and shift (unsigned dword extend).
static ASMJIT_INLINE_NODEBUG constexpr Shift uxtx(uint32_t value) noexcept { return Shift(ShiftOp::kUXTX, value); }
//! Constructs a `SXTB #value` extend and shift (signed byte extend).
static ASMJIT_INLINE_NODEBUG constexpr Shift sxtb(uint32_t value) noexcept { return Shift(ShiftOp::kSXTB, value); }
//! Constructs a `SXTH #value` extend and shift (signed hword extend).
static ASMJIT_INLINE_NODEBUG constexpr Shift sxth(uint32_t value) noexcept { return Shift(ShiftOp::kSXTH, value); }
//! Constructs a `SXTW #value` extend and shift (signed word extend).
static ASMJIT_INLINE_NODEBUG constexpr Shift sxtw(uint32_t value) noexcept { return Shift(ShiftOp::kSXTW, value); }
//! Constructs a `SXTX #value` extend and shift (signed dword extend).
static ASMJIT_INLINE_NODEBUG constexpr Shift sxtx(uint32_t value) noexcept { return Shift(ShiftOp::kSXTX, value); }
//! \}
ASMJIT_END_SUB_NAMESPACE
ASMJIT_BEGIN_SUB_NAMESPACE(a32)
using namespace arm;
//! Data type that can be encoded with AArch32 instruction identifier.
//!
//! \note Data types are frequently used with AArch32 SIMD instructions. For example `VMAX` instruction can
//! use almost all datatypes in a form `VMAX.F32`, `VMAX.S16`, `VMAX.U32`, etc... Emitter automatically adds
//! the required data type at emit level.
enum class DataType : uint32_t {
//! No data type specified (default for all general purpose instructions).
kNone = 0,
//! 8-bit signed integer, specified as `.s8` in assembly.
kS8 = 1,
//! 16-bit signed integer, specified as `.s16` in assembly.
kS16 = 2,
//! 32-bit signed integer, specified as `.s32` in assembly.
kS32 = 3,
//! 64-bit signed integer, specified as `.s64` in assembly.
kS64 = 4,
//! 8-bit unsigned integer, specified as `.u8` in assembly.
kU8 = 5,
//! 16-bit unsigned integer, specified as `.u16` in assembly.
kU16 = 6,
//! 32-bit unsigned integer, specified as `.u32` in assembly.
kU32 = 7,
//! 64-bit unsigned integer, specified as `.u64` in assembly.
kU64 = 8,
//! 16-bit floating point (half precision), specified as `.f16` in assembly.
kF16 = 10,
//! 32-bit floating point (single precision), specified as `.f32` in assembly.
kF32 = 11,
//! 64-bit floating point (double precision), specified as `.f64` in assembly.
kF64 = 12,
//! 8-bit polynomial.
kP8 = 13,
//! 16-bit BF16 floating point.
kBF16 = 14,
//! 64-bit polynomial.
kP64 = 15,
//! Maximum value of `DataType`.
kMaxValue = 15
};
static ASMJIT_INLINE_NODEBUG uint32_t dataTypeSize(DataType dt) noexcept {
static constexpr uint8_t table[] = { 0, 1, 2, 4, 8, 1, 2, 4, 8, 2, 4, 8, 1, 2, 8 };
return table[size_t(dt)];
}
ASMJIT_END_SUB_NAMESPACE
ASMJIT_BEGIN_SUB_NAMESPACE(a64)
using namespace arm;
ASMJIT_END_SUB_NAMESPACE
#endif // ASMJIT_CORE_ARCHCOMMONS_H_INCLUDED

View File

@ -74,6 +74,9 @@ enum class Arch : uint8_t {
ASMJIT_ARCH_X86 == 32 ? kX86 :
ASMJIT_ARCH_X86 == 64 ? kX64 :
ASMJIT_ARCH_RISCV == 32 ? kRISCV32 :
ASMJIT_ARCH_RISCV == 64 ? kRISCV64 :
ASMJIT_ARCH_ARM == 32 && ASMJIT_ARCH_LE ? kARM :
ASMJIT_ARCH_ARM == 32 && ASMJIT_ARCH_BE ? kARM_BE :
ASMJIT_ARCH_ARM == 64 && ASMJIT_ARCH_LE ? kAArch64 :

View File

@ -25,6 +25,7 @@ ASMJIT_BEGIN_NAMESPACE
//! Check out architecture specific assemblers for more details and examples:
//!
//! - \ref x86::Assembler - X86/X64 assembler implementation.
//! - \ref a64::Assembler - AArch64 assembler implementation.
class ASMJIT_VIRTAPI BaseAssembler : public BaseEmitter {
public:
ASMJIT_NONCOPYABLE(BaseAssembler)

View File

@ -590,7 +590,7 @@ Error BaseBuilder::_emit(InstId instId, const Operand_& o0, const Operand_& o1,
EmitterUtils::opArrayFromEmitArgs(opArray, o0, o1, o2, opExt);
ValidationFlags validationFlags = isCompiler() ? ValidationFlags::kEnableVirtRegs : ValidationFlags::kNone;
Error err = _funcs.validate(arch(), BaseInst(instId, options, _extraReg), opArray, opCount, validationFlags);
Error err = _funcs.validate(BaseInst(instId, options, _extraReg), opArray, opCount, validationFlags);
if (ASMJIT_UNLIKELY(err)) {
#ifndef ASMJIT_NO_LOGGING

View File

@ -48,7 +48,7 @@ enum class NodeType : uint8_t {
// [BaseBuilder]
//! Node is \ref InstNode or \ref InstExNode.
//! Node is \ref InstNode.
kInst = 1,
//! Node is \ref SectionNode.
kSection = 2,
@ -181,6 +181,7 @@ public:
//! Check out architecture specific builders for more details and examples:
//!
//! - \ref x86::Builder - X86/X64 builder implementation.
//! - \ref a64::Builder - AArch64 builder implementation.
class ASMJIT_VIRTAPI BaseBuilder : public BaseEmitter {
public:
ASMJIT_NONCOPYABLE(BaseBuilder)
@ -546,7 +547,7 @@ public:
uint8_t _reserved1;
};
//! Data that can have different meaning dependning on \ref NodeType.
//! Data that can have different meaning depending on \ref NodeType.
union {
//! Data useful by any node type.
AnyData _any;
@ -694,8 +695,9 @@ public:
//! Returns user data casted to `T*`.
//!
//! User data is decicated to be used only by AsmJit users and not touched by the library. The data has a pointer
//! size so you can either store a pointer or `intptr_t` value through `setUserDataAsIntPtr()`.
//! User data is dedicated to be used only by AsmJit users and not touched by the library. The data is of a pointer
//! size so you can either store a pointer or `int64_t` value through `setUserDataAsPtr()`, `setUserDataAsInt64()`
//! and `setUserDataAsUInt64()`.
template<typename T>
ASMJIT_INLINE_NODEBUG T* userDataAsPtr() const noexcept { return static_cast<T*>(_userDataPtr); }
//! Returns user data casted to `int64_t`.
@ -747,12 +749,15 @@ public:
//! \name Constants
//! \{
enum : uint32_t {
//! Count of embedded operands per `InstNode` that are always allocated as a part of the instruction. Minimum
//! embedded operands is 4, but in 32-bit more pointers are smaller and we can embed 5. The rest (up to 6 operands)
//! is always stored in `InstExNode`.
kBaseOpCapacity = uint32_t((128 - sizeof(BaseNode) - sizeof(BaseInst)) / sizeof(Operand_))
};
//! The number of embedded operands for a default \ref InstNode instance that are always allocated as a part of
//! the instruction itself. Minimum embedded operands is 4, but in 32-bit more pointers are smaller and we can
//! embed 5. The rest (up to 6 operands) is considered extended.
//!
//! The number of operands InstNode holds is decided when \ref InstNode is created.
static constexpr uint32_t kBaseOpCapacity = uint32_t((128 - sizeof(BaseNode) - sizeof(BaseInst)) / sizeof(Operand_));
//! Count of maximum number of operands \ref InstNode can hold.
static constexpr uint32_t kFullOpCapacity = Globals::kMaxOpCount;
//! \}
@ -761,8 +766,6 @@ public:
//! Base instruction data.
BaseInst _baseInst;
//! First 4 or 5 operands (indexed from 0).
Operand_ _opArray[kBaseOpCapacity];
//! \}
@ -811,11 +814,17 @@ public:
//! \name Instruction Options
//! \{
//! Returns instruction options, see \ref InstOptions for more details.
ASMJIT_INLINE_NODEBUG InstOptions options() const noexcept { return _baseInst.options(); }
//! Tests whether instruction has the given \option` set/enabled.
ASMJIT_INLINE_NODEBUG bool hasOption(InstOptions option) const noexcept { return _baseInst.hasOption(option); }
//! Sets instruction `options` to the provided value, resetting all others.
ASMJIT_INLINE_NODEBUG void setOptions(InstOptions options) noexcept { _baseInst.setOptions(options); }
//! Adds instruction `options` to the instruction.
ASMJIT_INLINE_NODEBUG void addOptions(InstOptions options) noexcept { _baseInst.addOptions(options); }
//! Clears instruction `options` of the instruction (disables the given options).
ASMJIT_INLINE_NODEBUG void clearOptions(InstOptions options) noexcept { _baseInst.clearOptions(options); }
//! Resets instruction options to none - disabling all instruction options.
ASMJIT_INLINE_NODEBUG void resetOptions() noexcept { _baseInst.resetOptions(); }
//! \}
@ -850,38 +859,52 @@ public:
ASMJIT_INLINE_NODEBUG void setOpCount(uint32_t opCount) noexcept { _inst._opCount = uint8_t(opCount); }
//! Returns operands array.
ASMJIT_INLINE_NODEBUG Operand* operands() noexcept { return (Operand*)_opArray; }
ASMJIT_INLINE_NODEBUG Operand* operands() noexcept {
return reinterpret_cast<Operand*>(reinterpret_cast<uint8_t*>(this) + sizeof(InstNode));
}
//! Returns operands array (const).
ASMJIT_INLINE_NODEBUG const Operand* operands() const noexcept { return (const Operand*)_opArray; }
ASMJIT_INLINE_NODEBUG const Operand* operands() const noexcept {
return reinterpret_cast<const Operand*>(reinterpret_cast<const uint8_t*>(this) + sizeof(InstNode));
}
//! Returns operand at the given `index`.
inline Operand& op(uint32_t index) noexcept {
ASMJIT_ASSERT(index < opCapacity());
return _opArray[index].as<Operand>();
Operand* ops = operands();
return ops[index].as<Operand>();
}
//! Returns operand at the given `index` (const).
inline const Operand& op(uint32_t index) const noexcept {
ASMJIT_ASSERT(index < opCapacity());
return _opArray[index].as<Operand>();
const Operand* ops = operands();
return ops[index].as<Operand>();
}
//! Sets operand at the given `index` to `op`.
inline void setOp(uint32_t index, const Operand_& op) noexcept {
ASMJIT_ASSERT(index < opCapacity());
_opArray[index].copyFrom(op);
Operand* ops = operands();
ops[index].copyFrom(op);
}
//! Resets operand at the given `index` to none.
inline void resetOp(uint32_t index) noexcept {
ASMJIT_ASSERT(index < opCapacity());
_opArray[index].reset();
Operand* ops = operands();
ops[index].reset();
}
//! Resets operands at `[start, end)` range.
inline void resetOpRange(uint32_t start, uint32_t end) noexcept {
Operand* ops = operands();
for (uint32_t i = start; i < end; i++)
_opArray[i].reset();
ops[i].reset();
}
//! \}
@ -889,33 +912,47 @@ public:
//! \name Utilities
//! \{
//! Tests whether the given operand type `opType` is used by the instruction.
inline bool hasOpType(OperandType opType) const noexcept {
const Operand* ops = operands();
for (uint32_t i = 0, count = opCount(); i < count; i++)
if (_opArray[i].opType() == opType)
if (ops[i].opType() == opType)
return true;
return false;
}
//! Tests whether the instruction uses at least one register operand.
inline bool hasRegOp() const noexcept { return hasOpType(OperandType::kReg); }
//! Tests whether the instruction uses at least one memory operand.
inline bool hasMemOp() const noexcept { return hasOpType(OperandType::kMem); }
//! Tests whether the instruction uses at least one immediate operand.
inline bool hasImmOp() const noexcept { return hasOpType(OperandType::kImm); }
//! Tests whether the instruction uses at least one label operand.
inline bool hasLabelOp() const noexcept { return hasOpType(OperandType::kLabel); }
//! Returns the index of the given operand type `opType`.
//!
//! \note If the operand type wa found, the value returned represents its index in \ref operands()
//! array, otherwise \ref Globals::kNotFound is returned to signalize that the operand was not found.
inline uint32_t indexOfOpType(OperandType opType) const noexcept {
uint32_t i = 0;
uint32_t count = opCount();
const Operand* ops = operands();
while (i < count) {
if (_opArray[i].opType() == opType)
break;
if (ops[i].opType() == opType)
return i;
i++;
}
return i;
return Globals::kNotFound;
}
//! A shortcut that calls `indexOfOpType(OperandType::kMem)`.
inline uint32_t indexOfMemOp() const noexcept { return indexOfOpType(OperandType::kMem); }
//! A shortcut that calls `indexOfOpType(OperandType::kImm)`.
inline uint32_t indexOfImmOp() const noexcept { return indexOfOpType(OperandType::kImm); }
//! A shortcut that calls `indexOfOpType(OperandType::kLabel)`.
inline uint32_t indexOfLabelOp() const noexcept { return indexOfOpType(OperandType::kLabel); }
//! \}
@ -924,20 +961,40 @@ public:
//! \{
//! \cond INTERNAL
//! Returns uint32_t[] view that represents BaseInst::RegOnly and instruction operands.
ASMJIT_INLINE_NODEBUG uint32_t* _getRewriteArray() noexcept { return &_baseInst._extraReg._id; }
//! \overload
ASMJIT_INLINE_NODEBUG const uint32_t* _getRewriteArray() const noexcept { return &_baseInst._extraReg._id; }
//! Maximum value of rewrite id - 6 operands each having 4 slots is 24, one RegOnly having 2 slots => 26.
static constexpr uint32_t kMaxRewriteId = 26 - 1;
//! Returns a rewrite index of the given pointer to `id`.
//!
//! This function returns a value that can be then passed to `\ref rewriteIdAtIndex() function. It can address
//! any id from any operand that is used by the instruction in addition to \ref BaseInst::regOnly field, which
//! can also be used by the register allocator.
inline uint32_t getRewriteIndex(const uint32_t* id) const noexcept {
const uint32_t* array = _getRewriteArray();
ASMJIT_ASSERT(array <= id);
size_t index = (size_t)(id - array);
ASMJIT_ASSERT(index < 32);
ASMJIT_ASSERT(index <= kMaxRewriteId);
return uint32_t(index);
}
//! Rewrites the given `index` to the provided identifier `id`.
//!
//! \note This is an internal function that is used by a \ref BaseCompiler implementation to rewrite virtual
//! registers to physical registers. The rewriter in this case sees all operands as array of uint32 values
//! and the given `index` describes a position in this array. For example a single \ref Operand would be
//! decomposed to 4 uint32_t values, where the first at index 0 would be operand signature, next would be
//! base id, etc... This is a comfortable way of patching operands without having to check for their types.
inline void rewriteIdAtIndex(uint32_t index, uint32_t id) noexcept {
ASMJIT_ASSERT(index <= kMaxRewriteId);
uint32_t* array = _getRewriteArray();
array[index] = id;
}
@ -949,43 +1006,40 @@ public:
//! \{
//! \cond INTERNAL
static ASMJIT_INLINE_NODEBUG uint32_t capacityOfOpCount(uint32_t opCount) noexcept {
return opCount <= kBaseOpCapacity ? kBaseOpCapacity : Globals::kMaxOpCount;
//! Returns the capacity required for the given operands count `opCount`.
//!
//! There are only two capacities used - \ref kBaseOpCapacity and \ref kFullOpCapacity, so this function
//! is used to decide between these two. The general rule is that instructions that can be represented with
//! \ref kBaseOpCapacity would use this value, and all others would take \ref kFullOpCapacity.
static ASMJIT_INLINE_NODEBUG constexpr uint32_t capacityOfOpCount(uint32_t opCount) noexcept {
return opCount <= kBaseOpCapacity ? kBaseOpCapacity : kFullOpCapacity;
}
static ASMJIT_INLINE_NODEBUG size_t nodeSizeOfOpCapacity(uint32_t opCapacity) noexcept {
size_t base = sizeof(InstNode) - kBaseOpCapacity * sizeof(Operand);
return base + opCapacity * sizeof(Operand);
//! Calculates the size of \ref InstNode required to hold at most `opCapacity` operands.
//!
//! This function is used internally to allocate \ref InstNode.
static ASMJIT_INLINE_NODEBUG constexpr size_t nodeSizeOfOpCapacity(uint32_t opCapacity) noexcept {
return sizeof(InstNode) + opCapacity * sizeof(Operand);
}
//! \endcond
//! \}
};
//! Instruction node with maximum number of operands.
//! Instruction node with embedded operands following \ref InstNode layout.
//!
//! This node is created automatically by Builder/Compiler in case that the required number of operands exceeds
//! the default capacity of `InstNode`.
class InstExNode : public InstNode {
//! \note This is used to make tools such as static analysis and compilers happy about the layout. There were two
//! instruction nodes in the past, having the second extend the operand array of the first, but that has caused
//! undefined behavior and made recent tools unhappy about that.
template<uint32_t kN>
class InstNodeWithOperands : public InstNode {
public:
ASMJIT_NONCOPYABLE(InstExNode)
Operand_ _operands[kN];
//! \name Members
//! \{
//! Continued `_opArray[]` to hold up to `kMaxOpCount` operands.
Operand_ _opArrayEx[Globals::kMaxOpCount - kBaseOpCapacity];
//! \}
//! \name Construction & Destruction
//! \{
//! Creates a new `InstExNode` instance.
ASMJIT_INLINE_NODEBUG InstExNode(BaseBuilder* cb, InstId instId, InstOptions options, uint32_t opCapacity = Globals::kMaxOpCount) noexcept
: InstNode(cb, instId, options, opCapacity) {}
//! \}
//! Creates a new `InstNodeWithOperands` instance.
ASMJIT_INLINE_NODEBUG InstNodeWithOperands(BaseBuilder* cb, InstId instId, InstOptions options, uint32_t opCount) noexcept
: InstNode(cb, instId, options, opCount, kN) {}
};
//! Section node.
@ -1012,9 +1066,9 @@ public:
//! \{
//! Creates a new `SectionNode` instance.
ASMJIT_INLINE_NODEBUG SectionNode(BaseBuilder* cb, uint32_t secionId = 0) noexcept
ASMJIT_INLINE_NODEBUG SectionNode(BaseBuilder* cb, uint32_t sectionId = 0) noexcept
: BaseNode(cb, NodeType::kSection, NodeFlags::kHasNoEffect),
_id(secionId),
_id(sectionId),
_nextSection(nullptr) {}
//! \}

View File

@ -13,6 +13,7 @@
ASMJIT_BEGIN_NAMESPACE
//! \cond INTERNAL
//! \addtogroup asmjit_builder
//! \{
@ -28,6 +29,7 @@ static inline void BaseBuilder_assignInstState(BaseBuilder* self, InstNode* node
}
//! \}
//! \endcond
ASMJIT_END_NAMESPACE

View File

@ -132,8 +132,8 @@ CodeHolder::~CodeHolder() noexcept {
CodeHolder_resetInternal(this, ResetPolicy::kHard);
}
// CodeHolder - Init & Reset
// =========================
// CodeHolder - Initialization & Reset
// ===================================
inline void CodeHolder_setSectionDefaultName(
Section* section,
@ -908,7 +908,7 @@ size_t CodeHolder::codeSize() const noexcept {
}
}
if ((sizeof(uint64_t) > sizeof(size_t) && offset > SIZE_MAX) || of)
if ((sizeof(uint64_t) > sizeof(size_t) && offset > uint64_t(SIZE_MAX)) || of)
return SIZE_MAX;
return size_t(offset);

View File

@ -45,7 +45,7 @@ enum class ExpressionOpType : uint8_t {
kSra = 5
};
//! Value tyoe that can be used within an \ref Expression.
//! Value type that can be used within an \ref Expression.
enum class ExpressionValueType : uint8_t {
//! No value or invalid.
kNone = 0,
@ -272,6 +272,9 @@ public:
//! Offset format type, used by \ref OffsetFormat.
enum class OffsetType : uint8_t {
// Common Offset Formats
// ---------------------
//! A value having `_immBitCount` bits and shifted by `_immBitShift`.
//!
//! This offset type is sufficient for many targets that store offset as a continuous set bits within an
@ -284,14 +287,82 @@ enum class OffsetType : uint8_t {
// AArch64 Specific Offset Formats
// -------------------------------
//! AARCH64 ADR format of `[.|immlo:2|.....|immhi:19|.....]`.
//! AArch64 ADR format of `[.|immlo:2|.....|immhi:19|.....]`.
kAArch64_ADR,
//! AARCH64 ADRP format of `[.|immlo:2|.....|immhi:19|.....]` (4kB pages).
//! AArch64 ADRP format of `[.|immlo:2|.....|immhi:19|.....]` (4kB pages).
kAArch64_ADRP,
// AArch32 Specific Offset Formats (T16 & T32)
// -------------------------------------------
//! AArch32 THUMBv2 immediate encoding of 'ADR' instruction (12-bit payload and sign bit):
//!
//! `|.....|imm:1|..N.N|......|imm:3|....|imm:8|`
//!
//! Where `N` is one if the offset is negative. The immediate is encoded as absolute value of the offset if negative.
kThumb32_ADR,
//! AArch32 THUMBv2 immediate encoding of 'BLX' instruction (23-bit immediate payload, multiplied by 4):
//!
//! `|.....|imm[22]|imm[19:10]|..|ja|1|jb|imm[9:0]|0`
//!
//! Where:
//!
//! - `ja` is calculated as imm[22] ^ imm[21] ^ 1.
//! - `jb` is calculated as imm[22] ^ imm[20] ^ 1.
kThumb32_BLX,
//! AArch32 THUMBv2 immediate encoding of 'B' instruction without `<cond>` (24-bit immediate payload, multiplied by 2):
//!
//! `|.....|imm[23]|imm[20:11]|..|ja|1|jb|imm[10:0]`
//!
//! Where:
//!
//! - `ja` is calculated as imm[23] ^ imm[22] ^ 1.
//! - `jb` is calculated as imm[23] ^ imm[21] ^ 1.
kThumb32_B,
//! AArch32 THUMBv2 immediate encoding of 'B' instruction with `<cond>` (20-bit immediate payload, multiplied by 2).
//!
//! `|.....|imm[19]|....|imm[16:11]|..|ja|1|jb|imm[10:0]`
//!
//! Where:
//!
//! - `ja` is calculated as imm[19] ^ imm[18] ^ 1.
//! - `jb` is calculated as imm[19] ^ imm[17] ^ 1.
kThumb32_BCond,
// AArch32 Specific Offset Formats (A32)
// -------------------------------------
//! AArch32 ADR instruction, which uses a standard 12-bit immediate encoding that is used by other ARM instructions.
kAArch32_ADR,
//! AArch32 signed offset that is similar to `kSignedOffset`, however it uses absolute value of the offset and its
//! sign is encoded in 23rd bit of the opcode.
//!
//! `|........|U.......|........|........|`
//!
kAArch32_U23_SignedOffset,
//! AArch32 offset format that encodes 8-bit offset as:
//!
//! `|........|U.......|....|imm[7:4]|....|imm[3:0]|`
//!
//! in a 32-bit word, where U is a sign of the displacement and the displacement itself is encoded as its absolute
//! value.
kAArch32_U23_0To3At0_4To7At8,
//! AArch32 offset format that encodes a signed 25-bit offset as:
//!
//! `|.......|imm[0]|imm[24:1]|`
//!
//! in a 32-bit word.
kAArch32_1To24At0_0At24,
//! Maximum value of `OffsetFormatType`.
kMaxValue = kAArch64_ADRP
kMaxValue = kAArch32_1To24At0_0At24
};
//! Provides information about formatting offsets, absolute addresses, or their parts. Offset format is used by both
@ -350,15 +421,24 @@ struct OffsetFormat {
//! Returns the type of the offset.
ASMJIT_INLINE_NODEBUG OffsetType type() const noexcept { return _type; }
//! Returns whether the offset is encoded as an absolute value of the offset with additional field(s) that represent
//! the sign (AArch32 U/N fields in the opcode).
//!
//! If true, the offset itself is always positive and a separate U/N field is used to indicate the sign of the offset
//! (usually `U==1` means ADD, but sometimes `N==1` means negative offset, which implies SUB).
ASMJIT_INLINE_NODEBUG bool hasSignBit() const noexcept {
return _type == OffsetType::kThumb32_ADR ||
_type == OffsetType::kAArch32_ADR ||
_type == OffsetType::kAArch32_U23_SignedOffset ||
_type == OffsetType::kAArch32_U23_0To3At0_4To7At8;
}
//! Returns flags.
ASMJIT_INLINE_NODEBUG uint32_t flags() const noexcept { return _flags; }
//! Returns the size of the region/instruction where the offset is encoded.
ASMJIT_INLINE_NODEBUG uint32_t regionSize() const noexcept { return _regionSize; }
//! Returns the offset of the word relative to the start of the region where the offset is.
ASMJIT_INLINE_NODEBUG uint32_t valueOffset() const noexcept { return _valueOffset; }
//! Returns the size of the data-type (word) that contains the offset, in bytes.
ASMJIT_INLINE_NODEBUG uint32_t valueSize() const noexcept { return _valueSize; }
//! Returns the count of bits of the offset value in the data it's stored in.
@ -850,7 +930,7 @@ public:
//! use the same slot.
//!
//! This function should be considered internal as it's used by assemblers to insert an absolute address into the
//! address table. Inserting address into address table without creating a particula relocation entry makes no sense.
//! address table. Inserting address into address table without creating a particular relocation entry makes no sense.
ASMJIT_API Error addAddressToAddressTable(uint64_t address) noexcept;
//! \}
@ -1018,7 +1098,8 @@ public:
//! Relocates the code to the given `baseAddress`.
//!
//! \param baseAddress Absolute base address where the code will be relocated to. Please note that nothing is
//! copied to such base address, it's just an absolute value used by the relocator to resolve all stored relocations.
//! copied to such base address, it's just an absolute value used by the relocation code to resolve all stored
//! relocations.
//!
//! \note This should never be called more than once.
ASMJIT_API Error relocateToBase(uint64_t baseAddress) noexcept;

View File

@ -6,6 +6,7 @@
#include "../core/api-build_p.h"
#include "../core/codeholder.h"
#include "../core/codewriter_p.h"
#include "../arm/armutils.h"
ASMJIT_BEGIN_NAMESPACE
@ -19,9 +20,20 @@ bool CodeWriterUtils::encodeOffset32(uint32_t* dst, int64_t offset64, const Offs
return false;
uint32_t value;
uint32_t u = 0;
bool unsignedLogic = format.type() == OffsetType::kUnsignedOffset;
// First handle all offsets that use additional field for their sign and the offset is encoded as its
// absolute value.
if (format.hasSignBit()) {
u = uint32_t(offset64 >= 0);
if (u == 0)
offset64 = -offset64;
unsignedLogic = true;
}
// First handle all unsigned offset types.
if (format.type() == OffsetType::kUnsignedOffset) {
if (unsignedLogic) {
if (discardLsb) {
ASMJIT_ASSERT(discardLsb <= 32);
if ((offset64 & Support::lsbMask<uint32_t>(discardLsb)) != 0)
@ -57,6 +69,97 @@ bool CodeWriterUtils::encodeOffset32(uint32_t* dst, int64_t offset64, const Offs
return true;
}
// Opcode: {.....|imm:1|..N.N|......|imm:3|....|imm:8}
case OffsetType::kThumb32_ADR: {
// Sanity checks.
if (format.valueSize() != 4 || bitCount != 12 || bitShift != 0)
return false;
uint32_t imm8 = (value & 0x00FFu);
uint32_t imm3 = (value & 0x0700u) << (12 - 8);
uint32_t imm1 = (value & 0x0800u) << (26 - 11);
uint32_t n = u ^ 1u;
*dst = imm8 | imm3 | imm1 | (n << 21) | (n << 23);
return true;
}
// Opcode: {....|.|imm[22]|imm[19:10]|..|ja|.|jb|imm[9:0]|.}
case OffsetType::kThumb32_BLX:
// The calculation is the same as `B`, but the first LSB bit must be zero, so account for that.
value <<= 1;
ASMJIT_FALLTHROUGH;
// Opcode: {....|.|imm[23]|imm[20:11]|..|ja|.|jb|imm[10:0]}
case OffsetType::kThumb32_B: {
// Sanity checks.
if (format.valueSize() != 4)
return false;
uint32_t ia = (value & 0x0007FFu);
uint32_t ib = (value & 0x1FF800u) << (16 - 11);
uint32_t ic = (value & 0x800000u) << (26 - 23);
uint32_t ja = ((~value >> 23) ^ (value >> 22)) & 1u;
uint32_t jb = ((~value >> 23) ^ (value >> 21)) & 1u;
*dst = ia | ib | ic | (ja << 14) | (jb << 11);
return true;
}
// Opcode: {....|.|imm[19]|....|imm[16:11]|..|ja|.|jb|imm[10:0]}
case OffsetType::kThumb32_BCond: {
// Sanity checks.
if (format.valueSize() != 4 || bitCount != 20 || bitShift != 0)
return false;
uint32_t ia = (value & 0x0007FFu);
uint32_t ib = (value & 0x01F800u) << (16 - 11);
uint32_t ic = (value & 0x080000u) << (26 - 19);
uint32_t ja = ((~value >> 19) ^ (value >> 22)) & 1u;
uint32_t jb = ((~value >> 19) ^ (value >> 21)) & 1u;
*dst = ia | ib | ic | (ja << 14) | (jb << 11);
return true;
}
case OffsetType::kAArch32_ADR: {
uint32_t encodedImm;
if (!arm::Utils::encodeAArch32Imm(value, &encodedImm))
return false;
*dst = (Support::bitMask(22) << u) | (encodedImm << bitShift);
return true;
}
case OffsetType::kAArch32_U23_SignedOffset: {
*dst = (value << bitShift) | (u << 23);
return true;
}
case OffsetType::kAArch32_U23_0To3At0_4To7At8: {
// Sanity checks.
if (format.valueSize() != 4 || bitCount != 8 || bitShift != 0)
return false;
uint32_t immLo = (value & 0x0Fu);
uint32_t immHi = (value & 0xF0u) << (8 - 4);
*dst = immLo | immHi | (u << 23);
return true;
}
case OffsetType::kAArch32_1To24At0_0At24: {
// Sanity checks.
if (format.valueSize() != 4 || bitCount != 25 || bitShift != 0)
return false;
uint32_t immLo = (value & 0x0000001u) << 24;
uint32_t immHi = (value & 0x1FFFFFEu) >> 1;
*dst = immLo | immHi;
return true;
}
case OffsetType::kAArch64_ADR:
case OffsetType::kAArch64_ADRP: {
// Sanity checks.

View File

@ -47,6 +47,7 @@ class InvokeNode;
//! Check out architecture specific compilers for more details and examples:
//!
//! - \ref x86::Compiler - X86/X64 compiler implementation.
//! - \ref a64::Compiler - AArch64 compiler implementation.
class ASMJIT_VIRTAPI BaseCompiler : public BaseBuilder {
public:
ASMJIT_NONCOPYABLE(BaseCompiler)
@ -162,6 +163,8 @@ public:
//!
//! \note This version accepts a snprintf() format `fmt` followed by a variadic arguments.
ASMJIT_API Error _newRegFmt(BaseReg* ASMJIT_NONNULL(out), TypeId typeId, const char* fmt, ...);
//! \overload
inline Error _newRegFmt(BaseReg* ASMJIT_NONNULL(out), TypeId typeId) { return _newRegFmt(out, typeId, nullptr); }
//! Creates a new virtual register compatible with the provided reference register `ref`.
ASMJIT_API Error _newReg(BaseReg* ASMJIT_NONNULL(out), const BaseReg& ref, const char* name = nullptr);
@ -325,7 +328,7 @@ public:
//! \note This node should be only used to represent jump where the jump target cannot be deduced by examining
//! instruction operands. For example if the jump target is register or memory location. This pattern is often
//! used to perform indirect jumps that use jump table, e.g. to implement `switch{}` statement.
class JumpNode : public InstNode {
class JumpNode : public InstNodeWithOperands<InstNode::kBaseOpCapacity> {
public:
ASMJIT_NONCOPYABLE(JumpNode)
@ -340,7 +343,7 @@ public:
//! \{
inline JumpNode(BaseCompiler* ASMJIT_NONNULL(cc), InstId instId, InstOptions options, uint32_t opCount, JumpAnnotation* annotation) noexcept
: InstNode(cc, instId, options, opCount, kBaseOpCapacity),
: InstNodeWithOperands(cc, instId, options, opCount),
_annotation(annotation) {
setType(NodeType::kJump);
}
@ -531,7 +534,7 @@ public:
};
//! Function return, used by \ref BaseCompiler.
class FuncRetNode : public InstNode {
class FuncRetNode : public InstNodeWithOperands<InstNode::kBaseOpCapacity> {
public:
ASMJIT_NONCOPYABLE(FuncRetNode)
@ -539,7 +542,8 @@ public:
//! \{
//! Creates a new `FuncRetNode` instance.
inline FuncRetNode(BaseBuilder* ASMJIT_NONNULL(cb)) noexcept : InstNode(cb, BaseInst::kIdAbstract, InstOptions::kNone, 0) {
inline FuncRetNode(BaseBuilder* ASMJIT_NONNULL(cb)) noexcept
: InstNodeWithOperands(cb, BaseInst::kIdAbstract, InstOptions::kNone, 0) {
_any._nodeType = NodeType::kFuncRet;
}
@ -547,12 +551,12 @@ public:
};
//! Function invocation, used by \ref BaseCompiler.
class InvokeNode : public InstNode {
class InvokeNode : public InstNodeWithOperands<InstNode::kBaseOpCapacity> {
public:
ASMJIT_NONCOPYABLE(InvokeNode)
//! Operand pack provides multiple operands that can be associated with a single return value of function
//! argument. Sometims this is necessary to express an argument or return value that requires multiple
//! argument. Sometimes this is necessary to express an argument or return value that requires multiple
//! registers, for example 64-bit value in 32-bit mode or passing / returning homogeneous data structures.
struct OperandPack {
//! Operands.
@ -594,7 +598,7 @@ public:
//! Creates a new `InvokeNode` instance.
inline InvokeNode(BaseBuilder* ASMJIT_NONNULL(cb), InstId instId, InstOptions options) noexcept
: InstNode(cb, instId, options, kBaseOpCapacity),
: InstNodeWithOperands(cb, instId, options, 0),
_funcDetail(),
_args(nullptr) {
setType(NodeType::kInvoke);
@ -619,9 +623,9 @@ public:
ASMJIT_INLINE_NODEBUG const FuncDetail& detail() const noexcept { return _funcDetail; }
//! Returns the target operand.
ASMJIT_INLINE_NODEBUG Operand& target() noexcept { return _opArray[0].as<Operand>(); }
ASMJIT_INLINE_NODEBUG Operand& target() noexcept { return op(0); }
//! \overload
ASMJIT_INLINE_NODEBUG const Operand& target() const noexcept { return _opArray[0].as<Operand>(); }
ASMJIT_INLINE_NODEBUG const Operand& target() const noexcept { return op(0); }
//! Returns the number of function return values.
ASMJIT_INLINE_NODEBUG bool hasRet() const noexcept { return _funcDetail.hasRet(); }

View File

@ -27,6 +27,9 @@ enum class ConstPoolScope : uint32_t {
};
//! Constant pool.
//!
//! Constant pool is designed to hold 1, 2, 4, 8, 16, 32, and 64 byte constants. It's not designed to hold constants
//! having arbitrary length like strings and arrays.
class ConstPool {
public:
ASMJIT_NONCOPYABLE(ConstPool)
@ -199,9 +202,17 @@ public:
//! \name Construction & Destruction
//! \{
ASMJIT_API ConstPool(Zone* zone) noexcept;
//! Creates a new constant pool that would use `zone` as a memory allocator.
ASMJIT_API explicit ConstPool(Zone* zone) noexcept;
//! Destroys this constant pool.
ASMJIT_API ~ConstPool() noexcept;
//! \}
//! \name Reset
//! \{
//! Resets this constant pool and its allocator to `zone`.
ASMJIT_API void reset(Zone* zone) noexcept;
//! \}

View File

@ -7,6 +7,8 @@
#include "../core/cpuinfo.h"
#include "../core/support.h"
#include <atomic>
// Required by `__cpuidex()` and `_xgetbv()`.
#if ASMJIT_ARCH_X86
#if defined(_MSC_VER)
@ -14,12 +16,15 @@
#endif
#endif // ASMJIT_ARCH_X86
#if !defined(_WIN32)
#include <unistd.h>
#endif
#if ASMJIT_ARCH_ARM
// Required by various utilities that are required by features detection.
#if !defined(_WIN32)
#include <errno.h>
#include <sys/utsname.h>
#include <unistd.h>
#endif
//! Required to detect CPU and features on Apple platforms.
@ -566,6 +571,7 @@ static ASMJIT_FAVOR_SIZE void detectX86Cpu(CpuInfo& cpu) noexcept {
// Other resources:
// https://en.wikipedia.org/wiki/AArch64
// https://en.wikipedia.org/wiki/Apple_silicon#List_of_Apple_processors
// https://developer.arm.com/downloads/-/exploration-tools/feature-names-for-a-profile
// https://developer.arm.com/architectures/learn-the-architecture/understanding-the-armv8-x-extensions/single-page
#if ASMJIT_ARCH_ARM
@ -659,8 +665,8 @@ static inline void populateBaseARMFeatures(CpuInfo& cpu) noexcept {
#endif
}
// CpuInfo - Detect - ARM - Madatory Features of ARM Architectures
// ===============================================================
// CpuInfo - Detect - ARM - Mandatory Features of ARM Architectures
// ================================================================
// Populates mandatory ARMv8.[v]A features.
ASMJIT_MAYBE_UNUSED
@ -678,22 +684,21 @@ static ASMJIT_FAVOR_SIZE void populateARMv8AFeatures(CpuFeatures::ARM& features,
features.add(Ext::kHCX, Ext::kPAN3, Ext::kWFXT, Ext::kXS);
ASMJIT_FALLTHROUGH;
case 6: // ARMv8.6
// Missing: AMUv1p1.
features.add(Ext::kBF16, Ext::kECV, Ext::kFGT, Ext::kI8MM);
features.add(Ext::kAMU1_1, Ext::kBF16, Ext::kECV, Ext::kFGT, Ext::kI8MM);
ASMJIT_FALLTHROUGH;
case 5: // ARMv8.5
// Missing: CSV2_2.
features.add(Ext::kBTI, Ext::kDPB2, Ext::kFLAGM2, Ext::kFRINTTS, Ext::kSB, Ext::kSPECRES, Ext::kSSBS);
features.add(Ext::kBTI, Ext::kCSV2, Ext::kDPB2, Ext::kFLAGM2, Ext::kFRINTTS, Ext::kSB, Ext::kSPECRES, Ext::kSSBS);
ASMJIT_FALLTHROUGH;
case 4: // ARMv8.4
// Missing: AMUv1, SEL2, TLBIOS, TLBIRANGE.
features.add(Ext::kDIT, Ext::kDOTPROD, Ext::kFLAGM, Ext::kLRCPC2, Ext::kLSE2, Ext::kMPAM, Ext::kNV, Ext::kTRF);
features.add(Ext::kAMU1, Ext::kDIT, Ext::kDOTPROD, Ext::kFLAGM,
Ext::kLRCPC2, Ext::kLSE2, Ext::kMPAM, Ext::kNV,
Ext::kSEL2, Ext::kTLBIOS, Ext::kTLBIRANGE, Ext::kTRF);
ASMJIT_FALLTHROUGH;
case 3: // ARMv8.3
features.add(Ext::kCCIDX, Ext::kFCMA, Ext::kJSCVT, Ext::kLRCPC, Ext::kPAUTH);
ASMJIT_FALLTHROUGH;
case 2: // ARMv8.2
features.add(Ext::kCRC32, Ext::kDPB, Ext::kPAN2, Ext::kRAS, Ext::kUAO);
features.add(Ext::kDPB, Ext::kPAN2, Ext::kRAS, Ext::kUAO);
ASMJIT_FALLTHROUGH;
case 1: // ARMv8.1
features.add(Ext::kCRC32, Ext::kLOR, Ext::kLSE, Ext::kPAN, Ext::kRDM, Ext::kVHE);
@ -752,9 +757,25 @@ static ASMJIT_FORCE_INLINE void mergeAArch64CPUIDFeatureNA(CpuFeatures::ARM& fea
if (f3 != Ext::kNone) features.addIf(val >= 3, f3);
}
// Merges a feature that contains 0b0000 when it doesn't exist and starts at 0b0001 when it does.
// Merges a feature identified by a single bit at `offset`.
ASMJIT_MAYBE_UNUSED
static ASMJIT_FORCE_INLINE void mergeAArch64CPUIDFeatureExt(CpuFeatures::ARM& features, uint64_t regBits, uint32_t offset,
static ASMJIT_FORCE_INLINE void mergeAArch64CPUIDFeature1B(CpuFeatures::ARM& features, uint64_t regBits, uint32_t offset, Ext::Id f1) noexcept {
features.addIf((regBits & (uint64_t(1) << offset)) != 0, f1);
}
// Merges a feature-list starting from 0b01 when it does (0b00 means feature not supported).
ASMJIT_MAYBE_UNUSED
static ASMJIT_FORCE_INLINE void mergeAArch64CPUIDFeature2B(CpuFeatures::ARM& features, uint64_t regBits, uint32_t offset, Ext::Id f1, Ext::Id f2, Ext::Id f3) noexcept {
uint32_t val = uint32_t((regBits >> offset) & 0x3u);
if (f1 != Ext::kNone) features.addIf(val >= 1, f1);
if (f2 != Ext::kNone) features.addIf(val >= 2, f2);
if (f3 != Ext::kNone) features.addIf(val == 3, f3);
}
// Merges a feature-list starting from 0b0001 when it does (0b0000 means feature not supported).
ASMJIT_MAYBE_UNUSED
static ASMJIT_FORCE_INLINE void mergeAArch64CPUIDFeature4B(CpuFeatures::ARM& features, uint64_t regBits, uint32_t offset,
Ext::Id f1,
Ext::Id f2 = Ext::kNone,
Ext::Id f3 = Ext::kNone,
@ -770,65 +791,70 @@ static ASMJIT_FORCE_INLINE void mergeAArch64CPUIDFeatureExt(CpuFeatures::ARM& fe
if (f4 != Ext::kNone) features.addIf(val >= 4, f4);
}
#define MERGE_FEATURE_N_A(identifier, reg, offset, ...) mergeAArch64CPUIDFeatureNA(cpu.features().arm(), reg, offset, __VA_ARGS__)
#define MERGE_FEATURE_EXT(identifier, reg, offset, ...) mergeAArch64CPUIDFeatureExt(cpu.features().arm(), reg, offset, __VA_ARGS__)
// Merges a feature that is identified by an exact bit-combination of 4 bits.
ASMJIT_MAYBE_UNUSED
static ASMJIT_FORCE_INLINE void mergeAArch64CPUIDFeature4S(CpuFeatures::ARM& features, uint64_t regBits, uint32_t offset, uint32_t value, Ext::Id f1) noexcept {
features.addIf(uint32_t((regBits >> offset) & 0xFu) == value, f1);
}
#define MERGE_FEATURE_NA(identifier, reg, offset, ...) mergeAArch64CPUIDFeatureNA(cpu.features().arm(), reg, offset, __VA_ARGS__)
#define MERGE_FEATURE_1B(identifier, reg, offset, ...) mergeAArch64CPUIDFeature1B(cpu.features().arm(), reg, offset, __VA_ARGS__)
#define MERGE_FEATURE_2B(identifier, reg, offset, ...) mergeAArch64CPUIDFeature2B(cpu.features().arm(), reg, offset, __VA_ARGS__)
#define MERGE_FEATURE_4B(identifier, reg, offset, ...) mergeAArch64CPUIDFeature4B(cpu.features().arm(), reg, offset, __VA_ARGS__)
#define MERGE_FEATURE_4S(identifier, reg, offset, ...) mergeAArch64CPUIDFeature4S(cpu.features().arm(), reg, offset, __VA_ARGS__)
// Detects features based on the content of ID_AA64PFR0_EL1 and ID_AA64PFR1_EL1 registers.
ASMJIT_MAYBE_UNUSED
static ASMJIT_FAVOR_SIZE void detectAArch64FeaturesViaCPUID_AA64PFR0_AA64PFR1(CpuInfo& cpu, uint64_t fpr0, uint64_t fpr1) noexcept {
static inline void detectAArch64FeaturesViaCPUID_AA64PFR0_AA64PFR1(CpuInfo& cpu, uint64_t fpr0, uint64_t fpr1) noexcept {
// ID_AA64PFR0_EL1
// ===============
// FP and AdvSIMD bits should match (i.e. if FP features FP16, ASIMD must feature it too).
MERGE_FEATURE_N_A("FP bits [19:16]" , fpr0, 16, Ext::kFP, Ext::kFP16);
MERGE_FEATURE_N_A("AdvSIMD bits [23:20]" , fpr0, 20, Ext::kASIMD, Ext::kFP16);
MERGE_FEATURE_NA("FP bits [19:16]" , fpr0, 16, Ext::kFP, Ext::kFP16);
MERGE_FEATURE_NA("AdvSIMD bits [23:20]" , fpr0, 20, Ext::kASIMD, Ext::kFP16);
/*
MERGE_FEATURE_EXT("GIC bits [27:24]" , fpr0, 24, ...);
*/
MERGE_FEATURE_EXT("RAS bits [31:28]" , fpr0, 28, Ext::kRAS, Ext::kRAS1_1, Ext::kRAS2);
MERGE_FEATURE_EXT("SVE bits [35:32]" , fpr0, 32, Ext::kSVE);
/*
MERGE_FEATURE_EXT("SEL2 bits [39:36]" , fpr0, 36, ...);
MERGE_FEATURE_EXT("MPAM bits [43:40]" , fpr0, 40, ...);
MERGE_FEATURE_EXT("AMU bits [47:44]" , fpr0, 44, ...);
*/
MERGE_FEATURE_EXT("DIT bits [51:48]" , fpr0, 48, Ext::kDIT);
MERGE_FEATURE_EXT("RME bits [55:52]" , fpr0, 52, Ext::kRME);
/*
MERGE_FEATURE_EXT("CSV2 bits [59:56]" , fpr0, 56, ...);
MERGE_FEATURE_EXT("CSV3 bits [63:60]" , fpr0, 60, ...);
MERGE_FEATURE_4B("GIC bits [27:24]" , fpr0, 24, ...);
*/
MERGE_FEATURE_4B("RAS bits [31:28]" , fpr0, 28, Ext::kRAS, Ext::kRAS1_1, Ext::kRAS2);
MERGE_FEATURE_4B("SVE bits [35:32]" , fpr0, 32, Ext::kSVE);
MERGE_FEATURE_4B("SEL2 bits [39:36]" , fpr0, 36, Ext::kSEL2);
MERGE_FEATURE_4B("MPAM bits [43:40]" , fpr0, 40, Ext::kMPAM);
MERGE_FEATURE_4B("AMU bits [47:44]" , fpr0, 44, Ext::kAMU1, Ext::kAMU1_1);
MERGE_FEATURE_4B("DIT bits [51:48]" , fpr0, 48, Ext::kDIT);
MERGE_FEATURE_4B("RME bits [55:52]" , fpr0, 52, Ext::kRME);
MERGE_FEATURE_4B("CSV2 bits [59:56]" , fpr0, 56, Ext::kCSV2, Ext::kCSV2, Ext::kCSV2, Ext::kCSV2_3);
MERGE_FEATURE_4B("CSV3 bits [63:60]" , fpr0, 60, Ext::kCSV3);
// ID_AA64PFR1_EL1
// ===============
MERGE_FEATURE_EXT("BT bits [3:0]" , fpr1, 0, Ext::kBTI);
MERGE_FEATURE_EXT("SSBS bits [7:4]" , fpr1, 4, Ext::kSSBS, Ext::kSSBS2);
MERGE_FEATURE_EXT("MTE bits [11:8]" , fpr1, 8, Ext::kMTE, Ext::kMTE2, Ext::kMTE3);
MERGE_FEATURE_4B("BT bits [3:0]" , fpr1, 0, Ext::kBTI);
MERGE_FEATURE_4B("SSBS bits [7:4]" , fpr1, 4, Ext::kSSBS, Ext::kSSBS2);
MERGE_FEATURE_4B("MTE bits [11:8]" , fpr1, 8, Ext::kMTE, Ext::kMTE2, Ext::kMTE3);
/*
MERGE_FEATURE_EXT("RAS_frac bits [15:12]" , fpr1, 12, ...);
MERGE_FEATURE_EXT("MPAM_frac bits [19:16]" , fpr1, 16, ...);
MERGE_FEATURE_4B("RAS_frac bits [15:12]" , fpr1, 12, ...);
MERGE_FEATURE_4B("MPAM_frac bits [19:16]" , fpr1, 16, ...);
*/
MERGE_FEATURE_EXT("SME bits [27:24]" , fpr1, 24, Ext::kSME, Ext::kSME2);
MERGE_FEATURE_EXT("RNDR_trap bits [31:28]" , fpr1, 28, Ext::kRNG_TRAP);
MERGE_FEATURE_4B("SME bits [27:24]" , fpr1, 24, Ext::kSME, Ext::kSME2);
MERGE_FEATURE_4B("RNDR_trap bits [31:28]" , fpr1, 28, Ext::kRNG_TRAP);
/*
MERGE_FEATURE_EXT("CSV2_frac bits [35:32]" , fpr1, 32, ...);
MERGE_FEATURE_4B("CSV2_frac bits [35:32]" , fpr1, 32, ...);
*/
MERGE_FEATURE_EXT("NMI bits [39:36]" , fpr1, 36, Ext::kNMI);
MERGE_FEATURE_4B("NMI bits [39:36]" , fpr1, 36, Ext::kNMI);
/*
MERGE_FEATURE_EXT("MTE_frac bits [43:40]" , fpr1, 40, ...);
MERGE_FEATURE_EXT("GCS bits [47:44]" , fpr1, 44, ...);
MERGE_FEATURE_4B("MTE_frac bits [43:40]" , fpr1, 40, ...);
*/
MERGE_FEATURE_EXT("THE bits [51:48]" , fpr1, 48, Ext::kTHE);
MERGE_FEATURE_4B("GCS bits [47:44]" , fpr1, 44, Ext::kGCS);
MERGE_FEATURE_4B("THE bits [51:48]" , fpr1, 48, Ext::kTHE);
// MTEX extensions are only available when MTE3 is available.
if (cpu.features().arm().hasMTE3())
MERGE_FEATURE_EXT("MTEX bits [55:52]" , fpr1, 52, Ext::kMTE4);
MERGE_FEATURE_4B("MTEX bits [55:52]" , fpr1, 52, Ext::kMTE4);
/*
MERGE_FEATURE_EXT("DF2 bits [59:56]" , fpr1, 56, ...);
MERGE_FEATURE_EXT("PFAR bits [63:60]" , fpr1, 60, ...);
MERGE_FEATURE_4B("DF2 bits [59:56]" , fpr1, 56, ...);
*/
MERGE_FEATURE_4B("PFAR bits [63:60]" , fpr1, 60, Ext::kPFAR);
// ID_AA64PFR0_EL1 + ID_AA64PFR1_EL1
// =================================
@ -849,176 +875,212 @@ static ASMJIT_FAVOR_SIZE void detectAArch64FeaturesViaCPUID_AA64PFR0_AA64PFR1(Cp
// Detects features based on the content of ID_AA64ISAR0_EL1 and ID_AA64ISAR1_EL1 registers.
ASMJIT_MAYBE_UNUSED
static ASMJIT_FAVOR_SIZE void detectAArch64FeaturesViaCPUID_AA64ISAR0_AA64ISAR1(CpuInfo& cpu, uint64_t isar0, uint64_t isar1) noexcept {
static inline void detectAArch64FeaturesViaCPUID_AA64ISAR0_AA64ISAR1(CpuInfo& cpu, uint64_t isar0, uint64_t isar1) noexcept {
// ID_AA64ISAR0_EL1
// ================
MERGE_FEATURE_EXT("AES bits [7:4]" , isar0, 4, Ext::kAES, Ext::kPMULL);
MERGE_FEATURE_EXT("SHA1 bits [11:8]" , isar0, 8, Ext::kSHA1);
MERGE_FEATURE_EXT("SHA2 bits [15:12]" , isar0, 12, Ext::kSHA256, Ext::kSHA512);
MERGE_FEATURE_EXT("CRC32 bits [19:16]" , isar0, 16, Ext::kCRC32);
MERGE_FEATURE_EXT("Atomic bits [23:20]" , isar0, 20, Ext::kNone, Ext::kLSE, Ext::kLSE128);
MERGE_FEATURE_EXT("TME bits [27:24]" , isar0, 24, Ext::kTME);
MERGE_FEATURE_EXT("RDM bits [31:28]" , isar0, 28, Ext::kRDM);
MERGE_FEATURE_EXT("SHA3 bits [35:32]" , isar0, 32, Ext::kSHA3);
MERGE_FEATURE_EXT("SM3 bits [39:36]" , isar0, 36, Ext::kSM3);
MERGE_FEATURE_EXT("SM4 bits [43:40]" , isar0, 40, Ext::kSM4);
MERGE_FEATURE_EXT("DP bits [47:44]" , isar0, 44, Ext::kDOTPROD);
MERGE_FEATURE_EXT("FHM bits [51:48]" , isar0, 48, Ext::kFHM);
MERGE_FEATURE_EXT("TS bits [55:52]" , isar0, 52, Ext::kFLAGM, Ext::kFLAGM2);
MERGE_FEATURE_4B("AES bits [7:4]" , isar0, 4, Ext::kAES, Ext::kPMULL);
MERGE_FEATURE_4B("SHA1 bits [11:8]" , isar0, 8, Ext::kSHA1);
MERGE_FEATURE_4B("SHA2 bits [15:12]" , isar0, 12, Ext::kSHA256, Ext::kSHA512);
MERGE_FEATURE_4B("CRC32 bits [19:16]" , isar0, 16, Ext::kCRC32);
MERGE_FEATURE_4B("Atomic bits [23:20]" , isar0, 20, Ext::kNone, Ext::kLSE, Ext::kLSE128);
MERGE_FEATURE_4B("TME bits [27:24]" , isar0, 24, Ext::kTME);
MERGE_FEATURE_4B("RDM bits [31:28]" , isar0, 28, Ext::kRDM);
MERGE_FEATURE_4B("SHA3 bits [35:32]" , isar0, 32, Ext::kSHA3);
MERGE_FEATURE_4B("SM3 bits [39:36]" , isar0, 36, Ext::kSM3);
MERGE_FEATURE_4B("SM4 bits [43:40]" , isar0, 40, Ext::kSM4);
MERGE_FEATURE_4B("DP bits [47:44]" , isar0, 44, Ext::kDOTPROD);
MERGE_FEATURE_4B("FHM bits [51:48]" , isar0, 48, Ext::kFHM);
MERGE_FEATURE_4B("TS bits [55:52]" , isar0, 52, Ext::kFLAGM, Ext::kFLAGM2);
/*
MERGE_FEATURE_EXT("TLB bits [59:56]" , isar0, 56, ...);
MERGE_FEATURE_4B("TLB bits [59:56]" , isar0, 56, ...);
*/
MERGE_FEATURE_EXT("RNDR bits [63:60]" , isar0, 60, Ext::kFLAGM, Ext::kRNG);
MERGE_FEATURE_4B("RNDR bits [63:60]" , isar0, 60, Ext::kFLAGM, Ext::kRNG);
// ID_AA64ISAR1_EL1
// ================
MERGE_FEATURE_EXT("DPB bits [3:0]" , isar1, 0, Ext::kDPB, Ext::kDPB2);
MERGE_FEATURE_4B("DPB bits [3:0]" , isar1, 0, Ext::kDPB, Ext::kDPB2);
/*
MERGE_FEATURE_EXT("APA bits [7:4]" , isar1, 4, ...);
MERGE_FEATURE_EXT("API bits [11:8]" , isar1, 8, ...);
MERGE_FEATURE_4B("APA bits [7:4]" , isar1, 4, ...);
MERGE_FEATURE_4B("API bits [11:8]" , isar1, 8, ...);
*/
MERGE_FEATURE_EXT("JSCVT bits [15:12]" , isar1, 12, Ext::kJSCVT);
MERGE_FEATURE_EXT("FCMA bits [19:16]" , isar1, 16, Ext::kFCMA);
MERGE_FEATURE_EXT("LRCPC bits [23:20]" , isar1, 20, Ext::kLRCPC, Ext::kLRCPC2, Ext::kLRCPC3);
MERGE_FEATURE_4B("JSCVT bits [15:12]" , isar1, 12, Ext::kJSCVT);
MERGE_FEATURE_4B("FCMA bits [19:16]" , isar1, 16, Ext::kFCMA);
MERGE_FEATURE_4B("LRCPC bits [23:20]" , isar1, 20, Ext::kLRCPC, Ext::kLRCPC2, Ext::kLRCPC3);
/*
MERGE_FEATURE_EXT("GPA bits [27:24]" , isar1, 24, ...);
MERGE_FEATURE_EXT("GPI bits [31:28]" , isar1, 28, ...);
MERGE_FEATURE_4B("GPA bits [27:24]" , isar1, 24, ...);
MERGE_FEATURE_4B("GPI bits [31:28]" , isar1, 28, ...);
*/
MERGE_FEATURE_EXT("FRINTTS bits [35:32]" , isar1, 32, Ext::kFRINTTS);
MERGE_FEATURE_EXT("SB bits [39:36]" , isar1, 36, Ext::kSB);
MERGE_FEATURE_EXT("SPECRES bits [43:40]" , isar1, 40, Ext::kSPECRES, Ext::kSPECRES2);
MERGE_FEATURE_EXT("BF16 bits [47:44]" , isar1, 44, Ext::kBF16, Ext::kEBF16);
MERGE_FEATURE_EXT("DGH bits [51:48]" , isar1, 48, Ext::kDGH);
MERGE_FEATURE_EXT("I8MM bits [55:52]" , isar1, 52, Ext::kI8MM);
MERGE_FEATURE_EXT("XS bits [59:56]" , isar1, 56, Ext::kXS);
MERGE_FEATURE_EXT("LS64 bits [63:60]" , isar1, 60, Ext::kLS64, Ext::kLS64_V, Ext::kLS64_ACCDATA);
MERGE_FEATURE_4B("FRINTTS bits [35:32]" , isar1, 32, Ext::kFRINTTS);
MERGE_FEATURE_4B("SB bits [39:36]" , isar1, 36, Ext::kSB);
MERGE_FEATURE_4B("SPECRES bits [43:40]" , isar1, 40, Ext::kSPECRES, Ext::kSPECRES2);
MERGE_FEATURE_4B("BF16 bits [47:44]" , isar1, 44, Ext::kBF16, Ext::kEBF16);
MERGE_FEATURE_4B("DGH bits [51:48]" , isar1, 48, Ext::kDGH);
MERGE_FEATURE_4B("I8MM bits [55:52]" , isar1, 52, Ext::kI8MM);
MERGE_FEATURE_4B("XS bits [59:56]" , isar1, 56, Ext::kXS);
MERGE_FEATURE_4B("LS64 bits [63:60]" , isar1, 60, Ext::kLS64, Ext::kLS64_V, Ext::kLS64_ACCDATA);
}
// Detects features based on the content of ID_AA64ISAR2_EL1 register.
ASMJIT_MAYBE_UNUSED
static ASMJIT_FAVOR_SIZE void detectAArch64FeaturesViaCPUID_AA64ISAR2(CpuInfo& cpu, uint64_t isar2) noexcept {
MERGE_FEATURE_EXT("WFxT bits [3:0]" , isar2, 0, Ext::kNone, Ext::kWFXT);
MERGE_FEATURE_EXT("RPRES bits [7:4]" , isar2, 4, Ext::kRPRES);
static inline void detectAArch64FeaturesViaCPUID_AA64ISAR2(CpuInfo& cpu, uint64_t isar2) noexcept {
MERGE_FEATURE_4B("WFxT bits [3:0]" , isar2, 0, Ext::kNone, Ext::kWFXT);
MERGE_FEATURE_4B("RPRES bits [7:4]" , isar2, 4, Ext::kRPRES);
/*
MERGE_FEATURE_EXT("GPA3 bits [11:8]" , isar2, 8, ...);
MERGE_FEATURE_EXT("APA3 bits [15:12]" , isar2, 12, ...);
MERGE_FEATURE_4B("GPA3 bits [11:8]" , isar2, 8, ...);
MERGE_FEATURE_4B("APA3 bits [15:12]" , isar2, 12, ...);
*/
MERGE_FEATURE_EXT("MOPS bits [19:16]" , isar2, 16, Ext::kMOPS);
MERGE_FEATURE_EXT("BC bits [23:20]" , isar2, 20, Ext::kHBC);
/*
MERGE_FEATURE_EXT("PAC_frac bits [27:24]" , isar2, 24, ...);
*/
MERGE_FEATURE_EXT("CLRBHB bits [31:28]" , isar2, 28, Ext::kCLRBHB);
MERGE_FEATURE_EXT("SYSREG128 bits [35:32]" , isar2, 32, Ext::kSYSREG128);
MERGE_FEATURE_EXT("SYSINSTR128 bits [39:36]" , isar2, 36, Ext::kSYSINSTR128);
MERGE_FEATURE_EXT("PRFMSLC bits [43:40]" , isar2, 40, Ext::kPRFMSLC);
MERGE_FEATURE_EXT("RPRFM bits [51:48]" , isar2, 48, Ext::kRPRFM);
MERGE_FEATURE_EXT("CSSC bits [55:52]" , isar2, 52, Ext::kCSSC);
MERGE_FEATURE_4B("MOPS bits [19:16]" , isar2, 16, Ext::kMOPS);
MERGE_FEATURE_4B("BC bits [23:20]" , isar2, 20, Ext::kHBC);
MERGE_FEATURE_4B("PAC_frac bits [27:24]" , isar2, 24, Ext::kCONSTPACFIELD);
MERGE_FEATURE_4B("CLRBHB bits [31:28]" , isar2, 28, Ext::kCLRBHB);
MERGE_FEATURE_4B("SYSREG128 bits [35:32]" , isar2, 32, Ext::kSYSREG128);
MERGE_FEATURE_4B("SYSINSTR128 bits [39:36]" , isar2, 36, Ext::kSYSINSTR128);
MERGE_FEATURE_4B("PRFMSLC bits [43:40]" , isar2, 40, Ext::kPRFMSLC);
MERGE_FEATURE_4B("RPRFM bits [51:48]" , isar2, 48, Ext::kRPRFM);
MERGE_FEATURE_4B("CSSC bits [55:52]" , isar2, 52, Ext::kCSSC);
MERGE_FEATURE_4B("LUT bits [59:56]" , isar2, 56, Ext::kLUT);
}
// TODO: This register is not accessed at the moment.
#if 0
// Detects features based on the content of ID_AA64ISAR3_EL1register.
ASMJIT_MAYBE_UNUSED
static ASMJIT_FAVOR_SIZE void detectAArch64FeaturesViaCPUID_AA64MMFR0(CpuInfo& cpu, uint64_t mmfr0) noexcept {
static inline void detectAArch64FeaturesViaCPUID_AA64ISAR3(CpuInfo& cpu, uint64_t isar3) noexcept {
// ID_AA64ISAR3_EL1
// ================
MERGE_FEATURE_4B("CPA bits [3:0]" , isar3, 0, Ext::kCPA, Ext::kCPA2);
MERGE_FEATURE_4B("FAMINMAX bits [7:4]" , isar3, 4, Ext::kFAMINMAX);
MERGE_FEATURE_4B("TLBIW bits [11:8]" , isar3, 8, Ext::kTLBIW);
}
#endif
ASMJIT_MAYBE_UNUSED
static inline void detectAArch64FeaturesViaCPUID_AA64MMFR0(CpuInfo& cpu, uint64_t mmfr0) noexcept {
// ID_AA64MMFR0_EL1
// ================
/*
MERGE_FEATURE_EXT("PARange bits [3:0]" , mmfr0, 0, ...);
MERGE_FEATURE_EXT("ASIDBits bits [7:4]" , mmfr0, 4, ...);
MERGE_FEATURE_EXT("BigEnd bits [11:8]" , mmfr0, 8, ...);
MERGE_FEATURE_EXT("SNSMem bits [15:12]" , mmfr0, 12, ...);
MERGE_FEATURE_EXT("BigEndEL0 bits [19:16]" , mmfr0, 16, ...);
MERGE_FEATURE_EXT("TGran16 bits [23:20]" , mmfr0, 20, ...);
MERGE_FEATURE_EXT("TGran64 bits [27:24]" , mmfr0, 24, ...);
MERGE_FEATURE_EXT("TGran4 bits [31:28]" , mmfr0, 28, ...);
MERGE_FEATURE_EXT("TGran16_2 bits [35:32]" , mmfr0, 32, ...);
MERGE_FEATURE_EXT("TGran64_2 bits [39:36]" , mmfr0, 36, ...);
MERGE_FEATURE_EXT("TGran4_2 bits [43:40]" , mmfr0, 40, ...);
MERGE_FEATURE_EXT("ExS bits [47:44]" , mmfr0, 44, ...);
MERGE_FEATURE_4B("PARange bits [3:0]" , mmfr0, 0, ...);
MERGE_FEATURE_4B("ASIDBits bits [7:4]" , mmfr0, 4, ...);
MERGE_FEATURE_4B("BigEnd bits [11:8]" , mmfr0, 8, ...);
MERGE_FEATURE_4B("SNSMem bits [15:12]" , mmfr0, 12, ...);
MERGE_FEATURE_4B("BigEndEL0 bits [19:16]" , mmfr0, 16, ...);
MERGE_FEATURE_4B("TGran16 bits [23:20]" , mmfr0, 20, ...);
MERGE_FEATURE_4B("TGran64 bits [27:24]" , mmfr0, 24, ...);
MERGE_FEATURE_4B("TGran4 bits [31:28]" , mmfr0, 28, ...);
MERGE_FEATURE_4B("TGran16_2 bits [35:32]" , mmfr0, 32, ...);
MERGE_FEATURE_4B("TGran64_2 bits [39:36]" , mmfr0, 36, ...);
MERGE_FEATURE_4B("TGran4_2 bits [43:40]" , mmfr0, 40, ...);
MERGE_FEATURE_4B("ExS bits [47:44]" , mmfr0, 44, ...);
*/
MERGE_FEATURE_EXT("FGT bits [59:56]" , mmfr0, 56, Ext::kFGT, Ext::kFGT2);
MERGE_FEATURE_EXT("ECV bits [63:60]" , mmfr0, 60, Ext::kECV);
MERGE_FEATURE_4B("FGT bits [59:56]" , mmfr0, 56, Ext::kFGT, Ext::kFGT2);
MERGE_FEATURE_4B("ECV bits [63:60]" , mmfr0, 60, Ext::kECV);
}
ASMJIT_MAYBE_UNUSED
static ASMJIT_FAVOR_SIZE void detectAArch64FeaturesViaCPUID_AA64MMFR1(CpuInfo& cpu, uint64_t mmfr1) noexcept {
static inline void detectAArch64FeaturesViaCPUID_AA64MMFR1(CpuInfo& cpu, uint64_t mmfr1) noexcept {
// ID_AA64MMFR1_EL1
// ================
MERGE_FEATURE_4B("HAFDBS bits [3:0]" , mmfr1, 0, Ext::kHAFDBS, Ext::kNone, Ext::kHAFT, Ext::kHDBSS);
MERGE_FEATURE_4B("VMIDBits bits [7:4]" , mmfr1, 4, Ext::kVMID16);
MERGE_FEATURE_4B("VH bits [11:8]" , mmfr1, 8, Ext::kVHE);
MERGE_FEATURE_4B("HPDS bits [15:12]" , mmfr1, 12, Ext::kHPDS, Ext::kHPDS2);
MERGE_FEATURE_4B("LO bits [19:16]" , mmfr1, 16, Ext::kLOR);
MERGE_FEATURE_4B("PAN bits [23:20]" , mmfr1, 20, Ext::kPAN, Ext::kPAN2, Ext::kPAN3);
/*
MERGE_FEATURE_EXT("HAFDBS bits [3:0]" , mmfr1, 0, ...);
MERGE_FEATURE_EXT("VMIDBits bits [7:4]" , mmfr1, 4, ...);
MERGE_FEATURE_4B("SpecSEI bits [27:24]" , mmfr1, 24, ...);
*/
MERGE_FEATURE_EXT("VH bits [11:8]" , mmfr1, 8, Ext::kVHE);
MERGE_FEATURE_4B("XNX bits [31:28]" , mmfr1, 28, Ext::kXNX);
/*
MERGE_FEATURE_EXT("HPDS bits [15:12]" , mmfr1, 12, ...);
MERGE_FEATURE_4B("TWED bits [35:32]" , mmfr1, 32, ...);
MERGE_FEATURE_4B("ETS bits [39:36]" , mmfr1, 36, ...);
*/
MERGE_FEATURE_EXT("LO bits [19:16]" , mmfr1, 16, Ext::kLOR);
MERGE_FEATURE_EXT("PAN bits [23:20]" , mmfr1, 20, Ext::kPAN, Ext::kPAN2, Ext::kPAN3);
MERGE_FEATURE_4B("HCX bits [43:40]" , mmfr1, 40, Ext::kHCX);
MERGE_FEATURE_4B("AFP bits [47:44]" , mmfr1, 44, Ext::kAFP);
/*
MERGE_FEATURE_EXT("SpecSEI bits [27:24]" , mmfr1, 24, ...);
MERGE_FEATURE_EXT("XNX bits [31:28]" , mmfr1, 28, ...);
MERGE_FEATURE_EXT("TWED bits [35:32]" , mmfr1, 32, ...);
MERGE_FEATURE_EXT("ETS bits [39:36]" , mmfr1, 36, ...);
*/
MERGE_FEATURE_EXT("HCX bits [43:40]" , mmfr1, 40, Ext::kHCX);
MERGE_FEATURE_EXT("AFP bits [47:44]" , mmfr1, 44, Ext::kAFP);
/*
MERGE_FEATURE_EXT("nTLBPA bits [51:48]" , mmfr1, 48, ...);
MERGE_FEATURE_EXT("TIDCP1 bits [55:52]" , mmfr1, 52, ...);
MERGE_FEATURE_EXT("CMOW bits [59:56]" , mmfr1, 56, ...);
MERGE_FEATURE_EXT("ECBHB bits [63:60]" , mmfr1, 60, ...);
MERGE_FEATURE_4B("nTLBPA bits [51:48]" , mmfr1, 48, ...);
MERGE_FEATURE_4B("TIDCP1 bits [55:52]" , mmfr1, 52, ...);
*/
MERGE_FEATURE_4B("CMOW bits [59:56]" , mmfr1, 56, Ext::kCMOW);
MERGE_FEATURE_4B("ECBHB bits [63:60]" , mmfr1, 60, Ext::kECBHB);
}
ASMJIT_MAYBE_UNUSED
static ASMJIT_FAVOR_SIZE void detectAArch64FeaturesViaCPUID_AA64MMFR2(CpuInfo& cpu, uint64_t mmfr2) noexcept {
static inline void detectAArch64FeaturesViaCPUID_AA64MMFR2(CpuInfo& cpu, uint64_t mmfr2) noexcept {
// ID_AA64MMFR2_EL1
// ================
/*
MERGE_FEATURE_EXT("CnP bits [3:0]" , mmfr2, 0, ...);
MERGE_FEATURE_4B("CnP bits [3:0]" , mmfr2, 0, ...);
*/
MERGE_FEATURE_EXT("UAO bits [7:4]" , mmfr2, 4, Ext::kUAO);
MERGE_FEATURE_4B("UAO bits [7:4]" , mmfr2, 4, Ext::kUAO);
/*
MERGE_FEATURE_EXT("LSM bits [11:8]" , mmfr2, 8, ...);
MERGE_FEATURE_EXT("IESB bits [15:12]" , mmfr2, 12, ...);
MERGE_FEATURE_EXT("VARange bits [19:16]" , mmfr2, 16, ...);
MERGE_FEATURE_4B("LSM bits [11:8]" , mmfr2, 8, ...);
MERGE_FEATURE_4B("IESB bits [15:12]" , mmfr2, 12, ...);
*/
MERGE_FEATURE_EXT("CCIDX bits [23:20]" , mmfr2, 20, Ext::kCCIDX);
MERGE_FEATURE_EXT("NV bits [27:24]" , mmfr2, 24, Ext::kNV, Ext::kNV2);
MERGE_FEATURE_4B("VARange bits [19:16]" , mmfr2, 16, Ext::kLVA, Ext::kLVA3);
MERGE_FEATURE_4B("CCIDX bits [23:20]" , mmfr2, 20, Ext::kCCIDX);
MERGE_FEATURE_4B("NV bits [27:24]" , mmfr2, 24, Ext::kNV, Ext::kNV2);
/*
MERGE_FEATURE_EXT("ST bits [31:28]" , mmfr2, 28, ...);
MERGE_FEATURE_4B("ST bits [31:28]" , mmfr2, 28, ...);
*/
MERGE_FEATURE_EXT("AT bits [35:32]" , mmfr2, 32, Ext::kLSE2);
MERGE_FEATURE_4B("AT bits [35:32]" , mmfr2, 32, Ext::kLSE2);
/*
MERGE_FEATURE_EXT("IDS bits [39:36]" , mmfr2, 36, ...);
MERGE_FEATURE_EXT("FWB bits [43:40]" , mmfr2, 40, ...);
MERGE_FEATURE_EXT("TTL bits [51:48]" , mmfr2, 48, ...);
MERGE_FEATURE_EXT("BBM bits [55:52]" , mmfr2, 52, ...);
MERGE_FEATURE_EXT("EVT bits [59:56]" , mmfr2, 56, ...);
MERGE_FEATURE_EXT("E0PD bits [63:60]" , mmfr2, 60, ...);
MERGE_FEATURE_4B("IDS bits [39:36]" , mmfr2, 36, ...);
MERGE_FEATURE_4B("FWB bits [43:40]" , mmfr2, 40, ...);
MERGE_FEATURE_4B("TTL bits [51:48]" , mmfr2, 48, ...);
MERGE_FEATURE_4B("BBM bits [55:52]" , mmfr2, 52, ...);
MERGE_FEATURE_4B("EVT bits [59:56]" , mmfr2, 56, ...);
MERGE_FEATURE_4B("E0PD bits [63:60]" , mmfr2, 60, ...);
*/
}
// Detects features based on the content of ID_AA64ZFR0_EL1 register.
ASMJIT_MAYBE_UNUSED
static ASMJIT_FAVOR_SIZE void detectAArch64FeaturesViaCPUID_AA64ZFR0(CpuInfo& cpu, uint64_t zfr0) noexcept {
MERGE_FEATURE_EXT("SVEver bits [3:0]" , zfr0, 0, Ext::kSVE2, Ext::kSVE2_1);
MERGE_FEATURE_EXT("AES bits [7:4]" , zfr0, 4, Ext::kSVE_AES, Ext::kSVE_PMULL128);
MERGE_FEATURE_EXT("BitPerm bits [19:16]" , zfr0, 16, Ext::kSVE_BITPERM);
MERGE_FEATURE_EXT("BF16 bits [23:20]" , zfr0, 20, Ext::kSVE_BF16, Ext::kSVE_EBF16);
MERGE_FEATURE_EXT("B16B16 bits [27:24]" , zfr0, 24, Ext::kSVE_B16B16);
MERGE_FEATURE_EXT("SHA3 bits [35:32]" , zfr0, 32, Ext::kSVE_SHA3);
MERGE_FEATURE_EXT("SM4 bits [43:40]" , zfr0, 40, Ext::kSVE_SM4);
MERGE_FEATURE_EXT("I8MM bits [47:44]" , zfr0, 44, Ext::kSVE_I8MM);
MERGE_FEATURE_EXT("F32MM bits [55:52]" , zfr0, 52, Ext::kSVE_F32MM);
MERGE_FEATURE_EXT("F64MM bits [59:56]" , zfr0, 56, Ext::kSVE_F64MM);
static inline void detectAArch64FeaturesViaCPUID_AA64ZFR0(CpuInfo& cpu, uint64_t zfr0) noexcept {
MERGE_FEATURE_4B("SVEver bits [3:0]" , zfr0, 0, Ext::kSVE2, Ext::kSVE2_1);
MERGE_FEATURE_4B("AES bits [7:4]" , zfr0, 4, Ext::kSVE_AES, Ext::kSVE_PMULL128);
MERGE_FEATURE_4B("BitPerm bits [19:16]" , zfr0, 16, Ext::kSVE_BITPERM);
MERGE_FEATURE_4B("BF16 bits [23:20]" , zfr0, 20, Ext::kSVE_BF16, Ext::kSVE_EBF16);
MERGE_FEATURE_4B("B16B16 bits [27:24]" , zfr0, 24, Ext::kSVE_B16B16);
MERGE_FEATURE_4B("SHA3 bits [35:32]" , zfr0, 32, Ext::kSVE_SHA3);
MERGE_FEATURE_4B("SM4 bits [43:40]" , zfr0, 40, Ext::kSVE_SM4);
MERGE_FEATURE_4B("I8MM bits [47:44]" , zfr0, 44, Ext::kSVE_I8MM);
MERGE_FEATURE_4B("F32MM bits [55:52]" , zfr0, 52, Ext::kSVE_F32MM);
MERGE_FEATURE_4B("F64MM bits [59:56]" , zfr0, 56, Ext::kSVE_F64MM);
}
#undef MERGE_FEATURE_EXT
#undef MERGE_FEATURE_N_A
ASMJIT_MAYBE_UNUSED
static inline void detectAArch64FeaturesViaCPUID_AA64SMFR0(CpuInfo& cpu, uint64_t smfr0) noexcept {
MERGE_FEATURE_1B("SF8DP2 bit [28]" , smfr0, 29, Ext::kSSVE_FP8DOT2);
MERGE_FEATURE_1B("SF8DP4 bit [29]" , smfr0, 29, Ext::kSSVE_FP8DOT4);
MERGE_FEATURE_1B("SF8FMA bit [30]" , smfr0, 30, Ext::kSSVE_FP8FMA);
MERGE_FEATURE_1B("F32F32 bit [32]" , smfr0, 32, Ext::kSME_F32F32);
MERGE_FEATURE_1B("BI32I32 bit [33]" , smfr0, 33, Ext::kSME_BI32I32);
MERGE_FEATURE_1B("B16F32 bit [34]" , smfr0, 34, Ext::kSME_B16F32);
MERGE_FEATURE_1B("F16F32 bit [35]" , smfr0, 35, Ext::kSME_F16F32);
MERGE_FEATURE_4S("I8I32 bits [39:36]" , smfr0, 36, 0xF, Ext::kSME_I8I32);
MERGE_FEATURE_1B("F8F32 bit [40]" , smfr0, 40, Ext::kSME_F8F32);
MERGE_FEATURE_1B("F8F16 bit [41]" , smfr0, 41, Ext::kSME_F8F16);
MERGE_FEATURE_1B("F16F16 bit [42]" , smfr0, 42, Ext::kSME_F16F16);
MERGE_FEATURE_1B("B16B16 bit [43]" , smfr0, 43, Ext::kSME_B16B16);
MERGE_FEATURE_4S("I16I32 bits [47:44]" , smfr0, 44, 0x5, Ext::kSME_I16I32);
MERGE_FEATURE_1B("F64F64 bit [48]" , smfr0, 48, Ext::kSME_F64F64);
MERGE_FEATURE_4S("I16I64 bits [55:52]" , smfr0, 52, 0xF, Ext::kSME_I16I64);
MERGE_FEATURE_4B("SMEver bits [59:56]" , smfr0, 56, Ext::kSME2, Ext::kSME2_1);
MERGE_FEATURE_1B("LUTv2 bit [60]" , smfr0, 60, Ext::kSME_LUTv2);
MERGE_FEATURE_1B("FA64 bit [63]" , smfr0, 63, Ext::kSME_FA64);
}
#undef MERGE_FEATURE_4S
#undef MERGE_FEATURE_4B
#undef MERGE_FEATURE_2B
#undef MERGE_FEATURE_1B
#undef MERGE_FEATURE_NA
// CpuInfo - Detect - ARM - CPU Vendor Features
// ============================================
@ -1081,11 +1143,10 @@ static ASMJIT_FAVOR_SIZE bool detectARMFeaturesViaAppleFamilyId(CpuInfo& cpu) no
// Apple A14/M1 (ARMv8.5-A).
case uint32_t(Id::kFIRESTORM_ICESTORM):
// Missing: CSV2, CSV3.
populateARMv8AFeatures(features, 4);
features.add(Ext::kAES, Ext::kDPB2, Ext::kECV, Ext::kFHM, Ext::kFLAGM2, Ext::kFP16, Ext::kFP16CONV,
Ext::kFRINTTS, Ext::kPMU, Ext::kPMULL, Ext::kSB, Ext::kSHA1, Ext::kSHA256, Ext::kSHA3,
Ext::kSHA512, Ext::kSSBS);
features.add(Ext::kAES, Ext::kCSV2, Ext::kCSV3, Ext::kDPB2, Ext::kECV, Ext::kFHM, Ext::kFLAGM2,
Ext::kFP16, Ext::kFP16CONV, Ext::kFRINTTS, Ext::kPMU, Ext::kPMULL, Ext::kSB,
Ext::kSHA1, Ext::kSHA256, Ext::kSHA3, Ext::kSHA512, Ext::kSSBS);
return true;
// Apple A15/M2.
@ -1390,7 +1451,7 @@ ASMJIT_AARCH64_DEFINE_CPUID_READ_FN(aarch64ReadZFR0, S3_0_C0_C4_4) // ID_AA64ZFR
// dependent is zeroed, only the bits that are used for CPU feature identification would be present.
//
// References:
// - https://docs.kernel.org/arm64/cpu-feature-registers.html
// - https://docs.kernel.org/arch/arm64/cpu-feature-registers.html
ASMJIT_MAYBE_UNUSED
static ASMJIT_FAVOR_SIZE void detectAArch64FeaturesViaCPUID(CpuInfo& cpu) noexcept {
populateBaseARMFeatures(cpu);
@ -1499,7 +1560,7 @@ static ASMJIT_FAVOR_SIZE void detectARMCpu(CpuInfo& cpu) noexcept {
#ifndef AT_HWCAP
#define AT_HWCAP 16
#endif // AT_HWCAP
#endif // !AT_HWCAP
#ifndef AT_HWCAP2
#define AT_HWCAP2 26
@ -1593,7 +1654,7 @@ static ASMJIT_FAVOR_SIZE void detectARMCpu(CpuInfo& cpu) noexcept {
#else
// Reference:
// - https://docs.kernel.org/arm64/elf_hwcaps.html
// - https://docs.kernel.org/arch/arm64/elf_hwcaps.html
// - https://github.com/torvalds/linux/blob/master/arch/arm64/include/uapi/asm/hwcap.h
static const HWCapMapping hwCapMapping[] = {
{ uint8_t(Ext::kFP) , 0 }, // HWCAP_FP
@ -1677,7 +1738,12 @@ static const HWCapMapping hwCap2Mapping[] = {
{ uint8_t(Ext::kSME_I16I32) , 39 }, // HWCAP2_SME_I16I32
{ uint8_t(Ext::kSME_BI32I32) , 40 }, // HWCAP2_SME_BI32I32
{ uint8_t(Ext::kSME_B16B16) , 41 }, // HWCAP2_SME_B16B16
{ uint8_t(Ext::kSME_F16F16) , 42 } // HWCAP2_SME_F16F16
{ uint8_t(Ext::kSME_F16F16) , 42 }, // HWCAP2_SME_F16F16
{ uint8_t(Ext::kMOPS) , 43 }, // HWCAP2_MOPS
{ uint8_t(Ext::kHBC) , 44 }, // HWCAP2_HBC
{ uint8_t(Ext::kSVE_B16B16) , 45 }, // HWCAP2_SVE_B16B16
{ uint8_t(Ext::kLRCPC3) , 46 }, // HWCAP2_LRCPC3
{ uint8_t(Ext::kLSE128) , 47 }, // HWCAP2_LSE128
};
static ASMJIT_FAVOR_SIZE void detectARMCpu(CpuInfo& cpu) noexcept {
@ -1751,6 +1817,9 @@ static ASMJIT_FAVOR_SIZE void detectARMCpu(CpuInfo& cpu) noexcept {
// Only read CPU_ID_AA64ZFR0 when either SVE or SME is available.
if (cpu.features().arm().hasAny(Ext::kSVE, Ext::kSME)) {
detectAArch64FeaturesViaCPUID_AA64ZFR0(cpu, openbsdReadAArch64CPUID(ID::kAA64ZFR0));
if (cpu.features().arm().hasSME())
detectAArch64FeaturesViaCPUID_AA64SMFR0(cpu, openbsdReadAArch64CPUID(ID::kAA64SMFR0));
}
postProcessARMCpuInfo(cpu);
@ -1883,7 +1952,7 @@ static ASMJIT_FAVOR_SIZE void detectARMCpu(CpuInfo& cpu) noexcept {
#else
#if ASMJIT_ARCH_ARM == 32
#pragma message("[asmjit] Disabling runtime CPU detection - unsupported OS/CPU combination (Unknown OS with ARM CPU)")
#pragma message("[asmjit] Disabling runtime CPU detection - unsupported OS/CPU combination (Unknown OS with AArch32 CPU)")
#else
#pragma message("[asmjit] Disabling runtime CPU detection - unsupported OS/CPU combination (Unknown OS with AArch64 CPU)")
#endif
@ -1902,13 +1971,13 @@ static ASMJIT_FAVOR_SIZE void detectARMCpu(CpuInfo& cpu) noexcept {
// CpuInfo - Detect - Host
// =======================
static uint32_t cpuInfoInitialized;
static CpuInfo cpuInfoGlobal(Globals::NoInit);
const CpuInfo& CpuInfo::host() noexcept {
// This should never cause a problem as the resulting information should always be the same.
// In the worst case it would just be overwritten non-atomically.
if (!cpuInfoInitialized) {
static std::atomic<uint32_t> cpuInfoInitialized;
static CpuInfo cpuInfoGlobal(Globals::NoInit);
// This should never cause a problem as the resulting information should always
// be the same. In the worst case it would just be overwritten non-atomically.
if (!cpuInfoInitialized.load(std::memory_order_relaxed)) {
CpuInfo cpuInfoLocal;
cpuInfoLocal._arch = Arch::kHost;
@ -1918,13 +1987,11 @@ const CpuInfo& CpuInfo::host() noexcept {
x86::detectX86Cpu(cpuInfoLocal);
#elif ASMJIT_ARCH_ARM
arm::detectARMCpu(cpuInfoLocal);
#else
#pragma message("[asmjit] Disabling runtime CPU detection - unsupported OS/CPU combination (Unknown CPU)")
#endif
cpuInfoLocal._hwThreadCount = detectHWThreadCount();
cpuInfoGlobal = cpuInfoLocal;
cpuInfoInitialized = 1;
cpuInfoInitialized.store(1, std::memory_order_seq_cst);
}
return cpuInfoGlobal;

View File

@ -22,11 +22,6 @@ ASMJIT_BEGIN_NAMESPACE
//! Each feature is represented by a single bit in an embedded bit array.
class CpuFeatures {
public:
//! A word that is used to represents feature bits.
typedef Support::BitWord BitWord;
//! Iterator that can iterate all CPU features set.
typedef Support::BitVectorIterator<BitWord> Iterator;
//! \name Constants
//! \{
@ -37,6 +32,13 @@ public:
};
//! \endcond
//! A word that is used to represents feature bits.
typedef Support::BitWord BitWord;
//! Iterator that can iterate all CPU features set.
typedef Support::BitVectorIterator<BitWord> Iterator;
typedef Support::Array<BitWord, kNumBitWords> Bits;
//! \}
//! \name Data
@ -48,15 +50,15 @@ public:
//! \{
//! Data bits.
Support::Array<BitWord, kNumBitWords> _bits;
Bits _bits;
//! \}
//! \name Overloaded Operators
//! \{
ASMJIT_INLINE_NODEBUG bool operator==(const Data& other) noexcept { return eq(other); }
ASMJIT_INLINE_NODEBUG bool operator!=(const Data& other) noexcept { return !eq(other); }
ASMJIT_INLINE_NODEBUG bool operator==(const Data& other) const noexcept { return equals(other); }
ASMJIT_INLINE_NODEBUG bool operator!=(const Data& other) const noexcept { return !equals(other); }
//! \}
@ -88,11 +90,16 @@ public:
return bool((_bits[idx] >> bit) & 0x1);
}
//! \cond NONE
template<typename FeatureId>
ASMJIT_INLINE_NODEBUG bool hasAny(const FeatureId& featureId) const noexcept {
return has(featureId);
}
//! \endcond
//! Tests whether any feature given is present.
//!
//! \note This is a variadic function template that can be used with multiple features.
template<typename FeatureId, typename... Args>
ASMJIT_INLINE_NODEBUG bool hasAny(const FeatureId& featureId, Args&&... otherFeatureIds) const noexcept {
return bool(unsigned(has(featureId)) | unsigned(hasAny(std::forward<Args>(otherFeatureIds)...)));
@ -111,6 +118,7 @@ public:
//! \name Manipulation
//! \{
//! Clears all features set.
ASMJIT_INLINE_NODEBUG void reset() noexcept { _bits.fill(0); }
//! Adds the given CPU `featureId` to the list of features.
@ -164,10 +172,14 @@ public:
}
//! Tests whether this CPU features data matches `other`.
ASMJIT_INLINE_NODEBUG bool eq(const Data& other) const noexcept { return _bits == other._bits; }
ASMJIT_INLINE_NODEBUG bool equals(const Data& other) const noexcept { return _bits == other._bits; }
#if !defined(ASMJIT_NO_DEPRECATED)
ASMJIT_DEPRECATED("Use CpuFeatures::Data::equals() instead")
ASMJIT_INLINE_NODEBUG bool eq(const Data& other) const noexcept { return equals(other); }
#endif // !ASMJIT_NO_DEPRECATED
//! \}
};
//! X86 specific features data.
@ -333,6 +345,7 @@ public:
};
#define ASMJIT_X86_FEATURE(FEATURE) \
/*! Tests whether FEATURE is present. */ \
ASMJIT_INLINE_NODEBUG bool has##FEATURE() const noexcept { return has(X86::k##FEATURE); }
ASMJIT_X86_FEATURE(MT)
@ -505,26 +518,45 @@ public:
kTHUMB, //!< CPU has THUMB (16-bit THUMB encoding) {A32}.
kTHUMBv2, //!< CPU has THUMBv2 (32-bit THUMB encoding) {A32}.
kABLE, //!< CPU has ABLE (address breakpoint linking extension) {A64}.
kADERR, //!< CPU has ADERR (asynchronous device error exceptions) {A64}.
kAES, //!< CPU has AES (ASIMD AES instructions).
kAFP, //!< CPU has AFP (alternate floating-point behavior) {A64}.
kAIE, //!< CPU has AIE (memory attribute index enhancement) {A64}.
kAMU1, //!< CPU has AMUv1 (activity monitors extension version 1) {A64}.
kAMU1_1, //!< CPU has AMUv1p1 (activity monitors extension version 1.1) {A64}.
kANERR, //!< CPU has ANERR (asynchronous normal error exception) {A64}.
kASIMD, //!< CPU has ASIMD (NEON on ARM/THUMB).
kBF16, //!< CPU has BF16 (BFloat16 instructions) {A64}.
kBRBE, //!< CPU has BRBE (branch record buffer extension) {A64}.
kBTI, //!< CPU has BTI (branch target identification).
kBWE, //!< CPU has BWE (breakpoint mismatch and range extension) {A64}.
kCCIDX, //!< CPU has CCIDX (extend of the CCSIDR number of sets).
kCHK, //!< CPU has CHK (CHKFEAT instruction) {A64}.
kCHK, //!< CPU has CHK (check feature status - CHKFEAT instruction) {A64}.
kCLRBHB, //!< CPU has CLRBHB (clear BHB instruction).
kCMOW, //!< CPU has CMOW (control for cache maintenance permission) {A64}.
kCONSTPACFIELD, //!< CPU has CONSTPACFIELD (PAC algorithm enhancement) {A64}.
kCPA, //!< CPU has CPA (instruction-only Checked Pointer Arithmetic) {A64}.
kCPA2, //!< CPU has CPA2 (checked Pointer Arithmetic) {A64}.
kCPUID, //!< CPU has CPUID (CPUID registers accessible in user-space).
kCRC32, //!< CPU has CRC32 (CRC32 instructions).
kCSSC, //!< CPU has CSSC (common short sequence compression) {A64}.
kCSV2, //!< CPU has CSV2 (cache speculation variant 2 version 2.1) {A64}.
kCSV2_3, //!< CPU has CSV2_3 (cache speculation variant 2 version 3) {A64}.
kCSV3, //!< CPU has CSV3 (cache speculation Variant 3) {A64}.
kD128, //!< CPU has D128 (128-bit translation tables, 56 bit PA) {A64}.
kDGH, //!< CPU has DGH (data gathering hint) {A64}.
kDIT, //!< CPU has DIT (data independent timing of instructions).
kDOTPROD, //!< CPU has DOTPROD (ASIMD Int8 dot product instructions).
kDPB, //!< CPU has DPB (DC CVAP instruction) {A64}.
kDPB2, //!< CPU has DPB2 (DC CVADP instruction) {A64}.
kEBEP, //!< CPU has EBEP (exception-based event profiling) {A64}.
kEBF16, //!< CPU has EBF16 (extended BFloat16 mode) {A64}.
kECBHB, //!< CPU has ECBHB (exploitative control using branch history information) {A64}.
kECV, //!< CPU has ECV (enhanced counter virtualization).
kEDHSR, //!< CPU has EDHSR (support for EDHSR) {A64}.
kEDSP, //!< CPU has EDSP (ARM/THUMB only).
kFAMINMAX, //!< CPU has FAMINMAX (floating-point maximum and minimum absolute value instructions) {A64}.
kFCMA, //!< CPU has FCMA (FCADD/FCMLA).
kFGT, //!< CPU has FGT (fine-grained traps).
kFGT2, //!< CPU has FGT2 (fine-grained traps 2).
@ -535,13 +567,25 @@ public:
kFP, //!< CPU has FP (floating-point) (on 32-bit ARM this means VFPv3).
kFP16, //!< CPU has FP16 (half-precision floating-point data processing).
kFP16CONV, //!< CPU has FP16CONV (half-precision float conversion).
kFP8, //!< CPU has FP8 (FP8 convert instructions) {A64}.
kFP8DOT2, //!< CPU has FP8DOT2 (FP8 2-way dot product to half-precision instructions) {A64}.
kFP8DOT4, //!< CPU has FP8DOT4 (FP8 4-way dot product to single-precision instructions) {A64}.
kFP8FMA, //!< CPU has FP8FMA (FP8 multiply-accumulate to half-precision and single-precision instructions) {A64}.
kFPMR, //!< CPU has FPMR (floating-point Mode Register) {A64}.
kFRINTTS, //!< CPU has FRINTTS (FRINT[32|64][X|Z] instructions) {A64}.
kGCS, //!< CPU has GCS (guarded control stack extension) {A64}.
kHBC, //!< CPU has HBC (hinted conditional branches) {A64}
kHACDBS, //!< CPU has HACDBS (hardware accelerator for cleaning Dirty state) {A64}.
kHAFDBS, //!< CPU has HAFDBS (hardware management of the access flag and dirty state) {A64}.
kHAFT, //!< CPU has HAFT (hardware managed access flag for table descriptors) {A64}.
kHDBSS, //!< CPU has HDBSS (hardware Dirty state tracking Structure) {A64}.
kHBC, //!< CPU has HBC (hinted conditional branches) {A64}.
kHCX, //!< CPU has HCX (support for the HCRX_EL2 register) {A64}.
kHPDS, //!< CPU has HPDS (hierarchical permission disables in translation tables ) {A64}.
kHPDS2, //!< CPU has HPDS2 (hierarchical permission disables) {A64}.
kI8MM, //!< CPU has I8MM (int8 matrix multiplication) {A64}.
kIDIVA, //!< CPU has IDIV (hardware SDIV and UDIV in ARM mode).
kIDIVT, //!< CPU has IDIV (hardware SDIV and UDIV in THUMB mode).
kITE, //!< CPU has ITE (instrumentation extension) {A64}.
kJSCVT, //!< CPU has JSCVT (JavaScript FJCVTS conversion instruction) {A64}.
kLOR, //!< CPU has LOR (limited ordering regions extension).
kLRCPC, //!< CPU has LRCPC (load-acquire RCpc instructions) {A64}.
@ -553,12 +597,24 @@ public:
kLSE, //!< CPU has LSE (large system extensions) {A64}.
kLSE128, //!< CPU has LSE128 (128-bit atomics) {A64}.
kLSE2, //!< CPU has LSE2 (large system extensions v2) {A64}.
kLUT, //!< CPU has LUT (lookup table instructions with 2-bit and 4-bit indices) {A64}.
kLVA, //!< CPU has LVA (large VA support) {A64}.
kLVA3, //!< CPU has LVA3 (56-bit VA) {A64}.
kMEC, //!< CPU has MEC (memory encryption contexts) {A64}.
kMOPS, //!< CPU has MOPS (memcpy and memset acceleration instructions) {A64}.
kMPAM, //!< CPU has MPAM (memory system partitioning and monitoring extension) {A64}.
kMTE, //!< CPU has MTE (instruction-only memory tagging extension) {A64}.
kMTE2, //!< CPU has MTE2 (full memory tagging extension) {A64}.
kMTE3, //!< CPU has MTE3 (MTE asymmetric fault handling) {A64}.
kMTE4, //!< CPU has MTE4 (MTE v4) {A64}.
kMTE_ASYM_FAULT, //!< CPU has MTE_ASYM_FAULT (memory tagging asymmetric faults) {A64}.
kMTE_ASYNC, //!< CPU has MTE_ASYNC (memory tagging asynchronous faulting) {A64}.
kMTE_CANONICAL_TAGS, //!< CPU has MTE_CANONICAL_TAGS (canonical tag checking for untagged memory) {A64}.
kMTE_NO_ADDRESS_TAGS, //!< CPU has MTE_NO_ADDRESS_TAGS (memory tagging with address tagging disabled) {A64}.
kMTE_PERM_S1, //!< CPU has MTE_PERM_S1 (allocation tag access permission) {A64}.
kMTE_STORE_ONLY, //!< CPU has MTE_STORE_ONLY (store-only tag checking) {A64}.
kMTE_TAGGED_FAR, //!< CPU has MTE_TAGGED_FAR (FAR_ELx on a tag check fault) {A64}.
kMTPMU, //!< CPU has MTPMU (multi-threaded PMU extensions) {A64}.
kNMI, //!< CPU has NMI (non-maskable Interrupt) {A64}.
kNV, //!< CPU has NV (nested virtualization enchancement) {A64}.
kNV2, //!< CPU has NV2 (enhanced support for nested virtualization) {A64}.
@ -566,19 +622,28 @@ public:
kPAN2, //!< CPU has PAN2 (PAN s1e1R and s1e1W variants) {A64}.
kPAN3, //!< CPU has PAN3 (support for SCTLR_ELx.EPAN) {A64}.
kPAUTH, //!< CPU has PAUTH (pointer authentication extension) {A64}.
kPFAR, //!< CPU has PFAR (physical fault address registers) {A64}.
kPMU, //!< CPU has PMU {A64}.
kPMULL, //!< CPU has PMULL {A64}.
kPRFMSLC, //!< CPU has PRFMSLC (PRFM instructions support the SLC target) {A64}
kPMULL, //!< CPU has PMULL (ASIMD PMULL instructions) {A64}.
kPRFMSLC, //!< CPU has PRFMSLC (PRFM instructions support the SLC target) {A64}.
kRAS, //!< CPU has RAS (reliability, availability and serviceability extensions).
kRAS1_1, //!< CPU has RASv1p1 (RAS v1.1).
kRAS2, //!< CPU has RASv2 (RAS v2).
kRASSA2, //!< CPU has RASSAv2 (RAS v2 system architecture).
kRDM, //!< CPU has RDM (rounding double multiply accumulate) {A64}.
kRME, //!< CPU has RME (memory encryption contexts extension) {A64}.
kRNG, //!< CPU has RNG (random number generation).
kRNG_TRAP, //!< CPU has RNG_TRAP (random number trap to EL3 field) {A64}.
kRPRES, //!< CPU has RPRES (increased precision of reciprocal estimate and RSQRT estimate) {A64}.
kRPRFM, //!! CPU has RPRFM (range prefetch hint instruction).
kRPRFM, //!< CPU has RPRFM (range prefetch hint instruction).
kS1PIE, //!< CPU has S1PIE (permission model enhancements) {A64}.
kS1POE, //!< CPU has S1POE (permission model enhancements) {A64}.
kS2PIE, //!< CPU has S2PIE (permission model enhancements) {A64}.
kS2POE, //!< CPU has S2POE (permission model enhancements) {A64}.
kSB, //!< CPU has SB (speculative barrier).
kSCTLR2, //!< CPU has SCTLR2 (extension to SCTLR_ELx) {A64}.
kSEBEP, //!< CPU has SEBEP (synchronous exception-based event profiling) {A64}.
kSEL2, //!< CPU has SEL2 (secure EL2) {A64}.
kSHA1, //!< CPU has SHA1 (ASIMD SHA1 instructions).
kSHA256, //!< CPU has SHA256 (ASIMD SHA256 instructions).
kSHA3, //!< CPU has SHA3 (ASIMD EOR3, RAX1, XAR, and BCAX instructions).
@ -589,20 +654,38 @@ public:
kSME2, //!< CPU has SME2 (SME v2) {A64}.
kSME2_1, //!< CPU has SME2p1 (SME v2.1) {A64}.
kSME_B16B16, //!< CPU has SME_B16B16 (SME non-widening BFloat16 to BFloat16 arithmetic) {A64}.
kSME_B16F32, //!< CPU has SME_B16F32 {A64}.
kSME_BI32I32, //!< CPU has SME_BI32I32 {A64}.
kSME_B16F32, //!< CPU has SME_B16F32 (BFMOPA and BFMOPS instructions that accumulate BFloat16 outer products into single-precision tiles) {A64}.
kSME_BI32I32, //!< CPU has SME_BI32I32 (BMOPA and BMOPS instructions that accumulate 1-bit binary outer products into 32-bit integer tiles) {A64}.
kSME_F16F16, //!< CPU has SME_F16F16 (SME2.1 non-widening half-precision FP16 to FP16 arithmetic) {A64}.
kSME_F16F32, //!< CPU has SME_F16F32 {A64}.
kSME_F32F32, //!< CPU has SME_F32F32 {A64}.
kSME_F64F64, //!< CPU has SME_F64F64 {A64}.
kSME_F8F16, //!< CPU has SME_F8F16 (SME2 ZA-targeting FP8 multiply-accumulate, dot product, and outer product to half-precision instructions) {A64}.
kSME_F8F32, //!< CPU has SME_F8F32 (SME2 ZA-targeting FP8 multiply-accumulate, dot product, and outer product to single-precision instructions) {A64}.
kSME_FA64, //!< CPU has SME_FA64 {A64}.
kSME_I16I32, //!< CPU has SME_I16I32 {A64}.
kSME_I16I64, //!< CPU has SME_I16I64 {A64}.
kSME_I8I32, //!< CPU has SME_I8I32 {A64}.
kSME_LUTv2, //!< CPU has SME_LUTv2 (lookup table instructions with 4-bit indices and 8-bit elements) {A64}.
kSPE, //!< CPU has SPE (statistical profiling extension) {A64}.
kSPE1_1, //!< CPU has SPEv1p1 (statistical profiling extensions version 1.1) {A64}.
kSPE1_2, //!< CPU has SPEv1p2 (statistical profiling extensions version 1.2) {A64}.
kSPE1_3, //!< CPU has SPEv1p3 (statistical profiling extensions version 1.3) {A64}.
kSPE1_4, //!< CPU has SPEv1p4 (statistical profiling extensions version 1.4) {A64}.
kSPE_ALTCLK, //!< CPU has SPE_ALTCLK (statistical profiling alternate clock domain extension) {A64}.
kSPE_CRR, //!< CPU has SPE_CRR (statistical profiling call return branch records) {A64}.
kSPE_EFT, //!< CPU has SPE_EFT (statistical profiling extended filtering by type) {A64}.
kSPE_FDS, //!< CPU has SPE_FDS (statistical profiling data source filtering) {A64}.
kSPE_FPF, //!< CPU has SPE_FPF (statistical profiling floating-point flag extension) {A64}.
kSPE_SME, //!< CPU has SPE_SME (statistical profiling extensions for SME) {A64}.
kSPECRES, //!< CPU has SPECRES (speculation restriction instructions).
kSPECRES2, //!< CPU has SPECRES2 (clear other speculative predictions).
kSPMU, //!< CPU has SPMU (system performance monitors extension) {A64}.
kSSBS, //!< CPU has SSBS (speculative store bypass safe instruction).
kSSBS2, //!< CPU has SSBS2 (MRS and MSR instructions for SSBS).
kSSVE_FP8DOT2, //!< CPU has SSVE_FP8DOT2 (SVE2 FP8 2-way dot product to half-precision instructions in Streaming SVE mode) {A64}.
kSSVE_FP8DOT4, //!< CPU has SSVE_FP8DOT4 (SVE2 FP8 4-way dot product to single-precision instructions in Streaming SVE mode) {A64}.
kSSVE_FP8FMA, //!< CPU has SSVE_FP8FMA (SVE2 FP8 multiply-accumulate to half-precision and single-precision instructions in Streaming SVE mode) {A64}.
kSVE, //!< CPU has SVE (SVE v1 - scalable vector extension) {A64}.
kSVE2, //!< CPU has SVE2 (SVE v2) {A64}.
kSVE2_1, //!< CPU has SVE2p1 (SVE v2.1) {A64}.
@ -620,12 +703,17 @@ public:
kSYSINSTR128, //!< CPU has SYSINSTR128 (128-bit system instructions) {A64}.
kSYSREG128, //!< CPU has SYSREG128 (128-bit system registers) {A64}.
kTHE, //!< CPU has THE (translation hardening extension).
kTLBIOS, //!< CPU has TLBIOS (TLBI instructions in Outer Shareable domain) {A64}.
kTLBIRANGE, //!< CPU has TLBIRANGE (TLBI range instructions) {A64}.
kTLBIW, //!< CPU has TLBIW (TLBI VMALL for dirty state) {A64}.
kTME, //!< CPU has TME (transactional memory extensions).
kTRF, //!< CPU has TRF (trace extension).
kTRF, //!< CPU has TRF (self-hosted trace extensions).
kUAO, //!< CPU has UAO (AArch64 v8.2 UAO PState) {A64}.
kVFP_D32, //!< CPU has VFP_D32 (32 VFP-D registers) (ARM/THUMB only).
kVHE, //!< CPU has VHE (virtual host extension).
kVMID16, //!< CPU has VMID16 (16-bit VMID) {A64}.
kWFXT, //!< CPU has WFxT (WFE and WFI instructions with timeout) {A64}.
kXNX, //!< CPU has XNX (translation table stage 2 unprivileged execute-never) {A64}.
kXS, //!< CPU has XS (XS attribute in TLBI and DSB instructions) {A64}.
// @EnumValuesEnd@
@ -633,6 +721,7 @@ public:
};
#define ASMJIT_ARM_FEATURE(FEATURE) \
/*! Tests whether FEATURE is present. */ \
ASMJIT_INLINE_NODEBUG bool has##FEATURE() const noexcept { return has(ARM::k##FEATURE); }
ASMJIT_ARM_FEATURE(THUMB)
@ -642,26 +731,45 @@ public:
ASMJIT_ARM_FEATURE(ARMv7)
ASMJIT_ARM_FEATURE(ARMv8a)
ASMJIT_ARM_FEATURE(ABLE)
ASMJIT_ARM_FEATURE(ADERR)
ASMJIT_ARM_FEATURE(AES)
ASMJIT_ARM_FEATURE(AFP)
ASMJIT_ARM_FEATURE(AIE)
ASMJIT_ARM_FEATURE(AMU1)
ASMJIT_ARM_FEATURE(AMU1_1)
ASMJIT_ARM_FEATURE(ANERR)
ASMJIT_ARM_FEATURE(ASIMD)
ASMJIT_ARM_FEATURE(BF16)
ASMJIT_ARM_FEATURE(BRBE)
ASMJIT_ARM_FEATURE(BTI)
ASMJIT_ARM_FEATURE(BWE)
ASMJIT_ARM_FEATURE(CCIDX)
ASMJIT_ARM_FEATURE(CHK)
ASMJIT_ARM_FEATURE(CLRBHB)
ASMJIT_ARM_FEATURE(CMOW)
ASMJIT_ARM_FEATURE(CONSTPACFIELD)
ASMJIT_ARM_FEATURE(CPA)
ASMJIT_ARM_FEATURE(CPA2)
ASMJIT_ARM_FEATURE(CPUID)
ASMJIT_ARM_FEATURE(CRC32)
ASMJIT_ARM_FEATURE(CSSC)
ASMJIT_ARM_FEATURE(CSV2)
ASMJIT_ARM_FEATURE(CSV2_3)
ASMJIT_ARM_FEATURE(CSV3)
ASMJIT_ARM_FEATURE(D128)
ASMJIT_ARM_FEATURE(DGH)
ASMJIT_ARM_FEATURE(DIT)
ASMJIT_ARM_FEATURE(DOTPROD)
ASMJIT_ARM_FEATURE(DPB)
ASMJIT_ARM_FEATURE(DPB2)
ASMJIT_ARM_FEATURE(EBEP)
ASMJIT_ARM_FEATURE(EBF16)
ASMJIT_ARM_FEATURE(ECBHB)
ASMJIT_ARM_FEATURE(ECV)
ASMJIT_ARM_FEATURE(EDHSR)
ASMJIT_ARM_FEATURE(EDSP)
ASMJIT_ARM_FEATURE(FAMINMAX)
ASMJIT_ARM_FEATURE(FCMA)
ASMJIT_ARM_FEATURE(FGT)
ASMJIT_ARM_FEATURE(FGT2)
@ -672,13 +780,25 @@ public:
ASMJIT_ARM_FEATURE(FP)
ASMJIT_ARM_FEATURE(FP16)
ASMJIT_ARM_FEATURE(FP16CONV)
ASMJIT_ARM_FEATURE(FP8)
ASMJIT_ARM_FEATURE(FP8DOT2)
ASMJIT_ARM_FEATURE(FP8DOT4)
ASMJIT_ARM_FEATURE(FP8FMA)
ASMJIT_ARM_FEATURE(FPMR)
ASMJIT_ARM_FEATURE(FRINTTS)
ASMJIT_ARM_FEATURE(GCS)
ASMJIT_ARM_FEATURE(HACDBS)
ASMJIT_ARM_FEATURE(HAFDBS)
ASMJIT_ARM_FEATURE(HAFT)
ASMJIT_ARM_FEATURE(HDBSS)
ASMJIT_ARM_FEATURE(HBC)
ASMJIT_ARM_FEATURE(HCX)
ASMJIT_ARM_FEATURE(HPDS)
ASMJIT_ARM_FEATURE(HPDS2)
ASMJIT_ARM_FEATURE(I8MM)
ASMJIT_ARM_FEATURE(IDIVA)
ASMJIT_ARM_FEATURE(IDIVT)
ASMJIT_ARM_FEATURE(ITE)
ASMJIT_ARM_FEATURE(JSCVT)
ASMJIT_ARM_FEATURE(LOR)
ASMJIT_ARM_FEATURE(LRCPC)
@ -690,12 +810,24 @@ public:
ASMJIT_ARM_FEATURE(LSE)
ASMJIT_ARM_FEATURE(LSE128)
ASMJIT_ARM_FEATURE(LSE2)
ASMJIT_ARM_FEATURE(LUT)
ASMJIT_ARM_FEATURE(LVA)
ASMJIT_ARM_FEATURE(LVA3)
ASMJIT_ARM_FEATURE(MEC)
ASMJIT_ARM_FEATURE(MOPS)
ASMJIT_ARM_FEATURE(MPAM)
ASMJIT_ARM_FEATURE(MTE)
ASMJIT_ARM_FEATURE(MTE2)
ASMJIT_ARM_FEATURE(MTE3)
ASMJIT_ARM_FEATURE(MTE4)
ASMJIT_ARM_FEATURE(MTE_ASYM_FAULT)
ASMJIT_ARM_FEATURE(MTE_ASYNC)
ASMJIT_ARM_FEATURE(MTE_CANONICAL_TAGS)
ASMJIT_ARM_FEATURE(MTE_NO_ADDRESS_TAGS)
ASMJIT_ARM_FEATURE(MTE_PERM_S1)
ASMJIT_ARM_FEATURE(MTE_STORE_ONLY)
ASMJIT_ARM_FEATURE(MTE_TAGGED_FAR)
ASMJIT_ARM_FEATURE(MTPMU)
ASMJIT_ARM_FEATURE(NMI)
ASMJIT_ARM_FEATURE(NV)
ASMJIT_ARM_FEATURE(NV2)
@ -703,19 +835,28 @@ public:
ASMJIT_ARM_FEATURE(PAN2)
ASMJIT_ARM_FEATURE(PAN3)
ASMJIT_ARM_FEATURE(PAUTH)
ASMJIT_ARM_FEATURE(PFAR)
ASMJIT_ARM_FEATURE(PMU)
ASMJIT_ARM_FEATURE(PMULL)
ASMJIT_ARM_FEATURE(PRFMSLC)
ASMJIT_ARM_FEATURE(RAS)
ASMJIT_ARM_FEATURE(RAS1_1)
ASMJIT_ARM_FEATURE(RAS2)
ASMJIT_ARM_FEATURE(RASSA2)
ASMJIT_ARM_FEATURE(RDM)
ASMJIT_ARM_FEATURE(RME)
ASMJIT_ARM_FEATURE(RNG)
ASMJIT_ARM_FEATURE(RNG_TRAP)
ASMJIT_ARM_FEATURE(RPRES)
ASMJIT_ARM_FEATURE(RPRFM)
ASMJIT_ARM_FEATURE(S1PIE)
ASMJIT_ARM_FEATURE(S1POE)
ASMJIT_ARM_FEATURE(S2PIE)
ASMJIT_ARM_FEATURE(S2POE)
ASMJIT_ARM_FEATURE(SB)
ASMJIT_ARM_FEATURE(SCTLR2)
ASMJIT_ARM_FEATURE(SEBEP)
ASMJIT_ARM_FEATURE(SEL2)
ASMJIT_ARM_FEATURE(SHA1)
ASMJIT_ARM_FEATURE(SHA256)
ASMJIT_ARM_FEATURE(SHA3)
@ -732,14 +873,32 @@ public:
ASMJIT_ARM_FEATURE(SME_F16F32)
ASMJIT_ARM_FEATURE(SME_F32F32)
ASMJIT_ARM_FEATURE(SME_F64F64)
ASMJIT_ARM_FEATURE(SME_F8F16)
ASMJIT_ARM_FEATURE(SME_F8F32)
ASMJIT_ARM_FEATURE(SME_FA64)
ASMJIT_ARM_FEATURE(SME_I16I32)
ASMJIT_ARM_FEATURE(SME_I16I64)
ASMJIT_ARM_FEATURE(SME_I8I32)
ASMJIT_ARM_FEATURE(SME_LUTv2)
ASMJIT_ARM_FEATURE(SPE)
ASMJIT_ARM_FEATURE(SPE1_1)
ASMJIT_ARM_FEATURE(SPE1_2)
ASMJIT_ARM_FEATURE(SPE1_3)
ASMJIT_ARM_FEATURE(SPE1_4)
ASMJIT_ARM_FEATURE(SPE_ALTCLK)
ASMJIT_ARM_FEATURE(SPE_CRR)
ASMJIT_ARM_FEATURE(SPE_EFT)
ASMJIT_ARM_FEATURE(SPE_FDS)
ASMJIT_ARM_FEATURE(SPE_FPF)
ASMJIT_ARM_FEATURE(SPE_SME)
ASMJIT_ARM_FEATURE(SPECRES)
ASMJIT_ARM_FEATURE(SPECRES2)
ASMJIT_ARM_FEATURE(SPMU)
ASMJIT_ARM_FEATURE(SSBS)
ASMJIT_ARM_FEATURE(SSBS2)
ASMJIT_ARM_FEATURE(SSVE_FP8DOT2)
ASMJIT_ARM_FEATURE(SSVE_FP8DOT4)
ASMJIT_ARM_FEATURE(SSVE_FP8FMA)
ASMJIT_ARM_FEATURE(SVE)
ASMJIT_ARM_FEATURE(SVE2)
ASMJIT_ARM_FEATURE(SVE2_1)
@ -757,12 +916,17 @@ public:
ASMJIT_ARM_FEATURE(SYSINSTR128)
ASMJIT_ARM_FEATURE(SYSREG128)
ASMJIT_ARM_FEATURE(THE)
ASMJIT_ARM_FEATURE(TLBIOS)
ASMJIT_ARM_FEATURE(TLBIRANGE)
ASMJIT_ARM_FEATURE(TLBIW)
ASMJIT_ARM_FEATURE(TME)
ASMJIT_ARM_FEATURE(TRF)
ASMJIT_ARM_FEATURE(UAO)
ASMJIT_ARM_FEATURE(VFP_D32)
ASMJIT_ARM_FEATURE(VHE)
ASMJIT_ARM_FEATURE(VMID16)
ASMJIT_ARM_FEATURE(WFXT)
ASMJIT_ARM_FEATURE(XNX)
ASMJIT_ARM_FEATURE(XS)
#undef ASMJIT_ARM_FEATURE
@ -785,6 +949,7 @@ public:
ASMJIT_INLINE_NODEBUG CpuFeatures() noexcept {}
ASMJIT_INLINE_NODEBUG CpuFeatures(const CpuFeatures& other) noexcept = default;
ASMJIT_INLINE_NODEBUG explicit CpuFeatures(const Data& other) noexcept : _data{other._bits} {}
ASMJIT_INLINE_NODEBUG explicit CpuFeatures(Globals::NoInit_) noexcept {}
//! \}
@ -794,8 +959,8 @@ public:
ASMJIT_INLINE_NODEBUG CpuFeatures& operator=(const CpuFeatures& other) noexcept = default;
ASMJIT_INLINE_NODEBUG bool operator==(const CpuFeatures& other) noexcept { return eq(other); }
ASMJIT_INLINE_NODEBUG bool operator!=(const CpuFeatures& other) noexcept { return !eq(other); }
ASMJIT_INLINE_NODEBUG bool operator==(const CpuFeatures& other) const noexcept { return equals(other); }
ASMJIT_INLINE_NODEBUG bool operator!=(const CpuFeatures& other) const noexcept { return !equals(other); }
//! \}
@ -849,6 +1014,7 @@ public:
//! \name Manipulation
//! \{
//! Clears all features set.
ASMJIT_INLINE_NODEBUG void reset() noexcept { _data.reset(); }
//! Adds the given CPU `featureId` to the list of features.
@ -864,7 +1030,12 @@ public:
ASMJIT_INLINE_NODEBUG void remove(Args&&... args) noexcept { return _data.remove(std::forward<Args>(args)...); }
//! Tests whether this CPU features matches `other`.
ASMJIT_INLINE_NODEBUG bool eq(const CpuFeatures& other) const noexcept { return _data.eq(other._data); }
ASMJIT_INLINE_NODEBUG bool equals(const CpuFeatures& other) const noexcept { return _data.equals(other._data); }
#if !defined(ASMJIT_NO_DEPRECATED)
ASMJIT_DEPRECATED("Use CpuFeatures::equals() instead")
ASMJIT_INLINE_NODEBUG bool eq(const CpuFeatures& other) const noexcept { return equals(other); }
#endif // !ASMJIT_NO_DEPRECATED
//! \}
};
@ -876,65 +1047,82 @@ public:
//! \{
//! Architecture.
Arch _arch;
Arch _arch {};
//! Sub-architecture.
SubArch _subArch;
SubArch _subArch {};
//! True if the CPU was detected, false if the detection failed or it's not available.
bool _wasDetected;
bool _wasDetected {};
//! Reserved for future use.
uint8_t _reserved;
uint8_t _reserved {};
//! CPU family ID.
uint32_t _familyId;
uint32_t _familyId {};
//! CPU model ID.
uint32_t _modelId;
uint32_t _modelId {};
//! CPU brand ID.
uint32_t _brandId;
uint32_t _brandId {};
//! CPU stepping.
uint32_t _stepping;
uint32_t _stepping {};
//! Processor type.
uint32_t _processorType;
uint32_t _processorType {};
//! Maximum number of addressable IDs for logical processors.
uint32_t _maxLogicalProcessors;
uint32_t _maxLogicalProcessors {};
//! Cache line size (in bytes).
uint32_t _cacheLineSize;
uint32_t _cacheLineSize {};
//! Number of hardware threads.
uint32_t _hwThreadCount;
uint32_t _hwThreadCount {};
//! CPU vendor string.
FixedString<16> _vendor;
FixedString<16> _vendor {};
//! CPU brand string.
FixedString<64> _brand;
FixedString<64> _brand {};
//! CPU features.
CpuFeatures _features;
CpuFeatures _features {};
//! \}
//! \name Construction & Destruction
//! \{
ASMJIT_INLINE_NODEBUG CpuInfo() noexcept { reset(); }
//! Creates a new CpuInfo instance.
ASMJIT_INLINE_NODEBUG CpuInfo() noexcept {}
//! Creates a copy of `other` instance.
ASMJIT_INLINE_NODEBUG CpuInfo(const CpuInfo& other) noexcept = default;
//! Creates an unitialized `CpuInfo` instance.
ASMJIT_INLINE_NODEBUG explicit CpuInfo(Globals::NoInit_) noexcept
: _features(Globals::NoInit) {};
//! \}
//! \name CPU Information Detection
//! \{
//! Returns the host CPU information.
//!
//! \note The returned reference is global - it's setup only once and then shared.
ASMJIT_API static const CpuInfo& host() noexcept;
//! \}
//! \name Overloaded Operators
//! \{
//! Copy assignment.
ASMJIT_INLINE_NODEBUG CpuInfo& operator=(const CpuInfo& other) noexcept = default;
//! \}
//! \name Initialization & Reset
//! \{
//! Initializes CpuInfo architecture and sub-architecture members to `arch` and `subArch`, respectively.
ASMJIT_INLINE_NODEBUG void initArch(Arch arch, SubArch subArch = SubArch::kUnknown) noexcept {
_arch = arch;
_subArch = subArch;
}
ASMJIT_INLINE_NODEBUG void reset() noexcept { memset(this, 0, sizeof(*this)); }
//! \}
//! \name Overloaded Operators
//! \{
ASMJIT_INLINE_NODEBUG CpuInfo& operator=(const CpuInfo& other) noexcept = default;
//! Resets this \ref CpuInfo to a default constructed state.
ASMJIT_INLINE_NODEBUG void reset() noexcept { *this = CpuInfo{}; }
//! \}
@ -1004,7 +1192,7 @@ public:
//! Returns a CPU vendor string.
ASMJIT_INLINE_NODEBUG const char* vendor() const noexcept { return _vendor.str; }
//! Tests whether the CPU vendor string is equal to `s`.
ASMJIT_INLINE_NODEBUG bool isVendor(const char* s) const noexcept { return _vendor.eq(s); }
ASMJIT_INLINE_NODEBUG bool isVendor(const char* s) const noexcept { return _vendor.equals(s); }
//! Returns a CPU brand string.
ASMJIT_INLINE_NODEBUG const char* brand() const noexcept { return _brand.str; }

View File

@ -216,14 +216,17 @@ ASMJIT_FAVOR_SIZE Error BaseEmitHelper::emitArgsAssignment(const FuncFrame& fram
}
else {
WorkData& wd = workData[outGroup];
if (!wd.isAssigned(outId)) {
if (!wd.isAssigned(outId) || curId == outId) {
EmitMove:
ASMJIT_PROPAGATE(
emitArgMove(
BaseReg(archTraits.regTypeToSignature(out.regType()), outId), out.typeId(),
BaseReg(archTraits.regTypeToSignature(cur.regType()), curId), cur.typeId()));
// Only reassign if this is not a sign/zero extension that happens on the same in/out register.
if (curId != outId)
wd.reassign(varId, outId, curId);
cur.initReg(out.regType(), outId, out.typeId());
if (outId == out.regId())

View File

@ -384,6 +384,7 @@ Error BaseEmitter::onDetach(CodeHolder* code) noexcept {
_errorHandler = nullptr;
_clearEmitterFlags(~kEmitterPreservedFlags);
_instructionAlignment = uint8_t(0);
_forcedInstOptions = InstOptions::kReserved;
_privateData = 0;
@ -393,6 +394,7 @@ Error BaseEmitter::onDetach(CodeHolder* code) noexcept {
_instOptions = InstOptions::kNone;
_extraReg.reset();
_inlineComment = nullptr;
_funcs.reset();
return kErrorOk;
}

View File

@ -194,6 +194,7 @@ ASMJIT_DEFINE_ENUM_FLAGS(DiagnosticOptions)
class ASMJIT_VIRTAPI BaseEmitter {
public:
ASMJIT_BASE_CLASS(BaseEmitter)
ASMJIT_NONCOPYABLE(BaseEmitter)
//! \name Members
//! \{
@ -202,6 +203,11 @@ public:
EmitterType _emitterType = EmitterType::kNone;
//! See \ref EmitterFlags.
EmitterFlags _emitterFlags = EmitterFlags::kNone;
//! Instruction alignment.
uint8_t _instructionAlignment = 0u;
//! \cond
uint8_t _reservedBaseEmitter = 0u;
//! \endcond
//! Validation flags in case validation is used.
//!
//! \note Validation flags are specific to the emitter and they are setup at construction time and then never
@ -262,7 +268,7 @@ public:
Arch arch,
const BaseInst& inst, const Operand_* operands, size_t opCount) ASMJIT_NOEXCEPT_TYPE;
typedef Error (ASMJIT_CDECL* ValidateFunc)(Arch arch, const BaseInst& inst, const Operand_* operands, size_t opCount, ValidationFlags validationFlags) ASMJIT_NOEXCEPT_TYPE;
typedef Error (ASMJIT_CDECL* ValidateFunc)(const BaseInst& inst, const Operand_* operands, size_t opCount, ValidationFlags validationFlags) ASMJIT_NOEXCEPT_TYPE;
//! Emit prolog implementation.
EmitProlog emitProlog;
@ -331,10 +337,17 @@ public:
//! Tests whether the emitter is destroyed (only used during destruction).
ASMJIT_INLINE_NODEBUG bool isDestroyed() const noexcept { return hasEmitterFlag(EmitterFlags::kDestroyed); }
//! \}
//! \cond INTERNAL
//! \name Internal Functions
//! \{
ASMJIT_INLINE_NODEBUG void _addEmitterFlags(EmitterFlags flags) noexcept { _emitterFlags |= flags; }
ASMJIT_INLINE_NODEBUG void _clearEmitterFlags(EmitterFlags flags) noexcept { _emitterFlags &= _emitterFlags & ~flags; }
//! \}
//! \endcond
//! \name Target Information
//! \{
@ -360,6 +373,17 @@ public:
//! Returns the target architecture's GP register size (4 or 8 bytes).
ASMJIT_INLINE_NODEBUG uint32_t registerSize() const noexcept { return environment().registerSize(); }
//! Returns a signature of a native general purpose register (either 32-bit or 64-bit depending on the architecture).
ASMJIT_INLINE_NODEBUG OperandSignature gpSignature() const noexcept { return _gpSignature; }
//! Returns instruction alignment.
//!
//! The following values are returned based on the target architecture:
//! - X86 and X86_64 - instruction alignment is 1
//! - AArch32 - instruction alignment is 4 in A32 mode and 2 in THUMB mode.
//! - AArch64 - instruction alignment is 4
ASMJIT_INLINE_NODEBUG uint32_t instructionAlignment() const noexcept { return _instructionAlignment; }
//! \}
//! \name Initialization & Finalization
@ -540,23 +564,38 @@ public:
//! \name Emitter State
//! \{
//! Resets the emitter state, which contains instruction options, extra register, and inline comment.
//!
//! Emitter can have a state that describes instruction options and extra register used by the instruction. Most
//! instructions don't need nor use the state, however, if an instruction uses a prefix such as REX or REP prefix,
//! which is set explicitly, then the state would contain it. This allows to mimic the syntax of assemblers such
//! as X86. For example `rep().movs(...)` would map to a `REP MOVS` instuction on X86. The same applies to various
//! hints and the use of a mask register in AVX-512 mode.
ASMJIT_INLINE_NODEBUG void resetState() noexcept {
resetInstOptions();
resetExtraReg();
resetInlineComment();
}
//! \cond INTERNAL
//! Grabs the current emitter state and resets the emitter state at the same time, returning the state the emitter
//! had before the state was reset.
ASMJIT_INLINE_NODEBUG State _grabState() noexcept {
State s{_instOptions | _forcedInstOptions, _extraReg, _inlineComment};
resetState();
return s;
}
//! \endcond
//! \}
//! \name Sections
//! \{
//! Switches the given `section`.
//!
//! Once switched, everything is added to the given `section`.
ASMJIT_API virtual Error section(Section* section);
//! \}
@ -620,35 +659,51 @@ public:
ASMJIT_API Error _emitI(InstId instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3, const Operand_& o4, const Operand_& o5);
//! Emits an instruction `instId` with the given `operands`.
//!
//! This is the most universal way of emitting code, which accepts an instruction identifier and instruction
//! operands. This is called an "unchecked" API as emit doesn't provide any type checks at compile-time. This
//! allows to emit instruction with just \ref Operand instances, which could be handy in some cases - for
//! example emitting generic code where you don't know whether some operand is register, memory, or immediate.
template<typename... Args>
ASMJIT_INLINE_NODEBUG Error emit(InstId instId, Args&&... operands) {
return _emitI(instId, Support::ForwardOp<Args>::forward(operands)...);
}
//! Similar to \ref emit(), but uses array of `operands` instead.
ASMJIT_INLINE_NODEBUG Error emitOpArray(InstId instId, const Operand_* operands, size_t opCount) {
return _emitOpArray(instId, operands, opCount);
}
//! Similar to \ref emit(), but emits instruction with both instruction options and extra register, followed
//! by an array of `operands`.
ASMJIT_FORCE_INLINE Error emitInst(const BaseInst& inst, const Operand_* operands, size_t opCount) {
setInstOptions(inst.options());
setExtraReg(inst.extraReg());
return _emitOpArray(inst.id(), operands, opCount);
}
//! \}
//! \cond INTERNAL
//! \name Emit Internals
//! \{
//! Emits an instruction - all 6 operands must be defined.
ASMJIT_API virtual Error _emit(InstId instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_* oExt);
//! Emits instruction having operands stored in array.
ASMJIT_API virtual Error _emitOpArray(InstId instId, const Operand_* operands, size_t opCount);
//! \endcond
//! \}
//! \endcond
//! \name Emit Utilities
//! \{
//! Emits a function prolog described by the given function `frame`.
ASMJIT_API Error emitProlog(const FuncFrame& frame);
//! Emits a function epilog described by the given function `frame`.
ASMJIT_API Error emitEpilog(const FuncFrame& frame);
//! Emits code that reassigns function `frame` arguments to the given `args`.
ASMJIT_API Error emitArgsAssignment(const FuncFrame& frame, const FuncArgsAssignment& args);
//! \}

View File

@ -24,7 +24,7 @@ enum class Vendor : uint8_t {
//! Unknown or uninitialized platform vendor.
kUnknown = 0,
//! Maximum value of `PlatformVendor`.
//! Maximum value of `Vendor`.
kMaxValue = kUnknown,
//! Platform vendor detected at compile-time.
@ -116,7 +116,7 @@ enum class Platform : uint8_t {
//! Platform ABI (application binary interface).
enum class PlatformABI : uint8_t {
//! Unknown or uninitialied environment.
//! Unknown or uninitialized environment.
kUnknown = 0,
//! Microsoft ABI.
kMSVC,
@ -126,6 +126,8 @@ enum class PlatformABI : uint8_t {
kAndroid,
//! Cygwin ABI.
kCygwin,
//! Darwin ABI.
kDarwin,
//! Maximum value of `PlatformABI`.
kMaxValue,
@ -142,11 +144,26 @@ enum class PlatformABI : uint8_t {
kGNU
#elif defined(__ANDROID__)
kAndroid
#elif defined(__APPLE__)
kDarwin
#else
kUnknown
#endif
};
//! Floating point ABI (ARM).
enum class FloatABI : uint8_t {
kHardFloat = 0,
kSoftFloat,
kHost =
#if ASMJIT_ARCH_ARM == 32 && defined(__SOFTFP__)
kSoftFloat
#else
kHardFloat
#endif
};
//! Object format.
//!
//! \note AsmJit doesn't really use anything except \ref ObjectFormat::kUnknown and \ref ObjectFormat::kJIT at
@ -186,53 +203,56 @@ public:
//! \{
//! Architecture.
Arch _arch;
Arch _arch = Arch::kUnknown;
//! Sub-architecture type.
SubArch _subArch;
SubArch _subArch = SubArch::kUnknown;
//! Vendor type.
Vendor _vendor;
Vendor _vendor = Vendor::kUnknown;
//! Platform.
Platform _platform;
Platform _platform = Platform::kUnknown;
//! Platform ABI.
PlatformABI _platformABI;
PlatformABI _platformABI = PlatformABI::kUnknown;
//! Object format.
ObjectFormat _objectFormat;
ObjectFormat _objectFormat = ObjectFormat::kUnknown;
//! Floating point ABI.
FloatABI _floatABI = FloatABI::kHardFloat;
//! Reserved for future use, must be zero.
uint8_t _reserved[2];
uint8_t _reserved = 0;
//! \}
//! \name Construction & Destruction
//! \{
ASMJIT_INLINE_NODEBUG Environment() noexcept :
_arch(Arch::kUnknown),
_subArch(SubArch::kUnknown),
_vendor(Vendor::kUnknown),
_platform(Platform::kUnknown),
_platformABI(PlatformABI::kUnknown),
_objectFormat(ObjectFormat::kUnknown),
_reserved { 0, 0 } {}
//! Creates a default initialized environment (all values either unknown or set to safe defaults).
ASMJIT_INLINE_NODEBUG constexpr Environment() noexcept = default;
//! Creates a copy of `other` instance.
ASMJIT_INLINE_NODEBUG constexpr Environment(const Environment& other) noexcept = default;
ASMJIT_INLINE_NODEBUG explicit Environment(
//! Creates \ref Environment initialized to `arch`, `subArch`, `vendor`, `platform`, `platformABI`, `objectFormat`,
//! and `floatABI`.
ASMJIT_INLINE_NODEBUG constexpr explicit Environment(
Arch arch,
SubArch subArch = SubArch::kUnknown,
Vendor vendor = Vendor::kUnknown,
Platform platform = Platform::kUnknown,
PlatformABI abi = PlatformABI::kUnknown,
ObjectFormat objectFormat = ObjectFormat::kUnknown) noexcept {
init(arch, subArch, vendor, platform, abi, objectFormat);
}
ASMJIT_INLINE_NODEBUG Environment(const Environment& other) noexcept = default;
PlatformABI platformABI = PlatformABI::kUnknown,
ObjectFormat objectFormat = ObjectFormat::kUnknown,
FloatABI floatABI = FloatABI::kHardFloat) noexcept
: _arch(arch),
_subArch(subArch),
_vendor(vendor),
_platform(platform),
_platformABI(platformABI),
_objectFormat(objectFormat),
_floatABI(floatABI) {}
//! Returns the host environment constructed from preprocessor macros defined by the compiler.
//!
//! The returned environment should precisely match the target host architecture, sub-architecture, platform,
//! and ABI.
static ASMJIT_INLINE_NODEBUG Environment host() noexcept {
return Environment(Arch::kHost, SubArch::kHost, Vendor::kHost, Platform::kHost, PlatformABI::kHost, ObjectFormat::kUnknown);
return Environment(Arch::kHost, SubArch::kHost, Vendor::kHost, Platform::kHost, PlatformABI::kHost, ObjectFormat::kUnknown, FloatABI::kHost);
}
//! \}
@ -271,20 +291,10 @@ public:
}
//! Resets all members of the environment to zero / unknown.
ASMJIT_INLINE_NODEBUG void reset() noexcept {
_arch = Arch::kUnknown;
_subArch = SubArch::kUnknown;
_vendor = Vendor::kUnknown;
_platform = Platform::kUnknown;
_platformABI = PlatformABI::kUnknown;
_objectFormat = ObjectFormat::kUnknown;
_reserved[0] = 0;
_reserved[1] = 0;
}
ASMJIT_INLINE_NODEBUG void reset() noexcept { *this = Environment{}; }
ASMJIT_INLINE_NODEBUG bool equals(const Environment& other) const noexcept {
return _packed() == other._packed();
}
//! Tests whether this environment is equal to `other`.
ASMJIT_INLINE_NODEBUG bool equals(const Environment& other) const noexcept { return _packed() == other._packed(); }
//! Returns the architecture.
ASMJIT_INLINE_NODEBUG Arch arch() const noexcept { return _arch; }
@ -298,14 +308,19 @@ public:
ASMJIT_INLINE_NODEBUG PlatformABI platformABI() const noexcept { return _platformABI; }
//! Returns target's object format.
ASMJIT_INLINE_NODEBUG ObjectFormat objectFormat() const noexcept { return _objectFormat; }
//! Returns floating point ABI.
ASMJIT_INLINE_NODEBUG FloatABI floatABI() const noexcept { return _floatABI; }
//! Initializes \ref Environment to `arch`, `subArch`, `vendor`, `platform`, `platformABI`, `objectFormat`,
//! and `floatABI`.
inline void init(
Arch arch,
SubArch subArch = SubArch::kUnknown,
Vendor vendor = Vendor::kUnknown,
Platform platform = Platform::kUnknown,
PlatformABI platformABI = PlatformABI::kUnknown,
ObjectFormat objectFormat = ObjectFormat::kUnknown) noexcept {
ObjectFormat objectFormat = ObjectFormat::kUnknown,
FloatABI floatABI = FloatABI::kHardFloat) noexcept {
_arch = arch;
_subArch = subArch;
@ -313,18 +328,27 @@ public:
_platform = platform;
_platformABI = platformABI;
_objectFormat = objectFormat;
_reserved[0] = 0;
_reserved[1] = 0;
_floatABI = floatABI;
_reserved = 0;
}
//! Tests whether this environment describes a 32-bit X86.
ASMJIT_INLINE_NODEBUG bool isArchX86() const noexcept { return _arch == Arch::kX86; }
//! Tests whether this environment describes a 64-bit X86.
ASMJIT_INLINE_NODEBUG bool isArchX64() const noexcept { return _arch == Arch::kX64; }
//! Tests whether this environment describes a 32-bit ARM.
ASMJIT_INLINE_NODEBUG bool isArchARM() const noexcept { return isArchARM(_arch); }
//! Tests whether this environment describes a 32-bit ARM in THUMB mode.
ASMJIT_INLINE_NODEBUG bool isArchThumb() const noexcept { return isArchThumb(_arch); }
//! Tests whether this environment describes a 64-bit X86.
ASMJIT_INLINE_NODEBUG bool isArchAArch64() const noexcept { return isArchAArch64(_arch); }
//! Tests whether this environment describes a 32-bit MIPS.
ASMJIT_INLINE_NODEBUG bool isArchMIPS32() const noexcept { return isArchMIPS32(_arch); }
//! Tests whether this environment describes a 64-bit MIPS.
ASMJIT_INLINE_NODEBUG bool isArchMIPS64() const noexcept { return isArchMIPS64(_arch); }
//! Tests whether this environment describes a 32-bit RISC-V.
ASMJIT_INLINE_NODEBUG bool isArchRISCV32() const noexcept { return _arch == Arch::kRISCV32; }
//! Tests whether this environment describes a 64-bit RISC-V.
ASMJIT_INLINE_NODEBUG bool isArchRISCV64() const noexcept { return _arch == Arch::kRISCV64; }
//! Tests whether the architecture is 32-bit.
@ -352,13 +376,10 @@ public:
//! Tests whether the environment platform is Windows.
ASMJIT_INLINE_NODEBUG bool isPlatformWindows() const noexcept { return _platform == Platform::kWindows; }
//! Tests whether the environment platform is Linux.
ASMJIT_INLINE_NODEBUG bool isPlatformLinux() const noexcept { return _platform == Platform::kLinux; }
//! Tests whether the environment platform is Hurd.
ASMJIT_INLINE_NODEBUG bool isPlatformHurd() const noexcept { return _platform == Platform::kHurd; }
//! Tests whether the environment platform is Haiku.
ASMJIT_INLINE_NODEBUG bool isPlatformHaiku() const noexcept { return _platform == Platform::kHaiku; }
@ -382,6 +403,8 @@ public:
ASMJIT_INLINE_NODEBUG bool isMSVC() const noexcept { return _platformABI == PlatformABI::kMSVC; }
//! Tests whether the ABI is GNU.
ASMJIT_INLINE_NODEBUG bool isGNU() const noexcept { return _platformABI == PlatformABI::kGNU; }
//! Tests whether the ABI is GNU.
ASMJIT_INLINE_NODEBUG bool isDarwin() const noexcept { return _platformABI == PlatformABI::kDarwin; }
//! Returns a calculated stack alignment for this environment.
ASMJIT_API uint32_t stackAlignment() const noexcept;
@ -402,6 +425,9 @@ public:
//! Sets the object format to `objectFormat`.
ASMJIT_INLINE_NODEBUG void setObjectFormat(ObjectFormat objectFormat) noexcept { _objectFormat = objectFormat; }
//! Sets floating point ABI to `floatABI`.
ASMJIT_INLINE_NODEBUG void setFloatABI(FloatABI floatABI) noexcept { _floatABI = floatABI; }
//! \}
//! \name Static Utilities
@ -465,11 +491,6 @@ public:
return arch == Arch::kX86 || arch == Arch::kX64;
}
//! Tests whether the given architecture family is ARM, THUMB, or AArch64.
static ASMJIT_INLINE_NODEBUG bool isFamilyARM(Arch arch) noexcept {
return isArchARM(arch) || isArchAArch64(arch) || isArchThumb(arch);
}
//! Tests whether the given architecture family is AArch32 (ARM or THUMB).
static ASMJIT_INLINE_NODEBUG bool isFamilyAArch32(Arch arch) noexcept {
return isArchARM(arch) || isArchThumb(arch);
@ -480,7 +501,12 @@ public:
return isArchAArch64(arch);
}
//! Tests whether the given architecture family is MISP or MIPS64.
//! Tests whether the given architecture family is ARM, THUMB, or AArch64.
static ASMJIT_INLINE_NODEBUG bool isFamilyARM(Arch arch) noexcept {
return isFamilyAArch32(arch) || isFamilyAArch64(arch);
}
//! Tests whether the given architecture family is MIPS or MIPS64.
static ASMJIT_INLINE_NODEBUG bool isFamilyMIPS(Arch arch) noexcept {
return isArchMIPS32(arch) || isArchMIPS64(arch);
}

View File

@ -57,7 +57,7 @@ Error formatTypeId(String& sb, TypeId typeId) noexcept {
if (!TypeUtils::isValid(typeId))
return sb.append("unknown");
const char* typeName = "unknown";
const char* typeName = nullptr;
uint32_t typeSize = TypeUtils::sizeOf(typeId);
TypeId scalarType = TypeUtils::scalarOf(typeId);
@ -166,8 +166,8 @@ Error formatRegister(
#endif
#if !defined(ASMJIT_NO_AARCH64)
if (Environment::isFamilyAArch64(arch))
return a64::FormatterInternal::formatRegister(sb, formatFlags, emitter, arch, regType, regId);
if (Environment::isFamilyARM(arch))
return arm::FormatterInternal::formatRegister(sb, formatFlags, emitter, arch, regType, regId);
#endif
return kErrorInvalidArch;
@ -186,8 +186,8 @@ Error formatOperand(
#endif
#if !defined(ASMJIT_NO_AARCH64)
if (Environment::isFamilyAArch64(arch))
return a64::FormatterInternal::formatOperand(sb, formatFlags, emitter, arch, op);
if (Environment::isFamilyARM(arch))
return arm::FormatterInternal::formatOperand(sb, formatFlags, emitter, arch, op);
#endif
return kErrorInvalidArch;
@ -284,7 +284,7 @@ Error formatInstruction(
#endif
#if !defined(ASMJIT_NO_AARCH64)
if (Environment::isFamilyARM(arch))
if (Environment::isFamilyAArch64(arch))
return a64::FormatterInternal::formatInstruction(sb, formatFlags, emitter, arch, inst, operands, opCount);
#endif
@ -514,7 +514,7 @@ Error formatNode(
ASMJIT_PROPAGATE(sb.append("[FuncRet]"));
for (uint32_t i = 0; i < 2; i++) {
const Operand_& op = retNode->_opArray[i];
const Operand_& op = retNode->op(i);
if (!op.isNone()) {
ASMJIT_PROPAGATE(sb.append(i == 0 ? " " : ", "));
ASMJIT_PROPAGATE(formatOperand(sb, formatOptions.flags(), builder, builder->arch(), op));

View File

@ -20,8 +20,8 @@
ASMJIT_BEGIN_NAMESPACE
// CallConv - Init & Reset
// =======================
// CallConv - Initialization & Reset
// =================================
ASMJIT_FAVOR_SIZE Error CallConv::init(CallConvId ccId, const Environment& environment) noexcept {
reset();
@ -75,7 +75,7 @@ ASMJIT_FAVOR_SIZE Error FuncDetail::init(const FuncSignature& signature, const E
#if !defined(ASMJIT_NO_AARCH64)
if (environment.isFamilyAArch64())
return a64::FuncInternal::initFuncDetail(*this, signature, registerSize);
return a64::FuncInternal::initFuncDetail(*this, signature);
#endif
// We should never bubble here as if `cc.init()` succeeded then there has to be an implementation for the current
@ -283,4 +283,18 @@ ASMJIT_FAVOR_SIZE Error FuncArgsAssignment::updateFuncFrame(FuncFrame& frame) co
return kErrorOk;
}
// Func API - Tests
// ================
#if defined(ASMJIT_TEST)
UNIT(func_signature) {
FuncSignature signature;
signature.setRetT<int8_t>();
signature.addArgT<int16_t>();
signature.addArg(TypeId::kInt32);
EXPECT_EQ(signature, FuncSignature::build<int8_t, int16_t, int32_t>());
}
#endif
ASMJIT_END_NAMESPACE

View File

@ -28,9 +28,6 @@ ASMJIT_BEGIN_NAMESPACE
//! - Target specific - calling conventions that are used by a particular architecture and ABI. For example
//! Windows 64-bit calling convention and AMD64 SystemV calling convention.
enum class CallConvId : uint8_t {
//! None or invalid (can't be used).
kNone = 0,
// Universal Calling Conventions
// -----------------------------
@ -38,48 +35,38 @@ enum class CallConvId : uint8_t {
//!
//! This is a universal calling convention, which is used to initialize specific calling conventions based on
//! architecture, platform, and its ABI.
kCDecl = 1,
kCDecl = 0,
//! `__stdcall` on targets that support this calling convention (X86).
//!
//! \note This calling convention is only supported on 32-bit X86. If used on environment that doesn't support
//! this calling convention it will be replaced by \ref CallConvId::kCDecl.
kStdCall = 2,
kStdCall = 1,
//! `__fastcall` on targets that support this calling convention (X86).
//!
//! \note This calling convention is only supported on 32-bit X86. If used on environment that doesn't support
//! this calling convention it will be replaced by \ref CallConvId::kCDecl.
kFastCall = 3,
kFastCall = 2,
//! `__vectorcall` on targets that support this calling convention (X86/X64).
//!
//! \note This calling convention is only supported on 32-bit and 64-bit X86 architecture on Windows platform.
//! If used on environment that doesn't support this calling it will be replaced by \ref CallConvId::kCDecl.
kVectorCall = 4,
kVectorCall = 3,
//! `__thiscall` on targets that support this calling convention (X86).
//!
//! \note This calling convention is only supported on 32-bit X86 Windows platform. If used on environment that
//! doesn't support this calling convention it will be replaced by \ref CallConvId::kCDecl.
kThisCall = 5,
kThisCall = 4,
//! `__attribute__((regparm(1)))` convention (GCC and Clang).
kRegParm1 = 6,
kRegParm1 = 5,
//! `__attribute__((regparm(2)))` convention (GCC and Clang).
kRegParm2 = 7,
kRegParm2 = 6,
//! `__attribute__((regparm(3)))` convention (GCC and Clang).
kRegParm3 = 8,
//! Soft-float calling convention (ARM).
//!
//! Floating point arguments are passed via general purpose registers.
kSoftFloat = 9,
//! Hard-float calling convention (ARM).
//!
//! Floating point arguments are passed via SIMD registers.
kHardFloat = 10,
kRegParm3 = 7,
//! AsmJit specific calling convention designed for calling functions inside a multimedia code that don't use many
//! registers internally, but are long enough to be called and not inlined. These functions are usually used to
@ -91,28 +78,32 @@ enum class CallConvId : uint8_t {
// ABI-Specific Calling Conventions
// --------------------------------
//! Soft-float calling convention (AArch32).
//!
//! Floating point arguments are passed via general purpose registers.
kSoftFloat = 30,
//! Hard-float calling convention (AArch32).
//!
//! Floating point arguments are passed via SIMD registers.
kHardFloat = 31,
//! X64 System-V calling convention.
kX64SystemV = 32,
//! X64 Windows calling convention.
kX64Windows = 33,
//! Maximum value of `CallConvId`.
kMaxValue = kX64Windows,
kMaxValue = kX64Windows
// Host Calling Conventions
// ------------------------
// Deprecated Aliases
// ------------------
//! Host calling convention detected at compile-time.
kHost =
#if defined(_DOXYGEN)
DETECTED_AT_COMPILE_TIME
#elif ASMJIT_ARCH_ARM == 32 && defined(__SOFTFP__)
kSoftFloat
#elif ASMJIT_ARCH_ARM == 32 && !defined(__SOFTFP__)
kHardFloat
#else
kCDecl
#endif
#if !defined(ASMJIT_NO_DEPRECATED)
,
kNone = kCDecl,
kHost = kCDecl
#endif // !ASMJIT_NO_DEPRECATED
};
//! Strategy used by calling conventions to assign registers to function arguments.
@ -128,6 +119,8 @@ enum class CallConvStrategy : uint8_t {
kX64Windows = 1,
//! Windows 64-bit __vectorcall register assignment strategy.
kX64VectorCall = 2,
//! Apple's AArch64 calling convention (differs compared to AArch64 calling convention used by Linux).
kAArch64Apple = 3,
//! Maximum value of `CallConvStrategy`.
kMaxValue = kX64VectorCall
@ -189,6 +182,11 @@ struct CallConv {
//! Natural stack alignment as defined by OS/ABI.
uint8_t _naturalStackAlignment;
//! \cond INTERNAL
//! Reserved for future use.
uint8_t _reserved[2];
//! \endcond
//! Calling convention flags.
CallConvFlags _flags;
@ -229,7 +227,7 @@ struct CallConv {
//! as it prevents from using an uninitialized data (CallConv doesn't have a constructor that would initialize it,
//! it's just a struct).
ASMJIT_INLINE_NODEBUG void reset() noexcept {
memset(this, 0, sizeof(*this));
*this = CallConv{};
memset(_passedOrder.data(), 0xFF, sizeof(_passedOrder));
}
@ -362,8 +360,15 @@ struct CallConv {
//! Function signature.
//!
//! Contains information about function return type, count of arguments and their TypeIds. Function signature is
//! a low level structure which doesn't contain platform specific or calling convention specific information.
//! Contains information about a function return type, count of arguments, and their TypeIds. Function signature
//! is a low level structure which doesn't contain platform specific or calling convention specific information.
//! It's typically used to describe function arguments in a C-API like form, which is then used to calculate a
//! \ref FuncDetail instance, which then maps function signature into a platform and calling convention specific
//! format.
//!
//! Function signature can be built either dynamically by using \ref addArg() and \ref addArgT() functionality,
//! or dynamically by using a template-based \ref FuncSignature::build() function, which maps template types
//! into a function signature.
struct FuncSignature {
//! \name Constants
//! \{
@ -377,36 +382,91 @@ struct FuncSignature {
//! \{
//! Calling convention id.
CallConvId _ccId;
CallConvId _ccId = CallConvId::kCDecl;
//! Count of arguments.
uint8_t _argCount;
uint8_t _argCount = 0;
//! Index of a first VA or `kNoVarArgs`.
uint8_t _vaIndex;
uint8_t _vaIndex = kNoVarArgs;
//! Return value TypeId.
TypeId _ret;
//! Function arguments TypeIds.
const TypeId* _args;
TypeId _ret = TypeId::kVoid;
//! Reserved for future use.
uint8_t _reserved[4] {};
//! Function argument TypeIds.
TypeId _args[Globals::kMaxFuncArgs] {};
//! \}
//! \name Construction & Destruction
//! \{
//! Default constructed function signature, initialized to \ref CallConvId::kCDecl, having no return value and no arguments.
ASMJIT_FORCE_INLINE constexpr FuncSignature() = default;
//! Copy constructor, which is initialized to the same function signature as `other`.
ASMJIT_FORCE_INLINE constexpr FuncSignature(const FuncSignature& other) = default;
//! Initializes the function signature with calling convention id `ccId` and variable argument's index `vaIndex`.
ASMJIT_FORCE_INLINE constexpr FuncSignature(CallConvId ccId, uint32_t vaIndex = kNoVarArgs) noexcept
: _ccId(ccId),
_vaIndex(uint8_t(vaIndex)) {}
//! Initializes the function signature with calling convention id `ccId`, `vaIndex`, return value, and function arguments.
template<typename... Args>
ASMJIT_FORCE_INLINE constexpr FuncSignature(CallConvId ccId, uint32_t vaIndex, TypeId ret, Args&&...args) noexcept
: _ccId(ccId),
_argCount(uint8_t(sizeof...(args))),
_vaIndex(uint8_t(vaIndex)),
_ret(ret),
_args{std::forward<Args>(args)...} {}
//! Builds a function signature based on `RetValueAndArgs`. The first template argument is a function return type,
//! and function arguments follow.
//!
//! \note This function returns a new function signature, which can be passed to functions where it's required. It's
//! a convenience function that allows to build function signature statically based on types known at compile time,
//! which is common in JIT code generation.
template<typename... RetValueAndArgs>
static ASMJIT_INLINE_NODEBUG constexpr FuncSignature build(CallConvId ccId = CallConvId::kCDecl, uint32_t vaIndex = kNoVarArgs) noexcept {
return FuncSignature(ccId, vaIndex, (TypeId(TypeUtils::TypeIdOfT<RetValueAndArgs>::kTypeId))... );
}
//! \}
//! \name Overloaded Operators
//! \{
//! Copy assignment - function signature can be copied by value.
ASMJIT_FORCE_INLINE FuncSignature& operator=(const FuncSignature& other) noexcept = default;
//! Compares this function signature with `other` for equality..
ASMJIT_FORCE_INLINE bool operator==(const FuncSignature& other) const noexcept { return equals(other); }
//! Compares this function signature with `other` for inequality..
ASMJIT_FORCE_INLINE bool operator!=(const FuncSignature& other) const noexcept { return !equals(other); }
//! \}
//! \name Initialization & Reset
//! \{
//! Initializes the function signature.
inline void init(CallConvId ccId, uint32_t vaIndex, TypeId ret, const TypeId* args, uint32_t argCount) noexcept {
ASMJIT_ASSERT(argCount <= 0xFF);
_ccId = ccId;
_argCount = uint8_t(argCount);
_vaIndex = uint8_t(vaIndex);
_ret = ret;
_args = args;
}
//! Resets this function signature to a default constructed state.
ASMJIT_INLINE_NODEBUG void reset() noexcept { *this = FuncSignature{}; }
//! \}
//! \name Equality & Comparison
//! \{
//! Compares this function signature with `other` for equality..
ASMJIT_INLINE_NODEBUG bool equals(const FuncSignature& other) const noexcept {
return _ccId == other._ccId &&
_argCount == other._argCount &&
_vaIndex == other._vaIndex &&
_ret == other._ret &&
memcmp(_args, other._args, sizeof(_args)) == 0;
}
//! \}
//! \name Accessors
//! \{
@ -415,6 +475,55 @@ struct FuncSignature {
//! Sets the calling convention to `ccId`;
ASMJIT_INLINE_NODEBUG void setCallConvId(CallConvId ccId) noexcept { _ccId = ccId; }
//! Tests whether the function signature has a return value.
ASMJIT_INLINE_NODEBUG bool hasRet() const noexcept { return _ret != TypeId::kVoid; }
//! Returns the type of the return value.
ASMJIT_INLINE_NODEBUG TypeId ret() const noexcept { return _ret; }
//! Sets the return type to `retType`.
ASMJIT_INLINE_NODEBUG void setRet(TypeId retType) noexcept { _ret = retType; }
//! Sets the return type based on `T`.
template<typename T>
ASMJIT_INLINE_NODEBUG void setRetT() noexcept { setRet(TypeId(TypeUtils::TypeIdOfT<T>::kTypeId)); }
//! Returns the array of function arguments' types.
ASMJIT_INLINE_NODEBUG const TypeId* args() const noexcept { return _args; }
//! Returns the number of function arguments.
ASMJIT_INLINE_NODEBUG uint32_t argCount() const noexcept { return _argCount; }
//! Returns the type of the argument at index `i`.
inline TypeId arg(uint32_t i) const noexcept {
ASMJIT_ASSERT(i < _argCount);
return _args[i];
}
//! Sets the argument at index `index` to `argType`.
inline void setArg(uint32_t index, TypeId argType) noexcept {
ASMJIT_ASSERT(index < _argCount);
_args[index] = argType;
}
//! Sets the argument at index `i` to the type based on `T`.
template<typename T>
inline void setArgT(uint32_t index) noexcept { setArg(index, TypeId(TypeUtils::TypeIdOfT<T>::kTypeId)); }
//! Tests whether an argument can be added to the signature, use before calling \ref addArg() and \ref addArgT().
//!
//! \note If you know that you are not adding more arguments than \ref Globals::kMaxFuncArgs then it's not necessary
//! to use this function. However, if you are adding arguments based on user input, for example, then either check
//! the number of arguments before using function signature or use \ref canAddArg() before actually adding them to
//! the function signature.
inline bool canAddArg() const noexcept { return _argCount < Globals::kMaxFuncArgs; }
//! Appends an argument of `type` to the function prototype.
inline void addArg(TypeId type) noexcept {
ASMJIT_ASSERT(_argCount < Globals::kMaxFuncArgs);
_args[_argCount++] = type;
}
//! Appends an argument of type based on `T` to the function prototype.
template<typename T>
inline void addArgT() noexcept { addArg(TypeId(TypeUtils::TypeIdOfT<T>::kTypeId)); }
//! Tests whether the function has variable number of arguments (...).
ASMJIT_INLINE_NODEBUG bool hasVarArgs() const noexcept { return _vaIndex != kNoVarArgs; }
//! Returns the variable arguments (...) index, `kNoVarArgs` if none.
@ -424,76 +533,21 @@ struct FuncSignature {
//! Resets the variable arguments index (making it a non-va function).
ASMJIT_INLINE_NODEBUG void resetVaIndex() noexcept { _vaIndex = kNoVarArgs; }
//! Returns the number of function arguments.
ASMJIT_INLINE_NODEBUG uint32_t argCount() const noexcept { return _argCount; }
ASMJIT_INLINE_NODEBUG bool hasRet() const noexcept { return _ret != TypeId::kVoid; }
//! Returns the return value type.
ASMJIT_INLINE_NODEBUG TypeId ret() const noexcept { return _ret; }
//! Returns the type of the argument at index `i`.
inline TypeId arg(uint32_t i) const noexcept {
ASMJIT_ASSERT(i < _argCount);
return _args[i];
}
//! Returns the array of function arguments' types.
ASMJIT_INLINE_NODEBUG const TypeId* args() const noexcept { return _args; }
//! \}
};
template<typename... RET_ARGS>
#if !defined(ASMJIT_NO_DEPRECATED)
template<typename... RetValueAndArgs>
class FuncSignatureT : public FuncSignature {
public:
ASMJIT_INLINE_NODEBUG FuncSignatureT(CallConvId ccId = CallConvId::kHost, uint32_t vaIndex = kNoVarArgs) noexcept {
static constexpr TypeId ret_args[] = { (TypeId(TypeUtils::TypeIdOfT<RET_ARGS>::kTypeId))... };
init(ccId, vaIndex, ret_args[0], ret_args + 1, uint32_t(ASMJIT_ARRAY_SIZE(ret_args) - 1));
}
ASMJIT_DEPRECATED("Use FuncSignature::build<RetValueAndArgs>() instead")
ASMJIT_INLINE_NODEBUG constexpr FuncSignatureT(CallConvId ccId = CallConvId::kCDecl, uint32_t vaIndex = kNoVarArgs) noexcept
: FuncSignature(ccId, vaIndex, (TypeId(TypeUtils::TypeIdOfT<RetValueAndArgs>::kTypeId))... ) {}
};
//! Function signature builder.
class FuncSignatureBuilder : public FuncSignature {
public:
TypeId _builderArgList[Globals::kMaxFuncArgs];
//! \name Initialization & Reset
//! \{
ASMJIT_INLINE_NODEBUG FuncSignatureBuilder(CallConvId ccId = CallConvId::kHost, uint32_t vaIndex = kNoVarArgs) noexcept {
init(ccId, vaIndex, TypeId::kVoid, _builderArgList, 0);
}
//! \}
//! \name Accessors
//! \{
//! Sets the return type to `retType`.
ASMJIT_INLINE_NODEBUG void setRet(TypeId retType) noexcept { _ret = retType; }
//! Sets the return type based on `T`.
template<typename T>
ASMJIT_INLINE_NODEBUG void setRetT() noexcept { setRet(TypeId(TypeUtils::TypeIdOfT<T>::kTypeId)); }
//! Sets the argument at index `index` to `argType`.
inline void setArg(uint32_t index, TypeId argType) noexcept {
ASMJIT_ASSERT(index < _argCount);
_builderArgList[index] = argType;
}
//! Sets the argument at index `i` to the type based on `T`.
template<typename T>
inline void setArgT(uint32_t index) noexcept { setArg(index, TypeId(TypeUtils::TypeIdOfT<T>::kTypeId)); }
//! Appends an argument of `type` to the function prototype.
inline void addArg(TypeId type) noexcept {
ASMJIT_ASSERT(_argCount < Globals::kMaxFuncArgs);
_builderArgList[_argCount++] = type;
}
//! Appends an argument of type based on `T` to the function prototype.
template<typename T>
inline void addArgT() noexcept { addArg(TypeId(TypeUtils::TypeIdOfT<T>::kTypeId)); }
//! \}
};
ASMJIT_DEPRECATED("Use FuncSignature instead of FuncSignatureBuilder")
typedef FuncSignature FuncSignatureBuilder;
#endif // !ASMJIT_NO_DEPRECATED
//! Argument or return value (or its part) as defined by `FuncSignature`, but with register or stack address
//! (and other metadata) assigned.
@ -536,15 +590,17 @@ struct FuncValue {
//!
//! \{
//! Initializes the `typeId` of this `FuncValue`.
//! Initializes this `FuncValue` only to the `typeId` provided - the rest of the values will be cleared.
ASMJIT_INLINE_NODEBUG void initTypeId(TypeId typeId) noexcept {
_data = uint32_t(typeId) << kTypeIdShift;
}
//! Initializes this `FuncValue` to a register of `regType`, `regId`, and assigns its `typeId` and `flags`.
ASMJIT_INLINE_NODEBUG void initReg(RegType regType, uint32_t regId, TypeId typeId, uint32_t flags = 0) noexcept {
_data = (uint32_t(regType) << kRegTypeShift) | (regId << kRegIdShift) | (uint32_t(typeId) << kTypeIdShift) | kFlagIsReg | flags;
}
//! Initializes this `FuncValue` to a stack at the given `offset` and assigns its `typeId`.
ASMJIT_INLINE_NODEBUG void initStack(int32_t offset, TypeId typeId) noexcept {
_data = (uint32_t(offset) << kStackOffsetShift) | (uint32_t(typeId) << kTypeIdShift) | kFlagIsStack;
}
@ -561,11 +617,13 @@ struct FuncValue {
//!
//! \{
//! Assigns a register of `regType` and `regId`.
inline void assignRegData(RegType regType, uint32_t regId) noexcept {
ASMJIT_ASSERT((_data & (kRegTypeMask | kRegIdMask)) == 0);
_data |= (uint32_t(regType) << kRegTypeShift) | (regId << kRegIdShift) | kFlagIsReg;
}
//! Assigns a stack location at `offset`.
inline void assignStackOffset(int32_t offset) noexcept {
ASMJIT_ASSERT((_data & kStackOffsetMask) == 0);
_data |= (uint32_t(offset) << kStackOffsetShift) | kFlagIsStack;
@ -579,7 +637,9 @@ struct FuncValue {
//! Returns true if the value is initialized (explicit bool cast).
ASMJIT_INLINE_NODEBUG explicit operator bool() const noexcept { return _data != 0; }
//! \cond INTERNAL
ASMJIT_INLINE_NODEBUG void _replaceValue(uint32_t mask, uint32_t value) noexcept { _data = (_data & ~mask) | value; }
//! \endcond
//! Tests whether the `FuncValue` has a flag `flag` set.
ASMJIT_INLINE_NODEBUG bool hasFlag(uint32_t flag) const noexcept { return Support::test(_data, flag); }
@ -661,40 +721,52 @@ public:
return n;
}
//! Returns values in this value in the pack.
//!
//! \note The returned array has exactly \ref Globals::kMaxValuePack elements.
ASMJIT_INLINE_NODEBUG FuncValue* values() noexcept { return _values; }
//! \overload
ASMJIT_INLINE_NODEBUG const FuncValue* values() const noexcept { return _values; }
//! Resets a value at the given `index` in the pack, which makes it unassigned.
inline void resetValue(size_t index) noexcept {
ASMJIT_ASSERT(index < Globals::kMaxValuePack);
_values[index].reset();
}
//! Tests whether the value at the given `index` in the pack is assigned.
inline bool hasValue(size_t index) noexcept {
ASMJIT_ASSERT(index < Globals::kMaxValuePack);
return _values[index].isInitialized();
}
//! Assigns a register at the given `index` to `reg` and an optional `typeId`.
inline void assignReg(size_t index, const BaseReg& reg, TypeId typeId = TypeId::kVoid) noexcept {
ASMJIT_ASSERT(index < Globals::kMaxValuePack);
ASMJIT_ASSERT(reg.isPhysReg());
_values[index].initReg(reg.type(), reg.id(), typeId);
}
//! Assigns a register at the given `index` to `regType`, `regId`, and an optional `typeId`.
inline void assignReg(size_t index, RegType regType, uint32_t regId, TypeId typeId = TypeId::kVoid) noexcept {
ASMJIT_ASSERT(index < Globals::kMaxValuePack);
_values[index].initReg(regType, regId, typeId);
}
//! Assigns a stack location at the given `index` to `offset` and an optional `typeId`.
inline void assignStack(size_t index, int32_t offset, TypeId typeId = TypeId::kVoid) noexcept {
ASMJIT_ASSERT(index < Globals::kMaxValuePack);
_values[index].initStack(offset, typeId);
}
//! Accesses the value in the pack at the given `index`.
//!
//! \note The maximum index value is `Globals::kMaxValuePack - 1`.
inline FuncValue& operator[](size_t index) {
ASMJIT_ASSERT(index < Globals::kMaxValuePack);
return _values[index];
}
//! \overload
inline const FuncValue& operator[](size_t index) const {
ASMJIT_ASSERT(index < Globals::kMaxValuePack);
return _values[index];
@ -717,6 +789,11 @@ enum class FuncAttributes : uint32_t {
kHasFuncCalls = 0x00000020u,
//! Function has aligned save/restore of vector registers.
kAlignedVecSR = 0x00000040u,
//! Function must begin with an instruction that marks a start of a branch or function.
//!
//! * `ENDBR32/ENDBR64` instruction is inserted at the beginning of the function (X86, X86_64).
//! * `BTI` instruction is inserted at the beginning of the function (AArch64)
kIndirectBranchProtection = 0x00000080u,
//! FuncFrame is finalized and can be used by prolog/epilog inserter (PEI).
kIsFinalized = 0x00000800u,
@ -753,10 +830,8 @@ public:
//! \name Constants
//! \{
enum : uint8_t {
//! Doesn't have variable number of arguments (`...`).
kNoVarArgs = 0xFFu
};
//! Function doesn't have a variable number of arguments (`...`).
static constexpr uint8_t kNoVarArgs = 0xFFu;
//! \}
@ -785,22 +860,35 @@ public:
//! \name Construction & Destruction
//! \{
//! Creates a default constructed \ref FuncDetail.
ASMJIT_INLINE_NODEBUG FuncDetail() noexcept {}
//! Copy constructor.
//!
//! Function details are copyable.
ASMJIT_INLINE_NODEBUG FuncDetail(const FuncDetail& other) noexcept = default;
//! Initializes this `FuncDetail` to the given signature.
ASMJIT_API Error init(const FuncSignature& signature, const Environment& environment) noexcept;
ASMJIT_INLINE_NODEBUG void reset() noexcept { *this = FuncDetail{}; }
//! \}
//! \name Overloaded Operators
//! \{
//! Assignment operator, copies `other` to this \ref FuncDetail.
ASMJIT_INLINE_NODEBUG FuncDetail& operator=(const FuncDetail& other) noexcept = default;
//! \}
//! \name Reset
//! \{
//! Resets the function detail to its default constructed state.
ASMJIT_INLINE_NODEBUG void reset() noexcept { *this = FuncDetail{}; }
//! \}
//! \name Accessors
//! \{
@ -957,87 +1045,98 @@ public:
//! \{
//! Function attributes.
FuncAttributes _attributes;
FuncAttributes _attributes {};
//! Target architecture.
Arch _arch;
Arch _arch {};
//! SP register ID (to access call stack and local stack).
uint8_t _spRegId;
uint8_t _spRegId = uint8_t(BaseReg::kIdBad);
//! SA register ID (to access stack arguments).
uint8_t _saRegId;
uint8_t _saRegId = uint8_t(BaseReg::kIdBad);
//! Red zone size (copied from CallConv).
uint8_t _redZoneSize;
uint8_t _redZoneSize = 0;
//! Spill zone size (copied from CallConv).
uint8_t _spillZoneSize;
uint8_t _spillZoneSize = 0;
//! Natural stack alignment (copied from CallConv).
uint8_t _naturalStackAlignment;
uint8_t _naturalStackAlignment = 0;
//! Minimum stack alignment to turn on dynamic alignment.
uint8_t _minDynamicAlignment;
uint8_t _minDynamicAlignment = 0;
//! Call stack alignment.
uint8_t _callStackAlignment;
uint8_t _callStackAlignment = 0;
//! Local stack alignment.
uint8_t _localStackAlignment;
uint8_t _localStackAlignment = 0;
//! Final stack alignment.
uint8_t _finalStackAlignment;
uint8_t _finalStackAlignment = 0;
//! Adjustment of the stack before returning (X86-STDCALL).
uint16_t _calleeStackCleanup;
uint16_t _calleeStackCleanup = 0;
//! Call stack size.
uint32_t _callStackSize;
uint32_t _callStackSize = 0;
//! Local stack size.
uint32_t _localStackSize;
uint32_t _localStackSize = 0;
//! Final stack size (sum of call stack and local stack).
uint32_t _finalStackSize;
uint32_t _finalStackSize = 0;
//! Local stack offset (non-zero only if call stack is used).
uint32_t _localStackOffset;
uint32_t _localStackOffset = 0;
//! Offset relative to SP that contains previous SP (before alignment).
uint32_t _daOffset;
uint32_t _daOffset = 0;
//! Offset of the first stack argument relative to SP.
uint32_t _saOffsetFromSP;
uint32_t _saOffsetFromSP = 0;
//! Offset of the first stack argument relative to SA (_saRegId or FP).
uint32_t _saOffsetFromSA;
uint32_t _saOffsetFromSA = 0;
//! Local stack adjustment in prolog/epilog.
uint32_t _stackAdjustment;
uint32_t _stackAdjustment = 0;
//! Registers that are dirty.
Support::Array<RegMask, Globals::kNumVirtGroups> _dirtyRegs;
Support::Array<RegMask, Globals::kNumVirtGroups> _dirtyRegs {};
//! Registers that must be preserved (copied from CallConv).
Support::Array<RegMask, Globals::kNumVirtGroups> _preservedRegs;
Support::Array<RegMask, Globals::kNumVirtGroups> _preservedRegs {};
//! Size to save/restore per register group.
Support::Array<uint8_t, Globals::kNumVirtGroups> _saveRestoreRegSize;
Support::Array<uint8_t, Globals::kNumVirtGroups> _saveRestoreRegSize {};
//! Alignment of save/restore area per register group.
Support::Array<uint8_t, Globals::kNumVirtGroups> _saveRestoreAlignment;
Support::Array<uint8_t, Globals::kNumVirtGroups> _saveRestoreAlignment {};
//! Stack size required to save registers with push/pop.
uint16_t _pushPopSaveSize;
uint16_t _pushPopSaveSize = 0;
//! Stack size required to save extra registers that cannot use push/pop.
uint16_t _extraRegSaveSize;
uint16_t _extraRegSaveSize = 0;
//! Offset where registers saved/restored via push/pop are stored
uint32_t _pushPopSaveOffset;
uint32_t _pushPopSaveOffset = 0;
//! Offset where extra registers that cannot use push/pop are stored.
uint32_t _extraRegSaveOffset;
uint32_t _extraRegSaveOffset = 0;
//! \}
//! \name Construction & Destruction
//! \{
ASMJIT_INLINE_NODEBUG FuncFrame() noexcept { reset(); }
//! Creates a default constructed function frame, which has initialized all members to their default values.
ASMJIT_INLINE_NODEBUG FuncFrame() noexcept = default;
//! Creates a copy of `other` function frame.
ASMJIT_INLINE_NODEBUG FuncFrame(const FuncFrame& other) noexcept = default;
ASMJIT_API Error init(const FuncDetail& func) noexcept;
//! \}
ASMJIT_INLINE_NODEBUG void reset() noexcept {
memset(this, 0, sizeof(FuncFrame));
_spRegId = BaseReg::kIdBad;
_saRegId = BaseReg::kIdBad;
_daOffset = kTagInvalidOffset;
}
//! \name Initialization & Reset
//! \{
//! Initializes the function frame based on `func` detail.
ASMJIT_API Error init(const FuncDetail& func) noexcept;
//! Resets the function frame into its default constructed state.
ASMJIT_INLINE_NODEBUG void reset() noexcept { *this = FuncFrame{}; }
//! \}
//! \name Overloaded Operators
//! \{
//! Copy assignment - function frame is copy assignable.
ASMJIT_INLINE_NODEBUG FuncFrame& operator=(const FuncFrame& other) noexcept = default;
//! \}
@ -1072,11 +1171,18 @@ public:
//! Tests whether the function calls other functions.
ASMJIT_INLINE_NODEBUG bool hasFuncCalls() const noexcept { return hasAttribute(FuncAttributes::kHasFuncCalls); }
//! Sets `kFlagHasCalls` to true.
//! Sets `FuncAttributes::kHasFuncCalls` to true.
ASMJIT_INLINE_NODEBUG void setFuncCalls() noexcept { addAttributes(FuncAttributes::kHasFuncCalls); }
//! Sets `kFlagHasCalls` to false.
//! Sets `FuncAttributes::kHasFuncCalls` to false.
ASMJIT_INLINE_NODEBUG void resetFuncCalls() noexcept { clearAttributes(FuncAttributes::kHasFuncCalls); }
//! Tests whether the function uses indirect branch protection, see \ref FuncAttributes::kIndirectBranchProtection.
ASMJIT_INLINE_NODEBUG bool hasIndirectBranchProtection() const noexcept { return hasAttribute(FuncAttributes::kIndirectBranchProtection); }
//! Enabled indirect branch protection (sets `FuncAttributes::kIndirectBranchProtection` attribute to true).
ASMJIT_INLINE_NODEBUG void setIndirectBranchProtection() noexcept { addAttributes(FuncAttributes::kIndirectBranchProtection); }
//! Disables indirect branch protection (sets `FuncAttributes::kIndirectBranchProtection` attribute to false).
ASMJIT_INLINE_NODEBUG void resetIndirectBranchProtection() noexcept { clearAttributes(FuncAttributes::kIndirectBranchProtection); }
//! Tests whether the function has AVX enabled.
ASMJIT_INLINE_NODEBUG bool isAvxEnabled() const noexcept { return hasAttribute(FuncAttributes::kX86_AVXEnabled); }
//! Enables AVX use.
@ -1251,11 +1357,16 @@ public:
addDirtyRegs(std::forward<Args>(args)...);
}
//! A helper function to set all registers from all register groups dirty.
//!
//! \note This should not be used in general as it's the most pessimistic case. However, it can be used for testing
//! or in cases in which all registers are considered clobbered.
ASMJIT_INLINE_NODEBUG void setAllDirty() noexcept {
for (size_t i = 0; i < ASMJIT_ARRAY_SIZE(_dirtyRegs); i++)
_dirtyRegs[i] = 0xFFFFFFFFu;
}
//! A helper function to set all registers from the given register `group` dirty.
inline void setAllDirty(RegGroup group) noexcept {
ASMJIT_ASSERT(group <= RegGroup::kMaxVirt);
_dirtyRegs[group] = 0xFFFFFFFFu;
@ -1278,6 +1389,7 @@ public:
return _preservedRegs[group];
}
//! Returns the size of a save-restore are for the required register `group`.
inline uint32_t saveRestoreRegSize(RegGroup group) const noexcept {
ASMJIT_ASSERT(group <= RegGroup::kMaxVirt);
return _saveRestoreRegSize[group];
@ -1332,25 +1444,27 @@ public:
//! \{
//! Function detail.
const FuncDetail* _funcDetail;
const FuncDetail* _funcDetail {};
//! Register that can be used to access arguments passed by stack.
uint8_t _saRegId;
uint8_t _saRegId = uint8_t(BaseReg::kIdBad);
//! Reserved for future use.
uint8_t _reserved[3];
uint8_t _reserved[3] {};
//! Mapping of each function argument.
FuncValuePack _argPacks[Globals::kMaxFuncArgs];
FuncValuePack _argPacks[Globals::kMaxFuncArgs] {};
//! \}
//! \name Construction & Destruction
//! \{
//! Creates either a default initialized `FuncArgsAssignment` or to assignment that links to `fd`, if non-null.
ASMJIT_INLINE_NODEBUG explicit FuncArgsAssignment(const FuncDetail* fd = nullptr) noexcept { reset(fd); }
ASMJIT_INLINE_NODEBUG FuncArgsAssignment(const FuncArgsAssignment& other) noexcept {
memcpy(this, &other, sizeof(*this));
}
//! Copy constructor.
ASMJIT_INLINE_NODEBUG FuncArgsAssignment(const FuncArgsAssignment& other) noexcept = default;
//! Resets this `FuncArgsAssignment` to either default constructed state or to assignment that links to `fd`,
//! if non-null.
inline void reset(const FuncDetail* fd = nullptr) noexcept {
_funcDetail = fd;
_saRegId = uint8_t(BaseReg::kIdBad);
@ -1360,10 +1474,20 @@ public:
//! \}
//! \name Overloaded Operators
//! \{
//! Copy assignment.
ASMJIT_INLINE_NODEBUG FuncArgsAssignment& operator=(const FuncArgsAssignment& other) noexcept = default;
//! \}
//! \name Accessors
//! \{
//! Returns the associated \ref FuncDetail of this `FuncArgsAssignment`.
ASMJIT_INLINE_NODEBUG const FuncDetail* funcDetail() const noexcept { return _funcDetail; }
//! Associates \ref FuncDetails with this `FuncArgsAssignment`.
ASMJIT_INLINE_NODEBUG void setFuncDetail(const FuncDetail* fd) noexcept { _funcDetail = fd; }
ASMJIT_INLINE_NODEBUG bool hasSARegId() const noexcept { return _saRegId != BaseReg::kIdBad; }
@ -1371,47 +1495,59 @@ public:
ASMJIT_INLINE_NODEBUG void setSARegId(uint32_t regId) { _saRegId = uint8_t(regId); }
ASMJIT_INLINE_NODEBUG void resetSARegId() { _saRegId = uint8_t(BaseReg::kIdBad); }
//! Returns assigned argument at `argIndex` and `valueIndex`.
//!
//! \note `argIndex` refers to he function argument and `valueIndex` refers to a value pack (in case multiple
//! values are passed as a single argument).
inline FuncValue& arg(size_t argIndex, size_t valueIndex) noexcept {
ASMJIT_ASSERT(argIndex < ASMJIT_ARRAY_SIZE(_argPacks));
return _argPacks[argIndex][valueIndex];
}
//! \overload
inline const FuncValue& arg(size_t argIndex, size_t valueIndex) const noexcept {
ASMJIT_ASSERT(argIndex < ASMJIT_ARRAY_SIZE(_argPacks));
return _argPacks[argIndex][valueIndex];
}
//! Tests whether argument at `argIndex` and `valueIndex` has been assigned.
inline bool isAssigned(size_t argIndex, size_t valueIndex) const noexcept {
ASMJIT_ASSERT(argIndex < ASMJIT_ARRAY_SIZE(_argPacks));
return _argPacks[argIndex][valueIndex].isAssigned();
}
//! Assigns register at `argIndex` and value index of 0 to `reg` and an optional `typeId`.
inline void assignReg(size_t argIndex, const BaseReg& reg, TypeId typeId = TypeId::kVoid) noexcept {
ASMJIT_ASSERT(argIndex < ASMJIT_ARRAY_SIZE(_argPacks));
ASMJIT_ASSERT(reg.isPhysReg());
_argPacks[argIndex][0].initReg(reg.type(), reg.id(), typeId);
}
//! Assigns register at `argIndex` and value index of 0 to `regType`, `regId`, and an optional `typeId`.
inline void assignReg(size_t argIndex, RegType regType, uint32_t regId, TypeId typeId = TypeId::kVoid) noexcept {
ASMJIT_ASSERT(argIndex < ASMJIT_ARRAY_SIZE(_argPacks));
_argPacks[argIndex][0].initReg(regType, regId, typeId);
}
//! Assigns stack at `argIndex` and value index of 0 to `offset` and an optional `typeId`.
inline void assignStack(size_t argIndex, int32_t offset, TypeId typeId = TypeId::kVoid) noexcept {
ASMJIT_ASSERT(argIndex < ASMJIT_ARRAY_SIZE(_argPacks));
_argPacks[argIndex][0].initStack(offset, typeId);
}
//! Assigns register at `argIndex` and `valueIndex` to `reg` and an optional `typeId`.
inline void assignRegInPack(size_t argIndex, size_t valueIndex, const BaseReg& reg, TypeId typeId = TypeId::kVoid) noexcept {
ASMJIT_ASSERT(argIndex < ASMJIT_ARRAY_SIZE(_argPacks));
ASMJIT_ASSERT(reg.isPhysReg());
_argPacks[argIndex][valueIndex].initReg(reg.type(), reg.id(), typeId);
}
//! Assigns register at `argIndex` and `valueIndex` to `regType`, `regId`, and an optional `typeId`.
inline void assignRegInPack(size_t argIndex, size_t valueIndex, RegType regType, uint32_t regId, TypeId typeId = TypeId::kVoid) noexcept {
ASMJIT_ASSERT(argIndex < ASMJIT_ARRAY_SIZE(_argPacks));
_argPacks[argIndex][valueIndex].initReg(regType, regId, typeId);
}
//! Assigns stack at `argIndex` and `valueIndex` to `offset` and an optional `typeId`.
inline void assignStackInPack(size_t argIndex, size_t valueIndex, int32_t offset, TypeId typeId = TypeId::kVoid) noexcept {
ASMJIT_ASSERT(argIndex < ASMJIT_ARRAY_SIZE(_argPacks));
_argPacks[argIndex][valueIndex].initStack(offset, typeId);
@ -1430,6 +1566,9 @@ public:
_assignAllInternal(argIndex + 1, std::forward<Args>(args)...);
}
//! Assigns all argument at once.
//!
//! \note This function can be only used if the arguments don't contain value packs (multiple values per argument).
template<typename... Args>
inline void assignAll(Args&&... args) noexcept {
_assignAllInternal(0, std::forward<Args>(args)...);
@ -1442,8 +1581,8 @@ public:
//! Update `FuncFrame` based on function's arguments assignment.
//!
//! \note You MUST call this in order to use `BaseEmitter::emitArgsAssignment()`, otherwise the FuncFrame would
//! not contain the information necessary to assign all arguments into the registers and/or stack specified.
//! \note This function must be called in order to use `BaseEmitter::emitArgsAssignment()`, otherwise the \ref FuncFrame
//! would not contain the information necessary to assign all arguments into the registers and/or stack specified.
ASMJIT_API Error updateFuncFrame(FuncFrame& frame) const noexcept;
//! \}
@ -1454,4 +1593,3 @@ public:
ASMJIT_END_NAMESPACE
#endif // ASMJIT_CORE_FUNC_H_INCLUDED

View File

@ -102,10 +102,24 @@ ASMJIT_FAVOR_SIZE Error FuncArgsContext::initWorkData(const FuncFrame& frame, co
ASMJIT_ASSERT(dstWd != nullptr);
dstWd->assign(varId, srcId);
// The best case, register is allocated where it is expected to be.
if (dstId == srcId)
// The best case, register is allocated where it is expected to be. However, we should
// not mark this as done if both registers are GP and sign or zero extension is required.
if (dstId == srcId) {
if (dstGroup != RegGroup::kGp) {
var.markDone();
}
else {
TypeId dt = dst.typeId();
TypeId st = src.typeId();
uint32_t dstSize = TypeUtils::sizeOf(dt);
uint32_t srcSize = TypeUtils::sizeOf(st);
if (dt == TypeId::kVoid || st == TypeId::kVoid || dstSize <= srcSize)
var.markDone();
}
}
}
else {
if (ASMJIT_UNLIKELY(srcGroup > RegGroup::kMaxVirt))
return DebugUtils::errored(kErrorInvalidState);

View File

@ -87,6 +87,7 @@ ASMJIT_FAVOR_SIZE const char* DebugUtils::errorAsString(Error err) noexcept {
"ExpressionOverflow\0"
"FailedToOpenAnonymousMemory\0"
"FailedToOpenFile\0"
"ProtectionFailure\0"
"<Unknown>\0";
static const uint16_t sErrorIndex[] = {
@ -94,7 +95,7 @@ ASMJIT_FAVOR_SIZE const char* DebugUtils::errorAsString(Error err) noexcept {
247, 264, 283, 298, 314, 333, 352, 370, 392, 410, 429, 444, 460, 474, 488,
508, 533, 551, 573, 595, 612, 629, 645, 661, 677, 694, 709, 724, 744, 764,
784, 817, 837, 852, 869, 888, 909, 929, 943, 964, 978, 996, 1012, 1028, 1047,
1073, 1088, 1104, 1119, 1134, 1164, 1188, 1207, 1235, 1252
1073, 1088, 1104, 1119, 1134, 1164, 1188, 1207, 1235, 1252, 1270
};
// @EnumStringEnd@

View File

@ -96,10 +96,9 @@ static constexpr uint32_t kMaxTreeHeight = (ASMJIT_ARCH_BITS == 32 ? 30 : 61) +
static constexpr uint32_t kMaxOpCount = 6;
//! Maximum arguments of a function supported by the Compiler / Function API.
static constexpr uint32_t kMaxFuncArgs = 16;
static constexpr uint32_t kMaxFuncArgs = 32;
//! The number of values that can be assigned to a single function argument or
//! return value.
//! The number of values that can be assigned to a single function argument or return value.
static constexpr uint32_t kMaxValuePack = 4;
//! Maximum number of physical registers AsmJit can use per register group.
@ -132,14 +131,18 @@ static constexpr uint32_t kNumVirtGroups = 4;
struct Init_ {};
struct NoInit_ {};
//! A decorator used to initialize.
static const constexpr Init_ Init {};
//! A decorator used to not initialize.
static const constexpr NoInit_ NoInit {};
} // {Globals}
//! Casts a `void*` pointer `func` to a function pointer `Func`.
template<typename Func>
static ASMJIT_INLINE_NODEBUG Func ptr_as_func(void* func) noexcept { return Support::ptr_cast_impl<Func, void*>(func); }
//! Casts a function pointer `func` to a void pointer `void*`.
template<typename Func>
static ASMJIT_INLINE_NODEBUG void* func_as_ptr(Func func) noexcept { return Support::ptr_cast_impl<void*, Func>(func); }
@ -331,6 +334,10 @@ enum ErrorCode : uint32_t {
//! \note This is a generic error that is used by internal filesystem API.
kErrorFailedToOpenFile,
//! Protection failure can be returned from a virtual memory allocator or when trying to change memory access
//! permissions.
kErrorProtectionFailure,
// @EnumValuesEnd@
//! Count of AsmJit error codes.

View File

@ -24,12 +24,12 @@ ASMJIT_BEGIN_NAMESPACE
Error InstAPI::instIdToString(Arch arch, InstId instId, String& output) noexcept {
#if !defined(ASMJIT_NO_X86)
if (Environment::isFamilyX86(arch))
return x86::InstInternal::instIdToString(arch, instId, output);
return x86::InstInternal::instIdToString(instId, output);
#endif
#if !defined(ASMJIT_NO_AARCH64)
if (Environment::isFamilyAArch64(arch))
return a64::InstInternal::instIdToString(arch, instId, output);
return a64::InstInternal::instIdToString(instId, output);
#endif
return DebugUtils::errored(kErrorInvalidArch);
@ -38,12 +38,12 @@ Error InstAPI::instIdToString(Arch arch, InstId instId, String& output) noexcept
InstId InstAPI::stringToInstId(Arch arch, const char* s, size_t len) noexcept {
#if !defined(ASMJIT_NO_X86)
if (Environment::isFamilyX86(arch))
return x86::InstInternal::stringToInstId(arch, s, len);
return x86::InstInternal::stringToInstId(s, len);
#endif
#if !defined(ASMJIT_NO_AARCH64)
if (Environment::isFamilyAArch64(arch))
return a64::InstInternal::stringToInstId(arch, s, len);
return a64::InstInternal::stringToInstId(s, len);
#endif
return 0;
@ -56,13 +56,17 @@ InstId InstAPI::stringToInstId(Arch arch, const char* s, size_t len) noexcept {
#ifndef ASMJIT_NO_VALIDATION
Error InstAPI::validate(Arch arch, const BaseInst& inst, const Operand_* operands, size_t opCount, ValidationFlags validationFlags) noexcept {
#if !defined(ASMJIT_NO_X86)
if (Environment::isFamilyX86(arch))
return x86::InstInternal::validate(arch, inst, operands, opCount, validationFlags);
if (Environment::isFamilyX86(arch)) {
if (arch == Arch::kX86)
return x86::InstInternal::validateX86(inst, operands, opCount, validationFlags);
else
return x86::InstInternal::validateX64(inst, operands, opCount, validationFlags);
}
#endif
#if !defined(ASMJIT_NO_AARCH64)
if (Environment::isFamilyAArch64(arch))
return a64::InstInternal::validate(arch, inst, operands, opCount, validationFlags);
return a64::InstInternal::validate(inst, operands, opCount, validationFlags);
#endif
return DebugUtils::errored(kErrorInvalidArch);
@ -84,7 +88,7 @@ Error InstAPI::queryRWInfo(Arch arch, const BaseInst& inst, const Operand_* oper
#if !defined(ASMJIT_NO_AARCH64)
if (Environment::isFamilyAArch64(arch))
return a64::InstInternal::queryRWInfo(arch, inst, operands, opCount, out);
return a64::InstInternal::queryRWInfo(inst, operands, opCount, out);
#endif
return DebugUtils::errored(kErrorInvalidArch);
@ -103,7 +107,7 @@ Error InstAPI::queryFeatures(Arch arch, const BaseInst& inst, const Operand_* op
#if !defined(ASMJIT_NO_AARCH64)
if (Environment::isFamilyAArch64(arch))
return a64::InstInternal::queryFeatures(arch, inst, operands, opCount, out);
return a64::InstInternal::queryFeatures(inst, operands, opCount, out);
#endif
return DebugUtils::errored(kErrorInvalidArch);

View File

@ -303,6 +303,9 @@ public:
ASMJIT_INLINE_NODEBUG arm::CondCode armCondCode() const noexcept { return (arm::CondCode)getInstIdPart<InstIdParts::kARM_Cond>(); }
ASMJIT_INLINE_NODEBUG void setArmCondCode(arm::CondCode cc) noexcept { setInstIdPart<InstIdParts::kARM_Cond>(uint32_t(cc)); }
ASMJIT_INLINE_NODEBUG a32::DataType armDt() const noexcept { return (a32::DataType)getInstIdPart<InstIdParts::kA32_DT>(); }
ASMJIT_INLINE_NODEBUG a32::DataType armDt2() const noexcept { return (a32::DataType)getInstIdPart<InstIdParts::kA32_DT2>(); }
//! \}
//! \name Statics
@ -312,12 +315,12 @@ public:
return id | (uint32_t(cc) << Support::ConstCTZ<uint32_t(InstIdParts::kARM_Cond)>::value);
}
static ASMJIT_INLINE_NODEBUG constexpr InstId composeARMInstId(uint32_t id, arm::DataType dt, arm::CondCode cc = arm::CondCode::kAL) noexcept {
static ASMJIT_INLINE_NODEBUG constexpr InstId composeARMInstId(uint32_t id, a32::DataType dt, arm::CondCode cc = arm::CondCode::kAL) noexcept {
return id | (uint32_t(dt) << Support::ConstCTZ<uint32_t(InstIdParts::kA32_DT)>::value)
| (uint32_t(cc) << Support::ConstCTZ<uint32_t(InstIdParts::kARM_Cond)>::value);
}
static ASMJIT_INLINE_NODEBUG constexpr InstId composeARMInstId(uint32_t id, arm::DataType dt, arm::DataType dt2, arm::CondCode cc = arm::CondCode::kAL) noexcept {
static ASMJIT_INLINE_NODEBUG constexpr InstId composeARMInstId(uint32_t id, a32::DataType dt, a32::DataType dt2, arm::CondCode cc = arm::CondCode::kAL) noexcept {
return id | (uint32_t(dt) << Support::ConstCTZ<uint32_t(InstIdParts::kA32_DT)>::value)
| (uint32_t(dt2) << Support::ConstCTZ<uint32_t(InstIdParts::kA32_DT2)>::value)
| (uint32_t(cc) << Support::ConstCTZ<uint32_t(InstIdParts::kARM_Cond)>::value);
@ -344,16 +347,16 @@ enum class CpuRWFlags : uint32_t {
// Common RW Flags (0x000000FF)
// ----------------------------
//! Carry flag.
kCF = 0x00000001u,
//! Signed overflow flag.
kOF = 0x00000002u,
//! Sign flag (negative/sign, if set).
kSF = 0x00000004u,
kOF = 0x00000001u,
//! Carry flag.
kCF = 0x00000002u,
//! Zero and/or equality flag (1 if zero/equal).
kZF = 0x00000008u,
kZF = 0x00000004u,
//! Sign flag (negative/sign, if set).
kSF = 0x00000008u,
// X86 Specific RW Flags (0xFFFFFF00)
// X86 Specific RW Flags
// ----------------------------------
//! Carry flag (X86, X86_64).
@ -384,12 +387,22 @@ enum class CpuRWFlags : uint32_t {
//! FPU C2 status flag (X86, X86_64).
kX86_C2 = 0x00040000u,
//! FPU C3 status flag (X86, X86_64).
kX86_C3 = 0x00080000u
kX86_C3 = 0x00080000u,
// ARM Specific RW Flags
// ----------------------------------
kARM_V = kOF,
kARM_C = kCF,
kARM_Z = kZF,
kARM_N = kSF,
kARM_Q = 0x00000100u,
kARM_GE = 0x00000200u
};
ASMJIT_DEFINE_ENUM_FLAGS(CpuRWFlags)
//! Operand read/write flags describe how the operand is accessed and some additional features.
enum class OpRWFlags {
enum class OpRWFlags : uint32_t {
//! No flags.
kNone = 0,
@ -499,7 +512,8 @@ struct OpRWInfo {
_consecutiveLeadCount = 0;
_resetReserved();
uint64_t mask = Support::lsbMask<uint64_t>(regSize);
uint64_t mask = Support::lsbMask<uint64_t>(Support::min<uint32_t>(regSize, 64));
_readByteMask = Support::test(opFlags, OpRWFlags::kRead) ? mask : uint64_t(0);
_writeByteMask = Support::test(opFlags, OpRWFlags::kWrite) ? mask : uint64_t(0);
_extendByteMask = 0;

101
deps/asmjit/src/asmjit/core/instdb.cpp vendored Normal file
View File

@ -0,0 +1,101 @@
// This file is part of AsmJit project <https://asmjit.com>
//
// See asmjit.h or LICENSE.md for license and copyright information
// SPDX-License-Identifier: Zlib
#include "../core/api-build_p.h"
#include "../core/instdb_p.h"
ASMJIT_BEGIN_NAMESPACE
namespace InstNameUtils {
static constexpr uint32_t kBufferSize = 32;
static ASMJIT_FORCE_INLINE char decode5BitChar(uint32_t c) noexcept {
uint32_t base = c <= 26 ? uint32_t('a') - 1u : uint32_t('0') - 27u;
return char(base + c);
}
static ASMJIT_FORCE_INLINE size_t decodeToBuffer(char nameOut[kBufferSize], uint32_t nameValue, const char* stringTable) noexcept {
size_t i;
if (nameValue & 0x80000000u) {
// Small string of 5-bit characters.
for (i = 0; i < 6; i++, nameValue >>= 5) {
uint32_t c = nameValue & 0x1F;
if (c == 0)
break;
nameOut[i] = decode5BitChar(c);
}
return i;
}
else {
size_t prefixBase = nameValue & 0xFFFu;
size_t prefixSize = (nameValue >> 12) & 0xFu;
size_t suffixBase = (nameValue >> 16) & 0xFFFu;
size_t suffixSize = (nameValue >> 28) & 0x7u;
for (i = 0; i < prefixSize; i++)
nameOut[i] = stringTable[prefixBase + i];
char* suffixOut = nameOut + prefixSize;
for (i = 0; i < suffixSize; i++)
suffixOut[i] = stringTable[suffixBase + i];
return prefixSize + suffixSize;
}
}
Error decode(String& output, uint32_t nameValue, const char* stringTable) noexcept {
char nameData[kBufferSize];
size_t nameSize = decodeToBuffer(nameData, nameValue, stringTable);
return output.append(nameData, nameSize);
}
InstId find(const char* s, size_t len, const InstNameIndex& nameIndex, const uint32_t* nameTable, const char* stringTable) noexcept {
if (ASMJIT_UNLIKELY(!s))
return BaseInst::kIdNone;
if (len == SIZE_MAX)
len = strlen(s);
if (ASMJIT_UNLIKELY(len == 0 || len > nameIndex.maxNameLength))
return BaseInst::kIdNone;
uint32_t prefix = uint32_t(s[0]) - 'a';
if (ASMJIT_UNLIKELY(prefix > 'z' - 'a'))
return BaseInst::kIdNone;
size_t base = nameIndex.data[prefix].start;
size_t end = nameIndex.data[prefix].end;
if (ASMJIT_UNLIKELY(!base))
return BaseInst::kIdNone;
char nameData[kBufferSize];
for (size_t lim = end - base; lim != 0; lim >>= 1) {
size_t instId = base + (lim >> 1);
size_t nameSize = decodeToBuffer(nameData, nameTable[instId], stringTable);
int result = Support::compareStringViews(s, len, nameData, nameSize);
if (result < 0)
continue;
if (result > 0) {
base = instId + 1;
lim--;
continue;
}
return InstId(instId);
}
return BaseInst::kIdNone;
}
} // {InstNameUtils}
ASMJIT_END_NAMESPACE

40
deps/asmjit/src/asmjit/core/instdb_p.h vendored Normal file
View File

@ -0,0 +1,40 @@
// This file is part of AsmJit project <https://asmjit.com>
//
// See asmjit.h or LICENSE.md for license and copyright information
// SPDX-License-Identifier: Zlib
#ifndef ASMJIT_CORE_INSTDB_P_H_INCLUDED
#define ASMJIT_CORE_INSTDB_P_H_INCLUDED
#include "../core/inst.h"
#include "../core/string.h"
ASMJIT_BEGIN_NAMESPACE
//! \cond INTERNAL
//! \addtogroup asmjit_instruction_db
//! \{
struct InstNameIndex {
struct Span {
uint16_t start;
uint16_t end;
};
Span data[26];
uint16_t maxNameLength;
};
namespace InstNameUtils {
Error decode(String& output, uint32_t nameValue, const char* stringTable) noexcept;
InstId find(const char* s, size_t len, const InstNameIndex& nameIndex, const uint32_t* nameTable, const char* stringTable) noexcept;
} // {InstNameUtils}
//! \}
//! \endcond
ASMJIT_END_NAMESPACE
#endif // ASMJIT_CORE_INSTDB_P_H_INCLUDED

View File

@ -37,12 +37,13 @@ static constexpr uint32_t kJitAllocatorMaxBlockSize = 1024 * 1024 * 64;
// ===========================
static inline uint32_t JitAllocator_defaultFillPattern() noexcept {
#if ASMJIT_ARCH_X86
// X86 and X86_64 - 4x 'int3' instruction.
if (ASMJIT_ARCH_X86)
return 0xCCCCCCCCu;
#else
// Unknown...
return 0u;
#endif
}
// JitAllocator - BitVectorRangeIterator
@ -447,7 +448,7 @@ static inline JitAllocatorPrivateImpl* JitAllocatorImpl_new(const JitAllocator::
// Setup pool count to [1..3].
size_t poolCount = 1;
if (Support::test(options, JitAllocatorOptions::kUseMultiplePools))
poolCount = kJitAllocatorMultiPoolCount;;
poolCount = kJitAllocatorMultiPoolCount;
// Setup block size [64kB..256MB].
if (blockSize < 64 * 1024 || blockSize > 256 * 1024 * 1024 || !Support::isPowerOf2(blockSize))
@ -1398,11 +1399,12 @@ static void test_jit_allocator_alloc_release() noexcept {
using Opt = JitAllocatorOptions;
VirtMem::HardenedRuntimeInfo hri = VirtMem::hardenedRuntimeInfo();
TestParams testParams[] = {
{ "Default" , Opt::kNone, 0, 0 },
{ "16MB blocks" , Opt::kNone, 16 * 1024 * 1024, 0 },
{ "256B granularity" , Opt::kNone, 0, 256 },
{ "kUseDualMapping" , Opt::kUseDualMapping , 0, 0 },
{ "kUseMultiplePools" , Opt::kUseMultiplePools, 0, 0 },
{ "kFillUnusedMemory" , Opt::kFillUnusedMemory, 0, 0 },
{ "kImmediateRelease" , Opt::kImmediateRelease, 0, 0 },
@ -1410,6 +1412,7 @@ static void test_jit_allocator_alloc_release() noexcept {
{ "kUseLargePages" , Opt::kUseLargePages, 0, 0 },
{ "kUseLargePages | kFillUnusedMemory" , Opt::kUseLargePages | Opt::kFillUnusedMemory, 0, 0 },
{ "kUseLargePages | kAlignBlockSizeToLargePage", Opt::kUseLargePages | Opt::kAlignBlockSizeToLargePage, 0, 0 },
{ "kUseDualMapping" , Opt::kUseDualMapping , 0, 0 },
{ "kUseDualMapping | kFillUnusedMemory" , Opt::kUseDualMapping | Opt::kFillUnusedMemory, 0, 0 }
};
@ -1426,6 +1429,12 @@ static void test_jit_allocator_alloc_release() noexcept {
}
for (uint32_t testId = 0; testId < ASMJIT_ARRAY_SIZE(testParams); testId++) {
// Don't try to allocate dual-mapping if dual mapping is not possible - it would fail the test.
if (Support::test(testParams[testId].options, JitAllocatorOptions::kUseDualMapping) &&
!Support::test(hri.flags, VirtMem::HardenedRuntimeFlags::kDualMapping)) {
continue;
}
INFO("JitAllocator(%s)", testParams[testId].name);
JitAllocator::CreateParams params {};

View File

@ -65,7 +65,7 @@ enum class JitAllocatorOptions : uint32_t {
//! since AsmJit can be compiled as a shared library and used by applications instrumented by UBSAN, it's not
//! possible to conditionally compile the support only when necessary.
//!
//! \important This flag controls a workaround to make it possible to use LLVM UBSAN with AsmJit's \ref JitAllocator.
//! \remarks This flag controls a workaround to make it possible to use LLVM UBSAN with AsmJit's \ref JitAllocator.
//! There is no undefined behavior even when `kDisableInitialPadding` is used, however, that doesn't really matter
//! as LLVM's UBSAN introduces one, and according to LLVM developers it's a "trade-off". This flag is safe to use
//! when the code is not instrumented with LLVM's UBSAN.
@ -73,7 +73,7 @@ enum class JitAllocatorOptions : uint32_t {
//! Enables the use of large pages, if they are supported and the process can actually allocate them.
//!
//! \important This flag is a hint - if large pages can be allocated, JitAllocator would try to allocate them.
//! \remarks This flag is a hint - if large pages can be allocated, JitAllocator would try to allocate them.
//! However, if the allocation fails, it will still try to fallback to use regular pages as \ref JitAllocator
//! is designed to minimize allocation failures, so a regular page is better than no page at all. Also, if a
//! block \ref JitAllocator wants to allocate is too small to consume a whole large page, regular page(s) will
@ -174,7 +174,7 @@ public:
uint32_t fillPattern = 0;
// Reset the content of `CreateParams`.
inline void reset() noexcept { memset(this, 0, sizeof(*this)); }
ASMJIT_INLINE_NODEBUG void reset() noexcept { *this = CreateParams{}; }
};
//! Creates a `JitAllocator` instance.
@ -272,17 +272,18 @@ public:
//! Depending on the type of the allocation strategy this could either be:
//!
//! - the same address as returned by `rx()` if the allocator uses RWX mapping (pages have all of Read, Write,
//! and Execute permissions) or MAP_JIT, which requires either \ref ProtectJitReadWriteScope or to call
//! VirtMem::protectJitMemory() manually.
//! and Execute permissions) or MAP_JIT, which requires either \ref VirtMem::ProtectJitReadWriteScope or to
//! call \ref VirtMem::protectJitMemory() manually.
//! - a valid pointer, but not the same as `rx` - this would be valid if dual mapping is used.
//! - NULL pointer, in case that the allocation strategy doesn't use RWX, MAP_JIT, or dual mapping. In this
//! case only \ref JitAllocator can copy new code into the executable memory referenced by \ref Addr.
//! case only \ref JitAllocator can copy new code into the executable memory referenced by \ref Span.
//!
//! \note If `rw()` returns a non-null pointer it's important to use either VirtMem::protectJitMemory() or
//! \ref ProtectJitReadWriteScope to guard the write, because in case of `MAP_JIT` it would temporarily switch
//! the permissions of the pointer to RW (that's per thread permissions). if \ref ProtectJitReadWriteScope is
//! not used it's important to clear the instruction cache via \ref VirtMem::flushInstructionCache() after the
//! write is done.
//! \ref VirtMem::ProtectJitReadWriteScope to guard the write, because in case of `MAP_JIT` it would temporarily
//! switch the permissions of the pointer to RW (that's per thread permissions).
//!
//! If \ref VirtMem::ProtectJitReadWriteScope is not used it's important to clear the instruction cache via
//! \ref VirtMem::flushInstructionCache() after the write is done.
ASMJIT_INLINE_NODEBUG void* rw() const noexcept { return _rw; }
//! Returns size of this span, aligned to the allocator granularity.
@ -430,12 +431,12 @@ public:
//! This is mostly for internal purposes, please use \ref WriteScope::write() instead.
ASMJIT_API Error scopedWrite(WriteScopeData& scope, Span& span, size_t offset, const void* src, size_t size) noexcept;
//! Alternative to `JitAllocator::write(span. writeFunc, userData)`, but under a write `scope`.
//! Alternative to `JitAllocator::write(span, writeFunc, userData)`, but under a write `scope`.
//!
//! This is mostly for internal purposes, please use \ref WriteScope::write() instead.
ASMJIT_API Error scopedWrite(WriteScopeData& scope, Span& span, WriteFunc writeFunc, void* userData) noexcept;
//! Alternative to `JitAllocator::write(span. <lambda>)`, but under a write `scope`.
//! Alternative to `JitAllocator::write(span, [lambda])`, but under a write `scope`.
//!
//! This is mostly for internal purposes, please use \ref WriteScope::write() instead.
template<class Lambda>

View File

@ -20,8 +20,10 @@ class CodeHolder;
//! \addtogroup asmjit_virtual_memory
//! \{
//! JIT execution runtime is a special `Target` that is designed to store and
//! execute the generated code.
//! JIT execution runtime is a special `Target` that is designed to store and execute a generated code.
//!
//! JIT runtime is the easiest way of using AsmJit as it abstracts allocation and deallocation of virtual memory
//! where executable code can be placed and from which it can be executed as well.
class ASMJIT_VIRTAPI JitRuntime : public Target {
public:
ASMJIT_NONCOPYABLE(JitRuntime)
@ -37,6 +39,16 @@ public:
//! Destroys the `JitRuntime` instance.
ASMJIT_API ~JitRuntime() noexcept override;
//! \}
//! \name Accessors
//! \{
//! Resets the \ref JitRuntime, freeing everything that was allocated by it.
//!
//! Depending on `resetPolicy` the currently held memory can be either freed entirely when ResetPolicy::kHard is used,
//! or the allocator can keep some of it for next allocations when ResetPolicy::kSoft is used, which is the default
//! behavior.
ASMJIT_INLINE_NODEBUG void reset(ResetPolicy resetPolicy = ResetPolicy::kSoft) noexcept {
_allocator.reset(resetPolicy);
}

View File

@ -23,13 +23,15 @@ enum class OperandType : uint32_t {
kReg = 1,
//! Operand is a memory.
kMem = 2,
//! Operand is a register-list.
kRegList = 3,
//! Operand is an immediate value.
kImm = 3,
kImm = 4,
//! Operand is a label.
kLabel = 4,
kLabel = 5,
//! Maximum value of `OperandType`.
kMaxValue = kLabel
kMaxValue = kRegList
};
static_assert(uint32_t(OperandType::kMem) == uint32_t(OperandType::kReg) + 1,
@ -47,7 +49,7 @@ enum class RegType : uint8_t {
//! No register - unused, invalid, multiple meanings.
kNone = 0,
//! This is not a register type. This value is reserved for a \ref Label that used in \ref BaseMem as a base.
//! This is not a register type. This value is reserved for a \ref Label that's used in \ref BaseMem as a base.
//!
//! Label tag is used as a sub-type, forming a unique signature across all operand types as 0x1 is never associated
//! with any register type. This means that a memory operand's BASE register can be constructed from virtually any
@ -55,7 +57,7 @@ enum class RegType : uint8_t {
kLabelTag = 1,
//! Universal type describing program counter (PC) or instruction pointer (IP) register, if the target architecture
//! actually exposes it as a separate register type, which most modern targets do.
//! actually exposes it as a separate register type, which most modern architectures do.
kPC = 2,
//! 8-bit low general purpose register (X86).
@ -64,21 +66,21 @@ enum class RegType : uint8_t {
kGp8Hi = 4,
//! 16-bit general purpose register (X86).
kGp16 = 5,
//! 32-bit general purpose register (X86|ARM).
//! 32-bit general purpose register (X86|AArch32|AArch64).
kGp32 = 6,
//! 64-bit general purpose register (X86|ARM).
//! 64-bit general purpose register (X86|AArch64).
kGp64 = 7,
//! 8-bit view of a vector register (ARM).
//! 8-bit view of a vector register (AArch64).
kVec8 = 8,
//! 16-bit view of a vector register (ARM).
//! 16-bit view of a vector register (AArch64).
kVec16 = 9,
//! 32-bit view of a vector register (ARM).
//! 32-bit view of a vector register (AArch32|AArch64).
kVec32 = 10,
//! 64-bit view of a vector register (ARM).
//! 64-bit view of a vector register (AArch32|AArch64).
//!
//! \note This is never used for MMX registers on X86, MMX registers have its own category.
kVec64 = 11,
//! 128-bit view of a vector register (X86|ARM).
//! 128-bit view of a vector register (X86|AArch32|AArch64).
kVec128 = 12,
//! 256-bit view of a vector register (X86).
kVec256 = 13,
@ -98,9 +100,6 @@ enum class RegType : uint8_t {
// X86 Specific Register Types
// ---------------------------
// X86 Specific Register Types
// ===========================
//! Instruction pointer (RIP), only addressable in \ref x86::Mem in 64-bit targets.
kX86_Rip = kPC,
//! Low GPB register (AL, BL, CL, DL, ...).
@ -137,7 +136,7 @@ enum class RegType : uint8_t {
kX86_Tmm = kExtra + 6,
// ARM Specific Register Types
// ===========================
// ---------------------------
//! Program pointer (PC) register (AArch64).
kARM_PC = kPC,
@ -153,7 +152,9 @@ enum class RegType : uint8_t {
kARM_VecS = kVec32,
//! 64-bit view of VFP/ASIMD register (D).
kARM_VecD = kVec64,
//! 128-bit view of VFP/ASIMD register (Q|V).
//! 128-bit view of VFP/ASIMD register (Q).
kARM_VecQ = kVec128,
//! 128-bit view of VFP/ASIMD register (V).
kARM_VecV = kVec128,
//! Maximum value of `RegType`.
@ -172,8 +173,8 @@ enum class RegGroup : uint8_t {
//! Describes X86 XMM|YMM|ZMM registers ARM/AArch64 V registers.
kVec = 1,
//! Extra virtual group #2 that can be used by Compiler for register allocation.
kExtraVirt2 = 2,
//! Mask register group compatible with all backends that can use masking.
kMask = 2,
//! Extra virtual group #3 that can be used by Compiler for register allocation.
kExtraVirt3 = 3,
@ -186,8 +187,8 @@ enum class RegGroup : uint8_t {
// X86 Specific Register Groups
// ----------------------------
//! K register group (KReg) - maps to \ref RegGroup::kExtraVirt2 (X86, X86_64).
kX86_K = kExtraVirt2,
//! K register group (KReg) - maps to \ref RegGroup::kMask (X86, X86_64).
kX86_K = kMask,
//! MMX register group (MM) - maps to \ref RegGroup::kExtraVirt3 (X86, X86_64).
kX86_MM = kExtraVirt3,
@ -331,25 +332,25 @@ struct OperandSignature {
ASMJIT_INLINE_NODEBUG constexpr uint32_t bits() const noexcept { return _bits; }
ASMJIT_INLINE_NODEBUG void setBits(uint32_t bits) noexcept { _bits = bits; }
template<uint32_t kFieldMask, uint32_t kFieldShift = Support::ConstCTZ<kFieldMask>::value>
template<uint32_t kFieldMask>
ASMJIT_INLINE_NODEBUG constexpr bool hasField() const noexcept {
return (_bits & kFieldMask) != 0;
}
template<uint32_t kFieldMask, uint32_t kFieldShift = Support::ConstCTZ<kFieldMask>::value>
template<uint32_t kFieldMask>
ASMJIT_INLINE_NODEBUG constexpr bool hasField(uint32_t value) const noexcept {
return (_bits & kFieldMask) != value << kFieldShift;
return (_bits & kFieldMask) != value << Support::ConstCTZ<kFieldMask>::value;
}
template<uint32_t kFieldMask, uint32_t kFieldShift = Support::ConstCTZ<kFieldMask>::value>
template<uint32_t kFieldMask>
ASMJIT_INLINE_NODEBUG constexpr uint32_t getField() const noexcept {
return (_bits >> kFieldShift) & (kFieldMask >> kFieldShift);
return (_bits >> Support::ConstCTZ<kFieldMask>::value) & (kFieldMask >> Support::ConstCTZ<kFieldMask>::value);
}
template<uint32_t kFieldMask, uint32_t kFieldShift = Support::ConstCTZ<kFieldMask>::value>
template<uint32_t kFieldMask>
ASMJIT_INLINE_NODEBUG void setField(uint32_t value) noexcept {
ASMJIT_ASSERT((value & ~(kFieldMask >> kFieldShift)) == 0);
_bits = (_bits & ~kFieldMask) | (value << kFieldShift);
ASMJIT_ASSERT(((value << Support::ConstCTZ<kFieldMask>::value) & ~kFieldMask) == 0);
_bits = (_bits & ~kFieldMask) | (value << Support::ConstCTZ<kFieldMask>::value);
}
ASMJIT_INLINE_NODEBUG constexpr OperandSignature subset(uint32_t mask) const noexcept { return OperandSignature{_bits & mask}; }
@ -389,8 +390,8 @@ struct OperandSignature {
ASMJIT_INLINE_NODEBUG void setRegType(RegType regType) noexcept { setField<kRegTypeMask>(uint32_t(regType)); }
ASMJIT_INLINE_NODEBUG void setRegGroup(RegGroup regGroup) noexcept { setField<kRegGroupMask>(uint32_t(regGroup)); }
ASMJIT_INLINE_NODEBUG void setMemBaseType(RegGroup baseType) noexcept { setField<kMemBaseTypeMask>(uint32_t(baseType)); }
ASMJIT_INLINE_NODEBUG void setMemIndexType(RegGroup indexType) noexcept { setField<kMemIndexTypeMask>(uint32_t(indexType)); }
ASMJIT_INLINE_NODEBUG void setMemBaseType(RegType baseType) noexcept { setField<kMemBaseTypeMask>(uint32_t(baseType)); }
ASMJIT_INLINE_NODEBUG void setMemIndexType(RegType indexType) noexcept { setField<kMemIndexTypeMask>(uint32_t(indexType)); }
ASMJIT_INLINE_NODEBUG void setPredicate(uint32_t predicate) noexcept { setField<kPredicateMask>(predicate); }
ASMJIT_INLINE_NODEBUG void setSize(uint32_t size) noexcept { setField<kSizeMask>(size); }
@ -529,7 +530,12 @@ struct Operand_ {
//! \endcond
//! Initializes the operand from `other` operand (used by operator overloads).
ASMJIT_INLINE_NODEBUG void copyFrom(const Operand_& other) noexcept { memcpy(this, &other, sizeof(Operand_)); }
ASMJIT_INLINE_NODEBUG void copyFrom(const Operand_& other) noexcept {
_signature._bits = other._signature._bits;
_baseId = other._baseId;
_data[0] = other._data[0];
_data[1] = other._data[1];
}
//! Resets the `Operand` to none.
//!
@ -590,6 +596,22 @@ struct Operand_ {
//! \}
//! \name Equality
//! \{
//! Tests whether the operand is 100% equal to `other` operand.
//!
//! \note This basically performs a binary comparison, if aby bit is
//! different the operands are not equal.
ASMJIT_INLINE_NODEBUG constexpr bool equals(const Operand_& other) const noexcept {
return bool(unsigned(_signature == other._signature) &
unsigned(_baseId == other._baseId ) &
unsigned(_data[0] == other._data[0] ) &
unsigned(_data[1] == other._data[1] ));
}
//! \}
//! \name Accessors
//! \{
@ -608,6 +630,8 @@ struct Operand_ {
//!
//! \note Improper use of `setSignature()` can lead to hard-to-debug errors.
ASMJIT_INLINE_NODEBUG void setSignature(const Signature& signature) noexcept { _signature = signature; }
//! \overload
ASMJIT_INLINE_NODEBUG void setSignature(uint32_t signature) noexcept { _signature._bits = signature; }
//! Returns the type of the operand, see `OpType`.
ASMJIT_INLINE_NODEBUG constexpr OperandType opType() const noexcept { return _signature.opType(); }
@ -615,6 +639,10 @@ struct Operand_ {
ASMJIT_INLINE_NODEBUG constexpr bool isNone() const noexcept { return _signature == Signature::fromBits(0); }
//! Tests whether the operand is a register (`OperandType::kReg`).
ASMJIT_INLINE_NODEBUG constexpr bool isReg() const noexcept { return opType() == OperandType::kReg; }
//! Tests whether the operand is a register-list.
//!
//! \note Register-list is currently only used by 32-bit ARM architecture.
ASMJIT_INLINE_NODEBUG constexpr bool isRegList() const noexcept { return opType() == OperandType::kRegList; }
//! Tests whether the operand is a memory location (`OperandType::kMem`).
ASMJIT_INLINE_NODEBUG constexpr bool isMem() const noexcept { return opType() == OperandType::kMem; }
//! Tests whether the operand is an immediate (`OperandType::kImm`).
@ -638,32 +666,71 @@ struct Operand_ {
//! not initialized.
ASMJIT_INLINE_NODEBUG constexpr uint32_t id() const noexcept { return _baseId; }
//! Tests whether the operand is 100% equal to `other` operand.
//!
//! \note This basically performs a binary comparison, if aby bit is
//! different the operands are not equal.
ASMJIT_INLINE_NODEBUG constexpr bool equals(const Operand_& other) const noexcept {
return (_signature == other._signature) &
(_baseId == other._baseId ) &
(_data[0] == other._data[0] ) &
(_data[1] == other._data[1] ) ;
}
//! Tests whether the operand is a register matching the given register `type`.
ASMJIT_INLINE_NODEBUG constexpr bool isReg(RegType type) const noexcept {
return _signature.subset(Signature::kOpTypeMask | Signature::kRegTypeMask) == (Signature::fromOpType(OperandType::kReg) | Signature::fromRegType(type));
}
//! Tests whether the operand is register and of register `type` and `id`.
ASMJIT_INLINE_NODEBUG constexpr bool isReg(RegType type, uint32_t id) const noexcept {
return isReg(type) && this->id() == id;
//! Tests whether the operand is a register of the provided register group `regGroup`.
ASMJIT_INLINE_NODEBUG constexpr bool isReg(RegGroup regGroup) const noexcept {
return _signature.subset(Signature::kOpTypeMask | Signature::kRegGroupMask) == (Signature::fromOpType(OperandType::kReg) | Signature::fromRegGroup(regGroup));
}
//! Tests whether the operand is register and of register type `regType` and `regId`.
ASMJIT_INLINE_NODEBUG constexpr bool isReg(RegType regType, uint32_t regId) const noexcept { return isReg(regType) && _baseId == regId; }
//! Tests whether the operand is register and of register group `regGroup` and `regId`.
ASMJIT_INLINE_NODEBUG constexpr bool isReg(RegGroup regGroup, uint32_t regId) const noexcept { return isReg(regGroup) && _baseId == regId; }
//! Tests whether the register is a general purpose register (any size).
ASMJIT_INLINE_NODEBUG constexpr bool isGp() const noexcept { return isReg(RegGroup::kGp); }
//! Tests whether the register is a 32-bit general purpose register.
ASMJIT_INLINE_NODEBUG constexpr bool isGp32() const noexcept { return isReg(RegType::kGp32); }
//! Tests whether the register is a 64-bit general purpose register.
ASMJIT_INLINE_NODEBUG constexpr bool isGp64() const noexcept { return isReg(RegType::kGp64); }
//! Tests whether the register is a vector register of any size.
ASMJIT_INLINE_NODEBUG constexpr bool isVec() const noexcept { return isReg(RegGroup::kVec); }
//! Tests whether the register is an 8-bit vector register or view (AArch64).
ASMJIT_INLINE_NODEBUG constexpr bool isVec8() const noexcept { return isReg(RegType::kVec8); }
//! Tests whether the register is a 16-bit vector register or view (AArch64).
ASMJIT_INLINE_NODEBUG constexpr bool isVec16() const noexcept { return isReg(RegType::kVec16); }
//! Tests whether the register is a 32-bit vector register or view (AArch32, AArch64).
ASMJIT_INLINE_NODEBUG constexpr bool isVec32() const noexcept { return isReg(RegType::kVec32); }
//! Tests whether the register is a 64-bit vector register or view (AArch32, AArch64).
ASMJIT_INLINE_NODEBUG constexpr bool isVec64() const noexcept { return isReg(RegType::kVec64); }
//! Tests whether the register is a 128-bit vector register or view (AArch32, AArch64, X86, X86_64).
ASMJIT_INLINE_NODEBUG constexpr bool isVec128() const noexcept { return isReg(RegType::kVec128); }
//! Tests whether the register is a 256-bit vector register or view (X86, X86_64).
ASMJIT_INLINE_NODEBUG constexpr bool isVec256() const noexcept { return isReg(RegType::kVec256); }
//! Tests whether the register is a 512-bit vector register or view (X86, X86_64).
ASMJIT_INLINE_NODEBUG constexpr bool isVec512() const noexcept { return isReg(RegType::kVec512); }
//! Tests whether the register is a mask register of any size.
ASMJIT_INLINE_NODEBUG constexpr bool isMask() const noexcept { return isReg(RegGroup::kMask); }
//! Tests whether the operand is a register matching the given register `type`.
ASMJIT_INLINE_NODEBUG constexpr bool isRegList(RegType type) const noexcept {
return _signature.subset(Signature::kOpTypeMask | Signature::kRegTypeMask) == (Signature::fromOpType(OperandType::kRegList) | Signature::fromRegType(type));
}
//! Tests whether the operand is a register or memory.
//!
//! \note This is useful on X86 and X86_64 architectures as many instructions support Reg/Mem operand combination.
//! So if the user code works with just \ref Operand, it's possible to check whether the operand is either a register
//! or memory location with a single check.
ASMJIT_INLINE_NODEBUG constexpr bool isRegOrMem() const noexcept {
return Support::isBetween<uint32_t>(uint32_t(opType()), uint32_t(OperandType::kReg), uint32_t(OperandType::kMem));
}
//! Tests whether the operand is a register, register-list, or memory.
//!
//! \note This is useful on 32-bit ARM architecture to check whether an operand references a register. It can be
//! used in other architectures too, but it would work identically to \ref isRegOrMem() as other architectures
//! don't provide register lists.
ASMJIT_INLINE_NODEBUG constexpr bool isRegOrRegListOrMem() const noexcept {
return Support::isBetween<uint32_t>(uint32_t(opType()), uint32_t(OperandType::kReg), uint32_t(OperandType::kRegList));
}
//! \}
//! \name Accessors (X86 Specific)
@ -680,10 +747,10 @@ struct Operand_ {
}
#if !defined(ASMJIT_NO_DEPRECATED)
ASMJIT_DEPRECATED("hasSize() is no longer portable - use x86RmSize() instead if, your target is X86/X86_64")
ASMJIT_DEPRECATED("hasSize() is no longer portable - use x86RmSize() instead, if your target is X86/X86_64")
ASMJIT_INLINE_NODEBUG constexpr bool hasSize() const noexcept { return x86RmSize() != 0u; }
ASMJIT_DEPRECATED("hasSize() is no longer portable - use x86RmSize() instead if, your target is X86/X86_64")
ASMJIT_DEPRECATED("hasSize() is no longer portable - use x86RmSize() instead, if your target is X86/X86_64")
ASMJIT_INLINE_NODEBUG constexpr bool hasSize(uint32_t s) const noexcept { return x86RmSize() == s; }
ASMJIT_DEPRECATED("size() is no longer portable - use x86RmSize() instead, if your target is X86/X86_64")
@ -712,7 +779,7 @@ public:
//! Creates an operand initialized to raw `[u0, u1, u2, u3]` values.
ASMJIT_INLINE_NODEBUG constexpr Operand(Globals::Init_, const Signature& u0, uint32_t u1, uint32_t u2, uint32_t u3) noexcept
: Operand_{ u0, u1, { u2, u3 }} {}
: Operand_{{u0._bits}, u1, {u2, u3}} {}
//! Creates an uninitialized operand (dangerous).
ASMJIT_INLINE_NODEBUG explicit Operand(Globals::NoInit_) noexcept {}
@ -818,10 +885,8 @@ struct BaseRegTraits {
kTypeId = uint32_t(TypeId::kVoid),
//! RegType is not valid by default.
kValid = 0,
//! Count of registers (0 if none).
kCount = 0,
//! Zero type by default (defeaults to None).
//! Zero type by default (defaults to None).
kType = uint32_t(RegType::kNone),
//! Zero group by default (defaults to GP).
kGroup = uint32_t(RegGroup::kGp),
@ -834,7 +899,7 @@ struct BaseRegTraits {
};
//! \endcond
//! Physical or virtual register operand.
//! Physical or virtual register operand (base).
class BaseReg : public Operand {
public:
//! \name Constants
@ -916,7 +981,7 @@ public:
}
//! Tests whether the register is valid (either virtual or physical).
ASMJIT_INLINE_NODEBUG constexpr bool isValid() const noexcept { return (_signature != 0) & (_baseId != kIdBad); }
ASMJIT_INLINE_NODEBUG constexpr bool isValid() const noexcept { return bool(unsigned(_signature != 0) & unsigned(_baseId != kIdBad)); }
//! Tests whether this is a physical register.
ASMJIT_INLINE_NODEBUG constexpr bool isPhysReg() const noexcept { return _baseId < kIdBad; }
@ -930,8 +995,10 @@ public:
//! Tests whether the register is a general purpose register (any size).
ASMJIT_INLINE_NODEBUG constexpr bool isGp() const noexcept { return isGroup(RegGroup::kGp); }
//! Tests whether the register is a vector register.
//! Tests whether the register is a vector register of any size.
ASMJIT_INLINE_NODEBUG constexpr bool isVec() const noexcept { return isGroup(RegGroup::kVec); }
//! Tests whether the register is a mask register of any size.
ASMJIT_INLINE_NODEBUG constexpr bool isMask() const noexcept { return isGroup(RegGroup::kMask); }
using Operand_::isReg;
@ -1098,13 +1165,10 @@ struct RegOnly {
//! \cond INTERNAL
//! Adds a template specialization for `REG_TYPE` into the local `RegTraits`.
#define ASMJIT_DEFINE_REG_TRAITS(REG, REG_TYPE, GROUP, SIZE, COUNT, TYPE_ID) \
#define ASMJIT_DEFINE_REG_TRAITS(REG_TYPE, GROUP, SIZE, TYPE_ID) \
template<> \
struct RegTraits<REG_TYPE> { \
typedef REG RegT; \
\
static constexpr uint32_t kValid = 1; \
static constexpr uint32_t kCount = COUNT; \
static constexpr RegType kType = REG_TYPE; \
static constexpr RegGroup kGroup = GROUP; \
static constexpr uint32_t kSize = SIZE; \
@ -1168,6 +1232,196 @@ public:
: BASE(Signature{kSignature}, id) {}
//! \endcond
//! List of physical registers (base).
//!
//! \note List of registers is only used by some ARM instructions at the moment.
class BaseRegList : public Operand {
public:
//! \name Constants
//! \{
enum : uint32_t {
kSignature = Signature::fromOpType(OperandType::kRegList).bits()
};
//! \}
//! \name Construction & Destruction
//! \{
//! Creates a dummy register operand.
ASMJIT_INLINE_NODEBUG constexpr BaseRegList() noexcept
: Operand(Globals::Init, Signature::fromOpType(OperandType::kRegList), 0, 0, 0) {}
//! Creates a new register operand which is the same as `other` .
ASMJIT_INLINE_NODEBUG constexpr BaseRegList(const BaseRegList& other) noexcept
: Operand(other) {}
//! Creates a new register operand compatible with `other`, but with a different `id`.
ASMJIT_INLINE_NODEBUG constexpr BaseRegList(const BaseRegList& other, RegMask regMask) noexcept
: Operand(Globals::Init, other._signature, regMask, 0, 0) {}
//! Creates a register initialized to the given `signature` and `id`.
ASMJIT_INLINE_NODEBUG constexpr BaseRegList(const Signature& signature, RegMask regMask) noexcept
: Operand(Globals::Init, signature, regMask, 0, 0) {}
ASMJIT_INLINE_NODEBUG explicit BaseRegList(Globals::NoInit_) noexcept
: Operand(Globals::NoInit) {}
//! \}
//! \name Overloaded Operators
//! \{
ASMJIT_INLINE_NODEBUG BaseRegList& operator=(const BaseRegList& other) noexcept = default;
//! \}
//! \name Accessors
//! \{
//! Tests whether the register-list is valid, which means it has a type and at least a single register in the list.
ASMJIT_INLINE_NODEBUG constexpr bool isValid() const noexcept { return bool(unsigned(_signature != 0u) & unsigned(_baseId != 0u)); }
//! Tests whether the register type matches `type` - same as `isReg(type)`, provided for convenience.
ASMJIT_INLINE_NODEBUG constexpr bool isType(RegType type) const noexcept { return _signature.subset(Signature::kRegTypeMask) == Signature::fromRegType(type); }
//! Tests whether the register group matches `group`.
ASMJIT_INLINE_NODEBUG constexpr bool isGroup(RegGroup group) const noexcept { return _signature.subset(Signature::kRegGroupMask) == Signature::fromRegGroup(group); }
//! Tests whether the register is a general purpose register (any size).
ASMJIT_INLINE_NODEBUG constexpr bool isGp() const noexcept { return isGroup(RegGroup::kGp); }
//! Tests whether the register is a vector register.
ASMJIT_INLINE_NODEBUG constexpr bool isVec() const noexcept { return isGroup(RegGroup::kVec); }
//! Returns the register type.
ASMJIT_INLINE_NODEBUG constexpr RegType type() const noexcept { return _signature.regType(); }
//! Returns the register group.
ASMJIT_INLINE_NODEBUG constexpr RegGroup group() const noexcept { return _signature.regGroup(); }
//! Returns the size of a single register in this register-list or 0 if unspecified.
ASMJIT_INLINE_NODEBUG constexpr uint32_t size() const noexcept { return _signature.getField<Signature::kSizeMask>(); }
//! Returns the register list as a mask, where each bit represents one physical register.
ASMJIT_INLINE_NODEBUG constexpr RegMask list() const noexcept { return _baseId; }
//! Sets the register list to `mask`.
ASMJIT_INLINE_NODEBUG void setList(RegMask mask) noexcept { _baseId = mask; }
//! Remoes all registers from the register-list by making the underlying register-mask zero.
ASMJIT_INLINE_NODEBUG void resetList() noexcept { _baseId = 0; }
//! Adds registers passed by a register `mask` to the register-list.
ASMJIT_INLINE_NODEBUG void addList(RegMask mask) noexcept { _baseId |= mask; }
//! Removes registers passed by a register `mask` to the register-list.
ASMJIT_INLINE_NODEBUG void clearList(RegMask mask) noexcept { _baseId &= ~mask; }
//! Uses AND operator to combine the current register-list with other register `mask`.
ASMJIT_INLINE_NODEBUG void andList(RegMask mask) noexcept { _baseId &= mask; }
//! Uses XOR operator to combine the current register-list with other register `mask`.
ASMJIT_INLINE_NODEBUG void xorList(RegMask mask) noexcept { _baseId ^= mask; }
//! Checks whether a physical register `physId` is in the register-list.
ASMJIT_INLINE_NODEBUG bool hasReg(uint32_t physId) const noexcept { return physId < 32u ? (_baseId & (1u << physId)) != 0 : false; }
//! Adds a physical register `physId` to the register-list.
ASMJIT_INLINE_NODEBUG void addReg(uint32_t physId) noexcept { addList(1u << physId); }
//! Removes a physical register `physId` from the register-list.
ASMJIT_INLINE_NODEBUG void clearReg(uint32_t physId) noexcept { clearList(1u << physId); }
//! Clones the register-list operand.
ASMJIT_INLINE_NODEBUG constexpr BaseRegList clone() const noexcept { return BaseRegList(*this); }
//! Casts this register to `RegT` by also changing its signature.
//!
//! \note Improper use of `cloneAs()` can lead to hard-to-debug errors.
template<typename RegListT>
ASMJIT_INLINE_NODEBUG constexpr RegListT cloneAs() const noexcept { return RegListT(Signature(RegListT::kSignature), list()); }
//! Casts this register to `other` by also changing its signature.
//!
//! \note Improper use of `cloneAs()` can lead to hard-to-debug errors.
template<typename RegListT>
ASMJIT_INLINE_NODEBUG constexpr RegListT cloneAs(const RegListT& other) const noexcept { return RegListT(other.signature(), list()); }
//! \}
};
template<typename RegT>
class RegListT : public BaseRegList {
public:
//! \name Construction & Destruction
//! \{
//! Creates a dummy register operand.
ASMJIT_INLINE_NODEBUG constexpr RegListT() noexcept
: BaseRegList() {}
//! Creates a new register operand which is the same as `other` .
ASMJIT_INLINE_NODEBUG constexpr RegListT(const RegListT& other) noexcept
: BaseRegList(other) {}
//! Creates a new register operand compatible with `other`, but with a different `id`.
ASMJIT_INLINE_NODEBUG constexpr RegListT(const RegListT& other, RegMask regMask) noexcept
: BaseRegList(other, regMask) {}
//! Creates a register initialized to the given `signature` and `id`.
ASMJIT_INLINE_NODEBUG constexpr RegListT(const Signature& signature, RegMask regMask) noexcept
: BaseRegList(signature, regMask) {}
//! Creates a register initialized to the given `signature` and `regs`.
ASMJIT_INLINE_NODEBUG RegListT(const Signature& signature, std::initializer_list<RegT> regs) noexcept
: BaseRegList(signature, RegMask(0)) { addRegs(regs); }
ASMJIT_INLINE_NODEBUG explicit RegListT(Globals::NoInit_) noexcept
: BaseRegList(Globals::NoInit) {}
//! \}
//! \name Overloaded Operators
//! \{
ASMJIT_INLINE_NODEBUG RegListT& operator=(const RegListT& other) noexcept = default;
//! \}
//! \name Accessors
//! \{
using BaseRegList::addList;
using BaseRegList::clearList;
using BaseRegList::andList;
using BaseRegList::xorList;
//! Adds registers to this register-list as provided by `other` register-list.
ASMJIT_INLINE_NODEBUG void addList(const RegListT<RegT>& other) noexcept { addList(other.list()); }
//! Removes registers contained in `other` register-list.
ASMJIT_INLINE_NODEBUG void clearList(const RegListT<RegT>& other) noexcept { clearList(other.list()); }
//! Uses AND operator to combine the current register-list with `other` register-list.
ASMJIT_INLINE_NODEBUG void andList(const RegListT<RegT>& other) noexcept { andList(other.list()); }
//! Uses XOR operator to combine the current register-list with `other` register-list.
ASMJIT_INLINE_NODEBUG void xorList(const RegListT<RegT>& other) noexcept { xorList(other.list()); }
using BaseRegList::addReg;
using BaseRegList::clearReg;
ASMJIT_INLINE_NODEBUG void addReg(const RegT& reg) noexcept {
if (reg.id() < 32u)
addReg(reg.id());
}
ASMJIT_INLINE_NODEBUG void addRegs(std::initializer_list<RegT> regs) noexcept {
for (const RegT& reg : regs)
addReg(reg);
}
ASMJIT_INLINE_NODEBUG void clearReg(const RegT& reg) noexcept {
if (reg.id() < 32u)
clearReg(reg.id());
}
ASMJIT_INLINE_NODEBUG void clearRegs(std::initializer_list<RegT> regs) noexcept {
for (const RegT& reg : regs)
clearReg(reg);
}
//! \}
};
//! Base class for all memory operands.
//!
//! The data is split into the following parts:
@ -1316,8 +1570,13 @@ public:
//! Sets the id of the BASE register (without modifying its type).
ASMJIT_INLINE_NODEBUG void setBaseId(uint32_t id) noexcept { _baseId = id; }
//! Sets the register type of the BASE register (without modifying its id).
ASMJIT_INLINE_NODEBUG void setBaseType(RegType regType) noexcept { _signature.setMemBaseType(regType); }
//! Sets the id of the INDEX register (without modifying its type).
ASMJIT_INLINE_NODEBUG void setIndexId(uint32_t id) noexcept { _data[kDataMemIndexId] = id; }
//! Sets the register type of the INDEX register (without modifying its id).
ASMJIT_INLINE_NODEBUG void setIndexType(RegType regType) noexcept { _signature.setMemIndexType(regType); }
//! Sets the base register to type and id of the given `base` operand.
ASMJIT_INLINE_NODEBUG void setBase(const BaseReg& base) noexcept { return _setBase(base.type(), base.id()); }

View File

@ -67,7 +67,7 @@ public:
uint32_t registerCount = arch == Arch::kX86 ? 8 : 16;
_availableRegs[RegGroup::kGp] = Support::lsbMask<RegMask>(registerCount) & ~Support::bitMask(4u);
_availableRegs[RegGroup::kVec] = Support::lsbMask<RegMask>(registerCount);
_availableRegs[RegGroup::kExtraVirt2] = Support::lsbMask<RegMask>(8);
_availableRegs[RegGroup::kMask] = Support::lsbMask<RegMask>(8);
_availableRegs[RegGroup::kExtraVirt3] = Support::lsbMask<RegMask>(8);
return kErrorOk;
}
@ -75,7 +75,7 @@ public:
case Arch::kAArch64: {
_availableRegs[RegGroup::kGp] = 0xFFFFFFFFu & ~Support::bitMask(18, 31u);
_availableRegs[RegGroup::kVec] = 0xFFFFFFFFu;
_availableRegs[RegGroup::kExtraVirt2] = 0;
_availableRegs[RegGroup::kMask] = 0;
_availableRegs[RegGroup::kExtraVirt3] = 0;
return kErrorOk;
}
@ -767,6 +767,12 @@ enum class RATiedFlags : uint32_t {
// Instruction Flags (Never used by RATiedReg)
// -------------------------------------------
//! Instruction has been patched to address a memory location instead of a register.
//!
//! This is currently only possible on X86 or X86_64 targets. It informs rewriter to rewrite the instruction if
//! necessary.
kInst_RegToMemPatched = 0x40000000u,
//! Instruction is transformable to another instruction if necessary.
//!
//! This is flag that is only used by \ref RAInst to inform register allocator that the instruction has some

View File

@ -21,8 +21,8 @@ static ASMJIT_FORCE_INLINE RATiedReg* RALocal_findTiedRegByWorkId(RATiedReg* tie
return nullptr;
}
// RALocalAllocator - Init & Reset
// ===============================
// RALocalAllocator - Initialization & Reset
// =========================================
Error RALocalAllocator::init() noexcept {
PhysToWorkMap* physToWorkMap;
@ -137,6 +137,7 @@ Error RALocalAllocator::switchToAssignment(PhysToWorkMap* dstPhysToWorkMap, cons
dst.initMaps(dstPhysToWorkMap, _tmpWorkToPhysMap);
dst.assignWorkIdsFromPhysIds();
// TODO: Remove this - finally enable this functionality.
if (tryMode)
return kErrorOk;
@ -601,6 +602,7 @@ Error RALocalAllocator::allocInst(InstNode* node) noexcept {
tiedReg->_useRewriteMask = 0;
tiedReg->markUseDone();
raInst->addFlags(RATiedFlags::kInst_RegToMemPatched);
usePending--;
rmAllocated = true;
@ -687,7 +689,7 @@ Error RALocalAllocator::allocInst(InstNode* node) noexcept {
// ------
//
// ALLOCATE / SHUFFLE all registers that we marked as `willUse` and weren't allocated yet. This is a bit
// complicated as the allocation is iterative. In some cases we have to wait before allocating a particual
// complicated as the allocation is iterative. In some cases we have to wait before allocating a particular
// physical register as it's still occupied by some other one, which we need to move before we can use it.
// In this case we skip it and allocate another some other instead (making it free for another iteration).
//
@ -836,7 +838,7 @@ Error RALocalAllocator::allocInst(InstNode* node) noexcept {
// STEP 9
// ------
//
// Vector registers can be cloberred partially by invoke - find if that's the case and clobber when necessary.
// Vector registers can be clobbered partially by invoke - find if that's the case and clobber when necessary.
if (node->isInvoke() && group == RegGroup::kVec) {
const InvokeNode* invokeNode = node->as<InvokeNode>();

View File

@ -29,52 +29,45 @@ public:
typedef RAAssignment::WorkToPhysMap WorkToPhysMap;
//! Link to `BaseRAPass`.
BaseRAPass* _pass;
BaseRAPass* _pass {};
//! Link to `BaseCompiler`.
BaseCompiler* _cc;
BaseCompiler* _cc {};
//! Architecture traits.
const ArchTraits* _archTraits;
const ArchTraits* _archTraits {};
//! Registers available to the allocator.
RARegMask _availableRegs;
RARegMask _availableRegs {};
//! Registers clobbered by the allocator.
RARegMask _clobberedRegs;
RARegMask _clobberedRegs {};
//! Register assignment (current).
RAAssignment _curAssignment;
RAAssignment _curAssignment {};
//! Register assignment used temporarily during assignment switches.
RAAssignment _tmpAssignment;
RAAssignment _tmpAssignment {};
//! Link to the current `RABlock`.
RABlock* _block;
RABlock* _block {};
//! InstNode.
InstNode* _node;
InstNode* _node {};
//! RA instruction.
RAInst* _raInst;
RAInst* _raInst {};
//! Count of all TiedReg's.
uint32_t _tiedTotal;
uint32_t _tiedTotal {};
//! TiedReg's total counter.
RARegCount _tiedCount;
RARegCount _tiedCount {};
//! Temporary workToPhysMap that can be used freely by the allocator.
WorkToPhysMap* _tmpWorkToPhysMap;
WorkToPhysMap* _tmpWorkToPhysMap {};
//! \name Construction & Destruction
//! \{
inline RALocalAllocator(BaseRAPass* pass) noexcept
inline explicit RALocalAllocator(BaseRAPass* pass) noexcept
: _pass(pass),
_cc(pass->cc()),
_archTraits(pass->_archTraits),
_availableRegs(pass->_availableRegs),
_clobberedRegs(),
_curAssignment(),
_block(nullptr),
_node(nullptr),
_raInst(nullptr),
_tiedTotal(),
_tiedCount() {}
_availableRegs(pass->_availableRegs) {}
Error init() noexcept;

View File

@ -366,21 +366,18 @@ Error BaseRAPass::initSharedAssignments(const ZoneVector<uint32_t>& sharedAssign
class RABlockVisitItem {
public:
RABlock* _block {};
uint32_t _index {};
inline RABlockVisitItem(RABlock* block, uint32_t index) noexcept
: _block(block),
_index(index) {}
inline RABlockVisitItem(const RABlockVisitItem& other) noexcept
: _block(other._block),
_index(other._index) {}
inline RABlockVisitItem(const RABlockVisitItem& other) noexcept = default;
inline RABlockVisitItem& operator=(const RABlockVisitItem& other) noexcept = default;
inline RABlock* block() const noexcept { return _block; }
inline uint32_t index() const noexcept { return _index; }
RABlock* _block;
uint32_t _index;
};
Error BaseRAPass::buildCFGViews() noexcept {
@ -479,12 +476,18 @@ Error BaseRAPass::buildCFGDominators() noexcept {
entryBlock->setIDom(entryBlock);
bool changed = true;
uint32_t nIters = 0;
#ifndef ASMJIT_NO_LOGGING
uint32_t numIters = 0;
#endif
while (changed) {
nIters++;
changed = false;
#ifndef ASMJIT_NO_LOGGING
numIters++;
#endif
uint32_t i = _pov.size();
while (i) {
RABlock* block = _pov[--i];
@ -511,7 +514,7 @@ Error BaseRAPass::buildCFGDominators() noexcept {
}
}
ASMJIT_RA_LOG_FORMAT(" Done (%u iterations)\n", nIters);
ASMJIT_RA_LOG_FORMAT(" Done (%u iterations)\n", numIters);
return kErrorOk;
}
@ -759,6 +762,13 @@ namespace LiveOps {
static ASMJIT_FORCE_INLINE bool op(BitWord* dst, const BitWord* a, const BitWord* b, const BitWord* c, uint32_t n) noexcept {
BitWord changed = 0;
#if defined(_MSC_VER) && _MSC_VER <= 1938
// MSVC workaround (see #427).
//
// MSVC incorrectly auto-vectorizes this loop when used with <In> operator. For some reason it trashes a content
// of a register, which causes the result to be incorrect. It's a compiler bug we have to prevent unfortunately.
#pragma loop(no_vector)
#endif
for (uint32_t i = 0; i < n; i++) {
BitWord before = dst[i];
BitWord after = Operator::op(before, a[i], b[i], c[i]);
@ -770,7 +780,7 @@ namespace LiveOps {
return changed != 0;
}
static ASMJIT_FORCE_INLINE bool recalcInOut(RABlock* block, uint32_t numBitWords, bool initial = false) noexcept {
static ASMJIT_NOINLINE bool recalcInOut(RABlock* block, uint32_t numBitWords, bool initial = false) noexcept {
bool changed = initial;
const RABlocks& successors = block->successors();
@ -801,7 +811,6 @@ ASMJIT_FAVOR_SPEED Error BaseRAPass::buildLiveness() noexcept {
uint32_t numAllBlocks = blockCount();
uint32_t numReachableBlocks = reachableBlockCount();
uint32_t numVisits = numReachableBlocks;
uint32_t numWorkRegs = workRegCount();
uint32_t numBitWords = ZoneBitVector::_wordsPerBits(numWorkRegs);
@ -871,7 +880,7 @@ ASMJIT_FAVOR_SPEED Error BaseRAPass::buildLiveness() noexcept {
if (tiedReg->hasConsecutiveParent()) {
RAWorkReg* consecutiveParentReg = workRegById(tiedReg->consecutiveParent());
consecutiveParentReg->addImmediateConsecutive(allocator(), workId);
ASMJIT_PROPAGATE(consecutiveParentReg->addImmediateConsecutive(allocator(), workId));
}
}
@ -891,6 +900,10 @@ ASMJIT_FAVOR_SPEED Error BaseRAPass::buildLiveness() noexcept {
// Calculate IN/OUT of Each Block
// ------------------------------
#ifndef ASMJIT_NO_LOGGING
uint32_t numVisits = numReachableBlocks;
#endif
{
ZoneStack<RABlock*> workList;
ZoneBitVector workBits;
@ -921,7 +934,9 @@ ASMJIT_FAVOR_SPEED Error BaseRAPass::buildLiveness() noexcept {
}
}
}
#ifndef ASMJIT_NO_LOGGING
numVisits++;
#endif
}
workList.reset();
@ -1208,6 +1223,7 @@ ASMJIT_FAVOR_SPEED Error BaseRAPass::binPack(RegGroup group) noexcept {
uint32_t numWorkRegs = workRegs.size();
RegMask availableRegs = _availableRegs[group];
RegMask preservedRegs = func()->frame().preservedRegs(group);
// First try to pack everything that provides register-id hint as these are most likely function arguments and fixed
// (precolored) virtual registers.
@ -1339,19 +1355,31 @@ ASMJIT_FAVOR_SPEED Error BaseRAPass::binPack(RegGroup group) noexcept {
if (workReg->isAllocated())
continue;
RegMask physRegs = availableRegs;
if (physRegs & workReg->preferredMask())
physRegs &= workReg->preferredMask();
RegMask remainingPhysRegs = availableRegs;
if (remainingPhysRegs & workReg->preferredMask())
remainingPhysRegs &= workReg->preferredMask();
while (physRegs) {
RegMask preferredMask = physRegs;
uint32_t physId = Support::ctz(preferredMask);
RegMask physRegs = remainingPhysRegs & ~preservedRegs;
remainingPhysRegs &= preservedRegs;
for (;;) {
if (!physRegs) {
if (!remainingPhysRegs)
break;
physRegs = remainingPhysRegs;
remainingPhysRegs = 0;
}
uint32_t physId = Support::ctz(physRegs);
if (workReg->clobberSurvivalMask()) {
preferredMask &= workReg->clobberSurvivalMask();
if (preferredMask)
RegMask preferredMask = (physRegs | remainingPhysRegs) & workReg->clobberSurvivalMask();
if (preferredMask) {
if (preferredMask & ~remainingPhysRegs)
preferredMask &= ~remainingPhysRegs;
physId = Support::ctz(preferredMask);
}
}
LiveRegSpans& live = _globalLiveSpans[group][physId];
Error err = tmpSpans.nonOverlappingUnionOf(allocator(), live, workReg->liveSpans(), LiveRegData(workReg->virtId()));
@ -1366,7 +1394,8 @@ ASMJIT_FAVOR_SPEED Error BaseRAPass::binPack(RegGroup group) noexcept {
if (ASMJIT_UNLIKELY(err != 0xFFFFFFFFu))
return err;
physRegs ^= Support::bitMask(physId);
physRegs &= ~Support::bitMask(physId);
remainingPhysRegs &= ~Support::bitMask(physId);
}
// Keep it in `workRegs` if it was not allocated.

View File

@ -335,6 +335,8 @@ public:
//! Clears instruction `flags` from this RAInst.
ASMJIT_INLINE_NODEBUG void clearFlags(RATiedFlags flags) noexcept { _flags &= ~flags; }
//! Tests whether one operand of this instruction has been patched from Reg to Mem.
ASMJIT_INLINE_NODEBUG bool isRegToMemPatched() const noexcept { return hasFlag(RATiedFlags::kInst_RegToMemPatched); }
//! Tests whether this instruction can be transformed to another instruction if necessary.
ASMJIT_INLINE_NODEBUG bool isTransformable() const noexcept { return hasFlag(RATiedFlags::kInst_IsTransformable); }

View File

@ -463,7 +463,7 @@ Error String::truncate(size_t newSize) noexcept {
return kErrorOk;
}
bool String::eq(const char* other, size_t size) const noexcept {
bool String::equals(const char* other, size_t size) const noexcept {
const char* aData = data();
const char* bData = other;
@ -499,8 +499,8 @@ UNIT(core_string) {
EXPECT_EQ(s.capacity(), String::kSSOCapacity);
EXPECT_EQ(s.data()[0], 'a');
EXPECT_EQ(s.data()[1], '\0');
EXPECT_TRUE(s.eq("a"));
EXPECT_TRUE(s.eq("a", 1));
EXPECT_TRUE(s.equals("a"));
EXPECT_TRUE(s.equals("a", 1));
EXPECT_EQ(s.assignChars('b', 4), kErrorOk);
EXPECT_EQ(s.size(), 4u);
@ -510,8 +510,8 @@ UNIT(core_string) {
EXPECT_EQ(s.data()[2], 'b');
EXPECT_EQ(s.data()[3], 'b');
EXPECT_EQ(s.data()[4], '\0');
EXPECT_TRUE(s.eq("bbbb"));
EXPECT_TRUE(s.eq("bbbb", 4));
EXPECT_TRUE(s.equals("bbbb"));
EXPECT_TRUE(s.equals("bbbb", 4));
EXPECT_EQ(s.assign("abc"), kErrorOk);
EXPECT_EQ(s.size(), 3u);
@ -520,16 +520,16 @@ UNIT(core_string) {
EXPECT_EQ(s.data()[1], 'b');
EXPECT_EQ(s.data()[2], 'c');
EXPECT_EQ(s.data()[3], '\0');
EXPECT_TRUE(s.eq("abc"));
EXPECT_TRUE(s.eq("abc", 3));
EXPECT_TRUE(s.equals("abc"));
EXPECT_TRUE(s.equals("abc", 3));
const char* large = "Large string that will not fit into SSO buffer";
EXPECT_EQ(s.assign(large), kErrorOk);
EXPECT_TRUE(s.isLargeOrExternal());
EXPECT_EQ(s.size(), strlen(large));
EXPECT_GT(s.capacity(), String::kSSOCapacity);
EXPECT_TRUE(s.eq(large));
EXPECT_TRUE(s.eq(large, strlen(large)));
EXPECT_TRUE(s.equals(large));
EXPECT_TRUE(s.equals(large, strlen(large)));
const char* additional = " (additional content)";
EXPECT_TRUE(s.isLargeOrExternal());
@ -543,10 +543,10 @@ UNIT(core_string) {
EXPECT_TRUE(s.isLargeOrExternal()); // Clear should never release the memory.
EXPECT_EQ(s.appendUInt(1234), kErrorOk);
EXPECT_TRUE(s.eq("1234"));
EXPECT_TRUE(s.equals("1234"));
EXPECT_EQ(s.assignUInt(0xFFFF, 16, 0, StringFormatFlags::kAlternate), kErrorOk);
EXPECT_TRUE(s.eq("0xFFFF"));
EXPECT_TRUE(s.equals("0xFFFF"));
StringTmp<64> sTmp;
EXPECT_TRUE(sTmp.isLargeOrExternal());

View File

@ -53,9 +53,12 @@ union FixedString {
//! \name Utilities
//! \{
inline bool eq(const char* other) const noexcept {
return strcmp(str, other) == 0;
}
inline bool equals(const char* other) const noexcept { return strcmp(str, other) == 0; }
#if !defined(ASMJIT_NO_DEPRECATED)
ASMJIT_DEPRECATED("Use FixedString::equals() instead")
inline bool eq(const char* other) const noexcept { return equals(other); }
#endif // !ASMJIT_NO_DEPRECATED
//! \}
};
@ -158,11 +161,11 @@ public:
return *this;
}
ASMJIT_INLINE_NODEBUG bool operator==(const char* other) const noexcept { return eq(other); }
ASMJIT_INLINE_NODEBUG bool operator!=(const char* other) const noexcept { return !eq(other); }
ASMJIT_INLINE_NODEBUG bool operator==(const char* other) const noexcept { return equals(other); }
ASMJIT_INLINE_NODEBUG bool operator!=(const char* other) const noexcept { return !equals(other); }
ASMJIT_INLINE_NODEBUG bool operator==(const String& other) const noexcept { return eq(other); }
ASMJIT_INLINE_NODEBUG bool operator!=(const String& other) const noexcept { return !eq(other); }
ASMJIT_INLINE_NODEBUG bool operator==(const String& other) const noexcept { return equals(other); }
ASMJIT_INLINE_NODEBUG bool operator!=(const String& other) const noexcept { return !equals(other); }
//! \}
@ -312,8 +315,16 @@ public:
//! Truncate the string length into `newSize`.
ASMJIT_API Error truncate(size_t newSize) noexcept;
ASMJIT_API bool eq(const char* other, size_t size = SIZE_MAX) const noexcept;
ASMJIT_INLINE_NODEBUG bool eq(const String& other) const noexcept { return eq(other.data(), other.size()); }
ASMJIT_API bool equals(const char* other, size_t size = SIZE_MAX) const noexcept;
ASMJIT_INLINE_NODEBUG bool equals(const String& other) const noexcept { return equals(other.data(), other.size()); }
#if !defined(ASMJIT_NO_DEPRECATED)
ASMJIT_DEPRECATED("Use String::equals() instead")
ASMJIT_INLINE_NODEBUG bool eq(const char* other, size_t size = SIZE_MAX) const noexcept { return equals(other, size); }
ASMJIT_DEPRECATED("Use String::equals() instead")
ASMJIT_INLINE_NODEBUG bool eq(const String& other) const noexcept { return equals(other.data(), other.size()); }
#endif // !ASMJIT_NO_DEPRECATED
//! \}

View File

@ -230,7 +230,7 @@ static ASMJIT_INLINE_NODEBUG constexpr T lsbMask(const CountT& n) noexcept {
: n ? T(shr(allOnes<T>(), bitSizeOf<T>() - size_t(n))) : T(0);
}
//! Generats a leading bit-mask that has `n` most significant (leading) bits set.
//! Generates a leading bit-mask that has `n` most significant (leading) bits set.
template<typename T, typename CountT>
static ASMJIT_INLINE_NODEBUG constexpr T msbMask(const CountT& n) noexcept {
typedef typename std::make_unsigned<T>::type U;
@ -321,12 +321,12 @@ struct BitScanCalc<T, 0> {
};
template<typename T>
constexpr ASMJIT_INLINE_NODEBUG uint32_t clzFallback(const T& x) noexcept {
ASMJIT_INLINE_NODEBUG constexpr uint32_t clzFallback(const T& x) noexcept {
return BitScanCalc<T, bitSizeOf<T>() / 2u>::clz(BitScanData<T>{x, 1}).n;
}
template<typename T>
constexpr ASMJIT_INLINE_NODEBUG uint32_t ctzFallback(const T& x) noexcept {
ASMJIT_INLINE_NODEBUG constexpr uint32_t ctzFallback(const T& x) noexcept {
return BitScanCalc<T, bitSizeOf<T>() / 2u>::ctz(BitScanData<T>{x, 1}).n;
}
@ -457,31 +457,30 @@ namespace Internal {
}
static ASMJIT_INLINE_NODEBUG uint32_t constPopcntImpl(uint64_t x) noexcept {
if (ASMJIT_ARCH_BITS >= 64) {
#if ASMJIT_ARCH_BITS >= 64
x = x - ((x >> 1) & 0x5555555555555555u);
x = (x & 0x3333333333333333u) + ((x >> 2) & 0x3333333333333333u);
return uint32_t((((x + (x >> 4)) & 0x0F0F0F0F0F0F0F0Fu) * 0x0101010101010101u) >> 56);
}
else {
#else
return constPopcntImpl(uint32_t(x >> 32)) +
constPopcntImpl(uint32_t(x & 0xFFFFFFFFu));
}
#endif
}
static ASMJIT_INLINE_NODEBUG uint32_t popcntImpl(uint32_t x) noexcept {
#if defined(__GNUC__)
#if defined(__GNUC__)
return uint32_t(__builtin_popcount(x));
#else
#else
return constPopcntImpl(asUInt(x));
#endif
#endif
}
static ASMJIT_INLINE_NODEBUG uint32_t popcntImpl(uint64_t x) noexcept {
#if defined(__GNUC__)
#if defined(__GNUC__)
return uint32_t(__builtin_popcountll(x));
#else
#else
return constPopcntImpl(asUInt(x));
#endif
#endif
}
}
//! \endcond
@ -596,9 +595,9 @@ namespace Internal {
template<typename T> inline T subOverflowImpl(const T& x, const T& y, FastUInt8* of) noexcept { return subOverflowFallback(x, y, of); }
template<typename T> inline T mulOverflowImpl(const T& x, const T& y, FastUInt8* of) noexcept { return mulOverflowFallback(x, y, of); }
#if defined(__GNUC__) && !defined(ASMJIT_NO_INTRINSICS)
#if defined(__clang__) || __GNUC__ >= 5
#define ASMJIT_ARITH_OVERFLOW_SPECIALIZE(FUNC, T, RESULT_T, BUILTIN) \
#if defined(__GNUC__) && !defined(ASMJIT_NO_INTRINSICS)
#if defined(__clang__) || __GNUC__ >= 5
#define ASMJIT_ARITH_OVERFLOW_SPECIALIZE(FUNC, T, RESULT_T, BUILTIN) \
template<> \
inline T FUNC(const T& x, const T& y, FastUInt8* of) noexcept { \
RESULT_T result; \
@ -617,13 +616,13 @@ namespace Internal {
ASMJIT_ARITH_OVERFLOW_SPECIALIZE(mulOverflowImpl, uint32_t, unsigned int , __builtin_umul_overflow )
ASMJIT_ARITH_OVERFLOW_SPECIALIZE(mulOverflowImpl, int64_t , long long , __builtin_smulll_overflow)
ASMJIT_ARITH_OVERFLOW_SPECIALIZE(mulOverflowImpl, uint64_t, unsigned long long, __builtin_umulll_overflow)
#undef ASMJIT_ARITH_OVERFLOW_SPECIALIZE
#endif
#endif
#undef ASMJIT_ARITH_OVERFLOW_SPECIALIZE
#endif
#endif
// There is a bug in MSVC that makes these specializations unusable, maybe in the future...
#if defined(_MSC_VER) && 0
#define ASMJIT_ARITH_OVERFLOW_SPECIALIZE(FUNC, T, ALT_T, BUILTIN) \
#if defined(_MSC_VER) && 0
#define ASMJIT_ARITH_OVERFLOW_SPECIALIZE(FUNC, T, ALT_T, BUILTIN) \
template<> \
inline T FUNC(T x, T y, FastUInt8* of) noexcept { \
ALT_T result; \
@ -632,12 +631,12 @@ namespace Internal {
}
ASMJIT_ARITH_OVERFLOW_SPECIALIZE(addOverflowImpl, uint32_t, unsigned int , _addcarry_u32 )
ASMJIT_ARITH_OVERFLOW_SPECIALIZE(subOverflowImpl, uint32_t, unsigned int , _subborrow_u32)
#if ARCH_BITS >= 64
#if ARCH_BITS >= 64
ASMJIT_ARITH_OVERFLOW_SPECIALIZE(addOverflowImpl, uint64_t, unsigned __int64 , _addcarry_u64 )
ASMJIT_ARITH_OVERFLOW_SPECIALIZE(subOverflowImpl, uint64_t, unsigned __int64 , _subborrow_u64)
#endif
#undef ASMJIT_ARITH_OVERFLOW_SPECIALIZE
#endif
#endif
#undef ASMJIT_ARITH_OVERFLOW_SPECIALIZE
#endif
} // {Internal}
//! \endcond
@ -926,19 +925,6 @@ static ASMJIT_INLINE_NODEBUG const char* findPackedString(const char* p, uint32_
return p;
}
//! Compares two instruction names.
//!
//! `a` is a null terminated instruction name from arch-specific `nameData[]`
//! table. `b` is a possibly non-null terminated instruction name passed to
//! `InstAPI::stringToInstId()`.
static ASMJIT_FORCE_INLINE int cmpInstName(const char* a, const char* b, size_t size) noexcept {
for (size_t i = 0; i < size; i++) {
int c = int(uint8_t(a[i])) - int(uint8_t(b[i]));
if (c != 0) return c;
}
return int(uint8_t(a[size]));
}
//! Compares two string views.
static ASMJIT_FORCE_INLINE int compareStringViews(const char* aData, size_t aSize, const char* bData, size_t bSize) noexcept {
size_t size = Support::min(aSize, bSize);
@ -951,6 +937,7 @@ static ASMJIT_FORCE_INLINE int compareStringViews(const char* aData, size_t aSiz
return int(aSize) - int(bSize);
}
// Support - Memory Read Access - 8 Bits
// =====================================
@ -997,9 +984,9 @@ static ASMJIT_INLINE_NODEBUG int16_t readI16aBE(const void* p) noexcept { return
template<ByteOrder BO = ByteOrder::kNative>
static inline uint32_t readU24u(const void* p) noexcept {
uint32_t b0 = readU8(static_cast<const uint8_t*>(p) + (BO == ByteOrder::kLE ? 2 : 0));
uint32_t b1 = readU8(static_cast<const uint8_t*>(p) + (BO == ByteOrder::kLE ? 1 : 1));
uint32_t b2 = readU8(static_cast<const uint8_t*>(p) + (BO == ByteOrder::kLE ? 0 : 2));
uint32_t b0 = readU8(static_cast<const uint8_t*>(p) + (BO == ByteOrder::kLE ? 2u : 0u));
uint32_t b1 = readU8(static_cast<const uint8_t*>(p) + 1u);
uint32_t b2 = readU8(static_cast<const uint8_t*>(p) + (BO == ByteOrder::kLE ? 0u : 2u));
return (b0 << 16) | (b1 << 8) | b2;
}
@ -1119,7 +1106,7 @@ static ASMJIT_INLINE_NODEBUG void writeI16aBE(void* p, int16_t x) noexcept { wri
template<ByteOrder BO = ByteOrder::kNative>
static inline void writeU24u(void* p, uint32_t v) noexcept {
static_cast<uint8_t*>(p)[0] = uint8_t((v >> (BO == ByteOrder::kLE ? 0 : 16)) & 0xFFu);
static_cast<uint8_t*>(p)[1] = uint8_t((v >> (BO == ByteOrder::kLE ? 8 : 8)) & 0xFFu);
static_cast<uint8_t*>(p)[1] = uint8_t((v >> 8) & 0xFFu);
static_cast<uint8_t*>(p)[2] = uint8_t((v >> (BO == ByteOrder::kLE ? 16 : 0)) & 0xFFu);
}
@ -1580,6 +1567,75 @@ static ASMJIT_INLINE_NODEBUG void qSort(T* base, size_t size, const CompareT& cm
Internal::QSortImpl<T, CompareT>::sort(base, size, cmp);
}
// Support - ReverseIterator
// =========================
//! Reverse iterator to avoid including `<iterator>` header for iteration over arrays, specialized for
//! AsmJit use (noexcept by design).
template<typename T>
class ArrayReverseIterator {
public:
//! \name Members
//! \{
T* _ptr {};
//! \}
//! \name Construction & Destruction
//! \{
ASMJIT_INLINE_NODEBUG constexpr ArrayReverseIterator() noexcept = default;
ASMJIT_INLINE_NODEBUG constexpr ArrayReverseIterator(const ArrayReverseIterator& other) noexcept = default;
ASMJIT_INLINE_NODEBUG constexpr ArrayReverseIterator(T* ptr) noexcept : _ptr(ptr) {}
//! \}
//! \name Overloaded Operators
//! \{
ASMJIT_INLINE_NODEBUG ArrayReverseIterator& operator=(const ArrayReverseIterator& other) noexcept = default;
ASMJIT_INLINE_NODEBUG bool operator==(const T* other) const noexcept { return _ptr == other; }
ASMJIT_INLINE_NODEBUG bool operator==(const ArrayReverseIterator& other) const noexcept { return _ptr == other._ptr; }
ASMJIT_INLINE_NODEBUG bool operator!=(const T* other) const noexcept { return _ptr != other; }
ASMJIT_INLINE_NODEBUG bool operator!=(const ArrayReverseIterator& other) const noexcept { return _ptr != other._ptr; }
ASMJIT_INLINE_NODEBUG bool operator<(const T* other) const noexcept { return _ptr < other; }
ASMJIT_INLINE_NODEBUG bool operator<(const ArrayReverseIterator& other) const noexcept { return _ptr < other._ptr; }
ASMJIT_INLINE_NODEBUG bool operator<=(const T* other) const noexcept { return _ptr <= other; }
ASMJIT_INLINE_NODEBUG bool operator<=(const ArrayReverseIterator& other) const noexcept { return _ptr <= other._ptr; }
ASMJIT_INLINE_NODEBUG bool operator>(const T* other) const noexcept { return _ptr > other; }
ASMJIT_INLINE_NODEBUG bool operator>(const ArrayReverseIterator& other) const noexcept { return _ptr > other._ptr; }
ASMJIT_INLINE_NODEBUG bool operator>=(const T* other) const noexcept { return _ptr >= other; }
ASMJIT_INLINE_NODEBUG bool operator>=(const ArrayReverseIterator& other) const noexcept { return _ptr >= other._ptr; }
ASMJIT_INLINE_NODEBUG ArrayReverseIterator& operator++() noexcept { _ptr--; return *this; }
ASMJIT_INLINE_NODEBUG ArrayReverseIterator& operator--() noexcept { _ptr++; return *this; }
ASMJIT_INLINE_NODEBUG ArrayReverseIterator operator++(int) noexcept { ArrayReverseIterator prev(*this); _ptr--; return prev; }
ASMJIT_INLINE_NODEBUG ArrayReverseIterator operator--(int) noexcept { ArrayReverseIterator prev(*this); _ptr++; return prev; }
template<typename Diff> ASMJIT_INLINE_NODEBUG ArrayReverseIterator operator+(const Diff& n) noexcept { return ArrayReverseIterator(_ptr -= n); }
template<typename Diff> ASMJIT_INLINE_NODEBUG ArrayReverseIterator operator-(const Diff& n) noexcept { return ArrayReverseIterator(_ptr += n); }
template<typename Diff> ASMJIT_INLINE_NODEBUG ArrayReverseIterator& operator+=(const Diff& n) noexcept { _ptr -= n; return *this; }
template<typename Diff> ASMJIT_INLINE_NODEBUG ArrayReverseIterator& operator-=(const Diff& n) noexcept { _ptr += n; return *this; }
ASMJIT_INLINE_NODEBUG constexpr T& operator*() const noexcept { return _ptr[-1]; }
ASMJIT_INLINE_NODEBUG constexpr T* operator->() const noexcept { return &_ptr[-1]; }
template<typename Diff> ASMJIT_INLINE_NODEBUG T& operator[](const Diff& n) noexcept { return *(_ptr - n - 1); }
ASMJIT_INLINE_NODEBUG operator T*() const noexcept { return _ptr; }
//! \}
};
// Support - Array
// ===============

View File

@ -16,43 +16,6 @@ ASMJIT_BEGIN_NAMESPACE
namespace Support {
//! \cond INTERNAL
static ASMJIT_FORCE_INLINE char decode5BitChar(uint32_t c) noexcept {
uint32_t base = c <= 26 ? uint32_t('a') - 1u : uint32_t('0') - 27u;
return char(base + c);
}
static ASMJIT_FORCE_INLINE size_t decodeInstName(char nameOut[32], uint32_t index, const char* stringTable) noexcept {
size_t i;
if (index & 0x80000000u) {
// Small string of 5-bit characters.
for (i = 0; i < 6; i++, index >>= 5) {
uint32_t c = index & 0x1F;
if (c == 0)
break;
nameOut[i] = decode5BitChar(c);
}
return i;
}
else {
size_t prefixBase = index & 0xFFFu;
size_t prefixSize = (index >> 12) & 0xFu;
size_t suffixBase = (index >> 16) & 0xFFFu;
size_t suffixSize = (index >> 28) & 0x7u;
for (i = 0; i < prefixSize; i++)
nameOut[i] = stringTable[prefixBase + i];
char* suffixOut = nameOut + prefixSize;
for (i = 0; i < suffixSize; i++)
suffixOut[i] = stringTable[suffixBase + i];
return prefixSize + suffixSize;
}
}
//! \endcond
} // {Support}

View File

@ -202,9 +202,13 @@ static ASMJIT_INLINE_NODEBUG constexpr bool isInt64(TypeId typeId) noexcept { re
//! Tests whether a given type is a scalar 64-bit integer (unsigned).
static ASMJIT_INLINE_NODEBUG constexpr bool isUInt64(TypeId typeId) noexcept { return typeId == TypeId::kUInt64; }
//! Tests whether a given type is an 8-bit general purpose register representing either signed or unsigned 8-bit integer.
static ASMJIT_INLINE_NODEBUG constexpr bool isGp8(TypeId typeId) noexcept { return isBetween(typeId, TypeId::kInt8, TypeId::kUInt8); }
//! Tests whether a given type is a 16-bit general purpose register representing either signed or unsigned 16-bit integer
static ASMJIT_INLINE_NODEBUG constexpr bool isGp16(TypeId typeId) noexcept { return isBetween(typeId, TypeId::kInt16, TypeId::kUInt16); }
//! Tests whether a given type is a 32-bit general purpose register representing either signed or unsigned 32-bit integer
static ASMJIT_INLINE_NODEBUG constexpr bool isGp32(TypeId typeId) noexcept { return isBetween(typeId, TypeId::kInt32, TypeId::kUInt32); }
//! Tests whether a given type is a 64-bit general purpose register representing either signed or unsigned 64-bit integer
static ASMJIT_INLINE_NODEBUG constexpr bool isGp64(TypeId typeId) noexcept { return isBetween(typeId, TypeId::kInt64, TypeId::kUInt64); }
//! Tests whether a given type is a scalar floating point of any size.
@ -216,21 +220,41 @@ static ASMJIT_INLINE_NODEBUG constexpr bool isFloat64(TypeId typeId) noexcept {
//! Tests whether a given type is a scalar 80-bit float.
static ASMJIT_INLINE_NODEBUG constexpr bool isFloat80(TypeId typeId) noexcept { return typeId == TypeId::kFloat80; }
//! Tests whether a given type is a mask register of any size.
static ASMJIT_INLINE_NODEBUG constexpr bool isMask(TypeId typeId) noexcept { return isBetween(typeId, TypeId::_kMaskStart, TypeId::_kMaskEnd); }
//! Tests whether a given type is an 8-bit mask register.
static ASMJIT_INLINE_NODEBUG constexpr bool isMask8(TypeId typeId) noexcept { return typeId == TypeId::kMask8; }
//! Tests whether a given type is an 16-bit mask register.
static ASMJIT_INLINE_NODEBUG constexpr bool isMask16(TypeId typeId) noexcept { return typeId == TypeId::kMask16; }
//! Tests whether a given type is an 32-bit mask register.
static ASMJIT_INLINE_NODEBUG constexpr bool isMask32(TypeId typeId) noexcept { return typeId == TypeId::kMask32; }
//! Tests whether a given type is an 64-bit mask register.
static ASMJIT_INLINE_NODEBUG constexpr bool isMask64(TypeId typeId) noexcept { return typeId == TypeId::kMask64; }
//! Tests whether a given type is an MMX register.
//!
//! \note MMX functionality is in general deprecated on X86 architecture. AsmJit provides it just for completeness.
static ASMJIT_INLINE_NODEBUG constexpr bool isMmx(TypeId typeId) noexcept { return isBetween(typeId, TypeId::_kMmxStart, TypeId::_kMmxEnd); }
//! Tests whether a given type is an MMX register, which only uses the low 32 bits of data (only specific cases).
//!
//! \note MMX functionality is in general deprecated on X86 architecture. AsmJit provides it just for completeness.
static ASMJIT_INLINE_NODEBUG constexpr bool isMmx32(TypeId typeId) noexcept { return typeId == TypeId::kMmx32; }
//! Tests whether a given type is an MMX register, which uses 64 bits of data (default).
//!
//! \note MMX functionality is in general deprecated on X86 architecture. AsmJit provides it just for completeness.
static ASMJIT_INLINE_NODEBUG constexpr bool isMmx64(TypeId typeId) noexcept { return typeId == TypeId::kMmx64; }
//! Tests whether a given type is a vector register of any size.
static ASMJIT_INLINE_NODEBUG constexpr bool isVec(TypeId typeId) noexcept { return isBetween(typeId, TypeId::_kVec32Start, TypeId::_kVec512End); }
//! Tests whether a given type is a 32-bit or 32-bit view of a vector register.
static ASMJIT_INLINE_NODEBUG constexpr bool isVec32(TypeId typeId) noexcept { return isBetween(typeId, TypeId::_kVec32Start, TypeId::_kVec32End); }
//! Tests whether a given type is a 64-bit or 64-bit view of a vector register.
static ASMJIT_INLINE_NODEBUG constexpr bool isVec64(TypeId typeId) noexcept { return isBetween(typeId, TypeId::_kVec64Start, TypeId::_kVec64End); }
//! Tests whether a given type is a 128-bit or 128-bit view of a vector register.
static ASMJIT_INLINE_NODEBUG constexpr bool isVec128(TypeId typeId) noexcept { return isBetween(typeId, TypeId::_kVec128Start, TypeId::_kVec128End); }
//! Tests whether a given type is a 256-bit or 256-bit view of a vector register.
static ASMJIT_INLINE_NODEBUG constexpr bool isVec256(TypeId typeId) noexcept { return isBetween(typeId, TypeId::_kVec256Start, TypeId::_kVec256End); }
//! Tests whether a given type is a 512-bit or 512-bit view of a vector register.
static ASMJIT_INLINE_NODEBUG constexpr bool isVec512(TypeId typeId) noexcept { return isBetween(typeId, TypeId::_kVec512Start, TypeId::_kVec512End); }
//! \cond

View File

@ -76,8 +76,6 @@
#define MAP_ANONYMOUS MAP_ANON
#endif
#define ASMJIT_ANONYMOUS_MEMORY_USE_FD
// Android NDK doesn't provide `shm_open()` and `shm_unlink()`.
#if !defined(__BIONIC__) && !defined(ASMJIT_NO_SHM_OPEN)
#define ASMJIT_HAS_SHM_OPEN
@ -89,18 +87,60 @@
#define ASMJIT_VM_SHM_DETECT 1
#endif
#if defined(__APPLE__) && TARGET_OS_OSX && ASMJIT_ARCH_ARM >= 64
#if defined(__APPLE__) && TARGET_OS_OSX
#if ASMJIT_ARCH_X86 != 0
#define ASMJIT_ANONYMOUS_MEMORY_USE_MACH_VM_REMAP
#endif
#if ASMJIT_ARCH_ARM >= 64
#define ASMJIT_HAS_PTHREAD_JIT_WRITE_PROTECT_NP
#endif
#endif
#if defined(__APPLE__) && ASMJIT_ARCH_X86 == 0
#define ASMJIT_NO_DUAL_MAPPING
#endif
#if defined(__NetBSD__) && defined(MAP_REMAPDUP) && defined(PROT_MPROTECT)
#undef ASMJIT_ANONYMOUS_MEMORY_USE_FD
#define ASMJIT_ANONYMOUS_MEMORY_USE_REMAPDUP
#endif
#if !defined(ASMJIT_ANONYMOUS_MEMORY_USE_REMAPDUP) && \
!defined(ASMJIT_ANONYMOUS_MEMORY_USE_MACH_VM_REMAP) && \
!defined(ASMJIT_NO_DUAL_MAPPING)
#define ASMJIT_ANONYMOUS_MEMORY_USE_FD
#endif
#endif
#include <atomic>
#if defined(ASMJIT_ANONYMOUS_MEMORY_USE_MACH_VM_REMAP)
#include <mach/mach.h>
#include <mach/mach_time.h>
extern "C" {
#ifdef mig_external
mig_external
#else
extern
#endif
kern_return_t mach_vm_remap(
vm_map_t target_task,
mach_vm_address_t *target_address,
mach_vm_size_t size,
mach_vm_offset_t mask,
int flags,
vm_map_t src_task,
mach_vm_address_t src_address,
boolean_t copy,
vm_prot_t *cur_protection,
vm_prot_t *max_protection,
vm_inherit_t inheritance
);
} // {extern "C"}
#endif
ASMJIT_BEGIN_SUB_NAMESPACE(VirtMem)
// Virtual Memory Utilities
@ -141,6 +181,11 @@ static size_t detectLargePageSize() noexcept {
return ::GetLargePageMinimum();
}
static bool hasDualMappingSupport() noexcept {
// TODO: This assumption works on X86 platforms, this may not work on AArch64.
return true;
}
// Returns windows-specific protectFlags from \ref MemoryFlags.
static DWORD protectFlagsFromMemoryFlags(MemoryFlags memoryFlags) noexcept {
DWORD protectFlags;
@ -165,7 +210,12 @@ static DWORD desiredAccessFromMemoryFlags(MemoryFlags memoryFlags) noexcept {
}
static HardenedRuntimeFlags getHardenedRuntimeFlags() noexcept {
return HardenedRuntimeFlags::kNone;
HardenedRuntimeFlags flags = HardenedRuntimeFlags::kNone;
if (hasDualMappingSupport())
flags |= HardenedRuntimeFlags::kDualMapping;
return flags;
}
Error alloc(void** p, size_t size, MemoryFlags memoryFlags) noexcept {
@ -703,8 +753,8 @@ static bool hasHardenedRuntime() noexcept {
// Detects whether MAP_JIT is available.
static inline bool hasMapJitSupport() noexcept {
#if defined(__APPLE__) && TARGET_OS_OSX && ASMJIT_ARCH_ARM >= 64
// OSX on AArch64 always uses hardened runtime + MAP_JIT:
#if defined(__APPLE__) && TARGET_OS_OSX && ASMJIT_ARCH_X86 == 0
// Apple platforms always use hardened runtime + MAP_JIT on non-x86 hardware:
// - https://developer.apple.com/documentation/apple_silicon/porting_just-in-time_compilers_to_apple_silicon
return true;
#elif defined(__APPLE__) && TARGET_OS_OSX
@ -746,6 +796,14 @@ static inline int mmMapJitFromMemoryFlags(MemoryFlags memoryFlags) noexcept {
#endif
}
static inline bool hasDualMappingSupport() noexcept {
#if defined(ASMJIT_NO_DUAL_MAPPING)
return false;
#else
return true;
#endif
}
static HardenedRuntimeFlags getHardenedRuntimeFlags() noexcept {
HardenedRuntimeFlags flags = HardenedRuntimeFlags::kNone;
@ -755,6 +813,9 @@ static HardenedRuntimeFlags getHardenedRuntimeFlags() noexcept {
if (hasMapJitSupport())
flags |= HardenedRuntimeFlags::kMapJit;
if (hasDualMappingSupport())
flags |= HardenedRuntimeFlags::kDualMapping;
return flags;
}
@ -828,6 +889,7 @@ Error protect(void* p, size_t size, MemoryFlags memoryFlags) noexcept {
// Virtual Memory [Posix] - Dual Mapping
// =====================================
#if !defined(ASMJIT_NO_DUAL_MAPPING)
static Error unmapDualMapping(DualMapping* dm, size_t size) noexcept {
Error err1 = unmapMemory(dm->rx, size);
Error err2 = kErrorOk;
@ -843,6 +905,7 @@ static Error unmapDualMapping(DualMapping* dm, size_t size) noexcept {
dm->rw = nullptr;
return kErrorOk;
}
#endif // !ASMJIT_NO_DUAL_MAPPING
#if defined(ASMJIT_ANONYMOUS_MEMORY_USE_REMAPDUP)
static Error allocDualMappingUsingRemapdup(DualMapping* dmOut, size_t size, MemoryFlags memoryFlags) noexcept {
@ -875,16 +938,105 @@ static Error allocDualMappingUsingRemapdup(DualMapping* dmOut, size_t size, Memo
}
#endif
Error allocDualMapping(DualMapping* dm, size_t size, MemoryFlags memoryFlags) noexcept {
dm->rx = nullptr;
dm->rw = nullptr;
#if defined(ASMJIT_ANONYMOUS_MEMORY_USE_MACH_VM_REMAP)
static Error asmjitErrorFromKernResult(kern_return_t result) noexcept {
switch (result) {
case KERN_PROTECTION_FAILURE:
return DebugUtils::errored(kErrorProtectionFailure);
case KERN_NO_SPACE:
return DebugUtils::errored(kErrorOutOfMemory);
case KERN_INVALID_ARGUMENT:
return DebugUtils::errored(kErrorInvalidArgument);
default:
return DebugUtils::errored(kErrorInvalidState);
}
}
if (off_t(size) <= 0)
return DebugUtils::errored(size == 0 ? kErrorInvalidArgument : kErrorTooLarge);
static Error allocDualMappingUsingMachVmRemap(DualMapping* dmOut, size_t size, MemoryFlags memoryFlags) noexcept {
DualMapping dm {};
#if defined(ASMJIT_ANONYMOUS_MEMORY_USE_REMAPDUP)
return allocDualMappingUsingRemapdup(dm, size, memoryFlags);
#elif defined(ASMJIT_ANONYMOUS_MEMORY_USE_FD)
MemoryFlags mmapFlags = MemoryFlags::kAccessReadWrite | (memoryFlags & MemoryFlags::kMapShared);
ASMJIT_PROPAGATE(mapMemory(&dm.rx, size, mmapFlags));
vm_prot_t curProt;
vm_prot_t maxProt;
int rwProtectFlags = VM_PROT_READ | VM_PROT_WRITE;
int rxProtectFlags = VM_PROT_READ;
if (Support::test(memoryFlags, MemoryFlags::kAccessExecute))
rxProtectFlags |= VM_PROT_EXECUTE;
kern_return_t result {};
do {
vm_map_t task = mach_task_self();
mach_vm_address_t remappedAddr {};
#if defined(VM_FLAGS_RANDOM_ADDR)
int remapFlags = VM_FLAGS_ANYWHERE | VM_FLAGS_RANDOM_ADDR;
#else
int remapFlags = VM_FLAGS_ANYWHERE;
#endif
// Try to remap the existing memory into a different address.
result = mach_vm_remap(
task, // target_task
&remappedAddr, // target_address
size, // size
0, // mask
remapFlags, // flags
task, // src_task
(mach_vm_address_t)dm.rx, // src_address
false, // copy
&curProt, // cur_protection
&maxProt, // max_protection
VM_INHERIT_DEFAULT); // inheritance
if (result != KERN_SUCCESS)
break;
dm.rw = (void*)remappedAddr;
// Now, try to change permissions of both map regions into RW and RX. The vm_protect()
// API is used twice as we also want to set maximum permissions, so nobody would be
// allowed to change the RX region back to RW or RWX (if RWX is allowed).
uint32_t i;
for (i = 0; i < 2; i++) {
bool setMaximum = (i == 0);
result = vm_protect(
task, // target_task
(vm_address_t)dm.rx, // address
size, // size
setMaximum, // set_maximum
rxProtectFlags); // new_protection
if (result != KERN_SUCCESS)
break;
result = vm_protect(task, // target_task
(vm_address_t)dm.rw, // address
size, // size
setMaximum, // set_maximum
rwProtectFlags); // new_protection
if (result != KERN_SUCCESS)
break;
}
} while (0);
if (result != KERN_SUCCESS) {
unmapDualMapping(&dm, size);
return DebugUtils::errored(asmjitErrorFromKernResult(result));
}
*dmOut = dm;
return kErrorOk;
}
#endif // ASMJIT_ANONYMOUS_MEMORY_USE_MACH_VM_REMAP
#if defined(ASMJIT_ANONYMOUS_MEMORY_USE_FD)
static Error allocDualMappingUsingFile(DualMapping* dm, size_t size, MemoryFlags memoryFlags) noexcept {
bool preferTmpOverDevShm = Support::test(memoryFlags, MemoryFlags::kMappingPreferTmp);
if (!preferTmpOverDevShm) {
AnonymousMemoryStrategy strategy;
@ -910,13 +1062,39 @@ Error allocDualMapping(DualMapping* dm, size_t size, MemoryFlags memoryFlags) no
dm->rx = ptr[0];
dm->rw = ptr[1];
return kErrorOk;
}
#endif // ASMJIT_ANONYMOUS_MEMORY_USE_FD
Error allocDualMapping(DualMapping* dm, size_t size, MemoryFlags memoryFlags) noexcept {
dm->rx = nullptr;
dm->rw = nullptr;
#if defined(ASMJIT_NO_DUAL_MAPPING)
DebugUtils::unused(size, memoryFlags);
return DebugUtils::errored(kErrorFeatureNotEnabled);
#else
#error "[asmjit] VirtMem::allocDualMapping() doesn't have implementation for the target OS and compiler"
if (off_t(size) <= 0)
return DebugUtils::errored(size == 0 ? kErrorInvalidArgument : kErrorTooLarge);
#if defined(ASMJIT_ANONYMOUS_MEMORY_USE_REMAPDUP)
return allocDualMappingUsingRemapdup(dm, size, memoryFlags);
#elif defined(ASMJIT_ANONYMOUS_MEMORY_USE_MACH_VM_REMAP)
return allocDualMappingUsingMachVmRemap(dm, size, memoryFlags);
#elif defined(ASMJIT_ANONYMOUS_MEMORY_USE_FD)
return allocDualMappingUsingFile(dm, size, memoryFlags);
#else
#error "[asmjit] VirtMem::allocDualMapping() doesn't have implementation for the target OS or architecture"
#endif
#endif // ASMJIT_NO_DUAL_MAPPING
}
Error releaseDualMapping(DualMapping* dm, size_t size) noexcept {
#if defined(ASMJIT_NO_DUAL_MAPPING)
DebugUtils::unused(dm, size);
return DebugUtils::errored(kErrorFeatureNotEnabled);
#else
return unmapDualMapping(dm, size);
#endif // ASMJIT_NO_DUAL_MAPPING
}
#endif
@ -996,8 +1174,8 @@ void protectJitMemory(ProtectJitAccess access) noexcept {
ASMJIT_END_SUB_NAMESPACE
// JitAllocator - Tests
// ====================
// Virtual Memory - Tests
// ======================
#if defined(ASMJIT_TEST)
ASMJIT_BEGIN_NAMESPACE
@ -1017,8 +1195,9 @@ UNIT(virt_mem) {
INFO("VirtMem::hardenedRuntimeInfo():");
INFO(" flags:");
INFO(" kEnabled: %s", Support::test(hardenedFlags, VirtMem::HardenedRuntimeFlags::kEnabled) ? "true" : "false");
INFO(" kMapJit: %s", Support::test(hardenedFlags, VirtMem::HardenedRuntimeFlags::kMapJit) ? "true" : "false");
INFO(" kEnabled: %s" , Support::test(hardenedFlags, VirtMem::HardenedRuntimeFlags::kEnabled ) ? "true" : "false");
INFO(" kMapJit: %s" , Support::test(hardenedFlags, VirtMem::HardenedRuntimeFlags::kMapJit ) ? "true" : "false");
INFO(" kDualMapping: %s", Support::test(hardenedFlags, VirtMem::HardenedRuntimeFlags::kDualMapping) ? "true" : "false");
}
ASMJIT_END_NAMESPACE

View File

@ -10,6 +10,7 @@
#ifndef ASMJIT_NO_JIT
#include "../core/globals.h"
#include "../core/support.h"
ASMJIT_BEGIN_NAMESPACE
@ -143,7 +144,7 @@ enum class MemoryFlags : uint32_t {
//! Request large memory mapped pages.
//!
//! \important If this option is used and large page(s) cannot be mapped, the allocation will fail. Fallback to
//! \remarks If this option is used and large page(s) cannot be mapped, the allocation will fail. Fallback to
//! regular pages must be done by the user in this case. Higher level API such as \ref JitAllocator provides an
//! additional mechanism to allocate regular page(s) when large page(s) allocation fails.
kMMapLargePages = 0x00000200u,
@ -215,15 +216,31 @@ enum class HardenedRuntimeFlags : uint32_t {
//! architecture.
kEnabled = 0x00000001u,
//! Read+Write+Execute can only be allocated with MAP_JIT flag (Apple specific, only available on OSX).
kMapJit = 0x00000002u
//! Read+Write+Execute can only be allocated with MAP_JIT flag (Apple specific, only available on Apple platforms).
kMapJit = 0x00000002u,
//! Read+Write+Execute can be allocated with dual mapping approach (one region with RW and the other with RX).
kDualMapping = 0x00000004u
};
ASMJIT_DEFINE_ENUM_FLAGS(HardenedRuntimeFlags)
//! Hardened runtime information.
struct HardenedRuntimeInfo {
//! \name Members
//! \{
//! Hardened runtime flags.
HardenedRuntimeFlags flags;
//! \}
//! \name Accessors
//! \{
//! Tests whether the hardened runtime `flag` is set.
ASMJIT_INLINE_NODEBUG bool hasFlag(HardenedRuntimeFlags flag) const noexcept { return Support::test(flags, flag); }
//! \}
};
//! Returns runtime features provided by the OS.
@ -275,7 +292,7 @@ public:
//! \}
//! \name Construction / Destruction
//! \name Construction & Destruction
//! \{
//! Makes the given memory block RW protected.

View File

@ -16,8 +16,8 @@ ASMJIT_BEGIN_NAMESPACE
// and should never be modified.
const Zone::Block Zone::_zeroBlock = { nullptr, nullptr, 0 };
// Zone - Init & Reset
// ===================
// Zone - Initialization & Reset
// =============================
void Zone::_init(size_t blockSize, size_t blockAlignment, const Support::Temporary* temporary) noexcept {
ASMJIT_ASSERT(blockSize >= kMinBlockSize);
@ -215,8 +215,8 @@ static bool ZoneAllocator_hasDynamicBlock(ZoneAllocator* self, ZoneAllocator::Dy
}
#endif
// ZoneAllocator - Init & Reset
// ============================
// ZoneAllocator - Initialization & Reset
// ======================================
void ZoneAllocator::reset(Zone* zone) noexcept {
// Free dynamic blocks.
@ -227,9 +227,9 @@ void ZoneAllocator::reset(Zone* zone) noexcept {
block = next;
}
// Zero the entire class and initialize to the given `zone`.
memset(this, 0, sizeof(*this));
_zone = zone;
memset(_slots, 0, sizeof(_slots));
_dynamicBlocks = nullptr;
}
// asmjit::ZoneAllocator - Alloc & Release

View File

@ -9,8 +9,8 @@
ASMJIT_BEGIN_NAMESPACE
// ZoneStackBase - Init & Reset
// ============================
// ZoneStackBase - Initialization & Reset
// ======================================
Error ZoneStackBase::_init(ZoneAllocator* allocator, size_t middleIndex) noexcept {
ZoneAllocator* oldAllocator = _allocator;

View File

@ -52,7 +52,8 @@ Error ZoneVectorBase::_grow(ZoneAllocator* allocator, uint32_t sizeOfT, uint32_t
Error ZoneVectorBase::_reserve(ZoneAllocator* allocator, uint32_t sizeOfT, uint32_t n) noexcept {
uint32_t oldCapacity = _capacity;
if (oldCapacity >= n) return kErrorOk;
if (oldCapacity >= n)
return kErrorOk;
uint32_t nBytes = n * sizeOfT;
if (ASMJIT_UNLIKELY(nBytes < n))
@ -65,11 +66,10 @@ Error ZoneVectorBase::_reserve(ZoneAllocator* allocator, uint32_t sizeOfT, uint3
return DebugUtils::errored(kErrorOutOfMemory);
void* oldData = _data;
if (_size)
if (oldData && _size) {
memcpy(newData, oldData, size_t(_size) * sizeOfT);
if (oldData)
allocator->release(oldData, size_t(oldCapacity) * sizeOfT);
}
_capacity = uint32_t(allocatedBytes / sizeOfT);
ASMJIT_ASSERT(_capacity >= n);
@ -289,9 +289,22 @@ static void test_zone_vector(ZoneAllocator* allocator, const char* typeName) {
}
EXPECT_FALSE(vec.empty());
EXPECT_EQ(vec.size(), uint32_t(kMax));
EXPECT_EQ(vec.indexOf(T(0)), uint32_t(0));
EXPECT_EQ(vec.indexOf(T(kMax - 1)), uint32_t(kMax - 1));
EXPECT_EQ(vec.begin()[0], 0);
EXPECT_EQ(vec.end()[-1], kMax - 1);
EXPECT_EQ(vec.rbegin()[0], kMax - 1);
EXPECT_EQ(vec.rend()[-1], 0);
int64_t fsum = 0;
int64_t rsum = 0;
for (const T& item : vec) { fsum += item; }
for (auto it = vec.rbegin(); it != vec.rend(); ++it) { rsum += *it; }
EXPECT_EQ(fsum, rsum);
vec.release(allocator);
}

View File

@ -128,8 +128,8 @@ public:
typedef T* iterator;
typedef const T* const_iterator;
typedef std::reverse_iterator<iterator> reverse_iterator;
typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
typedef Support::ArrayReverseIterator<T> reverse_iterator;
typedef Support::ArrayReverseIterator<const T> const_reverse_iterator;
//! \name Construction & Destruction
//! \{
@ -194,8 +194,13 @@ public:
if (ASMJIT_UNLIKELY(_size == _capacity))
ASMJIT_PROPAGATE(grow(allocator, 1));
::memmove(static_cast<T*>(_data) + 1, _data, size_t(_size) * sizeof(T));
memcpy(_data, &item, sizeof(T));
memmove(static_cast<void*>(static_cast<T*>(_data) + 1),
static_cast<const void*>(_data),
size_t(_size) * sizeof(T));
memcpy(static_cast<void*>(_data),
static_cast<const void*>(&item),
sizeof(T));
_size++;
return kErrorOk;
@ -209,10 +214,15 @@ public:
ASMJIT_PROPAGATE(grow(allocator, 1));
T* dst = static_cast<T*>(_data) + index;
::memmove(dst + 1, dst, size_t(_size - index) * sizeof(T));
memcpy(dst, &item, sizeof(T));
_size++;
memmove(static_cast<void*>(dst + 1),
static_cast<const void*>(dst),
size_t(_size - index) * sizeof(T));
memcpy(static_cast<void*>(dst),
static_cast<const void*>(&item),
sizeof(T));
_size++;
return kErrorOk;
}
@ -221,9 +231,11 @@ public:
if (ASMJIT_UNLIKELY(_size == _capacity))
ASMJIT_PROPAGATE(grow(allocator, 1));
memcpy(static_cast<T*>(_data) + _size, &item, sizeof(T));
_size++;
memcpy(static_cast<void*>(static_cast<T*>(_data) + _size),
static_cast<const void*>(&item),
sizeof(T));
_size++;
return kErrorOk;
}
@ -234,7 +246,9 @@ public:
ASMJIT_PROPAGATE(grow(allocator, size));
if (size) {
memcpy(static_cast<T*>(_data) + _size, other._data, size_t(size) * sizeof(T));
memcpy(static_cast<void*>(static_cast<T*>(_data) + _size),
static_cast<const void*>(other._data),
size_t(size) * sizeof(T));
_size += size;
}
@ -249,10 +263,15 @@ public:
ASMJIT_ASSERT(_size < _capacity);
T* data = static_cast<T*>(_data);
if (_size)
::memmove(data + 1, data, size_t(_size) * sizeof(T));
if (_size) {
memmove(static_cast<void*>(data + 1),
static_cast<const void*>(data),
size_t(_size) * sizeof(T));
}
memcpy(data, &item, sizeof(T));
memcpy(static_cast<void*>(data),
static_cast<const void*>(&item),
sizeof(T));
_size++;
}
@ -263,7 +282,9 @@ public:
ASMJIT_FORCE_INLINE void appendUnsafe(const T& item) noexcept {
ASMJIT_ASSERT(_size < _capacity);
memcpy(static_cast<T*>(_data) + _size, &item, sizeof(T));
memcpy(static_cast<void*>(static_cast<T*>(_data) + _size),
static_cast<const void*>(&item),
sizeof(T));
_size++;
}
@ -273,17 +294,26 @@ public:
ASMJIT_ASSERT(index <= _size);
T* dst = static_cast<T*>(_data) + index;
::memmove(dst + 1, dst, size_t(_size - index) * sizeof(T));
memcpy(dst, &item, sizeof(T));
memmove(static_cast<void*>(dst + 1),
static_cast<const void*>(dst),
size_t(_size - index) * sizeof(T));
memcpy(static_cast<void*>(dst),
static_cast<const void*>(&item),
sizeof(T));
_size++;
}
//! Concatenates all items of `other` at the end of the vector.
ASMJIT_FORCE_INLINE void concatUnsafe(const ZoneVector<T>& other) noexcept {
uint32_t size = other._size;
ASMJIT_ASSERT(_capacity - _size >= size);
if (size) {
memcpy(static_cast<T*>(_data) + _size, other._data, size_t(size) * sizeof(T));
memcpy(static_cast<void*>(static_cast<T*>(_data) + _size),
static_cast<const void*>(other._data),
size_t(size) * sizeof(T));
_size += size;
}
}
@ -311,8 +341,11 @@ public:
T* data = static_cast<T*>(_data) + i;
size_t size = --_size - i;
if (size)
::memmove(data, data + 1, size_t(size) * sizeof(T));
if (size) {
memmove(static_cast<void*>(data),
static_cast<const void*>(data + 1),
size_t(size) * sizeof(T));
}
}
//! Pops the last element from the vector and returns it.
@ -460,8 +493,8 @@ public:
//! \name Overloaded Operators
//! \{
ASMJIT_INLINE_NODEBUG bool operator==(const ZoneBitVector& other) const noexcept { return eq(other); }
ASMJIT_INLINE_NODEBUG bool operator!=(const ZoneBitVector& other) const noexcept { return !eq(other); }
ASMJIT_INLINE_NODEBUG bool operator==(const ZoneBitVector& other) const noexcept { return equals(other); }
ASMJIT_INLINE_NODEBUG bool operator!=(const ZoneBitVector& other) const noexcept { return !equals(other); }
//! \}
@ -628,7 +661,7 @@ public:
_data[idx] &= (BitWord(1) << bit) - 1u;
}
ASMJIT_FORCE_INLINE bool eq(const ZoneBitVector& other) const noexcept {
ASMJIT_FORCE_INLINE bool equals(const ZoneBitVector& other) const noexcept {
if (_size != other._size)
return false;
@ -642,6 +675,11 @@ public:
return true;
}
#if !defined(ASMJIT_NO_DEPRECATED)
ASMJIT_DEPRECATED("Use ZoneVector::equals() instead")
ASMJIT_FORCE_INLINE bool eq(const ZoneBitVector& other) const noexcept { return equals(other); }
#endif // !ASMJIT_NO_DEPRECATED
//! \}
//! \name Memory Management

View File

@ -345,6 +345,10 @@ static ASMJIT_FORCE_INLINE uint32_t x86AltOpcodeOf(const InstDB::InstInfo* info)
return InstDB::_altOpcodeTable[info->_altOpcodeIndex];
}
static ASMJIT_FORCE_INLINE bool x86IsMmxOrXmm(const Reg& reg) noexcept {
return reg.type() == RegType::kX86_Mm || reg.type() == RegType::kX86_Xmm;
}
// x86::Assembler - X86BufferWriter
// ================================
@ -529,8 +533,6 @@ static ASMJIT_FORCE_INLINE bool x86ShouldUseMovabs(Assembler* self, X86BufferWri
Assembler::Assembler(CodeHolder* code) noexcept : BaseAssembler() {
_archMask = (uint64_t(1) << uint32_t(Arch::kX86)) |
(uint64_t(1) << uint32_t(Arch::kX64)) ;
assignEmitterFuncs(this);
if (code)
code->attach(this);
}
@ -560,7 +562,7 @@ ASMJIT_FAVOR_SPEED Error Assembler::_emit(InstId instId, const Operand_& o0, con
const Operand_* rmRel; // Memory operand or operand that holds Label|Imm.
uint32_t rmInfo; // Memory operand's info based on x86MemInfo.
uint32_t rbReg; // Memory base or modRM register.
uint32_t rbReg = 0; // Memory base or modRM register.
uint32_t rxReg; // Memory index register.
uint32_t opReg; // ModR/M opcode or register id.
@ -610,7 +612,7 @@ ASMJIT_FAVOR_SPEED Error Assembler::_emit(InstId instId, const Operand_& o0, con
Operand_ opArray[Globals::kMaxOpCount];
EmitterUtils::opArrayFromEmitArgs(opArray, o0, o1, o2, opExt);
err = _funcs.validate(arch(), BaseInst(instId, options, _extraReg), opArray, Globals::kMaxOpCount, ValidationFlags::kNone);
err = _funcs.validate(BaseInst(instId, options, _extraReg), opArray, Globals::kMaxOpCount, ValidationFlags::kNone);
if (ASMJIT_UNLIKELY(err))
goto Failed;
}
@ -653,7 +655,6 @@ ASMJIT_FAVOR_SPEED Error Assembler::_emit(InstId instId, const Operand_& o0, con
// This sequence seems to be the fastest.
opcode = InstDB::_mainOpcodeTable[instInfo->_mainOpcodeIndex];
opReg = opcode.extractModO();
rbReg = 0;
opcode |= instInfo->_mainOpcodeValue;
// Encoding Scope
@ -991,13 +992,13 @@ CaseX86M_GPB_MulDiv:
goto EmitX86R;
// MOD/RM: Alternative encoding selected via instruction options.
opcode += 2;
opcode += 2u;
std::swap(opReg, rbReg);
goto EmitX86R;
}
if (isign3 == ENC_OPS2(Reg, Mem)) {
opcode += 2;
opcode += 2u;
opcode.addArithBySize(o0.x86RmSize());
opReg = o0.id();
@ -1075,7 +1076,7 @@ CaseX86M_GPB_MulDiv:
goto EmitX86Op;
}
opcode += size != 1 ? (immSize != 1 ? 1 : 3) : 0;
opcode += size != 1 ? (immSize != 1 ? 1u : 3u) : 0u;
goto EmitX86R;
}
@ -1095,7 +1096,7 @@ CaseX86M_GPB_MulDiv:
if (Support::isInt8(immValue) && !Support::test(options, InstOptions::kLongForm))
immSize = 1;
opcode += memSize != 1 ? (immSize != 1 ? 1 : 3) : 0;
opcode += memSize != 1 ? (immSize != 1 ? 1u : 3u) : 0u;
opcode.addPrefixBySize(memSize);
rmRel = &o0;
@ -1251,7 +1252,7 @@ CaseX86M_GPB_MulDiv:
// This seems to be the only exception of encoding '66F2' prefix.
if (o1.x86RmSize() == 2) writer.emit8(0x66);
opcode += o1.x86RmSize() != 1;
opcode += uint32_t(o1.x86RmSize() != 1u);
goto EmitX86M;
}
break;
@ -1379,7 +1380,7 @@ CaseX86M_GPB_MulDiv:
if (ASMJIT_UNLIKELY(o0.id() != Gp::kIdAx || o1.id() != Gp::kIdDx))
goto InvalidInstruction;
opcode += o0.x86RmSize() != 1;
opcode += uint32_t(o0.x86RmSize() != 1u);
opcode.add66hBySize(o0.x86RmSize());
goto EmitX86Op;
}
@ -1395,7 +1396,7 @@ CaseX86M_GPB_MulDiv:
goto AmbiguousOperandSize;
rmRel = &o0;
opcode += (size != 1);
opcode += uint32_t(size != 1u);
opcode.add66hBySize(size);
goto EmitX86OpImplicitMem;
@ -1552,7 +1553,7 @@ CaseX86M_GPB_MulDiv:
if (!Support::test(options, InstOptions::kX86_ModRM))
goto EmitX86R;
opcode += 2;
opcode += 2u;
std::swap(opReg, rbReg);
goto EmitX86R;
}
@ -1563,7 +1564,7 @@ CaseX86M_GPB_MulDiv:
if (!Support::test(options, InstOptions::kX86_ModRM))
goto EmitX86R;
opcode += 2;
opcode += 2u;
std::swap(opReg, rbReg);
goto EmitX86R;
}
@ -1652,7 +1653,7 @@ CaseX86M_GPB_MulDiv:
// Handle a special form of `mov al|ax|eax|rax, [ptr64]` that doesn't use MOD.
if (opReg == Gp::kIdAx && !rmRel->as<Mem>().hasBaseOrIndex()) {
if (x86ShouldUseMovabs(this, writer, o0.x86RmSize(), options, rmRel->as<Mem>())) {
opcode += 0xA0;
opcode += 0xA0u;
immValue = rmRel->as<Mem>().offset();
goto EmitX86OpMovAbs;
}
@ -1661,7 +1662,7 @@ CaseX86M_GPB_MulDiv:
if (o0.x86RmSize() == 1)
FIXUP_GPB(o0, opReg);
opcode += 0x8A;
opcode += 0x8Au;
goto EmitX86M;
}
}
@ -1685,7 +1686,7 @@ CaseX86M_GPB_MulDiv:
// Handle a special form of `mov [ptr64], al|ax|eax|rax` that doesn't use MOD.
if (opReg == Gp::kIdAx && !rmRel->as<Mem>().hasBaseOrIndex()) {
if (x86ShouldUseMovabs(this, writer, o1.x86RmSize(), options, rmRel->as<Mem>())) {
opcode += 0xA2;
opcode += 0xA2u;
immValue = rmRel->as<Mem>().offset();
goto EmitX86OpMovAbs;
}
@ -1694,7 +1695,7 @@ CaseX86M_GPB_MulDiv:
if (o1.x86RmSize() == 1)
FIXUP_GPB(o1, opReg);
opcode += 0x88;
opcode += 0x88u;
goto EmitX86M;
}
}
@ -1995,7 +1996,7 @@ CaseX86PushPop_Gp:
if (ASMJIT_UNLIKELY(o1.id() != Gp::kIdCx))
goto InvalidInstruction;
opcode += 2;
opcode += 2u;
goto EmitX86R;
}
@ -2020,7 +2021,7 @@ CaseX86PushPop_Gp:
if (ASMJIT_UNLIKELY(o1.id() != Gp::kIdCx))
goto InvalidInstruction;
opcode += 2;
opcode += 2u;
rmRel = &o0;
goto EmitX86M;
}
@ -2390,7 +2391,7 @@ CaseFpuArith_Mem:
}
if (o0.x86RmSize() == 8 && commonInfo->hasFlag(InstDB::InstFlags::kFpuM64)) {
opcode += 4;
opcode += 4u;
goto EmitX86M;
}
@ -2415,7 +2416,7 @@ CaseFpuArith_Mem:
rmRel = &o0;
if (o0.x86RmSize() == 2 && commonInfo->hasFlag(InstDB::InstFlags::kFpuM16)) {
opcode += 4;
opcode += 4u;
goto EmitX86M;
}
@ -2433,7 +2434,7 @@ CaseFpuArith_Mem:
case InstDB::kEncodingFpuRDef:
if (isign3 == 0) {
opcode += 1;
opcode += 1u;
goto EmitFpuOp;
}
ASMJIT_FALLTHROUGH;
@ -2575,6 +2576,7 @@ CaseFpuArith_Mem:
case InstDB::kEncodingExtMovd:
CaseExtMovd:
if (x86IsMmxOrXmm(o0.as<Reg>())) {
opReg = o0.id();
opcode.add66hIf(Reg::isXmm(o0));
@ -2589,8 +2591,10 @@ CaseExtMovd:
rmRel = &o1;
goto EmitX86M;
}
}
// The following instructions use the secondary opcode.
if (x86IsMmxOrXmm(o1.as<Reg>())) {
opcode &= Opcode::kW;
opcode |= x86AltOpcodeOf(instInfo);
opReg = o1.id();
@ -2607,6 +2611,7 @@ CaseExtMovd:
rmRel = &o0;
goto EmitX86M;
}
}
break;
case InstDB::kEncodingExtMovq:
@ -2621,7 +2626,7 @@ CaseExtMovd:
if (!Support::test(options, InstOptions::kX86_ModMR))
goto EmitX86R;
opcode += 0x10;
opcode += 0x10u;
std::swap(opReg, rbReg);
goto EmitX86R;
}
@ -5089,6 +5094,9 @@ Error Assembler::onAttach(CodeHolder* code) noexcept {
Arch arch = code->arch();
ASMJIT_PROPAGATE(Base::onAttach(code));
_instructionAlignment = uint8_t(1);
assignEmitterFuncs(this);
if (Environment::is32Bit(arch)) {
// 32 bit architecture - X86.
_forcedInstOptions |= InstOptions::kX86_InvalidRex;

View File

@ -151,7 +151,7 @@ ASMJIT_BEGIN_SUB_NAMESPACE(x86)
//! printf("Status: %s\n", DebugUtils::errorAsString(err));
//!
//! // Ambiguous operand size - the pointer requires size.
//! err = a.inc(x86::ptr(x86::rax), 1);
//! err = a.inc(x86::ptr(x86::rax));
//! printf("Status: %s\n", DebugUtils::errorAsString(err));
//!
//! return 0;
@ -230,6 +230,9 @@ ASMJIT_BEGIN_SUB_NAMESPACE(x86)
//! targets easily. If you want to create a register of native size dynamically by specifying its id it's also possible:
//!
//! ```
//! #include <asmjit/x86.h>
//! using namespace asmjit;
//!
//! void example(x86::Assembler& a) {
//! x86::Gp zax = a.gpz(x86::Gp::kIdAx);
//! x86::Gp zbx = a.gpz(x86::Gp::kIdBx);
@ -360,18 +363,19 @@ ASMJIT_BEGIN_SUB_NAMESPACE(x86)
//! x86::Gp src_a = a.zcx();
//! x86::Gp src_b = a.zdx();
//!
//! X86::Xmm vec0 = x86::xmm0;
//! X86::Xmm vec1 = x86::xmm1;
//! x86::Xmm vec0 = x86::xmm0;
//! x86::Xmm vec1 = x86::xmm1;
//!
//! // Create/initialize FuncDetail and FuncFrame.
//! FuncDetail func;
//! func.init(FuncSignatureT<void, int*, const int*, const int*>(CallConvId::kHost));
//! func.init(FuncSignature::build<void, int*, const int*, const int*>(),
//! rt.environment());
//!
//! FuncFrame frame;
//! frame.init(func);
//!
//! // Make XMM0 and XMM1 dirty - RegGroup::kVec describes XMM|YMM|ZMM registers.
//! frame.setDirtyRegs(RegGroup::kVec, IntUtils::mask(0, 1));
//! frame.setDirtyRegs(RegGroup::kVec, Support::bitMask(0, 1));
//!
//! // Alternatively, if you don't want to use register masks you can pass BaseReg
//! // to addDirtyRegs(). The following code would add both xmm0 and xmm1.
@ -379,7 +383,7 @@ ASMJIT_BEGIN_SUB_NAMESPACE(x86)
//!
//! FuncArgsAssignment args(&func); // Create arguments assignment context.
//! args.assignAll(dst, src_a, src_b);// Assign our registers to arguments.
//! args.updateFrameInfo(frame); // Reflect our args in FuncFrame.
//! args.updateFuncFrame(frame); // Reflect our args in FuncFrame.
//! frame.finalize(); // Finalize the FuncFrame (updates it).
//!
//! a.emitProlog(frame); // Emit function prolog.
@ -537,16 +541,16 @@ ASMJIT_BEGIN_SUB_NAMESPACE(x86)
//!
//! void prefixesExample(x86::Assembler& a) {
//! // Lock prefix for implementing atomics:
//! // lock add dword ptr [dst], 1
//! a.lock().add(x86::dword_ptr(dst), 1);
//! // lock add dword ptr [rdi], 1
//! a.lock().add(x86::dword_ptr(x86::rdi), 1);
//!
//! // Similarly, XAcquire/XRelease prefixes are also available:
//! // xacquire add dword ptr [dst], 1
//! a.xacquire().add(x86::dword_ptr(dst), 1);
//! // xacquire add dword ptr [rdi], 1
//! a.xacquire().add(x86::dword_ptr(x86::rdi), 1);
//!
//! // Rep prefix (see also repe/repz and repne/repnz):
//! // rep movs byte ptr [dst], byte ptr [src]
//! a.rep().movs(x86::byte_ptr(dst), x86::byte_ptr(src));
//! // rep movs byte ptr [rdi], byte ptr [rsi]
//! a.rep().movs(x86::byte_ptr(x86::rdi), x86::byte_ptr(x86::rsi));
//!
//! // Forcing REX prefix in 64-bit mode.
//! // rex mov eax, 1
@ -610,10 +614,10 @@ ASMJIT_BEGIN_SUB_NAMESPACE(x86)
//! // -----------------
//! //
//! // - Broadcast data is part of memory operand.
//! // - Use x86::Mem::_1toN(), which returns a new x86::Mem operand.
//! // - Use x86::Mem::_1to2(), x86::Mem::_1to4(), etc..., which returns a new x86::Mem operand with broadcast.
//!
//! // vaddpd zmm0 {k1} {z}, zmm1, [rcx] {1to8}
//! a.k(k1).z().vaddpd(zmm0, zmm1, x86::mem(rcx)._1to8());
//! a.k(k1).z().vaddpd(zmm0, zmm1, x86::ptr(rcx)._1to8());
//!
//! // Embedded Rounding & Suppress-All-Exceptions
//! // -------------------------------------------
@ -659,6 +663,7 @@ public:
//! \}
//! \endcond
//! \cond INTERNAL
//! \name Emit
//! \{

View File

@ -18,8 +18,6 @@ ASMJIT_BEGIN_SUB_NAMESPACE(x86)
Builder::Builder(CodeHolder* code) noexcept : BaseBuilder() {
_archMask = (uint64_t(1) << uint32_t(Arch::kX86)) |
(uint64_t(1) << uint32_t(Arch::kX64)) ;
assignEmitterFuncs(this);
if (code)
code->attach(this);
}
@ -29,7 +27,12 @@ Builder::~Builder() noexcept {}
// =====================
Error Builder::onAttach(CodeHolder* code) noexcept {
return Base::onAttach(code);
ASMJIT_PROPAGATE(Base::onAttach(code));
_instructionAlignment = uint8_t(1);
assignEmitterFuncs(this);
return kErrorOk;
}
Error Builder::onDetach(CodeHolder* code) noexcept {

Some files were not shown because too many files have changed in this diff Show More