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

View File

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

View File

@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.5 FATAL_ERROR) cmake_minimum_required(VERSION 3.8 FATAL_ERROR)
cmake_policy(PUSH) cmake_policy(PUSH)
@ -33,15 +33,13 @@ if (DEFINED ASMJIT_BUILD_STATIC)
set(ASMJIT_STATIC "${ASMJIT_BUILD_STATIC}") set(ASMJIT_STATIC "${ASMJIT_BUILD_STATIC}")
endif() endif()
# AsmJit - Configuration # AsmJit - Configuration - Build
# ====================== # ==============================
# AsmJit testing.
if (NOT DEFINED ASMJIT_TEST) if (NOT DEFINED ASMJIT_TEST)
set(ASMJIT_TEST FALSE) set(ASMJIT_TEST FALSE)
endif() endif()
# AsmJit build options
if (NOT DEFINED ASMJIT_EMBED) if (NOT DEFINED ASMJIT_EMBED)
set(ASMJIT_EMBED FALSE) set(ASMJIT_EMBED FALSE)
endif() endif()
@ -54,15 +52,22 @@ if (NOT DEFINED ASMJIT_SANITIZE)
set(ASMJIT_SANITIZE FALSE) set(ASMJIT_SANITIZE FALSE)
endif() endif()
if (NOT DEFINED ASMJIT_NO_NATVIS)
set(ASMJIT_NO_NATVIS FALSE)
endif()
if (NOT DEFINED ASMJIT_NO_CUSTOM_FLAGS) if (NOT DEFINED ASMJIT_NO_CUSTOM_FLAGS)
set(ASMJIT_NO_CUSTOM_FLAGS FALSE) set(ASMJIT_NO_CUSTOM_FLAGS FALSE)
endif() 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) if (NOT DEFINED ASMJIT_NO_X86)
set(ASMJIT_NO_X86 FALSE) set(ASMJIT_NO_X86 FALSE)
endif() endif()
@ -75,7 +80,9 @@ if (NOT DEFINED ASMJIT_NO_FOREIGN)
set(ASMJIT_NO_FOREIGN FALSE) set(ASMJIT_NO_FOREIGN FALSE)
endif() endif()
# AsmJit features selection. # AsmJit - Configuration - Features
# =================================
if (NOT DEFINED ASMJIT_NO_DEPRECATED) if (NOT DEFINED ASMJIT_NO_DEPRECATED)
set(ASMJIT_NO_DEPRECATED FALSE) set(ASMJIT_NO_DEPRECATED FALSE)
endif() endif()
@ -89,19 +96,19 @@ if (NOT DEFINED ASMJIT_NO_JIT)
endif() endif()
if (NOT DEFINED ASMJIT_NO_TEXT) if (NOT DEFINED ASMJIT_NO_TEXT)
set(ASMJIT_NO_TEXT ${ASMJIT_NO_TEXT}) set(ASMJIT_NO_TEXT FALSE)
endif() endif()
if (NOT DEFINED ASMJIT_NO_LOGGING) if (NOT DEFINED ASMJIT_NO_LOGGING)
set(ASMJIT_NO_LOGGING FALSE) set(ASMJIT_NO_LOGGING ${ASMJIT_NO_TEXT})
endif() endif()
if (NOT DEFINED ASMJIT_NO_VALIDATION) if (NOT DEFINED ASMJIT_NO_VALIDATION)
set(ASMJIT_NO_VALIDATION ${ASMJIT_NO_VALIDATION}) set(ASMJIT_NO_VALIDATION FALSE)
endif() endif()
if (NOT DEFINED ASMJIT_NO_INTROSPECTION) if (NOT DEFINED ASMJIT_NO_INTROSPECTION)
set(ASMJIT_NO_INTROSPECTION ${ASMJIT_NO_INTROSPECTION}) set(ASMJIT_NO_INTROSPECTION FALSE)
endif() endif()
if (NOT DEFINED ASMJIT_NO_BUILDER) if (NOT DEFINED ASMJIT_NO_BUILDER)
@ -109,16 +116,17 @@ if (NOT DEFINED ASMJIT_NO_BUILDER)
endif() endif()
if (NOT DEFINED ASMJIT_NO_COMPILER) 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() endif()
# EMBED implies STATIC. # AsmJit - Configuration - CMake Introspection
if (ASMJIT_EMBED AND NOT ASMJIT_STATIC) # ============================================
set(ASMJIT_STATIC TRUE)
endif()
set(ASMJIT_DIR "${CMAKE_CURRENT_LIST_DIR}" CACHE PATH "Location of 'asmjit'") 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_TEST "${ASMJIT_TEST}" CACHE BOOL "Build 'asmjit' test applications")
set(ASMJIT_EMBED "${ASMJIT_EMBED}" CACHE BOOL "Embed 'asmjit' library (no targets)") set(ASMJIT_EMBED "${ASMJIT_EMBED}" CACHE BOOL "Embed 'asmjit' library (no targets)")
set(ASMJIT_STATIC "${ASMJIT_STATIC}" CACHE BOOL "Build 'asmjit' library as static") 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}") set_property(TARGET ${target} APPEND_STRING PROPERTY LINK_FLAGS " ${link_flag}")
endforeach() 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) target_compile_features(${target} PUBLIC cxx_std_11)
endif()
set_property(TARGET ${target} PROPERTY CXX_EXTENSIONS NO) set_property(TARGET ${target} PROPERTY CXX_EXTENSIONS NO)
set_property(TARGET ${target} PROPERTY CXX_VISIBILITY_PRESET hidden) 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}>) 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 # 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. # We will have to keep this most likely forever as some users may still be using it.
set(ASMJIT_INCLUDE_DIR "${ASMJIT_INCLUDE_DIRS}") set(ASMJIT_INCLUDE_DIR "${ASMJIT_INCLUDE_DIRS}")
@ -282,7 +275,7 @@ endif()
# Support for sanitizers. # Support for sanitizers.
if (ASMJIT_SANITIZE) if (ASMJIT_SANITIZE)
ASMJIT_detect_sanitizers(ASMJIT_SANITIZE_CFLAGS ${ASMJIT_SANITIZE}) asmjit_detect_sanitizers(ASMJIT_SANITIZE_CFLAGS ${ASMJIT_SANITIZE})
if (ASMJIT_SANITIZE_CFLAGS) if (ASMJIT_SANITIZE_CFLAGS)
message("-- Enabling sanitizers: '${ASMJIT_SANITIZE_CFLAGS}'") message("-- Enabling sanitizers: '${ASMJIT_SANITIZE_CFLAGS}'")
@ -363,21 +356,21 @@ else()
set(ASMJIT_TARGET_TYPE "SHARED") set(ASMJIT_TARGET_TYPE "SHARED")
endif() endif()
foreach(build_option ASMJIT_STATIC foreach(build_option # AsmJit build options.
ASMJIT_STATIC
ASMJIT_NO_DEPRECATED
# AsmJit backends selection. # AsmJit backends selection.
ASMJIT_NO_X86 ASMJIT_NO_X86
ASMJIT_NO_AARCH64 ASMJIT_NO_AARCH64
ASMJIT_NO_FOREIGN ASMJIT_NO_FOREIGN
# AsmJit features selection. # AsmJit features selection.
ASMJIT_NO_DEPRECATED
ASMJIT_NO_SHM_OPEN
ASMJIT_NO_JIT ASMJIT_NO_JIT
ASMJIT_NO_TEXT ASMJIT_NO_TEXT
ASMJIT_NO_LOGGING ASMJIT_NO_LOGGING
ASMJIT_NO_BUILDER ASMJIT_NO_INTROSPECTION
ASMJIT_NO_COMPILER
ASMJIT_NO_VALIDATION ASMJIT_NO_VALIDATION
ASMJIT_NO_INTROSPECTION) ASMJIT_NO_BUILDER
ASMJIT_NO_COMPILER)
if (${build_option}) if (${build_option})
List(APPEND ASMJIT_CFLAGS "-D${build_option}") List(APPEND ASMJIT_CFLAGS "-D${build_option}")
List(APPEND ASMJIT_PRIVATE_CFLAGS "-D${build_option}") List(APPEND ASMJIT_PRIVATE_CFLAGS "-D${build_option}")
@ -443,6 +436,8 @@ set(ASMJIT_SRC_LIST
asmjit/core/globals.h asmjit/core/globals.h
asmjit/core/inst.cpp asmjit/core/inst.cpp
asmjit/core/inst.h asmjit/core/inst.h
asmjit/core/instdb.cpp
asmjit/core/instdb_p.h
asmjit/core/jitallocator.cpp asmjit/core/jitallocator.cpp
asmjit/core/jitallocator.h asmjit/core/jitallocator.h
asmjit/core/jitruntime.cpp asmjit/core/jitruntime.cpp
@ -562,9 +557,7 @@ foreach(src_file ${ASMJIT_SRC_LIST})
endif() endif()
endforeach() endforeach()
if (NOT ${CMAKE_VERSION} VERSION_LESS "3.8.0")
source_group(TREE "${ASMJIT_DIR}" FILES ${ASMJIT_SRC}) source_group(TREE "${ASMJIT_DIR}" FILES ${ASMJIT_SRC})
endif()
# AsmJit - Summary # AsmJit - Summary
# ================ # ================
@ -597,7 +590,7 @@ if (NOT ASMJIT_EMBED)
$<BUILD_INTERFACE:${ASMJIT_INCLUDE_DIRS}> $<BUILD_INTERFACE:${ASMJIT_INCLUDE_DIRS}>
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>) $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>)
# Add blend2d::blend2d alias. # Add asmjit::asmjit alias.
add_library(asmjit::asmjit ALIAS asmjit) add_library(asmjit::asmjit ALIAS asmjit)
# TODO: [CMAKE] Deprecated alias - we use projectname::libraryname convention now. # TODO: [CMAKE] Deprecated alias - we use projectname::libraryname convention now.
add_library(AsmJit::AsmJit ALIAS asmjit) add_library(AsmJit::AsmJit ALIAS asmjit)
@ -662,6 +655,7 @@ if (NOT ASMJIT_EMBED)
CFLAGS_REL ${ASMJIT_PRIVATE_CFLAGS_REL}) CFLAGS_REL ${ASMJIT_PRIVATE_CFLAGS_REL})
foreach(_target asmjit_test_emitters foreach(_target asmjit_test_emitters
asmjit_test_execute
asmjit_test_x86_sections) asmjit_test_x86_sections)
asmjit_add_target(${_target} TEST asmjit_add_target(${_target} TEST
SOURCES test/${_target}.cpp SOURCES test/${_target}.cpp
@ -682,11 +676,29 @@ if (NOT ASMJIT_EMBED)
if (NOT (ASMJIT_NO_BUILDER OR ASMJIT_NO_COMPILER)) 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). # 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") 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() else()
asmjit_detect_cflags(sse2_flags "-msse2") asmjit_detect_cflags(ASMJIT_SSE2_CFLAGS "-msse2")
endif()
endif() endif()
asmjit_add_target(asmjit_test_compiler TEST asmjit_add_target(asmjit_test_compiler TEST
SOURCES test/asmjit_test_compiler.cpp SOURCES test/asmjit_test_compiler.cpp
@ -694,7 +706,7 @@ if (NOT ASMJIT_EMBED)
test/asmjit_test_compiler_a64.cpp test/asmjit_test_compiler_a64.cpp
test/asmjit_test_compiler_x86.cpp test/asmjit_test_compiler_x86.cpp
LIBRARIES asmjit::asmjit LIBRARIES asmjit::asmjit
CFLAGS ${ASMJIT_PRIVATE_CFLAGS} ${sse2_flags} CFLAGS ${ASMJIT_PRIVATE_CFLAGS} ${ASMJIT_SSE2_CFLAGS}
CFLAGS_DBG ${ASMJIT_PRIVATE_CFLAGS_DBG} CFLAGS_DBG ${ASMJIT_PRIVATE_CFLAGS_DBG}
CFLAGS_REL ${ASMJIT_PRIVATE_CFLAGS_REL}) CFLAGS_REL ${ASMJIT_PRIVATE_CFLAGS_REL})
endif() 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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages 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 Home Page (asmjit.com)](https://asmjit.com)
* [Official Repository (asmjit/asmjit)](https://github.com/asmjit/asmjit) * [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) * [Zlib License](./LICENSE.md)
See [asmjit.com](https://asmjit.com) page for more details, examples, and documentation. 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) * [Documentation Index](https://asmjit.com/doc/index.html)
* [Build Instructions](https://asmjit.com/doc/group__asmjit__build.html) * [Build Instructions](https://asmjit.com/doc/group__asmjit__build.html)
Contributing
------------
* See [CONTRIBUTING](./CONTRIBUTING.md) page for more details
Breaking Changes Breaking Changes
---------------- ----------------
Breaking the API is sometimes inevitable, what to do? 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: * 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_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_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_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_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. * [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 Organization
-------------------- --------------------
* **`/`** - Project root. * **`/`** - Project root
* **src** - Source code. * **src** - Source code
* **asmjit** - Source code and headers (always point include path in here). * **asmjit** - Source code and headers (always point include path in here)
* **core** - Core API, backend independent except relocations. * **core** - Core API, backend independent except relocations
* **arm** - ARM specific API, used only by ARM and AArch64 backends. * **arm** - ARM specific API, used only by ARM and AArch64 backends
* **x86** - X86 specific API, used only by X86 and X64 backends. * **x86** - X86 specific API, used only by X86 and X64 backends
* **test** - Unit and integration tests (don't embed in your project). * **test** - Unit and integration tests (don't embed in your project)
* **tools** - Tools used for configuring, documenting, and generating files. * **tools** - Tools used for configuring, documenting, and generating files
TODO Ports
---- -----
* [ ] Ports: * [ ] 32-bit ARM/Thumb port (work in progress)
* [ ] 32-bit ARM/Thumb port. * [ ] RISC-V port (not in progress, help welcome)
* [ ] 64-bit ARM (AArch64) port.
* [ ] RISC-V port.
Support 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 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: AsmJit database provides the following ISAs:
* `isa_x86.json` - provides X86 instruction data (both 32-bit and 64-bit) * `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_aarch32.json` - provides AArch32 instruction data (A32/T16/T32 encoding)
* `isa_a64.json` - provides AArch64 instruction data * `isa_aarch64.json` - provides AArch64 instruction data (A64 encoding)
* `isa_aarch64_sme.json` - provides AArch64 SME instruction data (work-in-progress)
To Be Documented To Be Documented
---------------- ----------------
This project will be refactored and documented in the future. 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> // This file is part of AsmJit project <https://asmjit.com>
// //
// See asmjit.h or LICENSE.md for license and copyright information // See asmjit.h or LICENSE.md for license and copyright information
// SPDX-License-Identifier: Zlib // SPDX-License-Identifier: (Zlib or Unlicense)
(function($scope, $as) { (function($scope, $as) {
"use strict"; "use strict";
@ -29,14 +28,14 @@ const arm = $scope[$as] = dict();
// Database // 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. // 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 // 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 = { const FieldInfo = {
"P" : { "bits": 1 }, "P" : { "bits": 1 },
"U" : { "bits": 1 }, "U" : { "bits": 1 },
@ -56,6 +55,7 @@ const FieldInfo = {
"cmode" : { "bits": 4 }, "cmode" : { "bits": 4 },
"Cn" : { "bits": 4 }, "Cn" : { "bits": 4 },
"Cm" : { "bits": 4 }, "Cm" : { "bits": 4 },
"Rd" : { "bits": 4, "read": false, "write": true }, "Rd" : { "bits": 4, "read": false, "write": true },
"Rd2" : { "bits": 4, "read": false, "write": true }, "Rd2" : { "bits": 4, "read": false, "write": true },
"RdLo" : { "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 }, "Rs" : { "bits": 4, "read": true , "write": false },
"Rs2" : { "bits": 4, "read": true , "write": false }, "Rs2" : { "bits": 4, "read": true , "write": false },
"RsList": { "bits": 4, "read": true , "write": false , "list": true }, "RsList": { "bits": 4, "read": true , "write": false , "list": true },
"Sd" : { "bits": 4, "read": false, "write": true }, "Sd" : { "bits": 4, "read": false, "write": true },
"Sd2" : { "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 }, "Sx" : { "bits": 4, "read": true , "write": true },
"Sn" : { "bits": 4, "read": true , "write": false }, "Sn" : { "bits": 4, "read": true , "write": false },
"Sm" : { "bits": 4, "read": true , "write": false }, "Sm" : { "bits": 4, "read": true , "write": false },
"Ss" : { "bits": 4, "read": true , "write": false }, "Ss" : { "bits": 4, "read": true , "write": false },
"Ss2" : { "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 }, "Dd" : { "bits": 4, "read": false, "write": true },
"Dd2" : { "bits": 4, "read": false, "write": true }, "Dd2" : { "bits": 4, "read": false, "write": true },
"Dd3" : { "bits": 4, "read": false, "write": true }, "Dd3" : { "bits": 4, "read": false, "write": true },
"Dd4" : { "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 }, "Dx" : { "bits": 4, "read": true , "write": true },
"Dx2" : { "bits": 4, "read": true , "write": true }, "Dx2" : { "bits": 4, "read": true , "write": true },
"Dn" : { "bits": 4, "read": true , "write": false }, "Dn" : { "bits": 4, "read": true , "write": false },
@ -92,18 +97,18 @@ const FieldInfo = {
"Ds2" : { "bits": 4, "read": true , "write": false }, "Ds2" : { "bits": 4, "read": true , "write": false },
"Ds3" : { "bits": 4, "read": true , "write": false }, "Ds3" : { "bits": 4, "read": true , "write": false },
"Ds4" : { "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 }, "Vd" : { "bits": 4, "read": false, "write": true },
"Vd2" : { "bits": 4, "read": false, "write": true }, "Vd2" : { "bits": 4, "read": false, "write": true },
"Vd3" : { "bits": 4, "read": false, "write": true }, "Vd3" : { "bits": 4, "read": false, "write": true },
"Vd4" : { "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 }, "Vx" : { "bits": 4, "read": true , "write": true },
"Vx2" : { "bits": 4, "read": true , "write": true }, "Vx2" : { "bits": 4, "read": true , "write": true },
"Vn" : { "bits": 4, "read": true , "write": false }, "Vn" : { "bits": 4, "read": true , "write": false },
"Vm" : { "bits": 4, "read": true , "write": false }, "Vm" : { "bits": 4, "read": true , "write": false },
"Vs" : { "bits": 4, "read": true , "write": false }, "Vs" : { "bits": 4, "read": true , "write": false },
"Vs2" : { "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; arm.FieldInfo = FieldInfo;
@ -192,10 +197,16 @@ function decomposeOperand(s) {
const elementSuffix = "[#i]"; const elementSuffix = "[#i]";
let element = null; let element = null;
let consecutive = 0; let consecutive = 0;
let userRegList = false;
if (s.endsWith("^")) {
userRegList = true;
s = s.substring(0, s.length - 1);
}
if (s.endsWith(elementSuffix)) { if (s.endsWith(elementSuffix)) {
element = "#i"; element = "#i";
s = s.substr(0, s.length - elementSuffix.length); s = s.substring(0, s.length - elementSuffix.length);
} }
if (s.endsWith("++")) { if (s.endsWith("++")) {
@ -219,7 +230,8 @@ function decomposeOperand(s) {
data : s, data : s,
element : element, element : element,
restrict: restrict, restrict: restrict,
consecutive: consecutive consecutive: consecutive,
userRegList: true
}; };
} }
@ -238,8 +250,8 @@ function splitOpcodeFields(s) {
return out.map((field) => { return field.trim(); }); return out.map((field) => { return field.trim(); });
} }
// asmdb.arm.Operand // asmdb.aarch32.Operand
// ================= // =====================
// ARM operand. // ARM operand.
class Operand extends base.Operand { class Operand extends base.Operand {
@ -267,17 +279,44 @@ class Operand extends base.Operand {
else else
return 0; return 0;
} }
isRelative() {
if (this.type === "imm")
return this.name === "relA" || this.name === "relS" || this.name === "relZ";
else
return false;
}
} }
arm.Operand = Operand; arm.Operand = Operand;
// asmdb.arm.Instruction // asmdb.aarch32.Instruction
// ===================== // =========================
function patternFromOperand(key) { function patternFromOperand(key) {
return key; return key;
// return key.replace(/\b(?:[RVDS](?:d|s|n|m|x|x2))\b/, "R"); // 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. // ARM instruction.
class Instruction extends base.Instruction { class Instruction extends base.Instruction {
constructor(db, data) { constructor(db, data) {
@ -482,13 +521,14 @@ class Instruction extends base.Instruction {
const m = part.match(/^([A-Za-z]\w*)/); const m = part.match(/^([A-Za-z]\w*)/);
if (m.length < part.length) { if (m.length < part.length) {
op.base.exp = exp.parse(part); op.base.exp = simplifyMemoryExpression(exp.parse(part));
op.base.field = m[1]; op.base.field = m[1];
} }
} }
else if (part.startsWith("#")) { else if (part.startsWith("#")) {
let p = part.substring(1); let p = part.substring(1);
let u = "1"; let u = "1";
let alwaysNegative = false;
let offExp = null; let offExp = null;
let offMul = 1; let offMul = 1;
@ -498,6 +538,11 @@ class Instruction extends base.Instruction {
p = p.substring(3); p = p.substring(3);
} }
if (p.startsWith("-")) {
alwaysNegative = false;
p = p.substring(1);
}
const expMatch = p.match(/^([A-Za-z]\w*)==/); const expMatch = p.match(/^([A-Za-z]\w*)==/);
if (expMatch) { if (expMatch) {
offExp = exp.parse(p); offExp = exp.parse(p);
@ -515,6 +560,7 @@ class Instruction extends base.Instruction {
op.offset.u = u; op.offset.u = u;
op.offset.exp = offExp; op.offset.exp = offExp;
op.offset.mul = offMul; op.offset.mul = offMul;
op.offset.negative = alwaysNegative;
} }
else { else {
let p = part; let p = part;
@ -531,7 +577,7 @@ class Instruction extends base.Instruction {
const m = p.match(/^([A-Za-z]\w*)/); const m = p.match(/^([A-Za-z]\w*)/);
if (m.length < p.length) { if (m.length < p.length) {
op.index.exp = exp.parse(p); op.index.exp = simplifyMemoryExpression(exp.parse(p));
op.index.field = m[1]; op.index.field = m[1];
} }
} }
@ -858,8 +904,8 @@ class Instruction extends base.Instruction {
} }
arm.Instruction = Instruction; arm.Instruction = Instruction;
// asmdb.arm.ISA // asmdb.aarch32.ISA
// ============= // =================
function mergeGroupData(data, group) { function mergeGroupData(data, group) {
for (let k in group) { for (let k in group) {
@ -921,7 +967,7 @@ class ISA extends base.ISA {
hasOwn.call(obj, "t16") ? "t16" : ""; hasOwn.call(obj, "t16") ? "t16" : "";
if (!encoding) 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++) { for (let j = 0; j < names.length; j++) {
const inst = new Instruction(this, names[j], operands, encoding.toUpperCase(), obj[encoding], obj); 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; arm.ISA = ISA;
}).apply(this, typeof module === "object" && module && module.exports }).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> // This file is part of AsmJit project <https://asmjit.com>
// //
// See asmjit.h or LICENSE.md for license and copyright information // See asmjit.h or LICENSE.md for license and copyright information
// SPDX-License-Identifier: Zlib // SPDX-License-Identifier: (Zlib or Unlicense)
(function($scope, $as) { (function($scope, $as) {
"use strict"; "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. // 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 // 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 = { const FieldInfo = {
"P" : { "bits": 1 }, "P" : { "bits": 1 },
"U" : { "bits": 1 }, "U" : { "bits": 1 },

View File

@ -1,7 +1,7 @@
// This file is part of AsmJit project <https://asmjit.com> // This file is part of AsmJit project <https://asmjit.com>
// //
// See asmjit.h or LICENSE.md for license and copyright information // See asmjit.h or LICENSE.md for license and copyright information
// SPDX-License-Identifier: Zlib // SPDX-License-Identifier: (Zlib or Unlicense)
(function($scope, $as) { (function($scope, $as) {
"use strict"; "use strict";
@ -205,7 +205,7 @@ class Operand {
toString() { return this.data; } toString() { return this.data; }
isReg() { return !!this.reg; } isReg() { return !!this.reg && this.type !== "reg-list"; }
isMem() { return !!this.mem; } isMem() { return !!this.mem; }
isImm() { return !!this.imm; } isImm() { return !!this.imm; }
isRel() { return !!this.rel; } isRel() { return !!this.rel; }
@ -259,6 +259,20 @@ class Instruction {
return out; 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) { _assignAttribute(key, value) {
switch (key) { switch (key) {
case "ext": 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> // This file is part of AsmJit project <https://asmjit.com>
// //
// See asmjit.h or LICENSE.md for license and copyright information // See asmjit.h or LICENSE.md for license and copyright information
// SPDX-License-Identifier: Zlib // SPDX-License-Identifier: (Zlib or Unlicense)
(function($scope, $as) { (function($scope, $as) {
"use strict"; "use strict";
@ -87,6 +87,7 @@ class ExpNode {
info() { return null; } info() { return null; }
clone() { throw new Error("ExpNode.clone() must be overridden"); } 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"); } toString(ctx) { throw new Error("ExpNode.toString() must be overridden"); }
} }
@ -97,6 +98,7 @@ class ImmNode extends ExpNode {
} }
clone() { return new ImmNode(this.imm); } clone() { return new ImmNode(this.imm); }
evaluate(ctx) { return this.imm; }
toString(ctx) { return ctx ? ctx.stringifyImmediate(this.imm) : String(this.imm); } toString(ctx) { return ctx ? ctx.stringifyImmediate(this.imm) : String(this.imm); }
} }
@ -106,7 +108,8 @@ class VarNode extends ExpNode {
this.name = name || ""; 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); } toString(ctx) { return ctx ? ctx.stringifyVariable(this.name) : String(this.name); }
} }
@ -117,7 +120,14 @@ class CallNode extends ExpNode {
this.args = args || []; 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) { toString(ctx) {
if (this.name === "$bit") { if (this.name === "$bit") {
@ -143,8 +153,23 @@ class UnaryNode extends ExpNode {
this.child = child || null; this.child = child || null;
} }
info() { return kUnaryOperators[this.op]; } info() {
clone() { return new UnaryNode(this.op, this.left ? this.left.clone() : null); } 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) { toString(ctx) {
return this.info().emit.replace(/@1/g, () => { return this.info().emit.replace(/@1/g, () => {
@ -166,8 +191,40 @@ class BinaryNode extends ExpNode {
this.right = right || null; this.right = right || null;
} }
info() { return kBinaryOperators[this.op]; } info() {
clone() { return new BinaryNode(this.op, this.left ? this.left.clone() : null, this.right ? this.right.clone() : null); } 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) { toString(ctx) {
return this.info().emit.replace(/@[1-2]/g, (p) => { 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 Unary(op, child) { return new UnaryNode(op, child); }
function Binary(op, left, right) { return new BinaryNode(op, left, right); } function Binary(op, left, right) { return new BinaryNode(op, left, right); }
/*
// TODO: Unused, remove?
function Negate(child) { return Unary("-", child); } function Negate(child) { return Unary("-", child); }
function BitNot(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 Ge(left, right) { return Binary(">=", left, right); }
function And(left, right) { return Binary("&&", left, right); } function And(left, right) { return Binary("&&", left, right); }
function Or(left, right) { return Binary("||", left, right); } function Or(left, right) { return Binary("||", left, right); }
*/
// Expression Tokenizer // Expression Tokenizer
// -------------------- // --------------------
@ -256,7 +312,44 @@ function newToken(type, position, data, value) {
const NoToken = newToken(kTokenNone, -1, "<end>", null); const NoToken = newToken(kTokenNone, -1, "<end>", null);
// Must be reset before it can be used, use `RegExp.lastIndex`. // 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) { function tokenize(source) {
const len = source.length; const len = source.length;
@ -268,23 +361,34 @@ function tokenize(source) {
let c, cat; // Current character code and category. let c, cat; // Current character code and category.
while (i < len) { while (i < len) {
cat = Category(c = source.charCodeAt(i)); c = source.charCodeAt(i);
cat = Category(c);
if (cat === kCharSpace) { if (cat === kCharSpace) {
i++; i++;
} }
else if (cat === kCharDigit) { else if (cat === kCharDigit) {
const n = tokens.length - 1; 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] === ".") { if (n >= 0 && tokens[n].data === "." && source[i - 1] === ".") {
tokens.length = n; tokens.length = n;
i--; 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))); tokens.push(newToken(kTokenValue, i, data, parseFloat(data)));
i += data.length; i += data.length;
} }
}
else if (cat === kCharAlpha) { else if (cat === kCharAlpha) {
start = i; start = i;
while (++i < len && ((cat = Category(source.charCodeAt(i))) === kCharAlpha || cat === kCharDigit)) while (++i < len && ((cat = Category(source.charCodeAt(i))) === kCharAlpha || cat === kCharDigit))
@ -623,6 +727,29 @@ $scope[$as] = {
Call: Call, Call: Call,
Unary: Unary, Unary: Unary,
Binary: Binary, 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, Visitor: Visitor,
ExpressionError: ExpressionError, ExpressionError: ExpressionError,

View File

@ -1,11 +1,11 @@
// This file is part of AsmJit project <https://asmjit.com> // This file is part of AsmJit project <https://asmjit.com>
// //
// See asmjit.h or LICENSE.md for license and copyright information // See asmjit.h or LICENSE.md for license and copyright information
// SPDX-License-Identifier: Zlib // SPDX-License-Identifier: (Zlib or Unlicense)
"use strict"; "use strict";
exports.base = require("./base.js"); exports.base = require("./base.js");
exports.arm = require("./arm.js"); exports.aarch32 = require("./aarch32.js");
exports.aarch64 = require("./aarch64.js"); exports.aarch64 = require("./aarch64.js");
exports.x86 = require("./x86.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": "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": "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": "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 Wd, Wd2, [Xn|SP, #offS*4]" , "op": "00101000|01|offS:7|Rd2|Rn|Rd"},
{"inst": "ldnp Xd, Xd2, [Xn|SP, #soff*8]" , "op": "10101000|01|soff: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, #soff*4]{@}{!}" , "op": "0010100|!post|W|1|soff: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, #soff*8]{@}{!}" , "op": "1010100|!post|W|1|soff: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, #soff*4]{@}{!}" , "op": "0110100|!post|W|1|soff: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, #zoff*4]" , "op": "10111001|01|zoff:12|Rn|Rd"}, {"inst": "ldr Wd, [Xn|SP, #offZ*4]" , "op": "10111001|01|offZ:12|Rn|Rd"},
{"inst": "ldr Xd, [Xn|SP, #zoff*8]" , "op": "11111001|01|zoff:12|Rn|Rd"}, {"inst": "ldr Xd, [Xn|SP, #offZ*8]" , "op": "11111001|01|offZ:12|Rn|Rd"},
{"inst": "ldr Wd, [Xn|SP, #soff*4]@" , "op": "10111000|010|soff:9|01|Rn|Rd"}, {"inst": "ldr Wd, [Xn|SP, #offS*4]@" , "op": "10111000|010|offS:9|01|Rn|Rd"},
{"inst": "ldr Xd, [Xn|SP, #soff*8]@" , "op": "11111000|010|soff:9|01|Rn|Rd"}, {"inst": "ldr Xd, [Xn|SP, #offS*8]@" , "op": "11111000|010|offS:9|01|Rn|Rd"},
{"inst": "ldr Wd, [Xn|SP, #soff*4]!" , "op": "10111000|010|soff:9|11|Rn|Rd"}, {"inst": "ldr Wd, [Xn|SP, #offS*4]!" , "op": "10111000|010|offS:9|11|Rn|Rd"},
{"inst": "ldr Xd, [Xn|SP, #soff*8]!" , "op": "11111000|010|soff: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 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 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 Wd, [PC, #offS*4]" , "op": "00011000|offS:19|Rd"},
{"inst": "ldr Xd, [PC, #soff*4]" , "op": "01011000|soff:19|Rd"}, {"inst": "ldr Xd, [PC, #offS*4]" , "op": "01011000|offS:19|Rd"},
{"inst": "ldrb Wd, [Xn|SP, #zoff]" , "op": "00111001|01|zoff:12|Rn|Rd"}, {"inst": "ldrb Wd, [Xn|SP, #offZ]" , "op": "00111001|01|offZ:12|Rn|Rd"},
{"inst": "ldrb Wd, [Xn|SP, #soff]@" , "op": "00111000|010|soff:9|01|Rn|Rd"}, {"inst": "ldrb Wd, [Xn|SP, #offS]@" , "op": "00111000|010|offS:9|01|Rn|Rd"},
{"inst": "ldrb Wd, [Xn|SP, #soff]!" , "op": "00111000|010|soff:9|11|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": "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, #offZ*2]" , "op": "01111001|01|offZ:12|Rn|Rd"},
{"inst": "ldrh Wd, [Xn|SP, #soff*2]@" , "op": "01111000|010|soff:9|01|Rn|Rd"}, {"inst": "ldrh Wd, [Xn|SP, #offS*2]@" , "op": "01111000|010|offS:9|01|Rn|Rd"},
{"inst": "ldrh Wd, [Xn|SP, #soff*2]!" , "op": "01111000|010|soff:9|11|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": "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 Wd, [Xn|SP, #offZ]" , "op": "00111001|11|offZ:12|Rn|Rd"},
{"inst": "ldrsb Xd, [Xn|SP, #zoff]" , "op": "00111001|10|zoff:12|Rn|Rd"}, {"inst": "ldrsb Xd, [Xn|SP, #offZ]" , "op": "00111001|10|offZ:12|Rn|Rd"},
{"inst": "ldrsb Wd, [Xn|SP, #soff]@" , "op": "00111000|110|soff:9|01|Rn|Rd"}, {"inst": "ldrsb Wd, [Xn|SP, #offS]@" , "op": "00111000|110|offS:9|01|Rn|Rd"},
{"inst": "ldrsb Xd, [Xn|SP, #soff]@" , "op": "00111000|100|soff:9|01|Rn|Rd"}, {"inst": "ldrsb Xd, [Xn|SP, #offS]@" , "op": "00111000|100|offS:9|01|Rn|Rd"},
{"inst": "ldrsb Wd, [Xn|SP, #soff]!" , "op": "00111000|110|soff:9|11|Rn|Rd"}, {"inst": "ldrsb Wd, [Xn|SP, #offS]!" , "op": "00111000|110|offS:9|11|Rn|Rd"},
{"inst": "ldrsb Xd, [Xn|SP, #soff]!" , "op": "00111000|100|soff: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 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": "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 Wd, [Xn|SP, #offZ*2]" , "op": "01111001|11|offZ:12|Rn|Rd"},
{"inst": "ldrsh Xd, [Xn|SP, #zoff*2]" , "op": "01111001|10|zoff:12|Rn|Rd"}, {"inst": "ldrsh Xd, [Xn|SP, #offZ*2]" , "op": "01111001|10|offZ:12|Rn|Rd"},
{"inst": "ldrsh Wd, [Xn|SP, #soff*2]@" , "op": "01111000|110|soff:9|01|Rn|Rd"}, {"inst": "ldrsh Wd, [Xn|SP, #offS*2]@" , "op": "01111000|110|offS:9|01|Rn|Rd"},
{"inst": "ldrsh Xd, [Xn|SP, #soff*2]@" , "op": "01111000|100|soff:9|01|Rn|Rd"}, {"inst": "ldrsh Xd, [Xn|SP, #offS*2]@" , "op": "01111000|100|offS:9|01|Rn|Rd"},
{"inst": "ldrsh Wd, [Xn|SP, #soff*2]!" , "op": "01111000|110|soff:9|11|Rn|Rd"}, {"inst": "ldrsh Wd, [Xn|SP, #offS*2]!" , "op": "01111000|110|offS:9|11|Rn|Rd"},
{"inst": "ldrsh Xd, [Xn|SP, #soff*2]!" , "op": "01111000|100|soff: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 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": "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, #offZ*4]" , "op": "10111001|10|offZ:12|Rn|Rd"},
{"inst": "ldrsw Xd, [Xn|SP, #soff*4]@" , "op": "10111000|100|soff:9|01|Rn|Rd"}, {"inst": "ldrsw Xd, [Xn|SP, #offS*4]@" , "op": "10111000|100|offS:9|01|Rn|Rd"},
{"inst": "ldrsw Xd, [Xn|SP, #soff*4]!" , "op": "10111000|100|soff:9|11|Rn|Rd"}, {"inst": "ldrsw Xd, [Xn|SP, #offS*4]!" , "op": "10111000|100|offS:9|11|Rn|Rd"},
{"inst": "ldrsw Xd, [PC, #soff*4]" , "op": "10011000|soff:19|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": "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 Wd, [Xn|SP, #offS]" , "op": "10111000|010|offS:9|10|Rn|Rd"},
{"inst": "ldtr Xd, [Xn|SP, #soff]" , "op": "11111000|010|soff:9|10|Rn|Rd"}, {"inst": "ldtr Xd, [Xn|SP, #offS]" , "op": "11111000|010|offS:9|10|Rn|Rd"},
{"inst": "ldtrb Wd, [Xn|SP, #soff]" , "op": "00111000|010|soff:9|10|Rn|Rd"}, {"inst": "ldtrb Wd, [Xn|SP, #offS]" , "op": "00111000|010|offS:9|10|Rn|Rd"},
{"inst": "ldtrh Wd, [Xn|SP, #soff]" , "op": "01111000|010|soff:9|10|Rn|Rd"}, {"inst": "ldtrh Wd, [Xn|SP, #offS]" , "op": "01111000|010|offS:9|10|Rn|Rd"},
{"inst": "ldtrsb Wd, [Xn|SP, #soff]" , "op": "00111000|100|soff:9|10|Rn|Rd"}, {"inst": "ldtrsb Wd, [Xn|SP, #offS]" , "op": "00111000|100|offS:9|10|Rn|Rd"},
{"inst": "ldtrsb Xd, [Xn|SP, #soff]" , "op": "00111000|110|soff:9|10|Rn|Rd"}, {"inst": "ldtrsb Xd, [Xn|SP, #offS]" , "op": "00111000|110|offS:9|10|Rn|Rd"},
{"inst": "ldtrsh Wd, [Xn|SP, #soff]" , "op": "01111000|100|soff:9|10|Rn|Rd"}, {"inst": "ldtrsh Wd, [Xn|SP, #offS]" , "op": "01111000|100|offS:9|10|Rn|Rd"},
{"inst": "ldtrsh Xd, [Xn|SP, #soff]" , "op": "01111000|110|soff:9|10|Rn|Rd"}, {"inst": "ldtrsh Xd, [Xn|SP, #offS]" , "op": "01111000|110|offS:9|10|Rn|Rd"},
{"inst": "ldtrsw Xd, [Xn|SP, #soff]" , "op": "10111000|100|soff:9|10|Rn|Rd"}, {"inst": "ldtrsw Xd, [Xn|SP, #offS]" , "op": "10111000|100|offS:9|10|Rn|Rd"},
{"inst": "ldur Wd, [Xn|SP, #soff]" , "op": "10111000|010|soff:9|00|Rn|Rd"}, {"inst": "ldur Wd, [Xn|SP, #offS]" , "op": "10111000|010|offS:9|00|Rn|Rd"},
{"inst": "ldur Xd, [Xn|SP, #soff]" , "op": "11111000|010|soff:9|00|Rn|Rd"}, {"inst": "ldur Xd, [Xn|SP, #offS]" , "op": "11111000|010|offS:9|00|Rn|Rd"},
{"inst": "ldurb Wd, [Xn|SP, #soff]" , "op": "00111000|010|soff:9|00|Rn|Rd"}, {"inst": "ldurb Wd, [Xn|SP, #offS]" , "op": "00111000|010|offS:9|00|Rn|Rd"},
{"inst": "ldurh Wd, [Xn|SP, #soff]" , "op": "01111000|010|soff:9|00|Rn|Rd"}, {"inst": "ldurh Wd, [Xn|SP, #offS]" , "op": "01111000|010|offS:9|00|Rn|Rd"},
{"inst": "ldursb Wd, [Xn|SP, #soff]" , "op": "00111000|100|soff:9|00|Rn|Rd"}, {"inst": "ldursb Wd, [Xn|SP, #offS]" , "op": "00111000|100|offS:9|00|Rn|Rd"},
{"inst": "ldursb Xd, [Xn|SP, #soff]" , "op": "00111000|110|soff:9|00|Rn|Rd"}, {"inst": "ldursb Xd, [Xn|SP, #offS]" , "op": "00111000|110|offS:9|00|Rn|Rd"},
{"inst": "ldursh Wd, [Xn|SP, #soff]" , "op": "01111000|100|soff:9|00|Rn|Rd"}, {"inst": "ldursh Wd, [Xn|SP, #offS]" , "op": "01111000|100|offS:9|00|Rn|Rd"},
{"inst": "ldursh Xd, [Xn|SP, #soff]" , "op": "01111000|110|soff:9|00|Rn|Rd"}, {"inst": "ldursh Xd, [Xn|SP, #offS]" , "op": "01111000|110|offS:9|00|Rn|Rd"},
{"inst": "ldursw Xd, [Xn|SP, #soff]" , "op": "10111000|100|soff:9|00|Rn|Rd"}, {"inst": "ldursw Xd, [Xn|SP, #offS]" , "op": "10111000|100|offS:9|00|Rn|Rd"},
{"inst": "ldxp Wd, Wd2, [Xn|SP, #soff*4]" , "op": "10001000|011|11111|0|Rd2|Rn|Rd"}, {"inst": "ldxp Wd, Wd2, [Xn|SP, #offS*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": "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 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": "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"}, {"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 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": "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, 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, [Xn|SP, #offZ]" , "op": "11111001|10|offZ: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": "prfm #prf_op, [PC, #offS*4]" , "op": "11011000|offS: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": "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": "pssbb" , "op": "11010101|000|00011|0011|0100|100|11111"},
{"inst": "rbit Wd, Wn" , "op": "01011010|110|00000|0|00000|Rn|Rd"}, {"inst": "rbit Wd, Wn" , "op": "01011010|110|00000|0|00000|Rn|Rd"},
{"inst": "rbit Xd, Xn" , "op": "11011010|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": "sev" , "op": "11010101|000|00011|0010|0000|100|11111"},
{"inst": "sevl" , "op": "11010101|000|00011|0010|0000|101|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": "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": "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": "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"}, {"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": "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": "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": "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 Ws, Ws2, [Xn|SP, #offS*4]" , "op": "00101000|00|offS:7|Rs2|Rn|Rs"},
{"inst": "stnp Xs, Xs2, [Xn|SP, #simm*8]" , "op": "10101000|00|simm: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, #simm*4]{@}{!}" , "op": "0010100|!post|W|0|simm: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, #simm*8]{@}{!}" , "op": "1010100|!post|W|0|simm: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, #zoff*4]" , "op": "10111001|00|zoff:12|Rn|Rs"}, {"inst": "str Ws, [Xn|SP, #offZ*4]" , "op": "10111001|00|offZ:12|Rn|Rs"},
{"inst": "str Xs, [Xn|SP, #zoff*8]" , "op": "11111001|00|zoff:12|Rn|Rs"}, {"inst": "str Xs, [Xn|SP, #offZ*8]" , "op": "11111001|00|offZ:12|Rn|Rs"},
{"inst": "str Ws, [Xn|SP, #soff*4]@" , "op": "10111000|000|soff:9|01|Rn|Rs"}, {"inst": "str Ws, [Xn|SP, #offS*4]@" , "op": "10111000|000|offS:9|01|Rn|Rs"},
{"inst": "str Xs, [Xn|SP, #soff*8]@" , "op": "11111000|000|soff:9|01|Rn|Rs"}, {"inst": "str Xs, [Xn|SP, #offS*8]@" , "op": "11111000|000|offS:9|01|Rn|Rs"},
{"inst": "str Ws, [Xn|SP, #soff*4]!" , "op": "10111000|000|soff:9|11|Rn|Rs"}, {"inst": "str Ws, [Xn|SP, #offS*4]!" , "op": "10111000|000|offS:9|11|Rn|Rs"},
{"inst": "str Xs, [Xn|SP, #soff*8]!" , "op": "11111000|000|soff: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 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": "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, #offZ]" , "op": "00111001|00|offZ:12|Rn|Rs"},
{"inst": "strb Ws, [Xn|SP, #soff]@" , "op": "00111000|000|soff:9|01|Rn|Rs"}, {"inst": "strb Ws, [Xn|SP, #offS]@" , "op": "00111000|000|offS:9|01|Rn|Rs"},
{"inst": "strb Ws, [Xn|SP, #soff]!" , "op": "00111000|000|soff:9|11|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": "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, #offZ*2]" , "op": "01111001|00|offZ:12|Rn|Rs"},
{"inst": "strh Ws, [Xn|SP, #soff*2]@" , "op": "01111000|000|soff:9|01|Rn|Rs"}, {"inst": "strh Ws, [Xn|SP, #offS*2]@" , "op": "01111000|000|offS:9|01|Rn|Rs"},
{"inst": "strh Ws, [Xn|SP, #soff*2]!" , "op": "01111000|000|soff:9|11|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": "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 Ws, [Xn|SP, #offS]" , "op": "10111000|000|offS:9|10|Rn|Rs"},
{"inst": "sttr Xs, [Xn|SP, #soff]" , "op": "11111000|000|soff:9|10|Rn|Rs"}, {"inst": "sttr Xs, [Xn|SP, #offS]" , "op": "11111000|000|offS:9|10|Rn|Rs"},
{"inst": "sttrb Ws, [Xn|SP, #soff]" , "op": "00111000|000|soff:9|10|Rn|Rs"}, {"inst": "sttrb Ws, [Xn|SP, #offS]" , "op": "00111000|000|offS:9|10|Rn|Rs"},
{"inst": "sttrh Ws, [Xn|SP, #soff]" , "op": "01111000|000|soff:9|10|Rn|Rs"}, {"inst": "sttrh Ws, [Xn|SP, #offS]" , "op": "01111000|000|offS:9|10|Rn|Rs"},
{"inst": "stur Ws, [Xn|SP, #soff]" , "op": "10111000|000|soff:9|00|Rn|Rs"}, {"inst": "stur Ws, [Xn|SP, #offS]" , "op": "10111000|000|offS:9|00|Rn|Rs"},
{"inst": "stur Xs, [Xn|SP, #soff]" , "op": "11111000|000|soff:9|00|Rn|Rs"}, {"inst": "stur Xs, [Xn|SP, #offS]" , "op": "11111000|000|offS:9|00|Rn|Rs"},
{"inst": "sturb Ws, [Xn|SP, #soff]" , "op": "00111000|000|soff:9|00|Rn|Rs"}, {"inst": "sturb Ws, [Xn|SP, #offS]" , "op": "00111000|000|offS:9|00|Rn|Rs"},
{"inst": "sturh Ws, [Xn|SP, #soff]" , "op": "01111000|000|soff: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, 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": "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"}, {"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 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 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": "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 Wd, Wn" , "op": "00010011|000|00000|0|00111|Rn|Rd"},
{"inst": "sxtb Xd, Wn" , "op": "10010011|010|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"}, {"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": "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 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 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 Wd, Wn, #immS" , "op": "00010001|110|000|immS:8|Rn|Rd"},
{"inst": "smax Xd, Xn, #simm" , "op": "10010001|110|000|simm: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 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 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 Wd, Wn, #immS" , "op": "00010001|110|010|immS:8|Rn|Rd"},
{"inst": "smin Xd, Xn, #simm" , "op": "10010001|110|010|simm: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 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 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 Wd, Wn, #immZ" , "op": "00010001|110|001|immZ:8|Rn|Rd"},
{"inst": "umax Xd, Xn, #simm" , "op": "10010001|110|001|simm: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 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 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 Wd, Wn, #immZ" , "op": "00010001|110|011|immZ:8|Rn|Rd"},
{"inst": "umin Xd, Xn, #simm" , "op": "10010001|110|011|simm:8|Rn|Rd"} {"inst": "umin Xd, Xn, #immZ" , "op": "10010001|110|011|immZ:8|Rn|Rd"}
]}, ]},
{"category": "GP GP_EXT", "ext": "D128", "data": [ {"category": "GP GP_EXT", "ext": "D128", "data": [
@ -510,21 +510,21 @@
]}, ]},
{"category": "GP GP_EXT", "ext": "LRCPC2", "data": [ {"category": "GP GP_EXT", "ext": "LRCPC2", "data": [
{"inst": "ldapur Wd, [Xn|SP, #soff]" , "op": "10011001|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, #soff]" , "op": "11011001|010|soff:9|00|Rn|Rd"}, {"inst": "ldapur Xd, [Xn|SP, #offS]" , "op": "11011001|010|offS:9|00|Rn|Rd"},
{"inst": "ldapurb Wd, [Xn|SP, #soff]" , "op": "00011001|010|soff:9|00|Rn|Rd"}, {"inst": "ldapurb Wd, [Xn|SP, #offS]" , "op": "00011001|010|offS:9|00|Rn|Rd"},
{"inst": "ldapurh Wd, [Xn|SP, #soff]" , "op": "01011001|010|soff: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 Wd, [Xn|SP, #offS]" , "op": "00011001|110|offS:9|00|Rn|Rd"},
{"inst": "ldapursb Xd, [Xn|SP, #soff]" , "op": "00011001|100|soff:9|00|Rn|Rd"}, {"inst": "ldapursb Xd, [Xn|SP, #offS]" , "op": "00011001|100|offS:9|00|Rn|Rd"},
{"inst": "ldapursh Wd, [Xn|SP, #soff]" , "op": "01011001|110|soff:9|00|Rn|Rd"}, {"inst": "ldapursh Wd, [Xn|SP, #offS]" , "op": "01011001|110|offS:9|00|Rn|Rd"},
{"inst": "ldapursh Xd, [Xn|SP, #soff]" , "op": "01011001|100|soff:9|00|Rn|Rd"}, {"inst": "ldapursh Xd, [Xn|SP, #offS]" , "op": "01011001|100|offS:9|00|Rn|Rd"},
{"inst": "ldapursw Xd, [Xn|SP, #soff]" , "op": "10011001|100|soff: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 Ws, [Xn|SP, #offS]" , "op": "10011001|000|offS:9|00|Rn|Rs"},
{"inst": "stlur Xs, [Xn|SP, #soff]" , "op": "11011001|000|soff:9|00|Rn|Rs"}, {"inst": "stlur Xs, [Xn|SP, #offS]" , "op": "11011001|000|offS:9|00|Rn|Rs"},
{"inst": "stlurb Ws, [Xn|SP, #soff]" , "op": "00011001|000|soff:9|00|Rn|Rs"}, {"inst": "stlurb Ws, [Xn|SP, #offS]" , "op": "00011001|000|offS:9|00|Rn|Rs"},
{"inst": "stlurh Ws, [Xn|SP, #soff]" , "op": "01011001|000|soff: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": [ {"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": "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": "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": "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": "ldg Xd, [Xn|SP, #offS*16]" , "op": "11011001|011|offS:9|00|Rn|Rd"},
{"inst": "st2g Xs|SP, [Xn|SP, #soff*16]{@}{!}" , "op": "11011001|101|soff:9|!post|W|Rn|Rs"}, {"inst": "st2g Xs|SP, [Xn|SP, #offS*16]{@}{!}" , "op": "11011001|101|offS:9|!post|W|Rn|Rs"},
{"inst": "stg Xs|SP, [Xn|SP, #soff*16]{@}{!}" , "op": "11011001|001|soff: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, #soff*16]{@}{!}" , "op": "0110100|!post|W|0|soff:7|Rs2|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, #soff*16]{@}{!}" , "op": "11011001|111|soff:9|!post|W|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, #soff*16]{@}{!}" , "op": "11011001|011|soff: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": "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": "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"} {"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": "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": "eretaa" , "op": "11010110|100|11111|0000|10|11111|11111" , "control": "return"},
{"inst": "eretab" , "op": "11010110|100|11111|0000|11|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": "ldraa Xd, [Xn|SP, #offS]{!}" , "op": "11111000|0|offS:1|1|offS:9|W1|Rn|Rd"},
{"inst": "ldrab Xd, [Xn|SP, #soff]{!}" , "op": "11111000|1|soff:1|1|soff: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": "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": "pacdb Xd, Xn|SP" , "op": "11011010|110|00001|0|00011|Rn|Rd"},
{"inst": "pacdza Xd" , "op": "11011010|110|00001|0|01010|11111|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, 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": "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": "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 Sd, Sd2, [Xn|SP, #offS*4]" , "op": "00101100|01|offS:7|Vd2|Vn|Vd"},
{"inst": "ldnp Dd, Dd2, [Xn|SP, #soff*8]" , "op": "01101100|01|soff: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, #soff*16]" , "op": "10101100|01|soff: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, #soff*4]{@}{!}" , "op": "0010110|!post|W|1|soff: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, #soff*8]{@}{!}" , "op": "0110110|!post|W|1|soff: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, #soff*16]{@}{!}" , "op": "1010110|!post|W|1|soff: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 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 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 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 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 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 Sd, [PC, #offS*4]" , "op": "00011100|offS:19|Vd"},
{"inst": "ldr Dd, [PC, #soff*4]" , "op": "01011100|soff:19|Vd"}, {"inst": "ldr Dd, [PC, #offS*4]" , "op": "01011100|offS:19|Vd"},
{"inst": "ldr Qd, [PC, #soff*4]" , "op": "10011100|soff:19|Vd"}, {"inst": "ldr Qd, [PC, #offS*4]" , "op": "10011100|offS:19|Vd"},
{"inst": "ldr Bd, [Xn|SP, #zoff]" , "op": "00111101|01|zoff:12|Rn|Vd"}, {"inst": "ldr Bd, [Xn|SP, #offZ]" , "op": "00111101|01|offZ:12|Rn|Vd"},
{"inst": "ldr Hd, [Xn|SP, #zoff*2]" , "op": "01111101|01|zoff:12|Rn|Vd"}, {"inst": "ldr Hd, [Xn|SP, #offZ*2]" , "op": "01111101|01|offZ:12|Rn|Vd"},
{"inst": "ldr Sd, [Xn|SP, #zoff*4]" , "op": "10111101|01|zoff:12|Rn|Vd"}, {"inst": "ldr Sd, [Xn|SP, #offZ*4]" , "op": "10111101|01|offZ:12|Rn|Vd"},
{"inst": "ldr Dd, [Xn|SP, #zoff*8]" , "op": "11111101|01|zoff:12|Rn|Vd"}, {"inst": "ldr Dd, [Xn|SP, #offZ*8]" , "op": "11111101|01|offZ:12|Rn|Vd"},
{"inst": "ldr Qd, [Xn|SP, #zoff*16]" , "op": "00111101|11|zoff:12|Rn|Vd"}, {"inst": "ldr Qd, [Xn|SP, #offZ*16]" , "op": "00111101|11|offZ:12|Rn|Vd"},
{"inst": "ldr Bd, [Xn|SP, #soff]!" , "op": "00111100|010|soff:9|11|Rn|Vd"}, {"inst": "ldr Bd, [Xn|SP, #offS]!" , "op": "00111100|010|offS:9|11|Rn|Vd"},
{"inst": "ldr Hd, [Xn|SP, #soff*2]!" , "op": "01111100|010|soff:9|11|Rn|Vd"}, {"inst": "ldr Hd, [Xn|SP, #offS*2]!" , "op": "01111100|010|offS:9|11|Rn|Vd"},
{"inst": "ldr Sd, [Xn|SP, #soff*4]!" , "op": "10111100|010|soff:9|11|Rn|Vd"}, {"inst": "ldr Sd, [Xn|SP, #offS*4]!" , "op": "10111100|010|offS:9|11|Rn|Vd"},
{"inst": "ldr Dd, [Xn|SP, #soff*8]!" , "op": "11111100|010|soff:9|11|Rn|Vd"}, {"inst": "ldr Dd, [Xn|SP, #offS*8]!" , "op": "11111100|010|offS:9|11|Rn|Vd"},
{"inst": "ldr Qd, [Xn|SP, #soff*16]!" , "op": "00111100|110|soff:9|11|Rn|Vd"}, {"inst": "ldr Qd, [Xn|SP, #offS*16]!" , "op": "00111100|110|offS:9|11|Rn|Vd"},
{"inst": "ldr Bd, [Xn|SP, #soff]@" , "op": "00111100|010|soff:9|01|Rn|Vd"}, {"inst": "ldr Bd, [Xn|SP, #offS]@" , "op": "00111100|010|offS:9|01|Rn|Vd"},
{"inst": "ldr Hd, [Xn|SP, #soff*2]@" , "op": "01111100|010|soff:9|01|Rn|Vd"}, {"inst": "ldr Hd, [Xn|SP, #offS*2]@" , "op": "01111100|010|offS:9|01|Rn|Vd"},
{"inst": "ldr Sd, [Xn|SP, #soff*4]@" , "op": "10111100|010|soff:9|01|Rn|Vd"}, {"inst": "ldr Sd, [Xn|SP, #offS*4]@" , "op": "10111100|010|offS:9|01|Rn|Vd"},
{"inst": "ldr Dd, [Xn|SP, #soff*8]@" , "op": "11111100|010|soff:9|01|Rn|Vd"}, {"inst": "ldr Dd, [Xn|SP, #offS*8]@" , "op": "11111100|010|offS:9|01|Rn|Vd"},
{"inst": "ldr Qd, [Xn|SP, #soff*16]@" , "op": "00111100|110|soff:9|01|Rn|Vd"}, {"inst": "ldr Qd, [Xn|SP, #offS*16]@" , "op": "00111100|110|offS:9|01|Rn|Vd"},
{"inst": "ldur Bd, [Xn|SP, #soff]" , "op": "00111100|010|soff:9|00|Rn|Vd"}, {"inst": "ldur Bd, [Xn|SP, #offS]" , "op": "00111100|010|offS:9|00|Rn|Vd"},
{"inst": "ldur Hd, [Xn|SP, #soff]" , "op": "01111100|010|soff:9|00|Rn|Vd"}, {"inst": "ldur Hd, [Xn|SP, #offS]" , "op": "01111100|010|offS:9|00|Rn|Vd"},
{"inst": "ldur Sd, [Xn|SP, #soff]" , "op": "10111100|010|soff:9|00|Rn|Vd"}, {"inst": "ldur Sd, [Xn|SP, #offS]" , "op": "10111100|010|offS:9|00|Rn|Vd"},
{"inst": "ldur Dd, [Xn|SP, #soff]" , "op": "11111100|010|soff:9|00|Rn|Vd"}, {"inst": "ldur Dd, [Xn|SP, #offS]" , "op": "11111100|010|offS:9|00|Rn|Vd"},
{"inst": "ldur Qd, [Xn|SP, #soff]" , "op": "00111100|110|soff: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": "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.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"}, {"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.4H" , "op": "00001110|01|11000|11010|10|Vn|Vd"},
{"inst": "sminv Hd, Vn.8H" , "op": "01001110|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": "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": "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 Vd.ta, Vn.tb, Vm.tb" , "op": "01001110|sz|1|Vm|10000|0|Vn|Vd" , "t": "8H.16B 4S.8H 2D.4S"}, {"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 Vd.4S, Vn.4H, Vm.H[#dx]" , "op": "00001111|01|idx[1:0]|Vm:4|0010|idx[2]|0|Vn|Vd"}, {"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 Vd.4S, Vn.8H, Vm.H[#dx]" , "op": "01001111|01|idx[1:0]|Vm:4|0010|idx[2]|0|Vn|Vd"}, {"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 Vd.2D, Vn.2S, Vm.S[#dx]" , "op": "00001111|10|idx[0] |Vm |0010|idx[1]|0|Vn|Vd"}, {"inst": "smlal Vx.2D, Vn.2S, Vm.S[#dx]" , "op": "00001111|10|idx[0] |Vm |0010|idx[1]|0|Vn|Vx"},
{"inst": "smlal2 Vd.2D, Vn.4S, Vm.S[#dx]" , "op": "01001111|10|idx[0] |Vm |0010|idx[1]|0|Vn|Vd"}, {"inst": "smlal2 Vx.2D, Vn.4S, Vm.S[#dx]" , "op": "01001111|10|idx[0] |Vm |0010|idx[1]|0|Vn|Vx"},
{"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": "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 Vd.ta, Vn.tb, Vm.tb" , "op": "01001110|sz|1|Vm|10100|0|Vn|Vd" , "t": "8H.16B 4S.8H 2D.4S"}, {"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 Vd.4S, Vn.4H, Vm.H[#dx]" , "op": "00001111|01|idx[1:0]|Vm:4|0110|idx[2]|0|Vn|Vd"}, {"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 Vd.4S, Vn.8H, Vm.H[#dx]" , "op": "01001111|01|idx[1:0]|Vm:4|0110|idx[2]|0|Vn|Vd"}, {"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 Vd.2D, Vn.2S, Vm.S[#dx]" , "op": "00001111|10|idx[0] |Vm |0110|idx[1]|0|Vn|Vd"}, {"inst": "smlsl Vx.2D, Vn.2S, Vm.S[#dx]" , "op": "00001111|10|idx[0] |Vm |0110|idx[1]|0|Vn|Vx"},
{"inst": "smlsl2 Vd.2D, Vn.4S, Vm.S[#dx]" , "op": "01001111|10|idx[0] |Vm |0110|idx[1]|0|Vn|Vd"}, {"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.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 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"}, {"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, 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==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": "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 Sd, Sd2, [Xn|SP, #offS*4]" , "op": "00101100|00|offS:7|Vs2|Vn|Vs"},
{"inst": "stnp Dd, Dd2, [Xn|SP, #soff*8]" , "op": "01101100|00|soff: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, #soff*16]" , "op": "10101100|00|soff: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, #soff*4]{@}{!}" , "op": "0010110|!post|W|0|soff: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, #soff*8]{@}{!}" , "op": "0110110|!post|W|0|soff: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, #soff*16]{@}{!}" , "op": "1010110|!post|W|0|soff: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 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 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 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 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 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 Bd, [Xn|SP, #offZ]" , "op": "00111101|00|offZ:12|Rn|Vs"},
{"inst": "str Hd, [Xn|SP, #zoff*2]" , "op": "01111101|00|zoff:12|Rn|Vs"}, {"inst": "str Hd, [Xn|SP, #offZ*2]" , "op": "01111101|00|offZ:12|Rn|Vs"},
{"inst": "str Sd, [Xn|SP, #zoff*4]" , "op": "10111101|00|zoff:12|Rn|Vs"}, {"inst": "str Sd, [Xn|SP, #offZ*4]" , "op": "10111101|00|offZ:12|Rn|Vs"},
{"inst": "str Dd, [Xn|SP, #zoff*8]" , "op": "11111101|00|zoff:12|Rn|Vs"}, {"inst": "str Dd, [Xn|SP, #offZ*8]" , "op": "11111101|00|offZ:12|Rn|Vs"},
{"inst": "str Qd, [Xn|SP, #zoff*16]" , "op": "00111101|10|zoff:12|Rn|Vs"}, {"inst": "str Qd, [Xn|SP, #offZ*16]" , "op": "00111101|10|offZ:12|Rn|Vs"},
{"inst": "str Bd, [Xn|SP, #soff]!" , "op": "00111100|000|soff:9|11|Rn|Vs"}, {"inst": "str Bd, [Xn|SP, #offS]!" , "op": "00111100|000|offS:9|11|Rn|Vs"},
{"inst": "str Hd, [Xn|SP, #soff*2]!" , "op": "01111100|000|soff:9|11|Rn|Vs"}, {"inst": "str Hd, [Xn|SP, #offS*2]!" , "op": "01111100|000|offS:9|11|Rn|Vs"},
{"inst": "str Sd, [Xn|SP, #soff*4]!" , "op": "10111100|000|soff:9|11|Rn|Vs"}, {"inst": "str Sd, [Xn|SP, #offS*4]!" , "op": "10111100|000|offS:9|11|Rn|Vs"},
{"inst": "str Dd, [Xn|SP, #soff*8]!" , "op": "11111100|000|soff:9|11|Rn|Vs"}, {"inst": "str Dd, [Xn|SP, #offS*8]!" , "op": "11111100|000|offS:9|11|Rn|Vs"},
{"inst": "str Qd, [Xn|SP, #soff*16]!" , "op": "00111100|100|soff:9|11|Rn|Vs"}, {"inst": "str Qd, [Xn|SP, #offS*16]!" , "op": "00111100|100|offS:9|11|Rn|Vs"},
{"inst": "str Bd, [Xn|SP, #soff]@" , "op": "00111100|000|soff:9|01|Rn|Vs"}, {"inst": "str Bd, [Xn|SP, #offS]@" , "op": "00111100|000|offS:9|01|Rn|Vs"},
{"inst": "str Hd, [Xn|SP, #soff*2]@" , "op": "01111100|000|soff:9|01|Rn|Vs"}, {"inst": "str Hd, [Xn|SP, #offS*2]@" , "op": "01111100|000|offS:9|01|Rn|Vs"},
{"inst": "str Sd, [Xn|SP, #soff*4]@" , "op": "10111100|000|soff:9|01|Rn|Vs"}, {"inst": "str Sd, [Xn|SP, #offS*4]@" , "op": "10111100|000|offS:9|01|Rn|Vs"},
{"inst": "str Dd, [Xn|SP, #soff*8]@" , "op": "11111100|000|soff:9|01|Rn|Vs"}, {"inst": "str Dd, [Xn|SP, #offS*8]@" , "op": "11111100|000|offS:9|01|Rn|Vs"},
{"inst": "str Qd, [Xn|SP, #soff*16]@" , "op": "00111100|100|soff:9|01|Rn|Vs"}, {"inst": "str Qd, [Xn|SP, #offS*16]@" , "op": "00111100|100|offS:9|01|Rn|Vs"},
{"inst": "stur Bd, [Xn|SP, #soff]" , "op": "00111100|000|soff:9|00|Rn|Vs"}, {"inst": "stur Bd, [Xn|SP, #offS]" , "op": "00111100|000|offS:9|00|Rn|Vs"},
{"inst": "stur Hd, [Xn|SP, #soff]" , "op": "01111100|000|soff:9|00|Rn|Vs"}, {"inst": "stur Hd, [Xn|SP, #offS]" , "op": "01111100|000|offS:9|00|Rn|Vs"},
{"inst": "stur Sd, [Xn|SP, #soff]" , "op": "10111100|000|soff:9|00|Rn|Vs"}, {"inst": "stur Sd, [Xn|SP, #offS]" , "op": "10111100|000|offS:9|00|Rn|Vs"},
{"inst": "stur Dd, [Xn|SP, #soff]" , "op": "11111100|000|soff:9|00|Rn|Vs"}, {"inst": "stur Dd, [Xn|SP, #offS]" , "op": "11111100|000|offS:9|00|Rn|Vs"},
{"inst": "stur Qd, [Xn|SP, #soff]" , "op": "00111100|100|soff: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 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": "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"}, {"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": [ {"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": "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 Bd, [Xn|SP, #offS]" , "op": "00011101|010|offS:9|10|Rn|Vd"},
{"inst": "ldapur Hd, [Xn|SP, #soff]" , "op": "01011101|010|soff:9|10|Rn|Vd"}, {"inst": "ldapur Hd, [Xn|SP, #offS]" , "op": "01011101|010|offS:9|10|Rn|Vd"},
{"inst": "ldapur Sd, [Xn|SP, #soff]" , "op": "10011101|010|soff:9|10|Rn|Vd"}, {"inst": "ldapur Sd, [Xn|SP, #offS]" , "op": "10011101|010|offS:9|10|Rn|Vd"},
{"inst": "ldapur Dd, [Xn|SP, #soff]" , "op": "11011101|010|soff:9|10|Rn|Vd"}, {"inst": "ldapur Dd, [Xn|SP, #offS]" , "op": "11011101|010|offS:9|10|Rn|Vd"},
{"inst": "ldapur Qd, [Xn|SP, #soff]" , "op": "00011101|110|soff: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": "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 Bs, [Xn|SP, #offS]" , "op": "00011101|000|offS:9|10|Rn|Vs"},
{"inst": "stlur Hs, [Xn|SP, #soff]" , "op": "01011101|000|soff:9|10|Rn|Vs"}, {"inst": "stlur Hs, [Xn|SP, #offS]" , "op": "01011101|000|offS:9|10|Rn|Vs"},
{"inst": "stlur Ss, [Xn|SP, #soff]" , "op": "10011101|000|soff:9|10|Rn|Vs"}, {"inst": "stlur Ss, [Xn|SP, #offS]" , "op": "10011101|000|offS:9|10|Rn|Vs"},
{"inst": "stlur Ds, [Xn|SP, #soff]" , "op": "11011101|000|soff:9|10|Rn|Vs"}, {"inst": "stlur Ds, [Xn|SP, #offS]" , "op": "11011101|000|offS:9|10|Rn|Vs"},
{"inst": "stlur Qs, [Xn|SP, #soff]" , "op": "00011101|100|soff:9|10|Rn|Vs"} {"inst": "stlur Qs, [Xn|SP, #offS]" , "op": "00011101|100|offS:9|10|Rn|Vs"}
]}, ]},
{"category": "ASIMD", "ext": "RDM", "data": [ {"category": "ASIMD", "ext": "RDM", "data": [

View File

@ -624,200 +624,405 @@
{"inst": "[rep] outs R:dx, R:m32(ds:zsi)" , "op": "6F"} {"inst": "[rep] outs R:dx, R:m32(ds:zsi)" , "op": "6F"}
]}, ]},
{"category": "GP GP_EXT", "data": [ {"category": "GP GP_EXT", "ext": "I486", "data": [
{"inst": "aadd X:m32, r32" , "op": "MR: 0F 38 FC /r" , "ext": "RAO_INT"}, {"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": "aadd X:m64, r64" , "op": "MR: REX.W 0F 38 FC /r" , "ext": "RAO_INT"}, {"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": "aand X:m32, r32" , "op": "MR: 66 0F 38 FC /r" , "ext": "RAO_INT"}, {"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": "aand X:m64, r64" , "op": "MR: REX.W 66 0F 38 FC /r" , "ext": "RAO_INT"}, {"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": "adcx X:~r32, ~r32/m32" , "op": "RM: 66 0F 38 F6 /r" , "ext": "ADX" , "io": "CF=X"}, {"inst": "cpuid X:<eax>, W:<ebx>, X:<ecx>, W:<edx>" , "op": "0F A2" , "volatile": true},
{"inst": "adcx X:~r64, ~r64/m64" , "op": "RM: REX.W 66 0F 38 F6 /r" , "ext": "ADX" , "io": "CF=X"}, {"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": "adox X:~r32, ~r32/m32" , "op": "RM: F3 0F 38 F6 /r" , "ext": "ADX" , "io": "OF=X"}, {"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": "adox X:~r64, ~r64/m64" , "op": "RM: REX.W F3 0F 38 F6 /r" , "ext": "ADX" , "io": "OF=X"}, {"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": "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": "[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"}
{"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 CRYPTO_HASH", "data": [ {"category": "GP GP_EXT", "ext": "3DNOW", "volatile": true, "data": [
{"inst": "crc32 X:r32, r8/m8" , "op": "RM: F2 0F 38 F0 /r" , "ext": "SSE4_2"}, {"inst": "prefetch R:mem" , "op": "0F 0D /0"}
{"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"}, {"category": "GP GP_EXT", "ext": "ADX", "data": [
{"inst": "crc32 X:r64, r64/m64" , "op": "RM: REX.W F2 0F 38 F1 /r" , "ext": "SSE4_2"} {"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": [ {"category": "GP", "volatile": true, "data": [
@ -863,21 +1068,35 @@
{"inst": "xlatb" , "op": "D7"} {"inst": "xlatb" , "op": "D7"}
]}, ]},
{"category": "GP", "deprecated": true, "data": [ {"category": "GP", "deprecated": true, "arch": "X86", "data": [
{"inst": "aaa x:<ax>" , "op": "37" , "arch": "X86", "io": "OF=U SF=U ZF=U AF=W PF=U CF=W"}, {"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" , "arch": "X86", "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" , "arch": "X86", "io": "OF=U SF=W ZF=W AF=U PF=W CF=U"}, {"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" , "arch": "X86", "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" , "arch": "X86", "io": "ZF=W"}, {"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" , "arch": "X86"}, {"inst": "bound R:r16, R:m32" , "op": "RM: 66 62 /r"},
{"inst": "bound R:r32, R:m64" , "op": "RM: 62 /r" , "arch": "X86"}, {"inst": "bound R:r32, R:m64" , "op": "RM: 62 /r"},
{"inst": "daa x:<ax>" , "op": "27" , "arch": "X86", "io": "OF=U SF=W ZF=W AF=W PF=W CF=W"}, {"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" , "arch": "X86", "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" , "arch": "X86", "io": "OF=R"}, {"inst": "into" , "op": "CE" , "io": "OF=R"},
{"inst": "popa" , "op": "66 61" , "arch": "X86"}, {"inst": "popa" , "op": "66 61"},
{"inst": "popad" , "op": "61" , "arch": "X86"}, {"inst": "popad" , "op": "61"},
{"inst": "pusha" , "op": "66 60" , "arch": "X86"}, {"inst": "pusha" , "op": "66 60"},
{"inst": "pushad" , "op": "60" , "arch": "X86"} {"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": [ {"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"} {"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": [ {"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": "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": "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": "lgdt R:mem" , "op": "0F 01 /2"},
{"inst": "lidt R:mem" , "op": "0F 01 /3"}, {"inst": "lidt R:mem" , "op": "0F 01 /3"},
{"inst": "lldt R:r16/m16" , "op": "0F 00 /2"}, {"inst": "lldt R:r16/m16" , "op": "0F 00 /2"},
{"inst": "lmsw R:r16/m16" , "op": "0F 01 /6"}, {"inst": "lmsw R:r16/m16" , "op": "0F 01 /6"},
{"inst": "ltr R:r16/m16" , "op": "0F 00 /3"}, {"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": "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": "swapgs" , "op": "0F 01 F8" , "arch": "X64"},
{"inst": "sysexit" , "op": "0F 35"}, {"inst": "sysexit" , "op": "0F 35"},
{"inst": "sysexitq" , "op": "REX.W 0F 35"}, {"inst": "sysexitq" , "op": "REX.W 0F 35"},
{"inst": "sysret" , "op": "0F 07" , "arch": "X64"}, {"inst": "sysret" , "op": "0F 07" , "arch": "X64"},
{"inst": "sysretq" , "op": "REX.W 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"}, {"category": "GP", "ext": "I486", "volatile": true, "privilege": "L0", "data": [
{"inst": "wrmsr R:<edx>, R:<eax>, R:<ecx>" , "op": "0F 30" , "ext": "MSR" , "io": "MSR=W"}, {"inst": "invd" , "op": "0F 08"},
{"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": "invlpg R:mem" , "op": "0F 01 /7"},
{"inst": "wrssd W:r32/m32, r32" , "op": "MR: 0F 38 F6 /r" , "ext": "CET_SS"}, {"inst": "invpcid R:r32, R:m128" , "op": "RM: 66 0F 38 82 /r" , "arch": "X86"},
{"inst": "wrssq W:r64/m64, r64" , "op": "MR: REX.W 0F 38 F6 /r" , "ext": "CET_SS"}, {"inst": "invpcid R:r64, R:m128" , "op": "RM: 66 0F 38 82 /r" , "arch": "X64"},
{"inst": "xrstors R:mem, <edx>, <eax>" , "op": "0F C7 /3" , "ext": "XSAVES" , "io": "XCR=R"}, {"inst": "wbinvd" , "op": "0F 09"}
{"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"}, {"category": "GP GP_EXT", "ext": "CET_IBT", "volatile": true, "privilege": "L0", "data": [
{"inst": "xsaves W:mem, <edx>, <eax>" , "op": "0F C7 /5" , "ext": "XSAVES" , "io": "XCR=R"}, {"inst": "endbr32" , "op": "F3 0F 1E FB"},
{"inst": "xsaves64 W:mem, <edx>, <eax>" , "op": "REX.W 0F C7 /5" , "ext": "XSAVES" , "io": "XCR=R"}, {"inst": "endbr64" , "op": "F3 0F 1E FA"}
{"inst": "xsetbv R:<edx>, R:<eax>, R:<ecx>" , "op": "0F 01 D1" , "ext": "XSAVE" , "io": "XCR=W"} ]},
{"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": [ {"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": "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": "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": "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" , "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: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"}, {"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: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: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": "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: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": "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"}, {"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"} {"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": [ {"category": "MMX STATE", "deprecated": true, "volatile": true, "data": [
{"inst": "emms" , "op": "0F 77" , "ext": "MMX"}, {"inst": "emms" , "op": "0F 77" , "ext": "MMX"},
{"inst": "femms" , "op": "0F 0E" , "ext": "3DNOW"} {"inst": "femms" , "op": "0F 0E" , "ext": "3DNOW"}
@ -1649,7 +1817,7 @@
{"inst": "movntdqa W:xmm, m128" , "op": "RM: 66 0F 38 2A /r"}, {"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": "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": "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": "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": "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"}, {"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"} {"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": [ {"category": "SSE SIMD CRYPTO_HASH", "ext": "AESNI", "data": [
{"inst": "aesdec X:xmm, xmm/m128" , "op": "RM: 66 0F 38 DE /r"}, {"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"}, {"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"} {"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": [ {"category": "SSE SIMD CRYPTO_HASH", "ext": "SHA", "data": [
{"inst": "sha1msg1 xmm, xmm/m128" , "op": "RM: 0F 38 C9 /r"}, {"inst": "sha1msg1 xmm, xmm/m128" , "op": "RM: 0F 38 C9 /r"},
{"inst": "sha1msg2 xmm, xmm/m128" , "op": "RM: 0F 38 CA /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"} {"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": [ {"category": "AVX STATE", "ext": "AVX", "data": [
{"inst": "vldmxcsr R:m32" , "op": "VEX.LZ.0F.WIG AE /2", "io": "MXCSR=W"}, {"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"}, {"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": [ {"category": "AVX SIMD", "ext": "AVX SM3", "data": [
{"inst": "vsm3msg1 X:xmm, xmm, xmm/m128" , "op": "RVM: VEX.128.NP.0F38.W0 DA /r"}, {"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": "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": [ {"category": "AVX SIMD", "ext": "AVX SM4", "data": [

View File

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

View File

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

View File

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

View File

@ -21,12 +21,16 @@
ASMJIT_BEGIN_SUB_NAMESPACE(a64) 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 // a64::Assembler - Cond
// ===================== // =====================
static inline uint32_t condCodeToOpcodeCond(uint32_t cond) noexcept { static inline uint32_t condCodeToOpcodeCond(uint32_t cond) noexcept { return (uint32_t(cond) - 2u) & 0xFu; }
return (uint32_t(cond) - 2u) & 0xFu;
}
// a64::Assembler - Bits // 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) }; static const uint8_t armShiftOpToLdStOptMap[] = { ASMJIT_LOOKUP_TABLE_16(VALUE, 0) };
#undef VALUE #undef VALUE
static inline constexpr uint32_t diff(RegType a, RegType b) noexcept {
return uint32_t(a) - uint32_t(b);
}
// asmjit::a64::Assembler - SizeOp // asmjit::a64::Assembler - SizeOp
// =============================== // ===============================
@ -118,25 +118,25 @@ struct SizeOpTable {
}; };
#define VALUE_BIN(x) { \ #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_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) | (Vec::kElementTypeNone)) ? SizeOp::k00Q : \ 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) | (Vec::kElementTypeB )) ? SizeOp::k00 : \ 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) | (Vec::kElementTypeB )) ? SizeOp::k00Q : SizeOp::kInvalid \ x == (((uint32_t(RegType::kARM_VecV) - uint32_t(RegType::kARM_VecB)) << 3) | uint32_t(VecElementType::kB )) ? SizeOp::k00Q : SizeOp::kInvalid \
} }
#define VALUE_ANY(x) { \ #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_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) | (Vec::kElementTypeNone)) ? SizeOp::k01S : \ 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) | (Vec::kElementTypeNone)) ? SizeOp::k10S : \ 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) | (Vec::kElementTypeNone)) ? SizeOp::k11S : \ 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) | (Vec::kElementTypeB )) ? SizeOp::k00 : \ 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) | (Vec::kElementTypeB )) ? SizeOp::k00Q : \ 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) | (Vec::kElementTypeH )) ? SizeOp::k01 : \ 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) | (Vec::kElementTypeH )) ? SizeOp::k01Q : \ 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) | (Vec::kElementTypeS )) ? SizeOp::k10 : \ 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) | (Vec::kElementTypeS )) ? SizeOp::k10Q : \ 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) | (Vec::kElementTypeD )) ? SizeOp::k11S : \ 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) | (Vec::kElementTypeD )) ? SizeOp::k11Q : SizeOp::kInvalid \ 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] = { 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; 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. // Instruction data or Assembler is wrong if this triggers an assertion failure.
ASMJIT_ASSERT(vecOpType < InstDB::kVO_Count); ASMJIT_ASSERT(vecOpType < InstDB::kVO_Count);
// ElementType uses 3 bits in the operand signature, it should never overflow. // 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 SizeOpMap& map = sizeOpMap[vecOpType];
const SizeOpTable& table = sizeOpTable[map.tableId]; 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 op = table.array[index];
SizeOp modifiedOp { uint8_t(op.value & map.sizeOpMask) }; 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 { 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: // Memory must be either:
@ -537,7 +537,7 @@ static inline bool pickFpOpcode(const Vec& reg, uint32_t sOp, uint32_t sHf, uint
else { else {
// Vector operation [HSD]. // Vector operation [HSD].
uint32_t q = diff(reg.type(), RegType::kARM_VecD); 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)) if (q > 1u || sz > 2u || !Support::bitTest(szBits[vHf].sizeMask, sz))
return false; return false;
@ -716,8 +716,6 @@ static inline bool checkValidRegs(const Operand_& o0, const Operand_& o1, const
Assembler::Assembler(CodeHolder* code) noexcept : BaseAssembler() { Assembler::Assembler(CodeHolder* code) noexcept : BaseAssembler() {
_archMask = uint64_t(1) << uint32_t(Arch::kAArch64); _archMask = uint64_t(1) << uint32_t(Arch::kAArch64);
assignEmitterFuncs(this);
if (code) if (code)
code->attach(this); code->attach(this);
} }
@ -803,7 +801,7 @@ Error Assembler::_emit(InstId instId, const Operand_& o0, const Operand_& o1, co
Operand_ opArray[Globals::kMaxOpCount]; Operand_ opArray[Globals::kMaxOpCount];
EmitterUtils::opArrayFromEmitArgs(opArray, o0, o1, o2, opExt); 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)) if (ASMJIT_UNLIKELY(err))
goto Failed; goto Failed;
} }
@ -2241,6 +2239,86 @@ Error Assembler::_emit(InstId instId, const Operand_& o0, const Operand_& o1, co
break; 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] // [Base - Load / Store]
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
@ -2272,7 +2350,7 @@ Error Assembler::_emit(InstId instId, const Operand_& o0, const Operand_& o1, co
if (m.hasBaseReg()) { if (m.hasBaseReg()) {
// [Base {Offset | Index}] // [Base {Offset | Index}]
if (m.hasIndex()) { if (m.hasIndex()) {
uint32_t opt = armShiftOpToLdStOptMap[m.predicate()]; uint32_t opt = armShiftOpToLdStOptMap[size_t(m.shiftOp())];
if (opt == 0xFF) if (opt == 0xFF)
goto InvalidAddress; goto InvalidAddress;
@ -2690,7 +2768,7 @@ Case_BaseLdurStur:
// hD, vS.{4|8}h (16-bit) // hD, vS.{4|8}h (16-bit)
// sD, vS.4s (32-bit) // sD, vS.4s (32-bit)
uint32_t sz = diff(o0.as<Reg>().type(), RegType::kARM_VecH); 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. // Size greater than 1 means 64-bit elements, not supported.
if ((sz | elementSz) > 1 || sz != elementSz) if ((sz | elementSz) > 1 || sz != elementSz)
@ -2812,7 +2890,7 @@ Case_BaseLdurStur:
if (q > 1) if (q > 1)
goto InvalidInstruction; 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) if (sz == 0 || sz > 3)
goto InvalidInstruction; goto InvalidInstruction;
@ -2904,7 +2982,7 @@ Case_BaseLdurStur:
if (q > 1) if (q > 1)
goto InvalidInstruction; 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) if (sz == 0 || sz > 3)
goto InvalidInstruction; goto InvalidInstruction;
@ -3086,11 +3164,11 @@ Case_BaseLdurStur:
if (uint32_t(opcode.hasQ()) != q) if (uint32_t(opcode.hasQ()) != q)
goto InvalidInstruction; goto InvalidInstruction;
if (rL.isVecS4() && rN.elementType() == Vec::kElementTypeH && !opData.isCvtxn()) { if (rL.isVecS4() && rN.elementType() == VecElementType::kH && !opData.isCvtxn()) {
goto EmitOp_Rd0_Rn5; goto EmitOp_Rd0_Rn5;
} }
if (rL.isVecD2() && rN.elementType() == Vec::kElementTypeS) { if (rL.isVecD2() && rN.elementType() == VecElementType::kS) {
opcode |= B(22); opcode |= B(22);
goto EmitOp_Rd0_Rn5; goto EmitOp_Rd0_Rn5;
} }
@ -3201,8 +3279,8 @@ Case_BaseLdurStur:
} }
if (uint32_t(o0.as<Reg>().type()) != uint32_t(o1.as<Reg>().type()) + qIsOptional || if (uint32_t(o0.as<Reg>().type()) != uint32_t(o1.as<Reg>().type()) + qIsOptional ||
o0.as<Vec>().elementType() != opData.tA || uint32_t(o0.as<Vec>().elementType()) != opData.tA ||
o1.as<Vec>().elementType() != opData.tB) uint32_t(o1.as<Vec>().elementType()) != opData.tB)
goto InvalidInstruction; goto InvalidInstruction;
if (!o2.as<Vec>().hasElementIndex()) { if (!o2.as<Vec>().hasElementIndex()) {
@ -3214,7 +3292,7 @@ Case_BaseLdurStur:
goto EmitOp_Rd0_Rn5_Rm16; goto EmitOp_Rd0_Rn5_Rm16;
} }
else { else {
if (o2.as<Vec>().elementType() != opData.tElement) if (uint32_t(o2.as<Vec>().elementType()) != opData.tElement)
goto InvalidInstruction; goto InvalidInstruction;
if (o2.as<Reg>().id() > 15) if (o2.as<Reg>().id() > 15)
@ -3364,7 +3442,7 @@ Case_BaseLdurStur:
} }
else { else {
uint32_t q = diff(o0.as<Vec>().type(), RegType::kARM_VecD); 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) if (q > 1 || sz > 2)
goto InvalidInstruction; goto InvalidInstruction;
@ -3418,7 +3496,7 @@ Case_BaseLdurStur:
if (q > 1) if (q > 1)
goto InvalidInstruction; goto InvalidInstruction;
uint32_t sz = o0.as<Vec>().elementType() - Vec::kElementTypeH; uint32_t sz = diff(o0.as<Vec>().elementType(), VecElementType::kH);
if (sz > 2) if (sz > 2)
goto InvalidInstruction; goto InvalidInstruction;
@ -3442,7 +3520,7 @@ Case_BaseLdurStur:
if (isign4 == ENC_OPS2(Reg, Reg)) { if (isign4 == ENC_OPS2(Reg, Reg)) {
// The first destination operand is scalar, which matches element-type of source vectors. // The first destination operand is scalar, which matches element-type of source vectors.
uint32_t L = (instFlags & InstDB::kInstFlagLong) != 0; 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; goto InvalidInstruction;
SizeOp sizeOp = armElementTypeToSizeOp(opData.vecOpType, o1.as<Reg>().type(), o1.as<Vec>().elementType()); SizeOp sizeOp = armElementTypeToSizeOp(opData.vecOpType, o1.as<Reg>().type(), o1.as<Vec>().elementType());
@ -3542,7 +3620,7 @@ Case_BaseLdurStur:
if (!sizeOp.isValid()) if (!sizeOp.isValid())
goto InvalidInstruction; 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; goto InvalidInstruction;
opcode.reset(opData.opcode()); 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()) if (o0.as<Reg>().type() != o1.as<Reg>().type() || o1.as<Reg>().type() != o2.as<Reg>().type())
goto InvalidInstruction; goto InvalidInstruction;
if (o0.as<Vec>().elementType() != opData.tA || if (uint32_t(o0.as<Vec>().elementType()) != opData.tA ||
o1.as<Vec>().elementType() != opData.tB || uint32_t(o1.as<Vec>().elementType()) != opData.tB ||
o2.as<Vec>().elementType() != opData.tB) uint32_t(o2.as<Vec>().elementType()) != opData.tB)
goto InvalidInstruction; goto InvalidInstruction;
opcode.reset(uint32_t(opData.vectorOp) << 10); 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()) if (o0.as<Reg>().type() != o1.as<Reg>().type() || !o2.as<Reg>().isVecV())
goto InvalidInstruction; goto InvalidInstruction;
if (o0.as<Vec>().elementType() != opData.tA || if (uint32_t(o0.as<Vec>().elementType()) != opData.tA ||
o1.as<Vec>().elementType() != opData.tB || uint32_t(o1.as<Vec>().elementType()) != opData.tB ||
o2.as<Vec>().elementType() != opData.tElement) uint32_t(o2.as<Vec>().elementType()) != opData.tElement)
goto InvalidInstruction; goto InvalidInstruction;
uint32_t elementIndex = o2.as<Vec>().elementIndex(); uint32_t elementIndex = o2.as<Vec>().elementIndex();
@ -3871,13 +3949,13 @@ Case_BaseLdurStur:
case InstDB::kEncodingSimdDup: SimdDup: { case InstDB::kEncodingSimdDup: SimdDup: {
if (isign4 == ENC_OPS2(Reg, Reg)) { if (isign4 == ENC_OPS2(Reg, Reg)) {
// Truth table of valid encodings of `Q:1|ElementType:3` // Truth table of valid encodings of `Q:1|ElementType:3`
uint32_t kValidEncodings = B(Vec::kElementTypeB + 0) | uint32_t kValidEncodings = B(uint32_t(VecElementType::kB) + 0) |
B(Vec::kElementTypeH + 0) | B(uint32_t(VecElementType::kH) + 0) |
B(Vec::kElementTypeS + 0) | B(uint32_t(VecElementType::kS) + 0) |
B(Vec::kElementTypeB + 8) | B(uint32_t(VecElementType::kB) + 8) |
B(Vec::kElementTypeH + 8) | B(uint32_t(VecElementType::kH) + 8) |
B(Vec::kElementTypeS + 8) | B(uint32_t(VecElementType::kS) + 8) |
B(Vec::kElementTypeD + 8) ; B(uint32_t(VecElementType::kD) + 8) ;
uint32_t q = diff(o0.as<Reg>().type(), RegType::kARM_VecD); 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 // 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). // 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)) if (q > 1 || !Support::bitTest(kValidEncodings, (q << 3) | elementType))
goto InvalidInstruction; goto InvalidInstruction;
@ -3907,7 +3985,7 @@ Case_BaseLdurStur:
// DUP - Vec (scalar) <- Vec[N]. // DUP - Vec (scalar) <- Vec[N].
uint32_t lsbIndex = diff(o0.as<Reg>().type(), RegType::kARM_VecB); 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; goto InvalidInstruction;
uint32_t imm5 = ((dstIndex << 1) | 1u) << lsbIndex; uint32_t imm5 = ((dstIndex << 1) | 1u) << lsbIndex;
@ -3920,7 +3998,7 @@ Case_BaseLdurStur:
} }
else { else {
// DUP - Vec (all) <- Vec[N]. // 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)) if (q > 1 || !Support::bitTest(kValidEncodings, (q << 3) | elementType))
goto InvalidInstruction; goto InvalidInstruction;
@ -3945,7 +4023,7 @@ Case_BaseLdurStur:
if (!o0.as<Vec>().hasElementIndex()) if (!o0.as<Vec>().hasElementIndex())
goto InvalidInstruction; 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 dstIndex = o0.as<Vec>().elementIndex();
uint32_t lsbIndex = elementType - 1u; uint32_t lsbIndex = elementType - 1u;
@ -4114,7 +4192,6 @@ Case_BaseLdurStur:
if (inverted) { if (inverted) {
imm8 = ~imm8 & 0xFFu; imm8 = ~imm8 & 0xFFu;
inverted = 0;
} }
cmode = B(3) | B(2) | B(1); cmode = B(3) | B(2) | B(1);
@ -4147,7 +4224,6 @@ Case_BaseLdurStur:
case 3: case 3:
if (inverted) { if (inverted) {
imm8 = ~imm8 & 0xFFu; imm8 = ~imm8 & 0xFFu;
inverted = 0;
} }
op = 1; op = 1;
@ -4435,7 +4511,7 @@ Case_BaseLdurStur:
if (m.hasBaseReg()) { if (m.hasBaseReg()) {
// [Base {Offset | Index}] // [Base {Offset | Index}]
if (m.hasIndex()) { if (m.hasIndex()) {
uint32_t opt = armShiftOpToLdStOptMap[m.predicate()]; uint32_t opt = armShiftOpToLdStOptMap[size_t(m.shiftOp())];
if (opt == 0xFFu) if (opt == 0xFFu)
goto InvalidAddress; goto InvalidAddress;
@ -4659,7 +4735,7 @@ Case_SimdLdurStur:
uint32_t q = 0; uint32_t q = 0;
uint32_t rm = 0; uint32_t rm = 0;
uint32_t rn = m.baseId(); 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 opcSsize = sz;
uint32_t offsetPossibility = 0; uint32_t offsetPossibility = 0;
@ -5091,6 +5167,10 @@ Error Assembler::align(AlignMode alignMode, uint32_t alignment) {
Error Assembler::onAttach(CodeHolder* code) noexcept { Error Assembler::onAttach(CodeHolder* code) noexcept {
ASMJIT_PROPAGATE(Base::onAttach(code)); ASMJIT_PROPAGATE(Base::onAttach(code));
_instructionAlignment = uint8_t(4);
assignEmitterFuncs(this);
return kErrorOk; return kErrorOk;
} }

View File

@ -23,7 +23,7 @@ class ASMJIT_VIRTAPI Assembler
public: public:
typedef BaseAssembler Base; typedef BaseAssembler Base;
//! \name Construction / Destruction //! \name Construction & Destruction
//! \{ //! \{
ASMJIT_API Assembler(CodeHolder* code = nullptr) noexcept; 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 //! \name Emit
//! \{ //! \{

View File

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

View File

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

View File

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

View File

@ -169,7 +169,7 @@ Error EmitHelper::emitArgMove(
if (TypeUtils::isInt(dstTypeId)) { if (TypeUtils::isInt(dstTypeId)) {
if (TypeUtils::isInt(srcTypeId)) { 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)}); dst.setSignature(OperandSignature{x ? uint32_t(GpX::kSignature) : uint32_t(GpW::kSignature)});
_emitter->setInlineComment(comment); _emitter->setInlineComment(comment);
@ -186,7 +186,7 @@ Error EmitHelper::emitArgMove(
case TypeId::kInt16: instId = Inst::kIdLdrsh; break; case TypeId::kInt16: instId = Inst::kIdLdrsh; break;
case TypeId::kUInt16: instId = Inst::kIdLdrh; break; case TypeId::kUInt16: instId = Inst::kIdLdrh; break;
case TypeId::kInt32: instId = x ? Inst::kIdLdrsw : Inst::kIdLdr; 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::kInt64: instId = Inst::kIdLdr; break;
case TypeId::kUInt64: instId = Inst::kIdLdr; break; case TypeId::kUInt64: instId = Inst::kIdLdr; break;
default: default:
@ -312,6 +312,12 @@ ASMJIT_FAVOR_SIZE Error EmitHelper::emitProlog(const FuncFrame& frame) {
{ Inst::kIdStr_v, Inst::kIdStp_v } { 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; uint32_t adjustInitialOffset = pei.sizeTotal;
for (RegGroup group : Support::EnumValues<RegGroup, RegGroup::kGp, RegGroup::kVec>{}) { for (RegGroup group : Support::EnumValues<RegGroup, RegGroup::kGp, RegGroup::kVec>{}) {
@ -339,7 +345,7 @@ ASMJIT_FAVOR_SIZE Error EmitHelper::emitProlog(const FuncFrame& frame) {
else else
ASMJIT_PROPAGATE(emitter->emit(insts.pairInstId, regs[0], regs[1], mem)); ASMJIT_PROPAGATE(emitter->emit(insts.pairInstId, regs[0], regs[1], mem));
mem.resetToFixedOffset(); mem.resetOffsetMode();
if (i == 0 && frame.hasPreservedFP()) { if (i == 0 && frame.hasPreservedFP()) {
ASMJIT_PROPAGATE(emitter->mov(x29, sp)); ASMJIT_PROPAGATE(emitter->mov(x29, sp));
@ -421,7 +427,7 @@ ASMJIT_FAVOR_SIZE Error EmitHelper::emitEpilog(const FuncFrame& frame) {
else else
ASMJIT_PROPAGATE(emitter->emit(insts.pairInstId, regs[0], regs[1], mem)); ASMJIT_PROPAGATE(emitter->emit(insts.pairInstId, regs[0], regs[1], mem));
mem.resetToFixedOffset(); mem.resetOffsetMode();
} }
} }

View File

@ -79,11 +79,22 @@ struct EmitterExplicitT {
// These two are unfortunately reported by the sanitizer. We know what we do, however, the sanitizer doesn't. // 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. // 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 ASMJIT_INLINE_NODEBUG 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 const This* _emitter() const noexcept { return static_cast<const This*>(this); }
//! \endcond //! \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 //! \name General Purpose Instructions
//! \{ //! \{
@ -514,6 +525,8 @@ struct EmitterExplicitT {
ASMJIT_INST_2x(ldxrb, Ldxrb, Gp, Mem) ASMJIT_INST_2x(ldxrb, Ldxrb, Gp, Mem)
ASMJIT_INST_2x(ldxrh, Ldxrh, 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(stadd, Stadd, Gp, Mem)
ASMJIT_INST_2x(staddb, Staddb, Gp, Mem) ASMJIT_INST_2x(staddb, Staddb, Gp, Mem)
ASMJIT_INST_2x(staddh, Staddh, Gp, Mem) ASMJIT_INST_2x(staddh, Staddh, Gp, Mem)

View File

@ -29,12 +29,10 @@ ASMJIT_FAVOR_SIZE Error FormatterInternal::formatInstruction(
Arch arch, Arch arch,
const BaseInst& inst, const Operand_* operands, size_t opCount) noexcept { const BaseInst& inst, const Operand_* operands, size_t opCount) noexcept {
DebugUtils::unused(arch);
// Format instruction options and instruction mnemonic. // Format instruction options and instruction mnemonic.
InstId instId = inst.realId(); InstId instId = inst.realId();
if (instId < Inst::_kIdCount) if (instId != Inst::kIdNone && instId < Inst::_kIdCount)
ASMJIT_PROPAGATE(InstInternal::instIdToString(arch, instId, sb)); ASMJIT_PROPAGATE(InstInternal::instIdToString(instId, sb));
else else
ASMJIT_PROPAGATE(sb.appendFormat("[InstId=#%u]", unsigned(instId))); 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 { ASMJIT_FAVOR_SIZE Error initCallConv(CallConv& cc, CallConvId ccId, const Environment& environment) noexcept {
cc.setArch(environment.arch()); cc.setArch(environment.arch());
cc.setStrategy(environment.isDarwin() ? CallConvStrategy::kAArch64Apple : CallConvStrategy::kDefault);
cc.setSaveRestoreRegSize(RegGroup::kGp, 8); cc.setSaveRestoreRegSize(RegGroup::kGp, 8);
cc.setSaveRestoreRegSize(RegGroup::kVec, 8); cc.setSaveRestoreRegSize(RegGroup::kVec, 8);
cc.setSaveRestoreAlignment(RegGroup::kGp, 16); cc.setSaveRestoreAlignment(RegGroup::kGp, 16);
cc.setSaveRestoreAlignment(RegGroup::kVec, 16); cc.setSaveRestoreAlignment(RegGroup::kVec, 16);
cc.setSaveRestoreAlignment(RegGroup::kExtraVirt2, 1); cc.setSaveRestoreAlignment(RegGroup::kMask, 1);
cc.setSaveRestoreAlignment(RegGroup::kExtraVirt3, 1); cc.setSaveRestoreAlignment(RegGroup::kExtraVirt3, 1);
cc.setPassedOrder(RegGroup::kGp, 0, 1, 2, 3, 4, 5, 6, 7); cc.setPassedOrder(RegGroup::kGp, 0, 1, 2, 3, 4, 5, 6, 7);
cc.setPassedOrder(RegGroup::kVec, 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; 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); DebugUtils::unused(signature);
const CallConv& cc = func.callConv(); const CallConv& cc = func.callConv();
@ -77,6 +78,13 @@ ASMJIT_FAVOR_SIZE Error initFuncDetail(FuncDetail& func, const FuncSignature& si
uint32_t i; uint32_t i;
uint32_t argCount = func.argCount(); 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()) { if (func.hasRet()) {
for (uint32_t valueIndex = 0; valueIndex < Globals::kMaxValuePack; valueIndex++) { for (uint32_t valueIndex = 0; valueIndex < Globals::kMaxValuePack; valueIndex++) {
TypeId typeId = func._rets[valueIndex].typeId(); TypeId typeId = func._rets[valueIndex].typeId();
@ -119,7 +127,8 @@ ASMJIT_FAVOR_SIZE Error initFuncDetail(FuncDetail& func, const FuncSignature& si
} }
switch (cc.strategy()) { switch (cc.strategy()) {
case CallConvStrategy::kDefault: { case CallConvStrategy::kDefault:
case CallConvStrategy::kAArch64Apple: {
uint32_t gpzPos = 0; uint32_t gpzPos = 0;
uint32_t vecPos = 0; uint32_t vecPos = 0;
@ -140,7 +149,9 @@ ASMJIT_FAVOR_SIZE Error initFuncDetail(FuncDetail& func, const FuncSignature& si
gpzPos++; gpzPos++;
} }
else { 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)); arg.assignStackOffset(int32_t(stackOffset));
stackOffset += size; stackOffset += size;
} }
@ -164,7 +175,9 @@ ASMJIT_FAVOR_SIZE Error initFuncDetail(FuncDetail& func, const FuncSignature& si
vecPos++; vecPos++;
} }
else { 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)); arg.assignStackOffset(int32_t(stackOffset));
stackOffset += size; stackOffset += size;
} }
@ -178,7 +191,7 @@ ASMJIT_FAVOR_SIZE Error initFuncDetail(FuncDetail& func, const FuncSignature& si
return DebugUtils::errored(kErrorInvalidState); return DebugUtils::errored(kErrorInvalidState);
} }
func._argStackSize = stackOffset; func._argStackSize = Support::alignUp(stackOffset, 8u);
return kErrorOk; return kErrorOk;
} }

View File

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

View File

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

View File

@ -15,65 +15,22 @@
ASMJIT_BEGIN_SUB_NAMESPACE(a64) ASMJIT_BEGIN_SUB_NAMESPACE(a64)
namespace InstInternal {
// a64::InstInternal - Text // a64::InstInternal - Text
// ======================== // ========================
#ifndef ASMJIT_NO_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); uint32_t realId = instId & uint32_t(InstIdParts::kRealId);
DebugUtils::unused(arch);
if (ASMJIT_UNLIKELY(!Inst::isDefinedId(realId))) if (ASMJIT_UNLIKELY(!Inst::isDefinedId(realId)))
return DebugUtils::errored(kErrorInvalidInstruction); return DebugUtils::errored(kErrorInvalidInstruction);
return InstNameUtils::decode(output, InstDB::_instNameIndexTable[realId], InstDB::_instNameStringTable);
char nameData[32];
size_t nameSize = Support::decodeInstName(nameData, InstDB::_instNameIndexTable[realId], InstDB::_instNameStringTable);
return output.append(nameData, nameSize);
} }
InstId InstInternal::stringToInstId(Arch arch, const char* s, size_t len) noexcept { InstId stringToInstId(const char* s, size_t len) noexcept {
DebugUtils::unused(arch); return InstNameUtils::find(s, len, InstDB::instNameIndex, InstDB::_instNameIndexTable, InstDB::_instNameStringTable);
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;
} }
#endif // !ASMJIT_NO_TEXT #endif // !ASMJIT_NO_TEXT
@ -81,9 +38,9 @@ InstId InstInternal::stringToInstId(Arch arch, const char* s, size_t len) noexce
// ============================ // ============================
#ifndef ASMJIT_NO_VALIDATION #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: // TODO:
DebugUtils::unused(arch, inst, operands, opCount, validationFlags); DebugUtils::unused(inst, operands, opCount, validationFlags);
return kErrorOk; return kErrorOk;
} }
#endif // !ASMJIT_NO_VALIDATION #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 }; 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 { Error queryRWInfo(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));
// Get the instruction data. // Get the instruction data.
uint32_t realId = inst.id() & uint32_t(InstIdParts::kRealId); 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.isReg()) {
if (srcOp.as<Vec>().hasElementIndex()) { if (srcOp.as<Vec>().hasElementIndex()) {
// Only part of the vector is accessed if element index [] is used. // 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 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); uint64_t accessMask = uint64_t(Support::lsbMask<uint32_t>(elementSize)) << (elementIndex * elementSize);
op._readByteMask &= accessMask; op._readByteMask &= accessMask;
@ -243,8 +194,7 @@ Error InstInternal::queryRWInfo(Arch arch, const BaseInst& inst, const Operand_*
} }
if (memOp.hasIndex()) { if (memOp.hasIndex()) {
op.addOpFlags(OpRWFlags::kMemIndexRead); op.addOpFlags(memOp.isPreOrPost() ? OpRWFlags::kMemIndexRW : OpRWFlags::kMemIndexRead);
op.addOpFlags(memOp.isPreOrPost() ? OpRWFlags::kMemIndexWrite : OpRWFlags::kNone);
} }
} }
} }
@ -258,13 +208,15 @@ Error InstInternal::queryRWInfo(Arch arch, const BaseInst& inst, const Operand_*
// ================================= // =================================
#ifndef ASMJIT_NO_INTROSPECTION #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. // TODO: [ARM] QueryFeatures not implemented yet.
DebugUtils::unused(arch, inst, operands, opCount, out); DebugUtils::unused(inst, operands, opCount, out);
return kErrorOk; return kErrorOk;
} }
#endif // !ASMJIT_NO_INTROSPECTION #endif // !ASMJIT_NO_INTROSPECTION
} // {InstInternal}
// a64::InstInternal - Unit // a64::InstInternal - Unit
// ======================== // ========================

View File

@ -18,17 +18,17 @@ ASMJIT_BEGIN_SUB_NAMESPACE(a64)
namespace InstInternal { namespace InstInternal {
#ifndef ASMJIT_NO_TEXT #ifndef ASMJIT_NO_TEXT
Error ASMJIT_CDECL instIdToString(Arch arch, InstId instId, String& output) noexcept; Error ASMJIT_CDECL instIdToString(InstId instId, String& output) noexcept;
InstId ASMJIT_CDECL stringToInstId(Arch arch, const char* s, size_t len) noexcept; InstId ASMJIT_CDECL stringToInstId(const char* s, size_t len) noexcept;
#endif // !ASMJIT_NO_TEXT #endif // !ASMJIT_NO_TEXT
#ifndef ASMJIT_NO_VALIDATION #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 #endif // !ASMJIT_NO_VALIDATION
#ifndef ASMJIT_NO_INTROSPECTION #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 queryRWInfo(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 queryFeatures(const BaseInst& inst, const Operand_* operands, size_t opCount, CpuFeatures* out) noexcept;
#endif // !ASMJIT_NO_INTROSPECTION #endif // !ASMJIT_NO_INTROSPECTION
} // {InstInternal} } // {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. //! SIMD element access of half-words can only be used with v0..15.
kInstFlagVH0_15 = 0x00000010u, 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 kInstFlagConsecutive = 0x00000080u
}; };

View File

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

View File

@ -56,7 +56,7 @@ UNIT(a64_operand) {
EXPECT_EQ(vd_1.group(), RegGroup::kVec); EXPECT_EQ(vd_1.group(), RegGroup::kVec);
EXPECT_EQ(vd_1.id(), 15u); EXPECT_EQ(vd_1.id(), 15u);
EXPECT_TRUE(vd_1.isVecD2()); 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_TRUE(vd_1.hasElementIndex());
EXPECT_EQ(vd_1.elementIndex(), 1u); EXPECT_EQ(vd_1.elementIndex(), 1u);
@ -65,7 +65,7 @@ UNIT(a64_operand) {
EXPECT_EQ(vs_3.group(), RegGroup::kVec); EXPECT_EQ(vs_3.group(), RegGroup::kVec);
EXPECT_EQ(vs_3.id(), 15u); EXPECT_EQ(vs_3.id(), 15u);
EXPECT_TRUE(vs_3.isVecS4()); 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_TRUE(vs_3.hasElementIndex());
EXPECT_EQ(vs_3.elementIndex(), 3u); EXPECT_EQ(vs_3.elementIndex(), 3u);
@ -74,7 +74,7 @@ UNIT(a64_operand) {
EXPECT_EQ(vb_4.group(), RegGroup::kVec); EXPECT_EQ(vb_4.group(), RegGroup::kVec);
EXPECT_EQ(vb_4.id(), 15u); EXPECT_EQ(vb_4.id(), 15u);
EXPECT_TRUE(vb_4.isVecB4x4()); 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_TRUE(vb_4.hasElementIndex());
EXPECT_EQ(vb_4.elementIndex(), 3u); EXPECT_EQ(vb_4.elementIndex(), 3u);
} }

View File

@ -13,24 +13,276 @@ ASMJIT_BEGIN_SUB_NAMESPACE(a64)
//! \addtogroup asmjit_a64 //! \addtogroup asmjit_a64
//! \{ //! \{
using arm::Reg; class GpW;
using arm::Mem; class GpX;
using arm::Gp;
using arm::GpW;
using arm::GpX;
using arm::Vec; class VecB;
using arm::VecB; class VecH;
using arm::VecH; class VecS;
using arm::VecS; class VecD;
using arm::VecD; class VecV;
using arm::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 #ifndef _DOXYGEN
namespace regs { namespace regs {
#endif #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 w0 = GpW(0);
static constexpr GpW w1 = GpW(1); static constexpr GpW w1 = GpW(1);
@ -305,8 +557,94 @@ static constexpr VecV v31 = VecV(31);
using namespace regs; using namespace regs;
#endif #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 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 #endif // ASMJIT_ARM_A64OPERAND_H_INCLUDED

View File

@ -131,7 +131,7 @@ Error RACFGBuilder::onInst(InstNode* inst, InstControlFlow& controlType, RAInstB
InstId instId = inst->id(); InstId instId = inst->id();
uint32_t opCount = inst->opCount(); uint32_t opCount = inst->opCount();
const Operand* opArray = inst->operands(); 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); const InstDB::InstInfo& instInfo = InstDB::infoById(instId);
uint32_t singleRegOps = 0; uint32_t singleRegOps = 0;
@ -230,7 +230,7 @@ Error RACFGBuilder::onInst(InstNode* inst, InstControlFlow& controlType, RAInstB
if (reg.as<Vec>().hasElementIndex()) { if (reg.as<Vec>().hasElementIndex()) {
// Only the first 0..15 registers can be used if the register uses // Only the first 0..15 registers can be used if the register uses
// element accessor that accesses half-words (h[0..7] elements). // 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)) if (Support::test(flags, RATiedFlags::kUse))
useId &= 0x0000FFFFu; useId &= 0x0000FFFFu;
else else
@ -595,14 +595,14 @@ void ARMRAPass::onInit() noexcept {
_archTraits = &ArchTraits::byArch(arch); _archTraits = &ArchTraits::byArch(arch);
_physRegCount.set(RegGroup::kGp, 32); _physRegCount.set(RegGroup::kGp, 32);
_physRegCount.set(RegGroup::kVec, 32); _physRegCount.set(RegGroup::kVec, 32);
_physRegCount.set(RegGroup::kExtraVirt2, 0); _physRegCount.set(RegGroup::kMask, 0);
_physRegCount.set(RegGroup::kExtraVirt3, 0); _physRegCount.set(RegGroup::kExtraVirt3, 0);
_buildPhysIndex(); _buildPhysIndex();
_availableRegCount = _physRegCount; _availableRegCount = _physRegCount;
_availableRegs[RegGroup::kGp] = Support::lsbMask<uint32_t>(_physRegCount.get(RegGroup::kGp)); _availableRegs[RegGroup::kGp] = Support::lsbMask<uint32_t>(_physRegCount.get(RegGroup::kGp));
_availableRegs[RegGroup::kVec] = Support::lsbMask<uint32_t>(_physRegCount.get(RegGroup::kVec)); _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)); _availableRegs[RegGroup::kExtraVirt3] = Support::lsbMask<uint32_t>(_physRegCount.get(RegGroup::kExtraVirt3));
_scratchRegIndexes[0] = uint8_t(27); _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. // make unavailable all registers that are special and cannot be used in general.
bool hasFP = _func->frame().hasPreservedFP(); 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::kIdFp);
makeUnavailable(RegGroup::kGp, Gp::kIdSp); makeUnavailable(RegGroup::kGp, Gp::kIdSp);

View File

@ -9,7 +9,7 @@
#include "../core/misc_p.h" #include "../core/misc_p.h"
#include "../core/support.h" #include "../core/support.h"
#include "../arm/armformatter_p.h" #include "../arm/armformatter_p.h"
#include "../arm/armoperand.h" #include "../arm/a64operand.h"
#include "../arm/a64instapi_p.h" #include "../arm/a64instapi_p.h"
#include "../arm/a64instdb_p.h" #include "../arm/a64instdb_p.h"
@ -31,26 +31,45 @@ Error FormatterInternal::formatFeature(String& sb, uint32_t featureId) noexcept
"ARMv8a\0" "ARMv8a\0"
"THUMB\0" "THUMB\0"
"THUMBv2\0" "THUMBv2\0"
"ABLE\0"
"ADERR\0"
"AES\0" "AES\0"
"AFP\0" "AFP\0"
"AIE\0"
"AMU1\0"
"AMU1_1\0"
"ANERR\0"
"ASIMD\0" "ASIMD\0"
"BF16\0" "BF16\0"
"BRBE\0"
"BTI\0" "BTI\0"
"BWE\0"
"CCIDX\0" "CCIDX\0"
"CHK\0" "CHK\0"
"CLRBHB\0" "CLRBHB\0"
"CMOW\0"
"CONSTPACFIELD\0"
"CPA\0"
"CPA2\0"
"CPUID\0" "CPUID\0"
"CRC32\0" "CRC32\0"
"CSSC\0" "CSSC\0"
"CSV2\0"
"CSV2_3\0"
"CSV3\0"
"D128\0" "D128\0"
"DGH\0" "DGH\0"
"DIT\0" "DIT\0"
"DOTPROD\0" "DOTPROD\0"
"DPB\0" "DPB\0"
"DPB2\0" "DPB2\0"
"EBEP\0"
"EBF16\0" "EBF16\0"
"ECBHB\0"
"ECV\0" "ECV\0"
"EDHSR\0"
"EDSP\0" "EDSP\0"
"FAMINMAX\0"
"FCMA\0" "FCMA\0"
"FGT\0" "FGT\0"
"FGT2\0" "FGT2\0"
@ -61,13 +80,25 @@ Error FormatterInternal::formatFeature(String& sb, uint32_t featureId) noexcept
"FP\0" "FP\0"
"FP16\0" "FP16\0"
"FP16CONV\0" "FP16CONV\0"
"FP8\0"
"FP8DOT2\0"
"FP8DOT4\0"
"FP8FMA\0"
"FPMR\0"
"FRINTTS\0" "FRINTTS\0"
"GCS\0" "GCS\0"
"HACDBS\0"
"HAFDBS\0"
"HAFT\0"
"HDBSS\0"
"HBC\0" "HBC\0"
"HCX\0" "HCX\0"
"HPDS\0"
"HPDS2\0"
"I8MM\0" "I8MM\0"
"IDIVA\0" "IDIVA\0"
"IDIVT\0" "IDIVT\0"
"ITE\0"
"JSCVT\0" "JSCVT\0"
"LOR\0" "LOR\0"
"LRCPC\0" "LRCPC\0"
@ -79,12 +110,24 @@ Error FormatterInternal::formatFeature(String& sb, uint32_t featureId) noexcept
"LSE\0" "LSE\0"
"LSE128\0" "LSE128\0"
"LSE2\0" "LSE2\0"
"LUT\0"
"LVA\0"
"LVA3\0"
"MEC\0"
"MOPS\0" "MOPS\0"
"MPAM\0" "MPAM\0"
"MTE\0" "MTE\0"
"MTE2\0" "MTE2\0"
"MTE3\0" "MTE3\0"
"MTE4\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" "NMI\0"
"NV\0" "NV\0"
"NV2\0" "NV2\0"
@ -92,19 +135,28 @@ Error FormatterInternal::formatFeature(String& sb, uint32_t featureId) noexcept
"PAN2\0" "PAN2\0"
"PAN3\0" "PAN3\0"
"PAUTH\0" "PAUTH\0"
"PFAR\0"
"PMU\0" "PMU\0"
"PMULL\0" "PMULL\0"
"PRFMSLC\0" "PRFMSLC\0"
"RAS\0" "RAS\0"
"RAS1_1\0" "RAS1_1\0"
"RAS2\0" "RAS2\0"
"RASSA2\0"
"RDM\0" "RDM\0"
"RME\0" "RME\0"
"RNG\0" "RNG\0"
"RNG_TRAP\0" "RNG_TRAP\0"
"RPRES\0" "RPRES\0"
"RPRFM\0" "RPRFM\0"
"S1PIE\0"
"S1POE\0"
"S2PIE\0"
"S2POE\0"
"SB\0" "SB\0"
"SCTLR2\0"
"SEBEP\0"
"SEL2\0"
"SHA1\0" "SHA1\0"
"SHA256\0" "SHA256\0"
"SHA3\0" "SHA3\0"
@ -121,14 +173,32 @@ Error FormatterInternal::formatFeature(String& sb, uint32_t featureId) noexcept
"SME_F16F32\0" "SME_F16F32\0"
"SME_F32F32\0" "SME_F32F32\0"
"SME_F64F64\0" "SME_F64F64\0"
"SME_F8F16\0"
"SME_F8F32\0"
"SME_FA64\0" "SME_FA64\0"
"SME_I16I32\0" "SME_I16I32\0"
"SME_I16I64\0" "SME_I16I64\0"
"SME_I8I32\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" "SPECRES\0"
"SPECRES2\0" "SPECRES2\0"
"SPMU\0"
"SSBS\0" "SSBS\0"
"SSBS2\0" "SSBS2\0"
"SSVE_FP8DOT2\0"
"SSVE_FP8DOT4\0"
"SSVE_FP8FMA\0"
"SVE\0" "SVE\0"
"SVE2\0" "SVE2\0"
"SVE2_1\0" "SVE2_1\0"
@ -146,25 +216,35 @@ Error FormatterInternal::formatFeature(String& sb, uint32_t featureId) noexcept
"SYSINSTR128\0" "SYSINSTR128\0"
"SYSREG128\0" "SYSREG128\0"
"THE\0" "THE\0"
"TLBIOS\0"
"TLBIRANGE\0"
"TLBIW\0"
"TME\0" "TME\0"
"TRF\0" "TRF\0"
"UAO\0" "UAO\0"
"VFP_D32\0" "VFP_D32\0"
"VHE\0" "VHE\0"
"VMID16\0"
"WFXT\0" "WFXT\0"
"XNX\0"
"XS\0" "XS\0"
"<Unknown>\0"; "<Unknown>\0";
static const uint16_t sFeatureIndex[] = { static const uint16_t sFeatureIndex[] = {
0, 5, 11, 17, 24, 30, 38, 42, 46, 52, 57, 61, 67, 71, 78, 84, 90, 95, 100, 0, 5, 11, 17, 24, 30, 38, 43, 49, 53, 57, 61, 66, 73, 79, 85, 90, 95, 99,
104, 108, 116, 120, 125, 131, 135, 140, 145, 149, 154, 158, 164, 171, 176, 103, 109, 113, 120, 125, 139, 143, 148, 154, 160, 165, 170, 177, 182, 187,
179, 184, 193, 201, 205, 209, 213, 218, 224, 230, 236, 240, 246, 253, 260, 191, 195, 203, 207, 212, 217, 223, 229, 233, 239, 244, 253, 258, 262, 267,
265, 278, 285, 289, 296, 301, 306, 311, 315, 320, 325, 330, 334, 337, 341, 271, 277, 284, 289, 292, 297, 306, 310, 318, 326, 333, 338, 346, 350, 357,
345, 350, 355, 361, 365, 371, 379, 383, 390, 395, 399, 403, 407, 416, 422, 364, 369, 375, 379, 383, 388, 394, 399, 405, 411, 415, 421, 425, 431, 438,
428, 431, 436, 443, 448, 455, 459, 463, 467, 472, 479, 490, 501, 513, 524, 445, 450, 463, 470, 474, 481, 486, 490, 494, 499, 503, 508, 513, 517, 522,
535, 546, 557, 566, 577, 588, 598, 606, 615, 620, 626, 630, 635, 642, 650, 527, 532, 547, 557, 576, 596, 608, 623, 638, 644, 648, 651, 655, 659, 664,
661, 670, 682, 692, 702, 712, 721, 734, 743, 751, 763, 773, 777, 781, 785, 669, 675, 680, 684, 690, 698, 702, 709, 714, 721, 725, 729, 733, 742, 748,
789, 797, 801, 806, 809 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@ // @EnumStringEnd@
@ -178,14 +258,14 @@ ASMJIT_FAVOR_SIZE Error FormatterInternal::formatCondCode(String& sb, CondCode c
static const char condCodeData[] = static const char condCodeData[] =
"al\0" "na\0" "al\0" "na\0"
"eq\0" "ne\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" "hi\0" "ls\0" "ge\0" "lt\0" "gt\0" "le\0"
"<Unknown>"; "<Unknown>";
return sb.append(condCodeData + Support::min<uint32_t>(uint32_t(cc), 16u) * 3); return sb.append(condCodeData + Support::min<uint32_t>(uint32_t(cc), 16u) * 3);
} }
ASMJIT_FAVOR_SIZE Error FormatterInternal::formatShiftOp(String& sb, ShiftOp shiftOp) noexcept { ASMJIT_FAVOR_SIZE Error FormatterInternal::formatShiftOp(String& sb, ShiftOp shiftOp) noexcept {
const char* str = "<Unknown>"; const char* str = nullptr;
switch (shiftOp) { switch (shiftOp) {
case ShiftOp::kLSL: str = "lsl"; break; case ShiftOp::kLSL: str = "lsl"; break;
case ShiftOp::kLSR: str = "lsr"; 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::kSXTH: str = "sxth"; break;
case ShiftOp::kSXTW: str = "sxtw"; break; case ShiftOp::kSXTW: str = "sxtw"; break;
case ShiftOp::kSXTX: str = "sxtx"; break; case ShiftOp::kSXTX: str = "sxtx"; break;
default: str = "<Unknown>"; break;
} }
return sb.append(str); return sb.append(str);
} }
@ -208,6 +289,25 @@ ASMJIT_FAVOR_SIZE Error FormatterInternal::formatShiftOp(String& sb, ShiftOp shi
// arm::FormatterInternal - Format Register // 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( ASMJIT_FAVOR_SIZE Error FormatterInternal::formatRegister(
String& sb, String& sb,
FormatFlags flags, FormatFlags flags,
@ -264,31 +364,22 @@ ASMJIT_FAVOR_SIZE Error FormatterInternal::formatRegister(
if (Environment::is64Bit(arch)) { if (Environment::is64Bit(arch)) {
letter = 'w'; letter = 'w';
if (rId == Gp::kIdZr) if (rId == a64::Gp::kIdZr)
return sb.append("wzr", 3); return sb.append("wzr", 3);
if (rId == Gp::kIdSp) if (rId == a64::Gp::kIdSp)
return sb.append("wsp", 3); return sb.append("wsp", 3);
} }
else { else {
letter = 'r'; 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; break;
case RegType::kARM_GpX: case RegType::kARM_GpX:
if (Environment::is64Bit(arch)) { if (Environment::is64Bit(arch)) {
if (rId == Gp::kIdZr) if (rId == a64::Gp::kIdZr)
return sb.append("xzr", 3); return sb.append("xzr", 3);
if (rId == Gp::kIdSp) if (rId == a64::Gp::kIdSp)
return sb.append("sp", 2); return sb.append("sp", 2);
letter = 'x'; letter = 'x';
@ -299,7 +390,7 @@ ASMJIT_FAVOR_SIZE Error FormatterInternal::formatRegister(
ASMJIT_FALLTHROUGH; ASMJIT_FALLTHROUGH;
default: default:
ASMJIT_PROPAGATE(sb.appendFormat("<Reg-%u>?$u", uint32_t(regType), rId)); ASMJIT_PROPAGATE(sb.appendFormat("<Reg-%u>?%u", uint32_t(regType), rId));
break; break;
} }
@ -307,54 +398,69 @@ ASMJIT_FAVOR_SIZE Error FormatterInternal::formatRegister(
ASMJIT_PROPAGATE(sb.appendFormat("%c%u", letter, rId)); ASMJIT_PROPAGATE(sb.appendFormat("%c%u", letter, rId));
} }
constexpr uint32_t kElementTypeCount = uint32_t(a64::VecElementType::kMaxValue) + 1;
if (elementType) { if (elementType) {
char elementLetter = '\0'; elementType = Support::min(elementType, kElementTypeCount);
uint32_t elementCount = 0;
switch (elementType) { FormatElementData elementData = formatElementDataTable[elementType];
case Vec::kElementTypeB: uint32_t elementCount = elementData.elementCount;
elementLetter = 'b';
elementCount = 16;
break;
case Vec::kElementTypeH: if (regType == RegType::kARM_VecD) {
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)
elementCount /= 2u; 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) { if (elementIndex != 0xFFFFFFFFu) {
// This should only be used by AArch32 - AArch64 requires an additional elementType in index[].
ASMJIT_PROPAGATE(sb.appendFormat("[%u]", elementIndex)); ASMJIT_PROPAGATE(sb.appendFormat("[%u]", elementIndex));
} }
return kErrorOk; 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 // a64::FormatterInternal - Format Operand
// ======================================= // =======================================
@ -368,10 +474,10 @@ ASMJIT_FAVOR_SIZE Error FormatterInternal::formatOperand(
if (op.isReg()) { if (op.isReg()) {
const BaseReg& reg = op.as<BaseReg>(); const BaseReg& reg = op.as<BaseReg>();
uint32_t elementType = op.as<Vec>().elementType(); uint32_t elementType = op._signature.getField<BaseVec::kSignatureRegElementTypeMask>();
uint32_t elementIndex = op.as<Vec>().elementIndex(); uint32_t elementIndex = op.as<BaseVec>().elementIndex();
if (!op.as<Vec>().hasElementIndex()) if (!op.as<BaseVec>().hasElementIndex())
elementIndex = 0xFFFFFFFFu; elementIndex = 0xFFFFFFFFu;
return formatRegister(sb, flags, emitter, arch, reg.type(), reg.id(), elementType, elementIndex); 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()) { if (m.hasShift()) {
ASMJIT_PROPAGATE(sb.append(' ')); ASMJIT_PROPAGATE(sb.append(' '));
if (!m.isPreOrPost()) if (!m.isPreOrPost())
ASMJIT_PROPAGATE(formatShiftOp(sb, (ShiftOp)m.predicate())); ASMJIT_PROPAGATE(formatShiftOp(sb, m.shiftOp()));
ASMJIT_PROPAGATE(sb.appendFormat(" %u", m.shift())); ASMJIT_PROPAGATE(sb.appendFormat(" %u", m.shift()));
} }
@ -449,6 +555,12 @@ ASMJIT_FAVOR_SIZE Error FormatterInternal::formatOperand(
if (op.isImm()) { if (op.isImm()) {
const Imm& i = op.as<Imm>(); const Imm& i = op.as<Imm>();
int64_t val = i.value(); 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) { if (Support::test(flags, FormatFlags::kHexImms) && uint64_t(val) > 9) {
ASMJIT_PROPAGATE(sb.append("0x")); ASMJIT_PROPAGATE(sb.append("0x"));
@ -463,6 +575,11 @@ ASMJIT_FAVOR_SIZE Error FormatterInternal::formatOperand(
return Formatter::formatLabel(sb, flags, emitter, op.id()); 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>"); return sb.append("<None>");
} }

View File

@ -43,6 +43,14 @@ Error ASMJIT_CDECL formatRegister(
uint32_t elementType = 0, uint32_t elementType = 0,
uint32_t elementIndex = 0xFFFFFFFF) noexcept; 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( Error ASMJIT_CDECL formatOperand(
String& sb, String& sb,
FormatFlags flags, FormatFlags flags,

View File

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

View File

@ -19,18 +19,7 @@ ASMJIT_BEGIN_SUB_NAMESPACE(arm)
class Reg; class Reg;
class Mem; class Mem;
class Gp; //! Register traits (AArch32/AArch64).
class GpW;
class GpX;
class Vec;
class VecB;
class VecH;
class VecS;
class VecD;
class VecV;
//! Register traits (ARM/AArch64).
//! //!
//! Register traits contains information about a particular register type. It's used by asmjit to setup register //! 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 //! 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 {}; struct RegTraits : public BaseRegTraits {};
//! \cond //! \cond
// <--------------------+-----+-------------------------+------------------------+---+---+------------------+ // <--------------------+------------------------+------------------------+---+------------------+
// | Reg | Reg-Type | Reg-Group |Sz |Cnt| TypeId | // | Reg-Type | Reg-Group |Sz | TypeId |
// <--------------------+-----+-------------------------+------------------------+---+---+------------------+ // <--------------------+------------------------+------------------------+---+------------------+
ASMJIT_DEFINE_REG_TRAITS(GpW , RegType::kARM_GpW , RegGroup::kGp , 4 , 32, TypeId::kInt32 ); ASMJIT_DEFINE_REG_TRAITS(RegType::kARM_GpW , RegGroup::kGp , 4 , TypeId::kInt32 ); // AArch32 & AArch64
ASMJIT_DEFINE_REG_TRAITS(GpX , RegType::kARM_GpX , RegGroup::kGp , 8 , 32, TypeId::kInt64 ); ASMJIT_DEFINE_REG_TRAITS(RegType::kARM_GpX , RegGroup::kGp , 8 , TypeId::kInt64 ); // AArch64
ASMJIT_DEFINE_REG_TRAITS(VecB , RegType::kARM_VecB , RegGroup::kVec , 1 , 32, TypeId::kVoid ); ASMJIT_DEFINE_REG_TRAITS(RegType::kARM_VecB , RegGroup::kVec , 1 , TypeId::kVoid ); // AArch64
ASMJIT_DEFINE_REG_TRAITS(VecH , RegType::kARM_VecH , RegGroup::kVec , 2 , 32, TypeId::kVoid ); ASMJIT_DEFINE_REG_TRAITS(RegType::kARM_VecH , RegGroup::kVec , 2 , TypeId::kVoid ); // AArch64
ASMJIT_DEFINE_REG_TRAITS(VecS , RegType::kARM_VecS , RegGroup::kVec , 4 , 32, TypeId::kInt32x1 ); ASMJIT_DEFINE_REG_TRAITS(RegType::kARM_VecS , RegGroup::kVec , 4 , TypeId::kInt32x1 ); // AArch32 & AArch64
ASMJIT_DEFINE_REG_TRAITS(VecD , RegType::kARM_VecD , RegGroup::kVec , 8 , 32, TypeId::kInt32x2 ); ASMJIT_DEFINE_REG_TRAITS(RegType::kARM_VecD , RegGroup::kVec , 8 , TypeId::kInt32x2 ); // AArch32 & AArch64
ASMJIT_DEFINE_REG_TRAITS(VecV , RegType::kARM_VecV , RegGroup::kVec , 16, 32, TypeId::kInt32x4 ); 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 //! \endcond
//! Register (ARM). //! Register operand that can represent AArch32 and AArch64 registers.
class Reg : public BaseReg { class Reg : public BaseReg {
public: public:
ASMJIT_DEFINE_ABSTRACT_REG(Reg, BaseReg) 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; } 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). //! 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; } 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). //! 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; } 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). //! 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; } 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). //! 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; } 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). //! 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; } 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). //! 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; } 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> template<RegType kRegType>
ASMJIT_INLINE_NODEBUG void setRegT(uint32_t id) noexcept { ASMJIT_INLINE_NODEBUG void setRegT(uint32_t id) noexcept {
setSignature(RegTraits<kRegType>::kSignature); setSignature(RegTraits<kRegType>::kSignature);
@ -99,7 +101,7 @@ public:
static ASMJIT_INLINE_NODEBUG TypeId typeIdOfT() noexcept { return RegTraits<kRegType>::kTypeId; } static ASMJIT_INLINE_NODEBUG TypeId typeIdOfT() noexcept { return RegTraits<kRegType>::kTypeId; }
template<RegType kRegType> 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 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(); } 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)); } 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). //! Vector register base - a common base for both AArch32 & AArch64 vector register.
class Gp : public Reg { class BaseVec : public Reg {
public: public:
ASMJIT_DEFINE_ABSTRACT_REG(Gp, Reg) ASMJIT_DEFINE_ABSTRACT_REG(BaseVec, Reg)
//! Special register id. //! Additional signature bits used by a vector register.
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.
enum AdditionalBits : uint32_t { enum AdditionalBits : uint32_t {
// Register element type (3 bits). // Register element type (3 bits).
// |........|........|.XXX....|........| // |........|........|.XXX....|........|
@ -178,57 +145,8 @@ public:
kSignatureRegElementIndexMask = 0x0F << kSignatureRegElementIndexShift 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). //! 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>(); } 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. //! Returns element index of the register.
ASMJIT_INLINE_NODEBUG constexpr uint32_t elementIndex() const noexcept { return _signature.getField<kSignatureRegElementIndexMask>(); } ASMJIT_INLINE_NODEBUG constexpr uint32_t elementIndex() const noexcept { return _signature.getField<kSignatureRegElementIndexMask>(); }
//! Sets element index of the register to `elementType`. //! Sets element index of the register to `elementType`.
@ -240,140 +158,8 @@ public:
ASMJIT_INLINE_NODEBUG void resetElementIndex() noexcept { ASMJIT_INLINE_NODEBUG void resetElementIndex() noexcept {
_signature &= ~(kSignatureRegElementFlagMask | kSignatureRegElementIndexMask); _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). //! Memory operand (ARM).
class Mem : public BaseMem { class Mem : public BaseMem {
public: public:
@ -385,23 +171,18 @@ public:
kSignatureMemShiftValueShift = 14, kSignatureMemShiftValueShift = 14,
kSignatureMemShiftValueMask = 0x1Fu << kSignatureMemShiftValueShift, kSignatureMemShiftValueMask = 0x1Fu << kSignatureMemShiftValueShift,
// Shift operation type (4 bits). // Index shift operation (4 bits).
// |........|XXXX....|........|........| // |........|XXXX....|........|........|
kSignatureMemPredicateShift = 20, kSignatureMemShiftOpShift = 20,
kSignatureMemPredicateMask = 0x0Fu << kSignatureMemPredicateShift kSignatureMemShiftOpMask = 0x0Fu << kSignatureMemShiftOpShift,
// Offset mode type (2 bits).
// |......XX|........|........|........|
kSignatureMemOffsetModeShift = 24,
kSignatureMemOffsetModeMask = 0x03u << kSignatureMemOffsetModeShift
}; };
//! \endcond //! \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 //! \name Construction & Destruction
//! \{ //! \{
@ -438,11 +219,11 @@ public:
: BaseMem(Signature::fromOpType(OperandType::kMem) | : BaseMem(Signature::fromOpType(OperandType::kMem) |
Signature::fromMemBaseType(base.type()) | Signature::fromMemBaseType(base.type()) |
Signature::fromMemIndexType(index.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::fromValue<kSignatureMemShiftValueMask>(shift.value()) |
signature, base.id(), index.id(), 0) {} 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) | : BaseMem(Signature::fromOpType(OperandType::kMem) |
signature, uint32_t(base >> 32), 0, int32_t(uint32_t(base & 0xFFFFFFFFu))) {} 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. //! Clones the memory operand and makes it pre-index.
ASMJIT_INLINE_NODEBUG Mem pre() const noexcept { ASMJIT_INLINE_NODEBUG Mem pre() const noexcept {
Mem result(*this); Mem result(*this);
result.setPredicate(kOffsetPreIndex); result.setOffsetMode(OffsetMode::kPreIndex);
return result; return result;
} }
//! Clones the memory operand, applies a given offset `off` and makes it pre-index. //! Clones the memory operand, applies a given offset `off` and makes it pre-index.
ASMJIT_INLINE_NODEBUG Mem pre(int64_t off) const noexcept { ASMJIT_INLINE_NODEBUG Mem pre(int64_t off) const noexcept {
Mem result(*this); Mem result(*this);
result.setPredicate(kOffsetPreIndex); result.setOffsetMode(OffsetMode::kPreIndex);
result.addOffset(off); result.addOffset(off);
return result; return result;
} }
@ -486,14 +267,14 @@ public:
//! Clones the memory operand and makes it post-index. //! Clones the memory operand and makes it post-index.
ASMJIT_INLINE_NODEBUG Mem post() const noexcept { ASMJIT_INLINE_NODEBUG Mem post() const noexcept {
Mem result(*this); Mem result(*this);
result.setPredicate(kOffsetPostIndex); result.setOffsetMode(OffsetMode::kPostIndex);
return result; return result;
} }
//! Clones the memory operand, applies a given offset `off` and makes it post-index. //! Clones the memory operand, applies a given offset `off` and makes it post-index.
ASMJIT_INLINE_NODEBUG Mem post(int64_t off) const noexcept { ASMJIT_INLINE_NODEBUG Mem post(int64_t off) const noexcept {
Mem result(*this); Mem result(*this);
result.setPredicate(kOffsetPostIndex); result.setOffsetMode(OffsetMode::kPostIndex);
result.addOffset(off); result.addOffset(off);
return result; return result;
} }
@ -520,88 +301,85 @@ public:
setShift(shift); setShift(shift);
} }
ASMJIT_INLINE_NODEBUG void setIndex(const BaseReg& index, Shift shift) noexcept {
setIndex(index);
setShift(shift);
}
//! \} //! \}
//! \name ARM Specific Features //! \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. //! Gets whether the memory operand has shift (aka scale) constant.
ASMJIT_INLINE_NODEBUG constexpr bool hasShift() const noexcept { return _signature.hasField<kSignatureMemShiftValueMask>(); } ASMJIT_INLINE_NODEBUG constexpr bool hasShift() const noexcept { return _signature.hasField<kSignatureMemShiftValueMask>(); }
//! Gets the memory operand's shift (aka scale) constant. //! Gets the memory operand's shift (aka scale) constant.
ASMJIT_INLINE_NODEBUG constexpr uint32_t shift() const noexcept { return _signature.getField<kSignatureMemShiftValueMask>(); } ASMJIT_INLINE_NODEBUG constexpr uint32_t shift() const noexcept { return _signature.getField<kSignatureMemShiftValueMask>(); }
//! Sets the memory operand's shift (aka scale) constant. //! Sets the memory operand's shift (aka scale) constant.
ASMJIT_INLINE_NODEBUG void setShift(uint32_t shift) noexcept { _signature.setField<kSignatureMemShiftValueMask>(shift); } 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. //! Resets the memory operand's shift (aka scale) constant to zero.
ASMJIT_INLINE_NODEBUG void resetShift() noexcept { _signature.setField<kSignatureMemShiftValueMask>(0); } 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). //! \name Shift Operation Construction
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). //! Constructs a `LSL #value` shift (logical shift left).
static ASMJIT_INLINE_NODEBUG constexpr Mem ptr_pre(const Gp& base, int32_t offset = 0) noexcept { static ASMJIT_INLINE_NODEBUG constexpr Shift lsl(uint32_t value) noexcept { return Shift(ShiftOp::kLSL, value); }
return Mem(base, offset, OperandSignature::fromValue<Mem::kSignatureMemPredicateMask>(Mem::kOffsetPreIndex)); //! 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. //! \name Memory Operand Construction
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). //! Creates `[base]` absolute memory operand (AArch32 or AArch64).
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.
//! //!
//! \note The concept of absolute memory operands doesn't exist on ARM, the ISA only provides PC relative addressing. //! \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 //! 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 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 #endif // ASMJIT_ARM_ARMOPERAND_H_INCLUDED

View File

@ -6,6 +6,7 @@
#ifndef ASMJIT_ARM_ARMUTILS_H_INCLUDED #ifndef ASMJIT_ARM_ARMUTILS_H_INCLUDED
#define ASMJIT_ARM_ARMUTILS_H_INCLUDED #define ASMJIT_ARM_ARMUTILS_H_INCLUDED
#include "../core/support.h"
#include "../arm/armglobals.h" #include "../arm/armglobals.h"
ASMJIT_BEGIN_SUB_NAMESPACE(arm) ASMJIT_BEGIN_SUB_NAMESPACE(arm)
@ -16,6 +17,38 @@ ASMJIT_BEGIN_SUB_NAMESPACE(arm)
//! Public utilities and helpers for targeting AArch32 and AArch64 architectures. //! Public utilities and helpers for targeting AArch32 and AArch64 architectures.
namespace Utils { 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. //! Decomposed fields of a logical immediate value.
struct LogicalImm { struct LogicalImm {
uint32_t n; 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); 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 //! 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 //! 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. //! verify that the immediate is encodable before using the value.

View File

@ -3,7 +3,7 @@
// SPDX-License-Identifier: Zlib // SPDX-License-Identifier: Zlib
// Official GitHub Repository: https://github.com/asmjit/asmjit // 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 // This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages // 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 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. //! 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_X86 - Disables both X86 and X86_64 backends.
//! - \ref ASMJIT_NO_FOREIGN - Disables the support for foreign architectures. //! - \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 //! ### Features Selection
//! //!
//! AsmJit builds by defaults all supported features, which includes all emitters, logging, instruction validation and //! 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_...` //! introspection, and JIT memory allocation. Features can be disabled at compile time by using `ASMJIT_NO_...`
//! definitions. //! 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_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 //! - \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 //! be used together with \ref ASMJIT_NO_LOGGING as logging doesn't make sense without the ability to query
//! instruction names, register names, etc... //! 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_VALIDATION - Disables validation API.
//! //!
//! - \ref ASMJIT_NO_INTROSPECTION - Disables instruction introspection API, must be used together with \ref //! - \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 //! ASMJIT_NO_COMPILER as \ref asmjit_compiler requires introspection for its liveness analysis and register
//! allocation. //! 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 //! \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). //! 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. //! The possibility to disable certain features exists mainly for customized AsmJit builds.
@ -231,7 +240,7 @@ namespace asmjit {
//! //!
//! Useful tips before you start: //! 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 //! - 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 //! 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` //! because some compilers would warn about that. If your project compiles fine with `ASMJIT_NO_DEPRECATED`
//! it's not using anything, which was 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 //! ### Changes committed at 2023-09-10
//! //!
//! Core changes: //! Core changes:
@ -419,7 +469,7 @@ namespace asmjit {
//! // Calling a function (Compiler) changed - use invoke() instead of call(). //! // Calling a function (Compiler) changed - use invoke() instead of call().
//! void functionInvocation(x86::Compiler& cc) { //! void functionInvocation(x86::Compiler& cc) {
//! InvokeNode* invokeNode; //! 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: //! with assembler requires the knowledge of the following:
//! //!
//! - \ref BaseAssembler and architecture-specific assemblers: //! - \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 Operand and its variations:
//! - \ref BaseReg - Base class for a register operand, inherited by: //! - \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 BaseMem - Base class for a memory operand, inherited by:
//! - \ref x86::Mem - Memory operand specific to X86 architecture. //! - \ref x86::Mem - Memory operand specific to X86 architecture.
//! - \ref arm::Mem - Memory operand specific to AArch64 architecture.
//! - \ref Imm - Immediate (value) operand. //! - \ref Imm - Immediate (value) operand.
//! - \ref Label - Label operand. //! - \ref Label - Label operand.
//! //!
@ -1029,7 +1082,7 @@ namespace asmjit {
//! //!
//! // Type-unsafe, but possible. //! // Type-unsafe, but possible.
//! a.emit(x86::Inst::kIdMov, dst, m); //! 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); //! a.emit(x86::Inst::kIdMov, dst, op);
//! } //! }
//! ``` //! ```
@ -1100,10 +1153,10 @@ namespace asmjit {
//! //!
//! void testX86Mem() { //! void testX86Mem() {
//! // The same as: dword ptr [rax + rbx]. //! // 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]. //! // 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() { //! void testX86Mem() {
//! // The same as: dword ptr [rax + 12]. //! // 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.hasBase(); // true.
//! mem.hasIndex(); // false. //! mem.hasIndex(); // false.
//! mem.size(); // 4. //! mem.size(); // 4.
//! mem.offset(); // 12. //! 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.addOffset(-1); // Adds -1 to the offset and makes it 11.
//! mem.setOffset(0); // Sets the offset to 0. //! mem.setOffset(0); // Sets the offset to 0.
//! mem.setBase(rcx); // Changes BASE to RCX. //! mem.setBase(x86::rcx); // Changes BASE to RCX.
//! mem.setIndex(rax); // Changes INDEX to RAX. //! mem.setIndex(x86::rax); // Changes INDEX to RAX.
//! mem.hasIndex(); // true. //! mem.hasIndex(); // true.
//! } //! }
//! // ... //! // ...
@ -1219,7 +1272,8 @@ namespace asmjit {
//! //!
//! ### Builder Examples //! ### 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 //! \defgroup asmjit_compiler Compiler
@ -1256,7 +1310,8 @@ namespace asmjit {
//! //!
//! ### Compiler Examples //! ### 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 //! ### Compiler Tips
//! //!
@ -1428,7 +1483,7 @@ namespace asmjit {
//! The first example illustrates how to format operands: //! The first example illustrates how to format operands:
//! //!
//! ``` //! ```
//! #include <asmjit/core.h> //! #include <asmjit/x86.h>
//! #include <stdio.h> //! #include <stdio.h>
//! //!
//! using namespace asmjit; //! using namespace asmjit;
@ -1453,17 +1508,17 @@ namespace asmjit {
//! // compatible with what AsmJit normally does. //! // compatible with what AsmJit normally does.
//! Arch arch = Arch::kX64; //! Arch arch = Arch::kX64;
//! //!
//! log(arch, rax); // Prints 'rax'. //! logOperand(arch, rax); // Prints 'rax'.
//! log(arch, ptr(rax, rbx, 2)); // Prints '[rax + rbx * 4]`. //! logOperand(arch, ptr(rax, rbx, 2)); // Prints '[rax + rbx * 4]`.
//! log(arch, dword_ptr(rax, rbx, 2)); // Prints 'dword [rax + rbx * 4]`. //! logOperand(arch, dword_ptr(rax, rbx, 2)); // Prints 'dword [rax + rbx * 4]`.
//! log(arch, imm(42)); // Prints '42'. //! logOperand(arch, imm(42)); // Prints '42'.
//! } //! }
//! ``` //! ```
//! //!
//! Next example illustrates how to format whole instructions: //! Next example illustrates how to format whole instructions:
//! //!
//! ``` //! ```
//! #include <asmjit/core.h> //! #include <asmjit/x86.h>
//! #include <stdio.h> //! #include <stdio.h>
//! #include <utility> //! #include <utility>
//! //!
@ -1478,7 +1533,7 @@ namespace asmjit {
//! FormatFlags formatFlags = FormatFlags::kNone; //! FormatFlags formatFlags = FormatFlags::kNone;
//! //!
//! // The formatter expects operands in an array. //! // The formatter expects operands in an array.
//! Operand_ operands { std::forward<Args>(args)... }; //! Operand_ operands[] { std::forward<Args>(args)... };
//! //!
//! StringTmp<128> sb; //! StringTmp<128> sb;
//! Formatter::formatInstruction( //! Formatter::formatInstruction(
@ -1500,13 +1555,13 @@ namespace asmjit {
//! // Prints 'vaddpd zmm0, zmm1, [rax] {1to8}'. //! // Prints 'vaddpd zmm0, zmm1, [rax] {1to8}'.
//! logInstruction(arch, //! logInstruction(arch,
//! BaseInst(Inst::kIdVaddpd), //! BaseInst(Inst::kIdVaddpd),
//! zmm0, zmm1, ptr(rax)._1toN()); //! zmm0, zmm1, ptr(rax)._1to8());
//! //!
//! // BaseInst abstracts instruction id, instruction options, and extraReg. //! // BaseInst abstracts instruction id, instruction options, and extraReg.
//! // Prints 'lock add [rax], rcx'. //! // Prints 'lock add [rax], rcx'.
//! logInstruction(arch, //! logInstruction(arch,
//! BaseInst(Inst::kIdAdd, InstOptions::kX86_Lock), //! BaseInst(Inst::kIdAdd, InstOptions::kX86_Lock),
//! x86::ptr(rax), rcx); //! ptr(rax), rcx);
//! //!
//! // Similarly an extra register (like AVX-512 selector) can be used. //! // Similarly an extra register (like AVX-512 selector) can be used.
//! // Prints 'vaddpd zmm0 {k2} {z}, zmm1, [rax]'. //! // Prints 'vaddpd zmm0 {k2} {z}, zmm1, [rax]'.
@ -1624,7 +1679,7 @@ namespace asmjit {
//! //!
//! Each instruction can be then queried for the following information: //! 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. //! - \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)) #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. //! 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 //! \def ASMJIT_ABI_NAMESPACE
//! //!
@ -27,7 +27,7 @@
//! AsmJit default, which makes it possible to use multiple AsmJit libraries within a single project, totally //! 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. //! controlled by users. This is useful especially in cases in which some of such library comes from third party.
#if !defined(ASMJIT_ABI_NAMESPACE) #if !defined(ASMJIT_ABI_NAMESPACE)
#define ASMJIT_ABI_NAMESPACE _abi_1_11 #define ASMJIT_ABI_NAMESPACE _abi_1_13
#endif // !ASMJIT_ABI_NAMESPACE #endif // !ASMJIT_ABI_NAMESPACE
//! \} //! \}
@ -42,7 +42,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <iterator> #include <initializer_list>
#include <limits> #include <limits>
#include <type_traits> #include <type_traits>
#include <utility> #include <utility>
@ -112,7 +112,7 @@ namespace asmjit {
#define ASMJIT_NO_INTROSPECTION #define ASMJIT_NO_INTROSPECTION
// Avoid doxygen preprocessor using feature-selection definitions. // Avoid doxygen preprocessor using feature-selection definitions.
#undef ASMJIT_BUILD_EMBNED #undef ASMJIT_BUILD_EMBED
#undef ASMJIT_BUILD_STATIC #undef ASMJIT_BUILD_STATIC
#undef ASMJIT_BUILD_DEBUG #undef ASMJIT_BUILD_DEBUG
#undef ASMJIT_BUILD_RELEASE #undef ASMJIT_BUILD_RELEASE
@ -163,6 +163,41 @@ namespace asmjit {
// Target Architecture Detection // 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__) #if defined(_M_X64) || defined(__x86_64__)
#define ASMJIT_ARCH_X86 64 #define ASMJIT_ARCH_X86 64
#elif defined(_M_IX86) || defined(__X86__) || defined(__i386__) #elif defined(_M_IX86) || defined(__X86__) || defined(__i386__)
@ -187,7 +222,14 @@ namespace asmjit {
#define ASMJIT_ARCH_MIPS 0 #define ASMJIT_ARCH_MIPS 0
#endif #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 #if ASMJIT_ARCH_BITS == 0
#undef ASMJIT_ARCH_BITS #undef ASMJIT_ARCH_BITS
#if defined(__LP64__) || defined(_LP64) #if defined(__LP64__) || defined(_LP64)
@ -212,62 +254,88 @@ namespace asmjit {
#define ASMJIT_NO_X86 #define ASMJIT_NO_X86
#endif #endif
#if !ASMJIT_ARCH_ARM && !defined(ASMJIT_NO_AARCH64) #if ASMJIT_ARCH_ARM != 64 && !defined(ASMJIT_NO_AARCH64)
#define ASMJIT_NO_AARCH64 #define ASMJIT_NO_AARCH64
#endif #endif
#endif #endif
//! \endcond
// C++ Compiler and Features Detection // C++ Compiler and Features Detection
// =================================== // ===================================
#define ASMJIT_CXX_GNU 0 #if defined(__GNUC__) && defined(__has_attribute)
#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)
#define ASMJIT_CXX_HAS_ATTRIBUTE(NAME, CHECK) (__has_attribute(NAME)) #define ASMJIT_CXX_HAS_ATTRIBUTE(NAME, CHECK) (__has_attribute(NAME))
#else #else
#define ASMJIT_CXX_HAS_ATTRIBUTE(NAME, CHECK) (!(!(CHECK))) #define ASMJIT_CXX_HAS_ATTRIBUTE(NAME, CHECK) (!(!(CHECK)))
#endif #endif // !ASMJIT_CXX_HAS_ATTRIBUTE
// API Decorators & C++ Extensions // API Decorators & C++ Extensions
// =============================== // ===============================
//! \addtogroup asmjit_core
//! \{
//! \def ASMJIT_API //! \def ASMJIT_API
//! //!
//! A decorator that is used to decorate API that AsmJit exports when built as a shared library. //! 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). // API (Export / Import).
#if !defined(ASMJIT_STATIC) #if !defined(ASMJIT_STATIC)
#if defined(_WIN32) && (defined(_MSC_VER) || defined(__MINGW32__)) #if defined(_WIN32) && (defined(_MSC_VER) || defined(__MINGW32__))
@ -295,13 +363,7 @@ namespace asmjit {
#define ASMJIT_VARAPI extern ASMJIT_API #define ASMJIT_VARAPI extern ASMJIT_API
#endif #endif
//! \def ASMJIT_VIRTAPI #if defined(__GNUC__) && !defined(_WIN32)
//!
//! 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__)
#define ASMJIT_VIRTAPI ASMJIT_API #define ASMJIT_VIRTAPI ASMJIT_API
#else #else
#define ASMJIT_VIRTAPI #define ASMJIT_VIRTAPI
@ -458,17 +520,10 @@ namespace asmjit {
//! Marks function, class, struct, enum, or anything else as deprecated. //! Marks function, class, struct, enum, or anything else as deprecated.
#if defined(__GNUC__) #if defined(__GNUC__)
#define ASMJIT_DEPRECATED(MESSAGE) __attribute__((__deprecated__(MESSAGE))) #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) #elif defined(_MSC_VER)
#define ASMJIT_DEPRECATED(MESSAGE) __declspec(deprecated(MESSAGE)) #define ASMJIT_DEPRECATED(MESSAGE) __declspec(deprecated(MESSAGE))
#define ASMJIT_DEPRECATED_STRUCT(MESSAGE) /* not usable if a deprecated function uses it */
#else #else
#define ASMJIT_DEPRECATED(MESSAGE) #define ASMJIT_DEPRECATED(MESSAGE)
#define ASMJIT_DEPRECATED_STRUCT(MESSAGE)
#endif #endif
// Utilities. // Utilities.
@ -477,66 +532,60 @@ namespace asmjit {
#if ASMJIT_CXX_HAS_ATTRIBUTE(no_sanitize, 0) #if ASMJIT_CXX_HAS_ATTRIBUTE(no_sanitize, 0)
#define ASMJIT_ATTRIBUTE_NO_SANITIZE_UNDEF __attribute__((__no_sanitize__("undefined"))) #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__)) #define ASMJIT_ATTRIBUTE_NO_SANITIZE_UNDEF __attribute__((__no_sanitize_undefined__))
#else #else
#define ASMJIT_ATTRIBUTE_NO_SANITIZE_UNDEF #define ASMJIT_ATTRIBUTE_NO_SANITIZE_UNDEF
#endif #endif
// Begin-Namespace & End-Namespace Macros // Diagnostic Macros
// ====================================== // ======================================
#if defined _DOXYGEN #if !defined(__clang__) && !defined(__INTEL_COMPILER) && !defined(_DOXYGEN)
#define ASMJIT_BEGIN_NAMESPACE namespace asmjit { #if defined(__GNUC__) && __GNUC__ == 4
#define ASMJIT_END_NAMESPACE } // There is a bug in GCC 4.X that has been fixed in GCC 5+, so just silence the warning.
#elif defined(__clang__) #define ASMJIT_BEGIN_DIAGNOSTIC_SCOPE \
#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 { \
_Pragma("GCC diagnostic push") \ _Pragma("GCC diagnostic push") \
_Pragma("GCC diagnostic ignored \"-Wmissing-field-initializers\"") _Pragma("GCC diagnostic ignored \"-Wmissing-field-initializers\"")
#define ASMJIT_END_NAMESPACE \ #define ASMJIT_END_DIAGNOSTIC_SCOPE \
_Pragma("GCC diagnostic pop") \ _Pragma("GCC diagnostic pop")
}} #elif defined(_MSC_VER)
#elif defined(__GNUC__) && __GNUC__ >= 8 #define ASMJIT_BEGIN_DIAGNOSTIC_SCOPE \
#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 { \
__pragma(warning(push)) \ __pragma(warning(push)) \
__pragma(warning(disable: 4127)) /* conditional expression is const */ \ __pragma(warning(disable: 4127)) /* conditional expression is const */ \
__pragma(warning(disable: 4201)) /* nameless struct/union */ __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 \ #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 // C++ Utilities
// ============= // =============
@ -612,10 +661,4 @@ namespace asmjit {
} }
#endif #endif
// Cleanup Api-Config Specific Macros
// ==================================
#undef ASMJIT_CXX_GNU
#undef ASMJIT_CXX_MAKE_VER
#endif // ASMJIT_CORE_API_CONFIG_H_INCLUDED #endif // ASMJIT_CORE_API_CONFIG_H_INCLUDED

View File

@ -29,8 +29,8 @@ enum class CondCode : uint8_t {
kNE = 0x03u, //!< Z==0 (any_sign !=) kNE = 0x03u, //!< Z==0 (any_sign !=)
kCS = 0x04u, //!< C==1 (unsigned >=) kCS = 0x04u, //!< C==1 (unsigned >=)
kHS = 0x04u, //!< C==1 (unsigned >=) kHS = 0x04u, //!< C==1 (unsigned >=)
kCC = 0x05u, //!< C==0 (unsigned < )
kLO = 0x05u, //!< C==0 (unsigned < ) kLO = 0x05u, //!< C==0 (unsigned < )
kCC = 0x05u, //!< C==0 (unsigned < )
kMI = 0x06u, //!< N==1 (is negative) kMI = 0x06u, //!< N==1 (is negative)
kPL = 0x07u, //!< N==0 (is positive or zero) kPL = 0x07u, //!< N==0 (is positive or zero)
kVS = 0x08u, //!< V==1 (is overflow) kVS = 0x08u, //!< V==1 (is overflow)
@ -42,21 +42,24 @@ enum class CondCode : uint8_t {
kGT = 0x0Eu, //!< Z==0 & N==V (signed > ) kGT = 0x0Eu, //!< Z==0 & N==V (signed > )
kLE = 0x0Fu, //!< Z==1 | N!=V (signed <=) kLE = 0x0Fu, //!< Z==1 | N!=V (signed <=)
kSign = kMI, //!< Sign. kZero = kEQ, //!< Zero flag (alias to equal).
kNotSign = kPL, //!< Not sign. kNotZero = kNE, //!< Not zero (alias to Not Equal).
kOverflow = kVS, //!< Signed overflow.
kNotOverflow = kVC, //!< Not signed overflow.
kEqual = kEQ, //!< Equal `a == b`. kEqual = kEQ, //!< Equal `a == b`.
kNotEqual = kNE, //!< Not Equal `a != b`. kNotEqual = kNE, //!< Not Equal `a != b`.
kZero = kEQ, //!< Zero (alias to equal). kCarry = kCS, //!< Carry flag.
kNotZero = kNE, //!< Not Zero (alias to Not Equal). kNotCarry = kCC, //!< Not carry.
kSign = kMI, //!< Sign flag.
kNotSign = kPL, //!< Not sign.
kNegative = kMI, //!< Negative. kNegative = kMI, //!< Negative.
kPositive = kPL, //!< Positive or zero. kPositive = kPL, //!< Positive or zero.
kOverflow = kVS, //!< Signed overflow.
kNotOverflow = kVC, //!< Not signed overflow.
kSignedLT = kLT, //!< Signed `a < b`. kSignedLT = kLT, //!< Signed `a < b`.
kSignedLE = kLE, //!< Signed `a <= b`. kSignedLE = kLE, //!< Signed `a <= b`.
kSignedGT = kGT, //!< Signed `a > b`. kSignedGT = kGT, //!< Signed `a > b`.
@ -67,49 +70,51 @@ enum class CondCode : uint8_t {
kUnsignedGT = kHI, //!< Unsigned `a > b`. kUnsignedGT = kHI, //!< Unsigned `a > b`.
kUnsignedGE = kHS, //!< 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). kAlways = kAL, //!< No condition code (always).
kMaxValue = 0x0Fu //!< Maximum value of `CondCode`. 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. //! Negates a condition code.
static ASMJIT_INLINE_NODEBUG constexpr CondCode negateCond(CondCode cond) noexcept { return CondCode(uint8_t(cond) ^ uint8_t(1)); } 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). //! Memory offset mode.
enum class DataType : uint32_t { //!
//! No data type specified (default for all general purpose instructions). //! Describes either fixed, pre-index, or post-index offset modes.
kNone = 0, enum class OffsetMode : uint32_t {
//! 8-bit signed integer, specified as `.s8` in assembly. //! Fixed offset mode (either no index at all or a regular index without a write-back).
kS8 = 1, kFixed = 0u,
//! 16-bit signed integer, specified as `.s16` in assembly. //! Pre-index "[BASE, #Offset {, <shift>}]!" with write-back.
kS16 = 2, kPreIndex = 1u,
//! 32-bit signed integer, specified as `.s32` in assembly. //! Post-index "[BASE], #Offset {, <shift>}" with write-back.
kS32 = 3, kPostIndex = 2u
//! 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
}; };
//! Shift operation predicate (ARM) describes either SHIFT or EXTEND operation. //! Shift operation predicate (ARM) describes either SHIFT or EXTEND operation.
@ -187,45 +192,70 @@ public:
//! Sets shift operation to `op`. //! Sets shift operation to `op`.
ASMJIT_INLINE_NODEBUG void setOp(ShiftOp op) noexcept { _op = 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; } ASMJIT_INLINE_NODEBUG constexpr uint32_t value() const noexcept { return _value; }
//! Sets shift amount to `value`. //! Sets shift amount to `value`.
ASMJIT_INLINE_NODEBUG void setValue(uint32_t value) noexcept { _value = 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_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 #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 == 32 ? kX86 :
ASMJIT_ARCH_X86 == 64 ? kX64 : 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_LE ? kARM :
ASMJIT_ARCH_ARM == 32 && ASMJIT_ARCH_BE ? kARM_BE : ASMJIT_ARCH_ARM == 32 && ASMJIT_ARCH_BE ? kARM_BE :
ASMJIT_ARCH_ARM == 64 && ASMJIT_ARCH_LE ? kAArch64 : 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: //! Check out architecture specific assemblers for more details and examples:
//! //!
//! - \ref x86::Assembler - X86/X64 assembler implementation. //! - \ref x86::Assembler - X86/X64 assembler implementation.
//! - \ref a64::Assembler - AArch64 assembler implementation.
class ASMJIT_VIRTAPI BaseAssembler : public BaseEmitter { class ASMJIT_VIRTAPI BaseAssembler : public BaseEmitter {
public: public:
ASMJIT_NONCOPYABLE(BaseAssembler) 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); EmitterUtils::opArrayFromEmitArgs(opArray, o0, o1, o2, opExt);
ValidationFlags validationFlags = isCompiler() ? ValidationFlags::kEnableVirtRegs : ValidationFlags::kNone; 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)) { if (ASMJIT_UNLIKELY(err)) {
#ifndef ASMJIT_NO_LOGGING #ifndef ASMJIT_NO_LOGGING

View File

@ -48,7 +48,7 @@ enum class NodeType : uint8_t {
// [BaseBuilder] // [BaseBuilder]
//! Node is \ref InstNode or \ref InstExNode. //! Node is \ref InstNode.
kInst = 1, kInst = 1,
//! Node is \ref SectionNode. //! Node is \ref SectionNode.
kSection = 2, kSection = 2,
@ -181,6 +181,7 @@ public:
//! Check out architecture specific builders for more details and examples: //! Check out architecture specific builders for more details and examples:
//! //!
//! - \ref x86::Builder - X86/X64 builder implementation. //! - \ref x86::Builder - X86/X64 builder implementation.
//! - \ref a64::Builder - AArch64 builder implementation.
class ASMJIT_VIRTAPI BaseBuilder : public BaseEmitter { class ASMJIT_VIRTAPI BaseBuilder : public BaseEmitter {
public: public:
ASMJIT_NONCOPYABLE(BaseBuilder) ASMJIT_NONCOPYABLE(BaseBuilder)
@ -546,7 +547,7 @@ public:
uint8_t _reserved1; uint8_t _reserved1;
}; };
//! Data that can have different meaning dependning on \ref NodeType. //! Data that can have different meaning depending on \ref NodeType.
union { union {
//! Data useful by any node type. //! Data useful by any node type.
AnyData _any; AnyData _any;
@ -694,8 +695,9 @@ public:
//! Returns user data casted to `T*`. //! 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 //! 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 `intptr_t` value through `setUserDataAsIntPtr()`. //! size so you can either store a pointer or `int64_t` value through `setUserDataAsPtr()`, `setUserDataAsInt64()`
//! and `setUserDataAsUInt64()`.
template<typename T> template<typename T>
ASMJIT_INLINE_NODEBUG T* userDataAsPtr() const noexcept { return static_cast<T*>(_userDataPtr); } ASMJIT_INLINE_NODEBUG T* userDataAsPtr() const noexcept { return static_cast<T*>(_userDataPtr); }
//! Returns user data casted to `int64_t`. //! Returns user data casted to `int64_t`.
@ -747,12 +749,15 @@ public:
//! \name Constants //! \name Constants
//! \{ //! \{
enum : uint32_t { //! The number of embedded operands for a default \ref InstNode instance that are always allocated as a part of
//! Count of embedded operands per `InstNode` that are always allocated as a part of the instruction. Minimum //! the instruction itself. Minimum embedded operands is 4, but in 32-bit more pointers are smaller and we can
//! embedded operands is 4, but in 32-bit more pointers are smaller and we can embed 5. The rest (up to 6 operands) //! embed 5. The rest (up to 6 operands) is considered extended.
//! is always stored in `InstExNode`. //!
kBaseOpCapacity = uint32_t((128 - sizeof(BaseNode) - sizeof(BaseInst)) / sizeof(Operand_)) //! 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. //! Base instruction data.
BaseInst _baseInst; BaseInst _baseInst;
//! First 4 or 5 operands (indexed from 0).
Operand_ _opArray[kBaseOpCapacity];
//! \} //! \}
@ -811,11 +814,17 @@ public:
//! \name Instruction Options //! \name Instruction Options
//! \{ //! \{
//! Returns instruction options, see \ref InstOptions for more details.
ASMJIT_INLINE_NODEBUG InstOptions options() const noexcept { return _baseInst.options(); } 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); } 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); } 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); } 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); } 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(); } 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); } ASMJIT_INLINE_NODEBUG void setOpCount(uint32_t opCount) noexcept { _inst._opCount = uint8_t(opCount); }
//! Returns operands array. //! 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). //! 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`. //! Returns operand at the given `index`.
inline Operand& op(uint32_t index) noexcept { inline Operand& op(uint32_t index) noexcept {
ASMJIT_ASSERT(index < opCapacity()); ASMJIT_ASSERT(index < opCapacity());
return _opArray[index].as<Operand>();
Operand* ops = operands();
return ops[index].as<Operand>();
} }
//! Returns operand at the given `index` (const). //! Returns operand at the given `index` (const).
inline const Operand& op(uint32_t index) const noexcept { inline const Operand& op(uint32_t index) const noexcept {
ASMJIT_ASSERT(index < opCapacity()); 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`. //! Sets operand at the given `index` to `op`.
inline void setOp(uint32_t index, const Operand_& op) noexcept { inline void setOp(uint32_t index, const Operand_& op) noexcept {
ASMJIT_ASSERT(index < opCapacity()); ASMJIT_ASSERT(index < opCapacity());
_opArray[index].copyFrom(op);
Operand* ops = operands();
ops[index].copyFrom(op);
} }
//! Resets operand at the given `index` to none. //! Resets operand at the given `index` to none.
inline void resetOp(uint32_t index) noexcept { inline void resetOp(uint32_t index) noexcept {
ASMJIT_ASSERT(index < opCapacity()); ASMJIT_ASSERT(index < opCapacity());
_opArray[index].reset();
Operand* ops = operands();
ops[index].reset();
} }
//! Resets operands at `[start, end)` range. //! Resets operands at `[start, end)` range.
inline void resetOpRange(uint32_t start, uint32_t end) noexcept { inline void resetOpRange(uint32_t start, uint32_t end) noexcept {
Operand* ops = operands();
for (uint32_t i = start; i < end; i++) for (uint32_t i = start; i < end; i++)
_opArray[i].reset(); ops[i].reset();
} }
//! \} //! \}
@ -889,33 +912,47 @@ public:
//! \name Utilities //! \name Utilities
//! \{ //! \{
//! Tests whether the given operand type `opType` is used by the instruction.
inline bool hasOpType(OperandType opType) const noexcept { inline bool hasOpType(OperandType opType) const noexcept {
const Operand* ops = operands();
for (uint32_t i = 0, count = opCount(); i < count; i++) for (uint32_t i = 0, count = opCount(); i < count; i++)
if (_opArray[i].opType() == opType) if (ops[i].opType() == opType)
return true; return true;
return false; return false;
} }
//! Tests whether the instruction uses at least one register operand.
inline bool hasRegOp() const noexcept { return hasOpType(OperandType::kReg); } 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); } 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); } 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); } 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 { inline uint32_t indexOfOpType(OperandType opType) const noexcept {
uint32_t i = 0; uint32_t i = 0;
uint32_t count = opCount(); uint32_t count = opCount();
const Operand* ops = operands();
while (i < count) { while (i < count) {
if (_opArray[i].opType() == opType) if (ops[i].opType() == opType)
break; return i;
i++; i++;
} }
return i; return Globals::kNotFound;
} }
//! A shortcut that calls `indexOfOpType(OperandType::kMem)`.
inline uint32_t indexOfMemOp() const noexcept { return 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); } 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); } inline uint32_t indexOfLabelOp() const noexcept { return indexOfOpType(OperandType::kLabel); }
//! \} //! \}
@ -924,20 +961,40 @@ public:
//! \{ //! \{
//! \cond INTERNAL //! \cond INTERNAL
//! Returns uint32_t[] view that represents BaseInst::RegOnly and instruction operands.
ASMJIT_INLINE_NODEBUG uint32_t* _getRewriteArray() noexcept { return &_baseInst._extraReg._id; } 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; } 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 { inline uint32_t getRewriteIndex(const uint32_t* id) const noexcept {
const uint32_t* array = _getRewriteArray(); const uint32_t* array = _getRewriteArray();
ASMJIT_ASSERT(array <= id); ASMJIT_ASSERT(array <= id);
size_t index = (size_t)(id - array); size_t index = (size_t)(id - array);
ASMJIT_ASSERT(index < 32); ASMJIT_ASSERT(index <= kMaxRewriteId);
return uint32_t(index); 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 { inline void rewriteIdAtIndex(uint32_t index, uint32_t id) noexcept {
ASMJIT_ASSERT(index <= kMaxRewriteId);
uint32_t* array = _getRewriteArray(); uint32_t* array = _getRewriteArray();
array[index] = id; array[index] = id;
} }
@ -949,43 +1006,40 @@ public:
//! \{ //! \{
//! \cond INTERNAL //! \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 { //! Calculates the size of \ref InstNode required to hold at most `opCapacity` operands.
size_t base = sizeof(InstNode) - kBaseOpCapacity * sizeof(Operand); //!
return base + opCapacity * sizeof(Operand); //! 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 //! \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 //! \note This is used to make tools such as static analysis and compilers happy about the layout. There were two
//! the default capacity of `InstNode`. //! instruction nodes in the past, having the second extend the operand array of the first, but that has caused
class InstExNode : public InstNode { //! undefined behavior and made recent tools unhappy about that.
template<uint32_t kN>
class InstNodeWithOperands : public InstNode {
public: public:
ASMJIT_NONCOPYABLE(InstExNode) Operand_ _operands[kN];
//! \name Members //! 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) {}
//! 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) {}
//! \}
}; };
//! Section node. //! Section node.
@ -1012,9 +1066,9 @@ public:
//! \{ //! \{
//! Creates a new `SectionNode` instance. //! 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), : BaseNode(cb, NodeType::kSection, NodeFlags::kHasNoEffect),
_id(secionId), _id(sectionId),
_nextSection(nullptr) {} _nextSection(nullptr) {}
//! \} //! \}

View File

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

View File

@ -132,8 +132,8 @@ CodeHolder::~CodeHolder() noexcept {
CodeHolder_resetInternal(this, ResetPolicy::kHard); CodeHolder_resetInternal(this, ResetPolicy::kHard);
} }
// CodeHolder - Init & Reset // CodeHolder - Initialization & Reset
// ========================= // ===================================
inline void CodeHolder_setSectionDefaultName( inline void CodeHolder_setSectionDefaultName(
Section* section, 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_MAX;
return size_t(offset); return size_t(offset);

View File

@ -45,7 +45,7 @@ enum class ExpressionOpType : uint8_t {
kSra = 5 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 { enum class ExpressionValueType : uint8_t {
//! No value or invalid. //! No value or invalid.
kNone = 0, kNone = 0,
@ -272,6 +272,9 @@ public:
//! Offset format type, used by \ref OffsetFormat. //! Offset format type, used by \ref OffsetFormat.
enum class OffsetType : uint8_t { enum class OffsetType : uint8_t {
// Common Offset Formats
// ---------------------
//! A value having `_immBitCount` bits and shifted by `_immBitShift`. //! 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 //! 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 Specific Offset Formats
// ------------------------------- // -------------------------------
//! AARCH64 ADR format of `[.|immlo:2|.....|immhi:19|.....]`. //! AArch64 ADR format of `[.|immlo:2|.....|immhi:19|.....]`.
kAArch64_ADR, kAArch64_ADR,
//! AARCH64 ADRP format of `[.|immlo:2|.....|immhi:19|.....]` (4kB pages). //! AArch64 ADRP format of `[.|immlo:2|.....|immhi:19|.....]` (4kB pages).
kAArch64_ADRP, 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`. //! 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 //! 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. //! Returns the type of the offset.
ASMJIT_INLINE_NODEBUG OffsetType type() const noexcept { return _type; } 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. //! Returns flags.
ASMJIT_INLINE_NODEBUG uint32_t flags() const noexcept { return _flags; } ASMJIT_INLINE_NODEBUG uint32_t flags() const noexcept { return _flags; }
//! Returns the size of the region/instruction where the offset is encoded. //! Returns the size of the region/instruction where the offset is encoded.
ASMJIT_INLINE_NODEBUG uint32_t regionSize() const noexcept { return _regionSize; } 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. //! 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; } ASMJIT_INLINE_NODEBUG uint32_t valueOffset() const noexcept { return _valueOffset; }
//! Returns the size of the data-type (word) that contains the offset, in bytes. //! Returns the size of the data-type (word) that contains the offset, in bytes.
ASMJIT_INLINE_NODEBUG uint32_t valueSize() const noexcept { return _valueSize; } 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. //! 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. //! use the same slot.
//! //!
//! This function should be considered internal as it's used by assemblers to insert an absolute address into the //! 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; ASMJIT_API Error addAddressToAddressTable(uint64_t address) noexcept;
//! \} //! \}
@ -1018,7 +1098,8 @@ public:
//! Relocates the code to the given `baseAddress`. //! Relocates the code to the given `baseAddress`.
//! //!
//! \param baseAddress Absolute base address where the code will be relocated to. Please note that nothing is //! \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. //! \note This should never be called more than once.
ASMJIT_API Error relocateToBase(uint64_t baseAddress) noexcept; ASMJIT_API Error relocateToBase(uint64_t baseAddress) noexcept;

View File

@ -6,6 +6,7 @@
#include "../core/api-build_p.h" #include "../core/api-build_p.h"
#include "../core/codeholder.h" #include "../core/codeholder.h"
#include "../core/codewriter_p.h" #include "../core/codewriter_p.h"
#include "../arm/armutils.h"
ASMJIT_BEGIN_NAMESPACE ASMJIT_BEGIN_NAMESPACE
@ -19,9 +20,20 @@ bool CodeWriterUtils::encodeOffset32(uint32_t* dst, int64_t offset64, const Offs
return false; return false;
uint32_t value; 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. // First handle all unsigned offset types.
if (format.type() == OffsetType::kUnsignedOffset) { if (unsignedLogic) {
if (discardLsb) { if (discardLsb) {
ASMJIT_ASSERT(discardLsb <= 32); ASMJIT_ASSERT(discardLsb <= 32);
if ((offset64 & Support::lsbMask<uint32_t>(discardLsb)) != 0) 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; 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_ADR:
case OffsetType::kAArch64_ADRP: { case OffsetType::kAArch64_ADRP: {
// Sanity checks. // Sanity checks.

View File

@ -47,6 +47,7 @@ class InvokeNode;
//! Check out architecture specific compilers for more details and examples: //! Check out architecture specific compilers for more details and examples:
//! //!
//! - \ref x86::Compiler - X86/X64 compiler implementation. //! - \ref x86::Compiler - X86/X64 compiler implementation.
//! - \ref a64::Compiler - AArch64 compiler implementation.
class ASMJIT_VIRTAPI BaseCompiler : public BaseBuilder { class ASMJIT_VIRTAPI BaseCompiler : public BaseBuilder {
public: public:
ASMJIT_NONCOPYABLE(BaseCompiler) ASMJIT_NONCOPYABLE(BaseCompiler)
@ -162,6 +163,8 @@ public:
//! //!
//! \note This version accepts a snprintf() format `fmt` followed by a variadic arguments. //! \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, ...); 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`. //! 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); 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 //! \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 //! 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. //! 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: public:
ASMJIT_NONCOPYABLE(JumpNode) ASMJIT_NONCOPYABLE(JumpNode)
@ -340,7 +343,7 @@ public:
//! \{ //! \{
inline JumpNode(BaseCompiler* ASMJIT_NONNULL(cc), InstId instId, InstOptions options, uint32_t opCount, JumpAnnotation* annotation) noexcept 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) { _annotation(annotation) {
setType(NodeType::kJump); setType(NodeType::kJump);
} }
@ -531,7 +534,7 @@ public:
}; };
//! Function return, used by \ref BaseCompiler. //! Function return, used by \ref BaseCompiler.
class FuncRetNode : public InstNode { class FuncRetNode : public InstNodeWithOperands<InstNode::kBaseOpCapacity> {
public: public:
ASMJIT_NONCOPYABLE(FuncRetNode) ASMJIT_NONCOPYABLE(FuncRetNode)
@ -539,7 +542,8 @@ public:
//! \{ //! \{
//! Creates a new `FuncRetNode` instance. //! 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; _any._nodeType = NodeType::kFuncRet;
} }
@ -547,12 +551,12 @@ public:
}; };
//! Function invocation, used by \ref BaseCompiler. //! Function invocation, used by \ref BaseCompiler.
class InvokeNode : public InstNode { class InvokeNode : public InstNodeWithOperands<InstNode::kBaseOpCapacity> {
public: public:
ASMJIT_NONCOPYABLE(InvokeNode) ASMJIT_NONCOPYABLE(InvokeNode)
//! Operand pack provides multiple operands that can be associated with a single return value of function //! 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. //! registers, for example 64-bit value in 32-bit mode or passing / returning homogeneous data structures.
struct OperandPack { struct OperandPack {
//! Operands. //! Operands.
@ -594,7 +598,7 @@ public:
//! Creates a new `InvokeNode` instance. //! Creates a new `InvokeNode` instance.
inline InvokeNode(BaseBuilder* ASMJIT_NONNULL(cb), InstId instId, InstOptions options) noexcept inline InvokeNode(BaseBuilder* ASMJIT_NONNULL(cb), InstId instId, InstOptions options) noexcept
: InstNode(cb, instId, options, kBaseOpCapacity), : InstNodeWithOperands(cb, instId, options, 0),
_funcDetail(), _funcDetail(),
_args(nullptr) { _args(nullptr) {
setType(NodeType::kInvoke); setType(NodeType::kInvoke);
@ -619,9 +623,9 @@ public:
ASMJIT_INLINE_NODEBUG const FuncDetail& detail() const noexcept { return _funcDetail; } ASMJIT_INLINE_NODEBUG const FuncDetail& detail() const noexcept { return _funcDetail; }
//! Returns the target operand. //! 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 //! \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. //! Returns the number of function return values.
ASMJIT_INLINE_NODEBUG bool hasRet() const noexcept { return _funcDetail.hasRet(); } 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.
//!
//! 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 { class ConstPool {
public: public:
ASMJIT_NONCOPYABLE(ConstPool) ASMJIT_NONCOPYABLE(ConstPool)
@ -199,9 +202,17 @@ public:
//! \name Construction & Destruction //! \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; ASMJIT_API ~ConstPool() noexcept;
//! \}
//! \name Reset
//! \{
//! Resets this constant pool and its allocator to `zone`.
ASMJIT_API void reset(Zone* zone) noexcept; ASMJIT_API void reset(Zone* zone) noexcept;
//! \} //! \}

View File

@ -7,6 +7,8 @@
#include "../core/cpuinfo.h" #include "../core/cpuinfo.h"
#include "../core/support.h" #include "../core/support.h"
#include <atomic>
// Required by `__cpuidex()` and `_xgetbv()`. // Required by `__cpuidex()` and `_xgetbv()`.
#if ASMJIT_ARCH_X86 #if ASMJIT_ARCH_X86
#if defined(_MSC_VER) #if defined(_MSC_VER)
@ -14,12 +16,15 @@
#endif #endif
#endif // ASMJIT_ARCH_X86 #endif // ASMJIT_ARCH_X86
#if !defined(_WIN32)
#include <unistd.h>
#endif
#if ASMJIT_ARCH_ARM #if ASMJIT_ARCH_ARM
// Required by various utilities that are required by features detection. // Required by various utilities that are required by features detection.
#if !defined(_WIN32) #if !defined(_WIN32)
#include <errno.h> #include <errno.h>
#include <sys/utsname.h> #include <sys/utsname.h>
#include <unistd.h>
#endif #endif
//! Required to detect CPU and features on Apple platforms. //! Required to detect CPU and features on Apple platforms.
@ -566,6 +571,7 @@ static ASMJIT_FAVOR_SIZE void detectX86Cpu(CpuInfo& cpu) noexcept {
// Other resources: // Other resources:
// https://en.wikipedia.org/wiki/AArch64 // https://en.wikipedia.org/wiki/AArch64
// https://en.wikipedia.org/wiki/Apple_silicon#List_of_Apple_processors // 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 // https://developer.arm.com/architectures/learn-the-architecture/understanding-the-armv8-x-extensions/single-page
#if ASMJIT_ARCH_ARM #if ASMJIT_ARCH_ARM
@ -659,8 +665,8 @@ static inline void populateBaseARMFeatures(CpuInfo& cpu) noexcept {
#endif #endif
} }
// CpuInfo - Detect - ARM - Madatory Features of ARM Architectures // CpuInfo - Detect - ARM - Mandatory Features of ARM Architectures
// =============================================================== // ================================================================
// Populates mandatory ARMv8.[v]A features. // Populates mandatory ARMv8.[v]A features.
ASMJIT_MAYBE_UNUSED 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); features.add(Ext::kHCX, Ext::kPAN3, Ext::kWFXT, Ext::kXS);
ASMJIT_FALLTHROUGH; ASMJIT_FALLTHROUGH;
case 6: // ARMv8.6 case 6: // ARMv8.6
// Missing: AMUv1p1. features.add(Ext::kAMU1_1, Ext::kBF16, Ext::kECV, Ext::kFGT, Ext::kI8MM);
features.add(Ext::kBF16, Ext::kECV, Ext::kFGT, Ext::kI8MM);
ASMJIT_FALLTHROUGH; ASMJIT_FALLTHROUGH;
case 5: // ARMv8.5 case 5: // ARMv8.5
// Missing: CSV2_2. features.add(Ext::kBTI, Ext::kCSV2, Ext::kDPB2, Ext::kFLAGM2, Ext::kFRINTTS, Ext::kSB, Ext::kSPECRES, Ext::kSSBS);
features.add(Ext::kBTI, Ext::kDPB2, Ext::kFLAGM2, Ext::kFRINTTS, Ext::kSB, Ext::kSPECRES, Ext::kSSBS);
ASMJIT_FALLTHROUGH; ASMJIT_FALLTHROUGH;
case 4: // ARMv8.4 case 4: // ARMv8.4
// Missing: AMUv1, SEL2, TLBIOS, TLBIRANGE. features.add(Ext::kAMU1, Ext::kDIT, Ext::kDOTPROD, Ext::kFLAGM,
features.add(Ext::kDIT, Ext::kDOTPROD, Ext::kFLAGM, Ext::kLRCPC2, Ext::kLSE2, Ext::kMPAM, Ext::kNV, Ext::kTRF); Ext::kLRCPC2, Ext::kLSE2, Ext::kMPAM, Ext::kNV,
Ext::kSEL2, Ext::kTLBIOS, Ext::kTLBIRANGE, Ext::kTRF);
ASMJIT_FALLTHROUGH; ASMJIT_FALLTHROUGH;
case 3: // ARMv8.3 case 3: // ARMv8.3
features.add(Ext::kCCIDX, Ext::kFCMA, Ext::kJSCVT, Ext::kLRCPC, Ext::kPAUTH); features.add(Ext::kCCIDX, Ext::kFCMA, Ext::kJSCVT, Ext::kLRCPC, Ext::kPAUTH);
ASMJIT_FALLTHROUGH; ASMJIT_FALLTHROUGH;
case 2: // ARMv8.2 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; ASMJIT_FALLTHROUGH;
case 1: // ARMv8.1 case 1: // ARMv8.1
features.add(Ext::kCRC32, Ext::kLOR, Ext::kLSE, Ext::kPAN, Ext::kRDM, Ext::kVHE); 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); 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 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 f1,
Ext::Id f2 = Ext::kNone, Ext::Id f2 = Ext::kNone,
Ext::Id f3 = 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); 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__) // Merges a feature that is identified by an exact bit-combination of 4 bits.
#define MERGE_FEATURE_EXT(identifier, reg, offset, ...) mergeAArch64CPUIDFeatureExt(cpu.features().arm(), reg, offset, __VA_ARGS__) 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. // Detects features based on the content of ID_AA64PFR0_EL1 and ID_AA64PFR1_EL1 registers.
ASMJIT_MAYBE_UNUSED 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 // ID_AA64PFR0_EL1
// =============== // ===============
// FP and AdvSIMD bits should match (i.e. if FP features FP16, ASIMD must feature it too). // 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_NA("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("AdvSIMD bits [23:20]" , fpr0, 20, Ext::kASIMD, Ext::kFP16);
/* /*
MERGE_FEATURE_EXT("GIC bits [27:24]" , fpr0, 24, ...); MERGE_FEATURE_4B("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("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 // ID_AA64PFR1_EL1
// =============== // ===============
MERGE_FEATURE_EXT("BT bits [3:0]" , fpr1, 0, Ext::kBTI); MERGE_FEATURE_4B("BT bits [3:0]" , fpr1, 0, Ext::kBTI);
MERGE_FEATURE_EXT("SSBS bits [7:4]" , fpr1, 4, Ext::kSSBS, Ext::kSSBS2); MERGE_FEATURE_4B("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("MTE bits [11:8]" , fpr1, 8, Ext::kMTE, Ext::kMTE2, Ext::kMTE3);
/* /*
MERGE_FEATURE_EXT("RAS_frac bits [15:12]" , fpr1, 12, ...); MERGE_FEATURE_4B("RAS_frac bits [15:12]" , fpr1, 12, ...);
MERGE_FEATURE_EXT("MPAM_frac bits [19:16]" , fpr1, 16, ...); 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_4B("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("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_4B("MTE_frac bits [43:40]" , fpr1, 40, ...);
MERGE_FEATURE_EXT("GCS bits [47:44]" , fpr1, 44, ...);
*/ */
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. // MTEX extensions are only available when MTE3 is available.
if (cpu.features().arm().hasMTE3()) 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_4B("DF2 bits [59:56]" , fpr1, 56, ...);
MERGE_FEATURE_EXT("PFAR bits [63:60]" , fpr1, 60, ...);
*/ */
MERGE_FEATURE_4B("PFAR bits [63:60]" , fpr1, 60, Ext::kPFAR);
// ID_AA64PFR0_EL1 + ID_AA64PFR1_EL1 // 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. // Detects features based on the content of ID_AA64ISAR0_EL1 and ID_AA64ISAR1_EL1 registers.
ASMJIT_MAYBE_UNUSED 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 // ID_AA64ISAR0_EL1
// ================ // ================
MERGE_FEATURE_EXT("AES bits [7:4]" , isar0, 4, Ext::kAES, Ext::kPMULL); MERGE_FEATURE_4B("AES bits [7:4]" , isar0, 4, Ext::kAES, Ext::kPMULL);
MERGE_FEATURE_EXT("SHA1 bits [11:8]" , isar0, 8, Ext::kSHA1); MERGE_FEATURE_4B("SHA1 bits [11:8]" , isar0, 8, Ext::kSHA1);
MERGE_FEATURE_EXT("SHA2 bits [15:12]" , isar0, 12, Ext::kSHA256, Ext::kSHA512); MERGE_FEATURE_4B("SHA2 bits [15:12]" , isar0, 12, Ext::kSHA256, Ext::kSHA512);
MERGE_FEATURE_EXT("CRC32 bits [19:16]" , isar0, 16, Ext::kCRC32); MERGE_FEATURE_4B("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_4B("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_4B("TME bits [27:24]" , isar0, 24, Ext::kTME);
MERGE_FEATURE_EXT("RDM bits [31:28]" , isar0, 28, Ext::kRDM); MERGE_FEATURE_4B("RDM bits [31:28]" , isar0, 28, Ext::kRDM);
MERGE_FEATURE_EXT("SHA3 bits [35:32]" , isar0, 32, Ext::kSHA3); MERGE_FEATURE_4B("SHA3 bits [35:32]" , isar0, 32, Ext::kSHA3);
MERGE_FEATURE_EXT("SM3 bits [39:36]" , isar0, 36, Ext::kSM3); MERGE_FEATURE_4B("SM3 bits [39:36]" , isar0, 36, Ext::kSM3);
MERGE_FEATURE_EXT("SM4 bits [43:40]" , isar0, 40, Ext::kSM4); MERGE_FEATURE_4B("SM4 bits [43:40]" , isar0, 40, Ext::kSM4);
MERGE_FEATURE_EXT("DP bits [47:44]" , isar0, 44, Ext::kDOTPROD); MERGE_FEATURE_4B("DP bits [47:44]" , isar0, 44, Ext::kDOTPROD);
MERGE_FEATURE_EXT("FHM bits [51:48]" , isar0, 48, Ext::kFHM); MERGE_FEATURE_4B("FHM bits [51:48]" , isar0, 48, Ext::kFHM);
MERGE_FEATURE_EXT("TS bits [55:52]" , isar0, 52, Ext::kFLAGM, Ext::kFLAGM2); 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 // 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_4B("APA bits [7:4]" , isar1, 4, ...);
MERGE_FEATURE_EXT("API bits [11:8]" , isar1, 8, ...); MERGE_FEATURE_4B("API bits [11:8]" , isar1, 8, ...);
*/ */
MERGE_FEATURE_EXT("JSCVT bits [15:12]" , isar1, 12, Ext::kJSCVT); MERGE_FEATURE_4B("JSCVT bits [15:12]" , isar1, 12, Ext::kJSCVT);
MERGE_FEATURE_EXT("FCMA bits [19:16]" , isar1, 16, Ext::kFCMA); MERGE_FEATURE_4B("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("LRCPC bits [23:20]" , isar1, 20, Ext::kLRCPC, Ext::kLRCPC2, Ext::kLRCPC3);
/* /*
MERGE_FEATURE_EXT("GPA bits [27:24]" , isar1, 24, ...); MERGE_FEATURE_4B("GPA bits [27:24]" , isar1, 24, ...);
MERGE_FEATURE_EXT("GPI bits [31:28]" , isar1, 28, ...); MERGE_FEATURE_4B("GPI bits [31:28]" , isar1, 28, ...);
*/ */
MERGE_FEATURE_EXT("FRINTTS bits [35:32]" , isar1, 32, Ext::kFRINTTS); MERGE_FEATURE_4B("FRINTTS bits [35:32]" , isar1, 32, Ext::kFRINTTS);
MERGE_FEATURE_EXT("SB bits [39:36]" , isar1, 36, Ext::kSB); MERGE_FEATURE_4B("SB bits [39:36]" , isar1, 36, Ext::kSB);
MERGE_FEATURE_EXT("SPECRES bits [43:40]" , isar1, 40, Ext::kSPECRES, Ext::kSPECRES2); MERGE_FEATURE_4B("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_4B("BF16 bits [47:44]" , isar1, 44, Ext::kBF16, Ext::kEBF16);
MERGE_FEATURE_EXT("DGH bits [51:48]" , isar1, 48, Ext::kDGH); MERGE_FEATURE_4B("DGH bits [51:48]" , isar1, 48, Ext::kDGH);
MERGE_FEATURE_EXT("I8MM bits [55:52]" , isar1, 52, Ext::kI8MM); MERGE_FEATURE_4B("I8MM bits [55:52]" , isar1, 52, Ext::kI8MM);
MERGE_FEATURE_EXT("XS bits [59:56]" , isar1, 56, Ext::kXS); MERGE_FEATURE_4B("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("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. // Detects features based on the content of ID_AA64ISAR2_EL1 register.
ASMJIT_MAYBE_UNUSED ASMJIT_MAYBE_UNUSED
static ASMJIT_FAVOR_SIZE void detectAArch64FeaturesViaCPUID_AA64ISAR2(CpuInfo& cpu, uint64_t isar2) noexcept { static inline void detectAArch64FeaturesViaCPUID_AA64ISAR2(CpuInfo& cpu, uint64_t isar2) noexcept {
MERGE_FEATURE_EXT("WFxT bits [3:0]" , isar2, 0, Ext::kNone, Ext::kWFXT); MERGE_FEATURE_4B("WFxT bits [3:0]" , isar2, 0, Ext::kNone, Ext::kWFXT);
MERGE_FEATURE_EXT("RPRES bits [7:4]" , isar2, 4, Ext::kRPRES); MERGE_FEATURE_4B("RPRES bits [7:4]" , isar2, 4, Ext::kRPRES);
/* /*
MERGE_FEATURE_EXT("GPA3 bits [11:8]" , isar2, 8, ...); MERGE_FEATURE_4B("GPA3 bits [11:8]" , isar2, 8, ...);
MERGE_FEATURE_EXT("APA3 bits [15:12]" , isar2, 12, ...); MERGE_FEATURE_4B("APA3 bits [15:12]" , isar2, 12, ...);
*/ */
MERGE_FEATURE_EXT("MOPS bits [19:16]" , isar2, 16, Ext::kMOPS); MERGE_FEATURE_4B("MOPS bits [19:16]" , isar2, 16, Ext::kMOPS);
MERGE_FEATURE_EXT("BC bits [23:20]" , isar2, 20, Ext::kHBC); 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_EXT("PAC_frac bits [27:24]" , isar2, 24, ...); MERGE_FEATURE_4B("CLRBHB bits [31:28]" , isar2, 28, Ext::kCLRBHB);
*/ MERGE_FEATURE_4B("SYSREG128 bits [35:32]" , isar2, 32, Ext::kSYSREG128);
MERGE_FEATURE_EXT("CLRBHB bits [31:28]" , isar2, 28, Ext::kCLRBHB); MERGE_FEATURE_4B("SYSINSTR128 bits [39:36]" , isar2, 36, Ext::kSYSINSTR128);
MERGE_FEATURE_EXT("SYSREG128 bits [35:32]" , isar2, 32, Ext::kSYSREG128); MERGE_FEATURE_4B("PRFMSLC bits [43:40]" , isar2, 40, Ext::kPRFMSLC);
MERGE_FEATURE_EXT("SYSINSTR128 bits [39:36]" , isar2, 36, Ext::kSYSINSTR128); MERGE_FEATURE_4B("RPRFM bits [51:48]" , isar2, 48, Ext::kRPRFM);
MERGE_FEATURE_EXT("PRFMSLC bits [43:40]" , isar2, 40, Ext::kPRFMSLC); MERGE_FEATURE_4B("CSSC bits [55:52]" , isar2, 52, Ext::kCSSC);
MERGE_FEATURE_EXT("RPRFM bits [51:48]" , isar2, 48, Ext::kRPRFM); MERGE_FEATURE_4B("LUT bits [59:56]" , isar2, 56, Ext::kLUT);
MERGE_FEATURE_EXT("CSSC bits [55:52]" , isar2, 52, Ext::kCSSC);
} }
// TODO: This register is not accessed at the moment.
#if 0
// Detects features based on the content of ID_AA64ISAR3_EL1register.
ASMJIT_MAYBE_UNUSED 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 // ID_AA64MMFR0_EL1
// ================ // ================
/* /*
MERGE_FEATURE_EXT("PARange bits [3:0]" , mmfr0, 0, ...); MERGE_FEATURE_4B("PARange bits [3:0]" , mmfr0, 0, ...);
MERGE_FEATURE_EXT("ASIDBits bits [7:4]" , mmfr0, 4, ...); MERGE_FEATURE_4B("ASIDBits bits [7:4]" , mmfr0, 4, ...);
MERGE_FEATURE_EXT("BigEnd bits [11:8]" , mmfr0, 8, ...); MERGE_FEATURE_4B("BigEnd bits [11:8]" , mmfr0, 8, ...);
MERGE_FEATURE_EXT("SNSMem bits [15:12]" , mmfr0, 12, ...); MERGE_FEATURE_4B("SNSMem bits [15:12]" , mmfr0, 12, ...);
MERGE_FEATURE_EXT("BigEndEL0 bits [19:16]" , mmfr0, 16, ...); MERGE_FEATURE_4B("BigEndEL0 bits [19:16]" , mmfr0, 16, ...);
MERGE_FEATURE_EXT("TGran16 bits [23:20]" , mmfr0, 20, ...); MERGE_FEATURE_4B("TGran16 bits [23:20]" , mmfr0, 20, ...);
MERGE_FEATURE_EXT("TGran64 bits [27:24]" , mmfr0, 24, ...); MERGE_FEATURE_4B("TGran64 bits [27:24]" , mmfr0, 24, ...);
MERGE_FEATURE_EXT("TGran4 bits [31:28]" , mmfr0, 28, ...); MERGE_FEATURE_4B("TGran4 bits [31:28]" , mmfr0, 28, ...);
MERGE_FEATURE_EXT("TGran16_2 bits [35:32]" , mmfr0, 32, ...); MERGE_FEATURE_4B("TGran16_2 bits [35:32]" , mmfr0, 32, ...);
MERGE_FEATURE_EXT("TGran64_2 bits [39:36]" , mmfr0, 36, ...); MERGE_FEATURE_4B("TGran64_2 bits [39:36]" , mmfr0, 36, ...);
MERGE_FEATURE_EXT("TGran4_2 bits [43:40]" , mmfr0, 40, ...); MERGE_FEATURE_4B("TGran4_2 bits [43:40]" , mmfr0, 40, ...);
MERGE_FEATURE_EXT("ExS bits [47:44]" , mmfr0, 44, ...); MERGE_FEATURE_4B("ExS bits [47:44]" , mmfr0, 44, ...);
*/ */
MERGE_FEATURE_EXT("FGT bits [59:56]" , mmfr0, 56, Ext::kFGT, Ext::kFGT2); MERGE_FEATURE_4B("FGT bits [59:56]" , mmfr0, 56, Ext::kFGT, Ext::kFGT2);
MERGE_FEATURE_EXT("ECV bits [63:60]" , mmfr0, 60, Ext::kECV); MERGE_FEATURE_4B("ECV bits [63:60]" , mmfr0, 60, Ext::kECV);
} }
ASMJIT_MAYBE_UNUSED 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 // 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_4B("SpecSEI bits [27:24]" , mmfr1, 24, ...);
MERGE_FEATURE_EXT("VMIDBits bits [7:4]" , mmfr1, 4, ...);
*/ */
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_4B("HCX bits [43:40]" , mmfr1, 40, Ext::kHCX);
MERGE_FEATURE_EXT("PAN bits [23:20]" , mmfr1, 20, Ext::kPAN, Ext::kPAN2, Ext::kPAN3); MERGE_FEATURE_4B("AFP bits [47:44]" , mmfr1, 44, Ext::kAFP);
/* /*
MERGE_FEATURE_EXT("SpecSEI bits [27:24]" , mmfr1, 24, ...); MERGE_FEATURE_4B("nTLBPA bits [51:48]" , mmfr1, 48, ...);
MERGE_FEATURE_EXT("XNX bits [31:28]" , mmfr1, 28, ...); MERGE_FEATURE_4B("TIDCP1 bits [55:52]" , mmfr1, 52, ...);
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("CMOW bits [59:56]" , mmfr1, 56, Ext::kCMOW);
MERGE_FEATURE_4B("ECBHB bits [63:60]" , mmfr1, 60, Ext::kECBHB);
} }
ASMJIT_MAYBE_UNUSED 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 // 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_4B("LSM bits [11:8]" , mmfr2, 8, ...);
MERGE_FEATURE_EXT("IESB bits [15:12]" , mmfr2, 12, ...); MERGE_FEATURE_4B("IESB bits [15:12]" , mmfr2, 12, ...);
MERGE_FEATURE_EXT("VARange bits [19:16]" , mmfr2, 16, ...);
*/ */
MERGE_FEATURE_EXT("CCIDX bits [23:20]" , mmfr2, 20, Ext::kCCIDX); MERGE_FEATURE_4B("VARange bits [19:16]" , mmfr2, 16, Ext::kLVA, Ext::kLVA3);
MERGE_FEATURE_EXT("NV bits [27:24]" , mmfr2, 24, Ext::kNV, Ext::kNV2); 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_4B("IDS bits [39:36]" , mmfr2, 36, ...);
MERGE_FEATURE_EXT("FWB bits [43:40]" , mmfr2, 40, ...); MERGE_FEATURE_4B("FWB bits [43:40]" , mmfr2, 40, ...);
MERGE_FEATURE_EXT("TTL bits [51:48]" , mmfr2, 48, ...); MERGE_FEATURE_4B("TTL bits [51:48]" , mmfr2, 48, ...);
MERGE_FEATURE_EXT("BBM bits [55:52]" , mmfr2, 52, ...); MERGE_FEATURE_4B("BBM bits [55:52]" , mmfr2, 52, ...);
MERGE_FEATURE_EXT("EVT bits [59:56]" , mmfr2, 56, ...); MERGE_FEATURE_4B("EVT bits [59:56]" , mmfr2, 56, ...);
MERGE_FEATURE_EXT("E0PD bits [63:60]" , mmfr2, 60, ...); MERGE_FEATURE_4B("E0PD bits [63:60]" , mmfr2, 60, ...);
*/ */
} }
// Detects features based on the content of ID_AA64ZFR0_EL1 register. // Detects features based on the content of ID_AA64ZFR0_EL1 register.
ASMJIT_MAYBE_UNUSED ASMJIT_MAYBE_UNUSED
static ASMJIT_FAVOR_SIZE void detectAArch64FeaturesViaCPUID_AA64ZFR0(CpuInfo& cpu, uint64_t zfr0) noexcept { static inline 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_4B("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_4B("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_4B("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_4B("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_4B("B16B16 bits [27:24]" , zfr0, 24, Ext::kSVE_B16B16);
MERGE_FEATURE_EXT("SHA3 bits [35:32]" , zfr0, 32, Ext::kSVE_SHA3); MERGE_FEATURE_4B("SHA3 bits [35:32]" , zfr0, 32, Ext::kSVE_SHA3);
MERGE_FEATURE_EXT("SM4 bits [43:40]" , zfr0, 40, Ext::kSVE_SM4); MERGE_FEATURE_4B("SM4 bits [43:40]" , zfr0, 40, Ext::kSVE_SM4);
MERGE_FEATURE_EXT("I8MM bits [47:44]" , zfr0, 44, Ext::kSVE_I8MM); MERGE_FEATURE_4B("I8MM bits [47:44]" , zfr0, 44, Ext::kSVE_I8MM);
MERGE_FEATURE_EXT("F32MM bits [55:52]" , zfr0, 52, Ext::kSVE_F32MM); MERGE_FEATURE_4B("F32MM bits [55:52]" , zfr0, 52, Ext::kSVE_F32MM);
MERGE_FEATURE_EXT("F64MM bits [59:56]" , zfr0, 56, Ext::kSVE_F64MM); MERGE_FEATURE_4B("F64MM bits [59:56]" , zfr0, 56, Ext::kSVE_F64MM);
} }
#undef MERGE_FEATURE_EXT ASMJIT_MAYBE_UNUSED
#undef MERGE_FEATURE_N_A 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 // 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). // Apple A14/M1 (ARMv8.5-A).
case uint32_t(Id::kFIRESTORM_ICESTORM): case uint32_t(Id::kFIRESTORM_ICESTORM):
// Missing: CSV2, CSV3.
populateARMv8AFeatures(features, 4); populateARMv8AFeatures(features, 4);
features.add(Ext::kAES, Ext::kDPB2, Ext::kECV, Ext::kFHM, Ext::kFLAGM2, Ext::kFP16, Ext::kFP16CONV, features.add(Ext::kAES, Ext::kCSV2, Ext::kCSV3, Ext::kDPB2, Ext::kECV, Ext::kFHM, Ext::kFLAGM2,
Ext::kFRINTTS, Ext::kPMU, Ext::kPMULL, Ext::kSB, Ext::kSHA1, Ext::kSHA256, Ext::kSHA3, Ext::kFP16, Ext::kFP16CONV, Ext::kFRINTTS, Ext::kPMU, Ext::kPMULL, Ext::kSB,
Ext::kSHA512, Ext::kSSBS); Ext::kSHA1, Ext::kSHA256, Ext::kSHA3, Ext::kSHA512, Ext::kSSBS);
return true; return true;
// Apple A15/M2. // 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. // dependent is zeroed, only the bits that are used for CPU feature identification would be present.
// //
// References: // References:
// - https://docs.kernel.org/arm64/cpu-feature-registers.html // - https://docs.kernel.org/arch/arm64/cpu-feature-registers.html
ASMJIT_MAYBE_UNUSED ASMJIT_MAYBE_UNUSED
static ASMJIT_FAVOR_SIZE void detectAArch64FeaturesViaCPUID(CpuInfo& cpu) noexcept { static ASMJIT_FAVOR_SIZE void detectAArch64FeaturesViaCPUID(CpuInfo& cpu) noexcept {
populateBaseARMFeatures(cpu); populateBaseARMFeatures(cpu);
@ -1499,7 +1560,7 @@ static ASMJIT_FAVOR_SIZE void detectARMCpu(CpuInfo& cpu) noexcept {
#ifndef AT_HWCAP #ifndef AT_HWCAP
#define AT_HWCAP 16 #define AT_HWCAP 16
#endif // AT_HWCAP #endif // !AT_HWCAP
#ifndef AT_HWCAP2 #ifndef AT_HWCAP2
#define AT_HWCAP2 26 #define AT_HWCAP2 26
@ -1593,7 +1654,7 @@ static ASMJIT_FAVOR_SIZE void detectARMCpu(CpuInfo& cpu) noexcept {
#else #else
// Reference: // 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 // - https://github.com/torvalds/linux/blob/master/arch/arm64/include/uapi/asm/hwcap.h
static const HWCapMapping hwCapMapping[] = { static const HWCapMapping hwCapMapping[] = {
{ uint8_t(Ext::kFP) , 0 }, // HWCAP_FP { 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_I16I32) , 39 }, // HWCAP2_SME_I16I32
{ uint8_t(Ext::kSME_BI32I32) , 40 }, // HWCAP2_SME_BI32I32 { uint8_t(Ext::kSME_BI32I32) , 40 }, // HWCAP2_SME_BI32I32
{ uint8_t(Ext::kSME_B16B16) , 41 }, // HWCAP2_SME_B16B16 { 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 { 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. // Only read CPU_ID_AA64ZFR0 when either SVE or SME is available.
if (cpu.features().arm().hasAny(Ext::kSVE, Ext::kSME)) { if (cpu.features().arm().hasAny(Ext::kSVE, Ext::kSME)) {
detectAArch64FeaturesViaCPUID_AA64ZFR0(cpu, openbsdReadAArch64CPUID(ID::kAA64ZFR0)); detectAArch64FeaturesViaCPUID_AA64ZFR0(cpu, openbsdReadAArch64CPUID(ID::kAA64ZFR0));
if (cpu.features().arm().hasSME())
detectAArch64FeaturesViaCPUID_AA64SMFR0(cpu, openbsdReadAArch64CPUID(ID::kAA64SMFR0));
} }
postProcessARMCpuInfo(cpu); postProcessARMCpuInfo(cpu);
@ -1883,7 +1952,7 @@ static ASMJIT_FAVOR_SIZE void detectARMCpu(CpuInfo& cpu) noexcept {
#else #else
#if ASMJIT_ARCH_ARM == 32 #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 #else
#pragma message("[asmjit] Disabling runtime CPU detection - unsupported OS/CPU combination (Unknown OS with AArch64 CPU)") #pragma message("[asmjit] Disabling runtime CPU detection - unsupported OS/CPU combination (Unknown OS with AArch64 CPU)")
#endif #endif
@ -1902,13 +1971,13 @@ static ASMJIT_FAVOR_SIZE void detectARMCpu(CpuInfo& cpu) noexcept {
// CpuInfo - Detect - Host // CpuInfo - Detect - Host
// ======================= // =======================
static uint32_t cpuInfoInitialized; const CpuInfo& CpuInfo::host() noexcept {
static std::atomic<uint32_t> cpuInfoInitialized;
static CpuInfo cpuInfoGlobal(Globals::NoInit); static CpuInfo cpuInfoGlobal(Globals::NoInit);
const CpuInfo& CpuInfo::host() noexcept { // This should never cause a problem as the resulting information should always
// This should never cause a problem as the resulting information should always be the same. // be the same. In the worst case it would just be overwritten non-atomically.
// In the worst case it would just be overwritten non-atomically. if (!cpuInfoInitialized.load(std::memory_order_relaxed)) {
if (!cpuInfoInitialized) {
CpuInfo cpuInfoLocal; CpuInfo cpuInfoLocal;
cpuInfoLocal._arch = Arch::kHost; cpuInfoLocal._arch = Arch::kHost;
@ -1918,13 +1987,11 @@ const CpuInfo& CpuInfo::host() noexcept {
x86::detectX86Cpu(cpuInfoLocal); x86::detectX86Cpu(cpuInfoLocal);
#elif ASMJIT_ARCH_ARM #elif ASMJIT_ARCH_ARM
arm::detectARMCpu(cpuInfoLocal); arm::detectARMCpu(cpuInfoLocal);
#else
#pragma message("[asmjit] Disabling runtime CPU detection - unsupported OS/CPU combination (Unknown CPU)")
#endif #endif
cpuInfoLocal._hwThreadCount = detectHWThreadCount(); cpuInfoLocal._hwThreadCount = detectHWThreadCount();
cpuInfoGlobal = cpuInfoLocal; cpuInfoGlobal = cpuInfoLocal;
cpuInfoInitialized = 1; cpuInfoInitialized.store(1, std::memory_order_seq_cst);
} }
return cpuInfoGlobal; return cpuInfoGlobal;

View File

@ -22,11 +22,6 @@ ASMJIT_BEGIN_NAMESPACE
//! Each feature is represented by a single bit in an embedded bit array. //! Each feature is represented by a single bit in an embedded bit array.
class CpuFeatures { class CpuFeatures {
public: 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 //! \name Constants
//! \{ //! \{
@ -37,6 +32,13 @@ public:
}; };
//! \endcond //! \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 //! \name Data
@ -48,15 +50,15 @@ public:
//! \{ //! \{
//! Data bits. //! Data bits.
Support::Array<BitWord, kNumBitWords> _bits; Bits _bits;
//! \} //! \}
//! \name Overloaded Operators //! \name Overloaded Operators
//! \{ //! \{
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) noexcept { return !eq(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); return bool((_bits[idx] >> bit) & 0x1);
} }
//! \cond NONE
template<typename FeatureId> template<typename FeatureId>
ASMJIT_INLINE_NODEBUG bool hasAny(const FeatureId& featureId) const noexcept { ASMJIT_INLINE_NODEBUG bool hasAny(const FeatureId& featureId) const noexcept {
return has(featureId); 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> template<typename FeatureId, typename... Args>
ASMJIT_INLINE_NODEBUG bool hasAny(const FeatureId& featureId, Args&&... otherFeatureIds) const noexcept { ASMJIT_INLINE_NODEBUG bool hasAny(const FeatureId& featureId, Args&&... otherFeatureIds) const noexcept {
return bool(unsigned(has(featureId)) | unsigned(hasAny(std::forward<Args>(otherFeatureIds)...))); return bool(unsigned(has(featureId)) | unsigned(hasAny(std::forward<Args>(otherFeatureIds)...)));
@ -111,6 +118,7 @@ public:
//! \name Manipulation //! \name Manipulation
//! \{ //! \{
//! Clears all features set.
ASMJIT_INLINE_NODEBUG void reset() noexcept { _bits.fill(0); } ASMJIT_INLINE_NODEBUG void reset() noexcept { _bits.fill(0); }
//! Adds the given CPU `featureId` to the list of features. //! Adds the given CPU `featureId` to the list of features.
@ -164,10 +172,14 @@ public:
} }
//! Tests whether this CPU features data matches `other`. //! 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. //! X86 specific features data.
@ -333,6 +345,7 @@ public:
}; };
#define ASMJIT_X86_FEATURE(FEATURE) \ #define ASMJIT_X86_FEATURE(FEATURE) \
/*! Tests whether FEATURE is present. */ \
ASMJIT_INLINE_NODEBUG bool has##FEATURE() const noexcept { return has(X86::k##FEATURE); } ASMJIT_INLINE_NODEBUG bool has##FEATURE() const noexcept { return has(X86::k##FEATURE); }
ASMJIT_X86_FEATURE(MT) ASMJIT_X86_FEATURE(MT)
@ -505,26 +518,45 @@ public:
kTHUMB, //!< CPU has THUMB (16-bit THUMB encoding) {A32}. kTHUMB, //!< CPU has THUMB (16-bit THUMB encoding) {A32}.
kTHUMBv2, //!< CPU has THUMBv2 (32-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). kAES, //!< CPU has AES (ASIMD AES instructions).
kAFP, //!< CPU has AFP (alternate floating-point behavior) {A64}. 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). kASIMD, //!< CPU has ASIMD (NEON on ARM/THUMB).
kBF16, //!< CPU has BF16 (BFloat16 instructions) {A64}. kBF16, //!< CPU has BF16 (BFloat16 instructions) {A64}.
kBRBE, //!< CPU has BRBE (branch record buffer extension) {A64}.
kBTI, //!< CPU has BTI (branch target identification). 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). 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). 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). kCPUID, //!< CPU has CPUID (CPUID registers accessible in user-space).
kCRC32, //!< CPU has CRC32 (CRC32 instructions). kCRC32, //!< CPU has CRC32 (CRC32 instructions).
kCSSC, //!< CPU has CSSC (common short sequence compression) {A64}. 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}. kD128, //!< CPU has D128 (128-bit translation tables, 56 bit PA) {A64}.
kDGH, //!< CPU has DGH (data gathering hint) {A64}. kDGH, //!< CPU has DGH (data gathering hint) {A64}.
kDIT, //!< CPU has DIT (data independent timing of instructions). kDIT, //!< CPU has DIT (data independent timing of instructions).
kDOTPROD, //!< CPU has DOTPROD (ASIMD Int8 dot product instructions). kDOTPROD, //!< CPU has DOTPROD (ASIMD Int8 dot product instructions).
kDPB, //!< CPU has DPB (DC CVAP instruction) {A64}. kDPB, //!< CPU has DPB (DC CVAP instruction) {A64}.
kDPB2, //!< CPU has DPB2 (DC CVADP 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}. 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). kECV, //!< CPU has ECV (enhanced counter virtualization).
kEDHSR, //!< CPU has EDHSR (support for EDHSR) {A64}.
kEDSP, //!< CPU has EDSP (ARM/THUMB only). 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). kFCMA, //!< CPU has FCMA (FCADD/FCMLA).
kFGT, //!< CPU has FGT (fine-grained traps). kFGT, //!< CPU has FGT (fine-grained traps).
kFGT2, //!< CPU has FGT2 (fine-grained traps 2). 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). kFP, //!< CPU has FP (floating-point) (on 32-bit ARM this means VFPv3).
kFP16, //!< CPU has FP16 (half-precision floating-point data processing). kFP16, //!< CPU has FP16 (half-precision floating-point data processing).
kFP16CONV, //!< CPU has FP16CONV (half-precision float conversion). 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}. kFRINTTS, //!< CPU has FRINTTS (FRINT[32|64][X|Z] instructions) {A64}.
kGCS, //!< CPU has GCS (guarded control stack extension) {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}. 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}. kI8MM, //!< CPU has I8MM (int8 matrix multiplication) {A64}.
kIDIVA, //!< CPU has IDIV (hardware SDIV and UDIV in ARM mode). kIDIVA, //!< CPU has IDIV (hardware SDIV and UDIV in ARM mode).
kIDIVT, //!< CPU has IDIV (hardware SDIV and UDIV in THUMB 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}. kJSCVT, //!< CPU has JSCVT (JavaScript FJCVTS conversion instruction) {A64}.
kLOR, //!< CPU has LOR (limited ordering regions extension). kLOR, //!< CPU has LOR (limited ordering regions extension).
kLRCPC, //!< CPU has LRCPC (load-acquire RCpc instructions) {A64}. kLRCPC, //!< CPU has LRCPC (load-acquire RCpc instructions) {A64}.
@ -553,12 +597,24 @@ public:
kLSE, //!< CPU has LSE (large system extensions) {A64}. kLSE, //!< CPU has LSE (large system extensions) {A64}.
kLSE128, //!< CPU has LSE128 (128-bit atomics) {A64}. kLSE128, //!< CPU has LSE128 (128-bit atomics) {A64}.
kLSE2, //!< CPU has LSE2 (large system extensions v2) {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}. kMOPS, //!< CPU has MOPS (memcpy and memset acceleration instructions) {A64}.
kMPAM, //!< CPU has MPAM (memory system partitioning and monitoring extension) {A64}. kMPAM, //!< CPU has MPAM (memory system partitioning and monitoring extension) {A64}.
kMTE, //!< CPU has MTE (instruction-only memory tagging extension) {A64}. kMTE, //!< CPU has MTE (instruction-only memory tagging extension) {A64}.
kMTE2, //!< CPU has MTE2 (full memory tagging extension) {A64}. kMTE2, //!< CPU has MTE2 (full memory tagging extension) {A64}.
kMTE3, //!< CPU has MTE3 (MTE asymmetric fault handling) {A64}. kMTE3, //!< CPU has MTE3 (MTE asymmetric fault handling) {A64}.
kMTE4, //!< CPU has MTE4 (MTE v4) {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}. kNMI, //!< CPU has NMI (non-maskable Interrupt) {A64}.
kNV, //!< CPU has NV (nested virtualization enchancement) {A64}. kNV, //!< CPU has NV (nested virtualization enchancement) {A64}.
kNV2, //!< CPU has NV2 (enhanced support for nested virtualization) {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}. kPAN2, //!< CPU has PAN2 (PAN s1e1R and s1e1W variants) {A64}.
kPAN3, //!< CPU has PAN3 (support for SCTLR_ELx.EPAN) {A64}. kPAN3, //!< CPU has PAN3 (support for SCTLR_ELx.EPAN) {A64}.
kPAUTH, //!< CPU has PAUTH (pointer authentication extension) {A64}. kPAUTH, //!< CPU has PAUTH (pointer authentication extension) {A64}.
kPFAR, //!< CPU has PFAR (physical fault address registers) {A64}.
kPMU, //!< CPU has PMU {A64}. kPMU, //!< CPU has PMU {A64}.
kPMULL, //!< CPU has PMULL {A64}. kPMULL, //!< CPU has PMULL (ASIMD PMULL instructions) {A64}.
kPRFMSLC, //!< CPU has PRFMSLC (PRFM instructions support the SLC target) {A64} kPRFMSLC, //!< CPU has PRFMSLC (PRFM instructions support the SLC target) {A64}.
kRAS, //!< CPU has RAS (reliability, availability and serviceability extensions). kRAS, //!< CPU has RAS (reliability, availability and serviceability extensions).
kRAS1_1, //!< CPU has RASv1p1 (RAS v1.1). kRAS1_1, //!< CPU has RASv1p1 (RAS v1.1).
kRAS2, //!< CPU has RASv2 (RAS v2). kRAS2, //!< CPU has RASv2 (RAS v2).
kRASSA2, //!< CPU has RASSAv2 (RAS v2 system architecture).
kRDM, //!< CPU has RDM (rounding double multiply accumulate) {A64}. kRDM, //!< CPU has RDM (rounding double multiply accumulate) {A64}.
kRME, //!< CPU has RME (memory encryption contexts extension) {A64}. kRME, //!< CPU has RME (memory encryption contexts extension) {A64}.
kRNG, //!< CPU has RNG (random number generation). kRNG, //!< CPU has RNG (random number generation).
kRNG_TRAP, //!< CPU has RNG_TRAP (random number trap to EL3 field) {A64}. 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}. 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). 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). kSHA1, //!< CPU has SHA1 (ASIMD SHA1 instructions).
kSHA256, //!< CPU has SHA256 (ASIMD SHA256 instructions). kSHA256, //!< CPU has SHA256 (ASIMD SHA256 instructions).
kSHA3, //!< CPU has SHA3 (ASIMD EOR3, RAX1, XAR, and BCAX 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, //!< CPU has SME2 (SME v2) {A64}.
kSME2_1, //!< CPU has SME2p1 (SME v2.1) {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_B16B16, //!< CPU has SME_B16B16 (SME non-widening BFloat16 to BFloat16 arithmetic) {A64}.
kSME_B16F32, //!< CPU has SME_B16F32 {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 {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_F16F16, //!< CPU has SME_F16F16 (SME2.1 non-widening half-precision FP16 to FP16 arithmetic) {A64}.
kSME_F16F32, //!< CPU has SME_F16F32 {A64}. kSME_F16F32, //!< CPU has SME_F16F32 {A64}.
kSME_F32F32, //!< CPU has SME_F32F32 {A64}. kSME_F32F32, //!< CPU has SME_F32F32 {A64}.
kSME_F64F64, //!< CPU has SME_F64F64 {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_FA64, //!< CPU has SME_FA64 {A64}.
kSME_I16I32, //!< CPU has SME_I16I32 {A64}. kSME_I16I32, //!< CPU has SME_I16I32 {A64}.
kSME_I16I64, //!< CPU has SME_I16I64 {A64}. kSME_I16I64, //!< CPU has SME_I16I64 {A64}.
kSME_I8I32, //!< CPU has SME_I8I32 {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). kSPECRES, //!< CPU has SPECRES (speculation restriction instructions).
kSPECRES2, //!< CPU has SPECRES2 (clear other speculative predictions). 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). kSSBS, //!< CPU has SSBS (speculative store bypass safe instruction).
kSSBS2, //!< CPU has SSBS2 (MRS and MSR instructions for SSBS). 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}. kSVE, //!< CPU has SVE (SVE v1 - scalable vector extension) {A64}.
kSVE2, //!< CPU has SVE2 (SVE v2) {A64}. kSVE2, //!< CPU has SVE2 (SVE v2) {A64}.
kSVE2_1, //!< CPU has SVE2p1 (SVE v2.1) {A64}. kSVE2_1, //!< CPU has SVE2p1 (SVE v2.1) {A64}.
@ -620,12 +703,17 @@ public:
kSYSINSTR128, //!< CPU has SYSINSTR128 (128-bit system instructions) {A64}. kSYSINSTR128, //!< CPU has SYSINSTR128 (128-bit system instructions) {A64}.
kSYSREG128, //!< CPU has SYSREG128 (128-bit system registers) {A64}. kSYSREG128, //!< CPU has SYSREG128 (128-bit system registers) {A64}.
kTHE, //!< CPU has THE (translation hardening extension). 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). 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}. kUAO, //!< CPU has UAO (AArch64 v8.2 UAO PState) {A64}.
kVFP_D32, //!< CPU has VFP_D32 (32 VFP-D registers) (ARM/THUMB only). kVFP_D32, //!< CPU has VFP_D32 (32 VFP-D registers) (ARM/THUMB only).
kVHE, //!< CPU has VHE (virtual host extension). 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}. 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}. kXS, //!< CPU has XS (XS attribute in TLBI and DSB instructions) {A64}.
// @EnumValuesEnd@ // @EnumValuesEnd@
@ -633,6 +721,7 @@ public:
}; };
#define ASMJIT_ARM_FEATURE(FEATURE) \ #define ASMJIT_ARM_FEATURE(FEATURE) \
/*! Tests whether FEATURE is present. */ \
ASMJIT_INLINE_NODEBUG bool has##FEATURE() const noexcept { return has(ARM::k##FEATURE); } ASMJIT_INLINE_NODEBUG bool has##FEATURE() const noexcept { return has(ARM::k##FEATURE); }
ASMJIT_ARM_FEATURE(THUMB) ASMJIT_ARM_FEATURE(THUMB)
@ -642,26 +731,45 @@ public:
ASMJIT_ARM_FEATURE(ARMv7) ASMJIT_ARM_FEATURE(ARMv7)
ASMJIT_ARM_FEATURE(ARMv8a) ASMJIT_ARM_FEATURE(ARMv8a)
ASMJIT_ARM_FEATURE(ABLE)
ASMJIT_ARM_FEATURE(ADERR)
ASMJIT_ARM_FEATURE(AES) ASMJIT_ARM_FEATURE(AES)
ASMJIT_ARM_FEATURE(AFP) 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(ASIMD)
ASMJIT_ARM_FEATURE(BF16) ASMJIT_ARM_FEATURE(BF16)
ASMJIT_ARM_FEATURE(BRBE)
ASMJIT_ARM_FEATURE(BTI) ASMJIT_ARM_FEATURE(BTI)
ASMJIT_ARM_FEATURE(BWE)
ASMJIT_ARM_FEATURE(CCIDX) ASMJIT_ARM_FEATURE(CCIDX)
ASMJIT_ARM_FEATURE(CHK) ASMJIT_ARM_FEATURE(CHK)
ASMJIT_ARM_FEATURE(CLRBHB) 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(CPUID)
ASMJIT_ARM_FEATURE(CRC32) ASMJIT_ARM_FEATURE(CRC32)
ASMJIT_ARM_FEATURE(CSSC) ASMJIT_ARM_FEATURE(CSSC)
ASMJIT_ARM_FEATURE(CSV2)
ASMJIT_ARM_FEATURE(CSV2_3)
ASMJIT_ARM_FEATURE(CSV3)
ASMJIT_ARM_FEATURE(D128) ASMJIT_ARM_FEATURE(D128)
ASMJIT_ARM_FEATURE(DGH) ASMJIT_ARM_FEATURE(DGH)
ASMJIT_ARM_FEATURE(DIT) ASMJIT_ARM_FEATURE(DIT)
ASMJIT_ARM_FEATURE(DOTPROD) ASMJIT_ARM_FEATURE(DOTPROD)
ASMJIT_ARM_FEATURE(DPB) ASMJIT_ARM_FEATURE(DPB)
ASMJIT_ARM_FEATURE(DPB2) ASMJIT_ARM_FEATURE(DPB2)
ASMJIT_ARM_FEATURE(EBEP)
ASMJIT_ARM_FEATURE(EBF16) ASMJIT_ARM_FEATURE(EBF16)
ASMJIT_ARM_FEATURE(ECBHB)
ASMJIT_ARM_FEATURE(ECV) ASMJIT_ARM_FEATURE(ECV)
ASMJIT_ARM_FEATURE(EDHSR)
ASMJIT_ARM_FEATURE(EDSP) ASMJIT_ARM_FEATURE(EDSP)
ASMJIT_ARM_FEATURE(FAMINMAX)
ASMJIT_ARM_FEATURE(FCMA) ASMJIT_ARM_FEATURE(FCMA)
ASMJIT_ARM_FEATURE(FGT) ASMJIT_ARM_FEATURE(FGT)
ASMJIT_ARM_FEATURE(FGT2) ASMJIT_ARM_FEATURE(FGT2)
@ -672,13 +780,25 @@ public:
ASMJIT_ARM_FEATURE(FP) ASMJIT_ARM_FEATURE(FP)
ASMJIT_ARM_FEATURE(FP16) ASMJIT_ARM_FEATURE(FP16)
ASMJIT_ARM_FEATURE(FP16CONV) 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(FRINTTS)
ASMJIT_ARM_FEATURE(GCS) 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(HBC)
ASMJIT_ARM_FEATURE(HCX) ASMJIT_ARM_FEATURE(HCX)
ASMJIT_ARM_FEATURE(HPDS)
ASMJIT_ARM_FEATURE(HPDS2)
ASMJIT_ARM_FEATURE(I8MM) ASMJIT_ARM_FEATURE(I8MM)
ASMJIT_ARM_FEATURE(IDIVA) ASMJIT_ARM_FEATURE(IDIVA)
ASMJIT_ARM_FEATURE(IDIVT) ASMJIT_ARM_FEATURE(IDIVT)
ASMJIT_ARM_FEATURE(ITE)
ASMJIT_ARM_FEATURE(JSCVT) ASMJIT_ARM_FEATURE(JSCVT)
ASMJIT_ARM_FEATURE(LOR) ASMJIT_ARM_FEATURE(LOR)
ASMJIT_ARM_FEATURE(LRCPC) ASMJIT_ARM_FEATURE(LRCPC)
@ -690,12 +810,24 @@ public:
ASMJIT_ARM_FEATURE(LSE) ASMJIT_ARM_FEATURE(LSE)
ASMJIT_ARM_FEATURE(LSE128) ASMJIT_ARM_FEATURE(LSE128)
ASMJIT_ARM_FEATURE(LSE2) 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(MOPS)
ASMJIT_ARM_FEATURE(MPAM) ASMJIT_ARM_FEATURE(MPAM)
ASMJIT_ARM_FEATURE(MTE) ASMJIT_ARM_FEATURE(MTE)
ASMJIT_ARM_FEATURE(MTE2) ASMJIT_ARM_FEATURE(MTE2)
ASMJIT_ARM_FEATURE(MTE3) ASMJIT_ARM_FEATURE(MTE3)
ASMJIT_ARM_FEATURE(MTE4) 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(NMI)
ASMJIT_ARM_FEATURE(NV) ASMJIT_ARM_FEATURE(NV)
ASMJIT_ARM_FEATURE(NV2) ASMJIT_ARM_FEATURE(NV2)
@ -703,19 +835,28 @@ public:
ASMJIT_ARM_FEATURE(PAN2) ASMJIT_ARM_FEATURE(PAN2)
ASMJIT_ARM_FEATURE(PAN3) ASMJIT_ARM_FEATURE(PAN3)
ASMJIT_ARM_FEATURE(PAUTH) ASMJIT_ARM_FEATURE(PAUTH)
ASMJIT_ARM_FEATURE(PFAR)
ASMJIT_ARM_FEATURE(PMU) ASMJIT_ARM_FEATURE(PMU)
ASMJIT_ARM_FEATURE(PMULL) ASMJIT_ARM_FEATURE(PMULL)
ASMJIT_ARM_FEATURE(PRFMSLC) ASMJIT_ARM_FEATURE(PRFMSLC)
ASMJIT_ARM_FEATURE(RAS) ASMJIT_ARM_FEATURE(RAS)
ASMJIT_ARM_FEATURE(RAS1_1) ASMJIT_ARM_FEATURE(RAS1_1)
ASMJIT_ARM_FEATURE(RAS2) ASMJIT_ARM_FEATURE(RAS2)
ASMJIT_ARM_FEATURE(RASSA2)
ASMJIT_ARM_FEATURE(RDM) ASMJIT_ARM_FEATURE(RDM)
ASMJIT_ARM_FEATURE(RME) ASMJIT_ARM_FEATURE(RME)
ASMJIT_ARM_FEATURE(RNG) ASMJIT_ARM_FEATURE(RNG)
ASMJIT_ARM_FEATURE(RNG_TRAP) ASMJIT_ARM_FEATURE(RNG_TRAP)
ASMJIT_ARM_FEATURE(RPRES) ASMJIT_ARM_FEATURE(RPRES)
ASMJIT_ARM_FEATURE(RPRFM) 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(SB)
ASMJIT_ARM_FEATURE(SCTLR2)
ASMJIT_ARM_FEATURE(SEBEP)
ASMJIT_ARM_FEATURE(SEL2)
ASMJIT_ARM_FEATURE(SHA1) ASMJIT_ARM_FEATURE(SHA1)
ASMJIT_ARM_FEATURE(SHA256) ASMJIT_ARM_FEATURE(SHA256)
ASMJIT_ARM_FEATURE(SHA3) ASMJIT_ARM_FEATURE(SHA3)
@ -732,14 +873,32 @@ public:
ASMJIT_ARM_FEATURE(SME_F16F32) ASMJIT_ARM_FEATURE(SME_F16F32)
ASMJIT_ARM_FEATURE(SME_F32F32) ASMJIT_ARM_FEATURE(SME_F32F32)
ASMJIT_ARM_FEATURE(SME_F64F64) ASMJIT_ARM_FEATURE(SME_F64F64)
ASMJIT_ARM_FEATURE(SME_F8F16)
ASMJIT_ARM_FEATURE(SME_F8F32)
ASMJIT_ARM_FEATURE(SME_FA64) ASMJIT_ARM_FEATURE(SME_FA64)
ASMJIT_ARM_FEATURE(SME_I16I32) ASMJIT_ARM_FEATURE(SME_I16I32)
ASMJIT_ARM_FEATURE(SME_I16I64) ASMJIT_ARM_FEATURE(SME_I16I64)
ASMJIT_ARM_FEATURE(SME_I8I32) 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(SPECRES)
ASMJIT_ARM_FEATURE(SPECRES2) ASMJIT_ARM_FEATURE(SPECRES2)
ASMJIT_ARM_FEATURE(SPMU)
ASMJIT_ARM_FEATURE(SSBS) ASMJIT_ARM_FEATURE(SSBS)
ASMJIT_ARM_FEATURE(SSBS2) 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(SVE)
ASMJIT_ARM_FEATURE(SVE2) ASMJIT_ARM_FEATURE(SVE2)
ASMJIT_ARM_FEATURE(SVE2_1) ASMJIT_ARM_FEATURE(SVE2_1)
@ -757,12 +916,17 @@ public:
ASMJIT_ARM_FEATURE(SYSINSTR128) ASMJIT_ARM_FEATURE(SYSINSTR128)
ASMJIT_ARM_FEATURE(SYSREG128) ASMJIT_ARM_FEATURE(SYSREG128)
ASMJIT_ARM_FEATURE(THE) ASMJIT_ARM_FEATURE(THE)
ASMJIT_ARM_FEATURE(TLBIOS)
ASMJIT_ARM_FEATURE(TLBIRANGE)
ASMJIT_ARM_FEATURE(TLBIW)
ASMJIT_ARM_FEATURE(TME) ASMJIT_ARM_FEATURE(TME)
ASMJIT_ARM_FEATURE(TRF) ASMJIT_ARM_FEATURE(TRF)
ASMJIT_ARM_FEATURE(UAO) ASMJIT_ARM_FEATURE(UAO)
ASMJIT_ARM_FEATURE(VFP_D32) ASMJIT_ARM_FEATURE(VFP_D32)
ASMJIT_ARM_FEATURE(VHE) ASMJIT_ARM_FEATURE(VHE)
ASMJIT_ARM_FEATURE(VMID16)
ASMJIT_ARM_FEATURE(WFXT) ASMJIT_ARM_FEATURE(WFXT)
ASMJIT_ARM_FEATURE(XNX)
ASMJIT_ARM_FEATURE(XS) ASMJIT_ARM_FEATURE(XS)
#undef ASMJIT_ARM_FEATURE #undef ASMJIT_ARM_FEATURE
@ -785,6 +949,7 @@ public:
ASMJIT_INLINE_NODEBUG CpuFeatures() noexcept {} ASMJIT_INLINE_NODEBUG CpuFeatures() noexcept {}
ASMJIT_INLINE_NODEBUG CpuFeatures(const CpuFeatures& other) noexcept = default; 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 {} 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 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) const noexcept { return equals(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); }
//! \} //! \}
@ -849,6 +1014,7 @@ public:
//! \name Manipulation //! \name Manipulation
//! \{ //! \{
//! Clears all features set.
ASMJIT_INLINE_NODEBUG void reset() noexcept { _data.reset(); } ASMJIT_INLINE_NODEBUG void reset() noexcept { _data.reset(); }
//! Adds the given CPU `featureId` to the list of features. //! 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)...); } ASMJIT_INLINE_NODEBUG void remove(Args&&... args) noexcept { return _data.remove(std::forward<Args>(args)...); }
//! Tests whether this CPU features matches `other`. //! 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. //! Architecture.
Arch _arch; Arch _arch {};
//! Sub-architecture. //! Sub-architecture.
SubArch _subArch; SubArch _subArch {};
//! True if the CPU was detected, false if the detection failed or it's not available. //! True if the CPU was detected, false if the detection failed or it's not available.
bool _wasDetected; bool _wasDetected {};
//! Reserved for future use. //! Reserved for future use.
uint8_t _reserved; uint8_t _reserved {};
//! CPU family ID. //! CPU family ID.
uint32_t _familyId; uint32_t _familyId {};
//! CPU model ID. //! CPU model ID.
uint32_t _modelId; uint32_t _modelId {};
//! CPU brand ID. //! CPU brand ID.
uint32_t _brandId; uint32_t _brandId {};
//! CPU stepping. //! CPU stepping.
uint32_t _stepping; uint32_t _stepping {};
//! Processor type. //! Processor type.
uint32_t _processorType; uint32_t _processorType {};
//! Maximum number of addressable IDs for logical processors. //! Maximum number of addressable IDs for logical processors.
uint32_t _maxLogicalProcessors; uint32_t _maxLogicalProcessors {};
//! Cache line size (in bytes). //! Cache line size (in bytes).
uint32_t _cacheLineSize; uint32_t _cacheLineSize {};
//! Number of hardware threads. //! Number of hardware threads.
uint32_t _hwThreadCount; uint32_t _hwThreadCount {};
//! CPU vendor string. //! CPU vendor string.
FixedString<16> _vendor; FixedString<16> _vendor {};
//! CPU brand string. //! CPU brand string.
FixedString<64> _brand; FixedString<64> _brand {};
//! CPU features. //! CPU features.
CpuFeatures _features; CpuFeatures _features {};
//! \} //! \}
//! \name Construction & Destruction //! \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; ASMJIT_INLINE_NODEBUG CpuInfo(const CpuInfo& other) noexcept = default;
//! Creates an unitialized `CpuInfo` instance.
ASMJIT_INLINE_NODEBUG explicit CpuInfo(Globals::NoInit_) noexcept ASMJIT_INLINE_NODEBUG explicit CpuInfo(Globals::NoInit_) noexcept
: _features(Globals::NoInit) {}; : _features(Globals::NoInit) {};
//! \}
//! \name CPU Information Detection
//! \{
//! Returns the host CPU information. //! 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; 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. //! Initializes CpuInfo architecture and sub-architecture members to `arch` and `subArch`, respectively.
ASMJIT_INLINE_NODEBUG void initArch(Arch arch, SubArch subArch = SubArch::kUnknown) noexcept { ASMJIT_INLINE_NODEBUG void initArch(Arch arch, SubArch subArch = SubArch::kUnknown) noexcept {
_arch = arch; _arch = arch;
_subArch = subArch; _subArch = subArch;
} }
ASMJIT_INLINE_NODEBUG void reset() noexcept { memset(this, 0, sizeof(*this)); } //! Resets this \ref CpuInfo to a default constructed state.
ASMJIT_INLINE_NODEBUG void reset() noexcept { *this = CpuInfo{}; }
//! \}
//! \name Overloaded Operators
//! \{
ASMJIT_INLINE_NODEBUG CpuInfo& operator=(const CpuInfo& other) noexcept = default;
//! \} //! \}
@ -1004,7 +1192,7 @@ public:
//! Returns a CPU vendor string. //! Returns a CPU vendor string.
ASMJIT_INLINE_NODEBUG const char* vendor() const noexcept { return _vendor.str; } ASMJIT_INLINE_NODEBUG const char* vendor() const noexcept { return _vendor.str; }
//! Tests whether the CPU vendor string is equal to `s`. //! 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. //! Returns a CPU brand string.
ASMJIT_INLINE_NODEBUG const char* brand() const noexcept { return _brand.str; } 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 { else {
WorkData& wd = workData[outGroup]; WorkData& wd = workData[outGroup];
if (!wd.isAssigned(outId)) { if (!wd.isAssigned(outId) || curId == outId) {
EmitMove: EmitMove:
ASMJIT_PROPAGATE( ASMJIT_PROPAGATE(
emitArgMove( emitArgMove(
BaseReg(archTraits.regTypeToSignature(out.regType()), outId), out.typeId(), BaseReg(archTraits.regTypeToSignature(out.regType()), outId), out.typeId(),
BaseReg(archTraits.regTypeToSignature(cur.regType()), curId), cur.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); wd.reassign(varId, outId, curId);
cur.initReg(out.regType(), outId, out.typeId()); cur.initReg(out.regType(), outId, out.typeId());
if (outId == out.regId()) if (outId == out.regId())

View File

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

View File

@ -194,6 +194,7 @@ ASMJIT_DEFINE_ENUM_FLAGS(DiagnosticOptions)
class ASMJIT_VIRTAPI BaseEmitter { class ASMJIT_VIRTAPI BaseEmitter {
public: public:
ASMJIT_BASE_CLASS(BaseEmitter) ASMJIT_BASE_CLASS(BaseEmitter)
ASMJIT_NONCOPYABLE(BaseEmitter)
//! \name Members //! \name Members
//! \{ //! \{
@ -202,6 +203,11 @@ public:
EmitterType _emitterType = EmitterType::kNone; EmitterType _emitterType = EmitterType::kNone;
//! See \ref EmitterFlags. //! See \ref EmitterFlags.
EmitterFlags _emitterFlags = EmitterFlags::kNone; EmitterFlags _emitterFlags = EmitterFlags::kNone;
//! Instruction alignment.
uint8_t _instructionAlignment = 0u;
//! \cond
uint8_t _reservedBaseEmitter = 0u;
//! \endcond
//! Validation flags in case validation is used. //! 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 //! \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, Arch arch,
const BaseInst& inst, const Operand_* operands, size_t opCount) ASMJIT_NOEXCEPT_TYPE; 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. //! Emit prolog implementation.
EmitProlog emitProlog; EmitProlog emitProlog;
@ -331,10 +337,17 @@ public:
//! Tests whether the emitter is destroyed (only used during destruction). //! Tests whether the emitter is destroyed (only used during destruction).
ASMJIT_INLINE_NODEBUG bool isDestroyed() const noexcept { return hasEmitterFlag(EmitterFlags::kDestroyed); } 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 _addEmitterFlags(EmitterFlags flags) noexcept { _emitterFlags |= flags; }
ASMJIT_INLINE_NODEBUG void _clearEmitterFlags(EmitterFlags flags) noexcept { _emitterFlags &= _emitterFlags & ~flags; } ASMJIT_INLINE_NODEBUG void _clearEmitterFlags(EmitterFlags flags) noexcept { _emitterFlags &= _emitterFlags & ~flags; }
//! \} //! \}
//! \endcond
//! \name Target Information //! \name Target Information
//! \{ //! \{
@ -360,6 +373,17 @@ public:
//! Returns the target architecture's GP register size (4 or 8 bytes). //! Returns the target architecture's GP register size (4 or 8 bytes).
ASMJIT_INLINE_NODEBUG uint32_t registerSize() const noexcept { return environment().registerSize(); } 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 //! \name Initialization & Finalization
@ -540,23 +564,38 @@ public:
//! \name Emitter State //! \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 { ASMJIT_INLINE_NODEBUG void resetState() noexcept {
resetInstOptions(); resetInstOptions();
resetExtraReg(); resetExtraReg();
resetInlineComment(); 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 { ASMJIT_INLINE_NODEBUG State _grabState() noexcept {
State s{_instOptions | _forcedInstOptions, _extraReg, _inlineComment}; State s{_instOptions | _forcedInstOptions, _extraReg, _inlineComment};
resetState(); resetState();
return s; return s;
} }
//! \endcond
//! \} //! \}
//! \name Sections //! \name Sections
//! \{ //! \{
//! Switches the given `section`.
//!
//! Once switched, everything is added to the given `section`.
ASMJIT_API virtual Error section(Section* 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); 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`. //! 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> template<typename... Args>
ASMJIT_INLINE_NODEBUG Error emit(InstId instId, Args&&... operands) { ASMJIT_INLINE_NODEBUG Error emit(InstId instId, Args&&... operands) {
return _emitI(instId, Support::ForwardOp<Args>::forward(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) { ASMJIT_INLINE_NODEBUG Error emitOpArray(InstId instId, const Operand_* operands, size_t opCount) {
return _emitOpArray(instId, operands, 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) { ASMJIT_FORCE_INLINE Error emitInst(const BaseInst& inst, const Operand_* operands, size_t opCount) {
setInstOptions(inst.options()); setInstOptions(inst.options());
setExtraReg(inst.extraReg()); setExtraReg(inst.extraReg());
return _emitOpArray(inst.id(), operands, opCount); return _emitOpArray(inst.id(), operands, opCount);
} }
//! \}
//! \cond INTERNAL //! \cond INTERNAL
//! \name Emit Internals
//! \{
//! Emits an instruction - all 6 operands must be defined. //! 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); 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. //! Emits instruction having operands stored in array.
ASMJIT_API virtual Error _emitOpArray(InstId instId, const Operand_* operands, size_t opCount); ASMJIT_API virtual Error _emitOpArray(InstId instId, const Operand_* operands, size_t opCount);
//! \endcond
//! \} //! \}
//! \endcond
//! \name Emit Utilities //! \name Emit Utilities
//! \{ //! \{
//! Emits a function prolog described by the given function `frame`.
ASMJIT_API Error emitProlog(const FuncFrame& 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); 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); 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. //! Unknown or uninitialized platform vendor.
kUnknown = 0, kUnknown = 0,
//! Maximum value of `PlatformVendor`. //! Maximum value of `Vendor`.
kMaxValue = kUnknown, kMaxValue = kUnknown,
//! Platform vendor detected at compile-time. //! Platform vendor detected at compile-time.
@ -116,7 +116,7 @@ enum class Platform : uint8_t {
//! Platform ABI (application binary interface). //! Platform ABI (application binary interface).
enum class PlatformABI : uint8_t { enum class PlatformABI : uint8_t {
//! Unknown or uninitialied environment. //! Unknown or uninitialized environment.
kUnknown = 0, kUnknown = 0,
//! Microsoft ABI. //! Microsoft ABI.
kMSVC, kMSVC,
@ -126,6 +126,8 @@ enum class PlatformABI : uint8_t {
kAndroid, kAndroid,
//! Cygwin ABI. //! Cygwin ABI.
kCygwin, kCygwin,
//! Darwin ABI.
kDarwin,
//! Maximum value of `PlatformABI`. //! Maximum value of `PlatformABI`.
kMaxValue, kMaxValue,
@ -142,11 +144,26 @@ enum class PlatformABI : uint8_t {
kGNU kGNU
#elif defined(__ANDROID__) #elif defined(__ANDROID__)
kAndroid kAndroid
#elif defined(__APPLE__)
kDarwin
#else #else
kUnknown kUnknown
#endif #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. //! Object format.
//! //!
//! \note AsmJit doesn't really use anything except \ref ObjectFormat::kUnknown and \ref ObjectFormat::kJIT at //! \note AsmJit doesn't really use anything except \ref ObjectFormat::kUnknown and \ref ObjectFormat::kJIT at
@ -186,53 +203,56 @@ public:
//! \{ //! \{
//! Architecture. //! Architecture.
Arch _arch; Arch _arch = Arch::kUnknown;
//! Sub-architecture type. //! Sub-architecture type.
SubArch _subArch; SubArch _subArch = SubArch::kUnknown;
//! Vendor type. //! Vendor type.
Vendor _vendor; Vendor _vendor = Vendor::kUnknown;
//! Platform. //! Platform.
Platform _platform; Platform _platform = Platform::kUnknown;
//! Platform ABI. //! Platform ABI.
PlatformABI _platformABI; PlatformABI _platformABI = PlatformABI::kUnknown;
//! Object format. //! Object format.
ObjectFormat _objectFormat; ObjectFormat _objectFormat = ObjectFormat::kUnknown;
//! Floating point ABI.
FloatABI _floatABI = FloatABI::kHardFloat;
//! Reserved for future use, must be zero. //! Reserved for future use, must be zero.
uint8_t _reserved[2]; uint8_t _reserved = 0;
//! \} //! \}
//! \name Construction & Destruction //! \name Construction & Destruction
//! \{ //! \{
ASMJIT_INLINE_NODEBUG Environment() noexcept : //! Creates a default initialized environment (all values either unknown or set to safe defaults).
_arch(Arch::kUnknown), ASMJIT_INLINE_NODEBUG constexpr Environment() noexcept = default;
_subArch(SubArch::kUnknown), //! Creates a copy of `other` instance.
_vendor(Vendor::kUnknown), ASMJIT_INLINE_NODEBUG constexpr Environment(const Environment& other) noexcept = default;
_platform(Platform::kUnknown),
_platformABI(PlatformABI::kUnknown),
_objectFormat(ObjectFormat::kUnknown),
_reserved { 0, 0 } {}
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, Arch arch,
SubArch subArch = SubArch::kUnknown, SubArch subArch = SubArch::kUnknown,
Vendor vendor = Vendor::kUnknown, Vendor vendor = Vendor::kUnknown,
Platform platform = Platform::kUnknown, Platform platform = Platform::kUnknown,
PlatformABI abi = PlatformABI::kUnknown, PlatformABI platformABI = PlatformABI::kUnknown,
ObjectFormat objectFormat = ObjectFormat::kUnknown) noexcept { ObjectFormat objectFormat = ObjectFormat::kUnknown,
FloatABI floatABI = FloatABI::kHardFloat) noexcept
init(arch, subArch, vendor, platform, abi, objectFormat); : _arch(arch),
} _subArch(subArch),
_vendor(vendor),
ASMJIT_INLINE_NODEBUG Environment(const Environment& other) noexcept = default; _platform(platform),
_platformABI(platformABI),
_objectFormat(objectFormat),
_floatABI(floatABI) {}
//! Returns the host environment constructed from preprocessor macros defined by the compiler. //! 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, //! The returned environment should precisely match the target host architecture, sub-architecture, platform,
//! and ABI. //! and ABI.
static ASMJIT_INLINE_NODEBUG Environment host() noexcept { 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. //! Resets all members of the environment to zero / unknown.
ASMJIT_INLINE_NODEBUG void reset() noexcept { ASMJIT_INLINE_NODEBUG void reset() noexcept { *this = Environment{}; }
_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 bool equals(const Environment& other) const noexcept { //! Tests whether this environment is equal to `other`.
return _packed() == other._packed(); ASMJIT_INLINE_NODEBUG bool equals(const Environment& other) const noexcept { return _packed() == other._packed(); }
}
//! Returns the architecture. //! Returns the architecture.
ASMJIT_INLINE_NODEBUG Arch arch() const noexcept { return _arch; } ASMJIT_INLINE_NODEBUG Arch arch() const noexcept { return _arch; }
@ -298,14 +308,19 @@ public:
ASMJIT_INLINE_NODEBUG PlatformABI platformABI() const noexcept { return _platformABI; } ASMJIT_INLINE_NODEBUG PlatformABI platformABI() const noexcept { return _platformABI; }
//! Returns target's object format. //! Returns target's object format.
ASMJIT_INLINE_NODEBUG ObjectFormat objectFormat() const noexcept { return _objectFormat; } 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( inline void init(
Arch arch, Arch arch,
SubArch subArch = SubArch::kUnknown, SubArch subArch = SubArch::kUnknown,
Vendor vendor = Vendor::kUnknown, Vendor vendor = Vendor::kUnknown,
Platform platform = Platform::kUnknown, Platform platform = Platform::kUnknown,
PlatformABI platformABI = PlatformABI::kUnknown, PlatformABI platformABI = PlatformABI::kUnknown,
ObjectFormat objectFormat = ObjectFormat::kUnknown) noexcept { ObjectFormat objectFormat = ObjectFormat::kUnknown,
FloatABI floatABI = FloatABI::kHardFloat) noexcept {
_arch = arch; _arch = arch;
_subArch = subArch; _subArch = subArch;
@ -313,18 +328,27 @@ public:
_platform = platform; _platform = platform;
_platformABI = platformABI; _platformABI = platformABI;
_objectFormat = objectFormat; _objectFormat = objectFormat;
_reserved[0] = 0; _floatABI = floatABI;
_reserved[1] = 0; _reserved = 0;
} }
//! Tests whether this environment describes a 32-bit X86.
ASMJIT_INLINE_NODEBUG bool isArchX86() const noexcept { return _arch == Arch::kX86; } 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; } 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); } 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); } 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); } 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); } 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); } 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; } 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; } ASMJIT_INLINE_NODEBUG bool isArchRISCV64() const noexcept { return _arch == Arch::kRISCV64; }
//! Tests whether the architecture is 32-bit. //! Tests whether the architecture is 32-bit.
@ -352,13 +376,10 @@ public:
//! Tests whether the environment platform is Windows. //! Tests whether the environment platform is Windows.
ASMJIT_INLINE_NODEBUG bool isPlatformWindows() const noexcept { return _platform == Platform::kWindows; } ASMJIT_INLINE_NODEBUG bool isPlatformWindows() const noexcept { return _platform == Platform::kWindows; }
//! Tests whether the environment platform is Linux. //! Tests whether the environment platform is Linux.
ASMJIT_INLINE_NODEBUG bool isPlatformLinux() const noexcept { return _platform == Platform::kLinux; } ASMJIT_INLINE_NODEBUG bool isPlatformLinux() const noexcept { return _platform == Platform::kLinux; }
//! Tests whether the environment platform is Hurd. //! Tests whether the environment platform is Hurd.
ASMJIT_INLINE_NODEBUG bool isPlatformHurd() const noexcept { return _platform == Platform::kHurd; } ASMJIT_INLINE_NODEBUG bool isPlatformHurd() const noexcept { return _platform == Platform::kHurd; }
//! Tests whether the environment platform is Haiku. //! Tests whether the environment platform is Haiku.
ASMJIT_INLINE_NODEBUG bool isPlatformHaiku() const noexcept { return _platform == Platform::kHaiku; } 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; } ASMJIT_INLINE_NODEBUG bool isMSVC() const noexcept { return _platformABI == PlatformABI::kMSVC; }
//! Tests whether the ABI is GNU. //! Tests whether the ABI is GNU.
ASMJIT_INLINE_NODEBUG bool isGNU() const noexcept { return _platformABI == PlatformABI::kGNU; } 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. //! Returns a calculated stack alignment for this environment.
ASMJIT_API uint32_t stackAlignment() const noexcept; ASMJIT_API uint32_t stackAlignment() const noexcept;
@ -402,6 +425,9 @@ public:
//! Sets the object format to `objectFormat`. //! Sets the object format to `objectFormat`.
ASMJIT_INLINE_NODEBUG void setObjectFormat(ObjectFormat objectFormat) noexcept { _objectFormat = 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 //! \name Static Utilities
@ -465,11 +491,6 @@ public:
return arch == Arch::kX86 || arch == Arch::kX64; 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). //! Tests whether the given architecture family is AArch32 (ARM or THUMB).
static ASMJIT_INLINE_NODEBUG bool isFamilyAArch32(Arch arch) noexcept { static ASMJIT_INLINE_NODEBUG bool isFamilyAArch32(Arch arch) noexcept {
return isArchARM(arch) || isArchThumb(arch); return isArchARM(arch) || isArchThumb(arch);
@ -480,7 +501,12 @@ public:
return isArchAArch64(arch); 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 { static ASMJIT_INLINE_NODEBUG bool isFamilyMIPS(Arch arch) noexcept {
return isArchMIPS32(arch) || isArchMIPS64(arch); return isArchMIPS32(arch) || isArchMIPS64(arch);
} }

View File

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

View File

@ -20,8 +20,8 @@
ASMJIT_BEGIN_NAMESPACE ASMJIT_BEGIN_NAMESPACE
// CallConv - Init & Reset // CallConv - Initialization & Reset
// ======================= // =================================
ASMJIT_FAVOR_SIZE Error CallConv::init(CallConvId ccId, const Environment& environment) noexcept { ASMJIT_FAVOR_SIZE Error CallConv::init(CallConvId ccId, const Environment& environment) noexcept {
reset(); reset();
@ -75,7 +75,7 @@ ASMJIT_FAVOR_SIZE Error FuncDetail::init(const FuncSignature& signature, const E
#if !defined(ASMJIT_NO_AARCH64) #if !defined(ASMJIT_NO_AARCH64)
if (environment.isFamilyAArch64()) if (environment.isFamilyAArch64())
return a64::FuncInternal::initFuncDetail(*this, signature, registerSize); return a64::FuncInternal::initFuncDetail(*this, signature);
#endif #endif
// We should never bubble here as if `cc.init()` succeeded then there has to be an implementation for the current // 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; 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 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 //! - 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. //! Windows 64-bit calling convention and AMD64 SystemV calling convention.
enum class CallConvId : uint8_t { enum class CallConvId : uint8_t {
//! None or invalid (can't be used).
kNone = 0,
// Universal Calling Conventions // 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 //! This is a universal calling convention, which is used to initialize specific calling conventions based on
//! architecture, platform, and its ABI. //! architecture, platform, and its ABI.
kCDecl = 1, kCDecl = 0,
//! `__stdcall` on targets that support this calling convention (X86). //! `__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 //! \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. //! this calling convention it will be replaced by \ref CallConvId::kCDecl.
kStdCall = 2, kStdCall = 1,
//! `__fastcall` on targets that support this calling convention (X86). //! `__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 //! \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. //! 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). //! `__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. //! \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. //! 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). //! `__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 //! \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. //! 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). //! `__attribute__((regparm(1)))` convention (GCC and Clang).
kRegParm1 = 6, kRegParm1 = 5,
//! `__attribute__((regparm(2)))` convention (GCC and Clang). //! `__attribute__((regparm(2)))` convention (GCC and Clang).
kRegParm2 = 7, kRegParm2 = 6,
//! `__attribute__((regparm(3)))` convention (GCC and Clang). //! `__attribute__((regparm(3)))` convention (GCC and Clang).
kRegParm3 = 8, kRegParm3 = 7,
//! 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,
//! AsmJit specific calling convention designed for calling functions inside a multimedia code that don't use many //! 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 //! 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 // 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. //! X64 System-V calling convention.
kX64SystemV = 32, kX64SystemV = 32,
//! X64 Windows calling convention. //! X64 Windows calling convention.
kX64Windows = 33, kX64Windows = 33,
//! Maximum value of `CallConvId`. //! Maximum value of `CallConvId`.
kMaxValue = kX64Windows, kMaxValue = kX64Windows
// Host Calling Conventions // Deprecated Aliases
// ------------------------ // ------------------
//! Host calling convention detected at compile-time. #if !defined(ASMJIT_NO_DEPRECATED)
kHost = ,
#if defined(_DOXYGEN) kNone = kCDecl,
DETECTED_AT_COMPILE_TIME kHost = kCDecl
#elif ASMJIT_ARCH_ARM == 32 && defined(__SOFTFP__) #endif // !ASMJIT_NO_DEPRECATED
kSoftFloat
#elif ASMJIT_ARCH_ARM == 32 && !defined(__SOFTFP__)
kHardFloat
#else
kCDecl
#endif
}; };
//! Strategy used by calling conventions to assign registers to function arguments. //! Strategy used by calling conventions to assign registers to function arguments.
@ -128,6 +119,8 @@ enum class CallConvStrategy : uint8_t {
kX64Windows = 1, kX64Windows = 1,
//! Windows 64-bit __vectorcall register assignment strategy. //! Windows 64-bit __vectorcall register assignment strategy.
kX64VectorCall = 2, kX64VectorCall = 2,
//! Apple's AArch64 calling convention (differs compared to AArch64 calling convention used by Linux).
kAArch64Apple = 3,
//! Maximum value of `CallConvStrategy`. //! Maximum value of `CallConvStrategy`.
kMaxValue = kX64VectorCall kMaxValue = kX64VectorCall
@ -189,6 +182,11 @@ struct CallConv {
//! Natural stack alignment as defined by OS/ABI. //! Natural stack alignment as defined by OS/ABI.
uint8_t _naturalStackAlignment; uint8_t _naturalStackAlignment;
//! \cond INTERNAL
//! Reserved for future use.
uint8_t _reserved[2];
//! \endcond
//! Calling convention flags. //! Calling convention flags.
CallConvFlags _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, //! as it prevents from using an uninitialized data (CallConv doesn't have a constructor that would initialize it,
//! it's just a struct). //! it's just a struct).
ASMJIT_INLINE_NODEBUG void reset() noexcept { ASMJIT_INLINE_NODEBUG void reset() noexcept {
memset(this, 0, sizeof(*this)); *this = CallConv{};
memset(_passedOrder.data(), 0xFF, sizeof(_passedOrder)); memset(_passedOrder.data(), 0xFF, sizeof(_passedOrder));
} }
@ -362,8 +360,15 @@ struct CallConv {
//! Function signature. //! Function signature.
//! //!
//! Contains information about function return type, count of arguments and their TypeIds. Function signature is //! Contains information about a function return type, count of arguments, and their TypeIds. Function signature
//! a low level structure which doesn't contain platform specific or calling convention specific information. //! 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 { struct FuncSignature {
//! \name Constants //! \name Constants
//! \{ //! \{
@ -377,36 +382,91 @@ struct FuncSignature {
//! \{ //! \{
//! Calling convention id. //! Calling convention id.
CallConvId _ccId; CallConvId _ccId = CallConvId::kCDecl;
//! Count of arguments. //! Count of arguments.
uint8_t _argCount; uint8_t _argCount = 0;
//! Index of a first VA or `kNoVarArgs`. //! Index of a first VA or `kNoVarArgs`.
uint8_t _vaIndex; uint8_t _vaIndex = kNoVarArgs;
//! Return value TypeId. //! Return value TypeId.
TypeId _ret; TypeId _ret = TypeId::kVoid;
//! Function arguments TypeIds. //! Reserved for future use.
const TypeId* _args; 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 //! \name Initialization & Reset
//! \{ //! \{
//! Initializes the function signature. //! Resets this function signature to a default constructed state.
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;
}
ASMJIT_INLINE_NODEBUG void reset() noexcept { *this = FuncSignature{}; } 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 //! \name Accessors
//! \{ //! \{
@ -415,6 +475,55 @@ struct FuncSignature {
//! Sets the calling convention to `ccId`; //! Sets the calling convention to `ccId`;
ASMJIT_INLINE_NODEBUG void setCallConvId(CallConvId ccId) noexcept { _ccId = 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 (...). //! Tests whether the function has variable number of arguments (...).
ASMJIT_INLINE_NODEBUG bool hasVarArgs() const noexcept { return _vaIndex != kNoVarArgs; } ASMJIT_INLINE_NODEBUG bool hasVarArgs() const noexcept { return _vaIndex != kNoVarArgs; }
//! Returns the variable arguments (...) index, `kNoVarArgs` if none. //! 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). //! Resets the variable arguments index (making it a non-va function).
ASMJIT_INLINE_NODEBUG void resetVaIndex() noexcept { _vaIndex = kNoVarArgs; } 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 { class FuncSignatureT : public FuncSignature {
public: public:
ASMJIT_INLINE_NODEBUG FuncSignatureT(CallConvId ccId = CallConvId::kHost, uint32_t vaIndex = kNoVarArgs) noexcept { ASMJIT_DEPRECATED("Use FuncSignature::build<RetValueAndArgs>() instead")
static constexpr TypeId ret_args[] = { (TypeId(TypeUtils::TypeIdOfT<RET_ARGS>::kTypeId))... }; ASMJIT_INLINE_NODEBUG constexpr FuncSignatureT(CallConvId ccId = CallConvId::kCDecl, uint32_t vaIndex = kNoVarArgs) noexcept
init(ccId, vaIndex, ret_args[0], ret_args + 1, uint32_t(ASMJIT_ARRAY_SIZE(ret_args) - 1)); : FuncSignature(ccId, vaIndex, (TypeId(TypeUtils::TypeIdOfT<RetValueAndArgs>::kTypeId))... ) {}
}
}; };
//! Function signature builder. ASMJIT_DEPRECATED("Use FuncSignature instead of FuncSignatureBuilder")
class FuncSignatureBuilder : public FuncSignature { typedef FuncSignature FuncSignatureBuilder;
public: #endif // !ASMJIT_NO_DEPRECATED
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)); }
//! \}
};
//! Argument or return value (or its part) as defined by `FuncSignature`, but with register or stack address //! Argument or return value (or its part) as defined by `FuncSignature`, but with register or stack address
//! (and other metadata) assigned. //! (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 { ASMJIT_INLINE_NODEBUG void initTypeId(TypeId typeId) noexcept {
_data = uint32_t(typeId) << kTypeIdShift; _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 { 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; _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 { ASMJIT_INLINE_NODEBUG void initStack(int32_t offset, TypeId typeId) noexcept {
_data = (uint32_t(offset) << kStackOffsetShift) | (uint32_t(typeId) << kTypeIdShift) | kFlagIsStack; _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 { inline void assignRegData(RegType regType, uint32_t regId) noexcept {
ASMJIT_ASSERT((_data & (kRegTypeMask | kRegIdMask)) == 0); ASMJIT_ASSERT((_data & (kRegTypeMask | kRegIdMask)) == 0);
_data |= (uint32_t(regType) << kRegTypeShift) | (regId << kRegIdShift) | kFlagIsReg; _data |= (uint32_t(regType) << kRegTypeShift) | (regId << kRegIdShift) | kFlagIsReg;
} }
//! Assigns a stack location at `offset`.
inline void assignStackOffset(int32_t offset) noexcept { inline void assignStackOffset(int32_t offset) noexcept {
ASMJIT_ASSERT((_data & kStackOffsetMask) == 0); ASMJIT_ASSERT((_data & kStackOffsetMask) == 0);
_data |= (uint32_t(offset) << kStackOffsetShift) | kFlagIsStack; _data |= (uint32_t(offset) << kStackOffsetShift) | kFlagIsStack;
@ -579,7 +637,9 @@ struct FuncValue {
//! Returns true if the value is initialized (explicit bool cast). //! Returns true if the value is initialized (explicit bool cast).
ASMJIT_INLINE_NODEBUG explicit operator bool() const noexcept { return _data != 0; } 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; } 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. //! Tests whether the `FuncValue` has a flag `flag` set.
ASMJIT_INLINE_NODEBUG bool hasFlag(uint32_t flag) const noexcept { return Support::test(_data, flag); } ASMJIT_INLINE_NODEBUG bool hasFlag(uint32_t flag) const noexcept { return Support::test(_data, flag); }
@ -661,40 +721,52 @@ public:
return n; 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; } ASMJIT_INLINE_NODEBUG FuncValue* values() noexcept { return _values; }
//! \overload
ASMJIT_INLINE_NODEBUG const FuncValue* values() const noexcept { return _values; } 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 { inline void resetValue(size_t index) noexcept {
ASMJIT_ASSERT(index < Globals::kMaxValuePack); ASMJIT_ASSERT(index < Globals::kMaxValuePack);
_values[index].reset(); _values[index].reset();
} }
//! Tests whether the value at the given `index` in the pack is assigned.
inline bool hasValue(size_t index) noexcept { inline bool hasValue(size_t index) noexcept {
ASMJIT_ASSERT(index < Globals::kMaxValuePack); ASMJIT_ASSERT(index < Globals::kMaxValuePack);
return _values[index].isInitialized(); 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 { inline void assignReg(size_t index, const BaseReg& reg, TypeId typeId = TypeId::kVoid) noexcept {
ASMJIT_ASSERT(index < Globals::kMaxValuePack); ASMJIT_ASSERT(index < Globals::kMaxValuePack);
ASMJIT_ASSERT(reg.isPhysReg()); ASMJIT_ASSERT(reg.isPhysReg());
_values[index].initReg(reg.type(), reg.id(), typeId); _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 { inline void assignReg(size_t index, RegType regType, uint32_t regId, TypeId typeId = TypeId::kVoid) noexcept {
ASMJIT_ASSERT(index < Globals::kMaxValuePack); ASMJIT_ASSERT(index < Globals::kMaxValuePack);
_values[index].initReg(regType, regId, typeId); _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 { inline void assignStack(size_t index, int32_t offset, TypeId typeId = TypeId::kVoid) noexcept {
ASMJIT_ASSERT(index < Globals::kMaxValuePack); ASMJIT_ASSERT(index < Globals::kMaxValuePack);
_values[index].initStack(offset, typeId); _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) { inline FuncValue& operator[](size_t index) {
ASMJIT_ASSERT(index < Globals::kMaxValuePack); ASMJIT_ASSERT(index < Globals::kMaxValuePack);
return _values[index]; return _values[index];
} }
//! \overload
inline const FuncValue& operator[](size_t index) const { inline const FuncValue& operator[](size_t index) const {
ASMJIT_ASSERT(index < Globals::kMaxValuePack); ASMJIT_ASSERT(index < Globals::kMaxValuePack);
return _values[index]; return _values[index];
@ -717,6 +789,11 @@ enum class FuncAttributes : uint32_t {
kHasFuncCalls = 0x00000020u, kHasFuncCalls = 0x00000020u,
//! Function has aligned save/restore of vector registers. //! Function has aligned save/restore of vector registers.
kAlignedVecSR = 0x00000040u, 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). //! FuncFrame is finalized and can be used by prolog/epilog inserter (PEI).
kIsFinalized = 0x00000800u, kIsFinalized = 0x00000800u,
@ -753,10 +830,8 @@ public:
//! \name Constants //! \name Constants
//! \{ //! \{
enum : uint8_t { //! Function doesn't have a variable number of arguments (`...`).
//! Doesn't have variable number of arguments (`...`). static constexpr uint8_t kNoVarArgs = 0xFFu;
kNoVarArgs = 0xFFu
};
//! \} //! \}
@ -785,22 +860,35 @@ public:
//! \name Construction & Destruction //! \name Construction & Destruction
//! \{ //! \{
//! Creates a default constructed \ref FuncDetail.
ASMJIT_INLINE_NODEBUG FuncDetail() noexcept {} ASMJIT_INLINE_NODEBUG FuncDetail() noexcept {}
//! Copy constructor.
//!
//! Function details are copyable.
ASMJIT_INLINE_NODEBUG FuncDetail(const FuncDetail& other) noexcept = default; ASMJIT_INLINE_NODEBUG FuncDetail(const FuncDetail& other) noexcept = default;
//! Initializes this `FuncDetail` to the given signature. //! Initializes this `FuncDetail` to the given signature.
ASMJIT_API Error init(const FuncSignature& signature, const Environment& environment) noexcept; ASMJIT_API Error init(const FuncSignature& signature, const Environment& environment) noexcept;
ASMJIT_INLINE_NODEBUG void reset() noexcept { *this = FuncDetail{}; }
//! \} //! \}
//! \name Overloaded Operators //! \name Overloaded Operators
//! \{ //! \{
//! Assignment operator, copies `other` to this \ref FuncDetail.
ASMJIT_INLINE_NODEBUG FuncDetail& operator=(const FuncDetail& other) noexcept = default; 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 //! \name Accessors
//! \{ //! \{
@ -957,87 +1045,98 @@ public:
//! \{ //! \{
//! Function attributes. //! Function attributes.
FuncAttributes _attributes; FuncAttributes _attributes {};
//! Target architecture. //! Target architecture.
Arch _arch; Arch _arch {};
//! SP register ID (to access call stack and local stack). //! 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). //! SA register ID (to access stack arguments).
uint8_t _saRegId; uint8_t _saRegId = uint8_t(BaseReg::kIdBad);
//! Red zone size (copied from CallConv). //! Red zone size (copied from CallConv).
uint8_t _redZoneSize; uint8_t _redZoneSize = 0;
//! Spill zone size (copied from CallConv). //! Spill zone size (copied from CallConv).
uint8_t _spillZoneSize; uint8_t _spillZoneSize = 0;
//! Natural stack alignment (copied from CallConv). //! Natural stack alignment (copied from CallConv).
uint8_t _naturalStackAlignment; uint8_t _naturalStackAlignment = 0;
//! Minimum stack alignment to turn on dynamic alignment. //! Minimum stack alignment to turn on dynamic alignment.
uint8_t _minDynamicAlignment; uint8_t _minDynamicAlignment = 0;
//! Call stack alignment. //! Call stack alignment.
uint8_t _callStackAlignment; uint8_t _callStackAlignment = 0;
//! Local stack alignment. //! Local stack alignment.
uint8_t _localStackAlignment; uint8_t _localStackAlignment = 0;
//! Final stack alignment. //! Final stack alignment.
uint8_t _finalStackAlignment; uint8_t _finalStackAlignment = 0;
//! Adjustment of the stack before returning (X86-STDCALL). //! Adjustment of the stack before returning (X86-STDCALL).
uint16_t _calleeStackCleanup; uint16_t _calleeStackCleanup = 0;
//! Call stack size. //! Call stack size.
uint32_t _callStackSize; uint32_t _callStackSize = 0;
//! Local stack size. //! Local stack size.
uint32_t _localStackSize; uint32_t _localStackSize = 0;
//! Final stack size (sum of call stack and local stack). //! 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). //! 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). //! 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. //! 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). //! 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. //! Local stack adjustment in prolog/epilog.
uint32_t _stackAdjustment; uint32_t _stackAdjustment = 0;
//! Registers that are dirty. //! Registers that are dirty.
Support::Array<RegMask, Globals::kNumVirtGroups> _dirtyRegs; Support::Array<RegMask, Globals::kNumVirtGroups> _dirtyRegs {};
//! Registers that must be preserved (copied from CallConv). //! 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. //! 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. //! 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. //! 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. //! 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 //! 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. //! Offset where extra registers that cannot use push/pop are stored.
uint32_t _extraRegSaveOffset; uint32_t _extraRegSaveOffset = 0;
//! \} //! \}
//! \name Construction & Destruction //! \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_INLINE_NODEBUG FuncFrame(const FuncFrame& other) noexcept = default;
ASMJIT_API Error init(const FuncDetail& func) noexcept; //! \}
ASMJIT_INLINE_NODEBUG void reset() noexcept { //! \name Initialization & Reset
memset(this, 0, sizeof(FuncFrame)); //! \{
_spRegId = BaseReg::kIdBad;
_saRegId = BaseReg::kIdBad; //! Initializes the function frame based on `func` detail.
_daOffset = kTagInvalidOffset; 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. //! Tests whether the function calls other functions.
ASMJIT_INLINE_NODEBUG bool hasFuncCalls() const noexcept { return hasAttribute(FuncAttributes::kHasFuncCalls); } 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); } 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); } 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. //! Tests whether the function has AVX enabled.
ASMJIT_INLINE_NODEBUG bool isAvxEnabled() const noexcept { return hasAttribute(FuncAttributes::kX86_AVXEnabled); } ASMJIT_INLINE_NODEBUG bool isAvxEnabled() const noexcept { return hasAttribute(FuncAttributes::kX86_AVXEnabled); }
//! Enables AVX use. //! Enables AVX use.
@ -1251,11 +1357,16 @@ public:
addDirtyRegs(std::forward<Args>(args)...); 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 { ASMJIT_INLINE_NODEBUG void setAllDirty() noexcept {
for (size_t i = 0; i < ASMJIT_ARRAY_SIZE(_dirtyRegs); i++) for (size_t i = 0; i < ASMJIT_ARRAY_SIZE(_dirtyRegs); i++)
_dirtyRegs[i] = 0xFFFFFFFFu; _dirtyRegs[i] = 0xFFFFFFFFu;
} }
//! A helper function to set all registers from the given register `group` dirty.
inline void setAllDirty(RegGroup group) noexcept { inline void setAllDirty(RegGroup group) noexcept {
ASMJIT_ASSERT(group <= RegGroup::kMaxVirt); ASMJIT_ASSERT(group <= RegGroup::kMaxVirt);
_dirtyRegs[group] = 0xFFFFFFFFu; _dirtyRegs[group] = 0xFFFFFFFFu;
@ -1278,6 +1389,7 @@ public:
return _preservedRegs[group]; return _preservedRegs[group];
} }
//! Returns the size of a save-restore are for the required register `group`.
inline uint32_t saveRestoreRegSize(RegGroup group) const noexcept { inline uint32_t saveRestoreRegSize(RegGroup group) const noexcept {
ASMJIT_ASSERT(group <= RegGroup::kMaxVirt); ASMJIT_ASSERT(group <= RegGroup::kMaxVirt);
return _saveRestoreRegSize[group]; return _saveRestoreRegSize[group];
@ -1332,25 +1444,27 @@ public:
//! \{ //! \{
//! Function detail. //! Function detail.
const FuncDetail* _funcDetail; const FuncDetail* _funcDetail {};
//! Register that can be used to access arguments passed by stack. //! 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. //! Reserved for future use.
uint8_t _reserved[3]; uint8_t _reserved[3] {};
//! Mapping of each function argument. //! Mapping of each function argument.
FuncValuePack _argPacks[Globals::kMaxFuncArgs]; FuncValuePack _argPacks[Globals::kMaxFuncArgs] {};
//! \} //! \}
//! \name Construction & Destruction //! \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 explicit FuncArgsAssignment(const FuncDetail* fd = nullptr) noexcept { reset(fd); }
ASMJIT_INLINE_NODEBUG FuncArgsAssignment(const FuncArgsAssignment& other) noexcept { //! Copy constructor.
memcpy(this, &other, sizeof(*this)); 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 { inline void reset(const FuncDetail* fd = nullptr) noexcept {
_funcDetail = fd; _funcDetail = fd;
_saRegId = uint8_t(BaseReg::kIdBad); _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 //! \name Accessors
//! \{ //! \{
//! Returns the associated \ref FuncDetail of this `FuncArgsAssignment`.
ASMJIT_INLINE_NODEBUG const FuncDetail* funcDetail() const noexcept { return _funcDetail; } 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 void setFuncDetail(const FuncDetail* fd) noexcept { _funcDetail = fd; }
ASMJIT_INLINE_NODEBUG bool hasSARegId() const noexcept { return _saRegId != BaseReg::kIdBad; } 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 setSARegId(uint32_t regId) { _saRegId = uint8_t(regId); }
ASMJIT_INLINE_NODEBUG void resetSARegId() { _saRegId = uint8_t(BaseReg::kIdBad); } 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 { inline FuncValue& arg(size_t argIndex, size_t valueIndex) noexcept {
ASMJIT_ASSERT(argIndex < ASMJIT_ARRAY_SIZE(_argPacks)); ASMJIT_ASSERT(argIndex < ASMJIT_ARRAY_SIZE(_argPacks));
return _argPacks[argIndex][valueIndex]; return _argPacks[argIndex][valueIndex];
} }
//! \overload
inline const FuncValue& arg(size_t argIndex, size_t valueIndex) const noexcept { inline const FuncValue& arg(size_t argIndex, size_t valueIndex) const noexcept {
ASMJIT_ASSERT(argIndex < ASMJIT_ARRAY_SIZE(_argPacks)); ASMJIT_ASSERT(argIndex < ASMJIT_ARRAY_SIZE(_argPacks));
return _argPacks[argIndex][valueIndex]; 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 { inline bool isAssigned(size_t argIndex, size_t valueIndex) const noexcept {
ASMJIT_ASSERT(argIndex < ASMJIT_ARRAY_SIZE(_argPacks)); ASMJIT_ASSERT(argIndex < ASMJIT_ARRAY_SIZE(_argPacks));
return _argPacks[argIndex][valueIndex].isAssigned(); 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 { inline void assignReg(size_t argIndex, const BaseReg& reg, TypeId typeId = TypeId::kVoid) noexcept {
ASMJIT_ASSERT(argIndex < ASMJIT_ARRAY_SIZE(_argPacks)); ASMJIT_ASSERT(argIndex < ASMJIT_ARRAY_SIZE(_argPacks));
ASMJIT_ASSERT(reg.isPhysReg()); ASMJIT_ASSERT(reg.isPhysReg());
_argPacks[argIndex][0].initReg(reg.type(), reg.id(), typeId); _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 { inline void assignReg(size_t argIndex, RegType regType, uint32_t regId, TypeId typeId = TypeId::kVoid) noexcept {
ASMJIT_ASSERT(argIndex < ASMJIT_ARRAY_SIZE(_argPacks)); ASMJIT_ASSERT(argIndex < ASMJIT_ARRAY_SIZE(_argPacks));
_argPacks[argIndex][0].initReg(regType, regId, typeId); _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 { inline void assignStack(size_t argIndex, int32_t offset, TypeId typeId = TypeId::kVoid) noexcept {
ASMJIT_ASSERT(argIndex < ASMJIT_ARRAY_SIZE(_argPacks)); ASMJIT_ASSERT(argIndex < ASMJIT_ARRAY_SIZE(_argPacks));
_argPacks[argIndex][0].initStack(offset, typeId); _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 { 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(argIndex < ASMJIT_ARRAY_SIZE(_argPacks));
ASMJIT_ASSERT(reg.isPhysReg()); ASMJIT_ASSERT(reg.isPhysReg());
_argPacks[argIndex][valueIndex].initReg(reg.type(), reg.id(), typeId); _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 { 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)); ASMJIT_ASSERT(argIndex < ASMJIT_ARRAY_SIZE(_argPacks));
_argPacks[argIndex][valueIndex].initReg(regType, regId, typeId); _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 { inline void assignStackInPack(size_t argIndex, size_t valueIndex, int32_t offset, TypeId typeId = TypeId::kVoid) noexcept {
ASMJIT_ASSERT(argIndex < ASMJIT_ARRAY_SIZE(_argPacks)); ASMJIT_ASSERT(argIndex < ASMJIT_ARRAY_SIZE(_argPacks));
_argPacks[argIndex][valueIndex].initStack(offset, typeId); _argPacks[argIndex][valueIndex].initStack(offset, typeId);
@ -1430,6 +1566,9 @@ public:
_assignAllInternal(argIndex + 1, std::forward<Args>(args)...); _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> template<typename... Args>
inline void assignAll(Args&&... args) noexcept { inline void assignAll(Args&&... args) noexcept {
_assignAllInternal(0, std::forward<Args>(args)...); _assignAllInternal(0, std::forward<Args>(args)...);
@ -1442,8 +1581,8 @@ public:
//! Update `FuncFrame` based on function's arguments assignment. //! Update `FuncFrame` based on function's arguments assignment.
//! //!
//! \note You MUST call this in order to use `BaseEmitter::emitArgsAssignment()`, otherwise the FuncFrame would //! \note This function must be called in order to use `BaseEmitter::emitArgsAssignment()`, otherwise the \ref FuncFrame
//! not contain the information necessary to assign all arguments into the registers and/or stack specified. //! 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; ASMJIT_API Error updateFuncFrame(FuncFrame& frame) const noexcept;
//! \} //! \}
@ -1454,4 +1593,3 @@ public:
ASMJIT_END_NAMESPACE ASMJIT_END_NAMESPACE
#endif // ASMJIT_CORE_FUNC_H_INCLUDED #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); ASMJIT_ASSERT(dstWd != nullptr);
dstWd->assign(varId, srcId); dstWd->assign(varId, srcId);
// The best case, register is allocated where it is expected to be. // The best case, register is allocated where it is expected to be. However, we should
if (dstId == srcId) // 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(); 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 { else {
if (ASMJIT_UNLIKELY(srcGroup > RegGroup::kMaxVirt)) if (ASMJIT_UNLIKELY(srcGroup > RegGroup::kMaxVirt))
return DebugUtils::errored(kErrorInvalidState); return DebugUtils::errored(kErrorInvalidState);

View File

@ -87,6 +87,7 @@ ASMJIT_FAVOR_SIZE const char* DebugUtils::errorAsString(Error err) noexcept {
"ExpressionOverflow\0" "ExpressionOverflow\0"
"FailedToOpenAnonymousMemory\0" "FailedToOpenAnonymousMemory\0"
"FailedToOpenFile\0" "FailedToOpenFile\0"
"ProtectionFailure\0"
"<Unknown>\0"; "<Unknown>\0";
static const uint16_t sErrorIndex[] = { 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, 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, 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, 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@ // @EnumStringEnd@

View File

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

View File

@ -24,12 +24,12 @@ ASMJIT_BEGIN_NAMESPACE
Error InstAPI::instIdToString(Arch arch, InstId instId, String& output) noexcept { Error InstAPI::instIdToString(Arch arch, InstId instId, String& output) noexcept {
#if !defined(ASMJIT_NO_X86) #if !defined(ASMJIT_NO_X86)
if (Environment::isFamilyX86(arch)) if (Environment::isFamilyX86(arch))
return x86::InstInternal::instIdToString(arch, instId, output); return x86::InstInternal::instIdToString(instId, output);
#endif #endif
#if !defined(ASMJIT_NO_AARCH64) #if !defined(ASMJIT_NO_AARCH64)
if (Environment::isFamilyAArch64(arch)) if (Environment::isFamilyAArch64(arch))
return a64::InstInternal::instIdToString(arch, instId, output); return a64::InstInternal::instIdToString(instId, output);
#endif #endif
return DebugUtils::errored(kErrorInvalidArch); 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 { InstId InstAPI::stringToInstId(Arch arch, const char* s, size_t len) noexcept {
#if !defined(ASMJIT_NO_X86) #if !defined(ASMJIT_NO_X86)
if (Environment::isFamilyX86(arch)) if (Environment::isFamilyX86(arch))
return x86::InstInternal::stringToInstId(arch, s, len); return x86::InstInternal::stringToInstId(s, len);
#endif #endif
#if !defined(ASMJIT_NO_AARCH64) #if !defined(ASMJIT_NO_AARCH64)
if (Environment::isFamilyAArch64(arch)) if (Environment::isFamilyAArch64(arch))
return a64::InstInternal::stringToInstId(arch, s, len); return a64::InstInternal::stringToInstId(s, len);
#endif #endif
return 0; return 0;
@ -56,13 +56,17 @@ InstId InstAPI::stringToInstId(Arch arch, const char* s, size_t len) noexcept {
#ifndef ASMJIT_NO_VALIDATION #ifndef ASMJIT_NO_VALIDATION
Error InstAPI::validate(Arch arch, const BaseInst& inst, const Operand_* operands, size_t opCount, ValidationFlags validationFlags) noexcept { Error InstAPI::validate(Arch arch, const BaseInst& inst, const Operand_* operands, size_t opCount, ValidationFlags validationFlags) noexcept {
#if !defined(ASMJIT_NO_X86) #if !defined(ASMJIT_NO_X86)
if (Environment::isFamilyX86(arch)) if (Environment::isFamilyX86(arch)) {
return x86::InstInternal::validate(arch, inst, operands, opCount, validationFlags); if (arch == Arch::kX86)
return x86::InstInternal::validateX86(inst, operands, opCount, validationFlags);
else
return x86::InstInternal::validateX64(inst, operands, opCount, validationFlags);
}
#endif #endif
#if !defined(ASMJIT_NO_AARCH64) #if !defined(ASMJIT_NO_AARCH64)
if (Environment::isFamilyAArch64(arch)) if (Environment::isFamilyAArch64(arch))
return a64::InstInternal::validate(arch, inst, operands, opCount, validationFlags); return a64::InstInternal::validate(inst, operands, opCount, validationFlags);
#endif #endif
return DebugUtils::errored(kErrorInvalidArch); 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 !defined(ASMJIT_NO_AARCH64)
if (Environment::isFamilyAArch64(arch)) if (Environment::isFamilyAArch64(arch))
return a64::InstInternal::queryRWInfo(arch, inst, operands, opCount, out); return a64::InstInternal::queryRWInfo(inst, operands, opCount, out);
#endif #endif
return DebugUtils::errored(kErrorInvalidArch); 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 !defined(ASMJIT_NO_AARCH64)
if (Environment::isFamilyAArch64(arch)) if (Environment::isFamilyAArch64(arch))
return a64::InstInternal::queryFeatures(arch, inst, operands, opCount, out); return a64::InstInternal::queryFeatures(inst, operands, opCount, out);
#endif #endif
return DebugUtils::errored(kErrorInvalidArch); 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 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 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 //! \name Statics
@ -312,12 +315,12 @@ public:
return id | (uint32_t(cc) << Support::ConstCTZ<uint32_t(InstIdParts::kARM_Cond)>::value); 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) return id | (uint32_t(dt) << Support::ConstCTZ<uint32_t(InstIdParts::kA32_DT)>::value)
| (uint32_t(cc) << Support::ConstCTZ<uint32_t(InstIdParts::kARM_Cond)>::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) 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(dt2) << Support::ConstCTZ<uint32_t(InstIdParts::kA32_DT2)>::value)
| (uint32_t(cc) << Support::ConstCTZ<uint32_t(InstIdParts::kARM_Cond)>::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) // Common RW Flags (0x000000FF)
// ---------------------------- // ----------------------------
//! Carry flag.
kCF = 0x00000001u,
//! Signed overflow flag. //! Signed overflow flag.
kOF = 0x00000002u, kOF = 0x00000001u,
//! Sign flag (negative/sign, if set). //! Carry flag.
kSF = 0x00000004u, kCF = 0x00000002u,
//! Zero and/or equality flag (1 if zero/equal). //! 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). //! Carry flag (X86, X86_64).
@ -384,12 +387,22 @@ enum class CpuRWFlags : uint32_t {
//! FPU C2 status flag (X86, X86_64). //! FPU C2 status flag (X86, X86_64).
kX86_C2 = 0x00040000u, kX86_C2 = 0x00040000u,
//! FPU C3 status flag (X86, X86_64). //! 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) ASMJIT_DEFINE_ENUM_FLAGS(CpuRWFlags)
//! Operand read/write flags describe how the operand is accessed and some additional features. //! Operand read/write flags describe how the operand is accessed and some additional features.
enum class OpRWFlags { enum class OpRWFlags : uint32_t {
//! No flags. //! No flags.
kNone = 0, kNone = 0,
@ -499,7 +512,8 @@ struct OpRWInfo {
_consecutiveLeadCount = 0; _consecutiveLeadCount = 0;
_resetReserved(); _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); _readByteMask = Support::test(opFlags, OpRWFlags::kRead) ? mask : uint64_t(0);
_writeByteMask = Support::test(opFlags, OpRWFlags::kWrite) ? mask : uint64_t(0); _writeByteMask = Support::test(opFlags, OpRWFlags::kWrite) ? mask : uint64_t(0);
_extendByteMask = 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 { static inline uint32_t JitAllocator_defaultFillPattern() noexcept {
#if ASMJIT_ARCH_X86
// X86 and X86_64 - 4x 'int3' instruction. // X86 and X86_64 - 4x 'int3' instruction.
if (ASMJIT_ARCH_X86)
return 0xCCCCCCCCu; return 0xCCCCCCCCu;
#else
// Unknown... // Unknown...
return 0u; return 0u;
#endif
} }
// JitAllocator - BitVectorRangeIterator // JitAllocator - BitVectorRangeIterator
@ -447,7 +448,7 @@ static inline JitAllocatorPrivateImpl* JitAllocatorImpl_new(const JitAllocator::
// Setup pool count to [1..3]. // Setup pool count to [1..3].
size_t poolCount = 1; size_t poolCount = 1;
if (Support::test(options, JitAllocatorOptions::kUseMultiplePools)) if (Support::test(options, JitAllocatorOptions::kUseMultiplePools))
poolCount = kJitAllocatorMultiPoolCount;; poolCount = kJitAllocatorMultiPoolCount;
// Setup block size [64kB..256MB]. // Setup block size [64kB..256MB].
if (blockSize < 64 * 1024 || blockSize > 256 * 1024 * 1024 || !Support::isPowerOf2(blockSize)) 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; using Opt = JitAllocatorOptions;
VirtMem::HardenedRuntimeInfo hri = VirtMem::hardenedRuntimeInfo();
TestParams testParams[] = { TestParams testParams[] = {
{ "Default" , Opt::kNone, 0, 0 }, { "Default" , Opt::kNone, 0, 0 },
{ "16MB blocks" , Opt::kNone, 16 * 1024 * 1024, 0 }, { "16MB blocks" , Opt::kNone, 16 * 1024 * 1024, 0 },
{ "256B granularity" , Opt::kNone, 0, 256 }, { "256B granularity" , Opt::kNone, 0, 256 },
{ "kUseDualMapping" , Opt::kUseDualMapping , 0, 0 },
{ "kUseMultiplePools" , Opt::kUseMultiplePools, 0, 0 }, { "kUseMultiplePools" , Opt::kUseMultiplePools, 0, 0 },
{ "kFillUnusedMemory" , Opt::kFillUnusedMemory, 0, 0 }, { "kFillUnusedMemory" , Opt::kFillUnusedMemory, 0, 0 },
{ "kImmediateRelease" , Opt::kImmediateRelease, 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" , Opt::kUseLargePages, 0, 0 },
{ "kUseLargePages | kFillUnusedMemory" , Opt::kUseLargePages | Opt::kFillUnusedMemory, 0, 0 }, { "kUseLargePages | kFillUnusedMemory" , Opt::kUseLargePages | Opt::kFillUnusedMemory, 0, 0 },
{ "kUseLargePages | kAlignBlockSizeToLargePage", Opt::kUseLargePages | Opt::kAlignBlockSizeToLargePage, 0, 0 }, { "kUseLargePages | kAlignBlockSizeToLargePage", Opt::kUseLargePages | Opt::kAlignBlockSizeToLargePage, 0, 0 },
{ "kUseDualMapping" , Opt::kUseDualMapping , 0, 0 },
{ "kUseDualMapping | kFillUnusedMemory" , Opt::kUseDualMapping | Opt::kFillUnusedMemory, 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++) { 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); INFO("JitAllocator(%s)", testParams[testId].name);
JitAllocator::CreateParams params {}; 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 //! 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. //! 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 //! 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 //! 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. //! 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. //! 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 //! 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 //! 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 //! 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; uint32_t fillPattern = 0;
// Reset the content of `CreateParams`. // 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. //! Creates a `JitAllocator` instance.
@ -272,17 +272,18 @@ public:
//! Depending on the type of the allocation strategy this could either be: //! 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, //! - 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 //! and Execute permissions) or MAP_JIT, which requires either \ref VirtMem::ProtectJitReadWriteScope or to
//! VirtMem::protectJitMemory() manually. //! call \ref VirtMem::protectJitMemory() manually.
//! - a valid pointer, but not the same as `rx` - this would be valid if dual mapping is used. //! - 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 //! - 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 //! \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 //! \ref VirtMem::ProtectJitReadWriteScope to guard the write, because in case of `MAP_JIT` it would temporarily
//! the permissions of the pointer to RW (that's per thread permissions). if \ref ProtectJitReadWriteScope is //! switch the permissions of the pointer to RW (that's per thread permissions).
//! not used it's important to clear the instruction cache via \ref VirtMem::flushInstructionCache() after the //!
//! write is done. //! 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; } ASMJIT_INLINE_NODEBUG void* rw() const noexcept { return _rw; }
//! Returns size of this span, aligned to the allocator granularity. //! 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. //! 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; 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. //! 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; 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. //! This is mostly for internal purposes, please use \ref WriteScope::write() instead.
template<class Lambda> template<class Lambda>

View File

@ -20,8 +20,10 @@ class CodeHolder;
//! \addtogroup asmjit_virtual_memory //! \addtogroup asmjit_virtual_memory
//! \{ //! \{
//! JIT execution runtime is a special `Target` that is designed to store and //! JIT execution runtime is a special `Target` that is designed to store and execute a generated code.
//! execute the 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 { class ASMJIT_VIRTAPI JitRuntime : public Target {
public: public:
ASMJIT_NONCOPYABLE(JitRuntime) ASMJIT_NONCOPYABLE(JitRuntime)
@ -37,6 +39,16 @@ public:
//! Destroys the `JitRuntime` instance. //! Destroys the `JitRuntime` instance.
ASMJIT_API ~JitRuntime() noexcept override; 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 { ASMJIT_INLINE_NODEBUG void reset(ResetPolicy resetPolicy = ResetPolicy::kSoft) noexcept {
_allocator.reset(resetPolicy); _allocator.reset(resetPolicy);
} }

View File

@ -23,13 +23,15 @@ enum class OperandType : uint32_t {
kReg = 1, kReg = 1,
//! Operand is a memory. //! Operand is a memory.
kMem = 2, kMem = 2,
//! Operand is a register-list.
kRegList = 3,
//! Operand is an immediate value. //! Operand is an immediate value.
kImm = 3, kImm = 4,
//! Operand is a label. //! Operand is a label.
kLabel = 4, kLabel = 5,
//! Maximum value of `OperandType`. //! Maximum value of `OperandType`.
kMaxValue = kLabel kMaxValue = kRegList
}; };
static_assert(uint32_t(OperandType::kMem) == uint32_t(OperandType::kReg) + 1, 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. //! No register - unused, invalid, multiple meanings.
kNone = 0, 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 //! 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 //! 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, kLabelTag = 1,
//! Universal type describing program counter (PC) or instruction pointer (IP) register, if the target architecture //! 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, kPC = 2,
//! 8-bit low general purpose register (X86). //! 8-bit low general purpose register (X86).
@ -64,21 +66,21 @@ enum class RegType : uint8_t {
kGp8Hi = 4, kGp8Hi = 4,
//! 16-bit general purpose register (X86). //! 16-bit general purpose register (X86).
kGp16 = 5, kGp16 = 5,
//! 32-bit general purpose register (X86|ARM). //! 32-bit general purpose register (X86|AArch32|AArch64).
kGp32 = 6, kGp32 = 6,
//! 64-bit general purpose register (X86|ARM). //! 64-bit general purpose register (X86|AArch64).
kGp64 = 7, kGp64 = 7,
//! 8-bit view of a vector register (ARM). //! 8-bit view of a vector register (AArch64).
kVec8 = 8, kVec8 = 8,
//! 16-bit view of a vector register (ARM). //! 16-bit view of a vector register (AArch64).
kVec16 = 9, kVec16 = 9,
//! 32-bit view of a vector register (ARM). //! 32-bit view of a vector register (AArch32|AArch64).
kVec32 = 10, 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. //! \note This is never used for MMX registers on X86, MMX registers have its own category.
kVec64 = 11, kVec64 = 11,
//! 128-bit view of a vector register (X86|ARM). //! 128-bit view of a vector register (X86|AArch32|AArch64).
kVec128 = 12, kVec128 = 12,
//! 256-bit view of a vector register (X86). //! 256-bit view of a vector register (X86).
kVec256 = 13, kVec256 = 13,
@ -98,9 +100,6 @@ enum class RegType : uint8_t {
// X86 Specific Register Types // X86 Specific Register Types
// --------------------------- // ---------------------------
// X86 Specific Register Types
// ===========================
//! Instruction pointer (RIP), only addressable in \ref x86::Mem in 64-bit targets. //! Instruction pointer (RIP), only addressable in \ref x86::Mem in 64-bit targets.
kX86_Rip = kPC, kX86_Rip = kPC,
//! Low GPB register (AL, BL, CL, DL, ...). //! Low GPB register (AL, BL, CL, DL, ...).
@ -137,7 +136,7 @@ enum class RegType : uint8_t {
kX86_Tmm = kExtra + 6, kX86_Tmm = kExtra + 6,
// ARM Specific Register Types // ARM Specific Register Types
// =========================== // ---------------------------
//! Program pointer (PC) register (AArch64). //! Program pointer (PC) register (AArch64).
kARM_PC = kPC, kARM_PC = kPC,
@ -153,7 +152,9 @@ enum class RegType : uint8_t {
kARM_VecS = kVec32, kARM_VecS = kVec32,
//! 64-bit view of VFP/ASIMD register (D). //! 64-bit view of VFP/ASIMD register (D).
kARM_VecD = kVec64, 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, kARM_VecV = kVec128,
//! Maximum value of `RegType`. //! Maximum value of `RegType`.
@ -172,8 +173,8 @@ enum class RegGroup : uint8_t {
//! Describes X86 XMM|YMM|ZMM registers ARM/AArch64 V registers. //! Describes X86 XMM|YMM|ZMM registers ARM/AArch64 V registers.
kVec = 1, kVec = 1,
//! Extra virtual group #2 that can be used by Compiler for register allocation. //! Mask register group compatible with all backends that can use masking.
kExtraVirt2 = 2, kMask = 2,
//! Extra virtual group #3 that can be used by Compiler for register allocation. //! Extra virtual group #3 that can be used by Compiler for register allocation.
kExtraVirt3 = 3, kExtraVirt3 = 3,
@ -186,8 +187,8 @@ enum class RegGroup : uint8_t {
// X86 Specific Register Groups // X86 Specific Register Groups
// ---------------------------- // ----------------------------
//! K register group (KReg) - maps to \ref RegGroup::kExtraVirt2 (X86, X86_64). //! K register group (KReg) - maps to \ref RegGroup::kMask (X86, X86_64).
kX86_K = kExtraVirt2, kX86_K = kMask,
//! MMX register group (MM) - maps to \ref RegGroup::kExtraVirt3 (X86, X86_64). //! MMX register group (MM) - maps to \ref RegGroup::kExtraVirt3 (X86, X86_64).
kX86_MM = kExtraVirt3, kX86_MM = kExtraVirt3,
@ -331,25 +332,25 @@ struct OperandSignature {
ASMJIT_INLINE_NODEBUG constexpr uint32_t bits() const noexcept { return _bits; } ASMJIT_INLINE_NODEBUG constexpr uint32_t bits() const noexcept { return _bits; }
ASMJIT_INLINE_NODEBUG void setBits(uint32_t bits) noexcept { _bits = 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 { ASMJIT_INLINE_NODEBUG constexpr bool hasField() const noexcept {
return (_bits & kFieldMask) != 0; 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 { 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 { 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_INLINE_NODEBUG void setField(uint32_t value) noexcept {
ASMJIT_ASSERT((value & ~(kFieldMask >> kFieldShift)) == 0); ASMJIT_ASSERT(((value << Support::ConstCTZ<kFieldMask>::value) & ~kFieldMask) == 0);
_bits = (_bits & ~kFieldMask) | (value << kFieldShift); _bits = (_bits & ~kFieldMask) | (value << Support::ConstCTZ<kFieldMask>::value);
} }
ASMJIT_INLINE_NODEBUG constexpr OperandSignature subset(uint32_t mask) const noexcept { return OperandSignature{_bits & mask}; } 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 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 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 setMemBaseType(RegType baseType) noexcept { setField<kMemBaseTypeMask>(uint32_t(baseType)); }
ASMJIT_INLINE_NODEBUG void setMemIndexType(RegGroup indexType) noexcept { setField<kMemIndexTypeMask>(uint32_t(indexType)); } 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 setPredicate(uint32_t predicate) noexcept { setField<kPredicateMask>(predicate); }
ASMJIT_INLINE_NODEBUG void setSize(uint32_t size) noexcept { setField<kSizeMask>(size); } ASMJIT_INLINE_NODEBUG void setSize(uint32_t size) noexcept { setField<kSizeMask>(size); }
@ -529,7 +530,12 @@ struct Operand_ {
//! \endcond //! \endcond
//! Initializes the operand from `other` operand (used by operator overloads). //! 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. //! 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 //! \name Accessors
//! \{ //! \{
@ -608,6 +630,8 @@ struct Operand_ {
//! //!
//! \note Improper use of `setSignature()` can lead to hard-to-debug errors. //! \note Improper use of `setSignature()` can lead to hard-to-debug errors.
ASMJIT_INLINE_NODEBUG void setSignature(const Signature& signature) noexcept { _signature = signature; } 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`. //! Returns the type of the operand, see `OpType`.
ASMJIT_INLINE_NODEBUG constexpr OperandType opType() const noexcept { return _signature.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); } ASMJIT_INLINE_NODEBUG constexpr bool isNone() const noexcept { return _signature == Signature::fromBits(0); }
//! Tests whether the operand is a register (`OperandType::kReg`). //! Tests whether the operand is a register (`OperandType::kReg`).
ASMJIT_INLINE_NODEBUG constexpr bool isReg() const noexcept { return opType() == 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`). //! Tests whether the operand is a memory location (`OperandType::kMem`).
ASMJIT_INLINE_NODEBUG constexpr bool isMem() const noexcept { return opType() == OperandType::kMem; } ASMJIT_INLINE_NODEBUG constexpr bool isMem() const noexcept { return opType() == OperandType::kMem; }
//! Tests whether the operand is an immediate (`OperandType::kImm`). //! Tests whether the operand is an immediate (`OperandType::kImm`).
@ -638,32 +666,71 @@ struct Operand_ {
//! not initialized. //! not initialized.
ASMJIT_INLINE_NODEBUG constexpr uint32_t id() const noexcept { return _baseId; } 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`. //! Tests whether the operand is a register matching the given register `type`.
ASMJIT_INLINE_NODEBUG constexpr bool isReg(RegType type) const noexcept { ASMJIT_INLINE_NODEBUG constexpr bool isReg(RegType type) const noexcept {
return _signature.subset(Signature::kOpTypeMask | Signature::kRegTypeMask) == (Signature::fromOpType(OperandType::kReg) | Signature::fromRegType(type)); 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`. //! Tests whether the operand is a register of the provided register group `regGroup`.
ASMJIT_INLINE_NODEBUG constexpr bool isReg(RegType type, uint32_t id) const noexcept { ASMJIT_INLINE_NODEBUG constexpr bool isReg(RegGroup regGroup) const noexcept {
return isReg(type) && this->id() == id; 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. //! 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 { ASMJIT_INLINE_NODEBUG constexpr bool isRegOrMem() const noexcept {
return Support::isBetween<uint32_t>(uint32_t(opType()), uint32_t(OperandType::kReg), uint32_t(OperandType::kMem)); 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) //! \name Accessors (X86 Specific)
@ -680,10 +747,10 @@ struct Operand_ {
} }
#if !defined(ASMJIT_NO_DEPRECATED) #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_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_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") 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. //! 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 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). //! Creates an uninitialized operand (dangerous).
ASMJIT_INLINE_NODEBUG explicit Operand(Globals::NoInit_) noexcept {} ASMJIT_INLINE_NODEBUG explicit Operand(Globals::NoInit_) noexcept {}
@ -818,10 +885,8 @@ struct BaseRegTraits {
kTypeId = uint32_t(TypeId::kVoid), kTypeId = uint32_t(TypeId::kVoid),
//! RegType is not valid by default. //! RegType is not valid by default.
kValid = 0, 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), kType = uint32_t(RegType::kNone),
//! Zero group by default (defaults to GP). //! Zero group by default (defaults to GP).
kGroup = uint32_t(RegGroup::kGp), kGroup = uint32_t(RegGroup::kGp),
@ -834,7 +899,7 @@ struct BaseRegTraits {
}; };
//! \endcond //! \endcond
//! Physical or virtual register operand. //! Physical or virtual register operand (base).
class BaseReg : public Operand { class BaseReg : public Operand {
public: public:
//! \name Constants //! \name Constants
@ -916,7 +981,7 @@ public:
} }
//! Tests whether the register is valid (either virtual or physical). //! 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. //! Tests whether this is a physical register.
ASMJIT_INLINE_NODEBUG constexpr bool isPhysReg() const noexcept { return _baseId < kIdBad; } 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). //! Tests whether the register is a general purpose register (any size).
ASMJIT_INLINE_NODEBUG constexpr bool isGp() const noexcept { return isGroup(RegGroup::kGp); } 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); } 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; using Operand_::isReg;
@ -1098,13 +1165,10 @@ struct RegOnly {
//! \cond INTERNAL //! \cond INTERNAL
//! Adds a template specialization for `REG_TYPE` into the local `RegTraits`. //! 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<> \ template<> \
struct RegTraits<REG_TYPE> { \ struct RegTraits<REG_TYPE> { \
typedef REG RegT; \
\
static constexpr uint32_t kValid = 1; \ static constexpr uint32_t kValid = 1; \
static constexpr uint32_t kCount = COUNT; \
static constexpr RegType kType = REG_TYPE; \ static constexpr RegType kType = REG_TYPE; \
static constexpr RegGroup kGroup = GROUP; \ static constexpr RegGroup kGroup = GROUP; \
static constexpr uint32_t kSize = SIZE; \ static constexpr uint32_t kSize = SIZE; \
@ -1168,6 +1232,196 @@ public:
: BASE(Signature{kSignature}, id) {} : BASE(Signature{kSignature}, id) {}
//! \endcond //! \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. //! Base class for all memory operands.
//! //!
//! The data is split into the following parts: //! The data is split into the following parts:
@ -1316,8 +1570,13 @@ public:
//! Sets the id of the BASE register (without modifying its type). //! Sets the id of the BASE register (without modifying its type).
ASMJIT_INLINE_NODEBUG void setBaseId(uint32_t id) noexcept { _baseId = id; } 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). //! Sets the id of the INDEX register (without modifying its type).
ASMJIT_INLINE_NODEBUG void setIndexId(uint32_t id) noexcept { _data[kDataMemIndexId] = id; } 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. //! 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()); } 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; uint32_t registerCount = arch == Arch::kX86 ? 8 : 16;
_availableRegs[RegGroup::kGp] = Support::lsbMask<RegMask>(registerCount) & ~Support::bitMask(4u); _availableRegs[RegGroup::kGp] = Support::lsbMask<RegMask>(registerCount) & ~Support::bitMask(4u);
_availableRegs[RegGroup::kVec] = Support::lsbMask<RegMask>(registerCount); _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); _availableRegs[RegGroup::kExtraVirt3] = Support::lsbMask<RegMask>(8);
return kErrorOk; return kErrorOk;
} }
@ -75,7 +75,7 @@ public:
case Arch::kAArch64: { case Arch::kAArch64: {
_availableRegs[RegGroup::kGp] = 0xFFFFFFFFu & ~Support::bitMask(18, 31u); _availableRegs[RegGroup::kGp] = 0xFFFFFFFFu & ~Support::bitMask(18, 31u);
_availableRegs[RegGroup::kVec] = 0xFFFFFFFFu; _availableRegs[RegGroup::kVec] = 0xFFFFFFFFu;
_availableRegs[RegGroup::kExtraVirt2] = 0; _availableRegs[RegGroup::kMask] = 0;
_availableRegs[RegGroup::kExtraVirt3] = 0; _availableRegs[RegGroup::kExtraVirt3] = 0;
return kErrorOk; return kErrorOk;
} }
@ -767,6 +767,12 @@ enum class RATiedFlags : uint32_t {
// Instruction Flags (Never used by RATiedReg) // 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. //! 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 //! 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; return nullptr;
} }
// RALocalAllocator - Init & Reset // RALocalAllocator - Initialization & Reset
// =============================== // =========================================
Error RALocalAllocator::init() noexcept { Error RALocalAllocator::init() noexcept {
PhysToWorkMap* physToWorkMap; PhysToWorkMap* physToWorkMap;
@ -137,6 +137,7 @@ Error RALocalAllocator::switchToAssignment(PhysToWorkMap* dstPhysToWorkMap, cons
dst.initMaps(dstPhysToWorkMap, _tmpWorkToPhysMap); dst.initMaps(dstPhysToWorkMap, _tmpWorkToPhysMap);
dst.assignWorkIdsFromPhysIds(); dst.assignWorkIdsFromPhysIds();
// TODO: Remove this - finally enable this functionality.
if (tryMode) if (tryMode)
return kErrorOk; return kErrorOk;
@ -601,6 +602,7 @@ Error RALocalAllocator::allocInst(InstNode* node) noexcept {
tiedReg->_useRewriteMask = 0; tiedReg->_useRewriteMask = 0;
tiedReg->markUseDone(); tiedReg->markUseDone();
raInst->addFlags(RATiedFlags::kInst_RegToMemPatched);
usePending--; usePending--;
rmAllocated = true; 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 // 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. // 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). // 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 // 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) { if (node->isInvoke() && group == RegGroup::kVec) {
const InvokeNode* invokeNode = node->as<InvokeNode>(); const InvokeNode* invokeNode = node->as<InvokeNode>();

View File

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

View File

@ -366,21 +366,18 @@ Error BaseRAPass::initSharedAssignments(const ZoneVector<uint32_t>& sharedAssign
class RABlockVisitItem { class RABlockVisitItem {
public: public:
RABlock* _block {};
uint32_t _index {};
inline RABlockVisitItem(RABlock* block, uint32_t index) noexcept inline RABlockVisitItem(RABlock* block, uint32_t index) noexcept
: _block(block), : _block(block),
_index(index) {} _index(index) {}
inline RABlockVisitItem(const RABlockVisitItem& other) noexcept inline RABlockVisitItem(const RABlockVisitItem& other) noexcept = default;
: _block(other._block),
_index(other._index) {}
inline RABlockVisitItem& operator=(const RABlockVisitItem& other) noexcept = default; inline RABlockVisitItem& operator=(const RABlockVisitItem& other) noexcept = default;
inline RABlock* block() const noexcept { return _block; } inline RABlock* block() const noexcept { return _block; }
inline uint32_t index() const noexcept { return _index; } inline uint32_t index() const noexcept { return _index; }
RABlock* _block;
uint32_t _index;
}; };
Error BaseRAPass::buildCFGViews() noexcept { Error BaseRAPass::buildCFGViews() noexcept {
@ -479,12 +476,18 @@ Error BaseRAPass::buildCFGDominators() noexcept {
entryBlock->setIDom(entryBlock); entryBlock->setIDom(entryBlock);
bool changed = true; bool changed = true;
uint32_t nIters = 0;
#ifndef ASMJIT_NO_LOGGING
uint32_t numIters = 0;
#endif
while (changed) { while (changed) {
nIters++;
changed = false; changed = false;
#ifndef ASMJIT_NO_LOGGING
numIters++;
#endif
uint32_t i = _pov.size(); uint32_t i = _pov.size();
while (i) { while (i) {
RABlock* block = _pov[--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; 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 { static ASMJIT_FORCE_INLINE bool op(BitWord* dst, const BitWord* a, const BitWord* b, const BitWord* c, uint32_t n) noexcept {
BitWord changed = 0; 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++) { for (uint32_t i = 0; i < n; i++) {
BitWord before = dst[i]; BitWord before = dst[i];
BitWord after = Operator::op(before, a[i], b[i], c[i]); BitWord after = Operator::op(before, a[i], b[i], c[i]);
@ -770,7 +780,7 @@ namespace LiveOps {
return changed != 0; 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; bool changed = initial;
const RABlocks& successors = block->successors(); const RABlocks& successors = block->successors();
@ -801,7 +811,6 @@ ASMJIT_FAVOR_SPEED Error BaseRAPass::buildLiveness() noexcept {
uint32_t numAllBlocks = blockCount(); uint32_t numAllBlocks = blockCount();
uint32_t numReachableBlocks = reachableBlockCount(); uint32_t numReachableBlocks = reachableBlockCount();
uint32_t numVisits = numReachableBlocks;
uint32_t numWorkRegs = workRegCount(); uint32_t numWorkRegs = workRegCount();
uint32_t numBitWords = ZoneBitVector::_wordsPerBits(numWorkRegs); uint32_t numBitWords = ZoneBitVector::_wordsPerBits(numWorkRegs);
@ -871,7 +880,7 @@ ASMJIT_FAVOR_SPEED Error BaseRAPass::buildLiveness() noexcept {
if (tiedReg->hasConsecutiveParent()) { if (tiedReg->hasConsecutiveParent()) {
RAWorkReg* consecutiveParentReg = workRegById(tiedReg->consecutiveParent()); 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 // Calculate IN/OUT of Each Block
// ------------------------------ // ------------------------------
#ifndef ASMJIT_NO_LOGGING
uint32_t numVisits = numReachableBlocks;
#endif
{ {
ZoneStack<RABlock*> workList; ZoneStack<RABlock*> workList;
ZoneBitVector workBits; ZoneBitVector workBits;
@ -921,7 +934,9 @@ ASMJIT_FAVOR_SPEED Error BaseRAPass::buildLiveness() noexcept {
} }
} }
} }
#ifndef ASMJIT_NO_LOGGING
numVisits++; numVisits++;
#endif
} }
workList.reset(); workList.reset();
@ -1208,6 +1223,7 @@ ASMJIT_FAVOR_SPEED Error BaseRAPass::binPack(RegGroup group) noexcept {
uint32_t numWorkRegs = workRegs.size(); uint32_t numWorkRegs = workRegs.size();
RegMask availableRegs = _availableRegs[group]; 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 // First try to pack everything that provides register-id hint as these are most likely function arguments and fixed
// (precolored) virtual registers. // (precolored) virtual registers.
@ -1339,19 +1355,31 @@ ASMJIT_FAVOR_SPEED Error BaseRAPass::binPack(RegGroup group) noexcept {
if (workReg->isAllocated()) if (workReg->isAllocated())
continue; continue;
RegMask physRegs = availableRegs; RegMask remainingPhysRegs = availableRegs;
if (physRegs & workReg->preferredMask()) if (remainingPhysRegs & workReg->preferredMask())
physRegs &= workReg->preferredMask(); remainingPhysRegs &= workReg->preferredMask();
while (physRegs) { RegMask physRegs = remainingPhysRegs & ~preservedRegs;
RegMask preferredMask = physRegs; remainingPhysRegs &= preservedRegs;
uint32_t physId = Support::ctz(preferredMask);
for (;;) {
if (!physRegs) {
if (!remainingPhysRegs)
break;
physRegs = remainingPhysRegs;
remainingPhysRegs = 0;
}
uint32_t physId = Support::ctz(physRegs);
if (workReg->clobberSurvivalMask()) { if (workReg->clobberSurvivalMask()) {
preferredMask &= workReg->clobberSurvivalMask(); RegMask preferredMask = (physRegs | remainingPhysRegs) & workReg->clobberSurvivalMask();
if (preferredMask) if (preferredMask) {
if (preferredMask & ~remainingPhysRegs)
preferredMask &= ~remainingPhysRegs;
physId = Support::ctz(preferredMask); physId = Support::ctz(preferredMask);
} }
}
LiveRegSpans& live = _globalLiveSpans[group][physId]; LiveRegSpans& live = _globalLiveSpans[group][physId];
Error err = tmpSpans.nonOverlappingUnionOf(allocator(), live, workReg->liveSpans(), LiveRegData(workReg->virtId())); 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)) if (ASMJIT_UNLIKELY(err != 0xFFFFFFFFu))
return err; return err;
physRegs ^= Support::bitMask(physId); physRegs &= ~Support::bitMask(physId);
remainingPhysRegs &= ~Support::bitMask(physId);
} }
// Keep it in `workRegs` if it was not allocated. // Keep it in `workRegs` if it was not allocated.

View File

@ -335,6 +335,8 @@ public:
//! Clears instruction `flags` from this RAInst. //! Clears instruction `flags` from this RAInst.
ASMJIT_INLINE_NODEBUG void clearFlags(RATiedFlags flags) noexcept { _flags &= ~flags; } 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. //! Tests whether this instruction can be transformed to another instruction if necessary.
ASMJIT_INLINE_NODEBUG bool isTransformable() const noexcept { return hasFlag(RATiedFlags::kInst_IsTransformable); } 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; 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* aData = data();
const char* bData = other; const char* bData = other;
@ -499,8 +499,8 @@ UNIT(core_string) {
EXPECT_EQ(s.capacity(), String::kSSOCapacity); EXPECT_EQ(s.capacity(), String::kSSOCapacity);
EXPECT_EQ(s.data()[0], 'a'); EXPECT_EQ(s.data()[0], 'a');
EXPECT_EQ(s.data()[1], '\0'); EXPECT_EQ(s.data()[1], '\0');
EXPECT_TRUE(s.eq("a")); EXPECT_TRUE(s.equals("a"));
EXPECT_TRUE(s.eq("a", 1)); EXPECT_TRUE(s.equals("a", 1));
EXPECT_EQ(s.assignChars('b', 4), kErrorOk); EXPECT_EQ(s.assignChars('b', 4), kErrorOk);
EXPECT_EQ(s.size(), 4u); EXPECT_EQ(s.size(), 4u);
@ -510,8 +510,8 @@ UNIT(core_string) {
EXPECT_EQ(s.data()[2], 'b'); EXPECT_EQ(s.data()[2], 'b');
EXPECT_EQ(s.data()[3], 'b'); EXPECT_EQ(s.data()[3], 'b');
EXPECT_EQ(s.data()[4], '\0'); EXPECT_EQ(s.data()[4], '\0');
EXPECT_TRUE(s.eq("bbbb")); EXPECT_TRUE(s.equals("bbbb"));
EXPECT_TRUE(s.eq("bbbb", 4)); EXPECT_TRUE(s.equals("bbbb", 4));
EXPECT_EQ(s.assign("abc"), kErrorOk); EXPECT_EQ(s.assign("abc"), kErrorOk);
EXPECT_EQ(s.size(), 3u); EXPECT_EQ(s.size(), 3u);
@ -520,16 +520,16 @@ UNIT(core_string) {
EXPECT_EQ(s.data()[1], 'b'); EXPECT_EQ(s.data()[1], 'b');
EXPECT_EQ(s.data()[2], 'c'); EXPECT_EQ(s.data()[2], 'c');
EXPECT_EQ(s.data()[3], '\0'); EXPECT_EQ(s.data()[3], '\0');
EXPECT_TRUE(s.eq("abc")); EXPECT_TRUE(s.equals("abc"));
EXPECT_TRUE(s.eq("abc", 3)); EXPECT_TRUE(s.equals("abc", 3));
const char* large = "Large string that will not fit into SSO buffer"; const char* large = "Large string that will not fit into SSO buffer";
EXPECT_EQ(s.assign(large), kErrorOk); EXPECT_EQ(s.assign(large), kErrorOk);
EXPECT_TRUE(s.isLargeOrExternal()); EXPECT_TRUE(s.isLargeOrExternal());
EXPECT_EQ(s.size(), strlen(large)); EXPECT_EQ(s.size(), strlen(large));
EXPECT_GT(s.capacity(), String::kSSOCapacity); EXPECT_GT(s.capacity(), String::kSSOCapacity);
EXPECT_TRUE(s.eq(large)); EXPECT_TRUE(s.equals(large));
EXPECT_TRUE(s.eq(large, strlen(large))); EXPECT_TRUE(s.equals(large, strlen(large)));
const char* additional = " (additional content)"; const char* additional = " (additional content)";
EXPECT_TRUE(s.isLargeOrExternal()); EXPECT_TRUE(s.isLargeOrExternal());
@ -543,10 +543,10 @@ UNIT(core_string) {
EXPECT_TRUE(s.isLargeOrExternal()); // Clear should never release the memory. EXPECT_TRUE(s.isLargeOrExternal()); // Clear should never release the memory.
EXPECT_EQ(s.appendUInt(1234), kErrorOk); 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_EQ(s.assignUInt(0xFFFF, 16, 0, StringFormatFlags::kAlternate), kErrorOk);
EXPECT_TRUE(s.eq("0xFFFF")); EXPECT_TRUE(s.equals("0xFFFF"));
StringTmp<64> sTmp; StringTmp<64> sTmp;
EXPECT_TRUE(sTmp.isLargeOrExternal()); EXPECT_TRUE(sTmp.isLargeOrExternal());

View File

@ -53,9 +53,12 @@ union FixedString {
//! \name Utilities //! \name Utilities
//! \{ //! \{
inline bool eq(const char* other) const noexcept { inline bool equals(const char* other) const noexcept { return strcmp(str, other) == 0; }
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; 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 equals(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 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 !eq(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`. //! Truncate the string length into `newSize`.
ASMJIT_API Error truncate(size_t newSize) noexcept; ASMJIT_API Error truncate(size_t newSize) noexcept;
ASMJIT_API bool eq(const char* other, size_t size = SIZE_MAX) const noexcept; ASMJIT_API bool equals(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_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); : 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> template<typename T, typename CountT>
static ASMJIT_INLINE_NODEBUG constexpr T msbMask(const CountT& n) noexcept { static ASMJIT_INLINE_NODEBUG constexpr T msbMask(const CountT& n) noexcept {
typedef typename std::make_unsigned<T>::type U; typedef typename std::make_unsigned<T>::type U;
@ -321,12 +321,12 @@ struct BitScanCalc<T, 0> {
}; };
template<typename T> 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; return BitScanCalc<T, bitSizeOf<T>() / 2u>::clz(BitScanData<T>{x, 1}).n;
} }
template<typename T> 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; return BitScanCalc<T, bitSizeOf<T>() / 2u>::ctz(BitScanData<T>{x, 1}).n;
} }
@ -457,15 +457,14 @@ namespace Internal {
} }
static ASMJIT_INLINE_NODEBUG uint32_t constPopcntImpl(uint64_t x) noexcept { 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 - ((x >> 1) & 0x5555555555555555u);
x = (x & 0x3333333333333333u) + ((x >> 2) & 0x3333333333333333u); x = (x & 0x3333333333333333u) + ((x >> 2) & 0x3333333333333333u);
return uint32_t((((x + (x >> 4)) & 0x0F0F0F0F0F0F0F0Fu) * 0x0101010101010101u) >> 56); return uint32_t((((x + (x >> 4)) & 0x0F0F0F0F0F0F0F0Fu) * 0x0101010101010101u) >> 56);
} #else
else {
return constPopcntImpl(uint32_t(x >> 32)) + return constPopcntImpl(uint32_t(x >> 32)) +
constPopcntImpl(uint32_t(x & 0xFFFFFFFFu)); constPopcntImpl(uint32_t(x & 0xFFFFFFFFu));
} #endif
} }
static ASMJIT_INLINE_NODEBUG uint32_t popcntImpl(uint32_t x) noexcept { static ASMJIT_INLINE_NODEBUG uint32_t popcntImpl(uint32_t x) noexcept {
@ -926,19 +925,6 @@ static ASMJIT_INLINE_NODEBUG const char* findPackedString(const char* p, uint32_
return p; 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. //! Compares two string views.
static ASMJIT_FORCE_INLINE int compareStringViews(const char* aData, size_t aSize, const char* bData, size_t bSize) noexcept { 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); 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); return int(aSize) - int(bSize);
} }
// Support - Memory Read Access - 8 Bits // 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> template<ByteOrder BO = ByteOrder::kNative>
static inline uint32_t readU24u(const void* p) noexcept { 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 b0 = readU8(static_cast<const uint8_t*>(p) + (BO == ByteOrder::kLE ? 2u : 0u));
uint32_t b1 = readU8(static_cast<const uint8_t*>(p) + (BO == ByteOrder::kLE ? 1 : 1)); uint32_t b1 = readU8(static_cast<const uint8_t*>(p) + 1u);
uint32_t b2 = readU8(static_cast<const uint8_t*>(p) + (BO == ByteOrder::kLE ? 0 : 2)); uint32_t b2 = readU8(static_cast<const uint8_t*>(p) + (BO == ByteOrder::kLE ? 0u : 2u));
return (b0 << 16) | (b1 << 8) | b2; 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> template<ByteOrder BO = ByteOrder::kNative>
static inline void writeU24u(void* p, uint32_t v) noexcept { 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)[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); 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); 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 // Support - Array
// =============== // ===============

View File

@ -16,43 +16,6 @@ ASMJIT_BEGIN_NAMESPACE
namespace Support { namespace Support {
//! \cond INTERNAL //! \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 //! \endcond
} // {Support} } // {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). //! 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; } 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); } 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); } 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); } 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); } 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. //! 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. //! 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; } 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); } 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; } 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; } 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; } 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; } 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); } 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; } 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; } 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); } 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); } 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); } 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); } 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); } 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); } static ASMJIT_INLINE_NODEBUG constexpr bool isVec512(TypeId typeId) noexcept { return isBetween(typeId, TypeId::_kVec512Start, TypeId::_kVec512End); }
//! \cond //! \cond

View File

@ -76,8 +76,6 @@
#define MAP_ANONYMOUS MAP_ANON #define MAP_ANONYMOUS MAP_ANON
#endif #endif
#define ASMJIT_ANONYMOUS_MEMORY_USE_FD
// Android NDK doesn't provide `shm_open()` and `shm_unlink()`. // Android NDK doesn't provide `shm_open()` and `shm_unlink()`.
#if !defined(__BIONIC__) && !defined(ASMJIT_NO_SHM_OPEN) #if !defined(__BIONIC__) && !defined(ASMJIT_NO_SHM_OPEN)
#define ASMJIT_HAS_SHM_OPEN #define ASMJIT_HAS_SHM_OPEN
@ -89,18 +87,60 @@
#define ASMJIT_VM_SHM_DETECT 1 #define ASMJIT_VM_SHM_DETECT 1
#endif #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 #define ASMJIT_HAS_PTHREAD_JIT_WRITE_PROTECT_NP
#endif #endif
#endif
#if defined(__APPLE__) && ASMJIT_ARCH_X86 == 0
#define ASMJIT_NO_DUAL_MAPPING
#endif
#if defined(__NetBSD__) && defined(MAP_REMAPDUP) && defined(PROT_MPROTECT) #if defined(__NetBSD__) && defined(MAP_REMAPDUP) && defined(PROT_MPROTECT)
#undef ASMJIT_ANONYMOUS_MEMORY_USE_FD
#define ASMJIT_ANONYMOUS_MEMORY_USE_REMAPDUP #define ASMJIT_ANONYMOUS_MEMORY_USE_REMAPDUP
#endif #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 #endif
#include <atomic> #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) ASMJIT_BEGIN_SUB_NAMESPACE(VirtMem)
// Virtual Memory Utilities // Virtual Memory Utilities
@ -141,6 +181,11 @@ static size_t detectLargePageSize() noexcept {
return ::GetLargePageMinimum(); 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. // Returns windows-specific protectFlags from \ref MemoryFlags.
static DWORD protectFlagsFromMemoryFlags(MemoryFlags memoryFlags) noexcept { static DWORD protectFlagsFromMemoryFlags(MemoryFlags memoryFlags) noexcept {
DWORD protectFlags; DWORD protectFlags;
@ -165,7 +210,12 @@ static DWORD desiredAccessFromMemoryFlags(MemoryFlags memoryFlags) noexcept {
} }
static HardenedRuntimeFlags getHardenedRuntimeFlags() 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 { Error alloc(void** p, size_t size, MemoryFlags memoryFlags) noexcept {
@ -703,8 +753,8 @@ static bool hasHardenedRuntime() noexcept {
// Detects whether MAP_JIT is available. // Detects whether MAP_JIT is available.
static inline bool hasMapJitSupport() noexcept { static inline bool hasMapJitSupport() noexcept {
#if defined(__APPLE__) && TARGET_OS_OSX && ASMJIT_ARCH_ARM >= 64 #if defined(__APPLE__) && TARGET_OS_OSX && ASMJIT_ARCH_X86 == 0
// OSX on AArch64 always uses hardened runtime + MAP_JIT: // 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 // - https://developer.apple.com/documentation/apple_silicon/porting_just-in-time_compilers_to_apple_silicon
return true; return true;
#elif defined(__APPLE__) && TARGET_OS_OSX #elif defined(__APPLE__) && TARGET_OS_OSX
@ -746,6 +796,14 @@ static inline int mmMapJitFromMemoryFlags(MemoryFlags memoryFlags) noexcept {
#endif #endif
} }
static inline bool hasDualMappingSupport() noexcept {
#if defined(ASMJIT_NO_DUAL_MAPPING)
return false;
#else
return true;
#endif
}
static HardenedRuntimeFlags getHardenedRuntimeFlags() noexcept { static HardenedRuntimeFlags getHardenedRuntimeFlags() noexcept {
HardenedRuntimeFlags flags = HardenedRuntimeFlags::kNone; HardenedRuntimeFlags flags = HardenedRuntimeFlags::kNone;
@ -755,6 +813,9 @@ static HardenedRuntimeFlags getHardenedRuntimeFlags() noexcept {
if (hasMapJitSupport()) if (hasMapJitSupport())
flags |= HardenedRuntimeFlags::kMapJit; flags |= HardenedRuntimeFlags::kMapJit;
if (hasDualMappingSupport())
flags |= HardenedRuntimeFlags::kDualMapping;
return flags; return flags;
} }
@ -828,6 +889,7 @@ Error protect(void* p, size_t size, MemoryFlags memoryFlags) noexcept {
// Virtual Memory [Posix] - Dual Mapping // Virtual Memory [Posix] - Dual Mapping
// ===================================== // =====================================
#if !defined(ASMJIT_NO_DUAL_MAPPING)
static Error unmapDualMapping(DualMapping* dm, size_t size) noexcept { static Error unmapDualMapping(DualMapping* dm, size_t size) noexcept {
Error err1 = unmapMemory(dm->rx, size); Error err1 = unmapMemory(dm->rx, size);
Error err2 = kErrorOk; Error err2 = kErrorOk;
@ -843,6 +905,7 @@ static Error unmapDualMapping(DualMapping* dm, size_t size) noexcept {
dm->rw = nullptr; dm->rw = nullptr;
return kErrorOk; return kErrorOk;
} }
#endif // !ASMJIT_NO_DUAL_MAPPING
#if defined(ASMJIT_ANONYMOUS_MEMORY_USE_REMAPDUP) #if defined(ASMJIT_ANONYMOUS_MEMORY_USE_REMAPDUP)
static Error allocDualMappingUsingRemapdup(DualMapping* dmOut, size_t size, MemoryFlags memoryFlags) noexcept { 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 #endif
Error allocDualMapping(DualMapping* dm, size_t size, MemoryFlags memoryFlags) noexcept { #if defined(ASMJIT_ANONYMOUS_MEMORY_USE_MACH_VM_REMAP)
dm->rx = nullptr; static Error asmjitErrorFromKernResult(kern_return_t result) noexcept {
dm->rw = nullptr; 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) static Error allocDualMappingUsingMachVmRemap(DualMapping* dmOut, size_t size, MemoryFlags memoryFlags) noexcept {
return DebugUtils::errored(size == 0 ? kErrorInvalidArgument : kErrorTooLarge); DualMapping dm {};
#if defined(ASMJIT_ANONYMOUS_MEMORY_USE_REMAPDUP) MemoryFlags mmapFlags = MemoryFlags::kAccessReadWrite | (memoryFlags & MemoryFlags::kMapShared);
return allocDualMappingUsingRemapdup(dm, size, memoryFlags); ASMJIT_PROPAGATE(mapMemory(&dm.rx, size, mmapFlags));
#elif defined(ASMJIT_ANONYMOUS_MEMORY_USE_FD)
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); bool preferTmpOverDevShm = Support::test(memoryFlags, MemoryFlags::kMappingPreferTmp);
if (!preferTmpOverDevShm) { if (!preferTmpOverDevShm) {
AnonymousMemoryStrategy strategy; AnonymousMemoryStrategy strategy;
@ -910,13 +1062,39 @@ Error allocDualMapping(DualMapping* dm, size_t size, MemoryFlags memoryFlags) no
dm->rx = ptr[0]; dm->rx = ptr[0];
dm->rw = ptr[1]; dm->rw = ptr[1];
return kErrorOk; 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 #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
#endif // ASMJIT_NO_DUAL_MAPPING
} }
Error releaseDualMapping(DualMapping* dm, size_t size) noexcept { 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); return unmapDualMapping(dm, size);
#endif // ASMJIT_NO_DUAL_MAPPING
} }
#endif #endif
@ -996,8 +1174,8 @@ void protectJitMemory(ProtectJitAccess access) noexcept {
ASMJIT_END_SUB_NAMESPACE ASMJIT_END_SUB_NAMESPACE
// JitAllocator - Tests // Virtual Memory - Tests
// ==================== // ======================
#if defined(ASMJIT_TEST) #if defined(ASMJIT_TEST)
ASMJIT_BEGIN_NAMESPACE ASMJIT_BEGIN_NAMESPACE
@ -1019,6 +1197,7 @@ UNIT(virt_mem) {
INFO(" flags:"); INFO(" flags:");
INFO(" kEnabled: %s" , Support::test(hardenedFlags, VirtMem::HardenedRuntimeFlags::kEnabled ) ? "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(" kMapJit: %s" , Support::test(hardenedFlags, VirtMem::HardenedRuntimeFlags::kMapJit ) ? "true" : "false");
INFO(" kDualMapping: %s", Support::test(hardenedFlags, VirtMem::HardenedRuntimeFlags::kDualMapping) ? "true" : "false");
} }
ASMJIT_END_NAMESPACE ASMJIT_END_NAMESPACE

View File

@ -10,6 +10,7 @@
#ifndef ASMJIT_NO_JIT #ifndef ASMJIT_NO_JIT
#include "../core/globals.h" #include "../core/globals.h"
#include "../core/support.h"
ASMJIT_BEGIN_NAMESPACE ASMJIT_BEGIN_NAMESPACE
@ -143,7 +144,7 @@ enum class MemoryFlags : uint32_t {
//! Request large memory mapped pages. //! 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 //! 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. //! additional mechanism to allocate regular page(s) when large page(s) allocation fails.
kMMapLargePages = 0x00000200u, kMMapLargePages = 0x00000200u,
@ -215,15 +216,31 @@ enum class HardenedRuntimeFlags : uint32_t {
//! architecture. //! architecture.
kEnabled = 0x00000001u, kEnabled = 0x00000001u,
//! Read+Write+Execute can only be allocated with MAP_JIT flag (Apple specific, only available on OSX). //! Read+Write+Execute can only be allocated with MAP_JIT flag (Apple specific, only available on Apple platforms).
kMapJit = 0x00000002u 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) ASMJIT_DEFINE_ENUM_FLAGS(HardenedRuntimeFlags)
//! Hardened runtime information. //! Hardened runtime information.
struct HardenedRuntimeInfo { struct HardenedRuntimeInfo {
//! \name Members
//! \{
//! Hardened runtime flags. //! Hardened runtime flags.
HardenedRuntimeFlags 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. //! 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. //! Makes the given memory block RW protected.

View File

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

View File

@ -9,8 +9,8 @@
ASMJIT_BEGIN_NAMESPACE ASMJIT_BEGIN_NAMESPACE
// ZoneStackBase - Init & Reset // ZoneStackBase - Initialization & Reset
// ============================ // ======================================
Error ZoneStackBase::_init(ZoneAllocator* allocator, size_t middleIndex) noexcept { Error ZoneStackBase::_init(ZoneAllocator* allocator, size_t middleIndex) noexcept {
ZoneAllocator* oldAllocator = _allocator; 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 { Error ZoneVectorBase::_reserve(ZoneAllocator* allocator, uint32_t sizeOfT, uint32_t n) noexcept {
uint32_t oldCapacity = _capacity; uint32_t oldCapacity = _capacity;
if (oldCapacity >= n) return kErrorOk; if (oldCapacity >= n)
return kErrorOk;
uint32_t nBytes = n * sizeOfT; uint32_t nBytes = n * sizeOfT;
if (ASMJIT_UNLIKELY(nBytes < n)) if (ASMJIT_UNLIKELY(nBytes < n))
@ -65,11 +66,10 @@ Error ZoneVectorBase::_reserve(ZoneAllocator* allocator, uint32_t sizeOfT, uint3
return DebugUtils::errored(kErrorOutOfMemory); return DebugUtils::errored(kErrorOutOfMemory);
void* oldData = _data; void* oldData = _data;
if (_size) if (oldData && _size) {
memcpy(newData, oldData, size_t(_size) * sizeOfT); memcpy(newData, oldData, size_t(_size) * sizeOfT);
if (oldData)
allocator->release(oldData, size_t(oldCapacity) * sizeOfT); allocator->release(oldData, size_t(oldCapacity) * sizeOfT);
}
_capacity = uint32_t(allocatedBytes / sizeOfT); _capacity = uint32_t(allocatedBytes / sizeOfT);
ASMJIT_ASSERT(_capacity >= n); ASMJIT_ASSERT(_capacity >= n);
@ -289,9 +289,22 @@ static void test_zone_vector(ZoneAllocator* allocator, const char* typeName) {
} }
EXPECT_FALSE(vec.empty()); EXPECT_FALSE(vec.empty());
EXPECT_EQ(vec.size(), uint32_t(kMax)); 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.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.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); vec.release(allocator);
} }

View File

@ -128,8 +128,8 @@ public:
typedef T* iterator; typedef T* iterator;
typedef const T* const_iterator; typedef const T* const_iterator;
typedef std::reverse_iterator<iterator> reverse_iterator; typedef Support::ArrayReverseIterator<T> reverse_iterator;
typedef std::reverse_iterator<const_iterator> const_reverse_iterator; typedef Support::ArrayReverseIterator<const T> const_reverse_iterator;
//! \name Construction & Destruction //! \name Construction & Destruction
//! \{ //! \{
@ -194,8 +194,13 @@ public:
if (ASMJIT_UNLIKELY(_size == _capacity)) if (ASMJIT_UNLIKELY(_size == _capacity))
ASMJIT_PROPAGATE(grow(allocator, 1)); ASMJIT_PROPAGATE(grow(allocator, 1));
::memmove(static_cast<T*>(_data) + 1, _data, size_t(_size) * sizeof(T)); memmove(static_cast<void*>(static_cast<T*>(_data) + 1),
memcpy(_data, &item, sizeof(T)); static_cast<const void*>(_data),
size_t(_size) * sizeof(T));
memcpy(static_cast<void*>(_data),
static_cast<const void*>(&item),
sizeof(T));
_size++; _size++;
return kErrorOk; return kErrorOk;
@ -209,10 +214,15 @@ public:
ASMJIT_PROPAGATE(grow(allocator, 1)); ASMJIT_PROPAGATE(grow(allocator, 1));
T* dst = static_cast<T*>(_data) + index; T* dst = static_cast<T*>(_data) + index;
::memmove(dst + 1, dst, size_t(_size - index) * sizeof(T)); memmove(static_cast<void*>(dst + 1),
memcpy(dst, &item, sizeof(T)); static_cast<const void*>(dst),
_size++; size_t(_size - index) * sizeof(T));
memcpy(static_cast<void*>(dst),
static_cast<const void*>(&item),
sizeof(T));
_size++;
return kErrorOk; return kErrorOk;
} }
@ -221,9 +231,11 @@ public:
if (ASMJIT_UNLIKELY(_size == _capacity)) if (ASMJIT_UNLIKELY(_size == _capacity))
ASMJIT_PROPAGATE(grow(allocator, 1)); ASMJIT_PROPAGATE(grow(allocator, 1));
memcpy(static_cast<T*>(_data) + _size, &item, sizeof(T)); memcpy(static_cast<void*>(static_cast<T*>(_data) + _size),
_size++; static_cast<const void*>(&item),
sizeof(T));
_size++;
return kErrorOk; return kErrorOk;
} }
@ -234,7 +246,9 @@ public:
ASMJIT_PROPAGATE(grow(allocator, size)); ASMJIT_PROPAGATE(grow(allocator, size));
if (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; _size += size;
} }
@ -249,10 +263,15 @@ public:
ASMJIT_ASSERT(_size < _capacity); ASMJIT_ASSERT(_size < _capacity);
T* data = static_cast<T*>(_data); T* data = static_cast<T*>(_data);
if (_size) if (_size) {
::memmove(data + 1, data, size_t(_size) * sizeof(T)); 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++; _size++;
} }
@ -263,7 +282,9 @@ public:
ASMJIT_FORCE_INLINE void appendUnsafe(const T& item) noexcept { ASMJIT_FORCE_INLINE void appendUnsafe(const T& item) noexcept {
ASMJIT_ASSERT(_size < _capacity); 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++; _size++;
} }
@ -273,17 +294,26 @@ public:
ASMJIT_ASSERT(index <= _size); ASMJIT_ASSERT(index <= _size);
T* dst = static_cast<T*>(_data) + index; T* dst = static_cast<T*>(_data) + index;
::memmove(dst + 1, dst, size_t(_size - index) * sizeof(T)); memmove(static_cast<void*>(dst + 1),
memcpy(dst, &item, sizeof(T)); static_cast<const void*>(dst),
size_t(_size - index) * sizeof(T));
memcpy(static_cast<void*>(dst),
static_cast<const void*>(&item),
sizeof(T));
_size++; _size++;
} }
//! Concatenates all items of `other` at the end of the vector. //! Concatenates all items of `other` at the end of the vector.
ASMJIT_FORCE_INLINE void concatUnsafe(const ZoneVector<T>& other) noexcept { ASMJIT_FORCE_INLINE void concatUnsafe(const ZoneVector<T>& other) noexcept {
uint32_t size = other._size; uint32_t size = other._size;
ASMJIT_ASSERT(_capacity - _size >= size); ASMJIT_ASSERT(_capacity - _size >= size);
if (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; _size += size;
} }
} }
@ -311,8 +341,11 @@ public:
T* data = static_cast<T*>(_data) + i; T* data = static_cast<T*>(_data) + i;
size_t size = --_size - i; size_t size = --_size - i;
if (size) if (size) {
::memmove(data, data + 1, size_t(size) * sizeof(T)); 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. //! Pops the last element from the vector and returns it.
@ -460,8 +493,8 @@ public:
//! \name Overloaded Operators //! \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 equals(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); }
//! \} //! \}
@ -628,7 +661,7 @@ public:
_data[idx] &= (BitWord(1) << bit) - 1u; _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) if (_size != other._size)
return false; return false;
@ -642,6 +675,11 @@ public:
return true; 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 //! \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]; 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 // x86::Assembler - X86BufferWriter
// ================================ // ================================
@ -529,8 +533,6 @@ static ASMJIT_FORCE_INLINE bool x86ShouldUseMovabs(Assembler* self, X86BufferWri
Assembler::Assembler(CodeHolder* code) noexcept : BaseAssembler() { Assembler::Assembler(CodeHolder* code) noexcept : BaseAssembler() {
_archMask = (uint64_t(1) << uint32_t(Arch::kX86)) | _archMask = (uint64_t(1) << uint32_t(Arch::kX86)) |
(uint64_t(1) << uint32_t(Arch::kX64)) ; (uint64_t(1) << uint32_t(Arch::kX64)) ;
assignEmitterFuncs(this);
if (code) if (code)
code->attach(this); 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. const Operand_* rmRel; // Memory operand or operand that holds Label|Imm.
uint32_t rmInfo; // Memory operand's info based on x86MemInfo. 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 rxReg; // Memory index register.
uint32_t opReg; // ModR/M opcode or register id. 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]; Operand_ opArray[Globals::kMaxOpCount];
EmitterUtils::opArrayFromEmitArgs(opArray, o0, o1, o2, opExt); 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)) if (ASMJIT_UNLIKELY(err))
goto Failed; 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. // This sequence seems to be the fastest.
opcode = InstDB::_mainOpcodeTable[instInfo->_mainOpcodeIndex]; opcode = InstDB::_mainOpcodeTable[instInfo->_mainOpcodeIndex];
opReg = opcode.extractModO(); opReg = opcode.extractModO();
rbReg = 0;
opcode |= instInfo->_mainOpcodeValue; opcode |= instInfo->_mainOpcodeValue;
// Encoding Scope // Encoding Scope
@ -991,13 +992,13 @@ CaseX86M_GPB_MulDiv:
goto EmitX86R; goto EmitX86R;
// MOD/RM: Alternative encoding selected via instruction options. // MOD/RM: Alternative encoding selected via instruction options.
opcode += 2; opcode += 2u;
std::swap(opReg, rbReg); std::swap(opReg, rbReg);
goto EmitX86R; goto EmitX86R;
} }
if (isign3 == ENC_OPS2(Reg, Mem)) { if (isign3 == ENC_OPS2(Reg, Mem)) {
opcode += 2; opcode += 2u;
opcode.addArithBySize(o0.x86RmSize()); opcode.addArithBySize(o0.x86RmSize());
opReg = o0.id(); opReg = o0.id();
@ -1075,7 +1076,7 @@ CaseX86M_GPB_MulDiv:
goto EmitX86Op; goto EmitX86Op;
} }
opcode += size != 1 ? (immSize != 1 ? 1 : 3) : 0; opcode += size != 1 ? (immSize != 1 ? 1u : 3u) : 0u;
goto EmitX86R; goto EmitX86R;
} }
@ -1095,7 +1096,7 @@ CaseX86M_GPB_MulDiv:
if (Support::isInt8(immValue) && !Support::test(options, InstOptions::kLongForm)) if (Support::isInt8(immValue) && !Support::test(options, InstOptions::kLongForm))
immSize = 1; immSize = 1;
opcode += memSize != 1 ? (immSize != 1 ? 1 : 3) : 0; opcode += memSize != 1 ? (immSize != 1 ? 1u : 3u) : 0u;
opcode.addPrefixBySize(memSize); opcode.addPrefixBySize(memSize);
rmRel = &o0; rmRel = &o0;
@ -1251,7 +1252,7 @@ CaseX86M_GPB_MulDiv:
// This seems to be the only exception of encoding '66F2' prefix. // This seems to be the only exception of encoding '66F2' prefix.
if (o1.x86RmSize() == 2) writer.emit8(0x66); if (o1.x86RmSize() == 2) writer.emit8(0x66);
opcode += o1.x86RmSize() != 1; opcode += uint32_t(o1.x86RmSize() != 1u);
goto EmitX86M; goto EmitX86M;
} }
break; break;
@ -1379,7 +1380,7 @@ CaseX86M_GPB_MulDiv:
if (ASMJIT_UNLIKELY(o0.id() != Gp::kIdAx || o1.id() != Gp::kIdDx)) if (ASMJIT_UNLIKELY(o0.id() != Gp::kIdAx || o1.id() != Gp::kIdDx))
goto InvalidInstruction; goto InvalidInstruction;
opcode += o0.x86RmSize() != 1; opcode += uint32_t(o0.x86RmSize() != 1u);
opcode.add66hBySize(o0.x86RmSize()); opcode.add66hBySize(o0.x86RmSize());
goto EmitX86Op; goto EmitX86Op;
} }
@ -1395,7 +1396,7 @@ CaseX86M_GPB_MulDiv:
goto AmbiguousOperandSize; goto AmbiguousOperandSize;
rmRel = &o0; rmRel = &o0;
opcode += (size != 1); opcode += uint32_t(size != 1u);
opcode.add66hBySize(size); opcode.add66hBySize(size);
goto EmitX86OpImplicitMem; goto EmitX86OpImplicitMem;
@ -1552,7 +1553,7 @@ CaseX86M_GPB_MulDiv:
if (!Support::test(options, InstOptions::kX86_ModRM)) if (!Support::test(options, InstOptions::kX86_ModRM))
goto EmitX86R; goto EmitX86R;
opcode += 2; opcode += 2u;
std::swap(opReg, rbReg); std::swap(opReg, rbReg);
goto EmitX86R; goto EmitX86R;
} }
@ -1563,7 +1564,7 @@ CaseX86M_GPB_MulDiv:
if (!Support::test(options, InstOptions::kX86_ModRM)) if (!Support::test(options, InstOptions::kX86_ModRM))
goto EmitX86R; goto EmitX86R;
opcode += 2; opcode += 2u;
std::swap(opReg, rbReg); std::swap(opReg, rbReg);
goto EmitX86R; 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. // 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 (opReg == Gp::kIdAx && !rmRel->as<Mem>().hasBaseOrIndex()) {
if (x86ShouldUseMovabs(this, writer, o0.x86RmSize(), options, rmRel->as<Mem>())) { if (x86ShouldUseMovabs(this, writer, o0.x86RmSize(), options, rmRel->as<Mem>())) {
opcode += 0xA0; opcode += 0xA0u;
immValue = rmRel->as<Mem>().offset(); immValue = rmRel->as<Mem>().offset();
goto EmitX86OpMovAbs; goto EmitX86OpMovAbs;
} }
@ -1661,7 +1662,7 @@ CaseX86M_GPB_MulDiv:
if (o0.x86RmSize() == 1) if (o0.x86RmSize() == 1)
FIXUP_GPB(o0, opReg); FIXUP_GPB(o0, opReg);
opcode += 0x8A; opcode += 0x8Au;
goto EmitX86M; 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. // 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 (opReg == Gp::kIdAx && !rmRel->as<Mem>().hasBaseOrIndex()) {
if (x86ShouldUseMovabs(this, writer, o1.x86RmSize(), options, rmRel->as<Mem>())) { if (x86ShouldUseMovabs(this, writer, o1.x86RmSize(), options, rmRel->as<Mem>())) {
opcode += 0xA2; opcode += 0xA2u;
immValue = rmRel->as<Mem>().offset(); immValue = rmRel->as<Mem>().offset();
goto EmitX86OpMovAbs; goto EmitX86OpMovAbs;
} }
@ -1694,7 +1695,7 @@ CaseX86M_GPB_MulDiv:
if (o1.x86RmSize() == 1) if (o1.x86RmSize() == 1)
FIXUP_GPB(o1, opReg); FIXUP_GPB(o1, opReg);
opcode += 0x88; opcode += 0x88u;
goto EmitX86M; goto EmitX86M;
} }
} }
@ -1995,7 +1996,7 @@ CaseX86PushPop_Gp:
if (ASMJIT_UNLIKELY(o1.id() != Gp::kIdCx)) if (ASMJIT_UNLIKELY(o1.id() != Gp::kIdCx))
goto InvalidInstruction; goto InvalidInstruction;
opcode += 2; opcode += 2u;
goto EmitX86R; goto EmitX86R;
} }
@ -2020,7 +2021,7 @@ CaseX86PushPop_Gp:
if (ASMJIT_UNLIKELY(o1.id() != Gp::kIdCx)) if (ASMJIT_UNLIKELY(o1.id() != Gp::kIdCx))
goto InvalidInstruction; goto InvalidInstruction;
opcode += 2; opcode += 2u;
rmRel = &o0; rmRel = &o0;
goto EmitX86M; goto EmitX86M;
} }
@ -2390,7 +2391,7 @@ CaseFpuArith_Mem:
} }
if (o0.x86RmSize() == 8 && commonInfo->hasFlag(InstDB::InstFlags::kFpuM64)) { if (o0.x86RmSize() == 8 && commonInfo->hasFlag(InstDB::InstFlags::kFpuM64)) {
opcode += 4; opcode += 4u;
goto EmitX86M; goto EmitX86M;
} }
@ -2415,7 +2416,7 @@ CaseFpuArith_Mem:
rmRel = &o0; rmRel = &o0;
if (o0.x86RmSize() == 2 && commonInfo->hasFlag(InstDB::InstFlags::kFpuM16)) { if (o0.x86RmSize() == 2 && commonInfo->hasFlag(InstDB::InstFlags::kFpuM16)) {
opcode += 4; opcode += 4u;
goto EmitX86M; goto EmitX86M;
} }
@ -2433,7 +2434,7 @@ CaseFpuArith_Mem:
case InstDB::kEncodingFpuRDef: case InstDB::kEncodingFpuRDef:
if (isign3 == 0) { if (isign3 == 0) {
opcode += 1; opcode += 1u;
goto EmitFpuOp; goto EmitFpuOp;
} }
ASMJIT_FALLTHROUGH; ASMJIT_FALLTHROUGH;
@ -2575,6 +2576,7 @@ CaseFpuArith_Mem:
case InstDB::kEncodingExtMovd: case InstDB::kEncodingExtMovd:
CaseExtMovd: CaseExtMovd:
if (x86IsMmxOrXmm(o0.as<Reg>())) {
opReg = o0.id(); opReg = o0.id();
opcode.add66hIf(Reg::isXmm(o0)); opcode.add66hIf(Reg::isXmm(o0));
@ -2589,8 +2591,10 @@ CaseExtMovd:
rmRel = &o1; rmRel = &o1;
goto EmitX86M; goto EmitX86M;
} }
}
// The following instructions use the secondary opcode. // The following instructions use the secondary opcode.
if (x86IsMmxOrXmm(o1.as<Reg>())) {
opcode &= Opcode::kW; opcode &= Opcode::kW;
opcode |= x86AltOpcodeOf(instInfo); opcode |= x86AltOpcodeOf(instInfo);
opReg = o1.id(); opReg = o1.id();
@ -2607,6 +2611,7 @@ CaseExtMovd:
rmRel = &o0; rmRel = &o0;
goto EmitX86M; goto EmitX86M;
} }
}
break; break;
case InstDB::kEncodingExtMovq: case InstDB::kEncodingExtMovq:
@ -2621,7 +2626,7 @@ CaseExtMovd:
if (!Support::test(options, InstOptions::kX86_ModMR)) if (!Support::test(options, InstOptions::kX86_ModMR))
goto EmitX86R; goto EmitX86R;
opcode += 0x10; opcode += 0x10u;
std::swap(opReg, rbReg); std::swap(opReg, rbReg);
goto EmitX86R; goto EmitX86R;
} }
@ -5089,6 +5094,9 @@ Error Assembler::onAttach(CodeHolder* code) noexcept {
Arch arch = code->arch(); Arch arch = code->arch();
ASMJIT_PROPAGATE(Base::onAttach(code)); ASMJIT_PROPAGATE(Base::onAttach(code));
_instructionAlignment = uint8_t(1);
assignEmitterFuncs(this);
if (Environment::is32Bit(arch)) { if (Environment::is32Bit(arch)) {
// 32 bit architecture - X86. // 32 bit architecture - X86.
_forcedInstOptions |= InstOptions::kX86_InvalidRex; _forcedInstOptions |= InstOptions::kX86_InvalidRex;

View File

@ -151,7 +151,7 @@ ASMJIT_BEGIN_SUB_NAMESPACE(x86)
//! printf("Status: %s\n", DebugUtils::errorAsString(err)); //! printf("Status: %s\n", DebugUtils::errorAsString(err));
//! //!
//! // Ambiguous operand size - the pointer requires size. //! // 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)); //! printf("Status: %s\n", DebugUtils::errorAsString(err));
//! //!
//! return 0; //! 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: //! 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) { //! void example(x86::Assembler& a) {
//! x86::Gp zax = a.gpz(x86::Gp::kIdAx); //! x86::Gp zax = a.gpz(x86::Gp::kIdAx);
//! x86::Gp zbx = a.gpz(x86::Gp::kIdBx); //! 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_a = a.zcx();
//! x86::Gp src_b = a.zdx(); //! x86::Gp src_b = a.zdx();
//! //!
//! X86::Xmm vec0 = x86::xmm0; //! x86::Xmm vec0 = x86::xmm0;
//! X86::Xmm vec1 = x86::xmm1; //! x86::Xmm vec1 = x86::xmm1;
//! //!
//! // Create/initialize FuncDetail and FuncFrame. //! // Create/initialize FuncDetail and FuncFrame.
//! FuncDetail func; //! 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; //! FuncFrame frame;
//! frame.init(func); //! frame.init(func);
//! //!
//! // Make XMM0 and XMM1 dirty - RegGroup::kVec describes XMM|YMM|ZMM registers. //! // 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 //! // 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. //! // 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. //! FuncArgsAssignment args(&func); // Create arguments assignment context.
//! args.assignAll(dst, src_a, src_b);// Assign our registers to arguments. //! 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). //! frame.finalize(); // Finalize the FuncFrame (updates it).
//! //!
//! a.emitProlog(frame); // Emit function prolog. //! a.emitProlog(frame); // Emit function prolog.
@ -537,16 +541,16 @@ ASMJIT_BEGIN_SUB_NAMESPACE(x86)
//! //!
//! void prefixesExample(x86::Assembler& a) { //! void prefixesExample(x86::Assembler& a) {
//! // Lock prefix for implementing atomics: //! // Lock prefix for implementing atomics:
//! // lock add dword ptr [dst], 1 //! // lock add dword ptr [rdi], 1
//! a.lock().add(x86::dword_ptr(dst), 1); //! a.lock().add(x86::dword_ptr(x86::rdi), 1);
//! //!
//! // Similarly, XAcquire/XRelease prefixes are also available: //! // Similarly, XAcquire/XRelease prefixes are also available:
//! // xacquire add dword ptr [dst], 1 //! // xacquire add dword ptr [rdi], 1
//! a.xacquire().add(x86::dword_ptr(dst), 1); //! a.xacquire().add(x86::dword_ptr(x86::rdi), 1);
//! //!
//! // Rep prefix (see also repe/repz and repne/repnz): //! // Rep prefix (see also repe/repz and repne/repnz):
//! // rep movs byte ptr [dst], byte ptr [src] //! // rep movs byte ptr [rdi], byte ptr [rsi]
//! a.rep().movs(x86::byte_ptr(dst), x86::byte_ptr(src)); //! a.rep().movs(x86::byte_ptr(x86::rdi), x86::byte_ptr(x86::rsi));
//! //!
//! // Forcing REX prefix in 64-bit mode. //! // Forcing REX prefix in 64-bit mode.
//! // rex mov eax, 1 //! // rex mov eax, 1
@ -610,10 +614,10 @@ ASMJIT_BEGIN_SUB_NAMESPACE(x86)
//! // ----------------- //! // -----------------
//! // //! //
//! // - Broadcast data is part of memory operand. //! // - 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} //! // 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 //! // Embedded Rounding & Suppress-All-Exceptions
//! // ------------------------------------------- //! // -------------------------------------------
@ -659,6 +663,7 @@ public:
//! \} //! \}
//! \endcond //! \endcond
//! \cond INTERNAL
//! \name Emit //! \name Emit
//! \{ //! \{

View File

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

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